fix performent issual of using medicine

This commit is contained in:
Chomper9981
2026-04-08 16:58:48 +07:00
parent 1bcae728db
commit 8ad46acfaa
3 changed files with 144 additions and 16 deletions
@@ -81,6 +81,7 @@ namespace BrewMonster.Scripts
var old = m_aItems[iSlot];
m_aItems[iSlot] = pItem;
EventBus.Publish(new InventoryChangedEvent());
return old;
}
@@ -92,6 +93,7 @@ namespace BrewMonster.Scripts
}
m_aItems[iSlot] = pItem;
EventBus.Publish(new InventoryChangedEvent());
}
public EC_IvtrItem GetItem(int iSlot, bool bRemove = false)
@@ -144,6 +146,7 @@ namespace BrewMonster.Scripts
m_aItems[iSlot] = newItem;
piLastSlot = iSlot;
piLastAmount = iAmount;
EventBus.Publish(new InventoryChangedEvent());
return true;
}
if (slotItem.GetTemplateID() != tid)
@@ -156,6 +159,7 @@ namespace BrewMonster.Scripts
slotItem.AddAmount(add);
piLastSlot = iSlot;
piLastAmount = slotItem.GetCount();
EventBus.Publish(new InventoryChangedEvent());
return true;
}
@@ -179,6 +183,7 @@ namespace BrewMonster.Scripts
{
piLastSlot = i;
piLastAmount = slotItem.GetCount();
EventBus.Publish(new InventoryChangedEvent());
return true;
}
}
@@ -201,6 +206,7 @@ namespace BrewMonster.Scripts
m_aItems[firstEmpty] = newItem;
piLastSlot = firstEmpty;
piLastAmount = iAmount;
EventBus.Publish(new InventoryChangedEvent());
return true;
}
@@ -240,6 +246,7 @@ namespace BrewMonster.Scripts
}
RemoveItem(iSrc, iAmount);
EventBus.Publish(new InventoryChangedEvent());
return true;
}
@@ -258,6 +265,7 @@ namespace BrewMonster.Scripts
if (newCount <= 0)
m_aItems[iSlot] = null;
EventBus.Publish(new InventoryChangedEvent());
return true;
}
@@ -352,4 +360,10 @@ namespace BrewMonster.Scripts
}
}
}
/// <summary>
/// Fired when any client-side inventory pack mutates (add/remove/move/merge).
/// Used by UI (quickbar HP/MP auto-pick) to re-evaluate best potions.
/// </summary>
public struct InventoryChangedEvent { }
}
@@ -7,6 +7,30 @@ namespace BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay
public class AUIImageHPMPItem : AUIImagePicture
{
[SerializeField] TMP_Text m_TextAmount;
private int _lastTemplateId = int.MinValue;
private int _lastCount = int.MinValue;
public bool TrySetTemplateId(int templateId)
{
if (_lastTemplateId == templateId)
return false;
_lastTemplateId = templateId;
return true;
}
public bool TrySetCount(int count)
{
if (_lastCount == count)
return false;
_lastCount = count;
return true;
}
public void ResetRenderCache()
{
_lastTemplateId = int.MinValue;
_lastCount = int.MinValue;
}
public void SetText(string text)
{
if(m_TextAmount != null)
@@ -23,8 +23,34 @@ namespace BrewMonster
[SerializeField] AUIImageHPMPItem HpItemButton = new AUIImageHPMPItem();
[SerializeField] AUIImageHPMPItem MpItemButton = new AUIImageHPMPItem();
private bool _hpmpReselectDirty = true;
int currentListIndex = 0;
CECSkill assignedSkill = null;
public override void OnEnable()
{
base.OnEnable();
EventBus.Subscribe<InventoryChangedEvent>(OnInventoryChanged);
EventBus.Subscribe<CECHostPlayer.HostPlayerLevelUpUIEvent>(OnHostLevelUp);
_hpmpReselectDirty = true;
}
public override void OnDisable()
{
EventBus.Unsubscribe<InventoryChangedEvent>(OnInventoryChanged);
EventBus.Unsubscribe<CECHostPlayer.HostPlayerLevelUpUIEvent>(OnHostLevelUp);
base.OnDisable();
}
private void OnInventoryChanged(InventoryChangedEvent _)
{
_hpmpReselectDirty = true;
}
private void OnHostLevelUp(CECHostPlayer.HostPlayerLevelUpUIEvent _)
{
_hpmpReselectDirty = true;
}
/// <summary>
/// Apply for a license remove later
/// </summary>
@@ -59,7 +85,7 @@ namespace BrewMonster
AUIImagePicture pCell;
CECSkill pSkill = new CECSkill(-1, -1);
AUIClockIcon pClock;
int iIconFile, nMax = 0;
int iIconFile = 0, nMax = 0;
int nCurPanel9 = GetCurPanel1();
int nCurPanel8 = GetCurPanel2();
@@ -444,6 +470,9 @@ namespace BrewMonster
if (pHost == null)
return;
bool forceReselect = _hpmpReselectDirty;
_hpmpReselectDirty = false;
// Pick the first level-usable medicine with the lowest required level.
// Prefer highest heal amount first, then lowest required level.
// 优先选择“回复量最高”的药品,其次选择需求等级最低的 (Prefer max heal, tie-break by level).
@@ -519,6 +548,12 @@ namespace BrewMonster
return item != null;
}
static int GetHealScore(EC_IvtrMedicine med, int majorTypeId)
{
if (med == null) return 0;
return majorTypeId == 1794 ? med.GetHpAddTotal() : med.GetMpAddTotal();
}
void ApplyItemToButton(AUIImageHPMPItem button, CECShortcut sc, EC_IvtrItem item)
{
if (button == null)
@@ -526,23 +561,39 @@ namespace BrewMonster
if (sc == null || item == null)
{
button.ResetRenderCache();
button.Clear();
button.SetText(string.Empty);
return;
}
button.SetDataPtr(sc);
// Inventory UI uses templateId -> ResolveItemIconSprite, which is more reliable than string icon keys here.
// 背包UI使用 templateId -> ResolveItemIconSprite,这里也用同一套更稳定 (use same path as Inventory UI).
var sprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(item.GetTemplateID());
if (sprite != null)
button.SetImage(sprite);
else
button.Clear();
// Skill-slot logic pattern: if the shortcut ptr hasn't changed and we're not forced to reselect,
// avoid re-applying icon/text every frame.
// 复用技能格的思路:快捷键指针没变且非强制刷新时,不要每帧重刷图标/数量。
if (button.GetDataPtr() != sc)
{
button.SetDataPtr(sc);
button.ResetRenderCache();
}
int tid = item.GetTemplateID();
if (button.TrySetTemplateId(tid))
{
// Inventory UI path is more reliable than icon-file string matching.
// 背包UI的路径更稳定:按模板ID解析Sprite,而不是匹配图标文件名字符串
var sp = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(tid);
if (sp != null)
button.SetImage(sp);
else
button.Clear();
}
// Amount text / 数量文本
int count = Mathf.Max(0, item.GetCount());
button.SetText(count > 0 ? count.ToString() : string.Empty);
if (button.TrySetCount(count))
{
button.SetText(count > 0 ? count.ToString() : string.Empty);
}
// Cooldown overlay / 冷却遮罩
var clock = button.GetClockIcon();
@@ -566,9 +617,13 @@ namespace BrewMonster
// Interactable state should reflect usability.
// 可交互状态应该反映物品是否可使用
bool usable = item.CheckUseCondition();
// Mirror host-side restrictions like `UseItemInPack` uses (CANDO_USEITEM etc.)
// 同步宿主侧限制(例如 CANDO_USEITEM),与 UseItemInPack 的限制保持一致
bool hostCanUseItem = pHost.CanDo(CECHostPlayer.ActionCanDo.CANDO_USEITEM);
bool usable = hostCanUseItem && item.CheckUseCondition() && !item.IsFrozen();
bool cooling = (cool > 0 && max > 0);
button.SetInteract(usable && !cooling);
bool interact = usable && !cooling;
button.SetInteract(interact);
}
void EnsureAssignedAndRefresh(AUIImageHPMPItem button, int majorTypeId)
@@ -578,15 +633,50 @@ namespace BrewMonster
CECShortcut sc = button.GetDataPtr();
// Resolve current shortcut item (if any).
bool hasCurrent = TryGetShortcutItem(pHost, sc, out EC_IvtrItem item);
// Find best candidate (used both for initial assign and reselect-on-change).
bool hasBest = TryFindBestMedicine(pHost, majorTypeId, out int bestPack, out int bestSlot, out EC_IvtrMedicine bestMed);
// If there is no valid shortcut item, auto assign the best available potion.
if (!TryGetShortcutItem(pHost, sc, out EC_IvtrItem item))
if (!hasCurrent)
{
if (TryFindBestMedicine(pHost, majorTypeId, out int pack, out int slot, out EC_IvtrMedicine med))
if (hasBest)
{
var newSc = new CECSCItem();
newSc.Init(pack, slot, med);
newSc.Init(bestPack, bestSlot, bestMed);
sc = newSc;
item = med;
item = bestMed;
hasCurrent = true;
}
}
else if (forceReselect && hasBest)
{
// Upgrade to best potion when inventory changes or level changes.
// 背包变化或升级时,自动切换到“最优药品”
var curMed = item as EC_IvtrMedicine;
if (curMed == null || curMed.GetMajorTypeId() != majorTypeId || !curMed.CheckUseCondition())
{
var newSc = new CECSCItem();
newSc.Init(bestPack, bestSlot, bestMed);
sc = newSc;
item = bestMed;
}
else
{
int curHeal = GetHealScore(curMed, majorTypeId);
int bestHeal = GetHealScore(bestMed, majorTypeId);
int curReq = curMed.GetLevelReq();
int bestReq = bestMed.GetLevelReq();
if (bestHeal > curHeal || (bestHeal == curHeal && bestReq < curReq))
{
var newSc = new CECSCItem();
newSc.Init(bestPack, bestSlot, bestMed);
sc = newSc;
item = bestMed;
}
}
}