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