001package com.fs.starfarer.api.impl.campaign;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.List;
006import java.util.Map;
007import java.util.Random;
008
009import java.awt.Color;
010
011import org.lwjgl.input.Keyboard;
012
013import com.fs.starfarer.api.Global;
014import com.fs.starfarer.api.campaign.BattleAPI;
015import com.fs.starfarer.api.campaign.BattleAPI.BattleSide;
016import com.fs.starfarer.api.campaign.CampaignFleetAPI;
017import com.fs.starfarer.api.campaign.CargoAPI;
018import com.fs.starfarer.api.campaign.CoreInteractionListener;
019import com.fs.starfarer.api.campaign.EngagementResultForFleetAPI;
020import com.fs.starfarer.api.campaign.FactionAPI;
021import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin.DataForEncounterSide;
022import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin.DisengageHarryAvailability;
023import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin.EngagementOutcome;
024import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin.PursueAvailability;
025import com.fs.starfarer.api.campaign.FleetEncounterContextPlugin.Status;
026import com.fs.starfarer.api.campaign.FleetMemberPickerListener;
027import com.fs.starfarer.api.campaign.InteractionDialogAPI;
028import com.fs.starfarer.api.campaign.InteractionDialogPlugin;
029import com.fs.starfarer.api.campaign.OptionPanelAPI;
030import com.fs.starfarer.api.campaign.RuleBasedDialog;
031import com.fs.starfarer.api.campaign.SectorEntityToken;
032import com.fs.starfarer.api.campaign.SectorEntityToken.VisibilityLevel;
033import com.fs.starfarer.api.campaign.TextPanelAPI;
034import com.fs.starfarer.api.campaign.VisualPanelAPI;
035import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI;
036import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.EncounterOption;
037import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.InitialBoardingResponse;
038import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.PursuitOption;
039import com.fs.starfarer.api.campaign.events.CampaignEventPlugin;
040import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
041import com.fs.starfarer.api.campaign.rules.MemKeys;
042import com.fs.starfarer.api.campaign.rules.MemoryAPI;
043import com.fs.starfarer.api.campaign.rules.RuleAPI;
044import com.fs.starfarer.api.campaign.rules.RulesAPI;
045import com.fs.starfarer.api.characters.PersonAPI;
046import com.fs.starfarer.api.combat.BattleCreationContext;
047import com.fs.starfarer.api.combat.CombatReadinessPlugin;
048import com.fs.starfarer.api.combat.EngagementResultAPI;
049import com.fs.starfarer.api.fleet.CrewCompositionAPI;
050import com.fs.starfarer.api.fleet.FleetGoal;
051import com.fs.starfarer.api.fleet.FleetMemberAPI;
052import com.fs.starfarer.api.impl.campaign.FleetEncounterContext.BoardingResult;
053import com.fs.starfarer.api.impl.campaign.FleetEncounterContext.EngageBoardableOutcome;
054import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
055import com.fs.starfarer.api.impl.campaign.ids.Sounds;
056import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
057import com.fs.starfarer.api.impl.campaign.rulecmd.DumpMemory;
058import com.fs.starfarer.api.impl.campaign.rulecmd.FireBest;
059import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption;
060import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.BaseOptionStoryPointActionDelegate;
061import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.StoryOptionParams;
062import com.fs.starfarer.api.impl.campaign.skills.BaseSkillEffectDescription;
063import com.fs.starfarer.api.impl.combat.CRPluginImpl;
064import com.fs.starfarer.api.ui.LabelAPI;
065import com.fs.starfarer.api.ui.TooltipMakerAPI;
066import com.fs.starfarer.api.util.Misc;
067import com.fs.starfarer.api.util.Pair;
068
069public class FleetInteractionDialogPluginImpl implements InteractionDialogPlugin, RuleBasedDialog {
070
071        public static float EMERGENCY_REPAIRS_MAX_DP = Global.getSettings().getFloat("emergencyRepairsMaxDPValue");
072        
073        public static String DO_NOT_AUTO_SHOW_FC_PORTRAIT = "$doNotAutoShowFleetCommanderPortrait";
074        
075        public static interface FIDConfigGen {
076                FIDConfig createConfig();
077        }
078        
079        public static class FIDConfig {
080                public boolean showCommLinkOption = true;
081                public boolean leaveAlwaysAvailable = false;
082                public boolean showWarningDialogWhenNotHostile = true;
083                public boolean showTransponderStatus = true;
084                public boolean showFleetAttitude = true;
085                public boolean showEngageText = true;
086                public boolean alwaysAttackVsAttack = false;
087                public boolean alwaysPursue = false;
088                public boolean alwaysHarry = false;
089                public boolean alwaysLetGo = false;
090                public boolean dismissOnLeave = true;
091                public boolean withSalvage = true;
092                public boolean lootCredits = true;
093                
094                public boolean showCrRecoveryText = true;
095                
096                public boolean showVictoryText = true;
097                
098                //public boolean postLootLeaveHasShortcut = true;
099                
100                public boolean impactsEnemyReputation = true;
101                public boolean impactsAllyReputation = true;
102                
103                public boolean pullInAllies = true;
104                public boolean pullInEnemies = true;
105                public boolean pullInStations = true;
106                
107                //public String postLootLeaveOptionText = null;
108                public String noSalvageLeaveOptionText = null;
109                public String firstTimeEngageOptionText = null;
110                public String afterFirstTimeEngageOptionText = null;
111                
112                public FIDDelegate delegate = null;
113                public boolean printXPToDialog = false;
114                
115                public boolean justShowFleets = false;
116                public boolean showPullInText = true;
117                
118                public boolean straightToEngage = false;
119                public boolean playerAttackingStation = false;
120                public boolean playerDefendingStation = false;
121                
122                
123//              /**
124//               * Note: this means that retreating won't work, either - the player will be forced to re-engage every time until
125//               * one fleet is entirely destroyed.
126//               * Also this gets broken completely by e.g. Phase Anchor
127//               */
128//              public boolean noLeaveOption = false;
129                
130                public boolean noLeaveOptionOnFirstEngagement = false;
131                
132                
133                public Random salvageRandom = null;
134        }
135        
136        public static interface FIDDelegate {
137                public void postPlayerSalvageGeneration(InteractionDialogAPI dialog, FleetEncounterContext context, CargoAPI salvage);
138                public void battleContextCreated(InteractionDialogAPI dialog, BattleCreationContext bcc);
139                public void notifyLeave(InteractionDialogAPI dialog);
140        }
141        
142        public static class BaseFIDDelegate implements FIDDelegate {
143                public void battleContextCreated(InteractionDialogAPI dialog, BattleCreationContext bcc) {}
144                public void notifyLeave(InteractionDialogAPI dialog) {}
145                public void postPlayerSalvageGeneration(InteractionDialogAPI dialog, FleetEncounterContext context, CargoAPI salvage) {}
146        }
147        
148        
149        
150        
151        protected static enum VisualType {
152                FLEET_INFO,
153                OTHER,
154        }
155        
156        
157        public static enum OptionId {
158                INIT,
159                PRINT_ONGOING_BATTLE_INFO,
160                BEGIN_FLEET_ENCOUNTER_2,
161                OPEN_COMM,
162                CUT_COMM,
163                ENGAGE,
164                FORCE_ENGAGE,
165                ATTEMPT_TO_DISENGAGE,
166                DISENGAGE,
167                CLEAN_DISENGAGE,
168                SCUTTLE,
169                PURSUE,
170                AUTORESOLVE_PURSUE,
171                HARRY_PURSUE,
172                LET_THEM_GO,
173                LEAVE,
174                LOOT_THEN_LEAVE,
175                CONTINUE_LEAVE,
176                CONTINUE,
177                GO_TO_MAIN,
178                GO_TO_PRE_BATTLE,
179                RECOVERY_SELECT,
180                RECOVERY_CONTINUE,
181                CONTINUE_FROM_VICTORY_TRIGGERS,
182                CONTINUE_LOOT,
183                CONTINUE_INTO_BATTLE,
184                
185                CONTINUE_INTO_BOARDING,
186                BOARDING_ACTION,
187                SELECT_FLAGSHIP,
188                CRASH_MOTHBALL,
189                ENGAGE_BOARDABLE,
190                ABORT_BOARDING_ACTION,
191                HARD_DOCK,
192                LAUNCH_ASSAULT_TEAMS,
193                LET_IT_GO,
194                
195                SELECTOR_MARINES,
196                SELECTOR_CREW,
197                
198                REINIT_CONTINUE,
199                
200                INITIATE_BATTLE,
201                JOIN_ONGOING_BATTLE,
202                CONTINUE_ONGOING_BATTLE,
203                
204                EMERGENCY_REPAIRS,
205                
206                DEV_MODE_ESCAPE,
207        }
208        
209        
210        protected InteractionDialogAPI dialog;
211        protected TextPanelAPI textPanel;
212        protected OptionPanelAPI options;
213        protected VisualPanelAPI visual;
214        
215        protected CampaignFleetAPI playerFleet;
216        protected CampaignFleetAPI otherFleet;
217        
218        protected FleetGoal playerGoal = FleetGoal.ATTACK;
219        protected FleetGoal otherGoal = FleetGoal.ATTACK;
220
221        protected VisualType currVisualType = VisualType.FLEET_INFO;
222        
223        protected FleetEncounterContext context = new FleetEncounterContext();
224        
225        protected static final Color HIGHLIGHT_COLOR = Global.getSettings().getColor("buttonShortcut");
226        protected static final Color FRIEND_COLOR = Global.getSettings().getColor("textFriendColor");
227        protected static final Color ENEMY_COLOR = Misc.getNegativeHighlightColor();
228        
229        protected RuleBasedInteractionDialogPluginImpl conversationDelegate;
230        protected boolean ongoingBattle = false;
231        protected boolean firstEngagement = true;
232        protected boolean joinedBattle = false;
233        
234        protected boolean forceEngage = false;
235        
236        protected boolean shownTooLargeToRetreatMessage = false;
237        
238        public static boolean inConversation = false;
239        public static boolean directToComms = false;
240        
241        protected FIDConfig config;
242        
243//      public static class FleetMemberPreEncounterData {
244//              public int indexInFleet;
245//              public PersonAPI officer;
246//      }
247        protected List<FleetMemberAPI> membersInOrderPreEncounter = new ArrayList<FleetMemberAPI>();
248        
249        public FleetInteractionDialogPluginImpl() {
250                this(null);
251        }
252        public FleetInteractionDialogPluginImpl(FIDConfig params) {
253                this.config = params;
254                
255                if (origFlagship == null) {
256                        origFlagship = Global.getSector().getPlayerFleet().getFlagship();
257                }
258                if (origCaptains.isEmpty()) {
259                        for (FleetMemberAPI member : Global.getSector().getPlayerFleet().getFleetData().getMembersListCopy()) {
260                                origCaptains.put(member, member.getCaptain());
261                        }
262                        membersInOrderPreEncounter = new ArrayList<FleetMemberAPI>(Global.getSector().getPlayerFleet().getFleetData().getMembersListCopy());
263                }
264        }
265        
266        public Map<String, MemoryAPI> getMemoryMap() {
267                return conversationDelegate == null ? null : conversationDelegate.getMemoryMap();
268        }
269        
270        private boolean skipAttitudeOnInit = false;
271        public void reinit(boolean withContinueOnRuleFound) {
272                RulesAPI rules = Global.getSector().getRules();
273                RuleAPI rule = rules.getBestMatching(null, "BeginFleetEncounter", dialog, conversationDelegate.getMemoryMap());
274                if (rule == null || !withContinueOnRuleFound) {
275                        reinitPostContinue();
276                } else {
277                        options.clearOptions();
278                        options.addOption("Continue", OptionId.REINIT_CONTINUE, null);
279                        if (Global.getSettings().isDevMode()) {
280                                DevMenuOptions.addOptions(dialog);
281                        }
282                }
283        }
284        
285        public void reinitPostContinue() {
286                //init(dialog);
287                inConversation = false;
288                directToComms = false;
289                
290                boolean cont = conversationDelegate.getMemoryMap().get(MemKeys.LOCAL).getBoolean("$fidpi_addContinue");
291                conversationDelegate.getMemoryMap().get(MemKeys.LOCAL).unset("$fidpi_addContinue");
292                
293                if (cont) {
294                        conversationDelegate.fireBest("BeginFleetEncounter2");
295                } else {
296                        conversationDelegate.fireBest("BeginFleetEncounter");
297                }
298                if (conversationDelegate.getMemoryMap().get(MemKeys.LOCAL).getBoolean("$fidpi_addContinue")) {
299                        options.clearOptions();
300                        options.addOption("Continue", OptionId.BEGIN_FLEET_ENCOUNTER_2);
301                } else {
302                        if (directToComms) {
303                                optionSelected(null, OptionId.OPEN_COMM);
304                        } else {
305                                //skipAttitudeOnInit = true;
306                                optionSelected(null, OptionId.INIT);
307                        }
308                }
309        }
310        
311        public void init(InteractionDialogAPI dialog) {
312                this.dialog = dialog;
313                
314                if (this.config == null) {
315                        MemoryAPI memory = dialog.getInteractionTarget().getMemoryWithoutUpdate();
316//                      if (memory.contains(MemFlags.FLEET_INTERACTION_DIALOG_CONFIG_OVERRIDE)) {
317//                              this.config = (FIDConfig) memory.get(MemFlags.FLEET_INTERACTION_DIALOG_CONFIG_OVERRIDE);
318//                      } else 
319                        if (memory.contains(MemFlags.FLEET_INTERACTION_DIALOG_CONFIG_OVERRIDE_GEN)) {
320                                this.config = ((FIDConfigGen) memory.get(MemFlags.FLEET_INTERACTION_DIALOG_CONFIG_OVERRIDE_GEN)).createConfig();
321                        } else {
322                                this.config = new FIDConfig();
323                        }
324                }
325                
326                
327                
328//              boolean sampling = true;
329//              while (sampling) {
330                if (Global.getSettings().isDevMode()) {
331                        dialog.setOptionOnEscape("dev mode exit", OptionId.DEV_MODE_ESCAPE);
332                        dialog.setOptionOnConfirm("dev mode exit", OptionId.DEV_MODE_ESCAPE);
333                }
334                
335                textPanel = dialog.getTextPanel();
336                options = dialog.getOptionPanel();
337                visual = dialog.getVisualPanel();
338
339                playerFleet = Global.getSector().getPlayerFleet();
340                if (playerFleet != null) {
341                        playerFleet.getFleetData().ensureHasFlagship();
342                }
343                otherFleet = (CampaignFleetAPI) (dialog.getInteractionTarget());
344
345//              playerFleet.getFleetData().takeSnapshot();
346//              otherFleet.getFleetData().takeSnapshot();
347                
348                if (context.getBattle() == null) {
349                        if (otherFleet.getBattle() == null || otherFleet.getBattle().isDone()) {
350                                ongoingBattle = false;
351                                BattleAPI battle = Global.getFactory().createBattle(playerFleet, otherFleet);
352                                context.setBattle(battle);
353                                pullInNearbyFleets();
354                        } else {
355                                ongoingBattle = true;
356                                context.setBattle(otherFleet.getBattle());
357                                if (context.getBattle().canJoin(playerFleet)) {
358                                        //context.getBattle().join(playerFleet);
359                                        pullInNearbyFleets();
360                                }
361                        }
362                }
363                
364                for (CampaignFleetAPI fleet : context.getBattle().getBothSides()) {
365                        fleet.inflateIfNeeded();
366                }
367                context.getBattle().genCombined();
368                
369                visual.setVisualFade(0.25f, 0.25f);
370                if (!config.straightToEngage) {
371                        if (ongoingBattle && !joinedBattle) {
372                                BattleAPI b = context.getBattle();
373                                String titleOne = b.getPrimary(b.getSideOne()).getNameWithFactionKeepCase();
374                                if (b.getSideOne().size() > 1) titleOne += ", with allies";
375                                String titleTwo = b.getPrimary(b.getSideTwo()).getNameWithFactionKeepCase();
376                                if (b.getSideTwo().size() > 1) titleTwo += ", with allies";
377                                visual.showPreBattleJoinInfo(null, playerFleet, Misc.ucFirst(titleOne), Misc.ucFirst(titleTwo), context);
378                        } else {
379                                //visual.showFleetInfo((String)null, playerFleet, (String)null, otherFleet, context);
380                                showFleetInfo();
381                        }
382                }
383                
384                inConversation = false;
385                directToComms = false;
386                conversationDelegate = new RuleBasedInteractionDialogPluginImpl();
387                conversationDelegate.setEmbeddedMode(true);
388                conversationDelegate.init(dialog);
389                
390//              }
391                
392                if (!config.justShowFleets) {
393        //              if (ongoingBattle) {
394                        conversationDelegate.getMemoryMap().get(MemKeys.LOCAL).set("$ongoingBattle", ongoingBattle, 0);
395                        boolean cont = conversationDelegate.getMemoryMap().get(MemKeys.LOCAL).getBoolean("$fidpi_addContinue");
396                        conversationDelegate.getMemoryMap().get(MemKeys.LOCAL).unset("$fidpi_addContinue");
397                        if (!ongoingBattle && !config.straightToEngage) {
398                                if (cont) {
399                                        conversationDelegate.fireBest("BeginFleetEncounter2");
400                                } else {
401                                        conversationDelegate.fireBest("BeginFleetEncounter");
402                                }
403                        }
404        //              } else {
405        //                      conversationDelegate.fireBest("OngoingBattleEncounter");
406        //              }
407                
408                        if (conversationDelegate.getMemoryMap().get(MemKeys.LOCAL).getBoolean("$fidpi_addContinue")) {
409                                options.clearOptions();
410                                options.addOption("Continue", OptionId.BEGIN_FLEET_ENCOUNTER_2);
411                        } else {
412                                if (directToComms) {
413                                        optionSelected(null, OptionId.OPEN_COMM);
414                                } else {
415                                        optionSelected(null, OptionId.INIT);
416                                }
417                        }
418                        
419                        if (config.straightToEngage) {
420                                if (ongoingBattle) {
421                                        optionSelected(null, OptionId.JOIN_ONGOING_BATTLE);
422                                } else {
423                                        optionSelected(null, OptionId.ENGAGE);
424                                }
425                        }
426                } else {
427//                      if (config.showPullInText) {
428//                              optionSelected(null, OptionId.PRINT_ONGOING_BATTLE_INFO);
429//                      }
430                }
431        }
432        
433        public void printOngoingBattleInfo() {
434                optionSelected(null, OptionId.PRINT_ONGOING_BATTLE_INFO);
435        }
436        
437        
438        protected List<CampaignFleetAPI> pulledIn = new ArrayList<CampaignFleetAPI>();
439        protected void pullInNearbyFleets() {
440                BattleAPI b = context.getBattle();
441                if (!ongoingBattle) {
442                        b.join(Global.getSector().getPlayerFleet());
443                }
444                
445                BattleSide playerSide = b.pickSide(Global.getSector().getPlayerFleet());
446                
447                boolean hostile = otherFleet.getAI() != null && otherFleet.getAI().isHostileTo(playerFleet);
448                if (ongoingBattle) hostile = true;
449                
450                //canDecline = otherFleet.getAI() != null && other
451                
452//              boolean someJoined = false;
453                CampaignFleetAPI actualPlayer = Global.getSector().getPlayerFleet();
454                CampaignFleetAPI actualOther = (CampaignFleetAPI) (dialog.getInteractionTarget());
455                
456                //textPanel.addParagraph("Projecting nearby fleet movements:");
457                //textPanel.addParagraph("You encounter a ");
458                pulledIn.clear();
459                
460                if (config.pullInStations && !b.isStationInvolved()) {
461                        SectorEntityToken closestEntity = null;
462                        CampaignFleetAPI closest = null;
463                        Pair<SectorEntityToken, CampaignFleetAPI> p = Misc.getNearestStationInSupportRange(actualOther);
464                        if (p != null) {
465                                closestEntity = p.one;
466                                closest = p.two;
467                        }
468                        
469                        if (closest != null) {
470                                BattleSide joiningSide = b.pickSide(closest, true);
471                                boolean canJoin = joiningSide != BattleSide.NO_JOIN;
472                                if (!config.pullInAllies && joiningSide == playerSide) {
473                                        canJoin = false;
474                                }
475                                if (!config.pullInEnemies && joiningSide != playerSide) {
476                                        canJoin = false;
477                                }
478                                if (b == closest.getBattle()) {
479                                        canJoin = false;
480                                }
481                                if (closest.getBattle() != null) {
482                                        canJoin = false;
483                                }
484                                
485                                if (canJoin) {
486                                        if (closestEntity != null) {
487                                                closestEntity.getMarket().reapplyIndustries(); // need to pick up station CR value, in some cases
488                                        }
489                                        b.join(closest);
490                                        pulledIn.add(closest);
491                                        
492                                        if (!config.straightToEngage && config.showPullInText) {
493                                                if (b.getSide(playerSide) == b.getSideFor(closest)) {
494                                                        textPanel.addParagraph(
495                                                                        Misc.ucFirst(closest.getNameWithFactionKeepCase()) + ": supporting your forces.");//, FRIEND_COLOR);
496                                                } else {
497                                                        if (hostile) {
498                                                                textPanel.addParagraph(Misc.ucFirst(closest.getNameWithFactionKeepCase()) + ": supporting the enemy.");//, ENEMY_COLOR);
499                                                        } else {
500                                                                textPanel.addParagraph(Misc.ucFirst(closest.getNameWithFactionKeepCase()) + ": supporting the opposing side.");
501                                                        }
502                                                }
503                                                textPanel.highlightFirstInLastPara(closest.getNameWithFactionKeepCase() + ":", closest.getFaction().getBaseUIColor());
504                                        }
505                                }
506                        }
507                }
508                
509                
510                for (CampaignFleetAPI fleet : actualPlayer.getContainingLocation().getFleets()) {
511                        if (b == fleet.getBattle()) continue;
512                        if (fleet.getBattle() != null) continue;
513                        
514                        if (fleet.isStationMode()) continue;
515                        
516                        float dist = Misc.getDistance(actualOther.getLocation(), fleet.getLocation());
517                        dist -= actualOther.getRadius();
518                        dist -= fleet.getRadius();
519//                      if (dist < Misc.getBattleJoinRange()) {
520//                              System.out.println("Checking: " + fleet.getNameWithFaction());
521//                      }
522                        
523                        if (fleet.getFleetData().getNumMembers() <= 0) continue;
524                        
525                        float baseSensorRange = playerFleet.getBaseSensorRangeToDetect(fleet.getSensorProfile());
526                        boolean visible = fleet.isVisibleToPlayerFleet();
527                        VisibilityLevel level = fleet.getVisibilityLevelToPlayerFleet();
528//                      if (dist < Misc.getBattleJoinRange() && 
529//                                      (dist < baseSensorRange || (visible && level != VisibilityLevel.SENSOR_CONTACT))) {
530//                              System.out.println("2380dfwef");
531//                      }
532                        float joinRange = Misc.getBattleJoinRange();
533                        if (fleet.getFaction().isPlayerFaction() && !fleet.isStationMode()) {
534                                joinRange += Global.getSettings().getFloat("battleJoinRangePlayerFactionBonus");
535                        }
536                        if (dist < joinRange && 
537                                        (dist < baseSensorRange || (visible && level != VisibilityLevel.SENSOR_CONTACT)) && 
538                                        ((fleet.getAI() != null && fleet.getAI().wantsToJoin(b, true)) || fleet.isStationMode())) {
539                                
540                                boolean ignore = fleet.getMemoryWithoutUpdate() != null && 
541                                                fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.FLEET_IGNORES_OTHER_FLEETS);
542                                if (ignore) continue;
543                                
544                                BattleSide joiningSide = b.pickSide(fleet, true);
545                                if (!config.pullInAllies && joiningSide == playerSide) continue;
546                                if (!config.pullInEnemies && joiningSide != playerSide) continue;
547                                
548                                b.join(fleet);
549                                pulledIn.add(fleet);
550                                //if (b.isPlayerSide(b.getSideFor(fleet))) {
551                                if (!config.straightToEngage && config.showPullInText) {
552                                        if (b.getSide(playerSide) == b.getSideFor(fleet)) {
553                                                textPanel.addParagraph(Misc.ucFirst(fleet.getNameWithFactionKeepCase()) + ": supporting your forces.");//, FRIEND_COLOR);
554                                        } else {
555                                                if (hostile) {
556                                                        textPanel.addParagraph(Misc.ucFirst(fleet.getNameWithFactionKeepCase()) + ": joining the enemy.");//, ENEMY_COLOR);
557                                                } else {
558                                                        textPanel.addParagraph(Misc.ucFirst(fleet.getNameWithFactionKeepCase()) + ": supporting the opposing side.");
559                                                }
560                                        }
561                                        textPanel.highlightFirstInLastPara(fleet.getNameWithFactionKeepCase() + ":", fleet.getFaction().getBaseUIColor());
562                                }
563//                              someJoined = true;
564                        }
565                }
566                
567                if (otherFleet != null) otherFleet.inflateIfNeeded();
568                for (CampaignFleetAPI curr : pulledIn) {
569                        curr.inflateIfNeeded();
570                }
571                
572//              if (!someJoined) {
573//                      addText("No nearby fleets will join the battle.");
574//              }
575                if (!ongoingBattle) {
576                        b.genCombined();
577                        b.takeSnapshots();
578                        playerFleet = b.getPlayerCombined();
579                        otherFleet = b.getNonPlayerCombined();
580                        if (!config.straightToEngage) {
581                                showFleetInfo();
582                        }
583                }
584                
585        }
586        
587        
588        protected EngagementResultAPI lastResult = null;
589        public void backFromEngagement(EngagementResultAPI result) {
590
591                // failsafe
592                if (playerGoal == null && otherGoal == null) {
593                        EngagementResultForFleetAPI player = result.didPlayerWin() ? result.getWinnerResult() : result.getLoserResult();
594                        EngagementResultForFleetAPI other = result.didPlayerWin() ? result.getLoserResult() : result.getWinnerResult();
595                        if (player.getDeployed().isEmpty()) {
596                                playerGoal = FleetGoal.ATTACK;
597                                otherGoal = FleetGoal.ATTACK;                           
598                        } else {
599                                playerGoal = FleetGoal.ATTACK;
600                                otherGoal = FleetGoal.ATTACK;
601                        }
602                        player.setGoal(playerGoal);
603                        other.setGoal(otherGoal);
604                }
605                
606                if (!ongoingBattle) {
607                        if (!otherFleet.getMemoryWithoutUpdate().contains(MemFlags.MEMORY_KEY_IGNORE_PLAYER_COMMS)) {
608                                otherFleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_IGNORE_PLAYER_COMMS, true, 0);
609                        }
610                }
611                
612                result.setBattle(context.getBattle());
613                
614                context.processEngagementResults(result);
615                lastResult = result;
616                
617                boolean startedWithAllies = false;
618                if (context.getBattle() != null) {
619                        startedWithAllies = context.getBattle().getPlayerSideSnapshot().size() > 1;
620                }
621                if (!Global.getSector().getPlayerFleet().isValidPlayerFleet() &&
622                                startedWithAllies && context.getBattle().getPlayerSide().size() > 1) {
623                                //!context.getBattle().getPlayerCombined().getFleetData().getMembersListCopy().isEmpty()) {
624                        showFleetInfo();
625                        addText(getString("battleFleetLost"));
626                        addText(getString("finalOutcomeNoShipsLeft"));
627                        options.clearOptions();
628                        options.addOption("Leave", OptionId.LEAVE, null);
629                        options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
630                        return;
631                }
632                
633                restoreOrigCaptains();
634                if (origFlagship != null) {
635                        if (selectedFlagship != null) {
636                                PersonAPI captain = origFlagship.getCaptain();
637                                if (captain != null && !captain.isPlayer()) {
638                                        selectedFlagship.setCaptain(captain);
639                                }
640                        }
641                        Global.getSector().getPlayerFleet().getFleetData().setFlagship(origFlagship);
642//                      origFlagship = null;
643//                      selectedFlagship = null;
644                        
645                }
646                
647                if (context.getLastEngagementOutcome() == null) {
648                        return; // failsafe
649                }
650
651                boolean totalDefeat = !playerFleet.isValidPlayerFleet();
652                boolean mutualDestruction = context.getLastEngagementOutcome() == EngagementOutcome.MUTUAL_DESTRUCTION;
653                
654                DataForEncounterSide playerSide = context.getDataFor(playerFleet);
655                CrewCompositionAPI crewLosses = playerSide.getCrewLossesDuringLastEngagement();
656                if ((int)crewLosses.getCrewInt() + (int)crewLosses.getMarines() > 0 && !totalDefeat && !mutualDestruction) {
657                        addText(getString("casualtyReport"));
658                        
659                        DataForEncounterSide data = context.getDataFor(playerFleet);
660                        int crewLost = (int) (data.getCrewLossesDuringLastEngagement().getCrewInt());
661                        int marinesLost = (int) (data.getCrewLossesDuringLastEngagement().getMarines());
662                        String crewLostStr = getApproximate(crewLost);
663                        if (crewLostStr.equals("no")) crewLostStr = "";
664                        if (crewLostStr.indexOf(" ") >= 0) {
665                                crewLostStr = crewLostStr.substring(crewLostStr.indexOf(" ") + 1);
666                        }
667                        String marinesLostStr = getApproximate(marinesLost);
668                        if (marinesLostStr.equals("no")) marinesLostStr = "";
669                        if (marinesLostStr.indexOf(" ") >= 0) {
670                                marinesLostStr = marinesLostStr.substring(marinesLostStr.indexOf(" ") + 1);
671                        }
672                        textPanel.highlightInLastPara(HIGHLIGHT_COLOR, crewLostStr, marinesLostStr);
673                }
674                
675                boolean showFleetInfo = false;
676                
677                switch (context.getLastEngagementOutcome()) {
678                case PURSUIT_PLAYER_OUT_FIRST_WIN:
679                        addText(getString("playerOutFirstPursuitWin"));
680                        showFleetInfo = true;
681                        break;
682                case PURSUIT_PLAYER_OUT_FIRST_LOSS:
683                        addText(getString("playerOutFirstPursuitLoss"));
684                        showFleetInfo = true;
685                        break;
686                case BATTLE_PLAYER_OUT_FIRST_WIN:
687                        addText(getString("playerOutFirstEngageWin"));
688                        showFleetInfo = true;
689                        break;
690                case BATTLE_PLAYER_OUT_FIRST_LOSS:
691                        addText(getString("playerOutFirstEngageLoss"));
692                        showFleetInfo = true;
693                        break;
694                case ESCAPE_PLAYER_OUT_FIRST_WIN:
695                        addText(getString("playerOutFirstEscapeWin"));
696                        showFleetInfo = true;
697                        break;
698                case ESCAPE_PLAYER_OUT_FIRST_LOSS:
699                        addText(getString("playerOutFirstEscapeLoss"));
700                        showFleetInfo = true;
701                        break;
702                case BATTLE_ENEMY_WIN:
703                        addText(getString("battleDefeat"));
704                        showFleetInfo = true;
705                        //enemyHasPostCombatOptions = true;
706                        break;
707                case BATTLE_ENEMY_WIN_TOTAL:
708                        addText(getString("battleTotalDefeat"));
709                        showFleetInfo = true;
710                        break;
711                case BATTLE_PLAYER_WIN:
712                        if (config.showVictoryText) {
713                                addText(getString("battleVictory"));
714                        }
715                        showFleetInfo = true;
716                        break;
717                case BATTLE_PLAYER_WIN_TOTAL:
718                        if (config.showVictoryText) {
719                                addText(getString("battleTotalVictory"));
720                        }
721                        showFleetInfo = true;
722                        break;
723                case ESCAPE_ENEMY_LOSS_TOTAL:
724                        if (config.showVictoryText) {
725                                addText(getString("pursuitTotalVictory"));
726                        }
727                        showFleetInfo = true;
728                        break;
729                case ESCAPE_ENEMY_SUCCESS:
730                        if (result.getLoserResult().getDisabled().isEmpty() && result.getLoserResult().getDestroyed().isEmpty()) {
731                                addText(getString("pursuitVictoryNoLosses"));
732                        } else {
733                                addText(getString("pursuitVictoryLosses"));
734                        }
735                        showFleetInfo = true;
736                        break;
737                case ESCAPE_ENEMY_WIN:
738                        addText(getString("pursuitDefeat"));
739                        showFleetInfo = true;
740                        break;
741                case ESCAPE_ENEMY_WIN_TOTAL:
742                        addText(getString("pursuitTotalDefeat"));
743                        showFleetInfo = true;
744                        break;
745                case ESCAPE_PLAYER_LOSS_TOTAL:
746                        addText(getString("escapeTotalDefeat"));
747                        showFleetInfo = true;
748                        break;
749                case ESCAPE_PLAYER_SUCCESS:
750                        addText(getString("escapeDefeat"));
751                        showFleetInfo = true;
752                        break;
753                case ESCAPE_PLAYER_WIN:
754                        addText(getString("escapeVictory"));
755                        showFleetInfo = true;
756                        break;
757                case ESCAPE_PLAYER_WIN_TOTAL:
758                        addText(getString("escapeTotalVictory"));
759                        showFleetInfo = true;
760                        break;
761                case MUTUAL_DESTRUCTION:
762                        addText(getString("engagementMutualDestruction"));
763                        // bit of a hack. this'll make it so that the player's ships have a chance to be repaired
764                        // in the event of mutual destruction by adding them to the enemy fleet side's "disabled enemy ships" list.
765                        // it'll work by using the existing vs-player boarding path
766                        if (mutualDestruction) {
767                                DataForEncounterSide otherData = context.getDataFor(otherFleet);
768                                for (FleetMemberAPI member : result.getLoserResult().getDisabled()) {
769                                        otherData.addEnemy(member, Status.DISABLED);
770                                }
771                        }
772                }
773                
774                EngagementOutcome last = context.getLastEngagementOutcome();
775                if (last == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_LOSS ||
776                                last == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN) {
777                        float recoveryFraction = context.performPostEngagementRecoveryBoth(result);
778                        if (recoveryFraction > 0) {
779                                if (config.showCrRecoveryText) {
780                                        addText(getString("bothRecovery"));
781                                }
782                        }
783                } else {
784                        float recoveryFraction = context.performPostVictoryRecovery(result);
785                        if (recoveryFraction > 0) {
786                                if (config.showCrRecoveryText) {
787                                        if (context.didPlayerWinLastEngagement()) {
788                                                addText(getString("playerRecovery"));
789                                        } else {
790                                                addText(getString("enemyRecovery"));
791                                        }
792                                }
793                        }
794                }
795                
796                if (showFleetInfo) {
797                        //visual.showFleetInfo((String)null, playerFleet, (String)null, otherFleet, context);
798                        showFleetInfo();
799                }
800                
801                addPostBattleAttitudeText();
802                
803                
804                if (config.straightToEngage) {
805                        //optionSelected(null, OptionId.LEAVE);
806                        goToEncounterEndPath();
807                } else {
808                        if (ongoingBattle) {
809                                options.clearOptions();
810                                updateEngagementChoice(true);
811                        } else {
812                                updateMainState(true);
813                        }
814                }
815                
816                if (isFightingOver()) {
817                        if (context.isEngagedInHostilities()) {
818                                context.getDataFor(playerFleet).setDisengaged(!context.didPlayerWinMostRecentBattleOfEncounter());
819                                context.getDataFor(otherFleet).setDisengaged(context.didPlayerWinMostRecentBattleOfEncounter());
820                        }
821                }
822        }
823        
824        protected void addPostBattleAttitudeText() {
825                if (!config.showFleetAttitude) return;
826                
827                if (!ongoingBattle) {
828                        if (!context.wasLastEngagementEscape()) {
829                                boolean otherWantsToRun = otherFleetWantsToDisengage() && otherCanDisengage();
830                                if (!otherWantsToRun) {
831                                        if (context.didPlayerWinLastEngagement()) {
832                                                addText(getString("cleanDisengageOpportunity"), getString("highlightCleanDisengage"), Misc.getPositiveHighlightColor());
833                                        } else if (didEnoughToDisengage(playerFleet)) {
834                                                addText(getString("playerDisruptedEnemy"), getString("highlghtDisruptedEnemy"), Misc.getPositiveHighlightColor());
835                                        }
836                                }
837                        }
838                }
839                if (!isFightingOver()) {
840                        String side = "";
841                        if (context.getBattle() != null && context.getBattle().getNonPlayerSide().size() > 1) {
842                                side = "Side";
843                        }
844                        if (otherFleetWantsToFight()) {
845                                addText(getString("postBattleAggressive" + side));
846                        } else if (otherFleetWantsToDisengage()) {
847                                if (!otherCanDisengage()) {
848                                        addText(getString("postBattleAggressive" + side));
849                                } else {
850                                        addText(getString("postBattleDisengage" + side));
851                                }
852                        } else {
853                                if (otherFleetHoldingVsStrongerEnemy()) {
854                                        addText(getString("postBattleHoldVsStrongerEnemy" + side));
855                                } else {
856                                        addText(getString("postBattleNeutral" + side));
857                                }
858                        }
859                }
860        }
861        
862        public List<FleetMemberAPI> getPursuitCapablePlayerShips() {
863                List<FleetMemberAPI> members = new ArrayList<FleetMemberAPI>();
864                for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
865                        if (member.isAlly()) continue;
866                        if (member.isCivilian()) continue;
867                        members.add(member);
868                }
869                return members;
870        }
871        
872        public void optionSelected(String text, Object optionData) {
873                if (optionData == null) return;
874                
875                // might not be a string if it's the dev-mode "escape to leave" option
876                if (inConversation && optionData instanceof String) {
877                        conversationDelegate.optionSelected(text, optionData);
878                        if (!inConversation) {
879                                //optionSelected(null, OptionId.CUT_COMM);
880                                //optionSelected(null, OptionId.INIT);
881                        }
882                        return;
883                }
884                
885                if (optionData == DumpMemory.OPTION_ID) {
886                        //new DumpMemory().execute(null, dialog, null, conversationDelegate.getMemoryMap());
887                        new DumpMemory().execute(null, dialog, null, getMemoryMap());
888                        return;
889                } else if (DevMenuOptions.isDevOption(optionData)) {
890                        DevMenuOptions.execute(dialog, (String) optionData);
891                        return;
892                }
893                
894                if (optionData instanceof String) {
895                        //??? failsafe
896                        optionSelected(null, OptionId.CUT_COMM);
897                        return;
898                }
899                
900                OptionId option = (OptionId) optionData;
901                
902//              if (option == OptionId.OPEN_COMM) {
903//                      textPanel.clear();
904//              }
905                
906                if (text != null) {
907                        //textPanel.addParagraph(text, Global.getSettings().getColor("buttonText"));
908                        dialog.addOptionSelectedText(option);
909                }
910                
911                switch (option) {
912                case PRINT_ONGOING_BATTLE_INFO:
913                        if (ongoingBattle) {
914                                if (!config.straightToEngage) addText(getString("ongoingBattleEncounter"));
915                                BattleAPI b = context.getBattle();
916                                b.genCombined();
917                                
918                                BattleSide side = b.pickSide(playerFleet);
919                                BattleSide sideAssumingTransponderOn = b.pickSide(playerFleet, false);
920                                
921                                if (!config.straightToEngage) {
922                                        if (side == sideAssumingTransponderOn && side == BattleSide.NO_JOIN) {
923                                                addText(getString("ongoingBattleNoJoin"));
924                                        } else if (side != sideAssumingTransponderOn && side == BattleSide.NO_JOIN) {
925                                                addText(getString("ongoingBattleNoJoinTransponder"));
926                                        } else {
927                                                addText(getString("ongoingBattleShareIFF"));
928                                        }
929                                }
930                        }
931                        break;
932                case INIT:
933                        if (ongoingBattle) {
934                                if (!config.straightToEngage) addText(getString("ongoingBattleEncounter"));
935                                BattleAPI b = context.getBattle();
936                                b.genCombined();
937                                
938                                BattleSide side = b.pickSide(playerFleet);
939                                BattleSide sideAssumingTransponderOn = b.pickSide(playerFleet, false);
940                                
941                                if (!config.straightToEngage) {
942                                        if (side == sideAssumingTransponderOn && side == BattleSide.NO_JOIN) {
943                                                addText(getString("ongoingBattleNoJoin"));
944                                        } else if (side != sideAssumingTransponderOn && side == BattleSide.NO_JOIN) {
945                                                addText(getString("ongoingBattleNoJoinTransponder"));
946                                        } else {
947                                                addText(getString("ongoingBattleShareIFF"));
948                                        }
949                                }
950//                              if (context.getBattle().canJoin(playerFleet)) {
951//                                      BattleSide playerSide = b.pickSide(playerFleet);
952//                                      CampaignFleetAPI prePlayerAllies = b.getCombined(playerSide);
953//                                      CampaignFleetAPI enemies = b.getOtherSideCombined(playerSide);
954//                              }
955                        } else {
956                                boolean hostile = otherFleet.getAI() != null && otherFleet.getAI().isHostileTo(playerFleet);
957                                hostile |= context.isEngagedInHostilities();
958                                if (!skipAttitudeOnInit) {
959                                        String side = "";
960                                        if (context.getBattle() != null && context.getBattle().getNonPlayerSide().size() > 1) {
961                                                side = "Side";
962                                        }
963                                        if (config.showFleetAttitude) {
964                                                boolean hasStation = false;
965                                                boolean allStation = true;
966                                                for (CampaignFleetAPI curr : context.getBattle().getSideFor(otherFleet)) {
967                                                        allStation &= curr.isStationMode();
968                                                        hasStation |= curr.isStationMode();
969                                                }
970                                                if (otherFleetWantsToFight() && !canDisengage() && hasStation && !allStation) {
971                                                        addText(getString("initialWithStationVsLargeFleet"));
972                                                } else if (otherFleetWantsToFight()) {
973                                                        addText(getString("initialAggressive" + side));
974                                                } else if (otherFleetWantsToDisengage()) {
975                                                        if (!otherCanDisengage()) {
976                                                                if (hostile) {
977                                                                        addText(getString("initialNeutral" + side));
978                                                                } else {
979                                                                        addText(getString("initialNeutral" + side));
980                                                                }
981                                                        } else {
982                                                                if (hostile) {
983                                                                        addText(getString("initialDisengage" + side));
984                                                                } else {
985                                                                        addText(getString("initialCareful" + side));
986                                                                }
987                                                        }
988                                                } else {
989                                                        if (otherFleetHoldingVsStrongerEnemy()) {
990                                                                addText(getString("initialHoldVsStrongerEnemy" + side));
991                                                        } else {
992                                                                addText(getString("initialNeutral" + side));
993                                                        }
994                                                }
995                                        }
996                                }
997                                if (!shownKnownStatus && config.showTransponderStatus && !otherFleet.getFaction().isNeutralFaction()) {
998                                        shownKnownStatus = true;
999                                        String side = "";
1000                                        if (context.getBattle() != null && context.getBattle().getNonPlayerSide().size() > 1) {
1001                                                side = "Side";
1002                                        }
1003                                        if (!otherFleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_SKIP_TRANSPONDER_STATUS_INFO)) {
1004                                                //boolean knows = otherFleet.knowsWhoPlayerIs();
1005                                                boolean knows = context.getBattle() != null && context.getBattle().getNonPlayerSide() != null &&
1006                                                                context.getBattle().knowsWhoPlayerIs(context.getBattle().getNonPlayerSide());
1007                                                if (!knows) {
1008                                                        addText(getString("initialDoesntKnow" + side));
1009                                                } else {
1010                                                        CampaignFleetAPI actualPlayer = Global.getSector().getPlayerFleet();
1011                                                        if (actualPlayer.isTransponderOn()) {
1012                                                                addText(getString("initialKnows" + side));
1013                                                        } else {
1014                                                                if (actualPlayer.hasShipsWithUniqueSig()) {
1015                                                                        addText(getString("initialKnowsUnique" + side));
1016                                                                } else {
1017                                                                        addText(getString("initialKnowsTOff" + side));
1018                                                                }
1019                                                        }
1020                                                }
1021                                        }
1022                                }
1023                                //textPanel.highlightFirstInLastPara("neutral posture", HIGHLIGHT_COLOR);
1024                        }
1025                        updateMainState(true);
1026                        break;
1027                case REINIT_CONTINUE:
1028                        reinitPostContinue();
1029                        break;
1030                case INITIATE_BATTLE:
1031                        {
1032//                      BattleAPI b = context.getBattle();
1033//                      b.join(Global.getSector().getPlayerFleet());
1034//                      
1035//                      boolean someJoined = false;
1036//                      CampaignFleetAPI actualPlayer = Global.getSector().getPlayerFleet();
1037//                      for (CampaignFleetAPI fleet : actualPlayer.getContainingLocation().getFleets()) {
1038//                              if (b == fleet.getBattle()) continue;
1039//                              
1040//                              float dist = Misc.getDistance(actualPlayer.getLocation(), fleet.getLocation());
1041//                              dist -= actualPlayer.getRadius();
1042//                              dist -= fleet.getRadius();
1043//                              if (dist < 200 && fleet.getAI() != null && fleet.getAI().wantsToJoin(b)) {
1044//                                      b.join(fleet);
1045//                                      addText(Misc.ucFirst(fleet.getNameWithFaction()) + " will join the battle.");
1046//                                      textPanel.highlightFirstInLastPara(fleet.getNameWithFaction(), fleet.getFaction().getBaseUIColor());
1047//                                      someJoined = true;
1048//                              }
1049//                      }
1050//                      if (!someJoined) {
1051//                              addText("No nearby fleets will join the battle.");
1052//                      }
1053//                      
1054//                      b.genCombined();
1055//                      
1056//                      showFleetInfo();
1057//                      
1058//                      playerFleet = b.getPlayerCombined();
1059//                      otherFleet = b.getNonPlayerCombined();
1060                        
1061                        //updateEngagementChoice(true);
1062                        updateMainState(true);
1063                        }
1064                        break;
1065                case JOIN_ONGOING_BATTLE:
1066                        if (context.getBattle().canJoin(playerFleet)) {
1067                                BattleAPI b = context.getBattle();
1068                                for (CampaignFleetAPI fleet : b.getBothSides()) {
1069                                        fleet.inflateIfNeeded();
1070                                }
1071                                b.genCombined();
1072                                
1073                                BattleSide playerSide = b.pickSide(playerFleet);
1074                                CampaignFleetAPI prePlayerAllies = b.getCombined(playerSide);
1075                                CampaignFleetAPI enemies = b.getOtherSideCombined(playerSide);
1076                                
1077                                boolean alliedWantsToFightBefore = fleetWantsToFight(prePlayerAllies, enemies);
1078                                boolean alliedWantsToDisengageBefore = fleetWantsToDisengage(prePlayerAllies, enemies) && fleetCanDisengage(prePlayerAllies);
1079                                boolean alliedHoldingBefore = fleetHoldingVsStrongerEnemy(prePlayerAllies, enemies);
1080                                boolean otherWantsToFightBefore = fleetWantsToFight(enemies, prePlayerAllies);
1081                                boolean otherWantsToDisengageBefore = fleetWantsToDisengage(enemies, prePlayerAllies) && fleetCanDisengage(enemies);
1082                                boolean otherHoldingBefore = fleetHoldingVsStrongerEnemy(enemies, prePlayerAllies);
1083                                
1084                                //System.out.println("Ships before: " + prePlayerAllies.getFleetData().getMembersListCopy().size());
1085                                
1086                                b.join(playerFleet);
1087                                b.genCombined();
1088                                
1089                                showFleetInfo();
1090                                joinedBattle = true;
1091                                
1092                                playerFleet = b.getPlayerCombined();
1093                                otherFleet = b.getNonPlayerCombined();
1094                                
1095                                //System.out.println("Ships after: " + playerFleet.getFleetData().getMembersListCopy().size());
1096                                boolean alliedWantsToFight = alliedFleetWantsToFight();
1097                                boolean alliedWantsToDisengage = alliedFleetWantsToDisengage() && alliedCanDisengage();
1098                                boolean alliedHolding = alliedFleetHoldingVsStrongerEnemy();
1099                                
1100                                boolean otherWantsToFight = otherFleetWantsToFight();
1101                                boolean otherWantsToDisengage = otherFleetWantsToDisengage() && otherCanDisengage();
1102                                boolean otherHolding = otherFleetHoldingVsStrongerEnemy();
1103                                
1104                                b.takeSnapshots();
1105                                
1106                                options.clearOptions();
1107                                updateEngagementChoice(true);
1108                                if (!allyEngagementChoiceNoBattle) {
1109                                        updatePreCombat();
1110                                }
1111                        } else {
1112                                addText("Failed to join battle; shouldn't happen.");
1113                                updateMainState(true);
1114                        }
1115                        break;
1116                case CONTINUE_ONGOING_BATTLE:
1117                        updatePreCombat();
1118                        break;
1119                case FORCE_ENGAGE:
1120                        if (config.showEngageText) {
1121                                addText(getString("engageForce"));
1122                        }
1123                case ENGAGE:
1124                        //visual.showImagePortion("illustrations", "hound_hangar", 350, 75, 800, 800, 0, 0, 400, 400);
1125                        boolean forceEngage = option == OptionId.FORCE_ENGAGE;
1126                        if (otherFleetWantsToDisengage() && otherCanDisengage() && !forceEngage) {
1127                                playerGoal = FleetGoal.ATTACK;
1128                                otherGoal = FleetGoal.ESCAPE;
1129                                if (config.showEngageText) {
1130                                        addText(getString("engagePursuit"));
1131                                }
1132                        } else {
1133                                playerGoal = FleetGoal.ATTACK;
1134                                otherGoal = FleetGoal.ATTACK;
1135                                if (config.showEngageText) {
1136                                        addText(getString("engageMutual"));
1137                                }
1138                        }
1139                        updatePreCombat();
1140                        break;
1141                case CONTINUE_INTO_BATTLE:
1142//                      if (context.getBattle() == null) {
1143//                              if (otherFleet.getBattle() != null) {
1144//                                      context.setBattle(otherFleet.getBattle());
1145//                              } else {
1146//                                      BattleAPI battle = Global.getFactory().createBattle(playerFleet, otherFleet);
1147//                                      context.setBattle(battle);
1148//                              }
1149//                      }
1150                                
1151                        BattleCreationContext bcc;
1152                        if (config.alwaysAttackVsAttack){
1153                                playerGoal = FleetGoal.ATTACK;
1154                                otherGoal = FleetGoal.ATTACK;
1155                        }
1156                        
1157                        if (context.getBattle() != null) {
1158                                BattleAPI b = context.getBattle();
1159                                
1160                                if (b.isStationInvolved()) {
1161                                        boolean regen = false;
1162                                        if (b.isStationInvolvedOnPlayerSide()) {
1163                                                if (otherGoal == FleetGoal.ESCAPE) {
1164                                                        regen = true;
1165                                                }
1166                                        } else {
1167                                                if (playerGoal == FleetGoal.ESCAPE) {
1168                                                        regen = true;
1169                                                }
1170                                        }
1171                                        
1172                                        if (regen) {
1173                                                b.genCombined(false);
1174                                        }
1175                                }
1176                                
1177                                CampaignFleetAPI combinedPlayer = b.getPlayerCombined();
1178                                CampaignFleetAPI combinedEnemy = b.getNonPlayerCombined();
1179                                
1180//                              playerGoal = null;
1181//                              otherGoal = null;
1182                                
1183                                bcc = new BattleCreationContext(combinedPlayer, playerGoal, combinedEnemy, otherGoal);
1184                                bcc.setPlayerCommandPoints((int) Global.getSector().getPlayerFleet().getCommanderStats().getCommandPoints().getModifiedValue());
1185                                
1186                                if (b.isStationInvolved() && playerGoal != FleetGoal.ESCAPE && otherGoal != FleetGoal.ESCAPE) {
1187                                        bcc.objectivesAllowed = false;
1188                                }
1189                                if (config.delegate != null) {
1190                                        config.delegate.battleContextCreated(dialog, bcc);
1191                                }
1192                                
1193                                if (firstEngagement) {
1194                                        if (playerGoal != FleetGoal.ESCAPE && ongoingBattle) {
1195                                                bcc.setInitialStepSize(1.5f);
1196                                                bcc.setInitialNumSteps(10 + (float) Math.random() * 30);
1197                                        }
1198                                        firstEngagement = false;
1199                                } else {
1200                                        if (playerGoal != FleetGoal.ESCAPE && ongoingBattle) {
1201                                                bcc.setInitialStepSize(1.5f);
1202                                                bcc.setInitialNumSteps(5 + (float) Math.random() * 5);
1203                                        }
1204                                }
1205                        } else {
1206                                bcc = new BattleCreationContext(playerFleet, playerGoal, otherFleet, otherGoal);
1207                                bcc.setPlayerCommandPoints((int) Global.getSector().getPlayerFleet().getCommanderStats().getCommandPoints().getModifiedValue());
1208                                if (config.delegate != null) {
1209                                        config.delegate.battleContextCreated(dialog, bcc);
1210                                }
1211                        }
1212                        
1213                        if (playerGoal == FleetGoal.ESCAPE) {
1214                                //DataForEncounterSide data = context.getDataFor(otherFleet);
1215                                CampaignFleetAIAPI ai = playerFleet.getAI();
1216                                if (ai != null) {
1217                                        ai.performCrashMothballingPriorToEscape(context, otherFleet);
1218                                }
1219                        } else if (otherGoal == FleetGoal.ESCAPE) {
1220                                //DataForEncounterSide data = context.getDataFor(playerFleet);
1221                                CampaignFleetAIAPI ai = otherFleet.getAI();
1222                                if (ai != null) {
1223                                        ai.performCrashMothballingPriorToEscape(context, playerFleet);
1224                                }
1225                        }
1226                        
1227                        visual.fadeVisualOut();
1228                        dialog.startBattle(bcc);
1229                        break;
1230                case CLEAN_DISENGAGE:
1231                case DISENGAGE:
1232//                      CampaignFleetAIAPI ai = otherFleet.getAI();
1233//                      PursuitOption po = otherFleet.getAI().pickPursuitOption(context, playerFleet);
1234                        PursuitOption po = pickPursuitOption(otherFleet, playerFleet, context);
1235                        if (otherFleetHoldingVsStrongerEnemy() || !otherFleetWantsToFight() || canDisengageCleanly(playerFleet)) {
1236                                po = PursuitOption.LET_THEM_GO;
1237                        }
1238                        
1239                        context.applyPursuitOption(otherFleet, playerFleet, po);
1240                        context.getDataFor(playerFleet).setDisengaged(true);
1241                        context.getDataFor(otherFleet).setDisengaged(false);
1242                        switch (po) {
1243                        case PURSUE:
1244                                // shouldn't happen here, or we'd be in ATTEMPT_TO_DISENGAGE
1245                        case HARRY:
1246                                context.applyPursuitOption(otherFleet, playerFleet, PursuitOption.HARRY);
1247                                addText(getString("enemyHarass"));
1248                                context.setEngagedInHostilities(true); // this was commented out, why?
1249                                context.setOtherFleetHarriedPlayer(true);
1250                                context.getDataFor(playerFleet).setDisengaged(true);
1251                                context.getDataFor(otherFleet).setDisengaged(false);
1252                                break;
1253                        case LET_THEM_GO:
1254                                if (canDisengageCleanly(playerFleet)) {
1255                                        context.setEngagedInHostilities(true); // so that other fleets stand down and don't insta-pursue
1256                                        addText(getString("enemyUnableToPursue"));
1257                                } else {
1258                                        addText(getString("enemyDecidesNotToPursue"));
1259                                }
1260                                break;
1261                        }
1262                        updateMainState(true);
1263                        break;
1264                case ATTEMPT_TO_DISENGAGE:
1265                        boolean letGo = true;
1266                        if (otherFleetWantsToFight()) {
1267                                //PursuitOption pursuitOption = otherFleet.getAI().pickPursuitOption(context, playerFleet);
1268                                PursuitOption pursuitOption = pickPursuitOption(otherFleet, playerFleet, context);
1269                                if (pursuitOption == PursuitOption.PURSUE) {
1270                                        playerGoal = FleetGoal.ESCAPE;
1271                                        otherGoal = FleetGoal.ATTACK;
1272                                        addText(getString("enemyPursuit"));
1273                                        letGo = false;
1274                                        updatePreCombat();
1275                                } else if (pursuitOption == PursuitOption.HARRY) {
1276                                        context.applyPursuitOption(otherFleet, playerFleet, PursuitOption.HARRY);
1277                                        addText(getString("enemyHarass"));
1278                                        context.setEngagedInHostilities(true);
1279                                        //context.getDataFor(playerFleet).setDisengaged(!context.isEngagedInHostilities());
1280                                        context.getDataFor(playerFleet).setDisengaged(true);
1281                                        context.getDataFor(otherFleet).setDisengaged(false);
1282                                        updateMainState(true);
1283                                        letGo = false;
1284                                } else {
1285                                        letGo = true;
1286                                }
1287                        }
1288                        if (letGo) {
1289                                //PursueAvailability pa = context.getPursuitAvailability(otherFleet, playerFleet);
1290                                PursueAvailability pa = getPursuitAvailability(otherFleet);
1291                                DisengageHarryAvailability dha = context.getDisengageHarryAvailability(otherFleet, playerFleet);
1292                                if (dha == DisengageHarryAvailability.AVAILABLE || pa == PursueAvailability.AVAILABLE) {
1293                                        addText(getString("enemyDecidesNotToPursue"));
1294                                } else {
1295                                        addText(getString("enemyUnableToPursue"));
1296                                }
1297                                context.getDataFor(playerFleet).setDisengaged(true);
1298                                context.getDataFor(otherFleet).setDisengaged(!context.isEngagedInHostilities());
1299                                updateMainState(true);
1300                        }
1301                        
1302//                      String name = "Corvus III";
1303//                      SectorEntityToken planet = Global.getSector().getStarSystem("Corvus").getEntityByName(name);
1304//                      //planet = Global.getSector().getStarSystem("Corvus").getStar();
1305//                      if (planet != null) {
1306//                              addText("Incoming visual feed from " + name + ".");
1307//                              visual.showPlanetInfo(planet);
1308//                      } else {
1309//                              addText("Planet " + name + " not found in the Corvus system.");
1310//                      }
1311//                      dialog.showTextPanel();
1312                        //dialog.hideTextPanel();
1313                        //dialog.setXOffset(-200);
1314                        break;
1315                case BEGIN_FLEET_ENCOUNTER_2:
1316                        reinitPostContinue();
1317                        break;
1318                case OPEN_COMM:
1319                        CampaignFleetAPI actualOther = (CampaignFleetAPI) (dialog.getInteractionTarget());
1320                        dialog.showTextPanel();
1321                        dialog.flickerStatic(0.1f, 0.1f);
1322                        
1323                        inConversation = true;
1324                        conversationDelegate = new RuleBasedInteractionDialogPluginImpl();
1325                        conversationDelegate.setEmbeddedMode(true);
1326                        conversationDelegate.init(dialog);
1327                        
1328                        dialog.getInteractionTarget().setActivePerson(actualOther.getCommander());
1329                        conversationDelegate.notifyActivePersonChanged();
1330                        
1331                        boolean otherWantsToRun = otherFleetWantsToDisengage() && otherCanDisengage();
1332                        MemoryAPI mem = conversationDelegate.getMemoryMap().get(MemKeys.LOCAL);
1333                        if (otherWantsToRun) {
1334                                mem.unset("$weakerThanPlayerButHolding");
1335                        }
1336                        
1337                        if (!conversationDelegate.fireBest("OpenCommLink")) {
1338                                addText("You try to establish a comm link, but only get static.");
1339                                dialog.getInteractionTarget().setActivePerson(null);
1340                                conversationDelegate.notifyActivePersonChanged();
1341                                inConversation = false;
1342                        }
1343                        if (inConversation && !visual.isShowingPersonInfo(actualOther.getCommander()) &&
1344                                        !dialog.getInteractionTarget().getMemoryWithoutUpdate().getBoolean(DO_NOT_AUTO_SHOW_FC_PORTRAIT)) {
1345                                visual.showPersonInfo(actualOther.getCommander());
1346                        }
1347                        break;
1348                case CUT_COMM:
1349                        dialog.showTextPanel();
1350                        dialog.flickerStatic(0.1f, 0.1f);
1351                        
1352//                      addText(getString("cutComm"));
1353//                      visual.showFleetInfo((String)null, playerFleet, (String)null, otherFleet, context);
1354//                      updateMainState();
1355                        
1356                        inConversation = false;
1357//                      addText(getString("cutComm"));
1358                        //visual.showFleetInfo((String)null, playerFleet, (String)null, otherFleet, context);
1359                        showFleetInfo();
1360                        optionSelected(null, OptionId.INIT);
1361                        
1362                        break;
1363                case PURSUE:
1364                        playerGoal = FleetGoal.ATTACK;
1365                        otherGoal = FleetGoal.ESCAPE;
1366                        addText(getString("pursue"));
1367                        updatePreCombat();
1368                        break;
1369                case AUTORESOLVE_PURSUE:
1370                        List<FleetMemberAPI> members = getPursuitCapablePlayerShips();
1371//                      List<FleetMemberAPI> members = new ArrayList<FleetMemberAPI>();
1372//                      for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
1373//                              if (member.isAlly()) continue;
1374//                              if (member.isCivilian()) continue;
1375//                              members.add(member);
1376//                      }
1377                        dialog.showFleetMemberPickerDialog("Select craft to send in pursuit", "Ok", "Cancel", 
1378                                        4, 8, 58f, false, true, members,
1379                        new FleetMemberPickerListener() {
1380                                public void pickedFleetMembers(List<FleetMemberAPI> members) {
1381                                        if (members != null && !members.isEmpty()) {
1382                                                BattleAutoresolverPluginImpl resolver = new BattleAutoresolverPluginImpl(context.getBattle());
1383                                                resolver.resolvePlayerPursuit(context, members);
1384                                                if (resolver.getResult() != null) {
1385                                                        addText(getString("pursuitAutoresolve"));
1386                                                        if (context.getBattle() != null) {
1387                                                                CampaignFleetAPI player = Global.getSector().getPlayerFleet();
1388                                                                CampaignFleetAPI ally = null;
1389                                                                float alliedFP = 0;
1390                                                                for (CampaignFleetAPI curr : context.getBattle().getPlayerSide()) {
1391                                                                        if (!curr.isPlayerFleet() && !curr.getFleetData().getMembersListCopy().isEmpty() &&
1392                                                                                        !curr.isStationMode()) {
1393                                                                                if (ally == null) ally = curr;
1394                                                                                alliedFP += ally.getFleetPoints();
1395                                                                        }
1396                                                                }
1397                                                                float playerFP = 0f;
1398                                                                for (FleetMemberAPI member : members) {
1399                                                                        playerFP += member.getFleetPointCost();
1400                                                                }
1401                                                                float damage = 0f;
1402                                                                for (FleetMemberAPI member : resolver.getResult().getLoserResult().getDisabled()) {
1403                                                                        damage += member.getFleetPointCost();
1404                                                                }
1405                                                                for (FleetMemberAPI member : resolver.getResult().getLoserResult().getDestroyed()) {
1406                                                                        damage += member.getFleetPointCost();
1407                                                                }
1408                                                                float total = playerFP + alliedFP;
1409                                                                if (total < 1) total = 1;
1410                                                                context.setPlayerFPHullDamageToEnemies(context.getPlayerFPHullDamageToEnemies() + damage * playerFP / total);
1411                                                                if (ally != null && alliedFP > 0) {
1412                                                                        context.setAllyFPHullDamageToEnemies(context.getAllyFPHullDamageToEnemies() + damage * alliedFP / total);
1413                                                                }
1414                                                        }
1415                                                        backFromEngagement(resolver.getResult());
1416                                                }
1417                                        }
1418                                }
1419                                public void cancelledFleetMemberPicking() {
1420                                        
1421                                }
1422                        });
1423                        break;
1424                case CRASH_MOTHBALL:
1425                        List<FleetMemberAPI> choices = getCrashMothballable(playerFleet.getFleetData().getCombatReadyMembersListCopy());
1426                        dialog.showFleetMemberPickerDialog("Select craft to crash-mothball", "Ok", "Cancel", 
1427                                        3, 7, 58f, false, true, choices,
1428                        new FleetMemberPickerListener() {
1429                                public void pickedFleetMembers(List<FleetMemberAPI> members) {
1430                                        for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
1431                                                member.getRepairTracker().setCrashMothballed(false);
1432                                        }
1433                                        if (members != null && !members.isEmpty()) {
1434                                                for (FleetMemberAPI member : members) {
1435                                                        member.getRepairTracker().setCrashMothballed(true);
1436                                                }
1437                                                
1438                                                crashMothballList = createShipNameListString(members);
1439                                                if (members.size() == 1) {
1440                                                        addText(getString("crashMothballSelectedOneShip"));
1441                                                } else {
1442                                                        addText(getString("crashMothballSelectedMultiple"));
1443                                                }
1444                                        }
1445                                }
1446                                public void cancelledFleetMemberPicking() {
1447                                        
1448                                }
1449                        });
1450                        break;
1451                case SCUTTLE:
1452                        break;
1453                case GO_TO_PRE_BATTLE:
1454                        updateEngagementChoice(false);
1455                        break;
1456                case GO_TO_MAIN:
1457                        if (config.straightToEngage) {
1458                                optionSelected(null, OptionId.LEAVE);
1459                                break;
1460                        }
1461                        List<CampaignFleetAPI> playerSide = context.getBattle().getPlayerSide();
1462                        List<CampaignFleetAPI> otherSide = context.getBattle().getNonPlayerSide();
1463                        //context.getBattle().leave(playerFleet);
1464                        if (joinedBattle) {
1465                                //context.getBattle().leave(otherFleet);
1466                                joinedBattle = false;
1467                        }
1468                        if (ongoingBattle) {
1469                                playerFleet = Global.getSector().getPlayerFleet();
1470                                otherFleet = (CampaignFleetAPI) (dialog.getInteractionTarget());
1471                                context.getBattle().leave(playerFleet, context.isEngagedInHostilities() || context.isOtherFleetHarriedPlayer());
1472                                BattleAPI b = context.getBattle();
1473                                String titleOne = b.getPrimary(b.getSideOne()).getNameWithFactionKeepCase();
1474                                if (b.getSideOne().size() > 1) titleOne += ", with allies";
1475                                String titleTwo = b.getPrimary(b.getSideTwo()).getNameWithFactionKeepCase();
1476                                if (b.getSideTwo().size() > 1) titleTwo += ", with allies";
1477                                visual.showPreBattleJoinInfo(null, playerFleet, Misc.ucFirst(titleOne), Misc.ucFirst(titleTwo), context);
1478                        } else {
1479//                              context.getBattle().uncombine();
1480//                              if (playerSide != null) {
1481//                                      for (CampaignFleetAPI curr : new ArrayList<CampaignFleetAPI>(playerSide)) {
1482//                                              if (curr != playerFleet) {
1483//                                                      context.getBattle().leave(curr);
1484//                                              }
1485//                                      }
1486//                              }
1487//                              if (otherSide != null) {
1488//                                      for (CampaignFleetAPI curr : new ArrayList<CampaignFleetAPI>(otherSide)) {
1489//                                              if (curr != otherFleet) {
1490//                                                      context.getBattle().leave(curr);
1491//                                              }
1492//                                      }
1493//                              }
1494//                              showFleetInfo();
1495                        }
1496                        updateMainState(false);
1497                        break;
1498                case CONTINUE:
1499                        visual.showCustomPanel(810, 400, new ExampleCustomUIPanel());
1500                        dialog.hideTextPanel();
1501                        break;
1502                case DEV_MODE_ESCAPE:
1503                        context.applyAfterBattleEffectsIfThereWasABattle();
1504                        
1505                        BattleAPI b = context.getBattle();
1506                        if (b.isPlayerInvolved()) {
1507                                cleanUpBattle();
1508                        }
1509                case LOOT_THEN_LEAVE:
1510                        //goToEncounterEndPath();
1511                        winningPath();
1512                        break;
1513                case LEAVE:
1514                case CONTINUE_LEAVE:
1515                        if (option != OptionId.CONTINUE_LEAVE) {
1516                                if (context.adjustPlayerReputation(dialog, getString("friendlyFireRepLoss"),
1517                                                                                                   config.impactsAllyReputation, config.impactsEnemyReputation)) {
1518                                        options.clearOptions();
1519                                        options.addOption("Continue", OptionId.CONTINUE_LEAVE, null);
1520                                        if (!config.straightToEngage) {
1521                                                options.setShortcut(OptionId.CONTINUE_LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
1522                                        }
1523                                        break;
1524                                }
1525                        }
1526                        if (isFightingOver()) {
1527                                if (!context.hasWinnerAndLoser()) {
1528                                        if (context.getDataFor(playerFleet).isWonLastEngagement()) {
1529                                                context.getDataFor(playerFleet).setDisengaged(false);
1530                                                context.getDataFor(otherFleet).setDisengaged(true);
1531                                        } else {
1532                                                context.getDataFor(playerFleet).setDisengaged(true);
1533                                                context.getDataFor(otherFleet).setDisengaged(false);
1534                                        }
1535                                }
1536                        } else {
1537                                if (context.isEngagedInHostilities()) {
1538                                        context.getDataFor(playerFleet).setDisengaged(true);
1539                                        context.getDataFor(otherFleet).setDisengaged(false);
1540                                } else {
1541                                        context.getDataFor(playerFleet).setDisengaged(true);
1542                                        context.getDataFor(otherFleet).setDisengaged(true);
1543                                }
1544                        }
1545                        
1546                        if (config.printXPToDialog) {
1547                                context.setTextPanelForXPGain(textPanel);
1548                                textPanel.setFontSmallInsignia();
1549                        }
1550                        context.applyAfterBattleEffectsIfThereWasABattle();
1551                        context.setTextPanelForXPGain(null);
1552                        textPanel.setFontInsignia();
1553                        
1554//                      if (config.dismissOnLeave) {
1555//                              Global.getSector().getCampaignUI().addMessage("Game paused");
1556//                      }
1557                        
1558                        cleanUpBattle();
1559//                      context.getBattle().leave(Global.getSector().getPlayerFleet());
1560//                      if (!ongoingBattle) {
1561//                              context.getBattle().finish();
1562//                      }
1563                        
1564                        if (config.dismissOnLeave) {
1565                                dialog.dismiss();
1566                        } else {
1567                                //options.clearOptions();
1568                                dialog.setOptionOnEscape("", null);
1569                                dialog.setOptionOnConfirm("", null);
1570                        }
1571                        if (config.delegate != null) {
1572                                config.delegate.notifyLeave(dialog);
1573                        }
1574                        break;
1575                case HARRY_PURSUE:
1576                        addText(getString("playerHarass"));
1577                        context.applyPursuitOption(playerFleet, otherFleet, PursuitOption.HARRY);
1578                        context.setEngagedInHostilities(true);
1579                        context.getDataFor(playerFleet).setDisengaged(false);
1580                        context.getDataFor(otherFleet).setDisengaged(true);
1581                        context.setEngagedInHostilities(true);
1582                        goToEncounterEndPath();
1583                        break;                  
1584                case LET_THEM_GO:
1585                        addText(getString("playerLetGo"));
1586                        //context.getDataFor(playerFleet).setDisengaged(!context.isEngagedInHostilities());
1587                        context.getDataFor(playerFleet).setDisengaged(false);
1588                        context.getDataFor(otherFleet).setDisengaged(true);
1589                        goToEncounterEndPath();
1590                        break;
1591                case RECOVERY_CONTINUE:
1592                        goToEncounterEndPath();
1593                        break;
1594                case RECOVERY_SELECT:
1595                        if (!recoverableShips.isEmpty() || !storyRecoverableShips.isEmpty()) {
1596                                dialog.showFleetMemberRecoveryDialog("Select ships to recover", 
1597                                                recoverableShips, storyRecoverableShips,
1598                                new FleetMemberPickerListener() {
1599                                        public void pickedFleetMembers(List<FleetMemberAPI> members) {
1600                                                if (members != null && !members.isEmpty()) {
1601                                                        recoveredShips.clear();
1602                                                        recoveredShips.addAll(members);
1603                                                        FleetEncounterContext.recoverShips(members, context, playerFleet, otherFleet);
1604                                                        
1605                                                        ListenerUtil.reportShipsRecovered(members, dialog);
1606                                                        
1607                                                        CampaignFleetAPI player = Global.getSector().getPlayerFleet();
1608                                                        restoreOrigCaptains();
1609                                                        player.getFleetData().sortToMatchOrder(membersInOrderPreEncounter);
1610                                                        
1611                                                        
1612                                                        showFleetInfo();
1613                                                        winningPath();
1614                                                }
1615                                        }
1616                                        public void cancelledFleetMemberPicking() {
1617                                        }
1618                                });
1619                        }
1620                        break;
1621                case CONTINUE_FROM_VICTORY_TRIGGERS:
1622                        winningPath();
1623                        break;
1624                case CONTINUE_LOOT:
1625                        visual.setVisualFade(0, 0);
1626                        dialog.hideTextPanel();
1627                        dialog.hideVisualPanel();
1628                        
1629                        Global.getSector().reportEncounterLootGenerated(context, context.getLoot());
1630                        
1631                        visual.showLoot("Salvaged", context.getLoot(), true, new CoreInteractionListener() {
1632                                public void coreUIDismissed() {
1633                                        if (config.printXPToDialog) {
1634                                                context.setTextPanelForXPGain(textPanel);
1635                                                textPanel.setFontSmallInsignia();
1636                                        }
1637                                        context.applyAfterBattleEffectsIfThereWasABattle();
1638                                        context.setTextPanelForXPGain(null);
1639                                        textPanel.setFontInsignia();
1640//                                      context.getBattle().uncombine();
1641//                                      context.getBattle().leave(Global.getSector().getPlayerFleet());
1642                                        cleanUpBattle();
1643                                        
1644                                        if (config.dismissOnLeave) {
1645                                                dialog.dismiss();
1646                                                dialog.hideTextPanel();
1647                                                dialog.hideVisualPanel();
1648                                        } else {
1649                                                dialog.showTextPanel();
1650                                                dialog.showVisualPanel();
1651                                                //options.clearOptions();
1652                                                dialog.setOptionOnEscape("", null);
1653                                                dialog.setOptionOnConfirm("", null);
1654                                        }
1655                                        if (config.delegate != null) {
1656                                                config.delegate.notifyLeave(dialog);
1657                                        }
1658                                }
1659                        });
1660                        options.clearOptions();
1661                        dialog.setPromptText("");
1662                        //options.addOption("Leave", OptionId.LEAVE, null);
1663                        break;
1664                case CONTINUE_INTO_BOARDING:
1665                        goToEncounterEndPath();
1666                        break;
1667                case BOARDING_ACTION:
1668                        boardingPhase++;
1669                        CampaignFleetAPI sourceFleet = context.getBattle().getSourceFleet(toBoard);
1670                        boardingResult = context.boardShip(toBoard, Global.getSector().getPlayerFleet(), sourceFleet);
1671                        //boardingResult = context.boardShip(toBoard, Global.getSector().getPlayerFleet(), otherFleet);
1672                        goToEncounterEndPath();
1673                        break;
1674                case SELECT_FLAGSHIP:
1675                        members = new ArrayList<FleetMemberAPI>();
1676                        for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
1677//                              if (member.isFighterWing()) continue;
1678//                              if (member.isAlly()) continue;
1679                                if (!isValidTransferCommandTarget(member)) continue;
1680                                members.add(member);
1681                        }
1682                        if (!members.isEmpty()) {
1683                                dialog.showFleetMemberPickerDialog("Select flagship for this engagement", "Ok", "Cancel", 
1684                                                3, 7, 58f, false, false, members,
1685                                new FleetMemberPickerListener() {
1686                                        public void pickedFleetMembers(List<FleetMemberAPI> members) {
1687                                                if (members != null && !members.isEmpty()) {
1688//                                                      if (origFlagship == null) {
1689//                                                              origFlagship = Global.getSector().getPlayerFleet().getFlagship();
1690//                                                              if (origCaptains.isEmpty()) {
1691//                                                                      //origCaptains.clear();
1692//                                                                      for (FleetMemberAPI member : Global.getSector().getPlayerFleet().getFleetData().getMembersListCopy()) {
1693//                                                                              origCaptains.put(member, member.getCaptain());
1694//                                                                      }
1695//                                                              }
1696//                                                      }
1697                                                        if (!members.get(0).isFlagship()) {
1698                                                                restoreOrigCaptains();
1699                                                                
1700                                                                selectedFlagship = members.get(0);
1701                                                                PersonAPI captain = selectedFlagship.getCaptain();
1702                                                                Global.getSector().getPlayerFleet().getFleetData().setFlagship(selectedFlagship);
1703                                                                if (origFlagship != null && captain != null && !captain.isPlayer()) {
1704                                                                        origFlagship.setCaptain(captain);
1705                                                                }
1706                                                                addText(getString("selectedFlagship"));
1707                                                        }
1708                                                }
1709                                        }
1710                                        public void cancelledFleetMemberPicking() {
1711                                                
1712                                        }
1713                                });
1714                        }
1715                        break;
1716                case ENGAGE_BOARDABLE:
1717                        EngageBoardableOutcome outcome = context.engageBoardableShip(toBoard, otherFleet, playerFleet);
1718                        switch (outcome) {
1719                        case DESTROYED:
1720                                addText(getString("engageBoardableDestroyed"));
1721                                break;
1722                        case DISABLED:
1723                                addText(getString("engageBoardableDisabled"));
1724                                break;
1725                        case ESCAPED:
1726                                addText(getString("engageBoardableEscaped"));
1727                                break;
1728                        }
1729                        toBoard = null;
1730                        goToEncounterEndPath();
1731                        break;
1732                case LET_IT_GO:
1733                        context.letBoardableGo(toBoard, otherFleet, playerFleet);
1734                        addText(getString("letBoardableGo"));
1735                        toBoard = null;
1736                        goToEncounterEndPath();
1737                        break;
1738//              case ABORT_BOARDING_ACTION:
1739//                      context.letBoardableGo(toBoard, otherFleet, playerFleet);
1740//                      addText(getString("letBoardableGo"));
1741//                      toBoard = null;
1742//                      goToEncounterEndPath();
1743//                      break;
1744//              case HARD_DOCK:
1745//                      initBoardingParty();
1746//                      if (boardingParty != null) {
1747//                              boardingAttackType = BoardingAttackType.SHIP_TO_SHIP;
1748//                              boardingResult = context.boardShip(toBoard, boardingParty, boardingAttackType, boardingTaskForce, Global.getSector().getPlayerFleet(), otherFleet);
1749//                              goToEncounterEndPath();
1750//                      }
1751//                      break;
1752//              case LAUNCH_ASSAULT_TEAMS:
1753//                      initBoardingParty();
1754//                      if (boardingParty != null) {
1755//                              boardingAttackType = BoardingAttackType.LAUNCH_FROM_DISTANCE;
1756//                              boardingResult = context.boardShip(toBoard, boardingParty, boardingAttackType, boardingTaskForce, Global.getSector().getPlayerFleet(), otherFleet);
1757//                              goToEncounterEndPath();
1758//                      }
1759//                      break;
1760                }
1761        }
1762
1763        protected void rememberWasBeaten() {
1764                if (context.getBattle() == null) return;
1765                
1766                for (CampaignFleetAPI other : context.getBattle().getNonPlayerSide()) {
1767                        MemoryAPI mem = other.getMemoryWithoutUpdate();
1768                        if (!mem.getBoolean(MemFlags.MEMORY_KEY_RECENTLY_DEFEATED_BY_PLAYER)) {
1769                                mem.set(MemFlags.MEMORY_KEY_RECENTLY_DEFEATED_BY_PLAYER, true, 0.5f);
1770                        }
1771                }
1772        }
1773        
1774        protected void restoreOrigCaptains() {
1775                if (origCaptains != null) {
1776                        for (FleetMemberAPI member : origCaptains.keySet()) {
1777                                PersonAPI captain = origCaptains.get(member);
1778                                if (captain != null) {
1779                                        member.setCaptain(captain);
1780                                }
1781                        }
1782                }
1783        }
1784
1785        protected boolean cleanedUp = false;
1786        public void cleanUpBattle() {
1787                if (cleanedUp) return;
1788                cleanedUp = true;
1789                
1790                BattleAPI b = context.getBattle();
1791                
1792                DataForEncounterSide enemyData = context.getDataFor(b.getNonPlayerCombined());
1793                DataForEncounterSide playerData = context.getDataFor(b.getPlayerCombined());
1794                if (enemyData != null && playerData != null && enemyData.disengaged() && !playerData.disengaged()) {
1795                        rememberWasBeaten();
1796                }
1797                
1798                
1799                b.leave(Global.getSector().getPlayerFleet(), context.isEngagedInHostilities() || context.isOtherFleetHarriedPlayer());
1800                
1801                BattleSide playerSide = b.getPlayerSide() == b.getSideOne() ? BattleSide.ONE : BattleSide.TWO;
1802                BattleSide otherSide = b.getPlayerSide() == b.getSideOne() ? BattleSide.TWO : BattleSide.ONE;
1803                
1804                BattleSide winner = context.didPlayerWinMostRecentBattleOfEncounter() ? playerSide : otherSide;
1805                if (!context.isEngagedInHostilities() && !context.isOtherFleetHarriedPlayer()) winner = BattleSide.NO_JOIN;
1806                
1807                if (!ongoingBattle) {
1808                        b.finish(winner, context.isEngagedInHostilities() || context.isOtherFleetHarriedPlayer());
1809                        Global.getSector().getPlayerFleet().getFleetData().setSyncNeeded();
1810                        Global.getSector().getPlayerFleet().getFleetData().syncIfNeeded();
1811                } else if (ongoingBattle) {
1812                        EngagementOutcome last = context.getLastEngagementOutcome();
1813                        boolean finished = false;
1814                        if (last == EngagementOutcome.BATTLE_PLAYER_WIN_TOTAL) {
1815                                List<CampaignFleetAPI> other = b.getSide(otherSide);
1816                                if (other != null && other.size() == 1) {
1817                                        CampaignFleetAPI f = other.get(0);
1818                                        if (f != null && f.isStationMode()) {
1819                                                b.finish(winner, true);
1820                                                finished = true;
1821                                        }
1822                                }
1823                        }
1824                        if (!finished) {
1825                                if (last == EngagementOutcome.ESCAPE_ENEMY_SUCCESS || 
1826                                                last == EngagementOutcome.ESCAPE_PLAYER_SUCCESS ||
1827                                                harryEndedBattle ||
1828                                                context.isOtherFleetHarriedPlayer() ||
1829                                                allyEngagementChoiceNoBattle) {
1830                                        b.finish(winner, true);
1831                                } else {
1832                                        for (CampaignFleetAPI curr : pulledIn) {
1833                                                b.leave(curr, context.isEngagedInHostilities() || context.isOtherFleetHarriedPlayer());
1834                                        }
1835                                }
1836                        }
1837                }
1838                if (context.isEngagedInHostilities()) {
1839                        b.applyVisibilityMod(Global.getSector().getPlayerFleet());
1840                }
1841                
1842        }
1843        
1844        
1845        protected boolean okToLeave = false;
1846        protected boolean didRepairs = false;
1847        protected boolean didBoardingCheck = false;
1848        protected boolean didRecoveryCheck = false;
1849        protected boolean pickedMemberToBoard = false;
1850        protected FleetMemberAPI toBoard = null;
1851        protected String repairedShipList = null;
1852        //protected String boardingTaskForceList = null;
1853        //protected List<FleetMemberAPI> boardingTaskForce = null;
1854        protected int boardingPhase = 0;
1855        protected float boardingPercentSuccess = 0;
1856        
1857        protected String crashMothballList = null;
1858        protected CrewCompositionAPI maxBoardingParty = null;
1859        protected CrewCompositionAPI boardingParty = null;
1860        //protected BoardingAttackType boardingAttackType = null;
1861        protected BoardingResult boardingResult = null;
1862        protected FleetMemberAPI selectedFlagship = null;
1863        protected FleetMemberAPI origFlagship = null;
1864        protected Map<FleetMemberAPI, PersonAPI> origCaptains = new HashMap<FleetMemberAPI, PersonAPI>();
1865        
1866        protected InitialBoardingResponse aiBoardingResponse = null;
1867        
1868        protected boolean shownKnownStatus = false;
1869        
1870        protected void goToEncounterEndPath() {
1871//              boolean otherWantsToRun = otherFleetWantsToDisengage() && otherCanDisengage();
1872//              if (context.didPlayerWinEncounter() ||
1873//                              (config.straightToEngage && 
1874//                                              context.getLastEngagementOutcome() == EngagementOutcome.BATTLE_PLAYER_WIN) ||
1875//                              (otherWantsToRun && 
1876//                                              context.getLastEngagementOutcome() == EngagementOutcome.BATTLE_PLAYER_WIN)) {
1877//                      winningPath();
1878                if (context.didPlayerWinMostRecentBattleOfEncounter() ||
1879                                (config.straightToEngage && 
1880                                                context.getLastEngagementOutcome() == EngagementOutcome.BATTLE_PLAYER_WIN)) {
1881                        winningPath();
1882                } else {
1883                        losingPath();
1884                }
1885        }
1886        
1887        protected void losingPath() {
1888                options.clearOptions();
1889                
1890                context.getDataFor(playerFleet).setDisengaged(true);
1891                
1892                if (!recoveredCrew) {
1893                        recoveredCrew = true;
1894                        context.recoverCrew(otherFleet);
1895                }
1896                
1897                boolean playerHasReadyShips = !playerFleet.getFleetData().getCombatReadyMembersListCopy().isEmpty();
1898                boolean otherHasReadyShips = !otherFleet.getFleetData().getCombatReadyMembersListCopy().isEmpty();
1899                boolean totalDefeat = !playerFleet.isValidPlayerFleet();
1900                boolean mutualDestruction = context.getLastEngagementOutcome() == EngagementOutcome.MUTUAL_DESTRUCTION;
1901//              if (!didBoardingCheck) {
1902//                      didBoardingCheck = true;
1903//                      toBoard = context.pickShipToBoard(otherFleet, playerFleet);
1904//                      if (toBoard != null) {
1905//                              pickedMemberToBoard = true;
1906//                              options.addOption("Continue", OptionId.CONTINUE_INTO_BOARDING, null);
1907//                              return;
1908//                      }
1909//              }
1910                
1911                if (toBoard != null && aiBoardingResponse == null) {
1912                        visual.showFleetMemberInfo(toBoard);
1913                        
1914                        if (mutualDestruction) {
1915                                addText(getString("mutualDestructionRepairs"));
1916                                aiBoardingResponse = InitialBoardingResponse.LET_IT_GO;
1917                        } else {
1918                                if (totalDefeat) {
1919                                        addText(getString("lastFriendlyShipRepairs"));
1920                                } else {
1921                                        addText(getString("friendlyShipBoardable"));
1922                                }
1923                                aiBoardingResponse = otherFleet.getAI().pickBoardingResponse(context, toBoard, playerFleet);
1924                        }
1925                        
1926                        if (!otherHasReadyShips) {
1927                                aiBoardingResponse = InitialBoardingResponse.LET_IT_GO;
1928                        }
1929                        
1930                        options.addOption("Continue", OptionId.CONTINUE_INTO_BOARDING, null);
1931                        return;
1932                }
1933                
1934                if (toBoard != null && aiBoardingResponse != null) {
1935                        switch (aiBoardingResponse) {
1936                        case BOARD:
1937                                break;
1938                        case ENGAGE:
1939                                EngageBoardableOutcome outcome = context.engageBoardableShip(toBoard, playerFleet, otherFleet);
1940                                switch (outcome) {
1941                                case DESTROYED:
1942                                        if (totalDefeat) {
1943                                                addText(getString("lastFriendlyBoardableDestroyed"));
1944                                        } else {
1945                                                addText(getString("engageFriendlyBoardableDestroyed"));
1946                                        }
1947                                        break;
1948                                case DISABLED:
1949                                        if (totalDefeat) {
1950                                                addText(getString("lastFriendlyBoardableDisabled"));
1951                                        } else {
1952                                                addText(getString("engageFriendlyBoardableDisabled"));
1953                                        }
1954                                        break;
1955                                case ESCAPED:
1956                                        if (totalDefeat) {
1957                                                addText(getString("lastFriendlyBoardableEscaped"));
1958                                        } else {
1959                                                addText(getString("engageFriendlyBoardableEscaped"));
1960                                        }
1961                                        break;
1962                                }
1963                                break;
1964                        case LET_IT_GO:
1965                                context.letBoardableGo(toBoard, playerFleet, otherFleet);
1966                                if (!mutualDestruction) {
1967                                        if (totalDefeat) {
1968                                                addText(getString("engageFriendlyBoardableLetGo"));
1969                                        } else {
1970                                                addText(getString("lastFriendlyBoardableLetGo"));
1971                                        }
1972                                }
1973                                break;
1974                        }
1975                }
1976                
1977                totalDefeat = !playerFleet.isValidPlayerFleet();
1978                if (totalDefeat) {
1979                        addText(getString("finalOutcomeNoShipsLeft"));
1980                }
1981                
1982                if (pickedMemberToBoard) {
1983                        //visual.showFleetInfo((String)null, playerFleet, (String)null, otherFleet, context);
1984                        showFleetInfo();
1985                }
1986                
1987                if (config.salvageRandom != null) {
1988                        context.setSalvageRandom(config.salvageRandom);
1989                }
1990                context.generateLoot(null, config.lootCredits);
1991                context.autoLoot();
1992                //context.repairShips();
1993                String leave = "Leave";
1994                if (config.straightToEngage) {
1995                        leave = "Continue";
1996                }
1997                options.addOption(leave, OptionId.LEAVE, null);
1998                if (!config.straightToEngage) {
1999                        options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2000                } else {
2001                        optionSelected(null, OptionId.LEAVE);
2002                }
2003        }
2004        
2005        protected boolean recoveredCrew = false;
2006        protected boolean lootedCredits = false;
2007        protected boolean firedVictoryTriggers = false;
2008        protected String creditsLooted = null;
2009        protected void winningPath() {
2010                options.clearOptions();
2011                DataForEncounterSide playerData = context.getDataFor(playerFleet);
2012                context.getDataFor(otherFleet).setDisengaged(true);
2013                
2014                if (!firedVictoryTriggers && context.getBattle() != null && context.getBattle().getNonPlayerSideSnapshot() != null) {
2015                        
2016                        SectorEntityToken prev = dialog.getInteractionTarget();
2017                        RuleBasedInteractionDialogPluginImpl plugin = new RuleBasedInteractionDialogPluginImpl();
2018                        plugin.setEmbeddedMode(true);
2019                        //plugin.init(dialog);
2020                        dialog.setPlugin(plugin);
2021                        BattleAPI battle = context.getBattle();
2022                        boolean firedAnyTriggers = false;
2023                        
2024                        //for (CampaignFleetAPI other : battle.getNonPlayerSide()) {
2025                        for (CampaignFleetAPI other : battle.getNonPlayerSideSnapshot()) {
2026                                dialog.setInteractionTarget(other);
2027                                plugin.init(dialog);
2028                                
2029                                MemoryAPI mem = other.getMemoryWithoutUpdate();
2030                                List<FleetMemberAPI> losses = Misc.getSnapshotMembersLost(other);
2031                                List<FleetMemberAPI> remaining = other.getFleetData().getMembersListCopy();
2032                                
2033                                int fpTotal = 0;
2034                                int fpLost = 0;
2035                                int fpRemaining = 0;
2036                                for (FleetMemberAPI curr : losses) {
2037                                        fpLost += curr.getFleetPointCost();
2038                                        fpTotal += curr.getFleetPointCost();
2039                                }
2040                                for (FleetMemberAPI curr : remaining) {
2041                                        fpRemaining += curr.getFleetPointCost();
2042                                        fpTotal += curr.getFleetPointCost();
2043                                }
2044                                
2045                                mem.set("$someShipsDestroyed", !losses.isEmpty(), 0);
2046                                mem.set("$fpLost", fpLost, 0);
2047                                mem.set("$fpRemaining", fpRemaining, 0);
2048                                mem.set("$fpLostFraction", (float) fpLost / Math.max(1, fpTotal), 0);
2049                                mem.set("$battle", battle, 0);
2050                                
2051                                List<String> triggers = Misc.getDefeatTriggers(other, false);
2052                                if (triggers != null) {
2053                                        //DebugFlags.PRINT_RULES_DEBUG_INFO = true;
2054                                        for (String trigger : new ArrayList<String>(triggers)) {
2055                                                boolean fired = FireBest.fire(null, dialog, plugin.getMemoryMap(), trigger);
2056                                                if (fired) {
2057                                                        triggers.remove(trigger);
2058                                                        firedAnyTriggers = true;
2059                                                }
2060                                        }
2061                                }
2062                                Misc.clearDefeatTriggersIfNeeded(other);
2063                        }
2064                        
2065                        dialog.setInteractionTarget(prev);
2066                        dialog.setPlugin(this);
2067                        firedVictoryTriggers = true;
2068                        
2069                        if (firedAnyTriggers) {
2070                                options.addOption("Continue", OptionId.CONTINUE_FROM_VICTORY_TRIGGERS, null);
2071                                return;
2072                        }
2073                }
2074                
2075                if (!recoveredCrew) {
2076                        recoveredCrew = true;
2077                        if ((int)playerData.getRecoverableCrewLosses().getCrewInt() + (int)playerData.getRecoverableCrewLosses().getMarines() > 0) {
2078                                addText(getString("recoveryReport"));
2079                                DataForEncounterSide data = context.getDataFor(playerFleet);
2080                                int crewRecovered = (int) data.getRecoverableCrewLosses().getCrew();
2081                                int marinesRecovered = (int) data.getRecoverableCrewLosses().getMarines();
2082                                String crewRecStr = "" + crewRecovered;
2083                                if (crewRecovered <= 0) {
2084                                        crewRecStr = "";
2085                                }
2086                                String marinesRecStr = "" + marinesRecovered;
2087                                if (marinesRecovered <= 0) {
2088                                        marinesRecStr = "";
2089                                }
2090                                //if (crewRecStr != null && marinesRecStr != null) {
2091                                        textPanel.highlightInLastPara(HIGHLIGHT_COLOR, crewRecStr, marinesRecStr);
2092                                //} else if (crewRecStr != null) {
2093                                        //textPanel.highlightInLastPara(HIGHLIGHT_COLOR, crewRecStr);
2094                                //} else if (marinesRecStr != null) {
2095                                        //textPanel.highlightInLastPara(HIGHLIGHT_COLOR, marinesRecStr);
2096                                //}
2097                                
2098                                context.recoverCrew(playerFleet);
2099                        }
2100                }
2101                
2102                CampaignFleetAPI actualPlayer = Global.getSector().getPlayerFleet();
2103                
2104                boolean playerHasPersonnel = actualPlayer.getCargo().getMarines() > 0;
2105                boolean playerHasReadyShips = !actualPlayer.getFleetData().getCombatReadyMembersListCopy().isEmpty();
2106
2107                
2108                if (!didRecoveryCheck) {
2109                        didRecoveryCheck = true;
2110                        recoverableShips = context.getRecoverableShips(context.getBattle(), playerFleet, otherFleet);
2111                        storyRecoverableShips = context.getStoryRecoverableShips();
2112                        
2113                        if (recoverableShips == null) recoverableShips = new ArrayList<FleetMemberAPI>();
2114                        if (storyRecoverableShips == null) storyRecoverableShips = new ArrayList<FleetMemberAPI>();
2115                        
2116                        if (!recoverableShips.isEmpty() || !storyRecoverableShips.isEmpty()) {
2117                                int crew = actualPlayer.getCargo().getCrew();
2118                                int needed = (int)actualPlayer.getFleetData().getMinCrew();
2119                                
2120                                int num = recoverableShips.size() + storyRecoverableShips.size();
2121                                String numString = "several ships disabled or destroyed";
2122                                if (num == 1) numString = "a ship disabled";
2123                                String pre = "The salvage chief reports that " + numString + " during the battle " +
2124                                                         "can be restored to basic functionalty. Recovering ships instead of breaking " +
2125                                                         "them for salvage will greatly reduce the salvage gained from these ships.";
2126                                textPanel.addParagraph(pre);
2127//                              if (extra > 0) {
2128//                                      textPanel.addPara(pre + 
2129//                                                      "You have %s extra crew available, beyond what's " +
2130//                                                      "already required to operate your current ships.", Misc.getHighlightColor(), "" + extra);
2131//                              } else {
2132//                                      textPanel.addParagraph(pre + 
2133//                                                      "You have no extra crew available for any recovered vessels, beyond what's " +
2134//                                                      "already required to operate your current ships.");
2135//                              }
2136                                
2137                                boolean playerShipsRecoverable = false;
2138                                for (FleetMemberAPI member : recoverableShips) {
2139                                        if (member.getOwner() == 0 && !member.isAlly()) {
2140                                                playerShipsRecoverable = true;
2141                                                break;
2142                                        }
2143                                }
2144                                for (FleetMemberAPI member : storyRecoverableShips) {
2145                                        if (member.getOwner() == 0 && !member.isAlly()) {
2146                                                playerShipsRecoverable = true;
2147                                                break;
2148                                        }
2149                                }
2150                                
2151                                boolean onlyDifficultRecovery = recoverableShips.isEmpty() && !storyRecoverableShips.isEmpty();
2152                                if (playerShipsRecoverable) {
2153                                        textPanel.setFontSmallInsignia();
2154                                        textPanel.addParagraph( "Disabled ships from your fleet are available for recovery", Misc.getHighlightColor());
2155                                        textPanel.setFontInsignia();
2156                                        options.addOption("Consider ship recovery", OptionId.RECOVERY_SELECT, Misc.getHighlightColor(),
2157                                                        "Disabled ships from your fleet are available for recovery.");
2158                                } else {
2159                                        Color color = Misc.getButtonTextColor();
2160                                        if (onlyDifficultRecovery) {
2161                                                color = Misc.getStoryOptionColor();
2162                                        }
2163                                        options.addOption("Consider ship recovery", OptionId.RECOVERY_SELECT, color, null);
2164                                }
2165                                
2166                                options.addOption("Continue", OptionId.RECOVERY_CONTINUE, null);
2167                                if (playerShipsRecoverable) {
2168                                        options.addOptionConfirmation(OptionId.RECOVERY_CONTINUE, 
2169                                                        "Disabled ships from your fleet are available for recovery.\n\nIf you proceed without recovering them, "
2170                                                        + "they will be lost permanently.", "Proceed", "Cancel");
2171                                }
2172                                
2173                                return;
2174                        }
2175                }
2176                
2177                
2178                
2179                context.adjustPlayerReputation(dialog, getString("friendlyFireRepLoss"),
2180                                                                           config.impactsAllyReputation, config.impactsEnemyReputation);
2181                
2182                
2183//              "noSalvageReport":"There's no salvage to be had.",
2184//              "noSalvageReportPlayerDidNothing":"Your $fleetOrShip does not participate in salvage operations due to its limited contributions throughout the encounter.",
2185//              "salvageReportPlayer":"Your $fleetOrShip is able to participate in salvage operations due to its contributions throughout the encounter.",
2186                boolean validFleet = playerFleet.isValidPlayerFleet();
2187                BattleAPI battle = context.getBattle();
2188                boolean hasAllies = false;
2189                boolean startedWithAllies = false;
2190                if (battle != null) {
2191                        hasAllies = context.getBattle().getPlayerSide().size() <= 1;
2192                        startedWithAllies = context.getBattle().getPlayerSideSnapshot().size() > 1;
2193                }
2194                
2195                if (!lootedCredits && config.withSalvage) {
2196                        Random resetSalvageRandomTo = null;
2197                        
2198                        if (config.salvageRandom != null) {
2199                                context.setSalvageRandom(config.salvageRandom);
2200                                resetSalvageRandomTo = Misc.getRandom(config.salvageRandom.nextLong(), 11);
2201                        }
2202                        
2203                        context.generateLoot(recoveredShips, config.lootCredits);
2204                        
2205                        if (resetSalvageRandomTo != null) {
2206                                config.salvageRandom = resetSalvageRandomTo;
2207                        }
2208                        if (config.delegate != null) {
2209                                config.delegate.postPlayerSalvageGeneration(dialog, context, context.getLoot());
2210                        }
2211                        lootedCredits = true;
2212                        
2213                        float credits = context.getCreditsLooted();
2214                        if (context.isEngagedInHostilities() && context.getLastEngagementOutcome() != null) {
2215                                if (validFleet) {
2216                                        if (credits <= 0 && context.getLoot().isEmpty()) {
2217                                                if (startedWithAllies) {
2218                                                        addText(getString("noSalvageReportPlayerDidNothing"));
2219                                                } else {
2220                                                        addText(getString("noSalvageReport"));
2221                                                }
2222                                        } else {
2223                                                if (startedWithAllies) {
2224                                                        addText(getString("salvageReportPlayer"));
2225                                                }
2226                                        }
2227                                }
2228                        }
2229                        
2230                        //creditsLooted = "" + (int) credits;
2231                        creditsLooted = Misc.getWithDGS((int)credits);
2232                        if (credits > 0 && validFleet) {
2233                                addText(getString("creditsLootedReport"));
2234                                textPanel.highlightLastInLastPara(creditsLooted, HIGHLIGHT_COLOR);
2235                                Global.getSector().getPlayerFleet().getCargo().getCredits().add(credits);
2236                                
2237//                              PromoteOfficerIntel intel = new PromoteOfficerIntel(textPanel);
2238//                              Global.getSector().getIntelManager().addIntel(intel, false, textPanel);
2239                        }
2240                }
2241                
2242                if (!context.getLoot().isEmpty() && validFleet) {
2243                        options.addOption("Pick through the wreckage", OptionId.CONTINUE_LOOT, null);
2244                } else {
2245                        if (!validFleet) {
2246                                addText(getString("finalOutcomeNoShipsLeft"));
2247                        }
2248                        String leave = "Leave";
2249                        boolean withEscape = true;
2250                        if (config.noSalvageLeaveOptionText != null && validFleet && context.getLoot().isEmpty()) {
2251                                leave = config.noSalvageLeaveOptionText;
2252                                withEscape = false;
2253                        }
2254                        options.addOption(leave, OptionId.LEAVE, null);
2255                        if (withEscape) {
2256                                options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2257                        }
2258                }
2259        }
2260        
2261        protected List<FleetMemberAPI> getCrashMothballable(List<FleetMemberAPI> all) {
2262                List<FleetMemberAPI> result = new ArrayList<FleetMemberAPI>();
2263                CombatReadinessPlugin crPlugin = Global.getSettings().getCRPlugin();
2264                for (FleetMemberAPI member : all) {
2265                        if (member.isAlly()) continue;
2266                        if (!member.isFighterWing() && member.getRepairTracker().getCR() < crPlugin.getMalfunctionThreshold(member.getStats())) {
2267                                result.add(member);
2268                        }
2269                }
2270                return result;
2271        }
2272
2273        
2274        protected OptionId lastOptionMousedOver = null;
2275        public void optionMousedOver(String optionText, Object optionData) {
2276                
2277                if (inConversation) {
2278                        conversationDelegate.optionMousedOver(optionText, optionData);
2279                        return;
2280                }
2281                
2282                if (optionData instanceof String) return;
2283                
2284                if (optionData == null) {
2285                        if (currVisualType != VisualType.FLEET_INFO) {
2286                                showFleetInfo();
2287                                currVisualType = VisualType.FLEET_INFO;
2288                        }
2289                        lastOptionMousedOver = null;
2290                        return;
2291                }
2292                OptionId option = (OptionId) optionData;
2293                if (option == lastOptionMousedOver) return;
2294                lastOptionMousedOver = option;
2295        }
2296        
2297        protected void showFleetInfo() {
2298                BattleAPI b = context.getBattle();
2299                if (b != null && b.isPlayerInvolved()) {
2300                        String titleOne = "Your forces";
2301                        if (b.isPlayerInvolved() && b.getPlayerSide().size() > 1) {
2302                                titleOne += ", with allies";
2303                        }
2304                        if (!Global.getSector().getPlayerFleet().isValidPlayerFleet() && b.getPlayerSide().size() > 1) {
2305                                titleOne = "Allied forces";
2306                        }
2307                        String titleTwo = null;
2308                        if (b.getPrimary(b.getNonPlayerSide()) != null) {
2309                                titleTwo = b.getPrimary(b.getNonPlayerSide()).getNameWithFactionKeepCase();
2310                        }
2311                        if (b.getNonPlayerSide().size() > 1) titleTwo += ", with allies";
2312                        visual.showFleetInfo(titleOne, b.getPlayerCombined(), Misc.ucFirst(titleTwo), b.getNonPlayerCombined(), context);
2313                } else {
2314                        visual.showFleetInfo((String)null, playerFleet, (String)null, otherFleet, context);
2315                }
2316        }
2317        
2318        public void advance(float amount) {
2319                
2320        }
2321        
2322        protected void addText(String text) {
2323                textPanel.addParagraph(text);
2324        }
2325        protected void addText(String text, Color color) {
2326                textPanel.addParagraph(text, color);
2327        }
2328        protected void addText(String text, String hl, Color hlColor) {
2329                LabelAPI label = textPanel.addParagraph(text);
2330                label.setHighlight(hl);
2331                label.setHighlightColor(hlColor);
2332        }
2333        
2334        protected void appendText(String text) {
2335                textPanel.appendToLastParagraph(" " + text);
2336        }
2337        
2338        protected void updateDialogState() {
2339                options.clearOptions();
2340                options.addOption("Cut the comm link", OptionId.CUT_COMM, null);
2341        }
2342        
2343        protected boolean isValidTransferCommandTarget(FleetMemberAPI member) {
2344                if (member.isFighterWing() || member.isAlly()) return false;
2345                if (Misc.isAutomated(member)) return false;
2346                if (Misc.isUnremovable(member.getCaptain())) return false;
2347                return true;
2348        }
2349        
2350        protected void updatePreCombat() {
2351
2352                if (!context.isComputedDifficulty()) {
2353                        context.computeBattleDifficulty();
2354                }
2355                float diff = context.getDifficulty();
2356                if (diff >= 1f) {
2357                        String percent = "+" + (int) Math.round((diff - 1f) * 100f) + "%";
2358                        textPanel.setFontSmallInsignia();
2359                        textPanel.addPara("Additional XP due to overall battle difficulty: %s",
2360                                        Misc.getGrayColor(), Misc.getHighlightColor(), percent);
2361                        textPanel.setFontInsignia();
2362                }
2363                
2364                
2365                options.clearOptions();
2366                
2367                //playerFleet.updateCounts();
2368                //int nonFighters = playerFleet.getFleetData().getMembersListCopy().size() - playerFleet.getNumFighters();
2369                boolean canTransfer = false;
2370                for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
2371//                      if (member.isFighterWing() || member.isAlly()) continue;
2372                        if (member.isFlagship()) continue;
2373                        if (!isValidTransferCommandTarget(member)) continue;
2374                        canTransfer = true;
2375                        break;
2376                }
2377                if (playerGoal == FleetGoal.ATTACK && otherGoal == FleetGoal.ESCAPE) {
2378                        String tooltipText = getString("tooltipPursueAutoresolve");
2379                        options.addOption("Order your second-in-command to handle it", OptionId.AUTORESOLVE_PURSUE, tooltipText);
2380                        options.addOption("Transfer command for this engagement", OptionId.SELECT_FLAGSHIP, getString("tooltipSelectFlagship"));
2381                        //if (nonFighters <= 1) {
2382                        if (!canTransfer) {
2383                                options.setEnabled(OptionId.SELECT_FLAGSHIP, false);
2384                        }
2385                        options.addOption("Take command of the action", OptionId.CONTINUE_INTO_BATTLE, null);
2386                } else {
2387                        options.addOption("Transfer command for this engagement", OptionId.SELECT_FLAGSHIP, getString("tooltipSelectFlagship"));
2388                        //if (nonFighters <= 1) {
2389                        if (!canTransfer) {
2390                                options.setEnabled(OptionId.SELECT_FLAGSHIP, false);
2391                        }
2392                        if (playerGoal == FleetGoal.ESCAPE) {
2393                                List<FleetMemberAPI> choices = getCrashMothballable(playerFleet.getFleetData().getCombatReadyMembersListCopy());
2394                                
2395                                options.addOption("Crash-mothball some of your ships to prevent malfunctions", OptionId.CRASH_MOTHBALL, null);
2396                                if (choices.isEmpty()) {
2397                                        options.setEnabled(OptionId.CRASH_MOTHBALL, false);
2398                                        options.setTooltip(OptionId.CRASH_MOTHBALL, getString("tooltipCrashMothballUnavailable"));
2399                                } else {
2400                                        options.setTooltip(OptionId.CRASH_MOTHBALL, getString("tooltipCrashMothball"));
2401                                }
2402                        }
2403                        if (config.straightToEngage) {
2404                                options.addOption("Continue into battle", OptionId.CONTINUE_INTO_BATTLE, null);
2405                        } else {
2406                                options.addOption("Continue", OptionId.CONTINUE_INTO_BATTLE, null);
2407                        }
2408                }
2409                
2410                boolean canGoBack = ongoingBattle || otherGoal == FleetGoal.ESCAPE || Global.getSettings().isDevMode();
2411                if (canGoBack) {
2412                        options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2413                        options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2414                }
2415//              if (ongoingBattle) {
2416//                      options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2417//                      options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2418//              }
2419                if (Global.getSettings().isDevMode()) {
2420//                      if (ongoingBattle) {
2421////                            options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2422////                            options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2423//                      } else {
2424//                              options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2425//                              options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2426//                      }
2427                        DevMenuOptions.addOptions(dialog);
2428                }
2429        }
2430        
2431        protected String createShipNameListString(List<FleetMemberAPI> members) {
2432                String str = "";
2433                int fighters = 0;
2434                int ships = 0;
2435                for (FleetMemberAPI member : members) {
2436                        boolean last = members.indexOf(member) == members.size() - 1;
2437                        boolean secondToLast = members.indexOf(member) == members.size() - 2;
2438                        boolean fighter = member.isFighterWing();
2439                        if (fighter) {
2440                                fighters++;
2441                        } else {
2442                                ships++;
2443                                if (last && fighters == 0 && ships > 1) {
2444                                        if (members.size() > 2) {
2445                                                str += ", and the " + member.getShipName();
2446                                        } else {
2447                                                str += " and the " + member.getShipName();
2448                                        }
2449                                } else {
2450                                        str += "the " + member.getShipName();
2451                                }
2452                        }
2453                        if (!last && !secondToLast && !fighter) {
2454                                str += ", ";
2455                        } 
2456                        
2457                        if (last && fighters > 0) {
2458                                if (fighters == 1) {
2459                                        if (ships == 0) {
2460                                                str += "a fighter wing";
2461                                        } else {
2462                                                if (ships > 1) {
2463                                                        str += ", and a fighter wing";
2464                                                } else {
2465                                                        str += " and a fighter wing";
2466                                                }
2467                                        }
2468                                } else {
2469                                        if (ships == 0) {
2470                                                str += "several fighter wings";
2471                                        } else {
2472                                                if (ships > 1) {
2473                                                        str += ", and several fighter wings";
2474                                                } else {
2475                                                        str += " and several fighter wings";
2476                                                }
2477                                        }
2478                                }
2479                        }
2480                }
2481                return str;
2482        }
2483        
2484        protected void updateMainState(boolean withText) {
2485                options.clearOptions();
2486                
2487                if (isFightingOver()) {
2488                        goToEncounterEndPath();
2489                        return;
2490                }
2491                
2492                if (ongoingBattle) {
2493                        BattleAPI battle = context.getBattle();
2494                        boolean playerHasReadyShips = false;
2495                        for (FleetMemberAPI member : playerFleet.getFleetData().getCombatReadyMembersListCopy()) {
2496                                if (!member.isAlly()) {
2497                                        playerHasReadyShips = true;
2498                                }
2499                        }
2500                        if (!joinedBattle && battle.canJoin(playerFleet)) {
2501                                options.addOption("Join the battle", OptionId.JOIN_ONGOING_BATTLE, null);
2502                                
2503                                BattleSide side = battle.pickSide(playerFleet);
2504                                if (side != null) {
2505                                        List<CampaignFleetAPI> otherSide = battle.getOtherSide(side);
2506                                        if (otherSide != null) {
2507                                                boolean knows = battle.knowsWhoPlayerIs(otherSide);
2508                                                boolean lowImpact = false;
2509                                                CampaignFleetAPI otherPrimary = battle.getPrimary(otherSide);
2510                                                if (otherPrimary != null) {
2511                                                        lowImpact |= otherPrimary.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_LOW_REP_IMPACT) == true;
2512                                                        lowImpact |= otherPrimary.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_NO_REP_IMPACT) == true;
2513                                                }
2514                                                FactionAPI nonHostile = getNonHostileOtherFaction(otherSide);
2515                                                if (nonHostile != null && knows && !lowImpact && !context.isEngagedInHostilities() &&
2516                                                                config.showWarningDialogWhenNotHostile) {
2517                                                        options.addOptionConfirmation(OptionId.JOIN_ONGOING_BATTLE, "The " + nonHostile.getDisplayNameLong() + " " + nonHostile.getDisplayNameIsOrAre() + " not currently hostile, and you have been positively identified. Are you sure you want to engage in hostilities with one of their fleets?", "Yes", "Never mind");
2518                                                }
2519                                        }
2520                                }
2521                                
2522                                if (!playerHasReadyShips) {
2523                                        options.setEnabled(OptionId.JOIN_ONGOING_BATTLE, false);
2524                                }
2525                        }
2526                        
2527                        options.addOption("Leave", OptionId.LEAVE, null);
2528                        options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2529                        if (Global.getSettings().isDevMode()) {
2530                                DevMenuOptions.addOptions(dialog);
2531                        }
2532                } else {
2533                        if (config.showCommLinkOption) {
2534                                if (otherFleet.getMemoryWithoutUpdate().is("$hailing", true)) {
2535                                        options.addOption("Accept the comm request", OptionId.OPEN_COMM, Misc.getHighlightedOptionColor(), null);
2536                                        otherFleet.getMemoryWithoutUpdate().unset("$hailing");
2537                                } else if (otherFleet.getMemoryWithoutUpdate().is("$highlightComms", true)) {
2538                                        options.addOption("Open a comm link", OptionId.OPEN_COMM, Misc.getHighlightedOptionColor(), null);
2539                                        otherFleet.getMemoryWithoutUpdate().unset("$highlightComms");
2540                                } else {
2541                                        options.addOption("Open a comm link", OptionId.OPEN_COMM, null);
2542                                }
2543                        }
2544                
2545                        boolean smuggler = otherFleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_SMUGGLER);
2546                        if (otherFleet.getFaction().isPlayerFaction() && !smuggler) {
2547                                options.addOption("Leave", OptionId.LEAVE, null);
2548                                options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2549                        } else {
2550                                updateEngagementChoice(withText);
2551                        }
2552                }
2553                
2554        }
2555        
2556        
2557        protected boolean allyEngagementChoiceNoBattle = false;
2558        protected boolean harryEndedBattle = false;
2559        private List<FleetMemberAPI> recoverableShips;
2560        private List<FleetMemberAPI> storyRecoverableShips;
2561        private List<FleetMemberAPI> recoveredShips = new ArrayList<FleetMemberAPI>();
2562        protected void updateEngagementChoice(boolean withText) {
2563                allyEngagementChoiceNoBattle = false;
2564                //options.clearOptions();
2565                if (isFightingOver()) {
2566                        goToEncounterEndPath();
2567                        return;
2568                }
2569                //options.clearOptions();
2570                
2571                BattleAPI b = context.getBattle();
2572
2573                if (ongoingBattle && b.getPlayerSide() != null && b.getPlayerSide().size() <= 1) {
2574                //if (ongoingBattle && b.getPlayerSide() != null && b.isPlayerPrimary()) {
2575                        ongoingBattle = false;
2576                        if (config.showCommLinkOption) {
2577                                options.addOption("Open a comm link", OptionId.OPEN_COMM, null);
2578                        }
2579                }
2580        
2581                playerGoal = null;
2582                otherGoal = null;
2583                
2584                boolean alliedWantsToFight = alliedFleetWantsToFight();
2585                boolean alliedWantsToRun = alliedFleetWantsToDisengage() && alliedCanDisengage();
2586                boolean alliedHolding = alliedFleetHoldingVsStrongerEnemy();
2587                boolean otherWantsToFight = otherFleetWantsToFight();
2588                boolean otherWantsToRun = otherFleetWantsToDisengage() && otherCanDisengage();
2589                //otherWantsToRun = otherFleetWantsToDisengage() && otherCanDisengage();
2590                boolean otherHolding = otherFleetHoldingVsStrongerEnemy();
2591                
2592                //boolean otherWantsToRun = otherFleetWantsToDisengage() && otherCanDisengage();
2593                boolean playerHasReadyShips = false;
2594                boolean allyHasReadyShips = false;
2595                for (FleetMemberAPI member : playerFleet.getFleetData().getCombatReadyMembersListCopy()) {
2596                        if (member.isAlly() && !member.isStation()) {
2597                                allyHasReadyShips = true;
2598                        } else {
2599                                playerHasReadyShips = true;
2600                        }
2601                }
2602                
2603                if (otherWantsToRun && canDisengageCleanly(otherFleet)) {
2604//                      if (didEnoughToDisengage(otherFleet)) {
2605//                              if (context.getBattle().getPlayerSide().size() > 1) {
2606//                                      if (withText) addText(getString("enemyDisruptedPlayerSide"), Misc.getNegativeHighlightColor());
2607//                              } else {
2608//                                      if (withText) addText(getString("enemyDisruptedPlayer"), Misc.getNegativeHighlightColor());
2609//                              }
2610//                      } else {
2611                                if (context.getBattle().getPlayerSide().size() > 1) {
2612                                        if (withText) addText(getString("enemyCleanDisengageSide"));
2613                                } else {
2614                                        if (withText) addText(getString("enemyCleanDisengage"));
2615                                }
2616//                      }
2617                        goToEncounterEndPath();
2618                } else if (otherWantsToRun) {
2619                        String pursueTooltip = "tooltipPursue";
2620                        String harassTooltip = "tooltipHarassRetreat";
2621                        String letThemGoTooltip = "tooltipLetThemGo";
2622                        if (!context.isEngagedInHostilities()) {
2623                                letThemGoTooltip = "tooltipLetThemGoNoPenalty";
2624                        }
2625                        
2626                        boolean canPursue = false;
2627                        boolean canHasass = false;
2628                        //PursueAvailability pa = context.getPursuitAvailability(playerFleet, otherFleet);
2629                        PursueAvailability pa = getPursuitAvailability(playerFleet);
2630                        //List<FleetMemberAPI> members = getPursuitCapablePlayerShips();
2631                        //if (members.isEmpty()) pa = PursueAvailability.NO_READY_SHIPS;
2632                        
2633                        DisengageHarryAvailability dha = context.getDisengageHarryAvailability(playerFleet, otherFleet);
2634                        
2635                        switch (pa) {
2636                        case AVAILABLE:
2637                                canPursue = true;
2638                                break;
2639                        case LOST_LAST_ENGAGEMENT:
2640                                pursueTooltip = "tooltipPursueLostLast";
2641                                break;
2642                        case NO_READY_SHIPS:
2643                                pursueTooltip = "tooltipNoReadyShips";
2644                                break;
2645                        case TOOK_SERIOUS_LOSSES:
2646                                if (context.getBattle().getPlayerSide().size() > 1) {
2647                                        if (withText) addText(getString("enemyDisruptedPlayerSide"), getString("highlightDisruptedPlayer"), Misc.getNegativeHighlightColor());
2648                                } else {
2649                                        if (withText) addText(getString("enemyDisruptedPlayer"), getString("highlightDisruptedPlayer"), Misc.getNegativeHighlightColor());
2650                                }
2651                                pursueTooltip = "tooltipPursueSeriousLosses";
2652                                break;
2653                        case TOO_SLOW:
2654                                pursueTooltip = "tooltipPursueTooSlow";
2655                                break;
2656                        }
2657                        
2658                        switch (dha) {
2659                        case AVAILABLE:
2660                                canHasass = true;
2661                                break;
2662                        case NO_READY_SHIPS:
2663                                harassTooltip = "tooltipNoReadyShips";
2664                                break;
2665                        }
2666                        
2667                        if (ongoingBattle) {
2668                                boolean station = false;
2669                                if (playerFleet != null) {
2670                                        for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
2671                                                if (member.isStation()) {
2672                                                        station = true;
2673                                                        break;
2674                                                }
2675                                        }
2676                                }
2677                                
2678                                //boolean letGo = (!canPursue && !canHasass) || !allyHasReadyShips || station;
2679                                boolean letGo = (!canPursue && !canHasass) || !allyHasReadyShips;// || station;
2680                                if (station) { // make it so the player can decide to pursue
2681                                        letGo = false;
2682                                        alliedWantsToFight = true;
2683                                }
2684                                //if (!letGo) {
2685                                        //PursuitOption po = playerFleet.getAI().pickPursuitOption(context, otherFleet);
2686                                        PursuitOption po = pickPursuitOption(playerFleet, otherFleet, context);
2687                                        po = PursuitOption.PURSUE;
2688                                        if (alliedWantsToRun || alliedHolding || !alliedWantsToFight || letGo) {
2689                                                po = PursuitOption.LET_THEM_GO;
2690                                        }
2691                                        if (!canPursue && canHasass) {
2692                                                po = PursuitOption.HARRY;
2693                                        }
2694                                        //po = PursuitOption.LET_THEM_GO;
2695                                        //po = PursuitOption.HARRY;
2696                                        switch (po) {
2697                                        case PURSUE:
2698                                                if (withText) addText(getString("ongoingBattlePursue"));
2699                                                playerGoal = FleetGoal.ATTACK;
2700                                                otherGoal = FleetGoal.ESCAPE;
2701                                                options.addOption("Join the pursuit", OptionId.CONTINUE_ONGOING_BATTLE, getString(pursueTooltip));
2702                                                if (!canPursue || !playerHasReadyShips) {
2703                                                        options.setEnabled(OptionId.CONTINUE_ONGOING_BATTLE, false);
2704                                                }
2705                                                break;
2706                                        case HARRY:
2707                                                // CR loss from harrying
2708                                                context.applyPursuitOption(playerFleet, otherFleet, po);
2709                                                
2710                                                if (withText) addText(getString("ongoingBattleHarass"));
2711                                                context.setEngagedInHostilities(true);
2712                                                context.getDataFor(playerFleet).setDisengaged(false);
2713                                                context.getDataFor(otherFleet).setDisengaged(true);
2714                                                allyEngagementChoiceNoBattle = true;
2715                                                harryEndedBattle = true;
2716                                                //rememberWasBeaten();
2717                                                break;
2718                                        case LET_THEM_GO:
2719                                                letGo = true;
2720                                                if (context.isEngagedInHostilities()) {
2721                                                        context.getDataFor(playerFleet).setDisengaged(false);
2722                                                        context.getDataFor(otherFleet).setDisengaged(true);
2723                                                }
2724                                                allyEngagementChoiceNoBattle = true;
2725                                                //rememberWasBeaten();
2726                                                break;
2727                                        }
2728                                //}
2729                                if (letGo) {
2730                                        if (withText) addText(getString("ongoingBattleLetGo"));
2731                                        allyEngagementChoiceNoBattle = true;
2732                                }
2733                                
2734                                if (context.isEngagedInHostilities() && context.isBattleOver()) {
2735                                        goToEncounterEndPath();
2736                                } else {
2737                                        if (context.isEngagedInHostilities()) {
2738                                                options.addOption("Perform a salvage operation, then leave", OptionId.LOOT_THEN_LEAVE, null);
2739                                                options.setShortcut(OptionId.LOOT_THEN_LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2740                                        } else {
2741                                                options.addOption("Leave", OptionId.LEAVE, null);
2742                                                options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2743                                        }
2744                                }
2745                        } else {
2746                                CampaignFleetAIAPI ai = otherFleet.getAI();
2747                                boolean hostile = false;
2748                                if (ai != null) {
2749                                        hostile = ai.isHostileTo(playerFleet) || context.isEngagedInHostilities();
2750                                }
2751                                
2752                                options.addOption("Pursue them", OptionId.PURSUE, getString(pursueTooltip));
2753                                
2754                                if (playerHasReadyShips) {
2755                                        options.addOption("Maneuver to force a pitched battle", OptionId.FORCE_ENGAGE, "Outmaneuver the opposing fleet, forcing them to fight you head on.");
2756                                        boolean knows = context.getBattle() != null && context.getBattle().getNonPlayerSide() != null &&
2757                                                                        context.getBattle().knowsWhoPlayerIs(context.getBattle().getNonPlayerSide());
2758                                        boolean lowImpact = context.isLowRepImpact() || context.isNoRepImpact();
2759                                        FactionAPI nonHostile = getNonHostileOtherFaction();
2760                                        //if (!playerFleet.getFaction().isHostileTo(otherFleet.getFaction()) && knows && !context.isEngagedInHostilities()) {
2761                                        if (nonHostile != null && knows && !lowImpact && !context.isEngagedInHostilities() &&
2762                                                        config.showWarningDialogWhenNotHostile) {
2763                                                options.addOptionConfirmation(OptionId.FORCE_ENGAGE, "The " + nonHostile.getDisplayNameLong() + " " + nonHostile.getDisplayNameIsOrAre() + " not currently hostile, and you have been positively identified. Are you sure you want to engage one of their fleets?", "Yes", "Never mind");
2764                                        }
2765                                } else {
2766                                        options.addOption("Maneuver to force a pitched battle", OptionId.ENGAGE, getString("tooltipNoReadyShips"));
2767                                        options.setEnabled(OptionId.FORCE_ENGAGE, false);
2768                                }
2769                                SetStoryOption.set(dialog, 1, OptionId.FORCE_ENGAGE, "forceBattle", Sounds.STORY_POINT_SPEND_COMBAT,
2770                                                "Maneuvered to force pitched battle with " + otherFleet.getNameWithFactionKeepCase());
2771                                
2772                                options.addOption("Harry their retreat", OptionId.HARRY_PURSUE, getString(harassTooltip));
2773                                boolean knows = context.getBattle() != null && context.getBattle().getNonPlayerSide() != null &&
2774                                                                context.getBattle().knowsWhoPlayerIs(context.getBattle().getNonPlayerSide());
2775                                boolean lowImpact = context.isLowRepImpact() || context.isNoRepImpact();
2776                                FactionAPI nonHostile = getNonHostileOtherFaction();
2777                                //if (!playerFleet.getFaction().isHostileTo(otherFleet.getFaction()) && knows && !context.isEngagedInHostilities()) {
2778                                if (nonHostile != null && knows && !lowImpact && !context.isEngagedInHostilities() &&
2779                                                config.showWarningDialogWhenNotHostile) {
2780                                        options.addOptionConfirmation(OptionId.HARRY_PURSUE, "The " + nonHostile.getDisplayNameLong() + " " + nonHostile.getDisplayNameIsOrAre() + " not currently hostile, and you have been positively identified. Are you sure you want to engage in hostilities with one of their fleets?", "Yes", "Never mind");
2781                                        options.addOptionConfirmation(OptionId.PURSUE, "The " + nonHostile.getDisplayNameLong() + " " + nonHostile.getDisplayNameIsOrAre() + " not currently hostile, and you have been positively identified. Are you sure you want to engage in hostilities with one of their fleets?", "Yes", "Never mind");
2782                                }
2783                                if (hostile) {
2784                                        options.addOption("Let them go", OptionId.LET_THEM_GO, getString(letThemGoTooltip));
2785                                } else {
2786                                        options.addOption("Leave", OptionId.LEAVE, null);
2787                                        options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2788//                                      options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2789//                                      options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2790                                }
2791                                
2792                                if (!canPursue || !playerHasReadyShips) {
2793                                        options.setEnabled(OptionId.PURSUE, false);
2794                                }
2795                                if (!canHasass || !playerHasReadyShips) {
2796                                        options.setEnabled(OptionId.HARRY_PURSUE, false);
2797                                }
2798                        }
2799                } else {
2800                        if (ongoingBattle) {
2801                                if (alliedWantsToRun) {
2802                                        if (withText && !config.straightToEngage) addText(getString("ongoingBattleDisengage"));
2803                                        playerGoal = FleetGoal.ESCAPE;
2804                                        otherGoal = FleetGoal.ATTACK;
2805                                        options.addOption("Join the disengage attempt", OptionId.CONTINUE_ONGOING_BATTLE, null);
2806                                } else {
2807                                        boolean station = false;
2808                                        if (playerFleet != null) {
2809                                                for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
2810                                                        if (member.isStation()) {
2811                                                                station = true;
2812                                                                break;
2813                                                        }
2814                                                }
2815                                        }
2816                                        
2817                                        if (withText && !config.straightToEngage) {
2818                                                if (station) {
2819                                                        addText(getString("ongoingBattleStation"));
2820                                                } else {
2821                                                        addText(getString("ongoingBattleEngage"));
2822                                                }
2823                                        }
2824                                        playerGoal = FleetGoal.ATTACK;
2825                                        otherGoal = FleetGoal.ATTACK;
2826                                        
2827                                        if (playerHasReadyShips) {
2828                                                options.addOption("Join the engagement", OptionId.CONTINUE_ONGOING_BATTLE, null);
2829                                        } else {
2830                                                options.addOption("Join the engagement", OptionId.CONTINUE_ONGOING_BATTLE, getString("tooltipNoReadyShips"));
2831                                                options.setEnabled(OptionId.CONTINUE_ONGOING_BATTLE, false);
2832                                        }
2833                                        
2834                                        options.addOption("Leave", OptionId.LEAVE, null);
2835                                        options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2836                                }
2837                        } else {
2838                                String engageText = "Move in to engage";
2839                                if (config.firstTimeEngageOptionText != null && !context.isEngagedInHostilities()) {
2840                                        engageText = config.firstTimeEngageOptionText;
2841                                }
2842                                if (config.afterFirstTimeEngageOptionText != null && context.isEngagedInHostilities()) {
2843                                        engageText = config.afterFirstTimeEngageOptionText;
2844                                }
2845                                if (playerHasReadyShips) {
2846                                        options.addOption(engageText, OptionId.ENGAGE, getString("tooltipEngage"));
2847                                        boolean knows = context.getBattle() != null && context.getBattle().getNonPlayerSide() != null &&
2848                                                                        context.getBattle().knowsWhoPlayerIs(context.getBattle().getNonPlayerSide());
2849                                        boolean lowImpact = context.isLowRepImpact() || context.isNoRepImpact();
2850                                        FactionAPI nonHostile = getNonHostileOtherFaction();
2851                                        //if (!playerFleet.getFaction().isHostileTo(otherFleet.getFaction()) && knows && !context.isEngagedInHostilities()) {
2852                                        if (nonHostile != null && knows && !lowImpact && !context.isEngagedInHostilities() &&
2853                                                        config.showWarningDialogWhenNotHostile) {
2854                                                options.addOptionConfirmation(OptionId.ENGAGE, "The " + nonHostile.getDisplayNameLong() + " " + nonHostile.getDisplayNameIsOrAre() + " not currently hostile, and you have been positively identified. Are you sure you want to engage one of their fleets?", "Yes", "Never mind");
2855                                        }
2856                                } else {
2857                                        options.addOption(engageText, OptionId.ENGAGE, getString("tooltipNoReadyShips"));
2858                                        options.setEnabled(OptionId.ENGAGE, false);
2859                                }
2860                                CampaignFleetAIAPI ai = otherFleet.getAI();
2861                                boolean hostile = false;
2862                                if (ai != null) {
2863                                        hostile = ai.isHostileTo(playerFleet) || context.isEngagedInHostilities();
2864                                }
2865                                //if (!config.noLeaveOption) {
2866                                boolean noLeave = !context.isEngagedInHostilities() && config.noLeaveOptionOnFirstEngagement;
2867                                if (!noLeave) {
2868                                        if (!config.leaveAlwaysAvailable &&
2869                                                        (otherFleetWantsToFight() || (hostile && !otherFleetWantsToDisengage()))) {
2870                                                if (canDisengageCleanly(playerFleet)) {
2871                                                        options.addOption("Disengage", OptionId.DISENGAGE, getString("tooltipCleanDisengage"));
2872                                                } else if (canDisengageWithoutPursuit(playerFleet) && !(!otherFleetWantsToFight() && !otherFleetWantsToDisengage())) {
2873                                                        options.addOption("Disengage", OptionId.DISENGAGE, getString("tooltipHarrassableDisengage"));
2874                                                } else {
2875                                                        if (otherFleetHoldingVsStrongerEnemy() || (!otherFleetWantsToFight() && !otherFleetWantsToDisengage())) {
2876                                                                options.addOption("Leave", OptionId.LEAVE, null);
2877                                                                options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2878        //                                                      options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2879        //                                                      options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2880                                                        } else {
2881                                                                boolean addSPDisengage = true;
2882                                                                if (canDisengage() || !playerHasReadyShips) {
2883                                                                        options.addOption("Attempt to disengage", OptionId.ATTEMPT_TO_DISENGAGE, getString("tootipAttemptToDisengage"));
2884                                                                        
2885                                                                        addSPDisengage = true;
2886                                                                        
2887                                                                } else {
2888                                                                        boolean hasStation = false;
2889                                                                        boolean allStation = true;
2890                                                                        for (CampaignFleetAPI curr : context.getBattle().getSideFor(otherFleet)) {
2891                                                                                allStation &= curr.isStationMode();
2892                                                                                hasStation |= curr.isStationMode();
2893                                                                        }
2894                                                                        
2895                                                                        if (hasStation) {
2896                                                                                if (allStation) {
2897                                                                                        options.addOption("Disengage", OptionId.DISENGAGE, getString("tooltipCleanDisengage"));
2898                                                                                } else {
2899                                                                                        options.addOption("Disengage", OptionId.DISENGAGE, getString("tooltipHarrassableDisengage"));
2900                                                                                }
2901                                                                                addSPDisengage = false;
2902                                                                        } else {
2903                                                                                if (withText && !shownTooLargeToRetreatMessage) {
2904                                                                                        shownTooLargeToRetreatMessage = true;
2905                                                                                        //addText(getString("playerTooLargeToDisengage"), getString("highlightTooLarge"), Misc.getNegativeHighlightColor());
2906                                                                                        //addText(getString("playerTooLargeCanFightToDisengage"), getString("highlightCanFight"), Misc.getHighlightColor());
2907                                                                                        LabelAPI label = textPanel.addParagraph(getString("playerTooLargeToDisengage"));
2908                                                                                        label.setHighlight(getString("highlightTooLarge"), getString("highlightDisengage"));
2909                                                                                        label.setHighlightColors(Misc.getNegativeHighlightColor(), Misc.getHighlightColor());
2910                                                                                }
2911                                                                        }
2912                                                                }
2913                                                                if (addSPDisengage) {
2914                                                                        //options.addOption("Execute a series of special maneuvers, allowing you to disengage cleanly", OptionId.DISENGAGE);
2915                                                                        options.addOption("Disengage by executing a series of special maneuvers", OptionId.CLEAN_DISENGAGE,
2916                                                                                                          "Allows your fleet to disengage without being pursued.");
2917                                                                        SetStoryOption.set(dialog, 1, OptionId.CLEAN_DISENGAGE, "cleanDisengage", Sounds.STORY_POINT_SPEND_COMBAT,
2918                                                                                        "Maneuvered to disengage from " + otherFleet.getNameWithFactionKeepCase());
2919                                                                        
2920                                                                        addEmergencyRepairsOption();
2921                                                                }
2922                                                        }
2923                                                }
2924                                        } else {
2925                                                options.addOption("Leave", OptionId.LEAVE, null);
2926                                                options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2927        //                                      options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2928        //                                      options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2929                                        }
2930                                }
2931                        }
2932                }
2933                
2934                if (playerOutBeforeAllies()) {
2935                        if (!options.hasOption(OptionId.LEAVE) && 
2936                                        !options.hasOption(OptionId.LET_THEM_GO) &&
2937                                        !options.hasOption(OptionId.DISENGAGE)) {
2938                                options.addOption("Leave", OptionId.LEAVE, null);
2939                                options.setShortcut(OptionId.LEAVE, Keyboard.KEY_ESCAPE, false, false, false, true);
2940                        }
2941                }
2942                
2943                if (Global.getSettings().isDevMode()) {
2944                        DevMenuOptions.addOptions(dialog);
2945                }
2946                
2947                // if it's an ongoing battle, this will all get cleared out by a subsequent call to updatePreCombat()
2948//              if (!options.hasOption(OptionId.GO_TO_MAIN)) {
2949//                      options.addOption("Go back", OptionId.GO_TO_MAIN, null);
2950//                      options.setShortcut(OptionId.GO_TO_MAIN, Keyboard.KEY_ESCAPE, false, false, false, true);
2951//              }
2952                
2953//              if (Global.getSettings().isDevMode()) {
2954//                      DevMenuOptions.addOptions(dialog);
2955//              }
2956        }
2957        
2958        //addSPDisengage
2959        protected void addEmergencyRepairsOption() {
2960                if (didRepairs) return;
2961                
2962                final CampaignFleetAPI fleet = Global.getSector().getPlayerFleet();
2963                final List<FleetMemberAPI> members = new ArrayList<FleetMemberAPI>();
2964                
2965                final float crThreshold = CRPluginImpl.MALFUNCTION_START;
2966                final float hullThreshold = 0.4f;
2967                final float bonusRepairAmount = 0.1f; // threshold + this = repair level
2968                
2969                FleetMemberAPI flagship = fleet.getFlagship();
2970                if (flagship != null && !flagship.isMothballed()) {
2971                        if (flagship.getStatus().getHullFraction() < hullThreshold ||
2972                                        flagship.getRepairTracker().getBaseCR() < crThreshold) {
2973                                members.add(flagship);
2974                        }
2975                }
2976                
2977                for (FleetMemberAPI curr : fleet.getFleetData().getMembersListCopy()) {
2978                        if (curr == flagship) continue;
2979                        if (curr.isMothballed()) continue;
2980                        if (!curr.getCaptain().isDefault()) {
2981                                if (curr.getStatus().getHullFraction() < hullThreshold ||
2982                                                curr.getRepairTracker().getBaseCR() < crThreshold) {
2983                                        members.add(curr);
2984                                }
2985                        }
2986                        if (members.size() > 12) break; // just in case, since these are listed in the dialog w/o a limit check
2987                }
2988                
2989//              for (FleetMemberAPI curr : fleet.getFleetData().getMembersListCopy()) {
2990//                      if (curr == flagship) continue;
2991//                      if (curr.isMothballed()) continue;
2992//                      if (!members.contains(curr)) {
2993//                              if (curr.getStatus().getHullFraction() < hullThreshold ||
2994//                                              curr.getRepairTracker().getBaseCR() < crThreshold) {
2995//                                      members.add(curr);
2996//                              }
2997//                      }
2998//                      if (members.size() > 12) break; // just in case, since these are listed in the dialog w/o a limit check
2999//              }
3000//              
3001                if (members.isEmpty()) return;
3002                
3003                options.addOption("Perform limited emergency repairs", OptionId.EMERGENCY_REPAIRS,
3004                                  "Brings your flagship and a few officer-controlled ships back up to reasonable " +
3005                                  "combat readiness and repairs some hull damage.");
3006                StoryOptionParams params = new StoryOptionParams(OptionId.EMERGENCY_REPAIRS,
3007                                        1, "emergencyRepairs", Sounds.STORY_POINT_SPEND_COMBAT, 
3008                                        "Performed emergency repairs when facing " + otherFleet.getNameWithFactionKeepCase());
3009                
3010                SetStoryOption.set(dialog, params, 
3011                                new BaseOptionStoryPointActionDelegate(dialog, params) {
3012                                        @Override
3013                                        public void confirm() {
3014                                                float dpUsed = 0f;
3015                                                for (FleetMemberAPI member : members) {
3016                                                        float dpRemaining = EMERGENCY_REPAIRS_MAX_DP - dpUsed;
3017                                                        float shipDP = member.getDeploymentPointsCost();
3018                                                        dpUsed += shipDP;
3019                                                
3020                                                        float fraction = dpRemaining / shipDP;
3021                                                        if (fraction >= 0.99f) fraction = 1f;
3022                                                        
3023                                                        float baseRepairCRLevel = crThreshold + bonusRepairAmount * (float) Math.random();
3024                                                        float baseRepairHullLevel = hullThreshold + bonusRepairAmount * (float) Math.random();
3025                                                        
3026                                                        float cr = member.getRepairTracker().getBaseCR();
3027                                                        float repairedCR = cr + (baseRepairCRLevel - cr) * fraction;
3028                                                        if (cr < repairedCR) {
3029                                                                //member.getRepairTracker().setCR(cr + (repairedCR - cr) * fraction);
3030                                                                member.getRepairTracker().applyCREvent(repairedCR - cr, "Emergency repairs");
3031                                                        }
3032                                                        float hull = member.getStatus().getHullFraction();
3033                                                        float repairedHull = hull + (baseRepairHullLevel - hull) * fraction;
3034                                                        if (hull < repairedHull) {
3035                                                                member.getStatus().setHullFraction(repairedHull);
3036                                                        }
3037                                                        
3038                                                        String str = BaseSkillEffectDescription.getValueLabelForMember(member);
3039                                                        textPanel.setFontSmallInsignia();
3040                                                        textPanel.addPara(str + " performed emergency repairs", Misc.getPositiveHighlightColor());
3041                                                        textPanel.highlightFirstInLastPara(str, Misc.getHighlightColor());
3042                                                        textPanel.setFontInsignia();
3043                                                        
3044                                                        if (fraction < 1f) {
3045                                                                break;
3046                                                        }
3047                                                }
3048                                                didRepairs = true;
3049                                                playerFleet.getFleetData().setSyncNeeded();
3050                                                playerFleet.getFleetData().syncIfNeeded();
3051                                                dialog.getOptionPanel().setEnabled(OptionId.EMERGENCY_REPAIRS, false);
3052                                        }
3053
3054                                        @Override
3055                                        public void createDescription(TooltipMakerAPI info) {
3056                                                super.createDescription(info);
3057                                                info.setParaFontDefault();
3058                                                float opad = 10f;
3059                                                float pad = 3f;
3060                                                info.addPara("Will bring up to %s deployment points worth of ships up " +
3061                                                                "to approximately %s combat readiness and %s hull integrity. Starts with the " +
3062                                                                "flagship and then goes on to officer-controlled ships, in the " +
3063                                                                "order they are placed in the fleet. " +
3064                                                                "Does not affect other ships.",
3065                                                                0f, Misc.getHighlightColor(),
3066                                                                "" + (int) EMERGENCY_REPAIRS_MAX_DP,
3067                                                                "" + (int) Math.round((crThreshold + bonusRepairAmount) * 100f) + "%",
3068                                                                "" + (int) Math.round((hullThreshold + bonusRepairAmount) * 100f) + "%"
3069                                                                );
3070                                                
3071                                                info.addPara("The repairs will affect:", opad);
3072                                                info.setBulletedListMode(BaseIntelPlugin.INDENT);
3073                                                float initPad = 10f;
3074                                                
3075                                                float dpUsed = 0f;
3076                                                for (FleetMemberAPI member : members) {
3077                                                        float dpRemaining = EMERGENCY_REPAIRS_MAX_DP - dpUsed;
3078                                                        float shipDP = member.getDeploymentPointsCost();
3079                                                        dpUsed += shipDP;
3080                                                        
3081                                                        dpUsed += shipDP;
3082                                                        
3083                                                        float fraction = dpRemaining / shipDP;
3084                                                        if (fraction >= 0.99f) fraction = 1f;
3085                                                        
3086                                                        String str = BaseSkillEffectDescription.getValueLabelForMember(member);
3087                                                        
3088                                                        String post = " (full effect)";
3089                                                        if (fraction < 1) post = " (partial effect)";
3090                                                        
3091                                                        info.addPara(str + post, initPad);
3092                                                        initPad = 0f;
3093                                                        
3094                                                        if (dpUsed >= EMERGENCY_REPAIRS_MAX_DP) break;
3095                                                }
3096                                                info.setBulletedListMode(null);
3097                                                info.addSpacer(20f);
3098                                        }
3099                                });
3100//              SetStoryOption.set(dialog, 1, OptionId.EMERGENCY_REPAIRS, "emergencyRepairs", Sounds.STORY_POINT_SPEND_COMBAT,
3101//                              "Performed emergency repairs when facing " + otherFleet.getNameWithFactionKeepCase());
3102        }
3103        
3104        protected PursuitOption pickPursuitOption(CampaignFleetAPI fleet, CampaignFleetAPI other, FleetEncounterContext context) {
3105                context.setNoHarryBecauseOfStation(false);
3106                
3107                if (fleet.getAI() == null) return PursuitOption.LET_THEM_GO;
3108                
3109                if (config.alwaysPursue) {
3110                        return PursuitOption.PURSUE;
3111                }
3112                if (config.alwaysHarry) {
3113                        return PursuitOption.HARRY;
3114                }
3115                if (config.alwaysLetGo) {
3116                        return PursuitOption.LET_THEM_GO;
3117                }
3118                
3119                boolean allStation = false;
3120                boolean hasStation = false;
3121                if (context.getBattle() != null) {
3122                        allStation = true;
3123                        for (CampaignFleetAPI curr : context.getBattle().getSideFor(fleet)) {
3124//                              if (curr.isStationMode()) {
3125//                                      return PursuitOption.HARRY;
3126//                              }
3127                                allStation &= curr.isStationMode();
3128                                hasStation |= curr.isStationMode();
3129                        }
3130                        //if (allStation) {
3131                }
3132                
3133                PursuitOption option = fleet.getAI().pickPursuitOption(context, other);
3134                if (hasStation && option == PursuitOption.HARRY) {
3135                        context.setNoHarryBecauseOfStation(true);
3136                        return PursuitOption.LET_THEM_GO;
3137                }
3138                return option;
3139        }
3140        
3141        public FactionAPI getNonHostileOtherFaction() {
3142                if (context.getBattle() == null) return null;
3143                
3144                //BattleSide playerSide = context.getBattle().pickSide(Global.getSector().getPlayerFleet());
3145                
3146                //List<CampaignFleetAPI> otherSide = context.getBattle().getSideFor(otherFleet);
3147                List<CampaignFleetAPI> otherSide = context.getBattle().getNonPlayerSide();
3148                return getNonHostileOtherFaction(otherSide);
3149        }
3150        
3151        public FactionAPI getNonHostileOtherFaction(List<CampaignFleetAPI> otherSide) {
3152                FactionAPI player = Global.getSector().getPlayerFaction();
3153                int max = -1;
3154                CampaignFleetAPI result = null;         
3155                
3156                if (otherSide != null) {
3157                        for (CampaignFleetAPI other : otherSide) {
3158                                if (!player.isHostileTo(other.getFaction()) && other.getFleetPoints() > max) {
3159                                        result = other;
3160                                        max = other.getFleetPoints();
3161                                }
3162                        }
3163                }
3164                return result == null ? null : result.getFaction();
3165        }
3166        
3167        protected boolean playerOutBeforeAllies() {
3168                EngagementOutcome last = context.getLastEngagementOutcome();
3169                if (last == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN ||
3170                                last == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_LOSS ||
3171                                last == EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_WIN ||
3172                                last == EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_LOSS ||
3173                                last == EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_WIN ||
3174                                last == EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_LOSS
3175                        ) {
3176                        return true;
3177                }
3178                return false;
3179        }
3180        
3181        public static boolean canDisengage() {
3182                CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
3183                float total = 0f;
3184                for (FleetMemberAPI member : playerFleet.getFleetData().getMembersListCopy()) {
3185                        if (member.canBeDeployedForCombat()) {
3186                                total += member.getDeploymentPointsCost();
3187                        }
3188                }
3189                return total <= getDisengageSize();
3190        }
3191        
3192        protected boolean otherCanDisengage() {
3193                return fleetCanDisengage(otherFleet);
3194        }
3195        
3196        protected boolean alliedCanDisengage() {
3197                return fleetCanDisengage(playerFleet);
3198        }
3199        
3200        protected boolean fleetCanDisengage(CampaignFleetAPI fleet) {
3201                float total = 0f;
3202                for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
3203                        if (member.canBeDeployedForCombat()) {
3204                                total += member.getDeploymentPointsCost();
3205                        }
3206                }
3207                return total <= getDisengageSize();
3208        }
3209        
3210        public static float getDisengageSize() {
3211                float abs = Global.getSettings().getFloat("maxDisengageSize");
3212                float fraction = Global.getSettings().getFloat("maxDisengageFraction") * Global.getSettings().getBattleSize();
3213                return Math.min(abs, fraction);
3214        }
3215        
3216        protected boolean didEnoughToDisengage(CampaignFleetAPI fleet) {
3217                DataForEncounterSide data = context.getDataFor(fleet);
3218                return data.isDidEnoughToDisengage();
3219        }
3220        
3221        protected boolean canDisengageCleanly(CampaignFleetAPI fleet) {
3222                //if (wasEnemyDisrupted(fleet)) return true;
3223                DataForEncounterSide data = context.getDataFor(fleet);
3224                if (data.isWonLastEngagement()) return true;
3225                
3226                if (data.isDidEnoughToDisengage()) return true;
3227                
3228                if (fleet == playerFleet) {
3229                        for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
3230                                if (member.isStation()) return true;
3231                        }
3232                }
3233                
3234                EngagementOutcome last = context.getLastEngagementOutcome();
3235                if (fleet == playerFleet && !ongoingBattle &&
3236                                (last == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_WIN ||
3237                                last == EngagementOutcome.BATTLE_PLAYER_OUT_FIRST_LOSS ||
3238                                last == EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_WIN ||
3239                                last == EngagementOutcome.PURSUIT_PLAYER_OUT_FIRST_LOSS ||
3240                                last == EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_WIN ||
3241                                last == EngagementOutcome.ESCAPE_PLAYER_OUT_FIRST_LOSS)) {
3242                        return true;
3243                }
3244                
3245                return false;
3246        }
3247        protected boolean canDisengageWithoutPursuit(CampaignFleetAPI fleet) {
3248                CampaignFleetAPI other = playerFleet;
3249                if (other == fleet) other = otherFleet;
3250                PursueAvailability pa = getPursuitAvailability(other);
3251                return pa != PursueAvailability.AVAILABLE;
3252        }
3253        
3254        protected PursueAvailability getPursuitAvailability(CampaignFleetAPI fleet) {
3255                CampaignFleetAPI other = playerFleet;
3256                if (other == fleet) other = otherFleet;
3257                PursueAvailability pa = context.getPursuitAvailability(fleet, other);
3258                if (pa == PursueAvailability.TOO_SLOW && fleet == playerFleet) {
3259                        pa = PursueAvailability.AVAILABLE;
3260                }
3261                return pa;
3262        }
3263        
3264        protected String getString(String id) {
3265                String str = Global.getSettings().getString("fleetInteractionDialog", id);
3266                
3267                String faction = otherFleet.getFaction().getEntityNamePrefix();
3268                if (faction == null || faction.isEmpty()) {
3269                        faction = otherFleet.getFaction().getDisplayName();
3270                }
3271                
3272                if (otherFleet.getFaction().isNeutralFaction()) {
3273                        faction = "opposing";
3274                }
3275                
3276                String fleetName = otherFleet.getName();
3277                String firstName = otherFleet.getCommander().getName().getFirst();
3278                String lastName = otherFleet.getCommander().getName().getLast();
3279                String fleetOrShip = "fleet";
3280                if (otherFleet.getFleetData().getMembersListCopy().size() == 1) {
3281                        fleetOrShip = "ship";
3282                        if (otherFleet.getFleetData().getMembersListCopy().get(0).isFighterWing()) {
3283                                fleetOrShip = "fighter wing";
3284                        }
3285                }
3286                String playerFleetOrShip = "fleet";
3287                if (playerFleet.getFleetData().getMembersListCopy().size() == 1) {
3288                        playerFleetOrShip = "ship";
3289                        if (playerFleet.getFleetData().getMembersListCopy().get(0).isFighterWing()) {
3290                                playerFleetOrShip = "fighter wing";
3291                        }
3292                }
3293                
3294                DataForEncounterSide data = context.getDataFor(playerFleet);
3295                if (data != null) {
3296                        int crewLost = (int) (data.getCrewLossesDuringLastEngagement().getCrewInt());
3297                        String crewLostStr = getApproximate(crewLost);
3298                        
3299                        int marinesLost = (int) (data.getCrewLossesDuringLastEngagement().getMarines());
3300                        String marinesLostStr = getApproximate(marinesLost);
3301                        
3302                        int crewRecovered = (int) data.getRecoverableCrewLosses().getCrewInt();
3303                        int marinesRecovered = (int) data.getRecoverableCrewLosses().getMarines();
3304                
3305                        String crewRecStr = "" + crewRecovered;
3306                        if (crewRecovered <= 0) {
3307                                crewRecStr = "no";
3308                        }
3309                        String marinesRecStr = "" + marinesRecovered;
3310                        if (marinesRecovered <= 0) {
3311                                marinesRecStr = "no";
3312                        }
3313                        
3314                        str = str.replaceAll("\\$crewLost", crewLostStr);
3315                        str = str.replaceAll("\\$marinesLost", marinesLostStr);
3316                        str = str.replaceAll("\\$crewLost", crewLostStr);
3317                        str = str.replaceAll("\\$crewRecovered", crewRecStr);
3318                        str = str.replaceAll("\\$marinesRecovered", marinesRecStr);
3319                }
3320                
3321                if (toBoard != null) {
3322                        int numLifeSigns = (int) (toBoard.getCrewComposition().getCrew() + toBoard.getCrewComposition().getMarines());
3323                        str = str.replaceAll("\\$numLifeSigns", getApproximate(numLifeSigns));
3324                        
3325                        str = str.replaceAll("\\$boardableShipName", toBoard.getShipName());
3326                }
3327                
3328                str = str.replaceAll("\\$faction", faction);
3329                str = str.replaceAll("\\$fleetName", fleetName);
3330                str = str.replaceAll("\\$firstName", firstName);
3331                str = str.replaceAll("\\$lastName", lastName);
3332                str = str.replaceAll("\\$fleetOrShip", fleetOrShip);
3333                str = str.replaceAll("\\$playerFleetOrShip", playerFleetOrShip);
3334                
3335                if (selectedFlagship != null) {
3336                        str = str.replaceAll("\\$flagship", "the " + selectedFlagship.getShipName());
3337                }
3338                
3339                str = str.replaceAll("\\$creditsLooted", creditsLooted);
3340                
3341                if (crashMothballList != null) {
3342                        str = str.replaceAll("\\$crashMothballList", crashMothballList);
3343                }
3344                
3345                if (repairedShipList != null) {
3346                        str = str.replaceAll("\\$repairedShipList", repairedShipList);
3347                }
3348                
3349                int marines = Global.getSector().getPlayerFleet().getCargo().getMarines();
3350                str = str.replaceAll("\\$marines", "" + marines);
3351                
3352                str = str.replaceAll("\\$boardingSuccessChance", "" + (int) boardingPercentSuccess + "%");
3353                
3354                if (boardingResult != null) {
3355                        str = str.replaceAll("\\$boardingCrewLost", getIntOrNo(boardingResult.getAttackerLosses().getCrew()));
3356                        str = str.replaceAll("\\$boardingMarinesLost", getIntOrNo(boardingResult.getAttackerLosses().getMarines()));
3357                        str = str.replaceAll("\\$boardingEnemyCrewLost", getIntOrNo(boardingResult.getDefenderLosses().getCrew()));
3358                        str = str.replaceAll("\\$boardingEnemyMarinesLost", getIntOrNo(boardingResult.getDefenderLosses().getMarines()));
3359                }
3360                
3361//              # $alliedFactionAndTheirAllies "Hegemony forces and their allies"
3362//              # $enemyFactionAndTheirAllies "Hegemony forces and their allies"
3363//              # $yourForcesWereOrYourSideWas
3364                BattleAPI b = context.getBattle();
3365                if (b != null) {
3366                        BattleSide playerSide = b.pickSide(Global.getSector().getPlayerFleet());
3367                        CampaignFleetAPI sideOnePrimary = b.getPrimary(b.getSideOne());
3368                        CampaignFleetAPI sideTwoPrimary = b.getPrimary(b.getSideTwo());
3369                        if (playerSide != BattleSide.NO_JOIN) {
3370                                sideOnePrimary = b.getPrimary(b.getSide(playerSide));
3371                                sideTwoPrimary = b.getPrimary(b.getOtherSide(playerSide));
3372                        }
3373                        
3374                        if (sideOnePrimary != null) {
3375                                String strOne = sideOnePrimary.getFaction().getEntityNamePrefix() + " forces";
3376                                if (strOne.startsWith(" ")) {
3377                                        strOne = sideOnePrimary.getFaction().getDisplayName() + " forces";
3378                                }
3379//                              if (b.isStationInvolved(b.getSideFor(sideOnePrimary))) {
3380//                                      strOne = strOne.replaceFirst(" forces", " station");
3381//                              }
3382                                for (CampaignFleetAPI fleet : b.getSideFor(sideOnePrimary)) {
3383                                        if (fleet.getFaction() != sideOnePrimary.getFaction()) {
3384                                                if (fleet.isPlayerFleet()) continue;
3385                                                strOne += " and their allies";
3386                                                break;
3387                                        }
3388                                }
3389                                str = str.replaceAll("\\$alliedFactionAndTheirAllies", strOne);
3390                        }
3391                        if (sideTwoPrimary != null) {
3392                                String strTwo = sideTwoPrimary.getFaction().getEntityNamePrefix() + " forces";
3393                                if (strTwo.startsWith(" ")) {
3394                                        strTwo = sideTwoPrimary.getFaction().getDisplayName() + " forces";
3395                                }
3396//                              if (b.isStationInvolved(b.getSideFor(sideTwoPrimary))) {
3397//                                      strTwo = strTwo.replaceFirst(" forces", " station");
3398//                              }
3399                                for (CampaignFleetAPI fleet : b.getSideFor(sideTwoPrimary)) {
3400                                        if (fleet.getFaction() != sideTwoPrimary.getFaction()) {
3401                                                if (fleet.isPlayerFleet()) continue;
3402                                                strTwo += " and their allies";
3403                                                break;
3404                                        }
3405                                }
3406                                str = str.replaceAll("\\$enemyFactionAndTheirAllies", strTwo);
3407                        }
3408                        
3409                        //$yourForcesWereOrYourSideWas
3410                        String yourForcesWere = "Your forces were";
3411                        if (b.getPlayerSide() != null && b.getPlayerSide().size() > 1) {
3412                                yourForcesWere = "Your side was";
3413                        }
3414                        str = str.replaceAll("\\$yourForcesWereOrYourSideWas", yourForcesWere);
3415                }
3416                
3417
3418//              float recoveryFraction = context.getStandDownRecoveryFraction();
3419//              str = str.replaceAll("\\$standDownRecovery", "" + (int) (recoveryFraction * 100f));
3420                
3421                return str;
3422        }
3423        
3424        protected String getIntOrNo(float value) {
3425                if (value < 1) {
3426                        return "no";
3427                }
3428                return "" + (int) value;
3429        }
3430        
3431        protected String getApproximate(float value) {
3432                int v = (int) value;
3433                String str = "multiple";
3434                if (v <= 0) {
3435                        str = "no";
3436                } else if (v < 10) {
3437                        str = "" + v;
3438                } else if (v < 100) {
3439                        v = (int) Math.round((float) v/10f) * 10;
3440                        str = "approximately " + v;
3441                } else if (v < 1000) {
3442                        v = (int) Math.round((float) v/10f) * 10;
3443                        str = "approximately " + v;
3444                } else {
3445                        v = (int) Math.round((float) v/100f) * 100;
3446                        str = "" + v;
3447                }
3448                return str;
3449        }
3450        
3451        protected String getApproximateNumOnly(float value) {
3452                int v = (int) value;
3453                String str = "";
3454                if (v <= 0) {
3455                        str = "asdasd";
3456                } else if (v < 10) {
3457                        str = "" + v;
3458                } else if (v < 100) {
3459                        v = (int) Math.round((float) v/10f) * 10;
3460                        str = "" + v;
3461                } else if (v < 1000) {
3462                        v = (int) Math.round((float) v/10f) * 10;
3463                        str = "" + v;
3464                } else {
3465                        v = (int) Math.round((float) v/100f) * 100;
3466                        str = "" + v;
3467                }
3468                return str;
3469        }
3470        
3471        
3472        protected boolean isFightingOver() {
3473                return context.isBattleOver() || 
3474                                (context.getDataFor(otherFleet).disengaged() && context.getDataFor(playerFleet).disengaged());
3475//              return context.getDataFor(playerFleet).getLastGoal() == FleetGoal.ESCAPE ||
3476//                         context.getDataFor(otherFleet).getLastGoal() == FleetGoal.ESCAPE;
3477                //return context.getWinnerData().getLastGoal() == FleetGoal.ESCAPE || context.getLoserData().getLastGoal() == FleetGoal.ESCAPE;
3478        }
3479        
3480        public boolean alliedFleetWantsToFight() {
3481                return fleetWantsToFight(playerFleet, otherFleet);
3482        }
3483        public boolean otherFleetWantsToFight() {
3484                return fleetWantsToFight(otherFleet, playerFleet);
3485        }
3486        public boolean otherFleetWantsToFight(boolean assumeHostile) {
3487                return fleetWantsToFight(otherFleet, playerFleet, assumeHostile);
3488        }
3489        protected boolean fleetWantsToFight(CampaignFleetAPI fleet, CampaignFleetAPI other) {
3490                return fleetWantsToFight(fleet, other, false);
3491        }
3492        protected boolean fleetWantsToFight(CampaignFleetAPI fleet, CampaignFleetAPI other, boolean assumeHostile) {
3493                if (config.alwaysAttackVsAttack) return true;
3494                
3495                boolean hasNonCivReserves = false;
3496                for (FleetMemberAPI member : context.getDataFor(fleet).getInReserveDuringLastEngagement()) {
3497                        if (!member.isCivilian()) {
3498                                hasNonCivReserves = true;
3499                                break;
3500                        }
3501                }
3502                if (context.isEngagedInHostilities() &&
3503                                !context.getDataFor(fleet).isWonLastEngagement() &&
3504                                !hasNonCivReserves) {
3505                        return false;
3506                }
3507                
3508                CampaignFleetAIAPI ai = fleet.getAI();
3509                if (ai == null) return false;
3510                EncounterOption option = ai.pickEncounterOption(context, other);
3511                
3512                if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_MAKE_ALLOW_DISENGAGE)) return false;
3513                
3514                return (ai.isHostileTo(other) || context.isEngagedInHostilities() || assumeHostile || 
3515                                fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_MAKE_PREVENT_DISENGAGE) //||
3516                                // "aggressive" just means "always engage IF already hostile"
3517                                //fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_MAKE_AGGRESSIVE)
3518                                ) &&
3519                                (option == EncounterOption.ENGAGE || (option == EncounterOption.HOLD && ongoingBattle));
3520        }
3521        
3522        
3523        
3524        protected boolean alliedFleetHoldingVsStrongerEnemy() {
3525                return fleetHoldingVsStrongerEnemy(playerFleet, otherFleet);
3526        }
3527        protected boolean otherFleetHoldingVsStrongerEnemy() {
3528//              if (otherFleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_MAKE_HOLD_VS_STRONGER_COMBAT_ONLY)) {
3529//                      return true;
3530//              }
3531                return fleetHoldingVsStrongerEnemy(otherFleet, playerFleet);
3532        }
3533        protected boolean fleetHoldingVsStrongerEnemy(CampaignFleetAPI fleet, CampaignFleetAPI other) {
3534                CampaignFleetAIAPI ai = fleet.getAI();
3535                if (ai == null) return false;
3536                boolean hostile = ai.isHostileTo(other) || (other.getAI() != null && other.getAI().isHostileTo(fleet)) || context.isEngagedInHostilities();
3537                if (!hostile) return false;
3538                
3539                if (ai.pickEncounterOption(context, other) == EncounterOption.HOLD_VS_STRONGER) return true;
3540                
3541                return fleetWantsToDisengage(fleet, other) && !fleetCanDisengage(fleet);
3542        }
3543        
3544        protected boolean alliedFleetWantsToDisengage() {
3545                return fleetWantsToDisengage(playerFleet, otherFleet);
3546        }
3547        
3548        protected boolean otherFleetWantsToDisengage() {
3549                return fleetWantsToDisengage(otherFleet, playerFleet);
3550        }
3551        protected boolean fleetWantsToDisengage(CampaignFleetAPI fleet, CampaignFleetAPI other) {
3552                if (config.alwaysAttackVsAttack) return false;
3553                
3554                boolean hasNonCivReserves = false;
3555                for (FleetMemberAPI member : context.getDataFor(fleet).getInReserveDuringLastEngagement()) {
3556                        if (!member.isCivilian()) {
3557                                hasNonCivReserves = true;
3558                                break;
3559                        }
3560                }
3561                if (context.isEngagedInHostilities() &&
3562                                !context.getDataFor(fleet).isWonLastEngagement() &&
3563                                !hasNonCivReserves) {
3564                        return true;
3565                }
3566                
3567                CampaignFleetAIAPI ai = fleet.getAI();
3568                if (ai == null) return false;
3569                return ai.pickEncounterOption(context, other) == EncounterOption.DISENGAGE;
3570        }
3571
3572        public Object getContext() {
3573                return context;
3574        }
3575
3576        public void updateMemory() {
3577                if (conversationDelegate != null) {
3578                        conversationDelegate.updateMemory();
3579                }
3580        }
3581        
3582        public void notifyActivePersonChanged() {
3583                if (conversationDelegate != null) {
3584                        conversationDelegate.notifyActivePersonChanged();
3585                }
3586        }
3587
3588        public void setActiveMission(CampaignEventPlugin mission) {
3589                if (mission == null) {
3590                        conversationDelegate.getMemoryMap().remove(MemKeys.MISSION);
3591                } else {
3592                        MemoryAPI memory = mission.getMemory();
3593                        if (memory != null) {
3594                                conversationDelegate.getMemoryMap().put(MemKeys.MISSION, memory);
3595                        } else {
3596                                conversationDelegate.getMemoryMap().remove(MemKeys.MISSION);
3597                        }
3598                }
3599        }
3600        public void setPlayerFleet(CampaignFleetAPI playerFleet) {
3601                this.playerFleet = playerFleet;
3602        }
3603
3604        
3605}
3606
3607
3608