diff --git a/cockatrice/src/client/network/replay_timeline_widget.cpp b/cockatrice/src/client/network/replay_timeline_widget.cpp index 0bfe93c79..28ae604b4 100644 --- a/cockatrice/src/client/network/replay_timeline_widget.cpp +++ b/cockatrice/src/client/network/replay_timeline_widget.cpp @@ -93,7 +93,7 @@ void ReplayTimelineWidget::skipToTime(int newTime, bool doRewindBuffering) if (isBackwardsSkip) { handleBackwardsSkip(doRewindBuffering); } else { - processNewEvents(); + processNewEvents(FORWARD_SKIP); } update(); @@ -130,7 +130,7 @@ void ReplayTimelineWidget::processRewind() // process the rewind currentEvent = 0; emit rewound(); - processNewEvents(); + processNewEvents(BACKWARD_SKIP); } QSize ReplayTimelineWidget::sizeHint() const @@ -147,17 +147,24 @@ void ReplayTimelineWidget::replayTimerTimeout() { currentTime += 200; - processNewEvents(); + processNewEvents(NORMAL_PLAYBACK); if (!(currentTime % 1000)) update(); } /// Processes all unprocessed events up to the current time. -void ReplayTimelineWidget::processNewEvents() +void ReplayTimelineWidget::processNewEvents(PlaybackMode playbackMode) { while ((currentEvent < replayTimeline.size()) && (replayTimeline[currentEvent] < currentTime)) { - emit processNextEvent(); + Player::EventProcessingOptions options; + + // backwards skip => always skip reveal windows + // forwards skip => skip reveal windows that don't happen within 10 seconds of the target + if (playbackMode == BACKWARD_SKIP || currentTime - replayTimeline[currentEvent] > 10000) + options |= Player::EventProcessingOption::SKIP_REVEAL_WINDOW; + + emit processNextEvent(options); ++currentEvent; } if (currentEvent == replayTimeline.size()) { diff --git a/cockatrice/src/client/network/replay_timeline_widget.h b/cockatrice/src/client/network/replay_timeline_widget.h index c096e388f..cd7077757 100644 --- a/cockatrice/src/client/network/replay_timeline_widget.h +++ b/cockatrice/src/client/network/replay_timeline_widget.h @@ -1,6 +1,8 @@ #ifndef REPLAY_TIMELINE_WIDGET #define REPLAY_TIMELINE_WIDGET +#include "../../game/player/player.h" + #include #include #include @@ -12,11 +14,18 @@ class ReplayTimelineWidget : public QWidget { Q_OBJECT signals: - void processNextEvent(); + void processNextEvent(Player::EventProcessingOptions options); void replayFinished(); void rewound(); private: + enum PlaybackMode + { + NORMAL_PLAYBACK, + FORWARD_SKIP, + BACKWARD_SKIP + }; + QTimer *replayTimer; static constexpr int BASE_REWIND_BUFFERING_TIMEOUT_MS = 180; static constexpr int MAX_REWIND_BUFFERING_TIMEOUT_MS = 280; @@ -33,7 +42,7 @@ private: void handleBackwardsSkip(bool doRewindBuffering); int calcRewindBufferingTimeout() const; void processRewind(); - void processNewEvents(); + void processNewEvents(PlaybackMode playbackMode); private slots: void replayTimerTimeout(); diff --git a/cockatrice/src/client/tabs/tab_game.cpp b/cockatrice/src/client/tabs/tab_game.cpp index 2b178e003..f16767a59 100644 --- a/cockatrice/src/client/tabs/tab_game.cpp +++ b/cockatrice/src/client/tabs/tab_game.cpp @@ -614,9 +614,9 @@ void TabGame::closeRequest() actLeaveGame(); } -void TabGame::replayNextEvent() +void TabGame::replayNextEvent(Player::EventProcessingOptions options) { - processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), nullptr); + processGameEventContainer(replay->event_list(timelineWidget->getCurrentEvent()), nullptr, options); } void TabGame::replayFinished() @@ -862,7 +862,9 @@ Player *TabGame::addPlayer(int playerId, const ServerInfo_User &info) return newPlayer; } -void TabGame::processGameEventContainer(const GameEventContainer &cont, AbstractClient *client) +void TabGame::processGameEventContainer(const GameEventContainer &cont, + AbstractClient *client, + Player::EventProcessingOptions options) { const GameEventContext &context = cont.context(); messageLog->containerProcessingStarted(context); @@ -937,7 +939,7 @@ void TabGame::processGameEventContainer(const GameEventContainer &cont, Abstract qDebug() << "unhandled game event: invalid player id"; break; } - player->processGameEvent(eventType, event, context); + player->processGameEvent(eventType, event, context, options); emitUserEvent(); } } @@ -1731,7 +1733,8 @@ void TabGame::createReplayDock() // timeline widget timelineWidget = new ReplayTimelineWidget; timelineWidget->setTimeline(replayTimeline); - connect(timelineWidget, SIGNAL(processNextEvent()), this, SLOT(replayNextEvent())); + connect(timelineWidget, SIGNAL(processNextEvent(Player::EventProcessingOptions)), this, + SLOT(replayNextEvent(Player::EventProcessingOptions))); connect(timelineWidget, SIGNAL(replayFinished()), this, SLOT(replayFinished())); connect(timelineWidget, &ReplayTimelineWidget::rewound, this, &TabGame::replayRewind); diff --git a/cockatrice/src/client/tabs/tab_game.h b/cockatrice/src/client/tabs/tab_game.h index 16befd8bc..d3452bda5 100644 --- a/cockatrice/src/client/tabs/tab_game.h +++ b/cockatrice/src/client/tabs/tab_game.h @@ -2,6 +2,7 @@ #define TAB_GAME_H #include "../../client/tearoff_menu.h" +#include "../../game/player/player.h" #include "pb/event_leave.pb.h" #include "pb/serverinfo_game.pb.h" #include "tab.h" @@ -47,7 +48,6 @@ class Event_Ping; class Event_GameSay; class Event_Kicked; class Event_ReverseTurn; -class Player; class CardZone; class AbstractCardItem; class CardItem; @@ -221,7 +221,7 @@ signals: void openDeckEditor(const DeckLoader *deck); void notIdle(); private slots: - void replayNextEvent(); + void replayNextEvent(Player::EventProcessingOptions options); void replayFinished(); void replayPlayButtonToggled(bool checked); void replayFastForwardButtonToggled(bool checked); @@ -307,7 +307,9 @@ public: return activeCard; } - void processGameEventContainer(const GameEventContainer &cont, AbstractClient *client); + void processGameEventContainer(const GameEventContainer &cont, + AbstractClient *client, + Player::EventProcessingOptions options); PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd); PendingCommand *prepareGameCommand(const QList &cmdList); public slots: diff --git a/cockatrice/src/client/tabs/tab_supervisor.cpp b/cockatrice/src/client/tabs/tab_supervisor.cpp index 567469249..fc6c46728 100644 --- a/cockatrice/src/client/tabs/tab_supervisor.cpp +++ b/cockatrice/src/client/tabs/tab_supervisor.cpp @@ -541,7 +541,7 @@ void TabSupervisor::processGameEventContainer(const GameEventContainer &cont) { TabGame *tab = gameTabs.value(cont.game_id()); if (tab) - tab->processGameEventContainer(cont, qobject_cast(sender())); + tab->processGameEventContainer(cont, qobject_cast(sender()), {}); else qDebug() << "gameEvent: invalid gameId"; } diff --git a/cockatrice/src/game/player/player.cpp b/cockatrice/src/game/player/player.cpp index e20b60451..f51006d4f 100644 --- a/cockatrice/src/game/player/player.cpp +++ b/cockatrice/src/game/player/player.cpp @@ -2341,7 +2341,7 @@ void Player::eventDrawCards(const Event_DrawCards &event) emit logDrawCards(this, event.number(), _deck->getCards().size() == 0); } -void Player::eventRevealCards(const Event_RevealCards &event) +void Player::eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options) { CardZone *zone = zones.value(QString::fromStdString(event.zone_name())); if (!zone) { @@ -2388,7 +2388,7 @@ void Player::eventRevealCards(const Event_RevealCards &event) showZoneView = false; } } - if (showZoneView && !cardList.isEmpty()) { + if (!options.testFlag(SKIP_REVEAL_WINDOW) && showZoneView && !cardList.isEmpty()) { static_cast(scene())->addRevealedZoneView(this, zone, cardList, event.grant_write_access()); } @@ -2415,7 +2415,10 @@ void Player::eventChangeZoneProperties(const Event_ChangeZoneProperties &event) } } -void Player::processGameEvent(GameEvent::GameEventType type, const GameEvent &event, const GameEventContext &context) +void Player::processGameEvent(GameEvent::GameEventType type, + const GameEvent &event, + const GameEventContext &context, + EventProcessingOptions options) { switch (type) { case GameEvent::GAME_SAY: @@ -2470,7 +2473,7 @@ void Player::processGameEvent(GameEvent::GameEventType type, const GameEvent &ev eventDrawCards(event.GetExtension(Event_DrawCards::ext)); break; case GameEvent::REVEAL_CARDS: - eventRevealCards(event.GetExtension(Event_RevealCards::ext)); + eventRevealCards(event.GetExtension(Event_RevealCards::ext), options); break; case GameEvent::CHANGE_ZONE_PROPERTIES: eventChangeZoneProperties(event.GetExtension(Event_ChangeZoneProperties::ext)); diff --git a/cockatrice/src/game/player/player.h b/cockatrice/src/game/player/player.h index 5c655add1..832e613b7 100644 --- a/cockatrice/src/game/player/player.h +++ b/cockatrice/src/game/player/player.h @@ -226,6 +226,13 @@ private slots: void initSayMenu(); +public: + enum EventProcessingOption + { + SKIP_REVEAL_WINDOW = 0x0001 + }; + Q_DECLARE_FLAGS(EventProcessingOptions, EventProcessingOption) + private: TabGame *game; QMenu *sbMenu, *countersMenu, *sayMenu, *createPredefinedTokenMenu, *mRevealLibrary, *mLendLibrary, *mRevealTopCard, @@ -332,7 +339,7 @@ private: void eventDestroyCard(const Event_DestroyCard &event); void eventAttachCard(const Event_AttachCard &event); void eventDrawCards(const Event_DrawCards &event); - void eventRevealCards(const Event_RevealCards &event); + void eventRevealCards(const Event_RevealCards &event, EventProcessingOptions options); void eventChangeZoneProperties(const Event_ChangeZoneProperties &event); void cmdSetTopCard(Command_MoveCard &cmd); void cmdSetBottomCard(Command_MoveCard &cmd); @@ -470,7 +477,10 @@ public: void processPlayerInfo(const ServerInfo_Player &info); void processCardAttachment(const ServerInfo_Player &info); - void processGameEvent(GameEvent::GameEventType type, const GameEvent &event, const GameEventContext &context); + void processGameEvent(GameEvent::GameEventType type, + const GameEvent &event, + const GameEventContext &context, + EventProcessingOptions options); PendingCommand *prepareGameCommand(const ::google::protobuf::Message &cmd); PendingCommand *prepareGameCommand(const QList &cmdList); @@ -480,6 +490,8 @@ public: void setLastToken(CardInfoPtr cardInfo); }; +Q_DECLARE_OPERATORS_FOR_FLAGS(Player::EventProcessingOptions) + class AnnotationDialog : public QInputDialog { Q_OBJECT