@waica/archetype-isometric 0.8.0 → 0.9.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/assets/ATTRIBUTION.md +40 -19
- package/assets/waica-iso-hero.png +0 -0
- package/assets/waica-iso-orc.png +0 -0
- package/assets/waica-iso-villager.png +0 -0
- package/dist/animation.d.ts +5 -1
- package/dist/animation.js +9 -2
- package/dist/controls.js +2 -0
- package/dist/manifest.js +1 -0
- package/dist/prefabs.d.ts +8 -4
- package/dist/prefabs.js +33 -14
- package/dist/registry-data.js +2 -1
- package/dist/scene-default.js +12 -2
- package/dist/ui.js +13 -0
- package/package.json +3 -3
package/assets/ATTRIBUTION.md
CHANGED
|
@@ -15,30 +15,51 @@ offline from originals retained outside the repository at
|
|
|
15
15
|
`Human-Worker-Red.png`, and `Orc-Grunt.png` (each 768×256, 24×8 cells
|
|
16
16
|
of 32×32).
|
|
17
17
|
|
|
18
|
-
Each output character sheet is
|
|
19
|
-
one transparent pixel between cells. Output
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
Each output character sheet is 11 columns × 5 rows of native 32×32 cells with
|
|
19
|
+
one transparent pixel between cells (362×164). Output rows are the five
|
|
20
|
+
east-facing rows of the pack; the source row order, verified on the sheets
|
|
21
|
+
themselves (2026-08-29), is `0 s · 1 se · 2 e · 3 ne · 4 n · 5 nw · 6 w · 7 sw`.
|
|
22
|
+
An earlier composition took rows 5/6/7 as `ne/e/se`, which face west, and
|
|
23
|
+
shipped the hero walking the wrong way; the row → facing table below is the
|
|
24
|
+
corrected one.
|
|
22
25
|
|
|
23
|
-
| Output facing | Source row | Cell
|
|
26
|
+
| Output facing | Source row | Cell rectangle of output column `c` |
|
|
24
27
|
|---|---:|---|
|
|
25
|
-
| `n` | 4 | `(
|
|
26
|
-
| `ne` |
|
|
27
|
-
| `e` |
|
|
28
|
-
| `se` |
|
|
29
|
-
| `s` | 0 | `(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
| `n` | 4 | `(32·src[c], 128, 32·src[c]+32, 160)` |
|
|
29
|
+
| `ne` | 3 | `(32·src[c], 96, 32·src[c]+32, 128)` |
|
|
30
|
+
| `e` | 2 | `(32·src[c], 64, 32·src[c]+32, 96)` |
|
|
31
|
+
| `se` | 1 | `(32·src[c], 32, 32·src[c]+32, 64)` |
|
|
32
|
+
| `s` | 0 | `(32·src[c], 0, 32·src[c]+32, 32)` |
|
|
33
|
+
|
|
34
|
+
Output columns are `0 idle · 1–3 walk · 4–6 attack · 7–8 hurt · 9–10 death`.
|
|
35
|
+
`src[c]` is the source column each output column is cut from; the warrior's
|
|
36
|
+
sword/hurt/death poses sit one column earlier than the other two sheets':
|
|
37
|
+
|
|
38
|
+
| Output file | Character | `src[0..10]` |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `waica-iso-hero.png` | `Warrior-Blue.png` | 0, 1, 2, 3, 5, 6, 7, 19, 20, 22, 23 |
|
|
41
|
+
| `waica-iso-villager.png` | `Human-Worker-Red.png` | 0, 1, 2, 3, 6, 7, 8, 20, 21, 22, 23 |
|
|
42
|
+
| `waica-iso-orc.png` | `Orc-Grunt.png` | 0, 1, 2, 3, 6, 7, 8, 20, 21, 22, 23 |
|
|
43
|
+
|
|
44
|
+
The villager sheet carries the sword columns like the others but declares no
|
|
45
|
+
`attack-*` clips.
|
|
46
|
+
|
|
47
|
+
### Mirror check
|
|
48
|
+
|
|
49
|
+
The west rows are absent from the committed sheets and resolve through
|
|
50
|
+
runtime mirroring (`flipX`). The composition compared every used cell of
|
|
51
|
+
source rows 1/2/3 against the horizontal mirror of rows 7/6/5. They are
|
|
52
|
+
**not pixel-exact**: idle/walk poses differ by 1–5 pixels (a hand or a
|
|
53
|
+
strap), and the sword poses by 30–60 pixels because the sword stays in the
|
|
54
|
+
same hand on both sides. Mirroring the east art is therefore a stylistic
|
|
55
|
+
approximation, not a lossless substitute — acceptable for the demo, and the
|
|
56
|
+
reason the human protocol checks the mirrored directions by eye.
|
|
36
57
|
|
|
37
58
|
| Output file | Character | Output size |
|
|
38
59
|
|---|---|---:|
|
|
39
|
-
| `waica-iso-hero.png` | `Warrior-Blue.png` |
|
|
40
|
-
| `waica-iso-villager.png` | `Human-Worker-Red.png` |
|
|
41
|
-
| `waica-iso-orc.png` | `Orc-Grunt.png` |
|
|
60
|
+
| `waica-iso-hero.png` | `Warrior-Blue.png` | 362×164 |
|
|
61
|
+
| `waica-iso-villager.png` | `Human-Worker-Red.png` | 362×164 |
|
|
62
|
+
| `waica-iso-orc.png` | `Orc-Grunt.png` | 362×164 |
|
|
42
63
|
|
|
43
64
|
## hawkbirdtree isometric tileset
|
|
44
65
|
|
|
Binary file
|
package/assets/waica-iso-orc.png
CHANGED
|
Binary file
|
|
Binary file
|
package/dist/animation.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { DirectionalAnimation } from '@waica/engine';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Five authored facings plus three horizontal mirror fallbacks; every pose
|
|
4
|
+
* a character can be asked for (walk, attack, hurt, death) degrades to its
|
|
5
|
+
* idle, still facing the right way.
|
|
6
|
+
*/
|
|
3
7
|
export declare const ISOMETRIC_ANIMATION: DirectionalAnimation;
|
package/dist/animation.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Five authored facings plus three horizontal mirror fallbacks; every pose
|
|
3
|
+
* a character can be asked for (walk, attack, hurt, death) degrades to its
|
|
4
|
+
* idle, still facing the right way.
|
|
5
|
+
*/
|
|
2
6
|
export const ISOMETRIC_ANIMATION = {
|
|
3
7
|
directions: ['n', 'ne', 'e', 'se', 's', 'sw', 'w', 'nw'],
|
|
4
8
|
fallbacks: {
|
|
@@ -6,5 +10,8 @@ export const ISOMETRIC_ANIMATION = {
|
|
|
6
10
|
nw: { dir: 'ne', flip: true },
|
|
7
11
|
sw: { dir: 'se', flip: true },
|
|
8
12
|
},
|
|
9
|
-
contract: {
|
|
13
|
+
contract: {
|
|
14
|
+
required: ['idle'],
|
|
15
|
+
fallbacks: { walk: 'idle', attack: 'idle', hurt: 'idle', death: 'idle' },
|
|
16
|
+
},
|
|
10
17
|
};
|
package/dist/controls.js
CHANGED
|
@@ -4,6 +4,7 @@ export const ISOMETRIC_BINDINGS = {
|
|
|
4
4
|
left: ['ArrowLeft', 'KeyA'],
|
|
5
5
|
right: ['ArrowRight', 'KeyD'],
|
|
6
6
|
interact: ['KeyE', 'Space'],
|
|
7
|
+
attack: ['KeyX', 'KeyJ'],
|
|
7
8
|
};
|
|
8
9
|
export const ISOMETRIC_ACTION_LABELS = {
|
|
9
10
|
up: 'Move up',
|
|
@@ -11,4 +12,5 @@ export const ISOMETRIC_ACTION_LABELS = {
|
|
|
11
12
|
left: 'Move left',
|
|
12
13
|
right: 'Move right',
|
|
13
14
|
interact: 'Interact',
|
|
15
|
+
attack: 'Attack',
|
|
14
16
|
};
|
package/dist/manifest.js
CHANGED
package/dist/prefabs.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { PrefabJson } from '@waica/engine';
|
|
2
|
+
type ClipMap = Record<string, {
|
|
3
|
+
frames: number[];
|
|
4
|
+
fps: number;
|
|
5
|
+
loop?: boolean;
|
|
6
|
+
}>;
|
|
2
7
|
export declare const ISOMETRIC_HERO_SPRITE: {
|
|
3
8
|
texture: string;
|
|
4
9
|
cols: number;
|
|
@@ -9,10 +14,9 @@ export declare const ISOMETRIC_HERO_SPRITE: {
|
|
|
9
14
|
width: number;
|
|
10
15
|
height: number;
|
|
11
16
|
anchorY: number;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
fps: number;
|
|
15
|
-
}>;
|
|
17
|
+
offsetY: number;
|
|
18
|
+
clips: ClipMap;
|
|
16
19
|
initialClip: string;
|
|
17
20
|
};
|
|
18
21
|
export declare const ISOMETRIC_PREFABS: Record<string, PrefabJson>;
|
|
22
|
+
export {};
|
package/dist/prefabs.js
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import { ISO_PLAYER_STATE_GRAPH, NPC_STATE_GRAPH, PATROLLER_STATE_GRAPH, } from '@waica/behaviors';
|
|
2
2
|
const SHIPPED_DIRECTIONS = ['n', 'ne', 'e', 'se', 's'];
|
|
3
|
-
|
|
3
|
+
/** One row per shipped facing: idle · walk×3 · attack×3 · hurt×2 · death×2. */
|
|
4
|
+
const SHEET_COLUMNS = 11;
|
|
5
|
+
const SOUTH_ROW = SHIPPED_DIRECTIONS.indexOf('s') * SHEET_COLUMNS;
|
|
6
|
+
/** Transparent texels under the boots in every Puny cell, in render units (9 / 16). */
|
|
7
|
+
const BOOTS_OFFSET = -9 / 16;
|
|
8
|
+
function directionalClips({ plain = false, attacks = false }) {
|
|
4
9
|
const clips = {};
|
|
5
10
|
SHIPPED_DIRECTIONS.forEach((direction, row) => {
|
|
6
|
-
const first = row *
|
|
11
|
+
const first = row * SHEET_COLUMNS;
|
|
7
12
|
clips[`idle-${direction}`] = { frames: [first], fps: 2 };
|
|
8
13
|
clips[`walk-${direction}`] = {
|
|
9
14
|
frames: [first + 1, first + 2, first + 3, first + 2],
|
|
10
15
|
fps: 8,
|
|
11
16
|
};
|
|
17
|
+
if (attacks) {
|
|
18
|
+
clips[`attack-${direction}`] = { frames: [first + 4, first + 5, first + 6], fps: 12, loop: false };
|
|
19
|
+
}
|
|
20
|
+
clips[`hurt-${direction}`] = { frames: [first + 7, first + 8], fps: 8, loop: false };
|
|
21
|
+
clips[`death-${direction}`] = { frames: [first + 9, first + 10], fps: 4, loop: false };
|
|
12
22
|
});
|
|
13
|
-
if (
|
|
14
|
-
clips['idle'] = { frames: [
|
|
15
|
-
clips['walk'] = { frames: [
|
|
23
|
+
if (plain) {
|
|
24
|
+
clips['idle'] = { frames: [SOUTH_ROW], fps: 2 };
|
|
25
|
+
clips['walk'] = { frames: [SOUTH_ROW + 1, SOUTH_ROW + 2, SOUTH_ROW + 3, SOUTH_ROW + 2], fps: 8 };
|
|
16
26
|
}
|
|
17
27
|
return clips;
|
|
18
28
|
}
|
|
19
|
-
function characterSprite(texture,
|
|
29
|
+
function characterSprite(texture, options = {}) {
|
|
20
30
|
return {
|
|
21
31
|
texture,
|
|
22
|
-
cols:
|
|
32
|
+
cols: SHEET_COLUMNS,
|
|
23
33
|
rows: 5,
|
|
24
34
|
spacingX: 1,
|
|
25
35
|
spacingY: 1,
|
|
@@ -27,11 +37,16 @@ function characterSprite(texture, includePlain = false) {
|
|
|
27
37
|
width: 2,
|
|
28
38
|
height: 2,
|
|
29
39
|
anchorY: 0,
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
// The Puny bodies stand 9 texels above their cell's bottom edge; shift the
|
|
41
|
+
// quad down so the boots sit on the entity's position (its footprint),
|
|
42
|
+
// instead of floating half a unit up the screen — over a water diamond
|
|
43
|
+
// the body is actually stopped short of.
|
|
44
|
+
offsetY: BOOTS_OFFSET,
|
|
45
|
+
clips: directionalClips(options),
|
|
46
|
+
initialClip: options.plain ? 'idle' : 'idle-s',
|
|
32
47
|
};
|
|
33
48
|
}
|
|
34
|
-
export const ISOMETRIC_HERO_SPRITE = characterSprite('waica:iso-hero');
|
|
49
|
+
export const ISOMETRIC_HERO_SPRITE = characterSprite('waica:iso-hero', { attacks: true });
|
|
35
50
|
const MAP_WIDTH = 16;
|
|
36
51
|
const MAP_HEIGHT = 16;
|
|
37
52
|
const GRASS = 0;
|
|
@@ -68,8 +83,9 @@ export const ISOMETRIC_PREFABS = {
|
|
|
68
83
|
},
|
|
69
84
|
},
|
|
70
85
|
{ type: 'Hitbox', props: { width: 0.8, height: 0.8 } },
|
|
86
|
+
{ type: 'MeleeAttack' },
|
|
71
87
|
{ type: 'Respawnable' },
|
|
72
|
-
{ type: 'Health', props: { max: 3, invulnerability: 1 } },
|
|
88
|
+
{ type: 'Health', props: { max: 3, invulnerability: 1, stat: 'health' } },
|
|
73
89
|
],
|
|
74
90
|
},
|
|
75
91
|
'characters/villager': {
|
|
@@ -78,7 +94,7 @@ export const ISOMETRIC_PREFABS = {
|
|
|
78
94
|
components: [
|
|
79
95
|
{
|
|
80
96
|
type: 'AnimatedSprite',
|
|
81
|
-
props: characterSprite('waica:iso-villager', true),
|
|
97
|
+
props: characterSprite('waica:iso-villager', { plain: true }),
|
|
82
98
|
},
|
|
83
99
|
{
|
|
84
100
|
type: 'StateMachine',
|
|
@@ -99,8 +115,10 @@ export const ISOMETRIC_PREFABS = {
|
|
|
99
115
|
{
|
|
100
116
|
type: 'AnimatedSprite',
|
|
101
117
|
props: {
|
|
102
|
-
...characterSprite('waica:iso-orc', true),
|
|
103
|
-
|
|
118
|
+
...characterSprite('waica:iso-orc', { attacks: true }),
|
|
119
|
+
// Patrol reports facing, so the orc resolves directional clips
|
|
120
|
+
// from the first frame: it starts down its rail, screen south-east.
|
|
121
|
+
initialClip: 'walk-se',
|
|
104
122
|
},
|
|
105
123
|
},
|
|
106
124
|
{ type: 'Patrol', props: { axis: 'horizontal', distance: 2, speed: 2 } },
|
|
@@ -114,6 +132,7 @@ export const ISOMETRIC_PREFABS = {
|
|
|
114
132
|
},
|
|
115
133
|
{ type: 'Hitbox', props: { width: 0.8, height: 0.7 } },
|
|
116
134
|
{ type: 'Hazard', props: { stompable: false, contactDamage: 1 } },
|
|
135
|
+
{ type: 'Health', props: { max: 2, invulnerability: 0.3 } },
|
|
117
136
|
],
|
|
118
137
|
},
|
|
119
138
|
'objects/crate': {
|
package/dist/registry-data.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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';
|
|
2
|
+
import { Chaser, Collectible, Hazard, Health, Interactable, IsoMotor, Lifetime, MeleeAttack, Patrol, Respawnable, } from '@waica/behaviors';
|
|
3
3
|
import { ISOMETRIC_ART } from './art.js';
|
|
4
4
|
import { ISOMETRIC_PREFABS } from './prefabs.js';
|
|
5
5
|
import { ISOMETRIC_UI } from './ui.js';
|
|
@@ -13,6 +13,7 @@ export const ISOMETRIC_REGISTRY_DATA = {
|
|
|
13
13
|
Hitbox,
|
|
14
14
|
StateMachine,
|
|
15
15
|
IsoMotor,
|
|
16
|
+
MeleeAttack,
|
|
16
17
|
Interactable,
|
|
17
18
|
Collectible,
|
|
18
19
|
Patrol,
|
package/dist/scene-default.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Full-map framing plus a follow feel tuned for the diamond: a tight dead
|
|
3
|
+
* zone and no horizontal lookahead (the engine's step kicks in every time
|
|
4
|
+
* the player crosses walking speed, which lurches on short runs), a little
|
|
5
|
+
* vertical lead, and softer smoothing than the platformer default.
|
|
6
|
+
*/
|
|
1
7
|
function isometricCamera() {
|
|
2
8
|
return {
|
|
3
9
|
position: [0, -8],
|
|
4
10
|
zoom: 12,
|
|
5
11
|
limits: { minX: -16, maxX: 16, minY: -16, maxY: 0 },
|
|
12
|
+
deadzoneWidth: 0.5,
|
|
13
|
+
deadzoneHeight: 0.5,
|
|
14
|
+
lookahead: 0,
|
|
15
|
+
lookaheadY: 0.5,
|
|
16
|
+
smoothing: 4,
|
|
6
17
|
};
|
|
7
18
|
}
|
|
8
19
|
/** A logical 16x16 meadow projected into a 2:1 diamond at render time. */
|
|
@@ -12,7 +23,6 @@ export const ISOMETRIC_SCENE = {
|
|
|
12
23
|
camera: {
|
|
13
24
|
...isometricCamera(),
|
|
14
25
|
follow: 'Player',
|
|
15
|
-
lookaheadY: 1,
|
|
16
26
|
},
|
|
17
27
|
entities: [
|
|
18
28
|
{ name: 'Ground', prefab: 'tiles/ground', position: [0, 0] },
|
|
@@ -38,7 +48,7 @@ export const ISOMETRIC_SCENE = {
|
|
|
38
48
|
{ name: 'Rock-1', prefab: 'objects/rock', position: [5, 11] },
|
|
39
49
|
{ name: 'Rock-2', prefab: 'objects/rock', position: [9, 4] },
|
|
40
50
|
],
|
|
41
|
-
ui: ['crate-counter'],
|
|
51
|
+
ui: ['crate-counter', 'health'],
|
|
42
52
|
};
|
|
43
53
|
/** Blank projects retain the diamond grid and full-map framing. */
|
|
44
54
|
export const ISOMETRIC_BLANK_SCENE = {
|
package/dist/ui.js
CHANGED
|
@@ -12,6 +12,19 @@ export const ISOMETRIC_UI = {
|
|
|
12
12
|
}
|
|
13
13
|
</style>
|
|
14
14
|
<div class="crate-counter">📦 {{points}}</div>
|
|
15
|
+
`,
|
|
16
|
+
health: `<style>
|
|
17
|
+
.health {
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 12px;
|
|
20
|
+
right: 12px;
|
|
21
|
+
font: 600 20px system-ui, sans-serif;
|
|
22
|
+
color: #ef476f;
|
|
23
|
+
text-shadow: 0 1px 3px #000a;
|
|
24
|
+
user-select: none;
|
|
25
|
+
}
|
|
26
|
+
</style>
|
|
27
|
+
<div class="health">♥ {{health}}</div>
|
|
15
28
|
`,
|
|
16
29
|
...INTERACTABLE_UI,
|
|
17
30
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waica/archetype-isometric",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Waica isometric archetype — logical-grid action adventures with screen-relative movement",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@waica/behaviors": "^0.
|
|
28
|
-
"@waica/engine": "^0.
|
|
27
|
+
"@waica/behaviors": "^0.9.0",
|
|
28
|
+
"@waica/engine": "^0.9.0"
|
|
29
29
|
}
|
|
30
30
|
}
|