001package com.fs.starfarer.api.impl.campaign; 002 003import java.util.ArrayList; 004import java.util.HashSet; 005import java.util.List; 006import java.util.Random; 007import java.util.Set; 008 009import java.awt.Color; 010 011import org.apache.log4j.Logger; 012import org.lwjgl.util.vector.Vector2f; 013 014import com.fs.starfarer.api.EveryFrameScript; 015import com.fs.starfarer.api.Global; 016import com.fs.starfarer.api.campaign.BaseCampaignEventListener; 017import com.fs.starfarer.api.campaign.BattleAPI; 018import com.fs.starfarer.api.campaign.CampaignFleetAPI; 019import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 020import com.fs.starfarer.api.campaign.CargoAPI; 021import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType; 022import com.fs.starfarer.api.campaign.CargoStackAPI; 023import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI; 024import com.fs.starfarer.api.campaign.FactionAPI; 025import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode; 026import com.fs.starfarer.api.campaign.FactionProductionAPI; 027import com.fs.starfarer.api.campaign.FactionProductionAPI.ItemInProductionAPI; 028import com.fs.starfarer.api.campaign.FactionProductionAPI.ProductionItemType; 029import com.fs.starfarer.api.campaign.FleetInflater; 030import com.fs.starfarer.api.campaign.JumpPointAPI.JumpDestination; 031import com.fs.starfarer.api.campaign.LocationAPI; 032import com.fs.starfarer.api.campaign.PlanetAPI; 033import com.fs.starfarer.api.campaign.PlayerMarketTransaction; 034import com.fs.starfarer.api.campaign.PlayerMarketTransaction.ShipSaleInfo; 035import com.fs.starfarer.api.campaign.SectorAPI; 036import com.fs.starfarer.api.campaign.SectorEntityToken; 037import com.fs.starfarer.api.campaign.SpecialItemData; 038import com.fs.starfarer.api.campaign.SpecialItemPlugin; 039import com.fs.starfarer.api.campaign.StarSystemAPI; 040import com.fs.starfarer.api.campaign.comm.CommMessageAPI.MessageClickAction; 041import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 042import com.fs.starfarer.api.campaign.econ.Industry; 043import com.fs.starfarer.api.campaign.econ.MarketAPI; 044import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel; 045import com.fs.starfarer.api.campaign.econ.MonthlyReport; 046import com.fs.starfarer.api.campaign.econ.MonthlyReport.FDNode; 047import com.fs.starfarer.api.campaign.econ.SubmarketAPI; 048import com.fs.starfarer.api.campaign.listeners.ListenerUtil; 049import com.fs.starfarer.api.campaign.rules.MemoryAPI; 050import com.fs.starfarer.api.characters.AdminData; 051import com.fs.starfarer.api.characters.OfficerDataAPI; 052import com.fs.starfarer.api.characters.PersonAPI; 053import com.fs.starfarer.api.characters.SkillsChangeRemoveExcessOPEffect; 054import com.fs.starfarer.api.characters.SkillsChangeRemoveVentsCapsEffect; 055import com.fs.starfarer.api.combat.ShipVariantAPI; 056import com.fs.starfarer.api.fleet.FleetMemberAPI; 057import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin.DerelictShipData; 058import com.fs.starfarer.api.impl.campaign.econ.impl.InstallableItemEffect; 059import com.fs.starfarer.api.impl.campaign.econ.impl.ItemEffectsRepo; 060import com.fs.starfarer.api.impl.campaign.events.BaseEventPlugin.MarketFilter; 061import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflaterParams; 062import com.fs.starfarer.api.impl.campaign.fleets.RouteManager; 063import com.fs.starfarer.api.impl.campaign.ids.Commodities; 064import com.fs.starfarer.api.impl.campaign.ids.Drops; 065import com.fs.starfarer.api.impl.campaign.ids.Entities; 066import com.fs.starfarer.api.impl.campaign.ids.Factions; 067import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 068import com.fs.starfarer.api.impl.campaign.ids.Stats; 069import com.fs.starfarer.api.impl.campaign.ids.Tags; 070import com.fs.starfarer.api.impl.campaign.intel.MessageIntel; 071import com.fs.starfarer.api.impl.campaign.intel.misc.ProductionReportIntel; 072import com.fs.starfarer.api.impl.campaign.intel.misc.ProductionReportIntel.ProductionData; 073import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec.DropData; 074import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator; 075import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner; 076import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial; 077import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.PerShipData; 078import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.ShipCondition; 079import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.ShipRecoverySpecialData; 080import com.fs.starfarer.api.impl.campaign.shared.SharedData; 081import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin; 082import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldParams; 083import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldSource; 084import com.fs.starfarer.api.impl.campaign.tutorial.TutorialMissionIntel; 085import com.fs.starfarer.api.loading.FighterWingSpecAPI; 086import com.fs.starfarer.api.loading.HullModSpecAPI; 087import com.fs.starfarer.api.loading.WeaponSpecAPI; 088import com.fs.starfarer.api.util.IntervalUtil; 089import com.fs.starfarer.api.util.Misc; 090import com.fs.starfarer.api.util.WeightedRandomPicker; 091 092public class CoreScript extends BaseCampaignEventListener implements EveryFrameScript { 093 094 public static Logger log = Global.getLogger(CoreScript.class); 095 096 public static final String SHARED_DATA_KEY = "core_CEFSSharedDataKey"; 097 098 private SharedData shared; 099 100 private IntervalUtil timer = new IntervalUtil(0.5f, 1.5f); 101 //private Set<String> marketsWithAssignedPatrolScripts = new HashSet<String>(); 102 103 public CoreScript() { 104 super(true); 105// shared = new SharedData(); 106 shared = SharedData.getData(); 107 } 108 109 private boolean firstFrame = true; 110 public void advance(float amount) { 111 SectorAPI sector = Global.getSector(); 112 playRepChangeSoundsIfNeeded(); 113 114 if (sector.isPaused()) { 115 if (Global.getSettings().isDevMode()) { 116 //RouteManager.getInstance().advance(amount); 117 RouteManager.getInstance().advance(0f); 118 } 119 return; 120 } 121 122 if (firstFrame) { 123 firstFrame = false; 124 } 125 126 float days = sector.getClock().convertToDays(amount); 127 shared.advance(amount); 128 129 timer.advance(days); 130 if (timer.intervalElapsed()) { 131 } 132 133 RouteManager.getInstance().advance(amount); 134 135 Misc.computeCoreWorldsExtent(); 136 137 //updateSlipstreamVisibility(amount); 138 } 139 140// protected transient CampaignTerrainAPI currentStream = null; 141// public void updateSlipstreamVisibility(float amount) { 142// float sw = Global.getSettings().getFloat("sectorWidth"); 143// float sh = Global.getSettings().getFloat("sectorHeight"); 144// float minCellSize = 12000f; 145// float cellSize = Math.max(minCellSize, sw * 0.05f); 146// CollisionGridUtil grid = new CollisionGridUtil(-sw/2f, sw/2f, -sh/2f, sh/2f, cellSize); 147// Set<String> seenSystems = new LinkedHashSet<String>(); 148// for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 149// if (market.isHidden()) continue; 150// if (market.getContainingLocation() == null) continue; 151// if (!market.getContainingLocation().isHyperspace()) { 152// String systemId = market.getContainingLocation().getId(); 153// if (seenSystems.contains(systemId)) continue; 154// seenSystems.add(systemId); 155// } 156// //if (market.hasIndustry(Industries.SPACEPORT)) continue; 157// Industry spaceport = market.getIndustry(Industries.SPACEPORT); 158// if (spaceport == null || !spaceport.isFunctional()) continue; 159// 160// Vector2f loc = market.getLocationInHyperspace(); 161// float size = 10000f; 162// if (!market.getContainingLocation().hasTag(Tags.THEME_CORE)) { 163// size = 10000f; 164// } 165// //size = 200000; 166//// if (market.getName().equals("Tartessus")) { 167//// System.out.println("ewfwefe"); 168//// } 169// CustomStreamRevealer revealer = new CustomStreamRevealer(loc, size); 170// grid.addObject(revealer, loc, size * 2f, size * 2f); 171// } 172// 173// //System.out.println("BEGIN"); 174// float maxDist = 0f; 175// List<CampaignTerrainAPI> terrainList = Global.getSector().getHyperspace().getTerrainCopy(); 176// boolean processNext = false; 177// for (CampaignTerrainAPI terrain : terrainList) { 178// if (terrain.getPlugin() instanceof SlipstreamTerrainPlugin2) { 179// boolean process = false; 180// if (currentStream == null || processNext) { 181// process = true; 182// } else if (currentStream == terrain) { 183// processNext = true; 184// } 185// 186// if (!process) continue; 187// 188// currentStream = terrain; 189// if (terrainList.indexOf(terrain) == terrainList.size() - 1) { 190// currentStream = null; 191// } 192// //System.out.println("Processing: " + terrain.getId()); 193// SlipstreamTerrainPlugin2 stream = (SlipstreamTerrainPlugin2) terrain.getPlugin(); 194// for (SlipstreamSegment curr : stream.getSegments()) { 195// if (curr.discovered) continue; 196// Iterator<Object> iter = grid.getCheckIterator(curr.loc, curr.width / 2f, curr.width / 2f); 197// //Iterator<Object> iter = grid.getCheckIterator(curr.loc, 100f, 100f); 198// while (iter.hasNext()) { 199// Object obj = iter.next(); 200// if (obj instanceof CustomStreamRevealer) { 201// CustomStreamRevealer rev = (CustomStreamRevealer) obj; 202// Vector2f loc = rev.loc; 203// float radius = rev.radius; 204// 205// float dist = Misc.getDistance(loc, curr.loc); 206// if (dist > maxDist) { 207// maxDist = dist; 208//// if (dist >= 32500) { 209//// System.out.println("Rev loc: " + rev.loc); 210//// //grid.getCheckIterator(curr.loc, 100f, 100f); 211//// } 212// } 213// if (dist < radius) { 214// curr.discovered = true; 215// break; 216// } 217// } 218// } 219// } 220// break; 221// } 222// } 223// //System.out.println("Max dist: " + maxDist); 224// } 225 226 227 private void playRepChangeSoundsIfNeeded() { 228 if (deltaFaction != null) { 229 if (highestDelta > 0) { 230 Global.getSoundPlayer().playUISound("ui_rep_raise", 1, 1); 231 } else if (highestDelta < 0) { 232 Global.getSoundPlayer().playUISound("ui_rep_drop", 1, 1); 233 } 234 } 235 236 highestDelta = 0f; 237 deltaFaction = null; 238 } 239 240 241 private float highestDelta = 0f; 242 private String deltaFaction = null; 243 @Override 244 public void reportPlayerReputationChange(String faction, float delta) { 245 super.reportPlayerReputationChange(faction, delta); 246 if (Math.abs(delta) > Math.abs(highestDelta)) { 247 highestDelta = delta; 248 deltaFaction = faction; 249 } 250 } 251 252 253 @Override 254 public void reportPlayerReputationChange(PersonAPI person, float delta) { 255 super.reportPlayerReputationChange(person, delta); 256 if (Math.abs(delta) > Math.abs(highestDelta)) { 257 highestDelta = delta; 258 deltaFaction = person.getFaction().getId(); 259 } 260 } 261 262 263 public boolean isDone() { 264 return false; 265 } 266 267 public boolean runWhilePaused() { 268 return true; 269 } 270 271 272 @Override 273 public void reportPlayerMarketTransaction(PlayerMarketTransaction transaction) { 274 super.reportPlayerMarketTransaction(transaction); 275 276 for (ShipSaleInfo info : transaction.getShipsBought()) { 277 FleetMemberAPI member = info.getMember(); 278 if (!member.getVariant().hasTag(Tags.VARIANT_ALLOW_EXCESS_OP_ETC)){ 279 SkillsChangeRemoveExcessOPEffect.clampOP(member, Global.getSector().getPlayerStats()); 280 SkillsChangeRemoveVentsCapsEffect.clampNumVentsAndCaps(member, Global.getSector().getPlayerStats()); 281 } 282 } 283 284 285 286 SubmarketAPI submarket = transaction.getSubmarket(); 287 MarketAPI market = transaction.getMarket(); 288 289 if (!market.isPlayerOwned() && submarket.getPlugin().isParticipatesInEconomy() && 290 !submarket.getPlugin().isBlackMarket() && submarket.getFaction() == market.getFaction()) { 291 CargoAPI cargo = transaction.getSubmarket().getCargo(); 292 boolean didAnything = false; 293 OUTER: for (CargoStackAPI stack : transaction.getSold().getStacksCopy()) { 294 SpecialItemPlugin plugin = stack.getPlugin(); 295 if (plugin == null) continue; 296 297 SpecialItemData data = stack.getSpecialDataIfSpecial(); 298 299 InstallableItemEffect effect = ItemEffectsRepo.ITEM_EFFECTS.get(data.getId()); 300 for (Industry ind : market.getIndustries()) { 301 if (ind.wantsToUseSpecialItem(data)) { 302 if (effect != null) { 303 List<String> unmet = effect.getUnmetRequirements(ind); 304 if (unmet != null && !unmet.isEmpty()) { 305 continue; 306 } 307 } 308 309 if (ind.getSpecialItem() != null) { // upgrade, put item into cargo 310 cargo.addItems(CargoItemType.SPECIAL, ind.getSpecialItem(), 1); 311 } 312 cargo.removeItems(CargoItemType.SPECIAL, data, 1); 313 ind.setSpecialItem(data); 314 didAnything = true; 315 continue OUTER; 316 } 317 } 318 } 319 320 if (didAnything) { 321 cargo.sort(); 322 } 323 324 // not sure about doing this for major factions: 325 // - it can mess with their flavor, unintentionally if the player just sells blueprints 326 // - it can be a way to make their fleets weaker, by selling loads of blueprints for poor ships 327 //BlackMarketPlugin.delayedLearnBlueprintsFromTransaction(submarket.getFaction(), cargo, transaction); 328 } 329 330 //SharedData.getData().getPlayerActivityTracker().updateLastVisit(transaction.getMarket()); 331 332 // moved below code to BaseSubmarketPlugin 333// SubmarketAPI sub = transaction.getSubmarket(); 334// if (sub.getPlugin().isParticipatesInEconomy()) { 335// SharedData.getData().getPlayerActivityTracker().getPlayerTradeData(sub).addTransaction(transaction); 336// } 337 } 338 339 @Override 340 public void reportPlayerOpenedMarket(MarketAPI market) { 341 super.reportPlayerOpenedMarket(market); 342 SharedData.getData().getPlayerActivityTracker().updateLastVisit(market); 343 344 //new TempImmigrationModifier(market, 100, 200f, "Testing immigration mod"); 345 } 346 347 348 349 @Override 350 public void reportBattleOccurred(CampaignFleetAPI primaryWinner, BattleAPI battle) { 351 352 generateOrAddToDebrisFieldFromBattle(primaryWinner, battle); 353 354 if (!battle.isPlayerInvolved()) return; 355 356 357 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 358 if (!playerFleet.isValidPlayerFleet()) { 359 float fp = 0; 360 float crew = 0; 361 for (FleetMemberAPI member : Misc.getSnapshotMembersLost(playerFleet)) { 362 fp += member.getFleetPointCost(); 363 crew = member.getMinCrew(); 364 } 365 shared.setPlayerPreLosingBattleFP(fp); 366 shared.setPlayerPreLosingBattleCrew(crew); 367 shared.setPlayerLosingBattleTimestamp(Global.getSector().getClock().getTimestamp()); 368 } 369 370 371 for (final CampaignFleetAPI otherFleet : battle.getNonPlayerSideSnapshot()) { 372 if (otherFleet.hasScriptOfClass(TOffAlarm.class)) continue; 373 MemoryAPI memory = otherFleet.getMemoryWithoutUpdate(); 374 //if (!playerFleet.isTransponderOn()) { 375 //if (!memory.getBoolean(MemFlags.MEMORY_KEY_LOW_REP_IMPACT)) { 376 Misc.setFlagWithReason(memory, MemFlags.MEMORY_KEY_MAKE_HOSTILE_WHILE_TOFF, "battle", true, 7f + (float) Math.random() * 7f); 377 //} 378 //} 379 380 if (!otherFleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_LOW_REP_IMPACT) || 381 otherFleet.getMemoryWithoutUpdate().getBoolean(MemFlags.SPREAD_TOFF_HOSTILITY_IF_LOW_IMPACT)) { 382 otherFleet.addScript(new TOffAlarm(otherFleet)); 383 } 384 385 386 float fpLost = Misc.getSnapshotFPLost(otherFleet); 387 388 List<MarketAPI> markets = Misc.findNearbyLocalMarkets(otherFleet, 389 Global.getSettings().getFloat("sensorRangeMax") + 500f, 390 new MarketFilter() { 391 public boolean acceptMarket(MarketAPI market) { 392 //return market.getFaction().isAtWorst(otherFleet.getFaction(), RepLevel.COOPERATIVE); 393 return market.getFaction() != null && market.getFaction() == otherFleet.getFaction(); 394 } 395 }); 396 397 for (MarketAPI market : markets) { 398 MemoryAPI mem = market.getMemoryWithoutUpdate(); 399 float expire = fpLost; 400 if (mem.contains(MemFlags.MEMORY_KEY_PLAYER_HOSTILE_ACTIVITY_NEAR_MARKET)) { 401 expire += mem.getExpire(MemFlags.MEMORY_KEY_PLAYER_HOSTILE_ACTIVITY_NEAR_MARKET); 402 } 403 if (expire > 180) expire = 180; 404 if (expire > 0) { 405 mem.set(MemFlags.MEMORY_KEY_PLAYER_HOSTILE_ACTIVITY_NEAR_MARKET, true, expire); 406 } 407 } 408 } 409 410 Misc.getSimulatorPlugin().reportPlayerBattleOccurred(primaryWinner, battle); 411 } 412 413 414 @Override 415 public void reportFleetJumped(CampaignFleetAPI fleet, SectorEntityToken from, JumpDestination to) { 416 super.reportFleetJumped(fleet, from, to); 417 418 if (!fleet.isPlayerFleet()) return; 419 420 FactionAPI faction = Global.getSector().getPlayerFaction(); 421 Color color = faction.getBaseUIColor(); 422 Color dark = faction.getDarkUIColor(); 423 Color grid = faction.getGridUIColor(); 424 Color bright = faction.getBrightUIColor(); 425 426 if (fleet.getContainingLocation() instanceof StarSystemAPI) { 427 StarSystemAPI system = (StarSystemAPI) fleet.getContainingLocation(); 428 markSystemAsEntered(system, true); 429 } 430 } 431 432 public static void markSystemAsEntered(StarSystemAPI system, boolean withMessages) { 433 system.setEnteredByPlayer(true); 434 435 for (PlanetAPI planet : system.getPlanets()) { 436 if (planet.isStar()) continue; 437 438 MarketAPI market = planet.getMarket(); 439 if (market == null) continue; 440 if (market.getSurveyLevel() == SurveyLevel.NONE) { 441 market.setSurveyLevel(SurveyLevel.SEEN); 442 String type = planet.getSpec().getName(); 443 if (!planet.isGasGiant()) type += " World"; 444// Global.getSector().getCampaignUI().addMessage( 445// "New planet data: " + planet.getName() + ", " + type, 446// color); 447 448 449 if (withMessages) { 450// CommMessageAPI message = Global.getFactory().createMessage(); 451// message.setSubject("New planet data: " + planet.getName() + ", " + type); 452// message.setAction(MessageClickAction.INTEL_TAB); 453// message.setCustomData(planet); 454// message.setAddToIntelTab(false); 455// message.setSmallIcon(Global.getSettings().getSpriteName("intel_categories", "star_systems")); 456// Global.getSector().getCampaignUI().addMessage(message); 457 458 MessageIntel intel = new MessageIntel("New planet data: " + planet.getName() + ", " + type, 459 Misc.getBasePlayerColor());//, new String[] {"" + points}, Misc.getHighlightColor()); 460 intel.setIcon(Global.getSettings().getSpriteName("intel", "new_planet_info")); 461 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.INTEL_TAB, planet); 462 } 463 } 464 } 465 } 466 467 468 public static void addMiscToDropData(DropData data, FleetMemberAPI member, 469 boolean weapons, boolean mods, boolean fighters) { 470 ShipVariantAPI variant = member.getVariant(); 471 472 if (weapons) { 473 float p = Global.getSettings().getFloat("salvageWeaponProb"); 474 for (String slotId : variant.getNonBuiltInWeaponSlots()) { 475 String weaponId = variant.getWeaponId(slotId); 476 WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(weaponId); 477 if (spec.hasTag(Tags.NO_DROP)) continue; 478 data.addWeapon(weaponId, 1f * p); 479 } 480 } 481 482 if (mods) { 483 float p = Global.getSettings().getFloat("salvageHullmodProb"); 484 for (String id : member.getVariant().getHullMods()) { 485 HullModSpecAPI spec = Global.getSettings().getHullModSpec(id); 486 if (spec.isHidden() || spec.isHiddenEverywhere()) continue; 487 if (spec.hasTag(Tags.HULLMOD_NO_DROP)) continue; 488 data.addHullMod(id, 1f * p); 489 } 490 } 491 492 if (fighters) { 493 float p = Global.getSettings().getFloat("salvageWingProb"); 494 495 for (String id : member.getVariant().getFittedWings()) { 496 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(id); 497 if (spec.hasTag(Tags.WING_NO_DROP)) continue; 498 data.addFighterChip(id, 1f * p); 499 } 500 } 501 502 data.valueMult = Global.getSettings().getFloat("salvageDebrisFieldFraction"); 503 } 504 505 public static Set<String> getCargoCommodities(CargoAPI cargo) { 506 Set<String> result = new HashSet<String>(); 507 for (CargoStackAPI stack : cargo.getStacksCopy()) { 508 if (stack.isCommodityStack()) { 509 result.add(stack.getCommodityId()); 510 } 511 } 512 return result; 513 } 514 515 public static void generateOrAddToDebrisFieldFromBattle(CampaignFleetAPI primaryWinner, BattleAPI battle) { 516 517 if (primaryWinner == null) return; 518 LocationAPI location = primaryWinner.getContainingLocation(); 519 if (location == null) return; 520 521 //if (location.isHyperspace()) return; 522 523 boolean allowDebris = !location.isHyperspace(); 524 525 526 boolean playerInvolved = battle.isPlayerInvolved(); 527 528 DropData misc = new DropData(); 529 int miscChances = 0; 530 531 List<DropData> cargoList = new ArrayList<DropData>(); 532 533 WeightedRandomPicker<FleetMemberAPI> recoverySpecialChoices = new WeightedRandomPicker<FleetMemberAPI>(); 534 535 Vector2f com = new Vector2f(); 536 float count = 0f; 537 float fpDestroyed = 0; 538 for (CampaignFleetAPI fleet : battle.getSnapshotSideOne()) { 539 count++; 540 com.x += fleet.getLocation().x; 541 com.y += fleet.getLocation().y; 542 543 float fpForThisFleet = 0; 544 for (FleetMemberAPI loss : Misc.getSnapshotMembersLost(fleet)) { 545 //if (loss.getVariant().isStockVariant()) { 546 if (!loss.getVariant().hasTag(Tags.SHIP_RECOVERABLE)) { // was not recoverable by player 547 if (!loss.isStation() && !fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_NO_SHIP_RECOVERY)) { 548 recoverySpecialChoices.add(loss); 549 } 550 } 551 fpDestroyed += loss.getFleetPointCost(); 552 fpForThisFleet += loss.getFleetPointCost(); 553 if (allowDebris && !fleet.isPlayerFleet()) { 554 addMiscToDropData(misc, loss, true, true, true); 555 miscChances++; 556 } 557 } 558 if (allowDebris && !fleet.isPlayerFleet()) { 559 DropData cargo = new DropData(); 560 float cargoValue = fpForThisFleet * Global.getSettings().getFloat("salvageValuePerFP"); 561 cargoValue *= Global.getSettings().getFloat("salvageDebrisFieldFraction"); 562 if (cargoValue >= 1) { 563 for (String cid : getCargoCommodities(fleet.getCargo())) { 564 cargo.addCommodity(cid, 1f); 565 } 566 cargo.value = (int) cargoValue; 567 cargo.chances = 1; 568 cargoList.add(cargo); 569 } 570 } 571 } 572 for (CampaignFleetAPI fleet : battle.getSnapshotSideTwo()) { 573 count++; 574 com.x += fleet.getLocation().x; 575 com.y += fleet.getLocation().y; 576 577 float fpForThisFleet = 0; 578 for (FleetMemberAPI loss : Misc.getSnapshotMembersLost(fleet)) { 579 //if (loss.getVariant().isStockVariant()) { 580 if (!loss.getVariant().hasTag(Tags.SHIP_RECOVERABLE)) { // was not recoverable by player 581 if (!loss.isStation() && !fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_NO_SHIP_RECOVERY)) { 582 recoverySpecialChoices.add(loss); 583 } 584 } else { 585 loss.getVariant().removeTag(Tags.SHIP_RECOVERABLE); 586 } 587 588 fpDestroyed += loss.getFleetPointCost(); 589 fpForThisFleet += loss.getFleetPointCost(); 590 if (allowDebris && !fleet.isPlayerFleet()) { 591 addMiscToDropData(misc, loss, true, true, true); 592 miscChances++; 593 } 594 } 595 if (allowDebris && !fleet.isPlayerFleet()) { 596 DropData cargo = new DropData(); 597 float cargoValue = fpForThisFleet * Global.getSettings().getFloat("salvageValuePerFP"); 598 cargoValue *= Global.getSettings().getFloat("salvageDebrisFieldFraction"); 599 if (cargoValue >= 1) { 600 for (String cid : getCargoCommodities(fleet.getCargo())) { 601 cargo.addCommodity(cid, 1f); 602 } 603 cargo.value = (int) cargoValue; 604 cargo.chances = 1; 605 cargoList.add(cargo); 606 } 607 } 608 } 609 //Global.getSector().getPlayerFleet().getLocation() 610 if (count <= 0) return; 611 612 com.scale(1f / count); 613 614 // spawn some derelict ships, maybe. do this here, regardless of whether the value is enough to 615 // warrant a debris field 616 float numShips = recoverySpecialChoices.getItems().size(); 617 float chanceDerelict = 1f - 10f / (numShips + 10f); 618 //chanceNothing = 0f; 619 //Vector2f com = battle.computeCenterOfMass(); 620 621 // in a battle that involves the player, recoverable ships are non-stock variants 622 // (due to being prepared for recovery; dmods etc) and so don't show up as possible recovery choices here 623 // which is good! since 1) they could've been actually recovered, and 2) they already contributed to player salvage 624 // replaced this with Tags.SHIP_RECOVERABLE tag in recoverable variant for cleanness 625 int max = 3; 626 if (playerInvolved) { 627 max = 2; 628 chanceDerelict *= 0.25f; 629 } 630 for (int i = 0; i < max && !recoverySpecialChoices.isEmpty(); i++) { 631 boolean spawnShip = Math.random() < chanceDerelict; 632 if (spawnShip) { 633 FleetMemberAPI member = recoverySpecialChoices.pickAndRemove(); 634 String variantId = member.getVariant().getHullVariantId(); 635 if (!member.getVariant().isStockVariant()) variantId = member.getVariant().getOriginalVariant(); 636 if (variantId == null) continue; 637 DerelictShipData params = new DerelictShipData(new PerShipData(variantId, 638 DerelictShipEntityPlugin.pickBadCondition(null), 0f), false); 639 params.durationDays = DerelictShipEntityPlugin.getBaseDuration(member.getHullSpec().getHullSize()); 640 CustomCampaignEntityAPI entity = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity( 641 primaryWinner.getContainingLocation(), 642 Entities.WRECK, Factions.NEUTRAL, params); 643 entity.addTag(Tags.EXPIRES); 644 SalvageSpecialAssigner.assignSpecialForBattleWreck(entity); 645 646// entity.getLocation().x = com.x + (50f - (float) Math.random() * 100f); 647// entity.getLocation().y = com.y + (50f - (float) Math.random() * 100f); 648 649 float angle = (float) Math.random() * 360f; 650 float speed = 10f + 10f * (float) Math.random(); 651 Vector2f vel = Misc.getUnitVectorAtDegreeAngle(angle); 652 vel.scale(speed); 653 entity.getVelocity().set(vel); 654 655 entity.getLocation().x = com.x + vel.x * 3f; 656 entity.getLocation().y = com.y + vel.y * 3f; 657 } 658 } 659 660 661 662 float salvageValue = fpDestroyed * Global.getSettings().getFloat("salvageValuePerFP"); 663 if (Misc.isEasy()) { 664 salvageValue *= Global.getSettings().getFloat("easySalvageMult"); 665 } 666 667 668 salvageValue *= Global.getSettings().getFloat("salvageDebrisFieldFraction"); 669 float salvageXP = salvageValue * 0.1f; 670 671 float minForField = Global.getSettings().getFloat("minSalvageValueForDebrisField"); 672 //if (playerInvolved) minForField *= 6f; 673 if (playerInvolved) minForField = 2500f + (float) Math.random() * 1000f; 674 675 if (salvageValue < minForField || !allowDebris) return; 676 677 678 CampaignTerrainAPI debris = null; 679 for (CampaignTerrainAPI curr : primaryWinner.getContainingLocation().getTerrainCopy()) { 680 if (curr.getPlugin() instanceof DebrisFieldTerrainPlugin) { 681 DebrisFieldTerrainPlugin plugin = (DebrisFieldTerrainPlugin) curr.getPlugin(); 682 if (plugin.params.source == DebrisFieldSource.BATTLE && 683 plugin.params.density >= 1f && 684 plugin.containsPoint(com, 100f)) { 685 debris = curr; 686 break; 687 } 688 } 689 } 690 691 if (debris == null) { 692 DebrisFieldParams params = new DebrisFieldParams( 693 200f, // field radius - should not go above 1000 for performance reasons 694 -1f, // density, visual - affects number of debris pieces 695 1f, // duration in days 696 1f); // days the field will keep generating glowing pieces 697 params.source = DebrisFieldSource.BATTLE; 698 params.baseSalvageXP = (long) salvageXP; // base XP for scavenging in field 699 700 debris = (CampaignTerrainAPI) Misc.addDebrisField(location, params, null); 701 702 // makes the debris field always visible on map/sensors and not give any xp or notification on being discovered 703 //debris.setSensorProfile(null); 704 //debris.setDiscoverable(null); 705 706 // makes it discoverable and give 200 xp on being found 707 // sets the range at which it can be detected (as a sensor contact) to 2000 units 708 //debris.setDiscoverable(true); 709 //debris.setDiscoveryXP(200f); 710 //debris.setSensorProfile(1f); 711 //debris.getDetectedRangeMod().modifyFlat("gen", 2000); 712 713 debris.setDiscoverable(null); 714 debris.setDiscoveryXP(null); 715 //debris.setSensorProfile(1f); 716 //debris.getDetectedRangeMod().modifyFlat("gen", 1000); 717 718 debris.getLocation().set(com); 719 720 debris.getDropValue().clear(); 721 debris.getDropRandom().clear(); 722 } 723 724 725 DebrisFieldTerrainPlugin plugin = (DebrisFieldTerrainPlugin) debris.getPlugin(); 726 DropData basicDrop = null; 727 for (DropData data : debris.getDropValue()) { 728 if (Drops.BASIC.equals(data.group)) { 729 basicDrop = data; 730 break; 731 } 732 } 733 734 // since we're only adding to fields with density 1 (i.e. ones the player hasn't salvaged) 735 // no need to worry about how density would affect the salvage value of what we're adding 736 if (basicDrop == null) { 737 basicDrop = new DropData(); 738 basicDrop.group = Drops.BASIC; 739 debris.addDropValue(basicDrop); 740 } 741 basicDrop.value += salvageValue; 742 743 if (misc.getCustom() != null) { 744 misc.chances = miscChances; 745 746 float total = misc.getCustom().getTotal(); 747 if (total > 0) { 748 misc.addNothing(Math.max(1f, total)); 749 } 750 debris.addDropRandom(misc); 751 //misc.getCustom().print("MISC DROP"); 752 } 753 754 for (DropData cargo : cargoList) { 755 debris.addDropRandom(cargo); 756 } 757 758 759 //if (!battle.isPlayerInvolved()) { 760 ShipRecoverySpecialData data = ShipRecoverySpecial.getSpecialData(debris, null, true, false); 761 if (data != null && data.ships.size() < 3) { 762 float items = recoverySpecialChoices.getTotal(); 763 float total = items + 25f; 764 for (int i = 0; i < 3; i++) { 765 if ((float) Math.random() * total < items) { 766 FleetMemberAPI pick = recoverySpecialChoices.pick(); 767 if (pick != null) { 768 String variantId = pick.getVariant().getHullVariantId(); 769 if (!pick.getVariant().isStockVariant()) variantId = pick.getVariant().getOriginalVariant(); 770 data.addShip(variantId, ShipCondition.WRECKED, 0f); 771 } 772 } 773 } 774 } 775 //} 776 777// basicDrop = new DropData(); 778// basicDrop.group = "misc_test"; 779// basicDrop.value = 100000; 780// existing.addDropValue(basicDrop); 781 782 // resize and adjust duration here 783 784 float radius = 100f + (float) Math.min(900, Math.sqrt(basicDrop.value)); 785 float durationExtra = (float) Math.sqrt(salvageValue) * 0.1f; 786 787 float minDays = DebrisFieldTerrainPlugin.DISSIPATE_DAYS + 1f; 788 if (durationExtra < minDays) durationExtra = minDays; 789 790 float time = durationExtra + plugin.params.lastsDays; 791 if (time > 30f) time = 30f; 792 793 plugin.params.lastsDays = time; 794 plugin.params.glowsDays = time; 795 796 plugin.params.bandWidthInEngine = radius; 797 plugin.params.middleRadius = plugin.params.bandWidthInEngine / 2f; 798 799 float range = DebrisFieldTerrainPlugin.computeDetectionRange(plugin.params.bandWidthInEngine); 800 debris.getDetectedRangeMod().modifyFlat("gen", range); 801 } 802 803 804 805 806 @Override 807 public void reportPlayerDumpedCargo(CargoAPI cargo) { 808 super.reportPlayerDumpedCargo(cargo); 809 810 811 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 812 813 CustomCampaignEntityAPI pods = Misc.addCargoPods(playerFleet.getContainingLocation(), playerFleet.getLocation()); 814 pods.getCargo().addAll(cargo); 815 816 CargoPodsResponse script = new CargoPodsResponse(pods); 817 pods.getContainingLocation().addScript(script); 818 ListenerUtil.reportPlayerLeftCargoPods(pods); 819// pods.getMemoryWithoutUpdate().set(CargoPods.LOCKED, true); 820// pods.getMemoryWithoutUpdate().set(CargoPods.CAN_UNLOCK, true); 821 //pods.getMemoryWithoutUpdate().set(CargoPods.TRAPPED, true); 822 823 //pods.getDetectedRangeMod().modifyFlat("gen", 1000); 824 } 825 826 @Override 827 public void reportPlayerDidNotTakeCargo(CargoAPI cargo) { 828 super.reportPlayerDumpedCargo(cargo); 829 830 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 831 832 CustomCampaignEntityAPI pods = Misc.addCargoPods(playerFleet.getContainingLocation(), playerFleet.getLocation()); 833 pods.getCargo().addAll(cargo); 834 835 ListenerUtil.reportPlayerLeftCargoPods(pods); 836 } 837 838 protected Random prodRandom = new Random(); 839 public void doCustomProduction() { 840 FactionAPI pf = Global.getSector().getPlayerFaction(); 841 FactionProductionAPI prod = pf.getProduction(); 842 843 MarketAPI gatheringPoint = prod.getGatheringPoint(); 844 if (gatheringPoint == null) return; 845 846 //CargoAPI local = Misc.getLocalResourcesCargo(gatheringPoint); 847 CargoAPI local = Misc.getStorageCargo(gatheringPoint); 848 if (local == null) return; 849 850 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 851 MonthlyReport report = SharedData.getData().getCurrentReport(); 852 report.computeTotals(); 853 854 // limit production capacity by available credits - the net coming in this month plus what the player has 855 int total = (int) (report.getRoot().totalIncome - report.getRoot().totalUpkeep); 856 int credits = (int) playerFleet.getCargo().getCredits().get(); 857 credits += total; 858 if (credits < 0) credits = 0; 859 860 int capacity = prod.getMonthlyProductionCapacity(); 861 capacity = Math.min(capacity, credits); 862 //capacity = 1000000; 863 864 int remainingValue = capacity + prod.getAccruedProduction(); 865 //if (remainingValue <= 0) return; 866 867 868 869 //Random random = new Random(); 870 if (prodRandom == null) prodRandom = new Random(); 871 Random random = prodRandom; 872 873 WeightedRandomPicker<ItemInProductionAPI> picker = new WeightedRandomPicker<ItemInProductionAPI>(random); 874 for (ItemInProductionAPI item : prod.getCurrent()) { 875 if (item.getBuildDelay() > 0 && !Global.getSettings().isDevMode()) continue; 876 picker.add(item, item.getQuantity()); 877 } 878 int accrued = 0; 879 880 boolean wantedToDoProduction = !picker.isEmpty(); 881 boolean unableToDoProduction = capacity <= 0; 882 883 ProductionData data = new ProductionData(); 884 //CargoAPI cargo = Global.getFactory().createCargo(true); 885 //cargo.initMothballedShips(Factions.PLAYER); 886 CargoAPI cargo = data.getCargo("Heavy Industry - Custom Production"); 887 888 float quality = -1f; 889 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 890 if (!market.isPlayerOwned()) continue; 891 //quality = Math.max(quality, ShipQuality.getShipQuality(market, Factions.PLAYER)); 892 float currQuality = market.getStats().getDynamic().getMod(Stats.PRODUCTION_QUALITY_MOD).computeEffective(0f); 893 currQuality += market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).computeEffective(0f); 894 quality = Math.max(quality, currQuality); 895 } 896 quality -= Global.getSector().getFaction(Factions.PLAYER).getDoctrine().getShipQualityContribution(); 897 quality += 4f * Global.getSettings().getFloat("doctrineFleetQualityPerPoint"); 898 899 CampaignFleetAPI ships = Global.getFactory().createEmptyFleet(Factions.PLAYER, "temp", true); 900 ships.setCommander(Global.getSector().getPlayerPerson()); 901 DefaultFleetInflaterParams p = new DefaultFleetInflaterParams(); 902 p.quality = quality; 903 p.mode = ShipPickMode.PRIORITY_THEN_ALL; 904 p.persistent = false; 905 p.seed = random.nextLong(); 906 p.timestamp = null; 907 908 FleetInflater inflater = Misc.getInflater(ships, p); 909 ships.setInflater(inflater); 910 911 int totalCost = 0; 912 while (remainingValue > 0 && !picker.isEmpty()) { 913 ItemInProductionAPI pick = picker.pick(); 914 int baseCost = pick.getBaseCost(); 915 916 int count = Math.min(pick.getQuantity(), remainingValue / Math.max(1, baseCost)); 917 if (count > 0) { 918 count = random.nextInt(count) + 1; 919 } 920 if (count <= 0) { 921 accrued = remainingValue; 922 remainingValue = 0; 923 } else { 924 int currCost = count * baseCost; 925 totalCost += currCost; 926 remainingValue -= currCost; 927 928 if (pick.getType() == ProductionItemType.SHIP) { 929 List<String> variants = Global.getSettings().getHullIdToVariantListMap().get(pick.getSpecId()); 930 if (variants.isEmpty()) { 931 variants.add(pick.getSpecId() + "_Hull"); 932 continue; 933 } 934 935 int index = random.nextInt(variants.size()); 936 //cargo.addMothballedShip(FleetMemberType.SHIP, variants.get(index), null); 937 for (int i = 0; i < count; i++) { 938 ships.getFleetData().addFleetMember(variants.get(index)); 939 } 940 } else if (pick.getType() == ProductionItemType.FIGHTER) { 941 cargo.addFighters(pick.getSpecId(), count); 942 } else if (pick.getType() == ProductionItemType.WEAPON) { 943 cargo.addWeapons(pick.getSpecId(), count); 944 } 945 946 prod.removeItem(pick.getType(), pick.getSpecId(), count); 947 if (pick.getQuantity() <= 0) { 948 picker.remove(pick); 949 } 950 } 951 } 952 953 int weaponCost = 0; 954 ships.inflateIfNeeded(); 955 for (FleetMemberAPI member : ships.getFleetData().getMembersListCopy()) { 956 cargo.getMothballedShips().addFleetMember(member); 957 for (String wingId : member.getVariant().getNonBuiltInWings()) { 958 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(wingId); 959 weaponCost += spec.getBaseValue(); 960 } 961 for (String slotId : member.getVariant().getNonBuiltInWeaponSlots()) { 962 WeaponSpecAPI spec = member.getVariant().getWeaponSpec(slotId); 963 weaponCost += spec.getBaseValue(); 964 } 965 } 966 if (!DebugFlags.WEAPONS_HAVE_COST) { 967 weaponCost = 0; 968 } 969 970 // add some supplies, fuel, and crew 971 int addedValue = (int) (totalCost * Global.getSettings().getFloat("productionSuppliesBonusFraction")); 972 int sCost = (int) Global.getSettings().getCommoditySpec(Commodities.SUPPLIES).getBasePrice(); 973 int fCost = (int) Global.getSettings().getCommoditySpec(Commodities.FUEL).getBasePrice(); 974 int cCost = (int) Global.getSettings().getCommoditySpec(Commodities.CREW).getBasePrice(); 975 976 int supplies = (int) (addedValue * (0.5f * (0.5f + random.nextFloat() * 0.5f))) / sCost; 977 int fuel = (int) (addedValue * (0.3f * (0.5f + random.nextFloat() * 0.5f))) / fCost; 978 int crew = (addedValue - sCost * supplies - fCost * fuel) / cCost; 979 980 supplies = supplies / 10 * 10; 981 fuel = fuel / 10 * 10; 982 crew = crew / 10 * 10; 983 984 cargo.addSupplies(supplies); 985 cargo.addFuel(fuel); 986 cargo.addCrew(crew); 987 988 //accrued += remainingValue; // don't do that, only want to accrue when trying to produce something 989 990 totalCost -= prod.getAccruedProduction(); 991 totalCost += accrued; 992 if (totalCost < 0) totalCost = 0; 993 prod.setAccruedProduction(accrued); 994 995 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 996 if (!market.isPlayerOwned()) continue; 997 998 for (Industry ind : market.getIndustries()) { 999 Random curr = Misc.getRandom(random.nextLong(), 11); 1000 CargoAPI added = ind.generateCargoForGatheringPoint(curr); 1001 if (added != null && (!added.isEmpty() || 1002 (added.getMothballedShips() != null && !added.getMothballedShips().getMembersListCopy().isEmpty()))) { 1003 String title = ind.getCargoTitleForGatheringPoint(); 1004 data.getCargo(title).addAll(added, true); 1005 } 1006 } 1007 } 1008 1009 1010 // done with production 1011 1012 1013 1014 if (!data.isEmpty() || totalCost > 0 || (wantedToDoProduction && unableToDoProduction)) { 1015 if (totalCost > 0) { 1016 //MonthlyReport report = SharedData.getData().getCurrentReport(); 1017 1018 FDNode marketsNode = report.getNode(MonthlyReport.OUTPOSTS); 1019 if (marketsNode.name == null) { 1020 marketsNode.name = "Colonies"; 1021 marketsNode.custom = MonthlyReport.OUTPOSTS; 1022 marketsNode.tooltipCreator = report.getMonthlyReportTooltip(); 1023 } 1024 1025 FDNode production = report.getNode(marketsNode, MonthlyReport.PRODUCTION); 1026 production.name = "Custom production orders"; 1027 production.custom = MonthlyReport.PRODUCTION; 1028 production.custom2 = cargo; 1029 production.tooltipCreator = report.getMonthlyReportTooltip(); 1030 1031 production.upkeep += totalCost; 1032 1033 if (weaponCost > 0) { 1034 FDNode productionWeapons = report.getNode(marketsNode, MonthlyReport.PRODUCTION_WEAPONS); 1035 productionWeapons.name = "Weapons & fighter LPCs for produced ships"; 1036 productionWeapons.custom = MonthlyReport.PRODUCTION_WEAPONS; 1037 productionWeapons.tooltipCreator = report.getMonthlyReportTooltip(); 1038 productionWeapons.upkeep += weaponCost; 1039 } 1040 } 1041 1042 for (CargoAPI curr : data.data.values()) { 1043 local.addAll(curr); 1044 local.initMothballedShips(Factions.PLAYER); 1045 for (FleetMemberAPI member : curr.getMothballedShips().getMembersListCopy()) { 1046// member.getRepairTracker().setCR(0f); 1047// member.getRepairTracker().setMothballed(true); 1048 member.getRepairTracker().setMothballed(false); 1049 member.getRepairTracker().setCR(0.5f); 1050 local.getMothballedShips().addFleetMember(member); 1051 } 1052 } 1053 local.sort(); 1054 1055 ProductionReportIntel intel = new ProductionReportIntel(gatheringPoint, data, 1056 totalCost + weaponCost, prod.getAccruedProduction(), 1057 wantedToDoProduction && unableToDoProduction); 1058 Global.getSector().getIntelManager().addIntel(intel); 1059 } 1060 1061 } 1062 1063 1064 @Override 1065 public void reportEconomyMonthEnd() { 1066 super.reportEconomyMonthEnd(); 1067 1068 if (TutorialMissionIntel.isTutorialInProgress()) { 1069 return; 1070 } 1071 1072 MonthlyReport report = SharedData.getData().getCurrentReport(); 1073 FDNode marketsNode = report.getNode(MonthlyReport.OUTPOSTS); 1074 if (marketsNode.custom != null) { 1075 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 1076 if (!market.isPlayerOwned()) continue; 1077 1078 float incentive = market.getIncentiveCredits(); 1079 if (incentive > 0) { 1080 FDNode mNode = report.getNode(marketsNode, market.getId()); 1081 if (mNode.custom != null) { 1082 FDNode incNode = report.getNode(mNode, "incentives"); 1083 incNode.name = "Hazard pay"; 1084 incNode.custom = MonthlyReport.INCENTIVES; 1085 incNode.mapEntity = market.getPrimaryEntity(); 1086 incNode.tooltipCreator = report.getMonthlyReportTooltip(); 1087 incNode.upkeep += incentive; 1088 } 1089 market.setIncentiveCredits(0); 1090 } 1091 } 1092 } 1093 1094 1095 MonthlyReport previous = SharedData.getData().getPreviousReport(); 1096 float debt = previous.getDebt(); 1097 if (debt > 0) { 1098 MonthlyReport current = SharedData.getData().getCurrentReport(); 1099 current.getDebtNode().upkeep = debt; 1100 } 1101 1102 doCustomProduction(); 1103 1104 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 1105 1106 //MonthlyReport previous = SharedData.getData().getPreviousReport(); 1107 SharedData.getData().rollOverReport(); 1108 report = SharedData.getData().getPreviousReport(); 1109 report.setPreviousDebt(previous.getDebt()); 1110 report.setTimestamp(Global.getSector().getClock().getTimestamp()); 1111 1112 report.computeTotals(); 1113 int total = (int) (report.getRoot().totalIncome - report.getRoot().totalUpkeep); 1114 float credits = (int) playerFleet.getCargo().getCredits().get(); 1115 1116 float newCredits = credits + total; 1117 if (newCredits < 0) { 1118 report.setDebt((int) Math.abs(newCredits)); 1119 newCredits = 0; 1120 } 1121 playerFleet.getCargo().getCredits().set(newCredits); 1122 1123 1124 String totalStr = Misc.getDGSCredits(Math.abs(total)); 1125 //Global.getSector().getCampaignUI().showCoreUITab(CoreUITabId.OUTPOSTS); 1126 1127 String title = "Monthly income: " + totalStr; 1128 Color highlight = Misc.getHighlightColor(); 1129 if (total < 0) { 1130 title = "Monthly expenses: " + totalStr; 1131 highlight = Misc.getNegativeHighlightColor(); 1132 } 1133 1134 MessageIntel intel = new MessageIntel(title, 1135 Misc.getBasePlayerColor(), new String[] {totalStr}, highlight); 1136 intel.setIcon(Global.getSettings().getSpriteName("intel", "monthly_income_report")); 1137 1138 if (total >= 0) { 1139 intel.setSound("ui_intel_monthly_income_positive"); 1140 } else { 1141 intel.setSound("ui_intel_monthly_income_negative"); 1142 } 1143 1144 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.INCOME_TAB, Tags.INCOME_REPORT); 1145 1146 1147// CommMessageAPI message = FleetLog.beginEntry( 1148// title, 1149// playerFleet, 1150// highlight, 1151// totalStr 1152// ); 1153// 1154// message.setSmallIcon(Global.getSettings().getSpriteName("intel_categories", "bounties")); 1155// if (total >= 0) { 1156// message.getSection1().addPara("Over the last month, your fleet, outposts, and other ventures have" + 1157// " produced an income of " + totalStr + "."); 1158// } else { 1159// message.getSection1().addPara("Over the last month, your fleet, outposts, and other ventures have" + 1160// " produced expenses of " + totalStr + "."); 1161// } 1162// 1163// if (total < 0 && Math.abs(total) > credits) { 1164// message.getSection1().addPara("Your expenses have exceeded your credit balance. If this continues for more than a month, " + 1165// "some crew and officers may begin to leave, and other undertakings requiring credit expenditures may fail."); 1166// //message.getSection1().addPara("."); 1167// } 1168// message.getSection1().addPara("See the \"Income\" tab in the command screen for a detailed breakdown."); 1169// 1170// message.getSection1().setHighlights(totalStr); 1171// message.getSection1().setHighlightColors(highlight); 1172// 1173// message.setShowInCampaignList(true); 1174// message.setAddToIntelTab(true); 1175// message.setAction(MessageClickAction.INCOME_TAB); 1176// message.addTag(Tags.FLEET_LOG); 1177// message.addTag(Tags.INCOME_REPORT); 1178// 1179// Global.getSector().getCampaignUI().addMessage(message); 1180 } 1181 1182// private MonthlyReportNodeTooltipCreator monthlyReportTooltip; 1183// public static MonthlyReportNodeTooltipCreator getMonthlyReportTooltip() { 1184// if (monthlyReportTooltip == null) { 1185// monthlyReportTooltip = new MonthlyReportNodeTooltipCreator(); 1186// } 1187// return monthlyReportTooltip; 1188// } 1189 1190 1191 @Override 1192 public void reportEconomyTick(int iterIndex) { 1193 super.reportEconomyTick(iterIndex); 1194 1195 if (TutorialMissionIntel.isTutorialInProgress()) { 1196 return; 1197 } 1198 1199 //for (int i = 0; i < 100; i++) { 1200 int crewSalary = Global.getSettings().getInt("crewSalary"); 1201 int marineSalary = Global.getSettings().getInt("marineSalary"); 1202 1203 float numIter = Global.getSettings().getFloat("economyIterPerMonth"); 1204 float f = 1f / numIter; 1205 1206 //f = 1f; 1207 1208 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 1209 MonthlyReport report = SharedData.getData().getCurrentReport(); 1210 1211 1212 FDNode fleetNode = report.getNode(MonthlyReport.FLEET); 1213 fleetNode.name = "Fleet"; 1214 fleetNode.custom = MonthlyReport.FLEET; 1215 fleetNode.tooltipCreator = report.getMonthlyReportTooltip(); 1216 1217 int crewCost = playerFleet.getCargo().getCrew() * crewSalary; 1218 FDNode crewNode = report.getNode(fleetNode, MonthlyReport.CREW); 1219 crewNode.upkeep += crewCost * f; 1220 crewNode.name = "Crew payroll"; 1221 crewNode.custom = MonthlyReport.CREW; 1222 crewNode.tooltipCreator = report.getMonthlyReportTooltip(); 1223 1224 int marineCost = playerFleet.getCargo().getMarines() * marineSalary; 1225 if (marineSalary > 0) { 1226 FDNode marineNode = report.getNode(fleetNode, MonthlyReport.MARINES); 1227 marineNode.upkeep += marineCost * f; 1228 marineNode.name = "Marine payroll"; 1229 marineNode.custom = MonthlyReport.MARINES; 1230 marineNode.tooltipCreator = report.getMonthlyReportTooltip(); 1231 } 1232 1233 1234// List<PersonnelAtEntity> droppedOffMarines = PlayerFleetPersonnelTracker.getInstance().getDroppedOff(); 1235// for (PersonnelAtEntity curr : droppedOffMarines) { 1236// 1237// } 1238// for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 1239// if (Misc.playerHasStorageAccess(market)) { 1240// 1241// } 1242// } 1243 1244 1245 1246 //FDNode officersNode = report.getNode(MonthlyReport.OFFICERS); 1247 FDNode officersNode = report.getNode(fleetNode, MonthlyReport.OFFICERS); 1248 officersNode.name = "Officer payroll"; 1249 officersNode.custom = MonthlyReport.OFFICERS; 1250 officersNode.tooltipCreator = report.getMonthlyReportTooltip(); 1251 1252 1253 for (OfficerDataAPI od : playerFleet.getFleetData().getOfficersCopy()) { 1254 float salary = Misc.getOfficerSalary(od.getPerson()); 1255 FDNode oNode = report.getNode(officersNode, od.getPerson().getId()); 1256 oNode.name = od.getPerson().getName().getFullName(); 1257 oNode.upkeep += salary * f; 1258 oNode.custom = od; 1259 } 1260 1261 FDNode marketsNode = report.getNode(MonthlyReport.OUTPOSTS); 1262 marketsNode.name = "Colonies"; 1263 marketsNode.custom = MonthlyReport.OUTPOSTS; 1264 marketsNode.tooltipCreator = report.getMonthlyReportTooltip(); 1265 1266 FDNode storageNode = null; 1267// storageNode = report.getNode(MonthlyReport.STORAGE); 1268// storageNode.name = "Storage"; 1269// storageNode.custom = MonthlyReport.STORAGE; 1270// storageNode.tooltipCreator = report.getMonthlyReportTooltip(); 1271 1272 float storageFraction = Global.getSettings().getFloat("storageFreeFraction"); 1273 1274 int index = 0; 1275 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 1276// if (index % 10 != 3 || index == 0) { 1277// index++; 1278// continue; 1279// } 1280 1281 if (!market.isPlayerOwned() && Misc.playerHasStorageAccess(market)) { 1282 float vc = Misc.getStorageCargoValue(market); 1283 float vs = Misc.getStorageShipValue(market); 1284 1285 float fc = (int) (vc * storageFraction); 1286 float fs = (int) (vs * storageFraction); 1287 if (fc > 0 || fs > 0) { 1288 if (storageNode == null) { 1289 storageNode = report.getNode(MonthlyReport.STORAGE); 1290 storageNode.name = "Storage"; 1291 storageNode.custom = MonthlyReport.STORAGE; 1292 storageNode.tooltipCreator = report.getMonthlyReportTooltip(); 1293 } 1294 FDNode mNode = report.getNode(storageNode, market.getId()); 1295 String desc = ""; 1296 if (fc > 0 && fs > 0) { 1297 desc = "ships & cargo"; 1298 } else if (fc > 0) { 1299 desc = "cargo"; 1300 } else { 1301 desc = "ships"; 1302 } 1303 mNode.name = market.getName() + " (" + desc + ")"; 1304 mNode.custom = market; 1305 mNode.custom2 = MonthlyReport.STORAGE; 1306 //mNode.tooltipCreator = report.getMonthlyReportTooltip(); 1307 1308 mNode.upkeep += (fc + fs) * f; 1309 } 1310 continue; 1311 } 1312 1313 1314 //if (market.isHidden()) continue; 1315 //if (!Factions.DIKTAT.equals(market.getFaction().getId()) && !market.isPlayerOwned()) continue; 1316 if (!market.isPlayerOwned()) continue; 1317 1318 FDNode mNode = report.getNode(marketsNode, market.getId()); 1319 mNode.name = market.getName() + " (" + market.getSize() + ")"; 1320 mNode.custom = market; 1321 1322 FDNode indNode = report.getNode(mNode, "industries"); 1323 indNode.name = "Industries & structures"; 1324 indNode.custom = MonthlyReport.INDUSTRIES; 1325 indNode.mapEntity = market.getPrimaryEntity(); 1326 indNode.tooltipCreator = report.getMonthlyReportTooltip(); 1327// node.income += (int) market.getIndustryIncome(); 1328// node.upkeep += (int) market.getIndustryUpkeep(); 1329 1330 for (Industry curr : market.getIndustries()) { 1331 FDNode iNode = report.getNode(indNode, curr.getId()); 1332 iNode.name = curr.getCurrentName(); 1333 iNode.income += curr.getIncome().getModifiedInt() * f; 1334 iNode.upkeep += curr.getUpkeep().getModifiedInt() * f; 1335 iNode.custom = curr; 1336 iNode.mapEntity = market.getPrimaryEntity(); 1337 } 1338 1339 FDNode exportNode = report.getNode(mNode, "exports"); 1340 exportNode.name = "Exports"; 1341 exportNode.custom = MonthlyReport.EXPORTS; 1342 exportNode.mapEntity = market.getPrimaryEntity(); 1343 exportNode.tooltipCreator = report.getMonthlyReportTooltip(); 1344 1345 addExportsGroupedByCommodity(report, exportNode, market, f); 1346 //addExportsGroupedByFaction(report, exportNode, market, f); 1347 1348 1349// FDNode overheadNode = report.getNode(exportNode, "overhead"); 1350// if (overheadNode.name == null) { 1351// overheadNode.name = "Overhead"; 1352// overheadNode.icon = Global.getSettings().getSpriteName("income_report", "overhead"); 1353// overheadNode.custom = market; 1354// overheadNode.mapEntity = market.getPrimaryEntity(); 1355// overheadNode.tooltipCreator = new OverheadTooltipCreator(); 1356// } 1357// 1358// OverheadData overhead = computeOverhead(market); 1359// if (overhead.fraction > 0) { 1360// float totalIncome = market.getExportIncome(false); 1361// overheadNode.upkeep += totalIncome * overhead.fraction * f; 1362// } 1363 1364 index++; 1365 } 1366 //} 1367 1368 1369 FDNode adminNode = report.getNode(marketsNode, MonthlyReport.ADMIN); 1370 adminNode.name = "Administrators"; 1371 adminNode.custom = MonthlyReport.ADMIN; 1372 adminNode.tooltipCreator = report.getMonthlyReportTooltip(); 1373 1374 for (AdminData data : Global.getSector().getCharacterData().getAdmins()) { 1375 float salary = Misc.getAdminSalary(data.getPerson()); 1376 if (salary <= 0) continue; 1377 1378 FDNode aNode = report.getNode(adminNode, data.getPerson().getId()); 1379 aNode.name = data.getPerson().getName().getFullName(); 1380 if (data.getMarket() != null) { 1381 aNode.name += " (" + data.getMarket().getName() + ")"; 1382 } else { 1383 aNode.name += " (unassigned)"; 1384 salary *= Global.getSettings().getFloat("idleAdminSalaryMult"); 1385 } 1386 aNode.upkeep += salary * f; 1387 aNode.custom = data; 1388 } 1389 1390 //reportEconomyMonthEnd(); 1391 } 1392 1393// public static class OverheadData { 1394// public SupplierData max; 1395// public float fraction; 1396// } 1397// 1398// public static OverheadData computeOverhead(MarketAPI market) { 1399// OverheadData result = new OverheadData(); 1400// 1401// SupplierData max = null; 1402// float maxValue = 0; 1403// float total = 0f; 1404// List<CommodityOnMarketAPI> comList = market.getCommoditiesCopy(); 1405// for (CommodityOnMarketAPI com : comList) { 1406// for (SupplierData sd : com.getExports()) { 1407// int income = sd.getExportValue(market); 1408// if (income <= 0) continue; 1409// 1410// total += income; 1411// if (income > maxValue) { 1412// max = sd; 1413// maxValue = income; 1414// } 1415// } 1416// } 1417// 1418// if (max != null && maxValue > 0) { 1419// result.max = max; 1420// float units = total / maxValue; 1421// float mult = Misc.logOfBase(2f, units) + 1f; 1422// result.fraction = 1f - (mult / units); 1423// if (result.fraction < 0) result.fraction = 0; 1424// if (result.fraction > 0) { 1425// result.fraction = Math.round(result.fraction * 100f) / 100f; 1426// result.fraction = Math.max(result.fraction, 0.01f); 1427// } 1428// } 1429// return result; 1430// } 1431 1432 public static class ExportCommodityGroupData { 1433 public CommodityOnMarketAPI com; 1434 public int quantity; 1435 } 1436 1437 protected void addExportsGroupedByCommodity(MonthlyReport report, FDNode parent, MarketAPI market, float f) { 1438 for (CommodityOnMarketAPI com : market.getCommoditiesCopy()) { 1439 FDNode eNode = report.getNode(parent, com.getId()); 1440 eNode.name = com.getCommodity().getName(); 1441 eNode.income += com.getExportIncome() * f; 1442 eNode.custom = com; 1443 eNode.mapEntity = market.getPrimaryEntity(); 1444 } 1445 1446// List<FDNode> sorted = new ArrayList<FDNode>(parent.getChildren().values()); 1447// Collections.sort(sorted, new Comparator<FDNode>() { 1448// public int compare(FDNode o1, FDNode o2) { 1449// return o2.income - o1.income; 1450// } 1451// }); 1452// parent.getChildren().clear(); 1453// parent.getChildren(). 1454 } 1455 1456 1457 1458} 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476