001package com.fs.starfarer.api.impl.campaign.shared; 002 003import java.util.HashMap; 004import java.util.List; 005import java.util.Map; 006 007import com.fs.starfarer.api.Global; 008import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 009import com.fs.starfarer.api.campaign.econ.EconomyAPI; 010import com.fs.starfarer.api.campaign.econ.MarketAPI; 011import com.fs.starfarer.api.util.IntervalUtil; 012import com.fs.starfarer.api.util.Misc; 013import com.fs.starfarer.api.util.SaveableIterator; 014 015public class CommodityStatTracker { 016 017 public static class CommodityStats { 018 private float minSupplyPrice; 019 private float maxDemandPrice; 020 private float averageSupplyPrice; 021 private float averageDemandPrice; 022 private float totalDemand; 023 private float totalSupply; 024 private float totalStockpiles; 025 private final String commodityId; 026 027 public CommodityStats(String commodityId) { 028 this.commodityId = commodityId; 029 } 030 031// public MarketAPI getMarket() { 032// return Global.getSector().getEconomy().getMarket(marketId); 033// } 034// public CommodityOnMarketAPI getCommodity() { 035// MarketAPI market = getMarket(); 036// if (market == null) return null; 037// return market.getCommodityData(commodityId); 038// } 039 040 public float getMinSupplyPrice() { 041 return minSupplyPrice; 042 } 043 public float getMaxDemandPrice() { 044 return maxDemandPrice; 045 } 046 public float getAverageSupplyPrice() { 047 return averageSupplyPrice; 048 } 049 public float getAverageDemandPrice() { 050 return averageDemandPrice; 051 } 052 public String getCommodityId() { 053 return commodityId; 054 } 055 public void setMinSupplyPrice(float min) { 056 this.minSupplyPrice = min; 057 } 058 public void setMaxDemandPrice(float max) { 059 this.maxDemandPrice = max; 060 } 061 public void setAverageSupplyPrice(float average) { 062 this.averageSupplyPrice = average; 063 } 064 public float getTotalDemand() { 065 return totalDemand; 066 } 067 public void setTotalDemand(float totalDemand) { 068 this.totalDemand = totalDemand; 069 } 070 public float getTotalSupply() { 071 return totalSupply; 072 } 073 public void setTotalSupply(float totalSupply) { 074 this.totalSupply = totalSupply; 075 } 076 public float getTotalStockpiles() { 077 return totalStockpiles; 078 } 079 public void setTotalStockpiles(float totalStockpiles) { 080 this.totalStockpiles = totalStockpiles; 081 } 082 public void setAverageDemandPrice(float weightedAveragePrice) { 083 this.averageDemandPrice = weightedAveragePrice; 084 } 085 } 086 087 private IntervalUtil timer = new IntervalUtil(0.25f, 0.75f); 088 089 private Map<String, CommodityStats> priceData = new HashMap<String, CommodityStats>(); 090 private boolean firstFrame = true; 091 092 public void advance(float days) { 093 094 if (firstFrame) { 095 firstFrame = false; 096 EconomyAPI economy = Global.getSector().getEconomy(); 097 for (String id : economy.getAllCommodityIds()) { 098 updateCommodityStats(id); 099 } 100 } 101 102 timer.advance(days); 103 if (timer.intervalElapsed()) { 104 updateStatsNextStep(); 105 } 106 107// compute prices stuff here? min/max 108// add an "is price change significant" method; "is price significant" method 109 } 110 111 112 public boolean isSupplyPriceSignificant(CommodityOnMarketAPI com) { 113 return isSupplyPriceWithMultSignificant(com, 1f); 114 } 115 public boolean isDemandPriceSignificant(CommodityOnMarketAPI com) { 116 return isDemandPriceWithMultSignificant(com, 1f); 117 } 118 119 public boolean isSupplyPriceWithMultSignificant(CommodityOnMarketAPI com, float mult) { 120 float supplyPrice = Math.round(com.getMarket().getSupplyPrice(com.getId(), 1, true)); 121 supplyPrice *= mult; 122 return isSupplyPriceSignificant(com, supplyPrice); 123 } 124 125 public boolean isDemandPriceWithMultSignificant(CommodityOnMarketAPI com, float mult) { 126 float demandPrice = Math.round(com.getMarket().getDemandPrice(com.getId(), 1, true)); 127 demandPrice *= mult; 128 return isSupplyPriceSignificant(com, demandPrice); 129 } 130 131 public boolean isSupplyPriceSignificant(CommodityOnMarketAPI com, float price) { 132 //if (com.getAverageStockpileAfterDemand() < 100) return false; 133 CommodityStats stats = getStats(com.getId()); 134 float average = stats.getAverageSupplyPrice(); 135 //return price <= average * .5f || average - price > com.getCommodity().getBasePrice() * 0.5f; 136 //return price <= average * .5f || average - price > 100f; 137 //return price <= average * .5f || average - price > Math.max(100, com.getCommodity().getBasePrice() * 0.5f); 138 139// float margin = Misc.getProfitMargin(); 140// margin = Math.min(margin * 0.5f, average * 0.75f); 141 float flat = Misc.getProfitMarginFlat(); 142 float mult = Misc.getProfitMarginMult(); 143 144 return price <= average - flat || price <= average / mult; 145 146// if (margin < Misc.getProfitMargin() * 0.25f) { 147// margin = Misc.getProfitMargin() * 0.25f; 148// } 149// return price <= average - margin; 150 } 151 152 public boolean isDemandPriceSignificant(CommodityOnMarketAPI com, float price) { 153 //if (com.getDemand().getDemandValue() < 100) return false; 154 CommodityStats stats = getStats(com.getId()); 155 float average = stats.getAverageDemandPrice(); 156 //return price >= average * 1.75f || price - average > com.getCommodity().getBasePrice() * 1f; 157 //return price >= average * 1.75f || price - average > 100f; 158 //return price >= average * 1.75f || price - average > Math.max(100, com.getCommodity().getBasePrice() * 1f); 159 160 float flat = Misc.getProfitMarginFlat(); 161 float mult = Misc.getProfitMarginMult(); 162 163 return price >= average + flat || price >= average * mult; 164 165// float margin = Misc.getProfitMargin(); 166// margin = margin - Math.min(margin * 0.5f, average * 0.5f); 167// 168// return price >= average + margin; 169 } 170 171 172// public boolean isCommodityPriceSpreadInteresting(String commodityId) { 173// CommodityStats stats = getStats(commodityId); 174// 175// float diff = Math.abs(stats.getMaxDemandPrice() - stats.getMinSupplyPrice()); 176// return isCommodityPriceSpreadInteresting(commodityId, diff); 177// } 178// 179// public boolean isCommodityPriceSpreadInteresting(String commodityId, float diff) { 180// CommodityStats stats = getStats(commodityId); 181// if (diff > 100) return true; 182// if (diff > stats.getMinSupplyPrice() * .75f) return true; 183// return false; 184// } 185 186 187 188 private SaveableIterator<String> commodityIterator = null; 189 private void updateStatsNextStep() { 190 EconomyAPI economy = Global.getSector().getEconomy(); 191 192 if (commodityIterator == null || !commodityIterator.hasNext()) { 193 commodityIterator = new SaveableIterator<String>(economy.getAllCommodityIds()); 194 } 195 if (!commodityIterator.hasNext()) return; // no commodities exist, yyeah. 196 197 String commodityId = commodityIterator.next(); 198 199 updateCommodityStats(commodityId); 200 } 201 202 public void updateCommodityStats(String commodityId) { 203 EconomyAPI economy = Global.getSector().getEconomy(); 204 List<MarketAPI> markets = economy.getMarketsCopy(); 205 206 float totalSupply = 0; 207 float totalDemand = 0; 208 float totalStockpiles = 0; 209 float minSupplyPrice = Float.MAX_VALUE; 210 float maxDemandPrice = 0; 211 212 float totalSupplyPrice = 0; 213 float totalDemandPrice = 0; 214 215 for (MarketAPI market : markets) { 216 CommodityOnMarketAPI com = market.getCommodityData(commodityId); 217 218 //float supply = com.getSupplyValue(); 219 float demand = com.getDemand().getDemandValue(); 220 float supply = demand; 221 float stockpile = com.getStockpile(); 222 223 if (supply < 10 && demand < 10 && stockpile < 10) continue; 224 225 float supplyPrice = Math.round(market.getSupplyPrice(com.getId(), 1, true)); 226 float demandPrice = Math.round(market.getDemandPrice(com.getId(), 1, true)); 227 228 if (supplyPrice < minSupplyPrice) { 229 minSupplyPrice = supplyPrice; 230 } 231 if (demandPrice > maxDemandPrice) { 232 maxDemandPrice = demandPrice; 233 } 234 235 totalSupply += supply; 236 totalDemand += demand; 237 totalStockpiles += stockpile; 238 239 totalSupplyPrice += stockpile * supplyPrice; 240 totalDemandPrice += demand * demandPrice; 241 } 242 243 CommodityStats stats = getStats(commodityId); 244 245 stats.setTotalSupply(totalSupply); 246 stats.setTotalDemand(totalDemand); 247 stats.setTotalStockpiles(totalStockpiles); 248 stats.setMaxDemandPrice(maxDemandPrice); 249 stats.setMinSupplyPrice(minSupplyPrice); 250 251 if (totalStockpiles > 0) { 252 stats.setAverageSupplyPrice(totalSupplyPrice / totalStockpiles); 253 } else { 254 stats.setAverageSupplyPrice(0); 255 } 256 257 if (totalDemand > 0) { 258 stats.setAverageDemandPrice(totalDemandPrice / totalDemand); 259 } else { 260 stats.setAverageDemandPrice(0); 261 } 262 } 263 264 265 public CommodityStats getStats(String commodityId) { 266 CommodityStats data = priceData.get(commodityId); 267 if (data == null) { 268 data = new CommodityStats(commodityId); 269 priceData.put(commodityId, data); 270 } 271 return data; 272 } 273} 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297