001package com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special; 002 003import org.lwjgl.util.vector.Vector2f; 004 005import com.fs.starfarer.api.Global; 006import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 007import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI; 008import com.fs.starfarer.api.campaign.InteractionDialogAPI; 009import com.fs.starfarer.api.campaign.JumpPointAPI; 010import com.fs.starfarer.api.campaign.LocationAPI; 011import com.fs.starfarer.api.campaign.PlanetAPI; 012import com.fs.starfarer.api.campaign.SectorEntityToken; 013import com.fs.starfarer.api.campaign.StarSystemAPI; 014import com.fs.starfarer.api.combat.ShipAPI.HullSize; 015import com.fs.starfarer.api.combat.ShipVariantAPI; 016import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin; 017import com.fs.starfarer.api.impl.campaign.ids.Entities; 018import com.fs.starfarer.api.impl.campaign.ids.Tags; 019import com.fs.starfarer.api.impl.campaign.ids.Terrain; 020import com.fs.starfarer.api.impl.campaign.intel.misc.BreadcrumbIntelV2; 021import com.fs.starfarer.api.impl.campaign.intel.misc.SalvorsTallyIntel; 022import com.fs.starfarer.api.impl.campaign.procgen.Constellation; 023import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageSpecialInteraction.SalvageSpecialData; 024import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.SalvageSpecialInteraction.SalvageSpecialPlugin; 025import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin; 026import com.fs.starfarer.api.util.CountingMap; 027import com.fs.starfarer.api.util.Misc; 028 029public class BreadcrumbSpecial extends BaseSalvageSpecial { 030 031 032 public static class BreadcrumbSpecialData implements SalvageSpecialData { 033 public String targetId = null; 034 public String targetName = null; 035 public BreadcrumbSpecialData(String targetId) { 036 this.targetId = targetId; 037 } 038 039 public SalvageSpecialPlugin createSpecialPlugin() { 040// if (Global.getSector().getEntityById(targetId) instanceof PlanetAPI) { 041// return new SurveyDataSpecial(); 042// } 043 return new BreadcrumbSpecial(); 044 } 045 } 046 047 private BreadcrumbSpecialData data; 048 049 public BreadcrumbSpecial() { 050 } 051 052 053 @Override 054 public void init(InteractionDialogAPI dialog, Object specialData) { 055 super.init(dialog, specialData); 056 057 data = (BreadcrumbSpecialData) specialData; 058 059 initEntityLocation(); 060 } 061 062 private void initEntityLocation() { 063 if (data.targetId == null) { 064 initNothing(); 065 return; 066 } 067 068 SectorEntityToken target = Global.getSector().getEntityById(data.targetId); 069 if (target == null) { 070 initNothing(); 071 return; 072 } 073 074 // already discovered 075 if (!target.hasSensorProfile() && !target.isDiscoverable() && !(target instanceof PlanetAPI)) { 076 initNothing(); 077 return; 078 } 079 080 if (target instanceof PlanetAPI && !Misc.hasUnexploredRuins(target.getMarket())) { 081 initNothing(); 082 return; 083 } 084 085 086 String targetName = getNameWithAOrAn(target, data.targetName, true, false); 087 String targetNameUC = getNameWithAOrAn(target, data.targetName, false, true); 088 //String entityName = getNameWithAOrAn(entity, null); 089 String located = getLocatedString(target, true); 090 091 String nameForTitle = targetNameUC.substring(targetNameUC.indexOf(" ") + 1); 092 //if (target.getCu) 093 String subject = getString("Location: " + nameForTitle); 094 if (target instanceof PlanetAPI && target.getMarket() != null) { 095 subject = "Location: " + Misc.getRuinsSpec(target.getMarket()).getName(); 096 //subject += " Ruins"; 097 } 098 099 100 String text1 = "The $shortName's memory banks are partially accessible, and "; 101 text1 += "contain information indicating that " + targetName + " is " + located + "."; 102 103 String text1ForIntel = "While exploring $aOrAn $nameInText, your crews found a " + 104 "partially accessible memory bank " + 105 "containing information that indicates " + targetName + " is " + located + "."; 106 107 boolean debris = Entities.DEBRIS_FIELD_SHARED.equals(entity.getCustomEntityType()); 108 if (debris) { 109 text1 = "Your salvage crews find a functional memory bank in the debris. It contains information " + 110 "indicating that " + targetName.toLowerCase() + " is " + located + "."; 111 } 112 113 114 if (target.getCustomPlugin() instanceof DerelictShipEntityPlugin) { 115 DerelictShipEntityPlugin dsep = (DerelictShipEntityPlugin) target.getCustomPlugin(); 116 ShipVariantAPI variant = dsep.getData().ship.variant; 117 if (variant == null && dsep.getData().ship.variantId != null) { 118 variant = Global.getSettings().getVariant(dsep.getData().ship.variantId); 119 } 120 if (variant != null) { 121 String size = null; 122 if (variant.getHullSize() == HullSize.FRIGATE || 123 variant.getHullSize() == HullSize.DESTROYER) { 124 size = "Based on the information, it's likely the ship is small, a frigate or a destroyer at the largest."; 125 } else { 126 size = "The vessel is likely to be at least cruiser-sized."; 127 } 128 129 if (size != null) { 130 text1 += "\n\n" + size; 131 text1ForIntel += "\n\n" + size; 132 } 133 } 134 } 135 136 addText(text1); 137 138 BreadcrumbIntelV2 intel = new BreadcrumbIntelV2(target); 139 intel.setTitle(getString(subject)); 140 intel.setText(getString(text1ForIntel)); 141 142 143 String iconId = null; 144 if (target instanceof CampaignTerrainAPI && 145 ((CampaignTerrainAPI)target).getPlugin() instanceof DebrisFieldTerrainPlugin) { 146 iconId = "link_to_debris_field"; 147 } 148 if (iconId == null) { 149 boolean wreck = SalvorsTallyIntel.isDerelictShip(target); 150 boolean station = SalvorsTallyIntel.isOrbitalInstallation(target); 151 if (target instanceof PlanetAPI) { 152 iconId = "link_to_ruins"; 153 } else if (wreck) { 154 iconId = "link_to_derelict_ship"; 155 } else if (station) { 156 iconId = "link_to_orbital_installation"; 157 } else { 158 iconId = "link_to_other"; 159 } 160 } 161 162 if (iconId != null) { 163 //intel.setIcon(Global.getSettings().getSpriteName("intel", iconId)); 164 intel.setIconId(iconId); 165 } 166 Global.getSector().getIntelManager().addIntel(intel, false, text); 167 168// CommMessageAPI message = FleetLog.beginEntry(subject, target); 169// message.getSection1().addPara(getString(text1)); 170// FleetLog.addToLog(message, text); 171 172 //unsetData(); 173 setDone(true); 174 //setShowAgain(true); 175 } 176 177 public static String getNameWithAOrAn(SectorEntityToken target, String override, boolean lowercaseDebris, boolean forTitle) { 178 String targetAOrAn = "a"; 179 String targetName = override; 180 if (targetName == null) { 181 if (target instanceof CustomCampaignEntityAPI) { 182 CustomCampaignEntityAPI custom = (CustomCampaignEntityAPI) target; 183 targetName = custom.getCustomEntitySpec().getNameInText(); 184 if (forTitle) targetName = custom.getName(); 185 targetAOrAn = custom.getCustomEntitySpec().getAOrAn(); 186 } else if (target instanceof PlanetAPI) { 187 PlanetAPI planet = (PlanetAPI) target; 188 targetName = planet.getTypeNameWithLowerCaseWorld().toLowerCase(); 189 targetName += " with unexplored ruins"; 190 if (forTitle) targetName = planet.getTypeNameWithWorld(); 191 targetAOrAn = planet.getSpec().getAOrAn(); 192 } else { 193 targetName = target.getName(); 194 } 195 } 196 if (lowercaseDebris && target.hasTag(Tags.DEBRIS_FIELD)) { 197 targetName = targetName.toLowerCase(); 198 } 199 return targetAOrAn + " " + targetName; 200 } 201 202 public static String getLocatedString(SectorEntityToken target) { 203 return getLocatedString(target, false); 204 } 205 public static String getLocatedString(SectorEntityToken target, boolean withSystem) { 206 String loc = getLocationDescription(target, withSystem); 207 208 String orbiting = ""; 209 boolean useTerrain = false; 210 if (target.getOrbitFocus() != null) { 211 if (target.getOrbitFocus() instanceof PlanetAPI) { 212 PlanetAPI focus = (PlanetAPI) target.getOrbitFocus(); 213 boolean isPrimary = target.getContainingLocation() instanceof StarSystemAPI && 214 focus == ((StarSystemAPI)target.getContainingLocation()).getStar(); 215 if (!focus.isStar() || !isPrimary) { 216 orbiting = "orbiting " + focus.getSpec().getAOrAn() + " " + focus.getTypeNameWithLowerCaseWorld().toLowerCase() + " in "; 217 } else { 218 float dist = Misc.getDistance(focus.getLocation(), target.getLocation()); 219 //float dist = Misc.getDistance(new Vector2f(), target.getLocation()); 220 if (dist < 3000) { 221 orbiting = "located in the heart of "; 222 } else if (dist > 12000) { 223 orbiting = "located in the outer reaches of "; 224 } else { 225 //orbiting = "located in "; 226 orbiting = "located some distance away from the center of "; 227 } 228 useTerrain = true; 229 } 230 } else if (target.getOrbitFocus() instanceof CustomCampaignEntityAPI) { 231 CustomCampaignEntityAPI custom = (CustomCampaignEntityAPI) target.getOrbitFocus(); 232 orbiting = "orbiting " + custom.getCustomEntitySpec().getAOrAn() + " " + custom.getCustomEntitySpec().getNameInText() + " in "; 233 } else if (target.getOrbitFocus() instanceof JumpPointAPI) { 234 orbiting = "orbiting a jump-point in "; 235 } else if (target.getOrbitFocus() instanceof CampaignTerrainAPI) { 236 CampaignTerrainAPI t = (CampaignTerrainAPI) target.getOrbitFocus(); 237 String n = t.getPlugin().getNameForTooltip().toLowerCase(); 238 String a = Misc.getAOrAnFor(n); 239 orbiting = "located inside " + a + " " + n + " "; 240 241 float dist = Misc.getDistance(new Vector2f(), target.getLocation()); 242 if (dist < 3000) { 243 orbiting += "in the heart of "; 244 } else if (dist > 12000) { 245 orbiting += "in the outer reaches of "; 246 } else { 247 orbiting += "some distance away from the center of "; 248 } 249 } else { // center of a binary/nebula/etc 250 //float dist = Misc.getDistance(target.getOrbitFocus().getLocation(), target.getLocation()); 251 float dist = Misc.getDistance(new Vector2f(), target.getLocation()); 252 if (dist < 3000) { 253 orbiting = "located in the heart of "; 254 } else if (dist > 12000) { 255 orbiting = "located in the outer reaches of "; 256 } else { 257 //orbiting = "located in "; 258 orbiting = "located some distance away from the center of "; 259 } 260 useTerrain = true; 261 } 262 } else if (target.getContainingLocation() != null && target.getContainingLocation().isNebula()) { 263 float dist = Misc.getDistance(new Vector2f(), target.getLocation()); 264 if (dist < 3000) { 265 orbiting = "located in the heart of "; 266 } else if (dist > 12000) { 267 orbiting = "located on the outskirts of "; 268 } else { 269 //orbiting = "located in "; 270 orbiting = "located some distance away from the center of "; 271 } 272 useTerrain = true; 273 } 274 275 if (useTerrain) { 276 String terrainString = getTerrainString(target); 277 if (terrainString != null) { 278 orbiting = "located in " + terrainString + " in "; 279 } 280 } 281 282 if (orbiting == null || orbiting.isEmpty()) orbiting = "located in "; 283 return orbiting + loc; 284 } 285 286 public static String getTerrainString(SectorEntityToken entity) { 287 if (!(entity.getContainingLocation() instanceof StarSystemAPI)) return null; 288 289 StarSystemAPI system = (StarSystemAPI) entity.getContainingLocation(); 290 for (CampaignTerrainAPI terrain : system.getTerrainCopy()) { 291 if (!terrain.getPlugin().containsEntity(entity)) continue; 292 293 String type = terrain.getType(); 294 295 if (Terrain.ASTEROID_BELT.equals(type)) return "an asteroid belt"; 296 if (Terrain.ASTEROID_FIELD.equals(type)) return "an asteroid field"; 297 //if (Terrain.MAGNETIC_FIELD.equals(type)) return "a magnetic field"; 298 if (terrain.hasTag(Tags.ACCRETION_DISK)) return "an accretion disk"; 299 if (Terrain.RING.equals(type)) return "a ring system"; 300 } 301 302 return null; 303 } 304 305 public static String getLocationDescription(SectorEntityToken entity, boolean withSystem) { 306 LocationAPI loc = entity.getContainingLocation(); 307 if (loc == null) { 308 return "an unknown location"; 309 } 310 if (loc.isHyperspace()) { 311 return "hyperspace"; 312 } 313 StarSystemAPI system = (StarSystemAPI) loc; 314 315// if (system == Global.getSector().getCurrentLocation()) { 316// return "the " + system.getNameWithLowercaseType(); 317// } 318 319 //if (entity.getConstellation() != null && entity.getConstellation() != Global.getSector().getCurrentLocation().getConstellation()) { 320 if (withSystem || entity.getConstellation() == null || entity.getConstellation().getSystems().size() == 1 || 321 entity.isInCurrentLocation()) { 322 return "the " + system.getNameWithLowercaseType(); 323 } 324 325 Constellation c = entity.getConstellation(); 326 String cText = "in the " + c.getNameWithLowercaseType(); 327 if (c.getSystems().size() == 1) { 328 return "the " + system.getNameWithLowercaseType(); 329 } 330 331 if (system.isNebula()) { 332 return "a nebula " + cText; 333 } 334 335 if (system.getTertiary() != null) { 336 return "a trinary star system " + cText; 337 } 338 339 if (system.getSecondary() != null) { 340 return "a binary star system " + cText; 341 } 342 343 PlanetAPI star = system.getStar(); 344 if (star != null) { 345 if (star.getSpec().isBlackHole()) { 346 return "a black hole system " + cText; 347 } 348 349 //String sysText = star.getSpec().getAOrAn() + " " + star.getSpec().getName().toLowerCase() + " system "; 350 351 String type = getStarTypeName(star); 352 String color = getStarColorName(star); 353 354 String sysText = null; 355 CountingMap<String> counts = getTypeAndColorCounts(c); 356 int cColor = counts.getCount(color); 357 int cType = counts.getCount(type); 358 if (cColor > 1 && cType > cColor) { 359 sysText = "a system with " + star.getSpec().getAOrAn() + " " + color + " primary star "; 360 } else if (cType > 0) { 361 sysText = "a system with " + "a" + " " + type + " primary star "; 362 } else if (cColor > 0) { 363 sysText = "a system with " + star.getSpec().getAOrAn() + " " + color + " primary star "; 364 } 365 366 if (sysText != null) { 367 return sysText + cText; 368 } 369 } 370 371 //if (system.getType() == StarSystemType.SINGLE) { 372 return "the " + system.getNameWithLowercaseType() + " " + cText; 373 //return "orbit around " + getStarDescription(system.getStar()) + " " + cText; 374 } 375 376 public static CountingMap<String> getTypeAndColorCounts(Constellation c) { 377 CountingMap<String> map = new CountingMap<String>(); 378 for (StarSystemAPI system : c.getSystems()) { 379 PlanetAPI star = system.getStar(); 380 if (system.isNebula()) continue; 381 if (system.getSecondary() != null) continue; 382 if (system.getTertiary() != null) continue; 383 384 String type = getStarTypeName(star); 385 String color = getStarColorName(star); 386 if (type != null) map.add(type); 387 if (color != null) map.add(color); 388 } 389 return map; 390 } 391 392 public static String getStarTypeName(PlanetAPI star) { 393 String name = star.getSpec().getName().toLowerCase(); 394 if (name.contains(" dwarf")) { 395 return "dwarf"; 396 } else if (name.contains(" star")) { 397 return null; 398 } else if (name.contains(" giant")) { 399 return "giant"; 400 } else if (name.contains(" supergiant")) { 401 return "supergiant"; 402 } 403 return null; 404 } 405 public static String getStarColorName(PlanetAPI star) { 406 String name = star.getSpec().getName().toLowerCase(); 407 if (name.contains(" dwarf")) { 408 name = name.replace(" dwarf", ""); 409 } else if (name.contains(" star")) { 410 name = name.replace(" star", ""); 411 } else if (name.contains(" giant")) { 412 name = name.replace(" giant", ""); 413 } else if (name.contains(" supergiant")) { 414 name = name.replace(" supergiant", ""); 415 } 416 if (!name.equals(star.getSpec().getName().toLowerCase())) { 417 return name; 418 } 419 return null; 420 } 421 422 /* 423 public static String getStarDescription(PlanetAPI star) { 424 String type = star.getTypeId(); 425 426 if (type.equals(StarTypes.BLACK_HOLE)) return "a black hole"; 427 if (type.equals(StarTypes.NEUTRON_STAR)) return "a neutron star"; 428 429 if (type.equals(StarTypes.ORANGE) || 430 type.equals(StarTypes.ORANGE_GIANT)) { 431 return "an orange star"; 432 } 433 434 if (type.equals(StarTypes.RED_DWARF) || 435 type.equals(StarTypes.RED_SUPERGIANT) || 436 type.equals(StarTypes.RED_GIANT)) { 437 return "a red star"; 438 } 439 440 if (type.equals(StarTypes.BLUE_GIANT) || 441 type.equals(StarTypes.BLUE_SUPERGIANT)) { 442 return "a blue star"; 443 } 444 445 if (type.equals(StarTypes.BROWN_DWARF) || 446 type.equals(StarTypes.WHITE_DWARF)) { 447 return "a dim star"; 448 } 449 450 if (type.equals(StarTypes.YELLOW)) { 451 return "a yellow star"; 452 } 453 454 return "a star of unknown type"; 455 } 456 */ 457 458 @Override 459 public void optionSelected(String optionText, Object optionData) { 460 super.optionSelected(optionText, optionData); 461 } 462 463 464} 465 466 467