001package com.fs.starfarer.api.impl.campaign.fleets; 002 003import java.util.ArrayList; 004import java.util.List; 005 006import org.apache.log4j.Logger; 007 008import com.fs.starfarer.api.EveryFrameScript; 009import com.fs.starfarer.api.Global; 010import com.fs.starfarer.api.campaign.BaseCampaignEventListener; 011import com.fs.starfarer.api.campaign.BattleAPI; 012import com.fs.starfarer.api.campaign.CampaignFleetAPI; 013import com.fs.starfarer.api.campaign.FleetStubAPI; 014import com.fs.starfarer.api.campaign.SectorEntityToken; 015import com.fs.starfarer.api.campaign.econ.MarketAPI; 016import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 017import com.fs.starfarer.api.impl.campaign.fleets.FleetFactory.PatrolType; 018import com.fs.starfarer.api.impl.campaign.ids.Conditions; 019import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 020import com.fs.starfarer.api.impl.campaign.ids.Industries; 021import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 022import com.fs.starfarer.api.impl.campaign.ids.Ranks; 023import com.fs.starfarer.api.util.IntervalUtil; 024import com.fs.starfarer.api.util.Misc; 025import com.fs.starfarer.api.util.RollingAverageTracker; 026import com.fs.starfarer.api.util.WeightedRandomPicker; 027 028public class PatrolFleetManagerV2 extends BaseCampaignEventListener implements 029 EveryFrameScript, FleetEventListener { 030 031 public static Logger log = Global.getLogger(PatrolFleetManagerV2.class); 032 033 public static class PatrolFleetData { 034 public float startingFleetPoints = 0; 035 public FleetStubAPI stub; 036 public PatrolType type; 037 public MarketAPI sourceMarket; 038 public PatrolFleetData(FleetStubAPI fleet, PatrolType type) { 039 this.stub = fleet; 040 this.type = type; 041 } 042 } 043 044 //private float econInterval = Global.getSettings().getFloat("economyIntervalnGameDays"); 045 046 private MarketAPI market; 047 private List<PatrolFleetData> activePatrols = new ArrayList<PatrolFleetData>(); 048 private IntervalUtil tracker; 049 private int maxPatrols; 050 051 private RollingAverageTracker patrolBattlesLost; 052 public PatrolFleetManagerV2(MarketAPI market) { 053 super(true); 054 this.market = market; 055 056 float interval = Global.getSettings().getFloat("averagePatrolSpawnInterval"); 057 tracker = new IntervalUtil(interval * 0.75f, interval * 1.25f); 058 059 readResolve(); 060 } 061 062 protected Object readResolve() { 063 if (patrolBattlesLost == null) { 064 float patrolStrengthCheckInterval = Global.getSettings().getFloat("economyIntervalnGameDays"); 065 float min = patrolStrengthCheckInterval - Math.min(patrolStrengthCheckInterval * 0.5f, 2f); 066 float max = patrolStrengthCheckInterval + Math.min(patrolStrengthCheckInterval * 0.5f, 2f); 067 patrolBattlesLost = new RollingAverageTracker(min, max, Misc.getGenericRollingAverageFactor()); 068 } 069 return this; 070 } 071 072 public void advance(float amount) { 073 //if (true) return; 074 float days = Global.getSector().getClock().convertToDays(amount); 075 076 patrolBattlesLost.advance(days); 077 078 float losses = patrolBattlesLost.getAverage(); 079 080 //tracker.advance(days); 081 tracker.advance(days * Math.max(1f, losses)); 082 if (!tracker.intervalElapsed()) return; 083 084 if (market.hasCondition(Conditions.DECIVILIZED)) return; 085 086// if (market.getId().equals("jangala")) { 087// System.out.println("fwefwefe"); 088// } 089 090 List<PatrolFleetData> remove = new ArrayList<PatrolFleetData>(); 091 for (PatrolFleetData data : activePatrols) { 092 if (data.stub.getContainingLocation() == null || 093 (!data.stub.getContainingLocation().getFleets().contains(data.stub.getFleet()) && 094 !data.stub.getContainingLocation().getFleetStubs().contains(data.stub))) { 095 remove.add(data); 096 log.info("Cleaning up orphaned patrol [" + data.stub.getId() + "] for market [" + market.getName() + "]"); 097 } 098 } 099 activePatrols.removeAll(remove); 100 101// if (market.getId().equals("jangala")) { 102// System.out.println("23rwefwe"); 103// } 104 //maxPatrols = Math.max(1, market.getSize() - 3) + (int) (market.getStabilityValue() * 0.5f); 105 //float losses = patrolBattlesLost.getAverage(); 106 107 maxPatrols = (int) (Math.max(1, market.getSize() - 3) * (market.getStabilityValue() / 10f)) + 108 (int) Math.max(0, Math.min(losses, 5)); 109 if (maxPatrols < 1) maxPatrols = 1; 110 111 boolean hasStationOrSpaceport = market.hasIndustry(Industries.ORBITALSTATION) || 112 market.hasSpaceport() || 113 market.hasIndustry(Industries.BATTLESTATION); 114 if (market.hasIndustry(Industries.MILITARYBASE)) { 115 maxPatrols += 1; 116 if (hasStationOrSpaceport) maxPatrols++; 117 } 118 if (hasStationOrSpaceport) maxPatrols++; 119// if (market.getId().equals("jangala")) { 120// System.out.println("fwefewew"); 121// } 122 123 log.debug(""); 124 log.debug("Checking whether to spawn patrol for market [" + market.getName() + "]"); 125 if (activePatrols.size() < maxPatrols) { 126 log.info(activePatrols.size() + " out of a maximum " + maxPatrols + " patrols in play for market [" + market.getName() + "]"); 127 128 WeightedRandomPicker<PatrolType> picker = new WeightedRandomPicker<PatrolType>(); 129 picker.add(PatrolType.FAST, 130 Math.max(1, maxPatrols - getCount(PatrolType.COMBAT, PatrolType.HEAVY))); 131 picker.add(PatrolType.COMBAT, 132 Math.max(1, maxPatrols - getCount(PatrolType.FAST, PatrolType.HEAVY) + market.getSize()) + losses * 0.5f); 133 134 if (market.getSize() >= 5) { 135 picker.add(PatrolType.HEAVY, 136 Math.max(1, maxPatrols - getCount(PatrolType.FAST, PatrolType.COMBAT) + market.getSize() - 5) + losses); 137 } 138 139 140 PatrolType type = picker.pick(); 141 142 float combat = 0f; 143 float tanker = 0f; 144 float freighter = 0f; 145 String fleetType = FleetTypes.PATROL_SMALL; 146 switch (type) { 147 case FAST: 148 fleetType = FleetTypes.PATROL_SMALL; 149 combat = Math.round(3f + (float) Math.random() * 2f); 150 combat += Math.min(5f, losses * 2f); 151 break; 152 case COMBAT: 153 fleetType = FleetTypes.PATROL_MEDIUM; 154 combat = Math.round(6f + (float) Math.random() * 3f); 155 combat += Math.min(15f, losses * 4f); 156 157 tanker = Math.round((float) Math.random()); 158 break; 159 case HEAVY: 160 fleetType = FleetTypes.PATROL_LARGE; 161 combat = Math.round(10f + (float) Math.random() * 5f); 162 combat += Math.min(25f, losses * 6f); 163 164 tanker = 2f; 165 freighter = 2f; 166 break; 167 } 168 combat *= 1f + (market.getStabilityValue() / 20f); 169 //combat += Math.min(30f, losses * 3f); 170 171 //CampaignFleetAPI fleet = FleetFactoryV2.createFleet(new FleetParams( 172 173 SectorEntityToken entity = market.getPrimaryEntity(); 174 //FleetOrStubAPI fleet = FleetFactoryV2.createFleetOrStub(new FleetParams( 175 FleetStubAPI stub = null; 176// FleetFactoryV2.createStub(new FleetParams( 177// null, 178// market, 179// market.getFactionId(), 180// null, // fleet's faction, if different from above, which is also used for source market picking 181// fleetType, 182// combat, // combatPts 183// freighter, // freighterPts 184// tanker, // tankerPts 185// 0f, // transportPts 186// 0f, // linerPts 187// 0f, // civilianPts 188// 0f, // utilityPts 189// 0f, // qualityBonus 190// -1f, // qualityOverride 191// 1f + Math.min(1f, losses / 10f), // officer num mult 192// 0 + (int) losses// officer level bonus 193// ), entity.getContainingLocation(), 194// new Vector2f(entity.getLocation().x, entity.getLocation().y)); 195// 196// stub.addEventListener(this); 197 198 199 PatrolFleetData data = new PatrolFleetData(stub, type); 200 data.startingFleetPoints = combat * 3; 201 data.sourceMarket = market; 202 activePatrols.add(data); 203 204 PatrolAssignmentAIV2 ai = new PatrolAssignmentAIV2(stub, data); 205 stub.addScript(ai); 206 207 stub.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PATROL_FLEET, true); 208 209// if ((type == PatrolType.FAST && (float) Math.random() > 0.25f) || 210// (type == PatrolType.COMBAT && (float) Math.random() > 0.5f)) { 211 212 if (type == PatrolType.FAST || type == PatrolType.COMBAT) { 213 stub.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_CUSTOMS_INSPECTOR, true); 214 } 215 216 stub.setAdmiralPost(Ranks.POST_PATROL_COMMANDER); 217 218 switch (type) { 219 case FAST: 220 stub.setAdmiralRank(Ranks.SPACE_LIEUTENANT); 221 break; 222 case COMBAT: 223 stub.setAdmiralRank(Ranks.SPACE_COMMANDER); 224 break; 225 case HEAVY: 226 stub.setAdmiralRank(Ranks.SPACE_CAPTAIN); 227 break; 228 } 229 230// FleetStubConverterPlugin converter = stub.getConverter(); 231// if (stub.isStub() && converter.shouldConvertFromStub(stub)) { 232// converter.convertToFleet(stub); 233// } 234 235 236 log.info("Spawned patrol fleet [" + stub.getId() + "] from market " + market.getName()); 237 } else { 238 log.debug("Maximum number of " + maxPatrols + " patrols already in play for market [" + market.getName() + "]"); 239 } 240 } 241 242 private int getCount(PatrolType ... types) { 243 int count = 0; 244 for (PatrolType type : types) { 245 for (PatrolFleetData data : activePatrols) { 246 if (data.type == type) count++; 247 } 248 } 249 return count; 250 } 251 252 public boolean isDone() { 253 return false; 254 } 255 256 public boolean runWhilePaused() { 257 return false; 258 } 259 260 261 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 262 for (PatrolFleetData data : activePatrols) { 263 if (data.stub.getId() == fleet.getId()) { 264 activePatrols.remove(data); 265 break; 266 } 267 } 268 } 269 270 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 271 272 } 273 274 275 /* 276 @Override 277 public void reportFleetDespawned(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 278 super.reportFleetDespawned(fleet, reason, param); 279 280 for (PatrolFleetData data : activePatrols) { 281 if (data.fleet == fleet) { 282 activePatrols.remove(data); 283 break; 284 } 285 } 286 } 287 288 @Override 289 public void reportBattleOccurred(CampaignFleetAPI primaryWinner, BattleAPI battle) { 290 super.reportBattleOccurred(primaryWinner, battle); 291 292 boolean playerWon = battle.isPlayerSide(battle.getSideFor(primaryWinner)); 293 boolean playerLost = battle.isPlayerSide(battle.getOtherSideFor(primaryWinner)); 294 if (primaryWinner.isInOrNearSystem(market.getStarSystem())) { 295 // losing to pirates doesn't trigger patrol strength going up; don't want pirates wiped out 296 if (primaryWinner.getFaction().getId().equals(Factions.PIRATES)) return; 297 if (primaryWinner.getFaction().getId().equals(Factions.LUDDIC_PATH)) return; 298 299 for (CampaignFleetAPI loser : battle.getOtherSideSnapshotFor(primaryWinner)) { 300 if (loser.getFaction() == market.getFaction()) { 301 if (playerWon) { 302 patrolBattlesLost.add(1); 303 } else { 304 //patrolBattlesLost.add(1); 305 } 306 } else if (primaryWinner.getFaction() == market.getFaction()) { 307 // winning vs pirates doesn't trigger strength getting smaller, might happen too easily 308 if (loser.getFaction().getId().equals(Factions.PIRATES)) return; 309 if (loser.getFaction().getId().equals(Factions.LUDDIC_PATH)) return; 310 if (playerLost) { 311 patrolBattlesLost.sub(1); 312 } else { 313 //patrolBattlesLost.sub(1); 314 } 315 } 316 } 317 } 318 } 319 320 */ 321 322} 323 324 325 326 327 328 329 330 331 332 333 334 335 336