001package com.fs.starfarer.api.impl.campaign.intel.group;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Random;
006import java.util.Set;
007
008import java.awt.Color;
009
010import org.lwjgl.util.vector.Vector2f;
011
012import com.fs.starfarer.api.Global;
013import com.fs.starfarer.api.campaign.CampaignFleetAPI;
014import com.fs.starfarer.api.campaign.FactionAPI;
015import com.fs.starfarer.api.campaign.LocationAPI;
016import com.fs.starfarer.api.campaign.SectorEntityToken;
017import com.fs.starfarer.api.campaign.StarSystemAPI;
018import com.fs.starfarer.api.campaign.econ.Industry;
019import com.fs.starfarer.api.campaign.econ.MarketAPI;
020import com.fs.starfarer.api.campaign.rules.MemoryAPI;
021import com.fs.starfarer.api.impl.campaign.command.WarSimScript;
022import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator;
023import com.fs.starfarer.api.impl.campaign.fleets.RouteManager;
024import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData;
025import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
026import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner;
027import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment;
028import com.fs.starfarer.api.impl.campaign.ids.Conditions;
029import com.fs.starfarer.api.impl.campaign.ids.Factions;
030import com.fs.starfarer.api.impl.campaign.ids.Industries;
031import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
032import com.fs.starfarer.api.impl.campaign.ids.Tags;
033import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
034import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI.GenericRaidParams;
035import com.fs.starfarer.api.impl.campaign.missions.FleetCreatorMission;
036import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI.TravelState;
037import com.fs.starfarer.api.ui.LabelAPI;
038import com.fs.starfarer.api.ui.SectorMapAPI;
039import com.fs.starfarer.api.ui.TooltipMakerAPI;
040import com.fs.starfarer.api.util.CountingMap;
041import com.fs.starfarer.api.util.Misc;
042
043/**
044 * Uses a single RouteData as a placeholder to control the movement and spawning of multiple fleets
045 * to make it easier to sync them up. Once the fleets have spawned, they will not auto-despawn and will 
046 * carry out their actions until the action sequence is completed.
047 * 
048 * @author Alex
049 *
050 * Copyright 2023 Fractal Softworks, LLC
051 */
052/**
053 * @author Alex
054 *
055 * Copyright 2023 Fractal Softworks, LLC
056 */
057public abstract class FleetGroupIntel extends BaseIntelPlugin implements RouteFleetSpawner {
058        
059        public static interface FGIEventListener {
060                public void reportFGIAborted(FleetGroupIntel intel);
061        }
062        
063        public static String ABORT_UPDATE  = "abort_update";
064        public static String FLEET_LAUNCH_UPDATE  = "fleet_launch_update";
065        
066        public static final String KEY_SPAWN_FP = "$core_fgiSpawnFP";
067        
068        public static final String NEVER_STRAGGLER = "$core_fgiNeverStraggler";
069        public static final String KEY_POTENTIAL_STRAGGLER = "$core_fgiMaybeStraggler";
070        public static final String KEY_STRAGGLER_RETURN_COUNTDOWN = "$core_fgiStragglerReturnCountdown";
071
072        
073        public static boolean DEBUG = false;
074        
075
076        protected Random random = new Random();
077        protected List<FGAction> actions = new ArrayList<FGAction>();
078        protected RouteData route;
079        protected RouteSegment prevSegment;
080        protected List<CampaignFleetAPI> fleets = new ArrayList<CampaignFleetAPI>();
081        protected boolean spawnedFleets = false;
082        
083        protected boolean doIncrementalSpawn = true;
084        protected List<CampaignFleetAPI> spawning = new ArrayList<CampaignFleetAPI>();
085        protected LocationAPI spawnLocation = null;
086        protected float spawnDelay = 0f;
087        protected float elapsed = 0f;
088        
089        protected boolean aborted = false;
090        protected float totalFPSpawned = 0f;
091        protected float fleetAbortsMissionFPFraction = 0.33f;
092        protected float groupAbortsMissionFPFraction = 0.33f;
093        protected SectorEntityToken returnLocation;
094        protected FactionAPI faction;
095        protected int approximateNumberOfFleets = 3;
096        
097        protected FGIEventListener listener;
098
099        public FleetGroupIntel() {
100                Global.getSector().addScript(this);
101        }
102        
103        protected Object readResolve() {
104                if (random == null) {
105                        random = new Random();
106                }
107                
108                //random = new Random(142343232L);
109                //System.out.println("RNG CHECK 1: " + random.nextLong());
110                return this;
111        }
112        
113        public float getETAUntil(String actionId) {
114                return getETAUntil(actionId, false);
115        }
116        public float getETAUntil(String actionId, boolean untilEndOfAction) {
117                float eta = getDelayRemaining();
118                for (FGAction action : actions) {
119                        if (action.getId() != null && action.getId().equals(actionId)) {
120                                if (untilEndOfAction) {
121                                        eta += action.getEstimatedDaysToComplete();
122                                }
123                                return eta;
124                        } else {
125                                eta += action.getEstimatedDaysToComplete();
126                        }
127                }
128                return 0f;
129        }
130        
131        @Override
132        protected void notifyEnded() {
133                super.notifyEnded();
134                Global.getSector().removeScript(this);
135        }
136        
137        
138        protected boolean sourceWasEverMilitaryMarket = false;
139        protected boolean sendFleetLaunchUpdate = false;
140        protected boolean isSourceFunctionalMilitaryMarket() {
141                if (getSource() == null) return false;
142                MarketAPI market = getSource().getMarket();
143                if (market == null || market.isPlanetConditionMarketOnly()) return false;
144                
145                if (market.hasCondition(Conditions.DECIVILIZED)) {
146                        return false;
147                }
148                
149                Industry b = market.getIndustry(Industries.MILITARYBASE);
150                if (b == null) b = market.getIndustry(Industries.HIGHCOMMAND);
151                if (b == null || b.isDisrupted() || !b.isFunctional()) {
152                        return false;
153                }
154                return true;
155        }
156        
157        public boolean isInPreLaunchDelay() {
158                return route != null && route.getElapsed() <= 0f && route.getDelay() > 0;
159        }
160        
161        /**
162         * route needs to be created when this method is called.
163         * @param delay
164         */
165        public void setPreFleetDeploymentDelay(float delay) {
166                if (route != null) {
167                        route.setDelay(delay);
168                }
169        }
170        
171        public float getDelayRemaining() {
172                if (!isInPreLaunchDelay() || route == null) return 0f;
173                return route.getDelay();
174        }
175        
176        public float getElapsed() {
177                return elapsed;
178        }
179
180        public void setElapsed(float elapsed) {
181                this.elapsed = elapsed;
182        }
183
184        @Override
185        public void advance(float amount) {
186                super.advance(amount);
187                
188                elapsed += amount;
189                
190                if (isEnded() || isEnding() || isAborted()) return;
191                if (route == null) return;
192
193                if (isInPreLaunchDelay()) {
194                        sendFleetLaunchUpdate = true;
195                        boolean mil = isSourceFunctionalMilitaryMarket();
196                        sourceWasEverMilitaryMarket |= mil;
197                        if (!mil && sourceWasEverMilitaryMarket) {
198                                abort();
199                                return;
200                        }
201                        return;
202                }
203                
204                if (sendFleetLaunchUpdate) {
205                        sendFleetLaunchUpdate = false;
206                        sendUpdateIfPlayerHasIntel(FLEET_LAUNCH_UPDATE, !isPlayerTargeted());
207                }
208                
209                if (!isSucceeded() && !isEnding() && !isAborted() && shouldAbort()) {
210                        abort();
211                        return;
212                }
213                
214                if (!spawnedFleets) {
215                        if (!isSucceeded() && route.getExtra() != null && route.getExtra().damage != null && 
216                                        1f - route.getExtra().damage < getGroupAbortsMissionFPFraction()) {
217                                abort();
218                                return;
219                        }
220                        
221                        RouteSegment curr = route.getCurrent();
222                        if (curr != prevSegment || route.isExpired()) {
223                                if (prevSegment != null && prevSegment.custom instanceof FGAction) {
224                                        FGAction action = (FGAction) prevSegment.custom;
225                                        action.notifySegmentFinished(prevSegment);
226                                        actions.remove(action);
227                                        notifyActionFinished(action);
228                                        
229                                        if (route.isExpired()) {
230                                                finish(false);
231                                                return;
232                                        }
233                                }
234                        }
235                        prevSegment = curr;
236                } else {
237                        handleIncrementalSpawning(amount);
238                        pruneDestroyedOrDamagedFleetsAndAbortIfNeeded();
239                        
240                        if (actions.isEmpty()) {
241                                finish(false);
242                                return;
243                        }
244                        
245                        FGAction action = actions.get(0);
246                        if (action.isActionFinished()) {
247                                actions.remove(0);
248                                notifyActionFinished(action);
249                                return;
250                        }
251                        
252                        action.directFleets(amount);
253
254                        //System.out.println(action + " " + action.isActionFinished());
255                        if (action.isActionFinished()) {
256                                actions.remove(0);
257                                notifyActionFinished(action);
258                                return;
259                        }
260                }
261                
262        }
263        
264        protected boolean shouldAbort() {
265                return false;
266        }
267        
268        protected boolean shouldSendIntelUpdateWhenActionFinished(FGAction action) {
269                // presumably, !isFailed() is correct here but not 100% sure why -am
270                if (action instanceof FGWaitAction && (isAborted() || !isFailed())) {
271                        return false;
272                }
273                
274                if (action instanceof FGWaitAction) {
275                        FGWaitAction wait = (FGWaitAction) action;
276                        if (wait.getOrigDurDays() <= 0f) return false;
277                }
278                return action != null && action.getId() != null;
279        }
280        
281        protected void notifyActionFinished(FGAction action) {
282                if (action != null && shouldSendIntelUpdateWhenActionFinished(action)) {
283                        sendUpdateIfPlayerHasIntel(action.getId(), !isPlayerTargeted());
284                }
285        }
286        
287        
288        protected void pruneDestroyedOrDamagedFleetsAndAbortIfNeeded() {
289                if (isSucceeded()) {
290                        List<CampaignFleetAPI> remove = new ArrayList<CampaignFleetAPI>();
291                        for (CampaignFleetAPI fleet : fleets) {
292                                if (!fleet.isAlive()) {
293                                        remove.add(fleet);
294                                }
295                        }
296                        fleets.removeAll(remove);
297                        return; // already returning at this point, still clean up destroyed fleets though
298                }
299                
300                checkStragglers();
301                
302                List<CampaignFleetAPI> remove = new ArrayList<CampaignFleetAPI>();
303                float totalFP = 0f;
304                for (CampaignFleetAPI fleet : fleets) {
305                        float spawnFP = fleet.getMemoryWithoutUpdate().getFloat(KEY_SPAWN_FP);
306                        if (!fleet.isAlive()) {
307                                remove.add(fleet);
308                        } else if (fleet.getFleetPoints() <= spawnFP * getFleetAbortsMissionFPFraction()) {
309                                remove.add(fleet);
310                                giveReturnAssignments(fleet);
311                        } else {
312                                totalFP += fleet.getFleetPoints();
313                        }
314                }
315                
316                fleets.removeAll(remove);
317
318                if (totalFP < totalFPSpawned * getGroupAbortsMissionFPFraction()) {
319                        abort();
320                        return;
321                }
322                
323        }
324        
325        protected void checkStragglers() {
326                List<CampaignFleetAPI> remove = new ArrayList<CampaignFleetAPI>();
327                CountingMap<LocationAPI> fleetLocs = new CountingMap<LocationAPI>();
328                
329                for (CampaignFleetAPI fleet : fleets) {
330                        fleetLocs.add(fleet.getContainingLocation());
331                }
332                
333                LocationAPI withMostFleets = null;
334                int maxCount = 0;
335                for (LocationAPI loc : fleetLocs.keySet()) {
336                        int count = fleetLocs.getCount(loc);
337                        if (count > maxCount) {
338                                withMostFleets = loc;
339                                maxCount = count;
340                        }
341                }
342                
343                if (withMostFleets == null) return;
344                
345                Vector2f com = new Vector2f();
346                float weight = 0f;
347                for (CampaignFleetAPI fleet : fleets) {
348                        if (fleet.getContainingLocation() != withMostFleets) continue;
349                        float w = fleet.getFleetPoints();
350                        Vector2f loc = new Vector2f(fleet.getLocation());
351                        loc.scale(w);
352                        Vector2f.add(com, loc, com);
353                        weight += w;
354                }
355                
356                if (weight < 1f) weight = 1f;
357                com.scale(1f / weight);
358                
359                FGAction action = getCurrentAction();
360                boolean canBeStragglers = action != null && 
361                                (action instanceof FGTravelAction || action instanceof FGWaitAction);
362                
363                int maybeStragglers = 0;
364                for (CampaignFleetAPI fleet : fleets) {
365                        boolean potentialStraggler = fleet.getContainingLocation() != withMostFleets;
366                        if (!potentialStraggler) {
367                                potentialStraggler |= Misc.getDistance(fleet.getLocation(), com) > 4000;
368                        }
369                        if (fleet.getMemoryWithoutUpdate().getBoolean(NEVER_STRAGGLER) || !canBeStragglers) {
370                                potentialStraggler = false;
371                        }
372                        
373                        MemoryAPI mem = fleet.getMemoryWithoutUpdate();
374                        if (mem.contains(KEY_POTENTIAL_STRAGGLER)) {
375                                maybeStragglers++;
376                        }
377                        if (!potentialStraggler && mem.contains(KEY_POTENTIAL_STRAGGLER)) {
378                                mem.unset(KEY_POTENTIAL_STRAGGLER);
379                                mem.unset(KEY_STRAGGLER_RETURN_COUNTDOWN);
380                        } else if (mem.contains(KEY_POTENTIAL_STRAGGLER) && !mem.contains(KEY_STRAGGLER_RETURN_COUNTDOWN)) {
381                                remove.add(fleet);
382//                              if (fleet.getName().toLowerCase().contains("tactistar")) {
383//                                      System.out.println("fewfwef23r23r23r");
384//                              }
385                                giveReturnAssignments(fleet);
386                        } else if (potentialStraggler && !mem.contains(KEY_POTENTIAL_STRAGGLER)) {
387                                mem.set(KEY_POTENTIAL_STRAGGLER, true);
388                                mem.set(KEY_STRAGGLER_RETURN_COUNTDOWN, true, getPotentialStragglerCountdownDays());
389                        }
390                }
391                
392                fleets.removeAll(remove);
393                
394                //System.out.println(getClass().getSimpleName() + " maybe stragglers: " + maybeStragglers + ", fleets: " + fleets.size());
395        }
396        
397        protected float getPotentialStragglerCountdownDays() {
398                //if (true) return 0.5f;
399                return 30f;
400        }
401        
402        protected boolean failedButNotDefeated = false;
403        public boolean isFailedButNotDefeated() {
404                return failedButNotDefeated;
405        }
406
407        public void setFailedButNotDefeated(boolean failedButNotDefeated) {
408                this.failedButNotDefeated = failedButNotDefeated;
409        }
410        
411        public void abort() {
412                finish(true);
413        }
414        public void finish(boolean isAbort) {
415                boolean wasEnding = isEnding();
416                aborted = isAbort;
417                endAfterDelay();
418                
419                if (!isAbort) {
420                        setFailedButNotDefeated(true);
421                }
422                
423                if (spawnedFleets && !actions.isEmpty()) {
424                        if (!actions.get(0).isActionFinished()) {
425                                actions.get(0).setActionFinished(true);
426                        }
427                }
428                
429                if (route != null) {
430                        route.expire();
431                        RouteManager.getInstance().removeRoute(route);
432                }
433                
434                giveFleetsReturnAssignments();
435                
436                if (!wasEnding && isAbort) {
437                        sendUpdateIfPlayerHasIntel(ABORT_UPDATE, !isPlayerTargeted());
438                        
439                        if (listener != null) {
440                                listener.reportFGIAborted(this);
441                        }
442                }
443        }
444        
445        public boolean isSpawning() {
446                return spawning != null && !spawning.isEmpty();
447        }
448        
449        public boolean isAborted() {
450                return aborted;
451        }
452
453
454        protected void giveFleetsReturnAssignments() {
455                for (CampaignFleetAPI fleet : fleets) {
456                        giveReturnAssignments(fleet);
457                }
458        }
459        
460        protected void giveReturnAssignments(CampaignFleetAPI fleet) {
461                fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_FLEET_DO_NOT_GET_SIDETRACKED, true);
462                if (returnLocation != null) {
463                        Misc.giveStandardReturnAssignments(fleet, returnLocation, null, true);
464                } else {
465                        Misc.giveStandardReturnToSourceAssignments(fleet, true);
466                }
467        }
468        
469        
470        protected void createRoute(String factionId, int approximateTotalDifficultyPoints,
471                        int approximateNumberOfFleets, Object custom) {
472                createRoute(factionId, approximateTotalDifficultyPoints, approximateNumberOfFleets, custom, null);
473        }
474        protected void createRoute(String factionId, int approximateTotalDifficultyPoints,
475                                                                int approximateNumberOfFleets, Object custom, GenericRaidParams params) {
476                OptionalFleetData extra = new OptionalFleetData();
477                extra.strength = getApproximateStrengthForTotalDifficultyPoints(factionId, approximateTotalDifficultyPoints);
478                extra.factionId = faction.getId(); // needed for WarSimScript etc
479                if (params != null && params.source != null) {
480                        extra.quality = Misc.getShipQuality(params.source, extra.factionId);
481                }
482                route = RouteManager.getInstance().addRoute("FGI_" + getClass().getSimpleName(), null, 
483                                                        Misc.genRandomSeed(), extra, this, custom);
484                
485                for (FGAction action : actions) {
486                        int before = route.getSegments().size();
487                        action.addRouteSegment(route);
488                        int after = route.getSegments().size();
489                        for (int i = before; i < after; i++) {
490                                route.getSegments().get(i).custom = action;
491                        }
492                }
493                
494                this.approximateNumberOfFleets = approximateNumberOfFleets;
495        }
496        
497        public void setRoute(RouteData route) {
498                this.route = route;
499        }
500
501        public CampaignFleetAPI spawnFleet(RouteData route) {
502                //System.out.println("RNG CHECK 2: " + random.nextLong());
503                
504                RouteManager.getInstance().removeRoute(route);
505                
506                if (isSpawnedFleets()) return null;
507                
508                if (isEnded() || isEnding()) return null;
509                if (route == null || route.getCurrent() == null) {
510                        abort();
511                        return null;
512                }
513                
514                RouteSegment curr = route.getCurrent();
515                
516                while (!actions.isEmpty()) {
517                        FGAction action = actions.get(0);
518                        if (action == curr.custom) {
519                                break;
520                        }
521                        actions.remove(0);
522                }
523                
524                if (actions.isEmpty()) {
525                        abort();
526                        return(null);
527                }
528                
529                if (FleetGroupIntel.DEBUG) {
530                        System.out.println(getClass().getSimpleName() + ": about to spawn fleets");
531                }
532                
533                spawnFleets();
534                setSpawnedFleets(true);
535                
536                if (doIncrementalSpawn && route.getElapsed() < 0.1f) {
537                        spawning.addAll(fleets);
538                        fleets.clear();
539                        for (CampaignFleetAPI fleet : spawning) {
540                                if (spawnLocation == null) {
541                                        spawnLocation = fleet.getContainingLocation();
542                                }
543                                fleet.getContainingLocation().removeEntity(fleet);
544                        }
545                        handleIncrementalSpawning(0.1f);
546                } else {
547                        for (CampaignFleetAPI fleet : fleets) {
548                                int fp = fleet.getFleetPoints();
549                                fleet.getMemoryWithoutUpdate().set(KEY_SPAWN_FP, fp);
550                                totalFPSpawned += fp;
551                        }
552                }
553                
554                
555                actions.get(0).notifyFleetsSpawnedMidSegment(curr);
556                
557                // don't return any created fleets - route is removed anyway and we're managing the fleets directly
558                return null; 
559        }
560        
561        public void setNeverStraggler(CampaignFleetAPI fleet) {
562                if (fleet == null) return;
563                fleet.getMemoryWithoutUpdate().set(NEVER_STRAGGLER, true);
564        }
565        
566        public void handleIncrementalSpawning(float amount) {
567                if (spawning == null || spawning.isEmpty() || spawnLocation == null) return;
568                float days = Misc.getDays(amount);
569                spawnDelay -= days;
570                
571                if (spawnDelay <= 0) {
572                        spawnDelay = 0.25f + (float) Math.random() * 0.25f;
573                        
574                        CampaignFleetAPI fleet = spawning.remove(0);
575                        spawnLocation.addEntity(fleet);
576                        if (getSource() != null) {
577                                fleet.setLocation(getSource().getLocation().x, getSource().getLocation().y);
578                        }
579                        fleets.add(fleet);
580                        int fp = fleet.getFleetPoints();
581                        totalFPSpawned += fp;
582                        
583                        if (spawning.isEmpty()) {
584                                spawning = null;
585                                spawnLocation = null;
586                        }
587                }
588        }
589
590        
591        public boolean isDoIncrementalSpawn() {
592                return doIncrementalSpawn;
593        }
594
595        public void setDoIncrementalSpawn(boolean doIncrementalSpawn) {
596                this.doIncrementalSpawn = doIncrementalSpawn;
597        }
598
599        public float getTotalFPSpawned() {
600                return totalFPSpawned;
601        }
602        
603
604        public void setTotalFPSpawned(float totalFPSpawned) {
605                this.totalFPSpawned = totalFPSpawned;
606        }
607
608        public void setSpawnedFleets(boolean spawnedFleets) {
609                this.spawnedFleets = spawnedFleets;
610        }
611
612        public RouteSegment getSegmentForAction(FGAction action) {
613                for (RouteSegment seg : getRoute().getSegments()) {
614                        if (seg.custom == action) return seg;
615                }
616                return null;
617        }
618        
619        public void removeAction(String id) {
620                FGAction action = getAction(id);
621                if (action != null) {
622                        actions.remove(action);
623                }
624        }
625        public FGAction getAction(String id) {
626                for (FGAction curr : actions) {
627                        if (curr.getId() != null && curr.getId().equals(id)) {
628                                return curr;
629                        }
630                }
631                return null;
632        }
633        
634        public boolean isCurrent(String id) {
635                FGAction action = getAction(id);
636                return action != null && action == getCurrentAction();
637        }
638        
639        public FGAction getCurrentAction() {
640                if (actions.isEmpty()) return null;
641                return actions.get(0);
642        }
643        
644        public boolean isSpawnedFleets() {
645                return spawnedFleets;
646        }
647
648        public int getApproximateNumberOfFleets() {
649                return approximateNumberOfFleets;
650        }
651        
652        public void setApproximateNumberOfFleets(int approximateNumberOfFleets) {
653                this.approximateNumberOfFleets = approximateNumberOfFleets;
654        }
655
656        public List<CampaignFleetAPI> getFleets() {
657                return fleets;
658        }
659
660        public static TravelState getTravelState(RouteSegment segment) {
661                if (segment.isInSystem()) {
662                        return TravelState.IN_SYSTEM;
663                }
664                
665                if (segment.hasLeaveSystemPhase() && segment.getLeaveProgress() < 1f) {
666                        return TravelState.LEAVING_SYSTEM;
667                }
668                if (segment.hasEnterSystemPhase() && segment.getEnterProgress() > 0f) {
669                        return TravelState.ENTERING_SYSTEM;
670                }
671                
672                return TravelState.IN_HYPER_TRANSIT;
673        }
674        
675        public static LocationAPI getLocation(RouteSegment segment) {
676                return getLocationForState(segment, getTravelState(segment));
677        }
678        
679        public static LocationAPI getLocationForState(RouteSegment segment, TravelState state) {
680                switch (state) {
681                case ENTERING_SYSTEM: {
682                        if (segment.to != null) {
683                                return segment.to.getContainingLocation();
684                        }
685                        return segment.from.getContainingLocation();
686                }
687                case IN_HYPER_TRANSIT: return Global.getSector().getHyperspace();
688                case IN_SYSTEM: return segment.from.getContainingLocation();
689                case LEAVING_SYSTEM: return segment.from.getContainingLocation();
690                }
691                return null;
692        }
693        
694        
695        public static void setLocationAndCoordinates(CampaignFleetAPI fleet, RouteSegment current) {
696                TravelState state = getTravelState(current);
697                if (state == TravelState.LEAVING_SYSTEM) {
698                        float p = current.getLeaveProgress();
699                        SectorEntityToken jp = RouteLocationCalculator.findJumpPointToUse(fleet, current.from);
700                        if (jp == null) jp = current.from;
701                        RouteLocationCalculator.setLocation(fleet, p, current.from, jp);
702                }
703                else if (state == TravelState.ENTERING_SYSTEM) {
704                        float p = current.getEnterProgress();
705                        SectorEntityToken jp = RouteLocationCalculator.findJumpPointToUse(fleet, current.to);
706                        if (jp == null) jp = current.to;
707                        RouteLocationCalculator.setLocation(fleet, p, jp, current.to);
708                }
709                else if (state == TravelState.IN_SYSTEM) {
710                        float p = current.getTransitProgress();
711                        RouteLocationCalculator.setLocation(fleet, p, 
712                                                                                                current.from, current.to);
713                }
714                else if (state == TravelState.IN_HYPER_TRANSIT) {
715                        float p = current.getTransitProgress();
716                        SectorEntityToken t1 = Global.getSector().getHyperspace().createToken(
717                                                                                                                   current.from.getLocationInHyperspace().x, 
718                                                                                                                   current.from.getLocationInHyperspace().y);
719                        SectorEntityToken t2 = Global.getSector().getHyperspace().createToken(
720                                                                                                                   current.to.getLocationInHyperspace().x, 
721                                                                                                                   current.to.getLocationInHyperspace().y);                             
722                        RouteLocationCalculator.setLocation(fleet, p, t1, t2);
723                }
724        }
725        
726        
727        public List<FGAction> getActions() {
728                return actions;
729        }
730
731        public void addAction(FGAction action, String id) {
732                action.setId(id);
733                addAction(action);
734        }
735        public void addAction(FGAction action) {
736                action.setIntel(this);
737                actions.add(action);
738        }
739        
740        
741        public boolean shouldCancelRouteAfterDelayCheck(RouteData route) {
742                return false;
743        }
744
745        public boolean shouldRepeat(RouteData route) {
746                return false;
747        }
748
749        public void reportAboutToBeDespawnedByRouteManager(RouteData route) {
750                // should never happen since this class removes the route and never returns the created fleets to the manager
751        }
752
753        public SectorEntityToken getReturnLocation() {
754                return returnLocation;
755        }
756
757        public void setReturnLocation(SectorEntityToken returnLocation) {
758                this.returnLocation = returnLocation;
759        }
760        
761        
762        public float getFleetAbortsMissionFPFraction() {
763                return fleetAbortsMissionFPFraction;
764        }
765
766        public void setFleetAbortsMissionFPFraction(float fleetAbortsMissionFPFraction) {
767                this.fleetAbortsMissionFPFraction = fleetAbortsMissionFPFraction;
768        }
769
770        public float getGroupAbortsMissionFPFraction() {
771                return groupAbortsMissionFPFraction;
772        }
773
774        public void setGroupAbortsMissionFPFraction(float groupAbortsMissionFPFraction) {
775                this.groupAbortsMissionFPFraction = groupAbortsMissionFPFraction;
776        }
777        
778        @Override
779        public FactionAPI getFactionForUIColors() {
780                //return super.getFactionForUIColors();
781                return faction;
782        }
783
784        public void setFaction(String factionId) {
785                faction = Global.getSector().getFaction(factionId);
786                
787        }
788        public void setFaction(FactionAPI faction) {
789                this.faction = faction;
790        }
791
792        public FactionAPI getFaction() {
793                return faction;
794        }
795        
796        public RouteData getRoute() {
797                return route;
798        }
799
800        public static float getApproximateStrengthForTotalDifficultyPoints(String factionId, int points) {
801                float mult = 50f;
802                if (factionId != null) {
803                        FactionAPI faction = Global.getSector().getFaction(factionId);
804                        if (faction.getCustomBoolean("pirateBehavior")) {
805                                mult = 35f;
806                        }
807                }
808                return points * mult;
809        }
810        
811        /**
812         * Very approximately, the result is around 50 points of "effective strength" per point of difficulty.
813         * Lower (30-40) for pirates/Pathers, a bit >50 for some of the main factions.
814         * 
815         * Not a lot of difference for standard/quality/quantity, since those adjust size etc to try to stay even.
816         */
817        public static void computeSampleFleetStrengths() {
818                String factionId = null;
819                factionId = Factions.LUDDIC_CHURCH;
820                factionId = Factions.LUDDIC_PATH;
821                factionId = Factions.TRITACHYON;
822                factionId = Factions.DIKTAT;
823                factionId = Factions.REMNANTS;
824                factionId = Factions.PIRATES;
825                factionId = Factions.PERSEAN;
826                factionId = Factions.HEGEMONY;
827                factionId = Factions.INDEPENDENT;
828                
829                System.out.println("---------------------------------");
830                System.out.println("FACTION: " + factionId);
831                
832                System.out.println("Difficulty\tStd\tQuality\tQuantity");
833                for (int j = 0; j < 1; j++) {
834                for (int i = 1; i <= 10; i++) {
835                        
836                        Vector2f loc = new Vector2f();
837                        
838                        float strStandard = 0;
839                        float strQuality = 0;
840                        float strQuantity = 0;
841                        
842                        {
843                        FleetCreatorMission m = new FleetCreatorMission(new Random());
844                        m.beginFleet();
845                        m.createStandardFleet(i, factionId, loc);
846                        CampaignFleetAPI fleet = m.createFleet();
847                        if (fleet != null) {
848                                strStandard = fleet.getEffectiveStrength();
849                        }
850                        }
851
852                        {
853                        FleetCreatorMission m = new FleetCreatorMission(new Random());
854                        m.beginFleet();
855                        m.createQualityFleet(i, factionId, loc);
856                        CampaignFleetAPI fleet = m.createFleet();
857                        if (fleet != null) {
858                                strQuality = fleet.getEffectiveStrength();
859                        }
860                        }
861                        
862                        {
863                        FleetCreatorMission m = new FleetCreatorMission(new Random());
864                        m.beginFleet();
865                        m.createQuantityFleet(i, factionId, loc);
866                        CampaignFleetAPI fleet = m.createFleet();
867                        if (fleet != null) {
868                                strQuantity = fleet.getEffectiveStrength();
869                        }
870                        }
871                        
872                        
873                        System.out.println("" + i + "\t\t" + (int)strStandard + "\t" + (int)strQuality + "\t" + (int)strQuantity);
874                }
875                System.out.println("---------------------------------");
876                }
877        }
878
879        protected abstract boolean isPlayerTargeted();
880        protected abstract void spawnFleets();
881        protected abstract SectorEntityToken getSource();
882        protected abstract SectorEntityToken getDestination();
883        protected abstract String getBaseName();
884
885        protected abstract void addNonUpdateBulletPoints(TooltipMakerAPI info, Color tc, Object param,
886                                                                                                         ListInfoMode mode, float initPad);
887        protected abstract void addUpdateBulletPoints(TooltipMakerAPI info, Color tc, Object param,
888                                                                                                         ListInfoMode mode, float initPad);
889        
890        protected void addStatusSection(TooltipMakerAPI info, float width, float height, float opad) {
891                
892        }
893        protected void addAssessmentSection(TooltipMakerAPI info, float width, float height, float opad) {
894                
895        }
896        protected void addBasicDescription(TooltipMakerAPI info, float width, float height, float opad) {
897                
898        }
899        
900        @Override
901        public Set<String> getIntelTags(SectorMapAPI map) {
902                Set<String> tags = super.getIntelTags(map);
903                tags.add(Tags.INTEL_MILITARY);
904                
905                if (Misc.isHyperspaceAnchor(getDestination())) {
906                        StarSystemAPI system = Misc.getStarSystemForAnchor(getDestination());
907                        if (system != null && 
908                                        !Misc.getMarketsInLocation(system, Factions.PLAYER).isEmpty()) {
909                                tags.add(Tags.INTEL_COLONIES);
910                        }
911                }
912                
913                if (getDestination() != null && 
914                                getDestination().getContainingLocation() != null &&
915                                !Misc.getMarketsInLocation(getDestination().getContainingLocation(), Factions.PLAYER).isEmpty()) {
916                        tags.add(Tags.INTEL_COLONIES);
917                }
918                tags.add(getFaction().getId());
919                return tags;
920        }
921        
922        public String getSortString() {
923                return "Fleet Group Movement";
924        }
925        
926        public String getSuccessPostfix() {
927                return " - Successful";
928        }
929        
930        public String getFailurePostfix() {
931                if (isInPreLaunchDelay()) {
932                        return " - Aborted";
933                }
934                if (isFailedButNotDefeated()) {
935                        return " - Failed";
936                }
937                return " - Defeated";
938        }
939        
940        public String getName() {
941                String base = getBaseName();
942                if (isSucceeded()) {
943                        return base + getSuccessPostfix();
944                } else if (isFailed() || isAborted()) {
945                        return base + getFailurePostfix();
946                }
947                //return base + " - Over";
948                return base;
949        }
950        
951        public boolean isSucceeded() {
952                return false;
953        }
954        public boolean isFailed() {
955                return isAborted();
956        }
957        
958        @Override
959        public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) {
960                Color c = getTitleColor(mode);
961                
962                info.setParaFontDefault();
963                
964                info.addPara(getName(), c, 0f);
965                info.setParaFontDefault();
966                addBulletPoints(info, mode);
967        }
968        
969        @Override
970        public String getIcon() {
971                return getFaction().getCrest();
972        }
973        
974        public String getSmallDescriptionTitle() {
975                return getName();
976        }
977        
978        @Override
979        public IntelSortTier getSortTier() {
980//              if (isPlayerTargeted() && false) {
981//                      return IntelSortTier.TIER_2;
982//              }
983                return super.getSortTier();
984        }
985        
986        @Override
987        public SectorEntityToken getMapLocation(SectorMapAPI map) {
988                return getDestination();
989        }
990        
991        public List<ArrowData> getArrowData(SectorMapAPI map) {
992                
993                SectorEntityToken src = getSource();
994                SectorEntityToken dest = getDestination();
995                
996                if (src == null || dest == null || src.getContainingLocation() == dest.getContainingLocation()) {
997                        return null;
998                }
999                
1000                List<ArrowData> result = new ArrayList<ArrowData>();
1001                
1002                ArrowData arrow = new ArrowData(src, dest);
1003                arrow.color = getFactionForUIColors().getBaseUIColor();
1004                arrow.width = 20f;
1005                result.add(arrow);
1006                
1007                return result;
1008        }
1009
1010        public Random getRandom() {
1011                return random;
1012        }
1013
1014        public void setRandom(Random random) {
1015                this.random = random;
1016        }
1017        
1018        protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) {
1019                float pad = 3f;
1020                float opad = 10f;
1021                
1022                float initPad = pad;
1023                if (mode == ListInfoMode.IN_DESC) initPad = opad;
1024                
1025                Color tc = getBulletColorForMode(mode);
1026
1027                
1028                bullet(info);
1029                Object param = getListInfoParam();
1030                boolean isUpdate = param != null;
1031                
1032                if (!isUpdate) {
1033                        addNonUpdateBulletPoints(info, tc, param, mode, initPad);
1034                } else {
1035                        addUpdateBulletPoints(info, tc, param, mode, initPad);
1036                }
1037                unindent(info);
1038        }
1039        
1040        protected void addFactionBulletPoint(TooltipMakerAPI info, Color tc, float initPad) {
1041                info.addPara("Faction: " + faction.getDisplayName(), initPad, tc,
1042                                 faction.getBaseUIColor(), faction.getDisplayName());
1043        }
1044        
1045        protected void addArrivedBulletPoint(String destName, Color destHL, TooltipMakerAPI info, Color tc, float initPad) {
1046                LabelAPI label = info.addPara("Arrived at " + destName, tc, initPad);
1047                
1048                if (destHL != null) {
1049                        String hl = getNameWithNoType(destName);
1050                        label.setHighlightColor(destHL);
1051                        label.highlightLast(hl);
1052                }
1053        }
1054        
1055        public String getNameWithNoType(String systemName) {
1056                String hl = systemName;
1057                if (hl != null) {
1058                        if (hl.endsWith(" system")) {
1059                                hl = hl.replaceAll(" system", "");
1060                        }
1061                        if (hl.endsWith(" nebula")) {
1062                                hl = hl.replaceAll(" nebula", "");
1063                        }
1064                        if (hl.endsWith(" star system")) {
1065                                hl = hl.replaceAll(" star system", "");
1066                        }
1067                }
1068                return hl;
1069        }
1070        
1071        public static enum ETAType {
1072                ARRIVING,
1073                RETURNING,
1074                DEPARTURE,
1075                DEPLOYMENT,
1076        }
1077        
1078        protected void addETABulletPoints(String destName, Color destHL, boolean withDepartedText, float eta, 
1079                                                                         ETAType type, TooltipMakerAPI info, Color tc, float initPad) {
1080                Color h = Misc.getHighlightColor();
1081                
1082                String hl = getNameWithNoType(destName);
1083                
1084                if (type == ETAType.DEPLOYMENT) {
1085                        if ((int) eta <= 0) {
1086                                info.addPara("Fleet deployment imminent", tc, initPad);
1087                        } else {
1088                                String days = (int)eta == 1 ? "day" : "days";
1089                                info.addPara("Estimated %s " + days + " until fleet deployment",
1090                                                initPad, tc, h, "" + (int) eta);
1091                        }
1092                        return;
1093                }
1094                
1095                if (type == ETAType.DEPARTURE) {
1096                        if ((int) eta <= 0) {
1097                                info.addPara("Departure imminent", tc, initPad);
1098                        } else {
1099                                String days = (int)eta == 1 ? "day" : "days";
1100                                info.addPara("Estimated %s " + days + " until departure",
1101                                                initPad, tc, h, "" + (int) eta);
1102                        }
1103                        return;
1104                }
1105                
1106                LabelAPI label = null;
1107                if (withDepartedText && eta <= 0) {
1108                        // operations in same location as the "from"
1109                        label = info.addPara("Operating in the " + destName, tc, initPad);
1110                        
1111                        if (destHL != null && label != null) {
1112                                label.setHighlightColor(destHL);
1113                                label.highlightLast(hl);
1114                        }
1115                } else {
1116                        if (withDepartedText) {
1117                                String pre = "Departed for ";
1118                                if (type == ETAType.RETURNING) {
1119                                        pre = "Returning to ";
1120                                }
1121                                label = info.addPara(pre + destName, tc, initPad);
1122                                
1123                                if (destHL != null && label != null) {
1124                                        label.setHighlightColor(destHL);
1125                                        label.setHighlight(hl);
1126                                }
1127                                initPad = 0f;
1128                        }
1129                        if ((int) eta > 0) {
1130                                String days = (int)eta == 1 ? "day" : "days";
1131                                String post = " until arrival";
1132                                if (type == ETAType.RETURNING) {
1133                                        post = " until return";
1134                                }
1135                                if (!withDepartedText) {
1136                                        if (type == ETAType.RETURNING) post += " to " + destName;
1137                                        else if (type == ETAType.ARRIVING) post += " at " + destName;
1138                                }
1139                                label = info.addPara("Estimated %s " + days + post, initPad, tc, h, "" + (int) eta);
1140                                
1141                                if (!withDepartedText && destHL != null && label != null) {
1142                                        label.setHighlightColors(h, destHL);
1143                                        label.setHighlight("" + (int) eta, hl);
1144                                }
1145                        } else {
1146                                String pre = "Arrival at ";
1147                                if (type == ETAType.RETURNING) {
1148                                        pre = "Return to ";
1149                                }
1150                                label = info.addPara(pre + destName + " is imminent", tc, initPad);
1151                                
1152                                if (destHL != null && label != null) {
1153                                        label.setHighlightColor(destHL);
1154                                        label.highlightLast(hl);
1155                                }
1156                        }
1157                }
1158        }
1159        
1160        
1161        @Override
1162        public void createSmallDescription(TooltipMakerAPI info, float width, float height) {
1163                Color h = Misc.getHighlightColor();
1164                Color g = Misc.getGrayColor();
1165                Color tc = Misc.getTextColor();
1166                float pad = 3f;
1167                float opad = 10f;
1168                
1169                addBasicDescription(info, width, height, opad);
1170                
1171                addAssessmentSection(info, width, height, opad);
1172                
1173                addStatusSection(info, width, height, opad);
1174                
1175                addBulletPoints(info, ListInfoMode.IN_DESC);
1176        }
1177        
1178        protected void showMarketsInDanger(TooltipMakerAPI info, float opad, float width, StarSystemAPI system, 
1179                        List<MarketAPI> targets, String safeStr, String riskStr, String riskStrHighlight) {
1180                
1181                Color h = Misc.getHighlightColor();
1182                float raidStr  = getRoute().getExtra().getStrengthModifiedByDamage();
1183                float defenderStr = WarSimScript.getEnemyStrength(getFaction(), system, isPlayerTargeted());
1184                
1185                List<MarketAPI> safe = new ArrayList<MarketAPI>();
1186                List<MarketAPI> unsafe = new ArrayList<MarketAPI>();
1187                for (MarketAPI market : targets) {
1188                        float defensiveStr = defenderStr + WarSimScript.getStationStrength(market.getFaction(), system, market.getPrimaryEntity());
1189                        if (defensiveStr > raidStr * 1.25f) {
1190                                safe.add(market);
1191                        } else {
1192                                unsafe.add(market);
1193                        }
1194                }
1195                
1196                if (safe.size() == targets.size()) {
1197                        info.addPara("However, all colonies " + safeStr + ", " +
1198                                                 "owing to their orbital defenses.", opad);
1199                } else {
1200                        info.addPara("The following colonies " + riskStr, opad,
1201                                        Misc.getNegativeHighlightColor(), riskStrHighlight);
1202                        
1203                        FactionAPI f = Global.getSector().getPlayerFaction();
1204                        addMarketTable(info, f.getBaseUIColor(), f.getDarkUIColor(), f.getBrightUIColor(), unsafe, width, opad);
1205                }
1206        }
1207        
1208        /**
1209         * -1: fleet group is weaker
1210         * 0: evenly matched
1211         * 1: fleet group is stronger
1212         * @param target
1213         * @return
1214         */
1215        public int getRelativeFGStrength(StarSystemAPI target) {
1216                float raidStr  = getRoute().getExtra().getStrengthModifiedByDamage();
1217                float defenderStr = 0f;
1218                if (target != null) defenderStr = WarSimScript.getEnemyStrength(getFaction(), target, isPlayerTargeted());
1219                
1220                if (raidStr < defenderStr * 0.75f) {
1221                        return -1;
1222                } else if (raidStr < defenderStr * 1.25f) {
1223                        return 0;
1224                } else {
1225                        return 1;
1226                }
1227        }
1228        
1229        /**
1230         * Returns true if the defenses in the target system are weaker.
1231         * @return
1232         */
1233        protected boolean addStrengthDesc(TooltipMakerAPI info, float opad, StarSystemAPI system,
1234                                String forces, String outcomeFailure, String outcomeUncertain, String outcomeSuccess) {
1235                Color h = Misc.getHighlightColor();
1236                
1237                float raidStr  = getRoute().getExtra().getStrengthModifiedByDamage();
1238                float defenderStr = 0f;
1239                if (system != null) defenderStr = WarSimScript.getEnemyStrength(getFaction(), system, isPlayerTargeted());
1240                
1241                String strDesc = Misc.getStrengthDesc(raidStr);
1242                int numFleets = (int) getApproximateNumberOfFleets();
1243                String fleets = "fleets";
1244                if (numFleets == 1) fleets = "fleet";
1245                
1246                String defenderDesc = "";
1247                String defenderHighlight = "";
1248                Color defenderHighlightColor = h;
1249                
1250                boolean potentialDanger = false;
1251                String outcome = null;
1252                if (raidStr < defenderStr * 0.75f) {
1253                        defenderDesc = "The defending fleets are superior";
1254                        defenderHighlightColor = Misc.getPositiveHighlightColor();
1255                        defenderHighlight = "superior";
1256                        outcome = outcomeFailure;
1257                } else if (raidStr < defenderStr * 1.25f) {
1258                        defenderDesc = "The defending fleets are evenly matched";
1259                        defenderHighlightColor = h;
1260                        defenderHighlight = "evenly matched";
1261                        outcome = outcomeUncertain;
1262                        potentialDanger = true;
1263                } else {
1264                        defenderDesc = "The defending fleets are outmatched";
1265                        defenderHighlightColor = Misc.getNegativeHighlightColor();
1266                        defenderHighlight = "outmatched";
1267                        outcome = outcomeSuccess;
1268                        potentialDanger = true;
1269                }
1270                
1271                if (outcome != null) {
1272                        defenderDesc += ", and " + outcome + ".";
1273                } else {
1274                        defenderDesc += ".";
1275                }
1276                
1277                defenderDesc = " " + defenderDesc;
1278                
1279                if (system == null) defenderDesc = "";
1280                
1281                
1282                LabelAPI label = info.addPara("The " + forces + " are " +
1283                                                "projected to be %s and likely comprised of %s " + fleets + "." + defenderDesc,
1284                                opad, h, strDesc, "" + numFleets);
1285                label.setHighlight(strDesc, "" + numFleets, defenderHighlight);
1286                label.setHighlightColors(h, h, defenderHighlightColor);
1287                
1288                return potentialDanger;
1289        }
1290        
1291        
1292        /**
1293         * Returns true if the defenses in the target system are weaker.
1294         * @return
1295         */
1296        protected boolean addStrengthDesc(TooltipMakerAPI info, float opad, MarketAPI target,
1297                                String forces, String outcomeFailure, String outcomeUncertain, String outcomeSuccess) {
1298                Color h = Misc.getHighlightColor();
1299                
1300                float raidStr  = getRoute().getExtra().getStrengthModifiedByDamage();
1301                float defenderStr = 0f;
1302                StarSystemAPI system = target.getStarSystem();
1303                if (system != null) defenderStr = WarSimScript.getEnemyStrength(getFaction(), system, isPlayerTargeted());
1304                
1305                defenderStr += WarSimScript.getStationStrength(target.getFaction(), system, target.getPrimaryEntity());
1306                
1307                String strDesc = Misc.getStrengthDesc(raidStr);
1308                int numFleets = (int) getApproximateNumberOfFleets();
1309                String fleets = "fleets";
1310                if (numFleets == 1) fleets = "fleet";
1311                
1312                String defenderDesc = "";
1313                String defenderHighlight = "";
1314                Color defenderHighlightColor = h;
1315                
1316                boolean potentialDanger = false;
1317                String outcome = null;
1318                if (raidStr < defenderStr * 0.75f) {
1319                        defenderDesc = "The defending forces are superior";
1320                        defenderHighlightColor = Misc.getPositiveHighlightColor();
1321                        defenderHighlight = "superior";
1322                        outcome = outcomeFailure;
1323                } else if (raidStr < defenderStr * 1.25f) {
1324                        defenderDesc = "The defending forces are evenly matched";
1325                        defenderHighlightColor = h;
1326                        defenderHighlight = "evenly matched";
1327                        outcome = outcomeUncertain;
1328                        potentialDanger = true;
1329                } else {
1330                        defenderDesc = "The defending forces are outmatched";
1331                        defenderHighlightColor = Misc.getNegativeHighlightColor();
1332                        defenderHighlight = "outmatched";
1333                        outcome = outcomeSuccess;
1334                        potentialDanger = true;
1335                }
1336                
1337                if (outcome != null) {
1338                        defenderDesc += ", and " + outcome + ".";
1339                } else {
1340                        defenderDesc += ".";
1341                }
1342                
1343                defenderDesc = " " + defenderDesc;
1344                
1345                if (system == null) defenderDesc = "";
1346                
1347                
1348                LabelAPI label = info.addPara("The " + forces + " are " +
1349                                                "projected to be %s and likely comprised of %s " + fleets + "." + defenderDesc,
1350                                opad, h, strDesc, "" + numFleets);
1351                label.setHighlight(strDesc, "" + numFleets, defenderHighlight);
1352                label.setHighlightColors(h, h, defenderHighlightColor);
1353                
1354                return potentialDanger;
1355        }
1356        
1357
1358        
1359        public FGIEventListener getListener() {
1360                return listener;
1361        }
1362
1363        public void setListener(FGIEventListener listener) {
1364                this.listener = listener;
1365        }
1366        
1367        @Override
1368        public String getCommMessageSound() {
1369                if (isSendingUpdate()) {
1370                        return getSoundStandardUpdate();
1371                }
1372                return getSoundMajorPosting();
1373        }
1374        
1375        
1376}
1377
1378
1379
1380