Merge remote-tracking branch 'origin/develop' into feature/chat

This commit is contained in:
CuongNV
2026-04-25 11:38:27 +07:00
54 changed files with 145406 additions and 590 deletions
@@ -9330,6 +9330,11 @@ MonoBehaviour:
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: b052c37c53745f84ab3c7bb0e3c410b0
m_Address: Assets/ModelRenderer/Art/sfx/music/music/theme_c.mp3
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: b069390864138b445b51557b94886527
m_Address: "skill/02mage/\u5947\u95E8\u62A4\u7532_a"
m_ReadOnly: 0
@@ -12625,6 +12630,11 @@ MonoBehaviour:
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: e6da5caa83084c446b019f91ef4c3349
m_Address: Assets/ModelRenderer/Art/sfx/music/music/theme_a.mp3
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: e6df1ea9e172c414da99d57d3f22b935
m_Address: "monster/\u6218\u6597\u884C\u52A8/\u82B1\u8349\u7C7B\u8FD0\u52A82"
m_ReadOnly: 0
@@ -12905,6 +12915,11 @@ MonoBehaviour:
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: eb201c0efb391fa4797948562ef08604
m_Address: Assets/ModelRenderer/Art/sfx/music/music/theme_b.mp3
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: eb223a6970aaa9c45903924622374999
m_Address: "creature/monster/\u795D\u878D/\u6B7B\u4EA1_\u6C34\u4E2D"
m_ReadOnly: 0
@@ -1410,6 +1410,7 @@ GameObject:
- component: {fileID: 8354143382850346745}
- component: {fileID: 4448389894352443543}
- component: {fileID: 2791476417509046288}
- component: {fileID: 5118003806955342754}
m_Layer: 0
m_Name: "\u5996\u7CBE"
m_TagString: Untagged
@@ -2426,6 +2427,19 @@ MonoBehaviour:
- {fileID: 7400000, guid: 4272f3e6213068440b4f6bcbade34750, type: 2}
- {fileID: 7400000, guid: 31417b3fc94fd0f479454dc3fcfb5607, type: 2}
- {fileID: 7400000, guid: 62d8692c72987e049b94423ec51d734a, type: 2}
--- !u!114 &5118003806955342754
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5544128592671745444}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 472c24b873524423f95454131fd11da6, type: 3}
m_Name:
m_EditorClassIdentifier:
combinedActionSO: {fileID: 11400000, guid: ae900b2279845c944a6379064aff6f29, type: 2}
--- !u!1 &5909446679138261478
GameObject:
m_ObjectHideFlags: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ae900b2279845c944a6379064aff6f29
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -907,6 +907,7 @@ GameObject:
- component: {fileID: 5951415567811727370}
- component: {fileID: 2933135962091378275}
- component: {fileID: 720054061806230412}
- component: {fileID: 3892901130224629286}
m_Layer: 0
m_Name: "\u7FBD\u7075\u5973"
m_TagString: Untagged
@@ -2108,6 +2109,19 @@ MonoBehaviour:
- {fileID: 7400000, guid: 7dc5fb07f4f5562499bc69b645d4ed9d, type: 2}
- {fileID: 7400000, guid: 7a81940d288270d469be4f8b5230d733, type: 2}
- {fileID: 7400000, guid: 733d485f56790364189789dddbfffcfb, type: 2}
--- !u!114 &3892901130224629286
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1769235238873837677}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 472c24b873524423f95454131fd11da6, type: 3}
m_Name:
m_EditorClassIdentifier:
combinedActionSO: {fileID: 11400000, guid: 5c996ee4f0bbbba458a4557f2d3e2ae3, type: 2}
--- !u!1 &1815801885025748105
GameObject:
m_ObjectHideFlags: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c996ee4f0bbbba458a4557f2d3e2ae3
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
@@ -755,6 +755,7 @@ GameObject:
- component: {fileID: 8453557373466821518}
- component: {fileID: 2236231879345885960}
- component: {fileID: 1351688607369250648}
- component: {fileID: 6701388848577721347}
m_Layer: 0
m_Name: "\u7FBD\u8292\u7537"
m_TagString: Untagged
@@ -1873,6 +1874,19 @@ MonoBehaviour:
- {fileID: 7400000, guid: 7a1b518cc0ca31d409bddbea883fa85c, type: 2}
- {fileID: 7400000, guid: be95dd6728dc0ec4fa115f3f7f50ba60, type: 2}
- {fileID: 7400000, guid: 6f09eb60a12c79346989125f988affb5, type: 2}
--- !u!114 &6701388848577721347
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4570049467700484810}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 472c24b873524423f95454131fd11da6, type: 3}
m_Name:
m_EditorClassIdentifier:
combinedActionSO: {fileID: 11400000, guid: dae367eca7e062443b66ab96aca2c742, type: 2}
--- !u!1 &4654364276745040637
GameObject:
m_ObjectHideFlags: 0
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dae367eca7e062443b66ab96aca2c742
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: e6da5caa83084c446b019f91ef4c3349
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: eb201c0efb391fa4797948562ef08604
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: b052c37c53745f84ab3c7bb0e3c410b0
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:631a23da0c12055ef55e35b21fac40b752599bdbb43d0d2c1c7fd90674d4b791
size 118853
oid sha256:ce4ce7c6a9576c56a5fb0b213f19e1253afc78d679474cb203612a1e50e8093d
size 111451
@@ -290,7 +290,6 @@ namespace BrewMonster.Scripts
currentReleaseTime = Mathf.Min(currentReleaseTime, timeStamp);
}
_releaseAssetTimestamps[assetPath] = currentReleaseTime;
BMLogger.Log($"AddressableManager: Released asset: {assetPath}");
}
public void ForceReleaseAsset(string assetPath)
@@ -302,7 +301,6 @@ namespace BrewMonster.Scripts
Addressables.Release(loadedPrefabHandle);
}
_loadedPrefabAssets.Remove(assetPath);
BMLogger.Log($"AddressableManager: Force released prefab asset: {assetPath}");
}
else if (_loadedTextAssets.TryGetValue(assetPath, out var loadedTextHandle))
{
@@ -311,7 +309,6 @@ namespace BrewMonster.Scripts
Addressables.Release(loadedTextHandle);
}
_loadedTextAssets.Remove(assetPath);
BMLogger.Log($"AddressableManager: Force released text asset: {assetPath}");
}
else if (_loadedAudioAssets.TryGetValue(assetPath, out var loadedAudioHandle))
{
@@ -567,11 +567,9 @@ namespace BrewMonster
public string hitGrdGfxName;
public async UniTask<bool> Load(SkillStub skillStub, string flyGFXPath, string hitGrdGFXPath, string hitGFXPath)
{
flyGfxName = flyGFXPath;
hitGfxName = hitGFXPath;
hitGrdGfxName = hitGrdGFXPath;
// Load GFX prefabs / 加载GFX预制体
m_szFlyGfx = string.IsNullOrEmpty(flyGfxName) ? null : await AddressableManager.Instance.LoadPrefabAsync("gfx/" + flyGfxName);
m_szHitGfx = string.IsNullOrEmpty(hitGfxName) ? null : await AddressableManager.Instance.LoadPrefabAsync("gfx/" + hitGfxName);
@@ -873,7 +871,7 @@ public class CECAttackEvent
public int m_nSkillSection;
#if UNITY_EDITOR
int debugCounter = 0; // Debug counter to track Tick calls
int debugCounter = 0; // Debug counter to track Tick calls
#endif
public CECAttackEvent() { }
@@ -1075,7 +1073,7 @@ public class CECAttackEvent
if (bHideHitGfx) pszHitGFX = null;
// TODO: Implement AddSkillGfxEvent
// CECGameRun.Instance.GetWorld().GetSkillGfxMan().AddSkillGfxEvent(m_idHost, data.idTarget,
// CECGameRun.Instance.GetWorld().GetSkillGfxMan().AddSkillGfxEvent(m_idHost, data.idTarget,
// pszflyGFX, pszHitGFX, m_timeToDoDamage, false, GfxMoveMode.enumLinearMove, 1, 0, null, vFlyScale, vHitScale, data.dwModifier);
var target = EC_ManMessageMono.Instance?.GetObject(data.idTarget, 0)?.gameObject.transform;
if (target == null)
@@ -1113,7 +1111,7 @@ public class CECAttackEvent
// if (bHideHitGfx) szGFX = null;
// TODO: Implement AddSkillGfxEvent
// CECGameRun.Instance.GetWorld().GetSkillGfxMan().AddSkillGfxEvent(m_idHost, data.idTarget, null,
// CECGameRun.Instance.GetWorld().GetSkillGfxMan().AddSkillGfxEvent(m_idHost, data.idTarget, null,
// szGFX, m_timeToDoDamage, false, GfxMoveMode.enumLinearMove, 1, 0, null, vFlyScale, vHitScale, data.dwModifier);
// Temporary implementation for testing / 临时实现用于测试
@@ -1227,7 +1225,7 @@ public class CECAttackEvent
if (bHideHitGfx) pszHitGFX = null;
// TODO: Implement AddSkillGfxEvent
// CECGameRun.Instance.GetWorld().GetSkillGfxMan().AddSkillGfxEvent(m_idHost, data.idTarget,
// CECGameRun.Instance.GetWorld().GetSkillGfxMan().AddSkillGfxEvent(m_idHost, data.idTarget,
// pszflyGFX, pszHitGFX, m_timeToDoDamage, false, GfxMoveMode.enumLinearMove, 1,
// 0, null, vFlyScale, vHitScale, data.dwModifier);
}
@@ -44,7 +44,7 @@ namespace BrewMonster.Scripts
GetPlayer().SetPos(GetPlayer().GetPos() + vDir * fMoveDist);
}
if (GetPlayer().m_FightCnt.IncCounter(dwDeltaTime))
if (GetPlayer().m_FightCnt.IncCounter(dwDeltaTime * 1000))
{
Finish();
}
@@ -159,7 +159,6 @@ namespace BrewMonster.Scripts
return m_iType;
}
}
public class CECEPIdleWorkMatcher : CECEPWorkMatcher
{
int m_iType;
@@ -355,7 +354,6 @@ namespace BrewMonster.Scripts
new List<CECEPWork>()
};
private int m_iCurWorkType;
public CECEPWorkMan(EC_ElsePlayer pElsePlayer)
{
m_pElsePlayer = pElsePlayer;
@@ -798,12 +796,11 @@ namespace BrewMonster.Scripts
if (!GetPlayer().m_FightCnt.IsFull()){
GetPlayer().m_FightCnt.IncCounter(dwDeltaTime);
}
//todo: get model and check
// if (GetPlayer().GetPlayerModel() != null){
if (GetPlayer().GetPlayerModel() != null){
if (CECPlayer.IsMoveStandAction(GetPlayer().GetLowerBodyAction())){
GetPlayer().PlayAction(GetStandAction(), false);
}
// }
}
}
int GetStandAction()
@@ -38,7 +38,7 @@ public class CECCounter
public bool IncCounter(float dwCounter)
{
m_dwCounter += dwCounter;
return (m_dwCounter >= m_dwPeriod);
return (m_dwCounter >= m_dwPeriod) ? true : false;
}
// Decrease counter
@@ -123,7 +123,7 @@ namespace BrewMonster
public void SendStopMoveCmd(in Vector3 vPos, float fSpeed, int iMoveMode)
{
UnityEngine.Debug.LogWarning("HoangDev : SendStopMoveCmd");
// UnityEngine.Debug.LogWarning("HoangDev : SendStopMoveCmd");
iMoveMode |= (int)GPMoveMode.GP_MOVE_DEAD;
if (cmdstopdelayCounter >= 500)
@@ -330,7 +330,7 @@ namespace BrewMonster
m_fBlockMove += (vNewPos - m_pHost.GetPos()).Magnitude();
m_vBlockMove += vNewPos - m_pHost.GetPos();
m_fBlockTime += fTime;
//TO DO: in c++ set 1.0f. Convert to c#, this logic is wrong. If set time is 2.0, game run correct
//TO DO: in c++ set 1.0f. Convert to c#, this logic is wrong. If set time is 2.0, game run correct
if ((m_fBlockTime) >= 2.0f)
{
if (m_fBlockMove < GPDataTypeHelper.MIN_MOVELEN_ON_GROUND || m_vBlockMove.Magnitude() < GPDataTypeHelper.MIN_MOVELEN_FOR_DETECT_VIBRATION)
@@ -528,4 +528,4 @@ namespace BrewMonster
//the moving dist
public float fMoveDist;
};
}
}
+77 -18
View File
@@ -271,6 +271,54 @@ namespace BrewMonster
"胧族变身月仙男",
"胧族变身月仙女",
};
public Dictionary<int,List<string>> m_aWeaponSFX = new Dictionary<int,List<string>>()
{
{0, new List<string>{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}},
{1, new List<string>{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}},
{2, new List<string>{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}},
{3, new List<string>{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}},
{4, new List<string>{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}},
{5, new List<string>{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb"}},
{6, new List<string>{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}},
{7, new List<string>{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}},
{8, new List<string>{"item/weaponattack/fista", "item/weaponattack/fistb", "item/weaponattack/fistc", "item/weaponattack/fistd"}},
{9, new List<string>{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}},
{10, new List<string>{"item/weaponattack/fista", "item/weaponattack/fistb", "item/weaponattack/fistc", "item/weaponattack/fistd"}},
// {0, new List<string>{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}},
// {1, new List<string>{"item/weaponattack/2hheavya", "item/weaponattack/2hheavyb", "item/weaponattack/2hheavyc", "item/weaponattack/2hheavyd"}},
// {2, new List<string>{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}},
// {3, new List<string>{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}},
// {4, new List<string>{"item/weaponattack/fista", "item/weaponattack/fistb", "item/weaponattack/fistc", "item/weaponattack/fistd"}},
// {5, new List<string>{"item/weaponattack/magic", "item/weaponattack/magicb"}},
};
public Dictionary<int,List<string>> m_aWeaponHitSFX = new Dictionary<int,List<string>>()
{
{0, new List<string>{"item/weaponattack/hitsword", "item/weaponattack/hitswordbig"}},
{1, new List<string>{"item/weaponattack/hitsword", "item/weaponattack/hitswordbig"}},
{2, new List<string>{"item/weaponattack/hitmace", "item/weaponattack/hitmacebig"}},
{3, new List<string>{"item/weaponattack/hithammer", "item/weaponattack/hithammerbig"}},
{4, new List<string>{"item/weaponattack/hitaxe", "item/weaponattack/hitaxebig"}},
{5, new List<string>{"item/weaponattack/hithammer"}},
{6, new List<string>{"item/weaponattack/hitthrow"}},
{7, new List<string>{"item/weaponattack/hitthrow"}},
{8, new List<string>{"item/weaponattack/hithand"}},
{9, new List<string>{"item/weaponattack/hitthrow"}},
{10, new List<string>{"item/weaponattack/hithand"}},
// {0, new List<string>{"item/weaponattack/hitaxe", "item/weaponattack/hitaxebig"}},
// {1, new List<string>{"item/weaponattack/hithammer", "item/weaponattack/hithammerbig"}},
// {2, new List<string>{"item/weaponattack/hitblade", "item/weaponattack/hitbladebig"}},
// {3, new List<string>{"item/weaponattack/hitdagger"}},
// {4, new List<string>{"item/weaponattack/hitfist"}},
// {5, new List<string>{"item/weaponattack/hithand"}},
// {6, new List<string>{"item/weaponattack/hitstaff"}},
// {7, new List<string>{"item/weaponattack/hitmace", "item/weaponattack/hitmacebig"}},
// {8, new List<string>{"item/weaponattack/hitoriginal"}},
// {9, new List<string>{"item/weaponattack/hitsword", "item/weaponattack/hitswordbig"}},
// {10, new List<string>{"item/weaponattack/hittiger"}},
// {11, new List<string>{"item/weaponattack/hitwhip"}},
// {12, new List<string>{"item/weaponattack/hitthrow"}},
// {13, new List<string>{"item/weaponattack/hitbow", "item/weaponattack/hitbowbig"}},
};
public static class Effect_type
{
public const int EFF_FACEPILL = 1;
@@ -294,7 +342,7 @@ namespace BrewMonster
protected GameObject GetDummyModel(int i) => (i!=(int)PLAYERMODEL_TYPE.PLAYERMODEL_MAJOR&&i<(int)PLAYERMODEL_TYPE.PLAYERMODEL_MAX) ? m_pModels[i]:null;
protected GameObject GetMajorModel() => m_pModels[(int)PLAYERMODEL_TYPE.PLAYERMODEL_MAJOR];
protected GameObject GetPetModel() => m_pPetModel;
protected CECModel GetPlayerModel() => m_pPlayerCECModel;
public CECModel GetPlayerModel() => m_pPlayerCECModel;
protected const int OBJECT_EXT_STATE_COUNT = 6;
protected bool IsMajorModel(GameObject pModel) => GetMajorModel() != null && pModel == GetMajorModel();
public enum WeaponHangerPosition
@@ -1176,11 +1224,11 @@ namespace BrewMonster
}
}
public bool PlayAction(int iAction, bool bRestart = true, int iTransTime = 200, bool bQueue = false)
public bool PlayAction(int iAction, bool bRestart = true, int iTransTime = 200, bool bQueue = false, bool isElsePlayer = false)
{
return PlayActionWithConfig(iAction, 0, bRestart, iTransTime, bQueue);
}
public bool PlayAction(int iAction, int actionConfigID, bool bRestart = true, int iTransTime = 200, bool bQueue = false)
public bool PlayAction(int iAction, int actionConfigID, bool bRestart = true, int iTransTime = 200, bool bQueue = false, bool isElsePlayer = false)
{
return PlayActionWithConfig(iAction, actionConfigID, bRestart, iTransTime, bQueue);
}
@@ -1631,12 +1679,13 @@ namespace BrewMonster
int nRand = UnityEngine.Random.Range(0, 4);
string szAct = string.Empty;
string szShapeName = string.Empty;
GetShapeName(ref szShapeName);
int weapon_type = GetShowingWeaponType();
Debug.Log($"[THN]: PlayAttackAction weapon_type: {weapon_type}");
Debug.Log("PlayAttackAction: weapon_type=" + weapon_type);
int nTime1 = 0, nTime2 = 0;
int iAction = (int)PLAYER_ACTION_TYPE.ACT_ATTACK_1 + nRand;
string soundPath = m_aWeaponSFX[weapon_type][nRand%m_aWeaponSFX[weapon_type].Count];
string hitSoundPath = m_aWeaponHitSFX[weapon_type][nRand%m_aWeaponHitSFX[weapon_type].Count];
bool bHideFX = false;//!CECOptimize::Instance().GetGFX().CanShowAttack(GetCharacterID(), GetClassID());
PLAYER_ACTION action = m_PlayerActions[iAction];
@@ -1653,15 +1702,21 @@ namespace BrewMonster
{
// “起? 动作(挥起)
Debug.Log($"[THN]: PlayAttackAction action with weapon type: {weapon_type} and weapon attached: {m_bWeaponAttached}");
szAct = EC_Utility.BuildActionName(action, weapon_type, "起");
int iTransTime = 200;
//EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, iTransTime, true));
m_pActionController.PlayNonSkillActionWithName(iAction, szAct, true, iTransTime, bHideFX, attackEvent,COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX);
//swing sfx
//workaround for sound effect delay, it need to trigger via weapon combine action
SFXManager.Instance.PlaySkillSfxAtPointAsync(soundPath, Vector3.zero,iTransTime/1000f).Forget();
szAct = EC_Utility.BuildActionName(action, weapon_type, "落");
queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, true, attackEvent, iTransTime,false);
//EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
m_pActionController.QueueNonSkillActionWithName(iAction, szAct, 0, false, bHideFX);
//hit sfx
//workaround for sound effect delay, it need to trigger via weapon combine action
//.1f is a magic number to make sure the sound effect is triggered after the action is finished
SFXManager.Instance.PlaySkillSfxAtPointAsync(hitSoundPath, Vector3.zero,iTransTime/1000f+.1f).Forget();
//PlayNonSkillActionWithName(iAction, szAct, true, 200, true, ref pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX);gagága
/*
if (pRightHandWeapon != null && IsUsingMagicWeapon())
@@ -1707,7 +1762,6 @@ namespace BrewMonster
// nTime1 = m_pPlayerModel.GetComActTimeSpanByName(szAct);
szAct = EC_Utility.BuildActionName(action, weapon_type, "落", szActionMiddleName);
queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, false, attackEvent, 0, false);
//EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
m_pActionController.QueueNonSkillActionWithName(iAction, szAct, 0, false, false, true, false);
@@ -1723,8 +1777,6 @@ namespace BrewMonster
PLAYER_ACTION stand_action = m_PlayerActions[(int)PLAYER_ACTION_TYPE.ACT_FIGHTSTAND];
szAct = EC_Utility.BuildActionName(stand_action, 0);
int iTranstime = 300;
queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, false, attackEvent, iTranstime, false);
//EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent);
m_pActionController.QueueNonSkillActionWithName(iAction, szAct, iTranstime, false, false, true, false);
/* QueueNonSkillActionWithName(ACT_FIGHTSTAND, szAct, 300, false, bHideFX, true);
@@ -3883,20 +3935,23 @@ namespace BrewMonster
public string AnimationName;
public int ITransTime;
public bool IsForceStopPrevious;
public bool IsLoop;
public CECAttackEvent AttackEvent;
public PlayActionEvent(string shapeName, string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null)
public PlayActionEvent(string shapeName, string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null, bool isLoop = false)
{
this.AnimationName = shapeName + animationName;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
AttackEvent = attackEvent;
IsLoop = isLoop;
}
public PlayActionEvent(string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null)
public PlayActionEvent(string animationName, int iTransTime, bool isForceStopPrevious = false, CECAttackEvent attackEvent = null, bool isLoop = false)
{
this.AnimationName = animationName;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
AttackEvent = attackEvent;
IsLoop = isLoop;
}
}
public struct PLAYER_ACTION
@@ -3912,8 +3967,9 @@ namespace BrewMonster
public CECAttackEvent AttackEvent;
public bool IsHitAnim;
public bool IsForceStopPrevious;
public bool IsLoop;
public QueueActionEvent(string animationName, Action<bool, CECAttackEvent> setFlag, bool isHitAnim,
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false)
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false, bool isLoop = false)
{
this.AnimationName = animationName;
SetFlag = setFlag;
@@ -3921,10 +3977,11 @@ namespace BrewMonster
AttackEvent = attackEvent;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
IsLoop = isLoop;
}
public void SetData(string shapeName, string animationName, Action<bool, CECAttackEvent> setFlag, bool isHitAnim,
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false)
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false, bool isLoop = false)
{
this.AnimationName = shapeName + animationName;
SetFlag = setFlag;
@@ -3932,9 +3989,10 @@ namespace BrewMonster
AttackEvent = attackEvent;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
IsLoop = isLoop;
}
public void SetData(string animationName, Action<bool, CECAttackEvent> setFlag, bool isHitAnim,
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false)
CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false, bool isLoop = false)
{
this.AnimationName = animationName;
SetFlag = setFlag;
@@ -3942,6 +4000,7 @@ namespace BrewMonster
AttackEvent = attackEvent;
ITransTime = iTransTime;
IsForceStopPrevious = isForceStopPrevious;
IsLoop = isLoop;
}
}
public enum PLAYER_ACTION_TYPE
+25 -22
View File
@@ -369,7 +369,7 @@ public class CLuaMemTbl
public class CECModel
{
private bool m_bInheritParentId = true;
private int m_nId = 0;
public GameObject m_pPlayerModel;
@@ -397,7 +397,7 @@ public class CECModel
pNode->m_pActFlag = NULL;
}*/
}
public bool HasCHAABB() { return m_pMapModel.HasCHAABB(); }
/// <summary>
@@ -438,14 +438,14 @@ public class CECModel
{
m_skeletonBuilder = m_transform.GetComponentInChildren<SkeletonBuilder>(true);
}
if (m_skeletonBuilder == null && m_transform != null)
{
// Fallback: search in parent hierarchy (for NPCs/Players)
// 回退:在父层次结构中搜索(用于NPC/玩家)
m_skeletonBuilder = m_transform.GetComponentInParent<SkeletonBuilder>();
}
if (m_skeletonBuilder == null)
{
BMLogger.LogWarning($"[CECModel] SkeletonBuilder not found for {(m_transform != null ? m_transform.name : "null")}. Hooks will not be available.");
@@ -497,12 +497,12 @@ public class CECModel
return null; // Still no skeleton found
}
}
if (string.IsNullOrEmpty(hookName))
{
return null;
}
// Check cache first
// 首先检查缓存
if (m_hookCache.TryGetValue(hookName, out Transform cachedHook))
@@ -513,11 +513,11 @@ public class CECModel
}
m_hookCache.Remove(hookName); // Remove invalid entry
}
// Lookup from skeleton
// 从骨架查找
Transform hook = m_skeletonBuilder.GetHook(hookName, recursive);
if (hook != null)
{
m_hookCache[hookName] = hook; // Cache for performance
@@ -575,7 +575,7 @@ public class CECModel
{
if (string.IsNullOrEmpty(hangerName))
return null;
return m_childModels.TryGetValue(hangerName, out CECModel child) ? child : null;
}
@@ -671,27 +671,30 @@ public class CECModel
}
var actionInfos = combinedAction.m_ActLst;
var isLoop = combinedAction.m_nLoops == 1;
EventBus.PublishChannel(m_nId, new PlayActionEvent(actionInfos[0].m_strName, nTransTime, bForceStop, attackEvent));
EventBus.PublishChannel(m_nId, new PlayActionEvent(actionInfos[0].m_strName, nTransTime, bForceStop, attackEvent, isLoop));
for(int i = 1; i < actionInfos.Count; i++)
{
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false));
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop));
}
var eventInfoList = combinedAction.m_EventInfoLst;
if(eventInfoList != null && eventInfoList.Count > 0)
{
foreach(var eventInfo in eventInfoList)
{
//0 is sound event
if (eventInfo is FX_BASE_INFO sfx)
if(eventInfo.m_nType == 0)
{
if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0)
//0 is sound event
if (eventInfo is FX_BASE_INFO sfx)
{
string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0]);
soundpath = soundpath.ToLower();
SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget();
if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0)
{
string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0],true);
soundpath = soundpath.ToLower();
SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget();
}
}
}
}
}
return true;
@@ -717,10 +720,10 @@ public class CECModel
}
var actionInfos = combinedAction.m_ActLst;
var isLoop = combinedAction.m_nLoops == 1;
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[0].m_strName, null, false, attackEvent, nTransTime, bForceStopPrevAct));
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[0].m_strName, null, false, attackEvent, nTransTime, bForceStopPrevAct, isLoop));
for(int i = 1; i < actionInfos.Count; i++)
{
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false));
EventBus.PublishChannelClass(m_nId, new QueueActionEvent(actionInfos[i].m_strName, null, false, attackEvent, nTransTime, false, isLoop));
}
return true;
}
@@ -774,7 +777,7 @@ public class CECModel
// TODO Phase 4: Add hook scale factor support
// TODO 第4阶段:添加挂点缩放因子支持
// fScale *= pHook->GetScaleFactor();
#if UNITY_EDITOR
BMLogger.Log($"[CECModel.PlayGfx] Using hook '{szHook}' for GFX '{szPath}', scale={fScale}");
#endif
@@ -845,4 +848,4 @@ public class CECModel
public enum ActionChannel
{
ACTCHA_WOUND = 1,
};
};
+1 -1
View File
@@ -151,7 +151,7 @@ public class CECNPC : CECObject
// ACHAR thường là UTF-16LE → len là số byte
var nameBytes = r.ReadBytes(len);
m_strName = System.Text.Encoding.Unicode.GetString(nameBytes);
BMLogger.LogError($"HoangDev:m_npcUI = {m_npcUI}, NPC Name = " + m_strName);
// BMLogger.LogError($"HoangDev:m_npcUI = {m_npcUI}, NPC Name = " + m_strName);
if (m_npcUI != null)
{
m_npcUI.SetName(m_strName);
+5 -5
View File
@@ -18,20 +18,20 @@ public class NPCVisual : MonoBehaviour
public CECNPC.INFO GetNPCINFO => m_NPCInfo;
public bool TryPlayAction(string animationName, CECAttackEvent cECAttackEvent, bool isHit = false, bool bRestart = true)
{
BMLogger.LogMono(this, "HoangDev: TryPlayAction: " + animationName);
// BMLogger.LogMono(this, "HoangDev: TryPlayAction: " + animationName);
if (namedAnimancer == null) return false;
BMLogger.LogMono(this, "HoangDev: namedAnimancer == null: " + animationName);
// BMLogger.LogMono(this, "HoangDev: namedAnimancer == null: " + animationName);
if (namedAnimancer.IsPlaying(animationName)) return false;
BMLogger.LogMono(this, "HoangDev: namedAnimancerIsPlaying == null1: " + animationName);
// BMLogger.LogMono(this, "HoangDev: namedAnimancerIsPlaying == null1: " + animationName);
_currentState = namedAnimancer.TryPlay(animationName, fadeTime);
if (isHit)
{
_currentState.Events.OnEnd = () => SetHitOnEnd(cECAttackEvent);
}
if (_currentState != null)
BMLogger.LogMono(this, "HoangDev: _currentState != null1: " + _currentState.Clip.name);
// if (_currentState != null)
// BMLogger.LogMono(this, "HoangDev: _currentState != null1: " + _currentState.Clip.name);
return _currentState != null;
}
private void SetHitOnEnd(CECAttackEvent cECAttackEvent)
@@ -52,6 +52,8 @@ namespace CSNetwork
private Action<RoleInfo> _createRoleCallback;
private RoleInfo _selectedRole;
public bool IsConnected => _networkManager?.IsConnected ?? false;
public bool IsConnectedInternet => Application.internetReachability != NetworkReachability.NotReachable;
// When true, suppress *gameplay traffic* (mostly gamedatasend C2S commands) during logout/scene transitions.
// We still allow account/role flow protocols like rolelist/selectrole so "Return to Select Role" can work.
private volatile bool _suppressGameplayTraffic = false;
@@ -1437,7 +1439,7 @@ namespace CSNetwork
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PRODUCEITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.LEARN_SKILL:
BMLogger.LogError("### GameDataSend: LEARN_SKILL");
// BMLogger.LogError("### GameDataSend: LEARN_SKILL");
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_LEARNSKILL, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
break;
case CommandID.GAIN_PET:
@@ -2680,8 +2682,8 @@ namespace CSNetwork
{
gamedatasend gamedatasend = new gamedatasend();
gamedatasend.Data = C2SCommandFactory.CreateNPCSevLearnSkillCmd(idSkill);
BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill gamedatasend.Data : " + gamedatasend.Data.Size);
BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill idSkill : " + idSkill);
// BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill gamedatasend.Data : " + gamedatasend.Data.Size);
// BMLogger.LogError("HoangDev : c2s_SendCmdNPCSevLearnSkill idSkill : " + idSkill);
SendProtocol(gamedatasend);
}
@@ -547,7 +547,7 @@ namespace BrewMonster.Network
}
public static void c2s_SendCmdNPCSevLearnSkill(int idSkill)
{
BMLogger.LogError("c2s_SendCmdNPCSevLearnSkill");
// BMLogger.LogError("c2s_SendCmdNPCSevLearnSkill");
Instance._gameSession.c2s_SendCmdNPCSevLearnSkill(idSkill);
}
public static void c2s_CmdNPCSevHello(int nid)
@@ -167,20 +167,19 @@ namespace BrewMonster
{
m_pEPWorkMan.StartNormalWork(new CECEPWorkMove(m_pEPWorkMan));
}
// Play action
if (IsValidAction(m_iCurAction))
{
if (!IsPlayingAction((int)PLAYER_ACTION_TYPE.ACT_TRICK_JUMP) && !IsPlayingAction((int)PLAYER_ACTION_TYPE.ACT_TRICK_RUN))
{
if (m_iMoveMode == Move_Mode.MOVE_JUMP || m_iMoveMode == Move_Mode.MOVE_SLIDE)
PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_LOOP, false);
PlayAction((int)PLAYER_ACTION_TYPE.ACT_JUMP_LOOP, false, 200, false, true);
else
PlayAction(GetMoveStandAction(true), false);
PlayAction(GetMoveStandAction(true), false, 200, false, true);
}
}
}
else
PlayAction(GetMoveStandAction(true), true, 1, false);
PlayAction(GetMoveStandAction(true), true, 200, false, true);
}
public bool MovingTo(float dwDeltaTime)
@@ -217,7 +216,7 @@ namespace BrewMonster
if (Math.Abs(fMoveDelta - 0f) <= float.Epsilon || fMoveDelta >= fDist) //!fMoveDelta <=> (Math.Abs(fMoveDelta - 0f) <= float.Epsilon) Compare with 0
{
SetPos(m_vServerPos);
PlayAction(GetMoveStandAction(false), true, 1, false);
PlayAction(GetMoveStandAction(false), true, 200, false);
bRet = true;
}
else
@@ -277,7 +276,7 @@ namespace BrewMonster
{
m_bStopMove = false;
SetPos(Cmd.dest);
PlayAction(GetMoveStandAction(true), true, 1, false);
PlayAction(GetMoveStandAction(true), true, 200, false);
m_pEPWorkMan.FinishWork(CECEPWork.EP_work_ID.WORK_MOVE);
return;
}
@@ -323,7 +322,7 @@ namespace BrewMonster
m_pEPWorkMan.StartNormalWork(new CECEPWorkMove(m_pEPWorkMan));
}
PlayAction(GetMoveStandAction(true), true, 1, false);
PlayAction(GetMoveStandAction(true), true, 200, false);
}
public float GetDistToHost() { return m_fDistToHost; }
@@ -176,12 +176,14 @@ namespace BrewMonster.Scripts.Skills
//BMLogger.LogError("ProcessServiceList npcList.count:" + npcList.count);
//BMLogger.LogError("ProcessServiceList m_allProfNPCs.count:" + m_allProfNPCs.Count);
int i;
bool sawProfSkillNpcInList = false;
for (i = 0; i < npcList.count; i++)
{
int tid = npcList.list[i].tid;
//BMLogger.LogError("ProcessServiceList tid:" + tid);
if (m_allProfNPCs.Contains(tid))
{
sawProfSkillNpcInList = true;
//BMLogger.LogError("m_skillLearnNPCNID : " + m_skillLearnNPCNID);
//BMLogger.LogError("npcList.list[i].nid : " + npcList.list[i].nid);
if (m_skillLearnNPCNID != npcList.list[i].nid)
@@ -193,17 +195,25 @@ namespace BrewMonster.Scripts.Skills
//NotifyObservers(change);
break;
}
else
{
// Same trainer instance as before — still refresh service skills (re-enter world / list refresh).
SetCurServiceSkills(tid);
var change = new CECSkillPanelChange(CECSkillPanelChange.enumChangeMask.CHANGE_SKILL_NPC, 0, 0);
break;
}
}
}
if (i == npcList.count && m_skillLearnNPCNID != 0)
if (i == npcList.count && m_skillLearnNPCNID != 0 && !sawProfSkillNpcInList)
{
m_skillLearnNPCNID = 0;
SetCurServiceSkills(0);
var change = new CECSkillPanelChange(CECSkillPanelChange.enumChangeMask.CHANGE_SKILL_NPC, 0, 0);
//NotifyObservers(change);
}
m_npcListData.Clear();
// Do not Clear m_npcListData: Initialize() may run ProcessServiceList again after Release();
// clearing here left npcDataSize 0 on world re-entry. New packets replace m_npcListData in RecvNPCServiceList.
}
//BMLogger.LogError("HoangDev: m_npcListData.Size :"+ m_npcListData.Size);
@@ -320,10 +330,8 @@ namespace BrewMonster.Scripts.Skills
{
return;
}
else
{
ProcessServiceList();
}
ProcessServiceList();
}
private void InitSkillTreeRootMap(IEnumerable<int> rootSkills)
{
@@ -11,7 +11,8 @@ public class AudioManager : MonoBehaviour
[SerializeField] private AudioMixerGroup _bgmMixerGroup;
[SerializeField] private AudioMixerGroup _ambienceMixerGroup;
[SerializeField] private AudioMixerGroup _sfxMixerGroup;
public AudioMixerGroup GetSfxMixerGroup => _sfxMixerGroup;
private AudioSource _ambienceSource;
void Awake()
@@ -63,7 +64,7 @@ public class AudioManager : MonoBehaviour
IEnumerator FadeInBGM(AudioClip clip, float fadeTime)
{
BMLogger.LogError($"HoangDev: FadeInBGM fadeTime {fadeTime} clip: {clip.name}" );
// BMLogger.LogError($"HoangDev: FadeInBGM fadeTime {fadeTime} clip: {clip.name}" );
if (bgmSource.isPlaying)
yield return StartCoroutine(FadeOutBGM(fadeTime));
@@ -92,11 +92,23 @@ namespace BrewMonster.Scripts
/// Uses the cache fast-path when the clip is already loaded; otherwise waits
/// for AddressableManager initialization (no per-frame lambda polling) before
/// loading and playing the clip.
/// <para>
/// Pass <paramref name="delay"/> (in seconds) to postpone playback. The wait
/// can be cancelled early via <paramref name="cancellationToken"/>; if cancelled
/// the method exits silently without playing anything.
/// </para>
/// </summary>
public async UniTaskVoid PlaySkillSfxAtPointAsync(string address, Vector3 worldPos)
public async UniTaskVoid PlaySkillSfxAtPointAsync(
string address,
Vector3 worldPos,
float delay = 0f,
System.Threading.CancellationToken cancellationToken = default)
{
// BMLogger.LogError ($"HoangDev PlaySkillSfxAtPointAsync: address={address}, worldPos={worldPos}");
if (string.IsNullOrEmpty(address)) return;
if (delay > 0f)
await UniTask.Delay(System.TimeSpan.FromSeconds(delay), cancellationToken: cancellationToken);
var mgr = AddressableManager.Instance;
if (mgr == null) return;
@@ -207,5 +219,10 @@ namespace BrewMonster.Scripts
_moveSoundSource.Play();
}
}
public async UniTaskVoid StopMoveSoundAsync()
{
if (_moveSoundSource == null) return;
_moveSoundSource.Stop();
}
}
}
@@ -53,8 +53,6 @@ namespace BrewMonster.Scripts
Debug.LogWarning($"[WorldMusicController] No music entry found for worldTag {worldTag}");
return;
}
BMLogger.LogError($"HoangDev: SetupAudioSources entry.bgmPath.ToLower():{entry.bgmPath.ToLower()}");
BMLogger.LogError($"HoangDev: SetupAudioSources entry.ambiencePath.ToLower()):{entry.ambiencePath.ToLower()}");
var bgmClip = await AddressableManager.Instance.LoadAudioClipAsync(entry.bgmPath.ToLower());
var ambienceClip = await AddressableManager.Instance.LoadAudioClipAsync(entry.ambiencePath.ToLower());
@@ -667,8 +667,8 @@ namespace BrewMonster.Scripts.Task
if (m_uTaskCount > 0)
m_uTaskCount--;
else
// TaskInterface::WriteLog(pTask->GetPlayerId(), uTaskId, 0, "ClearTask, TaskCount == 0");
// else
// // TaskInterface::WriteLog(pTask->GetPlayerId(), uTaskId, 0, "ClearTask, TaskCount == 0");
if (pEntry.m_ParentIndex != 0xff)
{
@@ -6,7 +6,7 @@ using BrewMonster.Scripts.Skills;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace BrewMonster.UI
{
[DisallowMultipleComponent]
@@ -129,7 +129,7 @@ namespace BrewMonster.UI
{
CreateOneRankDlg(taoistRank);
}
}
// ʼڶԻһԷڴ / Initialize rank sub-dialogs once to avoid realloc
@@ -190,7 +190,15 @@ namespace BrewMonster.UI
FitSize();
ShowLastSelectedSkill();
}
totalSPText.text = " <color=yellow>Nguyên Thần:</color> " + GetHostPlayer().GetBasicProps().iSP.ToString() + "</color>";
UpdateTotalSPText();
}
public void UpdateTotalSPText()
{
if (totalSPText != null && GetHostPlayer() != null)
{
totalSPText.text = " <color=yellow>Nguyên Thần:</color> " + GetHostPlayer().GetBasicProps().iSP.ToString() + "</color>";
}
}
// ħ״̬ / Reset when switching between god/evil
@@ -214,7 +222,7 @@ namespace BrewMonster.UI
CDlgSkillSubListItem subListItem = pSub;
if(subListItem == null )
{
BMLogger.LogError("HoangDev: UpdateOneSubDlg subListItem is null for skillID " + skillID);
// BMLogger.LogError("HoangDev: UpdateOneSubDlg subListItem is null for skillID " + skillID);
return;
}
subListItem.UpdateSkill(skillID);
@@ -495,6 +503,7 @@ namespace BrewMonster.UI
}
else if (q.m_changeMask == CECSkillPanelChange.enumChangeMask.CHANGE_SKILL_LEVEL_UP)
{
UpdateTotalSPText();
if (q.m_skillLevel == 1)
{
foreach (var kv in m_skillSubDialogsMap)
@@ -106,6 +106,7 @@ namespace BrewMonster
int needMoney = CECHostSkillModel.Instance.GetSkillMoney(m_skillID, m_curLevel + 1);
int needSp = CECHostSkillModel.Instance.GetSkillSp(m_skillID, m_curLevel + 1);
string str = GPDataTypeHelper.ReplacePercentD(GetStringFromTable(11326), needMoney, needSp);
// var messagebox = uiManager.ShowMessageBox(new MessageBoxData()
// {
@@ -115,16 +116,38 @@ namespace BrewMonster
// });
var messagebox = uiManager.ShowMessageBoxYes("Game_LearnSkill", str, this,
() => {
UnityGameSession.c2s_SendCmdNPCSevLearnSkill(m_skillID);
int skillID = (int)GetData();
int nCondition = EC_Game.GetGameRun().GetHostPlayer().CheckSkillLearnCondition(skillID, true);
BMLogger.LogError("HoangDev: CDlgSkillSubListItem OnCommand_Upgrade clicked yes for nCondition " + nCondition);
CECHostPlayer hostPlayer = GetHostPlayer();
if (hostPlayer == null)
return;
if (0 == nCondition)
int skillID = m_skillID;
int recheck = hostPlayer.CheckSkillLearnCondition(skillID, true);
if (recheck != 0)
{
UnityGameSession.c2s_SendCmdNPCSevLearnSkill(skillID);
UpdateUpgradeBtn();
if (!string.IsNullOrEmpty(m_upgradeDisabledReason))
{
uiManager.ShowMessageBoxGeneral("MessageBox", m_upgradeDisabledReason, this);
}
return;
}
int costMoney = CECHostSkillModel.Instance.GetSkillMoney(skillID, m_curLevel + 1);
int costSp = CECHostSkillModel.Instance.GetSkillSp(skillID, m_curLevel + 1);
if(!hostPlayer.TryConsumeSkillLearnCost(costMoney, costSp))
{
UpdateUpgradeBtn();
if(!string.IsNullOrEmpty(m_upgradeDisabledReason))
{
uiManager.ShowMessageBoxGeneral("MessageBox", m_upgradeDisabledReason, this);
}
return;
}
UnityGameSession.c2s_SendCmdNPCSevLearnSkill(skillID);
UpdateUpgradeBtn();
});
messagebox.SetData((uint)m_skillID);
//GetGameUIMan()->MessageBox("Game_LearnSkill", str, //GetGameUIMan()->GetStringFromTable(231),
@@ -44,7 +44,6 @@ namespace BrewMonster
private void OnUIEvent(UIEvent uiEvent)
{
Debug.Log($"[HUDMainGamePlay] OnUIEvent: {uiEvent.EventType}");
switch(uiEvent.EventType)
{
case UIEventType.ShowTrace:
@@ -11,17 +11,17 @@ namespace BrewMonster.UI
public void OnClick()
{
// CECUIManager.Instance.ShowMessageBox(
// title: "Thoát",
// title: "Thoát",
// message: "Đang rời khỏi Thế Giới Hoàn Mỹ",
// messageBoxType: MessageBoxType.YesButton
// );
// CECGameRun.Instance.GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf());
// UnityGameSession.ReturnToSelectRole();
OnCommandRepick();
}
// void CDlgSystem3::OnCommandRepick(const char *szCommand)
// {
// a_LogOutput(1, "CDlgSystem3::OnCommandRepick ");
@@ -36,8 +36,8 @@ namespace BrewMonster.UI
// pMsgBox->SetIsModal(false);
// }
// }
void OnCommandRepick()
{
CECUIManager.Instance.ShowMessageBoxYes("Thoát",
@@ -47,8 +47,13 @@ namespace BrewMonster.UI
void OnClickYes()
{
CECGameRun.Instance.GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf());
if (!UnityGameSession.Instance.GameSession.IsConnectedInternet)
{
//force log out half
EC_Game.GetGameRun().SetLogoutFlag(1);
}
}
}
}
@@ -1,440 +1,427 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BrewMonster.Network;
using BrewMonster.Scripts;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace BrewMonster.UI
{
/// <summary>
/// Login Flow:
/// 1. Enter username and password
/// 2. Click login button
/// 3. Login success, get the list of characters
/// 4. Open the select character screen
/// </summary>
public class LoginScreenUI : MonoBehaviour
{
[SerializeField] private TMP_InputField _usernameInputField;
[SerializeField] private TMP_InputField _passwordInputField;
[SerializeField] private Button _loginButton;
[SerializeField] private SelecScreenCharacter _selectCharacterScreen;
private List<RoleInfo> _roleInfos;
private List<RoleInfo> _currentRoles;
private RoleInfo _pendingCreatedRole;
private bool _loginInProgress;
bool isDoneWorldRender = false;
bool isDoneNPCRender = false;
private SynchronizationContext context;
public AudioClip loginBGM;
void Awake()
{
// Ensure wrapper created early (Tech3C SDK).
_ = Tech3CSDKWrapper.Instance;
#if UNITY_EDITOR
_usernameInputField.gameObject.SetActive(true);
_passwordInputField.gameObject.SetActive(true);
#else
_usernameInputField.gameObject.SetActive(false);
_passwordInputField.gameObject.SetActive(false);
#endif
}
void OnEnable()
{
Tech3CSDKWrapper.Instance.SetLoginCallback(OnLoginCallback);
Tech3CSDKWrapper.Instance.SetLogoutCallback(OnLogoutCallback);
}
private void OnDisable()
{
Tech3CSDKWrapper.Instance.RemoveLoginCallback();
Tech3CSDKWrapper.Instance.RemoveLogoutCallback();
}
void Start()
{
AudioManager.Instance.PlayBGM(loginBGM, 1.5f);
_loginButton.onClick.AddListener(OnLoginButtonClicked);
context = SynchronizationContext.Current;
#if UNITY_EDITOR
// only load the username and password from the player prefs if in editor
_usernameInputField.text = PlayerPrefs.GetString("username", "");
_passwordInputField.text = PlayerPrefs.GetString("password", "");
#endif
// Default: login UI first, select-role hidden until login succeeds.
if (_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(false);
// ApplyLoginEntry(LogoutFlowState.ConsumeNextLoginEntry());
}
// Update is called once per frame
void Update()
{
if (_roleInfos != null)
{
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete);
_roleInfos = null;
}
#if UNITY_EDITOR
if (Input.GetKeyUp(KeyCode.LeftAlt))
{
_usernameInputField.text = "test016";
_passwordInputField.text = "123456";
OnLoginButtonClicked();
}
if (Input.GetKeyUp(KeyCode.Tab))
{
_usernameInputField.text = "test017";
_passwordInputField.text = "123456";
OnLoginButtonClicked();
}
#endif
}
public async void OnLoginButtonClicked()
{
if (_loginInProgress)
{
BMLogger.LogWarning("[LoginScreenUI] Login already in progress (ignored click).");
return;
}
_loginInProgress = true;
if (_loginButton != null) _loginButton.interactable = false;
// If username or password is empty, use Tech3C SDK login UI.
if (string.IsNullOrEmpty(_usernameInputField.text) || string.IsNullOrEmpty(_passwordInputField.text))
{
// Use Tech3C SDK login UI.
bool started = Tech3CSDKWrapper.Instance.Login();
if (!started)
{
// Fallback: manual username/password login (useful in dev if SDK not configured).
BMLogger.LogWarning("[LoginScreenUI] Tech3CSDKWrapper.Login() failed, fallback to manual login.");
await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
}
}
else
{
// otherwise use manual username/password login.
BMLogger.LogError("[LoginScreenUI] Username/password empty.");
await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
}
}
private async Task BeginGameLoginAsync(string username, string password)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
BMLogger.LogError("[LoginScreenUI] Username/password empty.");
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
return;
}
BMLogger.Log("OnLoginButtonClicked");
UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
PlayerPrefs.SetString("username", username);
PlayerPrefs.SetString("password", password);
PlayerPrefs.Save();
BMLogger.Log($"[LoginScreenUI] Connecting+login start user='{username}'");
await UnityGameSession.Login(username, password, OnLoginComplete);
}
/// <summary>
/// Apply how LoginScene should look after a logout flow.
/// Call this when LoginScene is already loaded (additive) and you need to switch UI without reloading the scene.
/// </summary>
public void ApplyLoginEntry(BrewMonster.Network.LogoutFlowState.LoginEntryTarget entry)
{
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
// Always refresh fields from PlayerPrefs (LogoutAccount clears them).
if (_usernameInputField != null) _usernameInputField.text = PlayerPrefs.GetString("username", "");
if (_passwordInputField != null) _passwordInputField.text = PlayerPrefs.GetString("password", "");
if (_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(false);
if (entry == BrewMonster.Network.LogoutFlowState.LoginEntryTarget.SelectRole)
{
// If we're returning to select role, skip straight to select role without showing login UI again, since we never fully left the game session.
OnLoginComplete(true);
return;
// Auto-login to reach Select Role like the original client, without showing Tech3C auth UI again.
if (!string.IsNullOrEmpty(_usernameInputField.text) && !string.IsNullOrEmpty(_passwordInputField.text))
{
BMLogger.Log("[LoginScreenUI] Auto-login triggered (return-to-select-role).");
_loginInProgress = true;
if (_loginButton != null) _loginButton.interactable = false;
_ = BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
}
}
}
/// <summary>
/// Callback when the login is complete.
/// Then get the list of characters
/// </summary>
private void OnLoginComplete(bool result)
{
BMLogger.Log($"[LoginScreenUI] OnLoginComplete result={result}");
if (!result)
{
BMLogger.LogError("Login failed");
if (_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(false);
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
return;
}
if (_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(true);
UnityGameSession.GetRoleListAsync(OnGetRoleListComplete);
}
/// <summary>
/// Callback when the list of characters is retrieved.
/// Then move to the select character screen
/// </summary>
private void OnGetRoleListComplete(List<RoleInfo> roleInfos)
{
if (roleInfos == null)
{
BMLogger.LogError("OnGetRoleListComplete: roleInfos is null");
// Keep whatever is currently shown; don't overwrite UI state with null.
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
return;
}
// Merge pending created role in case backend list hasn't updated yet.
if (_pendingCreatedRole != null)
{
bool exists = false;
for (int i = 0; i < roleInfos.Count; i++)
{
if (roleInfos[i].roleid == _pendingCreatedRole.roleid)
{
exists = true;
break;
}
}
if (!exists)
{
// Copy list so we don't mutate a list owned elsewhere.
var merged = new List<RoleInfo>(roleInfos.Count + 1);
merged.AddRange(roleInfos);
merged.Add(_pendingCreatedRole);
roleInfos = merged;
}
else
{
// Backend now includes the role; clear pending.
_pendingCreatedRole = null;
}
}
BMLogger.Log($"OnGetRoleListComplete: roles={roleInfos.Count}");
_roleInfos = roleInfos;
_currentRoles = roleInfos;
// Login flow finished; keep login button disabled (origin-like) once you're at select role.
_loginInProgress = false;
}
private void OnClickSelectCharacter(RoleInfo roleInfo)
{
LoadingSceneController.Instance.ShowLoadingScene(true);
UnityGameSession.SelectRoleAsync(roleInfo, OnSelectRoleComplete);
}
/// <summary>
/// Callback when a new character is created.
/// Refreshes the role list and keeps the character selection screen visible.
/// </summary>
private void OnCreateCharacterComplete(RoleInfo createdRole)
{
BMLogger.Log("Character created, refreshing role list...");
if (_selectCharacterScreen != null)
{
_selectCharacterScreen.gameObject.SetActive(true);
}
// Ensure the newly created role is visible immediately even if the server role list
// hasn't updated yet.
if (createdRole != null)
{
_pendingCreatedRole = createdRole;
if (_currentRoles == null)
{
_currentRoles = new List<RoleInfo>();
}
bool exists = false;
for (int i = 0; i < _currentRoles.Count; i++)
{
if (_currentRoles[i].roleid == createdRole.roleid)
{
exists = true;
break;
}
}
if (!exists)
{
_currentRoles.Add(createdRole);
}
_roleInfos = _currentRoles;
}
else
{
BMLogger.LogError("OnCreateCharacterComplete: createdRole is null (create-role callback returned null)");
}
// NOTE:
// Immediately requesting the role list after create has been observed to disconnect
// in some server builds. We rely on the createdRole callback to update UI instantly.
// A server sync can be done later (e.g., next time you open this screen / re-login).
}
private void OnSelectRoleComplete(RoleInfo roleInfo)
{
context.Post(_ =>
{
isDoneWorldRender = false;
isDoneNPCRender = false;
Action actLoadChar = () =>
{
if (!isDoneNPCRender || !isDoneWorldRender)
{
return;
}
};
SceneLoader.SceneLoadProcess = SceneLoadProcess.Loading;
SceneLoader.LoadingProgress = 0;
#if TESTFAST
string nameScene = "LoginScene";
SceneManager.UnloadSceneAsync(nameScene);
isDoneNPCRender = true;
isDoneWorldRender = true;
actLoadChar?.Invoke();
WorldMusicController.Instance.InitForWorld(roleInfo.worldtag);
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
#else
string nameScene = UnityGameSession.Instance.GetWorldInstanceName();
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Single,
(progress) =>
{
LoadingSceneController.Instance.SetProgress(progress);
},
(value) =>
{
isDoneWorldRender = value;
isDoneNPCRender = true;
isDoneWorldRender = true;
actLoadChar?.Invoke();
WorldMusicController.Instance.InitForWorld(roleInfo.worldtag);
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
});
#endif
}, null);
}
private async void OnEnterWorldComplete()
{
await Task.Delay(2000);
// Request all known packages: 0=Inventory,1=Equipment,2=Task
UnityGameSession.RequestAllInventoriesAsync(() => { /*BMLogger.Log("Sent Inventory Detail Requests (all packs)");*/ }, 0, 1, 2);
await Task.Delay(1000);
UnityGameSession.RequestCheckSecurityPassWd("");
// C++ friend_GetList(); required before Add Friend
await Task.Delay(1000);
UnityGameSession.Friend_GetList();
}
//private void OnInventoryReceived(List<InventoryItem> inventoryData)
//{
// _inventoryUI.DisplayInventory(inventoryData);
//}
#if UNITY_EDITOR
private void OnValidate()
{
if (_usernameInputField == null)
{
// find childrend with name "username"
_usernameInputField = transform.Find("username").GetComponent<TMP_InputField>();
}
if (_passwordInputField == null)
{
// find childrend with name "password"
_passwordInputField = transform.Find("password").GetComponent<TMP_InputField>();
}
if (_loginButton == null)
{
// find childrend with name "LoginBtn"
_loginButton = transform.Find("LoginBtn").GetComponent<Button>();
}
}
#endif
private async void OnLoginCallback(int errorCode, string userId, string password)
{
if (errorCode == 0)
{
BMLogger.Log($"Login success -- userId: {userId} - {password}");
// UnityGameSession.SetConnectionInfo("103.182.22.52", 29000);
UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
PlayerPrefs.SetString("username", userId);
PlayerPrefs.SetString("password", password);
PlayerPrefs.Save();
await BeginGameLoginAsync(userId, password);
}
else
{
// if it failed, the userId will be the error message
BMLogger.LogError($"Login failed -- errorCode: {errorCode}: {userId}");
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
}
}
private void OnLogoutCallback(int errorCode, string errorMessage)
{
if (errorCode == 0)
{
BMLogger.Log("Logout success");
}
else
{
BMLogger.LogError($"Logout failed -- errorCode: {errorCode}: {errorMessage}");
}
}
}
}
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using BrewMonster.Network;
using BrewMonster.Scripts;
using CSNetwork.Protocols;
using CSNetwork.Protocols.RPCData;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace BrewMonster.UI
{
/// <summary>
/// Login Flow:
/// 1. Enter username and password
/// 2. Click login button
/// 3. Login success, get the list of characters
/// 4. Open the select character screen
/// </summary>
public class LoginScreenUI : MonoBehaviour
{
[SerializeField] private TMP_InputField _usernameInputField;
[SerializeField] private TMP_InputField _passwordInputField;
[SerializeField] private Button _loginButton;
[SerializeField] private SelecScreenCharacter _selectCharacterScreen;
private List<RoleInfo> _roleInfos;
private List<RoleInfo> _currentRoles;
private RoleInfo _pendingCreatedRole;
private bool _loginInProgress;
bool isDoneWorldRender = false;
bool isDoneNPCRender = false;
private SynchronizationContext context;
[Header("Audio")]
[Tooltip("Set 3 clips to pick a random login theme.")]
[SerializeField] private AudioClip[] _loginBGMs;
void Awake()
{
// Ensure wrapper created early (Tech3C SDK).
_ = Tech3CSDKWrapper.Instance;
#if UNITY_EDITOR
_usernameInputField.gameObject.SetActive(true);
_passwordInputField.gameObject.SetActive(true);
#else
_usernameInputField.gameObject.SetActive(false);
_passwordInputField.gameObject.SetActive(false);
#endif
}
void OnEnable()
{
Tech3CSDKWrapper.Instance.SetLoginCallback(OnLoginCallback);
Tech3CSDKWrapper.Instance.SetLogoutCallback(OnLogoutCallback);
}
private void OnDisable()
{
Tech3CSDKWrapper.Instance.RemoveLoginCallback();
Tech3CSDKWrapper.Instance.RemoveLogoutCallback();
}
void Start()
{
var chosenBgm = PickLoginBGM();
if (chosenBgm != null && AudioManager.Instance != null)
{
AudioManager.Instance.PlayBGM(chosenBgm, 1.5f);
}
_loginButton.onClick.AddListener(OnLoginButtonClicked);
context = SynchronizationContext.Current;
#if UNITY_EDITOR
// only load the username and password from the player prefs if in editor
_usernameInputField.text = PlayerPrefs.GetString("username", "");
_passwordInputField.text = PlayerPrefs.GetString("password", "");
#endif
// Default: login UI first, select-role hidden until login succeeds.
if (_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(false);
// ApplyLoginEntry(LogoutFlowState.ConsumeNextLoginEntry());
}
private AudioClip PickLoginBGM()
{
if (_loginBGMs != null && _loginBGMs.Length > 0)
{
// UnityEngine.Random is deterministic per session and good enough here.
int idx = UnityEngine.Random.Range(0, _loginBGMs.Length);
return _loginBGMs[idx];
}
return null;
}
// Update is called once per frame
void Update()
{
if (_roleInfos != null)
{
_selectCharacterScreen.InitScreen(_roleInfos, OnClickSelectCharacter, OnCreateCharacterComplete);
_roleInfos = null;
}
#if UNITY_EDITOR
if (Input.GetKeyUp(KeyCode.LeftAlt))
{
_usernameInputField.text = "test016";
_passwordInputField.text = "123456";
OnLoginButtonClicked();
}
if (Input.GetKeyUp(KeyCode.Tab))
{
_usernameInputField.text = "test017";
_passwordInputField.text = "123456";
OnLoginButtonClicked();
}
#endif
}
public async void OnLoginButtonClicked()
{
if (_loginInProgress)
{
BMLogger.LogWarning("[LoginScreenUI] Login already in progress (ignored click).");
return;
}
_loginInProgress = true;
if (_loginButton != null) _loginButton.interactable = false;
// If username or password is empty, use Tech3C SDK login UI.
if (string.IsNullOrEmpty(_usernameInputField.text) || string.IsNullOrEmpty(_passwordInputField.text))
{
// Use Tech3C SDK login UI.
bool started = Tech3CSDKWrapper.Instance.Login();
if (!started)
{
// Fallback: manual username/password login (useful in dev if SDK not configured).
BMLogger.LogWarning("[LoginScreenUI] Tech3CSDKWrapper.Login() failed, fallback to manual login.");
await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
}
}
else
{
// otherwise use manual username/password login.
BMLogger.LogError("[LoginScreenUI] Username/password empty.");
await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
}
}
private async Task BeginGameLoginAsync(string username, string password)
{
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
BMLogger.LogError("[LoginScreenUI] Username/password empty.");
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
return;
}
BMLogger.Log("OnLoginButtonClicked");
UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
PlayerPrefs.SetString("username", username);
PlayerPrefs.SetString("password", password);
PlayerPrefs.Save();
BMLogger.Log($"[LoginScreenUI] Connecting+login start user='{username}'");
await UnityGameSession.Login(username, password, OnLoginComplete);
}
// NOTE: Previously had an ApplyLoginEntry helper that referenced LogoutFlowState.
// It was unused and caused build/lint issues on some platforms, so it's been removed.
/// <summary>
/// Callback when the login is complete.
/// Then get the list of characters
/// </summary>
private void OnLoginComplete(bool result)
{
BMLogger.Log($"[LoginScreenUI] OnLoginComplete result={result}");
if (!result)
{
BMLogger.LogError("Login failed");
if (_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(false);
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
return;
}
if (_selectCharacterScreen != null)
_selectCharacterScreen.gameObject.SetActive(true);
UnityGameSession.GetRoleListAsync(OnGetRoleListComplete);
}
/// <summary>
/// Callback when the list of characters is retrieved.
/// Then move to the select character screen
/// </summary>
private void OnGetRoleListComplete(List<RoleInfo> roleInfos)
{
if (roleInfos == null)
{
BMLogger.LogError("OnGetRoleListComplete: roleInfos is null");
// Keep whatever is currently shown; don't overwrite UI state with null.
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
return;
}
// Merge pending created role in case backend list hasn't updated yet.
if (_pendingCreatedRole != null)
{
bool exists = false;
for (int i = 0; i < roleInfos.Count; i++)
{
if (roleInfos[i].roleid == _pendingCreatedRole.roleid)
{
exists = true;
break;
}
}
if (!exists)
{
// Copy list so we don't mutate a list owned elsewhere.
var merged = new List<RoleInfo>(roleInfos.Count + 1);
merged.AddRange(roleInfos);
merged.Add(_pendingCreatedRole);
roleInfos = merged;
}
else
{
// Backend now includes the role; clear pending.
_pendingCreatedRole = null;
}
}
BMLogger.Log($"OnGetRoleListComplete: roles={roleInfos.Count}");
_roleInfos = roleInfos;
_currentRoles = roleInfos;
// Login flow finished; keep login button disabled (origin-like) once you're at select role.
_loginInProgress = false;
}
private void OnClickSelectCharacter(RoleInfo roleInfo)
{
LoadingSceneController.Instance.ShowLoadingScene(true);
UnityGameSession.SelectRoleAsync(roleInfo, OnSelectRoleComplete);
}
/// <summary>
/// Callback when a new character is created.
/// Refreshes the role list and keeps the character selection screen visible.
/// </summary>
private void OnCreateCharacterComplete(RoleInfo createdRole)
{
BMLogger.Log("Character created, refreshing role list...");
if (_selectCharacterScreen != null)
{
_selectCharacterScreen.gameObject.SetActive(true);
}
// Ensure the newly created role is visible immediately even if the server role list
// hasn't updated yet.
if (createdRole != null)
{
_pendingCreatedRole = createdRole;
if (_currentRoles == null)
{
_currentRoles = new List<RoleInfo>();
}
bool exists = false;
for (int i = 0; i < _currentRoles.Count; i++)
{
if (_currentRoles[i].roleid == createdRole.roleid)
{
exists = true;
break;
}
}
if (!exists)
{
_currentRoles.Add(createdRole);
}
_roleInfos = _currentRoles;
}
else
{
BMLogger.LogError("OnCreateCharacterComplete: createdRole is null (create-role callback returned null)");
}
// NOTE:
// Immediately requesting the role list after create has been observed to disconnect
// in some server builds. We rely on the createdRole callback to update UI instantly.
// A server sync can be done later (e.g., next time you open this screen / re-login).
}
private void OnSelectRoleComplete(RoleInfo roleInfo)
{
context.Post(_ =>
{
isDoneWorldRender = false;
isDoneNPCRender = false;
Action actLoadChar = () =>
{
if (!isDoneNPCRender || !isDoneWorldRender)
{
return;
}
};
SceneLoader.SceneLoadProcess = SceneLoadProcess.Loading;
SceneLoader.LoadingProgress = 0;
#if TESTFAST
string nameScene = "LoginScene";
SceneManager.UnloadSceneAsync(nameScene);
isDoneNPCRender = true;
isDoneWorldRender = true;
actLoadChar?.Invoke();
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
#else
string nameScene = UnityGameSession.Instance.GetWorldInstanceName();
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Single,
(progress) =>
{
LoadingSceneController.Instance.SetProgress(progress);
},
(value) =>
{
isDoneWorldRender = value;
isDoneNPCRender = true;
isDoneWorldRender = true;
actLoadChar?.Invoke();
AudioManager.Instance.StopBGM(1f);
WorldMusicController.Instance.InitForWorld(roleInfo.worldtag);
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
});
#endif
}, null);
}
private async void OnEnterWorldComplete()
{
await Task.Delay(2000);
// Request all known packages: 0=Inventory,1=Equipment,2=Task
UnityGameSession.RequestAllInventoriesAsync(() => { /*BMLogger.Log("Sent Inventory Detail Requests (all packs)");*/ }, 0, 1, 2);
await Task.Delay(1000);
UnityGameSession.RequestCheckSecurityPassWd("");
// C++ friend_GetList(); required before Add Friend
await Task.Delay(1000);
UnityGameSession.Friend_GetList();
}
//private void OnInventoryReceived(List<InventoryItem> inventoryData)
//{
// _inventoryUI.DisplayInventory(inventoryData);
//}
#if UNITY_EDITOR
private void OnValidate()
{
if (_usernameInputField == null)
{
// find childrend with name "username"
_usernameInputField = transform.Find("username").GetComponent<TMP_InputField>();
}
if (_passwordInputField == null)
{
// find childrend with name "password"
_passwordInputField = transform.Find("password").GetComponent<TMP_InputField>();
}
if (_loginButton == null)
{
// find childrend with name "LoginBtn"
_loginButton = transform.Find("LoginBtn").GetComponent<Button>();
}
}
#endif
private async void OnLoginCallback(int errorCode, string userId, string password)
{
if (errorCode == 0)
{
BMLogger.Log($"Login success -- userId: {userId} - {password}");
// UnityGameSession.SetConnectionInfo("103.182.22.52", 29000);
UnityGameSession.SetConnectionInfo("103.51.120.195", 29000);
PlayerPrefs.SetString("username", userId);
PlayerPrefs.SetString("password", password);
PlayerPrefs.Save();
await BeginGameLoginAsync(userId, password);
}
else
{
// if it failed, the userId will be the error message
BMLogger.LogError($"Login failed -- errorCode: {errorCode}: {userId}");
_loginInProgress = false;
if (_loginButton != null) _loginButton.interactable = true;
}
}
private void OnLogoutCallback(int errorCode, string errorMessage)
{
if (errorCode == 0)
{
BMLogger.Log("Logout success");
}
else
{
BMLogger.LogError($"Logout failed -- errorCode: {errorCode}: {errorMessage}");
}
}
}
}
@@ -1247,7 +1247,6 @@ public class NPCShopUIManager : AUIDialog
return;
}
// Server requires greeting before NPC service commands.
if (CurrentNPCID != 0)
UnityGameSession.c2s_CmdNPCSevHello((int)CurrentNPCID);
@@ -1259,7 +1258,9 @@ public class NPCShopUIManager : AUIDialog
return;
}
long totalSellValue = 0;
var itemsToSell = new List<npc_sell_item>(sellSlotToSourceSlot.Count);
foreach (var pair in sellSlotToSourceSlot)
{
int sourceSlot = pair.Value;
@@ -1276,10 +1277,13 @@ public class NPCShopUIManager : AUIDialog
continue;
}
// Ensure local DB props (including price) are present when needed.
item.GetDetailDataFromLocal();
int price = item.GetScaledPrice();
long itemTotal = (long)price * item.m_iCount;
if (itemTotal > 0)
totalSellValue += itemTotal;
itemsToSell.Add(new npc_sell_item
{
tid = item.m_tid,
@@ -1298,17 +1302,56 @@ public class NPCShopUIManager : AUIDialog
UnityGameSession.c2s_CmdNPCSevSell(itemsToSell.Count, itemsToSell.ToArray());
Debug.Log($"[NPCShopUIManager] Sent sell command for {itemsToSell.Count} item(s) to NPC {CurrentNPCID}");
ResetSellReadySlots();
// Optimistic local money update: cộng tiền ngay trên client
if (host != null && totalSellValue > 0)
{
ulong currentMoney = host.GetMoneyAmount();
ulong nextMoney = currentMoney + (ulong)totalSellValue;
if (nextMoney > uint.MaxValue)
nextMoney = uint.MaxValue;
host.SetMoneyAmount((uint)nextMoney);
RefreshMoneyUiFromHost(host);
}
ResetSellReadySlots();
UpdateSellTotalPriceText();
UpdateBuyPriceTexts();
// Ask server for refreshed inventory, then repaint UI so sold items disappear.
var inventoryUI = FindFirstObjectByType<EC_InventoryUI>();
UnityGameSession.RequestInventoryAsync(0, () =>
{
if (inventoryUI != null)
inventoryUI.RefreshAll();
var callbackHost = CECGameRun.Instance?.GetHostPlayer();
if (callbackHost != null)
RefreshMoneyUiFromHost(callbackHost);
UpdateBuyPriceTexts();
UpdateSellTotalPriceText();
});
}
private void RefreshMoneyUiFromHost(CECHostPlayer host)
{
if (host == null)
return;
ulong amount = host.GetMoneyAmount();
ulong maxAmount = (ulong)Math.Max(0, host.GetMaxMoneyAmount());
var inventoryUI = FindFirstObjectByType<EC_InventoryUI>();
if (inventoryUI != null && inventoryUI.gameObject.activeInHierarchy)
{
inventoryUI.UpdateMoney(amount, maxAmount);
}
else
{
EC_InventoryUI.CacheMoney(amount, maxAmount);
}
}
private void OnUseItemClicked()
{
if (!TryGetSelectedInventoryItem(out var selectedItem, out var selectedSlot))
@@ -28,6 +28,7 @@ namespace BrewMonster
public override void OnEnable()
{
_currentSelectSkill = null;
UpdateView();
_skillSetUpComboWidget.ShowSetUpContent(false);
_skillSetUpComboWidget.OnClickedSkillSlot += OnClickedSkillSlot;
@@ -45,6 +46,7 @@ namespace BrewMonster
public override void OnDisable()
{
_currentSelectComboSlot = null;
if (_currentSelectSkill is LearnedSkillUI learnedOnClose)
{
learnedOnClose.SetFocusFrame(false);
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fce16fcbe8e82ec4588c7fba89c7cb64
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,3 +1,4 @@
using BrewMonster.Network;
using CSNetwork.Common;
using UnityEngine;
using UnityEngine.UI;
@@ -54,10 +55,14 @@ namespace BrewMonster.UI
public override bool Render()
{
// UpdateHostPlayerPositionImage();
UpdateHostPlayerPositionImage();
return base.Render();
}
Vector3 _hostPlayerPosition;
Quaternion _hostPlayerRotation;
private void UpdateHostPlayerPositionImage()
{
if (_hostPlayerPositionImage == null)
@@ -65,7 +70,7 @@ namespace BrewMonster.UI
return;
}
if (!TryGetHostPlayerPosition(out Vector3 hostPosition))
if (!TryGetHostPlayerPosition(out _hostPlayerPosition, out _hostPlayerRotation))
{
_hostPlayerPositionImage.enabled = false;
return;
@@ -81,13 +86,16 @@ namespace BrewMonster.UI
_hostPlayerPositionImage.enabled = true;
RectTransform hostPlayerRectTransform = _hostPlayerPositionImage.rectTransform;
hostPlayerRectTransform.anchoredPosition = new Vector2(
hostPosition.x / _positionFactor,
hostPosition.z / _positionFactor);
_hostPlayerPosition.x / _positionFactor,
_hostPlayerPosition.z / _positionFactor);
hostPlayerRectTransform.localRotation = Quaternion.Euler(0, 0, -_hostPlayerRotation.eulerAngles.y);
}
private bool TryGetHostPlayerPosition(out Vector3 hostPlayerPosition)
private bool TryGetHostPlayerPosition(out Vector3 hostPlayerPosition, out Quaternion hostPlayerRotation)
{
hostPlayerPosition = Vector3.zero;
hostPlayerRotation = Quaternion.identity;
CECHostPlayer hostPlayer = GetHostPlayer();
if (hostPlayer == null || hostPlayer.transform == null)
@@ -96,6 +104,7 @@ namespace BrewMonster.UI
}
hostPlayerPosition = hostPlayer.transform.position;
hostPlayerRotation = hostPlayer.transform.rotation;
return true;
}
@@ -139,6 +148,20 @@ namespace BrewMonster.UI
}
}
/// <summary>
/// When user click on the map texture.
/// We will calculate the world coordinates from the local cursor position. Then move the host player to the world coordinates.
/// </summary>
/// <param name="localCursorPosition"></param>
public void OnMapClicked(Vector2 localCursorPosition)
{
var worldCoordinates = localCursorPosition * _positionFactor;
UnityGameSession.c2s_CmdGoto(worldCoordinates.x, 1.0f, worldCoordinates.y);
// close the map
OnCloseButtonClicked();
}
private void OnCloseButtonClicked()
{
CloseDialogue();
@@ -0,0 +1,59 @@
using UnityEngine;
using UnityEngine.EventSystems;
namespace BrewMonster.UI
{
public class WorldMapClickHandler : MonoBehaviour, IPointerClickHandler
{
[Tooltip("The RectTransform of the Map Image (Usually this GameObject)")]
private RectTransform mapRectTransform;
[SerializeField] private RectTransform _hostPlayerPositionImage;
public DlgWorldMap dlgWorldMap;
// The host player player (0,0,0) is not at the center of the map. It usually has an offset that we have to calculate at Awake
private Vector2 _hostPlayerOffsetPosition;
private void Awake()
{
// Get the RectTransform of the map
mapRectTransform = GetComponent<RectTransform>();
CalculateHostPlayerOffsetPosition();
}
private void CalculateHostPlayerOffsetPosition()
{
_hostPlayerOffsetPosition = _hostPlayerPositionImage.anchoredPosition;
// if the max/min of the anchor is 0.5 0.5, then the host player is at the center of the map
// however we have to calculate the offset of the player because it is not at the center of the map
// we can calculate the offset by the max/min of the anchor
var maxAnchor = _hostPlayerPositionImage.anchorMax;
var minAnchor = _hostPlayerPositionImage.anchorMin;
_hostPlayerOffsetPosition = new Vector2((maxAnchor.x - 0.5f) * mapRectTransform.rect.width, (maxAnchor.y - 0.5f) * mapRectTransform.rect.height);
}
// This triggers automatically when the user clicks/taps on this Image
public void OnPointerClick(PointerEventData eventData)
{
Vector2 localCursorPosition;
// Convert the screen click position to local anchored position inside the Map
bool isConverted = RectTransformUtility.ScreenPointToLocalPointInRectangle(
mapRectTransform,
eventData.position,
eventData.pressEventCamera,
out localCursorPosition
);
if (isConverted)
{
// convert the localCursorPosition to the local position of the host player position image
localCursorPosition -= _hostPlayerOffsetPosition;
dlgWorldMap.OnMapClicked(localCursorPosition);
}
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 876ab9894018f2b4cb4a0b04bf534bc5
+171 -4
View File
@@ -1,5 +1,80 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &191621185379502263
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 9142400375056319150}
- component: {fileID: 7949922842883969624}
- component: {fileID: 6790297770241223980}
m_Layer: 0
m_Name: click_pos
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &9142400375056319150
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 191621185379502263}
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: 7169122999130120872}
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, y: 0}
m_SizeDelta: {x: 15, y: 15}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7949922842883969624
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 191621185379502263}
m_CullTransparentMesh: 1
--- !u!114 &6790297770241223980
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 191621185379502263}
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: 1
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!1 &936441858863998774
GameObject:
m_ObjectHideFlags: 0
@@ -35,7 +110,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: 403.03613, y: 341.03613}
m_AnchoredPosition: {x: 549, y: 465}
m_SizeDelta: {x: 58, y: 58}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6772256914059310038
@@ -286,7 +361,7 @@ MonoBehaviour:
imageProgress: {fileID: 0}
mapImage: {fileID: 1174346096914174862}
_hostPlayerPositionImage: {fileID: 4036230907032538800}
_positionFactor: 2.5
_positionFactor: 1.8
_closeButton: {fileID: 8858186809287203567}
--- !u!1 &8308536083041954008
GameObject:
@@ -363,6 +438,81 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &8900623989843312765
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1510574663178069641}
- component: {fileID: 5533213955690836106}
- component: {fileID: 7414570214724661608}
m_Layer: 0
m_Name: hostplayerpos_debug
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &1510574663178069641
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8900623989843312765}
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: 7169122999130120872}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.518}
m_AnchorMax: {x: 0.5, y: 0.518}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 13, y: 16}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5533213955690836106
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8900623989843312765}
m_CullTransparentMesh: 1
--- !u!114 &7414570214724661608
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8900623989843312765}
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: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 448046091, guid: 99520ceed6182dd408f2da040fe0c033, type: 3}
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!1 &8924313797425690832
GameObject:
m_ObjectHideFlags: 0
@@ -374,6 +524,7 @@ GameObject:
- component: {fileID: 7169122999130120872}
- component: {fileID: 7283747291599937432}
- component: {fileID: 1174346096914174862}
- component: {fileID: 4521997594693838426}
m_Layer: 0
m_Name: MapTexture
m_TagString: Untagged
@@ -394,12 +545,14 @@ RectTransform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 5906545349664091413}
- {fileID: 1510574663178069641}
- {fileID: 9142400375056319150}
m_Father: {fileID: 7323734624486819451}
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, y: 0}
m_SizeDelta: {x: 1024, y: 1024}
m_AnchoredPosition: {x: -6.0056, y: -6.0056}
m_SizeDelta: {x: 1408.0024, y: 1408.0024}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7283747291599937432
CanvasRenderer:
@@ -439,3 +592,17 @@ MonoBehaviour:
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &4521997594693838426
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8924313797425690832}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 876ab9894018f2b4cb4a0b04bf534bc5, type: 3}
m_Name:
m_EditorClassIdentifier:
_hostPlayerPositionImage: {fileID: 5906545349664091413}
dlgWorldMap: {fileID: 135853640611757204}
+445
View File
@@ -0,0 +1,445 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2838129733766203984
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3636302681040170949}
- component: {fileID: 4656951194032224}
m_Layer: 0
m_Name: SFX
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &3636302681040170949
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2838129733766203984}
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_Children:
- {fileID: 2281112146744556387}
m_Father: {fileID: 4292995824318243454}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4656951194032224
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2838129733766203984}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4cfa292fff0815d40b82f32256b3f2cc, type: 3}
m_Name:
m_EditorClassIdentifier:
_moveSoundSource: {fileID: 2796487417538969809}
--- !u!1 &6634120867767479402
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4292995824318243454}
- component: {fileID: 3455241641456728159}
- component: {fileID: 8780839799255791761}
- component: {fileID: 8959945188973705721}
- component: {fileID: 6104956088685348673}
- component: {fileID: 8206320370371461788}
m_Layer: 0
m_Name: Music
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4292995824318243454
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6634120867767479402}
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_Children:
- {fileID: 3636302681040170949}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &3455241641456728159
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6634120867767479402}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ab7ebb437fbd3fb41a7300a381fcab00, type: 3}
m_Name:
m_EditorClassIdentifier:
bgmSource: {fileID: 8780839799255791761}
_bgmMixerGroup: {fileID: 1439606312574676259, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2}
_ambienceMixerGroup: {fileID: 661067059137401939, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2}
_sfxMixerGroup: {fileID: 217038053835239290, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2}
--- !u!82 &8780839799255791761
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6634120867767479402}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_Resource: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 1
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!82 &8959945188973705721
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6634120867767479402}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 0}
m_audioClip: {fileID: 0}
m_Resource: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!81 &6104956088685348673
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6634120867767479402}
m_Enabled: 1
--- !u!114 &8206320370371461788
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6634120867767479402}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f520c80aab01bbc45aa22010a90dfd66, type: 3}
m_Name:
m_EditorClassIdentifier:
_worldMusicDB: {fileID: 11400000, guid: 7602c1f71697aae42a7751212c5144dc, type: 2}
--- !u!1 &9129044928287689905
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2281112146744556387}
- component: {fileID: 2796487417538969809}
m_Layer: 0
m_Name: MoveSoundSource
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2281112146744556387
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9129044928287689905}
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_Children: []
m_Father: {fileID: 3636302681040170949}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!82 &2796487417538969809
AudioSource:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9129044928287689905}
m_Enabled: 1
serializedVersion: 4
OutputAudioMixerGroup: {fileID: 217038053835239290, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2}
m_audioClip: {fileID: 0}
m_Resource: {fileID: 0}
m_PlayOnAwake: 1
m_Volume: 1
m_Pitch: 1
Loop: 0
Mute: 0
Spatialize: 0
SpatializePostEffects: 0
Priority: 128
DopplerLevel: 1
MinDistance: 1
MaxDistance: 500
Pan2D: 0
rolloffMode: 0
BypassEffects: 0
BypassListenerEffects: 0
BypassReverbZones: 0
rolloffCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
- serializedVersion: 3
time: 1
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
panLevelCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
spreadCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 0
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
reverbZoneMixCustomCurve:
serializedVersion: 2
m_Curve:
- serializedVersion: 3
time: 0
value: 1
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2c6625b7f2f4bc2469813fb85db84114
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+2 -1
View File
@@ -866,7 +866,8 @@ namespace BrewMonster
if (CmdNormalAttack(true, false, 0, -1))
{
//AP_ActionEvent(AP_EVENT_MELEEOUTOFRANGE, 1);
// AP_ActionEvent(AP_EVENT_MELEEOUTOFRANGE, 1);
AP.AP_ActionEvent(AP.AP_EVENT_MELEEOUTOFRANGE, 1);
}
}
+22
View File
@@ -1732,5 +1732,27 @@ namespace BrewMonster
return bCanPick;
}
/// <summary>
/// Consume money + SP for learning skill on client side after player confirms.
/// Returns false if resources are not enough at commit time.
/// </summary>
public bool TryConsumeSkillLearnCost(int moneyCost, int spCost)
{
if (moneyCost < 0)
moneyCost = 0;
if (spCost < 0)
spCost = 0;
if ((long)GetMoneyAmount() < moneyCost)
return false;
if (m_BasicProps.iSP < spCost)
return false;
AddMoneyAmount(-moneyCost);
m_BasicProps.iSP = Math.Max(0, m_BasicProps.iSP - spCost);
return true;
}
}
}
+9 -2
View File
@@ -4213,7 +4213,7 @@ namespace BrewMonster
}
else
{
newId = 164;
newId = 0;
}
PlayMoveSound(newId);
@@ -4227,7 +4227,14 @@ namespace BrewMonster
{
if (id == _curMoveSndId) return;
_curMoveSndId = id;
SFXManager.Instance?.PlayMoveSoundAsync(id).Forget();
if(id > 0)
{
SFXManager.Instance?.PlayMoveSoundAsync(id).Forget();
}
else
{
SFXManager.Instance?.StopMoveSoundAsync().Forget();
}
}
/// <summary>
+79 -22
View File
@@ -11,7 +11,9 @@ namespace BrewMonster
{
public string AnimationName;
public bool IsForceStopPrevious;
public int ITransTime;
public CECAttackEvent AttackEvent;
public bool IsLoop;
}
public class PlayerVisual : MonoBehaviour
{
@@ -31,10 +33,15 @@ namespace BrewMonster
private const float FadeTime = 100;
private const FadeMode FadeMode = Animancer.FadeMode.FixedDuration;
QueueActionEvent queueActionEvent;
private string previousAnimationName;
private void PlayActionEventHandler(PlayActionEvent @event)
{
//when this trigger, clear all the animation in the queue which in the same layer of animancer
//prevent enqueue the same loop animation
bool loopcheck = @event.IsLoop == true && previousAnimationName == @event.AnimationName;
if(loopcheck)
{
return;
}
if (_animationQueue.Count > 0)
{
_animationQueue.Enqueue(new AnimationQueue
@@ -46,7 +53,8 @@ namespace BrewMonster
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
return;
}
InternalPlayAnimation(@event.AnimationName, @event.ITransTime);
previousAnimationName = @event.AnimationName;
InternalPlayAnimation(@event.AnimationName, @event.ITransTime, FadeMode, @event.IsLoop);
ApplyAttackSignalOnAnimationEnd(@event.AttackEvent);
}
public void InitPlayerEventDoneHandler()
@@ -133,15 +141,24 @@ namespace BrewMonster
}
public bool EnqueueAnimation(QueueActionEvent @event)
{
if (namedAnimancer == null) return false;
if (namedAnimancer == null)
{
return false;
}
if(previousAnimationName == @event.AnimationName)
{
return false;
}
previousAnimationName = @event.AnimationName;
_animationQueue.Enqueue(new AnimationQueue
{
AnimationName = @event.AnimationName,
IsForceStopPrevious = @event.IsForceStopPrevious,
AttackEvent = null
ITransTime = @event.ITransTime,
AttackEvent = @event.AttackEvent,
IsLoop = @event.IsLoop
});
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
if (!isHit)
{
queueActionEvent = @event;
@@ -149,23 +166,51 @@ namespace BrewMonster
}
return true;
}
/// <summary>
/// This function is used to enqueue an animation for looping when the animancer is not set to looping
/// </summary>
/// <param name="animationName"></param>
/// <returns></returns>
private bool EnqueueAnimationForLooping(string animationName)
{
if (namedAnimancer == null)
{
return false;
}
//prevent call if these is a animation already in the queue
if(_animationQueue.Count > 0)
{
return false;
}
_animationQueue.Enqueue(new AnimationQueue
{
AnimationName = animationName,
IsForceStopPrevious = false,
AttackEvent = null,
IsLoop = true
});
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
return true;
}
private void PlayNext()
{
if (_animationQueue.Count == 0)
{
return;
}
else
{
string animationQueueString = "";
foreach(var animation in _animationQueue)
{
animationQueueString += animation.AnimationName + ", ";
}
}
// if (_currentState == null)
// {
// _animationQueue.Dequeue();
// return;
// }
//peek next if IsForceStopPrevious is true, force end
if (_animationQueue.Peek().IsForceStopPrevious)
{
Debug.Log($" InternalPlayAnimation PlayNext: Force Stop Previous");
_currentState.Stop();
_currentState?.Stop();
_currentState = null;
}
if (_currentState != null && _currentState.NormalizedTime < 1f) return;
@@ -175,7 +220,8 @@ namespace BrewMonster
}
var animationQueue = _animationQueue.Dequeue();
_animationList = _animationQueue.Select(q => q.AnimationName).ToList();
InternalPlayAnimation(animationQueue.AnimationName);
previousAnimationName = animationQueue.AnimationName;
InternalPlayAnimation(animationQueue.AnimationName, animationQueue.ITransTime, FadeMode, animationQueue.IsLoop);
ApplyAttackSignalOnAnimationEnd(animationQueue.AttackEvent);
}
private void ApplyAttackSignalOnAnimationEnd(CECAttackEvent attackEvent)
@@ -192,7 +238,10 @@ namespace BrewMonster
}
void ApplyDamage()
{
if (queueActionEvent == null) return;
if (queueActionEvent == null)
{
return;
}
isHit = false;
queueActionEvent.SetFlag(true, queueActionEvent.AttackEvent);
queueActionEvent = null;
@@ -203,7 +252,8 @@ namespace BrewMonster
}
public bool IsAnimationExist(string animationName)
{
return namedAnimancer.States.TryGet("ActionName", out var existingState) ? true : false;
var exists = namedAnimancer.States.TryGet("ActionName", out var existingState) ? true : false;
return exists;
}
private string _currentAnimationName;
@@ -213,19 +263,26 @@ namespace BrewMonster
/// <param name="animationName"></param>
/// <param name="duration"></param>
/// <param name="fadeMode"></param>
private void InternalPlayAnimation(string animationName, float duration = FadeTime, FadeMode fadeMode = FadeMode)
private void InternalPlayAnimation(string animationName, float duration = FadeTime, FadeMode fadeMode = FadeMode, bool isLoop = false)
{
if (namedAnimancer == null)
{
return;
}
bool isState = namedAnimancer.States.TryGet(animationName, out var existingState) ? true : false;
if (isState)
{
_currentState = namedAnimancer.TryPlay(animationName, duration / 1000, fadeMode);
_currentAnimationName = animationName;
//Debug.Log($"InternalPlayAnimation: removeShapeName 1 TriggerName={removeShapeName}");
//if the animation is looping and the current state is not looping, play the animation again
if(isLoop == true && _currentState.IsLooping == false)
{
_currentState.Time = 0;
_currentState.Events.OnEnd = () => EnqueueAnimationForLooping(animationName);
}
return;
}
BMLogger.LogError($"Null name animation: {animationName}");
//BMLogger.LogError($"Null name animation: {animationName}");
}
/// <summary>
+35 -2
View File
@@ -5,6 +5,7 @@ using BrewMonster.Scripts;
using UnityEngine;
using UnityEngine.UI;
using BrewMonster;
using System.Collections;
namespace BrewMonster.UI
{
@@ -25,6 +26,9 @@ namespace BrewMonster.UI
private Action<RoleInfo> _onCreateCharacterComplete;
private List<RoleInfo> _roleInfos;
private Coroutine _showModelReadyCoroutine;
private int _pendingShowModelRoleId = -1;
private void OnEnable()
{
_btnEnterGame.onClick.AddListener(OnClickedEnterGame);
@@ -149,8 +153,37 @@ namespace BrewMonster.UI
_selectingCharacterItemUI.SetFocus(true);
_btnEnterGame.gameObject.SetActive(true);
if (playerModelPreview != null && characterItemUI.RoleInfo != null)
playerModelPreview.ShowPlayerModel(characterItemUI.RoleInfo.roleid);
if (playerModelPreview == null || characterItemUI.RoleInfo == null)
{
return;
}
_pendingShowModelRoleId = characterItemUI.RoleInfo.roleid;
if (_showModelReadyCoroutine != null)
{
StopCoroutine(_showModelReadyCoroutine);
}
_showModelReadyCoroutine = StartCoroutine(ShowSelectedModelWhenReady(_pendingShowModelRoleId));
}
private IEnumerator ShowSelectedModelWhenReady(int roleId)
{
while (isActiveAndEnabled && playerModelPreview != null)
{
if (_pendingShowModelRoleId != roleId)
{
yield break; // Role changed, stop this coroutine
}
if (playerModelPreview.playerModelIds != null && playerModelPreview.playerModelIds.Contains(roleId))
{
playerModelPreview.ShowPlayerModel(roleId);
_showModelReadyCoroutine = null;
yield break; // Model is ready, show it and stop this coroutine
}
yield return null;
}
_showModelReadyCoroutine = null;
}
private void OnCreateCharacterClicked()