Fix some bug

This commit is contained in:
Tran Hai Nam
2026-05-19 15:25:59 +07:00
parent 9baa8207b0
commit 6fb8e22355
16 changed files with 434 additions and 95 deletions
@@ -59,6 +59,11 @@ namespace BrewMonster
[Tooltip("When enabled, hides skills that fail weapon/form/move-env restrictions. Disable to show ALL positive skills regardless of restrictions.")]
[SerializeField] private bool filterByRestrictions = false;
[Tooltip("Match CDlgSkillSubList god/evil path: false = base+god ranks, true = base+evil ranks.")]
[SerializeField] private bool isEvilSkillPath;
[SerializeField] private int skillCatalogLevel = 1;
/// <summary>
/// World position of the draggable target marker, read by CECSkillGfxMan.get_pos_by_id.
/// 可拖动目标标记的世界坐标,由 CECSkillGfxMan.get_pos_by_id 读取。
@@ -101,6 +106,19 @@ namespace BrewMonster
gameRun.RegisterAnimSceneHostPlayer(player);
}
/// <summary>
/// Anim scene host swap: keep panel player reference in sync with bootstrap.
/// 动画场景切换角色时同步面板上的主角引用。
/// </summary>
public void SetHostPlayer(CECHostPlayer host)
{
if (host == null)
return;
player = host;
RegisterSceneHostWithGameRun();
}
/// <summary>Target id for <see cref="LocalCastSkill"/> / GFX composer (self = host cid, else marker NPC id).</summary>
private int ResolveLocalCastTargetId(CECSkill skill)
{
@@ -114,14 +132,11 @@ namespace BrewMonster
}
/// <summary>
/// Polls every 0.5 s until the host player has skills and CECGameUIMan is initialized,
/// then calls Refresh() once. If no server skill data arrives within <see cref="k_InjectFallbackSecs"/>
/// seconds, injects all config skills directly from SkillStub.map so the panel works offline.
/// Polls until CECGameUIMan is ready and the host has skills (server, skill model, or config fallback).
///
/// 每 0.5 秒轮询直到主角已有技能且 CECGameUIMan 初始化完毕,再调用一次 Refresh()
/// 若 k_InjectFallbackSecs 秒内未收到服务端技能数据,则直接从 SkillStub.map 注入所有配置技能,以便离线使用。
/// 轮询直到 UI 图集与技能数据就绪(服务端、CECHostSkillModel 或配置回退)
/// </summary>
private const float k_InjectFallbackSecs = 2f;
private const float k_RefreshWaitTimeoutSecs = 30f;
private IEnumerator RefreshWhenReady()
{
var wait = new WaitForSeconds(0.5f);
@@ -135,17 +150,23 @@ namespace BrewMonster
yield return wait;
}
// Wait for skills, but fall back to config injection after timeout.
// 等待技能数据,超时后回退到配置注入。
while (player != null && player.GetPositiveSkillNum() == 0)
ResolveHostPlayerReference();
// Wait for skills: server list, CECHostSkillModel catalog (anim scene), or config fallback.
// 等待技能:服务端列表、CECHostSkillModel 目录(动画场景)或配置回退。
while (player != null && !HasSkillCatalogOrPositiveSkills())
{
if (elapsed >= k_InjectFallbackSecs)
if (TryInjectFromSkillModel())
break;
if (elapsed >= k_RefreshWaitTimeoutSecs)
{
Debug.Log("[SkillTriggerPanel] No server skills after " +
$"{k_InjectFallbackSecs}s — injecting all config skills for offline debug.");
Debug.LogWarning("[SkillTriggerPanel] No skills after " +
$"{k_RefreshWaitTimeoutSecs}s — falling back to SkillStub.map injection.");
player.InjectDebugSkillsFromConfig();
break;
}
elapsed += 0.5f;
yield return wait;
}
@@ -153,6 +174,36 @@ namespace BrewMonster
Refresh();
}
private void ResolveHostPlayerReference()
{
if (player != null)
return;
player = EC_Game.GetGameRun()?.GetHostPlayer();
}
private bool HasSkillCatalogOrPositiveSkills()
{
if (player != null && player.GetPositiveSkillNum() > 0)
return true;
List<int> catalog = CECHostSkillModel.Instance?.CollectSkillSubListSkillIds(isEvilSkillPath);
return catalog != null && catalog.Count > 0;
}
private bool TryInjectFromSkillModel()
{
if (player == null)
return false;
List<int> catalog = CECHostSkillModel.Instance?.CollectSkillSubListSkillIds(isEvilSkillPath);
if (catalog == null || catalog.Count == 0)
return false;
player.InjectSkillsFromSkillModel(skillCatalogLevel, isEvilSkillPath);
return true;
}
/// <summary>
/// Loads every skill defined in SkillStub.map (config) directly into the player's skill list,
/// bypassing the server skill message. Use this when testing locally without a server.
@@ -171,6 +222,46 @@ namespace BrewMonster
Refresh();
}
/// <summary>
/// Rebuild host positive skills from <see cref="CECHostSkillModel"/> (same catalog as CDlgSkillSubList) and refresh the grid.
/// AnimScenePlayerBootstrap calls this after each model / role switch.
///
/// 从 CECHostSkillModel 重建主角正向技能并刷新网格(与 CDlgSkillSubList 相同目录)。
/// </summary>
[ContextMenu("Debug: Refresh From Skill Model")]
public void RefreshFromSkillModel(int level = -1)
{
ResolveHostPlayerReference();
if (player == null)
{
Debug.LogWarning("[SkillTriggerPanel] RefreshFromSkillModel: no host player.");
return;
}
if (level > 0)
skillCatalogLevel = level;
player.InjectSkillsFromSkillModel(skillCatalogLevel, isEvilSkillPath);
List<CECSkill> catalogSkills = player.BuildSkillSubListSkills(skillCatalogLevel, isEvilSkillPath);
PopulateSkillGrid(catalogSkills, catalogSkills.Count);
}
public void SetEvilSkillPath(bool isEvil)
{
isEvilSkillPath = isEvil;
}
/// <summary>
/// Reload from CECHostSkillModel and optionally hide skills that fail weapon/form/env checks.
/// 从技能目录重新加载;applyRestrictions 为 true 时按武器/形态/环境过滤。
/// </summary>
public void ResetSkillsFromSkillModel(bool applyRestrictions, int level = -1)
{
filterByRestrictions = applyRestrictions;
RefreshFromSkillModel(level);
}
/// <summary>
/// Clears and repopulates the skill grid from the host player's current positive skill list.
/// Call again after equipping a different weapon or changing shape/form.
@@ -197,12 +288,14 @@ namespace BrewMonster
return;
}
// Clear existing buttons / 清空已有按钮
foreach (Transform child in skillGridContainer)
Destroy(child.gameObject);
var catalogSkills = player.BuildSkillSubListSkills(skillCatalogLevel, isEvilSkillPath);
if (catalogSkills.Count > 0)
{
PopulateSkillGrid(catalogSkills, catalogSkills.Count);
return;
}
int total = player.GetPositiveSkillNum();
if (total == 0)
{
Debug.LogWarning("[SkillTriggerPanel] GetPositiveSkillNum() = 0. " +
@@ -211,6 +304,22 @@ namespace BrewMonster
return;
}
var positiveSkills = new List<CECSkill>(total);
for (int i = 0; i < total; i++)
{
CECSkill skill = player.GetPositiveSkillByIndex(i);
if (skill != null)
positiveSkills.Add(skill);
}
PopulateSkillGrid(positiveSkills, total);
}
private void PopulateSkillGrid(IReadOnlyList<CECSkill> skills, int totalListed)
{
foreach (Transform child in skillGridContainer)
Destroy(child.gameObject);
CECGameUIMan uiMan = CECUIManager.Instance?.GetInGameUIMan();
if (uiMan == null)
{
@@ -221,9 +330,9 @@ namespace BrewMonster
int added = 0;
int filtered = 0;
for (int i = 0; i < total; i++)
for (int i = 0; i < skills.Count; i++)
{
CECSkill skill = player.GetPositiveSkillByIndex(i);
CECSkill skill = skills[i];
if (skill == null)
continue;
@@ -235,21 +344,19 @@ namespace BrewMonster
GameObject cell = Instantiate(skillButtonPrefab, skillGridContainer);
// --- Icon ---
string iconName = skill.GetIconFile();
if (!string.IsNullOrEmpty(iconName))
{
Sprite icon = uiMan.GetIcon(iconName, EC_GAMEUI_ICONS.ICONS_SKILL);
Image cellImage = GetIconImage(cell);
Debug.Log("[SkillTriggerPanel] icon = " + (icon == null) + "cellImage = " + (cellImage == null));
if (cellImage != null && icon != null)
cellImage.sprite = icon;
}
else{
else
{
Debug.LogWarning("[SkillTriggerPanel] skill.GetIconFile() is empty for skill " + skill.GetSkillID());
}
// --- Label ---
Text cellText = cell.GetComponentInChildren<Text>();
if (cellText != null)
{
@@ -259,7 +366,6 @@ namespace BrewMonster
: $"{skillName}\nLv{skill.GetSkillLevel()}";
}
// --- Button ---
CECSkill captured = skill;
Button btn = cell.GetComponentInChildren<Button>();
if (btn != null)
@@ -269,7 +375,7 @@ namespace BrewMonster
}
Debug.Log($"[SkillTriggerPanel] Refreshed: {added} buttons added " +
$"({filtered} filtered by restrictions, {total} total positive skills).");
$"({filtered} filtered by restrictions, {totalListed} catalog/server skills).");
}
/// <summary>
@@ -294,7 +400,7 @@ namespace BrewMonster
if (weapon != null && weapon.CurEndurance > 0)
weaponId = (int)weapon.GetDBMajorType().id;
}
// Fake unlimited resources but keep real weapon / form / env values
// 伪造无限资源,但保留真实的武器/形态/环境值
UseRequirement fakeInfo = new UseRequirement
@@ -364,21 +470,21 @@ namespace BrewMonster
if (!animOk)
Debug.LogWarning($"[SkillTriggerPanel] PlaySkillCastAction returned false for skill {skillId} — animation may not play.");
// 2. Trigger VFX via the GFX composer — bypasses network entirely
// 通过特效组合器触发特效,完全绕过网络
var composerMan = CECAttacksMan.Instance?.GetSkillGfxComposerMan();
if (composerMan != null)
{
var targets = new List<TARGET_DATA>
{
new TARGET_DATA { idTarget = targetId, dwModifier = 0, nDamage = 0 }
};
composerMan.Play(skillId, player.GetCharacterID(), targetId, targets);
}
else
{
Debug.LogWarning($"[SkillTriggerPanel] composerMan is null — VFX skipped for skill {skillId}.");
}
// // 2. Trigger VFX via the GFX composer — bypasses network entirely
// // 通过特效组合器触发特效,完全绕过网络
// var composerMan = CECAttacksMan.Instance?.GetSkillGfxComposerMan();
// if (composerMan != null)
// {
// var targets = new List<TARGET_DATA>
// {
// new TARGET_DATA { idTarget = targetId, dwModifier = 0, nDamage = 0 }
// };
// composerMan.Play(skillId, player.GetCharacterID(), targetId, targets);
// }
// else
// {
// Debug.LogWarning($"[SkillTriggerPanel] composerMan is null — VFX skipped for skill {skillId}.");
// }
// 3. After 2s, play the release / 施放起+落 attack animation chain (local-only).
// 2 秒后播放施放攻击动画链(仅本地)。
@@ -397,20 +503,20 @@ namespace BrewMonster
// first try to find if there is already a skill attack event in attackman
CECAttackerEvents attackerEvents = CECAttacksMan.Instance.FindAttackByAttacker(hostPlayer.GetPlayerInfo().cid);
// if (attackerEvents)
// {
// pAttack = attackerEvents.Find(skillId, 0);
// if (pAttack != null)
// {
// // Ãæ¹¥»÷µÄ·ÇµÚÒ»´ÎÉ˺¦ÏûÏ¢
// pAttack.AddTarget(DEBUG_TARGET_ID, 1, 1);
// goto EXIT;
// }
// else
// {
// attackerEvents.Signal();
// }
// }
if (attackerEvents)
{
pAttack = attackerEvents.Find(skillId, 0);
if (pAttack != null)
{
// Ãæ¹¥»÷µÄ·ÇµÚÒ»´ÎÉ˺¦ÏûÏ¢
pAttack.AddTarget(DEBUG_TARGET_ID, 1, 1);
goto EXIT;
}
else
{
attackerEvents.Signal();
}
}
if (ElementSkill.IsGoblinSkill((uint)skillId) &&
ElementSkill.GetType((uint)skillId) == 2)
{