001package com.fs.starfarer.api.impl.campaign.fleets;
002
003import java.util.List;
004import java.util.Random;
005
006import com.fs.starfarer.api.Script;
007import com.fs.starfarer.api.campaign.CampaignFleetAPI;
008import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI;
009import com.fs.starfarer.api.campaign.FleetActionTextProvider;
010import com.fs.starfarer.api.campaign.FleetAssignment;
011import com.fs.starfarer.api.campaign.LocationAPI;
012import com.fs.starfarer.api.campaign.SectorEntityToken;
013import com.fs.starfarer.api.campaign.StarSystemAPI;
014import com.fs.starfarer.api.campaign.ai.FleetAssignmentDataAPI;
015import com.fs.starfarer.api.campaign.econ.MarketAPI;
016import com.fs.starfarer.api.impl.campaign.econ.impl.MilitaryBase.PatrolFleetData;
017import com.fs.starfarer.api.impl.campaign.fleets.FleetFactory.PatrolType;
018import com.fs.starfarer.api.impl.campaign.fleets.RouteLocationCalculator.TaskInterval;
019import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
020import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment;
021import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
022import com.fs.starfarer.api.impl.campaign.ids.Tags;
023import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI;
024import com.fs.starfarer.api.util.CountingMap;
025import com.fs.starfarer.api.util.Misc;
026import com.fs.starfarer.api.util.WeightedRandomPicker;
027
028public class PatrolAssignmentAIV4 extends RouteFleetAssignmentAI implements FleetActionTextProvider {
029
030        public static final String PREP_STAGE = "a";
031        public static final String TRAVEL_TO_STAGE = "b";
032        public static final String PATROL_STAGE = "c";
033        public static final String RETURN_STAGE = "d";
034        public static final String STAND_DOWN_STAGE = "e";
035        
036        
037        public PatrolAssignmentAIV4(CampaignFleetAPI fleet, RouteData route) {
038                super(fleet, route);
039        }
040        
041
042        @Override
043        protected void giveInitialAssignments() {
044                //super.giveInitialAssignments();
045                
046                
047                SectorEntityToken target = pickEntityToGuard();
048                if (target == null) return;
049                
050                RouteSegment current = route.getCurrent();
051                SectorEntityToken source = route.getMarket().getPrimaryEntity();
052
053                TaskInterval [] intervals = new TaskInterval[] {
054                                TaskInterval.days(3f + (float) Math.random() * 3f),
055                                TaskInterval.travel(),
056                                TaskInterval.remaining(1f),
057                                TaskInterval.travel(),
058                                TaskInterval.days(3f + (float) Math.random() * 3f),
059                };
060                
061                RouteLocationCalculator.computeIntervalsAndSetLocation(fleet, current.elapsed, current.daysMax,
062                                                                                                                        false, intervals, 
063                                                                                                                        source, source, target, target, source, source);
064                
065                fleet.clearAssignments();
066                
067                // time to spend traveling to location and patrolling it
068                float combinedTravelAndPatrolTime = intervals[1].value + intervals[2].value;
069                
070                if (intervals[0].value > 0) {
071                        fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, source, intervals[0].value, PREP_STAGE);
072                }
073                if (intervals[1].value > 0) {
074                        fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, target, combinedTravelAndPatrolTime, TRAVEL_TO_STAGE, 
075                                                                true, null, null);
076                        combinedTravelAndPatrolTime = 0f;
077                }
078                if (intervals[2].value > 0) {
079                        fleet.addAssignment(FleetAssignment.PATROL_SYSTEM, target, combinedTravelAndPatrolTime, PATROL_STAGE,
080                                        false,
081                                target.isSystemCenter() ? new Script() {
082                                        public void run() {
083                                                fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_ALLOW_LONG_PURSUIT, true);
084                                        }} : null,
085                                target.isSystemCenter() ? new Script() {
086                                        public void run() {
087                                                fleet.getMemoryWithoutUpdate().unset(MemFlags.MEMORY_KEY_ALLOW_LONG_PURSUIT);
088                                        }} : null
089                        );
090                }
091                
092                
093                if (intervals[3].value > 0) { // return for as long as it takes, not just the interval value
094                        fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, source, 1000f, RETURN_STAGE);
095                }
096                
097                // if there was no return stage, that means we spawned right in orbit, since
098                // here always justSpawned == true 
099                fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, source, intervals[4].value, STAND_DOWN_STAGE);
100                fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, source, 1000f,
101                                                        STAND_DOWN_STAGE, goNextScript(current));
102                
103                fleet.getAI().setActionTextProvider(this);
104                
105        }
106
107        public String getActionText(CampaignFleetAPI fleet) {
108//              if (Misc.getDistance(Global.getSector().getPlayerFleet(), fleet) < fleet.getRadius()) {
109//                      System.out.println("ewfwefwe");
110//              }
111                FleetAssignmentDataAPI curr = fleet.getCurrentAssignment();
112                if (curr == null) return null;
113                
114                String stage = curr.getActionText();
115                SectorEntityToken target = curr.getTarget();
116                
117                String name = "";
118                if (target != null) {
119                        name = target.getName();
120                        if (target instanceof CustomCampaignEntityAPI) {
121                                CustomCampaignEntityAPI cce = (CustomCampaignEntityAPI) target;
122                                if (name.equals(cce.getCustomEntitySpec().getDefaultName())) {
123                                        //name = name.toLowerCase();
124                                        name = cce.getCustomEntitySpec().getNameInText();
125                                }
126                        }
127                }
128                
129                boolean pirate = fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_PIRATE);
130                
131                if (pirate) {
132                        if (PREP_STAGE.equals(stage)) {
133                                return "preparing for patrol duty";
134                        } else if (TRAVEL_TO_STAGE.equals(stage) && target != null && !target.isSystemCenter() && !target.isInHyperspace()) {
135                                return "traveling to " + name;
136                        } else if (TRAVEL_TO_STAGE.equals(stage)) {
137                                return "traveling";
138                        } else if (PATROL_STAGE.equals(stage) && target != null) {
139                                if (target.hasTag(Tags.OBJECTIVE)) {
140                                        return "guarding " + name;
141                                } else if (target.hasTag(Tags.JUMP_POINT)) {
142                                        return "guarding " + name;
143                                } else if (target.getMarket() != null) {
144                                        return "defending " + name;
145                                } else {
146                                        return "patrolling";
147                                }
148                        } else if (RETURN_STAGE.equals(stage) && target != null && !target.isSystemCenter()) {
149                                return "returning to " + name;
150                        } else if (STAND_DOWN_STAGE.equals(stage)) {
151                                return "standing down";
152                        }
153                } else {
154                        if (PREP_STAGE.equals(stage)) {
155                                return "preparing for patrol duty";
156                        } else if (TRAVEL_TO_STAGE.equals(stage) && target != null && !target.isSystemCenter() && !target.isInHyperspace()) {
157                                return "traveling to " + name;
158                        } else if (TRAVEL_TO_STAGE.equals(stage)) {
159                                return "traveling";
160                        } else if (PATROL_STAGE.equals(stage) && target != null) {
161                                if (target.hasTag(Tags.OBJECTIVE)) {
162                                        return "guarding " + name;
163                                } else if (target.hasTag(Tags.JUMP_POINT)) {
164                                        return "guarding " + name;
165                                } else if (target.getMarket() != null) {
166                                        return "patrolling around " + name;
167                                } else {
168                                        return "patrolling";
169                                }
170                        } else if (RETURN_STAGE.equals(stage) && target != null && !target.isSystemCenter()) {
171                                return "returning to " + name;
172                        } else if (STAND_DOWN_STAGE.equals(stage)) {
173                                return "standing down from patrol duty";
174                        }
175                }
176                
177                //"traveling to " + target.getName()
178                //return "patrolling";
179                return null;
180        }
181
182        @Override
183        public void advance(float amount) {
184//              if (fleet.isInCurrentLocation() && 
185//                              Misc.getDistance(Global.getSector().getPlayerFleet(), fleet) < fleet.getRadius()) {
186//                      System.out.println("ewfwefwe");
187//              }
188                super.advance(amount);
189                
190                checkCapture(amount);
191                checkBuild(amount);
192        }
193        
194        
195        public SectorEntityToken pickEntityToGuard() {
196                Random random = route.getRandom(1);
197                
198                PatrolFleetData custom = (PatrolFleetData) route.getCustom();
199                PatrolType type = custom.type;
200                
201                LocationAPI loc = fleet.getContainingLocation();
202                if (loc == null) return null;
203                
204                WeightedRandomPicker<SectorEntityToken> picker = new WeightedRandomPicker<SectorEntityToken>(random);
205                
206                CountingMap<SectorEntityToken> existing = new CountingMap<SectorEntityToken>();
207                for (RouteData data : RouteManager.getInstance().getRoutesForSource(route.getSource())) {
208                        CampaignFleetAPI other = data.getActiveFleet();
209                        if (other == null) continue;
210                        FleetAssignmentDataAPI curr = other.getCurrentAssignment();
211                        if (curr == null || curr.getTarget() == null || 
212                                        curr.getAssignment() != FleetAssignment.PATROL_SYSTEM) {
213                                continue;
214                        }
215                        existing.add(curr.getTarget());
216                }
217
218                List<MarketAPI> markets = Misc.getMarketsInLocation(fleet.getContainingLocation());
219                int hostileMax = 0;
220                int ourMax = 0;
221                for (MarketAPI market : markets) {
222                        if (market.getFaction().isHostileTo(fleet.getFaction())) {
223                                hostileMax = Math.max(hostileMax, market.getSize());
224                        } else if (market.getFaction() == fleet.getFaction()) {
225                                ourMax = Math.max(ourMax, market.getSize());
226                        }
227                }
228                boolean inControl = ourMax > hostileMax;
229                
230                for (SectorEntityToken entity : loc.getEntitiesWithTag(Tags.OBJECTIVE)) {
231                        if (entity.getFaction() != fleet.getFaction()) continue;
232                        
233                        float w = 2f;
234                        for (int i = 0; i < existing.getCount(entity); i++) w *= 0.1f;
235
236                        if (type == PatrolType.HEAVY) w *= 0.1f;
237                        
238                        picker.add(entity, w);
239                }
240                
241                // patrol stable locations, will build there
242                for (SectorEntityToken entity : loc.getEntitiesWithTag(Tags.STABLE_LOCATION)) {
243                        float w = 2f;
244                        for (int i = 0; i < existing.getCount(entity); i++) w *= 0.1f;
245
246                        if (type == PatrolType.HEAVY) w *= 0.1f;
247                        
248                        picker.add(entity, w);
249                }
250                
251                if (inControl) {
252                        for (SectorEntityToken entity : loc.getJumpPoints()) {
253                                float w = 2f;
254                                for (int i = 0; i < existing.getCount(entity); i++) w *= 0.1f;
255                                
256                                if (type == PatrolType.HEAVY) w *= 0.1f;
257                                
258                                picker.add(entity, w);
259                        }
260                        
261                        if (loc instanceof StarSystemAPI && custom.type == PatrolType.HEAVY) {
262                                StarSystemAPI system = (StarSystemAPI) loc;
263                                if (system.getHyperspaceAnchor() != null) {
264                                        float w = 3f;
265                                        for (int i = 0; i < existing.getCount(system.getHyperspaceAnchor()); i++) w *= 0.1f;
266                                        picker.add(system.getHyperspaceAnchor(), w);
267                                }
268                        }
269                }
270                
271                for (MarketAPI market : markets) {
272                        if (market.getFaction().isHostileTo(fleet.getFaction())) continue;
273                        
274                        float w = 0f;
275                        if (market == route.getMarket()) {
276                                w = 5f;
277                        } else {
278                                // defend on-hostile non-military markets; prefer own faction
279                                //if (!market.hasSubmarket(Submarkets.GENERIC_MILITARY)) {
280                                if (market.getMemoryWithoutUpdate().getBoolean(MemFlags.MARKET_PATROL)) {
281                                        if (market.getFaction() != fleet.getFaction()) {
282                                                w = 0f; // don't patrol near patrolHQ/military markets of another faction
283                                        } else {
284                                                w = 4f;
285                                        }
286                                }
287                        }
288                        
289                        for (int i = 0; i < existing.getCount(market.getPrimaryEntity()); i++) w *= 0.1f;
290                        picker.add(market.getPrimaryEntity(), w);
291                }
292                
293                if (fleet.getContainingLocation() instanceof StarSystemAPI && type != PatrolType.HEAVY) {
294                        StarSystemAPI system = (StarSystemAPI) fleet.getContainingLocation();
295                        float w = 1f;
296                        for (int i = 0; i < existing.getCount(system.getCenter()); i++) w *= 0.1f;
297                        picker.add(system.getCenter(), w);
298                }
299                
300                SectorEntityToken target = picker.pick();
301                if (target == null) {
302                        target = route.market.getPrimaryEntity();
303                }
304                
305                return target;
306        }
307
308
309        
310}
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325