001package com.fs.starfarer.api.impl.campaign.rulecmd.missions; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.List; 007import java.util.Map; 008import java.util.Random; 009 010import org.lwjgl.input.Keyboard; 011 012import com.fs.starfarer.api.EveryFrameScript; 013import com.fs.starfarer.api.Global; 014import com.fs.starfarer.api.campaign.InteractionDialogAPI; 015import com.fs.starfarer.api.campaign.InteractionDialogPlugin; 016import com.fs.starfarer.api.campaign.SectorEntityToken; 017import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 018import com.fs.starfarer.api.campaign.econ.MarketAPI; 019import com.fs.starfarer.api.campaign.rules.MemKeys; 020import com.fs.starfarer.api.campaign.rules.MemoryAPI; 021import com.fs.starfarer.api.characters.ImportantPeopleAPI; 022import com.fs.starfarer.api.characters.PersonAPI; 023import com.fs.starfarer.api.combat.EngagementResultAPI; 024import com.fs.starfarer.api.impl.campaign.DebugFlags; 025import com.fs.starfarer.api.impl.campaign.DevMenuOptions; 026import com.fs.starfarer.api.impl.campaign.RuleBasedInteractionDialogPluginImpl; 027import com.fs.starfarer.api.impl.campaign.intel.bar.BarEventDialogPlugin; 028import com.fs.starfarer.api.impl.campaign.intel.bar.PortsideBarData; 029import com.fs.starfarer.api.impl.campaign.intel.bar.PortsideBarEvent; 030import com.fs.starfarer.api.impl.campaign.intel.bar.events.BarEventManager; 031import com.fs.starfarer.api.impl.campaign.intel.contacts.ContactIntel; 032import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionBarEventWrapper; 033import com.fs.starfarer.api.impl.campaign.rulecmd.BaseCommandPlugin; 034import com.fs.starfarer.api.impl.campaign.rulecmd.DumpMemory; 035import com.fs.starfarer.api.impl.campaign.rulecmd.FireAll; 036import com.fs.starfarer.api.impl.campaign.rulecmd.FireBest; 037import com.fs.starfarer.api.impl.campaign.rulecmd.ShowDefaultVisual; 038import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.AddBarEvent; 039import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.AddBarEvent.BarEventData; 040import com.fs.starfarer.api.impl.campaign.tutorial.TutorialMissionIntel; 041import com.fs.starfarer.api.util.FaderUtil; 042import com.fs.starfarer.api.util.Misc; 043import com.fs.starfarer.api.util.Misc.Token; 044import com.fs.starfarer.api.util.WeightedRandomPicker; 045 046/** 047 * BarCMD 048 */ 049public class BarCMD extends BaseCommandPlugin implements InteractionDialogPlugin { 050 051 public static float BAR_EVENT_MIN_TIME_BEFORE_CHANGING = 20f; 052 public static float BAR_EVENT_MAX_TIME_BEFORE_CHANGING = 40f; 053 054 protected SectorEntityToken entity; 055 protected InteractionDialogPlugin originalPlugin; 056 protected InteractionDialogAPI dialog; 057 protected Map<String, MemoryAPI> memoryMap; 058 059 public static class BarAmbiencePlayer implements EveryFrameScript { 060 public MarketAPI market; 061 public String soundId = "bar_ambience"; 062 public float pitch = 1f; 063 public float volume = 1f; 064 public float musicSuppression = 0.95f; 065 public boolean done = false; 066 067 public FaderUtil fader = new FaderUtil(0f, 0.5f); 068 public BarAmbiencePlayer(MarketAPI market) { 069 this.market = market; 070 if (market.getFaction() != null) { 071 soundId = market.getFaction().getBarSound(); 072 } 073 fader.fadeIn(); 074 } 075 public void advance(float amount) { 076 fader.advance(amount); 077 Global.getSector().getCampaignUI().suppressMusic(fader.getBrightness() * musicSuppression); 078 Global.getSoundPlayer().playUILoop(soundId, pitch, volume * fader.getBrightness()); 079 if (!Global.getSector().isPaused()) { 080 stop(); 081 } 082 } 083 public boolean isDone() { 084 return done; 085 } 086 public boolean runWhilePaused() { 087 return true; 088 } 089 public void stop() { 090 done = true; 091 } 092 } 093 094 public boolean execute(String ruleId, InteractionDialogAPI dialog, List<Token> params, final Map<String, MemoryAPI> memoryMap) { 095 this.dialog = dialog; 096 this.memoryMap = memoryMap; 097 if (dialog == null) return false; 098 099 String command = params.get(0).getString(memoryMap); 100 if (command == null) return false; 101 102 103 if (command.equals("returnFromEvent")) { 104 MemoryAPI mem = getEntityMemory(memoryMap); 105 BarCMD cmd = (BarCMD) mem.get("$BarCMD"); 106 mem.unset("$BarCMD"); 107 //mem.unset("$currMission_ref"); 108 109 dialog.setPlugin(cmd); 110 abortMissions(null); 111 112 dialog.getInteractionTarget().setActivePerson(null); 113 ((RuleBasedInteractionDialogPluginImpl)cmd.originalPlugin).updateMemory(); 114// memoryMap.put(MemKeys.LOCAL, dialog.getInteractionTarget().getMemoryWithoutUpdate()); 115// memoryMap.remove(MemKeys.ENTITY); 116 117 118 boolean withContinue = params.size() > 1 && params.get(1).getBoolean(memoryMap); 119 cmd.returningFromEvent(withContinue); 120 121 return true; 122 } else if (command.equals("accept")) { 123 //MemoryAPI mem = getEntityMemory(memoryMap); 124 //Object ref = mem.get("$currMission_ref"); 125 126 String missionId = params.get(1).getString(memoryMap); 127 HubMissionBarEventWrapper w = getWrapperFor(missionId); 128 if (w != null && w.getMission() != null) { 129 BarEventManager.getInstance().notifyWasInteractedWith(w); 130 w.getMission().accept(dialog, memoryMap); 131 } 132 return true; 133 } else if (command.equals("leaveBar")) { 134 leaveBar(); 135 return true; 136 } else if (command.equals("playAmbience")) { 137 entity = dialog.getInteractionTarget(); 138 if (!Global.getSector().hasTransientScript(BarAmbiencePlayer.class)) { 139 Global.getSector().addTransientScript(new BarAmbiencePlayer(entity.getMarket())); 140 } 141 return true; 142 } 143 144// else if (command.equals("addContact")) { 145// ContactIntel.addPotentialContact(contact, text) 146// } 147 148 entity = dialog.getInteractionTarget(); 149 originalPlugin = dialog.getPlugin(); 150 151 //FireBest.fire(null, dialog, memoryMap, "SalvageSpecialFinishedNoContinue"); 152 153 if (command.equals("showOptions")) { 154 showOptions(false); 155 if (!Global.getSector().hasTransientScript(BarAmbiencePlayer.class)) { 156 Global.getSector().addTransientScript(new BarAmbiencePlayer(entity.getMarket())); 157 } 158 } 159 160 return true; 161 } 162 163 public void showOptions(boolean returningFromEvent) { 164 dialog.getVisualPanel().restoreSavedVisual(); 165 dialog.getVisualPanel().saveCurrentVisual(); 166 167 PortsideBarData data = PortsideBarData.getInstance(); 168 169 MarketAPI market = entity.getMarket(); 170 171 dialog.getOptionPanel().clearOptions(); 172 173 Random random = new Random(BarEventManager.getInstance().getSeed(entity, null, null)); 174 175 int min = Global.getSettings().getInt("minBarEvents"); 176 int max = Global.getSettings().getInt("maxBarEvents"); 177 float pMult = Global.getSettings().getFloat("barEventProbOneMore"); 178 //int num = min + random.nextInt(max - min + 1); 179 180 WeightedRandomPicker<Integer> numPicker = new WeightedRandomPicker<Integer>(random); 181 float p = 1f; 182 for (int i = min; i <= max; i++) { 183 numPicker.add(i, p); 184 p *= pMult; 185 } 186 int num = numPicker.pick(); 187 188 if (DebugFlags.BAR_DEBUG) { 189 max = 8; 190 num = 7; 191 //num = max; 192 } 193 194 //num = 1000; 195 196 List<PortsideBarEvent> events = new ArrayList<PortsideBarEvent>(); 197 String key = "$BarCMD_shownEvents"; 198 MemoryAPI mem = market.getMemoryWithoutUpdate(); 199 boolean needToSaveShown = false; 200 if (mem.contains(key)) { 201 List<String> eventIds = (List<String>) mem.get(key); 202 for (String id : eventIds) { 203 OUTER: for (PortsideBarEvent event : data.getEvents()) { 204// for (GenericBarEventCreator c : BarEventManager.getInstance().getTimeout().getItems()) { 205// if (c.getBarEventId() != null && c.getBarEventId().equals(id)) { 206// continue OUTER; 207// } 208// } 209 if (id.equals(event.getBarEventId())) { 210 events.add(event); 211 } 212 } 213 } 214 num = Math.max(eventIds.size(), num); 215 } else { 216 events.addAll(data.getEvents()); 217 Collections.shuffle(events, random); 218 needToSaveShown = true; 219 } 220 221 boolean addedSomething = false; 222 223 AddBarEvent.clearTempEvents(market); 224 FireAll.fire(null, dialog, memoryMap, "AddBarEvents"); 225 226 List<BarEventData> temp = new ArrayList<BarEventData>(AddBarEvent.getTempEvents(market).events.values()); 227 for (int i = 0; i < max && i < temp.size(); i++) { 228 BarEventData b = temp.get(i); 229 if (!b.blurb.isEmpty()) { 230 dialog.getTextPanel().addPara(b.blurb); 231 } 232 dialog.getOptionPanel().addOption(b.option, b.optionId); 233 if (b.optionColor != null) { 234 dialog.setOptionColor(b.optionId, b.optionColor); 235 } 236 addedSomething = true; 237 } 238 239 List<String> shown = new ArrayList<String>(); 240 241 // used alongside with ip.excludeFromGetPerson() to ensure that the same person isn't picked 242 // for multiple concurrent bar events 243 ImportantPeopleAPI ip = Global.getSector().getImportantPeople(); 244 ip.resetExcludeFromGetPerson(); 245 246 // existing contacts don't show up at the bar, since the bar encounter dialogue is written 247 // assuming you're meeting them for the first time 248 for (IntelInfoPlugin intel : Global.getSector().getIntelManager().getIntel(ContactIntel.class)) { 249 ip.excludeFromGetPerson(((ContactIntel)intel).getPerson()); 250 } 251 252// BaseMissionHub.resetMissionAngle(null, market); 253// BaseMissionHub.getMissionAngle(null, market, random); 254 255 Collections.sort(events, new Comparator<PortsideBarEvent>() { 256 public int compare(PortsideBarEvent o1, PortsideBarEvent o2) { 257 boolean p1 = o1.isAlwaysShow(); 258 boolean p2 = o2.isAlwaysShow(); 259 if (p1 && !p2) return -1; 260 if (p2 && !p1) return 1; 261 return 0; 262 } 263 }); 264 265 int curr = 0; 266 //for (PortsideBarEvent event : data.getEvents()) { 267 for (PortsideBarEvent event : events) { 268 if (TutorialMissionIntel.isTutorialInProgress()) continue; 269 if (curr + temp.size() >= max) break; 270 271// if (curr < 16) { 272// curr++; 273// continue; 274// } 275 276 if (event.shouldRemoveEvent()) continue; 277 if (!event.shouldShowAtMarket(market)) continue; 278 279 event.addPromptAndOption(dialog, memoryMap); 280 if (event instanceof HubMissionBarEventWrapper) { 281 HubMissionBarEventWrapper w = (HubMissionBarEventWrapper) event; 282 if (w.getMission() == null) { // aborted during creation 283 continue; 284 } 285 } 286 event.wasShownAtMarket(market); 287 288 if (event.getBarEventId() != null) { 289 shown.add(event.getBarEventId()); 290 } 291 292 addedSomething = true; 293 294 if (!event.isAlwaysShow()) { 295 curr++; 296 } 297 if (curr >= num) break; 298 } 299 300 //BaseMissionHub.clearCreatedMissionsList(null, market); 301 ip.resetExcludeFromGetPerson(); 302 303 if (needToSaveShown) { 304 float time = BAR_EVENT_MIN_TIME_BEFORE_CHANGING + 305 (BAR_EVENT_MAX_TIME_BEFORE_CHANGING - BAR_EVENT_MIN_TIME_BEFORE_CHANGING) * random.nextFloat(); 306 mem.set(key, shown, time); 307 } 308 309 if (returningFromEvent) { 310 if (!addedSomething) { 311 dialog.getTextPanel().addPara("Nothing of note is going on at the bar."); 312 } else { 313 dialog.getTextPanel().addPara("You unobtrusively watch the patrons of the bar for " + 314 "a few minutes before deciding what to do next."); 315 } 316 } 317 318 dialog.getOptionPanel().addOption("Leave the bar", "barLeave"); 319 dialog.getOptionPanel().setShortcut("barLeave", Keyboard.KEY_ESCAPE, false, false, false, true); 320 321 322 if (Global.getSettings().isDevMode()) { 323 DevMenuOptions.addOptions(dialog); 324 } 325 326 dialog.setPlugin(this); 327 init(dialog); 328 } 329 330 public void optionSelected(String optionText, Object optionData) { 331 if (optionText != null) { 332 //dialog.getTextPanel().addParagraph(optionText, Global.getSettings().getColor("buttonText")); 333 dialog.addOptionSelectedText(optionData); 334 } 335 if (optionData == DumpMemory.OPTION_ID) { 336 new DumpMemory().execute(null, dialog, null, getMemoryMap()); 337 return; 338 } else if (DevMenuOptions.isDevOption(optionData)) { 339 DevMenuOptions.execute(dialog, (String) optionData); 340 return; 341 } 342 343 // need to abort any HubMissionBarEventWrapper missions that are *not* the current selection 344 String optionId = null; 345 if (optionData instanceof String) { 346 optionId = (String) optionData; 347 } 348 349 abortMissions(optionId); 350 351 if (optionData instanceof PortsideBarEvent) { 352 PortsideBarEvent event = (PortsideBarEvent) optionData; 353 BarEventDialogPlugin plugin = new BarEventDialogPlugin(this, this, event, memoryMap); 354 dialog.setPlugin(plugin); 355 plugin.init(dialog); 356 return; 357 } else if ("barLeave".equals(optionData)) { 358 leaveBar(); 359 } else if ("barContinue".equals(optionData)) { 360 showOptions(true); 361 } else if (optionData instanceof String) { 362 // a HubMissionBarEventWrapper option 363// HubMissionBarEventWrapper w = getWrapperFor(optionId); 364// if (w != null && w.getMission() != null) { 365// mem.set("$currMission_ref", w.getMission(), 0f); 366// } else { 367// mem.unset("$currMission_ref"); 368// } 369 370 MemoryAPI eMem = getEntityMemory(memoryMap); 371 eMem.set("$BarCMD", this, 0f); 372 373 HubMissionBarEventWrapper w = getWrapperFor(optionId); 374 if (w != null && w.getMission() != null) { 375 PersonAPI person = w.getMission().getPerson(); 376 if (person != null) { 377 dialog.getInteractionTarget().setActivePerson(person); 378 ((RuleBasedInteractionDialogPluginImpl)originalPlugin).updateMemory(); 379// memoryMap.put(MemKeys.ENTITY, memoryMap.get(MemKeys.LOCAL)); 380// memoryMap.put(MemKeys.LOCAL, person.getMemoryWithoutUpdate()); 381 dialog.getVisualPanel().showPersonInfo(person, true); 382 } 383 } 384 385 MemoryAPI mem = memoryMap.get(MemKeys.LOCAL); 386 dialog.setPlugin(originalPlugin); 387 mem.set("$option", (String) optionData, 0f); 388 389 FireBest.fire(null, dialog, memoryMap, "DialogOptionSelected"); 390 } 391 } 392 393 public void returningFromEvent(PortsideBarEvent event) { 394 returningFromEvent(event.endWithContinue()); 395 } 396 public void returningFromEvent(boolean withContinue) { 397 if (withContinue) { 398 dialog.getOptionPanel().clearOptions(); 399 dialog.getOptionPanel().addOption("Continue", "barContinue"); 400 } else { 401 showOptions(true); 402 } 403 } 404 405 public HubMissionBarEventWrapper getWrapperFor(String optionId) { 406 PortsideBarData data = PortsideBarData.getInstance(); 407 for (PortsideBarEvent event : data.getEvents()) { 408 if (event instanceof HubMissionBarEventWrapper) { 409 HubMissionBarEventWrapper w = (HubMissionBarEventWrapper) event; 410 if (w.getMission() == null) continue; 411 if (optionId != null && optionId.startsWith(w.getMission().getTriggerPrefix())) { 412 return w; 413 } 414 } 415 } 416 return null; 417 } 418 public void abortMissions(String optionId) { 419 PortsideBarData data = PortsideBarData.getInstance(); 420 for (PortsideBarEvent event : data.getEvents()) { 421 if (event instanceof HubMissionBarEventWrapper) { 422 HubMissionBarEventWrapper w = (HubMissionBarEventWrapper) event; 423 if (w.getMission() == null) continue; 424 if (optionId == null || !optionId.startsWith(w.getMission().getTriggerPrefix())) { 425 w.abortMission(); 426 } 427 } 428 } 429 } 430 431 432 public static BarAmbiencePlayer getAmbiencePlayer() { 433 for (EveryFrameScript script : Global.getSector().getTransientScripts()) { 434 if (script instanceof BarAmbiencePlayer) { 435 return (BarAmbiencePlayer) script; 436 } 437 } 438 return null; 439 } 440 441 public void leaveBar() { 442 443 BarAmbiencePlayer player = getAmbiencePlayer(); 444 if (player != null) { 445 player.stop(); 446 } 447 448 if (originalPlugin != null) { 449 dialog.setPlugin(originalPlugin); 450 } 451 452 new ShowDefaultVisual().execute(null, dialog, Misc.tokenize(""), memoryMap); 453 FireBest.fire(null, dialog, memoryMap, "ReturnFromBar"); 454 } 455 456 public void advance(float amount) { 457 } 458 public void backFromEngagement(EngagementResultAPI battleResult) { 459 } 460 public Object getContext() { 461 return null; 462 } 463 public Map<String, MemoryAPI> getMemoryMap() { 464 return memoryMap; 465 } 466 public void optionMousedOver(String optionText, Object optionData) { 467 } 468 469 public void init(InteractionDialogAPI dialog) { 470 } 471 472} 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490