Starsector API
Loading...
Searching...
No Matches
CodexDataV2.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.codex;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.Comparator;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.LinkedHashMap;
9import java.util.LinkedHashSet;
10import java.util.List;
11import java.util.Map;
12import java.util.Set;
13import java.util.UUID;
14
15import java.awt.Color;
16
17import org.json.JSONException;
18
19import com.fs.starfarer.api.Global;
20import com.fs.starfarer.api.ModPlugin;
21import com.fs.starfarer.api.ModSpecAPI;
22import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
23import com.fs.starfarer.api.campaign.CargoStackAPI;
24import com.fs.starfarer.api.campaign.PlanetSpecAPI;
25import com.fs.starfarer.api.campaign.SpecialItemPlugin;
26import com.fs.starfarer.api.campaign.SpecialItemSpecAPI;
27import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
28import com.fs.starfarer.api.campaign.impl.items.MultiBlueprintItemPlugin;
29import com.fs.starfarer.api.characters.MarketConditionSpecAPI;
30import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI;
31import com.fs.starfarer.api.characters.SkillSpecAPI;
32import com.fs.starfarer.api.combat.DamageType;
33import com.fs.starfarer.api.combat.ShipAPI;
34import com.fs.starfarer.api.combat.ShipAPI.HullSize;
35import com.fs.starfarer.api.combat.ShipHullSpecAPI;
36import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints;
37import com.fs.starfarer.api.combat.ShipSystemSpecAPI;
38import com.fs.starfarer.api.combat.ShipVariantAPI;
39import com.fs.starfarer.api.combat.WeaponAPI.AIHints;
40import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
41import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
42import com.fs.starfarer.api.fleet.FleetMemberAPI;
43import com.fs.starfarer.api.fleet.FleetMemberType;
44import com.fs.starfarer.api.impl.SharedUnlockData;
45import com.fs.starfarer.api.impl.campaign.econ.ResourceDepositsCondition;
46import com.fs.starfarer.api.impl.campaign.econ.impl.InstallableItemEffect;
47import com.fs.starfarer.api.impl.campaign.econ.impl.ItemEffectsRepo;
48import com.fs.starfarer.api.impl.campaign.ids.Abilities;
49import com.fs.starfarer.api.impl.campaign.ids.Commodities;
50import com.fs.starfarer.api.impl.campaign.ids.Conditions;
51import com.fs.starfarer.api.impl.campaign.ids.HullMods;
52import com.fs.starfarer.api.impl.campaign.ids.Industries;
53import com.fs.starfarer.api.impl.campaign.ids.Items;
54import com.fs.starfarer.api.impl.campaign.ids.Planets;
55import com.fs.starfarer.api.impl.campaign.ids.ShipSystems;
56import com.fs.starfarer.api.impl.campaign.ids.Skills;
57import com.fs.starfarer.api.impl.campaign.ids.Tags;
58import com.fs.starfarer.api.impl.combat.threat.EnergyLashActivatedSystem;
59import com.fs.starfarer.api.loading.AbilitySpecAPI;
60import com.fs.starfarer.api.loading.Description;
61import com.fs.starfarer.api.loading.Description.Type;
62import com.fs.starfarer.api.loading.FighterWingSpecAPI;
63import com.fs.starfarer.api.loading.HullModSpecAPI;
64import com.fs.starfarer.api.loading.IndustrySpecAPI;
65import com.fs.starfarer.api.loading.WeaponSlotAPI;
66import com.fs.starfarer.api.loading.WeaponSpecAPI;
67import com.fs.starfarer.api.loading.WingRole;
68import com.fs.starfarer.api.loading.WithSourceMod;
69import com.fs.starfarer.api.ui.CustomPanelAPI;
70import com.fs.starfarer.api.ui.TagDisplayAPI;
71import com.fs.starfarer.api.ui.TooltipMakerAPI;
72import com.fs.starfarer.api.ui.UIPanelAPI;
73import com.fs.starfarer.api.util.CountingMap;
74import com.fs.starfarer.api.util.ListMap;
75import com.fs.starfarer.api.util.Misc;
76
77public class CodexDataV2 {
78
79 public static boolean WITH_GAME_MECHANICS_CAT = false;
80 public static boolean WITH_CUSTOM_EXAMPLE_CAT = false;
81
82 public static boolean USE_KEY_NAMES_FOR_GALLERY = false;
83
84 public static CodexEntryPlugin ROOT;
85 public static Map<String, CodexEntryPlugin> ENTRIES = new LinkedHashMap<>();
86 public static Map<String, CodexEntryPlugin> SEEN_STATION_MODULES = new LinkedHashMap<>();
87
88 public static String TAG_EMPTY_MODULE = "empty_module";
89
90 public static String CAT_ROOT = "root";
91 public static String CAT_SHIPS = "ships";
92 public static String CAT_STATIONS = "stations";
93 public static String CAT_FIGHTERS = "fighters";
94 public static String CAT_WEAPONS = "weapons";
95 public static String CAT_HULLMODS = "hullmods";
96 public static String CAT_SHIP_SYSTEMS = "ship_systems";
97 public static String CAT_SPECIAL_ITEMS = "special_items";
98 public static String CAT_INDUSTRIES = "industries";
99 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);
123
126
127 // TODO: rearrange these as needed depending on where they show up as related
133 //CAT_SORT_RELATED_ENTRIES.put(CAT_TERRAIN, 145f);
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);
204
206 plugin.onAboutToStartGeneratingCodex();
207 }
208
215
221 //CodexEntryPlugin terrain = createTerrainCategory();
222 //CodexEntryPlugin factions = createFactionsCategory();
224
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
248 ROOT.addChild(mechanics);
249 }
250
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);
262 populatePlanetaryConditions(conditions);
264 populateCommodities(commodities, items);
265 populateIndustries(industries);
266 populateSkills(skills);
267 populateAbilities(abilities);
268 //populateTerrain(terrain);
269 //populateFactions(factions);
270 populateGallery(gallery);
272
275 }
276
278
280 plugin.onAboutToLinkCodexEntries();
281 }
282
284
288 }
289
292 }
293
295 plugin.onCodexDataGenerated();
296 }
297 }
298
299 public static void addUnknownEntry() {
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);
328 text.addPara("UNKNOWN ENTRY\n\nPlease register your Tri-Tachyon datapad to receive the latest news and updates.", 0f);
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
345 if (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode")) {
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);
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
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) {
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 }
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 }
431 tags.addGroup(opad * 1f);
432
433 tags.checkAll();
434 }
435
436 };
437 return hullmods;
438 }
439
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);
474
475 text.addPara("Spacer's manual description test.", 0f);
476
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 }
497 return cat;
498 }
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
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);
544 tags.addGroup(0f);
545
546 tags.checkAll();
547 }
548 };
549 return cat;
550 }
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);
584 tags.addGroup(0f);
585
586 tags.checkAll();
587 }
588 };
589 return cat;
590 }
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);
615 tags.addGroup(0f);
616
617 tags.checkAll();
618 }
619 };
620 return cat;
621 }
622
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();
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);
665 tags.addGroup(opad);
666
667 tags.checkAll();
668 }
669 };
670 return cat;
671 }
672
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;
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 }
707 tags.addGroup(0f);
708
709 tags.checkAll();
710 }
711 };
712 return shipSystems;
713 }
714
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
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 }
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);
764 tags.addGroup(opad);
765
766 tags.checkAll();
767 }
768 };
769 return fighters;
770 }
771
774 return custom;
775 }
776
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
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 }
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);
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);
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);
883 tags.addGroup(pad);
884
885 tags.checkAll();
886 }
887
888 };
889 return weapons;
890 }
891
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
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);
936 tags.addGroup(opad);
937
938 tags.checkAll();
939 }
940 };
941 skills.setRetainOrderOfChildren(true);
942 return skills;
943 }
944
946 CodexEntryV2 abilities = new CodexEntryV2(CAT_ABILITIES, "Abilities", getIcon(CAT_ABILITIES));
947 return abilities;
948 }
949
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;
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);
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);
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
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;
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 }
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()) {
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) {
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() {
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) {
1564
1565 if (spec.hasTag(Tags.HIDE_IN_CODEX)) continue;
1566
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() {
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() {
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
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
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;
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
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) {
2294 if (sysSpec.getDroneVariant() != null) {
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
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)) {
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) {
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));
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));
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
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
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
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);
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
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) {
3137 if (sysSpec.getDroneVariant() != null) {
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
static SettingsAPI getSettings()
Definition Global.java:57
boolean isPlayerAwareOfCommodity(String commodityId)
boolean isPlayerAwareOfIndustry(String industryId)
boolean isPlayerAwareOfCondition(String conditionId)
static Map< String, InstallableItemEffect > ITEM_EFFECTS
static final String SHOW_IN_CODEX_AS_SHIP
Definition Tags.java:510
static final String LIMITED_TOOLTIP_IF_LOCKED
Definition Tags.java:203
static final String CODEX_REQUIRE_RELATED
Definition Tags.java:518
static String getFleetMemberEntryId(FleetMemberAPI member)
static String getIndustryEntryId(String industryId)
static void makeUnrelated(String ... ids)
static String getFleetMemberBaseHullId(FleetMemberAPI member)
static String getGalleryEntryId(String galleryId)
static String getAptitudeName(SkillSpecAPI spec)
static Map< String, Float > CAT_SORT_RELATED_ENTRIES
static void populateGallery(CodexEntryPlugin parent)
static void linkFleetMemberEntryToRelated(CodexEntryPlugin entry, FleetMemberAPI member, boolean linkCaptainSkills)
static String getFighterEntryId(String wingId)
static void populateSkills(CodexEntryPlugin parent)
static String getCommodityEntryId(String commodityId)
static void populateAbilities(CodexEntryPlugin parent)
static String getShipSystemEntryId(String shipSystemId)
static void makeRelated(CodexEntryPlugin ... plugins)
static void populateIndustries(CodexEntryPlugin parent)
static List< CodexEntryPlugin > createTempFleetMemberEntry(FleetMemberAPI member)
static String getPlanetEntryId(String planetId)
static void populateWeapons(CodexEntryPlugin parent)
static String getHullmodEntryId(String hullModId)
static void populateFighters(CodexEntryPlugin parent)
static void makeRelated(List< CodexEntryPlugin > plugins)
static void populateHullMods(CodexEntryPlugin parent)
static void populateStarsAndPlanets(CodexEntryPlugin parent)
static String getWeaponEntryId(String weaponId)
static void populateShipSystems(CodexEntryPlugin parent, CodexEntryPlugin ships)
static CodexEntryPlugin addModuleEntry(CodexEntryPlugin parent, CodexEntryPlugin entryForParentShip, FleetMemberAPI member, boolean isEmptyHull)
static CodexEntryV2 createStarsAndPlanetsCategory()
static Map< String, CodexEntryPlugin > ENTRIES
static String getBaseHullIdEvenIfNotRestorableTo(ShipHullSpecAPI spec)
static String getFactionEntryId(String factionId)
static void rebuildIdToEntryMap(CodexEntryPlugin curr)
static void populateShipsAndStations(CodexEntryPlugin ships, CodexEntryPlugin stations)
static Map< String, CodexEntryPlugin > SEEN_STATION_MODULES
static void populateCommodities(CodexEntryPlugin commodities, CodexEntryPlugin items)
static String getConditionEntryId(String conditionId)
static void populateSpecialItems(CodexEntryPlugin parent)
static String getAbilityEntryId(String abilityId)
static void unlinkAndRemoveTempEntry(CodexEntryPlugin entry)
static String getShipEntryId(String shipId)
static List< CodexEntryPlugin > addModulesForVariant(ShipVariantAPI variant, boolean isEmptyHull, CodexEntryPlugin entry, CodexEntryPlugin parent)
static void populatePlanetaryConditions(CodexEntryPlugin parent)
static String getBaseHullId(ShipHullSpecAPI spec)
static void setCatSort(CodexEntryPlugin root, Set< CodexEntryPlugin > seen)
static boolean hasUnlockedEntryForShip(String hullId)
static String getItemEntryId(String itemId)
static boolean hasUnlockedEntry(String entryId)
static CodexEntryV2 createPlanetaryConditionsCategory()
static String getSkillEntryId(String skillId)
static CodexEntryPlugin getEntry(String id)
static String getMechanicEntryId(String mechanicId)
void setParent(CodexEntryPlugin parent)
void setRetainOrderOfChildren(boolean retainOrderOfChildren)
void addRelatedEntry(CodexEntryPlugin entry)
void add(K key, int quantity)
List< V > getList(String key)
Definition ListMap.java:27
List< V > get(Object key)
Definition ListMap.java:24
void add(String key, V value)
Definition ListMap.java:10
static String ucFirst(String str)
Definition Misc.java:559
static Color getBasePlayerColor()
Definition Misc.java:833
static Color getGrayColor()
Definition Misc.java:826
static Color getHighlightColor()
Definition Misc.java:792
static boolean canPlanetTypeRollHabitable(PlanetSpecAPI spec)
Definition Misc.java:6792
static Color getDarkPlayerColor()
Definition Misc.java:836
List< ModPlugin > getEnabledModPlugins()
AbilitySpecAPI getAbilitySpec(String abilityId)
Description getDescription(String id, Type type)
String getSpriteName(String category, String id)
List< WeaponSpecAPI > getActuallyAllWeaponSpecs()
List< ShipSystemSpecAPI > getAllShipSystemSpecs()
ShipVariantAPI getVariant(String variantId)
List< IndustrySpecAPI > getAllIndustrySpecs()
SkillSpecAPI getSkillSpec(String skillId)
ShipHullSpecAPI getHullSpec(String hullId)
Set< String > getIndustryDemand(String industryId)
IndustrySpecAPI getIndustrySpec(String industryId)
List< HullModSpecAPI > getAllHullModSpecs()
boolean getBoolean(String key)
List< String > getSpriteKeys(String category)
List< String > getSortedAbilityIds()
FleetMemberAPI createFleetMember(FleetMemberType type, String variantOrWingId)
List< SpecialItemSpecAPI > getAllSpecialItemSpecs()
List< PlanetSpecAPI > getAllPlanetSpecs()
List< CommoditySpecAPI > getAllCommoditySpecs()
List< ShipHullSpecAPI > getAllShipHullSpecs()
Set< String > getIndustrySupply(String industryId)
List< MarketConditionSpecAPI > getAllMarketConditionSpecs()
List< FighterWingSpecAPI > getAllFighterWingSpecs()
SpecialItemSpecAPI getSpecialItemSpecIfSpecial()
SpecialItemPlugin getNewPluginInstance(CargoStackAPI stack)
MutableCharacterStatsAPI getStats()
EnumSet< ShipTypeHints > getHints()
WeaponSlotAPI getWeaponSlot(String slotId)
Map< String, String > getStationModules()
ShipVariantAPI getModuleVariant(String slotId)
Collection< String > getFittedWeaponSlots()
void addRelatedEntry(CodexEntryPlugin entry)
void removeRelatedEntry(CodexEntryPlugin entry)
void setRetainOrderOfChildren(boolean retainOrderOfChildren)
EnumSet< WeaponAPI.AIHints > getAIHints()
TooltipMakerAPI createUIElement(float width, float height, boolean withScroller)
void updateUIElementSizeAndMakeItProcessInput(TooltipMakerAPI element)
UIPanelAPI wrapTooltipWithBox(TooltipMakerAPI tooltip)
PositionAPI inTL(float xPad, float yPad)
PositionAPI setSize(float width, float height)
PositionAPI inTR(float xPad, float yPad)
void setTotalOverrideForCurrentGroup(int totalOverrideForCurrentGroup)
void beginGroup(boolean mutuallyExclusive, String allText)
void setGroupChecked(int index, boolean checked)
LabelAPI addPara(String format, float pad, Color hl, String... highlights)
PositionAPI addComponent(UIComponentAPI custom)