001package com.fs.starfarer.api.impl.campaign.fleets;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import org.lwjgl.util.vector.Vector2f;
007
008import com.fs.starfarer.api.EveryFrameScript;
009import com.fs.starfarer.api.Global;
010import com.fs.starfarer.api.campaign.CampaignFleetAPI;
011import com.fs.starfarer.api.campaign.FleetAssignment;
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.fleets.PatrolFleetManager.PatrolFleetData;
017import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
018import com.fs.starfarer.api.util.IntervalUtil;
019import com.fs.starfarer.api.util.Misc;
020import com.fs.starfarer.api.util.WeightedRandomPicker;
021import com.fs.starfarer.api.util.Misc.FleetFilter;
022
023public class PatrolAssignmentAI implements EveryFrameScript {
024
025        private CampaignFleetAPI fleet;
026        private PatrolFleetData data;
027        
028        private IntervalUtil tracker = new IntervalUtil(0.5f, 1.5f);
029
030        public PatrolAssignmentAI(CampaignFleetAPI fleet, PatrolFleetData data) {
031                this.fleet = fleet;
032                this.data = data;
033                giveInitialAssignment();
034        }
035
036        private void giveInitialAssignment() {
037        
038                float daysToOrbit = getDaysToOrbit() * 0.25f;
039                if (daysToOrbit < 0.2f) {
040                        daysToOrbit = 0.2f;
041                }
042                //fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, data.sourceMarket.getPrimaryEntity(), daysToOrbit,
043                fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, data.sourceMarket.getPrimaryEntity(), daysToOrbit,
044                                "preparing for patrol duty");
045        }
046
047        private boolean orderedReturn = false;
048        public void advance(float amount) {
049                float days = Global.getSector().getClock().convertToDays(amount);
050                
051//              if (!orderedReturn) {
052//                      tracker.advance(days);
053//                      if (tracker.intervalElapsed()) {
054//                              checkNPCCustomsInspection();
055//                      }
056//                      performInspectionIfNeeded();
057//              }
058                
059                
060                
061//              if (fleet.getFaction().getId().equals("knights_of_ludd")) {
062//                      System.out.println("wesdfsd");
063//              }
064                if (fleet.getAI().getCurrentAssignment() != null) {
065                        float fp = fleet.getFleetPoints();
066                        if (fp < data.startingFleetPoints && !orderedReturn) {
067                                orderedReturn = true;
068                                fleet.clearAssignments();
069                                
070                                fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, data.sourceMarket.getPrimaryEntity(), 1000,
071                                                                        "returning to " + data.sourceMarket.getName());
072                                fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, data.sourceMarket.getPrimaryEntity(), 1f,
073                                                                        "standing down from patrol duty");
074                                fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, data.sourceMarket.getPrimaryEntity(), 1000);
075                        }
076                } else {
077//                      if (fleet.getFaction().getId().equals("knights_of_ludd")) {
078//                              System.out.println("wesdfsd");
079//                      }
080                        float daysToOrbit = getDaysToOrbit();
081                        StarSystemAPI system = data.sourceMarket.getStarSystem();
082                        if (system == null) {
083                                fleet.addAssignment(FleetAssignment.DEFEND_LOCATION, data.sourceMarket.getPrimaryEntity(), 20,
084                                                                        "patrolling around " + data.sourceMarket.getName());
085                                fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, data.sourceMarket.getPrimaryEntity(), 1000,
086                                                                        "returning to " + data.sourceMarket.getName());
087                                fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, data.sourceMarket.getPrimaryEntity(), daysToOrbit,
088                                                                        "standing down from patrol duty");
089                        } else {
090                                if ((float) Math.random() > 0.95f) {
091                                        fleet.addAssignment(FleetAssignment.PATROL_SYSTEM, system.getHyperspaceAnchor(), 20,
092                                                                                "patrolling around the " + system.getBaseName() + " star system");
093                                        fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, data.sourceMarket.getPrimaryEntity(), 1000,
094                                                                                "returning to " + data.sourceMarket.getName());
095                                        fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, data.sourceMarket.getPrimaryEntity(), daysToOrbit,
096                                                                                "standing down from patrol duty");
097                                } else {
098                                        WeightedRandomPicker<SectorEntityToken> defenseTargets = new WeightedRandomPicker<SectorEntityToken>();
099                                        SectorEntityToken generalPatrol = data.sourceMarket.getPrimaryEntity().getContainingLocation().createToken(0, 0);
100                                        
101//                                      for (SectorEntityToken relay : system.getEntitiesWithTag(Tags.COMM_RELAY)) {
102//                                              float weight = 10;
103//                                              if (data.type == PatrolType.FAST) weight = 40;
104//                                              defenseTargets.add(relay, weight);
105//                                      }
106//                                      defenseTargets.add(data.sourceMarket.getPrimaryEntity(), 30);
107                                        defenseTargets.add(generalPatrol, 30);
108                                        
109                                        SectorEntityToken pick = defenseTargets.pick();
110                                        
111                                        if (pick == generalPatrol) {
112                                                fleet.addAssignment(FleetAssignment.PATROL_SYSTEM, system.getStar(), 30,
113                                                                "patrolling the " + system.getBaseName() + " star system");
114                                                fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, data.sourceMarket.getPrimaryEntity(), 1000,
115                                                                "returning to " + data.sourceMarket.getName());
116                                                fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, data.sourceMarket.getPrimaryEntity(), daysToOrbit,
117                                                                "standing down from patrol duty");
118                                        } else {
119                                                fleet.addAssignment(FleetAssignment.DEFEND_LOCATION, pick, 30,
120                                                                "patrolling around " + pick.getName());
121                                                fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, data.sourceMarket.getPrimaryEntity(), 1000,
122                                                                "returning to " + data.sourceMarket.getName());
123                                                fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, data.sourceMarket.getPrimaryEntity(), daysToOrbit,
124                                                                "standing down from patrol duty");
125                                        }
126                                }
127                        }
128                }
129        }
130
131        
132        private void performInspectionIfNeeded() {
133                if (inspectionTarget != null) {
134//                      if (fleet.isInCurrentLocation()) {
135//                              System.out.println("fe423fr");
136//                      }
137                        List<CampaignFleetAPI> hostiles = Misc.findNearbyFleets(fleet, 200, new FleetFilter() {
138                                public boolean accept(CampaignFleetAPI curr) {
139                                        //return curr.getFaction().isHostileTo(fleet.getFaction());
140                                        return curr.isHostileTo(fleet);
141                                }
142                        });
143                        if (inspectionTarget != null) {
144                                float dist = Misc.getDistance(inspectionTarget.getLocation(), fleet.getLocation());
145                                if (dist > 2000 || inspectionTarget.getContainingLocation() != fleet.getContainingLocation()) {
146                                        inspectionTarget.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
147                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
148                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.FOLLOW);
149                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
150                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.FOLLOW);
151                                        //fleet.getAI().addAssignmentAtStart(FleetAssignment.FOLLOW, target, 2.5f, "performing customs inspection", null);
152                                        inspectionTarget = null;
153                                        fleet.getMemoryWithoutUpdate().unset("$performingNPCInspection"); 
154                                        return;
155                                }
156                        }
157                        
158                        if (fleet.getMemoryWithoutUpdate().contains("$performingNPCInspection") && hostiles.isEmpty()) {
159                                float dist = Misc.getDistance(inspectionTarget.getLocation(), fleet.getLocation());
160                                float radSum = inspectionTarget.getRadius() + fleet.getRadius();
161                                FleetAssignmentDataAPI curr = fleet.getAI().getCurrentAssignment();
162                                Vector2f offset = Vector2f.sub(fleet.getLocation(), inspectionTarget.getLocation(), new Vector2f());
163                                Misc.normalise(offset);
164                                offset.scale(radSum);
165                                Vector2f.add(inspectionTarget.getLocation(), offset, offset);
166                                SectorEntityToken loc = fleet.getContainingLocation().createToken(offset.x, offset.y);
167                                
168                                boolean forceReapproach = false;
169                                if (curr != null && curr.getTarget() != null) {
170                                        float locDist = Misc.getDistance(inspectionTarget.getLocation(), curr.getTarget().getLocation());
171                                        forceReapproach = locDist > radSum + 5f;
172                                }
173//                              if (fleet.isInCurrentLocation()) {
174//                                      System.out.println("dsfwefe");
175//                              }
176                                if ((dist - radSum > 5 || dist - radSum < -5) && (curr == null || curr.getAssignment() != FleetAssignment.FOLLOW || forceReapproach)) {
177                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
178                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.FOLLOW);
179                                        //fleet.getAI().addAssignmentAtStart(FleetAssignment.FOLLOW, inspectionTarget, 2f, "approaching " + inspectionTarget.getName(), null);
180                                        if (dist - radSum <= 50) {
181                                                fleet.getAI().addAssignmentAtStart(FleetAssignment.FOLLOW, loc, 0.1f, "performing customs inspection", null);
182                                        } else {
183                                                fleet.getAI().addAssignmentAtStart(FleetAssignment.FOLLOW, loc, 0.1f, "approaching " + inspectionTarget.getName(), null);
184                                        }
185                                } else if ((dist - radSum <= 5 && dist - radSum >= -5) && (curr == null || curr.getAssignment() != FleetAssignment.HOLD)) {
186                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.FOLLOW);
187                                        fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
188                                        
189                                        //fleet.getAI().addAssignmentAtStart(FleetAssignment.HOLD, inspectionTarget, 2f, "performing customs inspection", null);
190                                        //fleet.getAI().addAssignmentAtStart(FleetAssignment.HOLD, loc, 2f, "performing customs inspection", null);
191                                        fleet.getAI().addAssignmentAtStart(FleetAssignment.HOLD, null, 0.1f, "performing customs inspection", null);
192                                }
193                        } else {
194                                inspectionTarget.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
195                                fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
196                                fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.FOLLOW);
197                                fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.HOLD);
198                                fleet.getAI().removeFirstAssignmentIfItIs(FleetAssignment.FOLLOW);
199                                //fleet.getAI().addAssignmentAtStart(FleetAssignment.FOLLOW, target, 2.5f, "performing customs inspection", null);
200                                inspectionTarget = null;
201                                fleet.getMemoryWithoutUpdate().unset("$performingNPCInspection");
202                        }
203                }
204        }
205
206        private CampaignFleetAPI inspectionTarget = null;
207        private void checkNPCCustomsInspection() {
208                
209                //if (!fleet.isInCurrentLocation()) return;
210                //if ((float) Math.random() < 0.75f) return;
211                
212                if (inspectionTarget != null) return;
213                if (fleet.getMemoryWithoutUpdate().contains(MemFlags.FLEET_BUSY)) return;
214                if (!fleet.getMemoryWithoutUpdate().contains(MemFlags.MEMORY_KEY_CUSTOMS_INSPECTOR)) return;
215                
216                if (fleet.getAI() != null && 
217                                !fleet.getAI().isCurrentAssignment(FleetAssignment.PATROL_SYSTEM) && 
218                                !fleet.getAI().isCurrentAssignment(FleetAssignment.DEFEND_LOCATION)) return;
219                
220                MarketAPI closest = Misc.findNearestLocalMarketWithSameFaction(fleet, 1500);
221                if (closest == null || !closest.getFactionId().equals(fleet.getFaction().getId())) return;
222                
223                List<CampaignFleetAPI> hostiles = Misc.findNearbyFleets(fleet, 600, new FleetFilter() {
224                        public boolean accept(CampaignFleetAPI curr) {
225                                //return curr.getFaction().isHostileTo(fleet.getFaction());
226                                return curr.isHostileTo(fleet);
227                        }
228                });
229                if (!hostiles.isEmpty()) return;
230                
231                
232                List<CampaignFleetAPI> allFleets = new ArrayList<CampaignFleetAPI>(fleet.getContainingLocation().getFleets());
233                allFleets.addAll(fleet.getContainingLocation().getFleets());
234                WeightedRandomPicker<CampaignFleetAPI> picker = new WeightedRandomPicker<CampaignFleetAPI>();
235                for (final CampaignFleetAPI curr : allFleets) {
236                        if (curr == fleet) continue;
237                        //if (curr.getFaction().isHostileTo(fleet.getFaction())) continue;
238                        if (curr.isHostileTo(fleet)) continue;
239
240                        if (curr.isInHyperspaceTransition()) continue;
241                        
242                        if (!curr.getMemoryWithoutUpdate().contains(MemFlags.MEMORY_KEY_SMUGGLER) && 
243                                        !curr.getMemoryWithoutUpdate().contains(MemFlags.MEMORY_KEY_TRADE_FLEET)) continue;
244
245                        if (curr.getMemoryWithoutUpdate().contains("$recentlyInspected")) return;
246                        if (curr.getMemoryWithoutUpdate().contains(MemFlags.FLEET_BUSY)) return;
247                        
248                        float dist = Misc.getDistance(curr.getLocation(), fleet.getLocation());
249                        if (dist > 1000) continue;
250                        if (dist < 100) dist = 100f;
251                        
252                        hostiles = Misc.findNearbyFleets(curr, 600, new FleetFilter() {
253                                public boolean accept(CampaignFleetAPI curr) {
254                                        //return curr.getFaction().isHostileTo(curr.getFaction());
255                                        return curr.isHostileTo(fleet);
256                                }
257                        });
258                        if (!hostiles.isEmpty()) continue;
259
260                        picker.add(curr, 1000f / dist);
261                }
262                
263                if (picker.isEmpty()) return;
264                
265                CampaignFleetAPI target = picker.pick();
266
267                target.getMemoryWithoutUpdate().set("$recentlyInspected", true, 15f);
268                target.getMemoryWithoutUpdate().set(MemFlags.FLEET_BUSY, true, 3f);
269                fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_BUSY, true, 3f);
270                fleet.getMemoryWithoutUpdate().set("$performingNPCInspection", true, 2f);
271                //fleet.getMemoryWithoutUpdate().set("$performingNPCInspection", true, 3f);
272                
273                target.getAI().addAssignmentAtStart(FleetAssignment.HOLD, null, 2f, "standing by for inspection", null);
274                //fleet.getAI().addAssignmentAtStart(FleetAssignment.FOLLOW, target, 2.5f, "performing customs inspection", null);
275                
276                inspectionTarget = target;
277        }
278        
279        
280        
281        
282
283        private float getDaysToOrbit() {
284                float daysToOrbit = 0f;
285                switch (data.type) {
286                case FAST:
287                        daysToOrbit += 2f;
288                        break;
289                case COMBAT:
290                        daysToOrbit += 4f;
291                        break;
292                case HEAVY:
293                        daysToOrbit += 6f;
294                        break;
295                }
296                
297                daysToOrbit = daysToOrbit * (0.5f + (float) Math.random() * 0.5f);
298                return daysToOrbit;
299        }
300        
301        public boolean isDone() {
302                return false;
303        }
304        public boolean runWhilePaused() {
305                return false;
306        }
307
308}
309
310
311
312
313