1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-01 16:37:45 +00:00

Merge pull request #916 from hkAlice/bs-stuff

[3.x - EncounterFight] add prototype encounterfight, encounterstate, ifrit normal;
This commit is contained in:
Mordred 2023-03-08 21:49:42 +01:00 committed by GitHub
commit 2c7b790da3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 405 additions and 39 deletions

View file

@ -9,24 +9,26 @@ file( GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
Action/*.cpp
ContentFinder/*.cpp
DebugCommand/*.cpp
Encounter/*.cpp
Encounter/InstanceContent/*.cpp
Event/*.cpp
FreeCompany/*.cpp
Inventory/*.cpp
Linkshell/*.cpp
Manager/*.cpp
Math/*.cpp
Navi/*.cpp
Network/*.cpp
Network/Util/*.cpp
Network/Handlers/*.cpp
Network/PacketWrappers/*.cpp
Quest/*.cpp
Script/*.cpp
StatusEffect/*.cpp
Task/*.cpp
Territory/*.cpp
Territory/Housing/*.cpp
Util/*.cpp
Navi/*.cpp
Task/*.cpp
Quest/*.cpp )
Util/*.cpp )
add_executable( world ${SERVER_SOURCE_FILES} )

View file

@ -0,0 +1,75 @@
#include <memory>
#include <set>
#include <stack>
#include <Territory/InstanceContent.h>
#include <Logging/Logger.h>
#include <Actor/BNpc.h>
namespace Sapphire
{
class EncounterState
{
public:
using EncounterStatePtr = std::shared_ptr< EncounterState >;
using StateStack = std::stack< EncounterStatePtr >;
using StateStackPtr = std::shared_ptr< StateStack >;
protected:
bool m_bShouldFinish{ false };
StateStackPtr m_stateStack;
std::shared_ptr< EncounterFight > m_pEncounter;
uint64_t m_startTime{ 0 };
public:
EncounterState( std::shared_ptr< EncounterFight > pEncounter ) :
m_pEncounter( pEncounter )
{
};
virtual ~EncounterState() = default;
bool shouldFinish() { return m_bShouldFinish; };
virtual void init() = 0;
virtual void update( uint64_t deltaTime ) = 0;
virtual void finish() = 0;
};
enum class EncounterFightStatus
{
IDLE,
ACTIVE,
FAIL,
SUCCESS
};
class EncounterFight : public std::enable_shared_from_this< EncounterFight >
{
public:
EncounterFight( InstanceContentPtr pInstance ) :
m_pInstance( pInstance )
{
};
virtual ~EncounterFight() = default;
virtual void init() = 0;
virtual void start() = 0;
virtual void update( uint64_t deltaTime ) = 0;
virtual void reset() = 0;
virtual void addState( EncounterState::EncounterStatePtr pState, bool initState = true ) = 0;
virtual void addBNpc( Entity::BNpcPtr pBNpc ) = 0;
virtual void removeBNpc( uint32_t layoutId ) = 0;
virtual Entity::BNpcPtr getBNpc( uint32_t layoutId ) = 0;
virtual EncounterFightStatus getEncounterFightStatus() const = 0;
protected:
uint64_t m_startTime{ 0 };
EncounterState::StateStackPtr m_stateStack;
std::set< Entity::PlayerPtr > m_playerList;
std::unordered_map< uint32_t, Entity::BNpcPtr > m_bnpcs;
InstanceContentPtr m_pInstance;
EncounterFightStatus m_status{ EncounterFightStatus::IDLE };
};
}

View file

@ -0,0 +1,198 @@
#include <Encounter/EncounterFight.h>
namespace Sapphire
{
class IfritNormalData
{
public:
static constexpr int IFRIT = 4126276;
static constexpr int HELLFIRE = 0;
};
class IfritStateTwo : public EncounterState
{
public:
IfritStateTwo( EncounterFightPtr pEncounter ) : EncounterState( pEncounter )
{
}
void init() override
{
Logger::info( "stage 2 init" );
}
void update( uint64_t deltaTime ) override
{
if( m_startTime == 0 )
m_startTime = deltaTime;
auto timeElapsedMs = deltaTime - m_startTime;
auto pIfrit = m_pEncounter->getBNpc( IfritNormalData::IFRIT );
pIfrit->setRot( pIfrit->getRot() - .2f );
pIfrit->sendPositionUpdate();
if( timeElapsedMs > 5000 )
{
m_bShouldFinish = true;
}
}
void finish() override
{
Logger::info( "stage 2 done, going back to stage 1" );
}
};
class IfritStateOne : public EncounterState
{
public:
IfritStateOne( EncounterFightPtr pEncounter ) : EncounterState( pEncounter )
{
}
void init() override
{
Logger::info( "stage 1 init" );
}
void update( uint64_t deltaTime ) override
{
if( m_startTime == 0 )
m_startTime = deltaTime;
auto timeElapsedMs = deltaTime - m_startTime;
auto pIfrit = m_pEncounter->getBNpc( IfritNormalData::IFRIT );
pIfrit->setRot( pIfrit->getRot() + .2f );
pIfrit->sendPositionUpdate();
if( timeElapsedMs > 5000 )
{
auto ifritTwoState = std::make_shared< IfritStateTwo >( m_pEncounter );
m_pEncounter->addState( ifritTwoState );
}
if( timeElapsedMs > 12000 )
{
pIfrit->hateListGetHighest()->die();
}
}
void finish() override
{
}
};
class IfritEncounterFight : public EncounterFight
{
public:
IfritEncounterFight( InstanceContentPtr pInstance ) : EncounterFight( pInstance )
{
};
void init() override
{
m_status = EncounterFightStatus::IDLE;
m_startTime = 0;
m_stateStack = std::make_shared< EncounterState::StateStack >();
// todo: i don't like this
auto boss = m_pInstance->createBNpcFromLayoutId( IfritNormalData::IFRIT, 13884, Common::BNpcType::Enemy );
addBNpc( boss );
//instance.sendForward();
/*
auto ifritStateTwo = std::make_shared< IfritStateTwo >( m_stateStack );
m_stateStack->push( ifritStateTwo );*/
}
void addState( EncounterState::EncounterStatePtr pState, bool initState = true ) override
{
m_stateStack->push( pState );
if( initState )
pState->init();
}
void start() override
{
auto ifritInitState = std::make_shared< IfritStateOne >( shared_from_this() );
addState( ifritInitState );
m_status = EncounterFightStatus::ACTIVE;
}
void update( uint64_t deltaTime ) override
{
// todo: better way to start fights here..
auto ifrit = getBNpc( IfritNormalData::IFRIT );
if( ifrit; ifrit->hateListGetHighestValue() != 0 && m_status == EncounterFightStatus::IDLE )
{
m_startTime = deltaTime;
start();
}
if( m_status == EncounterFightStatus::ACTIVE && ifrit && !ifrit->hateListGetHighest()->isAlive() )
{
m_status = EncounterFightStatus::FAIL;
}
if( m_stateStack; !m_stateStack->empty() )
{
if( m_stateStack->top()->shouldFinish() )
{
m_stateStack->top()->finish();
m_stateStack->pop();
}
m_stateStack->top()->update( deltaTime );
}
}
void reset() override
{
auto boss = m_pInstance->getActiveBNpcByLayoutId( IfritNormalData::IFRIT );
if( boss )
{
removeBNpc( IfritNormalData::IFRIT );
m_pInstance->removeActor( boss );
}
init();
}
EncounterFightStatus getEncounterFightStatus() const override
{
return m_status;
}
void addBNpc( Entity::BNpcPtr pBNpc ) override
{
m_bnpcs[ pBNpc->getLayoutId() ] = pBNpc;
}
Entity::BNpcPtr getBNpc( uint32_t layoutId ) override
{
auto bnpc = m_bnpcs.find( layoutId );
if( bnpc != std::end( m_bnpcs ) )
return bnpc->second;
return nullptr;
}
void removeBNpc( uint32_t layoutId ) override
{
m_bnpcs.erase( layoutId );
}
};
}

View file

@ -29,6 +29,7 @@ TYPE_FORWARD( ItemContainer );
TYPE_FORWARD( Land );
TYPE_FORWARD( Linkshell );
TYPE_FORWARD( FreeCompany );
TYPE_FORWARD( EncounterFight );
namespace World
{

View file

@ -10,8 +10,7 @@ namespace Sapphire::Network::Packets::WorldPackets::Server
{
/**
* @brief The Client UI Initialization packet. This must be sent to the client
* once upon connection to configure the UI.
* @brief The MoveActor packet for updating an actor's position.
*/
class MoveActorPacket : public ZoneChannelPacket< FFXIVIpcActorMove >
{

View file

@ -290,6 +290,5 @@ namespace Sapphire::ScriptAPI
uint16_t param1, uint16_t param2 )
{
}
}

View file

@ -375,7 +375,7 @@ namespace Sapphire::ScriptAPI
};
/*!
* @brief The base class for any scripts that implement behaviour related to instance content zones
* @brief The base class for any scripts that implement behaviour related to quest battles
*/
class QuestBattleScript : public ScriptObject
{
@ -405,7 +405,6 @@ namespace Sapphire::ScriptAPI
return Common::Service< World::Manager::PlayerMgr >::ref();
}
};
}
#endif

View file

@ -241,7 +241,7 @@ Sapphire::ScriptAPI::ScriptObject** Sapphire::Scripting::ScriptLoader::getScript
}
else
{
Logger::warn( "did not find a win32initLinkshell export on a windows script target - the server will likely crash!" );
Logger::warn( "did not find a win32initWarp export on a windows script target - the server will likely crash!" );
}
#else
auto func = reinterpret_cast< getScripts >( dlsym( handle, "getScripts" ) );

View file

@ -683,10 +683,13 @@ bool Sapphire::Scripting::ScriptMgr::onZoneInit( const Territory& zone )
bool Sapphire::Scripting::ScriptMgr::onInstanceInit( InstanceContent& instance )
{
auto instId = instance.getDirectorId();
auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::InstanceContentScript >( instance.getDirectorId() );
if( script )
{
script->onInit( instance );
return true;
}
@ -700,6 +703,7 @@ bool Sapphire::Scripting::ScriptMgr::onInstanceUpdate( InstanceContent& instance
if( script )
{
script->onUpdate( instance, tickCount );
return true;
}

View file

@ -28,6 +28,8 @@
#include "InstanceContent.h"
#include "InstanceObjectCache.h"
#include <Encounter/InstanceContent/IfritNormal.h>
using namespace Sapphire::Common;
using namespace Sapphire::Network::Packets;
@ -54,6 +56,8 @@ Sapphire::InstanceContent::InstanceContent( std::shared_ptr< Excel::ExcelStruct<
m_currentBgm( pInstanceConfiguration->data().Music ),
m_instanceExpireTime( Util::getTimeSeconds() + 300 ),
m_instanceTerminateTime( 0 ),
m_instanceResetTime( 0 ),
m_instanceResetFinishTime( 0 ),
m_instanceTerminate( false )
{
@ -67,6 +71,11 @@ bool Sapphire::InstanceContent::init()
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onInstanceInit( *this );
// todo: every fight is now ifrit
m_pEncounter = std::make_shared< IfritEncounterFight >( std::dynamic_pointer_cast< InstanceContent, Territory >( shared_from_this() ) );
m_pEncounter->init();
return true;
}
@ -137,10 +146,10 @@ void Sapphire::InstanceContent::onUpdate( uint64_t tickCount )
if( it == m_playerMap.end() )
return;
auto player = it->second;
if( !player->isLoadingComplete() ||
!player->isDirectorInitialized() ||
player->hasCondition( PlayerCondition::WatchingCutscene ) )
auto pPlayer = it->second;
if( !pPlayer->isLoadingComplete() ||
!pPlayer->isDirectorInitialized() ||
pPlayer->hasCondition( PlayerCondition::WatchingCutscene ) )
return;
}
@ -158,12 +167,62 @@ void Sapphire::InstanceContent::onUpdate( uint64_t tickCount )
if( m_pEntranceEObj )
m_pEntranceEObj->setPermissionInvisibility( 1 );
m_state = DutyInProgress;
break;
}
case DutyReset:
{
// todo: revive players if trial/enclosed raid arena, add reset timer
if( m_instanceResetTime == 0 )
{
sendDutyReset();
m_instanceResetTime = tickCount + 3000;
return;
}
else if( tickCount < m_instanceResetTime )
return;
if( m_instanceResetFinishTime == 0 )
{
m_instanceResetFinishTime = tickCount + 5000;
m_pEncounter->reset();
auto& server = Common::Service< World::WorldServer >::ref();
for( const auto& playerIt : m_playerMap )
{
auto pPlayer = playerIt.second;
pPlayer->resetHp();
pPlayer->resetMp();
pPlayer->setStatus( Common::ActorStatus::Idle );
movePlayerToEntrance( *pPlayer );
auto zoneInPacket = makeActorControlSelf( pPlayer->getId(), Appear, 0x3, 0, 0, 0 );
auto setStatusPacket = makeActorControl( pPlayer->getId(), SetStatus, static_cast< uint8_t >( Common::ActorStatus::Idle ) );
server.queueForPlayer( pPlayer->getCharacterId(), zoneInPacket );
server.queueForPlayers( pPlayer->getInRangePlayerIds( true ), setStatusPacket );
}
if( m_pEntranceEObj )
m_pEntranceEObj->setPermissionInvisibility( 0 );
return;
}
else if( tickCount < m_instanceResetFinishTime )
return;
m_pEntranceEObj->setPermissionInvisibility( 1 );
sendForward();
m_state = DutyInProgress;
break;
}
case DutyInProgress:
{
@ -178,6 +237,9 @@ void Sapphire::InstanceContent::onUpdate( uint64_t tickCount )
m_instanceTerminate = true;
updateBNpcs( tickCount );
if( m_pEncounter->getEncounterFightStatus() == EncounterFightStatus::FAIL )
m_state = DutyReset;
break;
}
@ -215,6 +277,8 @@ void Sapphire::InstanceContent::onUpdate( uint64_t tickCount )
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onInstanceUpdate( *this, tickCount );
m_pEncounter->update( tickCount );
m_lastUpdate = tickCount;
}
@ -421,6 +485,17 @@ void Sapphire::InstanceContent::sendForward()
}
}
void Sapphire::InstanceContent::sendDutyReset()
{
auto& server = Common::Service< World::WorldServer >::ref();
for( const auto& playerIt : m_playerMap )
{
auto player = playerIt.second;
server.queueForPlayer( player->getCharacterId(), makeActorControlSelf( player->getId(), DirectorUpdate, getDirectorId(),
DirectorEventId::LoadingScreen, 3000 ) );
}
}
void Sapphire::InstanceContent::sendVoteState()
{
auto& server = Common::Service< World::WorldServer >::ref();
@ -454,15 +529,8 @@ Sapphire::InstanceContent::InstanceContentState Sapphire::InstanceContent::getSt
return m_state;
}
void Sapphire::InstanceContent::onBeforePlayerZoneIn( Sapphire::Entity::Player& player )
void Sapphire::InstanceContent::movePlayerToEntrance( Sapphire::Entity::Player& player )
{
// remove any players from the instance who aren't bound on zone in
if( !isPlayerBound( player.getId() ) )
player.exitInstance();
// if a player has already spawned once inside this instance, don't move them if they happen to zone in again
if( !hasPlayerPreviouslySpawned( player ) )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto& instanceObjectCache = Common::Service< InstanceObjectCache >::ref();
auto contentInfo = exdData.getRow< Excel::InstanceContent >( m_instanceContentId );
@ -487,6 +555,18 @@ void Sapphire::InstanceContent::onBeforePlayerZoneIn( Sapphire::Entity::Player&
player.setRot( PI );
player.setPos( { 0.f, 0.f, 0.f } );
}
}
void Sapphire::InstanceContent::onBeforePlayerZoneIn( Sapphire::Entity::Player& player )
{
// remove any players from the instance who aren't bound on zone in
if( !isPlayerBound( player.getId() ) )
player.exitInstance();
// if a player has already spawned once inside this instance, don't move them if they happen to zone in again
if( !hasPlayerPreviouslySpawned( player ) )
{
movePlayerToEntrance( player );
}
player.resetObjSpawnIndex();

View file

@ -5,6 +5,7 @@
#include "Event/Director.h"
#include "Forwards.h"
#include <Exd/Structs.h>
#include <memory>
namespace Sapphire
{
@ -121,6 +122,8 @@ namespace Sapphire
void sendDutyCommence();
void sendDutyReset();
void sendForward();
void sendDutyFailed( bool force );
@ -191,6 +194,8 @@ namespace Sapphire
bool isTerminationReady() const;
size_t getInstancePlayerCount() const;
void movePlayerToEntrance( Entity::Player& player );
private:
std::shared_ptr< Excel::ExcelStruct< Excel::InstanceContent > > m_instanceConfiguration;
std::shared_ptr< Excel::ExcelStruct< Excel::ContentFinderCondition > > m_contentFinderCondition;
@ -202,6 +207,8 @@ namespace Sapphire
uint32_t m_instanceExpireTime;
uint64_t m_instanceTerminateTime;
uint64_t m_instanceCommenceTime;
uint64_t m_instanceResetTime;
uint64_t m_instanceResetFinishTime;
bool m_voteState;
bool m_instanceTerminate;
@ -214,6 +221,8 @@ namespace Sapphire
// the players which are bound to the instance, regardless of inside or offline
std::set< uint32_t > m_boundPlayerIds;
EncounterFightPtr m_pEncounter;
};
}