diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -543,7 +543,7 @@ public: this->SelectFirstAvailableAirport(false); } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { CheckRedrawStationCoverage(this); } diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -451,7 +451,7 @@ public: } } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { CheckRedrawStationCoverage(this); } diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -539,7 +539,7 @@ public: if (widget == WID_CV_KEY_BUTTON) ShowGraphLegend(); } - virtual void OnTick() + virtual void OnGameTick() { this->UpdateStatistics(false); } @@ -998,9 +998,9 @@ struct PaymentRatesGraphWindow : BaseGra } } - virtual void OnTick() + virtual void OnGameTick() { - /* Override default OnTick */ + /* Override default OnGameTick */ } /** @@ -1239,7 +1239,7 @@ public: } - virtual void OnTick() + virtual void OnGameTick() { if (this->companies.NeedResort()) { this->SetDirty(); @@ -1472,7 +1472,7 @@ struct PerformanceRatingDetailWindow : W } } - virtual void OnTick() + virtual void OnGameTick() { if (_pause_mode != PM_UNPAUSED) return; diff --git a/src/group_gui.cpp b/src/group_gui.cpp --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -858,7 +858,7 @@ public: this->SetDirty(); } - virtual void OnTick() + virtual void OnGameTick() { if (_pause_mode != PM_UNPAUSED) return; if (this->groups.NeedResort() || this->vehicles.NeedResort()) { diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -647,7 +647,7 @@ public: if (success && !_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); } - virtual void OnTick() + virtual void OnGameTick() { if (_pause_mode != PM_UNPAUSED) return; if (!this->timer_enabled) return; diff --git a/src/main_gui.cpp b/src/main_gui.cpp --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -235,10 +235,11 @@ enum { struct MainWindow : Window { - uint refresh; + int refresh; - static const uint LINKGRAPH_REFRESH_PERIOD = 0xff; - static const uint LINKGRAPH_DELAY = 0xf; + /* Refresh times in milliseconds */ + static const uint LINKGRAPH_REFRESH_PERIOD = 7650; + static const uint LINKGRAPH_DELAY = 450; MainWindow(WindowDesc *desc) : Window(desc) { @@ -253,9 +254,9 @@ struct MainWindow : Window this->refresh = LINKGRAPH_DELAY; } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { - if (--this->refresh > 0) return; + if (!TimerElapsed(this->refresh, delta_ms)) return; this->refresh = LINKGRAPH_REFRESH_PERIOD; diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -454,15 +454,16 @@ static const char * const _credits[] = { struct AboutWindow : public Window { int text_position; ///< The top of the scrolling text - byte counter; ///< Used to scroll the text every 5 ticks int line_height; ///< The height of a single line static const int num_visible_lines = 19; ///< The number of lines visible simultaneously + static const uint TIMER_INTERVAL = 150; ///< Scrolling interval in ms + uint timer; + AboutWindow() : Window(&_about_desc) { this->InitNested(WN_GAME_OPTIONS_ABOUT); - this->counter = 5; this->text_position = this->GetWidget(WID_A_SCROLLING_TEXT)->pos_y + this->GetWidget(WID_A_SCROLLING_TEXT)->current_y; } @@ -502,11 +503,11 @@ struct AboutWindow : public Window { } } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { - if (--this->counter == 0) { - this->counter = 5; - this->text_position--; + uint count = CountIntervalElapsed(this->timer, delta_ms, TIMER_INTERVAL); + if (count > 0) { + this->text_position -= count; /* If the last text has scrolled start a new from the start */ if (this->text_position < (int)(this->GetWidget(WID_A_SCROLLING_TEXT)->pos_y - lengthof(_credits) * this->line_height)) { this->text_position = this->GetWidget(WID_A_SCROLLING_TEXT)->pos_y + this->GetWidget(WID_A_SCROLLING_TEXT)->current_y; diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -403,7 +403,7 @@ struct NewGRFParametersWindow : public W par_info->SetValue(this->grf_config, val); this->clicked_button = num; - this->timeout = 5; + this->timeout = 150; } } else if (par_info->type == PTYPE_UINT_ENUM && !par_info->complete_labels && click_count >= 2) { /* Display a query box so users can enter a custom value. */ @@ -483,9 +483,9 @@ struct NewGRFParametersWindow : public W } } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { - if (--this->timeout == 0) { + if (TimerElapsed(this->timeout, delta_ms)) { this->clicked_button = UINT_MAX; this->SetDirty(); } diff --git a/src/news_gui.cpp b/src/news_gui.cpp --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -260,11 +260,13 @@ struct NewsWindow : Window { uint16 chat_height; ///< Height of the chat window. uint16 status_height; ///< Height of the status bar window const NewsItem *ni; ///< News item to display. - static uint duration; ///< Remaining time for showing current news message (may only be accessed while a news item is displayed). + static int duration; ///< Remaining time for showing the current news message (may only be access while a news item is displayed). + + uint timer; NewsWindow(WindowDesc *desc, const NewsItem *ni) : Window(desc), ni(ni) { - NewsWindow::duration = 555; + NewsWindow::duration = 16650; const Window *w = FindWindowByClass(WC_SEND_NETWORK_MSG); this->chat_height = (w != NULL) ? w->height : 0; this->status_height = FindWindowById(WC_STATUS_BAR, 0)->height; @@ -485,11 +487,18 @@ struct NewsWindow : Window { this->SetWindowTop(newtop); } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { - /* Scroll up newsmessages from the bottom in steps of 4 pixels */ - int newtop = max(this->top - 4, _screen.height - this->height - this->status_height - this->chat_height); - this->SetWindowTop(newtop); + int count = CountIntervalElapsed(this->timer, delta_ms, 15); + if (count > 0) { + /* Scroll up newsmessages from the bottom */ + int newtop = max(this->top - 2 * count, _screen.height - this->height - this->status_height - this->chat_height); + this->SetWindowTop(newtop); + } + + /* Decrement the news timer. We don't need to action an elapsed event here, + * so no need to use TimerElapsed(). */ + if (NewsWindow::duration > 0) NewsWindow::duration -= delta_ms; } private: @@ -536,7 +545,7 @@ private: } }; -/* static */ uint NewsWindow::duration = 0; // Instance creation. +/* static */ int NewsWindow::duration = 0; // Instance creation. /** Open up an own newspaper window for the news item */ @@ -588,11 +597,8 @@ static bool ReadyForNextItem() * Check if the status bar message is still being displayed? */ if (IsNewsTickerShown()) return false; - /* Newspaper message, decrement duration counter */ - if (NewsWindow::duration != 0) NewsWindow::duration--; - /* neither newsticker nor newspaper are running */ - return (NewsWindow::duration == 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL); + return (NewsWindow::duration <= 0 || FindWindowById(WC_NEWS_WINDOW, 0) == NULL); } /** Move to the next news item */ diff --git a/src/openttd.cpp b/src/openttd.cpp --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -76,7 +76,7 @@ void IncreaseDate(); void DoPaletteAnimations(); void MusicLoop(); void ResetMusic(); -void CallWindowTickEvent(); +void CallWindowGameTickEvent(); bool HandleBootstrap(); extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY); @@ -1363,7 +1363,6 @@ void StateGameLoop() #ifndef DEBUG_DUMP_COMMANDS Game::GameLoop(); #endif - CallWindowTickEvent(); return; } @@ -1381,7 +1380,7 @@ void StateGameLoop() BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP); UpdateLandscapingLimits(); - CallWindowTickEvent(); + CallWindowGameTickEvent(); NewsLoop(); } else { if (_debug_desync_level > 2 && _date_fract == 0 && (_date & 0x1F) == 0) { @@ -1411,7 +1410,7 @@ void StateGameLoop() #endif UpdateLandscapingLimits(); - CallWindowTickEvent(); + CallWindowGameTickEvent(); NewsLoop(); cur_company.Restore(); } diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1368,7 +1368,7 @@ public: } } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { CheckRedrawStationCoverage(this); } diff --git a/src/road_gui.cpp b/src/road_gui.cpp --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -1053,7 +1053,7 @@ struct BuildRoadStationWindow : public P } } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { CheckRedrawStationCoverage(this); } diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp --- a/src/smallmap_gui.cpp +++ b/src/smallmap_gui.cpp @@ -1570,10 +1570,10 @@ int SmallMapWindow::GetPositionOnLegend( } } -/* virtual */ void SmallMapWindow::OnTick() +/* virtual */ void SmallMapWindow::OnRealtimeTick(uint delta_ms) { /* Update the window every now and then */ - if (--this->refresh != 0) return; + if (!TimerElapsed(this->refresh, delta_ms)) return; if (this->map_type == SMT_LINKSTATS) { uint32 company_mask = this->GetOverlayCompanyMask(); diff --git a/src/smallmap_gui.h b/src/smallmap_gui.h --- a/src/smallmap_gui.h +++ b/src/smallmap_gui.h @@ -67,8 +67,8 @@ protected: static const uint LEGEND_BLOB_WIDTH = 8; ///< Width of the coloured blob in front of a line text in the #WID_SM_LEGEND widget. static const uint INDUSTRY_MIN_NUMBER_OF_COLUMNS = 2; ///< Minimal number of columns in the #WID_SM_LEGEND widget for the #SMT_INDUSTRY legend. - static const uint FORCE_REFRESH_PERIOD = 0x1F; ///< map is redrawn after that many ticks - static const uint BLINK_PERIOD = 0x0F; ///< highlight blinking interval + static const uint FORCE_REFRESH_PERIOD = 930; ///< map is redrawn after that many milliseconds. + static const uint BLINK_PERIOD = 450; ///< highlight blinking interval in milliseconds. uint min_number_of_columns; ///< Minimal number of columns in legends. uint min_number_of_fixed_rows; ///< Minimal number of rows in the legends for the fixed layouts only (all except #SMT_INDUSTRY). @@ -79,7 +79,7 @@ protected: int32 subscroll; ///< Number of pixels (0..3) between the right end of the base tile and the pixel at the top-left corner of the smallmap display. int zoom; ///< Zoom level. Bigger number means more zoom-out (further away). - uint8 refresh; ///< Refresh counter, zeroed every FORCE_REFRESH_PERIOD ticks. + int refresh; ///< Refresh timer, in millseconds. LinkGraphOverlay *overlay; static void BreakIndustryChainLink(); @@ -187,7 +187,7 @@ public: virtual void OnInvalidateData(int data = 0, bool gui_scope = true); virtual bool OnRightClick(Point pt, int widget); virtual void OnMouseWheel(int wheel); - virtual void OnTick(); + virtual void OnRealtimeTick(uint delta_ms); virtual void OnScroll(Point delta); virtual void OnMouseOver(Point pt, int widget); }; diff --git a/src/station_gui.cpp b/src/station_gui.cpp --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -621,7 +621,7 @@ public: } } - virtual void OnTick() + virtual void OnGameTick() { if (_pause_mode != PM_UNPAUSED) return; if (this->stations.NeedResort()) { @@ -2312,7 +2312,7 @@ struct SelectStationWindow : Window { DeleteWindowById(WC_SELECT_STATION, 0); } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { if (_thd.dirty & 2) { _thd.dirty &= ~2; diff --git a/src/statusbar_gui.cpp b/src/statusbar_gui.cpp --- a/src/statusbar_gui.cpp +++ b/src/statusbar_gui.cpp @@ -79,10 +79,11 @@ static bool DrawScrollingStatusText(cons struct StatusBarWindow : Window { bool saving; int ticker_scroll; + uint ticker_timer; int reminder_timeout; static const int TICKER_STOP = 1640; ///< scrolling is finished when counter reaches this value - static const int REMINDER_START = 91; ///< initial value of the reminder counter (right dot on the right) + static const int REMINDER_START = 1350; ///< time in ms for reminder notification (red dot on the right) to stay static const int REMINDER_STOP = 0; ///< reminder disappears when counter reaches this value static const int COUNTER_STEP = 2; ///< this is subtracted from active counters every tick @@ -203,6 +204,7 @@ struct StatusBarWindow : Window { case SBI_SHOW_REMINDER: this->reminder_timeout = REMINDER_START; break; case SBI_NEWS_DELETED: this->ticker_scroll = TICKER_STOP; // reset ticker ... + this->ticker_timer = 0; this->reminder_timeout = REMINDER_STOP; // ... and reminder break; } @@ -217,19 +219,20 @@ struct StatusBarWindow : Window { } } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { if (_pause_mode != PM_UNPAUSED) return; if (this->ticker_scroll < TICKER_STOP) { // Scrolling text - this->ticker_scroll += COUNTER_STEP; - this->SetWidgetDirty(WID_S_MIDDLE); + uint count = CountIntervalElapsed(this->ticker_timer, delta_ms, 15); + if (count > 0) { + this->ticker_scroll += count; + this->SetWidgetDirty(WID_S_MIDDLE); + } } - if (this->reminder_timeout > REMINDER_STOP) { // Red blot to show there are new unread newsmessages - this->reminder_timeout -= COUNTER_STEP; - } else if (this->reminder_timeout < REMINDER_STOP) { - this->reminder_timeout = REMINDER_STOP; + // Red blot to show there are new unread newsmessages + if (TimerElapsed(this->reminder_timeout, delta_ms)) { this->SetWidgetDirty(WID_S_MIDDLE); } } diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -1978,6 +1978,8 @@ enum MainToolbarHotkeys { /** Main toolbar. */ struct MainToolbarWindow : Window { + int timer; + MainToolbarWindow(WindowDesc *desc) : Window(desc) { this->InitNested(0); @@ -1988,6 +1990,8 @@ struct MainToolbarWindow : Window { this->SetWidgetDisabledState(WID_TN_FAST_FORWARD, _networking); // if networking, disable fast-forward button PositionMainToolbar(this); DoZoomInOutWindow(ZOOM_NONE, this); + + this->timer = MILLISECONDS_PER_TICK; } virtual void FindWindowPlacementAndResize(int def_width, int def_height) @@ -2092,8 +2096,11 @@ struct MainToolbarWindow : Window { _last_started_action = CBF_NONE; } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { + if (!TimerElapsed(this->timer, delta_ms)) return; + this->timer = MILLISECONDS_PER_TICK; + if (this->IsWidgetLowered(WID_TN_PAUSE) != !!_pause_mode) { this->ToggleWidgetLoweredState(WID_TN_PAUSE); this->SetWidgetDirty(WID_TN_PAUSE); @@ -2310,6 +2317,8 @@ enum MainToolbarEditorHotkeys { }; struct ScenarioEditorToolbarWindow : Window { + int timer; + ScenarioEditorToolbarWindow(WindowDesc *desc) : Window(desc) { this->InitNested(0); @@ -2318,6 +2327,8 @@ struct ScenarioEditorToolbarWindow : Win CLRBITS(this->flags, WF_WHITE_BORDER); PositionMainToolbar(this); DoZoomInOutWindow(ZOOM_NONE, this); + + this->timer = MILLISECONDS_PER_TICK; } virtual void FindWindowPlacementAndResize(int def_width, int def_height) @@ -2445,8 +2456,11 @@ struct ScenarioEditorToolbarWindow : Win this->SetWidgetDirty(WID_TE_DATE_FORWARD); } - virtual void OnTick() + virtual void OnRealtimeTick(uint delta_ms) { + if (!TimerElapsed(this->timer, delta_ms)) return; + this->timer = MILLISECONDS_PER_TICK; + if (this->IsWidgetLowered(WID_TE_PAUSE) != !!_pause_mode) { this->ToggleWidgetLoweredState(WID_TE_PAUSE); this->SetDirty(); diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -1676,7 +1676,7 @@ public: this->SetDirty(); } - virtual void OnTick() + virtual void OnGameTick() { if (_pause_mode != PM_UNPAUSED) return; if (this->vehicles.NeedResort()) { @@ -2733,7 +2733,7 @@ public: } } - virtual void OnTick() + virtual void OnGameTick() { const Vehicle *v = Vehicle::Get(this->window_number); bool veh_stopped = v->IsStoppedInDepot(); diff --git a/src/window.cpp b/src/window.cpp --- a/src/window.cpp +++ b/src/window.cpp @@ -3075,13 +3075,32 @@ void InputLoop() } /** + * Dispatch OnRealtimeTick event over all windows + */ +void CallWindowRealtimeTickEvent(uint delta_ms) +{ + Window *w; + FOR_ALL_WINDOWS_FROM_FRONT(w) { + w->OnRealtimeTick(delta_ms); + } +} + +/** * Update the continuously changing contents of the windows, such as the viewports */ void UpdateWindows() { + static uint32 last_realtime_tick = _realtime_tick; + uint delta_ms = _realtime_tick - last_realtime_tick; + last_realtime_tick = _realtime_tick; + + if (delta_ms == 0) return; + PerformanceMeasurer framerate(PFE_DRAWING); PerformanceAccumulator::Reset(PFE_DRAWWORLD); + CallWindowRealtimeTickEvent(delta_ms); + Window *w; static int highlight_timer = 1; @@ -3263,13 +3282,13 @@ void InvalidateWindowClassesData(WindowC } /** - * Dispatch WE_TICK event over all windows + * Dispatch OnTick event over all windows */ -void CallWindowTickEvent() +void CallWindowGameTickEvent() { Window *w; FOR_ALL_WINDOWS_FROM_FRONT(w) { - w->OnTick(); + w->OnGameTick(); } } diff --git a/src/window_func.h b/src/window_func.h --- a/src/window_func.h +++ b/src/window_func.h @@ -56,4 +56,36 @@ void DeleteWindowByClass(WindowClass cls bool EditBoxInGlobalFocus(); Point GetCaretPosition(); +/** + * Count how many times the interval has elapsed, and update the timer. + * Use to ensure a specific amount of events happen within a timeframe, e.g. for animation. + * The timer value does not need to be initialised. + * @param timer Timer to test. Value will be increased. + * @param delta Time since last test. + * @param interval Timing interval. + * @return Number of times the interval has elapsed. + */ +static inline uint CountIntervalElapsed(uint &timer, uint delta, uint interval) +{ + uint count = delta / interval; + if (timer + (delta % interval) >= interval) count++; + timer = (timer + delta) % interval; + return count; +} + +/** + * Test if a timer has elapsed, and update the timer. + * Use to ensure an event happens only once within a timeframe, e.g. for window updates. + * The timer value must be initialised in order for the timer to elapsed. + * @param timer Timer to test. Value will be decreased. + * @param delta Time since last test. + * @return True iff the timer has elapsed. + */ +static inline bool TimerElapsed(int &timer, uint delta) +{ + if (timer <= 0) return false; + timer -= delta; + return timer <= 0; +} + #endif /* WINDOW_FUNC_H */ diff --git a/src/window_gui.h b/src/window_gui.h --- a/src/window_gui.h +++ b/src/window_gui.h @@ -678,7 +678,7 @@ public: /** * Called once per (game) tick. */ - virtual void OnTick() {} + virtual void OnGameTick() {} /** * Called once every 100 (game) ticks. @@ -686,6 +686,11 @@ public: virtual void OnHundredthTick() {} /** + * Called periodically. + */ + virtual void OnRealtimeTick(uint delta_ms) {} + + /** * Called when this window's timeout has been reached. */ virtual void OnTimeout() {}