001package com.fs.starfarer.api.impl.codex; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.HashMap; 007import java.util.HashSet; 008import java.util.LinkedHashMap; 009import java.util.LinkedHashSet; 010import java.util.List; 011import java.util.Map; 012import java.util.Set; 013import java.util.UUID; 014 015import java.awt.Color; 016 017import org.json.JSONException; 018 019import com.fs.starfarer.api.Global; 020import com.fs.starfarer.api.ModPlugin; 021import com.fs.starfarer.api.ModSpecAPI; 022import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType; 023import com.fs.starfarer.api.campaign.CargoStackAPI; 024import com.fs.starfarer.api.campaign.PlanetSpecAPI; 025import com.fs.starfarer.api.campaign.SpecialItemPlugin; 026import com.fs.starfarer.api.campaign.SpecialItemSpecAPI; 027import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI; 028import com.fs.starfarer.api.campaign.impl.items.MultiBlueprintItemPlugin; 029import com.fs.starfarer.api.characters.MarketConditionSpecAPI; 030import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI; 031import com.fs.starfarer.api.characters.SkillSpecAPI; 032import com.fs.starfarer.api.combat.DamageType; 033import com.fs.starfarer.api.combat.ShipAPI; 034import com.fs.starfarer.api.combat.ShipAPI.HullSize; 035import com.fs.starfarer.api.combat.ShipHullSpecAPI; 036import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints; 037import com.fs.starfarer.api.combat.ShipSystemSpecAPI; 038import com.fs.starfarer.api.combat.ShipVariantAPI; 039import com.fs.starfarer.api.combat.WeaponAPI.AIHints; 040import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize; 041import com.fs.starfarer.api.combat.WeaponAPI.WeaponType; 042import com.fs.starfarer.api.fleet.FleetMemberAPI; 043import com.fs.starfarer.api.fleet.FleetMemberType; 044import com.fs.starfarer.api.impl.SharedUnlockData; 045import com.fs.starfarer.api.impl.campaign.econ.ResourceDepositsCondition; 046import com.fs.starfarer.api.impl.campaign.econ.impl.InstallableItemEffect; 047import com.fs.starfarer.api.impl.campaign.econ.impl.ItemEffectsRepo; 048import com.fs.starfarer.api.impl.campaign.ids.Abilities; 049import com.fs.starfarer.api.impl.campaign.ids.Commodities; 050import com.fs.starfarer.api.impl.campaign.ids.Conditions; 051import com.fs.starfarer.api.impl.campaign.ids.HullMods; 052import com.fs.starfarer.api.impl.campaign.ids.Industries; 053import com.fs.starfarer.api.impl.campaign.ids.Items; 054import com.fs.starfarer.api.impl.campaign.ids.Planets; 055import com.fs.starfarer.api.impl.campaign.ids.ShipSystems; 056import com.fs.starfarer.api.impl.campaign.ids.Skills; 057import com.fs.starfarer.api.impl.campaign.ids.Tags; 058import com.fs.starfarer.api.impl.combat.threat.EnergyLashActivatedSystem; 059import com.fs.starfarer.api.loading.AbilitySpecAPI; 060import com.fs.starfarer.api.loading.Description; 061import com.fs.starfarer.api.loading.Description.Type; 062import com.fs.starfarer.api.loading.FighterWingSpecAPI; 063import com.fs.starfarer.api.loading.HullModSpecAPI; 064import com.fs.starfarer.api.loading.IndustrySpecAPI; 065import com.fs.starfarer.api.loading.WeaponSlotAPI; 066import com.fs.starfarer.api.loading.WeaponSpecAPI; 067import com.fs.starfarer.api.loading.WingRole; 068import com.fs.starfarer.api.loading.WithSourceMod; 069import com.fs.starfarer.api.ui.CustomPanelAPI; 070import com.fs.starfarer.api.ui.TagDisplayAPI; 071import com.fs.starfarer.api.ui.TooltipMakerAPI; 072import com.fs.starfarer.api.ui.UIPanelAPI; 073import com.fs.starfarer.api.util.CountingMap; 074import com.fs.starfarer.api.util.ListMap; 075import com.fs.starfarer.api.util.Misc; 076 077public class CodexDataV2 { 078 079 public static boolean WITH_GAME_MECHANICS_CAT = false; 080 public static boolean WITH_CUSTOM_EXAMPLE_CAT = false; 081 082 public static boolean USE_KEY_NAMES_FOR_GALLERY = false; 083 084 public static CodexEntryPlugin ROOT; 085 public static Map<String, CodexEntryPlugin> ENTRIES = new LinkedHashMap<>(); 086 public static Map<String, CodexEntryPlugin> SEEN_STATION_MODULES = new LinkedHashMap<>(); 087 088 public static String TAG_EMPTY_MODULE = "empty_module"; 089 090 public static String CAT_ROOT = "root"; 091 public static String CAT_SHIPS = "ships"; 092 public static String CAT_STATIONS = "stations"; 093 public static String CAT_FIGHTERS = "fighters"; 094 public static String CAT_WEAPONS = "weapons"; 095 public static String CAT_HULLMODS = "hullmods"; 096 public static String CAT_SHIP_SYSTEMS = "ship_systems"; 097 public static String CAT_SPECIAL_ITEMS = "special_items"; 098 public static String CAT_INDUSTRIES = "industries"; 099 public static String CAT_STARS_AND_PLANETS = "stars_and_planets"; 100 public static String CAT_PLANETARY_CONDITIONS = "conditions"; 101 public static String CAT_COMMODITIES = "commodities"; 102 public static String CAT_GALLERY = "gallery"; 103 public static String CAT_SKILLS = "skills"; 104 public static String CAT_ABILITIES = "abilities"; 105 //public static String CAT_TERRAIN = "terrain"; 106 //public static String CAT_FACTIONS = "factions"; 107 public static String CAT_GAME_MECHANICS = "game_mechanics"; 108 public static String CAT_CUSTOM_EXAMPLE = "custom_example"; 109 110 public static String UNKNOWN_ENTRY_ID = "unknown_entry"; 111 112 113 public static Map<String, Float> CAT_SORT_RELATED_ENTRIES = new HashMap<>(); 114 static { 115 //CAT_SORT_RELATED_ENTRIES.put(CAT_FACTIONS, -10f); 116 //CAT_SORT_RELATED_ENTRIES.put(CAT_FACTIONS, 500f); 117 CAT_SORT_RELATED_ENTRIES.put(CAT_SHIP_SYSTEMS, 0f); 118 CAT_SORT_RELATED_ENTRIES.put(CAT_HULLMODS, 10f); 119 CAT_SORT_RELATED_ENTRIES.put(CAT_WEAPONS, 20f); 120 CAT_SORT_RELATED_ENTRIES.put(CAT_FIGHTERS, 30f); 121 CAT_SORT_RELATED_ENTRIES.put(CAT_SHIPS, 40f); 122 CAT_SORT_RELATED_ENTRIES.put(CAT_STATIONS, 45f); 123 124 CAT_SORT_RELATED_ENTRIES.put(CAT_SKILLS, 50f); 125 CAT_SORT_RELATED_ENTRIES.put(CAT_ABILITIES, 60f); 126 127 // TODO: rearrange these as needed depending on where they show up as related 128 CAT_SORT_RELATED_ENTRIES.put(CAT_INDUSTRIES, 100f); 129 CAT_SORT_RELATED_ENTRIES.put(CAT_SPECIAL_ITEMS, 110f); 130 CAT_SORT_RELATED_ENTRIES.put(CAT_STARS_AND_PLANETS, 120f); 131 CAT_SORT_RELATED_ENTRIES.put(CAT_COMMODITIES, 130f); 132 CAT_SORT_RELATED_ENTRIES.put(CAT_PLANETARY_CONDITIONS, 140f); 133 //CAT_SORT_RELATED_ENTRIES.put(CAT_TERRAIN, 145f); 134 CAT_SORT_RELATED_ENTRIES.put(CAT_GAME_MECHANICS, 150f); 135 CAT_SORT_RELATED_ENTRIES.put(CAT_GALLERY, 160f); 136 137 } 138 139 140 public static String ALL_TECHS = "All designs"; 141 public static String ALL_APTITUDES = "All aptitudes"; 142 public static String ALL_SIZES = "All sizes"; 143 public static String ALL_TYPES = "All types"; 144 public static String ALL_DAMAGE_TYPES = "All damage"; 145 public static String HIGH_EXPLOSIVE = "High explosive"; 146 public static String KINETIC = "Kinetic"; 147 public static String DAM_ENERGY = "_Energy"; // leadiner _ gets removed from tag name because HACKS 148 public static String FRAGMENTATION = "Fragmentation"; 149 //public static String OTHER = "Other"; 150 151 public static String FRIGATES = "Frigates"; 152 public static String DESTROYERS = "Destroyers"; 153 public static String CRUISERS = "Cruisers"; 154 public static String CAPITALS = "Capitals"; 155 156 public static String COMBAT_SHIPS = "Warships"; 157 public static String PHASE_SHIPS = "Phase ships"; 158 public static String CARRIERS = "Carriers"; 159 public static String CIVILIAN = "Civilian"; 160 161 public static String SMALL = "Small"; 162 public static String MEDIUM = "Medium"; 163 public static String LARGE = "Large"; 164 public static String FIGHTER_WEAPON = "Fighter"; 165 166 public static String BALLISTIC = "Ballistic"; 167 public static String MISSILE = "Missile"; 168 public static String ENERGY = "Energy"; 169 public static String HYBRID = "Hybrid"; 170 public static String COMPOSITE = "Composite"; 171 public static String SYNERGY = "Synergy"; 172 public static String UNIVERSAL = "Universal"; 173 public static String BEAM = "Beam"; 174 175 public static String FIGHTER = "Fighters"; 176 public static String BOMBER = "Bombers"; 177 public static String INTERCEPTOR = "Interceptors"; 178 public static String OTHER = "Other"; 179 180 public static String DMODS = "D-mods"; 181 public static String INTRINSIC = "Intrinsic"; 182 183 public static String PLANETS = "Planets"; 184 public static String STARS = "Stars"; 185 public static String GAS_GIANTS = "Gas giants"; 186 public static String HABITABLE = "Habitable"; 187 188 public static String COLONY = "Colony items"; 189 public static String AI_CORE = "AI cores"; 190 public static String BLUEPRINTS = "Blueprint packages"; 191 192 public static String INDUSTRIES = "Industries"; 193 public static String STRUCTURES = "Structures"; 194 public static String STATIONS = "Stations"; 195 196 public static String RESOURCES = "Resources"; 197 198 public static String PILOTED_SHIP = "Piloted ship"; 199 200 201 public static void init() { 202 ROOT = new CodexEntryV2("root", "Codex categories", null); 203 ROOT.setRetainOrderOfChildren(true); 204 205 for (ModPlugin plugin : Global.getSettings().getModManager().getEnabledModPlugins()) { 206 plugin.onAboutToStartGeneratingCodex(); 207 } 208 209 CodexEntryPlugin ships = createShipsCategory(); 210 CodexEntryPlugin stations = createStationsCategory(); 211 CodexEntryPlugin fighters = createFightersCategory(); 212 CodexEntryPlugin weapons = createWeaponsCategory(); 213 CodexEntryPlugin hullmods = createHullModsCategory(); 214 CodexEntryPlugin shipSystems = createShipSystemsCategory(); 215 216 CodexEntryPlugin items = createSpecialItemsCategory(); 217 CodexEntryPlugin industries = createIndustriesCategory(); 218 CodexEntryPlugin planets = createStarsAndPlanetsCategory(); 219 CodexEntryPlugin conditions = createPlanetaryConditionsCategory(); 220 CodexEntryPlugin commodities = createCommoditiesCategory(); 221 //CodexEntryPlugin terrain = createTerrainCategory(); 222 //CodexEntryPlugin factions = createFactionsCategory(); 223 CodexEntryPlugin gallery = createGalleryCategory(); 224 225 CodexEntryPlugin skills = createSkillsCategory(); 226 CodexEntryPlugin abilities = createAbilitiesCategory(); 227 228 ROOT.addChild(ships); 229 //ships.addChild(stations); 230 ROOT.addChild(stations); 231 ROOT.addChild(fighters); 232 ROOT.addChild(weapons); 233 ROOT.addChild(hullmods); 234 ROOT.addChild(shipSystems); 235 ROOT.addChild(items); 236 ROOT.addChild(industries); 237 ROOT.addChild(commodities); 238 ROOT.addChild(planets); 239 ROOT.addChild(conditions); 240 //ROOT.addChild(terrain); 241 ROOT.addChild(skills); 242 ROOT.addChild(abilities); 243 //ROOT.addChild(factions); 244 ROOT.addChild(gallery); 245 246 if (WITH_GAME_MECHANICS_CAT) { 247 CodexEntryPlugin mechanics = createGameMechanicsCategory(); 248 ROOT.addChild(mechanics); 249 } 250 251 rebuildIdToEntryMap(); 252 253 setCatSort(ROOT, null); 254 255 populateShipsAndStations(ships, stations); 256 //populateStations(ships); 257 populateShipSystems(shipSystems, ships); 258 populateHullMods(hullmods); 259 populateWeapons(weapons); 260 populateFighters(fighters); 261 populateStarsAndPlanets(planets); 262 populatePlanetaryConditions(conditions); 263 populateSpecialItems(items); 264 populateCommodities(commodities, items); 265 populateIndustries(industries); 266 populateSkills(skills); 267 populateAbilities(abilities); 268 //populateTerrain(terrain); 269 //populateFactions(factions); 270 populateGallery(gallery); 271 addUnknownEntry(); 272 273 if (WITH_GAME_MECHANICS_CAT) { 274 CodexTextEntryLoader.loadTextEntries(); 275 } 276 277 rebuildIdToEntryMap(); 278 279 for (ModPlugin plugin : Global.getSettings().getModManager().getEnabledModPlugins()) { 280 plugin.onAboutToLinkCodexEntries(); 281 } 282 283 sortSkillsCategory(); 284 285 linkRelatedEntries(); 286 if (WITH_GAME_MECHANICS_CAT) { 287 CodexTextEntryLoader.linkRelated(); 288 } 289 290 if (WITH_CUSTOM_EXAMPLE_CAT) { 291 addCustomCodexEntryDetailPanelExample(); 292 } 293 294 for (ModPlugin plugin : Global.getSettings().getModManager().getEnabledModPlugins()) { 295 plugin.onCodexDataGenerated(); 296 } 297 } 298 299 public static void addUnknownEntry() { 300 ROOT.addChild(new CodexEntryV2(UNKNOWN_ENTRY_ID, 301 "UNKNOWN ENTRY", null, UNKNOWN_ENTRY_ID) { 302 @Override 303 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 304 info.addPara("UNKNOWN ENTRY", Misc.getBasePlayerColor(), 0f); 305 } 306 @Override 307 public boolean isVisible() { 308 return false; 309 } 310 @Override 311 public boolean isLocked() { 312 return false; 313 } 314 @Override 315 public boolean hasCustomDetailPanel() { 316 return true; 317 } 318 @Override 319 public void createCustomDetail(CustomPanelAPI panel, UIPanelAPI relatedEntries, CodexDialogAPI codex) { 320 float opad = 10f; 321 float width = panel.getPosition().getWidth(); 322 float horzBoxPad = 30f; 323 // the right width for a tooltip wrapped in a box to fit next to relatedEntries 324 float tw = width - 290f - opad - horzBoxPad + 10f; 325 326 TooltipMakerAPI text = panel.createUIElement(tw, 0, false); 327 text.setParaSmallInsignia(); 328 text.addPara("UNKNOWN ENTRY\n\nPlease register your Tri-Tachyon datapad to receive the latest news and updates.", 0f); 329 panel.updateUIElementSizeAndMakeItProcessInput(text); 330 UIPanelAPI box = panel.wrapTooltipWithBox(text); 331 panel.addComponent(box).inTL(0f, 0f); 332 if (relatedEntries != null) { 333 panel.addComponent(relatedEntries).inTR(0f, 0f); 334 } 335 336 float height = box.getPosition().getHeight(); 337 if (relatedEntries != null) { 338 height = Math.max(height, relatedEntries.getPosition().getHeight()); 339 } 340 } 341 }); 342 } 343 344 public static void addCustomCodexEntryDetailPanelExample() { 345 if (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode")) { 346 CodexEntryPlugin custom = createCustomExampleCategory(); 347 ROOT.addChild(custom); 348 349 CodexCustomEntryExample entry = new CodexCustomEntryExample("custom_example_1", 350 "Custom detail example", getIcon(CAT_CUSTOM_EXAMPLE)); 351 entry.setParam("Has to be set to something for detail to show; " 352 + "usually this is the data for what the entry is about"); 353 // or, can set it to a fighter wing/hull/weapon spec to get a custom icon 354 entry.setParam(Global.getSettings().getHullSpec("paragon")); 355 custom.addChild(entry); 356 entry.addRelatedEntry(getEntry(getHullmodEntryId(HullMods.ADVANCED_TARGETING_CORE))); 357 entry.addRelatedEntry(getEntry(getShipEntryId("onslaught"))); 358 entry.addRelatedEntry(getEntry(getWeaponEntryId("autopulse"))); 359 entry.addRelatedEntry(getEntry(getFighterEntryId("wasp_wing"))); 360 } 361 } 362 363 public static void setCatSort(CodexEntryPlugin root, Set<CodexEntryPlugin> seen) { 364 if (seen == null) seen = new LinkedHashSet<>(); 365 if (seen.contains(root)) return; 366 seen.add(root); 367 for (CodexEntryPlugin cat : root.getChildren()) { 368 if (!cat.isCategory()) continue; 369 Float sort = CAT_SORT_RELATED_ENTRIES.get(cat.getId()); 370 if (sort == null) sort = 1000f; 371 cat.setCategorySortTierForRelatedEntries(sort); 372 setCatSort(cat, seen); 373 } 374 } 375 376 public static CodexEntryV2 createHullModsCategory() { 377 CodexEntryV2 hullmods = new CodexEntryV2(CAT_HULLMODS, "Hullmods", getIcon(CAT_HULLMODS)) { 378 @Override 379 public boolean hasTagDisplay() { 380 return true; 381 } 382 @Override 383 public void configureTagDisplay(TagDisplayAPI tags) { 384 CountingMap<String> techs = new CountingMap<String>(); 385 CountingMap<String> types = new CountingMap<String>(); 386 int dmods = 0; 387 int intrinsic = 0; 388 int total = 0; 389 390 for (CodexEntryPlugin curr : getChildren()) { 391 if (!(curr.getParam() instanceof HullModSpecAPI)) continue; 392 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 393 HullModSpecAPI spec = (HullModSpecAPI) curr.getParam(); 394 395 if (spec.hasTag(Tags.HULLMOD_DMOD)) { 396 dmods++; 397 } else if (spec.isHidden()) { 398 intrinsic++; 399 } 400 401 techs.add(spec.getManufacturer()); 402 for (String type : spec.getUITags()) { 403 types.add(type); 404 } 405 total++; 406 } 407 408 if (dmods > 0) types.add(DMODS, dmods); 409 if (intrinsic > 0) types.add(INTRINSIC, intrinsic); 410 411 float opad = 10f; 412 float pad = 3f; 413 414 tags.beginGroup(false, ALL_TECHS); 415 List<String> keys = new ArrayList<String>(techs.keySet()); 416 Collections.sort(keys); 417 for (String tech : keys) { 418 tags.addTag(tech, tech, techs.getCount(tech)); 419 } 420 tags.setTotalOverrideForCurrentGroup(total); 421 tags.addGroup(0f); 422 423 424 tags.beginGroup(false, ALL_TYPES); 425 keys = new ArrayList<String>(types.keySet()); 426 Collections.sort(keys); 427 for (String type : keys) { 428 tags.addTag(type, type, types.getCount(type)); 429 } 430 tags.setTotalOverrideForCurrentGroup(total); 431 tags.addGroup(opad * 1f); 432 433 tags.checkAll(); 434 } 435 436 }; 437 return hullmods; 438 } 439 440 public static CodexEntryV2 createGameMechanicsCategory() { 441 CodexEntryV2 cat = new CodexEntryV2(CAT_GAME_MECHANICS, "Spacer's Manual", getIcon(CAT_GAME_MECHANICS)) { 442 @Override 443 public boolean hasCustomDetailPanel() { 444 return true; 445 } 446 447 @Override 448 public boolean hasDetail() { 449 return true; 450 } 451 452 @Override 453 public void createCustomDetail(CustomPanelAPI panel, UIPanelAPI relatedEntries, CodexDialogAPI codex) { 454 Color color = Misc.getBasePlayerColor(); 455 Color dark = Misc.getDarkPlayerColor(); 456 Color h = Misc.getHighlightColor(); 457 Color g = Misc.getGrayColor(); 458 float opad = 10f; 459 float pad = 3f; 460 float small = 5f; 461 462 float width = panel.getPosition().getWidth(); 463 464 float initPad = 0f; 465 466 float horzBoxPad = 30f; 467 468 // the right width for a tooltip wrapped in a box to fit next to relatedEntries 469 // 290 is the width of the related entries widget, but it may be null 470 float tw = width - 290f - opad - horzBoxPad + 10f; 471 472 TooltipMakerAPI text = panel.createUIElement(tw, 0, false); 473 text.setParaSmallInsignia(); 474 475 text.addPara("Spacer's manual description test.", 0f); 476 477 panel.updateUIElementSizeAndMakeItProcessInput(text); 478 479 UIPanelAPI box = panel.wrapTooltipWithBox(text); 480 panel.addComponent(box).inTL(0f, 0f); 481 if (relatedEntries != null) { 482 panel.addComponent(relatedEntries).inTR(0f, 0f); 483 } 484 485 float height = box.getPosition().getHeight(); 486 if (relatedEntries != null) { 487 height = Math.max(height, relatedEntries.getPosition().getHeight()); 488 } 489 panel.getPosition().setSize(width, height); 490 } 491 }; 492 493 return cat; 494 } 495 public static CodexEntryV2 createGalleryCategory() { 496 CodexEntryV2 cat = new CodexEntryV2(CAT_GALLERY, "Gallery", getIcon(CAT_GALLERY)); 497 return cat; 498 } 499 public static CodexEntryV2 createCommoditiesCategory() { 500 CodexEntryV2 cat = new CodexEntryV2(CAT_COMMODITIES, "Commodities", getIcon(CAT_COMMODITIES)) { 501 502 }; 503 return cat; 504 } 505 506// public static CodexEntryV2 createTerrainCategory() { 507// CodexEntryV2 cat = new CodexEntryV2(CAT_TERRAIN, "Terrain", getIcon(CAT_TERRAIN)) { 508// 509// }; 510// return cat; 511// } 512 513 public static CodexEntryV2 createIndustriesCategory() { 514 CodexEntryV2 cat = new CodexEntryV2(CAT_INDUSTRIES, "Industries", getIcon(CAT_INDUSTRIES)) { 515 @Override 516 public boolean hasTagDisplay() { 517 return true; 518 } 519 @Override 520 public void configureTagDisplay(TagDisplayAPI tags) { 521 int industry = 0; 522 int structure = 0; 523 int station = 0; 524 int other = 0; 525 int total = 0; 526 for (CodexEntryPlugin curr : getChildren()) { 527 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 528 if (!(curr.getParam() instanceof IndustrySpecAPI)) continue; 529 530 IndustrySpecAPI spec = (IndustrySpecAPI) curr.getParam(); 531 if (spec.hasTag(Industries.TAG_INDUSTRY)) industry++; 532 else if (spec.hasTag(Industries.TAG_STATION)) station++; 533 else if (spec.hasTag(Industries.TAG_STRUCTURE)) structure++; 534 else other++; 535 536 total++; 537 } 538 tags.beginGroup(false, ALL_TYPES); 539 tags.addTag(INDUSTRIES, industry); 540 tags.addTag(STRUCTURES, structure); 541 if (station > 0) tags.addTag(STATIONS, station); 542 if (other > 0) tags.addTag(OTHER, other); 543 tags.setTotalOverrideForCurrentGroup(total); 544 tags.addGroup(0f); 545 546 tags.checkAll(); 547 } 548 }; 549 return cat; 550 } 551 public static CodexEntryV2 createSpecialItemsCategory() { 552 CodexEntryV2 cat = new CodexEntryV2(CAT_SPECIAL_ITEMS, "Special items", getIcon(CAT_SPECIAL_ITEMS)) { 553 @Override 554 public boolean hasTagDisplay() { 555 return true; 556 } 557 @Override 558 public void configureTagDisplay(TagDisplayAPI tags) { 559 int colony = 0; 560 int core = 0; 561 int bp = 0; 562 int other = 0; 563 int total = 0; 564 for (CodexEntryPlugin curr : getChildren()) { 565 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 566 if (curr.getParam() instanceof SpecialItemSpecAPI) { 567 SpecialItemSpecAPI spec = (SpecialItemSpecAPI) curr.getParam(); 568 if (spec.hasTag(Items.TAG_COLONY_ITEM)) colony++; 569 else if (spec.hasTag(Items.TAG_BLUEPRINT_PACKAGE)) bp++; 570 else other++; 571 } else if (curr.getParam() instanceof CommoditySpecAPI) { 572 CommoditySpecAPI spec = (CommoditySpecAPI) curr.getParam(); 573 if (spec.hasTag(Commodities.TAG_AI_CORE)) core++; 574 else other++; 575 } 576 total++; 577 } 578 tags.beginGroup(false, ALL_TYPES); 579 if (colony > 0) tags.addTag(COLONY, colony); 580 if (bp > 0) tags.addTag(BLUEPRINTS, bp); 581 if (core > 0) tags.addTag(AI_CORE, core); 582 if (other > 0) tags.addTag(OTHER, other); 583 tags.setTotalOverrideForCurrentGroup(total); 584 tags.addGroup(0f); 585 586 tags.checkAll(); 587 } 588 }; 589 return cat; 590 } 591 public static CodexEntryV2 createPlanetaryConditionsCategory() { 592 CodexEntryV2 cat = new CodexEntryV2(CAT_PLANETARY_CONDITIONS, "Planetary conditions", getIcon(CAT_PLANETARY_CONDITIONS)) { 593 @Override 594 public boolean hasTagDisplay() { 595 return true; 596 } 597 @Override 598 public void configureTagDisplay(TagDisplayAPI tags) { 599 int resource = 0; 600 int other = 0; 601 int total = 0; 602 for (CodexEntryPlugin curr : getChildren()) { 603 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 604 if (!(curr.getParam() instanceof MarketConditionSpecAPI)) continue; 605 MarketConditionSpecAPI spec = (MarketConditionSpecAPI) curr.getParam(); 606 if (ResourceDepositsCondition.COMMODITY.containsKey(spec.getId())) resource++; 607 else other++; 608 609 total++; 610 } 611 tags.beginGroup(false, ALL_TYPES); 612 tags.addTag(RESOURCES, resource); 613 tags.addTag(OTHER, other); 614 tags.setTotalOverrideForCurrentGroup(total); 615 tags.addGroup(0f); 616 617 tags.checkAll(); 618 } 619 }; 620 return cat; 621 } 622 623 public static CodexEntryV2 createStarsAndPlanetsCategory() { 624 CodexEntryV2 cat = new CodexEntryV2(CAT_STARS_AND_PLANETS, "Stars & planets", getIcon(CAT_STARS_AND_PLANETS)) { 625 @Override 626 public boolean hasTagDisplay() { 627 return true; 628 } 629 @Override 630 public void configureTagDisplay(TagDisplayAPI tags) { 631 int gasGiants = 0; 632 int planets = 0; 633 int stars = 0; 634 int habitable = 0; 635 int total = 0; 636 637 for (CodexEntryPlugin curr : getChildren()) { 638 if (!(curr.getParam() instanceof PlanetSpecAPI)) continue; 639 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 640 641 PlanetSpecAPI spec = (PlanetSpecAPI) curr.getParam(); 642 if (Misc.canPlanetTypeRollHabitable(spec)) { 643 habitable++; 644 } 645 if (spec.isGasGiant()) { 646 gasGiants++; 647 } else if (spec.isStar()) { 648 stars++; 649 } else { 650 planets++; 651 } 652 653 total++; 654 } 655 656 float opad = 10f; 657 float pad = 3f; 658 659 tags.beginGroup(false, ALL_TYPES, 120f); 660 tags.addTag(STARS, stars); 661 tags.addTag(GAS_GIANTS, gasGiants); 662 tags.addTag(PLANETS, planets); 663 tags.addTag(HABITABLE, habitable); 664 tags.setTotalOverrideForCurrentGroup(total); 665 tags.addGroup(opad); 666 667 tags.checkAll(); 668 } 669 }; 670 return cat; 671 } 672 673 public static CodexEntryV2 createShipSystemsCategory() { 674 CodexEntryV2 shipSystems = new CodexEntryV2(CAT_SHIP_SYSTEMS, "Ship systems", getIcon(CAT_SHIP_SYSTEMS)) { 675 @Override 676 public boolean hasTagDisplay() { 677 return true; 678 } 679 @Override 680 public void configureTagDisplay(TagDisplayAPI tags) { 681 int total = 0; 682 CountingMap<String> types = new CountingMap<>(); 683 for (CodexEntryPlugin curr : getChildren()) { 684 if (!(curr.getParam() instanceof ShipSystemSpecAPI)) continue; 685 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 686 687 ShipSystemSpecAPI spec = (ShipSystemSpecAPI) curr.getParam(); 688 689 Description desc = Global.getSettings().getDescription(spec.getId(), Type.SHIP_SYSTEM); 690 if (desc.hasText2()) { 691 String typeStr = desc.getText2(); 692 types.add(typeStr); 693 } else { 694 types.add("Special"); 695 } 696 697 total++; 698 } 699 700 tags.beginGroup(false, ALL_TYPES); 701 List<String> keys = new ArrayList<String>(types.keySet()); 702 Collections.sort(keys); 703 for (String type : keys) { 704 tags.addTag(type, type, types.getCount(type)); 705 } 706 tags.setTotalOverrideForCurrentGroup(total); 707 tags.addGroup(0f); 708 709 tags.checkAll(); 710 } 711 }; 712 return shipSystems; 713 } 714 715 public static CodexEntryV2 createFightersCategory() { 716 CodexEntryV2 fighters = new CodexEntryV2(CAT_FIGHTERS, "Fighters", getIcon(CAT_FIGHTERS)) { 717 @Override 718 public boolean hasTagDisplay() { 719 return true; 720 } 721 @Override 722 public void configureTagDisplay(TagDisplayAPI tags) { 723 int fighter = 0; 724 int bomber = 0; 725 int interceptor = 0; 726 int other = 0; 727 int total = 0; 728 729 CountingMap<String> techs = new CountingMap<String>(); 730 for (CodexEntryPlugin curr : getChildren()) { 731 if (!(curr.getParam() instanceof FighterWingSpecAPI)) continue; 732 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 733 734 FighterWingSpecAPI spec = (FighterWingSpecAPI) curr.getParam(); 735 techs.add(spec.getVariant().getHullSpec().getManufacturer()); 736 if (spec.getRole() == WingRole.FIGHTER) fighter++; 737 else if (spec.getRole() == WingRole.BOMBER) bomber++; 738 else if (spec.getRole() == WingRole.INTERCEPTOR) interceptor++; 739 else other++; 740 741 total++; 742 } 743 744 float opad = 10f; 745 float pad = 3f; 746 747 if (!techs.isEmpty()) { 748 tags.beginGroup(false, ALL_TECHS); 749 List<String> keys = new ArrayList<String>(techs.keySet()); 750 Collections.sort(keys); 751 for (String tech : keys) { 752 tags.addTag(tech, tech, techs.getCount(tech)); 753 } 754 tags.setTotalOverrideForCurrentGroup(total); 755 tags.addGroup(0f); 756 } 757 758 tags.beginGroup(false, ALL_TYPES, 120f); 759 tags.addTag(FIGHTER, fighter); 760 tags.addTag(BOMBER, bomber); 761 tags.addTag(INTERCEPTOR, interceptor); 762 tags.addTag(OTHER, other); 763 tags.setTotalOverrideForCurrentGroup(total); 764 tags.addGroup(opad); 765 766 tags.checkAll(); 767 } 768 }; 769 return fighters; 770 } 771 772 public static CodexEntryV2 createCustomExampleCategory() { 773 CodexEntryV2 custom = new CodexEntryV2(CAT_CUSTOM_EXAMPLE, "Custom example", getIcon(CAT_CUSTOM_EXAMPLE)); 774 return custom; 775 } 776 777 public static CodexEntryV2 createWeaponsCategory() { 778 CodexEntryV2 weapons = new CodexEntryV2(CAT_WEAPONS, "Weapons", getIcon(CAT_WEAPONS)) { 779 @Override 780 public boolean hasTagDisplay() { 781 return true; 782 } 783 @Override 784 public void configureTagDisplay(TagDisplayAPI tags) { 785 int small = 0; 786 int medium = 0; 787 int large = 0; 788 int fighter = 0; 789 int ballistic = 0; 790 int missile = 0; 791 int energy = 0; 792 int hybrid = 0; 793 int composite = 0; 794 int synergy = 0; 795 int universal = 0; 796 int beam = 0; 797 int damHE = 0; 798 int damKinetic = 0; 799 int damEnergy = 0; 800 int damFrag = 0; 801 int damOther = 0; 802 int total = 0; 803 804 CountingMap<String> techs = new CountingMap<String>(); 805 for (CodexEntryPlugin curr : getChildren()) { 806 if (!(curr.getParam() instanceof WeaponSpecAPI)) continue; 807 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 808 809 WeaponSpecAPI spec = (WeaponSpecAPI) curr.getParam(); 810 techs.add(spec.getManufacturer()); 811 812 if (spec.getAIHints().contains(AIHints.SYSTEM) && 813 spec.getPrimaryRoleStr() != null && 814 spec.getPrimaryRoleStr().endsWith("(Fighter)")) { 815 fighter++; 816 } else { 817 if (spec.getSize() == WeaponSize.SMALL) small++; 818 else if (spec.getSize() == WeaponSize.MEDIUM) medium++; 819 else if (spec.getSize() == WeaponSize.LARGE) large++; 820 } 821 822 if (spec.getType() == WeaponType.BALLISTIC) ballistic++; 823 else if (spec.getType() == WeaponType.MISSILE) missile++; 824 else if (spec.getType() == WeaponType.ENERGY) energy++; 825 826 if (spec.getMountType() == WeaponType.HYBRID) hybrid++; 827 else if (spec.getMountType() == WeaponType.COMPOSITE) composite++; 828 else if (spec.getMountType() == WeaponType.SYNERGY) synergy++; 829 else if (spec.getMountType() == WeaponType.UNIVERSAL) universal++; 830 831 if (spec.getDamageType() == DamageType.HIGH_EXPLOSIVE) damHE++; 832 else if (spec.getDamageType() == DamageType.KINETIC) damKinetic++; 833 else if (spec.getDamageType() == DamageType.ENERGY) damEnergy++; 834 else if (spec.getDamageType() == DamageType.FRAGMENTATION) damFrag++; 835 else if (spec.getDamageType() == DamageType.OTHER) damOther++; 836 837 if (spec.isBeam()) beam++; 838 839 total++; 840 } 841 842 float opad = 10f; 843 float pad = 3f; 844 845 if (!techs.isEmpty()) { 846 tags.beginGroup(false, ALL_TECHS); 847 List<String> keys = new ArrayList<String>(techs.keySet()); 848 Collections.sort(keys); 849 for (String tech : keys) { 850 tags.addTag(tech, tech, techs.getCount(tech)); 851 } 852 tags.setTotalOverrideForCurrentGroup(total); 853 tags.addGroup(0f); 854 } 855 856 tags.beginGroup(false, ALL_SIZES, 120f); 857 tags.addTag(SMALL, small); 858 tags.addTag(MEDIUM, medium); 859 tags.addTag(LARGE, large); 860 if (fighter > 0) tags.addTag(FIGHTER_WEAPON, fighter); 861 tags.setTotalOverrideForCurrentGroup(total); 862 tags.addGroup(opad * 1f); 863 864 tags.beginGroup(false, ALL_TYPES, 120f); 865 tags.addTag(BALLISTIC, ballistic); 866 tags.addTag(MISSILE, missile); 867 tags.addTag(ENERGY, energy); 868 if (hybrid > 0) tags.addTag(HYBRID, hybrid); 869 if (composite > 0) tags.addTag(COMPOSITE, composite); 870 if (synergy > 0) tags.addTag(SYNERGY, synergy); 871 if (universal > 0) tags.addTag(UNIVERSAL, universal); 872 if (beam > 0) tags.addTag(BEAM, beam); 873 tags.setTotalOverrideForCurrentGroup(total); 874 tags.addGroup(pad); 875 876 tags.beginGroup(false, ALL_DAMAGE_TYPES, 140f); 877 tags.addTag(HIGH_EXPLOSIVE, damHE); 878 tags.addTag(KINETIC, damKinetic); 879 tags.addTag(FRAGMENTATION, damFrag); 880 tags.addTag(DAM_ENERGY, damEnergy); 881 if (damOther > 0) tags.addTag(OTHER, damOther); 882 tags.setTotalOverrideForCurrentGroup(total); 883 tags.addGroup(pad); 884 885 tags.checkAll(); 886 } 887 888 }; 889 return weapons; 890 } 891 892 public static CodexEntryV2 createSkillsCategory() { 893 CodexEntryV2 skills = new CodexEntryV2(CAT_SKILLS, "Skills", getIcon(CAT_SKILLS)) { 894 @Override 895 public boolean hasTagDisplay() { 896 return true; 897 } 898 @Override 899 public void configureTagDisplay(TagDisplayAPI tags) { 900 int personal = 0; 901 int other = 0; 902 int total = 0; 903 904 CountingMap<String> apts = new CountingMap<String>(); 905 for (CodexEntryPlugin curr : getChildren()) { 906 if (!(curr.getParam() instanceof SkillSpecAPI)) continue; 907 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 908 909 SkillSpecAPI spec = (SkillSpecAPI) curr.getParam(); 910 String apt = getAptitudeName(spec); 911 if (!apt.isEmpty()) apts.add(apt); 912 913 if (spec.isElite()) personal++; 914 else other++; 915 916 total++; 917 } 918 919 float opad = 10f; 920 float pad = 3f; 921 922 if (!apts.isEmpty()) { 923 tags.beginGroup(false, ALL_APTITUDES); 924 List<String> keys = new ArrayList<String>(apts.keySet()); 925 Collections.sort(keys); 926 for (String tech : keys) { 927 tags.addTag(tech, tech, apts.getCount(tech)); 928 } 929 tags.addGroup(0f); 930 } 931 932 tags.beginGroup(false, ALL_TYPES, 120f); 933 tags.addTag(PILOTED_SHIP, personal); 934 tags.addTag(OTHER, other); 935 tags.setTotalOverrideForCurrentGroup(total); 936 tags.addGroup(opad); 937 938 tags.checkAll(); 939 } 940 }; 941 skills.setRetainOrderOfChildren(true); 942 return skills; 943 } 944 945 public static CodexEntryV2 createAbilitiesCategory() { 946 CodexEntryV2 abilities = new CodexEntryV2(CAT_ABILITIES, "Abilities", getIcon(CAT_ABILITIES)); 947 return abilities; 948 } 949 950 public static CodexEntryV2 createShipsCategory() { 951 CodexEntryV2 ships = new CodexEntryV2(CAT_SHIPS, "Ships", getIcon(CAT_SHIPS)) { 952 @Override 953 public boolean hasTagDisplay() { 954 return true; 955 } 956 @Override 957 public void configureTagDisplay(TagDisplayAPI tags) { 958 int carrier = 0; 959 int civilian = 0; 960 int phase = 0; 961 int combat = 0; 962 int frigate = 0; 963 int destroyer = 0; 964 int cruiser = 0; 965 int capital = 0; 966 int stations = 0; 967 968 int total = 0; 969 CountingMap<String> counts = new CountingMap<String>(); 970 //for (UITableRow o : shipTable.getRows()) { 971 for (CodexEntryPlugin curr : getChildren()) { 972 if (!(curr.getParam() instanceof ShipHullSpecAPI)) continue; 973 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 974 975 boolean station = false; 976 //boolean stationModule = false; 977 if (curr.getParam2() instanceof FleetMemberAPI) { 978 FleetMemberAPI fm = (FleetMemberAPI) curr.getParam2(); 979 station = fm.isStation(); 980 } 981 982 ShipHullSpecAPI spec = (ShipHullSpecAPI) curr.getParam(); 983 boolean isPhase = spec.isPhase() || spec.getHints().contains(ShipTypeHints.PHASE); 984 985 counts.add(spec.getManufacturer()); 986 if (spec.isCarrier()) carrier++; 987 else if (spec.isCivilianNonCarrier()) civilian++; 988 else if (isPhase) phase++; 989 else if (!station) combat++; 990 991 if (spec.isCivilianNonCarrier() && isPhase) phase++; 992 993 if (station) stations++; 994 else if (spec.getHullSize() == HullSize.FRIGATE) frigate++; 995 else if (spec.getHullSize() == HullSize.DESTROYER) destroyer++; 996 else if (spec.getHullSize() == HullSize.CRUISER) cruiser++; 997 else if (spec.getHullSize() == HullSize.CAPITAL_SHIP) capital++; 998 999 total++; 1000 } 1001 1002 float opad = 10f; 1003 float pad = 3f; 1004 1005 if (!counts.isEmpty()) { 1006 tags.beginGroup(false, ALL_TECHS); 1007 List<String> keys = new ArrayList<String>(counts.keySet()); 1008 Collections.sort(keys); 1009 for (String tech : keys) { 1010 tags.addTag(tech, tech, counts.getCount(tech)); 1011 } 1012 tags.addGroup(0f); 1013 } 1014 1015 tags.beginGroup(false, ALL_SIZES, 120f); 1016 //tags.beginGroup(true, null, 110f); 1017 tags.addTag(FRIGATES, frigate); 1018 tags.addTag(DESTROYERS, destroyer); 1019 tags.addTag(CRUISERS, cruiser); 1020 tags.addTag(CAPITALS, capital); 1021 if (stations > 0) tags.addTag(STATIONS, stations); 1022 tags.setTotalOverrideForCurrentGroup(total); 1023 tags.addGroup(opad * 1f); 1024 1025 tags.beginGroup(false, ALL_TYPES, 120f); 1026 //tags.beginGroup(true, null, 110f); 1027 tags.addTag(COMBAT_SHIPS, combat); 1028 tags.addTag(PHASE_SHIPS , phase); 1029 tags.addTag(CARRIERS, carrier); 1030 tags.addTag(CIVILIAN, civilian); 1031 tags.setTotalOverrideForCurrentGroup(total); 1032 tags.addGroup(pad); 1033 1034 tags.setGroupChecked(0, true); 1035 tags.setGroupChecked(1, true); 1036 tags.setGroupChecked(2, true); 1037 } 1038 1039 }; 1040 return ships; 1041 } 1042 1043 public static CodexEntryV2 createStationsCategory() { 1044 CodexEntryV2 stations = new CodexEntryV2(CAT_STATIONS, "Stations", getIcon(CAT_STATIONS)) { 1045 @Override 1046 public boolean hasTagDisplay() { 1047 return true; 1048 } 1049 @Override 1050 public void configureTagDisplay(TagDisplayAPI tags) { 1051 int total = 0; 1052 CountingMap<String> counts = new CountingMap<String>(); 1053 //for (UITableRow o : shipTable.getRows()) { 1054 for (CodexEntryPlugin curr : getChildren()) { 1055 if (!(curr.getParam() instanceof ShipHullSpecAPI)) continue; 1056 if (!curr.isVisible() || curr.isLocked() || curr.skipForTags()) continue; 1057 1058 ShipHullSpecAPI spec = (ShipHullSpecAPI) curr.getParam(); 1059 counts.add(spec.getManufacturer()); 1060 total++; 1061 } 1062 1063 if (!counts.isEmpty()) { 1064 tags.beginGroup(false, ALL_TECHS); 1065 List<String> keys = new ArrayList<String>(counts.keySet()); 1066 Collections.sort(keys); 1067 for (String tech : keys) { 1068 tags.addTag(tech, tech, counts.getCount(tech)); 1069 } 1070 tags.setTotalOverrideForCurrentGroup(total); 1071 tags.addGroup(0f); 1072 } 1073 tags.checkAll(); 1074 } 1075 1076 }; 1077 return stations; 1078 } 1079 1080 public static void populateShipsAndStations(CodexEntryPlugin ships, CodexEntryPlugin stations) { 1081 List<ShipHullSpecAPI> shipSpecs = Global.getSettings().getAllShipHullSpecs(); 1082 for (final ShipHullSpecAPI spec : shipSpecs) { 1083 if (spec.getHullSize() == ShipAPI.HullSize.FIGHTER && !spec.hasTag(Tags.SHOW_IN_CODEX_AS_SHIP)) continue; 1084 if (spec.getHints().contains(ShipTypeHints.HIDE_IN_CODEX)) continue; 1085 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 1086 if (spec.isDefaultDHull()) continue; 1087 if (spec.getHullId().equals("shuttlepod")) continue; 1088 1089 boolean station = spec.getHints().contains(ShipTypeHints.STATION); 1090 String name = spec.getHullNameWithDashClass(); 1091 String designation = ""; 1092 if (spec.hasDesignation() && !spec.getDesignation().equals(spec.getHullName())) { 1093 designation = " " + spec.getDesignation().toLowerCase(); 1094 } 1095 if (station) { 1096 designation = ""; 1097 name = spec.getHullName(); 1098 } 1099 CodexEntryV2 curr = new CodexEntryV2(getShipEntryId(spec.getHullId()), 1100 spec.getHullNameWithDashClass() + designation, null, spec) { 1101 @Override 1102 public String getSortTitle() { 1103 return spec.getHullName(); 1104 } 1105 @Override 1106 public String getSearchString() { 1107 if (station) { 1108 return super.getSearchString() + " Station"; 1109 } 1110 return super.getSearchString(); 1111 } 1112 @Override 1113 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1114 info.addPara(spec.getHullName(), Misc.getBasePlayerColor(), 0f); 1115 if (station) { 1116 if (mode == ListMode.RELATED_ENTRIES) { 1117 info.addPara("Station", Misc.getGrayColor(), 0f); 1118 } 1119 } else if (spec.hasDesignation() && !spec.getDesignation().equals(spec.getHullName())) { 1120 info.addPara(Misc.ucFirst(spec.getDesignation().toLowerCase()), Misc.getGrayColor(), 0f); 1121 } 1122 } 1123 1124 @Override 1125 public boolean matchesTags(Set<String> tags) { 1126 HullSize size = spec.getHullSize(); 1127 1128 if (station) { 1129// String sizeTag = STATIONS; 1130// if (sizeTag != null && !tags.contains(sizeTag)) return false; 1131 1132 String m = spec.getManufacturer(); 1133 if (m != null && !tags.contains(m) && !tags.contains(ALL_TECHS)) return false; 1134 1135// if (!tags.contains(ALL_TYPES)) return false; 1136 1137 return true; 1138 } 1139 1140 String sizeTag = null; 1141 if (size == HullSize.FRIGATE) sizeTag = FRIGATES; 1142 if (size == HullSize.DESTROYER) sizeTag = DESTROYERS; 1143 if (size == HullSize.CRUISER) sizeTag = CRUISERS; 1144 if (size == HullSize.CAPITAL_SHIP) sizeTag = CAPITALS; 1145 1146 if (sizeTag != null && !tags.contains(sizeTag)) return false; 1147 1148 boolean isPhase = spec.isPhase() || spec.getHints().contains(ShipTypeHints.PHASE); 1149 boolean phaseCiv = isPhase && spec.isCivilianNonCarrier(); 1150 if (!phaseCiv && isPhase && !tags.contains(PHASE_SHIPS)) return false; 1151 if (!phaseCiv && spec.isCivilianNonCarrier() && !tags.contains(CIVILIAN)) return false; 1152 if (phaseCiv && !tags.contains(PHASE_SHIPS) && !tags.contains(CIVILIAN)) return false; 1153 1154 if (spec.isCarrier() && !tags.contains(CARRIERS)) return false; 1155 1156 boolean combat = !isPhase && !spec.isCarrier() && !spec.isCivilianNonCarrier(); 1157 if (combat && !tags.contains(COMBAT_SHIPS)) return false; 1158 1159 String m = spec.getManufacturer(); 1160 if (m != null && !tags.contains(m) && !tags.contains(ALL_TECHS)) return false; 1161 1162 return true; 1163 } 1164 1165 @Override 1166 public Set<String> getUnlockRelatedTags() { 1167 return spec.getTags(); 1168 } 1169 1170 @Override 1171 public boolean isUnlockedIfRequiresUnlock() { 1172 return SharedUnlockData.get().isPlayerAwareOfShip(spec.getHullId()); 1173 } 1174 }; 1175 if (station) { 1176 stations.addChild(curr); 1177 } else { 1178 ships.addChild(curr); 1179 } 1180 1181 String variantId = spec.getHullId() + "_Hull"; 1182 if (spec.getCodexVariantId() != null && !spec.getCodexVariantId().isBlank()) { 1183 variantId = spec.getCodexVariantId(); 1184 } 1185 ShipVariantAPI variant = Global.getSettings().getVariant(variantId); 1186 if (variant != null) { 1187 CodexEntryPlugin parent = ships; 1188 if (station) parent = stations; 1189 1190 int relBefore = curr.getRelatedEntries().size(); 1191 List<CodexEntryPlugin> added = addModulesForVariant(variant, true, curr, parent); 1192 // created entries, or they were already created but just added them as related 1193 if (!added.isEmpty() || relBefore < curr.getRelatedEntryIds().size()) { 1194 FleetMemberAPI member = Global.getSettings().createFleetMember(FleetMemberType.SHIP, variant); 1195 curr.setParam2(member); 1196 } 1197 } 1198 } 1199 } 1200 1201// handled by addShipsAndStations now 1202// public static void populateStations(CodexEntryPlugin parent) { 1203// List<IndustrySpecAPI> specs = Global.getSettings().getAllIndustrySpecs(); 1204// for (final IndustrySpecAPI spec : specs) { 1205// if (spec.hasTag(Industries.TAG_PARENT)) continue; 1206// if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 1207// 1208// if (spec.hasTag(Tags.STATION)) { 1209// String variantId = null; 1210// try { 1211// JSONObject json = new JSONObject(spec.getData()); 1212// variantId = json.getString("variant"); 1213// } catch (JSONException e) { 1214// continue; 1215// } 1216// 1217// if (variantId == null || variantId.isBlank()) continue; 1218// 1219// FleetMemberAPI member = createStationFleetMember(variantId); 1220// addStationEntry(parent, member, true, false); 1221// } 1222// } 1223// 1224// addStationEntry(parent, "remnant_station2_Standard", true, false); 1225// 1226// SalvageEntityGenDataSpec mothership = SalvageEntityGeneratorOld.getSalvageSpec(Entities.DERELICT_MOTHERSHIP); 1227// if (mothership != null) { 1228// String role = mothership.getStationRole(); 1229// String factionId = mothership.getDefFaction(); 1230// FactionSpecAPI faction = Global.getSettings().getFactionSpec(factionId); 1231// if (faction != null) { 1232// for (String variantId : faction.getAllVariantsForRole(role)) { 1233// addStationEntry(parent, variantId, true, false); 1234// } 1235// } 1236// } 1237// } 1238// 1239// public static void addStationEntry(CodexEntryPlugin parent, String variantId, boolean convertToHull, boolean forceAdd) { 1240// FleetMemberAPI member = createStationFleetMember(variantId); 1241// addStationEntry(parent, member, convertToHull, forceAdd); 1242// } 1243// 1244// public static FleetMemberAPI createStationFleetMember(String variantId) { 1245// FleetMemberAPI member = Global.getSettings().createFleetMember(FleetMemberType.SHIP, variantId); 1246// return member; 1247// } 1248// 1249// public static void addStationEntry(CodexEntryPlugin parent, FleetMemberAPI member, boolean convertToHull, boolean forceAdd) { 1250// ShipHullSpecAPI spec = member.getHullSpec(); 1251// 1252// if (!forceAdd && spec.hasTag(Tags.HIDE_STATION_IN_CODEX)) return; 1253// 1254// if (convertToHull) { 1255// String variantId = getFleetMemberBaseHullId(member) + "_Hull"; 1256// ShipVariantAPI variant = Global.getSettings().getVariant(variantId).clone(); 1257// // the variant already has empty _Hull modules, or should 1258// if (variant.getStationModules() == null || variant.getStationModules().isEmpty()) { 1259// // for (String slotId : member.getVariant().getModuleSlots()) { 1260// // ShipVariantAPI module = member.getVariant().getModuleVariant(slotId); 1261// // if (module != null) { 1262// // String moduleBaseHullVariantId = getBaseHullId(module.getHullSpec()) + "_Hull"; 1263// // ShipVariantAPI emptyModuleVariant = Global.getSettings().getVariant(moduleBaseHullVariantId).clone(); 1264// // variant.setModuleVariant(slotId, emptyModuleVariant); 1265// // } 1266// // } 1267// } 1268// member.setVariant(variant, false, true); 1269// } 1270// 1271// 1272// CodexEntryV2 curr = new CodexEntryV2(getShipEntryId(spec.getHullId()), 1273// spec.getHullName(), null, spec) { 1274// @Override 1275// public String getSortTitle() { 1276// return spec.getHullName(); 1277// } 1278// @Override 1279// public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1280// info.addPara(spec.getHullName(), Misc.getBasePlayerColor(), 0f); 1281// info.addPara("Station", Misc.getGrayColor(), 0f); 1282// } 1283// 1284// @Override 1285// public boolean matchesTags(Set<String> tags) { 1286// String sizeTag = STATIONS; 1287// if (sizeTag != null && !tags.contains(sizeTag)) return false; 1288// 1289// String m = spec.getManufacturer(); 1290// if (m != null && !tags.contains(m) && !tags.contains(ALL_TECHS)) return false; 1291// 1292// if (!tags.contains(ALL_TYPES)) return false; 1293// 1294// return true; 1295// } 1296// 1297// @Override 1298// public String getSearchString() { 1299// return super.getSearchString() + " Station"; 1300// } 1301// @Override 1302// public Set<String> getUnlockRelatedTags() { 1303// return spec.getTags(); 1304// } 1305// 1306// @Override 1307// public boolean isUnlockedIfRequiresUnlock() { 1308// return SharedUnlockData.get().isPlayerAwareOfShip(spec.getHullId()); 1309// } 1310// }; 1311// curr.setParam2(member); 1312// parent.addChild(curr); 1313// 1314// if (convertToHull) { 1315// curr.addTag(TAG_EMPTY_HULL_WITH_MODULES); 1316// } 1317// 1318// addModulesForVariant(member.getVariant(), convertToHull, curr, parent); 1319// } 1320 1321 public static List<CodexEntryPlugin> addModulesForVariant(ShipVariantAPI variant, boolean isEmptyHull, 1322 CodexEntryPlugin entry, CodexEntryPlugin parent) { 1323 List<CodexEntryPlugin> result = new ArrayList<>(); 1324 Set<String> seenVariants = new HashSet<>(); 1325 for (String slotId : variant.getStationModules().keySet()) { 1326 ShipVariantAPI moduleVariant = variant.getModuleVariant(slotId); 1327 if (moduleVariant == null) continue; 1328 if (moduleVariant.hasHullMod(HullMods.VASTBULK)) continue; 1329 1330 String moduleEntryId = getShipEntryId(moduleVariant.getHullSpec().getHullId()); 1331 if (isEmptyHull) { 1332 if (SEEN_STATION_MODULES.containsKey(moduleEntryId) || ENTRIES.containsKey(moduleEntryId)) { 1333 makeRelated(entry, SEEN_STATION_MODULES.get(moduleEntryId)); 1334 continue; 1335 } 1336 } else { 1337 // we're adding an entry for a specific fleet member's variant of the station 1338 // so, want to add all the modules as fleetmember entries 1339 // except for ones with duplicate variants 1340 try { 1341 String test = moduleVariant.toJSONObject().toString(4); 1342 if (seenVariants.contains(test)) continue; 1343 seenVariants.add(test); 1344 } catch (JSONException e) { 1345 } 1346 } 1347 1348 FleetMemberAPI moduleMember = Global.getSettings().createFleetMember(FleetMemberType.SHIP, moduleVariant); 1349 //if (moduleMember.getVariant().getFittedWeaponSlots().isEmpty()) continue; 1350 CodexEntryPlugin module = addModuleEntry(parent, entry, moduleMember, isEmptyHull); 1351 SEEN_STATION_MODULES.put(moduleEntryId, module); 1352 1353 makeRelated(entry, module); 1354 result.add(module); 1355 1356 if (!isEmptyHull) { 1357 linkFleetMemberEntryToRelated(module, moduleMember, false); 1358 } 1359 } 1360 return result; 1361 } 1362 1363 public static CodexEntryPlugin addModuleEntry(CodexEntryPlugin parent, CodexEntryPlugin entryForParentShip, 1364 FleetMemberAPI member, boolean isEmptyHull) { 1365 ShipHullSpecAPI spec = member.getHullSpec(); 1366 1367 String moduleEntryId = getShipEntryId(spec.getHullId()); 1368 if (!isEmptyHull) { // adding a temp entry 1369 moduleEntryId = UUID.randomUUID().toString(); 1370 } 1371 1372 CodexEntryV2 curr = new CodexEntryV2(moduleEntryId, 1373 spec.getHullName(), null, spec) { 1374 @Override 1375 public String getSortTitle() { 1376 return spec.getHullName(); 1377 } 1378 @Override 1379 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1380 info.addPara(spec.getHullName(), Misc.getBasePlayerColor(), 0f); 1381 info.addPara("Module", Misc.getGrayColor(), 0f); 1382 } 1383 @Override 1384 public boolean matchesTags(Set<String> tags) { 1385 return false; 1386 } 1387 @Override 1388 public boolean checkTagsWhenLocked() { 1389 // so that the module does not show up in the main list when it's locked 1390 // despite matchesTags() always being false 1391 return true; 1392 } 1393 @Override 1394 public boolean isVisible() { 1395 return entryForParentShip.isVisible(); 1396 } 1397 @Override 1398 public boolean isLocked() { 1399 return entryForParentShip.isLocked(); 1400 } 1401 @Override 1402 public String getSearchString() { 1403 return ""; 1404 } 1405 @Override 1406 public boolean skipForTags() { 1407 // actually, not adding it to the parent's children is easier 1408 // actually: not easier, it messes up the sort order in the related entries list 1409 return true; 1410 } 1411 }; 1412 curr.setParam2(member); 1413 if (isEmptyHull) { 1414 curr.addTag(TAG_EMPTY_MODULE); 1415 } 1416 if (parent != null) parent.addChild(curr); 1417 return curr; 1418 } 1419 1420 public static void populateShipSystems(CodexEntryPlugin parent, CodexEntryPlugin ships) { 1421 List<ShipSystemSpecAPI> specs = Global.getSettings().getAllShipSystemSpecs(); 1422 for (final ShipSystemSpecAPI spec : specs) { 1423 //if (spec.getTags().contains(Tags.RESTRICTED)) continue; 1424 if (spec.getTags().contains(Tags.HIDE_IN_CODEX)) continue; 1425 1426 Description desc = Global.getSettings().getDescription(spec.getId(), Type.SHIP_SYSTEM); 1427 String typeStr = desc.getText2(); 1428 CodexEntryV2 curr = new CodexEntryV2(getShipSystemEntryId(spec.getId()), 1429 spec.getName(), spec.getIconSpriteName(), spec) { 1430 @Override 1431 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1432 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 1433 if (mode == ListMode.RELATED_ENTRIES) { 1434 info.addPara("Ship system", Misc.getGrayColor(), 0f); 1435 } else if (desc.hasText2()) { 1436 info.addPara(typeStr, Misc.getGrayColor(), 0f); 1437 } else { 1438 info.addPara("Special", Misc.getGrayColor(), 0f); 1439 } 1440 } 1441 @Override 1442 public boolean matchesTags(Set<String> tags) { 1443 if (tags.contains(ALL_TYPES)) return true; 1444 Description desc = Global.getSettings().getDescription(spec.getId(), Type.SHIP_SYSTEM); 1445 String typeStr = desc.getText2(); 1446 if (!desc.hasText2()) typeStr = "Special"; 1447 return tags.contains(typeStr); 1448 } 1449 1450 @Override 1451 public boolean isVignetteIcon() { 1452 return true; 1453 } 1454 1455 @Override 1456 public Color getIconColor() { 1457 return Misc.getBasePlayerColor(); 1458 } 1459 1460 @Override 1461 public Set<String> getUnlockRelatedTags() { 1462 LinkedHashSet<String> tags = new LinkedHashSet<>(spec.getTags()); 1463 tags.add(Tags.CODEX_REQUIRE_RELATED); 1464 return tags; 1465 } 1466 1467 @Override 1468 public boolean isUnlockedIfRequiresUnlock() { 1469 return SharedUnlockData.get().isPlayerAwareOfShipSystem(spec.getId()); 1470 } 1471 1472 @Override 1473 public String getSearchString() { 1474 return super.getSearchString();// + " " + typeStr; 1475 } 1476 }; 1477 1478 parent.addChild(curr); 1479 } 1480 } 1481 1482 1483 public static void populateSkills(CodexEntryPlugin parent) { 1484 List<String> skillIds = Global.getSettings().getSkillIds(); 1485 for (String skillId : skillIds) { 1486 SkillSpecAPI spec = Global.getSettings().getSkillSpec(skillId); 1487 String aptitude = getAptitudeName(spec); 1488 String aptStr = ""; 1489 if (aptitude != null && !aptitude.isBlank()) { 1490 aptStr = " - " + aptitude + " skill"; 1491 } 1492 1493 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 1494 if (spec.isAptitudeEffect()) continue; 1495 1496 if (!spec.hasTag(Tags.SHOW_IN_CODEX) && !spec.hasTag(Tags.CODEX_UNLOCKABLE)) { 1497 if (spec.hasTag(Skills.TAG_NPC_ONLY)) continue; 1498 if (spec.hasTag(Skills.TAG_AI_CORE_ONLY)) continue; 1499 }; 1500 if (spec.hasTag(Skills.TAG_DEPRECATED)) continue; 1501 1502 CodexEntryV2 curr = new CodexEntryV2(getSkillEntryId(spec.getId()), 1503 spec.getName() + aptStr, spec.getSpriteName(), spec) { 1504 @Override 1505 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1506 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 1507 if (mode == ListMode.RELATED_ENTRIES || true) { 1508 String aptitude = getAptitudeName(spec); 1509 if (aptitude != null && !aptitude.isBlank()) { 1510 info.addPara(aptitude + " skill", Misc.getGrayColor(), 0f); 1511 } else { 1512 info.addPara("Skill", Misc.getGrayColor(), 0f); 1513 } 1514 } 1515 } 1516 1517 @Override 1518 public boolean matchesTags(Set<String> tags) { 1519 if (spec.isElite() && !tags.contains(PILOTED_SHIP)) return false; 1520 if (!spec.isElite() && !tags.contains(OTHER)) return false; 1521 1522 String aptitude = getAptitudeName(spec); 1523 if ((aptitude == null || aptitude.isBlank()) && !tags.contains(ALL_APTITUDES)) { 1524 return false; 1525 } 1526 return tags.contains(aptitude); 1527 } 1528 1529 @Override 1530 public boolean isVignetteIcon() { 1531 return true; 1532 } 1533 1534 @Override 1535 public Color getIconColor() { 1536 return Color.white; 1537 } 1538 1539 @Override 1540 public Set<String> getUnlockRelatedTags() { 1541 return spec.getTags(); 1542 } 1543 1544 @Override 1545 public boolean isUnlockedIfRequiresUnlock() { 1546 return SharedUnlockData.get().isPlayerAwareOfSkill(spec.getId()); 1547 } 1548 1549 @Override 1550 public String getSearchString() { 1551 String aptitude = getAptitudeName(spec); 1552 return super.getSearchString() + " " + aptitude; 1553 } 1554 }; 1555 1556 parent.addChild(curr); 1557 } 1558 } 1559 1560 public static void populateAbilities(CodexEntryPlugin parent) { 1561 List<String> ids = Global.getSettings().getSortedAbilityIds(); 1562 for (String id : ids) { 1563 AbilitySpecAPI spec = Global.getSettings().getAbilitySpec(id); 1564 1565 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 1566 1567 CodexEntryV2 curr = new CodexEntryV2(getAbilityEntryId(spec.getId()), 1568 spec.getName(), spec.getIconName(), spec) { 1569 @Override 1570 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1571 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 1572 if (mode == ListMode.RELATED_ENTRIES) { 1573 info.addPara("Ability", Misc.getGrayColor(), 0f); 1574 } 1575 } 1576 @Override 1577 public boolean matchesTags(Set<String> tags) { 1578 return super.matchesTags(tags); 1579 } 1580 1581 @Override 1582 public boolean isVignetteIcon() { 1583 return true; 1584 } 1585 1586 @Override 1587 public Color getIconColor() { 1588 return Color.white; 1589 } 1590 1591 @Override 1592 public Set<String> getUnlockRelatedTags() { 1593 return spec.getTags(); 1594 } 1595 1596 @Override 1597 public boolean isUnlockedIfRequiresUnlock() { 1598 return SharedUnlockData.get().isPlayerAwareOfAbility(spec.getId()); 1599 } 1600 1601 @Override 1602 public String getSearchString() { 1603 return super.getSearchString(); 1604 } 1605 }; 1606 1607 parent.addChild(curr); 1608 } 1609 } 1610 1611 public static String getAptitudeName(SkillSpecAPI spec) { 1612 String aptitude = spec.getGoverningAptitudeName(); 1613 if (spec.hasTag(Skills.TAG_AI_CORE_ONLY)) { 1614 aptitude = "AI core"; 1615 } 1616 return aptitude; 1617 } 1618 1619 1620 public static void sortSkillsCategory() { 1621 CodexEntryPlugin skills = getEntry(CAT_SKILLS); 1622 Collections.sort(skills.getChildren(), new Comparator<CodexEntryPlugin>() { 1623 @Override 1624 public int compare(CodexEntryPlugin o1, CodexEntryPlugin o2) { 1625 SkillSpecAPI s1 = null; 1626 SkillSpecAPI s2 = null; 1627 if (o1.getParam() instanceof SkillSpecAPI) { 1628 s1 = (SkillSpecAPI) o1.getParam(); 1629 } 1630 if (o2.getParam() instanceof SkillSpecAPI) { 1631 s2 = (SkillSpecAPI) o2.getParam(); 1632 } 1633 if (s1 != null && s2 == null) return Integer.MIN_VALUE; 1634 if (s2 != null && s1 == null) return Integer.MAX_VALUE; 1635 if (s1 == null && s2 == null) return o1.getTitle().compareTo(o2.getTitle()); 1636 1637 int tier1 = 0; 1638 int tier2 = 0; 1639 if (s1.hasTag(Skills.TAG_AI_CORE_ONLY)) tier1 = 5; 1640 if (s2.hasTag(Skills.TAG_AI_CORE_ONLY)) tier2 = 5; 1641 1642 if (tier1 != tier2) return tier1 - tier2; 1643 1644 int diff = s1.getGoverningAptitudeOrder() - s2.getGoverningAptitudeOrder(); 1645 if (diff != 0) return diff; 1646 1647 return (int) Math.signum(s1.getOrder() - s2.getOrder()); 1648 } 1649 }); 1650 } 1651 1652 1653 public static void populateWeapons(CodexEntryPlugin parent) { 1654 1655 for (WeaponSpecAPI spec : Global.getSettings().getActuallyAllWeaponSpecs()) { 1656 //if (spec.getTags().contains(Tags.RESTRICTED)) continue; 1657 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 1658 if ((spec.getAIHints().contains(AIHints.SYSTEM) || spec.getType() == WeaponType.SYSTEM) && 1659 !(spec.getAIHints().contains(AIHints.SHOW_IN_CODEX) || 1660 spec.hasTag(Tags.SHOW_IN_CODEX))) { 1661 continue; 1662 } 1663 1664 CodexEntryV2 curr = new CodexEntryV2(getWeaponEntryId(spec.getWeaponId()), 1665 spec.getWeaponName(), null, spec) { 1666 @Override 1667 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1668 info.addPara(spec.getWeaponName(), Misc.getBasePlayerColor(), 0f); 1669 if (mode == ListMode.RELATED_ENTRIES || true) { 1670 String size = spec.getSize().getDisplayName(); 1671 WeaponType type = spec.getMountType(); 1672 if (type == null) type = spec.getType(); 1673 1674 info.addPara(size + " " + type.getDisplayName().toLowerCase() + " weapon", Misc.getGrayColor(), 0f); 1675 //info.addPara("Weapon", Misc.getGrayColor(), 0f); 1676 } 1677 } 1678 1679 @Override 1680 public boolean matchesTags(Set<String> tags) { 1681 WeaponSize size = spec.getSize(); 1682 1683 String sizeTag = null; 1684 if (size == WeaponSize.SMALL) sizeTag = SMALL; 1685 if (size == WeaponSize.MEDIUM) sizeTag = MEDIUM; 1686 if (size == WeaponSize.LARGE) sizeTag = LARGE; 1687 if (spec.getAIHints().contains(AIHints.SYSTEM) && 1688 spec.getPrimaryRoleStr() != null && 1689 spec.getPrimaryRoleStr() != null && 1690 spec.getPrimaryRoleStr().endsWith("(Fighter)")) { 1691 sizeTag = FIGHTER_WEAPON; 1692 } 1693 1694 if (sizeTag != null && !tags.contains(sizeTag)) return false; 1695 1696 String damTypeTag = null; 1697 DamageType type = spec.getDamageType(); 1698 if (type == DamageType.HIGH_EXPLOSIVE) damTypeTag = HIGH_EXPLOSIVE; 1699 if (type == DamageType.KINETIC) damTypeTag = KINETIC; 1700 if (type == DamageType.ENERGY) damTypeTag = DAM_ENERGY; 1701 if (type == DamageType.FRAGMENTATION) damTypeTag = FRAGMENTATION; 1702 if (type == DamageType.OTHER) damTypeTag = OTHER; 1703 if (damTypeTag != null && !tags.contains(damTypeTag)) return false; 1704 1705 String m = spec.getManufacturer(); 1706 if (m != null && !tags.contains(m) && !tags.contains(ALL_TECHS)) return false; 1707 1708 1709 if (spec.getMountType() == WeaponType.HYBRID && tags.contains(HYBRID)) return true; 1710 if (spec.getMountType() == WeaponType.COMPOSITE && tags.contains(COMPOSITE)) return true; 1711 if (spec.getMountType() == WeaponType.SYNERGY && tags.contains(SYNERGY)) return true; 1712 if (spec.getMountType() == WeaponType.UNIVERSAL && tags.contains(UNIVERSAL)) return true; 1713 if (spec.isBeam() && tags.contains(BEAM)) return true; 1714 1715 if (spec.getType() == WeaponType.BALLISTIC && !tags.contains(BALLISTIC)) return false; 1716 if (spec.getType() == WeaponType.MISSILE && !tags.contains(MISSILE)) return false; 1717 if (spec.getType() == WeaponType.ENERGY && !tags.contains(ENERGY)) return false; 1718 1719 return true; 1720 } 1721 1722 @Override 1723 public Set<String> getUnlockRelatedTags() { 1724 return spec.getTags(); 1725 } 1726 1727 @Override 1728 public boolean isUnlockedIfRequiresUnlock() { 1729 return SharedUnlockData.get().isPlayerAwareOfWeapon(spec.getWeaponId()); 1730 } 1731 1732 }; 1733 parent.addChild(curr); 1734 } 1735 } 1736 1737 public static void populateFighters(CodexEntryPlugin parent) { 1738 1739 for (final FighterWingSpecAPI spec : Global.getSettings().getAllFighterWingSpecs()) { 1740 if (spec.getVariant().getHints().contains(ShipTypeHints.HIDE_IN_CODEX)) continue; 1741 if (spec.getTags().contains(Tags.HIDE_IN_CODEX)) continue; 1742 //if (spec.getTags().contains(Tags.RESTRICTED)) continue; 1743 1744 ShipVariantAPI variant = spec.getVariant(); 1745 String nameStr = variant.getHullSpec().getHullName() + " " + variant.getDisplayName(); 1746 if (spec.hasTag(Tags.SWARM_FIGHTER)) { 1747 nameStr = variant.getDisplayName() + " " + variant.getHullSpec().getHullName(); 1748 } 1749 if (variant.getDisplayName().isEmpty()) { 1750 nameStr = variant.getHullSpec().getHullName(); 1751 } 1752 String nameStr2 = nameStr; 1753 CodexEntryV2 curr = new CodexEntryV2(getFighterEntryId(spec.getId()), 1754 nameStr2, null, spec) { 1755 @Override 1756 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1757 info.addPara(nameStr2, Misc.getBasePlayerColor(), 0f); 1758 if (mode == ListMode.RELATED_ENTRIES || true) { 1759 String role = spec.getRole().name().toLowerCase(); 1760 role = Misc.ucFirst(role); 1761 if (spec.getRoleDesc() != null && !spec.getRoleDesc().isEmpty()) { 1762 role = spec.getRoleDesc(); 1763 } 1764 info.addPara(role, Misc.getGrayColor(), 0f); 1765 } 1766 } 1767 1768 @Override 1769 public boolean matchesTags(Set<String> tags) { 1770 if (spec.getRole() == WingRole.FIGHTER) { 1771 if (!tags.contains(FIGHTER)) return false; 1772 } else if (spec.getRole() == WingRole.BOMBER) { 1773 if (!tags.contains(BOMBER)) return false; 1774 } else if (spec.getRole() == WingRole.INTERCEPTOR) { 1775 if (!tags.contains(INTERCEPTOR)) return false; 1776 } else { 1777 if (!tags.contains(OTHER)) return false; 1778 } 1779 1780 String m = spec.getVariant().getHullSpec().getManufacturer(); 1781 if (m != null && !tags.contains(m) && !tags.contains(ALL_TECHS)) return false; 1782 1783 return true; 1784 } 1785 1786 @Override 1787 public Set<String> getUnlockRelatedTags() { 1788 return spec.getTags(); 1789 } 1790 1791 @Override 1792 public boolean isUnlockedIfRequiresUnlock() { 1793 return SharedUnlockData.get().isPlayerAwareOfFighter(spec.getId()); 1794 } 1795 1796 }; 1797 parent.addChild(curr); 1798 } 1799 } 1800 1801 public static class GalleryEntryData implements WithSourceMod { 1802 public String sprite; 1803 public Description desc; 1804 public ModSpecAPI sourceMod; 1805 @Override 1806 public ModSpecAPI getSourceMod() { 1807 return sourceMod; 1808 } 1809 } 1810 public static void populateGallery(CodexEntryPlugin parent) { 1811 String cat = "illustrations"; 1812 for (String key : Global.getSettings().getSpriteKeys(cat)) { 1813 String sprite = Global.getSettings().getSpriteName(cat, key); 1814 1815 Description desc = Global.getSettings().getDescription(key, Type.GALLERY); 1816 1817 if (desc.getText3().toLowerCase().contains(Tags.HIDE_IN_CODEX)) continue; 1818 1819 if (!desc.hasText1()) continue; 1820 1821 if (USE_KEY_NAMES_FOR_GALLERY) { 1822 String text = key.replaceAll("_", " "); 1823 text = Misc.ucFirst(text); 1824 desc.setText1(text); 1825 desc.setText2(text); 1826 } 1827 1828 GalleryEntryData data = new GalleryEntryData(); 1829 data.sprite = sprite; 1830 data.desc = desc; 1831 data.sourceMod = desc.getSourceMod(); 1832 1833 CodexEntryV2 curr = new CodexEntryV2(getGalleryEntryId(key), 1834 desc.getText1(), sprite, data) { 1835 @Override 1836 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1837 info.addPara(desc.getText1(), Misc.getBasePlayerColor(), 0f); 1838 if (mode == ListMode.RELATED_ENTRIES) { 1839 info.addPara("Illustration", Misc.getGrayColor(), 0f); 1840 } 1841 } 1842 @Override 1843 public boolean isVignetteIcon() { 1844 return true; 1845 } 1846 @Override 1847 public boolean isVisible() { 1848 return super.isVisible(); 1849 } 1850 @Override 1851 public boolean isLocked() { 1852 if (codexFullyUnlocked()) return false; 1853 if (SharedUnlockData.get().isPlayerAwareOfIllustration(key)) { 1854 return false; 1855 } 1856 return !desc.getText3().toLowerCase().contains("unlocked"); 1857 } 1858 }; 1859 parent.addChild(curr); 1860 } 1861 } 1862 1863 public static void populateSpecialItems(CodexEntryPlugin parent) { 1864 List<SpecialItemSpecAPI> specs = Global.getSettings().getAllSpecialItemSpecs(); 1865 for (final SpecialItemSpecAPI spec : specs) { 1866 //if (spec.hasTag(Items.TAG_BLUEPRINT_PACKAGE)) continue; 1867 if (spec.hasTag(Items.TAG_SINGLE_BP)) continue; 1868 if (spec.hasTag(Items.TAG_MODSPEC)) continue; 1869 if (spec.getId().equals(Items.INDUSTRY_BP)) continue; 1870 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 1871 1872 CodexEntryV2 curr = new CodexEntryV2(getItemEntryId(spec.getId()), 1873 spec.getName(), spec.getIconName(), spec) { 1874 @Override 1875 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1876 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 1877 if (mode == ListMode.RELATED_ENTRIES) { 1878 info.addPara("Special item", Misc.getGrayColor(), 0f); 1879 } 1880 } 1881 1882 @Override 1883 public boolean matchesTags(Set<String> tags) { 1884 boolean colony = spec.hasTag(Items.TAG_COLONY_ITEM); 1885 boolean bp = spec.hasTag(Items.TAG_BLUEPRINT_PACKAGE); 1886 if (tags.contains(OTHER) && !colony && !bp) return true; 1887 if (tags.contains(COLONY) && colony) return true; 1888 if (tags.contains(BLUEPRINTS) && bp) return true; 1889 return false; 1890 } 1891 1892 @Override 1893 public Set<String> getUnlockRelatedTags() { 1894 return spec.getTags(); 1895 } 1896 1897 @Override 1898 public boolean isUnlockedIfRequiresUnlock() { 1899 return SharedUnlockData.get().isPlayerAwareOfSpecialItem(spec.getId()); 1900 } 1901 1902 }; 1903 parent.addChild(curr); 1904 } 1905 } 1906 1907 public static void populateCommodities(CodexEntryPlugin commodities, CodexEntryPlugin items) { 1908 List<CommoditySpecAPI> specs = Global.getSettings().getAllCommoditySpecs(); 1909 for (final CommoditySpecAPI spec : specs) { 1910 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 1911 1912 boolean special = spec.hasTag(Commodities.TAG_AI_CORE) || spec.hasTag(Commodities.TAG_NON_ECONOMIC); 1913 CodexEntryV2 curr = new CodexEntryV2(getCommodityEntryId(spec.getId()), 1914 spec.getName(), spec.getIconName(), spec) { 1915 @Override 1916 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1917 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 1918 if (mode == ListMode.RELATED_ENTRIES) { 1919 if (special) { 1920 info.addPara("Special item", Misc.getGrayColor(), 0f); 1921 } else { 1922 info.addPara("Commodity", Misc.getGrayColor(), 0f); 1923 } 1924 } 1925 1926 } 1927 @Override 1928 public boolean matchesTags(Set<String> tags) { 1929 if (special) { 1930 boolean aiCore = spec.hasTag(Commodities.TAG_AI_CORE); 1931 if (tags.contains(OTHER) && !aiCore) return true; 1932 if (tags.contains(AI_CORE) && aiCore) return true; 1933 return false; 1934 } else { 1935 return super.matchesTags(tags); 1936 } 1937 } 1938 1939 @Override 1940 public Set<String> getUnlockRelatedTags() { 1941 return spec.getTags(); 1942 } 1943 1944 @Override 1945 public boolean isUnlockedIfRequiresUnlock() { 1946 return SharedUnlockData.get().isPlayerAwareOfCommodity(spec.getId()); 1947 } 1948 1949 }; 1950 if (special) { 1951 items.addChild(curr); 1952 } else { 1953 commodities.addChild(curr); 1954 } 1955 } 1956 } 1957 1958// public static CodexEntryV2 createFactionsCategory() { 1959// CodexEntryV2 cat = new CodexEntryV2(CAT_FACTIONS, "Factions", getIcon(CAT_FACTIONS)) { 1960// 1961// }; 1962// return cat; 1963// } 1964// public static void populateFactions(CodexEntryPlugin factions) { 1965// List<FactionSpecAPI> specs = Global.getSettings().getAllFactionSpecs(); 1966// for (final FactionSpecAPI spec : specs) { 1967// if (!spec.isShowInIntelTab()) continue; 1968// 1969// String name = Misc.ucFirst(spec.getDisplayName()); 1970// CodexEntryV2 curr = new CodexEntryV2(getFactionEntryId(spec.getId()), 1971// name, spec.getCrest(), spec) { 1972// @Override 1973// public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 1974// info.addPara(name, Misc.getBasePlayerColor(), 0f); 1975// if (mode == ListMode.RELATED_ENTRIES) { 1976// info.addPara("Faction", Misc.getGrayColor(), 0f); 1977// } 1978// 1979// } 1980// @Override 1981// public boolean isVignetteIcon() { 1982// return true; 1983// } 1984// @Override 1985// public Set<String> getUnlockRelatedTags() { 1986// return super.getUnlockRelatedTags(); 1987// } 1988// 1989// @Override 1990// public boolean isUnlockedIfRequiresUnlock() { 1991// return super.isUnlockedIfRequiresUnlock(); 1992// //return SharedUnlockData.get().isPlayerAwareOfCommodity(spec.getId()); 1993// } 1994// }; 1995// factions.addChild(curr); 1996// } 1997// } 1998 1999// doesn't work for various reasons - need icons, and terrain REALLY depends on being inside a campaign 2000// would need to create a parallel set of codex entries, not use the terrain plugins directly, which has its own problems 2001// public static void populateTerrain(CodexEntryPlugin terrain) { 2002// List<TerrainSpecAPI> specs = Global.getSettings().getAllTerrainSpecs(); 2003// for (final TerrainSpecAPI spec : specs) { 2004// if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 2005// 2006// SectorEntityToken temp = Global.getSettings().createLocationToken(0, 0); 2007// CampaignTerrainPlugin plugin = spec.getNewPluginInstance(temp, null); 2008// String name = Misc.ucFirst(plugin.getNameForTooltip()); 2009// CodexEntryV2 curr = new CodexEntryV2(getFactionEntryId(spec.getId()), 2010// name, null, spec) { 2011// @Override 2012// public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 2013// info.addPara(name, Misc.getBasePlayerColor(), 0f); 2014// if (mode == ListMode.RELATED_ENTRIES) { 2015// info.addPara("Terrain", Misc.getGrayColor(), 0f); 2016// } 2017// 2018// } 2019// @Override 2020// public boolean isVignetteIcon() { 2021// return true; 2022// } 2023// @Override 2024// public Set<String> getUnlockRelatedTags() { 2025// return super.getUnlockRelatedTags(); 2026// } 2027// 2028// @Override 2029// public boolean isUnlockedIfRequiresUnlock() { 2030// return super.isUnlockedIfRequiresUnlock(); 2031// //return SharedUnlockData.get().isPlayerAwareOfCommodity(spec.getId()); 2032// } 2033// }; 2034// terrain.addChild(curr); 2035// } 2036// } 2037 2038 public static void populateHullMods(CodexEntryPlugin parent) { 2039 List<HullModSpecAPI> specs = Global.getSettings().getAllHullModSpecs(); 2040 for (final HullModSpecAPI spec : specs) { 2041 if (spec.isHiddenEverywhere()) continue; 2042 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 2043 2044 CodexEntryV2 curr = new CodexEntryV2(getHullmodEntryId(spec.getId()), 2045 spec.getDisplayName(), spec.getSpriteName(), spec) { 2046 @Override 2047 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 2048 info.addPara(spec.getDisplayName(), Misc.getBasePlayerColor(), 0f); 2049 if (mode == ListMode.RELATED_ENTRIES) { 2050 info.addPara("Hullmod", Misc.getGrayColor(), 0f); 2051 } 2052 } 2053 @Override 2054 public boolean isVignetteIcon() { 2055 return true; 2056 } 2057 2058 @Override 2059 public boolean matchesTags(Set<String> tags) { 2060 if (!tags.contains(spec.getManufacturer())) { 2061 return false; 2062 } 2063 2064 if (tags.contains(ALL_TYPES)) return true; 2065 2066 boolean hasATag = false; 2067 for (String tag : spec.getUITags()) { 2068 if (tags.contains(tag)) hasATag = true; 2069 } 2070 if (spec.hasTag(Tags.HULLMOD_DMOD) && tags.contains(DMODS)) { 2071 hasATag = true; 2072 } 2073 if (spec.isHidden() && !spec.hasTag(Tags.HULLMOD_DMOD) && tags.contains(INTRINSIC)) { 2074 hasATag = true; 2075 } 2076 if (!hasATag) return false; 2077 2078 return true; 2079 } 2080 2081 @Override 2082 public Set<String> getUnlockRelatedTags() { 2083 return spec.getTags(); 2084 } 2085 2086 @Override 2087 public boolean isUnlockedIfRequiresUnlock() { 2088 return SharedUnlockData.get().isPlayerAwareOfHullmod(spec.getId()); 2089 } 2090 }; 2091 2092 parent.addChild(curr); 2093 } 2094 } 2095 2096 public static void populateIndustries(CodexEntryPlugin parent) { 2097 List<IndustrySpecAPI> specs = Global.getSettings().getAllIndustrySpecs(); 2098 for (final IndustrySpecAPI spec : specs) { 2099 if (spec.hasTag(Industries.TAG_PARENT)) continue; 2100 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 2101 2102 CodexEntryV2 curr = new CodexEntryV2(getIndustryEntryId(spec.getId()), 2103 spec.getName(), spec.getImageName(), spec) { 2104 @Override 2105 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 2106 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 2107 if (mode == ListMode.RELATED_ENTRIES || true) { 2108 //info.addPara("Industry", Misc.getGrayColor(), 0f); 2109 boolean structure = spec.hasTag(Industries.TAG_STRUCTURE); 2110 String type = "Industry"; 2111 if (structure) type = "Structure"; 2112 info.addPara(type, Misc.getGrayColor(), 0f); 2113 } 2114 } 2115 @Override 2116 public boolean isVignetteIcon() { 2117 return true; 2118 } 2119 @Override 2120 public boolean matchesTags(Set<String> tags) { 2121 boolean industry = spec.hasTag(Industries.TAG_INDUSTRY); 2122 boolean structure = spec.hasTag(Industries.TAG_STRUCTURE); 2123 boolean station = spec.hasTag(Industries.TAG_STATION); 2124 if (tags.contains(OTHER) && !industry && !structure && !station) return true; 2125 if (tags.contains(INDUSTRIES) && industry) return true; 2126 if (tags.contains(STRUCTURES) && structure && !station) return true; 2127 if (tags.contains(STATIONS) && station) return true; 2128 return false; 2129 } 2130 @Override 2131 public Set<String> getUnlockRelatedTags() { 2132 return spec.getTags(); 2133 } 2134 @Override 2135 public boolean isUnlockedIfRequiresUnlock() { 2136 return SharedUnlockData.get().isPlayerAwareOfIndustry(spec.getId()); 2137 } 2138 }; 2139 parent.addChild(curr); 2140 } 2141 } 2142 2143 public static void populateStarsAndPlanets(CodexEntryPlugin parent) { 2144 List<PlanetSpecAPI> specs = Global.getSettings().getAllPlanetSpecs(); 2145 for (final PlanetSpecAPI spec : specs) { 2146 //if (spec.isNebulaCenter()) continue; 2147 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 2148 2149 CodexEntryV2 curr = new CodexEntryV2(getPlanetEntryId(spec.getPlanetType()), 2150 spec.getName(), spec.getIconTexture(), spec) { 2151 @Override 2152 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 2153 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 2154 String type = "Planet"; 2155 if (spec.isGasGiant()) type = "Gas giant"; 2156 else if (spec.isNebulaCenter()) type = "Nebula"; 2157 else if (spec.isStar()) type = "Star"; 2158 if (mode == ListMode.RELATED_ENTRIES) { 2159 info.addPara(type, Misc.getGrayColor(), 0f); 2160 } 2161 } 2162 @Override 2163 public boolean matchesTags(Set<String> tags) { 2164 if (tags.contains(ALL_TYPES)) return true; 2165 2166 if (tags.contains(HABITABLE) && Misc.canPlanetTypeRollHabitable(spec)) { 2167 return true; 2168 } 2169 if (spec.isGasGiant() && !tags.contains(GAS_GIANTS)) { 2170 return false; 2171 } else if (spec.isStar() && !tags.contains(STARS)) { 2172 return false; 2173 } else if (!spec.isGasGiant() && !spec.isStar() && !tags.contains(PLANETS)) { 2174 return false; 2175 } 2176 2177 return true; 2178 } 2179 @Override 2180 public Set<String> getUnlockRelatedTags() { 2181 return spec.getTags(); 2182 } 2183 @Override 2184 public boolean isUnlockedIfRequiresUnlock() { 2185 return SharedUnlockData.get().isPlayerAwareOfPlanet(spec.getPlanetType()); 2186 } 2187 }; 2188 2189 parent.addChild(curr); 2190 } 2191 } 2192 2193 2194 public static void populatePlanetaryConditions(CodexEntryPlugin parent) { 2195 List<MarketConditionSpecAPI> specs = Global.getSettings().getAllMarketConditionSpecs(); 2196 for (final MarketConditionSpecAPI spec : specs) { 2197 2198 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue; 2199 2200 if (spec.getGenSpec() == null && 2201 !spec.hasTag(Tags.SHOW_IN_PLANET_LIST) && 2202 !spec.hasTag(Tags.SHOW_IN_CODEX)) { 2203 continue; 2204 } 2205 2206 CodexEntryV2 curr = new CodexEntryV2(getConditionEntryId(spec.getId()), 2207 spec.getName(), spec.getIcon(), spec) { 2208 @Override 2209 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 2210 info.addPara(spec.getName(), Misc.getBasePlayerColor(), 0f); 2211 if (mode == ListMode.RELATED_ENTRIES) { 2212 info.addPara("Planetary condition", Misc.getGrayColor(), 0f); 2213 } 2214 } 2215 @Override 2216 public boolean isVignetteIcon() { 2217 return true; 2218 } 2219 2220 @Override 2221 public boolean matchesTags(Set<String> tags) { 2222 boolean res = ResourceDepositsCondition.COMMODITY.containsKey(spec.getId()); 2223 if (tags.contains(OTHER) && !res) return true; 2224 if (tags.contains(RESOURCES) && res) return true; 2225 return false; 2226 } 2227 @Override 2228 public Set<String> getUnlockRelatedTags() { 2229 return spec.getTags(); 2230 } 2231 @Override 2232 public boolean isUnlockedIfRequiresUnlock() { 2233 return SharedUnlockData.get().isPlayerAwareOfCondition(spec.getId()); 2234 } 2235 }; 2236 2237 parent.addChild(curr); 2238 } 2239 } 2240 2241 2242 public static void linkRelatedEntries() { 2243 CodexEntryPlugin ships = getEntry(CAT_SHIPS); 2244 CodexEntryPlugin stations = getEntry(CAT_STATIONS); 2245 CodexEntryPlugin fighters = getEntry(CAT_FIGHTERS); 2246 CodexEntryPlugin weapons = getEntry(CAT_WEAPONS); 2247 CodexEntryPlugin hullmods = getEntry(CAT_HULLMODS); 2248 CodexEntryPlugin shipSystems = getEntry(CAT_SHIP_SYSTEMS); 2249 CodexEntryPlugin planets = getEntry(CAT_STARS_AND_PLANETS); 2250 CodexEntryPlugin conditions = getEntry(CAT_PLANETARY_CONDITIONS); 2251 CodexEntryPlugin items = getEntry(CAT_SPECIAL_ITEMS); 2252 CodexEntryPlugin commodities = getEntry(CAT_COMMODITIES); 2253 CodexEntryPlugin industries = getEntry(CAT_INDUSTRIES); 2254 CodexEntryPlugin skills = getEntry(CAT_SKILLS); 2255 CodexEntryPlugin abilities = getEntry(CAT_ABILITIES); 2256 //CodexEntryPlugin factions = getEntry(CAT_FACTIONS); 2257 2258 List<CodexEntryPlugin> shipsAndStations = new ArrayList<>(); 2259 shipsAndStations.addAll(ships.getChildren()); 2260 shipsAndStations.addAll(stations.getChildren()); 2261 2262 // link all ships that are based on the same hull - skins etc 2263 ListMap<CodexEntryPlugin> relatedHulls = new ListMap<>(); 2264 for (CodexEntryPlugin ship : shipsAndStations) { 2265 if (ship.getParam() instanceof ShipHullSpecAPI) { 2266 ShipHullSpecAPI spec = (ShipHullSpecAPI) ship.getParam(); 2267 String baseId = getBaseHullIdEvenIfNotRestorableTo(spec); 2268 relatedHulls.add(baseId, ship); 2269 if (spec.hasTag(Tags.DWELLER)) { 2270 relatedHulls.add(Tags.DWELLER + "_allParts", ship); 2271 } 2272 } 2273 } 2274 for (List<CodexEntryPlugin> list : relatedHulls.values()) { 2275 makeRelated(list); 2276 } 2277 2278 for (CodexEntryPlugin ship : shipsAndStations) { 2279 if (ship.getParam() instanceof ShipHullSpecAPI) { 2280 ShipHullSpecAPI spec = (ShipHullSpecAPI) ship.getParam(); 2281 2282 // link ships and their systems 2283 String sysId = spec.getShipSystemId(); 2284 if (sysId != null && !sysId.isBlank()) { 2285 String key = getShipSystemEntryId(sysId); 2286 CodexEntryPlugin sys = getEntry(key); 2287 if (sys != null) { 2288 sys.addRelatedEntry(ship); 2289 ship.addRelatedEntry(sys); 2290 2291 // link ship systems and ships with their drones, if any 2292 if (sys.getParam() instanceof ShipSystemSpecAPI) { 2293 ShipSystemSpecAPI sysSpec = (ShipSystemSpecAPI) sys.getParam(); 2294 if (sysSpec.getDroneVariant() != null) { 2295 ShipVariantAPI drone = Global.getSettings().getVariant(sysSpec.getDroneVariant()); 2296 if (drone != null) { 2297 String droneHullId = getBaseHullId(drone.getHullSpec()); 2298 String droneEntryId = getShipEntryId(droneHullId); 2299 makeRelated(ship.getId(), droneEntryId); 2300 makeRelated(sys.getId(), droneEntryId); 2301 } 2302 } 2303 } 2304 } 2305 } 2306 2307 if (!spec.isPhase()) { 2308 sysId = spec.getShipDefenseId(); 2309 String key = getShipSystemEntryId(sysId); 2310 CodexEntryPlugin sys = getEntry(key); 2311 if (sys != null) { 2312 sys.addRelatedEntry(ship); 2313 ship.addRelatedEntry(sys); 2314 } 2315 } 2316 2317 String variantId = spec.getHullId() + "_Hull"; 2318 if (spec.getCodexVariantId() != null && !spec.getCodexVariantId().isBlank()) { 2319 variantId = spec.getCodexVariantId(); 2320 } 2321 ShipVariantAPI variant = Global.getSettings().getVariant(variantId); 2322 2323 // link ships and their hullmods 2324 //for (String id : spec.getBuiltInMods()) { 2325 for (String id : variant.getHullMods()) { 2326 String key = getHullmodEntryId(id); 2327 CodexEntryPlugin mod = getEntry(key); 2328 if (mod != null) { 2329 mod.addRelatedEntry(ship); 2330 ship.addRelatedEntry(mod); 2331 2332 if (id.equals(HullMods.VAST_HANGAR)) { 2333 ship.addRelatedEntry(getEntry(getHullmodEntryId(HullMods.CONVERTED_HANGAR))); 2334 } 2335 } 2336 } 2337 2338 // link ships and their built-in weapons 2339 //for (String slotId : spec.getBuiltInWeapons().keySet()) { 2340 for (String slotId : variant.getFittedWeaponSlots()) { 2341 WeaponSlotAPI slot = spec.getWeaponSlot(slotId); 2342 if (slot == null || slot.isDecorative() || slot.isSystemSlot()) { 2343 continue; 2344 } 2345 //String id = spec.getBuiltInWeapons().get(slotId); 2346 String id = variant.getWeaponId(slotId); 2347 2348 String key = getWeaponEntryId(id); 2349 CodexEntryPlugin weapon = getEntry(key); 2350 if (weapon != null) { 2351 weapon.addRelatedEntry(ship); 2352 ship.addRelatedEntry(weapon); 2353 } 2354 } 2355 2356 // link ships and their built-in fighters 2357 //for (String id : spec.getBuiltInWings()) { 2358 for (String id : variant.getWings()) { 2359 String key = getFighterEntryId(id); 2360 CodexEntryPlugin fighter = getEntry(key); 2361 if (fighter != null) { 2362 fighter.addRelatedEntry(ship); 2363 ship.addRelatedEntry(fighter); 2364 } 2365 } 2366 } 2367 } 2368 2369 2370 2371 // link fighters to their hullmods, systems, and weapons 2372 for (CodexEntryPlugin fighter : fighters.getChildren()) { 2373 if (fighter.getParam() instanceof FighterWingSpecAPI) { 2374 FighterWingSpecAPI wing = (FighterWingSpecAPI) fighter.getParam(); 2375 ShipVariantAPI variant = wing.getVariant(); 2376 ShipHullSpecAPI spec = variant.getHullSpec(); 2377 2378 // link fighters and their systems 2379 String sysId = spec.getShipSystemId(); 2380 if (sysId != null) { 2381 String key = getShipSystemEntryId(sysId); 2382 CodexEntryPlugin sys = getEntry(key); 2383 if (sys != null) { 2384 sys.addRelatedEntry(fighter); 2385 fighter.addRelatedEntry(sys); 2386 } 2387 } 2388 2389 if (!spec.isPhase()) { 2390 sysId = spec.getShipDefenseId(); 2391 String key = getShipSystemEntryId(sysId); 2392 CodexEntryPlugin sys = getEntry(key); 2393 if (sys != null) { 2394 sys.addRelatedEntry(fighter); 2395 fighter.addRelatedEntry(sys); 2396 } 2397 } 2398 2399 // link fighters and their hullmods 2400 for (String id : variant.getHullMods()) { 2401 String key = getHullmodEntryId(id); 2402 CodexEntryPlugin mod = getEntry(key); 2403 if (mod != null) { 2404 mod.addRelatedEntry(fighter); 2405 fighter.addRelatedEntry(mod); 2406 } 2407 } 2408 2409 // link fighters and their weapons 2410 for (String slotId : variant.getFittedWeaponSlots()) { 2411 String id = variant.getWeaponId(slotId); 2412 String key = getWeaponEntryId(id); 2413 CodexEntryPlugin weapon = getEntry(key); 2414 if (weapon != null) { 2415 weapon.addRelatedEntry(fighter); 2416 fighter.addRelatedEntry(weapon); 2417 } 2418 } 2419 } 2420 } 2421 2422 List<CodexEntryPlugin> dem = new ArrayList<>(); 2423 // link related weapons to seach other 2424 for (CodexEntryPlugin weapon : weapons.getChildren()) { 2425 if (weapon.getParam() instanceof WeaponSpecAPI) { 2426 WeaponSpecAPI spec = (WeaponSpecAPI) weapon.getParam(); 2427 // DEM missiles 2428 if (spec.hasTag(Tags.DAMAGE_SOFT_FLUX) && spec.hasTag(Tags.DAMAGE_SPECIAL)) { 2429 dem.add(weapon); 2430 } 2431 } 2432 } 2433 makeRelated(dem); 2434 2435 2436 2437 ListMap<CodexEntryPlugin> relatedPlanets = new ListMap<>(); 2438 String gasGiantListId = "gas_giant_related_id"; 2439 2440 List<CodexEntryPlugin> habitablePlanets = new ArrayList<>(); 2441 2442 // link planets variations with the same description 2443 for (CodexEntryPlugin planet : planets.getChildren()) { 2444 if (planet.getParam() instanceof PlanetSpecAPI) { 2445 PlanetSpecAPI spec = (PlanetSpecAPI) planet.getParam(); 2446 String id = spec.getDescriptionId(); 2447 if (id == null) id = spec.getPlanetType(); 2448 2449 relatedPlanets.add(id, planet); 2450 2451 if (spec.isNebulaCenter()) { 2452 relatedPlanets.add("nebula_related_id", planet); 2453 } 2454 if (spec.isGasGiant()) { 2455 relatedPlanets.add(gasGiantListId, planet); 2456 } 2457 if (spec.isPulsar()) { 2458 relatedPlanets.add("pulsar_related_id", planet); 2459 } 2460 if (spec.isBlackHole()) { 2461 relatedPlanets.add("black_hole_related_id", planet); 2462 } 2463 if (Misc.canPlanetTypeRollHabitable(spec)) { 2464 habitablePlanets.add(planet); 2465 } 2466 } 2467 } 2468 for (List<CodexEntryPlugin> list : relatedPlanets.values()) { 2469 makeRelated(list); 2470 } 2471 makeRelated(getPlanetEntryId(Planets.PLANET_LAVA), getPlanetEntryId(Planets.PLANET_LAVA_MINOR)); 2472 2473 // link related conditions to each other 2474 ListMap<CodexEntryPlugin> relatedDeposits = new ListMap<>(); 2475 for (String cid : ResourceDepositsCondition.COMMODITY.keySet()) { 2476 String commodityId = ResourceDepositsCondition.COMMODITY.get(cid); 2477 relatedDeposits.add(commodityId, getEntry(getConditionEntryId(cid))); 2478 } 2479 for (List<CodexEntryPlugin> list : relatedDeposits.values()) { 2480 makeRelated(list); 2481 } 2482 2483 makeRelated(getConditionEntryId(Conditions.RUINS_SCATTERED), 2484 getConditionEntryId(Conditions.RUINS_WIDESPREAD), 2485 getConditionEntryId(Conditions.RUINS_EXTENSIVE), 2486 getConditionEntryId(Conditions.RUINS_VAST)); 2487 makeRelated(getConditionEntryId(Conditions.NO_ATMOSPHERE), 2488 getConditionEntryId(Conditions.THIN_ATMOSPHERE), 2489 getConditionEntryId(Conditions.TOXIC_ATMOSPHERE), 2490 getConditionEntryId(Conditions.DENSE_ATMOSPHERE)); 2491 makeRelated(getConditionEntryId(Conditions.DECIVILIZED), 2492 getConditionEntryId(Conditions.DECIVILIZED_SUBPOP)); 2493 makeRelated(getConditionEntryId(Conditions.COLD), 2494 getConditionEntryId(Conditions.VERY_COLD)); 2495 makeRelated(getConditionEntryId(Conditions.HOT), 2496 getConditionEntryId(Conditions.VERY_HOT)); 2497 makeRelated(getConditionEntryId(Conditions.LOW_GRAVITY), 2498 getConditionEntryId(Conditions.HIGH_GRAVITY)); 2499 makeRelated(getConditionEntryId(Conditions.POOR_LIGHT), 2500 getConditionEntryId(Conditions.DARK)); 2501 makeRelated(getConditionEntryId(Conditions.HABITABLE), 2502 getConditionEntryId(Conditions.MILD_CLIMATE)); 2503 makeRelated(getConditionEntryId(Conditions.TECTONIC_ACTIVITY), 2504 getConditionEntryId(Conditions.EXTREME_TECTONIC_ACTIVITY)); 2505 2506 // link Habitable to all planets that can roll it 2507 CodexEntryPlugin habitable = getEntry(getConditionEntryId(Conditions.HABITABLE)); 2508 for (CodexEntryPlugin planet : habitablePlanets) { 2509 makeRelated(habitable, planet); 2510 } 2511 2512 2513// for (CodexEntryPlugin curr : planets.getChildren()) { 2514// if (curr.getParam() instanceof MarketConditionSpecAPI) { 2515// MarketConditionSpecAPI spec = (MarketConditionSpecAPI) curr.getParam(); 2516// } 2517// } 2518 2519 2520 // link all commmodities sharing a demand class - AI cores, survey data, luxury goods/lobster 2521 ListMap<CodexEntryPlugin> commoditiesByDemandClass = new ListMap<>(); 2522 for (CodexEntryPlugin item : items.getChildren()) { 2523 if (item.getParam() instanceof CommoditySpecAPI) { 2524 CommoditySpecAPI spec = (CommoditySpecAPI) item.getParam(); 2525 commoditiesByDemandClass.add(spec.getDemandClass(), item); 2526 } 2527 } 2528 for (CodexEntryPlugin item : commodities.getChildren()) { 2529 if (item.getParam() instanceof CommoditySpecAPI) { 2530 CommoditySpecAPI spec = (CommoditySpecAPI) item.getParam(); 2531 commoditiesByDemandClass.add(spec.getDemandClass(), item); 2532 } 2533 } 2534 for (List<CodexEntryPlugin> list : commoditiesByDemandClass.values()) { 2535 makeRelated(list); 2536 } 2537 2538 2539 // link colony items to related conditions (and gas giants) 2540 // link blueprint packages to contents 2541 List<CodexEntryPlugin> gasGiants = relatedPlanets.getList(gasGiantListId); 2542 for (CodexEntryPlugin item : items.getChildren()) { 2543 if (item.getParam() instanceof SpecialItemSpecAPI) { 2544 SpecialItemSpecAPI spec = (SpecialItemSpecAPI) item.getParam(); 2545 if (spec.hasTag(Items.TAG_COLONY_ITEM)) { 2546 InstallableItemEffect effect = ItemEffectsRepo.ITEM_EFFECTS.get(spec.getId()); 2547 Set<String> relatedConditions = effect.getConditionsRelatedToRequirements(null); 2548 for (String conditionId : relatedConditions) { 2549 CodexEntryPlugin condition = getEntry(getConditionEntryId(conditionId)); 2550 if (condition != null) { 2551 item.addRelatedEntry(condition); 2552 condition.addRelatedEntry(item); 2553 } 2554 } 2555 List<String> req = effect.getRequirements(null); 2556 if (req != null && (req.contains(ItemEffectsRepo.GAS_GIANT) || 2557 req.contains(ItemEffectsRepo.NOT_A_GAS_GIANT))) { 2558 for (CodexEntryPlugin gasGiant : gasGiants) { 2559 item.addRelatedEntry(gasGiant); 2560 gasGiant.addRelatedEntry(item); 2561 } 2562 } 2563 } else if (spec.hasTag(Items.TAG_BLUEPRINT_PACKAGE)) { 2564 SpecialItemPlugin plugin = spec.getNewPluginInstance(null); 2565 plugin.init(null); 2566 if (plugin instanceof MultiBlueprintItemPlugin) { 2567 MultiBlueprintItemPlugin multi = (MultiBlueprintItemPlugin) plugin; 2568 for (String shipId : multi.getProvidedShips()) { 2569 makeRelated(item.getId(), getShipEntryId(shipId)); 2570 } 2571 for (String fighterId : multi.getProvidedFighters()) { 2572 makeRelated(item.getId(), getFighterEntryId(fighterId)); 2573 } 2574 for (String weaponId : multi.getProvidedWeapons()) { 2575 makeRelated(item.getId(), getWeaponEntryId(weaponId)); 2576 } 2577 } 2578 } 2579 } else if (item.getParam() instanceof CommoditySpecAPI) { 2580 CommoditySpecAPI spec = (CommoditySpecAPI) item.getParam(); 2581 // TODO - anything ??? 2582 } 2583 } 2584 2585 2586 // link colony items to industries 2587 for (CodexEntryPlugin curr : items.getChildren()) { 2588 if (curr.getParam() instanceof SpecialItemSpecAPI) { 2589 SpecialItemSpecAPI spec = (SpecialItemSpecAPI) curr.getParam(); 2590 if (spec.hasTag(Items.TAG_COLONY_ITEM)) { 2591 for (String industryId : spec.getParams().split(",")) { 2592 makeRelated(curr.getId(), getIndustryEntryId(industryId.trim())); 2593 } 2594 } 2595 } 2596 } 2597 2598 2599 ListMap<String> commodityToResourceConditions = new ListMap<>(); 2600 for (String condId : ResourceDepositsCondition.COMMODITY.keySet()) { 2601 String commodityId = ResourceDepositsCondition.COMMODITY.get(condId); 2602 commodityToResourceConditions.add(commodityId, condId); 2603 } 2604 2605 for (CodexEntryPlugin curr : industries.getChildren()) { 2606 if (curr.getParam() instanceof IndustrySpecAPI) { 2607 IndustrySpecAPI spec = (IndustrySpecAPI) curr.getParam(); 2608 2609 // link industries to commodities they produce/demand 2610 for (String comId : Global.getSettings().getIndustryDemand(spec.getId())) { 2611 makeRelated(curr.getId(), getCommodityEntryId(comId)); 2612 } 2613 for (String comId : Global.getSettings().getIndustrySupply(spec.getId())) { 2614 makeRelated(curr.getId(), getCommodityEntryId(comId)); 2615 if (!spec.getId().equals(Industries.AQUACULTURE)) { 2616 for (String condId : commodityToResourceConditions.get(comId)) { 2617 makeRelated(curr.getId(), getConditionEntryId(condId)); 2618 } 2619 } 2620 } 2621 2622 // link industries to their entire upgrade chain 2623 String otherId = spec.getUpgrade(); 2624 Set<String> seen = new HashSet<>(); // handle circular upgrade chain 2625 while (otherId != null && !seen.contains(otherId)) { 2626 seen.add(otherId); 2627 makeRelated(curr.getId(), getIndustryEntryId(otherId)); 2628 IndustrySpecAPI other = Global.getSettings().getIndustrySpec(otherId); 2629 otherId = other.getUpgrade(); 2630 } 2631 otherId = spec.getDowngrade(); 2632 while (otherId != null && !seen.contains(otherId)) { 2633 seen.add(otherId); 2634 makeRelated(curr.getId(), getIndustryEntryId(otherId)); 2635 IndustrySpecAPI other = Global.getSettings().getIndustrySpec(otherId); 2636 otherId = other.getDowngrade(); 2637 } 2638 } 2639 } 2640 2641 makeRelated(getIndustryEntryId(Industries.AQUACULTURE), getConditionEntryId(Conditions.WATER_SURFACE)); 2642 makeRelated(getIndustryEntryId(Industries.AQUACULTURE), getPlanetEntryId(Planets.PLANET_WATER)); 2643 makeRelated(getIndustryEntryId(Industries.AQUACULTURE), getConditionEntryId(Conditions.VOLTURNIAN_LOBSTER_PENS)); 2644 makeRelated(getIndustryEntryId(Industries.AQUACULTURE), getIndustryEntryId(Industries.FARMING)); 2645 makeUnrelated(getIndustryEntryId(Industries.FARMING), getConditionEntryId(Conditions.VOLTURNIAN_LOBSTER_PENS)); 2646 makeUnrelated(getIndustryEntryId(Industries.FARMING), getConditionEntryId(Conditions.WATER_SURFACE)); 2647 makeUnrelated(getIndustryEntryId(Industries.FARMING), getCommodityEntryId(Commodities.LOBSTER)); 2648 makeRelated(getConditionEntryId(Conditions.WATER_SURFACE), getPlanetEntryId(Planets.PLANET_WATER)); 2649 2650 // link commodities with their resource conditions 2651 for (String comId : commodityToResourceConditions.keySet()) { 2652 for (String condId : commodityToResourceConditions.get(comId)) { 2653 makeRelated(getCommodityEntryId(comId), getConditionEntryId(condId)); 2654 } 2655 } 2656 2657 // link weapons and related hullmods; fairly manual process, may comment this out later 2658 // "look here are all the missile weapons in the game in related entries" is not very helpful 2659 // so, try to limit it to smaller subsets that are more difficult to obtain using just tags 2660 // and/or just less obvious 2661 for (CodexEntryPlugin weapon : weapons.getChildren()) { 2662 if (weapon.getParam() instanceof WeaponSpecAPI) { 2663 WeaponSpecAPI spec = (WeaponSpecAPI) weapon.getParam(); 2664 if (spec.getAIHints().contains(AIHints.SYSTEM) && 2665 spec.getPrimaryRoleStr() != null && 2666 spec.getPrimaryRoleStr().endsWith("(Fighter)")) { 2667 continue; 2668 } 2669 String id = weapon.getId(); 2670 2671 if (spec.usesAmmo() && 2672 (spec.getType() == WeaponType.BALLISTIC || spec.getType() == WeaponType.ENERGY)) { 2673 makeRelated(id, getHullmodEntryId(HullMods.MAGAZINES)); 2674 } 2675 if (spec.usesAmmo() && spec.getAmmoPerSecond() <= 0 && spec.getType() == WeaponType.MISSILE) { 2676 //makeRelated(id, getHullModEntryId(HullMods.MISSLERACKS)); 2677 if (spec.getSize() == WeaponSize.SMALL) { 2678 makeRelated(id, getHullmodEntryId(HullMods.MISSILE_AUTOLOADER)); 2679 } 2680 } 2681// if (spec.isBeam()) { 2682// makeRelated(id, getHullModEntryId(HullMods.ADVANCEDOPTICS)); 2683// makeRelated(id, getHullModEntryId(HullMods.HIGH_SCATTER_AMP)); 2684// } 2685 //if (spec.getMountType() == WeaponType.BALLISTIC || spec.getMountType() == WeaponType.HYBRID) { 2686 if (spec.getMountType() == WeaponType.HYBRID && spec.getSize() != WeaponSize.LARGE && 2687 !spec.getAIHints().contains(AIHints.PD)) { 2688 makeRelated(id, getHullmodEntryId(HullMods.BALLISTIC_RANGEFINDER)); 2689 } 2690 2691// if (!spec.isBeam() && 2692// (spec.getType() == WeaponType.ENERGY || spec.getType() == WeaponType.HYBRID)) { 2693// makeRelated(id, getHullModEntryId(HullMods.COHERER)); 2694// } 2695 } 2696 } 2697 2698 for (CodexEntryPlugin skill : skills.getChildren()) { 2699 if (skill.getParam() instanceof SkillSpecAPI) { 2700 String id = skill.getId(); 2701 SkillSpecAPI spec = (SkillSpecAPI) skill.getParam(); 2702 2703 // link skill to unlocked hullmods 2704 for (String hullmodId : spec.getAllHullmodUnlocks()) { 2705 makeRelated(id, getHullmodEntryId(hullmodId)); 2706 } 2707 2708 // link skill to unlocked abilities 2709 for (String abilityId : spec.getAllAbilityUnlocks()) { 2710 makeRelated(id, getAbilityEntryId(abilityId)); 2711 } 2712 2713 // link AI-only skills to AI cores 2714 if (spec.hasTag(Skills.TAG_AI_CORE_ONLY)) { 2715 if (spec.isAdminSkill()) { 2716 makeRelated(id, getCommodityEntryId(Commodities.ALPHA_CORE)); 2717 } else { 2718 makeRelated(id, getCommodityEntryId(Commodities.ALPHA_CORE)); 2719 makeRelated(id, getCommodityEntryId(Commodities.BETA_CORE)); 2720 makeRelated(id, getCommodityEntryId(Commodities.GAMMA_CORE)); 2721 } 2722 } 2723 } 2724 } 2725 2726 2727 for (CodexEntryPlugin hullmod : hullmods.getChildren()) { 2728 if (hullmod.getParam() instanceof HullModSpecAPI) { 2729 HullModSpecAPI spec = (HullModSpecAPI) hullmod.getParam(); 2730 2731 if (spec.hasTag(Tags.HULLMOD_DMOD)) { 2732 makeRelated(getSkillEntryId(Skills.DERELICT_CONTINGENT), getHullmodEntryId(spec.getId())); 2733 if (spec.hasTag(Tags.HULLMOD_DAMAGE)) { 2734 makeRelated(getSkillEntryId(Skills.HULL_RESTORATION), getHullmodEntryId(spec.getId())); 2735 } 2736 } 2737 2738 CargoStackAPI req = spec.getEffect().getRequiredItem(); 2739 if (req != null) { 2740 if (req.getType() == CargoItemType.RESOURCES) { 2741 makeRelated(getHullmodEntryId(spec.getId()), getCommodityEntryId(req.getCommodityId())); 2742 } else if (req.getType() == CargoItemType.SPECIAL) { 2743 makeRelated(getHullmodEntryId(spec.getId()), getItemEntryId(req.getSpecialItemSpecIfSpecial().getId())); 2744 } 2745 } 2746 } 2747 } 2748 2749 for (CodexEntryPlugin curr : shipSystems.getChildren()) { 2750 if (curr.getParam() instanceof ShipSystemSpecAPI) { 2751 ShipSystemSpecAPI spec = (ShipSystemSpecAPI) curr.getParam(); 2752 if (spec.getStatsScript() instanceof EnergyLashActivatedSystem) { 2753 makeRelated(getShipSystemEntryId(ShipSystems.ENERGY_LASH), getShipSystemEntryId(spec.getId())); 2754 } 2755 } 2756 } 2757 2758 2759 // add some custom links between skills/abilities/hullmods/etc 2760 makeRelated(getSkillEntryId(Skills.AUTOMATED_SHIPS), getHullmodEntryId(HullMods.AUTOMATED)); 2761 makeRelated(getSkillEntryId(Skills.PHASE_CORPS), getHullmodEntryId(HullMods.PHASE_FIELD)); 2762 makeRelated(getSkillEntryId(Skills.CONTAINMENT_PROCEDURES), getAbilityEntryId(Abilities.EMERGENCY_BURN)); 2763 makeRelated(getSkillEntryId(Skills.NAVIGATION), getAbilityEntryId(Abilities.SUSTAINED_BURN)); 2764 makeRelated(getSkillEntryId(Skills.SENSORS), getAbilityEntryId(Abilities.GO_DARK)); 2765 makeRelated(getSkillEntryId(Skills.SENSORS), getAbilityEntryId(Abilities.SENSOR_BURST)); 2766 makeRelated(getAbilityEntryId(Abilities.INTERDICTION_PULSE), getAbilityEntryId(Abilities.SENSOR_BURST)); 2767 makeRelated(getAbilityEntryId(Abilities.INTERDICTION_PULSE), getAbilityEntryId(Abilities.SUSTAINED_BURN)); 2768 makeRelated(getAbilityEntryId(Abilities.INTERDICTION_PULSE), getAbilityEntryId(Abilities.EMERGENCY_BURN)); 2769 makeRelated(getAbilityEntryId(Abilities.INTERDICTION_PULSE), getAbilityEntryId(Abilities.TRANSVERSE_JUMP)); 2770 2771 makeRelated(getSkillEntryId(Skills.TACTICAL_DRILLS), getCommodityEntryId(Commodities.MARINES)); 2772 makeRelated(getHullmodEntryId(HullMods.GROUND_SUPPORT), getCommodityEntryId(Commodities.MARINES)); 2773 makeRelated(getHullmodEntryId(HullMods.ADVANCED_GROUND_SUPPORT), getCommodityEntryId(Commodities.MARINES)); 2774 2775 makeRelated(getHullmodEntryId(HullMods.NEURAL_INTEGRATOR), getHullmodEntryId(HullMods.AUTOMATED)); 2776 makeRelated(getSkillEntryId(Skills.NEURAL_LINK), getHullmodEntryId(HullMods.AUTOMATED)); 2777 2778 makeRelated(getSkillEntryId(Skills.NEURAL_LINK), getHullmodEntryId(HullMods.AUTOMATED)); 2779 2780 // phase skimmer and degraded phase skimmer 2781 makeRelated(getShipSystemEntryId("displacer"), getShipSystemEntryId("displacer_degraded")); 2782 2783 // relate terminator drone and termination sequence 2784 makeRelated(getFighterEntryId("terminator_wing"), getShipSystemEntryId("drone_strike")); 2785 2786 makeRelated(getHullmodEntryId(HullMods.VAST_HANGAR), getHullmodEntryId(HullMods.CONVERTED_HANGAR)); 2787 makeRelated(getHullmodEntryId(HullMods.DESIGN_COMPROMISES), getHullmodEntryId(HullMods.CONVERTED_HANGAR)); 2788 2789 // tech mining and all ruins 2790 makeRelated(getIndustryEntryId(Industries.TECHMINING), getConditionEntryId(Conditions.RUINS_SCATTERED)); 2791 makeRelated(getIndustryEntryId(Industries.TECHMINING), getConditionEntryId(Conditions.RUINS_WIDESPREAD)); 2792 makeRelated(getIndustryEntryId(Industries.TECHMINING), getConditionEntryId(Conditions.RUINS_EXTENSIVE)); 2793 makeRelated(getIndustryEntryId(Industries.TECHMINING), getConditionEntryId(Conditions.RUINS_VAST)); 2794 2795 2796 String substrateEntryId = getItemEntryId(Items.SHROUDED_SUBSTRATE); 2797 CodexEntryPlugin substrateEntry = getEntry(substrateEntryId); 2798 if (substrateEntry != null) { 2799 for (CodexEntryPlugin dwellerPart : relatedHulls.get(Tags.DWELLER + "_allParts")) { 2800 makeRelated(substrateEntry, dwellerPart); 2801 } 2802 } 2803 makeRelated(getWeaponEntryId("vortex_launcher"), getShipEntryId("shrouded_vortex")); 2804 2805// // link factions to things they sell 2806// for (CodexEntryPlugin curr : factions.getChildren()) { 2807// if (curr.getParam() instanceof FactionSpecAPI) { 2808// FactionSpecAPI spec = (FactionSpecAPI) curr.getParam(); 2809// String id = curr.getId(); 2810// 2811// // weapons 2812// for (String key : spec.getWeaponSellFrequency().keySet()) { 2813// Float val = spec.getWeaponSellFrequency().get(key); 2814// if (val > 1f) { 2815// makeRelated(id, getWeaponEntryId(key)); 2816// } 2817// } 2818// 2819// // fighters 2820// for (String key : spec.getFighterSellFrequency().keySet()) { 2821// Float val = spec.getFighterSellFrequency().get(key); 2822// if (val > 1f) { 2823// makeRelated(id, getFighterEntryId(key)); 2824// } 2825// } 2826// 2827// // hullmods 2828// for (String key : spec.getHullmodSellFrequency().keySet()) { 2829// Float val = spec.getHullmodSellFrequency().get(key); 2830// if (val > 1f) { 2831// makeRelated(id, getHullmodEntryId(key)); 2832// } 2833// } 2834// } 2835// } 2836 } 2837 2838 2839 public static void makeRelated(CodexEntryPlugin ... plugins) { 2840 for (CodexEntryPlugin one : plugins) { 2841 for (CodexEntryPlugin two : plugins) { 2842 if (one == two) continue; 2843 one.addRelatedEntry(two); 2844 two.addRelatedEntry(one); 2845 } 2846 } 2847 } 2848 public static void makeRelated(List<CodexEntryPlugin> plugins) { 2849 for (CodexEntryPlugin one : plugins) { 2850 for (CodexEntryPlugin two : plugins) { 2851 if (one == two) continue; 2852 one.addRelatedEntry(two); 2853 two.addRelatedEntry(one); 2854 } 2855 } 2856 } 2857 public static void makeRelated(String ... ids) { 2858 for (String id1 : ids) { 2859 CodexEntryPlugin one = getEntry(id1); 2860 if (one == null) continue; 2861 for (String id2 : ids) { 2862 if (id1 == id2) continue; 2863 CodexEntryPlugin two = getEntry(id2); 2864 if (two == null) continue; 2865 one.addRelatedEntry(two); 2866 two.addRelatedEntry(one); 2867 } 2868 } 2869 } 2870 public static void makeUnrelated(String ... ids) { 2871 for (String id1 : ids) { 2872 CodexEntryPlugin one = getEntry(id1); 2873 if (one == null) continue; 2874 for (String id2 : ids) { 2875 if (id1 == id2) continue; 2876 CodexEntryPlugin two = getEntry(id2); 2877 if (two == null) continue; 2878 one.removeRelatedEntry(two); 2879 two.removeRelatedEntry(one); 2880 } 2881 } 2882 } 2883 2884 public static String getBaseHullIdEvenIfNotRestorableTo(ShipHullSpecAPI spec) { 2885 String baseId = spec.getBaseHullId(); 2886 return baseId; 2887 } 2888 2889 public static String getBaseHullId(ShipHullSpecAPI spec) { 2890 ShipHullSpecAPI base = spec.getDParentHull(); 2891 2892 if (!spec.isDefaultDHull() && !spec.isRestoreToBase()) { 2893 base = spec; 2894 } 2895 2896 if (base == null && spec.isRestoreToBase()) { 2897 base = spec.getBaseHull(); 2898 } 2899 if (base == null) { 2900 base = spec; 2901 } 2902 2903 return base.getHullId(); 2904 } 2905 2906 public static String getFleetMemberEntryId(FleetMemberAPI member) { 2907 return getShipEntryId(getFleetMemberBaseHullId(member)); 2908 } 2909 2910 public static String getFleetMemberBaseHullId(FleetMemberAPI member) { 2911 ShipHullSpecAPI spec = member.getHullSpec(); 2912 ShipHullSpecAPI base = spec.getDParentHull(); 2913 2914 if (!spec.isDefaultDHull() && !spec.isRestoreToBase()) { 2915 base = spec; 2916 } 2917 2918 if (base == null && spec.isRestoreToBase()) { 2919 base = spec.getBaseHull(); 2920 } 2921 if (base == null) { 2922 base = spec; 2923 } 2924 2925 return base.getHullId(); 2926 } 2927 2928 2929 public static String getShipEntryId(String shipId) { 2930 return "codex_hull_" + shipId; 2931 } 2932 public static String getWeaponEntryId(String weaponId) { 2933 return "codex_weapon_" + weaponId; 2934 } 2935 public static String getFighterEntryId(String wingId) { 2936 return "codex_fighter_" + wingId; 2937 } 2938 public static String getShipSystemEntryId(String shipSystemId) { 2939 return "codex_system_" + shipSystemId; 2940 } 2941 public static String getHullmodEntryId(String hullModId) { 2942 return "codex_hullmod_" + hullModId; 2943 } 2944 public static String getPlanetEntryId(String planetId) { 2945 return "codex_planet_" + planetId; 2946 } 2947 public static String getConditionEntryId(String conditionId) { 2948 return "codex_condition_" + conditionId; 2949 } 2950 public static String getItemEntryId(String itemId) { 2951 return "codex_item_" + itemId; 2952 } 2953 public static String getIndustryEntryId(String industryId) { 2954 return "codex_industry_" + industryId; 2955 } 2956 public static String getCommodityEntryId(String commodityId) { 2957 return "codex_commodity_" + commodityId; 2958 } 2959 public static String getFactionEntryId(String factionId) { 2960 return "codex_faction_" + factionId; 2961 } 2962 public static String getMechanicEntryId(String mechanicId) { 2963 return "codex_mechanic_" + mechanicId; 2964 } 2965 public static String getGalleryEntryId(String galleryId) { 2966 return "codex_gallery_" + galleryId; 2967 } 2968 public static String getSkillEntryId(String skillId) { 2969 return "codex_skill_" + skillId; 2970 } 2971 public static String getAbilityEntryId(String abilityId) { 2972 return "codex_ability_" + abilityId; 2973 } 2974 2975 public static String getIcon(String key) { 2976 return Global.getSettings().getSpriteName("codex", key); 2977 } 2978 2979 public static CodexEntryPlugin getEntry(String id) { 2980 return ENTRIES.get(id); 2981 } 2982 2983 public static void rebuildIdToEntryMap() { 2984 ENTRIES.clear(); 2985 if (ROOT == null) return; 2986 2987 ENTRIES.put(ROOT.getId(), ROOT); 2988 rebuildIdToEntryMap(ROOT); 2989 } 2990 2991 public static void rebuildIdToEntryMap(CodexEntryPlugin curr) { 2992 for (CodexEntryPlugin child : curr.getChildren()) { 2993 ENTRIES.put(child.getId(), child); 2994 if (!child.getChildren().isEmpty()) { 2995 rebuildIdToEntryMap(child); 2996 } 2997 } 2998 } 2999 3000 public static boolean codexFullyUnlocked() { 3001// if (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode")) { 3002// return true; 3003// } 3004// if (true) return true; 3005 return Global.getSettings().getBoolean("allCodexEntriesUnlocked"); 3006 } 3007 3008 3009 3010 /** 3011 * Returns a list because it could be a station or a ship with modules. 3012 * @param member 3013 * @return 3014 */ 3015 public static List<CodexEntryPlugin> createTempFleetMemberEntry(FleetMemberAPI member) { 3016 ShipHullSpecAPI spec = member.getHullSpec(); 3017 ShipVariantAPI variant = member.getVariant(); 3018 String entryId = UUID.randomUUID().toString(); 3019 3020 boolean station = spec.getHints().contains(ShipTypeHints.STATION); 3021 boolean limited = variant.hasTag(Tags.SHIP_LIMITED_TOOLTIP); 3022 if (!SharedUnlockData.get().isPlayerAwareOfShip(variant.getHullSpec().getRestoredToHullId())) { 3023 limited |= variant.hasTag(Tags.LIMITED_TOOLTIP_IF_LOCKED); 3024 limited |= member.getHullSpec().hasTag(Tags.LIMITED_TOOLTIP_IF_LOCKED); 3025 } 3026 boolean limited2 = limited; 3027 3028 3029 Description desc = Global.getSettings().getDescription(spec.getDescriptionId(), Type.SHIP); 3030 String entryName = variant.getFullDesignationWithHullNameForShip(); 3031 if (!station && member.getShipName() != null && !member.getShipName().isBlank() && !member.isFighterWing()) { 3032 entryName = member.getShipName() + ", " + entryName; 3033 } 3034 if (limited) entryName = desc.getText2(); 3035 3036 CodexEntryV2 entry = new CodexEntryV2(entryId, entryName, null, member) { 3037 @Override 3038 public void createTitleForList(TooltipMakerAPI info, float width, ListMode mode) { 3039 //info.addPara(spec.getHullName(), Misc.getBasePlayerColor(), 0f); 3040 //info.addPara(member.getShipName() + " (" + spec.getHullName() + ")", Misc.getBasePlayerColor(), 0f); 3041 String name = member.getShipName(); 3042 if (name == null || name.isBlank()) { 3043 name = spec.getHullName(); 3044 } 3045 info.addPara(name, Misc.getBasePlayerColor(), 0f); 3046 if (limited2) { 3047 info.addPara(Misc.ucFirst(desc.getText2().toLowerCase()), Misc.getGrayColor(), 0f); 3048 } else if (spec.hasDesignation() && !spec.getDesignation().equals(spec.getHullName())) { 3049 //info.addPara(Misc.ucFirst(variant.getFullDesignationForShip().toLowerCase()), Misc.getGrayColor(), 0f); 3050 //info.addPara(Misc.ucFirst(spec.getHullNameWithDashClass().toLowerCase()), Misc.getGrayColor(), 0f); 3051 info.addPara(Misc.ucFirst(variant.getFullDesignationForShip().toLowerCase()), Misc.getGrayColor(), 0f); 3052 } else if (spec.getHints().contains(ShipTypeHints.STATION)) { 3053 info.addPara("Station", Misc.getGrayColor(), 0f); 3054 } else if (spec.getHints().contains(ShipTypeHints.MODULE)) { 3055 info.addPara("Module", Misc.getGrayColor(), 0f); 3056 } 3057 } 3058 @Override 3059 public boolean matchesTags(Set<String> tags) { 3060 return false; 3061 } 3062 @Override 3063 public boolean isVisible() { 3064 // only shows up in related lists because not added to its own parent category 3065 return true; 3066 } 3067 @Override 3068 public boolean isLocked() { 3069 return false; 3070 } 3071 }; 3072 3073 linkFleetMemberEntryToRelated(entry, member, true); 3074 3075 CodexEntryPlugin parent = getEntry(CAT_SHIPS); 3076 if (station) parent = getEntry(CAT_STATIONS); 3077 entry.setParent(parent); 3078 3079 List<CodexEntryPlugin> result = new ArrayList<>(); 3080 result.add(entry); 3081 3082 List<CodexEntryPlugin> modules = addModulesForVariant(member.getVariant(), false, entry, parent); 3083 result.addAll(modules); 3084 3085 return result; 3086 3087 } 3088 3089 /** 3090 * This method assumes the entry is a specific fleet member with a loadout, and NOT an empty hull. 3091 * Meaning, this entry was added on the fly such as e.g. from a tooltip of a fleet member. 3092 * The linking for empty hull entries is done in the linkRelatedEntries() method. 3093 * 3094 * @param entry 3095 * @param member 3096 * @param parentOfModuleEntry 3097 * @param parentOfModule 3098 */ 3099 public static void linkFleetMemberEntryToRelated(CodexEntryPlugin entry, 3100 FleetMemberAPI member, boolean linkCaptainSkills) { 3101 ShipHullSpecAPI spec = member.getHullSpec(); 3102 ShipVariantAPI variant = member.getVariant(); 3103 CodexEntryPlugin hullEntry = getEntry(getFleetMemberEntryId(member)); 3104 if (hullEntry != null) { 3105 for (CodexEntryPlugin rel : hullEntry.getRelatedEntries()) { 3106 if (rel.hasTag(TAG_EMPTY_MODULE)) continue; 3107// if (rel.getId().equals("codex_hull_station3")) { 3108// System.out.println("Relid: " + rel.getId()); 3109// } 3110 // skip all entries linking modules back to their empty hull 3111 // since we'll be linking to a non-empty hull 3112 // or, if we're viewing a module directly, don't link to parent hull at all 3113 // if getParam2() is a fleet member, we don't want to link to it 3114 if (rel.getParam2() instanceof FleetMemberAPI) { 3115 continue; 3116// FleetMemberAPI otherMember = (FleetMemberAPI) rel.getParam2(); 3117// String h1 = getFleetMemberBaseHullId(otherMember); 3118// String h2 = getFleetMemberBaseHullId(parentOfModule); 3119// if (h1.equals(h2)) continue; 3120 } 3121 entry.addRelatedEntry(rel); 3122 rel.addRelatedEntry(entry); 3123 } 3124 } 3125 3126 String sysId = spec.getShipSystemId(); 3127 if (sysId != null && !sysId.isBlank()) { 3128 String key = getShipSystemEntryId(sysId); 3129 CodexEntryPlugin sys = getEntry(key); 3130 if (sys != null) { 3131 sys.addRelatedEntry(entry); 3132 entry.addRelatedEntry(sys); 3133 3134 // link ship systems and ships with their drones, if any 3135 if (sys.getParam() instanceof ShipSystemSpecAPI) { 3136 ShipSystemSpecAPI sysSpec = (ShipSystemSpecAPI) sys.getParam(); 3137 if (sysSpec.getDroneVariant() != null) { 3138 ShipVariantAPI drone = Global.getSettings().getVariant(sysSpec.getDroneVariant()); 3139 if (drone != null) { 3140 String droneHullId = getBaseHullId(drone.getHullSpec()); 3141 String droneEntryId = getShipEntryId(droneHullId); 3142 makeRelated(entry.getId(), droneEntryId); 3143 makeRelated(sys.getId(), droneEntryId); 3144 } 3145 } 3146 } 3147 } 3148 } 3149 3150 3151 for (String slotId : variant.getFittedWeaponSlots()) { 3152 String wid = variant.getWeaponId(slotId); 3153 CodexEntryPlugin other = getEntry(getWeaponEntryId(wid)); 3154 if (other != null) { 3155 entry.addRelatedEntry(other); 3156 other.addRelatedEntry(entry); 3157 } 3158 } 3159 for (String wingId : variant.getFittedWings()) { 3160 CodexEntryPlugin other = getEntry(getFighterEntryId(wingId)); 3161 if (other != null) { 3162 entry.addRelatedEntry(other); 3163 other.addRelatedEntry(entry); 3164 } 3165 } 3166 for (String hullmodId : variant.getHullMods()) { 3167 CodexEntryPlugin other = getEntry(getHullmodEntryId(hullmodId)); 3168 if (other != null) { 3169 entry.addRelatedEntry(other); 3170 other.addRelatedEntry(entry); 3171 } 3172 } 3173 3174 if (linkCaptainSkills && member.getCaptain() != null && !member.getCaptain().isDefault()) { 3175 for (SkillLevelAPI sl : member.getCaptain().getStats().getSkillsCopy()) { 3176 if (sl.getLevel() > 0 && sl.getSkill().isCombatOfficerSkill()) { 3177 CodexEntryPlugin other = getEntry(getSkillEntryId(sl.getSkill().getId())); 3178 if (other != null) { 3179 entry.addRelatedEntry(other); 3180 other.addRelatedEntry(entry); 3181 } 3182 } 3183 } 3184 } 3185 } 3186 3187 public static boolean hasUnlockedEntry(String entryId) { 3188 CodexEntryPlugin entry = getEntry(entryId); 3189 return entry != null && !entry.isLocked(); 3190 } 3191 3192 public static boolean hasUnlockedEntryForShip(String hullId) { 3193 CodexEntryPlugin entry = getEntry(getShipEntryId(hullId)); 3194 return entry != null && !entry.isLocked(); 3195 } 3196 3197 public static void unlinkAndRemoveTempEntry(CodexEntryPlugin entry) { 3198 if (entry == null) return; 3199 for (CodexEntryPlugin rel : entry.getRelatedEntries()) { 3200 rel.removeRelatedEntry(entry.getId()); 3201 entry.removeRelatedEntry(rel.getId()); 3202 } 3203 ENTRIES.remove(entry.getId()); 3204 3205 Set<String> remove = new LinkedHashSet<>(); 3206 for (String id : SEEN_STATION_MODULES.keySet()) { 3207 if (SEEN_STATION_MODULES.get(id) == entry) { 3208 remove.add(id); 3209 } 3210 } 3211 for (String id : remove) { 3212 SEEN_STATION_MODULES.remove(id); 3213 } 3214 3215 if (entry.getParent() != null) { 3216 entry.getParent().getChildren().remove(entry); 3217 } 3218 } 3219} 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229