1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-27 22:57:45 +00:00
sapphire/src/world/Actor/BNpc.cpp

499 lines
12 KiB
C++
Raw Normal View History

#include <Util/Util.h>
#include <Util/UtilMath.h>
#include <Network/PacketContainer.h>
#include <Exd/ExdDataGenerated.h>
#include <utility>
#include <Network/CommonActorControl.h>
2018-09-20 23:31:38 +02:00
#include <Network/PacketWrappers/EffectPacket.h>
#include <Network/PacketDef/Zone/ClientZoneDef.h>
#include "Forwards.h"
#include "Action/Action.h"
#include "Territory/Zone.h"
#include "Network/GameConnection.h"
#include "Network/PacketWrappers/ActorControlPacket142.h"
#include "Network/PacketWrappers/ActorControlPacket143.h"
#include "Network/PacketWrappers/ActorControlPacket144.h"
#include "Network/PacketWrappers/UpdateHpMpTpPacket.h"
#include "Network/PacketWrappers/NpcSpawnPacket.h"
#include "Network/PacketWrappers/MoveActorPacket.h"
#include "StatusEffect/StatusEffect.h"
#include "Action/ActionCollision.h"
2018-11-20 21:32:13 +01:00
#include "ServerMgr.h"
#include "Session.h"
#include "Math/CalcBattle.h"
#include "Chara.h"
#include "Player.h"
#include "BNpc.h"
#include "BNpcTemplate.h"
#include "Manager/TerritoryMgr.h"
#include "Common.h"
2019-01-21 15:15:28 +01:00
#include <Logging/Logger.h>
using namespace Sapphire::Common;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::Server;
using namespace Sapphire::Network::ActorControl;
Sapphire::Entity::BNpc::BNpc( FrameworkPtr pFw ) :
Npc( ObjKind::BattleNpc, pFw )
{
}
Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX, float posY, float posZ, float rot,
uint8_t level, uint32_t maxHp, ZonePtr pZone, FrameworkPtr pFw ) :
Npc( ObjKind::BattleNpc, pFw )
{
m_id = id;
m_modelChara = pTemplate->getModelChara();
m_displayFlags = pTemplate->getDisplayFlags();
m_pose = pTemplate->getPose();
m_aggressionMode = pTemplate->getAggressionMode();
m_weaponMain = pTemplate->getWeaponMain();
m_weaponSub = pTemplate->getWeaponSub();
m_bNpcNameId = pTemplate->getBNpcNameId();
m_bNpcBaseId = pTemplate->getBNpcBaseId();
m_enemyType = pTemplate->getEnemyType();
m_pos.x = posX;
m_pos.y = posY;
m_pos.z = posZ;
m_rot = rot;
m_level = level;
m_invincibilityType = InvincibilityNone;
m_pCurrentZone = pZone;
m_spawnPos = m_pos;
m_maxHp = maxHp;
m_maxMp = 200;
m_hp = maxHp;
m_mp = 200;
m_state = BNpcState::Idle;
2019-01-20 23:49:43 +01:00
m_status = ActorStatus::Idle;
m_baseStats.max_hp = maxHp;
m_baseStats.max_mp = 200;
memcpy( m_customize, pTemplate->getCustomize(), sizeof( m_customize ) );
memcpy( m_modelEquip, pTemplate->getModelEquip(), sizeof( m_modelEquip ) );
}
Sapphire::Entity::BNpc::~BNpc()
{
}
uint8_t Sapphire::Entity::BNpc::getAggressionMode() const
{
return m_aggressionMode;
}
uint8_t Sapphire::Entity::BNpc::getEnemyType() const
{
return m_enemyType;
}
uint64_t Sapphire::Entity::BNpc::getWeaponMain() const
{
return m_weaponMain;
}
uint64_t Sapphire::Entity::BNpc::getWeaponSub() const
{
return m_weaponSub;
}
uint16_t Sapphire::Entity::BNpc::getModelChara() const
{
return m_modelChara;
}
uint8_t Sapphire::Entity::BNpc::getLevel() const
{
return m_level;
}
uint32_t Sapphire::Entity::BNpc::getBNpcBaseId() const
{
return m_bNpcBaseId;
}
uint32_t Sapphire::Entity::BNpc::getBNpcNameId() const
{
return m_bNpcNameId;
}
void Sapphire::Entity::BNpc::spawn( PlayerPtr pTarget )
{
2019-01-20 23:49:43 +01:00
pTarget->queuePacket( std::make_shared< NpcSpawnPacket >( *this, *pTarget ) );
2018-09-26 03:32:43 -04:00
}
void Sapphire::Entity::BNpc::despawn( PlayerPtr pTarget )
{
pTarget->freePlayerSpawnId( getId() );
}
Sapphire::Entity::BNpcState Sapphire::Entity::BNpc::getState() const
{
return m_state;
}
void Sapphire::Entity::BNpc::setState( BNpcState state )
{
m_state = state;
}
2019-01-23 17:07:40 +01:00
void Sapphire::Entity::BNpc::step()
{
if( m_naviLastPath.empty() )
// No path to track
return;
if( Util::distance( getPos().x, getPos().y, getPos().z, m_naviTarget.x, m_naviTarget.y, m_naviTarget.z ) <= 4 )
{
// Reached target
m_naviLastPath.clear();
return;
}
// Check if we have to recalculate
if( Util::getTimeMs() - m_lastUpdate > 500 )
{
auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, m_naviTarget );
if( !path.empty() )
{
for( int i = 0; i < path.size(); i++ )
Logger::debug( "[STEP] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z );
m_naviLastPath = path;
m_naviLastUpdate = Util::getTimeMs();
m_naviPathStep = 0;
}
else
{
Logger::debug( "No path found for target: {0} {1} {2}", m_naviTarget.x, m_naviTarget.y, m_naviTarget.z );
}
}
auto stepPos = m_naviLastPath[m_naviPathStep];
if( Util::distance( getPos().x, getPos().y, getPos().z, stepPos.x, stepPos.y, stepPos.z ) <= 4 )
{
// Reached step in path
m_naviPathStep++;
Logger::debug( "Reached step {0}", m_naviPathStep );
stepPos = m_naviLastPath[m_naviPathStep];
}
float rot = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z );
float newRot = PI - rot + ( PI / 2 );
face( stepPos );
float angle = Util::calcAngFrom( getPos().x, getPos().z, stepPos.x, stepPos.z ) + PI;
auto x = ( cosf( angle ) * 1.1f );
auto y = ( getPos().y + stepPos.y ) * 0.5f; // fake value while there is no collision
auto z = ( sinf( angle ) * 1.1f );
Common::FFXIVARR_POSITION3 newPos{ getPos().x + x, y, getPos().z + z };
setPos( newPos );
Common::FFXIVARR_POSITION3 tmpPos{ getPos().x + x, y, getPos().z + z };
setPos( tmpPos );
setRot( newRot );
sendPositionUpdate();
}
bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos )
{
if( Util::distance( getPos().x, getPos().y, getPos().z, pos.x, pos.y, pos.z ) <= 4 )
2019-01-23 17:07:40 +01:00
// Reached destination
return true;
2019-01-23 17:07:40 +01:00
if( m_naviTarget.x == pos.x && m_naviTarget.y == pos.y && m_naviTarget.z == pos.z )
// Targets are the same
return false;
2019-01-21 14:02:47 +01:00
auto path = m_pCurrentZone->getNaviProvider()->findFollowPath( m_pos, pos );
2019-01-21 02:42:47 +01:00
2019-01-23 17:07:40 +01:00
if( !path.empty() )
2019-01-21 15:15:28 +01:00
{
2019-01-23 17:07:40 +01:00
for( int i = 0; i < path.size(); i++ )
Logger::debug( "[MOVETO] {0}: {1} {2} {3}", i, path[i].x, path[i].y, path[i].z );
2019-01-21 15:15:28 +01:00
2019-01-23 17:07:40 +01:00
m_naviLastPath = path;
m_naviTarget = pos;
m_naviPathStep = 0;
m_naviLastUpdate = Util::getTimeMs();
}
else
{
Logger::debug( "No path found for target: {0} {1} {2}", pos.x, pos.y, pos.z );
2019-01-21 15:15:28 +01:00
}
2019-01-21 02:42:47 +01:00
/*
float rot = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z );
float newRot = PI - rot + ( PI / 2 );
face( pos );
float angle = Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ) + PI;
auto x = ( cosf( angle ) * 1.1f );
auto y = ( getPos().y + pos.y ) * 0.5f; // fake value while there is no collision
auto z = ( sinf( angle ) * 1.1f );
Common::FFXIVARR_POSITION3 newPos{ getPos().x + x, y, getPos().z + z };
setPos( newPos );
Common::FFXIVARR_POSITION3 tmpPos{ getPos().x + x, y, getPos().z + z };
setPos( tmpPos );
setRot( newRot );
sendPositionUpdate();
2019-01-21 02:42:47 +01:00
*/
return false;
}
void Sapphire::Entity::BNpc::sendPositionUpdate()
{
auto movePacket = std::make_shared< MoveActorPacket >( *getAsChara(), 0x3A, 0, 0, 0x5A );
sendToInRangeSet( movePacket );
}
void Sapphire::Entity::BNpc::hateListClear()
{
auto it = m_hateList.begin();
for( auto listEntry : m_hateList )
{
if( isInRangeSet( listEntry->m_pChara ) )
deaggro( listEntry->m_pChara );
}
m_hateList.clear();
}
Sapphire::Entity::CharaPtr Sapphire::Entity::BNpc::hateListGetHighest()
{
auto it = m_hateList.begin();
uint32_t maxHate = 0;
std::shared_ptr< HateListEntry > entry;
for( ; it != m_hateList.end(); ++it )
{
if( ( *it )->m_hateAmount > maxHate )
{
maxHate = ( *it )->m_hateAmount;
entry = *it;
}
}
if( entry && maxHate != 0 )
return entry->m_pChara;
return nullptr;
}
void Sapphire::Entity::BNpc::hateListAdd( Sapphire::Entity::CharaPtr pChara, int32_t hateAmount )
{
auto hateEntry = std::make_shared< HateListEntry >();
hateEntry->m_hateAmount = hateAmount;
hateEntry->m_pChara = pChara;
m_hateList.insert( hateEntry );
}
void Sapphire::Entity::BNpc::hateListUpdate( Sapphire::Entity::CharaPtr pChara, int32_t hateAmount )
{
for( auto listEntry : m_hateList )
{
if( listEntry->m_pChara == pChara )
{
listEntry->m_hateAmount += hateAmount;
return;
}
}
auto hateEntry = std::make_shared< HateListEntry >();
hateEntry->m_hateAmount = hateAmount;
hateEntry->m_pChara = pChara;
m_hateList.insert( hateEntry );
}
void Sapphire::Entity::BNpc::hateListRemove( Sapphire::Entity::CharaPtr pChara )
{
for( auto listEntry : m_hateList )
{
if( listEntry->m_pChara == pChara )
{
m_hateList.erase( listEntry );
if( pChara->isPlayer() )
{
PlayerPtr tmpPlayer = pChara->getAsPlayer();
2019-01-19 22:56:07 +01:00
tmpPlayer->onMobDeaggro( getAsBNpc() );
}
return;
}
}
}
bool Sapphire::Entity::BNpc::hateListHasActor( Sapphire::Entity::CharaPtr pChara )
{
for( auto listEntry : m_hateList )
{
if( listEntry->m_pChara == pChara )
return true;
}
return false;
}
void Sapphire::Entity::BNpc::aggro( Sapphire::Entity::CharaPtr pChara )
{
m_lastAttack = Util::getTimeMs();
hateListUpdate( pChara, 1 );
changeTarget( pChara->getId() );
setStance( Stance::Active );
m_state = BNpcState::Combat;
if( pChara->isPlayer() )
{
PlayerPtr tmpPlayer = pChara->getAsPlayer();
tmpPlayer->queuePacket( makeActorControl142( getId(), ActorControlType::ToggleWeapon, 0, 1, 1 ) );
2019-01-19 22:56:07 +01:00
tmpPlayer->onMobAggro( getAsBNpc() );
}
}
void Sapphire::Entity::BNpc::deaggro( Sapphire::Entity::CharaPtr pChara )
{
if( !hateListHasActor( pChara ) )
hateListRemove( pChara );
if( pChara->isPlayer() )
{
PlayerPtr tmpPlayer = pChara->getAsPlayer();
2019-01-19 22:56:07 +01:00
tmpPlayer->onMobDeaggro( getAsBNpc() );
}
}
void Sapphire::Entity::BNpc::update( int64_t currTime )
{
const uint8_t minActorDistance = 4;
const uint8_t aggroRange = 8;
2019-01-21 18:25:22 +01:00
const uint8_t maxDistanceToOrigin = 1000;
if( m_status == ActorStatus::Dead )
return;
switch( m_state )
{
2019-01-21 02:42:47 +01:00
case BNpcState::Retreat:
{
if( moveTo( m_spawnPos ) )
m_state = BNpcState::Idle;
}
break;
case BNpcState::Idle:
{
// passive mobs should ignore players unless aggro'd
if( m_aggressionMode == 1 )
return;
CharaPtr pClosestChara = getClosestChara();
if( pClosestChara && pClosestChara->isAlive() )
{
2019-01-21 02:42:47 +01:00
auto distance = Util::distance( getPos().x, getPos().y, getPos().z,
pClosestChara->getPos().x,
pClosestChara->getPos().y,
pClosestChara->getPos().z );
if( distance < aggroRange && pClosestChara->isPlayer() )
aggro( pClosestChara );
//if( distance < aggroRange && getbehavior() == 2 )
// aggro( pClosestActor );
}
2019-01-21 02:42:47 +01:00
}
2019-01-21 02:42:47 +01:00
case BNpcState::Combat:
{
auto pHatedActor = hateListGetHighest();
if( !pHatedActor )
return;
2019-01-21 02:42:47 +01:00
auto distanceOrig = Util::distance( getPos().x, getPos().y, getPos().z,
m_spawnPos.x,
m_spawnPos.y,
m_spawnPos.z );
2019-01-21 02:42:47 +01:00
if( pHatedActor && !pHatedActor->isAlive() )
{
hateListRemove( pHatedActor );
pHatedActor = hateListGetHighest();
}
2019-01-21 02:42:47 +01:00
if( pHatedActor )
{
2019-01-21 02:42:47 +01:00
auto distance = Util::distance( getPos().x, getPos().y, getPos().z,
pHatedActor->getPos().x,
pHatedActor->getPos().y,
pHatedActor->getPos().z );
2019-01-21 02:42:47 +01:00
if( distanceOrig > maxDistanceToOrigin )
{
2019-01-21 02:42:47 +01:00
hateListClear();
changeTarget( INVALID_GAME_OBJECT_ID );
setStance( Stance::Passive );
//setOwner( nullptr );
m_state = BNpcState::Retreat;
2019-01-21 02:42:47 +01:00
break;
}
if( distance > minActorDistance )
moveTo( pHatedActor->getPos() );
else
{
if( face( pHatedActor->getPos() ) )
sendPositionUpdate();
// in combat range. ATTACK!
autoAttack( pHatedActor );
}
}
2019-01-21 02:42:47 +01:00
else
{
changeTarget( INVALID_GAME_OBJECT_ID );
setStance( Stance::Passive );
//setOwner( nullptr );
m_state = BNpcState::Retreat;
}
}
}
2019-01-23 17:07:40 +01:00
step();
}
void Sapphire::Entity::BNpc::onActionHostile( Sapphire::Entity::CharaPtr pSource )
{
if( !hateListGetHighest() )
aggro( pSource );
//if( !getClaimer() )
// setOwner( pSource->getAsPlayer() );
}
void Sapphire::Entity::BNpc::onDeath()
{
setTargetId( INVALID_GAME_OBJECT_ID );
m_currentStance = Stance::Passive;
m_state = BNpcState::Dead;
hateListClear();
}