反動技とは
ポケモンでは反動技というものがあります。反動によっても様々で、現在の仕組みのままでも実装可能なものや、新たに処理が必要なものまであります。なので、一律で反動技という同じ処理は出来ません。
反動技(ポケモンwiki)
反動技(はんどうわざ)は、威力が大きい代わりに、自分が何らかのリスク(反動)を負うわざの俗称。
- 反動の種類
- 与えたダメージに対応した反動ダメージを受ける
- 与えたダメージによらずにダメージを受ける
- 技が外れるとダメージを受ける
- ひんしになる
- 動けなくなる
- 能力が低下する
1番と2番と6番は、技のeffectメソッドにて実装することができます。1番であれば「とっしん」や「すてみタックル」、2番であれば「わるあがき」が該当します。6番の能力低下技は第1世代の技の中にはありませんが、自分に対象の能力低下を行うだけなので、処理はさほど変わらず実装が簡単です。
4番の「ひんしになる」技は「じばく」や「だいばくはつ」が該当します。こちらは第1世代の技に該当するため作成しますが、今回はスルーします。
では、残りの3番「技が外れるとダメージを受ける」と、5番の「動けなくなる」を作成していきましょう。
はかいこうせん
まずは第1世代での高火力技の1つである「はかいこうせん」を作成します。こちらは、使用すると次のターン行動ができないという反動があります。技が外れると反動は発生しないため、こちらはeffectメソッドと状態変化を利用して反動の判定を導入しましょう。
反動による行動不能
技を使用後、使用ポケモンに「反動がある」という状態を残し、次のターンに伝える必要があります。なので、状態変化に格納するために新しく反動クラス(ScRecoil)を作成しましょう。
反動:状態変化(/Classes/StateChange/ScRecoil.php)
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/StateChange.php');
// 反動
class ScRecoil extends StateChange
{
/**
* 正式名称
* @var string
*/
protected $name = '反動';
/**
* 行動失敗
* @var string
*/
protected $failed_msg = '::pokemonは、反動で動けない';
}
行動失敗時(反動で動けない)際のメッセージのみ設定しています。次に、技(はかいこうせん)のeffectメソッドを使って反動をセットします。
はかいこうせん(/Classes/Move/SuperBeam.php)
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Move.php');
// はかいこうせん
class HyperBeam extends Move
{
/**
* 正式名称
* @var string
*/
protected $name = 'はかいこうせん';
/**
* 説明文
* @var string
*/
protected $description = '使ったポケモンは次のターン、反動で動けない。';
/**
* タイプ
* @var string
*/
protected $type = 'Normal';
/**
* 分類
* @var string(physical:物理|special:特殊|status:変化)
*/
protected $species = 'special';
/**
* 威力
* @var integer
*/
protected $power = 150;
/**
* 命中率
* @var integer
*/
protected $accuracy = 90;
/**
* 使用回数
* @var integer
*/
protected $pp = 5;
/**
* 優先度
* @var integer
*/
protected $priority = 0;
/**
* 追加効果
*
* @param array $args
* @return void
*/
public function effects(...$args)
{
/**
* @param Pokemon $atk 攻撃ポケモン
* @param Pokemon $def 防御ポケモン
*/
list($atk, $def) = $args;
// はんどうをセット
$atk->setSc('ScRecoil');
}
}
※setScのホワイトリストにScRecoilを追加しておきましょう
これで、はかいこうせんがヒットすれば反動状態をセットすることができます。
次に、行動前の状態変化チェックで反動の判定を追加しましょう。
チェック用トレイト(/App/Traits/Service/Battle/ServiceBattleCheckTrait.php)
/**
* アタック前の状態変化チェック
* 1. ひるみ
* 2. 反動
* 3. こんらん
* @param object Pokemon
* @return boolean
*/
protected function checkBeforeSc($pokemon)
{
$sc = $pokemon->getSc();
if(empty($sc)){
// 状態変化にかかっていない
return true;
}
/**
* ひるみ
*/
if(isset($sc['ScFlinch'])){
// 行動失敗(ひるみ解除はcheckAfterScで行う※先手はひるみの影響を受けないため)
return false;
}
/**
* 反動
*/
if(isset($sc['ScRecoil'])){
$recoil = new ScRecoil;
// 反動メッセージを格納
$this->setMessage($recoil->getFailedMessage($pokemon->getPrefixName()));
// 反動解除
$pokemon->releaseSc('ScRecoil');
return false;
}
/**
* こんらん
*/
反動はひるみ状態にされて行動不能になると解除されません。また、こんらんチェックの前に反動チェックが入るため、ひるみの後、こんらんの前に反動チェックを入れておきます。
チェック内容は至って簡単です。反動(ScRecoil)がセットされているかどうかを確認して、あれば行動不能メッセージを格納して反動解除、なければ次のチェックに移行するだけです。
これで反動による行動不能処理は完成です。
行動不能時のループ処理
反動があれば、次のターンの行動選択ができません。なので、チャージターンと同様に次のターンの行動をループさせましょう。
まず、簡単に対象の状態変化が確認できるようにポケモンクラス用のチェックトレイトを新しく用意しましょう。
チェックトレイト:ポケモン用(/Traits/Class/Pokemon/ClassPokemonCheckTrait.php)
<?php
trait ClassPokemonCheckTrait
{
/**
* 状態変化の確認用メソッド
* @param string $key
* @return void
*/
public function checkSc($key)
{
if(isset($this->sc[$key])){
return true;
}else{
return false;
}
}
/**
* 現在のレベルで覚えられる技があるか確認する処理
*
* @return integer
*/
protected function checkMove()
{
// レベルアップして覚えられる技があれば習得する
$level_move_keys = array_keys(array_column($this->level_move, 0), $this->level);
foreach($level_move_keys as $key){
$move_class = $this->level_move[$key][1];
// 覚えようとしている技(クラス)が存在かつ重複していないか
if(class_exists($move_class) && !in_array($move_class, $this->move, true)){
// 技クラスをセット
$this->setMove($move_class);
// インスタンス化
$move = new $move_class();
$this->setMessage($move->getName().'を覚えた!', 'success');
}
}
}
}
※ポケモンクラスに記述していた技チェックメソッド(checkMove)も移動しました
これで、checkScというメソッドを使えば、指定した状態変化にかかっているかどうかが簡単に確認できますね。
それでは、こちらを使ってループ処理を作成しましょう。
バトルコントローラー(/App/Controllers/Battle/BattleController.php)
/**
* @return void
*/
public function __construct()
{
// 親コンストラクタの呼び出し
parent::__construct();
// 引き継ぎ
$this->takeOver();
// 分岐処理
$this->branch();
// 次のターンへの分岐(ループ処理)
while($this->nextTurn());
// 親デストラクタの呼び出し
parent::__destruct();
}
-- 省略
/**
* 次のターンへの判定処理
*
* @return boolean
*/
private function nextTurn()
{
// ひんしポケモンがでた場合の処理
if($this->fainting['enemy'] || $this->fainting['friend']){
$this->judgment();
return false;
}
// チャージ中または反動有りなら再度アクション実行
if($this->chargeNow() || $this->pokemon->checkSc('ScRecoil')){
$this->branch();
return true;
}else{
$this->setMessage('行動を選択してください');
return false;
}
}
まずはコンストラクタ内で呼び出しているnextTurnメソッド部分を修正しました。
// 次のターンへの分岐(ループ処理)
while($this->nextTurn());
処理なしのwhileの判定にnextTurnメソッドを使用しています。もしnextTurnがtrueを返せば再度nextTurnを実行します。falseを返した時点で処理が終了です。
今までnextTurnメソッドでは返り値無しとしていましたが、もし処理が残っていればtrue、なければfalseを返すように変更しました。
/**
* 次のターンへの判定処理
*
* @return boolean
*/
private function nextTurn()
{
// ひんしポケモンがでた場合の処理
if($this->fainting['enemy'] || $this->fainting['friend']){
$this->judgment();
return false;
}
// チャージ中または反動有りなら再度アクション実行
if($this->chargeNow() || $this->pokemon->checkSc('ScRecoil')){
$this->branch();
return true;
}else{
$this->setMessage('行動を選択してください');
return false;
}
}
チャージチェックと同じタイミングで反動チェックを行なっています。もしどちらかがtrueであれば、再度branchが実行されます。これで行動不能時はループ処理が実行されて次のターンを自動処理してくれます。
※ループ処理を導入する際は永久ループに陥らないように注意してください
それでは「はかいこうせん」を使って動きを確認してみましょう。
正常に反動メッセージが返ってきました。これで反動技による行動不能の実装は完了です。
とびひざげり
数ある中でもわずかですが、攻撃が外れることにより自らがダメージを受けるという技があります。その1つが「とびひざげり」です。こちらも初代でサワムラーが覚えるため、実装していきましょう。
行動失敗による反動ダメージ
反動ダメージを受ける技は「とっしん」などがありますが、これらと違って「とびひざげり」はeffectメソッドで処理ができません。なぜなら「攻撃が外れたらダメージを受ける」という反動のため、外れた際にはeffectメソッドが実行されないからです。
なので、新しく技クラスにfailedという失敗時に実行するためのメソッドを作成しましょう。
技クラス(/Classes/Move.php)
/**
* 技の失敗
*
* @param object Pokemon $atk
* @return void
*/
public function failed($atk)
{
//
}
ほとんどの技では使用しないため、おなじみ空メソッドを作成しておきます。引数には攻撃ポケモンのインスタンス(オブジェクト)を受け取りましょう。
次に、とびひざげりのクラスを作成します。
とびひざげり(/Classes/Move/HighJumpKick.php)
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Move.php');
// とびひざげり
class HighJumpKick extends Move
{
/**
* 正式名称
* @var string
*/
protected $name = 'とびひざげり';
/**
* 説明文
* @var string
*/
protected $description = '攻撃がはずれると自分最大HPの半分のダメージを受ける。';
/**
* タイプ
* @var string
*/
protected $type = 'Fighting';
/**
* 分類
* @var string(physical:物理|special:特殊|status:変化)
*/
protected $species = 'physical';
/**
* 威力
* @var integer
*/
protected $power = 130;
/**
* 命中率
* @var integer
*/
protected $accuracy = 90;
/**
* 使用回数
* @var integer
*/
protected $pp = 10;
/**
* 優先度
* @var integer
*/
protected $priority = 0;
/**
* 技の失敗
*
* @param object Pokemon $atk
* @return void
*/
public function failed($atk)
{
// 最大HPの1/2ダメージを受ける
$atk->calRemainingHp('sub', $atk->getStats('HP') / 2);
// メッセージをセット
$this->setMessage('勢い余って'.$atk->getPrefixName().'は地面にぶつかった');
}
}
処理内容は、calRemainingHpメソッドを使って最大HPの半分を自らから減算するというものです。
failedメソッドではeffect同様に返り値はありません。メッセージを自身に格納するので、必要であれば実行後にメッセージを取り出して使用しましょう。
では攻撃が外れた際と、こうかがない等で技が当たらなかった時に失敗処理を呼び出すように攻撃用トレイト内のattackメソッドへ記述しましょう。まずは同一処理ができるように、メッセージの取得までの一連の流れをまとめたattackFailedというメソッドを作成します。
攻撃用トレイト(/Traits/Service/Battle/ServiceBattleAttackTrait.php)
/**
* 攻撃判定失敗時の処理
*
* @param object Pokemon $atk
* @param object Move $move
* @return void
*/
private function attackFailed($atk, $move)
{
// 技の失敗メソッドを呼び出し
$move->failed($atk);
// もしメッセージが返ってきていれば格納
if(!empty($move->getMessages())){
$this->setMessage($move->getMessages());
$move->resetMessage();
}
}
では、攻撃失敗判定で処理を終了させる前に作成したメソッドを呼び出しましょう。
攻撃用トレイト:attackメソッド内(/Traits/Service/Battle/ServiceBattleAttackTrait.php)
// 「こうかがない」の判定(命中率と威力がnullではなく、タイプ相性補正が0の場合)
if(!is_null($move->getAccuracy()) && !is_null($move->getPower()) && ($this->m === 0)){
// こうかがない
$this->setMessage($def_pokemon->getPrefixName().'には効果が無いみたいだ');
// 失敗判定
$this->attackFailed($atk_pokemon, $move);
return;
}
// 命中判定
if(!$this->checkHit($atk_pokemon, $def_pokemon, $move)){
// 攻撃失敗
$this->attackFailed($atk_pokemon, $move);
return;
}
では、実際にとびひざげりを使用して、技が外れた際の動きを確認してみましょう。
ガッツリと最大HPの半分の反動ダメージを受けることができました。calRemainingHpの引数は整数指定をしているため、小数点以下は切り捨てで計算されます。
これで、失敗時に反動ダメージを受ける技(とびひざげり)の実装が完了しました。
まとめ
いかがだったでしょうか。
今回のPHPポケモンは「反動技(はかいこうせん・とびひざげり)」の作成方法を紹介しました。
現在プログラミング学習に取り組んでいる方や、ゲーム開発に興味がある方は、ぜひ参考にしてみてくださいね。