[General] Use hard synchronization for melee attack animations

Previously, each client chose its own attack animations for DedicatedPlayers and DedicatedActors based on the direction they were walking in, which however led to desyncs for players with "Always Use Best Attack" enabled and for creatures which pick their attack animations randomly.
This commit is contained in:
David Cernat 2019-11-29 10:39:57 +02:00
parent a385fcdd87
commit 140c1c9c12
7 changed files with 77 additions and 18 deletions

View File

@ -609,6 +609,9 @@ namespace MWMechanics
mAttack = true; // attack starts just now mAttack = true; // attack starts just now
characterController.setAttackingOrSpell(true); characterController.setAttackingOrSpell(true);
if (!distantCombat)
characterController.setAIAttackType(chooseBestAttack(weapon));
/* /*
Start of tes3mp addition Start of tes3mp addition
@ -620,6 +623,7 @@ namespace MWMechanics
{ {
MechanicsHelper::resetAttack(localAttack); MechanicsHelper::resetAttack(localAttack);
localAttack->type = distantCombat ? mwmp::Attack::RANGED : mwmp::Attack::MELEE; localAttack->type = distantCombat ? mwmp::Attack::RANGED : mwmp::Attack::MELEE;
localAttack->attackAnimation = characterController.getAttackType();
localAttack->pressed = true; localAttack->pressed = true;
localAttack->shouldSend = true; localAttack->shouldSend = true;
} }
@ -627,9 +631,6 @@ namespace MWMechanics
End of tes3mp addition End of tes3mp addition
*/ */
if (!distantCombat)
characterController.setAIAttackType(chooseBestAttack(weapon));
mStrength = Misc::Rng::rollClosedProbability(); mStrength = Misc::Rng::rollClosedProbability();
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();

View File

@ -1569,7 +1569,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
/* /*
Start of tes3mp addition Start of tes3mp addition
If this mPtr belongs to a LocalPlayer or LocalActor, get their Attack and prepare If this mPtr belongs to a LocalPlayer or LocalActor, get their Cast and prepare
it for sending it for sending
*/ */
mwmp::Cast *localCast = MechanicsHelper::getLocalCast(mPtr); mwmp::Cast *localCast = MechanicsHelper::getLocalCast(mPtr);
@ -1694,17 +1694,7 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
} }
else else
{ {
/* if(mPtr == getPlayer())
Start of tes3mp change (major)
We need DedicatedPlayers and DedicatedActors to not have their attacks
cancelled here, so additional conditions have been added for them
*/
if(mPtr == getPlayer() || mwmp::PlayerList::isDedicatedPlayer(mPtr) ||
mwmp::Main::get().getCellController()->isDedicatedActor(mPtr))
/*
End of tes3mp change (major)
*/
{ {
if (Settings::Manager::getBool("best attack", "Game")) if (Settings::Manager::getBool("best attack", "Game"))
{ {
@ -1723,7 +1713,37 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
{ {
setAttackTypeBasedOnMovement(); setAttackTypeBasedOnMovement();
} }
/*
Start of tes3mp addition
Record the attack animation chosen so we can send it in the next PlayerAttack packet
*/
mwmp::Attack *localAttack = MechanicsHelper::getLocalAttack(mPtr);
if (localAttack)
localAttack->attackAnimation = mAttackType;
/*
End of tes3mp addition
*/
} }
/*
Start of tes3mp addition
If this is a DedicatedPlayer or DedicatedActor, use the attack animation received
in the latest Attack packet about them
*/
else
{
mwmp::Attack *dedicatedAttack = MechanicsHelper::getDedicatedAttack(mPtr);
if (dedicatedAttack)
mAttackType = dedicatedAttack->attackAnimation;
}
/*
End of tes3mp addition
*/
// else if (mPtr != getPlayer()) use mAttackType set by AiCombat // else if (mPtr != getPlayer()) use mAttackType set by AiCombat
startKey = mAttackType+" start"; startKey = mAttackType+" start";
stopKey = mAttackType+" min attack"; stopKey = mAttackType+" min attack";
@ -2935,6 +2955,19 @@ float CharacterController::getAttackStrength() const
return mAttackStrength; return mAttackStrength;
} }
/*
Start of tes3mp addition
Make it possible to get the current attack type from elsewhere in the code
*/
std::string CharacterController::getAttackType() const
{
return mAttackType;
}
/*
End of tes3mp addition
*/
void CharacterController::setActive(int active) void CharacterController::setActive(int active)
{ {
mAnimation->setActive(active); mAnimation->setActive(active);

View File

@ -295,6 +295,16 @@ public:
float getAttackStrength() const; float getAttackStrength() const;
/*
Start of tes3mp addition
Make it possible to get the current attack type from elsewhere in the code
*/
std::string getAttackType() const;
/*
End of tes3mp addition
*/
/// @see Animation::setActive /// @see Animation::setActive
void setActive(int active); void setActive(int active);

View File

@ -282,6 +282,11 @@ void MechanicsHelper::processAttack(Attack attack, const MWWorld::Ptr& attacker)
if (attack.success) if (attack.success)
LOG_APPEND(TimedLog::LOG_VERBOSE, "- damage: %f", attack.damage); LOG_APPEND(TimedLog::LOG_VERBOSE, "- damage: %f", attack.damage);
} }
else
{
if (attack.type == attack.MELEE)
LOG_APPEND(TimedLog::LOG_VERBOSE, "- animation: %s", attack.attackAnimation.c_str());
}
MWMechanics::CreatureStats &attackerStats = attacker.getClass().getCreatureStats(attacker); MWMechanics::CreatureStats &attackerStats = attacker.getClass().getCreatureStats(attacker);
MWWorld::Ptr victim; MWWorld::Ptr victim;

View File

@ -65,13 +65,15 @@ namespace mwmp
Target target; Target target;
char type; // 0 - melee, 1 - ranged
enum TYPE enum TYPE
{ {
MELEE = 0, MELEE = 0,
RANGED RANGED
}; };
char type;
std::string attackAnimation;
std::string rangedWeaponId; std::string rangedWeaponId;
std::string rangedAmmoId; std::string rangedAmmoId;

View File

@ -31,7 +31,11 @@ void PacketActorAttack::Actor(BaseActor &actor, bool send)
RW(actor.attack.isHit, send); RW(actor.attack.isHit, send);
if (actor.attack.type == mwmp::Attack::RANGED) if (actor.attack.type == mwmp::Attack::MELEE)
{
RW(actor.attack.attackAnimation, send);
}
else if (actor.attack.type == mwmp::Attack::RANGED)
{ {
RW(actor.attack.attackStrength, send); RW(actor.attack.attackStrength, send);
RW(actor.attack.rangedWeaponId, send); RW(actor.attack.rangedWeaponId, send);

View File

@ -32,7 +32,11 @@ void PacketPlayerAttack::Packet(RakNet::BitStream *bs, bool send)
RW(player->attack.isHit, send); RW(player->attack.isHit, send);
if (player->attack.type == mwmp::Attack::RANGED) if (player->attack.type == mwmp::Attack::MELEE)
{
RW(player->attack.attackAnimation, send);
}
else if (player->attack.type == mwmp::Attack::RANGED)
{ {
RW(player->attack.attackStrength, send); RW(player->attack.attackStrength, send);
RW(player->attack.rangedWeaponId, send); RW(player->attack.rangedWeaponId, send);