001package com.fs.starfarer.api.impl.campaign.plog; 002 003import java.io.UnsupportedEncodingException; 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Iterator; 007import java.util.LinkedHashMap; 008import java.util.LinkedHashSet; 009import java.util.List; 010import java.util.Map; 011import java.util.zip.DataFormatException; 012import java.util.zip.Deflater; 013import java.util.zip.Inflater; 014 015import com.fs.starfarer.api.Global; 016import com.fs.starfarer.api.campaign.CampaignClockAPI; 017import com.fs.starfarer.api.campaign.CargoAPI; 018import com.fs.starfarer.api.campaign.InteractionDialogAPI; 019import com.fs.starfarer.api.campaign.PlanetAPI; 020import com.fs.starfarer.api.campaign.PlayerMarketTransaction; 021import com.fs.starfarer.api.campaign.PlayerMarketTransaction.ShipSaleInfo; 022import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI; 023import com.fs.starfarer.api.campaign.econ.Industry; 024import com.fs.starfarer.api.campaign.econ.MarketAPI; 025import com.fs.starfarer.api.campaign.listeners.ColonyInteractionListener; 026import com.fs.starfarer.api.campaign.listeners.ColonyPlayerHostileActListener; 027import com.fs.starfarer.api.campaign.listeners.EconomyTickListener; 028import com.fs.starfarer.api.campaign.listeners.PlayerColonizationListener; 029import com.fs.starfarer.api.combat.ShipAPI.HullSize; 030import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD.TempData; 031import com.fs.starfarer.api.impl.campaign.terrain.BaseTiledTerrain; 032import com.fs.starfarer.api.plugins.SurveyPlugin; 033 034public class PlaythroughLog implements EconomyTickListener, 035 ColonyInteractionListener, 036 //EconomyUpdateListener, 037 PlayerColonizationListener, 038 ColonyPlayerHostileActListener { 039 040 public static final String KEY = "$core_playthroughLog"; 041 public static PlaythroughLog getInstance() { 042 Object test = Global.getSector().getMemoryWithoutUpdate().get(KEY); 043 if (test == null) { 044 test = new PlaythroughLog(); 045 Global.getSector().getMemoryWithoutUpdate().set(KEY, test); 046 Global.getSector().getListenerManager().addListener(test); 047 } 048 return (PlaythroughLog) test; 049 } 050 051 public static class PLIntelUIData { 052 public LinkedHashSet<String> selectedGraphs = new LinkedHashSet<String>(); 053 } 054 055 protected List<PLEntry> entries = new ArrayList<PLEntry>(); 056 protected Map<String, PLStat> stats = new LinkedHashMap<String, PLStat>(); 057 protected PLIntelUIData uiData = new PLIntelUIData(); 058 059 transient protected List<PLSnapshot> data = new ArrayList<PLSnapshot>(); 060 protected String saved = ""; 061 062 protected List<SModRecord> smodsInstalled = new ArrayList<SModRecord>(); 063 protected List<OfficerSkillGainRecord> officerSkillsLearned = new ArrayList<OfficerSkillGainRecord>(); 064 065 public PlaythroughLog() { 066 //Global.getSector().getEconomy().addUpdateListener(this); 067 initStats(); 068 } 069 070 071 072// // called from the UI when the player visits a colony etc 073// // take some samples here so that we have a better chance of catching changes in credits/fleet size/etc. 074 public void reportPlayerClosedMarket(MarketAPI market) { 075 reportEconomyTick(-1); 076 } 077 078 public void reportPlayerOpenedMarket(MarketAPI market) { 079 reportEconomyTick(-1); 080 } 081 082 public void reportEconomyTick(int iterIndex) { 083 if (Global.getSector().isInNewGameAdvance() || Global.getSector().getPlayerFleet() == null) return; 084 085// if (data.isEmpty()) { 086// PLSnapshot snapshot = new PLSnapshot(); 087// data.add(snapshot); 088// } 089 090 for (String key : stats.keySet()) { 091 PLStat stat = stats.get(key); 092 stat.accrueValue(); 093 } 094 } 095 096 public void reportEconomyMonthEnd() { 097 if (Global.getSector().isInNewGameAdvance() || Global.getSector().getPlayerFleet() == null) return; 098 099 takeSnapshot(false); 100 } 101 102 protected HullSize biggestBought = HullSize.FIGHTER; 103 public void reportPlayerMarketTransaction(PlayerMarketTransaction transaction) { 104 if (biggestBought == null) biggestBought = HullSize.FIGHTER; 105 for (ShipSaleInfo info : transaction.getShipsBought()) { 106 HullSize size = info.getMember().getHullSpec().getHullSize(); 107 if (size.ordinal() > biggestBought.ordinal()) { 108 biggestBought = size; 109 addEntry("Bought " + info.getMember().getVariant().getHullSpec().getNameWithDesignationWithDashClass()); 110 } 111 } 112 } 113 114 public void reportSaturationBombardmentFinished(InteractionDialogAPI dialog, MarketAPI market, TempData actionData) { 115 addEntry("Saturation-bombarded " + market.getName() + " (" + 116 "size " + (market.getSize() + 1) + " " + market.getFaction().getEntityNamePrefix() + " colony)"); 117 } 118 119 public void reportPlayerAbandonedColony(MarketAPI colony) { 120 String extra = ""; 121 if (colony.getPlanetEntity() != null) { 122 SurveyPlugin plugin = (SurveyPlugin) Global.getSettings().getNewPluginInstance("surveyPlugin"); 123 String cid = plugin.getSurveyDataType(colony.getPlanetEntity()); 124 CommoditySpecAPI pClass = Global.getSettings().getCommoditySpec(cid); 125 extra = " (" + pClass.getName() + " " + colony.getPlanetEntity().getTypeNameWithLowerCaseWorld() + ")"; 126 } 127 addEntry("Abdandoned size " + colony.getSize() + " colony " + colony.getOnOrAt() + " " + colony.getName() + extra); 128 } 129 130 public void reportPlayerColonizedPlanet(PlanetAPI planet) { 131 SurveyPlugin plugin = (SurveyPlugin) Global.getSettings().getNewPluginInstance("surveyPlugin"); 132 String cid = plugin.getSurveyDataType(planet); 133 CommoditySpecAPI pClass = Global.getSettings().getCommoditySpec(cid); 134 addEntry("Established colony on " + planet.getName() + " (" + 135 pClass.getName().replaceAll(" Survey Data", "") + " " + planet.getTypeNameWithLowerCaseWorld() + ")"); 136 } 137 138 139 public void takeSnapshot(boolean debug) { 140 PLSnapshot snapshot = new PLSnapshot(); 141 142 for (String key : stats.keySet()) { 143 PLStat stat = stats.get(key); 144 long value = stat.getValueForAllAccrued(); 145 146 if (debug) { 147 value += (int)((float) Math.random() * 500); 148 value -= (int)((float) Math.random() * 500); 149 if (value < 0) value = 0; 150 } 151 152 snapshot.getData().put(key, value); 153 } 154 155 // have to add it here otherwise getPrevValue() uses this snapshot not the actual previous one 156 data.add(snapshot); 157 } 158 159 160 public List<SModRecord> getSModsInstalled() { 161 return smodsInstalled; 162 } 163 164 public void addSModsInstalled(SModRecord record) { 165 smodsInstalled.add(record); 166 } 167 168 public List<OfficerSkillGainRecord> getOfficerSkillsLearned() { 169 return officerSkillsLearned; 170 } 171 172 public void addOfficerSkillRecord(OfficerSkillGainRecord record) { 173 officerSkillsLearned.add(record); 174 } 175 public void removeOfficerSkillRecord(String personId, String skillId, boolean elite) { 176 Iterator<OfficerSkillGainRecord> iter = officerSkillsLearned.iterator(); 177 while (iter.hasNext()) { 178 OfficerSkillGainRecord record = iter.next(); 179 if (record.personId.equals(personId) && record.skillId.equals(skillId) && record.elite == elite) { 180 iter.remove(); 181 } 182 } 183 } 184 185 186 187 protected Object readResolve() throws DataFormatException, UnsupportedEncodingException { 188 if (stats == null) { 189 stats = new LinkedHashMap<String, PLStat>(); 190 initStats(); 191 } 192 if (data == null) { 193 data = new ArrayList<PLSnapshot>(); 194 } 195 196 if (smodsInstalled == null) { 197 smodsInstalled = new ArrayList<SModRecord>(); 198 } 199 if (officerSkillsLearned == null) { 200 officerSkillsLearned = new ArrayList<OfficerSkillGainRecord>(); 201 } 202 203 204 byte [] input = BaseTiledTerrain.toByteArray(saved); 205 206 Inflater decompressor = new Inflater(); 207 decompressor.setInput(input); 208 209 StringBuilder result = new StringBuilder(); 210 byte [] temp = new byte[100]; 211 while (!decompressor.finished()) { 212 int read = decompressor.inflate(temp); 213 // this should be OK since the data is base64 encoded so should be ascii not utf8 i.e. no multi-byte chars 214 result.append(new String(temp, 0, read, "UTF-8")); 215 } 216 217 decompressor.end(); 218 219 saved = result.toString(); 220 221 data.clear(); 222 if (saved == null) saved = ""; 223 String [] parts = saved.split("\n"); 224 for (String p : parts) { 225 if (p.isEmpty()) continue; 226 PLSnapshot next = new PLSnapshot(p); 227 data.add(next); 228 } 229 return this; 230 } 231 232 protected Object writeReplace() throws UnsupportedEncodingException { 233 saved = ""; 234 for (PLSnapshot s : data) { 235 saved += s.getString() + "\n"; 236 } 237 if (!saved.isEmpty()) { 238 saved = saved.substring(0, saved.length() - 1); 239 } 240 241 Deflater compressor = new Deflater(); 242 compressor.setInput(saved.getBytes("UTF-8")); 243 compressor.finish(); 244 245 StringBuilder result = new StringBuilder(); 246 byte [] temp = new byte[100]; 247 while (!compressor.finished()) { 248 int read = compressor.deflate(temp); 249 result.append(BaseTiledTerrain.toHexString(Arrays.copyOf(temp, read))); 250 } 251 compressor.end(); 252 253 saved = result.toString(); 254 255 return this; 256 } 257 258 259 protected void initStats() { 260 addStat(new PLStatLevel()); 261 addStat(new PLStatFleet()); 262 addStat(new PLStatCredits()); 263 addStat(new PLStatSupplies()); 264 addStat(new PLStatFuel()); 265 addStat(new PLStatCargo()); 266 addStat(new PLStatCrew()); 267 addStat(new PLStatMarines()); 268 addStat(new PLStatColonies()); 269 } 270 271 public CampaignClockAPI getDateForIndex(int index) { 272 if (index < 0 || index >= data.size()) { 273 return Global.getSector().getClock(); 274 } 275 PLSnapshot s = data.get(index); 276 CampaignClockAPI clock = Global.getSector().getClock().createClock(s.getTimestamp()); 277 return clock; 278 } 279 280 public void addStat(PLStat stat) { 281 stats.put(stat.getId(), stat); 282 } 283 284 public Map<String, PLStat> getStats() { 285 return stats; 286 } 287 288 public long getPrevValue(String key) { 289 if (data.isEmpty()) return 0; 290 Long val = data.get(data.size() - 1).getData().get(key); 291 if (val == null) return 0; 292 return val; 293 } 294 295 public List<PLSnapshot> getData() { 296 return data; 297 } 298 299 public List<PLEntry> getEntries() { 300 return entries; 301 } 302 303 public void addEntry(PLEntry entry) { 304 entries.add(entry); 305 } 306 public void addEntry(String text) { 307 entries.add(new PLTextEntry(text)); 308 } 309 public void addEntry(String text, boolean story) { 310 entries.add(new PLTextEntry(text, story)); 311 } 312 313 public PLIntelUIData getUIData() { 314 if (uiData == null) { 315 uiData = new PLIntelUIData(); 316 } 317 return uiData; 318 } 319 320 public void reportRaidForValuablesFinishedBeforeCargoShown(InteractionDialogAPI dialog, MarketAPI market, TempData actionData, CargoAPI cargo) { 321 } 322 323 public void reportRaidToDisruptFinished(InteractionDialogAPI dialog, MarketAPI market, TempData actionData, Industry industry) { 324 } 325 326 public void reportTacticalBombardmentFinished(InteractionDialogAPI dialog, MarketAPI market, TempData actionData) { 327 } 328 329 public void reportPlayerOpenedMarketAndCargoUpdated(MarketAPI market) { 330 } 331} 332 333