モンスターボールとは
ポケモンのゲームにとっての楽しみの1つである「バトル」はある程度実装できてきましたが、もう一つ無くてはならない楽しみがあります。それが「ポケモンを集める」というコレクター要素です。
そして、ポケモンを捕まえるために欠かせない道具の1つが「モンスターボール」です。
モンスターボール
野生のポケモンに 投げて 捕まえるための ボール。カプセル式に なっている。
ポケモンを捕まえるための道具は、モンスターボールの他にもいくつか種類があります。初代では「モンスターボール」「スーパーボール」「ハイパーボール」「マスターボール」の4種類あり、それぞれ性能が異なります。
その辺りも含めて、ポケモンを捕まえるために必要な値や計算式を見ていきましょう。
捕獲補正率について
ボールごとには、捕獲補正率という性能を表す数値が割り当てられています。初代で登場する4種類は以下の通りです。
モンスターボール:1
スーパーボール :1.5
ハイパーボール :2
マスターボール :なし
マスターボールに捕獲補正率が割り当てられていないのは、他のボールと異なり100%捕まえられるボールだからです。モンスターボールの1(倍)が標準の数値で、それぞれの値はポケモンの「捕捉率」に乗算されます。
※第2世代以降で登場するボールでは、加算(減算)による補正を用いるボールも存在しています
では、この補正率を性能値として、モンスターボールのクラスを作成してみましょう。
モンスターボール(/Classes/Item/ItemPokeBall.php)
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Item.php');
// モンスターボール
class ItemPokeBall extends Item
{
/**
* 正式名称
* @var string
*/
protected $name = 'モンスターボール';
/**
* 説明文
* @var string
*/
protected $description = '野生のポケモンに投げて捕まえるためのボール。カプセル式になっている。';
/**
* カテゴリ
* @var string::general|health|ball|important|machine
*/
protected $category = 'ball';
/**
* 最大所有数
* @var integer
*/
protected $max = 99;
/**
* 買値
* @var integer
*/
protected $bid_price = 200;
/**
* 売値
* @var integer
*/
protected $sell_price = 100;
/**
* 対象
* @var string::friend|player|enemy
*/
protected $target = 'enemy';
/**
* 使用できるタイミング
* @var array
*/
protected $timing = ['battle'];
/**
* 捕獲補正率
* @var float
*/
protected $performance = 1.0;
}
対象は敵(enemy)、使用タイミングはバトルのみ(battle)、捕獲補正率(performance)を1.0で設定しました。
こちらのクラスをベースにしながら、捕獲判定を実装していきます。
捕獲判定の計算式
ポケモンを捕まえるための確率算出式は世代ごとに少しずつ異なっています。今回はwikiを参考にしつつ、記載されている第6世代の計算式を用いて算出します。
捕獲処理(ポケモンwiki)
捕獲処理の計算式
- B = (((最大HP × 3 – 現在HP × 2) × 捕捉率 × 捕獲補正) ÷ (最大HP × 3)) × 状態補正
- G = 65536÷(255÷B)^0.1875
- 揺れ判定(1回当たり) = (1〜65535の乱数) <= G
- 4回の揺れ判定をクリアしたら捕獲
状態補正は以下の通りです
通常 → 1
ねむり、こおり → 2.5
その他状態以上 → 1.5
上記式だけだとややこしく見えますが、基本的には必要数値を当てはめていくだけになります。
Bの値を算出してからGの値を算出、Gの値が乱数以下であるか比較するので、Gが65535に近ければ近いほど、捕まえる確率が高くなるということです。マスターボールを使えば、Gに65535が強制的にセットされると考えると、わかりやすいでしょう。
1揺れ毎に判定が行われるため、もし高確率が算出されたとしても、4回のうち1度でも乱数がGの値を上回ってしまえば捕獲失敗となります。
アイテム使用の分岐
モンスターボールが追加されたことにより、アイテム使用の分岐が必要になります。バトル中は使用可能、それ以外では使用不可とするためにjsによる選択時の分岐を追加しておきましょう。
アイテムモーダル(/Resources/Common/Modals/Item/item.php)
<!-- Modal -->
<?php $bag = player()->getBag(); ?>
<div class="modal fade" id="item-modal" tabindex="-1" role="dialog" aria-labelledby="item-modal-title" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="item-modal-title">
<img src="/Assets/img/icon/bag.png" alt="どうぐ" />
どうぐ
</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body my-2">
<?php # タブ ?>
<nav class="nav nav-pills nav-justified btn-group mb-3" id="item-modal-tab">
<?php $cnt = 0; ?>
<?php foreach($bag as $category => $items): ?>
<a class="btn btn-outline-secondary nav-item nav-link <?php if(!$cnt) echo 'active'; ?>" id="item-modal-<?=$category?>-tab" data-toggle="tab" href="#item-modal-<?=$category?>" role="tab" aria-controls="item-modal-<?=$category?>" aria-selected="true">
<img src="/Assets/img/item/category/<?=$category?>.png" alt="<?=transJp($category, 'item');?>">
</a>
<?php $cnt++; ?>
<?php endforeach; ?>
</nav>
<?php # コンテンツ ?>
<div class="tab-content" id="item-modal-tab-content">
<?php $cnt = 0; ?>
<?php foreach($bag as $category => $items): ?>
<div class="tab-pane fade show <?php if(!$cnt) echo 'active'; ?>" id="item-modal-<?=$category?>" role="tabpanel" aria-labelledby="item-modal-<?=$category?>">
<h6 class="font-weight-bolder mb-2"><?=transJp($category, 'item');?></h6>
<div class="bg-light p-3 mb-2 overflow-auto" style="height:120px;">
<div class="d-flex justify-content-between">
<h6 id="item-modal-<?=$category?>-name" class="font-weight-bolder mb-0 d-flex align-self-center"></h6>
<div class="item-action-button-group" data-category="<?=$category?>">
<?php # プレイヤー対象のアイテム使用 ?>
<form method="post" data-button="use" data-item_target="player" style="display:none">
<?php input_token(); ?>
<input type="hidden" name="action" value="item">
<input type="hidden" name="order">
<button type="submit" class="btn btn-sm btn-primary">使う</button>
</form>
<?php # 敵ポケモン対象のアイテム使用 ?>
<form method="post" data-button="use" data-item_target="enemy" style="display:none">
<?php input_token(); ?>
<input type="hidden" name="action" value="item">
<input type="hidden" name="order">
<button type="submit" class="btn btn-sm btn-primary">使う</button>
</form>
<?php # 味方ポケモン対象のアイテム使用 ?>
<button type="button" class="btn btn-sm btn-primary" data-button="use" data-item_target="friend" data-toggle="modal" data-target="#item-use-friend-modal" style="display:none">使う</button>
<?php # 捨てる ?>
<?php if(getPageName() !== 'battle'): ?>
<button type="button" class="btn btn-sm btn-danger" data-button="trash" data-toggle="modal" data-target="#item-trash-modal" style="display:none">捨てる</button>
<?php endif; ?>
</div>
</div>
<hr>
<p class="mb-0 small" id="item-modal-<?=$category?>-description"></p>
</div>
<div class="bg-light p-3 overflow-auto" style="height:300px;">
<?php if(empty($items)): ?>
<p class="mb-0">1つも持っていません</p>
<?php else: ?>
<table class="table table-sm table-hover table-selected table-bordered bg-white mb-0">
<tbody>
<?php foreach($items as $item): ?>
<?php # 利用不可(バトル中のみ) ?>
<?php if(!in_array('battle', $item['item']->getTiming(), true) ): ?>
<?php # 利用不可 ?>
<tr class="text-muted">
<td class="w-75">
<img src="/Assets/img/item/class/<?=get_class($item['item'])?>.png" alt="<?=$item['item']->getName()?>" class="mr-1" />
<?=$item['item']->getName()?>
<td class="w-25 text-right"><?=$item['count']?> 個</td>
</td>
</tr>
<?php else: ?>
<?php # 利用可能 ?>
<tr data-description="<?=$item['item']->getDescription()?>"
data-name="<?=$item['item']->getName()?>"
data-category="<?=$category?>"
data-target="<?=$item['item']->getTarget()?>"
data-use="<?=var_export($item['item']->allowUsed(getPageName()), true)?>"
data-trash="<?=var_export($item['item']->allowTrashed(), true)?>"
data-owned="<?=$item['count']?>"
data-order="<?=$item['order']?>"
class="item-row">
<td class="w-75">
<img src="/Assets/img/item/class/<?=get_class($item['item'])?>.png" alt="<?=$item['item']->getName()?>" class="mr-1" />
<?=$item['item']->getName()?>
</td>
<td class="w-25 text-right"><?=$item['count']?> 個</td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<?php $cnt++; ?>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
</div>
こちらはホーム画面とバトル画面で使用できるように共通化しました。jsによる制御は以下の通りです。
アイテム用js(/Public/Assets/js/Common/item.js)
/**
* アイテム詳細の表示
* @function on beforeunload
* @return void
*/
var showItemModalDetailInit = function(){
$('.item-row').on('click', function(){
var category = $(this).data('category');
var name = $(this).data('name');
var description = $(this).data('description');
var target = $(this).data('target');
var use = $(this).data('use');
var trash = $(this).data('trash');
var order = $(this).data('order');
// 操作ボタンを非表示
var button_group = $('.item-action-button-group[data-category="' + category + '"]');
button_group.find('[data-button="*"]').hide();
// 詳細に表示
$('#item-modal-' + category + '-name').text(name);
$('#item-modal-' + category + '-description').text(description);
// 「使う」ボタンを表示
if(use){
// targetに合わせた使うボタン(フォーム)を表示
var use_btn = button_group.find('[data-button="use"][data-item_target="' + target + '"]')
use_btn.show();
// もしアイテムの対象がenemyまたはplayerの場合はorderをセット
if(target === 'enemy' || target === 'player'){
use_btn.find('[name="order"]').val(order);
}
}
// 「捨てる」ボタンを表示
if(trash){
button_group.find('button[data-button="trash"]')
.show();
}
});
}
モンスターボールを使う際は、ターゲットを指定する必要がありませんので、フォーム自体はHTMLで生成しておき、選択時に必要な値をセットするという仕組みです。
送信する値は、アイテムクラスなど直接指定で飛ばさずに、アイテム番号(添字)を送りバックエンド側で制御していきます。ノードは変更されてしまう可能性があるため、最低限ですがそれらも想定した上で処理を組んでいます。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは「モンスターボール」実装段階の説明と、その詳細についてご紹介しました。
次は「モンスターボールの使用」から捕獲処理について説明予定ですので、乞うご期待ください!