1package com.fs.starfarer.api.impl.campaign.intel;
4import java.util.ArrayList;
6import java.util.Random;
9import org.apache.log4j.Logger;
11import com.fs.starfarer.api.EveryFrameScript;
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.campaign.BattleAPI;
14import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
15import com.fs.starfarer.api.campaign.CampaignFleetAPI;
16import com.fs.starfarer.api.campaign.FactionAPI;
17import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode;
18import com.fs.starfarer.api.campaign.FleetAssignment;
19import com.fs.starfarer.api.campaign.LocationAPI;
20import com.fs.starfarer.api.campaign.PlanetAPI;
21import com.fs.starfarer.api.campaign.RepLevel;
22import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult;
23import com.fs.starfarer.api.campaign.SectorEntityToken;
24import com.fs.starfarer.api.campaign.StarSystemAPI;
25import com.fs.starfarer.api.campaign.econ.MarketAPI;
26import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
27import com.fs.starfarer.api.characters.FullName.Gender;
28import com.fs.starfarer.api.characters.PersonAPI;
29import com.fs.starfarer.api.fleet.FleetMemberAPI;
30import com.fs.starfarer.api.fleet.FleetMemberType;
31import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin;
32import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
33import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
34import com.fs.starfarer.api.impl.campaign.DebugFlags;
35import com.fs.starfarer.api.impl.campaign.events.OfficerManagerEvent;
36import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3;
37import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3;
38import com.fs.starfarer.api.impl.campaign.ids.Factions;
39import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
40import com.fs.starfarer.api.impl.campaign.ids.Industries;
41import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
42import com.fs.starfarer.api.impl.campaign.ids.Ranks;
43import com.fs.starfarer.api.impl.campaign.ids.Tags;
44import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseManager;
45import com.fs.starfarer.api.impl.campaign.procgen.Constellation;
46import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BreadcrumbSpecial;
47import com.fs.starfarer.api.impl.campaign.shared.PersonBountyEventData;
48import com.fs.starfarer.api.impl.campaign.shared.SharedData;
49import com.fs.starfarer.api.ui.SectorMapAPI;
50import com.fs.starfarer.api.ui.TooltipMakerAPI;
51import com.fs.starfarer.api.util.Misc;
52import com.fs.starfarer.api.util.WeightedRandomPicker;
57 public static enum BountyType {
67 private float elapsedDays = 0f;
69 private float bountyCredits = 0;
76 private BountyType bountyType;
81 private int level = 0;
88 this.elapsedDays = elapsedDays;
107 if (bountyType == BountyType.DESERTER) {
108 bountyCredits *= 1.5f;
124 duration = Math.max(duration * 0.5f, Math.min(duration * 2f,
MAX_DURATION));
139 if (timeFactor < 0) timeFactor = 0;
140 if (timeFactor > 1) timeFactor = 1;
152 if (base > 10) base = 10;
154 boolean hasLow =
false;
155 boolean hasHigh =
false;
161 if (curr < base || curr == 0) hasLow =
true;
162 if (curr > base) hasHigh =
true;
169 }
else if (!hasHigh) {
170 level +=
new Random().nextInt(3) + 2;
173 if (level < 0) level = 0;
185 if (system.hasPulsar())
continue;
202 if (market.isHidden())
continue;
209 if (distToPlayer < noSpawnRange) mult = 0f;
211 if (mult <= 0)
continue;
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;
224 float dist = system.getLocation().length();
225 float distMult = Math.max(0, 50000f - dist);
227 systemPicker.
add(system, weight * mult * distMult);
232 if (system !=
null) {
235 if (planet.isStar())
continue;
236 if (planet.getMarket() !=
null &&
237 !planet.getMarket().isPlanetConditionMarketOnly())
continue;
241 hideoutLocation = picker.
pick();
245 if (hideoutLocation ==
null) {
252 private void pickFaction() {
256 boolean forceCommissionFaction =
true;
257 if (commFacId !=
null &&
getSharedData().isParticipating(commFacId)) {
260 if (bounty.faction !=
null && bounty.faction.
getId().equals(commFacId)) {
261 forceCommissionFaction =
false;
265 forceCommissionFaction =
false;
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;
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;
280 if (market.getFaction() !=
null) {
281 if (forceCommissionFaction && !market.getFaction().getId().equals(commFacId)) {
285 if (market.getFaction().isHostileTo(player)) {
295 picker.add(market, weight);
299 if (picker.isEmpty()) {
304 MarketAPI market = picker.pick();
305 faction = market.getFaction();
308 private void initBountyAmount() {
310 float highStabilityMult = 1f;
311 float base = Global.getSettings().getFloat(
"basePersonBounty");
312 float perLevel = Global.getSettings().getFloat(
"personBountyPerLevel");
314 float random = perLevel * (int)(Math.random() * 15) / 15f;
316 bountyCredits = (int) ((base + perLevel * level + random) * highStabilityMult);
319 private void initPerson() {
320 String factionId = Factions.PIRATES;
321 if (bountyType == BountyType.DESERTER) {
322 factionId = faction.
getId();
324 int personLevel = (int) (5 + level * 1.5f);
325 person = OfficerManagerEvent.createOfficer(Global.getSector().getFaction(factionId),
334 private void pickBountyType() {
335 WeightedRandomPicker<BountyType> picker =
new WeightedRandomPicker<BountyType>();
336 picker.add(BountyType.PIRATE, 10f);
340 picker.add(BountyType.DESERTER, 30f);
342 bountyType = picker.pick();
418 if (elapsedDays >= duration && !
isDone()) {
422 result =
new BountyResult(BountyResultType.END_TIME, 0,
null);
429 if (fleet ==
null)
return;
438 result =
new BountyResult(BountyResultType.END_OTHER, 0,
null);
447 float f = 1f - elapsedDays / duration;
457 super.notifyEnding();
465 if (hideoutLocation !=
null) {
478 if (
true)
return true;
485 if (
true)
return true;
498 if (battle.
isInvolved(fleet) && !playerInvolved) {
502 result =
new BountyResult(BountyResultType.END_OTHER, 0,
null);
518 int payment = (int) bountyCredits;
520 result =
new BountyResult(BountyResultType.END_OTHER, 0,
null);
527 log.info(String.format(
"Paying bounty of %d from faction [%s]", (
int) payment, faction.
getDisplayName()));
531 new RepActionEnvelope(RepActions.PERSON_BOUNTY_REWARD,
null,
null,
null,
true,
false),
533 result =
new BountyResult(BountyResultType.END_PLAYER_BOUNTY, payment, rep);
536 log.info(String.format(
"Not paying bounty, but improving rep with faction [%s]", faction.
getDisplayName()));
538 new RepActionEnvelope(RepActions.PERSON_BOUNTY_REWARD,
null,
null,
null,
true,
false),
540 result =
new BountyResult(BountyResultType.END_PLAYER_NO_BOUNTY, payment, rep);
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);
558 if (this.fleet == fleet) {
560 result =
new BountyResult(BountyResultType.END_OTHER, 0,
null);
567 private void spawnFleet() {
572 if (bountyType == BountyType.DESERTER) {
573 fleetFactionId = faction.
getId();
576 float qf = (float) level / 10f;
579 String fleetName =
"";
583 float fp = (5 + level * 5) * 5f;
584 fp *= 0.75f + (float) Math.random() * 0.25f;
599 FactionAPI faction = Global.getSector().getFaction(fleetFactionId);
601 if (fp > maxFp) fp = maxFp;
603 FleetParamsV3 params =
new FleetParamsV3(
608 FleetTypes.PERSON_BOUNTY_FLEET,
617 params.ignoreMarketFleetSizeMult =
true;
622 fleet = FleetFactoryV3.createFleet(params);
627 if (fleet ==
null || fleet.
isEmpty()) {
636 FleetFactoryV3.addCommanderSkills(person, fleet,
null);
640 Misc.makeImportant(fleet,
"pbe", duration + 20f);
653 fleet.
getAI().
addAssignment(FleetAssignment.ORBIT_AGGRESSIVE, hideoutLocation, 1000000f,
null);
665 public static enum BountyResultType {
667 END_PLAYER_NO_BOUNTY,
668 END_PLAYER_NO_REWARD,
673 public static class BountyResult {
674 public BountyResultType type;
676 public ReputationAdjustmentResult rep;
677 public BountyResult(BountyResultType type,
int payment, ReputationAdjustmentResult rep) {
679 this.payment = payment;
693 if (mode == ListInfoMode.IN_DESC) initPad = opad;
705 if (mode == ListInfoMode.IN_DESC) {
717 int days = (int) (duration - elapsedDays);
721 addDays(info,
"remaining", days, tc);
723 info.
addPara(
"Faction: " + faction.getDisplayName(), initPad, tc,
724 faction.getBaseUIColor(), faction.getDisplayName());
726 int days = (int) (duration - elapsedDays);
727 String daysStr =
"days";
732 info.
addPara(
"%s reward, %s " + daysStr +
" remaining", 0f, tc,
741 case END_PLAYER_BOUNTY:
744 null,
null, info, tc, isUpdate, 0f);
746 case END_PLAYER_NO_BOUNTY:
748 null,
null, info, tc, isUpdate, 0f);
750 case END_PLAYER_NO_REWARD:
752 null,
null, info, tc, isUpdate, 0f);
779 return "Personal Bounty";
787 case END_PLAYER_BOUNTY:
788 case END_PLAYER_NO_BOUNTY:
789 case END_PLAYER_NO_REWARD:
790 return "Bounty Completed - " + n;
793 return "Bounty Ended - " + n;
797 return "Personal Bounty - " + n;
822 String type =
"a notorious pirate";
823 if (bountyType == BountyType.DESERTER) type =
"a deserter";
825 String has = faction.getDisplayNameHasOrHave();
828 ", " + type +
", to justice.",
829 opad, faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle());
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());
845 info.
addPara(
"This bounty is no longer on offer.", opad);
873 if (hideoutLocation !=
null) {
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);
887 float iconSize = width / cols;
891 boolean deflate =
false;
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);
903 info.
addPara(
"level: " + level, 3f);
904 info.
addPara(
"type: " + bountyType.name(), 3f);
910 boolean deflate =
false;
917 List<FleetMemberAPI> list =
new ArrayList<FleetMemberAPI>();
918 Random random =
new Random(person.
getNameString().hashCode() * 170000);
927 if (list.size() >= max)
break;
929 if (member.isFighterWing())
continue;
931 float prob = (float) member.getFleetPointCost() / 20f;
932 prob += (float) max / (
float) members.size();
933 if (member.isFlagship()) prob = 1f;
936 if (random.nextFloat() > prob)
continue;
939 if (member.isFlagship()) {
945 if (!list.isEmpty()) {
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);
951 int num = members.size() - list.size();
952 num = Math.round((
float)num * (1f + random.nextFloat() * 0.5f));
954 if (num < 5) num = 0;
955 else if (num < 10) num = 5;
956 else if (num < 20) num = 10;
960 info.
addPara(
"The intel assessment notes the fleet may contain upwards of %s other ships" +
961 " of lesser significance.", opad, h,
"" + num);
963 info.
addPara(
"The intel assessment notes the fleet may contain several other ships" +
964 " of lesser significance.", opad);
983 Set<String> tags = super.getIntelTags(map);
985 tags.add(faction.getId());
993 if (c !=
null && map !=
null) {
996 if (entity ==
null) entity = hideoutLocation;
1013 this.duration = duration;
1017 return bountyCredits;
1021 this.bountyCredits = bountyCredits;
1029 this.bountyType = bountyType;
1041 return hideoutLocation;
static SettingsAPI getSettings()
static FactoryAPI getFactory()
static Logger getLogger(Class c)
static SectorAPI getSector()
static void addAdjustmentMessage(float delta, FactionAPI faction, PersonAPI person, TextPanelAPI panel, TooltipMakerAPI info, Color tc, boolean withCurrent, float pad)
static boolean PERSON_BOUNTY_DEBUG_INFO
static final String NEUTRAL
static final String PIRATES
List< EveryFrameScript > getActive()
void unindent(TooltipMakerAPI info)
void addDays(TooltipMakerAPI info, String after, float days)
void sendUpdateIfPlayerHasIntel(Object listInfoParam, TextPanelAPI textPanel)
Object getListInfoParam()
Color getBulletColorForMode(ListInfoMode mode)
void bullet(TooltipMakerAPI info)
Color getTitleColor(ListInfoMode mode)
void setDuration(float duration)
CampaignFleetAPI getFleet()
FleetMemberAPI getFlagship()
void reportMadeVisibleToPlayer()
void advanceImpl(float amount)
static PersonBountyEventData getSharedData()
SectorEntityToken getHideoutLocation()
void setBountyCredits(float bountyCredits)
void setBountyType(BountyType bountyType)
void setElapsedDays(float elapsedDays)
void cleanUpFleetAndEndIfNecessary()
BountyType getBountyType()
void pickHideoutLocation()
static float MAX_TIME_BASED_ADDED_LEVEL
boolean willRepIncrease()
void createSmallDescription(TooltipMakerAPI info, float width, float height)
void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode)
void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode)
String getSmallDescriptionTitle()
void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle)
void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param)
SectorEntityToken getMapLocation(SectorMapAPI map)
Set< String > getIntelTags(SectorMapAPI map)
float getTimeRemainingFraction()
static float MAX_DURATION
FactionAPI getFactionForUIColors()
static PersonBountyManager getInstance()
static PirateBaseManager getInstance()
static String getLocatedString(SectorEntityToken target)
PersonBountyEventData getPersonBountyEventData()
static SharedData getData()
static String getDGSCredits(float num)
static List< MarketAPI > getMarketsInLocation(LocationAPI location, String factionId)
static String ucFirst(String str)
static Color getGrayColor()
static float getDistance(SectorEntityToken from, SectorEntityToken to)
static float getDistanceToPlayerLY(Vector2f locInHyper)
static void makeUnimportant(SectorEntityToken entity, String reason)
static Color getHighlightColor()
static String getCommissionFactionId()
GO_TO_LOCATION_AND_DESPAWN
boolean isAtWorst(RepLevel level)
FleetMemberAPI createFleetMember(FleetMemberType type, String variantOrWingId)
OrbitAPI createCircularOrbit(SectorEntityToken focus, float angle, float orbitRadius, float orbitDays)
float getFloat(String key)
boolean onPlayerSide(CampaignFleetAPI fleet)
boolean isInvolved(CampaignFleetAPI test)
boolean isPlayerInvolved()
float convertToDays(float realSeconds)
void setName(String name)
CampaignFleetAIAPI getAI()
void setFaction(String factionId, boolean includeCaptains)
List< FleetMemberAPI > getMembersWithFightersCopy()
void setCommander(PersonAPI commander)
FleetMemberAPI getFlagship()
FleetDataAPI getFleetData()
void setNoFactionInName(boolean noFactionInName)
void setLocation(float x, float y)
boolean isInCurrentLocation()
MutableValue getCredits()
float getApproximateMaxFPPerFleet(ShipPickMode mode)
RepLevel getRelationshipLevel(FactionAPI faction)
PersonAPI createRandomPerson()
List< FleetMemberAPI > getMembersListCopy()
void addEventListener(FleetEventListener listener)
List< PlanetAPI > getPlanets()
void addEntity(SectorEntityToken entity)
SectorEntityToken createToken(float x, float y)
CampaignFleetAPI getPlayerFleet()
List< StarSystemAPI > getStarSystems()
ReputationAdjustmentResult adjustPlayerReputation(Object action, String factionId)
CampaignClockAPI getClock()
FactionAPI getPlayerFaction()
IntelManagerAPI getIntelManager()
LocationAPI getContainingLocation()
void setOrbit(OrbitAPI orbit)
Constellation getConstellation()
Vector2f getLocationInHyperspace()
MemoryAPI getMemoryWithoutUpdate()
void addAssignment(FleetAssignment assignment, SectorEntityToken target, float maxDurationInDays, Script onCompletion)
void queueIntel(IntelInfoPlugin plugin, float maxCommQueueDays)
void set(String key, Object value)
String getPortraitSprite()
void setRankId(String rank)
void setCaptain(PersonAPI commander)
SectorEntityToken getConstellationLabelEntity(Constellation c)