From 72a4a2e164450ea49881320c267fee79843348f5 Mon Sep 17 00:00:00 2001 From: DazedAnon Date: Wed, 15 Jul 2026 12:08:38 -0500 Subject: [PATCH] Enhance activity action handling by introducing companion verb checks in ActivityClauseAssembler and updating related methods for improved context management. Refactor ActivityLog to utilize new parameters for action logging, ensuring clarity in actor and place interactions. Increment version in mod.json to reflect these changes. --- IdleSpectator/ActivityClauseAssembler.cs | 20 ++++++-- IdleSpectator/ActivityInterestTable.cs | 14 ++++++ IdleSpectator/ActivityLog.cs | 22 +++++++-- IdleSpectator/ActorActivityPatches.cs | 59 +++++++++++++++--------- IdleSpectator/AgentHarness.cs | 45 ++++++++++++++++-- IdleSpectator/mod.json | 2 +- 6 files changed, 128 insertions(+), 34 deletions(-) diff --git a/IdleSpectator/ActivityClauseAssembler.cs b/IdleSpectator/ActivityClauseAssembler.cs index 0370889..09ef87c 100644 --- a/IdleSpectator/ActivityClauseAssembler.cs +++ b/IdleSpectator/ActivityClauseAssembler.cs @@ -48,20 +48,34 @@ public static class ActivityClauseAssembler action = StripToken(action, "{job}"); string subject = BuildSubject(ctx); - action = BindSingletonCompanions(ctx, action); + string verb = ActivityVerbMap.Resolve(key); + if (IsCompanionVerb(verb)) + { + action = BindSingletonCompanions(ctx, action); + } // Traits stay on the dossier chips - they only salt variety below, never named here. - string target = BuildObservedTargetClause(ctx, action); + string target = BuildObservedTargetClause(ctx, action, verb); string carrying = BuildCarryingClause(ctx, action); string pressure = BuildPressureClause(ctx, action + target); string line = subject + " " + action + target + carrying + pressure; return CollapseSpaces(line).Trim(); } - private static string BuildObservedTargetClause(ActivityContext ctx, string action) + private static bool IsCompanionVerb(string verb) + { + return verb == "social" + || verb == "lover" + || verb == "cry" + || verb == "swear" + || verb == "group"; + } + + private static string BuildObservedTargetClause(ActivityContext ctx, string action, string verb) { if (ctx == null + || !IsCompanionVerb(verb) || !ctx.TargetIsActor || string.IsNullOrEmpty(ctx.TargetName) || ctx.InCombat diff --git a/IdleSpectator/ActivityInterestTable.cs b/IdleSpectator/ActivityInterestTable.cs index c8f0863..52c586a 100644 --- a/IdleSpectator/ActivityInterestTable.cs +++ b/IdleSpectator/ActivityInterestTable.cs @@ -218,6 +218,20 @@ public static class ActivityInterestTable return !string.IsNullOrEmpty(name) ? name : place; } + /// Living actor interactee name only - never a building/place label. + public static string ActorTargetName(Actor actor) + { + ResolveTarget(actor, out string name, out bool isActor, out _); + return isActor ? name : ""; + } + + /// Building/tile/settlement label only - never an actor name. + public static string PlaceOrObjectLabel(Actor actor) + { + ResolveTarget(actor, out _, out bool isActor, out string place); + return isActor ? "" : (place ?? ""); + } + /// /// Prefer live actor targets (by name). Places/buildings stay as type labels. /// diff --git a/IdleSpectator/ActivityLog.cs b/IdleSpectator/ActivityLog.cs index 41bad56..368e17a 100644 --- a/IdleSpectator/ActivityLog.cs +++ b/IdleSpectator/ActivityLog.cs @@ -237,7 +237,16 @@ public static class ActivityLog NoteTask(actor, taskId); } - public static void NoteActionBeat(Actor actor, string actionKey, string rawFact, string target) + /// + /// Curated behaviour beat. is a living unit name only; + /// is a building/place/object label and never becomes a companion. + /// + public static void NoteActionBeat( + Actor actor, + string actionKey, + string rawFact, + string actorTarget = "", + string placeHint = "") { if (actor == null || !actor.isAlive() || string.IsNullOrEmpty(actionKey)) { @@ -267,14 +276,19 @@ public static class ActivityLog LastBeatAt[id] = now; ActivityContext ctx = ActivityInterestTable.BuildContext(actor, actionKey); - string taskId = ActivityInterestTable.SafeTaskId(actor); string jobId = ctx.JobId; - if (string.IsNullOrEmpty(ctx.TargetName) && !string.IsNullOrEmpty(target)) + + if (string.IsNullOrEmpty(ctx.TargetName) && !string.IsNullOrEmpty(actorTarget)) { - ctx.TargetName = target; + ctx.TargetName = actorTarget; ctx.TargetIsActor = true; } + if (string.IsNullOrEmpty(ctx.PlaceLabel) && !string.IsNullOrEmpty(placeHint)) + { + ctx.PlaceLabel = placeHint; + } + if (actionKey == "BehUnloadResources" || actionKey == "BehThrowResources") { if (string.IsNullOrEmpty(ctx.CarryingLabel)) diff --git a/IdleSpectator/ActorActivityPatches.cs b/IdleSpectator/ActorActivityPatches.cs index c6edfb2..beae0d0 100644 --- a/IdleSpectator/ActorActivityPatches.cs +++ b/IdleSpectator/ActorActivityPatches.cs @@ -32,13 +32,18 @@ public static class ActorActivityPatches return; } - string target = ActivityInterestTable.TargetLabel(pActor); - if (string.IsNullOrEmpty(target)) + string obj = ActivityInterestTable.PlaceOrObjectLabel(pActor); + if (string.IsNullOrEmpty(obj)) { - target = "flower"; + obj = "flower"; } - ActivityLog.NoteActionBeat(pActor, "BehPollinate", "Pollinates " + target, target); + ActivityLog.NoteActionBeat( + pActor, + "BehPollinate", + "Pollinates " + obj, + actorTarget: "", + placeHint: obj); } [HarmonyPatch(typeof(BehUnloadResources), nameof(BehUnloadResources.execute))] @@ -69,7 +74,8 @@ public static class ActorActivityPatches string fact = string.IsNullOrEmpty(carrying) ? "Unloads resources" : "Unloads " + carrying; - ActivityLog.NoteActionBeat(pActor, "BehUnloadResources", fact, "town"); + // Place comes from live context / city - never as a companion target. + ActivityLog.NoteActionBeat(pActor, "BehUnloadResources", fact); } [HarmonyPatch(typeof(BehAttackActorHuntingTarget), nameof(BehAttackActorHuntingTarget.execute))] @@ -81,12 +87,12 @@ public static class ActorActivityPatches return; } - string target = ActivityInterestTable.TargetLabel(pActor); + string actorTarget = ActivityInterestTable.ActorTargetName(pActor); ActivityLog.NoteActionBeat( pActor, "BehAttackActorHuntingTarget", - string.IsNullOrEmpty(target) ? "Hunts prey" : "Hunts " + target, - target); + string.IsNullOrEmpty(actorTarget) ? "Hunts prey" : "Hunts " + actorTarget, + actorTarget: actorTarget); } [HarmonyPatch(typeof(BehCityActorRemoveFire), nameof(BehCityActorRemoveFire.execute))] @@ -98,7 +104,12 @@ public static class ActorActivityPatches return; } - ActivityLog.NoteActionBeat(pActor, "BehCityActorRemoveFire", "Fights fire", "blaze"); + ActivityLog.NoteActionBeat( + pActor, + "BehCityActorRemoveFire", + "Fights fire", + actorTarget: "", + placeHint: "blaze"); } [HarmonyPatch(typeof(BehTryToSocialize), nameof(BehTryToSocialize.execute))] @@ -110,12 +121,12 @@ public static class ActorActivityPatches return; } - string target = ActivityInterestTable.TargetLabel(pActor); + string actorTarget = ActivityInterestTable.ActorTargetName(pActor); ActivityLog.NoteActionBeat( pActor, "BehTryToSocialize", - string.IsNullOrEmpty(target) ? "Tries to socialize" : "Tries to socialize with " + target, - target); + string.IsNullOrEmpty(actorTarget) ? "Tries to socialize" : "Tries to socialize with " + actorTarget, + actorTarget: actorTarget); } [HarmonyPatch(typeof(BehPlantCrops), nameof(BehPlantCrops.execute))] @@ -127,7 +138,7 @@ public static class ActorActivityPatches return; } - ActivityLog.NoteActionBeat(pActor, "BehPlantCrops", "Plants crops", ""); + ActivityLog.NoteActionBeat(pActor, "BehPlantCrops", "Plants crops"); } [HarmonyPatch(typeof(BehCityActorFertilizeCrop), nameof(BehCityActorFertilizeCrop.execute))] @@ -139,7 +150,7 @@ public static class ActorActivityPatches return; } - ActivityLog.NoteActionBeat(pActor, "BehCityActorFertilizeCrop", "Fertilizes crops", ""); + ActivityLog.NoteActionBeat(pActor, "BehCityActorFertilizeCrop", "Fertilizes crops"); } [HarmonyPatch(typeof(BehThrowResources), nameof(BehThrowResources.execute))] @@ -170,7 +181,7 @@ public static class ActorActivityPatches string fact = string.IsNullOrEmpty(carrying) ? "Throws resources" : "Throws " + carrying; - ActivityLog.NoteActionBeat(pActor, "BehThrowResources", fact, ""); + ActivityLog.NoteActionBeat(pActor, "BehThrowResources", fact); } [HarmonyPatch(typeof(BehBuildTarget), nameof(BehBuildTarget.execute))] @@ -182,12 +193,13 @@ public static class ActorActivityPatches return; } - string target = ActivityInterestTable.TargetLabel(pActor); + string obj = ActivityInterestTable.PlaceOrObjectLabel(pActor); ActivityLog.NoteActionBeat( pActor, "BehBuildTarget", - string.IsNullOrEmpty(target) ? "Works a build" : "Builds " + target, - target); + string.IsNullOrEmpty(obj) ? "Works a build" : "Builds " + obj, + actorTarget: "", + placeHint: obj); } [HarmonyPatch(typeof(BehPoopOutside), nameof(BehPoopOutside.execute))] @@ -199,7 +211,7 @@ public static class ActorActivityPatches return; } - ActivityLog.NoteActionBeat(pActor, "BehPoopOutside", "Private moment outdoors", ""); + ActivityLog.NoteActionBeat(pActor, "BehPoopOutside", "Private moment outdoors"); } [HarmonyPatch(typeof(BehPoopInside), nameof(BehPoopInside.execute))] @@ -211,7 +223,7 @@ public static class ActorActivityPatches return; } - ActivityLog.NoteActionBeat(pActor, "BehPoopInside", "Private moment indoors", ""); + ActivityLog.NoteActionBeat(pActor, "BehPoopInside", "Private moment indoors"); } [HarmonyPatch(typeof(BehBoatFishing), nameof(BehBoatFishing.execute))] @@ -223,7 +235,7 @@ public static class ActorActivityPatches return; } - ActivityLog.NoteActionBeat(pActor, "BehBoatFishing", "Fishes from the boat", ""); + ActivityLog.NoteActionBeat(pActor, "BehBoatFishing", "Fishes from the boat"); } [HarmonyPatch(typeof(BehBoatCollectFish), nameof(BehBoatCollectFish.execute))] @@ -235,7 +247,7 @@ public static class ActorActivityPatches return; } - ActivityLog.NoteActionBeat(pActor, "BehBoatCollectFish", "Collects the catch", ""); + ActivityLog.NoteActionBeat(pActor, "BehBoatCollectFish", "Collects the catch"); } [HarmonyPatch(typeof(BehClaimZoneForCityActorBorder), nameof(BehClaimZoneForCityActorBorder.execute))] @@ -264,6 +276,7 @@ public static class ActorActivityPatches pActor, "BehClaimZoneForCityActorBorder", string.IsNullOrEmpty(place) ? "Claims border land" : "Claims border land for " + place, - place); + actorTarget: "", + placeHint: place); } } diff --git a/IdleSpectator/AgentHarness.cs b/IdleSpectator/AgentHarness.cs index 2c5fd2e..1b5b729 100644 --- a/IdleSpectator/AgentHarness.cs +++ b/IdleSpectator/AgentHarness.cs @@ -2296,6 +2296,43 @@ public static class AgentHarness socialBindOk = socialBindOk && withTomi <= 1; + // Building/place labels must never become companion "with …" on work/unload. + ActivityContext bonfireCtx = ActivityContext.ForHarness( + actorName, + "bonfire", + "human", + place: "bonfire"); + ActivityProse.Format( + ActivityKind.TaskStart, + "type_bonfire", + bonfireCtx, + "Works a bonfire", + 12, + 1, + out string bonfireLine, + out _); + bool bonfireOk = !string.IsNullOrEmpty(bonfireLine) + && bonfireLine.IndexOf("with bonfire", System.StringComparison.OrdinalIgnoreCase) < 0; + + ActivityContext unloadTownCtx = ActivityContext.ForHarness( + actorName, + "town", + "human", + place: "Riverhold", + carrying: "wheat"); + ActivityProse.Format( + ActivityKind.ActionBeat, + "BehUnloadResources", + unloadTownCtx, + "Unloads wheat", + 13, + 1, + out string unloadTownLine, + out _); + bool unloadTownOk = !string.IsNullOrEmpty(unloadTownLine) + && unloadTownLine.IndexOf("wheat", System.StringComparison.OrdinalIgnoreCase) >= 0 + && unloadTownLine.IndexOf("with town", System.StringComparison.OrdinalIgnoreCase) < 0; + // Taxonomy kingdoms like "insect" must never become place clauses. string insectPlace = ActivityInterestTable.PickPlaceLabel( "insect", @@ -2377,17 +2414,19 @@ public static class AgentHarness && rhinoLine.IndexOf(" the rhino", System.StringComparison.OrdinalIgnoreCase) < 0; pass = dogOk && catOk && humanOk && unloadOk && distinct && placeRejectOk && placeKeepOk - && insectRejectOk && frogOk && frogJumpOk && liveTaxOk && rhinoOk && socialBindOk; + && insectRejectOk && frogOk && frogJumpOk && liveTaxOk && rhinoOk && socialBindOk + && bonfireOk && unloadTownOk; detail = $"dogOk={dogOk} catOk={catOk} humanOk={humanOk} unloadOk={unloadOk} distinct={distinct} " + $"placeRejectOk={placeRejectOk} placeKeepOk={placeKeepOk} insectRejectOk={insectRejectOk} " + $"frogOk={frogOk} frogJumpOk={frogJumpOk} liveTaxOk={liveTaxOk} rhinoOk={rhinoOk} " - + $"socialBindOk={socialBindOk} " + + $"socialBindOk={socialBindOk} bonfireOk={bonfireOk} unloadTownOk={unloadTownOk} " + $"frogFamily={frogCtx.Family} frogClass={frogCtx.TaxonomicClass} " + $"dogFamily={dogCtxLive.Family} dogTaxFamily={dogCtxLive.TaxonomicFamily} " + $"dog='{dogLine}' cat='{catLine}' human='{humanLine}' unload='{unloadLine}' " + $"buffalo='{buffaloLine}' buffaloTown='{buffaloTownLine}' beetle='{beetleLine}' " - + $"frog='{frogLine}' frogJump='{frogJumpLine}' rhino='{rhinoLine}' social='{socialLine}'"; + + $"frog='{frogLine}' frogJump='{frogJumpLine}' rhino='{rhinoLine}' social='{socialLine}' " + + $"bonfire='{bonfireLine}' unloadTown='{unloadTownLine}'"; break; } case "activity_taxonomy_audit": diff --git a/IdleSpectator/mod.json b/IdleSpectator/mod.json index 8b1f024..210f830 100644 --- a/IdleSpectator/mod.json +++ b/IdleSpectator/mod.json @@ -1,7 +1,7 @@ { "name": "IdleSpectator", "author": "dazed", - "version": "0.12.90", + "version": "0.12.91", "description": "AFK Idle Spectator (I) + Lore (L). Detailed living activity prose by default.", "GUID": "com.dazed.idlespectator" }