Starsector API
Loading...
Searching...
No Matches
TradeInfoUpdateEvent.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.events;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.Collections;
6import java.util.HashSet;
7import java.util.List;
8import java.util.Map;
9import java.util.Random;
10import java.util.Set;
11
12import org.apache.log4j.Logger;
13
14import com.fs.starfarer.api.Global;
15import com.fs.starfarer.api.campaign.CampaignClockAPI;
16import com.fs.starfarer.api.campaign.CampaignFleetAPI;
17import com.fs.starfarer.api.campaign.LocationAPI;
18import com.fs.starfarer.api.campaign.SectorEntityToken;
19import com.fs.starfarer.api.campaign.StarSystemAPI;
20import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
21import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
22import com.fs.starfarer.api.campaign.econ.MarketAPI;
23import com.fs.starfarer.api.campaign.events.CampaignEventTarget;
24import com.fs.starfarer.api.campaign.events.CampaignEventPlugin.PriceUpdatePlugin.PriceType;
25import com.fs.starfarer.api.impl.campaign.ids.Commodities;
26import com.fs.starfarer.api.impl.campaign.ids.Tags;
27import com.fs.starfarer.api.util.IntervalUtil;
28import com.fs.starfarer.api.util.SaveableIterator;
29import com.fs.starfarer.api.util.TimeoutTracker;
30import com.fs.starfarer.api.util.WeightedRandomPicker;
31
33
34 public static final float TIMEOUT = 30f;
35
36 public static Logger log = Global.getLogger(TradeInfoUpdateEvent.class);
37
38 private IntervalUtil remoteTracker;
39 private IntervalUtil localTracker;
40 private TimeoutTracker<String> sinceLastLocalReport = new TimeoutTracker<String>();
41 private TimeoutTracker<String> sinceLastRemoteReport = new TimeoutTracker<String>();
42
43 private List<PriceUpdatePlugin> updatesForNextReport = new ArrayList<PriceUpdatePlugin>();
44 private SectorEntityToken commRelayForNextReport = null;
45
46 public void init(String type, CampaignEventTarget eventTarget) {
47 super.init(type, eventTarget);
48 remoteTracker = new IntervalUtil(0.5f, 1.5f);
49 localTracker = new IntervalUtil(0.5f, 1.5f);
50 }
51
52 public void startEvent() {
53 super.startEvent();
54 }
55
56 public void advance(float amount) {
57 //if (true) return;
58
59 if (!isEventStarted()) return;
60 if (isDone()) return;
61
62 float days = Global.getSector().getClock().convertToDays(amount);
63
64 sinceLastLocalReport.advance(days);
65 sinceLastRemoteReport.advance(days);
66
67 localTracker.advance(days);
68 if (localTracker.intervalElapsed()) {
69 checkLocalPrices();
70 }
71
72 remoteTracker.advance(days);
73 if (remoteTracker.intervalElapsed()) {
74 checkRemotePrices();
75 }
76 }
77
78
79 //private SaveableIterator<SectorEntityToken> relayIter = null;
80 private SaveableIterator<StarSystemAPI> starSystemIter = null;
81
82 private CampaignEventTarget tempTarget;
83
84 private void checkRemotePrices() {
85
86 if (starSystemIter == null || !starSystemIter.hasNext()) {
87// List<SectorEntityToken> relays = Global.getSector().getIntel().getCommSnifferLocations();
88// if (Global.getSettings().isDevMode()) {
89// relays = Global.getSector().getEntitiesWithTag(Tags.COMM_RELAY);
90// }
91// relayIter = new SaveableIterator<SectorEntityToken>(relays);
92 List<StarSystemAPI> systems = new ArrayList<StarSystemAPI>(Global.getSector().getStarSystems());
93 Collections.shuffle(systems);
94 starSystemIter = new SaveableIterator<StarSystemAPI>(systems);
95
96 float size = systems.size();
97 float interval = 1.5f * TIMEOUT / size;
98 remoteTracker.setInterval(interval * 0.75f, interval * 1.25f);
99 }
100 if(!starSystemIter.hasNext()) return;
101
102
103 final StarSystemAPI system = starSystemIter.next();
104 //if (Global.getSector().getCurrentLocation() == system) return;
105
106
107 List<SectorEntityToken> relays = system.getEntitiesWithTag(Tags.COMM_RELAY);
108 List<SectorEntityToken> withIntel = Global.getSector().getIntel().getCommSnifferLocations();
109
110 SectorEntityToken relay = null;
111 for (SectorEntityToken curr : relays) {
112 if (withIntel.contains(curr)) {
113 relay = curr;
114 break;
115 }
116 }
117
118 boolean hasIntel = relay != null;
119 if (relay == null && relays.size() > 0) {
120 relay = relays.get(new Random().nextInt(relays.size()));
121 }
122
123 if (relay == null) return;
124
125 String id = relay.getContainingLocation().getId();
126 if (sinceLastRemoteReport.contains(id)) return;
127
128
129 List<PriceUpdate> list = getPriceUpdatesFor(system);
130 if (!list.isEmpty()) {
131
132 commRelayForNextReport = relay;
133 if (hasIntel) {
134 pickUpdatesFrom(system, list, PickMode.REMOTE_WITH_INTEL);
135 if (!updatesForNextReport.isEmpty()) {
136 sinceLastRemoteReport.set(id, TIMEOUT);
137// Global.getSector().reportEventStage(this, "prices_sniffer", relay, MessagePriority.SECTOR, new BaseOnMessageDeliveryScript() {
138// public void beforeDelivery(CommMessageAPI message) {
139// if (system != Global.getSector().getPlayerFleet().getContainingLocation()) {
140// message.setShowInCampaignList(false);
141// }
142// }
143// });
144 }
145 } else {
146 pickUpdatesFrom(system, list, PickMode.REMOTE);
147 if (!updatesForNextReport.isEmpty()) {
148 sinceLastRemoteReport.set(id, TIMEOUT);
149// Global.getSector().reportEventStage(this, "prices_remote", relay, MessagePriority.SECTOR, new BaseOnMessageDeliveryScript() {
150// public void beforeDelivery(CommMessageAPI message) {
151// if (system != Global.getSector().getPlayerFleet().getContainingLocation()) {
152// message.setShowInCampaignList(false);
153// }
154// }
155// });
156 }
157 }
158 }
159 }
160
161
162 private void checkLocalPrices() {
163 //if (Global.getSector().isInNewGameAdvance()) return;
164
165 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
166 if (playerFleet.isInHyperspace() || playerFleet.getContainingLocation() == null) return;
167 String id = playerFleet.getContainingLocation().getId();
168 if (sinceLastLocalReport.contains(id)) return;
169
170 List<PriceUpdate> list = getPriceUpdatesFor(playerFleet.getContainingLocation());
171
172 if (!list.isEmpty()) {
173 pickUpdatesFrom(playerFleet.getContainingLocation(), list, PickMode.LOCAL);
174 if (!updatesForNextReport.isEmpty()) {
175 sinceLastLocalReport.set(id, TIMEOUT);
176 //Global.getSector().reportEventStage(this, "prices_local", playerFleet, MessagePriority.SECTOR);
177 }
178 }
179 }
180
181 private static enum PickMode {
182 LOCAL,
183 REMOTE,
184 REMOTE_WITH_INTEL,
185 }
186
187 private void pickUpdatesFrom(LocationAPI system, List<PriceUpdate> updates, PickMode mode) {
188
189 float numMarkets = 0;
190 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
191 if (market.getContainingLocation() != system) continue;
192 //numMarkets += market.getSize();
193 numMarkets++;
194
195 }
196 int max = 0;
197 switch (mode) {
198 case LOCAL:
199 //max = (int) Math.max(2, numMarkets - 2);
200 max = (int) Math.max(2, numMarkets) + 1;
201 break;
202 case REMOTE:
203 max = (int) Math.max(1, numMarkets - 2);
204 break;
205 case REMOTE_WITH_INTEL:
206 max = (int) Math.max(1, numMarkets);
207 break;
208 }
209
210 if (max > 5) max = 5;
211 if (max < 1) max = 1;
212
213
214 log.info("");
215 log.info("");
216 log.info("Picking " + max + " updates");
217
218 WeightedRandomPicker<PriceUpdate> picker = new WeightedRandomPicker<PriceUpdate>();
219 List<PriceUpdatePlugin> known = getPlayerKnownUpdates();
220 for (PriceUpdate pu : updates) {
221 float weight = getWeightFor(pu, known);
222 if (weight <= 0) continue;
223
224 log.info(pu.getCommodity().getCommodity().getName() + "(" + pu.getCommodity().getMarket().getName() + "): weight " + weight);
225 picker.add(pu, weight);
226 }
227
228 log.info("");
229 updatesForNextReport.clear();
230 for (int i = 0; i < max; i++) {
231 PriceUpdate update = picker.pick();
232 if (update != null) {
233 log.info("Picked " + update.getCommodity().getCommodity().getName() + "(" + update.getCommodity().getMarket().getName() + ")");
234 updatesForNextReport.add(update);
235 picker.remove(update);
236 }
237 }
238 }
239
240
241 private void pickAllRelevantFromMarket(MarketAPI market, List<PriceUpdate> updates) {
242 log.info("Picking market updates");
243
244 updatesForNextReport.clear();
245 List<PriceUpdatePlugin> known = getPlayerKnownUpdates();
246 for (PriceUpdate update : updates) {
247// System.out.println("Checking for local update: " + update.getCommodity().getCommodity().getName());
248// if (!update.isSignificant()) continue;
249// float weight = getWeightFor(update, known);
250// log.info(update.getCommodity().getCommodity().getName() + ": weight " + (int) weight);
251 //if (update.getType() != PriceType.NORMAL || weight > 100) {
252 if (shouldUpdateLocally(update, known)) {
253 log.info("Adding " + update.getCommodity().getCommodity().getName() + "(" + update.getCommodity().getMarket().getName() + ")");
254 updatesForNextReport.add(update);
255 }
256 }
257 }
258
259
260 private List<PriceUpdate> getPriceUpdatesFor(LocationAPI system) {
261 List<PriceUpdate> updates = new ArrayList<PriceUpdate>();
262 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
263 if (market.getContainingLocation() != system) continue;
264 updates.addAll(getUpdatesFor(market));
265 }
266 return updates;
267 }
268
269 private List<PriceUpdate> getUpdatesFor(MarketAPI market) {
270 List<PriceUpdate> updates = new ArrayList<PriceUpdate>();
271 if (!market.isInEconomy()) {
272 return updates;
273 }
274 for (CommodityOnMarketAPI com : market.getAllCommodities()) {
275 if (com.isNonEcon()) continue;
276 if (com.isPersonnel()) continue;
277 float volumeFactor = com.getStockpile() + com.getDemand().getDemandValue();
278 if (volumeFactor < 50) continue;
279 PriceUpdate update = new PriceUpdate(com);
280 if (update.isSignificant()) {
281 updates.add(update);
282 }
283 }
284 return updates;
285 }
286
287 private float getWeightFor(PriceUpdatePlugin update, List<PriceUpdatePlugin> known) {
288 CommodityOnMarketAPI com = update.getCommodity();
289 MarketAPI market = update.getMarket();
290
291// if (market.getId().contains("achaman") && com.getId().equals("food")) {
292// System.out.println("dfsdfefw2");
293// }
294// if (com.getId().equals("food")) {
295// System.out.println("dfsdfefw2");
296// }
297
298 float volumeFactor = com.getStockpile() + com.getDemand().getDemandValue();
299 if (volumeFactor == 0) return 0f;
300
301 volumeFactor = (float) Math.sqrt(volumeFactor);
302
303 //volumeFactor *= com.getCommodity().getBasePrice();
304
305 //volumeFactor *= (float) market.getSize();
306
307 if (update.getType() == PriceType.NORMAL) {
308 //volumeFactor = (float) Math.sqrt(volumeFactor);
309 volumeFactor *= 0.25f;
310 }
311
312 float numCheap = 0;
313 float numExpensive = 0;
314 float numNormal = 0;
315
316
317 CampaignClockAPI clock = Global.getSector().getClock();
318 float daysSinceLastSame = Float.MAX_VALUE;
319
320 int numSeenSkipped = 0;
321 Set<CommodityOnMarketAPI> seen = new HashSet<CommodityOnMarketAPI>();
322 for (PriceUpdatePlugin curr : known) {
323 CommodityOnMarketAPI currCom = curr.getCommodity();
324 if (currCom == null) continue;
325 if (seen.contains(currCom)) {
326 numSeenSkipped++;
327 continue;
328 }
329 seen.add(currCom);
330 if (!currCom.getId().equals(com.getId())) continue;
331
332 if (currCom == com) {
333 float priceDiff = Math.abs(curr.getDemandPrice() + curr.getSupplyPrice() - update.getDemandPrice() - update.getSupplyPrice());
334 if (priceDiff < 0.2f * (curr.getDemandPrice() + curr.getSupplyPrice())) {
335 daysSinceLastSame = clock.getElapsedDaysSince(curr.getTimestamp());
336 }
337 }
338 //clock.getElapsedDaysSince(-55661070348000L)
339 switch (curr.getType()) {
340 case CHEAP:
341 numCheap++;
342 break;
343 case EXPENSIVE:
344 numExpensive++;
345 break;
346 case NORMAL:
347 numNormal++;
348 break;
349 }
350 }
351
352 if (daysSinceLastSame < 30) return 0f;
353
354 if (update.getType() == PriceType.NORMAL) {
355 if (numExpensive + numCheap == 0) {
356 return 0f;
357 }
358 if (numCheap == 0 && update.getAvailable() <= 10) {
359 return 0f;
360 }
361 if (numExpensive == 0 && update.getDemand() <= 10) {
362 return 0f;
363 }
364 }
365
366 float total = numCheap + numExpensive + numNormal;
367
368 float weightMult = 1f;
369 if (total <= 0) total = 1f;
370 switch (update.getType()) {
371 case CHEAP:
372 weightMult = 1f + 3f * Math.max(0, numNormal + numExpensive - numCheap) / total;
373 break;
374 case EXPENSIVE:
375 weightMult = 1f + 3f * Math.max(0, numNormal + numCheap - numExpensive) / total;
376
377 if (!com.isFuel() && !com.isPersonnel() && !com.getId().equals(Commodities.SUPPLIES)) {
378 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
379 float f = playerFleet.getCargo().getQuantity(CargoItemType.RESOURCES, com.getId()) / Math.max(playerFleet.getCargo().getMaxCapacity(), 1);
380 weightMult *= (1f + 2f * f);
381 }
382
383 break;
384 case NORMAL:
385 weightMult = 1f + 1f * Math.max(0, numCheap + numExpensive - numNormal) / total;
386 break;
387 }
388
389 return volumeFactor * weightMult;
390 }
391
392
393
394 private boolean shouldUpdateLocally(PriceUpdate update, List<PriceUpdatePlugin> known) {
395// if (update.getCommodity().getId().equals(Commodities.LOBSTER)) {
396// System.out.println("wfwefweew");
397// }
398 if (!update.isSignificant()) return false;
399
400 CommodityOnMarketAPI com = update.getCommodity();
401 MarketAPI market = com.getMarket();
402 StarSystemAPI system = market.getStarSystem();
403
404// float numCheap = 0;
405// float numExpensive = 0;
406// float numNormal = 0;
407// float numLocalNormal = 0;
408
409 CampaignClockAPI clock = Global.getSector().getClock();
410 float daysSinceLastSame = Float.MAX_VALUE;
411
412 int numSeenSkipped = 0;
413 Set<CommodityOnMarketAPI> seen = new HashSet<CommodityOnMarketAPI>();
414 PriceUpdatePlugin mostRecent = null;
415 for (PriceUpdatePlugin curr : known) {
416 CommodityOnMarketAPI currCom = curr.getCommodity();
417 if (currCom == null) continue;
418 if (seen.contains(currCom)) {
419 numSeenSkipped++;
420 continue;
421 }
422 seen.add(currCom);
423 if (!currCom.getId().equals(com.getId())) continue;
424
425 if (currCom == com) {
426 mostRecent = curr;
427 float priceDiff = Math.abs(curr.getDemandPrice() + curr.getSupplyPrice() - update.getDemandPrice() - update.getSupplyPrice());
428 if (priceDiff < 0.2f * (curr.getDemandPrice() + curr.getSupplyPrice())) {
429 daysSinceLastSame = clock.getElapsedDaysSince(curr.getTimestamp());
430 }
431 break;
432 }
433 //clock.getElapsedDaysSince(-55661070348000L)
434// switch (curr.getType()) {
435// case CHEAP:
436// numCheap++;
437// break;
438// case EXPENSIVE:
439// numExpensive++;
440// break;
441// case NORMAL:
442// numNormal++;
443// if (system != null && system == curr.getMarket().getStarSystem()) {
444// numLocalNormal++;
445// }
446// break;
447// }
448 }
449
450 if (daysSinceLastSame < 5) return false;
451
452 //boolean canSell = (int) Misc.getRounded(update.getAvailable()) >= 5;
453
454 if (update.getType() != PriceType.NORMAL) {
455 return true;
456 }
457
458 //if (mostRecent != null && mostRecent.getType() != PriceType.NORMAL && update.getType() == PriceType.NORMAL) {
459 if (mostRecent != null) {
460// if (mostRecent.getType() != PriceType.NORMAL && update.getType() == PriceType.NORMAL) {
461// update.get
462// }
463 return true;
464 }
465
466 return false;
467
468// CommodityStatTracker stats = SharedData.getData().getActivityTracker().getCommodityTracker();
469// CommodityStats cs = stats.getStats(update.getCommodity().getId());
470//
471// float numMarkets = Global.getSector().getEconomy().getMarketsCopy().size();
472// if (numMarkets < 1) return false; // ??? no markets
473//
474// if (com.getAverageStockpileAfterDemand() > cs.getTotalStockpiles() * 4f / numMarkets) {
475// return true;
476// }
477// if (com.getDemand().getDemandValue() > cs.getTotalDemand() * 1f / numMarkets) {
478// return true;
479// }
480
481// if (update.getType() == PriceType.NORMAL) {
482// if (numExpensive + numCheap == 0) return false;
483// if (numExpensive > 0 && (numCheap > 0 || numLocalNormal > 0)) return false;
484// }
485//
486// return true;
487 }
488
489
495 private List<PriceUpdatePlugin> getPlayerKnownUpdates() {
496
497 CampaignClockAPI clock = Global.getSector().getClock();
498
499 List<PriceUpdatePlugin> updates = new ArrayList<PriceUpdatePlugin>();
500// for (CommMessageAPI message : Global.getSector().getIntel().getMessagesCopy()) {
501// if (clock.getElapsedDaysSince(message.getTimeSent()) > 30) continue;
502// if (!message.hasTag(Tags.REPORT_PRICES)) continue;
503//
504// List<PriceUpdatePlugin> list = message.getPriceUpdates();
505// if (list == null || list.isEmpty()) continue;
506//
510// for (PriceUpdatePlugin curr : list) {
511// updates.add(curr);
512// }
513// }
514//
515// Collections.sort(updates, new Comparator<PriceUpdatePlugin>() {
516// public int compare(PriceUpdatePlugin o1, PriceUpdatePlugin o2) {
517// long result = (o2.getTimestamp() - o1.getTimestamp());
518// if (result > 0)
519// return 1;
520// if (result < 0)
521// return -1;
522// return 0;
523// }
524// });
525
526
527 return updates;
528 }
529
530
531 @Override
532 public void reportPlayerOpenedMarket(MarketAPI market) {
534 }
535
536 @Override
537 public void reportPlayerClosedMarket(MarketAPI market) {
538 //market.removeCondition(Conditions.EVENT_TRADE_DISRUPTION);
539 //market.getCommodityData(Commodities.HEAVY_MACHINERY).getPlayerPriceMod().modifyMult("sdfsdfsd", 0.1f);
541 }
542
543 protected void getLocalUpdates(MarketAPI market) {
544// float days = SharedData.getData().getPlayerActivityTracker().getDaysSinceLastVisitTo(market);
545// if (days < 3) return;
546
547 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
548// if (playerFleet.isInHyperspace()) return;
549
550 List<PriceUpdate> list = getUpdatesFor(market);
551 tempTarget = new CampaignEventTarget(market);
552 this.market = market;
553 if (!list.isEmpty()) {
554 pickAllRelevantFromMarket(market, list);
555 if (!updatesForNextReport.isEmpty()) {
556 //Global.getSector().reportEventStage(this, "prices_market", playerFleet, MessagePriority.DELIVER_IMMEDIATELY);
557 }
558 }
559 tempTarget = null;
560 this.market = null;
561 }
562
563
564
565 @Override
566 public List<PriceUpdatePlugin> getPriceUpdates() {
567 return updatesForNextReport;
568 }
569
570 @Override
571 public List<String> getRelatedCommodities() {
572 return super.getRelatedCommodities();
573 }
574
575
576 public Map<String, String> getTokenReplacements() {
577 Map<String, String> map = super.getTokenReplacements();
578// CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
579// if (playerFleet.isInHyperspace()) {
580// map.put("$fromSystem", "hyperspace");
581// } else {
582// map.put("$fromSystem", ((StarSystemAPI)playerFleet.getContainingLocation()).getBaseName());
583// }
584
585 if (commRelayForNextReport != null) {
586 map.put("$relayName", commRelayForNextReport.getName());
587 if (commRelayForNextReport.isInHyperspace()) {
588 map.put("$fromSystem", "hyperspace");
589 } else {
590 map.put("$fromSystem", ((StarSystemAPI)commRelayForNextReport.getContainingLocation()).getBaseName());
591 }
592 }
593
594 List<PriceUpdatePlugin> updates = getPriceUpdates();
595 if (updates != null && !updates.isEmpty()) {
596 String priceList = "Price information updated for: ";
597 for (PriceUpdatePlugin update : updates) {
598 CommodityOnMarketAPI com = update.getCommodity();
599 priceList += com.getCommodity().getName() + " (" + com.getMarket().getName() + "), ";
600 }
601 priceList = priceList.substring(0, priceList.length() - 2);
602 priceList += ".";
603 map.put("$priceList", priceList);
604 }
605 return map;
606 }
607
608 @Override
609 public String[] getHighlights(String stageId) {
610// List<String> result = new ArrayList<String>();
611// addTokensToList(result, "$neededFood");
612// return result.toArray(new String[0]);
613 return null;
614 }
615
616 @Override
617 public Color[] getHighlightColors(String stageId) {
618 return super.getHighlightColors(stageId);
619 }
620
621 @Override
622 public CampaignEventTarget getEventTarget() {
623 if (tempTarget != null) return tempTarget;
624 return super.getEventTarget();
625 }
626
627 public boolean isDone() {
628 return false;
629 }
630
631
632 @Override
633 public String getEventName() {
634 return "Trade info update"; // not used anywhere
635 }
636
637 @Override
638 public CampaignEventCategory getEventCategory() {
639 return CampaignEventCategory.DO_NOT_SHOW_IN_MESSAGE_FILTER;
640 }
641
642 public boolean showAllMessagesIfOngoing() {
643 return false;
644 }
645}
646
647
648
649
650
651
652
653
654
655
static Logger getLogger(Class c)
Definition Global.java:26
static SectorAPI getSector()
Definition Global.java:59
void init(String type, CampaignEventTarget eventTarget)