@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 +21 -0
- package/assets/ATTRIBUTION.md +72 -0
- package/assets/waica-iso-crate.png +0 -0
- package/assets/waica-iso-ground.png +0 -0
- package/assets/waica-iso-hero.png +0 -0
- package/assets/waica-iso-orc.png +0 -0
- package/assets/waica-iso-rock.png +0 -0
- package/assets/waica-iso-tree.png +0 -0
- package/assets/waica-iso-villager.png +0 -0
- package/dist/animation.d.ts +3 -0
- package/dist/animation.js +10 -0
- package/dist/art.d.ts +3 -0
- package/dist/art.js +10 -0
- package/dist/bundle.d.ts +2 -0
- package/dist/bundle.js +9 -0
- package/dist/controls.d.ts +3 -0
- package/dist/controls.js +14 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +15 -0
- package/dist/manifest.d.ts +16 -0
- package/dist/manifest.js +29 -0
- package/dist/prefabs.d.ts +18 -0
- package/dist/prefabs.js +196 -0
- package/dist/registry-data.d.ts +3 -0
- package/dist/registry-data.js +46 -0
- package/dist/registry.d.ts +4 -0
- package/dist/registry.js +17 -0
- package/dist/scene-default.d.ts +5 -0
- package/dist/scene-default.js +49 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +17 -0
- package/package.json +37 -0
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,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
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
|
+
];
|
package/dist/bundle.d.ts
ADDED
package/dist/bundle.js
ADDED
package/dist/controls.js
ADDED
|
@@ -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
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
};
|
package/dist/manifest.js
ADDED
|
@@ -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>;
|
package/dist/prefabs.js
ADDED
|
@@ -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,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
|
+
});
|
package/dist/registry.js
ADDED
|
@@ -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
|
+
}
|