cat content/blog/rpg-attack-logic.md
「たたかう」のロジックをコードで考える
- Node.js
- RPG
- 「たたかう」
RPG の「たたかう」処理を題材に、攻撃対象やダメージ計算のロジックをコードで考えるメモ。
勇者と逆さまの塔も、バトルマシンも、デモンズダンジョンも、RPGです。
そして、RPGといえば「たたかう」です。その「たたかう」のロジックを考えてみましょう。
ただのなぐりあいのロジック
例えば、こんな勇者とゴブリンがいます。
const hero = { // 勇者 maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, // 攻撃力 def: 10 // 防御力}
const goblin = { // ゴブリン maxHp: 100, hp: 100, maxMp: 0, mp: 0, atk: 20, // 攻撃力 def: 5 // 防御力}この2人に殴り合いをしてもらいましょう。 ダメージ計算式は、偉大なアルテリオス計算式を採用します。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 10,}
const goblin = { maxHp: 100, hp: 100, maxMp: 0, mp: 0, atk: 20, def: 5,}
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtkDamage = hero.atk - goblin.def goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) if (goblin.hp > 0) { const goblinAtkDamage = goblin.atk - hero.def hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}勇者のHP: 100 ゴブリンのHP: 100勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 90 ゴブリンのHP: 75勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 80 ゴブリンのHP: 50勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 70 ゴブリンのHP: 25勇者の攻撃、ゴブリンに25のダメージ!勇者の勝ち!勇者が hp を 70 残した状態で勝ちますね。でも、これでは色々と考慮がたりません。もっと考えてみましょう。
両者ともカッチカチの場合の殴り合いは永久に
例えば、勇者とゴブリンがお互いに防御力に特化していたら、どうなるでしょうか。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 999, // カッチカチ}
const goblin = { maxHp: 100, hp: 100, maxMp: 0, mp: 0, atk: 20, def: 999, // カッチカチ}
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtkDamage = hero.atk - goblin.def goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) if (goblin.hp > 0) { const goblinAtkDamage = goblin.atk - hero.def hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}答えは簡単、無限ループします。お互いにダメージがマイナスになる為、HPが0以下にならないからです。
このままでは、勇者の攻撃力より高い防御力を持った敵が現れた場合、バグと化してしまいます。対応策は色々とありますが、ここでは最低保障ダメージシステムを採用しましょう。意味は読んでそのままです。
const hero = { ...略 }
const goblin = { ...略 }
const damageCalculator = (offense, defense) => { const damage = offense - defense // 攻撃力 - 防御力 const least = Math.round(offense / 10) // 最低保証ダメージ(攻撃力の1/10) if (damage < least) { return least } return damage}
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtkDamage = damageCalculator(hero.atk, goblin.def) goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) if (goblin.hp > 0) { const goblinAtkDamage = damageCalculator(goblin.atk, hero.def) hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}勇者のHP: 100 ゴブリンのHP: 100勇者の攻撃、ゴブリンに3のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 98 ゴブリンのHP: 97勇者の攻撃、ゴブリンに3のダメージ!ゴブリンの攻撃、勇者に2のダメージ::中略::勇者のHP: 36 ゴブリンのHP: 4勇者の攻撃、ゴブリンに3のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 34 ゴブリンのHP: 1勇者の攻撃、ゴブリンに3のダメージ!勇者の勝ち!これで攻撃力の1/10のダメージは最低でも通るようになりましたので、いずれ決着がつくようになりました。
しかしこれでは「はぐれメタル」系の敵がいる場合、圧倒的にモンスターに不利になりますので、その場合は最低保証ダメージを 1 にするといいでしょう。
これで単純に殴り合う最低限のロジックができました。次はスキルについて考えてみましょう。これだけだと、ただただ脳筋が勝つゲームになってしまいます。
主人公のスキルよりモンスターのスキルの方が多彩説
まずはスキルについて、色々と考えてみましょう。スキルと一言に言ってもたくさんあります。
- 体力を回復する回復系スキル(アクティブ)
- 強い力で敵を攻撃する強撃系スキル(アクティブ)
- 毒などの状態以上にする特殊スキル(アクティブ)
- 敵の攻撃を一定確率で避けるスキル(パッシブ)
- 味方をかばうスキル(パッシブ)
- パーティにいるだけで味方or敵のステータスが増減するスキル(パッシブ)
- 敵味方全員のスキルを無効にするスキル(パッシブ)
などなど、スキルは無限にあります。これをどうロジックに落としていくか考えてみましょう。問題は、スキルの内容や発動タイミングが多岐に渡るということです。
もしあなたがスキルを実装するとしたらどうやりますか?
さすが遊戯王
自分の場合、色々と考えた結果、「たたかう」に、下記のようなフェイズを作ることにしました。
- スタートフェイズ(一番最初にスキルが発動する)
- セットフェイズ(戦闘開始時にスキルが発動する)
- アタックフェイズ(攻撃側のスキルが発動する)
- ディフェンスフェイズ(防御力側のスキルが発動する)
- ダメージ計算フェイズ(実際のダメージが計算される)
- アタックアフターフェイズ(ダメージ計算後に攻撃側のスキルが発動する)
- ディフェンスアフターフェイズ(ダメージ計算後に防御側のスキルが発動する)
- ダメージ発生フェイズ(上記の流れを元に計算されたダメージがHPから引かれる)
これぐらいのフェイズを設定してあげると、そこそこ複雑なスキルでもロジックを実装することが出来ました。ちなみに後から気がついたんですが、これほぼ遊戯王のターン進行と同じなんですよね。自分なりに知恵を絞った結果だったんですが、さすが遊戯王。
ではこれを、実際のロジックに落としてみましょう。
強撃系スキルの実装
例えば、よくある強撃系のスキルの mpを5消費して、自分の攻撃力を1回だけ250%上げる を実装してみます。なおスキルは自動発動にします。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 10, skill: { // skill追加 attack: { name: '強撃', info: 'mpを5消費して、自分の攻撃力を1回だけ250%上げる', cost: 5 // 消費mp } }}
const goblin = { maxHp: 300, // <- 少しタフに hp: 300, maxMp: 0, mp: 0, atk: 20, def: 5,}
const damageCalculator = (offense, defense) => { ...略 }
const executeAttackPhase = (hero) => { if (hero.skill.attack) { // アタックスキルを持っていたら if (hero.mp >= hero.skill.attack.cost) { // mpが足りていれば hero.mp -= hero.skill.attack.cost // mpを消費して return hero.atk * 3.5 // スキル使用時の勇者の攻撃力を返す } } return hero.atk // 通常の勇者の攻撃力を返す}
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtk = executeAttackPhase(hero) // アタックフェーズの実行 const heroAtkDamage = damageCalculator(heroAtk, goblin.def) goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) if (goblin.hp > 0) { const goblinAtkDamage = damageCalculator(goblin.atk, hero.def) hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}勇者のHP: 100 ゴブリンのHP: 300勇者の攻撃、ゴブリンに100のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 90 ゴブリンのHP: 200勇者の攻撃、ゴブリンに100のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 80 ゴブリンのHP: 100勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 70 ゴブリンのHP: 75勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 60 ゴブリンのHP: 50勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 50 ゴブリンのHP: 25勇者の攻撃、ゴブリンに25のダメージ!勇者の勝ち!ちゃんと強撃スキルが2回発動して倒せました。強撃系スキルは純粋に強いので、上昇幅や使用可能回数の調整が大切になります。頑張って調整しましょう。
次々考えていきましょう。
大防御
mpを1消費して、自分の防御力を1回だけ200%上げる について考えてみます。先程と似ていますが、発動タイミングが違いますね。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 10, skill: { attack: { name: '強撃', info: 'mpを5消費して、自分の攻撃力を1回だけ250%上げる', cost: 5 }, defense: { // skill追加 name: '大防御', info: 'mpを1消費して、自分の防御力を1回だけ200%上げる', cost: 1 // 消費mp } }}
const goblin = { ...略 }
const damageCalculator = (offense, defense) => { ...略 }
const executeAttackPhase = (hero) => { ...略 }
const executeDefensePhase = (hero) => { if (hero.skill.defense) { // ディフェンススキルを持っていたら if (hero.mp >= hero.skill.defense.cost) { // mpが足りていれば hero.mp -= hero.skill.defense.cost // mpを消費して return hero.def * 2 // スキル使用時の勇者の防御力を返す } } return hero.def // 通常の勇者の防御力を返す}
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtk = executeAttackPhase(hero) const heroAtkDamage = damageCalculator(heroAtk, goblin.def) goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) if (goblin.hp > 0) { const heroDefense = executeDefensePhase(hero) // ディフェンスフェーズの実行 const goblinAtkDamage = damageCalculator(goblin.atk, heroDefense) hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}勇者のHP: 100 ゴブリンのHP: 300勇者の攻撃、ゴブリンに100のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 98 ゴブリンのHP: 200勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 96 ゴブリンのHP: 175勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 94 ゴブリンのHP: 150勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 92 ゴブリンのHP: 125勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 90 ゴブリンのHP: 100勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 80 ゴブリンのHP: 75勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 70 ゴブリンのHP: 50勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 60 ゴブリンのHP: 25勇者の攻撃、ゴブリンに25のダメージ!勇者の勝ち!これで、敵からの攻撃時も大防御でダメージを軽減できました。アクティブスキルにすれば、敵の大技に合わせて繰り出すとユーザに「気持ちいい」を与えれますね。さぁ、どんどんいきましょう。
ステータス強化
戦闘開始時にパーティの攻撃力1.2倍 について考えてみます。ノーコストで、いるだけでバフ付与の、つよつよキャラですね。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 10, skill: { attack: { name: '強撃', info: 'mpを5消費して、自分の攻撃力を1回だけ250%上げる', cost: 5 }, defense: { name: '大防御', info: 'mpを1消費して、自分の防御力を1回だけ200%上げる', cost: 1 }, set: { // skill追加 name: 'ステータス強化', info: '戦闘開始時にパーティの攻撃力1.2倍', cost: 0 }, }}
const goblin = { ...略 }
const damageCalculator = (offense, defense) => { ...略 }
const executeAttackPhase = (hero) => { ...略 }
const executeDefensePhase = (hero) => { ...略 }
const executeSetPhase = (hero) => { if (hero.skill.set) { // セットスキルを持っていたら hero.atk = hero.atk * 1.2 // 攻撃力を1.2倍に }}
executeSetPhase(hero) // セットフェーズの実行
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtk = executeAttackPhase(hero) const heroAtkDamage = damageCalculator(heroAtk, goblin.def) goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) if (goblin.hp > 0) { const heroDefense = executeDefensePhase(hero) const goblinAtkDamage = damageCalculator(goblin.atk, heroDefense) hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}勇者のHP: 100 ゴブリンのHP: 300勇者の攻撃、ゴブリンに121のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 98 ゴブリンのHP: 179勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 96 ゴブリンのHP: 148勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 94 ゴブリンのHP: 117勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 92 ゴブリンのHP: 86勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージ-------------------勇者のHP: 90 ゴブリンのHP: 55勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 80 ゴブリンのHP: 24勇者の攻撃、ゴブリンに31のダメージ!勇者の勝ち!やっぱりステータス強化スキルは強いですね。ステータス強化と強撃の組み合わせはバランス崩壊に直結するので、気をつけましょう。さぁさぁどんどんいきましょう。
状態異常
攻撃時に相手を毒にする についても考えてみます。今回は確定で毒にできるとします。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 10, skill: { attack: { name: '強撃', info: 'mpを5消費して、自分の攻撃力を1回だけ250%上げる', cost: 5 }, defense: { name: '大防御', info: 'mpを1消費して、自分の防御力を1回だけ200%上げる', cost: 1 }, set: { // skill追加 name: 'ステータス強化', info: '戦闘開始時にパーティの攻撃力1.2倍', cost: 0 }, attackAfter: { name: '毒撃', info: '攻撃時に相手を毒にする', cost: 0 }, }}
const goblin = { ...略 }
const damageCalculator = (offense, defense) => { ...略 }
const executeAttackPhase = (hero) => { ...略 }
const executeDefensePhase = (hero) => { ...略 }
const executeSetPhase = (hero) => { ...略 }
const executeAttackAfterPhase = (hero) => { if (hero.skill.attackAfter) { // アタックアフタースキルを持っていたら goblin.status = 'poison' // 毒。毎ターンHPの1/10のダメージ }}
executeSetPhase(hero)
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtk = executeAttackPhase(hero) const heroAtkDamage = damageCalculator(heroAtk, goblin.def) goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) executeAttackAfterPhase(hero) // アタックアフターフェーズの実行 if (goblin.hp > 0) { const heroDefense = executeDefensePhase(hero) const goblinAtkDamage = damageCalculator(goblin.atk, heroDefense) hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) // 状態異常発生フェイズ if (goblin.status === 'poison') { const poisonDamage = Math.round(goblin.hp / 10) goblin.hp -= poisonDamage console.log(`ゴブリンに${poisonDamage}の毒ダメージ!`) } console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}勇者のHP: 100 ゴブリンのHP: 300勇者の攻撃、ゴブリンに121のダメージ!ゴブリンの攻撃、勇者に2のダメージゴブリンに18の毒ダメージ!-------------------勇者のHP: 98 ゴブリンのHP: 161勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージゴブリンに13の毒ダメージ!-------------------勇者のHP: 96 ゴブリンのHP: 117勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージゴブリンに9の毒ダメージ!-------------------勇者のHP: 94 ゴブリンのHP: 77勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージゴブリンに5の毒ダメージ!-------------------勇者のHP: 92 ゴブリンのHP: 41勇者の攻撃、ゴブリンに31のダメージ!ゴブリンの攻撃、勇者に2のダメージゴブリンに1の毒ダメージ!-------------------勇者のHP: 90 ゴブリンのHP: 9勇者の攻撃、ゴブリンに31のダメージ!勇者の勝ち!アタックアフターフェイズで毒を発生させました。確定で毒だと強すぎるので、実際には確率でにしたほうがいいと思います。勇者がつよつよになってきましたね!さぁまだまだ。
回避
次は 50%で攻撃を避ける スキルを実装してみましょう。忍者や盗賊がよくもってるスキルですね。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 10, skill: { attack: { name: '強撃', info: 'mpを5消費して、自分の攻撃力を1回だけ250%上げる', cost: 5 }, defense: { name: '大防御', info: 'mpを1消費して、自分の防御力を1回だけ200%上げる', cost: 1 }, set: { // skill追加 name: 'ステータス強化', info: '戦闘開始時にパーティの攻撃力1.2倍', cost: 0 }, attackAfter: { name: '毒撃', info: '攻撃時に相手を毒にする', cost: 0 }, defenseAfter: { name: '回避', info: '50%で攻撃を避ける', cost: 0 } }}
const goblin = { ...略 }
const damageCalculator = (offense, defense) => { ...略 }
const executeAttackPhase = (hero) => { ...略 }
const executeDefensePhase = (hero) => { ...略 }
const executeSetPhase = (hero) => { ...略 }
const executeAttackAfterPhase = (hero) => { ...略 }
const executeDefenseAfterPhase = (hero) => { if (hero.skill.defenseAfter) { // ディフェンスアフタースキルを持っていたら if (Math.random() > 0.5) { return true // 回避判定 } } return false // 回避判定}
executeSetPhase(hero)
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtk = executeAttackPhase(hero) const heroAtkDamage = damageCalculator(heroAtk, goblin.def) goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) executeAttackAfterPhase(hero) if (goblin.hp > 0) { const heroDefense = executeDefensePhase(hero) const goblinAtkDamage = damageCalculator(goblin.atk, heroDefense) const avoidanceFlg = executeDefenseAfterPhase(hero) // ディフェンスアフターフェーズの実行 if (!avoidanceFlg) { hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) } else { console.log(`勇者はゴブリンの攻撃を回避した!`) }
if (goblin.status === 'poison') { const poisonDamage = Math.round(goblin.hp / 10) goblin.hp -= poisonDamage console.log(`ゴブリンに${poisonDamage}の毒ダメージ!`) } console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}これで敵の攻撃を50%で避けることができるようになりました。更につよつよ勇者になってきましたね。次で最後にしましょう。
スキル無効化スキル
敵味方全員がスキル使用不可 というスキルを実装してみましょう。カタストロフ的なスキルですね。モンスターにスキルを設定してないから、デメリットしかありません。
const hero = { maxHp: 100, hp: 100, maxMp: 10, mp: 10, atk: 30, def: 10, skill: { attack: { name: '強撃', info: 'mpを5消費して、自分の攻撃力を1回だけ250%上げる', cost: 5 }, defense: { name: '大防御', info: 'mpを1消費して、自分の防御力を1回だけ200%上げる', cost: 1 }, set: { // skill追加 name: 'ステータス強化', info: '戦闘開始時にパーティの攻撃力1.2倍', cost: 0 }, attackAfter: { name: '毒撃', info: '攻撃時に相手を毒にする', cost: 0 }, defenseAfter: { name: '回避', info: '50%で攻撃を避ける', cost: 0 }, start: { name: 'スキル無効化', info: '敵味方全員がスキル使用不可', cost: 0 }, }}
const goblin = { ...略 }
const damageCalculator = (offense, defense) => { ...略 }
const executeAttackPhase = (hero) => { ...略 }
const executeDefensePhase = (hero) => { ...略 }
const executeSetPhase = (hero) => { ...略 }
const executeAttackAfterPhase = (hero) => { ...略 }
const executeDefenseAfterPhase = (hero) => { ...略 }
const executeStartPhase = (hero) => { if (hero.skill.start) { // スタートスキルを持っていたら hero.skill = {} console.log('全員のスキルを無効化!') }}
executeStartPhase(hero) // スタートフェーズの実行
executeSetPhase(hero)
while (hero.hp > 0 && goblin.hp > 0) { console.log(`勇者のHP: ${hero.hp} ゴブリンのHP: ${goblin.hp}`) const heroAtk = executeAttackPhase(hero) const heroAtkDamage = damageCalculator(heroAtk, goblin.def) goblin.hp -= heroAtkDamage console.log(`勇者の攻撃、ゴブリンに${heroAtkDamage}のダメージ!`) executeAttackAfterPhase(hero) if (goblin.hp > 0) { const heroDefense = executeDefensePhase(hero) const goblinAtkDamage = damageCalculator(goblin.atk, heroDefense) const avoidanceFlg = executeDefenseAfterPhase(hero) if (!avoidanceFlg) { hero.hp -= goblinAtkDamage console.log(`ゴブリンの攻撃、勇者に${goblinAtkDamage}のダメージ`) } else { console.log(`勇者はゴブリンの攻撃を回避した!`) }
if (goblin.status === 'poison') { const poisonDamage = Math.round(goblin.hp / 10) goblin.hp -= poisonDamage console.log(`ゴブリンに${poisonDamage}の毒ダメージ!`) } console.log('-------------------') }}
if (hero.hp) { console.log('勇者の勝ち!')} else { console.log('ゴブリンの勝ち!')}全員のスキルを無効化!勇者のHP: 100 ゴブリンのHP: 300勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 90 ゴブリンのHP: 275勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 80 ゴブリンのHP: 250勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 70 ゴブリンのHP: 225勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 60 ゴブリンのHP: 200勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 50 ゴブリンのHP: 175勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 40 ゴブリンのHP: 150勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 30 ゴブリンのHP: 125勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 20 ゴブリンのHP: 100勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------勇者のHP: 10 ゴブリンのHP: 75勇者の攻撃、ゴブリンに25のダメージ!ゴブリンの攻撃、勇者に10のダメージ-------------------ゴブリンの勝ち!こういう時の為のスタートフェイズです。無事に勇者は全スキルが封印されて、非合法強化勇者はゴブリンに負けました。
めでたしめでたし。
おわりに
普段何気なく遊んでいるゲームの「たたかう」ですが、こうやって自分でロジックを考えてソースコードに落としてみると、いろんな気付きがあるものです。面白そうだなぁと思ったら、ぜひやってみてください。ロジカルシンキングのトレーニングにもなります。