はじめに
前回は、せいでんきの割り込みが終わってから、元の攻撃へ戻るようにしました。
今回は、同じ Event に複数のハンドラがあるときの順番を決めます。数が小さい priority から実行し、同じ値は登録した順です。割り込みは深さ優先のままです。キューは別の順番として用意し、攻撃には使いません。
ガブリアス戦と、せいでんきが成功するリザードン戦のログは前回のままです。
今回のポイント
- 同じ種類のハンドラは、呼ぶ直前に
priorityで並べる。登録配列そのものは並べ替えない - せいでんきより先に登録しても、
priorityが大きければ連鎖のあとになる - 割り込みはスタック。子が終わってから、呼び出し元の続きと次のハンドラへ戻る
- キューは、すでに並んでいる処理を、あとから積んだ処理より先に実行する
優先度 1 の確認をせいでんきより先に登録したときの順は、次のとおりです。
せいでんき → まひ付与 → きのみ消費 → まひ回復 → 接触の確認 → ピカチュウの攻撃
ディレクトリ
増えたのは、スタックとキューを比べるファイルです。バトルの Section の並びは前回のままです。
Before
src/
├── main.ts
└── battle/
├── types.ts
├── section.ts
├── event.ts 同じ種類を登録順に実行する
├── status.ts
├── handlers.ts
├── scenario.ts リザードン対ピカチュウ
├── battle.ts
├── turn.ts
└── actions.ts
After
src/
├── main.ts
└── battle/
├── types.ts
├── section.ts
├── event.ts 同じ種類を priority で並べる
├── order.ts 追加。スタックとキューの比較
├── status.ts
├── handlers.ts
├── scenario.ts 優先度シナリオを追加
├── battle.ts
├── turn.ts
└── actions.ts
シーケンス
変わったのは、同じ afterDamage の呼び出し順です。登録が先でも、priority が 1 の確認はせいでんきの連鎖が戻ってから実行されます。
Before
After
スタックとキューは、A が C を追加してから B へ進むときの違いです。バトルの割り込みは左のスタックです。
コード
emit は、種類が一致したものだけを、その時点の priority で安定ソートします。省略は 0 です。
const ordered = handlers
.filter((handler) => handler.kind === event.kind)
.map((handler, index) => ({
handler,
priority: handler.priority ?? 0,
index,
}));
ordered.sort((left, right) => left.priority - right.priority || left.index - right.index);
スタックは子をその場で最後まで実行します。キューは末尾へ追加するだけで、いまのタスクの続きが先です。
task.run((child) => {
runTask(child);
});
task.run((child) => {
queue.push(child);
});
A、B があって A が C を追加すると、開始順はスタックが A, C, B、キューが A, B, C です。