diff --git a/IdleSpectator/ActivityActionComposer.cs b/IdleSpectator/ActivityActionComposer.cs index a714833..9663f0e 100644 --- a/IdleSpectator/ActivityActionComposer.cs +++ b/IdleSpectator/ActivityActionComposer.cs @@ -109,7 +109,7 @@ public static class ActivityActionComposer verb = string.IsNullOrEmpty(humanized) ? "move" : humanized.ToLowerInvariant(); } - string[] bag = ActivityActionLexicon.GetBag(voice, verb, hasTarget); + string[] bag = ActivityActionLexicon.GetBag(voice, verb, hasTarget, ctx.Terrain); return Pick(bag, voice, verb, subjectId, lineIndex); } diff --git a/IdleSpectator/ActivityActionLexicon.cs b/IdleSpectator/ActivityActionLexicon.cs index e3e3241..c9c25c9 100644 --- a/IdleSpectator/ActivityActionLexicon.cs +++ b/IdleSpectator/ActivityActionLexicon.cs @@ -5,7 +5,11 @@ namespace IdleSpectator; /// Phrase fragments keyed by the already-resolved voice. No ActorAsset lookup. public static class ActivityActionLexicon { - public static string[] GetBag(ProseVoice voice, string verb, bool hasTarget) + public static string[] GetBag( + ProseVoice voice, + string verb, + bool hasTarget, + ActivityTerrain terrain = ActivityTerrain.Unknown) { voice ??= ProseVoice.Unknown(); string v = string.IsNullOrEmpty(verb) ? "move" : verb; @@ -24,7 +28,7 @@ public static class ActivityActionLexicon string[] profile = ProfileBag(voice.ProfileTag, v, hasTarget); if (profile != null) return profile; - string[] body = BodyBag(voice.BaseBodyTag, v, hasTarget); + string[] body = BodyBag(voice.BaseBodyTag, v, hasTarget, terrain); return body ?? GenericBag(v, hasTarget); } @@ -53,8 +57,8 @@ public static class ActivityActionLexicon "move" => new[] { "shuffles ahead in soft, uneven steps{place_at}", "creeps forward as spores drift{place_at}" }, "wait" => new[] { "rests while spores drift around it{place_at}", "waits while faint spores drift{place_at}" }, "play" => new[] { "puffs through an odd little game{place_at}", "bobs about in playful bursts of spores{place_at}" }, - "eat" => new[] { "feeds through the damp earth{place_at}", "draws a slow meal from decay{place_at}" }, - "sleep" => new[] { "settles into a damp, quiet rest{place_at}", "folds down among drifting spores{place_at}" }, + "eat" => new[] { "feeds slowly on decaying matter{place_at}", "draws a slow meal from decay{place_at}" }, + "sleep" => new[] { "settles into a soft, quiet rest{place_at}", "folds down among drifting spores{place_at}" }, _ => GenericBag(verb, hasTarget) }; case ActivityModifier.Tumor: @@ -98,13 +102,13 @@ public static class ActivityActionLexicon { return body switch { - ActivityBodyTag.Canine => new[] { "pads ahead with a stiff, uneven gait{place_at}", "sniffs the ground and shambles on{place_at}" }, + ActivityBodyTag.Canine => new[] { "pads ahead with a stiff, uneven gait{place_at}", "sniffs ahead and shambles on{place_at}" }, ActivityBodyTag.Feline => new[] { "slinks ahead in eerie silence{place_at}", "pads forward with stiff precision{place_at}" }, ActivityBodyTag.Insect => new[] { "skitters ahead on stiff little legs{place_at}", "crawls forward in twitching bursts{place_at}" }, - ActivityBodyTag.Plant => new[] { "creeps ahead in withered motion{place_at}", "drags dead growth across the ground{place_at}" }, + ActivityBodyTag.Plant => new[] { "creeps ahead in withered motion{place_at}", "drags dead growth behind it{place_at}" }, ActivityBodyTag.Dragon => new[] { "lumbers ahead with the weight of a fallen giant{place_at}", "advances like ruin given motion{place_at}" }, ActivityBodyTag.Bird => new[] { "flutters ahead on lifeless wings{place_at}", "hops forward in ragged, uneven motions{place_at}" }, - _ => new[] { "shambles forward over uneven ground{place_at}", "drifts along in restless undeath{place_at}" } + _ => new[] { "shambles forward in uneven steps{place_at}", "drifts along in restless undeath{place_at}" } }; } @@ -133,7 +137,7 @@ public static class ActivityActionLexicon "sleep" => new[] { "coils into a guarded sleep{place_at}", "rests with one eye on the world{place_at}" }, "eat" => new[] { "feeds with terrifying appetite{place_at}", "devours a meal at leisure{place_at}" }, "laugh" => new[] { "rumbles with deep laughter{place_at}", "laughs in a dangerous rolling growl{place_at}" }, - "hunt" => Targeted(hasTarget, "descends on {target}{place_at}", "watches {target} from above{place_at}", "circles in search of prey{place_at}", "hunts across a wide range{place_at}"), + "hunt" => Targeted(hasTarget, "descends on {target}{place_at}", "keeps {target} in sight{place_at}", "circles in search of prey{place_at}", "hunts across a wide range{place_at}"), _ => null }; } @@ -193,27 +197,36 @@ public static class ActivityActionLexicon return null; } - private static string[] BodyBag(ActivityBodyTag body, string verb, bool hasTarget) + private static string[] BodyBag( + ActivityBodyTag body, + string verb, + bool hasTarget, + ActivityTerrain terrain) { if (verb == "move") { return body switch { ActivityBodyTag.Humanoid => new[] { "walks along, taking in the surroundings{place_at}", "wanders ahead at an easy pace{place_at}" }, - ActivityBodyTag.Canine => new[] { "trots along without hurry{place_at}", "roams ahead with nose to the ground{place_at}" }, + ActivityBodyTag.Canine => new[] { "trots along without hurry{place_at}", "roams ahead, following a scent{place_at}" }, ActivityBodyTag.Feline => new[] { "pads softly along{place_at}", "slinks ahead at an easy pace{place_at}" }, - ActivityBodyTag.Bird => new[] { "flutters onward through the air{place_at}", "hops and flaps along nearby{place_at}" }, - ActivityBodyTag.Insect => new[] { "scuttles along without hurry{place_at}", "skitters across the ground{place_at}" }, + ActivityBodyTag.Bird => new[] { "flutters steadily onward{place_at}", "hops and flaps along nearby{place_at}" }, + ActivityBodyTag.Insect => new[] { "scuttles along without hurry{place_at}", "skitters quickly forward{place_at}" }, ActivityBodyTag.Arachnid => new[] { "skitters onward on many legs{place_at}", "creeps ahead with careful steps{place_at}" }, - ActivityBodyTag.Amphibian => new[] { "hops from patch to patch{place_at}", "bounces along in damp, springy steps{place_at}" }, - ActivityBodyTag.Aquatic => new[] { "glides through the water{place_at}", "swims onward with easy strokes{place_at}" }, - ActivityBodyTag.Reptile => new[] { "slides along without hurry{place_at}", "creeps forward close to the ground{place_at}" }, + ActivityBodyTag.Amphibian => new[] { "hops from patch to patch{place_at}", "bounces along in springy steps{place_at}" }, + ActivityBodyTag.Aquatic => AquaticTerrainBag( + terrain, + "glides through the water{place_at}", + "swims onward with easy strokes{place_at}", + "moves forward in a smooth, winding motion{place_at}", + "twists steadily onward{place_at}"), + ActivityBodyTag.Reptile => new[] { "slides along without hurry{place_at}", "creeps forward in a low crawl{place_at}" }, ActivityBodyTag.Livestock => new[] { "ambles along without hurry{place_at}", "wanders in slow, heavy steps{place_at}" }, ActivityBodyTag.Plant => new[] { "creeps along as stems and leaves sway{place_at}", "eases forward in slow, leafy steps{place_at}" }, ActivityBodyTag.Fungi => new[] { "drifts along in soft, uneven steps{place_at}", "spreads forward as spores trail behind{place_at}" }, ActivityBodyTag.Protist => new[] { "oozes onward without hurry{place_at}", "pulses along in slow ripples{place_at}" }, ActivityBodyTag.Machina => new[] { "moves ahead in measured steps{place_at}", "tracks a precise course{place_at}" }, - ActivityBodyTag.Mythic => new[] { "drifts forward as if barely touching the ground{place_at}", "moves with an uncanny grace{place_at}" }, + ActivityBodyTag.Mythic => new[] { "drifts forward without a sound{place_at}", "moves with an uncanny grace{place_at}" }, ActivityBodyTag.Gregalia => new[] { "lopes onward with an uneven gait{place_at}", "wanders in twitchy, restless steps{place_at}" }, ActivityBodyTag.Primate => new[] { "lopes onward with nimble curiosity{place_at}", "moves ahead in quick clever steps{place_at}" }, ActivityBodyTag.Rodent => new[] { "scurries onward without hurry{place_at}", "pads ahead in quick little steps{place_at}" }, @@ -221,11 +234,11 @@ public static class ActivityActionLexicon ActivityBodyTag.Seal => new[] { "slides forward on its belly{place_at}", "bounds ahead with a sleek, rolling motion{place_at}" }, ActivityBodyTag.Procyonid => new[] { "pads onward, nosing around{place_at}", "scampers ahead in nimble steps{place_at}" }, ActivityBodyTag.Hyaenid => new[] { "lopes onward with rough confidence{place_at}", "trots ahead in rangy strides{place_at}" }, - ActivityBodyTag.Armadillo => new[] { "trundles onward close to the ground{place_at}", "scurries ahead in armored little steps{place_at}" }, - ActivityBodyTag.Crawler => new[] { "writhes forward across the ground{place_at}", "crawls ahead without hurry{place_at}" }, + ActivityBodyTag.Armadillo => new[] { "trundles onward with its body held low{place_at}", "scurries ahead in armored little steps{place_at}" }, + ActivityBodyTag.Crawler => new[] { "writhes steadily forward{place_at}", "crawls ahead without hurry{place_at}" }, ActivityBodyTag.Cold => new[] { "moves onward in a breath of cold{place_at}", "drifts ahead with wintry calm{place_at}" }, ActivityBodyTag.Crystal => new[] { "glides onward with crystalline precision{place_at}", "moves in stiff, glittering steps{place_at}" }, - ActivityBodyTag.Crab => new[] { "scuttles sideways across the ground{place_at}", "sidesteps onward behind a hard shell{place_at}" }, + ActivityBodyTag.Crab => new[] { "scuttles sideways{place_at}", "sidesteps onward behind a hard shell{place_at}" }, _ => GenericBag(verb, hasTarget) }; } @@ -239,8 +252,14 @@ public static class ActivityActionLexicon ActivityBodyTag.Bird => new[] { "flutters through playful arcs{place_at}", "hops and wings about in play{place_at}" }, ActivityBodyTag.Insect => new[] { "scuttles through a tiny game{place_at}", "darts about in quick bursts of play{place_at}" }, ActivityBodyTag.Arachnid => new[] { "skitters through a restless game{place_at}", "darts about on many legs{place_at}" }, - ActivityBodyTag.Amphibian => new[] { "hops about in damp play{place_at}", "bounces through a lively game{place_at}" }, - ActivityBodyTag.Aquatic => new[] { "splashes through play in the water{place_at}", "darts and circles in play{place_at}" }, + ActivityBodyTag.Amphibian => new[] { "hops about in lively play{place_at}", "bounces through a lively game{place_at}" }, + ActivityBodyTag.Aquatic => AquaticTerrainBag( + terrain, + "splashes through play in the water{place_at}", + "darts and circles through the water{place_at}", + "twists and darts through play{place_at}", + "wriggles through an energetic game{place_at}"), + ActivityBodyTag.Crab => new[] { "scuttles through a lively game{place_at}", "sidesteps and spins in play{place_at}" }, ActivityBodyTag.Reptile => new[] { "slides and coils in play{place_at}", "plays in slow, winding motions{place_at}" }, ActivityBodyTag.Livestock => new[] { "stomps about in clumsy play{place_at}", "frolics in heavy, eager bursts{place_at}" }, ActivityBodyTag.Plant => new[] { "sways through a slow game{place_at}", "twists and rustles in play{place_at}" }, @@ -260,7 +279,12 @@ public static class ActivityActionLexicon ActivityBodyTag.Amphibian => new[] { "crouches low and waits{place_at}", "sits still, ready to spring{place_at}" }, ActivityBodyTag.Feline => new[] { "settles into a watchful crouch{place_at}", "waits without looking away{place_at}" }, ActivityBodyTag.Canine => new[] { "waits with alert ears{place_at}", "holds a restless pause{place_at}" }, - ActivityBodyTag.Aquatic => new[] { "hovers in the water and waits{place_at}", "drifts almost motionless{place_at}" }, + ActivityBodyTag.Aquatic => AquaticTerrainBag( + terrain, + "hovers in the water and waits{place_at}", + "drifts almost motionless in the water{place_at}", + "rests almost motionless{place_at}", + "waits with only a faint twitch{place_at}"), ActivityBodyTag.Bird => new[] { "perches and waits{place_at}", "holds a light watchful pause{place_at}" }, ActivityBodyTag.Plant => new[] { "holds a rooted pause{place_at}", "stands still with leaves barely stirring{place_at}" }, ActivityBodyTag.Machina => new[] { "idles in a watchful hold{place_at}", "stays still and scans the surroundings{place_at}" }, @@ -272,11 +296,11 @@ public static class ActivityActionLexicon { return body switch { - ActivityBodyTag.Canine => Targeted(hasTarget, "hunts {target} with nose to the ground{place_at}", "follows {target}'s trail{place_at}", "follows the scent of prey{place_at}", "hunts with nose to the ground{place_at}"), - ActivityBodyTag.Feline => Targeted(hasTarget, "hunts {target} in silence{place_at}", "stalks {target} from cover{place_at}", "hunts without making a sound{place_at}", "stalks prey from cover{place_at}"), - ActivityBodyTag.Bird => Targeted(hasTarget, "hunts {target} from above{place_at}", "circles after {target}{place_at}", "hunts from above{place_at}", "circles after prey{place_at}"), + ActivityBodyTag.Canine => Targeted(hasTarget, "hunts {target} by scent{place_at}", "follows {target}'s trail{place_at}", "follows the scent of prey{place_at}", "hunts by scent{place_at}"), + ActivityBodyTag.Feline => Targeted(hasTarget, "hunts {target} in silence{place_at}", "stalks {target} without a sound{place_at}", "hunts without making a sound{place_at}", "stalks prey in silence{place_at}"), + ActivityBodyTag.Bird => Targeted(hasTarget, "tracks {target} with sharp eyes{place_at}", "circles after {target}{place_at}", "searches keenly for prey{place_at}", "circles after prey{place_at}"), ActivityBodyTag.Insect => Targeted(hasTarget, "scuttles after {target}{place_at}", "darts toward {target}{place_at}", "searches for prey in quick bursts{place_at}", "scuttles after prey{place_at}"), - ActivityBodyTag.Reptile => Targeted(hasTarget, "slides after {target}{place_at}", "creeps closer to {target}{place_at}", "waits low for prey to approach{place_at}", "stalks prey close to the ground{place_at}"), + ActivityBodyTag.Reptile => Targeted(hasTarget, "slides after {target}{place_at}", "creeps closer to {target}{place_at}", "waits low for prey to approach{place_at}", "stalks prey in a low crawl{place_at}"), _ => Targeted(hasTarget, "pursues {target}{place_at}", "closes in on {target}{place_at}", "searches for prey{place_at}", "moves quietly on the hunt{place_at}") }; } @@ -298,7 +322,12 @@ public static class ActivityActionLexicon ActivityBodyTag.Canine => new[] { "curls into sleep{place_at}", "settles into deep rest{place_at}" }, ActivityBodyTag.Feline => new[] { "curls into a neat sleep{place_at}", "settles into quiet rest{place_at}" }, ActivityBodyTag.Bird => new[] { "tucks into sleep{place_at}", "roosts into rest{place_at}" }, - ActivityBodyTag.Aquatic => new[] { "drifts into rest in the water{place_at}", "settles into wet quiet{place_at}" }, + ActivityBodyTag.Aquatic => AquaticTerrainBag( + terrain, + "drifts into rest in the water{place_at}", + "settles almost motionless in the water{place_at}", + "goes still to rest{place_at}", + "settles into a quiet sleep{place_at}"), ActivityBodyTag.Plant => new[] { "settles into leafy rest{place_at}", "goes completely still{place_at}" }, _ => new[] { "settles into deep sleep{place_at}", "rests after the day's demands{place_at}" } }, @@ -367,4 +396,16 @@ public static class ActivityActionLexicon ? new[] { withTargetA, withTargetB } : new[] { noTargetA, noTargetB }; } + + private static string[] AquaticTerrainBag( + ActivityTerrain terrain, + string liquidA, + string liquidB, + string nonLiquidA, + string nonLiquidB) + { + return terrain == ActivityTerrain.Liquid + ? new[] { liquidA, liquidB } + : new[] { nonLiquidA, nonLiquidB }; + } } diff --git a/IdleSpectator/ActivityContext.cs b/IdleSpectator/ActivityContext.cs index 0ff6a22..f52e02c 100644 --- a/IdleSpectator/ActivityContext.cs +++ b/IdleSpectator/ActivityContext.cs @@ -1,5 +1,12 @@ 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. @@ -41,6 +48,8 @@ public sealed class ActivityContext public bool TargetIsActor; /// Building, city, kingdom, or soft place label for clauses. public string PlaceLabel = ""; + /// Observed terrain under the actor when this snapshot was captured. + public ActivityTerrain Terrain; public string CarryingLabel = ""; public bool InCombat; public bool HasAttackTarget; @@ -68,7 +77,8 @@ public sealed class ActivityContext bool inCombat = false, bool isKing = false, bool isLeader = false, - bool isWarrior = false) + bool isWarrior = false, + ActivityTerrain terrain = ActivityTerrain.Unknown) { ActivityContext ctx = new ActivityContext { @@ -81,6 +91,7 @@ public sealed class ActivityContext JobId = jobId ?? "", TaskKey = taskKey ?? "", TopTraitId = traitId ?? "", + Terrain = terrain, IsChild = isChild, InCombat = inCombat, HasAttackTarget = inCombat && !string.IsNullOrEmpty(targetName), diff --git a/IdleSpectator/ActivityInterestTable.cs b/IdleSpectator/ActivityInterestTable.cs index 9ec2ad2..c8f0863 100644 --- a/IdleSpectator/ActivityInterestTable.cs +++ b/IdleSpectator/ActivityInterestTable.cs @@ -382,6 +382,7 @@ public static class ActivityInterestTable ctx.TaskKey = taskKeyHint ?? SafeTaskId(actor); ctx.JobId = SafeJobId(actor); + ctx.Terrain = ResolveTerrain(actor); try { @@ -463,6 +464,34 @@ public static class ActivityInterestTable return ctx; } + private static ActivityTerrain ResolveTerrain(Actor actor) + { + try + { + WorldTile tile = actor?.current_tile; + if (tile?.Type == null) + { + return ActivityTerrain.Unknown; + } + + if (tile.Type.liquid) + { + return ActivityTerrain.Liquid; + } + + if (tile.Type.ground) + { + return ActivityTerrain.Land; + } + } + catch + { + // A missing or changing tile is not safe evidence for environmental prose. + } + + return ActivityTerrain.Unknown; + } + public static string DeriveRoleLabel(bool isKing, bool isLeader, bool isWarrior) { if (isKing) diff --git a/IdleSpectator/ActivityVoice.cs b/IdleSpectator/ActivityVoice.cs index 924a35a..102ebf8 100644 --- a/IdleSpectator/ActivityVoice.cs +++ b/IdleSpectator/ActivityVoice.cs @@ -163,6 +163,7 @@ public static class ActivityVoiceFamilies public const string Arachnid = "arachnid"; public const string Amphibian = "amphibian"; public const string Aquatic = "aquatic"; + public const string Crustacean = "crustacean"; public const string Reptile = "reptile"; public const string Livestock = "livestock"; public const string Plant = "plant"; diff --git a/IdleSpectator/ActivityVoiceHarness.cs b/IdleSpectator/ActivityVoiceHarness.cs index d67f2a8..2742a5d 100644 --- a/IdleSpectator/ActivityVoiceHarness.cs +++ b/IdleSpectator/ActivityVoiceHarness.cs @@ -42,6 +42,21 @@ public static class ActivityVoiceHarness "patient citrus" }; + private static readonly string[] UngroundedTerrainFragments = + { + "through the water", + "in the water", + "through the air", + "across the ground", + "over uneven ground", + "from above", + "from cover", + "close to the ground", + "nose to the ground", + "damp play", + "wet quiet" + }; + public static ActivityVoiceAuditResult RunAudit(string outputDirectory) { List ids = ActivityAssetCatalog.EnumerateLiveActorAssetIds(); @@ -58,6 +73,7 @@ public static class ActivityVoiceHarness int collisions = 0; int vehicleMismatch = 0; int awkwardProse = 0; + int terrainLeaks = 0; string sample = ""; string vehicleFingerprint = null; var fingerprints = new Dictionary(StringComparer.Ordinal); @@ -114,6 +130,14 @@ public static class ActivityVoiceHarness awkwardProse++; AddSample(ref sample, id + ":awkward=" + awkward); } + string terrainLeak = voice.AssetKind == ActivityAssetKind.Vehicle + ? "" + : FindPhrase(fingerprint, UngroundedTerrainFragments); + if (!string.IsNullOrEmpty(terrainLeak)) + { + terrainLeaks++; + AddSample(ref sample, id + ":terrain=" + terrainLeak); + } if (voice.AssetKind == ActivityAssetKind.Vehicle) { @@ -173,6 +197,7 @@ public static class ActivityVoiceHarness && collisions == 0 && vehicleMismatch == 0 && awkwardProse == 0 + && terrainLeaks == 0 && pairFail == 0 && liveContextOk; return new ActivityVoiceAuditResult @@ -189,6 +214,7 @@ public static class ActivityVoiceHarness + " collisions=" + collisions + " vehicleMismatch=" + vehicleMismatch + " awkwardProse=" + awkwardProse + + " terrainLeaks=" + terrainLeaks + " pairFail=" + pairFail + " liveContextOk=" + liveContextOk + " sample='" + sample + "'" @@ -196,11 +222,16 @@ public static class ActivityVoiceHarness } private static string FindAwkwardPhrase(string fingerprint) + { + return FindPhrase(fingerprint, AwkwardProseFragments); + } + + private static string FindPhrase(string fingerprint, string[] phrases) { string text = fingerprint ?? ""; - for (int i = 0; i < AwkwardProseFragments.Length; i++) + for (int i = 0; i < phrases.Length; i++) { - string phrase = AwkwardProseFragments[i]; + string phrase = phrases[i]; if (text.IndexOf(phrase, StringComparison.OrdinalIgnoreCase) >= 0) { return phrase; @@ -222,6 +253,13 @@ public static class ActivityVoiceHarness } ActivityContext ctx = ActivityInterestTable.BuildContext(actor, "move"); + ActivityTerrain expectedTerrain = actor.current_tile?.Type == null + ? ActivityTerrain.Unknown + : actor.current_tile.Type.liquid + ? ActivityTerrain.Liquid + : actor.current_tile.Type.ground + ? ActivityTerrain.Land + : ActivityTerrain.Unknown; ActivityProse.Format( ActivityKind.TaskStart, "move", @@ -234,6 +272,7 @@ public static class ActivityVoiceHarness bool ok = ctx.Voice != null && ctx.Voice.LiveSpeciesId == (actor.asset.id ?? "") && ctx.Voice.EffectiveActionTag != ActivityBodyTag.Default + && ctx.Terrain == expectedTerrain && !string.IsNullOrEmpty(line); if (!ok) { diff --git a/IdleSpectator/ActivityVoiceResolver.cs b/IdleSpectator/ActivityVoiceResolver.cs index 4d4736c..3fdd36e 100644 --- a/IdleSpectator/ActivityVoiceResolver.cs +++ b/IdleSpectator/ActivityVoiceResolver.cs @@ -279,8 +279,8 @@ public static class ActivityVoiceResolver || EqualsIgnore(taxOrder, "scorpiones") || EqualsIgnore(taxOrder, "araneae")) return ActivityBodyTag.Arachnid; if (EqualsIgnore(taxClass, "amphibia")) return ActivityBodyTag.Amphibian; - if (EqualsIgnore(taxClass, "malacostraca") - || EqualsIgnore(taxClass, "actinopterygii") + if (EqualsIgnore(taxClass, "malacostraca")) return ActivityBodyTag.Crab; + if (EqualsIgnore(taxClass, "actinopterygii") || EqualsIgnore(taxClass, "chondrichthyes") || EqualsIgnore(taxClass, "cephalopoda") || EqualsIgnore(taxOrder, "testudines")) return ActivityBodyTag.Aquatic; @@ -342,6 +342,7 @@ public static class ActivityVoiceResolver ActivityBodyTag.Amphibian => ActivityVoiceFamilies.Amphibian, ActivityBodyTag.Aquatic => ActivityVoiceFamilies.Aquatic, ActivityBodyTag.Seal => ActivityVoiceFamilies.Aquatic, + ActivityBodyTag.Crab => ActivityVoiceFamilies.Crustacean, ActivityBodyTag.Reptile => ActivityVoiceFamilies.Reptile, ActivityBodyTag.Livestock => ActivityVoiceFamilies.Livestock, ActivityBodyTag.Plant => ActivityVoiceFamilies.Plant, @@ -352,7 +353,6 @@ public static class ActivityVoiceResolver ActivityBodyTag.Dragon => ActivityVoiceFamilies.Mythic, ActivityBodyTag.Cold => ActivityVoiceFamilies.Mythic, ActivityBodyTag.Crystal => ActivityVoiceFamilies.Special, - ActivityBodyTag.Crab => ActivityVoiceFamilies.Special, ActivityBodyTag.Alien => ActivityVoiceFamilies.Special, ActivityBodyTag.Construct => ActivityVoiceFamilies.Special, ActivityBodyTag.Tumor => ActivityVoiceFamilies.Special, diff --git a/IdleSpectator/AgentHarness.cs b/IdleSpectator/AgentHarness.cs index f6820b8..e2d7a23 100644 --- a/IdleSpectator/AgentHarness.cs +++ b/IdleSpectator/AgentHarness.cs @@ -2523,6 +2523,45 @@ public static class AgentHarness 1, out string rhinoPlay, out _); + ActivityProse.Format( + ActivityKind.TaskStart, + "task_unit_play", + ActivityContext.ForHarness( + "Crob", + "", + "crab", + terrain: ActivityTerrain.Land), + "Playing", + 108, + 1, + out string crabLandPlay, + out _); + ActivityProse.Format( + ActivityKind.TaskStart, + "task_unit_play", + ActivityContext.ForHarness( + "Pip", + "", + "piranha", + terrain: ActivityTerrain.Land), + "Playing", + 109, + 1, + out string fishLandPlay, + out _); + ActivityProse.Format( + ActivityKind.TaskStart, + "task_unit_play", + ActivityContext.ForHarness( + "Pip", + "", + "piranha", + terrain: ActivityTerrain.Liquid), + "Playing", + 109, + 1, + out string fishWaterPlay, + out _); bool taxDiverge = !string.IsNullOrEmpty(beetlePlay) && !string.IsNullOrEmpty(frogPlay) && !string.IsNullOrEmpty(rhinoPlay) @@ -2543,13 +2582,26 @@ public static class AgentHarness || frogPlay.IndexOf("splash", System.StringComparison.OrdinalIgnoreCase) >= 0 || frogPlay.IndexOf("damp", System.StringComparison.OrdinalIgnoreCase) >= 0 || frogPlay.IndexOf("jump", System.StringComparison.OrdinalIgnoreCase) >= 0); + bool crabLandOk = !string.IsNullOrEmpty(crabLandPlay) + && crabLandPlay.IndexOf("water", System.StringComparison.OrdinalIgnoreCase) < 0 + && crabLandPlay.IndexOf("wet", System.StringComparison.OrdinalIgnoreCase) < 0 + && (crabLandPlay.IndexOf("scuttl", System.StringComparison.OrdinalIgnoreCase) >= 0 + || crabLandPlay.IndexOf("sidestep", System.StringComparison.OrdinalIgnoreCase) >= 0); + bool fishTerrainOk = !string.IsNullOrEmpty(fishLandPlay) + && !string.IsNullOrEmpty(fishWaterPlay) + && fishLandPlay.IndexOf("water", System.StringComparison.OrdinalIgnoreCase) < 0 + && fishLandPlay.IndexOf("wet", System.StringComparison.OrdinalIgnoreCase) < 0 + && fishWaterPlay.IndexOf("water", System.StringComparison.OrdinalIgnoreCase) >= 0; - pass = farmerOk && minerOk && divergeOk && kingOk && huntPressureOk && taxDiverge; + pass = farmerOk && minerOk && divergeOk && kingOk && huntPressureOk && taxDiverge + && crabLandOk && fishTerrainOk; detail = $"farmerOk={farmerOk} minerOk={minerOk} divergeOk={divergeOk} kingOk={kingOk} " + $"huntPressureOk={huntPressureOk} taxDiverge={taxDiverge} " + + $"crabLandOk={crabLandOk} fishTerrainOk={fishTerrainOk} " + $"farmer='{farmerLine}' miner='{minerLine}' king='{kingCombatLine}' hunt='{huntCombatLine}' " - + $"beetle='{beetlePlay}' frog='{frogPlay}' rhino='{rhinoPlay}'"; + + $"beetle='{beetlePlay}' frog='{frogPlay}' rhino='{rhinoPlay}' " + + $"crabLand='{crabLandPlay}' fishLand='{fishLandPlay}' fishWater='{fishWaterPlay}'"; break; } case "activity_prose_library": diff --git a/IdleSpectator/mod.json b/IdleSpectator/mod.json index 7d0371b..1462626 100644 --- a/IdleSpectator/mod.json +++ b/IdleSpectator/mod.json @@ -1,7 +1,7 @@ { "name": "IdleSpectator", "author": "dazed", - "version": "0.12.72", + "version": "0.12.73", "description": "AFK Idle Spectator (I) + Lore (L). Detailed living activity prose by default.", "GUID": "com.dazed.idlespectator" }