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

4309 lines
132 KiB
C++

#include "stdafx.h"
#include "./ActionTriggers.h"
//==================================================================================================
#include <Global.h>
#include <PublicMath.h>
#include <3DEngine/Common/MathAssistant.h>
#include <PacketStruct_CG.h>
#include <PacketStruct_AG.h>
#include <SCSlotContainer.h>
#include <SCItemSlotContainer.h>
#include <ItemInfoParser.h>
#include <MapInfoParser.h>
#include <DropRatioInfoList.h>
#include "..\\..\\SolarTrigger\\ActionInfo.h"
#include "TriggerManagerEx.h"
#include "./Trigger.h"
#include "./Player.h"
#include <QuestManager.h>
#include <QuestManager_Concrete.h>
#include "./Map.h"
#include "GameZone.h"
#include "MovingPlanner.h"
#include "ItemManager.h"
#include "./MissionManager.h"
#include "./MissionRewardManager.h"
// 범위 데미지 위해
#include "MapObject.h"
#include "DropManager.h"
// 방향 지정 몬스터 리젠 위해
#include "PacketHandler//Handler_CG_SYNC.h"
#include "GameParty/NewGameParty.h"
#include "GameParty/NewGamePartyManager.h"
#include "StatusManager.h"
#include "GameField.h"
#include "SkillAttrCalculator.h"
#include "GameDominationField.h"
#include "AITypes.h"
#include "TargetManager.h"
#ifdef _NA_003649_20111115_BATTLE_ZONE_HARDMODE
#include "NPCReplaceInfoParser.h"
#endif
#include "CollectionStateInfo.h"
#include "CollectionManager.h"
#include "Collection.h"
#include "SummonManager.h"
#include "SummonedNPCs.h"
#include "Summoned.h"
#ifdef _NA_006826_20130722_DOMINATION_RENEWAL
#include "GameGuild.h"
#include "GameGuildManager.h"
#endif _NA_006826_20130722_DOMINATION_RENEWAL
#include "GameMissionRoom.h"
#ifdef _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER
#include "NPCStateManager.h"
#endif // _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER
//==================================================================================================
__IMPL_POOL(ACTION_CHANGE_OBJECTANI)
void
ACTION_CHANGE_OBJECTANI::OnAction()
{
WzID triggerID = getParent()->TriggerID();
getMGR()->MAP_STATE.Change(triggerID);
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, _T("[TriggerID:%u]:ACTION_CHANGE_OBJECTANI"), triggerID);
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_CHANGE_PATHTILE)
void
ACTION_CHANGE_PATHTILE::OnInit()
{
}
void
ACTION_CHANGE_PATHTILE::OnAction()
{
const ACTION_CHANGE_PATHTILE_INFO* pInfo = Get();
const BYTE attribute = pInfo->GetAttribute();
TriggerManagerEx* pTriggerManager = getMGR();
if(attribute == ns_trigger::CANMOVE)
pTriggerManager->UnsetAttribute(pInfo->GetTileID(), PTA_NO_WALK);
if(attribute == ns_trigger::CANNOTMOVE)
pTriggerManager->SetAttribute(pInfo->GetTileID(), PTA_NO_WALK);
pTriggerManager->MAP_STATE.Change(getParent()->TriggerID());
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_CHANGE_PATHTILE", getParent()->TriggerID());
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_CHANGE_OBJECTSTATE)
void
ACTION_CHANGE_OBJECTSTATE::OnAction()
{
const ACTION_CHANGE_OBJECTSTATE_INFO* pInfo = Get();
int object_id = pInfo->GetObjectID();
MapObject* pMapObject = getMGR()->FindMapObject(object_id);
DEBUG_CODE(if(!pMapObject) return ;);
switch(pInfo->GetState())
{
case ACTION_CHANGE_OBJECTSTATE_INFO::APPEAR:
{
}
break;
case ACTION_CHANGE_OBJECTSTATE_INFO::DISAPPEAR:
{
}
break;
case ACTION_CHANGE_OBJECTSTATE_INFO::REMOVE:
{
SUNLOG(eCRITICAL_LOG,
"["__FUNCTION__"] [ID:%u]:ACTION_CHANGE_OBJECTSTATE is Failed!!",
getParent()->TriggerID());
}
break;
}
getMGR()->MAP_STATE.Change(getParent()->TriggerID());
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_CHANGE_OBJECTSTATE", getParent()->TriggerID());
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_PLAY_EVENT)
void
ACTION_PLAY_EVENT::OnAction()
{
// 이벤트를 플레이하라고 알려라!
DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer == 0)
{
SUNLOG(eFULL_LOG, "[ACTION_PLAY_EVENT::OnAction] [U:%d]Player NULL!!", clicked_player_key);
return;
}
#if SUN_CODE_BACKUP
// (f100721.2L) remove code
//MSG_CG_TRIGGER_PLAY_EVENT_CMD msg;
//msg.m_nEventIndex = Get()->GetEventID();
//pPlayer->SendPacket(&msg, sizeof(msg));
// (f100721.2L) add routine to present a cinematic effect
getMGR()->MAP_STATE.Change(getParent()->TriggerID());
#endif
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_PLAY_EVENT", getParent()->TriggerID());
#endif
}
//==================================================================================================
class ActionSetAttack
{
public:
BYTE m_byTargetType;
BOOL m_bAttack;
ActionSetAttack(BYTE byTargetType, BOOL bAttack)
: m_byTargetType(byTargetType), m_bAttack(bAttack) {}
void operator()(Player* pPlayer)
{
// (WAVERIX) (090614) pStatusManager->Remove의 사용은 좀 고민해봐야 할 듯 ...
StatusManager* pStatusManager = pPlayer->GetStatusManager();
if(m_byTargetType == ACTION_SET_ISATTACK_INFO::TARGET_TYPE_USER ||
m_byTargetType == ACTION_SET_ISATTACK_INFO::TARGET_TYPE_ALL)
{
if(m_bAttack == ACTION_SET_ISATTACK_INFO::CAN_NOT_ATTACK)
pStatusManager->AllocStatus(eCHAR_STATE_ETC_DISABLE_VISION_TRIGGER);
else
pStatusManager->Remove(eCHAR_STATE_ETC_DISABLE_VISION_TRIGGER);
}
if(m_byTargetType == ACTION_SET_ISATTACK_INFO::TARGET_TYPE_MONSTER ||
m_byTargetType == ACTION_SET_ISATTACK_INFO::TARGET_TYPE_ALL)
{
if(m_bAttack == ACTION_SET_ISATTACK_INFO::CAN_NOT_ATTACK)
pStatusManager->AllocStatus(eCHAR_STATE_BLUR_TRIGGER);
else
pStatusManager->Remove(eCHAR_STATE_BLUR_TRIGGER);
}
}
void SetAttack(SATISFIED_PLAYER_PAIR rPair)
{
operator()(rPair.second);
}
};
//==================================================================================================
// 특정 유저(A)를 대상으로 A가 다른 객체 B를 공격 할수 없도록 만들거나,
// B 객체로 부터 공격 당하지 않게 만든다.
__IMPL_POOL(ACTION_SET_ISATTACK)
void
ACTION_SET_ISATTACK::OnAction()
{
const ACTION_SET_ISATTACK_INFO* pInfo = Get();
ActionSetAttack op(pInfo->GetTargetType(), pInfo->GetIsAttack());
Trigger* pTrigger = getParent();
if(DWORD clicked_player_key = pTrigger->GetClickedPlayerKey())
{
// pPlayer == 0인 조건이 생길 수 있다. 조건 추가한다.
if(Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key))
op(pPlayer);
}
else
{
pTrigger->ForeachSatisfiedPlayer(&ActionSetAttack::SetAttack, &op);
pTrigger->RemoveAllSatisfiedPlayer();
}
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_SET_ISATTACK", pTrigger->TriggerID());
#endif
}
//==================================================================================================
class WARP_PLAYER
{
public:
static void Warp(Player* pPlayer, MAPCODE enterFieldCode, WzID areaID)
{
GameZone* pZone = pPlayer->GetGameZonePtr();
if(pZone == 0)
return;
// (WAVERIX)(080610)(BUG-FIX) - 서버 강제 이동시 (죽은 상태일 경우) 발생되는 문제 해결 목적
// - related info (080604)
if(pPlayer->GetHP() == 0)
pPlayer->SetHP(1);
pZone->Warp(pPlayer, enterFieldCode, areaID);
MSG_CG_TRIGGER_PORTAL_ACK msg;
msg.m_LeaveType = msg.eLeaveType_Default;
msg.m_MapCode = pZone->GetMapCode();
msg.m_FieldCode = enterFieldCode;
msg.Encode();
pPlayer->SendPacket(&msg, sizeof(msg));
return;
}
};
class WarpControl
{
typedef GameField::_PLAYER_HASH GF_PLAYER_LIST;
public:
// Iterator 무결성 문제로 재작성
static void WarpAllPlayer(GameField* const pField, const MAPCODE fieldCode, const WzID areaID)
{
const GF_PLAYER_LIST& rPlayers = pField->GetRefPlayerList();
GF_PLAYER_LIST::const_iterator it = rPlayers.begin();
while(it != rPlayers.end())
{
Player* const pPlayer = it->second;
++it; // 아래 주석 참조.
if (obj_util::IsValid(pPlayer, __FUNCTION__, __LINE__) == false)
{
continue;
}
WARP_PLAYER::Warp(pPlayer, fieldCode, areaID);
}
/*
// 저 위에서 말한 iterator 무결성 문제란..
// WARP_PLAYER::Warp(...);
// => pZone->Warp(...);
// ==> pEnterZone->LeaveForWarp(...);
// ===> GameZone::Leave(...);
// {
// ...
// m_PlayerHash.erase(it); <= 이거 때문에 for 문을 사용하면 안됨.
// }
*/
}
};
class BackToTheVillage
{
public:
void operator()(Player* pPlayer)
{
MSG_AG_ZONE_MISSION_LEAVE_CMD msg;
msg.m_LeaveType = msg.eLeaveType_Default;
msg.m_ReturnMapCode = 0;
pPlayer->SendPacket(&msg, sizeof(msg));
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
};
//__NA001310_BATTLEZONE_PORTAL_RETURN_TARGET_MOVE_SUPPORT__
class ReturnToTargetStaticField
{
public:
ReturnToTargetStaticField(MAPCODE returnMapCode, FIELDCODE enterFieldCode, WzID areaID)
: m_ReturnMapCode(returnMapCode)
, m_EnterFieldCode(enterFieldCode)
, m_AreaID(areaID)
{}
~ReturnToTargetStaticField() {}
void operator()(Player* pPlayer)
{
pPlayer->SetStartMapCode(m_ReturnMapCode);
{
MSG_CG_TRIGGER_PORTAL_ACK msg;
msg.m_LeaveType = msg.eLeaveType_Target;
msg.m_MapCode = m_ReturnMapCode;
msg.m_FieldCode = m_EnterFieldCode;
msg.Encode();
pPlayer->SendPacket(&msg, sizeof(msg));
}
{
MSG_AG_ZONE_MISSION_LEAVE_CMD msg;
msg.m_LeaveType = msg.eLeaveType_Target;
msg.m_ReturnMapCode = m_ReturnMapCode;
pPlayer->SendPacket(&msg, sizeof(msg));
}
}
void operator()(std::pair<DWORD, Player*> pair_type)
{
operator()(pair_type.second);
}
private:
const MAPCODE m_ReturnMapCode;
const FIELDCODE m_EnterFieldCode;
const WzID m_AreaID;
};
//
class WarpPlayerForField
{
MAPCODE m_FieldCode;
WzID m_AreaID;
public:
WarpPlayerForField(MAPCODE fieldCode, WzID areaID) : m_FieldCode(fieldCode),m_AreaID(areaID) {}
~WarpPlayerForField() {}
void operator()(Player* pPlayer)
{
WARP_PLAYER::Warp(pPlayer, m_FieldCode, m_AreaID);
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
};
__IMPL_POOL(ACTION_PORTAL_PLAYER)
void
ACTION_PORTAL_PLAYER::OnAction()
{
Trigger* pTrigger = getParent();
ACTION_PORTAL_PLAYER_INFO* pInfo = Get();
MAPCODE enterFieldCode = static_cast<MAPCODE>(pInfo->GetMapCode());
int moveType = pInfo->GetType();
switch(moveType)
{
case pInfo->FIELDCODE:
{
if(pInfo->GetIsParty() == ns_trigger::PARTY)
{
// 현재 필드에 있는 사람을 모두 워프
// 조건에 해당하는 사람을 알 필요 없음
const WzID areaID = pInfo->GetAreaID();
WarpControl::WarpAllPlayer(getMGR()->GetField(), enterFieldCode, areaID);
}
else
{
ASSERT(pInfo->GetIsParty() == ns_trigger::PERSONAL);
if(DWORD clicked_player_key = pTrigger->GetClickedPlayerKey())
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer == 0)
{
SUNLOG(eFULL_LOG,
"["__FUNCTION__"] [U:%d]ACTION_REWARD_PLAYER Player NULL!!",
clicked_player_key);
return;
}
WARP_PLAYER::Warp(pPlayer, enterFieldCode, pInfo->GetAreaID());
}
else
{
/*
WarpPlayerForCondition op(enterFieldCode,Get()->GetAreaID());
getParent()->FindPlayer(op);
*/
WarpPlayerForField op(enterFieldCode, pInfo->GetAreaID());
pTrigger->ForeachSatisfiedPlayer(op);
pTrigger->RemoveAllSatisfiedPlayer();
}
}
}
break;
// 기존 코드가 선행검사(FIELDCODE == moveType)이냐 아니냐로 평가 하는 바람에 3가지 타입
//이 실재로 2가지 타입으로 사용되고 있었다. 별 수 없이 동일하게 처리한다.
case pInfo->RELATEDVILLAGE:
case pInfo->VILLAGECODE:
{
if(pInfo->GetIsParty() == ns_trigger::PARTY)
{
// 현재 필드에 있는 사람을 모두 워프
// 조건에 해당하는 사람을 알 필요 없음
BackToTheVillage op;
getMGR()->ForeachPlayer(op);
//WarpPlayerForField op(enterFieldCode,Get()->GetAreaID());
}
else
{
ASSERT(pInfo->GetIsParty() == ns_trigger::PERSONAL);
if(DWORD clicked_player_key = pTrigger->GetClickedPlayerKey())
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer == 0)
{
SUNLOG(eFULL_LOG,
"["__FUNCTION__"] [U:%d]ACTION_REWARD_PLAYER Player NULL!!",
pTrigger->GetClickedPlayerKey());
return;
}
BackToTheVillage op;
op(pPlayer);
}
else
{
BackToTheVillage op;
pTrigger->ForeachSatisfiedPlayer(op);
pTrigger->RemoveAllSatisfiedPlayer();
}
}
}
break;
//__NA001310_BATTLEZONE_PORTAL_RETURN_TARGET_MOVE_SUPPORT__
case pInfo->TARGET_RETURN:
{
BOOLEAN bParty = pInfo->GetIsParty() == ns_trigger::PARTY;
DWORD clicked_player_key = bParty ? 0 : pTrigger->GetClickedPlayerKey();
MAPCODE returnMapCode = enterFieldCode; //이 시점은 FieldCode가 아니다. MapCode
// 아래 pMapInfo에서 뻗는다면 테스트 시점 반드시 발견된다.
const sMAPINFO* pMapInfo = MapInfoParser::Instance()->FindMapInfo(returnMapCode);
BOOLEAN acceptableField = pMapInfo->byMKind == eZONETYPE_VILLAGE ||
pMapInfo->byMKind == eZONETYPE_FIELD ||
pMapInfo->byMKind == eZONETYPE_DOMINATION_FIELD ||
pMapInfo->byMKind == eZONETYPE_TUTORIAL_FIELD;
if(acceptableField == 0)
break;
enterFieldCode = pMapInfo->FCode[0];
ReturnToTargetStaticField op(returnMapCode, enterFieldCode, 0);
//3rd Arg: pInfo->GetAreaID() < 지원 여부 고민하자.
if(bParty)
{
getMGR()->ForeachPlayer(op);
}
else if(clicked_player_key)
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer == NULL)
{
SUNLOG(eFULL_LOG, "["__FUNCTION__"] [U:%d] Player NULL!!", clicked_player_key);
return;
}
op(pPlayer);
}
else
{
pTrigger->ForeachSatisfiedPlayer(op);
pTrigger->RemoveAllSatisfiedPlayer();
}
}
break;
}
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG,
"[TriggerID:%u]:ACTION_PORTAL_PLAYER",
pTrigger->TriggerID());
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_SET_OBJECTTHRUST)
void
ACTION_SET_OBJECTTHRUST::OnAction()
{
}
//==================================================================================================
// 조금 이상한 트리거지만 기획에서...
// 전체 유저에게 데미지를 준다. (영역데미지에서 영역이 빠진 버전)
__IMPL_POOL(ACTION_APPLY_DAMAGE)
class ApplyDamageHandler
{
public:
ApplyDamageHandler(DAMAGETYPE damage) : damage_(damage) {}
~ApplyDamageHandler(){}
void operator()(Player* pPlayer)
{
ASSERT(pPlayer);
DAMAGETYPE apply_damage = static_cast<DAMAGETYPE>(pPlayer->DecreaseHP(damage_));
SUNLOG(eDEV_LOG, "데미지 부여 : 트리거 데미지[%d] 실제 피해 데미지[%d] 플레이어 hp[%d]",
damage_, apply_damage, pPlayer->GetHP());
MSG_CG_TRIGGER_AREADAMAGE_BRD msg;
msg.m_dwObjectKey = pPlayer->GetObjectKey();
msg.m_wDamage = damage_;
msg.m_dwHP = pPlayer->GetHP();
#ifdef _NA_008804_20160502_COMBAT_LOG
time_t t;
util::TimeSync::time(&t);
msg.m_SvrTime = t;
#endif //(_NA_008804_20160502_COMBAT_LOG)
pPlayer->SendPacketAround((MSG_BASE_FORWARD*)&msg, sizeof(msg));
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
private:
DAMAGETYPE damage_;
};
void
ACTION_APPLY_DAMAGE::OnAction()
{
ACTION_APPLY_DAMAGE_INFO* pInfo = Get();
ApplyDamageHandler op((DAMAGETYPE)pInfo->GetDamage());
const BYTE byTarget = pInfo->GetTargetType(); // (CHANGES) (f100608.1L) (WAVERIX) add type
switch(byTarget)
{
case ACTION_APPLY_DAMAGE_INFO::TARGET_FIELD:
getMGR()->ForeachPlayer(op);
break;
case ACTION_APPLY_DAMAGE_INFO::TARGET_PERSONAL:
{
Player* pPlayer = NULL;
Trigger* pTrigger = getParent();
DWORD clickPlayer = pTrigger->GetClickedPlayerKey();
if (clickPlayer)
pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clickPlayer);
else
pPlayer = pTrigger->GetFirstSatisfiedPlayer();
if (pPlayer)
op(pPlayer);
else
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_APPLY_DAMAGE : Cant not Find Player",
pTrigger->TriggerID());
}
break;
}
}
//==================================================================================================
__IMPL_POOL(ACTION_REFLECT_DAMAGE)
void
ACTION_REFLECT_DAMAGE::OnAction()
{
}
//==================================================================================================
namespace ns_trigger {
struct CreateMonsterArgs
{
enum { MAX_NUMBER_OF_MONSTERS = 100, };
int area_id_;
int group_id_;
int number_of_monsters_;
struct MonsterInfo {
MONSTERCODE monster_code_;
short angle_;
} monters_list_[MAX_NUMBER_OF_MONSTERS];
inline void Clear() {
ZeroMemory(this, sizeof(*this));
}
inline void Init(int area_id, int group_id) {
area_id_ = area_id;
group_id_ = group_id;
}
inline void AddInfo(MONSTERCODE monster_code, short angle) {
if(number_of_monsters_ >= _countof(monters_list_))
return;
MonsterInfo& node = monters_list_[number_of_monsters_++];
node.monster_code_ = monster_code;
node.angle_ = angle;
}
};
static int /*number of regened*/
CreateMonsterByTrigger(TriggerManagerEx* pTriggerManager, CreateMonsterArgs* args)
{
WzSpecialArea* pAreaInfo = pTriggerManager->GetAreaInfo(args->area_id_);
nsAI::MovingPlanner* pMovingPlanner = pTriggerManager->GetField()->GetMovingPlanner();
//assert(pAreaInfo && pGameField);
//assert(args->number_of_monters > 0);
int number_of_regened = 0;
const ENUM_BVTYPE bounding_volume_type = pAreaInfo->m_wbvRegion.m_eBvType;
if(0) {
}
else if(bounding_volume_type == BVT_PATH_TILE_INDEX)
{
BvPathTileIndex& rTileIndex = pAreaInfo->m_wbvRegion.m_BvPathTileIndex;
const WORD number_of_max_tiles = min(WORD(rTileIndex.m_nCount), args->MAX_NUMBER_OF_MONSTERS);
//assert(number_of_max_tiles != 0);
WORD number_of_tiles = 0;
WORD tile_list[args->MAX_NUMBER_OF_MONSTERS] = { 0, };
// tile은 word값을 넘지 않는다. 문제가 생기면 MovingPlanner에서 판단 가능.
DWORD* tile_it = rTileIndex.m_pdwData;
for(DWORD loop = number_of_max_tiles; loop; --loop, ++tile_it)
{
const DWORD tile_id = *tile_it;
WORD tile_attr = pMovingPlanner->GetAttribute(tile_id);
if(tile_attr & PTA_NO_WALK)
continue;
tile_list[number_of_tiles++] = WORD(tile_id);
}
if (number_of_tiles == 0) {
SUNLOG(eCRITICAL_LOG, "[%s:%d] number_of_tiles == 0", __FUNCTION__, __LINE__);
return 0;
}
CreateMonsterArgs::MonsterInfo* pMonsterInfo = args->monters_list_;
//assert args->number_of_monsters_ <= args->MAX_NUMBER_OF_MONSTERS
WORD indexes_of_tiles = number_of_tiles - 1; //if assert failed then '-1' protection fault
WzVector spawn_pos;
for(DWORD loop = args->number_of_monsters_; loop; --loop)
{
DWORD random_index = random(0, indexes_of_tiles);
pMovingPlanner->GetRandomPosInTile(tile_list[random_index], &spawn_pos);
number_of_regened += \
pTriggerManager->CreateNPC(pMonsterInfo->monster_code_, 1, &spawn_pos,
args->area_id_, args->group_id_, pMonsterInfo->angle_);
++pMonsterInfo;
}
}
else if(bounding_volume_type == BVT_SPHERE)
{
BvSphere& bvSphere = pAreaInfo->m_wbvRegion.m_BvSphere;
WORD tile_attr;
const int tile_no_1st =
pMovingPlanner->GetTileToStand(bvSphere.m_wvCenter, 0, -1.0f, -1.0f, &tile_attr);
static const float ratio_x = g_SinTable.FCOS(45);
static const float ratio_y = g_SinTable.FSIN(45);
int tile_no = -1;
WzVector selected_pos;
if((tile_attr & PTA_NO_WALK) == 0)
{
selected_pos = bvSphere.m_wvCenter;
tile_no = tile_no_1st;
}
else
{
SetVector(&selected_pos,
ratio_x * bvSphere.m_fRadius, ratio_y * bvSphere.m_fRadius, 0.0f);
ViewBoxCubic cubic;
cubic.SetInit3D(&bvSphere.m_wvCenter, &selected_pos);
WzVector& max_point = cubic.Cubic.m_wvMax;
WzVector& min_point = cubic.Cubic.m_wvMin;
WzVector points[4] = {
{ max_point.x, max_point.y, bvSphere.m_wvCenter.z },
{ max_point.x, min_point.y, bvSphere.m_wvCenter.z },
{ min_point.x, max_point.y, bvSphere.m_wvCenter.z },
{ min_point.x, min_point.y, bvSphere.m_wvCenter.z },
};
for(int i = 0; i < 4; ++i)
{
CopyMemory(&selected_pos, &points[i], sizeof(selected_pos));
tile_no = pMovingPlanner->GetTileToStand(selected_pos, 0, -1.0f, -1.0f, &tile_attr);
if(tile_no_1st != tile_no && (tile_attr & PTA_NO_WALK) == 0)
if(tile_no >= 0)
break;
}
}
for (int count = (args->number_of_monsters_ - 1); count >= 0; --count)
{
CreateMonsterArgs::MonsterInfo& monster_info = args->monters_list_[count];
number_of_regened = \
pTriggerManager->CreateNPC(monster_info.monster_code_, 1, &selected_pos,
args->area_id_, args->group_id_, monster_info.angle_);
}
}
else
{
//...
}
//pTriggerManager->CreateNPC(pInfo->GetMonsterCode(), 1, &vPos, areaID, pInfo->GetType());
return number_of_regened;
}
}; //end of namespace 'ns_trigger'
//==================================================================================================
__IMPL_POOL(ACTION_CREATE_MONSTER)
void
ACTION_CREATE_MONSTER::OnAction()
{
m_iRegenCount = 0;
m_RegenTimer.SetTimer(0); //< 처음에는 반드시 한번 실행한다.
ACTION_CREATE_MONSTER_INFO* pInfo = Get();
if(pInfo->GetIsParty() == ns_trigger::PERSONAL)
{
// 아직 유저가 맵에 들어 오지 않은상황에서 0이 될 수 있다.!!!!!@@@@
DWORD number_of_players = getMGR()->GetPlayerNum();
m_iMaxCreateCount = pInfo->GetCreateCount() * (number_of_players == 0 ? 1 : number_of_players);
}
else
m_iMaxCreateCount = pInfo->GetCreateCount();
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_CREATE_MONSTER, 개수(%d)",
getParent()->TriggerID(), m_iMaxCreateCount);
#endif
}
BOOL
ACTION_CREATE_MONSTER::OnActionUpdate()
{
const ACTION_CREATE_MONSTER_INFO* pInfo = Get();
if(m_iRegenCount == pInfo->GetLoopCount())
{
return FALSE;
}
if(m_RegenTimer.IsExpired() == 0)
return TRUE;
TriggerManagerEx* pTriggerManager = getMGR();
const int areaID = pInfo->GetAreaID();
m_RegenTimer.SetTimer(pInfo->GetDelayTime());
ns_trigger::CreateMonsterArgs args;
args.Clear();
args.Init(areaID, pInfo->GetType());
MONSTERCODE monsterCode = (MONSTERCODE)pInfo->GetMonsterCode();
#ifdef _NA_003649_20111115_BATTLE_ZONE_HARDMODE
GameZone* const game_zone = getMGR()->GetField()->GetGameZone();
if(game_zone->GetZoneType() == eZONETYPE_MISSION)
{
const MONSTERCODE replaace_monster_code = \
AppliedNpcDependencyChain::Instance()->GetRepleaceMonsterCode(game_zone, monsterCode, true);
if (replaace_monster_code) {
monsterCode = replaace_monster_code;
}
}
#endif //_NA_003649_20111115_BATTLE_ZONE_HARDMODE
for(int loop = m_iMaxCreateCount; loop; --loop)
args.AddInfo(monsterCode, -1);
ns_trigger::CreateMonsterByTrigger(pTriggerManager, &args);
++m_iRegenCount;
return TRUE;
}
//==================================================================================================
class DisPlayMsgBoxOpr
{
public:
DisPlayMsgBoxOpr(MSG_CG_TRIGGER_DISPLAY_MSGBOX_CMD& rMsgCmd, BYTE byTarget)
: m_rMsgCmd(rMsgCmd)
, m_byTarget(byTarget) {}
~DisPlayMsgBoxOpr(){}
void operator()(Player* pPlayer)
{
if (ACTION_DISPLAY_MSGBOX_INFO::TARGET_FIELD == m_byTarget)
{
pPlayer->SendPacket(&m_rMsgCmd, sizeof(m_rMsgCmd));
return;
}
GUILDGUID guildGuid = pPlayer->GetGuildGuid();
GameZone* pZone = pPlayer->GetGameZonePtr();
if (!pZone ||
INVALID_GUILDGUID == guildGuid ||
eZONETYPE_DOMINATION_FIELD != pZone->GetZoneType())
return;
GameDominationField* pDomiZone = (GameDominationField*)pZone;
#ifdef _NA_006826_20130722_DOMINATION_RENEWAL
GUILDGUID defence_guild_guid = pDomiZone->GetDefenseGuild();
GameGuild* const defense_game_guild = g_GameGuildManager.FindGuild(defence_guild_guid);
bool is_defence_guild = false; // 방어측 길드인지
if (defense_game_guild == NULL)
{
// 방어길드가 존재하지 않으면 여기있는 사람들은 모두 공격측.
is_defence_guild = false;
}
else
{
if (pPlayer->GetGuildGuid() == defence_guild_guid)
{
is_defence_guild = true;
}
else
{
GuildRelationSystem::GuildRelationEntry* defense_guild_entry = \
defense_game_guild->GetRelationEntry();
eGUILD_RELATION player_relation = defense_guild_entry->GetRelationType(pPlayer->GetGuildGuid());
if (player_relation == eGUILD_RELATION_ALLIENCE)
{
is_defence_guild = true;
}
else
{
is_defence_guild = false;
}
}
}
// 공성,수성 여부 파악해서 메세지 전달
switch(m_byTarget)
{
case ACTION_DISPLAY_MSGBOX_INFO::TARGET_OFFENCE:
if (is_defence_guild == false)
pPlayer->SendPacket(&m_rMsgCmd, sizeof(m_rMsgCmd));
break;
case ACTION_DISPLAY_MSGBOX_INFO::TARGET_DEFENCE:
if (is_defence_guild == true)
pPlayer->SendPacket(&m_rMsgCmd, sizeof(m_rMsgCmd));
break;
}
#else
switch(m_byTarget)
{
case ACTION_DISPLAY_MSGBOX_INFO::TARGET_OFFENCE:
if (guildGuid == pDomiZone->GetOffenseGuild())
pPlayer->SendPacket(&m_rMsgCmd, sizeof(m_rMsgCmd));
break;
case ACTION_DISPLAY_MSGBOX_INFO::TARGET_DEFENCE:
if (guildGuid == pDomiZone->GetDefenseGuild())
pPlayer->SendPacket(&m_rMsgCmd, sizeof(m_rMsgCmd));
break;
}
#endif // _NA_006826_20130722_DOMINATION_RENEWAL
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
private:
BYTE m_byTarget;
MSG_CG_TRIGGER_DISPLAY_MSGBOX_CMD& m_rMsgCmd;
};
__IMPL_POOL(ACTION_DISPLAY_MSGBOX)
void
ACTION_DISPLAY_MSGBOX::OnAction()
{
const ACTION_DISPLAY_MSGBOX_INFO* pInfo = Get();
const BYTE byTarget = pInfo->GetTarget();
const BYTE byPersonal = ACTION_DISPLAY_MSGBOX_INFO::TARGET_PERSONAL;
MSG_CG_TRIGGER_DISPLAY_MSGBOX_CMD msg;
msg.m_nTextID = pInfo->GetTextID();
if (byPersonal == byTarget)
{
Player* pPlayer = NULL;
Trigger* pTrigger = getParent();
DWORD clickPlayer = pTrigger->GetClickedPlayerKey();
if (clickPlayer)
pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clickPlayer);
else
pPlayer = pTrigger->GetFirstSatisfiedPlayer();
if (pPlayer)
pPlayer->SendPacket(&msg, sizeof(msg));
else
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_DISPLAY_MSGBOX : Cant not Find Player",
pTrigger->TriggerID());
return;
}
DisPlayMsgBoxOpr op(msg,byTarget);
getMGR()->ForeachPlayer(op);
}
//==================================================================================================
class RemoveItemFromInventoryForCondition
{
SLOTCODE m_ItemCode;
int m_iCount;
public:
RemoveItemFromInventoryForCondition(SLOTCODE itemCode, int count)
: m_ItemCode(itemCode), m_iCount(count) {}
~RemoveItemFromInventoryForCondition() {}
BOOL operator()(Player* pPlayer)
{
ASSERT(SAFE_NUMERIC_TYPECAST(int, m_ItemCode, SLOTCODE));
ItemManager* pItemMan = pPlayer->GetItemManager();
SLOTCODE slotCode = (SLOTCODE)m_ItemCode;
POSTYPE pos = m_iCount;
MSG_CG_ITEM_LOSE_ACK msg;
if(RC::RC_ITEM_SUCCESS == pItemMan->DeleteAllKindOfItem(1, &slotCode, &pos, &(msg.m_ItemInfo)))
{
if(msg.m_ItemInfo.m_Count != 0)
pPlayer->SendPacket(&msg, msg.GetSize());
return TRUE;
}
return FALSE;
}
//BOOL operator()(Trigger::PLAYER_TRIGGER_INFO* pPlayerInfo)
//{
// //if(pPlayerInfo->m_State)
// {
// Player* pPlayer = pPlayerInfo->m_pPlayer;
// pPlayer->GetItemManager()->Lose(m_ItemCode, m_byAllOfItem);
// }
// return FALSE;
//}
};
//==================================================================================================
__IMPL_POOL(ACTION_REMOVE_ITEM)
void
ACTION_REMOVE_ITEM::OnAction()
{
const DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
if(clicked_player_key != 0)
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer != 0)
{
int iItemCode = Get()->GetItemCode();
ASSERT(SAFE_NUMERIC_TYPECAST(int, iItemCode, SLOTCODE));
ItemManager* pItemMan = pPlayer->GetItemManager();
SLOTCODE slotCode = (SLOTCODE)iItemCode;
POSTYPE pos = Get()->GetItemNum();
MSG_CG_ITEM_LOSE_ACK msg;
if(RC::RC_ITEM_SUCCESS == pItemMan->DeleteAllKindOfItem(1, &slotCode, &pos, &(msg.m_ItemInfo)))
{
if(msg.m_ItemInfo.m_Count != 0)
pPlayer->SendPacket(&msg, msg.GetSize());
}
}
else
{
SUNLOG(eFULL_LOG, "[U:%d]ACTION_REMOVE_ITEM Player NULL!!", clicked_player_key);
return;
}
}
else
{
RemoveItemFromInventoryForCondition op(Get()->GetItemCode(), Get()->GetItemNum());
getParent()->ForeachSatisfiedPlayer(op);
getParent()->RemoveAllSatisfiedPlayer();
}
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_REMOVE_ITEM", getParent()->TriggerID());
#endif
}
// 필드상에 있는 플레이어에게 인벤토리에 보상아이템을 준다.
class DistributeItemToInventoryOnField : public std::unary_function<Character *, void>
{
ACTION_REWARD_PLAYER* m_pReward;
public :
DistributeItemToInventoryOnField(ACTION_REWARD_PLAYER* pReward):m_pReward(pReward) {}
~DistributeItemToInventoryOnField(){}
void operator()(Player* pPlayer) const
{
GameField* pField = pPlayer->GetField();
if(pField == 0)
return;
WzVector vec = pPlayer->GetRandomPosInAround(2);
SCItemSlot slot;
slot.SetCode(m_pReward->Get()->GetItemCode());
g_DropManager.DropItemToField(pField, &vec, pPlayer->GetObjectKey(), 0, slot);
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
};
#ifdef __WAVERIX_UNUSED_SECTION__
// 조건에 합당한 플레이어에게 인벤토리에 보상을 준다.
class DistributeItemToInventoryForCondition
{
CODETYPE m_ItemCode;
int m_Count;
public:
DistributeItemToInventoryForCondition(CODETYPE ItemCode, int count)
: m_ItemCode(ItemCode), m_Count(count) {}
~DistributeItemToInventoryForCondition() {}
BOOL operator()(Trigger::PLAYER_TRIGGER_INFO* pPlayerInfo)
{
Player* pPlayer = pPlayerInfo->m_pPlayer;
///g_DropManager.DropItemToField(pPlayer->GetUserKey(), m_ItemCode);
///!!!!! temp TEMPTEMPTEMTPE
///!!!!! temp TEMPTEMPTEMTPE
///!!!!! temp TEMPTEMPTEMTPE
///!!!!! temp TEMPTEMPTEMTPE
///!!!!! temp TEMPTEMPTEMTPE
///!!!!! temp TEMPTEMPTEMTPE
///!!!!! temp TEMPTEMPTEMTPE
return FALSE;
}
};
#endif
//==================================================================================================
__IMPL_POOL(ACTION_REWARD_PLAYER)
void
ACTION_REWARD_PLAYER::OnAction()
{
const BASE_ITEMINFO* pItemInfo = ItemInfoParser::Instance()->GetItemInfo(Get()->GetItemCode());
if(pItemInfo == 0)
{
// 에러 처리
return;
}
ASSERT(Get()->GetCount() > 0);
DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
// 보상 대상이 개인으로 되어있고, 클릭한 플레이어가 존재할경우에,, 해당 플레이어 주변에 아이템준다
if( Get()->GetIsParty() == false && clicked_player_key != 0 )
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer)
{
DistributeItemToInventoryOnField op((ACTION_REWARD_PLAYER*)this);
op(pPlayer);
}
}
// 클릭한 플레이어가 존재하는 경우, 해당 플레이어가 속한 파티맴버들 주변에 아이템준다
else if( clicked_player_key != 0 )
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer)
{
WORD partyKey = pPlayer->GetPartyState().GetPartyKey();
DistributeItemToInventoryOnField Opr(this);
if(!NewGamePartyManager::Instance()->For_eachPlayers(partyKey, Opr))
SUNLOG(eCRITICAL_LOG,
"[ACTION_REWARD_PLAYER] GameParty is NULL! (%s)'s PartyKey (%u)",
pPlayer->GetCharName(), partyKey);
return;
}
SUNLOG(eCRITICAL_LOG, "[ACTION_REWARD_PLAYER] Player is NULL! ClickedUserKey %u",
clicked_player_key);
}
// 모든 사람에게 아이템을 준다
else
{
DistributeItemToInventoryOnField op((ACTION_REWARD_PLAYER*)this);
getMGR()->ForeachPlayer(op);
}
}
//==================================================================================================
__IMPL_POOL(ACTION_CLEAR_MISSION)
void
ACTION_CLEAR_MISSION::OnAction()
{
getMGR()->ClearMission();
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_CLEAR_MISSION", getParent()->TriggerID());
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_PORTAL_RANDOM)
void
ACTION_PORTAL_RANDOM::OnAction()
{
ACTION_PORTAL_RANDOM_INFO* pInfo = Get();
// 해당 영역을 얻어와서
MAPCODE enterFieldCode = static_cast<MAPCODE>(pInfo->GetMapID());
if(pInfo->GetRandom() == 0)
{
int nRandArea = rand() % 8;
// 랜덤하게 워프
if(pInfo->GetTarget() == ns_trigger::PARTY)
{
// 현재 필드에 있는 사람을 모두 워프
// 조건에 해당하는 사람을 알 필요 없음
const WzID areaID = pInfo->GetAreaID(nRandArea);
WarpControl::WarpAllPlayer(getMGR()->GetField(), enterFieldCode, areaID);
}
else
{
ASSERT(pInfo->GetTarget() == ns_trigger::PERSONAL);
if(DWORD clicked_player_key = getParent()->GetClickedPlayerKey())
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(NULL == pPlayer)
{
SUNLOG(eFULL_LOG, "["__FUNCTION__"] [U:%d] Player NULL!!", clicked_player_key);
return;
}
WARP_PLAYER::Warp(pPlayer, enterFieldCode, pInfo->GetAreaID(nRandArea));
}
else
{
WarpPlayerForField op(enterFieldCode,pInfo->GetAreaID(nRandArea));
getParent()->ForeachSatisfiedPlayer(op);
getParent()->RemoveAllSatisfiedPlayer();
}
}
}
else //if(pInfo->GetRandom() == ACTION_PORTAL_RANDOM::_ORDER)
{
// 파티원들을 순서대로 이동시킨다
if(pInfo->GetTarget() == ns_trigger::PARTY)
{
// 현재 필드에 있는 사람을 모두 워프
// 조건에 해당하는 사람을 알 필요 없음
BackToTheVillage op;
getMGR()->ForeachPlayer(op);
//WarpPlayerForField op(EnterFieldCode,pInfo->GetAreaID());
}
else
{
ASSERT(pInfo->GetTarget() == ns_trigger::PERSONAL);
if(DWORD clicked_player_key = getParent()->GetClickedPlayerKey())
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(NULL == pPlayer)
{
SUNLOG(eFULL_LOG, "["__FUNCTION__"] [U:%d]ACTION_REWARD_PLAYER Player NULL!!",
clicked_player_key);
return;
}
BackToTheVillage op;
op(pPlayer);
}
else
{
BackToTheVillage op;
getParent()->ForeachSatisfiedPlayer(op);
getParent()->RemoveAllSatisfiedPlayer();
}
}
}
}
__IMPL_POOL(ACTION_NPC_RANDOM_AREA_MOVE)
void
ACTION_NPC_RANDOM_AREA_MOVE::OnAction()
{
ACTION_NPC_RANDOM_AREA_MOVE_INFO* pInfo = Get();
int nRandArea = rand() % 8;
const WzID areaID = pInfo->GetAreaID(nRandArea);
DWORD object_key = getParent()->GetObjectKey();
NPC* const npc = getMGR()->FindNPC(object_key);
if (npc == NULL)
return;
GameField* pField = npc->GetField();
CWorldBase* pWorldBase = pField->GetFieldInfo()->GetWorldBase();
const int number_of_special_areas = pWorldBase->GetNumberOfSpecialArea();
WzSpecialArea* pSpecialArea = NULL;
if (!FlowControl::FCAssert(areaID < number_of_special_areas &&
(pSpecialArea = pWorldBase->GetSpecialAreaByIndex(areaID)) != NULL))
{
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|斤口=can't find special area (index = %u)"),
areaID);
return;
}
WzBoundingVolume& rVolume = pSpecialArea->m_wbvRegion;
if(BVT_PATH_TILE_INDEX == rVolume.m_eBvType)
{
WzVector wvTarget = {0,0,0};
BvPathTileIndex& rPATHTILE = rVolume.m_BvPathTileIndex;
DWORD dwSelectedTile = random(0, rPATHTILE.m_nCount - 1);
nsAI::MovingPlanner* const pMovingPlanner = pField->GetMovingPlanner();
wvTarget = pMovingPlanner->GetRandomPosInTile(rPATHTILE.m_pdwData[dwSelectedTile]);
if (pInfo->GetMoveOrWarp() == 0) { // 0: move
#ifdef _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER
npc->SetRegenPos(wvTarget);
npc->GetStateManager()->SetMoveAreaID(pSpecialArea->m_wiIdentity);
npc->ChangeState(STATE_ID_TRACK_AREA);
#else
npc->MoveAndBroadcast(&wvTarget, CMS_RUN);
#endif // _NA_007907_20141222_MODIFY_MONSTER_MOVE_TRIGGER
}
else if (pInfo->GetMoveOrWarp() == 1 ) {// 1: teleport
pField->TeleportObject(npc, &wvTarget);
}
}
}
//==================================================================================================
__IMPL_POOL(ACTION_ACTIVATE_TRIGGER)
void
ACTION_ACTIVATE_TRIGGER::OnAction()
{
const DWORD wz_trigger_id = Get()->GetWzTriggerID();
if(Get()->GetFlag() == 0)
getMGR()->InsertActiveTrigger(wz_trigger_id);
else
getMGR()->RequestRemoveActiveTrigger(wz_trigger_id);
// (f100721.2L) add routine to support a various planner trigger logic
getMGR()->MAP_STATE.Change(getParent()->TriggerID());
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_ACTIVATE_TRIGGER", getParent()->TriggerID());
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_AREA_DAMAGE)
class AreaDamageHandler
{
DWORD m_dwObjectKey;
float m_dist;
WzVector m_vPos;
DAMAGETYPE m_wDamage;
public:
AreaDamageHandler(DWORD dwObjectKey, WzVector* pvPos, float dist, DAMAGETYPE wDamage)
{
m_dwObjectKey = dwObjectKey;
m_vPos = *pvPos;
m_dist = dist * dist;
m_wDamage = wDamage;
}
~AreaDamageHandler(){}
void operator()(Player* const pPlayer)
{
const WzVector& curPos = *pPlayer->GetPosPtr();
WzVector wvDist = m_vPos - curPos;
float fDist2 = VectorLength2(&wvDist);
if(fDist2 <= m_dist)
{
pPlayer->DecreaseHP(m_wDamage);
// (WAVERIX) (061018) 패킷 처리 최적화할 여지가 보인다
MSG_CG_TRIGGER_AREADAMAGE_BRD msg;
msg.m_dwObjectKey = pPlayer->GetObjectKey();
msg.m_wDamage = m_wDamage;
msg.m_dwHP = pPlayer->GetHP();
#ifdef _NA_008804_20160502_COMBAT_LOG
time_t t;
util::TimeSync::time(&t);
msg.m_SvrTime = t;
#endif //(_NA_008804_20160502_COMBAT_LOG)
pPlayer->SendPacketAround((MSG_BASE_FORWARD*)&msg, sizeof(msg));
}
}
void operator()(std::pair<DWORD, Player *> pair_type)
{
operator()(pair_type.second);
}
};
void
ACTION_AREA_DAMAGE::OnAction()
{
ACTION_AREA_DAMAGE_INFO* pInfo = Get();
// 오브젝트 받아서
MapObject* pMapObject = getMGR()->FindMapObject(pInfo->GetObjectIndex());
ASSERT(NULL != pMapObject);
DEBUG_CODE(if(!pMapObject) return ;);
// 범위 안에 타겟(개인/파티) 있는지 체크하여 데미지 주자
WzVector vPos = *pMapObject->GetPosPtr();
float fRange = pInfo->GetRange();
AreaDamageHandler handler(pInfo->GetObjectIndex(), &vPos, fRange,
static_cast<DAMAGETYPE>(pInfo->GetDamage()));
getMGR()->ForeachPlayer(handler); // 범위 안에 개인이든 파티든 있으면 다 주는 거 아닌가?
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[ID:%u]:ACTION_AREA_DAMAGE", getParent()->TriggerID());
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_OPERATE_SWITCH)
void
ACTION_OPERATE_SWITCH::OnAction()
{
const ACTION_OPERATE_SWITCH_INFO& rInfo = *Get();
int cond_value = (int)getMGR()->GetSwitchValue(rInfo.GetSwitchID());
int argument = rInfo.GetArgument();
int apply_value = 0;
switch(rInfo.GetOperationType())
{
case eOPERATION_EQUAL: apply_value = argument; break;
case eOPERATION_PLUS: apply_value = (int)cond_value + argument; break;
case eOPERATION_MINUS: apply_value = (int)cond_value - argument; break;
case eOPERATION_MULTIPLE: apply_value = (int)cond_value * argument; break;
case eOPERATION_DIVIDE: argument ? apply_value = (int)(cond_value / argument) : 0; break;
}
if(DWORD(apply_value) >= 256 ||
(argument == 0 && rInfo.GetOperationType() == eOPERATION_DIVIDE))
{
SUNLOG(eCRITICAL_LOG, "[ACTION_OPERATE_SWITCH] OperationType[%u] Result[%d]",
rInfo.GetOperationType(), apply_value);
return;
}
if(SAFE_NUMERIC_TYPECAST(int, apply_value, BYTE))
getMGR()->SetSwitchValue(rInfo.GetSwitchID(), (BYTE)apply_value);
else
{
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_OPERATE_SWITCH", getParent()->TriggerID());
#endif
}
}
__IMPL_POOL(ACTION_OPERATE_SWITCH_EX)
void
ACTION_OPERATE_SWITCH_EX::OnAction()
{
const ACTION_OPERATE_SWITCH_EX_INFO& rInfo = *Get();
if (rInfo.GetTarget() == ns_trigger::ZONE)
{
#ifdef _NA_007369_20140625_ACTION_OPERATE_SWITCH_EX
GameZone* const game_zone = getMGR()->GetField()->GetGameZone();
if(game_zone->GetZoneType() == eZONETYPE_MISSION)
{
GameMissionRoom* mission_room = static_cast<GameMissionRoom*>(game_zone);
int cond_value = (int)mission_room->GetSwitchValue(rInfo.GetSwitchID());
int argument = rInfo.GetArgument();
int apply_value = 0;
switch(rInfo.GetOperationType())
{
case eOPERATION_EQUAL: apply_value = argument; break;
case eOPERATION_PLUS: apply_value = (int)cond_value + argument; break;
case eOPERATION_MINUS: apply_value = (int)cond_value - argument; break;
case eOPERATION_MULTIPLE: apply_value = (int)cond_value * argument; break;
case eOPERATION_DIVIDE: argument ? apply_value = (int)(cond_value / argument) : 0; break;
}
if(DWORD(apply_value) >= 256 ||
(argument == 0 && rInfo.GetOperationType() == eOPERATION_DIVIDE))
{
SUNLOG(eCRITICAL_LOG, "[ACTION_OPERATE_SWITCH] OperationType[%u] Result[%d]",
rInfo.GetOperationType(), apply_value);
return;
}
if(SAFE_NUMERIC_TYPECAST(int, apply_value, BYTE))
mission_room->SetSwitchValue(rInfo.GetSwitchID(), (BYTE)apply_value);
else
{
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_OPERATE_SWITCH", getParent()->TriggerID());
#endif
}
}
#endif //_NA_007369_20140625_ACTION_OPERATE_SWITCH_EX
}
}
//==================================================================================================
__IMPL_POOL(ACTION_ROAD_CONNECT)
void
ACTION_ROAD_CONNECT::OnAction() // 일정 영역의 길 연결을 설정 또는 해제 한다
{
const ACTION_ROAD_CONNECT_INFO* road_connect_action = Get();
const INT area_index = road_connect_action->GetAreaIndex();
TriggerManagerEx* const trigger_manager = getMGR();
// 지역이 유효한지 검사한다.
const WzSpecialArea* const area = trigger_manager->GetAreaInfo(area_index);
if (!area)
{
SUNLOG(eCRITICAL_LOG,
_T("|%s|지역이 유효하지 않습니다.|AreaIndex = %d|"),
__FUNCTION__,
area_index);
return;
}
// 영역 형식이 유효한지 검사한다.
const WzBoundingVolume& region = area->m_wbvRegion;
if (region.m_eBvType != BVT_PATH_TILE_INDEX)
{
SUNLOG(eCRITICAL_LOG,
_T("|%s|영역 형식이 유효하지 않습니다.|RegionType = %d|"),
__FUNCTION__,
region.m_eBvType);
return;
}
// 설정값이 유효한지 검사한다.
const BYTE can_go = road_connect_action->GetCanGo();
if (can_go != ns_trigger::CANMOVE && can_go != ns_trigger::CANNOTMOVE)
{
SUNLOG(eCRITICAL_LOG,
_T("|%s|설정값이 유효하지 않습니다.|CanGo = %u|"),
__FUNCTION__,
can_go);
return;
}
// 각각의 타일에 대해 설정값을 적용한다.
const BvPathTileIndex& tile_list = region.m_BvPathTileIndex;
for (DWORD i = 0; i < tile_list.m_nCount; ++i)
{
const INT tile_id = static_cast<INT>(tile_list.m_pdwData[i]);
if (can_go == ns_trigger::CANMOVE)
{
trigger_manager->UnsetAttribute(tile_id, PTA_NO_WALK);
}
else if (can_go == ns_trigger::CANNOTMOVE)
{
trigger_manager->SetAttribute(tile_id, PTA_NO_WALK);
}
}
(trigger_manager->MAP_STATE).Change(getParent()->TriggerID());
}
//==================================================================================================
__IMPL_POOL(ACTION_CREATE_DIR_MONSTER)
void
ACTION_CREATE_DIR_MONSTER::OnAction()
{
const ACTION_CREATE_DIR_MONSTER_INFO& rInfo = *Get();
m_iRegenCount = 0;
m_RegenTimer.SetTimer(0); //< 처음에는 반드시 한번 실행한다.
if(rInfo.GetIsParty() == ns_trigger::PERSONAL)
{
// 아직 유저가 맵에 들어 오지 않은상황에서 0이 될 수 있다.!!!!!@@@@
DWORD userCount = getMGR()->GetPlayerNum();
m_iMaxCreateCount = rInfo.GetCreateCount() * (userCount == 0 ? 1 : userCount);
}
else
m_iMaxCreateCount = rInfo.GetCreateCount();
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_CREATE_MONSTER,개수(%d)",
getParent()->TriggerID(), m_iMaxCreateCount);
#endif
}
BOOL
ACTION_CREATE_DIR_MONSTER::OnActionUpdate()
{
const ACTION_CREATE_DIR_MONSTER_INFO& rInfo = *Get();
if(m_iRegenCount == rInfo.GetLoopCount())
{
return FALSE;
}
if(m_RegenTimer.IsExpired() == 0)
return TRUE;
m_RegenTimer.SetTimer(rInfo.GetDelayTime());
ns_trigger::CreateMonsterArgs args;
args.Clear();
args.Init(rInfo.GetAreaID(), rInfo.GetType());
MONSTERCODE monsterCode = (MONSTERCODE)rInfo.GetMonsterCode();
#ifdef _NA_003649_20111115_BATTLE_ZONE_HARDMODE
GameZone* game_zone = getMGR()->GetField()->GetGameZone();
if(game_zone->GetZoneType() == eZONETYPE_MISSION)
{
MONSTERCODE replaace_monster_code = \
AppliedNpcDependencyChain::Instance()->GetRepleaceMonsterCode(game_zone, monsterCode, true);
if (replaace_monster_code) {
monsterCode = replaace_monster_code;
}
}
#endif //_NA_003649_20111115_BATTLE_ZONE_HARDMODE
WzVector vDirection = { rInfo.GetDirX(), rInfo.GetDirY(), rInfo.GetDirZ() };
short angle = 0;
{
float fangle = Math_GetAngleFromVector(&vDirection); // (NOTE) 안에서 Normalize수행.
angle = static_cast<short>(fangle * 180.0f / WZ_PI);
}
for(int loop = m_iMaxCreateCount; loop; --loop)
args.AddInfo(monsterCode, angle);
ns_trigger::CreateMonsterByTrigger(getMGR(), &args);
++m_iRegenCount;
return TRUE;
}
//==================================================================================================
__IMPL_POOL(ACTION_CHANGE_LIGHT)
void
ACTION_CHANGE_LIGHT::OnAction()
{
getMGR()->MAP_STATE.Change(getParent()->TriggerID());
}
//==================================================================================================
__IMPL_POOL(ACTION_CHANGE_OBJECT_TYPE)
void
ACTION_CHANGE_OBJECT_TYPE::OnAction()
{
getMGR()->MAP_STATE.Change(getParent()->TriggerID());
}
struct ActionOperator
{
class QUEST
{
public:
// ActionTrigger <퀘스트 만족여부>
class Satisfaction
{
QCODE m_QuestCode;
public:
Satisfaction(QCODE wQuestCode) : m_QuestCode(wQuestCode) {}
void operator()(Player* pPlayer)
{
// <060606, waverix>
// not implementation
SUNLOG(eFULL_LOG, "ActionOperator::QUEST::Satisfaction is not implemented.");
//Quest* pQuest = pPlayer->GetQuestManager()->FindQuest(m_QuestCode);
//if(!pQuest)
//{
//
//}
}
void Process(SATISFIED_PLAYER_PAIR rPair)
{
// <060606, waverix>
// not implementation
SUNLOG(eCRITICAL_LOG, "ActionOperator::QUEST::Satisfaction is not implemented.");
}
}; //ActionOperator::QUEST::Satisfaction
// ActionTrigger <퀘스트 부여>
class Grant
{
QCODE m_QuestCode;
public:
Grant(QCODE wQuestCode) : m_QuestCode(wQuestCode) {}
// 아래의 코드는
void operator()(Player* pPlayer)
{
QuestManager* pQuestManager = pPlayer->GetQuestManager();
//__NA_001337_20090511_EXPANSION_QUEST_COMPL_NUM
if(pQuestManager->IsQuestMax())
{
MSG_CG_QUEST_ACCEPT_NAK msg;
msg.m_byErrorCode = RC::RC_QUEST_CANNOTQUEST_MAXCOUNT;
pPlayer->SendPacket(&msg, sizeof(msg));
return ;
}
//
Quest* pQuest = pQuestManager->CreateQuest(m_QuestCode);
if(pQuest && (!pQuest->GetQuestInfo()->IsChildQuest()))
{
if(pQuestManager->CanDoAccept(m_QuestCode, pPlayer) == RC::RC_QUEST_SUCCESS)
{
// (WAVERIX) (060607) 아래의 패킷은 트리거용으로 하나 새로 만들어서 사용할 것.
MSG_CG_QUEST_ACCEPT_ACK msg;
pPlayer->SendPacket(&msg, sizeof(msg));
return;
}
}
pQuestManager->DestroyQuest(m_QuestCode);
}
void Process(SATISFIED_PLAYER_PAIR rPair)
{
Player* pPlayer = rPair.second;
QuestManager* pQuestMan = pPlayer->GetQuestManager();
//__NA_001337_20090511_EXPANSION_QUEST_COMPL_NUM
if(pQuestMan->IsQuestMax())
{
MSG_CG_QUEST_ACCEPT_NAK msg;
msg.m_byErrorCode = RC::RC_QUEST_CANNOTQUEST_MAXCOUNT;
pPlayer->SendPacket(&msg, sizeof(msg));
return ;
}
//
Quest* pQuest = pQuestMan->CreateQuest(m_QuestCode);
if(pQuest && (!pQuest->GetQuestInfo()->IsChildQuest()))
{
if(pQuestMan->CanDoAccept(m_QuestCode, pPlayer) == RC::RC_QUEST_SUCCESS)
{
// (WAVERIX) (060607) 아래의 패킷은 트리거용으로 하나 새로 만들어서 사용할 것.
MSG_CG_QUEST_ACCEPT_ACK msg;
pPlayer->SendPacket(&msg, sizeof(msg));
return;
}
}
pQuestMan->DestroyQuest(m_QuestCode);
}
}; // ActionOperator::QUEST::Grant
class Setup
{
QCODE m_QuestCode;
public:
Setup(QCODE wQuestCode) : m_QuestCode(wQuestCode) {}
// 아래의 코드는
void operator()(Player* pPlayer)
{
QuestManager* pQuestManager = pPlayer->GetQuestManager();
Quest* pQuest = pQuestManager->FindQuest(m_QuestCode);
if(pQuest == 0)
return;
if(pQuestManager->CanComplete(m_QuestCode, pPlayer) == RC::RC_QUEST_SUCCESS)
{
// 보상 체계를 갖춰야 하는가?
pQuestManager->DoReward(m_QuestCode, pPlayer);
//__NA_001337_20090511_EXPANSION_QUEST_COMPL_NUM
QUESTPART_COMPL* pCompNum = pQuestManager->FindComplQuest(m_QuestCode);
if(pCompNum && (0xFF == pCompNum->m_Num))
{
//(lst1024)(090611) 무한 반복퀘를 255회 이상하는 유저가 많아졌다.
// 혹시 다른 이유일수 있으니 로그 남긴다.
SUNLOG(eCRITICAL_LOG,
"["__FUNCTION__"] char(%s), guid(%d), Quest(%d), num(%d)",
pPlayer->GetCharName(), pPlayer->GetCharGuid(),
m_QuestCode, pCompNum->m_Num);
}
//SUNLOG(eCRITICAL_LOG, _T("Setup %u"), m_QuestCode);
}
return;
}
void Process(SATISFIED_PLAYER_PAIR rPair)
{
Player* pPlayer = rPair.second;
QuestManager* pQuestManager = pPlayer->GetQuestManager();
Quest* pQuest = pQuestManager->FindQuest(m_QuestCode);
if(pQuest == 0)
return;
if(pQuestManager->CanComplete(m_QuestCode, pPlayer) == RC::RC_QUEST_SUCCESS)
{
// 보상 체계를 갖춰야 하는가?
pQuestManager->DoReward(m_QuestCode, pPlayer);
//__NA_001337_20090511_EXPANSION_QUEST_COMPL_NUM
QUESTPART_COMPL* pCompNum = pQuestManager->FindComplQuest(m_QuestCode);
if(pCompNum && (0xFF == pCompNum->m_Num))
{
//(lst1024)(090611) 무한 반복퀘를 255회 이상하는 유저가 많아졌다.
// 혹시 다른 이유일수 있으니 로그 남긴다.
SUNLOG(eCRITICAL_LOG,
"["__FUNCTION__"] char(%s), guid(%d), Quest(%d), num(%d)",
pPlayer->GetCharName(), pPlayer->GetCharGuid(),
m_QuestCode, pCompNum->m_Num);
}
//SUNLOG(eCRITICAL_LOG, _T("Setup %u"), m_QuestCode);
}
return;
}
};
}; // ActionOperator::QUEST
}; // ActionOperator
//==================================================================================================
// <Waverix>
// Quest 관련 미구현 또는 분석 미비로 인한 트리거 항목 완료 미설정
__IMPL_POOL(ACTION_QUEST_SATISFACTION)
void
ACTION_QUEST_SATISFACTION::OnAction()
{
const ACTION_QUEST_SATISFACTION_INFO* pInfo = (const ACTION_QUEST_SATISFACTION_INFO*)Get();
ASSERT(SAFE_NUMERIC_TYPECAST(ULONG, pInfo->GetQuestID(), QCODE));
ActionOperator::QUEST::Satisfaction op((QCODE)pInfo->GetQuestID());
if(DWORD clicked_player_key = getParent()->GetClickedPlayerKey())
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer)
{
op(pPlayer);
return;
}
SUNLOG(eFULL_LOG, "[ACTION_QUEST_SATISFACTION::OnAction] [U:%d] Player NULL!!",
clicked_player_key);
return;
}
else
{
// Satisfied Player
getParent()->ForeachSatisfiedPlayer(&ActionOperator::QUEST::Satisfaction::Process, &op);
getParent()->RemoveAllSatisfiedPlayer();
}
}
//==================================================================================================
__IMPL_POOL(ACTION_QUEST_GRANT)
void
ACTION_QUEST_GRANT::OnAction()
{
const ACTION_QUEST_GRANT_INFO* pInfo = (const ACTION_QUEST_GRANT_INFO*)Get();
ASSERT(SAFE_NUMERIC_TYPECAST(ULONG, pInfo->GetQuestID(), QCODE));
ActionOperator::QUEST::Grant op((QCODE)pInfo->GetQuestID());
if(DWORD clicked_player_key = getParent()->GetClickedPlayerKey())
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer)
{
op(pPlayer);
return;
}
SUNLOG(eFULL_LOG, "[ACTION_QUEST_SATISFACTION::OnAction] [U:%d] Player NULL!!",
clicked_player_key);
}
else
{
// Satisfied Player
getParent()->ForeachSatisfiedPlayer(&ActionOperator::QUEST::Grant::Process, &op);
getParent()->RemoveAllSatisfiedPlayer();
}
}
__IMPL_POOL(ACTION_QUEST_SETUP)
void
ACTION_QUEST_SETUP::OnAction()
{
const ACTION_QUEST_SETUP_INFO* pInfo = (const ACTION_QUEST_SETUP_INFO*)Get();
ASSERT(SAFE_NUMERIC_TYPECAST(ULONG, pInfo->GetQuestID(), QCODE));
QCODE questCode = (QCODE)pInfo->GetQuestID();
ActionOperator::QUEST::Setup op((QCODE)pInfo->GetQuestID());
const DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
if(clicked_player_key != 0)
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer)
{
op(pPlayer);
return;
}
SUNLOG(eFULL_LOG, "[ACTION_QUEST_SATISFACTION::OnAction] [U:%d] Player NULL!!", clicked_player_key);
}
else
{
// Satisfied Player
getParent()->ForeachSatisfiedPlayer(&ActionOperator::QUEST::Setup::Process, &op);
getParent()->RemoveAllSatisfiedPlayer();
}
}
//==================================================================================================
__IMPL_POOL(ACTION_USE_CATEGORY)
void
ACTION_USE_CATEGORY::OnAction()
{
}
//==================================================================================================
// 기존에 존재하는 액션 트리거
//__IMPL_POOL(ACTION_CREATE_MONSTER)
__IMPL_POOL(ACTION_CREATE_MONSTER_GROUP)
void
ACTION_CREATE_MONSTER_GROUP::OnAction()
{
// 다음의 루틴은 ACTION_CREATE_MONSTER 의 로직을 따라한 것이다.
m_iRegenCount = 0;
m_dwTimeout = 0;
const ACTION_CREATE_MONSTER_GROUP_INFO* pInfo = Get();
if(ns_trigger::PARTY == pInfo->GetPartyState())
{
m_iMaxCreateCount = pInfo->GetGroupCount();
}
else //ns_trigger::PERSONAL
{
// 아직 유저가 맵에 들어 오지 않은상황에서 0이 될 수 있다.!!!!!@@@@
DWORD count = getMGR()->GetPlayerNum();
m_iMaxCreateCount = pInfo->GetGroupCount() * (count == 0 ? 1 : count);
}
}
BOOL
ACTION_CREATE_MONSTER_GROUP::OnActionUpdate()
{
const ACTION_CREATE_MONSTER_GROUP_INFO* pInfo = Get();
if(pInfo->GetGroupCount() == m_iRegenCount)
{
return FALSE;
}
else
{
// 몬스터 그룹 생성은 주기가 없다. 하지만 한번에 생성함으로 인한 부하(꼭 서버만의 부하는 아닐터)
// 를 감안해 적당한 주기를 주고 생성하기로 하자. 단 이를 위한 테스트는 해봐야 할 것이다.
const DWORD dwInterval = 100000; //10초?
DWORD dwCurTick = GetTickCount();
if(m_dwTimeout < dwCurTick)
{
m_dwTimeout = dwCurTick + dwInterval;
ASSERT(SAFE_NUMERIC_TYPECAST(int, pInfo->GetAreaID(), DWORD));
ASSERT(SAFE_NUMERIC_TYPECAST(int, pInfo->GetAreaID(), WORD));
// !!!!!!!몬스터 그룹 생성 코드가 빠져있다!!!!!!
}
}
return TRUE;
}
//==================================================================================================
__IMPL_POOL(ACTION_SHOW_OR_HIDE_UNIT)
void
ACTION_SHOW_OR_HIDE_UNIT::OnAction()
{
// 맵툴의 관점에서는 몬스터 나타남/사라짐이지만, 실제 서버에서는
// 몬스터 유닛 생성 / 파괴
//__NA_276_20070213_UNIT_TRIGGER
const ACTION_SHOW_OR_HIDE_UNIT_INFO* pInfo = (const ACTION_SHOW_OR_HIDE_UNIT_INFO*)Get();
const INT is_hide = pInfo->GetIsHide();
const WzID unit_ID = pInfo->GetMonsterUnitID();
switch (is_hide)
{
case ACTION_SHOW_OR_HIDE_UNIT_INFO::SHOW:
getMGR()->CreateNPCForUnitID(unit_ID);
break;
case ACTION_SHOW_OR_HIDE_UNIT_INFO::HIDE:
getMGR()->DestroyNPCForUnitID(unit_ID);
break;
default:
SUNLOG(eCRITICAL_LOG, "ACTION_SHOW_OR_HIDE_UNIT:Invalid Value(%u)", pInfo->GetIsHide());
return;
}
}
//==================================================================================================
__IMPL_POOL(ACTION_SHOW_GUIDE_MSGBOX)
void
ACTION_SHOW_GUIDE_MSGBOX::OnAction()
{
#ifndef __WAVERIX_NEW_TRIGGER_WORKS_TEST__
SUNLOG(eCRITICAL_LOG, "ACTION_SHOW_GUIDE_MSGBOX::OnAction");
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_SHOW_OR_HIDE_MSGBOX)
void
ACTION_SHOW_OR_HIDE_MSGBOX::OnAction()
{
}
//==================================================================================================
// 몬스터 랜덤 스폰
__IMPL_POOL(ACTION_RANDOM_MONSTER_SPAWN)
BOOST_STATIC_ASSERT(ACTION_RANDOM_MONSTER_SPAWN::MAX_RANDOM_STEP_NUM ==
DropRatioInfoList::MAX_RANDOM_STEP_NUM);
void
ACTION_RANDOM_MONSTER_SPAWN::OnAction()
{
m_nCurRegenCount = 0;
m_RegenTimer.SetTimer(0); //< 처음에는 반드시 한번 실행한다.
m_MonsterRandomizer.Clear();
const ACTION_RANDOM_MONSTER_SPAWN_INFO* pInfo = Get();
BOOL rt = FALSE;
// 몬스터 1부터 5까지 대입한다.
rt = m_MonsterRandomizer.AddRatio(pInfo->GetMonsterID_1(), pInfo->GetMonsterRate_1(),
MAX_MONSTER_RATIO);
ASSERT(rt);
rt = m_MonsterRandomizer.AddRatio(pInfo->GetMonsterID_2(), pInfo->GetMonsterRate_2(),
MAX_MONSTER_RATIO);
ASSERT(rt);
rt = m_MonsterRandomizer.AddRatio(pInfo->GetMonsterID_3(), pInfo->GetMonsterRate_3(),
MAX_MONSTER_RATIO);
ASSERT(rt);
rt = m_MonsterRandomizer.AddRatio(pInfo->GetMonsterID_4(), pInfo->GetMonsterRate_4(),
MAX_MONSTER_RATIO);
ASSERT(rt);
rt = m_MonsterRandomizer.AddRatio(pInfo->GetMonsterID_5(), pInfo->GetMonsterRate_5(),
MAX_MONSTER_RATIO);
ASSERT(rt);
}
BOOL
ACTION_RANDOM_MONSTER_SPAWN::OnActionUpdate()
{
const ACTION_RANDOM_MONSTER_SPAWN_INFO* pInfo = Get();
// 현재 리젠된 횟수와 Max리젠 횟수가 같다면... 그만 한다.
if(m_nCurRegenCount == pInfo->GetMaxRegenCount())
{
return FALSE;
}
if(m_RegenTimer.IsExpired() == 0)
return TRUE;
//1. 리젠시간이 지났다면.. 리젠시간을 다시 적용하고..
m_RegenTimer.SetTimer(pInfo->GetRegenTime());
ns_trigger::CreateMonsterArgs args;
args.Clear();
args.Init(pInfo->GetAreaID(), 0);
WzVector vDirection = { pInfo->GetDirX(), pInfo->GetDirY(), pInfo->GetDirZ() };
short angle = 0;
{
float fangle = Math_GetAngleFromVector(&vDirection); // (NOTE) 안에서 Normalize수행.
angle = static_cast<short>(fangle * 180.0f / WZ_PI);
}
for(int loop = GetMonsterCount(); loop; --loop)
{
MONSTERCODE monsterCode = GetRandomMonster();
#ifdef _NA_003649_20111115_BATTLE_ZONE_HARDMODE
GameZone* game_zone = getMGR()->GetField()->GetGameZone();
if(game_zone->GetZoneType() == eZONETYPE_MISSION)
{
MONSTERCODE replaace_monster_code = \
AppliedNpcDependencyChain::Instance()->GetRepleaceMonsterCode(game_zone, monsterCode, true);
if (replaace_monster_code) {
monsterCode = replaace_monster_code;
}
}
#endif //_NA_003649_20111115_BATTLE_ZONE_HARDMODE
args.AddInfo(monsterCode, angle);
}
ns_trigger::CreateMonsterByTrigger(getMGR(), &args);
++m_nCurRegenCount;
return TRUE;
}
//리젠시킬 몬스터의 개수를 구한다.
int
ACTION_RANDOM_MONSTER_SPAWN::GetMonsterCount()
{
int nMonsterCount = 0;
int nRandomCnt = random(Get()->GetMinCreateMonsterCnt(), Get()->GetMaxCreateMonsterCnt());
if(Get()->GetIsParty() == ns_trigger::PERSONAL)
{
// 아직 유저가 맵에 들어 오지 않은상황에서 0이 될 수 있다.!!!!!@@@@
DWORD dwPlayerNum = getMGR()->GetPlayerNum();
nMonsterCount = nRandomCnt * (dwPlayerNum == 0 ? 1 : dwPlayerNum);
}
else
nMonsterCount = nRandomCnt;
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_RANDOM_MONSTER_SPAWN, 개수(%d)",
getParent()->TriggerID(), nMonsterCount);
#endif
return nMonsterCount;
}
int
ACTION_RANDOM_MONSTER_SPAWN::GetRandomMonster()
{
return m_MonsterRandomizer.RandomizedKey();
}
//==================================================================================================
class ShowTheFloor
{
private:
MSG_CG_TRIGGER_SHOW_THE_FLOOR_CMD& m_rMsgCMD;
WORD m_PartyKey;
public:
ShowTheFloor(MSG_CG_TRIGGER_SHOW_THE_FLOOR_CMD& rMsgCMD) : m_rMsgCMD(rMsgCMD), m_PartyKey(0) {}
void operator()(Player* pPlayer)
{
WORD partyKey = pPlayer->GetPartyState().GetPartyKey();
m_rMsgCMD.m_dwPlayerKey = pPlayer->GetObjectKey();
m_PartyKey = partyKey;
if(!NewGamePartyManager::Instance()->SendPacketAllMember(partyKey, &m_rMsgCMD, sizeof(m_rMsgCMD)))
{
pPlayer->SendPacket(&m_rMsgCMD, sizeof(m_rMsgCMD));
}
}
};
__IMPL_POOL(ACTION_TOWER_OF_TRIAL_SHOW_THE_FLOOR)
void
ACTION_TOWER_OF_TRIAL_SHOW_THE_FLOOR::OnAction()
{
const ACTION_TOWER_OF_TRIAL_SHOW_THE_FLOOR_INFO* pInfo = Get();
MSG_CG_TRIGGER_SHOW_THE_FLOOR_CMD msgCMD;
msgCMD.m_byFloor = pInfo->GetFloor();
DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
ShowTheFloor fnShowTheFloor(msgCMD);
if(0 != clicked_player_key)
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer)
fnShowTheFloor(pPlayer);
}
else
{
getParent()->ForeachSatisfiedPlayer(fnShowTheFloor);
}
}
//#endif //
//==================================================================================================
class IActionTriggerEx;
class WarpPlayerForRandomFieldArea
{
public:
enum eRANDOM_FIELDnAREA { RANDOM_FIELDnAREA_MAP = 0, RANDOM_FIELDnAREA_AREA = 1 };
private:
eRANDOM_FIELDnAREA m_Type;
IActionTrigger* m_pIActionObj;
DWORD m_FixedValue;
public:
WarpPlayerForRandomFieldArea(eRANDOM_FIELDnAREA eType, DWORD dwFixedVal,
IActionTrigger* pIActionObj)
: m_Type(eType), m_pIActionObj(pIActionObj), m_FixedValue(dwFixedVal)
{}
~WarpPlayerForRandomFieldArea() {}
void operator()(Player* pPlayer)
{
GetRandomFieldArea_N_Warp(pPlayer);
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
BOOL GetRandomFieldArea_N_Warp(Player* pPlayer)
{
GameZone* pZone = pPlayer->GetGameZonePtr();
GameField* pField = pPlayer->GetField();
if(pZone == 0 || pField == 0)
return FALSE;
switch(m_Type)
{
case RANDOM_FIELDnAREA_MAP:
{
ACTION_RANDOM_MAP_MOVE* pAction = (ACTION_RANDOM_MAP_MOVE*)m_pIActionObj;
ACTION_RANDOM_MAP_MOVE_INFO* pInfo = pAction->Get();
WORD wSelectIdx = (WORD)pAction->PrepareNextCode();
FIELDCODE fcCODE = (FIELDCODE)pInfo->GetFieldID(wSelectIdx);
WARP_PLAYER::Warp(pPlayer, (MAPCODE)fcCODE, (WzID)m_FixedValue);
}
break;
case RANDOM_FIELDnAREA_AREA:
{
ACTION_RANDOM_AREA_MOVE* pAction = (ACTION_RANDOM_AREA_MOVE*)m_pIActionObj;
int iAreaIDX;
if(m_FixedValue)
iAreaIDX = (int)m_FixedValue;
else
{
ACTION_RANDOM_AREA_MOVE_INFO* pInfo = pAction->Get();
WORD wSelectIdx = (WORD)pAction->PrepareNextCode();
iAreaIDX = (int)pInfo->GetAreaIDX(wSelectIdx);
}
//FIELDCODE fcCODE = pField->GetFieldCode();
//WARP_PLAYER::Warp(pPlayer, (MAPCODE)fcCODE, wzAreaID);
//pPlayer->Portal(ePORTAL_TYPE_GM, fcCODE);
//const CHAR* RNDAREA = "STR0";
//wzAreaID = StrToWzID(RNDAREA);
CWorldBase* pWorldBase = pField->GetFieldInfo()->GetWorldBase();
const int number_of_special_areas = pWorldBase->GetNumberOfSpecialArea();
WzSpecialArea* pSpecialArea = NULL;
if (!FlowControl::FCAssert(iAreaIDX < number_of_special_areas &&
(pSpecialArea = pWorldBase->GetSpecialAreaByIndex(iAreaIDX)) != NULL))
{
SUNLOG(eCRITICAL_LOG,
_T("|["__FUNCTION__"]|斤口=can't find special area (index = %u)"),
iAreaIDX);
return false;
}
WzBoundingVolume& rVolume = pSpecialArea->m_wbvRegion;
if(BVT_PATH_TILE_INDEX == rVolume.m_eBvType)
{
WzVector wvTarget = {0,0,0};
BvPathTileIndex& rPATHTILE = rVolume.m_BvPathTileIndex;
DWORD dwSelectedTile = random(0, rPATHTILE.m_nCount - 1);
nsAI::MovingPlanner* const pMovingPlanner = pField->GetMovingPlanner();
wvTarget = pMovingPlanner->GetRandomPosInTile(rPATHTILE.m_pdwData[dwSelectedTile]);
pField->TeleportObject(pPlayer, &wvTarget);
}
}
break;
}
return TRUE;
}
};
//==================================================================================================
__IMPL_POOL(ACTION_RANDOM_MAP_MOVE)
void
ACTION_RANDOM_MAP_MOVE::OnInit()
{
//if(!m_INIT.IsInited())
//{
ACTION_RANDOM_MAP_MOVE_INFO* pInfo = Get();
m_List.clear();
int* pFIELDs = pInfo->GetFieldIDPtr();
for(DWORD i=0 ; i<ACTION_RANDOM_MAP_MOVE_INFO::_MAX_FIELDCODE_ID ; ++i, ++pFIELDs)
{
int iFieldCode = *pFIELDs;
if(ULONG_MAX != (DWORD)iFieldCode)
m_List.push_back((BYTE)i);
}
ENUM_T bIsRandom = (ENUM_T)pInfo->GetRandom();
// 처음 상태일 때, 순차와 동일해주므로 한번 주사위 굴려준다.
if(ACTION_RANDOM_MAP_MOVE_INFO::RNDMODE_RANDOM == bIsRandom)
PrepareNextCode();
// m_INIT.Inited();
//}
}
DWORD
ACTION_RANDOM_MAP_MOVE::PrepareNextCode()
{
ACTION_RANDOM_MAP_MOVE_INFO* pInfo = Get();
ENUM_T bIsRandom = (ENUM_T)pInfo->GetRandom();
DWORD dwRET = *(m_List.begin());
if(ACTION_RANDOM_MAP_MOVE_INFO::RNDMODE_RANDOM == bIsRandom)
random_shuffle(m_List.begin(), m_List.end());
else
rotate(m_List.begin(), m_List.begin()+1, m_List.end());
return dwRET;
}
void
ACTION_RANDOM_MAP_MOVE::OnAction()
{
ACTION_RANDOM_MAP_MOVE_INFO* pInfo = Get();
// 해당 영역을 얻어와서
WzID areaID_for_Moving = pInfo->GetAreaID(); //RZ~~
ENUM_T bIsRandom = (ENUM_T)pInfo->GetRandom();
ENUM_T bIsParty = (ENUM_T)pInfo->GetTarget();
ENUM_T retMapType = (ENUM_T)pInfo->GetType();
//BOOL bIsLoop = getParent()->IsLoop() ? TRUE : FALSE;
if(ACTION_RANDOM_MAP_MOVE_INFO::TARGET_PARTY == bIsParty)
{
// 선택된 맵코드
WORD wSelectedIdx = (WORD)*m_List.begin();
FIELDCODE fcCODE = (FIELDCODE)pInfo->GetFieldID(wSelectedIdx);
switch(retMapType)
{
case ACTION_RANDOM_MAP_MOVE_INFO::RETMAP_VILLAGE:
case ACTION_RANDOM_MAP_MOVE_INFO::RETMAP_STARTINGVILLAGE:
{
// 뭐냐 이건... 기존 형식 호환?
ASSERT(!"Not Supported RANDOM_MAP_MOVE");
}
break;
case ACTION_RANDOM_MAP_MOVE_INFO::RETMAP_BATTLEMAP:
{
const WzID areaID = Get()->GetAreaID();
WarpControl::WarpAllPlayer(getMGR()->GetField(), fcCODE, areaID_for_Moving);
}
break;
};
}
else
{
switch(retMapType)
{
case ACTION_RANDOM_MAP_MOVE_INFO::RETMAP_VILLAGE:
case ACTION_RANDOM_MAP_MOVE_INFO::RETMAP_STARTINGVILLAGE:
{
// 뭐냐 이건... 기존 형식 호환?
ASSERT(!"Not Supported RANDOM_MAP_MOVE");
}
break;
case ACTION_RANDOM_MAP_MOVE_INFO::RETMAP_BATTLEMAP:
{
WarpPlayerForRandomFieldArea oper(WarpPlayerForRandomFieldArea::RANDOM_FIELDnAREA_MAP,
areaID_for_Moving, this);
DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
if(clicked_player_key != 0)
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if(pPlayer)
oper(pPlayer);
else
{
SUNLOG(eFULL_LOG, "[ACTION_RANDOM_MAP_MOVE::OnAction] [U:%d] Player NULL!!",
getParent()->GetClickedPlayerKey());
return;
}
}
else
{
getParent()->ForeachSatisfiedPlayer(oper);
getParent()->RemoveAllSatisfiedPlayer();
}
}
break;
}
}
}
//==================================================================================================
__IMPL_POOL(ACTION_RANDOM_AREA_MOVE)
void
ACTION_RANDOM_AREA_MOVE::OnInit()
{
//if(!m_INIT.IsInited())
//{
ACTION_RANDOM_AREA_MOVE_INFO* pInfo = Get();
m_List.clear();
int* pAREAs = pInfo->GetAreaIDXPtr();
for(DWORD i=0 ; i<ACTION_RANDOM_AREA_MOVE_INFO::_MAX_AREA_ID ; ++i, ++pAREAs)
{
int wiArea = *pAREAs;
if(ULONG_MAX != (DWORD)wiArea)
m_List.push_back((BYTE)i);
}
// 기본 랜덤
PrepareNextCode();
// m_INIT.Inited();
//}
}
DWORD
ACTION_RANDOM_AREA_MOVE::PrepareNextCode()
{
ACTION_RANDOM_AREA_MOVE_INFO* pInfo = Get();
//ENUM_T procType = (ENUM_T)pInfo->GetProcType();
random_shuffle(m_List.begin(), m_List.end());
return *(m_List.begin());;
}
void
ACTION_RANDOM_AREA_MOVE::OnAction()
{
ACTION_RANDOM_AREA_MOVE_INFO* pInfo = Get();
ENUM_T bIsParty = (ENUM_T)pInfo->GetTarget();
ENUM_T procType = (ENUM_T)pInfo->GetProcType();
//BOOL bIsLoop = getParent()->IsLoop() ? TRUE : FALSE;
// 선택된 맵코드
WORD wSelectedIdx = (WORD)*m_List.begin();
int iAreaID = (int)pInfo->GetAreaIDX(wSelectedIdx);
if(ACTION_RANDOM_AREA_MOVE_INFO::PROC_EACH == procType)
PrepareNextCode();
if(ACTION_RANDOM_AREA_MOVE_INFO::TARGET_PARTY == bIsParty)
{
ASSERT(!"Not Supported RANDOM_AREA_MOVE");
}
else
{
DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
if(clicked_player_key != 0)
{
Player* pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
WarpPlayerForRandomFieldArea oper(WarpPlayerForRandomFieldArea::RANDOM_FIELDnAREA_AREA, (DWORD)iAreaID, this);
if(pPlayer)
oper(pPlayer);
else
{
SUNLOG(eFULL_LOG, "[ACTION_RANDOM_MAP_MOVE::OnAction] [U:%d] Player NULL!!", getParent()->GetClickedPlayerKey());
return;
}
}
else
{
if(ACTION_RANDOM_AREA_MOVE_INFO::PROC_EACH == procType)
iAreaID = 0;
WarpPlayerForRandomFieldArea oper(WarpPlayerForRandomFieldArea::RANDOM_FIELDnAREA_AREA, (DWORD)iAreaID, this);
getParent()->ForeachSatisfiedPlayer(oper);
getParent()->RemoveAllSatisfiedPlayer();
}
}
}
//==================================================================================================
__IMPL_POOL(ACTION_REMOVE_MONSTER)
void
ACTION_REMOVE_MONSTER::OnAction()
{
const ACTION_REMOVE_MONSTER_INFO* pActionInfo = (const ACTION_REMOVE_MONSTER_INFO*)Get();
if(!pActionInfo) return;
Player* pPlayer = NULL;
//1. 먼저 현재 클릭한 유저가 있는지 체크
DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
if(clicked_player_key)
{
pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
}
else
{
//2. 클릭한 유저가 없으면 현재 Area에 들어온 유저가 있는지 체크.
pPlayer = getParent()->GetFirstSatisfiedPlayer();
}
getMGR()->KillMonster(pActionInfo->GetMonsterCode(), pPlayer, pActionInfo->GetIsExpItemDrop());
}
__IMPL_POOL(ACTION_ADD_EXP)
class ApplyAddExpHandler
{
public:
ApplyAddExpHandler(EXPTYPE exp) : add_exp_(exp) {}
~ApplyAddExpHandler() {}
void operator()(Player* player)
{
//플레이어 상태검증
if (player == NULL) {
return;
}
if (player->IsDead()) {
return;
}
player->AddExp(add_exp_, 0, 0, TRUE);
}
private:
EXPTYPE add_exp_;
};
void
ACTION_ADD_EXP::OnAction()
{
const ACTION_ADD_EXP_INFO* pActionInfo = Get();
if(!pActionInfo) return;
//이 트리거는 원래 클릭한 유저만 하기로 했지만, 보너스로 지역에 들어온 유저도 해주자. 단 지역에 들어온 유저는 개인만 된다.
Player* pPlayer = NULL;
if(pActionInfo->GetIsParty())
{
// 파티면... 현재 맵의 모든 유저에게 경험치를 지급한다.
getMGR()->AddExpAllMember(pActionInfo->GetExp());
}
else
{
//개인이면..
ApplyAddExpHandler op(pActionInfo->GetExp());
getParent()->ForeachSatisfiedPlayer(op);
}
}
//==================================================================================================
class CalcAttrOpr
{
public:
CalcAttrOpr(const ACTION_MONSTER_STAT_UP_OR_DOWN_INFO* pInfo, GameField* pGameField) : m_pGameField(pGameField)
{
m_MonsterCode = pInfo->GetMonsterCode();
m_AbilityInfo.m_wAbilityID = pInfo->GetAbilityID();
m_AbilityInfo.m_iOption1 = eATTACK_TYPE_PHYSICAL_OPTION; // NOTE: f110719.4L, ...
m_AbilityInfo.m_iParam[0] = pInfo->GetAbilityParameter1();
m_AbilityInfo.m_iParam[1] = pInfo->GetAbilityParameter2();
m_OperationType = pInfo->GetOperationType();
// NOTE: f110719.4L
if (m_OperationType != ACTION_MONSTER_STAT_UP_OR_DOWN_INFO::eOPERATION_PLUS &&
m_OperationType != ACTION_MONSTER_STAT_UP_OR_DOWN_INFO::eOPERATION_MINUS)
{
SUNLOG(eCRITICAL_LOG,
_T("|[%s]|斤口=unexpected monster stat control operation type = %d ")
_T("of Npc(%d) of GameField(%d)|"),
__FUNCTION__, m_OperationType, m_MonsterCode, pGameField->GetFieldCode());
};
_CalcAttr();
}
~CalcAttrOpr() {}
void _CalcAttr()
{
typedef GameField::_NPC_HASH _NPC_HASH;
const _NPC_HASH& rMobs = m_pGameField->GetRefNPCList();
FOREACH_CONTAINER(const _NPC_HASH::value_type& rMob, rMobs, _NPC_HASH)
{
NPC* const npc = rMob.second;
//#ifdef _NA_004126_20120111_CHANGE_GAME_SERVER_OBJECT_FACTORY
if (npc != NULL && 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
_SetNPC(rMob.second);
}
}
void _SetNPC(NPC* const npc)
{
if (npc->GetBaseInfo()->m_MonsterCode != m_MonsterCode) {
return;
};
eATTR_TYPE attr_type = m_AbilityInfo.GetAttrType();
if (attr_type <= eATTR_TYPE_INVALID || attr_type >= eATTR_MAX) {
return;
};
SkillAttrCalculator skill_calculator(npc->GetAttr());
if (m_OperationType == ACTION_MONSTER_STAT_UP_OR_DOWN_INFO::eOPERATION_PLUS) {
skill_calculator.AddAttr(attr_type, \
m_AbilityInfo.m_iParam[0], m_AbilityInfo.m_iParam[1]);
}
else if(m_OperationType == ACTION_MONSTER_STAT_UP_OR_DOWN_INFO::eOPERATION_MINUS) {
skill_calculator.DelAttr(attr_type, \
m_AbilityInfo.m_iParam[0], m_AbilityInfo.m_iParam[1]);
}
}
private:
GameField* m_pGameField;
int m_MonsterCode;
BASE_ABILITYINFO m_AbilityInfo;
BYTE m_OperationType;
};
//==================================================================================================
//
__IMPL_POOL(ACTION_MONSTER_STAT_UP_OR_DOWN)
void
ACTION_MONSTER_STAT_UP_OR_DOWN::OnAction()
{
const ACTION_MONSTER_STAT_UP_OR_DOWN_INFO* pInfo = (const ACTION_MONSTER_STAT_UP_OR_DOWN_INFO*)Get();
CalcAttrOpr(pInfo, getMGR()->GetField());
#ifdef __TRIGGER_TEST_LOG
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_MONSTER_STAT_UP_OR_DOWN", getParent()->TriggerID());
#endif
}
//==================================================================================================
__IMPL_POOL(ACTION_RANDUM_TRIGER_ON_OR_OFF)
void
ACTION_RANDUM_TRIGER_ON_OR_OFF::OnAction()
{
ACTION_RANDUM_TRIGER_ON_OR_OFF_INFO* pInfo = (ACTION_RANDUM_TRIGER_ON_OR_OFF_INFO*)Get();
// 트리거ID는 최대한 8개를 넣을수 있도록 한다.
// 랜덤으로 돌리려는 트리거가 3개라면 3개만 입력하고, 나머지 5개를 공란으로 넣어도 작용한다.
int i = 0;
for(; ACTION_RANDUM_TRIGER_ON_OR_OFF_INFO::_MAX_TRIGGER_ID > i; ++i)
{
if(pInfo->GetWzTriggerID(i) == 0)
break;
}
// 1개 짜리는 따로 있으므로 2이상이 들어온다.
if(i > 1)
{
BYTE idx = random(1,i) - 1; // 1~8 => 0~7 로.
DWORD WzTriggerID = pInfo->GetWzTriggerID(idx);
if(ACTION_RANDUM_TRIGER_ON_OR_OFF_INFO::_TRIGGER_STATE_ON == pInfo->GetTriggerState())
getMGR()->InsertActiveTrigger(WzTriggerID);
else
getMGR()->RequestRemoveActiveTrigger(WzTriggerID);
}
}
//==================================================================================================
template<typename T> class NotifyMessageOpr
{
public:
NotifyMessageOpr(T& msg) : msg_(msg){}
~NotifyMessageOpr(){}
void operator()(Player* pPlayer)
{
pPlayer->SendPacket(&msg_, sizeof(msg_));
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
private:
T& msg_;
//
void operator=(const NotifyMessageOpr<T>&);
};
template<typename T> NotifyMessageOpr<T> NotifyMessage(T& msg)
{
return NotifyMessageOpr<T>(msg);
}
//
//{__NA001140_20080128_SOUND_TRIGGER_ADD
__IMPL_POOL(ACTION_PLAY_MUSIC)
void
ACTION_PLAY_MUSIC::OnAction()
{
const ACTION_PLAY_MUSIC_INFO* pInfo = Get();
// _NA001385_20090924_DOMINATION_TRIGGER [개인,파티 -> 개인,맵전체로 변경]
const BYTE byTarget = pInfo->GetTarget();
const BYTE byField = ACTION_PLAY_MUSIC_INFO::TARGET_FIELD;
const BYTE byPersonal = ACTION_PLAY_MUSIC_INFO::TARGET_PERSONAL;
MSG_CG_TRIGGER_PLAY_MUSIC_CMD msg;
msg.m_byTarget = byTarget;
msg.m_nMusCode = pInfo->GetMusCode();
switch(byTarget)
{
case byPersonal:
{
Player* pPlayer = NULL;
Trigger* pTrigger = getParent();
DWORD clickPlayer = pTrigger->GetClickedPlayerKey();
if (clickPlayer)
pPlayer = PlayerManager::Instance()->FindPlayerByObjKey(clickPlayer);
else
pPlayer = pTrigger->GetFirstSatisfiedPlayer();
if (pPlayer)
pPlayer->SendPacket(&msg, sizeof(msg));
else
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_PLAY_MUSIC : Cant not Find Player",
pTrigger->TriggerID());
}
break;
case byField:
{
getMGR()->ForeachPlayer(NotifyMessage(msg));
}
break;
}
}
//}__NA001140_20080128_SOUND_TRIGGER_ADD
__IMPL_POOL(ACTION_DOWN_OBJETC_HP)
void
ACTION_DOWN_OBJETC_HP::OnAction()
{
const ACTION_DOWN_OBJETC_HP_INFO* pInfo = Get();
const DWORD dwObjKey = pInfo->GetdwObjKey();
MapNpc* pMapNpc = getMGR()->FindMapNpc(dwObjKey);
if (NULL == pMapNpc)
return;
const BYTE btHPPercentage = pInfo->GetbtHPPercentage();
const DWORD dwMaxHP = pMapNpc->GetMaxHP();
const DWORD dwDecrement = (DWORD)(dwMaxHP * (btHPPercentage/100.0f));
pMapNpc->DecreaseHP(dwDecrement);
pMapNpc->SendHPChange();
}
__IMPL_POOL(ACTION_CHANGE_ATTACK_KIND)
void
ACTION_CHANGE_ATTACK_KIND::OnAction()
{
const ACTION_CHANGE_ATTACK_KIND_INFO* pInfo = Get();
const DWORD dwObjKey = pInfo->GetdwObjKey();
MapNpc* pMapNpc = getMGR()->FindMapNpc(dwObjKey);
if (NULL == pMapNpc)
return;
const BYTE attackImpossibility = ACTION_CHANGE_ATTACK_KIND_INFO::ATTACK_IMPOSSIBILITY;
const BYTE attackPossibility = ACTION_CHANGE_ATTACK_KIND_INFO::ATTACK_POSSIBILITY;
const BYTE btPossibility = pInfo->GetbtPossibility();
StatusManager* pStatusManager = pMapNpc->GetStatusManager();
if (attackImpossibility == btPossibility) {
//pStatusManager->AllocStatus(eCHAR_STATE_STEEL/*eCHAR_STATE_ETC_DISABLE_VISION_TRIGGER*/); <- X
pStatusManager->AllocStatus(eCHAR_STATE_STEEL, 15 * 1000/*eCHAR_STATE_ETC_DISABLE_VISION_TRIGGER*/); //<- better...
#ifdef _CN_000000_20130731_STATE_STEEL_MOD_BUG_LOG
SUNLOG(eCRITICAL_LOG,"["__FUNCTION__"]:AllocStatus(eCHAR_STATE_STEEL, BASE_EXPIRE_TIME_INFINITY)");
#endif // #ifdef _CN_000000_20130731_STATE_STEEL_MOD_BUG_LOG
}
else if(attackPossibility == btPossibility) {
pStatusManager->Remove(eCHAR_STATE_STEEL);
}
}
class NotifyStampOpr
{
public:
NotifyStampOpr(){}
~NotifyStampOpr(){}
void operator()(Player* pPlayer)
{
GUILDGUID guildGuid = pPlayer->GetGuildGuid();
GameZone* pZone = pPlayer->GetGameZonePtr();
if (!pZone ||
INVALID_GUILDGUID == guildGuid ||
eZONETYPE_DOMINATION_FIELD != pZone->GetZoneType())
return;
GameDominationField* pDomiZone = (GameDominationField*)pZone;
if (guildGuid == pDomiZone->GetOffenseGuild())
pPlayer->SendAllFieldCollectionInfo();
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
};
__IMPL_POOL(ACTION_ACTIVE_STAMP)
void
ACTION_ACTIVE_STAMP::OnAction()
{
NotifyStampOpr op;
getMGR()->ForeachPlayer(op);
}
//==================================================================================================
__IMPL_POOL(ACTION_ATTACK_OBJECTKIND)
class AattackActionNPCCodeTypeOpr
{
public:
AattackActionNPCCodeTypeOpr(const MONSTERCODE monster_code,
const eACTION_ATTACK_TYPE attack_type)
: monster_code_(monster_code)
, attack_type_(attack_type) {}
~AattackActionNPCCodeTypeOpr(){}
void operator()(NPC* npc)
{
const BASE_NPCINFO* const base_info = npc->GetBaseInfo();
const MONSTERCODE monster_code = base_info->m_MonsterCode;
if (monster_code == monster_code_)
{
StatusManager* status_manager = npc->GetStatusManager();
ACTION_ATTACK_OBJECTKIND::StatusProc(*status_manager, attack_type_);
}
}
void operator()(std::pair<DWORD,NPC*> pair_type)
{
operator()(pair_type.second);
}
private:
const MONSTERCODE monster_code_;
const eACTION_ATTACK_TYPE attack_type_;
};
void
ACTION_ATTACK_OBJECTKIND::OnAction()
{
const ACTION_ATTACK_OBJECTKIND_INFO* info = Get();
const DWORD object_key = info->GetdwObjectKey();
const eACTION_ATTACK_TYPE attack_type = static_cast<eACTION_ATTACK_TYPE>(info->GetbyAttack());
const eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(info->GetbyObjectType());
ProcessFunction action;
action = GetProcessFunction(object_type);
(this->*action)(object_key, attack_type);
}
ACTION_ATTACK_OBJECTKIND::ProcessFunction
ACTION_ATTACK_OBJECTKIND::GetProcessFunction(const eOBJECTCODE_TYPE object_type)
{
switch(object_type)
{
case NPCCODE_TYPE:
return &ACTION_ATTACK_OBJECTKIND::ActionNPCCodeType;
case UNITID_TYPE:
return &ACTION_ATTACK_OBJECTKIND::ActionUnitIDType;
case MAPNPC_TYPE:
return &ACTION_ATTACK_OBJECTKIND::ActionMapNpcType;
}
return &ACTION_ATTACK_OBJECTKIND::ActionDefaultType;
}
VOID
ACTION_ATTACK_OBJECTKIND::ActionDefaultType(const DWORD object_key,
const eACTION_ATTACK_TYPE attack_type)
{
__UNUSED((object_key, attack_type));
SUNLOG(eDEV_LOG, "[ACTION_ATTACK_OBJECTKIND]:TYPE_ERROR");
}
VOID
ACTION_ATTACK_OBJECTKIND::ActionNPCCodeType(const DWORD object_key,
const eACTION_ATTACK_TYPE attack_type)
{
const MONSTERCODE npc_code = static_cast<MONSTERCODE>(object_key);
AattackActionNPCCodeTypeOpr op(npc_code,attack_type);
getMGR()->ForeachNPC(op);
}
VOID
ACTION_ATTACK_OBJECTKIND::ActionUnitIDType(const DWORD object_key,
const eACTION_ATTACK_TYPE attack_type)
{
NPC* const npc = getMGR()->FindNPCForUnitID(object_key);
if (npc == NULL)
return;
StatusManager* const status_manager = npc->GetStatusManager();
StatusProc(*status_manager, attack_type);
}
VOID
ACTION_ATTACK_OBJECTKIND::ActionMapNpcType(const DWORD object_key,
const eACTION_ATTACK_TYPE attack_type)
{
MapNpc* const map_npc = getMGR()->FindMapNpc(object_key);
if (map_npc == NULL)
return;
StatusManager* const status_manager = map_npc->GetStatusManager();
StatusProc(*status_manager, attack_type);
}
//[몬스터/유닛/오브젝트]를 공격 할 수 [있게/없게] 만든다
// 특정 몬스터 객체가 플레이어에게 공격 당하지 않게 만든다.
VOID
ACTION_ATTACK_OBJECTKIND::StatusProc(StatusManager& status_manager,
const eACTION_ATTACK_TYPE attack_type)
{
switch(attack_type)
{
case ATTACK_IMPOSSIBILITY:
status_manager.AllocStatus(eCHAR_STATE_ETC_DISABLE_VISION_TRIGGER);
break;
case ATTACK_POSSIBILITY:
status_manager.Remove(eCHAR_STATE_ETC_DISABLE_VISION_TRIGGER);
break;
default:
break;
}
}
//==================================================================================================
__IMPL_POOL(ACTION_ACTIVE_SKILL_OBJECTKIND)
class ActiveSkillActionNPCCodeTypeOpr
{
public:
ActiveSkillActionNPCCodeTypeOpr(const MONSTERCODE monster_code, const SLOTCODE skill_code)
: monster_code_(monster_code),
skill_code_(skill_code)
{}
~ActiveSkillActionNPCCodeTypeOpr(){}
void operator()(NPC* npc)
{
const BASE_NPCINFO* const base_info = npc->GetBaseInfo();
const MONSTERCODE monster_code = base_info->m_MonsterCode;
if (monster_code == monster_code_)
{
npc->UseSkill(skill_code_);
}
}
void operator()(std::pair<DWORD,NPC*> pair_type)
{
operator()(pair_type.second);
}
private:
const MONSTERCODE monster_code_;
const SLOTCODE skill_code_;
};
void
ACTION_ACTIVE_SKILL_OBJECTKIND::OnAction()
{
const ACTION_ACTIVE_SKILL_OBJECTKIND_INFO* info = Get();
const DWORD object_key = info->GetdwObjectKey();
const eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(info->GetbyObjectType());
const SLOTCODE skill_code = static_cast<SLOTCODE>(info->GetdwSkillCode());
NPC* npc = NULL;
switch(object_type)
{
case UNITID_TYPE:
npc = getMGR()->FindNPCForUnitID(object_key);
if (npc != NULL)
npc->UseSkill(skill_code);
break;
case MAPNPC_TYPE:
npc = getMGR()->FindMapNpc(object_key);
if (npc != NULL)
npc->UseSkill(skill_code);
break;
case NPCCODE_TYPE:
{
const MONSTERCODE npc_code = static_cast<MONSTERCODE>(object_key);
ActiveSkillActionNPCCodeTypeOpr op(npc_code, skill_code);
getMGR()->ForeachNPC(op);
}
break;
default:
SUNLOG(eDEV_LOG, "[ACTION_ACTIVE_SKILL_OBJECTKIND]:TYPE_ERROR");
return;
}
}
//==================================================================================================
__IMPL_POOL(ACTION_SHOW_MESSAGEBOX_OBJECTKIND)
class ShowMessageBoxActionNPCCodeTypeOpr
{
public:
ShowMessageBoxActionNPCCodeTypeOpr(const MONSTERCODE monster_code, const DWORD message_code)
: monster_code_(monster_code)
{
msg_cmd_.m_dwMessageCode = message_code;
}
~ShowMessageBoxActionNPCCodeTypeOpr(){}
void operator()(NPC* npc)
{
const BASE_NPCINFO* const base_info = npc->GetBaseInfo();
const MONSTERCODE monster_code = base_info->m_MonsterCode;
if (monster_code == monster_code_)
{
msg_cmd_.m_dwObjectKey = npc->GetObjectKey();
npc->SendPacketAround(&msg_cmd_, sizeof(msg_cmd_));
}
}
void operator()(std::pair<DWORD,NPC*> pair_type)
{
operator()(pair_type.second);
}
private:
const MONSTERCODE monster_code_;
MSG_CG_TRIGGER_DISPLAY_BALLOON_MSGBOX_CMD msg_cmd_;
};
void
ACTION_SHOW_MESSAGEBOX_OBJECTKIND::OnAction()
{
const ACTION_SHOW_MESSAGEBOX_OBJECTKIND_INFO* info = Get();
const DWORD object_key = info->GetdwObjectKey();
const eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(info->GetbyObjectType());
const DWORD message_code = info->GetdwMessageCode();
MSG_CG_TRIGGER_DISPLAY_BALLOON_MSGBOX_CMD msg_cmd;
msg_cmd.m_dwMessageCode = message_code;
NPC* npc = NULL;
switch(object_type)
{
case UNITID_TYPE:
npc = getMGR()->FindNPCForUnitID(object_key);
if (npc != NULL)
{
msg_cmd.m_dwObjectKey = npc->GetObjectKey();
npc->SendPacketAround(&msg_cmd, sizeof(msg_cmd));
}
break;
case MAPNPC_TYPE:
npc = getMGR()->FindMapNpc(object_key);
if (npc != NULL)
{
msg_cmd.m_dwObjectKey = npc->GetObjectKey();
npc->SendPacketAround(&msg_cmd, sizeof(msg_cmd));
}
break;
case NPCCODE_TYPE:
{
const MONSTERCODE npc_code = static_cast<MONSTERCODE>(object_key);
ShowMessageBoxActionNPCCodeTypeOpr op(npc_code, message_code);
getMGR()->ForeachNPC(op);
}
break;
default:
SUNLOG(eDEV_LOG, "[ACTION_SHOW_MESSAGEBOX_OBJECTKIND]:TYPE_ERROR");
return;
}
}
//==================================================================================================
__IMPL_POOL(ACTION_SET_ATTACK_STATE_OBJECTKIND)
class SetAttackStateActionNPCCodeTypeOpr
{
public:
SetAttackStateActionNPCCodeTypeOpr(const MONSTERCODE monster_code,
const eACTION_ATTACK_TYPE attack_type,
const eTARGET_SEARCH_TYPE search_type,
const DWORD dest_object_key)
: monster_code_(monster_code),
attack_type_(attack_type),
search_type_(search_type),
dest_object_key_(dest_object_key){}
~SetAttackStateActionNPCCodeTypeOpr(){}
void operator()(NPC* npc)
{
const BASE_NPCINFO* const base_info = npc->GetBaseInfo();
const MONSTERCODE monster_code = base_info->m_MonsterCode;
if (monster_code == monster_code_)
{
ACTION_SET_ATTACK_STATE_OBJECTKIND::StatusProc(npc, attack_type_,
search_type_, dest_object_key_);
}
}
void operator()(std::pair<DWORD,NPC*> pair_type)
{
operator()(pair_type.second);
}
private:
const MONSTERCODE monster_code_;
const eACTION_ATTACK_TYPE attack_type_;
const eTARGET_SEARCH_TYPE search_type_;
const DWORD dest_object_key_;
};
VOID
ACTION_SET_ATTACK_STATE_OBJECTKIND::StatusProc(NPC* src_npc,
const eACTION_ATTACK_TYPE attack_type,
const eTARGET_SEARCH_TYPE search_type,
const DWORD dest_object_key)
{
StatusManager* const status_manager = src_npc->GetStatusManager();
switch(attack_type)
{
case ATTACK_IMPOSSIBILITY:
{
const BASE_NPCINFO* const info = src_npc->GetBaseInfo();
const BYTE attitude = info->m_byAttitude;
const eTARGET_SEARCH_TYPE search_type = src_npc->FindSearchTypeByAttitude(attitude);
src_npc->SetSearchType(search_type, 0);
status_manager->Remove(eCHAR_STATE_ETC_SKIP_UPDATE_TARGET_LIST);
src_npc->ChangeState(STATE_ID_WANDER);
}
break;
case ATTACK_POSSIBILITY:
{
src_npc->SetSearchType(search_type, dest_object_key);
status_manager->AllocStatus(eCHAR_STATE_ETC_SKIP_UPDATE_TARGET_LIST);
TargetManager* const target_manager = src_npc->GetTargetManager();
target_manager->ClearBattleRecord();
}
break;
default:
break;
}
}
void
ACTION_SET_ATTACK_STATE_OBJECTKIND::OnAction()
{
const ACTION_SET_ATTACK_STATE_OBJECTKIND_INFO* info = Get();
const DWORD object_key = info->GetdwObjectKey();
const DWORD dest_object_key = info->GetdwDestObjectKey();
eACTION_ATTACK_TYPE action_attack_type =
static_cast<eACTION_ATTACK_TYPE>(info->GetbyAttack());
const eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(info->GetbyObjectType());
const eOBJECTCODE_TYPE dest_object_type =
static_cast<eOBJECTCODE_TYPE>(info->GetbyDestObjectType());
//.//
eTARGET_SEARCH_TYPE search_type = eRARGET_NOT_SEARCH;
switch(dest_object_type)
{
case UNITID_TYPE:
search_type = eRARGET_SEARCH_UNITID;
break;
case MAPNPC_TYPE:
search_type = eRARGET_SEARCH_MAPOBJECTID;
break;
case NPCCODE_TYPE:
search_type = eRARGET_SEARCH_MONSTERCODE;
break;
case NON_TYPE: // _NA_0_20100817_HELLON_LAST_TRIGGER (공격이 멈춰진 상태)
search_type = eRARGET_SEARCH_NOT_SEARCH_AND_TARGET_CHNAGE;
action_attack_type = static_cast<eACTION_ATTACK_TYPE>(!info->GetbyAttack());
break;
}
//.//
NPC* src_npc = NULL;
switch(object_type)
{
case UNITID_TYPE:
src_npc = getMGR()->FindNPCForUnitID(object_key);
break;
case MAPNPC_TYPE:
src_npc = getMGR()->FindMapNpc(object_key);
break;
}
//.//
if (src_npc != NULL)
{
StatusProc(src_npc, action_attack_type, search_type, dest_object_key);
}
else
{
const MONSTERCODE npc_code = static_cast<MONSTERCODE>(object_key);
SetAttackStateActionNPCCodeTypeOpr op(npc_code, action_attack_type,
search_type, dest_object_key);
getMGR()->ForeachNPC(op);
}
}
//==================================================================================================
__IMPL_POOL(ACTION_DESTROY_OBJECTKIND)
void
ACTION_DESTROY_OBJECTKIND::OnAction()
{
const ACTION_DESTROY_OBJECTKIND_INFO* info = Get();
const DWORD object_key = info->GetdwObjectKey();
const BOOL is_reward =
info->GetbyReward() == ACTION_DESTROY_OBJECTKIND_INFO::REWARD ? TRUE : FALSE;
Player* player = NULL;
const DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
if(clicked_player_key)
player = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
else
player = getParent()->GetFirstSatisfiedPlayer();
NPC* npc = NULL;
const eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(info->GetbyObjectType());
switch(object_type)
{
case NPCCODE_TYPE:
getMGR()->KillMonster(static_cast<MONSTERCODE>(object_key), player, is_reward);
return;
case UNITID_TYPE:
npc = getMGR()->FindNPCForUnitID(object_key);
if (npc)
getMGR()->KillOneMonster(npc, player, is_reward);
break;
case MAPNPC_TYPE:
npc = getMGR()->FindMapNpc(object_key);
if (npc)
getMGR()->KillOneMonster(npc, player, is_reward);
break;
default:
SUNLOG(eDEV_LOG, "[ACTION_DESTROY_OBJECTKIND]:TYPE_ERROR");
return;
}
}
__IMPL_POOL(ACTION_CREATE_MONSTER_LINKED_MAPOBJECT)
void
ACTION_CREATE_MONSTER_LINKED_MAPOBJECT::OnAction()
{
const ACTION_CREATE_MONSTER_LINKED_MAPOBJECT_INFO* pInfo =
(const ACTION_CREATE_MONSTER_LINKED_MAPOBJECT_INFO*)Get();
const DWORD mapobjectID = pInfo->GetdwObjectKey();
MAPOBJECT_INFO* mapobject_info = getMGR()->FindMapObjectInfo(mapobjectID);
DWORD dwCode = mapobject_info->dwExtraInfos[eMAPOBJECT_MONSTER];
ASSERT(SAFE_NUMERIC_TYPECAST( DWORD, dwCode, WORD ));
WORD NPCCode = (WORD)dwCode;
getMGR()->CreateMapNPC( NPCCode, &mapobject_info->wvPos, mapobject_info );
}
//==================================================================================================
__IMPL_POOL(ACTION_CONTROL_TIMER)
void
ACTION_CONTROL_TIMER::OnAction()
{
const ACTION_CONTROL_TIMER_INFO* pInfo = (const ACTION_CONTROL_TIMER_INFO*)Get();
MSG_CG_TRIGGER_CONTROL_TIMER_CMD msg;
msg.m_dwTime = pInfo->GetdwTime();
msg.m_byControlType = pInfo->GetbyControlType();
getMGR()->ForeachPlayer(NotifyMessage(msg));
}
// implemented by _NA_0_20100705_DOMINATION_EVENT
__IMPL_POOL(ACTION_DO_COMMAND)
void
ACTION_DO_COMMAND::OnAction()
{
const ACTION_DO_COMMAND_INFO* pInfo = (const ACTION_DO_COMMAND_INFO*)Get();
switch(pInfo->GetbyCommandType())
{
case ACTION_DO_COMMAND_INFO::END_REGEN_EVENT: // 점령전 보상 이벤트 트리거의 종료 메시지(보상 종료 처리)
{
GameField* field = getMGR()->GetField();
GameZone* zone = field->GetGameZone();
if (zone->GetZoneType() != eZONETYPE_DOMINATION_FIELD)
{
return;
}
GameDominationField* domination = static_cast<GameDominationField*>(zone);
domination->EndEventTrigger();
//_NA_20100604_BETTER_DOMINATION_MANAGER
//점령전 보상 몬스터 사냥 카운트 횟수를 늘린다.
domination->UpdateGuildInfoByDomination(field->GetFieldCode());
}
break;
}
}
// ACTION_MOVE_CONTROL was implemented by _NA_0_20100817_HELLON_LAST_TRIGGER
__IMPL_POOL(ACTION_MOVE_CONTROL)
class MoveControlNPCCodeTypeOpr
{
public:
MoveControlNPCCodeTypeOpr(const MONSTERCODE monster_code, const eACTION_MOVE_TYPE move_type)
: monster_code_(monster_code)
, move_type_(move_type)
{}
~MoveControlNPCCodeTypeOpr(){}
void operator()(NPC* npc)
{
const BASE_NPCINFO* const base_info = npc->GetBaseInfo();
const MONSTERCODE monster_code = base_info->m_MonsterCode;
if (monster_code == monster_code_)
ACTION_MOVE_CONTROL::StatusProc(npc, move_type_);
}
void operator()(std::pair<DWORD,NPC*> pair_type)
{
operator()(pair_type.second);
}
private:
const MONSTERCODE monster_code_;
const eACTION_MOVE_TYPE move_type_;
};
VOID
ACTION_MOVE_CONTROL::StatusProc(NPC* npc, const eACTION_MOVE_TYPE move_type)
{
StatusManager* const status_manager = npc->GetStatusManager();
switch(move_type)
{
case MOVE_IMPOSSIBILITY:
status_manager->AllocStatus(eCHAR_STATE_ETC_TRIGGER_HOLDING);
npc->ChangeState(STATE_ID_STOP_IDLE);
break;
case MOVE_POSSIBILITY:
status_manager->Remove(eCHAR_STATE_ETC_TRIGGER_HOLDING);
npc->ChangeState(STATE_ID_WANDER);
break;
default:
break;
}
}
void
ACTION_MOVE_CONTROL::OnAction()
{
const ACTION_MOVE_CONTROL_INFO* pInfo = (const ACTION_MOVE_CONTROL_INFO*)Get();
const DWORD object_key = pInfo->GetdwObjectKey();
const eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(pInfo->GetbyObjectType());
const eACTION_MOVE_TYPE move_type = static_cast<eACTION_MOVE_TYPE>(pInfo->GetbyMove());
switch(object_type)
{
case UNITID_TYPE:
{
NPC* npc = getMGR()->FindNPCForUnitID(object_key);
if (npc)
StatusProc(npc, move_type);
}
break;
case NPCCODE_TYPE:
{
const MONSTERCODE npc_code = static_cast<MONSTERCODE>(object_key);
MoveControlNPCCodeTypeOpr op(npc_code, move_type);
getMGR()->ForeachNPC(op);
}
break;
default:
SUNLOG(eDEV_LOG, "[ACTION_MOVE_CONTROL]:TYPE_ERROR");
return;
}
}
__IMPL_POOL(ACTION_ANIMATION_CONTROL)
class AnimationControlNPCCodeTypeOpr
{
public:
AnimationControlNPCCodeTypeOpr(const MONSTERCODE monster_code, const DWORD animation_code)
: monster_code_(monster_code)
, animation_code_(animation_code)
{}
~AnimationControlNPCCodeTypeOpr(){}
void operator()(NPC* npc)
{
const BASE_NPCINFO* const base_info = npc->GetBaseInfo();
const MONSTERCODE monster_code = base_info->m_MonsterCode;
if (monster_code == monster_code_)
npc->SetTriggerAnimationCode(animation_code_);
}
void operator()(std::pair<DWORD,NPC*> pair_type)
{
operator()(pair_type.second);
}
private:
const MONSTERCODE monster_code_;
const DWORD animation_code_;
};
void
ACTION_ANIMATION_CONTROL::OnAction()
{
const ACTION_ANIMATION_CONTROL_INFO* pInfo = (const ACTION_ANIMATION_CONTROL_INFO*)Get();
DWORD animation_code = 0;
if (pInfo->GetbyLoop2() == ACTION_ANIMATION_CONTROL_INFO::LOOP)
{
animation_code = pInfo->GetdwAnimationCode2();
}
else if (pInfo->GetbyLoop1() == ACTION_ANIMATION_CONTROL_INFO::LOOP)
{
animation_code = pInfo->GetdwAnimationCode1();
}
const DWORD object_key = pInfo->GetdwObjectKey();
const eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(pInfo->GetbyObjectType());
switch(object_type)
{
case UNITID_TYPE:
{
NPC* npc = getMGR()->FindNPCForUnitID(object_key);
if (npc)
npc->SetTriggerAnimationCode(animation_code);
}
break;
case MAPNPC_TYPE:
{
NPC* npc = getMGR()->FindMapNpc(object_key);
if (npc)
npc->SetTriggerAnimationCode(animation_code);
}
break;
case NPCCODE_TYPE:
{
const MONSTERCODE npc_code = static_cast<MONSTERCODE>(object_key);
AnimationControlNPCCodeTypeOpr op(npc_code, animation_code);
getMGR()->ForeachNPC(op);
}
break;
default:
SUNLOG(eDEV_LOG, "[ACTION_ANIMATION_CONTROL]:TYPE_ERROR");
return;
}
}
// _NA_002098_20110307_ADD_NPC_CONTROL_TRIGGER
__IMPL_POOL(ACTION_NPC_APPLY_DAMAGE)
class NPCApplyDamageOper
{
public:
NPCApplyDamageOper(const MONSTERCODE monster_code, const INT damage) :
monster_code_(monster_code), damage_(damage)
{
}
~NPCApplyDamageOper() {}
void operator()(NPC* npc)
{
const BASE_NPCINFO* const base_info = npc->GetBaseInfo();
const MONSTERCODE monster_code = base_info->m_MonsterCode;
if (monster_code == monster_code_)
{
npc->DecreaseHP(damage_);
npc->SendAreaDamage(damage_);
}
}
void operator()(std::pair<DWORD, NPC*> pair_type)
{
operator()(pair_type.second);
}
private:
const MONSTERCODE monster_code_;
const INT damage_;
};
void ACTION_NPC_APPLY_DAMAGE::OnAction()
{
const ACTION_NPC_APPLY_DAMAGE_INFO* const apply_damage_info = Get();
const eOBJECTCODE_TYPE object_type =
static_cast<eOBJECTCODE_TYPE>(apply_damage_info->GetObjectType());
const DWORD object_key = apply_damage_info->GetObjectKey();
const INT damage = apply_damage_info->GetDamage();
switch(object_type)
{
case UNITID_TYPE:
{
NPC* const npc_unit = getMGR()->FindNPCForUnitID(object_key);
if (npc_unit != NULL)
{
npc_unit->DecreaseHP(damage);
npc_unit->SendAreaDamage(damage);
}
}
break;
case MAPNPC_TYPE:
{
NPC* const npc_map_obj = getMGR()->FindMapNpc(object_key);
if (npc_map_obj != NULL)
{
npc_map_obj->DecreaseHP(damage);
npc_map_obj->SendAreaDamage(damage);
}
}
break;
case NPCCODE_TYPE:
{
const MONSTERCODE monster_code = static_cast<MONSTERCODE>(object_key);
NPCApplyDamageOper oper(monster_code, damage);
getMGR()->ForeachNPC(oper);
}
break;
default:
SUNLOG(eDEV_LOG, "[ACTION_NPC_APPLY_DAMAGE]:TYPE_ERROR");
return;
}
}
//------------------------------------------------------------------------------
//_NA_004777_20120428_ADD_COMPLETE_COLLECTION_TRIGGER
//------------------------------------------------------------------------------
class CollectAllCollectiveCmd
{
public:
CollectAllCollectiveCmd(GameField* game_field, bool val, CODETYPE* object_key_ptr) :
game_field_(game_field),
is_active_(val),
object_key_ptr_(object_key_ptr)
{
}
void operator()(Player* player)
{
if (is_active_) {
player->SendAllFieldCollectionInfo();
}
else
{
CollectionManager* const collection_manager = game_field_->GetCollectManeger();
if (collection_manager == NULL)
{
return;
}
MSG_CG_COLLECT_DEACTIVE_BRD brd_msg;
brd_msg.m_byCount = 0;
for (int i = 0; i < MSG_CG_COLLECT_DEACTIVE_BRD::_MAX_FIELDCOLLECT_INFO_SIZE; ++i)
{
CODETYPE object_key = MAP_OBJECT_KEY + object_key_ptr_[i];
Collection* collection = static_cast<Collection*>(game_field_->FindObject(COLLECTION_OBJECT, object_key));
if (obj_util::IsValid(collection) == false)
{
continue;
}
if (collection->is_active() == true)
{
continue;
}
MAPOBJECT_INFO* pMapInfo = collection->GetMapObjectInfo();
RC::eCOLLECT_RESULT rc = collection_manager->ChkCondision(player, pMapInfo->CollectionID);
if(RC::RC_COLLECT_SUCCESS != rc)
continue;
brd_msg.m_ObjKeyCollection[brd_msg.m_byCount++] = collection->GetObjectKey();
}
if(brd_msg.m_byCount)
{
player->SendPacket(&brd_msg, (WORD)brd_msg.GetSize());
}
}
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
private:
GameField* game_field_;
bool is_active_;
CODETYPE* object_key_ptr_;
};
//------------------------------------------------------------------------------
__IMPL_POOL(ACTION_COMPLETE_COLLECTION)
//------------------------------------------------------------------------------
VOID ACTION_COMPLETE_COLLECTION::OnAction()
{
GameField* const field = getMGR()->GetField();
if (field == NULL)
{
return;
}
CollectionManager* const collection_manager = field->GetCollectManeger();
if (collection_manager == NULL)
{
return;
}
ACTION_COMPLETE_COLLECTION_INFO* const info = Get();
if (info == NULL)
{
return;
}
for (int i = 0; i < ACTION_COMPLETE_COLLECTION_INFO::kMaxCollectObject; ++i)
{
CODETYPE object_key = MAP_OBJECT_KEY + info->GetObjectKey(i);
Collection* collection = static_cast<Collection*>(field->FindObject(COLLECTION_OBJECT, object_key));
if (collection != NULL)
{
collection->set_active(info->GetEnableType() != 0);
}
}
CollectAllCollectiveCmd op(field, info->GetEnableType() != 0, info->GetObjectKeyPtr());
getMGR()->ForeachPlayer(op);
}
//-------------------------------------------------------------------------------------------------
// _NA_000000_20130104_ADD_ACTION_TRIGGER_APPLY_HPMPSD
//-------------------------------------------------------------------------------------------------
__IMPL_POOL(ACTION_APPLY_HPMPSD)
class ApplyHPMPSDHandler
{
public:
ApplyHPMPSDHandler(const BYTE point_type, const DWORD figure, const BYTE apply_type, const int team_key) :
point_type_(point_type),
figure_(figure),
apply_type_(apply_type),
team_key_(team_key),
is_team_(false)
{
if (team_key_ > -1)
is_team_ = true;
}
~ApplyHPMPSDHandler(){}
void operator()(Player* player)
{
ASSERT(player);
// 플레이어가 죽었다면 트리거 적용대상에서 제외..
if (player->IsDead())
return;
// 플레어의 팀을 고려해야 한다면..
if (is_team_)
{
if (team_key_ != player->GetTeamKey())
return;
}
DWORD apply_figure = 0;
switch(point_type_)
{
case ACTION_APPLY_HPMPSD_INFO::kHP:
{
if (apply_type_ == ACTION_APPLY_HPMPSD_INFO::kIncrease)
{
apply_figure = player->IncreaseHP(figure_);
#if !defined(_NA_000000_20140224_SUMMONER_REFACTORING)
// 소환수 고려..
SummonedNPCs* const summon_npc = \
SummonManager::Instance()->FindSummonNPCs(player->GetSummonerKey());
if (summon_npc == NULL)
break;
Summoned* const summoned = summon_npc->FindSummoned(0);
if (summoned == NULL)
break;
summoned->IncreaseHP(figure_);
#endif //_NA_000000_20140224_SUMMONER_REFACTORING
}
else
{
apply_figure = player->DecreaseHP(figure_);
MSG_CG_TRIGGER_AREADAMAGE_BRD msg;
msg.m_dwObjectKey = player->GetObjectKey();
msg.m_wDamage = figure_;
msg.m_dwHP = player->GetHP();
#ifdef _NA_008804_20160502_COMBAT_LOG
time_t t;
util::TimeSync::time(&t);
msg.m_SvrTime = t;
#endif //(_NA_008804_20160502_COMBAT_LOG)
player->SendPacketAround((MSG_BASE_FORWARD*)&msg, sizeof(msg));
#if !defined(_NA_000000_20140224_SUMMONER_REFACTORING)
// 소환수 고려..
SummonedNPCs* const summon_npc = \
SummonManager::Instance()->FindSummonNPCs(player->GetSummonerKey());
if (summon_npc == NULL)
break;
// 아직 소환할수있는 개체수가 1개이기 때문에 objectkey값을 구현하지 않았다..
// object 키값을 구현하지 않아 말도 안되는 0값으로 소환수를 찾는다..
// FindSummoned 함수 내부를 보면 오브젝트 키값이 있으면 안되는 구조다..
Summoned* const summoned = summon_npc->FindSummoned(0);
if (summoned)
{
summoned->DecreaseHP(figure_);
MSG_CG_TRIGGER_AREADAMAGE_BRD msg;
msg.m_dwObjectKey = summoned->GetObjectKey();
msg.m_wDamage = figure_;
msg.m_dwHP = summoned->GetHP();
#ifdef _NA_008804_20160502_COMBAT_LOG
time_t t;
util::TimeSync::time(&t);
msg.m_SvrTime = t;
#endif //(_NA_008804_20160502_COMBAT_LOG)
player->SendPacketAround((MSG_BASE_FORWARD*)&msg, sizeof(msg));
}
#endif //_NA_000000_20140224_SUMMONER_REFACTORING
}
}
break;
case ACTION_APPLY_HPMPSD_INFO::KMP:
{
if (apply_type_ == ACTION_APPLY_HPMPSD_INFO::kIncrease)
apply_figure = player->IncreaseMP(figure_);
else
apply_figure = player->DecreaseMP(figure_);
}
break;
case ACTION_APPLY_HPMPSD_INFO::KSD:
{
if (apply_type_ == ACTION_APPLY_HPMPSD_INFO::kIncrease)
apply_figure = player->IncreaseSD(figure_);
else
apply_figure = player->DecreaseSD(figure_);
}
break;
}
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
private:
BYTE point_type_; // HP/MP/SD
DWORD figure_; // 수치
BYTE apply_type_; // 증가/감소
int team_key_;
bool is_team_;
};
void
ACTION_APPLY_HPMPSD::OnAction()
{
const ACTION_APPLY_HPMPSD_INFO* const apply_hpmpsd_info = Get();
ApplyHPMPSDHandler op(apply_hpmpsd_info->GetPointType(),
apply_hpmpsd_info->GetFigure(),
apply_hpmpsd_info->GetApplyType(),
apply_hpmpsd_info->GetTeamKey());
const BYTE target_type = apply_hpmpsd_info->GetTargetType();
switch(target_type)
{
case ACTION_APPLY_HPMPSD_INFO::kPerson: // 개인
{
Player* player = NULL;
Trigger* trigger = getParent();
DWORD click_player = trigger->GetClickedPlayerKey();
if (click_player)
player = PlayerManager::Instance()->FindPlayerByObjKey(click_player);
else
player = trigger->GetFirstSatisfiedPlayer();
if (player)
op(player);
else
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_APPLY_DAMAGE : Cant not Find Player",
trigger->TriggerID());
}
break;
case ACTION_APPLY_HPMPSD_INFO::kField: // 전체
getMGR()->ForeachPlayer(op);
break;
case ACTION_APPLY_HPMPSD_INFO::kTeam: // 팀
getParent()->ForeachSatisfiedPlayer(op);
break;
}
}
//==================================================================================================
//-------------------------------------------------------------------------------------------------
// _NA_006658_20130422_ADD_ACTION_TRIGGER_CHANGE_OBJECT_HP
//-------------------------------------------------------------------------------------------------
__IMPL_POOL(ACTION_CHANGE_OBJECT_HP)
void ACTION_CHANGE_OBJECT_HP::OnAction()
{
const ACTION_CHANGE_OBJECT_HP_INFO* const info = Get();
const DWORD object_key = info->GetObjectkey(); // 해당 오브젝트 키
MapNpc* const map_npc = getMGR()->FindMapNpc(object_key);
if (NULL == map_npc) {
return;
}
const BYTE calc_type = info->GetCalcType(); // 계산 타입 (비율/수치)
const BYTE apply_type = info->GetApplyType(); // 적용 타입 (증가/감소)
DWORD figure = info->GetFigure(); // 값
const DWORD max_hp = map_npc->GetMaxHP();
DWORD change_hp = 0;
if (calc_type == ACTION_CHANGE_OBJECT_HP_INFO::kPersentage)
{
if (figure > 100) {
figure = 100;
}
change_hp = static_cast<DWORD>(max_hp * (figure / 100.0f));
}
else if(calc_type == ACTION_CHANGE_OBJECT_HP_INFO::kValue) {
change_hp = figure;
}
if (apply_type == ACTION_CHANGE_OBJECT_HP_INFO::kIncrease) {
map_npc->IncreaseHP(change_hp);
}
else if(apply_type == ACTION_CHANGE_OBJECT_HP_INFO::kDecrease) {
map_npc->DecreaseHP(change_hp);
}
map_npc->SendHPChange();
}
//=================================================================================================
//-------------------------------------------------------------------------------------------------
// _NA_006619_20130422_ADD_ACTION_TRIGGER_CHANGE_RADAR_MAP
//-------------------------------------------------------------------------------------------------
class ChangeRadarMapHandler
{
public:
explicit ChangeRadarMapHandler(const WzID sub_map_id_) : sub_map_id_(sub_map_id_)
{
}
~ChangeRadarMapHandler()
{
}
void operator()(Player* player)
{
MSG_CG_TRIGGER_CHANGE_RADAR_MAP_CMD cmd_msg;
cmd_msg.sub_map_id = sub_map_id_;
player->SendPacket(&cmd_msg, sizeof(cmd_msg));
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
private:
WzID sub_map_id_;
};
__IMPL_POOL(ACTION_CHANGE_RADAR_MAP)
void ACTION_CHANGE_RADAR_MAP::OnAction()
{
ACTION_CHANGE_RADAR_MAP_INFO* info = Get();
const WzID sub_map_id = info->GetSubMapID();
DWORD clicked_player_key = getParent()->GetClickedPlayerKey();
if(clicked_player_key != 0)
{
Player* const player = PlayerManager::Instance()->FindPlayerByObjKey(clicked_player_key);
if (obj_util::IsValid(player))
{
ChangeRadarMapHandler op(sub_map_id);
op(player);
}
}
else
{
ChangeRadarMapHandler op(sub_map_id);
getParent()->ForeachSatisfiedPlayer(op);
getParent()->RemoveAllSatisfiedPlayer();
}
}
//=================================================================================================
//-------------------------------------------------------------------------------------------------
// _NA_006654_20120422_MISSION_TRIGGER_ADD_AND_MODIFY
//-------------------------------------------------------------------------------------------------
__IMPL_POOL(ACTION_ATTACH_STATE_RANGE)
class AttachStateRangeHandler
{
public:
AttachStateRangeHandler(DWORD object_key, WzVector* pos, float range, eCHAR_STATE_TYPE state_type, DWORD time_duration, BYTE apply_type)
{
object_key_ = object_key;
object_pos_ = *pos;
range_ = range * range;
state_type_ = state_type;
time_duration_ = time_duration;
apply_type_ = apply_type;
}
~AttachStateRangeHandler(){}
void operator()(Player* const player)
{
const WzVector& cur_pos = *player->GetPosPtr();
WzVector distance = object_pos_ - cur_pos;
float distance2 = VectorLength2(&distance);
if(distance2 <= range_)
{
if (apply_type_ == 0)
{
// 상태부여
StatusManager* const status_manager = player->GetStatusManager();
ALLOC_STATUS_PAIR steel =
status_manager->AllocStatus(state_type_, time_duration_);
if (steel.second)
steel.second->SendStatusAddBRD();
}
else if (apply_type_ == 1)
{
// 상태해제
if (!player->GetStatusManager()->Remove(state_type_))
{
MSG_CG_STATUS_REMOVE_BRD brd_msg;
brd_msg.m_dwTargetKey = player->GetObjectKey();
brd_msg.m_dwStatusCode = static_cast<DWORD>(state_type_);
player->SendPacketAround(&brd_msg, brd_msg.GetSize());
}
}
}
}
void operator()(std::pair<DWORD, Player *> pair_type)
{
operator()(pair_type.second);
}
private:
DWORD object_key_;
float range_;
WzVector object_pos_;
eCHAR_STATE_TYPE state_type_;
DWORD time_duration_;
BYTE apply_type_;
};
void ACTION_ATTACH_STATE_RANGE::OnAction()
{
ACTION_ATTACH_STATE_RANGE_INFO* info = Get();
#ifdef _NA_007266_20140530_BUG_ATTACH_STATE_TRIGGER
eOBJECTCODE_TYPE object_type = static_cast<eOBJECTCODE_TYPE>(info->GetbyObjectType());
WzVector object_pos;
const float range = info->GetfRange();
switch(object_type)
{
case NPCCODE_TYPE:
// MKS 2014.05.30
// 초기 구현의도를 알 수 없음.
// 해당 몬스터코드를 가진 모든 몬스터가 이 트리거 동작시켜야하나?
// 구현 보류
break;
case UNITID_TYPE:
{
NPC* unit = getMGR()->FindNPCForUnitID(info->GetdwObjectKey());
ASSERT(NULL != unit);
DEBUG_CODE(if(!unit) return ;);
object_pos = *unit->GetPosPtr();
} break;
case MAPNPC_TYPE:
{
MapObject* map_object = getMGR()->FindMapObject(info->GetdwObjectKey());
ASSERT(NULL != map_object);
DEBUG_CODE(if(!map_object) return ;);
object_pos = *map_object->GetPosPtr();
} break;
case NON_TYPE:
default:
break;
}
#else
MapObject* map_object = getMGR()->FindMapObject(info->GetdwObjectKey());
ASSERT(NULL != map_object);
DEBUG_CODE(if(!map_object) return ;);
WzVector object_pos = *map_object->GetPosPtr();
const float range = info->GetfRange();
#endif //_NA_007266_20140530_BUG_ATTACH_STATE_TRIGGER
AttachStateRangeHandler handler(info->GetdwObjectKey(), &object_pos, range,
static_cast<eCHAR_STATE_TYPE>(info->GetdwStateCode()),
info->GetdwTimeDuration(), info->GetbyApplyType());
getMGR()->ForeachPlayer(handler);
}
//=================================================================================================
//-------------------------------------------------------------------------------------------------
// _NA_000000_20130925_ADD_ACTION_TRIGGER_CHANGE_PLAYER_HP
//-------------------------------------------------------------------------------------------------
__IMPL_POOL(ACTION_CHANGE_PLAYER_HP)
class ChangePlayerHPHandler
{
public:
ChangePlayerHPHandler(const DWORD figure, const BYTE calc_type, const BYTE apply_type) :
figure_(figure), // HP 변동시킬 수치
calc_type_(calc_type), // 계산 타입 (비율/수치)
apply_type_(apply_type) // 증가/감소
{}
~ChangePlayerHPHandler(){}
void operator()(Player* player)
{
ASSERT(player);
// 플레이어가 죽었다면 트리거 적용대상에서 제외..
if (player->IsDead())
return;
//비율/수치 여부 판단하고 실제 HP변화시킬 값 구한다
const DWORD player_max_hp = player->GetMaxHP();
DWORD change_hp = 0;
if (calc_type_ == ACTION_CHANGE_PLAYER_HP_INFO::kPersentage)
{
if (figure_ > 100) {
figure_ = 100;
}
change_hp = static_cast<DWORD>(player_max_hp * (figure_ / 100.0f));
}
else if(calc_type_ == ACTION_CHANGE_PLAYER_HP_INFO::kValue) {
change_hp = figure_;
}
DWORD apply_figure = 0;
if (apply_type_ == ACTION_CHANGE_PLAYER_HP_INFO::kIncrease)
{
apply_figure = player->IncreaseHP(change_hp);
}
else if (apply_type_ == ACTION_CHANGE_PLAYER_HP_INFO::kDecrease)
{
apply_figure = player->DecreaseHP(change_hp);
MSG_CG_TRIGGER_AREADAMAGE_BRD msg;
msg.m_dwObjectKey = player->GetObjectKey();
msg.m_wDamage = apply_figure;
msg.m_dwHP = player->GetHP();
#ifdef _NA_008804_20160502_COMBAT_LOG
time_t t;
util::TimeSync::time(&t);
msg.m_SvrTime = t;
#endif //(_NA_008804_20160502_COMBAT_LOG)
player->SendPacketAround((MSG_BASE_FORWARD*)&msg, sizeof(msg));
}
}
void operator()(std::pair<DWORD,Player *> pair_type)
{
operator()(pair_type.second);
}
private:
DWORD figure_; // 수치
BYTE calc_type_; // 계산 타입 (비율/수치)
BYTE apply_type_; // 증가/감소
};
void ACTION_CHANGE_PLAYER_HP::OnAction()
{
const ACTION_CHANGE_PLAYER_HP_INFO* const info = Get();
Player* player = NULL;
Trigger* trigger = getParent();
//value 가져온다
const BYTE target_type = info->GetTargetType();
const BYTE calc_type = info->GetCalcType(); // 계산 타입 (비율/수치)
const BYTE apply_type = info->GetApplyType(); // 적용 타입 (증가/감소)
DWORD figure = info->GetFigure(); // 비율/값
ChangePlayerHPHandler op(figure, calc_type, apply_type);
switch(target_type)
{
case ACTION_CHANGE_PLAYER_HP_INFO::kTargetPerson: // 개인
{
DWORD click_player = trigger->GetClickedPlayerKey();
if (click_player)
player = PlayerManager::Instance()->FindPlayerByObjKey(click_player);
else
trigger->ForeachSatisfiedPlayer(op);
if (player)
op(player);
else
SUNLOG(eDEV_LOG, "[TriggerID:%u]:ACTION_APPLY_DAMAGE : Cant not Find Player",
trigger->TriggerID());
}
break;
case ACTION_CHANGE_PLAYER_HP_INFO::kTargetParty: // 파티
DWORD click_player = trigger->GetClickedPlayerKey();
if(click_player != 0)
{
player = PlayerManager::Instance()->FindPlayerByObjKey(click_player);
WORD partyKey = player->GetPartyState().GetPartyKey();
if(!NewGamePartyManager::Instance()->For_eachPlayers(partyKey, op))
SUNLOG(eCRITICAL_LOG,
"[ACTION_REWARD_PLAYER] GameParty is NULL! (%s)'s PartyKey (%u)",
player->GetCharName(), partyKey);
return;
}
else
{
getMGR()->ForeachPlayer(op);
}
break;
}
}