diff --git a/src/openrct2/management/NewsItem.cpp b/src/openrct2/management/NewsItem.cpp index 2851d824e1..76cc2fb9f7 100644 --- a/src/openrct2/management/NewsItem.cpp +++ b/src/openrct2/management/NewsItem.cpp @@ -23,7 +23,7 @@ #include "../windows/Intent.h" #include "../world/Sprite.h" -NewsItemQueue gNewsItems; +NewsItemQueues gNewsItems; /** rct2: 0x0097BE7C */ const uint8_t news_type_properties[] = { @@ -39,24 +39,14 @@ const uint8_t news_type_properties[] = { NEWS_TYPE_HAS_SUBJECT, // NEWS_ITEM_GRAPH }; -NewsItem& NewsItemQueue::Current() +NewsItem& NewsItemQueues::Current() { - return Recent[0]; + return Recent.front(); } -const NewsItem& NewsItemQueue::Current() const +const NewsItem& NewsItemQueues::Current() const { - return Recent[0]; -} - -NewsItem& NewsItemQueue::Oldest() -{ - return Archived[0]; -} - -const NewsItem& NewsItemQueue::Oldest() const -{ - return Archived[0]; + return Recent.front(); } bool news_item_is_valid_idx(int32_t index) @@ -74,25 +64,25 @@ NewsItem* news_item_get(int32_t index) return gNewsItems.At(index); } -NewsItem& NewsItemQueue::operator[](size_t index) +NewsItem& NewsItemQueues::operator[](size_t index) { - return const_cast(const_cast(*this)[index]); + return const_cast(const_cast(*this)[index]); } -const NewsItem& NewsItemQueue::operator[](size_t index) const +const NewsItem& NewsItemQueues::operator[](size_t index) const { - if (index < NEWS_ITEM_HISTORY_START) + if (index < Recent.capacity()) return Recent[index]; else - return Archived[index - NEWS_ITEM_HISTORY_START]; + return Archived[index - Recent.capacity()]; } -NewsItem* NewsItemQueue::At(int32_t index) +NewsItem* NewsItemQueues::At(int32_t index) { - return const_cast(const_cast(*this).At(index)); + return const_cast(const_cast(*this).At(index)); } -const NewsItem* NewsItemQueue::At(int32_t index) const +const NewsItem* NewsItemQueues::At(int32_t index) const { if (news_item_is_valid_idx(index)) { @@ -115,25 +105,17 @@ bool news_item_is_queue_empty() return gNewsItems.IsEmpty(); } -bool NewsItemQueue::IsEmpty() const +bool NewsItemQueues::IsEmpty() const { - return Current().IsEmpty(); + return Recent.empty(); } /** * * rct2: 0x0066DF32 */ -void NewsItemQueue::Init() -{ - Current().Type = NEWS_ITEM_NULL; - Oldest().Type = NEWS_ITEM_NULL; -} - void news_item_init_queue() { - gNewsItems.Init(); - // Throttles for warning types (PEEP_*_WARNING) for (auto& warningThrottle : gPeepWarningThrottle) { @@ -144,7 +126,7 @@ void news_item_init_queue() context_broadcast_intent(&intent); } -uint16_t NewsItemQueue::IncrementTicks() +uint16_t NewsItemQueues::IncrementTicks() { return ++Current().Ticks; } @@ -160,7 +142,7 @@ static void news_item_tick_current() } } -int32_t NewsItemQueue::RemoveTime() const +int32_t NewsItemQueues::RemoveTime() const { if (!Recent[5].IsEmpty() && !Recent[4].IsEmpty() && !Recent[3].IsEmpty() && !Recent[2].IsEmpty()) { @@ -169,7 +151,7 @@ int32_t NewsItemQueue::RemoveTime() const return 320; } -bool NewsItemQueue::CurrentShouldBeArchived() const +bool NewsItemQueues::CurrentShouldBeArchived() const { return Current().Ticks >= RemoveTime(); } @@ -204,47 +186,25 @@ void news_item_close_current() gNewsItems.ArchiveCurrent(); } -void NewsItemQueue::ArchiveCurrent() +void NewsItemQueues::ArchiveCurrent() { // Check if there is a current message if (IsEmpty()) return; - AppendToArchive(Current()); + Archived.push_back(Current()); // Invalidate the news window window_invalidate_by_class(WC_RECENT_NEWS); // Dequeue the current news item, shift news up - memmove(Recent, Recent + 1, sizeof(NewsItem) * (std::size(Recent) - 1)); - Recent[NEWS_ITEM_HISTORY_START - 1].Type = NEWS_ITEM_NULL; + Recent.pop_front(); // Invalidate current news item bar auto intent = Intent(INTENT_ACTION_INVALIDATE_TICKER_NEWS); context_broadcast_intent(&intent); } -/** - * Finds a spare history slot or replaces an existing one if there are no spare - * slots available. - */ -void NewsItemQueue::AppendToArchive(NewsItem& item) -{ - auto it = std::find_if(std::begin(Archived), std::end(Archived), [](const auto& newsItem) { return newsItem.IsEmpty(); }); - if (it != std::end(Archived)) - { - *it = item; - ++it; - if (it != std::end(Archived)) - it->Type = NEWS_ITEM_NULL; - return; - } - - // Dequeue the first history news item, shift history up - memmove(Archived, Archived + 1, sizeof(NewsItem) * (std::size(Archived) - 1)); - Archived[MAX_NEWS_ITEMS_ARCHIVE - 1] = item; -} - /** * Get the (x,y,z) coordinates of the subject of a news item. * If the new item is no longer valid, return LOCATION_NULL in the x-coordinate @@ -339,17 +299,20 @@ std::optional news_item_get_subject_location(int32_t type, int32_t su return subjectLoc; } -NewsItem* NewsItemQueue::FirstOpenOrNewSlot() +NewsItem* NewsItemQueues::FirstOpenOrNewSlot() { - auto it = std::begin(Recent); - for (; !it->IsEmpty();) + for (auto emptySlots = Recent.capacity() - Recent.size(); emptySlots < 2; ++emptySlots) { - if (it + 2 >= std::end(Recent)) - ArchiveCurrent(); - else - it++; + ArchiveCurrent(); } - return &*it; + + auto res = Recent.end(); + // The for loop above guarantees there is always an extra element to use + assert(Recent.capacity() - Recent.size() >= 2); + auto newsItem = res + 1; + newsItem->Type = NEWS_ITEM_NULL; + + return &*res; } /** @@ -377,14 +340,7 @@ NewsItem* news_item_add_to_queue_raw(uint8_t type, const utf8* text, uint32_t as newsItem->Day = ((days_in_month[date_get_month(newsItem->MonthYear)] * gDateMonthTicks) >> 16) + 1; safe_strcpy(newsItem->Text, text, sizeof(newsItem->Text)); - NewsItem* res = newsItem; - - // Blatant disregard for what happens on the last element. - // TODO: Change this when we implement the queue ourselves. - newsItem++; - newsItem->Type = NEWS_ITEM_NULL; - - return res; + return newsItem; } /** @@ -506,8 +462,6 @@ void news_item_add_to_queue_custom(NewsItem* newNewsItem) { NewsItem* newsItem = gNewsItems.FirstOpenOrNewSlot(); *newsItem = *newNewsItem; - newsItem++; - newsItem->Type = NEWS_ITEM_NULL; } void news_item_remove(int32_t index) diff --git a/src/openrct2/management/NewsItem.h b/src/openrct2/management/NewsItem.h index 3623bac716..3367036cfc 100644 --- a/src/openrct2/management/NewsItem.h +++ b/src/openrct2/management/NewsItem.h @@ -12,6 +12,8 @@ #include "../common.h" #include "../world/Location.hpp" +#include +#include #include enum @@ -65,7 +67,123 @@ constexpr int32_t MAX_NEWS_ITEMS = NEWS_ITEM_HISTORY_START + MAX_NEWS_ITEMS_ARCH extern const uint8_t news_type_properties[10]; -struct NewsItemQueue +template class NewsItemQueue +{ +public: + static_assert(N > 0, "Cannot instantiate NewsItemQueue with size=0"); + + using value_type = typename std::array::value_type; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator = typename std::array::iterator; + using const_iterator = typename std::array::const_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + NewsItemQueue() + { + Queue[0].Type = NEWS_ITEM_NULL; + } + + constexpr iterator begin() noexcept + { + return std::begin(Queue); + } + constexpr const_iterator begin() const noexcept + { + return cbegin(); + } + constexpr const_iterator cbegin() const noexcept + { + return std::cbegin(Queue); + } + iterator end() noexcept + { + return std::find_if(std::begin(Queue), std::end(Queue), [](const_reference item) { return item.IsEmpty(); }); + } + const_iterator end() const noexcept + { + return cend(); + } + const_iterator cend() const noexcept + { + return std::find_if(std::cbegin(Queue), std::cend(Queue), [](const_reference item) { return item.IsEmpty(); }); + } + + constexpr bool empty() const noexcept + { + return Queue[0].IsEmpty(); + } + + size_type size() const noexcept + { + return std::distance(cbegin(), cend()); + } + + reference front() noexcept + { + return *begin(); + } + const_reference front() const noexcept + { + return *cbegin(); + } + reference back() noexcept + { + return *end(); + } + const_reference back() const noexcept + { + return *cend(); + } + + void pop_front() + { + std::move(std::begin(Queue) + 1, std::end(Queue), std::begin(Queue)); + Queue[N - 1].Type = NEWS_ITEM_NULL; + } + + void push_back(const_reference item) + { + auto it = end(); + if (!std::distance(it, std::end(Queue))) + { + // Reached queue max size, need to free some space + pop_front(); + Queue[N - 1] = item; + } + else + { + *it = item; + ++it; + if (std::distance(it, std::end(Queue))) + it->Type = NEWS_ITEM_NULL; + } + } + + reference operator[](size_type n) noexcept + { + return Queue[n]; + } + const_reference operator[](size_type n) const noexcept + { + return Queue[n]; + } + + constexpr size_type capacity() const noexcept + { + return N; + } + +private: + std::array Queue; +}; + +struct NewsItemQueues { NewsItem& operator[](size_t index); const NewsItem& operator[](size_t index) const; @@ -86,8 +204,6 @@ struct NewsItemQueue { for (auto& newsItem : Recent) { - if (newsItem.IsEmpty()) - break; p(newsItem); } } @@ -96,8 +212,6 @@ struct NewsItemQueue { for (auto& newsItem : Archived) { - if (newsItem.IsEmpty()) - break; p(newsItem); } } @@ -106,11 +220,11 @@ private: int32_t RemoveTime() const; void AppendToArchive(NewsItem& item); - NewsItem Recent[NEWS_ITEM_HISTORY_START]; - NewsItem Archived[MAX_NEWS_ITEMS_ARCHIVE]; + NewsItemQueue Recent; + NewsItemQueue Archived; }; -extern NewsItemQueue gNewsItems; +extern NewsItemQueues gNewsItems; void news_item_init_queue();