Difficulty scaling/zh: Difference between revisions
(Created page with "註記:從 NPC 那裡拿到的武器(阿卡莱古代研究所、英傑武器)不在上面的列表裡,從而不升級。") |
No edit summary |
||
(111 intermediate revisions by 3 users not shown) | |||
Line 27: | Line 27: | ||
== 阻止升級的情況 == | == 阻止升級的情況 == | ||
下面這個情況會導致敵人跟武器維持原樣。 | |||
* WorldMgr::sInstance->stageType == 1 (Open World stage) | * WorldMgr::sInstance->stageType == 1 (Open World stage) | ||
* 且 WorldMgr::sInstance->isAocField (current map is Trial of the Sword) | * 且 WorldMgr::sInstance->isAocField (current map is Trial of the Sword) | ||
* 且 WorldMgr::sInstance->disableScaling (set to true when entering Trial of the Sword) | * 且 WorldMgr::sInstance->disableScaling (set to true when entering Trial of the Sword) | ||
總而言之,遊戲不會升級劍之考驗裡的武器和敵人。 | |||
另一種會跳過升級的情況是 [[map area]] 等於 28, 也就是林克位在 "HateruSea",野外的考驗的塞哈特諾島。 | 另一種會跳過升級的情況是 [[map area]] 等於 28, 也就是林克位在 "HateruSea",野外的考驗的塞哈特諾島。 | ||
Line 42: | Line 45: | ||
* '''敵人手上的武器''': The flag <code>{MapName}_WeaponDrop_{ID}</code> is false, '''and''' [the actor property 'LevelSensorMode' is higher than 1 ''or'' the enemy is a Guardian Scout ('Enemy_Guardian_Mini' 廟裡的守護者)]. | * '''敵人手上的武器''': The flag <code>{MapName}_WeaponDrop_{ID}</code> is false, '''and''' [the actor property 'LevelSensorMode' is higher than 1 ''or'' the enemy is a Guardian Scout ('Enemy_Guardian_Mini' 廟裡的守護者)]. | ||
註記:從 NPC | 註記:從 NPC 那裡拿到的武器(阿卡莱古代研究所、英傑武器、怪物商人)不在上面的列表裡,從而不升級。 | ||
== 武器加成 == | |||
武器的加成有三個要素。 | |||
一是武器本身會被更新成更好的武器(騎士之劍 -> 王族之劍); | |||
二是武器的攻擊、防禦、耐久等數值擇一增加; | |||
三是增加多少。 | |||
藍標、白標(Blue/White)是比較低階的升級,標記成「某某某提升」。黃標(Yellow)是高階的升級,標記成「某某某大提升」。以攻擊力為例,藍標可能會增加 10~20 的攻擊力(攻擊力提升 +14),而黃標會增加 20~30(攻擊力大提升 +24)。 | |||
=== | === 加成的方向 === | ||
要往哪個方向加成(加攻擊還是加耐久?還是別的?)是隨機的, 每個可能的方向有同樣的機率被選到。 | |||
{|class="wikitable" | {|class="wikitable" | ||
! | ! 加成的方向 !! Available in bonus tiers | ||
|- | |- | ||
|- | |- | ||
| Attack up || Blue/White and Yellow | | Attack up 攻擊力 || Blue/White and Yellow | ||
|- | |- | ||
| Durability up || Blue/White and Yellow | | Durability up 耐久度提升 || Blue/White and Yellow | ||
|- | |- | ||
| Long throw || Yellow | | Long throw 遠距離投擲 || Yellow | ||
|- | |- | ||
| Multi-shot burst (bows) || Yellow | | Multi-shot burst (bows) 多發弓發數增加 || Yellow | ||
|- | |- | ||
| Quick shot (bows) || Yellow | | Quick shot (bows) 速射 || Yellow | ||
|- | |- | ||
| AddSurfMaster || Yellow | | AddSurfMaster || Yellow | ||
|- | |- | ||
| Shield guard up || Blue/White and Yellow | | Shield guard up 防禦力 || Blue/White and Yellow | ||
|- | |- | ||
| Critical Hit || Blue/White | | Critical Hit 最後爆擊 || Blue/White | ||
|- | |- | ||
| ZoomRapid || Yellow | | ZoomRapid || Yellow | ||
|} | |} | ||
需注意的是: | |||
* "Critical Hit" | * 最後爆擊 "Critical Hit" 只可能在 weaponCommonSharpWeaponAddCrit 是 true 且 加成的 tier 程度是 White/Blue (白色或藍色的「提升」而不是黃色的「大提升」)時出現。 但是隨著經驗值的提升,所有武器的加成都會漸漸變成 Yellow (黃色的「大提升」),所以「最後爆擊」在遊戲後期就不會再出現。 | ||
* AddSurfMaster is a bonus that only applies to shields and gives them a lower friction for shield surfing. That bonus type is unused in the game. | * AddSurfMaster is a bonus that only applies to shields and gives them a lower friction for shield surfing. That bonus type is unused in the game. | ||
=== | === 加成的程度 === | ||
武器加成多少(如攻擊力應當提升多少)的數值來源於這個表 [[ActorParam/GeneralParamList]] (with a copy of the information in [[ActorInfoData]]). Valid ranges and bonuses for each weapon are configured in the WeaponCommon section. | |||
==== amiibo ==== | ==== amiibo ==== | ||
{|class="wikitable" | {|class="wikitable" | ||
! | ! 加成的方向 !! 加成的程度 | ||
|- | |- | ||
| None || - | | None 不加成 || - | ||
|- | |- | ||
| Attack up || addAtkMax | | Attack up 攻擊力 || addAtkMax | ||
|- | |- | ||
| Durability up || addLifeMax | | Durability up 耐久度提升 || addLifeMax | ||
|- | |- | ||
| Long throw || addThrowMax | | Long throw 遠距離投擲 || addThrowMax | ||
|- | |- | ||
| Multi-shot burst (bows) || 5-shot burst | | Multi-shot burst (bows) 多發弓發數增加 || 5-shot burst 五連發 | ||
|- | |- | ||
| Quick shot (bows) || addRapidFireMin | | Quick shot (bows) 速射 || addRapidFireMin | ||
|- | |- | ||
| AddSurfMaster || [[bgparamlist#Global|GlobalParameter]]::shieldSurfMasterFrictionRatio | | AddSurfMaster || [[bgparamlist#Global|GlobalParameter]]::shieldSurfMasterFrictionRatio | ||
|- | |- | ||
| Shield guard up || addGuardMax | | Shield guard up 防禦力 || addGuardMax | ||
|} | |} | ||
這段的意思是 amiibo 武器的加成都會是固定的值。譬如說,如果遊戲決定增加攻擊力,而且這個武器的攻擊力最多 +20,那就會 +20。 | |||
==== | ==== 非 amiibo ==== | ||
{|class="wikitable" | {|class="wikitable" | ||
! | ! 加成的方向 !! 加成的程度 | ||
|- | |- | ||
| None || - | | None 不加成 || - | ||
|- | |- | ||
| Attack up || | | Attack up 攻擊力 || addAtkMin 到 addAtkMax 之間的隨機數字 | ||
|- | |- | ||
| Durability up || | | Durability up 耐久度提升 || addLifeMin 到 addLifeMax 之間的隨機數字 | ||
|- | |- | ||
| Long throw || | | Long throw 遠距離投擲 || addThrowMin 到 addThrowMax 之間的隨機數字 | ||
|- | |- | ||
| Multi-shot burst (bows) || 5-shot burst | | Multi-shot burst (bows) 多發弓發數增加 || 5-shot burst 五連發 | ||
|- | |- | ||
| Quick shot (bows) || | | Quick shot (bows) 速射 || addRapidFireMin 到 addRapidFireMax 之間的隨機數字 | ||
|- | |- | ||
| AddSurfMaster || [[bgparamlist#Global|GlobalParameter]]::shieldSurfMasterFrictionRatio | | AddSurfMaster || [[bgparamlist#Global|GlobalParameter]]::shieldSurfMasterFrictionRatio | ||
|- | |- | ||
| Shield guard up || | | Shield guard up 防禦力 || addGuardMin 到 addGuardMax 之間的隨機數字 | ||
|} | |} | ||
這段的意思是非 amiibo 武器的加成都是隨機的值。譬如說,如果遊戲決定增加攻擊力,而且這個武器的攻擊力從 +10 到 +20 都有可能,那最後生成的武器就會是 10 到 20 之間的隨機數字(應該是均勻分布)。 | |||
== | == 敵人 == | ||
遊戲在載入敵人時會根據一定的規則升級敵人。 | |||
不過 'LevelSensorMode' < 1 的時候所有敵人(跟武器)都不會升級。 | |||
註記:有些敵人本身是無法升級的(例如帶冰、火、電的蜥蜴), 這僅僅是代表他們自身就已經是最高級了。 而他們手上的武器只要滿足下列條件就可以升級: | |||
* 'LevelSensorMode' | * 'LevelSensorMode' 非零且經驗值足夠高 | ||
* | * 或是某個 flag 被強制寫入應該升級 the modifier tier is overridden using 'SharpWeaponJudgeType'. | ||
[1.3.0] 版本加入了大師模式。大師模式裡'''所有'''敵人都會先驗地提高一級。這個部分跟 'LevelSensorMode' 造成的升級互不衝突而且可以疊加。更精確的控制是由下面這些參數決定的 | |||
[1.3.0] | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! 參數 | ||
! | ! 預設值 | ||
! Description | ! Description | ||
|- | |- | ||
| IsHardModeActor | | IsHardModeActor | ||
| false | | false | ||
| | | 是否只在大師模式出現 | ||
|- | |- | ||
| DisableRankUpForHardMode | | DisableRankUpForHardMode | ||
| false | | false | ||
| | | 在大師模式裡是否避免升級 | ||
|} | |} | ||
在大師模式裡,某些敵人的 IsHardModeActor、DisableRankUpForHardMode、LevelSensorMode這三個參數會同時被遊戲設計師手動調整。調整的目的是讓大師模式的後期還是會存在低等級的怪物(例如台地南邊有個紅色小波)。 | |||
== | == 屬性 == | ||
=== <code>LevelSensorMode</code> === | === <code>LevelSensorMode</code> === | ||
這個 [[actor]] 屬性決定了一個武器或敵人會不會升級,一個敵人的 LevelSensorMode 屬性同時控制了它手上的武器升不升級。 | |||
不過西諾克斯的 LevelSensorMode 屬性並不控制他脖子上的武器如何升級(到頭來是脖子上而不是手上)。總之,西諾克斯用的是另一個機制來決定脖子上的武器如何升級,而且這個機制可以繞過 'LevelSensorMode'。 | |||
=== <code>SharpWeaponJudgeType</code> === | === <code>SharpWeaponJudgeType</code> === | ||
這個屬性決定某個敵人或武器「至少」會被加成多少。 | |||
(意即:就算經驗值不夠還是會加成。) | |||
在 [[Map unit|map units]] 這類的地方,下面這些值記載了遊戲打算如何加成武器。 | |||
{| class="wikitable" | {| class="wikitable" | ||
! | ! 值 | ||
! Description | ! Description | ||
|- | |- | ||
| 0 | | 0 | ||
| '''None''': | | '''None''': 不加成 | ||
|- | |- | ||
| 1 | | 1 | ||
| '''RandomBlue''': | | '''RandomBlue''': 要麼是藍標以上的加成,要麼不加成,隨機決定。 (with <code>weaponCommonSharpWeaponPer</code> being the probability). | ||
|- | |- | ||
| 2 | | 2 | ||
| '''Blue''': | | '''Blue''': 至少藍/白標,按經驗值往上加。 | ||
|- | |- | ||
| 3 | | 3 | ||
| '''Yellow''': | | '''Yellow''': 至少黃標。 | ||
|- | |- | ||
| 4 | | 4 | ||
| '''NoneForced''' ( | | '''NoneForced''' (限於寶箱): Weapon will ''never'' spawn with any modifiers. 寶箱的這個屬性會抹去其他一切升級。 | ||
|} | |} | ||
如果 [[Difficulty scaling#LevelSensorMode|scaling]] 處在打開狀態的話,武器有可能因為經驗值足夠的關係被升到更高級。 | |||
經驗值不夠則維持原升級(不會降級)。 | |||
換句話說,就算是設成 0 ('None') 的武器也不是永遠不升級了, | |||
而是遊戲開發者有意給玩家最低等級的武器。 | |||
只要玩家的經驗值高過某個門檻,就會得到 Blue 藍標加成,再高一點 Yellow 黃標加成。 | |||
== | == 升級的計算過程 == | ||
=== <code>Ecosystem::LevelSensor::loadByml</code> === | === <code>Ecosystem::LevelSensor::loadByml</code> === | ||
這個函數會被 <code>Ecosystem::init</code> 呼叫, | |||
後者是被 <code>ksys::InitializeApp</code> 呼叫。 | |||
它的功用是載入 byml 檔案 Ecosystem/[[LevelSensor.byml]]。 | |||
=== <code>Ecosystem::LevelSensor::calculatePoints</code> === | === <code>Ecosystem::LevelSensor::calculatePoints</code> === | ||
每當遊戲生成 actors 時,[[PlacementMgr]] 會呼叫這個函數。(actor 可以是武器或是敵人) | |||
它會用各種表格跟各種經驗值計算武器或敵人要升級多少。 | |||
遊戲到目前為止都只用殺敵數 <code>Defeated_%s_Num</code> 來計算經驗值並以此推測升級程度,但是從程式設計的角度來說可以用別的東西來計算經驗值。 | |||
有趣的是,遊戲利用殺敵數計算經驗值時,會把一個「原始經驗值」轉化成「用在武器升級的經驗值」跟「用在敵人升級的經驗值」。這樣設計師就可以在後期微調「原始」轉化到「武器」的匯率;「敵人」亦同。 | |||
<source lang="c++"> | <source lang="c++"> | ||
float points = 0.0; | float points = 0.0; | ||
for (kill_flag : this->byml["flag"]) | for (kill_flag : this->byml["flag"]) // 枚舉所有敵人 | ||
int kill_count = GameData::getIntegerFlag(kill_flag["name"]); | |||
points += kill_count * kill_flag["point"]; // 殺敵數 * 此敵人的分數 | |||
this->points = points; | this->points = points; // 原始經驗值 | ||
this->weapon_points = points * this->byml["setting"].Level2WeaponPower; | this->weapon_points = points * this->byml["setting"].Level2WeaponPower; // 換算成武器用分數 | ||
this->enemy_points = points * this->byml["setting"].Level2EnemyPower; | this->enemy_points = points * this->byml["setting"].Level2EnemyPower; // 換算成敵人用分數 | ||
</source> | </source> | ||
就我們能看到的部分,任天堂從來沒有調整過這兩個數值。 | |||
=== <code>Ecosystem::LevelSensor::scaleWeapon</code> === | === <code>Ecosystem::LevelSensor::scaleWeapon</code> === | ||
它負責處理武器的升級。有三個東西會呼叫它:寶箱、敵人{{Check}}、 <code>Ecosystem::LevelSensor::scaleActor</code>。 | |||
呼叫時給定一個武器的名字、預設的加成(至少多少)、跟目前玩家的經驗值。然後這個函數會回傳一個武器名稱(可能跟給定的不一樣),其中包括應當使用何種加成。 | |||
如果函數找半天找不到滿足給定的條件武器、加成,遊戲就會用預設的武器、預設的加成。(程式碼裡最後的 return false 會處理所有例外、失敗的情形。) | |||
Pseudocode (1.0.0): | Pseudocode (1.0.0): | ||
<source lang="c++"> | <source lang="c++"> | ||
bool Ecosystem::LevelSensor::scaleWeapon(const sead::SafeString& weapon_to_look_up, | bool Ecosystem::LevelSensor::scaleWeapon(const sead::SafeString& weapon_to_look_up, // 基礎武器 | ||
WeaponModifier required_modifier, // 預設至少加這麼多 | |||
const char** weapon_to_use_name, // 回傳的武器寫在這裡 | |||
WeaponModifier* modifier_to_use, // 回傳的加成寫在這裡 | |||
void* unknown) | void* unknown) | ||
{ | { | ||
// some checks using 'unknown' here which seems to be a pointer to the actor | // some checks using 'unknown' here which seems to be a pointer to the actor | ||
for (weapon_table : this->byml["weapon"]) { | for (weapon_table : this->byml["weapon"]) { | ||
// find the first weapon entry for which the player has enough points | // find the first weapon entry for which the player has enough points | ||
// with the specified name and modifier | // with the specified name and modifier | ||
// 不同的武器系列儲存在不同的 table 裡,要枚舉 table 再枚舉 table 裡的細項 | |||
i = -1; // 應該是 index,但是 -1 代表尚未找到 | |||
for (j = 0; j < weapon_table["actors"].size; ++j) { // 枚舉含有加成訊息的武器 | |||
entry = weapon_table["actors"][j]; | |||
float points_for_next_transition = entry["value"]; // 這個加成過的武器所需的經驗值 | |||
if (this->weapon_points > points_for_next_transition && // 你的經驗值夠 | |||
weapon_to_look_up == entry["name"] && // 武器的名字是對的 | |||
convert_to_modifier(entry["plus"]) == required_modifier) { // 加成的方向跟程度是對的 | |||
i = j; | |||
break; // 找到一個了(但是可能還有更多) | |||
} | |||
} | } | ||
if (i == -1) | if (i == -1) | ||
continue; // 找不到就試下一個 | |||
do { // 已知找到一個的情況下,試圖找更多 | |||
entry = weapon_table["actors"][i]; | |||
// not_rank_up means there is no link between weapons; | // not_rank_up means there is no link between weapons; | ||
// this table is just used to look up modifiers. | // this table is just used to look up modifiers. | ||
Line 274: | Line 283: | ||
// or until we reach a modifier that requires more points. | // or until we reach a modifier that requires more points. | ||
if (weapon_table["not_rank_up"] && entry["name"] != weapon_to_look_up) | if (weapon_table["not_rank_up"] && entry["name"] != weapon_to_look_up) | ||
break; // ???這個 table 裡的武器不能升級,或是 entry 是 null (boundary check) | |||
// otherwise, just go down the list until we reach the end or a weapon which | // otherwise, just go down the list until we reach the end or a weapon which | ||
// requires more points. this will possibly upgrade the weapon (e.g. Knight -> Royal). | // requires more points. this will possibly upgrade the weapon (e.g. Knight -> Royal). | ||
if (this->weapon_points <= entry["value"]) | if (this->weapon_points <= entry["value"]) | ||
break; // 枚舉到經驗值最高又符合條件的才停 | |||
++i; | ++i; | ||
} while (i < weapon_table["actors"].size); | } while (i < weapon_table["actors"].size); | ||
*weapon_to_use_name = entry["name"]; | *weapon_to_use_name = entry["name"]; | ||
*modifier_to_use = convert_to_modifier(entry["plus"]); | *modifier_to_use = convert_to_modifier(entry["plus"]); | ||
return true; // 有找到 | |||
} | } | ||
return false; // cannot scale up | return false; // cannot scale up // 沒找到 | ||
} | } | ||
</source> | </source> | ||
Line 294: | Line 303: | ||
=== <code>Ecosystem::LevelSensor::scaleActor</code> === | === <code>Ecosystem::LevelSensor::scaleActor</code> === | ||
處理敵人的升級。方法跟上面 <code>LevelSensor::scaleWeapon</code> 處理武器的升級類似。 | |||
Pseudocode (1.0.0): | Pseudocode (1.0.0): | ||
Line 301: | Line 310: | ||
if (actor->params["LevelSensorMode"] < 1) | if (actor->params["LevelSensorMode"] < 1) | ||
return false; | return false; | ||
if (actor_name.contains("Enemy")) { | if (actor_name.contains("Enemy")) { | ||
for (enemy_table : this->byml["enemy"]) { | for (enemy_table : this->byml["enemy"]) { | ||
Line 308: | Line 317: | ||
entry = enemy_table["actors"][j]; | entry = enemy_table["actors"][j]; | ||
if (entry["name"] == actor_name && this->enemy_points > entry["value"]) { | if (entry["name"] == actor_name && this->enemy_points > entry["value"]) { | ||
i = j; // 名字對,而且經驗值也夠 | |||
break; | |||
} | } | ||
} | } | ||
if (i == -1) | |||
continue; // 找不到換下一個 | |||
do { | do { | ||
entry = enemy_table["actors"][i]; | entry = enemy_table["actors"][i]; | ||
if (this->enemy_points <= entry["value"]) | if (this->enemy_points <= entry["value"]) | ||
break; // 有找到就枚舉到經驗值最高又符合條件的才停 | |||
++i; | ++i; | ||
} while (i < enemy_table["actors"].size); | } while (i < enemy_table["actors"].size); | ||
*actor_to_use = entry["name"]; | *actor_to_use = entry["name"]; | ||
return true; | return true; | ||
Line 328: | Line 337: | ||
return false; // cannot scale up | return false; // cannot scale up | ||
} | } | ||
if (actor_name.contains("Weapon")) { | if (actor_name.contains("Weapon")) { // 找到敵人後,手上的武器單獨處理 | ||
weapon_name = actor->getWeaponName(); | weapon_name = actor->getWeaponName(); | ||
modifier = actor->params["SharpWeaponJudgeType"]; | modifier = actor->params["SharpWeaponJudgeType"]; | ||
if (modifier == WeaponModifier::RandomBlue) | if (modifier == WeaponModifier::RandomBlue) | ||
modifier = get_random_blue_modifier(actor->getWeaponName()); // 抽獎 | |||
if (scaleWeapon(weapon_name, &weapon_to_use, &modifier_to_use)) { | if (scaleWeapon(weapon_name, &weapon_to_use, &modifier_to_use)) { | ||
actor->setProperty("SharpWeaponJudgeType", modifier_to_use); // 用找的 | |||
*actor_to_use = weapon_to_use; | |||
return true; // 有找到 | |||
} | } | ||
return false; // cannot scale up | return false; // cannot scale up // 沒找到 | ||
} | } | ||
</source> | </source> | ||
== | == 大數據 == | ||
精確的數值可以在這裡找到: | |||
* [https://docs.google.com/spreadsheets/d/e/2PACX-1vRSlyOD7FLAn1TUBn64Pu8Pld-WOfgcVByuywHMWvBTEV0j8potD1wkBs-MJJXf-gvEkpfItUCMqMk6/pubhtml kill point, enemy scaling and weapon scaling tables] | * [https://docs.google.com/spreadsheets/d/e/2PACX-1vRSlyOD7FLAn1TUBn64Pu8Pld-WOfgcVByuywHMWvBTEV0j8potD1wkBs-MJJXf-gvEkpfItUCMqMk6/pubhtml kill point, enemy scaling and weapon scaling tables] | ||
* | * [https://objmap.zeldamods.org 地圖裡也有]. | ||
This makes it possible to see both the required points for enemy/weapon upgrades, as well as all of the special cases extremely easily. | This makes it possible to see both the required points for enemy/weapon upgrades, as well as all of the special cases extremely easily. | ||
== | == 厄咒加儂 == | ||
厄咒加儂也有升級版,不過是依據的是另一個完全獨立的系統。 | |||
具體升級的內容是:神獸裡的厄咒加儂的血量是由玩家打死過多少別的加儂決定的。 | |||
<source lang="c++">__int64 SiteBoss::getInitialHP(SiteBoss *this) // 0x71002D01F4 | <source lang="c++">__int64 SiteBoss::getInitialHP(SiteBoss *this) // 0x71002D01F4 | ||
{ | { | ||
const int baseHp = Enemy::getInitialHP(this); | const int baseHp = Enemy::getInitialHP(this); // 基礎血量(底) | ||
const int halfBaseHp = baseHp >> 1; | const int halfBaseHp = baseHp >> 1; // 除以 2(台) | ||
const bool dieGanonWind = hasFlag_Die_PGanonWind(0); | const bool dieGanonWind = hasFlag_Die_PGanonWind(0); // 打過風嗎? | ||
const bool dieGanonWater = hasFlag_Die_PGanonWater(0); | const bool dieGanonWater = hasFlag_Die_PGanonWater(0); // 打過水嗎? | ||
const bool dieGanonFire = hasFlag_Die_PGanonFire(0); | const bool dieGanonFire = hasFlag_Die_PGanonFire(0); // 打過火嗎? | ||
const bool dieGanonElectric = hasFlag_Die_PGanonElectric(0); | const bool dieGanonElectric = hasFlag_Die_PGanonElectric(0); // 打過雷嗎? | ||
const int flags = this->siteBossFlags & 0xFFFFFFFC; | const int flags = this->siteBossFlags & 0xFFFFFFFC; // 玩家在哪裡 | ||
int multiplier; | int multiplier; | ||
if ( flags == 4 ) | if ( flags == 4 ) // 城堡裡 | ||
multiplier = 3; | |||
else if ( flags == 8 ) | else if ( flags == 8 ) // 幻影空間 | ||
multiplier = 4; | |||
else | else | ||
multiplier = dieGanonFire + dieGanonWind + dieGanonWater + dieGanonElectric; | multiplier = dieGanonFire + dieGanonWind + dieGanonWater + dieGanonElectric; | ||
Line 374: | Line 384: | ||
}</source> | }</source> | ||
換句換說,玩家第一個擊倒的厄咒加儂的血量是 800,下一個 1200,再下一個 1600,最後一個 2000。 | |||
=== Special case 1: | === Special case 1: 城堡裡的厄咒加儂 === | ||
Castle blights have <code>IsRemainBoss</code> set to false in their root AI parameters (see [[AIDef:AI/SiteBossSpearRoot]] for example), which sets flag 4. | Castle blights have <code>IsRemainBoss</code> set to false in their root AI parameters (see [[AIDef:AI/SiteBossSpearRoot]] for example), which sets flag 4. | ||
城堡裡的厄咒加儂,不管是第幾隻,血量都是 800+3×400 = 2000。 | |||
打死城堡裡的厄咒加儂並不計入經驗值。(相應的計數器不會增加。) | |||
=== Special case 2: | === Special case 2: 幻影空間裡的厄咒加儂 === | ||
Illusory Realm blights possess the <code>EnemySiteBoss_R</code> actor tag. This causes flag 8 to be set. So they will always have 500+4×250 = 1500 HP. | Illusory Realm blights possess the <code>EnemySiteBoss_R</code> actor tag. This causes flag 8 to be set. So they will always have 500+4×250 = 1500 HP. | ||
補充:幻影裡的厄咒加儂基礎血量是 500,其他情況是 800。 | |||
Interestingly, the Windblight AI function | Interestingly, the Windblight AI function doesn't check the actor tag but the actor name instead. For flag 8 to be set, the actor name must be <code>Enemy_SiteBoss_Bow_R</code>. | ||
<references /> | <references /> | ||
[[Category:Internals]] | [[Category:Internals{{#translation:}}]] | ||
[[Category:Game mechanics]] | [[Category:Game mechanics{{#translation:}}]] |
Latest revision as of 19:37, 23 May 2020
Difficulty scaling(等级系统)是旷野之息用来控制敌人跟武器等级的一个机制。你会注意到,在游玩的过程中,同一个地点的敌人可能在若干次血月后变成了更强的敌人,且其手上的武器也有所变化。
经验值
等级系统是根据存档里的一个“经验值”系统决定的。只有怪物死掉可以获得经验值。(意即解神庙、解任务之类的事情不算。)
每当有敌人死掉,游戏就会想增加 Defeated_{SameGroupActorName}_Num 这个计数器。 花括号的意思是,如果是波克布林死掉,就会更新波克布林专属的计数器。 不过,只有下列条件满足的时候,计数器才会真的增加。
- 计数器本身还不到 10(意思是大部分的怪物只有杀死前十只才算)
- 死掉的那个敌人没有 NotCountDefeatedNum flag(意思是某些特殊情况下不算)
- 导师米兹·乔希亚专用的计数器 'Defeated_Priest_Boss_Normal_Num' 是 0(意思是只有杀第一次才算)
- 魔兽加侬也是只有第一次才算
- 厄咒加侬只有在神兽里打败的才算,原始游戏打一次,DLC 跟幻影打一次,总共两次(在城堡里打的不算)
需要注意的是只要有任何敌人死掉,不管死法,也不管是不是林克亲手杀的,游戏都会试图增加计数器。下面会讲到哪些情况下不会算。
由于主线剧情会要求玩家杀怪,所以玩家或多或少都会累积经验值,并且相应地导致等级上升。
游戏的存档里会储存死了几只怪的计数器,有了计数之后 Ecosystem::LevelSensor 这个子系统会用 #Ecosystem::LevelSensor::calculatePoints 计算你的经验值。不同的怪会给你不同的经验,表格存在这里 #Ecosystem::LevelSensor::loadByml.
算出经验值之后,这个子系统会用这两个函数 #Ecosystem::LevelSensor::scaleWeapon 跟 #Ecosystem::LevelSensor::scaleActor 来决定敌人跟武器应该分别是什么等级。
阻止升级的情况
下面这个情况会导致敌人跟武器维持原样。
- WorldMgr::sInstance->stageType == 1 (Open World stage)
- 且 WorldMgr::sInstance->isAocField (current map is Trial of the Sword)
- 且 WorldMgr::sInstance->disableScaling (set to true when entering Trial of the Sword)
总而言之,游戏不会升级剑之考验里的武器和敌人。
另一种会跳过升级的情况是 map area 等于 28, 也就是林克位在 "HateruSea",野外的考验的塞哈特诺岛。
武器
下面这些武器在生成的时候会呼叫它的 'scaleWeapon' 函数
- 单独存在的武器: The actor property 'LevelSensorMode' is higher than 1 and it wasn't already picked up. 设定上可以升级而且还没被捡走
- 宝箱里的武器: If SharpWeaponJudgeType is not 4, when AIDef:AI/TreasureBox initialises the drop actor. 同样是设定上允许升级
- 西诺克斯脖子上的武器: The flag
{MapName}_Necklace_{i}_{HinoxName}_{ID}
is false. - 敌人手上的武器: The flag
{MapName}_WeaponDrop_{ID}
is false, and [the actor property 'LevelSensorMode' is higher than 1 or the enemy is a Guardian Scout ('Enemy_Guardian_Mini' 庙里的守护者)].
注记:从 NPC 那里拿到的武器(阿卡莱古代研究所、英杰武器、怪物商人)不在上面的列表里,从而不升级。
武器加成
武器的加成有三个要素。 一是武器本身会被更新成更好的武器(骑士之剑 -> 王族之剑); 二是武器的攻击、防御、耐久等数值择一增加; 三是增加多少。
蓝标、白标(Blue/White)是比较低阶的升级,标记成“某某某提升”。黄标(Yellow)是高阶的升级,标记成“某某某大提升”。以攻击力为例,蓝标可能会增加 10~20 的攻击力(攻击力提升 +14),而黄标会增加 20~30(攻击力大提升 +24)。
加成的方向
要往哪个方向加成(加攻击还是加耐久?还是别的?)是随机的, 每个可能的方向有同样的机率被选到。
加成的方向 | Available in bonus tiers |
---|---|
Attack up 攻击力 | Blue/White and Yellow |
Durability up 耐久度提升 | Blue/White and Yellow |
Long throw 远距离投掷 | Yellow |
Multi-shot burst (bows) 多发弓发数增加 | Yellow |
Quick shot (bows) 速射 | Yellow |
AddSurfMaster | Yellow |
Shield guard up 防御力 | Blue/White and Yellow |
Critical Hit 最后爆击 | Blue/White |
ZoomRapid | Yellow |
需注意的是:
- 最后爆击 "Critical Hit" 只可能在 weaponCommonSharpWeaponAddCrit 是 true 且 加成的 tier 程度是 White/Blue (白色或蓝色的“提升”而不是黄色的“大提升”)时出现。 但是随著经验值的提升,所有武器的加成都会渐渐变成 Yellow (黄色的“大提升”),所以“最后爆击”在游戏后期就不会再出现。
- AddSurfMaster is a bonus that only applies to shields and gives them a lower friction for shield surfing. That bonus type is unused in the game.
加成的程度
武器加成多少(如攻击力应当提升多少)的数值来源于这个表 bgparamlist (with a copy of the information in ActorInfoData). Valid ranges and bonuses for each weapon are configured in the WeaponCommon section.
amiibo
加成的方向 | 加成的程度 |
---|---|
None 不加成 | - |
Attack up 攻击力 | addAtkMax |
Durability up 耐久度提升 | addLifeMax |
Long throw 远距离投掷 | addThrowMax |
Multi-shot burst (bows) 多发弓发数增加 | 5-shot burst 五连发 |
Quick shot (bows) 速射 | addRapidFireMin |
AddSurfMaster | GlobalParameter::shieldSurfMasterFrictionRatio |
Shield guard up 防御力 | addGuardMax |
这段的意思是 amiibo 武器的加成都会是固定的值。譬如说,如果游戏决定增加攻击力,而且这个武器的攻击力最多 +20,那就会 +20。
非 amiibo
加成的方向 | 加成的程度 |
---|---|
None 不加成 | - |
Attack up 攻击力 | addAtkMin 到 addAtkMax 之间的随机数字 |
Durability up 耐久度提升 | addLifeMin 到 addLifeMax 之间的随机数字 |
Long throw 远距离投掷 | addThrowMin 到 addThrowMax 之间的随机数字 |
Multi-shot burst (bows) 多发弓发数增加 | 5-shot burst 五连发 |
Quick shot (bows) 速射 | addRapidFireMin 到 addRapidFireMax 之间的随机数字 |
AddSurfMaster | GlobalParameter::shieldSurfMasterFrictionRatio |
Shield guard up 防御力 | addGuardMin 到 addGuardMax 之间的随机数字 |
这段的意思是非 amiibo 武器的加成都是随机的值。譬如说,如果游戏决定增加攻击力,而且这个武器的攻击力从 +10 到 +20 都有可能,那最后生成的武器就会是 10 到 20 之间的随机数字(应该是均匀分布)。
敌人
游戏在载入敌人时会根据一定的规则升级敌人。
不过 'LevelSensorMode' < 1 的时候所有敌人(跟武器)都不会升级。
注记:有些敌人本身是无法升级的(例如带冰、火、电的蜥蜴), 这仅仅是代表他们自身就已经是最高级了。 而他们手上的武器只要满足下列条件就可以升级:
- 'LevelSensorMode' 非零且经验值足够高
- 或是某个 flag 被强制写入应该升级 the modifier tier is overridden using 'SharpWeaponJudgeType'.
[1.3.0] 版本加入了大师模式。大师模式里所有敌人都会先验地提高一级。这个部分跟 'LevelSensorMode' 造成的升级互不冲突而且可以叠加。更精确的控制是由下面这些参数决定的
参数 | 预设值 | Description |
---|---|---|
IsHardModeActor | false | 是否只在大师模式出现 |
DisableRankUpForHardMode | false | 在大师模式里是否避免升级 |
在大师模式里,某些敌人的 IsHardModeActor、DisableRankUpForHardMode、LevelSensorMode这三个参数会同时被游戏设计师手动调整。调整的目的是让大师模式的后期还是会存在低等级的怪物(例如台地南边有个红色小波)。
属性
LevelSensorMode
这个 actor 属性决定了一个武器或敌人会不会升级,一个敌人的 LevelSensorMode 属性同时控制了它手上的武器升不升级。
不过西诺克斯的 LevelSensorMode 属性并不控制他脖子上的武器如何升级(到头来是脖子上而不是手上)。总之,西诺克斯用的是另一个机制来决定脖子上的武器如何升级,而且这个机制可以绕过 'LevelSensorMode'。
SharpWeaponJudgeType
这个属性决定某个敌人或武器“至少”会被加成多少。 (意即:就算经验值不够还是会加成。)
在 map units 这类的地方,下面这些值记载了游戏打算如何加成武器。
值 | Description |
---|---|
0 | None: 不加成 |
1 | RandomBlue: 要么是蓝标以上的加成,要么不加成,随机决定。 (with weaponCommonSharpWeaponPer being the probability).
|
2 | Blue: 至少蓝/白标,按经验值往上加。 |
3 | Yellow: 至少黄标。 |
4 | NoneForced (限于宝箱): Weapon will never spawn with any modifiers. 宝箱的这个属性会抹去其他一切升级。 |
如果 scaling 处在打开状态的话,武器有可能因为经验值足够的关系被升到更高级。
经验值不够则维持原升级(不会降级)。
换句话说,就算是设成 0 ('None') 的武器也不是永远不升级了, 而是游戏开发者有意给玩家最低等级的武器。 只要玩家的经验值高过某个门槛,就会得到 Blue 蓝标加成,再高一点 Yellow 黄标加成。
升级的计算过程
Ecosystem::LevelSensor::loadByml
这个函数会被 Ecosystem::init
呼叫,
后者是被 ksys::InitializeApp
呼叫。
它的功用是载入 byml 档案 Ecosystem/LevelSensor.byml。
Ecosystem::LevelSensor::calculatePoints
每当游戏生成 actors 时,PlacementMgr 会呼叫这个函数。(actor 可以是武器或是敌人)
它会用各种表格跟各种经验值计算武器或敌人要升级多少。
游戏到目前为止都只用杀敌数 Defeated_%s_Num
来计算经验值并以此推测升级程度,但是从程式设计的角度来说可以用别的东西来计算经验值。
有趣的是,游戏利用杀敌数计算经验值时,会把一个“原始经验值”转化成“用在武器升级的经验值”跟“用在敌人升级的经验值”。这样设计师就可以在后期微调“原始”转化到“武器”的汇率;“敌人”亦同。
float points = 0.0;
for (kill_flag : this->byml["flag"]) // 枚舉所有敵人
int kill_count = GameData::getIntegerFlag(kill_flag["name"]);
points += kill_count * kill_flag["point"]; // 殺敵數 * 此敵人的分數
this->points = points; // 原始經驗值
this->weapon_points = points * this->byml["setting"].Level2WeaponPower; // 換算成武器用分數
this->enemy_points = points * this->byml["setting"].Level2EnemyPower; // 換算成敵人用分數
就我们能看到的部分,任天堂从来没有调整过这两个数值。
Ecosystem::LevelSensor::scaleWeapon
它负责处理武器的升级。有三个东西会呼叫它:宝箱、敌人[check]、 Ecosystem::LevelSensor::scaleActor
。
呼叫时给定一个武器的名字、预设的加成(至少多少)、跟目前玩家的经验值。然后这个函数会回传一个武器名称(可能跟给定的不一样),其中包括应当使用何种加成。
如果函数找半天找不到满足给定的条件武器、加成,游戏就会用预设的武器、预设的加成。(程式码里最后的 return false 会处理所有例外、失败的情形。)
Pseudocode (1.0.0):
bool Ecosystem::LevelSensor::scaleWeapon(const sead::SafeString& weapon_to_look_up, // 基礎武器
WeaponModifier required_modifier, // 預設至少加這麼多
const char** weapon_to_use_name, // 回傳的武器寫在這裡
WeaponModifier* modifier_to_use, // 回傳的加成寫在這裡
void* unknown)
{
// some checks using 'unknown' here which seems to be a pointer to the actor
for (weapon_table : this->byml["weapon"]) {
// find the first weapon entry for which the player has enough points
// with the specified name and modifier
// 不同的武器系列儲存在不同的 table 裡,要枚舉 table 再枚舉 table 裡的細項
i = -1; // 應該是 index,但是 -1 代表尚未找到
for (j = 0; j < weapon_table["actors"].size; ++j) { // 枚舉含有加成訊息的武器
entry = weapon_table["actors"][j];
float points_for_next_transition = entry["value"]; // 這個加成過的武器所需的經驗值
if (this->weapon_points > points_for_next_transition && // 你的經驗值夠
weapon_to_look_up == entry["name"] && // 武器的名字是對的
convert_to_modifier(entry["plus"]) == required_modifier) { // 加成的方向跟程度是對的
i = j;
break; // 找到一個了(但是可能還有更多)
}
}
if (i == -1)
continue; // 找不到就試下一個
do { // 已知找到一個的情況下,試圖找更多
entry = weapon_table["actors"][i];
// not_rank_up means there is no link between weapons;
// this table is just used to look up modifiers.
// so go down the list until there are no more entries for the requested weapon
// or until we reach a modifier that requires more points.
if (weapon_table["not_rank_up"] && entry["name"] != weapon_to_look_up)
break; // ???這個 table 裡的武器不能升級,或是 entry 是 null (boundary check)
// otherwise, just go down the list until we reach the end or a weapon which
// requires more points. this will possibly upgrade the weapon (e.g. Knight -> Royal).
if (this->weapon_points <= entry["value"])
break; // 枚舉到經驗值最高又符合條件的才停
++i;
} while (i < weapon_table["actors"].size);
*weapon_to_use_name = entry["name"];
*modifier_to_use = convert_to_modifier(entry["plus"]);
return true; // 有找到
}
return false; // cannot scale up // 沒找到
}
Ecosystem::LevelSensor::scaleActor
处理敌人的升级。方法跟上面 LevelSensor::scaleWeapon
处理武器的升级类似。
Pseudocode (1.0.0):
if (actor->params["LevelSensorMode"] < 1)
return false;
if (actor_name.contains("Enemy")) {
for (enemy_table : this->byml["enemy"]) {
i = -1;
for (j = 0; j < enemy_table["actors"].size; ++j) {
entry = enemy_table["actors"][j];
if (entry["name"] == actor_name && this->enemy_points > entry["value"]) {
i = j; // 名字對,而且經驗值也夠
break;
}
}
if (i == -1)
continue; // 找不到換下一個
do {
entry = enemy_table["actors"][i];
if (this->enemy_points <= entry["value"])
break; // 有找到就枚舉到經驗值最高又符合條件的才停
++i;
} while (i < enemy_table["actors"].size);
*actor_to_use = entry["name"];
return true;
}
return false; // cannot scale up
}
if (actor_name.contains("Weapon")) { // 找到敵人後,手上的武器單獨處理
weapon_name = actor->getWeaponName();
modifier = actor->params["SharpWeaponJudgeType"];
if (modifier == WeaponModifier::RandomBlue)
modifier = get_random_blue_modifier(actor->getWeaponName()); // 抽獎
if (scaleWeapon(weapon_name, &weapon_to_use, &modifier_to_use)) {
actor->setProperty("SharpWeaponJudgeType", modifier_to_use); // 用找的
*actor_to_use = weapon_to_use;
return true; // 有找到
}
return false; // cannot scale up // 沒找到
}
大数据
精确的数值可以在这里找到:
This makes it possible to see both the required points for enemy/weapon upgrades, as well as all of the special cases extremely easily.
厄咒加侬
厄咒加侬也有升级版,不过是依据的是另一个完全独立的系统。 具体升级的内容是:神兽里的厄咒加侬的血量是由玩家打死过多少别的加侬决定的。
__int64 SiteBoss::getInitialHP(SiteBoss *this) // 0x71002D01F4
{
const int baseHp = Enemy::getInitialHP(this); // 基礎血量(底)
const int halfBaseHp = baseHp >> 1; // 除以 2(台)
const bool dieGanonWind = hasFlag_Die_PGanonWind(0); // 打過風嗎?
const bool dieGanonWater = hasFlag_Die_PGanonWater(0); // 打過水嗎?
const bool dieGanonFire = hasFlag_Die_PGanonFire(0); // 打過火嗎?
const bool dieGanonElectric = hasFlag_Die_PGanonElectric(0); // 打過雷嗎?
const int flags = this->siteBossFlags & 0xFFFFFFFC; // 玩家在哪裡
int multiplier;
if ( flags == 4 ) // 城堡裡
multiplier = 3;
else if ( flags == 8 ) // 幻影空間
multiplier = 4;
else
multiplier = dieGanonFire + dieGanonWind + dieGanonWater + dieGanonElectric;
return baseHp + multiplier * halfBaseHp;
}
换句换说,玩家第一个击倒的厄咒加侬的血量是 800,下一个 1200,再下一个 1600,最后一个 2000。
Special case 1: 城堡里的厄咒加侬
Castle blights have IsRemainBoss
set to false in their root AI parameters (see AIDef:AI/SiteBossSpearRoot for example), which sets flag 4.
城堡里的厄咒加侬,不管是第几只,血量都是 800+3×400 = 2000。
打死城堡里的厄咒加侬并不计入经验值。(相应的计数器不会增加。)
Special case 2: 幻影空间里的厄咒加侬
Illusory Realm blights possess the EnemySiteBoss_R
actor tag. This causes flag 8 to be set. So they will always have 500+4×250 = 1500 HP.
补充:幻影里的厄咒加侬基础血量是 500,其他情况是 800。
Interestingly, the Windblight AI function doesn't check the actor tag but the actor name instead. For flag 8 to be set, the actor name must be Enemy_SiteBoss_Bow_R
.