93 lines
4.1 KiB
C++
93 lines
4.1 KiB
C++
// combo_table —— PC 连击段表(CRaceData::TComboAttackDataMap 的等价)。M2 §3.5。
|
||
//
|
||
// 参考端的 PC 连击段表**不在** .msm 里(.msm 只有 BaseModelFileName + HairData)。
|
||
// 它由 playersettingmodule.py 的 chrmgr.ReserveComboAttackNew / RegisterComboAttackNew
|
||
// 调用在 CRaceData 上建起来(UserInterface/PythonCharacterManagerModule.cpp →
|
||
// CRaceData::ReserveComboAttack / RegisterComboAttack,GameLib/RaceData.cpp)。
|
||
// 本解析器就把那批调用读成同一张表。
|
||
//
|
||
// NPC/怪物的连击段走另一条路(RaceManager.cpp __LoadRaceMotionList 读 motlist.txt,
|
||
// 只在 MODE_GENERAL 下登记 NAME_COMBO_ATTACK_1/2/3,没有段号向量),不在这里。
|
||
#pragma once
|
||
#include <cstdint>
|
||
#include <map>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace fmt {
|
||
|
||
// CRaceMotionData::EMode(chr.MOTION_MODE_*)。combo key 的高 16 位。
|
||
enum ComboMotionMode : uint16_t {
|
||
COMBO_MODE_RESERVED = 0,
|
||
COMBO_MODE_GENERAL = 1,
|
||
COMBO_MODE_ONEHAND_SWORD = 2,
|
||
COMBO_MODE_TWOHAND_SWORD = 3,
|
||
COMBO_MODE_DUALHAND_SWORD = 4,
|
||
COMBO_MODE_BOW = 5,
|
||
COMBO_MODE_FAN = 6,
|
||
COMBO_MODE_BELL = 7,
|
||
COMBO_MODE_FISHING = 8,
|
||
COMBO_MODE_HORSE = 9,
|
||
COMBO_MODE_HORSE_ONEHAND_SWORD = 10,
|
||
COMBO_MODE_HORSE_TWOHAND_SWORD = 11,
|
||
COMBO_MODE_HORSE_DUALHAND_SWORD = 12,
|
||
COMBO_MODE_HORSE_BOW = 13,
|
||
COMBO_MODE_HORSE_FAN = 14,
|
||
COMBO_MODE_HORSE_BELL = 15,
|
||
COMBO_MODE_WEDDING_DRESS = 16,
|
||
};
|
||
|
||
// CRaceMotionData::EName 的攻击段(chr.MOTION_NORMAL_ATTACK / chr.MOTION_COMBO_ATTACK_*)。
|
||
// ComboIndexVector 里存的就是这些段号。
|
||
enum : uint16_t {
|
||
COMBO_NAME_NORMAL_ATTACK = 13,
|
||
COMBO_NAME_COMBO_ATTACK_1 = 14,
|
||
COMBO_NAME_COMBO_ATTACK_8 = 21,
|
||
};
|
||
|
||
// RaceData.h: MAKE_COMBO_KEY(motion_mode, combo_type)
|
||
inline uint32_t make_combo_key(uint16_t motion_mode, uint16_t combo_type) {
|
||
return (static_cast<uint32_t>(motion_mode) << 16) | static_cast<uint32_t>(combo_type);
|
||
}
|
||
inline uint16_t combo_key_mode(uint32_t key) { return static_cast<uint16_t>((key >> 16) & 0xFFFF); }
|
||
inline uint16_t combo_key_type(uint32_t key) { return static_cast<uint16_t>(key & 0xFFFF); }
|
||
|
||
// playersettingmodule.py 按 __LoadGame<Class>Ex(race, path) 分四块,男女同一函数体
|
||
// (连击段表与性别无关),所以这里按职业收,不按 race。
|
||
enum ComboClass : int {
|
||
COMBO_CLASS_WARRIOR = 0,
|
||
COMBO_CLASS_ASSASSIN = 1,
|
||
COMBO_CLASS_SURA = 2,
|
||
COMBO_CLASS_SHAMAN = 3,
|
||
COMBO_CLASS_COUNT = 4,
|
||
};
|
||
|
||
// CRaceData::TComboAttackDataMap 的等价:MAKE_COMBO_KEY -> ComboIndexVector。
|
||
struct ComboTable {
|
||
// key = make_combo_key(mode, type);value = 有序的 COMBO_NAME_COMBO_ATTACK_* 段号,
|
||
// 下标即 dwComboArrayIndex(m_dwcurComboIndex - 1)。
|
||
std::map<uint32_t, std::vector<uint16_t>> combos;
|
||
|
||
// GetComboDataPointer:命中返回段号向量指针,否则 nullptr。
|
||
const std::vector<uint16_t>* get(uint16_t motion_mode, uint16_t combo_type) const;
|
||
bool empty() const { return combos.empty(); }
|
||
size_t key_count() const { return combos.size(); }
|
||
};
|
||
|
||
struct PlayerComboTables {
|
||
ComboTable per_class[COMBO_CLASS_COUNT];
|
||
// 越界返回一张空表(不抛)。
|
||
const ComboTable& klass(int combo_class) const;
|
||
};
|
||
|
||
// 解析 playersettingmodule.py 里的 chrmgr.ReserveComboAttackNew / RegisterComboAttackNew
|
||
// 调用,按 def __LoadGame<Class>Ex 分块填 per_class。照 CRaceData 语义:
|
||
// Reserve -> combos[key] = vector(count, 0)(重复 key 不覆盖,同 std::map::insert)
|
||
// Register -> combos[key][index] = motion(key 未 Reserve 或 index 越界则跳过)
|
||
// 认得的符号:chr.MOTION_MODE_*、chr.MOTION_COMBO_ATTACK_N(=13+N)、
|
||
// chr.MOTION_NORMAL_ATTACK(=13)、COMBO_TYPE_N(=N-1)、COMBO_INDEX_N(=N-1)、裸整数。
|
||
bool parse_player_combo_tables(const std::string& py_text, PlayerComboTables& out, std::string* err);
|
||
bool parse_player_combo_tables_file(const std::string& path, PlayerComboTables& out, std::string* err);
|
||
|
||
} // namespace fmt
|