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

Merged master to SQL_REWRITE_OWN

This commit is contained in:
Mordred 2017-10-03 23:03:58 +02:00
commit 8d6bc11dfd
41 changed files with 1420 additions and 403 deletions

View file

@ -37,7 +37,8 @@ include( "cmake/compiler.cmake" )
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
git_describe(VERSION --tags --dirty=-d)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/servers/Server_Common/Version.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/servers/Server_Common/Version.cpp" @ONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/servers/Server_Common/Version.cpp.in"
"${CMAKE_CURRENT_SOURCE_DIR}/src/servers/Server_Common/Version.cpp" @ONLY)
##########################################################################
# Common include folders
@ -56,5 +57,10 @@ link_directories(${SERVER_COMMON_DIR})
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/libraries/sapphire/datReader)
add_subdirectory("src/servers")
add_subdirectory("src/libraries/sapphire/datReader")
add_subdirectory("src/libraries/sapphire/mysqlConnector")
add_subdirectory("src/tools/exd_common_gen")
add_subdirectory("src/tools/quest_parser")

View file

@ -45,7 +45,7 @@
</script>
</head>
<body>
<body scroll="no">
<div id="TopDiv"></div>
<div class="container"><img src="assets/img/sapphire_logo.png" width="40%" height="40%"></div>
<div class="container" id="Conttwo">

View file

@ -45,7 +45,7 @@
</script>
</head>
<body>
<body scroll="no">
<div id="TopDiv"></div>
<div class="container"><img src="assets/img/sapphire_logo.png" width="40%" height="40%"></div>
<div class="container" id="Conttwo">

View file

@ -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

View file

@ -34,7 +34,7 @@ class OpeningGridaniaDef
def Scene00000( player )
{
player.eventPlay( this.id, 0, 0x2001, 0, 1,
player.eventPlay( this.id, 0, 0x04AC05, 0, 1,
fun( player, eventId, param1, param2, param3 )
{
player.setOpeningSequence( 1 );

View file

@ -72,7 +72,6 @@ class OpeningLimsaLominsaDef
player.eventPlay( this.id, 40, 1, 2, 1,
fun( player, eventId, param1, param2, param3 )
{
player.eventFinish( eventId, UNLOCK );
if( player.getOpeningSequence() == 2 )
{
// update the instance boundaries

View file

@ -35,7 +35,7 @@ class OpeningUldahDef
def Scene00000( player )
{
player.eventPlay( this.id, 0, 0x2001, 0, 1,
player.eventPlay( this.id, 0, 0x04AC05, 0, 1,
fun( player, eventId, param1, param2, param3 )
{
player.setOpeningSequence( 1 );

View file

@ -9,11 +9,11 @@
// Start NPC: 1001140
// End NPC: 1000100
class ManFst003Def
class ManFst003Def
{
def ManFst003Def()
{
// Basic quest information
// Basic quest information
this.name = "Close to Home";
this.id = 65659;
@ -24,13 +24,13 @@ class ManFst003Def
// GetQuestUI8BL
// GetQuestUI8CH
// Steps in this quest ( 0 is before accepting,
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
this.SEQ_0 = 0;
this.SEQ_1 = 1;
this.SEQ_FINISH = 255;
// Quest rewards
// Quest rewards
this.RewardExpFactor = 100;
this.RewardGil = 107;
@ -74,7 +74,6 @@ class ManFst003Def
def checkQuestCompletion( player, varIdx )
{
print( varIdx );
if (varIdx == 3)
{
player.questMessage(this.id, 1, 0, 0, 0 );
@ -118,8 +117,8 @@ class ManFst003Def
player.eventPlay( this.id, 1, 0x0EFB/*flags*/, 0/*unk*/, 0/*unk*/,
fun( player, eventId, param1, param2, param3 )
{
player.setQuestUI8AL( ManFst004.id, 1 );
ManFst003.checkQuestCompletion( player, 1 );
player.setQuestUI8AL( ManFst003.id, 1 );
ManFst003.checkQuestCompletion( player, 0 );
});
}
@ -128,7 +127,7 @@ class ManFst003Def
player.eventPlay( this.id, 2, 0, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
player.setQuestUI8BH( ManFst004.id, 1 );
player.setQuestUI8BH( ManFst003.id, 1 );
ManFst003.checkQuestCompletion( player, 3 );
});
}
@ -207,8 +206,8 @@ class ManFst003Def
player.eventPlay( this.id, 100, 0x0EFB, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
player.setQuestUI8CH( ManFst004.id, 0 ); // remove key item, since we have just traded it
player.setQuestUI8BL( ManFst004.id, 1 );
player.setQuestUI8CH( ManFst003.id, 0 ); // remove key item, since we have just traded it
player.setQuestUI8BL( ManFst003.id, 1 );
ManFst003.checkQuestCompletion(player, 2 );
});
}
@ -237,7 +236,6 @@ class ManFst003Def
},
fun( player, eventId, additional ) {},
eventId );
player.unlock();
}
else if( actor == this.ACTOR2 )
{
@ -257,4 +255,3 @@ class ManFst003Def
};
GLOBAL ManFst003 = ManFst003Def();

View file

@ -51,11 +51,12 @@ class ManSea001Def
// Available Scenes in this quest, not necessarly all are used
def Scene00000( player )
{
player.eventPlay( this.id, 0, HIDE_HOTBAR, 0/*unk*/, 0/*unk*/,
player.eventPlay( this.id, 0, 0x2000, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
if( param2 == 1 )
{
player.setOpeningSequence( 2 );
ManSea001.Scene00001( player );
}
});
@ -63,7 +64,7 @@ class ManSea001Def
def Scene00001( player )
{
player.eventPlay( this.id, 1, HIDE_HOTBAR, 0/*unk*/, 0/*unk*/,
player.eventPlay( this.id, 1, 0xF8482EFB, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
ManSea001.Scene00002( player );
@ -81,13 +82,13 @@ class ManSea001Def
def Scene00003( player )
{
player.eventPlay( this.id, 3, NONE, 0/*unk*/, 0/*unk*/,
player.eventPlay( this.id, 3, NONE, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
player.questUpdate( ManSea001.id, 0x01 ); // add quest to player.
// update the instance boundaries, call to the opening event
//player.eventPlay( ManSea001.OPENING_EVENT_HANDLER, 0x1E, HIDE_HOTBAR, 1, 0);
player.eventPlay( ManSea001.OPENING_EVENT_HANDLER, 0x1E, 0x2001, 1, 0);
});
}
@ -107,7 +108,7 @@ class ManSea001Def
def Scene00006( player )
{
player.eventPlay( this.id, 6, NONE, 0/*unk*/, 0/*unk*/,
player.eventPlay( this.id, 6, 0x20, 0/*unk*/, 0/*unk*/,
fun( player, eventId, param1, param2, param3 )
{
if( param2 == 1 )
@ -141,7 +142,7 @@ class ManSea001Def
def Scene00011( player )
{
player.eventPlay( this.id, 11, NONE, 0, 0,
player.eventPlay( this.id, 11, 0x2c02, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
ManSea001.Scene00012( player );
@ -150,7 +151,7 @@ class ManSea001Def
def Scene00012( player )
{
player.eventPlay( this.id, 12, NONE, 0, 0,
player.eventPlay( this.id, 12, 0x20, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
if( param2 == 1 )

View file

@ -9,25 +9,25 @@
// Start NPC: 1003987
// End NPC: 1003988
class ManWil001Def
class ManWil001Def
{
//////////////////////////////////////////////////////////////////////
// default ctor
def ManWil001Def()
{
// Basic quest information
// Basic quest information
this.name = "Coming to Ul'dah";
this.id = 66130;
// Quest vars / flags used
// GetQuestUI8AL
// Steps in this quest ( 0 is before accepting,
// Steps in this quest ( 0 is before accepting,
// 1 is first, 255 means ready for turning it in
this.SEQ_0 = 0;
this.SEQ_FINISH = 255;
// Quest rewards
// Quest rewards
this.RewardExpFactor = 50;
this.RewardGil = 103;
@ -49,11 +49,12 @@ class ManWil001Def
// Available Scenes in this quest, not necessarly all are used
def Scene00000( player )
{
player.eventPlay( this.id, 0, 0, 0, 0,
player.eventPlay( this.id, 0, 0x2000, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
if( param2 == 1 ) // accept quest
{
player.setOpeningSequence( 2 );
ManWil001.Scene00001( player );
}
});
@ -61,7 +62,7 @@ class ManWil001Def
def Scene00001( player )
{
player.eventPlay( this.id, 1, 0, 0, 0,
player.eventPlay( this.id, 1, 0xF8482EFB, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
ManWil001.Scene00002( player );
@ -70,11 +71,11 @@ class ManWil001Def
def Scene00002( player )
{
player.eventPlay( this.id, 2, 0, 0, 0,
player.eventPlay( this.id, 2, NONE, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
player.questUpdate( ManWil001.id, ManWil001Obj.SEQ_FINISH );// add quest to player.
player.eventPlay( ManWil001.OPENING_EVENT_HANDLER, 0x1E, 0x2001, 0, 0 );
player.questUpdate( ManWil001.id, ManWil001.SEQ_FINISH );// add quest to player.
player.eventPlay( ManWil001.OPENING_EVENT_HANDLER, 0x1E, 0x2001, 0, 0 );
});
}
@ -85,7 +86,7 @@ class ManWil001Def
def Scene00004( player )
{
player.eventPlay( this.id, 4, 0, 0, 0,
player.eventPlay( this.id, 4, 0x2c02, 0, 0,
fun( player, eventId, param1, param2, param3 )
{
ManWil001.Scene00005( player );
@ -94,7 +95,7 @@ class ManWil001Def
def Scene00005( player )
{
player.eventPlay( this.id, 5, 0/*flags*/, 0/*unk*/, 0/*unk*/,
player.eventPlay( this.id, 5, 0x20/*flags*/, 0/*unk*/, 0/*unk*/,
fun( player, eventId, param1, param2, param3 )
{
if( param2 == 1 ) // clicked finish button

View 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();

View file

@ -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,
@ -548,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,
@ -581,7 +604,7 @@ namespace Core {
GpGain = 14
};
enum ActionHitSeverityType : uint8_t
enum class ActionHitSeverityType : uint8_t
{
NormalDamage = 0,
CritHeal = 0,
@ -591,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,
@ -605,8 +641,16 @@ namespace Core {
StdDot,
};
enum InvincibilityType : uint8_t
{
InvincibilityNone,
InvincibilityRefill,
InvincibilityStayAlive,
};
enum struct PlayerStateFlag : uint8_t
{
SomeFlag,
NoCombat,
Combat,
Casting,
@ -614,9 +658,9 @@ namespace Core {
StatusAffliction1,
Occupied,
Occupied1,
Occupied2,
Occupied3,
BoundByDuty,
Occupied4,
DuelingArea,
@ -627,6 +671,7 @@ namespace Core {
PreparingToCraft,
Gathering,
Fishing,
BeingRaised,
BetweenAreas,
Stealthed,
@ -637,9 +682,9 @@ namespace Core {
BetweenAreas1,
SystemError,
LoggingOut,
InvalidLocation,
WaitingForDuty,
BoundByDuty1,
Mounting,
WatchingCutscene,
@ -657,9 +702,9 @@ namespace Core {
FreeTrail,
BeingMoved,
Mounting2,
StatusAffliction3,
StatusAffliction4,
RegisteringRaceOrMatch,
WaitingForRaceOrMatch,
WaitingForTripleTriadMatch,

View file

@ -323,70 +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
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
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
uint8_t typeshift = 0x6;
uint8_t mask = 1 << typeshift;
instantval &= mask;
bool final = ( instantval & mask ) == mask;
bool is_instant = final;
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;
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->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 ) );

View file

@ -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

View file

@ -50,7 +50,7 @@ void Core::Action::ActionCast::onStart()
m_pSource->getAsPlayer()->sendDebug( "onStart()" );
m_startTime = Util::getTimeMs();
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( getId() );
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( m_pSource->getId() );
castPacket.data().action_id = m_id;
castPacket.data().unknown = 1;

View 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;
}

View 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

View file

@ -45,7 +45,7 @@ void Core::Action::ActionTeleport::onStart()
m_startTime = Util::getTimeMs();
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( getId() );
GamePacketNew< FFXIVIpcActorCast, ServerZoneIpcType > castPacket( m_pSource->getId() );
castPacket.data().action_id = 5;
castPacket.data().unknown = 1;

View file

@ -53,8 +53,7 @@ void Core::Action::EventAction::onStart()
if( m_pSource->isPlayer() )
{
m_pSource->sendToInRangeSet( control, true );
m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::NoCombat );
m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Occupied1 );
m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::SomeFlag );
m_pSource->getAsPlayer()->sendStateFlags();
}
else
@ -85,8 +84,7 @@ void Core::Action::EventAction::onFinish()
if( m_pSource->isPlayer() )
{
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::NoCombat );
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::Occupied1 );
m_pSource->getAsPlayer()->unsetStateFlag( PlayerStateFlag::SomeFlag );
m_pSource->getAsPlayer()->sendStateFlags();
m_pSource->sendToInRangeSet( control, true );
}

View file

@ -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;
@ -53,13 +57,13 @@ std::string Core::Entity::Actor::getName() const
/*! \return true if the actor is of type player */
bool Core::Entity::Actor::isPlayer() const
{
return ( m_type == ActorType::Player ? true : false );
return m_type == ActorType::Player;
}
/*! \return true if the actor is of type mob */
bool Core::Entity::Actor::isMob() const
{
return ( m_type == ActorType::BattleNpc ? true : false );
return m_type == ActorType::BattleNpc;
}
/*! \return list of actors currently in range */
@ -109,6 +113,12 @@ uint16_t Core::Entity::Actor::getGp() const
return m_gp;
}
/*! \return current invincibility type */
InvincibilityType Core::Entity::Actor::getInvincibilityType() const
{
return m_invincibilityType;
}
/*! \return current class or job */
Core::Common::ClassJob Core::Entity::Actor::getClass() const
{
@ -161,32 +171,44 @@ uint32_t Core::Entity::Actor::getMaxMp() const
void Core::Entity::Actor::resetHp()
{
m_hp = getMaxHp();
sendStatusUpdate( true );
}
/*! \return reset mp to current max mp */
void Core::Entity::Actor::resetMp()
{
m_mp = getMaxMp();
sendStatusUpdate( true );
}
/*! \param hp amount to set ( caps to maxHp ) */
void Core::Entity::Actor::setHp( uint32_t hp )
{
m_hp = hp < getMaxHp() ? hp : getMaxHp();
sendStatusUpdate( true );
}
/*! \param mp amount to set ( caps to maxMp ) */
void Core::Entity::Actor::setMp( uint32_t mp )
{
m_mp = mp < getMaxMp() ? mp : getMaxMp();
sendStatusUpdate( true );
}
/*! \param mp amount to set ( caps to maxMp ) */
/*! \param gp amount to set*/
void Core::Entity::Actor::setGp( uint32_t gp )
{
m_gp = gp;
sendStatusUpdate( true );
}
/*! \param type invincibility type to set */
void Core::Entity::Actor::setInvincibilityType( Common::InvincibilityType type )
{
m_invincibilityType = type;
}
/*! \return current status of the actor */
Core::Entity::Actor::ActorStatus Core::Entity::Actor::getStatus() const
{
@ -240,10 +262,7 @@ bool Core::Entity::Actor::face( const Common::FFXIVARR_POSITION3& p )
setRotation( newRot );
if( oldRot != newRot )
return true;
return false;
return oldRot != newRot ? true : false;
}
/*!
@ -331,8 +350,18 @@ void Core::Entity::Actor::takeDamage( uint32_t damage )
{
if( damage >= m_hp )
{
m_hp = 0;
die();
switch( m_invincibilityType ) {
case InvincibilityNone:
setHp( 0 );
die();
break;
case InvincibilityRefill:
resetHp();
break;
case InvincibilityStayAlive:
setHp( 0 );
break;
}
}
else
m_hp -= damage;
@ -620,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 )
{
@ -629,7 +787,8 @@ void Core::Entity::Actor::addStatusEffect( StatusEffect::StatusEffectPtr pEffect
/*! \param StatusEffectPtr to be applied to the actor */
void Core::Entity::Actor::addStatusEffectById( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param )
{
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pSource.shared_from_this(), shared_from_this(), duration, 3000 ) );
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pSource.shared_from_this(),
shared_from_this(), duration, 3000 ) );
effect->setParam( param );
addStatusEffect( effect );
}
@ -639,7 +798,8 @@ void Core::Entity::Actor::addStatusEffectByIdIfNotExist( uint32_t id, int32_t du
{
if( !m_pStatusEffectContainer->hasStatusEffect( id ) )
{
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pSource.shared_from_this(), shared_from_this(), duration, 3000 ) );
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pSource.shared_from_this(),
shared_from_this(), duration, 3000 ) );
effect->setParam( param );
addStatusEffect( effect );
}

View file

@ -150,6 +150,8 @@ protected:
Action::ActionPtr m_pCurrentAction;
/*! Container for status effects */
StatusEffect::StatusEffectContainerPtr m_pStatusEffectContainer;
/*! Invincibility type */
Common::InvincibilityType m_invincibilityType;
public:
Actor();
@ -199,6 +201,8 @@ public:
uint16_t getGp() const;
Common::InvincibilityType getInvincibilityType() const;
Common::ClassJob getClass() const;
uint8_t getClassAsInt() const;
@ -225,12 +229,16 @@ public:
void setGp( uint32_t gp );
void setInvincibilityType( Common::InvincibilityType type );
void die();
ActorStatus getStatus() const;
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 ) {}

View file

@ -54,6 +54,7 @@ Core::Entity::BattleNpc::BattleNpc( uint32_t modelId, uint32_t nameid, const Com
m_type = ActorType::BattleNpc;
m_mode = MODE_IDLE;
m_targetId = INVALID_GAME_OBJECT_ID;
m_maxHp = 150;
m_maxMp = 100;
@ -82,6 +83,8 @@ Core::Entity::BattleNpc::BattleNpc( uint32_t modelId, uint32_t nameid, const Com
m_mobType = mobType;
m_invincibilityType = InvincibilityType::InvincibilityNone;
//m_type = static_cast< Common::ActorType >( type );
}
@ -229,7 +232,7 @@ void Core::Entity::BattleNpc::setOwner( Core::Entity::PlayerPtr pPlayer )
}
else
{
GamePacketNew< FFXIVIpcActorOwner, ServerZoneIpcType > setOwnerPacket(getId(), INVALID_GAME_OBJECT_ID);
GamePacketNew< FFXIVIpcActorOwner, ServerZoneIpcType > setOwnerPacket(getId(), INVALID_GAME_OBJECT_ID );
setOwnerPacket.data().type = 0x01;
setOwnerPacket.data().actorId = INVALID_GAME_OBJECT_ID;
sendToInRangeSet( setOwnerPacket );
@ -251,15 +254,15 @@ bool Core::Entity::BattleNpc::moveTo( Common::FFXIVARR_POSITION3& pos )
// reached destination
return true;
float rot = Math::Util::calcAngFrom(getPos().x, getPos().z, pos.x, pos.z);
float rot = Math::Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z );
float newRot = PI - rot + (PI / 2);
face( pos );
float angle = Math::Util::calcAngFrom( getPos().x, getPos().z, pos.x, pos.z ) + PI;
float x = static_cast< float >( cosf(angle) * 1.1f );
float x = static_cast< float >( cosf( angle ) * 1.1f );
float y = ( getPos().y + pos.y ) * 0.5f; // fake value while there is no collision
float z = static_cast< float >( sinf(angle) * 1.1f );
float z = static_cast< float >( sinf( angle ) * 1.1f );
Common::FFXIVARR_POSITION3 newPos;

View file

@ -104,7 +104,7 @@ private:
uint32_t m_unk2;
std::set< HateListEntry* > m_hateList;
ActorPtr m_pOwner;
int32_t m_timeOfDeath;
uint32_t m_timeOfDeath;
uint32_t m_mobType;
};

View file

@ -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 ) ) );
@ -113,7 +111,6 @@ uint32_t CalcBattle::calculateMaxMp( PlayerPtr pPlayer )
return result;
}
uint32_t CalcBattle::calculateHealValue( PlayerPtr pPlayer, uint32_t potency )
{
auto classInfoIt = g_exdData.m_classJobInfoMap.find( pPlayer->getClass() );

View file

@ -5,8 +5,10 @@
#include "Actor.h"
using namespace Core::Entity;
namespace Core {
namespace Entity {
namespace Data {
class CalcBattle
{

View file

@ -82,6 +82,7 @@ Core::Entity::Player::Player() :
m_onlineStatus = 0;
m_queuedZoneing = nullptr;
m_status = ActorStatus::Idle;
m_invincibilityType = InvincibilityType::InvincibilityNone;
memset( m_questTracking, 0, sizeof( m_questTracking ) );
memset( m_name, 0, sizeof( m_name ) );
@ -192,7 +193,7 @@ void Core::Entity::Player::prepareZoning( uint16_t targetZone, bool fadeOut, uin
preparePacket.data().targetZone = targetZone;
preparePacket.data().fadeOutTime = fadeOutTime;
preparePacket.data().animation = animation;
preparePacket.data().fadeOut = fadeOut == true ? 1 : 0;
preparePacket.data().fadeOut = static_cast< uint8_t >( fadeOut ? 1 : 0 );
queuePacket( preparePacket );
}
@ -216,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 );
@ -232,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;
@ -398,7 +399,7 @@ void Core::Entity::Player::setZone( uint32_t zoneId )
if( isLogin() )
{
GamePacketNew< FFXIVIpcCFAvailableContents, ServerZoneIpcType > contentFinderList( getId() );
for( auto i = 0; i < 72; i++ )
for( auto i = 0; i < sizeof( contentFinderList.data().contents ); i++ )
{
// unlock all contents for now
contentFinderList.data().contents[i] = 0xFF;
@ -632,7 +633,9 @@ void Core::Entity::Player::gainExp( uint32_t amount )
if( ( currentExp + amount ) >= neededExpToLevel )
{
// levelup
amount = ( currentExp + amount - neededExpToLevel ) > neededExpToLevelplus1 ? neededExpToLevelplus1 - 1 : ( currentExp + amount - neededExpToLevel );
amount = ( currentExp + amount - neededExpToLevel ) > neededExpToLevelplus1 ?
neededExpToLevelplus1 - 1 :
( currentExp + amount - neededExpToLevel );
setExp( amount );
gainLevel();
queuePacket( ActorControlPacket143( getId(), UpdateUiExp, static_cast< uint8_t >( getClass() ), amount ) );
@ -791,50 +794,6 @@ void Core::Entity::Player::setLevelForClass( uint8_t level, Core::Common::ClassJ
setSyncFlag( PlayerSyncFlags::ExpLevel );
}
void Core::Entity::Player::eventActionStart( uint32_t eventId,
uint32_t action,
ActionCallback finishCallback,
ActionCallback interruptCallback,
uint64_t additional )
{
Action::ActionPtr pEventAction( new Action::EventAction( shared_from_this(), eventId, action,
finishCallback, interruptCallback, additional ) );
setCurrentAction( pEventAction );
auto pEvent = getEvent( eventId );
if( !pEvent && getEventCount() )
{
// We're trying to play a nested event, need to start it first.
eventStart( getId(), eventId, Event::Event::Nest, 0, 0 );
pEvent = getEvent( eventId );
}
else if( !pEvent )
{
g_log.error( "Could not find event " + std::to_string( eventId ) + ", event has not been started!" );
return;
}
if( pEvent )
pEvent->setPlayedScene( true );
pEventAction->onStart();
}
void Core::Entity::Player::eventItemActionStart( uint32_t eventId,
uint32_t action,
ActionCallback finishCallback,
ActionCallback interruptCallback,
uint64_t additional )
{
Action::ActionPtr pEventItemAction( new Action::EventItemAction( shared_from_this(), eventId, action,
finishCallback, interruptCallback, additional ) );
setCurrentAction( pEventItemAction );
pEventItemAction->onStart();
}
void Core::Entity::Player::sendModel()
{
ModelEquipPacket modelEquip( getAsPlayer() );
@ -846,6 +805,12 @@ uint32_t Core::Entity::Player::getModelForSlot( Inventory::EquipSlot slot )
return m_modelEquip[slot];
}
void Core::Entity::Player::setModelForSlot( Inventory::EquipSlot slot, uint32_t val )
{
m_modelEquip[slot] = val;
setSyncFlag( PlayerSyncFlags::Status );
}
uint64_t Core::Entity::Player::getModelMainWeapon() const
{
return m_modelMainWeapon;
@ -973,7 +938,7 @@ void Core::Entity::Player::setGcRankAt( uint8_t index, uint8_t rank )
setSyncFlag( PlayerSyncFlags::GC );
}
const uint8_t * Core::Entity::Player::getStateFlags() const
const uint8_t* Core::Entity::Player::getStateFlags() const
{
return m_stateFlags;
}
@ -1086,21 +1051,16 @@ void Core::Entity::Player::update( int64_t currTime )
m_lastUpdate = currTime;
// @TODO needs to happen in a if check. Don't want autoattacking while an action is being performed.
if( !checkAction() )
{
if( m_targetId )
if( m_targetId && m_currentStance == Entity::Actor::Stance::Active && isAutoattackOn() )
{
auto mainWeap = m_pInventory->getItemAt( Inventory::GearSet0, Inventory::EquipSlot::MainHand );
// @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need...
for( auto actor : m_inRangeActors )
{
if( isAutoattackOn() &&
actor->getId() == m_targetId &&
actor->isAlive() &&
mainWeap &&
m_currentStance == Entity::Actor::Stance::Active
)
if( actor->getId() == m_targetId && actor->isAlive() && mainWeap )
{
// default autoattack range
// TODO make this dependant on bnpc size
@ -1514,114 +1474,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
@ -1644,9 +1496,7 @@ uint32_t Core::Entity::Player::getCFPenaltyMinutes() const
// check if penalty timestamp already passed current time
if (currentTimestamp > endTimestamp)
{
return 0;
}
auto deltaTime = endTimestamp - currentTimestamp;
return static_cast< uint32_t > ( ceil( static_cast< float > (deltaTime) / 60 ) );

View file

@ -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 */
@ -218,6 +218,8 @@ public:
const uint32_t * getModelArray() const;
/*! return the equipment model in a specified equipment slot */
uint32_t getModelForSlot( Inventory::EquipSlot slot );
/*! set the equipment model in a specified equipment slot */
void setModelForSlot( Inventory::EquipSlot slot, uint32_t val );
/*! return the current amount of currency of type */
uint32_t getCurrency( uint8_t type ) const;
/*! add amount to the currency of type */
@ -487,8 +489,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. */

View file

@ -18,6 +18,10 @@
#include "src/servers/Server_Zone/Network/PacketWrappers/EventPlayPacket.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/EventFinishPacket.h"
#include "src/servers/Server_Zone/Action/EventAction.h"
#include "src/servers/Server_Zone/Action/EventItemAction.h"
#include "src/servers/Server_Zone/Event/Event.h"
#include "src/servers/Server_Zone/Event/Event.h"
#include "Server_Zone/ServerZone.h"
@ -230,6 +234,50 @@ void Core::Entity::Player::eventFinish( uint32_t eventId, uint32_t freePlayer )
}
}
void Core::Entity::Player::eventActionStart( uint32_t eventId,
uint32_t action,
ActionCallback finishCallback,
ActionCallback interruptCallback,
uint64_t additional )
{
Action::ActionPtr pEventAction( new Action::EventAction( shared_from_this(), eventId, action,
finishCallback, interruptCallback, additional ) );
setCurrentAction( pEventAction );
auto pEvent = getEvent( eventId );
if( !pEvent && getEventCount() )
{
// We're trying to play a nested event, need to start it first.
eventStart( getId(), eventId, Event::Event::Nest, 0, 0 );
pEvent = getEvent( eventId );
}
else if( !pEvent )
{
g_log.error( "Could not find event " + std::to_string( eventId ) + ", event has not been started!" );
return;
}
if( pEvent )
pEvent->setPlayedScene( true );
pEventAction->onStart();
}
void Core::Entity::Player::eventItemActionStart( uint32_t eventId,
uint32_t action,
ActionCallback finishCallback,
ActionCallback interruptCallback,
uint64_t additional )
{
Action::ActionPtr pEventItemAction( new Action::EventItemAction( shared_from_this(), eventId, action,
finishCallback, interruptCallback, additional ) );
setCurrentAction( pEventItemAction );
pEventItemAction->onStart();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Core::Entity::Player::onLogin()

View file

@ -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 ) )
{

View file

@ -239,25 +239,6 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
else
pPlayer->setClassJob( static_cast<Core::Common::ClassJob> ( 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;
@ -273,6 +254,20 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
pPlayer->setEorzeaTimeOffset( timestamp );
pPlayer->sendNotice( "Eorzea time offset: " + std::to_string( timestamp ) );
}
else if ( subCommand == "model" )
{
uint32_t slot;
uint32_t val;
sscanf( params.c_str(), "%d %d", &slot, &val );
pPlayer->setModelForSlot( static_cast<Inventory::EquipSlot>( slot ), val );
pPlayer->sendModel();
pPlayer->sendDebug( "Model updated" );
}
else
{
pPlayer->sendUrgent( subCommand + " is not a valid SET command." );
}
}
@ -378,6 +373,10 @@ void Core::DebugCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlaye
pPlayer->queuePacket(controlPacket);*/
}
else
{
pPlayer->sendUrgent( subCommand + " is not a valid ADD command." );
}
}
@ -419,6 +418,10 @@ void Core::DebugCommandHandler::get( char * data, Core::Entity::PlayerPtr pPlaye
std::to_string( map_id ) + "\nZoneID: " +
std::to_string( pPlayer->getCurrentZone()->getId() ) + "\n" );
}
else
{
pPlayer->sendUrgent( subCommand + " is not a valid GET command." );
}
}
@ -486,6 +489,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() ) );
}

View file

@ -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 );

View file

@ -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();

View file

@ -70,6 +70,7 @@ enum GmCommand
Tp = 0x0066,
Gp = 0x0067,
Exp = 0x0068,
Inv = 0x006A,
Item = 0x00C8,
Gil = 0x00C9,
@ -352,6 +353,17 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac
" was set to " + std::to_string( targetPlayer->getGcRankArray()[targetPlayer->getGc() - 1] ) );
break;
}
case GmCommand::Inv:
{
if( targetActor->getInvincibilityType() == Common::InvincibilityType::InvincibilityRefill )
targetActor->setInvincibilityType( Common::InvincibilityType::InvincibilityNone );
else
targetActor->setInvincibilityType( Common::InvincibilityType::InvincibilityRefill );
pPlayer->sendNotice( "Invincibility for " + targetPlayer->getName() +
" was switched." );
break;
}
default:
pPlayer->sendUrgent( "GM1 Command not implemented: " + std::to_string( commandId ) );

View file

@ -54,7 +54,8 @@ Core::LinkshellMgr g_linkshellMgr;
Core::Db::DbWorkerPool< Core::Db::CharaDbConnection > CharacterDatabase;
Core::ServerZone::ServerZone( const std::string& configPath, uint16_t serverId )
: m_configPath( configPath )
: m_configPath( configPath ),
m_bRunning( true )
{
m_pConfig = XMLConfigPtr( new XMLConfig );
}
@ -332,12 +333,8 @@ void Core::ServerZone::run( int32_t argc, char* argv[] )
g_log.setLogPath( "log\\SapphireZone_" );
g_log.init();
g_log.info( "===========================================================" );
g_log.info( "Sapphire Server Project " );
g_log.info( "Version: " + Core::Version::VERSION );
g_log.info( "GitHash: " + Core::Version::GIT_HASH );
g_log.info( "Compiled: " __DATE__ " " __TIME__ );
g_log.info( "===========================================================" );
printBanner();
if( !loadSettings( argc, argv ) )
{
@ -364,24 +361,47 @@ 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() ) ) );
thread_list.emplace_back( std::thread( std::bind( &Network::Hive::Run, hive.get() ) ) );
g_log.info( "Server listening on port: " + std::to_string( m_port ) );
g_log.info( "Ready for connections..." );
while( true )
mainLoop();
for( auto& thread_entry : thread_list )
{
std::this_thread::sleep_for( std::chrono::milliseconds( 50 ) );
thread_entry.join();
}
}
void Core::ServerZone::printBanner() const
{
g_log.info("===========================================================" );
g_log.info( "Sapphire Server Project " );
g_log.info( "Version: x.y.z" );
g_log.info( "Compiled: " __DATE__ " " __TIME__ );
g_log.info( "===========================================================" );
}
void Core::ServerZone::mainLoop()
{
while( isRunning() )
{
this_thread::sleep_for( chrono::milliseconds( 50 ) );
g_zoneMgr.updateZones();
std::lock_guard<std::mutex> lock( m_sessionMutex );
for( auto sessionIt : m_sessionMap )
auto currTime = static_cast< uint32_t >( time( nullptr ) );
lock_guard< std::mutex > lock( this->m_sessionMutex );
for( auto sessionIt : this->m_sessionMap )
{
auto session = sessionIt.second;
if( session && session->getPlayer() )
@ -393,9 +413,9 @@ void Core::ServerZone::run( int32_t argc, char* argv[] )
}
}
uint32_t currTime = static_cast< uint32_t >( time( nullptr ) );
auto it = m_sessionMap.begin();
for( ; it != m_sessionMap.end(); )
auto it = this->m_sessionMap.begin();
for( ; it != this->m_sessionMap.end(); )
{
uint32_t diff = currTime - it->second->getLastDataTime();
@ -403,11 +423,11 @@ void Core::ServerZone::run( int32_t argc, char* argv[] )
if( diff > 20 )
{
g_log.info( "[" + std::to_string( it->second->getId() ) + "] Session time out" );
g_log.info("[" + std::to_string(it->second->getId() ) + "] Session time out" );
it->second->close();
// if( it->second.unique() )
{
it = m_sessionMap.erase( it );
it = this->m_sessionMap.erase(it );
}
}
else
@ -418,13 +438,6 @@ void Core::ServerZone::run( int32_t argc, char* argv[] )
}
}
// currently never reached, need a "stopServer" variable to break out of the above while loop
/*for( auto& thread_entry : thread_list )
{
thread_entry.join();
}*/
}
bool Core::ServerZone::createSession( uint32_t sessionId )
@ -510,3 +523,8 @@ void Core::ServerZone::updateSession( std::string playerName )
it->second->loadPlayer();
}
bool Core::ServerZone::isRunning() const
{
return m_bRunning;
}

View file

@ -40,12 +40,20 @@ namespace Core {
Entity::BattleNpcTemplatePtr getBnpcTemplate( std::string templateName );
void mainLoop();
bool isRunning() const;
void printBanner() const;
private:
uint16_t m_port;
std::string m_ip;
bool m_bRunning;
std::string m_configPath;
XMLConfigPtr m_pConfig;

View file

@ -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 ) );
}
}

View file

@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 2.6)
cmake_policy(SET CMP0015 NEW)
project(Tool_ExdCommonGen)
set(SAPPHIRE_BOOST_VER 1.63.0)
set(SAPPHIRE_BOOST_FOLDER_NAME boost_1_63_0)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../bin/")
file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*")
file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*")
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/")
add_executable(exd_common_gen ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES})
set_target_properties(exd_common_gen PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS ON
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_SOURCE_DIR}/../bin/"
)
if (UNIX)
target_link_libraries (exd_common_gen Common xivdat pthread mysqlclient dl z)
else()
target_link_libraries (exd_common_gen Common xivdat libmysql zlib1)
endif()
target_link_libraries(exd_common_gen ${Boost_LIBRARIES} ${Boost_LIBRARIES})

View file

@ -0,0 +1,560 @@
[14:21:11][info] Setting up EXD data
[14:21:12][info] /* This file has been automatically generated.
Changes will be lost upon regeneration.
To change the content edit tools/exd_common_gen */
namespace Core {
namespace Common {
///////////////////////////////////////////////////////////
//ActionCategory.exd
enum ActionCategory : uint8_t
{
Autoattack = 1,
Spell = 2,
Weaponskill = 3,
Ability = 4,
Item = 5,
DoLAbility = 6,
DoHAbility = 7,
Event = 8,
LimitBreak = 9,
System = 10,
Artillery = 11,
Mount = 12,
Glamour = 13,
ItemManipulation = 14,
AdrenalineRush = 15,
};
///////////////////////////////////////////////////////////
//BeastReputationRank.exd
enum BeastReputationRank : uint8_t
{
None = 0,
Neutral = 1,
Recognized = 2,
Friendly = 3,
Trusted = 4,
Respected = 5,
Honored = 6,
Sworn = 7,
Allied = 8,
};
///////////////////////////////////////////////////////////
//BeastTribe.exd
enum BeastTribe : uint8_t
{
Amaljaa = 1,
Sylphs = 2,
Kobolds = 3,
Sahagin = 4,
Ixal = 5,
VanuVanu = 6,
Vath = 7,
Moogles = 8,
};
///////////////////////////////////////////////////////////
//ClassJob.exd
enum ClassJob : uint8_t
{
Adventurer = 0,
Gladiator = 1,
Pugilist = 2,
Marauder = 3,
Lancer = 4,
Archer = 5,
Conjurer = 6,
Thaumaturge = 7,
Carpenter = 8,
Blacksmith = 9,
Armorer = 10,
Goldsmith = 11,
Leatherworker = 12,
Weaver = 13,
Alchemist = 14,
Culinarian = 15,
Miner = 16,
Botanist = 17,
Fisher = 18,
Paladin = 19,
Monk = 20,
Warrior = 21,
Dragoon = 22,
Bard = 23,
Whitemage = 24,
Blackmage = 25,
Arcanist = 26,
Summoner = 27,
Scholar = 28,
Rogue = 29,
Ninja = 30,
Machinist = 31,
Darkknight = 32,
Astrologian = 33,
Samurai = 34,
Redmage = 35,
};
///////////////////////////////////////////////////////////
//ContentType.exd
enum ContentType : uint8_t
{
DutyRoulette = 1,
Dungeons = 2,
Guildhests = 3,
Trials = 4,
Raids = 5,
PvP = 6,
QuestBattles = 7,
FATEs = 8,
TreasureHunt = 9,
Levequests = 10,
GrandCompany = 11,
Companions = 12,
BeastTribeQuests = 13,
OverallCompletion = 14,
PlayerCommendation = 15,
DisciplesoftheLand = 16,
DisciplesoftheHand = 17,
RetainerVentures = 18,
GoldSaucer = 19,
DeepDungeons = 21,
WondrousTails = 24,
};
///////////////////////////////////////////////////////////
//EmoteCategory.exd
enum EmoteCategory : uint8_t
{
General = 1,
Persistent = 2,
Expressions = 3,
};
///////////////////////////////////////////////////////////
//ExVersion.exd
enum ExVersion : uint8_t
{
ARealmReborn = 0,
Heavensward = 1,
Stormblood = 2,
};
///////////////////////////////////////////////////////////
//GrandCompany.exd
enum GrandCompany : uint8_t
{
None = 0,
Maelstrom = 1,
OrderoftheTwinAdder = 2,
ImmortalFlames = 3,
};
///////////////////////////////////////////////////////////
//GuardianDeity.exd
enum GuardianDeity : uint8_t
{
HalonetheFury = 1,
MenphinatheLover = 2,
ThaliaktheScholar = 3,
NymeiatheSpinner = 4,
LlymlaentheNavigator = 5,
OschontheWanderer = 6,
ByregottheBuilder = 7,
RhalgrtheDestroyer = 8,
AzeymatheWarden = 9,
NaldthaltheTraders = 10,
NophicatheMatron = 11,
AlthyktheKeeper = 12,
};
///////////////////////////////////////////////////////////
//ItemUICategory.exd
enum ItemUICategory : uint8_t
{
PugilistsArm = 1,
GladiatorsArm = 2,
MaraudersArm = 3,
ArchersArm = 4,
LancersArm = 5,
OnehandedThaumaturgesArm = 6,
TwohandedThaumaturgesArm = 7,
OnehandedConjurersArm = 8,
TwohandedConjurersArm = 9,
ArcanistsGrimoire = 10,
Shield = 11,
CarpentersPrimaryTool = 12,
CarpentersSecondaryTool = 13,
BlacksmithsPrimaryTool = 14,
BlacksmithsSecondaryTool = 15,
ArmorersPrimaryTool = 16,
ArmorersSecondaryTool = 17,
GoldsmithsPrimaryTool = 18,
GoldsmithsSecondaryTool = 19,
LeatherworkersPrimaryTool = 20,
LeatherworkersSecondaryTool = 21,
WeaversPrimaryTool = 22,
WeaversSecondaryTool = 23,
AlchemistsPrimaryTool = 24,
AlchemistsSecondaryTool = 25,
CulinariansPrimaryTool = 26,
CulinariansSecondaryTool = 27,
MinersPrimaryTool = 28,
MinersSecondaryTool = 29,
BotanistsPrimaryTool = 30,
BotanistsSecondaryTool = 31,
FishersPrimaryTool = 32,
FishingTackle = 33,
Head = 34,
Body = 35,
Legs = 36,
Hands = 37,
Feet = 38,
Waist = 39,
Necklace = 40,
Earrings = 41,
Bracelets = 42,
Ring = 43,
Medicine = 44,
Ingredient = 45,
Meal = 46,
Seafood = 47,
Stone = 48,
Metal = 49,
Lumber = 50,
Cloth = 51,
Leather = 52,
Bone = 53,
Reagent = 54,
Dye = 55,
Part = 56,
Furnishing = 57,
Materia = 58,
Crystal = 59,
Catalyst = 60,
Miscellany = 61,
SoulCrystal = 62,
Other = 63,
ConstructionPermit = 64,
Roof = 65,
ExteriorWall = 66,
Window = 67,
Door = 68,
RoofDecoration = 69,
ExteriorWallDecoration = 70,
Placard = 71,
Fence = 72,
InteriorWall = 73,
Flooring = 74,
CeilingLight = 75,
OutdoorFurnishing = 76,
Table = 77,
Tabletop = 78,
Wallmounted = 79,
Rug = 80,
Minion = 81,
Gardening = 82,
Demimateria = 83,
RoguesArm = 84,
SeasonalMiscellany = 85,
TripleTriadCard = 86,
DarkKnightsArm = 87,
MachinistsArm = 88,
AstrologiansArm = 89,
AirshipHull = 90,
AirshipRigging = 91,
AirshipAftcastle = 92,
AirshipForecastle = 93,
OrchestrionRoll = 94,
Painting = 95,
SamuraisArm = 96,
RedMagesArm = 97,
ScholarsArm = 98,
FishersSecondaryTool = 99,
};
///////////////////////////////////////////////////////////
//ItemSearchCategory.exd
enum ItemSearchCategory : uint8_t
{
PrimaryArms = 1,
PrimaryTools = 2,
PrimaryTools1 = 3,
Armor = 4,
Accessories = 5,
Medicines = 6,
Materials = 7,
Other = 8,
PugilistsArms = 9,
GladiatorsArms = 10,
MaraudersArms = 11,
ArchersArms = 12,
LancersArms = 13,
ThaumaturgesArms = 14,
ConjurersArms = 15,
ArcanistsArms = 16,
Shields = 17,
ThrowingWeapons = 18,
CarpentersTools = 19,
BlacksmithsTools = 20,
ArmorersTools = 21,
GoldsmithsTools = 22,
LeatherworkersTools = 23,
WeaversTools = 24,
AlchemistsTools = 25,
CulinariansTools = 26,
MinersTools = 27,
BotanistsTools = 28,
FishersTools = 29,
FishingTackle = 30,
Head = 31,
Undershirts = 32,
Body = 33,
Undergarments = 34,
Legs = 35,
Hands = 36,
Feet = 37,
Waist = 38,
Necklaces = 39,
Earrings = 40,
Bracelets = 41,
Rings = 42,
Medicine = 43,
Ingredients = 44,
Meals = 45,
Seafood = 46,
Stone = 47,
Metal = 48,
Lumber = 49,
Cloth = 50,
Leather = 51,
Bone = 52,
Reagents = 53,
Dyes = 54,
WeaponParts = 55,
Furnishings = 56,
Materia = 57,
Crystals = 58,
Catalysts = 59,
Miscellany = 60,
SoulCrystals = 61,
Arrows = 62,
QuestItems = 63,
Other1 = 64,
ExteriorFixtures = 65,
InteriorFixtures = 66,
OutdoorFurnishings = 67,
ChairsandBeds = 68,
Tables = 69,
Tabletop = 70,
Wallmounted = 71,
Rugs = 72,
RoguesArms = 73,
SeasonalMiscellany = 74,
Minions = 75,
DarkKnightsArms = 76,
MachinistsArms = 77,
AstrologiansArms = 78,
AirshipComponents = 79,
OrchestrionComponents = 80,
GardeningItems = 81,
Paintings = 82,
SamuraisArms = 83,
RedMagesArms = 84,
ScholarsArms = 85,
};
///////////////////////////////////////////////////////////
//OnlineStatus.exd
enum OnlineStatus : uint8_t
{
Producer = 1,
GameMaster = 2,
GameMaster1 = 3,
GameMaster2 = 4,
Disconnected = 5,
WaitingforFriendListApproval = 6,
WaitingforLinkshellApproval = 7,
WaitingforFreeCompanyApproval = 8,
NotFound = 9,
Offline = 10,
Mentor = 11,
Busy = 12,
PvP = 13,
PlayingTripleTriad = 14,
ViewingCutscene = 15,
UsingaChocoboPorter = 16,
AwayfromKeyboard = 17,
CameraMode = 18,
LookingforRepairs = 19,
LookingtoRepair = 20,
LookingtoMeldMateria = 21,
Roleplaying = 22,
LookingforParty = 23,
SwordforHire = 24,
WaitingforDutyFinder = 25,
RecruitingPartyMembers = 26,
Mentor1 = 27,
PvEMentor = 28,
TradeMentor = 29,
PvPMentor = 30,
Returner = 31,
NewAdventurer = 32,
AllianceLeader = 33,
AlliancePartyLeader = 34,
AlliancePartyMember = 35,
PartyLeader = 36,
PartyMember = 37,
PartyLeaderCrossworld = 38,
PartyMemberCrossworld = 39,
AnotherWorld = 40,
SharingDuty = 41,
SimilarDuty = 42,
InDuty = 43,
TrialAdventurer = 44,
FreeCompany = 45,
GrandCompany = 46,
Online = 47,
};
///////////////////////////////////////////////////////////
//Race.exd
enum Race : uint8_t
{
Hyur = 1,
Elezen = 2,
Lalafell = 3,
Miqote = 4,
Roegadyn = 5,
AuRa = 6,
};
///////////////////////////////////////////////////////////
//Tribe.exd
enum Tribe : uint8_t
{
Midlander = 1,
Highlander = 2,
Wildwood = 3,
Duskwight = 4,
Plainsfolk = 5,
Dunesfolk = 6,
SeekeroftheSun = 7,
KeeperoftheMoon = 8,
SeaWolf = 9,
Hellsguard = 10,
Raen = 11,
Xaela = 12,
};
///////////////////////////////////////////////////////////
//Town.exd
enum Town : uint8_t
{
Nowheresville = 0,
LimsaLominsa = 1,
Gridania = 2,
Uldah = 3,
Ishgard = 4,
Kugane = 7,
};
///////////////////////////////////////////////////////////
//Weather.exd
enum Weather : uint8_t
{
ClearSkies = 1,
FairSkies = 2,
Clouds = 3,
Fog = 4,
Wind = 5,
Gales = 6,
Rain = 7,
Showers = 8,
Thunder = 9,
Thunderstorms = 10,
DustStorms = 11,
Sandstorms = 12,
HotSpells = 13,
HeatWaves = 14,
Snow = 15,
Blizzards = 16,
Gloom = 17,
Auroras = 18,
Darkness = 19,
Tension = 20,
Clouds1 = 21,
StormClouds = 22,
RoughSeas = 23,
RoughSeas1 = 24,
Louring = 25,
HeatWaves1 = 26,
Gloom1 = 27,
Gales1 = 28,
Eruptions = 29,
FairSkies1 = 30,
FairSkies2 = 31,
FairSkies3 = 32,
FairSkies4 = 33,
FairSkies5 = 34,
Irradiance = 35,
CoreRadiation = 36,
CoreRadiation1 = 37,
CoreRadiation2 = 38,
CoreRadiation3 = 39,
ShelfClouds = 40,
ShelfClouds1 = 41,
ShelfClouds2 = 42,
ShelfClouds3 = 43,
Oppression = 44,
Oppression1 = 45,
Oppression2 = 46,
Oppression3 = 47,
Oppression4 = 48,
UmbralWind = 49,
UmbralStatic = 50,
Smoke = 51,
FairSkies6 = 52,
RoyalLevin = 53,
Hyperelectricity = 54,
RoyalLevin1 = 55,
Oppression5 = 56,
Thunder1 = 57,
Thunder2 = 58,
CutScene = 59,
Multiplicity = 60,
Multiplicity1 = 61,
Rain1 = 62,
FairSkies7 = 63,
Rain2 = 64,
FairSkies8 = 65,
Dragonstorm = 66,
Dragonstorm1 = 67,
Subterrain = 68,
Concordance = 69,
Concordance1 = 70,
BeyondTime = 71,
BeyondTime1 = 72,
BeyondTime2 = 73,
DemonicInfinity = 74,
DemonicInfinity1 = 75,
DemonicInfinity2 = 76,
DimensionalDisruption = 77,
DimensionalDisruption1 = 78,
DimensionalDisruption2 = 79,
Revelstorm = 80,
Revelstorm1 = 81,
EternalBliss = 82,
EternalBliss1 = 83,
Wyrmstorm = 84,
Wyrmstorm1 = 85,
Revelstorm2 = 86,
Quicklevin = 87,
Thunder3 = 88,
DimensionalDisruption3 = 89,
};
}
}

View file

@ -0,0 +1,114 @@
#include <GameData.h>
#include <File.h>
#include <DatCat.h>
#include <ExdData.h>
#include <ExdCat.h>
#include <Exd.h>
#include <iostream>
#include <cctype>
#include <set>
#include <src/servers/Server_Common/Exd/ExdData.h>
#include <src/servers/Server_Common/Logging/Logger.h>
#include <boost/range/algorithm/remove_if.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <fstream>
Core::Logger g_log;
Core::Data::ExdData g_exdData;
//const std::string datLocation( "/opt/sapphire_3_15_0/bin/sqpack" );
const std::string datLocation( "C:\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\sqpack\\ffxiv" );
std::string generateEnum( const std::string& exd, int8_t nameIndex, const std::string& type, bool useLang = true )
{
xiv::dat::GameData data( datLocation );
xiv::exd::ExdData eData( data );
std::map< std::string, uint32_t> nameMap;
std::string result = "\n ///////////////////////////////////////////////////////////\n";
result += " //" + exd + ".exd\n";
result += " enum " + exd + " : " + type + "\n";
result += " {\n";
auto lang = useLang ? xiv::exd::Language::en : xiv::exd::Language::none;
auto access = g_exdData.setupDatAccess( exd, lang );
auto rows = access.get_rows();
for( auto row : rows )
{
auto& fields = row.second;
uint32_t id = row.first;
auto test = boost::get< std::string >( &fields.at( nameIndex ) );
if( !test )
continue;
auto str = *test ;
str.erase( boost::remove_if( str, boost::is_any_of(",_-':!(){} \x02\x1f\x01\x03") ), str.end() );
if( str.empty() )
continue;
str[0] = std::toupper( str[0] );
auto it = nameMap.find( str );
if( it != nameMap.end() )
{
nameMap[str]++;
str = str + std::to_string( nameMap[str] );
}
else
{
nameMap[str] = 0;
}
result += " " + str + " = " + std::to_string( id ) + ",\n";
}
result += " };\n";
return result;
}
int main()
{
g_log.init();
g_log.info( "Setting up EXD data" );
if( !g_exdData.init( datLocation ) )
{
g_log.fatal( "Error setting up EXD data " );
return 0;
}
std::string result =
"/* This file has been automatically generated.\n Changes will be lost upon regeneration.\n To change the content edit tools/exd_common_gen */\n";
result += "namespace Core {\n";
result += "namespace Common {\n";
result += generateEnum( "ActionCategory", 0, "uint8_t" );
result += generateEnum( "BeastReputationRank", 1, "uint8_t" );
result += generateEnum( "BeastTribe", 10, "uint8_t" );
result += generateEnum( "ClassJob", 0, "uint8_t" );
result += generateEnum( "ContentType", 0, "uint8_t" );
result += generateEnum( "EmoteCategory", 0, "uint8_t" );
result += generateEnum( "ExVersion", 0, "uint8_t" );
result += generateEnum( "GrandCompany", 0, "uint8_t" );
result += generateEnum( "GuardianDeity", 0, "uint8_t" );
result += generateEnum( "ItemUICategory", 0, "uint8_t" );
result += generateEnum( "ItemSearchCategory", 0, "uint8_t" );
result += generateEnum( "OnlineStatus", 2, "uint8_t" );
result += generateEnum( "Race", 1, "uint8_t" );
result += generateEnum( "Tribe", 0, "uint8_t" );
result += generateEnum( "Town", 0, "uint8_t" );
result += generateEnum( "Weather", 1, "uint8_t" );
result += "}\n";
result += "}\n";
g_log.info( result );
return 0;
}

View file

@ -15,58 +15,6 @@ include_directories("../")
file(GLOB SERVER_PUBLIC_INCLUDE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/*")
file(GLOB SERVER_SOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}*.c*")
set(SERVER_COMMON_DIR ../../sapphire/Server_Common)
set(Boost_USE_STATIC_LIBS ON)
if(UNIX)
include_directories("/usr/include/mysql/")
message(STATUS "Setting GCC flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -m32")
find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system)
if(Boost_FOUND)
set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR})
else()
if (EXISTS /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME})
set(Boost_INCLUDE_DIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME})
set(BOOST_LIBRARYDIR /opt/build_libs/${SAPPHIRE_BOOST_FOLDER_NAME}/stage/lib)
find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system)
else()
message(FATAL_ERROR "Unable to find boost ${SAPPHIRE_BOOST_VER} package!")
endif()
endif()
else()
add_definitions(-D_WIN32_WINNT=0x601)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../lib/MySQL/")
message(STATUS "Setting MSVC flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/${SAPPHIRE_BOOST_FOLDER_NAME})
message(STATUS "Using boost in /src/lib")
set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/${SAPPHIRE_BOOST_FOLDER_NAME})
set(BOOST_LIBRARYDIR ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/${SAPPHIRE_BOOST_FOLDER_NAME}/lib32-msvc-14.0)
else()
find_package(Boost ${SAPPHIRE_BOOST_VER} COMPONENTS log log_setup thread date_time filesystem system)
if(Boost_FOUND)
set(BOOST_LIBRARY_DIR ${Boost_LIBRARY_DIR})
elseif ((EXISTS $ENV{BOOST_ROOT_DIR}) AND (EXISTS $ENV{BOOST_LIB_DIR}))
set(Boost_INCLUDE_DIR $ENV{BOOST_ROOT_DIR})
set(BOOST_LIBRARYDIR $ENV{BOOST_LIB_DIR})
else()
message(FATAL_ERROR "SapphireError: Unable to find boost ${SAPPHIRE_BOOST_VER} package and environment variables BOOST_ROOT_DIR and BOOST_LIB_DIR not set!")
endif()
endif()
endif()
include_directories(${Boost_INCLUDE_DIR})
link_directories(${BOOST_LIBRARYDIR})
link_directories(${SERVER_COMMON_DIR})
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../sapphire/datReader/)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/MySQL)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../lib/zlib)
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/")
add_executable(quest_parse ${SERVER_PUBLIC_INCLUDE_FILES} ${SERVER_SOURCE_FILES})

View file

@ -12,7 +12,6 @@
#include <fstream>
#include <windows.h>
Core::Logger g_log;
Core::Data::ExdData g_exdData;