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