[object Object]

← back to Animals

dog-park: arrow-key movement + Auto walk toggle (button/T-key), human input cancels auto

4071c18ce4b248847b78282c6e9ddd4cd82d7639 · 2026-05-13 11:46:30 -0700 · SteveStudio2

Files touched

Diff

commit 4071c18ce4b248847b78282c6e9ddd4cd82d7639
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 11:46:30 2026 -0700

    dog-park: arrow-key movement + Auto walk toggle (button/T-key), human input cancels auto
---
 public/dogpark/dogpark.js | 60 +++++++++++++++++++++++++++++++++++++++++++----
 public/dogpark/index.html |  8 +++++--
 2 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/public/dogpark/dogpark.js b/public/dogpark/dogpark.js
index 8d6c3fb..7f70d75 100644
--- a/public/dogpark/dogpark.js
+++ b/public/dogpark/dogpark.js
@@ -821,6 +821,8 @@ function sendChat() {
 }
 chatSend.addEventListener('click', sendChat);
 chatInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') sendChat(); });
+// Auto-walk HUD button — toggles the same flag the 'T' key drives.
+document.getElementById('autoBtn')?.addEventListener('click', () => setAutoMode(!autoMode));
 setInterval(updateProximityHud, 200);
 
 // ── State sync ────────────────────────────────────────────────────────────
@@ -943,19 +945,50 @@ function applyState(msg) {
 
 // ── Local control ────────────────────────────────────────────────────────
 const keys = new Set();
+// Map arrow keys onto WASD slots so the existing movement code stays single-
+// path. Anything that isn't an arrow falls through unchanged so 'shift', 'c',
+// 'p', etc. still work.
+const _aliasKey = (k) => ({
+  arrowup: 'w', arrowdown: 's', arrowleft: 'a', arrowright: 'd',
+}[k] || k);
+// "Auto" mode — chooses a random target inside the park and wanders toward it,
+// re-picking when close or after ~6s. Any human keypress cancels Auto so users
+// can grab the controls back instantly. Toggled via the HUD button or 'T' key.
+let autoMode = false;
+let _autoTarget = null;
+let _autoUntil = 0;
+function setAutoMode(on) {
+  autoMode = !!on;
+  const btn = document.getElementById('autoBtn');
+  if (btn) {
+    btn.textContent = autoMode ? '⏸ Stop auto' : '▶ Auto walk';
+    btn.classList.toggle('on', autoMode);
+  }
+  if (!autoMode) _autoTarget = null;
+}
 window.addEventListener('keydown', (e) => {
   if (e.repeat) return;
-  keys.add(e.key.toLowerCase());
-  if (e.key.toLowerCase() === 'c') {
+  // Don't capture keys when typing in the chat or join inputs.
+  const tag = (e.target?.tagName || '').toLowerCase();
+  if (tag === 'input' || tag === 'textarea' || tag === 'select') return;
+  const k = e.key.toLowerCase();
+  keys.add(_aliasKey(k));
+  // Movement input from a human cancels auto-walk immediately.
+  if (autoMode && ['w','a','s','d','arrowup','arrowdown','arrowleft','arrowright'].includes(k)) {
+    setAutoMode(false);
+  }
+  if (k === 'c') {
     if (ws?.readyState === WebSocket.OPEN) {
       ws.send(JSON.stringify({ t:'call' }));
       addLog('🗣 Called dog');
     }
-  } else if (e.key.toLowerCase() === 'p') {
+  } else if (k === 'p') {
     cameraMode = (cameraMode + 1) % 2;
+  } else if (k === 't') {
+    setAutoMode(!autoMode);
   }
 });
-window.addEventListener('keyup', (e) => keys.delete(e.key.toLowerCase()));
+window.addEventListener('keyup', (e) => keys.delete(_aliasKey(e.key.toLowerCase())));
 
 renderer.domElement.addEventListener('click', (e) => {
   // Throw a ball/frisbee in the direction the camera faces, with mouse-y for arc
@@ -992,6 +1025,25 @@ function step(dt) {
     if (keys.has('s')) mz += 1;
     if (keys.has('a')) mx -= 1;
     if (keys.has('d')) mx += 1;
+    // Auto-walk: pick a random point inside the park and stride toward it.
+    // Re-pick when within 1.2u or after the 6s deadline. Human input above
+    // already wins because setAutoMode(false) fires on keydown.
+    if (autoMode && !mx && !mz) {
+      const nowMs = performance.now();
+      const dxT = (_autoTarget?.x ?? myX) - myX;
+      const dzT = (_autoTarget?.z ?? myZ) - myZ;
+      const distT = Math.hypot(dxT, dzT);
+      if (!_autoTarget || distT < 1.2 || nowMs > _autoUntil) {
+        _autoTarget = {
+          x: (Math.random() - 0.5) * (PARK_HALF * 1.6),
+          z: (Math.random() - 0.5) * (PARK_HALF * 1.6),
+        };
+        _autoUntil = nowMs + 6000;
+      } else {
+        mx = dxT / distT;
+        mz = dzT / distT;
+      }
+    }
     if (mx || mz) {
       const len = Math.hypot(mx, mz);
       mx /= len; mz /= len;
diff --git a/public/dogpark/index.html b/public/dogpark/index.html
index 12185e2..b12e339 100644
--- a/public/dogpark/index.html
+++ b/public/dogpark/index.html
@@ -41,6 +41,9 @@
   #avatar-link:hover{background:#fdf5e6;border-color:#d4a04a;color:#5a4020}
   #hud{position:fixed;bottom:14px;left:14px;background:rgba(0,0,0,.55);padding:10px 14px;border-radius:10px;font-size:12px;line-height:1.6}
   #hud kbd{background:#fff;color:#000;padding:1px 6px;border-radius:4px;font-family:ui-monospace,Menlo,monospace;font-size:11px;margin:0 2px}
+  #autoBtn{background:rgba(212,160,74,.18);color:#f4f1ea;border:1px solid #d4a04a;padding:.4em .9em;border-radius:6px;font:inherit;font-size:12px;font-weight:600;cursor:pointer;letter-spacing:.04em}
+  #autoBtn:hover{background:rgba(212,160,74,.32)}
+  #autoBtn.on{background:#d4a04a;color:#0e0e10}
   #counter{position:fixed;top:50px;left:14px;background:rgba(0,0,0,.55);padding:8px 12px;border-radius:8px;font-size:12px}
   #log{position:fixed;bottom:14px;right:14px;width:240px;max-height:140px;overflow:auto;background:rgba(0,0,0,.55);padding:8px 12px;border-radius:8px;font-size:11px;line-height:1.5}
   #chatbar{position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(120%);background:rgba(14,14,16,.92);border:1px solid #d4a04a;border-radius:14px;padding:10px 14px;display:flex;align-items:center;gap:12px;z-index:60;backdrop-filter:blur(10px);box-shadow:0 12px 40px rgba(0,0,0,.5);transition:transform 220ms cubic-bezier(.2,.7,.2,1);min-width:380px}
@@ -147,9 +150,10 @@
 </div>
 <div id="log" style="display:none"></div>
 <div id="hud" style="display:none">
-  <div><kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> walk · <kbd>Shift</kbd> run · <kbd>C</kbd> call dog</div>
+  <div><kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> or <kbd>↑</kbd><kbd>↓</kbd><kbd>←</kbd><kbd>→</kbd> walk · <kbd>Shift</kbd> run · <kbd>C</kbd> call dog</div>
   <div><kbd>Click</kbd> throw ball · <kbd>Shift+Click</kbd> throw frisbee</div>
-  <div><kbd>P</kbd> first/third person</div>
+  <div><kbd>P</kbd> first/third person · <kbd>T</kbd> auto walk</div>
+  <div style="margin-top:6px"><button id="autoBtn" type="button">▶ Auto walk</button></div>
 </div>
 
 <script type="importmap">

← 3aa2726 dog-park: brand logo upper-left, hamburger nav upper-right w  ·  back to Animals  ·  snapshot: backup uncommitted work (1 files) 9a24d54 →