Files
Sun1602/Server/GameServer/GameField.cpp
T
2022-10-26 12:25:11 +08:00

1654 lines
50 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "stdafx.h"
#include "Gamefield.h"
#include <NPCInfoParser.h>
#include <MapInfoParser.h>
#include <CollectionParser.h>
//==================================================================================================
#include "./GameServerEx.h"
#include "./ObjectFactory.h"
#include "./Map.h"
#include "./GameZone.h"
#include "./GameInstanceDungeon.h"
#include "./MovingPlanner.h"
#include "./World/GameWarControlManager.h"
#include "./World/HighLoadBalancer.h"
#include "./TriggerManagerEx.h"
#include "./SummonedNPCs.h"
#include "./NpcAI/TargetFinder.h"
#include "./NpcAI/NPCStateManager.h"
#include "./UnitGroupManager.h"
#include "./UnitGroupInfoManager.h"
#include "./DropManager.h"
#include "./EffectManager.h"
#include "./Status/StatusManager.h"
#include "./Player.h"
#include "./Monster.h"
#include "./LuckyMonster.h"
#include "./SSQMonster.h"
#include "./MapObject.h"
#include "./Item.h"
#include "./MapNpc.h"
#include "./TotemNpc.h"
#include "./Summoned.h"
#include "./FriendMonster.h"
#include "./SyncMerchant.h"
#include "./Collection.h"
#include "./CollectionManager.h"
#include "./CrystalWarp.h"
#include "GameMissionRoom.h"
//==================================================================================================
__IMPL_CUSTOMPOOL_PTR(GameField);
const DWORD GameField::SECTOR_SYNC_UPDATE_INTERVAL = 50;
GameField::GameField()
: instanced_resourse_(false)
, m_pHLB( 0 )
, npc_trigger_events_(NULL)
{
m_pEffectManager = new EffectManager( this );
m_pTriggerManager = new TriggerManagerEx;
m_pUnitRegenManager = new UnitRegenManager;
m_pUnitGroupMgr = new UnitGroupManager;
m_pCollectionManager = new CollectionManager(this);
m_SyncUpdater.SetTimer(SECTOR_SYNC_UPDATE_INTERVAL);
}
GameField::~GameField()
{
SAFE_DELETE( m_pUnitGroupMgr );
SAFE_DELETE( m_pUnitRegenManager );
SAFE_DELETE( m_pEffectManager );
SAFE_DELETE( m_pTriggerManager );
SAFE_DELETE( m_pCollectionManager );
if (npc_trigger_events_) {
TAllocDelete(ns_trigger::TriggerNpcLinkMatchTable, npc_trigger_events_);
npc_trigger_events_ = NULL;
};
}
VOID GameField::Release()
{
m_pEffectManager->Release();
m_pTriggerManager->Release();
m_pCollectionManager->Release();
//__NA000000_20090320_DELAY_ITEM_DROP_TIME
g_DropManager.OnFieldRelease(m_pGameZone->GetKey());
// (WAVERIX)(080816)(MINOR-FIX)
// ObjectFactory::Instance()-> ==> pObjectFactory->
ObjectFactory* const pObjectFactory = ObjectFactory::Instance();
{
// NPC
for(_NPC_HASH::iterator it = m_NPCHash.begin(); it != m_NPCHash.end(); ++it)
{
NPC* npc = it->second;
if (npc != NULL)
{
npc->OnLeaveField();
}
}
for(_NPC_HASH::iterator it = m_NPCHash.begin(); it != m_NPCHash.end(); ++it)
{
NPC* npc = it->second;
if (npc != NULL)
{
pObjectFactory->FreeObject(npc);
npc = NULL;
}
}
}
{
// MapObject
_MAPOBJECT_HASH::iterator it = m_MapObjectHash.begin(), end = m_MapObjectHash.end();
for( ; it != end ; ++it )
{
MapObject* pMapObject = it->second;
pMapObject->OnLeaveField();
pObjectFactory->FreeObject( pMapObject );
}
}
{
// 아이템 제거
_ITEM_HASH::iterator end = m_FieldItemHash.end();
for( _ITEM_HASH::iterator it = m_FieldItemHash.begin(); it != end ; ++it )
{
Item* pItem = it->second;
pItem->OnLeaveField();
pObjectFactory->FreeObject( pItem );
}
}
{
// 채집물 제거
_COLLECTION_HASH::iterator end = m_FieldCollectHash.end();
for( _COLLECTION_HASH::iterator it = m_FieldCollectHash.begin(); it != end ; ++it )
{
Collection* pCollection = it->second;
pCollection->OnLeaveField();
pObjectFactory->FreeObject( pCollection );
}
}
m_FieldCollectHash.clear();
//
m_NPCHash.clear();
m_NPCHashForUnitID.clear();
m_MapObjectHash.clear();
m_FieldItemHash.clear();
m_PlayerHash.clear();
m_CharacterHash.clear();
m_pUnitGroupMgr->Clear();
m_pUnitRegenManager->Release();
instanced_resourse_ = false;
if (npc_trigger_events_) {
TAllocDelete(ns_trigger::TriggerNpcLinkMatchTable, npc_trigger_events_);
npc_trigger_events_ = NULL;
};
// NOTE: f110531.4L
HighLoadBalancer::Instance()->UnRegisterBuffer( this );
Field::Release();
}
//────────────────────────────────────────────────────────────────
// GameField::BuildSector의 후신
VOID GameField::BuildInstanceInfo()
{
if (instanced_resourse_) {
return;
};
// 트리거에서 MapObject의 제어를 하는 구문이 존재 하므로 로딩(TriggerManager->Init())
// 이전에 생성 해야 하지만, 그럴경우 MapObject 생성에 의한 BuildInstanceInfo()의 재기 호출이 발생 할 수 있다.
// instanced_resourse_ 설정을 최상위로 변경 재기 호출을 막는다.
// 기존 BuildInstanceInfo가 재기 호출 되었던 부분이 의도된 것이라면 이후 사이드 이펙트를 예측하기
// 어려운 부분이 있다. 허나 Init()에 "트리거를 두번 초기화 하지 않는다." 는 정의가 있는 걸로 보아
// 문제가 없어 보인다. TriggerManagerEx::m_bInitCompleted 값의 사용 방법으로 보아 BuildInstanceInfo 의
// 재기를 막기 위한 방편으로 보여 진다. (주의 필요) [5/18/2010 lst1024]
instanced_resourse_ = true;
if( m_MapObjectHash.size() == 0 )
AddMapObjectAll();
// 트리거 로딩
if( m_tszTriggerGroupName[0] != '0')
{
TriggerGroupInfo* pGroupInfo = GetFieldInfo()->GetTriggerGroupInfo( m_tszTriggerGroupName );
ASSERT( pGroupInfo && "청唐뇰랙莉!!(숨World.txt" );
if( pGroupInfo )
{
#ifdef _NA_002098_20110307_ADD_NPC_CONTROL_TRIGGER_BASE
if (npc_trigger_events_ == NULL) {
npc_trigger_events_ = TAllocNew(ns_trigger::TriggerNpcLinkMatchTable);
};
npc_trigger_events_->Init(this);
m_pTriggerManager->Init(*pGroupInfo, this);
// post event
if (npc_trigger_events_->IsExistTriggerAreaEvent() == false) {
TAllocDelete(ns_trigger::TriggerNpcLinkMatchTable, npc_trigger_events_);
npc_trigger_events_ = NULL;
}
#else
m_pTriggerManager->Init(*pGroupInfo, this);
#endif
}
else
{
SUNLOG( eCRITICAL_LOG
, "뒈暠덜쯤:%d,끝쒼덜쯤:%d,뇰랙포莉츰냔:%s"
, m_pGameZone->GetMapCode()
, m_pFieldInfo->GetFieldCode()
, m_tszTriggerGroupName
);
}
}
}
//────────────────────────────────────────────────────────────────
//────────────────────────────────────────────────────────────────
VOID
GameField::Init(
GameZone* pZone
, FieldInfo* pFieldInfo
, const TCHAR* pszGroupName
, UnitRegenInfoManager* pUnitRegenInfoMgr
, UnitGroupInfoManager* pUnitGroupInfoMgr
, bool bPreMapLoading, bool bUsingViewPort, bool bPreRegen
)
{
m_pGameZone = pZone;
m_bPreRegen = bPreRegen;
if( pszGroupName )
{
_tcsncpy( m_tszTriggerGroupName, pszGroupName, MAX_MAPNAME_LENGTH );
// (WAVERIX)(080815)(BUG-FIX) - Buffer Overrun
// - (OLD) CHAR m_tszTriggerGroupName[MAX_MAPNAME_LENGTH]; 선언에 대한
// m_tszTriggerGroupName[MAX_MAPNAME_LENGTH] = '\0'; 처리
// - (NEW) CHAR m_tszTriggerGroupName[MAX_MAPNAME_LENGTH+1]; (변경)
m_tszTriggerGroupName[MAX_MAPNAME_LENGTH] = '\0';
}
else
{
m_tszTriggerGroupName[0] = '0';
m_tszTriggerGroupName[1] = '\0';
}
instanced_resourse_ = false;
// NOTE: f110531.4L
HighLoadBalancer::Instance()->RegisterBuffer( this );
Field::Init( pFieldInfo, bPreMapLoading, bUsingViewPort, bPreRegen );
m_pMovingPlanner->Init( this );
ASSERT( m_pUnitGroupMgr->GetCount() == 0 );
m_pUnitGroupMgr->Clear();
m_pUnitGroupInfoMgr = pUnitGroupInfoMgr;
m_pUnitRegenManager->Init( this, pUnitRegenInfoMgr, bPreRegen );
#ifdef _NA_007992_20150127_EVENT_MONSTER_INVASION
//마을이면 Init()에서 BuildInstanceInfo()를 호출하고
//마을이 아니면 EnterField()에서 한다
if (pZone->GetZoneType() == eZONETYPE_VILLAGE) {
BuildInstanceInfo();
}
#endif //_NA_007992_20150127_EVENT_MONSTER_INVASION
}
//─────────────────────────────────────────
BOOL GameField::EnterField( Object *pObject, const WzVector *pwzVec, int iAngle )
{
const eOBJECT_TYPE objectType = pObject->GetObjectType();
// (WAVERIX)(080917)(NOTE)
// - 이걸 수행한 이유는 아무래도 일전의 불량 관리 CharacterHash에서 문제를 야기했을 가능성이 있다. 검증 후 불필요하게 만들자.
if( NULL != FindObject( objectType, pObject->GetObjectKey() ) )
return FALSE;
//__NA_276_20070213_UNIT_TRIGGER
DWORD npc_unit_id = 0;
if (pObject->IsEqualObjectKind(NPC_OBJECT))
{
NPC* pNPC = static_cast<NPC*>(pObject);
npc_unit_id = pNPC->GetRegenID();
if (npc_unit_id != 0)
{
if( NULL != FindNPCForUnitID(npc_unit_id))
return FALSE;
}
}
// (WAVERIX)(080917)(BUG-FIX) Field::EnterField 상태에서 GameField에 목록이 없는 상태에서 처리되는 상황 배제 목적
_AddObject( pObject );
const BOOL bEnterField = Field::EnterField( pObject, pwzVec, iAngle );
if( !bEnterField )
{
_DeleteObject( pObject );
return FALSE;
}
GameZone* const pGameZone = GetGameZone();
if( !pGameZone ) // GameZone이 없는데 냥묘을 반환하고 있다. 그 이유 및 타당성을 검증하라. (일단은 문의함... 찝찝하지만 넘어가자.
return TRUE;
#ifdef _NA_007992_20150127_EVENT_MONSTER_INVASION
if (pGameZone->GetZoneType() != eZONETYPE_VILLAGE) {
BuildInstanceInfo();
}
#endif //_NA_007992_20150127_EVENT_MONSTER_INVASION
pGameZone->SetZone( pObject, GetFieldCode(), 0, NULL );
if((objectType & CHARACTER_OBJECT) == CHARACTER_OBJECT)
{
Character* const pCharacter = static_cast<Character*>(pObject);
pCharacter->ChangePolicy(pGameZone);
// (CHANGES) (f100514.3L) add the flag to prevent ANY actions that
// a player do a delayed spiking whether the player request a LEAVE.
pCharacter->GetStatusManager()->ChangeInterState(eCHAR_STATE_ZONE_TRANSACTION, false);
// 공격 불가 상태로 전환시킨다.
sPOLICY_ZONE& rPolicyment = pGameZone->Policyment;
if(rPolicyment.FIELD & rPolicyment.ENABLE_SSQ_CONTROL)
{ //assert( GetTypeOfIndun() == INDUN_KIND_SS_QUEST );
GameWarControlManager::OnMsg(GameSSQCtrlCmd::CharacterState::Enter(pCharacter),
static_cast<GameInstanceDungeon*>(pGameZone));
}
}
#ifdef _NA_002098_20110307_ADD_NPC_CONTROL_TRIGGER_BASE
if (npc_unit_id != 0 && npc_trigger_events_ != NULL)
{
if (npc_trigger_events_->CheckExistAreaEvent(npc_unit_id))
{
NPC* npc = static_cast<NPC*>(pObject);
const sPOLICY_CHARACTER& CharPolicy = npc->Policyment;
npc->ChangePolicyPostEnterField(CharPolicy.PCC_EXIST_AREA_EVENT_NPC,
CharPolicy.PCC_DISABLE_ALL,
NULL);
}
}
#endif
if (npc_trigger_events_ != NULL && pObject->IsEqualObjectKind(NPC_OBJECT))
{
NPC* npc = static_cast<NPC*>(pObject);
MONSTERCODE monster_code = npc->GetBaseInfo()->m_MonsterCode;
if (npc_trigger_events_->CheckExistMonsterAreaEvent(monster_code))
{
const sPOLICY_CHARACTER& CharPolicy = npc->Policyment;
npc->ChangePolicyPostEnterField(CharPolicy.PCC_EXIST_AREA_EVENT_NPC,
CharPolicy.PCC_DISABLE_ALL,
NULL);
//!~ arycoat 2014.10.16
const int iTile = npc->GetPathExplorer()->GetTile();
if (GameField* game_field = npc->GetField()) {
if (ns_trigger::TriggerNpcLinkMatchTable* table =
game_field->GetNpcTriggerEvents())
{
#ifdef NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER
npc->Update(1);
#endif // NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER
table->OnChangeTile(-1, iTile, npc);
}
}
//~!
}
}
return TRUE;
}
//─────────────────────────────────────────
BOOL GameField::LeaveField( Object *pObject )
{
// (WAVERIX)(080917)(BUG-FIX) Field::LeaveField 이후 Object 제거하도록 수정
// 버퍼 초기화가 필요하다.
// 타락한 사원 관련
if(pObject->IsEqualObjectKind(CHARACTER_OBJECT))
{
Character* const pCharacter = static_cast<Character*>( pObject );
// (CHANGES) (f100514.3L) add the flag to prevent ANY actions that
// a player do a delayed spiking whether the player request a LEAVE.
pCharacter->GetStatusManager()->ChangeInterState(eCHAR_STATE_ZONE_TRANSACTION, true);
GameZone* const pGameZone = GetGameZone();
sPOLICY_ZONE& rPolicyment = pGameZone->Policyment;
if( rPolicyment.FIELD & rPolicyment.ENABLE_SSQ_CONTROL )
{
GameWarControlManager::OnMsg( GameSSQCtrlCmd::CharacterState::Leave( pCharacter ), static_cast<GameInstanceDungeon*>(pGameZone) );
}
}
const BOOL bLeavedSuccess = Field::LeaveField( pObject );
_DeleteObject( pObject );
// NOTE: f110531.4L
if (m_PlayerHash.empty()) {
HighLoadBalancer::Instance()->Update(this, true);
}
if( !bLeavedSuccess )
SUNLOG(eCRITICAL_LOG, "Field::LeaveField 呵겨댄轎! 쒸멩!!!!!!!!!! %8X",
pObject->GetObjectType());
return bLeavedSuccess;
}
//─────────────────────────────────────────
MapNpc* GameField::FindMapNpc(const Object::Key object_key) const
{
const MapObject* const map_object = FindMapObject(object_key);
if (map_object == NULL)
{
return NULL;
}
const Object::Key link_obj_key = map_object->GetLinkedNpcObjectKey();
return static_cast<MapNpc*>(FindObject(MAPNPC_OBJECT, link_obj_key));
}
MapObject* GameField::FindMapObject(const Object::Key object_key) const
{
_MAPOBJECT_HASH::const_iterator it = m_MapObjectHash.find(object_key);
if (it != m_MapObjectHash.end())
{
Object* const object = it->second;
if (obj_util::IsValid(object, __FUNCTION__, __LINE__) == false)
{
return NULL;
}
return it->second;
}
return NULL;
}
Character* GameField::FindCharacter(const Object::Key object_key) const
{
_CHARACTER_HASH::const_iterator it = m_CharacterHash.find(object_key);
if (it != m_CharacterHash.end())
{
Object* const object = it->second;
if (obj_util::IsValid(object, __FUNCTION__, __LINE__) == false)
{
return NULL;
}
return it->second;
}
return NULL;
}
NPC* GameField::FindNPCForUnitID(const DWORD unit_id) const
{
_NPC_HASH::const_iterator it = m_NPCHashForUnitID.find(unit_id);
if (it != m_NPCHashForUnitID.end())
{
Object* const object = it->second;
if (obj_util::IsValid(object, __FUNCTION__, __LINE__) == false)
{
return NULL;
}
return it->second;
}
return NULL;
}
void GameField::_LinkedMapNpc(Object* pObj)
{
ASSERT(pObj);
ASSERT(MAPNPC_OBJECT == pObj->GetObjectType());
MapNpc* pMapNpc = static_cast<MapNpc*>(pObj);
MapObject* pMapObject = FindMapObject(pMapNpc->GetMapObjectID());
if (NULL != pMapObject)
pMapObject->SetLinkedNpcObjectKey(pMapNpc->GetObjectKey());
}
void GameField::_UnLinkedMapNpc(Object* pObj)
{
ASSERT(pObj);
ASSERT(MAPNPC_OBJECT == pObj->GetObjectType());
MapNpc* pMapNpc = static_cast<MapNpc*>(pObj);
MapObject* pMapObject = FindMapObject(pMapNpc->GetMapObjectID());
if (NULL != pMapObject)
pMapObject->SetLinkedNpcObjectKey(0);
}
//─────────────────────────────────────────
void GameField::_AddObject( Object* pObj )
{
if( NULL != FindObject( pObj->GetObjectType(), pObj->GetObjectKey() ) )
return;
switch( pObj->GetObjectType() )
{
case OBJECT_OBJECT:
case CHARACTER_OBJECT:
case NONCHARACTER_OBJECT:
case NPC_OBJECT:
ASSERT(!"NPC_OBJECT Object add Error!");
break;
case PLAYER_OBJECT:
m_PlayerHash[pObj->GetObjectKey()] = static_cast<Player*>(pObj);
m_CharacterHash[pObj->GetObjectKey()] = static_cast<Character*>(pObj);
break;
case MONSTER_OBJECT:
case SUMMON_OBJECT:
case MAPNPC_OBJECT:
case MERCHANT_OBJECT:
case LUCKYMON_OBJECT:
case TOTEMNPC_OBJECT:
case SSQMONSTER_OBJECT:
case CRYSTALWARP_OBJECT:
case FRIEND_MONSTER_OBJECT:
case SYNC_MERCHANT_OBJECT:
if (NPC* npc = static_cast<NPC*>(pObj))
{
DWORD object_key = npc->GetObjectKey();
m_NPCHash[object_key] = npc;
m_CharacterHash[object_key] = npc;
const DWORD unit_id = npc->GetRegenID();
if (unit_id == 0) {
break;
};
_NPC_HASH::_Pairib result = \
m_NPCHashForUnitID.insert(_NPC_HASH::value_type(unit_id, npc));
if (result.second == false) {
assert(!"雷棍榴檄");
SUNLOG(eCRITICAL_LOG, "轟랬警속 m_NPCHashForUnitID 돕map(%u)", unit_id);
break;
};
};
break;
case ITEM_OBJECT:
case MONEY_OBJECT:
m_FieldItemHash[pObj->GetObjectKey()] = static_cast<Item *>(pObj);
break;
case COLLECTION_OBJECT:
m_FieldCollectHash[pObj->GetObjectKey()] = static_cast<Collection*>(pObj);
break;
default:
ASSERT(!"default Object add Error!");
break;
}
if (MAPNPC_OBJECT == pObj->GetObjectType())
_LinkedMapNpc(pObj);
}
//─────────────────────────────────────────
bool GameField::_DeleteObject( Object* pObj )
{
const eOBJECT_TYPE objType = pObj->GetObjectType();
if( NULL == FindObject( objType, pObj->GetObjectKey() ) )
return false;
if (MAPNPC_OBJECT == pObj->GetObjectType())
_UnLinkedMapNpc(pObj);
switch( pObj->GetObjectType() )
{
case OBJECT_OBJECT:
case CHARACTER_OBJECT:
case NONCHARACTER_OBJECT:
case NPC_OBJECT:
ASSERT(!"NPC_OBJECT Object rem Error!");
return false;
case PLAYER_OBJECT:
m_PlayerHash.erase( pObj->GetObjectKey() );
m_CharacterHash.erase( pObj->GetObjectKey() );
break;
case MONSTER_OBJECT:
case SUMMON_OBJECT:
case MERCHANT_OBJECT:
case MAPNPC_OBJECT:
case LUCKYMON_OBJECT:
case TOTEMNPC_OBJECT:
case SSQMONSTER_OBJECT:
case CRYSTALWARP_OBJECT:
case FRIEND_MONSTER_OBJECT:
case SYNC_MERCHANT_OBJECT:
if (DWORD object_key = pObj->GetObjectKey())
{
m_NPCHash.erase(object_key);
m_CharacterHash.erase(object_key);
const DWORD unit_id = static_cast<NPC*>(pObj)->GetRegenID();
if (unit_id != 0) {
m_NPCHashForUnitID.erase(unit_id);
};
};
break;
case ITEM_OBJECT:
case MONEY_OBJECT:
m_FieldItemHash.erase( pObj->GetObjectKey() );
break;
case COLLECTION_OBJECT:
m_FieldCollectHash.erase( pObj->GetObjectKey() );
break;
default:
ASSERT(!"default Object rem Error!");
return false;
}
return true;
}
//==================================================================================================
void GameField::DestroyObject( Object* pObj )
{
// (NOTE) important, designed by __NA000000_090310_GAMEFIELD_RECURSIVELY_CALL_PROBLEM_FIX__
pObj->RequestDelete();
}
//─────────────────────────────────────────
VOID GameField::AddMapObjectAll()
{
ASSERT( m_pSharedFieldInfo );
// 맵오브젝트 생성
MAPOBJECT_INFO_HASH & rMapObjectInfoTable = m_pSharedFieldInfo->GetMapObjectInfoHashTable();
MAPOBJECT_INFO *pMapInfo = NULL;
MAPOBJECT_INFO_HASH_IT it, end = rMapObjectInfoTable.end();
for( it = rMapObjectInfoTable.begin() ; it != end ; ++it )
{
pMapInfo = (it->second);
// 맵오브젝트
MapObject* pMapObject = static_cast<MapObject *>(ObjectFactory::Instance()->AllocObject(MAP_OBJECT));
pMapObject->Init();
pMapObject->SetObjectKey( pMapInfo->dwMapObjectID );
pMapObject->SetPos( &pMapInfo->wvPos );
m_MapObjectHash[pMapObject->GetObjectKey()] = pMapObject;
DWORD dwCode = pMapInfo->dwExtraInfos[eMAPOBJECT_MONSTER];
ASSERT(SAFE_NUMERIC_TYPECAST( DWORD, dwCode, WORD ));
WORD NPCCode = (WORD)dwCode;
//_NA_0_20100222_UNIT_TRIGGERS_REGEN
// 맵오브젝트에 연결된 몬스터의 경우 미리 생성되어 진다.
// 예외로 헬론의 성 서브 미션의 경우 트리거 액션에 의해 생성되어 진다.
const DWORD dwRegenType = pMapInfo->dwExtraInfos[eMAPOBJECT_LINKED_MONSTER_REGEN_TYPE];
const bool bPreRegen = !((dwRegenType & REGEN_TYPE_TRIGGER) == REGEN_TYPE_TRIGGER);
if( 0 != dwCode && false != bPreRegen)
{
if( !NPCInfoParser::Instance()->GetNPCInfo( NPCCode ) )
{
SUNLOG( eCRITICAL_LOG, "[Field:%u]灌冷돕뒈暠뚤蹶밍膠 (%u)", GetFieldCode(), pMapInfo->dwExtraInfos[eMAPOBJECT_MONSTER] );
SUNLOG( eCRITICAL_LOG, "[Field:%u]뒈暠뚤蹶(ID:%u),(띨棍斤口令)[잚謹(1):%u,밍膠(2):%u,(괏즛:%u,%u)]",
GetFieldCode(),
pMapInfo->dwMapObjectID,
pMapInfo->dwExtraInfos[eMAPOBJECT_TYPE],
pMapInfo->dwExtraInfos[eMAPOBJECT_MONSTER],
pMapInfo->dwExtraInfos[eMAPOBJECT_USE_ALPHA],
pMapInfo->dwExtraInfos[eMAPOBJECT_LINKED_MONSTER_REGEN_TYPE]
);
}
else
{
MapNpc* pNPC = CreateMapNPC( NPCCode, &pMapInfo->wvPos, pMapInfo );
ASSERT( pNPC );
}
}
// (lst1024)(채집) 필드 생성시 오브젝트중 채집물은 모두 생성해 둔다.
if( 0 != pMapInfo->CollectionID )
{
if( NULL == CollectionParser::Instance()->FindCollectionInfo(pMapInfo->CollectionID) )
{
char ch[5] = {0,};
SUNLOG( eCRITICAL_LOG,
"冷꼇돕꽃섞斤口. "
"끝쒼[%u] 뒈暠뚤蹶Key[%u] 꽃섞포ID[%s] "
"윈嵐斤口令[잚謹(1):%u,밍膠(2):%u,(괏즛:%u,%u)]",
GetFieldCode(),
pMapInfo->dwMapObjectID,
WzIDToString(ch,pMapInfo->CollectionID),
pMapInfo->dwExtraInfos[eMAPOBJECT_TYPE],
pMapInfo->dwExtraInfos[eMAPOBJECT_MONSTER],
pMapInfo->dwExtraInfos[eMAPOBJECT_USE_ALPHA],
pMapInfo->dwExtraInfos[eMAPOBJECT_LINKED_MONSTER_REGEN_TYPE] );
}
else
{
Collection* pCollection = CreateCollection( pMapInfo );
ASSERT( pCollection );
}
}
}
}
//─────────────────────────────────────────
//─────────────────────────────────────────
//─────────────────────────────────────────
VOID
GameField::Update(DWORD delta_tick)
{
if (!IsReady()) {
return;
}
#ifdef _NA_007992_20150127_EVENT_MONSTER_INVASION
GameZone* zone = GetGameZone();
if (zone != NULL && zone->GetZoneType() == eZONETYPE_VILLAGE)
{
m_pTriggerManager->Update();
if (m_PlayerHash.empty()) {
return;
}
}
else
{
if (m_PlayerHash.empty()) {
return;
}
m_pTriggerManager->Update();
}
#else
if (m_PlayerHash.empty()) {
return;
}
m_pTriggerManager->Update();
#endif //_NA_007992_20150127_EVENT_MONSTER_INVASION
m_pUnitRegenManager->Update();
m_pEffectManager->Update( GetTickCount() );
m_pCollectionManager->Update();
ObjectFactory* const object_factory = ObjectFactory::Instance();
// 아이템
for (_ITEM_HASH::iterator it = m_FieldItemHash.begin(); it != m_FieldItemHash.end();)
{
Item* item = it->second;
if (item == NULL)
{
m_FieldItemHash.erase(it++);
}
else if (item->Update(delta_tick) == FALSE)
{
Field::LeaveField(item);
m_FieldItemHash.erase(it++);
object_factory->FreeObject(item);
item = NULL;
}
else
{
++it;
}
}
// 몬스터
for (_NPC_HASH::iterator it = m_NPCHash.begin(); it != m_NPCHash.end();)
{
NPC* npc = it->second;
if (npc == NULL)
{
m_NPCHash.erase(it++);
}
//#ifdef _NA_004126_20120111_CHANGE_GAME_SERVER_OBJECT_FACTORY
else if (npc->IsDeleted())
{
const Object::Key key = npc->GetObjectKey();
SUNLOG(
eCRITICAL_LOG,
_T("|%s|%d|뚤蹶綠굳뇜.|ObjectKey = %d|"),
__FUNCTION__,
__LINE__,
key
);
m_NPCHash.erase(it++);
m_CharacterHash.erase(key);
}
//#endif
else if (npc->Update(delta_tick) == FALSE)
{
const CODETYPE monster_object_key = npc->GetObjectKey();
if (npc->IsEqualObjectKind(MONSTER_OBJECT))
{
if (npc->GetUnitRegenInfo()) //< 임시
{
ASSERT(npc->GetRegenID());
// 럭키 몬스터의 경우 소멸조건에 따라 다음 리젠타임이 달라진다.
DWORD regen_time = npc->GetUnitRegenInfo()->GetRegenTime();
if (npc->IsEqualObjectKind(LUCKYMON_OBJECT))
{
LuckyMonster* lucky_monster = static_cast<LuckyMonster*>(npc);
if (lucky_monster->GetRunAwayRegenTime())
{
regen_time = lucky_monster->GetRunAwayRegenTime();
}
}
m_pUnitRegenManager->AddOnWaiting(npc->GetRegenID(), regen_time);
}
}
Field::LeaveField(npc);
m_NPCHash.erase(it++);
m_CharacterHash.erase(monster_object_key); // (WAVERIX)(080822)(BUG-FIX) erase가 없었다.
//__NA_276_20070213_UNIT_TRIGGER
const DWORD regen_id = npc->GetRegenID();
if (regen_id != 0)
{
m_NPCHashForUnitID.erase(regen_id);
}
object_factory->FreeObject(npc);
npc = NULL;
}
else
{
++it;
}
}
Field::Update(delta_tick);
// NOTE: f110531.4L
if (m_SyncUpdater.IsExpired())
{
HighLoadBalancer::Instance()->Update(this, true);
}
}
//─────────────────────────────────────────
Item* GameField::CreateItem( const WzVector* pvPos, DWORD dwPlayerKey, DWORD dwFromMonsterKey, SCItemSlot & slot )
{
Item* pFieldItem = static_cast<Item *>(ObjectFactory::Instance()->AllocObject( ITEM_OBJECT ));
pFieldItem->Init();
pFieldItem->CreateItem( dwPlayerKey, dwFromMonsterKey, slot );
if( FALSE == EnterField( pFieldItem, pvPos, 0 ) )
{
ObjectFactory::Instance()->FreeObject( pFieldItem );
SUNLOG( eCRITICAL_LOG, "[Item EnterField] 貫零[%f, %f, %f] 呵겨!", pvPos->x, pvPos->y, pvPos->z );
return FALSE;
}
return pFieldItem;
}
//─────────────────────────────────────────
Item* GameField::CreateMoney( const WzVector* pvPos, DWORD dwPlayerKey, DWORD dwFromMonsterKey, const MONEY& Money)
{
Item* pFieldMoneyItem = static_cast<Item *>(ObjectFactory::Instance()->AllocObject( MONEY_OBJECT ));
pFieldMoneyItem->Init();
pFieldMoneyItem->CreateMoney( dwPlayerKey, dwFromMonsterKey, Money );
if( FALSE == EnterField( pFieldMoneyItem, pvPos, 0 ) )
{
ObjectFactory::Instance()->FreeObject( pFieldMoneyItem );
SUNLOG( eCRITICAL_LOG, "[Item EnterField] 貫零[%f, %f, %f]呵겨!", pvPos->x, pvPos->y, pvPos->z );
return NULL;
}
return pFieldMoneyItem;
}
//─────────────────────────────────────────
#include <GroupParser.h>
Monster* GameField::CreateMonster( WORD NPCCode, const WzVector *pvPos,
WORD wGroupID, int iAngle, BYTE byMoveType, DWORD dwMoveAreaID, WORD MissionAreaID/*미션전용임시*/ )
{
Monster *pSpawnMonster = AllocMonster( NPCCode, byMoveType, dwMoveAreaID );
if( !pSpawnMonster ) return NULL;
if( 0 != wGroupID )
{
DWORD dwNewGroupID = MAKEDWORD(MissionAreaID,wGroupID);
UnitGroupInfo* pGroupInfo = m_pUnitGroupInfoMgr->Find( dwNewGroupID );
if( !pGroupInfo )
{
BASE_GROUPINFO* pBaseGroupInfo = GroupParser::Instance()->GetGroupInfo( wGroupID );
ASSERT(pBaseGroupInfo);
if( !pBaseGroupInfo )
{
SUNLOG( eCRITICAL_LOG, "[끝쒼덜쯤:%u],Group[%u,%u] pBaseGroupInfo 槨 NULL", GetFieldCode(), dwNewGroupID, wGroupID );
}
else
{
pGroupInfo = new UnitGroupInfo;
pGroupInfo->SetID(dwNewGroupID);
pGroupInfo->SetLeaderRegenID(0);
m_pUnitGroupInfoMgr->Insert( dwNewGroupID, pGroupInfo );
}
}
if( pGroupInfo && 0 == pGroupInfo->GetLeaderRegenID() )
{
BASE_GROUPINFO* pBaseGroupInfo = GroupParser::Instance()->GetGroupInfo( wGroupID );
ASSERT(pBaseGroupInfo);
if( pBaseGroupInfo && NPCCode == pBaseGroupInfo->dwLeaderCode )
{
pGroupInfo->SetLeaderRegenID(pSpawnMonster->GetObjectKey());
}
}
else
{
//SUNLOG( eCRITICAL_LOG, "그룹[%u]이미 리더[%u]가 존재함[%u]2!", wGroupID, pGroupInfo->GetLeaderRegenID(), NPCCode );
}
pSpawnMonster->SetUnitGroupInfo( pGroupInfo );
}
if( !EnterField( pSpawnMonster, pvPos, iAngle ) )
{
ObjectFactory::Instance()->FreeObject( pSpawnMonster );
return NULL;
}
return pSpawnMonster;
}
//─────────────────────────────────────────
Monster* GameField::CreateSummonByNPC( WORD NPCCode, const WzVector* pvPos, Character *pSummoner, SummonedNPCs *pOwnerSummonNPCs )
{
BASE_NPCINFO* pInfo = NPCInfoParser::Instance()->GetNPCInfo( NPCCode );
ASSERT( pInfo );
if( !pInfo )
{
SUNLOG( eCRITICAL_LOG, _T("꼇닸瞳덜쯤돨 NPC 斤口[%u]]"), NPCCode );
return NULL;
}
Monster *pMonster = static_cast<Monster*>(ObjectFactory::Instance()->AllocObject( MONSTER_OBJECT ));
if (pMonster == NULL) {
return NULL;
}
pMonster->InitMonster( NPCCode, 0, 0 );
// NPC에 소환인포 포인터 공유
pMonster->SetSummonerKey( pSummoner->GetObjectKey() );
// 소환자의 타겟을 추적하도록 NPC의 상태를 바꾼다.
if( pSummoner->GetTargetChar() )
{
pMonster->SetTargetChar( pSummoner->GetTargetChar() );
pMonster->ChangeState( STATE_ID_HELP );
}
if( !EnterField( pMonster, pvPos, -1 ) )
{
ObjectFactory::Instance()->FreeObject( pMonster );
return NULL;
}
// 소환자의 소환인포에 몬스터 수 증가
pOwnerSummonNPCs->IncreaseSummoned( NPCCode, pMonster );
return pMonster;
}
//─────────────────────────────────────────
Summoned* GameField::CreateSummonByPlayer( WORD NPCCode, const WzVector* pvPos, Character *pSummoner,
SummonedNPCs* pOwnerSummonNPCs, DWORD dwExpireTime, SLOTCODE SkillCode, const PLAYER_SUMMON_INFO* const summon_info)
{
Summoned *pSummoned = static_cast<Summoned*>(ObjectFactory::Instance()->AllocObject( SUMMON_OBJECT ));
pSummoned->InitSummoned( NPCCode, SkillCode, pSummoner, dwExpireTime );
if (summon_info != NULL)
{
pSummoned->SetHP(summon_info->hp);
pSummoned->SetMP(summon_info->mp);
const eSUMMON_COMMAND command = (summon_info->command == eSUMMON_COMMAND_ATTACK) ?
eSUMMON_COMMAND_DELEGATE_ATTACK : summon_info->command;
pSummoned->SetCommandState(command, 0);
}
if( !EnterField( pSummoned, pvPos, -1 ) )
{
SUNLOG( eCRITICAL_LOG, "[CreateSummonByPlayer EnterField][뚤蹶Key:%u,NPC덜쯤:%u] 貫零[%f, %f, %f] 呵겨!",
pSummoned->GetObjectKey(), NPCCode, pvPos->x, pvPos->y, pvPos->z );
ObjectFactory::Instance()->FreeObject( pSummoned );
return NULL;
}
// 소환자의 소환인포에 몬스터 수 증가
pOwnerSummonNPCs->IncreaseSummoned( NPCCode, pSummoned );
// (CHANGES) (100205) (WAVERIX) 플레이어에 의한 소환수의 경우 AI update를 위해 세부 룰을 변경.
const sPOLICY_CHARACTER& CharPolicyment = pSummoned->Policyment;
pSummoned->ChangePolicyPostEnterField(CharPolicyment.PCC_CONTINUING_NPC_UPDATE,
CharPolicyment.PCC_DISABLE_ALL,
pSummoner);
return pSummoned;
}
//죽은 몬스터를 소환한다.
//제거후 소환자옆에 생성한다.
// __NA_S00015_20080828_NEW_HOPE_SHADOW
Summoned* GameField::CreateSummonDeadMonster( WORD NPCCode, const WzVector* pvPos, Character *pSummoner, SummonedNPCs* pOwnerSummonNPCs, SLOTCODE SkillCode )
{
Summoned *pSummoned = static_cast<Summoned*>(ObjectFactory::Instance()->AllocObject( SUMMON_OBJECT ));
pSummoned->InitSummonedDeadMonster( NPCCode, SkillCode, pSummoner );
if( !EnterField( pSummoned, pvPos, -1 ) )
{
SUNLOG( eCRITICAL_LOG, "[CreateSummonByPlayer EnterField][뚤蹶Key:%u,NPC덜쯤:%u] 貫零[%f, %f, %f] 呵겨!",
pSummoned->GetObjectKey(), NPCCode, pvPos->x, pvPos->y, pvPos->z );
ObjectFactory::Instance()->FreeObject( pSummoned );
return NULL;
}
// 소환자의 소환인포에 몬스터 수 증가
pOwnerSummonNPCs->IncreaseSummoned( NPCCode, pSummoned );
// (CHANGES) (100205) (WAVERIX) 플레이어에 의한 소환수의 경우 AI update를 위해 세부 룰을 변경.
const sPOLICY_CHARACTER& CharPolicyment = pSummoned->Policyment;
pSummoned->ChangePolicyPostEnterField(CharPolicyment.PCC_CONTINUING_NPC_UPDATE,
CharPolicyment.PCC_DISABLE_ALL,
pSummoner);
return pSummoned;
}
CrystalWarp* GameField::CreateSummonCrystalWarp( WORD NPCCode, const WzVector* pvPos, DWORD dwGuildGuid, DWORD dwExpireTime )
{
CrystalWarp* pCrystalWarp = static_cast<CrystalWarp*>(ObjectFactory::Instance()->AllocObject( CRYSTALWARP_OBJECT ));
pCrystalWarp->InitCrystalWarp( NPCCode, dwGuildGuid, dwExpireTime );
if( !EnterField( pCrystalWarp, pvPos, -1 ) )
{
SUNLOG( eCRITICAL_LOG, "[CreateSummonCrystalWarp] [뚤蹶Key:%u,NPC덜쯤:%u] 貫零[%f, %f, %f] 呵겨!",
pCrystalWarp->GetObjectKey(), NPCCode, pvPos->x, pvPos->y, pvPos->z );
ObjectFactory::Instance()->FreeObject( pCrystalWarp );
return NULL;
}
return pCrystalWarp;
}
//─────────────────────────────────────────
MapNpc* GameField::CreateMapNPC( WORD NPCCode, const WzVector* pvPos, MAPOBJECT_INFO *pMapInfo )
{
MapNpc *pMapNpc = static_cast<MapNpc*>(ObjectFactory::Instance()->AllocObject( MAPNPC_OBJECT ));
pMapNpc->InitMapNPC( NPCCode, pMapInfo );
int dir = static_cast<int>(WZRAD2DEG(pMapInfo->wzRot.z));
dir %= 360;
if (dir < 0)
dir += 360;
if( !EnterField(pMapNpc, pvPos, dir) )
{
SUNLOG( eCRITICAL_LOG, "[CreateMapNPC EnterField][뚤蹶Key:%u,NPC덜쯤:%u] 貫零[%f, %f, %f] 呵겨!",
pMapNpc->GetObjectKey(), NPCCode, pvPos->x, pvPos->y, pvPos->z );
ObjectFactory::Instance()->FreeObject( pMapNpc );
return NULL;
}
return pMapNpc;
}
//─────────────────────────────────────────
TotemNpc* GameField::CreateTotemNPC( WORD NPCCode, const WzVector* pvPos, Player* pSummoner, SCItemSlot & IN slot )
{
TotemNpc *pTotemNpc = static_cast<TotemNpc*>(ObjectFactory::Instance()->AllocObject( TOTEMNPC_OBJECT ));
pTotemNpc->InitTotemNpc( NPCCode, pSummoner, slot );
if( !EnterField( pTotemNpc, pvPos, -1 ) )
{
SUNLOG( eCRITICAL_LOG, "[CreateTotemNPC EnterField][뚤蹶Key:%u,NPC덜쯤:%u] 貫零[%f, %f, %f] 呵겨!",
pTotemNpc->GetObjectKey(), NPCCode, pvPos->x, pvPos->y, pvPos->z );
ObjectFactory::Instance()->FreeObject( pTotemNpc );
return NULL;
}
return pTotemNpc;
}
//─────────────────────────────────────────
//{__NA001283_20090225_COLLECTION_USE
Collection* GameField::CreateCollection( MAPOBJECT_INFO *pMapInfo )
{
Collection* pCollection = static_cast<Collection*>(ObjectFactory::Instance()->AllocObject( COLLECTION_OBJECT ));
pCollection->InitCollection( pMapInfo );
pCollection->SetObjectKey( pMapInfo->dwMapObjectID );
pCollection->SetPos( &pMapInfo->wvPos );
int dir = static_cast<int>(WZRAD2DEG(pMapInfo->wzRot.z));
dir %= 360;
if (dir < 0)
dir += 360;
if( !EnterField( pCollection, &pMapInfo->wvPos, dir ) )
{
char ch[5] = {0,};
SUNLOG( eCRITICAL_LOG, "[CreateCollection EnterField][뚤蹶Key:%u,wzId:%s] 貫零[%f, %f, %f] 呵겨!",
pCollection->GetObjectKey(), WzIDToString(ch, pMapInfo->CollectionID), pMapInfo->wvPos.x, pMapInfo->wvPos.y, pMapInfo->wvPos.z );
ObjectFactory::Instance()->FreeObject( pCollection );
return NULL;
}
return pCollection;
}
//}
//─────────────────────────────────────────
Monster* GameField::SpawnMonster( UnitRegenInfo* pUnitRegenInfo )
{
int npc_code = pUnitRegenInfo->GetRandomNPCCode();
const int npc_unit_type = pUnitRegenInfo->GetUnitType();
#ifdef _NA_003649_20111115_BATTLE_ZONE_HARDMODE
;{
MONSTERCODE replaace_monster_code = \
AppliedNpcDependencyChain::Instance()->GetRepleaceMonsterCode(GetGameZone(), npc_code, true);
if (replaace_monster_code) {
npc_code = replaace_monster_code;
}
};
#endif //_NA_003649_20111115_BATTLE_ZONE_HARDMODE
Monster* pSpawnMonster = AllocMonster( npc_code, npc_unit_type, 0 );
if( !pSpawnMonster )
{
SUNLOG( eCRITICAL_LOG, "[SpawnMonster] AllocMonster 呵겨!" );
return NULL;
}
pSpawnMonster->SetUnitRegenInfo( pUnitRegenInfo );
if( 0 != pUnitRegenInfo->GetGroupID() )
{
UnitGroupInfo* pGroupInfo = m_pUnitGroupInfoMgr->Find( pUnitRegenInfo->GetGroupID() );
if( pGroupInfo )
pSpawnMonster->SetUnitGroupInfo( pGroupInfo );
}
const WORD pos_index = pUnitRegenInfo->GetRandomPosIndex();
const WzVector& vStartPos = pUnitRegenInfo->GetStartPos(pos_index);
NPCStateManager* const state_manager = pSpawnMonster->GetStateManager();
state_manager->SetMovePosIndex(pos_index);
if( pUnitRegenInfo->GetDir() > 360 )
{
SUNLOG( eCRITICAL_LOG, "[SpawnMonster] 약잼[%f]낚놔렀鍋!", pUnitRegenInfo->GetDir() );
pUnitRegenInfo->SetDir( (float)( (int)pUnitRegenInfo->GetDir() % 360 ) );
}
BASE_NPCINFO *pMonsterInfo = NPCInfoParser::Instance()->GetNPCInfo(npc_code);
if (pMonsterInfo->m_bIsNotice)
{
TCHAR message[MAX_NOTICE_LEN + 1];
wsprintf(message,_T("溝固:[%u] 놔君瞳죄뒈暠“{%u}”뒈혐"),npc_code,GetFieldCode());
SUNLOG(eCRITICAL_LOG, "溝固:[%u] 놔君瞳죄뒈暠“{%u}”뒈혐", npc_code, GetFieldCode());
PlayerManager::Instance()->SendGmNotice(message,eNOTICE_WORLD); //xiaomi 警속밍膠릿삶무멩
}
if( FALSE == EnterField( pSpawnMonster, &vStartPos, (int)pUnitRegenInfo->GetDir() ) )
{
ObjectFactory::Instance()->FreeObject( pSpawnMonster );
SUNLOG( eCRITICAL_LOG, "[SpawnMonster EnterField][끝쒼덜쯤:%u,NPC덜쯤:%u] 貫零[%f, %f, %f] 呵겨!",
GetFieldCode(), npc_code, vStartPos.x, vStartPos.y, vStartPos.z );
return NULL;
}
return pSpawnMonster;
}
//─────────────────────────────────────────
Monster* GameField::AllocMonster( WORD NPCCode, BYTE byMoveType, DWORD dwMoveAreaID )
{
BASE_NPCINFO *pBaseNPCInfo = NPCInfoParser::Instance()->GetNPCInfo( NPCCode );
if( !pBaseNPCInfo )
{
SUNLOG( eCRITICAL_LOG, "[AllocMonster] 밍膠(%d)灌冷돕", NPCCode );
return NULL;
}
Monster *pSpawnMonster = NULL;
ObjectFactory* const pObjectFactory = ObjectFactory::Instance();
switch( pBaseNPCInfo->m_byGrade )
{
case eNPC_LUCKY_MONSTER:
{
LuckyMonster* pLuckyMonster = static_cast<LuckyMonster *>( pObjectFactory->AllocObject( LUCKYMON_OBJECT ) );
pSpawnMonster = pLuckyMonster;
}
break;
case eNPC_SSQ_MONSTER:
{
SSQMonster* pMonster = static_cast<SSQMonster *>( pObjectFactory->AllocObject( SSQMONSTER_OBJECT ) );
pSpawnMonster = pMonster;
}
break;
case eNPC_FRIEND_MONSTER:
{
FriendMonster* pFriendMonster = static_cast<FriendMonster*>(pObjectFactory->AllocObject(FRIEND_MONSTER_OBJECT));
pSpawnMonster = pFriendMonster;
}
break;
case eNPC_MOVE_SYNC_MERCHANT:
{
SyncMerchant* pSyncMerchant = static_cast<SyncMerchant*>( pObjectFactory->AllocObject( SYNC_MERCHANT_OBJECT ) );
pSpawnMonster = pSyncMerchant;
}
break;
default:
{
Monster* pMonster = static_cast<Monster *>( pObjectFactory->AllocObject( MONSTER_OBJECT ) );
pSpawnMonster = pMonster;
}
break;
}
if (pSpawnMonster == NULL) {
SUNLOG( eCRITICAL_LOG, "[AllocMonster] 밍膠(%d)/잚謹(%d)롸토呵겨", NPCCode, pBaseNPCInfo->m_byGrade );
return NULL;
}
pSpawnMonster->InitMonster( NPCCode, byMoveType, dwMoveAreaID );
return pSpawnMonster;
}
//─────────────────────────────────────────
void GameField::KillAllNPC(Player* player, int range, const FLOAT damage_percent)
{
const WzVector& player_pos = *player->GetPosPtr();
const float range_square = float(range * range);
for (_NPC_HASH::iterator it = m_NPCHash.begin(); it != m_NPCHash.end(); ++it)
{
NPC* const npc = it->second;
if (npc == NULL)
{
continue;
}
//#ifdef _NA_004126_20120111_CHANGE_GAME_SERVER_OBJECT_FACTORY
if (npc->IsDeleted())
{
const Object::Key key = npc->GetObjectKey();
SUNLOG(
eCRITICAL_LOG,
_T("|%s|%d|뚤蹶綠굳뇜.|ObjectKey = %d|"),
__FUNCTION__,
__LINE__,
key
);
continue;
}
//#endif
const WzVector& npc_pos = *npc->GetPosPtr();
const float dist_square = VectorLength2(&(npc_pos - player_pos));
if ((dist_square <= range_square) && (player->IsFriend(npc) == USER_RELATION_ENEMY))
{
if (damage_percent)
{
GmDamageNPC(player, npc, damage_percent);
}
else
{
KillNPC(player, npc, TRUE);
}
}
}
}
//─────────────────────────────────────────
// _NA_0_20100222_UNIT_TRIGGERS_ADD
VOID GameField::KillOneMonster( NPC* pNPC, Player* pPlayer, BOOL IsExpItemDrop )
{
if(pPlayer)
{
KillNPC(pPlayer, pNPC, IsExpItemDrop);
}
else
{
DestroyObject(pNPC); // (WAVERIX) (090121) leave_field + delete_object
//pNPC is a garbage
}
}
//보상없이 몬스터를 사라지게 한다.
void GameField::KillMonster(MONSTERCODE monster_code, Player* player, BOOL is_exp_item_drop)
{
for (_NPC_HASH::iterator it = m_NPCHash.begin(); it != m_NPCHash.end(); ++it)
{
NPC* const npc = it->second;
if (npc == NULL)
{
continue;
}
//#ifdef _NA_004126_20120111_CHANGE_GAME_SERVER_OBJECT_FACTORY
if (npc->IsDeleted())
{
const Object::Key key = npc->GetObjectKey();
SUNLOG(
eCRITICAL_LOG,
_T("|%s|%d|뚤蹶綠굳뇜.|ObjectKey = %d|"),
__FUNCTION__,
__LINE__,
key
);
continue;
}
//#endif
if (npc->GetBaseInfo()->m_MonsterCode != monster_code)
{
continue;
}
KillOneMonster(npc, player, is_exp_item_drop);
}
}
VOID GameField::AddExpAllMember( DWORD64 Exp )
{
_PLAYER_HASH::iterator it = m_PlayerHash.begin(), end = m_PlayerHash.end();
for( ; it != end ; )
{
Player* pPlayer = (Player*)it->second;
++it;
if( pPlayer )
{
pPlayer->AddExp( Exp, 0, 0, TRUE );
}
}
}
//─────────────────────────────────────────
VOID GameField::KillAllItem( Player *pPlayer, int iRange )
{
// (NOTE) important, designed by __NA000000_090310_GAMEFIELD_RECURSIVELY_CALL_PROBLEM_FIX__
const WzVector& playerPos = *pPlayer->GetPosPtr();
const float rangeSquare = float(iRange * iRange);
_ITEM_HASH::iterator it = m_FieldItemHash.begin(), end = m_FieldItemHash.end();
for( ; it != end; ++it)
{
Item* pItem = it->second;
const WzVector& itemPos = *pItem->GetPosPtr();
float fDistSquare = VectorLength2(&(playerPos - itemPos));
if(fDistSquare <= rangeSquare)
pItem->RequestDelete();
}
}
//─────────────────────────────────────────
BOOL GameField::KillNPC(Player* pPlayer, NPC* pNPC, BOOL bIsDrop, BOOLEAN bBrd)
{
// (WAVERIX) (090121) (CODE-REVISION)
// - 컴파일된 데이터의 이상인지 비정상적인 동작을 보이는 부분이 존재한다.
// 코드 재정렬을 수행하면 해결되므로 간단히 재정렬을 수행하기로 한다.
if( !pPlayer || !pNPC )
return FALSE;
//
if(pNPC->IsDeleteReserved())
return FALSE;
//
if(pNPC->IsAlive() == FALSE)
return FALSE;
/*
// NPC의 최대HP 만큼 데미지를 주려고 했으나, 데미지 타입이 WORD이므로 최대HP를 타입캐스팅할 때 수치가 잘못된다!
// 따라서 일단 현재 HP를 1로 셋팅한 후에 데미지 100을 주어서 몬스터를 죽이는 방식으로 바꾸었다.
pNPC->SetHP( 1 );
DAMAGETYPE wDamage = 100;
*/
// 운영팀의 요청으로 원래대로 처리함(2006.7.21)
// (WAVERIX) Dummy Code : Compile Issue
static NPC* npc_s = 0;
; npc_s = pNPC;
const DWORD maxHP = npc_s->GetMaxHP();
const DWORD maxSD = npc_s->GetMaxSD();
// f100908.1L
DamageArgs damage_args(pPlayer, eATTACK_TYPE_MELEE);
;{
//damage_args.option_.is_critical_;
//damage_args.option_.is_mirror_;
//damage_args.limit_hp_;
//damage_args.attack_regist_kind_;
//damage_args.sd_apply_;
damage_args.damage_ = maxHP;
};
// 정상적으로 경험치, 아이템분배를 처리한다면,
if( bIsDrop )
{
pNPC->SetHP( 1 );
pNPC->Damaged(&damage_args);
//pNPC->Damaged( pPlayer, eATTACK_TYPE_MELEE, wDamage, FALSE ); //요기에서 내부적으로 OnDead를 호출한다.
}
// 단순히 NPC를 죽이려고 한다면,
else
{
pNPC->SetHP( 0 );
pNPC->StopMoving();
DestroyObject( pNPC );
}
// (WAVERIX) (NOTE) 이 시점 해당 오브젝트는 Free되어 있는 상태일 확률이 높다. 사용시 주의!
if (bBrd)
{
MSGSUB_BATTLE_PLAYER_ATTACK_BRD msg_brd;
msg_brd.Clear();
//msg_brd.dwClientSerial = 0;
pPlayer->GetPos( &msg_brd.wvCurPos );
msg_brd.dwAttackerKey = pPlayer->GetObjectKey();
//msg_brd.style_presentation = 0; //_NA003109_STYLE_PRESENTATION_IMPROVEMENT
msg_brd.byAttackType = eATTACK_SEQUENCE_FIRST;
msg_brd.dwTargetKey = pNPC->GetObjectKey();
msg_brd.wDamage = damage_args.damage_;
msg_brd.dwTargetHP = pNPC->GetHP();
//msg_brd.byEffect = 0; // GM명령어로 죽이는 것이지만 이 값에 대한 세팅을 해주어야 함!!(20060115)
pPlayer->SendPacketAroundForSync(&msg_brd);
};
return TRUE;
}
BOOL GameField::GmDamageNPC(Player* pPlayer, NPC* pNPC, const FLOAT fDamagePercent)
{
// implemented by _NA001385_20090924_DOMINATION_
if( !pPlayer || !pNPC )
return FALSE;
//
if(pNPC->IsDeleteReserved())
return FALSE;
//
if(pNPC->IsAlive() == FALSE)
return FALSE;
if (fDamagePercent)
{
const DWORD curHp = pNPC->GetHP();
const DWORD maxHp = pNPC->GetMaxHP();
const FLOAT fDamage = (FLOAT)maxHp * fDamagePercent;
const DWORD dwDamage = (DWORD)fDamage;
pNPC->DecreaseHP(dwDamage);
pNPC->SendHPChange();
}
else
{
const DWORD maxHP = pNPC->GetMaxHP();
const DWORD maxSD = pNPC->GetMaxSD();
// f100908.1L
DamageArgs damage_args(pPlayer, eATTACK_TYPE_MELEE);
;{
damage_args.damage_ = maxHP;
};
pNPC->SetHP(1);
pNPC->Damaged(&damage_args);
//pNPC->Damaged(pPlayer, eATTACK_TYPE_MELEE, wDamage, FALSE); //요기에서 내부적으로 OnDead를 호출한다.
;{
MSGSUB_BATTLE_PLAYER_ATTACK_BRD msg_brd;
msg_brd.Clear();
//msg_brd.dwClientSerial = 0;
pPlayer->GetPos(&msg_brd.wvCurPos);
msg_brd.dwAttackerKey = pPlayer->GetObjectKey();
//msg_brd.style_presentation = 0; //_NA003109_STYLE_PRESENTATION_IMPROVEMENT
msg_brd.byAttackType = eATTACK_SEQUENCE_FIRST;
msg_brd.dwTargetKey = pNPC->GetObjectKey();
msg_brd.wDamage = damage_args.damage_;
msg_brd.dwTargetHP = pNPC->GetHP();
//msg_brd.byEffect = 0;
pPlayer->SendPacketAroundForSync(&msg_brd);
};
}
return TRUE;
}
//─────────────────────────────────────────
VOID GameField::SpawnSector( DWORD dwSectorIndex, Object* pObject )
{
// 미리 리젠했으면 리턴
if( m_bPreRegen ) return;
// 플레이어 등장인지 체크
if(pObject->IsEqualObjectKind(PLAYER_OBJECT) == 0)
return;
m_pUnitRegenManager->SpawnSector( dwSectorIndex );
}
//─────────────────────────────────────────
Object* GameField::FindObject(const eOBJECT_TYPE object_type, const Object::Key object_key) const
{
Object* obj = NULL;
switch(object_type)
{
case OBJECT_OBJECT:
case CHARACTER_OBJECT:
case NONCHARACTER_OBJECT:
case NPC_OBJECT:
{
SUNLOG(
eCRITICAL_LOG,
_T("|%s|%d|뚤蹶잚謹轟槻.|ObjectType = %d|"),
__FUNCTION__,
__LINE__,
object_type
);
return NULL;
}
break;
case PLAYER_OBJECT:
{
_PLAYER_HASH::const_iterator it = m_PlayerHash.find(object_key);
if (it != m_PlayerHash.end())
{
obj = it->second;
}
}
break;
case MONSTER_OBJECT:
case SUMMON_OBJECT:
case MERCHANT_OBJECT:
case MAPNPC_OBJECT:
case LUCKYMON_OBJECT:
case TOTEMNPC_OBJECT:
case SSQMONSTER_OBJECT:
case CRYSTALWARP_OBJECT:
case FRIEND_MONSTER_OBJECT:
case SYNC_MERCHANT_OBJECT:
{
_NPC_HASH::const_iterator it = m_NPCHash.find(object_key);
if (it != m_NPCHash.end())
{
obj = it->second;
}
}
break;
case ITEM_OBJECT:
case MONEY_OBJECT:
{
_ITEM_HASH::const_iterator it = m_FieldItemHash.find(object_key);
if (it != m_FieldItemHash.end())
{
obj = it->second;
}
}
break;
case COLLECTION_OBJECT:
{
_COLLECTION_HASH::const_iterator it = m_FieldCollectHash.find(object_key);
if (it != m_FieldCollectHash.end())
{
obj = it->second;
}
}
break;
default:
{
SUNLOG(
eCRITICAL_LOG,
_T("|%s|%d|뚤蹶잚謹轟槻.|ObjectType = %d|"),
__FUNCTION__,
__LINE__,
object_type
);
return NULL;
}
}
if (obj_util::IsValid(obj, __FUNCTION__, __LINE__) == false)
{
return NULL;
}
return obj;
}
/*
// (CHANGES) (f100504.1L) change the interface specification to support a complex condition
// the replacement facility for SendXXPacketToSector<IsSendPlayer>
bool IsSendPlayer::ChkCondision(Player* pPlayer)
{
return RC::RC_COLLECT_SUCCESS == m_pMgr->ChkCondision(pPlayer,m_CollectionID);
}
*/