ADd buff icon for npc and else player
This commit is contained in:
@@ -19,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<Sprite>> _loadedSpriteAssets = new();
|
||||
private Dictionary<string, AsyncOperationHandle<SpriteAtlas>> _loadedSpriteAtlasAssets = new();
|
||||
|
||||
private Dictionary<string, int> _loadedAssetReferenceCount = new();
|
||||
@@ -318,6 +319,43 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
}
|
||||
|
||||
AsyncOperationHandle<Sprite> _loadedSpriteHandle;
|
||||
|
||||
/// <summary>
|
||||
/// Load a Sprite asynchronously (e.g. UI art). Key must match the Addressables address.
|
||||
/// </summary>
|
||||
public async Task<Sprite> LoadSpriteAsync(string assetPath)
|
||||
{
|
||||
RemoveFromReleaseAssetDictionary(assetPath);
|
||||
|
||||
if (_loadedSpriteAssets.TryGetValue(assetPath, out _loadedSpriteHandle))
|
||||
{
|
||||
if (_loadedSpriteHandle.IsValid() && _loadedSpriteHandle.Result != null)
|
||||
{
|
||||
BMLogger.Log($"AddressableManager: Loaded sprite from cache: {assetPath}");
|
||||
return _loadedSpriteHandle.Result;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var handle = Addressables.LoadAssetAsync<Sprite>(assetPath.Trim());
|
||||
await handle.Task;
|
||||
if (handle.OperationException != null)
|
||||
{
|
||||
BMLogger.Log($"AddressableManager: Failed to load Sprite '{assetPath}': {handle.OperationException.Message}");
|
||||
return null;
|
||||
}
|
||||
_loadedSpriteAssets[assetPath] = handle;
|
||||
return handle.Result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BMLogger.LogError($"AddressableManager: Failed to load Sprite '{assetPath}': {e.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if this address is already in the audio cache (play immediately vs async load).
|
||||
/// </summary>
|
||||
@@ -377,6 +415,15 @@ namespace BrewMonster.Scripts
|
||||
_loadedAudioAssets.Remove(assetPath);
|
||||
BMLogger.Log($"AddressableManager: Force released audio asset: {assetPath}");
|
||||
}
|
||||
else if (_loadedSpriteAssets.TryGetValue(assetPath, out var loadedSpriteHandle))
|
||||
{
|
||||
if (loadedSpriteHandle.IsValid())
|
||||
{
|
||||
Addressables.Release(loadedSpriteHandle);
|
||||
}
|
||||
_loadedSpriteAssets.Remove(assetPath);
|
||||
BMLogger.Log($"AddressableManager: Force released sprite asset: {assetPath}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -412,9 +459,18 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
}
|
||||
_loadedAudioAssets.Clear();
|
||||
foreach (var kvp in _loadedSpriteAssets)
|
||||
{
|
||||
if (kvp.Value.IsValid())
|
||||
{
|
||||
Addressables.Release(kvp.Value);
|
||||
}
|
||||
}
|
||||
_loadedSpriteAssets.Clear();
|
||||
BMLogger.Log("AddressableManager: Released all assets");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if an asset is currently loaded in the cache.
|
||||
/// </summary>
|
||||
|
||||
@@ -446,8 +446,22 @@ public class CECNPCMan : IMsgHandler
|
||||
break;
|
||||
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCEXTSTATE:
|
||||
nid = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])Msg.dwParam1).id;
|
||||
break;
|
||||
{
|
||||
int cmdType = Convert.ToInt32(Msg.dwParam2);
|
||||
byte[] buf = (byte[])Msg.dwParam1;
|
||||
if (cmdType == CommandID.UPDATE_EXT_STATE)
|
||||
nid = GPDataTypeHelper.FromBytes<cmd_update_ext_state>(buf).id;
|
||||
else if (cmdType == CommandID.ICON_STATE_NOTIFY)
|
||||
{
|
||||
var iconCmd = new cmd_icon_state_notify();
|
||||
if (!iconCmd.Initialize(buf))
|
||||
return false;
|
||||
nid = iconCmd.id;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCCASTSKILL:
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using TMPro;
|
||||
using DG.Tweening; // cần DOTween
|
||||
using BrewMonster.Scripts.UI.GamePlay;
|
||||
using BrewMonster.Scripts;
|
||||
public enum ImageResType
|
||||
{
|
||||
IMG_POPUPNUM = 0,
|
||||
@@ -155,36 +157,36 @@ public class FLoatingTextManager : MonoBehaviour
|
||||
{
|
||||
pool.Enqueue(text);
|
||||
}
|
||||
public bool LoadAllImages()
|
||||
public async Task<bool> LoadAllImages()
|
||||
{
|
||||
LoadImage(ImageResType.IMG_HITMISSED, "InGame/未命中.tga");
|
||||
LoadImage(ImageResType.IMG_LEVELUP, "InGame/升级了.tga");
|
||||
LoadImage(ImageResType.IMG_GOTEXP, "InGame/经验.tga");
|
||||
LoadImage(ImageResType.IMG_GOTMONEY, "InGame/金钱.tga");
|
||||
LoadImage(ImageResType.IMG_DEADLYSTRIKE, "InGame/爆击.tga");
|
||||
LoadImage(ImageResType.IMG_GOTSP, "InGame/元神.tga");
|
||||
LoadImage(ImageResType.IMG_INVALIDHIT, "InGame/无效.tga");
|
||||
await LoadImage(ImageResType.IMG_HITMISSED, "InGame/未命中.tga");
|
||||
await LoadImage(ImageResType.IMG_LEVELUP, "InGame/升级了.tga");
|
||||
await LoadImage(ImageResType.IMG_GOTEXP, "InGame/经验.tga");
|
||||
await LoadImage(ImageResType.IMG_GOTMONEY, "InGame/金钱.tga");
|
||||
await LoadImage(ImageResType.IMG_DEADLYSTRIKE, "InGame/爆击.tga");
|
||||
await LoadImage(ImageResType.IMG_GOTSP, "InGame/元神.tga");
|
||||
await LoadImage(ImageResType.IMG_INVALIDHIT, "InGame/无效.tga");
|
||||
//LoadImage(ImageResType.IMG_TEAMLEADER, "Window/LeaderMark.tga");
|
||||
LoadImage(ImageResType.IMG_HPWARN, "InGame/hp_warn.tga");
|
||||
LoadImage(ImageResType.IMG_MPWARN, "InGame/mp_warn.tga");
|
||||
LoadImage(ImageResType.IMG_RETORT, "InGame/反震.tga");
|
||||
LoadImage(ImageResType.IMG_IMMUNE, "InGame/免疫.tga");
|
||||
await LoadImage(ImageResType.IMG_HPWARN, "InGame/hp_warn.tga");
|
||||
await LoadImage(ImageResType.IMG_MPWARN, "InGame/mp_warn.tga");
|
||||
await LoadImage(ImageResType.IMG_RETORT, "InGame/反震.tga");
|
||||
await LoadImage(ImageResType.IMG_IMMUNE, "InGame/免疫.tga");
|
||||
//LoadImage(ImageResType.IMG_TEAMMATE, "Window/Teammate.tga");
|
||||
LoadImage(ImageResType.IMG_PKSTATE, "InGame/PK状态标记.tga");
|
||||
LoadImage(ImageResType.IMG_GMFLAG, "InGame/GM标志.dds");
|
||||
LoadImage(ImageResType.IMG_ATTACKLOSE, "InGame/失败.tga");
|
||||
LoadImage(ImageResType.IMG_SUCCESS, "InGame/成功.tga");
|
||||
LoadImage(ImageResType.IMG_REBOUND, "InGame/复仇惩戒.tga");
|
||||
LoadImage(ImageResType.IMG_BEAT_BACK, "InGame/复仇镜像.tga");
|
||||
LoadImage(ImageResType.IMG_ADD, "InGame/吸血.tga");
|
||||
LoadImage(ImageResType.IMG_DODGE_DEBUFF, "InGame/状态闪避.tga");
|
||||
await LoadImage(ImageResType.IMG_PKSTATE, "InGame/PK状态标记.tga");
|
||||
await LoadImage(ImageResType.IMG_GMFLAG, "InGame/GM标志.dds");
|
||||
await LoadImage(ImageResType.IMG_ATTACKLOSE, "InGame/失败.tga");
|
||||
await LoadImage(ImageResType.IMG_SUCCESS, "InGame/成功.tga");
|
||||
await LoadImage(ImageResType.IMG_REBOUND, "InGame/复仇惩戒.tga");
|
||||
await LoadImage(ImageResType.IMG_BEAT_BACK, "InGame/复仇镜像.tga");
|
||||
await LoadImage(ImageResType.IMG_ADD, "InGame/吸血.tga");
|
||||
await LoadImage(ImageResType.IMG_DODGE_DEBUFF, "InGame/状态闪避.tga");
|
||||
//LoadImage(ImageResType.IMG_KING, "King/皇冠图标.tga");
|
||||
return false;
|
||||
}
|
||||
private void LoadImage(ImageResType type, string path)
|
||||
private async Task<bool> LoadImage(ImageResType type, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Same normalization as skill/gfx paths (PC backslashes → Addressables-style slashes).
|
||||
// 与技能 gfx 路径一致:反斜杠转为斜杠,便于与 Addressables 地址对齐。
|
||||
@@ -194,23 +196,17 @@ public class FLoatingTextManager : MonoBehaviour
|
||||
foreach (string address in candidates)
|
||||
{
|
||||
// TODO: use AddressableManager to load the sprite.
|
||||
var handle = Addressables.LoadAssetAsync<Sprite>(address);
|
||||
handle.WaitForCompletion();
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
|
||||
var sprite = await AddressableManager.Instance.LoadSpriteAsync(address);
|
||||
if (sprite != null)
|
||||
{
|
||||
imageDic[type] = handle.Result;
|
||||
_spriteLoadHandles.Add(handle);
|
||||
return;
|
||||
imageDic[type] = sprite;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (handle.IsValid())
|
||||
Addressables.Release(handle);
|
||||
|
||||
}
|
||||
Debug.Log($"[FLoatingTextManager] Sprite load failed for {type}. Addressables keys must match the catalog exactly; " +
|
||||
$"tried: {string.Join("; ", candidates)}. " +
|
||||
$"Similar file names are not auto-resolved (unlike fuzzy file search).");
|
||||
$"tried: {string.Join("; ", candidates)}. ");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -7,6 +7,7 @@ using ModelRenderer.Scripts.Common;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BrewMonster.Scripts.Chat;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
public class CECNPC : CECObject
|
||||
{
|
||||
@@ -44,6 +45,8 @@ public class CECNPC : CECObject
|
||||
protected CECPolicyAction m_pPolicyAction;
|
||||
public int m_iMMIndex;
|
||||
public int m_idAttackTarget;
|
||||
/// <summary>Buff/debuff icon states from server (ICON_STATE_NOTIFY). Same role as CECPlayer.m_aIconStates.</summary>
|
||||
public List<IconState> m_aIconStates = new List<IconState>();
|
||||
protected UINPC m_npcUI;
|
||||
private CECModel m_pNPCCECModel; // CECModel instance for hook system / 用于挂点系统的CECModel实例
|
||||
CECCounter m_IdleCnt = new CECCounter();
|
||||
@@ -199,7 +202,7 @@ public class CECNPC : CECObject
|
||||
{
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCATKRESULT: OnMsgNPCAtkResult(Msg); break;
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCSTARTPLAYACTION: OnMsgNPCStartPlayAction(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_NPCEXTSTATE: OnMsgNPCExtState(Msg); break;
|
||||
case long value when value == EC_MsgDef.MSG_NM_NPCEXTSTATE: OnMsgNPCExtState(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_NPCCASTSKILL: OnMsgNPCCastSkill(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_ENCHANTRESULT: OnMsgNPCEnchantResult(Msg); break;
|
||||
//case long value when value == EC_MsgDef.MSG_NM_NPCROOT: OnMsgNPCRoot(Msg); break;
|
||||
@@ -211,6 +214,29 @@ public class CECNPC : CECObject
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMsgNPCExtState(ECMSG Msg)
|
||||
{
|
||||
if (Convert.ToInt32(Msg.dwParam2) == CommandID.ICON_STATE_NOTIFY)
|
||||
{
|
||||
var cmd = new cmd_icon_state_notify();
|
||||
if (!cmd.Initialize((byte[])Msg.dwParam1))
|
||||
return;
|
||||
if (cmd.id != GetNPCID())
|
||||
return;
|
||||
m_aIconStates = cmd.states ?? new List<IconState>();
|
||||
if (m_aIconStates.Count > 1)
|
||||
{
|
||||
m_aIconStates.Sort((a, b) =>
|
||||
{
|
||||
if (a.id < b.id) return -1;
|
||||
if (a.id > b.id) return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
// UPDATE_EXT_STATE for NPC is handled separately from icon strip; extend if NPC state GFX is ported.
|
||||
}
|
||||
|
||||
private void OnMsgNPCLevel(ECMSG Msg)
|
||||
{
|
||||
cmd_level_up pCmd = GPDataTypeHelper.FromBytes<cmd_level_up>((byte[])Msg.dwParam1);
|
||||
@@ -656,6 +682,7 @@ public class CECNPC : CECObject
|
||||
|
||||
::memset(m_aExtStates, 0, sizeof(m_aExtStates));
|
||||
m_aIconStates.clear();*/
|
||||
m_aIconStates?.Clear();
|
||||
|
||||
m_pNPCModelPolicy = null;
|
||||
PoolManager.Instance.Despawn(m_modelVisual);
|
||||
|
||||
@@ -479,10 +479,15 @@ namespace BrewMonster
|
||||
|
||||
public bool ProcessMessage(ECMSG Msg)
|
||||
{
|
||||
if(Msg.dwMsg == EC_MsgDef.MSG_PM_PLAYEREXTSTATE)
|
||||
{
|
||||
Debug.Log("MSG_PM_ElsePLAYEREXTSTATE");
|
||||
}
|
||||
switch (Msg.dwMsg)
|
||||
{
|
||||
case EC_MsgDef.MSG_PM_PLAYERFLY: OnMsgPlayerFly(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERBASEINFO: OnMsgPlayerBaseInfo(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYEREXTSTATE: OnMsgPlayerExtState(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYEREQUIPDATA: OnMsgPlayerEquipData(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERATKRESULT: OnMsgPlayerAtkResult(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_CASTSKILL: OnMsgPlayerCastSkill(Msg); break;
|
||||
|
||||
@@ -19,6 +19,9 @@ namespace BrewMonster.Scripts.UI.GamePlay
|
||||
{
|
||||
ingameIcon.sprite = sprite;
|
||||
ingameIcon.gameObject.SetActive(true);
|
||||
color = new Color(color.r/255f, color.g/255f, color.b/255f, 1f);
|
||||
ingameIcon.color = color;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using BrewMonster.Managers;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
using BrewMonster.UI;
|
||||
using CSNetwork.GPDataType;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using BrewMonster.Network;
|
||||
using CSNetwork.GPDataType;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class HUDNPC : MonoBehaviour
|
||||
public class HUDNPC : MonoBehaviour, ITickable
|
||||
{
|
||||
[SerializeField] private Button _NPCIconBtn;
|
||||
[SerializeField] private TextMeshProUGUI _healthText;
|
||||
@@ -15,6 +20,26 @@ namespace BrewMonster
|
||||
[SerializeField] private Image healthImage;
|
||||
[SerializeField] private RawImage _avatarImage;
|
||||
|
||||
private List<AUIBuffIcon> _buffIcons;
|
||||
[SerializeField] private AUIBuffIcon _buffIconPrefab;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_buffIcons = new List<AUIBuffIcon>();
|
||||
TickInvoker.Instance.RegisterTickable(this);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
TickInvoker.Instance.UnregisterTickable(this);
|
||||
}
|
||||
|
||||
public bool Tick(uint dwDeltaTime)
|
||||
{
|
||||
UpdateBuffIcons();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_NPCIconBtn != null)
|
||||
@@ -40,13 +65,109 @@ namespace BrewMonster
|
||||
int id = host.GetSelectedTarget();
|
||||
if (id == 0 || id == host.GetCharacterID() || !GPDataTypeHelper.ISPLAYERID(id))
|
||||
return;
|
||||
CECUIManager.Instance?.ShowPlayerOptionsDialog(id, _NPCIconBtn.transform.position);
|
||||
|
||||
CECUIManager.Instance?.ShowPlayerOptionsDialog(id, _NPCIconBtn.transform.position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Target buff/debuff row for the selected non-host entity (other player or NPC).
|
||||
/// </summary>
|
||||
private void UpdateBuffIcons()
|
||||
{
|
||||
if (!gameObject.activeInHierarchy)
|
||||
{
|
||||
SetAllBuffIconsActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var host = EC_Game.GetGameRun()?.GetHostPlayer();
|
||||
if (host == null)
|
||||
{
|
||||
SetAllBuffIconsActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
int targetId = host.GetSelectedTarget();
|
||||
if (targetId == 0 || targetId == host.GetCharacterID())
|
||||
{
|
||||
SetAllBuffIconsActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
List<IconState> iconStates = null;
|
||||
if (GPDataTypeHelper.ISPLAYERID(targetId))
|
||||
{
|
||||
var player = EC_ManMessageMono.Instance?.GetECManPlayer?.GetPlayer(targetId);
|
||||
iconStates = player?.m_aIconStates;
|
||||
}
|
||||
else if (GPDataTypeHelper.ISNPCID(targetId))
|
||||
{
|
||||
var npc = EC_ManMessageMono.Instance?.CECNPCMan?.GetNPCFromAll(targetId);
|
||||
iconStates = npc?.m_aIconStates;
|
||||
}
|
||||
|
||||
if (iconStates == null || iconStates.Count == 0)
|
||||
{
|
||||
SetAllBuffIconsActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_buffIconPrefab == null)
|
||||
{
|
||||
SetAllBuffIconsActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var gameUIMan = CECUIManager.Instance?.GetInGameUIMan();
|
||||
if (gameUIMan == null)
|
||||
{
|
||||
SetAllBuffIconsActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_buffIcons.Count < iconStates.Count)
|
||||
{
|
||||
for (int i = _buffIcons.Count; i < iconStates.Count; i++)
|
||||
{
|
||||
var buffIcon = Instantiate(_buffIconPrefab, _buffIconPrefab.transform.parent);
|
||||
_buffIcons.Add(buffIcon);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _buffIcons.Count; i++)
|
||||
{
|
||||
if (i < iconStates.Count)
|
||||
{
|
||||
var teamState = GNET.QueryTeamState(iconStates[i].id);
|
||||
if (teamState == null)
|
||||
{
|
||||
_buffIcons[i].gameObject.SetActive(false);
|
||||
continue;
|
||||
}
|
||||
_buffIcons[i].gameObject.SetActive(true);
|
||||
var szFile = teamState.GetIcon();
|
||||
szFile = szFile.ToLower();
|
||||
gameUIMan.SetCover(_buffIcons[i], szFile, EC_GAMEUI_ICONS.ICONS_STATE);
|
||||
}
|
||||
else
|
||||
{
|
||||
_buffIcons[i].gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAllBuffIconsActive(bool active)
|
||||
{
|
||||
if (_buffIcons == null) return;
|
||||
for (int i = 0; i < _buffIcons.Count; i++)
|
||||
{
|
||||
if (_buffIcons[i] != null)
|
||||
_buffIcons[i].gameObject.SetActive(active);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetText(string healthText, string nameText, string statText)
|
||||
{
|
||||
_healthText.text = healthText;
|
||||
_healthText.text = healthText;
|
||||
_nameText.text = nameText;
|
||||
_statText.text = statText;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user