Starsector API
Loading...
Searching...
No Matches
MarketCMD.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.rulecmd.salvage;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.List;
6import java.util.Map;
7import java.util.Random;
8
9import org.lwjgl.input.Keyboard;
10import org.lwjgl.util.vector.Vector2f;
11
12import com.fs.starfarer.api.EveryFrameScript;
13import com.fs.starfarer.api.Global;
14import com.fs.starfarer.api.campaign.BattleAPI;
15import com.fs.starfarer.api.campaign.BattleAPI.BattleSide;
16import com.fs.starfarer.api.campaign.CampaignFleetAPI;
17import com.fs.starfarer.api.campaign.CargoAPI;
18import com.fs.starfarer.api.campaign.CoreInteractionListener;
19import com.fs.starfarer.api.campaign.FactionAPI;
20import com.fs.starfarer.api.campaign.GroundRaidTargetPickerDelegate;
21import com.fs.starfarer.api.campaign.InteractionDialogAPI;
22import com.fs.starfarer.api.campaign.InteractionDialogPlugin;
23import com.fs.starfarer.api.campaign.OptionPanelAPI;
24import com.fs.starfarer.api.campaign.RepLevel;
25import com.fs.starfarer.api.campaign.RuleBasedDialog;
26import com.fs.starfarer.api.campaign.SectorEntityToken;
27import com.fs.starfarer.api.campaign.TextPanelAPI;
28import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.ActionType;
29import com.fs.starfarer.api.campaign.econ.Industry;
30import com.fs.starfarer.api.campaign.econ.MarketAPI;
31import com.fs.starfarer.api.campaign.econ.MonthlyReport;
32import com.fs.starfarer.api.campaign.listeners.GroundRaidObjectivesListener.RaidResultData;
33import com.fs.starfarer.api.campaign.listeners.ListenerUtil;
34import com.fs.starfarer.api.campaign.rules.MemoryAPI;
35import com.fs.starfarer.api.characters.OfficerDataAPI;
36import com.fs.starfarer.api.characters.PersonAPI;
37import com.fs.starfarer.api.combat.BattleCreationContext;
38import com.fs.starfarer.api.combat.MutableStat;
39import com.fs.starfarer.api.combat.MutableStat.StatMod;
40import com.fs.starfarer.api.combat.StatBonus;
41import com.fs.starfarer.api.fleet.FleetMemberAPI;
42import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact;
43import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
44import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
45import com.fs.starfarer.api.impl.campaign.DebugFlags;
46import com.fs.starfarer.api.impl.campaign.FleetEncounterContext;
47import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl;
48import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.BaseFIDDelegate;
49import com.fs.starfarer.api.impl.campaign.FleetInteractionDialogPluginImpl.FIDConfig;
50import com.fs.starfarer.api.impl.campaign.MilitaryResponseScript;
51import com.fs.starfarer.api.impl.campaign.MilitaryResponseScript.MilitaryResponseParams;
52import com.fs.starfarer.api.impl.campaign.RuleBasedInteractionDialogPluginImpl;
53import com.fs.starfarer.api.impl.campaign.econ.RecentUnrest;
54import com.fs.starfarer.api.impl.campaign.econ.impl.PopulationAndInfrastructure;
55import com.fs.starfarer.api.impl.campaign.graid.DisruptIndustryRaidObjectivePluginImpl;
56import com.fs.starfarer.api.impl.campaign.graid.GroundRaidObjectivePlugin;
57import com.fs.starfarer.api.impl.campaign.ids.Commodities;
58import com.fs.starfarer.api.impl.campaign.ids.Conditions;
59import com.fs.starfarer.api.impl.campaign.ids.Factions;
60import com.fs.starfarer.api.impl.campaign.ids.Industries;
61import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
62import com.fs.starfarer.api.impl.campaign.ids.Sounds;
63import com.fs.starfarer.api.impl.campaign.ids.Stats;
64import com.fs.starfarer.api.impl.campaign.ids.Strings;
65import com.fs.starfarer.api.impl.campaign.ids.Tags;
66import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
67import com.fs.starfarer.api.impl.campaign.intel.deciv.DecivTracker;
68import com.fs.starfarer.api.impl.campaign.population.CoreImmigrationPluginImpl;
69import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
70import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity;
71import com.fs.starfarer.api.impl.campaign.rulecmd.BaseCommandPlugin;
72import com.fs.starfarer.api.impl.campaign.rulecmd.FireAll;
73import com.fs.starfarer.api.impl.campaign.rulecmd.FireBest;
74import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption;
75import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.BaseOptionStoryPointActionDelegate;
76import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.StoryOptionParams;
77import com.fs.starfarer.api.impl.campaign.rulecmd.ShowDefaultVisual;
78import com.fs.starfarer.api.impl.campaign.shared.SharedData;
79import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin;
80import com.fs.starfarer.api.ui.LabelAPI;
81import com.fs.starfarer.api.ui.TooltipMakerAPI;
82import com.fs.starfarer.api.ui.TooltipMakerAPI.StatModValueGetter;
83import com.fs.starfarer.api.util.Misc;
84import com.fs.starfarer.api.util.Misc.Token;
85
89public class MarketCMD extends BaseCommandPlugin {
90
91 public static enum RaidType {
92 CUSTOM_ONLY,
93 VALUABLE,
94 DISRUPT,
95 }
96
97 public static enum BombardType {
98 TACTICAL,
99 SATURATION,
100 }
101
102 public static enum RaidDangerLevel {
103 NONE("None", "None", Misc.getPositiveHighlightColor(), 0f, 60f, 1),
104 MINIMAL("Minimal", "Minimal", Misc.getPositiveHighlightColor(), 0.02f, 50f, 1),
105 LOW("Low", "Light", Misc.getPositiveHighlightColor(), 0.04f, 40f, 2),
106 MEDIUM("Medium", "Moderate", Misc.getHighlightColor(), 0.08f, 30f, 3),
107 HIGH("High", "Heavy", Misc.getNegativeHighlightColor(), 0.16f, 20f, 5),
108 EXTREME("Extreme", "Extreme", Misc.getNegativeHighlightColor(), 0.32f, 10f, 7);
109
110 private static RaidDangerLevel [] vals = values();
111
112 public String name;
113 public String lossesName;
114 public Color color;
115 public float marineLossesMult;
116 public int marineTokens;
117 public float disruptionDays;
118 private RaidDangerLevel(String name, String lossesName, Color color, float marineLossesMult, float disruptionDays, int marineTokens) {
119 this.name = name;
120 this.lossesName = lossesName;
121 this.color = color;
122 this.marineLossesMult = marineLossesMult;
123 this.disruptionDays = disruptionDays;
124 this.marineTokens = marineTokens;
125 }
126
127 public RaidDangerLevel next() {
128 int index = this.ordinal() + 1;
129 if (index >= vals.length) index = vals.length - 1;
130 return vals[index];
131 }
132 public RaidDangerLevel prev() {
133 int index = this.ordinal() - 1;
134 if (index < 0) index = 0;
135 return vals[index];
136 }
137 }
138
139 public static class TempData {
140 //public boolean canSurpriseRaid;
141 //public boolean isSurpriseRaid;
142 public boolean canRaid;
143 public boolean canBombard;
144
145 public int bombardCost;
146
147 public int marinesLost;
148
149 //public boolean canFail = false;
150 //public float failProb = 0f;
151
152 public float raidMult;
153
154 public float attackerStr;
155 public float defenderStr;
156
157 public boolean nonMarket = false;
158 public boolean secret = false;
159
160 public RaidType raidType = null;
161 public BombardType bombardType = null;
162 public CargoAPI raidLoot;
163 public int xpGained;
164 public Industry target = null;
165 public List<FactionAPI> willBecomeHostile = new ArrayList<FactionAPI>();
166 public List<Industry> bombardmentTargets = new ArrayList<Industry>();
167 public List<GroundRaidObjectivePlugin> objectives = new ArrayList<GroundRaidObjectivePlugin>();
168 public String contText;
169 public String raidGoBackTrigger;
170 public String raidContinueTrigger;
171 }
172
173 public static int HOSTILE_ACTIONS_TIMEOUT_DAYS = 60;
174 public static int TACTICAL_BOMBARD_TIMEOUT_DAYS = 120;
175 public static int SATURATION_BOMBARD_TIMEOUT_DAYS = 365;
176
177 public static int MIN_MARINE_TOKENS = 1;
178 public static float RE_PER_MARINE_TOKEN = 0.1f;
179 public static int MAX_MARINE_TOKENS = 10;
180 public static float LOSS_REDUCTION_PER_RESERVE_TOKEN = 0.05f;
181 public static float LOSS_INCREASE_PER_RAID = 0.5f;
182 public static float MAX_MARINE_LOSSES = 0.8f;
183
184 public static float MIN_RE_TO_REDUCE_MARINE_LOSSES = 0.5f;
185 public static float MAX_MARINE_LOSS_REDUCTION_MULT = 0.05f;
186
187 // for causing deficit; higher value means less units need to be raided to cause same deficit
188 public static float ECON_IMPACT_MULT = 1f;
189
190 public static float QUANTITY_MULT_NORMAL = 1f;
191 public static float QUANTITY_MULT_EXCESS = 2f;
192 public static float QUANTITY_MULT_DEFICIT = -0.5f;
193 public static float QUANTITY_MULT_OVERALL = 0.1f;
194
195
196 public static String ENGAGE = "mktEngage";
197
198 public static String RAID = "mktRaid";
199 public static String RAID_NON_MARKET = "mktRaidNonMarket";
200 //public static String RAID_SURPRISE = "mktRaidSurprise";
201 //public static String RAID_RARE = "mktRaidRare";
202 public static String RAID_VALUABLE = "mktRaidValuable";
203 public static String RAID_DISRUPT = "mktRaidDisrupt";
204 public static String RAID_GO_BACK = "mktRaidGoBack";
205 public static String RAID_CONFIRM_CONTINUE = "mktRaidConfirmContinue";
206
207 public static String RAID_CONFIRM = "mktRaidConfirm";
208 public static String RAID_CONFIRM_STORY = "mktRaidConfirmStory";
209 public static String RAID_NEVER_MIND = "mktRaidNeverMind";
210 public static String RAID_RESULT = "mktRaidResult";
211
212 public static String INVADE = "mktInvade";
213 public static String GO_BACK = "mktGoBack";
214
215 public static String BOMBARD = "mktBombard";
216 public static String BOMBARD_TACTICAL = "mktBombardTactical";
217 public static String BOMBARD_SATURATION = "mktBombardSaturation";
218 public static String BOMBARD_CONFIRM = "mktBombardConfirm";
219 public static String BOMBARD_NEVERMIND = "mktBombardNeverMind";
220 public static String BOMBARD_RESULT = "mktBombardResult";
221
222 public static String DEBT_RESULT_CONTINUE = "marketCmd_checkDebtContinue";
223
224
225
226
227 public static float DISRUPTION_THRESHOLD = 0.25f;
228 public static float VALUABLES_THRESHOLD = 0.05f;
229
237 protected MemoryAPI memory;
238 protected MarketAPI market;
240 protected Map<String, MemoryAPI> memoryMap;
242
243 protected TempData temp = new TempData();
244
245 public MarketCMD() {
246 //DebugFlags.MARKET_HOSTILITIES_DEBUG = false;
247 }
248
249 protected void clearTemp() {
250 if (temp != null) {
251 //temp.isSurpriseRaid = false;
252 temp.raidType = null;
253 temp.bombardType = null;
254 temp.raidLoot = null;
255 temp.target = null;
256 temp.willBecomeHostile.clear();
257 temp.bombardmentTargets.clear();
258 temp.objectives.clear();
259 temp.contText = null;
260 temp.raidGoBackTrigger = null;
261 temp.raidContinueTrigger = null;
262 //temp.canFail = false;
263 //temp.failProb = 0f;
264 }
265 }
266
270
271 protected void init(SectorEntityToken entity) {
272
274 this.entity = entity;
277
280
282
284
285 //DebugFlags.MARKET_HOSTILITIES_DEBUG = false;
286 //market.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_PLAYER_HOSTILE_ACTIVITY_NEAR_MARKET, true, 0.1f);
287
288 String key = "$MarketCMD_temp";
289 MemoryAPI mem = null;
290 if (market != null) {
292 } else {
294 }
295 if (mem.contains(key)) {
296 temp = (TempData) mem.get(key);
297 } else {
298 mem.set(key, temp, 0f);
299 }
300 }
301
302 public boolean execute(String ruleId, InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) {
303 this.dialog = dialog;
304 this.memoryMap = memoryMap;
305
306 String command = params.get(0).getString(memoryMap);
307 if (command == null) return false;
308
310 init(entity);
311
313
316
317 if (command.equals("showDefenses")) {
318 clearTemp();
319 //new ShowDefaultVisual().execute(null, dialog, Misc.tokenize(""), memoryMap);
320 showDefenses(true);
321 } else if (command.equals("goBackToDefenses")) {
322 if (temp.nonMarket) {
323 String trigger = temp.raidGoBackTrigger;
324 if (trigger == null || trigger.isEmpty()) trigger = "PopulateOptions";
325 clearTemp();
326 FireAll.fire(null, dialog, memoryMap, trigger);
327 return true;
328 }
329 clearTemp();
330 //new ShowDefaultVisual().execute(null, dialog, Misc.tokenize(""), memoryMap);
331 showDefenses(true);
332 //dialog.getVisualPanel().finishFadeFast();
333 } else if (command.equals("engage")) {
334 engage();
335 } else if (command.equals("raidMenu")) {
336// boolean surprise = "mktRaidSurprise".equals(memory.get("$option"));
337// temp.isSurpriseRaid = surprise;
338 raidMenu();
339// } else if (command.equals("raidRare")) {
340// raidRare();
341 } else if (command.equals("raidNonMarket")) {
343 } else if (command.equals("raidValuable")) {
344 raidValuable();
345 } else if (command.equals("raidDisrupt")) {
346 raidDisrupt();
347 } else if (command.equals("raidConfirm")) {
348 raidConfirm(false);
349 } else if (command.equals("raidConfirmContinue")) {
351 } else if (command.equals("raidNeverMind")) {
353 } else if (command.equals("addContinueToRaidResultOption")) {
354 addContinueOption(temp.contText);
355 } else if (command.equals("raidResult")) {
356 raidResult();
357 } else if (command.equals("bombardMenu")) {
358 bombardMenu();
359 } else if (command.equals("bombardTactical")) {
361 } else if (command.equals("bombardSaturation")) {
363 } else if (command.equals("bombardConfirm")) {
365 } else if (command.equals("bombardNeverMind")) {
367 } else if (command.equals("bombardResult")) {
369 } else if (command.equals("checkDebtEffect")) {
370 return checkDebtEffect();
371 } else if (command.equals("applyDebtEffect")) {
373 } else if (command.equals("checkMercsLeaving")) {
374 return checkMercsLeaving();
375 } else if (command.equals("convinceMercToStay")) {
377 } else if (command.equals("mercLeaves")) {
378 mercLeaves();
379 } else if (command.equals("assistVolturnInsurgents"))
380 {
381 MarketAPI volturn = Global.getSector().getEconomy().getMarket("volturn");
382 RecentUnrest.get(volturn).add(2, "The Luddic insurgency acquired unusually heavy weaponry somehow");
383 }
384
385 return true;
386 }
387
388 protected void showDefenses(boolean withText) {
391
392 boolean hasNonStation = false;
393 boolean hasOtherButInsignificant = true;
394 boolean hasStation = station != null;
395 boolean otherWantsToFight = false;
396 BattleAPI b = null;
397 FleetEncounterContext context = null;
399
400 boolean ongoingBattle = false;
401
402 boolean playerOnDefenderSide = false;
403 boolean playerCanNotJoin = false;
404
405 String stationType = "station";
406 if (station != null) {
407 FleetMemberAPI flagship = station.getFlagship();
408 if (flagship != null && flagship.getVariant() != null) {
409 String name = flagship.getVariant().getDesignation().toLowerCase();
410 stationType = name;
411 }
412 }
413
414 StationState state = getStationState();
415
416 if (market != null) {
418 }
419
420 if (primary == null) {
421 if (state == StationState.NONE) {
422 text.addPara("The colony has no orbital station or nearby fleets to defend it.");
423 } else {
425 text.addPara("There are no nearby fleets to defend the colony.");
426 }
427 } else {
428 ongoingBattle = primary.getBattle() != null;
429
430 CampaignFleetAPI pluginFleet = primary;
431 if (ongoingBattle) {
432 BattleSide playerSide = primary.getBattle().pickSide(playerFleet);
433 CampaignFleetAPI other = primary.getBattle().getPrimary(primary.getBattle().getOtherSide(playerSide));
434 if (other != null) {
435 pluginFleet = other;
436 }
437 }
438
439 FIDConfig params = new FIDConfig();
440 params.justShowFleets = true;
441 params.showPullInText = withText;
442 plugin = new FleetInteractionDialogPluginImpl(params);
443 //dialog.setInteractionTarget(primary);
444 dialog.setInteractionTarget(pluginFleet);
445 plugin.init(dialog);
446// if (ongoingBattle) {
447// plugin.setPlayerFleet(primary.getBattle().getPlayerCombined());
448// }
450
451
452 context = (FleetEncounterContext)plugin.getContext();
453 b = context.getBattle();
454
455 BattleSide playerSide = b.pickSide(playerFleet);
456 if (playerSide != BattleSide.NO_JOIN) {
457 if (b.getOtherSideCombined(playerSide).isEmpty()) {
458 playerSide = BattleSide.NO_JOIN;
459 }
460 }
461 playerCanNotJoin = playerSide == BattleSide.NO_JOIN;
462 if (!playerCanNotJoin) {
463 playerOnDefenderSide = b.getSide(playerSide) == b.getSideFor(primary);
464 }
465 if (!ongoingBattle) {
466 playerOnDefenderSide = false;
467 }
468
469 boolean otherHasStation = false;
470 if (playerSide != BattleSide.NO_JOIN) {
471 //for (CampaignFleetAPI fleet : b.getNonPlayerSide()) {
472 if (station != null) {
473 for (CampaignFleetAPI fleet : b.getSideFor(station)) {
474 if (!fleet.isStationMode()) {
475 hasNonStation = true;
476 hasOtherButInsignificant &= Misc.isInsignificant(fleet);
477 }
478 }
479 } else {
480 if (b.getNonPlayerSide() != null) {
481 for (CampaignFleetAPI fleet : b.getNonPlayerSide()) {
482 if (!fleet.isStationMode()) {
483 hasNonStation = true;
484 hasOtherButInsignificant &= Misc.isInsignificant(fleet);
485 }
486 }
487 } else {
488 hasNonStation = true;
489 }
490 }
491
492 for (CampaignFleetAPI fleet : b.getOtherSide(playerSide)) {
493 if (!fleet.isStationMode()) {
494 //hasNonStation = true;
495 } else {
496 otherHasStation = true;
497 }
498 }
499 }
500
501 if (!hasNonStation) hasOtherButInsignificant = false;
502
503 //otherWantsToFight = hasStation || plugin.otherFleetWantsToFight(true);
504
505 // inaccurate because it doesn't include the station in the "wants to fight" calculation, but, this is tricky
506 // and I don't want to break it right now
507 otherWantsToFight = otherHasStation || plugin.otherFleetWantsToFight(true);
508
509 if (withText) {
510 if (hasStation) {
511 String name = "An orbital station";
512 if (station != null) {
513 FleetMemberAPI flagship = station.getFlagship();
514 if (flagship != null) {
515 name = flagship.getVariant().getDesignation().toLowerCase();
516 stationType = name;
517 name = Misc.ucFirst(station.getFaction().getPersonNamePrefixAOrAn()) + " " +
518 station.getFaction().getPersonNamePrefix() + " " + name;
519 }
520 }
521 text.addPara(name + " dominates the orbit and prevents any " +
522 "hostile action, aside from a quick raid, unless it is dealt with.");
523
524
525 if (hasNonStation) {
526 if (ongoingBattle) {
527 text.addPara("There are defending ships present, but they are currently involved in a battle, "
528 + "and you could take advantage of the distraction to launch a raid.");
529 } else {
530 if (hasOtherButInsignificant) {
531 text.addPara("Defending ships are present, but not in sufficient strength " +
532 "to want to give battle or prevent any hostile action you might take.");
533 } else {
534 text.addPara("The defending ships present are, with the support of the station, sufficient to prevent " +
535 "raiding as well.");
536 }
537 }
538 }
539 } else if (hasNonStation && otherWantsToFight) {
541 text.addPara("Defending ships are present in sufficient strength to prevent any hostile action " +
542 "until they are dealt with.");
543 } else if (hasNonStation && !otherWantsToFight) {
545 text.addPara("Defending ships are present, but not in sufficient strength " +
546 "to want to give battle or prevent any hostile action you might take.");
547 }
548
549 plugin.printOngoingBattleInfo();
550 }
551 }
552
553 if (!hasNonStation) hasOtherButInsignificant = false;
554
556
557 String engageText = "Engage the defenders";
558
559 if (playerCanNotJoin) {
560 engageText = "Engage the defenders";
561 } else if (playerOnDefenderSide) {
562 if (hasStation && hasNonStation) {
563 engageText = "Aid the " + stationType + " and its defenders";
564 } else if (hasStation) {
565 engageText = "Aid the " + stationType + "";
566 } else {
567 engageText = "Aid the defenders";
568 }
569 } else {
570 if (ongoingBattle) {
571 engageText = "Aid the attacking forces";
572 } else {
573 if (hasStation && hasNonStation) {
574 engageText = "Engage the " + stationType + " and its defenders";
575 } else if (hasStation) {
576 engageText = "Engage the " + stationType + "";
577 } else {
578 engageText = "Engage the defenders";
579 }
580 }
581 }
582
583
584 options.addOption(engageText, ENGAGE);
585
586 temp.secret = false;
587 temp.canRaid = ongoingBattle || hasOtherButInsignificant || (hasNonStation && !otherWantsToFight) || !hasNonStation;
588 temp.canBombard = (hasOtherButInsignificant || (hasNonStation && !otherWantsToFight) || !hasNonStation) && !hasStation;
589 //temp.canSurpriseRaid = Misc.getDaysSinceLastRaided(market) < SURPRISE_RAID_TIMEOUT;
590
591 boolean couldRaidIfNotDebug = temp.canRaid;
593 if (!temp.canRaid || !temp.canBombard) {
594 text.addPara("(DEBUG mode: can raid and bombard anyway)");
595 }
596 temp.canRaid = true;
597 temp.canBombard = true;
598 //temp.canSurpriseRaid = true;
599 }
600
601// options.addOption("Launch a raid against the colony", RAID);
602// options.addOption("Consider an orbital bombardment", BOMBARD);
603// options.addOption("Launch a surprise raid against " + market.getName(), RAID_SURPRISE);
604 options.addOption("Launch a raid against " + market.getName() + "", RAID);
605 //dialog.setOptionColor(RAID_SURPRISE, Misc.getStoryOptionColor());
606 options.addOption("Consider an orbital bombardment of " + market.getName() + "", BOMBARD);
607
608 if (!temp.canRaid) {
609 options.setEnabled(RAID, false);
610 options.setTooltip(RAID, "The presence of enemy fleets that are willing to offer battle makes a raid impossible.");
611 }
612
613// if (!temp.canSurpriseRaid) {
624// options.setEnabled(RAID_SURPRISE, false);
625// options.setTooltip(RAID_SURPRISE, "This colony was raided within the last cycle and its ground defenses are on high alert, making a surprise raid impossible.");
626// }
627
628 if (!temp.canBombard) {
629 options.setEnabled(BOMBARD, false);
630 options.setTooltip(BOMBARD, "All defenses must be defeated to make a bombardment possible.");
631 }
632
633
634 //DEBUG = false;
635 if (temp.canRaid && getRaidCooldown() > 0) {// && couldRaidIfNotDebug) {
637 options.setEnabled(RAID, false);
638 text.addPara("Your forces will be able to organize another raid within a day or so.");
639 temp.canRaid = false;
640 } else {
641 text.addPara("Your forces will be able to organize another raid within a day or so.");
642 text.addPara("(DEBUG mode: can do it anyway)");
643 }
644 //options.setTooltip(RAID, "Need more time to organize another raid.");
645 }
646
647 //options.addOption("Launch a raid of the colony", RAID);
648
649
650 if (context != null && otherWantsToFight && !playerCanNotJoin) {
651 boolean knows = context.getBattle() != null && context.getBattle().getNonPlayerSide() != null &&
653 boolean lowImpact = context.isLowRepImpact();
654 FactionAPI nonHostile = plugin.getNonHostileOtherFaction();
655 //if (!playerFleet.getFaction().isHostileTo(otherFleet.getFaction()) && knows && !context.isEngagedInHostilities()) {
656 if (nonHostile != null && knows && !lowImpact && !context.isEngagedInHostilities()) {
658 "The " + nonHostile.getDisplayNameLong() +
659 " " + nonHostile.getDisplayNameIsOrAre() +
660 " not currently hostile, and you have been positively identified. " +
661 "Are you sure you want to engage in open hostilities?", "Yes", "Never mind");
662 }
663 } else if (context == null || playerCanNotJoin || !otherWantsToFight) {
664 options.setEnabled(ENGAGE, false);
665 if (!otherWantsToFight) {
666 if (ongoingBattle && playerOnDefenderSide && !otherWantsToFight) {
667 options.setTooltip(ENGAGE, "The attackers are in disarray and not currently attempting to engage the station.");
668 } else {
669 if (playerCanNotJoin) {
670 options.setTooltip(ENGAGE, "You're unable to join this battle.");
671 } else if (primary == null) {
672 options.setTooltip(ENGAGE, "There are no defenders to engage.");
673 } else {
674 options.setTooltip(ENGAGE, "The defenders are refusing to give battle to defend the colony.");
675 }
676 }
677 }
678 }
679
680 options.addOption("Go back", GO_BACK);
681 options.setShortcut(GO_BACK, Keyboard.KEY_ESCAPE, false, false, false, true);
682
683
684 if (plugin != null) {
685 plugin.cleanUpBattle();
686 }
687
688 }
689
690 public static float getRaidStr(CampaignFleetAPI fleet) {
691 float attackerStr = fleet.getCargo().getMaxPersonnel() * 0.25f;
692 float support = Misc.getFleetwideTotalMod(fleet, Stats.FLEET_GROUND_SUPPORT, 0f);
693 attackerStr += Math.min(support, attackerStr);
694
696 attackerStr = stat.computeEffective(attackerStr);
697
698 return attackerStr;
699 }
700
702 public static float getDefenderStr(MarketAPI market) {
703 return getDefenderStr(market, false);
704 }
705 public static float getDefenderStr(MarketAPI market, boolean forBombard) {
707 float defenderStr = (int) Math.round(stat.computeEffective(0f));
708 float added = getDefenderIncreaseValue(market);
709 defenderStr += added;
710
711 if (market.isPlayerOwned() && !forBombard) {
712 float marineDefenseValueMult = MARINES_IN_MARKET_CARGO_DEFENSE_BONUS;
714 if (cargo != null) {
715 defenderStr += cargo.getMarines() * marineDefenseValueMult;
716 }
718 if (cargo != null) {
719 defenderStr += cargo.getMarines() * marineDefenseValueMult;
720 }
721 }
722
723 return defenderStr;
724 }
725
727 return getRaidEffectiveness(market, getRaidStr(fleet));
728 }
729 public static float getRaidEffectiveness(MarketAPI market, float attackerStr) {
730 float defenderStr = getDefenderStr(market);
731 return attackerStr / Math.max(1f, (attackerStr + defenderStr));
732 }
733
734 public static int getMarinesFor(MarketAPI market, int tokens) {
735 float defenderStr = getDefenderStr(market);
736 return getMarinesFor((int)defenderStr, tokens);
737 }
738 public static int getMarinesFor(int defenderStrength, int tokens) {
739// mult = as / (as + ds);
740// tokens = mult / re_per
741//
742// t * re_per = as / (as + ds)
743// t * re_per * (as + ds) = as;
744// t * re_per * as + t * re_per * ds = as;
745// t * re_per * ds = as * (1 - t * re_per)
746// as = t * re_per * ds / (1 - t * re_per)
747
748
749 int marines = (int) Math.round((float) tokens * RE_PER_MARINE_TOKEN *
750 (float) defenderStrength / (1f - (float) tokens * RE_PER_MARINE_TOKEN));
751
752 return marines;
753 }
754 public static int getDisruptDaysPerToken(MarketAPI market, Industry industry) {
756 return (int) Math.round(obj.getBaseDisruptDuration(1));
757 }
758
759
760 protected void raidNonMarket() {
761 float width = 350;
762 float opad = 10f;
763 float small = 5f;
764
765 Color h = Misc.getHighlightColor();
766
767 temp.nonMarket = true;
768
769 float difficulty = memory.getFloat("$raidDifficulty");
770 temp.raidGoBackTrigger = memory.getString("$raidGoBackTrigger");
771 temp.raidContinueTrigger = memory.getString("$raidContinueTrigger");
772
773 dialog.getVisualPanel().showImagePortion("illustrations", "raid_prepare", 640, 400, 0, 0, 480, 300);
774
775 float marines = playerFleet.getCargo().getMarines();
777 if (support > marines) support = marines;
778
779 StatBonus attackerBase = new StatBonus();
780 StatBonus defenderBase = new StatBonus();
781
782 //defenderBase.modifyFlatAlways("base", baseDef, "Base value for a size " + market.getSize() + " colony");
783
784 attackerBase.modifyFlatAlways("core_marines", marines, "Marines on board");
785 attackerBase.modifyFlatAlways("core_support", support, "Fleet capability for ground support");
786
788 StatBonus defender = new StatBonus();
789 if (market != null && difficulty <= 0) defender = market.getStats().getDynamic().getMod(Stats.GROUND_DEFENSES_MOD);
790
791 defender.modifyFlat("difficulty", difficulty, "Expected resistance");
792
793 String surpriseKey = "core_surprise";
794// if (temp.isSurpriseRaid) {
795// //defender.modifyMult(surpriseKey, 0.1f, "Surprise raid");
796// attacker.modifyMult(surpriseKey, SURPRISE_RAID_STRENGTH_MULT, "Surprise raid");
797// }
798
799 String increasedDefensesKey = "core_addedDefStr";
800 float added = 0;
801 if (market != null) added = getDefenderIncreaseValue(market);
802 if (added > 0) {
803 defender.modifyFlat(increasedDefensesKey, added, "Increased defender preparedness");
804 }
805
806 float attackerStr = (int) Math.round(attacker.computeEffective(attackerBase.computeEffective(0f)));
807 float defenderStr = (int) Math.round(defender.computeEffective(defenderBase.computeEffective(0f)));
808
809 temp.attackerStr = attackerStr;
810 temp.defenderStr = defenderStr;
811
813
815
816 String has = faction.getDisplayNameHasOrHave();
817 String is = faction.getDisplayNameIsOrAre();
818 boolean hostile = faction.isHostileTo(Factions.PLAYER);
819 boolean tOn = playerFleet.isTransponderOn();
820 float initPad = 0f;
821 if (!hostile && !faction.isNeutralFaction()) {
822 if (tOn) {
824 " not currently hostile. Your fleet's transponder is on, and carrying out a raid " +
825 "will result in open hostilities.",
827 } else {
829 " not currently hostile. Your fleet's transponder is off, and carrying out a raid " +
830 "will only result in a minor penalty to your standing.",
832 }
833 initPad = opad;
834 }
835
836 float sep = small;
837 sep = 3f;
838 info.addPara("Raid strength: %s", initPad, h, "" + (int)attackerStr);
839 info.addStatModGrid(width, 50, opad, small, attackerBase, true, statPrinter(false));
840 if (!attacker.isUnmodified()) {
841 info.addStatModGrid(width, 50, opad, sep, attacker, true, statPrinter(true));
842 }
843
844
845 info.addPara("Operation difficulty: %s", opad, h, "" + (int)defenderStr);
846 //info.addStatModGrid(width, 50, opad, small, defenderBase, true, statPrinter());
847 //if (!defender.isUnmodified()) {
848 info.addStatModGrid(width, 50, opad, small, defender, true, statPrinter(true));
849 //}
850
851 defender.unmodifyFlat(increasedDefensesKey);
852 defender.unmodifyMult(surpriseKey);
853 attacker.unmodifyMult(surpriseKey);
854
856
857 boolean hasForces = true;
858 temp.raidMult = attackerStr / Math.max(1f, (attackerStr + defenderStr));
859 temp.raidMult = Math.round(temp.raidMult * 100f) / 100f;
860
861 {
862 Color eColor = h;
863 if (temp.raidMult < DISRUPTION_THRESHOLD && temp.raidMult < VALUABLES_THRESHOLD) {
865 }
866 text.addPara("Projected raid effectiveness: %s",
867 eColor,
868 "" + (int)(temp.raidMult * 100f) + "%");
869 //"" + (int)Math.round(temp.raidMult * 100f) + "%");
870 if (temp.raidMult < VALUABLES_THRESHOLD) {
871 text.addPara("You do not have the forces to carry out an effective raid.");
872 hasForces = false;
873 }
874 }
875
877
878 options.addOption("Designate raid objectives", RAID_VALUABLE);
879
880 if (!hasForces) {
882 }
883
884 options.addOption("Go back", RAID_GO_BACK);
885 options.setShortcut(RAID_GO_BACK, Keyboard.KEY_ESCAPE, false, false, false, true);
886 }
887
888
889// protected void raidRare() {
890//
891// }
892
893 protected void raidMenu() {
894 float width = 350;
895 float opad = 10f;
896 float small = 5f;
897
898// if (true) {
899// Global.getSector().getCampaignUI().showCoreUITab(CoreUITabId.CARGO);
900// return;
901// }
902
903 Color h = Misc.getHighlightColor();
904
905 temp.nonMarket = false;
906
907// dialog.getVisualPanel().showPlanetInfo(market.getPrimaryEntity());
908// dialog.getVisualPanel().finishFadeFast();
909 dialog.getVisualPanel().showImagePortion("illustrations", "raid_prepare", 640, 400, 0, 0, 480, 300);
910
911 float marines = playerFleet.getCargo().getMarines();
913 if (support > marines) support = marines;
914
915 StatBonus attackerBase = new StatBonus();
916 StatBonus defenderBase = new StatBonus();
917
918 //defenderBase.modifyFlatAlways("base", baseDef, "Base value for a size " + market.getSize() + " colony");
919
920 attackerBase.modifyFlatAlways("core_marines", marines, "Marines on board");
921 attackerBase.modifyFlatAlways("core_support", support, "Fleet capability for ground support");
922
925
926 String surpriseKey = "core_surprise";
927// if (temp.isSurpriseRaid) {
928// //defender.modifyMult(surpriseKey, 0.1f, "Surprise raid");
929// attacker.modifyMult(surpriseKey, SURPRISE_RAID_STRENGTH_MULT, "Surprise raid");
930// }
931
932 String increasedDefensesKey = "core_addedDefStr";
933 float added = getDefenderIncreaseValue(market);
934 if (added > 0) {
935 defender.modifyFlat(increasedDefensesKey, added, "Increased defender preparedness");
936 }
937
938 float attackerStr = (int) Math.round(attacker.computeEffective(attackerBase.computeEffective(0f)));
939 float defenderStr = (int) Math.round(defender.computeEffective(defenderBase.computeEffective(0f)));
940
941 temp.attackerStr = attackerStr;
942 temp.defenderStr = defenderStr;
943
945
947
948 String has = faction.getDisplayNameHasOrHave();
949 String is = faction.getDisplayNameIsOrAre();
950 boolean hostile = faction.isHostileTo(Factions.PLAYER);
951 boolean tOn = playerFleet.isTransponderOn();
952 float initPad = 0f;
953 if (!hostile) {
954 if (tOn) {
956 " not currently hostile. Your fleet's transponder is on, and carrying out a raid " +
957 "will result in open hostilities.",
959 } else {
961 " not currently hostile. Your fleet's transponder is off, and carrying out a raid " +
962 "will only result in a minor penalty to your standing.",
964 }
965 initPad = opad;
966 }
967
968 float sep = small;
969 sep = 3f;
970 info.addPara("Raid strength: %s", initPad, h, "" + (int)attackerStr);
971 info.addStatModGrid(width, 50, opad, small, attackerBase, true, statPrinter(false));
972 if (!attacker.isUnmodified()) {
973 info.addStatModGrid(width, 50, opad, sep, attacker, true, statPrinter(true));
974 }
975
976
977 info.addPara("Ground defense strength: %s", opad, h, "" + (int)defenderStr);
978 //info.addStatModGrid(width, 50, opad, small, defenderBase, true, statPrinter());
979 //if (!defender.isUnmodified()) {
980 info.addStatModGrid(width, 50, opad, small, defender, true, statPrinter(true));
981 //}
982
983 defender.unmodifyFlat(increasedDefensesKey);
984 defender.unmodifyMult(surpriseKey);
985 attacker.unmodifyMult(surpriseKey);
986
988
989 boolean hasForces = true;
990 boolean canDisrupt = true;
991 temp.raidMult = attackerStr / Math.max(1f, (attackerStr + defenderStr));
992 temp.raidMult = Math.round(temp.raidMult * 100f) / 100f;
993 //temp.raidMult = 1f;
994
995
996
997
998 {
999 //temp.failProb = 0f;
1000 Color eColor = h;
1001 if (temp.raidMult < DISRUPTION_THRESHOLD && temp.raidMult < VALUABLES_THRESHOLD) {
1003 }
1004 if (temp.raidMult < DISRUPTION_THRESHOLD) {
1005 //eColor = Misc.getNegativeHighlightColor();
1006 canDisrupt = false;
1007 //temp.canFail = true;
1008 } else if (temp.raidMult >= 0.7f) {
1009 //eColor = Misc.getPositiveHighlightColor();
1010 }
1011// text.addPara("Projected raid effectiveness: %s. " +
1012// "This will determine the outcome of the raid, " +
1013// "as well as the casualties suffered by your forces, if any.",
1014// eColor,
1015// "" + (int)Math.round(temp.raidMult * 100f) + "%");
1016 text.addPara("Projected raid effectiveness: %s",
1017 eColor,
1018 "" + (int)(temp.raidMult * 100f) + "%");
1019 //"" + (int)Math.round(temp.raidMult * 100f) + "%");
1020 if (!canDisrupt) {
1021 text.addPara("The ground defenses are too strong for your forces to be able to cause long-term disruption.");
1022 }
1023 if (temp.raidMult < VALUABLES_THRESHOLD) {
1024 text.addPara("You do not have the forces to carry out an effective raid to acquire valuables or achieve other objectives.");
1025 hasForces = false;
1026 }
1027// if (canDisrupt) {
1028// } else {
1029// text.addPara("Projected raid effectiveness: %s. " +
1030// "This will determine the outcome of the raid, " +
1031// "as well as the casualties suffered by your forces, if any.",
1032// eColor,
1033// "" + (int)Math.round(temp.raidMult * 100f) + "%");
1034// }
1035 }
1036
1038 canDisrupt = true;
1039 }
1040
1042
1043 //options.addOption("Try to acquire rare items, such as blueprints", RAID_RARE);
1044 //options.addOption("Try to acquire valuables, such as commodities, blueprints, and other items", RAID_VALUABLE);
1045 options.addOption("Try to acquire valuables, such as commodities or blueprints, or achieve other objectives", RAID_VALUABLE);
1046 options.addOption("Disrupt the operations of a specific industry or facility", RAID_DISRUPT);
1047
1048 if (!hasForces) {
1050 //options.setEnabled(RAID_RARE, false);
1051 }
1052
1053 if (!hasForces || !canDisrupt) {
1055 if (!canDisrupt) {
1056 String pct = "" + (int)Math.round(DISRUPTION_THRESHOLD * 100f) + "%";
1057 options.setTooltip(RAID_DISRUPT, "Requires at least " + pct + " raid effectiveness.");
1060 }
1061 }
1062
1063 options.addOption("Go back", RAID_GO_BACK);
1064 options.setShortcut(RAID_GO_BACK, Keyboard.KEY_ESCAPE, false, false, false, true);
1065 }
1066
1067// protected void raidRare() {
1068//
1069// }
1070
1071 protected void raidValuable() {
1072 temp.raidType = RaidType.VALUABLE;
1073
1074 List<GroundRaidObjectivePlugin> obj = new ArrayList<GroundRaidObjectivePlugin>();
1075
1076 // See: StandardGroundRaidObjectivesCreator; it creates the standard objectives with priority 0 below
1077 final RaidType useType = !temp.nonMarket ? temp.raidType : RaidType.CUSTOM_ONLY;
1078 //if (temp.nonMarket) useType = RaidType.CUSTOM_ONLY;
1079 for (int i = 0; i < 10; i++) {
1081 }
1082
1083 if (obj.isEmpty()) {
1084 text.addPara("After careful consideration, there do not appear to be any targets " +
1085 "likely to yield anything of value.");
1087 return;
1088 }
1089
1090
1091 dialog.showGroundRaidTargetPicker("Select raid objectives", "Select", market, obj,
1093 public void pickedGroundRaidTargets(List<GroundRaidObjectivePlugin> data) {
1094 float value = 0;
1095 for (GroundRaidObjectivePlugin curr : data) {
1096 value += curr.getProjectedCreditsValue();
1097 }
1098 Color h = Misc.getHighlightColor();
1099 List<String> names = new ArrayList<String>();
1100 for (GroundRaidObjectivePlugin curr : data) {
1101 names.add(curr.getNameOverride() != null ? curr.getNameOverride() : curr.getName());
1102 }
1103 String list = Misc.getAndJoined(names);
1104 String item = "objective";
1105 if (names.size() > 1) {
1106 item = "objectives";
1107 }
1108
1109 String isOrAre = "are";
1110 String marinesStr = "marines";
1111 if (playerCargo.getMarines() == 1) {
1112 isOrAre = "is";
1113 marinesStr = "marine";
1114 }
1115 //float losses = getProjectedMarineLossesFloat();
1116 LabelAPI label = text.addPara("Your marine commander submits a plan for your approval. Losses during this " +
1117 "operation are projected to be %s. There " + isOrAre + " a total of %s " +
1118 marinesStr + " in your fleet.",
1119 getMarineLossesColor(data), getProjectedMarineLosses(data).toLowerCase(),
1121 label.setHighlightColors(getMarineLossesColor(data), Misc.getHighlightColor());
1122 text.addPara(Misc.ucFirst(item) + " targeted: " + list + ".", h,
1123 names.toArray(new String[0]));
1124 if (value > 0) {
1125 text.addPara("The estimated value of the items obtained is projected to be around %s.",
1126 h, Misc.getDGSCredits(value));
1127 }
1128
1129// text.addPara("The marines are ready to go, awaiting your final confirmation. There are a total of %s " +
1130// "marines in your fleet.", Misc.getHighlightColor(), Misc.getWithDGS(playerCargo.getMarines()));
1131 text.addPara("The marines are ready to go, awaiting your final confirmation.");
1132 temp.objectives = data;
1134 }
1135
1136 public boolean isDisruptIndustryMode() {
1137 return false;
1138 }
1139
1140 public boolean isCustomOnlyMode() {
1141 return useType == RaidType.CUSTOM_ONLY;
1142 }
1143
1144 public void cancelledGroundRaidTargetPicking() {
1145
1146 }
1147
1148 public int getCargoSpaceNeeded(List<GroundRaidObjectivePlugin> data) {
1149 float total = 0;
1150 for (GroundRaidObjectivePlugin curr : data) {
1151 total += curr.getCargoSpaceNeeded();
1152 }
1153 return (int) total;
1154 }
1155
1156 public int getFuelSpaceNeeded(List<GroundRaidObjectivePlugin> data) {
1157 float total = 0;
1158 for (GroundRaidObjectivePlugin curr : data) {
1159 total += curr.getFuelSpaceNeeded();
1160 }
1161 return (int) total;
1162 }
1163
1164 public int getProjectedCreditsValue(List<GroundRaidObjectivePlugin> data) {
1165 float total = 0;
1166 for (GroundRaidObjectivePlugin curr : data) {
1167 total += curr.getProjectedCreditsValue();
1168 }
1169 return (int) total;
1170 }
1171
1172 public int getNumMarineTokens() {
1173 return MarketCMD.this.getNumMarineTokens();
1174 }
1175
1176 public MutableStat getMarineLossesStat(List<GroundRaidObjectivePlugin> data) {
1177 return MarketCMD.this.getMarineLossesStat(data);
1178 }
1179
1180 public String getProjectedMarineLosses(List<GroundRaidObjectivePlugin> data) {
1181 //return "" + (int) Math.round(getProjectedMarineLossesFloat());
1182 float marines = playerFleet.getCargo().getMarines();
1183 float losses = getAverageMarineLosses(data);
1184
1185 float f = losses / Math.max(1f, marines);
1186
1187 for (RaidDangerLevel level : RaidDangerLevel.values()) {
1188 float test = level.marineLossesMult + (level.next().marineLossesMult - level.marineLossesMult) * 0.5f;
1189 if (level == RaidDangerLevel.NONE) test = RaidDangerLevel.NONE.marineLossesMult;
1190 if (test >= f) {
1191 return level.lossesName;
1192 }
1193 }
1194 return RaidDangerLevel.EXTREME.lossesName;
1195 }
1196
1197 public float getAverageMarineLosses(List<GroundRaidObjectivePlugin> data) {
1198 return MarketCMD.this.getAverageMarineLosses(data);
1199 }
1200
1201 public Color getMarineLossesColor(List<GroundRaidObjectivePlugin> data) {
1202 float marines = playerFleet.getCargo().getMarines();
1203 float losses = getAverageMarineLosses(data);
1204
1205
1206 float f = losses / Math.max(1f, marines);
1207 if (f <= 0 && data.isEmpty()) return Misc.getGrayColor();
1208
1209 for (RaidDangerLevel level : RaidDangerLevel.values()) {
1210 float test = level.marineLossesMult + (level.next().marineLossesMult - level.marineLossesMult) * 0.5f;
1211 if (test >= f) {
1212 return level.color;
1213 }
1214 }
1215 return RaidDangerLevel.EXTREME.color;
1216 }
1217 public String getRaidEffectiveness() {
1218 return "" + (int)(temp.raidMult * 100f) + "%";
1219 }
1220 });
1221 }
1222
1223 protected void addBombardConfirmOptions() {
1225 options.addOption("Launch bombardment", BOMBARD_CONFIRM);
1226 options.addOption("Never mind", BOMBARD_NEVERMIND);
1227 options.setShortcut(BOMBARD_NEVERMIND, Keyboard.KEY_ESCAPE, false, false, false, true);
1228
1229 List<FactionAPI> nonHostile = new ArrayList<FactionAPI>();
1230 for (FactionAPI faction : temp.willBecomeHostile) {
1231 boolean hostile = faction.isHostileTo(Factions.PLAYER);
1232 if (!hostile) {
1233 nonHostile.add(faction);
1234 }
1235 }
1236
1237 if (nonHostile.size() == 1) {
1238 FactionAPI faction = nonHostile.get(0);
1240 "The " + faction.getDisplayNameLong() +
1242 " not currently hostile, and will become hostile if you carry out the bombardment. " +
1243 "Are you sure?", "Yes", "Never mind");
1244 } else if (nonHostile.size() > 1) {
1246 "Multiple factions that are not currently hostile " +
1247 "will become hostile if you carry out the bombardment. " +
1248 "Are you sure?", "Yes", "Never mind");
1249 }
1250 }
1251
1252 protected void raidDisrupt() {
1253 temp.raidType = RaidType.DISRUPT;
1254
1255 // See: StandardGroundRaidObjectivesCreator; it creates the standard objectives with priority 0 below
1256 List<GroundRaidObjectivePlugin> obj = new ArrayList<GroundRaidObjectivePlugin>();
1257 for (int i = 0; i < 10; i++) {
1259 }
1260
1261 if (obj.isEmpty()) {
1262 text.addPara("There are no industries or facilities present that could be disrupted by a raid.");
1264 return;
1265 }
1266
1267
1268 dialog.showGroundRaidTargetPicker("Select raid objectives", "Select", market, obj,
1270 public void pickedGroundRaidTargets(List<GroundRaidObjectivePlugin> data) {
1271 float value = 0;
1272 for (GroundRaidObjectivePlugin curr : data) {
1273 value += curr.getProjectedCreditsValue();
1274 }
1275 Color h = Misc.getHighlightColor();
1276 List<String> names = new ArrayList<String>();
1277 for (GroundRaidObjectivePlugin curr : data) {
1278 names.add(curr.getNameOverride() != null ? curr.getNameOverride() : curr.getName());
1279 }
1280 String list = Misc.getAndJoined(names);
1281 String item = "objective";
1282 if (names.size() > 1) {
1283 item = "objectives";
1284 }
1285
1286 //float losses = getProjectedMarineLossesFloat();
1287
1288 text.addPara("Your marine commander submits a plan for your approval. Losses during this " +
1289 "operation are projected to be %s.",
1290 getMarineLossesColor(data), getProjectedMarineLosses(data).toLowerCase());
1291 text.addPara(Misc.ucFirst(item) + " targeted: " + list + ".", h,
1292 names.toArray(new String[0]));
1293
1294 if (value > 0) {
1295 text.addPara("The estimated value of the items obtained is projected to be around %s.",
1296 h, Misc.getDGSCredits(value));
1297 }
1298
1299 text.addPara("The marines are ready to go, awaiting your final confirmation. There are a total of %s " +
1300 "marines in your fleet.", Misc.getHighlightColor(), Misc.getWithDGS(playerCargo.getMarines()));
1301 temp.objectives = data;
1303 }
1304
1305 public boolean isDisruptIndustryMode() {
1306 return true;
1307 }
1308
1309 public void cancelledGroundRaidTargetPicking() {
1310
1311 }
1312
1313 public int getCargoSpaceNeeded(List<GroundRaidObjectivePlugin> data) {
1314 float total = 0;
1315 for (GroundRaidObjectivePlugin curr : data) {
1316 total += curr.getCargoSpaceNeeded();
1317 }
1318 return (int) total;
1319 }
1320
1321 public int getFuelSpaceNeeded(List<GroundRaidObjectivePlugin> data) {
1322 float total = 0;
1323 for (GroundRaidObjectivePlugin curr : data) {
1324 total += curr.getFuelSpaceNeeded();
1325 }
1326 return (int) total;
1327 }
1328
1329 public int getProjectedCreditsValue(List<GroundRaidObjectivePlugin> data) {
1330 float total = 0;
1331 for (GroundRaidObjectivePlugin curr : data) {
1332 total += curr.getProjectedCreditsValue();
1333 }
1334 return (int) total;
1335 }
1336
1337 public int getNumMarineTokens() {
1338 return MarketCMD.this.getNumMarineTokens();
1339 }
1340
1341 public MutableStat getMarineLossesStat(List<GroundRaidObjectivePlugin> data) {
1342 return MarketCMD.this.getMarineLossesStat(data);
1343 }
1344
1345 public String getProjectedMarineLosses(List<GroundRaidObjectivePlugin> data) {
1346 //return "" + (int) Math.round(getProjectedMarineLossesFloat());
1347 float marines = playerFleet.getCargo().getMarines();
1348 float losses = getAverageMarineLosses(data);
1349
1350 float f = losses / Math.max(1f, marines);
1351
1352 for (RaidDangerLevel level : RaidDangerLevel.values()) {
1353 float test = level.marineLossesMult + (level.next().marineLossesMult - level.marineLossesMult) * 0.5f;
1354 if (level == RaidDangerLevel.NONE) test = RaidDangerLevel.NONE.marineLossesMult;
1355 if (test >= f) {
1356 return level.lossesName;
1357 }
1358 }
1359 return RaidDangerLevel.EXTREME.lossesName;
1360 }
1361
1362 public float getAverageMarineLosses(List<GroundRaidObjectivePlugin> data) {
1363 return MarketCMD.this.getAverageMarineLosses(data);
1364 }
1365
1366 public Color getMarineLossesColor(List<GroundRaidObjectivePlugin> data) {
1367 float marines = playerFleet.getCargo().getMarines();
1368 float losses = getAverageMarineLosses(data);
1369
1370
1371 float f = losses / Math.max(1f, marines);
1372 if (f <= 0) return Misc.getGrayColor();
1373
1374 for (RaidDangerLevel level : RaidDangerLevel.values()) {
1375 float test = level.marineLossesMult + (level.next().marineLossesMult - level.marineLossesMult) * 0.5f;
1376 if (test >= f) {
1377 return level.color;
1378 }
1379 }
1380 return RaidDangerLevel.EXTREME.color;
1381 }
1382 public String getRaidEffectiveness() {
1383 return "" + (int)(temp.raidMult * 100f) + "%";
1384 }
1385
1386 public boolean isCustomOnlyMode() {
1387 // TODO Auto-generated method stub
1388 return false;
1389 }
1390 });
1391
1392
1393// dialog.showIndustryPicker("Select raid target", "Select", market, targets, new IndustryPickerListener() {
1394// public void pickedIndustry(Industry industry) {
1395// raidDisruptIndustryPicked(industry);
1396// }
1397// public void cancelledIndustryPicking() {
1398//
1399// }
1400// });
1401 }
1402
1404 //float dur = getNumMarineTokens() * Global.getSettings().getFloat("raidDisruptDurationPerMarineToken") - ind.getDisruptedDays();
1405 float dur = getNumMarineTokens() * ind.getSpec().getDisruptDanger().disruptionDays - ind.getDisruptedDays();
1406 return (int) dur;
1407 }
1408
1409 public static int getBombardDestroyThreshold() {
1410 return Global.getSettings().getInt("bombardSaturationDestroySize");
1411
1412 }
1413 public static int getBombardDisruptDuration() {
1414 float dur = Global.getSettings().getFloat("bombardDisruptDuration");
1415 return (int) dur;
1416 }
1417
1418 protected void raidDisruptIndustryPicked(Industry target) {
1419 temp.target = target;
1420 text.addParagraph("Target: " + target.getCurrentName(), Global.getSettings().getColor("buttonText"));
1421
1422 float dur = computeBaseDisruptDuration(target);
1423
1424 Color h = Misc.getHighlightColor();
1425
1426 float already = target.getDisruptedDays();
1427 if (already > 0) {
1428 text.addPara(target.getNameForModifier() + " operations are already disrupted, and a raid will have " +
1429 "reduced effect.");
1430 }
1431
1432 text.addPara("Your ground forces commander estimates that given the relative force strengths, " +
1433 " the raid should disrupt all " + target.getCurrentName() + " operations for at least %s days.",
1434 h, "" + (int) Misc.getRounded(dur));
1435
1436 text.addPara("Your forces are ready to go, awaiting your final confirmation.");
1437
1439
1441 }
1442
1443
1444 protected void addNeverMindOption() {
1446 options.addOption("Never mind", RAID_NEVER_MIND);
1447 options.setShortcut(RAID_NEVER_MIND, Keyboard.KEY_ESCAPE, false, false, false, true);
1448 }
1449
1450 protected void addBombardNeverMindOption() {
1452 options.addOption("Never mind", BOMBARD_NEVERMIND);
1453 options.setShortcut(BOMBARD_NEVERMIND, Keyboard.KEY_ESCAPE, false, false, false, true);
1454 }
1455
1456 protected void addContinueOption() {
1457 addContinueOption(null);
1458 }
1459 protected void addContinueOption(String text) {
1460 if (text == null) text = "Continue";
1463 }
1464
1465
1466 public static final String DEFENDER_INCREASE_KEY = "$core_defenderIncrease";
1468 if (market == null) return 0f;
1470 if (e < 0) e = 0;
1471 return e;
1472 }
1473
1476 if(e < 0) e = 0;
1478 float max = getRaidDefenderIncreaseMax();
1479 if (e > max) e = max;
1480
1483 }
1484
1486 float e = getDefenderIncreaseRaw(market);
1488 float min = getRaidDefenderIncreaseMin();
1489
1491 float incr = Math.max(base * f, min);
1492
1493 float per = getRaidDefenderIncreasePerRaid();
1494
1495 return (int)(incr * e / per);
1496 }
1497
1498 protected static float getRaidDefenderIncreasePerRaid() {
1499 return Global.getSettings().getFloat("raidDefenderIncreasePerRaid");
1500 }
1501 protected static float getRaidDefenderIncreaseMax() {
1502 return Global.getSettings().getFloat("raidDefenderIncreaseMax");
1503 }
1504 protected static float getRaidDefenderIncreaseFraction() {
1505 return Global.getSettings().getFloat("raidDefenderIncreaseFraction");
1506 }
1507 protected static float getRaidDefenderIncreaseMin() {
1508 return Global.getSettings().getFloat("raidDefenderIncreaseMin");
1509 }
1510
1511
1512 protected float getRaidCooldownMax() {
1513 return Global.getSettings().getFloat("raidCooldownDays");
1514 }
1515
1516 protected void setRaidCooldown(float cooldown) {
1517 String key = "$raid_cooldown";
1518 Global.getSector().getMemoryWithoutUpdate().set(key, true, cooldown);
1519 }
1520
1521 protected float getRaidCooldown() {
1522 String key = "$raid_cooldown";
1524 }
1525
1526 protected Random getRandom() {
1527 String key = "$raid_random";
1528 MemoryAPI mem = null;
1530 if (market != null) {
1533 } else {
1534 entity = this.entity;
1536 }
1537 Random random = null;
1538 if (mem.contains(key)) {
1539 random = (Random) mem.get(key);
1540 } else {
1541 if (entity != null) {
1542 long seed = Misc.getSalvageSeed(entity);
1543 seed /= 321L;
1544 seed *= (Global.getSector().getClock().getMonth() + 10);
1545 random = new Random(seed);
1546 } else {
1547 random = new Random();
1548 }
1549 }
1550 mem.set(key, random, 30f);
1551
1552 return random;
1553 }
1554
1555
1556 public int getNumMarineTokens() {
1557 //if (true) return MAX_MARINE_TOKENS;
1558 int num = (int) Math.round(temp.raidMult / RE_PER_MARINE_TOKEN);
1559 if (num < MIN_MARINE_TOKENS) num = MIN_MARINE_TOKENS;
1560 if (num > MAX_MARINE_TOKENS) num = MAX_MARINE_TOKENS;
1561 return num;
1562 }
1563
1564 protected MutableStat getMarineLossesStat(List<GroundRaidObjectivePlugin> data) {
1565 MutableStat stat = new MutableStat(1f);
1566
1567 float total = 0f;
1568 float assignedTokens = 0f;
1569 for (GroundRaidObjectivePlugin curr : data) {
1570 RaidDangerLevel danger = curr.getDangerLevel();
1571 total += danger.marineLossesMult * (float) curr.getMarinesAssigned();
1572 assignedTokens += curr.getMarinesAssigned();
1573 }
1574
1575 float danger = total / Math.max(1f, assignedTokens);
1576
1577 float hazard = 1f;
1578 if (market != null) hazard = market.getHazardValue();
1579
1580 float reMult = 1f;
1581 if (temp.raidMult > MIN_RE_TO_REDUCE_MARINE_LOSSES) {
1582 float extra = (temp.raidMult - MIN_RE_TO_REDUCE_MARINE_LOSSES) / (1f - MIN_RE_TO_REDUCE_MARINE_LOSSES);
1583// extra = (float) Math.sqrt(extra);
1584// extra *= 0.5f;
1585 extra = MAX_MARINE_LOSS_REDUCTION_MULT + (1f - MAX_MARINE_LOSS_REDUCTION_MULT) * (1f - extra);
1586 reMult = extra;
1587 } else if (temp.raidMult < RE_PER_MARINE_TOKEN) {
1588 float extra = 1f + (RE_PER_MARINE_TOKEN - temp.raidMult) / RE_PER_MARINE_TOKEN;
1589 reMult = extra;
1590 }
1591
1592 if (market != null && reMult < 1f) {
1593 float minMarinesForAssignedTokens = getMarinesFor(market, (int) Math.round(assignedTokens));
1594 float actualMarines = Global.getSector().getPlayerFleet().getCargo().getMarines();
1595 if (actualMarines > minMarinesForAssignedTokens && actualMarines > 0) {
1596 reMult *= 0.5f + (0.5f * minMarinesForAssignedTokens / actualMarines);
1597 }
1598 }
1599
1600 float reservesMult = 1f;
1601 float maxTokens = getNumMarineTokens();
1602 if (maxTokens > assignedTokens) {
1603 reservesMult = 1f - (maxTokens - assignedTokens) * LOSS_REDUCTION_PER_RESERVE_TOKEN;
1604 reservesMult = Math.max(0.5f, reservesMult);
1605 }
1606
1607 float e = getDefenderIncreaseRaw(market);
1608 float per = getRaidDefenderIncreasePerRaid();
1609 float prep = e / per * LOSS_INCREASE_PER_RAID;
1610
1611 stat.modifyMultAlways("danger", danger, "Danger level of objectives");
1612 stat.modifyMult("hazard", hazard, "Colony hazard rating");
1613 if (reMult < 1f) {
1614 stat.modifyMultAlways("reMult", reMult, "High raid effectiveness");
1615 } else if (reMult > 1f) {
1616 stat.modifyMultAlways("reMult", reMult, "Low raid effectiveness");
1617 }
1618
1619 if (reservesMult < 1f && assignedTokens > 0) {
1620 stat.modifyMultAlways("reservesMult", reservesMult, "Forces held in reserve");
1621 }
1622// else if (reservesMult >= 1f && assignedTokens > 0) {
1623// stat.modifyMultAlways("reservesMult", 1f, "No forces held in reserve");
1624// }
1625
1626 stat.modifyMult("prep", 1f + prep, "Increased defender preparedness");
1627
1629
1631
1632 return stat;
1633 }
1634
1635
1636
1637 protected float getAverageMarineLosses(List<GroundRaidObjectivePlugin> data) {
1638 MutableStat stat = getMarineLossesStat(data);
1639 float mult = stat.getModifiedValue();
1640 if (mult > MAX_MARINE_LOSSES) {
1641 mult = MAX_MARINE_LOSSES;
1642 }
1643
1644 float marines = playerFleet.getCargo().getMarines();
1645 return marines * mult;
1646 }
1647
1648 protected void addMilitaryResponse() {
1649 if (market == null) return;
1650
1652 MilitaryResponseParams params = new MilitaryResponseParams(ActionType.HOSTILE,
1653 "player_ground_raid_" + market.getId(),
1656 0.75f,
1657 30f);
1659 }
1660 List<CampaignFleetAPI> fleets = market.getContainingLocation().getFleets();
1661 for (CampaignFleetAPI other : fleets) {
1662 if (other.getFaction() == market.getFaction()) {
1663 MemoryAPI mem = other.getMemoryWithoutUpdate();
1665 }
1666 }
1667 }
1668
1669 protected void raidConfirm(boolean secret) {
1670 if (temp.raidType == null) {
1671 raidNeverMind();
1672 return;
1673 }
1674
1675 temp.secret = secret;
1676
1677// if (temp.raidType == RaidType.VALUABLE) {
1678// dialog.getVisualPanel().showImagePortion("illustrations", "raid_valuables_result", 640, 400, 0, 0, 480, 300);
1679// } else if (temp.raidType == RaidType.DISRUPT) {
1680// dialog.getVisualPanel().showImagePortion("illustrations", "raid_disrupt_result", 640, 400, 0, 0, 480, 300);
1681// }
1682
1683 Random random = getRandom();
1684 //random = new Random();
1685
1688 }
1689
1691
1692
1693 // if done here, increases marine casualties from this raid - move it down later
1694// if (market != null) {
1695// applyDefenderIncreaseFromRaid(market);
1696// }
1697
1699
1700 //RecentUnrest.get(market).add(3, Misc.ucFirst(reason));
1701 int stabilityPenalty = 0;
1702 if (!temp.nonMarket) {
1703 String reason = "Recently raided";
1704 if (Misc.isPlayerFactionSetUp()) {
1705 reason = playerFaction.getDisplayName() + " raid";
1706 }
1707 float raidMultForStabilityPenalty = temp.raidMult;
1708 if (temp.objectives != null) {
1709 float assignedTokens = 0f;
1710 for (GroundRaidObjectivePlugin curr : temp.objectives) {
1711 assignedTokens += curr.getMarinesAssigned();
1712 }
1713 raidMultForStabilityPenalty = assignedTokens * 0.1f;
1714 }
1715
1716 stabilityPenalty = applyRaidStabiltyPenalty(market, reason, raidMultForStabilityPenalty);
1718 Factions.PLAYER, true, 30f);
1720 }
1721
1722 int marines = playerFleet.getCargo().getMarines();
1723 float probOfLosses = 1f;
1724
1725 int losses = 0;
1726 if (random.nextFloat() < probOfLosses) {
1727 float averageLosses = getAverageMarineLosses(temp.objectives);
1728 float variance = averageLosses / 4f;
1729
1730 //float randomizedLosses = averageLosses - variance + variance * 2f * random.nextFloat();
1731 float randomizedLosses = StarSystemGenerator.getNormalRandom(
1732 random, averageLosses - variance, averageLosses + variance);
1733 if (randomizedLosses < 1f) {
1734 randomizedLosses = random.nextFloat() < randomizedLosses ? 1f : 0f;
1735 }
1736 randomizedLosses = Math.round(randomizedLosses);
1737 losses = (int) randomizedLosses;
1738
1739 if (losses < 0) losses = 0;
1740 if (losses > marines) losses = marines;
1741 }
1742
1743 //losses = random.nextInt(marines / 2);
1744
1745 if (losses <= 0) {
1746 text.addPara("Your forces have not suffered any casualties.");
1747 temp.marinesLost = 0;
1748 } else {
1749 text.addPara("You forces have suffered casualties during the raid.", Misc.getHighlightColor(), "" + losses);
1751 temp.marinesLost = losses;
1753 }
1754
1755
1756 if (!secret) {
1757 boolean tOn = playerFleet.isTransponderOn();
1758 boolean hostile = faction.isHostileTo(Factions.PLAYER);
1759 CustomRepImpact impact = new CustomRepImpact();
1760 if (market != null) {
1761 impact.delta = market.getSize() * -0.01f * 1f;
1762 } else {
1763 impact.delta = -0.01f;
1764 }
1765 if (!hostile && tOn) {
1766 impact.ensureAtBest = RepLevel.HOSTILE;
1767 }
1768 if (impact.delta != 0 && !faction.isNeutralFaction()) {
1770 new RepActionEnvelope(RepActions.CUSTOM,
1771 impact, null, text, true, true),
1772 faction.getId());
1773 }
1774 }
1775
1776 if (stabilityPenalty > 0) {
1777 text.addPara("Stability of " + market.getName() + " reduced by %s.",
1778 Misc.getHighlightColor(), "" + stabilityPenalty);
1779 }
1780
1781// if (!temp.nonMarket) {
1782// if (temp.raidType == RaidType.VALUABLE || true) {
1783// text.addPara("The raid was successful in achieving its objectives.");
1784// }
1785// }
1786
1787 CargoAPI result = performRaid(random, temp.raidMult);
1788
1789 if (market != null) market.reapplyIndustries();
1790
1791 result.sort();
1792 result.updateSpaceUsed();
1793
1794 temp.raidLoot = result;
1795
1796// int raidCredits = (int)result.getCredits().get();
1797// if (raidCredits < 0) raidCredits = 0;
1798//
1799// //result.clear();
1800// if (raidCredits > 0) {
1801// AddRemoveCommodity.addCreditsGainText(raidCredits, text);
1802// playerFleet.getCargo().getCredits().add(raidCredits);
1803// }
1804
1805 if (temp.xpGained > 0) {
1807 }
1808 if (temp.raidType == RaidType.VALUABLE) {
1809 if (result.getTotalCrew() + result.getSpaceUsed() + result.getFuel() < 10) {
1810 dialog.getVisualPanel().showImagePortion("illustrations", "raid_covert_result", 640, 400, 0, 0, 480, 300);
1811 } else {
1812 dialog.getVisualPanel().showImagePortion("illustrations", "raid_valuables_result", 640, 400, 0, 0, 480, 300);
1813 }
1814 } else if (temp.raidType == RaidType.DISRUPT) {
1815 dialog.getVisualPanel().showImagePortion("illustrations", "raid_disrupt_result", 640, 400, 0, 0, 480, 300);
1816 }
1817
1818 boolean withContinue = false;
1819
1820 for (GroundRaidObjectivePlugin curr : temp.objectives) {
1821 if (curr.withContinueBeforeResult()) {
1822 withContinue = true;
1823 break;
1824 }
1825 }
1826
1827 if (market != null) {
1829 }
1830
1831// if (market.getMemoryWithoutUpdate().getBoolean("$raid_showContinueBeforeResult"))
1832// withContinue = true;
1833
1834 if (withContinue) {
1837 } else {
1839 }
1840 }
1841
1842 public void raidConfirmContinue() {
1843 //Random random = getRandom();
1844 String contText = null;
1845 if (temp.raidType == RaidType.VALUABLE || true) {
1846 if (!temp.nonMarket) {
1847 if (temp.raidType == RaidType.VALUABLE || true) {
1848 //text.addPara("The raid was successful in achieving its objectives.");
1849 }
1850 }
1851
1852// CargoAPI result = performRaid(random, temp.raidMult);
1853//
1854// if (market != null) market.reapplyIndustries();
1855//
1856// result.sort();
1857//
1858// temp.raidLoot = result;
1859
1860 int raidCredits = (int)temp.raidLoot.getCredits().get();
1861 if (raidCredits < 0) raidCredits = 0;
1862
1863 //result.clear();
1864 if (raidCredits > 0) {
1866 playerFleet.getCargo().getCredits().add(raidCredits);
1867 }
1868
1869// if (temp.xpGained > 0) {
1870// Global.getSector().getPlayerStats().addXP(temp.xpGained, dialog.getTextPanel());
1871// }
1872
1873 if (!temp.raidLoot.isEmpty()) {
1874 contText = "Pick through the spoils";
1875 }
1876 temp.contText = contText;
1877
1878 float assignedTokens = 0f;
1879 List<Industry> disrupted = new ArrayList<Industry>();
1880 for (GroundRaidObjectivePlugin curr : temp.objectives) {
1881 assignedTokens += curr.getMarinesAssigned();
1882 if (curr instanceof DisruptIndustryRaidObjectivePluginImpl && curr.getSource() != null) {
1883 disrupted.add(curr.getSource());
1884 }
1885 }
1886
1887 RaidResultData data = new RaidResultData();
1888 data.market = market;
1889 data.entity = entity;
1890 data.objectives = temp.objectives;
1891 data.type = temp.raidType;
1892 data.raidEffectiveness = temp.raidMult;
1893 data.xpGained = temp.xpGained;
1894 data.marinesTokensInReserve = (int) Math.round(getNumMarineTokens() - assignedTokens);
1895 data.marinesTokens = getNumMarineTokens();
1896 data.marinesLost = temp.marinesLost;
1897
1899
1900 if (temp.raidType == RaidType.VALUABLE) {
1902 } else if (temp.raidType == RaidType.DISRUPT) {
1903 for (Industry curr : disrupted) {
1905 }
1906 }
1907
1908 }
1909
1910 Global.getSoundPlayer().playUISound("ui_raid_finished", 1f, 1f);
1911
1912 FireBest.fire(null, dialog, memoryMap, "PostGroundRaid");
1913 }
1914
1915 protected CargoAPI performRaid(Random random, float raidEffectiveness) {
1916 CargoAPI result = Global.getFactory().createCargo(true);
1917
1918 float leftoverRE = (int)Math.round(raidEffectiveness * 100f) % (int)Math.round(RE_PER_MARINE_TOKEN * 100f);
1919 leftoverRE /= 100f;
1920 if (raidEffectiveness < RE_PER_MARINE_TOKEN) {
1921 //leftoverRE = leftoverRE - RE_PER_MARINE_TOKEN;
1922 leftoverRE = 0f;
1923 }
1924
1925 long baseSeed = random.nextLong();
1926
1927 int xp = 0;
1928 for (GroundRaidObjectivePlugin plugin : temp.objectives) {
1929 float lootMult = 1f + leftoverRE / Math.max(RE_PER_MARINE_TOKEN, raidEffectiveness);
1930
1931 Random curr = new Random(Misc.seedUniquifier() ^ (baseSeed * plugin.getClass().getName().hashCode()));
1932 xp += plugin.performRaid(result, curr, lootMult, dialog.getTextPanel());
1933 }
1934
1935 temp.xpGained = xp;
1936
1937 return result;
1938 }
1939
1940
1941 protected void raidNeverMind() {
1942 if (temp.nonMarket) {
1943 raidNonMarket();
1944 } else {
1945 raidMenu();
1946 }
1947 }
1948
1949
1950 protected void raidShowLoot() {
1951 dialog.getVisualPanel().showLoot("Spoils", temp.raidLoot, false, true, true, new CoreInteractionListener() {
1952 public void coreUIDismissed() {
1953 //dialog.dismiss();
1955 }
1956 });
1957 }
1958
1959
1960 protected void printStationState() {
1961 StationState state = getStationState();
1962 if (state == StationState.REPAIRS || state == StationState.UNDER_CONSTRUCTION) {
1964 String name = "orbital station";
1965 if (fleet != null) {
1966 FleetMemberAPI flagship = fleet.getFlagship();
1967 if (flagship != null) {
1968 name = flagship.getVariant().getDesignation().toLowerCase();
1969 }
1970 }
1971 if (state == StationState.REPAIRS) {
1972 text.addPara("The " + name + " has suffered extensive damage and is not currently combat-capable.");
1973 } else {
1974 text.addPara("The " + name + " is under construction and is not currently combat-capable.");
1975 }
1976 }
1977 }
1978
1979
1980 protected void engage() {
1983
1985
1987
1988 final FIDConfig config = new FIDConfig();
1989 config.leaveAlwaysAvailable = true;
1990 config.showCommLinkOption = false;
1991 config.showEngageText = false;
1992 config.showFleetAttitude = false;
1993 config.showTransponderStatus = false;
1994 //config.showWarningDialogWhenNotHostile = false;
1995 config.alwaysAttackVsAttack = true;
1996 config.impactsAllyReputation = true;
1997// config.impactsEnemyReputation = false;
1998// config.pullInAllies = false;
1999// config.pullInEnemies = false;
2000// config.lootCredits = false;
2001
2002// config.firstTimeEngageOptionText = "Engage the automated defenses";
2003// config.afterFirstTimeEngageOptionText = "Re-engage the automated defenses";
2004 config.noSalvageLeaveOptionText = "Continue";
2005
2006 config.dismissOnLeave = false;
2007 config.printXPToDialog = true;
2008
2009 config.straightToEngage = true;
2010
2012 config.playerAttackingStation = station != null;
2013
2015
2016 final InteractionDialogPlugin originalPlugin = dialog.getPlugin();
2017 config.delegate = new BaseFIDDelegate() {
2018 @Override
2019 public void notifyLeave(InteractionDialogAPI dialog) {
2020 if (primary.isStationMode()) {
2021 primary.getMemoryWithoutUpdate().clear();
2022 primary.clearAssignments();
2023 //primary.deflate();
2024 }
2025
2026 dialog.setPlugin(originalPlugin);
2028
2029 boolean quickExit = entity.hasTag(Tags.NON_CLICKABLE);
2030
2031 if (!Global.getSector().getPlayerFleet().isValidPlayerFleet() || quickExit) {
2033 dialog.getOptionPanel().addOption("Leave", "marketLeave");
2034 dialog.getOptionPanel().setShortcut("marketLeave", Keyboard.KEY_ESCAPE, false, false, false, true);
2035
2037 dialog.setPromptText("You decide to...");
2039 text.updateSize();
2040
2041// dialog.hideVisualPanel();
2042// dialog.getVisualPanel().finishFadeFast();
2043// dialog.hideTextPanel();
2044// dialog.dismiss();
2045 return;
2046 }
2047
2048 if (plugin.getContext() instanceof FleetEncounterContext) {
2051 // may need to do something here re: station being defeated & timed out
2052 //FireBest.fire(null, dialog, memoryMap, "BeatDefendersContinue");
2053 } else {
2054 //dialog.dismiss();
2055 }
2056
2057 if (context.isEngagedInHostilities()) {
2058 dialog.getInteractionTarget().getMemoryWithoutUpdate().set("$tradeMode", "NONE", 0);
2059 }
2060
2062 } else {
2063 showDefenses(false);
2064 }
2066
2067 //dialog.dismiss();
2068 }
2069 @Override
2070 public void battleContextCreated(InteractionDialogAPI dialog, BattleCreationContext bcc) {
2071 //bcc.aiRetreatAllowed = false;
2072 bcc.objectivesAllowed = false;
2073 }
2074 @Override
2075 public void postPlayerSalvageGeneration(InteractionDialogAPI dialog, FleetEncounterContext context, CargoAPI salvage) {
2076 }
2077
2078 };
2079
2080 dialog.setPlugin(plugin);
2081 plugin.init(dialog);
2082
2083 }
2084
2087 if (station == null) return null;
2088
2089 if (station.getFleetData().getMembersListCopy().isEmpty()) return null;
2090
2091 return station;
2092 }
2093
2096 if (primary == null) {
2097 CampaignFleetAPI best = null;
2098 float minDist = Float.MAX_VALUE;
2099 for (CampaignFleetAPI fleet : Misc.getNearbyFleets(entity, 2000)) {
2100 if (fleet.getBattle() != null) continue;
2101
2102 if (fleet.getFaction() != market.getFaction()) continue;
2103 if (fleet.getFleetData().getNumMembers() <= 0) continue;
2104
2105 float dist = Misc.getDistance(entity.getLocation(), fleet.getLocation());
2106 dist -= entity.getRadius();
2107 dist -= fleet.getRadius();
2108
2109 if (dist < Misc.getBattleJoinRange() ) {
2110 if (dist < minDist) {
2111 best = fleet;
2112 minDist = dist;
2113 }
2114 }
2115 }
2116 primary = best;
2117 } else {
2118 //primary.setLocation(entity.getLocation().x, entity.getLocation().y);
2119 }
2120 return primary;
2121 }
2122
2123 public static enum StationState {
2124 NONE,
2125 OPERATIONAL,
2126 UNDER_CONSTRUCTION,
2127 REPAIRS
2128 }
2129
2130 protected StationState getStationState() {
2132 boolean destroyed = false;
2133 if (fleet == null) {
2135 if (fleet != null) {
2136 destroyed = true;
2137 }
2138 }
2139
2140 if (fleet == null) return StationState.NONE;
2141
2143 if (market != null) {
2144 for (Industry ind : market.getIndustries()) {
2145 if (ind.getSpec().hasTag(Industries.TAG_STATION)) {
2146 if (ind.isBuilding() && !ind.isDisrupted() && !ind.isUpgrading()) {
2147 return StationState.UNDER_CONSTRUCTION;
2148 }
2149 }
2150 }
2151 }
2152
2153 if (destroyed) return StationState.REPAIRS;
2154
2155 return StationState.OPERATIONAL;
2156 }
2157
2158
2159 public static int applyRaidStabiltyPenalty(MarketAPI target, String desc, float re) {
2160 int penalty = 0;
2161 if (re >= 0.79f) penalty = 3;
2162 else if (re >= 0.59f) penalty = 2;
2163 else if (re >= 0.29f) penalty = 1;
2164 if (penalty > 0) {
2165 RecentUnrest.get(target).add(penalty, desc);
2166 }
2167 return penalty;
2168 }
2169
2170 public static int applyRaidStabiltyPenalty(MarketAPI target, String desc, float re, float maxPenalty) {
2171 int penalty = Math.round((0.45f + maxPenalty) * re);
2172 if (penalty > 0) {
2173 RecentUnrest.get(target).add(penalty, desc);
2174 }
2175 return penalty;
2176 }
2177
2178
2179 public static StatModValueGetter statPrinter(final boolean withNegative) {
2180 return new StatModValueGetter() {
2181 public String getPercentValue(StatMod mod) {
2182 String prefix = mod.getValue() > 0 ? "+" : "";
2183 return prefix + (int)(mod.getValue()) + "%";
2184 }
2185 public String getMultValue(StatMod mod) {
2186 return Strings.X + "" + Misc.getRoundedValue(mod.getValue());
2187 }
2188 public String getFlatValue(StatMod mod) {
2189 String prefix = mod.getValue() > 0 ? "+" : "";
2190 return prefix + (int)(mod.getValue()) + "";
2191 }
2192 public Color getModColor(StatMod mod) {
2193 if (withNegative && mod.getValue() < 1f) return Misc.getNegativeHighlightColor();
2194 return null;
2195 }
2196 };
2197 }
2198
2199
2201 float str = getDefenderStr(market, true);
2202 int result = (int) (str * Global.getSettings().getFloat("bombardFuelFraction"));
2203 if (result < 2) result = 2;
2204 if (fleet != null) {
2205 float bomardBonus = Misc.getFleetwideTotalMod(fleet, Stats.FLEET_BOMBARD_COST_REDUCTION, 0f);
2206 result -= bomardBonus;
2207 if (result < 0) result = 0;
2208 }
2209 return result;
2210 }
2211
2213 return (int) Global.getSettings().getFloat("bombardTacticalStability");
2214 }
2216 return (int) Global.getSettings().getFloat("bombardSaturationStability");
2217 }
2218
2219
2220 protected void bombardMenu() {
2221 float width = 350;
2222 float opad = 10f;
2223 float small = 5f;
2224
2225 Color h = Misc.getHighlightColor();
2226 Color b = Misc.getNegativeHighlightColor();
2227
2228 dialog.getVisualPanel().showImagePortion("illustrations", "bombard_prepare", 640, 400, 0, 0, 480, 300);
2229
2231
2233 String increasedBombardKey = "core_addedBombard";
2234 StatBonus bombardBonusStat = new StatBonus();
2235 if (bomardBonus > 0) {
2236 bombardBonusStat.modifyFlat(increasedBombardKey, -bomardBonus, "Specialized fleet bombardment capability");
2237 }
2238
2239 float defenderStr = (int) Math.round(defender.computeEffective(0f));
2240 defenderStr -= bomardBonus;
2241 if (defenderStr < 0) defenderStr = 0;
2242
2243 temp.defenderStr = defenderStr;
2244
2246
2247 info.setParaSmallInsignia();
2248
2249 String has = faction.getDisplayNameHasOrHave();
2250 String is = faction.getDisplayNameIsOrAre();
2251 boolean hostile = faction.isHostileTo(Factions.PLAYER);
2252 boolean tOn = playerFleet.isTransponderOn();
2253 float initPad = 0f;
2254 if (!hostile) {
2256 " not currently hostile. A bombardment is a major enough hostile action that it can't be concealed, " +
2257 "regardless of transponder status.",
2259 initPad = opad;
2260 }
2261
2262 info.addPara("Starship fuel can be easily destabilized, unlocking the destructive " +
2263 "potential of the antimatter it contains. Ground defenses can counter " +
2264 "a bombardment, though in practice it only means that more fuel is required to achieve " +
2265 "the same result.", initPad);
2266
2267
2268 if (bomardBonus > 0) {
2269 info.addPara("Effective ground defense strength: %s", opad, h, "" + (int)defenderStr);
2270 } else {
2271 info.addPara("Ground defense strength: %s", opad, h, "" + (int)defenderStr);
2272 }
2273 info.addStatModGrid(width, 50, opad, small, defender, true, statPrinter(true));
2274 if (!bombardBonusStat.isUnmodified()) {
2275 info.addStatModGrid(width, 50, opad, 3f, bombardBonusStat, true, statPrinter(false));
2276 }
2277
2278 text.addTooltip();
2279
2280// text.addPara("A tactical bombardment will only hit military targets and costs less fuel. A saturation " +
2281// "bombardment will devastate the whole colony, and only costs marginally more fuel, as the non-military " +
2282// "targets don't have nearly the same degree of hardening.");
2283
2284 temp.bombardCost = getBombardmentCost(market, playerFleet);
2285
2286 int fuel = (int) playerFleet.getCargo().getFuel();
2287 boolean canBombard = fuel >= temp.bombardCost;
2288
2289 LabelAPI label = text.addPara("A bombardment requires %s fuel. " +
2290 "You have %s fuel.",
2291 h, "" + temp.bombardCost, "" + fuel);
2292 label.setHighlight("" + temp.bombardCost, "" + fuel);
2293 label.setHighlightColors(canBombard ? h : b, h);
2294
2296
2297 options.addOption("Prepare a tactical bombardment", BOMBARD_TACTICAL);
2298 options.addOption("Prepare a saturation bombardment", BOMBARD_SATURATION);
2299
2301 canBombard = true;
2302 }
2303 if (!canBombard) {
2305 options.setTooltip(BOMBARD_TACTICAL, "Not enough fuel.");
2307 options.setTooltip(BOMBARD_SATURATION, "Not enough fuel.");
2308 }
2309
2310 options.addOption("Go back", RAID_GO_BACK);
2311 options.setShortcut(RAID_GO_BACK, Keyboard.KEY_ESCAPE, false, false, false, true);
2312 }
2313
2314
2315 protected void addConfirmOptions() {
2317// if (temp.isSurpriseRaid) {
2318// options.addOption("Launch surprise raid", RAID_CONFIRM);
2319// } else {
2320 //options.addOption("Launch full-scale raid", RAID_CONFIRM);
2321 options.addOption("Launch raid", RAID_CONFIRM);
2322// }
2323
2324 boolean tOn = playerFleet.isTransponderOn();
2325
2326 //if (!temp.nonMarket) {
2327 if (market != null && !market.isPlanetConditionMarketOnly()) {
2328 options.addOption("Make special efforts to keep your preparations secret, then proceed", RAID_CONFIRM_STORY);
2329 String req = "";
2330 if (tOn) {
2331 req = "\n\nRequires transponder to be turned off";
2333 }
2334 options.setTooltip(RAID_CONFIRM_STORY, "Suffer no penalty to your standing with " + market.getFaction().getDisplayNameWithArticle() + ". " +
2335 "Will not help if forced to turn your transponder on by patrols arriving to investigate the raid." + req);
2338 StoryOptionParams params = new StoryOptionParams(RAID_CONFIRM_STORY, 1, "noRepPenaltyRaid", Sounds.STORY_POINT_SPEND_LEADERSHIP,
2339 "Secretly raided " + market.getName() + "");
2340 SetStoryOption.set(dialog, params,
2341 new BaseOptionStoryPointActionDelegate(dialog, params) {
2342 @Override
2343 public void confirm() {
2344 super.confirm();
2345 raidConfirm(true);
2346 }
2347 });
2348 }
2349
2350
2351 options.addOption("Never mind", RAID_NEVER_MIND);
2352 options.setShortcut(RAID_NEVER_MIND, Keyboard.KEY_ESCAPE, false, false, false, true);
2353
2354 boolean hostile = faction.isHostileTo(Factions.PLAYER);
2355 if (tOn && !hostile && !faction.isNeutralFaction()) {
2357 "The " + faction.getDisplayNameLong() +
2359 " not currently hostile, and you have been positively identified. " +
2360 "Are you sure you want to engage in open hostilities?", "Yes", "Never mind");
2361 }
2362 }
2363
2364 public static List<Industry> getTacticalBombardmentTargets(MarketAPI market) {
2365 int dur = getBombardDisruptDuration();
2366 List<Industry> targets = new ArrayList<Industry>();
2367 for (Industry ind : market.getIndustries()) {
2368 if (ind.getSpec().hasTag(Industries.TAG_TACTICAL_BOMBARDMENT)) {
2369 if (ind.getDisruptedDays() >= dur * 0.8f) continue;
2370 targets.add(ind);
2371 }
2372 }
2373 return targets;
2374 }
2375
2376 protected void bombardTactical() {
2377
2378 temp.bombardType = BombardType.TACTICAL;
2379
2380 boolean hostile = faction.isHostileTo(Factions.PLAYER);
2381 temp.willBecomeHostile.clear();
2382 temp.willBecomeHostile.add(faction);
2383
2384 float opad = 10f;
2385 float small = 5f;
2386
2387 Color h = Misc.getHighlightColor();
2388 Color b = Misc.getNegativeHighlightColor();
2389
2390
2391 int dur = getBombardDisruptDuration();
2392
2393 List<Industry> targets = getTacticalBombardmentTargets(market);
2394 temp.bombardmentTargets.clear();
2395 temp.bombardmentTargets.addAll(targets);
2396
2397 if (targets.isEmpty()) {
2398 text.addPara(market.getName() + " does not have any undisrupted military targets that would be affected by a tactical bombardment.");
2400 return;
2401 }
2402
2403
2404 int fuel = (int) playerFleet.getCargo().getFuel();
2405 text.addPara("A tactical bombardment will destabilize the colony, and will also disrupt the " +
2406 "following military targets for approximately %s days:",
2407 h, "" + dur);
2408
2410
2411 info.setParaSmallInsignia();
2412 info.setParaFontDefault();
2413
2415 float initPad = 0f;
2416 for (Industry ind : targets) {
2417 //info.addPara(ind.getCurrentName(), faction.getBaseUIColor(), initPad);
2418 info.addPara(ind.getCurrentName(), initPad);
2419 initPad = 3f;
2420 }
2421 info.setBulletedListMode(null);
2422
2423 text.addTooltip();
2424
2425 text.addPara("The bombardment requires %s fuel. " +
2426 "You have %s fuel.",
2427 h, "" + temp.bombardCost, "" + fuel);
2428
2430 }
2431
2432 protected void bombardSaturation() {
2433
2434 temp.bombardType = BombardType.SATURATION;
2435
2436 temp.willBecomeHostile.clear();
2437 temp.willBecomeHostile.add(faction);
2438
2439 List<FactionAPI> nonHostile = new ArrayList<FactionAPI>();
2440 nonHostile.add(faction);
2442 if (temp.willBecomeHostile.contains(faction)) continue;
2444 boolean hostile = faction.isHostileTo(Factions.PLAYER);
2445 temp.willBecomeHostile.add(faction);
2446 if (!hostile) {
2447 nonHostile.add(faction);
2448 }
2449 }
2450
2451 }
2452
2453 float opad = 10f;
2454 float small = 5f;
2455
2456 Color h = Misc.getHighlightColor();
2457 Color b = Misc.getNegativeHighlightColor();
2458
2459
2460 int dur = getBombardDisruptDuration();
2461
2462 List<Industry> targets = new ArrayList<Industry>();
2463 for (Industry ind : market.getIndustries()) {
2464 if (!ind.getSpec().hasTag(Industries.TAG_NO_SATURATION_BOMBARDMENT)) {
2465 if (ind.getDisruptedDays() >= dur * 0.8f) continue;
2466 targets.add(ind);
2467 }
2468 }
2469 temp.bombardmentTargets.clear();
2470 temp.bombardmentTargets.addAll(targets);
2471
2472 boolean destroy = market.getSize() <= getBombardDestroyThreshold();
2473 if (Misc.isStoryCritical(market)) destroy = false;
2474
2475 int fuel = (int) playerFleet.getCargo().getFuel();
2476 if (destroy) {
2477 text.addPara("A saturation bombardment of a colony this size will destroy it utterly.");
2478 } else {
2479 text.addPara("A saturation bombardment will destabilize the colony, reduce its population, " +
2480 "and disrupt all operations for a long time.");
2481 }
2482
2483
2484// TooltipMakerAPI info = text.beginTooltip();
2485// info.setParaFontDefault();
2486//
2487// info.setBulletedListMode(BaseIntelPlugin.INDENT);
2488// float initPad = 0f;
2489// for (Industry ind : targets) {
2490// //info.addPara(ind.getCurrentName(), faction.getBaseUIColor(), initPad);
2491// info.addPara(ind.getCurrentName(), initPad);
2492// initPad = 3f;
2493// }
2494// info.setBulletedListMode(null);
2495//
2496// text.addTooltip();
2497
2498
2499 if (nonHostile.isEmpty()) {
2500 text.addPara("An atrocity of this scale can not be hidden, but any factions that would " +
2501 "be dismayed by such actions are already hostile to you.");
2502 } else {
2503 text.addPara("An atrocity of this scale can not be hidden, " +
2504 "and will make the following factions hostile:");
2505 }
2506
2507 if (!nonHostile.isEmpty()) {
2509 info.setParaFontDefault();
2510
2512 float initPad = 0f;
2513 for (FactionAPI fac : nonHostile) {
2514 info.addPara(Misc.ucFirst(fac.getDisplayName()), fac.getBaseUIColor(), initPad);
2515 initPad = 3f;
2516 }
2517 info.setBulletedListMode(null);
2518
2519 text.addTooltip();
2520 }
2521
2522 text.addPara("The bombardment requires %s fuel. " +
2523 "You have %s fuel.",
2524 h, "" + temp.bombardCost, "" + fuel);
2525
2527 }
2528
2529 protected void bombardConfirm() {
2530
2531 if (temp.bombardType == null) {
2533 return;
2534 }
2535
2536 if (temp.bombardType == BombardType.TACTICAL) {
2537 dialog.getVisualPanel().showImagePortion("illustrations", "bombard_tactical_result", 640, 400, 0, 0, 480, 300);
2538 } else {
2539 dialog.getVisualPanel().showImagePortion("illustrations", "bombard_saturation_result", 640, 400, 0, 0, 480, 300);
2540 }
2541
2542 Random random = getRandom();
2543
2545 float timeout = TACTICAL_BOMBARD_TIMEOUT_DAYS;
2546 if (temp.bombardType == BombardType.SATURATION) {
2548 }
2550
2551 timeout *= 0.7f;
2552
2554 if (curr == market) continue;
2555 boolean cares = curr.getFaction().getCustomBoolean(Factions.CUSTOM_CARES_ABOUT_ATROCITIES);
2556 cares &= temp.bombardType == BombardType.SATURATION;
2557
2558 if (curr.getFaction().isNeutralFaction()) continue;
2559 if (curr.getFaction().isPlayerFaction()) continue;
2560 if (curr.getFaction().isHostileTo(market.getFaction()) && !cares) continue;
2561
2562 Misc.increaseMarketHostileTimeout(curr, timeout);
2563 }
2564 }
2565
2567
2568 playerFleet.getCargo().removeFuel(temp.bombardCost);
2570
2571 for (FactionAPI curr : temp.willBecomeHostile) {
2572 CustomRepImpact impact = new CustomRepImpact();
2573 impact.delta = market.getSize() * -0.01f * 1f;
2574 impact.ensureAtBest = RepLevel.HOSTILE;
2575 if (temp.bombardType == BombardType.SATURATION) {
2576 if (curr == faction) {
2577 impact.ensureAtBest = RepLevel.VENGEFUL;
2578 }
2579 impact.delta = market.getSize() * -0.01f * 1f;
2580 }
2582 new RepActionEnvelope(RepActions.CUSTOM,
2583 impact, null, text, true, true),
2584 curr.getId());
2585 }
2586
2587 if (temp.bombardType == BombardType.SATURATION) {
2589 atrocities++;
2591
2592 if (market != null && market.getFaction() != null) {
2595 count++;
2597 }
2598 }
2599
2600
2601 int stabilityPenalty = getTacticalBombardmentStabilityPenalty();
2602 if (temp.bombardType == BombardType.SATURATION) {
2603 stabilityPenalty = getSaturationBombardmentStabilityPenalty();
2604 }
2605 boolean destroy = temp.bombardType == BombardType.SATURATION && market.getSize() <= getBombardDestroyThreshold();
2606 if (Misc.isStoryCritical(market)) destroy = false;
2607
2608 if (stabilityPenalty > 0 && !destroy) {
2609 String reason = "Recently bombarded";
2610 if (Misc.isPlayerFactionSetUp()) {
2611 reason = playerFaction.getDisplayName() + " bombardment";
2612 }
2613 RecentUnrest.get(market).add(stabilityPenalty, reason);
2614 text.addPara("Stability of " + market.getName() + " reduced by %s.",
2615 Misc.getHighlightColor(), "" + stabilityPenalty);
2616 }
2617
2620 }
2621
2622 if (!destroy) {
2623 for (Industry curr : temp.bombardmentTargets) {
2624 int dur = getBombardDisruptDuration();
2625 dur *= StarSystemGenerator.getNormalRandom(random, 1f, 1.25f);
2626 curr.setDisrupted(dur);
2627 }
2628 }
2629
2630
2631
2632 if (temp.bombardType == BombardType.TACTICAL) {
2633 text.addPara("Military operations disrupted.");
2634
2636 } else if (temp.bombardType == BombardType.SATURATION) {
2637 if (destroy) {
2639 text.addPara(market.getName() + " destroyed.");
2640 } else {
2641 int prevSize = market.getSize();
2643 if (prevSize == market.getSize()) {
2644 text.addPara("All operations disrupted.");
2645 } else {
2646 text.addPara("All operations disrupted. Colony size reduced to %s.",
2648 , "" + market.getSize());
2649 }
2650
2651 }
2653 }
2654
2655 if (dialog != null && dialog.getPlugin() instanceof RuleBasedDialog) {
2656 if (dialog.getInteractionTarget() != null &&
2658 Global.getSector().setPaused(false);
2660 Global.getSector().setPaused(true);
2661 }
2662 ((RuleBasedDialog) dialog.getPlugin()).updateMemory();
2663 }
2664
2666 Factions.PLAYER, true, 30f);
2667
2668 if (destroy) {
2669 if (dialog != null && dialog.getPlugin() instanceof RuleBasedDialog) {
2670 ((RuleBasedDialog) dialog.getPlugin()).updateMemory();
2671// market.getMemoryWithoutUpdate().unset("$tradeMode");
2672// entity.getMemoryWithoutUpdate().unset("$tradeMode");
2673 }
2674 }
2675
2677
2679 }
2680
2681
2682 protected void bombardNeverMind() {
2683 bombardMenu();
2684 }
2685
2686 protected void raidResult() {
2687 if (temp.raidLoot != null) {
2688 if (temp.raidLoot.isEmpty()) {
2689// clearTemp();
2690// showDefenses(true);
2691 //dialog.dismiss();
2693 } else {
2694 raidShowLoot();
2695 }
2696 return;
2697 } else {
2698 //dialog.dismiss();
2700 }
2701 }
2702
2703 protected void bombardResult() {
2704 //dialog.dismiss();
2706 }
2707
2708 protected void finishedRaidOrBombard() {
2709 //showDefenses(true);
2710
2712
2713 //FireAll.fire(null, dialog, memoryMap, "MarketPostOpen");
2714 dialog.getInteractionTarget().getMemoryWithoutUpdate().set("$menuState", "main", 0);
2718 }
2719 dialog.getInteractionTarget().getMemoryWithoutUpdate().set("$tradeMode", "NONE", 0);
2720 } else {
2721 // station that's now abandoned
2722 dialog.getInteractionTarget().getMemoryWithoutUpdate().set("$tradeMode", "OPEN", 0);
2723 }
2724
2725 if (temp.nonMarket) {
2726 String trigger = temp.raidContinueTrigger;
2727 if (trigger == null || trigger.isEmpty()) trigger = "OpenInteractionDialog";
2728 FireAll.fire(null, dialog, memoryMap, trigger);
2729 } else {
2730 FireAll.fire(null, dialog, memoryMap, "PopulateOptions");
2731 }
2732
2733 clearTemp();
2734 }
2735
2736 protected void addBombardContinueOption() {
2738 }
2739 protected void addBombardContinueOption(String text) {
2740 if (text == null) text = "Continue";
2743 }
2744
2745
2746 protected boolean checkDebtEffect() {
2747 String key = "$debt_effectTimeout";
2748 if (Global.getSector().getMemoryWithoutUpdate().contains(key)) return false;
2749
2750 //if (true) return true;
2751
2752 // can't exactly melt away in that small an outpost, not that it's outright desertion
2753 // but it's also not a great place to leave the fleet
2754 if (market.isPlayerOwned() && market.getSize() <= 3) return false;
2755
2757
2758
2759 // require 2 months of debt in a row
2760 if (report.getPreviousDebt() <= 0 || report.getDebt() <= 0) return false;
2761
2762 float debt = report.getDebt() + report.getPreviousDebt();
2763 float income = report.getRoot().totalIncome;
2764 if (income < 1) income = 1;
2765
2766 float f = debt / income;
2767 if (f > 1) f = 1;
2768 if (f < 0) f = 0;
2769 // don't penalize minor shortfalls
2770 if (f < 0.1f) return false;
2771
2772 // and don't reduce crew below a certain minimum
2773 int crew = playerFleet.getCargo().getCrew();
2774 int marines = playerFleet.getCargo().getMarines();
2775 if (crew <= 10 && marines <= 10) return false;
2776
2777 return true;
2778 }
2779
2780 protected void applyDebtEffect() {
2781
2783 float debt = report.getDebt() + report.getPreviousDebt();
2784 float income = report.getRoot().totalIncome;
2785 if (income < 1) income = 1;
2786
2787 float f = debt / income;
2788 if (f > 1) f = 1;
2789 if (f < 0) f = 0;
2790
2791 int crew = playerFleet.getCargo().getCrew();
2792 int marines = playerFleet.getCargo().getMarines();
2793
2794 float maxLossFraction = 0.03f + Math.min(f + 0.05f, 0.2f) * (float) Math.random();
2795 float marineLossFraction = 0.03f + Math.min(f + 0.05f, 0.2f) * (float) Math.random();
2796
2797
2798 int crewLoss = (int) (crew * maxLossFraction);
2799 if (crewLoss < 2) crewLoss = 2;
2800
2801 int marineLoss = (int) (marines * marineLossFraction);
2802 if (marineLoss < 2) marineLoss = 2;
2803
2804 dialog.getVisualPanel().showImagePortion("illustrations", "crew_leaving", 640, 400, 0, 0, 480, 300);
2805
2806 text.addPara("The lack of consistent pay over the last few months has caused discontent among your crew. " +
2807 "A number take this opportunity to leave your employment.");
2808
2809 if (crewLoss < crew) {
2810 playerFleet.getCargo().removeCrew(crewLoss);
2812 }
2813 if (marineLoss <= marines) {
2814 playerFleet.getCargo().removeMarines(marineLoss);
2816 }
2817
2818 String key = "$debt_effectTimeout";
2819 Global.getSector().getMemoryWithoutUpdate().set(key, true, 30f + (float) Math.random() * 10f);
2820
2823 }
2824
2825
2826 public void doGenericRaid(FactionAPI faction, float attackerStr) {
2827 doGenericRaid(faction, attackerStr, 3f);
2828 }
2829
2830 public void doGenericRaid(FactionAPI faction, float attackerStr, float maxPenalty) {
2831 doGenericRaid(faction, attackerStr, maxPenalty, false);
2832 }
2833 public void doGenericRaid(FactionAPI faction, float attackerStr, float maxPenalty, boolean allowedRepeat) {
2834 // needed for pirate raids not to stack
2835 // not needed anymore, but doesn't hurt anything
2836 if (!allowedRepeat && Misc.flagHasReason(market.getMemoryWithoutUpdate(),
2838 return;
2839 }
2840
2841 float re = getRaidEffectiveness(market, attackerStr);
2842 if (maxPenalty == 3) {
2844 } else {
2846 }
2847 //RecentUnrest.get(market).add(3, Misc.ucFirst(faction.getPersonNamePrefix()) + " raid");
2848
2850 faction.getId(), true, 30f);
2852 }
2853
2854 public boolean doIndustryRaid(FactionAPI faction, float attackerStr, Industry industry, float durMult) {
2855 temp.raidType = RaidType.DISRUPT;
2856 temp.target = industry;
2857
2858 StatBonus defenderBase = new StatBonus();
2859
2861 String increasedDefensesKey = "core_addedDefStr";
2862 float added = getDefenderIncreaseValue(market);
2863 if (added > 0) {
2864 defender.modifyFlat(increasedDefensesKey, added, "Increased defender preparedness");
2865 }
2866 float defenderStr = (int) Math.round(defender.computeEffective(defenderBase.computeEffective(0f)));
2867 defender.unmodifyFlat(increasedDefensesKey);
2868
2869 temp.attackerStr = attackerStr;
2870 temp.defenderStr = defenderStr;
2871
2872 boolean hasForces = true;
2873 boolean canDisrupt = true;
2874 temp.raidMult = attackerStr / Math.max(1f, (attackerStr + defenderStr));
2875 temp.raidMult = Math.round(temp.raidMult * 100f) / 100f;
2876
2877 if (temp.raidMult < VALUABLES_THRESHOLD) {
2878 hasForces = false;
2879 }
2880 if (temp.raidMult < DISRUPTION_THRESHOLD) {
2881 canDisrupt = false;
2882 }
2883 if (!canDisrupt) return false;
2884
2885
2886 Random random = getRandom();
2887
2889
2890 String reason = faction.getDisplayName() + " raid";
2891 if (faction.getPersonNamePrefix() != null) {
2892 reason = Misc.ucFirst(faction.getPersonNamePrefix()) + " raid";
2893 }
2894
2895 applyRaidStabiltyPenalty(market, reason, temp.raidMult);
2897 faction.getId(), true, 30f);
2899
2900 if (temp.target != null) {
2901 float dur = computeBaseDisruptDuration(temp.target);
2902 dur *= StarSystemGenerator.getNormalRandom(random, 1f, 1.25f);
2903 dur *= durMult;
2904 if (dur < 2) dur = 2;
2905 float already = temp.target.getDisruptedDays();
2906 temp.target.setDisrupted(already + dur);
2907 }
2908
2909 return true;
2910 }
2911
2912
2913 public void doBombardment(FactionAPI faction, BombardType type) {
2914 temp.bombardType = type;
2915
2916 Random random = getRandom();
2917
2918 int dur = getBombardDisruptDuration();
2919
2920 int stabilityPenalty = getTacticalBombardmentStabilityPenalty();
2921 if (temp.bombardType == BombardType.SATURATION) {
2922 stabilityPenalty = getSaturationBombardmentStabilityPenalty();
2923
2924 List<Industry> targets = new ArrayList<Industry>();
2925 for (Industry ind : market.getIndustries()) {
2926 if (!ind.getSpec().hasTag(Industries.TAG_NO_SATURATION_BOMBARDMENT)) {
2927 if (ind.getDisruptedDays() >= dur * 0.8f) continue;
2928 targets.add(ind);
2929 }
2930 }
2931 temp.bombardmentTargets.clear();
2932 temp.bombardmentTargets.addAll(targets);
2933 } else {
2934 List<Industry> targets = new ArrayList<Industry>();
2935 for (Industry ind : market.getIndustries()) {
2936 if (ind.getSpec().hasTag(Industries.TAG_TACTICAL_BOMBARDMENT)) {
2937 if (ind.getDisruptedDays() >= dur * 0.8f) continue;
2938 targets.add(ind);
2939 }
2940 }
2941 temp.bombardmentTargets.clear();
2942 temp.bombardmentTargets.addAll(targets);
2943 }
2944
2945
2946 if (stabilityPenalty > 0) {
2947 //String reason = faction.getDisplayName() + " bombardment";
2948 String reason = "Saturation bombardment";
2949 if (temp.bombardType == BombardType.TACTICAL) {
2950 reason = "Tactical bombardment";
2951 }
2952
2953 RecentUnrest.get(market).add(stabilityPenalty, reason);
2954 }
2955
2958 }
2959
2960 for (Industry curr : temp.bombardmentTargets) {
2962 dur *= StarSystemGenerator.getNormalRandom(random, 1f, 1.25f);
2963 curr.setDisrupted(dur);
2964 }
2965
2966 if (temp.bombardType == BombardType.TACTICAL) {
2967 } else if (temp.bombardType == BombardType.SATURATION) {
2968 boolean destroy = market.getSize() <= getBombardDestroyThreshold();
2969 if (Misc.isStoryCritical(market)) destroy = false;
2970 if (destroy) {
2972 } else {
2974 }
2975 }
2976
2977
2979 faction.getId(), true, 30f);
2980
2982 }
2983
2984
2985 public static void addBombardVisual(SectorEntityToken target) {
2986 if (target != null && target.isInCurrentLocation()) {
2987 int num = (int) (target.getRadius() * target.getRadius() / 300f);
2988 num *= 2;
2989 if (num > 150) num = 150;
2990 if (num < 10) num = 10;
2991 target.addScript(new BombardmentAnimation(num, target));
2992 }
2993 }
2994
2995 public static class BombardmentAnimation implements EveryFrameScript {
2996 public BombardmentAnimation(int num, SectorEntityToken target) {
2997 this.num = num;
2998 this.target = target;
2999 }
3000 int num = 0;
3001 SectorEntityToken target;
3002 int added = 0;
3003 float elapsed = 0;
3004 public boolean runWhilePaused() {
3005 return false;
3006 }
3007 public boolean isDone() {
3008 return added >= num;
3009 }
3010 public void advance(float amount) {
3011 elapsed += amount * (float) Math.random();
3012 if (elapsed < 0.03f) return;
3013
3014 elapsed = 0f;
3015
3016 int curr = (int) Math.round(Math.random() * 4);
3017 if (curr < 1) curr = 0;
3018
3019 Color color = new Color(255, 165, 100, 255);
3020
3021 Vector2f vel = new Vector2f();
3022
3023 if (target.getOrbit() != null &&
3024 target.getCircularOrbitRadius() > 0 &&
3025 target.getCircularOrbitPeriod() > 0 &&
3026 target.getOrbitFocus() != null) {
3027 float circumference = 2f * (float) Math.PI * target.getCircularOrbitRadius();
3028 float speed = circumference / target.getCircularOrbitPeriod();
3029
3030 float dir = Misc.getAngleInDegrees(target.getLocation(), target.getOrbitFocus().getLocation()) + 90f;
3032 vel.scale(speed / Global.getSector().getClock().getSecondsPerDay());
3033 }
3034
3035 for (int i = 0; i < curr; i++) {
3036 float glowSize = 50f + 50f * (float) Math.random();
3037 float angle = (float) Math.random() * 360f;
3038 float dist = (float) Math.sqrt(Math.random()) * target.getRadius();
3039
3040 float factor = 0.5f + 0.5f * (1f - (float)Math.sqrt(dist / target.getRadius()));;
3041 glowSize *= factor;
3042 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle);
3043 loc.scale(dist);
3044 Vector2f.add(loc, target.getLocation(), loc);
3045
3046 Color c2 = Misc.scaleColor(color, factor);
3047 //c2 = color;
3048 Misc.addHitGlow(target.getContainingLocation(), loc, vel, glowSize, c2);
3049 added++;
3050
3051 if (i == 0) {
3052 dist = Misc.getDistance(loc, Global.getSector().getPlayerFleet().getLocation());
3053 if (dist < HyperspaceTerrainPlugin.STORM_STRIKE_SOUND_RANGE) {
3054 float volumeMult = 1f - (dist / HyperspaceTerrainPlugin.STORM_STRIKE_SOUND_RANGE);
3055 volumeMult = (float) Math.sqrt(volumeMult);
3056 volumeMult *= 0.1f * factor;
3057 if (volumeMult > 0) {
3058 Global.getSoundPlayer().playSound("mine_explosion", 1f, 1f * volumeMult, loc, Misc.ZERO);
3059 }
3060 }
3061 }
3062 }
3063 }
3064 }
3065
3066
3067 protected boolean checkMercsLeaving() {
3068 String key = "$mercs_leaveTimeout";
3069 if (Global.getSector().getMemoryWithoutUpdate().contains(key)) return false;
3070
3071 if (market.isHidden()) return false;
3072 if (market.getSize() <= 3) return false;
3073
3074 List<OfficerDataAPI> mercs = Misc.getMercs(playerFleet);
3075 if (mercs.isEmpty()) return false;
3076
3078 boolean debt = report.getDebt() > 0;
3079
3080 float contractDur = Global.getSettings().getFloat("officerMercContractDur");
3081
3082 for (OfficerDataAPI od : mercs) {
3083 if (debt && od.getPerson().getMemoryWithoutUpdate().contains(key)) {
3084 continue;
3085 }
3086 float elapsed = Misc.getMercDaysSinceHired(od.getPerson());
3087
3088 if (elapsed > contractDur || (debt && elapsed > 45f)) { // make sure the merc was hired long enough to have seen the debt
3090 //dialog.getVisualPanel().showPersonInfo(getPerson(), true);
3091 ((RuleBasedInteractionDialogPluginImpl)dialog.getPlugin()).notifyActivePersonChanged();
3092 return true;
3093 }
3094 }
3095
3096 return false;
3097 }
3098
3099 protected void convinceMercToStay() {
3102
3103 if (merc != null) {
3104 Misc.setMercHiredNow(merc);
3105
3106 String key = "$mercs_leaveTimeout";
3107 Global.getSector().getMemoryWithoutUpdate().set(key, true, 5f + (float) Math.random() * 5f);
3108 merc.getMemoryWithoutUpdate().set(key, true, 35f + (float) Math.random() * 5f);
3109 }
3110
3111 }
3112
3113 protected void mercLeaves() {
3116
3117 if (merc != null) {
3119 if (member != null) {
3120 member.setCaptain(null);
3121 }
3123
3125
3126 String key = "$mercs_leaveTimeout";
3127 Global.getSector().getMemoryWithoutUpdate().set(key, true, 5f + (float) Math.random() * 5f);
3128 }
3129 }
3130
3131}
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
static SettingsAPI getSettings()
Definition Global.java:57
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:49
static FactoryAPI getFactory()
Definition Global.java:41
static SectorAPI getSector()
Definition Global.java:65
static void reportRaidObjectivesAchieved(RaidResultData data, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap)
static void modifyMarineLossesStatPreRaid(MarketAPI market, List< GroundRaidObjectivePlugin > objectives, MutableStat stat)
static void reportRaidForValuablesFinishedBeforeCargoShown(InteractionDialogAPI dialog, MarketAPI market, TempData actionData, CargoAPI cargo)
static void modifyRaidObjectives(MarketAPI market, SectorEntityToken entity, List< GroundRaidObjectivePlugin > objectives, RaidType type, int marineTokens, int priority)
static void reportSaturationBombardmentFinished(InteractionDialogAPI dialog, MarketAPI market, TempData actionData)
static void reportRaidToDisruptFinished(InteractionDialogAPI dialog, MarketAPI market, TempData actionData, Industry industry)
static void reportTacticalBombardmentFinished(InteractionDialogAPI dialog, MarketAPI market, TempData actionData)
void modifyMult(String source, float value)
void modifyMultAlways(String source, float value, String desc)
void modifyFlatAlways(String source, float value, String desc)
void modifyFlat(String source, float value)
float computeEffective(float baseValue)
static RecentUnrest get(MarketAPI market)
static final String FACTION_SATURATION_BOMBARED_BY_PLAYER
Definition MemFlags.java:64
static final String PLANETARY_OPERATIONS_MOD
Definition Stats.java:65
static final String FLEET_BOMBARD_COST_REDUCTION
Definition Stats.java:124
static final String PLANETARY_OPERATIONS_CASUALTIES_MULT
Definition Stats.java:66
static void decivilize(MarketAPI market, boolean fullDestroy)
static void addCommodityLossText(String commodityId, int quantity, TextPanelAPI text)
static void addCreditsGainText(int credits, TextPanelAPI text)
static void addOfficerLossText(PersonAPI officer, TextPanelAPI text)
static MemoryAPI getEntityMemory(Map< String, MemoryAPI > memoryMap)
static boolean fire(String ruleId, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap, String params)
Definition FireAll.java:55
static boolean fire(String ruleId, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap, String params)
Definition FireBest.java:56
static boolean set(String ruleId, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap, String params)
boolean execute(String ruleId, InteractionDialogAPI dialog, List< Token > params, Map< String, MemoryAPI > memoryMap)
boolean doIndustryRaid(FactionAPI faction, float attackerStr, Industry industry, float durMult)
static StatModValueGetter statPrinter(final boolean withNegative)
void doBombardment(FactionAPI faction, BombardType type)
static float getDefenderStr(MarketAPI market, boolean forBombard)
static float getRaidEffectiveness(MarketAPI market, CampaignFleetAPI fleet)
static int applyRaidStabiltyPenalty(MarketAPI target, String desc, float re)
void doGenericRaid(FactionAPI faction, float attackerStr)
CargoAPI performRaid(Random random, float raidEffectiveness)
static int applyRaidStabiltyPenalty(MarketAPI target, String desc, float re, float maxPenalty)
static int getMarinesFor(int defenderStrength, int tokens)
void doGenericRaid(FactionAPI faction, float attackerStr, float maxPenalty)
static int getBombardmentCost(MarketAPI market, CampaignFleetAPI fleet)
static int getDisruptDaysPerToken(MarketAPI market, Industry industry)
static int getMarinesFor(MarketAPI market, int tokens)
static List< Industry > getTacticalBombardmentTargets(MarketAPI market)
boolean execute(String ruleId, InteractionDialogAPI dialog, List< Token > params, Map< String, MemoryAPI > memoryMap)
void doGenericRaid(FactionAPI faction, float attackerStr, float maxPenalty, boolean allowedRepeat)
float getAverageMarineLosses(List< GroundRaidObjectivePlugin > data)
static void addBombardVisual(SectorEntityToken target)
MutableStat getMarineLossesStat(List< GroundRaidObjectivePlugin > data)
static float getRaidEffectiveness(MarketAPI market, float attackerStr)
static boolean isStoryCritical(MarketAPI market)
Definition Misc.java:6258
static String getDGSCredits(float num)
Definition Misc.java:1390
static String getWithDGS(float num)
Definition Misc.java:1381
static float getMercDaysSinceHired(PersonAPI person)
Definition Misc.java:5463
static Vector2f getUnitVectorAtDegreeAngle(float degrees)
Definition Misc.java:1196
static List< CampaignFleetAPI > getNearbyFleets(SectorEntityToken from, float maxDist)
Definition Misc.java:3747
static String ucFirst(String str)
Definition Misc.java:559
static Color getNegativeHighlightColor()
Definition Misc.java:802
static CargoAPI getStorageCargo(MarketAPI market)
Definition Misc.java:4307
static void increaseMarketHostileTimeout(MarketAPI market, float days)
Definition Misc.java:4911
static float getFleetwideTotalMod(CampaignFleetAPI fleet, String dynamicMemberStatId, float base)
Definition Misc.java:2779
static CampaignFleetAPI getStationBaseFleet(MarketAPI market)
Definition Misc.java:4590
static CargoAPI getLocalResourcesCargo(MarketAPI market)
Definition Misc.java:4314
static Color getGrayColor()
Definition Misc.java:826
static List< OfficerDataAPI > getMercs(CampaignFleetAPI fleet)
Definition Misc.java:5233
static float getDistance(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:599
static long getSalvageSeed(SectorEntityToken entity)
Definition Misc.java:4129
static List< Token > tokenize(String string)
Definition Misc.java:425
static boolean flagHasReason(MemoryAPI memory, String flagKey, String reason)
Definition Misc.java:1453
static MarketAPI getStationMarket(CampaignFleetAPI station)
Definition Misc.java:4609
static Color getHighlightColor()
Definition Misc.java:792
static boolean isInsignificant(CampaignFleetAPI fleet)
Definition Misc.java:6272
static String getAndJoined(List< String > strings)
Definition Misc.java:871
static void setMercHiredNow(PersonAPI person)
Definition Misc.java:5459
static float getRounded(float in)
Definition Misc.java:639
static boolean setFlagWithReason(MemoryAPI memory, String flagKey, String reason, boolean value, float expire)
Definition Misc.java:1439
static CampaignFleetAPI getStationFleet(MarketAPI market)
Definition Misc.java:4571
static boolean isPlayerFactionSetUp()
Definition Misc.java:4750
static float getBattleJoinRange()
Definition Misc.java:2227
static void setRaidedTimestamp(MarketAPI market)
Definition Misc.java:5816
static Color getPositiveHighlightColor()
Definition Misc.java:822
static float getAngleInDegrees(Vector2f v)
Definition Misc.java:1126
static String getRoundedValue(float value)
Definition Misc.java:647
CargoAPI createCargo(boolean unlimitedStacks)
SoundAPI playUISound(String id, float pitch, float volume)
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)
CampaignFleetAPI getPrimary(List< CampaignFleetAPI > side)
List< CampaignFleetAPI > getSideFor(CampaignFleetAPI participantOrCombined)
List< CampaignFleetAPI > getOtherSide(BattleSide side)
List< CampaignFleetAPI > getSide(BattleSide side)
BattleSide pickSide(CampaignFleetAPI fleet)
CampaignFleetAPI getOtherSideCombined(BattleSide side)
boolean knowsWhoPlayerIs(List< CampaignFleetAPI > side)
List< CampaignFleetAPI > getNonPlayerSide()
boolean isHostileTo(FactionAPI other)
FleetMemberAPI getMemberWithCaptain(PersonAPI captain)
List< FleetMemberAPI > getMembersListCopy()
void setInteractionTarget(SectorEntityToken interactionTarget)
void setPlugin(InteractionDialogPlugin plugin)
void showGroundRaidTargetPicker(String title, String okText, MarketAPI market, List< GroundRaidObjectivePlugin > data, GroundRaidTargetPickerDelegate listener)
void addScript(EveryFrameScript script)
List< CampaignFleetAPI > getFleets()
void setTooltip(Object data, String tooltipText)
void setTooltipHighlightColors(Object data, Color ... colors)
void addOptionConfirmation(Object optionId, String text, String yes, String no)
void setTooltipHighlights(Object data, String ... highlights)
void addOption(String text, Object data)
void setEnabled(Object data, boolean enabled)
void setShortcut(Object data, int code, boolean ctrl, boolean alt, boolean shift, boolean putLast)
ReputationAdjustmentResult adjustPlayerReputation(Object action, String factionId)
MutableCharacterStatsAPI getPlayerStats()
void addScript(EveryFrameScript script)
void setActivePerson(PersonAPI activePerson)
void showImagePortion(String category, String id, float x, float y, float w, float h, float xOffset, float yOffset, float displayWidth, float displayHeight)
void showLoot(String title, CargoAPI otherCargo, boolean generatePods, CoreInteractionListener listener)
List< MarketAPI > getMarkets(LocationAPI loc)
void expire(String key, float days)
void set(String key, Object value)
void addXP(long xp, TextPanelAPI textPanel, boolean withMessage, boolean allowBonusXP, boolean withLevelUp)
void setCaptain(PersonAPI commander)
int performRaid(CargoAPI loot, Random random, float lootMult, TextPanelAPI text)
void setHighlight(int start, int end)
void setHighlightColors(Color ... colors)
void addStatModGrid(float width, float valueWidth, float valuePad, float pad, MutableStat stat)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
void setBulletedListMode(String itemPrefix)