diff --git a/IdleSpectator/ActivityActionComposer.cs b/IdleSpectator/ActivityActionComposer.cs index 9663f0e..8133278 100644 --- a/IdleSpectator/ActivityActionComposer.cs +++ b/IdleSpectator/ActivityActionComposer.cs @@ -86,7 +86,7 @@ public static class ActivityActionComposer && BeatTemplates.TryGetValue(key, out string[] beatBag) && voice.AssetKind != ActivityAssetKind.Vehicle) { - return Pick(beatBag, voice, key, subjectId, lineIndex); + return Pick(beatBag, subjectId, lineIndex); } string verb = ActivityVerbMap.Resolve(key); @@ -110,7 +110,8 @@ public static class ActivityActionComposer } string[] bag = ActivityActionLexicon.GetBag(voice, verb, hasTarget, ctx.Terrain); - return Pick(bag, voice, verb, subjectId, lineIndex); + string phrase = Pick(bag, subjectId, lineIndex); + return ActivityModifierVoiceOverlay.Apply(phrase, voice, verb, subjectId, lineIndex); } private static ProseVoice EnsureVoice(ActivityContext ctx) @@ -135,8 +136,6 @@ public static class ActivityActionComposer private static string Pick( string[] bag, - ProseVoice voice, - string verb, long subjectId, int lineIndex) { @@ -150,29 +149,8 @@ public static class ActivityActionComposer return bag[0]; } - int slot = VerbSlot(verb); - int bit = voice.AssetKind == ActivityAssetKind.Vehicle - ? 0 - : ((voice.FlavorOrdinal >> slot) & 1); int actorVariation = (int)((subjectId ^ (lineIndex * 397L)) & 1L); - int index = (bit ^ actorVariation) % Math.Min(2, bag.Length); + int index = actorVariation % Math.Min(2, bag.Length); return bag[index]; } - - private static int VerbSlot(string verb) - { - switch ((verb ?? "").ToLowerInvariant()) - { - case "move": return 0; - case "wait": return 1; - case "play": return 2; - case "eat": return 3; - case "sleep": return 4; - case "hunt": return 5; - case "fight": return 6; - case "social": return 7; - case "laugh": return 8; - default: return 9; - } - } } diff --git a/IdleSpectator/ActivityActionLexicon.cs b/IdleSpectator/ActivityActionLexicon.cs index 537add9..d780f69 100644 --- a/IdleSpectator/ActivityActionLexicon.cs +++ b/IdleSpectator/ActivityActionLexicon.cs @@ -19,15 +19,19 @@ public static class ActivityActionLexicon return VehicleBag(v); } + string[] authored = ActivitySpeciesVoiceCatalog.GetBag(voice.BaseSpeciesId, v, terrain); + if (authored != null && authored.Length > 0) + { + return authored; + } + + // Safe fallback for unknown future assets that are not yet in the authored catalog. if (voice.Modifier != ActivityModifier.None) { string[] modifier = ModifierBag(voice, v, hasTarget); if (modifier != null) return modifier; } - string[] profile = ProfileBag(voice.ProfileTag, v, hasTarget); - if (profile != null) return profile; - string[] body = BodyBag(voice.BaseBodyTag, v, hasTarget, terrain); return body ?? GenericBag(v, hasTarget); } @@ -126,77 +130,6 @@ public static class ActivityActionLexicon }; } - private static string[] ProfileBag(string profile, string verb, bool hasTarget) - { - if (string.IsNullOrEmpty(profile)) return null; - if (profile == "dragon") - { - return verb switch - { - "move" => new[] { "advances at an unhurried pace{place_at}", "moves as if the world should make way{place_at}" }, - "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}", "keeps {target} in sight{place_at}", "circles in search of prey{place_at}", "hunts across a wide range{place_at}"), - _ => null - }; - } - - if (profile == "fairy") - { - return verb switch - { - "move" => new[] { "flits onward in bright little arcs{place_at}", "drifts ahead on glittering wings{place_at}" }, - "play" => new[] { "loops and twirls in sparkling arcs{place_at}", "flutters about mischievously{place_at}" }, - "laugh" => new[] { "chimes with tiny laughter{place_at}", "laughs in a bright little trill{place_at}" }, - _ => null - }; - } - - if (profile == "lemon_snail") - { - return verb switch - { - "move" => new[] { "glides ahead at an unhurried pace{place_at}", "follows a slow, winding trail{place_at}" }, - "play" => new[] { "turns in slow, winding circles{place_at}", "sways gently from side to side{place_at}" }, - "eat" => new[] { "nibbles through a meal slowly{place_at}", "takes a slow, careful meal{place_at}" }, - _ => null - }; - } - - if (profile == "cat") - { - return verb switch - { - "move" => new[] { "pads softly along{place_at}", "slinks ahead, watching the way{place_at}" }, - "play" => new[] { "bats, twists, and springs aside{place_at}", "springs into sudden leaps{place_at}" }, - _ => null - }; - } - - if (profile == "buffalo") - { - return verb switch - { - "move" => new[] { "trudges onward with steady weight{place_at}", "ambles ahead in slow, heavy steps{place_at}" }, - "play" => new[] { "charges, stops, and wheels around{place_at}", "stomps and bounds with clumsy enthusiasm{place_at}" }, - _ => null - }; - } - - if (profile == "crabzilla") - { - return verb switch - { - "move" => new[] { "takes enormous sideways strides{place_at}", "scuttles onward like a moving fortress{place_at}" }, - "fight" => Targeted(hasTarget, "brings a colossal claw against {target}{place_at}", "crashes into {target} like a siege engine{place_at}", "fights with city-shaking force{place_at}", "clashes like a moving fortress{place_at}"), - _ => null - }; - } - - return null; - } - private static string[] BodyBag( ActivityBodyTag body, string verb, diff --git a/IdleSpectator/ActivityModifierVoiceOverlay.cs b/IdleSpectator/ActivityModifierVoiceOverlay.cs new file mode 100644 index 0000000..9c43a9a --- /dev/null +++ b/IdleSpectator/ActivityModifierVoiceOverlay.cs @@ -0,0 +1,184 @@ +using System; + +namespace IdleSpectator; + +/// +/// Adds a meaningful transformed-variant tone without replacing the recovered base mob voice. +/// Native authored forms keep their own complete voice and do not pass through this overlay. +/// +public static class ActivityModifierVoiceOverlay +{ + private const string ContextTokens = "{place_at}{job_as}"; + + public static string Apply( + string basePhrase, + ProseVoice voice, + string verb, + long subjectId, + int lineIndex) + { + if (string.IsNullOrEmpty(basePhrase) + || voice == null + || voice.Modifier == ActivityModifier.None + || voice.LiveSpeciesId.Equals(voice.BaseSpeciesId, StringComparison.OrdinalIgnoreCase)) + { + return basePhrase; + } + + string[] bag = OverlayBag(voice.Modifier, voice.BaseBodyTag, verb); + if (bag == null || bag.Length == 0) + { + return basePhrase; + } + + int index = (int)((subjectId ^ (lineIndex * 397L)) & 1L) % bag.Length; + string core = basePhrase.EndsWith(ContextTokens, StringComparison.Ordinal) + ? basePhrase.Substring(0, basePhrase.Length - ContextTokens.Length) + : basePhrase; + return core + bag[index] + ContextTokens; + } + + private static string[] OverlayBag( + ActivityModifier modifier, + ActivityBodyTag body, + string verb) + { + return modifier switch + { + ActivityModifier.Undead => UndeadBag(body, verb), + ActivityModifier.Elemental => ElementalBag(verb), + ActivityModifier.Mush => MushBag(verb), + ActivityModifier.Tumor => TumorBag(verb), + ActivityModifier.Alien => AlienBag(verb), + ActivityModifier.Construct => ConstructBag(verb), + _ => null + }; + } + + private static string[] UndeadBag(ActivityBodyTag body, string verb) + { + if (verb == "move") + { + return body switch + { + ActivityBodyTag.Canine => V( + ", its paws landing in stiff, uneven beats", + ", its muzzle fixed ahead as its gait falters"), + ActivityBodyTag.Feline => V( + ", each silent step held rigid too long", + ", its once-fluid gait broken into sharp jolts"), + ActivityBodyTag.Bird => V( + ", its lifeless wings beating out of rhythm", + ", each ragged motion ending in a hard twitch"), + ActivityBodyTag.Insect => V( + ", its joints clicking in lifeless sequence", + ", each small step snapping into the next"), + ActivityBodyTag.Plant => V( + ", its withered growth dragging behind", + ", every deadened limb moving in rigid turns"), + ActivityBodyTag.Dragon => V( + ", its ruined frame carrying deathly weight", + ", every ancient joint grinding through the motion"), + _ => V( + ", its gait broken by stiff, uneven jolts", + ", every step landing with lifeless weight") + }; + } + + return verb switch + { + "wait" => V( + ", holding an unnaturally rigid posture", + ", without breath or any easy motion"), + "play" => V( + ", repeating each motion in empty jerks", + ", the movement breaking into stiff, empty rhythms"), + "eat" => V( + ", driven by a hunger that never eases", + ", consuming each bite without satisfaction"), + "sleep" => V( + ", lying rigid even at rest", + ", never fully losing its restless tension"), + "hunt" => V( + ", pursuing without tiring", + ", never slowing as the chase continues"), + "fight" => V( + ", striking without fear or pain", + ", pressing the attack with deathless persistence"), + "social" => V( + ", answering with hollow, halting sounds", + ", holding close in an uneasy silence"), + "laugh" => V( + ", the sound collapsing into a dry rasp", + ", each note ending in a hollow groan"), + "work" => V( + ", continuing in tireless, rigid motions", + ", never pausing as its joints grind on"), + _ => V( + ", its body moving with lifeless stiffness", + ", each motion marked by restless undeath") + }; + } + + private static string[] ElementalBag(string verb) + { + return verb switch + { + "wait" => V(", flickering without becoming still", ", its glow rising and falling"), + "sleep" => V(", dimming to a banked glow", ", its brightness settling low"), + "eat" => V(", drawing the meal into its inner heat", ", feeding the flame within"), + "fight" => V(", shedding sparks with every strike", ", flaring brighter through the clash"), + _ => V(", edged by quick tongues of flame", ", trailing brief curls of heat") + }; + } + + private static string[] MushBag(string verb) + { + return verb switch + { + "wait" => V(", faint spores drifting from every pause", ", its soft growth gently pulsing"), + "eat" => V(", drawing nourishment through fibrous growth", ", feeding in slow, spreading pulses"), + "fight" => V(", bursting spores through every impact", ", its soft mass recoiling and swelling"), + _ => V(", shedding a faint wake of spores", ", its softened form flexing with each motion") + }; + } + + private static string[] TumorBag(string verb) + { + return verb switch + { + "wait" => V(", its swollen mass quivering in place", ", uneven pulses passing through its body"), + "eat" => V(", absorbing the meal into its growing mass", ", folding each bite into hungry tissue"), + "fight" => V(", its mass surging behind every blow", ", new growth tightening through the clash"), + _ => V(", its form shifting in uneven pulses", ", swollen tissue rolling through each motion") + }; + } + + private static string[] AlienBag(string verb) + { + return verb switch + { + "wait" => V(", holding a posture that is difficult to read", ", its unfamiliar joints locking at odd angles"), + "social" => V(", answering in precise, unfamiliar signals", ", mirroring the exchange with uncanny timing"), + "fight" => V(", changing angles without warning", ", every strike arriving with uncanny precision"), + _ => V(", following an unfamiliar rhythm", ", each motion precise but difficult to predict") + }; + } + + private static string[] ConstructBag(string verb) + { + return verb switch + { + "wait" => V(", every mechanism settling into a fixed hold", ", its joints locking into measured stillness"), + "sleep" => V(", its moving parts falling dormant", ", the last small mechanisms winding down"), + "fight" => V(", every joint driving the next strike", ", its rigid frame absorbing the impact"), + "work" => V(", repeating each step with exact precision", ", its mechanisms keeping a flawless cadence"), + _ => V(", every joint moving in measured sequence", ", its mechanisms keeping a precise cadence") + }; + } + + private static string[] V(string first, string second) + { + return new[] { first, second }; + } +} diff --git a/IdleSpectator/ActivityProse.cs b/IdleSpectator/ActivityProse.cs index 39cef46..be842b3 100644 --- a/IdleSpectator/ActivityProse.cs +++ b/IdleSpectator/ActivityProse.cs @@ -624,6 +624,19 @@ public static class ActivityProse .Replace(" toward {target}", "") .Replace(" after {target}", "") .Replace(" at {target}", "") + .Replace(" against {target}", "") + .Replace(" before {target}", "") + .Replace(" beside {target}", "") + .Replace(" near {target}", "") + .Replace(" around {target}", "") + .Replace(" past {target}", "") + .Replace(" behind {target}", "") + .Replace(" under {target}", "") + .Replace(" over {target}", "") + .Replace(" across {target}", "") + .Replace(" from {target}", "") + .Replace(" into {target}", "") + .Replace(" upon {target}", "") .Replace(" {target}", "") .Replace("{target}", "someone"); } diff --git a/IdleSpectator/ActivitySpeciesVoice.cs b/IdleSpectator/ActivitySpeciesVoice.cs new file mode 100644 index 0000000..c4da63a --- /dev/null +++ b/IdleSpectator/ActivitySpeciesVoice.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; + +namespace IdleSpectator; + +public enum ActivityVoiceSource +{ + Unknown, + AuthoredSpecies, + TaxonomyFallback, + Vehicle +} + +/// +/// Complete authored action vocabulary for one verified base mob. +/// Contextual clauses such as actor, target, terrain, place, and job stay outside this type. +/// +public sealed class ActivitySpeciesVoice +{ + public static readonly string[] CoreVerbs = + { + "move", "wait", "play", "eat", "sleep", + "hunt", "fight", "social", "laugh", "work" + }; + + private readonly Dictionary _bags; + private readonly Dictionary _liquidBags = + new Dictionary(StringComparer.Ordinal); + + public string Id { get; } + + internal ActivitySpeciesVoice(string id, Dictionary bags) + { + Id = id ?? ""; + _bags = bags ?? new Dictionary(StringComparer.Ordinal); + } + + public string[] GetBag(string verb, ActivityTerrain terrain) + { + string key = verb ?? ""; + if (terrain == ActivityTerrain.Liquid + && _liquidBags.TryGetValue(key, out string[] liquid) + && liquid != null + && liquid.Length > 0) + { + return liquid; + } + + return _bags.TryGetValue(key, out string[] bag) ? bag : null; + } + + public bool HasCompleteCore() + { + for (int i = 0; i < CoreVerbs.Length; i++) + { + if (!_bags.TryGetValue(CoreVerbs[i], out string[] bag) + || bag == null + || bag.Length < 2 + || string.IsNullOrEmpty(bag[0]) + || string.IsNullOrEmpty(bag[1])) + { + return false; + } + } + + return true; + } + + internal void SetLiquidBag(string verb, string[] bag) + { + if (!string.IsNullOrEmpty(verb) && bag != null && bag.Length > 0) + { + _liquidBags[verb] = bag; + } + } +} diff --git a/IdleSpectator/ActivitySpeciesVoiceCatalog.cs b/IdleSpectator/ActivitySpeciesVoiceCatalog.cs new file mode 100644 index 0000000..a7d34f7 --- /dev/null +++ b/IdleSpectator/ActivitySpeciesVoiceCatalog.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; + +namespace IdleSpectator; + +/// Authoritative authored voice registry keyed by verified base actor asset id. +public static partial class ActivitySpeciesVoiceCatalog +{ + private static readonly Dictionary Voices = Build(); + + private static Dictionary Build() + { + var voices = new Dictionary(StringComparer.OrdinalIgnoreCase); + AddCivVoices(voices); + AddMammalVoices(voices); + AddAnimalVoices(voices); + AddInvertebrateVoices(voices); + AddFantasyVoices(voices); + return voices; + } + + public static bool TryGet(string baseSpeciesId, out ActivitySpeciesVoice voice) + { + return Voices.TryGetValue(baseSpeciesId ?? "", out voice); + } + + public static bool HasCompleteVoice(string baseSpeciesId) + { + return TryGet(baseSpeciesId, out ActivitySpeciesVoice voice) && voice.HasCompleteCore(); + } + + public static string VoiceKey(string baseSpeciesId) + { + return TryGet(baseSpeciesId, out ActivitySpeciesVoice voice) ? voice.Id : ""; + } + + public static string[] GetBag(string baseSpeciesId, string verb, ActivityTerrain terrain) + { + return TryGet(baseSpeciesId, out ActivitySpeciesVoice voice) + ? voice.GetBag(verb, terrain) + : null; + } + + public static List AuthoredIds() + { + var ids = new List(Voices.Keys); + ids.Sort(StringComparer.Ordinal); + return ids; + } + + private static void Add( + Dictionary voices, + string id, + string[] move, + string[] wait, + string[] play, + string[] eat, + string[] sleep, + string[] hunt, + string[] fight, + string[] social, + string[] laugh, + string[] work) + { + var bags = new Dictionary(StringComparer.Ordinal) + { + ["move"] = Contextualize(move), + ["wait"] = Contextualize(wait), + ["play"] = Contextualize(play), + ["eat"] = Contextualize(eat), + ["sleep"] = Contextualize(sleep), + ["hunt"] = Contextualize(hunt), + ["fight"] = Contextualize(fight), + ["social"] = Contextualize(social), + ["laugh"] = Contextualize(laugh), + ["work"] = Contextualize(work) + }; + voices[id] = new ActivitySpeciesVoice(id, bags); + } + + private static void AddLiquid( + Dictionary voices, + string id, + string verb, + string first, + string second) + { + if (voices.TryGetValue(id, out ActivitySpeciesVoice voice)) + { + voice.SetLiquidBag(verb, Contextualize(V(first, second))); + } + } + + private static string[] V(string first, string second) + { + return new[] { first, second }; + } + + private static string[] Contextualize(string[] phrases) + { + if (phrases == null) + { + return null; + } + + var result = new string[phrases.Length]; + for (int i = 0; i < phrases.Length; i++) + { + result[i] = (phrases[i] ?? "") + "{place_at}{job_as}"; + } + + return result; + } +} diff --git a/IdleSpectator/ActivitySpeciesVoices.Animals.cs b/IdleSpectator/ActivitySpeciesVoices.Animals.cs new file mode 100644 index 0000000..ef256a1 --- /dev/null +++ b/IdleSpectator/ActivitySpeciesVoices.Animals.cs @@ -0,0 +1,445 @@ +using System.Collections.Generic; + +namespace IdleSpectator; + +public static partial class ActivitySpeciesVoiceCatalog +{ + private static void AddAnimalVoices(Dictionary voices) + { + Add( + voices, + "chicken", + V( + "takes high, quick steps with head bobbing", + "hurries along with wings tucked tight"), + V( + "balances on one foot while its comb tilts", + "holds still and turns one bright eye"), + V( + "flutters both wings in a brief burst", + "hops and tosses its head with beak raised"), + V( + "pecks up morsels with brisk jabs", + "pins a bite beneath one foot and tears at it"), + V( + "tucks its beak beneath a folded wing", + "settles with feathers puffed around its body"), + V( + "scratches twice before darting toward {target}", + "tilts the head toward {target} and pecks forward"), + V( + "leaps with both spurs aimed at {target}", + "beats both wings before pecking at {target}"), + V( + "clucks while brushing shoulders with another", + "circles close with soft beak taps"), + V( + "breaks into rapid clucks with beak wide", + "shakes its comb through a rattling cackle"), + V( + "rakes with both feet in alternating strokes", + "carries material pinched in its beak")); + + Add( + voices, + "crab", + V( + "scuttles sideways on sharply jointed legs", + "keeps its shell level through a quick sidestep"), + V( + "holds both claws ready while its eyestalks pivot", + "rests on bent legs with mouthparts ticking"), + V( + "taps its claws together and spins sideways", + "weaves between its own raised pincers"), + V( + "passes small bites from pincer to mouthparts", + "tears a morsel apart between mismatched claws"), + V( + "folds its legs beneath the rim of its shell", + "tucks both pincers close and stills its eyestalks"), + V( + "probes toward {target} with one open pincer", + "advances sideways toward {target} with claws spread"), + V( + "snaps both pincers at {target}", + "raises one heavy claw and sidesteps toward {target}"), + V( + "waves one claw while its antennae sweep forward", + "touches pincers with another in measured taps"), + V( + "clicks its pincers in a quick clattering run", + "chatters its mouthparts beneath raised eyestalks"), + V( + "sorts loose material between precise pincers", + "grips and shifts a load one sidestep at a time")); + + Add( + voices, + "crocodile", + V( + "crawls on splayed legs with belly held low", + "drives forward while its heavy tail counterbalances"), + V( + "lies motionless with only its eyes shifting", + "holds its long jaw slightly parted"), + V( + "rolls its armored body and lashes its tail", + "makes a short feint with jaws agape"), + V( + "clamps a meal between interlocking teeth", + "tilts its broad head back to gulp a bite"), + V( + "rests its chin with legs drawn against its flanks", + "stills beneath ridged eyelids and folded jaws"), + V( + "creeps toward {target} with belly pressed low", + "freezes before surging toward {target}"), + V( + "parts long jaws before snapping at {target}", + "lashes an armored tail toward {target}"), + V( + "bumps an armored snout against another", + "rumbles low while resting jaw beside jaw"), + V( + "claps its jaws around a deep throat rumble", + "shudders through a run of gravelly bellows"), + V( + "shoves a load with its plated snout", + "drags a load in the hinge of its jaws")); + + Add( + voices, + "frog", + V( + "springs forward on long folded hind legs", + "bounds with forefeet tucked beneath its chest"), + V( + "squats still while its round eyes swivel", + "holds its crouch as its throat pulses"), + V( + "pops through a chain of short, crooked hops", + "bounces in place with toes spread wide"), + V( + "flicks its tongue around a morsel", + "swallows a bite with a slow double blink"), + V( + "folds all four legs beneath its body", + "lowers its eyelids over still, round eyes"), + V( + "swivels both eyes toward {target} before springing", + "crouches low before flicking a sticky tongue toward {target}"), + V( + "kicks both hind feet at {target}", + "thrusts both splayed forefeet toward {target}"), + V( + "pulses its throat through a carrying croak", + "answers another with quick chirps and toe taps"), + V( + "croaks in a bouncing, uneven chain", + "puffs its throat sac around a chirping trill"), + V( + "pushes a load with its blunt head", + "braces its hind legs and shifts a load forward")); + + Add( + voices, + "ostrich", + V( + "covers distance in long, springing strides", + "paces forward while its tall neck sways"), + V( + "stands high with its neck drawn straight", + "shifts between long legs while its head scans"), + V( + "fans its short wings and prances in a circle", + "bobs its long neck between skipping steps"), + V( + "pinches up a morsel with its flat beak", + "cranes its neck high to swallow a bite"), + V( + "folds its long legs beneath a feathered body", + "rests with its neck curved across its back"), + V( + "turns the long neck toward {target}", + "rushes toward {target} in lengthening strides"), + V( + "swings a long leg toward {target}", + "drives a two-toed kick at {target}"), + V( + "weaves its neck beside another's raised neck", + "fans loose wing feathers through a measured display"), + V( + "booms with its neck stretched upright", + "clacks its broad beak between hollow calls"), + V( + "carries material in its beak", + "nudges a load forward with its feathered breast")); + + Add( + voices, + "penguin", + V( + "waddles in short steps with flippers angled out", + "rocks forward from heel to heel"), + V( + "stands upright with both flippers tucked", + "shifts its weight across broad webbed feet"), + V( + "spins with its stiff flippers spread", + "drops to its belly and scoots with both feet"), + V( + "snaps its narrow beak around a bite", + "tilts its head back to swallow a morsel"), + V( + "stands with its beak tucked beneath one flipper", + "rests on its belly with flippers held close"), + V( + "leans toward {target} with beak aligned", + "waddles toward {target} in rapid steps"), + V( + "slaps both flippers at {target}", + "leans forward and pecks at {target}"), + V( + "taps beaks with another between low calls", + "presses shoulder to shoulder in a compact cluster"), + V( + "brays with its beak lifted high", + "shakes its chest through a run of sharp honks"), + V( + "shoves a load ahead with its chest", + "carries material at the tip of its beak")); + AddLiquid( + voices, + "penguin", + "move", + "cuts through the water with powerful flipper strokes", + "torpedoes beneath the surface with feet trailing"); + AddLiquid( + voices, + "penguin", + "wait", + "treads water with small strokes of both feet", + "bobs at the surface with flippers spread"); + AddLiquid( + voices, + "penguin", + "play", + "porpoises above the surface with flippers driving", + "loops underwater and twists through rising bubbles"); + AddLiquid( + voices, + "penguin", + "sleep", + "rests afloat with its beak tucked low", + "dozes while bobbing at the surface"); + + Add( + voices, + "piranha", + V( + "propels its compact body with quick tail beats", + "glides forward with paired fins angled wide"), + V( + "holds position with its tail twitching", + "hangs still while its gill covers pulse"), + V( + "wheels in a tight circle with fins flared", + "darts forward and pivots on a rigid tail flick"), + V( + "shears off a bite with triangular teeth", + "nips rapid mouthfuls with its jaw working"), + V( + "slows its tail until only its gills move", + "hangs nearly motionless with fins folded close"), + V( + "darts after {target} with gill covers flaring", + "darts toward {target} with jaws open"), + V( + "bites at {target} with interlocking teeth", + "whips the tail and snaps toward {target}"), + V( + "aligns beside another with fins matching pace", + "circles another while flicking its paired fins"), + V( + "clacks its triangular teeth in a rapid chatter", + "works its lower jaw through a clicking rattle"), + V( + "nudges a load with its blunt snout", + "grips material between its teeth")); + AddLiquid( + voices, + "piranha", + "move", + "slices through the water with rapid tail beats", + "darts beneath the surface with fins tucked"); + AddLiquid( + voices, + "piranha", + "wait", + "hovers in the water with fins fanning", + "faces the current while its tail makes tiny corrections"); + AddLiquid( + voices, + "piranha", + "play", + "zigzags through the water in sudden bursts", + "spirals beneath the surface with fins flared"); + AddLiquid( + voices, + "piranha", + "sleep", + "drifts underwater with its tail barely moving", + "rests below the surface with gills pulsing"); + + Add( + voices, + "seal", + V( + "inches forward by bunching its heavy body", + "pulls itself along with broad foreflippers"), + V( + "props its chest up while whiskers quiver", + "rests on folded flippers with muzzle raised"), + V( + "rolls onto its side and bats with both flippers", + "arches its body through a clumsy half-turn"), + V( + "grips a meal between pointed teeth", + "tears off a bite with a sharp head shake"), + V( + "curls with its flippers pressed against its sides", + "rests its whiskered muzzle on one foreflipper"), + V( + "turns a whiskered muzzle toward {target}", + "lunges toward {target} with jaws ready"), + V( + "bares pointed teeth before snapping at {target}", + "surges chest-first and bites toward {target}"), + V( + "touches whiskers with another between low grunts", + "leans close and answers another with a short bark"), + V( + "barks in bouncing, chesty bursts", + "huffs through its whiskers between blunt honks"), + V( + "pushes a load with its broad chest", + "grips a load between its teeth and pulls")); + AddLiquid( + voices, + "seal", + "move", + "swims through the water with sweeping hind-flipper strokes", + "dives beneath the surface with foreflippers tucked"); + AddLiquid( + voices, + "seal", + "wait", + "floats upright with its whiskered muzzle above water", + "treads water with slow hind-flipper sweeps"); + AddLiquid( + voices, + "seal", + "play", + "rolls underwater with bubbles streaming past its whiskers", + "slaps the surface and dives through the splash"); + AddLiquid( + voices, + "seal", + "sleep", + "dozes afloat with its muzzle tipped above water", + "rests at the surface with flippers drifting"); + + Add( + voices, + "snake", + V( + "slithers forward in deep S-shaped bends", + "glides by rippling its ribs beneath smooth scales"), + V( + "coils with its head raised above the loops", + "holds still while a forked tongue samples nearby scents"), + V( + "loops its body around itself in quick turns", + "darts its head between shifting coils"), + V( + "opens its hinged jaw around a meal", + "works a bite backward with alternating jaw steps"), + V( + "coils tightly with its head at the center", + "rests its jaw across a loop of scaled body"), + V( + "flicks a forked tongue toward {target}", + "glides toward {target} with head aligned"), + V( + "strikes with open jaws at {target}", + "coils and snaps toward {target}"), + V( + "rests in parallel coils beside another", + "follows another's motion with matched tongue flicks"), + V( + "hisses in short bursts with jaws parted", + "flicks its tongue through a breathy rattle"), + V( + "loops around a load and pulls", + "braces its coils to shift a load")); + + Add( + voices, + "turtle", + V( + "plods on clawed feet while its shell rocks", + "extends its neck and pulls its shell forward"), + V( + "withdraws its head halfway beneath the shell rim", + "blinks slowly with all four feet braced"), + V( + "rocks its shell from side to side", + "paws forward with alternating forefeet"), + V( + "clips off a bite with its hard beak", + "cranes its neck to reach a morsel"), + V( + "draws its limbs beneath the shell", + "rests its chin just inside the shell rim"), + V( + "extends the neck toward {target}", + "holds the hard beak open while lunging toward {target}"), + V( + "drives forward shell-first toward {target}", + "bites at {target} with a hard beak"), + V( + "stretches its neck beside another", + "taps its shell rim against another's shell"), + V( + "clicks its beak between throaty chirps", + "bobs its head through a rasping chuckle"), + V( + "shoves a load with the front of its shell", + "braces its claws and pushes a load forward")); + AddLiquid( + voices, + "turtle", + "move", + "paddles through the water with all four feet", + "glides beneath the surface with its shell level"); + AddLiquid( + voices, + "turtle", + "wait", + "floats at the surface with its nostrils raised", + "holds beneath the water with feet spread wide"); + AddLiquid( + voices, + "turtle", + "play", + "rolls underwater with its shell turning slowly", + "paddles in a circle just below the surface"); + AddLiquid( + voices, + "turtle", + "sleep", + "rests underwater with limbs tucked beneath its shell", + "dozes at the surface with its chin above water"); + } +} diff --git a/IdleSpectator/ActivitySpeciesVoices.Civs.cs b/IdleSpectator/ActivitySpeciesVoices.Civs.cs new file mode 100644 index 0000000..262c0e0 --- /dev/null +++ b/IdleSpectator/ActivitySpeciesVoices.Civs.cs @@ -0,0 +1,465 @@ +using System.Collections.Generic; + +namespace IdleSpectator; + +public static partial class ActivitySpeciesVoiceCatalog +{ + private static void AddCivVoices(Dictionary voices) + { + Add(voices, "civ_acid_gentleman", + V("paces with back held straight", "advances in an upright stride"), + V("holds chin high and shoulders level", "repeats each gesture at the same angle"), + V("practices an elaborate bow", "turns with one arm held across the chest"), + V("divides the meal into equal portions", "takes one bite between upright pauses"), + V("reclines with back still straight", "rests with every limb folded together"), + V("stalks {target} in even steps", "closes on {target} behind a rigid guard"), + V("slashes at {target} with a caustic hand", "parries {target} with a rigid forearm"), + V("offers another a deep bow", "trades clipped greetings with nearby company"), + V("lets out a clipped chuckle", "muffles a short laugh behind one hand"), + V("measures the task twice", "keeps records in matching groups")); + + Add(voices, "civ_alpaca", + V("steps lightly with chin held high", "pads onward in a woolly sway"), + V("plants all four feet and listens", "chews slowly with ears turned forward"), + V("bounds sideways in sudden hops", "prances through a woolly little dance"), + V("pulls mouthfuls from the meal", "chews each bite in a steady rhythm"), + V("tucks long legs beneath thick fleece", "settles into an upright doze"), + V("tracks {target} with ears pricked", "presses after {target} in springing strides"), + V("kicks at {target} with both hind feet", "shoulders {target} behind dense fleece"), + V("leans close for a soft-nosed greeting", "hums in low pulses beside nearby company"), + V("bursts into a breathy warble", "shakes all over with a muffled chuckle"), + V("carries the load", "sorts material between both forefeet")); + + Add(voices, "civ_armadillo", + V("scuttles with plated back bobbing", "trots onward in a low quick rhythm"), + V("sits back on haunches to listen", "holds still inside a half-curled shell"), + V("curls fully and spins", "uncurls and scampers in a tight loop"), + V("breaks the meal apart", "snuffles up each bite"), + V("curls tight beneath overlapping plates", "sleeps fully enclosed by the shell"), + V("sniffs out {target} with lowered snout", "trundles after {target} beneath a lowered shell"), + V("rams {target} in a plated roll", "claws at {target} from a crouched guard"), + V("taps shells in a brisk greeting", "chitters softly with nearby company"), + V("uncurls with a rattling little laugh", "chitters until the back plates shake"), + V("reinforces the construction", "hauls a load against one plated shoulder")); + + Add(voices, "civ_bear", + V("lumbers forward on heavy paws", "advances with a broad rolling gait"), + V("rises tall with head held high", "rests heavily on broad forepaws"), + V("rears and sways from paw to paw", "bounds through a spinning charge"), + V("scoops the meal with both paws", "licks the meal from broad claws"), + V("curls around one heavy forepaw", "drops into a deep rumbling sleep"), + V("follows {target} by scent", "surges after {target} with head low"), + V("mauls at {target} with sweeping claws", "rears over {target} and crashes down"), + V("claps another on the shoulder", "shares a booming greeting with nearby company"), + V("roars with belly-deep amusement", "huffs into a rumbling chuckle"), + V("lifts the load above the chest", "braces the construction with both forepaws")); + + Add(voices, "civ_beetle", + V("clicks onward on lacquered legs", "traces a straight line on quick feet"), + V("locks every joint and watches", "turns both feelers in slow arcs"), + V("spins on tightly planted legs", "clambers over its own folded limbs"), + V("clips the meal apart", "works steadily along each bite"), + V("folds all legs beneath a hard wing case", "rests sealed beneath the carapace"), + V("feels out {target} with raised antennae", "skitters after {target} in a straight line"), + V("drives a horn at {target}", "meets {target} behind a polished carapace"), + V("touches antennae in greeting", "joins nearby company with evenly spaced clicks"), + V("rattles wing cases through a laugh", "clicks out a quick dry chuckle"), + V("sorts material with repeated leg motions", "fits the construction edge to edge")); + + Add(voices, "civ_buffalo", + V("plods ahead with horns level", "advances in heavy rolling steps"), + V("stands solidly and chews the cud", "waits without shifting a hoof"), + V("bucks in a broad turning arc", "charges forward before wheeling back"), + V("tears off a mouthful", "grinds the meal with slow jaw strokes"), + V("kneels heavily with horns lowered", "sleeps inside a guard of curved horns"), + V("closes on {target} at a steady run", "drives after {target} with horns forward"), + V("gores at {target} from a planted stance", "buffets {target} with a massive brow"), + V("presses foreheads in greeting", "forms a horn-outward circle with nearby company"), + V("snorts into a rolling thunderous laugh", "bellows between blunt chuckles"), + V("pulls the load", "drives the construction into place")); + + Add(voices, "civ_candy_man", + V("skips along on glossy heels", "twirls onward in quick steps"), + V("straightens up and waits", "stands with stripes held vertically"), + V("spins in quick circles", "hops through a springy little dance"), + V("nibbles around the meal", "snaps off one bite"), + V("curls into a compact crinkling shape", "rests with glossy limbs folded together"), + V("feints toward {target} in a wide turn", "dashes after {target} in zigzag steps"), + V("clubs at {target} with a hardened fist", "pelts {target} with rapid hand strikes"), + V("greets another with a full turn", "offers quick bows to nearby company"), + V("tinkles with a high laugh", "crackles into rapid giggles"), + V("groups the work with both hands", "folds the craft in repeated motions")); + + Add(voices, "civ_capybara", + V("ambles onward on evenly placed feet", "takes broad slow steps"), + V("sits on folded haunches and observes", "rests a blunt chin against the chest"), + V("bobs the head in a low rhythm", "rolls slowly from side to side"), + V("chews each bite in slow jaw strokes", "holds the meal between both forepaws"), + V("sinks into a close compact curl", "dozes with nose tucked to the chest"), + V("trails {target} in even steps", "closes on {target} without breaking stride"), + V("shoves {target} with a solid shoulder", "snaps at {target} from a low brace"), + V("shifts aside for nearby company", "rests flank to flank beside another"), + V("chuckles in soft bubbling pulses", "squeaks once before a low laugh"), + V("keeps a measured working pace", "moves the load with both forepaws")); + + Add(voices, "civ_cat", + V("threads onward on silent paws", "saunters ahead with tail high"), + V("sits upright and washes one paw", "watches nearby motion through narrowed eyes"), + V("pounces on its own twitching tail", "springs sideways with back arched"), + V("takes bites from the meal", "pins each bite beneath one paw"), + V("turns three circles before curling tight", "tucks nose beneath one flank"), + V("stalks {target} belly-low", "springs after {target} in sudden silence"), + V("rakes at {target} with quick claws", "arches against {target} with teeth bared"), + V("winds around another in greeting", "offers nearby company a slow blink"), + V("chirps into a short laugh", "purrs until the sound breaks into chuckles"), + V("sorts material with paw tips", "checks the work from every angle")); + + Add(voices, "civ_chicken", + V("struts in quick head-bobbing steps", "hurries onward on scratching feet"), + V("stands high and fluffs every feather", "freezes with one eye turned sideways"), + V("flaps into a short hopping turn", "hops and pivots on quick feet"), + V("pecks the meal repeatedly", "scratches twice before each bite"), + V("tucks the beak beneath one wing", "settles into a rounded sleeping crouch"), + V("rushes after {target} with wings spread", "tracks {target} in darting circles"), + V("spurs at {target} in a flapping leap", "pecks at {target} between sharp sidesteps"), + V("clucks over civic news with another", "gathers near company with rapid calls"), + V("cackles in a rapid rising burst", "erupts into breathless clucks"), + V("sorts material with the beak", "scratches briskly at the task")); + + Add(voices, "civ_cow", + V("walks with a broad pendulous sway", "takes steady repeated steps"), + V("chews while standing hipshot", "waits without shifting a hoof"), + V("trots in a broad uneven circle", "flicks the tail and kicks up both heels"), + V("pulls in a mouthful", "chews the meal in repeated cycles"), + V("folds down into a heavy resting curl", "rests with muzzle against one flank"), + V("follows {target} with steady nostrils", "trots after {target} with lowered head"), + V("hooks at {target} with a sweeping horn", "kicks at {target} from a braced stance"), + V("greets another with a mellow call", "stands shoulder to shoulder with nearby company"), + V("moos into a long wobbling laugh", "shakes the dewlap with deep amusement"), + V("draws a steady load", "works the task at an even pace")); + + Add(voices, "civ_crab", + V("sidesteps briskly with claws held high", "scuttles onward on quick jointed legs"), + V("settles into a low armored crouch", "raises both eyestalks and waits"), + V("waves alternating claws in a quick dance", "spins through a tight sideways circle"), + V("snips the meal into mouthfuls", "holds each bite between polished pincers"), + V("tucks every limb beneath the shell", "rests fully closed inside the shell"), + V("circles {target} in tightening sidesteps", "scuttles after {target} with claws open"), + V("pinches at {target} with one heavy claw", "hammers {target} behind a raised shell"), + V("waves a claw in brisk greeting", "trades measured taps with nearby company"), + V("clacks both claws in choppy amusement", "bubbles out a sideways little laugh"), + V("cuts material with paired claws", "carries the load in paired claws")); + + Add(voices, "civ_crocodile", + V("prowls low with tail sweeping behind", "slides onward on splayed feet"), + V("lies motionless with jaws slightly open", "waits with one eye open"), + V("spins around its sweeping tail", "snaps between quick turns"), + V("gulps down a bite", "pins the meal before tearing it apart"), + V("sinks flat into a resting pose", "sleeps with one eye barely closed"), + V("creeps toward {target} in complete stillness", "bursts after {target} from a dead stop"), + V("clamps down on {target} with crushing jaws", "whips the tail across {target}"), + V("rumbles a low greeting to another", "rests flank to flank with nearby company"), + V("chuffs through a deep toothy laugh", "rattles the throat through low chuckles"), + V("drags the load", "guards the work without moving")); + + Add(voices, "civ_dog", + V("trots ahead with tail swinging", "bounds onward with ears streaming"), + V("sits with ears trained forward", "waits with paws tapping"), + V("bows low before springing in a circle", "chases its own tail in bounding loops"), + V("devours the meal in rapid bites", "holds a bite between both forepaws"), + V("circles three times and drops with a sigh", "sleeps with paws twitching"), + V("tracks {target} nose-first", "races after {target} with ears streaming"), + V("bites at {target} and holds fast", "surges into {target} with one shoulder"), + V("greets another with repeated sniffs", "leans one flank against nearby company"), + V("pants into a barking laugh", "yips through repeated chuckles"), + V("carries the load at a trot", "watches the work with ears forward")); + + Add(voices, "civ_fox", + V("slips forward on black paws", "weaves onward in a crossing line"), + V("wraps the tail once around the feet", "listens with ears angled apart"), + V("pounces on its own sweeping tail", "feints one way before springing another"), + V("nibbles around the meal first", "takes one bite at a time"), + V("curls nose-deep into a sweeping tail", "folds into a narrow sleeping curl"), + V("trails {target} with angled steps", "cuts ahead of {target} with a sudden dash"), + V("feints past {target} and snaps back", "slashes at {target} from an oblique angle"), + V("trades low whispers with another", "bows to nearby company with ears raised"), + V("barks out a quick laugh", "masks a short chuckle with the tail tip"), + V("keeps records in narrow groups", "checks each part of the work")); + + Add(voices, "civ_frog", + V("hops forward in compact arcs", "springs onward in quick bounds"), + V("squats with throat pulsing slowly", "holds still and blinks"), + V("leapfrogs over its own folded legs", "bounces through a springy little turn"), + V("flicks each bite straight into the mouth", "swallows a bite with a round-eyed blink"), + V("folds into a tiny crouched form", "dozes with chin resting on both hands"), + V("bounds after {target} in long leaps", "waits before springing toward {target}"), + V("kicks at {target} with both feet", "lashes at {target} with a whipping tongue"), + V("croaks a greeting to another", "joins nearby company in rhythmic calls"), + V("ribbits into a bubbling laugh", "inflates the throat with booming amusement"), + V("reaches upper parts of the construction", "presses the work with both hands")); + + Add(voices, "civ_garlic_man", + V("marches with papery layers rustling", "bobs onward on knobby feet"), + V("smooths one loose outer layer", "stands bundled beneath folded layers"), + V("spins with papery arms spread", "bobs from side to side in a rustling dance"), + V("pulls apart the meal segment by segment", "crunches one bite at a time"), + V("folds into its own dry layers", "gathers every loose layer before sleeping"), + V("follows {target} by the faintest scent", "rolls after {target} in a papery rush"), + V("buffets {target} with a knotted head", "strikes at {target} with both hands"), + V("greets another with a full-body bob", "rustles beside nearby company"), + V("rustles into a crackling laugh", "splits into quick giggles"), + V("groups material between both hands", "layers the craft with knobby hands")); + + Add(voices, "civ_goat", + V("clatters onward on nimble split hooves", "picks a quick high-stepping route"), + V("balances on four feet while waiting", "chews with one ear cocked"), + V("leaps high simply to twist around", "bounds sideways on springy legs"), + V("tears off a mouthful", "samples every part of the meal"), + V("kneels into a compact sleeping curl", "sleeps with legs tucked tightly"), + V("scrambles after {target} without slowing", "charges {target} with chin tucked"), + V("butts {target} with curled horns", "kicks at {target} from a sudden pivot"), + V("nuzzles another in brisk greeting", "joins nearby company with noisy bleats"), + V("bleats out a brash hiccupping laugh", "snorts between sharp little chuckles"), + V("works the task from a high stance", "clears material with quick bites")); + + Add(voices, "civ_hyena", + V("lope-walks with sloping shoulders", "circles onward without slowing"), + V("sits crookedly with jaws parted", "sniffs with head turning side to side"), + V("bounds in looping bursts", "spins with jaws open and shoulders lowered"), + V("cracks through one bite first", "guards the meal between broad jaws"), + V("drops into a loose sleeping sprawl", "sleeps with one ear twitching"), + V("runs {target} down in repeated stages", "fans wide before closing on {target}"), + V("crushes at {target} with heavy jaws", "harrows {target} with darting bites"), + V("shoulder-bumps another in greeting", "calls nearby company into a close group"), + V("whoops into a cascading laugh", "cackles until the flanks heave"), + V("breaks material into separate parts", "works the task with repeated jaw motions")); + + Add(voices, "civ_lemon_man", + V("zips along with a springy step", "rolls onward before popping upright"), + V("polishes the rind with one hand", "waits with arms folded over a pointed chest"), + V("bounces lightly on rounded feet", "spins on one pointed toe"), + V("takes a bite and puckers", "presses the meal before another bite"), + V("rests with limbs tucked against the rind", "curls around the pointed crown"), + V("pursues {target} in quick cutting turns", "rolls after {target} at sudden speed"), + V("sprays a stinging burst at {target}", "jabs at {target} with the pointed crown"), + V("greets another with a brisk nod", "pivots toward each nearby speaker"), + V("squeaks into a sharp fizzy laugh", "puckers before bursting into giggles"), + V("presses the material with both palms", "groups the work into even sections")); + + Add(voices, "civ_liliar", + V("glides with layered frills whispering", "drifts onward in measured steps"), + V("folds slender hands beneath the chin", "stands statuesque with head raised"), + V("weaves layered arms through a slow turn", "unfurls through a spinning dance"), + V("takes one bite between slender fingers", "eats the meal in equal morsels"), + V("closes layered frills around the body", "rests upright with hands folded together"), + V("tracks {target} without blinking", "sweeps toward {target} along a curved path"), + V("lashes at {target} with a supple stem", "cuts at {target} with sharpened leaves"), + V("exchanges a sweeping bow with another", "turns nearby company into a facing circle"), + V("chimes with a light silvery laugh", "hides a trilling chuckle behind slim fingers"), + V("braids material into the craft", "sets the construction with both hands")); + + Add(voices, "civ_monkey", + V("scampers ahead with arms swinging low", "bounds onward in long-armed strides"), + V("squats and picks through its fur", "hangs by the tail while watching"), + V("swings between its own planted hands", "somersaults with the tail curled high"), + V("takes the meal in quick bites", "stuffs both cheeks before chewing"), + V("curls up with tail wound around the body", "sleeps with both hands beneath the head"), + V("swings toward {target} before dropping close", "scrambles after {target} in quick bounds"), + V("clubs at {target} with heavy fists", "grapples {target} with hands and tail"), + V("grooms another during rapid chatter", "waves both hands at nearby company"), + V("chatters into a rolling shriek of laughter", "slaps both knees and howls"), + V("handles several parts of the task", "reaches upper parts of the construction")); + + Add(voices, "civ_penguin", + V("waddles forward with flippers held out", "slides onward on a rounded belly"), + V("rocks from heel to heel", "stands in a close inward-facing huddle"), + V("spins belly-down in a tight circle", "bobs through a brisk flipper dance"), + V("tips back a morsel", "pecks the meal apart with a narrow bill"), + V("tucks the bill beneath one flipper", "sleeps upright with flippers folded"), + V("slides after {target} in bounding lunges", "waddles toward {target} in short strides"), + V("jabs at {target} with a stiff beak", "flipper-slaps {target} from a planted stance"), + V("bows stiffly to another", "huddles shoulder to shoulder with nearby company"), + V("honks into a stuttering laugh", "wobbles through short chuckles"), + V("slides the load with steady force", "checks each part of the task in order")); + + Add(voices, "civ_piranha", + V("darts onward in brisk fin-led bursts", "weaves ahead with snapping turns"), + V("hovers with fins making tiny corrections", "holds position with jaws slightly open"), + V("loops around its own flicking tail", "snaps between quick turns"), + V("shears a bite from the meal", "tears each morsel into bites"), + V("settles with fins tucked close", "rests almost motionless"), + V("homes in on {target} with sudden speed", "circles {target} before rushing close"), + V("tears at {target} with flashing teeth", "darts past {target} in repeated bites"), + V("clicks teeth in brisk greeting", "moves tightly with nearby company"), + V("chatters into a sharp staccato laugh", "snaps the jaws between quick giggles"), + V("trims material in rapid cuts", "sorts the task into short stages")); + + Add(voices, "civ_rabbit", + V("hops along with ears streaming back", "bounds onward in quick doubles"), + V("sits tall and rotates both ears", "twitches the nose while waiting"), + V("leaps high and twists in midair", "boxes with alternating forepaws"), + V("nibbles the meal into scalloped edges", "holds a bite between both paws"), + V("folds long ears over the shoulders", "rests in a compact sleeping crouch"), + V("zigzags after {target} in rapid bounds", "tracks {target} with ears pitched forward"), + V("kicks at {target} with both hind feet", "boxes at {target} with flashing forepaws"), + V("touches noses twice in greeting", "shares rapid civic news with another"), + V("snickers through a trembling nose", "thumps once before breaking into laughter"), + V("carries the load in quick hops", "weaves material with both forepaws")); + + Add(voices, "civ_rat", + V("scurries onward with whiskers forward", "threads ahead in low quick steps"), + V("sits back and combs both whiskers", "waits with nose twitching rapidly"), + V("rolls between its own nimble paws", "spins and darts back with a squeak"), + V("gnaws the meal repeatedly", "cups a bite between both paws"), + V("curls tightly around the tail", "sleeps with forepaws tucked beneath the chin"), + V("sniffs a winding trail toward {target}", "cuts ahead of {target} with a quick dash"), + V("bites at {target} from below", "swipes at {target} with sharp claws"), + V("trades whispered civic news with another", "inclines its whiskers toward nearby company"), + V("squeaks into a high laugh", "wrings both paws through shrill giggles"), + V("sorts material by size", "checks each part of the task")); + + Add(voices, "civ_rhino", + V("stomps onward behind a lowered horn", "pushes ahead in broad heavy steps"), + V("stands immovable with ears raised", "rests thick chin against the chest"), + V("bucks through a heavy turning charge", "tosses the head and stamps both feet"), + V("grinds through the meal", "scoops up bites with the lower lip"), + V("drops heavily into a resting crouch", "sleeps with the horn angled clear"), + V("charges straight at {target}", "pounds after {target} in heavy steps"), + V("drives the horn at {target}", "tramples toward {target} behind thick hide"), + V("bumps shoulders twice in greeting", "stands solidly beside nearby company"), + V("bellows into a rough booming laugh", "snorts until the heavy sides shake"), + V("breaks material apart", "pushes the construction into place")); + + Add(voices, "civ_scorpion", + V("skitters forward beneath an arched tail", "advances on eight jointed feet"), + V("holds both pincers open and still", "waits with the stinger raised"), + V("waves both claws in alternating rhythm", "spins beneath a curling tail"), + V("snips the meal into paired portions", "draws each bite between small mouthparts"), + V("folds pincers beneath the plated body", "rests with the tail curled over the back"), + V("angles both pincers toward {target}", "skitters after {target} with tail cocked"), + V("stings at {target} over a raised guard", "seizes {target} between heavy pincers"), + V("crosses claws in a salute", "holds a fixed distance from nearby company"), + V("clicks both pincers in dry amusement", "trembles the tail through a rasping laugh"), + V("handles material between both pincers", "cuts and grips each part of the task")); + + Add(voices, "civ_seal", + V("galumphs ahead on broad flippers", "slides onward with whiskers streaming"), + V("props upright and balances on the belly", "rests whiskered chin against the chest"), + V("spins in a rolling turn", "claps flippers after a full-body flop"), + V("swallows a morsel whole", "pins the meal beneath one flipper"), + V("flops into a loose sleeping curl", "dozes with nose resting on the tail"), + V("slides after {target} in bounding lunges", "tracks {target} with twitching whiskers"), + V("body-slams {target} from a rolling start", "bites at {target} behind raised flippers"), + V("claps a loud welcome to another", "leans into nearby company whisker-first"), + V("barks into a bouncing breathless laugh", "claps through a series of honking chuckles"), + V("moves the load with both flippers", "smooths the work with both flippers")); + + Add(voices, "civ_sheep", + V("trots in a soft close-stepping rhythm", "follows a steady path with fleece bobbing"), + V("stands with fleece softly settling", "chews with drooping ears"), + V("bounds high on springy legs", "pushes nose-first into a woolly tumble"), + V("crops the meal in even bites", "chews beneath a woolly fringe"), + V("folds into a deep woolly curl", "sleeps with muzzle against one flank"), + V("follows {target} along a steady line", "rushes after {target} with head lowered"), + V("rams {target} with a thick brow", "kicks at {target} behind a woolly guard"), + V("gathers close for low civic chatter", "greets nearby company with soft bleats"), + V("bleats into a soft wavering laugh", "shivers the fleece with low chuckles"), + V("sorts and groups material", "moves the load in even motions")); + + Add(voices, "civ_snake", + V("glides forward in polished curves", "threads onward without a sound"), + V("coils tightly and watches", "raises the head with tongue flicking slowly"), + V("loops around its own rising coils", "sways through a winding rhythm"), + V("works the meal down in repeated gulps", "unhinges the jaw around a bite"), + V("knots into a compact sleeping coil", "rests beneath overlapping loops"), + V("tastes the trail toward {target}", "flows after {target} without a sound"), + V("strikes at {target} from a tight coil", "wraps {target} in tightening loops"), + V("loops into a low bow", "shares low whispers with another"), + V("hisses into a winding laugh", "rattles the tail through short chuckles"), + V("threads material in repeated loops", "winds material into the craft")); + + Add(voices, "civ_turtle", + V("plods onward beneath a ridged shell", "takes each step at a measured pace"), + V("draws halfway into the shell and waits", "rests chin on the shell rim"), + V("rocks carefully from side to side", "spins slowly on the curved shell"), + V("clips off a bite with a beaked mouth", "chews the meal in slow jaw strokes"), + V("withdraws every limb beneath the shell", "settles into a low resting pose"), + V("follows {target} without changing pace", "intercepts {target} along a direct line"), + V("snaps at {target} from behind the shell", "drives the shell edge into {target}"), + V("nods through a long greeting", "shares quiet company without crowding another"), + V("wheezes into a slow creaking laugh", "bobs the head through quiet chuckles"), + V("checks each construction joint", "carries a load beneath the shell")); + + Add(voices, "civ_unicorn", + V("canters with mane streaming", "prances onward on lifted hooves"), + V("stands with the horn lifted", "smooths the mane with repeated turns"), + V("dances an even stepping pattern", "tosses the mane through a high rear"), + V("selects one bite at a time", "takes morsels from the meal"), + V("folds long legs beneath the body", "rests with the horn angled high"), + V("gallops after {target} in a direct line", "closes on {target} with horn lowered"), + V("thrusts the horn at {target}", "strikes at {target} with raised hooves"), + V("offers another a deep bow", "leads nearby company in high steps"), + V("trills into a clear ringing laugh", "tosses the mane through short chuckles"), + V("engraves the craft with the horn tip", "draws a load with high steps")); + + Add(voices, "civ_wolf", + V("trots forward with shoulders rolling", "ranges onward without slowing"), + V("sits with tail wrapped close", "tests every nearby scent"), + V("bows low before bounding sideways", "chases its own tail in a quick circle"), + V("tears off a bite", "holds the meal beneath one paw"), + V("turns into a tight sleeping circle", "rests with muzzle tucked to one flank"), + V("courses after {target} along the flank", "drives {target} toward a tighter line"), + V("slashes at {target} with repeated bites", "knocks {target} down from the side"), + V("touches muzzles in a close greeting", "raises a gathering call to nearby company"), + V("yips into a rough rising laugh", "howls between long chuckles"), + V("patrols around the work", "moves the load in close formation")); + + Add(voices, "dwarf", + V("strides on short powerful legs", "marches onward with beard tucked close"), + V("plants both feet and folds thick arms", "waits with chin resting on one fist"), + V("stamps through a compact turning dance", "drops into a deep braced crouch"), + V("cuts an even bite and chews", "wipes the meal from a braided mustache"), + V("drops into a stout sleeping curl", "sleeps beneath a beard spread wide"), + V("tracks {target} with eyes fixed forward", "closes on {target} behind a low guard"), + V("hammers at {target} with heavy fists", "hooks {target} with a thick forearm"), + V("clasps forearms with another", "trades craft boasts with nearby company"), + V("booms with a body-shaking laugh", "snorts through a bristling beard"), + V("aligns the construction", "drives the craft in a steady rhythm")); + + Add(voices, "elf", + V("walks with a light even stride", "passes onward in near-silent steps"), + V("stands listening with head slightly bowed", "rests long fingers against one palm"), + V("balances through a turning step", "springs lightly from foot to foot"), + V("divides the meal into equal portions", "takes bites with long fingers"), + V("reclines with limbs folded together", "sleeps with one hand lightly raised"), + V("follows {target} with soundless steps", "draws a direct line toward {target}"), + V("strikes at {target} with a swift hand", "cuts at {target} with a slender hand"), + V("exchanges brief greetings with another", "shares civic lore with nearby company"), + V("laughs in a clear ascending ripple", "muffles a short chuckle with long fingers"), + V("shapes the craft with fingertips", "keeps records of the work")); + + Add(voices, "human", + V("hurries onward with arms swinging", "threads ahead in alternating steps"), + V("shifts weight with eyes forward", "pauses to catch a breath"), + V("hops through a quick uneven rhythm", "turns a small stumble into a low bow"), + V("divides the meal before taking a bite", "alternates bites with short pauses"), + V("curls with knees drawn to the chest", "rests with hands folded over the chest"), + V("follows {target} through repeated turns", "runs down {target} with eyes fixed forward"), + V("strikes at {target} with ready hands", "holds firm against {target} from a balanced stance"), + V("trades names and civic news with another", "waves nearby company into the conversation"), + V("breaks into an open laugh", "slaps one knee through a broad grin"), + V("checks the record", "moves briskly from task to task")); + + Add(voices, "orc", + V("stalks forward in long heavy strides", "shoulders onward with tusks high"), + V("squats with fists resting on the thighs", "stands with fists held at the sides"), + V("wrestles against its own braced strength", "stomps through a rough turning dance"), + V("tears the meal apart with both hands", "crunches through each bite"), + V("drops into a heavy sleeping sprawl", "sleeps with one fist tightly closed"), + V("runs {target} down with pounding steps", "closes on {target} behind raised forearms"), + V("chops at {target} with repeated hand strikes", "headbutts {target} between heavy blows"), + V("greets another with a forearm clash", "swaps loud boasts with nearby company"), + V("barks out a harsh booming laugh", "bares both tusks in rumbling amusement"), + V("heaves material into place", "drives the construction with both arms")); + } +} diff --git a/IdleSpectator/ActivitySpeciesVoices.Fantasy.cs b/IdleSpectator/ActivitySpeciesVoices.Fantasy.cs new file mode 100644 index 0000000..611e0cb --- /dev/null +++ b/IdleSpectator/ActivitySpeciesVoices.Fantasy.cs @@ -0,0 +1,441 @@ +using System.Collections.Generic; + +namespace IdleSpectator; + +public static partial class ActivitySpeciesVoiceCatalog +{ + private static void AddFantasyVoices(Dictionary voices) + { + Add(voices, "UFO", + V("glides in a soundless level line", "banks on a seamless silver rim"), + V("hovers without sway", "holds its lights in a fixed pattern"), + V("spins its rim in a bright calibration", "winks its lower lights in sequence"), + V("draws energy into its hull", "sips a thin charge through its underside"), + V("dims every light and hangs inert", "settles into a low-power hover"), + V("hunts {target} with a narrow scanning beam", "hunts {target} by pulsing its sensor ring"), + V("attacks {target} with a precise lower ray", "attacks {target} with a hot lance"), + V("answers nearby signals with colored flashes", "circles once while broadcasting soft tones"), + V("trills in quick electronic notes", "warbles through a rising chord"), + V("maps its task with scanning light", "aligns its panels over a precision task")); + + Add(voices, "alien", + V("bounds forward on springing legs", "scuttles sideways with its torso upright"), + V("crouches low while its eyes keep moving", "folds its long limbs into a compact perch"), + V("taps both hands in a rapid rhythm", "braids its narrow fingers in quick patterns"), + V("nibbles a meal in tiny bites", "draws energy through its clicking mouthparts"), + V("tucks its head between raised shoulders", "curls its limbs around its chest"), + V("hunts {target} with clicking mouthparts", "hunts {target} with darting eyes"), + V("attacks {target} with a hooked wrist", "attacks {target} from springing legs"), + V("touches fingertips and trades soft clicks", "mirrors another's posture in greeting"), + V("chatters in bubbling bursts", "chirps until its throat sac trembles"), + V("sorts material by shape with four quick hands", "fits task material with narrow fingers")); + + Add(voices, "angle", + V("slides point-first along a rigid path", "turns through a perfectly sharp corner"), + V("balances on one luminous vertex", "hangs motionless between intersecting lines"), + V("folds and unfolds into crisp new shapes", "traces a tiny polygon with luminous edges"), + V("absorbs energy through its luminous edges", "draws energy into its center"), + V("flattens into a dim, closed figure", "rests with every edge neatly aligned"), + V("hunts {target} with straight geometric feelers", "hunts {target} from a rotating vertex"), + V("attacks {target} with a razor-straight edge", "attacks {target} by closing its angles"), + V("meets another outline edge to edge", "signals by changing the count of its corners"), + V("chimes with laughter from every taut edge", "chimes a laugh in two exact intervals"), + V("scores precise boundaries across task material", "squares a precision task one line at a time")); + + Add(voices, "assimilator", + V("pulls itself forward on braided tendrils", "rolls its shifting bulk in measured surges"), + V("anchors several feelers and studies nearby motion", "compresses into a listening mound"), + V("cycles through borrowed gestures with imperfect timing", "forms and dissolves little grasping hands"), + V("wraps a meal in a clear membrane", "draws energy beneath its rippling surface"), + V("seals itself inside a toughened outer skin", "slows its inner currents to a faint pulse"), + V("hunts {target} with tasting filaments", "hunts {target} through borrowed noses"), + V("attacks {target} with a hardened wedge-limb", "attacks {target} by splitting and snapping shut"), + V("echoes nearby calls in layered voices", "offers a borrowed face that melts into another"), + V("bursts into a chorus of mismatched chuckles", "repeats one bright laugh from several mouths"), + V("weaves task material into useful shapes", "repairs its own surface with matched fibers")); + + Add(voices, "civ_crystal_golem", + V("strides with heavy faceted steps", "advances as inner light shifts between joints"), + V("plants both feet and becomes statue-still", "rests broad hands against its angular knees"), + V("refracts its inner light between its palms", "makes colored glints dance across its facets"), + V("draws energy through glowing seams", "feeds energy into its core"), + V("dims its core behind crossed stone arms", "locks each joint and enters a glassy stillness"), + V("hunts {target} by reading vibrations through one fist", "hunts {target} with a prism-bright gaze"), + V("attacks {target} with a massive crystalline forearm", "attacks {target} with a faceted shoulder"), + V("flashes slow colors through its chest", "touches knuckles and shares a resonant hum"), + V("booms with a core-deep resonance", "tinkles from every edge in a bright cascade"), + V("sets a heavy load with unerring weight", "cuts hard material along clean shining planes")); + + Add(voices, "cold_one", + V("stalks on stiff frost-rimmed limbs", "skates forward without lifting its feet"), + V("stands rigid while cold vapor leaks between its teeth", "hunches beneath a mantle of hard rime"), + V("carves tiny spirals into its own rime", "grows and snaps an icicle from one fingertip"), + V("crushes a meal between pale jaws", "drinks energy through a whitening palm"), + V("folds into a compact frost-crusted crouch", "goes still beneath a shell of opaque ice"), + V("hunts {target} by feeling warmth through spread fingers", "hunts {target} with a heat-seeking stare"), + V("attacks {target} with frost-rimmed claws", "attacks {target} with a numbing palm"), + V("greets with a breath of glittering vapor", "clicks frozen teeth in a measured exchange"), + V("crackles like ice under strain", "gives a thin laugh of chiming shards"), + V("freezes task material into a firm join", "shapes task material with repeated icy claw strokes")); + + Add(voices, "crabzilla", + V("thunders sideways on towering jointed legs", "hauls its armored bulk ahead with earthshaking steps"), + V("settles its vast shell onto folded limbs", "holds both colossal pincers beneath its shell"), + V("clacks its pincers in a booming rhythm", "crosses colossal pincers in a measured display"), + V("pulverizes a meal between grinding plates", "scoops a meal toward hidden mouthparts"), + V("tucks every limb beneath its fortress shell", "rests with eye stalks drawn into armored sockets"), + V("hunts {target} with sweeping eye stalks", "hunts {target} through vibrations in its leg tips"), + V("attacks {target} with a colossal pincer", "attacks {target} with an immense plated claw"), + V("raises both pincers toward another", "clacks a greeting for nearby company"), + V("rattles its shell with a booming clatter", "clacks its mouthparts in rolling bursts"), + V("levels task material with measured pincer taps", "packs material flat beneath a broad claw")); + + Add(voices, "crystal_sword", + V("floats point-first in a gleaming rush", "sweeps ahead with its hilt held level"), + V("hangs upright with its point barely lowered", "rests suspended inside a faint prismatic shimmer"), + V("twirls through a rapid sequence of cuts", "bounces colored light from edge to edge"), + V("draws energy along its fuller", "absorbs energy through its jewel-set guard"), + V("sinks point-down into a dormant hover", "dims its facets and stills every tremor"), + V("hunts {target} with its edge turned forward", "hunts {target} with its testing point"), + V("attacks {target} in a clean cutting arc", "attacks {target} with its ringing flat"), + V("salutes by lifting its hilt", "crosses guards and trades a clear ringing note"), + V("sings with a high crystalline peal", "rings out in a bright descending cadence"), + V("trims task material with exact strokes", "scores material using its shining point")); + + Add(voices, "demon", + V("strides on smoking hooves", "lunges ahead with wings drawn tight"), + V("squats with claws curled beneath its body", "folds leathery wings around a red-hot core"), + V("flicks its own embers between hooked claws", "snaps its tail through its own sparks"), + V("tears a meal with blackened fangs", "gulps energy until its throat glows"), + V("hangs upside down behind wrapped wings", "dozes in a crouch as smoke curls from its nostrils"), + V("hunts {target} through heat-sensing nostrils", "hunts {target} with horns lowered"), + V("attacks {target} with a burning claw", "attacks {target} with its barbed tail"), + V("butts horns with another in greeting", "shares a red-hot snarl with nearby company"), + V("cackles in a cracked furnace roar", "barks a burst of scorching laughter"), + V("brands task material with clawed sigils", "heats task material with beating wings")); + + Add(voices, "dragon", + V("surges forward on vast beating wings", "pads ahead with its plated tail held clear"), + V("coils around its foreclaws and watches", "perches high on folded haunches"), + V("chases its own tail with thunderous wingbeats", "snaps twice at its own wingtip"), + V("rips a meal with serrated teeth", "swallows a meal in one gulp"), + V("curls beneath the shelter of one broad wing", "rests its horned head across crossed forelegs"), + V("hunts {target} through flared nostrils", "hunts {target} with unblinking eyes"), + V("attacks {target} with both hind talons", "attacks {target} with a narrow torrent"), + V("touches brow horns in greeting", "rumbles low while fanning one wing"), + V("laughs in rolling thunder from its chest", "chuffs bright sparks between rolling growls"), + V("moves weighty loads in closed talons", "fuses material with a measured breath")); + + Add(voices, "druid", + V("walks with a crooked staff marking each step", "paces lightly beneath a leaf-woven cloak"), + V("leans on the staff and listens", "kneels with both palms pressed down"), + V("braids the cloak's leaf tips around the staff", "coaxes the staff's living grain into curls"), + V("chews a meal in slow bites", "draws energy through measured mouthfuls"), + V("rests beneath the hood with hands in sleeves", "curls around the crooked staff"), + V("hunts {target} through the staff's sensing grain", "hunts {target} with searching root-magic"), + V("attacks {target} with thorny growth", "attacks {target} with grasping vines"), + V("bows the leaf-woven hood toward another", "greets nearby company with a rustling cloak"), + V("chuckles through a rustling hood", "laughs softly as the leaf-woven cloak flutters"), + V("binds task material with fresh fibers", "guides material into an orderly weave")); + + Add(voices, "evil_mage", + V("sweeps forward behind a trailing black robe", "glides with a crooked staff held before the face"), + V("stands inside a ring of hovering runes", "hunches over the staff while shadows gather close"), + V("makes its dark sparks chase one another", "shuffles glowing symbols between its hands"), + V("draws energy through clenched teeth", "draws energy through the crooked staff"), + V("reclines in a shell of folded shadow", "dozes upright while the staff keeps watch"), + V("hunts {target} through a circling violet eye", "hunts {target} with crawling symbols"), + V("attacks {target} with crackling violet force", "attacks {target} with the hooked staff"), + V("bows with the face hidden by the hood", "trades layered whispers over clasped sleeves"), + V("snickers behind one raised hand", "laughs in a dry, echoing rasp"), + V("inks task material with a smoking fingertip", "distills murky energy for a waiting task")); + + Add(voices, "fairy", + V("zips ahead on shimmering wings", "darts in a loop while its own glow pulses"), + V("hovers with toes tucked beneath a tiny skirt", "perches lightly with wings folded upright"), + V("blows a ring of its own sparkling motes", "loops through its own shimmer in a quick dance"), + V("sips a meal in tiny mouthfuls", "nibbles a meal held in both hands"), + V("folds bright wings around a curled body", "dozes while suspended in a soft glow"), + V("hunts {target} in widening flight circles", "hunts {target} with shimmering wingbeats"), + V("attacks {target} with a needle-thin flash", "attacks {target} with its own sparkling motes"), + V("touches wingtips with another in greeting", "trades spiraling ribbons of its own colored light"), + V("giggles in tiny bell-like bursts", "trills a laugh in quick pulses"), + V("ties task material into delicate loops", "polishes material with its own sparkling motes")); + + Add(voices, "fire_elemental", + V("strides as a column of folding flame", "streams forward in licking orange ribbons"), + V("gathers into a steady upright blaze", "burns low around a white-hot center"), + V("flicks sparks into spinning hoops", "splits off a flame that chases its hands"), + V("devours fuel in a sudden flare", "draws energy inward until its core whitens"), + V("banks its body into a bed of embers", "closes around its core in a dim red shell"), + V("hunts {target} by reaching with thin flames", "hunts {target} through trembling tongues"), + V("attacks {target} with a roaring arm of fire", "attacks {target} in a ring of searing force"), + V("mingles its sparks with another in greeting", "bows to nearby company beneath a bright crown"), + V("roars with the pop of burning fuel", "crackles in a rising cascade"), + V("heats material to a visible glow", "seals a join with one white-hot fingertip")); + + Add(voices, "fire_elemental_blob", + V("wobbles forward in molten hops", "oozes ahead while flames lick from its crown"), + V("puddles into a quivering orange mound", "holds still as bubbles roll beneath its skin"), + V("pops little fire bubbles from its surface", "splits into two wobbling lobes and rejoins"), + V("rolls fuel into its glowing body", "melts a meal beneath a drooping fold"), + V("flattens into a dim ember-shape", "forms a cooling crust around its liquid center"), + V("hunts {target} with a glowing pseudopod", "hunts {target} with a forked lick of flame"), + V("attacks {target} with a clinging heat-glob", "attacks {target} by expanding its fiery body"), + V("bumps warm sides with another", "waves a small flaming lobe toward nearby company"), + V("gurgles through a popping laugh", "burps a chain of bright crackles"), + V("fills task seams with molten material", "presses its soft heat around a task join")); + + Add(voices, "fire_elemental_horse", + V("gallops on hooves of compact flame", "canters with a blazing mane streaming backward"), + V("stands with one ember-bright hoof cocked", "lowers its head as the mane settles to embers"), + V("prances while its own hooves pulse brightly", "tosses its mane in a shower of its own sparks"), + V("crops fuel with glowing teeth", "draws energy through flared nostrils"), + V("folds its legs beneath a banked fiery body", "rests with its muzzle tucked against a warm flank"), + V("hunts {target} with smoke-lined nostrils", "hunts {target} through one sensitive forehoof"), + V("attacks {target} with burning hooves", "attacks {target} with a sweeping tail of flame"), + V("touches glowing muzzles with another", "nickers to nearby company as its mane flickers"), + V("whinnies in a rising furnace note", "snorts a laughing burst of sparks"), + V("hauls a load in a heat-hazed stride", "stamps material firm with controlled hot hooves")); + + Add(voices, "fire_elemental_slug", + V("glides on a ribbon of molten glow", "inches forward beneath two wavering flame feelers"), + V("draws into a low smoldering coil", "rests with both feelers dimmed to red points"), + V("waves its feelers through its own curling sparks", "blows bright bubbles from its glowing mantle"), + V("rasps a meal with its heated mouth", "absorbs energy through its broad underside"), + V("seals itself beneath a cooled black crust", "tucks its feelers into a banked orange body"), + V("hunts {target} with sweeping hot feelers", "hunts {target} with heat-sensitive feelers"), + V("attacks {target} with a strip of clinging flame", "attacks {target} with a hot spark"), + V("touches feelers with another", "draws a curling ember-sign toward nearby company"), + V("sizzles in wet, bubbly chuckles", "pops softly along its glowing mantle"), + V("lays a narrow bead across task material", "smooths task joins with its heated foot")); + + Add(voices, "fire_elemental_snake", + V("slithers in a swift incandescent coil", "whips forward as a narrow line of flame"), + V("loops around its glowing core", "raises its head above a banked spiral"), + V("chases a spark through its own coils", "ties its blazing body into a loose knot"), + V("swallows fuel that glows down its length", "draws energy through a flickering forked tongue"), + V("curls into a dim ring of embers", "hides its head beneath overlapping fiery coils"), + V("hunts {target} with a split flame tongue", "hunts {target} through sensitive burning coils"), + V("attacks {target} in a searing straight line", "attacks {target} within burning coils"), + V("braids one loop beside another in greeting", "flicks a paired spark toward nearby company"), + V("hisses with a bright crackling cadence", "rattles its tail in popping bursts"), + V("threads heat through task material", "coils around a task join until it fuses cleanly")); + + Add(voices, "fire_skull", + V("drifts forward beneath a ragged flame", "bobs ahead with jaws aglow"), + V("hovers with empty sockets fixed ahead", "settles low as its crown burns blue"), + V("spins until its own flame forms a ring", "chatters its jaw to scatter its own sparks"), + V("bites fuel and crushes it into flame", "inhales energy through its hollow nose"), + V("dims its crown and floats jaw-down", "closes its mouth around the last red glow"), + V("hunts {target} with hollow sockets fixed forward", "hunts {target} through snapping teeth"), + V("attacks {target} with blazing jaws", "attacks {target} with a compact fiery bolt"), + V("clacks teeth toward another in greeting", "bumps brows and shares a tongue of flame"), + V("cackles through a loose rattling jaw", "howls with fire roaring from its mouth"), + V("scorches task material with a measured bite", "carries a hot task load between its teeth")); + + Add(voices, "ghost", + V("drifts with a trailing translucent hem", "passes forward in a wavering glide"), + V("hangs still with hands folded through its chest", "fades to a faint outline while watching"), + V("loops through its own misty train", "ripples its translucent outline without touching it"), + V("draws energy through its hollow form", "absorbs energy from its own aura"), + V("thins into an almost invisible veil", "folds into a dim floating silhouette"), + V("hunts {target} with one spectral hand cupped", "hunts {target} through its wavering outline"), + V("attacks {target} with a chilling hand", "attacks {target} with a face-stretching wail"), + V("passes one hand through another in greeting", "bows toward nearby company as its outline brightens"), + V("warbles in an airy, echoing laugh", "moans upward into a trembling chuckle"), + V("lifts a task load with unseen pressure", "guides task material without contact")); + + Add(voices, "god_finger", + V("walks on its tip in deliberate little hops", "slides upright with the nail leading"), + V("balances rigidly on one rounded tip", "curls slightly and holds a hooked pose"), + V("draws circles with its nail", "taps out an alternating five-beat rhythm"), + V("draws energy through its rounded tip", "absorbs energy through the nail"), + V("lies flat with the nail dimmed", "bends into a relaxed hook and goes still"), + V("hunts {target} with its nail pointed sharply", "hunts {target} through a sensitive fingertip"), + V("attacks {target} with a sudden flick", "attacks {target} with a gleaming nail"), + V("crooks itself toward another in greeting", "taps another tip in quick acknowledgment"), + V("drums a rolling laugh against itself", "wiggles through a silent pantomime of laughter"), + V("nudges task material into exact position", "presses firmly to set finished work")); + + Add(voices, "greg", + V("lopes ahead with elbows flung wide", "shuffles sideways in a lopsided gait"), + V("squats on its heels and stares without blinking", "stands slack-jawed with hands dangling"), + V("makes faces at its own waggling fingers", "hops twice and freezes in a crooked pose"), + V("crams a meal between its teeth", "licks every fingertip after a mouthful"), + V("sprawls flat with limbs at mismatched angles", "curls around its knees and snores through its nose"), + V("hunts {target} with abrupt zigzag sniffs", "hunts {target} by peering under its own arm"), + V("attacks {target} with windmilling fists", "attacks {target} with a lunging headbutt"), + V("waves with both hands far too close", "greets by copying every gesture a beat late"), + V("honks out a belly laugh", "wheezes until its shoulders bounce"), + V("stacks task material in uneven columns", "hammers task material in an irregular rhythm")); + + Add(voices, "jumpy_skull", + V("boings forward on a springing jaw", "hops in quick arcs with teeth clenched"), + V("balances on its lower teeth and quivers", "sits jaw-first with sockets darting about"), + V("bounces in place to a clacking beat", "snaps at its own rebounding jaw"), + V("catches a meal between chattering teeth", "grinds a meal between its teeth"), + V("rests upside down on the crown of its cranium", "wedges its jaw shut and stops bouncing"), + V("hunts {target} in tight hopping circles", "hunts {target} with one tilted socket"), + V("attacks {target} in a teeth-first arc", "attacks {target} with a ricocheting cranium"), + V("bumps craniums with another", "chatters a greeting toward nearby company"), + V("cackles in rapid jaw-clacking bursts", "rattles with a hollow, breathless guffaw"), + V("pounds task material with its crown", "carries a task load clenched between its teeth")); + + Add(voices, "living_house", + V("shuffles forward on creaking foundation stones", "lurches ahead as doors and shutters flap"), + V("settles squarely and braces every corner", "stands with chimney straight and windows fixed ahead"), + V("opens and closes its shutters in rhythm", "rocks its roofline in a slow, creaking dance"), + V("draws fuel through its front door", "feeds a meal into its grinding cellar"), + V("locks its door and dims every window", "sags gently beneath a resting roof"), + V("hunts {target} through swiveling windows", "hunts {target} through a listening chimney"), + V("attacks {target} with a heavy door", "attacks {target} behind a jutting porch"), + V("waves both shutters toward another", "rings an inner bell for nearby company"), + V("rattles every window in a booming laugh", "creaks from cellar to rafters in a long chuckle"), + V("repairs task material with an inner beam", "stacks material with measured foundation steps")); + + Add(voices, "living_plants", + V("walks on a braid of probing roots", "pulls itself forward with curling vines"), + V("anchors its roots and lifts every leaf", "folds broad fronds around a knotted stem"), + V("twirls seed pods on thin tendrils", "claps two broad leaves in a crisp rhythm"), + V("draws energy through fine root hairs", "unfurls leaves while absorbing energy"), + V("closes every leaf around its central bud", "sinks into a rooted, slow-pulsing rest"), + V("hunts {target} with pollen-sensitive tendrils", "hunts {target} through wandering roots"), + V("attacks {target} with thorned vines", "attacks {target} behind layered leaves"), + V("entwines tendrils in a slow greeting", "opens a bright bloom toward nearby company"), + V("rustles in a quick leafy chuckle", "shakes dry pods in a rattling laugh"), + V("weaves task material with flexible stems", "binds material with tightening roots")); + + Add(voices, "mush_animal", + V("scampers on stubby feet beneath a broad cap", "bounds forward as soft gills puff with each landing"), + V("sits back on its haunches and lowers its cap", "crouches with spongy ears tucked close"), + V("pounces on its own drifting spores", "rolls onto its back and pedals short feet"), + V("nibbles a meal with flat little teeth", "laps a meal with its tongue"), + V("curls beneath its own lowered cap", "nestles into a squat ball with gills closed"), + V("hunts {target} with a porous snout", "hunts {target} through trembling gills"), + V("attacks {target} with its broad cap", "attacks {target} beneath a blinding spore puff"), + V("rubs caps with another in greeting", "sniffs another and chirps through its gills"), + V("squeaks in damp, bubbly bursts", "chuffs until its broad cap wobbles"), + V("packs task material with quick forepaws", "carries a task load balanced on its cap")); + + Add(voices, "mush_unit", + V("marches on pale stalk-like legs", "strides with a broad cap tilted forward"), + V("stands with arms folded beneath hanging gills", "tilts its broad cap as the gills pulse"), + V("tosses a spore puff from palm to palm", "tips the broad cap through an exaggerated dance"), + V("chews a meal with a grinding mouth", "drinks a meal in slow mouthfuls"), + V("squats until the cap covers the whole body", "rests upright as the gills pulse slowly"), + V("hunts {target} with a veil of sensing spores", "hunts {target} with tapping fingertips"), + V("attacks {target} with a hardened forearm", "attacks {target} beneath a dense spore cloud"), + V("tips the cap in measured greeting", "presses palms and exchanges a faint spore plume"), + V("chuckles in low, papery puffs", "laughs until spores shake from the gills"), + V("cultivates task material into sturdy panels", "presses material into useful molded shapes")); + + Add(voices, "necromancer", + V("paces with a bone-tipped staff clicking beside each step", "glides as its own magic stirs the robe"), + V("bends over a circle of pale runes", "stands rigid while the staff whispers back"), + V("makes finger bones dance across one palm", "conducts a tiny procession of rattling fragments"), + V("draws energy through one raised hand", "draws wan energy through the bone-tipped staff"), + V("sleeps upright inside a ring of orbiting bones", "rests the brow against the staff as pale lights orbit"), + V("hunts {target} through a floating jaw's whispers", "hunts {target} with a cold spectral wisp"), + V("attacks {target} with conjured grasping hands", "attacks {target} with grave-pale force"), + V("greets another with a tap of the staff", "trades layered whispers with nearby company"), + V("laughs in a papery, hollow rasp", "chuckles while its orbiting bones knock together"), + V("threads task material into jointed frames", "inscribes pale commands along material")); + + Add(voices, "plague_doctor", + V("hurries with coat tails snapping behind", "steps beneath a long beaked mask"), + V("stands with gloved hands clasped at the chest", "tilts the mask while its beak filters sound"), + V("drums gloved fingers along one sleeve", "balances the long beak above folded gloves"), + V("takes measured bites behind the mask", "draws energy through the narrow beak"), + V("reclines without removing mask or gloves", "dozes seated with both gloves folded"), + V("hunts {target} through the mask's round lenses", "hunts {target} by turning the long beak"), + V("attacks {target} with both gloved hands", "attacks {target} with a thrust of the long beak"), + V("nods toward another and raises one glove", "bows the long beaked mask toward nearby company"), + V("chuckles dryly behind the hollow beak", "lets out a muffled laugh through layered cloth"), + V("measures task material into labeled portions", "sterilizes task material with a controlled flame")); + + Add(voices, "printer", + V("trundles forward on humming rollers", "slides along while its paper tray rattles"), + V("idles with a steady green indicator", "pauses as internal gears tick into alignment"), + V("feeds out a strip covered in dancing marks", "whirs through an alternating self-test pattern"), + V("draws ink from a sealed cartridge", "pulls a blank sheet through its turning rollers"), + V("powers down with its tray tucked closed", "dims its display while the print head parks"), + V("hunts {target} with a traveling scan bar", "hunts {target} through its narrow glass slit"), + V("attacks {target} with a rapid sheet volley", "attacks {target} with its snapping tray"), + V("beeps twice and flashes its status light", "exchanges neatly printed greeting slips"), + V("chatters its gears in a mechanical laugh", "chirps a laugh through sequenced tones"), + V("copies precise symbols onto task material", "collates task material into squared stacks")); + + Add(voices, "skeleton", + V("clatters forward on bare jointed feet", "strides with loose ribs clicking together"), + V("stands with both hands hooked into its rib cage", "sits in a neatly folded pile of bones"), + V("juggles three knuckles through its ribs", "removes its jaw and makes it chatter by hand"), + V("gnaws a meal with bare teeth", "pours energy through its empty mouth"), + V("stacks its bones into a tidy dormant heap", "leans against its own shin and goes slack"), + V("hunts {target} with empty sockets lowered", "hunts {target} with a detached ear bone"), + V("attacks {target} with a sharpened forearm", "attacks {target} by separating and reassembling"), + V("rattles finger bones toward another", "clicks jaws with nearby company"), + V("cackles with its jaw flapping wide", "shakes from skull to toes in hollow laughter"), + V("sorts task material by length", "fastens material with strips pulled through its ribs")); + + Add(voices, "tumor_monster_animal", + V("lopes on uneven limbs beneath pulsing growths", "scrambles forward with one swollen shoulder leading"), + V("huddles low as nodules throb along its back", "rests its heavy head on a cluster of folded limbs"), + V("chases a twitching tendril around its flank", "paws at bubbles moving beneath its hide"), + V("tears a meal with crowded teeth", "draws energy through several puckered mouths"), + V("curls around its largest pulsing mass", "slumps into a pulsing heap of slow contractions"), + V("hunts {target} through a ring of wet nostrils", "hunts {target} with sensitive nodules"), + V("attacks {target} behind a hardened growth", "attacks {target} with several mouths"), + V("nuzzles another with a broad sensory lobe", "trades low pulses through touching growths"), + V("gurgles from several throats at once", "wheezes in a wet, hiccupping laugh"), + V("packs task material with fleshy fibers", "drags material using a hooked extra limb")); + + Add(voices, "tumor_monster_unit", + V("lumbers upright on mismatched legs", "pulls a swollen frame forward with an oversized arm"), + V("hunches while clustered eyes blink in sequence", "braces both hands against a pulsing abdomen"), + V("makes two smaller hands wrestle across its chest", "drums a rhythm on hardened nodules"), + V("chews a meal through a sideways mouth", "feeds energy into a pulsing growth"), + V("folds extra limbs around its swollen torso", "rests with every eye closing one after another"), + V("hunts {target} with clustered eyes", "hunts {target} using a thin tasting tendril"), + V("attacks {target} with its oversized fist", "attacks {target} between hardened lobes"), + V("raises a small palm toward another", "touches sensory tendrils in a pulsing greeting"), + V("laughs in overlapping throaty pitches", "snorts through several vents in uneven rhythm"), + V("molds dense tissue around task material", "uses three hands to bind material under tension")); + + Add(voices, "white_mage", + V("walks beneath a robe edged in steady light", "glides with a smooth staff held upright"), + V("stands inside a soft ring of ordered sigils", "rests both hands atop a pearl-bright staff"), + V("folds little stars from threads of light", "makes small sparks orbit the staff"), + V("draws energy through an open palm", "draws energy through the staff"), + V("sleeps within a dim shell of pale light", "bows the hood and lets the staff stand guard"), + V("hunts {target} with a searching luminous mote", "hunts {target} through the staff's crystal"), + V("attacks {target} with a shining barrier", "attacks {target} with cleansing light"), + V("offers a pearl glow toward another", "bows as two pale halos briefly overlap"), + V("laughs in a clear, ringing cadence", "chuckles as bright motes bob around the hood"), + V("mends task material with woven light", "purifies material in a pearl-white glow")); + + Add(voices, "zombie", + V("shambles forward with one foot dragging", "lurches ahead on stiff, reaching arms"), + V("sways in place with its jaw hanging loose", "slumps forward until a sudden twitch straightens it"), + V("reaches repeatedly for its dangling sleeve", "rocks side to side in an uneven imitation of dancing"), + V("bites into a meal with broken teeth", "gnaws a meal without closing its lips"), + V("collapses in a limp heap", "dozes standing with its forehead pressed down"), + V("hunts {target} through a ruined nose", "hunts {target} with an unbroken forward stare"), + V("attacks {target} with both stiff hands", "attacks {target} with broken teeth"), + V("groans face to face with another", "pats another with a cold, stiff hand"), + V("gurgles through a crooked jaw", "barks a hoarse chuckle"), + V("hauls a load with stiff arms", "presses task material with its full weight")); + + Add(voices, "snowman", + V("waddles on a rolling rounded base", "pivots forward as its packed sections sway"), + V("balances its stacked form without swaying", "holds stick-like limbs beside its rounded body"), + V("twirls both stick-like limbs in broad circles", "rotates its rounded sections in opposite directions"), + V("absorbs energy through its packed body", "compacts a meal into its rounded middle"), + V("settles each packed section into a dormant stack", "draws its stick-like limbs against its cold body"), + V("hunts {target} by rotating its rounded upper body", "hunts {target} through pulsing cold-body effects"), + V("attacks {target} with sweeping stick-like limbs", "attacks {target} with a burst of its own packed snow"), + V("waves one stick-like limb toward another", "tips its rounded upper body toward nearby company"), + V("shakes its stacked sections in a powdery chuckle", "crunches through a soft, body-rumbling laugh"), + V("compacts task material with its rounded body", "pushes a load with both stick-like limbs")); + } +} diff --git a/IdleSpectator/ActivitySpeciesVoices.Invertebrates.cs b/IdleSpectator/ActivitySpeciesVoices.Invertebrates.cs new file mode 100644 index 0000000..e01bbab --- /dev/null +++ b/IdleSpectator/ActivitySpeciesVoices.Invertebrates.cs @@ -0,0 +1,261 @@ +using System.Collections.Generic; + +namespace IdleSpectator; + +public static partial class ActivitySpeciesVoiceCatalog +{ + private static void AddInvertebrateVoices(Dictionary voices) + { + Add( + voices, + "acid_blob", + V("rolls its leading lobe forward", "pours its mass into another low glide"), + V("holds its rim in a shallow quiver", "settles into a wide motionless pool"), + V("flicks small beads from its rippling edge", "folds two rising lobes over each other"), + V("spreads a thin feeding film across its meal", "draws morsels inward beneath its surface"), + V("flattens until only its skin trembles", "gathers into a low mound and stills"), + V("extends a narrow feeler toward {target}", "streams forward after {target}"), + V("lashes out at {target} with a sudden liquid fold", "rears its front edge at {target} and slaps it down"), + V("touches rims and trades slow surface pulses", "presses close and mirrors another's ripples"), + V("burbles as rings race across its surface", "pops a chain of little bubbles"), + V("pushes a broad lobe steadily at the task", "divides its mass around the task")); + + Add( + voices, + "ant_black", + V("keeps a straight course on quick jointed steps", "scurries forward with antennae sweeping"), + V("halts with both feelers angled ahead", "sets all six feet and samples with its antennae"), + V("darts aside and circles back", "crosses and uncrosses its paired forelegs"), + V("clips bites from its meal", "braces its forelegs and rasps at a morsel"), + V("tucks its legs beneath a compact crouch", "lowers its feelers and becomes still"), + V("advances toward {target} with rapid antenna taps", "follows after {target} in tight turns"), + V("locks mandibles on {target} and drives from its hind legs", "ducks low at {target} before snapping its jaws upward"), + V("meets another with brisk feeler strokes", "passes close to another and exchanges antenna taps"), + V("rattles its jaws in a clipped little rhythm", "shivers its feelers through a dry chuckle"), + V("hauls a load with its body pitched forward", "sorts material into a straight row")); + + Add( + voices, + "ant_blue", + V("advances in measured steps with wide feeler sweeps", "moves forward in alternating angular turns"), + V("balances high on straightened legs", "pauses to trace a slow arc with each feeler"), + V("pivots in tight loops on alternating feet", "rears and catches its forefeet in a paired clasp"), + V("rotates a morsel between paired mouthparts", "pares bites from its meal"), + V("folds into a narrow resting stance", "rests with its antennae laid along its head"), + V("triangulates toward {target} with alternating feeler taps", "takes an angular zigzag toward {target}"), + V("sidesteps at {target} and pinches from the flank", "sets its feet wide at {target} and closes both jaws"), + V("draws paired symbols with crossing antennae", "paces beside another in matching steps"), + V("ticks its mouthparts in a rising cadence", "taps its forefeet in a quick alternating pattern"), + V("taps each load with both feelers before shifting it", "fits material into a straight line")); + + Add( + voices, + "ant_green", + V("bounds forward with its forelegs held overhead", "threads ahead beneath a balanced abdomen"), + V("rests its forelegs across raised mandibles", "stops to groom its feelers with hooked wrists"), + V("spins beneath raised forelegs", "vaults forward and doubles back"), + V("saws at a morsel with its mandibles", "holds a morsel aloft while shaving off bites"), + V("curls around its tucked forelegs", "settles with its legs clasped close to its body"), + V("tests toward {target} with one lifted foreleg", "rushes after {target} with its jaws held open"), + V("swings its raised forelegs at {target}", "hooks at {target} and pulls sideways"), + V("opens its jaws while facing another", "links feelers with another and turns in a brief circle"), + V("scrapes its jaws together in bouncing beats", "drums an alternating pattern with its middle feet"), + V("cuts material into narrow sections", "carries sections of a load one by one")); + + Add( + voices, + "ant_red", + V("charges in short bursts with jaws spread", "races forward and brakes on splayed forelegs"), + V("rocks over its braced feet with feelers rigid", "holds a low stance with mandibles parted"), + V("pounces forward and kicks backward", "rushes in tight circles with its abdomen raised"), + V("tears brisk bites from its meal", "pins its meal and bites in rapid strokes"), + V("drops into a tight crouch without relaxing its jaws", "folds its limbs close and rests low"), + V("surges after {target} in rapid bursts", "cuts toward {target} with jaws spread"), + V("lunges at {target} with mandibles wide", "clamps on {target} and twists its whole body"), + V("bumps heads and trades forceful antenna strokes", "marches shoulder to shoulder with another"), + V("clacks its jaws in a rapid rolling burst", "stamps all six feet in a crackling rhythm"), + V("drives a load forward from behind", "grips the task and pulls without pause")); + + Add( + voices, + "bee", + V("zips ahead on rapidly beating wings", "bobs forward in a humming flight"), + V("hovers in place with dangling legs", "lands and combs its wings with its hind legs"), + V("loops upward and spirals back down", "dances in tight figures with a wagging abdomen"), + V("unfurls its tongue and draws from a meal", "packs pollen against its hind legs"), + V("folds its wings flat and tucks in its legs", "rests with its head lowered between its forelegs"), + V("angles its feelers toward {target}", "accelerates after {target} with a deepening buzz"), + V("dives at {target} with its abdomen curled beneath it", "buffets at {target} with wings and kicking legs"), + V("touches mouthparts with another in a brief exchange", "faces another while waggling its abdomen"), + V("buzzes in a tumbling uneven trill", "shakes its abdomen through an uneven hum"), + V("brushes pollen into compact bundles", "ferries a gathered load in steady flights")); + + Add( + voices, + "beetle", + V("trundles forward beneath closed wing cases", "clambers ahead as its hooked feet lift in sequence"), + V("draws in its legs and lowers its plated back", "stands braced while its feelers make short sweeps"), + V("rolls onto its back and pumps all six legs", "tips onto its side and rocks upright again"), + V("anchors its feet and chews its meal", "works its jaws steadily across a morsel"), + V("seals its wings and settles behind folded legs", "wedges its head low and becomes still"), + V("follows after {target} with its feelers forward", "lumbers toward {target} in a straight line"), + V("rams at {target} with its lowered head", "opens its wing cases at {target} and shoves with all six legs"), + V("knocks feelers against another's plated brow", "circles another and taps shell against shell"), + V("clicks its wing cases in hollow bursts", "rasps its feet against its plated sides"), + V("leans its hardened back into a load", "grips and drags material with hooked forefeet")); + + Add( + voices, + "bioblob", + V("inches forward as its front edge swells", "heaves its rounded membrane into a springy roll"), + V("balances as a domed pulse rises and falls", "rests while slow ripples cross its skin"), + V("bounces twice and catches itself in a wide wobble", "pinches off a bead and reels it back inside"), + V("wraps a membrane pocket around each morsel", "draws nutrients inward with a deep contracting pulse"), + V("dims its inner motion and sags into a low mound", "draws its membrane close around a still center"), + V("points a pulsing bulge toward {target}", "bounds after {target} with quick whole-body squeezes"), + V("hardens its front at {target} into a blunt swell", "compresses and springs at {target} as one mass"), + V("joins surfaces and trades alternating pulses", "echoes another's pulse from rim to rim"), + V("warbles through a chain of membrane pops", "jiggles until ripples cross its membrane in rings"), + V("forms gripping pockets around the task", "moves material with alternating squeezes")); + + Add( + voices, + "butterfly", + V("floats ahead on broad alternating wingbeats", "loops forward with legs tucked close"), + V("holds its wings upright and still", "opens and closes its wings in slow measured fans"), + V("spirals upward and glides back down", "dips and rises on alternating wingbeats"), + V("uncurls its feeding tube and takes from a meal", "steadies on four legs while drawing nutrients"), + V("folds its wings tight and draws in its legs", "rests behind the narrow line of closed wings"), + V("follows after {target} in rising uneven loops", "glides toward {target} with quick wingbeats"), + V("beats its wings hard at {target}", "veers at {target} and kicks with slender legs"), + V("circles another in paired rising arcs", "rests alongside another with wing edges nearly touching"), + V("flutters in a loose cascade of wingbeats", "bobs through an uneven airy chuckle"), + V("carries a load on its legs", "makes repeated passes over the task")); + + Add( + voices, + "flower_bud", + V("hitches its rootlets forward beneath a bending stalk", "leans and draws its stalk after its roots"), + V("holds its closed petals above a steady stalk", "anchors its rootlets and lets its leaves settle"), + V("twirls its stalk until the outer petals fan wide", "bobs its closed crown in quick springing arcs"), + V("presses root hairs outward and draws nutrients in", "cups a morsel beneath its lowest leaves"), + V("folds every petal inward around its center", "bows its crown and slackens its leaves"), + V("angles its crown toward {target}", "pulls its rootlets toward {target} in a tightening curve"), + V("whips its stalk at {target} with its crown closed", "braces its roots at {target} and sweeps its leaves outward"), + V("touches leaf tips and trades a tremor with another", "inclines its crown beside another's petals"), + V("shakes its petals in a crisp papery rattle", "nods through a cascade of rustling leaves"), + V("winds fine roots around the task", "uses paired leaves to lift and place material")); + + Add( + voices, + "fly", + V("shoots forward and stops in midair", "skims ahead on blurred, glassy wings"), + V("hovers with its forelegs rubbing together", "holds still and wipes each eye with a bent foreleg"), + V("darts in a square and retraces every turn", "loops upside down before righting in a snap"), + V("sets its feet and sponges at a meal", "lowers its mouthparts for quick repeated tastes"), + V("draws its legs close with wings laid flat", "tucks its legs close and stills its wing buzz"), + V("closes toward {target} by sharp angles", "tracks after {target} with abrupt hovering stops"), + V("slams sideways at {target} with a burst of wing speed", "grapples with {target} while its wings whine"), + V("faces another and drums with its forefeet", "orbits another in close mirrored darts"), + V("buzzes in a broken, hiccupping run", "rubs its wings into a thin trill"), + V("shuttles material in rapid trips", "uses its forefeet to turn the task bit by bit")); + + Add( + voices, + "garl", + V("lurches ahead as a layered bulb rocks over its roots", "sets fibrous roots, then hauls its stalk forward"), + V("settles its layered base and holds its stalk upright", "fans its narrow leaves above a rooted pause"), + V("spins on its bulb while loose skins flutter", "whips its leaf tips in alternating circles"), + V("threads fine roots around a meal and draws inward", "parts its papery layers to receive morsels"), + V("closes its leaves around a bowed stalk", "rests its bulb on one side with roots curled beneath"), + V("tilts its stalk toward {target}", "scrambles after {target} on bunching root fibers"), + V("swings its dense bulb at {target} in a low arc", "lashes at {target} with stiff leaves"), + V("braids leaf tips briefly with another's", "presses layered sides together and rustles"), + V("shudders until its dry skins chatter", "flicks its leaves in an alternating crackle"), + V("hooks the task with bundled root fibers", "wedges its layered base behind a load")); + + Add( + voices, + "grasshopper", + V("vaults ahead on folded hind legs", "springs forward and lands on braced forefeet"), + V("crouches with its hind legs folded beneath its abdomen", "holds still while its feelers trace broad arcs"), + V("bounds straight up and twists before landing", "kicks both hind legs and leaps after them"), + V("clips bites with side-working jaws", "grips a morsel between its forefeet and chews"), + V("folds its long hind legs tight against its sides", "rests low with feelers laid back"), + V("launches toward {target} in successive long bounds", "leans toward {target} and springs after {target}"), + V("drives both hind feet at {target} in a sharp kick", "leaps at {target} and drops forefeet-first"), + V("crosses feelers with another in alternating taps", "sits beside another and rubs a leg along one wing"), + V("scrapes out a skipping chirr", "pulses its hind legs through an alternating rattle"), + V("kicks loose material into a gathered load", "carries material between its forefeet in short hops")); + + Add( + voices, + "lemon_snail", + V("glides forward on a rippling muscular foot", "extends its eyestalks and slides beneath its shell"), + V("rests with its eyestalks half withdrawn", "holds its shell level while the foot stops rippling"), + V("waves both long feelers in crossing loops", "rocks its shell from side to side without advancing"), + V("rasps small strokes across the meal", "pins a morsel beneath the front of its foot"), + V("withdraws beneath the lip of its shell", "seals close with every feeler tucked away"), + V("stretches its eyestalks toward {target}", "glides after {target} with its head extended"), + V("swings its shell sideways at {target}", "rears its head and drives at {target} under its shell"), + V("touches feeler tips and circles shell to shell", "glides alongside another in a close parallel line"), + V("bobs its eyestalks in a slow alternating rhythm", "rocks its shell through a scraping chuckle"), + V("pushes material with the broad front of its foot", "anchors its shell and rasps steadily at the task")); + + Add( + voices, + "lil_pumpkin", + V("rolls its ribbed body as a short vine pulls ahead", "scampers on curling tendrils beneath its bobbing crown"), + V("sets its round body down among coiled tendrils", "holds its crown upright while the vine tips rest"), + V("bounces on its rounded base and spins a tendril", "catches one curling vine between two others"), + V("draws a morsel beneath its vine and presses close", "uses a split tendril to guide a morsel inward"), + V("curls every tendril against its ribbed sides", "tips onto one flank and lets its crown droop"), + V("uncurls an extended vine toward {target}", "rolls after {target} with tendrils reaching ahead"), + V("butts its hard rounded body at {target}", "snaps a coiled tendril at {target}"), + V("loops tendrils briefly around another's vine", "rests side by side and taps crowns with another"), + V("rattles its hollow body from within", "bounces as its hollow body knocks"), + V("winds tendrils around the task and pulls", "braces its round body behind a shifting load")); + + Add( + voices, + "sand_spider", + V("skates forward on eight low-splayed legs", "scuttles sideways with its body held low"), + V("freezes with each leg bent at a different angle", "settles low while its front legs test faint tremors"), + V("juggles its forefeet in alternating pairs", "spins once on four braced feet"), + V("pins its meal beneath folded forelegs", "works its mouthparts while holding the morsel still"), + V("draws all eight legs into a tight resting knot", "flattens its body and folds its forelegs inward"), + V("feels toward {target} through widely spread feet", "stalks toward {target} in alternating sideways steps"), + V("rears at {target} and strikes with the front pair", "hooks at {target} and pivots sideways"), + V("taps front feet in a measured exchange", "faces another and mirrors each lifted leg"), + V("drums eight feet in a tumbling cadence", "shakes its mouthparts through a dry clicking fit"), + V("draws fine strands around the task", "pulls bound material backward beneath its body")); + + Add( + voices, + "scorpion", + V("paces forward with pincers raised and tail balanced", "sidesteps ahead on eight compact legs"), + V("rests low beneath the arch of its tail", "holds both pincers open without a tremor"), + V("passes one pincer beneath the other", "circles its own tail tip in tight steps"), + V("shears a morsel apart between opposing pincers", "holds its meal in one claw and cuts with the other"), + V("lowers its tail and folds its claws beneath its head", "settles with every leg tucked under its plated body"), + V("tests toward {target} with open pincers", "tracks after {target} with its tail poised overhead"), + V("snaps both claws at {target} before driving its tail downward", "snaps one pincer at {target} and counters with the other"), + V("clasps pincers and steps through a paired turn", "touches claw tips in measured alternating taps"), + V("clacks its pincers in a brisk hollow rhythm", "rattles its plated tail in uneven pulses"), + V("grips the task between both claws", "drags material backward with its tail held clear")); + + Add( + voices, + "smore", + V("shuffles forward on a bundle of springy stalks", "tilts its stacked crown and steps on bending shoots"), + V("rests its layered crown above folded shoots", "sets its fibrous base and lets each stalk straighten"), + V("bounces its stacked layers in alternating tilts", "twirls a ribbonlike leaf around its crown"), + V("presses one layer around each morsel", "draws nutrients between two yielding layers"), + V("compresses its crown and folds its shoots inward", "settles into a squat stack with leaves tucked close"), + V("leans its upper layer toward {target}", "hurries after {target} on flexing stalks"), + V("springs its stacked body at {target}", "claps two broad layers at {target}"), + V("rests one broad layer against another's crown", "crosses ribbonlike leaves with another"), + V("wobbles until its stacked layers flap together", "rustles its leaves through a bouncing chuckle"), + V("pinches the task between broad yielding layers", "uses bundled stalks to shove material into place")); + } +} diff --git a/IdleSpectator/ActivitySpeciesVoices.Mammals.cs b/IdleSpectator/ActivitySpeciesVoices.Mammals.cs new file mode 100644 index 0000000..a04474f --- /dev/null +++ b/IdleSpectator/ActivitySpeciesVoices.Mammals.cs @@ -0,0 +1,303 @@ +using System.Collections.Generic; + +namespace IdleSpectator; + +public static partial class ActivitySpeciesVoiceCatalog +{ + private static void AddMammalVoices(Dictionary voices) + { + Add( + voices, + "alpaca", + V("paces on padded feet with its long neck held upright", "steps lightly while its fleece sways"), + V("stands chewing with its ears turning", "holds still behind a curtain of fleece"), + V("bounds sideways on stiff legs", "nudges and skips in a woolly burst"), + V("clips a bite with split lips", "chews steadily with a rolling jaw"), + V("folds its long legs beneath a fleece-covered body", "rests with its neck curved over tucked legs"), + V("raises its head near {target} and scans with upright ears", "advances toward {target} with its neck stretched high"), + V("lashes out near {target} with a sharp forward kick", "braces its padded feet before {target} and spits"), + V("touches noses beneath a pair of upright ears", "hums softly beside nearby company"), + V("lets out a bright, breathy hum", "burbles while chewing cud"), + V("carries the load on a straight, fleece-lined back", "kneels on padded joints beside the task")); + + Add( + voices, + "armadillo", + V("scuttles low beneath overlapping armor bands", "trots with claws ticking under a plated back"), + V("pauses with its pointed snout raised", "sits low while its armored bands settle"), + V("scrabbles in a quick circle with oversized claws", "pops upright and paddles its foreclaws"), + V("roots with a narrow snout and digs out each bite", "laps up morsels between rapid sniffs"), + V("curls its plated body around its soft underside", "dozes with nose and feet tucked under armor"), + V("samples scents near {target} with claws flexed", "searches around {target} with rapid sniffs"), + V("hunkers near {target} behind its shell and drives forward", "swipes at {target} with powerful digging claws"), + V("sniffs along another armored flank", "touches pointed snouts in a brief greeting"), + V("chatters in a dry, rapid burst", "squeaks from beneath its plated brow"), + V("excavates with alternating strokes of broad foreclaws", "pushes loose material aside with its armored shoulder")); + + Add( + voices, + "bandit", + V("slips ahead on quiet, measured steps", "keeps low with one hand held close"), + V("leans back and watches through narrowed eyes", "checks each side with short head turns"), + V("rolls one wrist and flexes quick fingers", "practices a feint and a smooth retreat"), + V("takes bites without lowering the guard", "eats quickly with one hand shielding the meal"), + V("sleeps curled tightly with both hands tucked in", "rests lightly with chin lowered to the chest"), + V("tracks movement near {target} from a crouch", "advances toward {target} with quick hands raised"), + V("slashes toward {target} from a low stance and withdraws", "turns aside near {target} and counters with the off hand"), + V("trades a brief hand sign at close range", "leans in to exchange a few short words"), + V("gives a muffled chuckle through closed lips", "snickers and taps two fingers against the chin"), + V("sorts material with quick fingertips", "checks the task with rapid hand movements")); + + Add( + voices, + "bear", + V("lumbers forward with heavy shoulders rolling", "advances on broad plantigrade paws"), + V("rises upright and works its broad nose", "sits back on its haunches with forepaws hanging"), + V("clasps both forepaws and rolls onto one shoulder", "rolls onto its back and bats with broad paws"), + V("tears off a bite with strong jaws", "scoops the meal inward with long curved claws"), + V("curls into a dense mound with paws over its muzzle", "settles heavily on its side and slows its breathing"), + V("follows the scent near {target} with muzzle sweeping side to side", "charges toward {target} with nose low"), + V("rears near {target} and brings both forepaws down", "drives at {target} behind a roar and a heavy swipe"), + V("rubs muzzle and shoulder against another broad flank", "sniffs another face with small rounded ears forward"), + V("huffs out a deep, rolling chortle", "grumbles in a sequence of breathy pulses"), + V("hauls with both forepaws and a braced back", "shoves the task onward with massive shoulders")); + + Add( + voices, + "buffalo", + V("moves in a heavy rolling stride beneath a horned brow", "trots with its shoulder hump rising and falling"), + V("stands broadside while its tail flicks", "holds its head low and works its jaw"), + V("bucks and twists with all four hooves briefly clear", "bounds forward and tosses its curved horns"), + V("tears off a bite with a wide muzzle", "chews steadily while its beard brushes its chest"), + V("folds its legs beneath a massive chest", "rests flank-down with its horned head upright"), + V("works its broad nostrils near {target} and follows", "pushes toward {target} with muzzle low and horns level"), + V("drops its horned head near {target} and drives forward", "swings its heavy skull at {target} in a short arc"), + V("presses shoulder to shoulder with nearby company", "grooms a nearby hide with slow tongue strokes"), + V("bellows in a deep, chesty rumble", "snorts and answers with a booming grunt"), + V("leans into the load with neck and shoulder hump", "drags steadily while broad hooves brace")); + + Add( + voices, + "capybara", + V("pads along on short legs with webbed toes splayed", "ambles forward with a blunt muzzle leading"), + V("sits squarely with half-lidded eyes and rounded ears", "rests its barrel-shaped body on folded feet"), + V("bobs up and scampers in a compact circle", "hops back on short legs and bobs again"), + V("nibbles the meal quickly with broad orange incisors", "chews a bite along one side of the jaw"), + V("lies flat with its blunt chin between its forefeet", "dozes in a compact heap with ears still twitching"), + V("sniffs near {target} in short deliberate bursts", "moves toward {target} with whiskers spread"), + V("bares broad incisors near {target} and lunges low", "braces before {target} and snaps sharply"), + V("touches blunt muzzles and settles beside nearby company", "grooms a nearby coat with careful front teeth"), + V("purrs in a bubbling series of soft clicks", "whistles once and follows with a throaty chuckle"), + V("grips and shifts material with incisors and forefeet", "pushes the task ahead with a broad chest")); + + Add( + voices, + "cat", + V("pads forward on silent, tucked claws", "threads ahead with tail held in a supple curve"), + V("sits upright and wraps its tail around neat paws", "freezes with whiskers spread and ears pivoting"), + V("pounces onto its forepaws and bats from side to side", "springs sideways with back arched and tail high"), + V("pins a morsel with one paw and tears it with its teeth", "licks its lips before taking another bite"), + V("curls nose to tail with paws hidden beneath", "kneads twice before folding into a compact curl"), + V("stalks toward {target} with scapulae rising under the coat", "waits near {target} before a sudden pounce"), + V("lashes at {target} with unsheathed foreclaws", "flattens its ears near {target} and strikes rapidly"), + V("greets with an upright tail and a cheek rub", "grooms a nearby coat with rough, measured licks"), + V("chatters out a bright trill", "purrs around a short chirrup"), + V("hooks the task closer with a careful paw", "inspects the work with whiskers forward and tail twitching")); + + Add( + voices, + "cow", + V("walks with an unhurried sway on cloven hooves", "steps forward while its broad muzzle bobs"), + V("stands ruminating with its tail switching", "rests its weight on three legs and chews"), + V("kicks up its hind heels in a heavy skip", "dips its broad forehead and skips sideways"), + V("draws in a bite with its rough tongue", "chews cud in slow circular strokes"), + V("folds its legs and settles on a broad belly", "lies on its side with muzzle resting low"), + V("follows the scent near {target} with muzzle extended", "advances toward {target} behind lowered horns"), + V("swings its horned head at {target} and stamps a forehoof", "drives toward {target} with forehead lowered"), + V("licks a nearby neck with a broad rough tongue", "stands flank to flank and exchanges low calls"), + V("moos in a rising, resonant call", "utters a deep lowing chuckle"), + V("leans its broad chest into the load", "pulls steadily with cloven hooves planted")); + + Add( + voices, + "dog", + V("trots ahead with nose active and tail balancing each turn", "pads forward in an even four-beat gait"), + V("sits on its haunches with ears trained forward", "waits with head tilted and tail tapping"), + V("bows on its forelegs and bounds sideways", "bounds in a circle with tail sweeping"), + V("holds the meal between both forepaws and gnaws", "bites repeatedly before licking its muzzle"), + V("circles twice and curls with nose beneath tail", "sprawls on its side with paws twitching"), + V("tracks the scent near {target} with nose sweeping low", "surges toward {target} in long bounding strides"), + V("bares its teeth near {target} and lunges from braced forelegs", "snaps at {target}, pivots, and drives in again"), + V("greets with a nose touch and a broadly wagging tail", "sniffs closely before settling alongside"), + V("pants through a breathy, barking chuckle", "lets out a short yip followed by a huff"), + V("carries the task firmly between its jaws", "digs in with forepaws and pulls backward")); + + Add( + voices, + "fox", + V("trots lightly with its brush held level", "slips forward on narrow paws with ears pricked"), + V("sits with its brush curled around slender legs", "stands listening while its pointed ears swivel"), + V("pounces high and lands forepaws-first", "whips around after its own sweeping brush"), + V("nips at the meal with a narrow muzzle", "pins the meal beneath one paw and chews"), + V("curls tightly beneath its sweeping brush", "rests with pointed muzzle tucked against its chest"), + V("tilts its head near {target} before pouncing", "stalks toward {target} with ears fixed ahead"), + V("darts at {target} with a quick bite and springs clear", "twists near {target} and snaps from a narrow stance"), + V("touches pointed muzzles and circles with brush raised", "grooms a nearby ear with delicate nibbles"), + V("gekker-calls in a rapid, rasping burst", "lets out a high yip followed by a throaty chatter"), + V("carries material delicately in its jaws", "scrapes at the task with quick alternating forepaws")); + + Add( + voices, + "goat", + V("picks a nimble path on hard cloven hooves", "bounds forward with short springing steps"), + V("stands chewing while horizontal pupils scan", "balances squarely with beard stirring beneath its chin"), + V("hops onto stiff legs and twists in place", "rears briefly and taps its horns together"), + V("takes a bite with mobile split lips", "strips off a bite and chews with a sideways jaw"), + V("tucks its legs beneath a narrow, deep chest", "rests chin-down with horns angled back"), + V("tests scents near {target} with quick sniffs", "bounds toward {target} with horns tipped forward"), + V("rears near {target} and crashes down behind its horns", "drives a compact headbutt at {target} from planted hooves"), + V("rubs horn bases against a nearby neck", "nibbles carefully along another coat"), + V("bleats in a broken, chuckling cadence", "snorts and answers with a nasal trill"), + V("pushes with its forehead while hooves grip", "carries a small load between nimble jaws")); + + Add( + voices, + "hyena", + V("lopes ahead with high shoulders and a sloping back", "pads forward on powerful forequarters"), + V("stands squarely with rounded ears tracking every sound", "sits back beneath a heavy neck and watches"), + V("bounds in a crooked circle with jaws gaping", "feints forward and wheels away on long forelegs"), + V("cracks a bite with massive premolars", "tears and swallows with rapid jaw strokes"), + V("lies belly-down with heavy jaws across its paws", "curls on one flank beneath raised shoulders"), + V("quarters near {target} with nose low", "paces toward {target} in an endurance lope"), + V("clamps onto {target} with crushing jaws and braces", "rushes at {target} behind powerful shoulders and snaps"), + V("greets with muzzle low and measured flank sniffing", "grooms a nearby coat with short front-tooth nibbles"), + V("whoops and cackles in a rising cascade", "chatters out a breathless, ringing laugh"), + V("drags the task with jaws locked and shoulders driving", "crushes material between broad teeth")); + + Add( + voices, + "monkey", + V("scampers on all fours with its long tail balancing", "bounds ahead and reaches down with deft hands"), + V("squats on its haunches and scans with quick head turns", "perches upright while its fingers pick through its coat"), + V("claps its hands and flicks its tail for balance", "leaps, lands, and flicks its tail for balance"), + V("turns the meal with nimble thumbs before biting", "holds a morsel in both hands and nibbles"), + V("curls on its side with hands tucked beneath its chin", "dozes in a compact crouch with tail wrapped close"), + V("searches near {target} with quick eyes and reaching hands", "bounds toward {target} in a four-limbed rush"), + V("grapples with {target} using both hands", "leaps at {target} with a slap before springing back"), + V("picks carefully through a nearby coat", "touches faces and chatters at close range"), + V("hoots through a rapid string of chattering notes", "bares its teeth in a breathy, bouncing chuckle"), + V("turns and fits material with nimble fingers", "carries material clasped against its chest")); + + Add( + voices, + "rabbit", + V("bounds forward on long hind feet", "hops in quick arcs with ears held upright"), + V("sits tall with nose pulsing and ears rotating", "crouches low on folded hind legs"), + V("leaps straight up and twists in midair", "darts in a tight circle before kicking out"), + V("clips each bite with sharp front incisors", "nibbles rapidly while its jaw circles"), + V("settles into a compact loaf with feet hidden", "lies stretched out with ears laid along its back"), + V("sniffs near {target} with nose twitching rapidly", "bounds toward {target} in sudden accelerating leaps"), + V("boxes at {target} with rapid forepaw strikes", "kicks toward {target} with both powerful hind feet"), + V("touches noses and settles flank to flank", "grooms a nearby forehead with tiny licks"), + V("honks softly and chatters its teeth", "lets out a bright squeal between quick nose twitches"), + V("scrapes and gathers with alternating forepaws", "carries small material between its incisors")); + + Add( + voices, + "raccoon", + V("ambles forward with masked face low and ringed tail trailing", "walks on nimble plantigrade paws with back arched"), + V("sits upright and rubs its sensitive forepaws together", "pauses with black fingers spread and ears forward"), + V("rolls onto one shoulder with both paws raised", "pounces, tumbles, and rights its ringed tail"), + V("feels over each morsel before taking a bite", "holds the meal in both forepaws and chews neatly"), + V("curls around its ringed tail with masked muzzle tucked", "dozes on its side with forepaws folded together"), + V("probes near {target} with sensitive fingers and a pointed nose", "follows the scent toward {target} with marked face low"), + V("grabs at {target} with both forepaws and bites sharply", "rears near {target} and slashes with black claws"), + V("sniffs another marked face and explores a nearby coat with both paws", "sits close and grooms with careful fingerlike claws"), + V("trills through a raspy series of chuckles", "chatters and chirrs beneath its dark facial markings"), + V("sorts and turns material with deft black paws", "pries at the task with sensitive fingers")); + + Add( + voices, + "rat", + V("scurries low with naked tail balancing each turn", "trots in quick close steps with whiskers fanned"), + V("sits upright and washes its muzzle between pink paws", "freezes with whiskers trembling and round ears raised"), + V("clasps its forepaws and chatters its incisors", "darts forward, wheels, and flicks its long tail"), + V("holds a morsel in both paws and gnaws with orange incisors", "nibbles in rapid bites while whiskers brush the meal"), + V("curls into a tight ball around its bare tail", "lies belly-down with pointed muzzle on folded paws"), + V("searches near {target} with whiskers sweeping both sides", "scurries toward {target} with nose held low"), + V("lunges at {target} and bites with chisel incisors", "rears near {target} to box rapidly with both forepaws"), + V("touches noses and grooms around a nearby ear", "huddles close while whiskers cross and tails coil"), + V("bruxes its incisors in a soft, crackling chuckle", "squeaks through a rapid series of chirps"), + V("gnaws the task with tireless incisors", "carries material tucked beneath its chin")); + + Add( + voices, + "rhino", + V("tramps forward on three-toed feet beneath a heavy horn", "moves with thick shoulders rolling under folded hide"), + V("stands nearly motionless while small ears swivel", "rests its massive head with horn angled low"), + V("bucks into a short heavy trot", "pivots on stout legs and tosses its horn"), + V("crops a bite with a broad square lip", "chews a bite behind thick folded lips"), + V("folds its stout legs and settles onto thick hide", "lies flank-down with horn and chin resting low"), + V("sniffs near {target} with ears flicking independently", "charges toward {target} with horn level"), + V("charges at {target} behind its lowered horn", "swings its massive skull near {target} and stamps forward"), + V("touches horns lightly before standing side by side", "rubs thick shoulder hide against a nearby flank"), + V("snorts in a rough, rumbling burst", "squeals once and follows with a throaty puff"), + V("pushes the load with horn base and broad forehead", "leans its full weight into the task")); + + Add( + voices, + "sheep", + V("walks in close steps beneath a thick fleece", "trots with narrow hooves and wool bouncing"), + V("stands chewing with ears angled out from the wool", "settles squarely while its dense fleece rises and falls"), + V("springs forward on stiff legs and lands together", "butts lightly, backs away, and hops again"), + V("crops a bite with a split upper lip", "chews cud in slow sideways turns"), + V("folds its legs beneath a round fleece-covered body", "rests with muzzle tucked against warm wool"), + V("lifts its muzzle near {target} and follows", "advances toward {target} with woolly forehead lowered"), + V("backs away from {target} and drives forward behind curled horns", "braces narrow hooves near {target} and strikes with its forehead"), + V("presses into nearby company until fleeces touch", "nuzzles beneath a nearby woolly cheek"), + V("bleats in a wavering, breathy cadence", "answers with a short nasal baa"), + V("pulls steadily with fleece bunching over its shoulders", "nudges the task ahead with a woolly brow")); + + Add( + voices, + "unicorn", + V("canters with a single spiral horn cutting a clean line", "steps high on bright cloven hooves with mane flowing"), + V("stands poised with horn upright and ears angled forward", "rests one hind hoof while its long mane settles"), + V("arches its neck and bounds on light hooves", "rears, paws once, and lands beneath its streaming mane"), + V("takes a bite with mobile lips", "chews slowly beneath the shadow of its spiral horn"), + V("folds its legs and rests with horn angled safely aside", "lies with its muzzle nestled against a flowing mane"), + V("tracks movement near {target} with horn aligned", "surges toward {target} in a light canter"), + V("drives at {target} with spiral horn lowered", "rears near {target} and strikes with both forehooves"), + V("touches muzzles beneath an arched neck", "combs a nearby mane with careful teeth"), + V("nickers in a clear, bell-like trill", "whinnies through a bright rippling cadence"), + V("lifts and guides the task with the base of its horn", "pulls in a balanced stride on cloven hooves")); + + Add( + voices, + "wolf", + V("lopes forward with long legs and level back", "trots in an efficient line with tail held low"), + V("stands with ears pricked and nose raised", "sits tall on its haunches and watches"), + V("bows low on the forelegs before bounding sideways", "wrestles with open jaws and careful pawing"), + V("holds the meal between forepaws and tears sideways", "crunches the meal with strong jaws before licking its muzzle"), + V("curls tightly with bushy tail over its nose", "lies chest-down with chin between outstretched paws"), + V("runs toward {target} with nose low and stride lengthening", "fans out near {target} at an efficient lope"), + V("rushes at {target} with jaws open and shoulders braced", "bites at {target}, releases, and circles on quick paws"), + V("greets with muzzle licks and a low sweeping tail", "leans flank-first against nearby company"), + V("pants out a rough, breathy chuff", "breaks into a rising series of yips and howls"), + V("drags the load with jaws set and legs driving", "scrapes and clears with powerful forepaws")); + + Add( + voices, + "worm", + V("extends its front segments and draws the rest forward", "crawls in rippling contractions along its bristled underside"), + V("holds its tapered front still while the body contracts", "rests in a loose curve with segments barely shifting"), + V("loops its flexible body and straightens in a quick ripple", "coils tightly before easing straight"), + V("draws the meal into its mouth opening", "rasps at the meal with steady muscular pulls"), + V("settles into a compact coil with segments relaxed", "lies fully extended with only faint body pulses"), + V("probes near {target} with a tapered front and rippling muscles", "crawls toward {target} in slow curves"), + V("whips near {target} with its front segments and recoils", "braces near {target} and lashes forward"), + V("presses along another segmented body", "twines briefly before crawling alongside"), + V("makes a faint rasp through contracting segments", "rustles in a quick sequence of dry body ripples"), + V("pulls loose material backward beneath its body", "works forward by bracing each band of tiny bristles")); + } +} diff --git a/IdleSpectator/ActivityVoice.cs b/IdleSpectator/ActivityVoice.cs index 102ebf8..ee1fec6 100644 --- a/IdleSpectator/ActivityVoice.cs +++ b/IdleSpectator/ActivityVoice.cs @@ -88,9 +88,8 @@ public sealed class ProseVoice public bool BaseIsCiv { get; } public bool BaseIsHumanoid { get; } public bool BaseIsAnimal { get; } - public string FlavorKey { get; } - public int FlavorOrdinal { get; } - public string ProfileTag { get; } + public string VoiceKey { get; } + public ActivityVoiceSource Source { get; } public bool BaseRecoveryExpected { get; } public bool BaseRecoverySucceeded { get; } @@ -106,9 +105,8 @@ public sealed class ProseVoice bool baseIsCiv, bool baseIsHumanoid, bool baseIsAnimal, - string flavorKey, - int flavorOrdinal, - string profileTag, + string voiceKey, + ActivityVoiceSource source, bool baseRecoveryExpected, bool baseRecoverySucceeded) { @@ -123,9 +121,8 @@ public sealed class ProseVoice BaseIsCiv = baseIsCiv; BaseIsHumanoid = baseIsHumanoid; BaseIsAnimal = baseIsAnimal; - FlavorKey = string.IsNullOrEmpty(flavorKey) ? LiveSpeciesId : flavorKey; - FlavorOrdinal = flavorOrdinal; - ProfileTag = profileTag ?? ""; + VoiceKey = voiceKey ?? ""; + Source = source; BaseRecoveryExpected = baseRecoveryExpected; BaseRecoverySucceeded = baseRecoverySucceeded; } @@ -144,9 +141,8 @@ public sealed class ProseVoice false, false, false, - speciesId, - 0, "", + ActivityVoiceSource.Unknown, false, false); } diff --git a/IdleSpectator/ActivityVoiceHarness.cs b/IdleSpectator/ActivityVoiceHarness.cs index 551c06c..7c0e180 100644 --- a/IdleSpectator/ActivityVoiceHarness.cs +++ b/IdleSpectator/ActivityVoiceHarness.cs @@ -42,7 +42,22 @@ public static class ActivityVoiceHarness "patient citrus", "game", "through play", - "in play" + "in play", + "blank persistence", + "seared meal", + "sugared meal", + "sweet meal", + "raw meal", + "tough meal", + "patient care", + "with determination", + "playfully", + "with delight", + "cheerful", + "eagerly", + "contentedly", + "stubborn focus", + "stubborn persistence" }; private static readonly string[] UngroundedTerrainFragments = @@ -64,9 +79,11 @@ public static class ActivityVoiceHarness { List ids = ActivityAssetCatalog.EnumerateLiveActorAssetIds(); var rows = new StringBuilder(32768); - rows.AppendLine("id\tbase\tkind\tmodifier\tfamily\tbody\teffective\tecology\tordinal\trecovery\tfingerprint"); + rows.AppendLine("id\tbase\tkind\tmodifier\tfamily\tbody\teffective\tecology\tsource\tvoice_key\trecovery\tfingerprint"); int missing = 0; + int missingAuthored = 0; + int orphanAuthored = 0; int defaults = 0; int emptyProse = 0; int leaks = 0; @@ -77,9 +94,15 @@ public static class ActivityVoiceHarness int vehicleMismatch = 0; int awkwardProse = 0; int terrainLeaks = 0; + int variantBaseFail = 0; + int authoredPhraseFail = 0; string sample = ""; string vehicleFingerprint = null; var fingerprints = new Dictionary(StringComparer.Ordinal); + var fingerprintsById = new Dictionary(StringComparer.OrdinalIgnoreCase); + var voicesById = new Dictionary(StringComparer.OrdinalIgnoreCase); + var liveBases = new HashSet(StringComparer.OrdinalIgnoreCase); + var validatedBases = new HashSet(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < ids.Count; i++) { @@ -93,6 +116,21 @@ public static class ActivityVoiceHarness } ProseVoice voice = ActivityVoiceResolver.Resolve(asset); + voicesById[id] = voice; + if (voice.AssetKind != ActivityAssetKind.Vehicle) + { + liveBases.Add(voice.BaseSpeciesId); + if (voice.Source != ActivityVoiceSource.AuthoredSpecies + || !ActivitySpeciesVoiceCatalog.HasCompleteVoice(voice.BaseSpeciesId)) + { + missingAuthored++; + AddSample(ref sample, id + ":voice-source=" + voice.Source); + } + else if (validatedBases.Add(voice.BaseSpeciesId)) + { + authoredPhraseFail += ValidateAuthoredVoice(voice.BaseSpeciesId, ref sample); + } + } if (voice.EffectiveActionTag == ActivityBodyTag.Default || string.IsNullOrEmpty(voice.BaseFamily) || voice.BaseFamily == ActivityVoiceFamilies.Default) @@ -125,6 +163,7 @@ public static class ActivityVoiceHarness int localEmpty; int localLeaks; string fingerprint = Fingerprint(id, out localEmpty, out localLeaks); + fingerprintsById[id] = fingerprint; emptyProse += localEmpty; leaks += localLeaks; string awkward = FindAwkwardPhrase(fingerprint); @@ -172,11 +211,40 @@ public static class ActivityVoiceHarness .Append(voice.BaseBodyTag).Append('\t') .Append(voice.EffectiveActionTag).Append('\t') .Append(voice.EcologyTag).Append('\t') - .Append(voice.FlavorOrdinal).Append('\t') + .Append(voice.Source).Append('\t') + .Append(voice.VoiceKey).Append('\t') .Append(voice.BaseRecoveryExpected ? voice.BaseRecoverySucceeded.ToString() : "n/a").Append('\t') .Append(fingerprint.Replace('\t', ' ')).Append('\n'); } + foreach (KeyValuePair pair in voicesById) + { + ProseVoice voice = pair.Value; + if (voice.AssetKind == ActivityAssetKind.Vehicle + || pair.Key.Equals(voice.BaseSpeciesId, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + if (!fingerprintsById.TryGetValue(voice.BaseSpeciesId, out string baseFingerprint) + || !fingerprintsById.TryGetValue(pair.Key, out string variantFingerprint) + || variantFingerprint.Equals(baseFingerprint, StringComparison.Ordinal)) + { + variantBaseFail++; + AddSample(ref sample, pair.Key + ":same-as-base=" + voice.BaseSpeciesId); + } + } + + List authoredIds = ActivitySpeciesVoiceCatalog.AuthoredIds(); + for (int i = 0; i < authoredIds.Count; i++) + { + if (!liveBases.Contains(authoredIds[i])) + { + orphanAuthored++; + AddSample(ref sample, authoredIds[i] + ":orphan-voice"); + } + } + int pairFail = PairwiseFailures(ref sample); bool liveContextOk = VerifyLiveContext(ref sample); try @@ -191,6 +259,8 @@ public static class ActivityVoiceHarness bool passed = ids.Count > 50 && missing == 0 + && missingAuthored == 0 + && orphanAuthored == 0 && defaults == 0 && emptyProse == 0 && leaks == 0 @@ -201,6 +271,8 @@ public static class ActivityVoiceHarness && vehicleMismatch == 0 && awkwardProse == 0 && terrainLeaks == 0 + && variantBaseFail == 0 + && authoredPhraseFail == 0 && pairFail == 0 && liveContextOk; return new ActivityVoiceAuditResult @@ -208,6 +280,8 @@ public static class ActivityVoiceHarness Passed = passed, Detail = "total=" + ids.Count + " missing=" + missing + + " missingAuthored=" + missingAuthored + + " orphanAuthored=" + orphanAuthored + " defaults=" + defaults + " empty=" + emptyProse + " leaks=" + leaks @@ -218,12 +292,68 @@ public static class ActivityVoiceHarness + " vehicleMismatch=" + vehicleMismatch + " awkwardProse=" + awkwardProse + " terrainLeaks=" + terrainLeaks + + " variantBaseFail=" + variantBaseFail + + " authoredPhraseFail=" + authoredPhraseFail + " pairFail=" + pairFail + " liveContextOk=" + liveContextOk + " sample='" + sample + "'" }; } + private static int ValidateAuthoredVoice(string baseSpeciesId, ref string sample) + { + int failures = 0; + string label = ActivityAssetCatalog.SpeciesDisplayLabel(baseSpeciesId); + for (int i = 0; i < ActivitySpeciesVoice.CoreVerbs.Length; i++) + { + string verb = ActivitySpeciesVoice.CoreVerbs[i]; + string[] bag = ActivitySpeciesVoiceCatalog.GetBag( + baseSpeciesId, + verb, + ActivityTerrain.Unknown); + if (bag == null || bag.Length < 2) + { + failures++; + AddSample(ref sample, baseSpeciesId + ":" + verb + ":incomplete"); + continue; + } + + for (int p = 0; p < bag.Length; p++) + { + string phrase = bag[p] ?? ""; + bool targetMissing = (verb == "hunt" || verb == "fight") + && phrase.IndexOf("{target}", StringComparison.Ordinal) < 0; + bool contextMissing = !phrase.EndsWith( + "{place_at}{job_as}", + StringComparison.Ordinal); + bool actorLeak = phrase.IndexOf("{actor}", StringComparison.Ordinal) >= 0; + bool speciesLeak = !string.IsNullOrEmpty(label) + && Regex.IsMatch( + phrase, + @"\b" + Regex.Escape(label) + @"\b", + RegexOptions.IgnoreCase); + string awkward = FindAwkwardPhrase(phrase); + string terrain = FindPhrase(phrase, UngroundedTerrainFragments); + if (targetMissing || contextMissing || actorLeak || speciesLeak + || !string.IsNullOrEmpty(awkward) || !string.IsNullOrEmpty(terrain)) + { + failures++; + AddSample( + ref sample, + baseSpeciesId + ":" + verb + ":" + p + + ":target=" + !targetMissing + + ":context=" + !contextMissing + + ":actor=" + actorLeak + + ":species=" + speciesLeak + + ":awkward=" + awkward + + ":terrain=" + terrain); + } + } + } + + return failures; + } + private static string FindAwkwardPhrase(string fingerprint) { return FindPhrase(fingerprint, AwkwardProseFragments); @@ -275,6 +405,8 @@ public static class ActivityVoiceHarness bool ok = ctx.Voice != null && ctx.Voice.LiveSpeciesId == (actor.asset.id ?? "") && ctx.Voice.EffectiveActionTag != ActivityBodyTag.Default + && (ctx.Voice.AssetKind == ActivityAssetKind.Vehicle + || ctx.Voice.Source == ActivityVoiceSource.AuthoredSpecies) && ctx.Terrain == expectedTerrain && !string.IsNullOrEmpty(line); if (!ok) diff --git a/IdleSpectator/ActivityVoiceProfiles.cs b/IdleSpectator/ActivityVoiceProfiles.cs deleted file mode 100644 index feff24f..0000000 --- a/IdleSpectator/ActivityVoiceProfiles.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace IdleSpectator; - -/// Thin flavor identities for creatures whose voice should be unmistakable. -public static class ActivityVoiceProfiles -{ - private static readonly Dictionary Profiles = - new Dictionary(StringComparer.OrdinalIgnoreCase) - { - ["dragon"] = "dragon", - ["fairy"] = "fairy", - ["cat"] = "cat", - ["dog"] = "dog", - ["buffalo"] = "buffalo", - ["lemon_snail"] = "lemon_snail", - ["human"] = "human", - ["elf"] = "elf", - ["dwarf"] = "dwarf", - ["orc"] = "orc", - ["crabzilla"] = "crabzilla", - ["god_finger"] = "god_finger", - ["greg"] = "greg", - ["angle"] = "angle", - ["cold_one"] = "cold", - ["snowman"] = "cold", - ["crystal_sword"] = "crystal" - }; - - public static string Resolve(string baseSpeciesId) - { - if (string.IsNullOrEmpty(baseSpeciesId)) - { - return ""; - } - - return Profiles.TryGetValue(baseSpeciesId, out string profile) ? profile : ""; - } -} diff --git a/IdleSpectator/ActivityVoiceResolver.cs b/IdleSpectator/ActivityVoiceResolver.cs index 3fdd36e..4103561 100644 --- a/IdleSpectator/ActivityVoiceResolver.cs +++ b/IdleSpectator/ActivityVoiceResolver.cs @@ -1,15 +1,10 @@ using System; -using System.Collections.Generic; namespace IdleSpectator; /// The only ActorAsset → prose voice classifier. public static class ActivityVoiceResolver { - private static readonly Dictionary FlavorOrdinals = - new Dictionary(StringComparer.Ordinal); - private static int _ordinalLibraryCount = -1; - public static ProseVoice Resolve(string speciesId) { ActorAsset asset = ActivityAssetCatalog.TryGetActorAsset(speciesId); @@ -24,8 +19,6 @@ public static class ActivityVoiceResolver } string liveId = liveAsset.id ?? ""; - int flavorOrdinal = FlavorOrdinal(liveId); - if (IsVehicle(liveId)) { return Build( @@ -36,7 +29,6 @@ public static class ActivityVoiceResolver ActivityBodyTag.Vehicle, ActivityEcologyTag.Water, ActivityVoiceFamilies.Special, - flavorOrdinal, false, true); } @@ -66,7 +58,6 @@ public static class ActivityVoiceResolver effective, ecology, family, - flavorOrdinal, recoveryExpected, recoverySucceeded); } @@ -119,7 +110,6 @@ public static class ActivityVoiceResolver ActivityBodyTag effective, ActivityEcologyTag ecology, string family, - int flavorOrdinal, bool recoveryExpected, bool recoverySucceeded) { @@ -128,6 +118,12 @@ public static class ActivityVoiceResolver ActivityBodyTag baseBody = kind == ActivityAssetKind.Vehicle ? ActivityBodyTag.Vehicle : ResolveBody(baseAsset, liveAsset, modifier); + bool authored = ActivitySpeciesVoiceCatalog.HasCompleteVoice(baseId); + ActivityVoiceSource source = kind == ActivityAssetKind.Vehicle + ? ActivityVoiceSource.Vehicle + : authored + ? ActivityVoiceSource.AuthoredSpecies + : ActivityVoiceSource.TaxonomyFallback; return new ProseVoice( liveId, baseId, @@ -140,9 +136,8 @@ public static class ActivityVoiceResolver baseAsset != null && baseAsset.civ, baseAsset != null && (baseAsset.is_humanoid || baseAsset.civ), baseAsset != null && baseAsset.default_animal, - liveId, - flavorOrdinal, - ActivityVoiceProfiles.Resolve(baseId), + authored ? ActivitySpeciesVoiceCatalog.VoiceKey(baseId) : baseId, + source, recoveryExpected, recoverySucceeded); } @@ -416,51 +411,6 @@ public static class ActivityVoiceResolver || EqualsIgnore(id, "crystal_sword"); } - private static int FlavorOrdinal(string id) - { - EnsureFlavorOrdinals(); - if (!string.IsNullOrEmpty(id) && FlavorOrdinals.TryGetValue(id, out int ordinal)) - { - return ordinal; - } - - return Math.Abs(StableHash(id)) % 1024; - } - - private static void EnsureFlavorOrdinals() - { - List ids = ActivityAssetCatalog.EnumerateLiveActorAssetIds(); - if (ids.Count == _ordinalLibraryCount && FlavorOrdinals.Count == ids.Count) - { - return; - } - - ids.Sort(StringComparer.Ordinal); - FlavorOrdinals.Clear(); - for (int i = 0; i < ids.Count; i++) - { - FlavorOrdinals[ids[i]] = i; - } - - _ordinalLibraryCount = ids.Count; - } - - private static int StableHash(string value) - { - unchecked - { - uint h = 2166136261; - string s = value ?? ""; - for (int i = 0; i < s.Length; i++) - { - h ^= s[i]; - h *= 16777619; - } - - return (int)(h & 0x7fffffff); - } - } - private static bool EqualsIgnore(string a, string b) { return !string.IsNullOrEmpty(a) && a.Equals(b, StringComparison.OrdinalIgnoreCase); diff --git a/IdleSpectator/AgentHarness.cs b/IdleSpectator/AgentHarness.cs index f9df1ad..06aaf7e 100644 --- a/IdleSpectator/AgentHarness.cs +++ b/IdleSpectator/AgentHarness.cs @@ -1711,6 +1711,15 @@ public static class AgentHarness detail = $"pending={have} want={want}"; break; } + case "pending_discovery_asset": + { + string assetId = ResolveAssetId(cmd); + bool want = string.IsNullOrEmpty(cmd.value) || ParseBool(cmd.value, true); + bool have = SpeciesDiscovery.IsPending(assetId); + pass = have == want; + detail = $"asset={assetId} pending={have} want={want} total={SpeciesDiscovery.PendingCount}"; + break; + } case "pending_interest": { int want = ParseCountExpect(cmd, defaultValue: 0); @@ -2572,23 +2581,23 @@ public static class AgentHarness && beetlePlay.IndexOf(" the beetle", System.StringComparison.OrdinalIgnoreCase) < 0 && frogPlay.IndexOf(" the frog", System.StringComparison.OrdinalIgnoreCase) < 0 && rhinoPlay.IndexOf(" the rhino", System.StringComparison.OrdinalIgnoreCase) < 0 - && (beetlePlay.IndexOf("scuttl", System.StringComparison.OrdinalIgnoreCase) >= 0 - || beetlePlay.IndexOf("skitter", System.StringComparison.OrdinalIgnoreCase) >= 0 - || beetlePlay.IndexOf("buzz", System.StringComparison.OrdinalIgnoreCase) >= 0 - || beetlePlay.IndexOf("tiny", System.StringComparison.OrdinalIgnoreCase) >= 0 - || beetlePlay.IndexOf("busy", System.StringComparison.OrdinalIgnoreCase) >= 0 - || beetlePlay.IndexOf("circuit", System.StringComparison.OrdinalIgnoreCase) >= 0) + && (beetlePlay.IndexOf("leg", System.StringComparison.OrdinalIgnoreCase) >= 0 + || beetlePlay.IndexOf("rocks", System.StringComparison.OrdinalIgnoreCase) >= 0 + || beetlePlay.IndexOf("wing", System.StringComparison.OrdinalIgnoreCase) >= 0) && (frogPlay.IndexOf("hop", System.StringComparison.OrdinalIgnoreCase) >= 0 || frogPlay.IndexOf("bounc", System.StringComparison.OrdinalIgnoreCase) >= 0 - || frogPlay.IndexOf("leap", System.StringComparison.OrdinalIgnoreCase) >= 0 - || frogPlay.IndexOf("splash", System.StringComparison.OrdinalIgnoreCase) >= 0 - || frogPlay.IndexOf("damp", System.StringComparison.OrdinalIgnoreCase) >= 0 - || frogPlay.IndexOf("jump", System.StringComparison.OrdinalIgnoreCase) >= 0); + || frogPlay.IndexOf("pops", System.StringComparison.OrdinalIgnoreCase) >= 0 + || frogPlay.IndexOf("toe", System.StringComparison.OrdinalIgnoreCase) >= 0) + && (rhinoPlay.IndexOf("horn", System.StringComparison.OrdinalIgnoreCase) >= 0 + || rhinoPlay.IndexOf("stout", System.StringComparison.OrdinalIgnoreCase) >= 0 + || rhinoPlay.IndexOf("buck", System.StringComparison.OrdinalIgnoreCase) >= 0 + || rhinoPlay.IndexOf("pivot", 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); + && (crabLandPlay.IndexOf("claw", System.StringComparison.OrdinalIgnoreCase) >= 0 + || crabLandPlay.IndexOf("pincer", System.StringComparison.OrdinalIgnoreCase) >= 0 + || crabLandPlay.IndexOf("side", System.StringComparison.OrdinalIgnoreCase) >= 0); bool fishTerrainOk = !string.IsNullOrEmpty(fishLandPlay) && !string.IsNullOrEmpty(fishWaterPlay) && fishLandPlay.IndexOf("water", System.StringComparison.OrdinalIgnoreCase) < 0 diff --git a/IdleSpectator/HarnessScenarios.cs b/IdleSpectator/HarnessScenarios.cs index dc98d7d..8bdae65 100644 --- a/IdleSpectator/HarnessScenarios.cs +++ b/IdleSpectator/HarnessScenarios.cs @@ -406,7 +406,7 @@ internal static class HarnessScenarios Step("d23", "age_current", wait: 2f), Step("d24", "assert", expect: "would_accept_curiosity", value: "true"), Step("d25", "discovery_drain"), - Step("d26", "assert", expect: "pending_discovery", value: "0"), + Step("d26", "assert", expect: "pending_discovery_asset", value: "false"), Step("d27", "assert", expect: "presented", asset: "auto", value: "true"), Step("d28", "director_run", wait: 1.2f), Step("d29", "assert", expect: "tip_matches_unit"), @@ -486,8 +486,8 @@ internal static class HarnessScenarios Step("act13d", "assert", expect: "activity_prose_grounding"), Step("act13e", "assert", expect: "activity_prose_library"), Step("act13f", "assert", expect: "activity_taxonomy_audit"), - Step("act14", "activity_force", asset: "laughing", label: "Laughing", count: 1), - Step("act14b", "assert", expect: "activity_log_contains", value: "laugh"), + Step("act14", "activity_force", asset: "laughing", label: "Laughing", count: 1, value: "angle"), + Step("act14b", "assert", expect: "activity_log_contains", value: "chime"), Step("act14c", "activity_force", asset: "eating", label: "Taking a meal", count: 1), Step("act15", "activity_force", asset: "farm", label: "Tends the fields", count: 1), Step("act16", "activity_force", asset: "hunt", label: "Hunts", count: 1, expect: "Barkley"), diff --git a/IdleSpectator/SpeciesDiscovery.cs b/IdleSpectator/SpeciesDiscovery.cs index 46c4f49..d105c3b 100644 --- a/IdleSpectator/SpeciesDiscovery.cs +++ b/IdleSpectator/SpeciesDiscovery.cs @@ -35,6 +35,11 @@ public static class SpeciesDiscovery public static int PendingCount => Pending.Count; + public static bool IsPending(string assetId) + { + return !string.IsNullOrEmpty(assetId) && Pending.Exists(p => p.AssetId == assetId); + } + public static int DrainedThisSession => _drainedThisSession; public static void OnSpectatorEnabled() diff --git a/IdleSpectator/mod.json b/IdleSpectator/mod.json index 98b0f16..f68a1c2 100644 --- a/IdleSpectator/mod.json +++ b/IdleSpectator/mod.json @@ -1,7 +1,7 @@ { "name": "IdleSpectator", "author": "dazed", - "version": "0.12.75", + "version": "0.12.79", "description": "AFK Idle Spectator (I) + Lore (L). Detailed living activity prose by default.", "GUID": "com.dazed.idlespectator" }