Merge pull request 'feature/implement-a3dcombineaction' (#350) from feature/implement-a3dcombineaction into develop

Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/350
This commit is contained in:
namth
2026-04-16 08:33:35 +00:00
26 changed files with 117494 additions and 167 deletions
@@ -1324,6 +1324,7 @@ GameObject:
- component: {fileID: 6000274837602301185}
- component: {fileID: 9114627606402321429}
- component: {fileID: 8328673869389635358}
- component: {fileID: 8158485627219515686}
m_Layer: 0
m_Name: "\u5996\u517D\u7537"
m_TagString: Untagged
@@ -2324,6 +2325,19 @@ MonoBehaviour:
- {fileID: 7400000, guid: bf46c8660fde0b4418e0246d5d9c504e, type: 2}
- {fileID: 7400000, guid: f0e3b03a82943994e89998546be354e2, type: 2}
- {fileID: 7400000, guid: 83a015f60036f8c47a958c36f5551441, type: 2}
--- !u!114 &8158485627219515686
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2105786326208748952}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 472c24b873524423f95454131fd11da6, type: 3}
m_Name:
m_EditorClassIdentifier:
combinedActionSO: {fileID: 11400000, guid: aaa221db89a7f14499cd18660718869a, type: 2}
--- !u!1 &2106904235774682341
GameObject:
m_ObjectHideFlags: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aaa221db89a7f14499cd18660718869a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -1937,6 +1937,7 @@ GameObject:
- component: {fileID: 4760280378842097466}
- component: {fileID: 3664697042232086950}
- component: {fileID: 6057715007290216867}
- component: {fileID: 4125229131285412072}
m_Layer: 0
m_Name: A3DSkinnedMeshRenderer_PlayerCharacter(Clone)
m_TagString: Untagged
@@ -3316,6 +3317,19 @@ MonoBehaviour:
- {fileID: 7400000, guid: b9c1b4aacfc88174f9bfcb8bf1c69eb5, type: 2}
- {fileID: 7400000, guid: d54a450816dbc4d4ca43afbe607052ad, type: 2}
- {fileID: 7400000, guid: cbff0726770bb1146bc70346d5fc38f9, type: 2}
--- !u!114 &4125229131285412072
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2570164803878810249}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 472c24b873524423f95454131fd11da6, type: 3}
m_Name:
m_EditorClassIdentifier:
combinedActionSO: {fileID: 11400000, guid: d4104c42c72cca647b9ebec0d6d35089, type: 2}
--- !u!1 &2587141697474146175
GameObject:
m_ObjectHideFlags: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d4104c42c72cca647b9ebec0d6d35089
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -4879,6 +4879,7 @@ GameObject:
- component: {fileID: 6264607540402582859}
- component: {fileID: 2789629435814780223}
- component: {fileID: 4504844427513838873}
- component: {fileID: 5130781569853185460}
m_Layer: 0
m_Name: "\u6CD5\u5E08\u5973"
m_TagString: Untagged
@@ -5946,6 +5947,19 @@ MonoBehaviour:
- {fileID: 7400000, guid: 3e474cefdc0b2e648aec98c966f26991, type: 2}
- {fileID: 7400000, guid: 37c12771675cd69499260a8cadf95621, type: 2}
- {fileID: 7400000, guid: f343209405aa0f7469aeb6429b1fc5a5, type: 2}
--- !u!114 &5130781569853185460
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5133661830825835001}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 472c24b873524423f95454131fd11da6, type: 3}
m_Name:
m_EditorClassIdentifier:
combinedActionSO: {fileID: 11400000, guid: ccf54ff0e6d1e8542a9183e8defc97e4, type: 2}
--- !u!1 &5156671091954275778
GameObject:
m_ObjectHideFlags: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ccf54ff0e6d1e8542a9183e8defc97e4
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c22cdb94f04a6469ea286207b819e3d4
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9e3b5e97e1e0c481dadb61d496065081
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,444 @@
using System.Collections.Generic;
using System.IO;
//using ModelViewer.Scene.SceneObject;
namespace ModelViewer.Common
{
public static class A3DCombinedActionConst
{
public static int EVENT_TYPE_NONE = -1;
public static int EVENT_TYPE_BASE = 100;
public static int EVENT_TYPE_GFX = EVENT_TYPE_BASE + 0;
public static int EVENT_TYPE_SFX = EVENT_TYPE_BASE + 1;
public static int EVENT_TYPE_CHLDACT = EVENT_TYPE_BASE + 2;
public static int EVENT_TYPE_MATCHG = EVENT_TYPE_BASE + 3;
public static int EVENT_TYPE_ATT_PT = EVENT_TYPE_BASE + 4;
public static int EVENT_TYPE_SCRIPT = EVENT_TYPE_BASE + 5;
public static int EVENT_TYPE_CAM_PT = EVENT_TYPE_BASE + 6;
public static int EVENT_TYPE_MODELSCLCHG = EVENT_TYPE_BASE + 7;
public static int EVENT_TYPE_MATTRANS = EVENT_TYPE_BASE + 8;
public static int EVENT_TYPE_AUDIOEVENT = EVENT_TYPE_BASE + 9;
// when add new type, change this definition
public static int EVENT_TYPE_END = EVENT_TYPE_AUDIOEVENT;
public static string _format_cact_name = "CombineActName: ";
public static string _format_act_count = "BaseActCount: ";
public static string _format_act_name = "BaseActName: ";
public static string _format_act_start_time = "ActStartTime: ";
public static string _format_act_LoopCount = "LoopCount: ";
public static string _format_act_LoopMinNum = "LoopMinNum: ";
public static string _format_act_LoopMaxNum = "LoopMaxNum: ";
public static string _format_start_time = "StartTime: ";
public static string _format_time_span = "TimeSpan: ";
public static string _format_rank_count = "RankCount: ";
public static string _format_rank = "Channel: "; // "Channel: %d, Rank: %d"
public static string _format_event_channel = "EventChannel: ";
public static string _format_play_speed = "PlaySpeed: ";
public static string _format_stopchildact = "StopChildAct: ";
public static string _format_resetmtlonstop = "ResetMtl: ";
public static string _format_once = "Once: ";
public static string _format_fx_count = "FxCount: ";
public static string _format_fx_type = "FxType: ";
public static string _format_fx_start_time = "FxStartTime: ";
public static string _format_fx_path_num = "FxFileNum: ";
public static string _format_fx_path = "FxFilePath: ";
public static string _format_hook_name = "HookName: ";
public static string _format_hook_offset = "HookOffset: ";
public static string _format_hook_yaw = "HookYaw: ";
public static string _format_hook_pitch = "HookPitch: ";
public static string _format_hook_rot = "HookRot: ";
public static string _format_bind_parent = "BindParent: ";
public static string _format_fadeout = "FadeOut: ";
public static string _format_model_alpha = "UseModelAlpha: ";
public static string _format_custom_path = "CustomPath: ";
public static string _format_atk_path = "AtkPath: ";
public static string _format_divisions = "Divisions: ";
public static string _format_atk_usedelay = "AtkUseDelay: ";
public static string _format_atk_delaycount = "AtkDelayNum: ";
public static string _format_atk_delaytime = "AtkDelayTime: ";
public static string _format_atk_orientation = "AtkOrient: ";
public static string _format_custom_data = "CustomData: ";
public static string _format_gfx_scale = "GfxScale: ";
public static string _format_gfx_alpha = "GfxAlpha: ";
public static string _format_gfx_play_speed = "GfxSpeed: ";
public static string _format_gfx_outer_path = "GfxOuterPath: ";
public static string _format_gfx_rel_ecm = "GfxRelToECM: ";
public static string _format_gfx_param_count = "GfxParamCount: ";
public static string _format_gfx_delay_time = "GfxDelayTime: ";
public static string _format_gfx_rot_with_model = "GfxRotWithModel: ";
public static string _format_param_ele_name = "ParamEleName: ";
public static string _format_param_id = "ParamId: ";
public static string _format_param_type = "ParamDataType: ";
public static string _format_param_is_cmd = "ParamDataIsCmd: ";
public static string _format_param_cmd = "ParamDataCmd: ";
public static string _format_param_pos = "ParamDataPos: ";
public static string _format_param_hook = "ParamDataHook: ";
public static string _format_child_act_count = "ChildActCount: ";
public static string _format_child_act_name = "ChildActName: ";
public static string _format_chld_hhname = "HHName: ";
public static string _format_transtime = "TransTime: ";
public static string _format_chld_istrail = "IsTrail: ";
public static string _format_chld_span = "TrailSpan: ";
public static string _format_chld_segs = "Segs: ";
public static string _format_pos = "Pos: ";
public static string _format_dir = "Dir: ";
public static string _format_matchg = "ColorValue: ";
public static string _format_apply_child = "ApplyChild: ";
public static string _format_mst_orgcol = "OrgColor: ";
public static string _format_mst_destnum = "DestNum: ";
public static string _format_mst_destcol = "Col: ";
public static string _format_mst_desttime = "Time: ";
public static string _format_event_type = "EventType: ";
public static string _format_event_count = "EventCount: ";
public static string _format_script_lines = "ScriptLines: ";
public static string _format_script_cfg_state = "ScriptCfgState: ";
public static string _format_script_usage = "ScriptUsage: ";
public static string _format_cam_dist2host = "Dist2Host: ";
public static string _format_cam_yaw2host = "Yaw2Host: ";
public static string _format_cam_pitch = "Pitch: ";
public static string _format_cam_yawacc = "YawAcc: ";
public static string _format_cam_pitchacc = "PitchAcc: ";
public static string _format_cam_angleacc = "AngleAcc: ";
public static string _format_cam_linearacc = "LinearAcc: ";
public static string _format_cam_isinterp = "IsInterp: ";
public static string _format_cam_beziernum = "BezierNum: ";
public static string _format_cam_beziervert = "";
public static string _format_audioevent = "AudioEvent: ";
public static string _format_audiomindist = "MinDist: ";
public static string _format_audiomaxdist = "MaxDist: ";
public static string _format_audiousecustom = "Custom: ";
public static string _format_audiovolume = "Volume: ";
public static int _trail_delta = 3;
}
[System.Serializable]
public class ACTION_INFO
{
public string m_strName;
public uint m_dwStartTime;
public uint m_dwEndTime;
public uint m_dwSpan;
public int m_nMinLoops;
public int m_nMaxLoops;
public bool Load(FileStream fileStream, StreamReader file, uint dwVersion)
{
bool isBinary = fileStream != null;
if (isBinary)
{
}
else
{
string szLine = string.Empty;
string tagValue;
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_act_name, out tagValue);
m_strName = tagValue;
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_act_start_time, out tagValue);
uint.TryParse(tagValue, out m_dwStartTime);
if (dwVersion < 6)
{
szLine = file.ReadLine();
}
else if (dwVersion >= 9 && dwVersion < 36)
{
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_act_LoopCount, out tagValue);
int nLoops;
int.TryParse(tagValue, out nLoops);
m_nMinLoops = m_nMaxLoops = nLoops;
}
else if (dwVersion >= 36)
{
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_act_LoopMinNum, out tagValue);
int.TryParse(tagValue, out m_nMinLoops);
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_act_LoopMaxNum, out tagValue);
int.TryParse(tagValue, out m_nMaxLoops);
}
}
return true;
}
}
[System.Serializable]
public class EVENT_INFO
{
public int m_nType;
public uint m_dwStartTime;
public uint m_dwTimeSpan;
public bool m_bOnce;
public A3DCombinedAction m_pAct;
public static EVENT_INFO LoadFromFile(A3DCombinedAction pAct, AFile pFile, StreamReader pTextFile, uint dwVersion)
{
string szLine = string.Empty;
string tagValue;
long dwReadLen;
int nType = A3DCombinedActionConst.EVENT_TYPE_NONE;
if (pFile != null)
{
// pFile->Read(&nType, sizeof(nType), &dwReadLen);
}
else
{
// pFile->ReadLine(szLine, AFILE_LINEMAXLEN, &dwReadLen);
// sscanf(szLine, _format_event_type, &nType);
tagValue = pTextFile.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_event_type,out tagValue);
nType = int.TryParse(tagValue, out nType) ? nType : A3DCombinedActionConst.EVENT_TYPE_NONE;
}
EVENT_INFO pEvent = new();
// if (!pEvent) return NULL;
//
// if (dwVersion >= 18 && !pEvent->LoadEventBase(pFile, dwVersion)
// || !pEvent->Load(pFile, dwVersion))
// {
// delete pEvent;
// return NULL;
// }
return pEvent;
}
}
[System.Serializable]
public class FX_BASE_INFO : EVENT_INFO
{
//public RandStringContainer[] m_pFiles;
public string m_strHookName;
//public A3DVECTOR3 m_vOffset;
public float m_fYaw;
public float m_fPitch;
public float m_fRot;
public bool m_bBindParent;
public bool m_bModelAlpha;
public bool m_bCustomFilePath;
public bool m_bUseECMHook;
public A3DMATRIX4 m_matTran;
public uint m_dwFadeOutTime;
public int m_nCustomData;
public static FX_BASE_INFO LoadFromFile(A3DCombinedAction pAct, FileStream fileStream, StreamReader file, uint dwVersion)
{
bool isBinary = fileStream != null;
if (isBinary)
{
}
else
{
string szLine = string.Empty;
string tagValue;
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_fx_type, out tagValue);
int nType = int.TryParse(tagValue, out nType) ? nType : 0;
}
// TODO: There are 3 types: GFX_INFO, SFX_INFO, AUDIOEVENT_INFO
return new FX_BASE_INFO();
}
}
[System.Serializable]
public class A3DCombinedAction
{
public string m_strName;
public int m_nLoops;
public bool IsLooping()
{
if (m_ActLst.Count == 0)
return false;
return m_ActLst[0].m_nMinLoops == -1 || m_ActLst[0].m_nMaxLoops == -1;
}
public List<ACTION_INFO> m_ActLst = new();
public List<EVENT_INFO> m_EventInfoLst = new();
// Original, this property describes the comact's span time, now the span time may change randomly,
// so this property only define the min span time
// And rename it from m_dwComActSpan -> m_dwComActMinSpan
public uint m_dwComActMinSpan;
public bool m_bInfinite;
//public byte[] m_Ranks = new byte[CECModelConst.ACTCHA_MAX];
public int[] m_aEventCounter = new int[A3DCombinedActionConst.EVENT_TYPE_END - A3DCombinedActionConst.EVENT_TYPE_BASE + 1];
public int m_nEventChannel;
public float m_fPlaySpeed;
public bool m_bResetMaterialScale; // Whether to reset color change state when action stops (default is true)
public bool m_bStopChildrenAct; // Whether to stop child model actions when action stops (default is false)
public bool Load(FileStream fileStream, StreamReader file, uint dwVersion)
{
int nActCount = 0;
int nFxCount = 0;
int nChildCount = 0;
int nEventCount = 0;
bool isBinary = fileStream != null;
if (isBinary)
{
}
else
{
string szLine = string.Empty;
string tagValue;
szLine = file.ReadLine();
while(!szLine.Contains(A3DCombinedActionConst._format_cact_name))
{
szLine = file.ReadLine();
}
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_cact_name, out tagValue);
m_strName = tagValue;
if (dwVersion >= 3)
{
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_act_LoopCount, out tagValue);
int.TryParse(tagValue, out m_nLoops);
}
else
m_nLoops = 1;
if (dwVersion >= 30)
{
int rank_count = 0;
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_rank_count, out tagValue);
int.TryParse(tagValue, out rank_count);
for (int i = 0; i < rank_count; i++)
{
int channel = 0;
int rank = 0;
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_rank, out tagValue);
//Output look like this: 2, Rank: 1
string[] values = tagValue.Split(',');
if (values.Length >= 2)
{
int.TryParse(values[0].Trim(), out channel);
int.TryParse(values[1].Substring(values[1].LastIndexOf(' ') + 1).Trim(), out rank);
}
// if (channel >= 0 && channel < CECModelConst.ACTCHA_MAX)
// {
// m_Ranks[channel] = (byte)rank;
// }
}
}
if (dwVersion >= 32)
{
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_event_channel, out tagValue);
int.TryParse(tagValue, out m_nEventChannel);
}
if (dwVersion >= 40)
{
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_play_speed, out tagValue);
float.TryParse(tagValue, out m_fPlaySpeed);
}
if (dwVersion >= 49)
{
int iRead = 0;
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_stopchildact, out tagValue);
int.TryParse(tagValue, out iRead);
m_bStopChildrenAct = (iRead != 0);
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_resetmtlonstop, out tagValue);
int.TryParse(tagValue, out iRead);
m_bResetMaterialScale = (iRead != 0);
}
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_act_count, out tagValue);
int.TryParse(tagValue, out nActCount);
for (int i = 0; i < nActCount; i++)
{
ACTION_INFO pInfo = new ACTION_INFO();
pInfo.Load(fileStream, file, dwVersion);
m_ActLst.Add(pInfo);
}
if (m_nLoops == -1 && nActCount == 1 && m_ActLst.Count > 0)
m_ActLst[0].m_nMinLoops = m_ActLst[0].m_nMaxLoops = -1;
if (dwVersion < 7)
{
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_fx_count, out tagValue);
int.TryParse(tagValue, out nFxCount);
for (int i = 0; i < nFxCount; i++)
{
FX_BASE_INFO pInfo = FX_BASE_INFO.LoadFromFile(this, fileStream, file, dwVersion);
if (pInfo == null) continue;
//TODO: Implement Load function
// pInfo.Load(fileStream, file, dwVersion);
m_EventInfoLst.Add(pInfo);
}
}
else
{
// pFile->ReadLine(szLine, AFILE_LINEMAXLEN, &dwReadLen);
// sscanf(szLine, _format_event_count, &nEventCount);
szLine = file.ReadLine();
AAssit.GetStringAfter(szLine, A3DCombinedActionConst._format_event_count, out tagValue);
int.TryParse(tagValue, out nEventCount);
EVENT_INFO pEvent = null;
for (int i = 0; i < nEventCount; i++)
{
pEvent = EVENT_INFO.LoadFromFile(this, null, file, dwVersion);
if (pEvent == null) continue;
m_EventInfoLst.Add(pEvent);
if (pEvent.m_nType > A3DCombinedActionConst.EVENT_TYPE_END)
continue;
// TODO: Maybe we need to implement this in the future
// m_aEventCounter[pEvent.m_nType - A3DCombinedActionConst.EVENT_TYPE_BASE]++;
}
}
}
/*/ TODO: Maybe we need to implement this in the future
ALISTPOSITION pos = m_EventInfoLst.GetHeadPosition();
while (pos) m_EventInfoLst.GetNext(pos)->Init(pDev);
pos = m_ActLst.GetHeadPosition();
while (pos)
{
if (m_ActLst.GetNext(pos)->IsInfinite()//*GetLoops() == -1)
{
m_bInfinite = true;
break;
}
}
//*/
return true;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 474b8a12d05e4c40bc8c0dbb6a01996c
timeCreated: 1741498229
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3eaf31844b1f7448c99ee2c03d26d192
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5197d06d8f33a41b9bf8428bb0e7c245
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,12 @@
using BrewMonster.Scripts.ECModel;
using UnityEngine;
namespace BrewMonster.Scripts.ECModel
{
public class CombineActHolder : MonoBehaviour
{
[SerializeField]
private CombinedActionSO combinedActionSO;
public CombinedActionSO ActionSO => combinedActionSO;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 472c24b873524423f95454131fd11da6
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using ModelViewer.Common;
using UnityEngine;
namespace BrewMonster.Scripts.ECModel
{
[CreateAssetMenu(fileName = "CombinedActionSO", menuName = "BrewMonster/CombinedActionSO")]
public class CombinedActionSO : ScriptableObject
{
public List<A3DCombinedAction> CombinedActionData = new();
public A3DCombinedAction GetActionByName(string name)
{
A3DCombinedAction returnValue = null;
returnValue = CombinedActionData.Find(x => x.m_strName == name);
return returnValue;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: efae13296c7c04fc59ae2d974329383b
+65 -20
View File
@@ -20,6 +20,7 @@ using ModelRenderer.Scripts.Common;
using Unity.VisualScripting;
using DG.Tweening.Plugins;
using PerfectWorld.Scripts.Managers;
using BrewMonster.Scripts.ECModel;
namespace BrewMonster
{
@@ -328,6 +329,11 @@ namespace BrewMonster
// Initialize CECModel for hook system
// 初始化CECModel以支持挂点系统
await InitializePlayerCECModel(profession,gender);
if (m_pPlayerCECModel == null || m_pPlayerModel == null)
{
throw new InvalidOperationException(
$"SetPlayerModel failed to initialize model. profession={profession}, gender={gender}");
}
// Cleanup old model if exists
// 清理旧模型(如果存在)
@@ -653,11 +659,36 @@ namespace BrewMonster
{
m_pPlayerCECModel = new CECModel();
}
m_pPlayerCECModel.SetId(m_PlayerInfo.cid);
if (NPCManager.Instance == null)
{
BMLogger.LogError("[CECPlayer] NPCManager.Instance is null while loading player model.");
return;
}
m_pPlayerCECModel.m_pPlayerModel = await NPCManager.Instance.GetModelPlayer(profession, gender);
if (m_pPlayerCECModel.m_pPlayerModel == null)
{
BMLogger.LogError($"[CECPlayer] GetModelPlayer returned null. profession={profession}, gender={gender}");
return;
}
// Find SkeletonBuilder component on model GameObject
// 在模型GameObject上查找SkeletonBuilder组件
SkeletonBuilder skeletonBuilder = m_pPlayerModel.GetComponentInChildren<SkeletonBuilder>();
CombineActHolder combineActHolder = m_pPlayerModel.GetComponentInChildren<CombineActHolder>();
CombinedActionSO combinedAction = null;
if (combineActHolder != null)
{
try
{
combinedAction = combineActHolder.ActionSO;
}
catch (Exception ex)
{
BMLogger.LogWarning($"[CECPlayer] Failed to read CombineActHolder.ActionSO on '{m_pPlayerModel.name}': {ex.Message}");
}
}
if (skeletonBuilder == null)
{
// SkeletonBuilder might not be built yet, try to find it after a frame
@@ -670,7 +701,10 @@ namespace BrewMonster
// 在CECModel上设置引用
m_pPlayerCECModel.SetSkeletonBuilder(skeletonBuilder);
m_pPlayerCECModel.SetTransform(m_pPlayerModel.transform);
if(combinedAction != null)
{
m_pPlayerCECModel.SetCombinedAction(combinedAction);
}
// Initialize skeleton builder (ensures hooks are available)
// 初始化骨架构建器(确保挂点可用)
m_pPlayerCECModel.InitializeSkeletonBuilder();
@@ -899,6 +933,10 @@ namespace BrewMonster
public void SetPlayerInfor(INFO playinfo)
{
m_PlayerInfo = playinfo;
if (m_pPlayerCECModel != null)
{
m_pPlayerCECModel.SetId(m_PlayerInfo.cid);
}
}
public INFO GetPlayInfo()
@@ -1156,8 +1194,6 @@ namespace BrewMonster
PLAYER_ACTION action = actionConfig;
int weapon_type = GetShowingWeaponType();
string szAct = "";
string szShapeName = "";
GetShapeName(ref szShapeName);
if(m_pActionController != null)
{
if (iAction != (int)PLAYER_ACTION_TYPE.ACT_WOUNDED)
@@ -1400,9 +1436,9 @@ namespace BrewMonster
// // ݲŶλ
// UpdateWeaponHangerPosByAction(iAction);
// }
EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, iTransTime));
// }
// EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, iTransTime));
m_pActionController.PlayNonSkillActionWithName(iAction, szAct, bRestart, iTransTime);
return true;
}
else
@@ -1555,7 +1591,8 @@ namespace BrewMonster
protected void ClearComActFlagAllRankNodes(bool v)
{
EventBus.PublishChannel(m_PlayerInfo.cid, new ClearComActFlagAllRankNodesEvent(v));
//EventBus.PublishChannel(m_PlayerInfo.cid, new ClearComActFlagAllRankNodesEvent(v));
m_pActionController.ClearComActFlagAllRankNodes(v);
}
public bool PlayAttackAction(int nAttackSpeed, ref int attackTime, CECAttackEvent attackEvent)
{
@@ -1572,6 +1609,7 @@ namespace BrewMonster
Debug.Log($"[THN]: PlayAttackAction weapon_type: {weapon_type}");
int nTime1 = 0, nTime2 = 0;
int iAction = (int)PLAYER_ACTION_TYPE.ACT_ATTACK_1 + nRand;
bool bHideFX = false;//!CECOptimize::Instance().GetGFX().CanShowAttack(GetCharacterID(), GetClassID());
PLAYER_ACTION action = m_PlayerActions[iAction];
if (string.IsNullOrEmpty(action.data.ActionPrefix))
@@ -1590,10 +1628,12 @@ namespace BrewMonster
Debug.Log($"[THN]: PlayAttackAction action with weapon type: {weapon_type} and weapon attached: {m_bWeaponAttached}");
szAct = EC_Utility.BuildActionName(action, weapon_type, "起");
int iTransTime = 200;
EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, iTransTime, true));
//EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, iTransTime, true));
m_pActionController.PlayNonSkillActionWithName(iAction, szAct, true, iTransTime, bHideFX, attackEvent,COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX);
szAct = EC_Utility.BuildActionName(action, weapon_type, "落");
queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, true, attackEvent, iTransTime,false);
EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
//EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
m_pActionController.QueueNonSkillActionWithName(iAction, szAct, 0, false, bHideFX);
//PlayNonSkillActionWithName(iAction, szAct, true, 200, true, ref pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX);gagága
/*
if (pRightHandWeapon != null && IsUsingMagicWeapon())
@@ -1631,8 +1671,8 @@ namespace BrewMonster
}
szAct = EC_Utility.BuildActionName(action, weapon_type, "起", szActionMiddleName);
EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, 200, true));
//EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, 200, true));
m_pActionController.PlayNonSkillActionWithName(iAction, szAct, true, 200);
// if (pRightHandWeapon != null && IsUsingMagicWeapon())
// pRightHandWeapon.PlayActionByName(_GenWeaponActionName(szAct, m_iGender), 1.0f, true, 200, true, iAction, bHideFX);
@@ -1640,7 +1680,8 @@ namespace BrewMonster
szAct = EC_Utility.BuildActionName(action, weapon_type, "落", szActionMiddleName);
queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, false, attackEvent, 0, false);
EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
//EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
m_pActionController.QueueNonSkillActionWithName(iAction, szAct, 0, false, false, true, false);
// if (pRightHandWeapon != null && IsUsingMagicWeapon())
// pRightHandWeapon.QueueAction(_GenWeaponActionName(szAct, m_iGender), 0, iAction, false, false, bHideFX);
@@ -1655,7 +1696,8 @@ namespace BrewMonster
szAct = EC_Utility.BuildActionName(stand_action, 0);
int iTranstime = 300;
queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, false, attackEvent, iTranstime, false);
EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
//EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
m_pActionController.QueueNonSkillActionWithName(iAction, szAct, iTranstime, false, false, true, false);
/* QueueNonSkillActionWithName(ACT_FIGHTSTAND, szAct, 300, false, bHideFX, true);
if (pRightHandWeapon != null && IsUsingMagicWeapon())
@@ -2474,13 +2516,13 @@ namespace BrewMonster
public bool PlaySkillAttackActionWithName(int idSkill, string szActName, bool bNoFX = false, CECAttackEvent attackEvent = null)
{
return m_pActionController != null
&& m_pActionController.PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, attackEvent != null ? new bool[] { attackEvent.m_bSignaled } : null, 0);
&& m_pActionController.PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, attackEvent, 0);
}
public bool QueueSkillAttackActionWithName(int idSkill, string szActName, int nTransTime = 200, bool bNoFX = false, bool bResetSpeed = false, bool bResetActFlag = false, bool[] pNewActFlag = null, uint dwNewFlagMode = 0)
public bool QueueSkillAttackActionWithName(int idSkill, string szActName, int nTransTime = 200, bool bNoFX = false, bool bResetSpeed = false, bool bResetActFlag = false, CECAttackEvent attackEvent = null, uint dwNewFlagMode = 0)
{
return m_pActionController != null
&& m_pActionController.QueueSkillAttackActionWithName(idSkill, szActName, nTransTime, bNoFX, bResetSpeed, bResetActFlag, pNewActFlag, dwNewFlagMode);
&& m_pActionController.QueueSkillAttackActionWithName(idSkill, szActName, nTransTime, bNoFX, bResetSpeed, bResetActFlag, attackEvent, dwNewFlagMode);
}
private void GetSkillSectionActionName(ref string szAct, int idSkill, int nSection)
@@ -3307,7 +3349,7 @@ namespace BrewMonster
}
catch (Exception ex)
{
BMLogger.LogError($"CECPlayer::LoadPlayerSkeleton, SetPlayerModel failed: {ex.Message}");
BMLogger.LogError($"CECPlayer::LoadPlayerSkeleton, SetPlayerModel failed: {ex}");
return false;
}
@@ -3814,17 +3856,20 @@ namespace BrewMonster
public string AnimationName;
public int ITransTime;
public bool IsForceStopPrevious;
public PlayActionEvent(string shapeName, string animationName, int iTransTime, bool isForceStopPrevious = false)
public CECAttackEvent AttackEvent;
public PlayActionEvent(string shapeName, string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null)
{
this.AnimationName = shapeName + animationName;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
AttackEvent = attackEvent;
}
public PlayActionEvent(string animationName, int iTransTime, bool isForceStopPrevious = false)
public PlayActionEvent(string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null)
{
this.AnimationName = animationName;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
AttackEvent = attackEvent;
}
}
public struct PLAYER_ACTION
+108 -33
View File
@@ -4,13 +4,14 @@ using System.Runtime.InteropServices;
using BrewMonster.Scripts;
using CSNetwork.GPDataType;
using UnityEngine;
using ModelViewer.Common;
using CombinedActMap = System.Collections.Generic.Dictionary<string, object>;
using CoGfxMap = System.Collections.Generic.Dictionary<string, object>;
using ConvexHullDataArray = System.Collections.Generic.List<object>;
using ECModelHookMap = System.Collections.Generic.Dictionary<string, object>;
using BrewMonster;
using BrewMonster.Scripts.ECModel;
public enum ECMScript
{
enumECMScriptStartAction = 0,
@@ -110,7 +111,7 @@ public class CECModelStaticData
private string m_strSkinModelPath = string.Empty;
private bool m_bAutoUpdate = true;
private bool m_bActMapped;
private readonly CombinedActMap m_ActionMap = new CombinedActMap();
public CombinedActionSO m_ActionMap;
private uint m_dwVersion;
private string m_strHook = string.Empty;
private string m_strCCName = string.Empty;
@@ -163,9 +164,10 @@ public class CECModelStaticData
m_CHAABB.Clear();
}
public bool LoadData(string szModelFile, bool bLoadAdditionalSkin)
public bool LoadData(CombinedActionSO combinedAction)
{
throw new NotImplementedException("CECModelStaticData.LoadData requires the legacy AFile system to be ported.");
m_ActionMap = combinedAction;
return true;
}
public bool Save(string szFile, CECModel pModel)
@@ -183,7 +185,7 @@ public class CECModelStaticData
m_BoneScales.Clear();
m_BoneScaleExArr.Clear();
m_CoGfxMap.Clear();
m_ActionMap.Clear();
m_ActionMap = null;
m_AdditionalSkinLst.Clear();
m_ChildInfoArray.Clear();
m_ConvexHullDataArr.Clear();
@@ -220,7 +222,7 @@ public class CECModelStaticData
public CoGfxMap GetCoGfxMap() => m_CoGfxMap;
public CombinedActMap GetCombinedActMap() => m_ActionMap;
public CombinedActionSO GetCombinedAction() => m_ActionMap;
public string GetPhysFileName() => m_strPhysFileName;
@@ -368,7 +370,7 @@ public class CECModel
private int m_nId = 0;
public GameObject m_pPlayerModel;
private const uint COMACT_FLAG_MODE_NONE = 0;
protected CECModelStaticData m_pMapModel;
protected CECModelStaticData m_pMapModel = new CECModelStaticData();
public SkeletonBuilder m_skeletonBuilder;
private Dictionary<string, Transform> m_hookCache = new Dictionary<string, Transform>();
private Dictionary<string, Transform> m_hangerPositionCache = new Dictionary<string, Transform>();
@@ -391,31 +393,7 @@ public class CECModel
pNode->m_pActFlag = NULL;
}*/
}
public bool QueueAction(CECNPC.INFO iNFO, string szActName, ref bool pNewActFlag, int nTransTime = 200, uint dwUserData = 0, bool bForceStopPrevAct = false, bool bCheckTailDup = false, bool bNoFx = false, bool bResetSpeed = false
/*joslian*/, bool bResetActFlag = false, uint dwNewFlagMode = COMACT_FLAG_MODE_NONE)
{
QueueAction(iNFO, 0, szActName, ref bResetActFlag, nTransTime, dwUserData, bForceStopPrevAct, bCheckTailDup, bNoFx, bResetSpeed/*joslian*/, pNewActFlag, dwNewFlagMode);
return true;
}
public bool QueueAction(CECNPC.INFO iNFO, int nChannel, string szActName, ref bool pNewActFlag, int nTransTime, uint dwUserData/* 0 */, bool bForceStopPrevAct, bool bCheckTailDup, bool bNoFx, bool bResetSpeed
/*joslian*/, bool bResetActFlag, uint dwNewFlagMode)
{
EventBus.PublishChannel(iNFO.nid, new QueueNPCActionEvent(szActName));
return true;
}
public void StopChannelAction(int nChannel, bool bStopAct, bool bStopFx = false)
{
//TODO: Implement StopChannelAction
}
public struct QueueNPCActionEvent
{
public string AnimationName;
public QueueNPCActionEvent(string animationName)
{
AnimationName = animationName;
}
}
public bool HasCHAABB() { return m_pMapModel.HasCHAABB(); }
/// <summary>
@@ -428,7 +406,12 @@ public class CECModel
m_skeletonBuilder = builder;
m_hookCache?.Clear(); // Invalidate cache on model change
}
public void SetCombinedAction(CombinedActionSO combinedAction)
{
//workaround: load data from combined action. C++ load from CMS file aka prefab. we already include those data inside the prefab
//dunno if we lack of any imformation
m_pMapModel.LoadData(combinedAction);
}
/// <summary>
/// Get the SkeletonBuilder component
/// 获取SkeletonBuilder组件
@@ -660,6 +643,98 @@ public class CECModel
child.transform.position += worldAlign;
}
}
//ref cant not be null so seperate into two functions
public bool PlayActionByName(string szActName, float fWeight=1.0f, bool bRestart=true, int nTransTime=200, bool bForceStop=true, uint dwUserData =0, bool bNoFx=false, CECAttackEvent attackEvent = null, uint dwFlagMode = 0)
{
return PlayActionByName(0, szActName, fWeight, bRestart, nTransTime, bForceStop, dwUserData, bNoFx, attackEvent, dwFlagMode);
}
//ref cant not be null so seperate into two functions
public bool QueueAction(string szActName, int nTransTime=200, uint dwUserData=0, bool bForceStopPrevAct=false, bool bCheckTailDup = false, bool bNoFx = false, bool bResetSpeed = false, bool bResetActFlag=false, CECAttackEvent attackEvent=null, uint dwNewFlagMode=0)
{
return QueueAction(0, szActName, nTransTime, dwUserData, bForceStopPrevAct, bCheckTailDup, bNoFx, bResetSpeed ,bResetActFlag, attackEvent, dwNewFlagMode);
}
//Final trigger function
public bool PlayActionByName(int nChannel, string szActName, float fWeight, bool bRestart, int nTransTime, bool bForceStop, uint dwUserData, bool bNoFx, CECAttackEvent attackEvent, uint dwFlagMode = 0)
{
if(szActName == null || szActName == string.Empty)
{
return false;
}
A3DCombinedAction combinedAction = GetComActByName(szActName);
if(combinedAction == null)
{
return false;
}
var actionInfos = combinedAction.m_ActLst;
var isLoop = combinedAction.m_nLoops == 1;
EventBus.PublishChannel(m_nId, new PlayActionEvent(actionInfos[0].m_strName, nTransTime, bForceStop, attackEvent));
for(int i = 1; i < actionInfos.Count; i++)
{
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false));
}
return true;
}
public A3DCombinedAction GetComActByName(string szActName)
{
if(m_pMapModel == null||m_pMapModel.m_ActionMap == null)
{
return null;
}
CombinedActionSO ActionMap = m_pMapModel.m_ActionMap;
return ActionMap.GetActionByName(szActName);
}
//Final trigger function
public bool QueueAction(int nChannel, string szActName, int nTransTime, uint dwUserData, bool bForceStopPrevAct, bool bCheckTailDup, bool bNoFx, bool bResetSpeed, bool bResetActFlag, CECAttackEvent attackEvent=null, uint dwNewFlagMode=0){
if(szActName == null || szActName == string.Empty)
{
return false;
}
A3DCombinedAction combinedAction = GetComActByName(szActName);
if(combinedAction == null)
{
return false;
}
var actionInfos = combinedAction.m_ActLst;
var isLoop = combinedAction.m_nLoops == 1;
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[0].m_strName, null, false, attackEvent, nTransTime, bForceStopPrevAct));
for(int i = 1; i < actionInfos.Count; i++)
{
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false));
}
return true;
}
public bool ClearComActFlagAllRankNodes(bool bSignalCurrent){
EventBus.PublishChannel(m_nId, new ClearComActFlagAllRankNodesEvent(bSignalCurrent));
return false;
}
public uint GetCurActionUserData(){
return 0;
}
public bool QueueAction(CECNPC.INFO iNFO, string szActName, ref bool pNewActFlag, int nTransTime = 200, uint dwUserData = 0, bool bForceStopPrevAct = false, bool bCheckTailDup = false, bool bNoFx = false, bool bResetSpeed = false
/*joslian*/, bool bResetActFlag = false, uint dwNewFlagMode = COMACT_FLAG_MODE_NONE)
{
QueueAction(iNFO, 0, szActName, ref bResetActFlag, nTransTime, dwUserData, bForceStopPrevAct, bCheckTailDup, bNoFx, bResetSpeed/*joslian*/, pNewActFlag, dwNewFlagMode);
return true;
}
public bool QueueAction(CECNPC.INFO iNFO, int nChannel, string szActName, ref bool pNewActFlag, int nTransTime, uint dwUserData/* 0 */, bool bForceStopPrevAct, bool bCheckTailDup, bool bNoFx, bool bResetSpeed
/*joslian*/, bool bResetActFlag, uint dwNewFlagMode)
{
EventBus.PublishChannel(iNFO.nid, new QueueNPCActionEvent(szActName));
return true;
}
public void StopChannelAction(int nChannel, bool bStopAct, bool bStopFx = false)
{
//TODO: Implement StopChannelAction
}
public struct QueueNPCActionEvent
{
public string AnimationName;
public QueueNPCActionEvent(string animationName)
{
AnimationName = animationName;
}
}
public void PlayGfx(string szPath, string szHook, float fScale, bool bFadeOut, A3DVECTOR3 vOffset, float fPitch, float fYaw, float fRot, bool bUseECMHook, uint dwFadeOutTime)
{
if (!bFadeOut)
@@ -75,7 +75,7 @@ namespace BrewMonster
{
// 当前C#只提供默认策略(不拆分上下半身)
// Use default policy in this C# port
m_actionPlayPolicy = new CECPlayerActionPlayPolicy(m_pPlayer, m_pPlayerModel);
m_actionPlayPolicy = new CECPlayerActionPlayDefaultPolicy(m_pPlayer, m_pPlayerModel);
}
private void ReleaseActionPlayPolicy()
@@ -90,16 +90,16 @@ namespace BrewMonster
return false;
}
public bool PlayNonSkillActionWithName(int iAction, string szActName, bool bRestart = true, int nTransTime = 200, bool bNoFx = false, bool[] pActFlag = null, uint dwFlagMode = 0)
public bool PlayNonSkillActionWithName(int iAction, string szActName, bool bRestart = true, int nTransTime = 200, bool bNoFx = false, CECAttackEvent attackEvent = null, uint dwFlagMode = 0)
{
return m_actionPlayPolicy != null
&& m_actionPlayPolicy.PlayNonSkillActionWithName(iAction, szActName, bRestart, nTransTime, bNoFx, pActFlag, dwFlagMode);
&& m_actionPlayPolicy.PlayNonSkillActionWithName(iAction, szActName, bRestart, nTransTime, bNoFx, attackEvent, dwFlagMode);
}
public bool QueueNonSkillActionWithName(int iAction, string szActName, int nTransTime = 200, bool bForceStopPrevAct = false, bool bNoFx = false, bool bResetSpeed = false, bool bResetActFlag = false, bool[] pNewActFlag = null, uint dwNewFlagMode = 0)
public bool QueueNonSkillActionWithName(int iAction, string szActName, int nTransTime = 200, bool bForceStopPrevAct = false, bool bNoFx = false, bool bResetSpeed = false, bool bResetActFlag = false, CECAttackEvent attackEvent = null, uint dwNewFlagMode = 0)
{
return m_actionPlayPolicy != null
&& m_actionPlayPolicy.QueueNonSkillActionWithName(iAction, szActName, nTransTime, bForceStopPrevAct, bNoFx, bResetSpeed, bResetActFlag, pNewActFlag, dwNewFlagMode);
&& m_actionPlayPolicy.QueueNonSkillActionWithName(iAction, szActName, nTransTime, bForceStopPrevAct, bNoFx, bResetSpeed, bResetActFlag, attackEvent, dwNewFlagMode);
}
public bool PlaySkillCastActionWithName(int idSkill, string szActName, bool bNoFX = false)
@@ -112,22 +112,24 @@ namespace BrewMonster
}
return false;
}
public bool PlaySkillAttackActionWithName(int idSkill, string szActName, bool bNoFX = false, bool[] pActFlag = null, uint dwFlagMode = 0)
public bool PlaySkillAttackActionWithName(int idSkill, string szActName, bool bNoFX = false, CECAttackEvent attackEvent = null, uint dwFlagMode = 0)
{
bool? pActFlag = null;
if (m_actionPlayPolicy != null
&& m_actionPlayPolicy.PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, pActFlag, dwFlagMode))
&& m_actionPlayPolicy.PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, attackEvent, dwFlagMode))
{
m_bSkillAttackActionPlayed = true;
return true;
}
return false;
}
public bool QueueSkillAttackActionWithName(int idSkill, string szActName, int nTransTime = 200, bool bNoFX = false, bool bResetSpeed = false, bool bResetActFlag = false, bool[] pNewActFlag = null, uint dwNewFlagMode = 0)
public bool QueueSkillAttackActionWithName(int idSkill, string szActName, int nTransTime = 200, bool bNoFX = false, bool bResetSpeed = false, bool bResetActFlag = false, CECAttackEvent attackEvent = null, uint dwNewFlagMode = 0)
{
bool? pNewActFlag = null;
return m_actionPlayPolicy != null
&& m_actionPlayPolicy.QueueSkillAttackActionWithName(idSkill, szActName, nTransTime, bNoFX, bResetSpeed, bResetActFlag, pNewActFlag, dwNewFlagMode);
&& m_actionPlayPolicy.QueueSkillAttackActionWithName(idSkill, szActName, nTransTime, bNoFX, bResetSpeed, bResetActFlag, attackEvent, dwNewFlagMode);
}
public bool PlayWoundActionWithName(string szActName)
@@ -239,7 +239,7 @@ namespace BrewMonster
}
// Non-skill action
public virtual bool PlayNonSkillActionWithName(int iAction, string szActName, bool bRestart, int nTransTime, bool bNoFx, bool[] pActFlag, uint dwFlagMode)
public virtual bool PlayNonSkillActionWithName(int iAction, string szActName, bool bRestart, int nTransTime, bool bNoFx, CECAttackEvent attackEvent, uint dwFlagMode)
{
if (m_pPlayer == null || string.IsNullOrEmpty(szActName))
{
@@ -250,11 +250,11 @@ namespace BrewMonster
return true;
}
public virtual bool QueueNonSkillActionWithName(int iAction, string szActName, int nTransTime, bool bForceStopPrevAct, bool bNoFx, bool bResetSpeed, bool bResetActFlag, bool[] pNewActFlag, uint dwNewFlagMode)
public virtual bool QueueNonSkillActionWithName(int iAction, string szActName, int nTransTime, bool bForceStopPrevAct, bool bNoFx, bool bResetSpeed, bool bResetActFlag, CECAttackEvent attackEvent = null, uint dwNewFlagMode = 0)
{
// 当前实现:直接播放(队列行为由上层系统处理)
// Current: just play, queue behavior handled by upper systems
return PlayNonSkillActionWithName(iAction, szActName, false, nTransTime, bNoFx, pNewActFlag, dwNewFlagMode);
return PlayNonSkillActionWithName(iAction, szActName, false, nTransTime, bNoFx, attackEvent, dwNewFlagMode);
}
// Skill actions
@@ -274,7 +274,7 @@ namespace BrewMonster
return true;
}
public virtual bool PlaySkillAttackActionWithName(int idSkill, string szActName, bool bNoFX, bool[] pActFlag, uint dwFlagMode)
public virtual bool PlaySkillAttackActionWithName(int idSkill, string szActName, bool bNoFX, CECAttackEvent attackEvent, uint dwFlagMode)
{
if (m_pPlayer == null || string.IsNullOrEmpty(szActName))
{
@@ -290,9 +290,9 @@ namespace BrewMonster
return true;
}
public virtual bool QueueSkillAttackActionWithName(int idSkill, string szActName, int nTransTime, bool bNoFX, bool bResetSpeed, bool bResetActFlag, bool[] pNewActFlag, uint dwNewFlagMode)
public virtual bool QueueSkillAttackActionWithName(int idSkill, string szActName, int nTransTime, bool bNoFX, bool bResetSpeed, bool bResetActFlag, CECAttackEvent attackEvent, uint dwNewFlagMode)
{
return PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, pNewActFlag, dwNewFlagMode);
return PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, attackEvent, dwNewFlagMode);
}
public virtual bool PlayWoundActionWithName(string szActName)
@@ -345,4 +345,120 @@ namespace BrewMonster
return -1;
}
}
class CECPlayerActionPlayDefaultPolicy : CECPlayerActionPlayPolicy{
public CECPlayerActionPlayDefaultPolicy(CECPlayer pPlayer, CECModel pPlayerModel): base(pPlayer, pPlayerModel){}
public override bool PlayNonSkillActionWithName(int iAction, string szActName, bool bRestart, int nTransTime, bool bNoFx, CECAttackEvent attackEvent, uint dwFlagMode)
{
ClearComActFlagAllRankNodes(true);
bool check = GetModel()!=null
&& GetModel().PlayActionByName(szActName, 1.0f, bRestart, nTransTime, true, (uint)iAction , bNoFx, attackEvent, dwFlagMode);
if (!check)
{
BMLogger.LogError($"Fall back to base implementation: {szActName} failed");
//fallback to base implementation(which is non policy based)
base.PlayNonSkillActionWithName(iAction, szActName, bRestart, nTransTime, bNoFx, attackEvent, dwFlagMode);
}
return check;
}
public override bool QueueNonSkillActionWithName(int iAction, string szActName, int nTransTime, bool bForceStopPrevAct, bool bNoFx, bool bResetSpeed, bool bResetActFlag, CECAttackEvent attackEvent = null, uint dwNewFlagMode = 0)
{
bool check = GetModel()!=null
&& GetModel().QueueAction(szActName, nTransTime, (uint)iAction, bForceStopPrevAct, false, bNoFx, bResetSpeed, bResetActFlag, attackEvent, dwNewFlagMode);
if (!check)
{
BMLogger.LogError($"Fall back to base implementation: {szActName} failed");
//fallback to base implementation(which is non policy based)
base.QueueNonSkillActionWithName(iAction, szActName, nTransTime, bForceStopPrevAct, bNoFx, bResetSpeed, bResetActFlag, attackEvent, dwNewFlagMode);
}
return check;
}
public override bool PlaySkillCastActionWithName(int idSkill, string szActName, bool bNoFX){
if (IsMoving()){
return false;
}
ClearComActFlagAllRankNodes(true);
bool check = GetModel()!=null
&& GetModel().PlayActionByName(szActName, 1.0f, true, 200, true);
if (!check)
{
BMLogger.LogError($"Fall back to base implementation: {szActName} failed");
//fallback to base implementation(which is non policy based)
base.PlaySkillCastActionWithName(idSkill, szActName, bNoFX);
}
return check;
}
public override bool PlaySkillAttackActionWithName(int idSkill, string szActName, bool bNoFX, CECAttackEvent attackEvent, uint dwFlagMode){
if (IsMoving()){
return false;
}
//LOG_DEBUG_INFO(AString().Format("PlaySkillAttackActionWithName:%s", szActName));
ClearComActFlagAllRankNodes(true);
bool check = GetModel() != null
&& GetModel().PlayActionByName(szActName, 1.0f, true, 200, true, (uint)PLAYER_ACTION_TYPE.ACT_CASTSKILL, bNoFX, attackEvent, dwFlagMode);
if (!check)
{
BMLogger.LogError($"Fall back to base implementation: {szActName} failed");
//fallback to base implementation(which is non policy based)
base.PlaySkillAttackActionWithName(idSkill, szActName, bNoFX, attackEvent, dwFlagMode);
}
return check;
}
public override bool QueueSkillAttackActionWithName(int idSkill, string szActName, int nTransTime, bool bNoFX, bool bResetSpeed, bool bResetActFlag, CECAttackEvent attackEvent, uint dwNewFlagMode){
//LOG_DEBUG_INFO(AString().Format("QueueSkillAttackActionWithName:%s", szActName));
bool check = GetModel()!=null
&& GetModel().QueueAction(szActName, nTransTime, (int)PLAYER_ACTION_TYPE.ACT_CASTSKILL, false, false, bNoFX, bResetSpeed, bResetActFlag, attackEvent, dwNewFlagMode);
if (!check)
{
BMLogger.LogError($"Fall back to base implementation: {szActName} failed");
//fallback to base implementation(which is non policy based)
base.QueueSkillAttackActionWithName(idSkill, szActName, nTransTime, bNoFX, bResetSpeed, bResetActFlag, attackEvent, dwNewFlagMode);
}
return check;
}
public override bool PlayWoundActionWithName(string szActName){
//A3DSkinModel not implemented in C#
return false;
// //LOG_DEBUG_INFO(AString().Format("PlayWoundActionWithName:%s", szActName));
// return GetModel()
// && GetModel()->GetA3DSkinModel()->PlayActionByName(szActName, CECModel::ACTCHA_WOUND, 1, 0, false);
}
public override void ClearComActFlagAllRankNodes(bool bSignalCurrent){
if (GetModel()!=null){
GetModel().ClearComActFlagAllRankNodes(bSignalCurrent);
}
}
public override void StopChannelAction(){
if (GetModel()!=null){
ClearComActFlagAllRankNodes(true);
GetModel().StopChannelAction(0, true);
}
}
public override void StopSkillAction(){
if (GetModel()!=null && IsPlayingCastingSkillAction()){
StopChannelAction();
}
}
public override bool IsPlayingAction(int iAction){
if (GetModel()!=null){
return (int)GetModel().GetCurActionUserData() == iAction;
}
return false;
}
public override bool IsPlayingMoveAction(){
if (GetModel()!=null){
return IsMoveAction((int)GetModel().GetCurActionUserData());
}
return false;
}
public override int GetLowerBodyAction(){
if (GetModel()!=null){
return (int)GetModel().GetCurActionUserData();
}
return -1;
}
}
}
@@ -86,9 +86,9 @@ namespace BrewMonster
public Skill150Stub() : base(150)
{
cls = 4;
name = "˺ҧ";
nativename = "˺ҧ";
icon = "˺ҧ";
name = "撕咬";
nativename = "撕咬";
icon = "撕咬";
max_level = 10;
type = 1;
apcost = 20;
@@ -109,11 +109,40 @@ namespace BrewMonster
long_range = 0;
restrict_corpse = 0;
allow_forms = 2;
effect = "˺ҧ";
effect = "撕咬";
doenchant = 1;
dobless = 0;
commoncooldown = 0;
commoncooldowntime = 0;
m_szFlyGfxPath = string.Empty;
m_szHitGrndGfxPath = string.Empty;
m_szHitGfxPath = "策划联入/人物技能/击中/撕咬击中.gfx";
// GFX Movement and Behavior Parameters / GFX移动和行为参数
m_MoveMode = (GfxMoveMode)0;
m_TargetMode = (GfxTargetMode)0;
m_AttFlyMode = (GfxAttackMode)0;
m_AttHitMode = (GfxAttackMode)0;
m_dwFlyTime = 0;
m_bTraceTarget = false;
m_FlyClusterCount = 1;
m_FlyClusterInterval = 0;
m_HitClusterCount = 1;
m_HitClusterInterval = 0;
m_bOneHit = true;
m_bFadeOut = false;
m_bRelScl = true;
m_fDefTarScl = 1.5f;
// Area parameters (commented out) / 区域参数(已注释)
// m_bArea = false;
// m_Shape = (EmitShape)0;
// m_vSize = new Vector3(0.0f, 0.0f, 0.0f);
// Param (commented out) / 参数(已注释)
// m_paramType = (GfxSkillValType)1;
// m_param = new GFX_SKILL_PARAM();
// m_param.nVal = 0;
restrict_weapons.Add(9);
restrict_weapons.Add(0);
range = new Range();
@@ -144,10 +173,7 @@ namespace BrewMonster
#if SKILL_CLIENT
public override int GetIntroduction(Skill skill, StringBuilder buffer, string format)
{
buffer.Append(GPDataTypeHelper.FormatSkillIntroduction(format,
skill.GetLevel(),
3.2f + 4 * skill.GetLevel(),
5 * skill.GetLevel() * skill.GetLevel() + 119.1f * skill.GetLevel() + 209.1f));
buffer.Append(GPDataTypeHelper.ReplacePercentD(format));
return buffer.Length;
}
#endif
+1 -1
View File
@@ -370,7 +370,7 @@ namespace BrewMonster
if (!await LoadPlayerSkeleton(true))
{
BMLogger.LogError("HoangDev CECHostPlayer::LoadResources, Failed to load skeleton");
BMLogger.LogError($"HoangDev CECHostPlayer::LoadResources, Failed to load skeleton. {m_strName}");
return false;
}
+33 -88
View File
@@ -11,6 +11,7 @@ namespace BrewMonster
{
public string AnimationName;
public bool IsForceStopPrevious;
public CECAttackEvent AttackEvent;
}
public class PlayerVisual : MonoBehaviour
{
@@ -36,11 +37,17 @@ namespace BrewMonster
//when this trigger, clear all the animation in the queue which in the same layer of animancer
if (_animationQueue.Count > 0)
{
_animationQueue.Enqueue(new AnimationQueue { AnimationName = @event.AnimationName, IsForceStopPrevious = @event.IsForceStopPrevious });
_animationQueue.Enqueue(new AnimationQueue
{
AnimationName = @event.AnimationName,
IsForceStopPrevious = @event.IsForceStopPrevious,
AttackEvent = @event.AttackEvent
});
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
return;
}
InternalPlayAnimation(@event.AnimationName, @event.ITransTime);
ApplyAttackSignalOnAnimationEnd(@event.AttackEvent);
}
public void InitPlayerEventDoneHandler()
{
@@ -127,7 +134,12 @@ namespace BrewMonster
public bool EnqueueAnimation(QueueActionEvent @event)
{
if (namedAnimancer == null) return false;
_animationQueue.Enqueue(new AnimationQueue { AnimationName = @event.AnimationName, IsForceStopPrevious = @event.IsForceStopPrevious });
_animationQueue.Enqueue(new AnimationQueue
{
AnimationName = @event.AnimationName,
IsForceStopPrevious = @event.IsForceStopPrevious,
AttackEvent = null
});
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
if (!isHit)
@@ -164,6 +176,19 @@ namespace BrewMonster
var animationQueue = _animationQueue.Dequeue();
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
InternalPlayAnimation(animationQueue.AnimationName);
ApplyAttackSignalOnAnimationEnd(animationQueue.AttackEvent);
}
private void ApplyAttackSignalOnAnimationEnd(CECAttackEvent attackEvent)
{
if (attackEvent == null || _currentState == null)
{
return;
}
_currentState.Events.OnEnd = () =>
{
attackEvent.m_bSignaled = true;
};
}
void ApplyDamage()
{
@@ -190,97 +215,17 @@ namespace BrewMonster
/// <param name="fadeMode"></param>
private void InternalPlayAnimation(string animationName, float duration = FadeTime, FadeMode fadeMode = FadeMode)
{
//Debug.Log($"InternalPlayAnimation: animationName FUllNAME={animationName}");
string fullName = animationName;
string removeShapeName = animationName;
string removeFlyName = animationName;
if(animationName.Contains("_"))
{
int underscoreIndex = animationName.IndexOf('_');
removeShapeName = animationName.Substring(underscoreIndex + 1);
}
bool isState = namedAnimancer.States.TryGet(removeShapeName, out var existingState) ? true : false;
bool isState = namedAnimancer.States.TryGet(animationName, out var existingState) ? true : false;
if (isState)
{
_currentState = namedAnimancer.TryPlay(removeShapeName, duration / 1000, fadeMode);
_currentAnimationName = removeShapeName;
_currentState = namedAnimancer.TryPlay(animationName, duration / 1000, fadeMode);
_currentAnimationName = animationName;
//Debug.Log($"InternalPlayAnimation: removeShapeName 1 TriggerName={removeShapeName}");
return;
}
bool isState2 = namedAnimancer.States.TryGet(fullName, out var existingState2) ? true : false;
if (isState2)
{
_currentState = namedAnimancer.TryPlay(fullName, duration / 1000, fadeMode);
_currentAnimationName = fullName;
return;
}
string fullName2 = fullName;
//if contain 空拳 change it to 通用 apply to full name and removeShapeName
if (fullName2.Contains("空拳"))
{
fullName2 = fullName2.Replace("空拳", "通用");
removeShapeName = removeShapeName.Replace("空拳", "通用");
}
bool isState3 = namedAnimancer.States.TryGet(removeShapeName, out var existingState3) ? true : false;
if (isState3)
{
_currentState = namedAnimancer.TryPlay(removeShapeName, duration / 1000, fadeMode);
_currentAnimationName = removeShapeName;
return;
}
bool isState4 = namedAnimancer.States.TryGet(fullName2, out var existingState4) ? true : false;
if (isState4)
{
_currentState = namedAnimancer.TryPlay(fullName2, duration / 1000, fadeMode);
_currentAnimationName = fullName2;
return;
}
int index = removeShapeName.IndexOf("空中");
if (index >= 0 && removeShapeName.Length >= index + 4)
{
removeFlyName = removeShapeName.Remove(index + 2, 2);
}
else
{
removeFlyName = removeShapeName;
}
bool isState5 = namedAnimancer.States.TryGet(removeFlyName, out var existingState5) ? true : false;
if (isState5)
{
_currentState = namedAnimancer.TryPlay(removeFlyName, duration / 1000, fadeMode);
_currentAnimationName = removeFlyName;
return;
}
if (fullName2.Contains("_通用"))
{
fullName2 = fullName2.Replace("_通用", "");
removeShapeName = removeShapeName.Replace("_通用", "");
removeFlyName = removeFlyName.Replace("_通用", "");
}
bool isState6 = namedAnimancer.States.TryGet(fullName2, out var existingState6) ? true : false;
if (isState6)
{
_currentState = namedAnimancer.TryPlay(fullName2, duration / 1000, fadeMode);
_currentAnimationName = fullName2;
return;
}
bool isState8 = namedAnimancer.States.TryGet(removeShapeName, out var existingState8) ? true : false;
if (isState8)
{
_currentState = namedAnimancer.TryPlay(removeShapeName, duration / 1000, fadeMode);
_currentAnimationName = removeShapeName;
return;
}
bool isState7 = namedAnimancer.States.TryGet(removeFlyName, out var existingState7) ? true : false;
if (isState7)
{
_currentState = namedAnimancer.TryPlay(removeFlyName, duration / 1000, fadeMode);
_currentAnimationName = removeFlyName;
return;
}
BMLogger.LogError($"Null name animation: {fullName}");
BMLogger.LogError($"Null name animation: {animationName}");
}
/// <summary>