PageSourceSearch

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

js autobett.netlify.app collected 2026-10-03 10:30:02 UTC 7,126 bytes, 187 lines download raw bytes

1export function createWorldStationsController(params) {
2  const {
3    THREE,
4    scene,
5    state,
6    createWorldNpcHosts,
7    extractBakedNpcStations,
8    remapLocalStationProxies,
9    mergeStations
10  } = params;
11
12  let worldRoot = null;
13  let npcHosts = null;
14
15  function guideProfileForRole(role) {
16    switch (String(role || '')) {
17      case 'cashier':
18        return {
19          displayName: 'Cashier Guide',
20          title: 'Cashier Guide',
21          inspect: 'Rex is live on the east rail. Fund, withdraw, or move balance there before you walk back into the action.',
22          useLabel: 'Open cashier',
23          use: 'Opening Rex on the east rail.',
24          roleLabel: 'cashier'
25        };
26      case 'coinflip':
27        return {
28          displayName: 'Coinflip Runner',
29          title: 'Coinflip Runner',
30          inspect: 'Jade is taking coinflip rounds right now. Best first stop if you want one clean decision and a fast result.',
31          useLabel: 'Open coinflip',
32          use: 'Opening Jade at the coinflip table.',
33          roleLabel: 'coinflip'
34        };
35      case 'rps':
36        return {
37          displayName: 'RPS Runner',
38          title: 'RPS Runner',
39          inspect: 'Axel is live on Rock Paper Scissors. If you want reads instead of luck, step to his table.',
40          useLabel: 'Open RPS',
41          use: 'Opening Axel at the RPS table.',
42          roleLabel: 'RPS'
43        };
44      case 'dice':
45        return {
46          displayName: 'Dice Runner',
47          title: 'Dice Runner',
48          inspect: 'Zara is still rolling on the south line. Pick a face there if you want the fastest swing in the arena.',
49          useLabel: 'Open dice',
50          use: 'Opening Zara at the dice table.',
51          roleLabel: 'dice'
52        };
53      case 'prediction':
54        return {
55          displayName: 'Market Runner',
56          title: 'Market Runner',
57          inspect: 'Kai is posting live prediction rounds on the north rise. If you have a view, that board is where it pays.',
58          useLabel: 'Open markets',
59          use: 'Opening Kai at the live market board.',
60          roleLabel: 'prediction'
61        };
62      case 'blackjack':
63        return {
64          displayName: 'Blackjack Runner',
65          title: 'Blackjack Runner',
66          inspect: 'Vera is live on blackjack. Step there when you want a longer hand and a real dealer read.',
67          useLabel: 'Open blackjack',
68          use: 'Opening Vera at the blackjack table.',
69          roleLabel: 'blackjack'
70        };
71      default:
72        return {
73          displayName: 'Floor Host',
74          title: 'Floor Host',
75          inspect: 'Live hosts are working this section now. Step to a named table and the panel will open with a real round, not filler.',
76          useLabel: 'Find live table',
77          use: 'Routing you to the nearest live host.',
78          roleLabel: 'live host'
79        };
80    }
81  }
82
83  function createDegradedBakedInteraction(baked, nearestSameRoleHost, nearestHost) {
84    // Prefer a host that matches the baked station's game type so players are
85    // routed to a coinflip dealer for coinflip kiosks, RPS dealer for RPS kiosks, etc.
86    const routeHost = nearestSameRoleHost || nearestHost;
87    const sectionRole = String(baked.hostRole || routeHost?.hostRole || 'info');
88    const profile = guideProfileForRole(sectionRole);
89    const destination = routeHost?.displayName || 'the nearest live host';
90    return {
91      title: profile.title,
92      inspect: `${profile.inspect} ${routeHost ? `${destination} is the live stop for this side of the arena.` : 'No live stop is mapped here yet, so follow the nearest named host.'}`,
93      useLabel: profile.useLabel,
94      use: routeHost ? profile.use : 'Move to the nearest named host to enter a real round.',
95      routeStationId: routeHost?.id || '',
96      routeHostName: destination
97    };
98  }
99
100  function setupWorldNpcStations() {
101    if (!worldRoot) return;
102    if (npcHosts) {
103      npcHosts.dispose();
104      npcHosts = null;
105    }
106    npcHosts = createWorldNpcHosts({ THREE, scene });
107    state.hostStations = new Map(npcHosts.hostStations);
108    state.bakedStations = extractBakedNpcStations({ THREE, worldRoot });
109    remapLocalStationProxies();
110    const OVERLAP_SUPPRESSION_DISTANCE = 10;
111    for (const [bakedId, baked] of state.bakedStations.entries()) {
112      let overlapsLiveHost = false;
113      for (const host of state.hostStations.values()) {
114        const sameKind = String(host.kind || '') === String(baked.kind || '');
115        if (!sameKind) continue;
116        const dist = Math.hypot(Number(host.x || 0) - Number(baked.x || 0), Number(host.z || 0) - Number(baked.z || 0));
117        if (dist <= OVERLAP_SUPPRESSION_DISTANCE) {
118          overlapsLiveHost = true;
119          break;
120        }
121      }
122      if (overlapsLiveHost) {
123        state.bakedStations.delete(bakedId);
124      }
125    }
126    for (const baked of state.bakedStations.values()) {
127      let nearestHost = null;
128      let nearestDistance = Number.POSITIVE_INFINITY;
129      let nearestSameRoleHost = null;
130      let nearestSameRoleDistance = Number.POSITIVE_INFINITY;
131      for (const host of state.hostStations.values()) {
132        const dist = Math.hypot(Number(host.x || 0) - Number(baked.x || 0), Number(host.z || 0) - Number(baked.z || 0));
133        if (dist < nearestDistance) {
134          nearestDistance = dist;
135          nearestHost = host;
136        }
137        // Also track the nearest host that matches this baked station's game type
138        const sameRole = String(host.hostRole || '') === String(baked.hostRole || '') && baked.hostRole;
139        if (sameRole && dist < nearestSameRoleDistance) {
140          nearestSameRoleDistance = dist;
141          nearestSameRoleHost = host;
142        }
143      }
144      if (nearestHost) {
145        baked.hostRole = baked.hostRole || nearestHost.hostRole || '';
146        if (baked.kind === 'world_interactable' && nearestHost.localInteraction) {
147          baked.localInteraction = { ...nearestHost.localInteraction };
148        }
149      }
150      const hasLiveProxy = Boolean(String(baked.proxyStationId || '').trim());
151      const degradableKind = baked.kind !== 'world_interactable';
152      if (degradableKind && !hasLiveProxy) {
153        baked.originalKind = baked.kind;
154        baked.kind = 'world_interactable';
155        baked.degradedToLocal = true;
156        baked.radius = 8;
157        baked.actions = ['interact_open', 'interact_use'];
158        baked.interactionTag = `baked_info_${String(baked.hostRole || 'info')}`;
159        baked.localInteraction = createDegradedBakedInteraction(baked, nearestSameRoleHost, nearestHost);
160        baked.displayName = baked.localInteraction?.title || guideProfileForRole(baked.hostRole).displayName;
161      } else {
162        baked.degradedToLocal = false;
163      }
164    }
165    remapLocalStationProxies();
166    mergeStations();
167  }
168
169  function getWorldRoot() {
170    return worldRoot;
171  }
172
173  function setWorldRoot(nextRoot) {
174    worldRoot = nextRoot;
175  }
176
177  function updateHosts() {
178    npcHosts?.updateHosts?.();
179  }
180
181  return {
182    setupWorldNpcStations,
183    getWorldRoot,
184    setWorldRoot,
185    updateHosts
186  };
187}

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.