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