2017-08-08 13:53:47 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <stdint.h>
|
2017-08-28 16:13:23 -03:00
|
|
|
#include <cmath>
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-19 00:18:40 +02:00
|
|
|
#include <src/servers/Server_Common/Database/Database.h>
|
|
|
|
#include <src/servers/Server_Common/Logging/Logger.h>
|
|
|
|
#include <src/servers/Server_Common/Exd/ExdData.h>
|
|
|
|
#include <src/servers/Server_Common/Util/Util.h>
|
|
|
|
#include <src/servers/Server_Common/Util/UtilMath.h>
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
#include "Player.h"
|
|
|
|
#include "BattleNpc.h"
|
|
|
|
|
2017-08-18 17:16:15 +02:00
|
|
|
#include "src/servers/Server_Zone/Network/PacketWrappers/MoveActorPacket.h"
|
|
|
|
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket142.h"
|
|
|
|
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h"
|
|
|
|
#include "src/servers/Server_Zone/StatusEffect/StatusEffectContainer.h"
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
using namespace Core::Common;
|
|
|
|
using namespace Core::Network::Packets;
|
|
|
|
using namespace Core::Network::Packets::Server;
|
|
|
|
|
|
|
|
extern Core::Logger g_log;
|
|
|
|
extern Core::Db::Database g_database;
|
|
|
|
extern Core::Data::ExdData g_exdData;
|
|
|
|
|
|
|
|
uint32_t Core::Entity::BattleNpc::m_nextID = 1149241694;
|
|
|
|
|
|
|
|
Core::Entity::BattleNpc::BattleNpc()
|
|
|
|
{
|
|
|
|
m_id = 0;
|
|
|
|
m_type = ActorType::BattleNpc;
|
|
|
|
m_status = ActorStatus::Idle;
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::Entity::BattleNpc::~BattleNpc()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::Entity::BattleNpc::BattleNpc( uint32_t modelId, uint32_t nameid, const Common::FFXIVARR_POSITION3& spawnPos,
|
|
|
|
uint32_t sizeId, uint32_t type, uint32_t level, uint32_t behaviour,
|
|
|
|
uint32_t mobType )
|
|
|
|
{
|
|
|
|
BattleNpc::m_nextID++;
|
|
|
|
m_id = BattleNpc::m_nextID;
|
|
|
|
//strcpy( m_name, pBNpc->m_name.c_str() );
|
|
|
|
|
|
|
|
m_pos = spawnPos;
|
|
|
|
m_posOrigin = spawnPos;
|
|
|
|
|
|
|
|
m_type = ActorType::BattleNpc;
|
|
|
|
|
|
|
|
m_mode = MODE_IDLE;
|
|
|
|
|
|
|
|
m_maxHp = 150;
|
|
|
|
m_maxMp = 100;
|
|
|
|
|
|
|
|
m_baseStats.max_hp = m_maxHp;
|
|
|
|
m_baseStats.max_mp = m_maxMp;
|
|
|
|
|
|
|
|
m_hp = m_maxHp;
|
|
|
|
m_mp = m_maxMp;
|
|
|
|
|
|
|
|
m_currentStance = Stance::Passive;
|
|
|
|
|
|
|
|
m_class = ClassJob::CLASS_GLADIATOR;
|
|
|
|
m_level = level > 0 ? level : 60;
|
|
|
|
|
|
|
|
m_modelId = modelId;
|
|
|
|
m_nameId = nameid;
|
|
|
|
|
|
|
|
m_behavior = behaviour;
|
|
|
|
|
|
|
|
m_bnpcBaseId = sizeId;
|
|
|
|
|
|
|
|
m_status = ActorStatus::Idle;
|
|
|
|
|
|
|
|
m_pOwner = nullptr;
|
|
|
|
|
|
|
|
m_mobType = mobType;
|
|
|
|
|
|
|
|
//m_type = static_cast< Common::ActorType >( type );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-08-15 11:51:59 +02:00
|
|
|
void Core::Entity::BattleNpc::initStatusEffectContainer()
|
|
|
|
{
|
|
|
|
m_pStatusEffectContainer = StatusEffect::StatusEffectContainerPtr( new StatusEffect::StatusEffectContainer( shared_from_this() ) );
|
|
|
|
}
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
// spawn this player for pTarget
|
|
|
|
void Core::Entity::BattleNpc::spawn( Core::Entity::PlayerPtr pTarget )
|
|
|
|
{
|
|
|
|
//GamePacketNew< FFXIVIpcActorSpawn > spawnPacket( getId(), pTarget->getId() );
|
|
|
|
|
|
|
|
//spawnPacket.data().unknown_0 = 0;
|
|
|
|
//spawnPacket.data().ownerId = m_pOwner == nullptr ? INVALID_GAME_OBJECT_ID : m_pOwner->getId();
|
|
|
|
//spawnPacket.data().targetId = INVALID_GAME_OBJECT_ID & 0xFFFFFFFF;
|
|
|
|
//spawnPacket.data().hPCurr = m_hp;
|
|
|
|
//spawnPacket.data().hPMax = m_baseStats.max_hp;
|
|
|
|
//spawnPacket.data().level = m_level;
|
|
|
|
////spawnPacket.data().tPCurr = 1000;
|
|
|
|
//spawnPacket.data().model = m_modelId;
|
|
|
|
//spawnPacket.data().bnpcBaseId = m_bnpcBaseId;
|
|
|
|
//spawnPacket.data().nameId = m_nameId;
|
|
|
|
//spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() );
|
|
|
|
//g_log.info(std::to_string(spawnPacket.data().spawnIndex) + " " + std::to_string(getId()));
|
|
|
|
//spawnPacket.data().status = static_cast< uint8_t >( m_status );
|
|
|
|
//spawnPacket.data().mobAgressive = m_behavior;
|
|
|
|
//spawnPacket.data().type = static_cast< uint8_t >( m_type );
|
|
|
|
//spawnPacket.data().mobTypeIcon = m_mobType;
|
|
|
|
//spawnPacket.data().unknown_33 = 5;
|
|
|
|
//spawnPacket.data().typeFlags = 4;
|
|
|
|
//spawnPacket.data().pos.x = m_pos.x;
|
|
|
|
//spawnPacket.data().pos.y = m_pos.y;
|
|
|
|
//spawnPacket.data().pos.z = m_pos.z;
|
|
|
|
//spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() );
|
|
|
|
////spawnPacket.data().unknown_B0[11] = 1;
|
|
|
|
////spawnPacket.data().unknown_B0[12] = 4;
|
|
|
|
////spawnPacket.data().unknown_B0[14] = 20;
|
|
|
|
|
|
|
|
//pTarget->queuePacket( spawnPacket );
|
|
|
|
|
2017-08-20 22:31:23 +02:00
|
|
|
GamePacketNew< FFXIVIpcNpcSpawn, ServerZoneIpcType > spawnPacket( getId(), pTarget->getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
|
|
|
|
spawnPacket.data().pos.x = m_pos.x;
|
|
|
|
spawnPacket.data().pos.y = m_pos.y;
|
|
|
|
spawnPacket.data().pos.z = m_pos.z;
|
|
|
|
|
|
|
|
spawnPacket.data().targetId = INVALID_GAME_OBJECT_ID & 0xFFFFFFFF;
|
|
|
|
spawnPacket.data().hPCurr = m_hp;
|
|
|
|
spawnPacket.data().hPMax = m_baseStats.max_hp;
|
|
|
|
spawnPacket.data().level = m_level;
|
|
|
|
|
|
|
|
spawnPacket.data().subtype = 5;
|
|
|
|
spawnPacket.data().enemyType = 4;
|
|
|
|
|
|
|
|
spawnPacket.data().modelChara = m_modelId;
|
|
|
|
spawnPacket.data().bNPCBase = m_bnpcBaseId;
|
|
|
|
spawnPacket.data().bNPCName = m_nameId;
|
|
|
|
spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() );
|
|
|
|
|
|
|
|
spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() );
|
|
|
|
|
|
|
|
spawnPacket.data().type = static_cast< uint8_t >( m_type );
|
|
|
|
|
|
|
|
spawnPacket.data().state = static_cast< uint8_t >( m_status );
|
|
|
|
|
|
|
|
pTarget->queuePacket( spawnPacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
// despawn
|
|
|
|
void Core::Entity::BattleNpc::despawn( Core::Entity::ActorPtr pTarget )
|
|
|
|
{
|
|
|
|
|
|
|
|
auto pPlayer = pTarget->getAsPlayer();
|
|
|
|
|
|
|
|
pPlayer->freePlayerSpawnId( getId() );
|
|
|
|
|
|
|
|
ActorControlPacket143 controlPacket( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 );
|
|
|
|
pPlayer->queuePacket( controlPacket );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::BattleNpc::getLevel() const
|
|
|
|
{
|
|
|
|
return m_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::Entity::StateMode Core::Entity::BattleNpc::getMode() const
|
|
|
|
{
|
|
|
|
return m_mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::setMode( Core::Entity::StateMode mode )
|
|
|
|
{
|
|
|
|
m_mode = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t Core::Entity::BattleNpc::getbehavior() const
|
|
|
|
{
|
|
|
|
return m_behavior;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::hateListAdd( Core::Entity::ActorPtr pActor, int32_t hateAmount )
|
|
|
|
{
|
|
|
|
HateListEntry* hateEntry = new HateListEntry();
|
|
|
|
hateEntry->m_hateAmount = hateAmount;
|
|
|
|
hateEntry->m_pActor = pActor;
|
|
|
|
|
|
|
|
m_hateList.insert( hateEntry );
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::Entity::ActorPtr Core::Entity::BattleNpc::hateListGetHighest()
|
|
|
|
{
|
|
|
|
|
|
|
|
auto it = m_hateList.begin();
|
|
|
|
uint32_t maxHate = 0;
|
|
|
|
HateListEntry* entry = nullptr;
|
|
|
|
for( ; it != m_hateList.end(); ++it )
|
|
|
|
{
|
|
|
|
if( ( *it )->m_hateAmount > maxHate )
|
|
|
|
{
|
|
|
|
maxHate = ( *it )->m_hateAmount;
|
|
|
|
entry = *it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( entry && maxHate != 0 )
|
|
|
|
return entry->m_pActor;
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::setOwner( Core::Entity::PlayerPtr pPlayer )
|
|
|
|
{
|
|
|
|
m_pOwner = pPlayer;
|
|
|
|
|
|
|
|
if( pPlayer != nullptr )
|
|
|
|
{
|
2017-08-20 22:31:23 +02:00
|
|
|
GamePacketNew< FFXIVIpcActorOwner, ServerZoneIpcType > setOwnerPacket( getId(), pPlayer->getId() );
|
2017-08-08 13:53:47 +02:00
|
|
|
setOwnerPacket.data().type = 0x01;
|
|
|
|
setOwnerPacket.data().actorId = pPlayer->getId();
|
|
|
|
sendToInRangeSet( setOwnerPacket );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-20 22:31:23 +02:00
|
|
|
GamePacketNew< FFXIVIpcActorOwner, ServerZoneIpcType > setOwnerPacket(getId(), INVALID_GAME_OBJECT_ID);
|
2017-08-08 13:53:47 +02:00
|
|
|
setOwnerPacket.data().type = 0x01;
|
|
|
|
setOwnerPacket.data().actorId = INVALID_GAME_OBJECT_ID;
|
|
|
|
sendToInRangeSet( setOwnerPacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::sendPositionUpdate()
|
|
|
|
{
|
|
|
|
MoveActorPacket movePacket( shared_from_this(), 0x3A, 0x00, 0, 0x5A );
|
|
|
|
sendToInRangeSet( movePacket );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::BattleNpc::moveTo( Common::FFXIVARR_POSITION3& pos )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( Math::Util::distance( getPos().x, getPos().y, getPos().z,
|
|
|
|
pos.x, pos.y, pos.z ) <= 4 )
|
|
|
|
// reached destination
|
|
|
|
return true;
|
|
|
|
|
|
|
|
float rot = Math::Util::calcAngFrom(getPos().x, getPos().z, pos.x, pos.z);
|
|
|
|
float newRot = PI - rot + (PI / 2);
|
|
|
|
|
|
|
|
face( pos );
|
|
|
|
float angle = Math::Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ) + PI;
|
|
|
|
|
|
|
|
float x = static_cast< float >( cosf(angle) * 1.1f );
|
|
|
|
float y = ( getPos().y + pos.y ) * 0.5f; // fake value while there is no collision
|
|
|
|
float z = static_cast< float >( sinf(angle) * 1.1f );
|
|
|
|
|
|
|
|
Common::FFXIVARR_POSITION3 newPos;
|
|
|
|
|
|
|
|
newPos.x = getPos().x + x;
|
|
|
|
newPos.y = y;
|
|
|
|
newPos.z = getPos().z + z;
|
|
|
|
|
|
|
|
setPosition( newPos );
|
|
|
|
|
|
|
|
Common::FFXIVARR_POSITION3 tmpPos;
|
|
|
|
tmpPos.x = getPos().x + x;
|
|
|
|
tmpPos.y = y;
|
|
|
|
tmpPos.z = getPos().z + z;
|
|
|
|
|
|
|
|
|
|
|
|
angle = angle * 2;
|
|
|
|
setPosition( tmpPos );
|
|
|
|
setRotation(newRot);
|
|
|
|
|
|
|
|
sendPositionUpdate();
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::aggro( Core::Entity::ActorPtr pActor )
|
|
|
|
{
|
|
|
|
|
|
|
|
m_lastAttack = Util::getTimeMs();
|
|
|
|
hateListUpdate( pActor, 1 );
|
|
|
|
|
|
|
|
changeTarget( pActor->getId() );
|
|
|
|
setStance( Stance::Active );
|
|
|
|
m_mode = MODE_COMBAT;
|
|
|
|
|
|
|
|
if( pActor->isPlayer() )
|
|
|
|
{
|
|
|
|
PlayerPtr tmpPlayer = pActor->getAsPlayer();
|
|
|
|
tmpPlayer->queuePacket( ActorControlPacket142( getId(), 0, 1, 1 ) );
|
|
|
|
tmpPlayer->onMobAggro( getAsBattleNpc() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::deaggro( Core::Entity::ActorPtr pActor )
|
|
|
|
{
|
|
|
|
if( !hateListHasActor( pActor ) )
|
|
|
|
hateListRemove( pActor );
|
|
|
|
|
|
|
|
if( pActor->isPlayer() )
|
|
|
|
{
|
|
|
|
PlayerPtr tmpPlayer = pActor->getAsPlayer();
|
|
|
|
tmpPlayer->onMobDeaggro( getAsBattleNpc() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::hateListClear()
|
|
|
|
{
|
|
|
|
auto it = m_hateList.begin();
|
|
|
|
for( ; it != m_hateList.end(); ++it )
|
|
|
|
{
|
|
|
|
if( isInRangeSet( ( *it )->m_pActor ) )
|
|
|
|
deaggro( ( *it )->m_pActor );
|
|
|
|
HateListEntry* tmpListEntry = ( *it );
|
|
|
|
delete tmpListEntry;
|
|
|
|
}
|
|
|
|
m_hateList.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::hateListRemove( Core::Entity::ActorPtr pActor )
|
|
|
|
{
|
|
|
|
auto it = m_hateList.begin();
|
|
|
|
for( ; it != m_hateList.end(); ++it )
|
|
|
|
{
|
|
|
|
if( ( *it )->m_pActor == pActor )
|
|
|
|
{
|
|
|
|
HateListEntry* pEntry = *it;
|
|
|
|
m_hateList.erase( it );
|
|
|
|
delete pEntry;
|
|
|
|
if( pActor->isPlayer() )
|
|
|
|
{
|
|
|
|
PlayerPtr tmpPlayer = pActor->getAsPlayer();
|
|
|
|
tmpPlayer->onMobDeaggro( getAsBattleNpc() );
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Core::Entity::BattleNpc::hateListHasActor( Core::Entity::ActorPtr pActor )
|
|
|
|
{
|
|
|
|
auto it = m_hateList.begin();
|
|
|
|
for( ; it != m_hateList.end(); ++it )
|
|
|
|
{
|
|
|
|
if( ( *it )->m_pActor == pActor )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::resetPos()
|
|
|
|
{
|
|
|
|
m_pos = m_posOrigin;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Core::Entity::BattleNpc::getNameId() const
|
|
|
|
{
|
|
|
|
return m_nameId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::hateListUpdate( Core::Entity::ActorPtr pActor, int32_t hateAmount )
|
|
|
|
{
|
|
|
|
|
|
|
|
auto it = m_hateList.begin();
|
|
|
|
for( ; it != m_hateList.end(); ++it )
|
|
|
|
{
|
|
|
|
if( ( *it )->m_pActor == pActor )
|
|
|
|
{
|
|
|
|
( *it )->m_hateAmount += hateAmount;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HateListEntry* hateEntry = new HateListEntry();
|
|
|
|
hateEntry->m_hateAmount = hateAmount;
|
|
|
|
hateEntry->m_pActor = pActor;
|
|
|
|
m_hateList.insert( hateEntry );
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::onDeath()
|
|
|
|
{
|
|
|
|
//LuaManager->onMobDeath( this );
|
|
|
|
|
|
|
|
setTimeOfDeath( static_cast< uint32_t >( time( nullptr ) ) );
|
|
|
|
setTargetId( INVALID_GAME_OBJECT_ID );
|
|
|
|
m_currentStance = Stance::Passive;
|
|
|
|
m_mode = MODE_IDLE;
|
|
|
|
m_hp = 0;
|
|
|
|
setOwner( nullptr );
|
|
|
|
|
|
|
|
// todo: fully ghetto retarded exp reward pls fix
|
|
|
|
{
|
|
|
|
uint32_t minHate = -1;
|
|
|
|
uint32_t maxHate = 0;
|
|
|
|
uint32_t totalHate = 0;
|
|
|
|
for( auto& pHateEntry : m_hateList )
|
|
|
|
{
|
|
|
|
if( pHateEntry->m_pActor->isPlayer() )
|
|
|
|
{
|
|
|
|
if( pHateEntry->m_hateAmount < minHate )
|
|
|
|
minHate = pHateEntry->m_hateAmount;
|
|
|
|
else if( pHateEntry->m_hateAmount > maxHate )
|
|
|
|
maxHate = pHateEntry->m_hateAmount;
|
|
|
|
}
|
|
|
|
totalHate += pHateEntry->m_hateAmount;
|
|
|
|
}
|
|
|
|
|
|
|
|
//uint32_t plsBeHatedThisMuchAtLeast = totalHate / ( maxHate + 2 ) / clamp( m_hateList.size(), 1.0f, 1.5f );
|
|
|
|
|
|
|
|
for( auto& pHateEntry : m_hateList )
|
|
|
|
{
|
|
|
|
// todo: this is pure retarded
|
|
|
|
// todo: check for companion
|
|
|
|
if( pHateEntry->m_pActor->isPlayer() ) // && pHateEntry->m_hateAmount >= plsBeHatedThisMuchAtLeast )
|
|
|
|
{
|
|
|
|
auto level = pHateEntry->m_pActor->getLevel();
|
|
|
|
auto levelDiff = (int)this->m_level - (int)level;
|
|
|
|
auto cappedLevelDiff = Math::Util::clamp( levelDiff, 1, 6 );
|
|
|
|
|
|
|
|
auto expNeeded = g_exdData.m_paramGrowthInfoMap[m_level + cappedLevelDiff - 1].needed_exp;
|
|
|
|
int32_t exp = 0;
|
|
|
|
|
|
|
|
// todo: arbitrary numbers pulled out of my ass
|
|
|
|
if( m_level <= 14 )
|
|
|
|
exp = ( expNeeded / ( 100 - levelDiff) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 9 ) ) + 1 ) );
|
|
|
|
else if( m_level <= 24 )
|
|
|
|
exp = ( expNeeded / ( 150 - levelDiff) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 8 ) ) + 1 ) );
|
|
|
|
else if( m_level <= 34 )
|
|
|
|
exp = ( expNeeded / ( 350 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 7 ) ) + 1 ) );
|
|
|
|
else if( m_level <= 44 )
|
|
|
|
exp = ( expNeeded / ( 550 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 6 ) ) + 1 ) );
|
|
|
|
else if( m_level <= 50 )
|
|
|
|
exp = ( expNeeded / ( 750 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 5 ) ) + 1 ) );
|
|
|
|
else
|
|
|
|
exp = ( expNeeded / ( 1200 - levelDiff ) ) + cappedLevelDiff + ( cappedLevelDiff * ( ( rand() % ( cappedLevelDiff * 4 ) ) + 1 ) );
|
|
|
|
|
|
|
|
|
|
|
|
// todo: this is actually retarded, we need real rand()
|
|
|
|
srand( time( NULL ) );
|
|
|
|
|
|
|
|
auto pPlayer = pHateEntry->m_pActor->getAsPlayer();
|
|
|
|
pPlayer->gainExp( exp );
|
|
|
|
pPlayer->onMobKill( m_nameId );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hateListClear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::onActionHostile( Core::Entity::ActorPtr pSource )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( hateListGetHighest() == nullptr )
|
|
|
|
aggro( pSource );
|
|
|
|
|
|
|
|
if( getClaimer() == nullptr )
|
|
|
|
setOwner( pSource->getAsPlayer() );
|
|
|
|
}
|
|
|
|
|
|
|
|
Core::Entity::ActorPtr Core::Entity::BattleNpc::getClaimer() const
|
|
|
|
{
|
|
|
|
return m_pOwner;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// HACK: this is highly experimental code, will have to be changed eventually
|
|
|
|
// since there are different types of mobs... (stationary, moving...) likely to be
|
|
|
|
// handled by scripts entirely.
|
|
|
|
void Core::Entity::BattleNpc::update( int64_t currTime )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( !isAlive() )
|
|
|
|
{
|
|
|
|
m_status = ActorStatus::Idle;
|
|
|
|
m_mode = MODE_IDLE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-15 11:51:59 +02:00
|
|
|
m_pStatusEffectContainer->update();
|
2017-08-08 13:53:47 +02:00
|
|
|
float distance = Math::Util::distance( m_pos.x, m_pos.y, m_pos.z,
|
|
|
|
m_posOrigin.x, m_posOrigin.y, m_posOrigin.z );
|
|
|
|
|
|
|
|
if( ( distance > 70 ) && m_mode != MODE_RETREAT )
|
|
|
|
{
|
|
|
|
changeTarget( INVALID_GAME_OBJECT_ID );
|
|
|
|
m_mode = MODE_RETREAT;
|
|
|
|
hateListClear();
|
|
|
|
setOwner( nullptr );
|
|
|
|
}
|
|
|
|
|
|
|
|
switch( m_mode )
|
|
|
|
{
|
|
|
|
|
|
|
|
case MODE_RETREAT:
|
|
|
|
{
|
|
|
|
if( moveTo( m_posOrigin ) )
|
|
|
|
m_mode = MODE_IDLE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MODE_IDLE:
|
|
|
|
{
|
|
|
|
ActorPtr pClosestActor = getClosestActor();
|
|
|
|
|
|
|
|
if( ( pClosestActor != nullptr ) && pClosestActor->isAlive() )
|
|
|
|
{
|
|
|
|
distance = Math::Util::distance( getPos().x, getPos().y, getPos().z,
|
|
|
|
pClosestActor->getPos().x,
|
|
|
|
pClosestActor->getPos().y,
|
|
|
|
pClosestActor->getPos().z );
|
|
|
|
|
|
|
|
//if( distance < 8 && getbehavior() == 2 )
|
|
|
|
// aggro( pClosestActor );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MODE_COMBAT:
|
|
|
|
{
|
|
|
|
ActorPtr pClosestActor = hateListGetHighest();
|
|
|
|
|
|
|
|
if( pClosestActor != nullptr && !pClosestActor->isAlive() )
|
|
|
|
{
|
|
|
|
hateListRemove( pClosestActor );
|
|
|
|
pClosestActor = hateListGetHighest();
|
|
|
|
}
|
|
|
|
|
|
|
|
if( pClosestActor != nullptr )
|
|
|
|
{
|
|
|
|
distance = Math::Util::distance( getPos().x, getPos().y, getPos().z,
|
|
|
|
pClosestActor->getPos().x,
|
|
|
|
pClosestActor->getPos().y,
|
|
|
|
pClosestActor->getPos().z );
|
|
|
|
|
|
|
|
if( distance > 4 )
|
|
|
|
moveTo( pClosestActor->getPos() );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( face( pClosestActor->getPos() ) )
|
|
|
|
sendPositionUpdate();
|
|
|
|
// in combat range. ATTACK!
|
|
|
|
autoAttack( pClosestActor );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
changeTarget( INVALID_GAME_OBJECT_ID );
|
|
|
|
setStance( Stance::Passive );
|
|
|
|
setOwner( nullptr );
|
|
|
|
m_mode = MODE_RETREAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Core::Entity::BattleNpc::getTimeOfDeath() const
|
|
|
|
{
|
|
|
|
return m_timeOfDeath;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Core::Entity::BattleNpc::setTimeOfDeath( uint32_t tod )
|
|
|
|
{
|
|
|
|
m_timeOfDeath = tod;
|
|
|
|
}
|
|
|
|
|