1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-25 19:17:45 +00:00

Action data update.

All values are parsed from ffxiv act plugin at https://github.com/ravahn/FFXIV_ACT_Plugin
This commit is contained in:
collett 2020-01-09 01:29:09 +09:00
parent 6eaa4c407f
commit 761b6cc94d
13 changed files with 2185 additions and 1585 deletions

View file

@ -13,14 +13,14 @@ public:
void onExecute( Sapphire::World::Action::Action& action ) override
{
/*
auto sourceChara = action.getSourceChara();
if( !sourceChara->isPlayer() )
return;
action.getEffectbuilder()->applyStatusEffect( sourceChara, 50, 30 );
sourceChara->getAsPlayer()->addStatusEffectByIdIfNotExist( 50, 20000, *sourceChara, 30 );
action.getEffectbuilder()->applyStatusEffect( sourceChara, sourceChara, 50, 20000, 30 );
*/
}
};

View file

@ -468,19 +468,21 @@ void Action::Action::buildEffects()
return;
}
if( !hasLutEntry || m_hitActors.empty() ) // this is just "if ( weCanNotUseGenericActionHandler )" in case we start to expand it.
if( !hasLutEntry ) // this is just "if ( weCanNotUseGenericActionHandler )" in case we start to expand it.
{
// send any effect packet added by script or an empty one just to play animation for other players
m_effectBuilder->buildAndSendPackets();
return;
}
// we have a valid lut entry
if( auto player = getSourceChara()->getAsPlayer() )
{
player->sendDebug( "Hit target: pot: {} (c: {}, f: {}, r: {}), heal pot: {}, mpp: {}",
m_lutEntry.potency, m_lutEntry.comboPotency, m_lutEntry.flankPotency, m_lutEntry.rearPotency,
m_lutEntry.curePotency, m_lutEntry.restoreMPPercentage );
player->sendDebug( "dpot: {} (dcpot: {}, ddpot: {}), hpot: {}, shpot: {}, ss: {}, ts: {}, gmp: {}, gjob: {}",
m_lutEntry.damagePotency, m_lutEntry.damageComboPotency, m_lutEntry.damageDirectionalPotency,
m_lutEntry.healPotency, m_lutEntry.selfHealPotency,
m_lutEntry.selfStatus, m_lutEntry.targetStatus,
m_lutEntry.gainMPPercentage, m_lutEntry.gainJobResource );
}
// when aoe, these effects are in the target whatever is hit first
@ -489,9 +491,9 @@ void Action::Action::buildEffects()
for( auto& actor : m_hitActors )
{
if( m_lutEntry.potency > 0 )
if( m_lutEntry.damagePotency > 0 )
{
auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.comboPotency : m_lutEntry.potency );
auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.damageComboPotency : m_lutEntry.damagePotency );
m_effectBuilder->damage( actor, actor, dmg.first, dmg.second );
if( dmg.first > 0 )
@ -505,15 +507,15 @@ void Action::Action::buildEffects()
if( !isComboAction() || isCorrectCombo() )
{
if( m_lutEntry.curePotency > 0 ) // actions with self heal
if( m_lutEntry.selfHealPotency > 0 ) // actions with self heal
{
auto heal = calcHealing( m_lutEntry.curePotency );
auto heal = calcHealing( m_lutEntry.selfHealPotency );
m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource );
}
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
if( m_lutEntry.gainMPPercentage > 0 && shouldRestoreMP )
{
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.gainMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
shouldRestoreMP = false;
}
@ -523,24 +525,34 @@ void Action::Action::buildEffects()
}
}
}
else if( m_lutEntry.curePotency > 0 )
else if( m_lutEntry.healPotency > 0 )
{
auto heal = calcHealing( m_lutEntry.curePotency );
auto heal = calcHealing( m_lutEntry.healPotency );
m_effectBuilder->heal( actor, actor, heal.first, heal.second );
if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
if( m_lutEntry.gainMPPercentage > 0 && shouldRestoreMP )
{
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.gainMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
shouldRestoreMP = false;
}
}
else if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP )
else if( m_lutEntry.gainMPPercentage > 0 && shouldRestoreMP )
{
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.gainMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource );
shouldRestoreMP = false;
}
if( m_lutEntry.targetStatus != 0 )
{
m_effectBuilder->applyStatusEffect( actor, m_pSource, m_lutEntry.targetStatus, m_lutEntry.targetStatusDuration, m_lutEntry.targetStatusParam );
}
}
if( m_lutEntry.selfStatus != 0 )
{
m_effectBuilder->applyStatusEffect( m_pSource, m_pSource, m_lutEntry.selfStatus, m_lutEntry.selfStatusDuration, m_lutEntry.selfStatusParam );
}
m_effectBuilder->buildAndSendPackets();
// at this point we're done with it and no longer need it
@ -565,7 +577,7 @@ bool Action::Action::playerPreCheck( Entity::Player& player )
return false;
// npc actions/non player actions
if( m_actionData->classJob == -1 )
if( m_actionData->classJob == -1 && !m_actionData->isRoleAction )
return false;
if( player.getLevel() < m_actionData->classJobLevel )
@ -574,7 +586,7 @@ bool Action::Action::playerPreCheck( Entity::Player& player )
auto currentClass = player.getClass();
auto actionClass = static_cast< Common::ClassJob >( m_actionData->classJob );
if( actionClass != Common::ClassJob::Adventurer && currentClass != actionClass )
if( actionClass != Common::ClassJob::Adventurer && currentClass != actionClass && !m_actionData->isRoleAction )
{
// check if not a base class action
auto exdData = m_pFw->get< Data::ExdDataGenerated >();
@ -777,18 +789,18 @@ bool Action::Action::preFilterActor( Sapphire::Entity::Actor& actor ) const
if( kind != ObjKind::BattleNpc && kind != ObjKind::Player )
return false;
if( !m_canTargetSelf && chara->getId() == m_pSource->getId() )
if( m_lutEntry.damagePotency > 0 && chara->getId() == m_pSource->getId() ) // !m_canTargetSelf
return false;
if( ( m_lutEntry.potency > 0 || m_lutEntry.curePotency > 0 ) && !chara->isAlive() ) // !m_canTargetDead not working for aoe
if( ( m_lutEntry.damagePotency > 0 || m_lutEntry.healPotency > 0 ) && !chara->isAlive() ) // !m_canTargetDead not working for aoe
return false;
if( m_lutEntry.potency > 0 && m_pSource->getObjKind() == chara->getObjKind() ) // !m_canTargetFriendly not working for aoe
if( m_lutEntry.damagePotency > 0 && m_pSource->getObjKind() == chara->getObjKind() ) // !m_canTargetFriendly not working for aoe
return false;
if( ( m_lutEntry.potency == 0 && m_lutEntry.curePotency > 0 ) && m_pSource->getObjKind() != chara->getObjKind() ) // !m_canTargetHostile not working for aoe
if( ( m_lutEntry.damagePotency == 0 && m_lutEntry.healPotency > 0 ) && m_pSource->getObjKind() != chara->getObjKind() ) // !m_canTargetHostile not working for aoe
return false;
return true;
}
@ -809,8 +821,8 @@ Sapphire::Entity::CharaPtr Action::Action::getHitChara()
bool Action::Action::hasValidLutEntry() const
{
return m_lutEntry.potency != 0 || m_lutEntry.comboPotency != 0 || m_lutEntry.flankPotency != 0 || m_lutEntry.frontPotency != 0 ||
m_lutEntry.rearPotency != 0 || m_lutEntry.curePotency != 0 || m_lutEntry.restoreMPPercentage != 0;
return m_lutEntry.damagePotency != 0 || m_lutEntry.healPotency != 0 || m_lutEntry.selfHealPotency != 0 || m_lutEntry.selfStatus != 0 ||
m_lutEntry.targetStatus != 0 || m_lutEntry.gainMPPercentage != 0 || m_lutEntry.gainJobResource != 0;
}
Action::EffectBuilderPtr Action::Action::getEffectbuilder()

View file

@ -12,9 +12,9 @@ bool ActionLut::validEntryExists( uint16_t actionId )
const auto& entry = it->second;
// if all of the fields are 0, it's not 'valid' due to parse error or no useful data in the tooltip
return entry.potency != 0 || entry.comboPotency != 0 || entry.flankPotency != 0 || entry.frontPotency != 0 ||
entry.rearPotency != 0 || entry.curePotency != 0;
// if all of these fields are 0, it's not 'valid' due to parse error or no useful data
return entry.damagePotency != 0 || entry.healPotency != 0 || entry.selfHealPotency != 0 || entry.selfStatus != 0 ||
entry.targetStatus != 0 || entry.gainMPPercentage != 0 || entry.gainJobResource != 0;
}
const ActionEntry& ActionLut::getEntry( uint16_t actionId )

View file

@ -7,13 +7,19 @@ namespace Sapphire::World::Action
{
struct ActionEntry
{
uint16_t potency;
uint16_t comboPotency;
uint16_t flankPotency;
uint16_t frontPotency;
uint16_t rearPotency;
uint16_t curePotency;
uint16_t restoreMPPercentage;
uint16_t damagePotency;
uint16_t damageComboPotency;
uint16_t damageDirectionalPotency;
uint16_t healPotency;
uint16_t selfHealPotency;
uint16_t selfStatus;
uint16_t selfStatusDuration;
uint16_t selfStatusParam;
uint16_t targetStatus;
uint16_t targetStatusDuration;
uint16_t targetStatusParam;
uint16_t gainMPPercentage;
uint16_t gainJobResource;
};
class ActionLut

File diff suppressed because it is too large Load diff

View file

@ -51,43 +51,43 @@ void EffectBuilder::moveToResultList( Entity::CharaPtr& chara, EffectResultPtr r
void EffectBuilder::heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
{
EffectResultPtr nextResult = make_EffectResult( healingTarget, getResultDelayMs() );
EffectResultPtr nextResult = make_EffectResult( healingTarget, nullptr, getResultDelayMs() );
nextResult->heal( amount, severity, flag );
moveToResultList( effectTarget, nextResult );
}
void EffectBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionEffectResultFlag flag )
{
EffectResultPtr nextResult = make_EffectResult( restoringTarget, getResultDelayMs() ); // restore mp source actor
EffectResultPtr nextResult = make_EffectResult( restoringTarget, nullptr, getResultDelayMs() ); // restore mp source actor
nextResult->restoreMP( amount, flag );
moveToResultList( target, nextResult );
}
void EffectBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag )
{
EffectResultPtr nextResult = make_EffectResult( damagingTarget, getResultDelayMs() );
EffectResultPtr nextResult = make_EffectResult( damagingTarget, nullptr, getResultDelayMs() );
nextResult->damage( amount, severity, flag );
moveToResultList( effectTarget, nextResult );
}
void EffectBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId )
{
EffectResultPtr nextResult = make_EffectResult( target, 0 );
EffectResultPtr nextResult = make_EffectResult( target, nullptr, 0 );
nextResult->startCombo( actionId );
moveToResultList( target, nextResult );
}
void EffectBuilder::comboSucceed( Entity::CharaPtr& target )
{
EffectResultPtr nextResult = make_EffectResult( target, 0 );
EffectResultPtr nextResult = make_EffectResult( target, nullptr, 0 );
nextResult->comboSucceed();
moveToResultList( target, nextResult );
}
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param )
void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint8_t param )
{
EffectResultPtr nextResult = make_EffectResult( target, 0 );
nextResult->applyStatusEffect( statusId, param );
EffectResultPtr nextResult = make_EffectResult( target, source, 0 );
nextResult->applyStatusEffect( statusId, duration, param );
moveToResultList( target, nextResult );
}

View file

@ -26,7 +26,7 @@ namespace Sapphire::World::Action
void comboSucceed( Entity::CharaPtr& target );
void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param );
void applyStatusEffect( Entity::CharaPtr& target, Entity::CharaPtr& source, uint16_t statusId, uint32_t duration, uint8_t param );
void buildAndSendPackets();

View file

@ -8,10 +8,12 @@ using namespace Sapphire;
using namespace Sapphire::World::Action;
EffectResult::EffectResult( Entity::CharaPtr target, uint64_t runAfter ) :
EffectResult::EffectResult( Entity::CharaPtr target, Entity::CharaPtr source, uint64_t runAfter ) :
m_target( std::move( target ) ),
m_source( std::move( source ) ),
m_delayMs( runAfter ),
m_value( 0 ),
m_value2( 0 ),
m_param0( 0 ),
m_param1( 0 ),
m_type( Common::ActionEffectType::Nothing ),
@ -21,6 +23,11 @@ EffectResult::EffectResult( Entity::CharaPtr target, uint64_t runAfter ) :
}
Entity::CharaPtr EffectResult::getSource() const
{
return m_source;
}
Entity::CharaPtr EffectResult::getTarget() const
{
return m_target;
@ -76,9 +83,10 @@ void EffectResult::comboSucceed()
m_type = Common::ActionEffectType::ComboSucceed;
}
void EffectResult::applyStatusEffect( uint16_t statusId, uint8_t param )
void EffectResult::applyStatusEffect( uint16_t statusId, uint32_t duration, uint8_t param )
{
m_value = statusId;
m_value2 = duration;
m_param2 = param;
m_type = Common::ActionEffectType::ApplyStatusEffect;
@ -121,6 +129,12 @@ void EffectResult::execute()
break;
}
case Common::ActionEffectType::ApplyStatusEffect:
{
m_target->addStatusEffectById( m_value, m_value2, *m_source, m_param2 );
break;
}
default:
break;
}

View file

@ -13,15 +13,16 @@ namespace Sapphire::World::Action
class EffectResult
{
public:
explicit EffectResult( Entity::CharaPtr target, uint64_t delayMs );
explicit EffectResult( Entity::CharaPtr target, Entity::CharaPtr source, uint64_t delayMs );
void damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void restoreMP( uint32_t amount, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void startCombo( uint16_t actionId );
void comboSucceed();
void applyStatusEffect( uint16_t statusId, uint8_t param );
void applyStatusEffect( uint16_t statusId, uint32_t duration, uint8_t param );
Entity::CharaPtr getSource() const;
Entity::CharaPtr getTarget() const;
uint32_t getValue() const;
@ -35,6 +36,7 @@ namespace Sapphire::World::Action
private:
uint64_t m_delayMs;
Entity::CharaPtr m_source;
Entity::CharaPtr m_target;
Common::ActionEffectType m_type;
@ -44,6 +46,7 @@ namespace Sapphire::World::Action
uint8_t m_param2;
uint32_t m_value;
uint32_t m_value2;
Common::ActionEffectResultFlag m_flag;
};
}

View file

@ -354,6 +354,8 @@ bool Sapphire::Entity::Chara::checkAction()
void Sapphire::Entity::Chara::update( uint64_t tickCount )
{
updateStatusEffects();
if( std::difftime( static_cast< time_t >( tickCount ), m_lastTickTime ) > 3000 )
{
onTick();
@ -557,6 +559,9 @@ void Sapphire::Entity::Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEf
/*! \param StatusEffectPtr to be applied to the actor */
void Sapphire::Entity::Chara::addStatusEffectById( uint32_t id, int32_t duration, Entity::Chara& source, uint16_t param )
{
if( hasStatusEffect( id ) ) // todo: check if we want to refresh it or discard and keep the old one
removeSingleStatusEffectById( id, false );
auto effect = StatusEffect::make_StatusEffect( id, source.getAsChara(), getAsChara(), duration, 3000, m_pFw );
effect->setParam( param );
addStatusEffect( effect );
@ -593,19 +598,19 @@ void Sapphire::Entity::Chara::statusEffectFreeSlot( uint8_t slotId )
m_statusEffectFreeSlotQueue.push( slotId );
}
void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id )
void Sapphire::Entity::Chara::removeSingleStatusEffectById( uint32_t id, bool sendPacket )
{
for( auto effectIt : m_statusEffectMap )
{
if( effectIt.second->getId() == id )
{
removeStatusEffect( effectIt.first );
removeStatusEffect( effectIt.first, sendPacket );
break;
}
}
}
void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId )
void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId, bool sendPacket )
{
auto pEffectIt = m_statusEffectMap.find( effectSlotId );
if( pEffectIt == m_statusEffectMap.end() )
@ -616,7 +621,8 @@ void Sapphire::Entity::Chara::removeStatusEffect( uint8_t effectSlotId )
auto pEffect = pEffectIt->second;
pEffect->removeStatus();
sendToInRangeSet( makeActorControl( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() );
if( sendPacket )
sendToInRangeSet( makeActorControl( getId(), StatusEffectLose, pEffect->getId() ), isPlayer() );
m_statusEffectMap.erase( effectSlotId );
@ -694,10 +700,10 @@ void Sapphire::Entity::Chara::updateStatusEffects()
uint32_t duration = effect->getDuration();
uint32_t tickRate = effect->getTickRate();
if( ( currentTimeMs - startTime ) > duration )
if( duration > 0 && ( currentTimeMs - startTime ) > duration )
{
// remove status effect
removeStatusEffect( effectIndex );
removeStatusEffect( effectIndex, true );
// break because removing invalidates iterators
break;
}
@ -746,7 +752,15 @@ void Sapphire::Entity::Chara::updateStatusEffects()
bool Sapphire::Entity::Chara::hasStatusEffect( uint32_t id )
{
return m_statusEffectMap.find( id ) != m_statusEffectMap.end();
//return m_statusEffectMap.find( id ) != m_statusEffectMap.end();
for( auto effectIt : m_statusEffectMap )
{
if( effectIt.second->getId() == id )
{
return true;
}
}
return false;
}
int64_t Sapphire::Entity::Chara::getLastUpdateTime() const

View file

@ -146,9 +146,9 @@ namespace Sapphire::Entity
/// Status effect functions
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect );
void removeStatusEffect( uint8_t effectSlotId );
void removeStatusEffect( uint8_t effectSlotId, bool sendPacket );
void removeSingleStatusEffectById( uint32_t id );
void removeSingleStatusEffectById( uint32_t id, bool sendPacket );
void updateStatusEffects();

View file

@ -1100,8 +1100,6 @@ void Sapphire::Entity::Player::update( uint64_t tickCount )
if( !isAlive() )
return;
updateStatusEffects();
m_lastUpdate = tickCount;
if( !checkAction() )

View file

@ -134,7 +134,7 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( FrameworkPtr pFw,
case ClientTriggerType::RemoveStatusEffect: // Remove status (clicking it off)
{
// todo: check if status can be removed by client from exd
player.removeSingleStatusEffectById( static_cast< uint32_t >( param1 ) );
player.removeSingleStatusEffectById( static_cast< uint32_t >( param1 ), true );
break;
}
case ClientTriggerType::CastCancel: // Cancel cast