001package com.fs.starfarer.api.impl.campaign.intel.raid;
002
003import java.awt.Color;
004import java.util.ArrayList;
005import java.util.List;
006import java.util.Random;
007import java.util.Set;
008
009import org.lwjgl.util.vector.Vector2f;
010
011import com.fs.starfarer.api.Global;
012import com.fs.starfarer.api.campaign.CampaignFleetAPI;
013import com.fs.starfarer.api.campaign.FactionAPI;
014import com.fs.starfarer.api.campaign.SectorEntityToken;
015import com.fs.starfarer.api.campaign.StarSystemAPI;
016import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin;
017import com.fs.starfarer.api.campaign.econ.MarketAPI;
018import com.fs.starfarer.api.impl.campaign.DebugFlags;
019import com.fs.starfarer.api.impl.campaign.command.WarSimScript;
020import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
021import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
022import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator;
023import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData;
024import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
025import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner;
026import com.fs.starfarer.api.impl.campaign.ids.Factions;
027import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
028import com.fs.starfarer.api.impl.campaign.ids.Ranks;
029import com.fs.starfarer.api.impl.campaign.ids.Tags;
030import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
031import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseAssignmentAI.FleetActionDelegate;
032import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI;
033import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD;
034import com.fs.starfarer.api.ui.Alignment;
035import com.fs.starfarer.api.ui.LabelAPI;
036import com.fs.starfarer.api.ui.SectorMapAPI;
037import com.fs.starfarer.api.ui.TooltipMakerAPI;
038import com.fs.starfarer.api.util.Misc;
039
040public class RaidIntel extends BaseIntelPlugin implements RouteFleetSpawner {
041
042        public static Object UPDATE_FAILED = new Object();
043        public static Object UPDATE_RETURNING = new Object();
044        public static Object ENTERED_SYSTEM_UPDATE = new Object();
045        
046        public static enum RaidStageStatus {
047                ONGOING,
048                SUCCESS,
049                FAILURE,
050        }
051        
052        public static interface RaidDelegate {
053                void notifyRaidEnded(RaidIntel raid, RaidStageStatus status);
054        }
055        
056        public static interface RaidStage {
057                RaidStageStatus getStatus();
058                void advance(float amount);
059                void notifyStarted();
060                float getExtraDaysUsed();
061                void showStageInfo(TooltipMakerAPI info);
062                float getElapsed();
063                float getMaxDays();
064        }
065        
066        protected int currentStage = 0;
067        protected int failStage = -1;
068        protected List<RaidStage> stages = new ArrayList<RaidStage>();
069        
070        protected String id = Misc.genUID();
071        protected String sid = "raid_" + id;
072        
073        protected float extraDays = 60f;
074        protected StarSystemAPI system;
075        protected FactionAPI faction;
076        protected float defenderStr = 0f;
077        protected RaidDelegate delegate;
078        
079        public RaidIntel(StarSystemAPI system, FactionAPI faction, RaidDelegate delegate) {
080                
081                //RAID_DEBUG = false;
082                
083                this.system = system;
084                this.faction = faction;
085                this.delegate = delegate;
086                
087                Global.getSector().addScript(this);
088//              Global.getSector().getIntelManager().addIntel(this);
089                
090                
091//              List<MarketAPI> targets = new ArrayList<MarketAPI>();
092//              for (MarketAPI market : Misc.getMarketsInLocation(system)) {
093//                      if (market.getFaction().isHostileTo(getFaction())) {
094//                              targets.add(market);
095//                      }
096//              }
097                defenderStr = WarSimScript.getEnemyStrength(getFaction(), system);
098        }
099        
100        public void sendEnteredSystemUpdate() {
101                //sendUpdateIfPlayerHasIntel(ENTERED_SYSTEM_UPDATE, false);
102        }
103        
104        public StarSystemAPI getSystem() {
105                return system;
106        }
107
108
109        public int getCurrentStage() {
110                return currentStage;
111        }
112        
113        public int getStageIndex(RaidStage stage) {
114                return stages.indexOf(stage);
115        }
116        
117        public int getFailStage() {
118                return failStage;
119        }
120
121        public OrganizeStage getOrganizeStage() {
122                for (RaidStage stage : stages) {
123                        if (stage instanceof OrganizeStage) {
124                                return (OrganizeStage) stage;
125                        }
126                }
127                return null;
128        }
129        public AssembleStage getAssembleStage() {
130                for (RaidStage stage : stages) {
131                        if (stage instanceof AssembleStage) {
132                                return (AssembleStage) stage;
133                        }
134                }
135                return null;
136                //return (AssembleStage) stages.get(0);
137        }
138        
139        public ActionStage getActionStage() {
140                for (RaidStage stage : stages) {
141                        if (stage instanceof ActionStage) {
142                                return (ActionStage) stage;
143                        }
144                }
145                return null;
146                //return (AssembleStage) stages.get(0);
147        }
148        
149        public void addStage(RaidStage stage) {
150                stages.add(stage);
151        }
152        
153        public String getRouteSourceId() {
154                return sid; 
155        }
156        
157        public float getExtraDays() {
158                return extraDays;
159        }
160
161        public void setExtraDays(float extraDays) {
162                this.extraDays = extraDays;
163        }
164
165
166
167        @Override
168        public boolean canMakeVisibleToPlayer(boolean playerInRelayRange) {
169                return super.canMakeVisibleToPlayer(playerInRelayRange);
170        }
171
172        public boolean shouldSendUpdate() {
173                if (DebugFlags.SEND_UPDATES_WHEN_NO_COMM || Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay()) {
174                        return true;
175                }
176                if (system != null && system == Global.getSector().getCurrentLocation()) {
177                        return true;
178                }
179                
180                return isPlayerTargeted();
181        }
182        
183        public boolean isPlayerTargeted() {
184                ActionStage action = getActionStage();
185                if (action != null && action.isPlayerTargeted()) return true;
186                return false;
187        }
188        
189        public String getCommMessageSound() {
190                if (isPlayerTargeted() && !isSendingUpdate()) {
191                        return getSoundColonyThreat();
192                }
193                
194                if (isSendingUpdate()) {
195                        return getSoundStandardUpdate();
196                }
197                return getSoundMajorPosting();
198        }
199        
200        @Override
201        protected void advanceImpl(float amount) {
202                super.advanceImpl(amount);
203        
204                if (currentStage >= stages.size()) {
205                        endAfterDelay();
206                        // do we really need an update after the raiders have returned to their source colonies?
207                        // as far as the player is concerned, the raid is really over when they head back, not when they get back
208                        
209                        // actually, the return stage finishes immediately since its updateStatus() sets to SUCCESS right away
210                        // so this update gets sent right after the action stage succeeds
211                        if (shouldSendUpdate()) {
212                                sendUpdateIfPlayerHasIntel(UPDATE_RETURNING, false);
213                        }
214                        return;
215                }
216                
217                RaidStage stage = stages.get(currentStage);
218                
219                stage.advance(amount);
220                
221                RaidStageStatus status = stage.getStatus();
222                if (status == RaidStageStatus.SUCCESS) {
223                        currentStage++;
224                        setExtraDays(Math.max(0, getExtraDays() - stage.getExtraDaysUsed()));
225                        if (currentStage < stages.size()) {
226                                stages.get(currentStage).notifyStarted();
227                        }
228                        return;
229                } else if (status == RaidStageStatus.FAILURE) {
230                        failedAtStage(stage);
231                        failStage = currentStage;
232                        endAfterDelay();
233                        if (shouldSendUpdate()) {
234                                sendUpdateIfPlayerHasIntel(UPDATE_FAILED, false);
235                        }
236                }
237        }
238        
239        public void forceFail(boolean withUpdate) {
240                int index = currentStage;
241                if (index >= stages.size()) index = stages.size() - 1;
242                failedAtStage(stages.get(index));
243                failStage = currentStage;
244                endAfterDelay();
245                if (withUpdate && shouldSendUpdate()) {
246                        sendUpdateIfPlayerHasIntel(UPDATE_FAILED, false);
247                }
248        }
249        
250        protected void failedAtStage(RaidStage stage) {
251                
252        }
253        
254
255        @Override
256        protected void notifyEnded() {
257                super.notifyEnded();
258                Global.getSector().removeScript(this);
259        }
260        
261        @Override
262        protected void notifyEnding() {
263                super.notifyEnding();
264                
265                if (delegate != null) {
266                        RaidStageStatus status = RaidStageStatus.SUCCESS;
267                        if (failStage >= 0) {
268                                status = RaidStageStatus.FAILURE;
269                        }
270                        delegate.notifyRaidEnded(this, status);
271                }
272        }
273
274        
275        public float getETA() {
276                int curr = getCurrentStage();
277                float eta = 0f;
278                for (RaidStage stage : stages) {
279                        if (stage instanceof ActionStage) {
280                                break;
281                        }
282                        //RouteLocationCalculator.getTravelDays(((TravelStage)stage).from, ((TravelStage)stage).to)
283                        int index = getStageIndex(stage);
284                        if (index < curr) {
285                                continue;
286                        }
287                        if (stage instanceof OrganizeStage) {
288                                eta += Math.max(0f, stage.getMaxDays() - stage.getElapsed());
289                        } else if (stage instanceof AssembleStage) {
290                                eta += Math.max(0f, 20f - stage.getElapsed());
291                        } else if (stage instanceof TravelStage) {
292                                float travelDays = RouteLocationCalculator.getTravelDays(getAssembleStage().gatheringPoint, system.getHyperspaceAnchor());
293                                eta += Math.max(0f, travelDays - stage.getElapsed());
294                        }
295                }
296                return eta;
297        }
298        
299        protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) {
300                
301                Color h = Misc.getHighlightColor();
302                Color g = Misc.getGrayColor();
303                float pad = 3f;
304                float opad = 10f;
305                
306                float initPad = pad;
307                if (mode == ListInfoMode.IN_DESC) initPad = opad;
308                
309                Color tc = getBulletColorForMode(mode);
310                
311                bullet(info);
312                boolean isUpdate = getListInfoParam() != null;
313                
314                float eta = getETA();
315                
316                info.addPara("Faction: " + faction.getDisplayName(), initPad, tc,
317                                         faction.getBaseUIColor(), faction.getDisplayName());
318                initPad = 0f;
319                
320                int max = 0;
321                MarketAPI target = null;
322                for (MarketAPI other : Misc.getMarketsInLocation(system)) {
323                        if (!other.getFaction().isHostileTo(faction)) continue;
324                        int size = other.getSize();
325                        if (size > max || (size == max && other.getFaction().isPlayerFaction())) {
326                                max = size;
327                                target = other;
328                        }
329                }
330                
331                
332                if (target != null) {
333                        FactionAPI other = target.getFaction();
334                        info.addPara("Target: " + other.getDisplayName(), initPad, tc,
335                                             other.getBaseUIColor(), other.getDisplayName());
336                }
337                
338                if (isUpdate) {
339                        if (getListInfoParam() == ENTERED_SYSTEM_UPDATE) {
340                                info.addPara("Arrived in-system", tc, initPad);
341                        } else {
342                                if (failStage < 0) {
343                                        info.addPara("Colonies in the " + system.getNameWithLowercaseType() + " have been raided", 
344                                                                tc, initPad);
345                                } else {
346                                        info.addPara("The raid on the " + system.getNameWithLowercaseType() + " has failed", 
347                                                        tc, initPad);
348                                }
349                        }
350                } else {
351                        info.addPara(system.getNameWithLowercaseType(), 
352                                                 tc, initPad);
353                }
354                initPad = 0f;
355                if (eta > 1 && failStage < 0 && getListInfoParam() != ENTERED_SYSTEM_UPDATE) {
356                        String days = getDaysString(eta);
357                        info.addPara("Estimated %s " + days + " until arrival", 
358                                        initPad, tc, h, "" + (int)Math.round(eta));
359                        initPad = 0f;
360                }
361                
362                unindent(info);
363        }
364        
365        @Override
366        public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) {
367                Color c = getTitleColor(mode);
368                
369                if (isPlayerTargeted() && false) {
370                        info.setParaSmallInsignia();
371                } else {
372                        info.setParaFontDefault();
373                }
374                
375                info.addPara(getName(), c, 0f);
376                info.setParaFontDefault();
377                addBulletPoints(info, mode);
378        }
379        
380        protected MarketAPI getFirstSource() {
381                AssembleStage as = getAssembleStage();
382                if (as == null) return null;
383                if (as.getSources() == null || as.getSources().isEmpty()) return null;
384                return as.getSources().get(0);
385        }
386        
387        @Override
388        public void createSmallDescription(TooltipMakerAPI info, float width, float height) {
389                Color h = Misc.getHighlightColor();
390                Color g = Misc.getGrayColor();
391                Color tc = Misc.getTextColor();
392                float pad = 3f;
393                float opad = 10f;
394                
395                info.addImage(getFactionForUIColors().getLogo(), width, 128, opad);
396                
397                FactionAPI faction = getFaction();
398                String has = faction.getDisplayNameHasOrHave();
399                String is = faction.getDisplayNameIsOrAre();
400                
401                AssembleStage as = getAssembleStage();
402                MarketAPI source = getFirstSource();
403                
404                //float raidStr = as.getSpawnFP();
405                float raidStr = as.getOrigSpawnFP();
406                raidStr = Misc.getAdjustedStrength(raidStr, source);
407                
408//              String strDesc = "";
409//              // fp, multiplied by roughly 0.25 to 4, depending on quality, colony size, doctrine
410//              if (raidStr < 150) {
411//                      strDesc = "very weak";
412//              } else if (raidStr < 300) {
413//                      strDesc = "somewhat weak";
414//              } else if (raidStr < 500) {
415//                      strDesc = "fairly strong";
416//              } else if (raidStr < 1000) {
417//                      strDesc = "strong";
418//              } else {
419//                      strDesc = "very strong";
420//              }
421                
422                String strDesc = getRaidStrDesc();
423                int numFleets = (int) getOrigNumFleets();
424                String fleets = "fleets";
425                if (numFleets == 1) fleets = " large fleet, or several smaller ones";
426                
427                LabelAPI label = info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " " + is + 
428                                " conducting a raid of the " + system.getName() + ". The raiding forces are " +
429                                                "projected to be " + strDesc + 
430                                                " and likely comprised of " + numFleets + " " + fleets + ".",
431                                opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle());
432                label.setHighlight(faction.getDisplayNameWithArticleWithoutArticle(), strDesc, "" + numFleets);
433                label.setHighlightColors(faction.getBaseUIColor(), h, h);
434                
435                List<MarketAPI> targets = new ArrayList<MarketAPI>();
436                for (MarketAPI market : Misc.getMarketsInLocation(system)) {
437                        if (market.getFaction().isHostileTo(faction)) {
438                                targets.add(market);
439                        }
440                }
441                
442                defenderStr = WarSimScript.getEnemyStrength(getFaction(), system);
443                
444                List<MarketAPI> safe = new ArrayList<MarketAPI>();
445                List<MarketAPI> unsafe = new ArrayList<MarketAPI>();
446                for (MarketAPI market : targets) {
447                        float defensiveStr = defenderStr + WarSimScript.getStationStrength(market.getFaction(), system, market.getPrimaryEntity());
448                        if (defensiveStr > raidStr * 1.25f) {
449                                safe.add(market);
450                        } else {
451                                unsafe.add(market);
452                        }
453                }
454                
455                if (!isEnding()) {
456                        if (targets.isEmpty()) {
457                                info.addPara("There are no colonies for the raid to target in the system.", opad);
458                        } else {
459                                boolean showSafe = false;
460                                if (raidStr < defenderStr * 0.75f) {
461                                        info.addPara("The raiding forces should be outmatched by fleets defending the system. In the absence of " +
462                                                        "other factors, the raid is unlikely to find success.", opad);
463                                } else if (raidStr < defenderStr * 1.25f) {
464                                        info.addPara("The raiding forces are evenly matched with fleets defending the system.", opad);
465                                        showSafe = true;
466                                } else {
467                                        info.addPara("The raiding forces are superior to the fleets defending the system.", opad);
468                                        showSafe = true;
469                                }
470                                if (showSafe) {
471                                        if (safe.size() == targets.size()) {
472                                                info.addPara("However, all colonies should be safe from the raid, " +
473                                                                         "owing to their orbital defenses.", opad);
474                                        } else {
475                                                info.addPara("Considering orbital defenses (if any), the following colonies are " +
476                                                                "at risk from the raid:", opad);
477                                                float initPad = opad;
478                                                for (MarketAPI market : unsafe) {
479                                                        addMarketToList(info, market, initPad, tc);
480                                                        initPad = 0f;
481                                                }
482                                                
483        //                                      info.addPara("Unless the raid is stopped, these colonies " +
484        //                                                      "may suffer " +
485        //                                                      "reduced stability, infrastructure damage, and a possible loss of stockpiled resources.", opad);
486                                                
487                                        }
488                                }
489                        }
490                }
491                
492                info.addSectionHeading("Status", 
493                                   faction.getBaseUIColor(), faction.getDarkUIColor(), Alignment.MID, opad);
494                
495                for (RaidStage stage : stages) {
496                        stage.showStageInfo(info);
497                        if (getStageIndex(stage) == failStage) break;
498                }
499        }
500        
501        
502        
503        @Override
504        public String getIcon() {
505                return faction.getCrest();
506        }
507        
508        @Override
509        public Set<String> getIntelTags(SectorMapAPI map) {
510                Set<String> tags = super.getIntelTags(map);
511                tags.add(Tags.INTEL_MILITARY);
512                if (!Misc.getMarketsInLocation(system, Factions.PLAYER).isEmpty()) {
513                        tags.add(Tags.INTEL_COLONIES);
514                }
515                tags.add(getFaction().getId());
516                return tags;
517        }
518        
519        public String getSortString() {
520                return "Raid";
521        }
522        
523        public String getName() {
524                String base = Misc.ucFirst(getFaction().getPersonNamePrefix()) + " Raid";
525                if (isEnding()) {
526                        if (isSendingUpdate() && failStage >= 0) {
527                                return base + " - Failed";
528                        }
529                        for (RaidStage stage : stages) {
530                                if (stage instanceof ActionStage && stage.getStatus() == RaidStageStatus.SUCCESS) {
531                                        return base + " - Successful";
532                                }
533                        }
534                        return base + " - Over";
535                }
536                return base;
537        }
538        
539        public boolean isFailed() {
540                return failStage >= 0;
541        }
542        
543        public boolean isSucceeded() {
544                for (RaidStage stage : stages) {
545                        if (stage instanceof ActionStage && stage.getStatus() == RaidStageStatus.SUCCESS) {
546                                return true;
547                        }
548                }
549                return false;
550        }
551        
552        
553        @Override
554        public FactionAPI getFactionForUIColors() {
555                return getFaction();
556        }
557        
558        public FactionAPI getFaction() {
559                return faction;
560        }
561
562        public String getSmallDescriptionTitle() {
563                return getName();
564        }
565
566        @Override
567        public SectorEntityToken getMapLocation(SectorMapAPI map) {
568                return system.getHyperspaceAnchor();
569        }
570        
571        public List<ArrowData> getArrowData(SectorMapAPI map) {
572                AssembleStage as = getAssembleStage();
573                if (as == null || !as.isSourceKnown()) return null;
574                
575                
576                SectorEntityToken from = as.gatheringPoint;
577                if (system == null|| system == from.getContainingLocation()) return null;
578                
579                List<ArrowData> result = new ArrayList<ArrowData>();
580                
581                SectorEntityToken entityFrom = from;
582                if (map != null && delegate instanceof IntelInfoPlugin && delegate != this) {
583                        SectorEntityToken iconEntity = map.getIntelIconEntity((IntelInfoPlugin)delegate);
584                        if (iconEntity != null) {
585                                entityFrom = iconEntity;
586                        }
587                }
588                
589                ArrowData arrow = new ArrowData(entityFrom, system.getCenter());
590                arrow.color = getFactionForUIColors().getBaseUIColor();
591                arrow.width = 20f;
592                result.add(arrow);
593                
594                return result;
595        }
596
597        public boolean shouldCancelRouteAfterDelayCheck(RouteData route) {
598                return false;
599        }
600
601        public boolean shouldRepeat(RouteData route) {
602                return false;
603        }
604
605        public void reportAboutToBeDespawnedByRouteManager(RouteData route) {
606        }
607
608        
609        
610        public CampaignFleetAPI spawnFleet(RouteData route) {
611                
612                Random random = route.getRandom();
613                
614                MarketAPI market = route.getMarket();
615                CampaignFleetAPI fleet = createFleet(market.getFactionId(), route, market, null, random);
616                
617                if (fleet == null || fleet.isEmpty()) return null;
618                
619                //fleet.addEventListener(this);
620                
621                market.getContainingLocation().addEntity(fleet);
622                fleet.setFacing((float) Math.random() * 360f);
623                // this will get overridden by the patrol assignment AI, depending on route-time elapsed etc
624                fleet.setLocation(market.getPrimaryEntity().getLocation().x, market.getPrimaryEntity().getLocation().x);
625                
626                fleet.addScript(createAssignmentAI(fleet, route));
627                
628                return fleet;
629        }
630        
631        public RouteFleetAssignmentAI createAssignmentAI(CampaignFleetAPI fleet, RouteData route) {
632                ActionStage action = getActionStage();
633                FleetActionDelegate delegate = null;
634                if (action instanceof FleetActionDelegate) {
635                        delegate = (FleetActionDelegate) action;
636                }
637                return new RaidAssignmentAI(fleet, route, delegate);
638        }
639        
640        public CampaignFleetAPI createFleet(String factionId, RouteData route, MarketAPI market, Vector2f locInHyper, Random random) {
641                if (random == null) random = new Random();
642                
643                OptionalFleetData extra = route.getExtra();
644                
645                float combat = extra.fp;
646                float tanker = extra.fp * (0.1f + random.nextFloat() * 0.05f);
647                float transport = extra.fp * (0.1f + random.nextFloat() * 0.05f);
648                float freighter = 0f;
649                combat -= tanker;
650                combat -= transport;
651                
652                FleetParamsV3 params = new FleetParamsV3(
653                                market, 
654                                locInHyper,
655                                factionId,
656                                route == null ? null : route.getQualityOverride(),
657                                extra.fleetType,
658                                combat, // combatPts
659                                freighter, // freighterPts 
660                                tanker, // tankerPts
661                                transport, // transportPts
662                                0f, // linerPts
663                                0f, // utilityPts
664                                0f // qualityMod, won't get used since routes mostly have quality override set
665                                );
666                //params.ignoreMarketFleetSizeMult = true; // already accounted for in extra.fp
667//              if (DebugFlags.RAID_DEBUG) {
668//                      params.qualityOverride = 1f;
669//              }
670                if (route != null) {
671                        params.timestamp = route.getTimestamp();
672                }
673                params.random = random;
674                CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params);
675                
676                if (fleet == null || fleet.isEmpty()) return null;
677                
678                fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_WAR_FLEET, true);
679                fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_RAIDER, true);
680                
681                if (fleet.getFaction().getCustomBoolean(Factions.CUSTOM_PIRATE_BEHAVIOR)) {
682                        fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PIRATE, true);
683                }
684                
685                String postId = Ranks.POST_PATROL_COMMANDER;
686                String rankId = Ranks.SPACE_COMMANDER;
687                
688                fleet.getCommander().setPostId(postId);
689                fleet.getCommander().setRankId(rankId);
690                
691                return fleet;
692        }
693        
694        
695        public float getRaidFPAdjusted() {
696                //AssembleStage as = getAssembleStage();
697                //MarketAPI source = as.getSources().get(0);
698                MarketAPI source = getFirstSource();
699                float raidFP = getRaidFP();
700                
701                // fp, multiplied by roughly 0.25 to 4, depending on quality, colony size, doctrine
702                float raidStr = Misc.getAdjustedFP(raidFP, source);
703                return raidStr;
704        }
705        
706        public float getRaidFP() {
707                AssembleStage as = getAssembleStage();
708                float raidStr = 0f;
709                for (RouteData route : as.getRoutes()) {
710                        CampaignFleetAPI fleet = route.getActiveFleet();
711                        if (fleet != null) {
712                                float mult = Misc.getAdjustedFP(1f, route.getMarket());
713                                if (mult < 1) mult = 1f;
714                                raidStr += fleet.getFleetPoints() / mult;
715                        } else {
716                                raidStr += route.getExtra().fp;
717                        }
718                }
719                if (raidStr <= 0 || as.getSpawnFP() > 0) {
720                        raidStr = Math.max(as.getOrigSpawnFP(), raidStr);
721                }
722                float raidFP = raidStr;
723                return raidFP;
724        }
725        
726        public float getNumFleets() {
727                AssembleStage as = getAssembleStage();
728                float num = as.getRoutes().size();
729                if (as.getSpawnFP() > 0) {
730                        num = Math.max(num, as.getOrigSpawnFP() / as.getLargeSize(false));
731                }
732                if (num < 1) num = 1;
733                return num;
734        }
735        
736        public float getOrigNumFleets() {
737                AssembleStage as = getAssembleStage();
738                float num = (float) Math.ceil(as.getOrigSpawnFP() / as.getLargeSize(false));
739                if (num < 1) num = 1;
740                return num;
741        }
742        
743        public float getRaidStr() {
744//              AssembleStage as = getAssembleStage();
745//              MarketAPI source = as.getSources().get(0);
746                MarketAPI source = getFirstSource();
747                float raidFP = getRaidFP();
748                
749                // fp, multiplied by roughly 0.25 to 4, depending on quality, colony size, doctrine
750                float raidStr = Misc.getAdjustedStrength(raidFP, source);
751                return raidStr;
752        }
753        
754        protected String getRaidStrDesc() {
755                return Misc.getStrengthDesc(getRaidStr());
756        }
757
758        public void addStandardStrengthComparisons(TooltipMakerAPI info, 
759                                                                        MarketAPI target, FactionAPI targetFaction, 
760                                                                        boolean withGround, boolean withBombard,
761                                                                        String raid, String raids) {
762                Color h = Misc.getHighlightColor();
763                float opad = 10f;
764                
765                //AssembleStage as = getAssembleStage();
766                //MarketAPI source = as.getSources().get(0);
767                float raidFP = getRaidFPAdjusted() / getNumFleets();
768                float raidStr = getRaidStr();
769                
770                //float defenderStr = WarSimScript.getEnemyStrength(getFaction(), system);
771                float defenderStr = WarSimScript.getFactionStrength(targetFaction, system);
772                float defensiveStr = defenderStr + WarSimScript.getStationStrength(targetFaction, system, target.getPrimaryEntity());
773                
774                float assumedRaidGroundStr = raidFP * Misc.FP_TO_GROUND_RAID_STR_APPROX_MULT;
775                float re = MarketCMD.getRaidEffectiveness(target, assumedRaidGroundStr);
776                
777                String spaceStr = "";
778                String groundStr = "";
779                String outcomeDesc = null;
780                boolean even = false;
781                if (raidStr < defensiveStr * 0.75f) {
782                        spaceStr = "outmatched";
783                        if (outcomeDesc == null) outcomeDesc = "The " + raid + " is likely to be defeated in orbit";
784                } else if (raidStr < defensiveStr * 1.25f) {
785                        spaceStr = "evenly matched";
786                        if (outcomeDesc == null) outcomeDesc = "The " + raids + " outcome is uncertain";
787                        even = true;
788                } else {
789                        spaceStr = "superior";
790                        if (!withGround && !withBombard) {
791                                if (outcomeDesc == null) outcomeDesc = "The " + raid + " is likely to be successful";
792                        }
793                }
794                
795                if (withGround) {
796                        if (re < 0.33f) {
797                                groundStr = "outmatched";
798                                if (outcomeDesc == null || even) outcomeDesc = "The " + raid + " is likely to be largely repelled by the ground defences";
799                        } else if (re < 0.66f) {
800                                groundStr = "evenly matched";
801                                if (outcomeDesc == null) outcomeDesc = "The " + raids + " outcome is uncertain";
802                        } else {
803                                groundStr = "superior";
804                                if (outcomeDesc == null) outcomeDesc = "The " + raid + " is likely to be successful";
805                        }
806                        //info.addPara("Compared to the defenses of " + target.getName() + ", the " + raids + " space forces are %s " +
807                        info.addPara("Compared to the defenses, the " + raids + " space forces are %s " +
808                                        "and its ground forces are %s." +
809                                        " " + outcomeDesc + ".", opad, h, spaceStr, groundStr);
810                } else if (withBombard) {
811                        float required = MarketCMD.getBombardmentCost(target, null);
812                        float available = raidFP * Misc.FP_TO_BOMBARD_COST_APPROX_MULT;
813                        
814                        if (required * .67 > available) {
815                                groundStr = "outmatched";
816                                if (outcomeDesc == null) outcomeDesc = "The bombardment is likely to be countered by the ground defences";
817                        } else if (required * 1.33f > available) {
818                                groundStr = "evenly matched";
819                                if (outcomeDesc == null) outcomeDesc = "The bombardment's outcome is uncertain";
820                        } else {
821                                groundStr = "superior";
822                                if (outcomeDesc == null) outcomeDesc = "The bombardment is likely to be successful";
823                        }
824                        //info.addPara("Compared to the defenses of " + target.getName() + ", the " + raids + " space forces are %s " +
825                        info.addPara("Compared to the defenses, the " + raids + " space forces are %s. " +
826                                        "" + outcomeDesc + ".", opad, h, spaceStr, groundStr);
827                        
828                } else {
829                        info.addPara("Compared to the defenses of " + target.getName() + ", " +
830                                                 "the " + raids + " space forces are %s." +
831                                                 " " + outcomeDesc + ".", opad, h, spaceStr, groundStr);
832                }
833        }
834        
835        @Override
836        public IntelSortTier getSortTier() {
837                if (isPlayerTargeted() && false) {
838                        return IntelSortTier.TIER_2;
839                }
840                return super.getSortTier();
841        }
842}
843
844
845
846
847
848
849