Merge branch 'develop' into feature/gfx-action

This commit is contained in:
vuong dinh hoang
2026-05-06 09:26:26 +07:00
23 changed files with 604 additions and 171 deletions
@@ -2655,6 +2655,11 @@ MonoBehaviour:
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 34322d27488583140acae7497762ccbc
m_Address: minimaps/161
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 343d97392dfbe4113b96ae477e3867eb
m_Address: litmodels/a61/7/litmodel_1007.bmd
m_ReadOnly: 0
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f809da92f7a74929fe4911b38ecddcf5bfe0fa9f667ad04f6c927511246975ae
size 308267
oid sha256:fdca6f6915cfc055b350581a53fbc4d2e19ab522da9eb954295d71d45d8aed58
size 303299
@@ -7,6 +7,7 @@ using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.U2D;
namespace BrewMonster.Scripts
{
@@ -18,6 +19,7 @@ namespace BrewMonster.Scripts
private Dictionary<string, AsyncOperationHandle<GameObject>> _loadedPrefabAssets = new();
private Dictionary<string, AsyncOperationHandle<TextAsset>> _loadedTextAssets = new();
private Dictionary<string, AsyncOperationHandle<AudioClip>> _loadedAudioAssets = new();
private Dictionary<string, AsyncOperationHandle<SpriteAtlas>> _loadedSpriteAtlasAssets = new();
private Dictionary<string, int> _loadedAssetReferenceCount = new();
@@ -174,6 +176,56 @@ namespace BrewMonster.Scripts
}
AsyncOperationHandle<SpriteAtlas> _loadedSpriteAtlasHandle;
/// <summary>Load a sprite atlas asynchronously.</summary>
public async Task<SpriteAtlas> LoadSpriteAtlasAsync(string assetPath)
{
// increase the reference count of the asset.
if (!_loadedAssetReferenceCount.ContainsKey(assetPath))
{
_loadedAssetReferenceCount[assetPath] = 0;
}
_loadedAssetReferenceCount[assetPath]++;
// remove the asset from the release timestamp dictionary. So it won't be released.
RemoveFromReleaseAssetDictionary(assetPath);
if (_loadedSpriteAtlasAssets.TryGetValue(assetPath, out _loadedSpriteAtlasHandle))
{
if (_loadedTextAssetHandle.IsValid() && _loadedTextAssetHandle.Result != null)
{
BMLogger.Log($"AddressableManager: Loaded text asset from cache: {assetPath}");
return _loadedSpriteAtlasHandle.Result;
}
else
{
BMLogger.Log($"AddressableManager: Text asset handle is invalid or result is null, need to load new one: {assetPath}");
}
}
try
{
var handle = Addressables.LoadAssetAsync<SpriteAtlas>(assetPath);
await handle.Task;
if (handle.OperationException != null)
{
BMLogger.Log($"AddressableManager: Failed to load Sprite Atlas '{assetPath}': {handle.OperationException.Message} {handle.OperationException.StackTrace}");
#if UNITY_EDITOR
_invalidAssetPaths.Add(assetPath);
#endif
return null;
}
_loadedSpriteAtlasAssets[assetPath] = handle;
return handle.Result;
}
catch (Exception e)
{
BMLogger.LogError($"AddressableManager: Failed to load SpriteAtlas '{assetPath}': {e}");
return null;
}
}
AsyncOperationHandle<GameObject> _loadedPrefabHandle;
/// <summary>
/// Load an asset asynchronously. The address should look like this: "models/npcs/npc/魅灵首领/魅灵首领/魅灵首领.prefab"
@@ -1,8 +1,8 @@
using UnityEngine;
using System;
using BrewMonster.UI;
using CSNetwork;
//TODO: [DUCK] EC_Game shouldn't be in BrewMonster.Network namespace, it should be in BrewMonster.Scripts namespace.
namespace BrewMonster.Network
{
public partial class EC_Game
@@ -78,7 +78,7 @@ namespace BrewMonster.Network
// return m_dwRealTickTime;
// return Time.realtimeSinceStartup;
// 0.018f is value to make mining process run fit with player animation. if have any issue => discussing with Mr. Drimdar
return (Time.unscaledDeltaTime - 0.018f) * 1000f;//-0.001f
return Mathf.Abs(Time.unscaledDeltaTime - 0.018f) * 1000f;//-0.001f
}
}
}
@@ -4,7 +4,7 @@ using System.Collections.Generic;
using BrewMonster.Network;
using BrewMonster.Utils;
using Random = UnityEngine.Random;
using UnityEngine;
namespace BrewMonster.Scripts
{
using WorkList = System.Collections.Generic.List<CECEPWork>;
@@ -279,61 +279,56 @@ namespace BrewMonster.Scripts
// class CECEPWorkSpell
public class CECEPWorkSpell : CECEPWork
{
CECCounter m_SkillCnt;
CECSkill m_pCurSkill;
int m_idCurSkillTarget;
public CECEPWorkSpell(CECEPWorkMan pWorkMan, uint dwPeriod, CECSkill pSkill, int target) : base(CECEPWork.EP_work_ID.WORK_SPELL, pWorkMan)
CECCounter m_SkillCnt;
CECSkill m_pCurSkill;
int m_idCurSkillTarget;
public CECEPWorkSpell(CECEPWorkMan pWorkMan, uint dwPeriod, CECSkill pSkill, int target) : base(EP_work_ID.WORK_SPELL, pWorkMan)
{
m_pCurSkill = pSkill;
m_idCurSkillTarget = target;
if (m_SkillCnt == null){
m_SkillCnt = new CECCounter();
}
m_SkillCnt.SetPeriod(dwPeriod);
m_SkillCnt.Reset();
}
// public CECEWorkSpell(){
// if (m_pCurSkill != null){
// m_pCurSkill = null;
// }
// }
public override void Start()
{
GetPlayer().m_iMoveMode = (int)MoveMode.MOVE_STAND;
GetPlayer().m_iMoveMode = (int)MoveMode.MOVE_STAND;
}
public override void Tick(float dwDeltaTime)
{
//TO DO: fix later
//GetPlayer().m_FightCnt.IncCounter(dwDeltaTime);
//int iRealTime = g_pGame.GetRealTickTime();
//if (m_SkillCnt.IncCounter(iRealTime))
//{
// Finish();
//}
//else
//{
// if (m_idCurSkillTarget != 0)
// {
// GetPlayer().TurnFaceTo(m_idCurSkillTarget);
// }
// if (GetPlayer().IsPlayingMoveAction() && !GetPlayer().IsWorkMoveRunning())
// { // ƶʩֹͣƶһֱڳƶ˴ͣ
// GetPlayer().PlayAction(CECPlayer::ACT_FIGHTSTAND);
// }
// if (!GetPlayer().IsPlayingAction())
// {
// GetPlayer().PlayAction((int)CECPlayer.PLAYER_ACTION_TYPE.ACT_FIGHTSTAND); // ܶɺ󡢲սվ
// }
//}
public override void Tick(float dwDeltaTime){
GetPlayer().m_FightCnt.IncCounter(dwDeltaTime *1000);
float iRealTime = EC_Game.GetRealTickTime();
if (m_SkillCnt.IncCounter(Time.unscaledDeltaTime * 1000f, out float fCounter, out float fPeriod)){
Finish();
}else{
if (m_idCurSkillTarget != 0){
GetPlayer().TurnFaceTo(m_idCurSkillTarget);
}
if (GetPlayer().IsPlayingMoveAction() && !GetPlayer().IsWorkMoveRunning()){ // ´ÓÒÆ¶¯Ê©·¨µ½Í£Ö¹Òƶ¯¡¢»áÒ»Ö±ÔÚ³ÖÐø²¥·ÅÒÆ¶¯¶¯×÷£¬´Ë´¦ÔÝÍ£
GetPlayer().PlayAction((int)PLAYER_ACTION_TYPE.ACT_FIGHTSTAND);
}
if (!GetPlayer().IsPlayingAction()){
GetPlayer().PlayAction((int)PLAYER_ACTION_TYPE.ACT_FIGHTSTAND); // ¼¼Äܶ¯×÷²¥·ÅÍê³Éºó¡¢²¥·ÅÕ½¶·Õ¾Á¢¶¯×÷
}
}
}
public override void Cancel()
{
//TO DO: fix later
//if (m_pCurSkill != null)
//{
// m_pCurSkill = null;
//}
//m_idCurSkillTarget = 0;
//GetPlayer().StopSkillAttackAction();
//GetPlayer().TurnFaceTo(0);
public override void Cancel(){
if (m_pCurSkill != null){
m_pCurSkill = null;
}
m_idCurSkillTarget = 0;
GetPlayer().StopSkillAttackAction();
GetPlayer().TurnFaceTo(0);
}
public CECSkill GetSkill()
{
public CECSkill GetSkill(){
return m_pCurSkill;
}
}
}
public class CECEPWorkMan
@@ -898,4 +893,43 @@ namespace BrewMonster.Scripts
//GetPlayer().StopModelMove(GetPlayer().m_vStopDir, g_vAxisY, 150);
}
};
public class CECEPWorkFlashMove : CECEPWork
{
private A3DVECTOR3 m_vServerPos;
private float m_fMoveSpeed;
public CECEPWorkFlashMove(CECEPWorkMan pWorkMan, A3DVECTOR3 vServerPos, float fMoveSpeed)
: base(CECEPWork.EP_work_ID.WORK_FLASHMOVE, pWorkMan)
{
m_vServerPos = vServerPos;
m_fMoveSpeed = fMoveSpeed;
}
public override void Start()
{
GetPlayer().m_iMoveMode = (int)MoveMode.MOVE_MOVE;
GetPlayer().PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_START, true, 0);
GetPlayer().PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_LOOP, false, 0, true);
}
public override void Tick(float dwDeltaTime)
{
base.Tick(dwDeltaTime);
A3DVECTOR3 vCurPos = GetPlayer().GetPos();
A3DVECTOR3 vDir = m_vServerPos - vCurPos;
float fDist = vDir.Normalize();
float fMoveDelta = m_fMoveSpeed * dwDeltaTime * 0.001f;
if (fMoveDelta >= fDist)
{
GetPlayer().SetPos(m_vServerPos);
Finish();
}
else
{
vCurPos = vCurPos + vDir * fMoveDelta;
GetPlayer().SetPos(vCurPos);
}
}
public override void Cancel()
{
GetPlayer().SetPos(m_vServerPos);
}
}
}
@@ -111,6 +111,7 @@ namespace BrewMonster
private bool m_bCooling;
private int m_iChargeCnt;
private int m_iChargeMax;
public int ChargeMax => m_iChargeMax;
private bool m_bCharging;
private static CECSkillStr l_SkillStr = new CECSkillStr();
@@ -40,6 +40,13 @@ public class CECCounter
m_dwCounter += dwCounter;
return (m_dwCounter >= m_dwPeriod) ? true : false;
}
public bool IncCounter(float dwCounter, out float fCounter, out float fPeriod)
{
m_dwCounter += dwCounter;
fCounter = m_dwCounter;
fPeriod = m_dwPeriod;
return (m_dwCounter >= m_dwPeriod) ? true : false;
}
// Decrease counter
public void DecCounter(float dwCounter)
+33 -10
View File
@@ -2491,7 +2491,6 @@ namespace BrewMonster
else
sprintf(szAct, "%s_¿ÕÖзɽ£_Ò÷³ª_%s", data.action_prefix, data.action_weapon_suffix[weapon_type].suffix);*/
}
bool bHideFX = false; /*!CECOptimize::Instance().GetGFX().CanShowCast(GetCharacterID(), GetClassID());*/
if (!PlaySkillCastActionWithName(idSkill, szAct, bHideFX))
{
@@ -2525,7 +2524,8 @@ namespace BrewMonster
public bool IsHangerOn() { return m_bHangerOn; }
public bool IsPlayingAction()
{
return true /*GetLowerBodyAction() != -1*/ ;
var check = GetLowerBodyAction() != -1 ;
return check;
}
public bool PlaySkillCastActionWithName(int idSkill, string szActName, bool bNoFX/* =false */)
{
@@ -4052,26 +4052,32 @@ namespace BrewMonster
public struct PlayActionEvent
{
public ChannelAct ChannelAct;
public string AnimationName;
public int ITransTime;
public bool IsForceStopPrevious;
public bool IsLoop;
public CECAttackEvent AttackEvent;
public PlayActionEvent(string shapeName, string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null, bool isLoop = false)
{
this.AnimationName = shapeName + animationName;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
AttackEvent = attackEvent;
IsLoop = isLoop;
}
public int Rank;
public PlayActionEvent(string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null, bool isLoop = false)
{
this.ChannelAct = null;
this.AnimationName = animationName;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
AttackEvent = attackEvent;
IsLoop = isLoop;
Rank = 0;
}
public PlayActionEvent(ref ChannelAct channelAct, string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null, bool isLoop = false, int rank = 0)
{
this.ChannelAct = channelAct;
this.AnimationName = animationName;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
AttackEvent = attackEvent;
IsLoop = isLoop;
Rank = rank;
}
}
public struct PLAYER_ACTION
@@ -4081,6 +4087,7 @@ namespace BrewMonster
};
public class QueueActionEvent
{
public ChannelAct ChannelAct;
public string AnimationName;
public int ITransTime;
public Action<bool, CECAttackEvent> SetFlag;
@@ -4088,9 +4095,11 @@ namespace BrewMonster
public bool IsHitAnim;
public bool IsForceStopPrevious;
public bool IsLoop;
public int Rank;
public QueueActionEvent(string animationName, Action<bool, CECAttackEvent> setFlag, bool isHitAnim,
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false, bool isLoop = false)
{
this.ChannelAct = null;
this.AnimationName = animationName;
SetFlag = setFlag;
IsHitAnim = isHitAnim;
@@ -4098,6 +4107,20 @@ namespace BrewMonster
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
IsLoop = isLoop;
Rank = 0;
}
public QueueActionEvent(ref ChannelAct channelAct, string animationName, Action<bool, CECAttackEvent> setFlag, bool isHitAnim,
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false, bool isLoop = false, int rank = 0)
{
this.ChannelAct = channelAct;
this.AnimationName = animationName;
SetFlag = setFlag;
IsHitAnim = isHitAnim;
AttackEvent = attackEvent;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
IsLoop = isLoop;
Rank = rank;
}
public void SetData(string shapeName, string animationName, Action<bool, CECAttackEvent> setFlag, bool isHitAnim,
+160 -14
View File
@@ -10,6 +10,7 @@ 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 ActQueue = System.Collections.Generic.List<A3DCombActDynData>;
using BrewMonster;
using BrewMonster.Scripts.ECModel;
using Unity.VisualScripting.FullSerializer;
@@ -45,7 +46,110 @@ public enum ECMScriptVar
enumECMVarSelf,
enumECMVarCount
}
public enum ActionChannel
{
ACTCHA_DEFAULT = 0,
ACTCHA_WOUND = 1,
ACTCHA_MAX = 16, // Keep this value same with A3DSkinModelActionCore::ACTCHA_MAX
};
public class A3DCombActDynData
{
public A3DCombinedAction combinedAction;
public int nChannel;
public int m_dwUserData;
public bool IsAllActionFinished => m_EventNames.Count == 0;
public List<string> m_EventNames = new List<string>();
public void SetUserData(int dwUserData) { m_dwUserData = dwUserData; }
public int GetUserData() { return m_dwUserData; }
}
public class ChannelActNode
{
public byte m_Rank;
public A3DCombActDynData m_pActive;
public bool m_pActFlag;
public int m_dwFlagMode;
public ActQueue m_ActQueue;
public ChannelActNode()
{
m_Rank = 0;
m_pActive = null;
m_pActFlag = false;
m_dwFlagMode = 0;
if(m_ActQueue == null)
{
m_ActQueue = new ActQueue();
}
m_ActQueue.Clear();
}
public void RemoveQueuedActs()
{
m_ActQueue.Clear();
}
}
public class ChannelAct
{
public List<ChannelActNode> m_RankNodes;
public ChannelAct()
{
Release();
}
public void Release()
{
if(m_RankNodes == null)
{
m_RankNodes = new List<ChannelActNode>();
}
m_RankNodes.Clear();
}
public ChannelActNode GetNodeByRank(byte rank)
{
if(m_RankNodes == null || m_RankNodes.Count == 0)
{
return null;
}
foreach(var node in m_RankNodes)
{
if(node.m_Rank == rank)
{
return node;
}
}
return null;
}
public ChannelActNode GetHighestRankNode()
{
if(m_RankNodes == null || m_RankNodes.Count == 0)
{
return null;
}
ChannelActNode highestNode = m_RankNodes[0];
foreach(var node in m_RankNodes)
{
if(node.m_Rank > highestNode.m_Rank)
{
highestNode = node;
}
}
return highestNode;
}
public ChannelActNode RemoveNodeByRank(byte rank)
{
foreach(var node in m_RankNodes)
{
if(node.m_Rank == rank)
{
m_RankNodes.Remove(node);
return node;
}
}
return null;
}
};
public static class CECModelConstants
{
public const int ECM_SCRIPT_MAX_VAR_COUNT = 8;
@@ -382,6 +486,15 @@ public class CECModel
private Transform m_transform;
public bool InheritParentId() => m_bInheritParentId;
public void SetId(int nId) => m_nId = nId;
//16 is
ChannelAct[] m_ChannelActs = InitChannelActs();
private static ChannelAct[] InitChannelActs()
{
var acts = new ChannelAct[(int)ActionChannel.ACTCHA_MAX];
for (int i = 0; i < acts.Length; i++)
acts[i] = new ChannelAct();
return acts;
}
public void ClearComActFlag(bool bSignalCurrent) { ClearComActFlag(0, bSignalCurrent); }
public void ClearComActFlag(int nChannel, bool bSignalCurrent)
{
@@ -672,11 +785,30 @@ public class CECModel
}
var actionInfos = combinedAction.m_ActLst;
var isLoop = combinedAction.m_nLoops == 1;
EventBus.PublishChannel(m_nId, new PlayActionEvent(actionInfos[0].m_strName, nTransTime, bForceStop, attackEvent, isLoop));
var node = m_ChannelActs[nChannel].GetNodeByRank(0);
if (node == null)
{
node = new ChannelActNode { m_Rank = 0 };
m_ChannelActs[nChannel].m_RankNodes.Add(node);
}
var actData = new A3DCombActDynData
{
combinedAction = combinedAction,
nChannel = nChannel,
m_dwUserData = (int)dwUserData
};
EventBus.PublishChannel(m_nId, new PlayActionEvent(ref m_ChannelActs[nChannel], actionInfos[0].m_strName, nTransTime, bForceStop, attackEvent, isLoop, node.m_Rank));
actData.m_EventNames.Add(actionInfos[0].m_strName);
for(int i = 1; i < actionInfos.Count; i++)
{
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop));
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop, node.m_Rank));
actData.m_EventNames.Add(actionInfos[i].m_strName);
}
node.m_pActive = actData;
//Todo: should move those event logic to channel act because their control the animation life span.
var eventInfoList = combinedAction.m_EventInfoLst;
if(eventInfoList != null && eventInfoList.Count > 0)
{
@@ -716,25 +848,44 @@ public class CECModel
return false;
}
A3DCombinedAction combinedAction = GetComActByName(szActName);
if(combinedAction == null)
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, isLoop));
var node = new ChannelActNode { m_Rank = (byte)m_ChannelActs[nChannel].m_RankNodes.Count};
m_ChannelActs[nChannel].m_RankNodes.Add(node);
var actData = new A3DCombActDynData
{
// combinedAction = combinedAction, // not needed now
nChannel = nChannel,
m_dwUserData = (int)dwUserData
};
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[0].m_strName, null, false, attackEvent, nTransTime, bForceStopPrevAct, isLoop, node.m_Rank));
for(int i = 1; i < actionInfos.Count; i++)
{
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop));
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(ref m_ChannelActs[nChannel], actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop, node.m_Rank));
actData.m_EventNames.Add(actionInfos[i].m_strName);
}
node.m_pActive = actData;
//todo: should add sfx and gfx logic to channel act. currently we dont have logic for queue action. only available for play action.
return true;
}
public bool ClearComActFlagAllRankNodes(bool bSignalCurrent){
EventBus.PublishChannel(m_nId, new ClearComActFlagAllRankNodesEvent(bSignalCurrent));
public bool ClearComActFlagAllRankNodes(bool bSignalCurrent)
{
foreach (var ca in m_ChannelActs)
ca?.Release();
//EventBus.PublishChannel(m_nId, new ClearComActFlagAllRankNodesEvent(bSignalCurrent));
return false;
}
public uint GetCurActionUserData(){
return 0;
public int GetCurActionUserData()
{
var returnValue = -1;
if (m_ChannelActs[0].GetHighestRankNode() != null)
returnValue = m_ChannelActs[0].GetHighestRankNode().m_pActive.GetUserData();
return returnValue;
}
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)
@@ -846,8 +997,3 @@ public class CECModel
// m_CoGfxMap[strKey] = pInfo;
}
}
// Action channel
public enum ActionChannel
{
ACTCHA_WOUND = 1,
};
@@ -450,13 +450,14 @@ namespace BrewMonster
}
public override bool IsPlayingMoveAction(){
if (GetModel()!=null){
return IsMoveAction((int)GetModel().GetCurActionUserData());
var userData = GetModel().GetCurActionUserData();
return IsMoveAction((int)userData);
}
return false;
}
public override int GetLowerBodyAction(){
if (GetModel()!=null){
return (int)GetModel().GetCurActionUserData();
return GetModel().GetCurActionUserData();
}
return -1;
}
@@ -672,80 +672,70 @@ namespace BrewMonster
// Get skill object (else players don't have skill lists, so we create a temporary skill reference)
// For else players, we mainly need the skill ID for animation purposes
int skillID = pCmd.skill;
if ((m_pCurSkill = new CECSkill(pCmd.skill, pCmd.level)) == null)
{
BMLogger.LogError($"OnMsgPlayerCastSkill: Failed to create CECSkill for skillID={pCmd.skill}, level={pCmd.level}");
return;
}
// Store current skill target
m_idCurSkillTarget = pCmd.target;
// Face the target
TurnFaceTo(pCmd.target);
uint dwPeriod = (uint)(pCmd.time + m_pCurSkill.GetExecuteTime()) * 10;
m_pEPWorkMan.StartNormalWork(new CECEPWorkSpell(m_pEPWorkMan, dwPeriod, new CECSkill(pCmd.skill, pCmd.level), pCmd.target));
PlaySkillCastAction(m_pCurSkill.GetSkillID());
// // Face the target
// TurnFaceTo(pCmd.target);
// Play skill cast animation
PlaySkillCastAction(skillID);
// // Play skill cast animation
// PlaySkillCastAction(skillID);
// Create a temporary skill object for tracking (if needed)
// Note: Else players don't maintain skill lists like host player does
// We create a minimal skill object just for the current cast
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
{
// Create a temporary skill object with level 1 (we don't know the actual level)
m_pCurSkill = new CECSkill(skillID, 1);
}
// // Create a temporary skill object for tracking (if needed)
// // Note: Else players don't maintain skill lists like host player does
// // We create a minimal skill object just for the current cast
// if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
// {
// // Create a temporary skill object with level 1 (we don't know the actual level)
// m_pCurSkill = new CECSkill(skillID, 1);
// }
// Enter fight state
EnterFightState();
// // Enter fight state
// EnterFightState();
break;
}
case CommandID.OBJECT_CAST_INSTANT_SKILL:
{
StopCurPosWork();
ClearCastingSkill();
m_pEPWorkMan.FinishWork(CECEPWork.EP_work_ID.WORK_SPELL);
cmd_object_cast_instant_skill pCmd =
GPDataTypeHelper.FromBytes<cmd_object_cast_instant_skill>((byte[])Msg.dwParam1);
int skillID = pCmd.skill;
m_idCurSkillTarget = pCmd.target;
TurnFaceTo(pCmd.target);
PlaySkillCastAction(skillID);
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
{
m_pCurSkill = new CECSkill(skillID, 1);
}
EnterFightState();
PlaySkillCastAction(pCmd.skill);
break;
}
case CommandID.OBJECT_CAST_POS_SKILL:
{
StopCurPosWork();
ClearCastingSkill();
m_pEPWorkMan.FinishWork(CECEPWork.EP_work_ID.WORK_SPELL);
cmd_object_cast_pos_skill pCmd =
GPDataTypeHelper.FromBytes<cmd_object_cast_pos_skill>((byte[])Msg.dwParam1);
int skillID = pCmd.skill;
SetServerPos(pCmd.pos);
// For position-based skills, target is the position, not an object
// We still play the cast animation
PlaySkillCastAction(skillID);
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
float fDist = A3d_Magnitude(pCmd.pos - GetPos());
if (fDist <= 0.0001f)
{
m_pCurSkill = new CECSkill(skillID, 1);
}
EnterFightState();
break;
}
case CommandID.SKILL_PERFORM:
{
// Skill perform - the skill has finished casting and is being executed
// For else players, we keep m_pCurSkill until attack result is received
// This allows PlayAttackEffect to use the skill information
// Durative skills (channeling) will continue until interrupted
if (m_pCurSkill != null && m_pCurSkill.IsDurative())
{
// For durative skills, we keep the skill active
// It will be cleared when SKILL_INTERRUPTED is received
SetPos(pCmd.pos);
return;
}
int nExecuteTime = ElementSkill.GetExecuteTime((uint)pCmd.skill, pCmd.level);
nExecuteTime = Mathf.Max(nExecuteTime, 50);
m_fMoveSpeed = 1.3f * fDist * 1000.0f / nExecuteTime;
m_pEPWorkMan.StartNormalWork(new CECEPWorkFlashMove(m_pEPWorkMan, pCmd.pos, m_fMoveSpeed));
break;
}
case CommandID.SKILL_INTERRUPTED:
@@ -753,13 +743,10 @@ namespace BrewMonster
// Skill was interrupted, clear current skill
cmd_skill_interrupted pCmd =
GPDataTypeHelper.FromBytes<cmd_skill_interrupted>((byte[])Msg.dwParam1);
if (m_pCurSkill != null)
{
StopSkillCastAction();
m_pCurSkill = null;
}
m_idCurSkillTarget = 0;
ClearComActFlagAllRankNodes(false);
ClearCastingSkill();
m_pEPWorkMan.FinishWork(CECEPWork.EP_work_ID.WORK_SPELL);
StopSkillCastAction();
break;
}
default:
@@ -768,7 +755,25 @@ namespace BrewMonster
}
}
}
private void ClearCastingSkill(){
if (m_pCurSkill != null){
m_pCurSkill = null;
}
m_idCurSkillTarget = 0;
}
private void StopCurPosWork()
{
if (m_pEPWorkMan.IsWorkRunning(CECEPWork.EP_work_ID.WORK_MOVE)){
m_pEPWorkMan.FinishRunningWork(CECEPWork.EP_work_ID.WORK_MOVE);
}
if (m_pEPWorkMan.IsWorkRunning(CECEPWork.EP_work_ID.WORK_PASSIVEMOVE)){
m_pEPWorkMan.FinishRunningWork(CECEPWork.EP_work_ID.WORK_PASSIVEMOVE);
}
if (m_pEPWorkMan.IsWorkRunning(CECEPWork.EP_work_ID.WORK_FLASHMOVE)){
m_pEPWorkMan.FinishRunningWork(CECEPWork.EP_work_ID.WORK_FLASHMOVE);
}
}
private async void LoadAppearGfx()
{
if (!m_pAppearGFX && m_iAppearFlag == (int)PlayerAppearFlag.APPEAR_ENTERWORLD)
@@ -39,10 +39,15 @@ namespace BrewMonster.Scripts
_loadVersion++;
int version = _loadVersion;
ClearModels();
if (roleInfos == null || roleInfos.Count == 0 || NPCManager.Instance == null)
{
ClearModels();
return;
}
List<GameObject> oldModels = new List<GameObject>(playerModels);
playerModels.Clear();
playerModelIds.Clear();
for (int i = 0; i < roleInfos.Count; i++)
{
@@ -68,6 +73,14 @@ namespace BrewMonster.Scripts
ApplyRequestedModelVisibility();
}
for(int i=0; i < oldModels.Count; i++)
{
if (oldModels[i] != null)
{
Destroy(oldModels[i]);
}
}
}
/// <summary>
@@ -108,13 +121,36 @@ namespace BrewMonster.Scripts
}
}
public void ClearModels()
public void HideAllPlayerModels()
{
_hasRequestedPlayerModelId = false;
for (int i = 0; i < playerModels.Count; i++)
{
if (playerModels[i] != null)
Destroy(playerModels[i]);
{
playerModels[i].SetActive(false);
}
}
}
public void ClearModels()
{
//for (int i = 0; i < playerModels.Count; i++)
//{
// if (playerModels[i] != null)
// {
// Destroy(playerModels[i]);
// }
//}
for (int i = 0; i < playerModels.Count; i++)
{
if (playerModels[i] != null)
{
Destroy(playerModels[i]);
}
}
playerModels.Clear();
playerModelIds.Clear();
}
@@ -123,14 +159,18 @@ namespace BrewMonster.Scripts
{
for (int i = 0; i < playerModels.Count; i++)
{
if (playerModels[i] != null)
if (modelRoot != null)
{
if (Application.isPlaying)
Destroy(playerModels[i]);
else
DestroyImmediate(playerModels[i]);
if(playerModels[i] != null)
{
if(Application.isPlaying)
Destroy(playerModels[i]);
else
DestroyImmediate(playerModels[i]);
}
}
}
playerModels.Clear();
playerModelIds.Clear();
}
@@ -139,13 +179,17 @@ namespace BrewMonster.Scripts
{
BMLogger.Log($"LoadPlayerModel: {role.roleid}");
var elemendataman = BrewMonster.ElementDataManProvider.GetElementDataMan();
GameObject prefab = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender);
if (prefab == null)
{
GameObject model = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender);
if(model == null)
return null;
}
model.SetActive(false);
BMLogger.Log($"LoadPlayerModel: {role.roleid} - prefab loaded");
GameObject model = Instantiate(prefab);
if (modelRoot != null)
{
model.transform.SetParent(modelRoot, false);
}
var playerDefaultEquipments = model.GetComponentInChildren<PlayerDefaultEquipments>();
if (playerDefaultEquipments == null)
{
@@ -196,6 +196,15 @@ namespace BrewMonster.Scripts.Skills
// ȴʱλ
public virtual int GetCoolingTime() { return 5000; }
// ִʱλ
public static int GetExecuteTime(uint id, int level)
{
Skill s = Skill.Create(id, level);
if (s == null)
return 0;
int ret = s.GetExecuteTime();
return ret;
}
public virtual int GetExecuteTime() { return 1000; }
// Ŀ, 0:Ŀ꣬1:ҪĿ꣬2:Ŀ?, 3:Ŀ?, 4:Ŀ?
public virtual int GetTargetType() { return 0; }
@@ -246,6 +246,11 @@ namespace BrewMonster.Scripts.UI.Inventory
return;
}
if (_previewInstance != null && _previewInstance != sourceRoot.gameObject)
{
DestroyPreviewInstance();
}
if (!sourceRoot.gameObject.activeSelf)
{
sourceRoot.gameObject.SetActive(true);
@@ -269,6 +274,15 @@ namespace BrewMonster.Scripts.UI.Inventory
EnsureCameraBindings();
}
private void DestroyPreviewInstance()
{
if (_previewInstance != null)
{
Destroy(_previewInstance);
_previewInstance = null;
}
}
private Transform ResolveSourceModelRoot()
{
@@ -56,6 +56,11 @@ namespace BrewMonster.UI
{
Tech3CSDKWrapper.Instance.SetLoginCallback(OnLoginCallback);
Tech3CSDKWrapper.Instance.SetLogoutCallback(OnLogoutCallback);
if(PlayerModelPreview.Instance != null)
{
PlayerModelPreview.Instance.HideAllPlayerModels();
}
}
private void OnDisable()
@@ -102,7 +107,7 @@ namespace BrewMonster.UI
{
if (_roleInfos != null)
{
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete);
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete, OnExitCharacterSelect);
_roleInfos = null;
}
@@ -353,6 +358,13 @@ namespace BrewMonster.UI
private async void OnEnterWorldComplete()
{
// initialize the mini map
CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
if (pGameUI != null)
{
pGameUI.m_pDlgMiniMap.InitializeMiniMap();
}
await Task.Delay(2000);
// Request all known packages: 0=Inventory,1=Equipment,2=Task
UnityGameSession.RequestAllInventoriesAsync(() => { /*BMLogger.Log("Sent Inventory Detail Requests (all packs)");*/ }, 0, 1, 2);
@@ -422,6 +434,21 @@ namespace BrewMonster.UI
{
BMLogger.LogError($"Logout failed -- errorCode: {errorCode}: {errorMessage}");
}
_loginInProgress = false;
if (_loginButton != null)
_loginButton.interactable = true;
}
private void OnExitCharacterSelect()
{
BMLogger.Log("Exiting character select, returning to login screen.");
if(_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(false);
_loginInProgress = false;
if(_loginButton != null)
_loginButton.interactable = true;
}
}
}
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Extensions;
using BrewMonster.UI;
@@ -55,13 +56,15 @@ namespace PerfectWorld.UI.MiniMap
private bool isShowMiniMap = true;
CECHostPlayer m_pHostPlayer;
private float coordinateFactor = 0.5f; // the factor to convert the world coordinates to the mini map coordinates.
Vector3Int _lastIntHostPos = Vector3Int.zero;
private int m_nMode; // TODO: currently, there is only get logic, not set logic
private void Awake()
{
LoadAllMiniMapTextures();
// LoadAllMiniMapTextures();
_worldMapButton.onClick.AddListener(OnMiniMapClicked);
}
@@ -80,7 +83,6 @@ namespace PerfectWorld.UI.MiniMap
m_pHostPlayer = GetHostPlayer();
if (m_pHostPlayer == null) return;
// TODO: This should be the position of the host player, not hardcoded.
Transform hostTransform = m_pHostPlayer.transform;
Vector3 vecPosHost = hostTransform.position;
Vector3Int currentIntHostPos = new Vector3Int(Mathf.RoundToInt(vecPosHost.x) / 10 + 400, Mathf.RoundToInt(vecPosHost.y) / 10, Mathf.RoundToInt(vecPosHost.z) / 10 + 550);
@@ -89,7 +91,7 @@ namespace PerfectWorld.UI.MiniMap
txtHostPos.text = $"{currentIntHostPos.x}, {currentIntHostPos.z}, ↑{currentIntHostPos.y}";
_lastIntHostPos = currentIntHostPos;
}
Vector2 hostPlayerPos = new Vector2(vecPosHost.x / 2, vecPosHost.z / 2);
Vector2 hostPlayerPos = new Vector2(vecPosHost.x * coordinateFactor, vecPosHost.z * coordinateFactor);
_transformMiniMapParent.anchoredPosition = -hostPlayerPos;
_hostPlayerIcon.localRotation = Quaternion.Euler(0, 0, -hostTransform.localRotation.eulerAngles.y);
}
@@ -117,6 +119,40 @@ namespace PerfectWorld.UI.MiniMap
// dlg?.OnInitDialog();
}
/// <summary>
/// Call this function when user enter the game world (after select role or when use GOTO to jump to a new instance).
/// This function will get the world instance data and setup the mini map.
/// </summary>
public async void InitializeMiniMap()
{
// get current world instance
var idInstance = CECGameRun.Instance?.GetWorld()?.GetInstanceID() ?? 161;
var worldInstance = EC_Game.GetGameRun()?.GetInstance(idInstance);
if (worldInstance == null)
{
BMLogger.LogError("InitializeMiniMap: worldInstance is null");
return;
}
// set the number of rows and columns of the mini map
nRow = (byte)worldInstance.GetRowNum();
nCol = (byte)worldInstance.GetColNum();
// use Addressable to load all the textures of the mini map
_spriteAtlas = await AddressableManager.Instance.LoadSpriteAtlasAsync($"minimaps/{idInstance}");
if (_spriteAtlas == null)
{
BMLogger.LogError("InitializeMiniMap: sprite atlas is null");
return;
}
LoadAllMiniMapTextures();
}
// keep this so we can load all textures of other map also.
[ContextMenu("LoadAllMiniMapTextures")]
public void LoadAllMiniMapTextures()
@@ -142,7 +178,7 @@ namespace PerfectWorld.UI.MiniMap
}
}
#if UNITY_EDITOR
// this is for debuging/testing while this feature was in development
[ContextMenu("MoveHostPlayerIconToPos")]
public void MoveHostPlayerIconToPos()
@@ -151,5 +187,6 @@ namespace PerfectWorld.UI.MiniMap
_transformMiniMapParent.anchoredPosition = -hostPlayerPos;
}
#endif
}
}
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: b764c7c6d08a20e41a8ebfb3435954db
guid: 34322d27488583140acae7497762ccbc
SpriteAtlasImporter:
externalObjects: {}
textureSettings:
+1 -1
View File
@@ -989,8 +989,8 @@ MonoBehaviour:
parentItems: {fileID: 2643174602035272289}
createCharacterButton: {fileID: 2685968509838782728}
_btnEnterGame: {fileID: 5173447691193350084}
_btnExit: {fileID: 1166159884785039946}
createCharacterScreen: {fileID: 0}
playerModelPreview: {fileID: 0}
--- !u!1 &7510180475820570348
GameObject:
m_ObjectHideFlags: 0
+20 -11
View File
@@ -14,6 +14,8 @@ namespace BrewMonster
public int ITransTime;
public CECAttackEvent AttackEvent;
public bool IsLoop;
public ChannelAct ChannelAct;
public int Rank;
}
public class PlayerVisual : MonoBehaviour
{
@@ -48,14 +50,16 @@ namespace BrewMonster
{
AnimationName = @event.AnimationName,
IsForceStopPrevious = @event.IsForceStopPrevious,
AttackEvent = @event.AttackEvent
AttackEvent = @event.AttackEvent,
ChannelAct = @event.ChannelAct,
Rank = @event.Rank
});
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
return;
}
previousAnimationName = @event.AnimationName;
InternalPlayAnimation(@event.AnimationName, @event.ITransTime, FadeMode, @event.IsLoop);
ApplyAttackSignalOnAnimationEnd(@event.AttackEvent);
ApplyAnimationEndCallbacks(@event.AttackEvent, @event.ChannelAct, @event.Rank);
}
public void InitPlayerEventDoneHandler()
{
@@ -156,7 +160,9 @@ namespace BrewMonster
IsForceStopPrevious = @event.IsForceStopPrevious,
ITransTime = @event.ITransTime,
AttackEvent = @event.AttackEvent,
IsLoop = @event.IsLoop
IsLoop = @event.IsLoop,
ChannelAct = @event.ChannelAct,
Rank = @event.Rank
});
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
if (!isHit)
@@ -222,18 +228,21 @@ namespace BrewMonster
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
previousAnimationName = animationQueue.AnimationName;
InternalPlayAnimation(animationQueue.AnimationName, animationQueue.ITransTime, FadeMode, animationQueue.IsLoop);
ApplyAttackSignalOnAnimationEnd(animationQueue.AttackEvent);
ApplyAnimationEndCallbacks(animationQueue.AttackEvent, animationQueue.ChannelAct, animationQueue.Rank);
}
private void ApplyAttackSignalOnAnimationEnd(CECAttackEvent attackEvent)
{
if (attackEvent == null || _currentState == null)
{
return;
}
private void ApplyAnimationEndCallbacks(CECAttackEvent attackEvent, ChannelAct channelAct, int rank)
{
if (_currentState == null) return;
_currentState.Events.OnEnd = () =>
{
attackEvent.m_bSignaled = true;
if (attackEvent != null)
attackEvent.m_bSignaled = true;
channelAct.GetNodeByRank((byte)rank).m_pActive.m_EventNames.Remove(_currentAnimationName);
if(channelAct.GetNodeByRank((byte)rank).m_pActive.IsAllActionFinished)
{
//channelAct.RemoveNodeByRank((byte)rank);
}
};
}
void ApplyDamage()
+24 -5
View File
@@ -18,11 +18,13 @@ namespace BrewMonster.UI
[SerializeField] private RectTransform parentItems;
[SerializeField] private Button createCharacterButton;
[SerializeField] private Button _btnEnterGame;
[SerializeField] private Button _btnExit;
[SerializeField] private CreateCharacterScreen createCharacterScreen;
private CharacterItemUI _selectingCharacterItemUI;
private Action<RoleInfo> _onClickItemChar;
private Action<RoleInfo> _onCreateCharacterComplete;
private Action _onExit;
private List<RoleInfo> _roleInfos;
private Coroutine _showModelReadyCoroutine;
@@ -33,6 +35,11 @@ namespace BrewMonster.UI
_btnEnterGame.onClick.AddListener(OnClickedEnterGame);
_btnEnterGame.gameObject.SetActive(false);
if(_btnExit != null)
{
_btnExit.onClick.AddListener(OnClickedExit);
}
if (_roleInfos != null && _roleInfos.Count > 0)
{
PlayerModelPreview.Instance?.ShowAllPlayerModels(_roleInfos);
@@ -57,10 +64,11 @@ namespace BrewMonster.UI
}
}
public void InitScreen(List<RoleInfo> roleInfos, Action<RoleInfo> OnClickItemChar, Action<RoleInfo> onCreateCharacterComplete = null)
public void InitScreen(List<RoleInfo> roleInfos, Action<RoleInfo> OnClickItemChar, Action<RoleInfo> onCreateCharacterComplete = null, Action onExit = null)
{
_onClickItemChar = OnClickItemChar;
_onCreateCharacterComplete = onCreateCharacterComplete;
_onExit = onExit;
// Clear existing items
if (parentItems != null)
@@ -110,7 +118,7 @@ namespace BrewMonster.UI
addButton.onClick.AddListener(OnCreateCharacterClicked);
}
}
}
else
{
@@ -120,13 +128,13 @@ namespace BrewMonster.UI
createCharacterButton.gameObject.SetActive(true);
}
}
// Load player preview 3D models
PlayerModelPreview.Instance?.ShowAllPlayerModels(roleInfos);
}
else
{
if (PlayerModelPreview.Instance != null)
if (PlayerModelPreview.Instance != null)
PlayerModelPreview.Instance.ShowAllPlayerModels(null);
// If roleInfos is null, show createCharacterButton
if (createCharacterButton != null)
@@ -143,7 +151,7 @@ namespace BrewMonster.UI
return;
}
if(_selectingCharacterItemUI!=null)
if (_selectingCharacterItemUI != null)
{
_selectingCharacterItemUI.SetFocus(false);
}
@@ -230,5 +238,16 @@ namespace BrewMonster.UI
BMLogger.LogError("No role selected");
}
}
private void OnClickedExit()
{
_onExit?.Invoke();
gameObject.SetActive(false);
if(PlayerModelPreview.Instance != null)
{
PlayerModelPreview.Instance.HideAllPlayerModels();
}
}
}
}