arrows for idle mode
This commit is contained in:
parent
9ae7f7ffa1
commit
9fdce47810
9 changed files with 122 additions and 2 deletions
|
|
@ -152,6 +152,39 @@ Use `fast_timing` / `director_run` instead.
|
|||
- Prefer `./scripts/harness-run.sh`; avoid `Application.Quit`.
|
||||
- After code changes, restart/relaunch so NML recompiles.
|
||||
|
||||
## Launching WorldBox (required for agents)
|
||||
|
||||
Steam **refuses** to start when the Shell tool runs sandboxed (looks like root):
|
||||
`Error: Cannot run as root user` / game never opens.
|
||||
|
||||
**Always** launch or restart with unrestricted permissions:
|
||||
|
||||
```bash
|
||||
# Shell tool: required_permissions: ["all"]
|
||||
cd /home/dazed/Projects/worldbox-mods/idle-mode
|
||||
./scripts/harness-run.sh --repeat 3 critical_smoke
|
||||
```
|
||||
|
||||
That runner starts Steam via `steam -applaunch 1206560` when needed.
|
||||
|
||||
### Manual restart after a mod.json bump
|
||||
|
||||
```bash
|
||||
# Shell tool: required_permissions: ["all"]
|
||||
# Kill only the game binary (not Steam itself):
|
||||
kill $(pgrep -f '/Steam/steamapps/common/worldbox/worldbox$') 2>/dev/null
|
||||
# or: kill -9 <pid of .../common/worldbox/worldbox>
|
||||
|
||||
nohup steam -applaunch 1206560 >/tmp/idle-spectator-harness-launch.log 2>&1 &
|
||||
|
||||
# Wait until NML loaded the mod (not just process up):
|
||||
# ~/.config/unity3d/mkarpenko/WorldBox/Player.log contains:
|
||||
# [IdleSpectator]: Harmony patches applied
|
||||
# Fail fast if log shows: error CS
|
||||
```
|
||||
|
||||
Do **not** call `steam -applaunch` (or `./scripts/harness-run.sh` when the game is down) from a sandboxed Shell - it fails silently from the user's POV.
|
||||
|
||||
## Definition of done
|
||||
|
||||
- Relevant harness scenario(s) PASS
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ Use harness `fast_timing` + `director_run` / `age_current` for scheduling tests.
|
|||
| Flaky health/no_bad mid-scenario | ambient retarget while harness busy | `FreezeDirector` unless `director_run` |
|
||||
| Steam hang after tests | `Application.Quit` | leave game running |
|
||||
| Director tests take forever | production dwell/rotate | `fast_timing` (not game 5x) |
|
||||
| Game never opens / `Cannot run as root user` | Shell sandboxed; Steam rejects root-like env | re-run launch/`harness-run.sh` with Shell `required_permissions: ["all"]` |
|
||||
| Old mod still loaded after edit | NML caches until restart | kill `.../common/worldbox/worldbox`, bump `mod.json`, relaunch with `all` perms |
|
||||
|
||||
## Useful asserts
|
||||
|
||||
|
|
|
|||
|
|
@ -1026,6 +1026,15 @@ public static class AgentHarness
|
|||
detail = $"remembered={_rememberedFocusKey} now={nowKey}";
|
||||
break;
|
||||
}
|
||||
case "focus_arrows":
|
||||
case "relationship_arrows":
|
||||
{
|
||||
bool pinned = FocusRelationshipArrows.IsPinnedToFocus();
|
||||
pass = SpectatorMode.Active && MoveCamera.hasFocusUnit() && pinned;
|
||||
detail =
|
||||
$"idle={SpectatorMode.Active} focus={MoveCamera.hasFocusUnit()} pinned={pinned} lastActor={(UnitSelectionEffect.last_actor != null ? SafeName(UnitSelectionEffect.last_actor) : "null")} dest={PlayerConfig.optionBoolEnabled("cursor_arrow_destination")} lover={PlayerConfig.optionBoolEnabled("cursor_arrow_lover")} attack={PlayerConfig.optionBoolEnabled("cursor_arrow_attack_target")}";
|
||||
break;
|
||||
}
|
||||
case "presented":
|
||||
{
|
||||
string assetId = ResolveAssetId(cmd);
|
||||
|
|
|
|||
17
IdleSpectator/FocusArrowPatches.cs
Normal file
17
IdleSpectator/FocusArrowPatches.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using HarmonyLib;
|
||||
|
||||
namespace IdleSpectator;
|
||||
|
||||
/// <summary>
|
||||
/// After vanilla hover clears <see cref="UnitSelectionEffect.last_actor"/>,
|
||||
/// re-pin the Idle Spectator focus so destination / lover / attack arrows still draw.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(UnitSelectionEffect), nameof(UnitSelectionEffect.update))]
|
||||
internal static class FocusArrowPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
private static void PinFocusAfterVanillaHover(UnitSelectionEffect __instance, float pElapsed)
|
||||
{
|
||||
FocusRelationshipArrows.PinFocusActor();
|
||||
}
|
||||
}
|
||||
55
IdleSpectator/FocusRelationshipArrows.cs
Normal file
55
IdleSpectator/FocusRelationshipArrows.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
namespace IdleSpectator;
|
||||
|
||||
/// <summary>
|
||||
/// While Idle Spectator focuses a unit, reuse vanilla destination / lover / attack
|
||||
/// cursor arrows by pinning <see cref="UnitSelectionEffect.last_actor"/>.
|
||||
/// Arrow visibility follows the player's existing PlayerConfig toggles
|
||||
/// (<c>cursor_arrow_destination</c>, <c>cursor_arrow_lover</c>,
|
||||
/// <c>cursor_arrow_attack_target</c>) - idle does not force them on or off.
|
||||
/// </summary>
|
||||
internal static class FocusRelationshipArrows
|
||||
{
|
||||
public static void OnSpectatorEnabled()
|
||||
{
|
||||
PinFocusActor();
|
||||
}
|
||||
|
||||
public static void OnSpectatorDisabled()
|
||||
{
|
||||
UnitSelectionEffect.setLastActor(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keep the focused unit as the vanilla "hovered" actor so relationship arrows draw
|
||||
/// when the matching cursor-arrow options are already enabled.
|
||||
/// Safe to call every frame; no-op when idle is off or there is no focus.
|
||||
/// </summary>
|
||||
public static void PinFocusActor()
|
||||
{
|
||||
if (!SpectatorMode.Active || !MoveCamera.hasFocusUnit())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Actor focus = MoveCamera._focus_unit;
|
||||
if (focus == null || !focus.isAlive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnitSelectionEffect.setLastActor(focus);
|
||||
}
|
||||
|
||||
public static bool IsPinnedToFocus()
|
||||
{
|
||||
if (!SpectatorMode.Active || !MoveCamera.hasFocusUnit())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Actor focus = MoveCamera._focus_unit;
|
||||
return focus != null
|
||||
&& focus.isAlive()
|
||||
&& UnitSelectionEffect.last_actor == focus;
|
||||
}
|
||||
}
|
||||
|
|
@ -131,6 +131,7 @@ internal static class HarnessScenarios
|
|||
Step("c26", "assert", expect: "health"),
|
||||
Step("c27", "assert", expect: "power_bar", value: "false"),
|
||||
Step("c28", "assert", expect: "no_bad"),
|
||||
Step("c29", "assert", expect: "focus_arrows"),
|
||||
|
||||
Step("c30", "watch", asset: "auto", label: "New species: {asset}", tier: "Curiosity"),
|
||||
Step("c31", "wait", wait: 0.3f),
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ public class ModClass : MonoBehaviour, IMod, IConfigurable, ILocalizable
|
|||
|
||||
WatchCaption.Update();
|
||||
Chronicle.Update();
|
||||
FocusRelationshipArrows.PinFocusActor();
|
||||
StateProbe.Update();
|
||||
AutoTest.Update();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ public static class SpectatorMode
|
|||
InterestDirector.OnSpectatorEnabled();
|
||||
SpeciesDiscovery.OnSpectatorEnabled();
|
||||
Chronicle.OnSpectatorEnabled();
|
||||
FocusRelationshipArrows.OnSpectatorEnabled();
|
||||
WorldTip.showNowTop("Idle Spectator ON (I or any input to stop; F9 chronicle)", pTranslate: false);
|
||||
LogService.LogInfo("[IdleSpectator] Spectator mode enabled");
|
||||
}
|
||||
|
|
@ -46,6 +47,7 @@ public static class SpectatorMode
|
|||
{
|
||||
InterestDirector.OnSpectatorDisabled();
|
||||
SpeciesDiscovery.OnSpectatorDisabled();
|
||||
FocusRelationshipArrows.OnSpectatorDisabled();
|
||||
CameraDirector.ClearFollow();
|
||||
WatchCaption.Clear();
|
||||
StateProbe.Reset();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "IdleSpectator",
|
||||
"author": "dazed",
|
||||
"version": "0.9.20",
|
||||
"description": "AFK Idle Spectator (I) + Chronicle (F9). Full trait names; rarity-ranked; E2E trait assert.",
|
||||
"version": "0.9.22",
|
||||
"description": "AFK Idle Spectator (I) + Chronicle (F9). Focus arrows respect vanilla toggles.",
|
||||
"GUID": "com.dazed.idlespectator"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue