進化直後の技習得
パーティー機能を導入に合わせて、至る場所を修正することになり、合わせて未実装だった機能を導入していきます。
見た目にはわからない部分や、とある条件が重ならなければ起こらない部分の作り込みが多いため、プレイユーザー目線からすると少し面白みが無いかも知れません。
ですが、そういった例外対応などがシステム開発には時間がかかったりするものです。
今回実装する「進化直後の技習得」も、その代表になる1つです。
初代御三家には、進化直後に技を覚えるポケモンはいませんが、進化レベルをスキップして進化させた場合であれば同じ状況を作ることが可能ですので、現状でも必要な機能となります。
技習得用サービスの作成
それでは、技習得機能を実装するためのサービスを作成しましょう。
技を習得するためのサービスは、バトル画面用に作成しました。内容は殆ど同じですが、部分的に違ってくる箇所があるため、新しく進化画面用のサービスとして用意します。
進化画面用技習得サービス(/App/Services/Evolve/LearnMoveService.php)
<?php
$root_path = __DIR__.'/../../..';
// 親クラス
require_once($root_path.'/App/Services/Service.php');
/**
* 技の習得処理
*/
class LearnMoveService extends Service
{
/**
* @var Pokemon:object
*/
protected $pokemon;
/**
* @var array
*/
protected $before_responses;
/**
* @var array
*/
protected $request;
/**
* @param parth:array
* @param order:integer
* @param before_response:array
* @param before_messages:array
* @param before_modals:array
* @param request:array
* @return void
*/
public function __construct($party, $order, $before_responses, $before_messages, $before_modals, $request)
{
$this->pokemon = $party[$order];
$this->before_responses = $this->unserializeObject($before_responses);
$this->before_messages = $before_messages;
$this->before_modals = $this->unserializeObject($before_modals);
$this->request = $request;
}
/**
* @return void
*/
public function execute()
{
// 技の置き換え
$this->replaceMove();
// レスポンスの引き継ぎ
setResponse(
$this->getUntreatedResponses($this->before_responses, $this->request['id'])
);
// メッセージの引き継ぎ
setMessage(
$this->getUntreatedResponses($this->before_messages, $this->request['id'], 'message')
);
// モーダルの引き継ぎ
setModal(
$this->getUntreatedResponses($this->before_modals, $this->request['id'], 'modal'), true
);
}
/**
* @return Pokomon:object
*/
public function getPokemon()
{
return $this->pokemon;
}
/**
* 技の置き換え
* @return void
*/
private function replaceMove()
{
// 忘れる技を取得
$forget_move = $this->pokemon
->getMove($this->request['forget']);
// 覚えさせる技を取得
$new_move = new $this->before_responses[$this->request['id']]['move'];
// 技を覚えさせる
$this->pokemon
->setMove($new_move, $this->request['forget']);
// メッセージの返却
setMessage('1 2の ……ポカン!');
setMessage($this->pokemon->getNickname().'は、'.$forget_move->getName().'の使い方をキレイに忘れた!そして......');
setMessage($this->pokemon->getNickname().'は新しく、'.$new_move->getName().'を覚えた!');
}
/**
* 未処理レスポンス・メッセージ・モーダルの引き継ぎ処理
* @param response:array
* @param msg_id:string
* @param param:string::response|message|modal
* @return array
*/
private function getUntreatedResponses(array $responses, string $msg_id, string $param='response')
{
$cnt = 1;
switch ($param) {
/********
* メッセージの引き継ぎ
*/
case 'message':
$key = array_search(
$msg_id,
array_column($responses, 1), # メッセージIDの位置は1番目
true
);
// 対象メッセージを含め3つ目までを削除
$cnt = 3;
break;
/********
* モーダルの引き継ぎ
*/
case 'modal':
$key = array_search(
$msg_id,
array_column($responses, 0), # メッセージIDの位置は0番目
true
);
break;
/********
* レスポンスの引き継ぎ
*/
default:
// メッセージIDのレスポンスが入った位置を取得
$key = array_search(
$msg_id,
array_keys($responses),
true
);
break;
}
// 未処理だけを切り出して返却
return array_splice($responses, $key + $cnt);
}
}
更に、技習得サービスへ移行させるための分岐をコントローラーへ追加しましょう。
進化画面用コントローラー(/App/Controllers/Evolve/EvolveController.php)
<?php
$root_path = __DIR__.'/../../..';
require_once($root_path.'/App/Controllers/Controller.php');
// サービス
require_once($root_path.'/App/Services/Evolve/EvolveService.php');
require_once($root_path.'/App/Services/Evolve/CancelService.php');
require_once($root_path.'/App/Services/Evolve/DefaultService.php');
require_once($root_path.'/App/Services/Evolve/LearnMoveService.php');
// トレイト
require_once($root_path.'/App/Traits/Controller/EvolveControllerTrait.php');
// 進化画面用コントローラー
class EvolveController extends Controller
{
use EvolveControllerTrait;
/**
* @return void
*/
protected $process_flg = false;
/**
* @return void
*/
public function __construct()
{
// 親コンストラクタの呼び出し
parent::__construct();
// 引き継ぎ
$this->takeOver();
// 分岐処理
$this->branch();
// 親デストラクタの呼び出し
parent::__destruct();
}
/**
* @return void
*/
public function __destruct()
{
// 次画面へ送るデータ
$_SESSION['__data']['party'] = $this->serializeObject($this->party);
$_SESSION['__data']['before_reponses'] = $this->serializeObject(getResponses());
$_SESSION['__data']['before_modals'] = $this->serializeObject(getModals());
$_SESSION['__data']['before_messages'] = getMessages();
if($this->process_flg){
$_SESSION['__data']['order'] = $this->order;
}
// 親デストラクタの呼び出し
parent::__destruct();
}
/**
* アクションに合わせた分岐
* @return void
*/
private function branch()
{
// 対象がいなければホーム画面へ移管
if(is_null($this->order)){
$this->goToHome();
}
// アクションに合わせた分岐
switch ($_POST['action'] ?? '') {
/******************************************
* 進化
*/
case 'evolve':
$service = new EvolveService($this->party, $this->order);
$service->execute();
// 新しくなったパーティーをセット
$this->party = $service->getParty();
// 処理中フラグを引き継ぎ
$this->process_flg = $service->process_flg;
break;
/******************************************
* 進化キャンセル
*/
case 'cancel':
$service = new CancelService($this->party, $this->order);
$service->execute();
// ページをリロード
$this->reload();
break;
/******************************************
* 技の習得
*/
case 'learn_move':
// サービス実行
$service = new LearnMoveService(
$this->party,
$this->order,
$_SESSION['__data']['before_reponses'],
$_SESSION['__data']['before_messages'],
$_SESSION['__data']['before_modals'],
$this->request('param')
);
$service->execute();
break;
/******************************************
* 進化確認画面
*/
default:
$service = new DefaultService($this->party, $this->order);
$service->execute();
break;
}
}
/**
* ホーム画面へ移管させる処理
* @return void
*/
protected function goToHome()
{
unset(
$_SESSION['__data']['order'],
$_SESSION['__data']['before_messages'],
$_SESSION['__data']['before_modals'],
$_SESSION['__data']['before_responses']
);
$_SESSION['__route'] = 'home';
header("Location: ./", true, 307);
}
/**
* リロード処理
* @return void
*/
protected function reload()
{
unset(
$_SESSION['__data']['order'],
$_SESSION['__data']['before_messages'],
$_SESSION['__data']['before_modals'],
$_SESSION['__data']['before_responses']
);
$_SESSION['__route'] = 'evolve';
header("Location: ./", true, 307);
}
}
技習得用にレスポンスの引き継ぎ処理用パラメーターを追加し、分岐を追加しました。進化直後は対象ポケモンに進化フラグが立っておらず、次のポケモンへ進化が移動してしまったり、技習得処理がされずホーム画面へ移管されてしまいます。それを防ぐために、セッションを通してポケモン番号を引き継いでいます。
※ヘッド部分で引き継ぎ処理をしていたセッションは、デストラクタで処理する方法に変更しました。問題なく動作しそうであればこちらで統一予定です。
処理中の判定
技を覚えようとしていれば、現在処理中ということをコントローラー内で判定できなければなりません。そのために、$process_flgというプロパティを用意しました。こちらを、進化処理内で作成して返却します。
進化処理をサービスにまとめたので、処理を見ていきましょう。
進化サービス(/App/Services/Evolve/EvolveService.php)
<?php
$root_path = __DIR__.'/../../..';
// 親クラス
require_once($root_path.'/App/Services/Service.php');
/**
* 進化用サービス
*/
class EvolveService extends Service
{
/**
* @var array
*/
private $party;
/**
* @var integer
*/
private $order;
/**
* @var boolean
*/
public $process_flg = false;
/**
* @return void
*/
public function __construct($party, $order)
{
$this->party = $party;
$this->order = $order;
}
/**
* @return void
*/
public function execute()
{
// 進化処理
$this->evolve();
}
/**
* パーティープロパティの取得
*
* @return array
*/
public function getParty()
{
return $this->party;
}
/**
* 進化
*
* @return void
*/
private function evolve()
{
// 対象ポケモンの取得
$before = $this->party[$this->order];
$after_class = $before->getAfterClass();
// 対象ポケモンが進化可能な状態か確認
if(
$before->getEvolveFlg()
&& class_exists($after_class)
){
// 現在のHPを取得
$before_hp = $before->getStats('HP');
// 進化ポケモンのインスタンスを生成
$after = new $after_class($before);
// HPの上昇値分だけ残りHPを加算(ひんし状態を除く)
if(!isset($after->sa['SaFainting'])){
$after->calRemainingHp('add', $after->getStats('HP') - $before_hp);
}
setMessage('おめでとう!'.$before->getNickName().'は'.$after->getName().'に進化した!');
// 進化後のインスタンスをパーティーにセット
$this->party[$this->order] = $after;
// 現在のレベルで習得できる技があるかチェック
if($after->getLevelMoveCount()){
$after->checkLevelMove();
$this->process_flg = true;
}
// 空メッセージのセット
setEmptyMessage();
}else{
setMessage('このポケモンは進化できません');
}
}
}
ポケモンの進化処理後、習得できる技があるかどうかを確認して、あればcheckLevelMoveを呼び出し、処理中フラグをtrueに変更しています。
これで、もし技の習得が介入した場合は処理中だということをコントローラーに伝えることができます。
ただ、連続で技を習得しようとすると進化サービスを経由しないため処理中フラグが折れてしまいます。だからといって、セッションのオーダーだけを先行させていれば複数ポケモンの進化待ち状態があれば次に進むこともありません。
それを防ぐために、ポケモン番号を抽出するためのメソッドにアクションの判定を追加します。
進化コントローラー用トレイト(/App/Traits/Controller/EvolveControllerTrait.php)
<?php
/**
* 進化コントローラー用トレイト
*/
trait EvolveControllerTrait
{
/**
* 引き継ぎ処理
* @return void
*/
protected function takeOver()
{
$this->party = $this->unserializeObject($_SESSION['__data']['party']);
$this->order = $this->selectEvolveTarget();
}
/**
* 進化ポケモンの選出
*
* @return object
*/
protected function selectEvolveTarget()
{
// 技習得処理中であればセッションの値を返却
if(
isset($_SESSION['__data']['order']) &&
($_POST['action'] ?? '') === 'learn_move'
){
return $_SESSION['__data']['order'];
}else{
$evolves = array_filter($this->party, function($pokemon){
return $pokemon->getEvolveFlg();
});
// 進化対象のポケモンがいなければ終了
if(empty($evolves)){
return null;
}else{
return array_key_first($evolves);
}
}
}
/**
* 味方ポケモン情報の取得
*
* @return object
*/
public function getPokemon()
{
return $this->party[$this->order] ?? '';
}
}
もしアクションがlearn_moveであればセッションのオーダー値を採用、そうでなければパーティー内の進化フラグが立っているポケモンから抽出するようにしています。
こうすることで、連続で技習得処理が入ったとしても、その処理を終えるまでは次のポケモンに移行したり、進化処理そのものが終了してしまうことはありません。
では、おなじみ「フシギダネ」と「カメックス」、威力445の「つるのムチ」ご協力のもと、試してみましょう。
今回はテスト用にフシギソウのレベル16で習得できる技に「かげぶんしん」と「あなをほる」を追加しました。問題なく2つの技が処理されていますね。
これで、進化直後の技習得処理が完成です。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは、進化直後の技習得処理の実装方法をご紹介しました。
システムの中では地味な部類且つイレギュラーな状況下での処理になりますが、システム開発ではこういったことが多いのも事実です。
余談ですが今回のテスト時に驚いたことは、前回に引き続きカメックスの耐久力とフシギダネの貧弱さです。当初レベル21のフシギダネとレベル25のカメックスでレベルアップ検証を行ない、つるのムチの威力を445で検証したところ、なんと高乱数1発でしか倒せないということがわかりました。2進化ポケモンの強さがよく分かる瞬間でした。
ゲーム開発に興味がある方や、プログラミング学習に取り組んでいる方は、ぜひ参考にしてみてくださいね。