001package com.fs.starfarer.api.impl.campaign.fleets; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.LinkedHashMap; 007import java.util.List; 008import java.util.Map; 009import java.util.Random; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.campaign.CampaignFleetAPI; 013import com.fs.starfarer.api.campaign.CargoAPI; 014import com.fs.starfarer.api.campaign.FactionAPI.ShipPickParams; 015import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI; 016import com.fs.starfarer.api.campaign.econ.MarketAPI; 017import com.fs.starfarer.api.fleet.FleetMemberAPI; 018import com.fs.starfarer.api.fleet.ShipRolePick; 019import com.fs.starfarer.api.impl.campaign.econ.impl.BaseIndustry; 020import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 021import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment; 022import com.fs.starfarer.api.impl.campaign.ids.Commodities; 023import com.fs.starfarer.api.impl.campaign.ids.Factions; 024import com.fs.starfarer.api.impl.campaign.ids.ShipRoles; 025import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI; 026import com.fs.starfarer.api.util.IntervalUtil; 027import com.fs.starfarer.api.util.Misc; 028import com.fs.starfarer.api.util.WeightedRandomPicker; 029 030public class EconomyFleetAssignmentAI extends RouteFleetAssignmentAI { 031 032 public static class CargoQuantityData { 033 public String cargo; 034 public int units; 035 public CargoQuantityData(String cargo, int units) { 036 this.cargo = cargo; 037 this.units = units; 038 } 039 040 public CommoditySpecAPI getCommodity() { 041 CommoditySpecAPI spec = Global.getSettings().getCommoditySpec(cargo); 042 return spec; 043 } 044 } 045 046 public static class EconomyRouteData { 047 public float cargoCap, fuelCap, personnelCap; 048 public float size; 049 public boolean smuggling = false; 050 public MarketAPI from; 051 public MarketAPI to; 052 053 public List<CargoQuantityData> cargoDeliver = new ArrayList<CargoQuantityData>(); 054 public List<CargoQuantityData> cargoReturn = new ArrayList<CargoQuantityData>(); 055 056 public void addDeliver(String id, int qty) { 057 cargoDeliver.add(new CargoQuantityData(id, qty)); 058 } 059 public void addReturn(String id, int qty) { 060 cargoReturn.add(new CargoQuantityData(id, qty)); 061 } 062 063 public static String getCargoList(List<CargoQuantityData> cargo) { 064 List<String> strings = new ArrayList<String>(); 065 066 List<CargoQuantityData> sorted = new ArrayList<CargoQuantityData>(cargo); 067 Collections.sort(sorted, new Comparator<CargoQuantityData>() { 068 public int compare(CargoQuantityData o1, CargoQuantityData o2) { 069 if (o1.getCommodity().isPersonnel() && !o2.getCommodity().isPersonnel()) { 070 return 1; 071 } 072 if (o2.getCommodity().isPersonnel() && !o1.getCommodity().isPersonnel()) { 073 return -1; 074 } 075 return o2.units - o1.units; 076 } 077 }); 078 079 for (CargoQuantityData curr : sorted) { 080 CommoditySpecAPI spec = curr.getCommodity(); 081 //CommodityOnMarketAPI com = from.getCommodityData(curr.cargo); 082 //if (com.getId().equals(Commodities.SHIPS)) { 083 if (spec.getId().equals(Commodities.SHIPS)) { 084 strings.add("ship hulls"); 085 continue; 086 } 087 //if (com.getCommodity().isMeta()) continue; 088 //strings.add(com.getCommodity().getName().toLowerCase()); 089 if (spec.isMeta()) continue; 090 strings.add(spec.getName().toLowerCase()); 091 } 092 if (strings.size() > 4) { 093 List<String> copy = new ArrayList<String>(); 094 copy.add(strings.get(0)); 095 copy.add(strings.get(1)); 096 copy.add("other commodities"); 097 strings = copy; 098 } 099 return Misc.getAndJoined(strings); 100 } 101 } 102 103 104 private String origFaction; 105 private IntervalUtil factionChangeTracker = new IntervalUtil(0.1f, 0.3f); 106 public EconomyFleetAssignmentAI(CampaignFleetAPI fleet, RouteData route) { 107 super(fleet, route); 108 //origFaction = fleet.getFaction().getId(); 109 origFaction = route.getFactionId(); 110 if (!getData().smuggling) { 111 origFaction = null; 112 factionChangeTracker = null; 113 } else { 114 factionChangeTracker.forceIntervalElapsed(); 115 doSmugglingFactionChangeCheck(0.1f); 116 } 117 } 118 119 public static String getCargoListDeliver(RouteData route) { 120 return getCargoList(route, route.getSegments().get(0)); 121 } 122 public static String getCargoListReturn(RouteData route) { 123 return getCargoList(route, route.getSegments().get(3)); 124 } 125 public static String getCargoList(RouteData route, RouteSegment segment) { 126// int index = route.getSegments().indexOf(segment); 127 EconomyRouteData data = (EconomyRouteData) route.getCustom(); 128 129 Integer id = segment.getId(); 130 131 if (id <= EconomyFleetRouteManager.ROUTE_DST_UNLOAD) { 132 return EconomyRouteData.getCargoList(data.cargoDeliver); 133 } 134 return EconomyRouteData.getCargoList(data.cargoReturn); 135 } 136 protected String getCargoList(RouteSegment segment) { 137 return getCargoList(route, segment); 138 } 139 140 protected void updateCargo(RouteSegment segment) { 141 //int index = route.getSegments().indexOf(segment); 142 143 // 0: loading from 144 // 1: moving to 145 // 2: unloading to 146 // 3: loading to 147 // 4: moving from 148 // 5: unloading from 149 150 Integer id = segment.getId(); 151 152 if (route.isExpired() || id == EconomyFleetRouteManager.ROUTE_SRC_LOAD || 153 id == EconomyFleetRouteManager.ROUTE_DST_LOAD) { 154 fleet.getCargo().clear(); 155 syncMothballedShips(0f, null); 156 return; 157 } 158 159 EconomyRouteData data = getData(); 160 MarketAPI cargoSource = data.from; 161 List<CargoQuantityData> list = data.cargoDeliver; 162 if (id > EconomyFleetRouteManager.ROUTE_DST_LOAD) { 163 cargoSource = data.to; 164 list = data.cargoReturn; 165 } 166 167 CargoAPI cargo = fleet.getCargo(); 168 cargo.clear(); 169 170 float total = 0f; 171 Map<String, Float> target = new LinkedHashMap<String, Float>(); 172 float ships = 0f; 173 for (CargoQuantityData curr : list) { 174 CommoditySpecAPI spec = Global.getSettings().getCommoditySpec(curr.cargo); 175 float qty = (int) (BaseIndustry.getSizeMult(curr.units) * spec.getEconUnit()); 176 177 if (curr.cargo.equals(Commodities.SHIPS)) { 178 ships = Math.max(ships, curr.units); 179 continue; 180 } 181 182 if (curr.cargo.equals(Commodities.FUEL)) { 183 cargo.addFuel(Math.min(qty, cargo.getMaxFuel())); 184 continue; 185 } 186 187 if (curr.cargo.equals(Commodities.CREW)) continue; 188 if (curr.cargo.equals(Commodities.MARINES)) continue; 189 190 total += qty; 191 target.put(curr.cargo, qty); 192 } 193 194 syncMothballedShips(ships, cargoSource); 195 196 if (total <= 0) return; 197 198 float maxCargo = cargo.getMaxCapacity(); 199 for (String cid : target.keySet()) { 200 float qty = target.get(cid); 201 202 cargo.addCommodity(cid, ((int) qty * Math.min(1f, maxCargo / total))); 203 } 204 } 205 206 protected void syncMothballedShips(float units, MarketAPI market) { 207 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 208 if (member.isMothballed()) { 209 fleet.getFleetData().removeFleetMember(member); 210 } 211 } 212 213 if (units <= 0) return; 214 215 216 Random random = new Random(); 217 if (route.getSeed() != null) { 218 random = new Random(route.getSeed()); 219 } 220 221 float add = units * 1.5f + random.nextInt(3); 222 223 EconomyRouteData data = getData(); 224 boolean sameFaction = data.from.getFaction() == data.to.getFaction(); 225 for (int i = 0; i < add; i++) { 226 WeightedRandomPicker<String> roles = new WeightedRandomPicker<String>(random); 227 roles.add(ShipRoles.COMBAT_FREIGHTER_SMALL, 20f); 228 roles.add(ShipRoles.FREIGHTER_SMALL, 20f); 229 roles.add(ShipRoles.TANKER_SMALL, 10f); 230 if (i >= 2) { 231 roles.add(ShipRoles.COMBAT_FREIGHTER_MEDIUM, 20f * (i - 1)); 232 roles.add(ShipRoles.FREIGHTER_MEDIUM, 20f * (i - 1)); 233 roles.add(ShipRoles.TANKER_MEDIUM, 10f * (i - 1)); 234 } 235 if (i >= 5) { 236 roles.add(ShipRoles.COMBAT_FREIGHTER_LARGE, 20f * (i - 2)); 237 roles.add(ShipRoles.FREIGHTER_LARGE, 20f * (i - 2)); 238 roles.add(ShipRoles.TANKER_LARGE, 10f * (i - 2)); 239 } 240 241 String role = roles.pick(); 242 ShipPickParams params = ShipPickParams.priority(); 243 if (!sameFaction) params = ShipPickParams.imported(); 244 List<ShipRolePick> picks = market.pickShipsForRole(role, params, random, null); 245 for (ShipRolePick pick : picks) { 246 FleetMemberAPI member = fleet.getFleetData().addFleetMember(pick.variantId); 247 member.getRepairTracker().setMothballed(true); 248 } 249 } 250 fleet.getFleetData().sort(); 251 } 252 253 254 @Override 255 protected String getStartingActionText(RouteSegment segment) { 256 String list = getCargoList(segment); 257 if (list.isEmpty()) { 258 return "preparing for a voyage to " + getData().to.getName(); 259 } 260 return "loading " + list + " at " + getData().from.getName(); 261 } 262 @Override 263 protected String getEndingActionText(RouteSegment segment) { 264 String list = getCargoList(segment); 265 if (list.isEmpty()) { 266 return "orbiting " + getData().from.getName(); 267 } 268 return "unloading " + list + " at " + getData().from.getName(); 269 } 270 271 @Override 272 protected String getTravelActionText(RouteSegment segment) { 273 String list = getCargoList(segment); 274 275 //int index = route.getSegments().indexOf(segment); 276 Integer id = segment.getId(); 277 if (id == EconomyFleetRouteManager.ROUTE_TRAVEL_DST || id == EconomyFleetRouteManager.ROUTE_TRAVEL_WS) { 278 if (list.isEmpty()) { 279 return "traveling to " + getData().to.getName(); 280 } 281 return "delivering " + list + " to " + getData().to.getName(); 282 } else if (id == EconomyFleetRouteManager.ROUTE_TRAVEL_SRC || id == EconomyFleetRouteManager.ROUTE_TRAVEL_BACK_WS) { 283 if (list.isEmpty()) { 284 return "returning to " + getData().from.getName(); 285 } 286 return "returning to " + getData().from.getName() + " with " + list; 287 } 288 return super.getTravelActionText(segment); 289 } 290 291 @Override 292 protected String getInSystemActionText(RouteSegment segment) { 293 String list = getCargoList(segment); 294 //int index = route.getSegments().indexOf(segment); 295 Integer id = segment.getId(); 296 297 if (id == EconomyFleetRouteManager.ROUTE_DST_UNLOAD) { 298 if (list.isEmpty()) { 299 return "orbiting " + getData().to.getName(); 300 } 301 return "unloading " + list + " at " + getData().to.getName(); 302 } else if (id == EconomyFleetRouteManager.ROUTE_DST_LOAD) { 303 if (list.isEmpty()) { 304 return "orbiting " + getData().to.getName(); 305 } 306 return "loading " + list + " at " + getData().to.getName(); 307 } else if (id == EconomyFleetRouteManager.ROUTE_RESUPPLY_WS || id == EconomyFleetRouteManager.ROUTE_RESUPPLY_BACK_WS) { 308 return "resupplying"; 309 } 310 311 return super.getInSystemActionText(segment); 312 } 313 314 315 @Override 316 protected void addEndingAssignment(RouteSegment current, boolean justSpawned) { 317 super.addEndingAssignment(current, justSpawned); 318 updateCargo(current); 319 } 320 321 @Override 322 protected void addLocalAssignment(RouteSegment current, boolean justSpawned) { 323 super.addLocalAssignment(current, justSpawned); 324 updateCargo(current); 325 } 326 327 @Override 328 protected void addStartingAssignment(RouteSegment current, boolean justSpawned) { 329 super.addStartingAssignment(current, justSpawned); 330 updateCargo(current); 331 } 332 333 @Override 334 protected void addTravelAssignment(RouteSegment current, boolean justSpawned) { 335 super.addTravelAssignment(current, justSpawned); 336 updateCargo(current); 337 } 338 339 340 protected EconomyRouteData getData() { 341 EconomyRouteData data = (EconomyRouteData) route.getCustom(); 342 return data; 343 } 344 345 346 @Override 347 public void advance(float amount) { 348 super.advance(amount); 349 doSmugglingFactionChangeCheck(amount); 350 } 351 352 353 public void doSmugglingFactionChangeCheck(float amount) { 354 EconomyRouteData data = getData(); 355 if (!data.smuggling) return; 356 float days = Global.getSector().getClock().convertToDays(amount); 357 358// if (fleet.isInCurrentLocation()) { 359// System.out.println("23wefwf23"); 360// days *= 100000f; 361// } 362 363 factionChangeTracker.advance(days); 364 if (factionChangeTracker.intervalElapsed() && fleet.getAI() != null) { 365 MarketAPI align = null; 366 if (data.from.getStarSystem() == fleet.getContainingLocation()) { 367 align = data.from; 368 } else if (data.to.getStarSystem() == fleet.getContainingLocation()) { 369 align = data.to; 370 } 371 372 if (align != null) { 373 String targetFac = origFaction; 374 boolean hostile = align.getFaction().isHostileTo(targetFac); 375 if (hostile) { 376 targetFac = Factions.INDEPENDENT; 377 hostile = align.getFaction().isHostileTo(targetFac); 378 } 379 if (hostile) { 380 targetFac = align.getFactionId(); 381 } 382 if (!fleet.getFaction().getId().equals(targetFac)) { 383 fleet.setFaction(targetFac, true); 384 } 385 } else { 386 String targetFac = origFaction; 387 if (fleet.isInHyperspace()) { 388 targetFac = Factions.INDEPENDENT; 389 } 390 if (!fleet.getFaction().getId().equals(targetFac)) { 391 fleet.setFaction(targetFac, true); 392 } 393 } 394 395// SectorEntityToken target = route.getMarket().getPrimaryEntity(); 396// FleetAssignmentDataAPI assignment = fleet.getAI().getCurrentAssignment(); 397// if (assignment != null && assignment.getAssignment() != FleetAssignment.STANDING_DOWN) { 398// target = assignment.getTarget(); 399// } 400// if (target != null && target.getFaction() != null) { 401// boolean targetHostile = target.getFaction().isHostileTo(origFaction); 402// boolean mathchesTarget = fleet.getFaction().getId().equals(target.getFaction().getId()); 403// boolean mathchesOrig = fleet.getFaction().getId().equals(origFaction); 404// float dist = Misc.getDistance(fleet.getLocation(), target.getLocation()); 405// if (dist < target.getRadius() + fleet.getRadius() + 1000) { 406// if (targetHostile && !mathchesTarget) { 407// fleet.setFaction(target.getFaction().getId(), true); 408// } 409// } else { 410// if (!mathchesOrig) { 411// fleet.setFaction(origFaction, true); 412// } 413// } 414// } 415 } 416 } 417 418} 419 420 421 422 423 424 425 426 427 428