38 private IntervalUtil remoteTracker;
39 private IntervalUtil localTracker;
40 private TimeoutTracker<String> sinceLastLocalReport =
new TimeoutTracker<String>();
41 private TimeoutTracker<String> sinceLastRemoteReport =
new TimeoutTracker<String>();
43 private List<PriceUpdatePlugin> updatesForNextReport =
new ArrayList<PriceUpdatePlugin>();
44 private SectorEntityToken commRelayForNextReport =
null;
48 remoteTracker =
new IntervalUtil(0.5f, 1.5f);
49 localTracker =
new IntervalUtil(0.5f, 1.5f);
64 sinceLastLocalReport.advance(days);
65 sinceLastRemoteReport.advance(days);
67 localTracker.advance(days);
68 if (localTracker.intervalElapsed()) {
72 remoteTracker.advance(days);
73 if (remoteTracker.intervalElapsed()) {
80 private SaveableIterator<StarSystemAPI> starSystemIter =
null;
82 private CampaignEventTarget tempTarget;
84 private void checkRemotePrices() {
86 if (starSystemIter ==
null || !starSystemIter.hasNext()) {
92 List<StarSystemAPI> systems =
new ArrayList<StarSystemAPI>(
Global.
getSector().getStarSystems());
93 Collections.shuffle(systems);
94 starSystemIter =
new SaveableIterator<StarSystemAPI>(systems);
96 float size = systems.size();
97 float interval = 1.5f *
TIMEOUT / size;
98 remoteTracker.setInterval(interval * 0.75f, interval * 1.25f);
100 if(!starSystemIter.hasNext())
return;
103 final StarSystemAPI system = starSystemIter.next();
107 List<SectorEntityToken> relays = system.getEntitiesWithTag(Tags.COMM_RELAY);
108 List<SectorEntityToken> withIntel =
Global.
getSector().getIntel().getCommSnifferLocations();
110 SectorEntityToken relay =
null;
111 for (SectorEntityToken curr : relays) {
112 if (withIntel.contains(curr)) {
118 boolean hasIntel = relay !=
null;
119 if (relay ==
null && relays.size() > 0) {
120 relay = relays.get(
new Random().nextInt(relays.size()));
123 if (relay ==
null)
return;
125 String
id = relay.getContainingLocation().getId();
126 if (sinceLastRemoteReport.contains(
id))
return;
129 List<PriceUpdate> list = getPriceUpdatesFor(system);
130 if (!list.isEmpty()) {
132 commRelayForNextReport = relay;
134 pickUpdatesFrom(system, list, PickMode.REMOTE_WITH_INTEL);
135 if (!updatesForNextReport.isEmpty()) {
136 sinceLastRemoteReport.set(
id,
TIMEOUT);
146 pickUpdatesFrom(system, list, PickMode.REMOTE);
147 if (!updatesForNextReport.isEmpty()) {
148 sinceLastRemoteReport.set(
id,
TIMEOUT);
162 private void checkLocalPrices() {
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;
170 List<PriceUpdate> list = getPriceUpdatesFor(playerFleet.getContainingLocation());
172 if (!list.isEmpty()) {
173 pickUpdatesFrom(playerFleet.getContainingLocation(), list, PickMode.LOCAL);
174 if (!updatesForNextReport.isEmpty()) {
175 sinceLastLocalReport.set(
id,
TIMEOUT);
181 private static enum PickMode {
187 private void pickUpdatesFrom(LocationAPI system, List<PriceUpdate> updates, PickMode mode) {
189 float numMarkets = 0;
190 for (MarketAPI
market : Global.getSector().getEconomy().getMarketsCopy()) {
191 if (
market.getContainingLocation() != system)
continue;
200 max = (int) Math.max(2, numMarkets) + 1;
203 max = (int) Math.max(1, numMarkets - 2);
205 case REMOTE_WITH_INTEL:
206 max = (int) Math.max(1, numMarkets);
210 if (max > 5) max = 5;
211 if (max < 1) max = 1;
216 log.info(
"Picking " + max +
" updates");
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;
224 log.info(pu.getCommodity().getCommodity().getName() +
"(" + pu.getCommodity().getMarket().getName() +
"): weight " + weight);
225 picker.add(pu, weight);
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);
241 private void pickAllRelevantFromMarket(MarketAPI
market, List<PriceUpdate> updates) {
242 log.info(
"Picking market updates");
244 updatesForNextReport.clear();
245 List<PriceUpdatePlugin> known = getPlayerKnownUpdates();
246 for (PriceUpdate update : updates) {
252 if (shouldUpdateLocally(update, known)) {
253 log.info(
"Adding " + update.getCommodity().getCommodity().getName() +
"(" + update.getCommodity().getMarket().getName() +
")");
254 updatesForNextReport.add(update);
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));
269 private List<PriceUpdate> getUpdatesFor(MarketAPI
market) {
270 List<PriceUpdate> updates =
new ArrayList<PriceUpdate>();
271 if (!
market.isInEconomy()) {
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()) {
287 private float getWeightFor(PriceUpdatePlugin update, List<PriceUpdatePlugin> known) {
288 CommodityOnMarketAPI com = update.getCommodity();
289 MarketAPI
market = update.getMarket();
298 float volumeFactor = com.getStockpile() + com.getDemand().getDemandValue();
299 if (volumeFactor == 0)
return 0f;
301 volumeFactor = (float) Math.sqrt(volumeFactor);
307 if (update.getType() == PriceType.NORMAL) {
309 volumeFactor *= 0.25f;
313 float numExpensive = 0;
317 CampaignClockAPI clock = Global.getSector().getClock();
318 float daysSinceLastSame = Float.MAX_VALUE;
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)) {
330 if (!currCom.getId().equals(com.getId()))
continue;
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());
339 switch (curr.getType()) {
352 if (daysSinceLastSame < 30)
return 0f;
354 if (update.getType() == PriceType.NORMAL) {
355 if (numExpensive + numCheap == 0) {
358 if (numCheap == 0 && update.getAvailable() <= 10) {
361 if (numExpensive == 0 && update.getDemand() <= 10) {
366 float total = numCheap + numExpensive + numNormal;
368 float weightMult = 1f;
369 if (total <= 0) total = 1f;
370 switch (update.getType()) {
372 weightMult = 1f + 3f * Math.max(0, numNormal + numExpensive - numCheap) / total;
375 weightMult = 1f + 3f * Math.max(0, numNormal + numCheap - numExpensive) / total;
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);
385 weightMult = 1f + 1f * Math.max(0, numCheap + numExpensive - numNormal) / total;
389 return volumeFactor * weightMult;
394 private boolean shouldUpdateLocally(PriceUpdate update, List<PriceUpdatePlugin> known) {
398 if (!update.isSignificant())
return false;
400 CommodityOnMarketAPI com = update.getCommodity();
401 MarketAPI
market = com.getMarket();
402 StarSystemAPI system =
market.getStarSystem();
409 CampaignClockAPI clock = Global.getSector().getClock();
410 float daysSinceLastSame = Float.MAX_VALUE;
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)) {
423 if (!currCom.getId().equals(com.getId()))
continue;
425 if (currCom == com) {
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());
450 if (daysSinceLastSame < 5)
return false;
454 if (update.getType() != PriceType.NORMAL) {
459 if (mostRecent !=
null) {
495 private List<PriceUpdatePlugin> getPlayerKnownUpdates() {
497 CampaignClockAPI clock = Global.getSector().getClock();
499 List<PriceUpdatePlugin> updates =
new ArrayList<PriceUpdatePlugin>();
550 List<PriceUpdate> list = getUpdatesFor(
market);
551 tempTarget =
new CampaignEventTarget(
market);
553 if (!list.isEmpty()) {
554 pickAllRelevantFromMarket(
market, list);
555 if (!updatesForNextReport.isEmpty()) {
567 return updatesForNextReport;
572 return super.getRelatedCommodities();
577 Map<String, String> map = super.getTokenReplacements();
585 if (commRelayForNextReport !=
null) {
586 map.put(
"$relayName", commRelayForNextReport.getName());
587 if (commRelayForNextReport.isInHyperspace()) {
588 map.put(
"$fromSystem",
"hyperspace");
590 map.put(
"$fromSystem", ((StarSystemAPI)commRelayForNextReport.getContainingLocation()).getBaseName());
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() +
"), ";
601 priceList = priceList.substring(0, priceList.length() - 2);
603 map.put(
"$priceList", priceList);
618 return super.getHighlightColors(stageId);
623 if (tempTarget !=
null)
return tempTarget;
624 return super.getEventTarget();
634 return "Trade info update";
639 return CampaignEventCategory.DO_NOT_SHOW_IN_MESSAGE_FILTER;