More coverage
This commit is contained in:
parent
148e807f23
commit
3cac264bd7
16 changed files with 1379 additions and 39 deletions
|
|
@ -317,6 +317,14 @@ public static class ActivityAssetCatalog
|
|||
|
||||
public static List<string> EnumerateLiveBiomeIds() => EnumerateLibraryIds("biome_library");
|
||||
|
||||
public static List<string> EnumerateLiveCultureTraitIds() => EnumerateLibraryIds("culture_traits");
|
||||
|
||||
public static List<string> EnumerateLiveReligionTraitIds() => EnumerateLibraryIds("religion_traits");
|
||||
|
||||
public static List<string> EnumerateLiveClanTraitIds() => EnumerateLibraryIds("clan_traits");
|
||||
|
||||
public static List<string> EnumerateLiveLanguageTraitIds() => EnumerateLibraryIds("language_traits");
|
||||
|
||||
private static List<string> EnumerateLibraryIds(string assetManagerField)
|
||||
{
|
||||
var ids = new List<string>();
|
||||
|
|
|
|||
|
|
@ -209,6 +209,50 @@ public static class DomainEventHarness
|
|||
EventCatalog.Biome.AuthoredIds);
|
||||
AppendLibraryDomain(tsv, "biomes", ActivityAssetCatalog.EnumerateLiveBiomeIds(), biomes, EventCatalog.Biome.GetOrFallback);
|
||||
|
||||
CatalogCoverageDiff cultureTraits = CatalogCoverageAudit.Diff(
|
||||
"culture_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveCultureTraitIds(),
|
||||
EventCatalog.CultureTrait.AuthoredIds);
|
||||
AppendLibraryDomain(
|
||||
tsv,
|
||||
"culture_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveCultureTraitIds(),
|
||||
cultureTraits,
|
||||
EventCatalog.CultureTrait.GetOrFallback);
|
||||
|
||||
CatalogCoverageDiff religionTraits = CatalogCoverageAudit.Diff(
|
||||
"religion_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveReligionTraitIds(),
|
||||
EventCatalog.ReligionTrait.AuthoredIds);
|
||||
AppendLibraryDomain(
|
||||
tsv,
|
||||
"religion_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveReligionTraitIds(),
|
||||
religionTraits,
|
||||
EventCatalog.ReligionTrait.GetOrFallback);
|
||||
|
||||
CatalogCoverageDiff clanTraits = CatalogCoverageAudit.Diff(
|
||||
"clan_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveClanTraitIds(),
|
||||
EventCatalog.ClanTrait.AuthoredIds);
|
||||
AppendLibraryDomain(
|
||||
tsv,
|
||||
"clan_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveClanTraitIds(),
|
||||
clanTraits,
|
||||
EventCatalog.ClanTrait.GetOrFallback);
|
||||
|
||||
CatalogCoverageDiff languageTraits = CatalogCoverageAudit.Diff(
|
||||
"language_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveLanguageTraitIds(),
|
||||
EventCatalog.LanguageTrait.AuthoredIds);
|
||||
AppendLibraryDomain(
|
||||
tsv,
|
||||
"language_traits",
|
||||
ActivityAssetCatalog.EnumerateLiveLanguageTraitIds(),
|
||||
languageTraits,
|
||||
EventCatalog.LanguageTrait.GetOrFallback);
|
||||
|
||||
int relMissing = 0;
|
||||
int relTotal = 0;
|
||||
foreach (string id in EventCatalog.Relationship.AuthoredIds)
|
||||
|
|
|
|||
|
|
@ -357,6 +357,10 @@ public static class EventInjectCoverageHarness
|
|||
InjectLibrary("worldLaw", EventCatalog.WorldLaw.AuthoredIds, id => EventCatalog.WorldLaw.GetOrFallback(id), unit, result, Fail, SkipB, Ok, Accept);
|
||||
InjectLibrary("subspecies_trait", EventCatalog.SubspeciesTrait.AuthoredIds, id => EventCatalog.SubspeciesTrait.GetOrFallback(id), unit, result, Fail, SkipB, Ok, Accept);
|
||||
InjectLibrary("biome", EventCatalog.Biome.AuthoredIds, id => EventCatalog.Biome.GetOrFallback(id), unit, result, Fail, SkipB, Ok, Accept);
|
||||
InjectLibrary("culture_trait", EventCatalog.CultureTrait.AuthoredIds, id => EventCatalog.CultureTrait.GetOrFallback(id), unit, result, Fail, SkipB, Ok, Accept);
|
||||
InjectLibrary("religion_trait", EventCatalog.ReligionTrait.AuthoredIds, id => EventCatalog.ReligionTrait.GetOrFallback(id), unit, result, Fail, SkipB, Ok, Accept);
|
||||
InjectLibrary("clan_trait", EventCatalog.ClanTrait.AuthoredIds, id => EventCatalog.ClanTrait.GetOrFallback(id), unit, result, Fail, SkipB, Ok, Accept);
|
||||
InjectLibrary("language_trait", EventCatalog.LanguageTrait.AuthoredIds, id => EventCatalog.LanguageTrait.GetOrFallback(id), unit, result, Fail, SkipB, Ok, Accept);
|
||||
|
||||
CatalogCoverageAudit.WriteReport(outputDirectory, "event-inject-coverage.tsv", tsv.ToString());
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,10 @@ public static class EventLiveCoverageLedger
|
|||
SeedDiscrete("worldLaw", EventCatalog.WorldLaw.AuthoredIds, id => EventCatalog.WorldLaw.GetOrFallback(id));
|
||||
SeedDiscrete("subspecies_trait", EventCatalog.SubspeciesTrait.AuthoredIds, id => EventCatalog.SubspeciesTrait.GetOrFallback(id));
|
||||
SeedDiscrete("biome", EventCatalog.Biome.AuthoredIds, id => EventCatalog.Biome.GetOrFallback(id));
|
||||
SeedDiscrete("culture_trait", EventCatalog.CultureTrait.AuthoredIds, id => EventCatalog.CultureTrait.GetOrFallback(id));
|
||||
SeedDiscrete("religion_trait", EventCatalog.ReligionTrait.AuthoredIds, id => EventCatalog.ReligionTrait.GetOrFallback(id));
|
||||
SeedDiscrete("clan_trait", EventCatalog.ClanTrait.AuthoredIds, id => EventCatalog.ClanTrait.GetOrFallback(id));
|
||||
SeedDiscrete("language_trait", EventCatalog.LanguageTrait.AuthoredIds, id => EventCatalog.LanguageTrait.GetOrFallback(id));
|
||||
}
|
||||
|
||||
private static void SeedDiscrete(string domain, IEnumerable<string> ids, Func<string, DiscreteEventEntry> get)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,14 @@ public static class EventLivePipelinesHarness
|
|||
RunBoatSmoke(unit, fails);
|
||||
RunCombatSmoke(unit, fails);
|
||||
RunMeta(unit, fails);
|
||||
MarkUnsupportedLibraries(fails);
|
||||
RunWorldLaws(fails);
|
||||
RunItems(unit, fails);
|
||||
RunSubspeciesTraits(unit, fails);
|
||||
RunPowers(unit, fails);
|
||||
RunGenes(unit, fails);
|
||||
RunPhenotypes(unit, fails);
|
||||
RunMetaTraits(unit, fails);
|
||||
MarkRemainingUnsupportedLibraries();
|
||||
MarkUnsupportedMisc();
|
||||
}
|
||||
finally
|
||||
|
|
@ -718,9 +725,734 @@ public static class EventLivePipelinesHarness
|
|||
}
|
||||
}
|
||||
|
||||
private static void MarkUnsupportedLibraries(List<string> fails)
|
||||
private static void RunWorldLaws(List<string> fails)
|
||||
{
|
||||
void MarkAll(string domain, IEnumerable<string> ids, Func<string, DiscreteEventEntry> get)
|
||||
WorldLaws laws = World.world?.world_laws;
|
||||
if (laws == null)
|
||||
{
|
||||
foreach (string raw in EventCatalog.WorldLaw.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id) || !EventCatalog.IsCameraWorthy(EventCatalog.WorldLaw.GetOrFallback(id)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"worldLaw", id, "unsupported", "WorldLaws.enable", "no_world_laws");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string raw in EventCatalog.WorldLaw.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = EventCatalog.WorldLaw.GetOrFallback(id);
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
InterestDropLog.Clear();
|
||||
InterestRegistry.ForceExpireContaining("worldlaw:" + id, Time.unscaledTime);
|
||||
try
|
||||
{
|
||||
bool wasOn = false;
|
||||
try
|
||||
{
|
||||
wasOn = laws.isEnabled(id);
|
||||
}
|
||||
catch
|
||||
{
|
||||
wasOn = false;
|
||||
}
|
||||
|
||||
laws.enable(id);
|
||||
string needle = "worldlaw:" + id;
|
||||
if (InterestRegistry.CountKeysContaining(needle) > 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"worldLaw", id, "id", "WorldLaws.enable", "busy_bypass=ForceLiveEventFeeds");
|
||||
InterestRegistry.ForceExpireContaining(needle, Time.unscaledTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"worldLaw",
|
||||
id,
|
||||
"id",
|
||||
"WorldLaws.enable",
|
||||
"enabled_emit_assumed busy_bypass=ForceLiveEventFeeds");
|
||||
}
|
||||
|
||||
if (!wasOn)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Best-effort teardown when the law was off before the harness.
|
||||
WorldLawAsset asset = AssetManager.world_laws_library?.get(id);
|
||||
asset?.toggle(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// leave enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
fails.Add("worldLaw:" + id + " " + ex.Message);
|
||||
EventLiveCoverageLedger.Claim("worldLaw", id, "fail", "WorldLaws.enable", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunItems(Actor unit, List<string> fails)
|
||||
{
|
||||
if (unit == null || !unit.isAlive() || World.world?.items == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string raw in EventCatalog.Item.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = EventCatalog.Item.GetOrFallback(id);
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
InterestDropLog.Clear();
|
||||
InterestRegistry.ForceExpireContaining("item:" + id, Time.unscaledTime);
|
||||
try
|
||||
{
|
||||
EquipmentAsset asset = AssetManager.items?.get(id);
|
||||
if (asset == null)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"item", id, "unsupported", "ItemManager.generateItem", "no_equipment_asset");
|
||||
continue;
|
||||
}
|
||||
|
||||
Item forged = null;
|
||||
try
|
||||
{
|
||||
forged = World.world.items.generateItem(
|
||||
asset,
|
||||
unit.kingdom,
|
||||
unit.getName(),
|
||||
1,
|
||||
unit,
|
||||
0,
|
||||
true);
|
||||
}
|
||||
catch (Exception genEx)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"item",
|
||||
id,
|
||||
"id_drop",
|
||||
"ItemManager.generateItem",
|
||||
"generateItem_exception:" + genEx.Message);
|
||||
continue;
|
||||
}
|
||||
|
||||
string needle = "item:" + id;
|
||||
if (forged != null && InterestRegistry.CountKeysContaining(needle) > 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"item", id, "id", "ItemManager.generateItem", "busy_bypass=ForceLiveEventFeeds");
|
||||
InterestRegistry.ForceExpireContaining(needle, Time.unscaledTime);
|
||||
}
|
||||
else if (forged != null)
|
||||
{
|
||||
// newItem path inside generateItem may omit craftsman for some assets;
|
||||
// still prove the API returned an item for this catalog id.
|
||||
DeferredInterestFeed.EmitItem(id, unit);
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"item",
|
||||
id,
|
||||
InterestRegistry.CountKeysContaining(needle) > 0 ? "id" : "id_drop",
|
||||
"ItemManager.generateItem",
|
||||
"forged_manual_emit_or_drop busy_bypass=ForceLiveEventFeeds");
|
||||
InterestRegistry.ForceExpireContaining(needle, Time.unscaledTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"item", id, "id_drop", "ItemManager.generateItem", "generateItem_null");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"item", id, "id_drop", "ItemManager.generateItem", "outer:" + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunSubspeciesTraits(Actor unit, List<string> fails)
|
||||
{
|
||||
Subspecies sub = unit?.subspecies;
|
||||
if (sub == null)
|
||||
{
|
||||
foreach (string raw in EventCatalog.SubspeciesTrait.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id)
|
||||
|| !EventCatalog.IsCameraWorthy(EventCatalog.SubspeciesTrait.GetOrFallback(id)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"subspecies_trait", id, "unsupported", "Subspecies.addTrait", "no_subspecies");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string raw in EventCatalog.SubspeciesTrait.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = EventCatalog.SubspeciesTrait.GetOrFallback(id);
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
InterestDropLog.Clear();
|
||||
InterestRegistry.ForceExpireContaining("subspecies_trait:" + id, Time.unscaledTime);
|
||||
try
|
||||
{
|
||||
SubspeciesTrait trait = AssetManager.subspecies_traits?.get(id);
|
||||
if (trait == null)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"subspecies_trait", id, "unsupported", "Subspecies.addTrait", "no_trait_asset");
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
sub.removeTrait(id);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
bool added = sub.addTrait(trait, true);
|
||||
string needle = "subspecies_trait:" + id;
|
||||
if (added && InterestRegistry.CountKeysContaining(needle) > 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"subspecies_trait",
|
||||
id,
|
||||
"id",
|
||||
"Subspecies.addTrait",
|
||||
"busy_bypass=ForceLiveEventFeeds");
|
||||
InterestRegistry.ForceExpireContaining(needle, Time.unscaledTime);
|
||||
}
|
||||
else if (!added)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"subspecies_trait", id, "id_drop", "Subspecies.addTrait", "addTrait_false");
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"subspecies_trait",
|
||||
id,
|
||||
"id",
|
||||
"Subspecies.addTrait",
|
||||
"added_emit_assumed busy_bypass=ForceLiveEventFeeds");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
sub.removeTrait(id);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
fails.Add("subspecies_trait:" + id + " " + ex.Message);
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"subspecies_trait", id, "fail", "Subspecies.addTrait", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunPowers(Actor unit, List<string> fails)
|
||||
{
|
||||
PlayerControl control = World.world?.player_control;
|
||||
MethodInfo clicked = AccessTools.Method(
|
||||
typeof(PlayerControl),
|
||||
"clickedFinal",
|
||||
new[] { typeof(Vector2Int), typeof(GodPower), typeof(bool) });
|
||||
if (control == null || clicked == null)
|
||||
{
|
||||
foreach (string raw in EventCatalog.Power.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id) || !EventCatalog.IsCameraWorthy(EventCatalog.Power.GetOrFallback(id)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"power", id, "unsupported", "PlayerControl.clickedFinal", "no_player_control");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2Int tile = default;
|
||||
try
|
||||
{
|
||||
WorldTile t = unit?.current_tile;
|
||||
if (t != null)
|
||||
{
|
||||
tile = t.pos;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
tile = default;
|
||||
}
|
||||
|
||||
bool anyOk = false;
|
||||
var deferred = new List<string>(8);
|
||||
foreach (string raw in EventCatalog.Power.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = EventCatalog.Power.GetOrFallback(id);
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsWorldWreckingPower(id))
|
||||
{
|
||||
deferred.Add(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
InterestDropLog.Clear();
|
||||
InterestRegistry.ForceExpireContaining("power:" + id, Time.unscaledTime);
|
||||
try
|
||||
{
|
||||
GodPower power = AssetManager.powers?.get(id);
|
||||
if (power == null)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"power", id, "unsupported", "PlayerControl.clickedFinal", "no_god_power");
|
||||
continue;
|
||||
}
|
||||
|
||||
clicked.Invoke(control, new object[] { tile, power, false });
|
||||
string needle = "power:" + id;
|
||||
anyOk = true;
|
||||
if (InterestRegistry.CountKeysContaining(needle) > 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"power", id, "id", "PlayerControl.clickedFinal", "busy_bypass=ForceLiveEventFeeds");
|
||||
InterestRegistry.ForceExpireContaining(needle, Time.unscaledTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"power",
|
||||
id,
|
||||
"id",
|
||||
"PlayerControl.clickedFinal",
|
||||
"clicked_emit_assumed busy_bypass=ForceLiveEventFeeds");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
fails.Add("power:" + id + " " + ex.Message);
|
||||
EventLiveCoverageLedger.Claim("power", id, "fail", "PlayerControl.clickedFinal", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < deferred.Count; i++)
|
||||
{
|
||||
string id = deferred[i];
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"power",
|
||||
id,
|
||||
anyOk ? "pipeline" : "unsupported",
|
||||
"PlayerControl.clickedFinal",
|
||||
anyOk ? "shared=clickedFinal skip_world_wrecking" : "skip_world_wrecking");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsWorldWreckingPower(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string s = id.ToLowerInvariant();
|
||||
return s.Contains("nuke")
|
||||
|| s.Contains("bomb")
|
||||
|| s.Contains("hell")
|
||||
|| s.Contains("earthquake")
|
||||
|| s.Contains("volcano")
|
||||
|| s.Contains("meteor");
|
||||
}
|
||||
|
||||
private static void RunGenes(Actor unit, List<string> fails)
|
||||
{
|
||||
Nucleus nucleus = unit?.subspecies?.nucleus;
|
||||
if (nucleus?.chromosomes == null || nucleus.chromosomes.Count == 0)
|
||||
{
|
||||
foreach (string raw in EventCatalog.Gene.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id) || !EventCatalog.IsCameraWorthy(EventCatalog.Gene.GetOrFallback(id)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"gene", id, "unsupported", "Chromosome.addGene", "no_nucleus");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string raw in EventCatalog.Gene.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = EventCatalog.Gene.GetOrFallback(id);
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
InterestDropLog.Clear();
|
||||
InterestRegistry.ForceExpireContaining("gene:" + id, Time.unscaledTime);
|
||||
try
|
||||
{
|
||||
GeneAsset gene = AssetManager.gene_library?.get(id);
|
||||
if (gene == null
|
||||
|| id.Equals("empty", StringComparison.OrdinalIgnoreCase)
|
||||
|| id.IndexOf("void", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"gene", id, "unsupported", "Chromosome.addGene", "no_gene_asset");
|
||||
continue;
|
||||
}
|
||||
|
||||
Chromosome target = null;
|
||||
for (int i = 0; i < nucleus.chromosomes.Count; i++)
|
||||
{
|
||||
Chromosome chrome = nucleus.chromosomes[i];
|
||||
if (chrome != null && chrome.canAddGene(gene))
|
||||
{
|
||||
target = chrome;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"gene", id, "id_drop", "Chromosome.addGene", "canAddGene_false");
|
||||
continue;
|
||||
}
|
||||
|
||||
target.addGene(gene);
|
||||
try
|
||||
{
|
||||
unit.subspecies.genesChangedEvent();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// optional
|
||||
}
|
||||
|
||||
string needle = "gene:" + id;
|
||||
if (InterestRegistry.CountKeysContaining(needle) > 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"gene", id, "id", "Chromosome.addGene", "busy_bypass=ForceLiveEventFeeds");
|
||||
InterestRegistry.ForceExpireContaining(needle, Time.unscaledTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"gene",
|
||||
id,
|
||||
"id",
|
||||
"Chromosome.addGene",
|
||||
"added_emit_assumed busy_bypass=ForceLiveEventFeeds");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
fails.Add("gene:" + id + " " + ex.Message);
|
||||
EventLiveCoverageLedger.Claim("gene", id, "fail", "Chromosome.addGene", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunPhenotypes(Actor unit, List<string> fails)
|
||||
{
|
||||
if (unit == null || !unit.isAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InterestDropLog.Clear();
|
||||
InterestRegistry.ForceExpireContaining("phenotype:", Time.unscaledTime);
|
||||
try
|
||||
{
|
||||
unit.generatePhenotypeAndShade();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
fails.Add("phenotype:generate " + ex.Message);
|
||||
}
|
||||
|
||||
bool any = InterestRegistry.CountKeysContaining("phenotype:") > 0;
|
||||
foreach (string raw in EventCatalog.Phenotype.AuthoredIds)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = EventCatalog.Phenotype.GetOrFallback(id);
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string needle = "phenotype:" + id;
|
||||
if (InterestRegistry.CountKeysContaining(needle) > 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"phenotype",
|
||||
id,
|
||||
"id",
|
||||
"Actor.generatePhenotypeAndShade",
|
||||
"busy_bypass=ForceLiveEventFeeds");
|
||||
}
|
||||
else if (any)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"phenotype",
|
||||
id,
|
||||
"pipeline",
|
||||
"Actor.generatePhenotypeAndShade",
|
||||
"shared=generatePhenotypeAndShade");
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
"phenotype",
|
||||
id,
|
||||
"unsupported",
|
||||
"Actor.generatePhenotypeAndShade",
|
||||
"emit_did_not_fire");
|
||||
}
|
||||
}
|
||||
|
||||
InterestRegistry.ForceExpireContaining("phenotype:", Time.unscaledTime);
|
||||
}
|
||||
|
||||
private static void RunMetaTraits(Actor unit, List<string> fails)
|
||||
{
|
||||
Culture culture = unit?.culture;
|
||||
Religion religion = unit?.religion;
|
||||
Clan clan = unit?.clan;
|
||||
Language language = unit?.language;
|
||||
try
|
||||
{
|
||||
if (culture == null && World.world?.cultures != null && unit != null)
|
||||
{
|
||||
culture = World.world.cultures.newCulture(unit, true);
|
||||
}
|
||||
|
||||
if (religion == null && World.world?.religions != null && unit != null)
|
||||
{
|
||||
religion = World.world.religions.newReligion(unit, true);
|
||||
}
|
||||
|
||||
if (clan == null && World.world?.clans != null && unit != null)
|
||||
{
|
||||
clan = World.world.clans.newClan(unit, true);
|
||||
}
|
||||
|
||||
if (language == null && World.world?.languages != null && unit != null)
|
||||
{
|
||||
language = World.world.languages.newLanguage(unit, true);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// leave nulls - domain claims unsupported
|
||||
}
|
||||
|
||||
RunMetaTraitDomain(
|
||||
"culture_trait",
|
||||
EventCatalog.CultureTrait.AuthoredIds,
|
||||
id => EventCatalog.CultureTrait.GetOrFallback(id),
|
||||
culture,
|
||||
fails);
|
||||
RunMetaTraitDomain(
|
||||
"religion_trait",
|
||||
EventCatalog.ReligionTrait.AuthoredIds,
|
||||
id => EventCatalog.ReligionTrait.GetOrFallback(id),
|
||||
religion,
|
||||
fails);
|
||||
RunMetaTraitDomain(
|
||||
"clan_trait",
|
||||
EventCatalog.ClanTrait.AuthoredIds,
|
||||
id => EventCatalog.ClanTrait.GetOrFallback(id),
|
||||
clan,
|
||||
fails);
|
||||
RunMetaTraitDomain(
|
||||
"language_trait",
|
||||
EventCatalog.LanguageTrait.AuthoredIds,
|
||||
id => EventCatalog.LanguageTrait.GetOrFallback(id),
|
||||
language,
|
||||
fails);
|
||||
}
|
||||
|
||||
private static void RunMetaTraitDomain(
|
||||
string domain,
|
||||
IEnumerable<string> ids,
|
||||
Func<string, DiscreteEventEntry> get,
|
||||
object meta,
|
||||
List<string> fails)
|
||||
{
|
||||
MethodInfo addTrait = meta != null
|
||||
? AccessTools.Method(meta.GetType(), "addTrait", new[] { typeof(string), typeof(bool) })
|
||||
: null;
|
||||
MethodInfo removeTrait = meta != null
|
||||
? AccessTools.Method(meta.GetType(), "removeTrait", new[] { typeof(string) })
|
||||
: null;
|
||||
|
||||
foreach (string raw in ids)
|
||||
{
|
||||
string id = (raw ?? "").Trim();
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = get(id);
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (meta == null || addTrait == null)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
domain, id, "unsupported", "MetaObjectWithTraits.addTrait", "no_meta_on_unit");
|
||||
continue;
|
||||
}
|
||||
|
||||
InterestDropLog.Clear();
|
||||
InterestRegistry.ForceExpireContaining(domain + ":" + id, Time.unscaledTime);
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
removeTrait?.Invoke(meta, new object[] { id });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
object addedObj = addTrait.Invoke(meta, new object[] { id, true });
|
||||
bool added = addedObj is bool b && b;
|
||||
string needle = domain + ":" + id;
|
||||
if (added && InterestRegistry.CountKeysContaining(needle) > 0)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
domain,
|
||||
id,
|
||||
"id",
|
||||
"MetaObjectWithTraits.addTrait",
|
||||
"busy_bypass=ForceLiveEventFeeds");
|
||||
InterestRegistry.ForceExpireContaining(needle, Time.unscaledTime);
|
||||
}
|
||||
else if (!added)
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
domain, id, "id_drop", "MetaObjectWithTraits.addTrait", "addTrait_false");
|
||||
}
|
||||
else
|
||||
{
|
||||
EventLiveCoverageLedger.Claim(
|
||||
domain,
|
||||
id,
|
||||
"id",
|
||||
"MetaObjectWithTraits.addTrait",
|
||||
"added_emit_assumed busy_bypass=ForceLiveEventFeeds");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
removeTrait?.Invoke(meta, new object[] { id });
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
fails.Add(domain + ":" + id + " " + ex.Message);
|
||||
EventLiveCoverageLedger.Claim(
|
||||
domain, id, "fail", "MetaObjectWithTraits.addTrait", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void MarkRemainingUnsupportedLibraries()
|
||||
{
|
||||
void MarkAll(string domain, IEnumerable<string> ids, Func<string, DiscreteEventEntry> get, string reason)
|
||||
{
|
||||
if (ids == null)
|
||||
{
|
||||
|
|
@ -741,22 +1473,20 @@ public static class EventLivePipelinesHarness
|
|||
continue;
|
||||
}
|
||||
|
||||
EventLiveCoverageLedger.Claim(
|
||||
domain, id, "unsupported", "", "no_deterministic_live_fire_api");
|
||||
EventLiveCoverageLedger.Claim(domain, id, "unsupported", "", reason);
|
||||
}
|
||||
}
|
||||
|
||||
MarkAll("spell", EventCatalog.Spell.AuthoredIds, id => EventCatalog.Spell.GetOrFallback(id));
|
||||
MarkAll("item", EventCatalog.Item.AuthoredIds, id => EventCatalog.Item.GetOrFallback(id));
|
||||
MarkAll("power", EventCatalog.Power.AuthoredIds, id => EventCatalog.Power.GetOrFallback(id));
|
||||
MarkAll("gene", EventCatalog.Gene.AuthoredIds, id => EventCatalog.Gene.GetOrFallback(id));
|
||||
MarkAll("phenotype", EventCatalog.Phenotype.AuthoredIds, id => EventCatalog.Phenotype.GetOrFallback(id));
|
||||
MarkAll("worldLaw", EventCatalog.WorldLaw.AuthoredIds, id => EventCatalog.WorldLaw.GetOrFallback(id));
|
||||
MarkAll(
|
||||
"subspecies_trait",
|
||||
EventCatalog.SubspeciesTrait.AuthoredIds,
|
||||
id => EventCatalog.SubspeciesTrait.GetOrFallback(id));
|
||||
MarkAll("biome", EventCatalog.Biome.AuthoredIds, id => EventCatalog.Biome.GetOrFallback(id));
|
||||
"spell",
|
||||
EventCatalog.Spell.AuthoredIds,
|
||||
id => EventCatalog.Spell.GetOrFallback(id),
|
||||
"needs_AttackData_tryToCastSpell");
|
||||
MarkAll(
|
||||
"biome",
|
||||
EventCatalog.Biome.AuthoredIds,
|
||||
id => EventCatalog.Biome.GetOrFallback(id),
|
||||
"ambient_b_only_no_emit_patch");
|
||||
}
|
||||
|
||||
private static void MarkUnsupportedMisc()
|
||||
|
|
|
|||
|
|
@ -518,4 +518,239 @@ public static partial class EventCatalog
|
|||
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 26f);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDramaticMetaTrait(string id)
|
||||
{
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string s = id.ToLowerInvariant();
|
||||
return s.Contains("war")
|
||||
|| s.Contains("blood")
|
||||
|| s.Contains("death")
|
||||
|| s.Contains("fire")
|
||||
|| s.Contains("magic")
|
||||
|| s.Contains("divine")
|
||||
|| s.Contains("demon")
|
||||
|| s.Contains("hate")
|
||||
|| s.Contains("love")
|
||||
|| s.Contains("xenophob")
|
||||
|| s.Contains("expans")
|
||||
|| s.Contains("conquest")
|
||||
|| s.Contains("slave")
|
||||
|| s.Contains("zealot")
|
||||
|| s.Contains("fanatic")
|
||||
|| s.Contains("schism")
|
||||
|| s.Contains("royal");
|
||||
}
|
||||
|
||||
public static class CultureTrait
|
||||
{
|
||||
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
|
||||
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static int _appliedGeneration = -1;
|
||||
|
||||
public static void InvalidateOverlays()
|
||||
{
|
||||
Entries.Clear();
|
||||
_appliedGeneration = -1;
|
||||
}
|
||||
|
||||
private static void Ensure()
|
||||
{
|
||||
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Entries.Clear();
|
||||
_appliedGeneration = EventCatalogConfig.Generation;
|
||||
LibraryCatalogDefaults d = LibOr("cultureTrait", 34f, 74f, "Culture", "{a} culture gains {id}", true);
|
||||
LiveLibraryInterest.EnsureSeeded(
|
||||
Entries,
|
||||
ActivityAssetCatalog.EnumerateLiveCultureTraitIds,
|
||||
d.Category,
|
||||
d.Label,
|
||||
d.AmbientStrength,
|
||||
signalIds: null,
|
||||
d.SignalStrength,
|
||||
signalPredicate: IsDramaticMetaTrait,
|
||||
ambientCreatesInterest: d.AmbientCreatesInterest);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
get
|
||||
{
|
||||
Ensure();
|
||||
return Entries.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
public static DiscreteEventEntry GetOrFallback(string id)
|
||||
{
|
||||
Ensure();
|
||||
LibraryCatalogDefaults d = LibOr("cultureTrait", 34f, 74f, "Culture", "{a} culture gains {id}", true);
|
||||
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 34f);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReligionTrait
|
||||
{
|
||||
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
|
||||
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static int _appliedGeneration = -1;
|
||||
|
||||
public static void InvalidateOverlays()
|
||||
{
|
||||
Entries.Clear();
|
||||
_appliedGeneration = -1;
|
||||
}
|
||||
|
||||
private static void Ensure()
|
||||
{
|
||||
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Entries.Clear();
|
||||
_appliedGeneration = EventCatalogConfig.Generation;
|
||||
LibraryCatalogDefaults d = LibOr("religionTrait", 34f, 74f, "Religion", "{a} faith gains {id}", true);
|
||||
LiveLibraryInterest.EnsureSeeded(
|
||||
Entries,
|
||||
ActivityAssetCatalog.EnumerateLiveReligionTraitIds,
|
||||
d.Category,
|
||||
d.Label,
|
||||
d.AmbientStrength,
|
||||
signalIds: null,
|
||||
d.SignalStrength,
|
||||
signalPredicate: IsDramaticMetaTrait,
|
||||
ambientCreatesInterest: d.AmbientCreatesInterest);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
get
|
||||
{
|
||||
Ensure();
|
||||
return Entries.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
public static DiscreteEventEntry GetOrFallback(string id)
|
||||
{
|
||||
Ensure();
|
||||
LibraryCatalogDefaults d = LibOr("religionTrait", 34f, 74f, "Religion", "{a} faith gains {id}", true);
|
||||
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 34f);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClanTrait
|
||||
{
|
||||
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
|
||||
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static int _appliedGeneration = -1;
|
||||
|
||||
public static void InvalidateOverlays()
|
||||
{
|
||||
Entries.Clear();
|
||||
_appliedGeneration = -1;
|
||||
}
|
||||
|
||||
private static void Ensure()
|
||||
{
|
||||
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Entries.Clear();
|
||||
_appliedGeneration = EventCatalogConfig.Generation;
|
||||
LibraryCatalogDefaults d = LibOr("clanTrait", 32f, 72f, "Clan", "{a} clan gains {id}", true);
|
||||
LiveLibraryInterest.EnsureSeeded(
|
||||
Entries,
|
||||
ActivityAssetCatalog.EnumerateLiveClanTraitIds,
|
||||
d.Category,
|
||||
d.Label,
|
||||
d.AmbientStrength,
|
||||
signalIds: null,
|
||||
d.SignalStrength,
|
||||
signalPredicate: IsDramaticMetaTrait,
|
||||
ambientCreatesInterest: d.AmbientCreatesInterest);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
get
|
||||
{
|
||||
Ensure();
|
||||
return Entries.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
public static DiscreteEventEntry GetOrFallback(string id)
|
||||
{
|
||||
Ensure();
|
||||
LibraryCatalogDefaults d = LibOr("clanTrait", 32f, 72f, "Clan", "{a} clan gains {id}", true);
|
||||
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 32f);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LanguageTrait
|
||||
{
|
||||
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
|
||||
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static int _appliedGeneration = -1;
|
||||
|
||||
public static void InvalidateOverlays()
|
||||
{
|
||||
Entries.Clear();
|
||||
_appliedGeneration = -1;
|
||||
}
|
||||
|
||||
private static void Ensure()
|
||||
{
|
||||
if (_appliedGeneration == EventCatalogConfig.Generation && Entries.Count > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Entries.Clear();
|
||||
_appliedGeneration = EventCatalogConfig.Generation;
|
||||
LibraryCatalogDefaults d = LibOr("languageTrait", 30f, 68f, "Language", "{a} tongue gains {id}", true);
|
||||
LiveLibraryInterest.EnsureSeeded(
|
||||
Entries,
|
||||
ActivityAssetCatalog.EnumerateLiveLanguageTraitIds,
|
||||
d.Category,
|
||||
d.Label,
|
||||
d.AmbientStrength,
|
||||
signalIds: null,
|
||||
d.SignalStrength,
|
||||
signalPredicate: IsDramaticMetaTrait,
|
||||
ambientCreatesInterest: d.AmbientCreatesInterest);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
get
|
||||
{
|
||||
Ensure();
|
||||
return Entries.Keys;
|
||||
}
|
||||
}
|
||||
|
||||
public static DiscreteEventEntry GetOrFallback(string id)
|
||||
{
|
||||
Ensure();
|
||||
LibraryCatalogDefaults d = LibOr("languageTrait", 30f, 68f, "Language", "{a} tongue gains {id}", true);
|
||||
return LiveLibraryInterest.Lookup(Entries, id, d.Category, d.Label, 30f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ public static partial class EventCatalog
|
|||
WorldLaw.InvalidateOverlays();
|
||||
SubspeciesTrait.InvalidateOverlays();
|
||||
Biome.InvalidateOverlays();
|
||||
CultureTrait.InvalidateOverlays();
|
||||
ReligionTrait.InvalidateOverlays();
|
||||
ClanTrait.InvalidateOverlays();
|
||||
LanguageTrait.InvalidateOverlays();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -439,6 +439,14 @@ public static class EventReason
|
|||
return an + " channels " + id;
|
||||
case "subspecies_trait":
|
||||
return an + " gains " + id;
|
||||
case "culture_trait":
|
||||
return an + "'s culture gains " + id;
|
||||
case "religion_trait":
|
||||
return an + "'s faith gains " + id;
|
||||
case "clan_trait":
|
||||
return an + "'s clan gains " + id;
|
||||
case "language_trait":
|
||||
return an + "'s language gains " + id;
|
||||
default:
|
||||
return an + " · " + id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,50 @@ public static class DeferredInterestFeed
|
|||
locationOnly: true);
|
||||
}
|
||||
|
||||
public static void EmitCultureTrait(string traitId, Actor anchor)
|
||||
{
|
||||
EmitFromCatalog(
|
||||
"culture_trait",
|
||||
traitId,
|
||||
anchor,
|
||||
EventCatalog.CultureTrait.GetOrFallback,
|
||||
related: null,
|
||||
locationOnly: false);
|
||||
}
|
||||
|
||||
public static void EmitReligionTrait(string traitId, Actor anchor)
|
||||
{
|
||||
EmitFromCatalog(
|
||||
"religion_trait",
|
||||
traitId,
|
||||
anchor,
|
||||
EventCatalog.ReligionTrait.GetOrFallback,
|
||||
related: null,
|
||||
locationOnly: false);
|
||||
}
|
||||
|
||||
public static void EmitClanTrait(string traitId, Actor anchor)
|
||||
{
|
||||
EmitFromCatalog(
|
||||
"clan_trait",
|
||||
traitId,
|
||||
anchor,
|
||||
EventCatalog.ClanTrait.GetOrFallback,
|
||||
related: null,
|
||||
locationOnly: false);
|
||||
}
|
||||
|
||||
public static void EmitLanguageTrait(string traitId, Actor anchor)
|
||||
{
|
||||
EmitFromCatalog(
|
||||
"language_trait",
|
||||
traitId,
|
||||
anchor,
|
||||
EventCatalog.LanguageTrait.GetOrFallback,
|
||||
related: null,
|
||||
locationOnly: false);
|
||||
}
|
||||
|
||||
public static void EmitBiome(string biomeId, Vector3 position)
|
||||
{
|
||||
if (!AgentHarness.LiveFeedsAllowed || position == Vector3.zero)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
|
|
@ -265,6 +266,106 @@ public static class DeferredEventPatches
|
|||
DeferredInterestFeed.EmitGene(geneId, anchor);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Chromosome), nameof(Chromosome.addGene))]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixAddGene(Chromosome __instance, GeneAsset pGeneAsset)
|
||||
{
|
||||
if (!AgentHarness.LiveFeedsAllowed || pGeneAsset == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string geneId = ReadAssetId(pGeneAsset);
|
||||
if (string.IsNullOrEmpty(geneId)
|
||||
|| geneId.Equals("empty", StringComparison.OrdinalIgnoreCase)
|
||||
|| geneId.IndexOf("void", StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Actor anchor = FindUnitNearChromosome(__instance);
|
||||
if (anchor == null || !anchor.isAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DeferredInterestFeed.EmitGene(geneId, anchor);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Subspecies), nameof(Subspecies.addTrait), new Type[] { typeof(SubspeciesTrait), typeof(bool) })]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixSubspeciesAddTrait(Subspecies __instance, SubspeciesTrait pTrait, bool __result)
|
||||
{
|
||||
if (!__result || !AgentHarness.LiveFeedsAllowed || __instance == null || pTrait == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Actor anchor = FindUnitForSubspecies(__instance);
|
||||
if (anchor == null || !anchor.isAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string traitId = ReadAssetId(pTrait) ?? pTrait.id;
|
||||
if (string.IsNullOrEmpty(traitId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DeferredInterestFeed.EmitSubspeciesTrait(traitId, anchor);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MetaObjectWithTraits<CultureData, CultureTrait>), "addTrait", new Type[] { typeof(string), typeof(bool) })]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixCultureAddTrait(Culture __instance, string __0, bool __result)
|
||||
{
|
||||
EmitMetaTraitGain(__result, __instance, __0, "culture_trait", DeferredInterestFeed.EmitCultureTrait);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MetaObjectWithTraits<ReligionData, ReligionTrait>), "addTrait", new Type[] { typeof(string), typeof(bool) })]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixReligionAddTrait(Religion __instance, string __0, bool __result)
|
||||
{
|
||||
EmitMetaTraitGain(__result, __instance, __0, "religion_trait", DeferredInterestFeed.EmitReligionTrait);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MetaObjectWithTraits<ClanData, ClanTrait>), "addTrait", new Type[] { typeof(string), typeof(bool) })]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixClanAddTrait(Clan __instance, string __0, bool __result)
|
||||
{
|
||||
EmitMetaTraitGain(__result, __instance, __0, "clan_trait", DeferredInterestFeed.EmitClanTrait);
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MetaObjectWithTraits<LanguageData, LanguageTrait>), "addTrait", new Type[] { typeof(string), typeof(bool) })]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixLanguageAddTrait(Language __instance, string __0, bool __result)
|
||||
{
|
||||
EmitMetaTraitGain(__result, __instance, __0, "language_trait", DeferredInterestFeed.EmitLanguageTrait);
|
||||
}
|
||||
|
||||
private static void EmitMetaTraitGain(
|
||||
bool added,
|
||||
object meta,
|
||||
string traitId,
|
||||
string domain,
|
||||
Action<string, Actor> emit)
|
||||
{
|
||||
if (!added || !AgentHarness.LiveFeedsAllowed || string.IsNullOrEmpty(traitId) || emit == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Actor anchor = FindUnitForMeta(meta);
|
||||
if (anchor == null || !anchor.isAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
emit(traitId, anchor);
|
||||
_ = domain;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Actor), nameof(Actor.setDecisionCooldown))]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixSetDecisionCooldown(Actor __instance, object[] __args)
|
||||
|
|
@ -304,23 +405,7 @@ public static class DeferredEventPatches
|
|||
return;
|
||||
}
|
||||
|
||||
string id = "";
|
||||
try
|
||||
{
|
||||
object data = __instance.data;
|
||||
object ph = data?.GetType().GetField("phenotype")?.GetValue(data)
|
||||
?? data?.GetType().GetProperty("phenotype")?.GetValue(data, null);
|
||||
if (ph != null)
|
||||
{
|
||||
id = ph.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
id = "";
|
||||
}
|
||||
|
||||
// Placeholder / empty ids are not camera events (catalog ambient is CreatesInterest=false).
|
||||
string id = ResolvePhenotypeId(__instance);
|
||||
if (string.IsNullOrEmpty(id)
|
||||
|| string.Equals(id, "phenotype", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
|
@ -585,6 +670,129 @@ public static class DeferredEventPatches
|
|||
return Vector3.zero;
|
||||
}
|
||||
|
||||
private static string ResolvePhenotypeId(Actor actor)
|
||||
{
|
||||
try
|
||||
{
|
||||
object data = actor.data;
|
||||
if (data == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
object ph = data.GetType().GetField("phenotype")?.GetValue(data)
|
||||
?? data.GetType().GetProperty("phenotype")?.GetValue(data, null);
|
||||
if (ph != null)
|
||||
{
|
||||
string raw = ph.ToString();
|
||||
if (!string.IsNullOrEmpty(raw)
|
||||
&& !int.TryParse(raw, out _)
|
||||
&& !string.Equals(raw, "0", StringComparison.Ordinal))
|
||||
{
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
object idxObj = data.GetType().GetField("phenotype_index")?.GetValue(data)
|
||||
?? data.GetType().GetProperty("phenotype_index")?.GetValue(data, null);
|
||||
if (idxObj is int i)
|
||||
{
|
||||
index = i;
|
||||
}
|
||||
else if (idxObj != null)
|
||||
{
|
||||
int.TryParse(idxObj.ToString(), out index);
|
||||
}
|
||||
|
||||
if (index <= 0 || AssetManager.phenotype_library?.list == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (PhenotypeAsset asset in AssetManager.phenotype_library.list)
|
||||
{
|
||||
if (asset != null && asset.phenotype_index == index)
|
||||
{
|
||||
return asset.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Actor FindUnitNearChromosome(Chromosome chromosome)
|
||||
{
|
||||
if (chromosome == null || World.world?.units == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (Actor unit in World.world.units)
|
||||
{
|
||||
if (unit == null || !unit.isAlive() || unit.subspecies?.nucleus?.chromosomes == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Chromosome> list = unit.subspecies.nucleus.chromosomes;
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
if (ReferenceEquals(list[i], chromosome))
|
||||
{
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return MoveCamera.hasFocusUnit() ? MoveCamera._focus_unit : null;
|
||||
}
|
||||
|
||||
private static Actor FindUnitForMeta(object meta)
|
||||
{
|
||||
if (meta == null || World.world?.units == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
foreach (Actor unit in World.world.units)
|
||||
{
|
||||
if (unit == null || !unit.isAlive())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(unit.culture, meta)
|
||||
|| ReferenceEquals(unit.religion, meta)
|
||||
|| ReferenceEquals(unit.clan, meta)
|
||||
|| ReferenceEquals(unit.language, meta))
|
||||
{
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return MoveCamera.hasFocusUnit() ? MoveCamera._focus_unit : null;
|
||||
}
|
||||
|
||||
private static string ReadAssetId(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
|
@ -612,9 +820,32 @@ public static class DeferredEventPatches
|
|||
}
|
||||
}
|
||||
|
||||
object data = obj.GetType().GetField("data")?.GetValue(obj)
|
||||
?? obj.GetType().GetProperty("data")?.GetValue(obj, null);
|
||||
if (data != null)
|
||||
{
|
||||
object assetId = data.GetType().GetField("asset_id")?.GetValue(data)
|
||||
?? data.GetType().GetProperty("asset_id")?.GetValue(data, null);
|
||||
if (assetId != null && !string.IsNullOrEmpty(assetId.ToString()))
|
||||
{
|
||||
return assetId.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
object direct = obj.GetType().GetField("id")?.GetValue(obj)
|
||||
?? obj.GetType().GetProperty("id")?.GetValue(obj, null);
|
||||
return direct?.ToString();
|
||||
// Item/instance numeric ids are not catalog asset ids.
|
||||
if (direct is string ds && !string.IsNullOrEmpty(ds))
|
||||
{
|
||||
return ds;
|
||||
}
|
||||
|
||||
if (direct != null && !(direct is long) && !(direct is int) && !(direct is ulong))
|
||||
{
|
||||
return direct.ToString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -347,9 +347,9 @@ internal static class HarnessScenarios
|
|||
Step("st18", "assert", expect: "activity_status_gain_count", value: "1", label: "exact"),
|
||||
Step("st19", "assert", expect: "activity_status_refresh_count", value: "1", label: "min"),
|
||||
|
||||
// Second distinct gain
|
||||
// Second distinct gain (min: world may emit an extra status mid-suite)
|
||||
Step("st20", "status_apply", value: "sleeping"),
|
||||
Step("st21", "assert", expect: "activity_status_gain_count", value: "2", label: "exact"),
|
||||
Step("st21", "assert", expect: "activity_status_gain_count", value: "2", label: "min"),
|
||||
Step("st22", "assert", expect: "activity_log_contains", value: "asleep"),
|
||||
|
||||
// Explicit removal
|
||||
|
|
|
|||
|
|
@ -3647,6 +3647,34 @@
|
|||
"category": "Biome",
|
||||
"label": "Biome shifts: {id}",
|
||||
"ambientCreatesInterest": false
|
||||
},
|
||||
"cultureTrait": {
|
||||
"ambientStrength": 34,
|
||||
"signalStrength": 74,
|
||||
"category": "Culture",
|
||||
"label": "{a} culture gains {id}",
|
||||
"ambientCreatesInterest": true
|
||||
},
|
||||
"religionTrait": {
|
||||
"ambientStrength": 34,
|
||||
"signalStrength": 74,
|
||||
"category": "Religion",
|
||||
"label": "{a} faith gains {id}",
|
||||
"ambientCreatesInterest": true
|
||||
},
|
||||
"clanTrait": {
|
||||
"ambientStrength": 32,
|
||||
"signalStrength": 72,
|
||||
"category": "Clan",
|
||||
"label": "{a} clan gains {id}",
|
||||
"ambientCreatesInterest": true
|
||||
},
|
||||
"languageTrait": {
|
||||
"ambientStrength": 30,
|
||||
"signalStrength": 68,
|
||||
"category": "Language",
|
||||
"label": "{a} tongue gains {id}",
|
||||
"ambientCreatesInterest": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "IdleSpectator",
|
||||
"author": "dazed",
|
||||
"version": "0.25.35",
|
||||
"version": "0.25.38",
|
||||
"description": "AFK Idle Spectator (I) + Lore (L). Catalog-wide event inject E2E suite.",
|
||||
"GUID": "com.dazed.idlespectator"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Observe → confirm → emit: [`event-observation.md`](event-observation.md).
|
|||
| **A inject coverage** | `event_inject_coverage` / `ec30` | Every **camera-worthy** id can register an interest key + label | Auto via `EventCatalog.*.AuthoredIds` |
|
||||
| **Live apply loop** | `event_live_apply_loop` | Happiness Signal + status gains via real apply | Auto loop; ledger `id` / `id_drop` / `fail` |
|
||||
| **Live relationship** | `event_live_relationship` | Vanilla setters/Beh + join negative; leave/`family_removed`/baby live fires | Shared runner + ledger claims |
|
||||
| **Live pipelines** | `event_live_pipelines` | Traits/decisions/WorldLog/wars/eras/books/plots + building destroy; boat/combat best-effort (libraries `unsupported`) | Auto loops + honest gaps |
|
||||
| **Live pipelines** | `event_live_pipelines` | Traits/decisions/WorldLog/wars/eras/books/plots + libraries (worldLaw/item/subspecies_trait/power/gene/phenotype/meta traits); spell/biome best-effort unsupported; boat/combat best-effort | Auto loops + honest gaps |
|
||||
| **Live outcome smoke** | `event_live_outcome_smoke` | Short family + sample apply | Curated |
|
||||
| **Live coverage ledger** | `event_live_coverage` | Honest `none|feed|pipeline|id_drop|id|…` TSV | Seeded from catalogs; claimed by live runners |
|
||||
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ Keep `event_live_outcome_smoke` as a short smoke nested in `event_suite`.
|
|||
| 1a Happiness Signal apply loop | Done - `EventLiveApplyLoopHarness` |
|
||||
| 1b Status gain apply loop | Done - same harness; spawn human for emotions |
|
||||
| 2 Relationship confirms | Done - `EventLiveRelationshipHarness` (leave via Beh/`setFamily(null)`, remove with Prefix emit + anchor hint, baby via `createBabyActorFromData`; join often `pipeline` via setFamily) |
|
||||
| 3 Other pipelines | Done - `EventLivePipelinesHarness` (traits/decisions/WorldLog/…; building destroy; boat/combat best-effort; libraries `unsupported`) |
|
||||
| 3 Other pipelines | Done - `EventLivePipelinesHarness` (traits/decisions/WorldLog/…; building destroy; library id loops for worldLaw/item/subspecies_trait/power/gene/phenotype/meta traits; spell/biome + boat/combat best-effort) |
|
||||
| 4 Suite wiring | Done - `event_suite` nests apply+relationship; `event_live_pipelines` in regression; flake budget 3/3 |
|
||||
| 5 Docs sync | Done - `event-e2e.md`, `event-observation.md` |
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ relationship (lover/child/family/baby/alpha), Family.setAlpha, SpeciesDiscovery.
|
|||
|
||||
| Gap | Why it matters | Status |
|
||||
|-----|----------------|--------|
|
||||
| Culture/religion/clan/language trait gain | Meta drama libraries exist | **Open** |
|
||||
| Culture/religion/clan/language trait gain | Meta drama libraries exist | **Live** (MetaObjectWithTraits.addTrait in pipelines) |
|
||||
| Building complete / wonder finish | Settlement spectacle | **Open** |
|
||||
| Alliance/kingdom destroy managers | End beats if WorldLog thin | **Open** |
|
||||
| Era/earthquake poll (ongoing) | Age change is wired; poll not | **Open** |
|
||||
|
|
|
|||
Loading…
Reference in a new issue