オウムがえしとは
今回は久々に新しい技を実装します。それが「オウムがえし」です。
オウムがえし(ポケモンwiki)
初代で登場した技であり、序盤に登場するポッポやオニスズメがレベルアップで覚える技の1つです。最新世代ではオウムがえしという技は実装されなくなった数少ない技の1つでもあります。
それもそのはず、判定がかなり面倒で特別処理が必要な技の1つだからです。
※実際の理由は知りません
コピーできない技
まず、オウムがえしの技詳細を見てみましょう。
相手が最後に使った技を出す。
技名の通り、相手が使った技をそっくりそのまま返してあげる(コピーして使う)のが、この技の仕様となっています。ですが、すべての技をコピーできるわけではありません。
自分を対象とする技
「せいちょう」や「きあいだめ」など、自身に対してのみ影響を与える技はコピーできません。
自分のフィールドを対象とする技
「しろいきり」や「リフレクター」など、味方フィールドに影響を与える技はコピーできません。
他の技が出る技
「オウムがえし」や「ゆびをふる」など、その技を使うことで別の技が発動する技はコピーできません。
例外
「わるあがき」や「カウンター」はコピーできません。
この通り、意外にもコピーできない技は多くあり、それらが相手の最後に使った技と該当する場合は失敗の判定を返す必要があります。
ターゲットの設定
まず、大きな括りとして「自分または味方(フィールド)を対象とする技」はコピーできません。なので、技クラスに対して技の対象(target)を設定することで判定ができるようにします。
技クラスへのプロパティ追加
/**
* 対象(使ったポケモンから見た立場)
* @var string::friend|enemy
*/
protected $target = 'enemy';
この値を取得できるように、親クラスにはgetTargetというメソッドを追加しておきましょう。これで、オウムがえしを発動した際にtargetを確認することで判定することが可能です。
では、オウムがえしの技クラスを作成しましょう。
オウムがえし(/Classes/Move/MoveMirrorMove.php)
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Move.php');
// オウムがえし
class MoveMirrorMove extends Move
{
/**
* 正式名称
* @var string
*/
protected $name = 'オウムがえし';
/**
* 説明文
* @var string
*/
protected $description = '相手が最後に使った技を出す。';
/**
* タイプ
* @var string
*/
protected $type = 'TypeFlying';
/**
* 分類
* @var string(physical:物理|special:特殊|status:変化)
*/
protected $species = 'status';
/**
* 威力
* @var integer
*/
protected $power = null;
/**
* 命中率
* @var integer
*/
protected $accuracy = null;
/**
* 使用回数
* @var integer
*/
protected $pp = 20;
/**
* 対象(使ったポケモンから見た立場)
* @var string
*/
protected $target = 'enemy';
/**
* オウム返し専用効果
*
* @param Pokemon $def 防御ポケモン
* @param battle_state:object::BattleState バトル状態
* @return mixed
*/
public function exMirrorMove($def, $battle_state)
{
$black_list = [
// オウム返し・カウンター・わるあがき・ものまね
get_class(), 'MoveCounter', 'MoveStruggle', 'Mimic'
];
// 相手が最後に使用した技を取得
$move_class = $battle_state->getLastMove($def->getPosition());
// 技が取得できない、またはコピーできない技一覧と一致すれば失敗
if(
empty($move_class) ||
in_array($move_class, $black_list, true)
){
return false;
}
// 取得した技をインスタンス化
$move = new $move_class;
if($move->getTarget() === 'friend'){
return false;
}
// 成功(技のインスタンスを返却)
return $move;
}
}
オウムがえしでは特別処理が入るため、effectsやchargeとは違う専用メソッド(exMirrorMove)を作成しました。
まず、先程作成したプロパティ(target)を使って判定できない例外技をブラックリストとして配列を用意します。現在わかる範囲で「オウムがえし」「カウンター」「わるあがき」「ものまね」を用意しました。
※ゆびをふるはターゲット不定のため外しました。ただ、不定を導入する必要がなさそうであれば、自分(friend)の設定にする予定です
引数では、判定に必要な相手ポケモンとバトル状態のオブジェクトを取得しています。
まず、バトルオブジェクトから最後に使用した技を取り出します。
※こちらは次項で説明します
もし技使用前(初ターン等)であったり、ブラックリストの技と一致した場合は失敗の判定としてfalseを返却します。その2つをクリアできれば、技をインスタンス化してターゲットを確認、friendであれば失敗とみなしfalseを返却します。
技成功の場合は、そのまま取得した技のインスタンスを返り値とします。これで、失敗ならfalse、成功ならコピーできた技を攻撃トレイトへ伝えることができます。
バトルシステムへの実装
それでは、バトルで使えるようにするため、バトルシステム内にオウムがえしの判定処理を実装していきましょう。
最後に使用した技
まず、前項で登場したバトルシステムから最後に使用した技を取得するための処理を追加します。
バトル状態クラス(/Classes/BattleState.php)
<?php
/**
* バトル状態クラス
*/
class BattleState
{
--省略
/**
* 最後に使用した技
* @var array
*/
private $last_moves;
/**
* @return void
*/
public function __construct()
{
$this->init();
}
/**==================================================================
* 初期化・初期値
==================================================================**/
/**
* 初期化
* @return void
*/
public function init() :void
{
$this->run = 0;
$this->dafaultFields();
$this->dafaultTurnDamages();
$this->dafaultLastMoves();
}
--省略
/**
* 最後に使用した技の初期値
* @return void
*/
public function dafaultLastMoves() :void
{
$this->last_moves = [
'friend' => '',
'enemy' => '',
'all' => '',
];
}
--省略
/**==================================================================
* 最後に使用した技
==================================================================**/
/**
* 最後に使用した技(クラス)の取得
* @param position:string::friend|enemy|all
* @return string
*/
public function getLastMove(string $position = 'all')
{
return $this->last_moves[$position];
}
/**
* 最後に使用した技(クラス)の格納
* @param move:object|string::Move
* @param position:string::friend|enemy
* @return void
*/
public function setLastMove(string $position, $move)
{
// オブジェクト・文字列(クラス名)両方を許可
if(is_object($move)){
$class = get_class($move);
}else{
$class = $move;
}
$this->last_moves[$position] = $class;
$this->last_moves['all'] = $class;
}
}
他のステータスと同じように、初期値を用意してセット、取得と格納用のメソッドを用意しました。1点異なる点は「すべての最終技」としてallの値をセットできるという点です。こちらは使うかどうかわかりませんが、技や状況によっては必要になる可能性を考慮して実装しておきました。
次に、最後に使用した技を格納するタイミングについてです。
ゲームの仕様を確認できていませんが、PHPポケモンでは「発動した技」を最後に使用した技とみなして格納する仕様にしました。
例えば以下のような流れが発生したケースで考えてみてください。
「敵:たいあたり」
→「味方:オウムがえし(たいあたり)」
→「敵:オウムがえし(???)」
上記の場合、敵が使用した「オウムがえし」ではどの技を返すのが正解なのかです。実際に試してみるとわかるかも知れませんが、技をそのまま格納すると「オウムがえし」が選ばれることになり失敗となります。ですが、実際に発動した技を考慮すれば「たいあたり」をコピーしなければなりません。
PHPポケモンでは「たいあたり」を返せるように、attackメソッドの返り値として技オブジェクトを返却し、attackメソッド直後に最後に使用した技を格納しました。
バトルサービス(/App/Services/Battle/FightService.php)
/**
* 行動順に攻撃処理
*
* @return boolean (false: ひんしポケモン有り)
*/
private function actionAttack($orders)
{
foreach($orders as list($atk, $def, $move)){
// 攻撃ポケモンの怒り解除
$atk->releaseSc('ScRage');
// 攻撃(返り値に使用した技を受け取る)
$attack_move = $this->attack($atk, $def, $move);
// 最後に使用した技を格納
$this->battle_state
->setLastMove($atk->getPosition(), $attack_move);
// バトル終了のレスポンスチェック(交代技など)
if(getResponse('end')){
break;
}
// ひんしチェック
$this->fainting = [
$atk->getPosition() => $this->checkFainting($atk),
$def->getPosition() => $this->checkFainting($def),
];
// どちらかがひんし状態なら処理終了
if($this->fainting['friend'] || $this->fainting['enemy']){
$result = false;
break;
}
} # endforeach
// 結果返却
return $result ?? true;
}
attackメソッドのreturnのタイミングでは、すべて技オブジェクトを返却しています。これで、最終技をバトル状態クラスに格納しています。
オウムがえしの特別処理
それでは、attackメソッド内でオウムがえしの判定を追加していきましょう。
攻撃トレイト(/App/Traits/Service/Battle/ServiceBattleAttackTrait.php)
/**
* 攻撃
* (攻撃→ダメージ計算→ひんし判定)
*
* @param atk_pokemon:object::Pokemon
* @param def_pokemon:object::Pokemon
* @param move:object::Move
* @return object::Move
*/
protected function attack(object $atk_pokemon, object $def_pokemon, object $move) :object
{
// 補正値の初期化
$this->m = 1;
// 行動チェック(状態異常・状態変化)
if(
!$this->checkBeforeSa($atk_pokemon) ||
!$this->checkBeforeSc($atk_pokemon)
){
// 行動失敗
return $move;
}
// 「わるあがき」の確認とPP消費処理
if(!$this->checkEnabledMove($move, $atk_pokemon)){
setMessage($atk_pokemon->getPrefixName().'は出すことのできる技がない');
}
// 「オウムがえし」の特別処理
if(get_class($move) === 'MoveMirrorMove'){
$mirror_move = $this->attackMirrorMove($atk_pokemon, $def_pokemon, $move);
if(!$mirror_move){
// 技失敗
setMessage('しかし上手く決まらなかった');
return $move;
}else{
// ミラー技をセット
$move = $mirror_move;
}
}
// チャージチェック
$charge = $move->charge($atk_pokemon);
if($charge){
// チャージターンなら行動終了
setMessage($charge);
return $move;
}
--省略
チャージチェック前にオウムがえしの特別処理を行いました。処理内容が多いのでメソッド分けをしています。
attackMirrorMoveの中身は以下の通りです。
/**
* オウムがえしの特別処理
*
* @param atk:object::Pokemon
* @param def:object::Pokemon
* @param move:object::Move
* @return mixed::Move|false
*/
private function attackMirrorMove($atk, $def, $move)
{
// オウムがえしで「チャージ状態」になっているかどうかを確認
$charge = $atk->getChargeMove();
if($charge){
return new $charge;
}
// オウムがえしで「あばれる状態」になっているかどうかを確認
$thrash = $atk->getThrashMove();
if($thrash){
return new $thrash;
}
// オウム返しの発動メッセージ
setMessage($atk->getPrefixName().'は'.$move->getName().'を使った!');
return $move->exMirrorMove($def, $this->battle_state);
}
ここで注目すべきポイントは「チャージ状態」と「あばれる状態」についてです。この2つの状態ではループが発生するため、状態変化にかかっていれば選択された技がそのまま実行されます。ただオウムがえしの判定をするだけでは「あばれる」をコピーした際に2回目の実行では「あばれる」が選択されず、永久に解除されないという状態に陥ってしまいます。チャージ技をコピーした場合も同じです。
それを回避するためにも2つ状態であれば、オウムがえしの特別処理ではなく「あばれる状態の技」または「チャージ状態の技」を抽出して返却します。
この2つの状態に当てはまらなければ、オウムがえしの処理を発生させ、コピーできた技を返却します。
では、実際にオウムがえしを使ってみましょう。
通常技に対する「オウムがえし」
「あばれる」に対する「オウムがえし」
※「あばれる」は検証用に威力を20にしています。また、ライチュウも検証用として「あばれる」しか使いません
それぞれ正常に処理が返りました。これで「オウムがえし」の実装は完了です。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは「オウムがえしの実装方法」をご紹介しました。
プログラミング学習に取り組んでいる方や、ゲームづくりに興味がある人は、ぜひ参考にしてみてくださいね。