0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

前回は、1ターンの処理を Section の階層に組み替えました。

イベント駆動開発を学びたい(Step2:Section)

今回は、攻撃の途中で Event を発火し、個別の処理を EventHandler で受け取るようにします。ハンドラを登録しないときは、実行結果は前回のままです。

今回のポイント

  • ダメージの補正は executeAction に直接書かず、beforeDamage のハンドラが event.damage を変える
  • 同じ種類のハンドラは登録順に同期実行し、全部終わってから攻撃の続きへ戻る
  • afterDamage は HP を減らしたあと。ここでダメージ値を変えても、すでに引いた HP には戻らない
  • 登録はバトルごとの EventBus で行い、実行中の追加登録はしない

ディレクトリ

増えたファイルは event.ts です。Section の並びは前回のままです。

Before

src/
├── main.ts
└── battle/
    ├── types.ts
    ├── section.ts
    ├── battle.ts    Battle が子 Section を並べる
    ├── turn.ts      Turn が子 Section を並べる
    └── actions.ts   executeAction が固定20ダメージを引く

After

src/
├── main.ts
└── battle/
    ├── types.ts
    ├── section.ts
    ├── event.ts     追加。Event と EventHandler の登録・発火
    ├── battle.ts    EventBus を Turn へ渡す
    ├── turn.ts      EventBus を攻撃 Section へ渡す
    └── actions.ts   executeAction の途中で Event を発火する

シーケンス

変わったのは攻撃の中です。executeAction が HP を直接 20 減らすのではなく、発火とハンドラの完了を挟みます。

Before

After

コード

EventBus は、種類が一致するハンドラだけを登録順に実行します。

export function createEventBus(): EventBus {
  const handlers: EventHandler[] = [];
  let emitDepth = 0;

  return {
    register(handler) {
      if (emitDepth > 0) {
        throw new Error("実行中にEventHandlerは登録できません");
      }
      handlers.push(handler);
    },
    emit(event, state) {
      const matched = handlers.filter((handler) => handler.kind === event.kind);
      emitDepth += 1;
      try {
        for (const handler of matched) {
          handler.handle(event, state);
        }
      } finally {
        emitDepth -= 1;
      }
    },
  };
}

beforeDamage のあとに、ハンドラが残した event.damage を HP から引きます。afterDamage はそのあとです。

export function executeAction(
  state: BattleState,
  action: Action,
  events?: EventBus,
): BattleState {
  const actor = findPokemon(state, action.actorId);
  const target = findPokemon(state, action.targetId);

  state.logs.push(`${actor.name}の${action.move}!`);

  const damageEvent: BattleEvent = {
    kind: "beforeDamage",
    actorId: actor.id,
    targetId: target.id,
    move: action.move,
    damage: BASE_DAMAGE,
  };
  events?.emit(damageEvent, state);

  target.currentHp -= damageEvent.damage;
  state.logs.push(`${target.name}に${damageEvent.damage}ダメージ!`);

  events?.emit(
    {
      kind: "afterDamage",
      actorId: actor.id,
      targetId: target.id,
      move: action.move,
      damage: damageEvent.damage,
    },
    state,
  );

  return state;
}

ハンドラがないときは damage は 20 のままなので、表示も両者 HP 80 のままです。特性や持ち物は、まだこのバスには登録していません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?