11.戦闘システム
1.ダメージ計算式の変更
今回は、通常攻撃とスキル・アイテムのダメージ計算式を2000風に変更してみたいと思います。
通常攻撃の計算
スキルとアイテムの計算
通常攻撃の計算
通常攻撃の計算式を以下のものに変更します。
(Aの攻撃力/2 - Bの防御力/4) * 属性修正
変更箇所は「ダメージ」とでも検索してみましょう。
それらしい部分は「● 通常攻撃によるダメージ計算」くらいですね。
ということで、Game_Battler#make_attack_damage_value を変更したいと思います。
デフォルトでは、小数点以下は切り捨てられているようですのが、小数を使って計算したいと思います。
小数の計算は、単純に小数点をつけて計算するだけです。
変数の値を使用したい場合は、Float(var) 関数を使っても良いですし、
var.to_f メソッドを使っても小数に変換できます。
基本計算の配列や属性修正の小数は、使用しないほうが高速だとは思います。
ダメージは、元々、小数ではないので整数に戻す処理も忘れずに追加します。
小数を丸めるには、truncate,round,ceil,floor など複数の方法があるので、
好きなように行ってください。今回は、切り捨てています。
ちなみに、切り上げのメソッドは用意されていないようです。
(Aの攻撃力/2 - Bの防御力/4) * 属性修正
変更箇所は「ダメージ」とでも検索してみましょう。
それらしい部分は「● 通常攻撃によるダメージ計算」くらいですね。
ということで、Game_Battler#make_attack_damage_value を変更したいと思います。
デフォルトでは、小数点以下は切り捨てられているようですのが、小数を使って計算したいと思います。
小数の計算は、単純に小数点をつけて計算するだけです。
変数の値を使用したい場合は、Float(var) 関数を使っても良いですし、
var.to_f メソッドを使っても小数に変換できます。
# 基本計算 (マイナスなら 0 に) damage = [0, (attacker.atk / 2.0) - (self.def / 4.0)].max # 属性修正 (小数点以下を切り捨てて、整数へ変換) damage = Integer(damage * elements_max_rate(attacker.element_set) / 100.0) # ダメージが 0 の行以降省略基本計算と属性修正は、複数行に分けて計算が行われていますが、一行で書くとこのようになります。
基本計算の配列や属性修正の小数は、使用しないほうが高速だとは思います。
ダメージは、元々、小数ではないので整数に戻す処理も忘れずに追加します。
小数を丸めるには、truncate,round,ceil,floor など複数の方法があるので、
好きなように行ってください。今回は、切り捨てています。
ちなみに、切り上げのメソッドは用意されていないようです。
スキルとアイテムの計算
スキルとアイテムの計算式を以下のものに変更します。
基本効果量 + (Aの攻撃力*打撃関係度/20) + (Aの精神力*精神関係度/40) * 属性修正
※ [防御無視] の設定が無効な場合は、属性修正の前に以下の計算結果を引く
(Bの防御力*打撃関係度/20) + (Bの精神力*精神関係度/40)
この2つは Game_Battler#make_obj_damage_value で計算されています。
通常攻撃より複雑ですが、コメントも付いてますし大丈夫ですね。
まず、ダメージがプラスかマイナスかで分岐してます。
プラスがダメージの計算で、マイナスが回復の計算ですね。
今回も小数で計算しているので、丸める処理を忘れず追加しましょう。
基本効果量 + (Aの攻撃力*打撃関係度/20) + (Aの精神力*精神関係度/40) * 属性修正
※ [防御無視] の設定が無効な場合は、属性修正の前に以下の計算結果を引く
(Bの防御力*打撃関係度/20) + (Bの精神力*精神関係度/40)
この2つは Game_Battler#make_obj_damage_value で計算されています。
通常攻撃より複雑ですが、コメントも付いてますし大丈夫ですね。
まず、ダメージがプラスかマイナスかで分岐してます。
プラスがダメージの計算で、マイナスが回復の計算ですね。
今回も小数で計算しているので、丸める処理を忘れず追加しましょう。
damage = obj.base_damage # 基本ダメージを取得 if damage > 0 # ダメージが正の数 damage += user.atk * obj.atk_f / 20.0 # 打撃関係度: 使用者 damage += user.spi * obj.spi_f / 40.0 # 精神関係度: 使用者 unless obj.ignore_defense # 防御力無視以外 damage -= self.def * obj.atk_f / 40.0 # 打撃関係度: 対象者 damage -= self.spi * obj.spi_f / 80.0 # 精神関係度: 対象者 end damage = 0 if damage < 0 # マイナスなら 0 に elsif damage < 0 # ダメージが負の数 damage += user.atk * obj.atk_f / 20.0 # 打撃関係度: 使用者 damage += user.spi * obj.spi_f / 40.0 # 精神関係度: 使用者 end # 属性修正 (小数点以下を切り捨てて、整数へ変換) damage = Integer(damage * elements_max_rate(attacker.element_set) / 100.0) # 分散 の行以降省略
・ 少数を使用した計算の結果は、少数になるので整数へ戻す処理が必要
・ 数値がプラスならダメージ、マイナスなら回復
・ 数値がプラスならダメージ、マイナスなら回復
2.命中率と回避率の変更
VXでは、命中率と回避率があり敏捷性などのパラメータに関係なく確率で判定します。
命中と回避は、それぞれ別に処理されるので、命中に成功しても、回避が成功すると攻撃は当たりません。
どちらも百分率で表すので、メソッドの戻り値は 0〜100 になるようにします。
命中率の補正
回避率の補正
命中と回避は、それぞれ別に処理されるので、命中に成功しても、回避が成功すると攻撃は当たりません。
どちらも百分率で表すので、メソッドの戻り値は 0〜100 になるようにします。
命中率の補正
命中率は以下のように変更します。
100 - (100-Aの武器の命中率) * (1+(Bの敏捷性/Aの敏捷性-1)/2)
命中率の計算は、Game_Battler#calc_hit で行われています。
100 - (100-Aの武器の命中率) * (1+(Bの敏捷性/Aの敏捷性-1)/2)
命中率の計算は、Game_Battler#calc_hit で行われています。
class Game_Battler def calc_hit(user, obj = nil) if obj == nil # 通常攻撃の場合 hit = 100-Integer((100-user.hit)*(1+(Float(self.agi)/user.agi-1)/2)) physical = true elsif obj.is_a?(RPG::Skill) # スキルの場合 # 中略 end return Integer(hit) # 小数点以下を切り捨てて、整数へ変換 end end
回避率の補正
2000 には、回避率の計算がないので、命中率を参考に適当に変更します。
Bの回避率 * (1+(Bの敏捷性/Aの敏捷性-1)/2)
回避率の計算は、Game_Battler#calc_eva で行われています。
Bの回避率 * (1+(Bの敏捷性/Aの敏捷性-1)/2)
回避率の計算は、Game_Battler#calc_eva で行われています。
class Game_Battler def calc_eva(user, obj = nil) eva = Integer(self.eva * (1 + (Float(self.agi) / user.agi - 1) / 2)) eva = 0 if eva < 0 # マイナスなら0 eva = 0 if obj != nil && !obj.physical_attack # 物理攻撃ではない場合 eva = 0 unless parriable? # 回避不能な状態の場合 return Integer(eva) # 小数点以下を切り捨てて、整数へ変換 end end
3.敗北してもゲームオーバーにしない
まずは、ゲームオーバーの処理を探します。
「ゲームオーバー」で全検索すると、1箇所だけ引っかかるはずです。
次に、この処理がどこで使われているのかを探します。
「call_gameover」で全検索すると、今度は2箇所あると思います。
update_scene_change の方は、イベントコマンドでの呼び出しなので、
変更すべきは Scene_Battle#battle_end の戦闘終了時の処理ですね。
$game_troop.can_lose というのが、敗北可能フラグです。
つまり、戦闘に敗北して、なおかつフラグがOFFのときにゲームオーバーになります。
ということなので、ここの敗北時の分岐を行わなければ良さそうです。
しかし、このままだとパーティのHPが0のままですので、次の戦闘からは必ず負けてしまいます。
復活させる処理が必要なので Scene_Battle#process_defeat に以下の処理を追加します。
これで、敗北時にパーティのHPが1にMPが0に変更されます。
$game_party.members を $game_party.dead_members に変更すると、
戦闘不能のアクターにのみ、処理を適用することもできます。
以下、まとめたもの。
「ゲームオーバー」で全検索すると、1箇所だけ引っかかるはずです。
次に、この処理がどこで使われているのかを探します。
「call_gameover」で全検索すると、今度は2箇所あると思います。
update_scene_change の方は、イベントコマンドでの呼び出しなので、
変更すべきは Scene_Battle#battle_end の戦闘終了時の処理ですね。
$game_troop.can_lose というのが、敗北可能フラグです。
つまり、戦闘に敗北して、なおかつフラグがOFFのときにゲームオーバーになります。
ということなので、ここの敗北時の分岐を行わなければ良さそうです。
if result == 2 and not $game_troop.can_lose
# ↓↓↓ 以下のように変更 ↓↓↓
if result == 2 && $game_switches[10]
これで、10番のスイッチがONのときだけゲームオーバーにするという処理に変わりました。しかし、このままだとパーティのHPが0のままですので、次の戦闘からは必ず負けてしまいます。
復活させる処理が必要なので Scene_Battle#process_defeat に以下の処理を追加します。
これで、敗北時にパーティのHPが1にMPが0に変更されます。
for actor in $game_party.members actor.hp = 1 actor.mp = 0 enddead_members で検索したところ一箇所でしか呼ばれていないので、このままでも良いと思いますが、
$game_party.members を $game_party.dead_members に変更すると、
戦闘不能のアクターにのみ、処理を適用することもできます。
以下、まとめたもの。
class Scene_Battle #-------------------------------------------------------------------------- # ● 敗北の処理 #-------------------------------------------------------------------------- alias _cao_gameover_process_defeat process_defeat def process_defeat _cao_gameover_process_defeat for actor in $game_party.members actor.hp = 1 actor.mp = 0 end end #-------------------------------------------------------------------------- # ● 戦闘終了 # result : 結果 (0:勝利 1:逃走 2:敗北) #-------------------------------------------------------------------------- def battle_end(result) if result == 2 && $game_switches[10] call_gameover else $game_party.clear_actions $game_party.remove_states_battle $game_troop.clear if $game_temp.battle_proc != nil $game_temp.battle_proc.call(result) $game_temp.battle_proc = nil end unless $BTEST $game_temp.map_bgm.play $game_temp.map_bgs.play end $scene = Scene_Map.new @message_window.clear Graphics.fadeout(30) end $game_temp.in_battle = false end end
4.戦闘開始時にMP0
戦闘開始時にMPをリセットして0にしたいと思います。
戦闘開始時なので、素直に start メソッドに処理を加えるとよさそうですね。
メソッドの意味的には、こちらの方が適切でしょうか?
戦闘開始時なので、素直に start メソッドに処理を加えるとよさそうですね。
class Scene_Battle alias _cao_mp0_start start def start _cao_mp0_start $game_party.members.each {|actor| actor.mp = 0 } end end他にも post_start や process_battle_start なども良さそうです。
メソッドの意味的には、こちらの方が適切でしょうか?
5.防御時にMPの回復オプション
今回は、新しいオプションを追加してみようと思います。
Scene_Battle の定義を順に見ていくと、「戦闘行動」についての定義がいくつか見つかります。
その中に「戦闘行動の実行」という定義があり、攻撃やスキルなどのコメントがあるので、
おそらくこの防御というところで処理されているのだろうと推測できます。
Scene_Battle#execute_action_guard メソッドで p :guard などを実行して確認しましょう。
防御行動を行っているバトラーの情報は、@active_battler に入っています。
@message_window.add_instant_text で、メッセージを追加できるようなので、
MPを回復した際には、メッセージを追加します。
このオプションを追加定義しましょう。
このオプションが有効なのは、アクターのみなので、まず Game_Battler クラスで
すべてのバトラーがMP回復を行わないように定義します。
これで、バトラーのタイプがアクターだった場合のみ判定を行うようにできます。
Scene_Battle の定義を順に見ていくと、「戦闘行動」についての定義がいくつか見つかります。
その中に「戦闘行動の実行」という定義があり、攻撃やスキルなどのコメントがあるので、
おそらくこの防御というところで処理されているのだろうと推測できます。
Scene_Battle#execute_action_guard メソッドで p :guard などを実行して確認しましょう。
防御行動を行っているバトラーの情報は、@active_battler に入っています。
@message_window.add_instant_text で、メッセージを追加できるようなので、
MPを回復した際には、メッセージを追加します。
class Scene_Battle #-------------------------------------------------------------------------- # ● 戦闘行動の実行 : 防御 #-------------------------------------------------------------------------- def execute_action_guard text = sprintf(Vocab::DoGuard, @active_battler.name) @message_window.add_instant_text(text) # MP回復 if @active_battler.recovery_mp_guard wait(20) value = rand(9) + 4 @active_battler.mp += value @message_window.add_instant_text("MPを #{value} 回復した!") end wait(45) end end@active_battler.recovery_mp_guard で、MP回復を行うバトラーかを判別しています。
このオプションを追加定義しましょう。
このオプションが有効なのは、アクターのみなので、まず Game_Battler クラスで
すべてのバトラーがMP回復を行わないように定義します。
class Game_Battler
#--------------------------------------------------------------------------
# ● オプション [MP回復防御] の取得
#--------------------------------------------------------------------------
def recovery_mp_guard
return false
end
end
さらに、Game_Actor ではMP回復するアクターかを判別する処理に書き換えます。これで、バトラーのタイプがアクターだった場合のみ判定を行うようにできます。
class Game_Actor
EXT_OPTIONS ||= {}
EXT_OPTIONS[:recovery_mp_guard] = [3, 4, 6]
#--------------------------------------------------------------------------
# ● オプション [MP回復防御] の取得
#--------------------------------------------------------------------------
def recovery_mp_guard
return EXT_OPTIONS[:recovery_mp_guard].include?(@actor_id)
end
end
6.MP転向オプション (ステート)
今回は、ダメージの一部をMPで肩代わりする機能を追加します。
オプションは、ステートの有無で判定したいと思います。
まず、Game_Battler にオプションを無効にする定義を行います。
ダメージの計算を行っても、すぐには反映されず変数に保存されます。
ですので、その変数 @hp_damage と @mp_damage の値を変更します。
詳細は、Game_Battler#execute_damage を辿ってください。
現在のMPと比較して少ないほうをダメージとしているのは、
MPが 0 のときには、100% のHPダメージが通るようにするためです。
オプションは、ステートの有無で判定したいと思います。
まず、Game_Battler にオプションを無効にする定義を行います。
class Game_Battler
#--------------------------------------------------------------------------
# ● オプション [吸収防御] の取得
#--------------------------------------------------------------------------
def absorb_guard
return false
end
end
Game_Actor でステートの有無を確認する処理に変更します。
class Game_Actor STATE_ABSORD_GUARD = 17 # 吸収防御のオプション (ステートID) #-------------------------------------------------------------------------- # ● オプション [吸収防御] の取得 #-------------------------------------------------------------------------- def absorb_guard return self.state?(STATE_ABSORD_GUARD) end endダメージの計算を行っているところを探して、処理を追加します。
ダメージの計算を行っても、すぐには反映されず変数に保存されます。
ですので、その変数 @hp_damage と @mp_damage の値を変更します。
詳細は、Game_Battler#execute_damage を辿ってください。
class Game_Battler #-------------------------------------------------------------------------- # ● 通常攻撃によるダメージ計算 #-------------------------------------------------------------------------- alias _cao_absorb_guard_make_attack_damage_value make_attack_damage_value def make_attack_damage_value(attacker) _cao_absorb_guard_make_attack_damage_value(attacker) # 防御中 & オプションON & ダメージ有り の場合 if self.guarding? && self.absorb_guard && @hp_damage > 0 damage = [self.mp, [1, @hp_damage / 10].max].min @hp_damage -= damage @mp_damage += damage end end endMP転向の処理ですが、HPダメージの 10% をMPダメージに変換しています。
現在のMPと比較して少ないほうをダメージとしているのは、
MPが 0 のときには、100% のHPダメージが通るようにするためです。