001package com.fs.starfarer.api.impl.campaign.intel.bases; 002 003import java.util.LinkedHashMap; 004import java.util.List; 005import java.util.Random; 006import java.util.Set; 007 008import java.awt.Color; 009 010import org.apache.log4j.Logger; 011import org.json.JSONException; 012import org.json.JSONObject; 013 014import com.fs.starfarer.api.EveryFrameScript; 015import com.fs.starfarer.api.Global; 016import com.fs.starfarer.api.campaign.BattleAPI; 017import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 018import com.fs.starfarer.api.campaign.CampaignFleetAPI; 019import com.fs.starfarer.api.campaign.FactionAPI; 020import com.fs.starfarer.api.campaign.PersonImportance; 021import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult; 022import com.fs.starfarer.api.campaign.SectorEntityToken; 023import com.fs.starfarer.api.campaign.StarSystemAPI; 024import com.fs.starfarer.api.campaign.TextPanelAPI; 025import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 026import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 027import com.fs.starfarer.api.campaign.econ.EconomyAPI.EconomyUpdateListener; 028import com.fs.starfarer.api.campaign.econ.Industry; 029import com.fs.starfarer.api.campaign.econ.MarketAPI; 030import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel; 031import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 032import com.fs.starfarer.api.campaign.listeners.ListenerUtil; 033import com.fs.starfarer.api.characters.PersonAPI; 034import com.fs.starfarer.api.combat.MutableStat.StatMod; 035import com.fs.starfarer.api.fleet.FleetMemberAPI; 036import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin; 037import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact; 038import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope; 039import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions; 040import com.fs.starfarer.api.impl.campaign.DebugFlags; 041import com.fs.starfarer.api.impl.campaign.ids.Conditions; 042import com.fs.starfarer.api.impl.campaign.ids.Entities; 043import com.fs.starfarer.api.impl.campaign.ids.Factions; 044import com.fs.starfarer.api.impl.campaign.ids.Industries; 045import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 046import com.fs.starfarer.api.impl.campaign.ids.Ranks; 047import com.fs.starfarer.api.impl.campaign.ids.Stats; 048import com.fs.starfarer.api.impl.campaign.ids.Submarkets; 049import com.fs.starfarer.api.impl.campaign.ids.Tags; 050import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 051import com.fs.starfarer.api.impl.campaign.intel.PersonBountyIntel.BountyResult; 052import com.fs.starfarer.api.impl.campaign.intel.PersonBountyIntel.BountyResultType; 053import com.fs.starfarer.api.impl.campaign.intel.bar.PortsideBarData; 054import com.fs.starfarer.api.impl.campaign.intel.bar.events.LuddicPathBaseBarEvent; 055import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel.BaseBountyData; 056import com.fs.starfarer.api.impl.campaign.intel.deciv.DecivTracker; 057import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel; 058import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel.RaidDelegate; 059import com.fs.starfarer.api.impl.campaign.intel.raid.RaidIntel.RaidStageStatus; 060import com.fs.starfarer.api.impl.campaign.procgen.MarkovNames; 061import com.fs.starfarer.api.impl.campaign.procgen.MarkovNames.MarkovNameResult; 062import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator; 063import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.AddedEntity; 064import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.EntityLocation; 065import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.LocationType; 066import com.fs.starfarer.api.ui.Alignment; 067import com.fs.starfarer.api.ui.SectorMapAPI; 068import com.fs.starfarer.api.ui.TooltipMakerAPI; 069import com.fs.starfarer.api.util.IntervalUtil; 070import com.fs.starfarer.api.util.Misc; 071import com.fs.starfarer.api.util.WeightedRandomPicker; 072 073public class LuddicPathBaseIntel extends BaseIntelPlugin implements EveryFrameScript, FleetEventListener, 074 EconomyUpdateListener, RaidDelegate { 075 076 public static final String PATHER_BASE_COMMANDER = "$patherBaseCommander"; 077 078 public static String MEM_FLAG = "$core_luddicPathBase"; 079 080 public static Object BOUNTY_EXPIRED_PARAM = new Object(); 081 public static Object DISCOVERED_PARAM = new Object(); 082 083 public static Logger log = Global.getLogger(LuddicPathBaseIntel.class); 084 085 protected PersonAPI baseCommander; 086 protected StarSystemAPI system; 087 protected MarketAPI market; 088 protected SectorEntityToken entity; 089 090 protected float elapsedDays = 0f; 091 protected float duration = 45f; 092 093 protected BaseBountyData bountyData = null; 094 095 protected IntervalUtil monthlyInterval = new IntervalUtil(20f, 40f); 096 protected int monthsNoBounty = 0; 097 098 protected boolean large = false; 099 100 protected Random random = new Random(); 101 102 public static LuddicPathBaseIntel getIntelFor(StarSystemAPI system) { 103 for (IntelInfoPlugin intel : Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class)) { 104 if (((LuddicPathBaseIntel)intel).getSystem() == system) { 105 return (LuddicPathBaseIntel) intel; 106 } 107 } 108 return null; 109 } 110 111 public static LuddicPathBaseIntel getIntelFor(MarketAPI market) { 112 for (IntelInfoPlugin p : Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class)) { 113 LuddicPathBaseIntel intel = (LuddicPathBaseIntel) p; 114 if (intel.getMarket() == market) return intel; 115 } 116 return null; 117 } 118 119 public LuddicPathBaseIntel(StarSystemAPI system, String factionId) { 120 this.system = system; 121 122 market = Global.getFactory().createMarket(Misc.genUID(), "Luddic Path Base", 3); 123 market.setSize(3); 124 market.setHidden(true); 125 market.getMemoryWithoutUpdate().set(MEM_FLAG, true); 126 market.getMemoryWithoutUpdate().set(MemFlags.HIDDEN_BASE_MEM_FLAG, true); 127 128 market.setFactionId(Factions.LUDDIC_PATH); 129 130 market.setSurveyLevel(SurveyLevel.FULL); 131 132 market.setFactionId(factionId); 133 market.addCondition(Conditions.POPULATION_3); 134 135 market.addIndustry(Industries.POPULATION); 136 market.addIndustry(Industries.SPACEPORT); 137 market.addIndustry(Industries.MILITARYBASE); 138 139 market.addSubmarket(Submarkets.SUBMARKET_OPEN); 140 market.addSubmarket(Submarkets.SUBMARKET_BLACK); 141 142 market.getTariff().modifyFlat("default_tariff", market.getFaction().getTariffFraction()); 143 144 LinkedHashMap<LocationType, Float> weights = new LinkedHashMap<LocationType, Float>(); 145 weights.put(LocationType.IN_ASTEROID_BELT, 10f); 146 weights.put(LocationType.IN_ASTEROID_FIELD, 10f); 147 weights.put(LocationType.IN_RING, 10f); 148 weights.put(LocationType.IN_SMALL_NEBULA, 10f); 149 weights.put(LocationType.GAS_GIANT_ORBIT, 10f); 150 weights.put(LocationType.PLANET_ORBIT, 10f); 151 WeightedRandomPicker<EntityLocation> locs = BaseThemeGenerator.getLocations(null, system, null, 100f, weights); 152 if (locs.isEmpty()) { 153 weights.clear(); 154 weights.put(LocationType.STAR_ORBIT, 10f); 155 weights.put(LocationType.OUTER_SYSTEM, 0.0001f); 156 locs = BaseThemeGenerator.getLocations(null, system, null, 100f, weights); 157 } 158 EntityLocation loc = locs.pick(); 159 160 if (loc == null) { 161 endImmediately(); 162 return; 163 } 164 165 AddedEntity added = BaseThemeGenerator.addNonSalvageEntity(system, loc, Entities.MAKESHIFT_STATION, factionId); 166 167 if (added == null || added.entity == null) { 168 endImmediately(); 169 return; 170 } 171 172 entity = added.entity; 173 174 175 String name = generateName(); 176 if (name == null) { 177 endImmediately(); 178 return; 179 } 180 181 market.setName(name); 182 entity.setName(name); 183 184 BaseThemeGenerator.convertOrbitWithSpin(entity, -5f); 185 186 market.setPrimaryEntity(entity); 187 entity.setMarket(market); 188 189 entity.setSensorProfile(1f); 190 entity.setDiscoverable(true); 191 entity.getDetectedRangeMod().modifyFlat("gen", 5000f); 192 193 market.setEconGroup(market.getId()); 194 market.getMemoryWithoutUpdate().set(DecivTracker.NO_DECIV_KEY, true); 195 196 market.reapplyIndustries(); 197 198 Global.getSector().getEconomy().addMarket(market, true); 199 200 baseCommander = market.getFaction().createRandomPerson(Misc.random); 201 baseCommander.setRankId(Ranks.SPACE_CAPTAIN); 202 baseCommander.setPostId(Ranks.POST_STATION_COMMANDER); 203 baseCommander.setImportanceAndVoice(PersonImportance.HIGH, Misc.random); 204 baseCommander.addTag(Tags.CONTACT_MILITARY); 205 //baseCommander.addTag(Tags.CONTACT_PATHER); // currently no missions for that 206 baseCommander.getMemoryWithoutUpdate().set(PATHER_BASE_COMMANDER, true); 207 baseCommander.setImportance(PersonImportance.VERY_HIGH); 208 market.getCommDirectory().addPerson(baseCommander); 209 210 Global.getSector().getIntelManager().addIntel(this, true); 211 if (!DebugFlags.PATHER_BASE_DEBUG) { 212 timestamp = null; 213 } 214 215 Global.getSector().getListenerManager().addListener(this); 216 Global.getSector().getEconomy().addUpdateListener(this); 217 218 large = random.nextFloat() > 0.5f; 219 updateStationIfNeeded(large); 220 221 PortsideBarData.getInstance().addEvent(new LuddicPathBaseBarEvent(this)); 222 223 log.info(String.format("Added luddic path base in [%s], isLarge: %s", system.getName(), "" + large)); 224 } 225 226 @Override 227 public boolean isHidden() { 228 //if (true) return false; 229 if (super.isHidden()) return true; 230 return timestamp == null; 231 } 232 233// public float getRaidFP() { 234// float base = getBaseRaidFP(); 235// return base * (0.75f + (float) Math.random() * 0.5f); 236// } 237// public float getBaseRaidFP() { 238// float base = 100f; 239// return base * (0.75f + (float) Math.random() * 0.5f); 240// } 241// 242 public void notifyRaidEnded(RaidIntel raid, RaidStageStatus status) { 243 if (status == RaidStageStatus.SUCCESS) { 244 } else { 245 } 246 } 247// 248// public void startRaid(StarSystemAPI target, float raidFP) { 249// boolean hasTargets = false; 250// for (MarketAPI curr : Misc.getMarketsInLocation(target)) { 251// if (curr.getFaction().isHostileTo(getFactionForUIColors())) { 252// hasTargets = true; 253// break; 254// } 255// } 256// 257// if (!hasTargets) return; 258// 259// RaidIntel raid = new RaidIntel(target, getFactionForUIColors(), this); 260// 261// //float raidFP = 1000; 262// float successMult = 0.75f; 263// 264// JumpPointAPI gather = null; 265// List<JumpPointAPI> points = system.getEntities(JumpPointAPI.class); 266// float min = Float.MAX_VALUE; 267// for (JumpPointAPI curr : points) { 268// float dist = Misc.getDistance(entity.getLocation(), curr.getLocation()); 269// if (dist < min) { 270// min = dist; 271// gather = curr; 272// } 273// } 274// 275// 276// PirateRaidAssembleStage assemble = new PirateRaidAssembleStage(raid, gather, this); 277// assemble.addSource(market); 278// assemble.setSpawnFP(raidFP); 279// assemble.setAbortFP(raidFP * successMult); 280// raid.addStage(assemble); 281// 282// 283// SectorEntityToken raidJump = RouteLocationCalculator.findJumpPointToUse(getFactionForUIColors(), target.getCenter()); 284// 285// TravelStage travel = new TravelStage(raid, gather, raidJump, false); 286// travel.setAbortFP(raidFP * successMult * successMult); 287// raid.addStage(travel); 288// 289// PirateRaidActionStage action = new PirateRaidActionStage(raid, target); 290// action.setAbortFP(raidFP * successMult * successMult * successMult); 291// raid.addStage(action); 292// 293// raid.addStage(new ReturnStage(raid)); 294// 295// if (!Misc.getMarketsInLocation(target, Factions.PLAYER).isEmpty()) { 296// Global.getSector().getIntelManager().addIntel(raid); 297// } else { 298// Global.getSector().getIntelManager().queueIntel(raid); 299// } 300// } 301 302 public StarSystemAPI getSystem() { 303 return system; 304 } 305 306 protected String pickStationType(boolean large) { 307 WeightedRandomPicker<String> stations = new WeightedRandomPicker<String>(); 308 309 //large = true; 310 311 try { 312 JSONObject json = getFactionForUIColors().getCustom().getJSONObject(Factions.CUSTOM_PATHER_BASES_SMALL); 313 if (large) json = getFactionForUIColors().getCustom().getJSONObject(Factions.CUSTOM_PATHER_BASES_LARGE); 314 for (String key : JSONObject.getNames(json)) { 315 stations.add(key, (float) json.optDouble(key, 0f)); 316 } 317 if (stations.isEmpty()) { 318 stations.add(Industries.ORBITALSTATION, 5f); 319 } 320 } catch (JSONException e) { 321 stations.clear(); 322 } 323 324 return stations.pick(); 325 } 326 327 protected Industry getStationIndustry() { 328 for (Industry curr : market.getIndustries()) { 329 if (curr.getSpec().hasTag(Industries.TAG_STATION)) { 330 return curr; 331 } 332 } 333 return null; 334 } 335 336 protected void updateStationIfNeeded(boolean large) { 337 Industry stationInd = getStationIndustry(); 338 339 String currIndId = null; 340 if (stationInd != null) { 341 currIndId = stationInd.getId(); 342 market.removeIndustry(stationInd.getId(), null, false); 343 stationInd = null; 344 } 345 346 if (currIndId == null) { 347 currIndId = pickStationType(large); 348 } 349 350 if (currIndId == null) return; 351 352 market.addIndustry(currIndId); 353 stationInd = getStationIndustry(); 354 if (stationInd == null) return; 355 356 stationInd.finishBuildingOrUpgrading(); 357 358 359 CampaignFleetAPI fleet = Misc.getStationFleet(entity); 360 if (fleet == null) return; 361 362 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy(); 363 if (members.size() < 1) return; 364 365 fleet.inflateIfNeeded(); 366 } 367 368 369 protected CampaignFleetAPI addedListenerTo = null; 370 @Override 371 protected void advanceImpl(float amount) { 372 float days = Global.getSector().getClock().convertToDays(amount); 373 //days *= 1000f; 374 //Global.getSector().getCurrentLocation().getName() 375 //entity.getContainingLocation().getName() 376 if (getPlayerVisibleTimestamp() == null && entity.isInCurrentLocation() && isHidden()) { 377 makeKnown(); 378 sendUpdateIfPlayerHasIntel(DISCOVERED_PARAM, false); 379 } 380 381 if (!sentBountyUpdate && bountyData != null && 382 (Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay() || 383 (!isHidden() && DebugFlags.SEND_UPDATES_WHEN_NO_COMM))) { 384 makeKnown(); 385 sendUpdateIfPlayerHasIntel(bountyData, false); 386 sentBountyUpdate = true; 387 } 388 389 CampaignFleetAPI fleet = Misc.getStationFleet(market); 390 if (fleet != null && addedListenerTo != fleet) { 391 if (addedListenerTo != null) { 392 addedListenerTo.removeEventListener(this); 393 } 394 fleet.addEventListener(this); 395 addedListenerTo = fleet; 396 } 397 398 monthlyInterval.advance(days); 399 if (monthlyInterval.intervalElapsed()) { 400 if (bountyData == null && random.nextFloat() < Math.min(0.3f, monthsNoBounty * 0.02f)) { 401 setBounty(); 402 } else { 403 monthsNoBounty++; 404 } 405 } 406 407// if (bountyData == null) { 408// setBounty(); 409// } 410 411 if (bountyData != null) { 412 boolean canEndBounty = !entity.isInCurrentLocation(); 413 bountyData.bountyElapsedDays += days; 414 if (bountyData.bountyElapsedDays > bountyData.bountyDuration && canEndBounty) { 415 endBounty(); 416 } 417 } 418 } 419 420 421 public void makeKnown() { 422 makeKnown(null); 423 } 424 public void makeKnown(TextPanelAPI text) { 425// entity.setDiscoverable(null); 426// entity.setSensorProfile(null); 427// entity.getDetectedRangeMod().unmodify("gen"); 428 429 if (getPlayerVisibleTimestamp() == null) { 430 Global.getSector().getIntelManager().removeIntel(this); 431 Global.getSector().getIntelManager().addIntel(this, text == null, text); 432 } 433 } 434 435 public float getTimeRemainingFraction() { 436 float f = 1f - elapsedDays / duration; 437 return f; 438 } 439 440 441 442 @Override 443 protected void notifyEnding() { 444 super.notifyEnding(); 445 log.info(String.format("Removing luddic path base at [%s]", system.getName())); 446 Global.getSector().getListenerManager().removeListener(this); 447 448 Global.getSector().getEconomy().removeMarket(market); 449 Global.getSector().getEconomy().removeUpdateListener(this); 450 Misc.removeRadioChatter(market); 451 market.advance(0f); 452 } 453 454 @Override 455 protected void notifyEnded() { 456 super.notifyEnded(); 457 } 458 459 460 461 protected BountyResult result = null; 462 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 463 if (isEnding()) return; 464 465 //CampaignFleetAPI station = Misc.getStationFleet(market); // null here since it's the skeleton station at this point 466 if (addedListenerTo != null && fleet == addedListenerTo) { 467 Misc.fadeAndExpire(entity); 468 endAfterDelay(); 469 470 result = new BountyResult(BountyResultType.END_OTHER, 0, null); 471 472 if (reason == FleetDespawnReason.DESTROYED_BY_BATTLE && 473 param instanceof BattleAPI) { 474 BattleAPI battle = (BattleAPI) param; 475 if (battle.isPlayerInvolved()) { 476 int payment = 0; 477 if (bountyData != null) { 478 payment = (int) (bountyData.baseBounty * battle.getPlayerInvolvementFraction()); 479 } 480 if (payment > 0) { 481 Global.getSector().getPlayerFleet().getCargo().getCredits().add(payment); 482 483 CustomRepImpact impact = new CustomRepImpact(); 484 impact.delta = bountyData.repChange * battle.getPlayerInvolvementFraction(); 485 if (impact.delta < 0.01f) impact.delta = 0.01f; 486 ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation( 487 new RepActionEnvelope(RepActions.CUSTOM, 488 impact, null, null, false, true), 489 bountyData.bountyFaction.getId()); 490 491 result = new BountyResult(BountyResultType.END_PLAYER_BOUNTY, payment, rep); 492 } else { 493 result = new BountyResult(BountyResultType.END_PLAYER_NO_REWARD, 0, null); 494 } 495 } 496 } 497 498 boolean sendUpdate = DebugFlags.SEND_UPDATES_WHEN_NO_COMM || 499 result.type != BountyResultType.END_OTHER || 500 Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay(); 501 sendUpdate = true; 502 if (sendUpdate) { 503 sendUpdateIfPlayerHasIntel(result, false); 504 } 505 506 for (LuddicPathCellsIntel cell : LuddicPathCellsIntel.getCellsForBase(this, false)) { 507 cell.makeSleeper(Global.getSettings().getFloat("patherCellDisruptionDuration")); 508 if (cell.getMarket().isPlayerOwned() || DebugFlags.PATHER_BASE_DEBUG) { 509 cell.sendUpdateIfPlayerHasIntel(LuddicPathCellsIntel.UPDATE_DISRUPTED, false); 510 } 511 ListenerUtil.reportCellDisrupted(cell); 512 } 513 514 PirateBaseManager.markRecentlyUsedForBase(system); 515 LuddicPathBaseManager.getInstance().incrDestroyed(); 516 517 } 518 } 519 520 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 521 522 } 523 524 public boolean runWhilePaused() { 525 return false; 526 } 527 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { 528 529 Color h = Misc.getHighlightColor(); 530 Color g = Misc.getGrayColor(); 531 float pad = 3f; 532 float opad = 10f; 533 534 float initPad = pad; 535 if (mode == ListInfoMode.IN_DESC) initPad = opad; 536 537 Color tc = getBulletColorForMode(mode); 538 539 bullet(info); 540 boolean isUpdate = getListInfoParam() != null; 541 542 543 if (bountyData != null && result == null) { 544 if (getListInfoParam() != BOUNTY_EXPIRED_PARAM) { 545 if (isUpdate || mode != ListInfoMode.IN_DESC) { 546 FactionAPI faction = bountyData.bountyFaction; 547 info.addPara("Bounty faction: " + faction.getDisplayName(), initPad, tc, 548 faction.getBaseUIColor(), faction.getDisplayName()); 549 initPad = 0f; 550 } 551 info.addPara("%s reward", initPad, tc, h, Misc.getDGSCredits(bountyData.baseBounty)); 552 addDays(info, "remaining", bountyData.bountyDuration - bountyData.bountyElapsedDays, tc); 553 } 554 } 555 556 if (result != null && bountyData != null) { 557 switch (result.type) { 558 case END_PLAYER_BOUNTY: 559 info.addPara("%s received", initPad, tc, h, Misc.getDGSCredits(result.payment)); 560 CoreReputationPlugin.addAdjustmentMessage(result.rep.delta, bountyData.bountyFaction, null, 561 null, null, info, tc, isUpdate, 0f); 562 break; 563 case END_TIME: 564 break; 565 case END_OTHER: 566 break; 567 568 } 569 } 570 571 unindent(info); 572 } 573 574 @Override 575 public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { 576 Color c = getTitleColor(mode); 577 info.addPara(getName(), c, 0f); 578 addBulletPoints(info, mode); 579 } 580 581 public String getSortString() { 582 if (getTagsForSort().contains(Tags.INTEL_FLEET_LOG) || getTagsForSort().contains(Tags.INTEL_EXPLORATION)) { 583 return getSortStringNewestFirst(); 584 } 585 586 String base = Misc.ucFirst(getFactionForUIColors().getPersonNamePrefix()); 587 return base + " Base"; 588 } 589 590 public String getName() { 591 String base = Misc.ucFirst(getFactionForUIColors().getPersonNamePrefix()); 592 593 if (getListInfoParam() == bountyData && bountyData != null) { 594 return base + " Base - Bounty Posted"; 595 } else if (getListInfoParam() == BOUNTY_EXPIRED_PARAM) { 596 return base + " Base - Bounty Expired"; 597 } 598 599 if (result != null) { 600 if (result.type == BountyResultType.END_PLAYER_BOUNTY) { 601 return base + " Base - Bounty Completed"; 602 } else if (result.type == BountyResultType.END_PLAYER_NO_REWARD) { 603 return base + " Base - Destroyed"; 604 } 605 } 606 607 String name = market.getName(); 608 if (isEnding()) { 609 //return "Base Abandoned - " + name; 610 return base + " Base - Abandoned"; 611 } 612 if (getListInfoParam() == DISCOVERED_PARAM) { 613 return base + " Base - Discovered"; 614 } 615 if (entity.isDiscoverable()) { 616 return base + " Base - Exact Location Unknown"; 617 } 618 return base + " Base - " + name; 619 } 620 621 @Override 622 public FactionAPI getFactionForUIColors() { 623 return market.getFaction(); 624 } 625 626 public String getSmallDescriptionTitle() { 627 return getName(); 628 } 629 630 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 631 632 Color h = Misc.getHighlightColor(); 633 Color g = Misc.getGrayColor(); 634 Color tc = Misc.getTextColor(); 635 float pad = 3f; 636 float opad = 10f; 637 638 FactionAPI faction = market.getFaction(); 639 640 info.addImage(faction.getLogo(), width, 128, opad); 641 642 String has = faction.getDisplayNameHasOrHave(); 643 644 info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " " + has + 645 " established a base in the " + 646 market.getContainingLocation().getNameWithLowercaseType() + ". " + 647 "The base serves to provide material support to active Pather cells on nearby colonies, enabling them " + 648 "to cause widespread damage and destruction.", 649 opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle()); 650 651 if (!entity.isDiscoverable()) { 652 if (large) { 653 info.addPara("It has extremely well-developed defensive capabilities " + 654 "and is protected by a large number of fleets.", opad); 655 } else { 656 info.addPara("It has well-developed defensive capabilities " + 657 "and is protected by a large number of fleets.", opad); 658 } 659 } else { 660 info.addPara("You have not yet discovered the exact location or capabilities of this base.", opad); 661 } 662 info.addSectionHeading("Recent events", 663 faction.getBaseUIColor(), faction.getDarkUIColor(), Alignment.MID, opad); 664 665 666 List<LuddicPathCellsIntel> cells = LuddicPathCellsIntel.getCellsForBase(this, false); 667 if (!cells.isEmpty()) { 668 float initPad = opad; 669 670 info.addPara("This base is known to be providing support to active Pather cells at the following colonies:", opad); 671 for (LuddicPathCellsIntel intel : cells) { 672 addMarketToList(info, intel.getMarket(), initPad, tc); 673 initPad = 0f; 674 } 675 initPad = 0f; 676 } else { 677 info.addPara("You do not know of any active pather cells this base might be providing support to.", opad); 678 } 679 680 681 if (bountyData != null) { 682 info.addPara(Misc.ucFirst(bountyData.bountyFaction.getDisplayNameWithArticle()) + " " + 683 bountyData.bountyFaction.getDisplayNameHasOrHave() + 684 " posted a bounty for the destruction of this base.", 685 opad, bountyData.bountyFaction.getBaseUIColor(), 686 bountyData.bountyFaction.getDisplayNameWithArticleWithoutArticle()); 687 688 if (result != null && result.type == BountyResultType.END_PLAYER_BOUNTY) { 689 info.addPara("You have successfully completed this bounty.", opad); 690 } 691 692 addBulletPoints(info, ListInfoMode.IN_DESC); 693 } 694 695 if (result != null) { 696 if (result.type == BountyResultType.END_PLAYER_NO_REWARD) { 697 info.addPara("You have destroyed this base.", opad); 698 } else if (result.type == BountyResultType.END_OTHER) { 699 info.addPara("It is rumored that this base is no longer operational.", opad); 700 } 701 } 702 703 } 704 705 public String getIcon() { 706 return Global.getSettings().getSpriteName("intel", "pather_base"); 707 //return market.getFaction().getCrest(); 708 } 709 710 public Set<String> getIntelTags(SectorMapAPI map) { 711 Set<String> tags = super.getIntelTags(map); 712 if (bountyData != null) { 713 tags.add(Tags.INTEL_BOUNTY); 714 } 715 716 tags.add(Tags.INTEL_EXPLORATION); 717 718// if (target != null && !Misc.getMarketsInLocation(target, Factions.PLAYER).isEmpty()) { 719// tags.add(Tags.INTEL_COLONIES); 720// } 721 722 for (LuddicPathCellsIntel cell : LuddicPathCellsIntel.getCellsForBase(this, true)) { 723 if (cell.getMarket().isPlayerOwned() && !cell.isSleeper()) { 724 tags.add(Tags.INTEL_COLONIES); 725 break; 726 } 727 } 728 729 tags.add(market.getFactionId()); 730 if (bountyData != null) { 731 tags.add(bountyData.bountyFaction.getId()); 732 } 733 return tags; 734 } 735 736 @Override 737 public SectorEntityToken getMapLocation(SectorMapAPI map) { 738 if (market.getPrimaryEntity().isDiscoverable()) { 739 return system.getCenter(); 740 } 741 return market.getPrimaryEntity(); 742 } 743 744 745 746 protected String generateName() { 747 MarkovNames.loadIfNeeded(); 748 749 MarkovNameResult gen = null; 750 for (int i = 0; i < 10; i++) { 751 gen = MarkovNames.generate(null); 752 if (gen != null) { 753 String test = gen.name; 754 if (test.toLowerCase().startsWith("the ")) continue; 755 String p = pickPostfix(); 756 if (p != null && !p.isEmpty()) { 757 test += " " + p; 758 } 759 if (test.length() > 22) continue; 760 761 return test; 762 } 763 } 764 return null; 765 } 766 767 private String pickPostfix() { 768 WeightedRandomPicker<String> post = new WeightedRandomPicker<String>(); 769 post.add("Asylum"); 770 //post.add("Base"); -> otherwise intel title can look like this: "Luddic Path Base: Scrimshaw Base" 771 post.add("Citadel"); 772 post.add("Hammer"); 773 post.add("Harbor"); 774 post.add("Haven"); 775 post.add("Hold"); 776 post.add("Locus"); 777 post.add("Nexus"); 778 post.add("Refuge"); 779 post.add("Sanctuary"); 780 post.add("Sanctum"); 781 post.add("Shadow"); 782 post.add("Shelter"); 783 post.add("Safehold"); 784 post.add("Terminus"); 785 post.add("Principle"); 786 post.add("Offering"); 787 post.add("Devotion"); 788 post.add("Atonement"); 789 post.add("Cleansing"); 790 post.add("Oblation"); 791 post.add("Sacrement"); 792 return post.pick(); 793 } 794 795 public void commodityUpdated(String commodityId) { 796 CommodityOnMarketAPI com = market.getCommodityData(commodityId); 797 int curr = 0; 798 String modId = market.getId(); 799 StatMod mod = com.getAvailableStat().getFlatStatMod(modId); 800 if (mod != null) { 801 curr = Math.round(mod.value); 802 } 803 804 int avWithoutPenalties = (int) Math.round(com.getAvailableStat().getBaseValue()); 805 for (StatMod m : com.getAvailableStat().getFlatMods().values()) { 806 if (m.value < 0) continue; 807 avWithoutPenalties += (int) Math.round(m.value); 808 } 809 810 int a = com.getAvailable() - curr; 811 a = avWithoutPenalties - curr; 812 int d = com.getMaxDemand(); 813 if (d > a) { 814 //int supply = Math.max(1, d - a - 1); 815 int supply = Math.max(1, d - a); 816 com.getAvailableStat().modifyFlat(modId, supply, "Brought in by smugglers"); 817 } 818 } 819 820 public void economyUpdated() { 821 float qualityBonus = 0f; 822 int light = 0; 823 int medium = 0; 824 int heavy = 0; 825 826 if (large) { 827 qualityBonus = 0.5f; 828 light = 4; 829 medium = 4; 830 heavy = 3; 831 } else { 832 qualityBonus = 0f; 833 light = 3; 834 medium = 2; 835 heavy = 1; 836 } 837 838 market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD). 839 modifyFlatAlways(market.getId(), qualityBonus, 840 "Development level"); 841 842 float fleetSizeBonus = 0.5f; 843 if (large) fleetSizeBonus = 1f; 844 market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).modifyFlatAlways(market.getId(), 845 fleetSizeBonus, 846 "Development level"); 847 848 String modId = market.getId(); 849 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_LIGHT_MOD).modifyFlat(modId, light); 850 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_MEDIUM_MOD).modifyFlat(modId, medium); 851 market.getStats().getDynamic().getMod(Stats.PATROL_NUM_HEAVY_MOD).modifyFlat(modId, heavy); 852 } 853 854 public boolean isEconomyListenerExpired() { 855 return isEnded(); 856 } 857 858 public MarketAPI getMarket() { 859 return market; 860 } 861 862 863 protected void setBounty() { 864 865 List<IntelInfoPlugin> bases = Global.getSector().getIntelManager().getIntel(LuddicPathBaseIntel.class); 866 for (IntelInfoPlugin curr : bases) { 867 LuddicPathBaseIntel intel = (LuddicPathBaseIntel) curr; 868 if (intel != this && intel.bountyData != null) { 869 return; 870 } 871 } 872 873 bountyData = new BaseBountyData(); 874 float base = 100000f; 875 if (large) { 876 base = Global.getSettings().getFloat("luddicPathBaseBountyLarge"); 877 bountyData.repChange = 0.05f; 878 } else { 879 base = Global.getSettings().getFloat("luddicPathBaseBountySmall"); 880 bountyData.repChange = 0.1f; 881 } 882 883 bountyData.baseBounty = base * (0.9f + (float) Math.random() * 0.2f); 884 bountyData.baseBounty = (int)(bountyData.baseBounty / 10000) * 10000; 885 886 887 WeightedRandomPicker<FactionAPI> picker = new WeightedRandomPicker<FactionAPI>(); 888 for (LuddicPathCellsIntel cell : LuddicPathCellsIntel.getCellsForBase(this, false)) { 889 FactionAPI faction = cell.getMarket().getFaction(); 890 //if (faction.isPlayerFaction()) continue; 891 picker.add(faction, (float) Math.pow(2f, cell.getMarket().getSize())); 892 } 893 894 FactionAPI faction = picker.pick(); 895 // player faction is in picker to reduce bounties offered on cells that are already bothering the player 896 if (faction == null || faction.isPlayerFaction()) { 897 bountyData = null; 898 return; 899 } 900 901 bountyData.bountyFaction = faction; 902 bountyData.bountyDuration = 180f; 903 bountyData.bountyElapsedDays = 0f; 904 905 monthsNoBounty = 0; 906 Misc.makeImportant(entity, "baseBounty"); 907 908 909// makeKnown(); 910// sendUpdateIfPlayerHasIntel(bountyData, false); 911 sentBountyUpdate = false; 912 } 913 914 protected boolean sentBountyUpdate = false; 915 protected void endBounty() { 916 sendUpdateIfPlayerHasIntel(BOUNTY_EXPIRED_PARAM, false); 917 bountyData = null; 918 monthsNoBounty = 0; 919 Misc.makeUnimportant(entity, "baseBounty"); 920 sentBountyUpdate = false; 921 } 922 923 924 public List<ArrowData> getArrowData(SectorMapAPI map) { 925 return null; 926 } 927 928 public SectorEntityToken getEntity() { 929 return entity; 930 } 931 932 public boolean isLarge() { 933 return large; 934 } 935 936 public PersonAPI getBaseCommander() { 937 return baseCommander; 938 } 939 public void setBaseCommander(PersonAPI baseCommander) { 940 this.baseCommander = baseCommander; 941 } 942} 943 944 945 946 947 948 949 950 951 952 953 954