@waica/cli 0.7.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/README.md +1 -1
- package/dist/editor/assets/{cssMode-eS4pAOyh.js → cssMode-DM5x3eXD.js} +1 -1
- package/dist/editor/assets/{freemarker2-FMQjZKQk.js → freemarker2-CmlzUOOI.js} +1 -1
- package/dist/editor/assets/{handlebars-dDYOnqat.js → handlebars-Dlz-T3Pu.js} +1 -1
- package/dist/editor/assets/{html-Ba6xZh4T.js → html-B-NfgvUd.js} +1 -1
- package/dist/editor/assets/{htmlMode-DC9qJr2p.js → htmlMode-K4hF3BAu.js} +1 -1
- package/dist/editor/assets/{index-CoyeInOT.js → index-Ca2oPxGt.js} +4626 -1104
- package/dist/editor/assets/{index-Cy5pmBhN.css → index-CzarIYLW.css} +1 -1
- package/dist/editor/assets/{javascript-DOwFvmvM.js → javascript-COj32tw-.js} +1 -1
- package/dist/editor/assets/{jsonMode-CcG7zlSf.js → jsonMode-DyL12Tvg.js} +1 -1
- package/dist/editor/assets/{liquid-Dfp71r7J.js → liquid-CGaRZ9Iu.js} +1 -1
- package/dist/editor/assets/{mdx-Ci7vVeht.js → mdx-L4fRd4xO.js} +1 -1
- package/dist/editor/assets/{python-Bluv3Y9Q.js → python-Z6FCrHUG.js} +1 -1
- package/dist/editor/assets/{razor-BtwTXMaK.js → razor-6XSfF-a3.js} +1 -1
- package/dist/editor/assets/{tsMode-D3_D-H85.js → tsMode-C24rNZgD.js} +1 -1
- package/dist/editor/assets/{typescript-Ch_PS5f7.js → typescript-BcFln1pD.js} +1 -1
- package/dist/editor/assets/waica-iso-hero-BsEdW0V7.png +0 -0
- package/dist/editor/assets/waica-iso-orc-DRK-SaXm.png +0 -0
- package/dist/editor/assets/waica-iso-villager-szDkD_Ag.png +0 -0
- package/dist/editor/assets/{xml-CI_bH_MX.js → xml-DJCue8rm.js} +1 -1
- package/dist/editor/assets/{yaml-LD9GTZyy.js → yaml-DiJf3V99.js} +1 -1
- package/dist/editor/index.html +2 -2
- package/dist/mcp/known-archetypes.js +5 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/ATTRIBUTION.md +93 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-crate.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-ground.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-hero.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-orc.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-rock.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-tree.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/assets/waica-iso-villager.png +0 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/animation.d.ts +7 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/animation.js +17 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/art.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/art.js +10 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/bundle.d.ts +2 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/bundle.js +9 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/controls.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/controls.js +16 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/index.d.ts +24 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/index.js +15 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/manifest.d.ts +16 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/manifest.js +30 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/prefabs.d.ts +22 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/prefabs.js +215 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry-data.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry-data.js +47 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry.d.ts +4 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/registry.js +17 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/scene-default.d.ts +5 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/scene-default.js +59 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/ui.d.ts +1 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/dist/ui.js +30 -0
- package/dist/mcp/node_modules/@waica/archetype-isometric/package.json +30 -0
- package/dist/mcp/node_modules/@waica/archetype-platformer/package.json +3 -3
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/animation.d.ts +2 -1
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/animation.js +6 -2
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/registry-data.js +2 -1
- package/dist/mcp/node_modules/@waica/archetype-topdown/dist/ui.js +2 -19
- package/dist/mcp/node_modules/@waica/archetype-topdown/package.json +3 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/facing.d.ts +18 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/facing.js +56 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-motor.d.ts +64 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-motor.js +77 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-player-role.d.ts +20 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/grid-player-role.js +133 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/health.d.ts +29 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/health.js +79 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.d.ts +4 -1
- package/dist/mcp/node_modules/@waica/behaviors/dist/index.js +4 -1
- package/dist/mcp/node_modules/@waica/behaviors/dist/interactable.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/interactable.js +25 -2
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-motor.d.ts +21 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-motor.js +24 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-player-states.d.ts +5 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/iso-player-states.js +9 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/melee-attack.d.ts +48 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/melee-attack.js +83 -0
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.d.ts +11 -3
- package/dist/mcp/node_modules/@waica/behaviors/dist/patrol.js +49 -5
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-motor.d.ts +7 -61
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-motor.js +8 -79
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-player-states.d.ts +4 -17
- package/dist/mcp/node_modules/@waica/behaviors/dist/topdown-player-states.js +10 -82
- package/dist/mcp/node_modules/@waica/behaviors/package.json +2 -2
- package/dist/mcp/node_modules/@waica/engine/dist/component.d.ts +2 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.d.ts +19 -1
- package/dist/mcp/node_modules/@waica/engine/dist/components/animated-sprite.js +35 -7
- package/dist/mcp/node_modules/@waica/engine/dist/components/dynamic-body.js +2 -4
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.d.ts +19 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/sprite.js +44 -8
- package/dist/mcp/node_modules/@waica/engine/dist/components/tilemap.d.ts +113 -0
- package/dist/mcp/node_modules/@waica/engine/dist/components/tilemap.js +316 -0
- package/dist/mcp/node_modules/@waica/engine/dist/entity.d.ts +3 -0
- package/dist/mcp/node_modules/@waica/engine/dist/entity.js +13 -1
- package/dist/mcp/node_modules/@waica/engine/dist/game.d.ts +4 -0
- package/dist/mcp/node_modules/@waica/engine/dist/game.js +41 -9
- package/dist/mcp/node_modules/@waica/engine/dist/index.d.ts +9 -0
- package/dist/mcp/node_modules/@waica/engine/dist/index.js +5 -0
- package/dist/mcp/node_modules/@waica/engine/dist/projection.d.ts +14 -0
- package/dist/mcp/node_modules/@waica/engine/dist/projection.js +30 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene-solids.d.ts +16 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene-solids.js +28 -0
- package/dist/mcp/node_modules/@waica/engine/dist/scene.d.ts +3 -1
- package/dist/mcp/node_modules/@waica/engine/dist/solid-axis.js +2 -4
- package/dist/mcp/node_modules/@waica/engine/dist/sprite-placement.d.ts +22 -0
- package/dist/mcp/node_modules/@waica/engine/dist/sprite-placement.js +14 -0
- package/dist/mcp/node_modules/@waica/engine/dist/tilemap-grid.d.ts +26 -0
- package/dist/mcp/node_modules/@waica/engine/dist/tilemap-grid.js +41 -0
- package/dist/mcp/node_modules/@waica/engine/package.json +1 -1
- package/dist/mcp/package-resolver.js +4 -0
- package/dist/mcp/project-component-runner.js +1 -0
- package/dist/mcp/template/src/stats.json +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ScreenFacing } from './facing.js';
|
|
2
|
+
import { GridMotor } from './grid-motor.js';
|
|
3
|
+
/** The eight screen-relative facings declared by the isometric archetype. */
|
|
4
|
+
export type IsoFacing = ScreenFacing;
|
|
5
|
+
/**
|
|
6
|
+
* Passive isometric motor. Input and facing are screen-relative while the
|
|
7
|
+
* shared GridMotor integrates and collides in logical square-grid space.
|
|
8
|
+
* State code owns the frame; this component has no onUpdate of its own.
|
|
9
|
+
*/
|
|
10
|
+
export declare class IsoMotor extends GridMotor<IsoFacing> {
|
|
11
|
+
static componentName: string;
|
|
12
|
+
facing: IsoFacing;
|
|
13
|
+
/**
|
|
14
|
+
* Under the diamond projection neither logical axis is "up the screen", so
|
|
15
|
+
* the footprint is square: as deep as it is wide, one cell-ish, instead of
|
|
16
|
+
* the short body top-down uses to let the player overlap a fence's top.
|
|
17
|
+
*/
|
|
18
|
+
hitboxHeight: number;
|
|
19
|
+
/** Accelerates screen-relative input toward a logical-space velocity. */
|
|
20
|
+
run(inputX: number, inputY: number, dt: number): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { screenInputToLogical } from '@waica/engine';
|
|
2
|
+
import { facingForInput } from './facing.js';
|
|
3
|
+
import { GridMotor } from './grid-motor.js';
|
|
4
|
+
/**
|
|
5
|
+
* Passive isometric motor. Input and facing are screen-relative while the
|
|
6
|
+
* shared GridMotor integrates and collides in logical square-grid space.
|
|
7
|
+
* State code owns the frame; this component has no onUpdate of its own.
|
|
8
|
+
*/
|
|
9
|
+
export class IsoMotor extends GridMotor {
|
|
10
|
+
static componentName = 'IsoMotor';
|
|
11
|
+
facing = 's';
|
|
12
|
+
/**
|
|
13
|
+
* Under the diamond projection neither logical axis is "up the screen", so
|
|
14
|
+
* the footprint is square: as deep as it is wide, one cell-ish, instead of
|
|
15
|
+
* the short body top-down uses to let the player overlap a fence's top.
|
|
16
|
+
*/
|
|
17
|
+
hitboxHeight = 0.9;
|
|
18
|
+
/** Accelerates screen-relative input toward a logical-space velocity. */
|
|
19
|
+
run(inputX, inputY, dt) {
|
|
20
|
+
const direction = screenInputToLogical(inputX, inputY);
|
|
21
|
+
this.accelerateToward(direction.x, direction.y, dt);
|
|
22
|
+
this.facing = facingForInput(inputX, inputY) ?? this.facing;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** The state graph new isometric player characters start with, as prefab data. */
|
|
2
|
+
export declare const ISO_PLAYER_STATE_GRAPH: import("@waica/engine").RoleGraph;
|
|
3
|
+
/** Drives IsoMotor from screen-relative actions and reports body state. */
|
|
4
|
+
export declare const isoPlayerUpdate: (ctx: import("@waica/engine").StateContext, dt: number) => void;
|
|
5
|
+
export declare const ISO_PLAYER_ROLE: import("@waica/engine").RoleDefinition;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createGridPlayerRole } from './grid-player-role.js';
|
|
2
|
+
import { IsoMotor } from './iso-motor.js';
|
|
3
|
+
const isoPlayer = createGridPlayerRole(IsoMotor, 'You control this character with screen-relative eight-direction movement ' +
|
|
4
|
+
'projected into an isometric logical world. Its states move the Motor.');
|
|
5
|
+
/** The state graph new isometric player characters start with, as prefab data. */
|
|
6
|
+
export const ISO_PLAYER_STATE_GRAPH = isoPlayer.graph;
|
|
7
|
+
/** Drives IsoMotor from screen-relative actions and reports body state. */
|
|
8
|
+
export const isoPlayerUpdate = isoPlayer.update;
|
|
9
|
+
export const ISO_PLAYER_ROLE = isoPlayer.role;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Component, type Entity } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* A short frontal strike. Passive like a motor: state code decides when it
|
|
4
|
+
* happens (the player's `attack` state) and calls strike(facing); this only
|
|
5
|
+
* knows where the blow lands and who can be hurt by it. The hit area is a
|
|
6
|
+
* `range` × `width` rectangle in logical space, laid along the facing from
|
|
7
|
+
* the attacker's position — under the isometric projection that is the
|
|
8
|
+
* diamond diagonal screen-east maps to, so "in front" matches the screen.
|
|
9
|
+
*/
|
|
10
|
+
export declare class MeleeAttack extends Component {
|
|
11
|
+
static componentName: string;
|
|
12
|
+
static displayName: string;
|
|
13
|
+
static params: {
|
|
14
|
+
damage: {
|
|
15
|
+
label: string;
|
|
16
|
+
min: number;
|
|
17
|
+
max: number;
|
|
18
|
+
step: number;
|
|
19
|
+
};
|
|
20
|
+
range: {
|
|
21
|
+
label: string;
|
|
22
|
+
min: number;
|
|
23
|
+
max: number;
|
|
24
|
+
step: number;
|
|
25
|
+
};
|
|
26
|
+
width: {
|
|
27
|
+
label: string;
|
|
28
|
+
min: number;
|
|
29
|
+
max: number;
|
|
30
|
+
step: number;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
damage: number;
|
|
34
|
+
/** How far in front of the attacker the blow reaches, in world units. */
|
|
35
|
+
range: number;
|
|
36
|
+
/** How wide the blow is, across the facing. */
|
|
37
|
+
width: number;
|
|
38
|
+
/**
|
|
39
|
+
* Lands one blow along `facing`: every other living entity with a Hitbox
|
|
40
|
+
* and a Health inside the strike area takes `damage` once, with this
|
|
41
|
+
* entity as the source. Returns the entities whose health actually
|
|
42
|
+
* dropped — a target still inside its invulnerability window is not
|
|
43
|
+
* counted. An unknown facing strikes nothing.
|
|
44
|
+
*/
|
|
45
|
+
strike(facing: string): Entity[];
|
|
46
|
+
/** The oriented rectangle in front of the attacker, as a unit-scaled polygon. */
|
|
47
|
+
private strikeArea;
|
|
48
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Component, Hitbox, collisionOverlap, } from '@waica/engine';
|
|
2
|
+
import { logicalDirection } from './facing.js';
|
|
3
|
+
import { Health } from './health.js';
|
|
4
|
+
/**
|
|
5
|
+
* A short frontal strike. Passive like a motor: state code decides when it
|
|
6
|
+
* happens (the player's `attack` state) and calls strike(facing); this only
|
|
7
|
+
* knows where the blow lands and who can be hurt by it. The hit area is a
|
|
8
|
+
* `range` × `width` rectangle in logical space, laid along the facing from
|
|
9
|
+
* the attacker's position — under the isometric projection that is the
|
|
10
|
+
* diamond diagonal screen-east maps to, so "in front" matches the screen.
|
|
11
|
+
*/
|
|
12
|
+
export class MeleeAttack extends Component {
|
|
13
|
+
static componentName = 'MeleeAttack';
|
|
14
|
+
static displayName = 'Melee attack';
|
|
15
|
+
static params = {
|
|
16
|
+
damage: { label: 'Damage', min: 0, max: 20, step: 1 },
|
|
17
|
+
range: { label: 'Range', min: 0.5, max: 5, step: 0.5 },
|
|
18
|
+
width: { label: 'Width', min: 0.5, max: 5, step: 0.5 },
|
|
19
|
+
};
|
|
20
|
+
damage = 1;
|
|
21
|
+
/** How far in front of the attacker the blow reaches, in world units. */
|
|
22
|
+
range = 1;
|
|
23
|
+
/** How wide the blow is, across the facing. */
|
|
24
|
+
width = 1;
|
|
25
|
+
/**
|
|
26
|
+
* Lands one blow along `facing`: every other living entity with a Hitbox
|
|
27
|
+
* and a Health inside the strike area takes `damage` once, with this
|
|
28
|
+
* entity as the source. Returns the entities whose health actually
|
|
29
|
+
* dropped — a target still inside its invulnerability window is not
|
|
30
|
+
* counted. An unknown facing strikes nothing.
|
|
31
|
+
*/
|
|
32
|
+
strike(facing) {
|
|
33
|
+
const direction = logicalDirection(facing, this.game.projection);
|
|
34
|
+
if (!direction)
|
|
35
|
+
return [];
|
|
36
|
+
const area = this.strikeArea(direction.x, direction.y);
|
|
37
|
+
const struck = [];
|
|
38
|
+
// A copy: a target with no death-handling graph is destroyed on the spot,
|
|
39
|
+
// which splices it out of the live array mid-loop (Game does the same).
|
|
40
|
+
for (const other of [...this.game.entities]) {
|
|
41
|
+
if (other === this.entity || !other.alive)
|
|
42
|
+
continue;
|
|
43
|
+
const hitbox = other.get(Hitbox);
|
|
44
|
+
const health = other.get(Health);
|
|
45
|
+
if (!hitbox || !health)
|
|
46
|
+
continue;
|
|
47
|
+
const body = {
|
|
48
|
+
x: other.position.x + hitbox.offsetX,
|
|
49
|
+
y: other.position.y + hitbox.offsetY,
|
|
50
|
+
width: hitbox.width,
|
|
51
|
+
height: hitbox.height,
|
|
52
|
+
shape: hitbox.shape,
|
|
53
|
+
points: hitbox.points,
|
|
54
|
+
};
|
|
55
|
+
if (!collisionOverlap(area, body))
|
|
56
|
+
continue;
|
|
57
|
+
const before = health.current;
|
|
58
|
+
health.damage(this.damage, this.entity);
|
|
59
|
+
if (health.current < before)
|
|
60
|
+
struck.push(other);
|
|
61
|
+
}
|
|
62
|
+
return struck;
|
|
63
|
+
}
|
|
64
|
+
/** The oriented rectangle in front of the attacker, as a unit-scaled polygon. */
|
|
65
|
+
strikeArea(dx, dy) {
|
|
66
|
+
const along = { x: (dx * this.range) / 2, y: (dy * this.range) / 2 };
|
|
67
|
+
const across = { x: (-dy * this.width) / 2, y: (dx * this.width) / 2 };
|
|
68
|
+
const points = [
|
|
69
|
+
[-along.x - across.x, -along.y - across.y],
|
|
70
|
+
[along.x - across.x, along.y - across.y],
|
|
71
|
+
[along.x + across.x, along.y + across.y],
|
|
72
|
+
[-along.x + across.x, -along.y + across.y],
|
|
73
|
+
];
|
|
74
|
+
return {
|
|
75
|
+
x: this.entity.position.x + along.x,
|
|
76
|
+
y: this.entity.position.y + along.y,
|
|
77
|
+
width: 1,
|
|
78
|
+
height: 1,
|
|
79
|
+
shape: 'polygon',
|
|
80
|
+
points,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { Component, type RoleDefinition, type RoleGraph } from '@waica/engine';
|
|
1
|
+
import { Component, type AnimationFacingProvider, type RoleDefinition, type RoleGraph } from '@waica/engine';
|
|
2
2
|
export type PatrolAxis = 'horizontal' | 'vertical';
|
|
3
3
|
/**
|
|
4
4
|
* Rail patrol: back and forth `distance` units from the starting
|
|
5
5
|
* position along one axis — sideways or up and down — turning around
|
|
6
|
-
* at the ends
|
|
6
|
+
* at the ends. It reports which way it walks as seen on screen, so a
|
|
7
|
+
* directional contract picks the matching clip; without one, walking
|
|
8
|
+
* sideways flips the sprite by scale instead.
|
|
7
9
|
* Passive like a motor: no onUpdate of its own — the 'patroller' logic
|
|
8
10
|
* set's walk state calls step(dt), so the StateMachine stays the single
|
|
9
11
|
* owner of the frame.
|
|
10
12
|
*/
|
|
11
|
-
export declare class Patrol extends Component {
|
|
13
|
+
export declare class Patrol extends Component implements AnimationFacingProvider {
|
|
12
14
|
static componentName: string;
|
|
13
15
|
static params: {
|
|
14
16
|
axis: {
|
|
@@ -36,6 +38,12 @@ export declare class Patrol extends Component {
|
|
|
36
38
|
private originY;
|
|
37
39
|
private dir;
|
|
38
40
|
onReady(): void;
|
|
41
|
+
/**
|
|
42
|
+
* The rail direction as rendered: the logical axis it walks along,
|
|
43
|
+
* projected when the scene is isometric — a horizontal rail reads as
|
|
44
|
+
* south-east/north-west there, a vertical one as south-west/north-east.
|
|
45
|
+
*/
|
|
46
|
+
getAnimationFacing(): string;
|
|
39
47
|
/** One patrol step: advance, turn at the rail's ends, flip the sprite. */
|
|
40
48
|
step(dt: number): void;
|
|
41
49
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { Component } from '@waica/engine';
|
|
1
|
+
import { Component, installedDirectionalAnimation, projectIsometric, } from '@waica/engine';
|
|
2
|
+
import { facingForInput } from './facing.js';
|
|
3
|
+
/** Seconds a hit stops the rail. */
|
|
4
|
+
const HURT_SECONDS = 0.25;
|
|
5
|
+
/** Seconds the death pose stays on screen before the entity goes away. */
|
|
6
|
+
const DEATH_SECONDS = 0.5;
|
|
2
7
|
/**
|
|
3
8
|
* Rail patrol: back and forth `distance` units from the starting
|
|
4
9
|
* position along one axis — sideways or up and down — turning around
|
|
5
|
-
* at the ends
|
|
10
|
+
* at the ends. It reports which way it walks as seen on screen, so a
|
|
11
|
+
* directional contract picks the matching clip; without one, walking
|
|
12
|
+
* sideways flips the sprite by scale instead.
|
|
6
13
|
* Passive like a motor: no onUpdate of its own — the 'patroller' logic
|
|
7
14
|
* set's walk state calls step(dt), so the StateMachine stays the single
|
|
8
15
|
* owner of the frame.
|
|
@@ -26,6 +33,18 @@ export class Patrol extends Component {
|
|
|
26
33
|
this.originX = this.entity.position.x;
|
|
27
34
|
this.originY = this.entity.position.y;
|
|
28
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* The rail direction as rendered: the logical axis it walks along,
|
|
38
|
+
* projected when the scene is isometric — a horizontal rail reads as
|
|
39
|
+
* south-east/north-west there, a vertical one as south-west/north-east.
|
|
40
|
+
*/
|
|
41
|
+
getAnimationFacing() {
|
|
42
|
+
const vertical = this.axis === 'vertical';
|
|
43
|
+
const lx = vertical ? 0 : this.dir;
|
|
44
|
+
const ly = vertical ? this.dir : 0;
|
|
45
|
+
const screen = this.game.projection === 'isometric' ? projectIsometric(lx, ly) : { x: lx, y: ly };
|
|
46
|
+
return facingForInput(Math.sign(screen.x), Math.sign(screen.y)) ?? 's';
|
|
47
|
+
}
|
|
29
48
|
/** One patrol step: advance, turn at the rail's ends, flip the sprite. */
|
|
30
49
|
step(dt) {
|
|
31
50
|
const pos = this.entity.position;
|
|
@@ -42,16 +61,32 @@ export class Patrol extends Component {
|
|
|
42
61
|
this.dir = 1;
|
|
43
62
|
}
|
|
44
63
|
pos[key] = next;
|
|
45
|
-
|
|
64
|
+
// With a directional contract installed the clip carries the mirroring
|
|
65
|
+
// (flipX); a scale flip on top would undo it.
|
|
66
|
+
if (!vertical && !installedDirectionalAnimation())
|
|
46
67
|
this.entity.scale.x = this.dir;
|
|
47
68
|
}
|
|
48
69
|
}
|
|
49
70
|
/** The state graph new patrolling characters start with, as prefab data. */
|
|
50
71
|
export const PATROLLER_STATE_GRAPH = {
|
|
51
72
|
initial: 'walk',
|
|
52
|
-
states: {
|
|
73
|
+
states: {
|
|
74
|
+
walk: {},
|
|
75
|
+
hurt: { transitions: [{ on: `timer:${HURT_SECONDS}`, to: 'walk' }] },
|
|
76
|
+
// The death pose, resolved directionally like every other clip.
|
|
77
|
+
dead: { clip: 'death' },
|
|
78
|
+
// Death first: a lethal hit must never land in hurt. The death edge is
|
|
79
|
+
// also what makes Health hand the death to this graph instead of
|
|
80
|
+
// destroying the entity on the spot.
|
|
81
|
+
'*': {
|
|
82
|
+
transitions: [
|
|
83
|
+
{ on: 'signal:death', to: 'dead' },
|
|
84
|
+
{ on: 'signal:hurt', to: 'hurt' },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
53
88
|
};
|
|
54
|
-
// The walking-critter role.
|
|
89
|
+
// The walking-critter role. Walks, flinches, dies; give patrolling
|
|
55
90
|
// characters more states by registering on top (defineStates('patroller',
|
|
56
91
|
// { chasing: {...} })) plus prefab data.
|
|
57
92
|
export const PATROLLER_ROLE = {
|
|
@@ -65,5 +100,14 @@ export const PATROLLER_ROLE = {
|
|
|
65
100
|
entity.get(Patrol)?.step(dt);
|
|
66
101
|
},
|
|
67
102
|
},
|
|
103
|
+
// The rail stops for the flinch: no step, nothing else to do.
|
|
104
|
+
hurt: { onUpdate() { } },
|
|
105
|
+
// Holds the death pose for a beat, then the body goes away for good.
|
|
106
|
+
dead: {
|
|
107
|
+
onUpdate({ entity, fsm }) {
|
|
108
|
+
if (fsm.elapsed >= DEATH_SECONDS)
|
|
109
|
+
entity.destroy();
|
|
110
|
+
},
|
|
111
|
+
},
|
|
68
112
|
},
|
|
69
113
|
};
|
|
@@ -1,72 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GridMotor } from './grid-motor.js';
|
|
2
2
|
/** The four facings the top-down animation contract declares. */
|
|
3
3
|
export type TopDownFacing = 'n' | 's' | 'e' | 'w';
|
|
4
4
|
/**
|
|
5
|
-
* Passive top-down motor:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* active state moves the body with. Eight-direction movement with the
|
|
9
|
-
* input vector normalized so diagonals match cardinal speed, no gravity,
|
|
10
|
-
* and per-axis collision against the scene's Solids — deterministic
|
|
11
|
-
* genre movement (Zelda-style), not "realistic" physics.
|
|
5
|
+
* Passive top-down motor: state code owns the frame and calls run() then
|
|
6
|
+
* step(). Eight-direction input is normalized so diagonals match cardinal
|
|
7
|
+
* speed; movement has no gravity and resolves each axis against Solids.
|
|
12
8
|
*/
|
|
13
|
-
export declare class TopDownMotor extends
|
|
9
|
+
export declare class TopDownMotor extends GridMotor<TopDownFacing> {
|
|
14
10
|
static componentName: string;
|
|
15
|
-
static displayName: string;
|
|
16
|
-
static params: {
|
|
17
|
-
moveSpeed: {
|
|
18
|
-
label: string;
|
|
19
|
-
min: number;
|
|
20
|
-
max: number;
|
|
21
|
-
step: number;
|
|
22
|
-
};
|
|
23
|
-
acceleration: {
|
|
24
|
-
label: string;
|
|
25
|
-
min: number;
|
|
26
|
-
max: number;
|
|
27
|
-
step: number;
|
|
28
|
-
};
|
|
29
|
-
deceleration: {
|
|
30
|
-
label: string;
|
|
31
|
-
min: number;
|
|
32
|
-
max: number;
|
|
33
|
-
step: number;
|
|
34
|
-
};
|
|
35
|
-
walkThreshold: {
|
|
36
|
-
label: string;
|
|
37
|
-
min: number;
|
|
38
|
-
max: number;
|
|
39
|
-
step: number;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
static transient: string[];
|
|
43
|
-
moveSpeed: number;
|
|
44
|
-
acceleration: number;
|
|
45
|
-
deceleration: number;
|
|
46
|
-
/** Speed above this reads as walking (the idle ↔ walk edge). */
|
|
47
|
-
walkThreshold: number;
|
|
48
|
-
/** The character's AABB hitbox — a footprint, shorter than the sprite. */
|
|
49
|
-
hitboxWidth: number;
|
|
50
|
-
hitboxHeight: number;
|
|
51
|
-
vx: number;
|
|
52
|
-
vy: number;
|
|
53
11
|
/** Four-direction facing; starts looking at the camera. */
|
|
54
12
|
facing: TopDownFacing;
|
|
55
|
-
/** The scene camera reads follow velocity through this explicit seam. */
|
|
56
|
-
getCameraVelocity(): CameraVelocity;
|
|
57
|
-
/** The StateMachine resolves directional clips through this explicit seam. */
|
|
58
|
-
getAnimationFacing(): string;
|
|
59
13
|
/**
|
|
60
|
-
* Accelerates toward the input vector
|
|
61
|
-
*
|
|
62
|
-
* facing follows the dominant input axis — ties keep the last facing.
|
|
14
|
+
* Accelerates toward the input vector and lets the dominant raw input axis
|
|
15
|
+
* select facing. Perfect ties preserve the previous facing.
|
|
63
16
|
*/
|
|
64
17
|
run(inputX: number, inputY: number, dt: number): void;
|
|
65
|
-
/** Live speed, for the idle ↔ walk edge. */
|
|
66
|
-
speed(): number;
|
|
67
|
-
/** Integrates velocity and resolves each axis against Solids. */
|
|
68
|
-
step(dt: number): void;
|
|
69
|
-
halt(): void;
|
|
70
|
-
private resolveAxis;
|
|
71
|
-
private collisionBody;
|
|
72
18
|
}
|
|
@@ -1,47 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GridMotor } from './grid-motor.js';
|
|
2
2
|
/**
|
|
3
|
-
* Passive top-down motor:
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* active state moves the body with. Eight-direction movement with the
|
|
7
|
-
* input vector normalized so diagonals match cardinal speed, no gravity,
|
|
8
|
-
* and per-axis collision against the scene's Solids — deterministic
|
|
9
|
-
* genre movement (Zelda-style), not "realistic" physics.
|
|
3
|
+
* Passive top-down motor: state code owns the frame and calls run() then
|
|
4
|
+
* step(). Eight-direction input is normalized so diagonals match cardinal
|
|
5
|
+
* speed; movement has no gravity and resolves each axis against Solids.
|
|
10
6
|
*/
|
|
11
|
-
export class TopDownMotor extends
|
|
7
|
+
export class TopDownMotor extends GridMotor {
|
|
12
8
|
static componentName = 'TopDownMotor';
|
|
13
|
-
static displayName = 'Motor';
|
|
14
|
-
static params = {
|
|
15
|
-
moveSpeed: { label: 'Speed', min: 1, max: 30, step: 0.5 },
|
|
16
|
-
acceleration: { label: 'Acceleration', min: 5, max: 200, step: 5 },
|
|
17
|
-
deceleration: { label: 'Deceleration', min: 5, max: 200, step: 5 },
|
|
18
|
-
walkThreshold: { label: 'Walk threshold', min: 0, max: 5, step: 0.1 },
|
|
19
|
-
};
|
|
20
|
-
static transient = ['vx', 'vy', 'facing'];
|
|
21
|
-
moveSpeed = 6;
|
|
22
|
-
acceleration = 60;
|
|
23
|
-
deceleration = 80;
|
|
24
|
-
/** Speed above this reads as walking (the idle ↔ walk edge). */
|
|
25
|
-
walkThreshold = 0.5;
|
|
26
|
-
/** The character's AABB hitbox — a footprint, shorter than the sprite. */
|
|
27
|
-
hitboxWidth = 0.9;
|
|
28
|
-
hitboxHeight = 0.6;
|
|
29
|
-
vx = 0;
|
|
30
|
-
vy = 0;
|
|
31
9
|
/** Four-direction facing; starts looking at the camera. */
|
|
32
10
|
facing = 's';
|
|
33
|
-
/** The scene camera reads follow velocity through this explicit seam. */
|
|
34
|
-
getCameraVelocity() {
|
|
35
|
-
return { vx: this.vx, vy: this.vy };
|
|
36
|
-
}
|
|
37
|
-
/** The StateMachine resolves directional clips through this explicit seam. */
|
|
38
|
-
getAnimationFacing() {
|
|
39
|
-
return this.facing;
|
|
40
|
-
}
|
|
41
11
|
/**
|
|
42
|
-
* Accelerates toward the input vector
|
|
43
|
-
*
|
|
44
|
-
* facing follows the dominant input axis — ties keep the last facing.
|
|
12
|
+
* Accelerates toward the input vector and lets the dominant raw input axis
|
|
13
|
+
* select facing. Perfect ties preserve the previous facing.
|
|
45
14
|
*/
|
|
46
15
|
run(inputX, inputY, dt) {
|
|
47
16
|
let x = inputX;
|
|
@@ -51,10 +20,7 @@ export class TopDownMotor extends Component {
|
|
|
51
20
|
x /= length;
|
|
52
21
|
y /= length;
|
|
53
22
|
}
|
|
54
|
-
|
|
55
|
-
const rateY = y !== 0 ? this.acceleration : this.deceleration;
|
|
56
|
-
this.vx = THREE.MathUtils.damp(this.vx, x * this.moveSpeed, rateX / this.moveSpeed, dt);
|
|
57
|
-
this.vy = THREE.MathUtils.damp(this.vy, y * this.moveSpeed, rateY / this.moveSpeed, dt);
|
|
23
|
+
this.accelerateToward(x, y, dt);
|
|
58
24
|
const absX = Math.abs(inputX);
|
|
59
25
|
const absY = Math.abs(inputY);
|
|
60
26
|
if (absX > absY)
|
|
@@ -62,41 +28,4 @@ export class TopDownMotor extends Component {
|
|
|
62
28
|
else if (absY > absX)
|
|
63
29
|
this.facing = inputY < 0 ? 's' : 'n';
|
|
64
30
|
}
|
|
65
|
-
/** Live speed, for the idle ↔ walk edge. */
|
|
66
|
-
speed() {
|
|
67
|
-
return Math.hypot(this.vx, this.vy);
|
|
68
|
-
}
|
|
69
|
-
/** Integrates velocity and resolves each axis against Solids. */
|
|
70
|
-
step(dt) {
|
|
71
|
-
const pos = this.entity.position;
|
|
72
|
-
const previousX = pos.x;
|
|
73
|
-
pos.x += this.vx * dt;
|
|
74
|
-
if (this.resolveAxis('x', previousX))
|
|
75
|
-
this.vx = 0;
|
|
76
|
-
const previousY = pos.y;
|
|
77
|
-
pos.y += this.vy * dt;
|
|
78
|
-
if (this.resolveAxis('y', previousY))
|
|
79
|
-
this.vy = 0;
|
|
80
|
-
}
|
|
81
|
-
halt() {
|
|
82
|
-
this.vx = 0;
|
|
83
|
-
this.vy = 0;
|
|
84
|
-
}
|
|
85
|
-
resolveAxis(axis, previous) {
|
|
86
|
-
return resolveSolidAxis({
|
|
87
|
-
entity: this.entity,
|
|
88
|
-
axis,
|
|
89
|
-
previous,
|
|
90
|
-
body: () => this.collisionBody(),
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
collisionBody() {
|
|
94
|
-
return {
|
|
95
|
-
x: this.entity.position.x,
|
|
96
|
-
y: this.entity.position.y,
|
|
97
|
-
width: this.hitboxWidth,
|
|
98
|
-
height: this.hitboxHeight,
|
|
99
|
-
shape: 'rectangle',
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
31
|
}
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
import { type RoleDefinition, type RoleGraph, type StateContext } from '@waica/engine';
|
|
2
|
-
/**
|
|
3
|
-
* The top-down 'player' role: the character you control from above. Both
|
|
4
|
-
* default states share one body update; they differ only in the edges
|
|
5
|
-
* their prefab data declares. Extending the player is defineStates
|
|
6
|
-
* ('player', { yourState: {...} }) plus a state in the prefab — never a
|
|
7
|
-
* fight with a parallel controller.
|
|
8
|
-
*/
|
|
9
1
|
/** The state graph new top-down player characters start with, as prefab data. */
|
|
10
|
-
export declare const TOPDOWN_PLAYER_STATE_GRAPH: RoleGraph;
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* signals. Unmatched signals are no-ops, so each state only reacts to
|
|
15
|
-
* the edges its data declares.
|
|
16
|
-
*/
|
|
17
|
-
export declare function topdownPlayerUpdate({ entity, game, fsm }: StateContext, dt: number): void;
|
|
18
|
-
export declare const TOPDOWN_PLAYER_ROLE: RoleDefinition;
|
|
2
|
+
export declare const TOPDOWN_PLAYER_STATE_GRAPH: import("@waica/engine").RoleGraph;
|
|
3
|
+
/** Drives TopDownMotor from project actions and reports body state. */
|
|
4
|
+
export declare const topdownPlayerUpdate: (ctx: import("@waica/engine").StateContext, dt: number) => void;
|
|
5
|
+
export declare const TOPDOWN_PLAYER_ROLE: import("@waica/engine").RoleDefinition;
|
|
@@ -1,86 +1,14 @@
|
|
|
1
|
-
import {} from '
|
|
2
|
-
import { Health } from './health.js';
|
|
3
|
-
import { interactUpdate } from './interactable.js';
|
|
4
|
-
import { Respawnable } from './respawnable.js';
|
|
1
|
+
import { createGridPlayerRole } from './grid-player-role.js';
|
|
5
2
|
import { TopDownMotor } from './topdown-motor.js';
|
|
6
3
|
/**
|
|
7
|
-
* The top-down
|
|
8
|
-
*
|
|
9
|
-
* their prefab data declares. Extending the player is defineStates
|
|
10
|
-
* ('player', { yourState: {...} }) plus a state in the prefab — never a
|
|
11
|
-
* fight with a parallel controller.
|
|
4
|
+
* The top-down player role: eight-direction logical-grid movement with
|
|
5
|
+
* normalized diagonals and no gravity, driven by TopDownMotor.
|
|
12
6
|
*/
|
|
7
|
+
const topdownPlayer = createGridPlayerRole(TopDownMotor, 'You control this character with the project controls: eight-direction ' +
|
|
8
|
+
'top-down movement with normalized diagonals and no gravity. Its ' +
|
|
9
|
+
'states move the Motor.');
|
|
13
10
|
/** The state graph new top-down player characters start with, as prefab data. */
|
|
14
|
-
export const TOPDOWN_PLAYER_STATE_GRAPH =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
transitions: [{ on: 'signal:move', to: 'walk' }],
|
|
19
|
-
},
|
|
20
|
-
walk: {
|
|
21
|
-
transitions: [{ on: 'signal:stop', to: 'idle' }],
|
|
22
|
-
},
|
|
23
|
-
dead: {
|
|
24
|
-
// Freeze on idle (resolved directionally) rather than name a death
|
|
25
|
-
// clip the stock sheets do not have and warn every frame.
|
|
26
|
-
clip: 'idle',
|
|
27
|
-
// A beat before control comes back, so death reads as an event.
|
|
28
|
-
transitions: [{ on: 'timer:0.8', to: 'idle' }],
|
|
29
|
-
},
|
|
30
|
-
// Dying is the same from every state, so the edge lives on '*'. Its
|
|
31
|
-
// presence is also the contract Health checks before signalling: a graph
|
|
32
|
-
// without it gets its entity destroyed instead.
|
|
33
|
-
'*': { transitions: [{ on: 'signal:death', to: 'dead' }] },
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* One body update shared by every default state: read the four-direction
|
|
38
|
-
* actions, accelerate, collide, then report what the body is doing as
|
|
39
|
-
* signals. Unmatched signals are no-ops, so each state only reacts to
|
|
40
|
-
* the edges its data declares.
|
|
41
|
-
*/
|
|
42
|
-
export function topdownPlayerUpdate({ entity, game, fsm }, dt) {
|
|
43
|
-
const motor = entity.get(TopDownMotor);
|
|
44
|
-
if (!motor)
|
|
45
|
-
return;
|
|
46
|
-
motor.run(game.input.axis('left', 'right'), game.input.axis('down', 'up'), dt);
|
|
47
|
-
motor.step(dt);
|
|
48
|
-
fsm.signal(motor.speed() > motor.walkThreshold ? 'move' : 'stop');
|
|
49
|
-
}
|
|
50
|
-
export const TOPDOWN_PLAYER_ROLE = {
|
|
51
|
-
description: 'You control this character with the project controls: eight-direction ' +
|
|
52
|
-
'top-down movement with normalized diagonals and no gravity. Its ' +
|
|
53
|
-
'states move the Motor.',
|
|
54
|
-
driver: 'TopDownMotor',
|
|
55
|
-
graph: TOPDOWN_PLAYER_STATE_GRAPH,
|
|
56
|
-
signals: {
|
|
57
|
-
move: 'moving',
|
|
58
|
-
stop: 'standing still',
|
|
59
|
-
},
|
|
60
|
-
states: {
|
|
61
|
-
// Always-hook: the interact lookup must survive custom states like an
|
|
62
|
-
// attack, so it runs in every state.
|
|
63
|
-
'*': {
|
|
64
|
-
onUpdate(ctx) {
|
|
65
|
-
interactUpdate(ctx);
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
// Fallback: a custom state without its own onUpdate keeps the full
|
|
69
|
-
// body update — its file only has to say what makes it special.
|
|
70
|
-
default: { onUpdate: topdownPlayerUpdate },
|
|
71
|
-
idle: { onUpdate: topdownPlayerUpdate },
|
|
72
|
-
walk: { onUpdate: topdownPlayerUpdate },
|
|
73
|
-
// Coming back is what leaving death means, so it hangs off onExit: any
|
|
74
|
-
// other way out of this state (a project's own edge) revives too.
|
|
75
|
-
dead: {
|
|
76
|
-
// A state without its own onUpdate falls back to the role's default
|
|
77
|
-
// body update, so without this no-op the player kept walking for the
|
|
78
|
-
// whole death beat instead of the graph taking control away.
|
|
79
|
-
onUpdate() { },
|
|
80
|
-
onExit({ entity }) {
|
|
81
|
-
entity.get(Respawnable)?.respawn();
|
|
82
|
-
entity.get(Health)?.heal(Infinity);
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
};
|
|
11
|
+
export const TOPDOWN_PLAYER_STATE_GRAPH = topdownPlayer.graph;
|
|
12
|
+
/** Drives TopDownMotor from project actions and reports body state. */
|
|
13
|
+
export const topdownPlayerUpdate = topdownPlayer.update;
|
|
14
|
+
export const TOPDOWN_PLAYER_ROLE = topdownPlayer.role;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waica/behaviors",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Waica built-in behaviors — the curated game-feel library",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,6 +19,6 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@waica/engine": "^0.
|
|
22
|
+
"@waica/engine": "^0.9.0"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -63,6 +63,8 @@ export declare abstract class Component {
|
|
|
63
63
|
onReady?(): void;
|
|
64
64
|
/** Runs once per frame. */
|
|
65
65
|
onUpdate?(dt: number): void;
|
|
66
|
+
/** Runs after the scene changes between identity and projected rendering. */
|
|
67
|
+
onProjectionChange?(projection: 'isometric' | null): void;
|
|
66
68
|
/** Runs when this entity's Hitbox overlaps another one's. */
|
|
67
69
|
onCollide?(other: Entity): void;
|
|
68
70
|
/** Runs when this entity's DynamicBody physically contacts a Solid. */
|