namespace IdleSpectator; public enum ActivityTerrain { Unknown, Land, Liquid } /// /// Snapshot of live actor facts for activity prose. Only observed fields - never invent events. /// Species / taxonomy / flags come from for every unit in the game. /// public sealed class ActivityContext { public string ActorName = ""; /// Raw actor asset id (frog, human, beetle, ...). public string SpeciesId = ""; /// Recovered living/body source for meaningful transformed variants. public string BaseSpeciesId = ""; /// Readable species word for prose (locale / asset id). public string SpeciesLabel = ""; public string Family = ActivityVoiceFamilies.Default; public string ModifierTag = ""; public ProseVoice Voice; public string TaxonomicKingdom = ""; public string TaxonomicPhylum = ""; public string TaxonomicClass = ""; public string TaxonomicOrder = ""; public string TaxonomicFamily = ""; public string TaxonomicGenus = ""; public string TaxonomicSpecies = ""; public bool IsHumanoid; public bool IsCiv; /// From - animal tone when not civ humanoid. public bool IsAnimal; public int Age; public bool IsChild; public string JobId = ""; public string CityName = ""; public string KingdomName = ""; public bool IsKing; public bool IsLeader; public bool IsWarrior; /// Observed role noun: king / leader / warrior when flags are true. public string RoleLabel = ""; public string TargetName = ""; public bool TargetIsActor; /// Building, city, kingdom, or soft place label for clauses. public string PlaceLabel = ""; /// /// True when is a concrete building/object (bonfire, house, …). /// False for settlements, realms, and soft places like "the wilds". /// public bool PlaceIsObject; /// Observed terrain under the actor when this snapshot was captured. public ActivityTerrain Terrain; public string CarryingLabel = ""; public bool InCombat; public bool HasAttackTarget; /// Observed trait id - salts prose variety only; never spelled in the activity line. public string TopTraitId = ""; /// Humanized trait label for salt/debug - dossier chips show the trait to the player. public string TopTraitLabel = ""; /// Locomotion / body manner from live taxonomy/flags (insect, amphibian, flying, ...). public string MannerTag = "default"; public string TaskKey = ""; public static ActivityContext Empty { get; } = new ActivityContext(); /// Harness / ForceNote: minimal context; species resolved from live library when present. public static ActivityContext ForHarness( string actorName, string targetName, string speciesId = "", string place = "", string carrying = "", string jobId = "", string taskKey = "", string traitId = "", bool isChild = false, bool inCombat = false, bool isKing = false, bool isLeader = false, bool isWarrior = false, ActivityTerrain terrain = ActivityTerrain.Unknown, bool placeIsObject = false) { ActivityContext ctx = new ActivityContext { ActorName = actorName ?? "", TargetName = targetName ?? "", TargetIsActor = !string.IsNullOrEmpty(targetName), SpeciesId = speciesId ?? "", PlaceLabel = place ?? "", PlaceIsObject = placeIsObject, CarryingLabel = carrying ?? "", JobId = jobId ?? "", TaskKey = taskKey ?? "", TopTraitId = traitId ?? "", Terrain = terrain, IsChild = isChild, InCombat = inCombat, HasAttackTarget = inCombat && !string.IsNullOrEmpty(targetName), IsKing = isKing, IsLeader = isLeader, IsWarrior = isWarrior }; ActorAsset asset = ActivityAssetCatalog.TryGetActorAsset(ctx.SpeciesId); if (asset != null) { ActivityVoiceResolver.ApplyToContext(ctx, asset); ctx.SpeciesLabel = ActivityAssetCatalog.SpeciesDisplayLabel(asset); } else { ctx.SpeciesLabel = ActivityAssetCatalog.SpeciesDisplayLabel(ctx.SpeciesId); ctx.Voice = ActivityVoiceResolver.Resolve(ctx.SpeciesId); ctx.BaseSpeciesId = ctx.Voice.BaseSpeciesId; ctx.Family = ctx.Voice.BaseFamily; ctx.MannerTag = ActivityVoiceResolver.TagName(ctx.Voice.EffectiveActionTag); ctx.ModifierTag = ActivityVoiceResolver.ModifierName(ctx.Voice.Modifier); ctx.IsCiv = ctx.Voice.BaseIsCiv; ctx.IsHumanoid = ctx.Voice.BaseIsHumanoid; ctx.IsAnimal = ctx.Voice.BaseIsAnimal; } if (!string.IsNullOrEmpty(taskKey) && taskKey.StartsWith("child_", System.StringComparison.OrdinalIgnoreCase)) { ctx.IsChild = true; } // Strip species-like place labels the same way live BuildContext does. if (!string.IsNullOrEmpty(ctx.PlaceLabel) && ActivityInterestTable.LooksLikeSpeciesPublic(ctx.PlaceLabel, ctx.SpeciesId)) { ctx.PlaceLabel = ""; ctx.PlaceIsObject = false; } ctx.RoleLabel = ActivityInterestTable.DeriveRoleLabel(ctx.IsKing, ctx.IsLeader, ctx.IsWarrior); ctx.TopTraitLabel = ActivityInterestTable.TraitLabelFromId(ctx.TopTraitId); return ctx; } }