mirror of
https://github.com/Jous99/F4MP.git
synced 2026-01-12 16:50:54 +01:00
169 lines
5.9 KiB
C++
169 lines
5.9 KiB
C++
#include <TiltedOnlinePCH.h>
|
|
|
|
#include <Systems/AnimationSystem.h>
|
|
|
|
#include <Games/Animation/TESActionData.h>
|
|
#include <Games/Animation/ActorMediator.h>
|
|
|
|
#include <Games/References.h>
|
|
|
|
#include <Forms/BGSAction.h>
|
|
#include <AI/AIProcess.h>
|
|
#include <Misc/MiddleProcess.h>
|
|
|
|
#include <Messages/ClientReferencesMoveRequest.h>
|
|
|
|
#include <Components.h>
|
|
#include <World.h>
|
|
|
|
#include <Forms/TESObjectCELL.h>
|
|
#include <Forms/TESWorldSpace.h>
|
|
|
|
extern thread_local const char* g_animErrorCode;
|
|
|
|
void AnimationSystem::Update(World& aWorld, Actor* apActor, RemoteAnimationComponent& aAnimationComponent, const uint64_t aTick) noexcept
|
|
{
|
|
auto& actions = aAnimationComponent.TimePoints;
|
|
|
|
const auto it = std::begin(actions);
|
|
if (it != std::end(actions) && it->Tick <= aTick)
|
|
{
|
|
// Check if animation graph is ready before attempting to play animations
|
|
if (!apActor->animationGraphHolder.IsReady())
|
|
{
|
|
// Animation graph not ready, keep the action in queue and try again later
|
|
return;
|
|
}
|
|
if (aAnimationComponent.ReplayCount > 0 && aAnimationComponent.ResetAnimationGraphForReplay)
|
|
{
|
|
apActor->animationGraphHolder.RevertAnimationGraphManager();
|
|
aAnimationComponent.ResetAnimationGraphForReplay = false;
|
|
}
|
|
|
|
const auto& first = *it;
|
|
|
|
const auto actionId = first.ActionId;
|
|
const auto targetId = first.TargetId;
|
|
|
|
const auto pAction = Cast<BGSAction>(TESForm::GetById(actionId));
|
|
const auto pTarget = Cast<TESObjectREFR>(TESForm::GetById(targetId));
|
|
|
|
apActor->actorState.flags1 = first.State1;
|
|
apActor->actorState.flags2 = first.State2;
|
|
|
|
apActor->LoadAnimationVariables(first.Variables);
|
|
|
|
aAnimationComponent.LastRanAction = first;
|
|
|
|
// Play the animation
|
|
TESActionData actionData(first.Type & 0x3, apActor, pAction, pTarget);
|
|
actionData.eventName = BSFixedString(first.EventName.c_str());
|
|
actionData.idleForm = Cast<TESIdleForm>(TESForm::GetById(first.IdleId));
|
|
actionData.someFlag = ((first.Type & 0x4) != 0) ? 1 : 0;
|
|
|
|
const auto result = ActorMediator::Get()->ForceAction(&actionData);
|
|
|
|
if (aAnimationComponent.ReplayCount > 0)
|
|
aAnimationComponent.ReplayCount--;
|
|
|
|
actions.pop_front();
|
|
}
|
|
}
|
|
|
|
void AnimationSystem::Setup(World& aWorld, const entt::entity aEntity) noexcept
|
|
{
|
|
aWorld.emplace_or_replace<RemoteAnimationComponent>(aEntity);
|
|
}
|
|
|
|
void AnimationSystem::Clean(World& aWorld, const entt::entity aEntity) noexcept
|
|
{
|
|
if (aWorld.all_of<RemoteAnimationComponent>(aEntity))
|
|
aWorld.remove<RemoteAnimationComponent>(aEntity);
|
|
}
|
|
|
|
void AnimationSystem::AddActionsForReplay(RemoteAnimationComponent& aAnimationComponent,
|
|
const ActionReplayChain& acReplay) noexcept
|
|
{
|
|
aAnimationComponent.TimePoints.insert(aAnimationComponent.TimePoints.end(), acReplay.Actions.begin(),
|
|
acReplay.Actions.end());
|
|
aAnimationComponent.ReplayCount = acReplay.Actions.size();
|
|
aAnimationComponent.ResetAnimationGraphForReplay = acReplay.ResetAnimationGraph;
|
|
}
|
|
|
|
void AnimationSystem::AddAction(RemoteAnimationComponent& aAnimationComponent, const std::string& acActionDiff) noexcept
|
|
{
|
|
auto itor = std::begin(aAnimationComponent.TimePoints);
|
|
const auto end = std::cend(aAnimationComponent.TimePoints);
|
|
|
|
auto& lastProcessedAction = aAnimationComponent.LastProcessedAction;
|
|
|
|
TiltedPhoques::ViewBuffer buffer((uint8_t*)acActionDiff.data(), acActionDiff.size());
|
|
Buffer::Reader reader(&buffer);
|
|
|
|
lastProcessedAction.ApplyDifferential(reader);
|
|
|
|
aAnimationComponent.TimePoints.push_back(lastProcessedAction);
|
|
}
|
|
|
|
void AnimationSystem::Serialize(World& aWorld, ClientReferencesMoveRequest& aMovementSnapshot, LocalComponent& localComponent, LocalAnimationComponent& animationComponent, FormIdComponent& formIdComponent)
|
|
{
|
|
const auto pForm = TESForm::GetById(formIdComponent.Id);
|
|
const auto pActor = Cast<Actor>(pForm);
|
|
if (!pActor)
|
|
return;
|
|
|
|
auto& update = aMovementSnapshot.Updates[localComponent.Id];
|
|
auto& movement = update.UpdatedMovement;
|
|
|
|
if (const auto pCell = pActor->parentCell)
|
|
World::Get().GetModSystem().GetServerModId(pCell->formID, movement.CellId.ModId, movement.CellId.BaseId);
|
|
|
|
if (const auto pWorldSpace = pActor->GetWorldSpace())
|
|
World::Get().GetModSystem().GetServerModId(pWorldSpace->formID, movement.WorldSpaceId.ModId, movement.WorldSpaceId.BaseId);
|
|
|
|
movement.Position = pActor->position;
|
|
|
|
movement.Rotation.x = pActor->rotation.x;
|
|
movement.Rotation.y = pActor->rotation.z;
|
|
|
|
pActor->SaveAnimationVariables(movement.Variables);
|
|
|
|
if (pActor->currentProcess && pActor->currentProcess->middleProcess)
|
|
{
|
|
movement.Direction = pActor->currentProcess->middleProcess->direction;
|
|
}
|
|
|
|
for (auto& entry : animationComponent.Actions)
|
|
{
|
|
update.ActionEvents.push_back(entry);
|
|
}
|
|
|
|
auto latestAction = animationComponent.GetLatestAction();
|
|
|
|
if (latestAction)
|
|
localComponent.CurrentAction = latestAction.MoveResult();
|
|
|
|
animationComponent.Actions.clear();
|
|
}
|
|
|
|
bool AnimationSystem::Serialize(World& aWorld, const ActionEvent& aActionEvent, const ActionEvent& aLastProcessedAction, std::string* apData)
|
|
{
|
|
uint32_t actionBaseId = 0;
|
|
uint32_t actionModId = 0;
|
|
if (!aWorld.GetModSystem().GetServerModId(aActionEvent.ActionId, actionModId, actionBaseId))
|
|
return false;
|
|
|
|
uint32_t targetBaseId = 0;
|
|
uint32_t targetModId = 0;
|
|
if (!aWorld.GetModSystem().GetServerModId(aActionEvent.TargetId, targetModId, targetBaseId))
|
|
return false;
|
|
|
|
uint8_t scratch[1 << 14];
|
|
TiltedPhoques::ViewBuffer buffer(scratch, std::size(scratch));
|
|
Buffer::Writer writer(&buffer);
|
|
aActionEvent.GenerateDifferential(aLastProcessedAction, writer);
|
|
|
|
apData->assign(buffer.GetData(), buffer.GetData() + writer.Size());
|
|
|
|
return true;
|
|
}
|