From d3f3fb5d05e9e096303644670ee03ecc5aa35b18 Mon Sep 17 00:00:00 2001 From: David Cernat Date: Sat, 8 Apr 2017 08:59:21 +0300 Subject: [PATCH] [Client] Correctly implement movement animation sync for NPCs --- apps/openmw/mwmechanics/character.cpp | 99 +++++++++++++++++++ apps/openmw/mwmp/Cell.cpp | 41 +++++++- apps/openmw/mwmp/DedicatedActor.cpp | 32 +----- apps/openmw/mwmp/DedicatedActor.hpp | 1 - apps/openmw/mwmp/LocalActor.cpp | 47 +-------- apps/openmw/mwmp/LocalActor.hpp | 2 - components/openmw-mp/Base/BaseActor.hpp | 10 ++ components/openmw-mp/Base/BaseEvent.hpp | 10 ++ components/openmw-mp/Base/BaseStructs.hpp | 30 ++++++ .../Packets/World/PacketActorFrame.cpp | 28 ++++++ 10 files changed, 227 insertions(+), 73 deletions(-) create mode 100644 components/openmw-mp/Base/BaseStructs.hpp diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index f1778bde0..7331d51a5 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -27,8 +27,19 @@ #include +/* + Start of tes3mp addition + + Include additional headers for multiplayer purposes +*/ +#include #include "../mwmp/Main.hpp" +#include "../mwmp/CellController.hpp" #include "../mwmp/LocalPlayer.hpp" +#include "../mwmp/LocalActor.hpp" +/* + End of tes3mp addition +*/ #include "../mwrender/animation.hpp" @@ -1577,6 +1588,25 @@ void CharacterController::updateAnimQueue() void CharacterController::update(float duration) { + /* + Start of tes3mp addition + + Keep track of LocalActor and DedicatedActor objects so as to reuse them + */ + bool hasLocalActorRecord = mwmp::Main::get().getCellController()->hasLocalActorRecord(mPtr); + bool hasDedicatedActorRecord = mwmp::Main::get().getCellController()->hasDedicatedActorRecord(mPtr); + mwmp::LocalActor *localActor; + mwmp::DedicatedActor *dedicatedActor; + + if (hasLocalActorRecord) + localActor = mwmp::Main::get().getCellController()->getLocalActor(mPtr); + + if (hasDedicatedActorRecord) + dedicatedActor = mwmp::Main::get().getCellController()->getDedicatedActor(mPtr); + /* + End of tes3mp addition + */ + MWBase::World *world = MWBase::Environment::get().getWorld(); const MWWorld::Class &cls = mPtr.getClass(); osg::Vec3f movement(0.f, 0.f, 0.f); @@ -1862,7 +1892,36 @@ void CharacterController::update(float duration) else forcestateupdate = updateCreatureState() || forcestateupdate; + /* + Start of tes3mp addition + + Save or load animation states for this actor, depending on whether it's a local + or dedicated one + */ + if (hasLocalActorRecord) + { + localActor->hasAnimStates = true; + localActor->animStates.idlestate = idlestate; + localActor->animStates.movestate = movestate; + localActor->animStates.jumpstate = jumpstate; + localActor->animStates.forcestateupdate = forcestateupdate; + } + else if (hasDedicatedActorRecord) + { + if (dedicatedActor->hasAnimStates) + { + idlestate = CharacterState(dedicatedActor->animStates.idlestate); + movestate = CharacterState(dedicatedActor->animStates.movestate); + jumpstate = JumpingState(dedicatedActor->animStates.jumpstate); + forcestateupdate = dedicatedActor->animStates.forcestateupdate; + } + } + /* + End of tes3mp addition + */ + refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate); + updateIdleStormState(inwater); } @@ -1952,6 +2011,27 @@ void CharacterController::update(float duration) if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr)) moved.z() = 1.0; + /* + Start of tes3mp addition + + Save or load movement velocity based on whether this is a local or dedicated actor + */ + if (hasLocalActorRecord) + { + localActor->hasMovement = true; + localActor->movement.x = moved.x(); + localActor->movement.y = moved.y(); + localActor->movement.z = moved.z(); + } + else if (hasDedicatedActorRecord) + { + if (dedicatedActor->hasMovement) + moved = osg::Vec3f(dedicatedActor->movement.x, dedicatedActor->movement.y, dedicatedActor->movement.z); + } + /* + End of tes3mp addition + */ + // Update movement if(mMovementAnimationControlled && mPtr.getClass().isActor()) world->queueMovement(mPtr, moved); @@ -2079,6 +2159,25 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int mAnimation->play(groupname, Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f, ((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1, loopfallback); + + /* + Start of tes3mp addition + + If we are the cell authority over this actor, we need to record this new + animation for it + */ + if (mwmp::Main::get().getCellController()->hasLocalActorRecord(mPtr)) + { + mwmp::LocalActor *actor = mwmp::Main::get().getCellController()->getLocalActor(mPtr); + actor->hasAnimation = true; + actor->animation.groupname = groupname; + actor->animation.mode = mode; + actor->animation.count = count; + actor->animation.persist = persist; + } + /* + End of tes3mp addition + */ } else if(mode == 0) { diff --git a/apps/openmw/mwmp/Cell.cpp b/apps/openmw/mwmp/Cell.cpp index 95130ed6e..e2d749be5 100644 --- a/apps/openmw/mwmp/Cell.cpp +++ b/apps/openmw/mwmp/Cell.cpp @@ -57,7 +57,28 @@ void Cell::updateLocal() worldObject.movementFlags = actor->movementFlags; worldObject.headPitch = actor->headPitch; worldObject.headYaw = actor->headYaw; - + worldObject.hasAnimation = actor->hasAnimation; + worldObject.hasAnimStates = actor->hasAnimStates; + worldObject.hasMovement = actor->hasMovement; + + if (actor->hasAnimation) + { + worldObject.animation = actor->animation; + } + + if (actor->hasAnimStates) + { + worldObject.animStates = actor->animStates; + } + + if (actor->hasMovement) + { + worldObject.movement = actor->movement; + } + + actor->hasAnimation = false; + actor->hasAnimStates = false; + actor->hasMovement = false; worldEvent->addObject(worldObject); ++it; @@ -161,6 +182,24 @@ void Cell::readCellFrame(WorldEvent& worldEvent) actor->direction = worldObject.direction; actor->drawState = worldObject.drawState; actor->movementFlags = worldObject.movementFlags; + actor->hasAnimation = worldObject.hasAnimation; + actor->hasAnimStates = worldObject.hasAnimStates; + actor->hasMovement = worldObject.hasMovement; + + if (actor->hasAnimation) + { + actor->animation = worldObject.animation; + } + + if (actor->hasAnimStates) + { + actor->animStates = worldObject.animStates; + } + + if (actor->hasMovement) + { + actor->movement = worldObject.movement; + } } } } diff --git a/apps/openmw/mwmp/DedicatedActor.cpp b/apps/openmw/mwmp/DedicatedActor.cpp index b5f31ba32..ff577fd69 100644 --- a/apps/openmw/mwmp/DedicatedActor.cpp +++ b/apps/openmw/mwmp/DedicatedActor.cpp @@ -1,3 +1,5 @@ +#include + #include "../mwbase/environment.hpp" #include "../mwmechanics/mechanicsmanagerimp.hpp" #include "../mwmechanics/movement.hpp" @@ -25,7 +27,6 @@ void DedicatedActor::update(float dt) { move(dt); setDrawState(); - setMovementFlags(); setAnimation(); } @@ -35,11 +36,6 @@ void DedicatedActor::move(float dt) world->moveObject(ptr, position.pos[0], position.pos[1], position.pos[2]); - MWMechanics::Movement& move = ptr.getClass().getMovementSettings(ptr); - move.mPosition[0] = direction.pos[0]; - move.mPosition[1] = direction.pos[1]; - move.mPosition[2] = direction.pos[2]; - world->rotateObject(ptr, position.rot[0], position.rot[1], position.rot[2]); } @@ -55,30 +51,12 @@ void DedicatedActor::setDrawState() ptr.getClass().getNpcStats(ptr).setDrawState(DrawState_Spell); } -void DedicatedActor::setMovementFlags() -{ - using namespace MWMechanics; - - MWMechanics::NpcStats *ptrNpcStats = &ptr.getClass().getNpcStats(ptr); - ptrNpcStats->setMovementFlag(CreatureStats::Flag_Run, (movementFlags & CreatureStats::Flag_Run) != 0); - ptrNpcStats->setMovementFlag(CreatureStats::Flag_Sneak, (movementFlags & CreatureStats::Flag_Sneak) != 0); - ptrNpcStats->setMovementFlag(CreatureStats::Flag_ForceJump, (movementFlags & CreatureStats::Flag_ForceJump) != 0); - ptrNpcStats->setMovementFlag(CreatureStats::Flag_ForceMoveJump, (movementFlags & CreatureStats::Flag_ForceMoveJump) != 0); -} - void DedicatedActor::setAnimation() { - MWBase::World *world = MWBase::Environment::get().getWorld(); - - if (headPitch != -1 && headYaw != -1) + if (hasAnimation) { - MWRender::Animation *animation = world->getAnimation(ptr); - - if (animation) - { - animation->setHeadPitch(headPitch); - animation->setHeadYaw(headYaw); - } + MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(ptr, + animation.groupname, animation.mode, animation.count, animation.persist); } } diff --git a/apps/openmw/mwmp/DedicatedActor.hpp b/apps/openmw/mwmp/DedicatedActor.hpp index a2ab60eee..d7d3d4dbd 100644 --- a/apps/openmw/mwmp/DedicatedActor.hpp +++ b/apps/openmw/mwmp/DedicatedActor.hpp @@ -17,7 +17,6 @@ namespace mwmp void update(float dt); void move(float dt); void setDrawState(); - void setMovementFlags(); void setAnimation(); MWWorld::Ptr getPtr(); diff --git a/apps/openmw/mwmp/LocalActor.cpp b/apps/openmw/mwmp/LocalActor.cpp index ca4635c63..768fbe4a3 100644 --- a/apps/openmw/mwmp/LocalActor.cpp +++ b/apps/openmw/mwmp/LocalActor.cpp @@ -1,3 +1,5 @@ +#include + #include "../mwbase/environment.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" @@ -14,6 +16,9 @@ LocalActor::LocalActor() { headPitch = -1; headYaw = -1; + hasAnimation = false; + hasAnimStates = false; + hasMovement = false; } LocalActor::~LocalActor() @@ -25,18 +30,11 @@ void LocalActor::update() { updatePosition(); updateDrawState(); - updateMovementFlags(); - updateAnimation(); } void LocalActor::updatePosition() { position = ptr.getRefData().getPosition(); - - MWMechanics::Movement &move = ptr.getClass().getMovementSettings(ptr); - direction.pos[0] = move.mPosition[0]; - direction.pos[1] = move.mPosition[1]; - direction.pos[2] = move.mPosition[2]; } void LocalActor::updateDrawState() @@ -44,41 +42,6 @@ void LocalActor::updateDrawState() drawState = ptr.getClass().getNpcStats(ptr).getDrawState(); } -void LocalActor::updateMovementFlags() -{ - using namespace MWMechanics; - - MWBase::World *world = MWBase::Environment::get().getWorld(); - MWMechanics::NpcStats ptrNpcStats = ptr.getClass().getNpcStats(ptr); - - bool run = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Run); - bool sneak = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Sneak); - bool forceJump = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceJump); - bool forceMoveJump = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceMoveJump); - -#define __SETFLAG(flag, value) (value) ? (movementFlags | flag) : (movementFlags & ~flag) - - movementFlags = __SETFLAG(CreatureStats::Flag_Sneak, sneak); - movementFlags = __SETFLAG(CreatureStats::Flag_Run, run); - movementFlags = __SETFLAG(CreatureStats::Flag_ForceJump, forceJump); - movementFlags = __SETFLAG(CreatureStats::Flag_ForceMoveJump, forceMoveJump); - -#undef __SETFLAG -} - -void LocalActor::updateAnimation() -{ - MWBase::World *world = MWBase::Environment::get().getWorld(); - - MWRender::Animation *animation = world->getAnimation(ptr); - - if (animation) - { - headPitch = animation->getHeadPitch(); - headYaw = animation->getHeadYaw(); - } -} - MWWorld::Ptr LocalActor::getPtr() { return ptr; diff --git a/apps/openmw/mwmp/LocalActor.hpp b/apps/openmw/mwmp/LocalActor.hpp index e844bc198..01f5f9957 100644 --- a/apps/openmw/mwmp/LocalActor.hpp +++ b/apps/openmw/mwmp/LocalActor.hpp @@ -17,8 +17,6 @@ namespace mwmp void updatePosition(); void updateDrawState(); - void updateMovementFlags(); - void updateAnimation(); MWWorld::Ptr getPtr(); void setPtr(const MWWorld::Ptr& newPtr); diff --git a/components/openmw-mp/Base/BaseActor.hpp b/components/openmw-mp/Base/BaseActor.hpp index e915958b0..7f312a735 100644 --- a/components/openmw-mp/Base/BaseActor.hpp +++ b/components/openmw-mp/Base/BaseActor.hpp @@ -2,6 +2,7 @@ #define OPENMW_BASEACTOR_HPP #include +#include namespace mwmp { @@ -25,6 +26,15 @@ namespace mwmp float headPitch; float headYaw; + + Animation animation; + bool hasAnimation; + + AnimStates animStates; + bool hasAnimStates; + + Movement movement; + bool hasMovement; }; } diff --git a/components/openmw-mp/Base/BaseEvent.hpp b/components/openmw-mp/Base/BaseEvent.hpp index d87d38f43..58b815b93 100644 --- a/components/openmw-mp/Base/BaseEvent.hpp +++ b/components/openmw-mp/Base/BaseEvent.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace mwmp @@ -62,6 +63,15 @@ namespace mwmp std::string varName; ContainerChanges containerChanges; + + Animation animation; + bool hasAnimation; + + AnimStates animStates; + bool hasAnimStates; + + Movement movement; + bool hasMovement; }; struct ObjectChanges diff --git a/components/openmw-mp/Base/BaseStructs.hpp b/components/openmw-mp/Base/BaseStructs.hpp new file mode 100644 index 000000000..29c3778db --- /dev/null +++ b/components/openmw-mp/Base/BaseStructs.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_BASESTRUCTS_HPP +#define OPENMW_BASESTRUCTS_HPP + +namespace mwmp +{ + struct Animation + { + std::string groupname; + int mode; + int count; + bool persist; + }; + + struct AnimStates + { + int idlestate; + int movestate; + int jumpstate; + bool forcestateupdate; + }; + + struct Movement + { + float x; + float y; + float z; + }; +} + +#endif //OPENMW_BASESTRUCTS_HPP diff --git a/components/openmw-mp/Packets/World/PacketActorFrame.cpp b/components/openmw-mp/Packets/World/PacketActorFrame.cpp index 1e65b39c3..669e49826 100644 --- a/components/openmw-mp/Packets/World/PacketActorFrame.cpp +++ b/components/openmw-mp/Packets/World/PacketActorFrame.cpp @@ -1,4 +1,5 @@ #include +#include #include "PacketActorFrame.hpp" using namespace mwmp; @@ -44,6 +45,33 @@ void PacketActorFrame::Packet(RakNet::BitStream *bs, bool send) RW(worldObject.headPitch, send); RW(worldObject.headYaw, send); + RW(worldObject.hasAnimation, send); + RW(worldObject.hasAnimStates, send); + RW(worldObject.hasMovement, send); + + if (worldObject.hasAnimation) + { + RW(worldObject.animation.groupname, send); + RW(worldObject.animation.mode, send); + RW(worldObject.animation.count, send); + RW(worldObject.animation.persist, send); + } + + if (worldObject.hasAnimStates) + { + RW(worldObject.animStates.idlestate, send); + RW(worldObject.animStates.movestate, send); + RW(worldObject.animStates.jumpstate, send); + RW(worldObject.animStates.forcestateupdate, send); + } + + if (worldObject.hasMovement) + { + RW(worldObject.movement.x, send); + RW(worldObject.movement.y, send); + RW(worldObject.movement.y, send); + } + if (!send) { event->objectChanges.objects.push_back(worldObject);