worldbox-observer-mod/IdleSpectator/ActivityVoiceResolver.cs

418 lines
18 KiB
C#

using System;
namespace IdleSpectator;
/// <summary>The only ActorAsset → prose voice classifier.</summary>
public static class ActivityVoiceResolver
{
public static ProseVoice Resolve(string speciesId)
{
ActorAsset asset = ActivityAssetCatalog.TryGetActorAsset(speciesId);
return asset != null ? Resolve(asset) : ProseVoice.Unknown(speciesId);
}
public static ProseVoice Resolve(ActorAsset liveAsset)
{
if (liveAsset == null)
{
return ProseVoice.Unknown();
}
string liveId = liveAsset.id ?? "";
if (IsVehicle(liveId))
{
return Build(
liveAsset,
liveAsset,
ActivityAssetKind.Vehicle,
ActivityModifier.None,
ActivityBodyTag.Vehicle,
ActivityEcologyTag.Water,
ActivityVoiceFamilies.Special,
false,
true);
}
ActivityModifier modifier = ResolveModifier(liveAsset);
bool recoveryExpected = modifier == ActivityModifier.Undead
&& liveId.StartsWith("zombie_", StringComparison.OrdinalIgnoreCase);
ActorAsset baseAsset = ResolveBaseAsset(liveAsset, modifier, out bool recoverySucceeded);
if (baseAsset == null)
{
baseAsset = liveAsset;
}
ActivityBodyTag body = ResolveBody(baseAsset, liveAsset, modifier);
ActivityEcologyTag ecology = ResolveEcology(baseAsset, body, modifier);
string family = ResolveFamily(baseAsset, body);
ActivityBodyTag effective = EffectiveTag(modifier, body);
ActivityAssetKind kind = IsExplicitSpecial(liveId)
? ActivityAssetKind.Special
: ActivityAssetKind.Creature;
return Build(
liveAsset,
baseAsset,
kind,
modifier,
effective,
ecology,
family,
recoveryExpected,
recoverySucceeded);
}
public static void ApplyToContext(ActivityContext ctx, ActorAsset liveAsset)
{
if (ctx == null)
{
return;
}
ProseVoice voice = Resolve(liveAsset);
ctx.Voice = voice;
ctx.SpeciesId = voice.LiveSpeciesId;
ctx.BaseSpeciesId = voice.BaseSpeciesId;
ctx.Family = voice.BaseFamily;
ctx.MannerTag = TagName(voice.EffectiveActionTag);
ctx.ModifierTag = ModifierName(voice.Modifier);
ctx.IsCiv = voice.BaseIsCiv;
ctx.IsHumanoid = voice.BaseIsHumanoid;
ctx.IsAnimal = voice.BaseIsAnimal;
if (liveAsset != null)
{
ctx.TaxonomicKingdom = liveAsset.name_taxonomic_kingdom ?? "";
ctx.TaxonomicPhylum = liveAsset.name_taxonomic_phylum ?? "";
ctx.TaxonomicClass = liveAsset.name_taxonomic_class ?? "";
ctx.TaxonomicOrder = liveAsset.name_taxonomic_order ?? "";
ctx.TaxonomicFamily = liveAsset.name_taxonomic_family ?? "";
ctx.TaxonomicGenus = liveAsset.name_taxonomic_genus ?? "";
ctx.TaxonomicSpecies = liveAsset.name_taxonomic_species ?? "";
}
}
public static string TagName(ActivityBodyTag tag)
{
return tag.ToString().ToLowerInvariant();
}
public static string ModifierName(ActivityModifier modifier)
{
return modifier == ActivityModifier.None ? "" : modifier.ToString().ToLowerInvariant();
}
private static ProseVoice Build(
ActorAsset liveAsset,
ActorAsset baseAsset,
ActivityAssetKind kind,
ActivityModifier modifier,
ActivityBodyTag effective,
ActivityEcologyTag ecology,
string family,
bool recoveryExpected,
bool recoverySucceeded)
{
string liveId = liveAsset?.id ?? "";
string baseId = baseAsset?.id ?? liveId;
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,
family,
baseBody,
ecology,
modifier,
effective,
kind,
baseAsset != null && baseAsset.civ,
baseAsset != null && (baseAsset.is_humanoid || baseAsset.civ),
baseAsset != null && baseAsset.default_animal,
authored ? ActivitySpeciesVoiceCatalog.VoiceKey(baseId) : baseId,
source,
recoveryExpected,
recoverySucceeded);
}
private static ActivityModifier ResolveModifier(ActorAsset asset)
{
string id = asset?.id ?? "";
string wild = asset?.kingdom_id_wild ?? "";
if (EqualsIgnore(wild, "undead")
|| id.StartsWith("zombie_", StringComparison.OrdinalIgnoreCase)
|| EqualsIgnore(id, "zombie")
|| EqualsIgnore(id, "skeleton")
|| EqualsIgnore(id, "ghost"))
{
return ActivityModifier.Undead;
}
if (EqualsIgnore(wild, "fire_elemental")
|| id.StartsWith("fire_elemental", StringComparison.OrdinalIgnoreCase))
{
return ActivityModifier.Elemental;
}
if (EqualsIgnore(wild, "mush") || id.StartsWith("mush_", StringComparison.OrdinalIgnoreCase))
{
return ActivityModifier.Mush;
}
if (EqualsIgnore(wild, "tumor") || id.StartsWith("tumor_", StringComparison.OrdinalIgnoreCase))
{
return ActivityModifier.Tumor;
}
if (EqualsIgnore(wild, "aliens") || EqualsIgnore(id, "alien") || EqualsIgnore(id, "ufo"))
{
return ActivityModifier.Alien;
}
if (EqualsIgnore(wild, "living_houses")
|| EqualsIgnore(id, "living_house")
|| EqualsIgnore(id, "assimilator")
|| EqualsIgnore(id, "printer")
|| EqualsIgnore(id, "god_finger")
|| EqualsIgnore(id, "crystal_sword"))
{
return ActivityModifier.Construct;
}
return ActivityModifier.None;
}
private static ActorAsset ResolveBaseAsset(
ActorAsset liveAsset,
ActivityModifier modifier,
out bool recoverySucceeded)
{
recoverySucceeded = true;
if (liveAsset == null || modifier != ActivityModifier.Undead)
{
return liveAsset;
}
string id = liveAsset.id ?? "";
string candidate = "";
if (id.StartsWith("zombie_civ_", StringComparison.OrdinalIgnoreCase))
{
candidate = id.Substring("zombie_".Length);
}
else if (id.StartsWith("zombie_animal_", StringComparison.OrdinalIgnoreCase))
{
candidate = id.Substring("zombie_animal_".Length);
}
else if (id.StartsWith("zombie_", StringComparison.OrdinalIgnoreCase))
{
candidate = id.Substring("zombie_".Length);
}
if (string.IsNullOrEmpty(candidate))
{
return liveAsset;
}
ActorAsset baseAsset = ActivityAssetCatalog.TryGetActorAsset(candidate);
recoverySucceeded = baseAsset != null;
return baseAsset ?? liveAsset;
}
private static ActivityBodyTag ResolveBody(
ActorAsset baseAsset,
ActorAsset liveAsset,
ActivityModifier modifier)
{
string id = baseAsset?.id ?? liveAsset?.id ?? "";
string liveId = liveAsset?.id ?? id;
string taxClass = baseAsset?.name_taxonomic_class ?? "";
string taxOrder = baseAsset?.name_taxonomic_order ?? "";
string taxFamily = baseAsset?.name_taxonomic_family ?? "";
if (IsVehicle(liveId)) return ActivityBodyTag.Vehicle;
if (EqualsIgnore(id, "dragon")) return ActivityBodyTag.Dragon;
if (EqualsIgnore(id, "crabzilla")) return ActivityBodyTag.Crab;
if (EqualsIgnore(id, "crystal_sword")) return ActivityBodyTag.Crystal;
if (EqualsIgnore(id, "snowman") || EqualsIgnore(id, "cold_one")) return ActivityBodyTag.Cold;
if (EqualsIgnore(id, "living_plants")) return ActivityBodyTag.Plant;
if (EqualsIgnore(id, "living_house") || EqualsIgnore(id, "god_finger")) return ActivityBodyTag.Construct;
if (EqualsIgnore(id, "assimilator") || EqualsIgnore(id, "printer")) return ActivityBodyTag.Machina;
if (EqualsIgnore(id, "sand_spider")) return ActivityBodyTag.Arachnid;
if (EqualsIgnore(id, "worm")) return ActivityBodyTag.Crawler;
if (id.StartsWith("ant_", StringComparison.OrdinalIgnoreCase)) return ActivityBodyTag.Insect;
if (modifier == ActivityModifier.Elemental)
{
if (liveId.EndsWith("_horse", StringComparison.OrdinalIgnoreCase)) return ActivityBodyTag.Livestock;
if (liveId.EndsWith("_snake", StringComparison.OrdinalIgnoreCase)) return ActivityBodyTag.Reptile;
if (liveId.EndsWith("_slug", StringComparison.OrdinalIgnoreCase)) return ActivityBodyTag.Crawler;
if (liveId.EndsWith("_blob", StringComparison.OrdinalIgnoreCase)) return ActivityBodyTag.Protist;
return ActivityBodyTag.Mythic;
}
if (modifier == ActivityModifier.Mush) return ActivityBodyTag.Fungi;
if (modifier == ActivityModifier.Tumor) return ActivityBodyTag.Tumor;
if (modifier == ActivityModifier.Alien) return ActivityBodyTag.Alien;
if (baseAsset != null && baseAsset.civ) return ActivityBodyTag.Humanoid;
if (EqualsIgnore(taxClass, "aves")) return ActivityBodyTag.Bird;
if (EqualsIgnore(taxClass, "insecta")
|| EqualsIgnore(taxOrder, "hymenoptera")
|| EqualsIgnore(taxOrder, "coleoptera")
|| EqualsIgnore(taxOrder, "lepidoptera")
|| EqualsIgnore(taxOrder, "diptera")
|| EqualsIgnore(taxOrder, "orthoptera")) return ActivityBodyTag.Insect;
if (EqualsIgnore(taxClass, "arachnida")
|| EqualsIgnore(taxOrder, "scorpiones")
|| EqualsIgnore(taxOrder, "araneae")) return ActivityBodyTag.Arachnid;
if (EqualsIgnore(taxClass, "amphibia")) return ActivityBodyTag.Amphibian;
if (EqualsIgnore(taxClass, "malacostraca")) return ActivityBodyTag.Crab;
if (EqualsIgnore(taxClass, "actinopterygii")
|| EqualsIgnore(taxClass, "chondrichthyes")
|| EqualsIgnore(taxClass, "cephalopoda")
|| EqualsIgnore(taxOrder, "testudines")) return ActivityBodyTag.Aquatic;
if (EqualsIgnore(taxClass, "reptilia")
|| EqualsIgnore(taxOrder, "squamata")
|| EqualsIgnore(taxOrder, "crocodilia")) return ActivityBodyTag.Reptile;
if (EqualsIgnore(taxFamily, "canidae")) return ActivityBodyTag.Canine;
if (EqualsIgnore(taxFamily, "felidae")) return ActivityBodyTag.Feline;
if (EqualsIgnore(taxFamily, "ursidae")) return ActivityBodyTag.Ursid;
if (EqualsIgnore(taxFamily, "phocidae")) return ActivityBodyTag.Seal;
if (EqualsIgnore(taxFamily, "procyonidae")) return ActivityBodyTag.Procyonid;
if (EqualsIgnore(taxFamily, "hyaenidae")) return ActivityBodyTag.Hyaenid;
if (EqualsIgnore(taxOrder, "primates")) return ActivityBodyTag.Primate;
if (EqualsIgnore(taxOrder, "rodentia")) return ActivityBodyTag.Rodent;
if (EqualsIgnore(taxOrder, "cingulata") || EqualsIgnore(taxFamily, "dasypodidae"))
return ActivityBodyTag.Armadillo;
if (EqualsIgnore(taxOrder, "artiodactyla")
|| EqualsIgnore(taxOrder, "perissodactyla")
|| EqualsIgnore(taxFamily, "bovidae")
|| EqualsIgnore(taxFamily, "suidae")
|| EqualsIgnore(taxFamily, "leporidae")
|| EqualsIgnore(taxFamily, "elephantidae")
|| EqualsIgnore(taxFamily, "rhinocerotidae")
|| EqualsIgnore(taxFamily, "camelidae")
|| EqualsIgnore(taxFamily, "equidae")) return ActivityBodyTag.Livestock;
string kingdom = baseAsset?.name_taxonomic_kingdom ?? "";
if (EqualsIgnore(kingdom, "plantae")) return ActivityBodyTag.Plant;
if (EqualsIgnore(kingdom, "fungi")) return ActivityBodyTag.Fungi;
if (EqualsIgnore(kingdom, "protista")) return ActivityBodyTag.Protist;
if (EqualsIgnore(kingdom, "machina")) return ActivityBodyTag.Machina;
if (EqualsIgnore(kingdom, "mythoria")) return ActivityBodyTag.Mythic;
if (EqualsIgnore(kingdom, "gregalia")) return ActivityBodyTag.Gregalia;
// All remaining current animalia assets are terrestrial mammals or explicit specials.
if (EqualsIgnore(taxClass, "mammalia")) return ActivityBodyTag.Livestock;
if (!string.IsNullOrEmpty(id)) return ActivityBodyTag.Mythic;
return ActivityBodyTag.Default;
}
private static string ResolveFamily(ActorAsset asset, ActivityBodyTag body)
{
if (asset != null && asset.civ) return ActivityVoiceFamilies.Humanoid;
string kingdom = asset?.name_taxonomic_kingdom ?? "";
if (EqualsIgnore(kingdom, "plantae")) return ActivityVoiceFamilies.Plant;
if (EqualsIgnore(kingdom, "fungi")) return ActivityVoiceFamilies.Fungi;
if (EqualsIgnore(kingdom, "protista")) return ActivityVoiceFamilies.Protist;
if (EqualsIgnore(kingdom, "machina")) return ActivityVoiceFamilies.Machina;
if (EqualsIgnore(kingdom, "mythoria")) return ActivityVoiceFamilies.Mythic;
if (EqualsIgnore(kingdom, "gregalia")) return ActivityVoiceFamilies.Gregalia;
return body switch
{
ActivityBodyTag.Canine => ActivityVoiceFamilies.Canine,
ActivityBodyTag.Feline => ActivityVoiceFamilies.Feline,
ActivityBodyTag.Bird => ActivityVoiceFamilies.Bird,
ActivityBodyTag.Insect => ActivityVoiceFamilies.Insect,
ActivityBodyTag.Arachnid => ActivityVoiceFamilies.Arachnid,
ActivityBodyTag.Amphibian => ActivityVoiceFamilies.Amphibian,
ActivityBodyTag.Aquatic => ActivityVoiceFamilies.Aquatic,
ActivityBodyTag.Seal => ActivityVoiceFamilies.Aquatic,
ActivityBodyTag.Crab => ActivityVoiceFamilies.Crustacean,
ActivityBodyTag.Reptile => ActivityVoiceFamilies.Reptile,
ActivityBodyTag.Livestock => ActivityVoiceFamilies.Livestock,
ActivityBodyTag.Plant => ActivityVoiceFamilies.Plant,
ActivityBodyTag.Fungi => ActivityVoiceFamilies.Fungi,
ActivityBodyTag.Protist => ActivityVoiceFamilies.Protist,
ActivityBodyTag.Machina => ActivityVoiceFamilies.Machina,
ActivityBodyTag.Mythic => ActivityVoiceFamilies.Mythic,
ActivityBodyTag.Dragon => ActivityVoiceFamilies.Mythic,
ActivityBodyTag.Cold => ActivityVoiceFamilies.Mythic,
ActivityBodyTag.Crystal => ActivityVoiceFamilies.Special,
ActivityBodyTag.Alien => ActivityVoiceFamilies.Special,
ActivityBodyTag.Construct => ActivityVoiceFamilies.Special,
ActivityBodyTag.Tumor => ActivityVoiceFamilies.Special,
ActivityBodyTag.Gregalia => ActivityVoiceFamilies.Gregalia,
ActivityBodyTag.Humanoid => ActivityVoiceFamilies.Humanoid,
ActivityBodyTag.Vehicle => ActivityVoiceFamilies.Special,
ActivityBodyTag.Default => ActivityVoiceFamilies.Default,
_ => ActivityVoiceFamilies.Mammal
};
}
private static ActivityEcologyTag ResolveEcology(
ActorAsset asset,
ActivityBodyTag body,
ActivityModifier modifier)
{
if (modifier == ActivityModifier.Undead) return ActivityEcologyTag.Undead;
if (modifier == ActivityModifier.Alien) return ActivityEcologyTag.Alien;
if (modifier == ActivityModifier.Construct) return ActivityEcologyTag.Machine;
if (body == ActivityBodyTag.Vehicle || body == ActivityBodyTag.Aquatic || body == ActivityBodyTag.Seal)
return ActivityEcologyTag.Water;
if (body == ActivityBodyTag.Bird || body == ActivityBodyTag.Insect) return ActivityEcologyTag.Air;
if (body == ActivityBodyTag.Plant) return ActivityEcologyTag.Plant;
if (body == ActivityBodyTag.Fungi || modifier == ActivityModifier.Mush) return ActivityEcologyTag.Fungal;
if (body == ActivityBodyTag.Machina) return ActivityEcologyTag.Machine;
if (body == ActivityBodyTag.Cold) return ActivityEcologyTag.Cold;
if (asset != null && asset.civ) return ActivityEcologyTag.Civilized;
if (body == ActivityBodyTag.Mythic || body == ActivityBodyTag.Dragon
|| modifier == ActivityModifier.Elemental) return ActivityEcologyTag.Mythic;
return ActivityEcologyTag.Land;
}
private static ActivityBodyTag EffectiveTag(ActivityModifier modifier, ActivityBodyTag body)
{
return modifier switch
{
ActivityModifier.Undead => ActivityBodyTag.Undead,
ActivityModifier.Elemental => ActivityBodyTag.Elemental,
ActivityModifier.Mush => ActivityBodyTag.Mush,
ActivityModifier.Tumor => ActivityBodyTag.Tumor,
ActivityModifier.Alien => ActivityBodyTag.Alien,
ActivityModifier.Construct => ActivityBodyTag.Construct,
_ => body
};
}
private static bool IsVehicle(string id)
{
return !string.IsNullOrEmpty(id)
&& id.StartsWith("boat_", StringComparison.OrdinalIgnoreCase);
}
private static bool IsExplicitSpecial(string id)
{
return EqualsIgnore(id, "crabzilla")
|| EqualsIgnore(id, "god_finger")
|| EqualsIgnore(id, "living_plants")
|| EqualsIgnore(id, "living_house")
|| EqualsIgnore(id, "printer")
|| EqualsIgnore(id, "assimilator")
|| EqualsIgnore(id, "crystal_sword");
}
private static bool EqualsIgnore(string a, string b)
{
return !string.IsNullOrEmpty(a) && a.Equals(b, StringComparison.OrdinalIgnoreCase);
}
}