1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-27 14:57:44 +00:00

Start of action target selection implementation

This commit is contained in:
NotAdam 2019-04-07 16:04:36 +10:00
parent 4e4ab53381
commit 1b49f2502d
12 changed files with 229 additions and 63 deletions

View file

@ -972,6 +972,13 @@ namespace Sapphire::Common
THREAT THREAT
}; };
enum CastType : uint8_t
{
SingleTarget = 1,
CircularAOE = 2,
RectangularAOE = 4,
};
using PlayerStateFlagList = std::vector< PlayerStateFlag >; using PlayerStateFlagList = std::vector< PlayerStateFlag >;
} }

View file

@ -45,7 +45,7 @@ foreach(_scriptDir ${children})
MODULE MODULE
${SCRIPT_BUILD_FILES} ${SCRIPT_BUILD_FILES}
"${SCRIPT_INCLUDE_FILES}" "${SCRIPT_INCLUDE_FILES}"
"${_scriptDir}/ScriptLoader.cpp" ) "${_scriptDir}/ScriptLoader.cpp" action/darkknight/ActionUnleash3621.cpp action/darkknight/ActionUnleash3621.h)
target_link_libraries( "script_${_name}" world ) target_link_libraries( "script_${_name}" world )

View file

@ -14,7 +14,10 @@ public:
void onExecute( Sapphire::Action::Action& action ) override void onExecute( Sapphire::Action::Action& action ) override
{ {
for( auto& chara : action.getHitActors() )
{
chara->setHp( chara->getHp() - chara->getMaxHp() * 0.34f );
}
} }
}; };

View file

@ -0,0 +1,24 @@
#include <Script/NativeScriptApi.h>
#include <ScriptObject.h>
#include <Actor/Player.h>
#include <Action/Action.h>
class ActionUnleash3621 :
public Sapphire::ScriptAPI::ActionScript
{
public:
ActionUnleash3621() :
Sapphire::ScriptAPI::ActionScript( 3621 )
{
}
void onExecute( Sapphire::Action::Action& action ) override
{
for( auto& chara : action.getHitActors() )
{
chara->setHp( chara->getHp() - chara->getMaxHp() * 0.34f );
}
}
};
EXPOSE_SCRIPT( ActionUnleash3621 );

View file

@ -17,6 +17,8 @@
#include "Network/PacketWrappers/ActorControlPacket143.h" #include "Network/PacketWrappers/ActorControlPacket143.h"
#include "Network/PacketWrappers/ActorControlPacket144.h" #include "Network/PacketWrappers/ActorControlPacket144.h"
#include <Network/PacketWrappers/EffectPacket.h> #include <Network/PacketWrappers/EffectPacket.h>
#include <Logging/Logger.h>
#include <Util/ActorFilter.h>
using namespace Sapphire::Common; using namespace Sapphire::Common;
using namespace Sapphire::Network; using namespace Sapphire::Network;
@ -69,8 +71,17 @@ bool Sapphire::Action::Action::init()
m_cooldownGroup = m_actionData->cooldownGroup; m_cooldownGroup = m_actionData->cooldownGroup;
m_range = m_actionData->range; m_range = m_actionData->range;
m_effectRange = m_actionData->effectRange; m_effectRange = m_actionData->effectRange;
m_castType = static_cast< Common::CastType >( m_actionData->castType );
m_aspect = static_cast< Common::ActionAspect >( m_actionData->aspect ); m_aspect = static_cast< Common::ActionAspect >( m_actionData->aspect );
// todo: move this to bitset
m_canTargetSelf = m_actionData->canTargetSelf;
m_canTargetParty = m_actionData->canTargetParty;
m_canTargetFriendly = m_actionData->canTargetFriendly;
m_canTargetHostile = m_actionData->canTargetHostile;
// todo: this one doesn't look right based on whats in that col, probably has shifted
m_canTargetDead = m_actionData->canTargetDead;
// a default range is set by the game for the class/job // a default range is set by the game for the class/job
if( m_range == -1 ) if( m_range == -1 )
{ {
@ -91,6 +102,8 @@ bool Sapphire::Action::Action::init()
// todo: add missing rows for secondaryCostType/secondaryCostType and rename the current rows to primaryCostX // todo: add missing rows for secondaryCostType/secondaryCostType and rename the current rows to primaryCostX
addDefaultActorFilters();
return true; return true;
} }
@ -297,8 +310,14 @@ void Sapphire::Action::Action::execute()
if( !hasClientsideTarget() ) if( !hasClientsideTarget() )
{ {
snapshotAffectedActors( m_hitActors );
if( !m_hitActors.empty() )
{
// only call script if actors are hit
pScriptMgr->onExecute( *this ); pScriptMgr->onExecute( *this );
} }
}
else if( auto player = m_pSource->getAsPlayer() ) else if( auto player = m_pSource->getAsPlayer() )
{ {
pScriptMgr->onEObjHit( *player, m_targetId, getId() ); pScriptMgr->onEObjHit( *player, m_targetId, getId() );
@ -313,42 +332,6 @@ void Sapphire::Action::Action::execute()
} }
} }
void Sapphire::Action::Action::calculateActionCost()
{
// todo: just a test handler for now to get MP output for each cast, not sure where we should put this
// check primary cost
switch( m_primaryCostType )
{
case ActionPrimaryCostType::None:
{
break;
}
case ActionPrimaryCostType::MagicPoints:
{
// todo: not sure if we should store the final value like this?
m_primaryCost = Math::CalcStats::calculateMpCost( *m_pSource, m_primaryCost );
break;
}
case ActionPrimaryCostType::TacticsPoints:
{
break;
}
default:
{
if( auto player = m_pSource->getAsPlayer() )
{
player->sendDebug( "action#{0} is missing a handler for cost type: {1}",
m_id, static_cast< uint8_t >( m_primaryCostType ) );
}
break;
}
}
// todo: secondary cost type needs to be handled
}
bool Sapphire::Action::Action::precheck() bool Sapphire::Action::Action::precheck()
{ {
if( auto player = m_pSource->getAsPlayer() ) if( auto player = m_pSource->getAsPlayer() )
@ -491,3 +474,101 @@ bool Sapphire::Action::Action::consumeResources()
{ {
return primaryCostCheck( true ) && secondaryCostCheck( true ); return primaryCostCheck( true ) && secondaryCostCheck( true );
} }
bool Sapphire::Action::Action::snapshotAffectedActors( std::vector< Entity::CharaPtr >& actors )
{
for( const auto& actor : m_pSource->getInRangeActors() )
{
// check for initial target validity based on flags in action exd (pc/enemy/etc.)
if( !preFilterActor( *actor ) )
continue;
for( const auto& filter : m_actorFilters )
{
if( filter->conditionApplies( *actor ) )
{
actors.push_back( actor->getAsChara() );
break;
}
}
}
if( auto player = m_pSource->getAsPlayer() )
{
player->sendDebug( "Hit {} actors with {} filters", actors.size(), m_actorFilters.size() );
for( const auto& actor : actors )
{
player->sendDebug( "hit actor#{}", actor->getId() );
}
}
}
void Sapphire::Action::Action::addActorFilter( World::Util::ActorFilterPtr filter )
{
m_actorFilters.push_back( std::move( filter ) );
}
void Sapphire::Action::Action::addDefaultActorFilters()
{
switch( m_castType )
{
case Common::CastType::SingleTarget:
{
auto filter = std::make_shared< World::Util::ActorFilterSingleTarget >( static_cast< uint32_t >( m_targetId ) );
addActorFilter( filter );
break;
}
case Common::CastType::CircularAOE:
{
auto filter = std::make_shared< World::Util::ActorFilterInRange >( m_pos, m_effectRange );
addActorFilter( filter );
break;
}
// case Common::CastType::RectangularAOE:
// {
// break;
// }
default:
{
Logger::error( "[{}] Action#{} has CastType#{} but that cast type is unhandled. Cancelling cast.",
m_pSource->getId(), getId(), m_castType );
interrupt();
}
}
}
bool Sapphire::Action::Action::preFilterActor( Sapphire::Entity::Actor& actor ) const
{
auto kind = actor.getObjKind();
// todo: are there any server side eobjs that players can hit?
if( kind != ObjKind::BattleNpc && kind != ObjKind::Player )
return false;
// todo: handle things such based on canTargetX
return true;
}
std::vector< Sapphire::Entity::CharaPtr >& Sapphire::Action::Action::getHitActors()
{
return m_hitActors;
}
Sapphire::Entity::CharaPtr Sapphire::Action::Action::getHitActor()
{
if( !m_hitActors.empty() )
{
return m_hitActors.at( 0 );
}
return nullptr;
}

View file

@ -2,6 +2,7 @@
#define _ACTION_H_ #define _ACTION_H_
#include <Common.h> #include <Common.h>
#include "Util/ActorFilter.h"
#include "ForwardsZone.h" #include "ForwardsZone.h"
namespace Sapphire::Data namespace Sapphire::Data
@ -76,6 +77,34 @@ namespace Sapphire::Action
*/ */
bool precheck(); bool precheck();
/*!
* @brief Snapshots characters affected by a cast.
* @param filters A vector of filters to be applied to the in range set of the caster
* @param actors Actors that match the filters are copied here
* @return true if actors are hit
*/
bool snapshotAffectedActors( std::vector< Entity::CharaPtr >& actors );
/*!
* @brief Adds an actor filter to this action.
* @param filter The ptr to the ActorFilter to add
*/
void addActorFilter( World::Util::ActorFilterPtr filter );
/*!
* @brief Adds the default actor filters based on the CastType entry in the Action exd.
*/
void addDefaultActorFilters();
std::vector< Entity::CharaPtr >& getHitActors();
/*!
* @brief Returns the first hit actor inside the m_hitActors vector.
* @return The CharaPtr otherwise nullptr
*/
Entity::CharaPtr getHitActor();
/*! /*!
* @brief Starts the cast. Finishes it immediately if there is no cast time (weaponskills). * @brief Starts the cast. Finishes it immediately if there is no cast time (weaponskills).
*/ */
@ -108,6 +137,8 @@ namespace Sapphire::Action
bool playerPrecheck( Entity::Player& player ); bool playerPrecheck( Entity::Player& player );
bool preFilterActor( Entity::Actor& actor ) const;
uint32_t m_id; uint32_t m_id;
Common::ActionPrimaryCostType m_primaryCostType; Common::ActionPrimaryCostType m_primaryCostType;
@ -119,7 +150,9 @@ namespace Sapphire::Action
uint8_t m_cooldownGroup; uint8_t m_cooldownGroup;
int8_t m_range; int8_t m_range;
uint8_t m_effectRange; uint8_t m_effectRange;
uint8_t m_xAxisModifier;
Common::ActionAspect m_aspect; Common::ActionAspect m_aspect;
Common::CastType m_castType;
uint32_t m_additionalData; uint32_t m_additionalData;
@ -127,12 +160,21 @@ namespace Sapphire::Action
Entity::CharaPtr m_pTarget; Entity::CharaPtr m_pTarget;
uint64_t m_targetId; uint64_t m_targetId;
bool m_canTargetSelf;
bool m_canTargetParty;
bool m_canTargetFriendly;
bool m_canTargetHostile;
bool m_canTargetDead;
Common::ActionInterruptType m_interruptType; Common::ActionInterruptType m_interruptType;
FrameworkPtr m_pFw; FrameworkPtr m_pFw;
Data::ActionPtr m_actionData; Data::ActionPtr m_actionData;
Common::FFXIVARR_POSITION3 m_pos; Common::FFXIVARR_POSITION3 m_pos;
std::vector< World::Util::ActorFilterPtr > m_actorFilters;
std::vector< Entity::CharaPtr > m_hitActors;
}; };
} }

View file

@ -45,6 +45,11 @@ void World::Manager::ActionMgr::handleTargetedPlayerAction( Entity::Player& play
{ {
auto action = Action::make_Action( player.getAsPlayer(), actionId, actionData, framework() ); auto action = Action::make_Action( player.getAsPlayer(), actionId, actionData, framework() );
action->setTargetId( targetId );
if( player.getId() == targetId )
action->setPos( player.getPos() );
if( !action->init() ) if( !action->init() )
return; return;
@ -55,8 +60,6 @@ void World::Manager::ActionMgr::handleTargetedPlayerAction( Entity::Player& play
return; return;
} }
action->setTargetId( targetId );
bootstrapAction( player, action, *actionData ); bootstrapAction( player, action, *actionData );
} }

View file

@ -17,11 +17,8 @@ Sapphire::World::Session::Session( uint32_t sessionId, FrameworkPtr pFw ) :
m_lastDataTime( Util::getTimeSeconds() ), m_lastDataTime( Util::getTimeSeconds() ),
m_lastSqlTime( Util::getTimeSeconds() ), m_lastSqlTime( Util::getTimeSeconds() ),
m_isValid( false ), m_isValid( false ),
m_pFw( pFw ) m_pFw( std::move( pFw ) ),
{ m_isReplaying( false )
}
Sapphire::World::Session::~Session()
{ {
} }

View file

@ -13,7 +13,7 @@ namespace Sapphire::World
public: public:
Session( uint32_t sessionId, FrameworkPtr pFw ); Session( uint32_t sessionId, FrameworkPtr pFw );
~Session(); ~Session() = default;
void setZoneConnection( Network::GameConnectionPtr zoneCon ); void setZoneConnection( Network::GameConnectionPtr zoneCon );

View file

@ -13,8 +13,19 @@ Sapphire::World::Util::ActorFilterInRange::ActorFilterInRange( Common::FFXIVARR_
bool Sapphire::World::Util::ActorFilterInRange::conditionApplies( const Entity::Actor& actor ) bool Sapphire::World::Util::ActorFilterInRange::conditionApplies( const Entity::Actor& actor )
{ {
return Sapphire::Util::distance( m_startPos.x, m_startPos.y, m_startPos.z, return Sapphire::Util::distance( m_startPos, actor.getPos() ) <= m_range;
actor.getPos().x, actor.getPos().y, actor.getPos().z ) <= m_range; }
///////////////////////////////////////////////////////////////////////////////////////////////////////
Sapphire::World::Util::ActorFilterSingleTarget::ActorFilterSingleTarget( uint32_t actorId ) :
m_actorId( actorId )
{
}
bool Sapphire::World::Util::ActorFilterSingleTarget::conditionApplies( const Sapphire::Entity::Actor& actor )
{
return actor.getId() == m_actorId;
} }
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////

View file

@ -9,7 +9,6 @@
namespace Sapphire::World::Util namespace Sapphire::World::Util
{ {
class ActorFilter class ActorFilter
{ {
public: public:
@ -18,6 +17,8 @@ namespace Sapphire::World::Util
virtual bool conditionApplies( const Entity::Actor& actor ) = 0; virtual bool conditionApplies( const Entity::Actor& actor ) = 0;
}; };
using ActorFilterPtr = std::shared_ptr< ActorFilter >;
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
class ActorFilterInRange : public ActorFilter class ActorFilterInRange : public ActorFilter
@ -29,18 +30,15 @@ namespace Sapphire::World::Util
bool conditionApplies( const Entity::Actor& actor ) override; bool conditionApplies( const Entity::Actor& actor ) override;
}; };
// usage in psudocode /////////////////////////////////////////////////////////////////////////////
//
// std::set< ActorPtr > filterActorList( inputSet, filter ) class ActorFilterSingleTarget : public ActorFilter
// { {
// std::set< ActorPtr > resultSet; uint32_t m_actorId;
// for( every actor in inputSet ) public:
// { explicit ActorFilterSingleTarget( uint32_t actorId );
// if( filter.conditionApplies( actor ) ) bool conditionApplies( const Entity::Actor& actor ) override;
// resultSet.insert( actor.asPointer() ); };
// }
// return resultSet;
//
} }
#endif #endif