namespace IdleSpectator; /// /// IdleSpectator event inventory (one type, split across partial files). /// /// Layer B (ticker): Activity / Life prose for live ids - completeness. /// Layer A (story camera): only CameraWorthy beats register interest and cut the camera. /// /// Call sites: EventCatalog.Status / .Happiness / .Combat / … /// Director only ranks A candidates; it does not author inventory. /// /// Domain files under Events/Catalogs/: /// EventCatalog.Status.cs /// EventCatalog.Happiness.cs /// EventCatalog.WorldLog.cs /// EventCatalog.Trait.cs /// EventCatalog.Book.cs /// EventCatalog.Era.cs /// EventCatalog.Plot.cs /// EventCatalog.Relationship.cs /// EventCatalog.Disaster.cs /// EventCatalog.WarType.cs /// EventCatalog.Libraries.cs /// Combat policy lives in this file. /// public static partial class EventCatalog { /// Drop cached catalog rows so the next Ensure reloads from event-catalog.json. public static void InvalidateAllOverlays() { Decision.InvalidateOverlays(); Happiness.InvalidateOverlays(); Status.InvalidateOverlays(); WorldLog.InvalidateOverlays(); Plot.InvalidateOverlays(); Relationship.InvalidateOverlays(); Story.InvalidateOverlays(); Trait.InvalidateOverlays(); Book.InvalidateOverlays(); Era.InvalidateOverlays(); Disaster.InvalidateOverlays(); WarType.InvalidateOverlays(); Spell.InvalidateOverlays(); Power.InvalidateOverlays(); Item.InvalidateOverlays(); Gene.InvalidateOverlays(); Phenotype.InvalidateOverlays(); WorldLaw.InvalidateOverlays(); SubspeciesTrait.InvalidateOverlays(); Biome.InvalidateOverlays(); CultureTrait.InvalidateOverlays(); ReligionTrait.InvalidateOverlays(); ClanTrait.InvalidateOverlays(); LanguageTrait.InvalidateOverlays(); } /// /// Layer A gate for happiness: Signal may own the camera; Ambient/Aggregate are B-only. /// public static bool IsCameraWorthy(HappinessPresentationTier presentation) => presentation == HappinessPresentationTier.Signal; /// Layer A gate for discrete / status / worldlog rows. public static bool IsCameraWorthy(bool createsInterest) => createsInterest; public static bool IsCameraWorthy(DiscreteEventEntry entry) => entry != null && entry.CreatesInterest && !entry.IsFallback; public static bool IsCameraWorthy(StatusInterestEntry entry) => entry != null && entry.CreatesInterest && !entry.IsFallback; public static bool IsCameraWorthy(WorldLogEventEntry entry) => entry != null && entry.CreatesInterest && !entry.IsFallback; public static bool IsCameraWorthy(HappinessCatalogEntry entry) => entry != null && IsCameraWorthy(entry.Presentation); /// Combat camera policy (activity + scanner share one key / dials). public static class Combat { public const float ActivityStrength = 88f; public const float ScannerStrengthFloor = 80f; public const float MinWatch = 4f; public const float MaxWatch = 60f; public const float ActivityTtl = 20f; public const float ScannerTtl = 18f; public static string Key(long subjectId) => "combat:" + subjectId; /// /// Unordered pair key so A-vs-B and B-vs-A collapse to one durable combat scene. /// public static string PairKey(long a, long b) { if (a == 0 && b == 0) { return "combat:0"; } if (a == 0) { return Key(b); } if (b == 0 || a == b) { return Key(a); } long lo = a < b ? a : b; long hi = a < b ? b : a; return "combat:pair:" + lo + ":" + hi; } public static string KeyFor(Actor follow, Actor related) { // Spectacle 1vN scraps share one scene so attack_target hops merge instead of // spawning combat:pair:owner:victimN keys that thrash Duel tips. if (follow != null && WorldActivityScanner.IsSpectaclePublic(follow)) { long sid = EventFeedUtil.SafeId(follow); if (sid != 0) { return "combat:spectacle:" + sid; } } if (related != null && WorldActivityScanner.IsSpectaclePublic(related)) { long sid = EventFeedUtil.SafeId(related); if (sid != 0) { return "combat:spectacle:" + sid; } } long a = EventFeedUtil.SafeId(follow); long b = EventFeedUtil.SafeId(related); return b != 0 ? PairKey(a, b) : Key(a); } } }