mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-02 16:57:47 +00:00
Merge pull request #946 from collett8192/Sapphire5.58_action
5.58 action update
This commit is contained in:
commit
a42353a0a8
38 changed files with 1740 additions and 322 deletions
|
@ -67,11 +67,20 @@ namespace Sapphire::Common
|
|||
French = 8
|
||||
};
|
||||
|
||||
enum TellFlags : uint8_t
|
||||
enum ChatFromType : uint8_t
|
||||
{
|
||||
GmTellMsg = 0x4,
|
||||
};
|
||||
|
||||
enum ChatChannelType : uint16_t
|
||||
{
|
||||
CWLinkshellChat = 0x0,
|
||||
PartyChat = 0x1,
|
||||
LinkshellChat = 0x2,
|
||||
FreeCompanyChat = 0x3,
|
||||
NoviceNetworkChat = 0x4
|
||||
};
|
||||
|
||||
enum BNpcType : uint8_t
|
||||
{
|
||||
Friendly = 0,
|
||||
|
@ -801,6 +810,22 @@ namespace Sapphire::Common
|
|||
InvincibilityIgnoreDamage,
|
||||
};
|
||||
|
||||
enum InviteReplyType : int32_t
|
||||
{
|
||||
DENY = 0x0,
|
||||
ACCEPT = 0x1,
|
||||
CANCEL = 0x2,
|
||||
};
|
||||
|
||||
enum InviteUpdateType : uint8_t
|
||||
{
|
||||
NEW_INVITE = 0x01,
|
||||
INVITE_CANCEL = 0x02,
|
||||
JOINED_PARTY = 0x03,
|
||||
ACCEPT_INVITE = 0x04,
|
||||
REJECT_INVITE = 0x05,
|
||||
};
|
||||
|
||||
enum PlayerStateFlag : uint8_t
|
||||
{
|
||||
HideUILockChar = 0, // as the name suggests, hides the ui and logs the char...
|
||||
|
@ -1170,6 +1195,16 @@ namespace Sapphire::Common
|
|||
RequireCorrectPositional = 2,
|
||||
};
|
||||
|
||||
enum class StatusRefreshPolicy : uint8_t
|
||||
{
|
||||
Stack = 0,
|
||||
ReplaceOrApply = 1,
|
||||
Extend = 2,
|
||||
ExtendOrApply = 3,
|
||||
Reject = 4,
|
||||
Custom = 15, // script handled
|
||||
};
|
||||
|
||||
enum class AstCardType : uint8_t
|
||||
{
|
||||
None = 0,
|
||||
|
|
|
@ -20,6 +20,19 @@ struct FFXIVIpcTell : FFXIVIpcBasePacket< Tell >
|
|||
char msg[1029];
|
||||
};
|
||||
|
||||
struct FFXIVIpcChannelChat : FFXIVIpcBasePacket< ChannelChat >
|
||||
{
|
||||
uint64_t channelId;
|
||||
uint64_t contentId;
|
||||
uint32_t charaId;
|
||||
uint8_t type;
|
||||
uint8_t unknown1;
|
||||
uint8_t unknown2;
|
||||
char name[32];
|
||||
char message[1024];
|
||||
uint8_t padding;
|
||||
};
|
||||
|
||||
/**
|
||||
* Structural representation of the packet sent by the server as response
|
||||
* to a failed tell because of unavailable target player
|
||||
|
|
|
@ -73,15 +73,14 @@ namespace Sapphire::Network::Packets
|
|||
SocialRequestError = 0xF0AD,
|
||||
|
||||
CFRegistered = 0x029F, // updated 5.58 hotfix
|
||||
SocialRequestResponse = 0x0082, // updated 5.58 hotfix
|
||||
SocialMessage = 0x03CB, // updated 5.58 hotfix
|
||||
SocialMessage2 = 0x01D7, // updated 5.58 hotfix
|
||||
SocialInviteResponse = 0x0082, // updated 5.58 hotfix
|
||||
SocialInviteUpdate = 0x03CB, // updated 5.58 hotfix
|
||||
SocialInviteResult = 0x01D7, // updated 5.58 hotfix
|
||||
CancelAllianceForming = 0xF0C6, // updated 4.2
|
||||
|
||||
LogMessage = 0x0118, // updated 5.58 hotfix
|
||||
|
||||
Chat = 0x00FE, // updated 5.58 hotfix
|
||||
PartyChat = 0x0065,
|
||||
|
||||
WorldVisitList = 0xF0FE, // added 4.5
|
||||
|
||||
|
@ -149,7 +148,7 @@ namespace Sapphire::Network::Packets
|
|||
SomeCustomiseChangePacketProbably = 0x00CD, // added 5.18
|
||||
|
||||
PartyList = 0x0349, // updated 5.58 hotfix
|
||||
PartyMessage = 0x00A4, // updated 5.58 hotfix
|
||||
PartyUpdate = 0x00A4, // updated 5.58 hotfix
|
||||
HateRank = 0x0150, // updated 5.58 hotfix
|
||||
HateList = 0x0243, // updated 5.58 hotfix
|
||||
ObjectSpawn = 0x0125, // updated 5.58 hotfix
|
||||
|
@ -333,16 +332,15 @@ namespace Sapphire::Network::Packets
|
|||
CancelLogout = 0x01AC, // updated 5.58 hotfix
|
||||
CFDutyInfoHandler = 0xF078, // updated 4.2
|
||||
|
||||
SocialReqSendHandler = 0x00D7, // updated 5.58 hotfix
|
||||
SocialResponseHandler = 0x023B, // updated 5.58 hotfix
|
||||
SocialInviteHandler = 0x00D7, // updated 5.58 hotfix
|
||||
SocialReplyHandler = 0x023B, // updated 5.58 hotfix
|
||||
CreateCrossWorldLS = 0x035D, // updated 5.58 hotfix
|
||||
|
||||
ChatHandler = 0x03B0, // updated 5.58 hotfix
|
||||
PartyChatHandler = 0x0065,
|
||||
PartySetLeaderHandler = 0x036C, // updated 5.58 hotfix
|
||||
LeavePartyHandler = 0x019D, // updated 5.58 hotfix
|
||||
KickPartyMemberHandler = 0x0262, // updated 5.58 hotfix
|
||||
DisbandPartyHandler = 0x0276, // updated 5.58 hotfix
|
||||
PartyChangeLeaderHandler = 0x036C, // updated 5.58 hotfix
|
||||
PartyLeaveHandler = 0x019D, // updated 5.58 hotfix
|
||||
PartyKickHandler = 0x0262, // updated 5.58 hotfix
|
||||
PartyDisbandHandler = 0x0276, // updated 5.58 hotfix
|
||||
|
||||
SocialListHandler = 0x01CA, // updated 5.58 hotfix
|
||||
SetSearchInfoHandler = 0x01D4, // updated 5.58 hotfix
|
||||
|
@ -433,6 +431,7 @@ namespace Sapphire::Network::Packets
|
|||
enum ServerChatIpcType : uint16_t
|
||||
{
|
||||
Tell = 0x0064, // updated for sb
|
||||
ChannelChat = 0x0065,
|
||||
PublicContentTell = 0x00FB, // added 4.5, this is used when receiving a /tell in PublicContent instances such as Eureka or Bozja
|
||||
TellErrNotFound = 0x0066,
|
||||
|
||||
|
@ -445,6 +444,7 @@ namespace Sapphire::Network::Packets
|
|||
enum ClientChatIpcType : uint16_t
|
||||
{
|
||||
TellReq = 0x0064,
|
||||
ChannelChatReq = 0x0065,
|
||||
PublicContentTellReq = 0x0326, // updated 5.35 hotfix, this is used when sending a /tell in PublicContent instances such as Eureka or Bozja
|
||||
};
|
||||
|
||||
|
|
|
@ -207,10 +207,10 @@ struct FFXIVIpcChatHandler :
|
|||
/* 001A */ char message[1012];
|
||||
};
|
||||
|
||||
struct FFXIVIpcPartyChatHandler :
|
||||
FFXIVIpcBasePacket< ChatHandler >
|
||||
struct FFXIVIpcChannelChatHandler :
|
||||
FFXIVIpcBasePacket< ChannelChatReq >
|
||||
{
|
||||
uint64_t unknown;
|
||||
uint64_t channelId;
|
||||
char message[1024];
|
||||
};
|
||||
|
||||
|
@ -362,8 +362,8 @@ struct FFXIVIpcWorldInteractionHandler :
|
|||
Common::FFXIVARR_POSITION3 position;
|
||||
};
|
||||
|
||||
struct FFXIVIpcSocialReqSendHandler :
|
||||
FFXIVIpcBasePacket< SocialReqSendHandler >
|
||||
struct FFXIVIpcSocialInviteHandler :
|
||||
FFXIVIpcBasePacket< SocialInviteHandler >
|
||||
{
|
||||
uint64_t unknown;
|
||||
uint8_t p1;
|
||||
|
@ -373,8 +373,8 @@ struct FFXIVIpcSocialReqSendHandler :
|
|||
uint8_t padding[5];
|
||||
};
|
||||
|
||||
struct FFXIVIpcSocialResponseHandler :
|
||||
FFXIVIpcBasePacket< SocialResponseHandler >
|
||||
struct FFXIVIpcSocialReplyHandler :
|
||||
FFXIVIpcBasePacket< SocialReplyHandler >
|
||||
{
|
||||
uint64_t contentId;
|
||||
uint8_t p1;
|
||||
|
@ -384,8 +384,8 @@ struct FFXIVIpcSocialResponseHandler :
|
|||
uint32_t unknown;
|
||||
};
|
||||
|
||||
struct FFXIVIpcPartySetLeaderHandler :
|
||||
FFXIVIpcBasePacket< PartySetLeaderHandler >
|
||||
struct FFXIVIpcPartyChangeLeaderHandler :
|
||||
FFXIVIpcBasePacket< PartyChangeLeaderHandler >
|
||||
{
|
||||
uint64_t contentId;
|
||||
uint8_t p1;
|
||||
|
@ -394,14 +394,14 @@ struct FFXIVIpcPartySetLeaderHandler :
|
|||
uint8_t padding[6];
|
||||
};
|
||||
|
||||
struct FFXIVIpcLeavePartyHandler :
|
||||
FFXIVIpcBasePacket< LeavePartyHandler >
|
||||
struct FFXIVIpcPartyLeaveHandler :
|
||||
FFXIVIpcBasePacket< PartyLeaveHandler >
|
||||
{
|
||||
uint64_t empty;
|
||||
};
|
||||
|
||||
struct FFXIVIpcKickPartyMemberHander :
|
||||
FFXIVIpcBasePacket< KickPartyMemberHandler >
|
||||
struct FFXIVIpcPartyKickHandler :
|
||||
FFXIVIpcBasePacket< PartyKickHandler >
|
||||
{
|
||||
uint64_t contentId;
|
||||
uint8_t p1;
|
||||
|
@ -410,8 +410,8 @@ struct FFXIVIpcKickPartyMemberHander :
|
|||
uint8_t padding[6];
|
||||
};
|
||||
|
||||
struct FFXIVIpcDisbandPartyHandler :
|
||||
FFXIVIpcBasePacket< DisbandPartyHandler >
|
||||
struct FFXIVIpcPartyDisbandHandler :
|
||||
FFXIVIpcBasePacket< PartyDisbandHandler >
|
||||
{
|
||||
uint64_t empty;
|
||||
};
|
||||
|
|
|
@ -46,19 +46,6 @@ namespace Sapphire::Network::Packets::Server
|
|||
char msg[1012];
|
||||
};
|
||||
|
||||
struct FFXIVIpcPartyChat : FFXIVIpcBasePacket< PartyChat >
|
||||
{
|
||||
uint64_t unknown;
|
||||
uint64_t contentId;
|
||||
uint32_t charaId;
|
||||
uint8_t u1;
|
||||
uint8_t u2;
|
||||
uint8_t u3;
|
||||
char name[32];
|
||||
char message[1024];
|
||||
uint8_t padding;
|
||||
};
|
||||
|
||||
struct FFXIVIpcChatBanned : FFXIVIpcBasePacket< ChatBanned >
|
||||
{
|
||||
uint8_t padding[4]; // I was not sure reinterpreting ZST is valid behavior in C++.
|
||||
|
@ -2178,7 +2165,7 @@ namespace Sapphire::Network::Packets::Server
|
|||
uint32_t param7;
|
||||
};
|
||||
|
||||
struct FFXIVIpcSocialMessage : FFXIVIpcBasePacket< SocialMessage >
|
||||
struct FFXIVIpcSocialInviteUpdate : FFXIVIpcBasePacket< SocialInviteUpdate >
|
||||
{
|
||||
uint64_t contentId;
|
||||
uint32_t expireTime;
|
||||
|
@ -2187,12 +2174,12 @@ namespace Sapphire::Network::Packets::Server
|
|||
uint8_t socialType;
|
||||
uint8_t padding;
|
||||
uint8_t type;
|
||||
uint8_t unknown4;
|
||||
uint8_t gender;
|
||||
char name[32];
|
||||
uint8_t padding2[6];
|
||||
};
|
||||
|
||||
struct FFXIVIpcSocialMessage2 : FFXIVIpcBasePacket< SocialMessage2 >
|
||||
struct FFXIVIpcSocialInviteResult : FFXIVIpcBasePacket< SocialInviteResult >
|
||||
{
|
||||
uint64_t contentId;
|
||||
uint32_t unknown3;
|
||||
|
@ -2203,40 +2190,42 @@ namespace Sapphire::Network::Packets::Server
|
|||
char name[32];
|
||||
};
|
||||
|
||||
struct FFXIVIpcSocialRequestResponse : FFXIVIpcBasePacket< SocialRequestResponse >
|
||||
struct FFXIVIpcSocialInviteResponse : FFXIVIpcBasePacket< SocialInviteResponse >
|
||||
{
|
||||
uint64_t contentId;
|
||||
uint32_t unknown3;
|
||||
uint8_t u1AlwaysOne;
|
||||
uint8_t socialType;
|
||||
uint8_t response;
|
||||
uint8_t u2AlwaysOne;
|
||||
uint8_t gender;
|
||||
char name[32];
|
||||
uint8_t padding;
|
||||
};
|
||||
|
||||
struct PartyMember
|
||||
{
|
||||
char name[32];
|
||||
uint64_t contentId;
|
||||
uint32_t charaId;
|
||||
uint32_t u1; // 3.x ParentEntityId?
|
||||
uint32_t u2; // 3.x PetEntityId?
|
||||
uint32_t hp;
|
||||
uint32_t maxHp;
|
||||
uint16_t mp;
|
||||
uint16_t maxMp;
|
||||
uint16_t u3;
|
||||
uint16_t zoneId;
|
||||
uint8_t gposeSelectable; // 3.x Valid?
|
||||
uint8_t classId;
|
||||
uint8_t u5; // 3.x ObjType?
|
||||
uint8_t level;
|
||||
uint8_t isLevelSync;
|
||||
uint8_t unknown[7];
|
||||
Common::StatusEffect effect[30];
|
||||
};
|
||||
|
||||
struct FFXIVIpcPartyList : FFXIVIpcBasePacket< PartyList >
|
||||
{
|
||||
struct
|
||||
{
|
||||
char name[32];
|
||||
uint64_t contentId;
|
||||
uint32_t charaId;
|
||||
uint32_t u1;
|
||||
uint32_t u2;
|
||||
uint32_t hp;
|
||||
uint32_t maxHp;
|
||||
uint16_t mp;
|
||||
uint16_t maxMp;
|
||||
uint16_t u3;
|
||||
uint16_t zoneId;
|
||||
uint8_t gposeSelectable;
|
||||
uint8_t classId;
|
||||
uint8_t u5;
|
||||
uint8_t level;
|
||||
uint8_t isLevelSync;
|
||||
uint8_t unknown[7];
|
||||
Common::StatusEffect effect[30];
|
||||
} member[8];
|
||||
PartyMember member[8];
|
||||
uint64_t partyId;
|
||||
uint64_t channelId;
|
||||
uint8_t leaderIndex;
|
||||
|
@ -2245,16 +2234,16 @@ namespace Sapphire::Network::Packets::Server
|
|||
uint32_t padding2;
|
||||
};
|
||||
|
||||
struct FFXIVIpcPartyMessage : FFXIVIpcBasePacket< PartyMessage >
|
||||
struct FFXIVIpcPartyUpdate : FFXIVIpcBasePacket< PartyUpdate >
|
||||
{
|
||||
uint64_t leaderContentId;
|
||||
uint64_t memberContentId;
|
||||
uint8_t u1;
|
||||
uint8_t u2;
|
||||
uint16_t type;
|
||||
uint8_t partySize; // ?
|
||||
char leaderName[32];
|
||||
char memberName[32];
|
||||
uint64_t executeContentId;
|
||||
uint64_t targetContentId;
|
||||
uint8_t executeGender;
|
||||
uint8_t targetGender;
|
||||
uint16_t updateStatus;
|
||||
uint8_t partySize;
|
||||
char executeName[32];
|
||||
char targetName[32];
|
||||
uint8_t padding[3];
|
||||
};
|
||||
|
||||
|
|
46
src/scripts/action/whm/ActionDivineBenison7432.cpp
Normal file
46
src/scripts/action/whm/ActionDivineBenison7432.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include <Script/NativeScriptApi.h>
|
||||
#include <ScriptObject.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Action/Action.h>
|
||||
#include <Math/CalcStats.h>
|
||||
|
||||
#include "StatusEffect/StatusEffect.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::StatusEffect;
|
||||
|
||||
const uint16_t STATUS_ID_DEVINE_BENISON = 1218;
|
||||
|
||||
class ActionDivineBenison7432 :
|
||||
public ScriptAPI::ActionScript
|
||||
{
|
||||
public:
|
||||
ActionDivineBenison7432() :
|
||||
ScriptAPI::ActionScript( 7432 )
|
||||
{
|
||||
}
|
||||
|
||||
void onExecute( Sapphire::World::Action::Action& action ) override
|
||||
{
|
||||
auto pTarget = action.getHitChara();
|
||||
if( pTarget )
|
||||
{
|
||||
float shieldValue = action.calcHealing( 500 ).first; // 500 ptc worth of shield
|
||||
shieldValue = Math::CalcStats::applyHealingReceiveMultiplier( *pTarget, shieldValue );
|
||||
|
||||
auto oldEffect = pTarget->getStatusEffectById( STATUS_ID_DEVINE_BENISON );
|
||||
if( !oldEffect.second || oldEffect.second->getEffectEntry().getRemainingShield() <= shieldValue )
|
||||
{
|
||||
World::Action::StatusEffectEntry effectEntry;
|
||||
effectEntry.init( Common::StatusEffectType::Shield, shieldValue, 0, 0, 0 );
|
||||
auto pNewEffect = Sapphire::StatusEffect::make_StatusEffect( STATUS_ID_DEVINE_BENISON, action.getSourceChara(), pTarget, 15000, 3000 );
|
||||
pNewEffect->replaceEffectEntry( effectEntry );
|
||||
action.getEffectbuilder()->applyStatusEffect( pTarget, action.getSourceChara(), pNewEffect );
|
||||
}
|
||||
else
|
||||
action.getEffectbuilder()->statusNoEffect( pTarget, STATUS_ID_DEVINE_BENISON );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EXPOSE_SCRIPT( ActionDivineBenison7432 );
|
|
@ -512,8 +512,8 @@ void Action::Action::buildEffects()
|
|||
if( !shouldHitThisTarget )
|
||||
continue;
|
||||
|
||||
bool shouldTriggerActionBonus = true;
|
||||
if( m_lutEntry.damagePotency > 0 )
|
||||
bool isValidVictim = false;
|
||||
if( m_lutEntry.damagePotency > 0 && actor->getObjKind() != m_pSource->getObjKind() /* is NOT friendly target, this will do for now */ )
|
||||
{
|
||||
Common::AttackType attackType = static_cast< Common::AttackType >( m_actionData->attackType );
|
||||
actor->onActionHostile( m_pSource );
|
||||
|
@ -539,7 +539,6 @@ void Action::Action::buildEffects()
|
|||
if( actor->hasInvulnerableEffect() )
|
||||
{
|
||||
dmg.first = 0;
|
||||
shouldTriggerActionBonus = false;
|
||||
}
|
||||
|
||||
if( dmg.first > 0 )
|
||||
|
@ -563,7 +562,6 @@ void Action::Action::buildEffects()
|
|||
if( dodged )
|
||||
{
|
||||
dmg.first = 0;
|
||||
shouldTriggerActionBonus = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -573,6 +571,7 @@ void Action::Action::buildEffects()
|
|||
|
||||
if( dmg.first > 0 )
|
||||
{
|
||||
isValidVictim = true;
|
||||
auto attackTypeEffect = m_actionData->attackType;
|
||||
if( attackTypeEffect == -1 )
|
||||
{
|
||||
|
@ -612,116 +611,117 @@ void Action::Action::buildEffects()
|
|||
m_effectBuilder->invulnerable( actor );
|
||||
}
|
||||
}
|
||||
|
||||
if( shouldTriggerActionBonus )
|
||||
{
|
||||
if( ( !isComboAction() || isCorrectCombo() ) )
|
||||
{
|
||||
if ( !m_actionData->preservesCombo ) // this matches retail packet, on all standalone actions even casts.
|
||||
{
|
||||
m_effectBuilder->startCombo( actor, getId() ); // this is on all targets hit
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::SelfHeal )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
auto heal = calcHealing( m_lutEntry.getSelfHealPotency() );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *m_pSource, heal.first );
|
||||
m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
}
|
||||
|
||||
if( validVictimCounter == 0 ) // effects only apply once if aoe, on first valid target (can be single target action as well)
|
||||
{
|
||||
if( isCorrectCombo() )
|
||||
m_effectBuilder->comboSucceed( actor );
|
||||
|
||||
if( m_isAutoAttack && player )
|
||||
{
|
||||
if( player->getClass() == Common::ClassJob::Paladin )
|
||||
{
|
||||
player->gaugePldSetOath( std::min( 100, player->gaugePldGetOath() + 5 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainMPPercentage )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.getMPGainPercentage() / 100, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
|
||||
if( ( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobResource ) && player )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
switch( m_lutEntry.getAffectedJob() )
|
||||
{
|
||||
case Common::ClassJob::Marauder:
|
||||
case Common::ClassJob::Warrior:
|
||||
{
|
||||
player->gaugeWarSetIb( std::min( 100, player->gaugeWarGetIb() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
player->gaugeDrkSetBlood( std::min( 100, player->gaugeDrkGetBlood() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Gunbreaker:
|
||||
{
|
||||
player->gaugeGnbSetAmmo( std::min( 2, player->gaugeGnbGetAmmo() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Samurai:
|
||||
{
|
||||
player->gaugeSamSetKenki( std::min( 100, player->gaugeSamGetKenki() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobTimer ) && player )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
switch( m_lutEntry.getAffectedJob() )
|
||||
{
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
player->gaugeDrkSetDarkSideTimer( std::min( 60000, player->gaugeDrkGetDarkSideTimer() + m_lutEntry.getJobTimerGain() ), true );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Dragoon:
|
||||
{
|
||||
player->gaugeDrgSetDragonTimer( std::min( 30000, player->gaugeDrgGetDragonTimer() + m_lutEntry.getJobTimerGain() ), true );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( validVictimCounter == 0 )
|
||||
firstValidVictim = actor;
|
||||
validVictimCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.healPotency > 0 && actor->getObjKind() == m_pSource->getObjKind() /* is friendly target, this will do for now */)
|
||||
{
|
||||
isValidVictim = true;
|
||||
auto heal = calcHealing( m_lutEntry.healPotency );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *actor, heal.first );
|
||||
m_effectBuilder->heal( actor, actor, heal.first, heal.second, Common::ActionEffectResultFlag::None, getExecutionDelay() + victimCounter * 100 );
|
||||
}
|
||||
|
||||
if( isValidVictim )
|
||||
{
|
||||
if( ( !isComboAction() || isCorrectCombo() ) )
|
||||
{
|
||||
if ( !m_actionData->preservesCombo ) // this matches retail packet, on all standalone actions even casts.
|
||||
{
|
||||
m_effectBuilder->startCombo( actor, getId() ); // this is on all targets hit
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::SelfHeal )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
auto heal = calcHealing( m_lutEntry.getSelfHealPotency() );
|
||||
heal.first = Math::CalcStats::applyHealingReceiveMultiplier( *m_pSource, heal.first );
|
||||
m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
}
|
||||
|
||||
if( validVictimCounter == 0 ) // effects only apply once if aoe, on first valid target (can be single target action as well)
|
||||
{
|
||||
if( isCorrectCombo() )
|
||||
m_effectBuilder->comboSucceed( actor );
|
||||
|
||||
if( m_isAutoAttack && player )
|
||||
{
|
||||
if( player->getClass() == Common::ClassJob::Paladin )
|
||||
{
|
||||
player->gaugePldSetOath( std::min( 100, player->gaugePldGetOath() + 5 ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainMPPercentage )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.getMPGainPercentage() / 100, Common::ActionEffectResultFlag::EffectOnSource );
|
||||
}
|
||||
|
||||
if( ( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobResource ) && player )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
switch( m_lutEntry.getAffectedJob() )
|
||||
{
|
||||
case Common::ClassJob::Marauder:
|
||||
case Common::ClassJob::Warrior:
|
||||
{
|
||||
player->gaugeWarSetIb( std::min( 100, player->gaugeWarGetIb() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
player->gaugeDrkSetBlood( std::min( 100, player->gaugeDrkGetBlood() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Gunbreaker:
|
||||
{
|
||||
player->gaugeGnbSetAmmo( std::min( 2, player->gaugeGnbGetAmmo() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Samurai:
|
||||
{
|
||||
player->gaugeSamSetKenki( std::min( 100, player->gaugeSamGetKenki() + m_lutEntry.getJobResourceGain() ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobTimer ) && player )
|
||||
{
|
||||
if( checkActionBonusRequirement() )
|
||||
{
|
||||
switch( m_lutEntry.getAffectedJob() )
|
||||
{
|
||||
case Common::ClassJob::Darkknight:
|
||||
{
|
||||
player->gaugeDrkSetDarkSideTimer( std::min( 60000, player->gaugeDrkGetDarkSideTimer() + m_lutEntry.getJobTimerGain() ), true );
|
||||
break;
|
||||
}
|
||||
case Common::ClassJob::Dragoon:
|
||||
{
|
||||
player->gaugeDrgSetDragonTimer( std::min( 30000, player->gaugeDrgGetDragonTimer() + m_lutEntry.getJobTimerGain() ), true );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( validVictimCounter == 0 )
|
||||
firstValidVictim = actor;
|
||||
validVictimCounter++;
|
||||
}
|
||||
|
||||
if( m_lutEntry.targetStatus != 0 )
|
||||
{
|
||||
if( shouldTriggerActionBonus || actor->getObjKind() == m_pSource->getObjKind() /* is friendly target, this will do for now */ )
|
||||
if( isValidVictim || actor->getObjKind() == m_pSource->getObjKind() /* is friendly target, this will do for now */ )
|
||||
{
|
||||
if( !isComboAction() || isCorrectCombo() )
|
||||
m_effectBuilder->applyStatusEffect( actor, m_pSource, m_lutEntry.targetStatus, m_lutEntry.targetStatusDuration, m_lutEntry.targetStatusParam, getExecutionDelay() + victimCounter * 100 );
|
||||
applyStatusEffect( false, actor, m_pSource, m_lutEntry.targetStatus, m_lutEntry.targetStatusDuration, m_lutEntry.targetStatusParam, getExecutionDelay() + victimCounter * 100 );
|
||||
}
|
||||
else if( actor->hasInvulnerableEffect() )
|
||||
{
|
||||
|
@ -735,9 +735,9 @@ void Action::Action::buildEffects()
|
|||
if( !isComboAction() || isCorrectCombo() )
|
||||
{
|
||||
if( firstValidVictim )
|
||||
m_effectBuilder->applyStatusEffect( firstValidVictim, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam, getExecutionDelay(), true );
|
||||
applyStatusEffect( true, firstValidVictim, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam, getExecutionDelay(), true );
|
||||
else if ( m_lutEntry.damagePotency == 0 ) // only non-offensive actions can apply self status without a valid victim
|
||||
m_effectBuilder->applyStatusEffect( m_pSource, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam, getExecutionDelay() );
|
||||
applyStatusEffect( true, m_pSource, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam, getExecutionDelay() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1168,6 +1168,9 @@ void Action::Action::addDefaultActorFilters()
|
|||
|
||||
bool Action::Action::preFilterActor( Sapphire::Entity::Actor& actor ) const
|
||||
{
|
||||
if( m_castType == Common::CastType::SingleTarget ) // trust the target sent by client is valid
|
||||
return true;
|
||||
|
||||
auto kind = actor.getObjKind();
|
||||
auto chara = actor.getAsChara();
|
||||
|
||||
|
@ -1175,19 +1178,16 @@ bool Action::Action::preFilterActor( Sapphire::Entity::Actor& actor ) const
|
|||
if( kind != ObjKind::BattleNpc && kind != ObjKind::Player )
|
||||
return false;
|
||||
|
||||
if( m_lutEntry.damagePotency > 0 && chara->getId() == m_pSource->getId() ) // !m_canTargetSelf
|
||||
return false;
|
||||
|
||||
if( ( m_lutEntry.damagePotency > 0 || m_lutEntry.healPotency > 0 ) && !chara->isAlive() ) // !m_canTargetDead not working for aoe
|
||||
return false;
|
||||
if( chara->isAlive() && m_lutEntry.healPotency > 0 && m_pSource->getObjKind() == chara->getObjKind() )
|
||||
return true;
|
||||
|
||||
if( m_lutEntry.damagePotency > 0 && m_pSource->getObjKind() == chara->getObjKind() ) // !m_canTargetFriendly not working for aoe
|
||||
return false;
|
||||
|
||||
if( ( m_lutEntry.damagePotency == 0 && m_lutEntry.healPotency > 0 ) && m_pSource->getObjKind() != chara->getObjKind() ) // !m_canTargetHostile not working for aoe
|
||||
return false;
|
||||
if( chara->isAlive() && m_lutEntry.damagePotency > 0 && m_pSource->getObjKind() != chara->getObjKind() )
|
||||
return true;
|
||||
|
||||
return true;
|
||||
if( !chara->isAlive() && m_lutEntry.damagePotency == 0 && m_lutEntry.healPotency == 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector< Sapphire::Entity::CharaPtr >& Action::Action::getHitCharas()
|
||||
|
@ -1334,4 +1334,75 @@ uint64_t Action::Action::getExecutionDelay() const
|
|||
{
|
||||
// let's see how 3.x is going to do it
|
||||
return 600;
|
||||
}
|
||||
|
||||
void Sapphire::World::Action::Action::applyStatusEffect( bool selfStatus, Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs, bool statusToSource )
|
||||
{
|
||||
auto hasSameStatus = false;
|
||||
auto hasSameStatusFromSameCaster = false;
|
||||
StatusEffect::StatusEffectPtr referenceStatus = nullptr;
|
||||
for( auto const& entry : statusToSource ? source->getStatusEffectMap() : target->getStatusEffectMap() )
|
||||
{
|
||||
auto statusEffect = entry.second;
|
||||
if( statusEffect->getId() == statusId )
|
||||
{
|
||||
hasSameStatus = true;
|
||||
if( !referenceStatus )
|
||||
referenceStatus = statusEffect;
|
||||
if( statusEffect->getSrcActorId() == source->getId() )
|
||||
{
|
||||
hasSameStatusFromSameCaster = true;
|
||||
referenceStatus = statusEffect;
|
||||
break;;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto policy = selfStatus ? m_lutEntry.getSelfStatusRefreshPolicy( hasSameStatusFromSameCaster ) : m_lutEntry.getTargetStatusRefreshPolicy( hasSameStatusFromSameCaster );
|
||||
int64_t policyValue = selfStatus ? m_lutEntry.getSelfStatusRefreshValue() : m_lutEntry.getTargetStatusRefreshValue();
|
||||
switch( policy )
|
||||
{
|
||||
case Common::StatusRefreshPolicy::Stack:
|
||||
{
|
||||
m_effectBuilder->applyStatusEffect( target, source, statusId, duration, param, resultDelayMs, statusToSource, false );
|
||||
break;
|
||||
}
|
||||
case Common::StatusRefreshPolicy::ReplaceOrApply:
|
||||
{
|
||||
m_effectBuilder->applyStatusEffect( target, source, statusId, duration, param, resultDelayMs, statusToSource, true );
|
||||
break;
|
||||
}
|
||||
case Common::StatusRefreshPolicy::Extend:
|
||||
case Common::StatusRefreshPolicy::ExtendOrApply:
|
||||
{
|
||||
int64_t remainingDuration = 0;
|
||||
if( hasSameStatus )
|
||||
{
|
||||
remainingDuration = static_cast< int64_t >( referenceStatus->getDuration() ) - ( Common::Util::getTimeMs() - referenceStatus->getStartTimeMs() ) - resultDelayMs;
|
||||
if( remainingDuration < 0 )
|
||||
remainingDuration = 0;
|
||||
}
|
||||
if( hasSameStatus || policy == Common::StatusRefreshPolicy::ExtendOrApply )
|
||||
{
|
||||
m_effectBuilder->applyStatusEffect( target, source, statusId, std::min( duration + remainingDuration, policyValue ), param, resultDelayMs, statusToSource, true );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Common::StatusRefreshPolicy::Reject:
|
||||
{
|
||||
if( !hasSameStatus )
|
||||
{
|
||||
m_effectBuilder->applyStatusEffect( target, source, statusId, duration, param, resultDelayMs, statusToSource, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_effectBuilder->statusNoEffect( target, statusId );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Common::StatusRefreshPolicy::Custom:
|
||||
{
|
||||
// expect script to handle it
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -176,6 +176,8 @@ namespace Sapphire::World::Action
|
|||
|
||||
bool hasValidLutEntry() const;
|
||||
|
||||
void applyStatusEffect( bool selfStatus, Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs = 500, bool statusToSource = false );
|
||||
|
||||
uint32_t m_id;
|
||||
|
||||
uint16_t m_sequence;
|
||||
|
|
|
@ -351,21 +351,46 @@ bool Sapphire::World::Action::StatusEffectEntry::canApplyToAction( uint32_t acti
|
|||
}
|
||||
}
|
||||
|
||||
Sapphire::World::Action::ActionEntry::ActionEntry( uint16_t dp, uint16_t dcp, uint16_t ddp, uint16_t hp, uint16_t ss, uint32_t ssd, uint16_t ssp, uint16_t ts, uint32_t tsd, uint16_t tsp, uint8_t be, uint8_t br, uint32_t bdu32 )
|
||||
Sapphire::World::Action::ActionEntry::ActionEntry( uint16_t dp, uint16_t dcp, uint16_t ddp, uint16_t hp, uint16_t ss, uint32_t ssd, uint16_t ssp, uint16_t ts, uint32_t tsd, uint16_t tsp, uint8_t be, uint8_t br, uint32_t bdu32 ) :
|
||||
damagePotency( dp ),
|
||||
damageComboPotency( dcp ),
|
||||
damageDirectionalPotency( ddp ),
|
||||
healPotency( hp ),
|
||||
selfStatus( ss ),
|
||||
selfStatusDuration( ssd ),
|
||||
selfStatusParam( ssp ),
|
||||
targetStatus( ts ),
|
||||
targetStatusDuration( tsd ),
|
||||
targetStatusParam ( tsp ),
|
||||
bonusEffect( be ),
|
||||
bonusRequirement( br ),
|
||||
bonusDataUInt32 ( bdu32 ),
|
||||
selfStatusRefreshPolicy( 1 ),
|
||||
selfStatusRefreshValue( 0 ),
|
||||
targetStatusRefreshPolicy( 1 ),
|
||||
targetStatusRefreshValue( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
Sapphire::World::Action::ActionEntry::ActionEntry( uint16_t dp, uint16_t dcp, uint16_t ddp, uint16_t hp, uint16_t ss, uint32_t ssd, uint16_t ssp, uint16_t ts, uint32_t tsd, uint16_t tsp, uint8_t be, uint8_t br, uint32_t bdu32, int ssrp, int32_t ssrv, int tsrp, int32_t tsrv ) :
|
||||
damagePotency( dp ),
|
||||
damageComboPotency( dcp ),
|
||||
damageDirectionalPotency( ddp ),
|
||||
healPotency( hp ),
|
||||
selfStatus( ss ),
|
||||
selfStatusDuration( ssd ),
|
||||
selfStatusParam( ssp ),
|
||||
targetStatus( ts ),
|
||||
targetStatusDuration( tsd ),
|
||||
targetStatusParam ( tsp ),
|
||||
bonusEffect( be ),
|
||||
bonusRequirement( br ),
|
||||
bonusDataUInt32 ( bdu32 ),
|
||||
selfStatusRefreshPolicy( static_cast< uint8_t >( ssrp ) ),
|
||||
selfStatusRefreshValue( ssrv ),
|
||||
targetStatusRefreshPolicy( static_cast< uint8_t >( tsrp ) ),
|
||||
targetStatusRefreshValue( tsrv )
|
||||
{
|
||||
damagePotency = dp;
|
||||
damageComboPotency = dcp;
|
||||
damageDirectionalPotency = ddp;
|
||||
healPotency = hp;
|
||||
selfStatus = ss;
|
||||
selfStatusDuration = ssd;
|
||||
selfStatusParam = ssp;
|
||||
targetStatus = ts;
|
||||
targetStatusDuration = tsd;
|
||||
targetStatusParam = tsp;
|
||||
bonusEffect = be;
|
||||
bonusRequirement = br;
|
||||
bonusDataUInt32 = bdu32;
|
||||
}
|
||||
|
||||
uint32_t Sapphire::World::Action::ActionEntry::getRawBonusData() const
|
||||
|
@ -429,3 +454,25 @@ uint16_t Sapphire::World::Action::ActionEntry::getDirectHitRateBonus() const
|
|||
return bonusDataUInt16L;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Sapphire::Common::StatusRefreshPolicy Sapphire::World::Action::ActionEntry::getSelfStatusRefreshPolicy( bool sameSource )
|
||||
{
|
||||
uint8_t policy = sameSource ? selfStatusRefreshPolicy >> 4 : selfStatusRefreshPolicy & 0x0F;
|
||||
return static_cast< Sapphire::Common::StatusRefreshPolicy >( policy );
|
||||
}
|
||||
|
||||
Sapphire::Common::StatusRefreshPolicy Sapphire::World::Action::ActionEntry::getTargetStatusRefreshPolicy( bool sameSource )
|
||||
{
|
||||
uint8_t policy = sameSource ? targetStatusRefreshPolicy >> 4 : targetStatusRefreshPolicy & 0x0F;
|
||||
return static_cast< Sapphire::Common::StatusRefreshPolicy >( policy );
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::ActionEntry::getSelfStatusRefreshValue()
|
||||
{
|
||||
return selfStatusRefreshValue;
|
||||
}
|
||||
|
||||
int32_t Sapphire::World::Action::ActionEntry::getTargetStatusRefreshValue()
|
||||
{
|
||||
return targetStatusRefreshValue;
|
||||
}
|
||||
|
|
|
@ -40,9 +40,14 @@ namespace Sapphire::World::Action
|
|||
uint8_t bonusDataByte4;
|
||||
};
|
||||
};
|
||||
uint8_t selfStatusRefreshPolicy;
|
||||
int32_t selfStatusRefreshValue;
|
||||
uint8_t targetStatusRefreshPolicy;
|
||||
int32_t targetStatusRefreshValue;
|
||||
public:
|
||||
ActionEntry() = default;
|
||||
ActionEntry( uint16_t dp, uint16_t dcp, uint16_t ddp, uint16_t hp, uint16_t ss, uint32_t ssd, uint16_t ssp, uint16_t ts, uint32_t tsd, uint16_t tsp, uint8_t be, uint8_t br, uint32_t bdu32 );
|
||||
ActionEntry( uint16_t dp, uint16_t dcp, uint16_t ddp, uint16_t hp, uint16_t ss, uint32_t ssd, uint16_t ssp, uint16_t ts, uint32_t tsd, uint16_t tsp, uint8_t be, uint8_t br, uint32_t bdu32, int ssrp, int32_t ssrv, int tsrp, int32_t tsrv );
|
||||
uint32_t getRawBonusData() const;
|
||||
uint8_t getDamageFallOffPercentage() const; // as the result percentage for 2nd (or more) victims, not the percentage to subtract from 100%
|
||||
uint16_t getSelfHealPotency() const;
|
||||
|
@ -52,6 +57,10 @@ namespace Sapphire::World::Action
|
|||
uint16_t getJobTimerGain() const;
|
||||
uint16_t getCritRateBonus() const;
|
||||
uint16_t getDirectHitRateBonus() const;
|
||||
Common::StatusRefreshPolicy getSelfStatusRefreshPolicy( bool sameSource );
|
||||
Common::StatusRefreshPolicy getTargetStatusRefreshPolicy( bool sameSource );
|
||||
int32_t getSelfStatusRefreshValue();
|
||||
int32_t getTargetStatusRefreshValue();
|
||||
};
|
||||
|
||||
struct StatusEffectEntry
|
||||
|
|
|
@ -135,14 +135,15 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
{ 31, { 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
|
||||
//Maim, メイム
|
||||
//has damage: potency 100, combo potency 300, directional potency 0
|
||||
//has damage: potency 100, combo potency 320, directional potency 0
|
||||
//has bonus effect: GainJobResource, 169148416
|
||||
//bonus effect requirement: RequireCorrectCombo
|
||||
{ 37, { 100, 300, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 169148416 } },
|
||||
{ 37, { 100, 320, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 169148416 } },
|
||||
|
||||
//Berserk, バーサク
|
||||
//applies to self: Berserk, バーサク, duration 10000, param 0
|
||||
{ 38, { 0, 0, 0, 0, 86, 10000, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
//applies to target: Storm's Eye, シュトルムブレハ, duration 15000, param 0, Extend, Extend, 60000
|
||||
{ 38, { 0, 0, 0, 0, 86, 10000, 0, 90, 15000, 0, 0, 0, 0, 0, 0, ( 2 << 4 ) + 2, 60000 } },
|
||||
|
||||
//Overpower, オーバーパワー
|
||||
//has damage: potency 130, combo potency 0, directional potency 0
|
||||
|
@ -158,10 +159,10 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
{ 46, { 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
|
||||
//Storm's Path, シュトルムヴィント
|
||||
//has damage: potency 100, combo potency 380, directional potency 0
|
||||
//has damage: potency 100, combo potency 420, directional potency 0
|
||||
//has bonus effect: 24, 336920826
|
||||
//bonus effect requirement: RequireCorrectCombo
|
||||
{ 42, { 100, 380, 0, 0, 0, 0, 0, 0, 0, 0, 24, 1, 336920826 } },
|
||||
{ 42, { 100, 420, 0, 0, 0, 0, 0, 0, 0, 0, 24, 1, 336920826 } },
|
||||
|
||||
//Thrill of Battle, スリル・オブ・バトル
|
||||
//applies to targets: Thrill of Battle, スリル・オブ・バトル, duration 20000, param 0
|
||||
|
@ -179,11 +180,11 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
{ 43, { 0, 0, 0, 0, 409, 8000, 0, 88, 8000, 0, 0, 0, 0 } },
|
||||
|
||||
//Storm's Eye, シュトルムブレハ
|
||||
//has damage: potency 100, combo potency 380, directional potency 0
|
||||
//applies to self: Storm's Eye, シュトルムブレハ, duration 30000, param 0
|
||||
//has damage: potency 100, combo potency 420, directional potency 0
|
||||
//applies to self: Storm's Eye, シュトルムブレハ, duration 30000, param 0, ExtendOrApply, ExtendOrApply, 60000
|
||||
//has bonus effect: GainJobResource, 169148416
|
||||
//bonus effect requirement: RequireCorrectCombo
|
||||
{ 45, { 100, 380, 0, 0, 90, 30000, 0, 0, 0, 0, 8, 1, 169148416 } },
|
||||
{ 45, { 100, 420, 0, 0, 90, 30000, 0, 0, 0, 0, 8, 1, 169148416, ( 3 << 4 ) + 3, 60000, 0, 0 } },
|
||||
|
||||
//Inner Beast, 原初の魂
|
||||
//has damage: potency 350, combo potency 0, directional potency 0
|
||||
|
@ -191,7 +192,8 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
|
||||
//Mythril Tempest, ミスリルテンペスト
|
||||
//has damage: potency 100, combo potency 200, directional potency 0
|
||||
{ 16462, { 100, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
//applies to self: Storm's Eye, シュトルムブレハ, duration 30000, param 0, Extend, Extend, 60000
|
||||
{ 16462, { 100, 200, 0, 0, 90, 30000, 0, 0, 0, 0, 0, 0, 0, ( 2 << 4 ) + 2, 60000, 0, 0 } },
|
||||
|
||||
//Steel Cyclone, スチールサイクロン
|
||||
//has damage: potency 220, combo potency 0, directional potency 0
|
||||
|
@ -231,7 +233,8 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
|
||||
//Inner Release, 原初の解放
|
||||
//applies to self: Inner Release, 原初の解放, duration 10000, param 65436
|
||||
{ 7389, { 0, 0, 0, 0, 1177, 10000, 65436, 0, 0, 0, 0, 0, 0 } },
|
||||
//applies to target: Storm's Eye, シュトルムブレハ, duration 15000, param 0, Extend, Extend, 60000
|
||||
{ 7389, { 0, 0, 0, 0, 1177, 10000, 65436, 90, 15000, 0, 0, 0, 0, 0, 0, ( 2 << 4 ) + 2, 60000 } },
|
||||
|
||||
//Chaotic Cyclone, カオティックサイクロン
|
||||
//has damage: potency 400, combo potency 0, directional potency 0
|
||||
|
@ -239,8 +242,8 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
{ 16463, { 400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 100 } },
|
||||
|
||||
//Nascent Flash, 原初の猛り
|
||||
//applies to targets: Sleep, 睡眠, duration 30000, param 0
|
||||
{ 16464, { 0, 0, 0, 0, 0, 0, 0, 3, 30000, 0, 0, 0, 0 } },
|
||||
//applies to targets: Nascent Flash, 原初の猛り, duration 6000, param 0
|
||||
{ 16464, { 0, 0, 0, 0, 0, 0, 0, 1857, 6000, 0, 0, 0, 0 } },
|
||||
|
||||
//Inner Chaos, インナーカオス
|
||||
//has damage: potency 920, combo potency 0, directional potency 0
|
||||
|
@ -1834,8 +1837,8 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
{ 7431, { 280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
|
||||
//Divine Benison, ディヴァインベニゾン
|
||||
//applies to targets: Divine Benison, ディヴァインベニゾン, duration 15000, param 0
|
||||
{ 7432, { 0, 0, 0, 0, 0, 0, 0, 1218, 15000, 0, 0, 0, 0 } },
|
||||
//applies to targets: Divine Benison, ディヴァインベニゾン, duration 15000, param 0 >> moved to script
|
||||
{ 7432, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
|
||||
//Plenary Indulgence, インドゥルゲンティア
|
||||
//applies to targets: Confession, インドゥルゲンティア, duration 10000, param 0
|
||||
|
@ -1860,8 +1863,8 @@ ActionLut::Lut ActionLut::m_actionLut =
|
|||
{ 16534, { 0, 0, 0, 300, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
|
||||
//Temperance, テンパランス
|
||||
//applies to targets: Temperance, テンパランス:効果, duration 20000, param 0
|
||||
{ 16536, { 0, 0, 0, 0, 0, 0, 0, 1873, 20000, 0, 0, 0, 0 } },
|
||||
//applies to targets: Temperance, テンパランス, duration 20000, param 0
|
||||
{ 16536, { 0, 0, 0, 0, 0, 0, 0, 1872, 20000, 0, 0, 0, 0 } },
|
||||
|
||||
//Ruin, ルイン
|
||||
//has damage: potency 160, combo potency 0, directional potency 0
|
||||
|
@ -3557,4 +3560,7 @@ ActionLut::StatusEffectTable ActionLut::m_statusEffectTable =
|
|||
|
||||
//Walking Dead, ウォーキングデッド: CannotDie
|
||||
{ 811, { 19, 0, 0, 0, 0 } },
|
||||
|
||||
//Divine Benison, ディヴァインベニゾン (scripted)
|
||||
{ 1218, { 0, 0, 0, 0, 0 } },
|
||||
};
|
||||
|
|
|
@ -99,17 +99,17 @@ void EffectBuilder::comboSucceed( Entity::CharaPtr& target )
|
|||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs, bool statusToSource )
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs, bool statusToSource, bool shouldReuse )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, source, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->applyStatusEffect( statusId, duration, param, statusToSource );
|
||||
nextResult->applyStatusEffect( statusId, duration, param, statusToSource, shouldReuse );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect, uint64_t resultDelayMs, bool statusToSource )
|
||||
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect, uint64_t resultDelayMs, bool statusToSource, bool shouldReuse )
|
||||
{
|
||||
EffectResultPtr nextResult = make_EffectResult( target, source, Common::Util::getTimeMs() + resultDelayMs );
|
||||
nextResult->applyStatusEffect( pStatusEffect, statusToSource );
|
||||
nextResult->applyStatusEffect( pStatusEffect, statusToSource, shouldReuse );
|
||||
moveToResultList( target, nextResult );
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ namespace Sapphire::World::Action
|
|||
|
||||
void comboSucceed( Entity::CharaPtr& target );
|
||||
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs = 500, bool statusToSource = false );
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect, uint64_t resultDelayMs = 500, bool statusToSource = false );
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint16_t param, uint64_t resultDelayMs = 500, bool statusToSource = false, bool shouldReuse = true );
|
||||
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, StatusEffect::StatusEffectPtr pStatusEffect, uint64_t resultDelayMs = 500, bool statusToSource = false, bool shouldReuse = true );
|
||||
|
||||
void statusNoEffect( Entity::CharaPtr& target, uint16_t statusId );
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ EffectResult::EffectResult( Entity::CharaPtr target, Entity::CharaPtr source, ui
|
|||
m_param1( 0 ),
|
||||
m_param2( 0 ),
|
||||
m_flag( Common::ActionEffectResultFlag::None ),
|
||||
m_pPreBuiltStatusEffect( nullptr )
|
||||
m_pPreBuiltStatusEffect( nullptr ),
|
||||
m_statusShouldReuse( true )
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -123,23 +124,23 @@ void EffectResult::comboSucceed()
|
|||
m_type = Common::ActionEffectType::ComboSucceed;
|
||||
}
|
||||
|
||||
void EffectResult::applyStatusEffect( uint16_t statusId, uint32_t duration, uint16_t param, bool statusToSource )
|
||||
void EffectResult::applyStatusEffect( uint16_t statusId, uint32_t duration, uint16_t param, bool statusToSource, bool shouldReuse )
|
||||
{
|
||||
m_value = statusId;
|
||||
m_statusDuration = duration;
|
||||
m_param2 = param;
|
||||
m_flag = statusToSource ? Common::ActionEffectResultFlag::EffectOnSource : Common::ActionEffectResultFlag::None;
|
||||
|
||||
m_statusShouldReuse = shouldReuse;
|
||||
m_type = statusToSource ? Common::ActionEffectType::ApplyStatusEffectSource : Common::ActionEffectType::ApplyStatusEffectTarget;
|
||||
}
|
||||
|
||||
void EffectResult::applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect, bool statusToSource )
|
||||
void EffectResult::applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect, bool statusToSource, bool shouldReuse )
|
||||
{
|
||||
m_value = pStatusEffect->getId();
|
||||
m_param2 = pStatusEffect->getParam();
|
||||
m_pPreBuiltStatusEffect = std::move( pStatusEffect );
|
||||
m_flag = statusToSource ? Common::ActionEffectResultFlag::EffectOnSource : Common::ActionEffectResultFlag::None;
|
||||
|
||||
m_statusShouldReuse = shouldReuse;
|
||||
m_type = statusToSource ? Common::ActionEffectType::ApplyStatusEffectSource : Common::ActionEffectType::ApplyStatusEffectTarget;
|
||||
}
|
||||
|
||||
|
@ -224,7 +225,7 @@ void EffectResult::execute()
|
|||
for( auto const& entry : applyTarget->getStatusEffectMap() )
|
||||
{
|
||||
auto statusEffect = entry.second;
|
||||
if( statusEffect->getId() == m_value && statusEffect->getSrcActorId() == m_source->getId() )
|
||||
if( statusEffect->getId() == m_value && m_statusShouldReuse )
|
||||
{
|
||||
if( m_pPreBuiltStatusEffect )
|
||||
{
|
||||
|
@ -232,7 +233,7 @@ void EffectResult::execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
statusEffect->refresh();
|
||||
statusEffect->refresh( m_statusDuration );
|
||||
}
|
||||
applyTarget->sendStatusEffectUpdate();
|
||||
return;
|
||||
|
|
|
@ -26,8 +26,8 @@ namespace Sapphire::World::Action
|
|||
void restoreMP( uint32_t amount, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
|
||||
void startCombo( uint16_t actionId );
|
||||
void comboSucceed();
|
||||
void applyStatusEffect( uint16_t statusId, uint32_t duration, uint16_t param, bool statusToSource = false );
|
||||
void applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect, bool statusToSource = false );
|
||||
void applyStatusEffect( uint16_t statusId, uint32_t duration, uint16_t param, bool statusToSource = false, bool shouldReuse = true );
|
||||
void applyStatusEffect( StatusEffect::StatusEffectPtr pStatusEffect, bool statusToSource = false, bool shouldReuse = true );
|
||||
void statusNoEffect( uint16_t statusId );
|
||||
void mount( uint16_t mountId );
|
||||
void provoke();
|
||||
|
@ -61,6 +61,7 @@ namespace Sapphire::World::Action
|
|||
Common::ActionEffectResultFlag m_flag;
|
||||
|
||||
StatusEffect::StatusEffectPtr m_pPreBuiltStatusEffect;
|
||||
bool m_statusShouldReuse;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -67,4 +67,6 @@ void EventItemAction::execute()
|
|||
void EventItemAction::start()
|
||||
{
|
||||
m_startTime = Common::Util::getTimeMs();
|
||||
if( !hasCastTime() )
|
||||
execute();
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX
|
|||
m_pCurrentTerritory = std::move( pZone );
|
||||
|
||||
m_spawnPos = m_pos;
|
||||
m_isMoving = false;
|
||||
|
||||
m_timeOfDeath = 0;
|
||||
m_targetId = Common::INVALID_GAME_OBJECT_ID64;
|
||||
|
@ -116,9 +117,7 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX
|
|||
m_radius *= modelSkeleton->radius;
|
||||
}
|
||||
|
||||
// todo: is this actually good?
|
||||
//m_naviTargetReachedDistance = m_scale * 2.f;
|
||||
m_naviTargetReachedDistance = 4.f;
|
||||
m_naviTargetReachedDistance = m_radius;
|
||||
|
||||
calculateStats();
|
||||
auto& scriptMgr = Common::Service< Sapphire::Scripting::ScriptMgr >::ref();
|
||||
|
@ -194,7 +193,7 @@ void Sapphire::Entity::BNpc::setState( BNpcState state )
|
|||
m_state = state;
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos )
|
||||
bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos, float radius )
|
||||
{
|
||||
|
||||
auto pNaviProvider = m_pCurrentTerritory->getNaviProvider();
|
||||
|
@ -208,14 +207,15 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos )
|
|||
}
|
||||
|
||||
auto pos1 = pNaviProvider->getMovePos( *this );
|
||||
|
||||
if( Util::distance( pos1, pos ) < getRadius() + 3.f )
|
||||
|
||||
if( Util::distance( pos1, pos ) < getNaviTargetReachedDistance() + radius )
|
||||
{
|
||||
// Reached destination
|
||||
face( pos );
|
||||
setPos( pos1 );
|
||||
sendPositionUpdate();
|
||||
pNaviProvider->updateAgentPosition( *this );
|
||||
m_isMoving = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -223,59 +223,38 @@ bool Sapphire::Entity::BNpc::moveTo( const FFXIVARR_POSITION3& pos )
|
|||
face( pos );
|
||||
setPos( pos1 );
|
||||
sendPositionUpdate();
|
||||
m_isMoving = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Sapphire::Entity::BNpc::moveTo( const Entity::Chara& targetChara )
|
||||
{
|
||||
|
||||
auto pNaviProvider = m_pCurrentTerritory->getNaviProvider();
|
||||
|
||||
if( !pNaviProvider )
|
||||
{
|
||||
Logger::error( "No NaviProvider for zone#{0} - {1}",
|
||||
m_pCurrentTerritory->getGuId(),
|
||||
m_pCurrentTerritory->getInternalName() );
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pos1 = pNaviProvider->getMovePos( *this );
|
||||
|
||||
if( Util::distance( pos1, targetChara.getPos() ) <= ( getRadius() + targetChara.getRadius() ) + 3.f )
|
||||
{
|
||||
// Reached destination
|
||||
face( targetChara.getPos() );
|
||||
setPos( pos1 );
|
||||
sendPositionUpdate();
|
||||
pNaviProvider->updateAgentPosition( *this );
|
||||
return true;
|
||||
}
|
||||
|
||||
m_pCurrentTerritory->updateActorPosition( *this );
|
||||
face( targetChara.getPos() );
|
||||
setPos( pos1 );
|
||||
sendPositionUpdate();
|
||||
return false;
|
||||
return moveTo( targetChara.getPos(), targetChara.getRadius() );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::BNpc::stopMoving()
|
||||
{
|
||||
auto pNaviProvider = m_pCurrentTerritory->getNaviProvider();
|
||||
if( !pNaviProvider )
|
||||
if( !pNaviProvider || !m_isMoving )
|
||||
return;
|
||||
sendPositionUpdate();
|
||||
pNaviProvider->updateAgentPosition( *this );
|
||||
m_isMoving = false;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::BNpc::sendPositionUpdate()
|
||||
{
|
||||
uint8_t unk1 = 0x3a;
|
||||
uint8_t animationType = 2;
|
||||
uint16_t moveSpeed = MoveSpeed::Walk;
|
||||
|
||||
if( m_state == BNpcState::Combat || m_state == BNpcState::Retreat )
|
||||
{
|
||||
animationType = 0;
|
||||
moveSpeed = MoveSpeed::Run;
|
||||
}
|
||||
|
||||
auto movePacket = std::make_shared< MoveActorPacket >( *getAsChara(), unk1, animationType, 0, 0x5A );
|
||||
auto movePacket = std::make_shared< MoveActorPacket >( *getAsChara(), unk1, animationType, 0, moveSpeed );
|
||||
sendToInRangeSet( movePacket );
|
||||
}
|
||||
|
||||
|
@ -329,7 +308,7 @@ void Sapphire::Entity::BNpc::hateListAddOrUpdate( Sapphire::Entity::CharaPtr pCh
|
|||
if( pChara->isPlayer() )
|
||||
{
|
||||
auto pPlayer = pChara->getAsPlayer();
|
||||
pPlayer->hateListAdd( getAsBNpc() );
|
||||
pPlayer->onMobAggro( getAsBNpc() );
|
||||
}
|
||||
auto& scriptMgr = Common::Service< Sapphire::Scripting::ScriptMgr >::ref();
|
||||
scriptMgr.onBNpcHateListAdd( *this, *pChara );
|
||||
|
@ -496,6 +475,7 @@ void Sapphire::Entity::BNpc::doDefaultBNpcUpdate( uint64_t tickCount )
|
|||
|
||||
m_roamPos = pNaviProvider->findRandomPositionInCircle( m_spawnPos, 5 );
|
||||
m_state = BNpcState::Roaming;
|
||||
pNaviProvider->updateAgentParameters( *this );
|
||||
}
|
||||
|
||||
checkAggro();
|
||||
|
@ -527,9 +507,6 @@ void Sapphire::Entity::BNpc::doDefaultBNpcUpdate( uint64_t tickCount )
|
|||
return;
|
||||
}
|
||||
|
||||
if( pNaviProvider->syncPosToChara( *this ) )
|
||||
sendPositionUpdate();
|
||||
|
||||
auto distance = Util::distance( getPos().x, getPos().y, getPos().z,
|
||||
pHatedActor->getPos().x, pHatedActor->getPos().y, pHatedActor->getPos().z );
|
||||
|
||||
|
@ -543,7 +520,7 @@ void Sapphire::Entity::BNpc::doDefaultBNpcUpdate( uint64_t tickCount )
|
|||
break;
|
||||
}
|
||||
|
||||
if( distance > ( getRadius() + pHatedActor->getRadius() ) )
|
||||
if( distance > ( getNaviTargetReachedDistance() + pHatedActor->getRadius() ) )
|
||||
{
|
||||
if( hasFlag( Immobile ) )
|
||||
break;
|
||||
|
@ -556,6 +533,8 @@ void Sapphire::Entity::BNpc::doDefaultBNpcUpdate( uint64_t tickCount )
|
|||
|
||||
if( distance < ( getRadius() + pHatedActor->getRadius() + 3.f ) )
|
||||
{
|
||||
stopMoving();
|
||||
|
||||
if( !hasFlag( TurningDisabled ) && face( pHatedActor->getPos() ) )
|
||||
sendPositionUpdate();
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace Sapphire::Entity
|
|||
float getNaviTargetReachedDistance() const;
|
||||
|
||||
// return true if it reached the position
|
||||
bool moveTo( const Common::FFXIVARR_POSITION3& pos );
|
||||
bool moveTo( const Common::FFXIVARR_POSITION3& pos, float radius = 0 );
|
||||
|
||||
bool moveTo( const Entity::Chara& targetChara );
|
||||
|
||||
|
@ -149,6 +149,7 @@ namespace Sapphire::Entity
|
|||
|
||||
Common::FFXIVARR_POSITION3 m_spawnPos;
|
||||
Common::FFXIVARR_POSITION3 m_roamPos;
|
||||
bool m_isMoving;
|
||||
|
||||
BNpcState m_state;
|
||||
std::set< std::shared_ptr< HateListEntry > > m_hateList;
|
||||
|
|
|
@ -1056,7 +1056,7 @@ void Sapphire::Entity::Chara::onTick()
|
|||
}
|
||||
}
|
||||
|
||||
if( thisTickDmg != 0 )
|
||||
if( thisTickDmg != 0 && isAlive() )
|
||||
{
|
||||
thisTickDmg = applyShieldProtection( thisTickDmg );
|
||||
if( thisTickDmg > 0 )
|
||||
|
@ -1065,7 +1065,7 @@ void Sapphire::Entity::Chara::onTick()
|
|||
static_cast< uint8_t >( ActionEffectType::Damage ), thisTickDmg ), true );
|
||||
}
|
||||
|
||||
if( thisTickHeal != 0 )
|
||||
if( thisTickHeal != 0 && isAlive() )
|
||||
{
|
||||
heal( thisTickHeal );
|
||||
sendToInRangeSet( makeActorControl( getId(), HPFloatingText, 0,
|
||||
|
|
|
@ -87,6 +87,7 @@ Sapphire::Entity::Player::Player() :
|
|||
m_onEnterEventDone( false ),
|
||||
m_falling( false ),
|
||||
m_pQueuedAction( nullptr ),
|
||||
m_partyId( 0 ),
|
||||
m_cfNotifiedContent( 0 )
|
||||
{
|
||||
m_id = 0;
|
||||
|
@ -136,7 +137,8 @@ uint32_t Sapphire::Entity::Player::getMaxHp()
|
|||
|
||||
uint32_t Sapphire::Entity::Player::getMaxMp()
|
||||
{
|
||||
return m_baseStats.max_mp;
|
||||
//return m_baseStats.max_mp;
|
||||
return 10000;
|
||||
}
|
||||
|
||||
uint16_t Sapphire::Entity::Player::getZoneId() const
|
||||
|
@ -249,6 +251,47 @@ uint64_t Sapphire::Entity::Player::getOnlineStatusMask() const
|
|||
return m_onlineStatus;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::addOnlineStatus( OnlineStatus status )
|
||||
{
|
||||
uint64_t statusValue = 1ull << static_cast< uint8_t >( status );
|
||||
uint64_t newFlags = getOnlineStatusMask() | statusValue;
|
||||
|
||||
setOnlineStatusMask( newFlags );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::addOnlineStatus( const std::vector< Common::OnlineStatus >& status )
|
||||
{
|
||||
uint64_t newFlags = getOnlineStatusMask();
|
||||
for( const auto& state : status )
|
||||
{
|
||||
uint64_t statusValue = 1ull << static_cast< uint8_t >( state );
|
||||
newFlags |= statusValue;
|
||||
}
|
||||
|
||||
setOnlineStatusMask( newFlags );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::removeOnlineStatus( OnlineStatus status )
|
||||
{
|
||||
uint64_t statusValue = 1ull << static_cast< uint8_t >( status );
|
||||
uint64_t newFlags = getOnlineStatusMask();
|
||||
newFlags &= ~statusValue;
|
||||
|
||||
setOnlineStatusMask( newFlags );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::removeOnlineStatus( const std::vector< Common::OnlineStatus >& status )
|
||||
{
|
||||
uint64_t newFlags = getOnlineStatusMask();
|
||||
for( const auto& state : status )
|
||||
{
|
||||
uint64_t statusValue = 1ull << static_cast< uint8_t >( state );
|
||||
newFlags &= ~statusValue;
|
||||
}
|
||||
|
||||
setOnlineStatusMask( newFlags );
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadeOutTime, uint16_t animation, uint8_t param4, uint8_t param7, uint8_t unknown )
|
||||
{
|
||||
auto preparePacket = makeZonePacket< FFXIVIpcPrepareZoning >( getId() );
|
||||
|
@ -1201,13 +1244,13 @@ void Sapphire::Entity::Player::update( uint64_t tickCount )
|
|||
auto chara = actor->getAsChara();
|
||||
|
||||
// default autoattack range
|
||||
float range = 3.f + chara->getRadius();
|
||||
float range = 3.f + chara->getRadius() + getRadius() * 0.5f;
|
||||
|
||||
// default autoattack range for ranged classes
|
||||
if( getClass() == ClassJob::Machinist ||
|
||||
getClass() == ClassJob::Bard ||
|
||||
getClass() == ClassJob::Archer )
|
||||
range = 25;
|
||||
range = 25.f + chara->getRadius() + getRadius() * 0.5f;
|
||||
|
||||
|
||||
if( Util::distance( getPos().x, getPos().y, getPos().z,
|
||||
|
@ -1434,6 +1477,14 @@ void Sapphire::Entity::Player::queuePacket( Network::Packets::FFXIVPacketBasePtr
|
|||
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::queuePacket( std::vector< Network::Packets::FFXIVPacketBasePtr > packets )
|
||||
{
|
||||
for( auto& packet : packets )
|
||||
{
|
||||
queuePacket( packet );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::queueChatPacket( Network::Packets::FFXIVPacketBasePtr pPacket )
|
||||
{
|
||||
auto& serverMgr = Common::Service< World::ServerMgr >::ref();
|
||||
|
@ -1606,14 +1657,17 @@ void Sapphire::Entity::Player::sendHateList()
|
|||
void Sapphire::Entity::Player::onMobAggro( BNpcPtr pBNpc )
|
||||
{
|
||||
hateListAdd( pBNpc );
|
||||
queuePacket( makeActorControl( getId(), ToggleAggro, 1 ) );
|
||||
setStateFlag( Common::PlayerStateFlag::InCombat );
|
||||
if( !hasStateFlag( Common::PlayerStateFlag::InCombat ) )
|
||||
{
|
||||
queuePacket( makeActorControl( getId(), ToggleAggro, 1 ) );
|
||||
setStateFlag( Common::PlayerStateFlag::InCombat );
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::onMobDeaggro( BNpcPtr pBNpc )
|
||||
{
|
||||
hateListRemove( pBNpc );
|
||||
if( m_actorIdTohateSlotMap.empty() )
|
||||
if( m_actorIdTohateSlotMap.empty() && hasStateFlag( Common::PlayerStateFlag::InCombat ) )
|
||||
{
|
||||
queuePacket( makeActorControl( getId(), ToggleAggro ) );
|
||||
unsetStateFlag( Common::PlayerStateFlag::InCombat );
|
||||
|
@ -2401,6 +2455,16 @@ bool Sapphire::Entity::Player::checkAction()
|
|||
return true;
|
||||
}
|
||||
|
||||
uint64_t Sapphire::Entity::Player::getPartyId() const
|
||||
{
|
||||
return m_partyId;
|
||||
}
|
||||
|
||||
void Sapphire::Entity::Player::setPartyId( uint64_t partyId )
|
||||
{
|
||||
m_partyId = partyId;
|
||||
}
|
||||
|
||||
std::vector< Sapphire::Entity::ShopBuyBackEntry >& Sapphire::Entity::Player::getBuyBackListForShop( uint32_t shopId )
|
||||
{
|
||||
return m_shopBuyBackMap[ shopId ];
|
||||
|
|
|
@ -543,6 +543,11 @@ namespace Sapphire::Entity
|
|||
/*! returns the current online status */
|
||||
uint64_t getOnlineStatusMask() const;
|
||||
|
||||
void addOnlineStatus( Common::OnlineStatus status );
|
||||
void addOnlineStatus( const std::vector< Common::OnlineStatus >& status );
|
||||
void removeOnlineStatus( Common::OnlineStatus status );
|
||||
void removeOnlineStatus( const std::vector< Common::OnlineStatus >& status );
|
||||
|
||||
/*! perform a teleport of a specified type ( teleport,return,aethernet ) */
|
||||
void teleport( uint16_t aetheryteId, uint8_t type = 1 );
|
||||
|
||||
|
@ -769,6 +774,7 @@ namespace Sapphire::Entity
|
|||
|
||||
/*! queue a packet for the player */
|
||||
void queuePacket( Network::Packets::FFXIVPacketBasePtr pPacket );
|
||||
void queuePacket( std::vector< Network::Packets::FFXIVPacketBasePtr > packets );
|
||||
|
||||
/*! queue a char connection packet for the player */
|
||||
void queueChatPacket( Network::Packets::FFXIVPacketBasePtr pPacket );
|
||||
|
@ -1070,6 +1076,9 @@ namespace Sapphire::Entity
|
|||
|
||||
void updateHuntingLog( uint16_t id );
|
||||
|
||||
uint64_t getPartyId() const;
|
||||
void setPartyId( uint64_t partyId );
|
||||
|
||||
World::SessionPtr getSession();
|
||||
|
||||
uint64_t m_lastMoveTime;
|
||||
|
@ -1223,6 +1232,8 @@ namespace Sapphire::Entity
|
|||
Common::Util::SpawnIndexAllocator< uint8_t > m_objSpawnIndexAllocator;
|
||||
Common::Util::SpawnIndexAllocator< uint8_t > m_actorSpawnIndexAllocator;
|
||||
|
||||
uint64_t m_partyId;
|
||||
|
||||
std::array< Common::HuntingLogEntry, 12 > m_huntingLogEntries;
|
||||
std::unordered_map< uint32_t, std::vector< ShopBuyBackEntry > > m_shopBuyBackMap;
|
||||
};
|
||||
|
|
144
src/world/Manager/ChatChannelMgr.cpp
Normal file
144
src/world/Manager/ChatChannelMgr.cpp
Normal file
|
@ -0,0 +1,144 @@
|
|||
#include <Network/PacketWrappers/ChannelChatPacket.h>
|
||||
#include <Logging/Logger.h>
|
||||
#include <Service.h>
|
||||
|
||||
#include "ChatChannelMgr.h"
|
||||
|
||||
#include "Actor/Player.h"
|
||||
#include "ServerMgr.h"
|
||||
#include "Session.h"
|
||||
#include "Network/GameConnection.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::Network;
|
||||
using namespace Sapphire::Network::Packets;
|
||||
using namespace Sapphire::World::Manager;
|
||||
|
||||
const uint64_t ChatChannelMgr::createChatChannel( Common::ChatChannelType type )
|
||||
{
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
|
||||
// get next id for new channel
|
||||
|
||||
uint32_t cNo = m_lastChatNo;
|
||||
|
||||
m_lastChatNo++;
|
||||
|
||||
uint16_t chatType = static_cast< uint16_t >( type );
|
||||
uint16_t worldId = server.getWorldId();
|
||||
|
||||
Data::ChatChannel cId;
|
||||
|
||||
cId.data.ChannelNo = cNo;
|
||||
cId.data.ChannelType = type;
|
||||
cId.data.WorldId = worldId;
|
||||
|
||||
// create our new chat channel
|
||||
|
||||
Data::ChatChannelMembers newChatChannel = {};
|
||||
|
||||
m_channels[ cId.ChannelID ] = newChatChannel;
|
||||
|
||||
Logger::debug( "Chat channel ID "
|
||||
+ std::to_string( cId.ChannelID )
|
||||
+ " created"
|
||||
);
|
||||
|
||||
return cId.ChannelID;
|
||||
}
|
||||
|
||||
void ChatChannelMgr::addToChannel( uint64_t channelId, Entity::Player& player )
|
||||
{
|
||||
if( !isChannelValid( channelId ) )
|
||||
{
|
||||
// channel id is invalid
|
||||
|
||||
Logger::warn( "Attempted to add player "
|
||||
+ std::to_string( player.getId() )
|
||||
+ " to invalid channel ID "
|
||||
+ std::to_string( channelId )
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto& channelMembers = m_channels[ channelId ];
|
||||
auto id = player.getId();
|
||||
|
||||
if( std::find( channelMembers.begin(), channelMembers.end(), id ) == channelMembers.end() )
|
||||
m_channels[ channelId ].emplace_back( id );
|
||||
}
|
||||
|
||||
void ChatChannelMgr::removeFromChannel( uint64_t channelId, Entity::Player& player )
|
||||
{
|
||||
if( !isChannelValid( channelId ) )
|
||||
{
|
||||
// channel id is invalid
|
||||
|
||||
Logger::warn( "Attempted to remove player "
|
||||
+ std::to_string( player.getId() )
|
||||
+ " from invalid channel ID "
|
||||
+ std::to_string( channelId )
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto& channelMembers = m_channels[ channelId ];
|
||||
auto id = player.getId();
|
||||
|
||||
auto it = std::find( channelMembers.begin(), channelMembers.end(), id );
|
||||
if( it != channelMembers.end() )
|
||||
channelMembers.erase( it );
|
||||
}
|
||||
|
||||
void ChatChannelMgr::sendMessageToChannel( uint64_t channelId, Entity::Player& sender, const std::string& message )
|
||||
{
|
||||
if( !isChannelValid( channelId ) )
|
||||
{
|
||||
// channel id is invalid
|
||||
|
||||
Logger::warn( "Attempted to send message from player "
|
||||
+ std::to_string( sender.getId() )
|
||||
+ " to invalid channel ID "
|
||||
+ std::to_string( channelId )
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto& channelMembers = m_channels[ channelId ];
|
||||
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
|
||||
// send message to all players in chat channel
|
||||
for( const auto id : channelMembers )
|
||||
{
|
||||
// skip sender from getting their own message
|
||||
if( id == sender.getId() )
|
||||
continue;
|
||||
|
||||
auto pPlayer = server.getSession( id )->getPlayer();
|
||||
|
||||
// check if player is online to recv message
|
||||
if( !pPlayer/*->isConnected()*/ )
|
||||
continue;
|
||||
|
||||
// prepare message packet, associating message and sender info with channel data
|
||||
auto chatToChannelPacket = std::make_shared< Packets::Server::ChannelChatPacket >( *pPlayer, sender, channelId, message );
|
||||
pPlayer->queueChatPacket( chatToChannelPacket );
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatChannelMgr::isChannelValid( uint64_t channelId ) const
|
||||
{
|
||||
return !( m_channels.find( channelId ) == m_channels.end() );
|
||||
}
|
||||
|
||||
const Data::ChatChannelMembers& ChatChannelMgr::getChatChannel( uint64_t channelId )
|
||||
{
|
||||
bool channelValid = isChannelValid( channelId );
|
||||
assert( channelValid );
|
||||
|
||||
return m_channels[ channelId ];
|
||||
}
|
45
src/world/Manager/ChatChannelMgr.h
Normal file
45
src/world/Manager/ChatChannelMgr.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "ForwardsZone.h"
|
||||
|
||||
namespace Sapphire::Data
|
||||
{
|
||||
using ChatChannelMembers = std::vector< uint32_t >;
|
||||
|
||||
union ChatChannel
|
||||
{
|
||||
uint64_t ChannelID;
|
||||
|
||||
struct ChannelData {
|
||||
uint32_t ChannelNo;
|
||||
uint16_t ChannelType;
|
||||
uint16_t WorldId;
|
||||
} data;
|
||||
};
|
||||
}
|
||||
|
||||
namespace Sapphire::World::Manager
|
||||
{
|
||||
class ChatChannelMgr
|
||||
{
|
||||
public:
|
||||
ChatChannelMgr() = default;
|
||||
~ChatChannelMgr() = default;
|
||||
|
||||
const uint64_t createChatChannel( Common::ChatChannelType type );
|
||||
|
||||
void addToChannel( uint64_t channelId, Entity::Player& player );
|
||||
void removeFromChannel( uint64_t channelId, Entity::Player& player );
|
||||
|
||||
void sendMessageToChannel( uint64_t channelId, Entity::Player& sender, const std::string& message );
|
||||
|
||||
bool isChannelValid( uint64_t channelId ) const;
|
||||
const Data::ChatChannelMembers& getChatChannel( uint64_t channelId );
|
||||
|
||||
private:
|
||||
std::map< uint64_t, Data::ChatChannelMembers > m_channels;
|
||||
uint32_t m_lastChatNo = 0x1000;
|
||||
};
|
||||
}
|
436
src/world/Manager/PartyMgr.cpp
Normal file
436
src/world/Manager/PartyMgr.cpp
Normal file
|
@ -0,0 +1,436 @@
|
|||
#include <Common.h>
|
||||
#include <Exd/ExdDataGenerated.h>
|
||||
#include <Logging/Logger.h>
|
||||
#include <Service.h>
|
||||
|
||||
#include <Network/PacketDef/Zone/ServerZoneDef.h>
|
||||
#include <Network/PacketContainer.h>
|
||||
|
||||
#include "Network/GameConnection.h"
|
||||
|
||||
#include "PartyMgr.h"
|
||||
#include "ServerMgr.h"
|
||||
#include "ChatChannelMgr.h"
|
||||
#include "PlayerMgr.h"
|
||||
|
||||
#include "Session.h"
|
||||
|
||||
#include "Actor/Player.h"
|
||||
|
||||
#include "Network/PacketWrappers/PartyUpdatePacket.h"
|
||||
|
||||
using namespace Sapphire;
|
||||
using namespace Sapphire::World::Manager;
|
||||
using namespace Sapphire::Network::Packets;
|
||||
using namespace Sapphire::Network::Packets::Server;
|
||||
|
||||
void PartyMgr::onJoin( Entity::Player& joiner, Entity::Player& inviter )
|
||||
{
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
auto& ccMgr = Common::Service< World::Manager::ChatChannelMgr >::ref();
|
||||
|
||||
auto& inviteePlayer = joiner;
|
||||
auto& invitingPlayer = inviter;
|
||||
|
||||
if( inviteePlayer.getPartyId() != 0 )
|
||||
{
|
||||
Logger::error( "Player#{} already in a party, cannot be invited!!", inviteePlayer.getId() );
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t partyId;
|
||||
// if there is no party yet, one has to be created
|
||||
PartyPtr party;
|
||||
if( inviteePlayer.getPartyId() == 0 && invitingPlayer.getPartyId() == 0 )
|
||||
{
|
||||
partyId = createParty();
|
||||
party = getParty( partyId );
|
||||
assert( party );
|
||||
|
||||
inviteePlayer.setPartyId( partyId );
|
||||
inviteePlayer.addOnlineStatus( Common::OnlineStatus::PartyMember );
|
||||
invitingPlayer.setPartyId( partyId );
|
||||
invitingPlayer.addOnlineStatus( Common::OnlineStatus::PartyLeader );
|
||||
|
||||
ccMgr.addToChannel( party->ChatChannel, invitingPlayer );
|
||||
ccMgr.addToChannel( party->ChatChannel, inviteePlayer );
|
||||
|
||||
party->MemberId.push_back( invitingPlayer.getId() );
|
||||
party->MemberId.push_back( inviteePlayer.getId() );
|
||||
party->PartyCount = 2;
|
||||
party->LeaderId = invitingPlayer.getId();
|
||||
}
|
||||
else if( inviteePlayer.getPartyId() == 0 )
|
||||
{
|
||||
partyId = invitingPlayer.getPartyId();
|
||||
party = getParty( partyId );
|
||||
|
||||
inviteePlayer.setPartyId( partyId );
|
||||
inviteePlayer.addOnlineStatus( Common::OnlineStatus::PartyMember );
|
||||
|
||||
ccMgr.addToChannel( party->ChatChannel, inviteePlayer );
|
||||
|
||||
party->MemberId.push_back( inviteePlayer.getId() );
|
||||
party->PartyCount++;
|
||||
}
|
||||
|
||||
auto pcUpdateParty = makePartyUpdate( invitingPlayer, inviteePlayer, UpdateStatus::JOINED, party->PartyCount );
|
||||
auto members = getPartyMembers( *party );
|
||||
sendPartyUpdate( *party );
|
||||
for( const auto& member : members )
|
||||
{
|
||||
member->queuePacket( pcUpdateParty );
|
||||
}
|
||||
}
|
||||
|
||||
void PartyMgr::onLeave( Sapphire::Entity::Player &leavingPlayer )
|
||||
{
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
|
||||
auto party = getParty( leavingPlayer.getPartyId() );
|
||||
assert( party );
|
||||
|
||||
auto leadingPlayer = getPartyLeader( *party );
|
||||
assert( leadingPlayer );
|
||||
|
||||
if( !leadingPlayer )
|
||||
return;
|
||||
|
||||
if( party->PartyCount == 2 )
|
||||
{
|
||||
onDisband( *leadingPlayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
auto members = getPartyMembers( *party );
|
||||
removeMember( *party, leavingPlayer.getAsPlayer() );
|
||||
|
||||
uint32_t newLeaderId = 0;
|
||||
for( const auto& member : members )
|
||||
{
|
||||
if( member->getId() == leavingPlayer.getId() )
|
||||
{
|
||||
member->removeOnlineStatus( { Common::OnlineStatus::PartyMember,
|
||||
Common::OnlineStatus::PartyLeader } );
|
||||
|
||||
leavingPlayer.queuePacket( makeZonePacket< FFXIVIpcPartyList >( leavingPlayer.getId() ) );
|
||||
member->queuePacket( makePartyUpdate( leadingPlayer, nullptr, UpdateStatus::KICK_SELF, party->PartyCount ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( leavingPlayer.getId() == party->LeaderId )
|
||||
{
|
||||
newLeaderId = party->MemberId[ 0 ];
|
||||
auto pPlayer = server.getSession( newLeaderId )->getPlayer();
|
||||
if( !pPlayer /*|| !pPlayer->isConnected() */)
|
||||
continue;
|
||||
pPlayer->addOnlineStatus( Common::OnlineStatus::PartyLeader );
|
||||
member->queuePacket( makePartyUpdate( leavingPlayer.getAsPlayer(), pPlayer, UpdateStatus::LEAVELEADER_LEAVED_MEMBER, party->PartyCount ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
member->queuePacket( makePartyUpdate( leavingPlayer.getAsPlayer(), nullptr, UpdateStatus::LEAVE_MEMBER, party->PartyCount ) );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
if( newLeaderId != 0 )
|
||||
party->LeaderId = newLeaderId;
|
||||
party->PartyCount--;
|
||||
sendPartyUpdate( *party );
|
||||
}
|
||||
}
|
||||
|
||||
void PartyMgr::onDisband( Entity::Player& disbandingPlayer )
|
||||
{
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
auto party = getParty( disbandingPlayer.getPartyId() );
|
||||
assert( party );
|
||||
|
||||
auto members = getPartyMembers( *party );
|
||||
for( const auto& member : members )
|
||||
{
|
||||
removeMember( *party, member );
|
||||
member->removeOnlineStatus( { Common::OnlineStatus::PartyMember, Common::OnlineStatus::PartyLeader } );
|
||||
member->queuePacket( { makePartyUpdate( disbandingPlayer, disbandingPlayer, UpdateStatus::DISBAND, party->PartyCount ), makeZonePacket< FFXIVIpcPartyList >( member->getId() ) } );
|
||||
}
|
||||
|
||||
removeParty( party->PartyID );
|
||||
}
|
||||
|
||||
void PartyMgr::onMoveZone( Sapphire::Entity::Player &movingPlayer )
|
||||
{
|
||||
if( movingPlayer.getPartyId() == 0 )
|
||||
return;
|
||||
auto party = getParty( movingPlayer.getPartyId() );
|
||||
assert( party );
|
||||
sendPartyUpdate( *party );
|
||||
}
|
||||
|
||||
void PartyMgr::onMemberDisconnect( Entity::Player& disconnectingPlayer )
|
||||
{
|
||||
if( disconnectingPlayer.getPartyId() == 0 )
|
||||
return;
|
||||
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
auto party = getParty( disconnectingPlayer.getPartyId() );
|
||||
assert( party );
|
||||
auto members = getPartyMembers( *party );
|
||||
auto pLeader = getPartyLeader( *party );
|
||||
|
||||
bool anyMembersOnline = false;
|
||||
|
||||
for( const auto& member : members )
|
||||
{
|
||||
if( member/*->isConnected()*/ )
|
||||
{
|
||||
anyMembersOnline = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if there are no party members online, destroy the party
|
||||
if( !anyMembersOnline )
|
||||
return onDisband( *pLeader );
|
||||
|
||||
for( const auto& member : members )
|
||||
{
|
||||
// TODO: 2nd argument here makes it automatically send passing leadership message
|
||||
member->queuePacket( { makePartyUpdate( disconnectingPlayer, UpdateStatus::OFFLINE_MEMBER, party->PartyCount ), makeZonePacket< FFXIVIpcPartyList >( member->getId() ) } );
|
||||
}
|
||||
|
||||
sendPartyUpdate( *party );
|
||||
}
|
||||
|
||||
void PartyMgr::onMemberRejoin( Entity::Player& joiningPlayer )
|
||||
{
|
||||
auto party = getParty( joiningPlayer.getPartyId() );
|
||||
assert( party );
|
||||
|
||||
// TODO: do we need a party update here? move zone handler already handles it
|
||||
}
|
||||
|
||||
void PartyMgr::onKick( const std::string& kickPlayerName, Entity::Player& leader )
|
||||
{
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
auto& playerMgr = Common::Service< World::Manager::PlayerMgr >::ref();
|
||||
auto party = getParty( leader.getPartyId() );
|
||||
assert( party );
|
||||
auto pLeader = getPartyLeader( *party );
|
||||
auto members = getPartyMembers( *party );
|
||||
auto pKickedPlayer = server.getSession( kickPlayerName )->getPlayer();
|
||||
if( !pKickedPlayer )
|
||||
{
|
||||
Logger::error( "Target player for kicking not found (\"{t}\")", kickPlayerName );
|
||||
return;
|
||||
}
|
||||
|
||||
if( party->PartyCount == 2 )
|
||||
{
|
||||
onDisband( *pLeader );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( const auto &member: members )
|
||||
{
|
||||
if( kickPlayerName == member->getName() )
|
||||
{
|
||||
removeMember( *party, member );
|
||||
member->removeOnlineStatus( Common::OnlineStatus::PartyMember );
|
||||
|
||||
member->queuePacket( { makePartyUpdate( *pLeader, *member, UpdateStatus::KICK_SELF, party->PartyCount ),
|
||||
makeZonePacket< FFXIVIpcPartyList >( member->getId() ) } );
|
||||
}
|
||||
else
|
||||
{
|
||||
member->queuePacket( makePartyUpdate( *pKickedPlayer, UpdateStatus::KICK_MEMBER, party->PartyCount ) );
|
||||
}
|
||||
}
|
||||
party->PartyCount--;
|
||||
sendPartyUpdate( *party );
|
||||
}
|
||||
}
|
||||
|
||||
void PartyMgr::onChangeLeader( const std::string& newLeaderName, Entity::Player& oldLeader )
|
||||
{
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
auto& playerMgr = Common::Service< World::Manager::PlayerMgr >::ref();
|
||||
auto party = getParty( oldLeader.getPartyId() );
|
||||
|
||||
auto pNewLeader = server.getSession( newLeaderName )->getPlayer();
|
||||
|
||||
if( !pNewLeader )
|
||||
{
|
||||
Logger::error( "Target player for new leader not found (\"{t}\")", newLeaderName );
|
||||
return;
|
||||
}
|
||||
|
||||
for( auto memberId : party->MemberId )
|
||||
{
|
||||
if( memberId == pNewLeader->getId() )
|
||||
{
|
||||
pNewLeader->addOnlineStatus( Common::OnlineStatus::PartyLeader );
|
||||
// this is not ideal, probably better to have a function which can add
|
||||
// and remove at the same time so packets are only triggered once
|
||||
oldLeader.addOnlineStatus( Common::OnlineStatus::PartyMember );
|
||||
oldLeader.removeOnlineStatus( Common::OnlineStatus::PartyLeader );
|
||||
|
||||
party->LeaderId = pNewLeader->getId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto members = getPartyMembers( *party );
|
||||
for( auto& member : members )
|
||||
{
|
||||
auto pcUpdateParty = makePartyUpdate( oldLeader.getAsPlayer(), pNewLeader, UpdateStatus::CHANGELEADER, party->PartyCount );
|
||||
member->queuePacket( pcUpdateParty );
|
||||
}
|
||||
|
||||
sendPartyUpdate( *party );
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint64_t PartyMgr::createParty()
|
||||
{
|
||||
auto& chatChannelMgr = Common::Service< ChatChannelMgr >::ref();
|
||||
auto party = std::make_shared< Party >();
|
||||
party->PartyID = getNextPartyId();
|
||||
party->ChatChannel = chatChannelMgr.createChatChannel( Common::ChatChannelType::PartyChat );
|
||||
m_partyIdMap[ party->PartyID ] = party;
|
||||
return party->PartyID;
|
||||
}
|
||||
|
||||
uint64_t PartyMgr::getNextPartyId()
|
||||
{
|
||||
return ++m_maxPartyId;
|
||||
}
|
||||
|
||||
PartyPtr PartyMgr::getParty( uint64_t partyId )
|
||||
{
|
||||
auto it = m_partyIdMap.find( partyId );
|
||||
if( it != m_partyIdMap.end() )
|
||||
return it->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector< Entity::PlayerPtr > PartyMgr::getPartyMembers( Party& party )
|
||||
{
|
||||
std::vector< Entity::PlayerPtr > members;
|
||||
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
for( auto& memberId : party.MemberId )
|
||||
{
|
||||
if( memberId == 0 )
|
||||
continue;
|
||||
|
||||
auto pPlayer = server.getSession( memberId )->getPlayer();
|
||||
|
||||
members.push_back( pPlayer );
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
Entity::PlayerPtr PartyMgr::getPartyLeader( Party& party )
|
||||
{
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
|
||||
if( party.LeaderId == 0 )
|
||||
return nullptr;
|
||||
|
||||
auto pLeader = server.getSession( party.LeaderId )->getPlayer();
|
||||
|
||||
return pLeader;
|
||||
}
|
||||
|
||||
void PartyMgr::sendPartyUpdate( Party& party )
|
||||
{
|
||||
auto& exdData = Common::Service< Data::ExdDataGenerated >::ref();
|
||||
auto partyMembers = getPartyMembers( party );
|
||||
std::vector< PartyMember > entries;
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
|
||||
for( const auto& member : partyMembers )
|
||||
{
|
||||
auto classJob = exdData.get< Data::ClassJob >( static_cast< uint8_t >( member->getClass() ) );
|
||||
if( !classJob )
|
||||
continue;
|
||||
|
||||
PartyMember memberEntry{};
|
||||
|
||||
memberEntry./*ParentEntityId*/u1 = Common::INVALID_GAME_OBJECT_ID;
|
||||
memberEntry./*PetEntityId*/u2 = Common::INVALID_GAME_OBJECT_ID;
|
||||
memberEntry.hp = member->getHp();
|
||||
memberEntry.maxHp = member->getMaxHp();
|
||||
memberEntry.mp = member->getMp();
|
||||
memberEntry.maxMp = member->getMaxMp();
|
||||
memberEntry.classId = static_cast< uint8_t >( member->getClass() );
|
||||
memberEntry.level = member->getLevel();
|
||||
//memberEntry.ObjType = 4; // 1 PC, 2 Buddy ??
|
||||
memberEntry.zoneId = member->getTerritoryTypeId();
|
||||
memberEntry./*Valid*/gposeSelectable = 1;
|
||||
//memberEntry.Tp = member->getTp();
|
||||
//memberEntry.Role = classJob->role;
|
||||
|
||||
entries.push_back( memberEntry );
|
||||
}
|
||||
|
||||
for( const auto& pMember : partyMembers )
|
||||
{
|
||||
size_t idx = 0;
|
||||
|
||||
auto updatePartyPacket = makeZonePacket< FFXIVIpcPartyList >( partyMembers[ 0 ]->getId() );
|
||||
auto& data = updatePartyPacket->data();
|
||||
data.partyId = party.PartyID;
|
||||
data.leaderIndex = getPartyLeaderIndex( party );
|
||||
data.channelId = party.ChatChannel;
|
||||
data.partySize = party.PartyCount;
|
||||
|
||||
for( const auto& member : partyMembers )
|
||||
{
|
||||
bool isConnected = /*member->isConnected()*/true;
|
||||
// if player is online and in the same zone as current member in party, display more data in partylist
|
||||
bool hasInfo = isConnected && member->getTerritoryTypeId() == pMember->getTerritoryTypeId();
|
||||
|
||||
if( hasInfo )
|
||||
{
|
||||
data.member[ idx ] = entries[ idx ];
|
||||
}
|
||||
|
||||
data.member[ idx ].contentId = member->getContentId();
|
||||
data.member[ idx ].charaId = member->getId();
|
||||
strcpy( data.member[ idx ].name, member->getName().c_str() );
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
pMember->queuePacket( updatePartyPacket );
|
||||
}
|
||||
}
|
||||
|
||||
void PartyMgr::removeParty( uint64_t partyId )
|
||||
{
|
||||
m_partyIdMap.erase( partyId );
|
||||
}
|
||||
|
||||
int8_t PartyMgr::getPartyLeaderIndex( const Party &party )
|
||||
{
|
||||
size_t idx = 0;
|
||||
for( const auto& memberId : party.MemberId )
|
||||
{
|
||||
if( memberId == party.LeaderId )
|
||||
return static_cast< int8_t >( idx );
|
||||
idx++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void PartyMgr::removeMember( Party &party, const Entity::PlayerPtr& pMember )
|
||||
{
|
||||
auto& ccMgr = Common::Service< World::Manager::ChatChannelMgr >::ref();
|
||||
pMember->setPartyId( 0 );
|
||||
ccMgr.removeFromChannel( party.ChatChannel, *pMember );
|
||||
party.MemberId.erase( std::remove( party.MemberId.begin(), party.MemberId.end(), pMember->getId() ), party.MemberId.end() );
|
||||
}
|
88
src/world/Manager/PartyMgr.h
Normal file
88
src/world/Manager/PartyMgr.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <ForwardsZone.h>
|
||||
#include <array>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Sapphire::World::Manager
|
||||
{
|
||||
|
||||
enum UpdateStatus : int32_t
|
||||
{
|
||||
NONE_8 = 0x0,
|
||||
JOINED = 0x1,
|
||||
CHANGELEADER = 0x2,
|
||||
DISBAND = 0x3,
|
||||
KICK_MEMBER = 0x4,
|
||||
KICK_SELF = 0x5,
|
||||
LEAVE_MEMBER = 0x6,
|
||||
LEAVE_SELF = 0x7,
|
||||
MOVEZONE = 0x8,
|
||||
MOVETERRITORY = 0x9,
|
||||
OFFLINE_MEMBER = 0xA,
|
||||
RECOVERY_MEMBER = 0xB,
|
||||
LEAVELEADER_LEAVED_MEMBER = 0xC,
|
||||
LEAVELEADER_LEAVED_SELF = 0xD,
|
||||
ADDMEMBER_BUDDY = 0xE,
|
||||
REMOVEMEMBER_BUDDY = 0xF,
|
||||
SENDREADYCHECK = 0x10,
|
||||
REPLYREADYCHECK = 0x11,
|
||||
};
|
||||
|
||||
struct Party
|
||||
{
|
||||
std::vector< uint32_t > MemberId;
|
||||
uint64_t PartyID;
|
||||
uint64_t ChatChannel;
|
||||
uint32_t LeaderId;
|
||||
uint8_t PartyCount;
|
||||
};
|
||||
|
||||
using PartyPtr = std::shared_ptr< Party >;
|
||||
|
||||
class PartyMgr
|
||||
{
|
||||
public:
|
||||
PartyMgr() = default;
|
||||
|
||||
/// Perform required actions for events
|
||||
void onJoin( Entity::Player& joiner, Entity::Player& inviter );
|
||||
void onLeave( Entity::Player& leavingPlayer );
|
||||
void onMoveZone( Entity::Player& movingPlayer );
|
||||
void onDisband( Entity::Player& disbandingPlayer );
|
||||
void onKick( const std::string& kickPlayerName, Entity::Player& leader );
|
||||
void onChangeLeader( const std::string& newLeaderName, Entity::Player& oldLeader );
|
||||
|
||||
void onMemberDisconnect( Entity::Player& disconnectingPlayer );
|
||||
void onMemberRejoin( Entity::Player& joiningPlayer );
|
||||
|
||||
void onJoinBuddy( Entity::Player& buddyOwner, Party& party );
|
||||
void onLeaveBuddy( Entity::Player& buddyOwner, Party& party );
|
||||
void onStartReadyCheck( Entity::Player& startingPlayer, Party& party );
|
||||
void onReplyReadyCheck( Entity::Player& replyingPlayer, Party& party );
|
||||
|
||||
///////////////////////////
|
||||
PartyPtr getParty( uint64_t partyId );
|
||||
|
||||
private:
|
||||
// arbitrary start range for party ids
|
||||
uint64_t m_maxPartyId = 0x0000044000000000;
|
||||
|
||||
uint64_t createParty();
|
||||
void removeParty( uint64_t partyId );
|
||||
uint64_t getNextPartyId();
|
||||
std::unordered_map< uint64_t, PartyPtr > m_partyIdMap;
|
||||
|
||||
static void sendPartyUpdate( Party& party );
|
||||
static void removeMember( Party& party, const Entity::PlayerPtr& pMember );
|
||||
static std::vector< Entity::PlayerPtr > getPartyMembers( Party& party );
|
||||
static Entity::PlayerPtr getPartyLeader( Party& party );
|
||||
|
||||
static int8_t getPartyLeaderIndex( const Party& party );
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -24,9 +24,9 @@ Sapphire::World::Navi::NaviProvider::NaviProvider( const std::string& internalNa
|
|||
m_internalName( internalName )
|
||||
{
|
||||
// Set defaults
|
||||
m_polyFindRange[ 0 ] = 10;
|
||||
m_polyFindRange[ 0 ] = 20;
|
||||
m_polyFindRange[ 1 ] = 20;
|
||||
m_polyFindRange[ 2 ] = 10;
|
||||
m_polyFindRange[ 2 ] = 20;
|
||||
}
|
||||
|
||||
bool Sapphire::World::Navi::NaviProvider::init()
|
||||
|
@ -355,7 +355,7 @@ std::vector< Sapphire::Common::FFXIVARR_POSITION3 >
|
|||
// iterPos[ 0 ], iterPos[ 1 ], iterPos[ 2 ],
|
||||
// targetPos[ 0 ], targetPos[ 1 ], targetPos[ 2 ] );
|
||||
|
||||
const float STEP_SIZE = 1.2f;
|
||||
const float STEP_SIZE = 0.5f;
|
||||
const float SLOP = 0.15f;
|
||||
|
||||
int32_t numSmoothPath = 0;
|
||||
|
@ -573,7 +573,8 @@ int32_t Sapphire::World::Navi::NaviProvider::addAgent( Entity::Chara& chara )
|
|||
std::memset( ¶ms, 0, sizeof( params ) );
|
||||
params.height = 3.f;
|
||||
params.maxAcceleration = 25.f;
|
||||
params.maxSpeed = std::pow( 2, chara.getRadius() * 0.35f ) + 1.f;
|
||||
//params.maxSpeed = std::pow( 2, chara.getRadius() * 0.35f ) + 1.f;
|
||||
params.maxSpeed = ( std::pow( 2.f, 1.f * 0.35f ) + 1.f ) * 0.5f;
|
||||
params.radius = ( chara.getRadius() ) * 0.75f;
|
||||
params.collisionQueryRange = params.radius * 12.0f;
|
||||
params.pathOptimizationRange = params.radius * 20.0f;
|
||||
|
@ -589,9 +590,10 @@ void Sapphire::World::Navi::NaviProvider::updateAgentParameters( Entity::BNpc& b
|
|||
std::memset( ¶ms, 0, sizeof( params ) );
|
||||
params.height = 3.f;
|
||||
params.maxAcceleration = 25.f;
|
||||
params.maxSpeed = std::pow( 2, bnpc.getRadius() * 0.35f ) + 1.f;
|
||||
//params.maxSpeed = std::pow( 2, bnpc.getRadius() * 0.35f ) + 1.f;
|
||||
params.maxSpeed = ( std::pow( 2.f, 1.f * 0.35f ) + 1.f ) * 0.5f;
|
||||
if( bnpc.getState() == Entity::BNpcState::Combat || bnpc.getState() == Entity::BNpcState::Retreat )
|
||||
params.maxSpeed *= 2;
|
||||
params.maxSpeed *= 2.5f;
|
||||
params.radius = ( bnpc.getRadius() ) * 0.75f;
|
||||
params.collisionQueryRange = params.radius * 12.0f;
|
||||
params.pathOptimizationRange = params.radius * 20.0f;
|
||||
|
|
|
@ -141,7 +141,15 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
|
|||
setZoneHandler( ClientZoneIpcType::InventoryEquipRecommendedItems, "InventoryEquipRecommendedItemsHandler", &GameConnection::inventoryEquipRecommendedItemsHandler );
|
||||
|
||||
setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler );
|
||||
setChatHandler( ClientChatIpcType::ChannelChatReq, "ChannelChatReq", &GameConnection::channelChatHandler );
|
||||
|
||||
setZoneHandler( ClientZoneIpcType::SocialInviteHandler, "SocialInviteHandler", &GameConnection::socialInviteHandler );
|
||||
setZoneHandler( ClientZoneIpcType::SocialReplyHandler, "SocialReplyHandler", &GameConnection::socialReplyHandler );
|
||||
|
||||
setZoneHandler( ClientZoneIpcType::PartyLeaveHandler, "PartyLeaveHandler", &GameConnection::partyLeaveHandler );
|
||||
setZoneHandler( ClientZoneIpcType::PartyDisbandHandler, "PartyDisbandHandler", &GameConnection::partyDisbandHandler );
|
||||
setZoneHandler( ClientZoneIpcType::PartyKickHandler, "PartyKickHandler", &GameConnection::partyKickHandler );
|
||||
setZoneHandler( ClientZoneIpcType::PartyChangeLeaderHandler, "PartyChangeLeaderHandler", &GameConnection::partyChangeLeaderHandler );
|
||||
}
|
||||
|
||||
Sapphire::Network::GameConnection::~GameConnection() = default;
|
||||
|
|
|
@ -177,6 +177,8 @@ namespace Sapphire::Network
|
|||
|
||||
DECLARE_HANDLER( tellHandler );
|
||||
|
||||
DECLARE_HANDLER( channelChatHandler );
|
||||
|
||||
DECLARE_HANDLER( reqPlaceHousingItem );
|
||||
|
||||
DECLARE_HANDLER( reqMoveHousingItem );
|
||||
|
@ -198,6 +200,14 @@ namespace Sapphire::Network
|
|||
DECLARE_HANDLER( eventYieldHandler );
|
||||
|
||||
DECLARE_HANDLER( inventoryEquipRecommendedItemsHandler );
|
||||
|
||||
DECLARE_HANDLER( socialInviteHandler );
|
||||
DECLARE_HANDLER( socialReplyHandler );
|
||||
|
||||
DECLARE_HANDLER( partyLeaveHandler );
|
||||
DECLARE_HANDLER( partyDisbandHandler );
|
||||
DECLARE_HANDLER( partyKickHandler );
|
||||
DECLARE_HANDLER( partyChangeLeaderHandler );
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
#include "Manager/HousingMgr.h"
|
||||
#include "Manager/RNGMgr.h"
|
||||
#include "Manager/ItemMgr.h"
|
||||
#include "Manager/PartyMgr.h"
|
||||
#include "Manager/ChatChannelMgr.h"
|
||||
|
||||
#include "Action/Action.h"
|
||||
#include "Inventory/Item.h"
|
||||
|
@ -438,31 +440,58 @@ void Sapphire::Network::GameConnection::socialListHandler( const Packets::FFXIVA
|
|||
int32_t entrysizes = sizeof( listPacket->data().entries );
|
||||
memset( listPacket->data().entries, 0, sizeof( listPacket->data().entries ) );
|
||||
|
||||
listPacket->data().entries[ 0 ].bytes[ 2 ] = player.getCurrentTerritory()->getTerritoryTypeId();
|
||||
listPacket->data().entries[ 0 ].bytes[ 3 ] = 0x80;
|
||||
listPacket->data().entries[ 0 ].bytes[ 4 ] = 0x02;
|
||||
listPacket->data().entries[ 0 ].bytes[ 6 ] = 0x3B;
|
||||
listPacket->data().entries[ 0 ].bytes[ 11 ] = 0x10;
|
||||
listPacket->data().entries[ 0 ].classJob = static_cast< uint8_t >( player.getClass() );
|
||||
listPacket->data().entries[ 0 ].contentId = player.getContentId();
|
||||
listPacket->data().entries[ 0 ].level = player.getLevel();
|
||||
listPacket->data().entries[ 0 ].zoneId = player.getCurrentTerritory()->getTerritoryTypeId();
|
||||
listPacket->data().entries[ 0 ].zoneId1 = 0x0100;
|
||||
// TODO: no idea what this does
|
||||
//listPacket.data().entries[0].one = 1;
|
||||
auto fillEntryAt = [ &listPacket ]( int i, Entity::PlayerPtr nextPlayer, bool isLeader )
|
||||
{
|
||||
listPacket->data().entries[ i ].bytes[ 2 ] = nextPlayer->getCurrentTerritory()->getTerritoryTypeId();
|
||||
listPacket->data().entries[ i ].bytes[ 3 ] = 0x80;
|
||||
listPacket->data().entries[ i ].bytes[ 4 ] = 0x02;
|
||||
listPacket->data().entries[ i ].bytes[ 6 ] = 0x3B;
|
||||
listPacket->data().entries[ i ].bytes[ 8 ] = isLeader;
|
||||
listPacket->data().entries[ i ].bytes[ 11 ] = 0x10;
|
||||
listPacket->data().entries[ i ].classJob = static_cast< uint8_t >( nextPlayer->getClass() );
|
||||
listPacket->data().entries[ i ].contentId = nextPlayer->getContentId();
|
||||
listPacket->data().entries[ i ].level = nextPlayer->getLevel();
|
||||
listPacket->data().entries[ i ].zoneId = nextPlayer->getCurrentTerritory()->getTerritoryTypeId();
|
||||
listPacket->data().entries[ i ].zoneId1 = 0x0100;
|
||||
// TODO: no idea what this does
|
||||
//listPacket.data().entries[0].one = 1;
|
||||
|
||||
memcpy( listPacket->data().entries[ 0 ].name, player.getName().c_str(), strlen( player.getName().c_str() ) );
|
||||
memcpy( listPacket->data().entries[ i ].name, nextPlayer->getName().c_str(), strlen( nextPlayer->getName().c_str() ) );
|
||||
|
||||
// GC icon
|
||||
listPacket->data().entries[ 0 ].bytes1[ 0 ] = 2;
|
||||
// client language J = 0, E = 1, D = 2, F = 3
|
||||
listPacket->data().entries[ 0 ].bytes1[ 1 ] = 1;
|
||||
// user language settings flag J = 1, E = 2, D = 4, F = 8
|
||||
listPacket->data().entries[ 0 ].bytes1[ 2 ] = 1 + 2;
|
||||
listPacket->data().entries[ 0 ].onlineStatusMask = player.getOnlineStatusMask();
|
||||
// GC icon
|
||||
listPacket->data().entries[ i ].bytes1[ 0 ] = 2;
|
||||
// client language J = 0, E = 1, D = 2, F = 3
|
||||
listPacket->data().entries[ i ].bytes1[ 1 ] = 1;
|
||||
// user language settings flag J = 1, E = 2, D = 4, F = 8
|
||||
listPacket->data().entries[ i ].bytes1[ 2 ] = 1 + 2 + 4 + 8;
|
||||
listPacket->data().entries[ i ].onlineStatusMask = nextPlayer->getOnlineStatusMask();
|
||||
};
|
||||
auto nextPlayer = player.getAsPlayer();
|
||||
fillEntryAt( 0, nextPlayer, false );
|
||||
if( player.getPartyId() != 0 )
|
||||
{
|
||||
// fill party members
|
||||
auto& partyMgr = Common::Service< World::Manager::PartyMgr >::ref();
|
||||
auto& server = Common::Service< World::ServerMgr >::ref();
|
||||
auto pParty = partyMgr.getParty( player.getPartyId() );
|
||||
assert( pParty );
|
||||
|
||||
int i = 1;
|
||||
for( auto id : pParty->MemberId )
|
||||
{
|
||||
nextPlayer = server.getSession( id )->getPlayer();
|
||||
if( nextPlayer->getId() == player.getId() )
|
||||
{
|
||||
// data already in entry 0, only change the leader flag
|
||||
listPacket->data().entries[ 0 ].bytes[ 8 ] = pParty->LeaderId == id;
|
||||
continue;
|
||||
}
|
||||
fillEntryAt( i, nextPlayer, pParty->LeaderId == id );
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
queueOutPacket( listPacket );
|
||||
|
||||
}
|
||||
else if( type == 2 )
|
||||
{ // friend list
|
||||
|
@ -598,12 +627,24 @@ void Sapphire::Network::GameConnection::tellHandler( const Packets::FFXIVARR_PAC
|
|||
|
||||
if( player.isActingAsGm() )
|
||||
{
|
||||
tellPacket->data().flags |= TellFlags::GmTellMsg;
|
||||
tellPacket->data().flags |= ChatFromType::GmTellMsg;
|
||||
}
|
||||
|
||||
pTargetPlayer->queueChatPacket( tellPacket );
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::channelChatHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
|
||||
{
|
||||
const auto packet = ChatChannelPacket< Client::FFXIVIpcChannelChatHandler >( inPacket );
|
||||
auto& data = packet.data();
|
||||
|
||||
auto& chatChannelMgr = Common::Service< ChatChannelMgr >::ref();
|
||||
|
||||
std::string message = std::string( data.message );
|
||||
|
||||
chatChannelMgr.sendMessageToChannel( data.channelId, player, message );
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::performNoteHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
Entity::Player& player )
|
||||
{
|
||||
|
|
73
src/world/Network/Handlers/PartyHandlers.cpp
Normal file
73
src/world/Network/Handlers/PartyHandlers.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include <Common.h>
|
||||
#include <Network/CommonNetwork.h>
|
||||
#include <Network/GamePacket.h>
|
||||
|
||||
#include <Network/PacketContainer.h>
|
||||
#include <Service.h>
|
||||
#include <Network/PacketDef/Zone/ClientZoneDef.h>
|
||||
|
||||
#include "Manager/PartyMgr.h"
|
||||
|
||||
#include "Network/GameConnection.h"
|
||||
|
||||
#include "Session.h"
|
||||
#include "Actor/Player.h"
|
||||
|
||||
using namespace Sapphire::Common;
|
||||
using namespace Sapphire::Network::Packets;
|
||||
using namespace Sapphire::World::Manager;
|
||||
|
||||
|
||||
void Sapphire::Network::GameConnection::partyLeaveHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
Entity::Player& player )
|
||||
{
|
||||
if( player.getPartyId() == 0 )
|
||||
return;
|
||||
|
||||
auto& partyMgr = Common::Service< Sapphire::World::Manager::PartyMgr >::ref();
|
||||
|
||||
partyMgr.onLeave( player );
|
||||
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::partyDisbandHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
Entity::Player& player )
|
||||
{
|
||||
if( player.getPartyId() == 0 )
|
||||
return;
|
||||
|
||||
auto& partyMgr = Common::Service< Sapphire::World::Manager::PartyMgr >::ref();
|
||||
|
||||
partyMgr.onDisband( player );
|
||||
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::partyKickHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
Entity::Player& player )
|
||||
{
|
||||
if( player.getPartyId() == 0 )
|
||||
return;
|
||||
|
||||
const auto packet = ZoneChannelPacket< Client::FFXIVIpcPartyKickHandler >( inPacket );
|
||||
const auto& data = packet.data();
|
||||
|
||||
auto& partyMgr = Common::Service< Sapphire::World::Manager::PartyMgr >::ref();
|
||||
|
||||
partyMgr.onKick( std::string( data.name ), player );
|
||||
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::partyChangeLeaderHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
|
||||
Entity::Player& player )
|
||||
{
|
||||
if( player.getPartyId() == 0 )
|
||||
return;
|
||||
|
||||
const auto packet = ZoneChannelPacket< Client::FFXIVIpcPartyChangeLeaderHandler >( inPacket );
|
||||
const auto& data = packet.data();
|
||||
|
||||
auto& partyMgr = Common::Service< Sapphire::World::Manager::PartyMgr >::ref();
|
||||
|
||||
partyMgr.onChangeLeader( std::string( data.name ), player );
|
||||
|
||||
}
|
41
src/world/Network/PacketWrappers/ChannelChatPacket.h
Normal file
41
src/world/Network/PacketWrappers/ChannelChatPacket.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
#pragma once
|
||||
|
||||
#include "Forwards.h"
|
||||
#include "Actor/Player.h"
|
||||
#include <Network/GamePacket.h>
|
||||
#include <Network/PacketDef/Chat/ServerChatDef.h>
|
||||
|
||||
namespace Sapphire::Network::Packets::Server
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The Chat packet.
|
||||
*/
|
||||
class ChannelChatPacket : public ChatChannelPacket< FFXIVIpcChannelChat >
|
||||
{
|
||||
public:
|
||||
ChannelChatPacket( Entity::Player& target,
|
||||
Entity::Player& sender,
|
||||
uint64_t channelId,
|
||||
const std::string& msg ) :
|
||||
ChatChannelPacket< FFXIVIpcChannelChat >( target.getId(), target.getId() )
|
||||
{
|
||||
initialize( sender, channelId, msg );
|
||||
};
|
||||
|
||||
private:
|
||||
void initialize( Entity::Player& sender, uint64_t channelId, const std::string& msg )
|
||||
{
|
||||
strcpy( m_data.message, msg.c_str() );
|
||||
strcpy( m_data.name, sender.getName().c_str() );
|
||||
|
||||
m_data.channelId = channelId;
|
||||
|
||||
m_data.contentId = sender.getContentId();
|
||||
m_data.charaId = sender.getId();
|
||||
|
||||
m_data.type = 0;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
127
src/world/Network/PacketWrappers/InviteHandlers.cpp
Normal file
127
src/world/Network/PacketWrappers/InviteHandlers.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
#include <Common.h>
|
||||
#include <Network/CommonNetwork.h>
|
||||
#include <Network/GamePacket.h>
|
||||
#include <Logging/Logger.h>
|
||||
#include <Network/PacketContainer.h>
|
||||
|
||||
#include <datReader/DatCategories/bg/LgbTypes.h>
|
||||
|
||||
#include <Network/PacketDef/Zone/ClientZoneDef.h>
|
||||
#include <Service.h>
|
||||
|
||||
#include "Network/GameConnection.h"
|
||||
#include "Session.h"
|
||||
|
||||
#include "Territory/Territory.h"
|
||||
|
||||
#include "Network/PacketWrappers/PlayerSetupPacket.h"
|
||||
|
||||
//#include "Manager/FriendListMgr.h"
|
||||
#include "Manager/PartyMgr.h"
|
||||
#include "Manager/PlayerMgr.h"
|
||||
//#include "Manager/FreeCompanyMgr.h"
|
||||
|
||||
#include "Action/Action.h"
|
||||
|
||||
#include "ServerMgr.h"
|
||||
#include "Forwards.h"
|
||||
|
||||
using namespace Sapphire::Common;
|
||||
using namespace Sapphire::Network::Packets;
|
||||
using namespace Sapphire::Network::Packets::Server;
|
||||
using namespace Sapphire::Network::Packets::Client;
|
||||
using namespace Sapphire::World::Manager;
|
||||
|
||||
|
||||
void Sapphire::Network::GameConnection::socialInviteHandler( const FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
|
||||
{
|
||||
const auto packet = ZoneChannelPacket< Client::FFXIVIpcSocialInviteHandler >( inPacket );
|
||||
|
||||
player.sendDebug( "Auth Type#{0}", packet.data().socialType );
|
||||
player.sendDebug( "Target Name: {0}", packet.data().name );
|
||||
|
||||
std::string name( packet.data().name );
|
||||
|
||||
auto& playerMgr = Common::Service< World::Manager::PlayerMgr >::ref();
|
||||
auto& server = Common::Service< Sapphire::World::ServerMgr >::ref();
|
||||
auto pTargetPlayer = server.getSession( name )->getPlayer();
|
||||
|
||||
if( !pTargetPlayer )
|
||||
return;
|
||||
|
||||
switch( packet.data().socialType )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
auto inviteResultPacket = makeZonePacket< Server::FFXIVIpcSocialInviteResult >( player.getId() );
|
||||
auto& data = inviteResultPacket->data();
|
||||
data.contentId = pTargetPlayer->getContentId();
|
||||
data.p1 = packet.data().p1;
|
||||
data.p2 = packet.data().p2;
|
||||
data.socialType = packet.data().socialType;
|
||||
strcpy( data.name, packet.data().name );
|
||||
player.queuePacket( inviteResultPacket );
|
||||
|
||||
auto inviteUpdatePacket = makeZonePacket< Server::FFXIVIpcSocialInviteUpdate >( pTargetPlayer->getId() );
|
||||
inviteUpdatePacket->data().contentId = player.getContentId();
|
||||
inviteUpdatePacket->data().expireTime = Common::Util::getTimeSeconds() + 30;
|
||||
inviteUpdatePacket->data().p1 = packet.data().p1;
|
||||
inviteUpdatePacket->data().p2 = packet.data().p2;
|
||||
inviteUpdatePacket->data().socialType = packet.data().socialType;
|
||||
inviteUpdatePacket->data().type = 1;
|
||||
inviteUpdatePacket->data().gender = player.getGender();
|
||||
strcpy( inviteUpdatePacket->data().name, player.getName().c_str() );
|
||||
pTargetPlayer->queuePacket( inviteUpdatePacket );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Sapphire::Network::GameConnection::socialReplyHandler( const FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
|
||||
{
|
||||
const auto packet = ZoneChannelPacket< Client::FFXIVIpcSocialReplyHandler >( inPacket );
|
||||
const auto& data = packet.data();
|
||||
|
||||
auto& playerMgr = Common::Service< World::Manager::PlayerMgr >::ref();
|
||||
auto& server = Common::Service< Sapphire::World::ServerMgr >::ref();
|
||||
auto pPlayer = server.getSession( data.contentId )->getPlayer();
|
||||
|
||||
if( !pPlayer )
|
||||
return;
|
||||
|
||||
auto inviteReplyPacket = makeZonePacket< Server::FFXIVIpcSocialInviteResponse >( player.getId() );
|
||||
auto& inviteReplyData = inviteReplyPacket->data();
|
||||
inviteReplyData.response = data.response;
|
||||
|
||||
switch( data.socialType )
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
auto& partyMgr = Common::Service< PartyMgr >::ref();
|
||||
|
||||
if( data.response == InviteReplyType::ACCEPT )
|
||||
{
|
||||
partyMgr.onJoin( player, *pPlayer );
|
||||
}
|
||||
|
||||
auto inviteUpPacket = makeZonePacket< Server::FFXIVIpcSocialInviteUpdate >( pPlayer->getId() );
|
||||
inviteUpPacket->data().contentId = player.getContentId();
|
||||
inviteUpPacket->data().expireTime = Common::Util::getTimeSeconds() + 30;
|
||||
inviteUpPacket->data().p1 = packet.data().p1;
|
||||
inviteUpPacket->data().p2 = packet.data().p2;
|
||||
inviteUpPacket->data().socialType = packet.data().socialType;
|
||||
inviteUpPacket->data().type = data.response == InviteReplyType::ACCEPT ? InviteUpdateType::ACCEPT_INVITE : InviteUpdateType::REJECT_INVITE;
|
||||
strcpy( inviteUpPacket->data().name, player.getName().c_str() );
|
||||
pPlayer->queuePacket( inviteUpPacket );
|
||||
|
||||
inviteReplyData.contentId == pPlayer->getContentId();
|
||||
inviteReplyData.socialType = data.socialType;
|
||||
inviteReplyData.gender = pPlayer->getGender();
|
||||
strcpy( inviteReplyData.name, pPlayer->getName().c_str() );
|
||||
player.queuePacket( inviteReplyPacket );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
83
src/world/Network/PacketWrappers/PartyUpdatePacket.h
Normal file
83
src/world/Network/PacketWrappers/PartyUpdatePacket.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include <Network/GamePacket.h>
|
||||
#include "Actor/Player.h"
|
||||
#include "Forwards.h"
|
||||
|
||||
namespace Sapphire::Network::Packets::Server
|
||||
{
|
||||
class PartyUpdatePacket : public ZoneChannelPacket< FFXIVIpcPartyUpdate >
|
||||
{
|
||||
public:
|
||||
PartyUpdatePacket( Entity::Player& executePlayer, Entity::Player& targetPlayer, uint8_t updateStatus, uint8_t count ) :
|
||||
ZoneChannelPacket< FFXIVIpcPartyUpdate >( executePlayer.getId(), executePlayer.getId() )
|
||||
{
|
||||
initialize( executePlayer, targetPlayer, updateStatus, count );
|
||||
};
|
||||
|
||||
PartyUpdatePacket( Entity::Player& executePlayer, uint8_t updateStatus, uint8_t count ) :
|
||||
ZoneChannelPacket< FFXIVIpcPartyUpdate >( executePlayer.getId(), executePlayer.getId() )
|
||||
{
|
||||
initialize( executePlayer, updateStatus, count );
|
||||
};
|
||||
|
||||
PartyUpdatePacket( const Entity::PlayerPtr& executePlayer, const Entity::PlayerPtr& targetPlayer, uint8_t updateStatus, uint8_t count ) :
|
||||
ZoneChannelPacket< FFXIVIpcPartyUpdate >( executePlayer->getId(), executePlayer->getId() )
|
||||
{
|
||||
initialize( executePlayer, targetPlayer, updateStatus, count );
|
||||
};
|
||||
|
||||
private:
|
||||
void initialize( Entity::Player& executePlayer, Entity::Player& targetPlayer, uint8_t updateStatus, uint8_t partySize )
|
||||
{
|
||||
m_data.executeContentId = executePlayer.getContentId();
|
||||
m_data.targetContentId = targetPlayer.getContentId();
|
||||
m_data.executeGender = executePlayer.getGender();
|
||||
m_data.targetGender = targetPlayer.getGender();
|
||||
m_data.updateStatus = updateStatus;
|
||||
m_data.partySize = partySize;
|
||||
strcpy( m_data.executeName, executePlayer.getName().c_str() );
|
||||
strcpy( m_data.targetName, targetPlayer.getName().c_str() );
|
||||
};
|
||||
|
||||
void initialize( Entity::Player& executePlayer, uint8_t updateStatus, uint8_t partySize )
|
||||
{
|
||||
m_data.executeContentId = executePlayer.getContentId();
|
||||
m_data.targetContentId = 0;
|
||||
m_data.executeGender = executePlayer.getGender();
|
||||
m_data.targetGender = 0;
|
||||
m_data.updateStatus = updateStatus;
|
||||
m_data.partySize = partySize;
|
||||
strcpy( m_data.targetName, executePlayer.getName().c_str() );
|
||||
};
|
||||
|
||||
void initialize( const Entity::PlayerPtr& executePlayer, const Entity::PlayerPtr& targetPlayer, uint8_t updateStatus, uint8_t partySize )
|
||||
{
|
||||
if( targetPlayer )
|
||||
{
|
||||
m_data.targetContentId = targetPlayer->getContentId();
|
||||
m_data.targetGender = targetPlayer->getGender();
|
||||
strcpy( m_data.targetName, targetPlayer->getName().c_str() );
|
||||
}
|
||||
|
||||
if( executePlayer )
|
||||
{
|
||||
m_data.executeContentId = executePlayer->getContentId();
|
||||
m_data.executeGender = executePlayer->getGender();
|
||||
strcpy( m_data.executeName, executePlayer->getName().c_str() );
|
||||
}
|
||||
|
||||
m_data.updateStatus = updateStatus;
|
||||
m_data.partySize = partySize;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
template< typename... Args >
|
||||
std::shared_ptr< PartyUpdatePacket > makePartyUpdate( Args... args )
|
||||
{
|
||||
return std::make_shared< PartyUpdatePacket >( args... );
|
||||
}
|
||||
|
||||
}
|
|
@ -42,6 +42,8 @@
|
|||
#include "Manager/NaviMgr.h"
|
||||
#include "Manager/ActionMgr.h"
|
||||
#include "Manager/MapMgr.h"
|
||||
#include "Manager/ChatChannelMgr.h"
|
||||
#include "Manager/PartyMgr.h"
|
||||
|
||||
#include "Territory/InstanceObjectCache.h"
|
||||
|
||||
|
@ -153,6 +155,9 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] )
|
|||
}
|
||||
Common::Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::set( pDb );
|
||||
|
||||
auto pChatChannelMgr = std::make_shared< Manager::ChatChannelMgr >();
|
||||
Common::Service< Manager::ChatChannelMgr >::set( pChatChannelMgr );
|
||||
|
||||
Logger::info( "LinkshellMgr: Caching linkshells" );
|
||||
auto pLsMgr = std::make_shared< Manager::LinkshellMgr >();
|
||||
if( !pLsMgr->loadLinkshells() )
|
||||
|
@ -227,6 +232,7 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] )
|
|||
auto pEventMgr = std::make_shared< Manager::EventMgr >();
|
||||
auto pItemMgr = std::make_shared< Manager::ItemMgr >();
|
||||
auto pRNGMgr = std::make_shared< Manager::RNGMgr >();
|
||||
auto pPartyMgr = std::make_shared< Manager::PartyMgr >();
|
||||
|
||||
Common::Service< DebugCommandMgr >::set( pDebugCom );
|
||||
Common::Service< Manager::PlayerMgr >::set( pPlayerMgr );
|
||||
|
@ -235,6 +241,7 @@ void Sapphire::World::ServerMgr::run( int32_t argc, char* argv[] )
|
|||
Common::Service< Manager::EventMgr >::set( pEventMgr );
|
||||
Common::Service< Manager::ItemMgr >::set( pItemMgr );
|
||||
Common::Service< Manager::RNGMgr >::set( pRNGMgr );
|
||||
Common::Service< Manager::PartyMgr >::set( pPartyMgr );
|
||||
|
||||
Logger::info( "World server running on {0}:{1}", m_ip, m_port );
|
||||
|
||||
|
@ -320,6 +327,7 @@ void Sapphire::World::ServerMgr::mainLoop()
|
|||
{
|
||||
Logger::info( "[{0}] Session removal", it->second->getId() );
|
||||
it = m_sessionMapById.erase( it );
|
||||
removeSession( pPlayer->getContentId() );
|
||||
removeSession( pPlayer->getName() );
|
||||
continue;
|
||||
}
|
||||
|
@ -334,6 +342,7 @@ void Sapphire::World::ServerMgr::mainLoop()
|
|||
// if( it->second.unique() )
|
||||
{
|
||||
it = m_sessionMapById.erase( it );
|
||||
removeSession( pPlayer->getContentId() );
|
||||
removeSession( pPlayer->getName() );
|
||||
}
|
||||
}
|
||||
|
@ -372,17 +381,13 @@ bool Sapphire::World::ServerMgr::createSession( uint32_t sessionId )
|
|||
return false;
|
||||
}
|
||||
|
||||
m_sessionMapByContentId[ newSession->getPlayer()->getContentId() ] = newSession;
|
||||
m_sessionMapByName[ newSession->getPlayer()->getName() ] = newSession;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void Sapphire::World::ServerMgr::removeSession( uint32_t sessionId )
|
||||
{
|
||||
m_sessionMapById.erase( sessionId );
|
||||
}
|
||||
|
||||
Sapphire::World::SessionPtr Sapphire::World::ServerMgr::getSession( uint32_t id )
|
||||
{
|
||||
//std::lock_guard<std::mutex> lock( m_sessionMutex );
|
||||
|
@ -394,6 +399,16 @@ Sapphire::World::SessionPtr Sapphire::World::ServerMgr::getSession( uint32_t id
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Sapphire::World::SessionPtr Sapphire::World::ServerMgr::getSession( uint64_t contentId )
|
||||
{
|
||||
auto it = m_sessionMapByContentId.find( contentId );
|
||||
|
||||
if( it != m_sessionMapByContentId.end() )
|
||||
return ( it->second );
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Sapphire::World::SessionPtr Sapphire::World::ServerMgr::getSession( const std::string& playerName )
|
||||
{
|
||||
//std::lock_guard<std::mutex> lock( m_sessionMutex );
|
||||
|
@ -406,6 +421,16 @@ Sapphire::World::SessionPtr Sapphire::World::ServerMgr::getSession( const std::s
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void Sapphire::World::ServerMgr::removeSession( uint32_t sessionId )
|
||||
{
|
||||
m_sessionMapById.erase( sessionId );
|
||||
}
|
||||
|
||||
void Sapphire::World::ServerMgr::removeSession( uint64_t contentId )
|
||||
{
|
||||
m_sessionMapByContentId.erase( contentId );
|
||||
}
|
||||
|
||||
void Sapphire::World::ServerMgr::removeSession( const std::string& playerName )
|
||||
{
|
||||
m_sessionMapByName.erase( playerName );
|
||||
|
|
|
@ -24,10 +24,8 @@ namespace Sapphire::World
|
|||
|
||||
bool createSession( uint32_t sessionId );
|
||||
|
||||
void removeSession( uint32_t sessionId );
|
||||
void removeSession( const std::string& playerName );
|
||||
|
||||
World::SessionPtr getSession( uint32_t id );
|
||||
World::SessionPtr getSession( uint64_t contentId );
|
||||
World::SessionPtr getSession( const std::string& playerName );
|
||||
|
||||
size_t getSessionCount() const;
|
||||
|
@ -66,11 +64,15 @@ namespace Sapphire::World
|
|||
Sapphire::Common::Config::WorldConfig m_config;
|
||||
|
||||
std::map< uint32_t, SessionPtr > m_sessionMapById;
|
||||
std::map< uint64_t, SessionPtr > m_sessionMapByContentId;
|
||||
std::map< std::string, SessionPtr > m_sessionMapByName;
|
||||
std::map< uint32_t, std::string > m_playerNameMapById;
|
||||
std::map< uint32_t, uint32_t > m_zones;
|
||||
std::map< std::string, Entity::BNpcTemplatePtr > m_bNpcTemplateMap;
|
||||
|
||||
void removeSession( uint32_t sessionId );
|
||||
void removeSession( uint64_t contentId );
|
||||
void removeSession( const std::string& playerName );
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#include "Network/GameConnection.h"
|
||||
#include "Actor/Player.h"
|
||||
|
||||
#include "Service.h"
|
||||
#include "Manager/PartyMgr.h"
|
||||
|
||||
#include "Session.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
@ -73,6 +76,12 @@ void Sapphire::World::Session::close()
|
|||
if( m_pPlayer )
|
||||
{
|
||||
m_pPlayer->clearBuyBackMap();
|
||||
if( m_pPlayer->getPartyId() != 0 )
|
||||
{
|
||||
// offline player is removed from party for now;
|
||||
auto& partyMgr = Common::Service< World::Manager::PartyMgr >::ref();
|
||||
partyMgr.onLeave( *m_pPlayer );
|
||||
}
|
||||
// do one last update to db
|
||||
m_pPlayer->updateSql();
|
||||
// reset the zone, so the zone handler knows to remove the actor
|
||||
|
|
|
@ -341,6 +341,12 @@ void Sapphire::StatusEffect::StatusEffect::refresh()
|
|||
applyStatus();
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::refresh( uint32_t newDuration )
|
||||
{
|
||||
m_duration = newDuration;
|
||||
refresh();
|
||||
}
|
||||
|
||||
void Sapphire::StatusEffect::StatusEffect::refresh( Sapphire::World::Action::StatusEffectEntry newEntry )
|
||||
{
|
||||
m_effectEntry = newEntry;
|
||||
|
|
|
@ -68,6 +68,7 @@ public:
|
|||
void markToRemove();
|
||||
|
||||
void refresh();
|
||||
void refresh( uint32_t newDuration );
|
||||
void refresh( Sapphire::World::Action::StatusEffectEntry newEntry );
|
||||
|
||||
private:
|
||||
|
|
Loading…
Add table
Reference in a new issue