Starsector API
Loading...
Searching...
No Matches
DeliveryBarEvent.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.intel.bar.events;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.LinkedHashSet;
6import java.util.List;
7import java.util.Random;
8import java.util.Set;
9
10import com.fs.starfarer.api.Global;
11import com.fs.starfarer.api.campaign.CargoAPI;
12import com.fs.starfarer.api.campaign.FactionAPI;
13import com.fs.starfarer.api.campaign.OptionPanelAPI;
14import com.fs.starfarer.api.campaign.TextPanelAPI;
15import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
16import com.fs.starfarer.api.campaign.econ.MarketAPI;
17import com.fs.starfarer.api.characters.PersonAPI;
18import com.fs.starfarer.api.impl.campaign.ids.Conditions;
19import com.fs.starfarer.api.impl.campaign.ids.Factions;
20import com.fs.starfarer.api.impl.campaign.ids.Ranks;
21import com.fs.starfarer.api.impl.campaign.ids.Sounds;
22import com.fs.starfarer.api.impl.campaign.ids.Tags;
23import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity;
24import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption;
25import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.BaseOptionStoryPointActionDelegate;
26import com.fs.starfarer.api.impl.campaign.rulecmd.SetStoryOption.StoryOptionParams;
27import com.fs.starfarer.api.ui.TooltipMakerAPI;
28import com.fs.starfarer.api.util.Misc;
29import com.fs.starfarer.api.util.WeightedRandomPicker;
30
32
33 public static String KEY_FAILED_RECENTLY = "$core_dmi_failedRecently";
34
35 public static String KEY_SAW_DELIVERY_EVENT_RECENTLY = "$core_dmi_sawRecently";
36 public static String KEY_ACCEPTED_AT_THIS_MARKET_RECENTLY = "$core_dmi_acceptedAtThisMarket";
37
38 public static float PROB_HIGHER_CAPACITY = 0.25f;
39
40 public static float FAILED_RECENTLY_DURATION = 365f;
41 public static float SAW_RECENTLY_DURATION = 180f;
42 public static float ACCEPTED_AT_THIS_MARKET_DURATION = 90f;
43
44 public static float PROB_TO_SHOW = 0.5f;
45
47 protected int reward;
48 protected int escrow;
49 protected float duration;
51 protected int playerCargoCap = 0;
52 protected int playerFuelCap = 0;
53
54 protected DestinationData data;
55
57 super();
58 }
59
61 //if (true) return true;
62 if (!super.shouldShowAtMarket(market)) return false;
63
64 if (market.getFactionId().equals(Factions.PIRATES)) return false;
65 if (market.getFactionId().equals(Factions.LUDDIC_PATH)) return false;
66
67 // what we want:
68 // 1) don't show at the same market for a while
69 // 2) randomly don't show at any particular market,
70 // unless the player hasn't seen any delivery events in a while
71
73 return false;
74 }
75
77
78 if (destination == null) return false;
79
80 if (escrow > 0 && market.isPlayerOwned()) return false;
81
83 && shownAt != market) {
84 if (random.nextFloat() > PROB_TO_SHOW) return false;
85 }
86
89
90 return true;
91 }
92
93 @Override
94 protected void regen(MarketAPI market) {
95 //if (this.market == market) return;
96 if (this.market != market) {
98 playerFuelCap = 0;
99 }
100
101 this.market = market;
102 random = new Random(seed + market.getId().hashCode());
103 //random = Misc.random;
104
106
107 if (destination != null) {
109 }
110 }
111
112 public static class DestinationData {
113 public MarketAPI dest;
114 public CommodityOnMarketAPI comFrom;
115 public CommodityOnMarketAPI com;
116 public float distLY;
117
118 public boolean fromHasPA = false;
119 public boolean fromHasCells = false;
120
121 public boolean hasPA = false;
122 public boolean hasCells = false;
123
124 public boolean illegal = false;
125
126 public float score = 0;
127
128 public DestinationData(MarketAPI from, MarketAPI dest,
130 this.dest = dest;
131 this.comFrom = comFrom;
132 this.com = comDest;
134
135 fromHasPA = from.hasCondition(Conditions.PIRATE_ACTIVITY);
136 fromHasCells = from.hasCondition(Conditions.PATHER_CELLS);
138 hasCells = dest.hasCondition(Conditions.PATHER_CELLS);
139
140 illegal = dest.isIllegal(com.getId());
141
142 score += Math.min(distLY, 10f);
143
144 if (fromHasPA) score += 10f;
145 if (fromHasCells) score += 5f;
146 if (hasPA) score += 10f;
147 if (hasCells) score += 5f;
148
149// score += (comFrom.getAvailable() + comFrom.getMaxSupply()) * 0.5f;
150// score += comDest.getMaxDemand();
151
152 }
153 }
154
155 protected void computeData(MarketAPI market) {
156
157 data = null;
158 destination = null;
159 reward = 0;
160 duration = 0;
161 faction = null;
162 quantity = 0;
163 commodity = null;
164
165 List<CommodityOnMarketAPI> commodities = new ArrayList<CommodityOnMarketAPI>();
167 if (com.isNonEcon()) continue;
168 if (com.isMeta()) continue;
169 if (com.isPersonnel()) continue;
170 if (com.isIllegal()) continue;
171
172 if (com.getAvailable() <= 0) continue;
173 if (com.getMaxSupply() <= 0) continue;
174
175 commodities.add(com);
176 }
177
178 List<DestinationData> potential = new ArrayList<DestinationData>();
179
180 float maxScore = 0;
181 float maxDist = 0;
183 if (other == market) continue;
184 if (other.isHidden()) continue;
185 if (other.isInvalidMissionTarget()) continue;
186
187 if (other.getEconGroup() == null && market.getEconGroup() != null) continue;
188 if (other.getEconGroup() != null && !other.getEconGroup().equals(market.getEconGroup())) continue;
189
190 if (other.getStarSystem() == null) continue;
191
192 //WeightedRandomPicker<T>
193 for (CommodityOnMarketAPI com : commodities) {
194 //CommodityOnMarketAPI otherCom = other.getCommodityData(com.getId());
195 CommodityOnMarketAPI otherCom = other.getCommodityData(com.getDemandClass());
196 if (otherCom.getMaxDemand() <= 0) continue;
197
198 DestinationData data = new DestinationData(market, other, com, otherCom);
199 if (data.illegal) continue;
200 if (data.score > maxScore) {
201 maxScore = data.score;
202 }
203 if (data.distLY > maxDist) {
204 maxDist = data.distLY;
205 }
206 potential.add(data);
207 }
208 }
209 if (maxDist > 10) maxDist = 10;
210
212 for (int i = 0; i < potential.size(); i++) {
213 DestinationData d = potential.get(i);
214 if (d.score > maxScore * 0.5f && d.distLY > maxDist * 0.5f) {
215 picker.add(d, d.score * d.score * d.score);
216 }
217 }
218
219// Collections.sort(potential, new Comparator<DestinationData>() {
220// public int compare(DestinationData o1, DestinationData o2) {
221// return (int) Math.signum(o2.score - o1.score);
222// }
223// });
224//
225//
226// WeightedRandomPicker<DestinationData> picker = new WeightedRandomPicker<DestinationData>(random);
227// for (int i = 0; i < potential.size() && i < 5; i++) {
228// DestinationData d = potential.get(i);
229// picker.add(d, d.score * d.score * d.score);
230// }
231
232 DestinationData pick = picker.pick();
233
234 if (pick == null) return;
235
236 destination = pick.dest;
237 duration = pick.distLY * 5 + 50;
238 duration = (int)duration / 10 * 10;
239
241 quantity = (int) cargo.getMaxCapacity();
242 if (pick.com.isFuel()) {
243 quantity = (int) cargo.getMaxFuel();
244 }
245
246 if (random.nextFloat() < PROB_HIGHER_CAPACITY) {
247 quantity *= 1f + random.nextFloat() * 3f;
248 quantity = (int) quantity;
249 }
250
251 // don't want mission at market to update quantity as player changes their fleet up
252 if (pick.com.isFuel()) {
253 if (playerFuelCap == 0) {
255 } else {
257 }
258 } else {
259 if (playerCargoCap == 0) {
261 } else {
263 }
264 }
265
266 quantity *= 0.5f + 0.25f * random.nextFloat();
267
268 // somewhat less of the more valuable stuff
269 quantity *= Math.min(1f, 200f / pick.comFrom.getCommodity().getBasePrice());
270
271 int limit = (int) (pick.comFrom.getAvailable() * pick.comFrom.getCommodity().getEconUnit());
272 limit *= 0.75f + 0.5f * random.nextFloat();
273 //if (quantity > 5000) quantity = 5000;
274 if (quantity > limit) quantity = limit;
275
276
277 if (quantity > 10000) quantity = quantity / 1000 * 1000;
278 else if (quantity > 100) quantity = quantity / 10 * 10;
279 else if (quantity > 10) quantity = quantity / 10 * 10;
280
281 if (quantity < 10) quantity = 10;
282
283
284 //float base = pick.com.getMarket().getSupplyPrice(pick.com.getId(), 1, true);
285 float base = pick.comFrom.getMarket().getSupplyPrice(pick.comFrom.getId(), 1, true);
286
287 if (quantity * base < 4000) {
288 base = Math.min(100, 4000 / quantity);
289 }
290
291// float minBase = 100;
292// if (quantity > 500) {
293// minBase = 50;
294// }
295 float minBase = 100f - 50f * Math.min(1f, quantity / 500f);
296 minBase = (base + minBase) * 0.75f;
297
298 if (base < minBase) base = minBase;
299
300 //float extra = 2000;
301
302 float mult = pick.score / 30f;
303 //if (market.isPlayerOwned() && mult > 2f) mult = 2f;
304 //if (market.isPlayerOwned()) mult *= 0.75f;
305
306 if (mult < 0.75f) mult = 0.75f;
307 //if (mult > 2) mult = 2;
308 reward = (int) (base * mult * quantity);
309
310// float minPerUnit = 50;
311// if (reward / quantity < minPerUnit) {
312// reward = (int) (minPerUnit * quantity);
313// }
314
315 reward = reward / 1000 * 1000;
316 if (reward < 4000) reward = 4000;
317
318
320 escrow = (int) (quantity * pick.comFrom.getCommodity().getBasePrice());
321 }
322
323
324 if (market.getFaction() == pick.dest.getFaction()) {
326 } else {
328 if (faction == null) faction = market.getFaction();
329 }
330
331 commodity = pick.comFrom.getId();
332
333 data = pick;
334 }
335
336 protected int getNegotiatedAmount() {
337 return (int) (reward * 1.5f);
338 }
339
340 protected void addStoryOption() {
341 String id = "negotiate_id";
342 options.addOption("Negotiate a higher fee for the delivery", id);
343
344 StoryOptionParams params = new StoryOptionParams(id, 1, "negotiateDeliveryFee", Sounds.STORY_POINT_SPEND_INDUSTRY,
345 "Negotiated higher fee for delivery of " + data.comFrom.getCommodity().getLowerCaseName() + " to " + data.dest.getName());
346
347 SetStoryOption.set(dialog, params,
348 new BaseOptionStoryPointActionDelegate(dialog, params) {
349
350 @Override
351 public void confirm() {
352 super.confirm();
357 options.addOption("Continue", OPTION_CONFIRM);
358 //optionSelected(null, OPTION_CONFIRM);
359 }
360
361 @Override
362 public String getTitle() {
363 //return "Negotiating delivery fee";
364 return null;
365 }
366
367 @Override
368 public void createDescription(TooltipMakerAPI info) {
369 float opad = 10f;
370
371 info.addSpacer(-opad);
372
374 info.addPara("You're able to negotiate the delivery fee from %s up to " +
375 "%s.", 0f, Misc.getHighlightColor(),
378
379 info.addSpacer(opad * 2f);
380 addActionCostSection(info);
381 }
382
383 });
384 }
385
386 @Override
387 protected boolean canAccept() {
388 if (escrow <= 0) return true;
389 float credits = Global.getSector().getPlayerFleet().getCargo().getCredits().get();
390 boolean canAfford = credits >= escrow;
391 return canAfford;
392 }
393
394 @Override
409
410 protected void createIntel() {
412
414 ACCEPTED_AT_THIS_MARKET_DURATION * (0.75f + random.nextFloat() * 0.5f));
415 }
416
417 @Override
418 protected void adjustPerson(PersonAPI person) {
419 super.adjustPerson(person);
422 }
423
424 @Override
425 protected String getPersonFaction() {
426 return faction.getId();
427 }
428
429 @Override
430 protected String getPersonRank() {
431 return Ranks.CITIZEN;
432 }
433
434 @Override
435 protected String getPersonPost() {
436 //return Ranks.CITIZEN;
439 }
440
441 @Override
442 protected float getPriceMult() {
443 return 0;
444 }
445
446 @Override
447 protected String getPrompt() {
448 if (faction.getId().equals(Factions.INDEPENDENT)) {
449 return "At a corner table, a concerned-looking " + getManOrWoman() +
450 " glumly examines " + getHisOrHer() + " TriPad.";
451 } else {
452 return "At a corner table, a concerned-looking " + getManOrWoman() +
453 " in " + faction.getPersonNamePrefixAOrAn() + " " + faction.getPersonNamePrefix() + " uniform " +
454 " glumly examines " + getHisOrHer() + " TriPad.";
455 }
456 }
457
458 @Override
459 protected String getOptionText() {
460 return "Nod to the concerned " + getManOrWoman() + " and walk over to " + getHisOrHer() + " table";
461 }
462
463 @Override
464 protected String getMainText() {
465 String str = "";
466 if (market.isPlayerOwned()) {
467 String sir = "sir";
468 if (Global.getSector().getPlayerPerson().isFemale()) sir = "ma'am";
469 str = "\"Oh, it's you, " + sir + "!\", " + getHeOrShe() +
470 " exclaims. Taking a moment to recover " + getHisOrHer() + " composure, " + getHeOrShe() + " says \"We've got a little logistical problem that could use " +
471 "your personal touch. " +
472 "There are %s units of " + data.comFrom.getCommodity().getLowerCaseName() + " that urgently need to be delivered " +
473 " to %s" +
474 ", in the " + data.dest.getStarSystem().getNameWithLowercaseType() + ". ";
475 if (data.fromHasPA || data.hasPA) {
476 str += "However, recent pirate activity has been making that difficult, and the regular trade fleets " +
477 "aren't quite up to the task.\"";
478 } else if (data.fromHasCells || data.hasCells) {
479 str += "However, recent Pather cell activity has been making that difficult, and the regular trade fleets " +
480 "aren't quite up to the task.\"";
481 } else {
482 str += "But, well, you know what trader captains are like. " +
483 "There have been some disagreements over hazard pay, and it's left us in the lurch.\"";
484 }
485 } else {
486 str = "After a brief introduction, " + getHeOrShe() + " wastes no time in getting to the point.\n\n" +
487 "\"I've got %s units of " + data.comFrom.getCommodity().getLowerCaseName() + " that urgently need to be delivered " +
488 " to %s" +
489 ", in the " + data.dest.getStarSystem().getNameWithLowercaseType() + ". ";
490 if (data.fromHasPA || data.hasPA) {
491 str += "Recent pirate activity has been making that difficult, but you look like someone that could " +
492 "get the job done.\"";
493 } else if (data.fromHasCells || data.hasCells) {
494 str += "Recent Pather cell activity has been making that difficult, but I'm sure you can handle " +
495 "any trouble.\"";
496 } else {
497 str += "We've had some disputes with the regular shipping company, and it's left us in the lurch. " +
498 "Should be a milk run for someone like you, though.\"";
499 }
500 }
501
502 //str += "\n\nYou recall that " + data.dest.getName() + " is under %s control, and %s light-years away. ";
503
504 String where = "located in hyperspace,";
505 if (data.dest.getStarSystem() != null) {
506 //where = "located in the " + data.dest.getStarSystem().getNameWithLowercaseType() + ", which is";
507 where = "located in the " + data.dest.getStarSystem().getNameWithLowercaseType() + "";
508 }
509 //str += "\n\nYou recall that " + data.dest.getName() + " is under %s control, and " + where + " %s light-years away. ";
510 str += "\n\nYou recall that " + data.dest.getName() + " is under %s control, and " + where + ".";
511
513 if (data.comFrom.isFuel()) {
514 int cap = cargo.getFreeFuelSpace();
515 if (cap > 1) {
516 str += " Your fleet's fuel tanks can hold an additional %s units of fuel.";
517 } else {
518 str += "%sYour fleet's fuel tanks are currently full."; // %s - need to have same number of highlights
519 }
520 } else {
521 int cap = (int) cargo.getSpaceLeft();
522 if (cap > 1) {
523 str += " Your fleet's holds can accommodate an additional %s units of cargo.";
524 } else {
525 str += "%sYour fleet's cargo holds are currently full.";
526 }
527 }
528
529 if (market.isPlayerOwned()) {
530 str += "\n\n" + Misc.ucFirst(getHeOrShe()) + " double-checks something on " + getHisOrHer() + " pad. " +
531 "\"The customer will pay %s upon delivery within %s days. Can you take this on?\"";
532 } else {
533 if (escrow > 0) {
534 str += "\n\n" + Misc.ucFirst(getHeOrShe()) + " double-checks something on " + getHisOrHer() + " pad. " +
535 "\"The offer is %s credits, payable upon delivery within %s days. You'll also have to " +
536 "transfer %s to an escrow account. This will be returned to you " +
537 "when you complete the delivery - " +
538 "standard insurance procedure, you understand.\"";
539 } else {
540 str += "\n\n" + Misc.ucFirst(getHeOrShe()) + " double-checks something on " + getHisOrHer() + " pad. " +
541 "\"The offer is %s credits, payable upon delivery within %s days. You in?\"";
542 }
543 }
544
545 return str;
546 }
547
548 @Override
549 protected String [] getMainTextTokens() {
551 int cap = 0;
552 if (data.comFrom.isFuel()) {
553 cap = cargo.getFreeFuelSpace();
554 } else {
555 cap = (int) cargo.getSpaceLeft();
556 }
557 return new String [] { Misc.getWithDGS(quantity), data.dest.getName(),
559 //Misc.getRoundedValueMaxOneAfterDecimal(data.distLY),
560 cap > 1 ? Misc.getWithDGS(cap) : " ",
564 }
565 @Override
566 protected Color [] getMainTextColors() {
567 return new Color [] { Misc.getHighlightColor(),
568 //data.dest.getFaction().getBaseUIColor(),
571 //Misc.getHighlightColor(),
576 }
577
578 @Override
579 protected String getConfirmText() {
580 if (market.isPlayerOwned()) {
581 return "Agree to handle the contract";
582 }
583 return "Accept the delivery contract";
584 }
585
586 protected String getNegotiatedText() {
587 return "\"You drive a hard bargain! Very well, it's a deal.\" " + Misc.ucFirst(getHeOrShe()) +
588 " does not appear too displeased. You consider that the initial offer " +
589 "was probably on the low side.";
590 }
591
592 @Override
593 protected String getCancelText() {
594 if (market.isPlayerOwned()) {
595 return "Decline, explaining that you've got other urgent business to attend to";
596 }
597 return "Decline the offer, explaining that you've got other plans";
598 }
599
600 @Override
601 protected String getAcceptText() {
602 return "You receive authorization codes to access a dockside warehouse, and " +
603 "contact your quartermaster with instructions to begin loading the cargo.";
604 }
605
606
608 return destination;
609 }
610
611 public int getReward() {
612 return reward;
613 }
614
615 public float getDuration() {
616 return duration;
617 }
618
620 return faction;
621 }
622
623 public DestinationData getData() {
624 return data;
625 }
626
627 public int getQuantity() {
628 return quantity;
629 }
630
631 public int getEscrow() {
632 return escrow;
633 }
634
635 protected boolean showCargoCap() {
636 return false;
637 }
638
639 @Override
640 protected void showTotalAndOptions() {
641 super.showTotalAndOptions();
642
644 String text = null;
645 Set<String> tags = new LinkedHashSet<String>();
646 tags.add(Tags.INTEL_MISSIONS);
647
648 dialog.getVisualPanel().showMapMarker(getDestination().getPrimaryEntity(),
649 "Destination: " + getDestination().getName(), getDestination().getFaction().getBaseUIColor(),
650 true, icon, text, tags);
651 }
652
653
654
655}
656
657
658
static SettingsAPI getSettings()
Definition Global.java:57
static SectorAPI getSector()
Definition Global.java:65
static void addCommodityGainText(String commodityId, int quantity, TextPanelAPI text)
static void addCreditsLossText(int credits, TextPanelAPI text)
static boolean set(String ruleId, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap, String params)
static Color getTextColor()
Definition Misc.java:839
static String getDGSCredits(float num)
Definition Misc.java:1390
static String getWithDGS(float num)
Definition Misc.java:1381
static float getDistanceLY(SectorEntityToken from, SectorEntityToken to)
Definition Misc.java:602
static String ucFirst(String str)
Definition Misc.java:559
static Color getHighlightColor()
Definition Misc.java:792
CommoditySpecAPI getCommoditySpec(String commodityId)
void addCommodity(String commodityId, float quantity)
void addOption(String text, Object data)
FactionAPI getFaction(String factionId)
void showMapMarker(SectorEntityToken marker, String title, Color titleColor, boolean withIntel, String icon, String text, Set< String > intelTags)
List< CommodityOnMarketAPI > getCommoditiesCopy()
boolean isIllegal(String commodityId)
void set(String key, Object value)
void setImportanceAndVoice(PersonImportance importance, Random random)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
UIComponentAPI addSpacer(float height)