mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Revamp CinemaManager component
Clean up the the implementation, improve the naming, and add some more documentation as well as more in-depth tests.
This commit is contained in:
parent
1d3cdec48d
commit
d882ab74a1
11 changed files with 285 additions and 183 deletions
|
|
@ -1,22 +1,21 @@
|
|||
Trigger.prototype.CounterMessage = function(data)
|
||||
{
|
||||
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
cmpGUIInterface.PushNotification({
|
||||
Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface)?.PushNotification({
|
||||
"players": [1, 2],
|
||||
"message": markForTranslation("Cutscene starts after 5 seconds"),
|
||||
"translateMessage": true
|
||||
});
|
||||
};
|
||||
|
||||
Trigger.prototype.StartCutscene = function(data)
|
||||
Trigger.prototype.StartCutscene = function()
|
||||
{
|
||||
var cmpCinemaManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_CinemaManager);
|
||||
const cmpCinemaManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_CinemaManager);
|
||||
if (!cmpCinemaManager)
|
||||
return;
|
||||
cmpCinemaManager.AddCinemaPathToQueue("test");
|
||||
cmpCinemaManager.Play();
|
||||
cmpCinemaManager.PushPathToQueue("test");
|
||||
cmpCinemaManager.StartPlayingQueue();
|
||||
};
|
||||
|
||||
var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
const cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
cmpTrigger.DoAfterDelay(1000, "CounterMessage", {});
|
||||
cmpTrigger.DoAfterDelay(6000, "StartCutscene", {});
|
||||
cmpTrigger.DoAfterDelay(5000, "StartCutscene", {});
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ GuiInterface.prototype.GetSimulationState = function()
|
|||
|
||||
const cmpCinemaManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_CinemaManager);
|
||||
if (cmpCinemaManager)
|
||||
ret.cinemaPlaying = cmpCinemaManager.IsPlaying();
|
||||
ret.cinemaPlaying = cmpCinemaManager.IsPlayingQueue();
|
||||
|
||||
const cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
|
||||
ret.victoryConditions = cmpEndGameManager.GetVictoryConditions();
|
||||
|
|
|
|||
|
|
@ -51,12 +51,12 @@ void CCinemaManager::Update(const float deltaRealTime) const
|
|||
return;
|
||||
|
||||
if (IsPlaying())
|
||||
cmpCinemaManager->PlayQueue(deltaRealTime, g_Game->GetView()->GetCamera());
|
||||
cmpCinemaManager->UpdateActivePath(deltaRealTime, g_Game->GetView()->GetCamera());
|
||||
}
|
||||
|
||||
void CCinemaManager::Render(Renderer::Backend::IDeviceCommandContext& deviceCommandContext) const
|
||||
{
|
||||
if (!IsEnabled() && m_DrawPaths)
|
||||
if (!IsPlaying() && m_DrawPaths)
|
||||
DrawPaths(deviceCommandContext);
|
||||
}
|
||||
|
||||
|
|
@ -121,15 +121,10 @@ void CCinemaManager::DrawNodes(Renderer::Backend::IDeviceCommandContext& deviceC
|
|||
}
|
||||
}
|
||||
|
||||
bool CCinemaManager::IsEnabled() const
|
||||
{
|
||||
CmpPtr<ICmpCinemaManager> cmpCinemaManager(g_Game->GetSimulation2()->GetSimContext().GetSystemEntity());
|
||||
return cmpCinemaManager && cmpCinemaManager->IsEnabled();
|
||||
}
|
||||
|
||||
bool CCinemaManager::IsPlaying() const
|
||||
{
|
||||
return IsEnabled() && g_Game && !g_Game->m_Paused;
|
||||
CmpPtr<ICmpCinemaManager> cmpCinemaManager(g_Game->GetSimulation2()->GetSimContext().GetSystemEntity());
|
||||
return cmpCinemaManager && cmpCinemaManager->IsPlayingQueue();
|
||||
}
|
||||
|
||||
bool CCinemaManager::GetPathsDrawing() const
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ public:
|
|||
void Render(Renderer::Backend::IDeviceCommandContext& deviceCommandContext) const;
|
||||
|
||||
bool IsPlaying() const;
|
||||
bool IsEnabled() const;
|
||||
|
||||
/**
|
||||
* Updates CCinemManager and current path
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -291,6 +291,9 @@ void CGameView::Update(const float deltaRealTime)
|
|||
{
|
||||
m->MiniMapTexture.Update(deltaRealTime);
|
||||
|
||||
m->CinemaManager.Update(deltaRealTime);
|
||||
if (m->CinemaManager.IsPlaying())
|
||||
return;
|
||||
// If camera movement is being handled by the touch-input system,
|
||||
// then we should stop to avoid conflicting with it
|
||||
if (g_TouchInput.IsEnabled())
|
||||
|
|
@ -299,10 +302,6 @@ void CGameView::Update(const float deltaRealTime)
|
|||
if (!g_app_has_focus)
|
||||
return;
|
||||
|
||||
m->CinemaManager.Update(deltaRealTime);
|
||||
if (m->CinemaManager.IsEnabled())
|
||||
return;
|
||||
|
||||
m->CameraController->Update(deltaRealTime);
|
||||
}
|
||||
|
||||
|
|
@ -354,7 +353,7 @@ entity_id_t CGameView::GetFollowedEntity()
|
|||
InReaction game_view_handler(const SDL_Event_* ev)
|
||||
{
|
||||
// put any events that must be processed even if inactive here
|
||||
if (!g_app_has_focus || !g_Game || !g_Game->IsGameStarted() || g_Game->GetView()->GetCinema()->IsEnabled())
|
||||
if (!g_app_has_focus || !g_Game || !g_Game->IsGameStarted() || g_Game->GetView()->GetCinema()->IsPlaying())
|
||||
return IN_PASS;
|
||||
|
||||
CGameView *pView=g_Game->GetView();
|
||||
|
|
|
|||
|
|
@ -26,11 +26,10 @@
|
|||
#include "ps/CLogger.h"
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/Game.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "renderer/SceneRenderer.h"
|
||||
#include "simulation2/MessageTypes.h"
|
||||
#include "simulation2/components/ICmpOverlayRenderer.h"
|
||||
#include "simulation2/components/ICmpRangeManager.h"
|
||||
#include "simulation2/components/ICmpSelectable.h"
|
||||
#include "simulation2/components/ICmpTerritoryManager.h"
|
||||
#include "simulation2/helpers/CinemaPath.h"
|
||||
#include "simulation2/system/Component.h"
|
||||
#include "simulation2/system/Entity.h"
|
||||
|
|
@ -50,6 +49,7 @@ public:
|
|||
static void ClassInit(CComponentManager& componentManager)
|
||||
{
|
||||
componentManager.SubscribeToMessageType(MT_Update);
|
||||
componentManager.SubscribeToMessageType(MT_Deserialized);
|
||||
}
|
||||
|
||||
DEFAULT_COMPONENT_ALLOCATOR(CinemaManager)
|
||||
|
|
@ -61,11 +61,11 @@ public:
|
|||
|
||||
void Init(const CParamNode&) override
|
||||
{
|
||||
m_Enabled = false;
|
||||
m_MapRevealed = false;
|
||||
m_ElapsedTime = fixed::Zero();
|
||||
m_TotalTime = fixed::Zero();
|
||||
m_CurrentPathElapsedTime = fixed::Zero();
|
||||
m_IsPlayingPathQueue = false;
|
||||
m_QueuePlayingElapsedTime = fixed::Zero();
|
||||
m_PathQueueDuration = fixed::Zero();
|
||||
m_ActivePathElapsedTime = fixed::Zero();
|
||||
m_WasMapRevealed = false;
|
||||
}
|
||||
|
||||
void Deinit() override
|
||||
|
|
@ -74,10 +74,10 @@ public:
|
|||
|
||||
void Serialize(ISerializer& serializer) override
|
||||
{
|
||||
serializer.Bool("Enabled", m_Enabled);
|
||||
serializer.NumberFixed_Unbounded("ElapsedTime", m_ElapsedTime);
|
||||
serializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_CurrentPathElapsedTime);
|
||||
serializer.Bool("MapRevealed", m_MapRevealed);
|
||||
serializer.Bool("IsPlayingPathQueue", m_IsPlayingPathQueue);
|
||||
serializer.NumberFixed_Unbounded("QueueElapsedTime", m_QueuePlayingElapsedTime);
|
||||
serializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_ActivePathElapsedTime);
|
||||
serializer.Bool("WasMapRevealed", m_WasMapRevealed);
|
||||
|
||||
serializer.NumberU32_Unbounded("NumberOfPaths", m_Paths.size());
|
||||
for (const std::pair<const CStrW, CCinemaPath>& it : m_Paths)
|
||||
|
|
@ -85,15 +85,15 @@ public:
|
|||
|
||||
serializer.NumberU32_Unbounded("NumberOfQueuedPaths", m_PathQueue.size());
|
||||
for (const CCinemaPath& path : m_PathQueue)
|
||||
serializer.String("PathName", path.GetName(), 1, 128);
|
||||
serializer.String("QueuedPathName", path.GetName(), 1, 128);
|
||||
}
|
||||
|
||||
void Deserialize(const CParamNode&, IDeserializer& deserializer) override
|
||||
{
|
||||
deserializer.Bool("Enabled", m_Enabled);
|
||||
deserializer.NumberFixed_Unbounded("ElapsedTime", m_ElapsedTime);
|
||||
deserializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_CurrentPathElapsedTime);
|
||||
deserializer.Bool("MapRevealed", m_MapRevealed);
|
||||
deserializer.Bool("IsPlayingPathQueue", m_IsPlayingPathQueue);
|
||||
deserializer.NumberFixed_Unbounded("QueueElapsedTime", m_QueuePlayingElapsedTime);
|
||||
deserializer.NumberFixed_Unbounded("CurrentPathElapsedTime", m_ActivePathElapsedTime);
|
||||
deserializer.Bool("WasMapRevealed", m_WasMapRevealed);
|
||||
|
||||
uint32_t numberOfPaths = 0;
|
||||
deserializer.NumberU32_Unbounded("NumberOfPaths", numberOfPaths);
|
||||
|
|
@ -108,55 +108,53 @@ public:
|
|||
for (uint32_t i = 0; i < numberOfQueuedPaths; ++i)
|
||||
{
|
||||
CStrW pathName;
|
||||
deserializer.String("PathName", pathName, 1, 128);
|
||||
deserializer.String("QueuedPathName", pathName, 1, 128);
|
||||
ENSURE(HasPath(pathName));
|
||||
AddCinemaPathToQueue(pathName);
|
||||
PushPathToQueue(pathName);
|
||||
}
|
||||
|
||||
if (!m_PathQueue.empty())
|
||||
{
|
||||
m_PathQueue.front().m_TimeElapsed = m_CurrentPathElapsedTime.ToFloat();
|
||||
m_PathQueue.front().m_TimeElapsed = m_ActivePathElapsedTime.ToFloat();
|
||||
m_PathQueue.front().Validate();
|
||||
}
|
||||
|
||||
SetEnabled(m_Enabled);
|
||||
}
|
||||
|
||||
void HandleMessage(const CMessage& msg, bool /*global*/) override
|
||||
{
|
||||
switch (msg.GetType())
|
||||
{
|
||||
case MT_Deserialized:
|
||||
if (!m_IsPlayingPathQueue)
|
||||
break;
|
||||
|
||||
m_IsPlayingPathQueue = false;
|
||||
StartPlayingQueue();
|
||||
break;
|
||||
case MT_Update:
|
||||
{
|
||||
const CMessageUpdate &msgData = static_cast<const CMessageUpdate&>(msg);
|
||||
if (!m_Enabled)
|
||||
if (!m_IsPlayingPathQueue)
|
||||
break;
|
||||
|
||||
// The paths play at a fixed speed, no matter the sim rate.
|
||||
// The turn length we have received here, however, is scaled by that rate.
|
||||
const fixed realTurnLength{msgData.turnLength / fixed::FromFloat(g_Game ? g_Game->GetSimRate() : 1.0f)};
|
||||
m_ElapsedTime += realTurnLength;
|
||||
m_CurrentPathElapsedTime += realTurnLength;
|
||||
if (m_CurrentPathElapsedTime >= m_PathQueue.front().GetDuration())
|
||||
m_QueuePlayingElapsedTime += realTurnLength;
|
||||
m_ActivePathElapsedTime += realTurnLength;
|
||||
if (m_ActivePathElapsedTime >= m_PathQueue.front().GetDuration())
|
||||
{
|
||||
CMessageCinemaPathEnded msgCinemaPathEnded(m_PathQueue.front().GetName());
|
||||
m_PathQueue.pop_front();
|
||||
GetSimContext().GetComponentManager().PostMessage(SYSTEM_ENTITY, msgCinemaPathEnded);
|
||||
m_CurrentPathElapsedTime = fixed::Zero();
|
||||
m_ActivePathElapsedTime = fixed::Zero();
|
||||
|
||||
if (!m_PathQueue.empty())
|
||||
m_PathQueue.front().Reset();
|
||||
}
|
||||
|
||||
if (m_ElapsedTime >= m_TotalTime)
|
||||
{
|
||||
m_CurrentPathElapsedTime = fixed::Zero();
|
||||
m_ElapsedTime = fixed::Zero();
|
||||
m_TotalTime = fixed::Zero();
|
||||
SetEnabled(false);
|
||||
GetSimContext().GetComponentManager().PostMessage(SYSTEM_ENTITY, CMessageCinemaQueueEnded());
|
||||
}
|
||||
|
||||
if (m_QueuePlayingElapsedTime >= m_PathQueueDuration)
|
||||
StopPlayingQueue();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -168,57 +166,28 @@ public:
|
|||
{
|
||||
if (m_Paths.find(path.GetName()) != m_Paths.end())
|
||||
{
|
||||
LOGWARNING("Path with name '%s' already exists", path.GetName().ToUTF8());
|
||||
LOGWARNING("Cinema path with name '%s' already exists", path.GetName().ToUTF8());
|
||||
return;
|
||||
}
|
||||
m_Paths[path.GetName()] = path;
|
||||
}
|
||||
|
||||
void AddCinemaPathToQueue(const CStrW& name) override
|
||||
{
|
||||
if (!HasPath(name))
|
||||
{
|
||||
LOGWARNING("Path with name '%s' doesn't exist", name.ToUTF8());
|
||||
return;
|
||||
}
|
||||
m_PathQueue.push_back(m_Paths[name]);
|
||||
|
||||
if (m_PathQueue.size() == 1)
|
||||
m_PathQueue.front().Reset();
|
||||
m_TotalTime += m_Paths[name].GetDuration();
|
||||
}
|
||||
|
||||
void Play() override
|
||||
{
|
||||
SetEnabled(true);
|
||||
}
|
||||
|
||||
void Stop() override
|
||||
{
|
||||
SetEnabled(false);
|
||||
}
|
||||
|
||||
bool HasPath(const CStrW& name) const override
|
||||
{
|
||||
return m_Paths.find(name) != m_Paths.end();
|
||||
}
|
||||
|
||||
void ClearQueue() override
|
||||
{
|
||||
m_PathQueue.clear();
|
||||
}
|
||||
|
||||
void DeletePath(const CStrW& name) override
|
||||
{
|
||||
if (!HasPath(name))
|
||||
{
|
||||
LOGWARNING("Path with name '%s' doesn't exist", name.ToUTF8());
|
||||
LOGWARNING("Cinema path with name '%s' doesn't exist", name.ToUTF8());
|
||||
return;
|
||||
}
|
||||
m_PathQueue.remove_if([name](const CCinemaPath& path) { return path.GetName() == name; });
|
||||
m_Paths.erase(name);
|
||||
}
|
||||
|
||||
bool HasPath(const CStrW& name) const override
|
||||
{
|
||||
return m_Paths.find(name) != m_Paths.end();
|
||||
}
|
||||
|
||||
const std::map<CStrW, CCinemaPath>& GetPaths() const override
|
||||
{
|
||||
return m_Paths;
|
||||
|
|
@ -229,41 +198,85 @@ public:
|
|||
m_Paths = newPaths;
|
||||
}
|
||||
|
||||
const std::list<CCinemaPath>& GetQueue() const override
|
||||
void PushPathToQueue(const CStrW& name) override
|
||||
{
|
||||
return m_PathQueue;
|
||||
if (!HasPath(name))
|
||||
{
|
||||
LOGWARNING("Cinema path with name '%s' doesn't exist", name.ToUTF8());
|
||||
return;
|
||||
}
|
||||
m_PathQueue.push_back(m_Paths[name]);
|
||||
|
||||
if (m_PathQueue.size() == 1)
|
||||
m_PathQueue.front().Reset();
|
||||
m_PathQueueDuration += m_Paths[name].GetDuration();
|
||||
}
|
||||
|
||||
bool IsEnabled() const override
|
||||
void ClearQueue() override
|
||||
{
|
||||
return m_Enabled;
|
||||
m_PathQueue.clear();
|
||||
}
|
||||
|
||||
void SetEnabled(bool enabled) override
|
||||
void StartPlayingQueue() override
|
||||
{
|
||||
if (m_PathQueue.empty() && enabled)
|
||||
enabled = false;
|
||||
|
||||
if (m_Enabled == enabled)
|
||||
if (m_IsPlayingPathQueue || m_PathQueue.empty())
|
||||
return;
|
||||
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSimContext().GetSystemEntity());
|
||||
if (cmpRangeManager)
|
||||
{
|
||||
if (enabled)
|
||||
m_MapRevealed = cmpRangeManager->GetLosRevealWholeMapForAll();
|
||||
// TODO: improve m_MapRevealed state and without fade in
|
||||
cmpRangeManager->SetLosRevealWholeMapForAll(enabled);
|
||||
m_WasMapRevealed = cmpRangeManager->GetLosRevealWholeMapForAll();
|
||||
// Note: this results in all fogged entities seen during the cinema path being revealed/updated in FOW
|
||||
// after the queue has ended.
|
||||
cmpRangeManager->SetLosRevealWholeMapForAll(true);
|
||||
}
|
||||
|
||||
m_Enabled = enabled;
|
||||
m_IsPlayingPathQueue = true;
|
||||
}
|
||||
|
||||
void PlayQueue(const float deltaRealTime, CCamera* camera) override
|
||||
void StopPlayingQueue() override
|
||||
{
|
||||
if (m_PathQueue.empty())
|
||||
if (!m_IsPlayingPathQueue)
|
||||
return;
|
||||
m_PathQueue.front().Play(deltaRealTime, camera);
|
||||
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSimContext().GetSystemEntity());
|
||||
if (cmpRangeManager)
|
||||
cmpRangeManager->SetLosRevealWholeMapForAll(m_WasMapRevealed);
|
||||
|
||||
GetSimContext().GetComponentManager().PostMessage(SYSTEM_ENTITY, CMessageCinemaQueueEnded());
|
||||
|
||||
m_ActivePathElapsedTime = fixed::Zero();
|
||||
m_QueuePlayingElapsedTime = fixed::Zero();
|
||||
m_PathQueueDuration = fixed::Zero();
|
||||
for (const CCinemaPath& path : m_PathQueue)
|
||||
m_PathQueueDuration += path.GetDuration();
|
||||
m_IsPlayingPathQueue = false;
|
||||
}
|
||||
|
||||
bool IsPlayingQueue() const override
|
||||
{
|
||||
return m_IsPlayingPathQueue;
|
||||
}
|
||||
|
||||
void UpdateActivePath(const float deltaRealTime, CCamera* camera) override
|
||||
{
|
||||
if (m_IsPlayingPathQueue)
|
||||
{
|
||||
if (m_PathQueue.empty())
|
||||
StopPlayingQueue();
|
||||
else
|
||||
m_PathQueue.front().Play(deltaRealTime, camera);
|
||||
}
|
||||
}
|
||||
|
||||
const CStrW GetActivePath() const override
|
||||
{
|
||||
return m_IsPlayingPathQueue ? m_PathQueue.front().GetName() : CStrW();
|
||||
}
|
||||
|
||||
const fixed GetActivePathElapsedTime() const override
|
||||
{
|
||||
return m_ActivePathElapsedTime;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -364,16 +377,22 @@ private:
|
|||
return CCinemaPath(data, pathSpline, targetSpline);
|
||||
}
|
||||
|
||||
bool m_Enabled;
|
||||
bool m_IsPlayingPathQueue;
|
||||
std::map<CStrW, CCinemaPath> m_Paths;
|
||||
std::list<CCinemaPath> m_PathQueue;
|
||||
fixed m_PathQueueDuration;
|
||||
|
||||
// States before playing
|
||||
bool m_MapRevealed;
|
||||
// Total time elapsed since starting to play the queue.
|
||||
fixed m_QueuePlayingElapsedTime;
|
||||
|
||||
fixed m_ElapsedTime;
|
||||
fixed m_TotalTime;
|
||||
fixed m_CurrentPathElapsedTime;
|
||||
// Time elapsed since the currently active path first started playing.
|
||||
fixed m_ActivePathElapsedTime;
|
||||
|
||||
// Time elapsed since the
|
||||
fixed m_QueueEndedElapsedTime;
|
||||
|
||||
// Whether the map was revealed before playing the queue.
|
||||
bool m_WasMapRevealed;
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_TYPE(CinemaManager)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -24,9 +24,13 @@
|
|||
|
||||
BEGIN_INTERFACE_WRAPPER(CinemaManager)
|
||||
DEFINE_INTERFACE_METHOD("AddPath", ICmpCinemaManager, AddPath)
|
||||
DEFINE_INTERFACE_METHOD("AddCinemaPathToQueue", ICmpCinemaManager, AddCinemaPathToQueue)
|
||||
DEFINE_INTERFACE_METHOD("HasPath", ICmpCinemaManager, HasPath)
|
||||
DEFINE_INTERFACE_METHOD("DeletePath", ICmpCinemaManager, DeletePath)
|
||||
DEFINE_INTERFACE_METHOD("IsPlaying", ICmpCinemaManager, IsEnabled)
|
||||
DEFINE_INTERFACE_METHOD("Play", ICmpCinemaManager, Play)
|
||||
DEFINE_INTERFACE_METHOD("Stop", ICmpCinemaManager, Stop)
|
||||
DEFINE_INTERFACE_METHOD("PushPathToQueue", ICmpCinemaManager, PushPathToQueue)
|
||||
DEFINE_INTERFACE_METHOD("ClearQueue", ICmpCinemaManager, ClearQueue)
|
||||
DEFINE_INTERFACE_METHOD("StartPlayingQueue", ICmpCinemaManager, StartPlayingQueue)
|
||||
DEFINE_INTERFACE_METHOD("IsPlayingQueue", ICmpCinemaManager, IsPlayingQueue)
|
||||
DEFINE_INTERFACE_METHOD("GetActivePath", ICmpCinemaManager, GetActivePath)
|
||||
DEFINE_INTERFACE_METHOD("GetActivePathElapsedTime", ICmpCinemaManager, GetActivePathElapsedTime)
|
||||
DEFINE_INTERFACE_METHOD("StopPlayingQueue", ICmpCinemaManager, StopPlayingQueue)
|
||||
END_INTERFACE_WRAPPER(CinemaManager)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -31,54 +31,83 @@ class CCinemaPath;
|
|||
class CStrW;
|
||||
|
||||
/**
|
||||
* Component for CCinemaManager class
|
||||
* TODO: write description
|
||||
* Manages a dynamic list of cinema paths (predefined camera movements/cutscenes) as well as a queue of paths that
|
||||
* is played one by one on command to all players. Simulation messages are sent each time a path ends and also when
|
||||
* the queue finishes as a whole.
|
||||
*/
|
||||
|
||||
class ICmpCinemaManager : public IComponent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Adds the path to the path list
|
||||
* @param CCinemaPath path data
|
||||
* Register a new path.
|
||||
* @param path path data
|
||||
*/
|
||||
virtual void AddPath(const CCinemaPath& path) = 0;
|
||||
|
||||
/**
|
||||
* Adds the path to the playlist
|
||||
* @param name path name
|
||||
*/
|
||||
virtual void AddCinemaPathToQueue(const CStrW& name) = 0;
|
||||
|
||||
virtual void Play() = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void PlayQueue(const float deltaRealTime, CCamera* camera) = 0;
|
||||
|
||||
/**
|
||||
* Checks the path name in the path list
|
||||
* @param name path name
|
||||
* @return true if path with that name exists, else false
|
||||
*/
|
||||
virtual bool HasPath(const CStrW& name) const = 0;
|
||||
|
||||
* Remove a path and its data entirely.
|
||||
* @param name path name
|
||||
*/
|
||||
virtual void DeletePath(const CStrW& name) = 0;
|
||||
|
||||
/**
|
||||
* Clears the playlist
|
||||
* Check whether a path exists (is registered under the given name)..
|
||||
* @param name path name
|
||||
*/
|
||||
virtual bool HasPath(const CStrW& name) const = 0;
|
||||
|
||||
/**
|
||||
* Get all registered paths, keyed by their names.
|
||||
*/
|
||||
virtual const std::map<CStrW, CCinemaPath>& GetPaths() const = 0;
|
||||
|
||||
/**
|
||||
* Override the entire list of existing paths.
|
||||
* @param newPaths new list of paths
|
||||
*/
|
||||
virtual void SetPaths(const std::map<CStrW, CCinemaPath>& newPaths) = 0;
|
||||
|
||||
/**
|
||||
* Push a path to the back of the queue.
|
||||
* @param name path name
|
||||
*/
|
||||
virtual void PushPathToQueue(const CStrW& name) = 0;
|
||||
|
||||
/**
|
||||
* Clear all paths from the queue.
|
||||
*/
|
||||
virtual void ClearQueue() = 0;
|
||||
|
||||
virtual const std::map<CStrW, CCinemaPath>& GetPaths() const = 0;
|
||||
virtual void SetPaths(const std::map<CStrW, CCinemaPath>& newPaths) = 0;
|
||||
virtual const std::list<CCinemaPath>& GetQueue() const = 0;
|
||||
|
||||
virtual bool IsEnabled() const = 0;
|
||||
/**
|
||||
* Start playing the paths in the queue one by one.
|
||||
*/
|
||||
virtual void StartPlayingQueue() = 0;
|
||||
|
||||
/**
|
||||
* Sets enable state of the cinema manager (shows/hide gui, show/hide rings, etc)
|
||||
* @param enable new state
|
||||
*/
|
||||
virtual void SetEnabled(bool enabled) = 0;
|
||||
* Stop playing the active path and the queue altogether.
|
||||
*/
|
||||
virtual void StopPlayingQueue() = 0;
|
||||
|
||||
/**
|
||||
* Whether the first path of the queue is being played at the moment.
|
||||
*/
|
||||
virtual bool IsPlayingQueue() const = 0;
|
||||
|
||||
/**
|
||||
* Send an update to the path currently playing for it to determine the new camera position.
|
||||
* Called every frame.
|
||||
*/
|
||||
virtual void UpdateActivePath(const float deltaRealTime, CCamera* camera) = 0;
|
||||
|
||||
/**
|
||||
* Get the name of the path currently playing, if any.
|
||||
*/
|
||||
virtual const CStrW GetActivePath() const = 0;
|
||||
|
||||
/**
|
||||
* Get the time elapsed since the currently active path started playing.
|
||||
*/
|
||||
virtual const fixed GetActivePathElapsedTime() const = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(CinemaManager)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -37,33 +37,91 @@
|
|||
class TestCmpCinemaManager : public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_basic()
|
||||
void test_managing_paths()
|
||||
{
|
||||
CXeromycesEngine xeromycesEngine;
|
||||
ComponentTestHelper test(*g_ScriptContext);
|
||||
|
||||
ICmpCinemaManager* cmp = test.Add<ICmpCinemaManager>(CID_CinemaManager, "", SYSTEM_ENTITY);
|
||||
|
||||
TS_ASSERT_EQUALS(cmp->HasPath(L"test"), false);
|
||||
TS_ASSERT(!cmp->HasPath(L"test"));
|
||||
cmp->AddPath(generatePath(L"test"));
|
||||
TS_ASSERT_EQUALS(cmp->HasPath(L"test"), true);
|
||||
cmp->DeletePath(L"test");
|
||||
TS_ASSERT_EQUALS(cmp->HasPath(L"test"), false);
|
||||
TS_ASSERT(cmp->HasPath(L"test"));
|
||||
TS_ASSERT(!cmp->HasPath(L"test_2"));
|
||||
cmp->SetPaths(std::map<CStrW, CCinemaPath>{{L"test_2", generatePath(L"test_2")}});
|
||||
TS_ASSERT(!cmp->HasPath(L"test"));
|
||||
TS_ASSERT(cmp->HasPath(L"test_2"));
|
||||
cmp->DeletePath(L"test_2");
|
||||
TS_ASSERT(!cmp->HasPath(L"test_2"));
|
||||
}
|
||||
|
||||
cmp->AddPath(generatePath(L"long_path", fixed::FromInt(3600)));
|
||||
TS_ASSERT_EQUALS(cmp->HasPath(L"long_path"), true);
|
||||
void test_playing_queue()
|
||||
{
|
||||
CXeromycesEngine xeromycesEngine;
|
||||
ComponentTestHelper test(*g_ScriptContext);
|
||||
|
||||
TS_ASSERT_EQUALS(cmp->IsEnabled(), false);
|
||||
cmp->AddCinemaPathToQueue(L"long_path");
|
||||
cmp->Play();
|
||||
size_t number_of_turns = 0;
|
||||
while (cmp->IsEnabled())
|
||||
ICmpCinemaManager* cmp = test.Add<ICmpCinemaManager>(CID_CinemaManager, "", SYSTEM_ENTITY);
|
||||
|
||||
CMessageUpdate updateMsg(fixed::FromInt(200));
|
||||
|
||||
cmp->AddPath(generatePath(L"path_1", fixed::FromInt(10000)));
|
||||
cmp->AddPath(generatePath(L"path_2", fixed::FromInt(5000)));
|
||||
|
||||
// Try getting the active path if there is none.
|
||||
TS_ASSERT_WSTR_EQUALS(cmp->GetActivePath(), L"");
|
||||
|
||||
// Try to start playing the queue if it's empty.
|
||||
cmp->StartPlayingQueue();
|
||||
TS_ASSERT(!cmp->IsPlayingQueue());
|
||||
|
||||
// Try stopping playing the queue if it's not playing in the first place.
|
||||
cmp->StartPlayingQueue();
|
||||
TS_ASSERT(!cmp->IsPlayingQueue());
|
||||
|
||||
cmp->PushPathToQueue(L"path_1");
|
||||
cmp->PushPathToQueue(L"path_2");
|
||||
// Try getting the active path if there is none.
|
||||
TS_ASSERT_WSTR_EQUALS(cmp->GetActivePath(), L"");
|
||||
|
||||
cmp->StartPlayingQueue();
|
||||
TS_ASSERT(cmp->IsPlayingQueue());
|
||||
TS_ASSERT_WSTR_EQUALS(cmp->GetActivePath(), L"path_1");
|
||||
TS_ASSERT_EQUALS(cmp->GetActivePathElapsedTime(), fixed::FromInt(0));
|
||||
|
||||
for (int i = 0; i < 35; i++)
|
||||
cmp->HandleMessage(updateMsg, true);
|
||||
TS_ASSERT(cmp->IsPlayingQueue());
|
||||
TS_ASSERT_WSTR_EQUALS(cmp->GetActivePath(), L"path_1");
|
||||
TS_ASSERT_EQUALS(cmp->GetActivePathElapsedTime(), fixed::FromInt(7000));
|
||||
|
||||
// Finish path_1 and start with path_2
|
||||
for (int i = 0; i < 20; i++)
|
||||
cmp->HandleMessage(updateMsg, true);
|
||||
|
||||
TS_ASSERT_WSTR_EQUALS(cmp->GetActivePath(), L"path_2");
|
||||
TS_ASSERT_EQUALS(cmp->GetActivePathElapsedTime(), fixed::FromInt(1000));
|
||||
|
||||
// Try restarting while a path is being played.
|
||||
// This should result in the active path starting again from the beginning.
|
||||
cmp->StopPlayingQueue();
|
||||
cmp->StartPlayingQueue();
|
||||
TS_ASSERT(cmp->IsPlayingQueue());
|
||||
TS_ASSERT_WSTR_EQUALS(cmp->GetActivePath(), L"path_2");
|
||||
|
||||
size_t remainingTurns = 0;
|
||||
while (cmp->IsPlayingQueue())
|
||||
{
|
||||
CMessageUpdate msg(fixed::FromInt(36));
|
||||
cmp->HandleMessage(msg, true);
|
||||
++number_of_turns;
|
||||
cmp->HandleMessage(updateMsg, true);
|
||||
remainingTurns++;
|
||||
}
|
||||
TS_ASSERT_EQUALS(number_of_turns, 100);
|
||||
TS_ASSERT_EQUALS(remainingTurns, 25);
|
||||
TS_ASSERT(!cmp->IsPlayingQueue());
|
||||
TS_ASSERT_WSTR_EQUALS(cmp->GetActivePath(), L"");
|
||||
TS_ASSERT_EQUALS(cmp->GetActivePathElapsedTime(), fixed::FromInt(0));
|
||||
|
||||
// Make sure the queue is empty.
|
||||
cmp->StartPlayingQueue();
|
||||
TS_ASSERT(!cmp->IsPlayingQueue());
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -41,7 +41,7 @@ namespace AtlasMessage {
|
|||
|
||||
MESSAGEHANDLER(CameraReset)
|
||||
{
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsPlaying())
|
||||
return;
|
||||
|
||||
CVector3D focus = g_Game->GetView()->GetCamera()->GetFocus();
|
||||
|
|
@ -64,7 +64,7 @@ MESSAGEHANDLER(CameraReset)
|
|||
|
||||
MESSAGEHANDLER(ScrollConstant)
|
||||
{
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsPlaying())
|
||||
return;
|
||||
|
||||
if (msg->dir < 0 || msg->dir > 5)
|
||||
|
|
@ -82,7 +82,7 @@ MESSAGEHANDLER(ScrollConstant)
|
|||
|
||||
MESSAGEHANDLER(Scroll)
|
||||
{
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled()) // TODO: do this better (probably a separate AtlasView class for cinematics)
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsPlaying()) // TODO: do this better (probably a separate AtlasView class for cinematics)
|
||||
return;
|
||||
|
||||
static CVector3D targetPos;
|
||||
|
|
@ -131,7 +131,7 @@ MESSAGEHANDLER(Scroll)
|
|||
|
||||
MESSAGEHANDLER(SmoothZoom)
|
||||
{
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsPlaying())
|
||||
return;
|
||||
|
||||
g_AtlasGameLoop->input.zoomDelta += msg->amount;
|
||||
|
|
@ -139,7 +139,7 @@ MESSAGEHANDLER(SmoothZoom)
|
|||
|
||||
MESSAGEHANDLER(RotateAround)
|
||||
{
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsPlaying())
|
||||
return;
|
||||
|
||||
static CVector3D focusPos;
|
||||
|
|
@ -245,7 +245,7 @@ QUERYHANDLER(GetView)
|
|||
|
||||
MESSAGEHANDLER(SetView)
|
||||
{
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsEnabled())
|
||||
if (!g_Game || g_Game->GetView()->GetCinema()->IsPlaying())
|
||||
return;
|
||||
|
||||
CGameView* view = g_Game->GetView();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (C) 2025 Wildfire Games.
|
||||
/* Copyright (C) 2026 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
|
|
@ -189,7 +189,7 @@ MESSAGEHANDLER(CinemaEvent)
|
|||
|
||||
if (msg->mode == eCinemaEventMode::SMOOTH)
|
||||
{
|
||||
cmpCinemaManager->AddCinemaPathToQueue(*msg->path);
|
||||
cmpCinemaManager->PushPathToQueue(*msg->path);
|
||||
}
|
||||
else if ( msg->mode == eCinemaEventMode::RESET )
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue