sentrychris / undeadbytes

https://sentrychris.github.io/undeadbytes
https://sentrychris.github.io/undeadbytes/play
MIT License
1 stars 2 forks source link

Add ability to store health pick-ups for later use via a hotkey #13

Closed sentrychris closed 12 months ago

sentrychris commented 12 months ago

At the moment, these pick-ups are picked-up regardless of player state, if health is full, then the health pick-up is wasted.

There should be a game condition where if the player's health is full, the item is instead stored when picked up.

Summary

The Health pickup entity should be refactored to allow for storing items picked up by the player.

How it currently works

Here is the current pickup() method in Health.js:

pickup () {
  AudioFX.snippet({ name: 'inject' });
  this.markToDelete = true;
}

This is triggered by the entity's update() method (every entity has an update method which is called on every frame or "repaint"):

update (game) {
    Collision.entityToPlayer(this, game, () => {
      this.pickup();
    });
  }

The actual action/effect from pickup items happens within Game.js in the onUpdate() method (which is what triggers each entity's update() method every frame):

for (let i = 0; i < this.entities.length; i++) {
  if (this.canUpdateEntity(this.entities[i])) {
    this.entities[i].update(this);
  }

  if (this.entities[i].type === 'pickup') {
    if (this.entities[i].markToDelete) { // this is set to true, therefore an item has been picked up
      // If a health item has been picked up, call the player entity to refill health
      if (this.entities[i].item === 'health') {
        this.player.refillHealth(this.entities[i].value);
      }
      ...
      // Remove picked up entities from the canvas
      this.entities.splice(i, 1);
    }
  }
  ...

So in sequence:

  1. The Game.js manager calls onUpdate every frame.
  2. onUpdate loops through the game entities (walls, player, enemies, pickup items)
  3. Each updateable entity's update() method is called to update the entity's position, collision vectors, bounds etc.
  4. In the case of pickup item entities, the Collision.js - entityToPlayer() method takes a 3rd parameter, this is a callback that is executed when the entities intersect. In other words, the pickup() callback is executed when the player reaches the item.
  5. The pickup() method plays the audio FX snippet and then marks the entity for deletion.
  6. Back in Game.js, the onUpdate() method, while looping through entities every frame, checks to see if the pickup item has been marked for deletion
  7. In the case of health items, the Player.js - refillHealth() method is called, passing the value of the pickup item (25)

How it should work

The markToDelete functionality needs to remain, as it is responsible for deleting the entity and removng it from the canvas once it's picked up.

However, the logic to refill the player's health (this.player.refillHealth(this.entities[i].value);) should be removed from the Game.js - onUpdate() method and instead handled in the Health.js entity's pickup() method, something like:

pickup (game) {
  const { player } = game;

  // If the player's health is full, then store the pickup instead
  if (player.health === 100) {
    // you could store a copy of the entity
    player.storePickupItem(this);
    AudioFX.snippet({ name: 'pickup' });
  } else {
    // Otherwise refill the player's health now
    player.refillHealth(this.value);
    AudioFX.snippet({ name: 'inject' });
  }

  this.markToDelete = true;
}

This should be fairly straight-forward, because the pickup() method is executed as a callback passed from the same class' update() method, which already has access to the game object... Example:

update (game) {
  Collision.entityToPlayer(this, game, () => { // we're already passing an anonymous function
    this.pickup(game); // ...so simply pass the game here
  });
}