Files
Client/Server/RylServerProject/RylGameLibrary/Creature/Monster/PatternMonster.cpp
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

3333 lines
93 KiB
C++

#include "stdafx.h"
#include <Utility/Math/Math.h>
#include <Utility/Time/Pulse/Pulse.h>
#include <Network/Packet/PacketCommand.h>
#include <Network/Packet/PacketStruct/ServerInfo.h>
#include <Network/Packet/WrapPacket.h>
#include <Network/Packet/PacketStruct/CastlePacket.h>
#include <Network/ClientSocket/ClientConstants.h>
#include <Network/Dispatch/GameClient/GameClientDispatch.h>
#include <Network/Dispatch/GameClient/SendCharAttack.h>
#include <Network/Dispatch/DBAgent/DBAgentDispatch.h>
#include <Network/Dispatch/DBAgent/DBAgentRequest.h>
#include <Map/FieldMap/VirtualArea/VirtualArea.h>
#include <Map/FieldMap/VirtualArea/VirtualAreaMgr.h>
#include <Map/FieldMap/VirtualArea/BGServer/BGServerMap.h>
#include <Skill/SkillTable.h>
#include <Community/Party/Party.h>
#include <Community/Party/PartyMgr.h>
#include <Creature/NPC/NPC.h>
#include <Creature/Monster/VirtualMonsterMgr.h>
#include <Creature/Character/CharRespawnMgr.h>
#include "PatternMonster.h"
#include "MonsterShout.h"
// ----------------------------------------------------------------------------------------------
// 소환수 (어드민 명령, 룬오프 스킬에 의함)
CSummonMonster::CSummonMonster()
: CMonster(), m_lpMaster(NULL), m_bGuard(false)
{
m_bAdminCmdSummon = true;
}
CSummonMonster::CSummonMonster(MonsterCreateInfo& MonsterCreateInfo, CCharacter* lpMaster)
: CMonster(MonsterCreateInfo, true), m_lpMaster(lpMaster), m_bGuard(false)
{
if (NULL != m_lpMaster)
{
m_CreatureStatus.m_nLevel = m_lpMaster->GetStatus().m_nLevel;
m_lCurrentFrame = SUMMON_TIME;
}
}
CSummonMonster::~CSummonMonster()
{
}
void CSummonMonster::GuardMe(CAggresiveCreature* lpTarget, unsigned short wThreat)
{
if (true == m_bGuard)
{
AttackCmd(lpTarget, wThreat);
}
}
void CSummonMonster::AttackCmd(CAggresiveCreature* lpTarget, unsigned short wThreat)
{
m_bLongRangeAttacked = true;
m_Threat.AddToThreatList(lpTarget, wThreat);
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_CMD_ATTACK);
}
void CSummonMonster::Attacked(void)
{
if (true == m_bGuard)
{
CMonster::Attacked();
}
}
bool CSummonMonster::Attack(AtType attackType, unsigned char cDefenderNum,
CAggresiveCreature** ppDefenders, unsigned char* cDefenderJudges, unsigned short* wDefenserMPHeal)
{
if (NULL == m_lpMaster)
{
return CMonster::Attack(attackType, cDefenderNum, ppDefenders, cDefenderJudges, wDefenserMPHeal);
}
unsigned char cOffencerJudge = 0;
unsigned short wOffencerMPHeal = 0;
unsigned short wError = PktAtAck::NO_SERVER_ERR;
const int MAX_BUFFER = sizeof(PktAtAck) + AtNode::MAX_DEFENDER_NUM * sizeof(DefenserNode);
char szBuffer[MAX_BUFFER];
PktAtAck* lpPktAtAck = reinterpret_cast<PktAtAck*>(szBuffer);
DefenserNode* lpDefenserNode = reinterpret_cast<DefenserNode*>(lpPktAtAck + 1);
unsigned char cDefender = 0;
unsigned char cIndex = 0;
for (; cIndex < cDefenderNum; ++cIndex)
{
if (NULL == ppDefenders[cIndex]) { continue; }
if (0 == ppDefenders[cIndex]->GetStatus().m_nNowHP) { continue; }
CCharacter* lpCharacter = NULL;
CMonster* lpSummonee = NULL;
// 타켓이 캐릭터인 경우
if (Creature::CT_PC == Creature::GetCreatureType(ppDefenders[cDefender]->GetCID()))
{
lpCharacter = reinterpret_cast<CCharacter *>(ppDefenders[cDefender]);
lpSummonee = lpCharacter->GetSummonee();
}
else
{
// 타겟이 소환수인 경우
if (Creature::IsSummonMonster(ppDefenders[cDefender]->GetCID()))
{
lpCharacter = reinterpret_cast<CSummonMonster *>(ppDefenders[cDefender])->GetMaster();
}
}
// TODO : 공격 방향을 설정해줍시다.
cDefenderJudges[cDefender] = ClientConstants::Judge_Front;
wDefenserMPHeal[cDefender] = 0;
// 대미지 반영
const unsigned short wDamage =
ppDefenders[cDefender]->ApplyDamage(attackType, this, cOffencerJudge, cDefenderJudges[cDefender], wOffencerMPHeal, wDefenserMPHeal[cDefender], wError);
// 공격 패킷 만들기
lpDefenserNode[cDefender].m_dwCharID = ppDefenders[cIndex]->GetCID();
lpDefenserNode[cDefender].m_sCurrHP = ppDefenders[cIndex]->GetStatus().m_nNowHP;
lpDefenserNode[cDefender].m_sCurrMP = ppDefenders[cIndex]->GetStatus().m_nNowMP;
lpDefenserNode[cDefender].m_wMaxHP = ppDefenders[cIndex]->GetStatus().m_StatusInfo.m_nMaxHP;
lpDefenserNode[cDefender].m_wMaxMP = ppDefenders[cIndex]->GetStatus().m_StatusInfo.m_nMaxMP;
lpDefenserNode[cDefender].m_wMPHeal = wDefenserMPHeal[cDefender];
lpDefenserNode[cDefender].m_wDamage = wDamage;
lpDefenserNode[cDefender].m_cJudge = cDefenderJudges[cDefender];
if (NULL != lpCharacter)
{
if (NULL != lpSummonee)
{
lpSummonee->GuardMe(this, wDamage);
}
lpCharacter->CalculateEquipDurability((ClientConstants::Judge_Guard == cDefenderJudges[cDefender]) ?
AtType::GUARD : AtType::DEFENCE);
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharAttacked(lpDispatch->GetSendStream(), this, ppDefenders[cDefender],
attackType, m_MotionInfo.m_fDirection, wDamage, cDefenderJudges[cDefender], wDefenserMPHeal[cDefender], PktBase::NO_SERVER_ERR);
}
}
++cDefender;
}
if (0 == cDefender) { return true; }
lpPktAtAck->m_dwCharID = m_dwCID;
lpPktAtAck->m_AtType = attackType;
lpPktAtAck->m_wHP = m_CreatureStatus.m_nNowHP;
lpPktAtAck->m_wMP = m_CreatureStatus.m_nNowMP;
lpPktAtAck->m_wMPHeal = wOffencerMPHeal;
lpPktAtAck->m_cJudge = cOffencerJudge;
lpPktAtAck->m_cDefenserNum = cDefender;
CGameClientDispatch* lpDispatch = m_lpMaster->GetDispatcher();
if (NULL != lpDispatch)
{
CSendStream& SendStream = lpDispatch->GetSendStream();
if (true == SendStream.WrapCompress(
szBuffer, sizeof(PktAtAck) + cDefender * sizeof(DefenserNode), CmdCharAttack, 0, wError) &&
PktBase::NO_SERVER_ERR == wError)
{
CCell* lpCell = GetCellPos().m_lpCell;
if (NULL != lpCell)
{
lpCell->SendAttackInfo(m_dwCID, attackType, cDefender, lpDefenserNode);
return true;
}
}
}
return false;
}
bool CSummonMonster::Dead(CAggresiveCreature* pOffencer)
{
if (NULL != m_lpMaster)
{
m_lpMaster->SetSummonee(NULL);
GameClientSendPacket::SendCharSummonCmd(m_lpMaster, this, PktSummonCmd::SUC_DIE, m_dwCID);
m_lpMaster = NULL;
}
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
if (NULL != m_CellPos.m_lpCell)
{
m_CellPos.m_lpCell->DeleteCreature(m_dwCID);
m_CellPos.m_lpCell = NULL;
}
return CAggresiveCreature::Dead(pOffencer);
}
EnemyCheck::EnemyType CSummonMonster::IsEnemy(CCreature* lpTarget)
{
if (NULL == m_lpMaster)
{
return CMonster::IsEnemy(lpTarget);
}
return m_lpMaster->IsEnemy(lpTarget);
}
bool CSummonMonster::GetMotion(unsigned long MotionID, MotionInfo &Motion)
{
if (NULL == m_lpMaster)
{
return CMonster::GetMotion(MotionID, Motion);
}
if (false == CMonster::GetMotion(MotionID, Motion))
{
return false;
}
// 소환수의 이동 속도는 소환자와 맞춰준다. (수식은 적정량...)
if (MonsterInfo::Z3D_CA_WALK == MotionID || MonsterInfo::Z3D_CA_RUN == MotionID)
{
Motion.m_fVelocity = ((4.9f * (1.0f + (m_lpMaster->GetCharStatus().m_nDEX - 20) * 0.03f)) * Motion.m_dwFrame) / FPS;
}
return true;
}
// ----------------------------------------------------------------------------------------------
// 명예의 석상
CStatue::CStatue()
: CMonster(), m_dwDuration(0), m_wBonusTurn(0), m_cHPUpdateCount(0),
m_dwLastAttackedTime(0), m_lpParent(NULL), m_lpLinkNPC(NULL)
{
}
CStatue::CStatue(MonsterCreateInfo& MonsterCreateInfo, CStatue* lpParent)
: CMonster(MonsterCreateInfo), m_dwDuration(0), m_wBonusTurn(0),
m_cHPUpdateCount(0), m_dwLastAttackedTime(0), m_lpParent(lpParent)
{
m_nMovingPattern = FIXED;
unsigned long dwNPCID = (MonsterCreateInfo.m_dwCID - Creature::MONSTER_BIT) | Creature::NPC_LINK_BIT;
m_lpLinkNPC = CCreatureManager::GetInstance().GetNPC(dwNPCID);
}
CStatue::~CStatue()
{
}
void CStatue::GiveMileage(char cGiveType)
{
switch (m_MonsterInfo.m_dwKID)
{
// 1레벨
case MonsterInfo::STATUE_HUMAN_COMPLETE1:
case MonsterInfo::STATUE_AKHAN_COMPLETE1:
// 2레벨
case MonsterInfo::STATUE_HUMAN_COMPLETE2:
case MonsterInfo::STATUE_AKHAN_COMPLETE2:
CCreatureManager::GetInstance().ProcessAllCharacter(CStatueInfo(this, true, cGiveType));
break;
}
}
bool CStatue::RegenHPAndMP(unsigned short usAdditionalHP, unsigned short usAdditionalMP, bool bAddDefaultRegenValue)
{
if (false == CAggresiveCreature::RegenHPAndMP(usAdditionalHP, usAdditionalMP, bAddDefaultRegenValue)) {
return false;
}
// 다크 카나번 국가 전쟁 석상
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
// 중계서버에 HP 업데이트 정보 보내기
SendHPUpdate();
}
/*
// 신규존 생명축출기
else if(CServerSetup::GetInstance().GetServerZone() == SERVER_ID::ZONE9)
{
// 중계서버에 HP 업데이트 정보 보내기
SendHPUpdate();
}
*/
return true;
}
bool CStatue::Dead(CAggresiveCreature* pOffencer)
{
if (STATE_ID_DIE == m_nCurrentState) { return false; }
unsigned short wDamage = m_CreatureStatus.m_nNowHP;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
if (NULL == pOffencer)
{
// 석상 로딩 완료
// AttackInfo 를 보내서 로딩중인 석상을 클라이언트들이 없애도록 해야한다.
AtType attackType;
DefenserNode Defenser[AtNode::MAX_DEFENDER_NUM] = {0, };
attackType.m_wType = AtType::RIGHT_MELEE;
Defenser[0].m_cJudge = ClientConstants::Judge_Front;
Defenser[0].m_dwCharID = GetCID();
Defenser[0].m_wMaxHP = m_CreatureStatus.m_StatusInfo.m_nMaxHP;
Defenser[0].m_wMaxMP = m_CreatureStatus.m_StatusInfo.m_nMaxMP;
Defenser[0].m_sCurrHP = m_CreatureStatus.m_nNowHP;
Defenser[0].m_sCurrMP = m_CreatureStatus.m_nNowMP;
Defenser[0].m_wMPHeal = 0;
Defenser[0].m_wDamage = wDamage;
CCell* lpCell = m_CellPos.m_lpCell;
if (lpCell)
{
lpCell->SendAttackInfo(0, attackType, 1, &Defenser[0]);
}
}
if (NULL != m_CellPos.m_lpCell)
{
m_CellPos.m_lpCell->DeleteCreature(m_dwCID);
m_CellPos.m_lpCell = NULL;
}
CStatue* lpNextStatue = NULL;
if (NULL == pOffencer)
{
// 석상 로딩 완료
switch (m_MonsterInfo.m_dwKID)
{
// 1레벨
case MonsterInfo::STATUE_HUMAN_LOADING1:
case MonsterInfo::STATUE_AKHAN_LOADING1:
case MonsterInfo::BG_STATUE_HUMAN_LOADING1:
case MonsterInfo::BG_STATUE_AKHAN_LOADING1:
case MonsterInfo::POWER_STATUE_HUMAN_LOADING1:
case MonsterInfo::POWER_STATUE_AKHAN_LOADING1:
case MonsterInfo::INT_STATUE_HUMAN_LOADING1:
case MonsterInfo::INT_STATUE_AKHAN_LOADING1:
case MonsterInfo::EXP_STATUE_HUMAN_LOADING1:
case MonsterInfo::EXP_STATUE_AKHAN_LOADING1:
case MonsterInfo::WEALTH_STATUE_HUMAN_LOADING1:
case MonsterInfo::WEALTH_STATUE_AKHAN_LOADING1:
case MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING1:
case MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING1:
// 2레벨
case MonsterInfo::STATUE_HUMAN_LOADING2:
case MonsterInfo::STATUE_AKHAN_LOADING2:
case MonsterInfo::BG_STATUE_HUMAN_LOADING2:
case MonsterInfo::BG_STATUE_AKHAN_LOADING2:
case MonsterInfo::POWER_STATUE_HUMAN_LOADING2:
case MonsterInfo::POWER_STATUE_AKHAN_LOADING2:
case MonsterInfo::INT_STATUE_HUMAN_LOADING2:
case MonsterInfo::INT_STATUE_AKHAN_LOADING2:
case MonsterInfo::EXP_STATUE_HUMAN_LOADING2:
case MonsterInfo::EXP_STATUE_AKHAN_LOADING2:
case MonsterInfo::WEALTH_STATUE_HUMAN_LOADING2:
case MonsterInfo::WEALTH_STATUE_AKHAN_LOADING2:
case MonsterInfo::LIFE_EXTRACT_HUMAN_LOADING2:
case MonsterInfo::LIFE_EXTRACT_AKHAN_LOADING2:
lpNextStatue = GetLinkStatue(MonsterInfo::GetCompleteStatueKID(static_cast<unsigned short>(m_MonsterInfo.m_dwKID)));
break;
default:
// 이 루틴은 어드민 명령에 의해서만 수행되어야 한다. (초기화)
CStatue* lpParent = (NULL == m_lpParent) ? this : m_lpParent;
lpNextStatue = GetLinkStatue(static_cast<unsigned short>(lpParent->m_MonsterInfo.m_dwKID));
break;
}
}
else
{
// 적군에 의해 석상이 부서진 경우
if (Creature::CT_PC == Creature::GetCreatureType(pOffencer->GetCID()))
{
CCharacter* lpCharacter = reinterpret_cast<CCharacter *>(pOffencer);
lpNextStatue = GetLinkStatue(MonsterInfo::GetLoadingStatueKID(lpCharacter->GetRace(), static_cast<unsigned short>(m_MonsterInfo.m_dwKID)));
}
else if (Creature::IsSummonMonster(pOffencer->GetCID()))
{
CSummonMonster* lpSummonMonster = reinterpret_cast<CSummonMonster*>(pOffencer);
CCharacter* lpMaster = lpSummonMonster->GetMaster();
if (lpMaster)
{
lpNextStatue = GetLinkStatue(MonsterInfo::GetLoadingStatueKID(lpMaster->GetRace(), static_cast<unsigned short>(m_MonsterInfo.m_dwKID)));
}
}
else
{
ERRLOG2(g_Log, "CID:0x%08x 석상을 부순 자가 캐릭터가 아닙니다. 공격자CID:0x%08x", m_dwCID, pOffencer->GetCID());
lpNextStatue = GetLinkStatue(MonsterInfo::GetDefaultStatueKID(static_cast<unsigned short>(m_MonsterInfo.m_dwKID)));
}
}
if (NULL != lpNextStatue)
{
lpNextStatue->Respawn(m_dwLastTime);
// 컨텐츠 : 다크 카나번 국가 전쟁
if (true == CServerSetup::GetInstance().UseContents(GameRYL::STONE_BATTLE))
{
// 2008.07.17 석상전일땐 석상정보를 전달해야한다.
// 다크 카나번 국가 전쟁시
if (SERVER_ID::STONE_WAR1 <= CServerSetup::GetInstance().GetServerZone() && CServerSetup::GetInstance().GetServerZone() <= SERVER_ID::STONE_WAR3)
{
// 중계서버에 KID 및 HP 업데이트 정보 보내기
lpNextStatue->SendKIDUpdate();
lpNextStatue->SendHPUpdate(true);
// 라지맵 표시 정보 보내기
CCreatureManager::GetInstance().SendRealmStatueDisplayInfo();
// 석상 리스폰 포인트 추가 혹은 삭제
unsigned long dwStatueIndex = (lpNextStatue->GetCID() & ~Creature::MONSTER_BIT) >> 16;
if (lpNextStatue->EnableRespawn() && MonsterInfo::IsRaceCompleteStatueKID(lpNextStatue->GetKID()))
{
CCharRespawnMgr::GetInstance().AddRealmStatueRespawnPoint(dwStatueIndex, lpNextStatue->GetNation(), lpNextStatue->GetCurrentPos());
}
else
{
CCharRespawnMgr::GetInstance().DeleteRealmStatueRespawnPoint(dwStatueIndex);
}
}
}
/*
if (true == CServerSetup::GetInstance().UseContents(GameRYL::NEWZONE_ZONE9))
{
// 신규존 생명 축출기
if (CServerSetup::GetInstance().GetServerZone() == SERVER_ID::ZONE9)
{
// 중계서버에 KID 및 HP 업데이트 정보 보내기
lpNextStatue->SendKIDUpdate();
lpNextStatue->SendHPUpdate(true);
// 라지맵 표시 정보 보내기
CCreatureManager::GetInstance().SendRealmStatueDisplayInfo();
// 신규존 생명 축출기 포인트 추가 혹은 삭제
unsigned long dwStatueIndex = (lpNextStatue->GetCID() & ~Creature::MONSTER_BIT) >> 16;
if (lpNextStatue->EnableRespawn() && MonsterInfo::IsRaceCompleteStatueKID(lpNextStatue->GetKID()))
{
CCharRespawnMgr::GetInstance().AddRealmStatueRespawnPoint(dwStatueIndex, lpNextStatue->GetNation(), lpNextStatue->GetCurrentPos());
}
else
{
CCharRespawnMgr::GetInstance().DeleteRealmStatueRespawnPoint(dwStatueIndex);
}
}
}
*/
}
// 방 정보 전송
if (GetMapIndex() != 0)
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (NULL != lpVirtualArea && lpVirtualArea->GetVirtualType() == VirtualArea::BGSERVERMAP)
{
VirtualArea::CBGServerMap* lpBGServerMap = reinterpret_cast<VirtualArea::CBGServerMap*>(lpVirtualArea);
if (VirtualArea::STATUE == lpBGServerMap->GetMapType())
{
lpBGServerMap->CalculateScore();
lpBGServerMap->SendMapInfo();
// 로그 남기기
CCharacter* lpOffencerChar = reinterpret_cast<CCharacter*>(pOffencer);
if (lpOffencerChar)
{
char szOffencerNation[8];
if (lpOffencerChar->GetRace() == CClass::HUMAN) strcpy(szOffencerNation, "HUMAN");
else strcpy(szOffencerNation, "AKHAN");
DETLOG7(g_Log, "Battle Server Log :: (Channel : %d, %s) - CID : 0x%08x 캐릭터(%s, %s)가 석상을 부셨습니다. (현재 Score - HM: %d AK: %d)",
(lpBGServerMap->GetMapIndex() & ~VirtualArea::BGSERVERMAP), lpBGServerMap->GetMapTypeName(),
lpOffencerChar->GetCID(), lpOffencerChar->GetCharacterName(), szOffencerNation,
lpBGServerMap->GetMapInfo().m_wScore[CClass::HUMAN], lpBGServerMap->GetMapInfo().m_wScore[CClass::AKHAN]);
}
else
{
DETLOG4(g_Log, "Battle Server Log :: (Channel : %d, %s) - 누군가가 석상을 부셨습니다. (현재 Score - HM: %d AK: %d)",
(lpBGServerMap->GetMapIndex() & ~VirtualArea::BGSERVERMAP), lpBGServerMap->GetMapTypeName(),
lpBGServerMap->GetMapInfo().m_wScore[CClass::HUMAN], lpBGServerMap->GetMapInfo().m_wScore[CClass::AKHAN]);
}
}
}
}
return CAggresiveCreature::Dead(pOffencer);
}
bool CStatue::Rest(void)
{
if (STATE_ID_DIE == m_nCurrentState)
{
return false;
}
m_wBonusTurn = 0;
m_CreatureStatus.m_nNowHP = 0;
m_dwLastBehaviorTick = m_dwLastTime = CPulse::GetInstance().GetLastTick();
m_lCurrentFrame = FPS;
m_bAttacking = false;
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_ZERO_HP);
if (NULL != m_CellPos.m_lpCell)
{
m_CellPos.m_lpCell->DeleteCreature(m_dwCID);
m_CellPos.m_lpCell = NULL;
}
return CAggresiveCreature::Dead(NULL);
}
EnemyCheck::EnemyType CStatue::IsEnemy(CCreature* lpTarget)
{
return CMonster::IsEnemy(lpTarget);
}
bool CStatue::CreateLinkStatue(unsigned short wKind)
{
// 초기 상태가 중립이 아닌 석상들을 위한 처리
// ex) 초기 상태가 휴먼 석상이라면 휴먼 석상을 만들 타이밍에 중립 석상을 만든다.
if (m_MonsterInfo.m_dwKID == wKind)
{
if (0 != GetMapIndex())
{
wKind = MonsterInfo::BG_STATUE_NEUTRALITY1;
}
else
{
// 1레벨
if (MonsterInfo::STATUE_NEUTRALITY1 <= wKind && MonsterInfo::STATUE_AKHAN_COMPLETE1 >= wKind)
{
wKind = MonsterInfo::STATUE_NEUTRALITY1;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY1 <= wKind && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE1 >= wKind)
{
wKind = MonsterInfo::POWER_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY1 <= wKind && MonsterInfo::INT_STATUE_AKHAN_COMPLETE1 >= wKind)
{
wKind = MonsterInfo::INT_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY1 <= wKind && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE1 >= wKind)
{
wKind = MonsterInfo::EXP_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY1 <= wKind && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE1 >= wKind)
{
wKind = MonsterInfo::WEALTH_STATUE_NEUTRALITY1;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY1 <= wKind && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE1 >= wKind)
{
wKind = MonsterInfo::LIFE_EXTRACT_NEUTRALITY1;
}
// 2레벨
else if (MonsterInfo::STATUE_NEUTRALITY2 <= wKind && MonsterInfo::STATUE_AKHAN_COMPLETE2 >= wKind)
{
wKind = MonsterInfo::STATUE_NEUTRALITY2;
}
else if (MonsterInfo::POWER_STATUE_NEUTRALITY2 <= wKind && MonsterInfo::POWER_STATUE_AKHAN_COMPLETE2 >= wKind)
{
wKind = MonsterInfo::POWER_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::INT_STATUE_NEUTRALITY2 <= wKind && MonsterInfo::INT_STATUE_AKHAN_COMPLETE2 >= wKind)
{
wKind = MonsterInfo::INT_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::EXP_STATUE_NEUTRALITY2 <= wKind && MonsterInfo::EXP_STATUE_AKHAN_COMPLETE2 >= wKind)
{
wKind = MonsterInfo::EXP_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::WEALTH_STATUE_NEUTRALITY2 <= wKind && MonsterInfo::WEALTH_STATUE_AKHAN_COMPLETE2 >= wKind)
{
wKind = MonsterInfo::WEALTH_STATUE_NEUTRALITY2;
}
else if (MonsterInfo::LIFE_EXTRACT_NEUTRALITY2 <= wKind && MonsterInfo::LIFE_EXTRACT_AKHAN_COMPLETE2 >= wKind)
{
wKind = MonsterInfo::LIFE_EXTRACT_NEUTRALITY2;
}
}
}
CMonster::MonsterCreateInfo tempInfo;
tempInfo.m_dwCID = (m_dwCID & ~Creature::MONSTER_KIND_BIT) | wKind;
// 이미 존재하는지 체크
if (0 != GetMapIndex())
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (lpVirtualArea)
{
CVirtualMonsterMgr* lpVirtualMonsterMgr = lpVirtualArea->GetMonsterManager();
if (lpVirtualMonsterMgr && NULL != lpVirtualMonsterMgr->GetCreature(tempInfo.m_dwCID))
{
ERRLOG0(g_Log, "석상 생성에 실패하였습니다.");
return false;
}
}
}
else
{
if (NULL != CCreatureManager::GetInstance().GetCreature(tempInfo.m_dwCID))
{
ERRLOG0(g_Log, "석상 생성에 실패하였습니다.");
return false;
}
}
tempInfo.m_nKID = wKind;
tempInfo.m_Pos = m_OriginalPosition;
CStatue* lpStatue = new CStatue(tempInfo, this);
if (NULL == lpStatue)
{
ERRLOG0(g_Log, "석상 생성에 실패하였습니다.");
return false;
}
lpStatue->SetMapIndex(GetMapIndex());
if (false == lpStatue->InitMonster(tempInfo.m_Pos, true))
{
ERRLOG0(g_Log, "석상 생성에 실패하였습니다.");
return false;
}
// 몬스터 추가
if (0 != GetMapIndex())
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (lpVirtualArea)
{
CVirtualMonsterMgr* lpVirtualMonsterMgr = lpVirtualArea->GetMonsterManager();
if (lpVirtualMonsterMgr) lpVirtualMonsterMgr->AddMonster(lpStatue);
}
}
else
{
CCreatureManager::GetInstance().AddCreature(lpStatue);
}
return true;
}
CStatue* CStatue::GetLinkStatue(unsigned short wKind)
{
CStatue* lpParent = (NULL == m_lpParent) ? this : m_lpParent;
if (0 != GetMapIndex())
{
VirtualArea::CVirtualArea* lpVirtualArea = VirtualArea::CVirtualAreaMgr::GetInstance().GetVirtualArea(GetMapIndex());
if (lpVirtualArea)
{
CVirtualMonsterMgr* lpVirtualMonsterMgr = lpVirtualArea->GetMonsterManager();
if (lpVirtualMonsterMgr)
{
return reinterpret_cast<CStatue *>(lpVirtualMonsterMgr->GetMonster(
(lpParent->GetCID() & ~Creature::MONSTER_KIND_BIT) | wKind));
}
}
}
else
{
return reinterpret_cast<CStatue *>(CCreatureManager::GetInstance().GetMonster(
(lpParent->GetCID() & ~Creature::MONSTER_KIND_BIT) | wKind));
}
return NULL;
}
Item::CItem* CStatue::SellToCharacter(CCharacter *lpCustomer, unsigned short wKindItem, TakeType takeType,
Item::CItem* lpRequestItem, unsigned long &dwPrice, unsigned short wCouponID, unsigned short &usError)
{
if (NULL != m_lpLinkNPC)
{
unsigned short wError = 0;
return m_lpLinkNPC->SellToCharacter(lpCustomer, wKindItem, takeType, lpRequestItem, dwPrice, wCouponID, wError);
usError = wError;
}
return NULL;
}
bool CStatue::SendKIDUpdate()
{
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 != lpDBAgentDispatch)
{
CSendStream& SendStream = lpDBAgentDispatch->GetSendStream();
char* lpBuffer = SendStream.GetBuffer(sizeof(PktStatueCmd) + sizeof(StatueInfo));
if (NULL != lpBuffer)
{
PktStatueCmd* lpPktStatueCmd = reinterpret_cast<PktStatueCmd *>(lpBuffer);
StatueInfo* lpStatueInfo = reinterpret_cast<StatueInfo*>(lpPktStatueCmd + 1);
lpPktStatueCmd->m_wSubCmd = PktStatueCmd::CMD_UPDATE_KID;
lpPktStatueCmd->m_dwValue = CServerSetup::GetInstance().GetServerChannel();
lpPktStatueCmd->m_wSize = sizeof(StatueInfo);
lpStatueInfo->m_dwIndex = (GetCID() & ~Creature::MONSTER_BIT) >> 16;
lpStatueInfo->m_wKID = GetKID();
lpStatueInfo->m_dwLostHP = 0;
return SendStream.WrapCrypt(sizeof(PktStatueCmd) + sizeof(StatueInfo), CmdStatueCmd, 0, 0);
}
}
return false;
}
bool CStatue::SendHPUpdate(bool bForce)
{
++m_cHPUpdateCount;
if (m_cHPUpdateCount >= MAX_HP_UPDATE_COUNT || bForce)
{
m_cHPUpdateCount = 0;
GET_SINGLE_DISPATCH(lpDBAgentDispatch,
CDBAgentDispatch, CDBAgentDispatch::GetDispatchTable());
if (0 != lpDBAgentDispatch)
{
CSendStream& SendStream = lpDBAgentDispatch->GetSendStream();
char* lpBuffer = SendStream.GetBuffer(sizeof(PktStatueCmd) + sizeof(StatueInfo));
if (NULL != lpBuffer)
{
PktStatueCmd* lpPktStatueCmd = reinterpret_cast<PktStatueCmd *>(lpBuffer);
StatueInfo* lpStatueInfo = reinterpret_cast<StatueInfo*>(lpPktStatueCmd + 1);
lpPktStatueCmd->m_wSubCmd = PktStatueCmd::CMD_UPDATE_LOST_HP;
lpPktStatueCmd->m_dwValue = CServerSetup::GetInstance().GetServerChannel();
lpPktStatueCmd->m_wSize = sizeof(StatueInfo);
lpStatueInfo->m_dwIndex = (GetCID() & ~Creature::MONSTER_BIT) >> 16;
lpStatueInfo->m_wKID = 0;
lpStatueInfo->m_dwLostHP = GetStatus().m_StatusInfo.m_nMaxHP - GetStatus().m_nNowHP;
return SendStream.WrapCrypt(sizeof(PktStatueCmd) + sizeof(StatueInfo), CmdStatueCmd, 0, 0);
}
else
{
return false;
}
}
else
{
return false;
}
}
return true;
}
void CStatue::SendAttacked()
{
unsigned long dwNowTime = timeGetTime();
if (dwNowTime - m_dwLastAttackedTime >= STATUE_ATTACKED_TIME)
{
m_dwLastAttackedTime = dwNowTime;
unsigned long dwIndex = (GetCID() & ~Creature::MONSTER_BIT) >> 16;
// 라지맵 표시 정보 보내기
CCreatureManager::GetInstance().SendRealmStatueDisplayInfo(dwIndex, true);
}
}
CStatueInfo::CStatueInfo(CStatue* lpStatue, bool bBonusTurn, char cGiveType)
: m_lpStatue(lpStatue), m_bBonusTurn(bBonusTurn), m_cGiveType(cGiveType)
{
}
bool CStatueInfo::operator () (CCharacter* lpCharacter)
{
if (NULL != lpCharacter)
{
if (lpCharacter->GetMapIndex() != m_lpStatue->GetMapIndex())
{
return false;
}
char cAddMileage = 1;
// if(SERVER_ID::STONE_WAR2 == CServerSetup::GetInstance().GetServerZone())
// cAddMileage = 5;
// else if(SERVER_ID::STONE_WAR3 == CServerSetup::GetInstance().GetServerZone())
// cAddMileage = 10;
PktStatueInfo pktSI;
pktSI.m_dwMileage = 0;
pktSI.m_wHumanNum = CCreatureManager::GetInstance().GetCharacterNum(CClass::HUMAN);
pktSI.m_wAkhanNum = CCreatureManager::GetInstance().GetCharacterNum(CClass::AKHAN);
pktSI.m_wNowHP = m_lpStatue->GetStatus().m_nNowHP;
pktSI.m_wKID = static_cast<unsigned short>(m_lpStatue->GetCID() & Creature::MONSTER_KIND_BIT);
pktSI.m_cState = m_lpStatue->GetCurrentState();
pktSI.m_cIndex = static_cast<unsigned char>(m_lpStatue->GetSerialNumber());
if (true == m_bBonusTurn)
{
if(m_cGiveType == 0) // 기존방식 과거 ZONE3번에서 진행했던 배틀그라운드 형식
{
const unsigned char cBonusTurn =
CCreatureManager::GetInstance().GetBonusTurn(lpCharacter->GetMapIndex());
if (0 != lpCharacter->GetStatus().m_nNowHP &&
0 != m_lpStatue->GetStatus().m_nNowHP &&
static_cast<unsigned char>(lpCharacter->GetRace()) == cBonusTurn &&
m_lpStatue->GetCurrentPos().GetDistance(lpCharacter->GetCurrentPos()) < CStatue::BONUS_DISTANCE)
{
lpCharacter->SetMileage(lpCharacter->GetMileage() + CStatue::BONUS_MILEAGE);
pktSI.m_dwMileage = lpCharacter->GetMileage();
}
}
else
{
// 해당석상이 어느종족꺼냐..
unsigned char cBonusTurn = CClass::MAX_RACE;
switch (m_lpStatue->GetKID())
{
case MonsterInfo::STATUE_HUMAN_COMPLETE1:
cAddMileage = 1;
cBonusTurn = CClass::HUMAN;
break;
case MonsterInfo::STATUE_HUMAN_COMPLETE2:
cAddMileage = 1;
cBonusTurn = CClass::HUMAN;
break;
case MonsterInfo::STATUE_AKHAN_COMPLETE1:
cAddMileage = 1;
cBonusTurn = CClass::AKHAN;
break;
case MonsterInfo::STATUE_AKHAN_COMPLETE2:
cAddMileage = 1;
cBonusTurn = CClass::AKHAN;
break;
}
if (0 != lpCharacter->GetStatus().m_nNowHP &&
0 != m_lpStatue->GetStatus().m_nNowHP &&
static_cast<unsigned char>(lpCharacter->GetRace()) == cBonusTurn)
{
lpCharacter->SetMileage(lpCharacter->GetMileage() + cAddMileage );
pktSI.m_dwMileage = lpCharacter->GetMileage();
}
}
}
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
char* szPacket = reinterpret_cast<char *>(&pktSI);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktStatueInfo), CmdStatueInfo, 0, 0))
{
return lpDispatch->GetSendStream().PutBuffer(szPacket, sizeof(PktStatueInfo), CmdStatueInfo);
}
}
}
return false;
}
CBlessMileageInfo::CBlessMileageInfo(char cBlessType)
: m_cBlessType(cBlessType)
{
m_cAddMileage = 1;
if(SERVER_ID::STONE_WAR2 == CServerSetup::GetInstance().GetServerZone())
m_cAddMileage = 5;
else if(SERVER_ID::STONE_WAR3 == CServerSetup::GetInstance().GetServerZone())
m_cAddMileage = 10;
}
bool CBlessMileageInfo::operator () (CCharacter* lpCharacter)
{
if (NULL != lpCharacter)
{
PktStatueInfo pktSI;
pktSI.m_dwMileage = 0;
pktSI.m_wHumanNum = CCreatureManager::GetInstance().GetCharacterNum(CClass::HUMAN);
pktSI.m_wAkhanNum = CCreatureManager::GetInstance().GetCharacterNum(CClass::AKHAN);
pktSI.m_wNowHP = 0;
pktSI.m_wKID = 0;
pktSI.m_cState = 0;
pktSI.m_cIndex = 0;
if (0 != lpCharacter->GetStatus().m_nNowHP)
{
lpCharacter->SetMileage(lpCharacter->GetMileage() + m_cAddMileage);
pktSI.m_dwMileage = lpCharacter->GetMileage();
}
CGameClientDispatch* lpDispatch = lpCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
char* szPacket = reinterpret_cast<char *>(&pktSI);
if (PacketWrap::WrapCrypt(szPacket, sizeof(PktStatueInfo), CmdStatueInfo, 0, 0))
{
return lpDispatch->GetSendStream().PutBuffer(szPacket, sizeof(PktStatueInfo), CmdStatueInfo);
}
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 오브젝트 몬스터 (꽃, 나무, 버섯류)
CGatherMonster::CGatherMonster()
: CMonster()
{
}
CGatherMonster::CGatherMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CMonster(MonsterCreate, bAdminCmdSummon)
{
}
CGatherMonster::~CGatherMonster()
{
}
// -------------------------------------------------------------------------------------
// 오브젝트 몬스터 (꽃, 나무, 버섯류)
CObjectMonster::CObjectMonster()
: CMonster()
{
}
CObjectMonster::CObjectMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CMonster(MonsterCreate, bAdminCmdSummon)
{
}
CObjectMonster::~CObjectMonster()
{
}
// -------------------------------------------------------------------------------------
// 스킬 사용 몬스터
CSkillMonster::CSkillMonster()
: CMonster(), m_cConsumeMPCount(0), m_bCasting(false), m_dwTargetCID(0), m_cSkillPattern(0), m_nCastingCount(0)
{
std::fill_n(m_lastCastTime, int(MAX_SKILL_PATTERN), 0);
ZeroMemory(&m_attackType, sizeof(AtType));
}
CSkillMonster::CSkillMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CMonster(MonsterCreate, bAdminCmdSummon), m_cConsumeMPCount(0), m_bCasting(false),
m_dwTargetCID(0), m_cSkillPattern(0), m_nCastingCount(0)
{
std::fill_n(m_lastCastTime, int(MAX_SKILL_PATTERN), 0);
ZeroMemory(&m_attackType, sizeof(AtType));
}
CSkillMonster::~CSkillMonster()
{
}
bool CSkillMonster::Dead(CAggresiveCreature* pOffencer)
{
m_bCasting = false;
const char* szCharacterName = 0;
if (0 != pOffencer && Creature::CT_PC == Creature::GetCreatureType(pOffencer->GetCID()))
{
szCharacterName = static_cast<CCharacter*>(pOffencer)->GetCharacterName();
}
CMonsterShout::GetInstance().Shout(m_dwCID, m_MonsterInfo.m_dwKID,
static_cast<unsigned short>(m_CurrentPos.m_fPointX),
static_cast<unsigned short>(m_CurrentPos.m_fPointZ),
CMonsterShout::DEAD, szCharacterName, 0);
return CMonster::Dead(pOffencer);
}
LONG CSkillMonster::SendMove(unsigned short nAniNum)
{
// edith 2009.07.16 몬스터는 스킬 캐스팅 중에는 이동하지 마라.
// if(m_bCasting)
// return 0;
return CMonster::SendMove(nAniNum);
}
bool CSkillMonster::Attack(AtType attackType, unsigned char cDefenderNum,
CAggresiveCreature** ppDefenders, unsigned char* cDefenderJudges, unsigned short* wDefenderMPHeal)
{
if (m_CreatureStatus.m_nNowHP == 0)
{
ERRLOG1(g_Log, "CID:0x%08x 죽은 몬스터가 공격하려고 하였습니다.", m_dwCID);
return false;
}
if (cDefenderNum > AtNode::MAX_DEFENDER_NUM)
{
ERRLOG2(g_Log, "CID:0x%08x 몬스터가 공격할 때, 방어자의 숫자가 최대 방어자 숫자를 넘었습니다. 방어자수 : %d",
m_dwCID, cDefenderNum);
cDefenderNum = AtNode::MAX_DEFENDER_NUM;
}
if (0 == (attackType.m_wType & AtType::SKILL_BIT) && 0 == cDefenderNum)
{
ERRLOG0(g_Log, "스킬이 아닌 일반 공격은, 반드시 타겟이 있을 경우에만 서버로 보내야 합니다.");
return false;
}
if (0 != (attackType.m_wType & AtType::SKILL_BIT))
{
const Skill::ProtoType* pSkillProtoType = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == pSkillProtoType)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, attackType.m_wType);
return false;
}
}
unsigned char cOffencerJudge = 0;
unsigned short wOffencerMPHeal = 0;
unsigned short wError = PktAtAck::NO_SERVER_ERR;
m_cConsumeMPCount = std::min(cDefenderNum, unsigned char(AtNode::MAX_MONSTER_DEFENDER_NUM));
unsigned char cDefender = 0;
unsigned char cIndex = 0;
// MON_TODO : by Vincent - 2004 : 2 : 25
DefenserNode Defenser[AtNode::MAX_DEFENDER_NUM];
// ------------
// 몬스터 외치기 때문에 넣음.
unsigned int nCriticalCount = 0;
CCharacter* lpDefendShoutCharacter = 0;
for (; cIndex < cDefenderNum; ++cIndex)
{
// MP 소모 타이밍까지의 카운트 (범위 마법은 한 번만 MP 소모)
--m_cConsumeMPCount;
if (NULL == ppDefenders[cIndex])
{
continue;
}
if (0 == ppDefenders[cIndex]->GetStatus().m_nNowHP)
{
continue;
}
// TODO : 공격 방향을 설정해줍시다.
cDefenderJudges[cDefender] = ClientConstants::Judge_Front;
wDefenderMPHeal[cDefender] = 0;
const unsigned short nPrevHP = ppDefenders[cIndex]->GetStatus().m_nNowHP;
const unsigned short nPrevMP = ppDefenders[cIndex]->GetStatus().m_nNowMP;
const unsigned short wPrevAttackerHP = m_CreatureStatus.m_nNowHP;
// 대미지 반영
const unsigned short wDamage = ppDefenders[cIndex]->ApplyDamage(attackType, this,
cOffencerJudge, cDefenderJudges[cDefender], wOffencerMPHeal, wDefenderMPHeal[cDefender], wError);
if (cDefenderJudges[cDefender] == ClientConstants::Judge_Critical)
{
++nCriticalCount;
}
const unsigned short nNowHP = ppDefenders[cIndex]->GetStatus().m_nNowHP;
const unsigned short nNowMP = ppDefenders[cIndex]->GetStatus().m_nNowMP;
// 스킬에 의한 자살 방지
if (0 == m_CreatureStatus.m_nNowHP)
{
m_CreatureStatus.m_nNowHP = wPrevAttackerHP;
wError = PktAtAck::FAIL_SUICIDE;
break;
}
else
{
if (Creature::CT_PC == Creature::GetCreatureType(ppDefenders[cIndex]->GetCID()))
{
// 방어자가 캐릭터인 경우
CCharacter* lpDefendCharacter = (CCharacter *)ppDefenders[cIndex];
if (0 == lpDefendShoutCharacter || 0 == (rand() % 3))
{
lpDefendShoutCharacter = lpDefendCharacter;
}
CMonster* lpSummonee = lpDefendCharacter->GetSummonee();
if (NULL != lpSummonee)
{
lpSummonee->GuardMe(this, wDamage);
}
lpDefendCharacter->CalculateEquipDurability((ClientConstants::Judge_Guard == cDefenderJudges[cDefender]) ?
AtType::GUARD : AtType::DEFENCE);
CGameClientDispatch* lpDispatch = lpDefendCharacter->GetDispatcher();
if (NULL != lpDispatch)
{
GameClientSendPacket::SendCharAttacked(lpDispatch->GetSendStream(), this, lpDefendCharacter,
attackType, m_MotionInfo.m_fDirection, wDamage, cDefenderJudges[cDefender],
wDefenderMPHeal[cDefender], PktBase::NO_SERVER_ERR);
}
}
}
Defenser[cDefender].m_cJudge = cDefenderJudges[cDefender];
Defenser[cDefender].m_dwCharID = ppDefenders[cDefender]->GetCID();
Defenser[cDefender].m_wMaxHP = ppDefenders[cDefender]->GetStatus().m_StatusInfo.m_nMaxHP;
Defenser[cDefender].m_wMaxMP = ppDefenders[cDefender]->GetStatus().m_StatusInfo.m_nMaxMP;
Defenser[cDefender].m_sCurrHP = ppDefenders[cDefender]->GetStatus().m_nNowHP;
Defenser[cDefender].m_sCurrMP = ppDefenders[cDefender]->GetStatus().m_nNowMP;
Defenser[cDefender].m_wMPHeal = wDefenderMPHeal[cDefender];
Defenser[cDefender].m_wDamage = wDamage;
++cDefender;
}
// 외치기 검사
CMonsterShout::Behavior eBehavior = CMonsterShout::NORMAL_ATTACK;
unsigned short usShoutSkill_ID = 0;
if (0 != (attackType.m_wType & AtType::SKILL_BIT))
{
if (0 == cDefender)
{
Skill::CFunctions::ConsumeMP(attackType, this, 0);
}
eBehavior = CMonsterShout::SKILL_ATTACK;
usShoutSkill_ID = attackType.m_wType;
}
else if (0 < nCriticalCount)
{
eBehavior = CMonsterShout::CRITICAL_ATTACK;
}
const char* szName = 0;
if (0 != lpDefendShoutCharacter)
{
szName = lpDefendShoutCharacter->GetCharacterName();
}
// 외치기 검색해서 외침. 이름은, 공격하는 경우이므로 맞는 넘 아무넘이나 한넘 잡아서 그넘 이름을 준다.
CMonsterShout::GetInstance().Shout(m_dwCID, m_MonsterInfo.m_dwKID,
static_cast<unsigned short>(m_CurrentPos.m_fPointX),
static_cast<unsigned short>(m_CurrentPos.m_fPointZ), eBehavior,
szName, usShoutSkill_ID);
// MON_TODO : by Vincent - 2004 : 2 : 25
CCell* lpCell = m_CellPos.m_lpCell;
if (lpCell)
{
lpCell->SendAttackInfo(GetCID(), attackType, cDefender, &Defenser[0]);
}
// ------------
return true;
}
bool CSkillMonster::UseSkill(AtType attackType, CAggresiveCreature** ppDefenders, char cSkillPattern)
{
if (NULL == ppDefenders[0])
{
return false;
}
// 스킬 얻기
const Skill::ProtoType* pThisSkill = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == pThisSkill)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, attackType.m_wType);
return false;
}
char cTargetType = Skill::Target::ENEMY;
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
unsigned short useMP = pThisSkill[cSkillLockCount].m_StartMP + pThisSkill[cSkillLockCount].m_LockMP +
(pThisSkill[cSkillLockCount].m_LevelMP + pThisSkill[cSkillLockCount].m_LockMP) * cSkillLevel ;
unsigned long dwCurrentTime = timeGetTime();
unsigned long dwCoolDownTime = pThisSkill[cSkillLockCount].m_dwCoolDownTime;
if (pThisSkill->m_bIsClassSkill) dwCoolDownTime = pThisSkill[0].m_dwCoolDownTime;
// 스킬을 사용할수 없는 상황인지 체크
if (m_CreatureStatus.m_nNowMP < useMP ||
dwCurrentTime - m_lastCastTime[cSkillPattern] < dwCoolDownTime ||
Math::Random::ComplexRandom(100) > m_MonsterInfo.m_wSkillUseRate)
{
return false;
}
// 스킬 거리 체크
unsigned char cDefenderNum = 1;
unsigned char nDefenserJudges[AtNode::MAX_DEFENDER_NUM];
unsigned short wDefenserMPHeal[AtNode::MAX_DEFENDER_NUM];
CAggresiveCreature* pTarget = ppDefenders[0];
if (NULL != pTarget)
{
float fSquareTargetDistance = (m_CurrentPos.m_fPointX - pTarget->GetCurrentPos().m_fPointX) *
(m_CurrentPos.m_fPointX - pTarget->GetCurrentPos().m_fPointX) +
(m_CurrentPos.m_fPointZ - pTarget->GetCurrentPos().m_fPointZ) *
(m_CurrentPos.m_fPointZ - pTarget->GetCurrentPos().m_fPointZ);
float fSquareEffectDistance = (pThisSkill[cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE) *
(pThisSkill[cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE);
if (fSquareTargetDistance > fSquareEffectDistance)
{
return false;
}
// 스킬 대상 설정
if (pThisSkill->m_eTargetType == Skill::Target::FRIEND ||
pThisSkill->m_eTargetType == Skill::Target::DEAD_FRIEND ||
pThisSkill->m_eTargetType == Skill::Target::FRIEND_EXCEPT_SELF ||
pThisSkill->m_eTargetType == Skill::Target::FRIEND_OBJECT ||
pThisSkill->m_eTargetType == Skill::Target::PARTY ||
pThisSkill->m_eTargetType == Skill::Target::SUMMON)
{
cTargetType = Skill::Target::FRIEND;
}
}
// TODO : 공격하는 방향을 설정합시다. (현재는 무조건 정면)
short nMaxDefenderNum = AtNode::MAX_DEFENDER_NUM;
std::fill_n(&nDefenserJudges[0], nMaxDefenderNum, ClientConstants::Judge_Front);
std::fill_n(&wDefenserMPHeal[0], nMaxDefenderNum, 0);
float fDir = CalcDir2D(GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointZ,
pTarget->GetCurrentPos().m_fPointX, pTarget->GetCurrentPos().m_fPointZ);
// 캐스팅 타입이면, 캐스트 모드로 변환하고, 나머지는 바로 사용한다.
if (pThisSkill->m_eSkillType == Skill::Type::CAST)
{
m_bCasting = true;
m_attackType = attackType;
m_dwTargetCID = ppDefenders[0]->GetCID();
m_cSkillPattern = cSkillPattern;
// edith 2009.08.06 스킬 공격시 이동하지 않게 하기
SendMove(1);
return true;
}
// 스킬 사용한 시간을 저장
m_lastCastTime[cSkillPattern] = dwCurrentTime;
// MON_TODO : 스킬 사용 정보를 보내준다.
CCell* lpCell = m_CellPos.m_lpCell;
if (lpCell)
{
CastObject castObject;
castObject.m_wTypeID = attackType.m_wType;
castObject.m_DstPos.fPointX = ppDefenders[0]->GetCurrentPos().m_fPointX * 100.f;
castObject.m_DstPos.fPointY = ppDefenders[0]->GetCurrentPos().m_fPointY * 100.f;
castObject.m_DstPos.fPointZ = ppDefenders[0]->GetCurrentPos().m_fPointZ * 100.f;
castObject.m_cObjectLevel = attackType.m_cSkillLockCount;
if (pThisSkill->m_bIsClassSkill) castObject.m_cObjectLevel = 0;
castObject.m_dwTargetID = ppDefenders[0]->GetCID();
castObject.m_cObjectType = 6;
const Skill::ProtoType* lpSkillProtoType = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == lpSkillProtoType)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x",
castObject.m_dwTargetID, attackType.m_wType);
return 0;
}
else
{
switch (lpSkillProtoType->m_usSkill_ID)
{
case 0x8104 : castObject.m_cObjectType = 11; break;
case 0x9104 : castObject.m_cObjectType = 31; break;
case 0x8603 : castObject.m_cObjectType = 12; break;
case 0x8702 : castObject.m_cObjectType = 20; break;
case 0x9802 : castObject.m_cObjectType = 34; break;
}
}
// edith 2009.08.06 몬스터가 스킬쓸때 스킬의 이팩트를 클라이언트에서 띄워주기 위한 부분.
lpCell->SendCastObjectInfo(GetCID(), ppDefenders[0]->GetCID(), castObject);
}
// 범위 마법이라면...
if (0 != pThisSkill[cSkillLockCount].m_fEffectExtent)
{
if (Skill::Target::PARTY == pThisSkill[cSkillLockCount].m_eTargetType)
{
// 파티 마법이라면
CParty* lpParty = ppDefenders[0]->GetParty();
if (NULL != lpParty)
{
return lpParty->Attack(attackType, ppDefenders, nDefenserJudges, this,
pThisSkill[attackType.m_cSkillLockCount].m_fEffectExtent, cTargetType);
}
}
else
{
// 일반 범위 마법
return CAggresiveCreature::MultiAttack(attackType, cDefenderNum, ppDefenders, nDefenserJudges,
GetCurrentPos(), fDir, pThisSkill[cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE, m_MonsterInfo.m_fAttackAngle, cTargetType);
}
}
// 사용하는 스킬의 종류에 따라서 Attack 호출 함수를 달리한다.
// Enchant 부여 마법의 경우는 한명에게만 사용한다.
return Attack(attackType, cDefenderNum, ppDefenders, nDefenserJudges, wDefenserMPHeal);
}
bool CSkillMonster::UseCastedSkill(void)
{
CAggresiveCreature* pTarget = reinterpret_cast<CAggresiveCreature*>(CCreatureManager::GetInstance().GetCreature(m_dwTargetCID));
if (NULL == pTarget) return false;
if (pTarget->GetMapIndex() != GetMapIndex()) return false;
// 스킬 얻기
const Skill::ProtoType* pThisSkill = CSkillMgr::GetInstance().GetSkillProtoType(m_attackType.m_wType);
if (NULL == pThisSkill)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, m_attackType.m_wType);
return false;
}
char cTargetType = Skill::Target::ENEMY;
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / 6;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % 6;
// 100% 확률로 스킬을 사용한 것이기 때문에.. 캐스트 타임을 셋팅하고, 마법 사용 확률은 검사하지 않는다.
m_lastCastTime[m_cSkillPattern] = timeGetTime();
unsigned char cDefenderNum = 1;
unsigned char nDefenserJudges[AtNode::MAX_DEFENDER_NUM];
unsigned short wDefenserMPHeal[AtNode::MAX_DEFENDER_NUM];
CAggresiveCreature* ppDefenders[AtNode::MAX_DEFENDER_NUM] = {0, };
ppDefenders[0] = pTarget;
// 스킬 거리 체크
if (NULL != pTarget)
{
float fSquareTargetDistance = (m_CurrentPos.m_fPointX - pTarget->GetCurrentPos().m_fPointX) *
(m_CurrentPos.m_fPointX - pTarget->GetCurrentPos().m_fPointX) +
(m_CurrentPos.m_fPointZ - pTarget->GetCurrentPos().m_fPointZ) *
(m_CurrentPos.m_fPointZ - pTarget->GetCurrentPos().m_fPointZ);
float fSquareEffectDistance = (pThisSkill[cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE) *
(pThisSkill[cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE);
if (fSquareTargetDistance > fSquareEffectDistance)
{
return false;
}
// 스킬 대상 설정
if (pThisSkill->m_eTargetType == Skill::Target::FRIEND ||
pThisSkill->m_eTargetType == Skill::Target::DEAD_FRIEND ||
pThisSkill->m_eTargetType == Skill::Target::FRIEND_EXCEPT_SELF ||
pThisSkill->m_eTargetType == Skill::Target::FRIEND_OBJECT ||
pThisSkill->m_eTargetType == Skill::Target::PARTY ||
pThisSkill->m_eTargetType == Skill::Target::SUMMON)
{
cTargetType = Skill::Target::FRIEND;
}
}
// TODO : 공격하는 방향을 설정합시다. (현재는 무조건 정면)
short nMaxDefenderNum = AtNode::MAX_DEFENDER_NUM;
std::fill_n(&nDefenserJudges[0], nMaxDefenderNum, ClientConstants::Judge_Front);
std::fill_n(&wDefenserMPHeal[0], nMaxDefenderNum, 0);
float fDir = CalcDir2D(GetCurrentPos().m_fPointX, GetCurrentPos().m_fPointZ,
pTarget->GetCurrentPos().m_fPointX, pTarget->GetCurrentPos().m_fPointZ);
// MON_TODO : 스킬 사용 정보를 보내준다.
CCell* lpCell = m_CellPos.m_lpCell;
if (lpCell)
{
CastObject castObject;
castObject.m_wTypeID = m_attackType.m_wType;
castObject.m_DstPos.fPointX = pTarget->GetCurrentPos().m_fPointX * 100.f;
castObject.m_DstPos.fPointY = pTarget->GetCurrentPos().m_fPointY * 100.f;
castObject.m_DstPos.fPointZ = pTarget->GetCurrentPos().m_fPointZ * 100.f;
// castObject.m_cObjectType = 0;
// castObject.m_cObjectLevel = cSkillLockCount;
// castObject.m_dwTargetID = pTarget->GetCID();
// castObject.m_cObjectType = 7;
castObject.m_cObjectType = 6;
castObject.m_cObjectLevel = m_attackType.m_cSkillLockCount;
if (pThisSkill->m_bIsClassSkill) castObject.m_cObjectLevel = 0;
castObject.m_dwTargetID = pTarget->GetCID();
lpCell->SendCastObjectInfo(GetCID(), pTarget->GetCID(), castObject);
}
// 범위 마법이라면...
if (0 != pThisSkill[cSkillLockCount].m_fEffectExtent)
{
if (Skill::Target::PARTY == pThisSkill[cSkillLockCount].m_eTargetType)
{
// 파티 마법이라면
CParty* lpParty = ppDefenders[0]->GetParty();
if (NULL != lpParty)
{
return lpParty->Attack(m_attackType, ppDefenders, nDefenserJudges, this,
pThisSkill[m_attackType.m_cSkillLockCount].m_fEffectExtent, cTargetType);
}
}
else
{
// 일반 범위 마법
return CAggresiveCreature::MultiAttack(m_attackType, cDefenderNum, ppDefenders, nDefenserJudges,
GetCurrentPos(), fDir, pThisSkill[cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE, m_MonsterInfo.m_fAttackAngle, cTargetType);
}
}
// 사용하는 스킬의 종류에 따라서 Attack 호출 함수를 달리한다.
// Enchant 부여 마법의 경우는 한명에게만 사용한다.
return Attack(m_attackType, cDefenderNum, ppDefenders, nDefenserJudges, wDefenserMPHeal);
}
float CSkillMonster::CalculateDistance(CAggresiveCreature* pTarget)
{
if (NULL == pTarget) return 0;
float fSquareTargetDistance = (m_CurrentPos.m_fPointX - pTarget->GetCurrentPos().m_fPointX) *
(m_CurrentPos.m_fPointX - pTarget->GetCurrentPos().m_fPointX) +
(m_CurrentPos.m_fPointZ - pTarget->GetCurrentPos().m_fPointZ) *
(m_CurrentPos.m_fPointZ - pTarget->GetCurrentPos().m_fPointZ);
return sqrtf(fSquareTargetDistance);
}
struct CompareLevel
{
typedef std::vector<CAggresiveCreature*> TargetVector;
bool operator () (const TargetVector::value_type& lhs, const TargetVector::value_type& rhs) const
{
return lhs->GetStatus().m_nLevel > rhs->GetStatus().m_nLevel;
}
};
CAggresiveCreature* CSkillMonster::FindHighestLVMember()
{
if (NULL == m_pParty) return NULL;
// 파티 멤버를 되져본다.
CMonsterParty* lpParty = reinterpret_cast<CMonsterParty* >(m_pParty);
// 해당 몬스터를 찾아서 저장해둘 벡터
std::vector<CMonster* > targetVector;
targetVector.clear();
targetVector.reserve(10);
for (int i=0; i<lpParty->GetMemberNum(); ++ i)
{
// 멤버 몬스터 얻어온기
CMonster* lpMonster = lpParty->GetMember(i);
// 거리 체크
float fSquareTargetDistance = (m_CurrentPos.m_fPointX - lpMonster->GetCurrentPos().m_fPointX) *
(m_CurrentPos.m_fPointX - lpMonster->GetCurrentPos().m_fPointX) +
(m_CurrentPos.m_fPointZ - lpMonster->GetCurrentPos().m_fPointZ) *
(m_CurrentPos.m_fPointZ - lpMonster->GetCurrentPos().m_fPointZ);
if (fSquareTargetDistance <= CCell::CELL_DISTANCE)
{
targetVector.push_back(lpMonster);
}
}
// 한명도 없다면...
if (targetVector.size() == 0)
{
return NULL;
}
// 해당 패턴에 선택된 몬스터 중에서 최고 레벨의 몬스터를 리턴한다.
std::sort(targetVector.begin(), targetVector.end(), CompareLevel());
return targetVector[0];
}
CAggresiveCreature* CSkillMonster::FindHighestLVMember(const AtType& attackType)
{
if (NULL == m_pParty)
{
if (true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
return NULL;
}
return this;
}
// 스킬 얻기
const Skill::ProtoType* pThisSkill = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == pThisSkill)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, attackType.m_wType);
return NULL;
}
// 파티 멤버를 되져본다.
CMonsterParty* lpParty = reinterpret_cast<CMonsterParty* >(m_pParty);
// 스킬 유효 거리
float fSquareEffectDistance = (pThisSkill[attackType.m_cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE) *
(pThisSkill[attackType.m_cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE);
// 해당 몬스터를 찾아서 저장해둘 벡터
std::vector<CMonster* > targetVector;
targetVector.clear();
for (int i=0; i<lpParty->GetMemberNum(); ++ i)
{
// 멤버 몬스터 얻어온기
CMonster* lpMonster = lpParty->GetMember(i);
// NULL 이거나 이미 해당 스킬이 부여된 상태라면 다음으로..
if (NULL == lpMonster || true == lpMonster->GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
continue;
}
// 거리 체크
float fSquareTargetDistance = (m_CurrentPos.m_fPointX - lpMonster->GetCurrentPos().m_fPointX) *
(m_CurrentPos.m_fPointX - lpMonster->GetCurrentPos().m_fPointX) +
(m_CurrentPos.m_fPointZ - lpMonster->GetCurrentPos().m_fPointZ) *
(m_CurrentPos.m_fPointZ - lpMonster->GetCurrentPos().m_fPointZ);
if (fSquareTargetDistance <= fSquareEffectDistance)
{
targetVector.push_back(lpMonster);
}
}
// 한명도 없다면...
if (targetVector.size() == 0)
{
return NULL;
}
// 해당 패턴에 선택된 몬스터 중에서 최고 레벨의 몬스터를 리턴한다.
std::sort(targetVector.begin(), targetVector.end(), CompareLevel());
return targetVector[0];
}
struct CompareHP
{
typedef std::vector<CAggresiveCreature*> TargetVector;
bool operator () (const TargetVector::value_type& lhs, const TargetVector::value_type& rhs) const
{
return lhs->GetStatus().m_nNowHP < rhs->GetStatus().m_nNowHP;
}
};
CAggresiveCreature* CSkillMonster::FindLowestHPMember(const AtType& attackType, bool bRegin, float fRemainHP)
{
if (NULL == m_pParty)
{
if (GetStatus().m_StatusInfo.m_nMaxHP * fRemainHP >= GetStatus().m_nNowHP) return this;
return NULL;
}
// 스킬 얻기
const Skill::ProtoType* pThisSkill = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == pThisSkill)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, attackType.m_wType);
return NULL;
}
// 파티 멤버를 되져본다.
CMonsterParty* lpParty = reinterpret_cast<CMonsterParty* >(m_pParty);
// 스킬 유효 거리
float fSquareEffectDistance = (pThisSkill[attackType.m_cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE) *
(pThisSkill[attackType.m_cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE);
// 해당 몬스터를 찾아서 저장해둘 벡터
std::vector<CMonster* > targetVector;
targetVector.clear();
for (int i=0; i<lpParty->GetMemberNum(); ++ i)
{
// 멤버 몬스터 얻어온기
CMonster* lpMonster = lpParty->GetMember(i);
// NULL 이면 다음으로..
if (NULL == lpMonster)
{
continue;
}
// HP가 체크
if (lpMonster->GetStatus().m_StatusInfo.m_nMaxHP * fRemainHP >= lpMonster->GetStatus().m_nNowHP)
{
if (bRegin)
{
// 거리 체크
float fSquareTargetDistance = (m_CurrentPos.m_fPointX - lpMonster->GetCurrentPos().m_fPointX) *
(m_CurrentPos.m_fPointX - lpMonster->GetCurrentPos().m_fPointX) +
(m_CurrentPos.m_fPointZ - lpMonster->GetCurrentPos().m_fPointZ) *
(m_CurrentPos.m_fPointZ - lpMonster->GetCurrentPos().m_fPointZ);
if (fSquareTargetDistance > fSquareEffectDistance) continue;
}
targetVector.push_back(lpMonster);
}
}
// 한명도 없다면...
if (targetVector.size() == 0)
{
return NULL;
}
// 해당 패턴에 선택된 몬스터 중에서 최저 HP 별로 sort 한다.
std::sort(targetVector.begin(), targetVector.end(), CompareHP());
return targetVector[0];
}
CAggresiveCreature* CSkillMonster::FindEnemy(const AtType& attackType, unsigned char cType1, unsigned char cType2)
{
// 스킬 얻기
const Skill::ProtoType* pThisSkill = CSkillMgr::GetInstance().GetSkillProtoType(attackType.m_wType);
if (NULL == pThisSkill)
{
ERRLOG2(g_Log, "CID:0x%08x 존재하지 않는 스킬 아이디입니다. Skill ID:0x%04x", m_dwCID, attackType.m_wType);
return NULL;
}
// 적의 CID Set 을 만든다.
CMonsterParty::PartyTargetSet targetSet;
targetSet.clear();
if (m_pParty)
{
// 파티 멤버의 적을 Set 에 넣는다.
CMonsterParty* lpParty = reinterpret_cast<CMonsterParty* >(m_pParty);
targetSet = lpParty->GetPartyTargetSet();
}
// 원래 타겟 또한 적의 Set 에 넣는다.
if (m_lpTarget) targetSet.insert(m_lpTarget->GetCID());
// 스킬 유효 거리
float fSquareEffectDistance = (pThisSkill[attackType.m_cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE) *
(pThisSkill[attackType.m_cSkillLockCount].m_fMaxRange + Skill::ERROR_OF_DISTANCE);
CMonsterParty::PartyTargetSet::iterator pos = targetSet.begin();
CMonsterParty::PartyTargetSet::iterator end = targetSet.end();
CAggresiveCreature* lpTarget = NULL;
while (pos != end)
{
// 스킬 유효 범위 내의 적을 찾는다.
CAggresiveCreature* lpCreature = CCreatureManager::GetInstance().GetAggresiveCreature(*pos);
if (NULL == lpCreature || EnemyCheck::EC_ENEMY != IsEnemy(lpCreature))
{
++pos;
continue;
}
if (NULL == lpTarget)
{
lpTarget = lpCreature;
++pos;
continue;
}
// 패턴 타입에 맞는 타겟을 찾는다.
switch (cType1)
{
case TYPE_LV:
{
if (TYPE_HIGHEST == cType2 && lpTarget->GetStatus().m_nLevel < lpCreature->GetStatus().m_nLevel) lpTarget = lpCreature;
else if (TYPE_LOWEST == cType2 && lpTarget->GetStatus().m_nLevel > lpCreature->GetStatus().m_nLevel) lpTarget = lpCreature;
}
break;
case TYPE_HP:
{
if (TYPE_HIGHEST == cType2 && lpTarget->GetStatus().m_nNowHP < lpCreature->GetStatus().m_nNowHP) lpTarget = lpCreature;
else if (TYPE_LOWEST == cType2 && lpTarget->GetStatus().m_nNowHP > lpCreature->GetStatus().m_nNowHP) lpTarget = lpCreature;
}
break;
}
++pos;
}
return lpTarget;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (디펜더)
CDefenderMonster::CDefenderMonster()
: CSkillMonster()
{
}
CDefenderMonster::CDefenderMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
}
CDefenderMonster::~CDefenderMonster()
{
}
bool CDefenderMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel)
{
//ERRLOG1(g_Log, "CID:0x%08x 마법을 쓸수 없는 녀석입니다. 몬스터의 스킬레벨이 0 으로 설정되어 있습니다.", m_dwCID);
return false;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, MAX_PATTERN = 4 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 조건 대상 행동
// B B D : 자신에게 가장 위협적인 적이 자신으로부터 5m 이상 멀어질 때
// : 자신에게 가장 위협적인 적에게
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
if (CalculateDistance(m_lpTarget) < 5.f)
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// E A C : 자신에게 부여 마법이 걸려져 있지 않을 때
// : 자신에게
// : C 스킬 사용
// 스킬이 없거나, 이미 부여 마법이 걸려 있는 상태라면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// C C B : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 가장 낮은 HP를 가지고 있는 아군
// : B 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// A B A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (워리어)
CWarriorMonster::CWarriorMonster()
: CSkillMonster()
{
}
CWarriorMonster::CWarriorMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
}
CWarriorMonster::~CWarriorMonster()
{
}
bool CWarriorMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel)
{
//ERRLOG1(g_Log, "CID:0x%08x 마법을 쓸수 없는 녀석입니다. 몬스터의 스킬레벨이 0 으로 설정되어 있습니다.", m_dwCID);
return false;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, MAX_PATTERN = 4 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 조건 대상 행동
// B B D : 자신에게 가장 위협적인 적이 자신으로부터 5m 이상 멀어질 때
// : 자신에게 가장 위협적인 적에게
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
if (CalculateDistance(m_lpTarget) < 5.f)
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// E A C : 자신에게 부여 마법이 걸려져 있지 않을 때
// : 자신에게
// : C 스킬 사용
// 스킬이 없거나, 이미 부여 마법이 걸려 있는 상태라면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// C C B : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 가장 낮은 HP를 가지고 있는 아군
// : B 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// C B A : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 자신에게 가장 위협적인 적
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (어콜라이트)
CAcolyteMonster::CAcolyteMonster()
: CSkillMonster()
{
m_wDefaultSearchRange = ACOLYTE_SEARCH_RANGE;
}
CAcolyteMonster::CAcolyteMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
m_wDefaultSearchRange = ACOLYTE_SEARCH_RANGE;
}
CAcolyteMonster::~CAcolyteMonster()
{
}
bool CAcolyteMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel)
{
//ERRLOG1(g_Log, "CID:0x%08x 마법을 쓸수 없는 녀석입니다. 몬스터의 스킬레벨이 0 으로 설정되어 있습니다.", m_dwCID);
return false;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, MAX_PATTERN = 4 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 조건 대상 행동
// C C B : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 가장 낮은 HP를 가지고 있는 아군
// : B 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// D C D : 그룹에 속해 있는 아군중 하나라도 HP가 70% 이하가 되었을 때
// : 가장 낮은 HP를 가지고 있는 아군
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true, 0.7f);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// A B A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// A J C : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 부여 스킬이 걸려있지 않은 아군 중 가장 높은 LV
// : C 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
// 해당 기술의 거리 안에 있는 최고 레벨의 해당 몬스터를 리턴해준다.
// 해당 부여 스킬의 스펠이 이미 걸려있는 녀석을 제외하고 찾아서 넘겨준다.
ppAggresiveCreature[0] = FindHighestLVMember(attackType);
// 한명도 없다면.. 다음 패턴으로
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (메이지)
CMageMonster::CMageMonster()
: CSkillMonster()
{
m_wDefaultSearchRange = MAGE_SEARCH_RANGE;
}
CMageMonster::CMageMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
m_wDefaultSearchRange = MAGE_SEARCH_RANGE;
}
CMageMonster::~CMageMonster()
{
}
bool CMageMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel)
{
//ERRLOG1(g_Log, "CID:0x%08x 마법을 쓸수 없는 녀석입니다. 몬스터의 스킬레벨이 0 으로 설정되어 있습니다.", m_dwCID);
return false;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, MAX_PATTERN = 4 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 조건 대상 행동
// F A B : 자신의 MP가 30% 이하가 되었을 때
// : 자신에게
// : B 스킬 사용
// 스킬이 없거나, 이미 부여 마법이 걸려 있는 상태라면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL] ||
m_CreatureStatus.m_nNowMP > m_CreatureStatus.m_StatusInfo.m_nMaxMP * 0.3f)
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// E A C : 자신에게 부여 마법이 걸려져 있지 않을 때
// : 자신에게
// : C 스킬 사용
// 스킬이 없거나, 이미 부여 마법이 걸려 있는 상태라면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// A F A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 가장 낮은 HP를 가지고 있는 적
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = FindEnemy(attackType, TYPE_HP, TYPE_LOWEST);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// A F D : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 가장 낮은 HP를 가지고 있는 적
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
ppAggresiveCreature[0] = FindEnemy(attackType, TYPE_HP, TYPE_LOWEST);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (보스)
CBossMonster::CBossMonster()
: CSkillMonster()
{
m_wDefaultSearchRange = BOSS_SEARCH_RANGE;
}
CBossMonster::CBossMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
m_wDefaultSearchRange = BOSS_SEARCH_RANGE;
}
CBossMonster::~CBossMonster()
{
}
bool CBossMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel)
{
//ERRLOG1(g_Log, "CID:0x%08x 마법을 쓸수 없는 녀석입니다. 몬스터의 스킬레벨이 0 으로 설정되어 있습니다.", m_dwCID);
return false;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, FIFTH_PATTERN=4, MAX_PATTERN = 5 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 전투가 시작되면 Boss 의 챈트 스킬을 발동
// 스킬이 없거나, 이미 챈트 스킬을 발동한 상태면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[BOSS_CHANT_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cChantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = BOSS_CHANT_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[BOSS_CHANT_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// A F A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 가장 낮은 HP를 가지고 있는 적
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = FindEnemy(attackType, TYPE_HP, TYPE_LOWEST);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// D A B : 그룹에 속해 있는 아군중 하나라도 HP가 70% 이하가 되었을 때
// : 자신에게
// : B 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true, 0.7f);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = this;
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// B B C : 자신에게 가장 위협적인 적이 자신으로부터 5m 이상 멀어질 때
// : 자신에게 가장 위협적인 적에게
// : C 스킬 사용
// 스킬이 없다면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
if (CalculateDistance(m_lpTarget) < 5.f)
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case FIFTH_PATTERN:
{
// 조건 대상 행동
// C B D : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 자신에게 가장 위협적인 적
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (치프)
CChiefMonster::CChiefMonster()
: CSkillMonster()
{
m_wDefaultSearchRange = CHIEF_SEARCH_RANGE;
}
CChiefMonster::CChiefMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
m_wDefaultSearchRange = CHIEF_SEARCH_RANGE;
}
CChiefMonster::~CChiefMonster()
{
}
bool CChiefMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel)
{
//ERRLOG1(g_Log, "CID:0x%08x 마법을 쓸수 없는 녀석입니다. 몬스터의 스킬레벨이 0 으로 설정되어 있습니다.", m_dwCID);
return false;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, FIFTH_PATTERN=4, MAX_PATTERN = 5 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 전투가 시작되면 Boss 의 챈트 스킬을 발동
// 스킬이 없거나, 이미 챈트 스킬을 발동한 상태면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[BOSS_CHANT_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cChantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = BOSS_CHANT_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[BOSS_CHANT_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// E A C : 자신에게 부여 마법이 걸려져 있지 않을 때
// : 자신에게
// : C 스킬 사용
// 스킬이 없거나, 이미 부여 마법이 걸려 있는 상태라면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// C C B : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 가장 낮은 HP를 가지고 있는 아군
// : B 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// A B A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case FIFTH_PATTERN:
{
// 조건 대상 행동
// A B D : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (네임드)
CNamedMonster::CNamedMonster()
: CSkillMonster()
{
m_wDefaultSearchRange = NAMED_SEARCH_RANGE;
}
CNamedMonster::CNamedMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
m_RespawnPosition = MonsterCreate.m_Pos;
if (!bAdminCmdSummon && m_wRespawnArea > 0)
{
MonsterCreate.m_Pos.m_fPointX += static_cast<float>(Math::Random::SimpleRandom(GetTickCount(), m_wRespawnArea*2) - m_wRespawnArea);
MonsterCreate.m_Pos.m_fPointZ += static_cast<float>(Math::Random::SimpleRandom(GetTickCount(), m_wRespawnArea*2) - m_wRespawnArea);
}
m_OriginalPosition = MonsterCreate.m_Pos;
m_wDefaultSearchRange = NAMED_SEARCH_RANGE;
}
CNamedMonster::~CNamedMonster()
{
}
void CNamedMonster::Respawn(unsigned long dwTick)
{
m_OriginalPosition = m_RespawnPosition;
if(m_wRespawnArea > 0)
{
float randomValue = static_cast<float>(Math::Random::ComplexRandom(m_wRespawnArea*2));
m_OriginalPosition.m_fPointX += randomValue - m_wRespawnArea;
randomValue = static_cast<float>(Math::Random::ComplexRandom(m_wRespawnArea*2));
m_OriginalPosition.m_fPointZ += randomValue - m_wRespawnArea;
}
InitMonster(m_OriginalPosition);
m_nCurrentState = STATE_ID_NORMAL;
// 리스폰시 외치기.
CMonsterShout::GetInstance().Shout(m_dwCID, m_MonsterInfo.m_dwKID,
static_cast<unsigned short>(m_CurrentPos.m_fPointX),
static_cast<unsigned short>(m_CurrentPos.m_fPointZ),
CMonsterShout::RESPAWN, 0, 0);
// 파티에 추가
CParty* lpParty = CPartyMgr::GetInstance().GetParty(GetPID());
if (NULL != lpParty)
{
SetParty(lpParty);
lpParty->Join(GetCID(), 0, NULL, GetMapIndex());
}
}
bool CNamedMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel) return false;
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, FIFTH_PATTERN=4, MAX_PATTERN = 5 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 전투가 시작되면 Boss 의 챈트 스킬을 발동
// 스킬이 없거나, 이미 챈트 스킬을 발동한 상태면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[BOSS_CHANT_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cChantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = BOSS_CHANT_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[BOSS_CHANT_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// C B A : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 자신에게 가장 위협적인 적
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// D A B : 그룹에 속해 있는 아군중 하나라도 HP가 70% 이하가 되었을 때
// : 자신에게
// : B 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true, 0.7f);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = this;
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// E A C : 자신에게 부여 마법이 걸려져 있지 않을 때
// : 자신에게
// : C 스킬 사용
// 스킬이 없거나, 이미 부여 마법이 걸려 있는 상태라면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case FIFTH_PATTERN:
{
// 조건 대상 행동
// A B D : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
const char* szTargetName = 0;
if (0 != ppAggresiveCreature[0] &&
Creature::CT_PC == Creature::GetCreatureType(ppAggresiveCreature[0]->GetCID()))
{
szTargetName = static_cast<CCharacter*>(ppAggresiveCreature[0])->GetCharacterName();
}
// 스킬 사용시 외치기
CMonsterShout::GetInstance().Shout(m_dwCID, m_MonsterInfo.m_dwKID,
static_cast<unsigned short>(m_CurrentPos.m_fPointX),
static_cast<unsigned short>(m_CurrentPos.m_fPointZ),
CMonsterShout::SKILL_ATTACK, szTargetName, attackType.m_wType);
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}
// -------------------------------------------------------------------------------------
// 몬스터 타입 (경비병)
CGuardMonster::CGuardMonster()
: CSkillMonster()
{
m_wDefaultSearchRange = GUARD_SEARCH_RANGE;
}
CGuardMonster::CGuardMonster(MonsterCreateInfo& MonsterCreate, bool bAdminCmdSummon)
: CSkillMonster(MonsterCreate, bAdminCmdSummon)
{
}
CGuardMonster::~CGuardMonster()
{
}
void CGuardMonster::SearchPlayer()
{
// TODO : 해상도 조절을 통해 float 계산을 없애보자.
if (NULL == m_CellPos.m_lpCell)
{
ERRLOG1(g_Log, "CID:0X%08 몬스터가 셀 범위 밖에 있습니다.", m_dwCID);
return;
}
CCell* pCell = NULL;
CCharacter* pTempTarget = NULL;
CCharacter* pCurrentTarget = NULL;
const float fSquareSearchRange = (float)(m_wSearchRange * m_wSearchRange);
for (int nCellCount = 0; nCellCount < CCell::CONNECT_NUM; ++nCellCount)
{
pCell = m_CellPos.m_lpCell->GetConnectCell(nCellCount);
if (NULL == pCell || false == pCell->IsCharacter())
{
continue;
}
pTempTarget = pCell->GetFirstCharacter();
while (NULL != pTempTarget)
{
if (pTempTarget->GetStatus().m_nNowHP > 0 &&
false == pTempTarget->IsRideArms() &&
EnemyCheck::EC_ENEMY == IsEnemy(pTempTarget))
{
if (false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Stealth) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Invincible) &&
false == pTempTarget->GetEnchantInfo().GetFlag(Skill::SpellID::Hide))
{
const float fDX = pTempTarget->GetCurrentPos().m_fPointX - GetCurrentPos().m_fPointX;
const float fDZ = pTempTarget->GetCurrentPos().m_fPointZ - GetCurrentPos().m_fPointZ;
const float fDistance = (fDX * fDX) + (fDZ * fDZ);
if (fDistance < fSquareSearchRange)
{
pCurrentTarget = pTempTarget;
break;
}
}
}
pTempTarget = pCell->GetNextCharacter();
}
}
if (NULL != pCurrentTarget)
{
m_Threat.AddToThreatList(pCurrentTarget, 1);
m_nCurrentState = CFSM::GetInstance().StateTransition(m_nCurrentState, INPUT_ID_SEEN_PLAYER);
}
}
bool CGuardMonster::SkillAttack(void)
{
unsigned char cSkillLockCount = m_MonsterInfo.m_cSkillLevel / CSkillMgr::MAX_SKILL_LEVEL;
unsigned char cSkillLevel = m_MonsterInfo.m_cSkillLevel % CSkillMgr::MAX_SKILL_LEVEL;
if (cSkillLockCount >= CSkillMgr::MAX_SKILL_LOCKCOUNT - 1)
{
cSkillLockCount = CSkillMgr::MAX_SKILL_LOCKCOUNT - 2;
}
if (cSkillLockCount > 0 && cSkillLevel < CSkillMonster::USE_SKILL_MIN_LEVEL)
{
--cSkillLockCount;
cSkillLevel = CSkillMonster::USE_SKILL_LEVEL;
}
// 마법을 쓸수 없는.. m_nSkillLevel 이 0 인 녀석이다.
if (0 == cSkillLockCount && 0 == cSkillLevel)
{
//ERRLOG1(g_Log, "CID:0x%08x 마법을 쓸수 없는 녀석입니다. 몬스터의 스킬레벨이 0 으로 설정되어 있습니다.", m_dwCID);
return false;
}
enum { FIRST_PATTERN=0, SECOND_PATTERN=1, THIRD_PATTERN=2, FOURTH_PATTERN=3, MAX_PATTERN = 4 };
int nSelectPattern = FIRST_PATTERN;
CAggresiveCreature* ppAggresiveCreature[AtNode::MAX_DEFENDER_NUM] = {0, };
AtType attackType;
attackType.m_cSkillLockCount = cSkillLockCount;
attackType.m_cSkillLevel = cSkillLevel;
char nSkillPattern = 0;
while (nSelectPattern < MAX_PATTERN)
{
switch (nSelectPattern)
{
case FIRST_PATTERN:
{
// 조건 대상 행동
// B B D : 자신에게 가장 위협적인 적이 자신으로부터 5m 이상 멀어질 때
// : 자신에게 가장 위협적인 적에게
// : D 스킬 사용
// 스킬이 없다면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[D_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = D_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[D_SKILL];
if (CalculateDistance(m_lpTarget) < 5.f)
{
++nSelectPattern;
continue;
}
ppAggresiveCreature[0] = m_lpTarget;
}
break;
case SECOND_PATTERN:
{
// 조건 대상 행동
// E A C : 자신에게 부여 마법이 걸려져 있지 않을 때
// : 자신에게
// : C 스킬 사용
// 스킬이 없거나, 이미 부여 마법이 걸려 있는 상태라면.. 다음 패턴으로 넘긴다.
if (0 == m_MonsterInfo.m_wSkillID[C_SKILL] ||
true == GetEnchantInfo().GetFlag(m_MonsterInfo.m_cEnchantSpellType))
{
++nSelectPattern;
continue;
}
nSkillPattern = C_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[C_SKILL];
ppAggresiveCreature[0] = this;
}
break;
case THIRD_PATTERN:
{
// 조건 대상 행동
// C C B : 그룹에 속해있는 아군중 하나라도 HP가 50% 이하가 되었을 때
// : 가장 낮은 HP를 가지고 있는 아군
// : B 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[B_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = B_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[B_SKILL];
ppAggresiveCreature[0] = FindLowestHPMember(attackType, true);
if (NULL == ppAggresiveCreature[0])
{
++nSelectPattern;
continue;
}
}
break;
case FOURTH_PATTERN:
{
// 조건 대상 행동
// A B A : 스킬 사용 가능 MP 잔여량 있음 / 대상이 스킬 사용 가능 범위에 있을 때
// : 자신에게 가장 위협적인 적에게
// : A 스킬 사용
// 스킬이 없다면.. 다음 패턴으로..
if (0 == m_MonsterInfo.m_wSkillID[A_SKILL])
{
++nSelectPattern;
continue;
}
nSkillPattern = A_SKILL;
attackType.m_wType = m_MonsterInfo.m_wSkillID[A_SKILL];
ppAggresiveCreature[0] = m_lpTarget;
}
break;
default:
{
ERRLOG1(g_Log, "CID:0x%08x 없는 패턴이 넘어왔습니다.", m_dwCID);
return false;
}
}
// 스킬 사용 (캐스팅 타입인 경우에는 사용할수 있는지만 체크하게 된다.)
if (true == UseSkill(attackType, ppAggresiveCreature, nSkillPattern))
{
return true;
}
else
{
++nSelectPattern;
}
}
return false;
}