001package com.fs.starfarer.api.impl.campaign.intel.misc; 002 003import java.util.ArrayList; 004import java.util.List; 005import java.util.Set; 006 007import java.awt.Color; 008 009import com.fs.starfarer.api.Global; 010import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 011import com.fs.starfarer.api.campaign.CustomEntitySpecAPI; 012import com.fs.starfarer.api.campaign.PlanetAPI; 013import com.fs.starfarer.api.campaign.SectorEntityToken; 014import com.fs.starfarer.api.campaign.StarSystemAPI; 015import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 016import com.fs.starfarer.api.campaign.econ.MarketAPI; 017import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel; 018import com.fs.starfarer.api.impl.campaign.ids.Conditions; 019import com.fs.starfarer.api.impl.campaign.ids.Entities; 020import com.fs.starfarer.api.impl.campaign.ids.Tags; 021import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 022import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin; 023import com.fs.starfarer.api.ui.SectorMapAPI; 024import com.fs.starfarer.api.ui.TooltipMakerAPI; 025import com.fs.starfarer.api.util.Misc; 026 027public class SalvorsTallyIntel extends BaseIntelPlugin { 028 029 public static enum SalvageValue { 030 NONE("none"), 031 LOW("low"), 032 MEDIUM("medium"), 033 HIGH("high"); 034 private String valueString; 035 private SalvageValue(String value) { 036 this.valueString = value; 037 } 038 public String getValueString() { 039 return valueString; 040 } 041 042 } 043 044 public static class SalvorsTally { 045 public int ruins; 046 public int orbital; 047 public int derelicts; 048 public int debris; 049 public int other; 050 public SalvageValue value; 051 public List<SectorEntityToken> all = new ArrayList<>(); 052 } 053 054 055 protected StarSystemAPI system; 056 protected long removalCheckTimestamp = 0; 057 protected float daysUntilRemoveCheck = 1f; 058 059 public SalvorsTallyIntel(StarSystemAPI system) { 060 this.system = system; 061 } 062 063 064 @Override 065 public boolean shouldRemoveIntel() { 066 if (!system.isCurrentLocation()) { 067 float daysSince = Global.getSector().getClock().getElapsedDaysSince(removalCheckTimestamp); 068 if (daysSince > daysUntilRemoveCheck) { 069 SalvorsTally tally = computeTally(); 070 if (tally.value == SalvageValue.NONE) { 071 return true; 072 } 073 removalCheckTimestamp = Global.getSector().getClock().getTimestamp(); 074 daysUntilRemoveCheck = 3f + (float) Math.random() * 3f; 075 } 076 } 077 return super.shouldRemoveIntel(); 078 } 079 080 081 public StarSystemAPI getSystem() { 082 return system; 083 } 084 085 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode, boolean isUpdate, Color tc, float initPad) { 086 Color h = Misc.getHighlightColor(); 087 Color g = Misc.getGrayColor(); 088 float pad = 3f; 089 float opad = 10f; 090 091 SalvorsTally tally = computeTally(); 092 093 bullet(info); 094 095 if (mode != ListInfoMode.IN_DESC) { 096 Color highlight = h; 097 if (tally.value == SalvageValue.NONE) highlight = tc; 098 info.addPara("Projected value: %s", initPad, tc, highlight, tally.value.getValueString()); 099 initPad = 0f; 100 } 101 if (tally.ruins > 0) { 102 String s = tally.ruins > 1 ? "s" : ""; 103 s = "s"; // "1 ruins" actually makes more sense here than "1 ruin" 104 info.addPara("%s unexplored planetside ruin" + s, initPad, tc, h, "" + tally.ruins); 105 initPad = 0f; 106 } 107 if (tally.orbital > 0) { 108 String s = tally.orbital > 1 ? "s" : ""; 109 info.addPara("%s orbital installation" + s, initPad, tc, h, "" + tally.orbital); 110 initPad = 0f; 111 } 112 if (tally.derelicts > 0) { 113 String s = tally.derelicts > 1 ? "s" : ""; 114 info.addPara("%s derelict ship" + s, initPad, tc, h, "" + tally.derelicts); 115 initPad = 0f; 116 } 117 if (tally.debris > 0) { 118 String s = tally.debris > 1 ? "s" : ""; 119 info.addPara("%s unexplored debris field" + s, initPad, tc, h, "" + tally.debris); 120 initPad = 0f; 121 } 122 if (tally.other > 0) { 123 String s = tally.other > 1 ? "s" : ""; 124 info.addPara("%s other source" + s + " of salvage", initPad, tc, h, "" + tally.other); 125 initPad = 0f; 126 } 127 128 129 unindent(info); 130 } 131 132 @Override 133 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 134 Color h = Misc.getHighlightColor(); 135 Color g = Misc.getGrayColor(); 136 Color tc = Misc.getTextColor(); 137 float pad = 3f; 138 float small = 3f; 139 float opad = 10f; 140 141 SalvorsTally tally = computeTally(); 142 143 info.addImage(Global.getSettings().getSpriteName("illustrations", "salvor_explore_hull"), width, opad); 144 145 if (tally.value == SalvageValue.NONE) { 146 info.addPara("No salvage is known to be available in the " + system.getNameWithLowercaseTypeShort() + ".", opad); 147 } else { 148 info.addPara("A log of the salvage known to be available in the " + 149 system.getNameWithLowercaseTypeShort() + ". The projected total value of the known salvage is %s.", 150 opad, h, tally.value.getValueString()); 151 } 152 153 addBulletPoints(info, ListInfoMode.IN_DESC); 154 155 info.addPara("Other, as yet undiscovered, salvage may be present in the system.", opad); 156 157 addLogTimestamp(info, tc, opad); 158 159 addDeleteButton(info, width); 160 } 161 162 @Override 163 public String getIcon() { 164 SalvorsTally tally = computeTally(); 165 if (tally.value == SalvageValue.NONE) { 166 return Global.getSettings().getSpriteName("intel", "salvors_tally_none"); 167 } else if (tally.value == SalvageValue.LOW) { 168 return Global.getSettings().getSpriteName("intel", "salvors_tally_low"); 169 } else if (tally.value == SalvageValue.MEDIUM) { 170 return Global.getSettings().getSpriteName("intel", "salvors_tally_medium"); 171 } else { 172 return Global.getSettings().getSpriteName("intel", "salvors_tally_high"); 173 } 174 } 175 176 @Override 177 public Set<String> getIntelTags(SectorMapAPI map) { 178 Set<String> tags = super.getIntelTags(map); 179 //tags.add(Tags.INTEL_FLEET_LOG); 180 SalvorsTally tally = computeTally(); 181 if (tally.value != SalvageValue.NONE && tally.value != SalvageValue.LOW) { 182 tags.add(Tags.INTEL_EXPLORATION); 183 } 184 tags.add(Tags.INTEL_SALVAGE); 185 return tags; 186 } 187 188 @Override 189 public SectorEntityToken getMapLocation(SectorMapAPI map) { 190 //return system.getCenter(); // shows star in hyperspace, not system map 191 SalvorsTally tally = computeTally(); 192 if (tally.all.size() == 1) { 193 return tally.all.get(0); 194 } 195 return system.createToken(0, 0); 196 } 197 198 public String getName() { 199 return "Salvor's Tally - " + system.getBaseName(); 200 } 201 202 203 @Override 204 public String getCommMessageSound() { 205 return super.getCommMessageSound(); 206 //return "ui_discovered_entity"; 207 } 208 209 public String getSortString() { 210 return getSortStringNewestFirst(); 211 } 212 213 214 protected transient long tallyTimestamp; 215 protected transient SalvorsTally cached; 216 public SalvorsTally computeTally() { 217 218 long ts = Global.getSector().getClock().getTimestamp(); 219 if (cached != null && ts == tallyTimestamp) { 220 return cached; 221 } 222 223 SalvorsTally tally = new SalvorsTally(); 224 225 int value = 0; 226 for (SectorEntityToken entity : system.getEntitiesWithTag(Tags.SALVAGEABLE)) { 227 if (entity.isDiscoverable()) continue; 228 if (entity.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue; 229 if (entity.hasTag(Tags.FADING_OUT_AND_EXPIRING)) continue; 230 if (entity.hasTag(Tags.EXPIRES)) continue; 231 232 boolean wreck = isDerelictShip(entity); 233 boolean station = isOrbitalInstallation(entity); 234 235 if (wreck) { 236 tally.derelicts++; 237 value += 1; 238 } else if (station) { 239 tally.orbital++; 240 value += 10; 241 } else { 242 tally.other++; 243 value += 1; 244 } 245 246 if (entity.hasTag(Tags.NEUTRINO_HIGH)) { 247 value += 20; 248 } 249 tally.all.add(entity); 250 } 251 252 for (CampaignTerrainAPI entity : system.getTerrainCopy()) { 253 if (entity.isDiscoverable()) continue; 254 if (entity.hasTag(Tags.EXPIRES)) continue; 255 256 if (entity instanceof CampaignTerrainAPI && 257 ((CampaignTerrainAPI)entity).getPlugin() instanceof DebrisFieldTerrainPlugin) { 258 DebrisFieldTerrainPlugin plugin = (DebrisFieldTerrainPlugin) ((CampaignTerrainAPI)entity).getPlugin(); 259 if (plugin.isScavenged()) { 260 continue; 261 } 262 263 tally.debris++; 264 value += 1; 265 tally.all.add(entity); 266 } 267 } 268 269 for (PlanetAPI planet : system.getPlanets()) { 270 if (planet.isStar()) continue; 271 272 MarketAPI market = planet.getMarket(); 273 if (market == null) continue; 274 275 if (market.getSurveyLevel() != SurveyLevel.FULL) continue; 276 277 if (Misc.hasUnexploredRuins(market)) { 278 tally.ruins++; 279 tally.all.add(planet); 280 if (market.hasCondition(Conditions.RUINS_SCATTERED)) { 281 value += 2; 282 } else if (market.hasCondition(Conditions.RUINS_WIDESPREAD)) { 283 value += 6; 284 } else if (market.hasCondition(Conditions.RUINS_EXTENSIVE)) { 285 value += 10; 286 } else if (market.hasCondition(Conditions.RUINS_VAST)) { 287 value += 20; 288 } 289 } 290 } 291 if (value <= 0) { 292 tally.value = SalvageValue.NONE; 293 } else if (value < 10) { 294 tally.value = SalvageValue.LOW; 295 } else if (value < 20) { 296 tally.value = SalvageValue.MEDIUM; 297 } else { 298 tally.value = SalvageValue.HIGH; 299 } 300 301 tallyTimestamp = ts; 302 cached = tally; 303 304 return tally; 305 } 306 307 public static boolean isDerelictShip(SectorEntityToken entity) { 308 boolean wreck = Entities.WRECK.equals(entity.getCustomEntityType()); 309 wreck |= entity.hasTag(Tags.WRECK); 310 return wreck; 311 } 312 313 public static boolean isOrbitalInstallation(SectorEntityToken entity) { 314 boolean station = false; 315 if (entity.getCustomEntitySpec() != null) { 316 CustomEntitySpecAPI spec = entity.getCustomEntitySpec(); 317 if (spec.getShortName().toLowerCase().contains("station") || 318 spec.getShortName().toLowerCase().contains("habitat") || 319 spec.getShortName().toLowerCase().contains("orbital") || 320 entity.hasTag("salvor_orbital")) { 321 station = true; 322 } 323 } 324 return station; 325 } 326 327 public static SalvorsTallyIntel getSalvorsTallyIntel(StarSystemAPI system) { 328 for (IntelInfoPlugin intel : Global.getSector().getIntelManager().getIntel(SalvorsTallyIntel.class)) { 329 if (((SalvorsTallyIntel)intel).getSystem() == system) return (SalvorsTallyIntel)intel; 330 } 331 return null; 332 } 333} 334 335