@adatechnology/game-floorplan 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 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,355 @@
1
+ # Migration: `PlantasSDK` (plantas.js) to `@adatechnology/game-floorplan`
2
+
3
+ Source: `sdk/plantas/plantas.js` (PlantasSDK, about v3) with its `teste.mjs`, `README.md` and `montar-visor.mjs`. The SDK was split into four packages:
4
+ the generic engine, a residential content pack, a commercial content pack and a post-apocalyptic genre pack.
5
+
6
+ This file covers the **engine** (`@adatechnology/game-floorplan`): API, options, result fields, ids and the generic vocabularies. Content tables are in:
7
+
8
+ - [`game-floorplan-residential/MIGRATION.md`](../game-floorplan-residential/MIGRATION.md): residential templates, rooms, library ids, residential item types and categories, styles, facade values.
9
+ - [`game-floorplan-commercial/MIGRATION.md`](../game-floorplan-commercial/MIGRATION.md): market and pharmacy, shop item types.
10
+ - [`game-floorplan-post-apocalyptic/MIGRATION.md`](../game-floorplan-post-apocalyptic/MIGRATION.md): the `protegida` / `abandonada` states and their items.
11
+
12
+ ## Behaviour
13
+
14
+ The generator logic is unchanged. For the same seed and options (after the id mapping below) the new packages produce the same positions,
15
+ rotations, sizes, colours, palette integers, state items and slots as the old SDK. This was checked with a throw-away script that ran both
16
+ generators over all templates, variants, states, seeds, sizes and styles and compared every number, boolean and colour: 0 differences.
17
+ Names and labels are strings and come from the locale files; the `pt-BR` locale reproduces the old Portuguese texts.
18
+ The only deliberate behaviour additions are opt-in: the `zone` option (urban zoning) and the `locale` option. Without them nothing changes.
19
+
20
+ ## Loading
21
+
22
+ | Old | New |
23
+ |---|---|
24
+ | `<script>` global `PlantasSDK` / `require('./plantas.js')` (everything in one file) | ESM `import { engine } from '@adatechnology/game-floorplan'` plus the packs you need, or IIFE globals `GameFloorplan`, `GameFloorplanResidential`, `GameFloorplanCommercial`, `GameFloorplanPostApocalyptic` |
25
+ | templates, items and states built in | content is registered: `residential.register(engine)`, `commercial.register(engine)`, `postApocalyptic.register(engine)` |
26
+ | (not available) | `register(engine, { region: 'br' })`: region data, see the residential MIGRATION |
27
+
28
+ Minimal replacement of `PlantasSDK`:
29
+
30
+ ```ts
31
+ import { engine } from '@adatechnology/game-floorplan'
32
+ import { register as residential } from '@adatechnology/game-floorplan-residential'
33
+ import { register as commercial } from '@adatechnology/game-floorplan-commercial'
34
+ import { register as postApocalyptic } from '@adatechnology/game-floorplan-post-apocalyptic'
35
+
36
+ residential(engine); commercial(engine); postApocalyptic(engine)
37
+ const plan = engine.generate('townhouse', { seed: 3, state: 'fortified', locale: 'pt-BR' })
38
+ ```
39
+
40
+ ## API
41
+
42
+ | Old | New |
43
+ |---|---|
44
+ | `PlantasSDK.gerar(id, opcoes)` | `engine.generate(id, options)` (also a named export `generate`) |
45
+ | `PlantasSDK.slots(id, opcoes)` | `engine.slots(id, options)` |
46
+ | `PlantasSDK.validar(res)` | `engine.validate(result, { locale? })` |
47
+ | `PlantasSDK.paleta(opcoes)` | `engine.palette(options)` |
48
+ | `PlantasSDK.listarTemplates()` | `engine.listTemplates()` |
49
+ | `PlantasSDK.limitesDe(id, opcoes)` | `engine.limitsOf(id, options)` (returns `{ limits, defaults }`) |
50
+ | `PlantasSDK.registrarTemplate(t)` | `engine.registerTemplate(t)` |
51
+ | `PlantasSDK.registrarItem(item)` | `engine.registerItem(item)` / `engine.registerItems(items)` |
52
+ | `PlantasSDK.registrarBiblioteca(nome, fn)` | `engine.registerLibrary(name, fn)` (`registerLibraryAlias(alias, id)` is new) |
53
+ | `PlantasSDK.criaRng(semente, chave)` | `engine.createRng(seed, key)` |
54
+ | `PlantasSDK.sobrepoe` | `engine.overlaps` |
55
+ | `PlantasSDK.zonaJanela` | `engine.windowZone` |
56
+ | `PlantasSDK.zonasAbertura` | `engine.openingZones` |
57
+ | `PlantasSDK.templates` | `engine.templates` |
58
+ | `PlantasSDK.catalogo` | `engine.catalog` |
59
+ | `PlantasSDK.biblioteca` | `engine.libraries` |
60
+ | `PlantasSDK.estilos` | `engine.styles` |
61
+ | `PlantasSDK.estados` | `engine.listStates()` (only `normal` until a state pack is registered) |
62
+ | `PlantasSDK.cor` | `engine.color` |
63
+ | `PlantasSDK.cor.lstar` / `hsl` / `hex` | `engine.color.lstar` / `hsl` / `hex` |
64
+ | `PlantasSDK.cor.MIN_PAREDE_PISO` | `engine.color.MIN_WALL_FLOOR_CONTRAST` |
65
+ | `PlantasSDK.cor.MIN_TECIDO_PISO` | `engine.color.MIN_FABRIC_FLOOR_CONTRAST` |
66
+ | `PlantasSDK.cor.SAT_ALTA` | `engine.color.HIGH_SATURATION` |
67
+ | `PlantasSDK.constantes.ESP` | `engine.constants.WALL_THICKNESS` |
68
+ | `PlantasSDK.constantes.ZONA_PORTA` | `engine.constants.DOOR_CLEARANCE` |
69
+ | `PlantasSDK.versao` | `engine.version` |
70
+ | (none) | `engine.registerState`, `setStateDistribution`, `registerStyle`, `registerPaletteStyle`, `registerRoomColors`, `registerDecorProfile`, `registerPass`, `registerGroundOverlay`, `registerWallTop`, `registerSeedKeys`, `registerLocale`, `translate`, `listLocales`, `slotKit`, `hashString`, `mulberry32` |
71
+ | (none) | zoning: `engine.defineZone`, `getZone`, `listZones`, `checkLot`, `registerPermeableGround` |
72
+
73
+ The engine is a module-level singleton (`engine`). Packs only import its *types* and receive it in `register(engine, options)`, so an IIFE page that loads
74
+ several bundles shares one registry.
75
+
76
+ ## Options of `generate` / `slots` / `palette`
77
+
78
+ | Old | New | Notes |
79
+ |---|---|---|
80
+ | `semente` | `seed` | number or string |
81
+ | `w`, `d` | `w`, `d` | unchanged |
82
+ | `estilo` | `style` | values in the residential MIGRATION; alias `nobre` is now `luxury` |
83
+ | `estado` | `state` | `protegida` is `fortified`, `abandonada` is `abandoned`; default drawn from the seed by the state pack |
84
+ | `padrao` | `tier` | `apartment_modern`: `medio` is `mid`, `alto` is `high` |
85
+ | `andares` | `floors` | `mansion`, `mid_modern_townhouse`, `high_end_contemporary` |
86
+ | `elevador` | `elevator` | `mansion` |
87
+ | `andar` | `floor` | storey of an apartment unit |
88
+ | (none) | `locale` | `en` (default) or `pt-BR`; chooses `name` and messages |
89
+ | (none) | `zone` | zone id or `ZoneDef`; see Zoning below |
90
+
91
+ ## Result of `generate`
92
+
93
+ | Old | New |
94
+ |---|---|
95
+ | `template` | `template` |
96
+ | `nome` | `name` (localized); the key is in `label` |
97
+ | `semente` | `seed` |
98
+ | `estilo` | `style` |
99
+ | `estado` | `state` |
100
+ | `w`, `d` | `w`, `d` |
101
+ | `andares` | `floors` |
102
+ | `comodos` | `rooms` |
103
+ | `paredes` | `walls` |
104
+ | `portas` | `doors` |
105
+ | `janelas` | `windows` |
106
+ | `abertos` | `openPairs` |
107
+ | `fachada` | `facade` |
108
+ | `lote` | `lot` (`null` for apartments, as before) |
109
+ | `moveis` | `items` |
110
+ | `estadoItens` | `stateItems` |
111
+ | `cores` | `colors` |
112
+ | `predio` | `building` |
113
+ | `faltando` | `missing` |
114
+ | (none) | `use`, `partyWalls`, `zoning` (only with `zone`) |
115
+
116
+ Floor (`andares[i]`): `indice` is `index`, `nome` is `name` (+ `label`), `y0` is `y0`, `pe` is `ceiling`.
117
+
118
+ Room (`comodos[i]`): `id`, `tipo` is `type`, `nome` is `name` (+ `label`), `andar` is `floor`, `u0 v0 u1 v1` unchanged, `partes` is `parts`, `liga` is `link`, `pe` is `ceiling`, `cor` is `color` (`{ wall, floor }` palette keys).
119
+
120
+ Wall (`paredes[i]`): `andar` is `floor`, `eixo` is `axis`, `esp` is `thickness`, `lados` is `sides`, `ext` is `exterior`, `tipo` is `type`, `h` is `h`.
121
+
122
+ Door (`portas[i]`): `andar` is `floor`, `eixo` is `axis`, `larg` is `width`, `a`, `b` unchanged (`b: 'fora'` is `'outside'`), `abre` is `opensTo`, `ext` is `exterior`, `lado` is `side`, `folhas` is `leaves`, `modelo` is `model`, `dobradica` is `hinge`, `zonas` is `zones`, `bloqueada` is `blocked`, `aberta` is `open`.
123
+
124
+ Window (`janelas[i]`): `andar` is `floor`, `eixo` is `axis`, `larg` is `width`, `comodo` is `room`, `lado` is `side`, `tabuas` is `boards`.
125
+
126
+ Facade (`fachada`): `estilo` is `style`, `cor` is `color` (`{ name, key, hex }`), `acabamento` is `finish`, `grades` is `grilles`, `telhado` is `roof`, `sacada` is `balcony`, `varanda` is `veranda`, `portaAco` is `steelDoor`, `andares` is `floors`, `roles` is `roles` (`muro` is `wall`).
127
+
128
+ Lot (`lote`): `rect`, `recuo` is `setback`, `quintal` is `backyard`, `estilo` is `style`, `piscina` is `pool`, `zonas` is `zones`, `perimetro` is `perimeter`, `portoes` is `gates`, `caminhos` is `paths`; new: `sideYard`.
129
+
130
+ Lot zone: `id`, `tipo` is `type`, `nome` is `name` (+ `label`), `partes` is `parts`, `piso` is `ground`.
131
+
132
+ Perimeter segment: `lado` is `side`, `tipo` is `type`, `altura` is `height`, `topo` is `top`, `alturaTotal` is `totalHeight`, `esp` is `thickness`, `cor` is `color` (`muro` is `wall`).
133
+
134
+ Gate: `tipo` is `type`, `modelo` is `model`, `larg` is `width`, `altura` is `height`, `lado` is `side`, `para` is `to`, `aberto` is `open`.
135
+
136
+ Path: `tipo` is `type`, `de` is `from`, `para` is `to`, `larg` is `width`, `rects` and `points` unchanged.
137
+
138
+ Item (`moveis[i]`): `tipo` is `type`, `categoria` is `category`, `fu`/`fv` are `frontU`/`frontV`, `comodo` is `room`, `andar` is `floor`, `externo` is `outdoor`, `sobre` is `on`, `modo` is `mode`, `folga` is `clearance`, `plano` is `flat`, `baixo` is `low`, `montagem` is `mount`, `janela` is `window`, `apoio` is `supportId`, `suporte` is `bracket`, `liga` is `link`, `cor` is `color`, `camada` is `layer`, `zona` is `zone`, `poligono` is `polygon`, `barricada` is `barricade`, `reviradas` is `ransacked`, `porta_aberta` is `doorOpen`, `movido` is `moved`, `criado` is `created`, `par` is `par`. `x z ry w d h y aabb slot id decor` unchanged.
139
+
140
+ Slot (`slots()` rows): `comodo` is `room`, `externo` is `outdoor`, `ancora` is `anchor`, `lado` is `side`, `obrigatorio` is `required`, `prob` is `chance`, `folga` is `clearance`, `frente` is `front`, `grupo` is `group`, `requer` is `requires`, `sobre` is `on`, `alvo` is `target`, `valido` is `valid`, `motivo` is `reason` (a code, see Reasons).
141
+
142
+ Building (`predio`, `apartment_modern`): `andares` is `floors`, `unidadesPorAndar` is `unitsPerFloor`, `peDireito` is `ceilingHeight`, `laje` is `slab`, `unidade` is `unit`, `corredor` is `corridor`, `hall`, `elevador` is `elevator`, `escada` is `stairs`, `unidades` is `units`, `espelhada` is `mirrored`, `nota` is `note` (now in English).
143
+
144
+ ## Ids that changed shape
145
+
146
+ | Old | New |
147
+ |---|---|
148
+ | door `p<floor>_<i>` (`p0_1`) | `d<floor>_<i>` (`d0_1`) |
149
+ | window `j<floor>_<i>` | `w<floor>_<i>` |
150
+ | perimeter `m<i>`, gate `g<i>` | unchanged |
151
+ | path `cam_social` / `cam_garagem` / `cam_carga` | `pedestrian_path` / `garage_path` / `cargo_path` |
152
+ | item `<room>.<slot>` (`sala.rackDir`) | same shape with the new room and slot ids (`living.tv_unit_right`) |
153
+ | expanded window slot `<id>@j<i>` | `<id>@w<i>` |
154
+ | state item `estado.<n>.<tipo>` | `state.<n>.<type>` |
155
+
156
+ ## Slot reasons (`motivo` to `reason`) and messages
157
+
158
+ `reason` is now a code; the text is localized under `reasons` in the locale files.
159
+
160
+ | Old text | New code |
161
+ |---|---|
162
+ | `pai inválido` | `invalid_parent` |
163
+ | `lado inválido` | `invalid_side` |
164
+ | `bloqueado por porta` | `blocked_by_door` |
165
+ | `parede aberta` | `open_wall` |
166
+ | `canto inválido` | `invalid_corner` |
167
+ | `sem janela` | `no_window` |
168
+ | `janela fora do cômodo` | `window_outside_room` |
169
+ | `slot maior que o cômodo` | `larger_than_room` |
170
+ | `âncora desconhecida` | `unknown_anchor` |
171
+
172
+ `validate()` messages are localized too: `Sobreposição: a e b` is `overlap`, `Porta bloqueada por id` is `door_blocked`, `Portão bloqueado por id` is `gate_blocked`, `Item obrigatório ausente: sala/slot` is `missing_required`; new: `zone_violation`.
173
+
174
+ ## Slot anchors, sides, facing, mounts
175
+
176
+ ### Anchors
177
+ | Old | New |
178
+ |---|---|
179
+ | `parede` | `wall` |
180
+ | `canto` | `corner` |
181
+ | `centro` | `center` |
182
+ | `janela` | `window` |
183
+ | `sobre` | `on` |
184
+ | `dentro` | `inside` |
185
+
186
+ ### Sides and corners
187
+ | Old | New |
188
+ |---|---|
189
+ | `frente` | `front` |
190
+ | `fundos` | `back` |
191
+ | `fundo` | `back` |
192
+ | `esq` | `left` |
193
+ | `dir` | `right` |
194
+ | `qualquer` | `any` |
195
+ | `frente-esq` | `front-left` |
196
+ | `frente-dir` | `front-right` |
197
+ | `fundo-esq` | `back-left` |
198
+ | `fundo-dir` | `back-right` |
199
+
200
+ ### Facing (`frente`)
201
+ | Old | New |
202
+ |---|---|
203
+ | `centro` | `center` |
204
+ | `porta` | `door` |
205
+ | `alvo` | `target` |
206
+
207
+ ### Mounts (`montagem`)
208
+ | Old | New |
209
+ |---|---|
210
+ | `teto` | `ceiling` |
211
+ | `parede` | `wall` |
212
+ | `janela` | `window` |
213
+ | `apoio` | `support` |
214
+ | `chao` | `floor` |
215
+ | `cobertura` | `roof` |
216
+ | `solo` | `ground` |
217
+ | `muro` | `wall_top` |
218
+
219
+ ## Lot zone types, wall tops, gates, doors, finishes
220
+
221
+ | Old | New |
222
+ |---|---|
223
+ | `vaga` | `parking` |
224
+ | `jardim` | `garden` |
225
+ | `quintal` | `backyard` |
226
+ | `lateral` | `side_yard` |
227
+
228
+ Boundary types: `muro` is `wall`, `cerca` is `fence`, `grade` is `railing`. Wall tops: `nada` is `none`, `caco` is `glass`, `concertina` is `razor_wire`, `grade` is `railing`, `arame_farpado` is `barbed_wire` (registered by the post-apocalyptic pack).
229
+ Gate types: `social` is `pedestrian`, `garagem` is `garage`, `carga` is `cargo`. Door models: `basculante` is `tilting`, `correr` is `sliding`, `aco` is `steel`, `dupla` is `double`, `folha` is `single`. Hinge `ini` is `start`. Materials: `ferro` is `iron`, `bloco` is `block`, `alambrado` is `wire_mesh`.
230
+
231
+ ### Miscellaneous vocabulary
232
+ | Old | New |
233
+ |---|---|
234
+ | `fora` | `outside` |
235
+ | `social` | `pedestrian` |
236
+ | `carga` | `cargo` |
237
+ | `garagem_int` | `inner_garage` |
238
+ | `pedestre` | `pedestrian` |
239
+ | `veiculo` | `vehicle` |
240
+ | `ferro` | `iron` |
241
+ | `bloco` | `block` |
242
+ | `alambrado` | `wire_mesh` |
243
+ | `basculante` | `tilting` |
244
+ | `correr` | `sliding` |
245
+ | `aco` | `steel` |
246
+ | `dupla` | `double` |
247
+ | `folha` | `single` |
248
+ | `ini` | `start` |
249
+ | `guardacorpo` | `railing` |
250
+ | `fibrocimento` | `fiber_cement` |
251
+ | `laje` | `slab` |
252
+ | `reboco` | `plaster` |
253
+ | `chapisco` | `roughcast` |
254
+ | `reboco_parcial` | `partial_plaster` |
255
+ | `bloco_exposto` | `exposed_block` |
256
+ | `tijolo_aparente` | `exposed_brick` |
257
+ | `analoga` | `analogous` |
258
+ | `complementar` | `complementary` |
259
+ | `complementar_dividida` | `split_complementary` |
260
+ | `triade` | `triad` |
261
+ | `parede_` | `wall_` |
262
+ | `piso_` | `floor_` |
263
+ | `madeira` | `wood` |
264
+ | `tecido_principal` | `fabric_main` |
265
+ | `tecido_acento` | `fabric_accent` |
266
+ | `quadro_acento` | `picture_accent` |
267
+ | `ext_piso` | `exterior_floor` |
268
+ | `gramado_` | `lawn` |
269
+ | `fachada` | `facade` |
270
+ | `fachada_detalhe` | `facade_detail` |
271
+ | `portao` | `gate` |
272
+ | `telhado` | `roof` |
273
+ | `teto_role` | `ceiling` |
274
+ | `agua` | `water` |
275
+ | `terra_role` | `soil` |
276
+ | `deck_madeira_role` | `wood_deck` |
277
+ | `parede` | `wall` |
278
+ | `piso` | `floor` |
279
+ | `objeto` | `object` |
280
+
281
+ ## Palette
282
+
283
+ `palette(options)` returns the same integers as `paleta`; only the keys are renamed. Room keys are `wall_<room type>` and `floor_<room type>`.
284
+
285
+ | Old palette key | New palette key |
286
+ |---|---|
287
+ | `fachada` | `facade` |
288
+ | `fachada_detalhe` | `facade_detail` |
289
+ | `muro` | `wall` |
290
+ | `portao` | `gate` |
291
+ | `telhado` | `roof` |
292
+ | `teto` | `ceiling` |
293
+ | `madeira` | `wood` |
294
+ | `tecido_principal` | `fabric_main` |
295
+ | `tecido_acento` | `fabric_accent` |
296
+ | `quadro_acento` | `picture_accent` |
297
+ | `metal` | `metal` |
298
+ | `ext_piso` | `exterior_floor` |
299
+ | `gramado` | `lawn` |
300
+ | `terra` | `dirt` |
301
+ | `deck_madeira` | `deck_floor` |
302
+ | `agua` | `water` |
303
+
304
+ Harmony schemes: `analoga` is `analogous`, `complementar` is `complementary`, `complementar_dividida` is `split_complementary`, `triade` is `triad`; `meta.modoAcento` is `meta.accentMode`.
305
+ The palette recipes per style are data supplied by the packs (`registerPaletteStyle`); the harmony algorithm and the contrast rules stay in the engine.
306
+
307
+ ## Engine-level item types and categories (ground cover)
308
+
309
+ | Old type | New type | Old category | New category |
310
+ |---|---|---|---|
311
+ | `planta_alta` | `plant_tall` | `planta` | `plant` |
312
+ | `planta_baixa` | `plant_short` | `planta` | `plant` |
313
+ | `lixeira_ext` | `outdoor_bin` | `lixeira_ext` | `outdoor_bin` |
314
+ | `gramado` | `lawn` | `piso_ext` | `ground_cover` |
315
+ | `piso_ceramico` | `tile_floor` | `piso_ext` | `ground_cover` |
316
+ | `cimentado` | `concrete` | `piso_ext` | `ground_cover` |
317
+ | `deck_madeira` | `deck_floor` | `piso_ext` | `ground_cover` |
318
+ | `terra` | `dirt` | `piso_ext` | `ground_cover` |
319
+
320
+ | Old category | New category |
321
+ |---|---|
322
+ | `lixeira_ext` | `outdoor_bin` |
323
+ | `piso_ext` | `ground_cover` |
324
+ | `planta` | `plant` |
325
+
326
+ `piso_ext` is now `ground_cover` and is built into the engine, because the ground of every lot zone needs it. `lawn` and `dirt` are registered as permeable grounds.
327
+
328
+ ## Reasons for changes you may notice
329
+
330
+ - Item and slot ids carry English names, and the old nightstand slots `criadoE` / `criadoD` are `nightstand_left` / `nightstand_right`.
331
+ - Everything the old SDK held in Portuguese (names, labels, reasons, messages) is now a key plus a locale entry (`en`, `pt-BR`). Register more with `engine.registerLocale('xx', table)`.
332
+ - Randomness is drawn from the same named streams as before so seeds reproduce. Those stream names are the only Portuguese left in the code, in each pack's `legacy-seeds.ts` (for example `'fachada'`, `'lote'`, `'c:sala'`, `'estado:protegida'`). Do not translate them: changing a stream name changes every plan generated from an existing seed.
333
+
334
+ ## Zoning (new)
335
+
336
+ Urban zoning is a generic layer in the engine; the zones themselves are content (see the residential and commercial `regions/br`).
337
+
338
+ ```ts
339
+ import { engine, defineZone, checkLot } from '@adatechnology/game-floorplan'
340
+
341
+ defineZone({
342
+ id: 'my_zone', allowedUses: ['residential_single'], maxFloors: 2, maxHeight: 9,
343
+ setbacks: { front: 5, side: 1.5, back: 3 }, maxCoverage: 0.5, maxFar: 1, minLot: 250, minFrontage: 10, permeableMin: 0.25,
344
+ })
345
+
346
+ const plan = engine.generate('mansion', { seed: 1, zone: 'my_zone' })
347
+ plan.floors.length // 2: clamped from 3
348
+ plan.zoning // { zone, adjustments: [{ field: 'floors', from: 3, to: 2, reason: 'max_floors' }, ...], violations: [] }
349
+ checkLot('my_zone', plan.lot, plan) // [] when compliant, otherwise [{ rule, actual, limit }]
350
+ ```
351
+
352
+ - Uses are generic strings: `residential_single`, `residential_multi`, `commercial`, `mixed`, `industrial`, or your own. Each template declares its `use` (default `residential_single`) and `floorRange`.
353
+ - `generate(..., { zone })` clamps `floors` (or an apartment's `floor`) to `maxFloors` and `maxHeight`, raises the front, side and back setbacks (party walls are exempt on the shared side) and turns paved garden, yard and side zones into lawn until `permeableMin` is met. Each change is listed in `zoning.adjustments` as `{ field, from, to, reason }`.
354
+ - What cannot be fixed by clamping (a use that is not allowed, minimum lot, frontage, coverage, floor area ratio) is reported in `zoning.violations`. `validate()` includes them as `zone_violation` messages.
355
+ - `checkLot(zone, lot, plan)` checks any generated plan against any zone, so you can ask whether a plan fits a neighbourhood without regenerating it.
package/README.md CHANGED
@@ -1,3 +1,124 @@
1
- # Temporary Holding Version
1
+ # @adatechnology/game-floorplan
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Seeded procedural floor plans: house shells, lots, recorded furniture slots, seeded fill, a colour-harmony palette, state plugins and urban zoning.
4
+ The engine is generic. Content (templates, furniture, regional data, house states) comes from the packs below, so a game ships only what it uses.
5
+ Zero runtime dependencies; works in the browser and in Node. The same seed and options always give the same plan.
6
+
7
+ | Package | Global | Adds |
8
+ |---|---|---|
9
+ | `@adatechnology/game-floorplan` (this one) | `GameFloorplan` | engine, zoning, locales, palette algorithm, 2D viewer |
10
+ | [`@adatechnology/game-floorplan-residential`](../game-floorplan-residential) | `GameFloorplanResidential` | 10 house and apartment templates, furniture, decor, region `br` |
11
+ | [`@adatechnology/game-floorplan-commercial`](../game-floorplan-commercial) | `GameFloorplanCommercial` | market and pharmacy |
12
+ | [`@adatechnology/game-floorplan-post-apocalyptic`](../game-floorplan-post-apocalyptic) | `GameFloorplanPostApocalyptic` | `fortified` and `abandoned` house states |
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ pnpm add @adatechnology/game-floorplan @adatechnology/game-floorplan-residential
18
+ ```
19
+
20
+ Browser without a bundler:
21
+
22
+ ```html
23
+ <script src="https://unpkg.com/@adatechnology/game-floorplan/dist/index.global.js"></script>
24
+ <script src="https://unpkg.com/@adatechnology/game-floorplan-residential/dist/index.global.js"></script>
25
+ <script>
26
+ GameFloorplanResidential.register(GameFloorplan.engine)
27
+ const plan = GameFloorplan.engine.generate('townhouse', { seed: 3 })
28
+ </script>
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ```ts
34
+ import { engine } from '@adatechnology/game-floorplan'
35
+ import { register as residential } from '@adatechnology/game-floorplan-residential'
36
+ import { register as postApocalyptic } from '@adatechnology/game-floorplan-post-apocalyptic'
37
+
38
+ residential(engine, { region: 'br' })
39
+ postApocalyptic(engine) // optional: adds the fortified / abandoned states
40
+
41
+ engine.listTemplates() // [{ id, name, use, style, limits, defaults, floors, options, ... }]
42
+ const plan = engine.generate('townhouse', { seed: 7, state: 'fortified', locale: 'pt-BR' })
43
+ plan.rooms; plan.doors; plan.windows; plan.items; plan.stateItems; plan.lot; plan.colors
44
+ engine.validate(plan) // string[] of problems, empty when the plan is sound
45
+ engine.slots('townhouse', { seed: 7 }) // the recorded slots, valid or not, with the reason when not
46
+ engine.palette({ seed: 7, style: 'mid' }) // the colour palette alone
47
+ ```
48
+
49
+ ### Options
50
+
51
+ | Option | Meaning |
52
+ |---|---|
53
+ | `seed` | number or string |
54
+ | `w`, `d` | lot-side width and depth in metres, inside `limitsOf(id).limits` |
55
+ | `style` | `budget`, `mid`, `high` (alias `luxury`), `commercial` |
56
+ | `state` | `normal` or a state registered by a pack; the default is drawn from the seed |
57
+ | `tier` | `mid` or `high` (`apartment_modern`) |
58
+ | `floors`, `elevator` | `mansion`, `mid_modern_townhouse`, `high_end_contemporary` |
59
+ | `floor` | storey of an apartment unit |
60
+ | `locale` | `en` (default) or `pt-BR`: `name` fields and messages |
61
+ | `zone` | zone id or definition: clamps the plan and reports adjustments |
62
+
63
+ ### Result
64
+
65
+ `template, name, use, seed, style, state, w, d, floors, rooms, walls, doors, windows, openPairs, facade, lot, items, stateItems, colors, building, missing, partyWalls`
66
+ and, with a zone, `zoning`. Every localizable string has a key (`label`) and a localized `name`.
67
+
68
+ ## How it fits together
69
+
70
+ - **Content is registered.** `registerItem(s)`, `registerLibrary` (slot libraries), `registerTemplate`, `registerStyle`, `registerPaletteStyle`, `registerRoomColors`, `registerDecorProfile`, `registerPass` (post-process, e.g. decor), `registerGroundOverlay`, `registerWallTop`, `registerLocale`. A pack is just a `register(engine, options)` function; packs only import the engine's types, so IIFE bundles share one registry.
71
+ - **States are plugins.** The engine knows `normal` only. `registerState(name, { apply(ctx), adjustPalette?, seedKey? })` adds a state; `setStateDistribution(weights)` sets how the default state is drawn from the seed.
72
+ - **Regions are data.** Residential and commercial take `{ region: 'br' }` (default). A region holds paint swatches, facade colour names and the zoning table; add `regions/us` without touching templates.
73
+ - **Everything visible is a key.** Names, labels, slot reasons, validation messages and viewer text live in locale tables (`en`, `pt-BR`); `registerLocale('xx', table)` adds more.
74
+
75
+ ### Urban zoning
76
+
77
+ ```ts
78
+ import { defineZone, checkLot } from '@adatechnology/game-floorplan'
79
+
80
+ defineZone({ id: 'my_zone', allowedUses: ['residential_single'], maxFloors: 2, maxHeight: 9,
81
+ setbacks: { front: 5, side: 1.5, back: 3 }, maxCoverage: 0.5, maxFar: 1, minLot: 250, minFrontage: 10, permeableMin: 0.25 })
82
+
83
+ const plan = engine.generate('mansion', { seed: 1, zone: 'my_zone' })
84
+ plan.zoning.adjustments // [{ field: 'floors', from: 3, to: 2, reason: 'max_floors' }, ...]
85
+ plan.zoning.violations // what clamping could not fix: use, minimum lot, frontage, coverage, floor area ratio
86
+ checkLot('my_zone', plan.lot, plan)
87
+ ```
88
+
89
+ Very tall buildings do not belong in house neighbourhoods, so each template declares its `use` and floor range and each zone says what it allows.
90
+ `generate` clamps floors and setbacks to the zone and turns paved yards into lawn to reach the permeable minimum; `validate` reports remaining violations.
91
+ Uses are plain strings (`residential_single`, `residential_multi`, `commercial`, `mixed`, `industrial`, ...). The Brazilian zone table (ZR1, ZR-AP, ZR2, ZR3/ZM, ZC, ZI) ships in the content packs' `regions/br`.
92
+
93
+ ## 2D viewer
94
+
95
+ `viewer/` holds a plain HTML/Canvas viewer that bundles the four IIFE builds into one offline file. Template, seed, state, style, zone, options, floor and language (English or Português) are selectable; hovering an item shows its id, and the side panel shows validation and the zoning report.
96
+
97
+ ```sh
98
+ pnpm build
99
+ pnpm --filter @adatechnology/game-floorplan viewer:build # writes packages/game-floorplan/viewer/dist/floorplan-viewer.html
100
+ ```
101
+
102
+ ## Tests
103
+
104
+ `pnpm --filter @adatechnology/game-floorplan-post-apocalyptic test` runs the property suite ported from the old SDK on all four packs (25 seeds per template, state and variant). `FULL=1` runs the original 200 seeds.
105
+
106
+ See [MIGRATION.md](MIGRATION.md) for the old-to-new name tables.
107
+
108
+ ## Português
109
+
110
+ Gerador procedural de plantas de casas e lotes, com semente: casca da casa, lote, slots de móveis, preenchimento por semente, paleta de cores harmônica, estados da casa (plugins) e zoneamento urbano.
111
+ O motor é genérico; o conteúdo vem dos pacotes de conteúdo (residencial, comercial) e do pacote de gênero (pós-apocalíptico). A mesma semente e as mesmas opções dão sempre a mesma planta.
112
+
113
+ ```ts
114
+ import { engine } from '@adatechnology/game-floorplan'
115
+ import { register as residencial } from '@adatechnology/game-floorplan-residential'
116
+
117
+ residencial(engine, { region: 'br' })
118
+ const planta = engine.generate('townhouse', { seed: 7, locale: 'pt-BR' }) // nomes em português
119
+ ```
120
+
121
+ - Opções antigas: `semente` é `seed`, `estado` é `state`, `estilo` é `style`, `padrao` é `tier`, `andares` é `floors`, `elevador` é `elevator`, `andar` é `floor`. A tabela completa está em [MIGRATION.md](MIGRATION.md).
122
+ - **Zoneamento:** `defineZone` define uma zona (usos permitidos, andares, altura, recuos, taxa de ocupação, coeficiente de aproveitamento, lote mínimo, testada, permeabilidade). `generate(..., { zone })` limita andares e recuos à zona e informa os ajustes; `checkLot` aponta as violações. Edifícios muito altos não entram em bairro de casas.
123
+ - **Visualizador 2D:** `pnpm build` e depois `pnpm --filter @adatechnology/game-floorplan viewer:build` geram um HTML único e offline, com seletor de idioma (English e Português).
124
+ - **Testes:** a suíte de propriedades do SDK antigo foi portada para o vitest; `FULL=1` roda as 200 sementes originais, o padrão usa 25.