knightonline/server/Npc.cpp

1114 lines
29 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "stdafx.h"
#include "Map.h"
#include "MagicInstance.h"
#include "../shared/DateTime.h"
using namespace std;
CNpc::CNpc() : Unit(UnitNPC)
{
Initialize();
}
CNpc::~CNpc()
{
}
/**
* @brief Initializes this object.
*/
void CNpc::Initialize()
{
cubeisactive = 0;
Unit::Initialize();
m_bIsEventNpc = false;
m_sSid = 0;
m_sPid = 0; // MONSTER(NPC) Picture ID
m_sSize = 100; // MONSTER(NPC) Size
m_strName.clear(); // MONSTER(NPC) Name
m_iMaxHP = 0; // <20>ִ<EFBFBD> HP
m_iHP = 0; // <20><><EFBFBD><EFBFBD> HP
m_byState = 0; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (NPC) <20><><EFBFBD><EFBFBD><EFBFBD>̻<EFBFBD>
m_tNpcType = 0; // NPC Type
// 0 : Normal Monster
// 1 : NPC
// 2 : <20><> <20>Ա<EFBFBD>,<2C>ⱸ NPC
// 3 : <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
m_iSellingGroup = 0;
//m_dwStepDelay = 0;
m_byDirection = 0; // npc<70><63> <20><><EFBFBD><EFBFBD>,,
m_iWeapon_1 = 0;
m_iWeapon_2 = 0;
m_NpcState = NPC_LIVE;
m_byGateOpen = true;
m_byObjectType = NORMAL_OBJECT;
m_JuraidGateOpen = 1;
m_byTrapNumber = 0;
m_oSocketID = -1;
m_bEventRoom = 0;
nIsPet = false;
strPetName = "";
strUserName = "";
nSerial = 0;
UserId = -1;
InitType3();
InitType4(true);
}
/**
* @brief Adds the NPC to the region.
*
* @param new_region_x The new region x coordinate.
* @param new_region_z The new region z coordinate.
*/
void CNpc::AddToRegion(int16 new_region_x, int16 new_region_z)
{
if(GetMap() == nullptr)
return;
if(GetMap()->GetXRegionMax() < new_region_x
|| GetMap()->GetZRegionMax() < new_region_z)
return;
GetRegion()->Remove(this);
SetRegion(new_region_x, new_region_z);
GetRegion()->Add(this);
}
/**
* @brief Sends the movement packet for the NPC.
*
* @param fPosX The position x coordinate.
* @param fPosY The position y coordinate.
* @param fPosZ The position z coordinate.
* @param fSpeed The speed.
*/
void CNpc::MoveResult(float fPosX, float fPosY, float fPosZ, float fSpeed)
{
if(nIsPet)
return;
/*Packet result(WIZ_NPC_MOVE);
SetPosition(fPosX, fPosY, fPosZ);
RegisterRegion();
#if __VERSION >= 1950
result << uint8(fSpeed);
#endif
result << GetID() << GetSPosX() << GetSPosZ() << GetSPosY() << uint16(1);
SendToRegion(&result);*/
Packet result(WIZ_NPC_MOVE, uint8(SUCCESS));
SetPosition(fPosX, fPosY, fPosZ);
RegisterRegion();
result << GetID() << GetSPosX() << GetSPosZ() << GetSPosY();
if(m_bIceSpeedAmount > 0)
result << fSpeed / float(m_bIceSpeedAmount);
else
result << fSpeed * float(85.99F);
SendToRegion(&result);
}
/**
* @brief Constructs an in/out packet for the NPC.
*
* @param result The packet buffer the constructed packet will be stored in.
* @param bType The type (in or out).
*/
void CNpc::GetInOut(Packet & result, uint8 bType)
{
result.Initialize(WIZ_NPC_INOUT);
result << bType << GetID();
if (bType != INOUT_OUT)
GetNpcInfo(result);
if (bType == INOUT_IN)
OnRespawn();
}
/**
* @brief Constructs and sends an in/out packet for the NPC.
*
* @param bType The type (in or out).
* @param fX The x coordinate.
* @param fZ The z coordinate.
* @param fY The y coordinate.
*/
void CNpc::SendInOut(uint8 bType, float fX, float fZ, float fY)
{
if (GetRegion() == nullptr)
{
SetRegion(GetNewRegionX(), GetNewRegionZ());
if (GetRegion() == nullptr)
return;
}
if (bType == INOUT_OUT)
{
GetRegion()->Remove(this);
}
else
{
GetRegion()->Add(this);
SetPosition(fX, fY, fZ);
}
Packet result;
GetInOut(result, bType);
SendToRegion(&result);
if (nIsPet)
{
Packet result3;
CUser *pUser = g_pMain->GetUserPtr(UserId);
CPet *mPet = g_pMain->GetPetPtr(nSerial);
if (mPet == nullptr
|| pUser == nullptr)
return;
if(pUser->LastWarpTime > (UNIXTIME - 2))
return;
result3.Initialize(WIZ_OBJECT_EVENT);
result3 << uint8(0x0B) << uint8(0x01) << GetID() << uint32(30403);
SendToRegion(&result3);
g_pMain->m_LivingPetArray.insert(std::make_pair(mPet->m_Serial, mPet));
Packet pet(WIZ_PET);
pet.DByte();
pet << uint8(0x01) << uint8(0x05) << uint8(0x01) << uint8(0x01) << uint8(0x00)
<< uint32(mPet->SpecialPetID)
<< mPet->GetName()
<< mPet->m_sClass
<< mPet->m_bLevel << uint16((mPet->m_iExp* 10000) / g_pMain->GetPetExpByLevel(mPet->m_bLevel))
<< uint16(mPet->m_sMaxHp) << uint16(mPet->m_sHp)
<< uint16(mPet->m_sMaxMp) << uint16(mPet->m_sMp)
<< uint16(mPet->m_sSatisfaction)
<< uint16(mPet->m_sAttack) << uint16(mPet->m_sAc)
<< uint8(mPet->m_sFireR) << uint8(mPet->m_sColdR) << uint8(mPet->m_sLightningR)
<< uint8(mPet->m_sMagicR) << uint8(mPet->m_sDiseaseR) << uint8(mPet->m_sPoisonR);
for (int i = 0; i < PET_MAX; i++) // pet cıkar görürsün de ne sorcaksan sor cıkıcam valla kanka 2 minik sorum var :D
{
_ITEM_DATA * pItem = &mPet->m_sItemArray[i];
//if(pItem->nNum > 0)
pet << pItem->nNum << pItem->sDuration << pItem->sCount << pItem->bFlag << uint16(0) << uint32(0) << pItem->nExpirationTime;
//else
//pet << uint32(0) << uint16(0) << uint16(0) << uint8(0) << uint16(0) << uint32(0) << uint32(0);
}
pUser->Send(&pet);
}
}
/**
* @brief Gets NPC information for use in various NPC packets.
*
* @param pkt The packet the information will be stored in.
*/
void CNpc::GetNpcInfo(Packet & pkt, uint8 myNation)
{
if(nIsPet)
{
pkt.SByte();
CUser *pUser = g_pMain->GetUserPtr(UserId);
if(!pUser)
return;
_ITEM_DATA *pItem = nullptr;
if ((pItem = pUser->GetItem(SHOULDER)) == nullptr)
return;
CPet *newPet = g_pMain->GetPetPtr(pItem->nSerialNum);
if(newPet == nullptr || newPet->pSession == nullptr)
return;
pkt << uint16(0) << uint8(newPet->isMonster() ? 1:2) << m_sPid << m_iSellingGroup
<< uint8(15) << uint32(0)
<< newPet->m_sSize << uint32(m_iWeapon_1) << uint32(m_iWeapon_2)
<< pUser->GetName() << newPet->m_strPetID
<< pUser->GetNation() << newPet->m_bLevel
<< newPet->GetSPosX() << newPet->GetSPosZ() << newPet->GetSPosY()
<< uint32(0) << uint8(0) << uint32(0) << int16(newPet->m_byDirection);
}
pkt.SByte();
#if (__VERSION >= 1950)
pkt << GetProtoID()
<< uint8(isMonster() ? 1 : 2) // Monster = 1, NPC = 2 (need to use a better flag)
<< m_sPid
<< m_iSellingGroup;
if (nIsPet && !m_bIsEventNpc)
pkt << uint8(64);
else
pkt << GetType();
pkt << uint32(0)
<< m_sSize
<< uint32(m_iWeapon_1) << uint32(m_iWeapon_2); // isPet() ? string
if (nIsPet && m_bIsEventNpc)
pkt << strUserName << strPetName;
if(myNation == uint8(-1))
pkt << uint8(isMonster() ? 0 : GetNation());
else
pkt << uint8(myNation);
pkt << GetLevel()
<< GetSPosX() << GetSPosZ() << GetSPosY();
if(GetProtoID() == 8110)
pkt << uint32(m_JuraidGateOpen);
else
pkt << uint32(m_byGateOpen);
pkt << m_byObjectType
<< uint16(0) << uint16(0) // unknown
<< int16(m_byDirection);
#endif
#if (__VERSION < 1950)
pkt << GetProtoID()
<< uint8(isMonster() ? 1 : 2) // Monster = 1, NPC = 2 (need to use a better flag)
<< m_sPid
<< GetType()
<< m_iSellingGroup
<< m_sSize
<< m_iWeapon_1 << m_iWeapon_2
// Monsters require 0 regardless, otherwise they'll act as NPCs.
<< uint8(isMonster() ? 0 : GetNation())
<< GetLevel()
<< GetSPosX() << GetSPosZ() << GetSPosY()
<< uint32(isGateOpen())
<< m_byObjectType
<< uint16(0) << uint16(0) // unknown
<< int16(m_byDirection);
#endif
}
/**
* @brief Sends a gate status.
*
* @param ObjectType object type
* @param bFlag The flag (open or shut).
* @param bSendAI true to update the AI server.
*/
void CNpc::SendGateFlag(uint8 bFlag /*= -1*/, bool bSendAI /*= true*/)
{
uint8 objectType = OBJECT_FLAG_LEVER;
_OBJECT_EVENT * pObjectEvent = GetMap()->GetObjectEvent(GetProtoID());
if (pObjectEvent)
objectType = (uint8)pObjectEvent->sType;
if (GetProtoID() == 8110)
objectType = OBJECT_GATE;
Packet result(WIZ_OBJECT_EVENT, objectType);
if (GetProtoID() == 8110)
{
m_JuraidGateOpen = bFlag;
result << uint8(1) << GetID() << bFlag;
SendToRegion(&result);
}
else
{
// If there's a flag to set, set it now.
if (bFlag >= 0)
m_byGateOpen = (bFlag == 1);
// Tell everyone nearby our new status.
result << uint8(1) << GetID() << m_byGateOpen;
SendToRegion(&result);
// Tell the AI server our new status
if (bSendAI)
{
result.Initialize(AG_NPC_GATE_OPEN);
result << GetID() << GetProtoID() << m_byGateOpen;
Send_AIServer(&result);
}
}
}
/**
* @brief Changes an NPC's hitpoints.
*
* @param amount The amount to adjust the HP by.
* @param pAttacker The attacker.
* @param bSendToAI true to update the AI server.
*/
void CNpc::HpChange(int amount, Unit *pAttacker /*= nullptr*/, bool bSendToAI /*= true*/)
{
uint16 tid = (pAttacker != nullptr ? pAttacker->GetID() : -1);
if(GetType() == NPC_FOSSIL && tid < NPC_BAND && amount != -1)
return;
if(GetType() == NPC_TREE && tid < NPC_BAND && amount != -20)
return;
// Implement damage/HP cap.
if (amount < -MAX_DAMAGE)
amount = -MAX_DAMAGE;
else if (amount > MAX_DAMAGE)
amount = MAX_DAMAGE;
// Glorious copypasta.
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(nIsPet && nSerial > 0)
{
CPet * pPet = g_pMain->GetPetPtr(nSerial);
if(pPet != nullptr)
pPet->HpChange(amount,pAttacker,false);
}
// NOTE: This will handle the death notification/looting.
if (bSendToAI)
SendHpChangeToAI(tid, nIsPet && nSerial > 0 && isDead() ? -50000 : amount);
if (pAttacker != nullptr
&& pAttacker->isPlayer())
TO_USER(pAttacker)->SendTargetHP(0, GetID(), amount);
}
void CNpc::HpChangeMagic(int amount, Unit *pAttacker /*= nullptr*/, AttributeType attributeType /*= AttributeNone*/)
{
uint16 tid = (pAttacker != nullptr ? pAttacker->GetID() : -1);
if(GetType() == NPC_FOSSIL || GetType() == NPC_TREE)
return;
// Implement damage/HP cap.
if (amount < -MAX_DAMAGE)
amount = -MAX_DAMAGE;
else if (amount > MAX_DAMAGE)
amount = MAX_DAMAGE;
HpChange(amount, pAttacker, false);
SendHpChangeToAI(tid, amount, attributeType);
}
void CNpc::SendHpChangeToAI(uint16 sTargetID, int amount, AttributeType attributeType /*= AttributeNone*/)
{
Packet result(AG_NPC_HP_CHANGE);
result << GetID() << sTargetID << m_iHP << amount << uint8(attributeType);
Send_AIServer(&result);
}
/**
* @brief Changes an NPC's mana.
*
* @param amount The amount to adjust the mana by.
*/
void CNpc::MSpChange(int amount)
{
}
bool CNpc::CastSkill(Unit * pTarget, uint32 nSkillID)
{
if (pTarget == nullptr)
return false;
MagicInstance instance;
instance.bSendFail = false;
instance.nSkillID = nSkillID;
instance.sCasterID = GetID();
instance.sTargetID = pTarget->GetID();
instance.Run();
return (instance.bSkillSuccessful);
}
float CNpc::GetRewardModifier(uint8 byLevel)
{
int iLevelDifference = GetLevel() - byLevel;
if (iLevelDifference <= -14)
return 0.2f;
else if (iLevelDifference <= -8 && iLevelDifference >= -13)
return 0.5f;
else if (iLevelDifference <= -2 && iLevelDifference >= -7)
return 0.8f;
return 1.0f;
}
float CNpc::GetPartyRewardModifier(uint32 nPartyLevel, uint32 nPartyMembers)
{
int iLevelDifference = GetLevel() - (nPartyLevel / nPartyMembers);
if (iLevelDifference >= 8)
return 1.3f;// was 2.0f edited byBrate
else if (iLevelDifference >= 5)
return 1.2f;// was 1.5f edited byBrate
else if (iLevelDifference >= 2)
return 1.1f;// was 1.2f edited byBrate
return 1.0f;
}
/**
* @brief Executes the death action.
*
* @param pKiller The killer.
*/
void CNpc::OnDeath(Unit *pKiller)
{
if (m_NpcState == NPC_DEAD || pKiller == nullptr)
return;
ASSERT(GetMap() != nullptr);
ASSERT(GetRegion() != nullptr);
m_NpcState = NPC_DEAD;
m_sACPercent = 100;
if (m_byObjectType == SPECIAL_OBJECT)
{
_OBJECT_EVENT *pEvent = GetMap()->GetObjectEvent(GetProtoID());
if (pEvent != nullptr)
pEvent->byLife = 0;
}
if (GetProtoID() != 31527
|| GetProtoID() != 31528
|| GetProtoID() != 31529
|| GetProtoID() != 31530)
Unit::OnDeath(pKiller);
OnDeathProcess(pKiller);
GetRegion()->Remove(TO_NPC(this));
SetRegion();
if(nIsPet && nSerial > 0)
{
CPet * pPet = g_pMain->GetPetPtr(nSerial);
if(pPet != nullptr)
pPet->Dead();
}
}
/**
* @brief Executes the death process.
*
* @param pKiller The killer.
*/
void CNpc::OnDeathProcess(Unit *pKiller)
{
if ((TO_NPC(this) == nullptr
|| pKiller == nullptr))
return;
if (pKiller->GetID() > NPC_BAND)
return;
CUser * pUser = TO_USER(pKiller);
if (pUser == nullptr)
return;
if (!m_bMonster)
{
switch (m_tNpcType)
{
case NPC_BIFROST_MONUMENT:
pUser->BifrostProcess(pUser);
break;
case NPC_CZ_MONUMENT:
CZMonumentProcess(pUser);
break;
case NPC_PVP_MONUMENT:
PVPMonumentProcess(pUser);
break;
case NPC_BATTLE_MONUMENT:
BattleMonumentProcess(pUser);
break;
case NPC_HUMAN_MONUMENT:
NationMonumentProcess(pUser);
break;
case NPC_KARUS_MONUMENT:
NationMonumentProcess(pUser);
break;
case NPC_DESTROYED_ARTIFACT:
pUser->CastleSiegeWarProcess(pUser);
break;
}
}
else if (m_bMonster)
{
if (GetProtoID() == 700 || GetProtoID() == 750 || GetProtoID() == 701 || GetProtoID() == 751)
{
if (pUser->V3_CheckExistEvent(STARTER_SEED_QUEST, 1))
{
_QUEST_HELPER * pQuestHelper ;
if (pUser->GetNation() == ELMORAD)
pQuestHelper = g_pMain->m_QuestHelperArray.GetData(5005);
else
pQuestHelper = g_pMain->m_QuestHelperArray.GetData(5002);
pUser->V3_QuestRunEvent(pQuestHelper,pQuestHelper->nEventTriggerIndex);
}
}
else if (g_pMain->m_MonsterRespawnListArray.GetData(GetProtoID()) != nullptr && pUser->isInPKZone())
{
g_pMain->SpawnEventNpc(g_pMain->m_MonsterRespawnListArray.GetData(GetProtoID())->sSid, true, GetZoneID(), GetX(), GetY(), GetZ(), g_pMain->m_MonsterRespawnListArray.GetData(GetProtoID())->sCount);
}
else if (g_pMain->pTempleEvent.ActiveEvent == TEMPLE_EVENT_JURAD_MOUNTAIN)
{
if (JuraidTempleEventZone())
{
if (g_pMain->m_MonsterRespawnListArray.GetData(GetProtoID()) != nullptr)
g_pMain->SpawnEventNpc(g_pMain->m_MonsterRespawnListArray.GetData(GetProtoID())->sSid, true, GetZoneID(), GetX(), GetY(), GetZ(), g_pMain->m_MonsterRespawnListArray.GetData(GetProtoID())->sCount,5,60*60,GetNation(),pUser->GetSocketID(),pUser->GetEventRoom());
if (GetProtoID() == 8100 || GetProtoID() == 8102)
{
pUser->GetNation() == KARUS ? g_pMain->pTempleEvent.KarusDeathRoom1[pUser->GetEventRoom()]++ : g_pMain->pTempleEvent.ElmoDeathRoom1[pUser->GetEventRoom()]++;
if ((g_pMain->pTempleEvent.KarusDeathRoom1[pUser->GetEventRoom()] > 3 && pUser->GetNation() == KARUS)
|| (g_pMain->pTempleEvent.ElmoDeathRoom1[pUser->GetEventRoom()] > 3 && pUser->GetNation() == ELMORAD))
{
if (pUser->GetNation() == KARUS)
{
g_pMain->pTempleEvent.KarusDeathRoom2[pUser->GetEventRoom()] = 0;
CNpc * pNpc = g_pMain->GetNpcPtr(g_pMain->pTempleEvent.JuraidKarusGateID1[pUser->GetEventRoom()]);
if(pNpc == nullptr)
printf("Bulunamadi... K 1\n");
return;
pNpc->SendGateFlag(2);
}
else
{
g_pMain->pTempleEvent.ElmoDeathRoom2[pUser->GetEventRoom()] = 0;
CNpc * pNpc = g_pMain->GetNpcPtr(g_pMain->pTempleEvent.JuraidElmoGateID1[pUser->GetEventRoom()]);
if(pNpc == nullptr)
printf("Bulunamadi... E 1\n");
return;
pNpc->SendGateFlag(2);
}
}
}
else if (GetProtoID () == 8101 || GetProtoID () == 8104)
{
pUser->GetNation() == KARUS ? g_pMain->pTempleEvent.KarusDeathRoom2[pUser->GetEventRoom()]++ : g_pMain->pTempleEvent.ElmoDeathRoom2[pUser->GetEventRoom()]++;
if ((g_pMain->pTempleEvent.KarusDeathRoom2[pUser->GetEventRoom()] > 3 && pUser->GetNation() == KARUS)
|| (g_pMain->pTempleEvent.ElmoDeathRoom2[pUser->GetEventRoom()] > 3 && pUser->GetNation() == ELMORAD))
{
if (pUser->GetNation() == KARUS)
{
g_pMain->pTempleEvent.KarusDeathRoom3[pUser->GetEventRoom()] = 0;
CNpc * pNpc = g_pMain->GetNpcPtr(g_pMain->pTempleEvent.JuraidKarusGateID2[pUser->GetEventRoom()]);
if(pNpc == nullptr)
printf("Bulunamadi... K 2\n");
return;
pNpc->SendGateFlag(2);
}
else
{
g_pMain->pTempleEvent.ElmoDeathRoom3[pUser->GetEventRoom()] = 0;
CNpc * pNpc = g_pMain->GetNpcPtr(g_pMain->pTempleEvent.JuraidElmoGateID2[pUser->GetEventRoom()]);
if(pNpc == nullptr)
printf("Bulunamadi... E 2\n");
return;
pNpc->SendGateFlag(2);
}
}
}
else if (GetProtoID () == 8103 || GetProtoID () == 8105)
{
pUser->GetNation() == KARUS ? g_pMain->pTempleEvent.KarusDeathRoom3[pUser->GetEventRoom()]++ : g_pMain->pTempleEvent.ElmoDeathRoom3[pUser->GetEventRoom()]++;
if ((g_pMain->pTempleEvent.KarusDeathRoom3[pUser->GetEventRoom()] > 3 && pUser->GetNation() == KARUS)
|| (g_pMain->pTempleEvent.ElmoDeathRoom3[pUser->GetEventRoom()] > 3 && pUser->GetNation() == ELMORAD))
{
if (pUser->GetNation() == KARUS)
{
CNpc * pNpc = g_pMain->GetNpcPtr(g_pMain->pTempleEvent.JuraidKarusGateID3[pUser->GetEventRoom()]);
if(pNpc == nullptr)
printf("Bulunamadi... K 3\n");
return;
pNpc->SendGateFlag(2);
}
else
{
CNpc * pNpc = g_pMain->GetNpcPtr(g_pMain->pTempleEvent.JuraidElmoGateID3[pUser->GetEventRoom()]);
if(pNpc == nullptr)
printf("Bulunamadi... E 3\n");
return;
pNpc->SendGateFlag(2);
}
}
}
else if (GetProtoID () == 8106)// Deva
{
g_pMain->pTempleEvent.isDevaControl[GetEventRoom()] = true;
g_pMain->pTempleEvent.isDevaFlag[GetEventRoom()] = true;
g_pMain->pTempleEvent.DevaNation[GetEventRoom()] = pUser->GetNation();
}
}
}
else if (m_tNpcType == NPC_CHAOS_STONE && pUser->isInPKZone())
{
ChaosStoneProcess(pUser,5);
}else if((m_sSid == g_pMain->Zone1Boss[GetEventRoom()] || m_sSid == g_pMain->Zone2Boss[GetEventRoom()] || m_sSid == g_pMain->Zone3Boss[GetEventRoom()]) && (GetZoneID() == ZONE_STONE1 || GetZoneID() == ZONE_STONE2 || GetZoneID() == ZONE_STONE3))
{
Packet dresult(WIZ_EVENT);
dresult << uint8(TEMPLE_EVENT_FINISH)
<< uint8(0) << uint8(0)
<< uint8(1) << uint8(20) << uint32(0);
TO_USER(pKiller)->Send(&dresult);
TO_USER(pKiller)->m_TimeMonsterStone = UNIXTIME + 20;
}
if (g_pMain->m_bForgettenTempleIsActive && GetZoneID() == ZONE_FORGOTTEN_TEMPLE)
g_pMain->m_ForgettenTempleMonsterList.erase(m_sNid);
if (pUser->isInParty())
{
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(pUser->GetPartyID());
if (pParty != nullptr)
{
for (int i = 0; i < 8; i++)
{
if (pParty->uid[i] >= 0)
{
CUser * pUserRange = g_pMain->GetUserPtr(pParty->uid[i]);
if (!isInRangeSlow(pUserRange, 50.0f) || pUserRange == nullptr)
continue;
CUser * pUserParty = g_pMain->GetUserPtr(pParty->uid[i]);
pUserParty->V3_MonsterCount(GetProtoID());
pUserParty->AchieveType2(this);
}
}
}
}
else
{
pUser->V3_MonsterCount(GetProtoID());
pUser->AchieveType2(this);
}
}
DateTime time;
string pKillerPartyUsers;
if (pUser->isInParty())
{
CUser *pPartyUser;
_PARTY_GROUP *pParty = g_pMain->GetPartyPtr(pUser->GetPartyID());
if (pParty)
{
for (int i = 0; i < MAX_PARTY_USERS; i++)
{
pPartyUser = g_pMain->GetUserPtr(pParty->uid[i]);
if (pPartyUser)
pKillerPartyUsers += string_format("%s,",pPartyUser->GetName().c_str());
}
}
if (!pKillerPartyUsers.empty())
pKillerPartyUsers = pKillerPartyUsers.substr(0,pKillerPartyUsers.length() - 1);
}
if (pKillerPartyUsers.empty())
g_pMain->WriteDeathNpcLogFile(string_format("[ %s - %d:%d:%d ] Killer=%s,SID=%d,Target=%s,Zone=%d,X=%d,Z=%d\n",m_bMonster ? "MONSTER" : "NPC",time.GetHour(),time.GetMinute(),time.GetSecond(),pKiller->GetName().c_str(),GetProtoID(),GetName().c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ())));
else
g_pMain->WriteDeathNpcLogFile(string_format("[ %s - %d:%d:%d ] Killer=%s,KillerParty=%s,SID=%d,Target=%s,Zone=%d,X=%d,Z=%d\n",m_bMonster ? "MONSTER" : "NPC",time.GetHour(),time.GetMinute(),time.GetSecond(),pKiller->GetName().c_str(),pKillerPartyUsers.c_str(),GetProtoID(),GetName().c_str(),GetZoneID(),uint16(GetX()),uint16(GetZ())));
InitType3();
InitType4(false);
}
void CNpc::InitType3()
{
for (int i = 0; i < MAX_TYPE3_REPEAT; i++)
m_durationalSkills[i].Reset();
m_bType3Flag = false;
}
void CNpc::InitType4(bool bRemoveSavedMagic /*= false*/, uint8 buffType /* = 0 */)
{
Type4BuffMap buffMap = m_buffMap; // copy the map
for (auto itr = buffMap.begin(); itr != buffMap.end(); itr++)
{
#ifdef GAMESERVER
if (buffType > 0 && itr->second.m_bBuffType != buffType)
continue;
CMagicProcess::RemoveType4Buff(itr->first, this, bRemoveSavedMagic, buffType > 0 ? true : false);
#endif
}
}
/**
* @brief Executes the Npc respawn.
*/
void CNpc::OnRespawn()
{
if (GetProtoID() == BORDER_WAR_MONUMENT_SID
|| (g_pMain->m_byBattleOpen == NATION_BATTLE
&& (GetProtoID() == ELMORAD_MONUMENT_SID
|| GetProtoID() == ASGA_VILLAGE_MONUMENT_SID
|| GetProtoID() == RAIBA_VILLAGE_MONUMENT_SID
|| GetProtoID() == DODO_CAMP_MONUMENT_SID
|| GetProtoID() == LUFERSON_MONUMENT_SID
|| GetProtoID() == LINATE_MONUMENT_SID
|| GetProtoID() == BELLUA_MONUMENT_SID
|| GetProtoID() == LAON_CAMP_MONUMENT_SID)))
{
_MONUMENT_INFORMATION * pData = new _MONUMENT_INFORMATION();
pData->sSid = GetProtoID();
pData->sNid = m_sNid;
pData->RepawnedTime = int32(UNIXTIME);
if (GetProtoID() == DODO_CAMP_MONUMENT_SID || GetProtoID() == LAON_CAMP_MONUMENT_SID)
g_pMain->m_bMiddleStatueNation = m_bNation;
if (!g_pMain->m_NationMonumentInformationArray.PutData(pData->sSid, pData))
delete pData;
}
else if (g_pMain->m_bForgettenTempleIsActive && GetZoneID() == ZONE_FORGOTTEN_TEMPLE)
g_pMain->m_ForgettenTempleMonsterList.insert(std::make_pair(m_sNid, GetProtoID()));
}
/**
* @brief Executes the death process.
*
* @param pUser The User.
* @param MonsterCount The Respawn boss count.
*/
void CNpc::ChaosStoneProcess(CUser *pUser, uint16 MonsterCount)
{
if (pUser == nullptr)
return;
g_pMain->SendNotice<CHAOS_STONE_ENEMY_NOTICE>("",GetZoneID(), Nation::ALL);
std::vector<uint32> MonsterSpawned;
std::vector<uint32> MonsterSpawnedFamily;
bool bLoopBack = true;
for (uint8 i = 0; i < MonsterCount;i++)
{
uint32 nMonsterNum = myrand(0, g_pMain->m_MonsterSummonListZoneArray.GetSize());
_MONSTER_SUMMON_LIST_ZONE * pMonsterSummonListZone = g_pMain->m_MonsterSummonListZoneArray.GetData(nMonsterNum);
if (pMonsterSummonListZone != nullptr)
{
if (pMonsterSummonListZone->ZoneID == GetZoneID())
{
if (std::find(MonsterSpawned.begin(),MonsterSpawned.end(),nMonsterNum) == MonsterSpawned.end())
{
if (std::find(MonsterSpawnedFamily.begin(),MonsterSpawnedFamily.end(),pMonsterSummonListZone->byFamily) == MonsterSpawnedFamily.end())
{
g_pMain->SpawnEventNpc(pMonsterSummonListZone->sSid, true,GetZoneID(), GetX(), GetY(), GetZ(), 1, CHAOS_STONE_MONSTER_RESPAWN_RADIUS, CHAOS_STONE_MONSTER_LIVE_TIME);
MonsterSpawned.push_back(nMonsterNum);
MonsterSpawnedFamily.push_back(pMonsterSummonListZone->byFamily);
bLoopBack = false;
}
}
}
}
if (bLoopBack)
i--;
else
bLoopBack = true;
}
}
/*
* @brief Executes the pvp monument process.
*
* @param pUser The User.
*/
void CNpc::PVPMonumentProcess(CUser *pUser)
{
if (pUser == nullptr)
return;
std::string sKillMonuPvP;
std::string sKillMonuEvent;
std::string sMonuNation;
Packet result(WIZ_CHAT, uint8(MONUMENT_NOTICE));
result << uint8(FORCE_CHAT) << pUser->GetNation() << pUser->GetName().c_str();
g_pMain->Send_Zone(&result, GetZoneID(), nullptr, Nation::ALL);
if (pUser->GetNation() == KARUS)
sMonuNation = "Karus";
else
sMonuNation = "Human";
sKillMonuPvP = string_format("%s has destroyed the %s. %s nation get +5 NPs per kill!",pUser->GetName().c_str(),GetName().c_str(),sMonuNation.c_str());
sKillMonuEvent = string_format("%s has destroyed the %s. %s nation get +10 NPs per kill!",pUser->GetName().c_str(),GetName().c_str(),sMonuNation.c_str());
if (GetZoneID() == ZONE_RONARK_LAND)
{
g_pMain->m_nPVPMonumentNation[GetZoneID()] = pUser->GetNation();
g_pMain->SendAnnouncement(sKillMonuPvP.c_str());
}
else
{
g_pMain->m_nEventMonumentNation[GetZoneID()] = pUser->GetNation();
g_pMain->SendAnnouncement(sKillMonuEvent.c_str());
}
g_pMain->NpcUpdate(GetProtoID(), m_bMonster, pUser->GetNation(), pUser->GetNation() == KARUS ? MONUMENT_KARUS_SPID : MONUMENT_ELMORAD_SPID);
pUser->GiveItem(BLUE_TREASURE_CHEST,1);
}
/*
* @brief Executes the battle monument process.
*
* @param pUser The User.
*/
void CNpc::BattleMonumentProcess(CUser *pUser)
{
if (pUser && g_pMain->m_byBattleOpen == NATION_BATTLE)
{
g_pMain->NpcUpdate(GetProtoID(), m_bMonster, pUser->GetNation(), pUser->GetNation() == KARUS ? MONUMENT_KARUS_SPID : MONUMENT_ELMORAD_SPID);
g_pMain->Announcement(DECLARE_BATTLE_MONUMENT_STATUS, Nation::ALL, m_byTrapNumber, pUser);
if (pUser->GetNation() == KARUS)
{
g_pMain->m_sKarusMonumentPoint +=2;
g_pMain->m_sKarusMonuments++;
if (g_pMain->m_sElmoMonuments != 0)
g_pMain->m_sElmoMonuments--;
if (g_pMain->m_sKarusMonuments >= 7){
g_pMain->m_sKarusMonumentPoint +=10;
Packet result(WIZ_MAP_EVENT);
result << uint8(3) << uint8(1) << short(15);
g_pMain->Send_Zone(&result,ZONE_BATTLE4);
}
if (g_pMain->m_sKilledElmoNpc == 3 && g_pMain->m_sKarusMonuments >= 7)
g_pMain->BattleZoneResult(pUser->GetNation());
}
else
{
g_pMain->m_sElmoMonumentPoint += 2;
g_pMain->m_sElmoMonuments++;
if (g_pMain->m_sKarusMonuments != 0)
g_pMain->m_sKarusMonuments--;
if (g_pMain->m_sElmoMonuments >= 7){
g_pMain->m_sElmoMonumentPoint +=10;
Packet result(WIZ_MAP_EVENT);
result << uint8(3) << uint8(2) << short(15);
g_pMain->Send_Zone(&result,ZONE_BATTLE4);
}
if (g_pMain->m_sKilledKarusNpc == 3 && g_pMain->m_sElmoMonuments >= 7)
g_pMain->BattleZoneResult(pUser->GetNation());
}
g_pMain->NereidsMonumentEvent(m_byTrapNumber,pUser->GetNation(),nullptr);
}
}
/*
* @brief Executes the nation monument process.
*
* @param pUser The User.
*/
void CNpc::NationMonumentProcess(CUser *pUser)
{
if (!pUser)
return;
if (g_pMain->m_byBattleOpen == NATION_BATTLE)
{
g_pMain->NpcUpdate(GetProtoID(), m_bMonster, pUser->GetNation());
g_pMain->Announcement(DECLARE_NATION_MONUMENT_STATUS, Nation::ALL, GetProtoID(), pUser);
uint16 sSid = 0;
foreach_stlmap (itr, g_pMain->m_NationMonumentInformationArray)
if (itr->second->sSid == (pUser->GetNation() == KARUS ? GetProtoID() + 10000 : GetProtoID() - 10000))
sSid = itr->second->sSid;
if (sSid != 0)
g_pMain->m_NationMonumentInformationArray.DeleteData(sSid);
}
else
{
g_pMain->NpcUpdate(GetProtoID(), m_bMonster, pUser->GetNation());
uint16 sSid = 0;
foreach_stlmap (itr, g_pMain->m_NationMonumentInformationArray)
if (itr->second->sSid == (pUser->GetNation() == KARUS ? GetProtoID() + 10000 : GetProtoID() - 10000))
sSid = itr->second->sSid;
if (sSid != 0)
g_pMain->m_NationMonumentInformationArray.DeleteData(sSid);
}
}
/*
* @brief Executes the Cz monument process.
*
* @param pUser The User.
*/
void CNpc::CZMonumentProcess(CUser *pUser)
{
if (pUser == nullptr)
return;
std::string sKillMonu;
std::string sRecoMonu;
sKillMonu = string_format("%s has destroyed the %s at Ronark Land!",pUser->GetName().c_str(),GetName().c_str());
sRecoMonu = string_format("%s has recovered the %s at Ronark Land!",pUser->GetName().c_str(),GetName().c_str());
g_pMain->NpcUpdate(GetProtoID(), m_bMonster, pUser->GetNation());
if (pUser->GetNation() == KARUS)
{
if (GetSpid() == 30092)
{
g_pMain->SendAnnouncement(sKillMonu.c_str());
pUser->GiveItem(BLUE_TREASURE_CHEST,1);
pUser->SendLoyaltyChange(150);
}
else
{
g_pMain->SendAnnouncement(sRecoMonu.c_str());
}
}
else
{
if (GetSpid() == 30091)
{
g_pMain->SendAnnouncement(sKillMonu.c_str());
pUser->GiveItem(BLUE_TREASURE_CHEST,1);
pUser->SendLoyaltyChange(150);
}
else
{
g_pMain->SendAnnouncement(sRecoMonu.c_str());
}
}
}
void CNpc::HPTimeChangeType3()
{
if (isDead()
|| !m_bType3Flag || isPlayer())
return;
uint16 totalActiveDurationalSkills = 0,
totalActiveDOTSkills = 0;
bool bIsDOT = false;
for (int i = 0; i < MAX_TYPE3_REPEAT; i++)
{
MagicType3 * pEffect = &m_durationalSkills[i];
if (!pEffect->m_byUsed)
continue;
// Has the required interval elapsed before using this skill?
if ((UNIXTIME - pEffect->m_tHPLastTime) >= pEffect->m_bHPInterval)
{
Unit * pUnit = g_pMain->GetUnitPtr(pEffect->m_sSourceID);
if (pUnit == nullptr)
continue;
if (!isInRangeSlow(pUnit, 50.0f))
m_bType3Flag = false;
// Reduce the HP
HpChange(pEffect->m_sHPAmount, pUnit); // do we need to specify the source of the DOT?
pEffect->m_tHPLastTime = UNIXTIME;
if (pEffect->m_sHPAmount < 0)
bIsDOT = true;
// Has the skill expired yet?
if (++pEffect->m_bTickCount == pEffect->m_bTickLimit)
{
Packet result(WIZ_MAGIC_PROCESS, uint8(MAGIC_DURATION_EXPIRED));
// Healing-over-time skills require the type 100
if (pEffect->m_sHPAmount > 0)
result << uint8(100);
else // Damage-over-time requires 200.
result << uint8(200);
Send_AIServer(&result);
pEffect->Reset();
}
}
if (pEffect->m_byUsed)
{
totalActiveDurationalSkills++;
if (pEffect->m_sHPAmount < 0)
totalActiveDOTSkills++;
}
}
// Have all the skills expired?
if (totalActiveDurationalSkills == 0)
m_bType3Flag = false;
}
void CNpc::Type4Duration()
{
Guard lock(m_buffLock);
if (m_buffMap.empty())
return;
foreach (itr, m_buffMap)
{
if (itr->second.m_tEndTime > UNIXTIME)
continue;
CMagicProcess::RemoveType4Buff(itr->first, this, true, false);
break; // only ever handle one at a time with the current logic
}
}