Merge pull request 'feature/status-icon-npc' (#420) from feature/status-icon-npc into develop

Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/420
This commit is contained in:
namth
2026-05-08 11:58:02 +00:00
9 changed files with 492 additions and 174 deletions
+212 -9
View File
@@ -1,5 +1,94 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1172007003875469888
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6367968922383945468}
- component: {fileID: 8777696811020310446}
- component: {fileID: 7675664214308521901}
- component: {fileID: 72103662956892879}
m_Layer: 5
m_Name: Background
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6367968922383945468
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1172007003875469888}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 2177533424404370772}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8777696811020310446
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1172007003875469888}
m_CullTransparentMesh: 1
--- !u!114 &7675664214308521901
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1172007003875469888}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &72103662956892879
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1172007003875469888}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_ShowMaskGraphic: 1
--- !u!1 &1273460584854605297
GameObject:
m_ObjectHideFlags: 0
@@ -37,7 +126,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -160, y: -42}
m_AnchoredPosition: {x: -150, y: 2.4}
m_SizeDelta: {x: 123, y: 123}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1981970930610958667
@@ -238,13 +327,14 @@ RectTransform:
m_Children:
- {fileID: 7183588826468540253}
- {fileID: 8841982213385894347}
- {fileID: 5298541048597512026}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 0, y: -50}
m_SizeDelta: {x: 100, y: 100}
m_Pivot: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 400, y: 129.4}
m_Pivot: {x: 0.5, y: 1}
--- !u!114 &9105272519104429228
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -263,6 +353,7 @@ MonoBehaviour:
_statText: {fileID: 7225922753763360209}
healthImage: {fileID: 391766345810538963}
_avatarImage: {fileID: 2274486941820472865}
_buffIconPrefab: {fileID: 6278983905934311425}
--- !u!1 &5230218528339883288
GameObject:
m_ObjectHideFlags: 0
@@ -298,11 +389,11 @@ RectTransform:
- {fileID: 1333050478342715942}
m_Father: {fileID: 7601428160728630082}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 22, y: -42.000004}
m_AnchorMin: {x: 0.5, y: 1}
m_AnchorMax: {x: 0.5, y: 1}
m_AnchoredPosition: {x: 22, y: -30}
m_SizeDelta: {x: 336, y: 95}
m_Pivot: {x: 0.5, y: 0.5}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &6172505155667263140
CanvasRenderer:
m_ObjectHideFlags: 0
@@ -341,6 +432,57 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &6604678107028403134
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2177533424404370772}
- component: {fileID: 6278983905934311425}
m_Layer: 5
m_Name: BuffIconTemplate
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &2177533424404370772
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6604678107028403134}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 6367968922383945468}
m_Father: {fileID: 5298541048597512026}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 12, y: -12}
m_SizeDelta: {x: 24, y: 24}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &6278983905934311425
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6604678107028403134}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 200754299bc29436e9c9d608b6927e2d, type: 3}
m_Name:
m_EditorClassIdentifier:
slotIndex: -1
disPlayImage: {fileID: 7675664214308521901}
--- !u!1 &7164650417342813199
GameObject:
m_ObjectHideFlags: 0
@@ -417,6 +559,67 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &7442649156186235319
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5298541048597512026}
- component: {fileID: 7950876844531695353}
m_Layer: 5
m_Name: BuffDisplayer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &5298541048597512026
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7442649156186235319}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.1249999, y: 1.1249999, z: 1.1249999}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2177533424404370772}
m_Father: {fileID: 7601428160728630082}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: 100, y: 32.6}
m_SizeDelta: {x: -130, y: 30}
m_Pivot: {x: 0, y: 1}
--- !u!114 &7950876844531695353
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7442649156186235319}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8a8695521f0d02e499659fee002a26c2, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Padding:
m_Left: 0
m_Right: 0
m_Top: 0
m_Bottom: 0
m_ChildAlignment: 0
m_StartCorner: 0
m_StartAxis: 0
m_CellSize: {x: 24, y: 24}
m_Spacing: {x: 2, y: 2}
m_Constraint: 0
m_ConstraintCount: 2
--- !u!1 &7483462528009603224
GameObject:
m_ObjectHideFlags: 0
@@ -587,7 +790,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -0.5261, y: -1.6}
m_AnchoredPosition: {x: -0.5261, y: -1.5999985}
m_SizeDelta: {x: 241.0417, y: 20.1006}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6237368962895350952
+4 -113
View File
@@ -19142,17 +19142,13 @@ PrefabInstance:
propertyPath: m_Name
value: HUDNPC
objectReference: {fileID: 0}
- target: {fileID: 5010991128992349155, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_Pivot.y
value: 0.5
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_AnchorMax.x
@@ -19172,23 +19168,11 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_SizeDelta.x
value: 100
value: 420
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_SizeDelta.y
value: 100
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_LocalScale.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_LocalScale.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_LocalScale.z
value: 1
value: 150
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_LocalPosition.x
@@ -19224,7 +19208,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_AnchoredPosition.y
value: -44.444336
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
@@ -19238,22 +19222,6 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: m_ConstrainProportionsScale
value: 1
objectReference: {fileID: 0}
- target: {fileID: 9105272519104429228, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: _nameText
value:
objectReference: {fileID: 8894872406734287223}
- target: {fileID: 9105272519104429228, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: _statText
value:
objectReference: {fileID: 7661620612787195405}
- target: {fileID: 9105272519104429228, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
propertyPath: _healthText
value:
objectReference: {fileID: 9151738778944310921}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
@@ -19264,17 +19232,6 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 7601428160728630082, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
m_PrefabInstance: {fileID: 1014481099860472796}
m_PrefabAsset: {fileID: 0}
--- !u!114 &7661620612787195405 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 7225922753763360209, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
m_PrefabInstance: {fileID: 1014481099860472796}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &8090791712930107248 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 9105272519104429228, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
@@ -19286,28 +19243,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d072871c8791e284dbad61ce13ba6887, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &8894872406734287223 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 8459104239633154731, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
m_PrefabInstance: {fileID: 1014481099860472796}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &9151738778944310921 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 8148604647689649493, guid: 7130d91d43d72c145a379b373c8c27b0, type: 3}
m_PrefabInstance: {fileID: 1014481099860472796}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1001 &1257740176129564677
PrefabInstance:
m_ObjectHideFlags: 0
@@ -22368,22 +22303,6 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2985336126980033672, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3313561919066668394, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3661057301147371696, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 3663290235883915697, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 3951120581356990872, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_AnchorMax.y
value: 0
@@ -22400,38 +22319,10 @@ PrefabInstance:
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4263483627851733539, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4263483627851733539, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4263483627851733539, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_SizeDelta.x
value: 24
objectReference: {fileID: 0}
- target: {fileID: 4263483627851733539, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_SizeDelta.y
value: 24
objectReference: {fileID: 0}
- target: {fileID: 4263483627851733539, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_AnchoredPosition.x
value: 12
objectReference: {fileID: 0}
- target: {fileID: 4263483627851733539, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_AnchoredPosition.y
value: -12
objectReference: {fileID: 0}
- target: {fileID: 4823752405346273106, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_Name
value: HUDPlayer
objectReference: {fileID: 0}
- target: {fileID: 4823752405346273106, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_IsActive
value: 1
objectReference: {fileID: 0}
- target: {fileID: 6806681442789174374, guid: 76408ccdbeb4c654291462fcff24a8c5, type: 3}
propertyPath: m_Pivot.x
value: 0
@@ -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>
+39 -10
View File
@@ -7,8 +7,9 @@ using ModelRenderer.Scripts.Common;
using System;
using System.Threading.Tasks;
using BrewMonster.Scripts.Chat;
using BrewMonster.Scripts.Skills;
using System.Collections.Generic;
using UnityEngine;
using BrewMonster.Scripts.Skills;
public class CECNPC : CECObject
{
[SerializeField] protected INFO m_NPCInfo;
@@ -53,6 +54,8 @@ public class CECNPC : CECObject
// [English] Currently displayed extended-state bitmask (diffed against new data to add/remove GFX)
protected uint[] m_aExtStatesShown = new uint[(int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT];
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();
@@ -220,6 +223,35 @@ 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;
});
}
}
if (Convert.ToInt32(Msg.dwParam2) == CommandID.UPDATE_EXT_STATE)
{
cmd_update_ext_state pCmd = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])Msg.dwParam1);
if (pCmd.id == m_NPCInfo.nid)
SetNewExtendStates(0, pCmd.states, (int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT);
}
// 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);
@@ -228,15 +260,7 @@ public class CECNPC : CECObject
// [中文] 处理服务器发来的扩展状态更新消息,驱动状态 GFX 的添加与移除
// [English] Handle server ext-state update message — drives state GFX add/remove
private void OnMsgNPCExtState(ECMSG Msg)
{
if (Convert.ToInt32(Msg.dwParam2) == CommandID.UPDATE_EXT_STATE)
{
cmd_update_ext_state pCmd = GPDataTypeHelper.FromBytes<cmd_update_ext_state>((byte[])Msg.dwParam1);
if (pCmd.id == m_NPCInfo.nid)
SetNewExtendStates(0, pCmd.states, (int)OBJECT_EXT_STATE.OBJECT_EXT_STATE_COUNT);
}
}
// [中文] 更新扩展状态并刷新 GFX 显示
// [English] Update the ext-state arrays and refresh GFX display
@@ -754,6 +778,11 @@ public class CECNPC : CECObject
}*/
// Clear extend states before model is released
/* ClearShowExtendStates();
::memset(m_aExtStates, 0, sizeof(m_aExtStates));
m_aIconStates.clear();*/
m_aIconStates?.Clear();
// [中文] 模型释放前先移除所有状态效果 GFX,并重置位图
// [English] Remove all state-effect GFX and reset bitmasks before the model is released
ClearShowExtendStates();
@@ -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
{
+127 -6
View File
@@ -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;
}