Starsector API
Loading...
Searching...
No Matches
LocalResourcesSubmarketPlugin.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.submarkets;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Comparator;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.List;
9import java.util.Map;
10import java.util.Set;
11
12import com.fs.starfarer.api.Global;
13import com.fs.starfarer.api.campaign.CargoAPI;
14import com.fs.starfarer.api.campaign.CargoStackAPI;
15import com.fs.starfarer.api.campaign.CoreUIAPI;
16import com.fs.starfarer.api.campaign.PlayerMarketTransaction;
17import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
18import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
19import com.fs.starfarer.api.campaign.econ.MonthlyReport;
20import com.fs.starfarer.api.campaign.econ.SubmarketAPI;
21import com.fs.starfarer.api.campaign.econ.EconomyAPI.EconomyUpdateListener;
22import com.fs.starfarer.api.campaign.econ.MonthlyReport.FDNode;
23import com.fs.starfarer.api.campaign.listeners.EconomyTickListener;
24import com.fs.starfarer.api.combat.MutableStat;
25import com.fs.starfarer.api.combat.MutableStat.StatMod;
26import com.fs.starfarer.api.impl.campaign.econ.impl.BaseIndustry;
27import com.fs.starfarer.api.impl.campaign.ids.Commodities;
28import com.fs.starfarer.api.impl.campaign.ids.Strings;
29import com.fs.starfarer.api.impl.campaign.ids.Submarkets;
30import com.fs.starfarer.api.impl.campaign.shared.SharedData;
31import com.fs.starfarer.api.ui.Alignment;
32import com.fs.starfarer.api.ui.TooltipMakerAPI;
33import com.fs.starfarer.api.util.Misc;
34
35public class LocalResourcesSubmarketPlugin extends BaseSubmarketPlugin implements EconomyUpdateListener, EconomyTickListener {
36
37 public static float STOCKPILE_MULT_PRODUCTION = Global.getSettings().getFloat("stockpileMultProduction");
38 public static float STOCKPILE_MULT_EXCESS = Global.getSettings().getFloat("stockpileMultExcess");
39 public static float STOCKPILE_MULT_IMPORTS = Global.getSettings().getFloat("stockpileMultImports");
40 public static float STOCKPILE_MAX_MONTHS = Global.getSettings().getFloat("stockpileMaxMonths");
41
42 public static float STOCKPILE_COST_MULT = Global.getSettings().getFloat("stockpileCostMult");
43 public static float STOCKPILE_SHORTAGE_COST_MULT = Global.getSettings().getFloat("stockpileShortageCostMult");
44
45
46 protected CargoAPI taken;
47 protected CargoAPI left;
48
49 protected Map<String, MutableStat> stockpilingBonus = new HashMap<String, MutableStat>();
50
55
61
62 public boolean showInFleetScreen() {
63 return false;
64 }
65
66 public boolean showInCargoScreen() {
67 return true;
68 }
69
70 public boolean isEnabled(CoreUIAPI ui) {
71 return true;
72 }
73
74 @Override
75 public void advance(float amount) {
76 super.advance(amount);
77
78 // need to add to stockpiles every frame because the player can see stockpiles
79 // in the "market info" screen and updateCargoPrePlayerInteraction() doesn't get called from there
80 addAndRemoveStockpiledResources(amount, true, false, true);
81
82// if (!Global.getSector().getListenerManager().hasListener(this)) {
83// Global.getSector().getListenerManager().addListener(this);
84// }
85 }
86
88 if (market.isIllegal(com)) {
90 return false;
91 }
92 return true;
93 }
94 return true;
95 }
96
97 @Override
98 public boolean isIllegalOnSubmarket(CargoStackAPI stack, TransferAction action) {
99 if (stack.getCommodityId() == null) return true;
101 return false;
102 }
103
104
105 @Override
106 public String getIllegalTransferText(CargoStackAPI stack, TransferAction action) {
107 return "Can only store resources";
108 }
109
110
111 @Override
113
114 int demand = com.getMaxDemand();
115
116 int shippingGlobal = com.getCommodityMarketData().getMaxShipping(com.getMarket(), false);
117 //int shippingInFaction = com.getCommodityMarketData().getMaxShipping(com.getMarket(), true);
118
119 int available = com.getAvailable();
120 String modId = submarket.getSpecId();
121 StatMod mod = com.getAvailableStat().getFlatStatMod(modId);
122 if (mod != null) {
123 available -= (int) mod.value;
124 if (available < 0) available = 0;
125 }
126
127 int production = com.getMaxSupply();
128 production = Math.min(production, available);
129
130 int export = 0;
131 demand = com.getMaxDemand();
132 export = (int) Math.min(production, shippingGlobal);
133
134 int extra = available - Math.max(export, demand);
135 if (extra < 0) extra = 0;
136
137 int deficit = demand - available;
138// int demandMet = Math.min(available, demand);
139// int demandMetWithLocal = Math.min(available, production) - extra;
140 int imports = available - production;
141 if (imports < 0) imports = 0;
142
143 production -= extra;
144
145 float unit = com.getCommodity().getEconUnit();
146
147 float limit = 0f;
148 limit += STOCKPILE_MULT_EXCESS * BaseIndustry.getSizeMult(extra) * unit;
149 limit += STOCKPILE_MULT_PRODUCTION * BaseIndustry.getSizeMult(production) * unit;
150 limit += STOCKPILE_MULT_IMPORTS * BaseIndustry.getSizeMult(imports) * unit;
151
152 String cid = com.getId();
153 if (stockpilingBonus.containsKey(cid)) {
154 limit += stockpilingBonus.get(cid).getModifiedValue() * unit;
155 }
156
157 //limit *= com.getMarket().getStockpileMult().getModifiedValue();
158 limit *= STOCKPILE_MAX_MONTHS;
159
160 if (deficit > 0) {
161 limit = 0;
162 }
163
164 if (limit < 0) limit = 0;
165
166 return (int) limit;
167 }
168
169
170
171
172 @Override
174// float mult = com.getMarket().getStockpileMult().getModifiedValue();
175// if (mult > 0) {
176// return 1f / mult;
177// }
178 return 1f / STOCKPILE_MAX_MONTHS;
179 }
180
181// public int getApproximateStockpilingCost() {
182// CargoAPI cargo = getCargo();
183//
184// float total = 0f;
185// for (CommodityOnMarketAPI com : market.getCommoditiesCopy()) {
186// if (com.isNonEcon()) continue;
187// if (com.getCommodity().isMeta()) continue;
188//
189// int limit = getStockpileLimit(com);
190// if (limit <= 0) continue;
191//
192// int needed = (int) (limit - cargo.getCommodityQuantity(com.getId()));
193// if (needed <= 0) continue;
194//
195// float price = getStockpilingUnitPrice(com.getCommodity());
196// total += price * needed;
197// }
198//
199// return (int)(Math.ceil(total / 1000f) * 1000f);
200// }
201
202
203 public void commodityUpdated(String commodityId) {
204 if (Global.getSector().isPaused()) {
206 addAndRemoveStockpiledResources(com, 0f, true, false, false);
207 }
208 }
209
210 public void economyUpdated() {
211 if (Global.getSector().isPaused()) { // to apply shortage-countering during economy steps in UI operations
212 addAndRemoveStockpiledResources(0f, true, false, false);
213 }
214 }
215
216 public boolean isEconomyListenerExpired() {
217// if (!market.isPlayerOwned()) {
218// market.removeSubmarket(submarket.getSpecId());
219// return true;
220// }
222 }
223
224
225
226
227 @Override
228 public boolean isParticipatesInEconomy() {
229 return false;
230 }
231
232 @Override
233 public boolean isHidden() {
234// if (true) return false;
235 return !market.isPlayerOwned();
236 }
237
238 public float getTariff() {
239 return 0f;
240 }
241
242 @Override
243 public boolean isFreeTransfer() {
244 return true;
245 }
246
247 protected transient CargoAPI preTransactionCargoCopy = null;
251 getCargo().sort();
252 //sinceLastCargoUpdate = 0f;
253 }
254
256 //addAndRemoveStockpiledResources(0f, true, false, false); // not needed b/c economyUpdated() gets called
257
258 sinceLastCargoUpdate = 0f; // to reset how long until one more unit gets added if something was drawn down to 0
259
262
263 taken.addAll(transaction.getBought());
264 left.addAll(transaction.getSold());
265
266 CargoAPI copy = taken.createCopy();
268 left.removeAll(copy);
269 }
270
271 protected Object readResolve() {
272 super.readResolve();
273
274 if (taken == null) {
276 }
277 if (left == null) {
279 }
280 if (stockpilingBonus == null) {
281 stockpilingBonus = new HashMap<String, MutableStat>();
282 }
283 return this;
284 }
285
286 public MutableStat getStockpilingBonus(String cid) {
287 MutableStat stat = stockpilingBonus.get(cid);
288 if (stat == null) {
289 stat = new MutableStat(0);
290 stockpilingBonus.put(cid, stat);
291 }
292 return stat;
293 }
294
295 public CargoAPI getLeft() {
296 return left;
297 }
298
300 List<CommodityOnMarketAPI> all = new ArrayList<CommodityOnMarketAPI>(market.getAllCommodities());
301
302 float totalCost = 0f;
303
305
306 for (CommodityOnMarketAPI com : all) {
307 int curr = (int) cargo.getCommodityQuantity(com.getId());
308 if (curr <= 0) continue;
309
311 units = Math.min(units, cargo.getCommodityQuantity(com.getId()));
312 units -= taken.getCommodityQuantity(com.getId());
313 if (units > 0) {
314 float per = LocalResourcesSubmarketPlugin.getStockpilingUnitPrice(com.getCommodity(), true);
315 totalCost += units * per;
316 }
317 }
318 return (int) totalCost;
319 }
320
321 public static int getStockpilingUnitPrice(CommoditySpecAPI spec, boolean forShortageCountering) {
322 float mult = STOCKPILE_COST_MULT;
323 if (forShortageCountering) mult = STOCKPILE_SHORTAGE_COST_MULT;
324 int result = (int) Math.round((spec.getBasePrice() * mult));
325 if (result < 1) result = 1;
326 return result;
327// float unitPrice = market.getDemandPrice(com.getId(), 1, true);
328// if (unitPrice < 1) unitPrice = 1;
329// return (int) unitPrice;
330 }
331
333 String modId = Submarkets.LOCAL_RESOURCES;
334
335 StatMod mod = com.getAvailableStat().getFlatMods().get(modId);
336 float modAlready = 0;
337 if (mod != null) modAlready = mod.value;
338
339 int demand = com.getMaxDemand();
340 int available = (int) Math.round(com.getAvailable() - modAlready);
341
342 if (demand > available) {
343 float deficitDrawBaseAmount = BaseIndustry.getSizeMult(demand) - BaseIndustry.getSizeMult(available);
344 deficitDrawBaseAmount *= com.getCommodity().getEconUnit();
345 return deficitDrawBaseAmount;
346 }
347 return 0;
348 }
349
350 protected boolean doShortageCountering(CommodityOnMarketAPI com, float amount, boolean withShortageCountering) {
352 String modId = submarket.getSpecId();
353
354 com.getAvailableStat().unmodifyFlat(modId);
355
356 int demand = com.getMaxDemand();
357 int available = com.getAvailable();
358
359// if (com.isIllegal() && com.getMarket().isPlayerOwned()) {
360// System.out.println("wefwefew");
361// }
362
363 if (withShortageCountering && demand > available) {
364 // draw resources and apply bonus
365 int deficit = demand - available;
366 if (deficit != deficit) return false; // bug report indicates possible NaN here; not sure how
367
368 float deficitDrawBaseAmount = BaseIndustry.getSizeMult(demand) - BaseIndustry.getSizeMult(available);
369 deficitDrawBaseAmount *= com.getCommodity().getEconUnit();
370
371 float days = Global.getSector().getClock().convertToDays(amount);
372
373 float drawAmount = deficitDrawBaseAmount * days / 30f;
374 float curr = cargo.getCommodityQuantity(com.getId());
375 if (curr > 0 && deficitDrawBaseAmount > 0) {
376 int daysLeft = (int) (curr / deficitDrawBaseAmount * 30f);
377 String daysStr = "days";
378 if (daysLeft <= 1) {
379 daysLeft = 1;
380 daysStr = "day";
381 }
382 com.getAvailableStat().modifyFlat(modId, deficit,
383 "Local resource stockpiles (" + daysLeft + " " + daysStr + " left)");
384
385 float free = left.getCommodityQuantity(com.getId());
386 free = Math.min(drawAmount, free);
387 left.removeCommodity(com.getId(), free);
388 if (drawAmount > 0) {
389 cargo.removeCommodity(com.getId(), drawAmount);
390 }
391 drawAmount -= free;
392
393 if (market.isPlayerOwned() && drawAmount > 0) {
395 FDNode node = report.getCounterShortageNode(market);
396
397 CargoAPI tooltipCargo = (CargoAPI) node.custom2;
398 float addToTooltipCargo = drawAmount;
399 float q = tooltipCargo.getCommodityQuantity(com.getId()) + addToTooltipCargo;
400 if (q < 1) {
401 addToTooltipCargo = 1f; // add at least 1 unit or it won't do anything
402 }
403 tooltipCargo.addCommodity(com.getId(), addToTooltipCargo);
404
405 float unitPrice = (int) getStockpilingUnitPrice(com.getCommodity(), true);
406 //node.upkeep += unitPrice * addAmount;
407
408 FDNode comNode = report.getNode(node, com.getId());
409
410 CommoditySpecAPI spec = com.getCommodity();
411 comNode.icon = spec.getIconName();
412 comNode.upkeep += unitPrice * drawAmount;
413 comNode.custom = com;
414
415 if (comNode.custom2 == null) {
416 comNode.custom2 = 0f;
417 }
418 comNode.custom2 = (Float)comNode.custom2 + drawAmount;
419
420 float qty = Math.max(1, (Float) comNode.custom2);
421 qty = (float) Math.ceil(qty);
422 comNode.name = spec.getName() + " " + Strings.X + Misc.getWithDGS(qty);
423 comNode.tooltipCreator = report.getMonthlyReportTooltip();
424 }
425 }
426 return true;
427 }
428 return false;
429 }
430
431
432 public void reportEconomyMonthEnd() {
435 return;
436 }
437 }
438
439 public void reportEconomyTick(int iterIndex) {
442 return;
443 }
444
445 int lastIterInMonth = (int) Global.getSettings().getFloat("economyIterPerMonth") - 1;
446 if (iterIndex != lastIterInMonth) return;
447
448 if (market.isPlayerOwned()) {
449 CargoAPI copy = taken.createCopy();
451 left.removeAll(copy);
452
454
455
456 for (CargoStackAPI stack : taken.getStacksCopy()) {
457 if (!stack.isCommodityStack()) continue;
458
459 FDNode node = report.getRestockingNode(market);
460 CargoAPI tooltipCargo = (CargoAPI) node.custom2;
461
462 float addToTooltipCargo = stack.getSize();
463 String cid = stack.getCommodityId();
464 float q = tooltipCargo.getCommodityQuantity(cid) + addToTooltipCargo;
465 if (q < 1) {
466 addToTooltipCargo = 1f; // add at least 1 unit or it won't do anything
467 }
468 tooltipCargo.addCommodity(cid, addToTooltipCargo);
469
470 float unitPrice = (int) getStockpilingUnitPrice(stack.getResourceIfResource(), false);
471 //node.upkeep += unitPrice * addAmount;
472
473 FDNode comNode = report.getNode(node, cid);
474
475 CommoditySpecAPI spec = stack.getResourceIfResource();
476 comNode.icon = spec.getIconName();
477 comNode.upkeep += unitPrice * addToTooltipCargo;
478 comNode.custom = market.getCommodityData(cid);
479
480 if (comNode.custom2 == null) {
481 comNode.custom2 = 0f;
482 }
483 comNode.custom2 = (Float)comNode.custom2 + addToTooltipCargo;
484
485 float qty = Math.max(1, (Float) comNode.custom2);
486 qty = (float) Math.ceil(qty);
487 comNode.name = spec.getName() + " " + Strings.X + Misc.getWithDGS(qty);
488 comNode.tooltipCreator = report.getMonthlyReportTooltip();
489 }
490 }
491 taken.clear();
492 }
493
494
495 @Override
496 public String getBuyVerb() {
497 return "Take";
498 }
499
500 @Override
501 public String getSellVerb() {
502 return "Leave";
503 }
504
505 public String getTariffTextOverride() {
506 return "End of month";
507 }
508 public String getTariffValueOverride() {
509 if (preTransactionCargoCopy == null) return null; // could happen when visiting colony from colony list screen
511 //preTransactionCargoCopy;
512
513 float total = 0f;
514 Set<String> seen = new HashSet<String>();
516 if (!stack.isCommodityStack()) continue;
517
518 String cid = stack.getCommodityId();
519 if (seen.contains(cid)) continue;
520 seen.add(cid);
521
523
525 int post = (int) cargo.getCommodityQuantity(cid);
526
527 int units = pre - post; // player taking this many units
528
529 units -= left.getCommodityQuantity(cid);
530
531 if (units > 0) {
532 float price = getStockpilingUnitPrice(com.getCommodity(), false);
533 total += price * units;
534 }
535 }
536
537 return Misc.getDGSCredits(total);
538 }
539
540 public String getTotalTextOverride() {
541 return "Now";
542 }
543 public String getTotalValueOverride() {
544 return "0" + Strings.C;
545 //return "";
546 }
547
548
549
550 public boolean isTooltipExpandable() {
551 return false;
552 }
553
554 public float getTooltipWidth() {
555 return 500f;
556 }
557
558 protected void createTooltipAfterDescription(TooltipMakerAPI tooltip, boolean expanded) {
559 List<CommodityOnMarketAPI> all = new ArrayList<CommodityOnMarketAPI>(market.getAllCommodities());
560
561 Collections.sort(all, new Comparator<CommodityOnMarketAPI>() {
562 public int compare(CommodityOnMarketAPI o1, CommodityOnMarketAPI o2) {
563 int limit1 = getStockpileLimit(o1);
564 int limit2 = getStockpileLimit(o2);
565 return limit2 - limit1;
566 }
567 });
568
569 float opad = 10f;
570
571 tooltip.beginGridFlipped(400f, 1, 70f, opad);
572 int j = 0;
573 for (CommodityOnMarketAPI com : all) {
574 if (com.isNonEcon()) continue;
575 if (com.getCommodity().isMeta()) continue;
576
577 if (!shouldHaveCommodity(com)) continue;
578
579 int limit = (int) Math.round(getStockpileLimit(com) * getStockpilingAddRateMult(com));
580 if (limit <= 0) continue;
581
582 tooltip.addToGrid(0, j++,
583 com.getCommodity().getName(),
584 Misc.getWithDGS(limit));
585 //Misc.getWithDGS(curr) + " / " + Misc.getWithDGS(limit));
586 }
587
588 tooltip.addPara("A portion of the resources produced by the colony will be made available here. " +
589 "These resources can be extracted from the colony's economy for a cost equal to %s of their base value. " +
590 "This cost will be deducted at the end of the month.", opad,
591 Misc.getHighlightColor(), "" + (int)Math.round(STOCKPILE_COST_MULT * 100f) + "%");
592
593 tooltip.addPara("These resources can also be used to counter temporary shortages, for a " +
594 "cost equal to %s of their base value. If additional resources are placed here, they " +
595 "will be used as well, at no cost.", opad,
596 Misc.getHighlightColor(), "" + (int)Math.round(STOCKPILE_SHORTAGE_COST_MULT * 100f) + "%");
597
598
599 tooltip.addSectionHeading("Stockpiled per month", market.getFaction().getBaseUIColor(), market.getFaction().getDarkUIColor(), Alignment.MID, opad);
600 if (j > 0) {
601 tooltip.addGrid(opad);
602
603 tooltip.addPara("Stockpiles are limited to %s the monthly rate.", opad,
605 } else {
606 tooltip.addPara("No stockpiling.", opad);
607 }
608 }
609
610}
611
612
613
614
static SettingsAPI getSettings()
Definition Global.java:57
static FactoryAPI getFactory()
Definition Global.java:41
static SectorAPI getSector()
Definition Global.java:65
MonthlyReportNodeTooltipCreator getMonthlyReportTooltip()
void modifyFlat(String source, float value)
HashMap< String, StatMod > getFlatMods()
void addAndRemoveStockpiledResources(float amount, boolean withShortageCountering, boolean withDecreaseToLimit, boolean withCargoUpdate)
boolean doShortageCountering(CommodityOnMarketAPI com, float amount, boolean withShortageCountering)
static int getStockpilingUnitPrice(CommoditySpecAPI spec, boolean forShortageCountering)
static String getDGSCredits(float num)
Definition Misc.java:1390
static String getWithDGS(float num)
Definition Misc.java:1381
static Color getHighlightColor()
Definition Misc.java:792
CargoAPI createCargo(boolean unlimitedStacks)
void removeCommodity(String id, float quantity)
List< CargoStackAPI > getStacksCopy()
void addCommodity(String commodityId, float quantity)
ListenerManagerAPI getListenerManager()
MarketShareDataAPI getMarketShareData(MarketAPI market)
int getMaxShipping(MarketAPI market, boolean inFaction)
void addUpdateListener(EconomyUpdateListener listener)
CommodityOnMarketAPI getCommodityData(String commodityId)
boolean isIllegal(String commodityId)
List< CommodityOnMarketAPI > getAllCommodities()
Object addToGrid(int x, int y, String label, String value)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
LabelAPI addSectionHeading(String str, Alignment align, float pad)
void beginGridFlipped(float itemWidth, int cols, float valueWidth, float valuePad)