Starsector API
Loading...
Searching...
No Matches
CommodityProductionMission.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.missions;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.List;
6import java.util.Map;
7import java.util.Set;
8
9import org.lwjgl.util.vector.Vector2f;
10
11import com.fs.starfarer.api.Global;
12import com.fs.starfarer.api.campaign.InteractionDialogAPI;
13import com.fs.starfarer.api.campaign.PersonImportance;
14import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
15import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
16import com.fs.starfarer.api.campaign.econ.MarketAPI;
17import com.fs.starfarer.api.campaign.econ.MonthlyReport;
18import com.fs.starfarer.api.campaign.econ.MonthlyReport.FDNode;
19import com.fs.starfarer.api.campaign.listeners.EconomyTickListener;
20import com.fs.starfarer.api.campaign.rules.MemoryAPI;
21import com.fs.starfarer.api.characters.PersonAPI;
22import com.fs.starfarer.api.impl.campaign.ids.Commodities;
23import com.fs.starfarer.api.impl.campaign.ids.Factions;
24import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
25import com.fs.starfarer.api.impl.campaign.ids.Ranks;
26import com.fs.starfarer.api.impl.campaign.ids.Tags;
27import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithBarEvent;
28import com.fs.starfarer.api.impl.campaign.shared.SharedData;
29import com.fs.starfarer.api.ui.SectorMapAPI;
30import com.fs.starfarer.api.ui.TooltipMakerAPI;
31import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator;
32import com.fs.starfarer.api.util.Misc;
33import com.fs.starfarer.api.util.WeightedRandomPicker;
34
35public class CommodityProductionMission extends HubMissionWithBarEvent implements EconomyTickListener, TooltipCreator {
36
37 public static float PROB_COMPLICATIONS = 0.5f;
38
39 public static float PROB_UNDERWORLD_BAR = 0.25f;
40
41 public static float MISSION_DAYS = 365f * 2f;
42 public static int MISSION_CYCLES = (int) Math.round(MISSION_DAYS / 365f);
43 public static int CONTRACT_DAYS = (int) Math.round(365f * 5f);
44 public static int CONTRACT_MONTHS = (int) Math.round(CONTRACT_DAYS * 12f / 365f);
45 public static int CONTRACT_CYCLES = (int) Math.round(CONTRACT_DAYS * 1f / 365f);
46
47 public static float REWARD_MULT_WHEN_PRODUCING_ALREADY = 0.2f;
49
50 public static enum Stage {
51 WAITING,
52 PAYING,
53 COMPLETED,
54 FAILED,
55 }
56
57 public static enum Variation {
58 PRODUCING_ALREADY,
59 NOT_PRODUCING,
60 }
61
62 public static class CheckPlayerProduction implements ConditionChecker {
63 protected String commodityId;
64 protected int quantity;
65 public CheckPlayerProduction(String commodityId, int quantity) {
66 this.commodityId = commodityId;
67 this.quantity = quantity;
68 }
69 public boolean conditionsMet() {
70 return isPlayerProducing(commodityId, quantity);
71 }
72 }
73
74// public static class ConditionsMet implements TriggerAction {
75//
76// }
77
78 protected Variation variation;
79 protected String commodityId;
80 protected int needed;
81 protected int monthlyPayment;
82 protected int totalPayment;
83
84 protected int monthsRemaining;
85 protected String uid;
86
87 @Override
88 protected boolean create(MarketAPI createdAt, boolean barEvent) {
89 //genRandom = Misc.random;
90 if (barEvent) {
91 setGiverRank(Ranks.CITIZEN);
92 String post = pickOne(Ranks.POST_TRADER, Ranks.POST_COMMODITIES_AGENT, Ranks.POST_PORTMASTER,
93 Ranks.POST_MERCHANT, Ranks.POST_INVESTOR, Ranks.POST_EXECUTIVE,
94 Ranks.POST_SENIOR_EXECUTIVE);
95 setGiverPost(post);
96 if (post.equals(Ranks.POST_SENIOR_EXECUTIVE)) {
97 setGiverImportance(pickHighImportance());
98 } else {
99 setGiverImportance(pickImportance());
100 }
101 if (rollProbability(PROB_UNDERWORLD_BAR)) {
102 setGiverTags(Tags.CONTACT_UNDERWORLD);
103 setGiverFaction(Factions.PIRATES);
104 } else {
105 setGiverTags(Tags.CONTACT_TRADE);
106 }
107 findOrCreateGiver(createdAt, false, false);
108 }
109
110 PersonAPI person = getPerson();
111 if (person == null) return false;
112
113 if (!setPersonMissionRef(person, "$cpm_ref")) {
114 return false;
115 }
116
117 if (barEvent) {
118 setGiverIsPotentialContactOnSuccess();
119 }
120
121 PersonImportance importance = person.getImportance();
122 int minNeeded = 3;
123 int maxNeeded = 9;
124 switch (importance) {
125 case VERY_LOW:
126 minNeeded = 2;
127 maxNeeded = 4;
128 break;
129 case LOW:
130 minNeeded = 3;
131 maxNeeded = 5;
132 break;
133 case MEDIUM:
134 minNeeded = 4;
135 maxNeeded = 6;
136 break;
137 case HIGH:
138 minNeeded = 5;
139 maxNeeded = 7;
140 break;
141 case VERY_HIGH:
142 minNeeded = 6;
143 maxNeeded = 10;
144 break;
145 }
146
147 needed = minNeeded + genRandom.nextInt(maxNeeded - minNeeded + 1);
148
149 MarketAPI market = getPerson().getMarket();
150 if (market == null) return false;
151 if (market.isPlayerOwned()) return false;
152
153 WeightedRandomPicker<String> commoditiesPlayerIsNotProducing = new WeightedRandomPicker<String>(genRandom);
154 WeightedRandomPicker<String> commoditiesPlayerIsProducing = new WeightedRandomPicker<String>(genRandom);
155
156 List<String> all = new ArrayList<String>();
157 for (CommoditySpecAPI spec : Global.getSettings().getAllCommoditySpecs()) {
158 if (spec.isPersonnel()) continue;
159 if (spec.isMeta()) continue;
160 if (spec.isNonEcon()) continue;
161 if (!spec.isPrimary()) continue;
162
163 if (market.getCommodityData(spec.getId()).getMaxDemand() < minNeeded / 2) continue;
164
165 boolean illegal = market.isIllegal(spec.getId());
166 if (illegal && !person.hasTag(Tags.CONTACT_UNDERWORLD)) continue;
167 if (!illegal && !person.hasTag(Tags.CONTACT_TRADE)) continue;
168
169 all.add(spec.getId());
170 }
171
172 for (String cid : all) {
173 commoditiesPlayerIsNotProducing.add(cid);
174 for (MarketAPI curr : Misc.getPlayerMarkets(true)) {
175 CommodityOnMarketAPI com = curr.getCommodityData(cid);
176 if (com.getMaxSupply() > 0) {
177 commoditiesPlayerIsProducing.add(cid, Math.max(1, 10 - com.getMaxSupply()));
178 commoditiesPlayerIsNotProducing.remove(cid);
179 }
180 }
181 }
182
183 commodityId = commoditiesPlayerIsNotProducing.pick();
184 if (commodityId == null) {
185 commodityId = commoditiesPlayerIsProducing.pick();
186 }
187 //commodityId = Commodities.VOLATILES;
188 if (commodityId == null) return false;
189
190 if (commodityId.equals(Commodities.ORGANS)) {
191 needed = Math.min(3, needed);
192 }
193 if (commodityId.equals(Commodities.DRUGS)) {
194 needed = Math.min(6, needed);
195 }
196
197 variation = commoditiesPlayerIsNotProducing.getItems().contains(commodityId) ?
198 Variation.NOT_PRODUCING : Variation.PRODUCING_ALREADY;
199
200
201 //float basePayment = getSpec().getExportValue();
202 float basePayment = 1000 + getSpec().getBasePrice() * 10;
203 if (variation == Variation.NOT_PRODUCING) {
205 } else {
207 }
208
209 monthlyPayment = getRoundNumber(basePayment * needed);
210 totalPayment = (int) Math.round(monthlyPayment * CONTRACT_MONTHS);
211 if (monthlyPayment <= 0) return false;
212
213
214 setStartingStage(Stage.WAITING);
215 setSuccessStage(Stage.COMPLETED);
216 setFailureStage(Stage.FAILED);
217
218 connectWithCustomCondition(Stage.WAITING, Stage.PAYING, new CheckPlayerProduction(commodityId, needed));
219 setTimeLimit(Stage.FAILED, MISSION_DAYS, null, Stage.PAYING);
220
222
223 return true;
224 }
225
226 @Override
227 public void setCurrentStage(Object next, InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) {
228 super.setCurrentStage(next, dialog, memoryMap);
229
230 if (next == Stage.PAYING) {
231 addPotentialContacts(dialog);
232 }
233 }
234
235
236
237 protected void updateInteractionDataImpl() {
238 set("$cpm_barEvent", isBarEvent());
239 set("$cpm_manOrWoman", getPerson().getManOrWoman());
240 set("$cpm_monthlyPayment", Misc.getWithDGS(monthlyPayment));
241 set("$cpm_underworld", getPerson().hasTag(Tags.CONTACT_UNDERWORLD));
242 set("$cpm_totalPayment", Misc.getWithDGS(totalPayment));
243 set("$cpm_missionCycles", MISSION_CYCLES);
244 set("$cpm_contractCycles", CONTRACT_CYCLES);
245 set("$cpm_commodityName", getSpec().getLowerCaseName());
246 set("$cpm_needed", needed);
247 set("$cpm_playerHasColony", !Misc.getPlayerMarkets(false).isEmpty());
248 }
249
250 @Override
251 public void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height) {
252 float opad = 10f;
253 Color h = Misc.getHighlightColor();
254 if (currentStage == Stage.WAITING) {
255 info.addPara("Produce at least %s units of " + getSpec().getLowerCaseName() + " at " +
256 "a colony under your control.", opad, h, "" + needed);
257 info.addPara("Once these terms are met, you will receive %s per month for the next " +
258 "%s cycles, for a total of %s, as long as production is maintained.", opad, h,
259 Misc.getDGSCredits(monthlyPayment),
260 "" + (int)CONTRACT_CYCLES,
261 Misc.getDGSCredits(totalPayment));
262 if (!playerHasAColony()) {
263 info.addPara("You will need to survey a suitable planet and establish a colony to complete " +
264 "this mission.", opad);
265 }
266 } else if (currentStage == Stage.PAYING) {
267 info.addPara("You've met the initial terms of the contract to produce %s units of " +
268 getSpec().getLowerCaseName() + " at " +
269 "a colony under your control.", opad, h, "" + needed);
270 info.addPara("As long these terms are met, you will receive %s per month over %s cycles for " +
271 "a total payout of %s, assuming there is no interruption in production.", opad, h,
272 Misc.getDGSCredits(monthlyPayment),
273 "" + (int)CONTRACT_CYCLES,
274 Misc.getDGSCredits(totalPayment));
275 info.addPara("Months remaining: %s", opad, h, "" + monthsRemaining);
277 info.addPara("You are currently meeting the terms of the contract.",
278 Misc.getPositiveHighlightColor(), opad);
279 } else {
280 info.addPara("You are not currently meeting the terms of the contract.",
281 Misc.getNegativeHighlightColor(), opad);
282 }
283 } else if (currentStage == Stage.COMPLETED) {
284 info.addPara("The contract is completed.", opad);
285 }
286 }
287
288 @Override
289 public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) {
290 Color h = Misc.getHighlightColor();
291 if (currentStage == Stage.WAITING) {
292 info.addPara("Produce at least %s units of " + getSpec().getLowerCaseName() + " at " +
293 "a colony", pad, tc, h, "" + needed);
294 return true;
295 } else if (currentStage == Stage.PAYING) {
296 info.addPara("Receiving %s per month", pad, tc, h, Misc.getDGSCredits(monthlyPayment));
297 info.addPara("Months remaining: %s", 0f, tc, h, "" + monthsRemaining);
299 info.addPara("Terms of contract met", tc, 0f);
300 } else {
301 info.addPara("Terms of contract not met",
302 Misc.getNegativeHighlightColor(), 0f);
303 }
304 return true;
305 }
306 return false;
307 }
308
309 @Override
310 public String getBaseName() {
311 return getSpec().getName() + " Production";
312 }
313
314
315 public static boolean playerHasAColony() {
316 return !Misc.getPlayerMarkets(true).isEmpty();
317 }
318 public static boolean isPlayerProducing(String commodityId, int quantity) {
319 for (MarketAPI market : Misc.getPlayerMarkets(true)) {
320 CommodityOnMarketAPI com = market.getCommodityData(commodityId);
321 if (com.getMaxSupply() >= quantity) return true;
322 }
323 return false;
324 }
325
326 protected transient CommoditySpecAPI spec;
327 protected CommoditySpecAPI getSpec() {
328 if (spec == null) {
330 }
331 return spec;
332 }
333
334
335 @Override
336 protected void notifyEnding() {
337 super.notifyEnding();
338 Global.getSector().getListenerManager().removeListener(this);
339 }
340
341 @Override
342 public void acceptImpl(InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) {
343 super.acceptImpl(dialog, memoryMap);
344
345 Global.getSector().getListenerManager().addListener(this);
346 uid = Misc.genUID();
347 connectWithGlobalFlag(Stage.PAYING, Stage.COMPLETED, getCompletionFlag());
348
349
350 if (rollProbability(PROB_COMPLICATIONS)) {
351 DelayedFleetEncounter e = new DelayedFleetEncounter(genRandom, getMissionId());
352 //e.setDelay(0f);
353 e.setDelay(MISSION_DAYS * 0.5f);
354 e.setLocationInnerSector(true, Factions.PIRATES);
355 //e.setEncounterInHyper();
356 e.beginCreate();
357 e.triggerCreateFleet(FleetSize.VERY_LARGE, FleetQuality.DEFAULT, Factions.PIRATES, FleetTypes.PATROL_LARGE, new Vector2f());
358 e.triggerSetAdjustStrengthBasedOnQuality(true, getQuality());
359 e.triggerSetStandardAggroPirateFlags();
361 e.triggerSetFleetMemoryValue("$cpm_commodityName", getSpec().getLowerCaseName());
362 e.triggerSetFleetGenericHailPermanent("CPMPirateHail");
363 e.endCreate();
364 }
365 }
366
367 public String getCompletionFlag() {
368 return "$" + getMissionId() + "_" + commodityId + "_" + uid + "_completed";
369 }
370
371
372 public void reportEconomyTick(int iterIndex) {
373 if (currentStage != Stage.PAYING) return;
374
375 int numIter = (int) Global.getSettings().getFloat("economyIterPerMonth");
376
377 MonthlyReport report = SharedData.getData().getCurrentReport();
378 FDNode colonyNode = report.getNode(MonthlyReport.OUTPOSTS);
379 FDNode paymentNode = report.getNode(colonyNode, getMissionId() + "_" + commodityId + "_" + uid);
380 paymentNode.income += monthlyPayment / numIter;
381 paymentNode.name = getBaseName();
382 //paymentNode.icon = Global.getSettings().getSpriteName("income_report", "generic_income");
383 paymentNode.icon = getSpec().getIconName();
384 paymentNode.tooltipCreator = this;
385 }
386
387
388 public void reportEconomyMonthEnd() {
390 //monthsRemaining = 0;
391 if (monthsRemaining <= 0) {
392 Global.getSector().getMemoryWithoutUpdate().set(getCompletionFlag(), true);
393 }
394 }
395
396 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
397 tooltip.addSpacer(-10f);
398 addDescriptionForNonEndStage(tooltip, getTooltipWidth(tooltipParam), 1000f);
399 }
400
401 public float getTooltipWidth(Object tooltipParam) {
402 return 450;
403 }
404
405 public boolean isTooltipExpandable(Object tooltipParam) {
406 return false;
407 }
408
409 protected String getMissionTypeNoun() {
410 return "contract";
411 }
412
413 @Override
414 public Set<String> getIntelTags(SectorMapAPI map) {
415 Set<String> tags = super.getIntelTags(map);
416 if (currentStage == Stage.PAYING) {
417 tags.add(Tags.INTEL_AGREEMENTS);
418 tags.remove(Tags.INTEL_ACCEPTED);
419 }
420 return tags;
421 }
422
423
424}
425
static SettingsAPI getSettings()
Definition Global.java:51
static SectorAPI getSector()
Definition Global.java:59
void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam)
void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height)
void acceptImpl(InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap)
void setCurrentStage(Object next, InteractionDialogAPI dialog, Map< String, MemoryAPI > memoryMap)
void triggerCreateFleet(FleetSize size, FleetQuality quality, String factionId, String type, SectorEntityToken roughlyWhere)
void setLocationInnerSector(boolean allowInsidePopulatedSystems, String requireLargestMarketNotHostileToFaction)
CommoditySpecAPI getCommoditySpec(String commodityId)
List< CommoditySpecAPI > getAllCommoditySpecs()