mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-08 17:13:57 -07:00
preliminary client-side replay watching code
This commit is contained in:
parent
65d4c77ee8
commit
4dc712286f
13 changed files with 1449 additions and 16 deletions
96
cockatrice/src/replay_timeline_widget.cpp
Normal file
96
cockatrice/src/replay_timeline_widget.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#include "replay_timeline_widget.h"
|
||||
#include <QPainter>
|
||||
#include <QPalette>
|
||||
#include <QTimer>
|
||||
#include <math.h>
|
||||
|
||||
ReplayTimelineWidget::ReplayTimelineWidget(QWidget *parent)
|
||||
: QWidget(parent), maxBinValue(1), maxTime(1), timeScaleFactor(1.0), currentTime(0), currentEvent(0)
|
||||
{
|
||||
replayTimer = new QTimer(this);
|
||||
connect(replayTimer, SIGNAL(timeout()), this, SLOT(replayTimerTimeout()));
|
||||
}
|
||||
|
||||
const int ReplayTimelineWidget::binLength = 5000;
|
||||
|
||||
void ReplayTimelineWidget::setTimeline(const QList<int> &_replayTimeline)
|
||||
{
|
||||
replayTimeline = _replayTimeline;
|
||||
histogram.clear();
|
||||
int binEndTime = binLength - 1;
|
||||
int binValue = 0;
|
||||
for (int i = 0; i < replayTimeline.size(); ++i) {
|
||||
if (replayTimeline[i] > binEndTime) {
|
||||
histogram.append(binValue);
|
||||
if (binValue > maxBinValue)
|
||||
maxBinValue = binValue;
|
||||
while (replayTimeline[i] > binEndTime + binLength) {
|
||||
histogram.append(0);
|
||||
binEndTime += binLength;
|
||||
}
|
||||
binValue = 1;
|
||||
binEndTime += binLength;
|
||||
} else
|
||||
++binValue;
|
||||
}
|
||||
histogram.append(binValue);
|
||||
if (!replayTimeline.isEmpty())
|
||||
maxTime = replayTimeline.last();
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.drawRect(0, 0, width() - 1, height() - 1);
|
||||
|
||||
qreal binWidth = (qreal) width() / histogram.size();
|
||||
QPainterPath path;
|
||||
path.moveTo(0, height() - 1);
|
||||
for (int i = 0; i < histogram.size(); ++i)
|
||||
path.lineTo(round(i * binWidth), (height() - 1) * (1.0 - (qreal) histogram[i] / maxBinValue));
|
||||
path.lineTo(width() - 1, height() - 1);
|
||||
path.lineTo(0, height() - 1);
|
||||
painter.fillPath(path, Qt::black);
|
||||
|
||||
const QColor barColor = QColor::fromHsv(120, 255, 255, 100);
|
||||
painter.fillRect(0, 0, (width() - 1) * currentTime / maxTime, height() - 1, barColor);
|
||||
}
|
||||
|
||||
QSize ReplayTimelineWidget::sizeHint() const
|
||||
{
|
||||
return QSize(-1, 50);
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::replayTimerTimeout()
|
||||
{
|
||||
currentTime += 200;
|
||||
while ((currentEvent < replayTimeline.size()) && (replayTimeline[currentEvent] < currentTime)) {
|
||||
emit processNextEvent();
|
||||
++currentEvent;
|
||||
}
|
||||
if (currentEvent == replayTimeline.size()) {
|
||||
emit replayFinished();
|
||||
replayTimer->stop();
|
||||
}
|
||||
|
||||
if (!(currentTime % 1000))
|
||||
update();
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::setTimeScaleFactor(qreal _timeScaleFactor)
|
||||
{
|
||||
timeScaleFactor = _timeScaleFactor;
|
||||
replayTimer->setInterval(200 / timeScaleFactor);
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::startReplay()
|
||||
{
|
||||
replayTimer->start(200 / timeScaleFactor);
|
||||
}
|
||||
|
||||
void ReplayTimelineWidget::stopReplay()
|
||||
{
|
||||
replayTimer->stop();
|
||||
}
|
||||
39
cockatrice/src/replay_timeline_widget.h
Normal file
39
cockatrice/src/replay_timeline_widget.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef REPLAY_TIMELINE_WIDGET
|
||||
#define REPLAY_TIMELINE_WIDGET
|
||||
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
|
||||
class QPaintEvent;
|
||||
class QTimer;
|
||||
|
||||
class ReplayTimelineWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void processNextEvent();
|
||||
void replayFinished();
|
||||
private:
|
||||
QTimer *replayTimer;
|
||||
QList<int> replayTimeline;
|
||||
QList<int> histogram;
|
||||
static const int binLength;
|
||||
int maxBinValue, maxTime;
|
||||
qreal timeScaleFactor;
|
||||
int currentTime;
|
||||
int currentEvent;
|
||||
private slots:
|
||||
void replayTimerTimeout();
|
||||
public:
|
||||
ReplayTimelineWidget(QWidget *parent = 0);
|
||||
void setTimeline(const QList<int> &_replayTimeline);
|
||||
QSize sizeHint() const;
|
||||
void setTimeScaleFactor(qreal _timeScaleFactor);
|
||||
int getCurrentEvent() const { return currentEvent; }
|
||||
public slots:
|
||||
void startReplay();
|
||||
void stopReplay();
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -6,6 +6,8 @@
|
|||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
|
||||
#include "tab_game.h"
|
||||
#include "tab_supervisor.h"
|
||||
#include "cardinfowidget.h"
|
||||
|
|
@ -26,6 +28,7 @@
|
|||
#include "main.h"
|
||||
#include "settingscache.h"
|
||||
#include "carddatabase.h"
|
||||
#include "replay_timeline_widget.h"
|
||||
|
||||
#include <google/protobuf/descriptor.h>
|
||||
#include "pending_command.h"
|
||||
|
|
@ -195,7 +198,7 @@ void DeckViewContainer::setDeck(DeckList *deck)
|
|||
deckView->setDeck(deck);
|
||||
readyStartButton->setEnabled(true);
|
||||
}
|
||||
|
||||
#include <QDebug>
|
||||
TabGame::TabGame(GameReplay *_replay)
|
||||
: Tab(0),
|
||||
hostId(-1),
|
||||
|
|
@ -213,6 +216,24 @@ TabGame::TabGame(GameReplay *_replay)
|
|||
gameId = replay->game_info().game_id();
|
||||
gameDescription = QString::fromStdString(replay->game_info().description());
|
||||
|
||||
// Create list: event number -> time [ms]
|
||||
// Distribute simultaneous events evenly across 1 second.
|
||||
int lastEventTimestamp = -1;
|
||||
const int eventCount = replay->event_list_size();
|
||||
for (int i = 0; i < eventCount; ++i) {
|
||||
int j = i + 1;
|
||||
while ((j < eventCount) && (replay->event_list(j).seconds_elapsed() == lastEventTimestamp))
|
||||
++j;
|
||||
|
||||
const int numberEventsThisSecond = j - i;
|
||||
for (int k = 0; k < numberEventsThisSecond; ++k)
|
||||
replayTimeline.append(replay->event_list(i + k).seconds_elapsed() * 1000 + (int) ((qreal) k / (qreal) numberEventsThisSecond * 1000));
|
||||
|
||||
if (j < eventCount)
|
||||
lastEventTimestamp = replay->event_list(j).seconds_elapsed();
|
||||
i += numberEventsThisSecond - 1;
|
||||
}
|
||||
|
||||
phasesToolbar = new PhasesToolbar;
|
||||
phasesToolbar->hide();
|
||||
|
||||
|
|
@ -241,6 +262,40 @@ TabGame::TabGame(GameReplay *_replay)
|
|||
QWidget *messageLogLayoutWidget = new QWidget;
|
||||
messageLogLayoutWidget->setLayout(messageLogLayout);
|
||||
|
||||
timelineWidget = new ReplayTimelineWidget;
|
||||
timelineWidget->setTimeline(replayTimeline);
|
||||
connect(timelineWidget, SIGNAL(processNextEvent()), this, SLOT(replayNextEvent()));
|
||||
connect(timelineWidget, SIGNAL(replayFinished()), this, SLOT(replayFinished()));
|
||||
|
||||
replayToStartButton = new QToolButton;
|
||||
replayToStartButton->setIconSize(QSize(32, 32));
|
||||
replayToStartButton->setIcon(QIcon(":/resources/replay_tostart.svg"));
|
||||
connect(replayToStartButton, SIGNAL(clicked()), this, SLOT(replayToStartButtonClicked()));
|
||||
replayStartButton = new QToolButton;
|
||||
replayStartButton->setIconSize(QSize(32, 32));
|
||||
replayStartButton->setIcon(QIcon(":/resources/replay_start.svg"));
|
||||
connect(replayStartButton, SIGNAL(clicked()), this, SLOT(replayStartButtonClicked()));
|
||||
replayPauseButton = new QToolButton;
|
||||
replayPauseButton->setIconSize(QSize(32, 32));
|
||||
replayPauseButton->setEnabled(false);
|
||||
replayPauseButton->setIcon(QIcon(":/resources/replay_pause.svg"));
|
||||
connect(replayPauseButton, SIGNAL(clicked()), this, SLOT(replayPauseButtonClicked()));
|
||||
replayStopButton = new QToolButton;
|
||||
replayStopButton->setIconSize(QSize(32, 32));
|
||||
replayStopButton->setEnabled(false);
|
||||
replayStopButton->setIcon(QIcon(":/resources/replay_stop.svg"));
|
||||
connect(replayStopButton, SIGNAL(clicked()), this, SLOT(replayStopButtonClicked()));
|
||||
replayFastForwardButton = new QToolButton;
|
||||
replayFastForwardButton->setIconSize(QSize(32, 32));
|
||||
replayFastForwardButton->setEnabled(false);
|
||||
replayFastForwardButton->setIcon(QIcon(":/resources/replay_fastforward.svg"));
|
||||
replayFastForwardButton->setCheckable(true);
|
||||
connect(replayFastForwardButton, SIGNAL(toggled(bool)), this, SLOT(replayFastForwardButtonToggled(bool)));
|
||||
replayToEndButton = new QToolButton;
|
||||
replayToEndButton->setIconSize(QSize(32, 32));
|
||||
replayToEndButton->setIcon(QIcon(":/resources/replay_toend.svg"));
|
||||
connect(replayStopButton, SIGNAL(clicked()), this, SLOT(replayToEndButtonClicked()));
|
||||
|
||||
splitter = new QSplitter(Qt::Vertical);
|
||||
splitter->addWidget(cardInfo);
|
||||
splitter->addWidget(playerListWidget);
|
||||
|
|
@ -251,6 +306,19 @@ TabGame::TabGame(GameReplay *_replay)
|
|||
mainLayout->addLayout(deckViewContainerLayout, 10);
|
||||
mainLayout->addWidget(splitter);
|
||||
|
||||
QHBoxLayout *replayControlLayout = new QHBoxLayout;
|
||||
replayControlLayout->addWidget(timelineWidget, 10);
|
||||
replayControlLayout->addWidget(replayToStartButton);
|
||||
replayControlLayout->addWidget(replayStartButton);
|
||||
replayControlLayout->addWidget(replayPauseButton);
|
||||
replayControlLayout->addWidget(replayStopButton);
|
||||
replayControlLayout->addWidget(replayFastForwardButton);
|
||||
replayControlLayout->addWidget(replayToEndButton);
|
||||
|
||||
QVBoxLayout *superMainLayout = new QVBoxLayout;
|
||||
superMainLayout->addLayout(mainLayout);
|
||||
superMainLayout->addLayout(replayControlLayout);
|
||||
|
||||
aNextPhase = 0;
|
||||
aNextTurn = 0;
|
||||
aRemoveLocalArrows = 0;
|
||||
|
|
@ -263,16 +331,11 @@ TabGame::TabGame(GameReplay *_replay)
|
|||
tabMenu->addAction(aLeaveGame);
|
||||
|
||||
retranslateUi();
|
||||
setLayout(mainLayout);
|
||||
setLayout(superMainLayout);
|
||||
|
||||
splitter->restoreState(settingsCache->getTabGameSplitterSizes());
|
||||
|
||||
messageLog->logReplayStarted(gameId);
|
||||
|
||||
gameTimer = new QTimer(this);
|
||||
gameTimer->setInterval(1000);
|
||||
connect(gameTimer, SIGNAL(timeout()), this, SLOT(nextReplayStep()));
|
||||
gameTimer->start();
|
||||
}
|
||||
|
||||
TabGame::TabGame(TabSupervisor *_tabSupervisor, QList<AbstractClient *> &_clients, const Event_GameJoined &event)
|
||||
|
|
@ -461,15 +524,62 @@ void TabGame::closeRequest()
|
|||
actLeaveGame();
|
||||
}
|
||||
|
||||
void TabGame::nextReplayStep()
|
||||
void TabGame::replayNextEvent()
|
||||
{
|
||||
if (replay->event_list_size() <= currentReplayStep) {
|
||||
QMessageBox::information(this, "", "done");
|
||||
gameTimer->stop();
|
||||
return;
|
||||
}
|
||||
processGameEventContainer(replay->event_list(currentReplayStep), 0);
|
||||
++currentReplayStep;
|
||||
processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), 0);
|
||||
}
|
||||
|
||||
void TabGame::replayFinished()
|
||||
{
|
||||
replayStartButton->setEnabled(true);
|
||||
replayPauseButton->setEnabled(false);
|
||||
replayStopButton->setEnabled(false);
|
||||
replayFastForwardButton->setEnabled(false);
|
||||
}
|
||||
|
||||
void TabGame::replayToStartButtonClicked()
|
||||
{
|
||||
// XXX
|
||||
}
|
||||
|
||||
void TabGame::replayStartButtonClicked()
|
||||
{
|
||||
replayStartButton->setEnabled(false);
|
||||
replayPauseButton->setEnabled(true);
|
||||
replayStopButton->setEnabled(true);
|
||||
replayFastForwardButton->setEnabled(true);
|
||||
|
||||
timelineWidget->startReplay();
|
||||
}
|
||||
|
||||
void TabGame::replayPauseButtonClicked()
|
||||
{
|
||||
replayStartButton->setEnabled(true);
|
||||
replayPauseButton->setEnabled(false);
|
||||
replayFastForwardButton->setEnabled(false);
|
||||
|
||||
timelineWidget->stopReplay();
|
||||
}
|
||||
|
||||
void TabGame::replayStopButtonClicked()
|
||||
{
|
||||
replayStartButton->setEnabled(true);
|
||||
replayPauseButton->setEnabled(false);
|
||||
replayStopButton->setEnabled(false);
|
||||
replayFastForwardButton->setEnabled(false);
|
||||
|
||||
timelineWidget->stopReplay();
|
||||
// XXX to start
|
||||
}
|
||||
|
||||
void TabGame::replayFastForwardButtonToggled(bool checked)
|
||||
{
|
||||
timelineWidget->setTimeScaleFactor(checked ? 10.0 : 1.0);
|
||||
}
|
||||
|
||||
void TabGame::replayToEndButtonClicked()
|
||||
{
|
||||
// XXX
|
||||
}
|
||||
|
||||
void TabGame::incrementGameTime()
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ class QSplitter;
|
|||
class QLabel;
|
||||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QToolButton;
|
||||
class QMenu;
|
||||
class ZoneViewLayout;
|
||||
class ZoneViewWidget;
|
||||
class PhasesToolbar;
|
||||
class PlayerListWidget;
|
||||
class ReplayTimelineWidget;
|
||||
class Response;
|
||||
class GameEventContainer;
|
||||
class GameEventContext;
|
||||
|
|
@ -108,8 +110,13 @@ private:
|
|||
QStringList phasesList;
|
||||
int currentPhase;
|
||||
int activePlayer;
|
||||
|
||||
// Replay related members
|
||||
GameReplay *replay;
|
||||
int currentReplayStep;
|
||||
QList<int> replayTimeline;
|
||||
ReplayTimelineWidget *timelineWidget;
|
||||
QToolButton *replayToStartButton, *replayStartButton, *replayPauseButton, *replayStopButton, *replayFastForwardButton, *replayToEndButton;
|
||||
|
||||
QSplitter *splitter;
|
||||
CardInfoWidget *cardInfo;
|
||||
|
|
@ -158,7 +165,15 @@ signals:
|
|||
void containerProcessingDone();
|
||||
void openMessageDialog(const QString &userName, bool focus);
|
||||
private slots:
|
||||
void nextReplayStep();
|
||||
void replayNextEvent();
|
||||
void replayFinished();
|
||||
void replayToStartButtonClicked();
|
||||
void replayStartButtonClicked();
|
||||
void replayPauseButtonClicked();
|
||||
void replayStopButtonClicked();
|
||||
void replayFastForwardButtonToggled(bool checked);
|
||||
void replayToEndButtonClicked();
|
||||
|
||||
void incrementGameTime();
|
||||
void adminLockChanged(bool lock);
|
||||
void newCardAdded(AbstractCardItem *card);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue