57 public static enum Ending {
64 public static class PlayerFoodTransaction {
65 public long timestamp;
70 public float quantity;
73 private boolean ended =
false;
75 private float maxDurationDays;
76 private float daysBeforeReliefCheck;
77 private float daysBeforeReliefSend;
78 private float elapsedDays = 0f;
79 private int stage = 0;
82 private float preEventFoodLevel;
85 private float baseFoodToMeetShortage = 0;
87 private float netDeliveredByPlayerBlack = 0;
88 private float netDeliveredByPlayerOther = 0;
91 private float originalReliefFleetPoints = 0;
92 private CampaignFleetAPI reliefFleet;
94 private MessagePriority messagePriority = MessagePriority.CLUSTER;
95 private String foodShortageConditionToken =
null;
97 private MarketAPI reliefMarket;
105 super.startEvent(
true);
106 if (
market ==
null)
return;
108 switch (
market.getSize()) {
112 messagePriority = MessagePriority.SYSTEM;
116 messagePriority = MessagePriority.CLUSTER;
122 messagePriority = MessagePriority.SECTOR;
130 baseFoodToMeetShortage = getBaseShortageAmount();
133 maxDurationDays = 20f + (float) Math.random() * 20f;
135 daysBeforeReliefCheck = 7f + (float) Math.random() * 7f;
136 daysBeforeReliefSend = daysBeforeReliefCheck + (float) Math.random() * 7f;
138 foodShortageConditionToken =
market.addCondition(Conditions.EVENT_FOOD_SHORTAGE,
this);
139 CommodityOnMarketAPI com =
market.getCommodityData(Commodities.FOOD);
141 preEventFoodLevel = com.getStockpile();
142 com.setStockpile(com.getStockpile() * getStockpileMult());
147 SubmarketAPI open =
market.getSubmarket(
"open_market");
149 float food = open.getCargo().getQuantity(CargoItemType.RESOURCES, Commodities.FOOD);
150 open.getCargo().removeItems(CargoItemType.RESOURCES, Commodities.FOOD, food);
153 SubmarketAPI black =
market.getSubmarket(
"black_market");
155 black.getPlugin().updateCargoPrePlayerInteraction();
156 black.getCargo().addItems(CargoItemType.RESOURCES, Commodities.FOOD,
157 (
int)Math.max(1, baseFoodToMeetShortage * (0.1f + (
float) Math.random() * 0.05f)));
163 ", food needed: " + (int) baseFoodToMeetShortage +
164 ", stockpile mult: " + getStockpileMult() +
165 ", max duration: " + (int) maxDurationDays +
" days");
169 private float getBaseShortageAmount() {
170 float stockpileMult = getStockpileMult();
171 CommodityOnMarketAPI com =
market.getCommodityData(Commodities.FOOD);
172 float amount = com.getStockpile() * (1f - stockpileMult);
173 if (amount < 50) amount = 50;
174 return Misc.getRounded(amount);
178 private float getStockpileMult() {
179 switch ((
int)
market.getSize()) {
218 switch ((
int)
market.getSize()) {
251 float mult = Math.min(1f, netDeliveredByPlayerBlack / baseFoodToMeetShortage);
252 return Math.round(base * mult);
269 if (elapsedDays > daysBeforeReliefCheck && stage == 0) {
273 public boolean acceptMarket(MarketAPI
market) {
280 if (SharedData.getData().getMarketsThatSentRelief().contains(
market.getId())) {
283 if (
market.getCommodityData(Commodities.FOOD).getStockpile() < baseFoodToMeetShortage) {
287 CampaignEventManagerAPI manager =
Global.
getSector().getEventManager();
288 EventProbabilityAPI ep = manager.getProbability(Events.FOOD_SHORTAGE,
market);
289 if (ep.getProbability() > 0.1) {
292 if (manager.isOngoing(ep)) {
295 if ((
float) Math.random() * 10f >
market.getStabilityValue())
return false;
303 if (
market.getFaction().getId().equals(
"pirates")) {
307 if (reliefMarket !=
null) {
308 SharedData.getData().getMarketsThatSentRelief().add(reliefMarket.getId(), 14f + (float) Math.random() * 14f);
309 Global.
getSector().reportEventStage(
this,
"warning_relief", reliefMarket.getPrimaryEntity(), messagePriority);
312 applyReliefFleetDidNotMakeItConsequences();
313 Global.
getSector().reportEventStage(
this,
"relief_unavailable", messagePriority);
317 if (elapsedDays > daysBeforeReliefSend && stage == 1) {
319 if (reliefMarket !=
null) {
321 if (reliefFleet ==
null) {
323 applyReliefFleetDidNotMakeItConsequences();
324 Global.
getSector().reportEventStage(
this,
"relief_unavailable", messagePriority);
326 originalReliefFleetPoints = reliefFleet.getFleetPoints();
328 SectorEntityToken
entity = reliefMarket.getPrimaryEntity();
329 reliefMarket.getPrimaryEntity().getContainingLocation().addEntity(reliefFleet);
330 reliefFleet.setLocation(
entity.getLocation().x,
entity.getLocation().y);
332 reliefFleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, reliefMarket.getPrimaryEntity(), 2,
333 "loading food from " + reliefMarket.getName(),
new Script() {
335 reliefFleet.getCargo().addItems(CargoItemType.RESOURCES, Commodities.FOOD,
336 reliefFleet.getCargo().getMaxCapacity() * (1f - FleetFactory.SUPPLIES_FRACTION));
339 reliefFleet.addAssignment(FleetAssignment.DELIVER_RESOURCES,
market.getPrimaryEntity(), 1000,
340 "delivering food relief from " + reliefMarket.getName() +
" to " +
market.getName());
341 reliefFleet.addAssignment(FleetAssignment.ORBIT_PASSIVE,
market.getPrimaryEntity(), 2,
342 "offloading food at " +
market.getName(),
new Script() {
344 endEvent(Ending.RELIEF_ARRIVED);
347 reliefFleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, reliefMarket.getPrimaryEntity(), 1000,
348 "returning to " + reliefMarket.getName() +
" after delivering food relief to " +
market.getName());
350 Global.
getSector().reportEventStage(
this,
"relief_sent", reliefMarket.getPrimaryEntity(), messagePriority);
351 log.info(
getLoggingId() +
" Sending relief fleet from " + reliefMarket.getName());
356 if (reliefFleet !=
null) {
357 float currPoints = reliefFleet.getFleetPoints();
358 if (currPoints < originalReliefFleetPoints * 0.5f) {
359 Global.
getSector().reportEventStage(
this,
"relief_aborted", reliefMarket.getPrimaryEntity(), messagePriority);
360 log.info(
getLoggingId() +
" Relief aborted, fleet returning to " + reliefMarket.getName());
362 reliefFleet.clearAssignments();
363 reliefFleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, reliefMarket.getPrimaryEntity(), 1000);
364 applyReliefFleetDidNotMakeItConsequences();
370 endEvent(Ending.DURATION_EXPIRED);
376 private void applyReliefFleetDidNotMakeItConsequences() {
379 log.info(
" +" + abortUnrest +
" unrest at " +
market.getName());
382 private void createReliefFleet() {
383 reliefFleet = FleetFactory.createEmptyFleet(reliefMarket.getFactionId(), FleetTypes.FOOD_RELIEF_FLEET, reliefMarket);
385 int size =
market.getSize();
387 float freighter = size * 1.5f;
388 float transport = size * 0.25f;
392 reliefFleet = FleetFactoryV3.createFleet(
new FleetParamsV3(
394 FleetTypes.FOOD_RELIEF_FLEET,
407 private void endEvent(Ending ending) {
409 market.removeCondition(Conditions.EVENT_FOOD_SHORTAGE);
412 CommodityOnMarketAPI com =
market.getCommodityData(Commodities.FOOD);
413 com.setStockpile(com.getStockpile() / getStockpileMult());
414 com.getPlayerSupplyPriceMod().unmodifyMult(
PRICE_MOD_ID);
415 com.getPlayerSupplyPriceMod().unmodifyFlat(
PRICE_MOD_ID);
423 Misc.unsetAll(
"$market.foodShortage", MemKeys.MARKET,
market.getMemoryWithoutUpdate());
424 final InteractionDialogAPI dialog =
Global.
getSector().getCampaignUI().getCurrentInteractionDialog();
426 case DURATION_EXPIRED:
427 Global.
getSector().reportEventStage(
this,
"end_expired", messagePriority);
430 market.getMemoryWithoutUpdate().set(
"$foodShortageExpired",
true, 10);
433 if (dialog !=
null && dialog.getPlugin().getMemoryMap() !=
null && dialog.getTextPanel() !=
null) {
436 if (elapsedDays < 7) {
437 ongoingStabilityImpact = 0;
438 Global.getSector().reportEventStage(
this,
"end_player_fast",
null, MessagePriority.DELIVER_IMMEDIATELY,
439 new BaseOnMessageDeliveryScript() {
440 public void beforeDelivery(CommMessageAPI message) {
441 Global.getSector().adjustPlayerReputation(
442 new RepActionEnvelope(RepActions.FOOD_SHORTAGE_PLAYER_ENDED_FAST,
market, message,
443 dialog.getTextPanel(),
true),
444 market.getFaction().getId());
448 market.getMemoryWithoutUpdate().set(
"$foodShortageEndedByPlayerFast",
true, 10);
455 Global.getSector().reportEventStage(
this,
"end_player",
null, MessagePriority.DELIVER_IMMEDIATELY,
456 new BaseOnMessageDeliveryScript() {
457 public void beforeDelivery(CommMessageAPI message) {
458 Global.getSector().adjustPlayerReputation(
459 new RepActionEnvelope(RepActions.FOOD_SHORTAGE_PLAYER_ENDED_NORMAL,
market, message,
460 dialog.getTextPanel(),
true),
461 market.getFaction().getId());
465 market.getMemoryWithoutUpdate().set(
"$foodShortageEndedByPlayer",
true, 10);
467 if (dialog !=
null && dialog.getPlugin().getMemoryMap() !=
null) {
468 dialog.getVisualPanel().hideCore();
469 FireBest.fire(
null, dialog, dialog.getPlugin().getMemoryMap(),
"FoodShortageEndedByPlayerSale");
472 case PLAYER_ENDED_BLACK:
473 if (dialog !=
null && dialog.getPlugin().getMemoryMap() !=
null && dialog.getTextPanel() !=
null) {
474 dialog.getTextPanel().addParagraph(
"Financial transaction confirmed", Global.getSettings().getColor(
"buttonText"));
477 Global.getSector().reportEventStage(
this,
"end_player_black",
null, MessagePriority.DELIVER_IMMEDIATELY,
478 new BaseOnMessageDeliveryScript() {
479 public void beforeDelivery(CommMessageAPI message) {
487 market.getMemoryWithoutUpdate().set(
"$foodShortageEndedByPlayerBlack",
true, 10);
488 if (dialog !=
null && dialog.getPlugin().getMemoryMap() !=
null) {
489 dialog.getVisualPanel().hideCore();
490 FireBest.fire(
null, dialog, dialog.getPlugin().getMemoryMap(),
"FoodShortageEndedByPlayerSale");
494 Global.getSector().reportEventStage(
this,
"end_relief_arrived",
null, messagePriority);
497 market.getMemoryWithoutUpdate().set(
"$foodShortageEndedByNPC",
true, 10);
501 for (SubmarketAPI sub :
market.getSubmarketsCopy()) {
502 if (sub.getPlugin().isFreeTransfer())
continue;
504 float food = sub.getCargo().getQuantity(CargoItemType.RESOURCES, Commodities.FOOD);
505 sub.getCargo().removeItems(CargoItemType.RESOURCES, Commodities.FOOD, food);
518 return messagePriority;
522 return messagePriority;
556 if (
market ==
null)
return;
557 if (transaction.getMarket() !=
market)
return;
564 SubmarketPlugin plugin = transaction.getSubmarket().getPlugin();
565 if (!plugin.isParticipatesInEconomy())
return;
567 float bought = transaction.getQuantityBought(Commodities.FOOD);
568 float sold = transaction.getQuantitySold(Commodities.FOOD);
569 float netBought = bought - sold;
571 if (netBought == 0)
return;
577 CommodityOnMarketAPI com =
market.getCommodityData(Commodities.FOOD);
578 float postTransactionLevel = com.getStockpile();
580 if (plugin.isBlackMarket()) {
581 netDeliveredByPlayerBlack -= bought;
582 netDeliveredByPlayerBlack += sold;
584 netDeliveredByPlayerOther -= bought;
585 netDeliveredByPlayerOther += sold;
588 if (postTransactionLevel >= preEventFoodLevel &&
591 endEvent(Ending.PLAYER_ENDED_BLACK);
593 endEvent(Ending.PLAYER_ENDED);
608 if (fleet == reliefFleet) {
609 Global.
getSector().reportEventStage(
this,
"relief_lost", reliefMarket.getPrimaryEntity(), messagePriority);
610 applyReliefFleetDidNotMakeItConsequences();
616 return Math.max(0, baseFoodToMeetShortage - netDeliveredByPlayerBlack - netDeliveredByPlayerOther);
620 Map<String, String> map = super.getTokenReplacements();
628 map.put(
"$targetFaction",
eventTarget.getEntity().getFaction().getDisplayName());
630 if (reliefMarket !=
null) {
631 SectorEntityToken primary = reliefMarket.getPrimaryEntity();
632 LocationAPI loc = primary.getContainingLocation();
633 if (loc instanceof StarSystemAPI) {
635 map.put(
"$reliefSystem", ((StarSystemAPI)loc).getBaseName());
637 map.put(
"$reliefSystem",
"hyperspace");
640 map.put(
"$reliefMarket", primary.getName());
643 if (reliefFleet !=
null) {
644 float dist = Misc.getDistance(primary.getLocationInHyperspace(),
market.getPrimaryEntity().getLocationInHyperspace());
645 float eta = dist / reliefFleet.getFleetData().getTravelSpeed();
652 s =
"within a few days";
653 }
else if (eta <= 7) {
655 }
else if (eta <= 14) {
656 s =
"within a couple of weeks";
657 }
else if (eta <= 30) {
658 s =
"within a month";
660 s =
"in the coming months";
667 map.put(
"$neededFood",
"" + needed);
679 List<String> result =
new ArrayList<String>();
680 if (
"start".equals(stageId)) {
683 if (
"relief_unavailable".equals(stageId)) {
686 if (
"relief_aborted".equals(stageId)) {
689 if (
"relief_lost".equals(stageId)) {
692 if (
"end_relief_arrived".equals(stageId)) {
695 if (
"end_expired".equals(stageId)) {
698 if (
"end_player".equals(stageId)) {
701 if (
"end_player_black".equals(stageId)) {
704 if (
"end_player_fast".equals(stageId)) {
706 return result.toArray(
new String[0]);
711 return super.getHighlightColors(stageId);
725 List<String> commodities =
new ArrayList<String>();
726 commodities.add(Commodities.FOOD);
732 List<PriceUpdatePlugin> updates =
new ArrayList<PriceUpdatePlugin>();
739 return "Possible food shortage - " +
market.getName() +
"";
742 return "Food shortage - " +
market.getName() +
" (over)";
744 return "Food shortage - " +
market.getName() +
"";