From c5ea6ec8ac735ac665666dda319a2eb7c31b7ca5 Mon Sep 17 00:00:00 2001 From: Maru Date: Sun, 24 Sep 2017 21:02:29 -0300 Subject: [PATCH 1/5] AoE refactor; --- scripts/chai/global.inc | 9 ++- src/servers/Server_Common/Common.h | 25 ++++--- src/servers/Server_Zone/Actor/CalcBattle.cpp | 5 +- src/servers/Server_Zone/Actor/Player.cpp | 68 ++++++++----------- .../StatusEffect/StatusEffectContainer.cpp | 4 +- 5 files changed, 52 insertions(+), 59 deletions(-) diff --git a/scripts/chai/global.inc b/scripts/chai/global.inc index 48bdbe32..906a3a0b 100644 --- a/scripts/chai/global.inc +++ b/scripts/chai/global.inc @@ -228,6 +228,9 @@ global CURRENCY_TOMESTONELORE = 0X0E //////////////////////////////////////////////////////////// // Skill handle types //////////////////////////////////////////////////////////// -global STD_DAMAGE = 0X00 -global STD_HEAL = 0X01 -global STD_DOT = 0X02 \ No newline at end of file +global STD_DAMAGE = 0X03 +global STD_HEAL = 0X04 +global STD_MP_LOSS = 0X0A +global STD_MP_GAIN = 0X0B +global STD_TP_LOSS = 0X0C +global STD_TP_GAIN = 0X0D \ No newline at end of file diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h index 752eb711..84c7125d 100644 --- a/src/servers/Server_Common/Common.h +++ b/src/servers/Server_Common/Common.h @@ -548,7 +548,7 @@ namespace Core { Unaspected = 7 // Doesn't imply magical unaspected damage - could be unaspected physical }; - enum struct ActionType : int8_t + enum class ActionType : int8_t { WeaponOverride = -1, // Needs more investigation (takes the damage type of the equipped weapon)? Unknown_0 = 0, @@ -562,7 +562,7 @@ namespace Core { LimitBreak = 8, }; - enum ActionEffectType : uint8_t + enum class ActionEffectType : uint8_t { Nothing = 0, Miss = 1, @@ -581,7 +581,7 @@ namespace Core { GpGain = 14 }; - enum ActionHitSeverityType : uint8_t + enum class ActionHitSeverityType : uint8_t { NormalDamage = 0, CritHeal = 0, @@ -591,6 +591,18 @@ namespace Core { CritDirectHitDamage = 3 }; + enum class AoeType + { + None, + SingleTarget, + TargetCircle, + Cone, + Line, + Unknown, + Unknown2, + GroundCircle, // for when you set aoe like asylum + }; + enum HandleActionType : uint8_t { Event, @@ -598,13 +610,6 @@ namespace Core { Teleport }; - enum HandleSkillType : uint8_t - { - StdDamage, - StdHeal, - StdDot, - }; - enum struct PlayerStateFlag : uint8_t { NoCombat, diff --git a/src/servers/Server_Zone/Actor/CalcBattle.cpp b/src/servers/Server_Zone/Actor/CalcBattle.cpp index f33c2e5c..9195412e 100644 --- a/src/servers/Server_Zone/Actor/CalcBattle.cpp +++ b/src/servers/Server_Zone/Actor/CalcBattle.cpp @@ -44,9 +44,6 @@ float CalcBattle::calculateBaseStat( PlayerPtr pPlayer ) base = 1.63f * level + 121.02f; // ARR Base Stat Formula (Off by one in several cases) else - // Old: base = 0.053f * ( level * level ) + ( 1.022f * level ) - 0.907f + 20; - // V1: base = 0.0523f * ( level * level ) + ( 1.04f * level ) + 19.405f; - // V2: base = 0.05223f * ( level * level ) + ( 1.0405f * level ) + 19.405f; base = 0.052602f * ( level * level ) + ( 1.0179f * level ) + 19.6f; return base; @@ -83,7 +80,7 @@ uint32_t CalcBattle::calculateMaxHp( PlayerPtr pPlayer ) else if ( level >= 50 ) approxBaseHp = 1700 + ( ( level - 50 ) * ( 1700 * 1.04325f ) ); else - approxBaseHp = paramGrowthInfoIt->second.mp_const * 0.7596f; + approxBaseHp = paramGrowthInfoIt->second.mp_const * 0.7667f; uint16_t result = static_cast< uint16_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) + floor( hpMod / 100.0f * ( vitStat - baseStat ) ) ); diff --git a/src/servers/Server_Zone/Actor/Player.cpp b/src/servers/Server_Zone/Actor/Player.cpp index 49bc2ad3..d91be9bb 100644 --- a/src/servers/Server_Zone/Actor/Player.cpp +++ b/src/servers/Server_Zone/Actor/Player.cpp @@ -1518,30 +1518,35 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, { sendDebug( std::to_string( pTarget.getId() ) ); sendDebug( "Handle script skill type: " + std::to_string( type ) ); - + auto actionInfoPtr = g_exdData.getActionInfo( actionId ); + sendDebug( actionInfoPtr->name ); + if ( actionInfoPtr->is_aoe ) + sendDebug( "is aoe: " + std::to_string( actionInfoPtr->is_aoe ) ); - switch( type ) + GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); + effectPacket.data().targetId = pTarget.getId(); + effectPacket.data().actionAnimationId = actionId; + 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().numEffects = 1; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); + effectPacket.data().effectTarget = pTarget.getId(); + effectPacket.data().effects[0].value = 0; + effectPacket.data().effects[0].effectType = static_cast < ActionEffectType >( type ); + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; + effectPacket.data().effects[0].unknown_3 = 7; + + switch ( type ) { - case Core::Common::HandleSkillType::StdDamage: + case 3: { sendDebug( "STD_DAMAGE" ); - GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); - effectPacket.data().targetId = pTarget.getId(); - effectPacket.data().actionAnimationId = actionId; - 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().numEffects = 1; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - effectPacket.data().effectTarget = pTarget.getId(); effectPacket.data().effects[0].value = static_cast< int16_t >( param1 ); - effectPacket.data().effects[0].effectType = ActionEffectType::Damage; - effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; - effectPacket.data().effects[0].unknown_3 = 7; sendToInRangeSet( effectPacket, true ); @@ -1553,25 +1558,15 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, break; } - case Core::Common::HandleSkillType::StdHeal: + case 4: { uint32_t calculatedHeal = CalcBattle::calculateHealValue( getAsPlayer(), static_cast< uint32_t >( param1 ) ); - sendDebug( "STD_HEAL" ); - - GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); - effectPacket.data().targetId = pTarget.getId(); - effectPacket.data().actionAnimationId = actionId; - 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().numEffects = 1; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - effectPacket.data().effectTarget = pTarget.getId(); - effectPacket.data().effects[0].value = calculatedHeal; + effectPacket.data().effects[0].value = static_cast< int16_t >( calculatedHeal ); effectPacket.data().effects[0].effectType = ActionEffectType::Heal; effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; - effectPacket.data().effects[0].unknown_3 = 7; + + sendDebug( "STD_HEAL" ); sendToInRangeSet( effectPacket, true ); @@ -1580,9 +1575,10 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, // todo: get proper packets: the following was just kind of thrown together from what we know // also toss AoE to another spot and make it generic - - if ( actionInfoPtr->is_aoe ) + sendDebug( actionInfoPtr->name ); + if ( actionInfoPtr->is_aoe ) { + sendDebug( "IS AOE LOL" ); for ( auto pCurAct : m_inRangePlayers ) { assert( pCurAct ); @@ -1591,20 +1587,12 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, if ( Math::Util::distance( pTarget.getPos().x, pTarget.getPos().y, pTarget.getPos().z, pCurAct->getPos().x, pCurAct->getPos().y, pCurAct->getPos().z ) <= actionInfoPtr->radius ) { - GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( pCurAct->getId() ); effectPacket.data().targetId = pCurAct->getId(); effectPacket.data().unknown_1 = 1; // the magic trick for getting it to work - effectPacket.data().unknown_2 = 1; effectPacket.data().unknown_8 = 1; effectPacket.data().unknown_5 = 1; - effectPacket.data().actionAnimationId = actionId; effectPacket.data().actionTextId = 0; - effectPacket.data().numEffects = 1; effectPacket.data().effectTarget = pCurAct->getId(); - effectPacket.data().effects[0].value = calculatedHeal; - effectPacket.data().effects[0].effectType = ActionEffectType::Heal; - effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; - effectPacket.data().effects[0].unknown_3 = 7; pCurAct->sendToInRangeSet( effectPacket, true ); pCurAct->heal( calculatedHeal ); @@ -1618,7 +1606,7 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, } default: - break; + break; } } diff --git a/src/servers/Server_Zone/StatusEffect/StatusEffectContainer.cpp b/src/servers/Server_Zone/StatusEffect/StatusEffectContainer.cpp index e842d0dc..c473aa54 100644 --- a/src/servers/Server_Zone/StatusEffect/StatusEffectContainer.cpp +++ b/src/servers/Server_Zone/StatusEffect/StatusEffectContainer.cpp @@ -194,13 +194,13 @@ void Core::StatusEffect::StatusEffectContainer::update() if( thisTickDmg != 0 ) { m_pOwner->takeDamage( thisTickDmg ); - m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), HPFloatingText, 0, 3, thisTickDmg ) ); + m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Damage ), thisTickDmg ) ); } if( thisTickHeal != 0 ) { m_pOwner->heal( thisTickDmg ); - m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), HPFloatingText, 0, 4, thisTickHeal ) ); + m_pOwner->sendToInRangeSet( ActorControlPacket142( m_pOwner->getId(), HPFloatingText, 0, static_cast< uint8_t >( ActionEffectType::Heal ), thisTickHeal ) ); } } From 067474408586642d683ae78b05b95a5c64af8093 Mon Sep 17 00:00:00 2001 From: Maru Date: Wed, 27 Sep 2017 04:31:41 -0300 Subject: [PATCH 2/5] Action Collision class (AoE, filtering); Refactoring script handler; Sorted action field + aoe_width property; --- scripts/chai/skill/thm/skillDef_147.chai | 18 +++ src/libraries | 2 +- src/servers/Server_Common/Common.h | 11 +- src/servers/Server_Common/Exd/ExdData.cpp | 89 ++++++------ src/servers/Server_Common/Exd/ExdData.h | 5 +- .../Server_Zone/Action/ActionCollision.cpp | 133 ++++++++++++++++++ .../Server_Zone/Action/ActionCollision.h | 37 +++++ src/servers/Server_Zone/Actor/Actor.cpp | 110 +++++++++++++++ src/servers/Server_Zone/Actor/Actor.h | 2 + src/servers/Server_Zone/Actor/CalcBattle.cpp | 1 + src/servers/Server_Zone/Actor/CalcBattle.h | 4 +- src/servers/Server_Zone/Actor/Player.cpp | 102 +------------- src/servers/Server_Zone/Actor/Player.h | 2 - src/servers/Server_Zone/ServerZone.cpp | 4 +- 14 files changed, 361 insertions(+), 159 deletions(-) create mode 100644 scripts/chai/skill/thm/skillDef_147.chai create mode 100644 src/servers/Server_Zone/Action/ActionCollision.cpp create mode 100644 src/servers/Server_Zone/Action/ActionCollision.h diff --git a/scripts/chai/skill/thm/skillDef_147.chai b/scripts/chai/skill/thm/skillDef_147.chai new file mode 100644 index 00000000..e36950a1 --- /dev/null +++ b/scripts/chai/skill/thm/skillDef_147.chai @@ -0,0 +1,18 @@ +// Skill Name: Fire II +// Skill ID: 147 + +class skillDef_147Def +{ + def skillDef_147Def() + { + + } + + def onFinish( player, target ) + { + player.handleScriptSkill( STD_DAMAGE, 147, 80, 0, target ); + } + +}; + +GLOBAL skillDef_147 = skillDef_147Def(); \ No newline at end of file diff --git a/src/libraries b/src/libraries index 4e08821a..376501b8 160000 --- a/src/libraries +++ b/src/libraries @@ -1 +1 @@ -Subproject commit 4e08821a45adbff969f6c4863bbe156d4229ffda +Subproject commit 376501b8f441bd6b6e75b1960b118aabd72fca9d diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h index 84c7125d..62271360 100644 --- a/src/servers/Server_Common/Common.h +++ b/src/servers/Server_Common/Common.h @@ -562,7 +562,7 @@ namespace Core { LimitBreak = 8, }; - enum class ActionEffectType : uint8_t + enum ActionEffectType : uint8_t { Nothing = 0, Miss = 1, @@ -591,16 +591,17 @@ namespace Core { CritDirectHitDamage = 3 }; - enum class AoeType + enum class ActionCollisionType : uint8_t { None, SingleTarget, - TargetCircle, + Circle, Cone, - Line, + Box, Unknown, Unknown2, - GroundCircle, // for when you set aoe like asylum + PersistentArea, // for when you set aoe like asylum + Unknown3 }; enum HandleActionType : uint8_t diff --git a/src/servers/Server_Common/Exd/ExdData.cpp b/src/servers/Server_Common/Exd/ExdData.cpp index 0c5fd98f..eebb4fab 100644 --- a/src/servers/Server_Common/Exd/ExdData.cpp +++ b/src/servers/Server_Common/Exd/ExdData.cpp @@ -323,70 +323,65 @@ bool Core::Data::ExdData::loadActionInfo() continue; } - std::string name = getField< std::string >( fields, 0 ); // 0 - uint8_t category = getField< uint8_t >( fields, 3 ); // 3 + std::string name = getField< std::string >( fields, 0 ); // 0 + uint8_t category = getField< uint8_t >( fields, 3 ); // 3 - int8_t class_job = getField< int8_t >( fields, 10 ); // 10 - uint8_t unlock_level = getField< uint8_t >( fields, 11 ); // 11 - int8_t range = getField< int8_t >( fields, 13 ); // 13 - bool can_target_self = getField< bool >( fields, 14 ); // 14 - bool can_target_party = getField< bool>( fields, 15 ); // 15 - bool can_target_friendly = getField< bool >( fields, 16 ); // 16 - bool can_target_enemy = getField< bool >( fields, 17 ); // 17 + int8_t class_job = getField< int8_t >( fields, 10 ); // 10 + uint8_t unlock_level = getField< uint8_t >( fields, 11 ); // 11 + int8_t range = getField< int8_t >( fields, 13 ); // 13 + bool can_target_self = getField< bool >( fields, 14 ); // 14 + bool can_target_party = getField< bool>( fields, 15 ); // 15 + bool can_target_friendly = getField< bool >( fields, 16 ); // 16 + bool can_target_enemy = getField< bool >( fields, 17 ); // 17 - bool is_aoe = getField< bool >( fields, 20 ); // 20 + bool is_ground_aoe = getField< bool >( fields, 20 ); // 20 // Column 23: Seems to be related to raising skills (Raise, Resurrection, Reanimate) - bool can_target_ko = getField< bool >( fields, 24 ); // 24 + bool can_target_ko = getField< bool >( fields, 24 ); // 24 - uint8_t aoe_type = getField< uint8_t >( fields, 26 ); // 26 - uint8_t radius = getField< uint8_t >( fields, 27 ); // 27 + uint8_t aoe_type = getField< uint8_t >( fields, 26 ); // 26 + uint8_t aoe_range = getField< uint8_t >( fields, 27 ); // 27 + uint8_t aoe_width = getField< uint8_t >( fields, 28 ); // 28 - uint8_t points_type = getField< uint8_t >( fields, 30 ); // 30 - uint16_t points_cost = getField< uint16_t >( fields, 31 ); // 31 + uint8_t points_type = getField< uint8_t >( fields, 30 ); // 30 + uint16_t points_cost = getField< uint16_t >( fields, 31 ); // 31 - uint32_t instantval = 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 - - uint8_t typeshift = 0x6; - uint8_t mask = 1 << typeshift; - instantval &= mask; - bool final = ( instantval & mask ) == mask; - bool is_instant = final; + 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 + uint8_t aspect = getField< uint8_t >( fields, 40 ); // 40 - info->id = id; - info->name = name; - info->category = category; + info->id = id; + info->name = name; + info->category = category; - info->class_job = class_job; - info->unlock_level = unlock_level; - info->range = range; - info->can_target_self = can_target_self; - info->can_target_party = can_target_party; + info->class_job = class_job; + info->unlock_level = unlock_level; + info->range = range; + info->can_target_self = can_target_self; + info->can_target_party = can_target_party; info->can_target_friendly = can_target_friendly; - info->can_target_enemy = can_target_enemy; + info->can_target_enemy = can_target_enemy; - info->can_target_ko = can_target_ko; + info->can_target_ko = can_target_ko; - info->is_aoe = is_aoe; + info->is_ground_aoe = is_ground_aoe; - info->aoe_type = aoe_type; - info->radius = radius; + info->aoe_type = aoe_type; + info->aoe_range = aoe_range; + info->aoe_width = aoe_width; - info->points_type = points_type; - info->points_cost = points_cost; + info->points_type = points_type; + info->points_cost = points_cost; - info->is_instant = is_instant; - info->cast_time = cast_time * 100; - info->recast_time = recast_time * 100; + info->is_instant = is_instant; + info->cast_time = cast_time * 100; + info->recast_time = recast_time * 100; - info->model = model; - info->aspect = aspect; + info->model = model; + info->aspect = aspect; m_actionInfoMap.emplace( std::make_pair( info->id, info ) ); diff --git a/src/servers/Server_Common/Exd/ExdData.h b/src/servers/Server_Common/Exd/ExdData.h index a6376550..a23d21ff 100644 --- a/src/servers/Server_Common/Exd/ExdData.h +++ b/src/servers/Server_Common/Exd/ExdData.h @@ -233,12 +233,13 @@ namespace Core { bool can_target_friendly; // 16 bool can_target_enemy; // 17 - bool is_aoe; // 20 + bool is_ground_aoe; // 20 bool can_target_ko; // 24 uint8_t aoe_type; // 26 - uint8_t radius; // 27 + uint8_t aoe_range; // 27 + uint8_t aoe_width; // 28 uint8_t points_type; // 30 uint16_t points_cost; // 31 diff --git a/src/servers/Server_Zone/Action/ActionCollision.cpp b/src/servers/Server_Zone/Action/ActionCollision.cpp new file mode 100644 index 00000000..93168ba6 --- /dev/null +++ b/src/servers/Server_Zone/Action/ActionCollision.cpp @@ -0,0 +1,133 @@ +#include +#include +#include + +#include "ActionCollision.h" +#include +#include +#include +#include + +using namespace Core::Entity; +using namespace Core::Common; + +// todo: add filters for allies, enemies only etc + +bool ActionCollision::isActorCollisionValid( ActorPtr actorPtr, AoeFilter aoeFilter ) +{ + bool collisionApplicable = false; + switch ( aoeFilter ) + { + case AoeFilter::All: + { + collisionApplicable = true; + } + case AoeFilter::Players: + { + collisionApplicable = actorPtr->isPlayer(); + } + case AoeFilter::Allies: + { + // todo: implement ally NPCs + collisionApplicable = !actorPtr->isMob(); + } + case AoeFilter::Party: + { + // todo: implement party + collisionApplicable = actorPtr->isPlayer(); + } + case AoeFilter::Enemies: + { + collisionApplicable = actorPtr->isMob(); + } + } + + return ( collisionApplicable && 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< ActorPtr > actorsCollided; + + switch ( static_cast< ActionCollisionType >( actionInfo->aoe_type ) ) + { + case ActionCollisionType::None: + case ActionCollisionType::SingleTarget: + { + // 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. + // 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 ) + { + // Make sure actor exists. If it doesn't we done goofed. + assert( pActor ); + + // Don't bother wasting on collision if actor doesn't apply for it + if ( !isActorCollisionValid( pActor, aoeFilter ) ) + break; + + // Test our collision from actor with the area generated by the action from the AoE data + if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width ) ) + { + // Add it to the actors collided with the area + actorsCollided.insert( pActor ); + } + } + break; + } + case ActionCollisionType::Circle: + { + for ( auto pActor : actorsInRange ) + { + assert( pActor ); + + if ( !isActorCollisionValid( pActor, aoeFilter ) ) + break; + + if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_range ) ) + { + actorsCollided.insert( pActor ); + } + } + break; + } + case ActionCollisionType::Box: + { + for ( auto pActor : actorsInRange ) + { + assert( pActor ); + + if ( !isActorCollisionValid( pActor, aoeFilter ) ) + break; + + if ( boxCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width, actionInfo->aoe_range ) ) + { + // todo: does this actually work? + + actorsCollided.insert( pActor ); + } + } + break; + } + default: + { + break; + } + } + + return actorsCollided; +} + +bool ActionCollision::radiusCollision( FFXIVARR_POSITION3 actorPosition, FFXIVARR_POSITION3 aoePosition, uint16_t radius ) +{ + return Core::Math::Util::distance( actorPosition.x, actorPosition.y, actorPosition.z, + aoePosition.x, aoePosition.y, aoePosition.z ) <= radius; +} + +bool ActionCollision::boxCollision( FFXIVARR_POSITION3 actorPosition, FFXIVARR_POSITION3 aoePosition, uint16_t width, uint16_t height ) +{ + return actorPosition.x < aoePosition.x + width && + actorPosition.x > aoePosition.x && + actorPosition.y < aoePosition.y + height && + actorPosition.y > aoePosition.y; +} \ No newline at end of file diff --git a/src/servers/Server_Zone/Action/ActionCollision.h b/src/servers/Server_Zone/Action/ActionCollision.h new file mode 100644 index 00000000..9cd92534 --- /dev/null +++ b/src/servers/Server_Zone/Action/ActionCollision.h @@ -0,0 +1,37 @@ +#ifndef _ACTIONCOLLISION_H +#define _ACTIONCOLLISION_H + +#include + +#include +#include "Action.h" + +namespace Core { + namespace Entity { + + enum class AoeFilter + { + All, // All actors in the AoE are applicable for collision + Players, // Only players + Allies, // Only allies (players, ally NPCs) + Party, // Only party members + Enemies // Only enemies + }; + + class ActionCollision + { + public: + + static bool isActorCollisionValid( ActorPtr actorPtr, AoeFilter aoeFilter ); + static std::set< ActorPtr > getActorsHitFromAction( Common::FFXIVARR_POSITION3 aoePosition, std::set< ActorPtr > actorsInRange, boost::shared_ptr< Data::ActionInfo > actionInfo, AoeFilter aoeFilter ); + + private: + static bool radiusCollision( Common::FFXIVARR_POSITION3 actorPosition, Common::FFXIVARR_POSITION3 aoePosition, uint16_t radius ); + static bool boxCollision( Common::FFXIVARR_POSITION3 actorPosition, Common::FFXIVARR_POSITION3 aoePosition, uint16_t width, uint16_t height ); + + }; + + } +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/Actor/Actor.cpp b/src/servers/Server_Zone/Actor/Actor.cpp index 1b4e3c38..8a5b61a2 100644 --- a/src/servers/Server_Zone/Actor/Actor.cpp +++ b/src/servers/Server_Zone/Actor/Actor.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "src/servers/Server_Zone/Forwards.h" #include "src/servers/Server_Zone/Action/Action.h" @@ -15,11 +16,14 @@ #include "src/servers/Server_Zone/StatusEffect/StatusEffectContainer.h" #include "src/servers/Server_Zone/StatusEffect/StatusEffect.h" +#include "src/servers/Server_Zone/Action/ActionCollision.h" #include "src/servers/Server_Zone/ServerZone.h" #include "src/servers/Server_Zone/Session.h" +#include "CalcBattle.h" #include "Player.h" extern Core::ServerZone g_serverZone; +extern Core::Data::ExdData g_exdData; using namespace Core::Common; using namespace Core::Network::Packets; @@ -620,6 +624,112 @@ void Core::Entity::Actor::autoAttack( ActorPtr pTarget ) } } +/*! +ChaiScript Skill Handler. + +\param GamePacketPtr to send +\param bool should be send to self? +*/ +void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint32_t actionId, uint64_t param1, uint64_t param2, Entity::Actor& pTarget ) +{ + + if ( isPlayer() ) + { + getAsPlayer()->sendDebug( std::to_string( pTarget.getId() ) ); + getAsPlayer()->sendDebug( "Handle script skill type: " + std::to_string( type ) ); + } + + + auto actionInfoPtr = g_exdData.getActionInfo( actionId ); + + // Prepare packet. This is seemingly common for all packets in the action handler. + + GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); + effectPacket.data().targetId = pTarget.getId(); + effectPacket.data().actionAnimationId = actionId; + 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().numEffects = 1; + effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); + 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; + + switch ( type ) + { + + case ActionEffectType::Damage: + { + + effectPacket.data().effects[0].value = static_cast< int16_t >( param1 ); + effectPacket.data().effects[0].effectType = ActionEffectType::Damage; + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; + + std::set< ActorPtr > actorsCollided = ActionCollision::getActorsHitFromAction( pTarget.getPos(), getInRangeActors( true ), actionInfoPtr, AoeFilter::Enemies ); + + for ( auto pHitActor : actorsCollided ) + { + 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().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 + pHitActor->takeDamage( static_cast< uint32_t >( param1 ) ); + pHitActor->onActionHostile( shared_from_this() ); + + if ( isPlayer() ) + getAsPlayer()->sendDebug( "AoE hit actor " + pHitActor->getName() ); + } + + break; + } + + case ActionEffectType::Heal: + { + uint32_t calculatedHeal = Data::CalcBattle::calculateHealValue( getAsPlayer(), static_cast< uint32_t >( param1 ) ); + + effectPacket.data().effects[0].value = calculatedHeal; + effectPacket.data().effects[0].effectType = ActionEffectType::Heal; + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; + + sendToInRangeSet( effectPacket, true ); + + // 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, AoeFilter::Allies ); + + for ( auto pHitActor : actorsCollided ) + { + 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().effects[0].value = calculatedHeal + ( rand() % 15 ); + + pHitActor->sendToInRangeSet( effectPacket, true ); + pHitActor->heal( calculatedHeal ); + + if ( isPlayer() ) + getAsPlayer()->sendDebug( "AoE hit actor " + pHitActor->getName() ); + } + + break; + } + + default: + break; + } +} + /*! \param StatusEffectPtr to be applied to the actor */ void Core::Entity::Actor::addStatusEffect( StatusEffect::StatusEffectPtr pEffect ) { diff --git a/src/servers/Server_Zone/Actor/Actor.h b/src/servers/Server_Zone/Actor/Actor.h index de6b9118..fee4251a 100644 --- a/src/servers/Server_Zone/Actor/Actor.h +++ b/src/servers/Server_Zone/Actor/Actor.h @@ -231,6 +231,8 @@ public: void setStatus( ActorStatus status ); + void handleScriptSkill( uint32_t type, uint32_t actionId, uint64_t param1, uint64_t param2, Entity::Actor& target ); + virtual void autoAttack( ActorPtr pTarget ); virtual void spawn( PlayerPtr pTarget ) {} diff --git a/src/servers/Server_Zone/Actor/CalcBattle.cpp b/src/servers/Server_Zone/Actor/CalcBattle.cpp index 9195412e..d83ead39 100644 --- a/src/servers/Server_Zone/Actor/CalcBattle.cpp +++ b/src/servers/Server_Zone/Actor/CalcBattle.cpp @@ -5,6 +5,7 @@ #include "Player.h" #include +using namespace Core::Data; using namespace Core::Entity; extern Core::Data::ExdData g_exdData; diff --git a/src/servers/Server_Zone/Actor/CalcBattle.h b/src/servers/Server_Zone/Actor/CalcBattle.h index f4885725..0cbde694 100644 --- a/src/servers/Server_Zone/Actor/CalcBattle.h +++ b/src/servers/Server_Zone/Actor/CalcBattle.h @@ -5,8 +5,10 @@ #include "Actor.h" +using namespace Core::Entity; + namespace Core { -namespace Entity { +namespace Data { class CalcBattle { diff --git a/src/servers/Server_Zone/Actor/Player.cpp b/src/servers/Server_Zone/Actor/Player.cpp index d91be9bb..4c6e3fe6 100644 --- a/src/servers/Server_Zone/Actor/Player.cpp +++ b/src/servers/Server_Zone/Actor/Player.cpp @@ -216,7 +216,7 @@ void Core::Entity::Player::calculateStats() auto paramGrowthInfo = paramGrowthInfoIt->second; // TODO: put formula somewhere else... - float base = CalcBattle::calculateBaseStat( getAsPlayer() ); + float base = Data::CalcBattle::calculateBaseStat( getAsPlayer() ); m_baseStats.str = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_str ) / 100 ) + tribeInfo.mod_str ); m_baseStats.dex = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_dex ) / 100 ) + tribeInfo.mod_dex ); @@ -232,9 +232,9 @@ void Core::Entity::Player::calculateStats() m_baseStats.attackPotMagic = paramGrowthInfo.base_secondary; m_baseStats.healingPotMagic = paramGrowthInfo.base_secondary; - m_baseStats.max_mp = CalcBattle::calculateMaxMp( getAsPlayer() ); + m_baseStats.max_mp = Data::CalcBattle::calculateMaxMp( getAsPlayer() ); - m_baseStats.max_hp = CalcBattle::calculateMaxHp( getAsPlayer() ); + m_baseStats.max_hp = Data::CalcBattle::calculateMaxHp( getAsPlayer() ); if( m_mp > m_baseStats.max_mp ) m_mp = m_baseStats.max_mp; @@ -1514,102 +1514,6 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) } -void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, uint64_t param1, uint64_t param2, Entity::Actor& pTarget ) -{ - sendDebug( std::to_string( pTarget.getId() ) ); - sendDebug( "Handle script skill type: " + std::to_string( type ) ); - - auto actionInfoPtr = g_exdData.getActionInfo( actionId ); - - sendDebug( actionInfoPtr->name ); - if ( actionInfoPtr->is_aoe ) - sendDebug( "is aoe: " + std::to_string( actionInfoPtr->is_aoe ) ); - - GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); - effectPacket.data().targetId = pTarget.getId(); - effectPacket.data().actionAnimationId = actionId; - 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().numEffects = 1; - effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); - effectPacket.data().effectTarget = pTarget.getId(); - effectPacket.data().effects[0].value = 0; - effectPacket.data().effects[0].effectType = static_cast < ActionEffectType >( type ); - effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; - effectPacket.data().effects[0].unknown_3 = 7; - - switch ( type ) - { - - case 3: - { - sendDebug( "STD_DAMAGE" ); - - effectPacket.data().effects[0].value = static_cast< int16_t >( param1 ); - - sendToInRangeSet( effectPacket, true ); - - if ( !pTarget.isAlive() ) - break; - - pTarget.takeDamage( static_cast< uint32_t >( param1 ) ); - pTarget.onActionHostile( shared_from_this() ); - break; - } - - case 4: - { - uint32_t calculatedHeal = CalcBattle::calculateHealValue( getAsPlayer(), static_cast< uint32_t >( param1 ) ); - - effectPacket.data().effects[0].value = static_cast< int16_t >( calculatedHeal ); - effectPacket.data().effects[0].effectType = ActionEffectType::Heal; - effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; - - sendDebug( "STD_HEAL" ); - - sendToInRangeSet( effectPacket, true ); - - if ( !pTarget.isAlive() ) - break; - - // todo: get proper packets: the following was just kind of thrown together from what we know - // also toss AoE to another spot and make it generic - sendDebug( actionInfoPtr->name ); - if ( actionInfoPtr->is_aoe ) - { - sendDebug( "IS AOE LOL" ); - for ( auto pCurAct : m_inRangePlayers ) - { - assert( pCurAct ); - if ( !pCurAct->isAlive() ) - break; - - if ( Math::Util::distance( pTarget.getPos().x, pTarget.getPos().y, pTarget.getPos().z, pCurAct->getPos().x, pCurAct->getPos().y, pCurAct->getPos().z ) <= actionInfoPtr->radius ) - { - effectPacket.data().targetId = pCurAct->getId(); - effectPacket.data().unknown_1 = 1; // the magic trick for getting it to work - effectPacket.data().unknown_8 = 1; - effectPacket.data().unknown_5 = 1; - effectPacket.data().actionTextId = 0; - effectPacket.data().effectTarget = pCurAct->getId(); - - pCurAct->sendToInRangeSet( effectPacket, true ); - pCurAct->heal( calculatedHeal ); - sendDebug( "AoE hit actor " + pCurAct->getName() ); - } - } - } - - pTarget.heal( calculatedHeal ); - break; - } - - default: - break; - } -} - ///////////////////////////// // Content Finder diff --git a/src/servers/Server_Zone/Actor/Player.h b/src/servers/Server_Zone/Actor/Player.h index 907a5916..11104463 100644 --- a/src/servers/Server_Zone/Actor/Player.h +++ b/src/servers/Server_Zone/Actor/Player.h @@ -487,8 +487,6 @@ public: void setAutoattack( bool mode ); bool isAutoattackOn() const; - void handleScriptSkill( uint32_t type, uint32_t actionId, uint64_t param1, uint64_t param2, Entity::Actor& target ); - // Content Finder handling ////////////////////////////////////////////////////////////////////////////////////////////////////// /*! Get an unix time when the player can register into content finder again. */ diff --git a/src/servers/Server_Zone/ServerZone.cpp b/src/servers/Server_Zone/ServerZone.cpp index 16042e03..2ff42a92 100644 --- a/src/servers/Server_Zone/ServerZone.cpp +++ b/src/servers/Server_Zone/ServerZone.cpp @@ -218,11 +218,11 @@ void Core::ServerZone::run( int32_t argc, char* argv[] ) Network::HivePtr hive( new Network::Hive() ); Network::addServerToHive< Network::GameConnection >( m_ip, m_port, hive ); - g_scriptMgr.init(); - g_log.info( "ZoneMgr: Setting up zones" ); g_zoneMgr.createZones(); + g_scriptMgr.init(); + std::vector< std::thread > thread_list; thread_list.push_back( std::thread( std::bind( &Network::Hive::Run, hive.get() ) ) ); From 478c0c66da15676c41ad25f7efdcd65db063f1d6 Mon Sep 17 00:00:00 2001 From: Maru Date: Thu, 28 Sep 2017 13:12:56 -0300 Subject: [PATCH 3/5] Happy little mistakes --- src/servers/Server_Zone/Action/ActionCollision.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/servers/Server_Zone/Action/ActionCollision.cpp b/src/servers/Server_Zone/Action/ActionCollision.cpp index 93168ba6..4c193fd7 100644 --- a/src/servers/Server_Zone/Action/ActionCollision.cpp +++ b/src/servers/Server_Zone/Action/ActionCollision.cpp @@ -21,24 +21,29 @@ bool ActionCollision::isActorCollisionValid( ActorPtr actorPtr, AoeFilter aoeFil case AoeFilter::All: { collisionApplicable = true; + break; } case AoeFilter::Players: { collisionApplicable = actorPtr->isPlayer(); + break; } case AoeFilter::Allies: { // todo: implement ally NPCs collisionApplicable = !actorPtr->isMob(); + break; } case AoeFilter::Party: { // todo: implement party collisionApplicable = actorPtr->isPlayer(); + break; } case AoeFilter::Enemies: { collisionApplicable = actorPtr->isMob(); + break; } } From 66c5f4be456b5a6c84167e7137584ded0997d790 Mon Sep 17 00:00:00 2001 From: Maru Date: Thu, 28 Sep 2017 17:35:07 -0300 Subject: [PATCH 4/5] Merge --- src/servers/Server_Common/Common.h | 23 +++++++++++++++++++ src/servers/Server_Common/Exd/ExdData.cpp | 13 +++++++++++ .../DebugCommand/DebugCommandHandler.cpp | 22 ++---------------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h index 62271360..a2a243be 100644 --- a/src/servers/Server_Common/Common.h +++ b/src/servers/Server_Common/Common.h @@ -383,6 +383,29 @@ namespace Core { instance, }; + enum TerritoryIntendedUseType : uint8_t //ToDo: Add The Rest of The Territory Types and Have Better Names For Them + { + Town = 0, + OpenWorld = 1, + Inn = 2, + Dungeon = 3, + JailArea = 5, + OpeningArea = 6, + BeforeTrialDung = 7, + AllianceRaid = 8, + OpenWorldInstanceBattle = 9, + Trial = 10, + HousingArea = 13, + HousingPrivateArea = 14, + MSQPrivateArea = 15, + Raids = 16, + RaidFights = 17, + ChocoboTutorial = 21, + Wedding = 22, + BeginnerTutorial = 27, + PalaceOfTheDead = 31, + }; + enum CharaLook : uint8_t { Race = 0x00, diff --git a/src/servers/Server_Common/Exd/ExdData.cpp b/src/servers/Server_Common/Exd/ExdData.cpp index eebb4fab..17007e55 100644 --- a/src/servers/Server_Common/Exd/ExdData.cpp +++ b/src/servers/Server_Common/Exd/ExdData.cpp @@ -345,6 +345,7 @@ bool Core::Data::ExdData::loadActionInfo() uint8_t points_type = getField< uint8_t >( fields, 30 ); // 30 uint16_t points_cost = getField< uint16_t >( fields, 31 ); // 31 +<<<<<<< HEAD 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 @@ -356,6 +357,18 @@ bool Core::Data::ExdData::loadActionInfo() info->id = id; info->name = name; 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->unlock_level = unlock_level; diff --git a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp index 6a4f6e84..b3137453 100644 --- a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp +++ b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp @@ -239,25 +239,6 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye else pPlayer->setClassJob( static_cast ( id ) ); } - else if( subCommand == "no" ) - { - int32_t id; - - sscanf( params.c_str(), "%d", &id ); - - uint8_t typeshift = 0x6; - uint8_t mask = 1 << typeshift; - id &= mask; - bool final = ( id & mask ) == mask; - pPlayer->sendDebug( std::to_string(final) ); - } - else if( subCommand == "aaah" ) - { - int32_t id; - sscanf( params.c_str(), "%d", &id ); - - pPlayer->sendDebug( std::to_string( pPlayer->actionHasCastTime( id ) ) ); - } else if ( subCommand == "cfpenalty" ) { int32_t minutes; @@ -486,6 +467,7 @@ void Core::DebugCommandHandler::nudge( char * data, Entity::PlayerPtr pPlayer, b void Core::DebugCommandHandler::serverInfo( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr< Core::DebugCommand > command ) { - pPlayer->sendDebug( "SapphireServer " + Version::VERSION + " - " + Version::GIT_HASH ); + pPlayer->sendDebug( "SapphireServer " + Version::VERSION + "\nRev: " + Version::GIT_HASH ); + pPlayer->sendDebug( "Compiled: " __DATE__ " " __TIME__ ); pPlayer->sendDebug( "Sessions: " + std::to_string( g_serverZone.getSessionCount() ) ); } From 15e1c32f8bac5d176302e8533c20fdfa7872140b Mon Sep 17 00:00:00 2001 From: Maru Date: Sun, 1 Oct 2017 21:50:09 -0300 Subject: [PATCH 5/5] ActionCollision implementation work; More definitions for actionInfo; Fix compiler warnings; --- src/servers/Server_Common/Exd/ExdData.cpp | 25 ++-- src/servers/Server_Common/Exd/ExdData.h | 6 + .../Server_Zone/Action/ActionCollision.cpp | 45 ++++---- .../Server_Zone/Action/ActionCollision.h | 6 +- src/servers/Server_Zone/Actor/Actor.cpp | 107 +++++++++++------- src/servers/Server_Zone/Actor/Player.h | 2 +- src/servers/Server_Zone/Actor/PlayerQuest.cpp | 8 +- .../Server_Zone/Inventory/Inventory.cpp | 6 +- src/servers/Server_Zone/Inventory/Inventory.h | 4 +- 9 files changed, 117 insertions(+), 92 deletions(-) diff --git a/src/servers/Server_Common/Exd/ExdData.cpp b/src/servers/Server_Common/Exd/ExdData.cpp index 17007e55..3eb295fd 100644 --- a/src/servers/Server_Common/Exd/ExdData.cpp +++ b/src/servers/Server_Common/Exd/ExdData.cpp @@ -345,30 +345,20 @@ bool Core::Data::ExdData::loadActionInfo() uint8_t points_type = getField< uint8_t >( fields, 30 ); // 30 uint16_t points_cost = getField< uint16_t >( fields, 31 ); // 31 -<<<<<<< HEAD + 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 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->name = name; 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->unlock_level = unlock_level; @@ -382,6 +372,7 @@ bool Core::Data::ExdData::loadActionInfo() info->is_ground_aoe = is_ground_aoe; + info->aoe_type = aoe_type; info->aoe_range = aoe_range; info->aoe_width = aoe_width; @@ -396,6 +387,12 @@ bool Core::Data::ExdData::loadActionInfo() info->model = model; 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 ) ); } diff --git a/src/servers/Server_Common/Exd/ExdData.h b/src/servers/Server_Common/Exd/ExdData.h index a23d21ff..f34e6b35 100644 --- a/src/servers/Server_Common/Exd/ExdData.h +++ b/src/servers/Server_Common/Exd/ExdData.h @@ -250,6 +250,12 @@ namespace Core { int8_t model; // 39 uint8_t aspect; // 40 + + uint16_t toggle_status_id; // 42 + + bool affects_position; // 47 + + bool is_aoe; // Internal only }; struct EventItemInfo diff --git a/src/servers/Server_Zone/Action/ActionCollision.cpp b/src/servers/Server_Zone/Action/ActionCollision.cpp index 4c193fd7..a0ab372e 100644 --- a/src/servers/Server_Zone/Action/ActionCollision.cpp +++ b/src/servers/Server_Zone/Action/ActionCollision.cpp @@ -11,46 +11,46 @@ using namespace Core::Entity; 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; - switch ( aoeFilter ) + bool actorApplicable = false; + switch ( targetFilter ) { - case AoeFilter::All: + case TargetFilter::All: { - collisionApplicable = true; + actorApplicable = true; break; } - case AoeFilter::Players: + case TargetFilter::Players: { - collisionApplicable = actorPtr->isPlayer(); + actorApplicable = actorPtr->isPlayer(); break; } - case AoeFilter::Allies: + case TargetFilter::Allies: { // todo: implement ally NPCs - collisionApplicable = !actorPtr->isMob(); + actorApplicable = !actorPtr->isMob(); break; } - case AoeFilter::Party: + case TargetFilter::Party: { // todo: implement party - collisionApplicable = actorPtr->isPlayer(); + actorApplicable = actorPtr->isPlayer(); break; } - case AoeFilter::Enemies: + case TargetFilter::Enemies: { - collisionApplicable = actorPtr->isMob(); + actorApplicable = actorPtr->isMob(); 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; @@ -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. // 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 ) { // Make sure actor exists. If it doesn't we done goofed. assert( pActor ); // Don't bother wasting on collision if actor doesn't apply for it - if ( !isActorCollisionValid( pActor, aoeFilter ) ) - break; + if ( !isActorApplicable( pActor, targetFilter ) ) + continue; // Test our collision from actor with the area generated by the action from the AoE data if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width ) ) @@ -86,8 +85,8 @@ std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXI { assert( pActor ); - if ( !isActorCollisionValid( pActor, aoeFilter ) ) - break; + if ( !isActorApplicable( pActor, targetFilter ) ) + continue; if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_range ) ) { @@ -102,8 +101,8 @@ std::set< Core::Entity::ActorPtr > ActionCollision::getActorsHitFromAction( FFXI { assert( pActor ); - if ( !isActorCollisionValid( pActor, aoeFilter ) ) - break; + if ( !isActorApplicable( pActor, targetFilter ) ) + continue; if ( boxCollision( pActor->getPos(), aoePosition, actionInfo->aoe_width, actionInfo->aoe_range ) ) { diff --git a/src/servers/Server_Zone/Action/ActionCollision.h b/src/servers/Server_Zone/Action/ActionCollision.h index 9cd92534..cff25934 100644 --- a/src/servers/Server_Zone/Action/ActionCollision.h +++ b/src/servers/Server_Zone/Action/ActionCollision.h @@ -9,7 +9,7 @@ namespace Core { namespace Entity { - enum class AoeFilter + enum class TargetFilter { All, // All actors in the AoE are applicable for collision Players, // Only players @@ -22,8 +22,8 @@ namespace Core { { public: - static bool isActorCollisionValid( ActorPtr actorPtr, AoeFilter aoeFilter ); - static std::set< ActorPtr > getActorsHitFromAction( Common::FFXIVARR_POSITION3 aoePosition, std::set< ActorPtr > actorsInRange, boost::shared_ptr< Data::ActionInfo > actionInfo, 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, TargetFilter targetFilter ); private: static bool radiusCollision( Common::FFXIVARR_POSITION3 actorPosition, Common::FFXIVARR_POSITION3 aoePosition, uint16_t radius ); diff --git a/src/servers/Server_Zone/Actor/Actor.cpp b/src/servers/Server_Zone/Actor/Actor.cpp index 8a5b61a2..b9d9e210 100644 --- a/src/servers/Server_Zone/Actor/Actor.cpp +++ b/src/servers/Server_Zone/Actor/Actor.cpp @@ -638,54 +638,67 @@ void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint32_t actionId, u getAsPlayer()->sendDebug( std::to_string( pTarget.getId() ) ); getAsPlayer()->sendDebug( "Handle script skill type: " + std::to_string( type ) ); } - 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. GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); effectPacket.data().targetId = pTarget.getId(); 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_3 = 1; effectPacket.data().actionTextId = actionId; effectPacket.data().numEffects = 1; effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); 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 ) { case ActionEffectType::Damage: { - - effectPacket.data().effects[0].value = static_cast< int16_t >( param1 ); + effectPacket.data().effects[0].value = param1; effectPacket.data().effects[0].effectType = ActionEffectType::Damage; 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 ); - - for ( auto pHitActor : actorsCollided ) + if ( !actionInfoPtr->is_aoe ) { - 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().effects[0].value = param1 + ( rand() % 15 ); + // If action on this specific target is valid... + if ( isPlayer() && !ActionCollision::isActorApplicable( pTarget.shared_from_this(), TargetFilter::Enemies ) ) + break; - pHitActor->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->onActionHostile( shared_from_this() ); + pTarget.takeDamage( static_cast< uint32_t >( param1 ) ); + pTarget.onActionHostile( shared_from_this() ); + sendToInRangeSet( effectPacket, true ); + } + else + { - if ( isPlayer() ) - getAsPlayer()->sendDebug( "AoE hit actor " + pHitActor->getName() ); + std::set< ActorPtr > actorsCollided = ActionCollision::getActorsHitFromAction( pTarget.getPos(), getInRangeActors( true ), actionInfoPtr, TargetFilter::Enemies ); + + for ( auto pHitActor : actorsCollided ) + { + effectPacket.data().targetId = pHitActor->getId(); + effectPacket.data().effectTarget = pHitActor->getId(); + + 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->onActionHostile( shared_from_this() ); + + // Debug + if ( isPlayer() ) + { + 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; @@ -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].hitSeverity = ActionHitSeverityType::NormalHeal; - sendToInRangeSet( effectPacket, true ); - - // 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, AoeFilter::Allies ); - - for ( auto pHitActor : actorsCollided ) + if ( !actionInfoPtr->is_aoe ) { - 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().effects[0].value = calculatedHeal + ( rand() % 15 ); + if ( isPlayer() && !ActionCollision::isActorApplicable( pTarget.shared_from_this(), TargetFilter::Allies ) ) + break; - pHitActor->sendToInRangeSet( effectPacket, true ); - pHitActor->heal( calculatedHeal ); - - if ( isPlayer() ) - getAsPlayer()->sendDebug( "AoE hit actor " + pHitActor->getName() ); + 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) + std::set< ActorPtr > actorsCollided = ActionCollision::getActorsHitFromAction( pTarget.getPos(), getInRangeActors( true ), actionInfoPtr, TargetFilter::Allies ); + + for ( auto pHitActor : actorsCollided ) + { + effectPacket.data().targetId = pTarget.getId(); + effectPacket.data().effectTarget = pHitActor->getId(); + + sendToInRangeSet( effectPacket, true ); + pHitActor->heal( calculatedHeal ); + + // Debug + if ( isPlayer() ) + { + 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; } diff --git a/src/servers/Server_Zone/Actor/Player.h b/src/servers/Server_Zone/Actor/Player.h index 11104463..568419f9 100644 --- a/src/servers/Server_Zone/Actor/Player.h +++ b/src/servers/Server_Zone/Actor/Player.h @@ -103,7 +103,7 @@ public: /*! load data for currently active quests */ bool loadActiveQuests(); /*! 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 */ bool hasQuest( uint16_t questId ); /*! return the current quest sequence */ diff --git a/src/servers/Server_Zone/Actor/PlayerQuest.cpp b/src/servers/Server_Zone/Actor/PlayerQuest.cpp index c6038f61..668460a7 100644 --- a/src/servers/Server_Zone/Actor/PlayerQuest.cpp +++ b/src/servers/Server_Zone/Actor/PlayerQuest.cpp @@ -74,7 +74,7 @@ bool Core::Entity::Player::loadActiveQuests() 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 ) ) { @@ -100,7 +100,7 @@ void Core::Entity::Player::finishQuest( uint16_t questId ) m_questTracking[ii] = -1; } - boost::shared_ptr pQuest = m_activeQuests[idx]; + boost::shared_ptr< QuestActive > pQuest = m_activeQuests[idx]; m_activeQuests[idx].reset(); m_freeQuestIdxQueue.push( idx ); @@ -123,7 +123,7 @@ void Core::Entity::Player::unfinishQuest( 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 ) ) { @@ -973,7 +973,7 @@ uint8_t Core::Entity::Player::getQuestSeq( uint16_t questId ) 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 ) ) { diff --git a/src/servers/Server_Zone/Inventory/Inventory.cpp b/src/servers/Server_Zone/Inventory/Inventory.cpp index bffe043b..2b552583 100644 --- a/src/servers/Server_Zone/Inventory/Inventory.cpp +++ b/src/servers/Server_Zone/Inventory/Inventory.cpp @@ -440,14 +440,14 @@ bool Core::Inventory::removeCrystal( CrystalType type, uint32_t amount ) return true; } -bool Core::Inventory::isObtainable( uint32_t catalogId, uint16_t quantity ) +bool Core::Inventory::isObtainable( uint32_t catalogId, uint8_t quantity ) { 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 ); @@ -587,7 +587,7 @@ void Core::Inventory::swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, ui { updateContainer( fromInventoryId, fromSlotId, nullptr ); fromInventoryId = getArmoryToEquipSlot( toSlot ); - fromSlotId = m_inventoryMap[fromInventoryId]->getFreeSlot(); + fromSlotId = static_cast < uint8_t >( m_inventoryMap[fromInventoryId]->getFreeSlot() ); } auto containerTypeFrom = getContainerType( fromInventoryId ); diff --git a/src/servers/Server_Zone/Inventory/Inventory.h b/src/servers/Server_Zone/Inventory/Inventory.h index adc99715..63dab641 100644 --- a/src/servers/Server_Zone/Inventory/Inventory.h +++ b/src/servers/Server_Zone/Inventory/Inventory.h @@ -140,7 +140,7 @@ public: InvSlotPairVec getSlotsOfItemsInInventory( uint32_t catalogId ); 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 swapItem( uint16_t fromInventoryId, uint8_t fromSlotId, uint16_t toInventoryId, uint8_t toSlot ); void discardItem( uint16_t fromInventoryId, uint8_t fromSlotId ); @@ -175,7 +175,7 @@ public: bool addCrystal( CrystalType type, uint32_t amount ); /*! remove amount from the crystals of type */ 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();