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

1638 lines
50 KiB
C++

#include "stdafx.h"
#include "Gamefield.h"
#include <NPCInfoParser.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
, "Map:%d,Field:%d,TriggerGroupName:%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 Fail Error! Caution!!!!!!!!!! %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(!"unexpected status");
SUNLOG(eCRITICAL_LOG, "Can not add 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]MapObject Monster(%u) Is Not Found", GetFieldCode(), pMapInfo->dwExtraInfos[eMAPOBJECT_MONSTER] );
SUNLOG( eCRITICAL_LOG, "[Field:%u]MapObject(ID:%u),(ExtraInfoValue)[TYPE(1):%u,MONSTER(2):%u,(Reserved:%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,
"轟랬冷돕꽃섞膠斤口. "
"Field[%u] MapObjectKey[%u] CollectionID[%s] "
"ExtraInfoValue[TYPE(1):%u,MONSTER(2):%u,(Reserved:%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|Object is already deleted.|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] Pos[%f, %f, %f] is failed!", 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] Pos[%f, %f, %f] is failed!", pvPos->x, pvPos->y, pvPos->z );
return NULL;
}
return pFieldMoneyItem;
}
//─────────────────────────────────────────
#include <GroupParser.h>
boost::shared_ptr<Monster> GameField::CreateMonster( WORD NPCCode, const WzVector *pvPos,
WORD wGroupID, int iAngle, BYTE byMoveType, DWORD dwMoveAreaID, WORD MissionAreaID/*미션전용임시*/ )
{
boost::shared_ptr<Monster>pSpawnMonster( AllocMonster( NPCCode, byMoveType, dwMoveAreaID ));
if( !pSpawnMonster ) return boost::shared_ptr<Monster>(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, "[Field:%u],Group[%u,%u] pBaseGroupInfo is 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;
}
//岬밍斤口渴놔 by:枷쟁팃숲 2021-06-28
//SUNLOG( eCRITICAL_LOG, "MonsterID[%u],[Field:%u],Group[%u,%u]",NPCCode, GetFieldCode(), wGroupID, wGroupID );
return pSpawnMonster;
}
//─────────────────────────────────────────
boost::shared_ptr<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("It isn't exist NPC Info for Code[%u]]"), NPCCode );
return NULL;
}
boost::shared_ptr<Monster>pMonster = boost::shared_ptr<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,Code:%u] Pos[%f, %f, %f] is failed!",
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,Code:%u] Pos[%f, %f, %f] is failed!",
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,Code:%u] Pos[%f, %f, %f] is failed!",
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,Code:%u] Pos[%f, %f, %f] is failed!",
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,Code:%u] Pos[%f, %f, %f] is failed!",
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] Pos[%f, %f, %f] is failed!",
pCollection->GetObjectKey(), WzIDToString(ch, pMapInfo->CollectionID), pMapInfo->wvPos.x, pMapInfo->wvPos.y, pMapInfo->wvPos.z );
ObjectFactory::Instance()->FreeObject( pCollection );
return NULL;
}
return pCollection;
}
//}
//─────────────────────────────────────────
boost::shared_ptr<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
boost::shared_ptr<Monster> pSpawnMonster = AllocMonster( npc_code, npc_unit_type, 0 );
if( !pSpawnMonster )
{
SUNLOG( eCRITICAL_LOG, "[SpawnMonster] 밍膠냥呵겨!" );
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] Dir[%f] is over!", pUnitRegenInfo->GetDir() );
pUnitRegenInfo->SetDir( (float)( (int)pUnitRegenInfo->GetDir() % 360 ) );
}
if( FALSE == EnterField( pSpawnMonster, &vStartPos, (int)pUnitRegenInfo->GetDir() ) )
{
ObjectFactory::Instance()->FreeObject( pSpawnMonster );
SUNLOG( eCRITICAL_LOG, "[SpawnMonster EnterField][Field:%u,Code:%u] Pos[%f, %f, %f] is failed!",
GetFieldCode(), npc_code, vStartPos.x, vStartPos.y, vStartPos.z );
return NULL;
}
return pSpawnMonster;
}
//─────────────────────────────────────────
boost::shared_ptr<Monster> GameField::AllocMonster( WORD NPCCode, BYTE byMoveType, DWORD dwMoveAreaID )
{
BASE_NPCINFO *pBaseNPCInfo = NPCInfoParser::Instance()->GetNPCInfo( NPCCode );
if( !pBaseNPCInfo )
{
SUNLOG( eCRITICAL_LOG, "[AllocMonster] MonterCode(%d)IsNotFound", NPCCode );
return NULL;
}
boost::shared_ptr<Monster>pSpawnMonster ;
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:
{
boost::shared_ptr<Monster> pMonster = boost::shared_ptr<Monster>( pObjectFactory->AllocObject( MONSTER_OBJECT ) );
pSpawnMonster = pMonster;
}
break;
}
if (pSpawnMonster == NULL) {
SUNLOG( eCRITICAL_LOG, "[AllocMonster] MonterCode(%d)/Type(%d) alloc fail", 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|Object is already deleted.|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|Object is already deleted.|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|Invalid object type.|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|Invalid object type.|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);
}
*/