@adatechnology/game-devtools 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/MIGRATION.md +209 -0
- package/README.md +121 -2
- package/dist/index.d.ts +270 -0
- package/dist/index.global.js +572 -0
- package/dist/index.global.js.map +1 -0
- package/dist/index.js +547 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anderson Fernandes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/MIGRATION.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Migration: `ModoLivreSDK` (modo-livre.js) to `@adatechnology/game-devtools`
|
|
2
|
+
|
|
3
|
+
Source: `sdk/teste/modo-livre.js` and its README. Every old name maps to exactly one new name. With `locale: 'pt-BR'` every user-facing string is
|
|
4
|
+
identical to the original (checked by running both implementations on the same inputs and comparing HUD, form and panel texts, placeholders,
|
|
5
|
+
aria-labels, notification messages and the copy text).
|
|
6
|
+
|
|
7
|
+
## Loading
|
|
8
|
+
|
|
9
|
+
| Old | New |
|
|
10
|
+
|---|---|
|
|
11
|
+
| global `ModoLivreSDK` (concatenated `<script>`) | ESM `import { create } from '@adatechnology/game-devtools'`, or IIFE `dist/index.global.js` exposing `GameDevtools` |
|
|
12
|
+
| `ModoLivreSDK.criar(THREE, cfg)` | `GameDevtools.create(THREE, options)` |
|
|
13
|
+
|
|
14
|
+
## Options (`criar` second argument)
|
|
15
|
+
|
|
16
|
+
| Old | New |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `chaveMarcas` (default `'sdk-modo-livre-marcas'`) | `marksStorageKey` (default `'game-devtools-marks'`) |
|
|
19
|
+
| `colecao` (default `'marcas'`) | `collection` (default `'marks'`) |
|
|
20
|
+
| `arquivo` (default `'marcas.txt'`) | `fileName` (default from the locale: `marks.txt` / `marcas.txt`) |
|
|
21
|
+
| `equipes` (default `[['geral', 'Geral']]`) | `teams` (default `[['general', <locale label>]]`) |
|
|
22
|
+
| `area` | `area` |
|
|
23
|
+
| `hora` | `time` |
|
|
24
|
+
| `msg(texto, segundos)` | `notify(text, seconds)` |
|
|
25
|
+
| `pausa(aberto)` | `pause(paused)` |
|
|
26
|
+
| `pos()` | `position()` |
|
|
27
|
+
| `limites` | `bounds` |
|
|
28
|
+
| `velocidade` | `speed` |
|
|
29
|
+
| `etiquetasMax` | `labelsMax` |
|
|
30
|
+
| `etiquetasDist` | `labelsDistance` (still not read, kept for compatibility) |
|
|
31
|
+
| (none) | `locale` (`'en'` default, `'pt-BR'`) and `strings` (partial overrides) |
|
|
32
|
+
|
|
33
|
+
Data that already exists under the old defaults keeps working if you pass the old values: `marksStorageKey: 'sdk-modo-livre-marcas'`,
|
|
34
|
+
`collection: 'marcas'`. The ZombiAdA adapter passes its own `chaveMarcas`, so only `collection` matters for server-side marks. Stored marks also carry
|
|
35
|
+
renamed fields (see "Mark fields" below): marks saved by the old SDK have `rumo`/`graus`/`olhar`/`hora`/`voo` and need a one-off conversion, or
|
|
36
|
+
simply start fresh.
|
|
37
|
+
|
|
38
|
+
Team ids are game data, so `'geral'` stays valid if you declare `teams` yourself.
|
|
39
|
+
|
|
40
|
+
## Returned object (`ml`)
|
|
41
|
+
|
|
42
|
+
State and fields:
|
|
43
|
+
|
|
44
|
+
| Old | New |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `ml.voo` | `dev.flying` |
|
|
47
|
+
| `ml.x`, `ml.y`, `ml.z` | `dev.x`, `dev.y`, `dev.z` |
|
|
48
|
+
| `ml.vel` | `dev.speed` |
|
|
49
|
+
| `ml.hud` | `dev.hud` |
|
|
50
|
+
| `ml.aberto` (`null`, `'form'`, `'lista'`) | `dev.openPanel` (`null`, `'form'`, `'list'`) |
|
|
51
|
+
| `ml.db` | `dev.db` |
|
|
52
|
+
| `ml.marcas` | `dev.marks` |
|
|
53
|
+
| `ml.chaves` | internal (use `dev.toggle`) |
|
|
54
|
+
| `ml.acoes` | internal (use `dev.action`) |
|
|
55
|
+
| `ml.horas` | internal (use `dev.timesOfDay`) |
|
|
56
|
+
| `ml.grupos` | internal (use `dev.places`) |
|
|
57
|
+
| `ml.cens` | `dev.scenarioList` (read-only) |
|
|
58
|
+
| `ml.cenAtivo` | `dev.activeScenario` (read-only) |
|
|
59
|
+
| `ml.etiq` | `dev.showLabels` |
|
|
60
|
+
| `ml.etiquetas` | internal (use `dev.labelsFor`) |
|
|
61
|
+
| `ml.abertos` | internal (which place groups are expanded) |
|
|
62
|
+
| `ml.irPara` | `dev.goTo` |
|
|
63
|
+
| `ml.extras` | `dev.extras` |
|
|
64
|
+
|
|
65
|
+
Methods:
|
|
66
|
+
|
|
67
|
+
| Old | New |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `ml.rumo(yaw)` | `dev.compass(yaw)` |
|
|
70
|
+
| `ml.aqui()` | `dev.here()` |
|
|
71
|
+
| `ml.linha(marca)` | `dev.line(mark)` |
|
|
72
|
+
| `ml.voa(dt, yaw, pitch, teclas)` | `dev.fly(dt, yaw, pitch, keys)` |
|
|
73
|
+
| `ml.cameraVoo(cam, yaw, pitch)` | `dev.flyCamera(camera, yaw, pitch)` |
|
|
74
|
+
| `ml.chave(id, nome, get, set)` | `dev.toggle(id, label, get, set)` |
|
|
75
|
+
| `ml.acao(id, nome, fn)` | `dev.action(id, label, fn)` |
|
|
76
|
+
| `ml.horasDoDia(lista, set)` | `dev.timesOfDay(list, set)` |
|
|
77
|
+
| `ml.lugares(fn)` | `dev.places(fn)` |
|
|
78
|
+
| `ml.etiquetasDe(fn)` | `dev.labelsFor(fn)` |
|
|
79
|
+
| `ml.cenarios(lista, aplicar, checar)` | `dev.scenarios(list, apply, check)` |
|
|
80
|
+
| `ml.cenario(id)` | `dev.scenario(id)` |
|
|
81
|
+
| `ml.checar(id)` | `dev.check(id)` |
|
|
82
|
+
| `ml.abre('form' \| 'lista')` | `dev.open('form' \| 'list')` |
|
|
83
|
+
| `ml.fecha()` | `dev.close()` |
|
|
84
|
+
| `ml.render()` | `dev.render()` |
|
|
85
|
+
| `ml.atualiza(dt, cam, visivel)` | `dev.update(dt, camera, visible)` |
|
|
86
|
+
| `ml.expoe()` | `dev.expose()` |
|
|
87
|
+
| (none) | `dev.dispose()` (new) |
|
|
88
|
+
|
|
89
|
+
## Key object for `voa` (`fly`)
|
|
90
|
+
|
|
91
|
+
| Old | New |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `frente` | `forward` |
|
|
94
|
+
| `lado` | `strafe` |
|
|
95
|
+
| `sobe` | `up` |
|
|
96
|
+
| `rapido` | `fast` |
|
|
97
|
+
|
|
98
|
+
## Mark and `aqui()` (`here()`) fields
|
|
99
|
+
|
|
100
|
+
| Old | New |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `x`, `y`, `z`, `yaw`, `pitch` | unchanged |
|
|
103
|
+
| `area` | `area` |
|
|
104
|
+
| `rumo` | `heading` |
|
|
105
|
+
| `graus` | `degrees` |
|
|
106
|
+
| `olhar` | `look` |
|
|
107
|
+
| `hora` | `time` |
|
|
108
|
+
| `voo` | `flying` |
|
|
109
|
+
| `team`, `text`, `at`, `by`, `id` (mark only) | unchanged |
|
|
110
|
+
|
|
111
|
+
`rumo(yaw)` returned `[rumo, graus]`; `compass(yaw)` returns `[heading, degrees]`.
|
|
112
|
+
|
|
113
|
+
## Callback and registration argument shapes
|
|
114
|
+
|
|
115
|
+
| Old | New |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `lugares(() => [[grupo, [[nome, ir]]]])` | `places(() => [[group, [[name, go]]]])` (same shape) |
|
|
118
|
+
| `etiquetasDe(cam => [{ x, y, z, texto, cor }])` | `labelsFor(camera => [{ x, y, z, text, color }])` |
|
|
119
|
+
| `texto` | `text` |
|
|
120
|
+
| `cor` | `color` |
|
|
121
|
+
| `horasDoDia([[nome, valor]], set)` | `timesOfDay([[label, value]], set)` (same shape) |
|
|
122
|
+
| `cenarios(lista, aplicar, checar)`: `checar(c) -> { criterio: { ok, nota } }` | `scenarios(list, apply, check)`: `check(s) -> { criterion: { ok, note } }` |
|
|
123
|
+
| `nota` | `note` |
|
|
124
|
+
| `chave(...)` `set` returns `true` to close the panel | `toggle(...)` `set` returns `true` to close the panel (same) |
|
|
125
|
+
| `acao(...)` `fn` returns `true` to keep the panel open | `action(...)` `fn` returns `true` to keep the panel open (same) |
|
|
126
|
+
|
|
127
|
+
## Browser-test globals (`expoe`)
|
|
128
|
+
|
|
129
|
+
| Old | New |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `window.__cenario` | `window.__scenario` |
|
|
132
|
+
| `window.__cenCheck` | `window.__scenarioCheck` |
|
|
133
|
+
| `window.__cenLista` | `window.__scenarioList` |
|
|
134
|
+
| `window.__modoLivre` | `window.__devtools` |
|
|
135
|
+
|
|
136
|
+
## DOM (ids, classes, data attributes)
|
|
137
|
+
|
|
138
|
+
| Old | New |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `#mlpos`, `.mlpos`, `.mlpos.voo` | `#gd-pos`, `.gd-pos`, `.gd-pos.flying` |
|
|
141
|
+
| `#mlform` | `#gd-form` |
|
|
142
|
+
| `#mllista` | `#gd-list` |
|
|
143
|
+
| `.mlpainel` | `.gd-panel` |
|
|
144
|
+
| `#mlet`, `.mlet` | `#gd-labels`, `.gd-labels` |
|
|
145
|
+
| `.mlrow` | `.gd-row` |
|
|
146
|
+
| `.mlsub` | `.gd-sub` |
|
|
147
|
+
| `.mlm` | `.gd-mark` |
|
|
148
|
+
| `.mlcopia`, `#mlcopia` | `.gd-copy`, `#gd-copy` |
|
|
149
|
+
| `#mleq` | `#gd-team` |
|
|
150
|
+
| `#mltxt` | `#gd-note` |
|
|
151
|
+
| `#mlbaixar` | `#gd-download` |
|
|
152
|
+
| `data-f="salva" / "fecha" / "copiar" / "baixar"` | `data-f="save" / "close" / "copy" / "download"` |
|
|
153
|
+
| `data-tg` | `data-toggle` |
|
|
154
|
+
| `data-ac` | `data-action` |
|
|
155
|
+
| `data-hora` | `data-time` |
|
|
156
|
+
| `data-lug` | `data-place` |
|
|
157
|
+
| `data-ir` | `data-goto` |
|
|
158
|
+
| `data-apaga` | `data-delete` |
|
|
159
|
+
| `data-g` | `data-g` (unchanged) |
|
|
160
|
+
|
|
161
|
+
The CSS variables read from the host (`--panel`, `--ink`, `--line`, `--accent`, `--muted`, `--f-mono`, `--f-display`) are unchanged.
|
|
162
|
+
|
|
163
|
+
## User-facing strings (now in the locale table)
|
|
164
|
+
|
|
165
|
+
`pt-BR` equals the previous Portuguese text exactly.
|
|
166
|
+
|
|
167
|
+
| Old (hard-coded) | New key | `en` |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `['N','NE','L','SE','S','SO','O','NO']` | `compass` | `N NE E SE S SW W NW` |
|
|
170
|
+
| `'Geral'` | `defaultTeam` | `General` |
|
|
171
|
+
| `'marcas.txt'` | `fileName` | `marks.txt` |
|
|
172
|
+
| `'Marcar ajuste'` | `markFormLabel` | `Mark adjustment` |
|
|
173
|
+
| `'Modo livre'` (panel heading and aria-label) | `panelTitle` | `Free mode` |
|
|
174
|
+
| `'Marcar este lugar'` | `markHereTitle` | `Mark this place` |
|
|
175
|
+
| `'olhando'` | `lookingAt` | `looking` |
|
|
176
|
+
| `'Equipe'` | `teamLabel` | `Team` |
|
|
177
|
+
| `'O que ajustar aqui?'` | `notePlaceholder` | `What should be adjusted here?` |
|
|
178
|
+
| `'Salvar (Enter)'` | `saveButton` | `Save (Enter)` |
|
|
179
|
+
| `'Cancelar (Esc)'` | `cancelButton` | `Cancel (Esc)` |
|
|
180
|
+
| `'sim'` / `'não'` | `yes` / `no` | `yes` / `no` |
|
|
181
|
+
| `'Lugares de teste'` | `placesTitle` | `Test places` |
|
|
182
|
+
| `` `Marcas (${n})` `` | `marksTitle(n)` | `Marks (n)` |
|
|
183
|
+
| `'Salvas no servidor desta página: Claude lê direto, não precisa copiar.'` | `savedOnServer` | `Saved on this page's server: Claude reads them directly, no need to copy.` |
|
|
184
|
+
| `'Salvas neste navegador. Para mandar: Copiar tudo e colar no chat, ou Baixar arquivo.'` | `savedInBrowser` | `Saved in this browser. To send them: Copy all and paste in the chat, or Download file.` |
|
|
185
|
+
| `'(sem nota)'` | `noNote` | `(no note)` |
|
|
186
|
+
| `'Ir'` | `goButton` | `Go` |
|
|
187
|
+
| `'Apagar'` | `deleteButton` | `Delete` |
|
|
188
|
+
| `'Nenhuma marca ainda.'` | `noMarks` | `No marks yet.` |
|
|
189
|
+
| `'Copiar tudo'` | `copyAll` | `Copy all` |
|
|
190
|
+
| `'Baixar arquivo'` | `downloadFile` | `Download file` |
|
|
191
|
+
| `'Fechar (Esc)'` | `closeButton` | `Close (Esc)` |
|
|
192
|
+
| `'Copiado'` | `copied` | `Copied` |
|
|
193
|
+
| `'Selecionado: Ctrl+C'` | `copySelected` | `Selected: Ctrl+C` |
|
|
194
|
+
| `'Marca salva. Claude já consegue ler'` | `savedServerMsg` | `Mark saved. Claude can already read it` |
|
|
195
|
+
| `'Não deu para salvar no servidor, ficou neste navegador'` | `serverFailMsg` | `Could not save to the server, it stayed in this browser` |
|
|
196
|
+
| `'Marca salva neste navegador. Abra o painel para copiar'` | `savedBrowserMsg` | `Mark saved in this browser. Open the panel to copy` |
|
|
197
|
+
| `olhar` (HUD) | `hudLook` | `look` |
|
|
198
|
+
| `` `voo ${v} m/s` `` | `hudFlight(speed)` | `fly <v> m/s` |
|
|
199
|
+
| `` `${n} marcas` `` | `hudMarks(n)` | `n mark(s)` |
|
|
200
|
+
|
|
201
|
+
Use `create(THREE, { locale: 'pt-BR' })` to keep the Portuguese UI. Override any entry with `strings`.
|
|
202
|
+
|
|
203
|
+
## Behaviour changes beyond renaming
|
|
204
|
+
|
|
205
|
+
- New `dispose()` removes the injected `<style>`, elements and the `wheel`/`keydown` listeners (the original had no teardown).
|
|
206
|
+
- Options set to `undefined` no longer erase their defaults (the original `{ ...defaults, ...cfg }` did).
|
|
207
|
+
- The mark text/serialisation logic is now also exported as pure functions (`formatMarkLine`, `serializeMarks`, `sortMarks`, `compass`) and the
|
|
208
|
+
scenario bookkeeping as `createScenarioRunner`, with no behaviour difference.
|
|
209
|
+
- Default `collection` and `marksStorageKey` changed (see above); pass the old values to keep existing data.
|
package/README.md
CHANGED
|
@@ -1,3 +1,122 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @adatechnology/game-devtools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
In-game test tooling for three.js games. It knows nothing about any game: the host plugs everything in through options and a handful of
|
|
4
|
+
registration calls.
|
|
5
|
+
|
|
6
|
+
- **Free-fly mode** that goes through everything (bounded, mouse-wheel speed).
|
|
7
|
+
- **Position HUD**: area, coordinates, compass heading, look angle, time of day, optional extras.
|
|
8
|
+
- **Notes and marks**: "mark this place" form with team and note. Marks are stored in the page's server database when one is available
|
|
9
|
+
(`window.claude`), otherwise in `localStorage`, with copy-all and download. Pure helpers format and serialise them.
|
|
10
|
+
- **Panel** with on/off switches, action buttons, time-of-day buttons and grouped test places.
|
|
11
|
+
- **Scenarios runner**: apply a scenario by id and run an automatic check that returns `{ criterion: { ok, note } }`.
|
|
12
|
+
- **Entity labels** floating over world points.
|
|
13
|
+
- Localised (`en`, `pt-BR`); every default string can be overridden.
|
|
14
|
+
|
|
15
|
+
`three` is a peer dependency used for types only: you pass your own `THREE` instance at runtime.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pnpm add @adatechnology/game-devtools three
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Browser without a bundler (the global is `GameDevtools`):
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<script src="https://unpkg.com/@adatechnology/game-devtools/dist/index.global.js"></script>
|
|
27
|
+
<script>
|
|
28
|
+
const dev = GameDevtools.create(THREE, { locale: 'en' })
|
|
29
|
+
</script>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import * as THREE from 'three'
|
|
36
|
+
import { create } from '@adatechnology/game-devtools'
|
|
37
|
+
|
|
38
|
+
const dev = create(THREE, {
|
|
39
|
+
marksStorageKey: 'my-game-marks',
|
|
40
|
+
teams: [['map', 'Map'], ['art', 'Art']],
|
|
41
|
+
area: (x, z) => 'Plaza',
|
|
42
|
+
time: () => '12:00',
|
|
43
|
+
notify: (text, seconds) => showToast(text, seconds),
|
|
44
|
+
position: () => ({ x, y, z, yaw, pitch }),
|
|
45
|
+
bounds: { x: [-500, 500], y: [0.4, 260], z: [-500, 500] },
|
|
46
|
+
pause: (paused) => { /* release the mouse, pause the game */ },
|
|
47
|
+
locale: 'en', // or 'pt-BR'
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
dev
|
|
51
|
+
.toggle('fog', 'Fog', () => fog, (on) => { fog = on })
|
|
52
|
+
.action('spawn', 'Spawn zombie', () => { spawn(); return true /* keep the panel open */ })
|
|
53
|
+
.timesOfDay([['Morning', 0.3], ['Night', 0.9]], setTimeOfDay)
|
|
54
|
+
.places(() => [['City', [['Plaza', () => teleport(0, 0)]]]])
|
|
55
|
+
.labelsFor((camera) => [{ x, y, z, text: 'npc-12', color: '#9fd0ff' }])
|
|
56
|
+
.scenarios(list, (scenario) => apply(scenario), (scenario) => ({ lit: { ok: isLit(), note: '...' } }))
|
|
57
|
+
|
|
58
|
+
dev.goTo = (x, y, z, yaw, pitch) => teleport(x, y, z, yaw, pitch)
|
|
59
|
+
|
|
60
|
+
// every frame
|
|
61
|
+
if (dev.flying) {
|
|
62
|
+
dev.fly(dt, yaw, pitch, { forward, strafe, up, fast })
|
|
63
|
+
dev.flyCamera(camera, yaw, pitch)
|
|
64
|
+
}
|
|
65
|
+
dev.update(dt, camera, visible)
|
|
66
|
+
|
|
67
|
+
// your own keys
|
|
68
|
+
dev.open('form') // or 'list'; dev.close()
|
|
69
|
+
dev.expose() // window.__scenario, __scenarioCheck, __scenarioList, __devtools for Playwright
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Keys stay in the game. Free-fly is on when you set `dev.flying = true`.
|
|
73
|
+
|
|
74
|
+
## API
|
|
75
|
+
|
|
76
|
+
### `create(THREE, options?) => Devtools`
|
|
77
|
+
|
|
78
|
+
| Option | Default | Meaning |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `marksStorageKey` | `'game-devtools-marks'` | localStorage key for marks |
|
|
81
|
+
| `collection` | `'marks'` | Server database collection |
|
|
82
|
+
| `fileName` | locale (`marks.txt` / `marcas.txt`) | Name of the downloaded file |
|
|
83
|
+
| `teams` | `[['general', 'General']]` | `[id, label]` pairs for the form |
|
|
84
|
+
| `area(x, z)` | `''` | Area name |
|
|
85
|
+
| `time()` | `''` | Time-of-day text |
|
|
86
|
+
| `notify(text, seconds)` | no-op | Short messages to the player |
|
|
87
|
+
| `pause(paused)` | no-op | Called with `true` when a panel opens and `false` when it closes |
|
|
88
|
+
| `position()` | origin | `{ x, y, z, yaw, pitch }` of the player |
|
|
89
|
+
| `bounds` | +-1e4 / y 0.4..400 | Free-fly limits |
|
|
90
|
+
| `speed` | `14` | Initial free-fly speed (m/s) |
|
|
91
|
+
| `labelsMax` | `48` | Label pool size |
|
|
92
|
+
| `locale` | `'en'` | `'en'` or `'pt-BR'` |
|
|
93
|
+
| `strings` | | Partial overrides of any user-facing string |
|
|
94
|
+
|
|
95
|
+
### `Devtools`
|
|
96
|
+
|
|
97
|
+
State (read/write): `flying`, `x`, `y`, `z`, `speed`, `hud`, `showLabels`, `goTo`, `extras`. State (read): `openPanel`, `marks`, `db`, `scenarioList`,
|
|
98
|
+
`activeScenario`.
|
|
99
|
+
|
|
100
|
+
Methods: `compass(yaw)`, `here()`, `line(mark)`, `fly(dt, yaw, pitch, keys)`, `flyCamera(camera, yaw, pitch)`, `toggle(...)`, `action(...)`,
|
|
101
|
+
`timesOfDay(...)`, `places(...)`, `labelsFor(...)`, `scenarios(list, apply, check)`, `scenario(id)`, `check(id?)`, `open(panel)`, `close()`, `render()`,
|
|
102
|
+
`update(dt, camera, visible?)`, `expose()`, `dispose()`.
|
|
103
|
+
|
|
104
|
+
### Pure helpers (no DOM)
|
|
105
|
+
|
|
106
|
+
`compass(yaw, headings)`, `formatMarkLine(mark, teams, strings)`, `sortMarks(marks)`, `serializeMarks(marks, teams, strings)` (returns `{ text, file }`; `file`
|
|
107
|
+
is the text plus the raw JSON array), `createScenarioRunner()`, `escapeHtml(s)`, and the `locales` table.
|
|
108
|
+
|
|
109
|
+
Coming from the Portuguese `ModoLivreSDK`? See [MIGRATION.md](./MIGRATION.md).
|
|
110
|
+
|
|
111
|
+
## Português
|
|
112
|
+
|
|
113
|
+
Ferramentas de teste dentro do jogo para three.js: voo livre que atravessa tudo, posição na tela, marcas com nota (banco do artifact ou
|
|
114
|
+
navegador), painel com chaves, ações, hora e lugares de teste, cenários com checagem automática e etiquetas sobre entidades. Não conhece nenhum jogo.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const dev = create(THREE, { locale: 'pt-BR', teams: [['mapa', 'Mapa']], area, time, notify, position, pause })
|
|
118
|
+
dev.toggle('neblina', 'Neblina', () => neblina, (on) => { neblina = on })
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Com `locale: 'pt-BR'` todos os textos (HUD, painel, formulário, mensagens, pontos cardeais "N NE L SE S SO O NO") ficam idênticos aos do SDK
|
|
122
|
+
original. A tabela de nomes antigos e novos está em [MIGRATION.md](./MIGRATION.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import * as ThreeNamespace from 'three';
|
|
2
|
+
|
|
3
|
+
/** Locales shipped with the package. Unknown locales fall back to `en`. */
|
|
4
|
+
type DevtoolsLocale = 'en' | 'pt-BR';
|
|
5
|
+
/** Every user-facing default string. Override any of them with the `strings` option. */
|
|
6
|
+
interface DevtoolsStrings {
|
|
7
|
+
/** Eight compass headings starting at north, clockwise (N, NE, E, SE, S, SW, W, NW). */
|
|
8
|
+
compass: [string, string, string, string, string, string, string, string];
|
|
9
|
+
/** Label of the default team. */
|
|
10
|
+
defaultTeam: string;
|
|
11
|
+
/** Default file name of the downloaded marks file. */
|
|
12
|
+
fileName: string;
|
|
13
|
+
/** aria-label of the "mark this place" form. */
|
|
14
|
+
markFormLabel: string;
|
|
15
|
+
/** aria-label and title of the main panel. */
|
|
16
|
+
panelTitle: string;
|
|
17
|
+
markHereTitle: string;
|
|
18
|
+
/** Word before the heading in position lines ("looking N 12"). */
|
|
19
|
+
lookingAt: string;
|
|
20
|
+
teamLabel: string;
|
|
21
|
+
notePlaceholder: string;
|
|
22
|
+
saveButton: string;
|
|
23
|
+
cancelButton: string;
|
|
24
|
+
yes: string;
|
|
25
|
+
no: string;
|
|
26
|
+
placesTitle: string;
|
|
27
|
+
marksTitle: (count: number) => string;
|
|
28
|
+
savedOnServer: string;
|
|
29
|
+
savedInBrowser: string;
|
|
30
|
+
noNote: string;
|
|
31
|
+
goButton: string;
|
|
32
|
+
deleteButton: string;
|
|
33
|
+
noMarks: string;
|
|
34
|
+
copyAll: string;
|
|
35
|
+
downloadFile: string;
|
|
36
|
+
closeButton: string;
|
|
37
|
+
copied: string;
|
|
38
|
+
copySelected: string;
|
|
39
|
+
savedServerMsg: string;
|
|
40
|
+
serverFailMsg: string;
|
|
41
|
+
savedBrowserMsg: string;
|
|
42
|
+
hudLook: string;
|
|
43
|
+
hudFlight: (speed: string) => string;
|
|
44
|
+
hudMarks: (count: number) => string;
|
|
45
|
+
}
|
|
46
|
+
declare const locales: Record<DevtoolsLocale, DevtoolsStrings>;
|
|
47
|
+
declare const resolveStrings: (locale?: string, overrides?: Partial<DevtoolsStrings>) => DevtoolsStrings;
|
|
48
|
+
|
|
49
|
+
/** The only three.js member the devtools need at runtime. Pass your own `THREE` instance. */
|
|
50
|
+
type ThreeLib = Pick<typeof ThreeNamespace, 'Vector3'>;
|
|
51
|
+
/** A camera that can project world points to the screen. */
|
|
52
|
+
type ProjectionCamera = ThreeNamespace.Camera;
|
|
53
|
+
/** Where the player is. Yaw 0 looks toward -z (north); positive pitch looks down. */
|
|
54
|
+
interface PlayerPosition {
|
|
55
|
+
x: number;
|
|
56
|
+
y: number;
|
|
57
|
+
z: number;
|
|
58
|
+
yaw: number;
|
|
59
|
+
pitch: number;
|
|
60
|
+
}
|
|
61
|
+
/** `[team id, team label]` pairs shown in the mark form. */
|
|
62
|
+
type Team = [id: string, label: string];
|
|
63
|
+
interface Bounds {
|
|
64
|
+
x: [number, number];
|
|
65
|
+
y: [number, number];
|
|
66
|
+
z: [number, number];
|
|
67
|
+
}
|
|
68
|
+
interface DevtoolsOptions {
|
|
69
|
+
/** localStorage key where marks are kept when there is no server database. */
|
|
70
|
+
marksStorageKey?: string;
|
|
71
|
+
/** Database collection used when a `window.claude` database is available. */
|
|
72
|
+
collection?: string;
|
|
73
|
+
/** File name of the downloaded marks file (default comes from the locale). */
|
|
74
|
+
fileName?: string;
|
|
75
|
+
/** Teams offered in the mark form (default: one team `general`). */
|
|
76
|
+
teams?: Team[];
|
|
77
|
+
/** Name of the area at world (x, z). */
|
|
78
|
+
area?: (x: number, z: number) => string;
|
|
79
|
+
/** Time-of-day text shown in the HUD and stored in marks. */
|
|
80
|
+
time?: () => string;
|
|
81
|
+
/** Short message to the player: `notify(text, seconds)`. */
|
|
82
|
+
notify?: (text: string, seconds?: number) => void;
|
|
83
|
+
/** `pause(true)` when a panel opens, `pause(false)` when it closes. */
|
|
84
|
+
pause?: (paused: boolean) => void;
|
|
85
|
+
/** Current player position. */
|
|
86
|
+
position?: () => PlayerPosition;
|
|
87
|
+
/** Free-fly limits. */
|
|
88
|
+
bounds?: Bounds;
|
|
89
|
+
/** Initial free-fly speed (m/s). */
|
|
90
|
+
speed?: number;
|
|
91
|
+
/** Pool size of the entity labels. */
|
|
92
|
+
labelsMax?: number;
|
|
93
|
+
/** Kept for compatibility; not read by the devtools. */
|
|
94
|
+
labelsDistance?: number;
|
|
95
|
+
locale?: DevtoolsLocale | (string & {});
|
|
96
|
+
/** Overrides for individual user-facing strings. */
|
|
97
|
+
strings?: Partial<DevtoolsStrings>;
|
|
98
|
+
}
|
|
99
|
+
/** Snapshot of "here": position, area, heading, time. */
|
|
100
|
+
interface HereInfo {
|
|
101
|
+
x: number;
|
|
102
|
+
y: number;
|
|
103
|
+
z: number;
|
|
104
|
+
yaw: number;
|
|
105
|
+
pitch: number;
|
|
106
|
+
area: string;
|
|
107
|
+
/** Compass heading, e.g. `NE`. */
|
|
108
|
+
heading: string;
|
|
109
|
+
/** Heading in degrees, 0..359. */
|
|
110
|
+
degrees: number;
|
|
111
|
+
/** Look angle in degrees (positive = up). */
|
|
112
|
+
look: number;
|
|
113
|
+
time: string;
|
|
114
|
+
flying: boolean;
|
|
115
|
+
}
|
|
116
|
+
/** A saved note. */
|
|
117
|
+
interface Mark extends HereInfo {
|
|
118
|
+
id: string;
|
|
119
|
+
team: string;
|
|
120
|
+
text: string;
|
|
121
|
+
/** Epoch milliseconds. */
|
|
122
|
+
at: number;
|
|
123
|
+
/** User id when saved on a server. */
|
|
124
|
+
by?: string;
|
|
125
|
+
}
|
|
126
|
+
interface FlyKeys {
|
|
127
|
+
/** -1..1 */
|
|
128
|
+
forward: number;
|
|
129
|
+
/** -1..1 */
|
|
130
|
+
strafe: number;
|
|
131
|
+
/** -1..1 */
|
|
132
|
+
up: number;
|
|
133
|
+
/** 4x speed when truthy. */
|
|
134
|
+
fast?: boolean | number;
|
|
135
|
+
}
|
|
136
|
+
interface EntityLabel {
|
|
137
|
+
x: number;
|
|
138
|
+
y: number;
|
|
139
|
+
z: number;
|
|
140
|
+
text: string;
|
|
141
|
+
/** CSS colour, default `#9fd0ff`. */
|
|
142
|
+
color?: string;
|
|
143
|
+
}
|
|
144
|
+
/** `[group name, [[place name, go()]]]` */
|
|
145
|
+
type PlaceGroup = [name: string, places: Array<[name: string, go: () => void]>];
|
|
146
|
+
interface Scenario {
|
|
147
|
+
id: string;
|
|
148
|
+
[extra: string]: unknown;
|
|
149
|
+
}
|
|
150
|
+
/** Result of a scenario check: criterion -> verdict. */
|
|
151
|
+
type ScenarioResult = Record<string, {
|
|
152
|
+
ok: boolean;
|
|
153
|
+
note?: string;
|
|
154
|
+
}>;
|
|
155
|
+
interface Devtools {
|
|
156
|
+
/** Whether free-fly is on. Set it from your own key handler. */
|
|
157
|
+
flying: boolean;
|
|
158
|
+
x: number;
|
|
159
|
+
y: number;
|
|
160
|
+
z: number;
|
|
161
|
+
/** Free-fly speed (m/s); the mouse wheel changes it while flying. */
|
|
162
|
+
speed: number;
|
|
163
|
+
/** Whether the position HUD is visible. */
|
|
164
|
+
hud: boolean;
|
|
165
|
+
/** Which panel is open (`null` when none). */
|
|
166
|
+
openPanel: 'form' | 'list' | null;
|
|
167
|
+
/** Whether entity labels are shown. */
|
|
168
|
+
showLabels: boolean;
|
|
169
|
+
/** Server database handle when `window.claude` offers one. */
|
|
170
|
+
db: any;
|
|
171
|
+
marks: Mark[];
|
|
172
|
+
scenarioList: Scenario[];
|
|
173
|
+
activeScenario: Scenario | null;
|
|
174
|
+
/** Set by the game: teleport to a mark. */
|
|
175
|
+
goTo: ((x: number, y: number, z: number, yaw: number, pitch: number) => void) | null;
|
|
176
|
+
/** Set by the game: extra text appended to the HUD line. */
|
|
177
|
+
extras: () => string;
|
|
178
|
+
/** Compass heading and degrees for a yaw. */
|
|
179
|
+
compass(yaw: number): [heading: string, degrees: number];
|
|
180
|
+
here(): HereInfo;
|
|
181
|
+
/** One-line text for a mark. */
|
|
182
|
+
line(mark: Pick<Mark, 'team' | 'area' | 'x' | 'y' | 'z' | 'heading' | 'degrees' | 'time'> & {
|
|
183
|
+
text?: string;
|
|
184
|
+
}): string;
|
|
185
|
+
/** Moves the free-fly position for this frame. */
|
|
186
|
+
fly(dt: number, yaw: number, pitch: number, keys: FlyKeys): void;
|
|
187
|
+
/** Places the camera at the free-fly position. */
|
|
188
|
+
flyCamera(camera: ProjectionCamera, yaw: number, pitch: number): void;
|
|
189
|
+
/** Registers an on/off switch in the panel; `set` returns true to close the panel. */
|
|
190
|
+
toggle(id: string, label: string, get: () => boolean, set: (on: boolean) => boolean | void): Devtools;
|
|
191
|
+
/** Registers a button; `fn` returns true to keep the panel open. */
|
|
192
|
+
action(id: string, label: string | (() => string), fn: () => boolean | void): Devtools;
|
|
193
|
+
/** Registers time-of-day buttons: `[[label, value]]`. */
|
|
194
|
+
timesOfDay(list: Array<[string, unknown]>, set: (value: unknown) => void): Devtools;
|
|
195
|
+
/** Registers test places. */
|
|
196
|
+
places(fn: () => PlaceGroup[]): Devtools;
|
|
197
|
+
/** Registers the entity label source. */
|
|
198
|
+
labelsFor(fn: (camera: ProjectionCamera) => EntityLabel[]): Devtools;
|
|
199
|
+
/** Registers scenarios with an `apply` and an automatic `check`. */
|
|
200
|
+
scenarios<S extends Scenario>(list: S[], apply: (scenario: S) => void, check: (scenario: S) => ScenarioResult): Devtools;
|
|
201
|
+
/** Applies a scenario; false if the id is unknown. */
|
|
202
|
+
scenario(id: string): boolean;
|
|
203
|
+
/** Checks a scenario (or the active one). */
|
|
204
|
+
check(id?: string): ScenarioResult;
|
|
205
|
+
open(which: 'form' | 'list'): void;
|
|
206
|
+
close(): void;
|
|
207
|
+
/** Re-renders the list panel. */
|
|
208
|
+
render(): void;
|
|
209
|
+
/** Call every frame. */
|
|
210
|
+
update(dt: number, camera: ProjectionCamera, visible?: boolean): void;
|
|
211
|
+
/** Exposes `window.__scenario`, `__scenarioCheck`, `__scenarioList`, `__devtools` for automated browser tests. */
|
|
212
|
+
expose(): void;
|
|
213
|
+
/** Removes the DOM, styles and listeners. */
|
|
214
|
+
dispose(): void;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Minimal shape of the optional `window.claude` runtime that provides a shared database. */
|
|
218
|
+
interface ClaudeRuntime {
|
|
219
|
+
use(name: 'db' | 'user'): Promise<any>;
|
|
220
|
+
}
|
|
221
|
+
declare global {
|
|
222
|
+
interface Window {
|
|
223
|
+
claude?: ClaudeRuntime;
|
|
224
|
+
__scenario?: Devtools['scenario'];
|
|
225
|
+
__scenarioCheck?: Devtools['check'];
|
|
226
|
+
__scenarioList?: unknown;
|
|
227
|
+
__devtools?: Devtools;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Creates the devtools: free-fly, position HUD, marks with notes, control panel, scenarios and entity labels.
|
|
232
|
+
* The game plugs everything in through the options and the registration methods; see the README.
|
|
233
|
+
*/
|
|
234
|
+
declare function create(THREE: ThreeLib, options?: DevtoolsOptions): Devtools;
|
|
235
|
+
|
|
236
|
+
/** Escapes text for use inside HTML. */
|
|
237
|
+
declare const escapeHtml: (s: unknown) => string;
|
|
238
|
+
/** Compass heading and degrees for a yaw (yaw 0 looks toward -z, i.e. north). */
|
|
239
|
+
declare function compass(yaw: number, headings: DevtoolsStrings['compass']): [string, number];
|
|
240
|
+
type LineMark = Pick<Mark, 'team' | 'area' | 'x' | 'y' | 'z' | 'heading' | 'degrees' | 'time'> & {
|
|
241
|
+
text?: string;
|
|
242
|
+
};
|
|
243
|
+
/** One-line text for a mark: `[Team] Area · x 1 y 2 z 3 · looking N 0° · 12:00 · note`. */
|
|
244
|
+
declare function formatMarkLine(m: LineMark, teams: Team[], strings: Pick<DevtoolsStrings, 'lookingAt'>): string;
|
|
245
|
+
/** Newest first, without mutating the input. */
|
|
246
|
+
declare const sortMarks: (marks: Mark[]) => Mark[];
|
|
247
|
+
/**
|
|
248
|
+
* Serialises marks for copy and download.
|
|
249
|
+
* `text` is one line per mark (newest first); `file` is that text, a blank line, and the raw JSON array
|
|
250
|
+
* (the content of the downloaded file, which Claude or a script can parse back).
|
|
251
|
+
*/
|
|
252
|
+
declare function serializeMarks(marks: Mark[], teams: Team[], strings: Pick<DevtoolsStrings, 'lookingAt'>): {
|
|
253
|
+
text: string;
|
|
254
|
+
file: string;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
interface ScenarioRunner<S extends Scenario = Scenario> {
|
|
258
|
+
/** Registers the scenarios, how to apply one and how to check it. */
|
|
259
|
+
set(list: S[], apply: (scenario: S) => void, check: (scenario: S) => ScenarioResult): void;
|
|
260
|
+
readonly list: S[];
|
|
261
|
+
readonly active: S | null;
|
|
262
|
+
/** Applies a scenario and makes it the active one; false if the id is unknown. */
|
|
263
|
+
run(id: string): boolean;
|
|
264
|
+
/** Checks the scenario with that id, or the active one when the id is unknown; `{}` when there is none. */
|
|
265
|
+
check(id?: string): ScenarioResult;
|
|
266
|
+
}
|
|
267
|
+
/** Pure scenario bookkeeping (no DOM): `{ id, ... }` scenarios, `apply(s)` and `check(s) -> { criterion: { ok, note } }`. */
|
|
268
|
+
declare function createScenarioRunner<S extends Scenario = Scenario>(): ScenarioRunner<S>;
|
|
269
|
+
|
|
270
|
+
export { type Bounds, type Devtools, type DevtoolsLocale, type DevtoolsOptions, type DevtoolsStrings, type EntityLabel, type FlyKeys, type HereInfo, type Mark, type PlaceGroup, type PlayerPosition, type ProjectionCamera, type Scenario, type ScenarioResult, type ScenarioRunner, type Team, type ThreeLib, compass, create, createScenarioRunner, escapeHtml, formatMarkLine, locales, resolveStrings, serializeMarks, sortMarks };
|