danprince / midas

🫅 Traditional roguelike where everything you touch turns to gold.
https://danprince.itch.io/midas
2 stars 0 forks source link

Effects #41

Open danprince opened 4 years ago

danprince commented 4 years ago

Need a system for effects that will serve as a template for handling the combinatorial complexity that comes with having lots of items and lots of object/tile types.

One option would be an event system where items can handle arbitrary incoming events, but I have an idea for some other variants too.

Function Driven

// behaviours/items.js

export default {
  longsword: {
    // called when the item is equipped
    equip(item, object) {},

    // called when the item is used
    use(item, object) {},

    // called when the item is unequipped
    unequip(item, object) {},

    // called when the player does an attack
    attack(item, object, target) {},

    // called when the object defends against an attack
    defend(item, object, target) {},

    // this could be extended to handle movement, transmutation and all sorts of actions
  }
}

The key with this model would be having a separate handler for each type of object. These handlers would also handle any objects that this object extended.

Data Driven

This approach would give every item a set of data driven effects that could be defined along with the data.

{
  "winged-sandals": {
    "effects": {
      "equip": ["start-flying"],
      "unequip": ["stop-flying"]
    }
  }
}

This approach is a little bit more indirection and makes it harder to switch between versions, but stays more in-line with the data-driven ethos across the rest of the codebase. It also makes items a bit more powerful and flexible due to being able to dynamically add additional effects for various actions.

danprince commented 4 years ago

Tiles

  "lava": {
    "extends": [],
    "walkable": false,
    "transmutable": false,
    "effects/enter": [
      ["burn", 12]
    ],
    "effects/rest": [
      ["burn", 20]
    ]
  }

Items/Blessings:

{
  "effects/equip": [],
  "effects/unequip": [],
  "effects/use": [],
  "effects/attack": [],
  "effects/defend": [],
  "effects/transmute": [],
  "effects/move": [],
  "effects/rest": [],
}
danprince commented 4 years ago
/**
 * Allow the effect handler to see where the effect came from.
 */
type EffectOwner =
  // this effect is intrinsic on the object
  | { type: "object", object: GameObject }

  // this effect comes from an item
  | { type: "item", item: Item }

  // this effect comes from a blessing
  | { type: "blessing", blessing: Blessing }

  // this effect comes from a tile
  | { type: "tile", tile: Tile }

interface Effect {
  owner: EffectOwner
}

type EffectResult =
  | { type: "success" }
  | { type: "fail" }

type EffectHandler<T extends Effect> = (effect: T) => EffectResult

interface EquipEffect extends Effect {
  item: Item,
  object: GameObject,
  slot: number,
}

interface UnequipEffect extends Effect {
  item: Item,
  object: GameObject,
  slot: number,
}

interface UseEffect extends Effect {
  item: Item,
  object: GameObject,
  slot: number,
}

interface AttackEffect extends Effect {
  attacker: GameObject,
  defender: GameObject,
  amount: number,
}

interface DefendEffect extends Effect {
  attacker: GameObject,
  defender: GameObject,
  amount: number,
}

interface TransmuteObjectEffect extends Effect {
  transmuter: GameObject,
  target: GameObject,
}

interface TransmuteTileEffect extends Effect {
  transmuter: GameObject,
  tile: Tile,
}

interface RestEffect extends Effect {
  object: GameObject,
}

interface EnterTileEffect extends Effect {
  tile: Tile,
  x: number,
  y: number,
}

interface ExitTileEffect extends Effect {
  tile: Tile,
  x: number,
  y: number,
}