@waica/behaviors 0.13.0 → 0.14.1

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/health.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Component, StateMachine } from '@waica/engine';
1
+ import { Component, SIMULATION_TIME_EPSILON, StateMachine, } from '@waica/engine';
2
2
  /**
3
3
  * Below this, current is treated as exactly zero. Repeated fractional
4
4
  * damage (e.g. ten hits of 0.1 against max: 1) can leave a floating-point
@@ -113,6 +113,12 @@ export class Health extends Component {
113
113
  onUpdate(dt) {
114
114
  if (this.invulnerable > 0) {
115
115
  this.invulnerable = Math.max(0, this.invulnerable - dt);
116
+ // this.invulnerable is a countdown of SIMULATION_STEP-sized dts, which
117
+ // float error can leave as a tiny positive residual instead of
118
+ // exactly 0 on the step that should close the window; without this,
119
+ // `> 0` above stays true for one whole extra step.
120
+ if (this.invulnerable <= SIMULATION_TIME_EPSILON)
121
+ this.invulnerable = 0;
116
122
  this.blink(dt);
117
123
  }
118
124
  if (this.deathPending) {
@@ -56,26 +56,16 @@ export function fireInteract(target, initiator) {
56
56
  * radius hides it again. Nearest one wins when several are in range.
57
57
  */
58
58
  export function interactUpdate({ entity, game }) {
59
- let nearest = null;
60
- let nearestEntity = null;
61
- let nearestDistance = Infinity;
62
- for (const other of game.entities) {
63
- if (other === entity)
64
- continue;
65
- const interactable = other.get(Interactable);
66
- if (!interactable)
67
- continue;
68
- const distance = Math.hypot(other.position.x - entity.position.x, other.position.y - entity.position.y);
69
- if (distance <= interactable.radius && distance < nearestDistance) {
70
- nearest = interactable;
71
- nearestEntity = other;
72
- nearestDistance = distance;
73
- }
74
- }
75
- if (!nearest || !nearestEntity) {
59
+ const nearestEntity = game.query.nearest(entity.position.x, entity.position.y, {
60
+ with: [Interactable],
61
+ exclude: entity,
62
+ where: (candidate, { distance }) => distance <= candidate.get(Interactable).radius,
63
+ });
64
+ if (!nearestEntity) {
76
65
  game.ui.hide(INTERACTABLE_UI_PIECE);
77
66
  return;
78
67
  }
68
+ const nearest = nearestEntity.get(Interactable);
79
69
  if (game.input.justPressed('interact') && !game.input.consumed('interact')) {
80
70
  // The press is spent: an input:interact edge needs a NEW press.
81
71
  game.input.consume('interact');
package/dist/lifetime.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Component } from '@waica/engine';
1
+ import { Component, SIMULATION_TIME_EPSILON } from '@waica/engine';
2
2
  /** Destroys its entity after a configurable amount of simulated time. */
3
3
  export class Lifetime extends Component {
4
4
  static componentName = 'Lifetime';
@@ -14,7 +14,10 @@ export class Lifetime extends Component {
14
14
  if (!this.entity.alive)
15
15
  return;
16
16
  this.elapsed += dt;
17
- if (this.elapsed >= this.seconds)
17
+ // this.elapsed is a sum of SIMULATION_STEP-sized dts, which float error
18
+ // can leave a hair under an exact multiple; the epsilon keeps a bare
19
+ // `>=` from waiting one whole step too long to destroy.
20
+ if (this.elapsed + SIMULATION_TIME_EPSILON >= this.seconds)
18
21
  this.entity.destroy();
19
22
  }
20
23
  }
@@ -1,4 +1,4 @@
1
- import { Component, Hitbox, collisionOverlap, } from '@waica/engine';
1
+ import { Component, Hitbox, } from '@waica/engine';
2
2
  import { logicalDirection } from './facing.js';
3
3
  import { Health } from './health.js';
4
4
  /**
@@ -43,25 +43,16 @@ export class MeleeAttack extends Component {
43
43
  this.game.audio.play(this.swingSound, { at: this.entity });
44
44
  const area = this.strikeArea(direction.x, direction.y);
45
45
  const struck = [];
46
- // A copy: a target with no death-handling graph is destroyed on the spot,
47
- // which splices it out of the live array mid-loop (Game does the same).
48
- for (const other of [...this.game.entities]) {
49
- if (other === this.entity || !other.alive)
46
+ const targets = this.game.query.area(area, {
47
+ with: [Health],
48
+ exclude: this.entity,
49
+ });
50
+ for (const other of targets) {
51
+ // Query results are eager live references: an earlier target can destroy
52
+ // a later one while this strike is still consuming its snapshot.
53
+ if (!other.alive)
50
54
  continue;
51
- const hitbox = other.get(Hitbox);
52
55
  const health = other.get(Health);
53
- if (!hitbox || !health)
54
- continue;
55
- const body = {
56
- x: other.position.x + hitbox.offsetX,
57
- y: other.position.y + hitbox.offsetY,
58
- width: hitbox.width,
59
- height: hitbox.height,
60
- shape: hitbox.shape,
61
- points: hitbox.points,
62
- };
63
- if (!collisionOverlap(area, body))
64
- continue;
65
56
  const before = health.current;
66
57
  health.damage(this.damage, this.entity);
67
58
  if (health.current < before)
package/dist/patrol.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Component, installedDirectionalAnimation, projectIsometric, } from '@waica/engine';
1
+ import { Component, installedDirectionalAnimation, projectIsometric, SIMULATION_TIME_EPSILON, } from '@waica/engine';
2
2
  import { facingForInput } from './facing.js';
3
3
  /** Seconds a hit stops the rail. */
4
4
  const HURT_SECONDS = 0.25;
@@ -105,7 +105,10 @@ export const PATROLLER_ROLE = {
105
105
  // Holds the death pose for a beat, then the body goes away for good.
106
106
  dead: {
107
107
  onUpdate({ entity, fsm }) {
108
- if (fsm.elapsed >= DEATH_SECONDS)
108
+ // fsm.elapsed is a sum of SIMULATION_STEP-sized dts, which float
109
+ // error can leave a hair under an exact multiple; the epsilon keeps
110
+ // a bare `>=` from waiting one whole step too long to destroy.
111
+ if (fsm.elapsed + SIMULATION_TIME_EPSILON >= DEATH_SECONDS)
109
112
  entity.destroy();
110
113
  },
111
114
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waica/behaviors",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
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.13.0"
22
+ "@waica/engine": "^0.14.1"
23
23
  }
24
24
  }