dont dedupe important verbs
This commit is contained in:
parent
8ab8a9880a
commit
7068ac44c5
6 changed files with 140 additions and 30 deletions
|
|
@ -849,7 +849,7 @@ public static class ActivityLog
|
|||
private static bool ShouldSuppressSameVerb(long subjectId, string key)
|
||||
{
|
||||
string verb = ResolveVerbKey(key);
|
||||
if (string.IsNullOrEmpty(verb))
|
||||
if (string.IsNullOrEmpty(verb) || ActivityVerbMap.IsSameVerbCooldownExempt(verb))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,39 @@ public static class ActivityVerbMap
|
|||
return !string.IsNullOrEmpty(Resolve(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// High-signal verbs skip the ActivityLog same-verb cooldown (combat, danger, romance, etc.).
|
||||
/// Spammy verbs like play/move/work still dedupe.
|
||||
/// </summary>
|
||||
public static bool IsSameVerbCooldownExempt(string verb)
|
||||
{
|
||||
if (string.IsNullOrEmpty(verb))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (verb)
|
||||
{
|
||||
case "fight":
|
||||
case "hunt":
|
||||
case "flee":
|
||||
case "lover":
|
||||
case "steal":
|
||||
case "loot":
|
||||
case "heal":
|
||||
case "ignite":
|
||||
case "extinguish":
|
||||
case "fire":
|
||||
case "harvest_life":
|
||||
case "possessed":
|
||||
case "confused":
|
||||
case "strange_urge":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<string> EnumerateLiveTaskIds()
|
||||
{
|
||||
return ActivityAssetCatalog.EnumerateLiveTaskIds();
|
||||
|
|
|
|||
|
|
@ -2006,15 +2006,20 @@ public static class AgentHarness
|
|||
}
|
||||
case "dossier_task_refresh":
|
||||
{
|
||||
// Stale focus snapshot must recover: blank chip, then sync from live AI.
|
||||
// Stale focus snapshot must recover: blank dossier TaskText, then sync from live AI.
|
||||
// Empty live AI is ok - chip may keep a sticky prior label (never cleared on gaps).
|
||||
Actor focus = MoveCamera.hasFocusUnit() ? MoveCamera._focus_unit : null;
|
||||
string live = UnitDossier.ReadLiveTask(focus);
|
||||
string after = WatchCaption.HarnessBlankAndRefreshTask();
|
||||
string chip = WatchCaption.ShownTaskChipText;
|
||||
bool chipMatches = string.IsNullOrEmpty(live)
|
||||
? string.IsNullOrEmpty(chip)
|
||||
: !string.IsNullOrEmpty(chip);
|
||||
pass = after == live && chipMatches;
|
||||
if (string.IsNullOrEmpty(live))
|
||||
{
|
||||
pass = string.IsNullOrEmpty(after);
|
||||
detail = $"live_empty dossier='{after}' sticky_chip='{chip}'";
|
||||
break;
|
||||
}
|
||||
|
||||
pass = after == live && !string.IsNullOrEmpty(chip);
|
||||
detail = $"live='{live}' dossier='{after}' chip='{chip}'";
|
||||
break;
|
||||
}
|
||||
|
|
@ -2292,6 +2297,32 @@ public static class AgentHarness
|
|||
$"added={added} want_added=1 suppressed={suppressed} want_suppressed>={keys.Length - 1} keys={keys.Length}";
|
||||
break;
|
||||
}
|
||||
case "activity_verb_exempt":
|
||||
{
|
||||
// Combat / high-signal verbs skip same-verb cooldown.
|
||||
Actor focus = MoveCamera.hasFocusUnit() ? MoveCamera._focus_unit : null;
|
||||
if (focus == null || !focus.isAlive())
|
||||
{
|
||||
pass = false;
|
||||
detail = "no_live_focus";
|
||||
break;
|
||||
}
|
||||
|
||||
ActivityLog.ResetSampleCounters();
|
||||
string[] keys =
|
||||
{
|
||||
"task_attack",
|
||||
"punch_a_building",
|
||||
"tantrum",
|
||||
"task_unit_attack"
|
||||
};
|
||||
int added = ActivityLog.HarnessSpamSameVerb(focus, keys);
|
||||
int suppressed = ActivityLog.VerbSuppressedSinceClear;
|
||||
pass = added == keys.Length && suppressed == 0;
|
||||
detail =
|
||||
$"added={added} want_added={keys.Length} suppressed={suppressed} want_suppressed=0";
|
||||
break;
|
||||
}
|
||||
case "activity_prose_sentence":
|
||||
{
|
||||
// Ensure mood/task labels become sentences, not "{name} laughing".
|
||||
|
|
|
|||
|
|
@ -614,6 +614,7 @@ internal static class HarnessScenarios
|
|||
Step("act13f", "assert", expect: "activity_taxonomy_audit"),
|
||||
Step("act13g", "assert", expect: "activity_status_audit"),
|
||||
Step("act13h", "assert", expect: "activity_verb_dedupe"),
|
||||
Step("act13h2", "assert", expect: "activity_verb_exempt"),
|
||||
Step("act13i", "assert", expect: "dossier_task_refresh"),
|
||||
Step("act14", "activity_force", asset: "laughing", label: "Laughing", count: 1, value: "angle"),
|
||||
Step("act14b", "assert", expect: "activity_log_contains", value: "celestial"),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ public static class WatchCaption
|
|||
private const float SexSize = 14f;
|
||||
private const float LiveMax = 44f;
|
||||
private const float ChipIcon = 12f;
|
||||
/// <summary>Fixed nametag task label slot so text swaps do not autofit-tick the header.</summary>
|
||||
private const float TaskLabelW = 72f;
|
||||
private const float TraitIcon = 12f;
|
||||
private const float HistoryIcon = 10f;
|
||||
private const float HistoryColMinW = 118f;
|
||||
|
|
@ -623,6 +625,7 @@ public static class WatchCaption
|
|||
|
||||
/// <summary>
|
||||
/// Keep the nametag task chip in sync with live AI (focus snapshot can miss brief no-task gaps).
|
||||
/// Never clears the chip on empty AI gaps - only replaces text when a new task is present.
|
||||
/// </summary>
|
||||
private static void RefreshLiveTask()
|
||||
{
|
||||
|
|
@ -638,19 +641,30 @@ public static class WatchCaption
|
|||
}
|
||||
|
||||
string live = UnitDossier.ReadLiveTask(actor);
|
||||
// Hold the last shown label through brief no-task gaps so autofit does not collapse.
|
||||
if (string.IsNullOrEmpty(live))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string prev = _current.TaskText ?? "";
|
||||
if (live == prev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasShowing = _taskText != null && _taskText.gameObject.activeSelf;
|
||||
_current.TaskText = live;
|
||||
ApplyTaskChip(live);
|
||||
ReplaceTaskChip(live);
|
||||
|
||||
// First appearance needs layout; later swaps only change text inside a fixed-width slot.
|
||||
if (!wasShowing)
|
||||
{
|
||||
bool hasBody = _current.UnitId != 0;
|
||||
bool hasReason = _reasonText != null && _reasonText.gameObject.activeSelf;
|
||||
Relayout(hasBody, CountActiveTraitSlots(), hasTask: true, hasReason, CountActiveHistorySlots());
|
||||
}
|
||||
|
||||
bool hasBody = _current.UnitId != 0;
|
||||
bool hasTask = !string.IsNullOrEmpty(live);
|
||||
bool hasReason = _reasonText != null && _reasonText.gameObject.activeSelf;
|
||||
Relayout(hasBody, CountActiveTraitSlots(), hasTask, hasReason, CountActiveHistorySlots());
|
||||
BringHeaderFront();
|
||||
}
|
||||
|
||||
|
|
@ -678,29 +692,61 @@ public static class WatchCaption
|
|||
return actor;
|
||||
}
|
||||
|
||||
private static void ApplyTaskChip(string taskText)
|
||||
private static string TruncateTaskLabel(string taskText)
|
||||
{
|
||||
bool hasTask = !string.IsNullOrEmpty(taskText);
|
||||
if (string.IsNullOrEmpty(taskText))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return taskText.Length > 14 ? taskText.Substring(0, 13) + "..." : taskText;
|
||||
}
|
||||
|
||||
/// <summary>Show or replace the task chip without ever blanking mid-update.</summary>
|
||||
private static void ReplaceTaskChip(string taskText)
|
||||
{
|
||||
if (string.IsNullOrEmpty(taskText))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_taskIcon != null)
|
||||
{
|
||||
HudIcons.Apply(_taskIcon, hasTask ? HudIcons.Task() : null);
|
||||
_taskIcon.gameObject.SetActive(hasTask);
|
||||
HudIcons.Apply(_taskIcon, HudIcons.Task());
|
||||
_taskIcon.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
if (_taskText != null)
|
||||
{
|
||||
string task = hasTask ? taskText : "";
|
||||
if (task.Length > 14)
|
||||
{
|
||||
task = task.Substring(0, 13) + "...";
|
||||
}
|
||||
|
||||
_taskText.text = task;
|
||||
_taskText.gameObject.SetActive(hasTask);
|
||||
_taskText.text = TruncateTaskLabel(taskText);
|
||||
_taskText.gameObject.SetActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Harness: blank the nametag task, then sync from live AI.</summary>
|
||||
private static void ApplyTaskChip(string taskText)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(taskText))
|
||||
{
|
||||
ReplaceTaskChip(taskText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_taskIcon != null)
|
||||
{
|
||||
HudIcons.Apply(_taskIcon, null);
|
||||
_taskIcon.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
if (_taskText != null)
|
||||
{
|
||||
_taskText.text = "";
|
||||
_taskText.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Harness: simulate a stale empty snapshot, then sync from live AI (replace, never stay blank if live has a task).
|
||||
/// </summary>
|
||||
public static string HarnessBlankAndRefreshTask()
|
||||
{
|
||||
if (_current != null)
|
||||
|
|
@ -708,7 +754,7 @@ public static class WatchCaption
|
|||
_current.TaskText = "";
|
||||
}
|
||||
|
||||
ApplyTaskChip("");
|
||||
// Do not hide the chip here - RefreshLiveTask must replace in place when live task exists.
|
||||
RefreshLiveTask();
|
||||
return _current != null ? (_current.TaskText ?? "") : "";
|
||||
}
|
||||
|
|
@ -1465,9 +1511,8 @@ public static class WatchCaption
|
|||
_taskText.gameObject.SetActive(true);
|
||||
PlaceLeftChip(_taskIcon.rectTransform, x, yFromTop + 3f, ChipIcon, ChipIcon);
|
||||
x += ChipIcon + 1f;
|
||||
float taskW = Mathf.Clamp(MeasureTextWidth(_taskText, 36f), 20f, 72f);
|
||||
PlaceLeftChip(_taskText.rectTransform, x, yFromTop, taskW, HeaderH);
|
||||
x += taskW + 4f;
|
||||
PlaceLeftChip(_taskText.rectTransform, x, yFromTop, TaskLabelW, HeaderH);
|
||||
x += TaskLabelW + 4f;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1521,7 +1566,7 @@ public static class WatchCaption
|
|||
if (hasTask && _taskIcon != null && _taskText != null)
|
||||
{
|
||||
x += ChipIcon + 1f;
|
||||
x += Mathf.Clamp(MeasureTextWidth(_taskText, 36f), 20f, 72f) + 4f;
|
||||
x += TaskLabelW + 4f;
|
||||
}
|
||||
|
||||
if (_sexIcon != null && _sexIcon.gameObject.activeSelf)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "IdleSpectator",
|
||||
"author": "dazed",
|
||||
"version": "0.12.100",
|
||||
"version": "0.12.103",
|
||||
"description": "AFK Idle Spectator (I) + Lore (L). Detailed living activity prose by default.",
|
||||
"GUID": "com.dazed.idlespectator"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue