Enhance Lore interactions and improve dismiss behavior.
- Allow multiple named entries for grave force command on the same tile - Update dismiss behavior to clear Lore and dossier while keeping idle off - Introduce new methods for managing dismiss grace period - Refactor focus handling for fallen subjects and their killers - Increment version to 0.28.13 in mod.json
This commit is contained in:
parent
c11b87aa2e
commit
8a4314be6f
7 changed files with 181 additions and 55 deletions
|
|
@ -1846,9 +1846,16 @@ public static class AgentHarness
|
||||||
|
|
||||||
case "grave_force":
|
case "grave_force":
|
||||||
{
|
{
|
||||||
// value=name, count=entries on same tile, tier="dx,dy" tile offset from focus/camera
|
// value=name or "A,B,C" (same tile), count=entries, tier="dx,dy" offset from focus/camera.
|
||||||
string gName = !string.IsNullOrEmpty(cmd.value) ? cmd.value : "GraveDummy";
|
// Comma names / count>1 share one captured tile so a walking focus cannot split the stack.
|
||||||
int n = Math.Max(1, cmd.count);
|
string rawName = !string.IsNullOrEmpty(cmd.value) ? cmd.value : "GraveDummy";
|
||||||
|
string[] named = rawName.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
for (int i = 0; i < named.Length; i++)
|
||||||
|
{
|
||||||
|
named[i] = named[i].Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = named.Length > 1 ? named.Length : Math.Max(1, cmd.count);
|
||||||
Vector3 pos = Vector3.zero;
|
Vector3 pos = Vector3.zero;
|
||||||
if (MoveCamera.hasFocusUnit() && MoveCamera._focus_unit != null)
|
if (MoveCamera.hasFocusUnit() && MoveCamera._focus_unit != null)
|
||||||
{
|
{
|
||||||
|
|
@ -1882,7 +1889,9 @@ public static class AgentHarness
|
||||||
int before = GraveMarkers.EntryCountAt(tx, ty);
|
int before = GraveMarkers.EntryCountAt(tx, ty);
|
||||||
for (int i = 0; i < n; i++)
|
for (int i = 0; i < n; i++)
|
||||||
{
|
{
|
||||||
string nm = n > 1 ? gName + i : gName;
|
string nm = named.Length > 1
|
||||||
|
? named[i]
|
||||||
|
: (n > 1 ? rawName + i : rawName);
|
||||||
long id = Chronicle.ForceOrphanHistory(
|
long id = Chronicle.ForceOrphanHistory(
|
||||||
nm,
|
nm,
|
||||||
!string.IsNullOrEmpty(cmd.asset) ? cmd.asset : "human",
|
!string.IsNullOrEmpty(cmd.asset) ? cmd.asset : "human",
|
||||||
|
|
@ -2143,12 +2152,25 @@ public static class AgentHarness
|
||||||
case "inspect_dismiss":
|
case "inspect_dismiss":
|
||||||
{
|
{
|
||||||
InspectUi.DismissAll();
|
InspectUi.DismissAll();
|
||||||
_cmdOk++;
|
bool cleared = !SpectatorMode.Active
|
||||||
|
&& !Chronicle.HudVisible
|
||||||
|
&& !WatchCaption.Visible
|
||||||
|
&& !ChronicleHud.GraveStackScopeActive
|
||||||
|
&& !InspectUi.GraveSessionActive;
|
||||||
|
if (cleared)
|
||||||
|
{
|
||||||
|
_cmdOk++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_cmdFail++;
|
||||||
|
}
|
||||||
|
|
||||||
Emit(
|
Emit(
|
||||||
cmd,
|
cmd,
|
||||||
ok: true,
|
cleared,
|
||||||
detail:
|
detail:
|
||||||
$"graveSession={InspectUi.GraveSessionActive} lore={Chronicle.HudVisible} scope={ChronicleHud.GraveStackScopeActive}");
|
$"cleared={cleared} idle={SpectatorMode.Active} lore={Chronicle.HudVisible} dossier={WatchCaption.Visible} scope={ChronicleHud.GraveStackScopeActive} graveSession={InspectUi.GraveSessionActive}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6127,6 +6149,17 @@ public static class AgentHarness
|
||||||
|
|
||||||
switch (expect)
|
switch (expect)
|
||||||
{
|
{
|
||||||
|
case "inspect_cleared":
|
||||||
|
{
|
||||||
|
pass = !SpectatorMode.Active
|
||||||
|
&& !Chronicle.HudVisible
|
||||||
|
&& !WatchCaption.Visible
|
||||||
|
&& !ChronicleHud.GraveStackScopeActive
|
||||||
|
&& !InspectUi.GraveSessionActive;
|
||||||
|
detail =
|
||||||
|
$"idle={SpectatorMode.Active} lore={Chronicle.HudVisible} dossier={WatchCaption.Visible} scope={ChronicleHud.GraveStackScopeActive} graveSession={InspectUi.GraveSessionActive}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "idle":
|
case "idle":
|
||||||
case "spectator":
|
case "spectator":
|
||||||
{
|
{
|
||||||
|
|
@ -7160,10 +7193,32 @@ public static class AgentHarness
|
||||||
ChronicleEntry latest = Chronicle.Latest;
|
ChronicleEntry latest = Chronicle.Latest;
|
||||||
string line = latest != null ? latest.Line : "";
|
string line = latest != null ? latest.Line : "";
|
||||||
string display = latest != null ? latest.DisplayLine : "";
|
string display = latest != null ? latest.DisplayLine : "";
|
||||||
pass = latest != null
|
bool inLatest = latest != null
|
||||||
&& !string.IsNullOrEmpty(needle)
|
&& !string.IsNullOrEmpty(needle)
|
||||||
&& line.IndexOf(needle, System.StringComparison.OrdinalIgnoreCase) >= 0;
|
&& line.IndexOf(needle, System.StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
detail = $"latest='{display}' needle='{needle}' date='{(latest != null ? latest.DateLabel : "")}'";
|
// Live lover/friend patches can append after a death sample in the same wait;
|
||||||
|
// still pass when the needle exists on the focus subject's recent history.
|
||||||
|
bool inRecent = false;
|
||||||
|
if (!inLatest && !string.IsNullOrEmpty(needle))
|
||||||
|
{
|
||||||
|
IReadOnlyList<ChronicleEntry> snap = Chronicle.SnapshotForFocus();
|
||||||
|
for (int i = snap.Count - 1; i >= 0 && i >= snap.Count - 8; i--)
|
||||||
|
{
|
||||||
|
ChronicleEntry e = snap[i];
|
||||||
|
if (e != null
|
||||||
|
&& !string.IsNullOrEmpty(e.Line)
|
||||||
|
&& e.Line.IndexOf(needle, System.StringComparison.OrdinalIgnoreCase) >= 0)
|
||||||
|
{
|
||||||
|
inRecent = true;
|
||||||
|
display = e.DisplayLine;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pass = inLatest || inRecent;
|
||||||
|
detail =
|
||||||
|
$"latest='{(latest != null ? latest.DisplayLine : "")}' hit='{display}' needle='{needle}' date='{(latest != null ? latest.DateLabel : "")}' recent={inRecent}";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "chronicle_latest_dated":
|
case "chronicle_latest_dated":
|
||||||
|
|
@ -9096,10 +9151,10 @@ public static class AgentHarness
|
||||||
pass = killerTag
|
pass = killerTag
|
||||||
&& killerId != 0
|
&& killerId != 0
|
||||||
&& focusId == killerId
|
&& focusId == killerId
|
||||||
&& WatchCaption.CurrentUnitId != 0
|
&& WatchCaption.CurrentUnitId == focusId
|
||||||
&& WatchCaption.CurrentUnitId != focusId;
|
&& DossierAvatar.ShowingLiveAvatar;
|
||||||
detail =
|
detail =
|
||||||
$"fallenFocus={detailStr} focusId={focusId} dossierId={WatchCaption.CurrentUnitId}";
|
$"fallenFocus={detailStr} focusId={focusId} dossierId={WatchCaption.CurrentUnitId} avatarLive={DossierAvatar.ShowingLiveAvatar}";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "lore_zoom_blocked":
|
case "lore_zoom_blocked":
|
||||||
|
|
|
||||||
|
|
@ -2163,9 +2163,8 @@ public static class Chronicle
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Camera for a dead subject.
|
/// Camera for a dead subject.
|
||||||
/// When <paramref name="preferPlace"/> is true (default), pan to death place / skull (no unit follow).
|
/// When <paramref name="preferPlace"/> is true (default), pan to death place / skull (no unit follow)
|
||||||
/// Killer follow is only via <see cref="FocusFallenKiller"/>.
|
/// and bind the dossier to the fallen unit. Killer follow is only via <see cref="FocusFallenKiller"/>.
|
||||||
/// Dossier always shows the fallen unit's last living state (not the killer).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool FocusFallenSubject(long unitId, bool preferPlace = true)
|
public static bool FocusFallenSubject(long unitId, bool preferPlace = true)
|
||||||
{
|
{
|
||||||
|
|
@ -2176,27 +2175,23 @@ public static class Chronicle
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UnitDossier fallen = ResolveFallenDossier(unitId);
|
|
||||||
if (fallen != null)
|
|
||||||
{
|
|
||||||
WatchCaption.PinWhilePaused();
|
|
||||||
WatchCaption.SetFromDossier(fallen);
|
|
||||||
}
|
|
||||||
|
|
||||||
ChronicleEntry death = LatestDeathEntry(unitId);
|
ChronicleEntry death = LatestDeathEntry(unitId);
|
||||||
if (!preferPlace && death != null && death.OtherId != 0)
|
if (!preferPlace && death != null && death.OtherId != 0)
|
||||||
{
|
{
|
||||||
Actor killer = FindUnitById(death.OtherId);
|
Actor killer = FindUnitById(death.OtherId);
|
||||||
if (killer != null && killer.isAlive())
|
if (killer != null && killer.isAlive())
|
||||||
{
|
{
|
||||||
CameraDirector.FocusUnit(killer, updateCaption: false);
|
return FocusFallenKiller(unitId);
|
||||||
LastFallenFocusDetail = "killer:" + death.OtherId;
|
|
||||||
LogService.LogInfo(
|
|
||||||
$"[IdleSpectator][LORE] fallen focus killer id={death.OtherId} victim={unitId}");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UnitDossier fallen = ResolveFallenDossier(unitId);
|
||||||
|
if (fallen != null)
|
||||||
|
{
|
||||||
|
WatchCaption.PinWhilePaused();
|
||||||
|
WatchCaption.SetFromDossier(fallen);
|
||||||
|
}
|
||||||
|
|
||||||
// Place / skull path: never keep following a living unit (e.g. prior killer focus).
|
// Place / skull path: never keep following a living unit (e.g. prior killer focus).
|
||||||
CameraDirector.ClearFollow();
|
CameraDirector.ClearFollow();
|
||||||
if (TryResolveFallenPlace(unitId, death, out Vector3 pos))
|
if (TryResolveFallenPlace(unitId, death, out Vector3 pos))
|
||||||
|
|
@ -2235,7 +2230,10 @@ public static class Chronicle
|
||||||
return killer;
|
return killer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Camera follow on the living killer of a fallen Lore subject.</summary>
|
/// <summary>
|
||||||
|
/// Camera follow + dossier on the living killer of a fallen Lore subject.
|
||||||
|
/// Lore detail stays on the fallen archive; only the idle dossier retargets.
|
||||||
|
/// </summary>
|
||||||
public static bool FocusFallenKiller(long unitId)
|
public static bool FocusFallenKiller(long unitId)
|
||||||
{
|
{
|
||||||
Actor killer = FindLivingKillerFor(unitId);
|
Actor killer = FindLivingKillerFor(unitId);
|
||||||
|
|
@ -2244,14 +2242,9 @@ public static class Chronicle
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UnitDossier fallen = ResolveFallenDossier(unitId);
|
|
||||||
if (fallen != null)
|
|
||||||
{
|
|
||||||
WatchCaption.PinWhilePaused();
|
|
||||||
WatchCaption.SetFromDossier(fallen);
|
|
||||||
}
|
|
||||||
|
|
||||||
CameraDirector.FocusUnit(killer, updateCaption: false);
|
CameraDirector.FocusUnit(killer, updateCaption: false);
|
||||||
|
WatchCaption.PinWhilePaused();
|
||||||
|
WatchCaption.SetFromActor(killer);
|
||||||
LastFallenFocusDetail = "killer:" + killer.getID();
|
LastFallenFocusDetail = "killer:" + killer.getID();
|
||||||
LastFallenFocusPosition = Vector3.zero;
|
LastFallenFocusPosition = Vector3.zero;
|
||||||
LogService.LogInfo(
|
LogService.LogInfo(
|
||||||
|
|
|
||||||
|
|
@ -161,9 +161,15 @@ public static class ChronicleHud
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Ignore outside-click dismiss briefly after skull open / Keep.</summary>
|
/// <summary>Ignore outside-click dismiss briefly after Lore open / Keep.</summary>
|
||||||
public static float IgnoreDismissUntil => _ignoreDismissUntil;
|
public static float IgnoreDismissUntil => _ignoreDismissUntil;
|
||||||
|
|
||||||
|
/// <summary>Arm a short grace so the open click does not instantly dismiss Lore.</summary>
|
||||||
|
public static void ArmDismissGrace(float seconds = 0.4f)
|
||||||
|
{
|
||||||
|
_ignoreDismissUntil = Time.unscaledTime + Mathf.Max(0.05f, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsCastTab => _tab == LoreTab.Living || _tab == LoreTab.Fallen;
|
private static bool IsCastTab => _tab == LoreTab.Living || _tab == LoreTab.Fallen;
|
||||||
|
|
||||||
/// <summary>True when Lore detail was opened via L/books and should track idle focus.</summary>
|
/// <summary>True when Lore detail was opened via L/books and should track idle focus.</summary>
|
||||||
|
|
@ -748,6 +754,7 @@ public static class ChronicleHud
|
||||||
_root.SetActive(visible);
|
_root.SetActive(visible);
|
||||||
if (visible)
|
if (visible)
|
||||||
{
|
{
|
||||||
|
ArmDismissGrace();
|
||||||
Rebuild(force: true);
|
Rebuild(force: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -871,7 +878,7 @@ public static class ChronicleHud
|
||||||
_graveStackActive = true;
|
_graveStackActive = true;
|
||||||
_graveTileX = tileX;
|
_graveTileX = tileX;
|
||||||
_graveTileY = tileY;
|
_graveTileY = tileY;
|
||||||
_ignoreDismissUntil = Time.unscaledTime + 0.4f;
|
ArmDismissGrace();
|
||||||
_searchText = "";
|
_searchText = "";
|
||||||
if (_searchField != null)
|
if (_searchField != null)
|
||||||
{
|
{
|
||||||
|
|
@ -944,7 +951,7 @@ public static class ChronicleHud
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ignoreDismissUntil = Time.unscaledTime + 0.4f;
|
ArmDismissGrace();
|
||||||
RefreshKeepGraveVisual();
|
RefreshKeepGraveVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -963,7 +970,7 @@ public static class ChronicleHud
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ignoreDismissUntil = Time.unscaledTime + 0.4f;
|
ArmDismissGrace();
|
||||||
LogService.LogInfo($"[IdleSpectator][LORE] deleted grave tile={tx},{ty}");
|
LogService.LogInfo($"[IdleSpectator][LORE] deleted grave tile={tx},{ty}");
|
||||||
InspectUi.DismissAll();
|
InspectUi.DismissAll();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -721,6 +721,16 @@ internal static class HarnessScenarios
|
||||||
Step("ie23", "assert", expect: "idle", value: "false"),
|
Step("ie23", "assert", expect: "idle", value: "false"),
|
||||||
Step("ie24", "assert", expect: "no_bad"),
|
Step("ie24", "assert", expect: "no_bad"),
|
||||||
|
|
||||||
|
// Outside dismiss closes Lore + dossier and keeps idle off (no auto-resume).
|
||||||
|
Step("ie30", "spectator", value: "on"),
|
||||||
|
Step("ie31", "focus", asset: "auto"),
|
||||||
|
Step("ie32", "lore_open_focus"),
|
||||||
|
Step("ie33", "wait", wait: 0.5f),
|
||||||
|
Step("ie34", "assert", expect: "idle", value: "false"),
|
||||||
|
Step("ie35", "inspect_dismiss"),
|
||||||
|
Step("ie36", "assert", expect: "inspect_cleared"),
|
||||||
|
Step("ie37", "assert", expect: "idle", value: "false"),
|
||||||
|
|
||||||
Step("ie90", "fast_timing", value: "false"),
|
Step("ie90", "fast_timing", value: "false"),
|
||||||
Step("ie99", "snapshot"),
|
Step("ie99", "snapshot"),
|
||||||
};
|
};
|
||||||
|
|
@ -2575,6 +2585,7 @@ internal static class HarnessScenarios
|
||||||
Step("ch18k11g", "wait", wait: 0.2f),
|
Step("ch18k11g", "wait", wait: 0.2f),
|
||||||
Step("ch18k11h", "assert", expect: "fallen_focus", value: "killer"),
|
Step("ch18k11h", "assert", expect: "fallen_focus", value: "killer"),
|
||||||
Step("ch18k11i", "assert", expect: "fallen_killer_camera"),
|
Step("ch18k11i", "assert", expect: "fallen_killer_camera"),
|
||||||
|
Step("ch18k11i2", "assert", expect: "dossier_matches_focus"),
|
||||||
Step("ch18k11j", "assert", expect: "fallen_killer_banner"),
|
Step("ch18k11j", "assert", expect: "fallen_killer_banner"),
|
||||||
Step("ch18k12", "screenshot", value: "hud-lore-fallen-slain.png"),
|
Step("ch18k12", "screenshot", value: "hud-lore-fallen-slain.png"),
|
||||||
Step("ch18k13", "wait", wait: 0.55f),
|
Step("ch18k13", "wait", wait: 0.55f),
|
||||||
|
|
@ -2691,10 +2702,8 @@ internal static class HarnessScenarios
|
||||||
Step("gv7", "spawn", asset: "human", count: 1),
|
Step("gv7", "spawn", asset: "human", count: 1),
|
||||||
Step("gv8", "focus", asset: "auto"),
|
Step("gv8", "focus", asset: "auto"),
|
||||||
Step("gv9", "wait", wait: 0.25f),
|
Step("gv9", "wait", wait: 0.25f),
|
||||||
// Stack three named deaths on the focus tile.
|
// Stack three named deaths on one captured tile (unit can walk between steps).
|
||||||
Step("gv10", "grave_force", value: "GraveAlice", count: 1),
|
Step("gv10", "grave_force", value: "GraveAlice,GraveBob,GraveCara"),
|
||||||
Step("gv11", "grave_force", value: "GraveBob", count: 1),
|
|
||||||
Step("gv12", "grave_force", value: "GraveCara", count: 1),
|
|
||||||
Step("gv13", "assert", expect: "grave_stacks", count: 1, label: "eq"),
|
Step("gv13", "assert", expect: "grave_stacks", count: 1, label: "eq"),
|
||||||
Step("gv14", "assert", expect: "grave_entries", count: 3),
|
Step("gv14", "assert", expect: "grave_entries", count: 3),
|
||||||
Step("gv15", "grave_open"),
|
Step("gv15", "grave_open"),
|
||||||
|
|
@ -2714,10 +2723,12 @@ internal static class HarnessScenarios
|
||||||
Step("gv24", "assert", expect: "lore_detail", value: "true"),
|
Step("gv24", "assert", expect: "lore_detail", value: "true"),
|
||||||
Step("gv25", "assert", expect: "fallen_focus", value: "place"),
|
Step("gv25", "assert", expect: "fallen_focus", value: "place"),
|
||||||
Step("gv26", "assert", expect: "fallen_place_no_follow"),
|
Step("gv26", "assert", expect: "fallen_place_no_follow"),
|
||||||
// Outside click / dismiss closes Lore (+ grave scope) together.
|
// Outside click / dismiss closes Lore + dossier and turns idle off.
|
||||||
Step("gv26b", "inspect_dismiss"),
|
Step("gv26b", "inspect_dismiss"),
|
||||||
Step("gv26c", "assert", expect: "grave_stack_scope", value: "false"),
|
Step("gv26c", "assert", expect: "grave_stack_scope", value: "false"),
|
||||||
Step("gv26d", "assert", expect: "lore_detail", value: "false"),
|
Step("gv26d", "assert", expect: "lore_detail", value: "false"),
|
||||||
|
Step("gv26d1", "assert", expect: "idle", value: "false"),
|
||||||
|
Step("gv26d2", "assert", expect: "inspect_cleared"),
|
||||||
// Delete removes the skull and closes Lore.
|
// Delete removes the skull and closes Lore.
|
||||||
Step("gv26e0", "grave_clear"),
|
Step("gv26e0", "grave_clear"),
|
||||||
Step("gv26e", "grave_force", value: "GraveDel", count: 1),
|
Step("gv26e", "grave_force", value: "GraveDel", count: 1),
|
||||||
|
|
@ -2750,6 +2761,7 @@ internal static class HarnessScenarios
|
||||||
Step("gv37", "wait", wait: 0.2f),
|
Step("gv37", "wait", wait: 0.2f),
|
||||||
Step("gv38", "assert", expect: "fallen_focus", value: "killer"),
|
Step("gv38", "assert", expect: "fallen_focus", value: "killer"),
|
||||||
Step("gv39", "assert", expect: "fallen_killer_camera"),
|
Step("gv39", "assert", expect: "fallen_killer_camera"),
|
||||||
|
Step("gv39b", "assert", expect: "dossier_matches_focus"),
|
||||||
// Cap eviction: shrink max stacks and flood tiles via orphans at offset positions.
|
// Cap eviction: shrink max stacks and flood tiles via orphans at offset positions.
|
||||||
Step("gv40", "lore_close"),
|
Step("gv40", "lore_close"),
|
||||||
Step("gv41", "set_setting", expect: "gravestone_max_stacks", value: "2"),
|
Step("gv41", "set_setting", expect: "gravestone_max_stacks", value: "2"),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ using UnityEngine;
|
||||||
namespace IdleSpectator;
|
namespace IdleSpectator;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shared dismiss for skull-opened Lore + paused dossier pin.
|
/// Outside-click dismiss for Lore + idle dossier. Always turns Idle Spectator off.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class InspectUi
|
public static class InspectUi
|
||||||
{
|
{
|
||||||
|
|
@ -15,9 +15,20 @@ public static class InspectUi
|
||||||
GraveSessionActive = true;
|
GraveSessionActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close Lore / dossier pin, clear grave scope, and force Idle Spectator off.
|
||||||
|
/// </summary>
|
||||||
public static void DismissAll()
|
public static void DismissAll()
|
||||||
{
|
{
|
||||||
GraveSessionActive = false;
|
GraveSessionActive = false;
|
||||||
|
// Outside dismiss must not auto-resume idle when Lore was opened via L.
|
||||||
|
ChronicleHud.NotifyManualIdleToggle();
|
||||||
|
|
||||||
|
if (SpectatorMode.Active)
|
||||||
|
{
|
||||||
|
SpectatorMode.SetActive(false, quiet: true);
|
||||||
|
}
|
||||||
|
|
||||||
ChronicleHud.ClearGraveStackScope();
|
ChronicleHud.ClearGraveStackScope();
|
||||||
if (ChronicleHud.DetailUnitId != 0)
|
if (ChronicleHud.DetailUnitId != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -29,13 +40,15 @@ public static class InspectUi
|
||||||
ChronicleHud.SetVisible(false);
|
ChronicleHud.SetVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
WatchCaption.ClearPausePin();
|
WatchCaption.HideForDismiss();
|
||||||
WatchCaption.ClearStatusBanner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Update()
|
public static void Update()
|
||||||
{
|
{
|
||||||
if (!GraveSessionActive)
|
bool loreOpen = ChronicleHud.Visible;
|
||||||
|
bool dossierOpen = WatchCaption.Visible;
|
||||||
|
bool idleOn = SpectatorMode.Active;
|
||||||
|
if (!loreOpen && !dossierOpen && !idleOn && !GraveSessionActive)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -50,13 +63,51 @@ public static class InspectUi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ChronicleHud.IsPointerOverPanel()
|
if (ChronicleHud.IsTypingSearch)
|
||||||
|| WatchCaption.IsPointerOverPanel())
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ChronicleHud.IsPointerOverPanel() || WatchCaption.IsPointerOverPanel())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skull click opens Lore this frame; leave the new session alone.
|
||||||
|
if (IsClickingGraveSkull())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click outside Lore / dossier closes the skull inspect session.
|
|
||||||
DismissAll();
|
DismissAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsClickingGraveSkull()
|
||||||
|
{
|
||||||
|
if (!ModSettings.GravestonesEnabled || GraveMarkers.StackCount <= 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (World.world != null && World.world.isOverUI())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WorldTile tile = World.world.getMouseTilePosCachedFrame() ?? World.world.getMouseTilePos();
|
||||||
|
if (tile != null && GraveMarkers.TryGetStack(tile.x, tile.y, out _))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 mouse = World.world.getMousePos();
|
||||||
|
return GraveMarkers.FindNearestStack(mouse, 1.35f) != null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -350,6 +350,14 @@ public static class WatchCaption
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Hide dossier after outside-click / inspect dismiss (idle already off).</summary>
|
||||||
|
public static void HideForDismiss()
|
||||||
|
{
|
||||||
|
_pinnedWhilePaused = false;
|
||||||
|
ClearStatusBanner();
|
||||||
|
SetVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
public static void ClearPausePin()
|
public static void ClearPausePin()
|
||||||
{
|
{
|
||||||
_pinnedWhilePaused = false;
|
_pinnedWhilePaused = false;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "IdleSpectator",
|
"name": "IdleSpectator",
|
||||||
"author": "dazed",
|
"author": "dazed",
|
||||||
"version": "0.28.11",
|
"version": "0.28.13",
|
||||||
"description": "AFK Idle Spectator (I) + Lore (L). Flavored death lines, full-width grave search, no skull counts.",
|
"description": "AFK Idle Spectator (I) + Lore (L). Killer button retargets dossier to the killer.",
|
||||||
"GUID": "com.dazed.idlespectator"
|
"GUID": "com.dazed.idlespectator"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue