001package com.fs.starfarer.api.impl.campaign.intel; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.List; 006import java.util.Random; 007import java.util.Set; 008 009import org.apache.log4j.Logger; 010 011import com.fs.starfarer.api.EveryFrameScript; 012import com.fs.starfarer.api.Global; 013import com.fs.starfarer.api.campaign.BattleAPI; 014import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 015import com.fs.starfarer.api.campaign.CampaignFleetAPI; 016import com.fs.starfarer.api.campaign.FactionAPI; 017import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode; 018import com.fs.starfarer.api.campaign.FleetAssignment; 019import com.fs.starfarer.api.campaign.LocationAPI; 020import com.fs.starfarer.api.campaign.PlanetAPI; 021import com.fs.starfarer.api.campaign.RepLevel; 022import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult; 023import com.fs.starfarer.api.campaign.SectorEntityToken; 024import com.fs.starfarer.api.campaign.StarSystemAPI; 025import com.fs.starfarer.api.campaign.econ.MarketAPI; 026import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 027import com.fs.starfarer.api.characters.FullName.Gender; 028import com.fs.starfarer.api.characters.PersonAPI; 029import com.fs.starfarer.api.fleet.FleetMemberAPI; 030import com.fs.starfarer.api.fleet.FleetMemberType; 031import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin; 032import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope; 033import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions; 034import com.fs.starfarer.api.impl.campaign.DebugFlags; 035import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent; 036import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3; 037import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3; 038import com.fs.starfarer.api.impl.campaign.ids.Factions; 039import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 040import com.fs.starfarer.api.impl.campaign.ids.Industries; 041import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 042import com.fs.starfarer.api.impl.campaign.ids.Ranks; 043import com.fs.starfarer.api.impl.campaign.ids.Tags; 044import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseManager; 045import com.fs.starfarer.api.impl.campaign.procgen.Constellation; 046import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BreadcrumbSpecial; 047import com.fs.starfarer.api.impl.campaign.shared.PersonBountyEventData; 048import com.fs.starfarer.api.impl.campaign.shared.SharedData; 049import com.fs.starfarer.api.ui.SectorMapAPI; 050import com.fs.starfarer.api.ui.TooltipMakerAPI; 051import com.fs.starfarer.api.util.Misc; 052import com.fs.starfarer.api.util.WeightedRandomPicker; 053 054public class PersonBountyIntel extends BaseIntelPlugin implements EveryFrameScript, FleetEventListener { 055 public static Logger log = Global.getLogger(PersonBountyIntel.class); 056 057 public static enum BountyType { 058 PIRATE, 059 DESERTER, 060 } 061 062 public static float MAX_DURATION = 90f; 063 064 //public static int FAST_START_LEVEL_INCREASE = 1; 065 public static float MAX_TIME_BASED_ADDED_LEVEL = 3; 066 067 private float elapsedDays = 0f; 068 private float duration = MAX_DURATION; 069 private float bountyCredits = 0; 070 071 private FactionAPI faction; 072 private PersonAPI person; 073 private CampaignFleetAPI fleet; 074 private FleetMemberAPI flagship; 075 076 private BountyType bountyType; 077 //private FleetType fleetType; 078 079 private SectorEntityToken hideoutLocation = null; 080 081 private int level = 0; 082 083 public float getElapsedDays() { 084 return elapsedDays; 085 } 086 087 public void setElapsedDays(float elapsedDays) { 088 this.elapsedDays = elapsedDays; 089 } 090 091 public static PersonBountyEventData getSharedData() { 092 return SharedData.getData().getPersonBountyEventData(); 093 } 094 095 public PersonBountyIntel() { 096 pickLevel(); 097 098 pickFaction(); 099 if (isDone()) return; 100 101 initBountyAmount(); 102 103 pickHideoutLocation(); 104 if (isDone()) return; 105 106 pickBountyType(); 107 if (bountyType == BountyType.DESERTER) { 108 bountyCredits *= 1.5f; 109 } 110 111 initPerson(); 112 if (isDone()) return; 113 114 spawnFleet(); 115 if (isDone()) return; 116 117 log.info(String.format("Starting person bounty by faction [%s] for person %s", faction.getDisplayName(), person.getName().getFullName())); 118 119 Global.getSector().getIntelManager().queueIntel(this); 120 } 121 122 public void reportMadeVisibleToPlayer() { 123 if (!isEnding() && !isEnded()) { 124 duration = Math.max(duration * 0.5f, Math.min(duration * 2f, MAX_DURATION)); 125 } 126 } 127 128 protected void pickLevel() { 129// if (true) { 130// level = 10; 131// //level = Misc.random.nextInt(11); 132// return; 133// } 134 135 int base = getSharedData().getLevel(); 136 137// if (Misc.isFastStart()) { 138 float timeFactor = (PirateBaseManager.getInstance().getDaysSinceStart() - 180f) / (365f * 2f); 139 if (timeFactor < 0) timeFactor = 0; 140 if (timeFactor > 1) timeFactor = 1; 141 142 int add = (int) Math.round(MAX_TIME_BASED_ADDED_LEVEL * timeFactor); 143 base += add; 144 //base += FAST_START_LEVEL_INCREASE; 145// } 146 147// if (Global.getSector().getPlayerFleet() != null) { 148// int playerLevel = Global.getSector().getPlayerFleet().getCommander().getStats().getLevel(); 149// base = Math.max(base, (playerLevel - 20) / 5); 150// } 151 152 if (base > 10) base = 10; 153 154 boolean hasLow = false; 155 boolean hasHigh = false; 156 for (EveryFrameScript s : PersonBountyManager.getInstance().getActive()) { 157 PersonBountyIntel bounty = (PersonBountyIntel) s; 158 159 int curr = bounty.getLevel(); 160 161 if (curr < base || curr == 0) hasLow = true; 162 if (curr > base) hasHigh = true; 163 } 164 165 level = base; 166 if (!hasLow) { 167 //level -= new Random().nextInt(2) + 1; 168 level = 0; 169 } else if (!hasHigh) { 170 level += new Random().nextInt(3) + 2; 171 } 172 173 if (level < 0) level = 0; 174 } 175 176 public int getLevel() { 177 return level; 178 } 179 180 protected void pickHideoutLocation() { 181 WeightedRandomPicker<StarSystemAPI> systemPicker = new WeightedRandomPicker<StarSystemAPI>(); 182 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 183 float mult = 0f; 184 185 if (system.hasPulsar()) continue; 186 187 if (system.hasTag(Tags.THEME_MISC_SKIP)) { 188 mult = 1f; 189 } else if (system.hasTag(Tags.THEME_MISC)) { 190 mult = 3f; 191 } else if (system.hasTag(Tags.THEME_REMNANT_NO_FLEETS)) { 192 mult = 3f; 193 } else if (system.hasTag(Tags.THEME_RUINS)) { 194 mult = 5f; 195 } else if (system.hasTag(Tags.THEME_REMNANT_DESTROYED)) { 196 mult = 3f; 197 } else if (system.hasTag(Tags.THEME_CORE_UNPOPULATED)) { 198 mult = 1f; 199 } 200 201 for (MarketAPI market : Misc.getMarketsInLocation(system)) { 202 if (market.isHidden()) continue; 203 mult = 0f; 204 break; 205 } 206 207 float distToPlayer = Misc.getDistanceToPlayerLY(system.getLocation()); 208 float noSpawnRange = Global.getSettings().getFloat("personBountyNoSpawnRangeAroundPlayerLY"); 209 if (distToPlayer < noSpawnRange) mult = 0f; 210 211 if (mult <= 0) continue; 212 213 float weight = system.getPlanets().size(); 214 for (PlanetAPI planet : system.getPlanets()) { 215 if (planet.isStar()) continue; 216 if (planet.getMarket() != null) { 217 float h = planet.getMarket().getHazardValue(); 218 if (h <= 0f) weight += 5f; 219 else if (h <= 0.25f) weight += 3f; 220 else if (h <= 0.5f) weight += 1f; 221 } 222 } 223 224 float dist = system.getLocation().length(); 225 float distMult = Math.max(0, 50000f - dist); 226 227 systemPicker.add(system, weight * mult * distMult); 228 } 229 230 StarSystemAPI system = systemPicker.pick(); 231 232 if (system != null) { 233 WeightedRandomPicker<SectorEntityToken> picker = new WeightedRandomPicker<SectorEntityToken>(); 234 for (SectorEntityToken planet : system.getPlanets()) { 235 if (planet.isStar()) continue; 236 if (planet.getMarket() != null && 237 !planet.getMarket().isPlanetConditionMarketOnly()) continue; 238 239 picker.add(planet); 240 } 241 hideoutLocation = picker.pick(); 242 } 243 244 245 if (hideoutLocation == null) { 246 endImmediately(); 247 } 248 } 249 250 251 252 private void pickFaction() { 253 FactionAPI player = Global.getSector().getPlayerFaction(); 254 255 String commFacId = Misc.getCommissionFactionId(); 256 boolean forceCommissionFaction = true; 257 if (commFacId != null && getSharedData().isParticipating(commFacId)) { 258 for (EveryFrameScript s : PersonBountyManager.getInstance().getActive()) { 259 PersonBountyIntel bounty = (PersonBountyIntel) s; 260 if (bounty.faction != null && bounty.faction.getId().equals(commFacId)) { 261 forceCommissionFaction = false; 262 } 263 } 264 } else { 265 forceCommissionFaction = false; 266 } 267 268 WeightedRandomPicker<MarketAPI> picker = new WeightedRandomPicker<MarketAPI>(); 269 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 270 if (!getSharedData().isParticipating(market.getFactionId())) continue; 271 if (market.getSize() < 3) continue; 272 if (market.isHidden()) continue; 273 if (market.getFaction().isPlayerFaction()) continue; 274 275 float weight = market.getSize(); 276 if (market.hasIndustry(Industries.PATROLHQ)) weight *= 1.5f; 277 if (market.hasIndustry(Industries.MILITARYBASE)) weight *= 3f; 278 if (market.hasIndustry(Industries.HIGHCOMMAND)) weight *= 5f; 279 280 if (market.getFaction() != null) { 281 if (forceCommissionFaction && !market.getFaction().getId().equals(commFacId)) { 282 continue; 283 } 284 285 if (market.getFaction().isHostileTo(player)) { 286 weight *= 0.5f; 287 } else { 288 // turned off to vary bounties a bit more 289 //float rel = market.getFaction().getRelToPlayer().getRel(); 290 //weight *= 1f + rel; // (0.5 to 2], given that it's not hostile if we're here 291 } 292 } 293 294 if (weight > 0) { 295 picker.add(market, weight); 296 } 297 } 298 299 if (picker.isEmpty()) { 300 endImmediately(); 301 return; 302 } 303 304 MarketAPI market = picker.pick(); 305 faction = market.getFaction(); 306 } 307 308 private void initBountyAmount() { 309 //float highStabilityMult = BaseMarketConditionPlugin.getHighStabilityBonusMult(market); 310 float highStabilityMult = 1f; 311 float base = Global.getSettings().getFloat("basePersonBounty"); 312 float perLevel = Global.getSettings().getFloat("personBountyPerLevel"); 313 314 float random = perLevel * (int)(Math.random() * 15) / 15f; 315 316 bountyCredits = (int) ((base + perLevel * level + random) * highStabilityMult); 317 } 318 319 private void initPerson() { 320 String factionId = Factions.PIRATES; 321 if (bountyType == BountyType.DESERTER) { 322 factionId = faction.getId(); 323 } 324 int personLevel = (int) (5 + level * 1.5f); 325 person = OfficerManagerEvent.createOfficer(Global.getSector().getFaction(factionId), 326 personLevel, false); 327 if (level >= 7) { 328 person.setRankId(Ranks.SPACE_ADMIRAL); 329 } else { 330 person.setRankId(Ranks.SPACE_CAPTAIN); 331 } 332 } 333 334 private void pickBountyType() { 335 WeightedRandomPicker<BountyType> picker = new WeightedRandomPicker<BountyType>(); 336 picker.add(BountyType.PIRATE, 10f); 337 338 //if (getSharedData().getLevel() >= 3) { 339 if (level >= 4) { 340 picker.add(BountyType.DESERTER, 30f); 341 } 342 bountyType = picker.pick(); 343 } 344 345// private String getTargetDesc() { 346// //targetDesc = person.getName().getFullName() + " is known to be a highly capable combat officer in command of a sizeable fleet."; 347// 348// ShipHullSpecAPI spec = flagship.getVariant().getHullSpec(); 349// String shipType = spec.getHullNameWithDashClass() + " " + spec.getDesignation().toLowerCase(); 350// 351// String heOrShe = "he"; 352// String hisOrHer = "his"; 353// if (person.isFemale()) { 354// heOrShe = "she"; 355// hisOrHer = "her"; 356// } 357// 358// String levelDesc = ""; 359// int personLevel = person.getStats().getLevel(); 360// if (personLevel <= 5) { 361// levelDesc = "an unremarkable officer"; 362// } else if (personLevel <= 10) { 363// levelDesc = "a capable officer"; 364// } else if (personLevel <= 15) { 365// levelDesc = "a highly capable officer"; 366// } else { 367// levelDesc = "an exceptionally capable officer"; 368// } 369// 370// String skillDesc = ""; 371// 372// if (person.getStats().getSkillLevel(Skills.OFFICER_MANAGEMENT) > 0) { 373// skillDesc = "having a high number of skilled subordinates"; 374// } else if (person.getStats().getSkillLevel(Skills.ELECTRONIC_WARFARE) > 0) { 375// skillDesc = "being proficient in electronic warfare"; 376// } else if (person.getStats().getSkillLevel(Skills.CARRIER_GROUP) > 0) { 377// skillDesc = "a noteworthy level of skill in running carrier operations"; 378// } else if (person.getStats().getSkillLevel(Skills.COORDINATED_MANEUVERS) > 0) { 379// skillDesc = "a high effectiveness in coordinating the maneuvers of ships during combat"; 380// } 381// 382// if (!skillDesc.isEmpty() && levelDesc.contains("unremarkable")) { 383// levelDesc = "an otherwise unremarkable officer"; 384// } 385// 386// String fleetDesc = ""; 387// if (level < 3) { 388// fleetDesc = "small"; 389// } else if (level <= 5) { 390// fleetDesc = "medium-sized"; 391// } else if (level <= 8) { 392// fleetDesc = "large"; 393// } else { 394// fleetDesc = "very large"; 395// } 396// 397// String targetDesc = String.format("%s is in command of a %s fleet and was last seen using a %s as %s flagship.", 398// person.getName().getFullName(), fleetDesc, shipType, hisOrHer); 399// 400// if (skillDesc.isEmpty()) { 401// targetDesc += String.format(" %s is known to be %s.", Misc.ucFirst(heOrShe), levelDesc); 402// } else { 403// targetDesc += String.format(" %s is %s known for %s.", Misc.ucFirst(heOrShe), levelDesc, skillDesc); 404// } 405// 406// //targetDesc += "\n\nLevel: " + level; 407// 408// return targetDesc; 409// } 410 411 @Override 412 protected void advanceImpl(float amount) { 413 float days = Global.getSector().getClock().convertToDays(amount); 414 //days *= 60f; 415 416 elapsedDays += days; 417 418 if (elapsedDays >= duration && !isDone()) { 419 boolean canEnd = fleet == null || !fleet.isInCurrentLocation(); 420 if (canEnd) { 421 log.info(String.format("Ending bounty on %s by %s", person.getName().getFullName(), faction.getDisplayName())); 422 result = new BountyResult(BountyResultType.END_TIME, 0, null); 423 sendUpdateIfPlayerHasIntel(result, true); 424 cleanUpFleetAndEndIfNecessary(); 425 return; 426 } 427 } 428 429 if (fleet == null) return; 430 431 if (fleet.isInCurrentLocation() && !fleet.getFaction().getId().equals(Factions.PIRATES)) { 432 fleet.setFaction(Factions.PIRATES, true); 433 } else if (!fleet.isInCurrentLocation() && !fleet.getFaction().getId().equals(Factions.NEUTRAL)) { 434 fleet.setFaction(Factions.NEUTRAL, true); 435 } 436 437 if (fleet.getFlagship() == null || fleet.getFlagship().getCaptain() != person) { 438 result = new BountyResult(BountyResultType.END_OTHER, 0, null); 439 boolean current = fleet.isInCurrentLocation(); 440 sendUpdateIfPlayerHasIntel(result, !current); 441 cleanUpFleetAndEndIfNecessary(); 442 return; 443 } 444 } 445 446 public float getTimeRemainingFraction() { 447 float f = 1f - elapsedDays / duration; 448 return f; 449 } 450 451 452 protected BountyResult result = null; 453 454 455 @Override 456 protected void notifyEnding() { 457 super.notifyEnding(); 458 cleanUpFleetAndEndIfNecessary(); 459 } 460 461 protected void cleanUpFleetAndEndIfNecessary() { 462 if (fleet != null) { 463 Misc.makeUnimportant(fleet, "pbe"); 464 fleet.clearAssignments(); 465 if (hideoutLocation != null) { 466 fleet.getAI().addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, hideoutLocation, 1000000f, null); 467 } else { 468 fleet.despawn(); 469 } 470 fleet = null; //can't null it because description uses it 471 } 472 if (!isEnding() && !isEnded()) { 473 endAfterDelay(); 474 } 475 } 476 477 protected boolean willPay() { 478 if (true) return true; 479 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 480 RepLevel level = playerFleet.getFaction().getRelationshipLevel(faction); 481 return level.isAtWorst(RepLevel.SUSPICIOUS); 482 } 483 484 protected boolean willRepIncrease() { 485 if (true) return true; 486 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 487 RepLevel level = playerFleet.getFaction().getRelationshipLevel(faction); 488 return level.isAtWorst(RepLevel.HOSTILE); 489 } 490 491 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 492 if (isDone() || result != null) return; 493 494 // also credit the player if they're in the same location as the fleet and nearby 495 float distToPlayer = Misc.getDistance(fleet, Global.getSector().getPlayerFleet()); 496 boolean playerInvolved = battle.isPlayerInvolved() || (fleet.isInCurrentLocation() && distToPlayer < 2000f); 497 498 if (battle.isInvolved(fleet) && !playerInvolved) { 499 if (fleet.getFlagship() == null || fleet.getFlagship().getCaptain() != person) { 500 fleet.setCommander(fleet.getFaction().createRandomPerson()); 501 //Global.getSector().reportEventStage(this, "other_end", market.getPrimaryEntity(), messagePriority); 502 result = new BountyResult(BountyResultType.END_OTHER, 0, null); 503 sendUpdateIfPlayerHasIntel(result, true); 504 cleanUpFleetAndEndIfNecessary(); 505 return; 506 } 507 } 508 509 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 510 if (!playerInvolved || !battle.isInvolved(fleet) || battle.onPlayerSide(fleet)) { 511 return; 512 } 513 514 // didn't destroy the original flagship 515 if (fleet.getFlagship() != null && fleet.getFlagship().getCaptain() == person) return; 516 517 //int payment = (int) (bountyCredits * battle.getPlayerInvolvementFraction()); 518 int payment = (int) bountyCredits; // don't bother about reducing the payout if the player didn't do it all themselves 519 if (payment <= 0) { 520 result = new BountyResult(BountyResultType.END_OTHER, 0, null); 521 sendUpdateIfPlayerHasIntel(result, true); 522 cleanUpFleetAndEndIfNecessary(); 523 return; 524 } 525 526 if (willPay()) { 527 log.info(String.format("Paying bounty of %d from faction [%s]", (int) payment, faction.getDisplayName())); 528 529 playerFleet.getCargo().getCredits().add(payment); 530 ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation( 531 new RepActionEnvelope(RepActions.PERSON_BOUNTY_REWARD, null, null, null, true, false), 532 faction.getId()); 533 result = new BountyResult(BountyResultType.END_PLAYER_BOUNTY, payment, rep); 534 sendUpdateIfPlayerHasIntel(result, false); 535 } else if (willRepIncrease()) { 536 log.info(String.format("Not paying bounty, but improving rep with faction [%s]", faction.getDisplayName())); 537 ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation( 538 new RepActionEnvelope(RepActions.PERSON_BOUNTY_REWARD, null, null, null, true, false), 539 faction.getId()); 540 result = new BountyResult(BountyResultType.END_PLAYER_NO_BOUNTY, payment, rep); 541 sendUpdateIfPlayerHasIntel(result, false); 542 } else { 543 log.info(String.format("Not paying bounty or improving rep with faction [%s]", faction.getDisplayName())); 544 result = new BountyResult(BountyResultType.END_PLAYER_NO_REWARD, 0, null); 545 sendUpdateIfPlayerHasIntel(result, false); 546 } 547 548 549 getSharedData().reportSuccess(); 550 //removeBounty(); 551 552 cleanUpFleetAndEndIfNecessary(); 553 } 554 555 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 556 if (isDone() || result != null) return; 557 558 if (this.fleet == fleet) { 559 fleet.setCommander(fleet.getFaction().createRandomPerson()); 560 result = new BountyResult(BountyResultType.END_OTHER, 0, null); 561 sendUpdateIfPlayerHasIntel(result, true); 562 cleanUpFleetAndEndIfNecessary(); 563 } 564 } 565 566 567 private void spawnFleet() { 568// level = 10; 569// bountyType = BountyType.PIRATE; 570 571 String fleetFactionId = Factions.PIRATES; 572 if (bountyType == BountyType.DESERTER) { 573 fleetFactionId = faction.getId(); 574 } 575 576 float qf = (float) level / 10f; 577 if (qf > 1) qf = 1; 578 579 String fleetName = ""; 580 581 fleetName = person.getName().getLast() + "'s" + " Fleet"; 582 583 float fp = (5 + level * 5) * 5f; 584 fp *= 0.75f + (float) Math.random() * 0.25f; 585 586 if (level >= 7) { 587 fp += 20f; 588 } 589 if (level >= 8) { 590 fp += 30f; 591 } 592 if (level >= 9) { 593 fp += 50f; 594 } 595 if (level >= 10) { 596 fp += 50f; 597 } 598 599 FactionAPI faction = Global.getSector().getFaction(fleetFactionId); 600 float maxFp = faction.getApproximateMaxFPPerFleet(ShipPickMode.PRIORITY_THEN_ALL) * 1.1f; 601 if (fp > maxFp) fp = maxFp; 602 603 FleetParamsV3 params = new FleetParamsV3( 604 null, 605 hideoutLocation.getLocationInHyperspace(), 606 fleetFactionId, 607 qf + 0.2f, // qualityOverride 608 FleetTypes.PERSON_BOUNTY_FLEET, 609 fp, // combatPts 610 0, // freighterPts 611 0, // tankerPts 612 0f, // transportPts 613 0f, // linerPts 614 0f, // utilityPts 615 0f // qualityMod 616 ); 617 params.ignoreMarketFleetSizeMult = true; 618// if (route != null) { 619// params.timestamp = route.getTimestamp(); 620// } 621 //params.random = random; 622 fleet = FleetFactoryV3.createFleet(params); 623 624// fleet.getFleetData().addFleetMember("station_small_Standard"); 625// fleet.getFleetData().sort(); 626 627 if (fleet == null || fleet.isEmpty()) { 628 endImmediately(); 629 return; 630 } 631 632 fleet.setCommander(person); 633 fleet.getFlagship().setCaptain(person); 634// fleet.getFleetData().setSyncNeeded(); 635// fleet.getFleetData().syncIfNeeded(); 636 FleetFactoryV3.addCommanderSkills(person, fleet, null); 637 638 //Misc.getSourceMarket(fleet).getStats().getDynamic().getValue(Stats.OFFICER_LEVEL_MULT); 639 640 Misc.makeImportant(fleet, "pbe", duration + 20f); 641 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PIRATE, true); 642 fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_NO_MILITARY_RESPONSE, true); 643 644 fleet.setNoFactionInName(true); 645 fleet.setFaction(Factions.NEUTRAL, true); 646 fleet.setName(fleetName); 647 648 fleet.addEventListener(this); 649 650 LocationAPI location = hideoutLocation.getContainingLocation(); 651 location.addEntity(fleet); 652 fleet.setLocation(hideoutLocation.getLocation().x - 500, hideoutLocation.getLocation().y + 500); 653 fleet.getAI().addAssignment(FleetAssignment.ORBIT_AGGRESSIVE, hideoutLocation, 1000000f, null); 654 655 flagship = fleet.getFlagship(); 656 657 } 658 659 public boolean runWhilePaused() { 660 return false; 661 } 662 663 664 665 public static enum BountyResultType { 666 END_PLAYER_BOUNTY, 667 END_PLAYER_NO_BOUNTY, 668 END_PLAYER_NO_REWARD, 669 END_OTHER, 670 END_TIME, 671 } 672 673 public static class BountyResult { 674 public BountyResultType type; 675 public int payment; 676 public ReputationAdjustmentResult rep; 677 public BountyResult(BountyResultType type, int payment, ReputationAdjustmentResult rep) { 678 this.type = type; 679 this.payment = payment; 680 this.rep = rep; 681 } 682 } 683 684 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { 685 686 687 Color h = Misc.getHighlightColor(); 688 Color g = Misc.getGrayColor(); 689 float pad = 3f; 690 float opad = 10f; 691 692 float initPad = pad; 693 if (mode == ListInfoMode.IN_DESC) initPad = opad; 694 695 Color tc = getBulletColorForMode(mode); 696 697 bullet(info); 698 boolean isUpdate = getListInfoParam() != null; 699 700 if (result == null) { 701// RepLevel level = Global.getSector().getPlayerFaction().getRelationshipLevel(faction.getId()); 702// Color relColor = faction.getRelColor(Factions.PLAYER); 703// String rel = level.getDisplayName().toLowerCase(); 704 705 if (mode == ListInfoMode.IN_DESC) { 706// if (!willRepIncrease()) { 707// LabelAPI label = info.addPara("No reward or rep increase (" + rel + ")", bodyColor, initPad); 708// label.setHighlight("(" + rel + ")"); 709// label.setHighlightColors(relColor); 710// } else if (!willPay()) { 711// LabelAPI label = info.addPara("No reward (" + rel + ")", bodyColor, initPad); 712// label.setHighlight("(" + rel + ")"); 713// label.setHighlightColors(relColor); 714// } else { 715 info.addPara("%s reward", initPad, tc, h, Misc.getDGSCredits(bountyCredits)); 716// } 717 int days = (int) (duration - elapsedDays); 718 if (days <= 1) { 719 days = 1; 720 } 721 addDays(info, "remaining", days, tc); 722 } else { 723 info.addPara("Faction: " + faction.getDisplayName(), initPad, tc, 724 faction.getBaseUIColor(), faction.getDisplayName()); 725 if (!isEnding()) { 726 int days = (int) (duration - elapsedDays); 727 String daysStr = "days"; 728 if (days <= 1) { 729 days = 1; 730 daysStr = "day"; 731 } 732 info.addPara("%s reward, %s " + daysStr + " remaining", 0f, tc, 733 h, Misc.getDGSCredits(bountyCredits), "" + days); 734 } 735 } 736 unindent(info); 737 return; 738 } 739 740 switch (result.type) { 741 case END_PLAYER_BOUNTY: 742 info.addPara("%s received", initPad, tc, h, Misc.getDGSCredits(result.payment)); 743 CoreReputationPlugin.addAdjustmentMessage(result.rep.delta, faction, null, 744 null, null, info, tc, isUpdate, 0f); 745 break; 746 case END_PLAYER_NO_BOUNTY: 747 CoreReputationPlugin.addAdjustmentMessage(result.rep.delta, faction, null, 748 null, null, info, tc, isUpdate, 0f); 749 break; 750 case END_PLAYER_NO_REWARD: 751 CoreReputationPlugin.addAdjustmentMessage(result.rep.delta, faction, null, 752 null, null, info, tc, isUpdate, 0f); 753 break; 754 case END_TIME: 755 break; 756 case END_OTHER: 757 break; 758 759 } 760 761 unindent(info); 762 } 763 764 public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { 765 Color h = Misc.getHighlightColor(); 766 Color g = Misc.getGrayColor(); 767 Color c = getTitleColor(mode); 768 float pad = 3f; 769 float opad = 10f; 770 771 info.addPara(getName(), c, 0f); 772 773 addBulletPoints(info, mode); 774 775 } 776 777 778 public String getSortString() { 779 return "Personal Bounty"; 780 } 781 782 public String getName() { 783 String n = person.getName().getFullName(); 784 785 if (result != null) { 786 switch (result.type) { 787 case END_PLAYER_BOUNTY: 788 case END_PLAYER_NO_BOUNTY: 789 case END_PLAYER_NO_REWARD: 790 return "Bounty Completed - " + n; 791 case END_OTHER: 792 case END_TIME: 793 return "Bounty Ended - " + n; 794 } 795 } 796 797 return "Personal Bounty - " + n; 798 } 799 800 @Override 801 public FactionAPI getFactionForUIColors() { 802 return faction; 803 } 804 805 public String getSmallDescriptionTitle() { 806 return getName(); 807 //return null; 808 } 809 810 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 811 Color h = Misc.getHighlightColor(); 812 Color g = Misc.getGrayColor(); 813 float pad = 3f; 814 float opad = 10f; 815 816 //info.addPara(getName(), c, 0f); 817 818 //info.addSectionHeading(getName(), Alignment.MID, 0f); 819 820 info.addImage(person.getPortraitSprite(), width, 128, opad); 821 822 String type = "a notorious pirate"; 823 if (bountyType == BountyType.DESERTER) type = "a deserter"; 824 825 String has = faction.getDisplayNameHasOrHave(); 826 info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " " + has + 827 " posted a bounty for bringing " + person.getName().getFullName() + 828 ", " + type + ", to justice.", 829 opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle()); 830 831 if (result != null) { 832 if (result.type == BountyResultType.END_PLAYER_BOUNTY) { 833 info.addPara("You have successfully completed this bounty.", opad); 834 } else if (result.type == BountyResultType.END_PLAYER_NO_BOUNTY) { 835 info.addPara("You have successfully completed this bounty, but received no " + 836 "credit reward because of your standing with " + 837 Misc.ucFirst(faction.getDisplayNameWithArticle()) + ".", 838 opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle()); 839 } else if (result.type == BountyResultType.END_PLAYER_NO_REWARD) { 840 info.addPara("You have successfully completed this bounty, but received no " + 841 "reward because of your standing with " + 842 Misc.ucFirst(faction.getDisplayNameWithArticle()) + ".", 843 opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle()); 844 } else { 845 info.addPara("This bounty is no longer on offer.", opad); 846 } 847 } else { 848// RepLevel level = Global.getSector().getPlayerFaction().getRelationshipLevel(faction.getId()); 849// Color relColor = faction.getRelColor(Factions.PLAYER); 850// String rel = level.getDisplayName().toLowerCase(); 851// if (!willRepIncrease()) { 852// LabelAPI label = info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " is " + rel + 853// " towards you, and you would receive no payment or reputation increase for " + 854// "completing the bounty.", opad); 855// label.setHighlight(faction.getDisplayNameWithArticleWithoutArticle(), rel); 856// label.setHighlightColors(faction.getBaseUIColor(), relColor); 857// } else if (!willPay()) { 858// LabelAPI label = info.addPara(Misc.ucFirst(faction.getDisplayNameWithArticle()) + " is " + rel + 859// " towards you, and you would receive no payment for completing the bounty, but " + 860// "it would improve your standing.", opad); 861// label.setHighlight(faction.getDisplayNameWithArticleWithoutArticle(), rel); 862// label.setHighlightColors(faction.getBaseUIColor(), relColor); 863// } 864 } 865 866 addBulletPoints(info, ListInfoMode.IN_DESC); 867 if (result == null) { 868// String targetDesc = getTargetDesc(); 869// if (targetDesc != null) { 870// info.addPara(targetDesc, opad); 871// } 872 873 if (hideoutLocation != null) { 874 SectorEntityToken fake = hideoutLocation.getContainingLocation().createToken(0, 0); 875 fake.setOrbit(Global.getFactory().createCircularOrbit(hideoutLocation, 0, 1000, 100)); 876 877 String loc = BreadcrumbSpecial.getLocatedString(fake); 878 loc = loc.replaceAll("orbiting", "hiding out near"); 879 loc = loc.replaceAll("located in", "hiding out in"); 880 String sheIs = "She is"; 881 if (person.getGender() == Gender.MALE) sheIs = "He is"; 882 info.addPara(sheIs + " rumored to be " + loc + ".", opad); 883 } 884 885 886 int cols = 7; 887 float iconSize = width / cols; 888 889 890 if (DebugFlags.PERSON_BOUNTY_DEBUG_INFO) { 891 boolean deflate = false; 892 if (!fleet.isInflated()) { 893 fleet.setFaction(Factions.PIRATES, true); 894 fleet.inflateIfNeeded(); 895 deflate = true; 896 } 897 898 String her = "her"; 899 if (person.getGender() == Gender.MALE) her = "his"; 900 info.addPara("The bounty posting also contains partial intel on the ships under " + her + " command. (DEBUG: full info)", opad); 901 info.addShipList(cols, 3, iconSize, getFactionForUIColors().getBaseUIColor(), fleet.getMembersWithFightersCopy(), opad); 902 903 info.addPara("level: " + level, 3f); 904 info.addPara("type: " + bountyType.name(), 3f); 905 906 if (deflate) { 907 fleet.deflate(); 908 } 909 } else { 910 boolean deflate = false; 911 if (!fleet.isInflated()) { 912 fleet.setFaction(Factions.PIRATES, true); 913 fleet.inflateIfNeeded(); 914 deflate = true; 915 } 916 917 List<FleetMemberAPI> list = new ArrayList<FleetMemberAPI>(); 918 Random random = new Random(person.getNameString().hashCode() * 170000); 919 920// WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>(random); 921// picker.addAll(fleet.getFleetData().getMembersListCopy()); 922// int picks = (int) Math.round(picker.getItems().size() * 0.5f); 923 924 List<FleetMemberAPI> members = fleet.getFleetData().getMembersListCopy(); 925 int max = 7; 926 for (FleetMemberAPI member : members) { 927 if (list.size() >= max) break; 928 929 if (member.isFighterWing()) continue; 930 931 float prob = (float) member.getFleetPointCost() / 20f; 932 prob += (float) max / (float) members.size(); 933 if (member.isFlagship()) prob = 1f; 934 //if (members.size() <= max) prob = 1f; 935 936 if (random.nextFloat() > prob) continue; 937 938 FleetMemberAPI copy = Global.getFactory().createFleetMember(FleetMemberType.SHIP, member.getVariant()); 939 if (member.isFlagship()) { 940 copy.setCaptain(person); 941 } 942 list.add(copy); 943 } 944 945 if (!list.isEmpty()) { 946 String her = "her"; 947 if (person.getGender() == Gender.MALE) her = "his"; 948 info.addPara("The bounty posting also contains partial intel on some of the ships under " + her + " command.", opad); 949 info.addShipList(cols, 1, iconSize, getFactionForUIColors().getBaseUIColor(), list, opad); 950 951 int num = members.size() - list.size(); 952 num = Math.round((float)num * (1f + random.nextFloat() * 0.5f)); 953 954 if (num < 5) num = 0; 955 else if (num < 10) num = 5; 956 else if (num < 20) num = 10; 957 else num = 20; 958 959 if (num > 1) { 960 info.addPara("The intel assessment notes the fleet may contain upwards of %s other ships" + 961 " of lesser significance.", opad, h, "" + num); 962 } else { 963 info.addPara("The intel assessment notes the fleet may contain several other ships" + 964 " of lesser significance.", opad); 965 } 966 } 967 968 if (deflate) { 969 fleet.deflate(); 970 } 971 } 972 } 973 974 //info.addButton("Accept", "Accept", faction.getBaseUIColor(), faction.getDarkUIColor(), (int)(width/2), 20f, opad); 975 976 } 977 978 public String getIcon() { 979 return person.getPortraitSprite(); 980 } 981 982 public Set<String> getIntelTags(SectorMapAPI map) { 983 Set<String> tags = super.getIntelTags(map); 984 tags.add(Tags.INTEL_BOUNTY); 985 tags.add(faction.getId()); 986 return tags; 987 } 988 989 @Override 990 public SectorEntityToken getMapLocation(SectorMapAPI map) { 991 Constellation c = hideoutLocation.getConstellation(); 992 SectorEntityToken entity = null; 993 if (c != null && map != null) { 994 entity = map.getConstellationLabelEntity(c); 995 } 996 if (entity == null) entity = hideoutLocation; 997 return entity; 998 } 999 1000 public PersonAPI getPerson() { 1001 return person; 1002 } 1003 1004 public CampaignFleetAPI getFleet() { 1005 return fleet; 1006 } 1007 1008 public float getDuration() { 1009 return duration; 1010 } 1011 1012 public void setDuration(float duration) { 1013 this.duration = duration; 1014 } 1015 1016 public float getBountyCredits() { 1017 return bountyCredits; 1018 } 1019 1020 public void setBountyCredits(float bountyCredits) { 1021 this.bountyCredits = bountyCredits; 1022 } 1023 1024 public BountyType getBountyType() { 1025 return bountyType; 1026 } 1027 1028 public void setBountyType(BountyType bountyType) { 1029 this.bountyType = bountyType; 1030 } 1031 1032 public FactionAPI getFaction() { 1033 return faction; 1034 } 1035 1036 public FleetMemberAPI getFlagship() { 1037 return flagship; 1038 } 1039 1040 public SectorEntityToken getHideoutLocation() { 1041 return hideoutLocation; 1042 } 1043 1044 public BountyResult getResult() { 1045 return result; 1046 } 1047 1048// @Override 1049// public boolean hasLargeDescription() { 1050// return true; 1051// } 1052 1053 1054} 1055 1056