Handle logic of Duel
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -51,6 +51,7 @@ namespace BrewMonster
|
||||
public void Init(info_player_1 Info, int iAppearFlag)
|
||||
{
|
||||
SetUpPlayer();
|
||||
m_iCID = (int)CECObject.Class_ID.OCID_ELSEPLAYER;
|
||||
m_dwResFlags = (uint)PlayerResourcesReadyFlag.RESFG_ALL;
|
||||
m_pEPWorkMan = new CECEPWorkMan(this);
|
||||
|
||||
|
||||
@@ -205,6 +205,13 @@ public partial class CECGameRun
|
||||
CECPlayer.InitStaticRes();
|
||||
hostPlayer = ObjectSpawner.Instance.InstantiateObject(_playerPrefab, setThisAsParent: true).AddComponent<CECHostPlayer>();
|
||||
hostPlayer.InitCharacter(info);
|
||||
|
||||
if (hostPlayer != null)
|
||||
{
|
||||
var t = Type.GetType("BrewMonster.UI.SelectedTargetHUDController, Assembly-CSharp");
|
||||
if (t != null && hostPlayer.GetComponent(t) == null)
|
||||
hostPlayer.gameObject.AddComponent(t);
|
||||
}
|
||||
}
|
||||
public CECMonster GetMonster()
|
||||
{
|
||||
|
||||
+154
-23
@@ -622,6 +622,7 @@ namespace BrewMonster
|
||||
case int value when value == EC_MsgDef.MSG_HST_NEWTEAMMEM: OnMsgHstNewTeamMem(Msg); break;
|
||||
case int value when value == EC_MsgDef.MSG_HST_TEAMMEMBERDATA: OnMsgHstTeamMemberData(Msg); break;
|
||||
case int value when value == EC_MsgDef.MSG_HST_CONTINUECOMBOSKILL: OnMsgContinueComboSkill(Msg); break;
|
||||
case int value when value == EC_MsgDef.MSG_PM_DUELOPT: OnMsgHstDuelOpt(Msg); break;
|
||||
}
|
||||
|
||||
/*if (bActionStartSkill)
|
||||
@@ -714,6 +715,76 @@ namespace BrewMonster
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>Update host duel state from S2C duel commands (MSG_PM_DUELOPT).</summary>
|
||||
private void OnMsgHstDuelOpt(ECMSG Msg)
|
||||
{
|
||||
int cmdId = Convert.ToInt32(Msg.dwParam2);
|
||||
byte[] data = Msg.dwParam1 as byte[];
|
||||
int idOpp = 0;
|
||||
if (data != null && data.Length >= 4)
|
||||
idOpp = BitConverter.ToInt32(data, 0);
|
||||
switch (cmdId)
|
||||
{
|
||||
case CommandID.DUEL_PREPARE:
|
||||
m_pvp.iDuelState = Duel_state.DUEL_ST_PREPARE;
|
||||
m_pvp.idDuelOpp = idOpp;
|
||||
m_pvp.iDuelTimeCnt = data != null && data.Length >= 8 ? BitConverter.ToInt32(data, 4) : 3000;
|
||||
break;
|
||||
case CommandID.HOST_DUEL_START:
|
||||
m_pvp.iDuelState = Duel_state.DUEL_ST_INDUEL;
|
||||
m_pvp.idDuelOpp = idOpp;
|
||||
m_pvp.iDuelTimeCnt = 0;
|
||||
// Origin: server must be notified of force-attack (PVP) so it accepts attacks on the duel opponent
|
||||
NotifyServerForceAttack(true);
|
||||
break;
|
||||
case CommandID.DUEL_STOP:
|
||||
case CommandID.DUEL_RESULT:
|
||||
m_pvp.iDuelState = Duel_state.DUEL_ST_STOPPING;
|
||||
m_pvp.iDuelTimeCnt = data != null && data.Length >= 8 ? BitConverter.ToInt32(data, 4) : 3000;
|
||||
if (data != null && data.Length >= 12)
|
||||
m_pvp.iDuelRlt = BitConverter.ToInt32(data, 8);
|
||||
NotifyServerForceAttack(false);
|
||||
break;
|
||||
case CommandID.DUEL_CANCEL:
|
||||
case CommandID.DUEL_REJECT_REQUEST:
|
||||
m_pvp.iDuelState = Duel_state.DUEL_ST_NONE;
|
||||
m_pvp.idDuelOpp = 0;
|
||||
m_pvp.iDuelTimeCnt = 0;
|
||||
NotifyServerForceAttack(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Called when MSG_PM_PLAYERDUELOPT (229) is received; server may send duel start to both participants. If host is one of the two ids, set duel state.</summary>
|
||||
public void OnMsgPlayerDuelStart(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length < 8) return;
|
||||
int id1 = BitConverter.ToInt32(data, 0);
|
||||
int id2 = BitConverter.ToInt32(data, 4);
|
||||
int cid = m_PlayerInfo.cid;
|
||||
if (cid == id1)
|
||||
{
|
||||
m_pvp.iDuelState = Duel_state.DUEL_ST_INDUEL;
|
||||
m_pvp.idDuelOpp = id2;
|
||||
m_pvp.iDuelTimeCnt = 0;
|
||||
NotifyServerForceAttack(true);
|
||||
}
|
||||
else if (cid == id2)
|
||||
{
|
||||
m_pvp.iDuelState = Duel_state.DUEL_ST_INDUEL;
|
||||
m_pvp.idDuelOpp = id1;
|
||||
m_pvp.iDuelTimeCnt = 0;
|
||||
NotifyServerForceAttack(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Origin: notify server of force-attack (PVP) state so it accepts/rejects attacks on players. Call when duel starts (true) or ends (false).</summary>
|
||||
private void NotifyServerForceAttack(bool bForceAttack)
|
||||
{
|
||||
byte refuseBless = EC_Utility.glb_BuildRefuseBLSMask();
|
||||
UnityGameSession.c2s_SendCmdNotifyForceAttack(glb_BuildPVPMask(bForceAttack), refuseBless);
|
||||
}
|
||||
|
||||
private void OnMsgContinueComboSkill(ECMSG Msg)
|
||||
{
|
||||
bool bMeleeing = ((int)Msg.dwParam1 == 1);
|
||||
@@ -2604,10 +2675,50 @@ namespace BrewMonster
|
||||
{
|
||||
case CommandID.OWN_ITEM_INFO:
|
||||
{
|
||||
Debug.Log("[Inventory] OWN_ITEM_INFO received");
|
||||
var data = Msg.dwParam1 as byte[];
|
||||
int hostId = Convert.ToInt32(Msg.dwParam3);
|
||||
LogInventoryPacket("OWN_ITEM_INFO", data, hostId);
|
||||
|
||||
// Parse and apply single-item update (e.g. after embed) so UI reflects new item info without relog
|
||||
if (data != null && data.Length >= 22)
|
||||
{
|
||||
byte byPackage = data[0];
|
||||
byte bySlot = data[1];
|
||||
int tid = BitConverter.ToInt32(data, 2);
|
||||
int expireDate = BitConverter.ToInt32(data, 6);
|
||||
int state = BitConverter.ToInt32(data, 10);
|
||||
uint count = BitConverter.ToUInt32(data, 14);
|
||||
ushort crc = BitConverter.ToUInt16(data, 18);
|
||||
ushort contentLength = BitConverter.ToUInt16(data, 20);
|
||||
byte[] content = null;
|
||||
if (contentLength > 0 && data.Length >= 22 + contentLength)
|
||||
{
|
||||
content = new byte[contentLength];
|
||||
Buffer.BlockCopy(data, 22, content, 0, contentLength);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var item = EC_IvtrItem.CreateItem(tid, expireDate, (int)count);
|
||||
item.Package = byPackage;
|
||||
item.Slot = bySlot;
|
||||
item.State = state;
|
||||
item.Crc = crc;
|
||||
item.Content = content;
|
||||
|
||||
var inv = GetInventory(byPackage);
|
||||
if (inv != null)
|
||||
{
|
||||
inv.SetItem(bySlot, item);
|
||||
if (byPackage == InventoryConst.IVTRTYPE_EQUIPPACK)
|
||||
UpdateEquipSkins();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[Inventory] OWN_ITEM_INFO apply failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2979,6 +3090,7 @@ namespace BrewMonster
|
||||
public override void SetUpPlayer()
|
||||
{
|
||||
base.SetUpPlayer();
|
||||
m_iCID = (int)CECObject.Class_ID.OCID_HOSTPLAYER;
|
||||
|
||||
m_IncantCnt = new CECCounter();
|
||||
m_IncantCnt.SetPeriod(1000);
|
||||
@@ -3234,8 +3346,6 @@ namespace BrewMonster
|
||||
|
||||
//if (!EC_Game.GetGameRun().GetWorld().GetObject(idTarget, 1))
|
||||
// return false;
|
||||
bool bStartNewWork = false;
|
||||
|
||||
bool bUseAutoPF = false;
|
||||
//CECPlayerWrapper* pWrapper = CECAutoPolicy::GetInstance().GetPlayerWrapper();
|
||||
//if (CECAutoPolicy::GetInstance().IsAutoPolicyEnabled() && pWrapper.GetAttackError() >= 2)
|
||||
@@ -3253,12 +3363,10 @@ namespace BrewMonster
|
||||
return false; // Host is attacking the target
|
||||
|
||||
pWorkTrace = (CECHPWorkTrace)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT);
|
||||
bStartNewWork = true;
|
||||
}
|
||||
else if (m_pWorkMan.CanStartWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT))
|
||||
{
|
||||
pWorkTrace = (CECHPWorkTrace)m_pWorkMan.CreateWork(CECHPWork.Host_work_ID.WORK_TRACEOBJECT);
|
||||
bStartNewWork = true;
|
||||
}
|
||||
|
||||
if (pWorkTrace != null)
|
||||
@@ -3268,14 +3376,20 @@ namespace BrewMonster
|
||||
bUseAutoPF);
|
||||
pWorkTrace.SetMoveCloseFlag(bMoreClose);
|
||||
|
||||
if (bStartNewWork)
|
||||
m_pWorkMan.StartWork_p1(pWorkTrace);
|
||||
// Always start/re-start so trace runs (reuse path only updated target; work may be queued not current)
|
||||
m_pWorkMan.StartWork_p1(pWorkTrace);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Use host's m_pvp (we update it from S2C duel packets). Base IsInDuel() reads CECPlayer.m_pvp which is never set.</summary>
|
||||
public new bool IsInDuel() { return m_pvp.iDuelState == Duel_state.DUEL_ST_INDUEL; }
|
||||
|
||||
/// <summary>Duel opponent character id when in duel; 0 otherwise. Used by HPWork so trace/melee send correct PVP mask.</summary>
|
||||
public int GetDuelOpponentId() { return m_pvp.idDuelOpp; }
|
||||
|
||||
public int AttackableJudge(int idTarget, bool bForceAttack)
|
||||
{
|
||||
if (CannotAttack())
|
||||
@@ -3368,19 +3482,22 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
}
|
||||
// TO DO: fix later
|
||||
//else if (GPDataTypeHelper.ISPLAYERID(idTarget))
|
||||
//{
|
||||
// // Check duel at first
|
||||
// if (m_pvp.iDuelState == Duel_state.DUEL_ST_INDUEL && m_pvp.idDuelOpp == idTarget)
|
||||
// return 1;
|
||||
// else if (m_pvp.iDuelState == Duel_state.DUEL_ST_STOPPING && m_pvp.idDuelOpp == idTarget)
|
||||
// return 0;
|
||||
else if (GPDataTypeHelper.ISPLAYERID(idTarget))
|
||||
{
|
||||
// Duel: can attack only duel opponent while in duel (origin: attack but not kill)
|
||||
if (m_pvp.iDuelState == Duel_state.DUEL_ST_INDUEL && m_pvp.idDuelOpp == idTarget)
|
||||
return 1;
|
||||
if (m_pvp.iDuelState == Duel_state.DUEL_ST_STOPPING && m_pvp.idDuelOpp == idTarget)
|
||||
return 0;
|
||||
|
||||
// // In sanctuary we cannot attack other players
|
||||
// if (m_bInSanctuary)
|
||||
// return 0;
|
||||
// In sanctuary we cannot attack other players
|
||||
if (m_bInSanctuary)
|
||||
return 0;
|
||||
|
||||
// Other player: no attack when not in duel (full PVP/free PVP/battle camp can be ported later)
|
||||
iRet = 0;
|
||||
}
|
||||
// TO DO: full PVP branch ported from origin (commented below)
|
||||
// //ASSERT(pObject.GetClassID() == CECObject::OCID_ELSEPLAYER);
|
||||
// EC_ElsePlayer pPlayer = (EC_ElsePlayer)pObject;
|
||||
// ROLEBASICPROP bp = pPlayer.GetBasicProps();
|
||||
@@ -4025,6 +4142,10 @@ namespace BrewMonster
|
||||
if (!pSkill.ReadyToCast())
|
||||
return false;
|
||||
|
||||
// Duel: so trace and CastSkill send correct PVP mask when touching duel opp
|
||||
if (IsInDuel() && idCastTarget == m_pvp.idDuelOpp)
|
||||
bForceAttack = true;
|
||||
|
||||
if (CECCastSkillWhenMove.Instance.IsSkillSupported(pSkill.GetSkillID(), this) &&
|
||||
m_pWorkMan.IsMovingToPosition() &&
|
||||
m_pWorkMan.CanCastSkillImmediately(pSkill.GetSkillID()))
|
||||
@@ -4301,6 +4422,10 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
|
||||
// Duel: server accepts attack/skill on player only with PVP/force mask
|
||||
if (IsInDuel() && idTarget == m_pvp.idDuelOpp)
|
||||
bForceAttack = true;
|
||||
|
||||
//TODO: Check cast condition - method not yet implemented
|
||||
int iRet = CheckSkillCastCondition(m_pPrepSkill);
|
||||
if (iRet != 0)
|
||||
@@ -4687,7 +4812,6 @@ namespace BrewMonster
|
||||
|
||||
public bool SelectTarget(int idTarget)
|
||||
{
|
||||
//BMLogger.LogError("HoangDev: HostPlayer SelectTarget");
|
||||
bool bRet = false;
|
||||
bool canDo = CanDo(ActionCanDo.CANDO_CHANGESELECT);
|
||||
bool canselect = CanSelectTarget(idTarget);
|
||||
@@ -4696,16 +4820,17 @@ namespace BrewMonster
|
||||
bRet = true;
|
||||
if (idTarget == 0)
|
||||
{
|
||||
//BMLogger.LogError("HoangDev: HostPlayer Unsetlect npc");
|
||||
UnityGameSession.c2s_CmdUnselect();
|
||||
m_idSelTarget = 0;
|
||||
m_idUCSelTarget = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//BMLogger.LogError("HoangDev: HostPlayer setlect npc");
|
||||
UnityGameSession.c2s_CmdSelectTarget(idTarget);
|
||||
m_idSelTarget = idTarget;
|
||||
m_idUCSelTarget = idTarget;
|
||||
}
|
||||
}
|
||||
|
||||
return bRet;
|
||||
}
|
||||
|
||||
@@ -6546,7 +6671,10 @@ namespace BrewMonster
|
||||
MaxHealth = maxHealth;
|
||||
IDNPC = idnpc;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Fired when selected target HUD should be hidden (deselect or switch).</summary>
|
||||
public struct TargetHUDClearEvent { }
|
||||
|
||||
public struct GNDINFO
|
||||
{
|
||||
@@ -7696,6 +7824,9 @@ namespace BrewMonster
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Duel: server accepts skill on opponent only with PVP/force mask
|
||||
if (IsInDuel() && m_idSelTarget == m_pvp.idDuelOpp)
|
||||
bForceAttack = true;
|
||||
|
||||
int idCastTarget = m_idSelTarget;
|
||||
int iTargetType = pSkill.GetTargetType();
|
||||
|
||||
@@ -21,6 +21,7 @@ public class CECUIManager : MonoSingleton<CECUIManager>
|
||||
[SerializeField] private int currentTargetNPCID;
|
||||
CECGameUIMan gameUI;
|
||||
AUIManager aUIManager;
|
||||
AUIDialog _dlgPlayerOptions;
|
||||
[SerializeField] private DialogScriptTableObject dialogResouce;
|
||||
[SerializeField] private Canvas canvasDlg;
|
||||
|
||||
@@ -37,6 +38,7 @@ public class CECUIManager : MonoSingleton<CECUIManager>
|
||||
base.Awake();
|
||||
|
||||
EventBus.Subscribe<CECHostPlayer.NPCINFO>(ShowUINPC);
|
||||
EventBus.Subscribe<CECHostPlayer.TargetHUDClearEvent>(OnTargetHUDClear);
|
||||
EventBus.Subscribe<NPCDiedEvent>(TryHideUINPC);
|
||||
EventBus.Subscribe<MessageBoxEvent>(OnMessageBox);
|
||||
|
||||
@@ -61,6 +63,7 @@ public class CECUIManager : MonoSingleton<CECUIManager>
|
||||
private void OnDestroy()
|
||||
{
|
||||
EventBus.Unsubscribe<CECHostPlayer.NPCINFO>(ShowUINPC);
|
||||
EventBus.Unsubscribe<CECHostPlayer.TargetHUDClearEvent>(OnTargetHUDClear);
|
||||
EventBus.Unsubscribe<NPCDiedEvent>(TryHideUINPC);
|
||||
EventBus.Unsubscribe<MessageBoxEvent>(OnMessageBox);
|
||||
}
|
||||
@@ -68,10 +71,17 @@ public class CECUIManager : MonoSingleton<CECUIManager>
|
||||
private void ShowUINPC(CECHostPlayer.NPCINFO obj)
|
||||
{
|
||||
npsUI.gameObject.SetActive(true);
|
||||
npsUI.SetText($"{obj.CurrentHealth}/{obj.MaxHealth}", obj.Name, "");
|
||||
npsUI.SetHealthImage((float)obj.CurrentHealth / (float)obj.MaxHealth);
|
||||
string hpText = obj.MaxHealth > 0 ? $"{obj.CurrentHealth}/{obj.MaxHealth}" : "-/-";
|
||||
npsUI.SetText(hpText, obj.Name ?? "", "");
|
||||
float fill = obj.MaxHealth > 0 ? (float)obj.CurrentHealth / (float)obj.MaxHealth : 1f;
|
||||
npsUI.SetHealthImage(fill);
|
||||
currentTargetNPCID = obj.IDNPC;
|
||||
}
|
||||
|
||||
private void OnTargetHUDClear(CECHostPlayer.TargetHUDClearEvent _)
|
||||
{
|
||||
npsUI.gameObject.SetActive(false);
|
||||
}
|
||||
public CDlgQuickBar GetCDlgQuickBar()
|
||||
{
|
||||
return m_pDlgQuickBar1;
|
||||
@@ -394,6 +404,16 @@ public class CECUIManager : MonoSingleton<CECUIManager>
|
||||
return gameUI;
|
||||
}
|
||||
|
||||
/// <summary>Shows the player options menu for the given character. Requires a prefab with id "DlgPlayerOptions" in DialogScriptTableObject.</summary>
|
||||
public void ShowPlayerOptionsDialog(int characterId)
|
||||
{
|
||||
if (characterId == 0 || canvasDlg == null) return;
|
||||
var gui = GetInGameUIMan();
|
||||
if (_dlgPlayerOptions == null)
|
||||
_dlgPlayerOptions = gui.GetDialog("DlgPlayerOptions");
|
||||
_dlgPlayerOptions?.ShowForPlayer(characterId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current target NPC ID (same as stored from NPCINFO event)
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user