001package com.fs.starfarer.api.impl.campaign.fleets.misc; 002 003import java.util.ArrayList; 004import java.util.LinkedHashMap; 005import java.util.List; 006import java.util.Map; 007import java.util.Random; 008 009import org.apache.log4j.Logger; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.campaign.BattleAPI; 013import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 014import com.fs.starfarer.api.campaign.CampaignFleetAPI; 015import com.fs.starfarer.api.campaign.SectorEntityToken; 016import com.fs.starfarer.api.campaign.econ.MarketAPI; 017import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 018import com.fs.starfarer.api.impl.campaign.fleets.BaseRouteFleetManager; 019import com.fs.starfarer.api.impl.campaign.fleets.RouteManager; 020import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData; 021import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 022import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment; 023import com.fs.starfarer.api.util.IntervalUtil; 024import com.fs.starfarer.api.util.TimeoutTracker; 025import com.fs.starfarer.api.util.WeightedRandomPicker; 026 027public class MiscFleetRouteManager extends BaseRouteFleetManager implements FleetEventListener { 028 029 public static List<MiscFleetCreatorPlugin> CREATORS = new ArrayList<MiscFleetCreatorPlugin>(); 030 static { 031 CREATORS.add(new MiscPilgrimFleetCreator()); 032 CREATORS.add(new MiscAcademyFleetCreator()); 033 } 034 035 public static class MiscRouteData { 036 public String creatorId; 037 public boolean smuggling = false; 038 public MarketAPI from; 039 public SectorEntityToken to; 040 public float size; 041 public String factionId; 042 public Object custom; 043 public MiscRouteData(String creatorId) { 044 this.creatorId = creatorId; 045 } 046 047 } 048 049 050 public static final Integer ROUTE_SRC_LOAD = 1; 051 public static final Integer ROUTE_TRAVEL_DST = 2; 052 public static final Integer ROUTE_DST_UNLOAD = 5; 053 public static final Integer ROUTE_DST_LOAD = 6; 054 public static final Integer ROUTE_TRAVEL_BACK_WS = 7; 055 public static final Integer ROUTE_RESUPPLY_BACK_WS = 8; 056 public static final Integer ROUTE_TRAVEL_SRC = 9; 057 public static final Integer ROUTE_SRC_UNLOAD = 10; 058 059 public static final String SOURCE_ID = "misc_fleet_"; 060 public static Logger log = Global.getLogger(MiscFleetRouteManager.class); 061 062 063 //protected TimeoutTracker<String> recentlySentPilgrims = new TimeoutTracker<String>(); 064 protected Map<String, TimeoutTracker<String>> recentlySent = new LinkedHashMap<String, TimeoutTracker<String>>(); 065 protected Random random = new Random(); 066 transient protected LinkedHashMap data = new LinkedHashMap(); 067 068 public MiscFleetRouteManager() { 069 super(1f, 14f); // whatever values; overridden below 070 071 float min = Global.getSettings().getFloatFromArray("miscSpawnInterval", 0); 072 float max = Global.getSettings().getFloatFromArray("miscSpawnInterval", 1); 073 interval = new IntervalUtil(min, max); 074 } 075 076 protected Object readResolve() { 077 if (recentlySent == null) { 078 recentlySent = new LinkedHashMap<String, TimeoutTracker<String>>(); 079 } 080 if (data == null) { 081 data = new LinkedHashMap(); 082 } 083 return this; 084 } 085 086 087 088 public Random getRandom() { 089 return random; 090 } 091 092 public TimeoutTracker<String> getTimeout(String creatorId) { 093 TimeoutTracker<String> result = recentlySent.get(creatorId); 094 if (result == null) { 095 result = new TimeoutTracker<String>(); 096 recentlySent.put(creatorId, result); 097 } 098 return result; 099 } 100 101 @Override 102 public void advance(float amount) { 103 104 //super.advance(amount * 10f); 105 super.advance(amount); 106 107 float days = Global.getSector().getClock().convertToDays(amount); 108 for (TimeoutTracker<String> curr : recentlySent.values()) { 109 curr.advance(days); 110 } 111 } 112 113 protected String getRouteSourceId() { 114 return SOURCE_ID; 115 } 116 117 protected int getMaxFleets() { 118 //if (true) return 1; 119 int numMarkets = Global.getSector().getEconomy().getNumMarkets(); 120 int maxBasedOnMarkets = numMarkets * 1; 121 return Math.min(maxBasedOnMarkets, Global.getSettings().getInt("maxMiscFleets")); 122 } 123 124 125 protected void addRouteFleetIfPossible() { 126 127 WeightedRandomPicker<MiscFleetCreatorPlugin> picker = new WeightedRandomPicker<MiscFleetCreatorPlugin>(random); 128 for (MiscFleetCreatorPlugin curr : CREATORS) { 129 int count = 0; 130 for (RouteData route : RouteManager.getInstance().getRoutesForSource(getRouteSourceId())) { 131 if (route.getCustom() instanceof MiscRouteData) { 132 MiscRouteData data = (MiscRouteData) route.getCustom(); 133 if (curr.getId().equals(data.creatorId)) { 134 count++; 135 } 136 } 137 } 138 if (count >= curr.getMaxFleetsForThisCreator()) { 139 continue; 140 } 141 142 picker.add(curr, curr.getFrequency()); 143 } 144 145 MiscFleetCreatorPlugin creator = picker.pick(); 146 if (creator == null) return; 147 148 MiscRouteData params = creator.createRouteParams(this, random); 149 150 if (params != null) { 151 152 MarketAPI from = params.from; 153 SectorEntityToken to = params.to; 154 log.info("Created misc [" + creator.getId() + "] " + from.getName() + " to " + to.getName()); 155 156 //Long seed = Misc.genRandomSeed(); 157 Long seed = random.nextLong(); 158 String id = getRouteSourceId(); 159 160 OptionalFleetData extra = new OptionalFleetData(from); 161 extra.factionId = params.factionId; 162 163 RouteData route = RouteManager.getInstance().addRoute(id, from, seed, extra, this); 164 route.setCustom(params); 165 166 float orbitDays = params.size * (0.75f + random.nextFloat() * 0.5f); 167 orbitDays *= 30f; 168 if (orbitDays < 1f) orbitDays = 1f; 169 if (orbitDays > 3f) orbitDays = 3f; 170 171 route.addSegment(new RouteSegment(ROUTE_SRC_LOAD, orbitDays, from.getPrimaryEntity())); 172 route.addSegment(new RouteSegment(ROUTE_TRAVEL_DST, from.getPrimaryEntity(), to)); 173 route.addSegment(new RouteSegment(ROUTE_DST_UNLOAD, orbitDays * 0.5f, to)); 174 route.addSegment(new RouteSegment(ROUTE_DST_LOAD, orbitDays * 0.5f, to)); 175 route.addSegment(new RouteSegment(ROUTE_TRAVEL_SRC, to, from.getPrimaryEntity())); 176 route.addSegment(new RouteSegment(ROUTE_SRC_UNLOAD, orbitDays, from.getPrimaryEntity())); 177 178 float min = Global.getSettings().getFloatFromArray("miscSpawnTimeoutPerMarket", 0); 179 float max = Global.getSettings().getFloatFromArray("miscSpawnTimeoutPerMarket", 1); 180 float timeout = min + (max - min) * random.nextFloat(); 181 182 getTimeout(creator.getId()).add(from.getId(), timeout); 183 } 184 } 185 186 187 public boolean shouldCancelRouteAfterDelayCheck(RouteData route) { 188 return false; 189 } 190 191 public static MiscFleetCreatorPlugin getCreator(String id) { 192 for (MiscFleetCreatorPlugin curr : CREATORS) { 193 if (curr.getId().equals(id)) { 194 return curr; 195 } 196 } 197 return null; 198 } 199 200 201 public CampaignFleetAPI spawnFleet(RouteData route) { 202 Random random = this.random; 203 if (route.getSeed() != null) { 204 random = new Random(route.getSeed()); 205 } 206 207 MiscRouteData data = (MiscRouteData) route.getCustom(); 208 209 MiscFleetCreatorPlugin curr = getCreator(data.creatorId); 210 if (curr != null) { 211 CampaignFleetAPI fleet = curr.createFleet(this, route, random); 212 if (fleet != null) { 213 fleet.addEventListener(this); 214 fleet.addScript(new MiscFleetAssignmentAI(fleet, route)); 215 } 216 return fleet; 217 } 218 return null; 219 } 220 221 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 222 RouteData route = RouteManager.getInstance().getRoute(getRouteSourceId(), fleet); 223 if (route == null || !(route.getCustom() instanceof MiscRouteData)) return; 224 225 //if (route.isExpired()) return; 226 if (!battle.isPlayerInvolved()) return; 227 228 MiscRouteData data = (MiscRouteData) route.getCustom(); 229 230 MiscFleetCreatorPlugin curr = getCreator(data.creatorId); 231 if (curr != null) { 232 curr.reportBattleOccurred(this, fleet, primaryWinner, battle); 233 } 234 } 235 236 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 237 RouteData route = RouteManager.getInstance().getRoute(getRouteSourceId(), fleet); 238 if (route == null || !(route.getCustom() instanceof MiscRouteData)) return; 239 240 MiscRouteData data = (MiscRouteData) route.getCustom(); 241 242 MiscFleetCreatorPlugin curr = getCreator(data.creatorId); 243 if (curr != null) { 244 curr.reportFleetDespawnedToListener(this, fleet, reason, param); 245 } 246 } 247 248 public boolean shouldRepeat(RouteData route) { 249 return false; 250 } 251 252 public void reportAboutToBeDespawnedByRouteManager(RouteData route) { 253 254 } 255 256 public LinkedHashMap getData() { 257 return data; 258 } 259 260 261} 262 263 264 265 266 267 268