mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-15 19:13:07 +01:00
Merge pull request #11884 from hdpoliveira/newsitem_queue_array
Create NewsItemQueue class
This commit is contained in:
@@ -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<NewsItem&>(const_cast<const NewsItemQueue&>(*this)[index]);
|
||||
return const_cast<NewsItem&>(const_cast<const NewsItemQueues&>(*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<NewsItem*>(const_cast<const NewsItemQueue&>(*this).At(index));
|
||||
return const_cast<NewsItem*>(const_cast<const NewsItemQueues&>(*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<CoordsXYZ> 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)
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "../common.h"
|
||||
#include "../world/Location.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
|
||||
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<std::size_t N> class NewsItemQueue
|
||||
{
|
||||
public:
|
||||
static_assert(N > 0, "Cannot instantiate NewsItemQueue with size=0");
|
||||
|
||||
using value_type = typename std::array<NewsItem, N>::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<NewsItem, N>::iterator;
|
||||
using const_iterator = typename std::array<NewsItem, N>::const_iterator;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_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<NewsItem, N> 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<NEWS_ITEM_HISTORY_START> Recent;
|
||||
NewsItemQueue<MAX_NEWS_ITEMS_ARCHIVE> Archived;
|
||||
};
|
||||
|
||||
extern NewsItemQueue gNewsItems;
|
||||
extern NewsItemQueues gNewsItems;
|
||||
|
||||
void news_item_init_queue();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user