Merge branch 'develop' into fixbug/npc-dialog
This commit is contained in:
@@ -1,85 +0,0 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using DG.Tweening; // cần DOTween
|
||||
|
||||
public class DamageTextManager : MonoBehaviour
|
||||
{
|
||||
public static DamageTextManager Instance { get; private set; }
|
||||
|
||||
[Header("Prefab")]
|
||||
[SerializeField] private TextMeshPro damageTextPrefab;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private int poolSize = 20;
|
||||
[SerializeField] private Vector3 offset = new Vector3(0, 2f, 0);
|
||||
[SerializeField] private float riseDistance = 1.5f;
|
||||
[SerializeField] private float riseDuration = 0.8f;
|
||||
|
||||
private readonly Queue<TextMeshPro> pool = new();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Singleton
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
// Tạo sẵn pool
|
||||
for (int i = 0; i < poolSize; i++)
|
||||
{
|
||||
var textObj = Instantiate(damageTextPrefab, transform);
|
||||
textObj.gameObject.SetActive(false);
|
||||
pool.Enqueue(textObj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gọi để spawn text damage
|
||||
/// </summary>
|
||||
public void ShowDamageText(Vector3 worldPos, int damage, Color color = default, float scale = 1f)
|
||||
{
|
||||
var text = GetFromPool();
|
||||
text.text = damage.ToString();
|
||||
text.color = color;
|
||||
text.fontSize = 6;
|
||||
text.transform.localScale = Vector3.one * scale;
|
||||
|
||||
Vector3 startPos = worldPos + offset;
|
||||
text.transform.position = startPos;
|
||||
text.gameObject.SetActive(true);
|
||||
|
||||
// Hiệu ứng bay lên + mờ dần
|
||||
text.transform.DOMoveY(startPos.y + riseDistance, riseDuration).SetEase(Ease.OutQuad);
|
||||
text.DOFade(0f, riseDuration)
|
||||
.SetEase(Ease.InQuad)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
text.alpha = 1f;
|
||||
text.gameObject.SetActive(false);
|
||||
ReturnToPool(text);
|
||||
});
|
||||
}
|
||||
|
||||
private TextMeshPro GetFromPool()
|
||||
{
|
||||
if (pool.Count > 0)
|
||||
{
|
||||
return pool.Dequeue();
|
||||
}
|
||||
|
||||
// Nếu hết pool, tạo thêm
|
||||
var text = Instantiate(damageTextPrefab, transform);
|
||||
text.gameObject.SetActive(false);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void ReturnToPool(TextMeshPro text)
|
||||
{
|
||||
pool.Enqueue(text);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7981b037b60f4a4989ff59265a308ad
|
||||
@@ -154,6 +154,7 @@ public partial class CECObject : MonoBehaviour
|
||||
{
|
||||
return EC_Utility.ToA3DVECTOR3(useTransform ? _objectTransform.position : objectPosition);
|
||||
}
|
||||
|
||||
|
||||
public Vector3 GetPosVector3(bool useTransform = true)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using TMPro;
|
||||
using DG.Tweening; // cần DOTween
|
||||
using BrewMonster.Scripts.UI.GamePlay;
|
||||
public enum ImageResType
|
||||
{
|
||||
IMG_POPUPNUM = 0,
|
||||
IMG_HITMISSED,
|
||||
IMG_FACTION,
|
||||
IMG_PATEQUEST,
|
||||
IMG_LEVELUP,
|
||||
IMG_GOTEXP,
|
||||
IMG_GOTMONEY,
|
||||
IMG_DEADLYSTRIKE,
|
||||
IMG_GOTSP,
|
||||
IMG_INVALIDHIT,
|
||||
IMG_TEAMLEADER,
|
||||
IMG_BOOTHBAR,
|
||||
IMG_HPWARN,
|
||||
IMG_MPWARN,
|
||||
IMG_RETORT,
|
||||
IMG_IMMUNE,
|
||||
IMG_TEAMMATE,
|
||||
IMG_PKSTATE,
|
||||
IMG_GMFLAG,
|
||||
IMG_ATTACKLOSE,
|
||||
IMG_SUCCESS,
|
||||
IMG_REBOUND,
|
||||
IMG_BEAT_BACK,
|
||||
IMG_ADD,
|
||||
IMG_DODGE_DEBUFF,
|
||||
IMG_KING,
|
||||
NUM_IMAGE,
|
||||
}
|
||||
public class FLoatingTextManager : MonoBehaviour
|
||||
{
|
||||
public static FLoatingTextManager Instance { get; private set; }
|
||||
|
||||
[Header("Prefab")]
|
||||
[SerializeField] private AUIFloatTextIcon floatTextIconPrefab;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private int poolSize = 20;
|
||||
[SerializeField] private Vector3 offset = new Vector3(0, 2f, 0);
|
||||
[SerializeField] private float riseDistance = 1.5f;
|
||||
[SerializeField] private float riseDuration = 0.8f;
|
||||
/// <summary>
|
||||
/// Minimum spacing between floating texts for the same source object (seconds).
|
||||
/// 同一来源物体连续飘字的最小间隔(秒)。
|
||||
/// </summary>
|
||||
[SerializeField] private float staggerIntervalSeconds = 0.3f;
|
||||
|
||||
private readonly Queue<AUIFloatTextIcon> pool = new();
|
||||
/// <summary>
|
||||
/// Per-source next allowed show time (Unity instance ID → Time.time).
|
||||
/// </summary>
|
||||
private readonly Dictionary<int, float> _nextFloatingTextTimeBySourceId = new();
|
||||
|
||||
[Header("Sprite List")]
|
||||
[SerializeField] private Dictionary<ImageResType, Sprite> imageDic = new Dictionary<ImageResType, Sprite>();
|
||||
|
||||
/// <summary>
|
||||
/// Keeps Addressables handles alive so sprites in imageDic are not released.
|
||||
/// 保留 Addressables 句柄,避免已加载的 Sprite 被卸载。
|
||||
/// </summary>
|
||||
private readonly List<AsyncOperationHandle<Sprite>> _spriteLoadHandles = new List<AsyncOperationHandle<Sprite>>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Singleton
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
// Tạo sẵn pool
|
||||
for (int i = 0; i < poolSize; i++)
|
||||
{
|
||||
var textObj = Instantiate(floatTextIconPrefab, transform);
|
||||
textObj.gameObject.SetActive(false);
|
||||
pool.Enqueue(textObj);
|
||||
}
|
||||
LoadAllImages();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
foreach (var h in _spriteLoadHandles)
|
||||
{
|
||||
if (h.IsValid())
|
||||
Addressables.Release(h);
|
||||
}
|
||||
_spriteLoadHandles.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gọi để spawn text damage
|
||||
/// </summary>
|
||||
/// <param name="sourceForStagger">If set, multiple calls for this object are spaced by <see cref="staggerIntervalSeconds"/>.</param>
|
||||
public void ShowText(Vector3 worldPos, int damage, Color color = default, float scale = 1f, ImageResType imageResType = ImageResType.NUM_IMAGE, UnityEngine.Object sourceForStagger = null)
|
||||
{
|
||||
float delay = 0f;
|
||||
UnityEngine.Object staggerSource = sourceForStagger;
|
||||
if (staggerSource != null)
|
||||
{
|
||||
int key = staggerSource.GetInstanceID();
|
||||
float now = Time.time;
|
||||
if (!_nextFloatingTextTimeBySourceId.TryGetValue(key, out float nextSlot))
|
||||
nextSlot = now;
|
||||
float showAt = Mathf.Max(now, nextSlot);
|
||||
delay = showAt - now;
|
||||
_nextFloatingTextTimeBySourceId[key] = showAt + staggerIntervalSeconds;
|
||||
}
|
||||
|
||||
void DoShow()
|
||||
{
|
||||
if (staggerSource != null && !staggerSource)
|
||||
return;
|
||||
|
||||
var text = GetFromPool();
|
||||
var imageShow = imageResType == ImageResType.NUM_IMAGE ? null : imageDic[imageResType];
|
||||
if (damage > 0)
|
||||
text.Show(worldPos, damage.ToString(), color, scale, riseDistance, riseDuration, imageShow, () => ReturnToPool(text));
|
||||
else
|
||||
text.Show(worldPos, "", color, scale, riseDistance, riseDuration, imageShow, () => ReturnToPool(text));
|
||||
}
|
||||
|
||||
if (delay <= 0f)
|
||||
DoShow();
|
||||
else
|
||||
DOVirtual.DelayedCall(delay, DoShow, false).SetTarget(this);
|
||||
}
|
||||
|
||||
private AUIFloatTextIcon GetFromPool()
|
||||
{
|
||||
if (pool.Count > 0)
|
||||
{
|
||||
return pool.Dequeue();
|
||||
}
|
||||
|
||||
// Nếu hết pool, tạo thêm
|
||||
var text = Instantiate(floatTextIconPrefab, transform);
|
||||
text.gameObject.SetActive(false);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void ReturnToPool(AUIFloatTextIcon text)
|
||||
{
|
||||
pool.Enqueue(text);
|
||||
}
|
||||
public 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");
|
||||
//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");
|
||||
//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");
|
||||
//LoadImage(ImageResType.IMG_KING, "King/皇冠图标.tga");
|
||||
return false;
|
||||
}
|
||||
private void LoadImage(ImageResType type, string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
|
||||
// Same normalization as skill/gfx paths (PC backslashes → Addressables-style slashes).
|
||||
// 与技能 gfx 路径一致:反斜杠转为斜杠,便于与 Addressables 地址对齐。
|
||||
string normalized = path.Replace('\\', '/');
|
||||
IReadOnlyList<string> candidates = BuildSpriteAddressCandidates(normalized);
|
||||
|
||||
foreach (string address in candidates)
|
||||
{
|
||||
var handle = Addressables.LoadAssetAsync<Sprite>(address);
|
||||
handle.WaitForCompletion();
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded && handle.Result != null)
|
||||
{
|
||||
imageDic[type] = handle.Result;
|
||||
_spriteLoadHandles.Add(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
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).");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Addressables uses exact string addresses. Imported UI art may drop .tga/.dds in the key; try those variants.
|
||||
/// Addressables 为精确字符串地址;导入后地址可能没有 .tga/.dds 等后缀,依次尝试这些候选。
|
||||
/// </summary>
|
||||
private static List<string> BuildSpriteAddressCandidates(string path)
|
||||
{
|
||||
var list = new List<string> { path };
|
||||
|
||||
void addUnique(string s)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(s) && !list.Contains(s))
|
||||
list.Add(s);
|
||||
}
|
||||
|
||||
string[] sourceExtensions = { ".tga", ".dds", ".bmp", ".png", ".jpg", ".jpeg" };
|
||||
foreach (string ext in sourceExtensions)
|
||||
{
|
||||
if (path.EndsWith(ext, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
addUnique(path.Substring(0, path.Length - ext.Length));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8635fe618431341d796413e99407c5fc
|
||||
@@ -2044,13 +2044,12 @@ namespace BrewMonster
|
||||
{
|
||||
if (nDamage == -2)
|
||||
{
|
||||
// this message is caused by a help skill, so don't use a wounded action here
|
||||
/* if (dwModifier & CECAttackEvent::MOD_IMMUNE)
|
||||
BubbleText(BUBBLE_IMMUNE, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_NULLITY)
|
||||
BubbleText(BUBBLE_INVALIDHIT, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_DODGE_DEBUFF)
|
||||
BubbleText(BUBBLE_DODGE_DEBUFF, 0);*/
|
||||
if ((dwModifier & (uint)MOD.MOD_IMMUNE) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_IMMUNE, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_NULLITY) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_INVALIDHIT, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_DODGE_DEBUFF) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_DODGE_DEBUFF, 0);
|
||||
}
|
||||
else if (nDamage == -1)
|
||||
{
|
||||
@@ -2058,14 +2057,14 @@ namespace BrewMonster
|
||||
// Just play a wounded action
|
||||
if (!OnDamaged(skill))
|
||||
{
|
||||
// PlayAction((int)PLAYER_ACTION_TYPE.ACT_WOUNDED);
|
||||
PlayAction((int)PLAYER_ACTION_TYPE.ACT_WOUNDED);
|
||||
}
|
||||
/*if (dwModifier & CECAttackEvent::MOD_IMMUNE)
|
||||
BubbleText(BUBBLE_IMMUNE, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_NULLITY)
|
||||
BubbleText(BUBBLE_INVALIDHIT, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_DODGE_DEBUFF)
|
||||
BubbleText(BUBBLE_DODGE_DEBUFF, 0);*/
|
||||
if ((dwModifier & (uint)MOD.MOD_IMMUNE) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_IMMUNE, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_NULLITY) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_INVALIDHIT, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_DODGE_DEBUFF) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_DODGE_DEBUFF, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2073,34 +2072,178 @@ namespace BrewMonster
|
||||
if (nDamage > 0)
|
||||
{
|
||||
int p1 = 0;
|
||||
/*if (dwModifier & CECAttackEvent::MOD_CRITICAL_STRIKE)
|
||||
if ((dwModifier & (int)MOD.MOD_CRITICAL_STRIKE) != 0)
|
||||
p1 |= 0x0001;
|
||||
|
||||
if (dwModifier & CECAttackEvent::MOD_RETORT)
|
||||
p1 |= 0x0002;*/
|
||||
if ((dwModifier & (uint)MOD.MOD_RETORT) != 0)
|
||||
p1 |= 0x0002;
|
||||
|
||||
if (!OnDamaged(skill))
|
||||
// PlayAction((int)PLAYER_ACTION_TYPE.ACT_WOUNDED);
|
||||
PlayAction((int)PLAYER_ACTION_TYPE.ACT_WOUNDED);
|
||||
|
||||
DamageTextManager.Instance.ShowDamageText(
|
||||
transform.position, nDamage, Color.yellow, 1.0f);
|
||||
/* if (dwModifier & CECAttackEvent::MOD_IMMUNE)
|
||||
BubbleText(BUBBLE_IMMUNE, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_REBOUND)
|
||||
BubbleText(BUBBLE_REBOUND, nDamage);
|
||||
else if (dwModifier & CECAttackEvent::MOD_BEAT_BACK)
|
||||
BubbleText(BUBBLE_BEAT_BACK, nDamage);
|
||||
else
|
||||
BubbleText(BUBBLE_DAMAGE, nDamage, p1);*/
|
||||
if ((dwModifier & (uint)MOD.MOD_IMMUNE) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_IMMUNE, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_REBOUND) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_REBOUND, (uint)nDamage);
|
||||
else if ((dwModifier & (uint)MOD.MOD_BEAT_BACK) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_BEAT_BACK, (uint)nDamage);
|
||||
else
|
||||
BubbleText((int)BubbleTextType.BUBBLE_DAMAGE, (uint)nDamage, p1);
|
||||
}
|
||||
/* else if (dwModifier & CECAttackEvent::MOD_IMMUNE)
|
||||
BubbleText(BUBBLE_IMMUNE, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_NULLITY)
|
||||
BubbleText(BUBBLE_INVALIDHIT, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_IMMUNE) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_IMMUNE, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_NULLITY) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_INVALIDHIT, 0);
|
||||
else
|
||||
BubbleText(BUBBLE_HITMISSED, 0);*/
|
||||
BubbleText((int)BubbleTextType.BUBBLE_HITMISSED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void BubbleText(int iIndex, uint dwNum, int p1 = 0)
|
||||
{
|
||||
|
||||
bool bHost = IsHostPlayer();
|
||||
// if (iIndex == BUBBLE_EXP || iIndex == BUBBLE_SP)
|
||||
// bHost = false;
|
||||
|
||||
Color dwCol = new Color(255, 205, 75);
|
||||
|
||||
if (bHost)
|
||||
{
|
||||
var pGameRun = EC_Game.GetGameRun();
|
||||
CECHostPlayer pHost = pGameRun?.GetHostPlayer();
|
||||
if (iIndex == (int)BubbleTextType.BUBBLE_EXP)
|
||||
{
|
||||
bool hasBookExp = pHost != null && pHost.IsReincarnationTomeActive;
|
||||
int msgId = (int)dwNum > 0
|
||||
? (hasBookExp ? (int)FixedMsg.FIXMSG_GOT_BOOKEXP : (int)FixedMsg.FIXMSG_GOTEXP)
|
||||
: (int)FixedMsg.FIXMSG_LOSTEXP;
|
||||
pGameRun?.AddFixedChannelMsg(msgId, (int)ChatChannel.GP_CHAT_FIGHT, (int)dwNum);
|
||||
}
|
||||
if (iIndex == (int)BubbleTextType.BUBBLE_SP)
|
||||
pGameRun?.AddFixedChannelMsg((int)FixedMsg.FIXMSG_GOTSP, (int)ChatChannel.GP_CHAT_FIGHT, (int)dwNum);
|
||||
if (iIndex == (int)BubbleTextType.BUBBLE_REALMEXP)
|
||||
pGameRun?.AddFixedChannelMsg((int)FixedMsg.FIXMSG_GOT_REALMEXP, (int)ChatChannel.GP_CHAT_FIGHT, (int)dwNum);
|
||||
}
|
||||
|
||||
// Chariot / mount (C++): raise bubble by mount dummy AABB * 2.3f. IsInChariot() lives on CECHostPlayer; wire via override or protected hook when ready.
|
||||
// if (IsInChariot())
|
||||
// {
|
||||
// var dummy = GetDummyModel((int)PLAYERMODEL_TYPE.PLAYERMODEL_DUMMYTYPE2);
|
||||
// if (dummy != null)
|
||||
// vPos = GetPosVector3() + g_vAxisY * (dummy.GetModelAABB().Extents.y * 2.3f);
|
||||
// else
|
||||
// vPos = GetPosVector3() + g_vAxisY * (m_aabb.Extents.y * 2.5f);
|
||||
// }
|
||||
// else
|
||||
// vPos = GetPosVector3() + g_vAxisY * (m_aabb.Extents.y * 2.5f);
|
||||
Vector3 vPos = GetPosVector3() + g_vAxisY * (m_aabb.Extents.y * 2.5f);
|
||||
|
||||
ImageResType bubbleTextType = ImageResType.NUM_IMAGE;
|
||||
|
||||
switch (iIndex)
|
||||
{
|
||||
case (int)BubbleTextType.BUBBLE_DAMAGE:
|
||||
|
||||
if (!bHost)
|
||||
dwCol = new Color(237, 56, 0);
|
||||
|
||||
if ((p1 & 0x0001) != 0)
|
||||
bubbleTextType = ImageResType.IMG_DEADLYSTRIKE;
|
||||
else if ((p1 & 0x0002) != 0)
|
||||
bubbleTextType = ImageResType.IMG_RETORT;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_EXP:
|
||||
|
||||
bubbleTextType = ImageResType.IMG_GOTEXP;
|
||||
// pDecal->SetScreenPos(80, 70);
|
||||
// pDecal->EnableScreenPos(true);
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_SP:
|
||||
|
||||
bubbleTextType = ImageResType.IMG_GOTSP;
|
||||
// pDecal->SetScreenPos(80, 90);
|
||||
// pDecal->EnableScreenPos(true);
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_MONEY:
|
||||
|
||||
bubbleTextType = ImageResType.IMG_GOTMONEY;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_LEVELUP:
|
||||
|
||||
bubbleTextType = ImageResType.IMG_LEVELUP;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_HITMISSED:
|
||||
|
||||
if (!bHost)
|
||||
dwCol = new Color(237, 56, 0);
|
||||
|
||||
bubbleTextType = ImageResType.IMG_HITMISSED;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_INVALIDHIT:
|
||||
|
||||
if (!bHost)
|
||||
dwCol = new Color(237, 56, 0);
|
||||
|
||||
bubbleTextType = ImageResType.IMG_INVALIDHIT;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_IMMUNE:
|
||||
|
||||
if (!bHost)
|
||||
dwCol = new Color(237, 56, 0);
|
||||
|
||||
bubbleTextType = ImageResType.IMG_IMMUNE;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_HPWARN:
|
||||
|
||||
dwCol = new Color(255, 255, 255);
|
||||
bubbleTextType = ImageResType.IMG_HPWARN;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_MPWARN:
|
||||
|
||||
dwCol = new Color(255, 255, 255);
|
||||
bubbleTextType = ImageResType.IMG_MPWARN;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_REBOUND:
|
||||
|
||||
bubbleTextType = ImageResType.IMG_REBOUND;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_BEAT_BACK:
|
||||
|
||||
bubbleTextType = ImageResType.IMG_BEAT_BACK;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_ADD:
|
||||
|
||||
dwCol = new Color(126, 206, 244);
|
||||
bubbleTextType = ImageResType.IMG_ADD;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_DODGE_DEBUFF:
|
||||
|
||||
if (!bHost)
|
||||
dwCol = new Color(237, 56, 0);
|
||||
|
||||
bubbleTextType = ImageResType.IMG_DODGE_DEBUFF;
|
||||
break;
|
||||
|
||||
default:
|
||||
bubbleTextType = ImageResType.NUM_IMAGE;
|
||||
break;
|
||||
}
|
||||
|
||||
FLoatingTextManager.Instance.ShowText(vPos, (int)dwNum, dwCol, 1f, bubbleTextType, this);
|
||||
}
|
||||
public void StopSkillAttackAction()
|
||||
{
|
||||
if (m_pActionController != null)
|
||||
@@ -3894,6 +4037,26 @@ namespace BrewMonster
|
||||
WINGTYPE_FLYSWORD, // 飞行器类型:飞剑
|
||||
WINGTYPE_DOUBLEWHEEL, // 飞行器类型:双脚飞行器
|
||||
};
|
||||
// Bubble text
|
||||
public enum BubbleTextType
|
||||
{
|
||||
BUBBLE_DAMAGE = 0,
|
||||
BUBBLE_EXP,
|
||||
BUBBLE_SP,
|
||||
BUBBLE_MONEY,
|
||||
BUBBLE_LEVELUP,
|
||||
BUBBLE_HITMISSED,
|
||||
BUBBLE_INVALIDHIT,
|
||||
BUBBLE_IMMUNE,
|
||||
BUBBLE_HPWARN,
|
||||
BUBBLE_MPWARN,
|
||||
BUBBLE_REBOUND, // ����
|
||||
BUBBLE_BEAT_BACK, // ����
|
||||
BUBBLE_ADD, // ��Ѫ�ļӺ�
|
||||
BUBBLE_DODGE_DEBUFF,
|
||||
BUBBLE_REALMEXP,
|
||||
BUBBLE_NUM,
|
||||
};
|
||||
public struct PVPINFO
|
||||
{
|
||||
public bool bEnable; // PVP switch
|
||||
|
||||
@@ -8,7 +8,6 @@ using System;
|
||||
using System.Threading.Tasks;
|
||||
using BrewMonster.Scripts.Chat;
|
||||
using UnityEngine;
|
||||
|
||||
public class CECNPC : CECObject
|
||||
{
|
||||
[SerializeField] protected INFO m_NPCInfo;
|
||||
@@ -824,58 +823,105 @@ public class CECNPC : CECObject
|
||||
// so if iDamage is -1 we will shoud the wounded animation
|
||||
if (iDamage == -1 && !m_bStartFight)
|
||||
PlayModelAction((int)NPCActionIndex.ACT_WOUNDED);
|
||||
DamageTextManager.Instance.ShowDamageText(transform.position, iDamage, Color.red, 1.0f);
|
||||
FLoatingTextManager.Instance.ShowText(transform.position, iDamage, Color.red, 1.0f, ImageResType.NUM_IMAGE, this);
|
||||
|
||||
/*if ((dwModifier & (uint)MOD.MOD_IMMUNE) != 0 *//* && !IsImmuneDisable()*//*)
|
||||
textma
|
||||
else if (dwModifier & CECAttackEvent::MOD_NULLITY)
|
||||
BubbleText(BUBBLE_INVALIDHIT, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_ENCHANT_FAILED)
|
||||
BubbleText(BUBBLE_LOSE, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_SUCCESS)
|
||||
BubbleText(BUBBLE_SUCCESS, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_DODGE_DEBUFF)
|
||||
BubbleText(BUBBLE_DODGE_DEBUFF, 0);*/
|
||||
if ((dwModifier & (uint)MOD.MOD_IMMUNE) != 0 /* && !IsImmuneDisable()*/)
|
||||
BubbleText((int)MOD.MOD_IMMUNE, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_NULLITY) != 0)
|
||||
BubbleText((int)MOD.MOD_NULLITY, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_ENCHANT_FAILED) != 0)
|
||||
BubbleText((int)MOD.MOD_ENCHANT_FAILED, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_SUCCESS) != 0)
|
||||
BubbleText((int)MOD.MOD_SUCCESS, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_DODGE_DEBUFF) != 0)
|
||||
BubbleText((int)MOD.MOD_DODGE_DEBUFF, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this message is related to the host, so we should show a pop up message
|
||||
// Popup a damage decal
|
||||
/* bool bDeadlyStrike = (dwModifier & CECAttackEvent::MOD_CRITICAL_STRIKE) ? true : false;
|
||||
bool bRetort = (dwModifier & CECAttackEvent::MOD_RETORT) ? true : false;*/
|
||||
bool bDeadlyStrike = (dwModifier & (uint)MOD.MOD_CRITICAL_STRIKE) != 0;
|
||||
bool bRetort = (dwModifier & (uint)MOD.MOD_RETORT) != 0;
|
||||
|
||||
if (iDamage > 0)
|
||||
{
|
||||
if (!m_bStartFight)
|
||||
PlayModelAction((int)NPCActionIndex.ACT_WOUNDED);
|
||||
|
||||
DamageTextManager.Instance.ShowDamageText(transform.position, iDamage, Color.red, 1.0f);
|
||||
/* int p1 = 0;
|
||||
if (bDeadlyStrike)
|
||||
p1 |= 0x0001;
|
||||
else if (bRetort)
|
||||
p1 |= 0x0002;*/
|
||||
// Damage number + tint/icons: BubbleText → ShowText only (avoid duplicate red plain text + orange BubbleText).
|
||||
// 伤害数字与图标只走 BubbleText,避免先红字再橙字飘两次。
|
||||
int p1 = 0;
|
||||
if (bDeadlyStrike)
|
||||
p1 |= 0x0001;
|
||||
else if (bRetort)
|
||||
p1 |= 0x0002;
|
||||
|
||||
/* if (dwModifier & CECAttackEvent::MOD_REBOUND)
|
||||
BubbleText(BUBBLE_REBOUND, (DWORD)iDamage);
|
||||
else if (dwModifier & CECAttackEvent::MOD_BEAT_BACK)
|
||||
BubbleText(BUBBLE_BEAT_BACK, (DWORD)iDamage);
|
||||
else
|
||||
BubbleText(BUBBLE_DAMAGE, (DWORD)iDamage, p1);*/
|
||||
if ((dwModifier & (uint)MOD.MOD_REBOUND) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_REBOUND, iDamage);
|
||||
else if ((dwModifier & (uint)MOD.MOD_BEAT_BACK) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_BEAT_BACK, iDamage);
|
||||
else
|
||||
BubbleText((int)BubbleTextType.BUBBLE_DAMAGE, iDamage, p1);
|
||||
}
|
||||
/* else if ((dwModifier & CECAttackEvent::MOD_IMMUNE) && !IsImmuneDisable())
|
||||
BubbleText(BUBBLE_IMMUNE, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_NULLITY)
|
||||
BubbleText(BUBBLE_INVALIDHIT, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_ENCHANT_FAILED)
|
||||
BubbleText(BUBBLE_LOSE, 0);
|
||||
else if (dwModifier & CECAttackEvent::MOD_SUCCESS)
|
||||
BubbleText(BUBBLE_SUCCESS, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_IMMUNE) != 0 /*&& !IsImmuneDisable()*/)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_IMMUNE, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_NULLITY) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_INVALIDHIT, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_ENCHANT_FAILED) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_LOSE, 0);
|
||||
else if ((dwModifier & (uint)MOD.MOD_SUCCESS) != 0)
|
||||
BubbleText((int)BubbleTextType.BUBBLE_SUCCESS, 0);
|
||||
else
|
||||
BubbleText(BUBBLE_HITMISSED, 0);*/
|
||||
BubbleText((int)BubbleTextType.BUBBLE_HITMISSED, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void BubbleText(int iIndex, int dwNum, int p1 = 0/* 0 */)
|
||||
{
|
||||
//FLoatingTextManager.Instance.ShowText(transform.position, dwNum, Color.red, 1.0f, (uint)iIndex);
|
||||
Color displayColor = new Color(237, 56, 0);
|
||||
ImageResType imageResType = ImageResType.NUM_IMAGE;
|
||||
switch (iIndex)
|
||||
{
|
||||
case (int)BubbleTextType.BUBBLE_DAMAGE:
|
||||
if ((p1 & 0x0001) != 0)
|
||||
imageResType = ImageResType.IMG_DEADLYSTRIKE;
|
||||
else if ((p1 & 0x0002) != 0)
|
||||
imageResType = ImageResType.IMG_RETORT;
|
||||
break;
|
||||
|
||||
case (int)BubbleTextType.BUBBLE_HITMISSED:
|
||||
imageResType = ImageResType.IMG_HITMISSED;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_INVALIDHIT:
|
||||
imageResType = ImageResType.IMG_INVALIDHIT;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_IMMUNE:
|
||||
imageResType = ImageResType.IMG_IMMUNE;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_HPWARN:
|
||||
imageResType = ImageResType.IMG_HPWARN;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_LOSE:
|
||||
imageResType = ImageResType.IMG_ATTACKLOSE;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_SUCCESS:
|
||||
imageResType = ImageResType.IMG_SUCCESS;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_REBOUND:
|
||||
imageResType = ImageResType.IMG_REBOUND;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_BEAT_BACK:
|
||||
imageResType = ImageResType.IMG_BEAT_BACK;
|
||||
break;
|
||||
case (int)BubbleTextType.BUBBLE_DODGE_DEBUFF:
|
||||
imageResType = ImageResType.IMG_DODGE_DEBUFF;
|
||||
break;
|
||||
default:
|
||||
imageResType = ImageResType.NUM_IMAGE;
|
||||
break;
|
||||
}
|
||||
FLoatingTextManager.Instance.ShowText(transform.position, dwNum, displayColor, 1.0f, imageResType, this);
|
||||
}
|
||||
public void WorkFinished(int iWorkID)
|
||||
{
|
||||
// Note: below judge can prevent many problems when we attempt to
|
||||
@@ -1710,6 +1756,19 @@ public class CECNPC : CECObject
|
||||
CECModel model = GetModel();
|
||||
return model?.GetHook(hookName, recursive);
|
||||
}
|
||||
public enum BubbleTextType
|
||||
{
|
||||
BUBBLE_DAMAGE = 0,
|
||||
BUBBLE_HITMISSED,
|
||||
BUBBLE_INVALIDHIT,
|
||||
BUBBLE_IMMUNE,
|
||||
BUBBLE_HPWARN,
|
||||
BUBBLE_LOSE,
|
||||
BUBBLE_SUCCESS,
|
||||
BUBBLE_REBOUND, // ����
|
||||
BUBBLE_BEAT_BACK, // ����
|
||||
BUBBLE_DODGE_DEBUFF,
|
||||
};
|
||||
}
|
||||
public struct ClearComActFlagEvent
|
||||
{
|
||||
@@ -1777,6 +1836,8 @@ public enum NPCActionIndex
|
||||
ACT_WOUNDED2,
|
||||
ACT_MAX,
|
||||
};
|
||||
//BUBBLE TEXT TYPE FOR NPC. HOST IS DIFFERENT FROM NPC.
|
||||
|
||||
public ref struct ByteReader
|
||||
{
|
||||
private ReadOnlySpan<byte> _span;
|
||||
|
||||
@@ -3039,5 +3039,11 @@ namespace CSNetwork.GPDataType
|
||||
{
|
||||
public int id; // self id or pet id.
|
||||
};
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct cmd_receive_exp
|
||||
{
|
||||
public int exp;
|
||||
public int sp;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1291,6 +1291,9 @@ namespace CSNetwork
|
||||
|
||||
break;
|
||||
}
|
||||
case CommandID.RECEIVE_EXP:
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_RECEIVEEXP, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
||||
break;
|
||||
case CommandID.LEVEL_UP:
|
||||
{
|
||||
cmd_level_up pCmdLevelUp = GPDataTypeHelper.FromBytes<cmd_level_up>(pDataBuf); ;
|
||||
|
||||
@@ -480,6 +480,7 @@ namespace BrewMonster
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandID.PET_REBUILD_INHERIT_START:
|
||||
case CommandID.PET_REBUILD_INHERIT_INFO:
|
||||
case CommandID.PET_REBUILD_INHERIT_END:
|
||||
@@ -568,6 +569,35 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnMsgHstReceiveExp (ECMSG Msg)
|
||||
{
|
||||
cmd_receive_exp pCmd = GPDataTypeHelper.FromBytes<cmd_receive_exp>((byte[])Msg.dwParam1);
|
||||
|
||||
if (m_ReincarnationTome.tome_active != 0) {
|
||||
//dunno what it do "?
|
||||
// m_ReincarnationTome.tome_exp += pCmd.exp;
|
||||
// CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
|
||||
// CDlgReincarnationBook* pDlgBook = dynamic_cast<CDlgReincarnationBook*>(pGameUI->GetDialog("Win_ReincarnationBook"));
|
||||
// if (pDlgBook && pDlgBook->IsShow()) pDlgBook->Update();
|
||||
// CDlgReincarnationRewrite* pDlgRewrite = dynamic_cast<CDlgReincarnationRewrite*>(pGameUI->GetDialog("Win_ReincarnationRewrite"));
|
||||
// if (pDlgRewrite && pDlgRewrite->IsShow()) pDlgRewrite->Update();
|
||||
}
|
||||
|
||||
if (pCmd.exp != 0)
|
||||
{
|
||||
// Print notify text
|
||||
// g_pGame->GetGameRun()->AddFixedMessage(pCmd->exp > 0 ? FIXMSG_GOTEXP : FIXMSG_LOSTEXP, (int)pCmd->exp);
|
||||
BubbleText((int)BubbleTextType.BUBBLE_EXP, (uint)pCmd.exp);
|
||||
}
|
||||
|
||||
if (pCmd.sp > 0)
|
||||
{
|
||||
// Print notify text
|
||||
// g_pGame->GetGameRun()->AddFixedMessage(FIXMSG_GOTSP, (int)pCmd->sp);
|
||||
BubbleText((int)BubbleTextType.BUBBLE_SP, (uint)pCmd.sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using TMPro;
|
||||
using DG.Tweening; // cần DOTween
|
||||
using BrewMonster.Scripts.UI.GamePlay;
|
||||
using UnityEngine.UI;
|
||||
namespace BrewMonster.Scripts.UI.GamePlay
|
||||
{
|
||||
public class AUIFloatTextIcon : MonoBehaviour
|
||||
{
|
||||
[SerializeField] TMP_Text text;
|
||||
[SerializeField] SpriteRenderer ingameIcon;
|
||||
public void Show(Vector3 worldPos, string damage, Color color = default, float scale = 1f, float riseDistance = 1f, float riseDuration = 1f, Sprite sprite = null, Action onComplete = null)
|
||||
{
|
||||
if(sprite != null)
|
||||
{
|
||||
ingameIcon.sprite = sprite;
|
||||
ingameIcon.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ingameIcon.gameObject.SetActive(false);
|
||||
}
|
||||
text.text = damage;
|
||||
text.color = color;
|
||||
text.fontSize = 6;
|
||||
text.transform.localScale = Vector3.one * scale;
|
||||
text.transform.position = worldPos;
|
||||
gameObject.SetActive(true);
|
||||
|
||||
// Hiệu ứng bay lên + mờ dần
|
||||
text.transform.DOMoveY(worldPos.y + riseDistance, riseDuration).SetEase(Ease.OutQuad);
|
||||
text.DOFade(0f, riseDuration)
|
||||
.SetEase(Ease.InQuad)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
text.alpha = 1f;
|
||||
text.gameObject.SetActive(false);
|
||||
onComplete?.Invoke();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0515aed5525ee47d4b5249804a555471
|
||||
Reference in New Issue
Block a user