001package com.fs.starfarer.api.impl.campaign.procgen.themes;
002
003import org.lwjgl.util.vector.Vector2f;
004
005import com.fs.starfarer.api.EveryFrameScript;
006import com.fs.starfarer.api.Global;
007import com.fs.starfarer.api.Script;
008import com.fs.starfarer.api.campaign.CampaignFleetAPI;
009import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI;
010import com.fs.starfarer.api.campaign.CustomEntitySpecAPI;
011import com.fs.starfarer.api.campaign.FleetAssignment;
012import com.fs.starfarer.api.campaign.SectorEntityToken;
013import com.fs.starfarer.api.campaign.SectorEntityToken.VisibilityLevel;
014import com.fs.starfarer.api.campaign.ai.FleetAIFlags;
015import com.fs.starfarer.api.campaign.ai.FleetAssignmentDataAPI;
016import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI;
017import com.fs.starfarer.api.campaign.econ.MarketAPI;
018import com.fs.starfarer.api.campaign.rules.MemoryAPI;
019import com.fs.starfarer.api.impl.campaign.fleets.RouteManager;
020import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
021import com.fs.starfarer.api.impl.campaign.ids.Tags;
022import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD;
023import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.Objectives;
024import com.fs.starfarer.api.plugins.BuildObjectiveTypePicker;
025import com.fs.starfarer.api.plugins.BuildObjectiveTypePicker.BuildObjectiveParams;
026import com.fs.starfarer.api.util.IntervalUtil;
027import com.fs.starfarer.api.util.Misc;
028
029public abstract class BaseAssignmentAI implements EveryFrameScript {
030
031        public interface FleetActionDelegate {
032                boolean canRaid(CampaignFleetAPI fleet, MarketAPI market);
033                String getRaidApproachText(CampaignFleetAPI fleet, MarketAPI market);
034                String getRaidActionText(CampaignFleetAPI fleet, MarketAPI market);
035                void performRaid(CampaignFleetAPI fleet, MarketAPI market);
036                
037                String getRaidPrepText(CampaignFleetAPI fleet, SectorEntityToken from);
038                String getRaidInSystemText(CampaignFleetAPI fleet);
039                String getRaidDefaultText(CampaignFleetAPI fleet);
040        }
041        
042        protected CampaignFleetAPI fleet;
043        protected Boolean done = null;
044        protected Boolean giveInitial = true;
045        protected FleetActionDelegate delegate = null;
046        
047        public BaseAssignmentAI() {
048        }
049
050        public BaseAssignmentAI(CampaignFleetAPI fleet) {
051                this.fleet = fleet;
052                giveInitialAssignments();
053        }
054        
055        public FleetActionDelegate getDelegate() {
056                return delegate;
057        }
058
059        public void setDelegate(FleetActionDelegate delegate) {
060                this.delegate = delegate;
061        }
062
063        protected abstract void giveInitialAssignments();
064        protected abstract void pickNext();
065
066        public void advance(float amount) {
067                if (fleet.getCurrentAssignment() == null) {
068                        pickNext();
069                }
070        }
071
072        
073        public boolean isDone() {
074                return done != null;
075        }
076        
077        public void setDone() {
078                done = true;
079        }
080
081        public boolean runWhilePaused() {
082                return false;
083        }
084
085
086
087        protected IntervalUtil raidTracker;
088        protected IntervalUtil capTracker;
089        protected IntervalUtil buildTracker;
090        protected void checkRaid(float amount) {
091                if (fleet.isInHyperspace()) return;
092                
093                if (raidTracker == null) {
094                        raidTracker = new IntervalUtil(0.3f, 0.7f);
095                }
096                raidTracker.advance(Misc.getDays(amount));
097                if (!raidTracker.intervalElapsed()) return;
098                
099                checkColonyAction();
100        }
101        
102        protected void checkCapture(float amount) {
103                if (fleet.isInHyperspace()) return;
104                
105                if (capTracker == null) {
106                         capTracker = new IntervalUtil(0.3f, 0.7f);
107                }
108                capTracker.advance(Misc.getDays(amount));
109                if (!capTracker.intervalElapsed()) return;
110                
111                checkObjectiveAction(false);
112        }
113        
114        protected void checkBuild(float amount) {
115                if (fleet.isInHyperspace()) return;
116                
117                if (buildTracker == null) {
118                        buildTracker = new IntervalUtil(0.3f, 0.7f);
119                }
120                buildTracker.advance(Misc.getDays(amount));
121                if (!buildTracker.intervalElapsed()) return;
122                
123                checkObjectiveAction(true);
124        }
125        
126        protected void checkObjectiveAction(boolean build) {
127//              if (!fleet.isInCurrentLocation() && fleet.getName().contains("Tactistar")) {
128//                      System.out.println("efwfe2534324");
129//              }
130                
131                if (!canTakeAction()) return;
132                
133                
134//              if (fleet.isInCurrentLocation()) {
135//                      System.out.println("fwewefe");
136//              }
137                SectorEntityToken closest = null;
138                float minDist = Float.MAX_VALUE;
139                
140                if (build) {
141                        for (SectorEntityToken objective : fleet.getContainingLocation().getEntitiesWithTag(Tags.STABLE_LOCATION)) {
142                                if (objective.hasTag(Tags.NON_CLICKABLE)) continue;
143                                if (objective.hasTag(Tags.FADING_OUT_AND_EXPIRING)) continue;
144                                
145                                // so that it's not continuously rebuilt if it keeps being salvaged by the player
146                                if (objective.getMemoryWithoutUpdate().getBoolean(MemFlags.RECENTLY_SALVAGED)) continue;
147                                
148                                float dist = Misc.getDistance(fleet, objective);
149                                if (dist < minDist) {
150                                        closest = objective;
151                                        minDist = dist;
152                                }
153                        }
154                } else {
155                        for (SectorEntityToken objective : fleet.getContainingLocation().getEntitiesWithTag(Tags.OBJECTIVE)) {
156                                if (objective.getFaction() == fleet.getFaction()) continue;
157                                if (!objective.getFaction().isHostileTo(fleet.getFaction()) && 
158                                                !Misc.isFleetMadeHostileToFaction(fleet, objective.getFaction())) {
159                                        boolean ownerHasColonyInSystem = false;
160                                        for (MarketAPI curr : Misc.getMarketsInLocation(objective.getContainingLocation())) {
161                                                if (curr.getFaction() == objective.getFaction() && 
162                                                                !curr.getFaction().isNeutralFaction()) {
163                                                        ownerHasColonyInSystem = true;
164                                                        break;
165                                                }
166                                        }
167                                        if (ownerHasColonyInSystem) continue;
168                                }
169                                
170                                float dist = Misc.getDistance(fleet, objective);
171                                if (dist < minDist) {
172                                        closest = objective;
173                                        minDist = dist;
174                                }
175                        }
176                }
177                
178                if (closest == null || minDist > 2000f) return;
179                
180                for (CampaignFleetAPI other : Misc.getNearbyFleets(closest, 2000f)) {
181                        if (other == fleet) continue;
182                        
183                        if (other.isHostileTo(fleet)) {
184                                if (other.getFleetPoints() > fleet.getFleetPoints() * 0.25f) return;
185                        }
186                        
187                        if (other.getFaction() == fleet.getFaction()) {
188                                float dist = Misc.getDistance(other, closest);
189                                if (dist < minDist) return;
190                        }
191                }
192                
193                if (build) {
194                        BuildObjectiveParams params = new BuildObjectiveParams();
195                        params.faction = fleet.getFaction();
196                        params.fleet = fleet;
197                        params.stableLoc = closest;
198                        BuildObjectiveTypePicker pick = Global.getSector().getGenericPlugins().pickPlugin(BuildObjectiveTypePicker.class, params);
199                        String type = null;
200                        if (pick != null) {
201                                type = pick.pickObjectiveToBuild(params);
202                        }
203                        if (type != null) {
204                                giveBuildOrder(closest, type);
205                        }
206                } else {
207                        giveCaptureOrder(closest);
208                }
209        }
210        
211        
212        protected void giveCaptureOrder(final SectorEntityToken target) {
213                clearTempAssignments(fleet);
214                
215//              if (!fleet.isInCurrentLocation() && fleet.getName().contains("Tactistar")) {
216//                      System.out.println("efwfe2534324");
217//              }
218                
219                Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), 
220                                                                MemFlags.FLEET_BUSY, TEMP_BUSY_REASON, true, (1.5f + (float) Math.random()));
221                
222                String name = ((CustomCampaignEntityAPI)target).getCustomEntitySpec().getNameInText();
223                String capText = "taking control of " + name;
224                String moveText = "moving to take control of " + name;
225                
226                Vector2f loc = Misc.getUnitVectorAtDegreeAngle(
227                                                        Misc.getAngleInDegrees(target.getLocation(), fleet.getLocation()));
228                float holdRadius = fleet.getRadius() + target.getRadius() - 10;
229                loc.scale(holdRadius);
230                Vector2f.add(loc, target.getLocation(), loc);
231                SectorEntityToken holdLoc = target.getContainingLocation().createToken(loc);
232                
233                holdLoc.setCircularOrbit(target,
234                                   Misc.getAngleInDegrees(target.getLocation(), fleet.getLocation()),
235                                        holdRadius, 1000000f);
236                fleet.getContainingLocation().addEntity(holdLoc);
237                Misc.fadeAndExpire(holdLoc, 5f);
238                
239                fleet.addAssignmentAtStart(FleetAssignment.HOLD, holdLoc, 0.5f, capText, new Script() {
240                        public void run() {
241                                if (target.isAlive()) {
242                                        Objectives o = new Objectives(target);
243                                        o.control(fleet.getFaction().getId());
244                                }
245                                clearTempAssignments(fleet);
246                        }
247                });
248                FleetAssignmentDataAPI curr = fleet.getCurrentAssignment();
249                if (curr != null) {
250                        curr.setCustom(TEMP_ASSIGNMENT);
251                }
252                
253                float dist = Misc.getDistance(target, fleet);
254                if (dist > fleet.getRadius() + target.getRadius() + 300f) {
255                        
256                        fleet.addAssignmentAtStart(FleetAssignment.DELIVER_CREW, target, 3f, moveText, null);
257                        curr = fleet.getCurrentAssignment();
258                        if (curr != null) {
259                                curr.setCustom(TEMP_ASSIGNMENT);
260                        }
261                }
262        }
263        
264        
265        protected void giveBuildOrder(final SectorEntityToken target, String type) {
266                clearTempAssignments(fleet);
267                
268                CustomEntitySpecAPI spec = Global.getSettings().getCustomEntitySpec(type);
269                
270                Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), 
271                                                                MemFlags.FLEET_BUSY, TEMP_BUSY_REASON, true, (1.5f + (float) Math.random()));
272                
273                String name = spec.getNameInText();
274                String capText = "constructing " + name;
275                String moveText = "moving to construct " + name;
276                
277                Vector2f loc = Misc.getUnitVectorAtDegreeAngle(
278                                                        Misc.getAngleInDegrees(target.getLocation(), fleet.getLocation()));
279                float holdRadius = fleet.getRadius() + target.getRadius();
280                loc.scale(holdRadius);
281                Vector2f.add(loc, target.getLocation(), loc);
282                SectorEntityToken holdLoc = target.getContainingLocation().createToken(loc); 
283                
284                holdLoc.setCircularOrbit(target,
285                                   Misc.getAngleInDegrees(target.getLocation(), fleet.getLocation()),
286                                        holdRadius, 1000000f);
287                fleet.getContainingLocation().addEntity(holdLoc);
288                Misc.fadeAndExpire(holdLoc, 5f);
289                
290                fleet.addAssignmentAtStart(FleetAssignment.HOLD, holdLoc, 0.5f, capText, new Script() {
291                        public void run() {
292                                if (target.isAlive()) {
293                                        // re-figure-out the type to avoid duplicates
294                                        BuildObjectiveParams params = new BuildObjectiveParams();
295                                        params.faction = fleet.getFaction();
296                                        params.fleet = fleet;
297                                        params.stableLoc = target;
298                                        BuildObjectiveTypePicker pick = Global.getSector().getGenericPlugins().pickPlugin(BuildObjectiveTypePicker.class, params);
299                                        String type = null;
300                                        if (pick != null) {
301                                                type = pick.pickObjectiveToBuild(params);
302                                        }
303                                        
304                                        Objectives o = new Objectives(target);
305                                        o.build(type, fleet.getFaction().getId());
306                                }
307                                clearTempAssignments(fleet);
308                        }
309                });
310                FleetAssignmentDataAPI curr = fleet.getCurrentAssignment();
311                if (curr != null) {
312                        curr.setCustom(TEMP_ASSIGNMENT);
313                }
314                
315                float dist = Misc.getDistance(target, fleet);
316                if (dist > fleet.getRadius() + target.getRadius() + 300f) {
317                        
318                        fleet.addAssignmentAtStart(FleetAssignment.DELIVER_CREW, target, 3f, moveText, null);
319                        curr = fleet.getCurrentAssignment();
320                        if (curr != null) {
321                                curr.setCustom(TEMP_ASSIGNMENT);
322                        }
323                }
324
325        }
326        
327        
328        
329        public static String TEMP_ASSIGNMENT = "temp_PAV4";
330        public static String TEMP_BUSY_REASON = "temp_PAV4";
331        protected void clearTempAssignments(CampaignFleetAPI fleet) {
332                Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), 
333                                                           MemFlags.FLEET_BUSY, TEMP_BUSY_REASON, false, 0f);
334                for (FleetAssignmentDataAPI curr : fleet.getAI().getAssignmentsCopy()) {
335                        if (TEMP_ASSIGNMENT.equals(curr.getCustom())) {
336                                fleet.getAI().removeAssignment(curr);
337                        }
338                }
339        }
340        
341        
342        
343        
344        
345        
346        
347        protected boolean canTakeAction() {
348                if (!RouteManager.isPlayerInSpawnRange(fleet)) return false;
349                
350                if (fleet.getBattle() != null) return false;
351                if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.FLEET_BUSY)) {
352                        return false;
353                }
354                
355                if (fleet.isCurrentAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN)) return false;
356                
357                MemoryAPI mem = fleet.getMemoryWithoutUpdate();
358                if (Misc.flagHasReason(mem, MemFlags.FLEET_BUSY, TEMP_BUSY_REASON)) return false;
359                        
360                
361                if (fleet.getAI() instanceof ModularFleetAIAPI) {
362                        ModularFleetAIAPI ai = (ModularFleetAIAPI) fleet.getAI();
363                        if (ai.getAssignmentModule().areAssignmentsFrozen()) return false;
364                }
365                
366                CampaignFleetAPI pursueTarget = mem.getFleet(FleetAIFlags.PURSUIT_TARGET);
367                CampaignFleetAPI fleeingFrom = mem.getFleet(FleetAIFlags.NEAREST_FLEEING_FROM);
368                
369                if (pursueTarget != null || fleeingFrom != null) {
370                        return false;
371                }
372                return true;
373        }
374        
375        
376        
377
378        protected void checkColonyAction() {
379                if (!canTakeAction()) return;
380                
381                if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.RECENTLY_PERFORMED_RAID)) {
382                        return;
383                }
384                
385                MarketAPI closest = null;
386                float minDist = Float.MAX_VALUE;
387                
388//              if (fleet.getFaction().getId().equals(Factions.HEGEMONY)) {
389//                      System.out.println("wefwefwe");
390//              }
391                
392                for (MarketAPI market : Misc.getMarketsInLocation(fleet.getContainingLocation())) {
393                        if (delegate != null) {
394                                if (!delegate.canRaid(fleet, market)) continue;
395                        } else {
396                                if (!market.getFaction().isHostileTo(fleet.getFaction()) && 
397                                                !Misc.isFleetMadeHostileToFaction(fleet, market.getFaction())) continue;
398                        }
399                        
400                        float dist = Misc.getDistance(fleet, market.getPrimaryEntity());
401                        if (dist < minDist) {
402                                closest = market;
403                                minDist = dist;
404                        }
405                }
406                
407                if (closest == null || minDist > 2000f) return;
408                
409                for (CampaignFleetAPI other : Misc.getNearbyFleets(closest.getPrimaryEntity(), 2000f)) {
410                        if (other == fleet) continue;
411                        
412                        if (other.isHostileTo(fleet)) {
413                                VisibilityLevel vis = other.getVisibilityLevelTo(fleet);
414                                boolean canSee = vis == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS || vis == VisibilityLevel.COMPOSITION_DETAILS;
415                                if (!canSee && other.getFaction() != fleet.getFaction()) continue;
416                                
417                                if (other.getAI() instanceof ModularFleetAIAPI) {
418                                        ModularFleetAIAPI ai = (ModularFleetAIAPI) other.getAI();
419                                        if (ai.isFleeing()) continue;
420                                        if (ai.isMaintainingContact()) continue;
421                                        
422                                        if (ai.getTacticalModule().getTarget() == fleet) return;
423                                        
424                                        MemoryAPI mem = other.getMemoryWithoutUpdate();
425                                        boolean smuggler = mem.getBoolean(MemFlags.MEMORY_KEY_SMUGGLER);
426                                        boolean trader = mem.getBoolean(MemFlags.MEMORY_KEY_TRADE_FLEET);
427                                        if (smuggler || trader) continue;
428                                }
429                                if (other.getFleetPoints() > fleet.getFleetPoints() * 0.25f || other.isStationMode()) return;
430                        }
431                        
432                        if (other.getFaction() == fleet.getFaction()) {
433                                if (other.isStationMode()) continue;
434                                
435                                boolean otherFromSameRaid = delegate != null && delegate.canRaid(other, closest);
436                                if (!(Misc.isRaider(other) && !Misc.isWarFleet(other) && !otherFromSameRaid)) continue;
437                                
438                                if (other.getFleetPoints() > fleet.getFleetPoints()) return;
439                                if (other.getFleetPoints() == fleet.getFleetPoints()) {
440                                        float dist = Misc.getDistance(other, closest.getPrimaryEntity());
441                                        if (dist < minDist) return;
442                                }
443                        }
444                }
445                
446                giveRaidOrder(closest);
447        }
448        
449        
450        protected void giveRaidOrder(final MarketAPI target) {
451                clearTempAssignments(fleet);
452                
453                Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), 
454                                                                MemFlags.FLEET_BUSY, TEMP_BUSY_REASON, true, (1.5f + (float) Math.random()));
455                
456                String name = target.getName();
457                String capText = "raiding " + name;
458                String moveText = "moving to raid " + name;
459                if (delegate != null) {
460                        String s = delegate.getRaidApproachText(fleet, target);
461                        if (s != null) moveText = s;
462                        
463                        s = delegate.getRaidActionText(fleet, target);
464                        if (s != null) capText = s;
465                }
466                
467                Vector2f loc = Misc.getUnitVectorAtDegreeAngle(
468                                                        Misc.getAngleInDegrees(target.getPrimaryEntity().getLocation(), fleet.getLocation()));
469                float holdRadius = fleet.getRadius() * 0.5f + target.getPrimaryEntity().getRadius();
470                loc.scale(holdRadius);
471                Vector2f.add(loc, target.getPrimaryEntity().getLocation(), loc);
472                SectorEntityToken holdLoc = target.getContainingLocation().createToken(loc);
473                holdLoc.setCircularOrbit(target.getPrimaryEntity(),
474                                                   Misc.getAngleInDegrees(target.getPrimaryEntity().getLocation(), fleet.getLocation()),
475                                                        holdRadius, 1000000f);
476                fleet.getContainingLocation().addEntity(holdLoc);
477                Misc.fadeAndExpire(holdLoc, 5f);
478                
479                final int fpAtStart = fleet.getFleetPoints();
480                //holdLoc = Global.getSector().getPlayerFleet();
481                fleet.addAssignmentAtStart(FleetAssignment.HOLD, holdLoc, 0.5f, capText, new Script() {
482                        public void run() {
483                                if (fpAtStart == fleet.getFleetPoints()) {
484                                        boolean raided = false;
485                                        if (delegate != null) {
486                                                if (delegate.canRaid(fleet, target)) {
487                                                        delegate.performRaid(fleet, target);
488                                                        raided = true;
489                                                }
490                                        } else {
491                                                new MarketCMD(target.getPrimaryEntity()).doGenericRaid(fleet.getFaction(),
492                                                                                                                                                           MarketCMD.getRaidStr(fleet));
493                                                raided = true;
494                                        }
495                                        
496                                        if (raided) {
497                                                fleet.getMemoryWithoutUpdate().set(MemFlags.RECENTLY_PERFORMED_RAID, true, 3f);
498                                        }
499                                        clearTempAssignments(fleet);
500                                }
501                        }
502                });
503                FleetAssignmentDataAPI curr = fleet.getCurrentAssignment();
504                if (curr != null) {
505                        curr.setCustom(TEMP_ASSIGNMENT);
506                }
507                
508                float dist = Misc.getDistance(target.getPrimaryEntity(), fleet);
509                //if (dist > fleet.getRadius() + target.getPrimaryEntity().getRadius() + 300f) {
510                if (dist > fleet.getRadius() + target.getPrimaryEntity().getRadius()) {
511                        fleet.addAssignmentAtStart(FleetAssignment.DELIVER_CREW, holdLoc, 3f, moveText, null);
512                        curr = fleet.getCurrentAssignment();
513                        if (curr != null) {
514                                curr.setCustom(TEMP_ASSIGNMENT);
515                        }
516                }
517        }
518        
519
520        
521        
522}
523
524
525
526
527
528
529
530
531
532