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
56 public void init(SubmarketAPI submarket) {
57 super.init(submarket);
58 Global.getSector().getEconomy().addUpdateListener(this);
59 Global.getSector().getListenerManager().addListener(this);
60 }
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
87 public boolean shouldHaveCommodity(CommodityOnMarketAPI com) {
88 if (market.isIllegal(com)) {
89 if (com.getCommodityMarketData().getMarketShareData(market).isSourceIsIllegal()) {
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;
100 if (stack.getResourceIfResource().hasTag(Commodities.TAG_NON_ECONOMIC)) 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
112 public int getStockpileLimit(CommodityOnMarketAPI com) {
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
173 public float getStockpilingAddRateMult(CommodityOnMarketAPI com) {
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()) {
205 CommodityOnMarketAPI com = market.getCommodityData(commodityId);
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// }
221 return !market.hasSubmarket(submarket.getSpecId());
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;
249 preTransactionCargoCopy = getCargo().createCopy();
251 getCargo().sort();
252 //sinceLastCargoUpdate = 0f;
253 }
254
255 public void reportPlayerMarketTransaction(PlayerMarketTransaction transaction) {
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
260 preTransactionCargoCopy = getCargo().createCopy();
262
263 taken.addAll(transaction.getBought());
264 left.addAll(transaction.getSold());
265
266 CargoAPI copy = taken.createCopy();
267 taken.removeAll(left);
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
304 CargoAPI cargo = getCargo();
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
332 public static float getDeficitMonthlyCommodityUnits(CommodityOnMarketAPI com) {
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) {
351 CargoAPI cargo = getCargo();
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) {
394 MonthlyReport report = SharedData.getData().getCurrentReport();
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() {
434 Global.getSector().getListenerManager().removeListener(this);
435 return;
436 }
437 }
438
439 public void reportEconomyTick(int iterIndex) {
441 Global.getSector().getListenerManager().removeListener(this);
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();
450 taken.removeAll(left);
451 left.removeAll(copy);
452
453 MonthlyReport report = SharedData.getData().getCurrentReport();
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
510 CargoAPI cargo = getCargo();
511 //preTransactionCargoCopy;
512
513 float total = 0f;
514 Set<String> seen = new HashSet<String>();
515 for (CargoStackAPI stack : preTransactionCargoCopy.getStacksCopy()) {
516 if (!stack.isCommodityStack()) continue;
517
518 String cid = stack.getCommodityId();
519 if (seen.contains(cid)) continue;
520 seen.add(cid);
521
522 CommodityOnMarketAPI com = market.getCommodityData(cid);
523
524 int pre = (int) preTransactionCargoCopy.getCommodityQuantity(cid);
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,
604 Misc.getHighlightColor(), "" + (int)STOCKPILE_MAX_MONTHS + Strings.X);
605 } else {
606 tooltip.addPara("No stockpiling.", opad);
607 }
608 }
609
610}
611
612
613
614
static SettingsAPI getSettings()
Definition Global.java:51
static FactoryAPI getFactory()
Definition Global.java:35
static SectorAPI getSector()
Definition Global.java:59
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)
CargoAPI createCargo(boolean unlimitedStacks)