diff --git a/src/servers/sapphire_zone/Actor/Actor.cpp b/src/servers/sapphire_zone/Actor/Actor.cpp index dca46593..88aa1c04 100644 --- a/src/servers/sapphire_zone/Actor/Actor.cpp +++ b/src/servers/sapphire_zone/Actor/Actor.cpp @@ -72,6 +72,12 @@ bool Core::Entity::Actor::isMob() const return m_objKind == ObjKind::BattleNpc; } +/*! \return true if the actor is of type resident */ +bool Core::Entity::Actor::isEventNpc() const +{ + return m_objKind == ObjKind::EventNpc; +} + /*! \return list of actors currently in range */ std::set< Core::Entity::ActorPtr > Core::Entity::Actor::getInRangeActors( bool includeSelf ) { @@ -418,6 +424,12 @@ Core::Entity::BattleNpcPtr Core::Entity::Actor::getAsBattleNpc() return boost::reinterpret_pointer_cast< Entity::BattleNpc, Entity::Actor >( shared_from_this() ); } +/*! \return pointer to this instance as EventNpcPtr */ +Core::Entity::EventNpcPtr Core::Entity::Actor::getAsEventNpc() +{ + return boost::reinterpret_pointer_cast< Entity::EventNpc, Entity::Actor >( shared_from_this() ); +} + /*! \return ActionPtr of the currently registered action, or nullptr */ Core::Action::ActionPtr Core::Entity::Actor::getCurrentAction() const { diff --git a/src/servers/sapphire_zone/Actor/Actor.h b/src/servers/sapphire_zone/Actor/Actor.h index aee58d2e..9ceffb55 100644 --- a/src/servers/sapphire_zone/Actor/Actor.h +++ b/src/servers/sapphire_zone/Actor/Actor.h @@ -218,6 +218,8 @@ public: bool isMob() const; + bool isEventNpc() const; + std::set< ActorPtr > getInRangeActors( bool includeSelf = false ); bool face( const Common::FFXIVARR_POSITION3& p ); @@ -296,6 +298,7 @@ public: PlayerPtr getAsPlayer(); BattleNpcPtr getAsBattleNpc(); + EventNpcPtr getAsEventNpc(); Action::ActionPtr getCurrentAction() const; diff --git a/src/servers/sapphire_zone/Actor/EventNpc.cpp b/src/servers/sapphire_zone/Actor/EventNpc.cpp new file mode 100644 index 00000000..5b1e4bc4 --- /dev/null +++ b/src/servers/sapphire_zone/Actor/EventNpc.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Player.h" +#include "EventNpc.h" + +#include "Network/PacketWrappers/MoveActorPacket.h" +#include "Network/PacketWrappers/ActorControlPacket142.h" +#include "Network/PacketWrappers/ActorControlPacket143.h" + +using namespace Core::Common; +using namespace Core::Network::Packets; +using namespace Core::Network::Packets::Server; + +extern Core::Logger g_log; +extern Core::Data::ExdData g_exdData; + +uint32_t Core::Entity::EventNpc::m_nextID = 1249241694; + +Core::Entity::EventNpc::EventNpc() +{ + m_id = 0; + m_objKind = ObjKind::EventNpc; + m_status = ActorStatus::Idle; +} + +Core::Entity::EventNpc::~EventNpc() +{ + +} + +Core::Entity::EventNpc::EventNpc( uint32_t enpcId, const Common::FFXIVARR_POSITION3& spawnPos, float rotation ) : Actor() +{ + EventNpc::m_nextID++; + m_id = EventNpc::m_nextID; + + m_pos = spawnPos; + m_posOrigin = spawnPos; + + m_objKind = ObjKind::EventNpc; + + m_targetId = static_cast< uint64_t >( INVALID_GAME_OBJECT_ID ); + + 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_eNpcId = enpcId; + + m_status = ActorStatus::Idle; + + m_invincibilityType = InvincibilityType::InvincibilityNone; + m_rot = rotation; + +} + +// spawn this player for pTarget +// TODO: Retail additionally sends Look+Models for EventNpcs even though it is not needed, add when the new exd reader is implemented(also counts for BNPCs) +void Core::Entity::EventNpc::spawn( PlayerPtr pTarget ) +{ + ZoneChannelPacket< FFXIVIpcNpcSpawn > spawnPacket( getId(), pTarget->getId() ); + + + spawnPacket.data().pos.x = m_pos.x; + spawnPacket.data().pos.y = m_pos.y; + spawnPacket.data().pos.z = m_pos.z; + + spawnPacket.data().targetId = pTarget->getId(); + spawnPacket.data().hPCurr = 1; + spawnPacket.data().hPMax = 1; + + spawnPacket.data().bNPCBase = m_eNpcId; + spawnPacket.data().bNPCName = m_eNpcId; + spawnPacket.data().spawnIndex = pTarget->getSpawnIdForActorId( getId() ); + + spawnPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); + + spawnPacket.data().type = static_cast< uint8_t >( m_objKind ); + + pTarget->queuePacket( spawnPacket ); +} + +// despawn +void Core::Entity::EventNpc::despawn( ActorPtr pTarget ) +{ + + auto pPlayer = pTarget->getAsPlayer(); + + pPlayer->freePlayerSpawnId( getId() ); + + ActorControlPacket143 controlPacket( m_id, DespawnZoneScreenMsg, 0x04, getId(), 0x01 ); + pPlayer->queuePacket( controlPacket ); + +} + +uint8_t Core::Entity::EventNpc::getLevel() const +{ + return m_level; +} + +void Core::Entity::EventNpc::resetPos() +{ + m_pos = m_posOrigin; +} + +uint32_t Core::Entity::EventNpc::getEnpcId() const +{ + return m_eNpcId; +} \ No newline at end of file diff --git a/src/servers/sapphire_zone/Actor/EventNpc.h b/src/servers/sapphire_zone/Actor/EventNpc.h new file mode 100644 index 00000000..fa994bbe --- /dev/null +++ b/src/servers/sapphire_zone/Actor/EventNpc.h @@ -0,0 +1,42 @@ +#ifndef _EVENTNPC_H +#define _EVENTNPC_H + +#include "Actor.h" + +namespace Core { +namespace Entity { + +// class for Mobs inheriting from Actor +class EventNpc : public Actor +{ +public: + EventNpc(); + ~EventNpc(); + + EventNpc( uint32_t enpcId, const Common::FFXIVARR_POSITION3& spawnPos, float rotation ); + + // send spawn packets to pTarget + void spawn( PlayerPtr pTarget ) override; + + // send despawn packets to pTarget + void despawn( ActorPtr pTarget ) override; + + uint8_t getLevel() const override; + + void resetPos(); + + uint32_t getEnpcId() const; + +private: + + static uint32_t m_nextID; + Common::FFXIVARR_POSITION3 m_posOrigin; + uint8_t m_level; + uint32_t m_eNpcId; + +}; + +} +} + +#endif diff --git a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp index 14c21499..14d6f05e 100644 --- a/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp +++ b/src/servers/sapphire_zone/DebugCommand/DebugCommandHandler.cpp @@ -23,6 +23,7 @@ #include "Actor/Player.h" #include "Actor/BattleNpc.h" +#include "Actor/EventNpc.h" #include "Zone/Zone.h" @@ -35,6 +36,7 @@ #include +#include "Network/PacketWrappers/PlayerSpawnPacket.h" extern Core::Scripting::ScriptManager g_scriptMgr; extern Core::Data::ExdData g_exdData; @@ -350,6 +352,33 @@ void Core::DebugCommandHandler::add( char * data, Entity::Player& player, boost: Network::Packets::GamePacketPtr pPe( new Network::Packets::GamePacket( opcode, 0x30, player.getId(), player.getId() ) ); player.queuePacket( pPe ); } + else if( subCommand == "eventnpc-self" ) + { + int32_t id; + + sscanf( params.c_str(), "%d", &id ); + + Network::Packets::ZoneChannelPacket< Network::Packets::Server::FFXIVIpcNpcSpawn > spawnPacket( player.getId() ); + spawnPacket.data().type = 3; + spawnPacket.data().pos = player.getPos(); + spawnPacket.data().rotation = player.getRotation(); + spawnPacket.data().bNPCBase = id; + spawnPacket.data().bNPCName = id; + spawnPacket.data().targetId = player.getId(); + player.queuePacket( spawnPacket ); + } + else if( subCommand == "eventnpc" ) + { + int32_t id; + + sscanf( params.c_str(), "%d", &id ); + + Entity::EventNpcPtr pENpc( new Entity::EventNpc( id, player.getPos(), player.getRotation() ) ); + + auto pZone = player.getCurrentZone(); + pENpc->setCurrentZone( pZone ); + pZone->pushActor( pENpc ); + } else if( subCommand == "actrl" ) { diff --git a/src/servers/sapphire_zone/Forwards.h b/src/servers/sapphire_zone/Forwards.h index 8dc06545..b3dbf611 100644 --- a/src/servers/sapphire_zone/Forwards.h +++ b/src/servers/sapphire_zone/Forwards.h @@ -31,6 +31,7 @@ namespace Core TYPE_FORWARD( Actor ); TYPE_FORWARD( Player ); TYPE_FORWARD( BattleNpc ); + TYPE_FORWARD( EventNpc ); TYPE_FORWARD( BattleNpcTemplate ); } diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h index 662f9574..61ad14fd 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include "Actor/Player.h" #include "Forwards.h" #include "Inventory/Inventory.h" @@ -119,7 +119,7 @@ namespace Server { //m_data.unknown_60 = 3; //m_data.unknown_61 = 7; - uint64_t currentTimeMs = Util::getTimeMs(); + uint64_t currentTimeMs = Core::Util::getTimeMs(); for( auto const& effect : player.getStatusEffectMap() ) { diff --git a/src/servers/sapphire_zone/Zone/Zone.cpp b/src/servers/sapphire_zone/Zone/Zone.cpp index c1b6dbc6..3b0fe8e0 100644 --- a/src/servers/sapphire_zone/Zone/Zone.cpp +++ b/src/servers/sapphire_zone/Zone/Zone.cpp @@ -19,6 +19,7 @@ #include "Actor/Actor.h" #include "Actor/Player.h" #include "Actor/BattleNpc.h" +#include "Actor/EventNpc.h" #include "Forwards.h" @@ -298,6 +299,12 @@ void Zone::pushActor( Entity::ActorPtr pActor ) pBNpc->setPosition( pBNpc->getPos() ); } + else if( pActor->getAsEventNpc() ) + { + Entity::EventNpcPtr pENpc = pActor->getAsEventNpc(); + m_EventNpcMap[pENpc->getId()] = pENpc; + pENpc->setPosition( pENpc->getPos() ); + } } @@ -762,7 +769,7 @@ void Zone::updateInRangeSet( Entity::ActorPtr pActor, Cell* pCell ) } } - else if( pActor->isMob() && pCurAct->isPlayer() && pActor->isAlive() ) + else if( ( pActor->isMob() || pActor->isEventNpc() ) && pCurAct->isPlayer() && pActor->isAlive() ) { auto pPlayer = pCurAct->getAsPlayer(); if( pPlayer->isLoadingComplete() ) diff --git a/src/servers/sapphire_zone/Zone/Zone.h b/src/servers/sapphire_zone/Zone/Zone.h index 4ea39cb1..4521373d 100644 --- a/src/servers/sapphire_zone/Zone/Zone.h +++ b/src/servers/sapphire_zone/Zone/Zone.h @@ -40,6 +40,7 @@ protected: std::unordered_map< int32_t, Entity::PlayerPtr > m_playerMap; std::unordered_map< int32_t, Entity::BattleNpcPtr > m_BattleNpcMap; + std::unordered_map< int32_t, Entity::EventNpcPtr > m_EventNpcMap; std::set< Entity::BattleNpcPtr > m_BattleNpcDeadMap;