@waica/behaviors 0.20.0 → 0.21.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/dist/damage-puff.d.ts +27 -0
- package/dist/damage-puff.js +40 -0
- package/dist/dust-puffs.d.ts +51 -0
- package/dist/dust-puffs.js +73 -0
- package/dist/dust-trail.d.ts +24 -0
- package/dist/dust-trail.js +38 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/swing-sparks.d.ts +32 -0
- package/dist/swing-sparks.js +33 -0
- package/package.json +2 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* A puff from this entity's ParticleEmitter wherever
|
|
4
|
+
* damage lands. Health announces every accepted hit on `game.events` as
|
|
5
|
+
* 'damage'; this moves its own (sprite-less) entity to the entity that took
|
|
6
|
+
* it and bursts there. The emitter should be in `space: 'world'`, so the
|
|
7
|
+
* puffs already in flight stay where they started when the next hit moves
|
|
8
|
+
* the source elsewhere. One of these serves every Health in the scene.
|
|
9
|
+
*/
|
|
10
|
+
export declare class DamagePuff extends Component {
|
|
11
|
+
static componentName: string;
|
|
12
|
+
static displayName: string;
|
|
13
|
+
static params: {
|
|
14
|
+
count: {
|
|
15
|
+
label: string;
|
|
16
|
+
min: number;
|
|
17
|
+
max: number;
|
|
18
|
+
step: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/** Particles per hit. */
|
|
22
|
+
count: number;
|
|
23
|
+
private unsubscribe?;
|
|
24
|
+
onReady(): void;
|
|
25
|
+
onDestroy(): void;
|
|
26
|
+
private puffAt;
|
|
27
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Component, Entity, ParticleEmitter } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* A puff from this entity's ParticleEmitter wherever
|
|
4
|
+
* damage lands. Health announces every accepted hit on `game.events` as
|
|
5
|
+
* 'damage'; this moves its own (sprite-less) entity to the entity that took
|
|
6
|
+
* it and bursts there. The emitter should be in `space: 'world'`, so the
|
|
7
|
+
* puffs already in flight stay where they started when the next hit moves
|
|
8
|
+
* the source elsewhere. One of these serves every Health in the scene.
|
|
9
|
+
*/
|
|
10
|
+
export class DamagePuff extends Component {
|
|
11
|
+
static componentName = 'DamagePuff';
|
|
12
|
+
static displayName = 'Damage puff';
|
|
13
|
+
static params = {
|
|
14
|
+
count: { label: 'Particles', min: 1, max: 64, step: 1 },
|
|
15
|
+
};
|
|
16
|
+
/** Particles per hit. */
|
|
17
|
+
count = 10;
|
|
18
|
+
unsubscribe;
|
|
19
|
+
onReady() {
|
|
20
|
+
this.unsubscribe = this.game.events.on('damage', (payload) => this.puffAt(payload));
|
|
21
|
+
}
|
|
22
|
+
onDestroy() {
|
|
23
|
+
this.unsubscribe?.();
|
|
24
|
+
this.unsubscribe = undefined;
|
|
25
|
+
}
|
|
26
|
+
puffAt(payload) {
|
|
27
|
+
const target = damagedEntity(payload);
|
|
28
|
+
const puff = this.entity.get(ParticleEmitter);
|
|
29
|
+
if (!target || !puff)
|
|
30
|
+
return;
|
|
31
|
+
this.entity.position.set(target.position.x, target.position.y, this.entity.position.z);
|
|
32
|
+
puff.emit(this.count);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** The entity a 'damage' event names, if the payload really carries one. */
|
|
36
|
+
function damagedEntity(payload) {
|
|
37
|
+
if (typeof payload !== 'object' || payload === null || !('entity' in payload))
|
|
38
|
+
return undefined;
|
|
39
|
+
return payload.entity instanceof Entity ? payload.entity : undefined;
|
|
40
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* Dust from this entity's own ParticleEmitter at the
|
|
4
|
+
* two moments a platformer body meets the ground hard — a small puff when a
|
|
5
|
+
* jump takes off from the ground, a bigger burst when the body lands. Both
|
|
6
|
+
* come from the same emitter (the dust look is authored on it in the
|
|
7
|
+
* prefab); only the count differs. It hooks the StateMachine's own edges
|
|
8
|
+
* instead of polling:
|
|
9
|
+
*
|
|
10
|
+
* - takeoff: entering 'jump' right after leaving 'idle' or 'run'. Leaving
|
|
11
|
+
* any other state first counts as airborne: the graph enters 'jump' from
|
|
12
|
+
* 'fall' in mid-air (a coyote jump, a stomp bounce), and a project state
|
|
13
|
+
* such as a dash can do the same. Those raise no dust.
|
|
14
|
+
* - landing: leaving 'jump' or 'fall' while the motor is grounded. A jump
|
|
15
|
+
* whose apex already lands on a ledge goes jump -> idle without a fall.
|
|
16
|
+
*
|
|
17
|
+
* To know which state was left, it adds an instance onExit hook to every
|
|
18
|
+
* state the machine declares (all but '*'). A role's own hooks for those
|
|
19
|
+
* states still run. A role 'default' onExit, however, only runs for states
|
|
20
|
+
* without an onExit of their own, so this hook would shadow it. No stock
|
|
21
|
+
* role declares one.
|
|
22
|
+
*
|
|
23
|
+
* Its siblings must already be mounted when it is, so it goes after the
|
|
24
|
+
* StateMachine and the ParticleEmitter in the prefab.
|
|
25
|
+
*/
|
|
26
|
+
export declare class DustPuffs extends Component {
|
|
27
|
+
static componentName: string;
|
|
28
|
+
static displayName: string;
|
|
29
|
+
static params: {
|
|
30
|
+
jumpCount: {
|
|
31
|
+
label: string;
|
|
32
|
+
min: number;
|
|
33
|
+
max: number;
|
|
34
|
+
step: number;
|
|
35
|
+
};
|
|
36
|
+
landCount: {
|
|
37
|
+
label: string;
|
|
38
|
+
min: number;
|
|
39
|
+
max: number;
|
|
40
|
+
step: number;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
static transient: string[];
|
|
44
|
+
/** Particles when a jump takes off from the ground. */
|
|
45
|
+
jumpCount: number;
|
|
46
|
+
/** Particles when the body lands. */
|
|
47
|
+
landCount: number;
|
|
48
|
+
/** Whether the last state left was a grounded one: set on every declared state's exit, read on entering 'jump'. */
|
|
49
|
+
private leftGround;
|
|
50
|
+
onReady(): void;
|
|
51
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Component, ParticleEmitter, StateMachine } from '@waica/engine';
|
|
2
|
+
import { PlatformerMotor } from './platformer-motor.js';
|
|
3
|
+
/** The stock player graph's grounded states: a jump entered from one of these is a takeoff. */
|
|
4
|
+
const GROUND_STATES = ['idle', 'run'];
|
|
5
|
+
/** The airborne states a landing can end: a grounded exit from either is a touchdown. */
|
|
6
|
+
const AIR_STATES = ['jump', 'fall'];
|
|
7
|
+
/**
|
|
8
|
+
* Dust from this entity's own ParticleEmitter at the
|
|
9
|
+
* two moments a platformer body meets the ground hard — a small puff when a
|
|
10
|
+
* jump takes off from the ground, a bigger burst when the body lands. Both
|
|
11
|
+
* come from the same emitter (the dust look is authored on it in the
|
|
12
|
+
* prefab); only the count differs. It hooks the StateMachine's own edges
|
|
13
|
+
* instead of polling:
|
|
14
|
+
*
|
|
15
|
+
* - takeoff: entering 'jump' right after leaving 'idle' or 'run'. Leaving
|
|
16
|
+
* any other state first counts as airborne: the graph enters 'jump' from
|
|
17
|
+
* 'fall' in mid-air (a coyote jump, a stomp bounce), and a project state
|
|
18
|
+
* such as a dash can do the same. Those raise no dust.
|
|
19
|
+
* - landing: leaving 'jump' or 'fall' while the motor is grounded. A jump
|
|
20
|
+
* whose apex already lands on a ledge goes jump -> idle without a fall.
|
|
21
|
+
*
|
|
22
|
+
* To know which state was left, it adds an instance onExit hook to every
|
|
23
|
+
* state the machine declares (all but '*'). A role's own hooks for those
|
|
24
|
+
* states still run. A role 'default' onExit, however, only runs for states
|
|
25
|
+
* without an onExit of their own, so this hook would shadow it. No stock
|
|
26
|
+
* role declares one.
|
|
27
|
+
*
|
|
28
|
+
* Its siblings must already be mounted when it is, so it goes after the
|
|
29
|
+
* StateMachine and the ParticleEmitter in the prefab.
|
|
30
|
+
*/
|
|
31
|
+
export class DustPuffs extends Component {
|
|
32
|
+
static componentName = 'DustPuffs';
|
|
33
|
+
static displayName = 'Dust puffs';
|
|
34
|
+
static params = {
|
|
35
|
+
jumpCount: { label: 'Jump puff', min: 0, max: 64, step: 1 },
|
|
36
|
+
landCount: { label: 'Landing burst', min: 0, max: 64, step: 1 },
|
|
37
|
+
};
|
|
38
|
+
static transient = ['leftGround'];
|
|
39
|
+
/** Particles when a jump takes off from the ground. */
|
|
40
|
+
jumpCount = 5;
|
|
41
|
+
/** Particles when the body lands. */
|
|
42
|
+
landCount = 12;
|
|
43
|
+
/** Whether the last state left was a grounded one: set on every declared state's exit, read on entering 'jump'. */
|
|
44
|
+
leftGround = false;
|
|
45
|
+
onReady() {
|
|
46
|
+
const machine = this.entity.get(StateMachine);
|
|
47
|
+
const dust = this.entity.get(ParticleEmitter);
|
|
48
|
+
if (!machine || !dust) {
|
|
49
|
+
console.warn(`[waica] "${this.entity.name}": DustPuffs needs a StateMachine and a ParticleEmitter before it.`);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const state of Object.keys(machine.states)) {
|
|
53
|
+
if (state === '*')
|
|
54
|
+
continue;
|
|
55
|
+
const grounded = GROUND_STATES.includes(state);
|
|
56
|
+
machine.on(state, { onExit: () => (this.leftGround = grounded) });
|
|
57
|
+
}
|
|
58
|
+
for (const state of AIR_STATES) {
|
|
59
|
+
machine.on(state, {
|
|
60
|
+
onExit: () => {
|
|
61
|
+
if (this.entity.get(PlatformerMotor)?.grounded)
|
|
62
|
+
dust.emit(this.landCount);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
machine.on('jump', {
|
|
67
|
+
onEnter: () => {
|
|
68
|
+
if (this.leftGround)
|
|
69
|
+
dust.emit(this.jumpCount);
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* A soft dust trail while this entity walks. The
|
|
4
|
+
* ParticleEmitter on the same entity does the continuous emission (its
|
|
5
|
+
* `rate` and dust look are authored in the prefab, with `emitting: false`);
|
|
6
|
+
* this only switches `emitting` on when the StateMachine enters the walking
|
|
7
|
+
* state and off when it leaves it, so the trail stops the moment the body
|
|
8
|
+
* does and the dust already raised settles on its own.
|
|
9
|
+
*
|
|
10
|
+
* Its siblings must already be mounted when it is, so it goes after the
|
|
11
|
+
* StateMachine and the ParticleEmitter in the prefab.
|
|
12
|
+
*/
|
|
13
|
+
export declare class DustTrail extends Component {
|
|
14
|
+
static componentName: string;
|
|
15
|
+
static displayName: string;
|
|
16
|
+
static params: {
|
|
17
|
+
state: {
|
|
18
|
+
label: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/** The state during which the trail is raised. */
|
|
22
|
+
state: string;
|
|
23
|
+
onReady(): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Component, ParticleEmitter, StateMachine } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* A soft dust trail while this entity walks. The
|
|
4
|
+
* ParticleEmitter on the same entity does the continuous emission (its
|
|
5
|
+
* `rate` and dust look are authored in the prefab, with `emitting: false`);
|
|
6
|
+
* this only switches `emitting` on when the StateMachine enters the walking
|
|
7
|
+
* state and off when it leaves it, so the trail stops the moment the body
|
|
8
|
+
* does and the dust already raised settles on its own.
|
|
9
|
+
*
|
|
10
|
+
* Its siblings must already be mounted when it is, so it goes after the
|
|
11
|
+
* StateMachine and the ParticleEmitter in the prefab.
|
|
12
|
+
*/
|
|
13
|
+
export class DustTrail extends Component {
|
|
14
|
+
static componentName = 'DustTrail';
|
|
15
|
+
static displayName = 'Dust trail';
|
|
16
|
+
static params = {
|
|
17
|
+
state: { label: 'State' },
|
|
18
|
+
};
|
|
19
|
+
/** The state during which the trail is raised. */
|
|
20
|
+
state = 'walk';
|
|
21
|
+
onReady() {
|
|
22
|
+
const machine = this.entity.get(StateMachine);
|
|
23
|
+
const dust = this.entity.get(ParticleEmitter);
|
|
24
|
+
if (!machine || !dust) {
|
|
25
|
+
console.warn(`[waica] "${this.entity.name}": DustTrail needs a StateMachine and a ParticleEmitter before it.`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
dust.emitting = machine.current === this.state;
|
|
29
|
+
machine.on(this.state, {
|
|
30
|
+
onEnter: () => {
|
|
31
|
+
dust.emitting = true;
|
|
32
|
+
},
|
|
33
|
+
onExit: () => {
|
|
34
|
+
dust.emitting = false;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,3 +23,7 @@ export { buildNavigationGrid } from './navigation-grid.js';
|
|
|
23
23
|
export type { GridCell, GridPoint, NavigationGrid } from './navigation-grid.js';
|
|
24
24
|
export { findPath, nearestReachableCell, planPath, reachableCells } from './pathfinding.js';
|
|
25
25
|
export type { PlannedPath } from './pathfinding.js';
|
|
26
|
+
export { SwingSparks } from './swing-sparks.js';
|
|
27
|
+
export { DamagePuff } from './damage-puff.js';
|
|
28
|
+
export { DustPuffs } from './dust-puffs.js';
|
|
29
|
+
export { DustTrail } from './dust-trail.js';
|
package/dist/index.js
CHANGED
|
@@ -21,3 +21,7 @@ export { Lifetime } from './lifetime.js';
|
|
|
21
21
|
export { ClickToMove, driveClickToMove } from './click-to-move.js';
|
|
22
22
|
export { buildNavigationGrid } from './navigation-grid.js';
|
|
23
23
|
export { findPath, nearestReachableCell, planPath, reachableCells } from './pathfinding.js';
|
|
24
|
+
export { SwingSparks } from './swing-sparks.js';
|
|
25
|
+
export { DamagePuff } from './damage-puff.js';
|
|
26
|
+
export { DustPuffs } from './dust-puffs.js';
|
|
27
|
+
export { DustTrail } from './dust-trail.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Component } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* A burst of sparks from this entity's own
|
|
4
|
+
* ParticleEmitter each time its StateMachine enters the swing state. The
|
|
5
|
+
* look (color, speed, lifetime) is authored on the emitter in the prefab;
|
|
6
|
+
* this only decides when, and how many. It hooks the state's onEnter
|
|
7
|
+
* instead of polling, so it fires exactly once per swing — right after
|
|
8
|
+
* the role's own onEnter has landed the strike.
|
|
9
|
+
*
|
|
10
|
+
* Its siblings must already be mounted when it is, so it goes after the
|
|
11
|
+
* StateMachine and the ParticleEmitter in the prefab.
|
|
12
|
+
*/
|
|
13
|
+
export declare class SwingSparks extends Component {
|
|
14
|
+
static componentName: string;
|
|
15
|
+
static displayName: string;
|
|
16
|
+
static params: {
|
|
17
|
+
state: {
|
|
18
|
+
label: string;
|
|
19
|
+
};
|
|
20
|
+
count: {
|
|
21
|
+
label: string;
|
|
22
|
+
min: number;
|
|
23
|
+
max: number;
|
|
24
|
+
step: number;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
/** The state whose entry is a swing. */
|
|
28
|
+
state: string;
|
|
29
|
+
/** Sparks per swing. */
|
|
30
|
+
count: number;
|
|
31
|
+
onReady(): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Component, ParticleEmitter, StateMachine } from '@waica/engine';
|
|
2
|
+
/**
|
|
3
|
+
* A burst of sparks from this entity's own
|
|
4
|
+
* ParticleEmitter each time its StateMachine enters the swing state. The
|
|
5
|
+
* look (color, speed, lifetime) is authored on the emitter in the prefab;
|
|
6
|
+
* this only decides when, and how many. It hooks the state's onEnter
|
|
7
|
+
* instead of polling, so it fires exactly once per swing — right after
|
|
8
|
+
* the role's own onEnter has landed the strike.
|
|
9
|
+
*
|
|
10
|
+
* Its siblings must already be mounted when it is, so it goes after the
|
|
11
|
+
* StateMachine and the ParticleEmitter in the prefab.
|
|
12
|
+
*/
|
|
13
|
+
export class SwingSparks extends Component {
|
|
14
|
+
static componentName = 'SwingSparks';
|
|
15
|
+
static displayName = 'Swing sparks';
|
|
16
|
+
static params = {
|
|
17
|
+
state: { label: 'State' },
|
|
18
|
+
count: { label: 'Sparks', min: 1, max: 64, step: 1 },
|
|
19
|
+
};
|
|
20
|
+
/** The state whose entry is a swing. */
|
|
21
|
+
state = 'attack';
|
|
22
|
+
/** Sparks per swing. */
|
|
23
|
+
count = 14;
|
|
24
|
+
onReady() {
|
|
25
|
+
const machine = this.entity.get(StateMachine);
|
|
26
|
+
const sparks = this.entity.get(ParticleEmitter);
|
|
27
|
+
if (!machine || !sparks) {
|
|
28
|
+
console.warn(`[waica] "${this.entity.name}": SwingSparks needs a StateMachine and a ParticleEmitter before it.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
machine.on(this.state, { onEnter: () => sparks.emit(this.count) });
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waica/behaviors",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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.21.0"
|
|
23
23
|
}
|
|
24
24
|
}
|