mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-05 09:33:25 -08:00
[Replay] Seek to the exact time of an event
Timeline seeks only expose whole timer intervals, so jumping to an event could leave that event unprocessed until the next tick. Add an event-time seek that also applies events at the target timestamp.
This commit is contained in:
parent
0fe4fd0d8c
commit
b863a53b35
4 changed files with 52 additions and 13 deletions
|
|
@ -57,7 +57,7 @@ ReplayManager::ReplayManager(QObject *parent, GameReplay *replay)
|
|||
|
||||
rewindBufferingTimer = new QTimer(this);
|
||||
rewindBufferingTimer->setSingleShot(true);
|
||||
connect(rewindBufferingTimer, &QTimer::timeout, this, &ReplayManager::processRewind);
|
||||
connect(rewindBufferingTimer, &QTimer::timeout, this, qOverload<>(&ReplayManager::processRewind));
|
||||
}
|
||||
|
||||
ReplayManager::~ReplayManager()
|
||||
|
|
@ -65,7 +65,7 @@ ReplayManager::~ReplayManager()
|
|||
delete replay;
|
||||
}
|
||||
|
||||
void ReplayManager::skipToTime(int newTime, bool doRewindBuffering)
|
||||
void ReplayManager::skipToTime(int newTime, bool doRewindBuffering, bool includeEventsAtTime)
|
||||
{
|
||||
// check boundary conditions
|
||||
if (newTime < 0) {
|
||||
|
|
@ -75,15 +75,17 @@ void ReplayManager::skipToTime(int newTime, bool doRewindBuffering)
|
|||
newTime = maxTime;
|
||||
}
|
||||
|
||||
newTime -= newTime % TIMER_INTERVAL_MS; // Time should always be a multiple of the interval
|
||||
if (!includeEventsAtTime) {
|
||||
newTime -= newTime % TIMER_INTERVAL_MS; // Time should always be a multiple of the interval
|
||||
}
|
||||
|
||||
const bool isBackwardsSkip = newTime < currentProcessedTime;
|
||||
currentVisualTime = newTime;
|
||||
|
||||
if (isBackwardsSkip) {
|
||||
handleBackwardsSkip(doRewindBuffering);
|
||||
handleBackwardsSkip(doRewindBuffering, includeEventsAtTime);
|
||||
} else {
|
||||
processNewEvents(FORWARD_SKIP);
|
||||
processNewEvents(FORWARD_SKIP, includeEventsAtTime);
|
||||
}
|
||||
|
||||
timeChanged(currentVisualTime);
|
||||
|
|
@ -95,7 +97,7 @@ void ReplayManager::skipToTime(int newTime, bool doRewindBuffering)
|
|||
* @param doRewindBuffering When true, if multiple backward skips are made in quick succession, only a single rewind
|
||||
* is processed at the end. When false, the backwards skip will always cause an immediate rewind.
|
||||
*/
|
||||
void ReplayManager::handleBackwardsSkip(bool doRewindBuffering)
|
||||
void ReplayManager::handleBackwardsSkip(bool doRewindBuffering, bool includeEventsAtTime)
|
||||
{
|
||||
if (doRewindBuffering) {
|
||||
// We use a one-shot timer to implement the rewind buffering.
|
||||
|
|
@ -105,11 +107,16 @@ void ReplayManager::handleBackwardsSkip(bool doRewindBuffering)
|
|||
rewindBufferingTimer->start(SettingsCache::instance().userInterface().getRewindBufferingMs());
|
||||
} else {
|
||||
// otherwise, process the rewind immediately
|
||||
processRewind();
|
||||
processRewind(includeEventsAtTime);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayManager::processRewind()
|
||||
{
|
||||
processRewind(false);
|
||||
}
|
||||
|
||||
void ReplayManager::processRewind(bool includeEventsAtTime)
|
||||
{
|
||||
// stop any queued-up rewinds
|
||||
rewindBufferingTimer->stop();
|
||||
|
|
@ -117,7 +124,7 @@ void ReplayManager::processRewind()
|
|||
// process the rewind
|
||||
currentEvent = 0;
|
||||
emit rewound();
|
||||
processNewEvents(BACKWARD_SKIP);
|
||||
processNewEvents(BACKWARD_SKIP, includeEventsAtTime);
|
||||
}
|
||||
|
||||
void ReplayManager::replayTimerTimeout()
|
||||
|
|
@ -134,11 +141,13 @@ void ReplayManager::replayTimerTimeout()
|
|||
}
|
||||
|
||||
/** @brief Processes all unprocessed events up to the current time. */
|
||||
void ReplayManager::processNewEvents(PlaybackMode playbackMode)
|
||||
void ReplayManager::processNewEvents(PlaybackMode playbackMode, bool includeEventsAtTime)
|
||||
{
|
||||
currentProcessedTime = currentVisualTime;
|
||||
|
||||
while (currentEvent < replayTimeline.size() && replayTimeline[currentEvent] < currentProcessedTime) {
|
||||
while (currentEvent < replayTimeline.size() &&
|
||||
(replayTimeline[currentEvent] < currentProcessedTime ||
|
||||
(includeEventsAtTime && replayTimeline[currentEvent] == currentProcessedTime))) {
|
||||
EventProcessingOptions options;
|
||||
|
||||
// backwards skip => always skip reveal windows
|
||||
|
|
@ -236,6 +245,16 @@ void ReplayManager::setTime(int time)
|
|||
skipToTime(time, false);
|
||||
}
|
||||
|
||||
void ReplayManager::setTimeForEvent(int eventIndex)
|
||||
{
|
||||
if (eventIndex < 0 || eventIndex >= replayTimeline.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Unlike a timeline seek, jumping to an event should apply that event immediately.
|
||||
skipToTime(replayTimeline.at(eventIndex), false, true);
|
||||
}
|
||||
|
||||
void ReplayManager::skipByAmount(int amount)
|
||||
{
|
||||
skipToTime(currentVisualTime + amount, amount < 0);
|
||||
|
|
|
|||
|
|
@ -41,10 +41,11 @@ class ReplayManager : public QObject
|
|||
///< to rewind buffering
|
||||
int currentEvent = 0; ///< current event's index
|
||||
|
||||
void skipToTime(int newTime, bool doRewindBuffering);
|
||||
void handleBackwardsSkip(bool doRewindBuffering);
|
||||
void skipToTime(int newTime, bool doRewindBuffering, bool includeEventsAtTime = false);
|
||||
void handleBackwardsSkip(bool doRewindBuffering, bool includeEventsAtTime = false);
|
||||
void processRewind();
|
||||
void processNewEvents(PlaybackMode playbackMode);
|
||||
void processRewind(bool includeEventsAtTime);
|
||||
void processNewEvents(PlaybackMode playbackMode, bool includeEventsAtTime = false);
|
||||
void handleSkipEmptySection();
|
||||
|
||||
private slots:
|
||||
|
|
@ -74,6 +75,7 @@ public slots:
|
|||
void startReplay();
|
||||
void stopReplay();
|
||||
void setTime(int time);
|
||||
void setTimeForEvent(int eventIndex);
|
||||
void skipByAmount(int amount); // use a negative amount to skip backwards
|
||||
|
||||
signals:
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ ReplayWidget::ReplayWidget(QWidget *parent, GameReplay *replay)
|
|||
connect(replayManager, &ReplayManager::eventReplayed, this, &ReplayWidget::eventReplayed);
|
||||
connect(replayManager, &ReplayManager::replayFinished, this, &ReplayWidget::replayFinished);
|
||||
connect(replayManager, &ReplayManager::rewound, this, &ReplayWidget::rewound);
|
||||
connect(replayManager, &ReplayManager::timeChanged, this, &ReplayWidget::timeChanged);
|
||||
|
||||
// timeline widget
|
||||
timelineWidget = new ReplayTimelineWidget;
|
||||
|
|
@ -96,6 +97,16 @@ ReplayWidget::ReplayWidget(QWidget *parent, GameReplay *replay)
|
|||
refreshShortcuts();
|
||||
}
|
||||
|
||||
const QList<int> &ReplayWidget::getReplayTimeline() const
|
||||
{
|
||||
return replayManager->getReplayTimeline();
|
||||
}
|
||||
|
||||
void ReplayWidget::seekToEvent(int eventIndex)
|
||||
{
|
||||
replayManager->setTimeForEvent(eventIndex);
|
||||
}
|
||||
|
||||
void ReplayWidget::replayFinished()
|
||||
{
|
||||
replayPlayButton->setChecked(false);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "../../../game/player/event_processing_options.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QWidget>
|
||||
#include <qtmetamacros.h>
|
||||
|
||||
|
|
@ -36,8 +37,14 @@ public:
|
|||
*/
|
||||
ReplayWidget(QWidget *parent, GameReplay *replay);
|
||||
|
||||
[[nodiscard]] const QList<int> &getReplayTimeline() const;
|
||||
|
||||
public slots:
|
||||
void seekToEvent(int eventIndex);
|
||||
|
||||
signals:
|
||||
void rewound();
|
||||
void timeChanged(int time);
|
||||
void eventReplayed(const GameEventContainer &cont, EventProcessingOptions options);
|
||||
|
||||
private:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue