Files
Sun1602/Server/GameServer/World/SSQ/GameSSQControlManagerImpl.cpp
2022-10-26 12:25:11 +08:00

1270 lines
42 KiB
C++

#include "stdafx.h"
#include <PacketStruct_AG.h>
#include <NPCInfoParser.h>
#include "GameInstanceDungeon.h"
#include "ItemManager.h"
#include "ItemExpDistributor.h"
#include "StatusManager.h"
#include "TradeManager.h"
#include "SummonManager.h"
#include "MovingPlanner.h"
#include <World/GameWarControlManager.h>
//_NA_001990_ACSYSTEM_ADD
#include "AchievementManager.h"
// for debugging control
#undef WAVERIX_DEBUGGING_OUTPUTn
#undef WAVERIX_DEBUGGING_OUTPUTnV
#define WAVERIX_DEBUGGING_OUTPUTn(msg) \
SUNLOG(eCRITICAL_LOG, "[%s][%u][%u] %s\n", __FUNCTION__, __LINE__, \
(DWORD)((GetTickCount()-tickvalue)/(1000)), msg); \
tickvalue = GetTickCount()
#define WAVERIX_DEBUGGING_OUTPUTnV(value) \
{ CHAR _message[10]; \
WAVERIX_DEBUGGING_OUTPUTn(itoa(value, _message, sizeof(_message))); }
static DWORD tickvalue = GetTickCount();
#include "GameSSQControlInfos.hxx"
#include "GameSSQPacketProcess.hxx"
#include "GameSSQUpdateCtrl.hxx"
#include "GameSSQControlManagerImpl.hxx"
#include "SSQHandlerInGame.h"
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// <GameSSQControlManagerImpl><Implementation>
//
GameSSQControlManagerImpl::GameSSQControlManagerImpl()
{
const eSERVER_TYPE serverType = g_pGameServer->GetServerType();
serverType == BATTLE_SERVER
? Control.RegisterBattleServerFunction()
: Control.RegisterFieldServerFunction()
;
}
GameSSQControlManagerImpl::~GameSSQControlManagerImpl()
{
}
//────────────────────────────────────────────────────────────────
namespace GameSSQControlInfo
{
//struct OpSSQInfoController
//{
// typedef SSQInfoParser::SSQINFO_PAIR SSQINFO_PAIR;
// GameSSQControlManagerImpl* const m_pSSQImpl;
// sTIME_INFO m_TimeInfo;
// OpSSQInfoController(GameSSQControlManagerImpl* const pImpl) : m_pSSQImpl(pImpl)
// {
// m_TimeInfo.CurTick = GetTickCount();
// util::TimeSync::GetLocalTime(&m_TimeInfo.SysTime);
// m_TimeInfo.WzTime = m_TimeInfo.SysTime.wHour*100 + m_TimeInfo.SysTime.wMinute;
// }
// VOID Register(const SSQINFO_PAIR& rPair) const
// {
// const INDEX_T IDofSSQ = rPair.first;
// SSQInfo* const pSSQInfo = rPair.second;
// GameSSQUpdateCtrl_None* const pCtrl = GameSSQUpdateCtrl_None_Info::Allocate();
// SSQMsgRouterInGame* const pRouter = router_info::Allocate();
// pCtrl->MigrationDefault(IDofSSQ, m_pSSQImpl, pRouter, pSSQInfo);
// pCtrl->Init();
// m_pSSQImpl->_RegisterUpdateCtrl(IDofSSQ, pCtrl);
// }
//};
};
//────────────────────────────────────────────────────────────────
SSQ_UPDATERS_IT
GameSSQControlManagerImpl::_FindCodeOfSSQ(const SSQCODE CodeOfSSQ)
{
SSQ_UPDATERS_IT itFind =
std::find_if(UpdateList.begin(), UpdateList.end()
, boost::bind(std::equal_to<SSQCODE>()
, boost::bind(&SSQ_UPDATER_NODE::GetCodeOfSSQ, _1)
, CodeOfSSQ
));
return itFind;
}
SSQ_UPDATERS_IT
GameSSQControlManagerImpl::_FindZoneKeyofSSQ(const KEYTYPE KeyOfSSQ)
{
SSQ_UPDATERS_IT itFind =
std::find_if(UpdateList.begin(), UpdateList.end()
, boost::bind(std::equal_to<KEYTYPE>()
, boost::bind(&SSQ_UPDATER_NODE::GetKey, _1)
, KeyOfSSQ
));
return itFind;
}
//────────────────────────────────────────────────────────────────
VOID
GameSSQControlManagerImpl::_RegisterUpdateCtrl(const SSQCODE CodeOfSSQ, GameSSQUpdateCtrl* const pCtrl)
{
SSQ_UPDATERS_IT itFind = _FindCodeOfSSQ(CodeOfSSQ);
if(itFind != UpdateList.end())
{
SSQ_UPDATER_NODE& rNode = *itFind;
rNode.pUpdater = pCtrl;
}
else
{
ASSERT(!"Impossible");
}
}
//────────────────────────────────────────────────────────────────
VOID
GameSSQControlManagerImpl::_InitializeUpdateCtrl()
{
SSQInfoParser* const pSSQInfoParser = SSQInfoParser::Instance();
sTIME_INFO timeInfo;
sTIME_INFO::SetTimeValue(timeInfo);
typedef SSQInfoParser::SSQINFO_TABLE SSQINFO_TABLE;
const SSQINFO_TABLE& rTable = pSSQInfoParser->GetRefSSQInfo();
FOREACH_CONTAINER(const SSQINFO_TABLE::value_type& rNode, rTable, SSQINFO_TABLE)
{
const SSQCODE CodeOfSSQ = rNode.first;
SSQInfo* const pSSQInfo = rNode.second;
GameSSQUpdateCtrl_None* const pCtrl = TAllocNew(GameSSQUpdateCtrl_None_Info::value_type);
SSQMsgRouterInGame* const pRouter = TAllocNew(router_info::value_type);
pCtrl->MigrationDefault(CodeOfSSQ, this, pRouter, pSSQInfo);
pCtrl->Init();
GameSSQUpdateCtrl::ConstVarsInit(pCtrl);
//_RegisterUpdateCtrl(IDofSSQ, pCtrl); <- 사용상 혼재로 코드를 이곳으로 옮겨버렸다.
SSQ_UPDATERS_IT itFind = _FindCodeOfSSQ(CodeOfSSQ);
ASSERT(itFind == UpdateList.end());
UpdateList.push_back(SSQ_UPDATER_NODE(CodeOfSSQ, pCtrl));
}
//OpSSQInfoController opr(this);
//pSSQInfoParser->ForeachSSQInfoBin(&OpSSQInfoController::Register, opr);
pSSQInfoParser->_LoadWat("./data/Map/Object/map_event01/map_event01.wat", false);
}
//────────────────────────────────────────────────────────────────
VOID
GameSSQControlManagerImpl::_ReleaseUpdateCtrl()
{
FOREACH_CONTAINER(const SSQ_UPDATER_NODE& rNode, UpdateList, SSQ_UPDATERS)
{
if(!rNode.pUpdater)
continue;
GameSSQUpdateCtrl* const pCtrl = rNode.pUpdater;
GameSSQUpdateCtrl::ConstVarsRelease(pCtrl);
TAllocDelete(router_info::value_type, pCtrl->m_pMsgParser);
switch(pCtrl->m_CurType)
{
#undef SSQ_UPDATE_CTRL_NODE
#define SSQ_UPDATE_CTRL_NODE(_type) \
case _type::value: TAllocDelete(_type::value_type, pCtrl); break;
SSQ_UPDATE_CTRL_LIST();
}
}
}
//────────────────────────────────────────────────────────────────
VOID
GameSSQControlManagerImpl::_ChangeUpdater(const SSQ_UPDATER_NODE& rNode)
{
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// <Internal Message Handler Implementation>
//
VOID
GameSSQControlManagerImpl::_OnDead(GameSSQUpdateCtrl* const pUpdateCtrl, Character* const pTarget, Character* const pKiller)
{
const DWORD ERRCODE_DEFAULT = 100;
// 포인트 계산
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FLOWCONTROL_START()
{
if(!FlowControl::FCAssert(pTarget))
break;
const eOBJECT_TYPE object_type_of_target = pTarget->GetObjectType();
const bool target_isPlayer = (object_type_of_target&PLAYER_OBJECT)==PLAYER_OBJECT;
const bool target_isSSQMob = (object_type_of_target&SSQMONSTER_OBJECT)==SSQMONSTER_OBJECT;
const SSQInfo* const pSSQInfo = pUpdateCtrl->GetSSQInfo();
// 기타 npc, 소환수 등등일 수도 있다... 있나? 그런 경우 스킵
if(!(target_isPlayer ^ target_isSSQMob))
break;
BOOLEAN need_update = FALSE; // reward 처리 여부, 죽음 처리 여부 (부활 상황 금지)
SSQCharacterStatusInGame* pControlStatus = 0;
if(target_isPlayer)
{ // 뭔가의 이유로 죽어버릴 수 있다.
Player* const pDeadPlayer = static_cast<Player*>(pTarget);
pControlStatus = &pDeadPlayer->SSQHandler->ControlStatus;
}
else
{
SSQMonster* const pDeadMob = static_cast<SSQMonster*>(pTarget);
pControlStatus = &pDeadMob->ControlStatus;
}
SSQCharacterStatusInGame& rStatus = *pControlStatus;
need_update = !(rStatus.FIELD & rStatus.CHECKED_DEAD);
if(need_update)
{
rStatus.AddField(rStatus.CHECKED_DEAD); // for sync
pUpdateCtrl->SharedVars.UpdateDecreaseCount(target_isPlayer);
}
const eTIME_TYPE updater_type = pUpdateCtrl->GetTimeType();
if(updater_type != GameSSQControlInfo::TT_BATTLE)
break;
//////////////////////////////////////////////////////////////////////////
// Target Leave Process <- 배틀시점에 내보낼...까?
if(need_update && target_isPlayer)
{
Player* const pDeadPlayer = static_cast<Player*>(pTarget);
ISSQHandlerInGame& rHandler = pDeadPlayer->SSQHandler;
rHandler->ConfigEvent_OnDead(pDeadPlayer, SSQHandlerInGame::LeaveTimeout_OnDead);
}
//////////////////////////////////////////////////////////////////////////
// Killer Process
// 이후 처리는 Killer가 Player인 경우에 처리하기로 한다. Killer가 없는 경우도 마찬가지.
const bool killer_isPlayer = pKiller && pKiller->IsEqualObjectKind(PLAYER_OBJECT);
if(!killer_isPlayer)
break;
Player* const pKillerPlayer = static_cast<Player*>(pKiller);
ISSQHandlerInGame& rHandler = pKillerPlayer->SSQHandler;
if(need_update)
{
//////////////////////////////////////////////////////////////////////////
// Point Process
SSQ_RESERVED_REWARD_INFO::QuestFail rPoint;
if(!rHandler->GetInfo_QuestFail(rPoint))
{
// 에러 처리 해야 하긴 해야 하는데....
break;
}
const DWORD kills_of_mob = (!!target_isSSQMob) + rPoint.MonsterKillNum;
const DWORD kills_of_player = (!!target_isPlayer) + rPoint.PlayerKillNum;
rHandler->UpdateInfo_PlayPoint(0, 0, (BYTE)kills_of_mob, (BYTE)kills_of_player);
};
if(need_update)
{ // Reward In Battle
// need apply - battle condition (입력된 아이템 소진이 요구된다. 여러가지 고려사항이 많다.)
RewardInBattle(pUpdateCtrl, pKillerPlayer);
};
return /*RETURN TRUE VALUE*/;
}
FLOWCONTROL_END;
return /*RETURN FALSE VALUE*/;
}
//────────────────────────────────────────────────────────────────
BOOLEAN
GameSSQControlManagerImpl::_OnEnterEvent(GameSSQUpdateCtrl* const pUpdateCtrl, Character* const pCharacter)
{
const eTIME_TYPE updater_type = pUpdateCtrl->GetTimeType();
const bool bUseSkillOnlyMe = updater_type != TT_BATTLE;
const bool isPlayer = pCharacter->IsEqualObjectKind(PLAYER_OBJECT);
StatusManager* const pStatusManager = pCharacter->GetStatusManager();
pStatusManager->Status.On_FilterControl(bUseSkillOnlyMe, TRUE, pUpdateCtrl->ConstVars.pCheckFilters);
pStatusManager->ChangeInterState(eCHAR_STATE_SSQ_CTRL_BLOCK_ATTACK, bUseSkillOnlyMe);
if(isPlayer)
{
Player* pPlayer = static_cast<Player*>(pCharacter);
ISSQHandlerInGame& rRewardHandler = pPlayer->SSQHandler;
if(!rRewardHandler.IsValidState())
rRewardHandler.Init();
const SSQInfo* const pSSQInfo = pUpdateCtrl->GetSSQInfo();
//!_NA_20110630_BATTLE_ZONE_RENEWAL
const DWORD object_key = pPlayer->GetObjectKey();
#if WAVERIX_CODE_RENEWAL
if(GameField* pGameField = pPlayer->GetField())
{
WAR_MSG_SINGLE_ASSEMBLER<MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD, \
MSGSUB_SSQ_PROTOCOL_INFORMATION_AMOUNT_DEDUCTED> assembler;
assembler.MSG.ObjectKey = object_key;
assembler.MSG.Cost = DWORD(pSSQInfo->m_Cost);
assembler.Completion();
MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD* pMsg = assembler.GetPacket();
pMsg->m_dwKey = pSSQInfo->m_MapCode;
pGameField->SendPacketToAgent(pMsg, assembler.GetSize());
}
else
{
ASSERT(!"Unexpected Status");
}
#endif
#if WAVERIX_CODE_BACKUP
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = pUpdateCtrl->GetMsgRouter()->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
{
MSGSUB_SSQ_PROTOCOL_INFORMATION_AMOUNT_DEDUCTED msg;
msg.ObjectKey = object_key;
msg.Cost = (DWORD)pSSQInfo->m_Cost;
msg.SerializeTo(pHeader);
}
#endif
// 펫을 소환해제 한다.
CPetManager& pet_manager = pPlayer->m_PetManager;
pet_manager.ReturnPet(TRUE);
}
BOOLEAN counter_update = isPlayer || pCharacter->IsEqualObjectKind(SSQMONSTER_OBJECT);
if(counter_update)
pUpdateCtrl->SharedVars.UpdateIncreaseCount(isPlayer);
return TRUE;
}
//────────────────────────────────────────────────────────────────
BOOLEAN
GameSSQControlManagerImpl::_OnLeaveEvent(GameSSQUpdateCtrl* const pUpdateCtrl,
Character* const pCharacter)
{
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FlowControl flow;
FLOWCONTROL_START()
{
const BOOLEAN isPlayer = pCharacter->IsEqualObjectKind(PLAYER_OBJECT);
if(!isPlayer)
return TRUE;
Player* pPlayer = static_cast<Player*>(pCharacter);
ISSQHandlerInGame& rRewardHandler = pPlayer->SSQHandler;
if(!FlowControl::FCAssert(rRewardHandler.IsValidState()))
return FALSE;
SSQCharacterStatusInGame& rStatus = rRewardHandler->ControlStatus;
const BOOLEAN isAlive = !(rStatus.FIELD & rStatus.CHECKED_DEAD);
if(isAlive)
pUpdateCtrl->SharedVars.UpdateDecreaseCount(TRUE);
const eEVENT_MODE_MULTIUSE reward_type = rRewardHandler->GetSSQEventID();
switch(reward_type)
{
case eEVENT_MULTIUSE_SSQ_REFUND: // 전투 전 나간 플레이어 대상
case eEVENT_MULTIUSE_SSQ_WINNER_REWARD: // 전투 후 나간 플레이어 대상, 계산은 그때 이뤄진다. (순위 등등이 필요하므로...)
break;
case eEVENT_MULTIUSE_SSQ_QUEST_FAIL_INFO: // 전투 중 나간 플레이어 대상
SettleAccounts(pUpdateCtrl, pPlayer);
UpdateToRankingAndExtra(pUpdateCtrl, pPlayer);
break;
}
// DB Update는 모두 여기서 이뤄진다.
MSG_DG_EVENT_SSQ_REWARD_UPDATE_SYN msgSYN;
msgSYN.m_dwKey = pPlayer->GetUserGuid();
msgSYN.m_CharGuid = pPlayer->GetCharGuid();
if(rRewardHandler->GetRewardInfo(msgSYN.m_RewardInfo))
{
g_pGameServer->SendToServer(GAME_DBPROXY, &msgSYN, msgSYN.GetSize());
}
return TRUE;
}
FLOWCONTROL_END;
return FALSE;
}
//────────────────────────────────────────────────────────────────
BOOLEAN
GameSSQControlManagerImpl::ProcessGamble(
GameSSQUpdateCtrl* const pUpdateCtrl
, GameField* const pField
, const DWORD prize_winner
, MSGSUB_SSQ_S2S_GAMBLE_RESULT& msg
)
{
const SSQInfo* const pSSQInfo = pUpdateCtrl->GetSSQInfo();
typedef GameField::_PLAYER_HASH _PLAYER_HASH;
const _PLAYER_HASH& rPlayers = pField->GetRefPlayerList();
const DWORD player_num = (DWORD)rPlayers.size();
DWORD selector = 0;
FOREACH_CONTAINER(_PLAYER_HASH::value_type& rPlayer, rPlayers, _PLAYER_HASH)
{
Player* const pPlayer = rPlayer.second;
if (obj_util::IsValid(pPlayer, __FUNCTION__, __LINE__) == false)
{
continue;
}
ISSQHandlerInGame& rRewarder = pPlayer->SSQHandler;
rRewarder->SetInfo_QuestFailed(0, 0, 0, 0); // 이 시점부터는 환불처리되지 않는다.
if(prize_winner == selector)
{
const MONEY sumOfAdmissionFee = (MONEY)pSSQInfo->m_Cost * player_num;
const SSQInfo::RATIO_T chargeRatio = min(pSSQInfo->m_GamebleInfo.m_ChargeRatio, 100);
const MONEY resultHeim =
MONEY((sumOfAdmissionFee * DWORD((BYTE)(100 - chargeRatio))) / 100);
// 갬블보상을 이 시점에 처리할 것인가? 아님 나중에 처리할 것인가?
pPlayer->PlusMoney(resultHeim);
ASSERT(SAFE_NUMERIC_TYPECAST(MONEY, resultHeim, DWORD));
msg.RewardHeim = (DWORD)resultHeim;
msg.CharName = pPlayer->GetCharName();
GAMELOG->LogMoney(MONEY_REWARD_SSQ_GAMBLE, pPlayer, resultHeim);
}
++selector;
}
return TRUE;
}
//────────────────────────────────────────────────────────────────
DWORD
GameSSQControlManagerImpl::SettleAccounts(GameSSQUpdateCtrl* const pUpdateCtrl,
Player* const pPlayer,
const SSQHandlerInGame::eOrder order)
{
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FlowControl flow;
FLOWCONTROL_START()
{
ISSQHandlerInGame& rRewarder = pPlayer->SSQHandler;
SSQ_RESERVED_REWARD_INFO info;
if(!FlowControl::FCAssert(rRewarder->GetRewardInfo(info)))
break;
const BOOLEAN bFailInfo = eEVENT_MULTIUSE_SSQ_QUEST_FAIL_INFO == info.m_SharedInfo.EventID;
const SSQInfo* const pSSQInfo = pUpdateCtrl->GetSSQInfo();
const eCHAR_TYPE char_type = pPlayer->GetCharType();
const DWORD point_of_player = pSSQInfo->m_PlayerHuntingInfo[char_type].m_HuntingPoint;
const DWORD point_of_monster = pSSQInfo->m_NpcHuntingInfo[char_type].m_HuntingPoint;
const FLOAT point_ratio =
((DWORD)order < (DWORD)SSQHandlerInGame::eOrder_None)
? pSSQInfo->m_SuccessPointInfo[ order ].m_FloatPoint
: 1.0f
;
const DWORD kills_of_player = bFailInfo ? info.m_QuestFail.PlayerKillNum : info.m_WinnerReward.PlayerKillNum;
const DWORD kills_of_monster= bFailInfo ? info.m_QuestFail.MonsterKillNum : info.m_WinnerReward.MonsterKillNum;
// Formula by Planning
// - (PC HuntingPoints + NPC HuntingPoints)*order_type
const DWORD total_point =
DWORD(((point_of_player*kills_of_player) + (point_of_monster*kills_of_monster)) * point_ratio);
const SSQInfo::SUMMONDED_NPC_INFO& rNPCs = pSSQInfo->m_SummonedNpcInfo;
const MONSTERCODE monster_code = rNPCs.List[0].m_NPCCode;
const BASE_NPCINFO* const pNpcInfo = NPCInfoParser::Instance()->GetNPCInfo(monster_code);
const FLOAT exp_ratio_monster = pNpcInfo->m_fGradeExpRatio;
GameZone* const pGameZone = pUpdateCtrl->GetSSQField();
DistributeExpOprArgs args(pGameZone,
pGameZone->GetEntryField(),
pGameZone->GetZoneType(),
0,
0,
1,
pPlayer->GetLevel(),
0.0f,
monster_code,
0,
exp_ratio_monster,
pNpcInfo->m_dwMaxHP, // 이 수치는 좀더 따져볼 필요 있다.
pNpcInfo->m_Level); // 이 수치는 좀더 따져볼 필요 있다.
const FLOAT exp_ratio_player_weight = pSSQInfo->m_ExpRatioInfo[char_type].m_FloatPoint;
DistributeExpOprResult query_result;
g_ItemExpDistributor.GetCalcedExpValue(pPlayer, &args, &query_result);
const EXPTYPE& result_exp_monster = query_result.TotalExp;
const EXPTYPE result_exp_player = (EXPTYPE)(result_exp_monster * exp_ratio_player_weight);
// Formula by Planning
const DWORD total_exp =
(DWORD)((result_exp_player*exp_ratio_player_weight)*kills_of_player)
+ (DWORD)((result_exp_monster*exp_ratio_player_weight)*kills_of_monster)
;
rRewarder->UpdateInfo_PlayPoint(total_point, total_exp, 0, 0);
rRewarder->SetBonusExpRatio(query_result.BonusExp);
return total_point;
}
FLOWCONTROL_END;
return 0;
}
//────────────────────────────────────────────────────────────────
VOID
GameSSQControlManagerImpl::UpdateToRankingAndExtra(GameSSQUpdateCtrl* const pUpdateCtrl,
Player* const pPlayer)
{
ISSQHandlerInGame& rRewarder = pPlayer->SSQHandler;
SSQ_RESERVED_REWARD_INFO info;
if(!rRewarder->GetRewardInfo(info))
return;
const eEVENT_MODE_MULTIUSE reward_type = rRewarder->GetSSQEventID();
switch(reward_type)
{
case eEVENT_MULTIUSE_SSQ_REFUND: // 전투 전 나간 플레이어 대상
break;
case eEVENT_MULTIUSE_SSQ_WINNER_REWARD: // 전투 후 나간 플레이어 대상, 계산은 그때 이뤄진다. (순위 등등이 필요하므로...)
case eEVENT_MULTIUSE_SSQ_QUEST_FAIL_INFO: // 전투 중 나간 플레이어 대상
{
#ifdef _NA000000_100427_SSQ_DEFAULT_REWARD_
// (NOTE) (f100427.1L) default reward
RewardDefault(pUpdateCtrl, pPlayer);
#endif
GameInstanceDungeon* const pIndun = pUpdateCtrl->GetSSQField();
#ifdef _NA_008012_20150130_RANKING_SYSTEM
RankingSystemCtl* const pRanking = pIndun->GetRankingSystemCtl();
if(!pRanking)
break;
#endif // _NA_008012_20150130_RANKING_SYSTEM
const DWORD total_point
= eEVENT_MULTIUSE_SSQ_WINNER_REWARD == reward_type
? info.m_WinnerReward.Point
: info.m_QuestFail.Point
;
#ifdef _NA_008012_20150130_RANKING_SYSTEM
pRanking->SetLastSettleAccount(pPlayer, (WORD)total_point);
#endif // _NA_008012_20150130_RANKING_SYSTEM
const DWORD total_exp =
eEVENT_MULTIUSE_SSQ_WINNER_REWARD == reward_type ? info.m_WinnerReward.Exp
: info.m_QuestFail.Exp;
pPlayer->AddExp(total_exp, pPlayer->GetObjectKey(), rRewarder->GetBonusExpRatio());
}
//SettleAccounts(pUpdateCtrl, pPlayer);
break;
}
}
//────────────────────────────────────────────────────────────────
BOOLEAN
GameSSQControlManagerImpl::_RewardProcess(const eRewardSelect reward_selector,
GameSSQUpdateCtrl* const pUpdateCtrl,
Player* const pPlayer,
const SSQInfo::REWARD_NODE& rRewardNode,
const SSQHandlerInGame::eOrder order)
{
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FLOWCONTROL_START()
{
// Check Inventory Constraint
ProtectorInventory& rPROTECT = pPlayer->GetWarehouseInventoryProtector().INVENTORY();
if(!rPROTECT.IsUsable())
return FALSE;
SLOTCODE item_code = rRewardNode.m_RewardCode;
POSTYPE item_num = (POSTYPE)rRewardNode.m_RewardLimit;
if(!(item_code && item_num))
return FALSE;
ItemManager* const pItemManager = pPlayer->GetItemManager();
BYTE msg_stream[max(sizeof(MSG_CG_ZONE_EX_SSQ_WINNER_REWARD_CMD),
sizeof(MSG_CG_ITEM_OBTAIN_ITEM_CMD))];
MSG_CG_ZONE_EX_SSQ_WINNER_REWARD_CMD* pCmdWinner = 0;
MSG_CG_ITEM_OBTAIN_ITEM_CMD* pCmdBattle = 0;
if(reward_selector & eReward_ForWinner)
pCmdWinner = new (msg_stream) MSG_CG_ZONE_EX_SSQ_WINNER_REWARD_CMD;
else
pCmdBattle = new (msg_stream) MSG_CG_ITEM_OBTAIN_ITEM_CMD;
// 이렇게 해야 한다. Winner Reward JUST_~_INFO->INVEN~_INFO로 변환할 것. result도 포함할 것.
INVENTORY_TOTAL_INFO* pInventoryTotalInfo = reward_selector&eReward_ForWinner
? &pCmdWinner->m_TotalInfo
: &pCmdBattle->m_tagInventory;
RC::eITEM_RESULT result = RC::RC_ITEM_SUCCESS;
;{ // (CHANGES) (f100427.1L) give the SSQ default reward regardless dead state
// to support do need the dead control with NO event
bool dead_control = ((reward_selector & eReward_ForWinner) == 0 &&
pPlayer->IsDead() != false);
if (dead_control) {
pPlayer->SetHP(1);
}
result = pItemManager->ObtainAllKindOfItem(1, &item_code, &item_num, pInventoryTotalInfo);
if (dead_control) {
pPlayer->SetHP(0);
}
};
const WORD msg_size = (reward_selector & eReward_ForWinner) ?
pCmdWinner->GetSize() : pCmdBattle->GetSize();
if (reward_selector & eReward_ForWinner)
{
pCmdWinner->m_Result = result;
pPlayer->SendPacket((MSG_BASE_FORWARD*)msg_stream, msg_size);
if(result != RC::RC_ITEM_SUCCESS)
break;
//GAMELOG-> 보상 처리 관련, 보상 성공/실패 로그로 해서 나중에 복구처리할 수 있도록 지원하자.
GAMELOG->LogMultiItem(ITEM_REWARD_SSQ_WINNER, pPlayer, 1, &item_code, &item_num);
}
else if (result == RC::RC_ITEM_SUCCESS)
{ // 전투 중 경품 처리 실패인 경우라면 차라리 보내지 않는 편이 나을 것이다.
pPlayer->SendPacket((MSG_BASE_FORWARD*)msg_stream, msg_size);
// NOTE: f110602.3L, the selection whether the player was basically rewarded
// for his particifation.
const int log_type = (reward_selector & this->eReward_InBattle) ?
ITEM_REWARD_SSQ_BATTLE_PRIZE : ITEM_REWARD_SSQ_DEFAULT_PRIZE;
GAMELOG->LogMultiItem(log_type, pPlayer, 1, &item_code, &item_num);
}
else
{
break;
}
//_NA_001990_ACSYSTEM_ADD
if(RC::RC_ITEM_SUCCESS == result)
{
const BASE_ITEMINFO* item_info = ItemInfoParser::Instance()->GetItemInfo(item_code);
if (item_info->m_wACCode)
pPlayer->GetACManager()->ProcessItemObtain(item_info->m_Code, item_info->m_wACCode,
item_info->m_byACReferenceID, item_num);
}
return TRUE;
}
FLOWCONTROL_END;
return FALSE;
}
BOOLEAN
GameSSQControlManagerImpl::RewardForWinner(GameSSQUpdateCtrl* const pUpdateCtrl,
Player* const pPlayer,
const SSQHandlerInGame::eOrder order) // 1,2,3등
{
// 최종 우승자에 대한 아이템 지급 처리, UI reporting은 마을|필드에서 별도로 처리된다.
const SSQInfo* const pSSQInfo = pUpdateCtrl->GetSSQInfo();
ASSERT((DWORD)SSQHandlerInGame::eOrder_None > (DWORD)order);
const SSQInfo::REWARD_WINNER_CHAR_INFO& rInfo = pSSQInfo->m_RewardWinnerInfo;
const eCHAR_TYPE char_type = pPlayer->GetCharType();
const SSQInfo::REWARD_NODE& rNode = rInfo[order][char_type];
const BOOLEAN item_rewarded = _RewardProcess(eReward_ForWinner, pUpdateCtrl, pPlayer, rNode, order);
ISSQHandlerInGame& rRewarder = pPlayer->SSQHandler;
SSQ_RESERVED_REWARD_INFO::WinnerReward rWinner;
if(!FlowControl::FCAssert(rRewarder->GetInfo_QuestWinner(rWinner)))
return FALSE;
rWinner.ItemCode = !item_rewarded ? rNode.m_RewardCode : 0 ;
rRewarder->SetInfo_WinnerReward(rWinner.Point, rWinner.Exp, rWinner.MonsterKillNum, rWinner.PlayerKillNum, rWinner.ItemCode);
return TRUE;
}
BOOLEAN
GameSSQControlManagerImpl::RewardInBattle(GameSSQUpdateCtrl* const pUpdateCtrl,
Player* const pPlayer)
{
const eTIME_TYPE updater_type = pUpdateCtrl->GetTimeType();
if(updater_type != GameSSQControlInfo::TT_BATTLE)
return FALSE;
GameSSQUpdateCtrl_Battle* const pBattleUpdater = static_cast<GameSSQUpdateCtrl_Battle*>(pUpdateCtrl);
GameWarSSQItemDistribution& rItemDistributer = pBattleUpdater->ItemDistributer;
const BYTE cur_earthquake_step = pBattleUpdater->GetCurEarthquakeStep();
const SSQInfo::REWARD_NODE rNode = rItemDistributer.Drop(cur_earthquake_step);
if(!rNode.m_RewardCode)
return TRUE;
if(_RewardProcess(eReward_InBattle, pUpdateCtrl, pPlayer, rNode, SSQHandlerInGame::eOrder_None))
{
SSQMsgRouterInGame* const pMsgRouter = pUpdateCtrl->GetMsgRouter();
BROADCASTER_MSG_AG_ZONE_SSQ_ROOM_CTRL_CMD& rMsgCMD = pMsgRouter->GetCMD_AG();
WAR_MSG_HDR* const pHeader = &rMsgCMD.MSG_HEADER;
{
MSGSUB_SSQ_PROTOCOL_INFORMATION_REWARD_ON_PLAYING msg;
msg.CharName = pPlayer->GetCharName();
msg.SerializeTo(pHeader);
}
return TRUE;
}
return FALSE;
}
#ifdef _NA000000_100427_SSQ_DEFAULT_REWARD_
// (NOTE) default reward process for all joined players
BOOLEAN
GameSSQControlManagerImpl::RewardDefault(GameSSQUpdateCtrl* const update_ctrl,
Player* const player)
{
const eTIME_TYPE updater_type = update_ctrl->GetTimeType();
if (updater_type < GameSSQControlInfo::TT_BATTLE) {
return false;
}
const SSQInfo* ssq_info = update_ctrl->GetSSQInfo();
if (_RewardProcess(eReward_Default, update_ctrl, player, ssq_info->m_DefaultRewardInfo,
SSQHandlerInGame::eOrder_None))
{
return true;
}
return false;
}
#endif
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// <Internal Message Handler Implementation>
//
namespace GameSSQControlInfo
{
static const GM_Spec::SSQ _GM_CMDs[GM_Spec::eCmd_upperbound]
= {
{ GM_Spec::eCmd_open, "open", },
{ GM_Spec::eCmd_close, "close", },
{ GM_Spec::eCmd_warp, "warp", },
{ GM_Spec::eCmd_status, "status", },
{ GM_Spec::eCmd_jump, "jump", },
{ GM_Spec::eCmd_summon, "summon", },
{ GM_Spec::eCmd_observer, "observer", },
// insert items
// ...
};
};
#include <SinTable.h>
#ifdef __WAVERIX_TEST_LOGIC_TASK_MACRO__
extern BOOLEAN Variation_SSQ_Summon_R1(const GameSSQCtrlCmd::GMCmd& rInfo, GameSSQUpdateCtrl* const pCtrl, Player* const pGmPlayer);
extern BOOLEAN Variation_SSQ_Summon_R2(const GameSSQCtrlCmd::GMCmd& rInfo, GameSSQUpdateCtrl* const pCtrl, Player* const pGmPlayer);
#endif
BOOLEAN
GameSSQControlManagerImpl::GM_Command_Process(const GameSSQCtrlCmd::GMCmd& rInfo, Player* const pGmPlayer)
{
//
GM_Spec::eCmd cmd_type = GM_Spec::eCmd_upperbound;
{
typedef util::ARRAY_TYPEINFO< const GM_Spec::SSQ,
0, GM_Spec::eCmd_upperbound > array_info;
FOREACH_ARRAY(const GM_Spec::SSQ& rSpec, _GM_CMDs, array_info)
{
const INT iRet = memcmp(rSpec.CmdString, rInfo.Arguments[1], sizeof(GM_Spec::String));
if(iRet)
continue;
cmd_type = rSpec.GM_Cmd;
break;
}
};
if(GM_Spec::eCmd_upperbound == cmd_type)
return FALSE;
// parse [2] string, [3] string
SSQInfo::SSQCODE CodeOfSSQ = 0;
GM_Spec::eOption gm_option = GM_Spec::eOption_None;
switch(cmd_type)
{
case GM_Spec::eCmd_open: //ssq open [mapcode]
case GM_Spec::eCmd_close: //ssq close [mapcode]
case GM_Spec::eCmd_status: //ssq status [mapcode]
{
CodeOfSSQ = (SSQInfo::SSQCODE)atoi(rInfo.Arguments[2]);
if(rInfo.Arguments[3][0] != '\0')
{ //나중에 테이블화 시키자.
if(strncmp(rInfo.Arguments[3], "fast", _countof(rInfo.Arguments[3])) == 0)
gm_option = GM_Spec::eOption_FastTrack;
}
}
break;
case GM_Spec::eCmd_warp: //ssq warp
case GM_Spec::eCmd_jump: //ssq jump (단계로 넘어간다)
//case GM_Spec::eCmd_summon: //ssq summon
case GM_Spec::eCmd_observer:
{
GameField* pCurField = pGmPlayer->GetField();
GameZone* pCurZone = 0;
if(!(pCurField
&& (pCurZone=pCurField->GetGameZone())
&& (pCurZone->GetZoneType() == eZONETYPE_INSTANCE)
))
return FALSE;
CodeOfSSQ = pCurZone->GetMapCode();
}
break;
case GM_Spec::eCmd_summon:
{
#ifdef __WAVERIX_TEST_LOGIC_TASK_MACRO__
if(!Variation_SSQ_Summon_R1(rInfo, 0, pGmPlayer))
return FALSE;
#endif
}
break;
}
SSQ_UPDATERS_IT itFind = _FindCodeOfSSQ(CodeOfSSQ);
if(itFind == UpdateList.end())
return FALSE;
SSQ_UPDATER_NODE& rNode = *itFind;
GameSSQUpdateCtrl* const pCtrl = rNode.pUpdater;
GameInstanceDungeon* const pDungeon = pCtrl->GetSSQField();
if(!pDungeon && GM_Spec::eCmd_jump == cmd_type)
{
return FlowControl::FCAssert(!"Impossible Logic");
}
if(pDungeon && GM_Spec::eCmd_open == cmd_type)
{
MSG_CG_ETC_DEBUGINFO_CMD msgCMD;
INT iLen = _snprintf(msgCMD.m_pszChatMsg, _countof(msgCMD.m_pszChatMsg),
"Already Opened - mapcode [%u]",
CodeOfSSQ);
msgCMD.m_byMsgLength = (BYTE)iLen;
pGmPlayer->SendPacket(&msgCMD, msgCMD.GetSize());
return TRUE;
}
switch(cmd_type)
{
case GM_Spec::eCmd_open:
case GM_Spec::eCmd_close:
case GM_Spec::eCmd_status:
case GM_Spec::eCmd_jump:
{
SSQMsgRouterInGame* const pMsgRouter = pCtrl->GetMsgRouter();
WAR_MSG_SINGLE_ASSEMBLER< MSG_AG_ZONE_SSQ_ROOM_INFO_BRD, MSGSUB_SSQ_S2S_GM_CONTROL_REQ > assembler;
GM_Spec::MsgFormat& rCommand = assembler.MSG.Command;
rCommand.Cate = GM_Spec::eCategory_SSQ;
rCommand.GM_Cmd = cmd_type;
rCommand.CodeOfSSQ = CodeOfSSQ;
rCommand.GMObjectKey= pGmPlayer->GetObjectKey();
rCommand.Option = gm_option;
assembler.Completion();
MSG_AG_ZONE_SSQ_ROOM_INFO_BRD* const pMsg = assembler.GetPacket();
pMsg->m_dwKey = CodeOfSSQ;
g_pGameServer->SendToServer(AGENT_SERVER, assembler.GetPacket(), assembler.GetSize());
}
break;
case GM_Spec::eCmd_warp:
{
GameField* pField = pGmPlayer->GetField();
WzVector center_pos = { SSQClosedSpaceCenter_X, SSQClosedSpaceCenter_Y, SSQClosedSpaceCenter_Z+1.0f };
pField->TeleportObject(pGmPlayer, &center_pos, TRUE);
}
break;
case GM_Spec::eCmd_observer:
{
const SSQInfo* const pSSQInfo = pCtrl->GetSSQInfo();
GameInstanceDungeon* const pIndun = pCtrl->GetSSQField();
if(!pIndun)
break;
using namespace nsSSQ::Observer;
GameWarObserverMode observer(pIndun);
if(rInfo.Arguments[2][0] == '\0')
{
const Rules::eC2G_ReqRule rules =
Rules::eC2G_ReqRule(Rules::eC2G_ReqRule_CondInSSQField |
Rules::eC2G_ReqRule_CondObserverModeOff |
Rules::eC2G_ReqRule_CondDeadState);
observer.Enter(rules, pGmPlayer);
}
else
{
const Rules::eC2G_ReqRule rules =
Rules::eC2G_ReqRule(Rules::eC2G_ReqRule_CondInSSQField |
Rules::eC2G_ReqRule_CondObserverModeOn |
Rules::eC2G_ReqRule_CondDeadState);
const Info::eKeyCmd key_cmd = (Info::eKeyCmd)atoi(rInfo.Arguments[2]);
observer.ChangeCoordinates(rules, pGmPlayer, key_cmd);
}
}
break;
case GM_Spec::eCmd_summon:
{
#ifdef __WAVERIX_TEST_LOGIC_TASK_MACRO__
if(!Variation_SSQ_Summon_R2(rInfo, pCtrl, pGmPlayer))
return FALSE;
#endif
}
break;
}
return TRUE;
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// <Packet Handler Implementation>
//
SSQ_PACKET_HANDLER_AG_DECL(AG_ZONE, AG_ZONE_SSQ_JOIN_INFO_SYN)
{
const DWORD userKey = pRecvMsg->m_dwKey;
Player* const pPlayer = PlayerManager::Instance()->FindPlayerByUserKey(userKey);
if(!pPlayer) return;
const SSQCODE CodeOfSSQ = SSQCODE(pRecvMsg->m_CodeOfSSQ);
//////////////////////////////////////////////////////////////////////////
// (WAVERIX)(NOTE)(080217)
// - 허용 조건(in Client)
// - 1) in FIELD_SERVER -- checked
// - 2) 카오 상태 불가 -- checked
// - 3) 입장 가능시간 -- checked
// - 4) 해당 아이템 존재 <- GameServer
// - 5) 입장 가능 하임 보유 여부 <- GameServer
// - 6) 입장 가능 레벨 -- checked
//////////////////////////////////////////////////////////////////////////
BYTE errorGroup = MSG_AG_ZONE_SSQ_JOIN_INFO_ACK::RET_ROOM;
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FlowControl flow;
FLOWCONTROL_START()
{
// <assertion, 키가 있으면 pInstanceDungeon도 존재한다.
// 이 패킷은 FieldServer에서만 처리되는 패킷이다. <- error code 추가할 것
IfFailThenBreak(Control.IsCheckedFieldServer(), RC::RC_ROOM_INVALID_ROOM_TYPE);
SSQ_UPDATERS_IT itFind = _FindCodeOfSSQ(CodeOfSSQ);
if(itFind == UpdateList.end())
return;
SSQ_UPDATER_NODE& rNode = *itFind;
GameSSQUpdateCtrl* const pCtrl = rNode.GetUpdater();
// Agent에 의해 Level, Chao, 등이 검사되었음.
const SSQInfo* const pSSQInfo = pCtrl->GetSSQInfo();
{ // 보상/환불 정보 있는지 검사
ISSQHandlerInGame& rHandler = pPlayer->SSQHandler;
if(rHandler.IsValidState())
{
errorGroup = MSG_AG_ZONE_SSQ_JOIN_ACK::RET_ROOM;
//IfFailThenBreak(!rHandler->IsDBTransaction(), RC::RC_ROOM_FAILED);
// // 시간 동기화 상의 문제, 복잡한 패킷플로우를 배제한 결과로 인한 것... 그냥 재 요청하도록 유도하자.
SSQ_RESERVED_REWARD_INFO info; // 보상/환불 정보가 없어야지 다음 단계로 넘어갈 수 있다.
IfFailThenBreak(!rHandler->GetRewardInfo(info),
RC::RC_ROOM_CANNOT_ENTER_BEFORE_REWARD_OR_REFUND);
}
};
SLOTCODE rItemCode = 0;
{ // 제약 조건 검사
errorGroup = MSG_AG_ZONE_SSQ_JOIN_INFO_ACK::RET_ROOM;
const MONEY money = pPlayer->GetMoney();
IfFailThenBreak(money >= pSSQInfo->m_Cost, RC::RC_ROOM_INSUFFICIENT_MONEY);
//!_NA_20110630_BATTLE_ZONE_RENEWAL
};
{ // 강제 상태 변경 처리
// 1) 파티 상태 해제 <- by Agent
// --JOIN--2) 개인상점 상태 해제
// --JOIN--3) Pet소환 상태 해제
// --JOIN--4) 소환체 상태(Summon Valkyrie)해제
// 강제 상태 해제는 JOIN_ACK을 처리하기 전에 처리하는 형태로 확정
// TODO(WAVERIX)
};
{ // Success
MSG_AG_ZONE_SSQ_JOIN_INFO_ACK msgACK;
msgACK.SetSuccessResult(CodeOfSSQ, rItemCode);
pPlayer->SendPacket(&msgACK, msgACK.GetSize());
};
return /*RETURN TRUE VALUE*/;
}
FLOWCONTROL_END;
//////////////////////////////////////////////////////////////////////////
// <ERROR CONTROL>
const DWORD errorCode = flow.GetErrorCode();
MSG_AG_ZONE_SSQ_JOIN_INFO_ACK msgFAIL;
msgFAIL.SetFailResult(errorGroup, errorCode);
pPlayer->SendPacket(&msgFAIL, msgFAIL.GetSize());
// Post Process
if(MSG_AG_ZONE_SSQ_JOIN_ACK::RET_ROOM == errorGroup)
{
switch(errorCode)
{
case RC::RC_ROOM_CANNOT_ENTER_BEFORE_REWARD_OR_REFUND:
//pPlayer->SSQHandler->Send_RewardExistInfo(pPlayer); // not check SSQHandler.IsValidState()
pPlayer->SSQHandler->Send_RewardProcess(pPlayer, FALSE);
break;
}
}
return /*RETURN FALSE VALUE*/;
}
//────────────────────────────────────────────────────────────────
SSQ_PACKET_HANDLER_AG_DECL(AG_ZONE, AG_ZONE_SSQ_JOIN_SYN)
{
WAVERIX_DEBUGGING_OUTPUTn(0);
const SSQInfo::INDEX_T IDofSSQ = (SSQInfo::INDEX_T)pRecvMsg->m_CodeOfSSQ;
//////////////////////////////////////////////////////////////////////////
// (WAVERIX)(NOTE)(080217)
// - 허용 조건(in Client)
// - 1) in FIELD_SERVER -- checked
// - 2) 카오 상태 불가 -- checked
// - 3) 입장 가능시간 -- checked
// - 4) 해당 아이템 존재 <- GameServer
// - 5) 입장 가능 하임 보유 여부 <- GameServer
// - 6) 입장 가능 레벨 -- checked
//////////////////////////////////////////////////////////////////////////
BYTE errorGroup = MSG_AG_ZONE_SSQ_JOIN_ACK::RET_ROOM;
Player* pPlayer = 0;
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FlowControl flow;
FLOWCONTROL_START()
{
// <assertion, 키가 있으면 pInstanceDungeon도 존재한다.
// 이 패킷은 FieldServer에서만 처리되는 패킷이다.
if(Control.IsCheckedFieldServer())
{
const DWORD userKey = pRecvMsg->m_dwKey;
pPlayer = PlayerManager::Instance()->FindPlayerByUserKey(userKey);
if(pPlayer == 0)
return;
SSQ_UPDATERS_IT itFind = _FindCodeOfSSQ(IDofSSQ);
if(itFind == UpdateList.end())
return;
SSQ_UPDATER_NODE& rNode = *itFind;
GameSSQUpdateCtrl* const pCtrl = rNode.GetUpdater();
// Agent에 의해 Level, Chao, 등이 검사되었음.
const SSQInfo* const pSSQInfo = pCtrl->GetSSQInfo();
{ // 제약 조건 검사
errorGroup = MSG_AG_ZONE_SSQ_JOIN_ACK::RET_ROOM;
const MONEY money = pPlayer->GetMoney();
IfFailThenBreak(money >= pSSQInfo->m_Cost, RC::RC_ROOM_INSUFFICIENT_MONEY);
//!_NA_20110630_BATTLE_ZONE_RENEWAL
};
// 강제 상태 변경 처리
// 1) 파티 상태 해제 <- by Agent
// --JOIN--2) 개인상점 상태 해제
// --JOIN--3) Pet소환 상태 해제
// --JOIN--4) 소환체 상태(Summon Valkyrie)해제
{ // --JOIN--2) 개인상점 상태 해제
ePLAYER_BEHAVE_STATE behaveState = pPlayer->GetBehave();
if(behaveState == PLAYER_BEHAVE_TRADE_STATE)
{
MSG_CG_TRADE_CANCEL_CMD msgCMD;
Player* pCounterPlayer = pPlayer->GetTradeContainer()->GetCounterPlayer();
if(pCounterPlayer->GetBehave() != PLAYER_BEHAVE_TRADE_STATE)
{
SUNLOG(eCRITICAL_LOG, "Move시TRADE_STATE상태이상[1:%s][2:%s],%u",
pPlayer->GetCharName(),
pCounterPlayer->GetCharName(), pCounterPlayer->GetBehave());
}
else
{
pCounterPlayer->SendPacket(&msgCMD, sizeof(msgCMD));
}
g_TradeManager.EndTrade(pPlayer);
behaveState = pPlayer->GetBehave();
pPlayer->SendPacket(&msgCMD, sizeof(msgCMD));
}
errorGroup = MSG_AG_ZONE_SSQ_JOIN_ACK::RET_ROOM;
IfFailThenBreak(behaveState == PLAYER_BEHAVE_IDLE_STATE, RC::RC_ROOM_INVALID_PLAYER_STATE);
};
{ // --JOIN--3) Pet소환 상태 해제
//const BOOLEAN bSummoned = pPlayer->m_PetManager.AlreadySummonedPet();
//<- 플레이어로부터 펫 정보를 받아서 처리할 수 있도록 할 것.
CPetManager& pet_manager = pPlayer->m_PetManager;
pet_manager.ReturnPet(TRUE);
};
{ // (CHANGES) (091208) (WAVERIX) 탈 것 추가되면서 소환 해제 처리를 수행한다.
pPlayer->Rider.Command_SummonRelease(IVehicleEvent::EnterField());
};
{ // --JOIN--4) 소환체 상태(Summon Valkyrie)해제
//const DWORD summonerKey = pPlayer->GetSummonerKey();
SummonManager* const pSummonManager = SummonManager::Instance();
pSummonManager->LeaveSummonNPCs(pPlayer);
};
{
StatusManager* const pStatusManager = pPlayer->GetStatusManager();
pStatusManager->RemoveNonCashStatus();
}
{ // Success
MSG_AG_ZONE_SSQ_JOIN_ACK msgACK;
msgACK.SetSuccessResult((MAPCODE)IDofSSQ);
pPlayer->SendPacket(&msgACK, msgACK.GetSize());
};
}
else //if(Control.IsCheckedBattleServer)
{
// 현 시점, BattleServer에는 해당 플레이어가 존재하지 않는다.
SSQ_UPDATERS_IT itFind = _FindCodeOfSSQ(IDofSSQ);
if(itFind == UpdateList.end())
return;
SSQ_UPDATER_NODE& rNode = *itFind;
GameSSQUpdateCtrl& rCtrl = *rNode.GetUpdater();
// Agent에 의해 Level, Chao, 등이 검사되었음.
const SSQInfo* const pSSQInfo = rCtrl.GetSSQInfo();
{ // 제약 조건 검사
// 인원 검사, '나(BattleServer) 살아 있어' 하는 응답이라고 보면 될 듯 하다.
};
{ // Success
MSG_AG_ZONE_SSQ_JOIN_ACK msgACK;
msgACK.SetSuccessResult((MAPCODE)IDofSSQ);
pSession->SendPacket(&msgACK, msgACK.GetSize());
};
}
return /*RETURN TRUE VALUE*/;
}
FLOWCONTROL_END;
if(pPlayer)
{
MSG_AG_ZONE_SSQ_JOIN_ACK msgFail;
msgFail.SetFailResult(errorGroup, flow.GetErrorCode());
pPlayer->SendPacket(&msgFail, msgFail.GetSize());
}
return;
}
//────────────────────────────────────────────────────────────────
SSQ_PACKET_HANDLER_AG_DECL(AG_ZONE, AG_ZONE_SSQ_ROOM_CTRL_CMD)
{
WAVERIX_DEBUGGING_OUTPUTn(0);
// 소멸될 때는 키를 해제해야 한다. 해당 포인터를 할당할까? 음...
const SSQCODE CodeOfSSQ = (SSQCODE)pRecvMsg->m_dwKey;
SSQ_UPDATERS_IT itFind = _FindCodeOfSSQ(CodeOfSSQ);
if(itFind == UpdateList.end())
return;
SSQ_UPDATER_NODE& rNode = *itFind;
const KEYTYPE KEYofSSQ = rNode.GetKey();
if(!KEYofSSQ)
return;
//AutoTransactionCtrlNode trans(&rNode);
rNode.pUpdater->RecvedMsgProcess(pRecvMsg->m_Header);
}