1 強化概述
在遊戲中,強化模塊是我們經常需要實現的模塊,一般來說,強化可以提供裝備的等級,屬性,或者品質。強化也需要一定的條件,比如銀幣等。
2 強化規則
初始裝備的等級爲1級
每強化一次,裝備的等級提高一次
裝備被強化的最高等級爲10級
每次強化需要銀幣,且隨着等級的不斷升高,所需要的銀幣數量就增加
越高檔次的裝備,強化相同等級,需要的銀幣的數量越多
強化概率隨着等級的升高而減少
不同檔次的裝備強化到相同等級時,概率相同
加入寶石,可以增加強化的概率
使用保護道具,強化失敗後,也不損壞裝備
3 強化結果
每次強化等級提升一次,同時,屬性也有所增加,並且具有一定的概率
強化失敗,裝備摔壞
隨着屬性值的增加(所有增加的屬性值的總和),裝備的品質會發生改變。
4 強化的數據庫設計
在遊戲中,我們可以獲取到2件相同的裝備(備註:有的遊戲不允許獲取到兩件一模一樣的裝備),如果我們有2件相同的裝備,在數據庫裏面的存儲就會有兩種方式:堆疊和不堆疊。我們該如何選擇存儲方式呢?在我們的遊戲中,因爲裝備涉及到強化,也就是說,起初,我獲取到2件一模一樣的裝備 即裝備的ID相同,如果我強化成功其中的一個,那麼導致的結果是,我有了2件"不一樣"的裝備,雖然裝備的ID是一樣,但是它們的屬性可能不一樣。這樣的設定就要求我們在存儲這2件裝備時,不能堆疊,否則,我就不知道,我到底強化的是哪一個裝備。
強化系統中,設計的表: 我的裝備表,裝備配置表,強化配置表,寶石配置表,我的寶石表。
我的裝備表設計:
這是我的一個裝備的表的設計
備註: 我們的裝備的類型可能不止一種,在我們命名裝備的ID時,我們採用的方式是 裝備的類型+裝備的id 如:333001 333爲裝備的類型 001爲裝備的id
在這個表中,我們需要注意的是,equipped_by_usid 表中這個裝備是否裝配到我的某個船上,如果是0表示沒有使用,如果不爲零,則表示爲我的船的id.在需求上,我們可能需要知道這個裝備裝配到了哪個船上,還有一個需求是,我需要知道我的船裝配了那幾個裝備,所以,需要通過船的id找到他裝配了哪些裝備
cannon_num, armor_num, hp 這些屬性是如果把這個裝備裝配到船上,改變船的對應的屬性。
裝備靜態配置表:
在裝備的配置表中,hp表示是這個裝備裝配到船上後,增加船的耐久度,還有一些改變船增益的屬性,我沒有列出來。
裝備強化後,可能修改的屬性有:hp,rank , inclination, sell_price 等。
強化的配置表:
這個表中,forged_level 表示的是強化到某個等級 cost_price 表示,強化到這個等級需要的銀幣 success_odds 表示強化到這個等級的概率
cannon_num_incr_config 這個屬性的值是0:3250;1:4500;2:2250這樣的類型 表示,強化後,增加0的概率是3250/10000 增加1的概率是4500/10000。
程序設計
在裝備系統中,我們採用面向對象的面向思想設計,這是一個強化爐的對象,需要的屬性操作強化的人,本次要強化的裝備對象,是否使用保護道具,強化放入的寶石,本次強化的配置信息,強化後,更屬性的增量, 強化後的結果清單等。
在強化爐裏面,我們需要知道本次強化成功的概率,強化成功後屬性增加的範圍。執行強化操作後,增加裝備的各個屬性,增加強化記錄等各種操作。
強化流程
強化前,我們需要知道被強化的裝備對象,操作者的對象,本次強化成功的概率,強化成功裝備屬性增加的範圍,是否使用了寶石等各種信息。
選擇一個被強化的裝備後,我們需要得到強化這個裝備的各種信息。 這些操作是控制層Controller的函數
public function beforefoundryAction()
{
// 準備強化的船首像 userFhId
$userFhId = $this->getInt('uniq_id');
// 是否選擇了【聖殿祝福石】保護
$useProtection = $this->getBool('use_protection');
// 當前選中的船首像
if ($userFhId > 0) {
// 實例化一個鍛造爐
$forge = new Model_Forge();
// 設置操作者
$forge->setOwner($this->_user);
// 實例化一個船首像
if (! $item = $this->_user->item->getOneItem($userFhId)) {
throws('Invalid TargetItem');
}
// 已是頂級,無需強化
if ($item->isTopForgedLevel()) {
throws(_('該船首像已經是頂級了,無需繼續強化'));
}
// 把船首像放入爐中
$forge->setItem($item);
// 強化配置(預計強化效果)
$forgeConfig = $forge->getForgeConfig();
// 解析得到本次強化各屬性的提升範圍
$forgeIncrRanges = Model_Forge::parseForgeIncrRanges($forgeConfig);
// 本次強化價格
$forgePrice = $forgeConfig['cost_price'];
// 計算成功率(N/10000)
$successOdds = $forge->calcSuccessOdds();
// 該船首像下一級的圖片
$item['next_img_path'] = MyHelper_View::getFigureheadImg($item['img_path'], $item['forged_level'] + 1);
// 五級以上的不能使用【聖殿祝福石】
if ($item['forged_level'] > 5) {
$useProtection = false;
}
// 傳出模板變量
$this->assign('item', $item);
$this->assign('successOdds', $successOdds / 100);
$this->assign('forgePrice', $forgePrice);
$this->assign('forgeIncrRanges', $forgeIncrRanges);
}
執行強化
在執行強化前,我們需要根據已經得到的數據,對裝備的某些屬性進行修改。
/**
* 執行強化
*/
public function forgeAction()
{
$userFhId = $this->getInt('uniq_id'); // 船首像 userFhId
$jewelIds = $this->getInts('jewel_ids'); // 強化寶石 itemIds
$useProtection = $this->getBool('use_protection'); // 是否使用保護道具(即消費金塊)
// 實例化一個鍛造爐
$forge = new Model_Forge();
// 設置操作者
$forge->setOwner($this->_user);
// 實例化一個船首像
if (! $item = $this->_user->item->getOneItem($userFhId)) {
throws('Invalid TargetItem');
}
// 已是頂級,無需強化
if ($item->isTopForgedLevel()) {
throws(_('該船首像已經是頂級了,無需繼續強化'));
}
// 把船首像放入爐中
$forge->setItem($item);
// 放入保護道具
// 保護5級以下的物品,強化失敗後不被損毀
if ($useProtection) {
$forge->useProtection();
}
// 放入強化材料:寶石(提高強化成功率)
if ($jewelIds) {
$forge->addJewels($jewelIds);
}
// 開始鍛造
$forge->process();
// 獲取鍛造結果
$result = $forge->getResult();
// 強化失敗
if (! $result['forge_result']) {
if ($result['is_destroyed']) {
$message = _('很可惜,此次強化失敗,強化的船首像損壞了,不要氣餒,下次繼續努力');
} else {
$message = _('很可惜,此次強化失敗,不要氣餒,下次繼續努力');
}
$this->jsonx($message, 'success', array(
'forge_result' => $result['forge_result'],
'is_destroyed' => $result['is_destroyed']
));
// 強化成功
} else {
// 強化後的實例
$itemForged = $result['item_forged'];
$this->jsonx(_('恭喜您,船首像強化成功'), 'success', array(
'forge_result' => $result['forge_result'],
'is_top' => $itemForged->isTopForgedLevel(), // 是否已經強化到頂級
'content' => $this->render('item-forged', array(
'item' => $itemForged, // 強化後的實例
'incrs' => $result['incrs'], // 本次強化後的升級量
))
));
}
}
以上,就是面向對象的方式,實現的強化的基本步驟。 下面是Model層重要的具體方法的詳細介紹。
在Model層中,我定義了強化類需要的屬性和方法
<?php
/**
* 鍛造爐 模型(強化船首像)
*
* @author JiangJian <[email protected]>
* $Id: Forge.php 4024 2013-07-15 06:11:58Z sunli $
*
* @version 2013.05.13 備註:按產品需求要去除 quality 品質的作用
* 爲防止需求反覆,代碼暫不做修改,quality 只在內部默默更新,實際上沒什麼作用,也不體現在前端UI上
*/
class Model_Forge extends Core_Model_Abstract
{
/**
* 當前操作人
*
* @var Model_User
*/
private $_user;
/**
* 本次要強化的對象
*
* @var Model_Item_*
*/
private $_item;
/**
* 是否使用保護道具(金塊)
*
* @var array
*/
private $_useProtection = false;
/**
* 被放入爐中的寶石
*
* @var array
*/
private $_jewels = array();
/**
* 本次強化等級配置信息
*
* @var array
*/
private $_forgeConfig = array();
/**
* 強化成功後各屬性獲得的增量
*
* @var array
*/
private $_incrs = array();
/**
* 強化結果清單
*
* @var array
*/
private $_result = array(
'is_destroyed' => 0, // 原物品是否損毀
'forge_result' => 0, // 強化結果:成敗
'success_odds' => 0, // 本次成功率
'incrs' => array(), // 強化增量效果
'item_forged' => null, // 強化後的裝備實例
'quality_changed' => 0, // 是否產生質變(變色)
);
/**
* 保護道具需消費的金塊數
*
* @var int
*/
const PROTECTION_COST_GOLD = 20;
/**
* 放入要鍛造的裝備
*
* @param object Model_Item_* $item
* @return array
*/
public function setItem(Model_Item_Abstract $item)
{
if (! Model_Item::isType('figurehead', $item['type'])) {
throws(_('只有“船首像”可被強化'));
}
$this->_item = $item;
// 初始化本次強化配置信息
$this->_initForgeConfig();
}
/**
* 設置操作人
*
* @param object Model_User $item
* @return void
*/
public function setOwner(Model_User $user)
{
$this->_user = $user;
}
/**
* 設置是否使用保護道具(金塊保護)
*
* @param object Model_User $item
* @return void
*/
public function useProtection($bool = true)
{
$this->_useProtection = (bool) $bool;
}
/**
* 放入強化材料:寶石(提高強化成功率)
*
* @param array $jewelIds
* @return void
*/
public function addJewels(array $jewelIds)
{
}
private function _consumeJewels()
{
}
/**
* 初始化當前強化配置
*
* @return void
*/
private function _initForgeConfig()
{
// 獲取強化等級配置信息
if (! $this->_forgeConfig = self::getExpectedForgedEffect($this->_item)) {
throws(_('該裝備強化配置信息缺失,無法進行強化'));
}
}
public function getForgeConfig()
{
}
/**
* 鍛造前檢測
*
* @return void
*/
private function _initCheck()
{
// 檢測銀幣餘額
// 檢測金塊餘額
}
/**
* 鍛造進行(外觀模式)
*
* @return bool
*/
public function process()
{
// 前提檢測
$this->_initCheck();
// 鍛造進行
$forgeResult = $this->_process();
// 每日任務行爲統計 累計強化船首像次數(不論成功與否)
$this->_user->dailyTask->process(506012);
// 保存鍛造日誌
$this->_saveLog();
return $forgeResult;
}
/**
* 鍛造進行核心流程
*
* @return bool
*/
private function _process()
{
// 消費銀幣
$this->_user->base->consumeSilver($this->_forgeConfig['cost_price']);
// 使用保護道具(即扣金塊)
if ($this->_useProtection) {
$this->_user->base->consumeGold(self::PROTECTION_COST_GOLD, 0, '強化時使用了【聖殿祝福石】');
}
// 消耗強化寶石
$this->_consumeJewels();
// 計算成功率(N/10000)
$successOdds = $this->calcSuccessOdds();
// 強化結果 true/false
$forgeResult = Helper_Probability::isHit($successOdds);
// 強化失敗
if (! $forgeResult) {
// 保護石(聖殿祝福石)可保護5級(含)及以下的船首像失敗後不被損毀
if (! ($this->_useProtection && $this->_item['forged_level'] <= 5)) {
// 如果該船首像已裝配到船上,則先從船上卸載
// 特別注意:爲了提高UE,卸載時不檢測倉容,即允許倉容超出
if ($this->_item['equipped_by_usid']) {
$this->_user->ship->unloadItem($this->_item['equipped_by_usid'], 'figurehead', false);
}
// 原物品損毀
$this->_user->item->remove($this->_item['uniq_id']);
// 記錄日誌
$this->_result['is_destroyed'] = 1;
}
// 強化成功
} else {
// 強化成功後各屬性的增量
$this->_getIncrement();
// 保存強化成果
$this->_saveIncrement();
// 檢測是否發生了變色(品質提升、質變)
$this->_qualityChanged();
// 增加船首像鍛造成功次數(統計)
$this->_user->stats->addTotalItemForgeSuccessTimes();
// 每日任務行爲統計 累計強化成功次數
// 2013.06.28 按陸超的新版每日任務需求,暫時去除該行爲埋點
// $this->_user->dailyTask->process(504008);
// 每日任務行爲統計 把一個船首像強化到指定等級
// 2013.06.28 按陸超的新版每日任務需求,暫時去除該行爲埋點
// $this->_user->dailyTask->process(504035, 1, $this->_forgeConfig['forged_level']);
// 強化船首像達到5級,既可獲取成就熠熠生輝
if ($this->_forgeConfig['forged_level'] == 5) {
$this->_user->achievement->achieve(721051);
}
// 強化船首像達到10級,既可獲取成就神乎其技
if ($this->_forgeConfig['forged_level'] == 10) {
$this->_user->achievement->achieve(721052);
}
}
// 記錄日誌
$this->_result['success_odds'] = $successOdds;
$this->_result['forge_result'] = $forgeResult;
return $forgeResult;
}
/**
* 計算成功率(累計寶石的加成)
*
* @return int
*/
public function calcSuccessOdds()
{
}
/**
* 強化成功後各屬性獲得的增量
*
* @return void
*/
private function _getIncrement()
{
}
/**
* 保存強化成果
*
* @return bool
*/
private function _saveIncrement()
{
}
/**
* 質變檢測和處理(顏色變化)
* 如果發生質變,則更新 quality 字段
*
* @return void
*/
private function _qualityChanged()
{
}
/**
* 靜態檢測函數
* 判斷是否發生顏色變化(質變)
*
* @param array $itemForged 強化後的物品
* @param array $itemOrginal 強化前的物品
* @return int $quality 1/2/3/4/false (false 表示未發生改變)
*/
public static function isQualityChanged($itemForged, $itemOrginal)
{
}
/**
* 保存本次強化記錄
*
* @return bool
*/
private function _saveLog()
{
}
/**
* 原物品是否損壞(強化失敗後)
*
* @return bool
*/
public function isDestroyed()
{
}
/**
* 返回本次強化詳細結果
*
* @return array
*/
public function getResult()
{
}
/**
* 本次強化使用寶石情況
*
* @return string json
*/
public function _getJewelsLog()
{
$return = '';
if ($this->_jewels) {
foreach ($this->_jewels as $value) {
$jewel = $value['object'];
$jewels[] = array(
'item_id' => $jewel['id'],
'amount' => $value['amount'],
'success_odds_incr' => $jewel['success_odds'] * $value['amount'],
);
}
$return = json_encode($jewels); // 使用json保存數據的結果
}
return $return;
}
/**
* 獲取本次使用的強化配置
*
* @param int $rank 當前裝備檔次
* @param int $inclination 當前裝備偏重方向
* @param int $forgedLevel 強化等級
* @return array
*/
public static function getStaticForgeConfig($rank, $inclination, $forgedLevel)
{
}
/**
* 返回某裝備強化後的預計效果
* 通俗說:強化後,裝備會有哪些改進
*
* @param Model_Item_Abstract $item
* @return array
*/
public static function getExpectedForgedEffect(Model_Item_Abstract $item)
{
// 本次準備強化到哪個等級
$nextForgedLevel = $item['forged_level'] + 1;
// 獲取強化等級配置信息
return self::getStaticForgeConfig($item['rank'], $item['inclination'], $nextForgedLevel);
}
/**
* 解析得到本次強化各屬性的提升範圍
*
* @param array $forgeConfig
* @return array
*/
public static function parseForgeIncrRanges(array $forgeConfig)
{
}
}
在這個強化系統的設計中,我們採用的面向對象的方式,提供的代碼的重用率,外觀模式的使用,保證了代碼的整潔可讀性。
強化容易出現bug,請特別重點留意。
在我們設計的遊戲中,我們需要冗餘一些信息在玩家的user表中,因爲遊戲中,需要經常讀取玩家的戰鬥力,耐久度等各種信息,例如艦隊的戰鬥力,耐久度。艦隊是由出航的船組成的,如果出航的船的戰鬥力發現變化,需要修改冗餘在user表中的冗餘信息。
這裏,就有一個問題,如果裝備沒有被使用,那麼強化後,不需要更新某些冗餘字段。
如果裝備被裝配在某個船上,那麼就需要更新這個船的某些信息,如,戰鬥力,耐久度
如果這個船時出航狀態,也就是說,這個船在艦隊中,那麼需要更新user表中的冗餘字段。