@waica/archetype-isometric 0.7.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 Ayrton Marini and Waica contributors
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.
@@ -0,0 +1,72 @@
1
+ # Art attribution and composition receipt
2
+
3
+ All source art is Creative Commons Zero (CC0). The run recomposed these files
4
+ offline from originals retained outside the repository at
5
+ `~/Sync/workspace/waica-art-src/iso-kit-a/`.
6
+
7
+ ## Puny Characters
8
+
9
+ - **Puny Characters** by Shade (merchant-shade)
10
+ - Source: https://merchant-shade.itch.io/16x16-puny-characters
11
+ - Mirror: https://opengameart.org/content/puny-characters
12
+ - Licence shown by the source page: **Creative Commons Zero v1.0 Universal**.
13
+ The author also states: “No need to give me credit.”
14
+ - Source files: `puny-characters/orcs/Puny-Characters/Warrior-Blue.png`,
15
+ `Human-Worker-Red.png`, and `Orc-Grunt.png` (each 768×256, 24×8 cells
16
+ of 32×32).
17
+
18
+ Each output character sheet is 4 columns × 5 rows of native 32×32 cells with
19
+ one transparent pixel between cells. Output columns are source columns 0, 1,
20
+ 2 and 3: idle followed by the three walk poses. Output rows and exact source
21
+ cell rectangles are:
22
+
23
+ | Output facing | Source row | Cell rectangles `(left, top, right, bottom)` |
24
+ |---|---:|---|
25
+ | `n` | 4 | `(0,128,32,160)`, `(32,128,64,160)`, `(64,128,96,160)`, `(96,128,128,160)` |
26
+ | `ne` | 5 | `(0,160,32,192)`, `(32,160,64,192)`, `(64,160,96,192)`, `(96,160,128,192)` |
27
+ | `e` | 6 | `(0,192,32,224)`, `(32,192,64,224)`, `(64,192,96,224)`, `(96,192,128,224)` |
28
+ | `se` | 7 | `(0,224,32,256)`, `(32,224,64,256)`, `(64,224,96,256)`, `(96,224,128,256)` |
29
+ | `s` | 0 | `(0,0,32,32)`, `(32,0,64,32)`, `(64,0,96,32)`, `(96,0,128,32)` |
30
+
31
+ The offline composition compared every one of those four poses against the
32
+ source mirror pair. Source row 2 (`w`) is the pixel-exact horizontal mirror of
33
+ row 6 (`e`), row 3 (`nw`) mirrors row 5 (`ne`), and row 1 (`sw`) mirrors row 7
34
+ (`se`) in all three character files. The west rows are therefore intentionally
35
+ absent from the committed sheets and resolve through runtime mirroring.
36
+
37
+ | Output file | Character | Output size |
38
+ |---|---|---:|
39
+ | `waica-iso-hero.png` | `Warrior-Blue.png` | 131×164 |
40
+ | `waica-iso-villager.png` | `Human-Worker-Red.png` | 131×164 |
41
+ | `waica-iso-orc.png` | `Orc-Grunt.png` | 131×164 |
42
+
43
+ ## hawkbirdtree isometric tileset
44
+
45
+ - **32x32 Isometric Tileset Version 0_1** by hawkbirdtree
46
+ - Source: https://opengameart.org/content/32x32-isometric-tileset-version-01
47
+ - Licence: **Creative Commons Zero (CC0)**.
48
+ - Source file: `hawkbirdtree-64x32/big_isometric_tileset64x32.png`
49
+ (1024×1344).
50
+
51
+ `waica-iso-ground.png` is a 5×1 regular sheet of flat 64×32 diamonds with
52
+ one transparent pixel between columns. No raised/cube tile is included:
53
+
54
+ | Index | Meaning | Source rectangle `(left, top, right, bottom)` |
55
+ |---:|---|---|
56
+ | 0 | grass | `(832,576,896,608)` |
57
+ | 1 | dirt | `(896,576,960,608)` |
58
+ | 2 | path | `(832,640,896,672)` |
59
+ | 3 | water | `(896,640,960,672)` |
60
+ | 4 | border | `(960,640,1024,672)` |
61
+
62
+ Tall art remains a Sprite prop. To keep nearest-neighbour sampling on one
63
+ power-of-two texel-density family, the crate is reduced to 32×32 with nearest
64
+ sampling and the 30×22 rock is bottom-centred at `(1,10)` on a transparent
65
+ 32×32 canvas. The tree remains native at 32 texels per render unit; characters
66
+ remain at the human-approved 16 texels per render unit.
67
+
68
+ | Output file | Source rectangle `(left, top, right, bottom)` | Output size |
69
+ |---|---|---:|
70
+ | `waica-iso-tree.png` | `(768,208,832,304)`, transparent bottom trimmed | 64×94 |
71
+ | `waica-iso-rock.png` | `(784,868,814,890)`, padded at `(1,10)` | 32×32 |
72
+ | `waica-iso-crate.png` | `(896,832,960,896)`, nearest-resized | 32×32 |
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ import type { DirectionalAnimation } from '@waica/engine';
2
+ /** Five authored facings plus three horizontal mirror fallbacks. */
3
+ export declare const ISOMETRIC_ANIMATION: DirectionalAnimation;
@@ -0,0 +1,10 @@
1
+ /** Five authored facings plus three horizontal mirror fallbacks. */
2
+ export const ISOMETRIC_ANIMATION = {
3
+ directions: ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'],
4
+ fallbacks: {
5
+ w: { dir: 'e', flip: true },
6
+ nw: { dir: 'ne', flip: true },
7
+ sw: { dir: 'se', flip: true },
8
+ },
9
+ contract: { required: ['idle'], fallbacks: { walk: 'idle' } },
10
+ };
package/dist/art.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { ArchetypeArt } from '@waica/engine';
2
+ /** Stock art as Node-safe package data. */
3
+ export declare const ISOMETRIC_ART: ArchetypeArt[];
package/dist/art.js ADDED
@@ -0,0 +1,10 @@
1
+ /** Stock art as Node-safe package data. */
2
+ export const ISOMETRIC_ART = [
3
+ { file: 'waica-iso-hero.png', uri: 'waica:iso-hero' },
4
+ { file: 'waica-iso-villager.png', uri: 'waica:iso-villager' },
5
+ { file: 'waica-iso-orc.png', uri: 'waica:iso-orc' },
6
+ { file: 'waica-iso-ground.png', uri: 'waica:iso-ground' },
7
+ { file: 'waica-iso-tree.png', uri: 'waica:iso-tree' },
8
+ { file: 'waica-iso-rock.png', uri: 'waica:iso-rock' },
9
+ { file: 'waica-iso-crate.png', uri: 'waica:iso-crate' },
10
+ ];
@@ -0,0 +1,2 @@
1
+ import type { ArchetypeBundle } from '@waica/engine';
2
+ export declare const ISOMETRIC_BUNDLE: ArchetypeBundle;
package/dist/bundle.js ADDED
@@ -0,0 +1,9 @@
1
+ import { CHASER_ROLE, ISO_PLAYER_ROLE, NPC_ROLE, PATROLLER_ROLE, } from '@waica/behaviors';
2
+ export const ISOMETRIC_BUNDLE = {
3
+ roles: {
4
+ player: ISO_PLAYER_ROLE,
5
+ patroller: PATROLLER_ROLE,
6
+ chaser: CHASER_ROLE,
7
+ npc: NPC_ROLE,
8
+ },
9
+ };
@@ -0,0 +1,3 @@
1
+ import type { InputBindings } from '@waica/engine';
2
+ export declare const ISOMETRIC_BINDINGS: Readonly<InputBindings>;
3
+ export declare const ISOMETRIC_ACTION_LABELS: Readonly<Record<string, string>>;
@@ -0,0 +1,14 @@
1
+ export const ISOMETRIC_BINDINGS = {
2
+ up: ['ArrowUp', 'KeyW'],
3
+ down: ['ArrowDown', 'KeyS'],
4
+ left: ['ArrowLeft', 'KeyA'],
5
+ right: ['ArrowRight', 'KeyD'],
6
+ interact: ['KeyE', 'Space'],
7
+ };
8
+ export const ISOMETRIC_ACTION_LABELS = {
9
+ up: 'Move up',
10
+ down: 'Move down',
11
+ left: 'Move left',
12
+ right: 'Move right',
13
+ interact: 'Interact',
14
+ };
@@ -0,0 +1,24 @@
1
+ export { ISOMETRIC_ANIMATION } from './animation.js';
2
+ export { ISOMETRIC_ART } from './art.js';
3
+ export { ISOMETRIC_BUNDLE } from './bundle.js';
4
+ export { ISOMETRIC_ACTION_LABELS, ISOMETRIC_BINDINGS } from './controls.js';
5
+ export { ISOMETRIC_PREFABS, ISOMETRIC_HERO_SPRITE } from './prefabs.js';
6
+ export { ISOMETRIC_ART_URLS, ISOMETRIC_PALETTE, ISOMETRIC_REGISTRY, } from './registry.js';
7
+ export { ISOMETRIC_BLANK_SCENE, ISOMETRIC_SCENE } from './scene-default.js';
8
+ export { ISOMETRIC_UI } from './ui.js';
9
+ export declare const ARCHETYPE: {
10
+ id: string;
11
+ label: string;
12
+ scene: import("@waica/engine").SceneJson;
13
+ blankScene: import("@waica/engine").SceneJson;
14
+ palette: import("@waica/engine").EntityTemplate[];
15
+ prefabs: Record<string, import("@waica/engine").PrefabJson>;
16
+ art: import("@waica/engine").ArchetypeArt[];
17
+ entityIcons: Readonly<Record<string, string>>;
18
+ bindings: Readonly<import("@waica/engine").InputBindings>;
19
+ actionLabels: Readonly<Record<string, string>>;
20
+ bundle: import("@waica/engine").ArchetypeBundle;
21
+ animation: import("@waica/engine").DirectionalAnimation<string>;
22
+ registry: import("@waica/engine").SceneRegistry;
23
+ artUrls: Record<string, string>;
24
+ };
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ import { ARCHETYPE as DATA_ARCHETYPE } from './manifest.js';
2
+ import { ISOMETRIC_ART_URLS, ISOMETRIC_REGISTRY } from './registry.js';
3
+ export { ISOMETRIC_ANIMATION } from './animation.js';
4
+ export { ISOMETRIC_ART } from './art.js';
5
+ export { ISOMETRIC_BUNDLE } from './bundle.js';
6
+ export { ISOMETRIC_ACTION_LABELS, ISOMETRIC_BINDINGS } from './controls.js';
7
+ export { ISOMETRIC_PREFABS, ISOMETRIC_HERO_SPRITE } from './prefabs.js';
8
+ export { ISOMETRIC_ART_URLS, ISOMETRIC_PALETTE, ISOMETRIC_REGISTRY, } from './registry.js';
9
+ export { ISOMETRIC_BLANK_SCENE, ISOMETRIC_SCENE } from './scene-default.js';
10
+ export { ISOMETRIC_UI } from './ui.js';
11
+ export const ARCHETYPE = {
12
+ ...DATA_ARCHETYPE,
13
+ registry: ISOMETRIC_REGISTRY,
14
+ artUrls: ISOMETRIC_ART_URLS,
15
+ };
@@ -0,0 +1,16 @@
1
+ export declare const ISOMETRIC_ENTITY_ICONS: Readonly<Record<string, string>>;
2
+ export declare const ARCHETYPE: {
3
+ id: string;
4
+ label: string;
5
+ scene: import("@waica/engine").SceneJson;
6
+ blankScene: import("@waica/engine").SceneJson;
7
+ registry: import("@waica/engine").SceneRegistry;
8
+ palette: import("@waica/engine").EntityTemplate[];
9
+ prefabs: Record<string, import("@waica/engine").PrefabJson>;
10
+ art: import("@waica/engine").ArchetypeArt[];
11
+ entityIcons: Readonly<Record<string, string>>;
12
+ bindings: Readonly<import("@waica/engine").InputBindings>;
13
+ actionLabels: Readonly<Record<string, string>>;
14
+ bundle: import("@waica/engine").ArchetypeBundle;
15
+ animation: import("@waica/engine").DirectionalAnimation<string>;
16
+ };
@@ -0,0 +1,29 @@
1
+ import { ISOMETRIC_ANIMATION } from './animation.js';
2
+ import { ISOMETRIC_ART } from './art.js';
3
+ import { ISOMETRIC_BUNDLE } from './bundle.js';
4
+ import { ISOMETRIC_ACTION_LABELS, ISOMETRIC_BINDINGS } from './controls.js';
5
+ import { ISOMETRIC_PREFABS } from './prefabs.js';
6
+ import { ISOMETRIC_PALETTE, ISOMETRIC_REGISTRY_DATA } from './registry-data.js';
7
+ import { ISOMETRIC_BLANK_SCENE, ISOMETRIC_SCENE } from './scene-default.js';
8
+ export const ISOMETRIC_ENTITY_ICONS = {
9
+ IsoMotor: '🧭',
10
+ Tilemap: '💎',
11
+ Interactable: '💬',
12
+ Collectible: '📦',
13
+ Hazard: '👹',
14
+ };
15
+ export const ARCHETYPE = {
16
+ id: 'isometric',
17
+ label: 'Isometric',
18
+ scene: ISOMETRIC_SCENE,
19
+ blankScene: ISOMETRIC_BLANK_SCENE,
20
+ registry: ISOMETRIC_REGISTRY_DATA,
21
+ palette: ISOMETRIC_PALETTE,
22
+ prefabs: ISOMETRIC_PREFABS,
23
+ art: ISOMETRIC_ART,
24
+ entityIcons: ISOMETRIC_ENTITY_ICONS,
25
+ bindings: ISOMETRIC_BINDINGS,
26
+ actionLabels: ISOMETRIC_ACTION_LABELS,
27
+ bundle: ISOMETRIC_BUNDLE,
28
+ animation: ISOMETRIC_ANIMATION,
29
+ };
@@ -0,0 +1,18 @@
1
+ import type { PrefabJson } from '@waica/engine';
2
+ export declare const ISOMETRIC_HERO_SPRITE: {
3
+ texture: string;
4
+ cols: number;
5
+ rows: number;
6
+ spacingX: number;
7
+ spacingY: number;
8
+ pixelArt: boolean;
9
+ width: number;
10
+ height: number;
11
+ anchorY: number;
12
+ clips: Record<string, {
13
+ frames: number[];
14
+ fps: number;
15
+ }>;
16
+ initialClip: string;
17
+ };
18
+ export declare const ISOMETRIC_PREFABS: Record<string, PrefabJson>;
@@ -0,0 +1,196 @@
1
+ import { ISO_PLAYER_STATE_GRAPH, NPC_STATE_GRAPH, PATROLLER_STATE_GRAPH, } from '@waica/behaviors';
2
+ const SHIPPED_DIRECTIONS = ['n', 'ne', 'e', 'se', 's'];
3
+ function directionalClips(includePlain) {
4
+ const clips = {};
5
+ SHIPPED_DIRECTIONS.forEach((direction, row) => {
6
+ const first = row * 4;
7
+ clips[`idle-${direction}`] = { frames: [first], fps: 2 };
8
+ clips[`walk-${direction}`] = {
9
+ frames: [first + 1, first + 2, first + 3, first + 2],
10
+ fps: 8,
11
+ };
12
+ });
13
+ if (includePlain) {
14
+ clips['idle'] = { frames: [16], fps: 2 };
15
+ clips['walk'] = { frames: [17, 18, 19, 18], fps: 8 };
16
+ }
17
+ return clips;
18
+ }
19
+ function characterSprite(texture, includePlain = false) {
20
+ return {
21
+ texture,
22
+ cols: 4,
23
+ rows: 5,
24
+ spacingX: 1,
25
+ spacingY: 1,
26
+ pixelArt: true,
27
+ width: 2,
28
+ height: 2,
29
+ anchorY: 0,
30
+ clips: directionalClips(includePlain),
31
+ initialClip: includePlain ? 'idle' : 'idle-s',
32
+ };
33
+ }
34
+ export const ISOMETRIC_HERO_SPRITE = characterSprite('waica:iso-hero');
35
+ const MAP_WIDTH = 16;
36
+ const MAP_HEIGHT = 16;
37
+ const GRASS = 0;
38
+ const DIRT = 1;
39
+ const PATH = 2;
40
+ const WATER = 3;
41
+ const BORDER = 4;
42
+ function tileAt(column, row) {
43
+ if (column === 0 || row === 0 || column === MAP_WIDTH - 1 || row === MAP_HEIGHT - 1) {
44
+ return BORDER;
45
+ }
46
+ if (column >= 11 && column <= 12 && row >= 5 && row <= 6)
47
+ return WATER;
48
+ if (column === 7 || row === 7)
49
+ return PATH;
50
+ if (column >= 2 && column <= 5 && row >= 2 && row <= 4)
51
+ return DIRT;
52
+ return GRASS;
53
+ }
54
+ const GROUND_CELLS = Array.from({ length: MAP_WIDTH * MAP_HEIGHT }, (_, index) => tileAt(index % MAP_WIDTH, Math.floor(index / MAP_WIDTH)));
55
+ export const ISOMETRIC_PREFABS = {
56
+ 'characters/player': {
57
+ waicaPrefab: 1,
58
+ type: 'character',
59
+ components: [
60
+ { type: 'AnimatedSprite', props: ISOMETRIC_HERO_SPRITE },
61
+ { type: 'IsoMotor' },
62
+ {
63
+ type: 'StateMachine',
64
+ props: {
65
+ role: 'player',
66
+ initial: ISO_PLAYER_STATE_GRAPH.initial,
67
+ states: ISO_PLAYER_STATE_GRAPH.states,
68
+ },
69
+ },
70
+ { type: 'Hitbox', props: { width: 0.8, height: 0.8 } },
71
+ { type: 'Respawnable' },
72
+ { type: 'Health', props: { max: 3, invulnerability: 1 } },
73
+ ],
74
+ },
75
+ 'characters/villager': {
76
+ waicaPrefab: 1,
77
+ type: 'character',
78
+ components: [
79
+ {
80
+ type: 'AnimatedSprite',
81
+ props: characterSprite('waica:iso-villager', true),
82
+ },
83
+ {
84
+ type: 'StateMachine',
85
+ props: {
86
+ role: 'npc',
87
+ initial: NPC_STATE_GRAPH.initial,
88
+ states: NPC_STATE_GRAPH.states,
89
+ },
90
+ },
91
+ { type: 'Interactable', props: { line: 'Welcome to Diamond Meadow!', radius: 1.5 } },
92
+ { type: 'Solid', props: { width: 0.8, height: 0.6 } },
93
+ ],
94
+ },
95
+ 'characters/orc': {
96
+ waicaPrefab: 1,
97
+ type: 'character',
98
+ components: [
99
+ {
100
+ type: 'AnimatedSprite',
101
+ props: {
102
+ ...characterSprite('waica:iso-orc', true),
103
+ initialClip: 'walk',
104
+ },
105
+ },
106
+ { type: 'Patrol', props: { axis: 'horizontal', distance: 2, speed: 2 } },
107
+ {
108
+ type: 'StateMachine',
109
+ props: {
110
+ role: 'patroller',
111
+ initial: PATROLLER_STATE_GRAPH.initial,
112
+ states: PATROLLER_STATE_GRAPH.states,
113
+ },
114
+ },
115
+ { type: 'Hitbox', props: { width: 0.8, height: 0.7 } },
116
+ { type: 'Hazard', props: { stompable: false, contactDamage: 1 } },
117
+ ],
118
+ },
119
+ 'objects/crate': {
120
+ waicaPrefab: 1,
121
+ type: 'object',
122
+ components: [
123
+ {
124
+ type: 'Sprite',
125
+ props: {
126
+ texture: 'waica:iso-crate',
127
+ pixelArt: true,
128
+ width: 1,
129
+ height: 1,
130
+ anchorY: 0,
131
+ },
132
+ },
133
+ { type: 'Hitbox', props: { width: 0.6, height: 0.6 } },
134
+ { type: 'Collectible', props: { value: 1 } },
135
+ ],
136
+ },
137
+ 'objects/tree': {
138
+ waicaPrefab: 1,
139
+ type: 'object',
140
+ components: [
141
+ {
142
+ type: 'Sprite',
143
+ props: {
144
+ texture: 'waica:iso-tree',
145
+ pixelArt: true,
146
+ width: 2,
147
+ height: 47 / 16,
148
+ anchorY: 0,
149
+ },
150
+ },
151
+ { type: 'Solid', props: { width: 0.8, height: 0.6 } },
152
+ ],
153
+ },
154
+ 'objects/rock': {
155
+ waicaPrefab: 1,
156
+ type: 'object',
157
+ components: [
158
+ {
159
+ type: 'Sprite',
160
+ props: {
161
+ texture: 'waica:iso-rock',
162
+ pixelArt: true,
163
+ width: 1,
164
+ height: 1,
165
+ anchorY: 0,
166
+ },
167
+ },
168
+ { type: 'Solid', props: { width: 0.8, height: 0.6 } },
169
+ ],
170
+ },
171
+ 'tiles/ground': {
172
+ waicaPrefab: 1,
173
+ type: 'tile',
174
+ components: [
175
+ {
176
+ type: 'Tilemap',
177
+ props: {
178
+ texture: 'waica:iso-ground',
179
+ cols: 5,
180
+ rows: 1,
181
+ spacingX: 1,
182
+ spacingY: 1,
183
+ cellWidth: 64,
184
+ cellHeight: 32,
185
+ pixelArt: true,
186
+ mapWidth: MAP_WIDTH,
187
+ mapHeight: MAP_HEIGHT,
188
+ cellSize: 1,
189
+ cells: GROUND_CELLS,
190
+ solidTiles: [WATER, BORDER],
191
+ layer: -2,
192
+ },
193
+ },
194
+ ],
195
+ },
196
+ };
@@ -0,0 +1,3 @@
1
+ import { type EntityTemplate, type SceneRegistry } from '@waica/engine';
2
+ export declare const ISOMETRIC_REGISTRY_DATA: SceneRegistry;
3
+ export declare const ISOMETRIC_PALETTE: EntityTemplate[];
@@ -0,0 +1,46 @@
1
+ import { AnimatedSprite, Hitbox, Solid, Sprite, StateMachine, Tilemap, } from '@waica/engine';
2
+ import { Chaser, Collectible, Hazard, Health, Interactable, IsoMotor, Lifetime, Patrol, Respawnable, } from '@waica/behaviors';
3
+ import { ISOMETRIC_ART } from './art.js';
4
+ import { ISOMETRIC_PREFABS } from './prefabs.js';
5
+ import { ISOMETRIC_UI } from './ui.js';
6
+ const PACKAGE_ASSETS = Object.fromEntries(ISOMETRIC_ART.map((art) => [art.uri, `assets/${art.file}`]));
7
+ export const ISOMETRIC_REGISTRY_DATA = {
8
+ components: {
9
+ Sprite,
10
+ AnimatedSprite,
11
+ Tilemap,
12
+ Solid,
13
+ Hitbox,
14
+ StateMachine,
15
+ IsoMotor,
16
+ Interactable,
17
+ Collectible,
18
+ Patrol,
19
+ Chaser,
20
+ Hazard,
21
+ Health,
22
+ Respawnable,
23
+ Lifetime,
24
+ },
25
+ resolveAsset: (uri) => PACKAGE_ASSETS[uri] ?? uri,
26
+ prefabs: ISOMETRIC_PREFABS,
27
+ ui: ISOMETRIC_UI,
28
+ };
29
+ const PALETTE_ICONS = {
30
+ player: '🧭',
31
+ villager: '🧑‍🌾',
32
+ orc: '👹',
33
+ crate: '📦',
34
+ tree: '🌳',
35
+ rock: '🪨',
36
+ ground: '💎',
37
+ };
38
+ export const ISOMETRIC_PALETTE = Object.entries(ISOMETRIC_PREFABS).map(([key, prefab]) => {
39
+ const base = key.slice(key.indexOf('/') + 1);
40
+ return {
41
+ label: base,
42
+ icon: PALETTE_ICONS[base] ?? '▣',
43
+ category: prefab.type,
44
+ make: () => ({ name: base.charAt(0).toUpperCase() + base.slice(1), prefab: key }),
45
+ };
46
+ });
@@ -0,0 +1,4 @@
1
+ import type { SceneRegistry } from '@waica/engine';
2
+ export { ISOMETRIC_PALETTE } from './registry-data.js';
3
+ export declare const ISOMETRIC_ART_URLS: Record<string, string>;
4
+ export declare const ISOMETRIC_REGISTRY: SceneRegistry;
@@ -0,0 +1,17 @@
1
+ import { ISOMETRIC_ART } from './art.js';
2
+ import { ISOMETRIC_REGISTRY_DATA } from './registry-data.js';
3
+ export { ISOMETRIC_PALETTE } from './registry-data.js';
4
+ export const ISOMETRIC_ART_URLS = {
5
+ 'waica-iso-hero.png': new URL('../assets/waica-iso-hero.png', import.meta.url).href,
6
+ 'waica-iso-villager.png': new URL('../assets/waica-iso-villager.png', import.meta.url).href,
7
+ 'waica-iso-orc.png': new URL('../assets/waica-iso-orc.png', import.meta.url).href,
8
+ 'waica-iso-ground.png': new URL('../assets/waica-iso-ground.png', import.meta.url).href,
9
+ 'waica-iso-tree.png': new URL('../assets/waica-iso-tree.png', import.meta.url).href,
10
+ 'waica-iso-rock.png': new URL('../assets/waica-iso-rock.png', import.meta.url).href,
11
+ 'waica-iso-crate.png': new URL('../assets/waica-iso-crate.png', import.meta.url).href,
12
+ };
13
+ const BUILTIN_ASSETS = Object.fromEntries(ISOMETRIC_ART.map((art) => [art.uri, ISOMETRIC_ART_URLS[art.file] ?? art.uri]));
14
+ export const ISOMETRIC_REGISTRY = {
15
+ ...ISOMETRIC_REGISTRY_DATA,
16
+ resolveAsset: (uri) => BUILTIN_ASSETS[uri] ?? uri,
17
+ };
@@ -0,0 +1,5 @@
1
+ import type { SceneJson } from '@waica/engine';
2
+ /** A logical 16x16 meadow projected into a 2:1 diamond at render time. */
3
+ export declare const ISOMETRIC_SCENE: SceneJson;
4
+ /** Blank projects retain the diamond grid and full-map framing. */
5
+ export declare const ISOMETRIC_BLANK_SCENE: SceneJson;
@@ -0,0 +1,49 @@
1
+ function isometricCamera() {
2
+ return {
3
+ position: [0, -8],
4
+ zoom: 12,
5
+ limits: { minX: -16, maxX: 16, minY: -16, maxY: 0 },
6
+ };
7
+ }
8
+ /** A logical 16x16 meadow projected into a 2:1 diamond at render time. */
9
+ export const ISOMETRIC_SCENE = {
10
+ waicaScene: 3,
11
+ render: { sort: 'y', projection: 'isometric' },
12
+ camera: {
13
+ ...isometricCamera(),
14
+ follow: 'Player',
15
+ lookaheadY: 1,
16
+ },
17
+ entities: [
18
+ { name: 'Ground', prefab: 'tiles/ground', position: [0, 0] },
19
+ { name: 'Player', prefab: 'characters/player', position: [8, 8] },
20
+ {
21
+ name: 'Villager',
22
+ prefab: 'characters/villager',
23
+ position: [6, 9],
24
+ overrides: { Interactable: { line: 'The water sparkles, but it blocks the trail.' } },
25
+ },
26
+ {
27
+ name: 'Orc',
28
+ prefab: 'characters/orc',
29
+ position: [10, 10],
30
+ overrides: { Patrol: { axis: 'horizontal', distance: 2.5, speed: 1.5 } },
31
+ },
32
+ { name: 'Crate-1', prefab: 'objects/crate', position: [5, 6] },
33
+ { name: 'Crate-2', prefab: 'objects/crate', position: [10, 4] },
34
+ { name: 'Crate-3', prefab: 'objects/crate', position: [12, 10] },
35
+ { name: 'Tree-1', prefab: 'objects/tree', position: [4, 10] },
36
+ { name: 'Tree-2', prefab: 'objects/tree', position: [10, 12] },
37
+ { name: 'Tree-3', prefab: 'objects/tree', position: [12, 3] },
38
+ { name: 'Rock-1', prefab: 'objects/rock', position: [5, 11] },
39
+ { name: 'Rock-2', prefab: 'objects/rock', position: [9, 4] },
40
+ ],
41
+ ui: ['crate-counter'],
42
+ };
43
+ /** Blank projects retain the diamond grid and full-map framing. */
44
+ export const ISOMETRIC_BLANK_SCENE = {
45
+ waicaScene: 3,
46
+ render: { sort: 'y', projection: 'isometric' },
47
+ camera: isometricCamera(),
48
+ entities: [],
49
+ };
package/dist/ui.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const ISOMETRIC_UI: Record<string, string>;
package/dist/ui.js ADDED
@@ -0,0 +1,17 @@
1
+ import { INTERACTABLE_UI } from '@waica/behaviors';
2
+ export const ISOMETRIC_UI = {
3
+ 'crate-counter': `<style>
4
+ .crate-counter {
5
+ position: absolute;
6
+ top: 12px;
7
+ left: 12px;
8
+ font: 600 20px system-ui, sans-serif;
9
+ color: #ffd166;
10
+ text-shadow: 0 1px 3px #000a;
11
+ user-select: none;
12
+ }
13
+ </style>
14
+ <div class="crate-counter">📦 {{points}}</div>
15
+ `,
16
+ ...INTERACTABLE_UI,
17
+ };
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@waica/archetype-isometric",
3
+ "version": "0.7.0",
4
+ "description": "Waica isometric archetype — logical-grid action adventures with screen-relative movement",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/chichex/waica",
10
+ "directory": "packages/archetype-isometric"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "assets"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./manifest": {
22
+ "types": "./dist/manifest.d.ts",
23
+ "default": "./dist/manifest.js"
24
+ }
25
+ },
26
+ "dependencies": {
27
+ "@waica/behaviors": "^0.7.0",
28
+ "@waica/engine": "^0.7.0"
29
+ },
30
+ "devDependencies": {
31
+ "@types/node": "^24.13.3"
32
+ },
33
+ "scripts": {
34
+ "build": "node ../../scripts/clean-dist.mjs && tsc -p tsconfig.build.json",
35
+ "typecheck": "tsc --noEmit"
36
+ }
37
+ }