Healthy Director
This commit is contained in:
parent
57d2e70476
commit
4491423236
8 changed files with 350 additions and 26 deletions
|
|
@ -191,6 +191,23 @@ public static class AgentHarness
|
|||
break;
|
||||
}
|
||||
|
||||
case "force_live_feeds":
|
||||
{
|
||||
bool on = ParseBool(cmd.value, defaultValue: true);
|
||||
ForceLiveEventFeeds = on;
|
||||
_cmdOk++;
|
||||
Emit(cmd, ok: true, detail: $"force_live_feeds={on}");
|
||||
break;
|
||||
}
|
||||
|
||||
case "drop_clear":
|
||||
{
|
||||
InterestDropLog.Clear();
|
||||
_cmdOk++;
|
||||
Emit(cmd, ok: true, detail: "drops_cleared");
|
||||
break;
|
||||
}
|
||||
|
||||
case "age_current":
|
||||
{
|
||||
float seconds = cmd.wait > 0f ? cmd.wait : ParseFloat(cmd.value, 2f);
|
||||
|
|
@ -2261,6 +2278,7 @@ public static class AgentHarness
|
|||
bool ok = published != null;
|
||||
if (ok)
|
||||
{
|
||||
InterestFeeds.HarnessEnsureCursorBefore(published.Sequence);
|
||||
_cmdOk++;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -290,6 +290,7 @@ public static class EventCatalogConfig
|
|||
InterestCategory = ExtractJsonString(obj, "category") ?? "",
|
||||
CanonicalMilestoneKey = ExtractJsonString(obj, "canonicalMilestoneKey") ?? "",
|
||||
StatusOverlapId = ExtractJsonString(obj, "statusOverlapId") ?? "",
|
||||
StatusOverlapKind = ParseStatusOverlapKind(obj, ExtractJsonString(obj, "statusOverlapId")),
|
||||
VariantsWithRelated = ExtractStringArray(obj, "variantsWithRelated"),
|
||||
VariantsWithoutRelated = ExtractStringArray(obj, "variantsWithoutRelated")
|
||||
};
|
||||
|
|
@ -468,6 +469,51 @@ public static class EventCatalogConfig
|
|||
return Enum.TryParse(raw, true, out TEnum value) ? value : fallback;
|
||||
}
|
||||
|
||||
private static HappinessStatusOverlapKind ParseStatusOverlapKind(string json, string statusOverlapId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(statusOverlapId))
|
||||
{
|
||||
return HappinessStatusOverlapKind.None;
|
||||
}
|
||||
|
||||
HappinessStatusOverlapKind parsed = ParseEnum(
|
||||
json, "statusOverlapKind", HappinessStatusOverlapKind.None);
|
||||
if (parsed != HappinessStatusOverlapKind.None)
|
||||
{
|
||||
return parsed;
|
||||
}
|
||||
|
||||
// Legacy rows without statusOverlapKind: offset end-pings vs onset holds.
|
||||
string id = ExtractJsonString(json, "id") ?? "";
|
||||
if (IsLegacyOffsetOverlap(id))
|
||||
{
|
||||
return HappinessStatusOverlapKind.Offset;
|
||||
}
|
||||
|
||||
return HappinessStatusOverlapKind.Hold;
|
||||
}
|
||||
|
||||
private static bool IsLegacyOffsetOverlap(string effectId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(effectId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (effectId.Trim().ToLowerInvariant())
|
||||
{
|
||||
case "just_laughed":
|
||||
case "just_sang":
|
||||
case "just_swore":
|
||||
case "just_cried":
|
||||
case "just_had_tantrum":
|
||||
case "just_inspired":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string[] ExtractStringArray(string json, string key)
|
||||
{
|
||||
if (!TryExtractArray(json, key, out string arr) || string.IsNullOrEmpty(arr))
|
||||
|
|
|
|||
|
|
@ -27,6 +27,30 @@ public static class InterestFeeds
|
|||
InterestDropLog.Clear();
|
||||
}
|
||||
|
||||
/// <summary>Keep drain cursor coherent when the happiness router sequence resets.</summary>
|
||||
public static void OnHappinessRouterCleared()
|
||||
{
|
||||
_happinessCursor = HappinessEventRouter.CurrentSequence;
|
||||
HappinessDrain.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Harness: if the router was cleared without syncing the feed cursor, rewind so
|
||||
/// <see cref="HarnessDrainOnce"/> can see the new sequence.
|
||||
/// </summary>
|
||||
public static void HarnessEnsureCursorBefore(ulong sequence)
|
||||
{
|
||||
if (sequence == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_happinessCursor >= sequence)
|
||||
{
|
||||
_happinessCursor = sequence - 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static ulong HappinessCursor => _happinessCursor;
|
||||
|
||||
public static int CivicBoostCount
|
||||
|
|
@ -728,6 +752,27 @@ public static class InterestFeeds
|
|||
label = occ.SubjectName + " · " + occ.EffectId;
|
||||
}
|
||||
|
||||
InterestCompletionKind completion = grief
|
||||
? InterestCompletionKind.HappinessGrief
|
||||
: InterestCompletionKind.FixedDwell;
|
||||
string statusId = grief ? "crying" : "";
|
||||
|
||||
if (!grief
|
||||
&& !hatchMoment
|
||||
&& !alphaMoment
|
||||
&& !TryBindStatusOverlapCamera(
|
||||
occ,
|
||||
subject,
|
||||
ref key,
|
||||
ref minWatch,
|
||||
ref maxWatch,
|
||||
ref strength,
|
||||
ref completion,
|
||||
ref statusId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var candidate = new InterestCandidate
|
||||
{
|
||||
Key = key,
|
||||
|
|
@ -747,16 +792,14 @@ public static class InterestFeeds
|
|||
AssetId = occ.SubjectSpecies,
|
||||
SpeciesId = occ.SubjectSpecies,
|
||||
HappinessEffectId = occ.EffectId,
|
||||
StatusId = grief ? "crying" : "",
|
||||
StatusId = statusId,
|
||||
CorrelationKey = occ.CorrelationKey,
|
||||
CreatedAt = Time.unscaledTime,
|
||||
LastSeenAt = Time.unscaledTime,
|
||||
ExpiresAt = Time.unscaledTime + ttl,
|
||||
MinWatch = minWatch,
|
||||
MaxWatch = maxWatch,
|
||||
Completion = grief
|
||||
? InterestCompletionKind.HappinessGrief
|
||||
: InterestCompletionKind.FixedDwell
|
||||
Completion = completion
|
||||
};
|
||||
candidate.ParticipantIds.Add(occ.SubjectId);
|
||||
if (occ.RelatedId != 0)
|
||||
|
|
@ -778,6 +821,117 @@ public static class InterestFeeds
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Camera-worthy status-overlap happiness must not FixedDwell-claim a status the unit lacks.
|
||||
/// Hold (onset): StatusPhase while live, else drop. Offset (end-ping): FixedDwell without status.
|
||||
/// </summary>
|
||||
private static bool TryBindStatusOverlapCamera(
|
||||
HappinessOccurrence occ,
|
||||
Actor subject,
|
||||
ref string key,
|
||||
ref float minWatch,
|
||||
ref float maxWatch,
|
||||
ref float strength,
|
||||
ref InterestCompletionKind completion,
|
||||
ref string statusId)
|
||||
{
|
||||
if (occ == null || string.IsNullOrEmpty(occ.StatusOverlapId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
HappinessStatusOverlapKind kind = ResolveStatusOverlapKind(occ);
|
||||
if (kind == HappinessStatusOverlapKind.None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string overlapId = occ.StatusOverlapId.Trim();
|
||||
StatusInterestEntry statusEntry = EventCatalog.Status.GetOrFallback(overlapId);
|
||||
if (!EventCatalog.IsCameraWorthy(statusEntry))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hasStatus = StatusGameApi.HasStatus(subject, overlapId)
|
||||
|| InterestCompletionHasStatus(subject, overlapId);
|
||||
if (hasStatus)
|
||||
{
|
||||
key = "status:" + overlapId + ":" + occ.SubjectId;
|
||||
statusId = overlapId;
|
||||
completion = InterestCompletionKind.StatusPhase;
|
||||
minWatch = Mathf.Max(minWatch, 2f);
|
||||
maxWatch = Mathf.Max(maxWatch, InterestScoringConfig.W.maxWatchStatus);
|
||||
strength = Mathf.Max(strength, statusEntry.EventStrength);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (kind == HappinessStatusOverlapKind.Offset)
|
||||
{
|
||||
// End-ping after the status cleared - short FixedDwell, keep happiness key.
|
||||
maxWatch = Mathf.Min(maxWatch, 12f);
|
||||
return true;
|
||||
}
|
||||
|
||||
InterestDropLog.Record("status_overlap_absent", occ.EffectId ?? overlapId);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static HappinessStatusOverlapKind ResolveStatusOverlapKind(HappinessOccurrence occ)
|
||||
{
|
||||
if (occ.StatusOverlapKind != HappinessStatusOverlapKind.None)
|
||||
{
|
||||
return occ.StatusOverlapKind;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(occ.StatusOverlapId))
|
||||
{
|
||||
return HappinessStatusOverlapKind.None;
|
||||
}
|
||||
|
||||
string id = (occ.EffectId ?? "").Trim().ToLowerInvariant();
|
||||
switch (id)
|
||||
{
|
||||
case "just_laughed":
|
||||
case "just_sang":
|
||||
case "just_swore":
|
||||
case "just_cried":
|
||||
case "just_had_tantrum":
|
||||
case "just_inspired":
|
||||
return HappinessStatusOverlapKind.Offset;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
string clause = (occ.PlainClause ?? "").Trim();
|
||||
if (clause.StartsWith("finishes", StringComparison.OrdinalIgnoreCase)
|
||||
|| clause.StartsWith("comes down", StringComparison.OrdinalIgnoreCase)
|
||||
|| clause.StartsWith("calms after", StringComparison.OrdinalIgnoreCase)
|
||||
|| clause.StartsWith("lets inspiration", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return HappinessStatusOverlapKind.Offset;
|
||||
}
|
||||
|
||||
return HappinessStatusOverlapKind.Hold;
|
||||
}
|
||||
|
||||
private static bool InterestCompletionHasStatus(Actor actor, string statusId)
|
||||
{
|
||||
if (actor == null || string.IsNullOrEmpty(statusId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return actor.hasStatus(statusId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Direct registry insert for harness happiness signals (skips drain filters).</summary>
|
||||
public static InterestCandidate RegisterHappinessSignal(
|
||||
Actor subject,
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public static class HappinessEventRouter
|
|||
_lastEffectId = "";
|
||||
_counterSubjectId = 0;
|
||||
_nextSequence = 1;
|
||||
InterestFeeds.OnHappinessRouterCleared();
|
||||
}
|
||||
|
||||
/// <summary>Push listener for spectator interest intake (in addition to <see cref="DrainSince"/>).</summary>
|
||||
|
|
@ -539,7 +540,8 @@ public static class HappinessEventRouter
|
|||
Context = entry.Context,
|
||||
InterestCategory = entry.InterestCategory,
|
||||
CanonicalMilestoneKey = entry.CanonicalMilestoneKey,
|
||||
StatusOverlapId = entry.StatusOverlapId
|
||||
StatusOverlapId = entry.StatusOverlapId,
|
||||
StatusOverlapKind = entry.StatusOverlapKind
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,17 @@ public enum HappinessOverlapPolicy
|
|||
Continuation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How a happiness <see cref="HappinessCatalogEntry.StatusOverlapId"/> relates to camera hold.
|
||||
/// Hold = onset claim (require live status); Offset = end-ping FixedDwell when status is gone.
|
||||
/// </summary>
|
||||
public enum HappinessStatusOverlapKind
|
||||
{
|
||||
None,
|
||||
Hold,
|
||||
Offset
|
||||
}
|
||||
|
||||
public enum HappinessRelationRole
|
||||
{
|
||||
None,
|
||||
|
|
@ -69,6 +80,7 @@ public sealed class HappinessCatalogEntry
|
|||
public float EventStrength = 40f;
|
||||
public string CanonicalMilestoneKey = "";
|
||||
public string StatusOverlapId = "";
|
||||
public HappinessStatusOverlapKind StatusOverlapKind = HappinessStatusOverlapKind.None;
|
||||
public string[] VariantsWithRelated = Array.Empty<string>();
|
||||
public string[] VariantsWithoutRelated = Array.Empty<string>();
|
||||
public bool IsFallback;
|
||||
|
|
@ -102,6 +114,7 @@ public sealed class HappinessOccurrence
|
|||
public string InterestCategory = "";
|
||||
public string CanonicalMilestoneKey = "";
|
||||
public string StatusOverlapId = "";
|
||||
public HappinessStatusOverlapKind StatusOverlapKind = HappinessStatusOverlapKind.None;
|
||||
public int SampledCount = 1;
|
||||
public int TotalAffectedCount = 1;
|
||||
public string CivEventKey = "";
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ internal static class HarnessScenarios
|
|||
case "camera_presentability":
|
||||
case "presentability":
|
||||
return CameraPresentability();
|
||||
case "status_overlap_camera":
|
||||
case "status_overlap":
|
||||
return StatusOverlapCamera();
|
||||
case "director_action_priority":
|
||||
case "action_priority":
|
||||
return DirectorActionPriority();
|
||||
|
|
@ -1025,6 +1028,76 @@ internal static class HarnessScenarios
|
|||
|
||||
Step("cp90", "fast_timing", value: "false"),
|
||||
Step("cp99", "snapshot"),
|
||||
Nested("cp_status_overlap", "status_overlap_camera"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status-overlap happiness must not FixedDwell-claim a camera-worthy status the unit lacks.
|
||||
/// Hold while status is live; offset end-pings still register briefly without it.
|
||||
/// </summary>
|
||||
private static List<HarnessCommand> StatusOverlapCamera()
|
||||
{
|
||||
return new List<HarnessCommand>
|
||||
{
|
||||
Step("so0", "dismiss_windows"),
|
||||
Step("so1", "wait_world"),
|
||||
Step("so2", "set_setting", expect: "enabled", value: "true"),
|
||||
Step("so3", "fast_timing", value: "true"),
|
||||
Step("so4", "spawn", asset: "human", count: 1),
|
||||
Step("so5", "pick_unit", asset: "human"),
|
||||
Step("so6", "spectator", value: "off"),
|
||||
Step("so7", "spectator", value: "on"),
|
||||
Step("so8", "focus", asset: "auto"),
|
||||
Step("so9", "interest_end_session"),
|
||||
Step("so9b", "interest_expire_pending", value: ""),
|
||||
Step("so9c", "force_live_feeds", value: "true"),
|
||||
|
||||
// Orphan onset: just_cursed without cursed status must not own the camera.
|
||||
Step("so10", "happiness_reset"),
|
||||
Step("so11", "drop_clear"),
|
||||
Step("so12", "happiness_remember_only", value: "just_cursed"),
|
||||
Step("so13", "interest_feeds_tick"),
|
||||
Step("so14", "assert", expect: "drop_contains", value: "status_overlap_absent"),
|
||||
Step("so15", "assert", expect: "interest_no_key", value: "just_cursed"),
|
||||
Step("so16", "assert", expect: "interest_no_key", value: "status:cursed"),
|
||||
|
||||
// Same class: possessed onset without status.
|
||||
Step("so20", "drop_clear"),
|
||||
Step("so21", "happiness_remember_only", value: "just_possessed"),
|
||||
Step("so22", "interest_feeds_tick"),
|
||||
Step("so23", "assert", expect: "drop_contains", value: "status_overlap_absent"),
|
||||
Step("so24", "assert", expect: "interest_no_key", value: "just_possessed"),
|
||||
|
||||
// Offset end-ping still registers without the status.
|
||||
Step("so30", "drop_clear"),
|
||||
Step("so31", "happiness_remember_only", value: "just_inspired"),
|
||||
Step("so32", "interest_feeds_tick"),
|
||||
Step("so33", "assert", expect: "interest_has_key", value: "just_inspired"),
|
||||
Step("so34", "interest_expire_pending", value: "just_inspired"),
|
||||
|
||||
// Live cursed status owns StatusPhase; happiness Continuation merges.
|
||||
// Short timer: cursed may not clear via finishStatusEffect (re-applies / sticky).
|
||||
Step("so40", "interest_end_session"),
|
||||
Step("so41", "interest_expire_pending", value: ""),
|
||||
Step("so42", "drop_clear"),
|
||||
Step("so43", "status_apply", value: "cursed", label: "0.4"),
|
||||
Step("so44", "assert", expect: "interest_has_key", value: "status:cursed"),
|
||||
Step("so45", "happiness_remember_only", value: "just_cursed"),
|
||||
Step("so46", "interest_feeds_tick"),
|
||||
Step("so47", "assert", expect: "interest_has_key", value: "status:cursed"),
|
||||
Step("so48", "assert", expect: "interest_no_key", value: "happiness:just_cursed"),
|
||||
Step("so49", "wait", wait: 1.1f),
|
||||
Step("so50", "assert", expect: "activity_status_active", value: "cursed", label: "false"),
|
||||
Step("so50b", "age_current", wait: 3f),
|
||||
Step("so50c", "director_run", wait: 1.2f),
|
||||
Step("so51", "interest_end_session"),
|
||||
Step("so52", "interest_expire_pending", value: "status:cursed"),
|
||||
Step("so53", "assert", expect: "interest_no_key", value: "status:cursed"),
|
||||
|
||||
Step("so90", "force_live_feeds", value: "false"),
|
||||
Step("so91", "fast_timing", value: "false"),
|
||||
Step("so99", "snapshot"),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"modVersion": "0.25.1",
|
||||
"title": "IdleSpectator event catalog",
|
||||
"updated": "2026-07-16",
|
||||
"note": "Authored IdleSpectator event catalog. Score ladder (EventStrength): 95-100 epic world (war/kingdom fall/major disaster); 88-94 major spectacle; 78-87 kingdom politics / peak grief; 70-77 life milestones (notice band); 55-69 notable beats; 40-54 warm social; 28-39 soft daily Signal; ≤25 B-only FX. decisions.action = infinitive after \"decides to\"; other domains use label/variants. scoring-model.json remains global formula (margins/multipliers).",
|
||||
"note": "Authored IdleSpectator event catalog. Score ladder (EventStrength): 95-100 epic world (war/kingdom fall/major disaster); 88-94 major spectacle; 78-87 kingdom politics / peak grief; 70-77 life milestones (notice band); 55-69 notable beats; 40-54 warm social; 28-39 soft daily Signal; \u226425 B-only FX. decisions.action = infinitive after \"decides to\"; other domains use label/variants. scoring-model.json remains global formula (margins/multipliers).",
|
||||
"decisions": [
|
||||
{
|
||||
"id": "find_lover",
|
||||
|
|
@ -299,7 +299,8 @@
|
|||
"eats and feels better",
|
||||
"enjoys a meal"
|
||||
],
|
||||
"statusOverlapId": "just_ate"
|
||||
"statusOverlapId": "just_ate",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "just_received_gift",
|
||||
|
|
@ -390,7 +391,8 @@
|
|||
"has a bad dream",
|
||||
"is troubled by a bad dream"
|
||||
],
|
||||
"statusOverlapId": "had_bad_dream"
|
||||
"statusOverlapId": "had_bad_dream",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "had_good_dream",
|
||||
|
|
@ -409,7 +411,8 @@
|
|||
"has a good dream",
|
||||
"dreams pleasantly"
|
||||
],
|
||||
"statusOverlapId": "had_good_dream"
|
||||
"statusOverlapId": "had_good_dream",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "had_nightmare",
|
||||
|
|
@ -428,7 +431,8 @@
|
|||
"has a nightmare",
|
||||
"is haunted by a nightmare"
|
||||
],
|
||||
"statusOverlapId": "had_nightmare"
|
||||
"statusOverlapId": "had_nightmare",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "slept_outside",
|
||||
|
|
@ -665,7 +669,8 @@
|
|||
"is smitten"
|
||||
],
|
||||
"canonicalMilestoneKey": "milestone_lover",
|
||||
"statusOverlapId": "fell_in_love"
|
||||
"statusOverlapId": "fell_in_love",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "just_had_child",
|
||||
|
|
@ -756,7 +761,8 @@
|
|||
"finishes laughing",
|
||||
"had a good laugh"
|
||||
],
|
||||
"statusOverlapId": "laughing"
|
||||
"statusOverlapId": "laughing",
|
||||
"statusOverlapKind": "offset"
|
||||
},
|
||||
{
|
||||
"id": "just_sang",
|
||||
|
|
@ -775,7 +781,8 @@
|
|||
"finishes singing",
|
||||
"sings with feeling"
|
||||
],
|
||||
"statusOverlapId": "singing"
|
||||
"statusOverlapId": "singing",
|
||||
"statusOverlapKind": "offset"
|
||||
},
|
||||
{
|
||||
"id": "just_swore",
|
||||
|
|
@ -794,7 +801,8 @@
|
|||
"finishes swearing",
|
||||
"lets out a string of curses"
|
||||
],
|
||||
"statusOverlapId": "swearing"
|
||||
"statusOverlapId": "swearing",
|
||||
"statusOverlapKind": "offset"
|
||||
},
|
||||
{
|
||||
"id": "just_cried",
|
||||
|
|
@ -813,7 +821,8 @@
|
|||
"finishes crying",
|
||||
"cries it out"
|
||||
],
|
||||
"statusOverlapId": "crying"
|
||||
"statusOverlapId": "crying",
|
||||
"statusOverlapKind": "offset"
|
||||
},
|
||||
{
|
||||
"id": "just_talked_gossip",
|
||||
|
|
@ -850,7 +859,8 @@
|
|||
"is startled",
|
||||
"jumps in surprise"
|
||||
],
|
||||
"statusOverlapId": "surprised"
|
||||
"statusOverlapId": "surprised",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "just_born",
|
||||
|
|
@ -887,7 +897,8 @@
|
|||
"is magnetized",
|
||||
"feels a magnetic pull"
|
||||
],
|
||||
"statusOverlapId": "magnetized"
|
||||
"statusOverlapId": "magnetized",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "just_forced_power",
|
||||
|
|
@ -924,7 +935,8 @@
|
|||
"is possessed",
|
||||
"falls under possession"
|
||||
],
|
||||
"statusOverlapId": "possessed"
|
||||
"statusOverlapId": "possessed",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "strange_urge",
|
||||
|
|
@ -943,7 +955,8 @@
|
|||
"feels a strange urge",
|
||||
"is gripped by a strange urge"
|
||||
],
|
||||
"statusOverlapId": "strange_urge"
|
||||
"statusOverlapId": "strange_urge",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "just_had_tantrum",
|
||||
|
|
@ -962,7 +975,8 @@
|
|||
"finishes a tantrum",
|
||||
"calms after a tantrum"
|
||||
],
|
||||
"statusOverlapId": "tantrum"
|
||||
"statusOverlapId": "tantrum",
|
||||
"statusOverlapKind": "offset"
|
||||
},
|
||||
{
|
||||
"id": "just_felt_the_divine",
|
||||
|
|
@ -999,7 +1013,8 @@
|
|||
"is enchanted",
|
||||
"feels an enchantment take hold"
|
||||
],
|
||||
"statusOverlapId": "enchanted"
|
||||
"statusOverlapId": "enchanted",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "just_inspired",
|
||||
|
|
@ -1018,7 +1033,8 @@
|
|||
"comes down from inspiration",
|
||||
"lets inspiration settle into contentment"
|
||||
],
|
||||
"statusOverlapId": "inspired"
|
||||
"statusOverlapId": "inspired",
|
||||
"statusOverlapKind": "offset"
|
||||
},
|
||||
{
|
||||
"id": "wrote_book",
|
||||
|
|
@ -1182,7 +1198,8 @@
|
|||
"is cursed",
|
||||
"falls under a curse"
|
||||
],
|
||||
"statusOverlapId": "cursed"
|
||||
"statusOverlapId": "cursed",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "starving",
|
||||
|
|
@ -1201,7 +1218,8 @@
|
|||
"is starving",
|
||||
"goes hungry"
|
||||
],
|
||||
"statusOverlapId": "starving"
|
||||
"statusOverlapId": "starving",
|
||||
"statusOverlapKind": "hold"
|
||||
},
|
||||
{
|
||||
"id": "conquered_city",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "IdleSpectator",
|
||||
"author": "dazed",
|
||||
"version": "0.25.47",
|
||||
"description": "AFK Idle Spectator (I) + Lore (L). Focus hold repair mid-idle.",
|
||||
"version": "0.25.50",
|
||||
"description": "AFK Idle Spectator (I) + Lore (L). Status-overlap camera hold gate.",
|
||||
"GUID": "com.dazed.idlespectator"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue