Refine event handling in IdleSpectator by introducing new scoring logic for species rarity bonuses and enhancing event catalog entries for relationship and status events. Update presentation tiers for happiness events to ensure accurate representation in gameplay. Increment version in mod.json to 0.24.0 to reflect these changes.
This commit is contained in:
parent
2c1f80cdb4
commit
f4be23e23d
26 changed files with 513 additions and 179 deletions
|
|
@ -211,11 +211,11 @@ public static class ActivityHappinessHarness
|
|||
mustContain: new[] { "hatches from an egg" },
|
||||
mustNotContain: Array.Empty<string>());
|
||||
|
||||
// Camera demotion: ticker crumbs must stay Ambient (Layer B only).
|
||||
// Camera demotion: only true FX / bookkeeping stay Ambient (Layer B).
|
||||
// Interesting life/social beats are Signal; director ranks by EventStrength.
|
||||
string[] ambientRequired =
|
||||
{
|
||||
"just_received_gift", "just_gave_gift", "just_cried", "just_inspired",
|
||||
"got_robbed", "lost_fight", "just_injured", "just_made_friend"
|
||||
"got_poked", "got_caught", "paid_tax", "just_ate", "just_pooped"
|
||||
};
|
||||
for (int i = 0; i < ambientRequired.Length; i++)
|
||||
{
|
||||
|
|
@ -226,6 +226,20 @@ public static class ActivityHappinessHarness
|
|||
}
|
||||
}
|
||||
|
||||
string[] signalRequired =
|
||||
{
|
||||
"just_made_friend", "just_injured", "just_found_house", "got_robbed",
|
||||
"fallen_in_love", "become_alpha", "just_had_child"
|
||||
};
|
||||
for (int i = 0; i < signalRequired.Length; i++)
|
||||
{
|
||||
HappinessCatalogEntry e = EventCatalog.Happiness.GetOrFallback(signalRequired[i]);
|
||||
if (e.Presentation != HappinessPresentationTier.Signal)
|
||||
{
|
||||
proseRegressions++;
|
||||
}
|
||||
}
|
||||
|
||||
bool passed = missingAuthored == 0
|
||||
&& missingIcon == 0
|
||||
&& localeLeaks == 0
|
||||
|
|
|
|||
|
|
@ -185,10 +185,12 @@ public static class ActivityStatusHarness
|
|||
}
|
||||
|
||||
int cameraDemotionFailures = 0;
|
||||
// Brief FX / cooldowns must stay B. Story-readable statuses must stay A.
|
||||
string[] bOnlyStatus =
|
||||
{
|
||||
"surprised", "confused", "motivated", "inspired", "magnetized",
|
||||
"starving", "ash_fever", "powerup", "voices_in_my_head", "angry"
|
||||
"afterglow", "cough", "dash", "dodge", "flicked", "just_ate", "on_guard",
|
||||
"recovery_combat_action", "recovery_plot", "recovery_social", "recovery_spell",
|
||||
"shield", "slowness", "spell_boost", "spell_silence", "egg"
|
||||
};
|
||||
for (int i = 0; i < bOnlyStatus.Length; i++)
|
||||
{
|
||||
|
|
@ -202,7 +204,8 @@ public static class ActivityStatusHarness
|
|||
string[] aStatus =
|
||||
{
|
||||
"drowning", "burning", "frozen", "poisoned", "possessed", "cursed",
|
||||
"pregnant", "crying", "rage", "stunned", "soul_harvested", "tantrum"
|
||||
"pregnant", "crying", "rage", "stunned", "soul_harvested", "tantrum",
|
||||
"fell_in_love", "starving", "angry", "sleeping", "inspired", "ash_fever"
|
||||
};
|
||||
for (int i = 0; i < aStatus.Length; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2747,10 +2747,10 @@ public static class AgentHarness
|
|||
source = "boat";
|
||||
break;
|
||||
case "building":
|
||||
id = "building_damage";
|
||||
key = "building:building_damage:" + EventFeedUtil.SafeId(unit);
|
||||
label = "Damages a building: " + EventFeedUtil.SafeName(unit);
|
||||
strength = 70f;
|
||||
id = "building_destroy";
|
||||
key = "building:destroy:harness:" + EventFeedUtil.SafeId(unit);
|
||||
label = EventReason.BuildingFalls("harness_hall", unit);
|
||||
strength = 88f;
|
||||
category = "Spectacle";
|
||||
source = "building";
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -7,63 +7,65 @@ namespace IdleSpectator;
|
|||
public static partial class EventCatalog
|
||||
{
|
||||
public static class Book
|
||||
{
|
||||
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
|
||||
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
static Book()
|
||||
{
|
||||
Add("family_story", 52f, "Culture", "{a} writes a family story");
|
||||
Add("love_story", 58f, "Culture", "{a} writes a love story");
|
||||
Add("friendship_story", 50f, "Culture", "{a} writes a friendship story");
|
||||
Add("bad_story_about_king", 62f, "Culture", "{a} writes a scandalous book");
|
||||
Add("fable", 48f, "Culture", "{a} writes a fable");
|
||||
Add("warfare_manual", 60f, "Culture", "{a} writes a warfare manual");
|
||||
Add("economy_manual", 48f, "Culture", "{a} writes an economy manual");
|
||||
Add("stewardship_manual", 48f, "Culture", "{a} writes a stewardship manual");
|
||||
Add("diplomacy_manual", 55f, "Culture", "{a} writes a diplomacy manual");
|
||||
Add("mathbook", 45f, "Culture", "{a} writes a math book");
|
||||
Add("biology_book", 45f, "Culture", "{a} writes a biology book");
|
||||
Add("history_book", 55f, "Culture", "{a} writes a history book");
|
||||
}
|
||||
private static readonly Dictionary<string, DiscreteEventEntry> Entries =
|
||||
new Dictionary<string, DiscreteEventEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private static void Add(
|
||||
string id,
|
||||
float strength,
|
||||
string category,
|
||||
string label)
|
||||
{
|
||||
Entries[id] = new DiscreteEventEntry
|
||||
static Book()
|
||||
{
|
||||
Id = id,
|
||||
EventStrength = strength,
|
||||
Category = category,
|
||||
CreatesInterest = true,
|
||||
LabelTemplate = label
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AuthoredIds => Entries.Keys;
|
||||
|
||||
public static bool HasAuthored(string id) =>
|
||||
!string.IsNullOrEmpty(id) && Entries.ContainsKey(id.Trim());
|
||||
|
||||
public static DiscreteEventEntry GetOrFallback(string id)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(id) && Entries.TryGetValue(id.Trim(), out DiscreteEventEntry entry))
|
||||
{
|
||||
return entry;
|
||||
// All authored books are Layer A; EventStrength ranks them.
|
||||
Add("family_story", 52f, "Culture", "{a} writes a family story");
|
||||
Add("love_story", 58f, "Culture", "{a} writes a love story");
|
||||
Add("friendship_story", 50f, "Culture", "{a} writes a friendship story");
|
||||
Add("bad_story_about_king", 62f, "Culture", "{a} writes a scandalous book");
|
||||
Add("fable", 48f, "Culture", "{a} writes a fable");
|
||||
Add("warfare_manual", 60f, "Culture", "{a} writes a warfare manual");
|
||||
Add("economy_manual", 48f, "Culture", "{a} writes an economy manual");
|
||||
Add("stewardship_manual", 48f, "Culture", "{a} writes a stewardship manual");
|
||||
Add("diplomacy_manual", 55f, "Culture", "{a} writes a diplomacy manual");
|
||||
Add("mathbook", 45f, "Culture", "{a} writes a math book");
|
||||
Add("biology_book", 45f, "Culture", "{a} writes a biology book");
|
||||
Add("history_book", 55f, "Culture", "{a} writes a history book");
|
||||
}
|
||||
|
||||
string key = string.IsNullOrEmpty(id) ? "unknown" : id.Trim();
|
||||
return new DiscreteEventEntry
|
||||
private static void Add(
|
||||
string id,
|
||||
float strength,
|
||||
string category,
|
||||
string label)
|
||||
{
|
||||
Id = key,
|
||||
EventStrength = 45f,
|
||||
Category = "Culture",
|
||||
LabelTemplate = "{a} writes a book",
|
||||
IsFallback = true
|
||||
};
|
||||
Entries[id] = new DiscreteEventEntry
|
||||
{
|
||||
Id = id,
|
||||
EventStrength = strength,
|
||||
Category = category,
|
||||
CreatesInterest = true,
|
||||
LabelTemplate = label
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<string> AuthoredIds => Entries.Keys;
|
||||
|
||||
public static bool HasAuthored(string id) =>
|
||||
!string.IsNullOrEmpty(id) && Entries.ContainsKey(id.Trim());
|
||||
|
||||
public static DiscreteEventEntry GetOrFallback(string id)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(id) && Entries.TryGetValue(id.Trim(), out DiscreteEventEntry entry))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
|
||||
string key = string.IsNullOrEmpty(id) ? "unknown" : id.Trim();
|
||||
return new DiscreteEventEntry
|
||||
{
|
||||
Id = key,
|
||||
EventStrength = 45f,
|
||||
Category = "Culture",
|
||||
LabelTemplate = "{a} writes a book",
|
||||
CreatesInterest = true,
|
||||
IsFallback = true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "got_robbed",
|
||||
EventStrength = 50f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.RelatedOptional,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -93,7 +93,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "lost_fight",
|
||||
EventStrength = 55f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -147,7 +147,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_received_gift",
|
||||
EventStrength = 50f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.RelatedOptional,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -160,7 +160,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_gave_gift",
|
||||
EventStrength = 50f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.RelatedOptional,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -186,7 +186,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_slept",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -199,7 +199,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "had_bad_dream",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -213,7 +213,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "had_good_dream",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -227,7 +227,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "had_nightmare",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -241,7 +241,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "slept_outside",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -429,7 +429,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_read_book",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -442,7 +442,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_played",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -455,7 +455,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_talked",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.RelatedOptional,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -468,7 +468,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_laughed",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -482,7 +482,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_sang",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -496,7 +496,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_swore",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -510,7 +510,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_cried",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -524,7 +524,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_talked_gossip",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.RelatedOptional,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -537,7 +537,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_surprised",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -566,7 +566,7 @@ public static partial class EventCatalog
|
|||
// Game id uses British spelling; status asset is magnetized.
|
||||
Id = "just_magnetised",
|
||||
EventStrength = 45f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -580,7 +580,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_forced_power",
|
||||
EventStrength = 45f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -607,7 +607,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "strange_urge",
|
||||
EventStrength = 45f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -621,7 +621,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_had_tantrum",
|
||||
EventStrength = 45f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -635,7 +635,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_felt_the_divine",
|
||||
EventStrength = 45f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -648,7 +648,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_enchanted",
|
||||
EventStrength = 45f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -662,7 +662,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_inspired",
|
||||
EventStrength = 45f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
@ -729,7 +729,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_found_house",
|
||||
EventStrength = 55f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ProjectToLife,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -742,7 +742,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_lost_house",
|
||||
EventStrength = 65f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ProjectToLife,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -755,7 +755,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_made_friend",
|
||||
EventStrength = 50f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.RelatedOptional,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.CanonicalMilestone,
|
||||
|
|
@ -769,7 +769,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "just_injured",
|
||||
EventStrength = 65f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ProjectToLife,
|
||||
Overlap = HappinessOverlapPolicy.Independent,
|
||||
|
|
@ -796,7 +796,7 @@ public static partial class EventCatalog
|
|||
{
|
||||
Id = "starving",
|
||||
EventStrength = 30f,
|
||||
Presentation = HappinessPresentationTier.Ambient,
|
||||
Presentation = HappinessPresentationTier.Signal,
|
||||
Context = HappinessContextRequirement.None,
|
||||
Life = HappinessLifePolicy.ActivityOnly,
|
||||
Overlap = HappinessOverlapPolicy.Continuation,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public static partial class EventCatalog
|
|||
ambientStrength: 48f,
|
||||
SignalIds,
|
||||
signalStrength: 88f,
|
||||
ambientCreatesInterest: false);
|
||||
ambientCreatesInterest: true);
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
|
|
@ -115,12 +115,13 @@ public static partial class EventCatalog
|
|||
}
|
||||
|
||||
string s = id.ToLowerInvariant();
|
||||
// Routine AI ticks often include "king" in the name - those are not camera events.
|
||||
// Pure AI ticks - not story beats.
|
||||
if (s.Contains("idle")
|
||||
|| s.Contains("walking")
|
||||
|| s.Contains("check")
|
||||
|| s.Contains("wait")
|
||||
|| s.Contains("sleep"))
|
||||
|| s.Contains("sleep")
|
||||
|| s.Contains("random"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -136,7 +137,17 @@ public static partial class EventCatalog
|
|||
|| s.Contains("siege")
|
||||
|| s.Contains("found")
|
||||
|| s.Contains("city_foundation")
|
||||
|| (s.Contains("king") && (s.Contains("war") || s.Contains("city") || s.Contains("rebellion")));
|
||||
|| s.Contains("army")
|
||||
|| s.Contains("lover")
|
||||
|| s.Contains("plot")
|
||||
|| s.Contains("marry")
|
||||
|| s.Contains("baby")
|
||||
|| s.Contains("child")
|
||||
|| s.Contains("king")
|
||||
|| s.Contains("leader")
|
||||
|| s.Contains("clan")
|
||||
|| s.Contains("religion")
|
||||
|| s.Contains("culture");
|
||||
}
|
||||
|
||||
/// <summary>True when a decision id should be allowed to own the idle camera.</summary>
|
||||
|
|
@ -148,7 +159,7 @@ public static partial class EventCatalog
|
|||
ActivityAssetCatalog.EnumerateLiveDecisionIds,
|
||||
"Politics",
|
||||
"{a} decides {id}",
|
||||
ambientStrength: 32f,
|
||||
ambientStrength: 40f,
|
||||
signalIds: null,
|
||||
signalStrength: 86f,
|
||||
signalPredicate: IsKingdomDecision,
|
||||
|
|
@ -206,7 +217,7 @@ public static partial class EventCatalog
|
|||
signalIds: null,
|
||||
signalStrength: 80f,
|
||||
signalPredicate: IsLegendaryItem,
|
||||
ambientCreatesInterest: false);
|
||||
ambientCreatesInterest: true);
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
|
|
@ -263,7 +274,8 @@ public static partial class EventCatalog
|
|||
ambientStrength: 34f,
|
||||
signalIds: null,
|
||||
signalStrength: 78f,
|
||||
signalPredicate: IsDramatic);
|
||||
signalPredicate: IsDramatic,
|
||||
ambientCreatesInterest: true);
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
|
|
@ -297,7 +309,7 @@ public static partial class EventCatalog
|
|||
signalIds: null,
|
||||
signalStrength: 70f,
|
||||
signalPredicate: id => id != null && (id.Contains("warfare") || id.Contains("magic") || id.Contains("mutation")),
|
||||
ambientCreatesInterest: false);
|
||||
ambientCreatesInterest: true);
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
|
|
@ -330,7 +342,7 @@ public static partial class EventCatalog
|
|||
ambientStrength: 28f,
|
||||
signalIds: null,
|
||||
signalStrength: 60f,
|
||||
ambientCreatesInterest: false);
|
||||
ambientCreatesInterest: true);
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
|
|
@ -364,7 +376,7 @@ public static partial class EventCatalog
|
|||
signalIds: null,
|
||||
signalStrength: 70f,
|
||||
signalPredicate: id => id != null && (id.Contains("war") || id.Contains("death") || id.Contains("plague")),
|
||||
ambientCreatesInterest: false);
|
||||
ambientCreatesInterest: true);
|
||||
|
||||
public static IEnumerable<string> AuthoredIds
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,10 +18,12 @@ public static partial class EventCatalog
|
|||
Add("add_child", 68f, "Relationship", "{a} gains a child, {b}");
|
||||
Add("new_family", 70f, "Relationship", "{a} starts a new family");
|
||||
Add("family_removed", 50f, "Relationship", "{a}'s family ends");
|
||||
// Seeking / pack churn are still candidates; low strength lets the director bury them.
|
||||
Add("find_lover", 45f, "Relationship", "{a} is seeking a lover");
|
||||
Add("become_alpha", 72f, "LifeChapter", "{a} becomes the family alpha");
|
||||
Add("family_group_new", 60f, "Relationship", "{a} forms a family pack");
|
||||
Add("family_group_join", 36f, "Relationship", "{a} joins a family pack", createsInterest: false);
|
||||
Add("family_group_leave", 36f, "Relationship", "{a} leaves a family pack", createsInterest: false);
|
||||
Add("family_group_join", 36f, "Relationship", "{a} joins a family pack");
|
||||
Add("family_group_leave", 36f, "Relationship", "{a} leaves a family pack");
|
||||
Add("baby_created", 70f, "Relationship", "{a} is created as a baby");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public sealed class StatusInterestEntry
|
|||
|
||||
/// <summary>
|
||||
/// Authored interest policy for every live status asset. Prose stays in ActivityStatusProse.
|
||||
/// Default: story-readable statuses are Layer A. Only brief FX / cooldowns stay B-only.
|
||||
/// </summary>
|
||||
public static partial class EventCatalog
|
||||
{
|
||||
|
|
@ -25,7 +26,7 @@ public static partial class EventCatalog
|
|||
|
||||
static Status()
|
||||
{
|
||||
// Spectacle / camera-worthy transformations
|
||||
// Spectacles / crisis
|
||||
Add("burning", 70f, "StatusTransformation", true);
|
||||
Add("possessed", 72f, "StatusTransformation", true);
|
||||
Add("possessed_follower", 60f, "StatusTransformation", true);
|
||||
|
|
@ -34,47 +35,52 @@ public static partial class EventCatalog
|
|||
Add("drowning", 50f, "StatusTransformation", true);
|
||||
Add("stunned", 50f, "StatusTransformation", true);
|
||||
Add("rage", 62f, "StatusTransformation", true);
|
||||
Add("angry", 48f, "StatusAmbient", false);
|
||||
Add("angry", 48f, "StatusAmbient", true);
|
||||
Add("cursed", 60f, "StatusTransformation", true);
|
||||
Add("soul_harvested", 75f, "StatusTransformation", true);
|
||||
Add("voices_in_my_head", 55f, "StatusAmbient", false);
|
||||
Add("voices_in_my_head", 55f, "StatusAmbient", true);
|
||||
Add("tantrum", 52f, "StatusTransformation", true);
|
||||
Add("egg", 40f, "StatusTransformation", false);
|
||||
Add("magnetized", 55f, "StatusAmbient", false);
|
||||
Add("invincible", 40f, "StatusTransformation", false);
|
||||
Add("powerup", 54f, "StatusAmbient", false);
|
||||
Add("magnetized", 55f, "StatusAmbient", true);
|
||||
Add("invincible", 50f, "StatusTransformation", true);
|
||||
Add("powerup", 54f, "StatusAmbient", true);
|
||||
Add("enchanted", 52f, "StatusTransformation", true);
|
||||
Add("ash_fever", 50f, "StatusAmbient", false);
|
||||
Add("starving", 48f, "StatusAmbient", false);
|
||||
Add("surprised", 42f, "StatusAmbient", false);
|
||||
Add("confused", 40f, "StatusAmbient", false);
|
||||
Add("strange_urge", 45f, "StatusAmbient", false);
|
||||
Add("inspired", 42f, "Emotion", false);
|
||||
Add("motivated", 40f, "Emotion", false);
|
||||
Add("fell_in_love", 42f, "Relationship", false);
|
||||
Add("ash_fever", 50f, "StatusAmbient", true);
|
||||
Add("starving", 52f, "StatusAmbient", true);
|
||||
Add("surprised", 42f, "StatusAmbient", true);
|
||||
Add("confused", 40f, "StatusAmbient", true);
|
||||
Add("strange_urge", 45f, "StatusAmbient", true);
|
||||
Add("inspired", 42f, "Emotion", true);
|
||||
Add("motivated", 40f, "Emotion", true);
|
||||
Add("fell_in_love", 58f, "Relationship", true);
|
||||
Add("pregnant", 58f, "LifeChapter", true);
|
||||
Add("pregnant_parthenogenesis", 58f, "LifeChapter", true);
|
||||
Add("crying", 50f, "Grief", true, extendsGrief: true);
|
||||
|
||||
// Authored Ambient - never owns camera (task chip / enrichment only).
|
||||
Add("festive_spirit", 28f, "StatusAmbient", false);
|
||||
Add("laughing", 26f, "StatusAmbient", false);
|
||||
Add("singing", 26f, "StatusAmbient", false);
|
||||
Add("sleeping", 22f, "StatusAmbient", false);
|
||||
Add("handsome_migrant", 30f, "StatusAmbient", false);
|
||||
// Life / social readable beats (lower strength; still camera-eligible)
|
||||
Add("festive_spirit", 36f, "StatusAmbient", true);
|
||||
Add("laughing", 34f, "StatusAmbient", true);
|
||||
Add("singing", 34f, "StatusAmbient", true);
|
||||
Add("sleeping", 32f, "StatusAmbient", true);
|
||||
Add("handsome_migrant", 44f, "StatusAmbient", true);
|
||||
Add("being_suspicious", 40f, "StatusAmbient", true);
|
||||
Add("budding", 48f, "LifeChapter", true);
|
||||
Add("caffeinated", 36f, "StatusAmbient", true);
|
||||
Add("had_bad_dream", 40f, "Emotion", true);
|
||||
Add("had_good_dream", 38f, "Emotion", true);
|
||||
Add("had_nightmare", 44f, "Emotion", true);
|
||||
Add("swearing", 34f, "StatusAmbient", true);
|
||||
Add("taking_roots", 46f, "LifeChapter", true);
|
||||
Add("uprooting", 46f, "LifeChapter", true);
|
||||
|
||||
// Authored but do not create interest candidates (cooldowns / ambient / brief FX)
|
||||
// Egg gain is becoming an egg (not a camera beat). Loss → EmitHatch.
|
||||
Add("egg", 40f, "StatusTransformation", false);
|
||||
|
||||
// Brief FX / combat cooldowns - ticker only (Layer B).
|
||||
AddNoInterest("afterglow");
|
||||
AddNoInterest("being_suspicious");
|
||||
AddNoInterest("budding");
|
||||
AddNoInterest("caffeinated");
|
||||
AddNoInterest("cough");
|
||||
AddNoInterest("dash");
|
||||
AddNoInterest("dodge");
|
||||
AddNoInterest("flicked");
|
||||
AddNoInterest("had_bad_dream");
|
||||
AddNoInterest("had_good_dream");
|
||||
AddNoInterest("had_nightmare");
|
||||
AddNoInterest("just_ate");
|
||||
AddNoInterest("on_guard");
|
||||
AddNoInterest("recovery_combat_action");
|
||||
|
|
@ -85,9 +91,6 @@ public static partial class EventCatalog
|
|||
AddNoInterest("slowness");
|
||||
AddNoInterest("spell_boost");
|
||||
AddNoInterest("spell_silence");
|
||||
AddNoInterest("swearing");
|
||||
AddNoInterest("taking_roots");
|
||||
AddNoInterest("uprooting");
|
||||
}
|
||||
|
||||
private static void Add(
|
||||
|
|
|
|||
|
|
@ -50,6 +50,11 @@ public static class EventReason
|
|||
return NameOrSomeone(a) + " is seeking a lover";
|
||||
}
|
||||
|
||||
public static string BecomeAlpha(Actor a)
|
||||
{
|
||||
return NameOrSomeone(a) + " becomes the family alpha";
|
||||
}
|
||||
|
||||
public static string Hatch(Actor a)
|
||||
{
|
||||
return NameOrSomeone(a) + " hatches from an egg";
|
||||
|
|
|
|||
|
|
@ -479,6 +479,67 @@ public static class InterestFeeds
|
|||
|
||||
private static string HatchKey(long subjectId) => "hatch:" + subjectId;
|
||||
|
||||
private static string AlphaKey(long subjectId) => "alpha:" + subjectId;
|
||||
|
||||
/// <summary>
|
||||
/// Family alpha succession. Shared key with happiness <c>become_alpha</c> so emotion and
|
||||
/// emotion-less paths merge into one camera beat.
|
||||
/// </summary>
|
||||
public static void EmitAlpha(Actor actor, string source)
|
||||
{
|
||||
if (actor == null || !actor.isAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AgentHarness.Busy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long id = SafeId(actor);
|
||||
if (id == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DiscreteEventEntry entry = EventCatalog.Relationship.GetOrFallback("become_alpha");
|
||||
if (!EventCatalog.IsCameraWorthy(entry))
|
||||
{
|
||||
InterestDropLog.Record("createsInterest=false", "become_alpha");
|
||||
return;
|
||||
}
|
||||
|
||||
float strength = Mathf.Max(
|
||||
entry.EventStrength,
|
||||
InterestScoring.EventStrengthForHappiness("become_alpha"));
|
||||
var candidate = new InterestCandidate
|
||||
{
|
||||
Key = AlphaKey(id),
|
||||
LeadKind = InterestLeadKind.EventLed,
|
||||
Category = string.IsNullOrEmpty(entry.Category) ? "LifeChapter" : entry.Category,
|
||||
Source = string.IsNullOrEmpty(source) ? "alpha" : source,
|
||||
EventStrength = strength,
|
||||
VisualConfidence = 0.8f,
|
||||
Position = actor.current_position,
|
||||
FollowUnit = actor,
|
||||
SubjectId = id,
|
||||
Label = EventReason.BecomeAlpha(actor),
|
||||
AssetId = SafeAsset(actor),
|
||||
SpeciesId = SafeAsset(actor),
|
||||
HappinessEffectId = "become_alpha",
|
||||
CreatedAt = Time.unscaledTime,
|
||||
LastSeenAt = Time.unscaledTime,
|
||||
ExpiresAt = Time.unscaledTime + 25f,
|
||||
MinWatch = 5f,
|
||||
MaxWatch = 22f,
|
||||
Completion = InterestCompletionKind.FixedDwell
|
||||
};
|
||||
candidate.ParticipantIds.Add(id);
|
||||
InterestScoring.ScoreCheap(candidate);
|
||||
EventFeedUtil.RegisterCandidate(candidate);
|
||||
}
|
||||
|
||||
public static void OnChronicleMilestone(Actor subject, string milestoneKey, Actor related, string label)
|
||||
{
|
||||
if (subject == null || !subject.isAlive() || string.IsNullOrEmpty(milestoneKey))
|
||||
|
|
@ -614,12 +675,23 @@ public static class InterestFeeds
|
|||
bool grief = occ.EffectId != null && occ.EffectId.StartsWith("death_");
|
||||
float strength = InterestScoring.EventStrengthForHappiness(occ.EffectId);
|
||||
bool hatchMoment = IsFreshLifeMoment(occ.EffectId);
|
||||
bool alphaMoment = IsAlphaMoment(occ.EffectId);
|
||||
|
||||
// Hatch shares a key with status egg-loss so both paths merge into one camera beat.
|
||||
string key = hatchMoment
|
||||
? HatchKey(occ.SubjectId)
|
||||
: ("happiness:" + occ.EffectId + ":" + occ.SubjectId + ":" + occ.RelatedId
|
||||
+ ":" + CorrBucket(occ.CorrelationKey));
|
||||
// Hatch / alpha share keys with status-loss / Family.setAlpha so paths merge.
|
||||
string key;
|
||||
if (hatchMoment)
|
||||
{
|
||||
key = HatchKey(occ.SubjectId);
|
||||
}
|
||||
else if (alphaMoment)
|
||||
{
|
||||
key = AlphaKey(occ.SubjectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
key = "happiness:" + occ.EffectId + ":" + occ.SubjectId + ":" + occ.RelatedId
|
||||
+ ":" + CorrBucket(occ.CorrelationKey);
|
||||
}
|
||||
|
||||
// Hatch: short queue TTL, but on-camera floor comes from minCameraDwell.
|
||||
float ttl = hatchMoment ? 7f : 35f;
|
||||
|
|
@ -630,11 +702,23 @@ public static class InterestFeeds
|
|||
strength = Mathf.Max(strength, 88f);
|
||||
}
|
||||
|
||||
if (alphaMoment)
|
||||
{
|
||||
strength = Mathf.Max(strength, EventCatalog.Relationship.GetOrFallback("become_alpha").EventStrength);
|
||||
ttl = 25f;
|
||||
minWatch = 5f;
|
||||
maxWatch = 22f;
|
||||
}
|
||||
|
||||
string label;
|
||||
if (hatchMoment)
|
||||
{
|
||||
label = EventReason.Hatch(subject);
|
||||
}
|
||||
else if (alphaMoment)
|
||||
{
|
||||
label = EventReason.BecomeAlpha(subject);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(occ.PlainClause))
|
||||
{
|
||||
label = occ.SubjectName + " " + occ.PlainClause;
|
||||
|
|
@ -649,7 +733,9 @@ public static class InterestFeeds
|
|||
Key = key,
|
||||
LeadKind = InterestLeadKind.EventLed,
|
||||
Category = grief ? "FamilyEmotion" : MapHappinessCategory(cat),
|
||||
Source = hatchMoment ? "happiness_hatch" : "happiness",
|
||||
Source = hatchMoment
|
||||
? "happiness_hatch"
|
||||
: (alphaMoment ? "happiness_alpha" : "happiness"),
|
||||
EventStrength = strength,
|
||||
VisualConfidence = 0.75f,
|
||||
Position = occ.Position != Vector3.zero ? occ.Position : subject.current_position,
|
||||
|
|
@ -887,6 +973,12 @@ public static class InterestFeeds
|
|||
|| string.Equals(id, "just_hatched", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static bool IsAlphaMoment(string effectId)
|
||||
{
|
||||
return !string.IsNullOrEmpty(effectId)
|
||||
&& string.Equals(effectId.Trim(), "become_alpha", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private static string MapHappinessCategory(string cat)
|
||||
{
|
||||
switch ((cat ?? "").ToLowerInvariant())
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@ public static class RelationshipInterestFeed
|
|||
return EventReason.LoverParted(subject);
|
||||
case "find_lover":
|
||||
return EventReason.SeekingLover(subject);
|
||||
case "become_alpha":
|
||||
return EventReason.BecomeAlpha(subject);
|
||||
case "add_child":
|
||||
return EventReason.NewChild(subject, related);
|
||||
case "baby_created":
|
||||
|
|
|
|||
|
|
@ -35,12 +35,9 @@ public static class BoatEventPatches
|
|||
[HarmonyPostfix]
|
||||
public static void PostfixLoad(Actor pActor, BehResult __result)
|
||||
{
|
||||
if (pActor == null || !pActor.isAlive() || __result == BehResult.Stop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EmitBoat("boat_load", 55f, pActor, EventReason.Boat(pActor, "load"));
|
||||
// Loading is routine transport churn - unload/trade stay on Layer A.
|
||||
_ = pActor;
|
||||
_ = __result;
|
||||
}
|
||||
|
||||
private static void EmitBoat(string eventId, float strength, Actor actor, string label)
|
||||
|
|
|
|||
|
|
@ -12,23 +12,9 @@ public static class BuildingEventPatches
|
|||
[HarmonyPostfix]
|
||||
public static void PostfixDamageBuilding(Actor pActor, BehResult __result)
|
||||
{
|
||||
if (pActor == null || !pActor.isAlive() || __result == BehResult.Stop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Building target = null;
|
||||
try
|
||||
{
|
||||
target = pActor.beh_building_target;
|
||||
}
|
||||
catch
|
||||
{
|
||||
target = null;
|
||||
}
|
||||
|
||||
string label = EventReason.BuildingDamage(pActor, target);
|
||||
Emit("building_damage", 70f, pActor, label);
|
||||
// Routine siege chips are Layer B noise. Camera waits for consume/destroy.
|
||||
_ = pActor;
|
||||
_ = __result;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(BehConsumeTargetBuilding), nameof(BehConsumeTargetBuilding.execute))]
|
||||
|
|
|
|||
|
|
@ -62,6 +62,29 @@ public static class MetaEventPatches
|
|||
EmitNew("new_city", "Politics", 74f, __result, "city");
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(KingdomManager), nameof(KingdomManager.makeNewCivKingdom))]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixNewCivKingdom(Kingdom __result, Actor pActor)
|
||||
{
|
||||
if (__result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Actor anchor = pActor != null && pActor.isAlive() ? pActor : TryReadActor(__result);
|
||||
if (anchor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MetaInterestFeed.EmitMeta(
|
||||
"new_kingdom",
|
||||
"Politics",
|
||||
82f,
|
||||
anchor,
|
||||
EventReason.MetaNew(anchor, "kingdom"));
|
||||
}
|
||||
|
||||
private static void EmitNew(
|
||||
string eventId,
|
||||
string category,
|
||||
|
|
|
|||
|
|
@ -74,6 +74,32 @@ public static class PlotEventPatches
|
|||
PlotInterestFeed.Emit(__state, __instance, "leave");
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(Plot), nameof(Plot.finishPlot))]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixFinishPlot(Plot __instance, PlotState pState, Actor pActor)
|
||||
{
|
||||
if (__instance == null || pState != PlotState.Finished)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Prefer finishing actor; fall back to plot author inside EmitFromPlot.
|
||||
if (pActor != null && pActor.isAlive())
|
||||
{
|
||||
string assetId = ReadPlotAssetId(__instance);
|
||||
if (string.IsNullOrEmpty(assetId))
|
||||
{
|
||||
PlotInterestFeed.EmitFromPlot(__instance, "complete");
|
||||
return;
|
||||
}
|
||||
|
||||
PlotInterestFeed.Emit(assetId, pActor, "complete");
|
||||
return;
|
||||
}
|
||||
|
||||
PlotInterestFeed.EmitFromPlot(__instance, "complete");
|
||||
}
|
||||
|
||||
private static string ReadPlotAssetId(object plot)
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -183,4 +183,20 @@ public static class RelationshipEventPatches
|
|||
|
||||
RelationshipInterestFeed.Emit("family_group_leave", pActor, ActorRelation.ResolvePackRelated(pActor));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alpha succession is happiness-gated by emotions in vanilla. Patch the family API so
|
||||
/// animals without emotions still get a Layer A / Activity-capable beat.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(Family), "setAlpha")]
|
||||
[HarmonyPostfix]
|
||||
public static void PostfixSetAlpha(Actor pActor, bool pNew)
|
||||
{
|
||||
if (!pNew || pActor == null || !pActor.isAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InterestFeeds.EmitAlpha(pActor, "family_set_alpha");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,12 +119,7 @@ public static class TraitEventPatches
|
|||
return;
|
||||
}
|
||||
|
||||
// Quiet-world Ambient traits must not steal the camera.
|
||||
if (entry.EventStrength < InterestScoringConfig.W.noticeScoreMin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Quiet traits still register; low EventStrength lets the director bury them.
|
||||
// Spawn/birth endowment: only legendary rarities.
|
||||
if (gained && IsSpawnTraitWindow(actor) && !LegendaryBirthTraits.Contains(entry.Id))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1197,12 +1197,20 @@ internal static class HarnessScenarios
|
|||
|
||||
Step("et20", "domain_feed", asset: "relationship", value: "set_lover"),
|
||||
Step("et21", "assert", expect: "interest_has_key", value: "rel:set_lover"),
|
||||
Step("et21a", "domain_feed", asset: "relationship", value: "become_alpha"),
|
||||
Step("et21b", "assert", expect: "interest_has_key", value: "rel:become_alpha"),
|
||||
Step("et21c", "interest_force_session", asset: "human",
|
||||
label: "{a} becomes the family alpha", tier: "Action", expect: "alpha_reason",
|
||||
value: "lead=event;evt=72"),
|
||||
Step("et21d", "assert", expect: "dossier_contains", value: "family alpha"),
|
||||
Step("et22", "domain_feed", asset: "plot", value: "summon_meteor_rain"),
|
||||
Step("et23", "assert", expect: "interest_has_key", value: "plot:summon_meteor_rain"),
|
||||
Step("et24", "domain_feed", asset: "era", value: "age_chaos"),
|
||||
Step("et25", "assert", expect: "interest_has_key", value: "era:age_chaos"),
|
||||
Step("et26", "domain_feed", asset: "meta", value: "new_city"),
|
||||
Step("et27", "assert", expect: "interest_has_key", value: "meta:new_city"),
|
||||
Step("et27b", "domain_feed", asset: "meta", value: "new_kingdom"),
|
||||
Step("et27c", "assert", expect: "interest_has_key", value: "meta:new_kingdom"),
|
||||
Step("et28", "domain_feed", asset: "book", value: "bad_story_about_king"),
|
||||
Step("et29", "assert", expect: "interest_has_key", value: "book:bad_story_about_king"),
|
||||
Step("et30", "domain_feed", asset: "trait", value: "immortal"),
|
||||
|
|
|
|||
|
|
@ -199,11 +199,46 @@ public static class InterestScoring
|
|||
sig += Mathf.Min(w.metaRenownCap, snap.Renown / renownDiv);
|
||||
sig += Mathf.Min(w.metaKillsCap, snap.Kills * w.metaKillsFactor);
|
||||
sig += Mathf.Min(w.metaLevelCap, snap.Level * w.metaLevelFactor);
|
||||
sig += SpeciesRarityBonus(snap.SpeciesId, w);
|
||||
snap.CharacterSignificance = sig;
|
||||
MetaCache[id] = snap;
|
||||
return snap;
|
||||
}
|
||||
|
||||
private static float SpeciesRarityBonus(string speciesId, ScoringWeights w)
|
||||
{
|
||||
if (string.IsNullOrEmpty(speciesId) || w == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Dictionary<string, int> counts = WorldActivityScanner.CountSpeciesPopulations();
|
||||
if (counts == null || !counts.TryGetValue(speciesId, out int pop) || pop <= 0)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
if (pop == 1)
|
||||
{
|
||||
return w.speciesSingletonBonus;
|
||||
}
|
||||
|
||||
int rareMax = w.speciesRareMaxPop > 0 ? w.speciesRareMaxPop : 5;
|
||||
if (pop <= rareMax)
|
||||
{
|
||||
return w.speciesRareBonus;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
return 0f;
|
||||
}
|
||||
|
||||
private static void ApplyMeta(InterestCandidate c, InterestMetadataSnapshot snap)
|
||||
{
|
||||
if (c == null || snap == null)
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ public class ScoringWeights
|
|||
public float metaKillsCap = 12f;
|
||||
public float metaLevelFactor = 0.4f;
|
||||
public float metaLevelCap = 10f;
|
||||
/// <summary>Bonus when the follow unit is the only living member of its species.</summary>
|
||||
public float speciesSingletonBonus = 14f;
|
||||
/// <summary>Bonus when species population is between 2 and speciesRareMaxPop inclusive.</summary>
|
||||
public float speciesRareBonus = 8f;
|
||||
public int speciesRareMaxPop = 5;
|
||||
|
||||
public int notableKills = 40;
|
||||
public float notableRenown = 200f;
|
||||
|
|
|
|||
|
|
@ -106,6 +106,9 @@ public static class MutationDiscoveryHarness
|
|||
"ArmyManager.newArmy",
|
||||
"AllianceManager.newAlliance",
|
||||
"CityManager.newCity",
|
||||
"KingdomManager.makeNewCivKingdom",
|
||||
"Family.setAlpha",
|
||||
"Plot.finishPlot",
|
||||
"ItemManager.generateItem",
|
||||
"ItemManager.newItem",
|
||||
"SubspeciesManager.newSpecies",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "IdleSpectator",
|
||||
"author": "dazed",
|
||||
"version": "0.22.0",
|
||||
"description": "AFK Idle Spectator (I) + Lore (L). Layer B ticker + Layer A story camera.",
|
||||
"version": "0.24.0",
|
||||
"description": "AFK Idle Spectator (I) + Lore (L). Interesting beats are candidates; director ranks.",
|
||||
"GUID": "com.dazed.idlespectator"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,9 @@
|
|||
"metaKillsCap": 12,
|
||||
"metaLevelFactor": 0.4,
|
||||
"metaLevelCap": 10,
|
||||
"speciesSingletonBonus": 14,
|
||||
"speciesRareBonus": 8,
|
||||
"speciesRareMaxPop": 5,
|
||||
|
||||
"notableKills": 40,
|
||||
"notableRenown": 200,
|
||||
|
|
|
|||
|
|
@ -39,15 +39,25 @@ Ambient / Character fill is exempt - A events may take the camera immediately.
|
|||
| Happiness | `IngestHappiness` | catalog + EventReason | Signal = A; see [event-prose-audit.md](event-prose-audit.md) |
|
||||
| Status | `OnStatusChange` | EventReason.Status | Gain A if CreatesInterest; egg **loss** → hatch A |
|
||||
| Scanner combat | WorldActivityScanner | EventReason.Fight / Battle | `EventCatalog.Combat` |
|
||||
| War / Plot / Relationship / Trait / Building / Boat / Book / Era | Feeds + patches | EventReason / catalog | A via CreatesInterest |
|
||||
| War / Plot / Relationship / Trait / Building / Boat / Book / Era / Kingdom | Feeds + patches | EventReason / catalog | A via CreatesInterest; alpha via Family.setAlpha |
|
||||
| Libraries | Deferred feeds | EventReason.Library | Mostly B; spectacle ids A |
|
||||
| Character fill | InterestDirector.TryCharacterFill | empty Label | Only when no pending EventLed |
|
||||
|
||||
## Layer A demotion (this pass)
|
||||
## Layer A policy
|
||||
|
||||
Happiness Signal → Ambient (B ticker only): gifts, cry, inspired, enchanted, divine, magnetised, strange_urge, tantrum, forced_power, lost_fight, got_robbed, house find/lose, made friend, injured.
|
||||
Interesting beats register as interest candidates (`Signal` / `CreatesInterest`).
|
||||
Director ranks by `EventStrength` + scoring - do not hard-gate interesting life off Layer A.
|
||||
|
||||
Status CreatesInterest → false (Activity remains): surprised, confused, motivated, inspired, magnetized, starving, ash_fever, powerup, voices_in_my_head, angry.
|
||||
Happiness Ambient (B) only: `got_poked`, `got_caught`, `paid_tax`, `just_ate`, `just_pooped`.
|
||||
Happiness Aggregates stay civic mass (B unit camera).
|
||||
|
||||
Status B: brief FX/cooldowns + egg gain only.
|
||||
|
||||
Noise still skipped at patch (not dial): building_damage chips, boat_load.
|
||||
|
||||
## Full surface
|
||||
|
||||
See [world-event-inventory.md](world-event-inventory.md) for live library counts, gaps, and refresh commands.
|
||||
|
||||
## Interrupt / hold / grace
|
||||
|
||||
|
|
|
|||
|
|
@ -39,11 +39,12 @@ Scope: Happiness (all authored), Relationship (all), Status/WorldLog/Plot/etc. s
|
|||
|
||||
Grief deaths, `fallen_in_love`, gifts, house find/lose, `just_got_out_of_egg`, `just_became_adult`, WorldLog `king_new` template roles, Status `egg` / `pregnant` / `drowning` prose.
|
||||
|
||||
## A/B camera demotion (2026-07-16)
|
||||
## A/B camera policy (2026-07-16)
|
||||
|
||||
Happiness Signal → Ambient (ticker only): gifts, cry, inspired, enchanted, divine, magnetised, strange_urge, tantrum happiness, forced_power, lost_fight, got_robbed, house find/lose, made friend, injured.
|
||||
Interesting = interest candidate. Director ranks by strength.
|
||||
|
||||
Status CreatesInterest → false (Activity remains): surprised, confused, motivated, inspired, magnetized, starving, ash_fever, powerup, voices_in_my_head, angry.
|
||||
Happiness Ambient only for true FX: poke, caught stub, tax, ate, pooped.
|
||||
Status CreatesInterest false only for brief FX/cooldowns + egg gain.
|
||||
|
||||
Gate helper: `EventCatalog.IsCameraWorthy`. Drops: `InterestDropLog`.
|
||||
|
||||
|
|
|
|||
91
docs/world-event-inventory.md
Normal file
91
docs/world-event-inventory.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# World event inventory (full surface)
|
||||
|
||||
Source of truth for what IdleSpectator covers across the **whole** game.
|
||||
Refresh via live libraries + `mutation_discovery` / domain audits under `.harness/`.
|
||||
|
||||
Audit date: 2026-07-16.
|
||||
|
||||
## Policy
|
||||
|
||||
**If it is interesting, it is an interest candidate.**
|
||||
`CreatesInterest` / Happiness `Signal` registers Layer A.
|
||||
`EventStrength` + scoring decide who gets the spotlight.
|
||||
Only true brief FX, cooldowns, civic Aggregates, and god-tool / biome spam stay B-only.
|
||||
|
||||
## How to read this
|
||||
|
||||
| Layer | Meaning |
|
||||
|-------|---------|
|
||||
| **B** | Activity / Life / chronicle prose may fire; no interest candidate |
|
||||
| **A** | Registers interest; director ranks by points |
|
||||
| **Gap** | Game fires a lifecycle beat; we have no reliable A path |
|
||||
|
||||
## Live libraries (enumerated)
|
||||
|
||||
| Library | ~Count | Owner |
|
||||
|---------|-------:|-------|
|
||||
| happiness_library | 67 | EventCatalog.Happiness |
|
||||
| status | 58 | EventCatalog.Status |
|
||||
| world_log_library | 41 | EventCatalog.WorldLog |
|
||||
| disasters | 17 | via WorldLog |
|
||||
| war_types_library | 5 | EventCatalog.WarType |
|
||||
| traits | 116 | EventCatalog.Trait (all CI; strength ranks) |
|
||||
| plots_library | 28 | EventCatalog.Plot |
|
||||
| era_library | 11 | EventCatalog.Era |
|
||||
| book_types | 12 | EventCatalog.Book (all A) |
|
||||
| decisions / powers / spells / items | 127 / 339 / 12 / 111 | EventCatalog.Libraries |
|
||||
| subspecies_traits / genes / phenotypes | 204 / 47 / 52 | Libraries |
|
||||
| buildings | 618 | patches (consume/destroy A; damage skipped) |
|
||||
| actor_library | 322 | SpeciesDiscovery / scoring |
|
||||
| culture/religion/clan/language traits | 78 / 40 / 29 / 26 | **no event catalog yet** |
|
||||
|
||||
## Layer A sizes (policy)
|
||||
|
||||
| Domain | Authored | Layer A |
|
||||
|--------|--------:|--------:|
|
||||
| Happiness | 67 | ~49 Signal (5 Ambient FX; 13 Aggregate civic) |
|
||||
| Status | 58 | ~42 CreatesInterest (FX/cooldowns + egg gain stay B) |
|
||||
| WorldLog | 41 | ~40 |
|
||||
| Relationship | 11 | all interesting discrete (seeker/pack at low strength) |
|
||||
| Plot / Era / War / Book | 28 / 11 / 5 / 12 | all A |
|
||||
| Trait | 116 | all CI (no noticeScoreMin hard drop) |
|
||||
| Spell / Item / Gene / Phenotype / WorldLaw / SubspeciesTrait | live | ambient A at low strength; Signal bumps spectacle |
|
||||
| Power / Decision / Biome | live | Signal predicate only (tools / idle AI / terrain) |
|
||||
| Building / Boat | none | destroy/consume/trade/unload; damage/load skipped |
|
||||
|
||||
## Wired hooks (major)
|
||||
|
||||
Happiness, status (+ egg-loss hatch), WorldLog, wars, plots (new/cancel/join/leave/finish),
|
||||
meta genesis (culture/religion/language/clan/army/alliance/city/kingdom),
|
||||
books, traits, deferred libraries, boats (trade/unload), buildings (consume/destroy),
|
||||
relationship (lover/child/family/baby/alpha), Family.setAlpha, SpeciesDiscovery.
|
||||
|
||||
## Known gaps / thin areas
|
||||
|
||||
| Gap | Why it matters | Status |
|
||||
|-----|----------------|--------|
|
||||
| Culture/religion/clan/language trait gain | Meta drama libraries exist | **Open** |
|
||||
| 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** |
|
||||
|
||||
## Stay B (true noise)
|
||||
|
||||
- Happiness: `got_poked`, `got_caught`, `paid_tax`, `just_ate`, `just_pooped` + all Aggregates
|
||||
- Status: dash/dodge/shield/recovery_*/spell_*/cough/flicked/just_ate/on_guard/afterglow/slowness + egg gain
|
||||
- Libraries: Power ambient (god paint tools), Decision ambient (idle AI; interesting decisions are Signal), Biome ambient
|
||||
- Patches: `building_damage`, `boat_load`
|
||||
|
||||
## Scoring notes
|
||||
|
||||
`TotalScore = EventStrength + scale + CharacterSignificance * charWeight + visual + novelty`.
|
||||
|
||||
CharacterSignificance includes favorite/king/leader/renown/kills/level and species rarity.
|
||||
|
||||
## Refresh commands
|
||||
|
||||
```bash
|
||||
./scripts/harness-run.sh --no-launch mutation_discovery
|
||||
./scripts/harness-run.sh --no-launch domain_event_audit
|
||||
./scripts/harness-run.sh --no-launch activity_happiness_audit
|
||||
```
|
||||
Loading…
Reference in a new issue