Fix some bug
This commit is contained in:
@@ -15,7 +15,7 @@ MonoBehaviour:
|
||||
m_DefaultGroup: 712e3991f28e549e7a56ee582a977810
|
||||
m_currentHash:
|
||||
serializedVersion: 2
|
||||
Hash: 584471775533227dbfd66c0814b46e32
|
||||
Hash: 00000000000000000000000000000000
|
||||
m_OptimizeCatalogSize: 0
|
||||
m_BuildRemoteCatalog: 0
|
||||
m_CatalogRequestsTimeout: 0
|
||||
|
||||
@@ -4869,7 +4869,7 @@ Transform:
|
||||
m_GameObject: {fileID: 4197944621747409826}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0.6878776, y: -0.16378161, z: 0.16378164, w: 0.6878776}
|
||||
m_LocalPosition: {x: 0.024, y: 0.554, z: -0.33}
|
||||
m_LocalPosition: {x: 0.26, y: 1.51, z: -0.52}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
@@ -5595,7 +5595,7 @@ ParticleSystem:
|
||||
sphericalDirectionAmount: 0
|
||||
randomPositionAmount: 0
|
||||
radius:
|
||||
value: 0.71
|
||||
value: 2.13
|
||||
mode: 0
|
||||
spread: 0
|
||||
speed:
|
||||
@@ -19445,8 +19445,8 @@ Transform:
|
||||
serializedVersion: 2
|
||||
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_LocalScale: {x: 3, y: 3, z: 3}
|
||||
m_ConstrainProportionsScale: 1
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1604809390761698253}
|
||||
m_LocalEulerAnglesHint: {x: -90, y: 0, z: 0}
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4274349585449340130}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
|
||||
--- !u!33 &7795735887883509743
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4135001540345507112}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0, y: -0.1, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
|
||||
--- !u!33 &5782369747155273987
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2368932797394218666}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalRotation: {x: 0.35355344, y: 0.6123724, z: 0.6123724, w: 0.35355344}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: -30, y: 90, z: 90}
|
||||
--- !u!33 &6354786712980373978
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 486393464438265562}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
|
||||
--- !u!33 &2795371212256588815
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5042710232424246754}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
|
||||
--- !u!33 &7463675474240568897
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 192856137541653222}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: 0.7071068, w: 0}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: -90, y: 180, z: 0}
|
||||
--- !u!33 &7385562017347778284
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5834653072341985716}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: 0.3535534, y: 0.61237246, z: -0.3535534, w: 0.61237246}
|
||||
m_LocalPosition: {x: 0, y: -0.2, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 60, y: 90, z: 0}
|
||||
--- !u!33 &5428329696048806453
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6619641526290080649}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: -0.7071068, w: 0}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 180, z: 0}
|
||||
--- !u!33 &7228491550620403586
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1020594218048739884}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0.73, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
|
||||
--- !u!33 &3048989088492932353
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using BrewMonster;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PerfectWorld.Scripts
|
||||
@@ -18,7 +20,7 @@ namespace PerfectWorld.Scripts
|
||||
|
||||
/// <summary>
|
||||
/// Test scene only: bootstraps <see cref="CECHostPlayer"/> model load. After each successful <see cref="CECPlayer.SetPlayerModel"/>,
|
||||
/// optionally applies serialized weapon prefabs via <see cref="CECPlayer.AnimSceneAttachWeaponPrefabs"/> (sets <c>m_uAttackType</c> like equip data).
|
||||
/// applies serialized weapon prefabs via <see cref="CECPlayer.AnimSceneAttachWeaponPrefabs"/> (sets <c>m_uAttackType</c> like equip data).
|
||||
/// After each load, destroys the previous major model instance so it does not linger in the hierarchy.
|
||||
/// Optional: discard the whole host instance and instantiate a fresh prefab when switching role (see <see cref="replaceHostOnRoleSwitch"/>).
|
||||
/// </summary>
|
||||
@@ -28,6 +30,14 @@ namespace PerfectWorld.Scripts
|
||||
[Tooltip("Scene host player. Do not put this bootstrap component on the same GameObject as the host if you enable Replace host on role switch.")]
|
||||
[SerializeField]
|
||||
private CECHostPlayer player;
|
||||
[SerializeField]
|
||||
private Camera cameraObject;
|
||||
[SerializeField]
|
||||
private List<Transform> cameraPoses;
|
||||
[SerializeField]
|
||||
[Range(0, 10)]
|
||||
private int activeCameraPos;
|
||||
|
||||
|
||||
[Header("Initial Role")]
|
||||
[SerializeField]
|
||||
@@ -43,12 +53,9 @@ namespace PerfectWorld.Scripts
|
||||
|
||||
[Tooltip("Which weaponByActionType row to apply after load / when using Re-apply in the custom inspector.")]
|
||||
[SerializeField]
|
||||
[Range(0, 14)]
|
||||
[Range(0, 10)]
|
||||
private int activeWeaponActionTypeIndex;
|
||||
|
||||
[SerializeField]
|
||||
private bool applyWeaponAfterModelLoad = true;
|
||||
|
||||
[Header("Animation scene — element data compat")]
|
||||
[Tooltip(
|
||||
"If true, sets m_iFashionWeaponType = -1 on the host so PlayAction / GetShowingWeaponType do not call IsFashionWeaponTypeFit with missing DT_FASHION_WEAPON_CONFIG (avoids null action_mask NRE). Disable only if you load that config row.")]
|
||||
@@ -62,6 +69,15 @@ namespace PerfectWorld.Scripts
|
||||
[SerializeField]
|
||||
private bool bindPlayerVisualAfterModelLoad = true;
|
||||
|
||||
[Header("Animation scene — skill catalog")]
|
||||
[Tooltip("Match CDlgSkillSubList god/evil path when building the debug skill grid.")]
|
||||
[SerializeField]
|
||||
private bool isEvilSkillPath;
|
||||
|
||||
[Tooltip("Default level for skills injected from CECHostSkillModel in anim scenes.")]
|
||||
[SerializeField]
|
||||
private int skillCatalogLevel = 1;
|
||||
|
||||
[Header("Test-only — cleanup & host swap")]
|
||||
[Tooltip("After SetPlayerModel, Destroy the previous major .ecm instance (frees scene objects left by re-load).")]
|
||||
[SerializeField]
|
||||
@@ -88,6 +104,26 @@ namespace PerfectWorld.Scripts
|
||||
|
||||
await BootstrapAsync((byte)profession, (byte)gender, fromInitialStart: true);
|
||||
}
|
||||
public void ChangeCameraPos(int i)
|
||||
{
|
||||
if(i<0)
|
||||
activeCameraPos = cameraPoses.Count-1;
|
||||
if (i>= cameraPoses.Count )
|
||||
activeCameraPos = 0;
|
||||
else
|
||||
activeCameraPos = i;
|
||||
cameraObject.transform.parent = cameraPoses[activeCameraPos];
|
||||
cameraObject.transform.localRotation = default;
|
||||
cameraObject.transform.localPosition = default;
|
||||
}
|
||||
public void ChangeNextCamera()
|
||||
{
|
||||
ChangeCameraPos (++activeCameraPos );
|
||||
}
|
||||
public void ChangePreviosCamera()
|
||||
{
|
||||
ChangeCameraPos (--activeCameraPos );
|
||||
}
|
||||
|
||||
/// <summary>Swap profession/gender at runtime (async load inside).</summary>
|
||||
public void SwitchRole(Profession newProfession, Gender newGender)
|
||||
@@ -190,6 +226,28 @@ namespace PerfectWorld.Scripts
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-apply weapon, rebuild skill catalog for current role, and refresh SkillTriggerPanel.
|
||||
/// 重新应用武器、重建技能目录并刷新技能面板。
|
||||
/// </summary>
|
||||
public void ResetSkillPanel(bool filterByRestrictions)
|
||||
{
|
||||
if (player == null)
|
||||
{
|
||||
Debug.LogWarning("[AnimSceneBootstrap] ResetSkillPanel: player is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyWeaponForActiveSlot();
|
||||
RefreshSkillTriggerPanel(filterByRestrictions);
|
||||
}
|
||||
|
||||
/// <summary>Reload skill grid showing only skills usable with current weapon/form/env.</summary>
|
||||
public void ResetSkillsWithRestrictions() => ResetSkillPanel(filterByRestrictions: true);
|
||||
|
||||
/// <summary>Reload skill grid with all catalog skills (no weapon/form/env filter).</summary>
|
||||
public void ResetSkillsWithoutRestrictions() => ResetSkillPanel(filterByRestrictions: false);
|
||||
|
||||
private void ReplaceHostPlayerInstance()
|
||||
{
|
||||
if (hostPlayerPrefab == null)
|
||||
@@ -229,6 +287,7 @@ namespace PerfectWorld.Scripts
|
||||
_busy = true;
|
||||
try
|
||||
{
|
||||
ChangeCameraPos(activeCameraPos);
|
||||
Debug.Log("[AnimSceneBootstrap] AnimScenePlayerBootstrap — waiting for ElementDataManProvider...");
|
||||
int waitedFrames = 0;
|
||||
while (!ElementDataManProvider.IsDataLoaded)
|
||||
@@ -277,15 +336,9 @@ namespace PerfectWorld.Scripts
|
||||
await player.SetPlayerModel(prof, gen);
|
||||
Debug.Log("[AnimSceneBootstrap] SetPlayerModel pipeline finished.");
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
EC_Game.GetGameRun()?.RegisterAnimSceneHostPlayer(player);
|
||||
}
|
||||
ApplyWeaponForActiveSlot();
|
||||
|
||||
if (applyWeaponAfterModelLoad && player != null)
|
||||
{
|
||||
ApplyWeaponForActiveSlot();
|
||||
}
|
||||
AnimSceneInitSkillModelAndRefreshPanel(prof, gen);
|
||||
|
||||
AnimSceneBindPlayerVisualIfEnabled();
|
||||
|
||||
@@ -309,5 +362,44 @@ namespace PerfectWorld.Scripts
|
||||
_busy = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sync profession/gender, rebuild CECHostSkillModel for the current role, and refresh SkillTriggerPanel.
|
||||
/// SetPlayerModel loads visuals only — m_iProfession is not updated otherwise, and skill catalog init
|
||||
/// is skipped in anim scenes (no LoadPlayerSkeleton → OnAllResourceReady).
|
||||
///
|
||||
/// 同步职业/性别,重建 CECHostSkillModel,并刷新 SkillTriggerPanel。
|
||||
/// </summary>
|
||||
private void AnimSceneInitSkillModelAndRefreshPanel(byte prof, byte gen)
|
||||
{
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
player.m_iProfession = prof;
|
||||
player.m_iGender = gen;
|
||||
|
||||
EC_Game.GetGameRun()?.RegisterAnimSceneHostPlayer(player);
|
||||
|
||||
SkillStubs.Init();
|
||||
CECHostSkillModel.Instance.Initialize(prof);
|
||||
Debug.Log($"[AnimSceneBootstrap] CECHostSkillModel initialized for profession={prof}, " +
|
||||
$"catalogSkills={CECHostSkillModel.Instance.CollectSkillSubListSkillIds(isEvilSkillPath).Count}.");
|
||||
|
||||
RefreshSkillTriggerPanel(filterByRestrictions: false);
|
||||
}
|
||||
|
||||
private void RefreshSkillTriggerPanel(bool filterByRestrictions)
|
||||
{
|
||||
if (SkillTriggerPanel.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SkillTriggerPanel.Instance.SetHostPlayer(player);
|
||||
SkillTriggerPanel.Instance.SetEvilSkillPath(isEvilSkillPath);
|
||||
SkillTriggerPanel.Instance.ResetSkillsFromSkillModel(filterByRestrictions, skillCatalogLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -110,6 +110,27 @@ namespace PerfectWorld.Scripts
|
||||
boot.ApplyWeaponForActiveSlot();
|
||||
}
|
||||
}
|
||||
|
||||
// EditorGUILayout.Space(8f);
|
||||
// EditorGUILayout.LabelField("Play Mode — skill grid", EditorStyles.boldLabel);
|
||||
// EditorGUILayout.HelpBox(
|
||||
// "Reset Skills (With Restrictions) re-applies the active weapon slot, rebuilds the skill catalog, " +
|
||||
// "and shows only skills that pass weapon / form / move-env checks.",
|
||||
// MessageType.Info);
|
||||
// using (new EditorGUI.DisabledScope(!Application.isPlaying))
|
||||
// {
|
||||
// EditorGUILayout.BeginHorizontal();
|
||||
// if (GUILayout.Button("Reset Skills", GUILayout.Height(28f)))
|
||||
// {
|
||||
// ((AnimScenePlayerBootstrap)target).ResetSkillsWithoutRestrictions();
|
||||
// }
|
||||
|
||||
// if (GUILayout.Button("Reset Skills (With Restrictions)", GUILayout.Height(28f)))
|
||||
// {
|
||||
// ((AnimScenePlayerBootstrap)target).ResetSkillsWithRestrictions();
|
||||
// }
|
||||
// EditorGUILayout.EndHorizontal();
|
||||
// }
|
||||
}
|
||||
|
||||
private static void TryPlayActionOnHost(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Animancer;
|
||||
using BrewMonster;
|
||||
using CSNetwork;
|
||||
using CSNetwork.GPDataType;
|
||||
using ModelRenderer.Scripts.GameData;
|
||||
@@ -50,6 +51,51 @@ namespace BrewMonster.Scripts.Skills
|
||||
{
|
||||
return m_allRankProfSkills;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same skill ID set as <see cref="BrewMonster.UI.CDlgSkillSubList"/> — walks base/god/evil ranks,
|
||||
/// applies god/evil path filter and skips overridden skills.
|
||||
/// 与 CDlgSkillSubList 相同的技能 ID 集合。
|
||||
/// </summary>
|
||||
public List<int> CollectSkillSubListSkillIds(bool isEvil)
|
||||
{
|
||||
var result = new List<int>();
|
||||
var seen = new HashSet<int>();
|
||||
|
||||
void CollectRankRange(CECTaoistRank begin, CECTaoistRank end)
|
||||
{
|
||||
for (CECTaoistRank taoistRank = begin; taoistRank != end; taoistRank = taoistRank.GetNext())
|
||||
{
|
||||
if (isEvil && taoistRank.IsGodRank())
|
||||
continue;
|
||||
if (!isEvil && taoistRank.IsEvilRank())
|
||||
continue;
|
||||
|
||||
int rankId = taoistRank.GetID();
|
||||
if (!m_allRankProfSkills.TryGetValue(rankId, out List<int> rankSkills) ||
|
||||
rankSkills == null || rankSkills.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (int skillId in rankSkills)
|
||||
{
|
||||
if (ElementSkill.IsOverridden((uint)skillId))
|
||||
continue;
|
||||
if (seen.Add(skillId))
|
||||
result.Add(skillId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CollectRankRange(CECTaoistRank.GetBaseRankBegin(), CECTaoistRank.GetBaseRankEnd());
|
||||
CollectRankRange(CECTaoistRank.GetGodRankBegin(), CECTaoistRank.GetGodRankEnd());
|
||||
CollectRankRange(CECTaoistRank.GetEvilRankBegin(), CECTaoistRank.GetEvilRankEnd());
|
||||
|
||||
result.Sort();
|
||||
return result;
|
||||
}
|
||||
|
||||
public CECHostSkillModel()
|
||||
{
|
||||
m_skillLearnNPCNID = 0;
|
||||
@@ -127,12 +173,18 @@ namespace BrewMonster.Scripts.Skills
|
||||
}
|
||||
}
|
||||
public void Initialize()
|
||||
{
|
||||
Initialize(null);
|
||||
}
|
||||
|
||||
/// <param name="professionOverride">When set (anim test scene), filters catalog by this profession instead of querying host.</param>
|
||||
public void Initialize(int? professionOverride)
|
||||
{
|
||||
//BMLogger.LogError("HoangDev CECHostSkillModel Initialize called");
|
||||
// Çå¿ÕËùÓм¼ÄÜ£¬·ÀÖ¹ÒòΪ¶à¸ö½ÇÉ«µÇ¼µ¼ÖÂÖØ¸´¼ÓÔØ¼¼ÄÜ
|
||||
Release();
|
||||
|
||||
InitAllSkillsOfCurProf();
|
||||
InitAllSkillsOfCurProf(professionOverride);
|
||||
FindAllNPCsOfCurProf();
|
||||
HashSet<int> rootSkills = GetRootSkillSet();
|
||||
InitSkillTreeHeightMap(rootSkills);
|
||||
@@ -489,6 +541,11 @@ namespace BrewMonster.Scripts.Skills
|
||||
}
|
||||
}
|
||||
public void InitAllSkillsOfCurProf()
|
||||
{
|
||||
InitAllSkillsOfCurProf(null);
|
||||
}
|
||||
|
||||
public void InitAllSkillsOfCurProf(int? professionOverride)
|
||||
{
|
||||
// --- B1: Thu thập toàn bộ skill từ các NPC có cung cấp dịch vụ học skill ---
|
||||
HashSet<uint> npcSkills = new HashSet<uint>();
|
||||
@@ -526,7 +583,8 @@ namespace BrewMonster.Scripts.Skills
|
||||
{
|
||||
ElementSkill pSkill = ElementSkill.Create(curID, 1);
|
||||
int cls = pSkill.GetCls();
|
||||
int playerCls = CECGameRun.Instance.GetHostPlayer().GetProfession();
|
||||
int playerCls = professionOverride ??
|
||||
CECGameRun.Instance.GetHostPlayer().GetProfession();
|
||||
|
||||
bool isSameClass = (cls == playerCls || cls == 255);
|
||||
bool isProvidedByNPC = npcSkills.Contains(curID);
|
||||
|
||||
@@ -286,6 +286,68 @@ namespace BrewMonster
|
||||
$"({m_aPtSkills.Count} active, {m_aPsSkills.Count} passive) at level {level}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Animation test / offline: populate skill lists from <see cref="CECHostSkillModel"/> catalog
|
||||
/// (same source and filters as CDlgSkillSubList).
|
||||
/// Call <see cref="CECHostSkillModel.Initialize"/> before this so the catalog matches the current profession.
|
||||
///
|
||||
/// 动画测试 / 离线:从 CECHostSkillModel 目录填充技能列表(与 CDlgSkillSubList 相同来源与过滤)。
|
||||
/// </summary>
|
||||
public void InjectSkillsFromSkillModel(int level = 1, bool isEvil = false)
|
||||
{
|
||||
m_aPtSkills.Clear();
|
||||
m_aPsSkills.Clear();
|
||||
|
||||
List<int> skillIds = CECHostSkillModel.Instance?.CollectSkillSubListSkillIds(isEvil);
|
||||
if (skillIds == null || skillIds.Count == 0)
|
||||
{
|
||||
BMLogger.LogWarning(
|
||||
"InjectSkillsFromSkillModel: CECHostSkillModel catalog is empty — call CECHostSkillModel.Initialize() first.");
|
||||
return;
|
||||
}
|
||||
|
||||
int injected = 0;
|
||||
foreach (int skillId in skillIds)
|
||||
{
|
||||
CECSkill skill = new CECSkill(skillId, level);
|
||||
|
||||
int type = (int)ElementSkill.GetType((uint)skillId);
|
||||
if (skill.SkillCore != null)
|
||||
type = skill.GetType();
|
||||
|
||||
if (type != (int)CECSkill.SkillType.TYPE_PASSIVE &&
|
||||
type != (int)CECSkill.SkillType.TYPE_PRODUCE &&
|
||||
type != (int)CECSkill.SkillType.TYPE_LIVE)
|
||||
m_aPtSkills.Add(skill);
|
||||
else
|
||||
m_aPsSkills.Add(skill);
|
||||
|
||||
injected++;
|
||||
}
|
||||
|
||||
BMLogger.Log($"InjectSkillsFromSkillModel: profession={m_iProfession}, isEvil={isEvil}, " +
|
||||
$"catalog={skillIds.Count}, injected={injected} " +
|
||||
$"({m_aPtSkills.Count} active, {m_aPsSkills.Count} passive) at level {level}.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build <see cref="CECSkill"/> instances for every ID returned by
|
||||
/// <see cref="CECHostSkillModel.CollectSkillSubListSkillIds"/> — includes passive skills shown in the skill tree UI.
|
||||
/// 构建与 CDlgSkillSubList 完全一致的技能列表(含被动)。
|
||||
/// </summary>
|
||||
public List<CECSkill> BuildSkillSubListSkills(int level = 1, bool isEvil = false)
|
||||
{
|
||||
var skills = new List<CECSkill>();
|
||||
List<int> skillIds = CECHostSkillModel.Instance?.CollectSkillSubListSkillIds(isEvil);
|
||||
if (skillIds == null)
|
||||
return skills;
|
||||
|
||||
foreach (int skillId in skillIds)
|
||||
skills.Add(new CECSkill(skillId, level));
|
||||
|
||||
return skills;
|
||||
}
|
||||
|
||||
private void OnMsgHstLearnSkill(ECMSG Msg)
|
||||
{
|
||||
cmd_learn_skill pCmd = GPDataTypeHelper.FromBytes<cmd_learn_skill>((byte[])Msg.dwParam1);
|
||||
|
||||
Reference in New Issue
Block a user