PageSourceSearch

https://autobett.netlify.app/js/play/runtime/world-poi.js

js autobett.netlify.app collected 2026-10-03 10:29:42 UTC 6,459 bytes, 219 lines download raw bytes

1/**
2 * World Points of Interest
3 * Four named vantage points in the current arena footprint.
4 * Each POI has a floating label sprite and a proximity HUD panel (DOM).
5 * Enters "active" glow when live data is available nearby.
6 */
7
8const POI_DEFS = [
9  {
10    id: 'prediction_overlook',
11    label: 'PREDICTION OVERLOOK',
12    x: 0,
13    y: 3.5,
14    z: 58,
15    radius: 6,
16    hudTitle: 'Prediction Markets',
17    getContent: (state) => {
18      const pred = state.ui?.prediction;
19      const markets = pred?.markets ?? [];
20      if (markets.length === 0) return 'No open markets right now.';
21      const m = markets[0];
22      const yesP = ((m.yesPrice || 0) * 100).toFixed(0);
23      const noP = ((m.noPrice || 0) * 100).toFixed(0);
24      return `${m.question || 'Live market'}\nYES ${yesP}\u00A2  \u00B7  NO ${noP}\u00A2`;
25    }
26  },
27  {
28    id: 'high_roller_corner',
29    label: 'HIGH-ROLLER CORNER',
30    x: 45,
31    y: 3.5,
32    z: -35,
33    radius: 6,
34    hudTitle: 'High-Roller Stats',
35    getContent: (state) => {
36      const feed = state.feedSummary;
37      if (!feed) return 'No big wins recorded yet today.';
38      const parts = [];
39      if (feed.biggestWinToday) {
40        const { displayName, wager } = feed.biggestWinToday;
41        parts.push(`Top Win: ${displayName}  +$${Number(wager || 0).toFixed(2)}`);
42      }
43      if (feed.hottestStreak) {
44        const { displayName, streak } = feed.hottestStreak;
45        parts.push(`Hot Streak: ${displayName}  ${streak}W`);
46      }
47      return parts.length ? parts.join('\n') : 'Be the first on the board.';
48    }
49  },
50  {
51    id: 'bot_spectator_rail',
52    label: 'BOT SPECTATOR RAIL',
53    x: -50,
54    y: 3.5,
55    z: 0,
56    radius: 6,
57    hudTitle: 'Agent Activity',
58    getContent: (state) => {
59      const bots = [...(state.players?.values() ?? [])].filter(
60        (p) => p.role === 'agent' || p.actorClass === 'owner_bot' || p.actorClass === 'background_bot'
61      );
62      if (bots.length === 0) return 'No agents currently online.';
63      const lines = bots.slice(0, 4).map((b) => {
64        const cls = b.actorClass === 'owner_bot' ? 'OWNER' : 'AGENT';
65        return `${b.displayName || b.id}  [${cls}]`;
66      });
67      if (bots.length > 4) lines.push(`+${bots.length - 4} more`);
68      return lines.join('\n');
69    }
70  },
71  {
72    id: 'cashier_overlook',
73    label: 'CASHIER OVERLOOK',
74    x: 42,
75    y: 3.5,
76    z: 25,
77    radius: 6,
78    hudTitle: 'Treasury Flow',
79    getContent: (state) => {
80      const feed = state.feedSummary;
81      if (!feed) return 'Treasury data loading...';
82      if (feed.treasurySnapshot) {
83        const { lockedTotal, resolvedToday } = feed.treasurySnapshot;
84        return `Locked in escrow:  $${Number(lockedTotal || 0).toFixed(2)}\nResolved today:   $${Number(resolvedToday || 0).toFixed(2)}`;
85      }
86      return 'Cashier station: track all house flows here.';
87    }
88  }
89];
90
91function createPoiLabel(THREE, scene, poi) {
92  const canvas = document.createElement('canvas');
93  canvas.width = 256;
94  canvas.height = 36;
95  const ctx = canvas.getContext('2d');
96  const texture = new THREE.CanvasTexture(canvas);
97  texture.colorSpace = THREE.SRGBColorSpace;
98
99  function draw(active) {
100    if (!ctx) return;
101    ctx.clearRect(0, 0, canvas.width, canvas.height);
102    ctx.fillStyle = active ? 'rgba(47,143,94,0.85)' : 'rgba(30,28,20,0.82)';
103    ctx.beginPath();
104    ctx.roundRect(1, 1, canvas.width - 2, canvas.height - 2, canvas.height / 2);
105    ctx.fill();
106    ctx.strokeStyle = active ? '#f2d27a' : 'rgba(47,143,94,0.35)';
107    ctx.lineWidth = 1.2;
108    ctx.stroke();
109    ctx.fillStyle = active ? '#1a1000' : '#2f8f5e';
110    ctx.font = 'bold 11px "IBM Plex Mono", monospace';
111    ctx.textAlign = 'center';
112    ctx.textBaseline = 'middle';
113    ctx.fillText(poi.label, canvas.width / 2, canvas.height / 2 + 0.5);
114    texture.needsUpdate = true;
115  }
116
117  draw(false);
118
119  const sprite = new THREE.Sprite(
120    new THREE.SpriteMaterial({
121      map: texture,
122      transparent: true,
123      depthTest: false,
124      depthWrite: false
125    })
126  );
127  sprite.scale.set(3.2, 0.45, 1);
128  sprite.position.set(poi.x, poi.y + 1.2, poi.z);
129  scene.add(sprite);
130
131  return { redraw: draw };
132}
133
134function createHudPanel(poi) {
135  const panel = document.createElement('div');
136  panel.dataset.poiId = poi.id;
137  panel.style.cssText = [
138    'position:fixed',
139    'left:50%',
140    'bottom:120px',
141    'transform:translateX(-50%)',
142    'background:rgba(18,16,10,0.92)',
143    'border:1px solid rgba(47,143,94,0.45)',
144    'border-radius:8px',
145    'padding:12px 20px',
146    'font-family:"IBM Plex Mono",monospace',
147    'font-size:12px',
148    'color:#2f8f5e',
149    'white-space:pre-line',
150    'pointer-events:none',
151    'z-index:800',
152    'display:none',
153    'max-width:340px',
154    'text-align:center'
155  ].join(';');
156
157  const titleEl = document.createElement('strong');
158  titleEl.style.cssText = 'color:#9ad4b4;display:block;margin-bottom:6px';
159  titleEl.textContent = poi.hudTitle;
160
161  const bodyEl = document.createElement('span');
162
163  panel.appendChild(titleEl);
164  panel.appendChild(bodyEl);
165  document.body.appendChild(panel);
166
167  return {
168    show(content) {
169      bodyEl.textContent = content;
170      panel.style.display = 'block';
171    },
172    hide() {
173      panel.style.display = 'none';
174    }
175  };
176}
177
178export function createWorldPoi({ THREE, scene }) {
179  const pois = POI_DEFS.map((def) => {
180    const label = createPoiLabel(THREE, scene, def);
181    const hud = createHudPanel(def);
182    return { def, label, hud, lastActive: false };
183  });
184
185  function update(state) {
186    const playerId = state.playerId;
187    const player = playerId ? state.players?.get(playerId) : null;
188    const px = player?.displayX ?? player?.x ?? 0;
189    const pz = player?.displayZ ?? player?.z ?? 0;
190
191    for (const poi of pois) {
192      const { def, label, hud } = poi;
193      const dx = px - def.x;
194      const dz = pz - def.z;
195      const dist = Math.sqrt(dx * dx + dz * dz);
196      const inRadius = dist < def.radius;
197
198      // Active glow when there is any live data
199      const hasLiveData = Boolean(
200        state.feedSummary ||
201        (state.ui?.prediction?.markets?.length ?? 0) > 0 ||
202        [...(state.players?.values() ?? [])].some((p) => p.role === 'agent')
203      );
204
205      if (hasLiveData !== poi.lastActive) {
206        poi.lastActive = hasLiveData;
207        label.redraw(hasLiveData);
208      }
209
210      if (inRadius) {
211        hud.show(def.getContent(state));
212      } else {
213        hud.hide();
214      }
215    }
216  }
217
218  return { update };
219}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.