001package com.fs.starfarer.api.impl.campaign.fleets; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.HashMap; 007import java.util.List; 008import java.util.Map; 009import java.util.Random; 010 011import org.apache.log4j.Logger; 012 013import com.fs.starfarer.api.Global; 014import com.fs.starfarer.api.campaign.BattleAPI; 015import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 016import com.fs.starfarer.api.campaign.CampaignFleetAPI; 017import com.fs.starfarer.api.campaign.StarSystemAPI; 018import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 019import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI; 020import com.fs.starfarer.api.campaign.econ.MarketAPI; 021import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 022import com.fs.starfarer.api.impl.campaign.command.WarSimScript; 023import com.fs.starfarer.api.impl.campaign.command.WarSimScript.LocationDanger; 024import com.fs.starfarer.api.impl.campaign.econ.ShippingDisruption; 025import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI.CargoQuantityData; 026import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI.EconomyRouteData; 027import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData; 028import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 029import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment; 030import com.fs.starfarer.api.impl.campaign.ids.Commodities; 031import com.fs.starfarer.api.impl.campaign.ids.Factions; 032import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 033import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 034import com.fs.starfarer.api.impl.campaign.intel.events.PiracyRespiteScript; 035import com.fs.starfarer.api.impl.campaign.intel.misc.TradeFleetDepartureIntel; 036import com.fs.starfarer.api.impl.campaign.rulecmd.KantaCMD; 037import com.fs.starfarer.api.impl.campaign.shared.SharedData; 038import com.fs.starfarer.api.util.Misc; 039import com.fs.starfarer.api.util.TimeoutTracker; 040import com.fs.starfarer.api.util.WeightedRandomPicker; 041 042public class EconomyFleetRouteManager extends BaseRouteFleetManager implements FleetEventListener { 043 044 public static final Integer ROUTE_SRC_LOAD = 1; 045 public static final Integer ROUTE_TRAVEL_DST = 2; 046 public static final Integer ROUTE_TRAVEL_WS = 3; 047 public static final Integer ROUTE_RESUPPLY_WS = 4; 048 public static final Integer ROUTE_DST_UNLOAD = 5; 049 public static final Integer ROUTE_DST_LOAD = 6; 050 public static final Integer ROUTE_TRAVEL_BACK_WS = 7; 051 public static final Integer ROUTE_RESUPPLY_BACK_WS = 8; 052 public static final Integer ROUTE_TRAVEL_SRC = 9; 053 public static final Integer ROUTE_SRC_UNLOAD = 10; 054 055 public static final String SOURCE_ID = "econ"; 056 public static Logger log = Global.getLogger(EconomyFleetRouteManager.class); 057 058 public static Map<LocationDanger, Float> DANGER_LOSS_PROB = new HashMap<LocationDanger, Float>(); 059 static { 060 DANGER_LOSS_PROB.put(LocationDanger.NONE, 0.01f); 061 DANGER_LOSS_PROB.put(LocationDanger.MINIMAL, 0.03f); 062 DANGER_LOSS_PROB.put(LocationDanger.LOW, 0.07f); 063 DANGER_LOSS_PROB.put(LocationDanger.MEDIUM, 0.1f); 064 DANGER_LOSS_PROB.put(LocationDanger.HIGH, 0.15f); 065 DANGER_LOSS_PROB.put(LocationDanger.EXTREME, 0.2f); 066 } 067 068 069 protected TimeoutTracker<String> recentlySentTradeFleet = new TimeoutTracker<String>(); 070 071 public EconomyFleetRouteManager() { 072 //super(1f, 14f); 073 super(0.2f, 0.3f); 074 } 075 076 protected Object readResolve() { 077 if (recentlySentTradeFleet == null) { 078 recentlySentTradeFleet = new TimeoutTracker<String>(); 079 } 080 return this; 081 } 082 083 @Override 084 public void advance(float amount) { 085 super.advance(amount); 086 087 float days = Global.getSector().getClock().convertToDays(amount); 088 recentlySentTradeFleet.advance(days); 089 090// MarketAPI from = pickSourceMarket(); 091// MarketAPI to = pickDestMarket(from); 092 } 093 094 protected String getRouteSourceId() { 095 return SOURCE_ID; 096 } 097 098 protected int getMaxFleets() { 099 int numMarkets = Global.getSector().getEconomy().getNumMarkets(); 100 int maxBasedOnMarkets = numMarkets * 2; 101 return Math.min(maxBasedOnMarkets, Global.getSettings().getInt("maxEconFleets")); 102 } 103 104 public static boolean ENEMY_STRENGTH_CHECK_EXCLUDE_PIRATES = false; 105 106 protected void addRouteFleetIfPossible() { 107 MarketAPI from = pickSourceMarket(); 108 MarketAPI to = pickDestMarket(from); 109 if (from != null && to != null) { 110 111 EconomyRouteData data = createData(from, to); 112 if (data == null) return; 113 114 log.info("Added trade fleet route from " + from.getName() + " to " + to.getName()); 115 116 Long seed = Misc.genRandomSeed(); 117 String id = getRouteSourceId(); 118 119 OptionalFleetData extra = new OptionalFleetData(from); 120 float tier = data.size; 121 float stability = from.getStabilityValue(); 122 String factionId = from.getFactionId(); 123 if (!from.getFaction().isHostileTo(Factions.INDEPENDENT) && 124 !to.getFaction().isHostileTo(Factions.INDEPENDENT)) { 125 if ((float) Math.random() * 10f > stability + tier) { 126 factionId = Factions.INDEPENDENT; 127 } 128 } 129 if (data.smuggling) { 130 factionId = Factions.INDEPENDENT; 131 } 132 extra.factionId = factionId; 133 134 RouteData route = RouteManager.getInstance().addRoute(id, from, seed, extra, this); 135 route.setCustom(data); 136 137 138 StarSystemAPI sysFrom = data.from.getStarSystem(); 139 StarSystemAPI sysTo = data.to.getStarSystem(); 140 141// if (sysFrom.getName().startsWith("Rama") || 142// sysTo.getName().startsWith("Rama")) { 143// System.out.println("32ff32f23"); 144// } 145 146 if (PiracyRespiteScript.playerHasPiracyRespite()) { 147 if (from.getFaction().isPlayerFaction() || to.getFaction().isPlayerFaction()) { 148 ENEMY_STRENGTH_CHECK_EXCLUDE_PIRATES = true; 149 } 150 } 151 152 LocationDanger dFrom = WarSimScript.getDangerFor(factionId, sysFrom); 153 LocationDanger dTo = WarSimScript.getDangerFor(factionId, sysTo); 154 155 ENEMY_STRENGTH_CHECK_EXCLUDE_PIRATES = false; 156 157 LocationDanger danger = dFrom.ordinal() > dTo.ordinal() ? dFrom : dTo; 158 159 if (sysFrom != null && sysFrom.isCurrentLocation()) { 160 // the player is in the from location, don't auto-lose the trade fleet 161 // let it get destroyed by actual fleets, if it does 162 danger = LocationDanger.NONE; 163 } 164 165// if (danger != LocationDanger.NONE) { 166// System.out.println("efwe234523fwe " + danger.name()); 167// dFrom = WarSimScript.getDangerFor(factionId, sysFrom); 168// dTo = WarSimScript.getDangerFor(factionId, sysTo); 169// } 170 float pLoss = DANGER_LOSS_PROB.get(danger); 171 if (data.smuggling) pLoss *= 0.5; 172 if ((float) Math.random() < pLoss) { 173 boolean returning = (float) Math.random() < 0.5f; 174 applyLostShipping(data, returning, true, true, true); 175 RouteManager.getInstance().removeRoute(route); 176 return; 177 } 178 179 180 //float distLY = Misc.getDistanceLY(from.getLocationInHyperspace(), to.getLocation()); 181 182 float orbitDays = 2f + (float) Math.random() * 3f; 183 //float endDays = 8f + (float) Math.random() * 3f; // longer since includes time from jump-point to source 184 185 //orbitDays = 1f; 186 orbitDays = data.size * (0.75f + (float) Math.random() * 0.5f); 187 188// endDays = 0.5f; 189// float totalTravelTime = orbitDays + endDays + travelDays * 2f; 190 191 //boolean inSystem = from.getContainingLocation() == to.getContainingLocation(); 192 193// WaystationBonus ws = Misc.getWaystation(from, to); 194// if (ws != null && !data.smuggling) { 195// route.addSegment(new RouteSegment(ROUTE_SRC_LOAD, orbitDays, from.getPrimaryEntity())); 196// route.addSegment(new RouteSegment(ROUTE_TRAVEL_WS, from.getPrimaryEntity(), ws.market.getPrimaryEntity())); 197// route.addSegment(new RouteSegment(ROUTE_RESUPPLY_WS, orbitDays, ws.market.getPrimaryEntity())); 198// route.addSegment(new RouteSegment(ROUTE_TRAVEL_DST, ws.market.getPrimaryEntity(), to.getPrimaryEntity())); 199// route.addSegment(new RouteSegment(ROUTE_DST_UNLOAD, orbitDays * 0.5f, to.getPrimaryEntity())); 200// route.addSegment(new RouteSegment(ROUTE_DST_LOAD, orbitDays * 0.5f, to.getPrimaryEntity())); 201// route.addSegment(new RouteSegment(ROUTE_TRAVEL_BACK_WS, to.getPrimaryEntity(), ws.market.getPrimaryEntity())); 202// route.addSegment(new RouteSegment(ROUTE_RESUPPLY_BACK_WS, orbitDays, ws.market.getPrimaryEntity())); 203// route.addSegment(new RouteSegment(ROUTE_TRAVEL_SRC, ws.market.getPrimaryEntity(), from.getPrimaryEntity())); 204// route.addSegment(new RouteSegment(ROUTE_SRC_UNLOAD, orbitDays, from.getPrimaryEntity())); 205// } else { 206 route.addSegment(new RouteSegment(ROUTE_SRC_LOAD, orbitDays, from.getPrimaryEntity())); 207 route.addSegment(new RouteSegment(ROUTE_TRAVEL_DST, from.getPrimaryEntity(), to.getPrimaryEntity())); 208 route.addSegment(new RouteSegment(ROUTE_DST_UNLOAD, orbitDays * 0.5f, to.getPrimaryEntity())); 209 route.addSegment(new RouteSegment(ROUTE_DST_LOAD, orbitDays * 0.5f, to.getPrimaryEntity())); 210 route.addSegment(new RouteSegment(ROUTE_TRAVEL_SRC, to.getPrimaryEntity(), from.getPrimaryEntity())); 211 route.addSegment(new RouteSegment(ROUTE_SRC_UNLOAD, orbitDays, from.getPrimaryEntity())); 212// } 213 214 setDelayAndSendMessage(route); 215 216 recentlySentTradeFleet.add(from.getId(), Global.getSettings().getFloat("minEconSpawnIntervalPerMarket")); 217 } 218 } 219 220 protected void setDelayAndSendMessage(RouteData route) { 221 EconomyRouteData data = (EconomyRouteData) route.getCustom(); 222 223 float delay = 0.1f; 224 delay = 10f; 225 if (data.size <= 4f) { 226 delay = 5f; 227 } else if (data.size <= 6f) { 228 delay = 10f; 229 } else { 230 delay = 15f; 231 } 232 delay *= 0.75f + (float) Math.random() * 0.5f; 233 delay = (int) delay; 234 route.setDelay(delay); 235 236 if (!Factions.PLAYER.equals(route.getFactionId())) { 237 // queues itself 238 new TradeFleetDepartureIntel(route); 239 } 240 } 241 242 243 244 245 public MarketAPI pickSourceMarket() { 246 //return Global.getSector().getEconomy().getMarket("jangala"); 247 //return Global.getSector().getEconomy().getMarket("sindria"); 248 //if (true) return Global.getSector().getEconomy().getMarket("chicomoztoc"); 249 250 WeightedRandomPicker<MarketAPI> markets = new WeightedRandomPicker<MarketAPI>(); 251 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 252 if (market.isHidden()) continue; 253 //float distLY = Misc.getDistanceToPlayerLY(market.getPrimaryEntity()); 254 if (!market.hasSpaceport()) continue; // markets w/o spaceports don't launch fleets 255 if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(market.getId())) continue; 256 257// if (market.getId().equals("volturn")) { 258// return market; 259// } 260 261 if (recentlySentTradeFleet.contains(market.getId())) continue; 262 263 markets.add(market, market.getSize()); 264 265// if (market.getName().toLowerCase().equals("jannow")) { 266// markets.add(market, 100000f); 267// } 268 } 269 return markets.pick(); 270 } 271 272 public MarketAPI pickDestMarket(MarketAPI from) { 273 //return Global.getSector().getEconomy().getMarket("asharu"); 274 //return Global.getSector().getEconomy().getMarket("chicomoztoc"); 275 //if (true) return Global.getSector().getEconomy().getMarket("sindria"); 276 if (from == null) return null; 277 278 WeightedRandomPicker<MarketAPI> markets = new WeightedRandomPicker<MarketAPI>(); 279 280// com.getCommodityMarketData().getMarkets() 281// for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 282// if ((from.getEconGroup() == null && market.getEconGroup() != null) || 283// (from.getEconGroup() != null && !from.getEconGroup().equals(market.getEconGroup()))) { 284// continue; 285// } 286// } 287 288 List<CommodityOnMarketAPI> relevant = new ArrayList<CommodityOnMarketAPI>(); 289 for (CommodityOnMarketAPI com : from.getAllCommodities()) { 290 if (com.isNonEcon()) continue; 291 292 int exported = Math.min(com.getAvailable(), com.getMaxSupply()); 293 int imported = Math.max(0, com.getMaxDemand() - exported); 294 if (imported > 0 || exported > 0) { 295 relevant.add(com); 296 } 297 } 298 299 for (MarketAPI market : Global.getSector().getEconomy().getMarketsInGroup(from.getEconGroup())) { 300 if (market.isHidden()) continue; 301 if (!market.hasSpaceport()) continue; // markets w/o spaceports don't launch fleets 302 if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(market.getId())) continue; 303 if (market == from) continue; 304 305 int shipping = Misc.getShippingCapacity(market, market.getFaction() == from.getFaction()); 306 if (shipping <= 0) continue; 307 308 float w = 0f; 309 for (CommodityOnMarketAPI com : relevant) { 310 int exported = Math.min(com.getAvailable(), com.getMaxSupply()); 311 exported = Math.min(exported, shipping); 312 int imported = Math.max(0, com.getMaxDemand() - exported); 313 imported = Math.min(imported, shipping); 314 315 CommodityOnMarketAPI other = market.getCommodityData(com.getId()); 316 exported = Math.min(exported, other.getMaxDemand() - other.getMaxSupply()); 317 if (exported < 0) exported = 0; 318 imported = Math.min(imported, Math.min(other.getAvailable(), other.getMaxSupply())); 319 320 w += imported; 321 w += exported; 322 } 323 324 if (from.getFaction().isHostileTo(market.getFaction())) { 325 w *= 0.25f; 326 } 327 markets.add(market, w); 328 } 329 330 return markets.pick(); 331 332// for (CommodityOnMarketAPI com : from.getCommoditiesCopy()) { 333// 334// SupplierData sd = com.getSupplier(); 335// if (sd != null) { 336// if (sd.getMarket() == from) continue; 337// if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(sd.getMarket().getId())) continue; 338// markets.add(sd.getMarket(), sd.getQuantity()); 339// } 340// for (SupplierData curr : com.getExports()) { 341// if (curr.getMarket() == from) continue; 342// if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(sd.getMarket().getId())) continue; 343// markets.add(curr.getMarket(), curr.getQuantity()); 344// } 345// } 346// return markets.pick(); 347 348 } 349 350 public static EconomyRouteData createData(MarketAPI from, MarketAPI to) { 351 EconomyRouteData smuggling = new EconomyRouteData(); 352 smuggling.from = from; 353 smuggling.to = to; 354 smuggling.smuggling = true; 355 356 EconomyRouteData legal = new EconomyRouteData(); 357 legal.from = from; 358 legal.to = to; 359 legal.smuggling = false; 360 361 float legalTotal = 0; 362 float smugglingTotal = 0; 363 364 365 List<CommodityOnMarketAPI> relevant = new ArrayList<CommodityOnMarketAPI>(); 366 for (CommodityOnMarketAPI com : from.getAllCommodities()) { 367 if (com.isNonEcon()) continue; 368 CommodityOnMarketAPI orig = com; 369 int exported = Math.min(com.getAvailable(), com.getMaxSupply()); 370 if (!com.getCommodity().isPrimary()) { 371 com = from.getCommodityData(com.getCommodity().getDemandClass()); 372 } 373 374 int imported = Math.max(0, com.getMaxDemand() - exported); 375 if (imported > 0 || exported > 0) { 376 relevant.add(orig); 377 } 378 } 379 380 int shipping = Misc.getShippingCapacity(from, to.getFaction() == from.getFaction()); 381 for (CommodityOnMarketAPI com : relevant) { 382 CommodityOnMarketAPI orig = com; 383 int exported = Math.min(com.getAvailable(), com.getMaxSupply()); 384 exported = Math.min(exported, shipping); 385 386 if (!com.getCommodity().isPrimary()) { 387 com = from.getCommodityData(com.getCommodity().getDemandClass()); 388 } 389 390 int imported = Math.max(0, com.getMaxDemand() - exported); 391 imported = Math.min(imported, shipping); 392 if (orig != com) imported = 0; 393 394 CommodityOnMarketAPI other = to.getCommodityData(com.getId()); 395 exported = Math.min(exported, other.getMaxDemand() - other.getMaxSupply()); 396 if (exported < 0) exported = 0; 397 imported = Math.min(imported, Math.min(other.getAvailable(), other.getMaxSupply())); 398 399 if (imported < 0) imported = 0; 400 401 if (imported <= 0 && exported <= 0) continue; 402 403 boolean illegal = com.getCommodityMarketData().getMarketShareData(from).isSourceIsIllegal() || 404 com.getCommodityMarketData().getMarketShareData(to).isSourceIsIllegal() || 405 from.getFaction().isHostileTo(to.getFaction()); 406 407 if (imported > exported) { 408 if (illegal) { 409 smuggling.addReturn(orig.getId(), imported); 410 smugglingTotal += imported; 411 } else { 412 legal.addReturn(orig.getId(), imported); 413 legalTotal += imported; 414 } 415 } else { 416 if (illegal) { 417 smuggling.addDeliver(orig.getId(), exported); 418 smugglingTotal += exported; 419 } else { 420 legal.addDeliver(orig.getId(), exported); 421 legalTotal += exported; 422 } 423 } 424 } 425 426 Comparator<CargoQuantityData> comp = new Comparator<CargoQuantityData>() { 427 public int compare(CargoQuantityData o1, CargoQuantityData o2) { 428 if (o1.getCommodity().isPersonnel() && !o2.getCommodity().isPersonnel()) { 429 return 1; 430 } 431 if (o2.getCommodity().isPersonnel() && !o1.getCommodity().isPersonnel()) { 432 return -1; 433 } 434 return o2.units - o1.units; 435 } 436 }; 437 Collections.sort(legal.cargoDeliver, comp); 438 Collections.sort(legal.cargoReturn, comp); 439 Collections.sort(smuggling.cargoDeliver, comp); 440 Collections.sort(smuggling.cargoReturn, comp); 441 442 if (smugglingTotal <= 0 && legalTotal <= 0) return null; 443 444 EconomyRouteData data = null; 445 if ((float) Math.random() * (smugglingTotal + legalTotal) < smugglingTotal) { 446 data = smuggling; 447 } else { 448 data = legal; 449 } 450 451 while (data.cargoDeliver.size() > 4) { 452 data.cargoDeliver.remove(4); 453 } 454 while (data.cargoReturn.size() > 4) { 455 data.cargoReturn.remove(4); 456 } 457 458// data.cargoDeliver = data.cargoDeliver.subList(0, Math.min(data.cargoDeliver.size(), 4)); 459// data.cargoReturn = data.cargoReturn.subList(0, Math.min(data.cargoReturn.size(), 4)); 460 461// data.cargoDeliver.clear(); 462// data.cargoReturn.clear(); 463// data.addDeliver(Commodities.SHIPS, 5); 464 465 float max = 0f; 466 for (CargoQuantityData curr : data.cargoDeliver) { 467 if (curr.units > max) max = curr.units; 468 } 469 for (CargoQuantityData curr : data.cargoReturn) { 470 if (curr.units > max) max = curr.units; 471 } 472 473 int types = Math.max(data.cargoDeliver.size(), data.cargoReturn.size()); 474 if (types >= 3) { 475 data.size++; 476 } 477 if (types >= 4) { 478 data.size++; 479 } 480 481 data.size = max; 482 483 484 return data; 485 } 486 487 488 489 public boolean shouldCancelRouteAfterDelayCheck(RouteData route) { 490 String factionId = route.getFactionId(); 491 492 boolean smuggling = false; 493 if (route.getCustom() instanceof EconomyRouteData) { 494 smuggling = ((EconomyRouteData) route.getCustom()).smuggling; 495 } 496 497 if (factionId != null && route.getMarket() != null && !smuggling && 498 route.getMarket().getFaction().isHostileTo(factionId)) { 499 return true; 500 } 501 return false; 502 } 503 504 505 public CampaignFleetAPI spawnFleet(RouteData route) { 506 Random random = new Random(); 507 if (route.getSeed() != null) { 508 random = new Random(route.getSeed()); 509 } 510 511 CampaignFleetAPI fleet = createTradeRouteFleet(route, random); 512 if (fleet == null) return null;; 513 514 //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_TRADE_FLEET, true); 515 516 if (KantaCMD.playerHasProtection() && route.custom instanceof EconomyRouteData) { 517 EconomyRouteData data = (EconomyRouteData) route.custom; 518 if (data.from != null && data.to != null) { 519 if (data.from.getFaction().isPlayerFaction() || data.to.getFaction().isPlayerFaction()) { 520 fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_IGNORED_BY_FACTION, Factions.PIRATES); 521 } 522 } 523 } 524 525 fleet.addEventListener(this); 526 527 fleet.addScript(new EconomyFleetAssignmentAI(fleet, route)); 528 return fleet; 529 } 530 531 532 public static String getFleetTypeIdForTier(float tier, boolean smuggling) { 533 String type = FleetTypes.TRADE; 534 if (tier <= 3) type = FleetTypes.TRADE_SMALL; 535 if (smuggling) { 536 type = FleetTypes.TRADE_SMUGGLER; 537 } 538 return type; 539 } 540 541 public static CampaignFleetAPI createTradeRouteFleet(RouteData route, Random random) { 542 EconomyRouteData data = (EconomyRouteData) route.getCustom(); 543 544 MarketAPI from = data.from; 545 MarketAPI to = data.to; 546 547// if (from.getId().equals("umbra")) { 548// System.out.println("wefwefw"); 549// } 550 551 float tier = data.size; 552 553 if (data.smuggling && tier > 4) { 554 tier = 4; 555 } 556 557// data.smuggling = false; 558// tier = 4; 559 560// float stability = from.getStabilityValue(); 561// String factionId = from.getFactionId(); 562// if (!from.getFaction().isHostileTo(Factions.INDEPENDENT) && 563// !to.getFaction().isHostileTo(Factions.INDEPENDENT)) { 564// if ((float) Math.random() * 10f > stability + tier) { 565// factionId = Factions.INDEPENDENT; 566// } 567// } 568// 569// if (data.smuggling) { 570// factionId = Factions.INDEPENDENT; 571// } 572 String factionId = route.getFactionId(); 573 574 float total = 0f; 575 float fuel = 0f; 576 float cargo = 0f; 577 float personnel = 0f; 578 //float ships = 0f; 579 580 List<CargoQuantityData> all = new ArrayList<CargoQuantityData>(); 581 all.addAll(data.cargoDeliver); 582 all.addAll(data.cargoReturn); 583 for (CargoQuantityData curr : all) { 584 CommoditySpecAPI spec = curr.getCommodity(); 585// if (spec.getId().equals(Commodities.SHIPS)) { 586// ships = Math.max(ships, curr.units); 587// } 588 if (spec.isMeta()) continue; 589 590 total += curr.units; 591 if (spec.hasTag(Commodities.TAG_PERSONNEL)) { 592 personnel += curr.units; 593 } else if (spec.getId().equals(Commodities.FUEL)) { 594 fuel += curr.units; 595 } else { 596 cargo += curr.units; 597 } 598 } 599 600// System.out.println("Ships: " + ships); 601 if (total < 1f) total = 1f; 602 603 float fuelFraction = fuel / total; 604 float personnelFraction = personnel / total; 605 float cargoFraction = cargo / total; 606 607// fuelFraction = 0.33f; 608// personnelFraction = 0.33f; 609// cargoFraction = 0.33f; 610 611 if (fuelFraction + personnelFraction + cargoFraction > 0) { 612 float mult = 1f / (fuelFraction + personnelFraction + cargoFraction); 613 fuelFraction *= mult; 614 personnelFraction *= mult; 615 cargoFraction *= mult; 616 } 617 618 log.info("Creating trade fleet of tier " + tier + " for market [" + from.getName() + "]"); 619 620 float stabilityFactor = 1f + from.getStabilityValue() / 20f; 621 622 float combat = Math.max(1f, tier * stabilityFactor * 0.5f) * 10f; 623 float freighter = tier * 2f * cargoFraction * 3f; 624 float tanker = tier * 2f * fuelFraction * 3f; 625 float transport = tier * 2f * personnelFraction * 3f; 626 float liner = 0f; 627 628 //float utility = 1f + tier * 0.25f; 629 float utility = 0f; 630 631 String type = getFleetTypeIdForTier(tier, data.smuggling); 632 if (data.smuggling) { 633 //combat *= 2f; 634 freighter *= 0.5f; 635 tanker *= 0.5f; 636 transport *= 0.5f; 637 liner *= 0.5f; 638 } 639 640 FleetParamsV3 params = new FleetParamsV3( 641 from, 642 null, // locInHyper 643 factionId, 644 route.getQualityOverride(), // qualityOverride 645 type, 646 combat, // combatPts 647 freighter, // freighterPts 648 tanker, // tankerPts 649 transport, // transportPts 650 liner, // linerPts 651 utility, // utilityPts 652 0f //-0.5f // qualityBonus 653 ); 654 params.timestamp = route.getTimestamp(); 655 params.onlyApplyFleetSizeToCombatShips = true; 656 params.maxShipSize = 3; 657 params.officerLevelBonus = -2; 658 params.officerNumberMult = 0.5f; 659 params.random = random; 660 CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params); 661 662 if (fleet == null || fleet.isEmpty()) return null; 663 664 if (Misc.isPirateFaction(fleet.getFaction())) { 665 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_FORCE_TRANSPONDER_OFF, true); 666 } 667 668 if (data.smuggling) { 669 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_SMUGGLER, true); 670 Misc.makeLowRepImpact(fleet, "smuggler"); 671 } else { 672 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_TRADE_FLEET, true); 673 } 674 675 //cargoCap, fuelCap, personnelCap; 676 data.cargoCap = fleet.getCargo().getMaxCapacity(); 677 data.fuelCap = fleet.getCargo().getMaxFuel(); 678 data.personnelCap = fleet.getCargo().getMaxPersonnel(); 679 680 //ShippingDisruption.getShippingDisruption(from).getShippingPenalty().addTemporaryModFlat(1f, "fwefwe", 1f); 681 682 return fleet; 683 684 } 685 686 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 687 // already reduced by losses taken 688 //System.out.println("Cargo: " + fleet.getCargo().getMaxCapacity()); 689 690 RouteData route = RouteManager.getInstance().getRoute(getRouteSourceId(), fleet); 691 if (route == null || !(route.getCustom() instanceof EconomyRouteData)) return; 692 693 if (route.isExpired()) return; 694 695 EconomyRouteData data = (EconomyRouteData) route.getCustom(); 696 697 float cargoCap = fleet.getCargo().getMaxCapacity(); 698 float fuelCap = fleet.getCargo().getMaxFuel(); 699 float personnelCap = fleet.getCargo().getMaxPersonnel(); 700 701 float lossFraction = 0.34f; 702 703 //boolean returning = route.getCurrentIndex() >= 3; 704 boolean returning = false; 705 if (route.getCurrent() != null && route.getCurrentSegmentId() >= ROUTE_DST_LOAD) { 706 returning = true; 707 } 708 709 // whether it lost enough carrying capacity to count as an economic loss 710 // of that commodity at destination markets 711 boolean lostCargo = data.cargoCap * lossFraction > cargoCap; 712 boolean lostFuel = data.fuelCap * lossFraction > fuelCap; 713 boolean lostPersonnel = data.personnelCap * lossFraction > personnelCap; 714 715 // set to 0f so that the loss doesn't happen multiple times for a commodity 716 if (lostCargo) data.cargoCap = 0f; 717 if (lostFuel) data.fuelCap = 0f; 718 if (lostPersonnel) data.personnelCap = 0f; 719 720 applyLostShipping(data, returning, lostCargo, lostFuel, lostPersonnel); 721 722 // if it's lost all 3 capacities, also trigger a general shipping capacity loss at market 723 boolean allThreeLost = true; 724 allThreeLost &= data.cargoCap <= 0f || lostCargo; 725 allThreeLost &= data.fuelCap <= 0f || lostFuel; 726 allThreeLost &= data.personnelCap <= 0f || lostPersonnel; 727// if (fullyLost) { 728 729 boolean applyAccessLoss = allThreeLost; 730 if (applyAccessLoss) { 731 ShippingDisruption.getDisruption(data.from).addShippingLost(data.size); 732 ShippingDisruption.getDisruption(data.from).notifyDisrupted(ShippingDisruption.ACCESS_LOSS_DURATION); 733 } 734 735 } 736 737 738 public static void applyLostShipping(EconomyRouteData data, boolean returning, boolean cargo, boolean fuel, boolean personnel) { 739 if (!cargo && !fuel && !personnel) return; 740 741 int penalty = 1; 742 int penalty2 = 2; 743 if (!returning) { 744 for (CargoQuantityData curr : data.cargoDeliver) { 745 CommodityOnMarketAPI com = data.to.getCommodityData(curr.cargo); 746 if (!fuel && com.isFuel()) continue; 747 if (!personnel && com.isPersonnel()) continue; 748 if (!cargo && !com.isFuel() && !com.isPersonnel()) continue; 749 750 com.getAvailableStat().addTemporaryModFlat( 751 ShippingDisruption.ACCESS_LOSS_DURATION, 752 ShippingDisruption.COMMODITY_LOSS_PREFIX + Misc.genUID(), "Recent incoming shipment lost", -penalty2); 753 754 ShippingDisruption.getDisruption(data.to).notifyDisrupted(ShippingDisruption.ACCESS_LOSS_DURATION); 755 } 756 } 757 for (CargoQuantityData curr : data.cargoReturn) { 758 CommodityOnMarketAPI com = data.from.getCommodityData(curr.cargo); 759 if (!fuel && com.isFuel()) continue; 760 if (!personnel && com.isPersonnel()) continue; 761 if (!cargo && !com.isFuel() && !com.isPersonnel()) continue; 762 763 com.getAvailableStat().addTemporaryModFlat( 764 ShippingDisruption.ACCESS_LOSS_DURATION, 765 ShippingDisruption.COMMODITY_LOSS_PREFIX + Misc.genUID(), "Recent incoming shipment lost", -penalty2); 766 767 ShippingDisruption.getDisruption(data.from).notifyDisrupted(ShippingDisruption.ACCESS_LOSS_DURATION); 768 } 769 } 770 771 772 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 773 774 } 775 776 public boolean shouldRepeat(RouteData route) { 777 return false; 778 } 779 780 public void reportAboutToBeDespawnedByRouteManager(RouteData route) { 781 782 } 783 784 785} 786 787 788 789 790 791 792