Files
2022-10-26 12:25:11 +08:00

3120 lines
100 KiB
C++

// Player.cpp: implementation of the Player class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Player.h"
//////////////////////////////////////////////////////////////////////////
// <ProgramCommon>
#include <PublicMath.h>
#include <CharInfoParser.h>
#include <SkillInfoParser.h>
// <Quest>
#include <QuestDefine.h>
#include <QuestStruct.h>
#include <QuestManager.h>
// <Common>
#include <ServerSession.h>
#include <PacketStruct_AG.h>
#include <WayPoint.h>
#include <FileParser.h>
#include <ServerOptionParser.h>
#include <PacketControl/WarMessages_Sync.h>
#include <PacketControl/PacketControlS2S.h>
// <SunWriteLog>
#include <LogTimeFormat.h>
// <Internal GameServer>
#include "GameServerEx.h"
#include "PlayerManager.h"
#include "Map.h"
#include "NPC.h"
#include "GameField.h"
#include "GameZone.h"
#include "GameZoneManager.h"
#include "GameShopManager.h"
#include "CharacterFormula.h"
// <Internal GameServer STATUS CONTROLLER in Player>
#include "MissionManager.h"
#include "ItemManager.h"
#include "TradeSlotContainer.h"
#include "TradeManager.h"
#include "VendorManager.h"
#include "VendorPageManager.h"
#include "SummonManager.h"
#include "SummonedNPCs.h"
#include "SlotManager.h"
#include "SkillManager.h"
#include "QuickManager.h"
#include "QuickStyleManager.h"
#include "TriggerManager.h"
#include "StatusManager.h"
#include "PVPInfo.h"
#include "EventInventorySlotContainer.h"
#include <AreaConquestCommonHeader.h>
#include "FatigueManager.h"
#include "QuestManager_Concrete.h"
#include "QuestInfoManagerEx.h"
#include "ChaoState.h"
#include "CHuntingManager.h"
#include "CompetitionInfo.h"
#include "View.h"
#include "MovementChecker.h"
#include "MoveStateControl.h"
// <ETCs>
#include "EtcStatus.h"
#include "RecoverStatus.h"
#include "GameCHuntingRoom.h"
#include "CoolTimerFactory.h"
#include <ServerOptionParserEx.h>
#include "SkillSlotManager.h"
#include <SkillSystem/PerkManager.h> //_NA002217_100728_EP2_PERK_SYSTEM_
#include <World/GameWarControlManager.h>
#include "GameInstanceDungeon.h"
#include "MovingPlanner.h"
#include "CollectedList.h"
#include "WarehouseSlotContainer.h"
#include "GuildWarehouseSlotContainer.h"
#include "GroupInfoParser.h"
#ifdef _NA_1419_20100121_ACCUMULATE_POINT
#include "AccumulatePoint.h"
#endif
#ifdef _NA_008012_20150204_SUN_RANKING_SYSTEM
#include <EquipmentSlotContainer.h>
#include "SunRanking/SunRankingGradeManager.h"
#endif // _NA_008012_20150204_SUN_RANKING_SYSTEM
#ifdef _NA_008132_20150316_MONDSCHEIN_TEXTLOG
#include "DominationInfoParser.h"
#endif //_NA_008132_20150316_MONDSCHEIN_TEXTLOG
#ifdef _NA_008334_20150608_SONNENSCHEIN
#include "GameGuildManager.h"
#include "GameGuild.h"
#endif //_NA_008334_20150608_SONNENSCHEIN
#if defined (_NA_20100420_CHANGE_SYSTEM_MEMO_LOGIC) || \
defined (_NA_20100812_INTEGRATE_SYSTEM_MEMO_DOMINATION)
#include "PostManager.h"
#endif
#include "./Slot/EtherealizeImpl.h"
//_NA_004035_20120227_COSTUME_ITEM
#include <SkillAttrCalculator.h>
#include "InventorySlotContainer.h"
#include <AwakeningInfoParser.h>
//[Costume]=========================================================================================
#ifdef _NA_008610_20160104_ADD_DECORATECOSTUME
bool Player::CostumeHandler::Use(const SLOTCODE item_code, const POSTYPE item_pos)
{
CInventorySlotContainer* const inventory_container =
static_cast<CInventorySlotContainer*>(owner_->GetItemManager()->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(item_pos));
WORD item_type = item_slot.GetItemInfo()->m_wType;
// 아이템 타입 검사
if (item_slot.GetItemInfo() == NULL ||
(item_type != eITEMTYPE_COSTUME && item_type != eITEMTYPE_COSTUME_DECORATE))
{
return false;
}
// 코스튬 성별 검증..
if (item_slot.GetItemInfo()->IsCanUseGender(owner_->GetCharInfo()->gender) == false)
{
return false;
}
// 코스튬 클래스 검증..
const eCHAR_TYPE char_type = static_cast<eCHAR_TYPE>(owner_->GetCharInfo()->m_byClassCode);
if (item_slot.GetItemInfo()->CheckValidEquipClass(char_type) == false)
{
return false;
}
// 이미 코스튬 착용중일 경우 처리
if (item_type == eITEMTYPE_COSTUME && owner_->char_info_.is_equipped_costume_ == true)
{
// 착용 해제..
if (owner_->char_info_.costume_item_pos_ == item_pos)
{
return Equipped(item_slot, false);
}
// 이전 코스튬 해제 후 새로운 코스튬 착용..
else
{
SCItemSlot& previous_item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(owner_->char_info_.costume_item_pos_));
const BASE_ITEMINFO* precious_item_info = previous_item_slot.GetItemInfo();
if (precious_item_info && precious_item_info->m_wType == eITEMTYPE_COSTUME)
{
ApplyOption(previous_item_slot, false);
}
else
{
owner_->char_info_.is_equipped_costume_ = 0;
owner_->char_info_.costume_item_pos_ = INVALID_POSTYPE_VALUE;
owner_->char_info_.costume_item_code_ = 0;
return false;
}
return Equipped(item_slot, true);
}
}
else if (item_type == eITEMTYPE_COSTUME_DECORATE && owner_->char_info_.is_equipped_decorate_costume_ == true)
{
// 착용 해제..
if (owner_->char_info_.decorate_costume_item_pos_ == item_pos)
{
return Equipped(item_slot, false);
}
// 이전 코스튬 해제 후 새로운 코스튬 착용..
else
{
SCItemSlot& previous_item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(owner_->char_info_.decorate_costume_item_pos_));
const BASE_ITEMINFO* precious_item_info = previous_item_slot.GetItemInfo();
if (precious_item_info && precious_item_info->m_wType == eITEMTYPE_COSTUME)
{
ApplyOption(previous_item_slot, false);
}
else
{
owner_->char_info_.is_equipped_decorate_costume_ = 0;
owner_->char_info_.decorate_costume_item_pos_ = INVALID_POSTYPE_VALUE;
owner_->char_info_.decorate_costume_item_code_ = 0;
return false;
}
return Equipped(item_slot, true);
}
}
// 코스튬 미착용중일 경우 처리
else
{
return Equipped(item_slot, true);
}
return false;
}
void Player::CostumeHandler::SetCostumeInfo()
{
if (is_applyed_option_ == false)
{
// 메인 코스튬
if (owner_->char_info_.is_equipped_costume_ == true)
{
CInventorySlotContainer* const inventory_container =
static_cast<CInventorySlotContainer*>(owner_->GetItemManager()->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(owner_->char_info_.costume_item_pos_));
const BASE_ITEMINFO* item_info = item_slot.GetItemInfo();
if (item_info == NULL || item_info->m_wType != eITEMTYPE_COSTUME)
{
owner_->char_info_.is_equipped_costume_ = 0;
owner_->char_info_.costume_item_code_ = 0;
owner_->char_info_.costume_item_pos_ = INVALID_POSTYPE_VALUE;
return;
}
ApplyOption(item_slot, true);
}
// 데코 코스튬
if (owner_->char_info_.is_equipped_decorate_costume_ == true)
{
CInventorySlotContainer* const inventory_container =
static_cast<CInventorySlotContainer*>(owner_->GetItemManager()->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(owner_->char_info_.decorate_costume_item_pos_));
const BASE_ITEMINFO* item_info = item_slot.GetItemInfo();
if (item_info == NULL || item_info->m_wType != eITEMTYPE_COSTUME_DECORATE)
{
owner_->char_info_.is_equipped_decorate_costume_ = 0;
owner_->char_info_.decorate_costume_item_code_ = 0;
owner_->char_info_.decorate_costume_item_pos_ = INVALID_POSTYPE_VALUE;
return;
}
ApplyOption(item_slot, true);
}
}
}
#else
bool Player::CostumeHandler::Use(const SLOTCODE item_code, const POSTYPE item_pos)
{
CInventorySlotContainer* const inventory_container =
static_cast<CInventorySlotContainer*>(owner_->GetItemManager()->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(item_pos));
if (item_slot.GetItemInfo() == NULL || item_slot.GetItemInfo()->m_wType != eITEMTYPE_COSTUME)
{
return false;
}
// 코스튬 성별 검증..
if (item_slot.GetItemInfo()->IsCanUseGender(owner_->GetCharInfo()->gender) == false)
{
return false;
}
// 코스튬 클래스 검증..
const eCHAR_TYPE char_type = static_cast<eCHAR_TYPE>(owner_->GetCharInfo()->m_byClassCode);
if (item_slot.GetItemInfo()->CheckValidEquipClass(char_type) == false)
{
return false;
}
// 이미 착용 중..
if (owner_->char_info_.is_equipped_costume_)
{
// 착용 해제..
if (owner_->char_info_.costume_item_pos_ == item_pos)
{
return Equipped(item_slot, false);
}
// 이전 코스튬 해제 후 새로운 코스튬 착용..
else
{
SCItemSlot& previous_item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(owner_->char_info_.costume_item_pos_));
const BASE_ITEMINFO* precious_item_info = previous_item_slot.GetItemInfo();
if (precious_item_info && precious_item_info->m_wType == eITEMTYPE_COSTUME)
{
ApplyOption(previous_item_slot, false);
//Equipped(previous_item_slot, false);
}
else
{
owner_->char_info_.is_equipped_costume_ = 0;
owner_->char_info_.costume_item_pos_ = INVALID_POSTYPE_VALUE;
owner_->char_info_.costume_item_code_ = 0;
return false;
}
return Equipped(item_slot, true);
}
}
// 미 착용 중..
else
{
return Equipped(item_slot, true);
}
return false;
}
void Player::CostumeHandler::SetCostumeInfo()
{
if (owner_->char_info_.is_equipped_costume_ && is_applyed_option_ == false)
{
CInventorySlotContainer* const inventory_container =
static_cast<CInventorySlotContainer*>(owner_->GetItemManager()->GetItemSlotContainer(SI_INVENTORY));
SCItemSlot& item_slot =
static_cast<SCItemSlot&>(inventory_container->GetSlot(owner_->char_info_.costume_item_pos_));
const BASE_ITEMINFO* item_info = item_slot.GetItemInfo();
if (item_info == NULL || item_info->m_wType != eITEMTYPE_COSTUME)
{
owner_->char_info_.is_equipped_costume_ = 0;
owner_->char_info_.costume_item_code_ = 0;
owner_->char_info_.costume_item_pos_ = INVALID_POSTYPE_VALUE;
return;
}
ApplyOption(item_slot, true);
}
}
#endif //_NA_008610_20160104_ADD_DECORATECOSTUME
bool Player::CostumeHandler::Equipped(SCItemSlot& item_slot, const bool is_equipped)
{
if (is_equipped)
{
ApplyOption(item_slot, true);
#ifdef _NA_000000_20151126_MODIFY_COSTUME_CALCULATE_DATETIME
const BASE_ITEMINFO* const item_info = item_slot.GetItemInfo();
if (item_info &&
(item_info->m_ChargeSubType == eCHARGE_SUB_FIXED_AMOUNT_EX ||
item_info->m_ChargeSubType == eCHARGE_SUB_COORDINATE_EX ||
item_info->m_ChargeSubType == eCHARGE_SUB_FIXED_AMOUNT_EX2)
)
#endif //_NA_000000_20151126_MODIFY_COSTUME_CALCULATE_DATETIME
{
item_slot.FireUp_DateTime();
}
}
else
{
ApplyOption(item_slot, false);
}
//코스튬 정보 저장
#ifdef _NA_008610_20160104_ADD_DECORATECOSTUME
WORD item_type = item_slot.GetItemInfo()->m_wType;
if (item_type == eITEMTYPE_COSTUME)
{
owner_->char_info_.is_equipped_costume_ = is_equipped;
owner_->char_info_.costume_item_pos_ = is_equipped ? item_slot.GetPos() : INVALID_POSTYPE_VALUE;
owner_->char_info_.costume_item_code_ = is_equipped ? item_slot.GetItemCode() : 0;
//패킷전송
{
// 클라전송..
MSG_CG_ITEM_USE_COSTUME_ACK ack_msg;
ack_msg.Init();
ack_msg.is_equipped_costume_ = is_equipped;
ack_msg.costume_item_pos_ = is_equipped ? item_slot.GetPos() : INVALID_POSTYPE_VALUE;
owner_->SendPacket(&ack_msg, ack_msg.GetSize());
// BRD..
MSG_CG_ITEM_USE_COSTUME_BRD brd_msg;
brd_msg.Init();
brd_msg.object_key_ = owner_->GetObjectKey();
brd_msg.costume_item_code_ = is_equipped ? item_slot.GetItemCode() : 0;
owner_->SendPacketAround(&brd_msg,brd_msg.GetSize(),false);
}
}
else if (item_type == eITEMTYPE_COSTUME_DECORATE)
{
owner_->char_info_.is_equipped_decorate_costume_ = is_equipped;
owner_->char_info_.decorate_costume_item_pos_ = is_equipped ? item_slot.GetPos() : INVALID_POSTYPE_VALUE;
owner_->char_info_.decorate_costume_item_code_ = is_equipped ? item_slot.GetItemCode() : 0;
//패킷전송
{
// 클라전송..
MSG_CG_ITEM_USE_DECORATE_COSTUME_ACK ack_msg;
ack_msg.Init();
ack_msg.is_equipped_costume_ = is_equipped;
ack_msg.deco_costume_item_pos_ = is_equipped ? item_slot.GetPos() : INVALID_POSTYPE_VALUE;
owner_->SendPacket(&ack_msg, ack_msg.GetSize());
// BRD..
MSG_CG_ITEM_USE_DECORATE_COSTUME_BRD brd_msg;
brd_msg.Init();
brd_msg.object_key_ = owner_->GetObjectKey();
brd_msg.deco_costume_item_code_ = is_equipped ? item_slot.GetItemCode() : 0;
owner_->SendPacketAround(&brd_msg,brd_msg.GetSize(),false);
}
}
#else
owner_->char_info_.is_equipped_costume_ = is_equipped;
owner_->char_info_.costume_item_pos_ = is_equipped ? item_slot.GetPos() : INVALID_POSTYPE_VALUE;
owner_->char_info_.costume_item_code_ = is_equipped ? item_slot.GetItemCode() : 0;
//패킷전송
{
// 클라전송..
MSG_CG_ITEM_USE_COSTUME_ACK ack_msg;
ack_msg.Init();
ack_msg.is_equipped_costume_ = is_equipped;
ack_msg.costume_item_pos_ = is_equipped ? item_slot.GetPos() : INVALID_POSTYPE_VALUE;
owner_->SendPacket(&ack_msg, ack_msg.GetSize());
// BRD..
MSG_CG_ITEM_USE_COSTUME_BRD brd_msg;
brd_msg.Init();
brd_msg.object_key_ = owner_->GetObjectKey();
brd_msg.costume_item_code_ = is_equipped ? item_slot.GetItemCode() : 0;
owner_->SendPacketAround(&brd_msg,brd_msg.GetSize(),false);
}
#endif //_NA_008610_20160104_ADD_DECORATECOSTUME
// 로그를 기록한다.
{
TCHAR add_info[MAX_ADDINFO_SIZE] = { 0 };
::_sntprintf(add_info, MAX_ADDINFO_SIZE, _T("ACTIVE:%u"), (is_equipped ? 1 : 0));
add_info[MAX_ADDINFO_SIZE-1] = _T('\0');
GAMELOG->LogItem(ITEM_USE_COSTUME, owner_, &item_slot, 0, 1, NULL, NULL, add_info);
}
return true;
}
void Player::CostumeHandler::ApplyOption(SCItemSlot& item_slot, const bool is_add) {
const BASE_ITEMINFO* const item_info = item_slot.GetItemInfo();
if (item_info == NULL ||
(item_info->m_wType != eITEMTYPE_COSTUME && item_info->m_wType != eITEMTYPE_COSTUME_DECORATE)) {
return;
}
SkillAttrCalculator skill_calculator(owner_->GetAttr());
for (int op = 0; op < _countof(item_info->m_wOptionIndex); ++op) {
const int attr_option_index = item_info->m_wOptionIndex[op];
if (attr_option_index == 0) {
continue;
}
if (attr_option_index >= _countof(g_ITEM_OPTION)) {
continue;
}
const int attr_value = item_info->m_iOptionValue[op];
if (attr_value == 0) {
continue;
}
const int attr_value_type = item_info->m_byOptionType[op];
if (is_add) {
skill_calculator.AddAttr( \
g_ITEM_OPTION[attr_option_index], attr_value_type, attr_value, true);
} else {
skill_calculator.DelAttr( \
g_ITEM_OPTION[attr_option_index], attr_value_type, attr_value, true);
}
}
#ifdef _RU_006860_20130806_COSTUME_PCBANG_EFFECT
//PC방버프 적용여부
const bool pc_room_status = (owner_->CanApplyPCBangBenefit(true) != false);
if(pc_room_status == true) {
for (int op = 0; op < _countof(item_info->m_wPCOptionIndex); ++op) {
const int attr_option_index = item_info->m_wPCOptionIndex[op];
if (attr_option_index == 0) {
continue;
}
if (attr_option_index >= _countof(g_ITEM_OPTION)) {
continue;
}
const int attr_value = item_info->m_iPCOptionValue[op];
if (attr_value == 0) {
continue;
}
const int attr_value_type = item_info->m_byPCOptionType[op];
//피시방옵션이 적용된 상태에서 다시 피시방옵션을 적용하려는 사태와
//피시방옵션을 적용받고있지 않는 상태에서 피시방옵션수치만큼을 제거하려는 사태를 막기 위함.
if (is_add) {
skill_calculator.AddAttr( \
g_ITEM_OPTION[attr_option_index], attr_value_type, attr_value, true);
} else {
skill_calculator.DelAttr( \
g_ITEM_OPTION[attr_option_index], attr_value_type, attr_value, true);
}
}
}
#endif //_RU_006860_20130806_COSTUME_PCBANG_EFFECT
//xiaomi add Awakening
const AwakeningInfo* awakening_info = AwakeningInfoParser::Instance()->FindData(item_slot.GetCode());
if ( awakening_info != NULL ) {
if ( item_slot.GetAwakening() > 0 ) {
if ( item_slot.GetAwakening() <= MAX_AWAKENING_GRADE ) {
std::map<BYTE, DWORD>::const_iterator iter;
DWORD awaken = item_slot.GetAwakening();
iter = awakening_info->awakening_level_info_map.find(awaken);
if ( iter != awakening_info->awakening_level_info_map.end() ) {
DWORD awakening_effect = (iter->second);
if (is_add) {
skill_calculator.AddAttr( \
g_ITEM_OPTION[awakening_info->optKind], awakening_info->optType, awakening_effect, true);
} else {
skill_calculator.DelAttr( \
g_ITEM_OPTION[awakening_info->optKind], awakening_info->optType, awakening_effect, true);
}
}
iter = awakening_info->awakening_level_addinfo_map.find(awaken);
if ( iter != awakening_info->awakening_level_addinfo_map.end() ) {
DWORD awakening_addeffect = (iter->second);
if (is_add) {
skill_calculator.AddAttr( \
g_ITEM_OPTION[awakening_info->addKind], awakening_info->addType, awakening_addeffect, true);
} else {
skill_calculator.DelAttr( \
g_ITEM_OPTION[awakening_info->addKind], awakening_info->addType, awakening_addeffect, true);
}
}
}
}
}
owner_->GetAttr().UpdateEx();
const BOOL is_block = (is_add ? TRUE : FALSE);
item_slot.SetUserBlocked(is_block);
is_applyed_option_ = is_add;
}
//==================================================================================================
Player::Player()
: RENDER_EFFECT(m_RenderEffect)
, m_pServerSession ( NULL )
, m_NextExp( 0 )
, costume_handler_(this) //_NA_004035_20120227_COSTUME_ITEM
{
SetObjectType(PLAYER_OBJECT);
// CHANGES: f110307.2L
;{
ns_object::SearchReferences ref_option = { 0, };
ref_option.hp = &char_info_.m_iHP;
_RegisterSearchReference(ref_option);
};
// f100827.4L, singleton setting to a linker binding
new (&attributes_linker_) ns_object::PlayerAttributeLinker(this);
//
m_pItemManager = new ItemManager;
m_pSlotManager = new SlotManager;
m_pMissionManager = new MissionManager;
m_pQuestManager = new QuestManager_Concrete(&g_QuestInfoManager);
m_pQuickManager = new QuickManager;
//m_pStyleManager = new QuickStyleManager;
m_pMovementChecker = new MovementChecker(this);
m_pPVPInfo = new PVPInfo(this);
m_pFatigueManager = new FatigueManager;
m_pCompetitionInfo = new CompetitionInfo(this);
m_pACManager = new AchievementManager;
m_pSkillSlotManager = new SkillSlotManager;
perk_manager_ = NULL;
#ifdef _NA002217_100728_EP2_PERK_SYSTEM_
perk_manager_ = new nsSkill::PerkManager();
#endif
m_pEventSlotContainer = new EventInventorySlotContainer;
m_pWayPoint = new WayPoint;
m_pCharWayPoint = new CharWayPoint;
m_pChaoState = new ChaoState(this);
m_pInvitateFriend = new InvitateFriendInfo(this);
m_Attr.SetOwner(&attributes_linker_);
m_bExpiredPlayer = TRUE;
m_PreVillageMapCode = 0;
//_NA_0_20100722_AUTO_SYSTEM
m_lastReturnScrollMapCode = 0;
ZeroMemory(&m_lastReturnScrollPos, sizeof(m_lastReturnScrollPos));
m_byDebugInfoFlag = 0;
m_pCollectedList = new CollectedList;
#ifdef _NA_1419_20100121_ACCUMULATE_POINT
m_pAccumulatePoint = new AccumulatePoint(this);
#endif
#if defined (_NA_20100420_CHANGE_SYSTEM_MEMO_LOGIC) || \
defined (_NA_20100812_INTEGRATE_SYSTEM_MEMO_DOMINATION)
m_PostManager = new PostManager;
#endif
#ifdef _NA_003027_20111013_HONOR_SYSTEM
honor_manager_ = new HonorManager;
#endif //_NA_003027_20111013_HONOR_SYSTEM
#ifdef _NA004034_20120102_POINT_WALLET_SYSTEM
point_wallet_manager_ = new PointWalletManager;
#endif //_NA004034_20120102_POINT_WALLET_SYSTEM
//_NA_005026_20120527_CHAOS_ZONE_BATTLE_GROUND
battle_ground_player_info_ = new BattleGroundPlayerInfo(this);
#ifdef _NA_007065_20140217_CHAOSZONE_GOLDRUSH
goldrush_playerinfo_ = new GoldRushPlayerInfo(this);
#endif //_NA_007065_20140217_CHAOSZONE_GOLDRUSH
extra_info_manager_.Init(this);
#ifdef _NA_006633_20130415_ADD_BATTLEGROUND_ASSIST
player_assist_meter = new PlayerAssistMeter(this);
#endif //_NA_006633_20130415_ADD_BATTLEGROUND_ASSIST
#ifdef _NA_008069_20150210_ADD_SPA_SYSTEM
player_spa_info_ = new PlayerSpaInfo(this);
#endif //_NA_008069_20150210_ADD_SPA_SYSTEM
random_manager_ = new RandomManager;
// MUST BE last field
m_StartCheckCode = eVERIFY_MAGICCODE_INIT;
m_EndCheckCode = eVERIFY_MAGICCODE_INIT;
}
Player::~Player()
{
SAFE_DELETE( m_pPVPInfo );
SAFE_DELETE( m_pEventSlotContainer );
SAFE_DELETE( m_pSlotManager );
SAFE_DELETE( m_pItemManager );
SAFE_DELETE( m_pMissionManager );
SAFE_DELETE( m_pQuestManager );
SAFE_DELETE( m_pQuickManager );
//SAFE_DELETE( m_pStyleManager );
SAFE_DELETE( m_pMovementChecker );
SAFE_DELETE( m_pFatigueManager );
SAFE_DELETE( m_pACManager );
SAFE_DELETE( m_pCompetitionInfo );
SAFE_DELETE( m_pWayPoint );
SAFE_DELETE( m_pCharWayPoint );
SAFE_DELETE( m_pInvitateFriend );
SAFE_DELETE( m_pChaoState );
SAFE_DELETE( m_pSkillSlotManager );
#ifdef _NA002217_100728_EP2_PERK_SYSTEM_
SAFE_DELETE(perk_manager_);
#endif
SAFE_DELETE( m_pCollectedList );
#ifdef _NA_1419_20100121_ACCUMULATE_POINT
SAFE_DELETE( m_pAccumulatePoint );
#endif
#if defined (_NA_20100420_CHANGE_SYSTEM_MEMO_LOGIC) || \
defined (_NA_20100812_INTEGRATE_SYSTEM_MEMO_DOMINATION)
SAFE_DELETE( m_PostManager );
#endif
#ifdef _NA_003027_20111013_HONOR_SYSTEM
SAFE_DELETE( honor_manager_ )
#endif //_NA_003027_20111013_HONOR_SYSTEM
#ifdef _NA004034_20120102_POINT_WALLET_SYSTEM
SAFE_DELETE(point_wallet_manager_)
#endif //_NA004034_20120102_POINT_WALLET_SYSTEM
//_NA_005026_20120527_CHAOS_ZONE_BATTLE_GROUND
SAFE_DELETE(battle_ground_player_info_)
#ifdef _NA_007065_20140217_CHAOSZONE_GOLDRUSH
SAFE_DELETE(goldrush_playerinfo_)
#endif //_NA_007065_20140217_CHAOSZONE_GOLDRUSH
#ifdef _NA_006633_20130415_ADD_BATTLEGROUND_ASSIST
SAFE_DELETE(player_assist_meter)
#endif //_NA_006633_20130415_ADD_BATTLEGROUND_ASSIST
#ifdef _NA_008069_20150210_ADD_SPA_SYSTEM
SAFE_DELETE(player_spa_info_)
#endif //_NA_008069_20150210_ADD_SPA_SYSTEM
SAFE_DELETE(random_manager_)
// (CHANGES) (f100614.4L) add a slot type for a trigger area linker to support a multi-area link
trigger_links_.ns_trigger::TriggerPlayerLinkInGameSlots::~TriggerPlayerLinkInGameSlots();
m_StartCheckCode = eVERIFY_MAGICCODE_RELEASE;
m_EndCheckCode = eVERIFY_MAGICCODE_RELEASE;
}
BOOL Player::Init()
{
m_StartInitCode = eVERIFY_MAGICCODE_INIT;
RENDER_EFFECT.Clear();
ZeroMemory(&char_info_, sizeof(char_info_));
char_name_hash_ = 0; // NOTE: f110517.3L, this field is not cleared on 'Release'
m_bExpiredPlayer = FALSE;
m_PreVillageMapCode = 0;
//_NA_0_20100722_AUTO_SYSTEM
m_lastReturnScrollMapCode = 0;
ZeroMemory(&m_lastReturnScrollPos, sizeof(m_lastReturnScrollPos));
m_dwLastSendTick = GetTickCount();
m_dwSentSize = 0;
m_pVendorPage = NULL;
m_pSlotManager->Init( this );
m_pItemManager->Init( m_pSlotManager );
m_pSkillSlotManager->Init( m_pSlotManager );
m_pQuickManager->Init( m_pSlotManager );
//m_pStyleManager->Init( m_pSlotManager );
#ifdef _NA002217_100728_EP2_PERK_SYSTEM_
perk_manager_->Reuse(this);
#endif
m_pEventSlotContainer->ClearAll(); //< 이벤트 인벤토리 클리어
m_pEventSlotContainer->SetPlayer(this);
m_pMissionManager->Clear();
m_pQuestManager->Init(this);
m_pACManager->Init(this);
#ifdef _NA_1419_20100121_ACCUMULATE_POINT
m_pAccumulatePoint->Init( this );
#endif
m_pMovementChecker->Reset();
m_pWayPoint->Clear();
m_pCharWayPoint->Clear();
m_pChaoState->Init();
m_DBPUpdateTimer.SetTimer( _DBP_UPDATE_CYCLE_DELAY );
m_pMoveStateControl->Init( this, CMS_RUN );
// 아이템 쿨타임 설정
SetItemCoolTime( TRUE, SKILL_COOLTIME_MARGIN );
// 파티장 근처로 가기 쿨타임 설정
SetSkillCoolTime( eACTION_START_SKILL_CODE + eACTION_PARTY_MEMBER_SUMMON, SKILL_COOLTIME_MARGIN );
SetSkillCoolTime( eACTION_START_SKILL_CODE + eACTION_PARTY_MEMBER_WARP, SKILL_COOLTIME_MARGIN );
StopMoving();
SetTradeContainer(NULL);
SetVendorContainer(NULL);
SetBehave(PLAYER_BEHAVE_IDLE_STATE);
#ifdef _NA_008012_20150130_RANKING_SYSTEM
SetRankingSystemCtl(NULL);
#endif // _NA_008012_20150130_RANKING_SYSTEM
m_pPVPInfo->Init();
m_pCompetitionInfo->Init();
m_bDoingAction = FALSE;
m_byVkrReloadCount = MAX_VALKYRIE_RELOAD_COUNT;
m_ShapShotLogTimer.SetTimer( SNAPSHOT_LOG_CYCLE );
if (GetFatigueManager()->IsApplyFatigue() == TRUE) {
m_FatigueLogTimer.SetTimer( FATIGUE_LOG_CYCLE );
}
m_byLastAttackSeq = eATTACK_SEQUENCE_NONE;
m_byDebugInfoFlag = 0;
memset( &m_GMStateInfo, 0, sizeof(STATE_GM_INFO) );
//추가 이동속도비율 기본값 1
m_fAddMoveSpeedRatio = 1.0f;
SetMoveState( CMS_RUN );
#ifdef _NA_008012_20150130_RANKING_SYSTEM
for(INT i=0;i<MAX_MISSION_NUM+1;++i)
*(m_pBestMissionTime+i) = INT_MAX;
#endif // _NA_008012_20150130_RANKING_SYSTEM
m_ItemOwnFlag.Init();
GetWarehouseInventoryProtector().Init( this );
m_EndInitCode = eVERIFY_MAGICCODE_INIT;
m_bIsFallDead = FALSE;
m_bCheckedStat = FALSE;
m_PetManager.Init( this );
m_EtherManager.Init( this );
m_PlayerEvent.Init();
// (WAVERIX) (090614) (BUG-FIX) 초기화 하다 마는 문제 수정 (뭐냐, 이건... version-439 조xx xxx 퇴사자의 테러?)
// (WAVERIX) (090614) (NOTE) 이 로직은 마지막에 둘 것.
//if( Character::Init() )
//{
// // 초기화될때 다음 렙업 경험치 설정
// m_NextExp = 0;
// return TRUE;
//}
#ifdef __CN_OO0510__FATIGUE_MATCHING_UPDATE
m_wPlayTime = 0;
m_wOfflineTime = 0;
m_byAge = 0;
m_byRealNameAuth = 0;
#endif //__CN_OO0510__FATIGUE_MATCHING_UPDATE
UnexpectedUserEnterRoom() = FALSE;
m_GuildWarehouseRight = FALSE;
etheria_control_option_.Clear();
m_LoadingCheckTimer.Clear();
m_listPlayerSummonInfo.clear();
m_pCollectedList->Clear();
m_pInvitateFriend->ClearAllFriendList();
#ifdef _NA_003027_20111013_HONOR_SYSTEM
honor_manager_->init(this);
#endif //_NA_003027_20111013_HONOR_SYSTEM
#ifdef _NA004034_20120102_POINT_WALLET_SYSTEM
point_wallet_manager_->init(this);
#endif //_NA004034_20120102_POINT_WALLET_SYSTEM
random_manager_->Init();
#ifdef _NA_006633_20130415_ADD_BATTLEGROUND_ASSIST
player_assist_meter->Init();
#endif //_NA_006633_20130415_ADD_BATTLEGROUND_ASSIST
//__NA000000_090613_TRIGGER_LOGIC_REARRANGE__
// (CHANGES) (f100614.4L) add a slot type for a trigger area linker to support a multi-area link
trigger_links_.ns_trigger::TriggerPlayerLinkInGameSlots::~TriggerPlayerLinkInGameSlots();
new (&trigger_links_) ns_trigger::TriggerPlayerLinkInGameSlots(this);
new (&zone_transaction_info_) ns_object::PlayerZoneTransaction(this); // CHANGES: f110103.2L
// (WAVERIX) (090614) (NOTE) 이 로직은 마지막에 둘 것.
if(Character::Init())
{
// 초기화될때 다음 렙업 경험치 설정
m_NextExp = 0;
return TRUE;
}
return FALSE;
}
VOID Player::Release()
{
// CHANGES: f101115.3L
CloseSharedTransactions();
//
RENDER_EFFECT.Clear();
//SUNLOG( eFULL_LOG, "[%s]Release", GetCharName() );
m_pWayPoint->Clear();
m_pCharWayPoint->Clear();
m_pChaoState->Init();
m_pACManager->Release();
m_pQuestManager->Release();
m_pSlotManager->Release();
m_pQuickManager->Release();
//m_pStyleManager->Release();
#ifdef _NA002217_100728_EP2_PERK_SYSTEM_
perk_manager_->Reuse(NULL);
#endif
m_pItemManager->Release();
m_pSkillSlotManager->Release();
#ifdef _NA_1419_20100121_ACCUMULATE_POINT
m_pAccumulatePoint->Release();
#endif
// 쿨타임 해제
FreeAllSkillCoolTime( TRUE );
FreeItemCoolTime( TRUE );
{ // (WAVERIX) (090607) (BUG-FIX) 쉐도우 잘못된 타입 비교 구문 제거 및 Player일괄 제거로 수정.
#if !defined(_NA_000000_20140224_SUMMONER_REFACTORING)
DWORD summoner_key = GetSummonerKey();
if(summoner_key != 0) // auto-relase key
#endif //_NA_000000_20140224_SUMMONER_REFACTORING
SummonManager::Instance()->LeaveSummonNPCs(this);
}
// (WAVERIX) (090619) (NOTE) 이 것의 위치는 좀 더 따져볼 필요 있음.
Character::Release();
m_Attr.Clear();
//////////////////////////////////////////////////////////////////////////
m_bExpiredPlayer = TRUE;
m_PreVillageMapCode = 0;
//_NA_0_20100722_AUTO_SYSTEM
m_lastReturnScrollMapCode = 0;
ZeroMemory(&m_lastReturnScrollPos, sizeof(m_lastReturnScrollPos));
SetBehave(PLAYER_BEHAVE_IDLE_STATE);
#ifdef _NA_008012_20150130_RANKING_SYSTEM
SetRankingSystemCtl(NULL);
#endif // _NA_008012_20150130_RANKING_SYSTEM
m_pPVPInfo->Init();
m_pCompetitionInfo->Init();
//m_dwLastSendTick = GetTickCount();
m_dwSentSize = 0;
m_bDoingAction = FALSE;
m_byVkrReloadCount = MAX_VALKYRIE_RELOAD_COUNT;
//m_ShapShotLogTimer.SetTimer( SNAPSHOT_LOG_CYCLE );
m_byDebugInfoFlag = 0;
//추가 이동속도비율 기본값 1
m_fAddMoveSpeedRatio = 1.0f;
//SetMoveState( CMS_RUN );
m_PlayerEvent.Release();
// Destroy();
/* SetServerSession( NULL );
SetUserKey( 0 );
SetState( PLAYER_DESTROYED );*/
//////////////////////////////////////////////////////////////////////////
m_LoadingCheckTimer.Clear();
SSQHandler.Release(); // Init은 필요시에 수행된다.
Rider.Release();
Wings.Release(); // NOTE: f110315.2L
DestroySummonInfo();
m_pCollectedList->Clear();
// (CHANGES) (f100614.4L) add a slot type for a trigger area linker to support a multi-area link
trigger_links_.Disconnect(NULL);
//m_TriggerLink.Delete(); //__NA000000_090613_TRIGGER_LOGIC_REARRANGE__
new (&zone_transaction_info_) ns_object::PlayerZoneTransaction(this); // CHANGES: f110103.2L
m_pInvitateFriend->ClearAllFriendList();
// (NOTE) 맨 마지막 설정할 것.
m_StartInitCode = eVERIFY_MAGICCODE_RELEASE;
m_EndInitCode = eVERIFY_MAGICCODE_RELEASE;
}
VOID Player::Create( ServerSession * pServerSession, DWORD UserKey,
const TCHAR * pszID, const CHAR * pszIP,
time_t LoginTime_t )
{
ASSERT( pServerSession );
ASSERT( pServerSession && pServerSession->GetServerType() == AGENT_SERVER );
SetServerSession( pServerSession );
SetUserKey( UserKey );
SetState( PLAYER_CREATED );
SetUserID( pszID );
SetUserIP( pszIP );
SetLoginTime_t( LoginTime_t );
}
//
//VOID Player::Destroy()
//{
// SetServerSession( NULL );
// SetUserKey( 0 );
// SetState( PLAYER_DESTROYED );
//}
// CHANGES: f101115.3L, make an independent method
// for stable player closing process using shared data.
void Player::CloseSharedTransactions()
{
const ePLAYER_BEHAVE_STATE behave_state = GetBehave();
switch (behave_state)
{
case PLAYER_BEHAVE_TRADE_STATE:
{
SUNLOG(eFULL_LOG, "Release시거래상태[%s]", GetCharName());
Player* counter_player = GetTradeContainer()->GetCounterPlayer();
if (counter_player->GetBehave() != PLAYER_BEHAVE_TRADE_STATE) {
SUNLOG(eCRITICAL_LOG, "Release시플2거래상태[%s]이상", counter_player->GetCharName());
}
g_TradeManager.EndTrade(this);
MSG_CG_TRADE_CANCEL_CMD cancel_cmd;
counter_player->SendPacket(&cancel_cmd, sizeof(cancel_cmd));
}
break;
case PLAYER_BEHAVE_VENDOR_ESTABLISHER_STATE:
{
RC::eVENDOR_RESULT result = g_VendorManager.EndVendor(this);
if (result != RC::RC_VENDOR_SUCCESS) {
SUNLOG(eCRITICAL_LOG, "Release시밴더End실패[%s]", GetCharName());
}
MSG_CG_VENDOR_END_BRD msg_brd;
msg_brd.m_dwPlayerKey = GetObjectKey();
SendPacketAround(&msg_brd, sizeof(msg_brd), false);
}
break;
case PLAYER_BEHAVE_VENDOR_OBSERVER_STATE:
{
#if !defined(_NA_000000_20131031_GAMELOG_REDUCE)
GAMELOG->LogItem(ITEM_VENDOR_VIEW_END, this, NULL);
#endif //_NA_000000_20131031_GAMELOG_REDUCE
RC::eVENDOR_RESULT result = g_VendorManager.EndViewVendor(this);
if (result != RC::RC_VENDOR_SUCCESS) {
SUNLOG(eCRITICAL_LOG, "Release시밴더뷰End실패[%s]", GetCharName());
}
}
break;
};
//
if (m_pVendorPage != NULL)
{
RC::eVENDOR_LIST_RESULT result = g_VendorPageManager.EndVendorList(this);
if (result != RC::RC_VENDOR_LIST_SUCCESS) {
SUNLOG(eCRITICAL_LOG, "Release시 EndVendorList 실패[%s]", GetCharName());
}
}
#if !defined _NA_0_20110613_GAME_SHOP_RENEWAL
g_GameShopManager.OnPlayerDisconnect(this);
#endif
}
BOOL Player::Update(const util::ServerTimeInfoPerFrame* time_info_per_frame, BOOL bSectorUpdate)
{
static Player* pTHIS = NULL;
pTHIS = Player::VerifyNRestoreTHIS( this );
if( !pTHIS )
return FALSE;
// 아직 EnterField 전이면 Update를 실행하지 않는다.
GameField* pField = GetField();
if( !( pField && pField->IsReady() ) )
return TRUE;
// [SusunLove]
static struct
{
eCHAR_TYPE eCurType;
DWORD dwSummonerKey;
eCHAO_STATE eCharState;
LEVELTYPE Level;
DWORD UserKey;
eZONE_STATE eRoomState;
DWORD FieldPointer;
MAPCODE FieldCode;
INT SkillCount;
DWORD dwSectorIndex;
} DEBUG_STK;
DEBUG_STK.eCurType = GetCharType();
DEBUG_STK.dwSummonerKey = GetSummonerKey();
DEBUG_STK.eCharState = GetChaoState().GetChaoState();
DEBUG_STK.Level = GetLevel();
DEBUG_STK.UserKey = GetUserKey();
DEBUG_STK.eRoomState = GetGameZoneState();
DEBUG_STK.FieldPointer = static_cast<DWORD>(reinterpret_cast<__int64>(pField));
DEBUG_STK.FieldCode = m_GameFieldCode;
DEBUG_STK.SkillCount = GetSkillManager()->GetActiveSkillNum();
DEBUG_STK.dwSectorIndex = GetSectorIndex();
// <NOTICE WARNING>
Character* pCHAR = dynamic_cast<Character*>(pTHIS);
if (pCHAR->Update(time_info_per_frame->delta_tick_) == FALSE)
{
SUNLOG( eCRITICAL_LOG, "[%s:%d] Character::Update() fail", __FUNCTION__, __LINE__ );
// character object released or delete requested
return FALSE;
}
//
if( m_DBPUpdateTimer.IsExpired(FALSE) )
{
/// DBProxy로 업데이트 패킷을 날린다.
SerializeInfoToDBProxy(UPDATE_FACTOR_CHAR_UPDATE);
}
m_pItemManager->Update();
#ifdef _NA002217_100728_EP2_PERK_SYSTEM_
perk_manager_->Update(time_info_per_frame);
#endif
// 피로도 시스템
m_pFatigueManager->Update();
// 경쟁헌팅 인포 업데이트
m_pCompetitionInfo->Update();
// 카오상태 갱신
m_pChaoState->Update();
m_PetManager.Update();
m_EtherManager.Update();
#ifdef _NA_1419_20100121_ACCUMULATE_POINT
m_pAccumulatePoint->Update();
#endif
// 액션 딜레이 체크
if( IsDoingAction() && IsActionExpired() )
{
SetActionDelay( 0 );
}
// (WAVERIX) (081223) (NOTE) 띄엄띄엄 수정했더니 이후 수정 구조상 너무 복잡해진다. 일괄 블록 수정하기로 한다.
// (WAVERIX) (081004) (NOTE) 의 실천 사항
// (PURPOSE) 플레이어의 이동 상태 갱신 처리가 이동 중에 기반하고 있어서 이동 완료/정지 상태에서의
// Sector갱신이 안될 수 있는 동기화 문제 수정
BOOLEAN isMoved = FALSE;
{ // Move State Update
WzVector prevPos = *GetPosPtr();
SECTOR_IDX sectorIndexPrev = GetSectorIndex();
PlanEvent->Update(time_info_per_frame->delta_tick_, FALSE );
const WzVector* nextPos = GetPosPtr();
isMoved = memcmp( &prevPos, nextPos, sizeof(prevPos) ) != 0;
if( bSectorUpdate )
{
SECTOR_IDX sectorIndexLastest = pField->GetSectorIndex( nextPos );
pField->CheckAndApplyChangeSector( this, sectorIndexLastest );
// CHANGES: f110926.1L,
if (sectorIndexPrev != sectorIndexLastest) {
this->NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedPos);
};
}
};
if( isMoved )
{ // Move Constraints
const ePLAYER_BEHAVE_STATE behaveState = this->GetBehave();
switch( behaveState )
{
case PLAYER_BEHAVE_TRADE_STATE:
{ // 거래중일 경우 움직일 경우 취소
MSG_CG_TRADE_CANCEL_CMD cmsg;
Player* pPlayer = GetTradeContainer()->GetCounterPlayer();
if( pPlayer->GetBehave() != PLAYER_BEHAVE_TRADE_STATE )
{
SUNLOG( eCRITICAL_LOG, "Move시TRADE_STATE상태이상[1:%s][2:%s],%u",
GetCharName(), pPlayer->GetCharName(), pPlayer->GetBehave() );
}
else
{
pPlayer->SendPacket( &cmsg, sizeof(cmsg) );
}
g_TradeManager.EndTrade( this );
this->SendPacket( &cmsg, sizeof(cmsg) );
}
break;
}
};
SendChangedStatus();
#ifdef _NA_0_20110803_PLAYER_MOVEMENT_SYNC
m_pMovementChecker->Update_Moving_Turn(time_info_per_frame->delta_tick_);
#endif
Rider.Update(this, time_info_per_frame->delta_tick_);
Wings.Update(this, *time_info_per_frame);
// 스냅샷 로그 추가.
// 일정시간이 지나면 이 부분 호출..
if( m_ShapShotLogTimer.IsExpired() )
{
m_ShapShotLogTimer.Reset( SNAPSHOT_LOG_CYCLE );
GAMELOG->LogSnapShot( this );
}
if (GetFatigueManager()->IsApplyFatigue() == TRUE)
{
if( m_FatigueLogTimer.IsExpired() )
{
m_FatigueLogTimer.Reset( FATIGUE_LOG_CYCLE );
GAMELOG->LogFatigue( this );
}
}
return TRUE;
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Packet Send, SendEx
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
BOOL Player::SendPacket(MSG_BASE_FORWARD* forward_msg, WORD msg_size, BOOL need_compress)
{
__TOUCHED(need_compress);
forward_msg->m_dwKey = GetUserKey();
#ifdef _NA_0_20110613_GAME_SHOP_RENEWAL
const size_t kMaxPacketSize =
max(sizeof(MSG_CG_CHARINFO_CHAR_ITEM_CMD),
max(sizeof(MSG_CG_CHARINFO_SKILL_CMD),
max(sizeof(MSG_CG_CHARINFO_SKILL_PERK_CMD),
max(sizeof(MSG_CG_AREACONQUEST_STREAM_CMD),
max(sizeof(MSG_CG_SHOP_GIFT_STORAGE_LIST_ACK), 0)))));
#else
const size_t kMaxPacketSize =
max(sizeof(MSG_CG_CHARINFO_CHAR_ITEM_CMD),
max(sizeof(MSG_CG_CHARINFO_SKILL_CMD),
max(sizeof(MSG_CG_CHARINFO_SKILL_PERK_CMD),
max(sizeof(MSG_CG_AREACONQUEST_STREAM_CMD), 0))));
#endif
//----------------------------------------------------------------------------------------------
// for DEBUGGING
{
const DWORD curTick = GetTickCount();
if (curTick - m_dwLastSendTick >= 1000)
{
if (m_dwSentSize > (kMaxPacketSize * 4))
{
SUNLOG(eCRITICAL_LOG,
"["__FUNCTION__"] C:P={%u:%u} %d bytes have sent per second. UserKey(%d)",
forward_msg->m_byCategory, forward_msg->m_byProtocol, m_dwSentSize, GetUserKey() );
}
m_dwSentSize = 0;
m_dwLastSendTick = curTick;
}
m_dwSentSize += msg_size;
};
// 1) 꼭 필요한 것만 Server::PacketControl쪽에 등록해서 사용할 것
// 2) Agent에서 수신된 메시지의 내용을 참고할 것우 사용하지 말 것
// 3) 루프 돌면서 동일 메시지 플레이어마다 전송하지 말 것... 그걸 사용시 xxxAround 등의 함수를 사용할 것
nsPacket::PacketInfo packet_info = \
nsPacket::Server::PacketControl::Pack(GAME_SERVER, AGENT_SERVER, forward_msg, msg_size);
forward_msg = static_cast<MSG_BASE_FORWARD*>(packet_info.pMsg);
msg_size = packet_info.Size;
//
//----------------------------------------------------------------------------------------------
// for SENDING SIZE CONTROL
if (msg_size > kMaxPacketSize)
{
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"] C:P={%u:%u} Packet Size[%d] Access!! UserKey[%u]",
forward_msg->m_byCategory, forward_msg->m_byProtocol, msg_size, GetUserKey());
return false; // ...HUMMM...
};
//
//----------------------------------------------------------------------------------------------
if (GameField* const game_field = GetField()) {
game_field->SendPacket(this, forward_msg, msg_size);
return true;
};
//
return m_pServerSession->Send((BYTE*)forward_msg, msg_size);
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
BOOL Player::SendExPacket( DWORD dwNumberOfMessages, BYTE** pMsg, WORD* pwSize )
{
MSG_BASE_FORWARD* pSendMsg = (MSG_BASE_FORWARD*)pMsg[0];
pSendMsg->m_dwKey = GetUserKey();
DWORD TotSize = 0;
WORD* pwSizeOff = pwSize;
for( DWORD dwIdx=0 ; dwIdx<dwNumberOfMessages ; ++dwIdx, ++pwSizeOff )
TotSize += *pwSizeOff;
if( TotSize > 12000 )
{
SUNLOG( eCRITICAL_LOG, "[SendExPacket] TotSize[%u] is Big!! UserKey[%u]C[%d]P[%d]", TotSize, GetUserKey(), pSendMsg->m_byCategory, pSendMsg->m_byProtocol );
}
GameField* const pGameField = GetField();
return ( pGameField )
? pGameField->SendExPacket( this, dwNumberOfMessages, pMsg, pwSize ), TRUE
: m_pServerSession->SendEx( dwNumberOfMessages, pMsg, pwSize )
;
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
VOID Player::SendPacketAround( MSG_BASE_FORWARD * pMsg, WORD wSize, BOOL bSendToMe, GUILDGUID OtherGuildGuid)
{
//if( m_pViewPort->IsUsingViewPort() )
//{
// m_pViewPort->SendPacketAround( pMsg, wSize, bSendToMe, OtherGuildGuid );
//}
//else
//{
Character::SendPacketAround( pMsg, wSize, bSendToMe );
//}
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
VOID Player::SendExPacketAround( DWORD dwNumberOfMessages, BYTE** pMsg, WORD* pwSize, BOOL bSendToMe )
{
//if( m_pViewPort->IsUsingViewPort() )
//{
// m_pViewPort->SendExPacketAround( dwNumberOfMessages, pMsg, pwSize, !!bSendToMe, INVALID_GUILDGUID );
//}
//else
//{
Character::SendExPacketAround( dwNumberOfMessages, pMsg, pwSize, bSendToMe );
//}
}
//────────────────────────────────────────────────────────────────
// CHANGES: f110531.4L, remove the broadcast upper boundary limitation using 1toN broadcaster
void Player::SendPacketForSync_(const BYTE _TAG, WAR_TAG_V* const IN msg, const WORD msg_size)
{
GameField* const game_field = GetField();
if (game_field == NULL) {
return;
};
MSG_AG_CONNECTION_PACKET_UNICASTER& unicast = MSG_COMPOSE_UNICASTER::InstanceOfBase();
MSG_CG_SYNC_COMPOSED_STREAM_CMD msg_cmd;
msg_cmd.m_Header->size = msg_size;
unicast.Init(&msg_cmd, sizeof(msg_cmd));
msg->_TAG = _TAG;
unicast.AppendMsg(reinterpret_cast<uint8_t*>(msg), msg_size);
uint16_t* const user_begin = unicast.GetUsersStartPtr();
unicast.AddUser(user_begin, this->GetObjectKey());
this->SendPacket(&unicast, unicast.GetSendingSize());
//
return;
#if 0
GameField* const pField = GetField();
if( !pField )
return;
const DWORD object_key = GetObjectKey();
MSG_AG_CONNECTION_PACKET_UNICASTER& rMsgUNI = MSG_COMPOSE_UNICASTER::InstanceOfBase();
MSG_CG_SYNC_COMPOSED_STREAM_CMD msg;
msg.m_Header->size = wSize;
rMsgUNI.Init( &msg, sizeof(msg) );
pMsg->_TAG = _TAG;
rMsgUNI.AppendMsg( reinterpret_cast<BYTE*>(pMsg), wSize );
DWORD* const pStartPtr = rMsgUNI.GetUsersStartPtr();
rMsgUNI.AddUser( pStartPtr, object_key );
SendPacket( &rMsgUNI, rMsgUNI.GetSendingSize() );
#endif
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
BOOL Player::SendToGuildServer( MSG_BASE_FORWARD * pMsg, WORD wSize )
{
ASSERT( g_pGameServer );
pMsg->m_dwKey = GetUserKey();
return g_pGameServer->SendToServer( GUILD_SERVER, pMsg, wSize );
}
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
BOOL Player::SendToGameDBPServer( MSG_BASE_FORWARD * pMsg, WORD wSize )
{
ASSERT( g_pGameServer );
pMsg->m_dwKey = GetUserKey();
return g_pGameServer->SendToServer( GAME_DBPROXY, pMsg, wSize );
}
//
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
VOID Player::ForceDisconnect( BYTE Reason )
{
MSG_AG_CONNECTION_UNREGISTER_CMD msg;
msg.m_byDisonnectCode = Reason;
SendPacket( &msg, sizeof(msg) );
}
//-------------------------------------------------------------------------------------------------
// 상태 변화
//-------------------------------------------------------------------------------------------------
// !!!!!!
#include "PVPManager.h"
#include "GamePVPRoom.h"
// below interfaces is extracted from the SCPlayer
// Item Cool Timer
void Player::SetItemCoolTime(BOOL bPoolUse, DWORD cool_time_margin)
{
// 쿨타임 타이머 해쉬 테이블 생성
CoolTimerFactory* cool_timer_factory = CoolTimerFactory::Instance();
ItemInfoParser* item_parser = ItemInfoParser::Instance();
const ITEM_COOLTIME_MAP* script_item_cool_time_table = item_parser->GetItemCoolTimeMap();
ITEM_COOLTIME_MAP::const_iterator it = script_item_cool_time_table->begin(),
end = script_item_cool_time_table->end();
for ( ; it != end; ++it)
{
const ITEM_COOLTIME_INFO& cool_time_info = it->second;
util::Timer* timer = cool_timer_factory->Alloc();
DWORD interval = cool_time_info.m_dwCoolTime > cool_time_margin
? cool_time_info.m_dwCoolTime - cool_time_margin
: cool_time_info.m_dwCoolTime;
timer->SetTimer(interval);
timer->InitCoolTime();
item_cool_time_table_[cool_time_info.m_wCoolTimeType] = timer;
}
}
void Player::FreeItemCoolTime(BOOL bPoolUse)
{
CoolTimerFactory* cool_timer_factory = CoolTimerFactory::Instance();
FOREACH_CONTAINER(const COOL_TIME_TABLE::value_type& node, item_cool_time_table_,
COOL_TIME_TABLE)
{
util::Timer* timer = node.second;
cool_timer_factory->Free(timer);
}
item_cool_time_table_.clear();
}
util::Timer* Player::GetItemCoolTimer(WORD wType)
{
COOL_TIME_TABLE::const_iterator found = item_cool_time_table_.find(wType);
if (found == item_cool_time_table_.end()) {
return NULL;
}
util::Timer* timer = found->second;
return timer;
};
void Player::InitializeItemCooltime()
{
FOREACH_CONTAINER(const COOL_TIME_TABLE::value_type& node, item_cool_time_table_,
COOL_TIME_TABLE)
{
util::Timer* timer = node.second;
timer->InitCoolTime();
}
}
VOID
Player::SetBehave( ePLAYER_BEHAVE_STATE state )
{
m_BehaveState = state;
// Agent로 알림
MSG_AG_STATUS_CHANGE_STATE_CMD cmsg;
cmsg.m_eBehaveState = m_BehaveState;
SendPacket( &cmsg, sizeof(cmsg) );
}
nsSync::CSSyncTuner* Player::GetSyncTuner() {
return m_pMovementChecker->GetSyncTuner();
}
VOID
Player::OnDead()
{
// (WAVERIX)(080910)(NOTE) 충분히 지저분해졌으므로 정리한다. 해당 매크로는 의존성을 설정할 예정
// - (Addin) 중복 처리되는 부분 삭제, 타락한 사원 코드 추가
const DWORD DEFAULT_SKIP = 10;
//////////////////////////////////////////////////////////////////////////
// <LOGIC CONTROL>
FlowControl flow;
FLOWCONTROL_START()
{
const DWORD object_key = GetObjectKey();
const DWORD curHP = GetHP();
SASSERT( curHP == 0, "[Player::OnDead] GetHP() != 0" ); //... 이것은 전혀 처리하지 않고 있다.
Character::OnDead();
{
// 죽었으면 적립된 HP를 0으로 한다.
SetDeadReserveHP( GetReserveHP() ); //현재 적립된 HP의 50%를 둔다.
SetReserveHP(0);
};
{ // 기타 내부 게임로직 처리 (주변에 알림과 순서 역전)
// #1) 소환체를 삭제한다.
SummonManager::Instance()->LeaveSummonNPCs( this );
// #2) __NA_0_PET_SYSTEM
m_PetManager.OnDead();
};
{ // Agent로 알림
MSG_AG_STATUS_DEAD_CMD msgCMD;
SendPacket( &msgCMD, sizeof(msgCMD) );
};
// 플레이어 사망 로그
MAPCODE mapcode = 0;
if( GetGameZonePtr() )
mapcode = GetGameZonePtr()->GetMapCode();
GAMELOG->WritePlayerDead( this, mapcode );
// LAST!!!
GameZone* const pGameZone = GetGameZonePtr();
GameField* const pGameField = GetField();
if( !(pGameZone && pGameField) )
break;
// Field관련 기타 코드들.
const eZONETYPE zone_type = pGameZone->GetZoneType();
switch( zone_type )
{
case eZONETYPE_PVP:
{
// (OLD COMMENT) !!!!!!!임시!!!!!!!!
// (WAVERIX)(080910)(NOTE) 정말 임시이길 바라지만, 정말 장수하고 있는 코드다.-.-;
if( ePRS_AT_PVP == GetGameZoneState() )
{
GamePVPRoom* const pPVPRoom = static_cast<GamePVPRoom *>( GetGameZonePtr() );
Character* const pKiller = pGameField->FindCharacter( m_dwKillerObjectKey );
pPVPRoom->GetPVPManager()->OnPVPResult( pKiller, this );
}
}
break;
//_NA_005026_20120527_CHAOS_ZONE_BATTLE_GROUND
case eZONETYPE_INSTANCE:
{
if(ePRS_AT_INSTANCE == GetGameZoneState() && GetField())
{
Character* const killer = GetField()->FindCharacter(m_dwKillerObjectKey);
#ifdef _NA_007065_20140217_CHAOSZONE_GOLDRUSH
ChaosZoneManager* chaoszone_manager = GetChaosZoneManager();
if (chaoszone_manager != NULL) {
chaoszone_manager->DeadCharacter(this, killer);
}
#else
if (IsThereBattleGroundZone() == true)
{
BattleGround* const battle_ground = \
static_cast<BattleGround*>(GetCurrentChaosZone());
if (battle_ground) {
battle_ground->DeadCharacter(this, killer);
}
}
#endif //_NA_007065_20140217_CHAOSZONE_GOLDRUSH
}
}
break;
// (WAVERIX)(080910)(NOTE)
// 기타 특수 이벤트는 여기에 걸 것. 이렇게 하는 이유... 함수 분기도 나중에 가면 정말 유지보수 하기 힘들다.
// 물론 여기에 특수 사례로 걸기 시작하면 그것 또한 유지보수하지 힘든 것은 마찬가지이다.
// 여기서 특수 이벤트를 발동시킬 경우는 일반화된 호출을 수행할 수 있도록 하자.
//case
}
if( Policyment.FIELD & Policyment.PCC_IN_SSQ_FIELD_PLAYER )
{
if( zone_type != eZONETYPE_INSTANCE )
break;
Character* const pCharacter = pGameField->FindCharacter( m_dwKillerObjectKey );
GameWarControlManager::OnMsg(
GameSSQCtrlCmd::CharacterEvent::OnDead( this, pCharacter ) // no-null check
, static_cast<GameInstanceDungeon*>(pGameZone)
);
}
return;
}
FLOWCONTROL_END;
return;
}
//-------------------------------------------------------------------------------------------------
// 위치 변화
//-------------------------------------------------------------------------------------------------
BOOL
Player::ExecuteThrust( BOOL bForcedMove, WzVector *pwvMoveDistance, WzVector &wvPosAfterThrust, FLOAT fMoveDistance, BOOL bDownAfterThrust )
{
GameField *pField = GetField();
if( !pField )
{
return FALSE;
}
// TODO: 밀리는 방향이 없는 경우 문제가 생긴다. 랜덤으로??
if (pwvMoveDistance->x == 0.0f && pwvMoveDistance->y == 0.0f )
{
pwvMoveDistance->x = 1.0f;
WzVector wvTemp = *pwvMoveDistance;
Math_ZRotateVector( &wvTemp, *pwvMoveDistance, rand() % 360 );
*pwvMoveDistance = *pwvMoveDistance * fMoveDistance;
}
// Player 밀리기 처리
if( !pField->FindPathThrust( this, pwvMoveDistance, bForcedMove ) )
{
return FALSE;
}
wvPosAfterThrust = GetPathExplorer()->GetTargetPos();
SetMoveState( CMS_SWIPE );
// 이동량 체크를 위해 누적
WzVector vCurPos; GetPos( &vCurPos );
MovementChecker* pCHECKER = GetMovementChecker();
pCHECKER->GetServerPathExpUpdate()->SetupLatestState( pCHECKER, .0f, 0, vCurPos, vCurPos );
return TRUE;
}
//-------------------------------------------------------------------------------------------------
// 아이템 관련
//-------------------------------------------------------------------------------------------------
RC::eITEM_RESULT Player::CanUseItem(const BASE_ITEMINFO* pItemInfo)
{
if( !pItemInfo )
return RC::RC_ITEM_COOLTIME_ERROR;
if( GetField()==NULL )
{
return RC::RC_ITEM_INVALIDSTATEOFPLAYER;
}
if( !GetStatusManager()->Status.IsPassConstraint( this, pItemInfo ) )
return RC::RC_ITEM_UNUSABLE_FUNCTION;
#ifdef _NA_008679_20160202_REWORK_BATTLEGROUND
if (IsThereBattleGroundZone() == true)
{
if (pItemInfo->m_ChargeType == eCHARGE_CHARGE &&
eITEMTYPE(pItemInfo->m_wType) == eITEMTYPE_WASTE)
{
return RC::RC_ITEM_CANNOT_USE_BATTLEGROUND;
}
}
#endif //_NA_008679_20160202_REWORK_BATTLEGROUND
// 쿨타임이 있을 경우 체크
util::Timer * pTimer = GetItemCoolTimer( pItemInfo->m_wCoolTimeType );
if( pTimer )
{
if( !pTimer->IsExpired( FALSE ) )
{
return RC::RC_ITEM_COOLTIME_ERROR;
}
}
//__CN_1299_WASTE_ITEM_LEVEL_LIMITE_CHECK
if( pItemInfo->m_LimitUseLevel != 0 && GetLevel() < pItemInfo->m_LimitUseLevel )
return RC::RC_ITEM_CANNOT_USE_PLAYER_LEVEL;
#ifdef _NA_20100307_BETAKEY_SYSTEM
if(!GetBetaKeyInfo()->CanAbilityContent(pItemInfo->m_betakey))
{
return RC::RC_ITEM_CANNOT_USE_BETAKEY;
}
#endif
return RC::RC_ITEM_SUCCESS;
}
VOID Player::ResetUseItem( WORD wCoolTimeType )
{
util::Timer * pTimer = GetItemCoolTimer( wCoolTimeType );
if( pTimer )
{
pTimer->Reset();
}
}
VOID Player::SetActionDelay( DWORD delay )
{
m_ActionTimer.SetTimer( delay );
if( delay )
{
m_bDoingAction = TRUE;
}
else if( m_bDoingAction )
{
m_bDoingAction = FALSE;
MSG_CG_SYNC_ACTION_EXPIRED_CMD msg;
SendPacket( &msg, sizeof(msg) );
}
}
DWORD Player::GetActionDelay()
{
return m_ActionTimer.GetIntervalTime();
}
BOOL Player::IsActionExpired()
{
if( m_ActionTimer.IsExpired() )
{
return TRUE;
}
return FALSE;
}
VOID Player::ApplyPenalty()
{
//레벨 10이하는 패널치 적용 없음
if( GetLevel() < MIN_PENALTY_LEVEL ) return;
// Zone에 따라 패널티를 안 받는 경우가 있다.
GameZone *pZone = GetGameZonePtr();
if( !pZone ) return;
// 내구도 감소
FLOAT fDuraDecRatio = 0;
if( ePRS_AT_CHUNTING == GetGameZoneState() )
{
GameCHuntingRoom *pCHunting = (GameCHuntingRoom*)GetGameZonePtr();
fDuraDecRatio = GetDuraDecRatio( GetGameZoneState(), (eCOMPETITION_MODE_TYPE)pCHunting->GetAdditionalCHuntingInfo()->m_Mode );
}
//_NA_005026_20120527_CHAOS_ZONE_BATTLE_GROUND
else if (ePRS_AT_INSTANCE == GetGameZoneState())
{
GameInstanceDungeon* const instance_dugeon =
static_cast<GameInstanceDungeon*>(GetGameZonePtr());
fDuraDecRatio = GetDuraDecRatio(GetGameZoneState(), instance_dugeon->GetChaosZoneType());
}
else
{
fDuraDecRatio = GetDuraDecRatio( GetGameZoneState(), (eCOMPETITION_MODE_TYPE)0);
}
if (nsSlot::DurabilityImpl* const impl = this->GetItemManager()->GetDurabilityImpl()) {
impl->DecreaseDuraAll(fDuraDecRatio);
};
}
BOOL Player::IsPvPAtt(WORD PTATroubld)
{
if( !GetGameZonePtr() ) return FALSE;
return GetGameZonePtr()->IsPvPAtt(this,PTATroubld);
}
//_NA_008706_20160229_TEXTLOG_PK
VOID Player::KillPlayer(Player* pDeadPlayer, BOOL bSummoned, DAMAGETYPE damage_value /*= 0*/)
{
if( !GetGameZonePtr() ) return;
GetGameZonePtr()->KillPlayer(this,pDeadPlayer,bSummoned, damage_value);
}
//공격 성향 채크.
//
RC::eBATTLE_RESULT Player::ChkAttackPvP( Character* pTargetChar )
{
#ifdef _NA_000000_20140224_SUMMONER_REFACTORING
if (pTargetChar == NULL) {
return RC::RC_BATTLE_FAILED;
}
if (pTargetChar->IsEqualObjectKind(SUMMON_OBJECT)) {
Character* const summon_owner = GetField()->FindCharacter(pTargetChar->GetSummonerKey());
return ChkAttackPvP(summon_owner);
}
#else
// 소환체 선처리
SummonedNPCs *pSummonedNPCs = SummonManager::Instance()->FindSummonNPCs( pTargetChar->GetSummonerKey() );
if( pSummonedNPCs )
{
// 타겟의 소환자기준 체크
Character *pSummoner = pSummonedNPCs->GetSummoner();
if( pSummoner && pTargetChar != pSummoner )
{
return ChkAttackPvP( pSummoner );
}
}
#endif //_NA_000000_20140224_SUMMONER_REFACTORING
//.//
RC::eBATTLE_RESULT ErrCode = RC::RC_BATTLE_SUCCESS;
if(pTargetChar->IsEqualObjectKind(PLAYER_OBJECT))
{
// 소환등을 하고 존을 떠난 경우 소유자가 유효한 존에 없을수 있다.
if( !GetGameZonePtr() )
return RC::RC_BATTLE_FAILED;
// 각 존에 존재하는 룰에 따른다.
GetGameZonePtr()->CanPVP(this,(Player*)pTargetChar,&ErrCode);
}
return ErrCode;
}
RC::eSKILL_RESULT Player::ChkSkillPvP( Character* pTargetChar )
{
#ifdef _NA_000000_20140224_SUMMONER_REFACTORING
if (pTargetChar->IsEqualObjectKind(SUMMON_OBJECT)) {
Character* const summon_owner = GetField()->FindCharacter(pTargetChar->GetSummonerKey());
if (summon_owner)
{
return ChkSkillPvP(summon_owner);
}
}
#else
// 소환체 선처리
SummonedNPCs *pSummonedNPCs = SummonManager::Instance()->FindSummonNPCs( pTargetChar->GetSummonerKey() );
if( pSummonedNPCs )
{
// 타겟의 소환자기준 체크
Character *pSummoner = pSummonedNPCs->GetSummoner();
if( pSummoner && pTargetChar != pSummoner )
{
return ChkSkillPvP( pSummoner );
}
}
#endif //_NA_000000_20140224_SUMMONER_REFACTORING
//.//
RC::eSKILL_RESULT ErrCode = RC::RC_SKILL_SUCCESS;
if(pTargetChar->IsEqualObjectKind(PLAYER_OBJECT))
{
// 소환등을 하고 존을 떠난 경우 소유자가 유효한 존에 없을수 있다.
if( !GetGameZonePtr() )
return RC::RC_SKILL_FAILED;
// 각 존에 존재하는 룰에 따른다.
GetGameZonePtr()->CanPVP(this,(Player*)pTargetChar,&ErrCode);
}
return ErrCode;
}
/*
// CHANGES, f100828.4L, removed
BASE_PLAYERINFO* Player::GetCharInfo()
{
m_Attr.GetCharInfo( GetCharInfo() );
return GetCharInfo();
}
*/
eUSER_RELATION_TYPE Player::IsFriend( Character *pTarget )
{
if (obj_util::IsValid(pTarget, __FUNCTION__, __LINE__) == false) {
return USER_RELATION_NEUTRALIST;
}
// 본인인지 체크한다.
if( GetObjectKey() == pTarget->GetObjectKey() ) return USER_RELATION_FRIEND;
if( this == pTarget ) return USER_RELATION_FRIEND;
#if !defined(_NA_000000_20140224_SUMMONER_REFACTORING)
// 소환체 선처리
SummonedNPCs *pSummonedNPCs = SummonManager::Instance()->FindSummonNPCs( pTarget->GetSummonerKey() );
if( pSummonedNPCs )
{
// 타겟의 소환자가 아군이라면 타겟도 아군이 된다.
Character *pSummoner = pSummonedNPCs->GetSummoner();
if( pSummoner && pTarget != pSummoner )
{
return IsFriend( pSummoner );
}
}
#endif //_NA_000000_20140224_SUMMONER_REFACTORING
if( Policyment.FIELD & Policyment.PCC_IN_SSQ_FIELD )
{
// SSQMonster는 몬스터들 끼리도 적으로 인식한다.
// 트랩 계열 NPC들을 공격하는 것을 방지하기 위해서 이들만 친구로 인식하도록 한다.
if(pTarget->IsEqualObjectKind(MAPNPC_OBJECT))
return USER_RELATION_FRIEND;
if(!pTarget->IsEqualObjectKind(SUMMON_OBJECT))
return USER_RELATION_ENEMY;
}
//.//
eOBJECT_TYPE eObjectType = pTarget->GetObjectType();
switch(eObjectType)
{
//.// 몬스터
case MONSTER_OBJECT:
case LUCKYMON_OBJECT:
case SSQMONSTER_OBJECT:
case TOTEMNPC_OBJECT: // 소환체 선처리의 예외 상황.
return USER_RELATION_ENEMY;
#ifdef _NA_000000_20140224_SUMMONER_REFACTORING
case SUMMON_OBJECT:
{
Character* summon_owner = NULL;
GameField* game_field = GetField();
DWORD summoner_key = pTarget->GetSummonerKey();
if( game_field == NULL ) {
summon_owner = PlayerManager::Instance()->FindPlayerByObjKey(summoner_key);
}
else {
summon_owner = game_field->FindCharacter(pTarget->GetSummonerKey());
}
if (summon_owner != NULL) {
return IsFriend(summon_owner);
}
} break;
#endif //_NA_000000_20140224_SUMMONER_REFACTORING
case MAPNPC_OBJECT:
case CRYSTALWARP_OBJECT:
return pTarget->IsFriend(this); // 수평적 호출로 인해 재귀 가능성 있다.
case FRIEND_MONSTER_OBJECT:
return USER_RELATION_FRIEND;
case SYNC_MERCHANT_OBJECT:
return USER_RELATION_NEUTRALIST;
//.// PVP
case PLAYER_OBJECT:
{
// 소환등을 하고 존을 떠난 경우 소유자가 유효한 존에 없을수 있다.
if( !GetGameZonePtr() )
return USER_RELATION_NEUTRALIST;
// 각 존에 존재하는 룰에 따른다.
return GetGameZonePtr()->IsFriendPVP(this,(Player*)pTarget);
}
break;
default:
SUNLOG( eCRITICAL_LOG, "Player::IsFriend Target 타입[%d]", eObjectType );
break;
}
return USER_RELATION_NEUTRALIST;
}
VOID Player::SetDBCharState( eDBCHAR_STATE stat, WORD wMinutes )
{
/*
GetCharInfo()->m_byCharState = stat;
switch( stat )
{
case DBCHAR_STATE_CHAR_BLOCK:
{
//WZTime::GetTime( 3, GetCharInfo()->m_tszStateTime, MAX_TIMEDATA_SIZE );
//추가적인 시간 기록
}
break;
}
*/
}
CWarehouseSlotContainer * Player::GetWarehouseContainer() const
{
return (CWarehouseSlotContainer *)GetItemManager()->GetItemSlotContainer(SI_WAREHOUSE);
}
CGuildWarehouseSlotContainer * Player::GetGuildWarehouseContainer()
{
return (CGuildWarehouseSlotContainer *)GetItemManager()->GetItemSlotContainer(SI_GUILDWAREHOUSE);
}
//부활
BOOL Player::OnResurrection( FLOAT fRecoverExp, FLOAT fRecoverHP, FLOAT fRecoverMP, Player *pHealer )
{
if( Character::OnResurrection( fRecoverExp, fRecoverHP, fRecoverMP, pHealer ) )
{
if( GetCharType() == eCHAR_BERSERKER )
{
SetMP( 0 );
}
else if(GetCharType() == eCHAR_DRAGON)
{
#ifdef _NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT
// 용변신 상태에서 죽으면
// 용변신이 풀리면서 HP 가 제 계산이 되어야 하는데 이미 죽은 상태여서 HP가 계산이 되지 않음
// 부활시 아이템 계산을 한번더 해줌
if (ItemManager* item_manager = GetItemManager())
{
item_manager->UpdateItemAttributes(true, false);
}
#endif //_NA_002289_20110322_EQUIPMENTS_CALCULATOR_CHANGES_FOR_IMPROVEMENT
}
//힐러가 존재 하지 않으면 안전지대에서 부활이나, 마을로 부활이다... 이경우는 리커버리를 준다.
if( !pHealer )
{
DWORD dwReserveHP = GetHP() / 2 + GetDeadReserveHP() / 2;
if( dwReserveHP >= ( GetMaxHP() - GetHP() ) )
SetReserveHP( GetMaxHP() - GetHP() );
else
SetReserveHP( dwReserveHP );
}
SetDeadReserveHP( 0 );
//{__NA_001290_20090525_SHIELD_SYSTEM
const DWORD curSD = GetSD();
if( curSD )
{
ALLOC_STATUS_PAIR pairBaseStatus = m_pStatusManager->AllocStatus( eCHAR_STATE_ETC_EXIST_SHELD_POINT );
if( pairBaseStatus.second )
pairBaseStatus.second->SendStatusAddBRD();
}
//}
// 강철 모드 세팅.
// 강철 상태의 적용시점이 섹터 진입 이후 이므로 SendStatusAddBRD를 통해 brd한다.
// 자살(안전지대이동) -> 섹터진입 -> OnResurrection
int steel_time = REBIRTH_STEEL_MODE_TIME;
// 점령전에서 강철 상태 악용으로 변경.
if (GetGameZonePtr()->GetZoneType() == eZONETYPE_DOMINATION_FIELD)
{
steel_time = DOMINATION_REBIRTH_STEEL_MODE_TIME;
}
// 전장에서는 강철 모드를 사용 하지 않는다.(무적모드사용)
//_NA_005026_20120527_CHAOS_ZONE_BATTLE_GROUND
#ifdef _NA_007065_20140217_CHAOSZONE_GOLDRUSH
if (GetChaosZoneType() == kBattleGround || GetChaosZoneType() == eCHAOSZONE_TYPE_GOLDRUSH) {
steel_time = 0;
}
#else
if (IsThereBattleGroundZone()) {
steel_time = 0;
}
#endif //_NA_007065_20140217_CHAOSZONE_GOLDRUSH
if (steel_time > 0)
{
ALLOC_STATUS_PAIR steel =
m_pStatusManager->AllocStatus(eCHAR_STATE_STEEL, steel_time);
if (steel.second)
steel.second->SendStatusAddBRD();
#ifdef _CN_000000_20130731_STATE_STEEL_MOD_BUG_LOG
SUNLOG(eCRITICAL_LOG, "["__FUNCTION__"] : AllocStatus(eCHAR_STATE_STEEL)");
#endif // _CN_000000_20130731_STATE_STEEL_MOD_BUG_LOG
}
//
// 플레이어 부활 로그 남기기
if( GetField() && GetField()->GetGameZone() )
{
GAMELOG->WritePlayerResurrection( this, pHealer );
}
// 플레이어 부활 로그 남기기 끝
// 파티와 관계 되므로 Agent에 보내서 처리한다.
MSG_AG_STATUS_RESURRECTION_CMD cmdMsg;
cmdMsg.m_dwCurHP = GetHP();
cmdMsg.m_dwCurMP = GetMP();
cmdMsg.m_dwCurSD = GetSD();
NotifyChangedStatus(PlayerStatEvent::eFlag_ChangedSD);
SendPacket( &cmdMsg, sizeof(cmdMsg) );
// (WAVERIX) (090506) (BUG-FIX) DEAD와 RESURRECTION의 패킷 순서 보정
{
MSGSUB_SYNC_CHARACTER_RESURRECTION_BRD msg;
msg.m_dwObjectKey = GetObjectKey();
msg.m_dwHP = GetHP();
msg.m_dwMP = GetMP();
#ifdef _NA_006638_20120415_ADD_SHIELD_INFO_IN_TARGET_INFO
msg.m_dwSD = GetSD();
#endif //_NA_006638_20120415_ADD_SHIELD_INFO_IN_TARGET_INFO
msg.m_wvCurPos = *GetPosPtr();
msg.m_dwReserveHP = GetReserveHP();
SendPacketAroundForSync(&msg, TRUE);
};
// // 부활을 알려주자.
// MSG_CG_STATUS_RESURRECTION_BRD sendMsg;
// sendMsg.m_dwObjectKey = GetObjectKey();
// sendMsg.m_dwHP = GetHP();
// sendMsg.m_dwMP = GetMP();
// GetPos(&sendMsg.m_wvCurPos);
// sendMsg.m_dwReserveHP = GetReserveHP();
// SendPacketAround( &sendMsg, sizeof(sendMsg) );
return TRUE;
}
else
{
MSG_CG_STATUS_RESURRECTION_NAK NakMsg;
NakMsg.m_dwObjectKey = GetObjectKey();
NakMsg.m_byErrorCode = 0; // Error Code 정의 필요
SendPacket( &NakMsg, sizeof(NakMsg) );
}
return FALSE;
}
FLOAT Player::GetHPRatio() const
{
if( m_Attr.GetMaxHP() <= 0 )
return 0;
return (FLOAT)char_info_.m_iHP / m_Attr.GetMaxHP();
}
FLOAT Player::GetMPRatio() const
{
if( m_Attr.GetMaxMP() <= 0 )
return 0;
return (FLOAT)char_info_.m_iMP / m_Attr.GetMaxMP();
}
FLOAT Player::GetSDRatio() const
{
if( m_Attr.GetMaxSD() <= 0 )
return 0;
return (FLOAT)char_info_.m_iSD / m_Attr.GetMaxSD();
}
BOOL Player::CanUsePotion()
{
// 경쟁헌팅 포션 사용횟수 제한
if( ePRS_AT_CHUNTING == GetGameZoneState() )
{
GameCHuntingRoom *pCHuntingRoom = (GameCHuntingRoom *)GetGameZonePtr();
if( pCHuntingRoom )
{
if( !pCHuntingRoom->GetCHuntingManager()->CanUsePotion( this ) )
return FALSE;
}
}
return TRUE;
}
VOID Player::UseHPPotion( INT recoverHP, INT iApplicationTime, INT iPeriodicTime )
{
// 포션 사용횟수
if( ePRS_AT_CHUNTING == GetGameZoneState() )
{
GameCHuntingRoom *pCHuntingRoom = (GameCHuntingRoom *)GetGameZonePtr();
if( pCHuntingRoom )
pCHuntingRoom->GetCHuntingManager()->UsePotion( this );
}
// 즉시 회복 물약을 사용했다면
const bool is_recover_instantly = (iApplicationTime == 1 && iPeriodicTime == 1);
if (is_recover_instantly)
{
// 즉시 회복 상태를 사용한다.
PeriodicRecover(eCHAR_STATE_ETC_ITEM_RECOVER_HP_INSTANTLY, recoverHP, 0, iApplicationTime, iPeriodicTime);
return;
}
PeriodicRecover( eCHAR_STATE_ETC_ITEM_RECOVER_HP, recoverHP, 0, iApplicationTime, iPeriodicTime );
}
VOID Player::UseMPPotion( INT recoverMP, INT iApplicationTime, INT iPeriodicTime )
{
// 포션 사용횟수
if( ePRS_AT_CHUNTING == GetGameZoneState() )
{
GameCHuntingRoom *pCHuntingRoom = (GameCHuntingRoom *)GetGameZonePtr();
pCHuntingRoom->GetCHuntingManager()->UsePotion( this );
}
PeriodicRecover( eCHAR_STATE_ETC_ITEM_RECOVER_MP, 0, recoverMP, iApplicationTime, iPeriodicTime );
}
VOID Player::StatPointUpItemUse()
{
BASE_PLAYERINFO * pBasePlayerInfo = this->GetCharInfo();
pBasePlayerInfo->m_iRemainStat += 5;
//갱신
UpdateCharStat();
//Player에게 스탯 변경을 통보한다.
MSG_CG_ITEM_STAT_ONE_CHANGE_CMD CmdMsg;
CmdMsg.m_AttrType = eATTR_TYPE_INVALID; //<사용 안함
CmdMsg.m_dwValue = 0; //<사용 안함
CmdMsg.m_iRemainStat = GetRemainStatPoint();
SendPacket( &CmdMsg, sizeof(CmdMsg) );
}
VOID Player::SetPreVillageMapCode( MAPCODE mapCode )
{
GetCharInfo()->m_iPreRegion = (INT)mapCode;
}
MAPCODE Player::GetPreVillageMapCode()
{
return (MAPCODE)GetCharInfo()->m_iPreRegion;
}
//_NA_0_20100722_AUTO_SYSTEM
VOID Player::SetlastReturnScroll()
{
GameField* game_field = GetField();
if( game_field == NULL )
return;
m_lastReturnScrollMapCode = game_field->GetFieldCode();
GetPos(&m_lastReturnScrollPos);
}
RC::ePORTAL_RESULT Player::MovelastReturnScroll()
{
RC::ePORTAL_RESULT retValue = CanPortal();
if( RC::RC_PORTAL_CAN_SUCCESS == retValue )
{
retValue = Portal(ePORTAL_TYPE_ITEM_EX, m_lastReturnScrollMapCode, m_lastReturnScrollPos, 0);
}
return retValue;
}
VOID Player::SetStartMapCode( MAPCODE mapCode )
{
GetCharInfo()->m_iRegion = (INT)mapCode;
}
MAPCODE Player::GetStartMapCode()
{
return (MAPCODE)GetCharInfo()->m_iRegion;
}
RC::ePORTAL_RESULT Player::CanPortal()
{
if( !GetField() )
return RC::RC_PORTAL_FIELD_IS_NULL;
GameZone* pGameZone = GetGameZonePtr();
if( !pGameZone )
return RC::RC_PORTAL_FIELD_IS_NULL;
eZONETYPE ZoneType = pGameZone->GetZoneType();
if( ZoneType != eZONETYPE_VILLAGE &&
ZoneType != eZONETYPE_FIELD &&
ZoneType != eZONETYPE_DOMINATION_FIELD &&
ZoneType != eZONETYPE_SPA_FIELD) //_NA_008069_20150210_ADD_SPA_SYSTEM
{
return RC::RC_PORTAL_UNABLE_CUR_ZONE;
}
// 현재 창고가 열려 있는 상태면 리턴처리..
CWarehouseSlotContainer* pWareHouse = GetWarehouseContainer();
if( !pWareHouse || pWareHouse->IsStarted() )
return RC::RC_PORTAL_INVALIDSTATE;
// (lst1024)(090627) 중국 길드 창고 이용한 하임 복사 버그
// 현재 길드창고가 열려 있는 상태면 리턴처리..
CGuildWarehouseSlotContainer* pGuildWareHouse = GetGuildWarehouseContainer();
if( pGuildWareHouse && pGuildWareHouse->IsOpened() )
return RC::RC_PORTAL_INVALIDSTATE;
// 플레이어의 상태가 기본상태가 아니면( 예:현재 개인상점을 개설한 상태등 )
const ePLAYER_BEHAVE_STATE state = GetBehave();
if( PLAYER_BEHAVE_IDLE_STATE != state )
return RC::RC_PORTAL_INVALIDSTATE;
return RC::RC_PORTAL_CAN_SUCCESS;
}
RC::ePORTAL_RESULT Player::Portal( ePORTAL_TYPE ePortalType, FIELDCODE ToFieldCode, WzVector & vDestPos, POSTYPE atItemPos )
{
if( !GetField() ) return RC::RC_PORTAL_FAIL;
// add rule by __NA_0_20090714_POTAL_ITEM_DONOT_MOVE_GMMAP
if( ePortalType != ePORTAL_TYPE_GM )
{
if( ToFieldCode == HardCode::MAPCODE_for_GM ||
ToFieldCode == HardCode::MAPCODE_for_TUTORIAL_FIELD )
{
return RC::RC_PORTAL_UNABLE_DEST_ZONE;
}
}
GameZone* pZone = g_GameZoneManager.GetFieldVillageZone( ToFieldCode );
if( !pZone ) return RC::RC_PORTAL_FAIL;
// 이동하려는 필드를 찾는다.
GameField *pToField = pZone->FindGameField( ToFieldCode );
if( !pToField ) return RC::RC_PORTAL_FAIL;
// 이동가능한 좌표가 아니면 이동가능한 주변 좌표를 찾는다.
if( vDestPos.x != 0 || vDestPos.y != 0 || vDestPos.z != 0 )
{
if( !pToField->IsMovableCoordinates( &vDestPos ) )
{
if( !pToField->GetRandomPosInAround( &vDestPos, 7, vDestPos, 30 ) )
return RC::RC_PORTAL_FAIL;
}
}
// 같은 필드 내에서의 이동
if (pToField == GetField())
{
if( GetField()->TeleportObject( this, &vDestPos ) )
return RC::RC_PORTAL_INSTANT_MOVE;
else
return RC::RC_PORTAL_FAIL;
}
// 필드 간 이동
else
{
SLOTCODE item_code = 0;
SCSlotContainer* pContainer = m_pItemManager->GetItemSlotContainer(SI_INVENTORY);
// NOTE: f110105.1L, fixed logical clearly that
// the validation of the container is inconsistent with an empty checking.
if (pContainer->ValidPos(atItemPos) && !pContainer->IsEmpty(atItemPos))
{
item_code = pContainer->GetSlot(atItemPos).GetCode();
}
#ifdef _NA_20100307_BETAKEY_SYSTEM
ReCallPlayer(GetCharName(), 0, ToFieldCode, vDestPos, ePortalType, atItemPos, item_code);
#else
MSG_AG_GM_GO_CMD AGMsg;
_tcsncpy( AGMsg.m_szCharName, GetCharName(), MAX_CHARNAME_LENGTH );
AGMsg.m_szCharName[MAX_CHARNAME_LENGTH] = '\0';
AGMsg.m_RoomKey = 0;
AGMsg.m_FieldCode = ToFieldCode;
GetPos( &AGMsg.m_vCurPos );
AGMsg.m_vDestPos = vDestPos;
AGMsg.m_PortalType = (BYTE)ePortalType;
AGMsg.m_atItemPos = atItemPos;
AGMsg.m_atItemCode = item_code;
SendPacket( &AGMsg, sizeof(AGMsg) );
#endif
return RC::RC_PORTAL_CAN_SUCCESS;
}
}
// implemented by __KR_001355_20090608_GROUPINGSYSTEM_DATA
VOID Player::GroupBonusAcceptForMoney( MONEY bonusMoney, MONEY addRatioMoney, eHEIM_ACQUISITION_TYPE eType )
{
tag_GroupInfo* groupInfo = GroupInfoParser::Instance()->GetGroupInfo();
FLOAT groupRatio = groupInfo->dwHeimRewardRatio/1000.0f;
switch( eType )
{
case eHEIM_ACQUISITION_PICKUP: //하임을 땅에서 주웠을 경우
if( groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_QUEST ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_MISSION)
return;
else
{
if( groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_ALL ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_HUNTING_RATIO ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_MISSION_RATIO ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_QUEST_RATIO)
{
g_pGameServer->NotifyHeimAcquisition(GetCharGuid(), eHEIM_ACQUISITION_PICKUP, MONEY(addRatioMoney * groupRatio) );
}
else
{
if( groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_HUNTING_NORATIO ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_MISSION_NORATIO ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_QUEST_NORATIO)
{
g_pGameServer->NotifyHeimAcquisition(GetCharGuid(), eHEIM_ACQUISITION_PICKUP, MONEY(bonusMoney * groupRatio) );
}
}
}
break;
case eHEIM_ACQUISITION_QUEST: //하임을 퀘스트 보상 받았을 경우
if( groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_HUNTING_NORATIO||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_HUNTING_RATIO )
return;
else
{
if( groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_ALL ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_QUEST ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_QUEST_NORATIO ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_QUEST_RATIO )
{
g_pGameServer->NotifyHeimAcquisition(GetCharGuid(), eHEIM_ACQUISITION_QUEST, MONEY(bonusMoney * groupRatio) );
}
}
break;
case eHEIM_ACQUISITION_MISSION: //하임을 미션 보상 받았을 경우
if( groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_HUNTING_NORATIO||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_HUNTING_RATIO )
return;
else
{
if( groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_ALL ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_MISSION ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_MISSION_NORATIO ||
groupInfo->flHeimApplyScope == GroupInfoParser::ACCEPT_MISSION_RATIO)
{
g_pGameServer->NotifyHeimAcquisition(GetCharGuid(), eHEIM_ACQUISITION_MISSION, MONEY(bonusMoney * groupRatio) );
}
}
break;
}
}
VOID Player::GroupBonusAcceptForEXP( EXPTYPE bonusEXP, EXPTYPE addRatioEXP, eEXP_ACQUISITION_TYPE eType )
{
tag_GroupInfo* groupInfo = GroupInfoParser::Instance()->GetGroupInfo();
FLOAT groupRatio = groupInfo->dwExpRewardRatio/1000.0f;
switch( eType )
{
case eEXP_ACQUISITION_HUNTING: //사냥으로 경험치를 얻었을 경우
if( groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_QUEST )
return;
else
{
if( groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_ALL ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_HUNTING_RATIO ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_MISSION_RATIO ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_QUEST_RATIO )
{
g_pGameServer->NotifyEXPAcquisition(GetCharGuid(), eEXP_ACQUISITION_HUNTING, EXPTYPE(addRatioEXP * groupRatio) );
}
else
{
if( groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_HUNTING_NORATIO ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_MISSION_NORATIO ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_QUEST_NORATIO )
{
g_pGameServer->NotifyEXPAcquisition(GetCharGuid(), eEXP_ACQUISITION_HUNTING, EXPTYPE(bonusEXP * groupRatio) );
}
}
}
break;
case eEXP_ACQUISITION_QUEST: //퀘스트 보상으로 경험치를 얻었을 경우
if( groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_HUNTING_NORATIO||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_HUNTING_RATIO )
return;
else
{
if( groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_ALL ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_QUEST ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_QUEST_NORATIO||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_QUEST_RATIO)
{
g_pGameServer->NotifyEXPAcquisition(GetCharGuid(), eEXP_ACQUISITION_QUEST, EXPTYPE(bonusEXP * groupRatio) );
}
}
break;
case eEXP_ACQUISITION_MISSION: //미션 보상으로 경험치를 얻었을 경우
if( groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_HUNTING_NORATIO||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_HUNTING_RATIO )
return;
else
{
if( groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_ALL ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_MISSION ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_MISSION_NORATIO ||
groupInfo->flExpApplyScope == GroupInfoParser::ACCEPT_MISSION_RATIO)
{
g_pGameServer->NotifyEXPAcquisition(GetCharGuid(), eEXP_ACQUISITION_MISSION, EXPTYPE(bonusEXP * groupRatio) );
}
}
break;
}
}
BOOL Player::CanApplyPCBangBenefit( BOOL bNationCheck )
{
if( bNationCheck )
{
// 중국에서는 PC방 전용혜택을 주지 않는다. 하지만 일부혜택(경험치 10%, PC방 이벤트)의 경우는 유지한다.
if( ServerOptionParserEx::Instance()->GetServerOption().m_byPCBangApplyBenefit == FALSE )
return FALSE;
}
#ifdef _NA_006413_20130201_PREMIUMSERVICE_ITEM
if (GetPlayerAttr().GetAttrValue(eATTR_PREMIUMSERVICE_PCBANG) > 0) {
return TRUE;
}
#endif //_NA_006413_20130201_PREMIUMSERVICE_ITEM
return (!m_PCRoomSts && m_dwReservedValue);
}
VOID Player::SetFallDead( WzVector BeforeDeadPos )
{
m_bIsFallDead = TRUE;
m_dwBeforeFallDeadPos = BeforeDeadPos;
}
RC::eSTATUS_RESULT Player::RebirthFromFallDead()
{
if( IsAlive() )
return RC::RC_STATUS_ALIVE;
if( !m_bIsFallDead )
return RC::RC_STATUS_NOT_FALL_DEAD;
//부활
SetSD( 0 );
SetHP( 1 );
SetMP( 0 );
SetPos( &m_dwBeforeFallDeadPos );
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
SetFP( 0 );
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
m_bIsFallDead = FALSE;
return RC::RC_STATUS_SUCCESS;
}
BOOL Player::SetInterceptionOption(BYTE byType, BOOL bFlag)
{
switch(byType)
{
case eINTERCEPTION_TRADE:
{
GetCharInfo()->m_bTradeFlag = bFlag; return TRUE;
}
break;
case eINTERCEPTION_WHISPER:
{
GetCharInfo()->m_bWhisperFlag = bFlag; return TRUE;
}
break;
default:
return FALSE;
break;
}
return FALSE;
}
BOOL Player::GetInterceptionOption(BYTE byType)
{
switch(byType)
{
case eINTERCEPTION_TRADE:
{
return GetCharInfo()->m_bTradeFlag;
}
break;
default:
return FALSE;
break;
}
return FALSE;
}
//__NA001336_090514_PARTY_NAVI_SUPPORT__
PlayerStatEvent::eFlag
Player::CheckChangedStatus(PlayerStatEvent::eFlag skip_flag)
{
DWORD flag = PlayerStatEvent::eFlag_None;
if(m_PartyEvent & PlayerStatEvent::eFlag_ChangedHP)
flag |= PlayerStatEvent::eFlag_ChangedHP;
if(m_PartyEvent & PlayerStatEvent::eFlag_ChangedMP)
flag |= PlayerStatEvent::eFlag_ChangedMP;
if(m_PartyEvent & PlayerStatEvent::eFlag_ChangedSD)
flag |= PlayerStatEvent::eFlag_ChangedSD;
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
if (m_PartyEvent & PlayerStatEvent::eFlag_ChangedFP) {
flag |= PlayerStatEvent::eFlag_ChangedFP;
}
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
if((skip_flag & PlayerStatEvent::eFlag_ChangedPos) == 0 && GetField())
{
static const WzVector radius = { 3.0f, 3.0f, 0.0f };
const WzVector* pPos = GetPosPtr();
if(m_LastUpdatedPartyNaviPosBox.IsValidCoordinateXY(pPos) == 0)
{
flag |= PlayerStatEvent::eFlag_ChangedPos;
m_LastUpdatedPartyNaviPosBox.SetInit2D(pPos, &radius);
}
}
return PlayerStatEvent::eFlag(flag);
}
void
Player::SendChangedStatus()
{
#ifdef _NA000000_SPEED_HACK_PREVENTION_SUPPORT
m_pMovementChecker->GetSyncTuner()->Update();
#endif
#ifdef _NA_007825_20141118_MODIFY_PARTY_CHARACTER_STATUSUPDATE
#else
if(GetPartyState().IsPartyMember() != 0)
return;
#endif //_NA_007825_20141118_MODIFY_PARTY_CHARACTER_STATUSUPDATE
// CHANGES: f110926.1L, notify the player navigation information
// to support world server notification
MSG_AG_PARTY_NAVI_INFO_BRD msg_navi;
msg_navi.m_CodeType = msg_navi.OPTION_PARTY_NAVI;
msg_navi.m_NumberOfNodes = 0;
// no party member single side notification
MSG_AG_STATUS_CHAR_STAT_CHANGE_CMD msgHpMp;
msgHpMp.m_NumberOfNodes = 0;
// (CHANGES) (f100514.2L) (WAVERIX) fix up a loss which an original hp value over-value state
// is possible if an old state is removed to replace a new state.
// reference (BUGFIX) (100107) fix up invalid state that hp is grater than max hp
typedef MSG_AG_STATUS_CHAR_STAT_CHANGE_CMD::uNode StatChangeNode;
const WORD playerKey = WORD(GetObjectKey());
GameField* const game_field = this->GetField();
if ((m_PartyEvent & PlayerStatEvent::eFlag_ChangedPos) && (game_field != NULL))
{
MSG_AG_PARTY_NAVI_INFO_BRD::Node& node = msg_navi.m_Nodes[msg_navi.m_NumberOfNodes++];
node.fieldCode = game_field->GetFieldCode(); // assert(GetField() != 0)
node.objectKey = playerKey;
const WzVector* current_pos = GetPosPtr();
node.posX = WORD(current_pos->x);
node.posY = WORD(current_pos->y);
node.posZ = WORD(current_pos->z);
};
if(m_PartyEvent & PlayerStatEvent::eFlag_ChangedHP)
{
StatChangeNode& node = msgHpMp.m_Nodes[msgHpMp.m_NumberOfNodes++];
node.m_HP.event = msgHpMp.eEvent_ChangedHP;
node.m_HP.objectKey = playerKey;
node.m_HP.maxHP = GetMaxHP();
node.m_HP.currentHP = GetHP();
node.m_HP.reservedHP = GetReserveHP();
// (CHANGES) (f100514.2L) to pending process related to HP events.
// (WARNING) the contents of the Reserved HP has problems if a hp is grater than a max hp
if (node.m_HP.currentHP > node.m_HP.maxHP) {
node.m_HP.currentHP = node.m_HP.maxHP;
SetHP(node.m_HP.currentHP);
}
}
if(m_PartyEvent & PlayerStatEvent::eFlag_ChangedMP)
{
StatChangeNode& node = msgHpMp.m_Nodes[msgHpMp.m_NumberOfNodes++];
node.m_MP.event = msgHpMp.eEvent_ChangedMP;
node.m_MP.objectKey = playerKey;
node.m_MP.maxMP = GetMaxMP();
node.m_MP.currentMP = GetMP();
// (CHANGES) (f100514.2L) to pending process related to MP events.
if (node.m_MP.currentMP > node.m_MP.maxMP) {
node.m_MP.currentMP = node.m_MP.maxMP;
SetMP(node.m_MP.currentMP);
}
}
if(m_PartyEvent & PlayerStatEvent::eFlag_ChangedSD)
{
StatChangeNode& node = msgHpMp.m_Nodes[msgHpMp.m_NumberOfNodes++];
node.m_SD.event = msgHpMp.eEvent_ChangedSD;
node.m_SD.objectKey = playerKey;
node.m_SD.maxSD = GetMaxSD();
node.m_SD.currentSD = GetSD();
// (CHANGES) (f100514.2L) to pending process related to MP events.
if (node.m_SD.currentSD > node.m_SD.maxSD) {
node.m_SD.currentSD = node.m_SD.maxSD;
SetSD(node.m_SD.currentSD);
}
}
#ifdef _NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
if (m_PartyEvent & PlayerStatEvent::eFlag_ChangedFP)
{
StatChangeNode& node = msgHpMp.m_Nodes[msgHpMp.m_NumberOfNodes++];
node.m_FP.event = msgHpMp.eEvent_ChangedFP;
node.m_FP.objectKey = playerKey;
node.m_FP.maxFP = GetMaxFP();
node.m_FP.currentFP = GetFP();
// (CHANGES) (f100514.2L) to pending process related to MP events.
if (node.m_FP.currentFP > node.m_FP.maxFP) {
node.m_FP.currentFP = node.m_FP.maxFP;
SetFP(node.m_FP.currentFP);
}
}
#endif //_NA_007514_20140828_NEW_CHARACTER_WITCHBLADE
//
NotifyChangedStatus(PlayerStatEvent::eFlag_None);
if (msg_navi.m_NumberOfNodes != 0) {
this->SendPacket(&msg_navi, msg_navi.GetSize());
};
if(msgHpMp.m_NumberOfNodes != 0) {
#ifdef _NA_007825_20141118_MODIFY_PARTY_CHARACTER_STATUSUPDATE
msgHpMp.user_count_ = 1;
#endif //_NA_007825_20141118_MODIFY_PARTY_CHARACTER_STATUSUPDATE
SendPacket(&msgHpMp, msgHpMp.GetSize());
}
}
//}__NA001336_090514_PARTY_NAVI_SUPPORT__
void
Player::Player_TransOff()
{
StatusManager* const pStatusManager = GetStatusManager();
StatusField& rStatus = pStatusManager->Status;
if(rStatus.IsPlayer_TransOn())
{
// 자연스럽게 해제하기 위해서 현재 투명상태의 만기 시간을 현재 시간으로 조절한다.
BaseStatus* pStatus = pStatusManager->FindStatus(eCHAR_STATE_TRANSPARENT);
if(pStatus)
pStatus->StopStatus();
}
}
//_KR_0_20091021_FIRENDINVITATION_EXTEND
VOID
Player::SetInvitatedFriendRecallRequestTime(DWORD FriendGUID, DWORD64 Recalltime)
{
RECALL_MSGBOX_TIME_MAP_ITER iter = m_RecallMsgBoxTimeMap.find(FriendGUID);
if(iter == m_RecallMsgBoxTimeMap.end())
{
m_RecallMsgBoxTimeMap.insert( RECALL_MSGBOX_TIME_MAP_PAIR( FriendGUID, Recalltime ) );
}
else
{
(*iter).second = Recalltime;
}
}
DWORD64
Player::GetInvitatedFriendRecallRequestTime(DWORD FriendGUID)
{
RECALL_MSGBOX_TIME_MAP_ITER iter = m_RecallMsgBoxTimeMap.find(FriendGUID);
if(iter == m_RecallMsgBoxTimeMap.end())
{
return 0;
}
return (*iter).second;
}
// __KR_001355_20090624_GROUPINGSYSTEM_ALL
void Player::NotifyGroupMoneyToPlayer(MONEY amountMoney)
{
MSG_CG_STATUS_GROUPMONEY_BRD sendMsg;
_tcsncpy(sendMsg.ptszMemeberName, this->GetCharName(), _countof(sendMsg.ptszMemeberName));
sendMsg.ptszMemeberName[_countof(sendMsg.ptszMemeberName) - 1] = '\0';
sendMsg.bonusMoney = amountMoney;
SendPacket(&sendMsg, sizeof(sendMsg));
}
// this has a different presentation from a complex riding casting
bool Player::SetupEtherealizer(nsSlot::EtheriaImpl* result_instance)
{
result_instance->SetInstance(this, &etheria_control_option_);
return true;
}
BOOL Player::ReCallPlayer(char const character_name[MAX_CHARNAME_LENGTH] ,const KEYTYPE room_key, FIELDCODE field_code,
WzVector dest_pos, ePORTAL_TYPE portal_type, POSTYPE item_pos, SLOTCODE item_code)
{
#ifdef _NA_20100307_BETAKEY_SYSTEM
if((!GetBetaKeyInfo()->CanAbilityContent(GetGameZonePtr()->GetMAPINFO()->nBetaKey)) &&
ePORTAL_TYPE_GM != portal_type)
{
MSG_CG_MAP_RECALL_NAK nak_msg;
nak_msg.error_code_ = MSG_CG_MAP_RECALL_NAK::RECALL_CANNOT_BETAKEY;
SendPacket( &nak_msg, sizeof(nak_msg));
return FALSE;
}
#endif
MSG_AG_GM_GO_CMD msg;
strncpy(msg.m_szCharName, character_name, _countof(msg.m_szCharName));
msg.m_szCharName[_countof(msg.m_szCharName) - 1] = '\0';
GetPos(&msg.m_vCurPos);
msg.m_RoomKey = room_key;
msg.m_FieldCode = field_code;
msg.m_vDestPos = dest_pos;
msg.m_PortalType = static_cast<BYTE>(portal_type);
msg.m_atItemPos = item_pos;
msg.m_atItemCode = item_code;
SendPacket( &msg, sizeof(msg) );
return TRUE;
}
bool Player::IsThereBattleGroundZone() const
{
if (GetGameZonePtr() && GetGameZonePtr()->GetZoneType() == eZONETYPE_INSTANCE)
{
const GameInstanceDungeon* instance_dungeon =
static_cast<GameInstanceDungeon*>(GetGameZonePtr());
if (instance_dungeon && instance_dungeon->GetChaosZoneManager())
{
if (instance_dungeon->GetChaosZoneType() == kBattleGround)
{
return true;
}
}
}
return false;
}
ChaosZone* const Player::GetCurrentChaosZone() const
{
if (GetGameZonePtr() && GetGameZonePtr()->GetZoneType() == eZONETYPE_INSTANCE)
{
const GameInstanceDungeon* instance_dungeon =
static_cast<GameInstanceDungeon*>(GetGameZonePtr());
if (instance_dungeon == NULL)
return NULL;
ChaosZoneManager* chaoszone_manager = instance_dungeon->GetChaosZoneManager();
if (chaoszone_manager)
{
return chaoszone_manager->GetCurrentZone();
}
}
return NULL;
}
ChaosZoneType Player::GetChaosZoneType() const
{
if (GetGameZonePtr() && GetGameZonePtr()->GetZoneType() == eZONETYPE_INSTANCE)
{
const GameInstanceDungeon* instance_dungeon =
static_cast<GameInstanceDungeon*>(GetGameZonePtr());
if (instance_dungeon && instance_dungeon->GetChaosZoneManager())
{
return instance_dungeon->GetChaosZoneType();
}
}
return kChaosZoneTypeNone;
}
#ifdef _NA_008012_20150204_SUN_RANKING_SYSTEM
bool Player::GetTotalEquipmentScore(int& out_total_item_score)
{
CEquipmentSlotContainer* const equip_container = static_cast<CEquipmentSlotContainer* const>(
GetItemManager()->GetItemSlotContainer(SI_EQUIPMENT));
if (equip_container == NULL)
{
return false;
}
const ns_formula::EquipSlotsManageInfo* const equip_manage_info = \
equip_container->GetEquipmentManageInfo();
if (equip_manage_info == NULL)
{
return false;
}
const ns_formula::EquipSlotsManageInfo::ItemScoreInfo& item_score_info = \
equip_manage_info->item_score_info;
out_total_item_score = item_score_info.GetTotalScore();
return true;
}
SunRankingPlayerInfo Player::GetSunRankingPlayerInfo()
{
SunRankingPlayerInfo out_info;
strncpy(out_info.char_name, GetCharName(), MAX_CHARNAME_LENGTH);
out_info.char_guid = GetCharGuid();
out_info.guild_guid = GetGuildGuid();
out_info.class_code = GetCharType();
for (eSUN_RANKING_TYPE type = eSUN_RANKING_TYPE_START; type < eSUN_RANKING_TYPE_END; ++type)
{
out_info.score[type] = 0;
}
return out_info;
}
void Player::OnSendAllPlayersSunRankingGradeInfo( const Sector& IN rSector )
{
VarMsg2* const pVarMsg = &(m_VarMsg2[SENDINFO_SUN_RANKING_GRADE]);
BYTE count = 0;
const BYTE MAX_PACKET_COUNT =
MSG_CG_SYNC_ALLPLAYERS_SUN_RANKING_GRADE_CMD::_MAX_SUN_RANKING_GRADE_INFO_SIZE;
MSG_CG_SYNC_ALLPLAYERS_SUN_RANKING_GRADE_CMD* pGradeInfo =
pVarMsg->StartTypePtr(Param2Class<MSG_CG_SYNC_ALLPLAYERS_SUN_RANKING_GRADE_CMD>());
pVarMsg->SetSize(pGradeInfo->GetSize());
ns_object::SearchNodeConstIterator it = rSector.GetPlayerRoot();
const ns_object::SearchNodeConstIterator end = rSector.GetPlayerEnd();
while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end))
{
Player* pPlayer = link_node->select.player_node.player;
if (obj_util::IsValid(pPlayer) == false)
{
continue;
}
if (this == pPlayer)
continue;
SUN_RANKING_GRADE_INFO* pack_info = (SUN_RANKING_GRADE_INFO*)pVarMsg->NextPtr();
SunRankingGradeManager::Instance()->GetTopGrade(pPlayer->GetCharGuid(),
pack_info->grade, pack_info->ranking_type);
pack_info->m_dwObjectKey = pPlayer->GetObjectKey();
pVarMsg->SetSize(pack_info->GetSize());
if (MAX_PACKET_COUNT == ++count)
{
pGradeInfo->m_byCount = count;
SendPacket((MSG_BASE_FORWARD*)*pVarMsg->GetMsgPPtr(),
*pVarMsg->GetMsgSizePtr(), TRUE);
pGradeInfo = pVarMsg->StartTypePtr(Param2Class<MSG_CG_SYNC_ALLPLAYERS_SUN_RANKING_GRADE_CMD>());
pVarMsg->SetSize(pGradeInfo->GetSize());
count = 0;
}
}
if(count)
{
pGradeInfo->m_byCount = count;
SendPacket((MSG_BASE_FORWARD*)*pVarMsg->GetMsgPPtr(), *pVarMsg->GetMsgSizePtr(), TRUE);
}
}
void Player::OnSendAllPlayersSunRankingGradeInfoToNeighbor( const Sector& IN rSector )
{
VarMsg2* const pVarMsg = &(m_VarMsg2[SENDINFO_SUN_RANKING_GRADE]);
ns_object::SearchNodeConstIterator it = rSector.GetPlayerRoot();
const ns_object::SearchNodeConstIterator end = rSector.GetPlayerEnd();
while (const ns_object::SearchNode::LinkNode* link_node = it.ClimbToNext(end))
{
Player* pPlayer = link_node->select.player_node.player;
if (obj_util::IsValid(pPlayer) == false)
{
continue;
}
if (this == pPlayer)
continue;
MSG_CG_SYNC_ALLPLAYERS_SUN_RANKING_GRADE_CMD* pGradeInfo =
pVarMsg->StartTypePtr(Param2Class<MSG_CG_SYNC_ALLPLAYERS_SUN_RANKING_GRADE_CMD>());
pVarMsg->SetSize(pGradeInfo->GetSize());
pGradeInfo->m_byCount = 1;
SUN_RANKING_GRADE_INFO* pack_info = (SUN_RANKING_GRADE_INFO*)pVarMsg->NextPtr();
SunRankingGradeManager::Instance()->GetTopGrade(this->GetCharGuid(),
pack_info->grade, pack_info->ranking_type);
pack_info->m_dwObjectKey = this->GetObjectKey();
pVarMsg->SetSize(pack_info->GetSize());
pPlayer->SendPacket((MSG_BASE_FORWARD*)*pVarMsg->GetMsgPPtr(),
*pVarMsg->GetMsgSizePtr(), TRUE);
}
}
#endif // _NA_008012_20150204_SUN_RANKING_SYSTEM
#ifdef _NA_008069_20150210_ADD_SPA_SYSTEM
VOID Player::RemoveDragonTransformStatus()
{
// 용변신State의 End상태에서 클라에게 용변신제거를 알린다
StatusManager* status_manager = this->GetStatusManager();
if (status_manager->IsDragonTransforming() == true) {
status_manager->RemoveDragonTransformStatus();
}
}
#endif //_NA_008069_20150210_ADD_SPA_SYSTEM
#ifdef _NA_008334_20150608_SONNENSCHEIN
GameGuild* Player::GetGuild() const
{
GUILDGUID guild_guid = this->GetGuildGuid();
if (guild_guid == 0) {
return NULL;
}
return g_GameGuildManager.FindGuild(guild_guid);
}
#endif //_NA_008334_20150608_SONNENSCHEIN
#ifdef _NA_008132_20150316_MONDSCHEIN_TEXTLOG
VOID Player::_WriteMondschienLog( Character* attacker, SLOTCODE skill_code, DAMAGETYPE damage_value )
{
GameZone* game_zone = this->GetGameZonePtr();
if (game_zone != NULL && game_zone->GetZoneType() == eZONETYPE_DOMINATION_FIELD )
{
eDOMINATION_TYPE domi_type = DominationInfoParser::Instance()->GetDominationTypeFromFieldCode(game_zone->GetMapCode());
if (domi_type == DOMINATION_TYPE_MONDSCHIEN ||
domi_type == DOMINATION_TYPE_NORMAL)
{
// 특정상황 처리
Player* attacker_player = static_cast<Player*>(attacker);
GUILDGUID player_guild_guid = attacker_player->GetGuildGuid();
//길드가 없으면 기록하지 않는다
if (attacker_player->GetGuildName() == NULL || this->GetGuildName() == NULL) {
return;
}
// (-) 피격자가 사망했을 경우
if (IsDead() == TRUE)
{
SUNMONDSCHIENLOG(eCRITICAL_LOG, TEXT("[AttackerGuild:%s]\t[Attacker:%s]\t[TargetGuild:%s]\t[Target:%s]\t[Player Dead!][skill:%d]\t%d"),
attacker_player->GetGuildName(), attacker_player->GetCharName(),
this->GetGuildName(), this->GetCharName(), skill_code, damage_value);
}
/*
// (1) 공격자가 '특정인'일 경우 //특정인 로깅 안쓰는중
if ( _IsLogTargetUser(attacker) == TRUE )
{
// 데미지 줬음!, 데미지수치 표시
if (IsDead() == TRUE) {
SUNMONDSCHIENLOG(eCRITICAL_LOG, TEXT("[Attacker:%s]\t[Target:%s]\t[Player Dead!][skill:%d]\t%d"),
attacker_player->GetCharName(), this->GetCharName(), skill_code, damage_value);
}
else {
SUNMONDSCHIENLOG(eCRITICAL_LOG, TEXT("[Attacker:%s]\t[Target:%s]\t[Damaged!][skill:%d]\t%d"),
attacker_player->GetCharName(), this->GetCharName(), skill_code, damage_value);
}
// Attacker(공격자)의 상태코드리스트
_WriteMondschienLog_StatusList(attacker);
//Target(피격자)의 상태코드리스트
_WriteMondschienLog_StatusList(this);
}
// (2) '지정길드' 이면서 피격자가 사망했을 경우
else if (IsDead() == TRUE && _IsLogTargetGuildUser(player_guild_guid))
{
SUNMONDSCHIENLOG(eCRITICAL_LOG, TEXT("[AttackerGuild:%s]\t[Attacker:%s]\t[TargetGuild:%s]\t[Target:%s]\t[Player Dead!][skill:%d]\t%d"),
attacker_player->GetGuildName(), attacker_player->GetCharName(),
this->GetGuildName(), this->GetCharName(), skill_code, damage_value);
}
*/
}
}
}
VOID Player::_WriteMondschienLog_StatusList(Character* user)
{
StatusManager* status_manager = user->GetStatusManager();
std::string player_status_list = "x";
if (status_manager != NULL) {
player_status_list = status_manager->PrintLogStatusList();
}
SUNMONDSCHIENLOG(eCRITICAL_LOG, TEXT("[Target:%s]\t[Target's Status]\t[%s]"),
user->GetCharName(), player_status_list.c_str());
}
BOOL Player::_IsLogTargetUser(Character* user)
{
//특정인 로깅 중단
if ( _tcscmp(user->GetCharName(), TEXT("Shuu")) == 0 ||
_tcscmp(user->GetCharName(), TEXT("z비쿄z")) == 0 ||
_tcscmp(user->GetCharName(), TEXT("kiss콱봤")) == 0 ||
_tcscmp(user->GetCharName(), TEXT("우라칸")) == 0 ||
_tcscmp(user->GetCharName(), TEXT("침뼙Death콩뮴땀")) == 0
)
{
return TRUE;
}
return FALSE;
}
BOOL Player::_IsLogTargetGuildUser( GUILDGUID guild_guid )
{
/*
BOOL is_target = FALSE;
switch(guild_guid)
{
case 21419: // HiddeN길드
case 22702: // sIlElySiumlIs
case 21929: // Sunfighters
case 21450: // BandOfBlood
is_target = TRUE;
break;
}
return is_target;
*/
//모든 유저에 한해 로그기록
return TRUE;
}
#endif //_NA_008132_20150316_MONDSCHEIN_TEXTLOG
#ifdef _NA_008706_20160229_TEXTLOG_PK
void Player::_WriteBattleMonitorLog( Character* attacker, SLOTCODE skill_code, DAMAGETYPE damage_value )
{
// [로그 기록 조건]
// 1. PvP의 경우에만 동작하도록 한다
if (attacker->IsEqualObjectKind(PLAYER_OBJECT) == false ||
this->IsEqualObjectKind(PLAYER_OBJECT) == false)
{
return;
}
// 2. 필드에서만 기록한다
GameZone* game_zone = this->GetGameZonePtr();
if (game_zone == NULL || game_zone->GetZoneType() != eZONETYPE_FIELD )
{
return;
}
// 3. 특정 유저 조건
if (_IsLogTargetUser(attacker) == FALSE)
{
return;
}
// [로그 기록]
SUNBATTLEMONITORLOG(eCRITICAL_LOG, TEXT("[Attacker:%s]\t[Target:%s]\t[skill:%d]\t[Damage:%d]"),
attacker->GetCharName(), this->GetCharName(), skill_code, damage_value);
if (this->IsDead() == TRUE)
{
SUNBATTLEMONITORLOG(eCRITICAL_LOG, TEXT("[Attacker:%s]\t[Target:%s]\t[Player Dead!]"),
attacker->GetCharName(), this->GetCharName());
}
}
#endif //_NA_008706_20160229_TEXTLOG_PK