worldbox-observer-mod/IdleSpectator/Events/Catalogs/EventCatalog.Libraries.cs
DazedAnon ba58a9ff38 feat(saga): deepen session cast stakes and cut idle hitch spikes
- Keep LifeSagaSession across Clear; wipe on map load/clear with bind key
- Soft-bias Prefer/cross-MC/cast and prefer roster MCs in aftermath/recover/park
- Resolve war/plot principals plus discrete/WorldLog labels from live libraries
- Amortize roster world scans, gate Relayout, and add hitch probe/save-slot helpers
2026-07-22 14:37:36 -05:00

893 lines
29 KiB
C#

using System;
using System.Collections.Generic;
namespace IdleSpectator;
/// <summary>
/// Live AssetManager library event dials (powers, genes, …).
/// Strengths/labels from <c>event-catalog.json</c> <c>libraries</c>; signal predicates stay in C#.
/// </summary>
public static partial class EventCatalog
{
private static LibraryCatalogDefaults LibOr(
string key,
float ambient,
float signal,
string category,
string label,
bool ambientCreatesInterest)
{
if (EventCatalogConfig.TryGetLibraryDefaults(key, out LibraryCatalogDefaults d) && d != null)
{
return d;
}
return new LibraryCatalogDefaults
{
AmbientStrength = ambient,
SignalStrength = signal,
Category = category,
Label = label,
AmbientCreatesInterest = ambientCreatesInterest
};
}
public static class Spell
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("spell", 48f, 88f, "Spectacle", "{a} casts {id}", false);
HashSet<string> signals = d.SignalIds.Count > 0
? d.SignalIds
: new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"summon_lightning", "summon_tornado", "cast_curse", "cast_fire",
"cast_blood_rain", "summon_meteor", "teleport"
};
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveSpellIds,
d.Category,
d.Label,
d.AmbientStrength,
signals,
d.SignalStrength,
signalPredicate: IsSpectacleSpell,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "spell");
}
/// <summary>
/// Spectacle casts own the camera; brief FX / utility (silence, shield, cure, grass) stay B.
/// </summary>
private static bool IsSpectacleSpell(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
if (s.Contains("silence")
|| s.Contains("shield")
|| s.Contains("cure")
|| s.Contains("grass")
|| s.Contains("vegetation")
|| s.Contains("skeleton"))
{
return false;
}
return s.StartsWith("summon_", StringComparison.Ordinal)
|| s.Contains("meteor")
|| s.Contains("tornado")
|| s.Contains("lightning")
|| s.Contains("teleport")
|| s.Contains("curse")
|| s.Contains("blood_rain")
|| s.Contains("cast_fire");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("spell", 48f, 88f, "Spectacle", "{a} casts {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 55f, libraryKind: "spell");
}
}
public static class Power
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static bool IsSpectaclePower(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
// God-tool editors / drip brushes stay B.
if (s.Contains("_edit")
|| s.Contains("traits_")
|| s.Contains("equipment_rain")
|| s.EndsWith("_brush", StringComparison.Ordinal)
|| s.Contains("draw_")
|| s.Contains("paint")
|| s.StartsWith("boat_", StringComparison.Ordinal))
{
return false;
}
// Creature drop powers that match live spectacle actors (evil_mage, necromancer, …).
if (WorldActivityScanner.IsSpectacleAssetId(s))
{
return true;
}
return s.Contains("meteor")
|| s.Contains("earthquake")
|| s.Contains("tornado")
|| s.Contains("volcano")
|| s.Contains("nuke")
|| s.Contains("bomb")
|| s.Contains("rain_blood")
|| s.Contains("blood_rain")
|| s.Contains("hell")
|| s.Contains("lightning")
|| s.Contains("storm")
|| s.Contains("acid")
|| s.Contains("fire")
|| s.Contains("disaster")
|| s.Contains("dragon")
|| s.Contains("demon")
|| s.Contains("plague")
|| s.Contains("zombie")
|| s.Contains("crabzilla")
|| s == "ufo"
|| s.Contains("god_finger")
|| s.Contains("cold_one")
|| s.Contains("heatray")
|| s.Contains("curse")
|| s.Contains("lava")
|| s.Contains("ash")
|| s.Contains("golden_brain")
|| s.Contains("corrupted_brain")
|| s.Contains("infection")
|| s.Contains("mage")
|| s.Contains("necromancer")
|| s.Contains("druid");
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("power", 28f, 92f, "Spectacle", "God power: {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLivePowerIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
signalPredicate: IsSpectaclePower,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "power");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("power", 28f, 92f, "Spectacle", "God power: {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 30f, libraryKind: "power");
}
}
public static class Item
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static bool IsLegendaryItem(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
// Only top-tier materials own the camera. Steel/silver/bone gear is soak noise.
return s.Contains("mythril") || s.Contains("adamantine");
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("item", 36f, 80f, "Item", "{a} gains {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveItemIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
signalPredicate: IsLegendaryItem,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "item");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("item", 36f, 80f, "Item", "{a} gains {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 40f, libraryKind: "item");
}
}
public static class SubspeciesTrait
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static bool IsDramatic(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
return s.Contains("immortal")
|| s.Contains("giant")
|| s.Contains("tiny")
|| s.Contains("fire")
|| s.Contains("frost")
|| s.Contains("poison")
|| s.Contains("plague")
|| s.Contains("magic")
|| s.Contains("divine")
|| s.Contains("demon")
|| s.Contains("undead")
|| s.Contains("dragon")
|| s.Contains("evolve")
|| s.Contains("mutate")
|| s.Contains("mutation")
|| s.Contains("death")
|| s.Contains("blood")
|| s.Contains("horror")
|| s.Contains("tentacle");
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("subspeciesTrait", 34f, 78f, "Evolution", "{a} evolves {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveSubspeciesTraitIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
signalPredicate: IsDramatic,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "subspecies_trait");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("subspeciesTrait", 34f, 78f, "Evolution", "{a} evolves {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 36f, libraryKind: "subspecies_trait");
}
}
public static class Gene
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
// Gene gain/remove is catalog noise - no Signal predicate, all B (like phenotypes).
LibraryCatalogDefaults d = LibOr("gene", 30f, 70f, "Genetics", "{a} gains gene {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveGeneIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "gene");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("gene", 30f, 70f, "Genetics", "{a} gains gene {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 30f, libraryKind: "gene");
}
}
public static class Phenotype
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
// Skin/shade phenotypes are ambient noise - no Signal predicate, all B.
LibraryCatalogDefaults d = LibOr("phenotype", 28f, 60f, "Genetics", "{a} shows {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLivePhenotypeIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "phenotype");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("phenotype", 28f, 60f, "Genetics", "{a} shows {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 28f, libraryKind: "phenotype");
}
}
public static class WorldLaw
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("worldLaw", 40f, 70f, "WorldLaw", "Law changed: {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveWorldLawIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
signalPredicate: IsDramaticWorldLaw,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "worldlaw");
}
private static bool IsDramaticWorldLaw(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
// God-law toggles are mostly UI spam. Keep era-defining / crisis switches only.
return s.Contains("cursed")
|| s.Contains("forever_")
|| s.Contains("disaster")
|| s.Contains("rebell");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("worldLaw", 40f, 70f, "WorldLaw", "Law changed: {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 40f, libraryKind: "worldlaw");
}
}
public static class Biome
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("biome", 26f, 50f, "Biome", "Biome shifts: {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveBiomeIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "biome");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("biome", 26f, 50f, "Biome", "Biome shifts: {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 26f, libraryKind: "biome");
}
}
private static bool IsDramaticMetaTrait(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
return s.Contains("war")
|| s.Contains("blood")
|| s.Contains("death")
|| s.Contains("fire")
|| s.Contains("magic")
|| s.Contains("divine")
|| s.Contains("demon")
|| s.Contains("hate")
|| s.Contains("love")
|| s.Contains("xenophob")
|| s.Contains("expans")
|| s.Contains("conquest")
|| s.Contains("slave")
|| s.Contains("zealot")
|| s.Contains("fanatic")
|| s.Contains("schism")
|| s.Contains("royal")
|| s.Contains("conscript")
|| s.Contains("join_or_die")
|| s.Contains("necro")
|| s.Contains("evil")
|| s.Contains("shattered")
|| s.Contains("titan")
|| s.Contains("ethno")
|| s.Contains("flame")
|| s.Contains("ice_weapon")
|| s.Contains("shotgun")
|| s.Contains("blaster");
}
/// <summary>
/// Culture camera: hard politics only. Lovers / layouts / craft skins are soak noise.
/// </summary>
private static bool IsDramaticCultureTrait(string id)
{
if (string.IsNullOrEmpty(id))
{
return false;
}
string s = id.ToLowerInvariant();
if (s.Contains("lover")
|| s.Contains("city_layout")
|| s.StartsWith("craft_", StringComparison.Ordinal)
|| s.Contains("ethno")
|| s.Contains("reading")
|| s.Contains("statue")
|| s.Contains("tower")
|| s.Contains("gossip"))
{
return false;
}
return s.Contains("conscript")
|| s.Contains("xenophob")
|| s.Contains("expans")
|| s.Contains("join_or_die")
|| s.Contains("happiness_from_war")
|| s.Contains("warriors_ascension")
|| s.Contains("shattered_crown");
}
public static class CultureTrait
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("cultureTrait", 34f, 74f, "Culture", "{a} culture gains {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveCultureTraitIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
signalPredicate: IsDramaticCultureTrait,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "culture_trait");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("cultureTrait", 34f, 74f, "Culture", "{a} culture gains {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 34f, libraryKind: "culture_trait");
}
}
public static class ReligionTrait
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
LibraryCatalogDefaults d = LibOr("religionTrait", 34f, 74f, "Religion", "{a} faith gains {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveReligionTraitIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
signalPredicate: IsDramaticMetaTrait,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "religion_trait");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("religionTrait", 34f, 74f, "Religion", "{a} faith gains {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 34f, libraryKind: "religion_trait");
}
}
public static class ClanTrait
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
// Clan blood / vein traits are gene-like soak noise - all B.
LibraryCatalogDefaults d = LibOr("clanTrait", 32f, 72f, "Clan", "{a} clan gains {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveClanTraitIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "clan_trait");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("clanTrait", 32f, 72f, "Clan", "{a} clan gains {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 32f, libraryKind: "clan_trait");
}
}
public static class LanguageTrait
{
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
private static int _appliedGeneration = -1;
public static void InvalidateOverlays()
{
Entries.Clear();
_appliedGeneration = -1;
}
private static void Ensure()
{
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
{
return;
}
Entries.Clear();
_appliedGeneration = EventCatalogConfig.Generation;
// Language flavor traits never own the camera.
LibraryCatalogDefaults d = LibOr("languageTrait", 30f, 68f, "Language", "{a} tongue gains {id}", false);
LiveLibraryInterest.EnsureSeeded(
Entries,
ActivityAssetCatalog.EnumerateLiveLanguageTraitIds,
d.Category,
d.Label,
d.AmbientStrength,
signalIds: null,
d.SignalStrength,
ambientCreatesInterest: d.AmbientCreatesInterest,
libraryKind: "language_trait");
}
public static IEnumerable<string> AuthoredIds
{
get
{
Ensure();
return Entries.Keys;
}
}
public static DiscreteEventEntry GetOrFallback(string id)
{
Ensure();
LibraryCatalogDefaults d = LibOr("languageTrait", 30f, 68f, "Language", "{a} tongue gains {id}", false);
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 30f, libraryKind: "language_trait");
}
}
}