1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-25 14:07:46 +00:00

ActionCollision implementation work; More definitions for actionInfo; Fix compiler warnings;

This commit is contained in:
Maru 2017-10-01 21:50:09 -03:00
parent 66c5f4be45
commit 15e1c32f8b
9 changed files with 117 additions and 92 deletions

View file

@ -345,7 +345,7 @@ bool Core::Data::ExdData::loadActionInfo()
uint8_t points_type = getField< uint8_t >( fields, 30 ); // 30 uint8_t points_type = getField< uint8_t >( fields, 30 ); // 30
uint16_t points_cost = getField< uint16_t >( fields, 31 ); // 31 uint16_t points_cost = getField< uint16_t >( fields, 31 ); // 31
<<<<<<< HEAD
bool is_instant = getField< bool >( fields, 35 ); // 35 bool is_instant = getField< bool >( fields, 35 ); // 35
uint16_t cast_time = getField< uint16_t >( fields, 36 ); // 36 uint16_t cast_time = getField< uint16_t >( fields, 36 ); // 36
uint16_t recast_time = getField< uint16_t >( fields, 37 ); // 37 uint16_t recast_time = getField< uint16_t >( fields, 37 ); // 37
@ -353,22 +353,12 @@ bool Core::Data::ExdData::loadActionInfo()
int8_t model = getField< int8_t >( fields, 39 ); // 39 int8_t model = getField< int8_t >( fields, 39 ); // 39
uint8_t aspect = getField< uint8_t >( fields, 40 ); // 40 uint8_t aspect = getField< uint8_t >( fields, 40 ); // 40
uint16_t toggle_status_id = getField< uint16_t >( fields, 42 ); // 42
bool affects_position = getField< bool >( fields, 47 ); // 47
info->id = id; info->id = id;
info->name = name; info->name = name;
info->category = category; info->category = category;
=======
bool is_instant = getField< bool >( fields, 35 ); // 35
uint16_t cast_time = getField< uint16_t >( fields, 36 ); // 36
uint16_t recast_time = getField< uint16_t >( fields, 37 ); // 37
int8_t model = getField< int8_t >( fields, 39 ); // 39: Action model
uint8_t aspect = getField< uint8_t >( fields, 40 ); // 40: Action aspect
info->id = id;
info->name = name;
info->category = category;
>>>>>>> 08f4c7651fafdaf8f4d98868a94ab4688eb71379
info->class_job = class_job; info->class_job = class_job;
info->unlock_level = unlock_level; info->unlock_level = unlock_level;
@ -382,6 +372,7 @@ bool Core::Data::ExdData::loadActionInfo()
info->is_ground_aoe = is_ground_aoe; info->is_ground_aoe = is_ground_aoe;
info->aoe_type = aoe_type; info->aoe_type = aoe_type;
info->aoe_range = aoe_range; info->aoe_range = aoe_range;
info->aoe_width = aoe_width; info->aoe_width = aoe_width;
@ -396,6 +387,12 @@ bool Core::Data::ExdData::loadActionInfo()
info->model = model; info->model = model;
info->aspect = aspect; info->aspect = aspect;
info->toggle_status_id = toggle_status_id;
info->affects_position = affects_position;
// If action type is SingleTarget with an AoE radius set, or if action type isn't SingleTarget
info->is_aoe = ( info->aoe_type == 1 && info->aoe_width != 0 ) || ( info->aoe_type != 1 );
m_actionInfoMap.emplace( std::make_pair( info->id, info ) ); m_actionInfoMap.emplace( std::make_pair( info->id, info ) );
} }

View file

@ -250,6 +250,12 @@ namespace Core {
int8_t model; // 39 int8_t model; // 39
uint8_t aspect; // 40 uint8_t aspect; // 40
uint16_t toggle_status_id; // 42
bool affects_position; // 47
bool is_aoe; // Internal only
}; };
struct EventItemInfo struct EventItemInfo

View file

@ -11,46 +11,46 @@
using namespace Core::Entity; using namespace Core::Entity;
using namespace Core::Common; using namespace Core::Common;
// todo: add filters for allies, enemies only etc // todo: add AoE actor limits (16, 32)
bool ActionCollision::isActorCollisionValid( ActorPtr actorPtr, AoeFilter aoeFilter ) bool ActionCollision::isActorApplicable( ActorPtr actorPtr, TargetFilter targetFilter )
{ {
bool collisionApplicable = false; bool actorApplicable = false;
switch ( aoeFilter ) switch ( targetFilter )
{ {
case AoeFilter::All: case TargetFilter::All:
{ {
collisionApplicable = true; actorApplicable = true;
break; break;
} }
case AoeFilter::Players: case TargetFilter::Players:
{ {
collisionApplicable = actorPtr->isPlayer(); actorApplicable = actorPtr->isPlayer();
break; break;
} }
case AoeFilter::Allies: case TargetFilter::Allies:
{ {
// todo: implement ally NPCs // todo: implement ally NPCs
collisionApplicable = !actorPtr->isMob(); actorApplicable = !actorPtr->isMob();
break; break;
} }
case AoeFilter::Party: case TargetFilter::Party:
{ {
// todo: implement party // todo: implement party
collisionApplicable = actorPtr->isPlayer(); actorApplicable = actorPtr->isPlayer();
break; break;
} }
case AoeFilter::Enemies: case TargetFilter::Enemies:
{ {
collisionApplicable = actorPtr->isMob(); actorApplicable = actorPtr->isMob();
break; break;
} }
} }
return ( collisionApplicable && actorPtr->isAlive() ); return ( actorApplicable && actorPtr->isAlive() );
} }
std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXIVARR_POSITION3 aoePosition, std::set< ActorPtr > actorsInRange, boost::shared_ptr< Core::Data::ActionInfo > actionInfo, AoeFilter aoeFilter ) std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXIVARR_POSITION3 aoePosition, std::set< ActorPtr > actorsInRange, boost::shared_ptr< Core::Data::ActionInfo > actionInfo, TargetFilter targetFilter )
{ {
std::set< ActorPtr > actorsCollided; std::set< ActorPtr > actorsCollided;
@ -61,15 +61,14 @@ std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXI
{ {
// This is actually needed. There is "splash damage" in actions marked as single target. // This is actually needed. There is "splash damage" in actions marked as single target.
// Notice how we're using aoe_width. How collision works for SingleTarget is unknown as of now. // Notice how we're using aoe_width. How collision works for SingleTarget is unknown as of now.
// TODO: Isn't it possible to stack 2 players in the same spot and glitch the action collision this way? Investigate
for ( auto pActor : actorsInRange ) for ( auto pActor : actorsInRange )
{ {
// Make sure actor exists. If it doesn't we done goofed. // Make sure actor exists. If it doesn't we done goofed.
assert( pActor ); assert( pActor );
// Don't bother wasting on collision if actor doesn't apply for it // Don't bother wasting on collision if actor doesn't apply for it
if ( !isActorCollisionValid( pActor, aoeFilter ) ) if ( !isActorApplicable( pActor, targetFilter ) )
break; continue;
// Test our collision from actor with the area generated by the action from the AoE data // Test our collision from actor with the area generated by the action from the AoE data
if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width ) ) if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width ) )
@ -86,8 +85,8 @@ std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXI
{ {
assert( pActor ); assert( pActor );
if ( !isActorCollisionValid( pActor, aoeFilter ) ) if ( !isActorApplicable( pActor, targetFilter ) )
break; continue;
if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_range ) ) if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_range ) )
{ {
@ -102,8 +101,8 @@ std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXI
{ {
assert( pActor ); assert( pActor );
if ( !isActorCollisionValid( pActor, aoeFilter ) ) if ( !isActorApplicable( pActor, targetFilter ) )
break; continue;
if ( boxCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width, actionInfo->aoe_range ) ) if ( boxCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width, actionInfo->aoe_range ) )
{ {

View file

@ -9,7 +9,7 @@
namespace Core { namespace Core {
namespace Entity { namespace Entity {
enum class AoeFilter enum class TargetFilter
{ {
All, // All actors in the AoE are applicable for collision All, // All actors in the AoE are applicable for collision
Players, // Only players Players, // Only players
@ -22,8 +22,8 @@ namespace Core {
{ {
public: public:
static bool isActorCollisionValid( ActorPtr actorPtr, AoeFilter aoeFilter ); static bool isActorApplicable( ActorPtr actorPtr, TargetFilter targetFilter );
static std::set< ActorPtr > getActorsHitFromAction( Common::FFXIVARR_POSITION3 aoePosition, std::set< ActorPtr > actorsInRange, boost::shared_ptr< Data::ActionInfo > actionInfo, AoeFilter aoeFilter ); static std::set< ActorPtr > getActorsHitFromAction( Common::FFXIVARR_POSITION3 aoePosition, std::set< ActorPtr > actorsInRange, boost::shared_ptr< Data::ActionInfo > actionInfo, TargetFilter targetFilter );
private: private:
static bool radiusCollision( Common::FFXIVARR_POSITION3 actorPosition, Common::FFXIVARR_POSITION3 aoePosition, uint16_t radius ); static bool radiusCollision( Common::FFXIVARR_POSITION3 actorPosition, Common::FFXIVARR_POSITION3 aoePosition, uint16_t radius );

View file

@ -639,53 +639,66 @@ void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint32_t actionId, u
getAsPlayer()->sendDebug( "Handle script skill type: " + std::to_string( type ) ); getAsPlayer()->sendDebug( "Handle script skill type: " + std::to_string( type ) );
} }
auto actionInfoPtr = g_exdData.getActionInfo( actionId ); auto actionInfoPtr = g_exdData.getActionInfo( actionId );
// Todo: Effect packet generator. 90% of this is basically setting params and it's basically unreadable.
// Prepare packet. This is seemingly common for all packets in the action handler. // Prepare packet. This is seemingly common for all packets in the action handler.
GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() );
effectPacket.data().targetId = pTarget.getId(); effectPacket.data().targetId = pTarget.getId();
effectPacket.data().actionAnimationId = actionId; effectPacket.data().actionAnimationId = actionId;
effectPacket.data().unknown_62 = 1; // Affects displaying action name next to number in floating text
effectPacket.data().unknown_2 = 1; // This seems to have an effect on the "double-cast finish" animation effectPacket.data().unknown_2 = 1; // This seems to have an effect on the "double-cast finish" animation
// effectPacket.data().unknown_3 = 1;
effectPacket.data().actionTextId = actionId; effectPacket.data().actionTextId = actionId;
effectPacket.data().numEffects = 1; effectPacket.data().numEffects = 1;
effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() );
effectPacket.data().effectTarget = pTarget.getId(); effectPacket.data().effectTarget = pTarget.getId();
effectPacket.data().effects[0].value = 0;
effectPacket.data().effects[0].effectType = ActionEffectType::Damage;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage;
effectPacket.data().effects[0].unknown_3 = 7;
// Todo: for each actor, calculate how much damage the calculated value should deal to them - 2-step damage calc. we only have 1-step
switch ( type ) switch ( type )
{ {
case ActionEffectType::Damage: case ActionEffectType::Damage:
{ {
effectPacket.data().effects[0].value = param1;
effectPacket.data().effects[0].value = static_cast< int16_t >( param1 );
effectPacket.data().effects[0].effectType = ActionEffectType::Damage; effectPacket.data().effects[0].effectType = ActionEffectType::Damage;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage;
effectPacket.data().effects[0].unknown_3 = 7;
std::set< ActorPtr > actorsCollided = ActionCollision::getActorsHitFromAction( pTarget.getPos(), getInRangeActors( true ), actionInfoPtr, AoeFilter::Enemies ); if ( !actionInfoPtr->is_aoe )
{
// If action on this specific target is valid...
if ( isPlayer() && !ActionCollision::isActorApplicable( pTarget.shared_from_this(), TargetFilter::Enemies ) )
break;
pTarget.takeDamage( static_cast< uint32_t >( param1 ) );
pTarget.onActionHostile( shared_from_this() );
sendToInRangeSet( effectPacket, true );
}
else
{
std::set< ActorPtr > actorsCollided = ActionCollision::getActorsHitFromAction( pTarget.getPos(), getInRangeActors( true ), actionInfoPtr, TargetFilter::Enemies );
for ( auto pHitActor : actorsCollided ) for ( auto pHitActor : actorsCollided )
{ {
effectPacket.data().targetId = pHitActor->getId(); effectPacket.data().targetId = pHitActor->getId();
effectPacket.data().unknown_1 = 1; // the magic trick for getting it to work
effectPacket.data().unknown_5 = 1;
effectPacket.data().unknown_8 = 1;
effectPacket.data().actionTextId = 0;
effectPacket.data().effectTarget = pHitActor->getId(); effectPacket.data().effectTarget = pHitActor->getId();
effectPacket.data().effects[0].value = param1 + ( rand() % 15 );
pHitActor->sendToInRangeSet( effectPacket, true ); // todo: send to range of what? ourselves? when mob script hits this is going to be lacking sendToInRangeSet( effectPacket, true ); // todo: send to range of what? ourselves? when mob script hits this is going to be lacking
pHitActor->takeDamage( static_cast< uint32_t >( param1 ) ); pHitActor->takeDamage( static_cast< uint32_t >( param1 ) );
pHitActor->onActionHostile( shared_from_this() ); pHitActor->onActionHostile( shared_from_this() );
// Debug
if ( isPlayer() ) if ( isPlayer() )
getAsPlayer()->sendDebug( "AoE hit actor " + pHitActor->getName() ); {
if ( pHitActor->isPlayer() ) {
getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) + " (" + pHitActor->getName() + ")" );
}
else
getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) );
}
}
} }
break; break;
@ -699,29 +712,39 @@ void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint32_t actionId, u
effectPacket.data().effects[0].effectType = ActionEffectType::Heal; effectPacket.data().effects[0].effectType = ActionEffectType::Heal;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal;
if ( !actionInfoPtr->is_aoe )
{
if ( isPlayer() && !ActionCollision::isActorApplicable( pTarget.shared_from_this(), TargetFilter::Allies ) )
break;
sendToInRangeSet( effectPacket, true ); sendToInRangeSet( effectPacket, true );
pTarget.heal( calculatedHeal );
}
else
{
// todo: get proper packets: the following was just kind of thrown together from what we know. atm buggy (packets look "delayed" from client)
// todo: get proper packets: the following was just kind of thrown together from what we know std::set< ActorPtr > actorsCollided = ActionCollision::getActorsHitFromAction( pTarget.getPos(), getInRangeActors( true ), actionInfoPtr, TargetFilter::Allies );
std::set< ActorPtr > actorsCollided = ActionCollision::getActorsHitFromAction( pTarget.getPos(), getInRangeActors( true ), actionInfoPtr, AoeFilter::Allies );
for ( auto pHitActor : actorsCollided ) for ( auto pHitActor : actorsCollided )
{ {
effectPacket.data().targetId = pHitActor->getId(); effectPacket.data().targetId = pTarget.getId();
effectPacket.data().unknown_1 = 1; // the magic trick for getting it to work
effectPacket.data().unknown_5 = 1;
effectPacket.data().unknown_8 = 1;
effectPacket.data().actionTextId = 0;
effectPacket.data().effectTarget = pHitActor->getId(); effectPacket.data().effectTarget = pHitActor->getId();
effectPacket.data().effects[0].value = calculatedHeal + ( rand() % 15 );
pHitActor->sendToInRangeSet( effectPacket, true ); sendToInRangeSet( effectPacket, true );
pHitActor->heal( calculatedHeal ); pHitActor->heal( calculatedHeal );
// Debug
if ( isPlayer() ) if ( isPlayer() )
getAsPlayer()->sendDebug( "AoE hit actor " + pHitActor->getName() ); {
if ( pHitActor->isPlayer() ) {
getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) + " (" + pHitActor->getName() + ")" );
}
else
getAsPlayer()->sendDebug( "AoE hit actor " + std::to_string( pHitActor->getId() ) );
}
}
} }
break; break;
} }

View file

@ -103,7 +103,7 @@ public:
/*! load data for currently active quests */ /*! load data for currently active quests */
bool loadActiveQuests(); bool loadActiveQuests();
/*! update quest ( register it as active quest if new ) */ /*! update quest ( register it as active quest if new ) */
void updateQuest( uint16_t questId, uint16_t sequence ); void updateQuest( uint16_t questId, uint8_t sequence );
/*! return true if quest is currently active */ /*! return true if quest is currently active */
bool hasQuest( uint16_t questId ); bool hasQuest( uint16_t questId );
/*! return the current quest sequence */ /*! return the current quest sequence */

View file

@ -74,7 +74,7 @@ bool Core::Entity::Player::loadActiveQuests()
void Core::Entity::Player::finishQuest( uint16_t questId ) void Core::Entity::Player::finishQuest( uint16_t questId )
{ {
int8_t idx = getQuestIndex( static_cast< uint16_t >( questId ) ); int16_t idx = getQuestIndex( questId );
if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) ) if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) )
{ {
@ -123,7 +123,7 @@ void Core::Entity::Player::unfinishQuest( uint16_t questId )
void Core::Entity::Player::removeQuest( uint16_t questId ) void Core::Entity::Player::removeQuest( uint16_t questId )
{ {
int8_t idx = getQuestIndex( static_cast< uint16_t >( questId ) ); int16_t idx = getQuestIndex( questId );
if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) ) if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) )
{ {
@ -973,7 +973,7 @@ uint8_t Core::Entity::Player::getQuestSeq( uint16_t questId )
return 0; return 0;
} }
void Core::Entity::Player::updateQuest( uint16_t questId, uint16_t sequence ) void Core::Entity::Player::updateQuest( uint16_t questId, uint8_t sequence )
{ {
if( hasQuest( questId ) ) if( hasQuest( questId ) )
{ {

View file

@ -440,14 +440,14 @@ bool Core::Inventory::removeCrystal( CrystalType type, uint32_t amount )
return true; return true;
} }
bool Core::Inventory::isObtainable( uint32_t catalogId, uint16_t quantity ) bool Core::Inventory::isObtainable( uint32_t catalogId, uint8_t quantity )
{ {
return true; return true;
} }
int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity ) int16_t Core::Inventory::addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint8_t quantity )
{ {
auto itemInfo = g_exdData.getItemInfo( catalogId ); auto itemInfo = g_exdData.getItemInfo( catalogId );
@ -587,7 +587,7 @@ void Core::Inventory::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, ui
{ {
updateContainer( fromInventoryId, fromSlotId, nullptr ); updateContainer( fromInventoryId, fromSlotId, nullptr );
fromInventoryId = getArmoryToEquipSlot( toSlot ); fromInventoryId = getArmoryToEquipSlot( toSlot );
fromSlotId = m_inventoryMap[fromInventoryId]->getFreeSlot(); fromSlotId = static_cast < uint8_t >( m_inventoryMap[fromInventoryId]->getFreeSlot() );
} }
auto containerTypeFrom = getContainerType( fromInventoryId ); auto containerTypeFrom = getContainerType( fromInventoryId );

View file

@ -140,7 +140,7 @@ public:
InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId ); InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId );
InvSlotPair getFreeBagSlot(); InvSlotPair getFreeBagSlot();
int16_t addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint16_t quantity = 1 ); int16_t addItem( uint16_t inventoryId, int8_t slotId, uint32_t catalogId, uint8_t quantity = 1 );
void moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); void moveItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot );
void swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); void swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot );
void discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ); void discardItem( uint16_t fromInventoryId, uint8_t fromSlotId );
@ -175,7 +175,7 @@ public:
bool addCrystal( CrystalType type, uint32_t amount ); bool addCrystal( CrystalType type, uint32_t amount );
/*! remove amount from the crystals of type */ /*! remove amount from the crystals of type */
bool removeCrystal( CrystalType type, uint32_t amount ); bool removeCrystal( CrystalType type, uint32_t amount );
bool isObtainable( uint32_t catalogId, uint16_t quantity ); bool isObtainable( uint32_t catalogId, uint8_t quantity );
void updateCrystalDb(); void updateCrystalDb();