完善客户端功能并同步差距文档
This commit is contained in:
@@ -20,6 +20,70 @@ static void loop_data(const Node& root, Msa& out) {
|
||||
}
|
||||
}
|
||||
|
||||
// `List HitPosition { t lastXYZ XYZ t lastXYZ XYZ ... }` —— 7 个一组切成采样点。
|
||||
static void hit_positions(const Node& win, std::vector<Msa::HitSample>& out) {
|
||||
const Node* lst = win.group("HitPosition");
|
||||
if (!lst || lst->lines.empty()) return;
|
||||
const std::vector<std::string>& t = lst->lines[0];
|
||||
for (size_t i = 0; i + 7 <= t.size(); i += 7) {
|
||||
Msa::HitSample s;
|
||||
s.time = float(std::atof(t[i].c_str()));
|
||||
for (int k = 0; k < 3; ++k) s.last_pos[k] = float(std::atof(t[i + 1 + k].c_str()));
|
||||
for (int k = 0; k < 3; ++k) s.pos[k] = float(std::atof(t[i + 4 + k].c_str()));
|
||||
out.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
// 一个命中窗(NRaceData::THitData)。老式写法里 win == AttackingData 组本身。
|
||||
static Msa::HitWindow hit_window(const Node& win) {
|
||||
Msa::HitWindow w;
|
||||
w.start_time = win.num("AttackingStartTime");
|
||||
w.end_time = win.num("AttackingEndTime");
|
||||
w.bone_name = win.str("AttackingBone");
|
||||
w.weapon_length = win.num("WeaponLength");
|
||||
hit_positions(win, w.samples);
|
||||
return w;
|
||||
}
|
||||
|
||||
// Group ComboInputData / Group AttackingData —— 照 EterGrnLib CGrannyMotion::LoadMotionData
|
||||
// 与 GameLib NRaceData::LoadMotionAttackData 的字段名读取;缺组时保持默认值。
|
||||
static void combat_data(const Node& root, Msa& out) {
|
||||
if (const Node* c = root.group("ComboInputData")) {
|
||||
out.has_combo_input = true;
|
||||
out.combo_pre_input_time = c->num("PreInputTime");
|
||||
out.combo_direct_input_time = c->num("DirectInputTime");
|
||||
out.combo_input_limit_time = c->num("InputLimitTime");
|
||||
out.combo_link_time = c->num("LinkTime");
|
||||
}
|
||||
if (const Node* a = root.group("AttackingData")) {
|
||||
out.has_attacking_data = true;
|
||||
out.attacking_type = a->inum("AttackType", a->inum("AttackingType", 0));
|
||||
out.motion_type = a->inum("MotionType", out.attacking_type);
|
||||
out.hitting_type = a->inum("HittingType", 0);
|
||||
out.stiffen_time = a->num("StiffenTime");
|
||||
out.invisible_time = a->num("InvisibleTime");
|
||||
out.external_force = a->num("ExternalForce");
|
||||
out.hit_limit_count = a->inum("HitLimitCount", 0);
|
||||
|
||||
// 新式:HitDataCount N + Group HitData00..0N;老式:无 HitDataCount,组本身即窗。
|
||||
bool has_children = false;
|
||||
for (const Node& g : a->groups) {
|
||||
const std::string& n = g.name;
|
||||
if (n.size() >= 7 && (n.compare(0, 7, "HitData") == 0 || n.compare(0, 7, "hitdata") == 0)) {
|
||||
out.hit_windows.push_back(hit_window(g));
|
||||
has_children = true;
|
||||
}
|
||||
}
|
||||
if (!has_children)
|
||||
out.hit_windows.push_back(hit_window(*a));
|
||||
|
||||
if (!out.hit_windows.empty()) {
|
||||
out.attack_start_time = out.hit_windows.front().start_time;
|
||||
out.attack_end_time = out.hit_windows.front().end_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_msa(const std::string& text, Msa& out, std::string* err) {
|
||||
Node root;
|
||||
if (!parse_textscript(text, root, err)) return false;
|
||||
@@ -29,6 +93,7 @@ bool parse_msa(const std::string& text, Msa& out, std::string* err) {
|
||||
out.duration = root.num("MotionDuration");
|
||||
vec3(root.find("Accumulation"), out.accumulation);
|
||||
loop_data(root, out);
|
||||
combat_data(root, out);
|
||||
|
||||
if (const Node* med = root.group("MotionEventData")) {
|
||||
int count = med->inum("MotionEventDataCount", int(med->groups.size()));
|
||||
@@ -57,6 +122,7 @@ bool parse_msa_file(const std::string& path, Msa& out, std::string* err) {
|
||||
out.duration = root.num("MotionDuration");
|
||||
vec3(root.find("Accumulation"), out.accumulation);
|
||||
loop_data(root, out);
|
||||
combat_data(root, out);
|
||||
if (const Node* med = root.group("MotionEventData"))
|
||||
for (const Node& ev : med->groups) {
|
||||
MotionEvent e;
|
||||
|
||||
Reference in New Issue
Block a user