82 lines
3.8 KiB
C++
82 lines
3.8 KiB
C++
// .msa —— Metin2 动作描述(文本,ScriptType MotionData)。M2 T1。
|
||
#pragma once
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
namespace fmt {
|
||
|
||
struct MotionEvent {
|
||
int type = 0; // MotionEventType(10=effect, 4/5=sound, ...)
|
||
float start_time = 0; // StartingTime
|
||
std::string effect_file; // EffectFileName(type 10)
|
||
std::string sound_file; // SoundFileName
|
||
std::string attaching_bone;
|
||
bool attaching = false;
|
||
bool following = false;
|
||
bool independent = false;
|
||
bool fishing_effect = false;
|
||
float position[3] = {0, 0, 0};
|
||
};
|
||
|
||
struct Msa {
|
||
std::string motion_gr2; // MotionFileName(原样,含 d:\... 前缀)
|
||
float duration = 0; // MotionDuration
|
||
float accumulation[3] = {0,0,0};// Accumulation(root motion 位移)
|
||
std::vector<MotionEvent> events;
|
||
bool has_loop_data = false; // LoopData 是片段循环,不等同于整段播放模式
|
||
int motion_loop_count = 0; // -1 无限;原客户端仅在 >1 或 -1 时回绕片段
|
||
bool loop_cancel_enable = false;
|
||
float loop_start_time = 0;
|
||
float loop_end_time = 0;
|
||
|
||
// Group ComboInputData —— 连击输入时间窗(EterGrnLib CGrannyMotion::LoadMotionData →
|
||
// CRaceMotionData::TComboInputData)。攻击节奏来自这里,不是硬编码间隔。
|
||
// PreInputTime → fInputStartTime 连击输入窗开始(可提前缓存下一击)
|
||
// DirectInputTime→ fNextComboTime 直接输入阈值 / 下一段连击起点(= 普攻节奏)
|
||
// InputLimitTime → fInputEndTime 连击输入窗关闭(超时归零)
|
||
// LinkTime → fComboLinkTime 段间衔接时间
|
||
bool has_combo_input = false;
|
||
float combo_pre_input_time = 0;
|
||
float combo_direct_input_time = 0;
|
||
float combo_input_limit_time = 0;
|
||
float combo_link_time = 0;
|
||
|
||
// Group AttackingData —— 命中判定窗(GameLib GameType.cpp NRaceData::TMotionAttackData)。
|
||
// 两种写法:老式(组内直接 AttackingStartTime/…/List HitPosition,无 HitDataCount)=
|
||
// 单个命中窗;新式(AttackType + MotionType + HitDataCount N + Group HitData00..0N)=
|
||
// N 个命中窗(多段挥击 / 旋风斩)。命中帧 / 硬直 / 无敌帧由动作数据驱动。
|
||
bool has_attacking_data = false;
|
||
int attacking_type = 0; // AttackType / AttackingType(SAttackData::iAttackType)
|
||
int motion_type = 0; // MotionType(缺省回退到 attacking_type)
|
||
int hitting_type = 0; // HittingType
|
||
float stiffen_time = 0; // StiffenTime(受击硬直,喂给 §3.7 / 受击方)
|
||
float invisible_time = 0; // InvisibleTime(攻击者无敌帧)
|
||
float external_force = 0; // ExternalForce(击退力度)
|
||
int hit_limit_count = 0; // HitLimitCount(0 = 不限)
|
||
|
||
// HitPosition 单个采样:time + 上一帧位置(xyz) + 当前帧位置(xyz),用于挥击扫掠球判定。
|
||
struct HitSample {
|
||
float time = 0;
|
||
float last_pos[3] = {0, 0, 0};
|
||
float pos[3] = {0, 0, 0};
|
||
};
|
||
// 一个命中窗(NRaceData::THitData)。
|
||
struct HitWindow {
|
||
float start_time = 0; // AttackingStartTime
|
||
float end_time = 0; // AttackingEndTime
|
||
std::string bone_name; // AttackingBone
|
||
float weapon_length = 0; // WeaponLength
|
||
std::vector<HitSample> samples; // List HitPosition
|
||
};
|
||
std::vector<HitWindow> hit_windows; // = THitDataContainer;老式写法长度为 1
|
||
|
||
// 兼容旧调用方:第一个命中窗的起止时间(无命中窗时为 0)。
|
||
float attack_start_time = 0;
|
||
float attack_end_time = 0;
|
||
};
|
||
|
||
bool parse_msa(const std::string& text, Msa& out, std::string* err);
|
||
bool parse_msa_file(const std::string& path, Msa& out, std::string* err);
|
||
|
||
} // namespace fmt
|