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 void onExecute( Sapphire::World::Action::Action& action ) override
{ {
/*
auto sourceChara = action.getSourceChara(); auto sourceChara = action.getSourceChara();
if( !sourceChara->isPlayer() ) if( !sourceChara->isPlayer() )
return; return;
action.getEffectbuilder()->applyStatusEffect( sourceChara, 50, 30 ); action.getEffectbuilder()->applyStatusEffect( sourceChara, sourceChara, 50, 20000, 30 );
*/
sourceChara->getAsPlayer()->addStatusEffectByIdIfNotExist( 50, 20000, *sourceChara, 30 );
} }
}; };

View file

@ -468,19 +468,21 @@ void Action::Action::buildEffects()
return; 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 // send any effect packet added by script or an empty one just to play animation for other players
m_effectBuilder->buildAndSendPackets(); m_effectBuilder->buildAndSendPackets();
return; return;
} }
// we have a valid lut entry // we have a valid lut entry
if( auto player = getSourceChara()->getAsPlayer() ) if( auto player = getSourceChara()->getAsPlayer() )
{ {
player->sendDebug( "Hit target: pot: {} (c: {}, f: {}, r: {}), heal pot: {}, mpp: {}", player->sendDebug( "dpot: {} (dcpot: {}, ddpot: {}), hpot: {}, shpot: {}, ss: {}, ts: {}, gmp: {}, gjob: {}",
m_lutEntry.potency, m_lutEntry.comboPotency, m_lutEntry.flankPotency, m_lutEntry.rearPotency, m_lutEntry.damagePotency, m_lutEntry.damageComboPotency, m_lutEntry.damageDirectionalPotency,
m_lutEntry.curePotency, m_lutEntry.restoreMPPercentage ); 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 // 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 ) 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 ); m_effectBuilder->damage( actor, actor, dmg.first, dmg.second );
if( dmg.first > 0 ) if( dmg.first > 0 )
@ -505,15 +507,15 @@ void Action::Action::buildEffects()
if( !isComboAction() || isCorrectCombo() ) 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 ); 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; 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 ); 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; 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; 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(); m_effectBuilder->buildAndSendPackets();
// at this point we're done with it and no longer need it // 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; return false;
// npc actions/non player actions // npc actions/non player actions
if( m_actionData->classJob == -1 ) if( m_actionData->classJob == -1 && !m_actionData->isRoleAction )
return false; return false;
if( player.getLevel() < m_actionData->classJobLevel ) if( player.getLevel() < m_actionData->classJobLevel )
@ -574,7 +586,7 @@ bool Action::Action::playerPreCheck( Entity::Player& player )
auto currentClass = player.getClass(); auto currentClass = player.getClass();
auto actionClass = static_cast< Common::ClassJob >( m_actionData->classJob ); 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 // check if not a base class action
auto exdData = m_pFw->get< Data::ExdDataGenerated >(); 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 ) if( kind != ObjKind::BattleNpc && kind != ObjKind::Player )
return false; return false;
if( !m_canTargetSelf && chara->getId() == m_pSource->getId() ) if( m_lutEntry.damagePotency > 0 && chara->getId() == m_pSource->getId() ) // !m_canTargetSelf
return false; 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; 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; 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 false;
return true; return true;
} }
@ -809,8 +821,8 @@ Sapphire::Entity::CharaPtr Action::Action::getHitChara()
bool Action::Action::hasValidLutEntry() const bool Action::Action::hasValidLutEntry() const
{ {
return m_lutEntry.potency != 0 || m_lutEntry.comboPotency != 0 || m_lutEntry.flankPotency != 0 || m_lutEntry.frontPotency != 0 || return m_lutEntry.damagePotency != 0 || m_lutEntry.healPotency != 0 || m_lutEntry.selfHealPotency != 0 || m_lutEntry.selfStatus != 0 ||
m_lutEntry.rearPotency != 0 || m_lutEntry.curePotency != 0 || m_lutEntry.restoreMPPercentage != 0; m_lutEntry.targetStatus != 0 || m_lutEntry.gainMPPercentage != 0 || m_lutEntry.gainJobResource != 0;
} }
Action::EffectBuilderPtr Action::Action::getEffectbuilder() Action::EffectBuilderPtr Action::Action::getEffectbuilder()

View file

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

View file

@ -7,13 +7,19 @@ namespace Sapphire::World::Action
{ {
struct ActionEntry struct ActionEntry
{ {
uint16_t potency; uint16_t damagePotency;
uint16_t comboPotency; uint16_t damageComboPotency;
uint16_t flankPotency; uint16_t damageDirectionalPotency;
uint16_t frontPotency; uint16_t healPotency;
uint16_t rearPotency; uint16_t selfHealPotency;
uint16_t curePotency; uint16_t selfStatus;
uint16_t restoreMPPercentage; uint16_t selfStatusDuration;
uint16_t selfStatusParam;
uint16_t targetStatus;
uint16_t targetStatusDuration;
uint16_t targetStatusParam;
uint16_t gainMPPercentage;
uint16_t gainJobResource;
}; };
class ActionLut 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 ) 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 ); nextResult->heal( amount, severity, flag );
moveToResultList( effectTarget, nextResult ); moveToResultList( effectTarget, nextResult );
} }
void EffectBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionEffectResultFlag flag ) 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 ); nextResult->restoreMP( amount, flag );
moveToResultList( target, nextResult ); moveToResultList( target, nextResult );
} }
void EffectBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag ) 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 ); nextResult->damage( amount, severity, flag );
moveToResultList( effectTarget, nextResult ); moveToResultList( effectTarget, nextResult );
} }
void EffectBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId ) 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 ); nextResult->startCombo( actionId );
moveToResultList( target, nextResult ); moveToResultList( target, nextResult );
} }
void EffectBuilder::comboSucceed( Entity::CharaPtr& target ) void EffectBuilder::comboSucceed( Entity::CharaPtr& target )
{ {
EffectResultPtr nextResult = make_EffectResult( target, 0 ); EffectResultPtr nextResult = make_EffectResult( target, nullptr, 0 );
nextResult->comboSucceed(); nextResult->comboSucceed();
moveToResultList( target, nextResult ); 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 ); EffectResultPtr nextResult = make_EffectResult( target, source, 0 );
nextResult->applyStatusEffect( statusId, param ); nextResult->applyStatusEffect( statusId, duration, param );
moveToResultList( target, nextResult ); moveToResultList( target, nextResult );
} }

View file

@ -26,7 +26,7 @@ namespace Sapphire::World::Action
void comboSucceed( Entity::CharaPtr& target ); 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(); void buildAndSendPackets();

View file

@ -8,10 +8,12 @@ using namespace Sapphire;
using namespace Sapphire::World::Action; 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_target( std::move( target ) ),
m_source( std::move( source ) ),
m_delayMs( runAfter ), m_delayMs( runAfter ),
m_value( 0 ), m_value( 0 ),
m_value2( 0 ),
m_param0( 0 ), m_param0( 0 ),
m_param1( 0 ), m_param1( 0 ),
m_type( Common::ActionEffectType::Nothing ), 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 Entity::CharaPtr EffectResult::getTarget() const
{ {
return m_target; return m_target;
@ -76,9 +83,10 @@ void EffectResult::comboSucceed()
m_type = Common::ActionEffectType::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_value = statusId;
m_value2 = duration;
m_param2 = param; m_param2 = param;
m_type = Common::ActionEffectType::ApplyStatusEffect; m_type = Common::ActionEffectType::ApplyStatusEffect;
@ -121,6 +129,12 @@ void EffectResult::execute()
break; break;
} }
case Common::ActionEffectType::ApplyStatusEffect:
{
m_target->addStatusEffectById( m_value, m_value2, *m_source, m_param2 );
break;
}
default: default:
break; break;
} }

View file

@ -13,15 +13,16 @@ namespace Sapphire::World::Action
class EffectResult class EffectResult
{ {
public: 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 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 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 restoreMP( uint32_t amount, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None );
void startCombo( uint16_t actionId ); void startCombo( uint16_t actionId );
void comboSucceed(); 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; Entity::CharaPtr getTarget() const;
uint32_t getValue() const; uint32_t getValue() const;
@ -35,6 +36,7 @@ namespace Sapphire::World::Action
private: private:
uint64_t m_delayMs; uint64_t m_delayMs;
Entity::CharaPtr m_source;
Entity::CharaPtr m_target; Entity::CharaPtr m_target;
Common::ActionEffectType m_type; Common::ActionEffectType m_type;
@ -44,6 +46,7 @@ namespace Sapphire::World::Action
uint8_t m_param2; uint8_t m_param2;
uint32_t m_value; uint32_t m_value;
uint32_t m_value2;
Common::ActionEffectResultFlag m_flag; Common::ActionEffectResultFlag m_flag;
}; };
} }

View file

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

View file

@ -146,9 +146,9 @@ namespace Sapphire::Entity
/// Status effect functions /// Status effect functions
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect ); 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(); void updateStatusEffects();

View file

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

View file

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