4253 lines
103 KiB
C++
4253 lines
103 KiB
C++
#include "stdafx.h"
|
||
#include "Region.h"
|
||
#include "Extern.h"
|
||
#include "Npc.h"
|
||
#include "User.h"
|
||
#include "NpcThread.h"
|
||
|
||
// 1m
|
||
//float surround_fx[8] = {0.0f, -0.7071f, -1.0f, -0.7083f, 0.0f, 0.7059f, 1.0000f, 0.7083f};
|
||
//float surround_fz[8] = {1.0f, 0.7071f, 0.0f, -0.7059f, -1.0f, -0.7083f, -0.0017f, 0.7059f};
|
||
// 2m
|
||
static float surround_fx[8] = {0.0f, -1.4142f, -2.0f, -1.4167f, 0.0f, 1.4117f, 2.0000f, 1.4167f};
|
||
static float surround_fz[8] = {2.0f, 1.4142f, 0.0f, -1.4167f, -2.0f, -1.4167f, -0.0035f, 1.4117f};
|
||
|
||
enum
|
||
{
|
||
TENDER_ATTACK_TYPE = 0, // The spawn is passive, i.e. won't attack until it's attacked, or it expects same-type spawns to help out.
|
||
ATROCITY_ATTACK_TYPE = 1 // The spawn is aggressive, i.e. it's just as happy to attack you first.
|
||
};
|
||
|
||
#define NO_ACTION 0
|
||
#define ATTACK_TO_TRACE 1 // °ø°İ¿¡¼ Ãß°İ
|
||
#define LONG_ATTACK_RANGE 15 // Àå°Å¸® °ø°İ À¯È¿°Å¸®
|
||
#define SHORT_ATTACK_RANGE 1 // Á÷Á¢°ø°İ À¯È¿°Å¸®
|
||
|
||
#define ARROW_MIN 291010000
|
||
#define ARROW_MAX 292010000
|
||
|
||
#define FAINTING_TIME 2 // in seconds
|
||
|
||
bool CNpc::RegisterRegion(float x, float z)
|
||
{
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr)
|
||
{
|
||
TRACE("#### Npc-SetUid Zone Fail : [name=%s], zone=%d #####\n", GetName().c_str(), GetZoneID());
|
||
return false;
|
||
}
|
||
|
||
int x1 = (int)x / TILE_SIZE;
|
||
int z1 = (int)z / TILE_SIZE;
|
||
int nRX = (int)x / VIEW_DIST;
|
||
int nRY = (int)z / VIEW_DIST;
|
||
|
||
if (x1 < 0 || z1 < 0 || x1 >= pMap->GetMapSize() || z1 >= pMap->GetMapSize())
|
||
{
|
||
TRACE("#### SetUid failed : [nid=%d, sid=%d, zone=%d], coordinates out of range of map x=%d, z=%d, map size=%d #####\n",
|
||
GetID(), GetProtoID(), GetZoneID(), x1, z1, pMap->GetMapSize());
|
||
return false;
|
||
}
|
||
|
||
// if(pMap->m_pMap[x1][z1].m_sEvent == 0) return false;
|
||
if(nRX > pMap->GetXRegionMax() || nRY > pMap->GetZRegionMax() || nRX < 0 || nRY < 0)
|
||
{
|
||
TRACE("#### SetUid Fail : [nid=%d, sid=%d], nRX=%d, nRZ=%d #####\n", GetID(), GetProtoID(), nRX, nRY);
|
||
return false;
|
||
}
|
||
|
||
if (GetRegionX() != nRX || GetRegionZ() != nRY)
|
||
{
|
||
int nOld_RX = GetRegionX();
|
||
int nOld_RZ = GetRegionZ();
|
||
|
||
m_sRegionX = nRX;
|
||
m_sRegionZ = nRY;
|
||
|
||
pMap->RegionNpcAdd(GetRegionX(), GetRegionZ(), GetID());
|
||
pMap->RegionNpcRemove(nOld_RX, nOld_RZ, GetID());
|
||
|
||
SendRegionUpdate();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
void CNpc::SendInOut(InOutType type)
|
||
{
|
||
Packet result(AG_NPC_INOUT);
|
||
result << uint8(type) << GetID() << GetX() << GetZ() << GetY();
|
||
g_pMain->Send(&result);
|
||
}
|
||
|
||
void CNpc::SendNpcInfo()
|
||
{
|
||
Packet result(AG_NPC_INFO);
|
||
result.SByte();
|
||
FillNpcInfo(result);
|
||
g_pMain->Send(&result);
|
||
}
|
||
|
||
/**
|
||
* @brief Sends a region update packet to the game server
|
||
* to indicate the NPC has changed regions, so it should
|
||
* handle showing/removing the NPCs from applicable players.
|
||
*/
|
||
void CNpc::SendRegionUpdate()
|
||
{
|
||
Packet result(AG_NPC_REGION_UPDATE);
|
||
result << GetID() << GetX() << GetY() << GetZ();
|
||
g_pMain->Send(&result);
|
||
}
|
||
|
||
CNpc::CNpc() : Unit(UnitNPC), m_bDelete(false),
|
||
m_NpcState(NPC_LIVE), m_OldNpcState(m_NpcState), m_byGateOpen(false), m_byObjectType(NORMAL_OBJECT), m_byPathCount(0),
|
||
m_byAttackPos(0), m_ItemUserLevel(0), m_Delay(0), m_nActiveSkillID(0), m_sActiveTargetID(-1), m_sActiveCastTime(0),
|
||
m_byDirection(0),m_bIsEventNpc(false),
|
||
m_proto(nullptr), m_pPath(nullptr)
|
||
{
|
||
InitTarget();
|
||
|
||
m_fDelayTime = getMSTime();
|
||
LastChangeTimeCC = UNIXTIME + 60;
|
||
m_tNpcAttType = ATROCITY_ATTACK_TYPE;
|
||
m_bHasFriends = false; // :'(
|
||
m_byMoveType = 1;
|
||
m_byInitMoveType = 1;
|
||
m_byRegenType = 0;
|
||
m_byDungeonFamily = 0;
|
||
m_bySpecialType = NpcSpecialTypeNone;
|
||
m_byTrapNumber = 0;
|
||
m_byChangeType = 0;
|
||
m_byDeadType = 0;
|
||
m_sMaxPathCount = 0;
|
||
m_sRealPathCount = 0;
|
||
|
||
nIsPet = false;
|
||
strPetName = "";
|
||
strUserName = "";
|
||
nSerial = 0;
|
||
UserId = -1;
|
||
m_bFirstLive = true;
|
||
|
||
m_fHPChangeTime = getMSTime();
|
||
m_tFaintingTime = 0;
|
||
|
||
m_nLimitMinX = m_nLimitMinZ = 0;
|
||
m_nLimitMaxX = m_nLimitMaxZ = 0;
|
||
m_bIsEventNpc = false;
|
||
m_fSecForRealMoveMetor = 0.0f;
|
||
InitUserList();
|
||
|
||
m_bTracing = false;
|
||
m_fTracingStartX = m_fTracingStartZ = 0.0f;
|
||
|
||
for(int i=0; i<NPC_MAX_PATH_LIST; i++) {
|
||
m_PathList.pPattenPos[i].x = -1;
|
||
m_PathList.pPattenPos[i].z = -1;
|
||
}
|
||
m_pPattenPos.x = m_pPattenPos.z = 0;
|
||
|
||
m_bMonster = false;
|
||
|
||
m_oSocketID = -1;
|
||
m_bEventRoom = 0;
|
||
UnixGateOpen = 0;
|
||
UnixGateClose = 0;
|
||
}
|
||
|
||
CNpc::~CNpc()
|
||
{
|
||
ClearPathFindData();
|
||
InitUserList();
|
||
}
|
||
|
||
void CNpc::ClearPathFindData()
|
||
{
|
||
m_bPathFlag = false;
|
||
m_sStepCount = 0;
|
||
m_iAniFrameCount = 0;
|
||
m_iAniFrameIndex = 0;
|
||
m_fAdd_x = m_fAdd_z = 0.0f;
|
||
|
||
for (int i = 0; i < MAX_PATH_LINE; i++)
|
||
{
|
||
m_pPoint[i].byType = 0;
|
||
m_pPoint[i].bySpeed = 0;
|
||
m_pPoint[i].fXPos = -1.0f;
|
||
m_pPoint[i].fZPos = -1.0f;
|
||
}
|
||
}
|
||
|
||
void CNpc::InitUserList()
|
||
{
|
||
m_sMaxDamageUserid = -1;
|
||
m_TotalDamage = 0;
|
||
for (int i = 0; i < NPC_HAVE_USER_LIST; i++)
|
||
m_DamagedUserList[i].Reset();
|
||
m_DamagedUserListCount = 0;
|
||
}
|
||
|
||
void CNpc::InitTarget()
|
||
{
|
||
if (m_byAttackPos != 0)
|
||
{
|
||
if (hasTarget() && m_Target.id < NPC_BAND)
|
||
{
|
||
CUser* pUser = g_pMain->GetUserPtr(m_Target.id);
|
||
if (pUser != nullptr
|
||
&& m_byAttackPos > 0 && m_byAttackPos < 9)
|
||
pUser->m_sSurroundNpcNumber[m_byAttackPos - 1] = -1;
|
||
}
|
||
}
|
||
|
||
m_byAttackPos = 0;
|
||
m_Target.id = -1;
|
||
m_Target.bSet = false;
|
||
m_Target.x = m_Target.y = m_Target.z = 0.0f;
|
||
m_bTracing = false;
|
||
}
|
||
|
||
void CNpc::Init()
|
||
{
|
||
m_pMap = g_pMain->GetZoneByID(GetZoneID());
|
||
m_Delay = 0;
|
||
m_fDelayTime = getMSTime();
|
||
|
||
if (GetMap() == nullptr)
|
||
{
|
||
TRACE("#### Npc-Init Zone Fail : [name=%s], zone=%d #####\n", GetName().c_str(), GetZoneID());
|
||
return;
|
||
}
|
||
}
|
||
|
||
void CNpc::InitPos()
|
||
{
|
||
static const float fDD = 1.5f;
|
||
static const float fx[4][5] =
|
||
{ // battle pos
|
||
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, // 0
|
||
{ 0.0f, -(fDD*2), -(fDD*2), -(fDD*4), -(fDD*4) }, // 1
|
||
{ 0.0f, 0.0f, -(fDD*2), -(fDD*2), -(fDD*2) }, // 2
|
||
{ 0.0f, -(fDD*2), -(fDD*2), -(fDD*2), -(fDD*4) } // 3
|
||
};
|
||
|
||
static const float fz[4][5] =
|
||
{ // battle pos
|
||
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f }, // 0
|
||
{ 0.0f, (fDD*1), -(fDD*1), (fDD*1), -(fDD*1) }, // 1
|
||
{ 0.0f, -(fDD*2), (fDD*1), (fDD*1), (fDD*3) }, // 2
|
||
{ 0.0f, (fDD*2), 0.0f, -(fDD*2), 0.0f } // 3
|
||
};
|
||
|
||
if (m_byBattlePos > 3)
|
||
return;
|
||
|
||
m_fBattlePos_x = fx[m_byBattlePos][m_byPathCount - 1];
|
||
m_fBattlePos_z = fz[m_byBattlePos][m_byPathCount - 1];
|
||
}
|
||
|
||
void CNpc::Load(uint16 sNpcID, CNpcTable * proto, bool bMonster, uint8 nation)
|
||
{
|
||
|
||
m_sNid = sNpcID + NPC_BAND;
|
||
|
||
m_proto = proto;
|
||
|
||
m_bMonster = bMonster;
|
||
|
||
m_sSize = proto->m_sSize;
|
||
m_iWeapon_1 = proto->m_iWeapon_1;
|
||
m_iWeapon_2 = proto->m_iWeapon_2;
|
||
m_bNation = nation == 0 ? proto->m_byGroup : nation;
|
||
m_bLevel = (uint8) proto->m_sLevel; // max level used that I know about is 250, no need for 2 bytes
|
||
|
||
// Monsters cannot, by design, be friendly to everybody.
|
||
if (isMonster() && GetNation() == Nation::ALL)
|
||
m_bNation = Nation::NONE;
|
||
|
||
m_byActType = proto->m_byActType;
|
||
m_byRank = proto->m_byRank;
|
||
m_byTitle = proto->m_byTitle;
|
||
m_iSellingGroup = proto->m_iSellingGroup;
|
||
m_iHP = proto->m_iMaxHP;
|
||
m_iMaxHP = proto->m_iMaxHP;
|
||
m_sMP = proto->m_sMaxMP;
|
||
m_sMaxMP = proto->m_sMaxMP;
|
||
m_sAttack = proto->m_sAttack;
|
||
m_sTotalAc = proto->m_sDefense;
|
||
m_fTotalHitrate = proto->m_sHitRate;
|
||
m_fTotalEvasionrate = proto->m_sEvadeRate;
|
||
m_sTotalHit = proto->m_sDamage;
|
||
m_sAttackDelay = proto->m_sAttackDelay;
|
||
m_sSpeed = proto->m_sSpeed;
|
||
|
||
// Object NPCs should have an effective speed of 1x (not that it should matter, mind)
|
||
if (m_byObjectType == SPECIAL_OBJECT)
|
||
m_sSpeed = 1000;
|
||
|
||
m_fSpeed_1 = (float)proto->m_bySpeed_1 * ((float)m_sSpeed / 1000);
|
||
m_fSpeed_2 = (float)proto->m_bySpeed_2 * ((float)m_sSpeed / 1000);
|
||
m_fOldSpeed_1 = (float)proto->m_bySpeed_1 * ((float)m_sSpeed / 1000);
|
||
m_fOldSpeed_2 = (float)proto->m_bySpeed_2 * ((float)m_sSpeed / 1000);
|
||
|
||
m_fSecForMetor = 4.0f;
|
||
m_sStandTime = proto->m_sStandTime;
|
||
m_sFireR = proto->m_byFireR;
|
||
m_sColdR = proto->m_byColdR;
|
||
m_sLightningR = proto->m_byLightningR;
|
||
m_sMagicR = proto->m_byMagicR;
|
||
m_sDiseaseR = proto->m_byDiseaseR;
|
||
m_sPoisonR = proto->m_byPoisonR;
|
||
m_bySearchRange = proto->m_bySearchRange;
|
||
m_byAttackRange = proto->m_byAttackRange;
|
||
m_byTracingRange = proto->m_byTracingRange;
|
||
m_iMoney = proto->m_iMoney;
|
||
m_iItem = proto->m_iItem;
|
||
|
||
m_sRegenTime = 160 * MINUTE;
|
||
m_sMaxPathCount = 0;
|
||
|
||
if(GetType() == OBJECT_ARTIFACT && proto->m_sSid == 541)
|
||
m_sRegenTime = 1;
|
||
|
||
m_pMap = g_pMain->GetZoneByID(GetZoneID());
|
||
m_bFirstLive = 1;
|
||
}
|
||
|
||
void CNpc::SendMoveResult(float fX, float fY, float fZ, float fSpeed /*= 0.0f*/)
|
||
{
|
||
/*Packet result(MOVE_RESULT, uint8(SUCCESS));
|
||
result << GetID() << fX << fZ << fY << fSpeed;
|
||
g_pMain->Send(&result);
|
||
RegisterRegion(fX, fZ);*/
|
||
Packet result(MOVE_RESULT, uint8(SUCCESS));
|
||
result << GetID() << fX << fZ << fY;
|
||
if(m_bIceSpeedAmount > 0)
|
||
result << fSpeed / float(m_bIceSpeedAmount);
|
||
else
|
||
result << fSpeed ;
|
||
g_pMain->Send(&result);
|
||
RegisterRegion(fX, fZ);
|
||
}
|
||
|
||
time_t CNpc::NpcLive()
|
||
{
|
||
// Dungeon Work
|
||
if (GetZoneID() == ZONE_DELOS
|
||
&& GetProto()->m_sSid != 541
|
||
&& g_pMain->CSWOpen)
|
||
return false;
|
||
|
||
if (m_byRegenType == 2 || (m_byRegenType == 1 && m_byChangeType == 100))
|
||
{
|
||
m_NpcState = NPC_LIVE;
|
||
return m_sRegenTime;
|
||
}
|
||
|
||
if(GetProtoID() == CHAOS_CUBE_SSID)
|
||
LastChangeTimeCC = UNIXTIME;
|
||
|
||
m_NpcState = SetLive() ? NPC_STANDING : NPC_LIVE;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
time_t CNpc::NpcTracing()
|
||
{
|
||
if (m_sStepCount != 0)
|
||
{
|
||
if (m_fPrevX < 0 || m_fPrevZ < 0)
|
||
{
|
||
TRACE("### Npc-NpcTracing Fail : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), m_fPrevX, m_fPrevZ);
|
||
}
|
||
else
|
||
{
|
||
m_curx = m_fPrevX;
|
||
m_curz = m_fPrevZ;
|
||
}
|
||
}
|
||
|
||
if (isNonAttackingObject())
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;
|
||
}
|
||
|
||
// Prevent spawns like Guard Towers from following
|
||
// targets while attacking.
|
||
if (m_byMoveType == 4 || m_byMoveType == 5)
|
||
{
|
||
m_NpcState = NPC_FIGHTING;
|
||
return 0;
|
||
}
|
||
|
||
auto result = IsCloseTarget(m_byAttackRange, AttackTypePhysical);
|
||
if (result == CloseTargetInGeneralRange)
|
||
{
|
||
NpcMoveEnd();
|
||
m_NpcState = NPC_FIGHTING;
|
||
return 0;
|
||
}
|
||
else if (result == CloseTargetInvalid)
|
||
{
|
||
InitTarget();
|
||
NpcMoveEnd();
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;
|
||
}
|
||
else if (result == CloseTargetInAttackRange && GetProto()->m_byDirectAttack == 2)
|
||
{
|
||
NpcMoveEnd();
|
||
m_NpcState = NPC_FIGHTING;
|
||
return 0;
|
||
}
|
||
|
||
if (m_byActionFlag == ATTACK_TO_TRACE)
|
||
{
|
||
m_byActionFlag = NO_ACTION;
|
||
m_bStopFollowingTarget = true;
|
||
|
||
// If we're not already following a user, define our start coords.
|
||
if (!m_bTracing)
|
||
{
|
||
m_fTracingStartX = GetX();
|
||
m_fTracingStartZ = GetZ();
|
||
m_bTracing = true;
|
||
}
|
||
}
|
||
|
||
if (m_bStopFollowingTarget)
|
||
{
|
||
if (!ResetPath())// && !m_tNpcTraceType)
|
||
{
|
||
TRACE("##### NpcTracing Fail : ÆĞ½ºÆÄÀÎµå ½ÇÆĞ , NPC_STANDINGÀ¸·Î ######\n");
|
||
InitTarget();
|
||
NpcMoveEnd(); // À̵¿ ³¡..
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
if ( (!m_bPathFlag && !StepMove())
|
||
|| (m_bPathFlag && !StepNoPathMove()))
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
TRACE("### NpcTracing Fail : StepMove ½ÇÆĞ, %s, %d ### \n", GetName().c_str(), GetID());
|
||
return 0;
|
||
}
|
||
|
||
if (!IsMovingEnd())
|
||
SendMoveResult(GetX(), GetY(), GetZ(), (float)m_sSpeed / 1000);
|
||
else
|
||
{
|
||
m_curx = m_fPrevX;
|
||
m_curz = m_fPrevZ;
|
||
SendMoveResult(m_fPrevX, m_fPrevY, m_fPrevZ, (float)m_sSpeed / 1000);
|
||
}
|
||
if (result == CloseTargetInAttackRange
|
||
&& GetProto()->m_byDirectAttack == 0
|
||
&& !isHealer())
|
||
TracingAttack();
|
||
|
||
return m_sSpeed;
|
||
}
|
||
|
||
time_t CNpc::NpcAttacking()
|
||
{
|
||
if (isDead())
|
||
{
|
||
Dead();
|
||
return -1;
|
||
}
|
||
|
||
if (isNonAttackingObject())
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;
|
||
}
|
||
|
||
auto result = IsCloseTarget(m_byAttackRange, AttackTypeNone);
|
||
|
||
|
||
if (result == CloseTargetInGeneralRange)
|
||
{
|
||
m_NpcState = NPC_FIGHTING;
|
||
return m_sAttackDelay;
|
||
}
|
||
|
||
int nValue = GetTargetPath();
|
||
if (nValue == -1)
|
||
{
|
||
if (!RandomMove())
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
InitTarget();
|
||
m_iAniFrameCount = 0;
|
||
m_NpcState = NPC_MOVING;
|
||
return 0;
|
||
}
|
||
else if (nValue == 0)
|
||
{
|
||
m_fSecForMetor = m_fSpeed_2;
|
||
IsNoPathFind(m_fSecForMetor);
|
||
}
|
||
|
||
m_NpcState = NPC_TRACING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
time_t CNpc::NpcMoving()
|
||
{
|
||
if (isDead())
|
||
{
|
||
Dead();
|
||
return -1;
|
||
}
|
||
|
||
if (m_sStepCount != 0)
|
||
{
|
||
if (m_fPrevX < 0 || m_fPrevZ < 0)
|
||
{
|
||
TRACE("### Npc-Moving Fail : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), m_fPrevX, m_fPrevZ);
|
||
}
|
||
else
|
||
{
|
||
m_curx = m_fPrevX;
|
||
m_curz = m_fPrevZ;
|
||
}
|
||
}
|
||
|
||
if (FindEnemy())
|
||
{
|
||
NpcMoveEnd();
|
||
m_NpcState = NPC_ATTACKING;
|
||
return 0;
|
||
}
|
||
|
||
if (IsMovingEnd())
|
||
{
|
||
m_curx = m_fPrevX;
|
||
m_curz = m_fPrevZ;
|
||
if (GetX() < 0 || GetZ() < 0)
|
||
TRACE("Npc-NpcMoving-2 : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), GetX(), GetZ());
|
||
|
||
m_NpcState = NPC_STANDING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
if ( (!m_bPathFlag && !StepMove())
|
||
|| (m_bPathFlag && !StepNoPathMove()))
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;
|
||
}
|
||
|
||
SendMoveResult(m_fPrevX, m_fPrevY, m_fPrevZ, (float)m_sSpeed / 1000);
|
||
return m_sSpeed;
|
||
}
|
||
void CNpc::ChaosCubeControl()
|
||
{
|
||
LastChangeTimeCC = UNIXTIME;
|
||
uint16 mrand,randx,randz;
|
||
mrand = myrand(1,50);
|
||
|
||
if(m_byGateOpen)
|
||
SendInOut(INOUT_OUT);
|
||
else
|
||
{
|
||
if(mrand > 30)
|
||
{
|
||
randx = myrand(64,187);
|
||
randz = myrand(107,156);
|
||
}
|
||
else
|
||
{
|
||
randx = myrand(100,150);
|
||
randz = myrand(95,195);
|
||
}
|
||
|
||
m_curx = float(uint16(randx));
|
||
m_curz = float(uint16(randz));
|
||
|
||
SendMoveResult(GetX(), GetY(), GetZ());
|
||
SendInOut(INOUT_IN);
|
||
|
||
}
|
||
|
||
m_byGateOpen = !m_byGateOpen;
|
||
}
|
||
|
||
time_t CNpc::NpcStanding()
|
||
{
|
||
/* if (g_pMain->m_bIsNight)
|
||
{
|
||
m_NpcState = NPC_SLEEPING;
|
||
return 0;
|
||
} */
|
||
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr)
|
||
{
|
||
TRACE("### NpcStanding Zone Index Error : nid=%d, name=%s, zone=%d ###\n", GetID(), GetName().c_str(), GetZoneID());
|
||
return -1;
|
||
}
|
||
|
||
CRoomEvent * pRoom = pMap->m_arRoomEventArray.GetData(m_byDungeonFamily);
|
||
if (pRoom != nullptr
|
||
&& pRoom->m_byStatus == 1)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
if (RandomMove())
|
||
{
|
||
|
||
m_iAniFrameCount = 0;
|
||
m_NpcState = NPC_MOVING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
m_NpcState = NPC_STANDING;
|
||
|
||
if(GetProtoID() == CHAOS_CUBE_SSID
|
||
&& LastChangeTimeCC + 30 < UNIXTIME)
|
||
ChaosCubeControl();
|
||
|
||
if (GetType() == NPC_SPECIAL_GATE
|
||
&& g_pMain->m_byBattleEvent == BATTLEZONE_OPEN
|
||
&& GetZoneID() != ZONE_DELOS)
|
||
{
|
||
if(m_byGateOpen && UnixGateClose == 30)
|
||
{
|
||
m_byGateOpen = !m_byGateOpen;
|
||
Packet result(AG_NPC_GATE_OPEN);
|
||
result << GetID() << GetProtoID() << m_byGateOpen;
|
||
g_pMain->Send(&result);
|
||
UnixGateOpen = 0;
|
||
}
|
||
else if (!m_byGateOpen && UnixGateOpen == 180)
|
||
{
|
||
m_byGateOpen = !m_byGateOpen;
|
||
Packet result(AG_NPC_GATE_OPEN);
|
||
result << GetID() << GetProtoID() << m_byGateOpen;
|
||
g_pMain->Send(&result);
|
||
UnixGateClose = 0;
|
||
}
|
||
UnixGateClose++;
|
||
UnixGateOpen++;
|
||
}
|
||
return m_sStandTime;
|
||
}
|
||
|
||
time_t CNpc::NpcBack()
|
||
{
|
||
if (isDead())
|
||
{
|
||
Dead();
|
||
return -1;
|
||
}
|
||
|
||
if (hasTarget()
|
||
&& g_pMain->GetUnitPtr(m_Target.id) == nullptr)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;//STEP_DELAY;
|
||
}
|
||
|
||
if (m_sStepCount != 0)
|
||
{
|
||
if (m_fPrevX < 0 || m_fPrevZ < 0)
|
||
{
|
||
TRACE("### Npc-NpcBack Fail-1 : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), m_fPrevX, m_fPrevZ);
|
||
}
|
||
else
|
||
{
|
||
m_curx = m_fPrevX;
|
||
m_curz = m_fPrevZ;
|
||
}
|
||
}
|
||
|
||
if (IsMovingEnd())
|
||
{
|
||
m_curx = m_fPrevX;
|
||
m_curz = m_fPrevZ;
|
||
|
||
if (GetX() < 0 || GetZ() < 0)
|
||
TRACE("Npc-NpcBack-2 : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), GetX(), GetZ());
|
||
|
||
SendMoveResult(GetX(), GetY(), GetZ());
|
||
m_NpcState = NPC_STANDING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
if ( (!m_bPathFlag && !StepMove())
|
||
|| (m_bPathFlag && !StepNoPathMove()))
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
SendMoveResult(m_fPrevX, m_fPrevY, m_fPrevZ, (float)m_sSpeed / 1000);
|
||
return m_sSpeed;
|
||
}
|
||
|
||
bool CNpc::SetLive()
|
||
{
|
||
/* Kontrolu buraya koyucaz csw icin*/
|
||
if (GetZoneID() == ZONE_DELOS
|
||
&& GetProto()->m_sSid != 541
|
||
&& g_pMain->CSWOpen)
|
||
return false;
|
||
|
||
int i = 0, j = 0;
|
||
m_iHP = m_iMaxHP;
|
||
m_sMP = m_sMaxMP;
|
||
m_iPattenFrame = 0;
|
||
m_bStopFollowingTarget = false;
|
||
m_byActionFlag = NO_ACTION;
|
||
m_byMaxDamagedNation = 0;
|
||
|
||
m_sRegionX = m_sRegionZ = -1;
|
||
m_fAdd_x = 0.0f; m_fAdd_z = 0.0f;
|
||
m_fStartPoint_X = 0.0f; m_fStartPoint_Y = 0.0f;
|
||
m_fEndPoint_X = 0.0f; m_fEndPoint_Y = 0.0f;
|
||
m_min_x = m_min_y = m_max_x = m_max_y = 0;
|
||
|
||
InitTarget();
|
||
ClearPathFindData();
|
||
InitUserList();
|
||
//InitPos();
|
||
|
||
CNpc* pNpc = nullptr;
|
||
|
||
if (m_bIsEventNpc && !m_bFirstLive)
|
||
{
|
||
m_bDelete = true;
|
||
return true;
|
||
}
|
||
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr)
|
||
return false;
|
||
|
||
if (m_bFirstLive)
|
||
{
|
||
m_nInitX = m_fPrevX = GetX();
|
||
m_nInitY = m_fPrevY = GetY();
|
||
m_nInitZ = m_fPrevZ = GetZ();
|
||
}
|
||
|
||
if (GetX() < 0 || GetZ() < 0)
|
||
TRACE("Npc-SetLive-1 : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), GetX(), GetZ());
|
||
|
||
int dest_x = (int)m_nInitX / TILE_SIZE;
|
||
int dest_z = (int)m_nInitZ / TILE_SIZE;
|
||
|
||
bool bMove = pMap->IsMovable(dest_x, dest_z);
|
||
|
||
if (GetType() != NPCTYPE_MONSTER /*|| m_bIsEventNpc*/)
|
||
{
|
||
m_curx = m_fPrevX = m_nInitX;
|
||
m_cury = m_fPrevY = m_nInitY;
|
||
m_curz = m_fPrevZ = m_nInitZ;
|
||
}
|
||
else
|
||
{
|
||
int nX = 0;
|
||
int nZ = 0;
|
||
int nTileX = 0;
|
||
int nTileZ = 0;
|
||
int nRandom = 0;
|
||
|
||
while (1)
|
||
{
|
||
i++;
|
||
nRandom = abs(m_nInitMinX - m_nInitMaxX);
|
||
if (nRandom <= 1)
|
||
nX = m_nInitMinX;
|
||
else
|
||
{
|
||
if (m_nInitMinX < m_nInitMaxX)
|
||
nX = myrand(m_nInitMinX, m_nInitMaxX);
|
||
else
|
||
nX = myrand(m_nInitMaxX, m_nInitMinX);
|
||
}
|
||
nRandom = abs(m_nInitMinY - m_nInitMaxY);
|
||
if (nRandom <= 1)
|
||
nZ = m_nInitMinY;
|
||
else
|
||
{
|
||
if (m_nInitMinY < m_nInitMaxY)
|
||
nZ = myrand(m_nInitMinY, m_nInitMaxY);
|
||
else
|
||
nZ = myrand(m_nInitMaxY, m_nInitMinY);
|
||
}
|
||
|
||
nTileX = nX / TILE_SIZE;
|
||
nTileZ = nZ / TILE_SIZE;
|
||
|
||
if (nTileX >= pMap->GetMapSize())
|
||
nTileX = pMap->GetMapSize();
|
||
if (nTileZ >= pMap->GetMapSize())
|
||
nTileZ = pMap->GetMapSize();
|
||
|
||
if (nTileX < 0 || nTileZ < 0)
|
||
{
|
||
TRACE("#### Npc-SetLive() Fail : nTileX=%d, nTileZ=%d #####\n", nTileX, nTileZ);
|
||
return false;
|
||
}
|
||
|
||
m_nInitX = m_fPrevX = m_curx = (float)nX;
|
||
m_nInitZ = m_fPrevZ = m_curz = (float)nZ;
|
||
|
||
if (GetX() < 0 || GetZ() < 0)
|
||
TRACE("Npc-SetLive-2 : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), GetX(), GetZ());
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
m_fHPChangeTime = getMSTime();
|
||
m_tFaintingTime = 0;
|
||
|
||
if (GetZoneID() == ZONE_FORGOTTEN_TEMPLE)
|
||
{
|
||
m_byActType = 4;
|
||
m_bySearchRange = myrand(200,255);
|
||
}
|
||
|
||
if (m_bFirstLive)
|
||
{
|
||
switch (m_byActType)
|
||
{
|
||
case 1:
|
||
case 2:
|
||
m_tNpcAttType = TENDER_ATTACK_TYPE;
|
||
break;
|
||
|
||
case 3:
|
||
case 4:
|
||
m_bHasFriends = true; // yay!
|
||
m_tNpcAttType = ATROCITY_ATTACK_TYPE;
|
||
break;
|
||
|
||
case 6:
|
||
break;
|
||
|
||
default:
|
||
m_tNpcAttType = ATROCITY_ATTACK_TYPE;
|
||
}
|
||
|
||
m_bFirstLive = false;
|
||
|
||
if (g_pMain->m_CurrentNPC.increment() == g_pMain->m_TotalNPC
|
||
&& !m_bIsEventNpc)
|
||
{
|
||
printf("Monster All Init Success - %d\n", (uint16) g_pMain->m_TotalNPC);
|
||
g_pMain->GameServerAcceptThread();
|
||
}
|
||
}
|
||
|
||
if (m_byMoveType == 2 && m_sMaxPathCount == 2)
|
||
{
|
||
__Vector3 vS, vE, vDir;
|
||
float fDir;
|
||
vS.Set((float)m_PathList.pPattenPos[0].x, 0, (float)m_PathList.pPattenPos[0].z);
|
||
vE.Set((float)m_PathList.pPattenPos[1].x, 0, (float)m_PathList.pPattenPos[1].z);
|
||
vDir = vE - vS;
|
||
vDir.Normalize();
|
||
Yaw2D(vDir.x, vDir.z, fDir);
|
||
m_byDirection = (int16)fDir;
|
||
}
|
||
|
||
RegisterRegion(GetX(), GetZ());
|
||
m_byDeadType = 0;
|
||
|
||
SendNpcInfo();
|
||
return true;
|
||
}
|
||
|
||
bool CNpc::RandomMove()
|
||
{
|
||
m_fSecForMetor = m_fSpeed_1;
|
||
|
||
if (GetMap() == nullptr
|
||
|| m_bySearchRange == 0
|
||
|| m_byMoveType == 0
|
||
|| m_byMoveType == 4)
|
||
return false;
|
||
|
||
float fDestX = -1.0f, fDestZ = -1.0f;
|
||
int max_xx = GetMap()->GetMapSize();
|
||
int max_zz = GetMap()->GetMapSize();
|
||
int x = 0, y = 0;
|
||
|
||
__Vector3 vStart, vEnd, vNewPos;
|
||
float fDis = 0.0f;
|
||
|
||
int nPathCount = 0;
|
||
|
||
int random_x = 0, random_z = 0;
|
||
|
||
if (m_byMoveType == 1)
|
||
{
|
||
random_x = 4;
|
||
random_z = 4;
|
||
|
||
switch (m_iPattenFrame)
|
||
{
|
||
case -4:
|
||
fDestX = GetX() + m_pPattenPos.x - (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z - (float)random_z/2;
|
||
m_iPattenFrame = 0;
|
||
break;
|
||
case -3:
|
||
fDestX = GetX() + m_pPattenPos.x - (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z - (float)random_z/2;
|
||
m_iPattenFrame--;
|
||
break;
|
||
case -2:
|
||
fDestX = GetX() + m_pPattenPos.x + (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z + (float)random_z/2;
|
||
m_iPattenFrame--;
|
||
break;
|
||
case -1:
|
||
fDestX = GetX() + m_pPattenPos.x + (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z + (float)random_z/2;
|
||
m_iPattenFrame--;
|
||
break;
|
||
case 0:
|
||
fDestX = (short)m_nInitX;
|
||
fDestZ = (short)m_nInitZ;
|
||
m_iPattenFrame = myrand(-1,1);
|
||
break;
|
||
case 1:
|
||
fDestX = GetX() + m_pPattenPos.x + (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z + (float)random_z/2;
|
||
m_iPattenFrame++;
|
||
break;
|
||
case 2:
|
||
fDestX = GetX() + m_pPattenPos.x + (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z + (float)random_z/2;
|
||
m_iPattenFrame++;
|
||
break;
|
||
case 3:
|
||
fDestX = GetX() + m_pPattenPos.x - (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z - (float)random_z/2;
|
||
m_iPattenFrame++;
|
||
break;
|
||
case 4:
|
||
fDestX = GetX() + m_pPattenPos.x - (float)random_x/2;
|
||
fDestZ = GetZ() + m_pPattenPos.z - (float)random_z/2;
|
||
m_iPattenFrame = 0;
|
||
break;
|
||
}
|
||
|
||
vStart.Set(GetX(), GetY(), GetZ());
|
||
vEnd.Set(fDestX, 0, fDestZ);
|
||
fDis = GetDistance(vStart, vEnd);
|
||
|
||
|
||
GetVectorPosition(vStart, vEnd, fDis > m_fSecForMetor ? m_fSecForMetor : fDis, &vNewPos);
|
||
fDestX = vNewPos.x;
|
||
fDestZ = vNewPos.z;
|
||
|
||
|
||
}
|
||
else if (m_byMoveType == 2)
|
||
{
|
||
if (IsInPathRange() == false)
|
||
{
|
||
nPathCount = GetNearPathPoint();
|
||
m_sRealPathCount = nPathCount;
|
||
if (!isInRangeSlow((float)m_PathList.pPattenPos[GetMyPath()].x + m_fBattlePos_x, (float)m_PathList.pPattenPos[GetMyPath()].z + m_fBattlePos_z,m_fSecForMetor))
|
||
{
|
||
vStart.Set(GetX(), GetY(), GetZ());
|
||
fDestX = (float)m_PathList.pPattenPos[GetMyPath()].x;
|
||
fDestZ = (float)m_PathList.pPattenPos[GetMyPath()].z;
|
||
vEnd.Set(fDestX, 0, fDestZ);
|
||
fDis = GetDistance(vStart, vEnd);
|
||
GetVectorPosition(vStart, vEnd, fDis > m_fSecForMetor ? m_fSecForMetor : fDis, &vNewPos);
|
||
fDestX = vNewPos.x;
|
||
fDestZ = vNewPos.z;
|
||
}
|
||
else
|
||
{
|
||
fDestX = (float)m_PathList.pPattenPos[GetMyPath()].x;
|
||
fDestZ = (float)m_PathList.pPattenPos[GetMyPath()].z;
|
||
|
||
if((m_sRealPathCount+1) == m_sMaxPathCount)
|
||
m_sRealPathCount = -m_sRealPathCount;
|
||
|
||
m_sRealPathCount++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
vStart.Set(GetX(), GetY(), GetZ());
|
||
fDestX = (float)m_PathList.pPattenPos[GetMyPath()].x;
|
||
fDestZ = (float)m_PathList.pPattenPos[GetMyPath()].z;
|
||
vEnd.Set(fDestX, 0, fDestZ);
|
||
fDis = GetDistance(vStart, vEnd);
|
||
GetVectorPosition(vStart, vEnd, fDis > m_fSecForMetor ? m_fSecForMetor : fDis, &vNewPos);
|
||
fDestX = vNewPos.x;
|
||
fDestZ = vNewPos.z;
|
||
if((m_sRealPathCount+1) == m_sMaxPathCount)
|
||
m_sRealPathCount = -m_sRealPathCount;
|
||
m_sRealPathCount++;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
vStart.Set(GetX(), 0, GetZ());
|
||
vEnd.Set(fDestX, 0, fDestZ);
|
||
|
||
if (GetX() < 0 || GetZ() < 0 || fDestX < 0 || fDestZ < 0)
|
||
{
|
||
/*TRACE("##### RandomMove Fail : value is negative.. [nid = %d, name=%s], cur_x=%.2f, z=%.2f, dest_x=%.2f, dest_z=%.2f#####\n",
|
||
GetID(), GetName().c_str(), GetX(), GetZ(), fDestX, fDestZ);*/
|
||
return false;
|
||
}
|
||
|
||
int mapWidth = (int)(max_xx * GetMap()->GetUnitDistance());
|
||
|
||
if (GetX() >= mapWidth || GetZ() >= mapWidth || fDestX >= mapWidth || fDestZ >= mapWidth)
|
||
{
|
||
/*TRACE("##### RandomMove Fail : value is overflow .. [nid = %d, name=%s], cur_x=%.2f, z=%.2f, dest_x=%.2f, dest_z=%.2f#####\n",
|
||
GetID(), GetName().c_str(), GetX(), GetZ(), fDestX, fDestZ);*/
|
||
return false;
|
||
}
|
||
|
||
if (GetType() == NPC_DUNGEON_MONSTER
|
||
&& !isInSpawnRange((int)fDestX, (int)fDestZ))
|
||
return false;
|
||
|
||
fDis = GetDistance(vStart, vEnd);
|
||
|
||
if (fDis <= m_fSecForMetor)
|
||
{
|
||
ClearPathFindData();
|
||
m_fStartPoint_X = GetX();
|
||
m_fStartPoint_Y = GetZ();
|
||
m_fEndPoint_X = fDestX;
|
||
m_fEndPoint_Y = fDestZ;
|
||
m_bPathFlag = true;
|
||
m_iAniFrameIndex = 1;
|
||
m_pPoint[0].fXPos = m_fEndPoint_X;
|
||
m_pPoint[0].fZPos = m_fEndPoint_Y;
|
||
return true;
|
||
}
|
||
|
||
float fTempRange = (float)fDis+2;
|
||
int min_x = (int)(GetX() - fTempRange)/TILE_SIZE; if(min_x < 0) min_x = 0;
|
||
int min_z = (int)(GetZ() - fTempRange)/TILE_SIZE; if(min_z < 0) min_z = 0;
|
||
int max_x = (int)(GetX() + fTempRange)/TILE_SIZE; if(max_x >= max_xx) max_x = max_xx - 1;
|
||
int max_z = (int)(GetZ() + fTempRange)/TILE_SIZE; if(min_z >= max_zz) min_z = max_zz - 1;
|
||
|
||
CPoint start, end;
|
||
start.x = (int)(GetX()/TILE_SIZE) - min_x;
|
||
start.y = (int)(GetZ()/TILE_SIZE) - min_z;
|
||
end.x = (int)(fDestX/TILE_SIZE) - min_x;
|
||
end.y = (int)(fDestZ/TILE_SIZE) - min_z;
|
||
|
||
if(start.x < 0 || start.y < 0 || end.x < 0 || end.y < 0) return false;
|
||
|
||
m_fStartPoint_X = GetX(); m_fStartPoint_Y = GetZ();
|
||
m_fEndPoint_X = fDestX; m_fEndPoint_Y = fDestZ;
|
||
|
||
m_min_x = min_x;
|
||
m_min_y = min_z;
|
||
m_max_x = max_x;
|
||
m_max_y = max_z;
|
||
|
||
if (m_byMoveType == 2 || m_byMoveType == 3)
|
||
{
|
||
IsNoPathFind(m_fSecForMetor);
|
||
return true;
|
||
}
|
||
|
||
return PathFind(start, end, m_fSecForMetor) == 1;
|
||
}
|
||
|
||
|
||
bool CNpc::RandomBackMove()
|
||
{
|
||
m_fSecForMetor = m_fSpeed_1;
|
||
|
||
if (m_bySearchRange == 0) return false;
|
||
|
||
float fDestX = -1.0f, fDestZ = -1.0f;
|
||
if (GetMap() == nullptr)
|
||
{
|
||
TRACE("#### Npc-RandomBackMove Zone Fail : [name=%s], zone=%d #####\n", GetName().c_str(), GetZoneID());
|
||
return false;
|
||
}
|
||
|
||
int max_xx = GetMap()->GetMapSize();
|
||
int max_zz = GetMap()->GetMapSize();
|
||
int x = 0, y = 0;
|
||
float fTempRange = (float)m_bySearchRange*2;
|
||
int min_x = (int)(GetX() - fTempRange)/TILE_SIZE; if(min_x < 0) min_x = 0;
|
||
int min_z = (int)(GetZ() - fTempRange)/TILE_SIZE; if(min_z < 0) min_z = 0;
|
||
int max_x = (int)(GetX() + fTempRange)/TILE_SIZE; if(max_x > max_xx) max_x = max_xx;
|
||
int max_z = (int)(GetZ() + fTempRange)/TILE_SIZE; if(max_z > max_zz) max_z = max_zz;
|
||
|
||
__Vector3 vStart, vEnd, vEnd22;
|
||
float fDis = 0.0f;
|
||
vStart.Set(GetX(), GetY(), GetZ());
|
||
|
||
uint16 nID = m_Target.id; // Target À» ±¸ÇÑ´Ù.
|
||
CUser* pUser = nullptr;
|
||
|
||
int iDir = 0;
|
||
|
||
int iRandomX = 0, iRandomZ = 0, iRandomValue=0;
|
||
iRandomValue = rand() % 2;
|
||
|
||
// Head towards player
|
||
if (nID < NPC_BAND)
|
||
{
|
||
pUser = g_pMain->GetUserPtr(nID);
|
||
if (pUser == nullptr)
|
||
return false;
|
||
|
||
if((int)pUser->GetX() != (int)GetX())
|
||
{
|
||
iRandomX = myrand((int)m_bySearchRange, (int)(m_bySearchRange*1.5));
|
||
iRandomZ = myrand(0, (int)m_bySearchRange);
|
||
|
||
if((int)pUser->GetX() > (int)GetX())
|
||
iDir = 1;
|
||
else
|
||
iDir = 2;
|
||
}
|
||
else // zÃàÀ¸·Î
|
||
{
|
||
iRandomZ = myrand((int)m_bySearchRange, (int)(m_bySearchRange*1.5));
|
||
iRandomX = myrand(0, (int)m_bySearchRange);
|
||
if((int)pUser->GetZ() > (int)GetZ())
|
||
iDir = 3;
|
||
else
|
||
iDir = 4;
|
||
}
|
||
|
||
switch(iDir)
|
||
{
|
||
case 1:
|
||
fDestX = GetX() - iRandomX;
|
||
if(iRandomValue == 0)
|
||
fDestZ = GetZ() - iRandomX;
|
||
else
|
||
fDestZ = GetZ() + iRandomX;
|
||
break;
|
||
case 2:
|
||
fDestX = GetX() + iRandomX;
|
||
if(iRandomValue == 0)
|
||
fDestZ = GetZ() - iRandomX;
|
||
else
|
||
fDestZ = GetZ() + iRandomX;
|
||
break;
|
||
case 3:
|
||
fDestZ = GetZ() - iRandomX;
|
||
if(iRandomValue == 0)
|
||
fDestX = GetX() - iRandomX;
|
||
else
|
||
fDestX = GetX() + iRandomX;
|
||
break;
|
||
case 4:
|
||
fDestZ = GetZ() - iRandomX;
|
||
if(iRandomValue == 0)
|
||
fDestX = GetX() - iRandomX;
|
||
else
|
||
fDestX = GetX() + iRandomX;
|
||
break;
|
||
}
|
||
|
||
vEnd.Set(fDestX, 0, fDestZ);
|
||
fDis = GetDistance(vStart, vEnd);
|
||
|
||
GetVectorPosition(vStart, vEnd, fDis > m_fSecForMetor ? m_fSecForMetor : fDis, &vEnd22);
|
||
fDestX = vEnd22.x;
|
||
fDestZ = vEnd22.z;
|
||
}
|
||
// Head towards monster/NPC
|
||
else
|
||
{
|
||
}
|
||
|
||
CPoint start, end;
|
||
start.x = (int)(GetX()/TILE_SIZE) - min_x;
|
||
start.y = (int)(GetZ()/TILE_SIZE) - min_z;
|
||
end.x = (int)(fDestX/TILE_SIZE) - min_x;
|
||
end.y = (int)(fDestZ/TILE_SIZE) - min_z;
|
||
|
||
if (start.x < 0 || start.y < 0 || end.x < 0 || end.y < 0)
|
||
return false;
|
||
|
||
m_fStartPoint_X = GetX(); m_fStartPoint_Y = GetZ();
|
||
m_fEndPoint_X = fDestX; m_fEndPoint_Y = fDestZ;
|
||
|
||
m_min_x = min_x;
|
||
m_min_y = min_z;
|
||
m_max_x = max_x;
|
||
m_max_y = max_z;
|
||
|
||
int nValue = PathFind(start, end, m_fSecForMetor);
|
||
if (nValue == 1)
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
bool CNpc::IsInPathRange()
|
||
{
|
||
|
||
static const float fPathRange = 40.0f;
|
||
return isInRangeSlow((float)m_PathList.pPattenPos[GetMyPath()].x + m_fBattlePos_x,
|
||
(float)m_PathList.pPattenPos[GetMyPath()].z + m_fBattlePos_z,
|
||
fPathRange + 1);
|
||
}
|
||
|
||
int CNpc::GetNearPathPoint()
|
||
{
|
||
int Number = 0;
|
||
float Range = 0.0f, myR = 0.0f;
|
||
foreach_array(i,m_PathList.pPattenPos)
|
||
{
|
||
_PattenPos * pPos = &m_PathList.pPattenPos[i];
|
||
|
||
if(pPos->x < 1
|
||
|| pPos->z < 1)
|
||
continue;
|
||
|
||
__Vector3 vTarget, vNpc;
|
||
vNpc.Set(GetX(),0,GetZ());
|
||
vTarget.Set(pPos->x, 0, pPos->z);
|
||
myR = GetDistance(vNpc,vTarget);
|
||
if((myR < Range || Range == 0.0f) && myR > 0.0f)
|
||
{
|
||
Range = myR;
|
||
Number = i;
|
||
}
|
||
}
|
||
|
||
int myRRR = myrand(0,10000);
|
||
if(myRRR > 5000)
|
||
Number = -Number;
|
||
|
||
return Number;
|
||
}
|
||
|
||
bool CNpc::isInSpawnRange(int nX, int nZ)
|
||
{
|
||
CRect r(m_nLimitMinX, m_nLimitMinZ, m_nLimitMaxX, m_nLimitMaxZ);
|
||
return r.PtInRect(nX, nZ);
|
||
}
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////
|
||
// PathFind ¸¦ ¼öÇàÇÑ´Ù.
|
||
//
|
||
int CNpc::PathFind(CPoint start, CPoint end, float fDistance)
|
||
{
|
||
ClearPathFindData();
|
||
|
||
if (start.x < 0 || start.y < 0
|
||
|| end.x < 0 || end.y < 0)
|
||
return -1;
|
||
|
||
if (start.x == end.x && start.y == end.y)
|
||
{
|
||
m_bPathFlag = true;
|
||
m_iAniFrameIndex = 1;
|
||
m_pPoint[0].fXPos = m_fEndPoint_X;
|
||
m_pPoint[0].fZPos = m_fEndPoint_Y;
|
||
return 1;
|
||
}
|
||
|
||
if (IsPathFindCheck(fDistance))
|
||
{
|
||
m_bPathFlag = true;
|
||
return 1;
|
||
}
|
||
|
||
m_vMapSize.cx = m_max_x - m_min_x + 1;
|
||
m_vMapSize.cy = m_max_y - m_min_y + 1;
|
||
|
||
m_pPath = nullptr;
|
||
|
||
m_vPathFind.SetMap(m_vMapSize.cx, m_vMapSize.cy, GetMap(), m_min_x, m_min_y);
|
||
m_pPath = m_vPathFind.FindPath(end.x, end.y, start.x, start.y);
|
||
|
||
int count = 0;
|
||
while (m_pPath != nullptr)
|
||
{
|
||
m_pPath = m_pPath->Parent;
|
||
if (m_pPath == nullptr)
|
||
break;
|
||
|
||
m_pPoint[count].pPoint.x = m_pPath->x + m_min_x;
|
||
m_pPoint[count++].pPoint.y = m_pPath->y + m_min_y;
|
||
}
|
||
|
||
if (count <= 0 || count >= MAX_PATH_LINE)
|
||
return 0;
|
||
|
||
m_iAniFrameIndex = count - 1;
|
||
int nAdd = GetDir(m_fStartPoint_X, m_fStartPoint_Y, m_fEndPoint_X, m_fEndPoint_Y);
|
||
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
if (i == (count - 1))
|
||
{
|
||
m_pPoint[i].fXPos = m_fEndPoint_X;
|
||
m_pPoint[i].fZPos = m_fEndPoint_Y;
|
||
}
|
||
else
|
||
{
|
||
m_pPoint[i].fXPos = (float)(m_pPoint[i].pPoint.x * TILE_SIZE + m_fAdd_x);
|
||
m_pPoint[i].fZPos = (float)(m_pPoint[i].pPoint.y * TILE_SIZE + m_fAdd_z);
|
||
}
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
void CNpc::Dead(Unit * pKiller /*= nullptr*/, bool bSendDeathPacket /*= false*/)
|
||
{
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr)
|
||
return;
|
||
|
||
m_iHP = 0;
|
||
m_NpcState = NPC_DEAD;
|
||
m_Delay = m_sRegenTime;
|
||
m_bFirstLive = false;
|
||
m_byDeadType = 100; // ÀüÀïÀ̺¥Æ®Áß¿¡¼ Á×´Â °æ¿ì
|
||
|
||
if (GetRegionX() > pMap->GetXRegionMax() || GetRegionZ() > pMap->GetZRegionMax())
|
||
{
|
||
TRACE("#### Npc-Dead() Fail : [nid=%d, sid=%d], nRX=%d, nRZ=%d #####\n",
|
||
GetID(), GetProtoID(), GetRegionX(), GetRegionZ());
|
||
return;
|
||
}
|
||
|
||
pMap->RegionNpcRemove(GetRegionX(), GetRegionZ(), GetID());
|
||
|
||
if (bSendDeathPacket)
|
||
{
|
||
|
||
SendExpToUserList();
|
||
SendDeathAnimation(pKiller);
|
||
if (isShowBox())
|
||
GiveNpcHaveItem();
|
||
}
|
||
}
|
||
|
||
bool CNpc::isShowBox()
|
||
{
|
||
uint8 bType = GetType();
|
||
|
||
if (bType == NPC_CHAOS_STONE
|
||
|| bType == NPC_PVP_MONUMENT
|
||
|| bType == NPC_BORDER_MONUMENT
|
||
|| bType == NPC_BIFROST_MONUMENT
|
||
|| bType == NPC_GUARD_TOWER1
|
||
|| bType == NPC_GUARD_TOWER2
|
||
|| bType == NPC_SCARECROW
|
||
|| bType == NPC_KARUS_WARDER1
|
||
|| bType == NPC_KARUS_WARDER2
|
||
|| bType == NPC_ELMORAD_WARDER1
|
||
|| bType == NPC_ELMORAD_WARDER2
|
||
|| bType == NPC_KARUS_GATEKEEPER
|
||
|| bType == NPC_ELMORAD_GATEKEEPER
|
||
|| bType == NPC_BATTLE_MONUMENT
|
||
|| bType == NPC_KARUS_MONUMENT
|
||
|| bType == NPC_HUMAN_MONUMENT
|
||
|| GetZoneID() == ZONE_FORGOTTEN_TEMPLE
|
||
|| GetZoneID() == ZONE_PRISON
|
||
|| nIsPet)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CNpc::FindEnemy()
|
||
{
|
||
if (isNonAttackingObject())
|
||
return false;
|
||
|
||
bool bIsGuard = isGuard();
|
||
|
||
// We shouldn't really need this anymore...
|
||
bool bIsNeutralZone = (GetZoneID() == ZONE_MORADONM2 || GetZoneID() == ZONE_MORADON || GetZoneID() == ZONE_ARENA);
|
||
|
||
// Disable AI enemy finding (of users) in neutral zones.
|
||
// Guards and monsters are, however, allowed.
|
||
if (!isMonster()
|
||
&& !bIsGuard
|
||
&& bIsNeutralZone)
|
||
return false;
|
||
|
||
// Healer Npc
|
||
int iMonsterNid = 0;
|
||
if (isHealer())
|
||
{
|
||
iMonsterNid = FindFriend(MonSearchNeedsHealing);
|
||
if (iMonsterNid != 0)
|
||
return true;
|
||
}
|
||
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr) return false;
|
||
CUser *pUser = nullptr;
|
||
CNpc *pNpc = nullptr;
|
||
|
||
int target_uid = 0;
|
||
__Vector3 vUser, vNpc;
|
||
float fDis = 0.0f;
|
||
float fCompareDis = 0.0f;
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
|
||
float fSearchRange = (float)m_bySearchRange;
|
||
|
||
int iExpand = FindEnemyRegion();
|
||
|
||
if (GetRegionX() > pMap->GetXRegionMax()
|
||
|| GetRegionZ() > pMap->GetZRegionMax())
|
||
return false;
|
||
|
||
/*** If we're a monster, we can find user enemies anywhere. If we're an NPC, we must not be friendly. ***/
|
||
if (isMonster()
|
||
|| (!GetMap()->areNPCsFriendly() || GetNation() != Nation::ALL))
|
||
{
|
||
fCompareDis = FindEnemyExpand(GetRegionX(), GetRegionZ(), fCompareDis, UnitPlayer);
|
||
|
||
int x=0, y=0;
|
||
// ÀÌ¿ôÇØ ÀÖ´Â RegionÀ» °Ë»öÇØ¼,, ¸óÀÇ À§Ä¡¿Í Á¦ÀÏ °¡±î¿î UserÀ» ÇâÇØ.. À̵¿..
|
||
for(int l=0; l<4; l++)
|
||
{
|
||
if(m_iFind_X[l] == 0 && m_iFind_Y[l] == 0) continue;
|
||
|
||
x = GetRegionX() + (m_iFind_X[l]);
|
||
y = GetRegionZ() + (m_iFind_Y[l]);
|
||
|
||
// À̺κР¼öÁ¤¿ä¸Á,,
|
||
if (x < 0
|
||
|| y < 0
|
||
|| x > pMap->GetXRegionMax()
|
||
|| y > pMap->GetZRegionMax())
|
||
continue;
|
||
|
||
fCompareDis = FindEnemyExpand(x, y, fCompareDis, UnitPlayer);
|
||
}
|
||
|
||
if (hasTarget() && (fCompareDis <= fSearchRange))
|
||
return true;
|
||
|
||
fCompareDis = 0.0f;
|
||
}
|
||
|
||
/*** Only find NPC/mob enemies if we are a guard ***/
|
||
if (bIsGuard) // || GetType() == NPCTYPE_MONSTER)
|
||
{
|
||
fCompareDis = FindEnemyExpand(GetRegionX(), GetRegionZ(), fCompareDis, UnitNPC);
|
||
|
||
int x=0, y=0;
|
||
|
||
// ÀÌ¿ôÇØ ÀÖ´Â RegionÀ» °Ë»öÇØ¼,, ¸óÀÇ À§Ä¡¿Í Á¦ÀÏ °¡±î¿î UserÀ» ÇâÇØ.. À̵¿..
|
||
for(int l=0; l<4; l++) {
|
||
if(m_iFind_X[l] == 0 && m_iFind_Y[l] == 0) continue;
|
||
|
||
x = GetRegionX() + (m_iFind_X[l]);
|
||
y = GetRegionZ() + (m_iFind_Y[l]);
|
||
|
||
// À̺κР¼öÁ¤¿ä¸Á,,
|
||
if(x < 0 || y < 0 || x > pMap->GetXRegionMax() || y > pMap->GetZRegionMax()) continue;
|
||
|
||
fCompareDis = FindEnemyExpand(x, y, fCompareDis, UnitNPC);
|
||
}
|
||
|
||
if (hasTarget() && (fCompareDis <= fSearchRange))
|
||
return true;
|
||
}
|
||
|
||
// ¾Æ¹«µµ ¾øÀ¸¹Ç·Î ¸®½ºÆ®¿¡ °ü¸®ÇÏ´Â À¯Àú¸¦ ÃʱâÈÇÑ´Ù.
|
||
InitUserList();
|
||
InitTarget();
|
||
return false;
|
||
}
|
||
|
||
// Npc°¡ À¯Àú¸¦ °Ë»öÇÒ¶§ ¾î´À Region±îÁö °Ë»öÇØ¾ß ÇÏ´ÂÁö¸¦ ÆÇ´Ü..
|
||
int CNpc::FindEnemyRegion()
|
||
{
|
||
/*
|
||
1 2 3
|
||
4 0 5
|
||
6 7 8
|
||
*/
|
||
int iRetValue = 0;
|
||
int iSX = GetRegionX() * VIEW_DIST;
|
||
int iSZ = GetRegionZ() * VIEW_DIST;
|
||
int iEX = (GetRegionX()+1) * VIEW_DIST;
|
||
int iEZ = (GetRegionZ()+1) * VIEW_DIST;
|
||
int iSearchRange = m_bySearchRange;
|
||
int iCurSX = (int)GetX() - m_bySearchRange;
|
||
int iCurSY = (int)GetX() - m_bySearchRange;
|
||
int iCurEX = (int)GetX() + m_bySearchRange;
|
||
int iCurEY = (int)GetX() + m_bySearchRange;
|
||
|
||
int iMyPos = GetMyField();
|
||
|
||
switch(iMyPos)
|
||
{
|
||
case 1:
|
||
if((iCurSX < iSX) && (iCurSY < iSZ))
|
||
iRetValue = 1;
|
||
else if((iCurSX > iSX) && (iCurSY < iSZ))
|
||
iRetValue = 2;
|
||
else if((iCurSX < iSX) && (iCurSY > iSZ))
|
||
iRetValue = 4;
|
||
else if((iCurSX >= iSX) && (iCurSY >= iSZ))
|
||
iRetValue = 0;
|
||
break;
|
||
case 2:
|
||
if((iCurEX < iEX) && (iCurSY < iSZ))
|
||
iRetValue = 2;
|
||
else if((iCurEX > iEX) && (iCurSY < iSZ))
|
||
iRetValue = 3;
|
||
else if((iCurEX <= iEX) && (iCurSY >= iSZ))
|
||
iRetValue = 0;
|
||
else if((iCurEX > iEX) && (iCurSY > iSZ))
|
||
iRetValue = 5;
|
||
break;
|
||
case 3:
|
||
if((iCurSX < iSX) && (iCurEY < iEZ))
|
||
iRetValue = 4;
|
||
else if((iCurSX >= iSX) && (iCurEY <= iEZ))
|
||
iRetValue = 0;
|
||
else if((iCurSX < iSX) && (iCurEY > iEZ))
|
||
iRetValue = 6;
|
||
else if((iCurSX > iSX) && (iCurEY > iEZ))
|
||
iRetValue = 7;
|
||
break;
|
||
case 4:
|
||
if((iCurEX <= iEX) && (iCurEY <= iEZ))
|
||
iRetValue = 0;
|
||
else if((iCurEX > iEX) && (iCurEY < iEZ))
|
||
iRetValue = 5;
|
||
else if((iCurEX < iEX) && (iCurEY > iEZ))
|
||
iRetValue = 7;
|
||
else if((iCurEX > iEX) && (iCurEY > iEZ))
|
||
iRetValue = 8;
|
||
break;
|
||
}
|
||
|
||
if(iRetValue <= 0) // Àӽ÷Πº¸Á¤(¹®Á¦½Ã),, Çϱâ À§ÇѰÍ..
|
||
iRetValue = 0;
|
||
|
||
switch(iRetValue)
|
||
{
|
||
case 0:
|
||
m_iFind_X[0] = 0; m_iFind_Y[0] = 0;
|
||
m_iFind_X[1] = 0; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = 0; m_iFind_Y[2] = 0;
|
||
m_iFind_X[3] = 0; m_iFind_Y[3] = 0;
|
||
break;
|
||
case 1:
|
||
m_iFind_X[0] = -1; m_iFind_Y[0] = -1;
|
||
m_iFind_X[1] = 0; m_iFind_Y[1] = -1;
|
||
m_iFind_X[2] = -1; m_iFind_Y[2] = 0;
|
||
m_iFind_X[3] = 0; m_iFind_Y[3] = 0;
|
||
break;
|
||
case 2:
|
||
m_iFind_X[0] = 0; m_iFind_Y[0] = -1;
|
||
m_iFind_X[1] = 0; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = 0; m_iFind_Y[2] = 0;
|
||
m_iFind_X[3] = 0; m_iFind_Y[3] = 0;
|
||
break;
|
||
case 3:
|
||
m_iFind_X[0] = 0; m_iFind_Y[0] = 0;
|
||
m_iFind_X[1] = 1; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = 0; m_iFind_Y[2] = 1;
|
||
m_iFind_X[3] = 1; m_iFind_Y[3] = 1;
|
||
break;
|
||
case 4:
|
||
m_iFind_X[0] = -1; m_iFind_Y[0] = 0;
|
||
m_iFind_X[1] = 0; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = 0; m_iFind_Y[2] = 0;
|
||
m_iFind_X[3] = 0; m_iFind_Y[3] = 0;
|
||
break;
|
||
case 5:
|
||
m_iFind_X[0] = 0; m_iFind_Y[0] = 0;
|
||
m_iFind_X[1] = 1; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = 0; m_iFind_Y[2] = 0;
|
||
m_iFind_X[3] = 0; m_iFind_Y[3] = 0;
|
||
break;
|
||
case 6:
|
||
m_iFind_X[0] = -1; m_iFind_Y[0] = 0;
|
||
m_iFind_X[1] = 0; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = -1; m_iFind_Y[2] = 1;
|
||
m_iFind_X[3] = 0; m_iFind_Y[3] = 1;
|
||
break;
|
||
case 7:
|
||
m_iFind_X[0] = 0; m_iFind_Y[0] = 0;
|
||
m_iFind_X[1] = 0; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = 0; m_iFind_Y[2] = 1;
|
||
m_iFind_X[3] = 0; m_iFind_Y[3] = 0;
|
||
break;
|
||
case 8:
|
||
m_iFind_X[0] = 0; m_iFind_Y[0] = 0;
|
||
m_iFind_X[1] = 1; m_iFind_Y[1] = 0;
|
||
m_iFind_X[2] = 0; m_iFind_Y[2] = 1;
|
||
m_iFind_X[3] = 1; m_iFind_Y[3] = 1;
|
||
break;
|
||
}
|
||
|
||
return iRetValue;
|
||
}
|
||
|
||
float CNpc::FindEnemyExpand(int nRX, int nRZ, float fCompDis, UnitType unitType)
|
||
{
|
||
MAP* pMap = GetMap();
|
||
float fDis = 0.0f;
|
||
if(pMap == nullptr) return fDis;
|
||
float fComp = fCompDis;
|
||
float fSearchRange = (float)m_bySearchRange;
|
||
uint16 target_uid;
|
||
__Vector3 vUser, vNpc, vMon;
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
|
||
// Finding players
|
||
if (unitType == UnitPlayer)
|
||
{
|
||
Guard lock(pMap->m_lock);
|
||
CRegion *pRegion = &pMap->m_ppRegion[nRX][nRZ];
|
||
|
||
if (pRegion == nullptr || (pRegion && pRegion->m_RegionUserArray.IsEmpty()))
|
||
return 0.0f;
|
||
|
||
foreach_stlmap (itr, pRegion->m_RegionUserArray)
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(*itr->second);
|
||
if (pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| !CanAttack(pUser)
|
||
|| pUser->m_bInvisibilityType
|
||
|| pUser->isGM()
|
||
|| GetNation() == Nation::ALL
|
||
|| pUser->GetEventRoom() != GetEventRoom()
|
||
|| (m_tNpcAttType == ATROCITY_ATTACK_TYPE && !IsDamagedUserList(pUser) && pUser->m_transformationType == TransformationMonster))//Transform
|
||
continue;
|
||
|
||
float fDis = Unit::GetDistanceSqrt(pUser);
|
||
if (fDis > fSearchRange
|
||
|| fDis < fComp)
|
||
continue;
|
||
|
||
target_uid = pUser->GetID();
|
||
fComp = fDis;
|
||
|
||
// Aggressive spawns don't mind attacking first.
|
||
if (m_tNpcAttType == ATROCITY_ATTACK_TYPE
|
||
// Passive spawns will only attack if they've been attacked first, or they've got backup! (Cowards!)
|
||
|| (m_tNpcAttType == TENDER_ATTACK_TYPE && (IsDamagedUserList(pUser) || (m_bHasFriends && m_Target.id == target_uid))))
|
||
{
|
||
m_Target.id = target_uid;
|
||
m_Target.bSet = true;
|
||
m_Target.x = pUser->GetX();
|
||
m_Target.y = pUser->GetY();
|
||
m_Target.z = pUser->GetZ();
|
||
}
|
||
}
|
||
|
||
}
|
||
// Finding NPCs/monsters
|
||
else if (unitType == UnitNPC)
|
||
{
|
||
Guard lock(pMap->m_lock);
|
||
CRegion *pRegion = &pMap->m_ppRegion[nRX][nRZ];
|
||
|
||
if (pRegion == nullptr || (pRegion && pRegion->m_RegionNpcArray.IsEmpty()))
|
||
return 0.0f;
|
||
|
||
foreach_stlmap (itr, pRegion->m_RegionNpcArray)
|
||
{
|
||
int nNpcid = *itr->second;
|
||
if( nNpcid < NPC_BAND ) continue;
|
||
CNpc *pNpc = g_pMain->GetNpcPtr(nNpcid);
|
||
|
||
if (pNpc == nullptr
|
||
|| pNpc == this
|
||
|| pNpc->isDead()
|
||
|| pNpc->GetNation() == Nation::ALL
|
||
|| pNpc->isNonAttackableObject()
|
||
|| !isHostileTo(pNpc))
|
||
continue;
|
||
|
||
vMon.Set(pNpc->GetX(), pNpc->GetY(), pNpc->GetZ());
|
||
fDis = GetDistance(vMon, vNpc);
|
||
|
||
if (fDis > fSearchRange || fDis < fComp)
|
||
continue;
|
||
|
||
target_uid = nNpcid;
|
||
fComp = fDis;
|
||
m_Target.id = target_uid;
|
||
m_Target.bSet = true;
|
||
m_Target.x = pNpc->GetX();
|
||
m_Target.y = pNpc->GetY();
|
||
m_Target.z = pNpc->GetZ();
|
||
}
|
||
}
|
||
|
||
return fComp;
|
||
}
|
||
|
||
// regionÀ» 4µîºĞÇØ¼ ¸ó½ºÅÍÀÇ ÇöÀç À§Ä¡°¡ regionÀÇ ¾î´À ºÎºĞ¿¡ µé¾î°¡´ÂÁö¸¦ ÆÇ´Ü
|
||
int CNpc::GetMyField()
|
||
{
|
||
int iRet = 0;
|
||
int iX = GetRegionX() * VIEW_DIST;
|
||
int iZ = GetRegionZ() * VIEW_DIST;
|
||
int iAdd = VIEW_DIST / 2;
|
||
int iCurX = (int)GetX(); // monster current position_x
|
||
int iCurZ = (int)GetZ();
|
||
if(COMPARE(iCurX, iX, iX+iAdd) && COMPARE(iCurZ, iZ, iZ+iAdd))
|
||
iRet = 1;
|
||
else if(COMPARE(iCurX, iX+iAdd, iX+VIEW_DIST) && COMPARE(iCurZ, iZ, iZ+iAdd))
|
||
iRet = 2;
|
||
else if(COMPARE(iCurX, iX, iX+iAdd) && COMPARE(iCurZ, iZ+iAdd, iZ+VIEW_DIST))
|
||
iRet = 3;
|
||
else if(COMPARE(iCurX, iX+iAdd, iX+VIEW_DIST) && COMPARE(iCurZ, iZ+iAdd, iZ+VIEW_DIST))
|
||
iRet = 4;
|
||
|
||
return iRet;
|
||
}
|
||
|
||
bool CNpc::IsDamagedUserList(CUser *pUser)
|
||
{
|
||
if (pUser == nullptr)
|
||
return false;
|
||
|
||
if (m_DamagedUserListCount == 0)
|
||
return false;
|
||
|
||
|
||
for (int i = 0; i < NPC_HAVE_USER_LIST; i++){
|
||
if (m_DamagedUserList[i].GetID == pUser->GetID())
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
int CNpc::IsSurround(CUser* pUser)
|
||
{
|
||
if (GetProto()->m_byDirectAttack)
|
||
return 0;
|
||
|
||
if(pUser == nullptr) return -2; // User°¡ ¾øÀ¸¹Ç·Î Ÿ°ÙÁöÁ¤ ½ÇÆĞ..
|
||
int nDir = pUser->IsSurroundCheck(GetX(), 0.0f, GetZ(), GetID());
|
||
if(nDir != 0)
|
||
{
|
||
m_byAttackPos = nDir;
|
||
return nDir;
|
||
}
|
||
return -1; // Ÿ°ÙÀÌ µÑ·¯ ½×¿© ÀÖÀ½...
|
||
}
|
||
|
||
// Path Find ·Î ãÀº±æÀ» ´Ù À̵¿ Çß´ÂÁö ÆÇ´Ü
|
||
bool CNpc::IsMovingEnd()
|
||
{
|
||
if (m_fPrevX == m_fEndPoint_X && m_fPrevZ == m_fEndPoint_Y)
|
||
{
|
||
//m_sStepCount = 0;
|
||
m_iAniFrameCount = 0;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// Step ¼ö ¸¸Å ŸÄÏÀ» ÇâÇØ À̵¿ÇÑ´Ù.
|
||
bool CNpc::StepMove()
|
||
{
|
||
m_fSecForMetor = m_fSpeed_1;
|
||
|
||
if(m_NpcState != NPC_MOVING && m_NpcState != NPC_TRACING && m_NpcState != NPC_BACK) return false;
|
||
|
||
__Vector3 vStart, vEnd, vDis;
|
||
float fDis;
|
||
|
||
float fOldCurX = 0.0f, fOldCurZ = 0.0f;
|
||
|
||
if(m_sStepCount == 0) {
|
||
fOldCurX = GetX(); fOldCurZ = GetZ();
|
||
}
|
||
else {
|
||
fOldCurX = m_fPrevX; fOldCurZ = m_fPrevZ;
|
||
}
|
||
|
||
vStart.Set(fOldCurX, 0, fOldCurZ);
|
||
vEnd.Set(m_pPoint[m_iAniFrameCount].fXPos, 0, m_pPoint[m_iAniFrameCount].fZPos);
|
||
|
||
// ¾ÈÀü ÄÚµå..
|
||
if(m_pPoint[m_iAniFrameCount].fXPos < 0 || m_pPoint[m_iAniFrameCount].fZPos < 0)
|
||
{
|
||
m_fPrevX = m_fEndPoint_X;
|
||
m_fPrevZ = m_fEndPoint_Y;
|
||
|
||
RegisterRegion(m_fPrevX, m_fPrevZ);
|
||
return false;
|
||
}
|
||
|
||
fDis = GetDistance(vStart, vEnd);
|
||
if (fDis > m_fSecForMetor)
|
||
{
|
||
GetVectorPosition(vStart, vEnd, m_fSecForMetor, &vDis);
|
||
m_fPrevX = vDis.x;
|
||
m_fPrevZ = vDis.z;
|
||
}
|
||
else
|
||
{
|
||
m_iAniFrameCount++;
|
||
if(m_iAniFrameCount == m_iAniFrameIndex)
|
||
{
|
||
vEnd.Set(m_pPoint[m_iAniFrameCount].fXPos, 0, m_pPoint[m_iAniFrameCount].fZPos);
|
||
fDis = GetDistance(vStart, vEnd);
|
||
// ¸¶Áö¸· ÁÂÇ¥´Â m_fSecForMetor ~ m_fSecForMetor+1 »çÀ̵µ °¡´ÉÇÏ°Ô À̵¿
|
||
if(fDis > m_fSecForMetor)
|
||
{
|
||
GetVectorPosition(vStart, vEnd, m_fSecForMetor, &vDis);
|
||
m_fPrevX = vDis.x;
|
||
m_fPrevZ = vDis.z;
|
||
//m_iAniFrameCount--;
|
||
}
|
||
else
|
||
{
|
||
m_fPrevX = m_fEndPoint_X;
|
||
m_fPrevZ = m_fEndPoint_Y;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
vEnd.Set(m_pPoint[m_iAniFrameCount].fXPos, 0, m_pPoint[m_iAniFrameCount].fZPos);
|
||
fDis = GetDistance(vStart, vEnd);
|
||
if(fDis >= m_fSecForMetor)
|
||
{
|
||
GetVectorPosition(vStart, vEnd, m_fSecForMetor, &vDis);
|
||
m_fPrevX = vDis.x;
|
||
m_fPrevZ = vDis.z;
|
||
}
|
||
else
|
||
{
|
||
m_fPrevX = m_fEndPoint_X;
|
||
m_fPrevZ = m_fEndPoint_Y;
|
||
}
|
||
}
|
||
}
|
||
|
||
vStart.Set(fOldCurX, 0, fOldCurZ);
|
||
vEnd.Set(m_fPrevX, 0, m_fPrevZ);
|
||
|
||
m_fSecForRealMoveMetor = GetDistance(vStart, vEnd);
|
||
|
||
if(m_fSecForRealMoveMetor > m_fSecForMetor+1)
|
||
{
|
||
TRACE("#### move fail : [nid = %d], m_fSecForMetor = %.2f\n", GetID(), m_fSecForRealMoveMetor);
|
||
}
|
||
|
||
if (m_sStepCount++ > 0)
|
||
{
|
||
m_curx = fOldCurX; m_curz = fOldCurZ;
|
||
if(GetX() < 0 || GetZ() < 0)
|
||
TRACE("Npc-StepMove : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), GetX(), GetZ());
|
||
|
||
return RegisterRegion(GetX(), GetZ());
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CNpc::StepNoPathMove()
|
||
{
|
||
if(m_NpcState != NPC_MOVING && m_NpcState != NPC_TRACING && m_NpcState != NPC_BACK) return false;
|
||
|
||
__Vector3 vStart, vEnd;
|
||
float fOldCurX = 0.0f, fOldCurZ = 0.0f;
|
||
|
||
if(m_sStepCount == 0) {
|
||
fOldCurX = GetX(); fOldCurZ = GetZ();
|
||
}
|
||
else {
|
||
fOldCurX = m_fPrevX; fOldCurZ = m_fPrevZ;
|
||
}
|
||
|
||
|
||
if(m_sStepCount < 0 || m_sStepCount >= m_iAniFrameIndex) {
|
||
TRACE("#### IsNoPtahfind Fail : nid=%d,%s, count=%d/%d ####\n", GetID(), GetName().c_str(), m_sStepCount, m_iAniFrameIndex);
|
||
return false;
|
||
}
|
||
|
||
vStart.Set(fOldCurX, 0, fOldCurZ);
|
||
m_fPrevX = m_pPoint[m_sStepCount].fXPos;
|
||
m_fPrevZ = m_pPoint[m_sStepCount].fZPos;
|
||
vEnd.Set(m_fPrevX, 0, m_fPrevZ);
|
||
|
||
if(m_fPrevX == -1 || m_fPrevZ == -1) {
|
||
TRACE("##### StepNoPath Fail : nid=%d,%s, x=%.2f, z=%.2f #####\n", GetID(), GetName().c_str(), m_fPrevX, m_fPrevZ);
|
||
return false;
|
||
}
|
||
|
||
m_fSecForRealMoveMetor = GetDistance(vStart, vEnd);
|
||
|
||
if (m_sStepCount++ > 0)
|
||
{
|
||
if(fOldCurX < 0 || fOldCurZ < 0) {
|
||
TRACE("#### Npc-StepNoPathMove Fail : nid=(%d, %s), x=%.2f, z=%.2f\n", GetID(), GetName().c_str(), fOldCurX, fOldCurZ);
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
m_curx = fOldCurX; m_curz = fOldCurZ;
|
||
}
|
||
|
||
return RegisterRegion(GetX(), GetZ());
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
CloseTargetResult CNpc::IsCloseTarget(int nRange, AttackType attackType)
|
||
{
|
||
if (!hasTarget())
|
||
return CloseTargetInvalid;
|
||
|
||
CUser * pUser = nullptr;
|
||
CNpc * pNpc = nullptr;
|
||
__Vector3 vUser, vWillUser, vNpc, vDistance;
|
||
float fDis = 0.0f, fWillDis = 0.0f, fX = 0.0f, fZ = 0.0f;
|
||
bool bUserType = false; // Ÿ°ÙÀÌ À¯ÀúÀ̸é true
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
|
||
if (m_Target.id < NPC_BAND)
|
||
{
|
||
pUser = g_pMain->GetUserPtr(m_Target.id);
|
||
if (pUser == nullptr)
|
||
{
|
||
InitTarget();
|
||
return CloseTargetInvalid;
|
||
}
|
||
|
||
vUser.Set(pUser->GetX(), pUser->GetY(), pUser->GetZ());
|
||
vWillUser.Set(pUser->m_fWill_x, pUser->m_fWill_y, pUser->m_fWill_z);
|
||
fX = pUser->GetX();
|
||
fZ = pUser->GetZ();
|
||
|
||
vDistance = vWillUser - vNpc;
|
||
fWillDis = vDistance.Magnitude();
|
||
fWillDis = fWillDis - m_proto->m_fBulk;
|
||
bUserType = true;
|
||
}
|
||
else
|
||
{
|
||
|
||
pNpc = g_pMain->GetNpcPtr(m_Target.id);
|
||
if(pNpc == nullptr)
|
||
{
|
||
|
||
InitTarget();
|
||
return CloseTargetInvalid;
|
||
}
|
||
vUser.Set(pNpc->GetX(), pNpc->GetY(), pNpc->GetZ());
|
||
fX = pNpc->GetX();
|
||
fZ = pNpc->GetZ();
|
||
}
|
||
|
||
vDistance = vUser - vNpc;
|
||
fDis = vDistance.Magnitude();
|
||
|
||
fDis = fDis - m_proto->m_fBulk;
|
||
|
||
|
||
if (fDis >= 30 && attackType ==1)
|
||
{
|
||
return CloseTargetInvalid;
|
||
|
||
}
|
||
|
||
if (GetType() == NPC_DUNGEON_MONSTER && !isInSpawnRange((int)vUser.x, (int)vUser.z))
|
||
return CloseTargetInvalid;
|
||
|
||
if (attackType == AttackTypePhysical)
|
||
{
|
||
|
||
m_bStopFollowingTarget = true;
|
||
if (pUser != nullptr)
|
||
{
|
||
if (m_Target.x == pUser->GetX() && m_Target.z == pUser->GetZ())
|
||
m_bStopFollowingTarget = true;
|
||
}
|
||
}
|
||
|
||
if ((int)fDis > nRange)
|
||
{
|
||
|
||
if (attackType == nRange)
|
||
{
|
||
m_bStopFollowingTarget = true;
|
||
m_Target.x = fX;
|
||
m_Target.z = fZ;
|
||
}
|
||
return CloseTargetNotInRange;
|
||
}
|
||
|
||
m_fEndPoint_X = GetX();
|
||
m_fEndPoint_Y = GetZ();
|
||
m_Target.x = fX;
|
||
m_Target.z = fZ;
|
||
|
||
if (GetProto()->m_byDirectAttack == 1)
|
||
{
|
||
|
||
if (fDis <= LONG_ATTACK_RANGE) return CloseTargetInGeneralRange;
|
||
else if (fDis <= nRange) return CloseTargetInAttackRange;
|
||
}
|
||
else
|
||
{
|
||
if (attackType == AttackTypeMagic)
|
||
{
|
||
|
||
if (fDis <= (SHORT_ATTACK_RANGE + m_proto->m_fBulk )) return CloseTargetInGeneralRange;
|
||
else if (fDis <= nRange) return CloseTargetInAttackRange;
|
||
|
||
if (bUserType && fWillDis > (SHORT_ATTACK_RANGE + m_proto->m_fBulk) && fWillDis <= nRange)
|
||
return CloseTargetInAttackRange;
|
||
}
|
||
else
|
||
{
|
||
|
||
if (fDis <= (SHORT_ATTACK_RANGE + m_proto->m_fBulk)) return CloseTargetInGeneralRange;
|
||
else if (fDis <= nRange) return CloseTargetInAttackRange;
|
||
}
|
||
}
|
||
|
||
return CloseTargetNotInRange;
|
||
}
|
||
|
||
// Target °ú NPC °£ Path FindingÀ» ¼öÇàÇÑ´Ù.
|
||
int CNpc::GetTargetPath(int option)
|
||
{
|
||
int nInitType = m_byInitMoveType;
|
||
if (m_byInitMoveType >= 100)
|
||
nInitType -= 100;
|
||
|
||
if (GetType() != NPC_MONSTER
|
||
&& m_byMoveType != nInitType)
|
||
m_byMoveType = nInitType;
|
||
|
||
m_fSecForMetor = m_fSpeed_2;
|
||
CUser* pUser = nullptr;
|
||
CNpc* pNpc = nullptr;
|
||
float iTempRange = 0.0f;
|
||
__Vector3 vUser, vNpc, vDistance, vEnd22;
|
||
float fDis = 0.0f;
|
||
float fDegree = 0.0f, fTargetDistance = 0.0f;
|
||
float fSurX = 0.0f, fSurZ = 0.0f;
|
||
|
||
// Player
|
||
if (m_Target.id < NPC_BAND)
|
||
{
|
||
pUser = g_pMain->GetUserPtr(m_Target.id);
|
||
if(pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| pUser->GetZoneID() != GetZoneID())
|
||
{
|
||
InitTarget();
|
||
return -1;
|
||
}
|
||
|
||
if(option == 1) { // magicÀ̳ª ȰµîÀ¸·Î °ø°İ ´çÇß´Ù¸é...
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
vUser.Set(pUser->GetX(), pUser->GetY(), pUser->GetZ());
|
||
fDis = GetDistance(vNpc, vUser);
|
||
if(fDis >= NPC_MAX_MOVE_RANGE) return -1; // ³Ê¹« °Å¸®°¡ ¸Ö¾î¼,, ÃßÀûÀÌ ¾ÈµÇ°Ô..
|
||
iTempRange = fDis + 10;
|
||
}
|
||
else {
|
||
iTempRange = (float)m_bySearchRange; // ÀϽÃÀûÀ¸·Î º¸Á¤ÇÑ´Ù.
|
||
if(IsDamagedUserList(pUser)) iTempRange = (float)(m_byTracingRange * 2); // °ø°İ¹ŞÀº »óŸé ãÀ» ¹üÀ§ Áõ°¡.
|
||
else iTempRange += 2;
|
||
}
|
||
|
||
if (m_bTracing
|
||
&& !isInRangeSlow(m_fTracingStartX, m_fTracingStartZ, iTempRange))
|
||
{
|
||
InitTarget();
|
||
return -1;
|
||
}
|
||
}
|
||
// NPC
|
||
else if(m_Target.id >= NPC_BAND) { // Target ÀÌ mon ÀÎ °æ¿ì
|
||
pNpc = g_pMain->GetNpcPtr(m_Target.id);
|
||
if(pNpc == nullptr) {
|
||
InitTarget();
|
||
return -1;
|
||
}
|
||
if(pNpc->m_iHP <= 0 || pNpc->m_NpcState == NPC_DEAD) {
|
||
InitTarget();
|
||
return -1;
|
||
}
|
||
|
||
iTempRange = (float)m_byTracingRange; // ÀϽÃÀûÀ¸·Î º¸Á¤ÇÑ´Ù.
|
||
}
|
||
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr)
|
||
return -1;
|
||
|
||
int max_xx = pMap->GetMapSize();
|
||
int max_zz = pMap->GetMapSize();
|
||
|
||
int min_x = (int)(GetX() - iTempRange)/TILE_SIZE; if(min_x < 0) min_x = 0;
|
||
int min_z = (int)(GetZ() - iTempRange)/TILE_SIZE; if(min_z < 0) min_z = 0;
|
||
int max_x = (int)(GetX() + iTempRange)/TILE_SIZE; if(max_x > max_xx) max_x = max_xx;
|
||
int max_z = (int)(GetZ() + iTempRange)/TILE_SIZE; if(min_z > max_zz) min_z = max_zz;
|
||
|
||
// Targeting player
|
||
if (m_Target.id < NPC_BAND)
|
||
{
|
||
if (pUser == nullptr)
|
||
return -1;
|
||
|
||
CRect r = CRect(min_x, min_z, max_x+1, max_z+1);
|
||
if (!r.PtInRect((int)pUser->GetX()/TILE_SIZE, (int)pUser->GetZ()/TILE_SIZE))
|
||
{
|
||
TRACE("### Npc-GetTargetPath() User Fail return -1: [nid=%d] t_Name=%s, AttackPos=%d ###\n", GetID(), pUser->GetName().c_str(), m_byAttackPos);
|
||
return -1;
|
||
}
|
||
|
||
m_fStartPoint_X = GetX(); m_fStartPoint_Y = GetZ();
|
||
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
vUser.Set(pUser->GetX(), pUser->GetY(), pUser->GetZ());
|
||
|
||
IsSurround(pUser);
|
||
|
||
if(m_byAttackPos > 0 && m_byAttackPos < 9) {
|
||
fDegree = (float)((m_byAttackPos-1)*45);
|
||
fTargetDistance = 2.0f+m_proto->m_fBulk;
|
||
ComputeDestPos(vUser, fDegree, fTargetDistance, &vEnd22);
|
||
fSurX = vEnd22.x - vUser.x; fSurZ = vEnd22.z - vUser.z;
|
||
m_fEndPoint_X = vUser.x + fSurX; m_fEndPoint_Y = vUser.z + fSurZ;
|
||
}
|
||
else
|
||
{
|
||
CalcAdaptivePosition(vNpc, vUser, 2.0f+m_proto->m_fBulk, &vEnd22);
|
||
m_fEndPoint_X = vEnd22.x; m_fEndPoint_Y = vEnd22.z;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (pNpc == nullptr)
|
||
return -1;
|
||
|
||
CRect r = CRect(min_x, min_z, max_x+1, max_z+1);
|
||
if (!r.PtInRect((int)pNpc->GetX()/TILE_SIZE, (int)pNpc->GetZ()/TILE_SIZE))
|
||
{
|
||
TRACE("### Npc-GetTargetPath() Npc Fail return -1: [nid=%d] t_Name=%s, AttackPos=%d ###\n", GetID(), pNpc->GetName().c_str(), m_byAttackPos);
|
||
return -1;
|
||
}
|
||
|
||
m_fStartPoint_X = GetX(); m_fStartPoint_Y = GetZ();
|
||
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
vUser.Set(pNpc->GetX(), pNpc->GetY(), pNpc->GetZ());
|
||
|
||
CalcAdaptivePosition(vNpc, vUser, 2.0f+m_proto->m_fBulk, &vEnd22);
|
||
m_fEndPoint_X = vEnd22.x; m_fEndPoint_Y = vEnd22.z;
|
||
}
|
||
|
||
vDistance = vEnd22 - vNpc;
|
||
fDis = vDistance.Magnitude();
|
||
|
||
if(fDis <= m_fSecForMetor) {
|
||
ClearPathFindData();
|
||
m_bPathFlag = true;
|
||
m_iAniFrameIndex = 1;
|
||
m_pPoint[0].fXPos = m_fEndPoint_X;
|
||
m_pPoint[0].fZPos = m_fEndPoint_Y;
|
||
return true;
|
||
}
|
||
|
||
if((int)fDis > iTempRange) {
|
||
TRACE("Npc-GetTargetPath() searchrange over Fail return -1: [nid=%d,%s]\n", GetID(), GetName().c_str());
|
||
return -1;
|
||
}
|
||
|
||
|
||
if (GetType() != NPC_DUNGEON_MONSTER
|
||
&& hasTarget())
|
||
return 0;
|
||
|
||
CPoint start, end;
|
||
start.x = (int)(GetX()/TILE_SIZE) - min_x;
|
||
start.y = (int)(GetZ()/TILE_SIZE) - min_z;
|
||
end.x = (int)(vEnd22.x/TILE_SIZE) - min_x;
|
||
end.y = (int)(vEnd22.z/TILE_SIZE) - min_z;
|
||
|
||
if (GetType() == NPC_DUNGEON_MONSTER
|
||
&& !isInSpawnRange((int)vEnd22.x, (int)vEnd22.z))
|
||
return -1;
|
||
|
||
m_min_x = min_x;
|
||
m_min_y = min_z;
|
||
m_max_x = max_x;
|
||
m_max_y = max_z;
|
||
|
||
return PathFind(start, end, m_fSecForMetor);
|
||
}
|
||
|
||
time_t CNpc::Attack()
|
||
{
|
||
try {
|
||
if (isDead())
|
||
return -1;
|
||
|
||
int nRandom = 0, nPercent=1000, SinglePercent=5000;
|
||
bool bTeleport = false;
|
||
|
||
if (isNonAttackingObject())
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
InitTarget();
|
||
return 0;
|
||
}
|
||
|
||
if (GetProto()->m_byDirectAttack == 1)
|
||
return LongAndMagicAttack();
|
||
|
||
int nStandingTime = m_sStandTime;
|
||
auto result = IsCloseTarget(m_byAttackRange, AttackTypeMagic);
|
||
|
||
if (result == CloseTargetNotInRange)
|
||
{
|
||
m_sStepCount = 0;
|
||
m_byActionFlag = ATTACK_TO_TRACE;
|
||
m_NpcState = NPC_TRACING;
|
||
return m_sAttackDelay;
|
||
}
|
||
else if (result == CloseTargetInAttackRange)
|
||
{
|
||
if (GetProto()->m_byDirectAttack == 2)
|
||
return LongAndMagicAttack();
|
||
|
||
m_sStepCount = 0;
|
||
m_byActionFlag = ATTACK_TO_TRACE;
|
||
m_NpcState = NPC_TRACING;
|
||
return m_sAttackDelay;
|
||
}
|
||
else if (result == CloseTargetInvalid)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
InitTarget();
|
||
return 0;
|
||
}
|
||
|
||
int nDamage = 0;
|
||
uint16 nID = m_Target.id; // Target À» ±¸ÇÑ´Ù.
|
||
|
||
// Targeting player
|
||
if (nID < NPC_BAND)
|
||
{
|
||
CUser * pUser = g_pMain->GetUserPtr(nID);
|
||
if (pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| pUser->m_bInvisibilityType)
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;
|
||
}
|
||
|
||
// Don't attack GMs.
|
||
if (pUser->isGM())
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_MOVING;
|
||
return 0;
|
||
}
|
||
|
||
if (GetProto()->m_byMagicAttack > 3)
|
||
{
|
||
nRandom = myrand(1, 10000);
|
||
if (nRandom < nPercent)
|
||
{
|
||
_MAGIC_TABLE * myMagic = nullptr;
|
||
bool Magic1 = false, Magic2 = false;
|
||
uint8 SelectedMagic = 0;
|
||
myMagic = g_pMain->m_MagictableArray.GetData(m_proto->m_iMagic1);
|
||
|
||
if(myMagic == nullptr)
|
||
Magic1 = false;
|
||
else if(myMagic->bMoral == 10)
|
||
Magic1 = true;
|
||
|
||
_MAGIC_TABLE * myMagic2 = g_pMain->m_MagictableArray.GetData(m_proto->m_iMagic2);
|
||
|
||
if(myMagic2 == nullptr)
|
||
Magic2 = false;
|
||
else if(myMagic2->bMoral == 10)
|
||
Magic2 = true;
|
||
|
||
if(!Magic2 && Magic1)
|
||
SelectedMagic = 1;
|
||
else if(Magic2 && !Magic1)
|
||
SelectedMagic = 2;
|
||
else if(Magic1 && Magic2)
|
||
SelectedMagic = myrand(0,10000) > 4999 ? 1 : 2;
|
||
else
|
||
SelectedMagic = 0;
|
||
|
||
if(SelectedMagic > 0)
|
||
{
|
||
CNpcMagicProcess::MagicPacket(MAGIC_EFFECTING, SelectedMagic == 1 ? m_proto->m_iMagic1 : m_proto->m_iMagic2, GetID(), -1, int16(pUser->GetX()), int16(pUser->GetY()), int16(pUser->GetZ()));
|
||
return m_sAttackDelay + 1000;
|
||
}
|
||
}
|
||
else if(nRandom < SinglePercent)
|
||
{
|
||
_MAGIC_TABLE * myMagic = nullptr;
|
||
bool Magic1 = false, Magic2 = false, Magic3 = false;
|
||
uint32 SelectedMagic = 0;
|
||
myMagic = g_pMain->m_MagictableArray.GetData(m_proto->m_iMagic1);
|
||
|
||
if(myMagic == nullptr)
|
||
Magic1 = false;
|
||
else if(myMagic->bMoral == 7)
|
||
Magic1 = true;
|
||
|
||
_MAGIC_TABLE * myMagic2 = g_pMain->m_MagictableArray.GetData(m_proto->m_iMagic2);
|
||
|
||
if(myMagic2 == nullptr)
|
||
Magic2 = false;
|
||
else if(myMagic2->bMoral == 7)
|
||
Magic2 = true;
|
||
|
||
_MAGIC_TABLE * myMagic3 = g_pMain->m_MagictableArray.GetData(m_proto->m_iMagic3);
|
||
|
||
if(myMagic3 == nullptr)
|
||
Magic3 = false;
|
||
else if(myMagic3->bMoral == 7)
|
||
Magic3 = true;
|
||
|
||
std::vector <uint32> MagicList;
|
||
|
||
if(Magic1)
|
||
MagicList.push_back(m_proto->m_iMagic1);
|
||
if(Magic2)
|
||
MagicList.push_back(m_proto->m_iMagic2);
|
||
if(Magic3)
|
||
MagicList.push_back(m_proto->m_iMagic3);
|
||
|
||
if(MagicList.size() > 0)
|
||
{
|
||
uint8 SelectedSkill = 0;
|
||
SelectedSkill = myrand(1,MagicList.size());
|
||
|
||
if(SelectedSkill == 1)
|
||
SelectedMagic = m_proto->m_iMagic1;
|
||
|
||
if(SelectedSkill == 2)
|
||
SelectedMagic = m_proto->m_iMagic2;
|
||
|
||
if(SelectedSkill == 3)
|
||
SelectedMagic = m_proto->m_iMagic3;
|
||
|
||
if(SelectedMagic > 0)
|
||
{
|
||
CNpcMagicProcess::MagicPacket(MAGIC_EFFECTING, SelectedMagic, GetID(), pUser->GetID());
|
||
return m_sAttackDelay;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (GetProto()->m_byMagicAttack == 2 || GetProto()->m_byMagicAttack == 3)
|
||
{
|
||
nRandom = myrand(1, 10000);
|
||
if (nRandom < nPercent)
|
||
{
|
||
CNpcMagicProcess::MagicPacket(MAGIC_EFFECTING, m_proto->m_iMagic1, GetID(), pUser->GetID());
|
||
return m_sAttackDelay;
|
||
}
|
||
}
|
||
|
||
|
||
SendAttackRequest(pUser->GetID());
|
||
}
|
||
else // Targeting NPC
|
||
{
|
||
CNpc * pNpc = g_pMain->GetNpcPtr(nID);
|
||
if (pNpc == nullptr
|
||
|| pNpc->isDead())
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return 0;
|
||
}
|
||
|
||
if (isHealer()
|
||
&& !isHostileTo(pNpc))
|
||
{
|
||
m_NpcState = NPC_HEALING;
|
||
return 0;
|
||
}
|
||
|
||
SendAttackRequest(pNpc->GetID());
|
||
}
|
||
|
||
return m_sAttackDelay;
|
||
}
|
||
catch (...) {
|
||
printf("Error catched\n");
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
void CNpc::SendAttackRequest(int16 tid)
|
||
{
|
||
Packet result(AG_ATTACK_REQ);
|
||
result << GetID() << tid;
|
||
g_pMain->Send(&result);
|
||
}
|
||
|
||
time_t CNpc::LongAndMagicAttack()
|
||
{
|
||
int nStandingTime = m_sStandTime;
|
||
auto result = IsCloseTarget(m_byAttackRange, AttackTypeMagic);
|
||
if (result == CloseTargetNotInRange)
|
||
{
|
||
m_sStepCount = 0;
|
||
m_byActionFlag = ATTACK_TO_TRACE;
|
||
m_NpcState = NPC_TRACING;
|
||
return 0;
|
||
}
|
||
else if (result == CloseTargetInAttackRange && GetProto()->m_byDirectAttack == 1)
|
||
{
|
||
m_sStepCount = 0;
|
||
m_byActionFlag = ATTACK_TO_TRACE;
|
||
m_NpcState = NPC_TRACING;
|
||
return 0;
|
||
}
|
||
else if (result == CloseTargetInvalid)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
InitTarget();
|
||
return 0;
|
||
}
|
||
|
||
CNpc* pNpc = nullptr;
|
||
CUser* pUser = nullptr;
|
||
int nDamage = 0;
|
||
uint16 nID = m_Target.id;
|
||
|
||
if (nID < NPC_BAND)
|
||
{
|
||
pUser = g_pMain->GetUserPtr(nID);
|
||
if (pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| pUser->m_bInvisibilityType
|
||
// Don't cast skills on GMs.
|
||
|| pUser->isGM())
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return nStandingTime;
|
||
}
|
||
|
||
CNpcMagicProcess::MagicPacket(MAGIC_CASTING, m_proto->m_iMagic1, GetID(), pUser->GetID());
|
||
return m_sAttackDelay;
|
||
}
|
||
else // Target monster/NPC
|
||
{
|
||
CNpc * pNpc = g_pMain->GetNpcPtr(nID);
|
||
if (pNpc == nullptr
|
||
|| pNpc->isDead())
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return nStandingTime;
|
||
}
|
||
}
|
||
|
||
return m_sAttackDelay;
|
||
}
|
||
|
||
void CNpc::TracingAttack()
|
||
{
|
||
uint16 nID = m_Target.id;
|
||
if (nID < NPC_BAND) // Target is a player
|
||
{
|
||
CUser * pUser = g_pMain->GetUserPtr(nID);
|
||
if (pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| pUser->m_bInvisibilityType
|
||
|| pUser->isGM()
|
||
|| !GetMap()->canAttackOtherNation()
|
||
|| pUser->GetID() == m_oSocketID)
|
||
return;
|
||
}
|
||
else // Target is an NPC/monster
|
||
{
|
||
CNpc * pNpc = g_pMain->GetNpcPtr(nID);
|
||
if (pNpc == nullptr
|
||
|| pNpc->isDead())
|
||
return;
|
||
}
|
||
SendAttackRequest(nID);
|
||
}
|
||
|
||
void CNpc::MoveAttack()
|
||
{
|
||
if (!hasTarget())
|
||
return;
|
||
|
||
__Vector3 vUser, vNpc;
|
||
__Vector3 vDistance, vEnd22;
|
||
|
||
float fDis = 0.0f;
|
||
float fX = 0.0f, fZ = 0.0f;
|
||
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
|
||
if (m_Target.id < NPC_BAND) // Target is a player
|
||
{
|
||
__Vector3 vUser;
|
||
CUser * pUser = g_pMain->GetUserPtr(m_Target.id);
|
||
if (pUser == nullptr)
|
||
{
|
||
InitTarget();
|
||
return;
|
||
}
|
||
vUser.Set(pUser->GetX(), pUser->GetY(), pUser->GetZ());
|
||
|
||
CalcAdaptivePosition(vNpc, vUser, 2, &vEnd22);
|
||
|
||
if(m_byAttackPos > 0 && m_byAttackPos < 9)
|
||
{
|
||
fX = vUser.x + surround_fx[m_byAttackPos-1]; fZ = vUser.z + surround_fz[m_byAttackPos-1];
|
||
vEnd22.Set(fX, 0, fZ);
|
||
}
|
||
else
|
||
{
|
||
fX = vEnd22.x; fZ = vEnd22.z;
|
||
}
|
||
}
|
||
else // Target is an NPC/monster
|
||
{
|
||
CNpc * pNpc = g_pMain->GetNpcPtr(m_Target.id);
|
||
if (pNpc == nullptr)
|
||
{
|
||
InitTarget();
|
||
return;
|
||
}
|
||
vUser.Set(pNpc->GetX(), pNpc->GetY(), pNpc->GetZ());
|
||
|
||
CalcAdaptivePosition(vNpc, vUser, 2, &vEnd22);
|
||
fX = vEnd22.x; fZ = vEnd22.z;
|
||
}
|
||
|
||
vDistance = vUser - vNpc;
|
||
fDis = vDistance.Magnitude();
|
||
|
||
if ((int)fDis < 3) return;
|
||
|
||
vDistance = vEnd22 - vNpc;
|
||
fDis = vDistance.Magnitude();
|
||
m_curx = vEnd22.x;
|
||
m_curz = vEnd22.z;
|
||
|
||
if (GetX() < 0 || GetZ() < 0)
|
||
{
|
||
TRACE("Npc-MoveAttack : nid=(%d, %s), x=%.2f, z=%.2f\n",
|
||
GetID(), GetName().c_str(), GetX(), GetZ());
|
||
}
|
||
|
||
// Move to target... then stop (this is really awkward behaviour.)
|
||
SendMoveResult(GetX(), GetY(), GetZ(), (float)m_sSpeed / 1000);
|
||
|
||
RegisterRegion(GetX(), GetZ());
|
||
|
||
m_fEndPoint_X = GetX();
|
||
m_fEndPoint_Y = GetZ();
|
||
}
|
||
|
||
bool CNpc::IsChangePath()
|
||
{
|
||
float fCurX=0.0f, fCurZ=0.0f;
|
||
GetTargetPos(fCurX, fCurZ);
|
||
|
||
__Vector3 vStart, vEnd;
|
||
vStart.Set(m_fEndPoint_X, 0, m_fEndPoint_Y);
|
||
vEnd.Set(fCurX, 0, fCurZ);
|
||
|
||
float fDis = GetDistance(vStart, vEnd);
|
||
float fCompDis = 3.0f;
|
||
|
||
if(fDis < fCompDis)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CNpc::GetTargetPos(float& x, float& z)
|
||
{
|
||
if (!hasTarget())
|
||
return false;
|
||
|
||
Unit * pUnit = g_pMain->GetUnitPtr(m_Target.id);
|
||
if (pUnit == nullptr)
|
||
return false;
|
||
|
||
x = pUnit->GetX();
|
||
z = pUnit->GetZ();
|
||
|
||
return true;
|
||
}
|
||
|
||
// Target °ú NPC °£¿¡ ±æÃ£±â¸¦ ´Ù½ÃÇÑ´Ù.
|
||
bool CNpc::ResetPath()
|
||
{
|
||
float cur_x, cur_z;
|
||
GetTargetPos(cur_x, cur_z);
|
||
|
||
// TRACE("ResetPath : user pos ,, x=%.2f, z=%.2f\n", cur_x, cur_z);
|
||
|
||
m_Target.x = cur_x;
|
||
m_Target.z = cur_z;
|
||
|
||
int nValue = GetTargetPath();
|
||
if(nValue == -1) // Ÿ°ÙÀÌ ¾ø¾îÁö°Å³ª,, ¸Ö¾îÁ³À½À¸·Î...
|
||
{
|
||
TRACE("Npc-ResetPath Fail - target_x = %.2f, z=%.2f, value=%d\n", m_Target.x, m_Target.z, nValue);
|
||
return false;
|
||
}
|
||
else if(nValue == 0) // Ÿ°Ù ¹æÇâÀ¸·Î ¹Ù·Î °£´Ù..
|
||
{
|
||
m_fSecForMetor = m_fSpeed_2; // °ø°İÀ϶§´Â ¶Ù´Â ¼Óµµ·Î...
|
||
IsNoPathFind(m_fSecForMetor);
|
||
}
|
||
|
||
//TRACE("Npc-ResetPath - target_x = %.2f, z=%.2f, value=%d\n", m_Target.x, m_Target.z, nValue);
|
||
|
||
return true;
|
||
}
|
||
|
||
void CNpc::ChangeTarget(int nAttackType, CUser *pUser)
|
||
{
|
||
int preDamage, lastDamage;
|
||
__Vector3 vUser, vNpc;
|
||
float fDistance1 = 0.0f, fDistance2 = 0.0f;
|
||
int iRandom = myrand(0, 100);
|
||
|
||
if (pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| !isHostileTo(pUser)
|
||
|| pUser->m_bInvisibilityType
|
||
|| pUser->isGM()
|
||
|| m_NpcState == NPC_FAINTING
|
||
|| isNonAttackingObject())
|
||
return;
|
||
|
||
CUser *preUser = nullptr;
|
||
if (hasTarget() && m_Target.id < NPC_BAND)
|
||
preUser = g_pMain->GetUserPtr(m_Target.id);
|
||
|
||
if (pUser == preUser)
|
||
{
|
||
if (m_bHasFriends || GetType() == NPC_BOSS)
|
||
FindFriend(GetType() == NPC_BOSS ? MonSearchAny : MonSearchSameFamily);
|
||
|
||
return;
|
||
}
|
||
|
||
if (preUser != nullptr)
|
||
{
|
||
preDamage = 0; lastDamage = 0;
|
||
|
||
if (iRandom >= 0 && iRandom < 50)
|
||
{
|
||
preDamage = preUser->GetDamage(this, nullptr, true);
|
||
lastDamage = pUser->GetDamage(this, nullptr, true);
|
||
|
||
if (preDamage > lastDamage)
|
||
return;
|
||
}
|
||
else if(iRandom >= 50 && iRandom < 80)
|
||
{
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
vUser.Set(preUser->GetX(), 0, preUser->GetZ());
|
||
fDistance1 = GetDistance(vNpc, vUser);
|
||
vUser.Set(pUser->GetX(), 0, pUser->GetZ());
|
||
fDistance2 = GetDistance(vNpc, vUser);
|
||
|
||
if (fDistance2 > fDistance1)
|
||
return;
|
||
}
|
||
else if (iRandom >= 80 && iRandom < 95)
|
||
{
|
||
preDamage = GetDamage(preUser, nullptr, true); /* preview the amount of damage that might be dealt for comparison */
|
||
lastDamage = GetDamage(pUser, nullptr, true);
|
||
//TRACE("Npc-changeTarget 333 - iRandom=%d, pre=%d, last=%d\n", iRandom, preDamage, lastDamage);
|
||
if(preDamage > lastDamage) return;
|
||
}
|
||
}
|
||
else if(preUser == nullptr && nAttackType == 1004) return; // Heal magic¿¡ ¹İÀÀÇÏÁö ¾Êµµ·Ï..
|
||
|
||
m_Target.id = pUser->GetID();
|
||
m_Target.bSet = true;
|
||
m_Target.x = pUser->GetX();
|
||
m_Target.y = pUser->GetY();
|
||
m_Target.z = pUser->GetZ();
|
||
|
||
//TRACE("Npc-changeTarget - target_x = %.2f, z=%.2f\n", m_Target.x, m_Target.z);
|
||
|
||
int nValue = 0;
|
||
// ¾î½½·· °Å¸®´Âµ¥ °ø°İÇÏ¸é ¹Ù·Î ¹İ°İ
|
||
if(m_NpcState == NPC_STANDING || m_NpcState == NPC_MOVING || m_NpcState == NPC_SLEEPING)
|
||
{ // °¡±îÀÌ ÀÖÀ¸¸é ¹İ°İÀ¸·Î À̾îÁö±¸
|
||
if(IsCloseTarget(pUser, m_byAttackRange) == true)
|
||
{
|
||
m_NpcState = NPC_FIGHTING;
|
||
m_Delay = 0;
|
||
}
|
||
else // ¹Ù·Î µµ¸Á°¡¸é ÁÂÇ¥¸¦ °»½ÅÇϰí ÃßÀû
|
||
{
|
||
nValue = GetTargetPath(1);
|
||
if(nValue == 1) // ¹İ°İ µ¿ÀÛÈÄ ¾à°£ÀÇ µô·¹ÀÌ ½Ã°£ÀÌ ÀÖÀ½
|
||
{
|
||
m_NpcState = NPC_TRACING;
|
||
m_Delay = 0;
|
||
}
|
||
else if(nValue == -1)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
m_Delay = 0;
|
||
}
|
||
else if(nValue == 0)
|
||
{
|
||
m_fSecForMetor = m_fSpeed_2; // °ø°İÀ϶§´Â ¶Ù´Â ¼Óµµ·Î...
|
||
IsNoPathFind(m_fSecForMetor);
|
||
m_NpcState = NPC_TRACING;
|
||
m_Delay = 0;
|
||
}
|
||
}
|
||
}
|
||
// else m_NpcState = NPC_ATTACKING; // ÇÑÂü °ø°İÇϴµ¥ ´©°¡ ¹æÇØÇÏ¸é ¸ñÇ¥¸¦ ¹Ù²Ş
|
||
|
||
if (m_bHasFriends || GetType() == NPC_BOSS)
|
||
FindFriend(GetType() == NPC_BOSS ? MonSearchAny : MonSearchSameFamily);
|
||
}
|
||
|
||
// ³ª¸¦ °ø°İÇÑ Npc¸¦ Ÿ°ÙÀ¸·Î »ï´Â´Ù.(±âÁØ : ·¾°ú HP¸¦ ±âÁØÀ¸·Î ¼±Á¤)
|
||
void CNpc::ChangeNTarget(CNpc *pNpc)
|
||
{
|
||
int preDamage, lastDamage;
|
||
__Vector3 vMonster, vNpc;
|
||
float fDist = 0.0f;
|
||
|
||
if (pNpc == nullptr
|
||
|| pNpc->m_NpcState == NPC_DEAD
|
||
|| !hasTarget()
|
||
|| m_Target.id < NPC_BAND)
|
||
return;
|
||
|
||
CNpc *preNpc = g_pMain->GetNpcPtr(m_Target.id);
|
||
if (preNpc == nullptr
|
||
|| pNpc == preNpc) return;
|
||
|
||
preDamage = GetDamage(preNpc, nullptr, true); /* preview the damage that might be dealt for comparison */
|
||
lastDamage = GetDamage(pNpc, nullptr, true);
|
||
|
||
vNpc.Set(GetX(), GetY(), GetZ());
|
||
vMonster.Set(preNpc->GetX(), 0, preNpc->GetZ());
|
||
fDist = GetDistance(vNpc, vMonster);
|
||
preDamage = (int)((double)preDamage/fDist + 0.5);
|
||
vMonster.Set(pNpc->GetX(), 0, pNpc->GetZ());
|
||
fDist = GetDistance(vNpc, vMonster);
|
||
lastDamage = (int)((double)lastDamage/fDist + 0.5);
|
||
|
||
if(preDamage > lastDamage) return;
|
||
|
||
m_Target.id = pNpc->GetID();
|
||
m_Target.bSet = true;
|
||
m_Target.x = pNpc->GetX();
|
||
m_Target.y = pNpc->GetZ();
|
||
m_Target.z = pNpc->GetZ();
|
||
|
||
int nValue = 0;
|
||
if (m_NpcState == NPC_STANDING || m_NpcState == NPC_MOVING || m_NpcState == NPC_SLEEPING)
|
||
{
|
||
if (IsCloseTarget(m_byAttackRange, AttackTypeNone) == 1)
|
||
{
|
||
m_NpcState = NPC_FIGHTING;
|
||
m_Delay = 0;
|
||
}
|
||
else
|
||
{
|
||
nValue = GetTargetPath();
|
||
if (nValue == 1)
|
||
{
|
||
m_NpcState = NPC_TRACING;
|
||
m_Delay = 0;
|
||
}
|
||
else if(nValue == -1)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
m_Delay = 0;
|
||
}
|
||
else if(nValue == 0)
|
||
{
|
||
m_fSecForMetor = m_fSpeed_2;
|
||
IsNoPathFind(m_fSecForMetor);
|
||
m_NpcState = NPC_TRACING;
|
||
m_Delay = 0;
|
||
}
|
||
}
|
||
}
|
||
// else m_NpcState = NPC_ATTACKING; // ÇÑÂü °ø°İÇϴµ¥ ´©°¡ ¹æÇØÇÏ¸é ¸ñÇ¥¸¦ ¹Ù²Ş
|
||
|
||
if (m_bHasFriends)
|
||
FindFriend();
|
||
}
|
||
|
||
void CNpc::RecvAttackReq(int nDamage, uint16 sAttackerID, AttributeType attributeType /*= AttributeNone*/)
|
||
{
|
||
m_DamagedUserListCount = 0;
|
||
for (int i = 0; i < NPC_HAVE_USER_LIST; i++){
|
||
|
||
if ((uint32(UNIXTIME) - m_DamagedUserList[i].lastdamagedt) < 60)
|
||
m_DamagedUserListCount++;
|
||
|
||
|
||
if ((uint32(UNIXTIME) - m_DamagedUserList[i].lastdamagedt) > 60 && m_DamagedUserList[i].GetID != sAttackerID && m_DamagedUserList[i].Damage > 0)
|
||
m_DamagedUserList[i].Reset();
|
||
|
||
}
|
||
|
||
|
||
|
||
if (isDead() || nDamage < 0)
|
||
return;
|
||
|
||
int MyDamage = 0;
|
||
MyDamage = nDamage;
|
||
|
||
CUser * pAttacker = g_pMain->GetUserPtr(sAttackerID);
|
||
|
||
if (pAttacker != nullptr
|
||
&& pAttacker->isPlayer())
|
||
{
|
||
if (IsDamagedUserList(pAttacker)){
|
||
for (int i = 0; i < NPC_HAVE_USER_LIST; i++){
|
||
|
||
if (m_DamagedUserList[i].GetID == pAttacker->GetID()){
|
||
m_DamagedUserList[i].Damage += MyDamage;
|
||
m_DamagedUserList[i].lastdamagedt = uint32(UNIXTIME);
|
||
}
|
||
}
|
||
}else{
|
||
if(m_DamagedUserListCount < NPC_HAVE_USER_LIST){
|
||
m_DamagedUserList[m_DamagedUserListCount].GetID = sAttackerID;
|
||
m_DamagedUserList[m_DamagedUserListCount].Damage = MyDamage;
|
||
m_DamagedUserList[m_DamagedUserListCount].lastdamagedt = uint32(UNIXTIME);
|
||
m_DamagedUserListCount++;
|
||
}
|
||
else
|
||
{
|
||
for (int i = 0; i < NPC_HAVE_USER_LIST; i++){
|
||
|
||
if (m_DamagedUserList[i].GetID == -1){
|
||
m_DamagedUserList[i].GetID = sAttackerID;
|
||
m_DamagedUserList[i].Damage = MyDamage;
|
||
m_DamagedUserList[i].lastdamagedt = uint32(UNIXTIME);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Unit * pAttackers = g_pMain->GetUnitPtr(sAttackerID);
|
||
|
||
m_TotalDamage += nDamage;
|
||
HpChange(-nDamage, pAttackers, false);
|
||
|
||
if (pAttackers == nullptr)
|
||
return;
|
||
|
||
if (!pAttackers->isPlayer())
|
||
{
|
||
ChangeNTarget(TO_NPC(pAttackers));
|
||
return;
|
||
}
|
||
|
||
if (m_NpcState != NPC_FAINTING)
|
||
{
|
||
int iRandom = myrand(1, 100);
|
||
int sDamage=0;
|
||
|
||
switch (attributeType)
|
||
{
|
||
case 1:
|
||
sDamage = (int)(10 + (40 - 40 * ((double)m_sFireR / 80)));
|
||
break;
|
||
case 2:
|
||
sDamage = (int)(10 + (40 - 40 * ((double)m_sColdR)));
|
||
break;
|
||
case 3:
|
||
sDamage = (int)(10 + (40 - 40 * ((double)m_sLightningR / 80)));
|
||
break;
|
||
case 4:
|
||
sDamage = (int)(10 + (40 - 40 * ((double)m_sMagicR / 80)));
|
||
break;
|
||
case 5:
|
||
sDamage = (int)(10 + (40 - 40 * ((double)m_sDiseaseR / 80)));
|
||
break;
|
||
case 6:
|
||
sDamage = (int)(10 + (40 - 40 * ((double)m_sPoisonR / 80)));
|
||
break;
|
||
sDamage = nDamage;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (COMPARE(iRandom, 0, sDamage))
|
||
{
|
||
m_NpcState = NPC_FAINTING;
|
||
m_Delay = 0;
|
||
m_tFaintingTime = UNIXTIME;
|
||
}
|
||
else
|
||
{
|
||
ChangeTarget(0, TO_USER(pAttacker));
|
||
}
|
||
}
|
||
}
|
||
|
||
void CNpc::NpcCalling(float fDis,float fDistance, __Vector3 oPos, __Vector3 cPost){
|
||
float bChamber = TILE_SIZE + 3;
|
||
|
||
|
||
if (m_bSpeedAmount > 0 && m_bSpeedAmount < 100) {
|
||
float pTile = (float)(10 - (m_bSpeedAmount / 10) - TILE_SIZE);
|
||
bChamber -= (pTile >= 1 ? pTile + 2 : 0);
|
||
}
|
||
else if (m_bSpeedAmount > 100) {
|
||
float pTile = (float)(10 - (m_bSpeedAmount / 10) - TILE_SIZE);
|
||
bChamber += (pTile >= 1 ? pTile + 2 : 0);
|
||
}
|
||
|
||
bChamber = fDis / bChamber;
|
||
if (bChamber > 15)
|
||
bChamber = 15;
|
||
|
||
m_curx = (cPost.x / bChamber) + oPos.x;
|
||
m_curz = (cPost.z / bChamber) + oPos.z;
|
||
|
||
|
||
float tempSpeed = m_fSecForRealMoveMetor;
|
||
|
||
|
||
if (tempSpeed == 0)
|
||
tempSpeed = m_fSpeed_1;
|
||
|
||
|
||
float nMoveSpeed = (float)(tempSpeed / ((float)m_sSpeed / 1000.0f));
|
||
|
||
if (nMoveSpeed != 0) {
|
||
SendMoveResult(m_curx, m_fPrevY, m_curz, (float)(nMoveSpeed));
|
||
}
|
||
}
|
||
|
||
void CNpc::HpChange(int amount, Unit *pAttacker /*= nullptr*/, bool bSendToGameServer /*= true*/)
|
||
{
|
||
uint16 tid = (pAttacker != nullptr ? pAttacker->GetID() : -1);
|
||
int32 oldHP = m_iHP;
|
||
|
||
// Implement damage/HP cap.
|
||
if (amount < -MAX_DAMAGE)
|
||
amount = -MAX_DAMAGE;
|
||
else if (amount > MAX_DAMAGE)
|
||
amount = MAX_DAMAGE;
|
||
|
||
if (amount < 0 && -amount >= m_iHP)
|
||
m_iHP = 0;
|
||
else if (amount >= 0 && m_iHP + amount > m_iMaxHP)
|
||
m_iHP = m_iMaxHP;
|
||
else
|
||
m_iHP += amount;
|
||
|
||
if (bSendToGameServer)
|
||
{
|
||
Packet result(AG_NPC_HP_CHANGE);
|
||
result << GetID() << tid << m_iHP << amount;
|
||
g_pMain->Send(&result);
|
||
}
|
||
|
||
if (m_iHP == 0)
|
||
Dead(pAttacker);
|
||
}
|
||
|
||
void CNpc::SendExpToUserList()
|
||
{
|
||
if (GetMap() == nullptr)
|
||
return;
|
||
|
||
std::string strMaxDamageUser;
|
||
int nMaxDamage = 0;
|
||
|
||
|
||
std::map<CUser *, int> filteredDamageList;
|
||
std::map<uint16, CUser *> partyIndex;
|
||
|
||
// Filter the damage list first, so we only send one packet per party.
|
||
// Rewards are shared based upon the total amount of damage dealt.
|
||
|
||
// NOTE: If a player logs out & another takes its place, it is currently possible
|
||
// For this new session to be counted as the old when the mob is killed.
|
||
// This does, however, requires the player to be in the same zone, in range at the time
|
||
// of the mob's death -- so it is rather unlikely. We should fix this later.
|
||
if (m_DamagedUserListCount > 0){
|
||
for (int i = 0; i < NPC_HAVE_USER_LIST; i++){
|
||
|
||
if (m_DamagedUserList[i].GetID < 0 || m_DamagedUserList[i].GetID > NPC_BAND)
|
||
continue;
|
||
|
||
if ((uint32(UNIXTIME) - m_DamagedUserList[i].lastdamagedt) > 60)
|
||
continue;
|
||
|
||
CUser * pUser = g_pMain->GetUserPtr(m_DamagedUserList[i].GetID);
|
||
if (pUser == nullptr
|
||
|| !isInRangeSlow(pUser, NPC_EXP_RANGE))
|
||
continue;
|
||
|
||
// Not in a party, we can add them to the list as a solo attacker.
|
||
if (!pUser->isInParty())
|
||
{
|
||
|
||
auto filteredDamageListItr = filteredDamageList.find(pUser);
|
||
|
||
if(filteredDamageListItr == filteredDamageList.end()){
|
||
filteredDamageList.insert(std::make_pair(pUser, m_DamagedUserList[i].Damage));
|
||
}else{
|
||
filteredDamageList[pUser] = m_DamagedUserList[i].Damage;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// In a party, so check if another party member is already in the list first.
|
||
auto partyItr = partyIndex.find(pUser->GetPartyID());
|
||
|
||
// No other party member, so add us to the filtered damage list & the party index
|
||
// for future reference.
|
||
if (partyItr == partyIndex.end() && pUser->isInParty())
|
||
{
|
||
|
||
filteredDamageList[pUser] = m_DamagedUserList[i].Damage;
|
||
|
||
partyIndex.insert(std::make_pair(pUser->GetPartyID(), pUser));
|
||
}
|
||
// There is another pf pir party members in the damage list already, so just add
|
||
// to their damage total.
|
||
else
|
||
{
|
||
filteredDamageList[partyItr->second] += m_DamagedUserList[i].Damage;
|
||
}
|
||
|
||
}
|
||
}
|
||
// Now we can go through the filtered list and tell the game server to distribute rewards
|
||
// for the kill.
|
||
if (filteredDamageList.size() != 0){
|
||
foreach (itr, filteredDamageList)
|
||
{
|
||
CUser * pUser = itr->first;
|
||
if (pUser->isPlayer() || pUser->isGM()){
|
||
Packet result(AG_USER_EXP);
|
||
result << pUser->GetID()
|
||
<< GetID()
|
||
// target's damage vs total damage dealt to the NPC
|
||
<< itr->second << m_TotalDamage
|
||
<< m_proto->m_iExp << m_proto->m_iLoyalty;
|
||
g_pMain->Send(&result);
|
||
|
||
if (itr->second > nMaxDamage)
|
||
{
|
||
m_sMaxDamageUserid = pUser->GetID();
|
||
strMaxDamageUser = pUser->GetName();
|
||
nMaxDamage = itr->second;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (g_pMain->m_byBattleEvent == BATTLEZONE_OPEN
|
||
&& !strMaxDamageUser.empty()
|
||
&& m_bySpecialType >= NpcSpecialTypeKarusWarder1 && m_bySpecialType <= NpcSpecialTypeElmoradKeeper)
|
||
{
|
||
Packet result(AG_BATTLE_EVENT, uint8(BATTLE_EVENT_MAX_USER));
|
||
|
||
switch (m_bySpecialType)
|
||
{
|
||
case NpcSpecialTypeKarusWarder1: result << uint8(3); g_pMain->m_sKillKarusNpc++; break;
|
||
case NpcSpecialTypeKarusWarder2: result << uint8(4); g_pMain->m_sKillKarusNpc++; break;
|
||
case NpcSpecialTypeElmoradWarder1: result << uint8(5); g_pMain->m_sKillElmoNpc++; break;
|
||
case NpcSpecialTypeElmoradWarder2: result << uint8(6); g_pMain->m_sKillElmoNpc++; break;
|
||
case NpcSpecialTypeKarusKeeper: result << uint8(7); g_pMain->m_sKillKarusNpc++; break;
|
||
case NpcSpecialTypeElmoradKeeper: result << uint8(8); g_pMain->m_sKillElmoNpc++; break;
|
||
}
|
||
|
||
result.SByte();
|
||
result << strMaxDamageUser;
|
||
g_pMain->Send(&result);
|
||
|
||
bool bKarusComplete = (g_pMain->m_sKillKarusNpc == GetMap()->m_sKarusRoom),
|
||
bElMoradComplete = (g_pMain->m_sKillElmoNpc == GetMap()->m_sElmoradRoom);
|
||
|
||
if (bKarusComplete || bElMoradComplete)
|
||
{
|
||
result.clear();
|
||
result << uint8(BATTLE_EVENT_RESULT)
|
||
<< uint8(bKarusComplete ? KARUS : ELMORAD)
|
||
<< strMaxDamageUser;
|
||
g_pMain->Send(&result);
|
||
}
|
||
}
|
||
}
|
||
bool CNpc::IsCloseTarget(CUser *pUser, int nRange)
|
||
{
|
||
if (pUser == nullptr
|
||
|| pUser->isDead()
|
||
|| !isInRangeSlow(pUser, nRange * 2.0f))
|
||
return false;
|
||
|
||
m_Target.id = pUser->GetID();
|
||
m_Target.bSet = true;
|
||
m_Target.x = pUser->GetX();
|
||
m_Target.y = pUser->GetY();
|
||
m_Target.z = pUser->GetZ();
|
||
|
||
return true;
|
||
}
|
||
|
||
int CNpc::FindFriend(MonSearchType type)
|
||
{
|
||
CNpc* pNpc = nullptr;
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr
|
||
|| m_bySearchRange == 0
|
||
|| (type != MonSearchNeedsHealing && hasTarget()))
|
||
return 0;
|
||
|
||
int min_x = (int)(GetX() - m_bySearchRange)/VIEW_DIST; if(min_x < 0) min_x = 0;
|
||
int min_z = (int)(GetZ() - m_bySearchRange)/VIEW_DIST; if(min_z < 0) min_z = 0;
|
||
int max_x = (int)(GetX() + m_bySearchRange)/VIEW_DIST; if(max_x > pMap->GetXRegionMax()) max_x = pMap->GetXRegionMax();
|
||
int max_z = (int)(GetZ() + m_bySearchRange)/VIEW_DIST; if(min_z > pMap->GetZRegionMax()) min_z = pMap->GetZRegionMax();
|
||
|
||
int search_x = max_x - min_x + 1;
|
||
int search_z = max_z - min_z + 1;
|
||
|
||
int i, j, count = 0;
|
||
_TargetHealer arHealer[9];
|
||
for(i=0; i<9; i++) {
|
||
arHealer[i].sNID = -1;
|
||
arHealer[i].sValue = 0;
|
||
}
|
||
|
||
for (i = 0; i < search_x; i++)
|
||
for (j = 0; j < search_z; j++)
|
||
FindFriendRegion(min_x+i, min_z+j, pMap, &arHealer[count], type);
|
||
|
||
int iValue = 0, iMonsterNid = 0;
|
||
for (i=0; i<9; i++)
|
||
{
|
||
if (iValue < arHealer[i].sValue)
|
||
{
|
||
iValue = arHealer[i].sValue;
|
||
iMonsterNid = arHealer[i].sNID;
|
||
}
|
||
}
|
||
|
||
if (iMonsterNid != 0)
|
||
{
|
||
m_Target.id = iMonsterNid;
|
||
m_Target.bSet = true;
|
||
return iMonsterNid;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
void CNpc::FindFriendRegion(int x, int z, MAP* pMap, _TargetHealer* pHealer, MonSearchType type)
|
||
{
|
||
if (x < 0 || z < 0 || x > pMap->GetXRegionMax() || z > pMap->GetZRegionMax())
|
||
{
|
||
TRACE("#### Npc-FindFriendRegion() Fail : [nid=%d, sid=%d], nRX=%d, nRZ=%d #####\n", GetID(), GetProtoID(), x, z);
|
||
return;
|
||
}
|
||
|
||
Guard lock(pMap->m_lock);
|
||
CRegion *pRegion = &pMap->m_ppRegion[x][z];
|
||
|
||
if (pRegion == nullptr)
|
||
return;
|
||
|
||
__Vector3 vStart, vEnd;
|
||
float fDis = 0.0f,
|
||
fSearchRange = (type == MonSearchNeedsHealing ? (float)m_byAttackRange : (float)m_byTracingRange);
|
||
int iValue = 0;
|
||
|
||
vStart.Set(GetX(), GetY(), GetZ());
|
||
|
||
foreach_stlmap (itr, pRegion->m_RegionNpcArray)
|
||
{
|
||
CNpc * pNpc = g_pMain->GetNpcPtr(itr->first);
|
||
|
||
if (pNpc != nullptr && pNpc->m_NpcState != NPC_DEAD && pNpc->GetID() != GetID())
|
||
{
|
||
vEnd.Set(pNpc->GetX(), pNpc->GetY(), pNpc->GetZ());
|
||
fDis = GetDistance(vStart, vEnd);
|
||
|
||
if (fDis <= fSearchRange)
|
||
{
|
||
if (type == MonSearchAny)
|
||
{
|
||
if (GetID() != pNpc->GetID())
|
||
{
|
||
//if (pNpc->hasTarget() && pNpc->m_NpcState == NPC_FIGHTING)
|
||
// continue;
|
||
|
||
pNpc->m_Target.id = m_Target.id;
|
||
pNpc->m_Target.bSet = true;
|
||
pNpc->m_Target.x = m_Target.x;
|
||
pNpc->m_Target.y = m_Target.y;
|
||
pNpc->m_Target.z = m_Target.z;
|
||
pNpc->NpcStrategy(NPC_ATTACK_SHOUT);
|
||
}
|
||
}
|
||
else if (type == MonSearchSameFamily)
|
||
{
|
||
if (pNpc->m_bHasFriends && GetID() != pNpc->GetID() && pNpc->m_proto->m_byFamilyType == m_proto->m_byFamilyType) {
|
||
//if (pNpc->hasTarget() && pNpc->m_NpcState == NPC_FIGHTING)
|
||
// continue; 10.12.16
|
||
|
||
pNpc->m_Target.id = m_Target.id;
|
||
pNpc->m_Target.bSet = true;
|
||
pNpc->m_Target.x = m_Target.x;
|
||
pNpc->m_Target.y = m_Target.y;
|
||
pNpc->m_Target.z = m_Target.z;
|
||
pNpc->NpcStrategy(NPC_ATTACK_SHOUT);
|
||
}
|
||
}
|
||
else if (type == MonSearchNeedsHealing)
|
||
{
|
||
if (pHealer == nullptr)
|
||
return;
|
||
|
||
int iHP = (int)(pNpc->m_iMaxHP * 0.9);
|
||
if(pNpc->m_iHP <= iHP) { // HP üũ
|
||
int iCompValue = (int)((pNpc->m_iMaxHP - pNpc->m_iHP) / (pNpc->m_iMaxHP * 0.01));
|
||
if (iValue < iCompValue) {
|
||
iValue = iCompValue;
|
||
pHealer->sNID = pNpc->GetID();
|
||
pHealer->sValue = iValue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CNpc::NpcStrategy(uint8 type)
|
||
{
|
||
switch(type)
|
||
{
|
||
case NPC_ATTACK_SHOUT:
|
||
m_NpcState = NPC_TRACING;
|
||
m_Delay = m_sSpeed;//STEP_DELAY;
|
||
m_fDelayTime = getMSTime();
|
||
break;
|
||
}
|
||
}
|
||
|
||
void CNpc::FillNpcInfo(Packet & result)
|
||
{
|
||
result << uint8(1)
|
||
<< GetID() << GetProtoID() << m_proto->m_sPid
|
||
<< m_sSize << m_iWeapon_1 << m_iWeapon_2
|
||
<< GetZoneID() << GetName()
|
||
<< (m_proto->m_byGroupSpecial > 0 ? m_proto->m_byGroupSpecial : GetNation()) << GetLevel()
|
||
<< GetX() << GetZ() << GetY() << m_byDirection
|
||
<< GetType()
|
||
<< m_iSellingGroup << m_iMaxHP << m_iHP
|
||
<< m_byGateOpen
|
||
<< m_fTotalHitrate << m_fTotalEvasionrate
|
||
<< m_sTotalAc << m_sTotalHit
|
||
<< m_byObjectType << m_byTrapNumber
|
||
<< m_bMonster << m_oSocketID << m_bEventRoom
|
||
// Include resistance data, note that we don't need to send modified amounts as
|
||
// there's no skill handling here - it happens in GameServer.
|
||
// We will probably need to update the AI server (from GameServer) with this data.
|
||
<< m_sFireR << m_sColdR << m_sLightningR
|
||
<< m_sMagicR << m_sDiseaseR << m_sPoisonR
|
||
<< m_bIsEventNpc << nIsPet << strPetName << strUserName << nSerial << UserId;
|
||
}
|
||
|
||
int CNpc::GetDir(float x1, float z1, float x2, float z2)
|
||
{
|
||
int nDir; // 3 4 5
|
||
// 2 8 6
|
||
// 1 0 7
|
||
|
||
int nDirCount = 0;
|
||
|
||
int x11 = (int)x1 / TILE_SIZE;
|
||
int y11 = (int)z1 / TILE_SIZE;
|
||
int x22 = (int)x2 / TILE_SIZE;
|
||
int y22 = (int)z2 / TILE_SIZE;
|
||
|
||
int deltax = x22 - x11;
|
||
int deltay = y22 - y11;
|
||
|
||
int fx = ((int)x1/TILE_SIZE) * TILE_SIZE;
|
||
int fy = ((int)z1/TILE_SIZE) * TILE_SIZE;
|
||
|
||
float add_x = x1 - fx;
|
||
float add_y = z1 - fy;
|
||
|
||
if (deltay==0) {
|
||
if (x22>x11) nDir = DIR_RIGHT;
|
||
else nDir = DIR_LEFT;
|
||
goto result_value;
|
||
}
|
||
if (deltax==0)
|
||
{
|
||
if (y22>y11) nDir = DIR_DOWN;
|
||
else nDir = DIR_UP;
|
||
goto result_value;
|
||
}
|
||
else
|
||
{
|
||
if (y22>y11)
|
||
{
|
||
if (x22>x11)
|
||
nDir = DIR_DOWNRIGHT;
|
||
else
|
||
nDir = DIR_DOWNLEFT;
|
||
}
|
||
else
|
||
{
|
||
if (x22 > x11)
|
||
nDir = DIR_UPRIGHT;
|
||
else
|
||
nDir = DIR_UPLEFT;
|
||
}
|
||
}
|
||
|
||
result_value:
|
||
|
||
switch(nDir)
|
||
{
|
||
case DIR_DOWN:
|
||
m_fAdd_x = add_x;
|
||
m_fAdd_z = 3;
|
||
break;
|
||
case DIR_DOWNLEFT:
|
||
m_fAdd_x = 1;
|
||
m_fAdd_z = 3;
|
||
break;
|
||
case DIR_LEFT:
|
||
m_fAdd_x = 1;
|
||
m_fAdd_z = add_y;
|
||
break;
|
||
case DIR_UPLEFT:
|
||
m_fAdd_x = 1;
|
||
m_fAdd_z = 1;
|
||
break;
|
||
case DIR_UP:
|
||
m_fAdd_x = add_x;
|
||
m_fAdd_z = 1;
|
||
break;
|
||
case DIR_UPRIGHT:
|
||
m_fAdd_x = 3;
|
||
m_fAdd_z = 1;
|
||
break;
|
||
case DIR_RIGHT:
|
||
m_fAdd_x = 3;
|
||
m_fAdd_z = add_y;
|
||
break;
|
||
case DIR_DOWNRIGHT:
|
||
m_fAdd_x = 3;
|
||
m_fAdd_z = 3;
|
||
break;
|
||
}
|
||
|
||
return nDir;
|
||
}
|
||
|
||
void CNpc::NpcMoveEnd()
|
||
{
|
||
RegisterRegion(GetX(), GetZ());
|
||
SendMoveResult(GetX(), GetY(), GetZ(), (float)m_sSpeed / 1000);
|
||
}
|
||
|
||
void CNpc::GetVectorPosition(__Vector3 & vOrig, __Vector3 & vDest, float fDis, __Vector3 * vResult)
|
||
{
|
||
*vResult = vDest - vOrig;
|
||
vResult->Magnitude();
|
||
vResult->Normalize();
|
||
*vResult *= fDis;
|
||
*vResult += vOrig;
|
||
}
|
||
|
||
float CNpc::GetDistance(__Vector3 & vOrig, __Vector3 & vDest)
|
||
{
|
||
return (vOrig - vDest).Magnitude();
|
||
}
|
||
|
||
bool CNpc::GetUserInView()
|
||
{
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr) return false;
|
||
//if( m_ZoneIndex > 5 || m_ZoneIndex < 0) return false; // ÀÓ½ÃÄÚµå ( 2002.03.24 )
|
||
int min_x = (int)(GetX() - NPC_VIEW_RANGE)/VIEW_DIST; if(min_x < 0) min_x = 0;
|
||
int min_z = (int)(GetZ() - NPC_VIEW_RANGE)/VIEW_DIST; if(min_z < 0) min_z = 0;
|
||
int max_x = (int)(GetX() + NPC_VIEW_RANGE)/VIEW_DIST; if(max_x > pMap->GetXRegionMax()) max_x = pMap->GetXRegionMax();
|
||
int max_z = (int)(GetZ() + NPC_VIEW_RANGE)/VIEW_DIST; if(max_z > pMap->GetZRegionMax()) max_z = pMap->GetZRegionMax();
|
||
|
||
int search_x = max_x - min_x + 1;
|
||
int search_z = max_z - min_z + 1;
|
||
|
||
bool bFlag = false;
|
||
int i, j;
|
||
|
||
for(i = 0; i < search_x; i++) {
|
||
for(j = 0; j < search_z; j++) {
|
||
bFlag = GetUserInViewRange(min_x+i, min_z+j);
|
||
if(bFlag == true) return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool CNpc::GetUserInViewRange(int x, int z)
|
||
{
|
||
MAP* pMap = GetMap();
|
||
if (pMap == nullptr || x < 0 || z < 0 || x > pMap->GetXRegionMax() || z > pMap->GetZRegionMax())
|
||
{
|
||
TRACE("#### Npc-GetUserInViewRange() Fail : [nid=%d, sid=%d], x1=%d, z1=%d #####\n", GetID(), GetProtoID(), x, z);
|
||
return false;
|
||
}
|
||
|
||
Guard lock(pMap->m_lock);
|
||
CRegion * pRegion = pMap->GetRegion(x, z);
|
||
|
||
if (pRegion == nullptr)
|
||
return false;
|
||
|
||
float fDis = 0.0f;
|
||
|
||
foreach_stlmap (itr, pRegion->m_RegionUserArray)
|
||
{
|
||
CUser *pUser = g_pMain->GetUserPtr(*itr->second);
|
||
if (pUser == nullptr)
|
||
continue;
|
||
|
||
if (isInRangeSlow(pUser, NPC_VIEW_RANGE))
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
void CNpc::CalcAdaptivePosition(__Vector3 & vPosOrig, __Vector3 & vPosDest, float fAttackDistance, __Vector3 * vResult)
|
||
{
|
||
*vResult = vPosOrig - vPosDest;
|
||
vResult->Normalize();
|
||
*vResult *= fAttackDistance;
|
||
*vResult += vPosDest;
|
||
}
|
||
|
||
bool CNpc::IsPathFindCheck(float fDistance)
|
||
{
|
||
int nX = 0, nZ = 0;
|
||
__Vector3 vStart, vEnd, vDis, vOldDis;
|
||
float fDis = 0.0f;
|
||
vStart.Set(m_fStartPoint_X, 0, m_fStartPoint_Y);
|
||
vEnd.Set(m_fEndPoint_X, 0, m_fEndPoint_Y);
|
||
vDis.Set(m_fStartPoint_X, 0, m_fStartPoint_Y);
|
||
int count = 0;
|
||
int nError = 0;
|
||
|
||
MAP* pMap = GetMap();
|
||
|
||
nX = (int)(vStart.x / TILE_SIZE);
|
||
nZ = (int)(vStart.z / TILE_SIZE);
|
||
if (pMap->IsMovable(nX, nZ))
|
||
return false;
|
||
|
||
nX = (int)(vEnd.x / TILE_SIZE);
|
||
nZ = (int)(vEnd.z / TILE_SIZE);
|
||
if (pMap->IsMovable(nX, nZ))
|
||
return false;
|
||
|
||
do
|
||
{
|
||
vOldDis.Set(vDis.x, 0, vDis.z);
|
||
GetVectorPosition(vDis, vEnd, fDistance, &vDis);
|
||
fDis = GetDistance(vOldDis, vEnd);
|
||
|
||
if (fDis > NPC_MAX_MOVE_RANGE)
|
||
{
|
||
nError = -1;
|
||
break;
|
||
}
|
||
|
||
nX = (int)(vDis.x / TILE_SIZE);
|
||
nZ = (int)(vDis.z / TILE_SIZE);
|
||
|
||
if (pMap->IsMovable(nX, nZ)
|
||
|| count >= MAX_PATH_LINE)
|
||
{
|
||
nError = -1;
|
||
break;
|
||
}
|
||
|
||
m_pPoint[count].fXPos = vEnd.x;
|
||
m_pPoint[count++].fZPos = vEnd.z;
|
||
|
||
} while (fDis <= fDistance);
|
||
|
||
m_iAniFrameIndex = count;
|
||
|
||
if (nError == -1)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
// ÆĞ½º ÆÄÀε带 ÇÏÁö ¾Ê°í °ø°İ´ë»óÀ¸·Î °¡´Â ·çƾ..
|
||
void CNpc::IsNoPathFind(float fDistance)
|
||
{
|
||
ClearPathFindData();
|
||
m_bPathFlag = true;
|
||
|
||
int nX = 0, nZ = 0;
|
||
__Vector3 vStart, vEnd, vDis, vOldDis;
|
||
float fDis = 0.0f;
|
||
vStart.Set(m_fStartPoint_X, 0, m_fStartPoint_Y);
|
||
vEnd.Set(m_fEndPoint_X, 0, m_fEndPoint_Y);
|
||
vDis.Set(m_fStartPoint_X, 0, m_fStartPoint_Y);
|
||
int count = 0;
|
||
int nError = 0;
|
||
|
||
|
||
fDis = GetDistance(vStart, vEnd);
|
||
if (fDis > NPC_MAX_MOVE_RANGE) {
|
||
ClearPathFindData();
|
||
TRACE("#### Npc-IsNoPathFind Fail : NPC_MAX_MOVE_RANGE overflow .. [nid = %d, name=%s], cur_x=%.2f, z=%.2f, dest_x=%.2f, dest_z=%.2f, fDis=%.2f#####\n",
|
||
GetID(), GetName().c_str(), m_fStartPoint_X, m_fStartPoint_Y, m_fEndPoint_X, m_fEndPoint_Y, fDis);
|
||
return;
|
||
}
|
||
|
||
|
||
if (GetMap() == nullptr)
|
||
{
|
||
ClearPathFindData();
|
||
TRACE("#### Npc-IsNoPathFind No map : [nid=%d, name=%s], zone=%d #####\n", GetID(), GetName().c_str(), GetZoneID());
|
||
return;
|
||
}
|
||
MAP* pMap = GetMap();
|
||
|
||
|
||
vOldDis.Set(vDis.x, 0, vDis.z);
|
||
fDis = GetDistance(vOldDis, vEnd);
|
||
|
||
|
||
if (count < 0 || count >= MAX_PATH_LINE)
|
||
count = 0;
|
||
|
||
|
||
|
||
GetVectorPosition(vDis, vEnd, fDistance, &vDis);
|
||
NpcCalling(fDis,fDistance, vOldDis, vDis);
|
||
|
||
if (count <= 0 || count >= MAX_PATH_LINE) {
|
||
ClearPathFindData();
|
||
TRACE("#### IsNoPtahfind Fail : nid=%d,%s, count=%d ####\n", GetID(), GetName().c_str(), count);
|
||
return;
|
||
}
|
||
m_iAniFrameIndex = count;
|
||
|
||
|
||
}
|
||
|
||
void CNpc::GiveNpcHaveItem()
|
||
{
|
||
bool isMonsterStone = false;
|
||
if (m_sMaxDamageUserid < 0 || m_sMaxDamageUserid > MAX_USER)
|
||
return;
|
||
|
||
int iPer = 0, iMakeItemCode = 0, iMoney = 0, iRandom, nCount = 0;
|
||
|
||
iRandom = myrand(70, 100);
|
||
iMoney = m_iMoney * iRandom / 100;
|
||
|
||
_NpcGiveItem m_GiveItemList[NPC_HAVE_ITEM_LIST];
|
||
if (iMoney > 0)
|
||
{
|
||
if (iMoney >= SHRT_MAX)
|
||
iMoney = 32000;
|
||
|
||
m_GiveItemList[nCount].sSid = TYPE_MONEY_SID;
|
||
m_GiveItemList[nCount++].count = iMoney;
|
||
}
|
||
|
||
_K_MONSTER_ITEM * pItem = g_pMain->m_NpcItemArray.GetData(m_iItem);
|
||
if (pItem != nullptr)
|
||
{
|
||
|
||
isMonsterStone = false;
|
||
// j = iItem
|
||
for (int j = 0; j < 5; j++)
|
||
{
|
||
|
||
iMakeItemCode = 0;
|
||
iRandom = myrand(1, 10000);
|
||
|
||
// Boş itemler
|
||
if (pItem->iItem[j] == 0 || pItem->sPercent[j] == 0)
|
||
{
|
||
iPer = MONSTERSTONEDROPRATE;
|
||
if(iRandom <= iPer && !isMonsterStone)
|
||
{
|
||
m_GiveItemList[nCount].sSid = ITEM_MONSTER_STONE;
|
||
m_GiveItemList[nCount].count = 1;
|
||
isMonsterStone = true;
|
||
nCount++;
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
|
||
iPer = pItem->sPercent[j];
|
||
// iRandom > iPer item çıkmadı ve Monster Stonede çıkmamışsa Monster stone ihtimalleri
|
||
if(iRandom > iPer && !isMonsterStone)
|
||
{
|
||
if(iRandom <= MONSTERSTONEDROPRATE)
|
||
{
|
||
m_GiveItemList[nCount].sSid = ITEM_MONSTER_STONE;
|
||
m_GiveItemList[nCount].count = 1;
|
||
isMonsterStone = true;
|
||
nCount++;
|
||
}
|
||
continue;
|
||
|
||
}
|
||
else if(iRandom > iPer)// İtem çıkmadıysa devam
|
||
continue;
|
||
|
||
// Item çıktı
|
||
// ItemProdution
|
||
if (pItem->iItem[j] < 100)
|
||
iMakeItemCode = ItemProdution(pItem->iItem[j]);
|
||
// ItemGroups
|
||
else if(pItem->iItem[j] < 100000000)
|
||
{
|
||
_MAKE_ITEM_GROUP * pGroup = g_pMain->m_MakeItemGroupArray.GetData(pItem->iItem[j]);
|
||
if (pGroup == nullptr)
|
||
continue;
|
||
|
||
iMakeItemCode = pGroup->iItems[myrand(1, pGroup->iItems.size()) - 1];
|
||
}
|
||
|
||
if (iMakeItemCode == 0 && pItem->iItem[j] < 100000000)
|
||
continue;
|
||
else if(iMakeItemCode == 0)
|
||
iMakeItemCode = pItem->iItem[j];
|
||
// Normal items
|
||
m_GiveItemList[nCount].sSid = iMakeItemCode;
|
||
|
||
if (m_GiveItemList[nCount].sSid == 391010000)
|
||
m_GiveItemList[nCount].count = 20;
|
||
else
|
||
m_GiveItemList[nCount].count = 1;
|
||
|
||
nCount++;
|
||
}
|
||
}
|
||
|
||
Packet result(AG_NPC_GIVE_ITEM);
|
||
result << m_sMaxDamageUserid << GetID()
|
||
<< GetZoneID() << GetRegionX() << GetRegionZ()
|
||
<< GetX() << GetZ() << GetY()
|
||
<< uint8(nCount);
|
||
|
||
for (int i = 0; i < nCount; i++)
|
||
result << m_GiveItemList[i].sSid << m_GiveItemList[i].count;
|
||
|
||
g_pMain->Send(&result);
|
||
}
|
||
|
||
|
||
void CNpc::Yaw2D(float fDirX, float fDirZ, float& fYawResult)
|
||
{
|
||
if ( fDirX >= 0.0f )
|
||
{
|
||
if ( fDirZ >= 0.0f )
|
||
fYawResult = (float)(asin(fDirX));
|
||
else
|
||
fYawResult = D3DXToRadian(90.0f) + (float)(acos(fDirX));
|
||
}
|
||
else
|
||
{
|
||
if ( fDirZ >= 0.0f )
|
||
fYawResult = D3DXToRadian(270.0f) + (float)(acos(-fDirX));
|
||
else
|
||
fYawResult = D3DXToRadian(180.0f) + (float)(asin(-fDirX));
|
||
}
|
||
}
|
||
|
||
void CNpc::ComputeDestPos(__Vector3 & vCur, float fDegree, float fDistance, __Vector3 * vResult)
|
||
{
|
||
__Matrix44 mtxRot;
|
||
vResult->Set(0.0f, 0.0f, 1.0f);
|
||
mtxRot.RotationY(D3DXToRadian(fDegree));
|
||
*vResult *= mtxRot;
|
||
*vResult *= fDistance;
|
||
*vResult += vCur;
|
||
}
|
||
|
||
void CNpc::HpChange()
|
||
{
|
||
m_fHPChangeTime = getMSTime();
|
||
|
||
if (isDead()
|
||
|| GetHealth() == GetMaxHealth())
|
||
return;
|
||
|
||
HpChange((int)(m_iMaxHP / 20));
|
||
}
|
||
|
||
bool CNpc::CheckFindEnemy()
|
||
{
|
||
if (isGuard())
|
||
return true;
|
||
|
||
MAP* pMap = GetMap();
|
||
|
||
if (pMap == nullptr)
|
||
return false;
|
||
|
||
if (pMap->GetRegion(GetRegionX(),GetRegionZ()) == nullptr)
|
||
return false;
|
||
else
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
int CNpc::ItemProdution(int item_number) // ¾ÆÀÌÅÛ Á¦ÀÛ
|
||
{
|
||
int iItemNumber = 0, iRandom = 0, i=0, iItemGrade = 0, iItemLevel = 0;
|
||
int iDefault = 0, iItemCode=0, iItemKey=0, iRand2=0, iRand3=0, iRand4=0, iRand5=0;
|
||
int iTemp1 = 0, iTemp2 = 0, iTemp3 = 0;
|
||
|
||
iRandom = myrand(1, 10000);
|
||
|
||
iItemGrade = GetItemGrade(item_number);
|
||
if(iItemGrade == 0) return 0;
|
||
iItemLevel = GetLevel() / 5;
|
||
|
||
if( COMPARE( iRandom, 1, 4001) ) { // ¹«±â±¸ ¾ÆÀÌÅÛ
|
||
iDefault = 100000000;
|
||
iRandom = myrand( 1, 10000 ); // ¹«±âÀÇ Á¾·ù¸¦ °áÁ¤(´Ü°Ë, °Ë, µµ³¢,,,,)
|
||
if( COMPARE ( iRandom, 1, 701 ) ) iRand2 = 10000000;
|
||
else if( COMPARE ( iRandom, 701, 1401 ) ) iRand2 = 20000000;
|
||
else if( COMPARE ( iRandom, 1401, 2101 ) ) iRand2 = 30000000;
|
||
else if( COMPARE ( iRandom, 2101, 2801 ) ) iRand2 = 40000000;
|
||
else if( COMPARE ( iRandom, 2801, 3501 ) ) iRand2 = 50000000;
|
||
else if( COMPARE ( iRandom, 3501, 5501 ) ) iRand2 = 60000000;
|
||
else if( COMPARE ( iRandom, 5501, 6501 ) ) iRand2 = 70000000;
|
||
else if( COMPARE ( iRandom, 6501, 8501 ) ) iRand2 = 80000000;
|
||
else if( COMPARE ( iRandom, 8501, 10001 ) ) iRand2 = 90000000;
|
||
|
||
iTemp1 = GetWeaponItemCodeNumber(true);
|
||
//TRACE("ItemProdution : GetWeaponItemCodeNumber() = %d, iRand2=%d\n", iTemp1, iRand2);
|
||
if( iTemp1 == 0 ) return 0;
|
||
iItemCode = iTemp1 * 100000; // ·çÆÃºĞÆ÷Ç¥ ÂüÁ¶
|
||
|
||
iRand3 = myrand(1, 10000); // Á¾Á·(¿¤¸ğ, Ä«·ç½º)
|
||
if( COMPARE( iRand3, 1, 5000) ) iRand3 = 10000;
|
||
else iRand3 = 50000;
|
||
iRand4 = myrand(1, 10000); // ÇѼÕ, ¾ç¼Õ¹«±âÀÎÁö¸¦ °áÁ¤
|
||
if( COMPARE( iRand4, 1, 5000) ) iRand4 = 0;
|
||
else iRand4 = 5000000;
|
||
|
||
iRandom = GetItemCodeNumber(iItemLevel, 1); // ·¹À̸ÅÁ÷Ç¥ Àû¿ë
|
||
//TRACE("ItemProdution : GetItemCodeNumber() = %d, iRand2=%d, iRand3=%d, iRand4=%d\n", iRandom, iRand2, iRand3, iRand4);
|
||
if(iRandom == -1) { // À߸øµÈ ¾ÆÀÌÅÛ »ı¼º½ÇÆĞ
|
||
return 0;
|
||
}
|
||
iRand5 = iRandom * 10;
|
||
iItemNumber = iDefault + iItemCode + iRand2 + iRand3 + iRand4 + iRand5 + iItemGrade;
|
||
|
||
//TRACE("ItemProdution : Weapon Success item_number = %d, default=%d, itemcode=%d, iRand2=%d, iRand3=%d, iRand4=%d, iRand5, iItemGrade=%d\n", iItemNumber, iDefault, iItemCode, iRand2, iRand3, iRand4, iRand5, iItemGrade);
|
||
}
|
||
else if( COMPARE( iRandom, 4001, 8001) ) { // ¹æ¾î±¸ ¾ÆÀÌÅÛ
|
||
iDefault = 200000000;
|
||
|
||
iTemp1 = GetWeaponItemCodeNumber(false);
|
||
//TRACE("ItemProdution : GetWeaponItemCodeNumber() = %d\n", iTemp1 );
|
||
if( iTemp1 == 0 ) return 0;
|
||
iItemCode = iTemp1 * 1000000; // ·çÆÃºĞÆ÷Ç¥ ÂüÁ¶
|
||
|
||
if( m_byMaxDamagedNation == KARUS ) { // Á¾Á·
|
||
iRandom = myrand(0, 10000); // Á÷¾÷ÀÇ °©¿ÊÀ» °áÁ¤
|
||
if( COMPARE( iRandom, 0, 2000) ) {
|
||
iRand2 = 0;
|
||
iRand3 = 10000; // Àü»ç°©¿ÊÀº ¾ÆÅ©Åõ¾Æ·º¸¸ °¡Áöµµ·Ï
|
||
}
|
||
else if( COMPARE( iRandom, 2000, 4000) ) {
|
||
iRand2 = 40000000;
|
||
iRand3 = 20000; // ·Î±×°©¿ÊÀº Åõ¾Æ·º¸¸ °¡Áöµµ·Ï
|
||
}
|
||
else if( COMPARE( iRandom, 4000, 6000) ) {
|
||
iRand2 = 60000000;
|
||
iRand3 = 30000; // ¸¶¹ı»ç°©¿ÊÀº ¸µÅ¬ Åõ¾Æ·º¸¸ °¡Áöµµ·Ï
|
||
}
|
||
else if( COMPARE( iRandom, 6000, 10001) ) {
|
||
iRand2 = 80000000;
|
||
iRandom = myrand(0, 10000);
|
||
if( COMPARE( iRandom, 0, 5000) ) iRand3 = 20000; // »çÁ¦°©¿ÊÀº Åõ¾Æ·º
|
||
else iRand3 = 40000; // »çÁ¦°©¿ÊÀº Ç»¸®Åõ¾Æ·º
|
||
}
|
||
}
|
||
else if( m_byMaxDamagedNation == ELMORAD ) {
|
||
iRandom = myrand(0, 10000); // Á÷¾÷ÀÇ °©¿ÊÀ» °áÁ¤
|
||
if( COMPARE( iRandom, 0, 3300) ) {
|
||
iRand2 = 0;
|
||
iItemKey = myrand(0, 10000); // Àü»ç°©¿ÊÀº ¸ğµç Á¾Á·ÀÌ °¡Áü
|
||
if( COMPARE( iItemKey, 0, 3333) ) iRand3 = 110000;
|
||
else if( COMPARE( iItemKey, 3333, 6666) ) iRand3 = 120000;
|
||
else if( COMPARE( iItemKey, 6666, 10001) ) iRand3 = 130000;
|
||
}
|
||
else if( COMPARE( iRandom, 3300, 5600) ) {
|
||
iRand2 = 40000000;
|
||
iItemKey = myrand(0, 10000); // ·Î±×°©¿ÊÀº ³²ÀÚ¿Í ¿©ÀÚ¸¸ °¡Áü
|
||
if( COMPARE( iItemKey, 0, 5000) ) iRand3 = 120000;
|
||
else iRand3 = 130000;
|
||
}
|
||
else if( COMPARE( iRandom, 5600, 7800) ) {
|
||
iRand2 = 60000000;
|
||
iItemKey = myrand(0, 10000); // ¸¶¹ı»ç°©¿ÊÀº ³²ÀÚ¿Í ¿©ÀÚ¸¸ °¡Áü
|
||
if( COMPARE( iItemKey, 0, 5000) ) iRand3 = 120000;
|
||
else iRand3 = 130000;
|
||
}
|
||
else if( COMPARE( iRandom, 7800, 10001) ) {
|
||
iRand2 = 80000000;
|
||
iItemKey = myrand(0, 10000); // »çÁ¦°©¿ÊÀº ³²ÀÚ¿Í ¿©ÀÚ¸¸ °¡Áü
|
||
if( COMPARE( iItemKey, 0, 5000) ) iRand3 = 120000;
|
||
else iRand3 = 130000;
|
||
}
|
||
|
||
}
|
||
|
||
iTemp2 = myrand(0, 10000); // ¸öÀÇ ºÎÀ§ ¾ÆÀÌÅÛ °áÁ¤
|
||
if( COMPARE( iTemp2, 0, 2000) ) iRand4 = 1000;
|
||
else if( COMPARE( iTemp2, 2000, 4000) ) iRand4 = 2000;
|
||
else if( COMPARE( iTemp2, 4000, 6000) ) iRand4 = 3000;
|
||
else if( COMPARE( iTemp2, 6000, 8000) ) iRand4 = 4000;
|
||
else if( COMPARE( iTemp2, 8000, 10001) ) iRand4 = 5000;
|
||
iRandom = GetItemCodeNumber(iItemLevel, 2); // ·¹À̸ÅÁ÷Ç¥ Àû¿ë
|
||
if(iRandom == -1) { // À߸øµÈ ¾ÆÀÌÅÛ »ı¼º½ÇÆĞ
|
||
return 0;
|
||
}
|
||
iRand5 = iRandom * 10;
|
||
iItemNumber = iDefault + iRand2 + iItemCode + iRand3 + iRand4 + iRand5 + iItemGrade; // iItemGrade : ¾ÆÀÌÅÛ µî±Ş»ı¼ºÇ¥ Àû¿ë
|
||
//TRACE("ItemProdution : Defensive Success item_number = %d, default=%d, iRand2=%d, itemcode=%d, iRand3=%d, iRand4=%d, iRand5, iItemGrade=%d\n", iItemNumber, iDefault, iRand2, iItemCode, iRand3, iRand4, iRand5, iItemGrade);
|
||
}
|
||
else if( COMPARE( iRandom, 8001, 10001) ) { // ¾Ç¼¼»ç¸® ¾ÆÀÌÅÛ
|
||
iDefault = 300000000;
|
||
iRandom = myrand(0, 10000); // ¾Ç¼¼»ç¸® Á¾·ù°áÁ¤(±Í°í¸®, ¸ñ°ÉÀÌ, ¹İÁö, º§Æ®)
|
||
if( COMPARE( iRandom, 0, 2500) ) iRand2 = 10000000;
|
||
else if( COMPARE( iRandom, 2500, 5000) ) iRand2 = 20000000;
|
||
else if( COMPARE( iRandom, 5000, 7500) ) iRand2 = 30000000;
|
||
else if( COMPARE( iRandom, 7500, 10001) ) iRand2 = 40000000;
|
||
iRand3 = myrand(1, 10000); // Á¾Á·(¿¤¸ğ¶óµå, Ä«·ç½º)
|
||
if( COMPARE( iRand3, 1, 5000) ) iRand3 = 110000;
|
||
else iRand3 = 150000;
|
||
iRandom = GetItemCodeNumber(iItemLevel, 3); // ·¹À̸ÅÁ÷Ç¥ Àû¿ë
|
||
//TRACE("ItemProdution : GetItemCodeNumber() = %d\n", iRandom);
|
||
if(iRandom == -1) { // À߸øµÈ ¾ÆÀÌÅÛ »ı¼º½ÇÆĞ
|
||
return 0;
|
||
}
|
||
iRand4 = iRandom * 10;
|
||
iItemNumber = iDefault + iRand2 + iRand3 + iRand4 + iItemGrade;
|
||
//TRACE("ItemProdution : Accessary Success item_number = %d, default=%d, iRand2=%d, iRand3=%d, iRand4=%d, iItemGrade=%d\n", iItemNumber, iDefault, iRand2, iRand3, iRand4, iItemGrade);
|
||
}
|
||
|
||
return iItemNumber;
|
||
}
|
||
|
||
int CNpc::GetItemGrade(int item_grade)
|
||
{
|
||
int iPercent = 0, iRandom = 0, i=0;
|
||
_MAKE_ITEM_GRADE_CODE* pItemData = nullptr;
|
||
|
||
iRandom = myrand(1, 1000);
|
||
pItemData = g_pMain->m_MakeGradeItemArray.GetData(item_grade);
|
||
if(pItemData == nullptr) return 0;
|
||
|
||
|
||
for(i=0; i<9; i++) {
|
||
if(i == 0) {
|
||
if(pItemData->sGrade[i] == 0) {
|
||
iPercent += pItemData->sGrade[i];
|
||
continue;
|
||
}
|
||
if( COMPARE( iRandom, 0, pItemData->sGrade[i]) ) return i+1;
|
||
else {
|
||
iPercent += pItemData->sGrade[i];
|
||
continue;
|
||
}
|
||
}
|
||
else {
|
||
if(pItemData->sGrade[i] == 0) {
|
||
iPercent += pItemData->sGrade[i];
|
||
continue;
|
||
}
|
||
|
||
if( COMPARE( iRandom, iPercent, iPercent+pItemData->sGrade[i]) ) return i+1;
|
||
else {
|
||
iPercent += pItemData->sGrade[i];
|
||
continue;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int CNpc::GetWeaponItemCodeNumber(bool bWeapon)
|
||
{
|
||
_MAKE_WEAPON * pItemData = nullptr;
|
||
|
||
if (bWeapon)
|
||
pItemData = g_pMain->m_MakeWeaponItemArray.GetData(GetLevel() / 10);
|
||
else
|
||
pItemData = g_pMain->m_MakeDefensiveItemArray.GetData(GetLevel() / 10);
|
||
|
||
if (pItemData == nullptr)
|
||
return 0;
|
||
|
||
for (int i = 0, iPercent = 0, iRandom = myrand(0, 1000); i < MAX_UPGRADE_WEAPON; i++)
|
||
{
|
||
if (pItemData->sClass[i] == 0)
|
||
{
|
||
iPercent += pItemData->sClass[i];
|
||
continue;
|
||
}
|
||
|
||
if (COMPARE(iRandom, iPercent, iPercent + pItemData->sClass[i]))
|
||
return i + 1;
|
||
|
||
iPercent += pItemData->sClass[i];
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int CNpc::GetItemCodeNumber(int level, int item_type)
|
||
{
|
||
int iItemCode = 0, iItemType = 0, iPercent = 0;
|
||
|
||
_MAKE_ITEM_LARE_CODE * pItemData = g_pMain->m_MakeLareItemArray.GetData(level);
|
||
if (pItemData == nullptr)
|
||
return -1;
|
||
|
||
int iItemPercent[] = { pItemData->sLareItem, pItemData->sMagicItem, pItemData->sGeneralItem };
|
||
int iRandom = myrand(0, 1000);
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
if (i == 0)
|
||
{
|
||
if (COMPARE(iRandom, 0, iItemPercent[i]))
|
||
{
|
||
iItemType = i+1;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
iPercent += iItemPercent[i];
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (COMPARE(iRandom, iPercent, iPercent+iItemPercent[i]))
|
||
{
|
||
iItemType = i+1;
|
||
break;
|
||
}
|
||
else {
|
||
iPercent += iItemPercent[i];
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
switch (iItemType)
|
||
{
|
||
case 1: // lare item
|
||
if (item_type == 1)
|
||
iItemCode = myrand(16, 24);
|
||
else if (item_type == 2)
|
||
iItemCode = myrand(12, 24);
|
||
else if (item_type == 3)
|
||
iItemCode = myrand(0, 10);
|
||
break;
|
||
|
||
case 2: // magic item
|
||
if (item_type == 1)
|
||
iItemCode = myrand(6, 15);
|
||
else if (item_type == 2)
|
||
iItemCode = myrand(6, 11);
|
||
else if (item_type == 3)
|
||
iItemCode = myrand(0, 10);
|
||
break;
|
||
|
||
case 3: // general item
|
||
if (item_type == 1
|
||
|| item_type == 2)
|
||
iItemCode = 5;
|
||
else if (item_type == 3)
|
||
iItemCode = myrand(0, 10);
|
||
break;
|
||
}
|
||
|
||
return iItemCode;
|
||
}
|
||
|
||
time_t CNpc::NpcSleeping()
|
||
{
|
||
if (!g_pMain->m_bIsNight)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
return m_Delay;
|
||
}
|
||
|
||
m_NpcState = NPC_SLEEPING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
time_t CNpc::NpcFainting()
|
||
{
|
||
if (UNIXTIME < (m_tFaintingTime + FAINTING_TIME))
|
||
return -1;
|
||
|
||
m_NpcState = NPC_STANDING;
|
||
m_tFaintingTime = 0;
|
||
return 0;
|
||
}
|
||
|
||
time_t CNpc::NpcHealing()
|
||
{
|
||
if (!isHealer())
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
auto result = IsCloseTarget(m_byAttackRange, AttackTypeMagic);
|
||
if (result == CloseTargetNotInRange)
|
||
{
|
||
m_sStepCount = 0;
|
||
m_byActionFlag = ATTACK_TO_TRACE;
|
||
m_NpcState = NPC_TRACING;
|
||
return 0;
|
||
}
|
||
else if (result == CloseTargetInAttackRange)
|
||
{
|
||
if (GetProto()->m_byDirectAttack == 2)
|
||
return LongAndMagicAttack();
|
||
|
||
|
||
m_sStepCount = 0;
|
||
m_byActionFlag = ATTACK_TO_TRACE;
|
||
m_NpcState = NPC_TRACING;
|
||
return 0;
|
||
}
|
||
else if (result == CloseTargetInvalid)
|
||
{
|
||
m_NpcState = NPC_STANDING;
|
||
InitTarget();
|
||
return 0;
|
||
}
|
||
|
||
if (hasTarget()
|
||
&& m_Target.id >= NPC_BAND)
|
||
{
|
||
CNpc * pNpc = g_pMain->GetNpcPtr(m_Target.id);
|
||
if (pNpc == nullptr
|
||
|| pNpc->isDead())
|
||
{
|
||
InitTarget();
|
||
return m_sStandTime;
|
||
}
|
||
|
||
int iHP = (int)(pNpc->GetMaxHealth() * 0.9);
|
||
if (pNpc->GetHealth() >= iHP)
|
||
{
|
||
InitTarget();
|
||
}
|
||
else
|
||
{
|
||
CNpcMagicProcess::MagicPacket(MAGIC_EFFECTING, m_proto->m_iMagic3, GetID(), m_Target.id);
|
||
return m_sAttackDelay;
|
||
}
|
||
}
|
||
|
||
int iMonsterNid = FindFriend(MonSearchNeedsHealing);
|
||
if (iMonsterNid == 0)
|
||
{
|
||
InitTarget();
|
||
m_NpcState = NPC_STANDING;
|
||
return m_sStandTime;
|
||
}
|
||
|
||
CNpcMagicProcess::MagicPacket(MAGIC_EFFECTING, m_proto->m_iMagic3, GetID(), iMonsterNid);
|
||
return m_sAttackDelay;
|
||
}
|
||
|
||
time_t CNpc::NpcCasting()
|
||
{
|
||
if (isDead())
|
||
return -1;
|
||
|
||
// Officially the attack delay overlaps with the cast time, so more often than not
|
||
// by the time the skill's cast, there should be very little to no delay for all cases I can see.
|
||
// Regardless, we'll allow for longer delays if set.
|
||
// NOTE: If it goes below 0 (which it will most of the time), the caller won't care to handle it.
|
||
time_t tAttackDelay = m_sAttackDelay - m_sActiveCastTime;
|
||
|
||
CNpcMagicProcess::MagicPacket(MAGIC_EFFECTING, m_nActiveSkillID, GetID(), m_sActiveTargetID);
|
||
|
||
m_NpcState = m_OldNpcState;
|
||
m_nActiveSkillID = 0;
|
||
m_sActiveTargetID = -1;
|
||
m_sActiveCastTime = 0;
|
||
|
||
return tAttackDelay;
|
||
}
|
||
|
||
int CNpc::GetPartyExp(int party_level, int man, int nNpcExp)
|
||
{
|
||
int nPartyExp = 0;
|
||
int nLevel = party_level / man;
|
||
double TempValue = 0;
|
||
nLevel = GetLevel() - nLevel;
|
||
|
||
if (nLevel < 2)
|
||
{
|
||
nPartyExp = nNpcExp; // x1
|
||
}
|
||
else if (nLevel >= 2 && nLevel < 5)
|
||
{
|
||
TempValue = nNpcExp * 1.1; // x1.1
|
||
nPartyExp = (int)TempValue;
|
||
if (TempValue > nPartyExp)
|
||
nPartyExp++;
|
||
}
|
||
else if (nLevel >= 5 && nLevel < 8)
|
||
{
|
||
TempValue = nNpcExp * 1.2; // x1.2
|
||
nPartyExp = (int)TempValue;
|
||
if (TempValue > nPartyExp)
|
||
nPartyExp++;
|
||
}
|
||
else if (nLevel >= 8)
|
||
{
|
||
TempValue = nNpcExp * 1.3; // x1.3
|
||
nPartyExp = (int)TempValue;
|
||
}
|
||
|
||
return nPartyExp;
|
||
}
|
||
|
||
void CNpc::ChangeAbility(int iChangeType)
|
||
{
|
||
if (iChangeType > 2)
|
||
return;
|
||
|
||
int nHP = 0, nAC=0, nDamage=0, nMagicR=0, nDiseaseR=0, nPoisonR=0, nLightningR=0, nFireR=0, nColdR=0;
|
||
CNpcTable* pNpcTable = GetProto();
|
||
|
||
if (iChangeType == BATTLEZONE_OPEN)
|
||
{
|
||
nHP = (int)(pNpcTable->m_iMaxHP / 2);
|
||
nAC = (int)(pNpcTable->m_sDefense * 0.2);
|
||
nDamage = (int)(pNpcTable->m_sDamage * 0.3);
|
||
nMagicR = (int)(pNpcTable->m_byMagicR / 2);
|
||
nDiseaseR = (int)(pNpcTable->m_byDiseaseR / 2);
|
||
nPoisonR = (int)(pNpcTable->m_byPoisonR / 2);
|
||
nLightningR = (int)(pNpcTable->m_byLightningR / 2);
|
||
nFireR = (int)(pNpcTable->m_byFireR / 2);
|
||
nColdR = (int)(pNpcTable->m_byColdR / 2);
|
||
m_iMaxHP = nHP;
|
||
|
||
if (GetHealth() > nHP)
|
||
HpChange();
|
||
|
||
m_sTotalAc = nAC;
|
||
m_sTotalHit = nDamage;
|
||
m_sFireR = nFireR;
|
||
m_sColdR = nColdR;
|
||
m_sLightningR = nLightningR;
|
||
m_sMagicR = nMagicR;
|
||
m_sDiseaseR = nDiseaseR;
|
||
m_sPoisonR = nPoisonR;
|
||
}
|
||
else if (iChangeType == BATTLEZONE_CLOSE)
|
||
{
|
||
m_iMaxHP = pNpcTable->m_iMaxHP;
|
||
if (GetMaxHealth() > GetHealth())
|
||
{
|
||
m_iHP = GetMaxHealth() - 50;
|
||
HpChange();
|
||
}
|
||
|
||
m_sTotalHit = pNpcTable->m_sDamage;
|
||
m_sTotalAc = pNpcTable->m_sDefense;
|
||
m_sFireR = pNpcTable->m_byFireR;
|
||
m_sColdR = pNpcTable->m_byColdR;
|
||
m_sLightningR = pNpcTable->m_byLightningR;
|
||
m_sMagicR = pNpcTable->m_byMagicR;
|
||
m_sDiseaseR = pNpcTable->m_byDiseaseR;
|
||
m_sPoisonR = pNpcTable->m_byPoisonR;
|
||
}
|
||
}
|