1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-03 20:49:41 +00:00

[Client] Correctly implement movement animation sync for NPCs

This commit is contained in:
David Cernat 2017-04-08 08:59:21 +03:00
parent 5b43e62c50
commit d3f3fb5d05
10 changed files with 226 additions and 72 deletions

View file

@ -27,8 +27,19 @@
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include <components/openmw-mp/Log.hpp>
#include "../mwmp/Main.hpp" #include "../mwmp/Main.hpp"
#include "../mwmp/CellController.hpp"
#include "../mwmp/LocalPlayer.hpp" #include "../mwmp/LocalPlayer.hpp"
#include "../mwmp/LocalActor.hpp"
/*
End of tes3mp addition
*/
#include "../mwrender/animation.hpp" #include "../mwrender/animation.hpp"
@ -1577,6 +1588,25 @@ void CharacterController::updateAnimQueue()
void CharacterController::update(float duration) 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(); MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Class &cls = mPtr.getClass(); const MWWorld::Class &cls = mPtr.getClass();
osg::Vec3f movement(0.f, 0.f, 0.f); osg::Vec3f movement(0.f, 0.f, 0.f);
@ -1862,7 +1892,36 @@ void CharacterController::update(float duration)
else else
forcestateupdate = updateCreatureState() || forcestateupdate; 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); refreshCurrentAnims(idlestate, movestate, jumpstate, forcestateupdate);
updateIdleStormState(inwater); updateIdleStormState(inwater);
} }
@ -1952,6 +2011,27 @@ void CharacterController::update(float duration)
if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr)) if (mFloatToSurface && cls.isActor() && cls.getCreatureStats(mPtr).isDead() && cls.canSwim(mPtr))
moved.z() = 1.0; 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 // Update movement
if(mMovementAnimationControlled && mPtr.getClass().isActor()) if(mMovementAnimationControlled && mPtr.getClass().isActor())
world->queueMovement(mPtr, moved); world->queueMovement(mPtr, moved);
@ -2079,6 +2159,25 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
mAnimation->play(groupname, Priority_Default, mAnimation->play(groupname, Priority_Default,
MWRender::Animation::BlendMask_All, false, 1.0f, MWRender::Animation::BlendMask_All, false, 1.0f,
((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1, loopfallback); ((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) else if(mode == 0)
{ {

View file

@ -57,7 +57,28 @@ void Cell::updateLocal()
worldObject.movementFlags = actor->movementFlags; worldObject.movementFlags = actor->movementFlags;
worldObject.headPitch = actor->headPitch; worldObject.headPitch = actor->headPitch;
worldObject.headYaw = actor->headYaw; 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); worldEvent->addObject(worldObject);
++it; ++it;
@ -161,6 +182,24 @@ void Cell::readCellFrame(WorldEvent& worldEvent)
actor->direction = worldObject.direction; actor->direction = worldObject.direction;
actor->drawState = worldObject.drawState; actor->drawState = worldObject.drawState;
actor->movementFlags = worldObject.movementFlags; 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;
}
} }
} }
} }

View file

@ -1,3 +1,5 @@
#include <components/openmw-mp/Log.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwmechanics/mechanicsmanagerimp.hpp" #include "../mwmechanics/mechanicsmanagerimp.hpp"
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
@ -25,7 +27,6 @@ void DedicatedActor::update(float dt)
{ {
move(dt); move(dt);
setDrawState(); setDrawState();
setMovementFlags();
setAnimation(); setAnimation();
} }
@ -35,11 +36,6 @@ void DedicatedActor::move(float dt)
world->moveObject(ptr, position.pos[0], position.pos[1], position.pos[2]); 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]); 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); 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() void DedicatedActor::setAnimation()
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); if (hasAnimation)
if (headPitch != -1 && headYaw != -1)
{ {
MWRender::Animation *animation = world->getAnimation(ptr); MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(ptr,
animation.groupname, animation.mode, animation.count, animation.persist);
if (animation)
{
animation->setHeadPitch(headPitch);
animation->setHeadYaw(headYaw);
}
} }
} }

View file

@ -17,7 +17,6 @@ namespace mwmp
void update(float dt); void update(float dt);
void move(float dt); void move(float dt);
void setDrawState(); void setDrawState();
void setMovementFlags();
void setAnimation(); void setAnimation();
MWWorld::Ptr getPtr(); MWWorld::Ptr getPtr();

View file

@ -1,3 +1,5 @@
#include <components/openmw-mp/Log.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
@ -14,6 +16,9 @@ LocalActor::LocalActor()
{ {
headPitch = -1; headPitch = -1;
headYaw = -1; headYaw = -1;
hasAnimation = false;
hasAnimStates = false;
hasMovement = false;
} }
LocalActor::~LocalActor() LocalActor::~LocalActor()
@ -25,18 +30,11 @@ void LocalActor::update()
{ {
updatePosition(); updatePosition();
updateDrawState(); updateDrawState();
updateMovementFlags();
updateAnimation();
} }
void LocalActor::updatePosition() void LocalActor::updatePosition()
{ {
position = ptr.getRefData().getPosition(); 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() void LocalActor::updateDrawState()
@ -44,41 +42,6 @@ void LocalActor::updateDrawState()
drawState = ptr.getClass().getNpcStats(ptr).getDrawState(); 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() MWWorld::Ptr LocalActor::getPtr()
{ {
return ptr; return ptr;

View file

@ -17,8 +17,6 @@ namespace mwmp
void updatePosition(); void updatePosition();
void updateDrawState(); void updateDrawState();
void updateMovementFlags();
void updateAnimation();
MWWorld::Ptr getPtr(); MWWorld::Ptr getPtr();
void setPtr(const MWWorld::Ptr& newPtr); void setPtr(const MWWorld::Ptr& newPtr);

View file

@ -2,6 +2,7 @@
#define OPENMW_BASEACTOR_HPP #define OPENMW_BASEACTOR_HPP
#include <components/esm/loadcell.hpp> #include <components/esm/loadcell.hpp>
#include <components/openmw-mp/Base/BaseStructs.hpp>
namespace mwmp namespace mwmp
{ {
@ -25,6 +26,15 @@ namespace mwmp
float headPitch; float headPitch;
float headYaw; float headYaw;
Animation animation;
bool hasAnimation;
AnimStates animStates;
bool hasAnimStates;
Movement movement;
bool hasMovement;
}; };
} }

View file

@ -3,6 +3,7 @@
#include <components/esm/loadcell.hpp> #include <components/esm/loadcell.hpp>
#include <components/esm/cellref.hpp> #include <components/esm/cellref.hpp>
#include <components/openmw-mp/Base/BaseStructs.hpp>
#include <RakNetTypes.h> #include <RakNetTypes.h>
namespace mwmp namespace mwmp
@ -62,6 +63,15 @@ namespace mwmp
std::string varName; std::string varName;
ContainerChanges containerChanges; ContainerChanges containerChanges;
Animation animation;
bool hasAnimation;
AnimStates animStates;
bool hasAnimStates;
Movement movement;
bool hasMovement;
}; };
struct ObjectChanges struct ObjectChanges

View file

@ -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

View file

@ -1,4 +1,5 @@
#include <components/openmw-mp/NetworkMessages.hpp> #include <components/openmw-mp/NetworkMessages.hpp>
#include <components/openmw-mp/Log.hpp>
#include "PacketActorFrame.hpp" #include "PacketActorFrame.hpp"
using namespace mwmp; using namespace mwmp;
@ -44,6 +45,33 @@ void PacketActorFrame::Packet(RakNet::BitStream *bs, bool send)
RW(worldObject.headPitch, send); RW(worldObject.headPitch, send);
RW(worldObject.headYaw, 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) if (!send)
{ {
event->objectChanges.objects.push_back(worldObject); event->objectChanges.objects.push_back(worldObject);