mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-25 14:07:46 +00:00
Merge pull request #116 from itsmaru/master
Action Collision class (AoE); More definitions; General cleaning;
This commit is contained in:
commit
3eed7acddb
19 changed files with 433 additions and 181 deletions
|
@ -228,6 +228,9 @@ global CURRENCY_TOMESTONELORE = 0X0E
|
|||
////////////////////////////////////////////////////////////
|
||||
// Skill handle types
|
||||
////////////////////////////////////////////////////////////
|
||||
global STD_DAMAGE = 0X00
|
||||
global STD_HEAL = 0X01
|
||||
global STD_DOT = 0X02
|
||||
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
|
18
scripts/chai/skill/thm/skillDef_147.chai
Normal file
18
scripts/chai/skill/thm/skillDef_147.chai
Normal file
|
@ -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();
|
|
@ -1 +1 @@
|
|||
Subproject commit f24f9418a993c8359be74fbaf6e13bbabe21c99b
|
||||
Subproject commit 376501b8f441bd6b6e75b1960b118aabd72fca9d
|
|
@ -571,7 +571,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,
|
||||
|
@ -604,7 +604,7 @@ namespace Core {
|
|||
GpGain = 14
|
||||
};
|
||||
|
||||
enum ActionHitSeverityType : uint8_t
|
||||
enum class ActionHitSeverityType : uint8_t
|
||||
{
|
||||
NormalDamage = 0,
|
||||
CritHeal = 0,
|
||||
|
@ -614,6 +614,19 @@ namespace Core {
|
|||
CritDirectHitDamage = 3
|
||||
};
|
||||
|
||||
enum class ActionCollisionType : uint8_t
|
||||
{
|
||||
None,
|
||||
SingleTarget,
|
||||
Circle,
|
||||
Cone,
|
||||
Box,
|
||||
Unknown,
|
||||
Unknown2,
|
||||
PersistentArea, // for when you set aoe like asylum
|
||||
Unknown3
|
||||
};
|
||||
|
||||
enum HandleActionType : uint8_t
|
||||
{
|
||||
Event,
|
||||
|
@ -634,7 +647,7 @@ namespace Core {
|
|||
InvincibilityRefill,
|
||||
InvincibilityStayAlive,
|
||||
};
|
||||
|
||||
|
||||
enum struct PlayerStateFlag : uint8_t
|
||||
{
|
||||
SomeFlag,
|
||||
|
|
|
@ -323,62 +323,74 @@ 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
|
||||
|
||||
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
|
||||
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
|
||||
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;
|
||||
uint16_t toggle_status_id = getField< uint16_t >( fields, 42 ); // 42
|
||||
bool affects_position = getField< bool >( fields, 47 ); // 47
|
||||
|
||||
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->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->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->points_type = points_type;
|
||||
info->points_cost = points_cost;
|
||||
info->aoe_type = aoe_type;
|
||||
info->aoe_range = aoe_range;
|
||||
info->aoe_width = aoe_width;
|
||||
|
||||
info->is_instant = is_instant;
|
||||
info->cast_time = cast_time * 100;
|
||||
info->recast_time = recast_time * 100;
|
||||
info->points_type = points_type;
|
||||
info->points_cost = points_cost;
|
||||
|
||||
info->model = model;
|
||||
info->aspect = aspect;
|
||||
info->is_instant = is_instant;
|
||||
info->cast_time = cast_time * 100;
|
||||
info->recast_time = recast_time * 100;
|
||||
|
||||
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 ) );
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -249,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
|
||||
|
|
137
src/servers/Server_Zone/Action/ActionCollision.cpp
Normal file
137
src/servers/Server_Zone/Action/ActionCollision.cpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include <src/servers/Server_Common/Util/Util.h>
|
||||
#include <src/servers/Server_Common/Exd/ExdData.h>
|
||||
#include <src/servers/Server_Common/Util/UtilMath.h>
|
||||
|
||||
#include "ActionCollision.h"
|
||||
#include <src/servers/Server_Zone/Actor/Actor.h>
|
||||
#include <src/servers/Server_Zone/Actor/Player.h>
|
||||
#include <cmath>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
using namespace Core::Entity;
|
||||
using namespace Core::Common;
|
||||
|
||||
// todo: add AoE actor limits (16, 32)
|
||||
|
||||
bool ActionCollision::isActorApplicable( ActorPtr actorPtr, TargetFilter targetFilter )
|
||||
{
|
||||
bool actorApplicable = false;
|
||||
switch ( targetFilter )
|
||||
{
|
||||
case TargetFilter::All:
|
||||
{
|
||||
actorApplicable = true;
|
||||
break;
|
||||
}
|
||||
case TargetFilter::Players:
|
||||
{
|
||||
actorApplicable = actorPtr->isPlayer();
|
||||
break;
|
||||
}
|
||||
case TargetFilter::Allies:
|
||||
{
|
||||
// todo: implement ally NPCs
|
||||
actorApplicable = !actorPtr->isMob();
|
||||
break;
|
||||
}
|
||||
case TargetFilter::Party:
|
||||
{
|
||||
// todo: implement party
|
||||
actorApplicable = actorPtr->isPlayer();
|
||||
break;
|
||||
}
|
||||
case TargetFilter::Enemies:
|
||||
{
|
||||
actorApplicable = actorPtr->isMob();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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, TargetFilter targetFilter )
|
||||
{
|
||||
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.
|
||||
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 ( !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 ) )
|
||||
{
|
||||
// Add it to the actors collided with the area
|
||||
actorsCollided.insert( pActor );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ActionCollisionType::Circle:
|
||||
{
|
||||
for ( auto pActor : actorsInRange )
|
||||
{
|
||||
assert( pActor );
|
||||
|
||||
if ( !isActorApplicable( pActor, targetFilter ) )
|
||||
continue;
|
||||
|
||||
if ( radiusCollision( pActor->getPos(), aoePosition, actionInfo->aoe_range ) )
|
||||
{
|
||||
actorsCollided.insert( pActor );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ActionCollisionType::Box:
|
||||
{
|
||||
for ( auto pActor : actorsInRange )
|
||||
{
|
||||
assert( pActor );
|
||||
|
||||
if ( !isActorApplicable( pActor, targetFilter ) )
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
37
src/servers/Server_Zone/Action/ActionCollision.h
Normal file
37
src/servers/Server_Zone/Action/ActionCollision.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#ifndef _ACTIONCOLLISION_H
|
||||
#define _ACTIONCOLLISION_H
|
||||
|
||||
#include <src/servers/Server_Common/Common.h>
|
||||
|
||||
#include <src/servers/Server_Zone/Actor/Actor.h>
|
||||
#include "Action.h"
|
||||
|
||||
namespace Core {
|
||||
namespace Entity {
|
||||
|
||||
enum class TargetFilter
|
||||
{
|
||||
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 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 );
|
||||
static bool boxCollision( Common::FFXIVARR_POSITION3 actorPosition, Common::FFXIVARR_POSITION3 aoePosition, uint16_t width, uint16_t height );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,6 +1,7 @@
|
|||
#include <src/servers/Server_Common/Util/Util.h>
|
||||
#include <src/servers/Server_Common/Util/UtilMath.h>
|
||||
#include <src/servers/Server_Common/Network/PacketContainer.h>
|
||||
#include <src/servers/Server_Common/Exd/ExdData.h>
|
||||
|
||||
#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;
|
||||
|
@ -645,6 +649,135 @@ 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 );
|
||||
|
||||
// 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().actionTextId = actionId;
|
||||
effectPacket.data().numEffects = 1;
|
||||
effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() );
|
||||
effectPacket.data().effectTarget = pTarget.getId();
|
||||
|
||||
// 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 = param1;
|
||||
effectPacket.data().effects[0].effectType = ActionEffectType::Damage;
|
||||
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage;
|
||||
effectPacket.data().effects[0].unknown_3 = 7;
|
||||
|
||||
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 )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if ( !actionInfoPtr->is_aoe )
|
||||
{
|
||||
if ( isPlayer() && !ActionCollision::isActorApplicable( pTarget.shared_from_this(), TargetFilter::Allies ) )
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! \param StatusEffectPtr to be applied to the actor */
|
||||
void Core::Entity::Actor::addStatusEffect( StatusEffect::StatusEffectPtr pEffect )
|
||||
{
|
||||
|
|
|
@ -237,6 +237,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 ) {}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Player.h"
|
||||
#include <cmath>
|
||||
|
||||
using namespace Core::Data;
|
||||
using namespace Core::Entity;
|
||||
|
||||
extern Core::Data::ExdData g_exdData;
|
||||
|
@ -44,9 +45,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 +81,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 ) ) );
|
||||
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
|
||||
#include "Actor.h"
|
||||
|
||||
using namespace Core::Entity;
|
||||
|
||||
namespace Core {
|
||||
namespace Entity {
|
||||
namespace Data {
|
||||
|
||||
class CalcBattle
|
||||
{
|
||||
|
|
|
@ -217,7 +217,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 );
|
||||
|
@ -233,9 +233,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;
|
||||
|
@ -1468,114 +1468,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 );
|
||||
|
||||
|
||||
switch( type )
|
||||
{
|
||||
|
||||
case Core::Common::HandleSkillType::StdDamage:
|
||||
{
|
||||
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 );
|
||||
|
||||
if ( !pTarget.isAlive() )
|
||||
break;
|
||||
|
||||
pTarget.takeDamage( static_cast< uint32_t >( param1 ) );
|
||||
pTarget.onActionHostile( shared_from_this() );
|
||||
break;
|
||||
}
|
||||
|
||||
case Core::Common::HandleSkillType::StdHeal:
|
||||
{
|
||||
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].effectType = ActionEffectType::Heal;
|
||||
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal;
|
||||
effectPacket.data().effects[0].unknown_3 = 7;
|
||||
|
||||
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
|
||||
|
||||
if ( actionInfoPtr->is_aoe )
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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 );
|
||||
sendDebug( "AoE hit actor " + pCurAct->getName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pTarget.heal( calculatedHeal );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////
|
||||
// Content Finder
|
||||
|
|
|
@ -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 */
|
||||
|
@ -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. */
|
||||
|
|
|
@ -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<QuestActive> 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 ) )
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -219,11 +219,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.emplace_back( std::thread( std::bind( &Network::Hive::Run, hive.get() ) ) );
|
||||
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue