mirror of
https://github.com/OpenRCT2/OpenRCT2.git
synced 2025-01-22 18:31:59 -05:00
Add plugin API for editing park messages (#11755)
This commit is contained in:
parent
310ff84b23
commit
03b1974912
5 changed files with 325 additions and 28 deletions
41
distribution/openrct2.d.ts
vendored
41
distribution/openrct2.d.ts
vendored
|
@ -896,7 +896,34 @@ declare global {
|
|||
"attraction" | "peep_on_attraction" | "peep" | "money" | "blank" | "research" | "guests" | "award" | "chart";
|
||||
|
||||
interface ParkMessage {
|
||||
/**
|
||||
* Whether the message has been shown and archived.
|
||||
*/
|
||||
readonly isArchived: boolean;
|
||||
|
||||
/**
|
||||
* The date this message was posted in total elapsed months.
|
||||
*/
|
||||
month: number;
|
||||
|
||||
/**
|
||||
* The day of the month this message was posted.
|
||||
*/
|
||||
day: number;
|
||||
|
||||
/**
|
||||
* How old the message is in number of ticks.
|
||||
*/
|
||||
tickCount: number;
|
||||
|
||||
/**
|
||||
* The format of the message such as the icon and whether location is enabled.
|
||||
*/
|
||||
type: ParkMessageType;
|
||||
|
||||
/**
|
||||
* The actual message content.
|
||||
*/
|
||||
text: string;
|
||||
|
||||
/**
|
||||
|
@ -905,6 +932,17 @@ declare global {
|
|||
* Researched item for research.
|
||||
*/
|
||||
subject?: number;
|
||||
|
||||
/**
|
||||
* Removes the message.
|
||||
*/
|
||||
remove(): void;
|
||||
}
|
||||
|
||||
interface ParkMessageDesc {
|
||||
type: ParkMessageType;
|
||||
text: string;
|
||||
subject?: number;
|
||||
}
|
||||
|
||||
interface Park {
|
||||
|
@ -912,9 +950,10 @@ declare global {
|
|||
rating: number;
|
||||
bankLoan: number;
|
||||
maxBankLoan: number;
|
||||
messages: ParkMessage[];
|
||||
|
||||
postMessage(message: string): void;
|
||||
postMessage(message: ParkMessage): void;
|
||||
postMessage(message: ParkMessageDesc): void;
|
||||
}
|
||||
|
||||
interface Cheats {
|
||||
|
|
|
@ -81,7 +81,7 @@ bool news_item_is_queue_empty()
|
|||
void news_item_init_queue()
|
||||
{
|
||||
news_item_get(0)->Type = NEWS_ITEM_NULL;
|
||||
news_item_get(11)->Type = NEWS_ITEM_NULL;
|
||||
news_item_get(NEWS_ITEM_HISTORY_START)->Type = NEWS_ITEM_NULL;
|
||||
|
||||
// Throttles for warning types (PEEP_*_WARNING)
|
||||
for (auto& warningThrottle : gPeepWarningThrottle)
|
||||
|
@ -164,11 +164,11 @@ void news_item_close_current()
|
|||
window_invalidate_by_class(WC_RECENT_NEWS);
|
||||
|
||||
// Dequeue the current news item, shift news up
|
||||
for (i = 0; i < 10; i++)
|
||||
for (i = 0; i < NEWS_ITEM_HISTORY_START - 1; i++)
|
||||
{
|
||||
newsItems[i] = newsItems[i + 1];
|
||||
}
|
||||
newsItems[10].Type = NEWS_ITEM_NULL;
|
||||
newsItems[NEWS_ITEM_HISTORY_START - 1].Type = NEWS_ITEM_NULL;
|
||||
|
||||
// Invalidate current news item bar
|
||||
auto intent = Intent(INTENT_ACTION_INVALIDATE_TICKER_NEWS);
|
||||
|
@ -177,7 +177,7 @@ void news_item_close_current()
|
|||
|
||||
static void news_item_shift_history_up()
|
||||
{
|
||||
const int32_t history_idx = 11;
|
||||
const int32_t history_idx = NEWS_ITEM_HISTORY_START;
|
||||
NewsItem* history_start = news_item_get(history_idx);
|
||||
const size_t count = sizeof(NewsItem) * (MAX_NEWS_ITEMS - 1 - history_idx);
|
||||
memmove(history_start, history_start + 1, count);
|
||||
|
@ -190,7 +190,7 @@ static void news_item_shift_history_up()
|
|||
static int32_t news_item_get_new_history_slot()
|
||||
{
|
||||
// Find an available history news item slot
|
||||
for (int32_t i = 11; i < MAX_NEWS_ITEMS; i++)
|
||||
for (int32_t i = NEWS_ITEM_HISTORY_START; i < MAX_NEWS_ITEMS; i++)
|
||||
{
|
||||
if (news_item_is_empty(i))
|
||||
return i;
|
||||
|
@ -301,7 +301,7 @@ static NewsItem* news_item_first_open_queue_slot()
|
|||
|
||||
while (newsItem->Type != NEWS_ITEM_NULL)
|
||||
{
|
||||
if (newsItem + 1 >= &gNewsItems[10])
|
||||
if (newsItem + 1 >= &gNewsItems[NEWS_ITEM_HISTORY_START - 1])
|
||||
news_item_close_current();
|
||||
else
|
||||
newsItem++;
|
||||
|
@ -438,7 +438,7 @@ void news_item_open_subject(int32_t type, int32_t subject)
|
|||
void news_item_disable_news(uint8_t type, uint32_t assoc)
|
||||
{
|
||||
// TODO: write test invalidating windows
|
||||
for (int32_t i = 0; i < 11; i++)
|
||||
for (int32_t i = 0; i < NEWS_ITEM_HISTORY_START; i++)
|
||||
{
|
||||
if (!news_item_is_empty(i))
|
||||
{
|
||||
|
@ -459,7 +459,7 @@ void news_item_disable_news(uint8_t type, uint32_t assoc)
|
|||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 11; i < MAX_NEWS_ITEMS; i++)
|
||||
for (int32_t i = NEWS_ITEM_HISTORY_START; i < MAX_NEWS_ITEMS; i++)
|
||||
{
|
||||
if (!news_item_is_empty(i))
|
||||
{
|
||||
|
@ -484,3 +484,20 @@ void news_item_add_to_queue_custom(NewsItem* newNewsItem)
|
|||
newsItem++;
|
||||
newsItem->Type = NEWS_ITEM_NULL;
|
||||
}
|
||||
|
||||
void news_item_remove(int32_t index)
|
||||
{
|
||||
if (index < 0 || index >= MAX_NEWS_ITEMS)
|
||||
return;
|
||||
|
||||
// News item is already null, no need to remove it
|
||||
if (gNewsItems[index].Type == NEWS_ITEM_NULL)
|
||||
return;
|
||||
|
||||
size_t newsBoundary = index < NEWS_ITEM_HISTORY_START ? NEWS_ITEM_HISTORY_START : MAX_NEWS_ITEMS;
|
||||
for (size_t i = index; i < newsBoundary - 1; i++)
|
||||
{
|
||||
gNewsItems[i] = gNewsItems[i + 1];
|
||||
}
|
||||
gNewsItems[newsBoundary - 1].Type = NEWS_ITEM_NULL;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 OpenRCT2 developers
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
|
@ -54,7 +54,8 @@ struct NewsItem
|
|||
utf8 Text[256];
|
||||
};
|
||||
|
||||
#define MAX_NEWS_ITEMS 61
|
||||
constexpr int32_t NEWS_ITEM_HISTORY_START = 11;
|
||||
constexpr int32_t MAX_NEWS_ITEMS = 61;
|
||||
|
||||
extern const uint8_t news_type_properties[10];
|
||||
|
||||
|
@ -82,3 +83,4 @@ bool news_item_is_queue_empty();
|
|||
bool news_item_is_valid_idx(int32_t index);
|
||||
|
||||
void news_item_add_to_queue_custom(NewsItem* newNewsItem);
|
||||
void news_item_remove(int32_t index);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
# include "../Context.h"
|
||||
# include "../common.h"
|
||||
# include "../core/String.hpp"
|
||||
# include "../management/Finance.h"
|
||||
# include "../management/NewsItem.h"
|
||||
# include "../windows/Intent.h"
|
||||
|
@ -24,6 +25,204 @@
|
|||
|
||||
namespace OpenRCT2::Scripting
|
||||
{
|
||||
static constexpr const char* ParkMessageTypeStrings[] = {
|
||||
"attraction", "peep_on_attraction", "peep", "money", "blank", "research", "guests", "award", "chart",
|
||||
};
|
||||
|
||||
inline uint8_t GetParkMessageType(const std::string& key)
|
||||
{
|
||||
auto it = std::find(std::begin(ParkMessageTypeStrings), std::end(ParkMessageTypeStrings), key);
|
||||
return it != std::end(ParkMessageTypeStrings)
|
||||
? static_cast<uint8_t>(NEWS_ITEM_RIDE + std::distance(std::begin(ParkMessageTypeStrings), it))
|
||||
: static_cast<uint8_t>(NEWS_ITEM_BLANK);
|
||||
}
|
||||
|
||||
inline std::string GetParkMessageType(uint8_t type)
|
||||
{
|
||||
// Decrement 1 as ParkMessageTypeStrings doesn't contain the null type
|
||||
type--;
|
||||
if (type < std::size(ParkMessageTypeStrings))
|
||||
{
|
||||
return ParkMessageTypeStrings[type];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<> inline NewsItem FromDuk(const DukValue& value)
|
||||
{
|
||||
NewsItem result{};
|
||||
result.Type = GetParkMessageType(value["type"].as_string());
|
||||
result.Assoc = value["subject"].as_int();
|
||||
result.Ticks = value["tickCount"].as_int();
|
||||
result.MonthYear = value["month"].as_int();
|
||||
result.Day = value["day"].as_int();
|
||||
|
||||
auto text = language_convert_string(value["text"].as_string());
|
||||
String::Set(result.Text, sizeof(result.Text), text.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
class ScParkMessage
|
||||
{
|
||||
private:
|
||||
size_t _index{};
|
||||
|
||||
public:
|
||||
ScParkMessage(size_t index)
|
||||
: _index(index)
|
||||
{
|
||||
}
|
||||
|
||||
static void Register(duk_context* ctx)
|
||||
{
|
||||
dukglue_register_property(ctx, &ScParkMessage::isArchived_get, nullptr, "isArchived");
|
||||
dukglue_register_property(ctx, &ScParkMessage::month_get, &ScParkMessage::month_set, "month");
|
||||
dukglue_register_property(ctx, &ScParkMessage::day_get, &ScParkMessage::day_set, "day");
|
||||
dukglue_register_property(ctx, &ScParkMessage::tickCount_get, &ScParkMessage::tickCount_set, "tickCount");
|
||||
dukglue_register_property(ctx, &ScParkMessage::type_get, &ScParkMessage::type_set, "type");
|
||||
dukglue_register_property(ctx, &ScParkMessage::subject_get, &ScParkMessage::subject_set, "subject");
|
||||
dukglue_register_property(ctx, &ScParkMessage::text_get, &ScParkMessage::text_set, "text");
|
||||
dukglue_register_method(ctx, &ScParkMessage::remove, "remove");
|
||||
}
|
||||
|
||||
private:
|
||||
NewsItem* GetMessage() const
|
||||
{
|
||||
return &gNewsItems[_index];
|
||||
}
|
||||
|
||||
bool isArchived_get() const
|
||||
{
|
||||
return _index >= NEWS_ITEM_HISTORY_START;
|
||||
}
|
||||
|
||||
uint16_t month_get() const
|
||||
{
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
return msg->MonthYear;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void month_set(uint16_t value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
msg->MonthYear = value;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t day_get() const
|
||||
{
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
return msg->Day;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void day_set(uint8_t value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
msg->Day = value;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t tickCount_get() const
|
||||
{
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
return msg->Ticks;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tickCount_set(uint16_t value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
msg->Ticks = value;
|
||||
}
|
||||
}
|
||||
|
||||
std::string type_get() const
|
||||
{
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
return GetParkMessageType(msg->Type);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void type_set(const std::string& value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
msg->Type = GetParkMessageType(value);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t subject_get() const
|
||||
{
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
return msg->Assoc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void subject_set(uint32_t value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
msg->Assoc = value;
|
||||
}
|
||||
}
|
||||
|
||||
std::string text_get() const
|
||||
{
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
return language_convert_string_to_tokens(msg->Text);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void text_set(const std::string& value)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
auto msg = GetMessage();
|
||||
if (msg != nullptr)
|
||||
{
|
||||
auto text = language_convert_string(value);
|
||||
String::Set(msg->Text, sizeof(msg->Text), text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void remove()
|
||||
{
|
||||
news_item_remove(static_cast<int32_t>(_index));
|
||||
}
|
||||
};
|
||||
|
||||
class ScPark
|
||||
{
|
||||
public:
|
||||
|
@ -75,6 +274,61 @@ namespace OpenRCT2::Scripting
|
|||
context_broadcast_intent(&intent);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ScParkMessage>> messages_get() const
|
||||
{
|
||||
std::vector<std::shared_ptr<ScParkMessage>> result;
|
||||
for (int32_t i = 0; i < NEWS_ITEM_HISTORY_START; i++)
|
||||
{
|
||||
if (news_item_is_empty(i))
|
||||
break;
|
||||
result.push_back(std::make_shared<ScParkMessage>(i));
|
||||
}
|
||||
for (int32_t i = NEWS_ITEM_HISTORY_START; i < MAX_NEWS_ITEMS; i++)
|
||||
{
|
||||
if (news_item_is_empty(i))
|
||||
break;
|
||||
result.push_back(std::make_shared<ScParkMessage>(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void messages_set(const std::vector<DukValue>& value)
|
||||
{
|
||||
int32_t index = 0;
|
||||
int32_t archiveIndex = NEWS_ITEM_HISTORY_START;
|
||||
for (const auto& item : value)
|
||||
{
|
||||
auto isArchived = item["isArchived"].as_bool();
|
||||
auto newsItem = FromDuk<NewsItem>(item);
|
||||
if (isArchived)
|
||||
{
|
||||
if (archiveIndex < MAX_NEWS_ITEMS)
|
||||
{
|
||||
gNewsItems[archiveIndex] = newsItem;
|
||||
archiveIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index < NEWS_ITEM_HISTORY_START)
|
||||
{
|
||||
gNewsItems[index] = newsItem;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End the lists by setting next item to null
|
||||
if (index < NEWS_ITEM_HISTORY_START)
|
||||
{
|
||||
gNewsItems[index].Type = NEWS_ITEM_NULL;
|
||||
}
|
||||
if (archiveIndex < MAX_NEWS_ITEMS)
|
||||
{
|
||||
gNewsItems[archiveIndex].Type = NEWS_ITEM_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void postMessage(DukValue message)
|
||||
{
|
||||
ThrowIfGameStateNotMutable();
|
||||
|
@ -116,25 +370,9 @@ namespace OpenRCT2::Scripting
|
|||
dukglue_register_property(ctx, &ScPark::rating_get, &ScPark::rating_set, "rating");
|
||||
dukglue_register_property(ctx, &ScPark::bankLoan_get, &ScPark::bankLoan_set, "bankLoan");
|
||||
dukglue_register_property(ctx, &ScPark::maxBankLoan_get, &ScPark::maxBankLoan_set, "maxBankLoan");
|
||||
dukglue_register_property(ctx, &ScPark::messages_get, &ScPark::messages_set, "messages");
|
||||
dukglue_register_method(ctx, &ScPark::postMessage, "postMessage");
|
||||
}
|
||||
|
||||
private:
|
||||
static uint8_t GetParkMessageType(const std::string& key)
|
||||
{
|
||||
static auto keys = { "attraction", "peep_on_attraction", "peep", "money", "blank", "research", "guests", "award",
|
||||
"chart" };
|
||||
uint8_t i = 0;
|
||||
for (const auto& k : keys)
|
||||
{
|
||||
if (k == key)
|
||||
{
|
||||
return NEWS_ITEM_RIDE + i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return NEWS_ITEM_BLANK;
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Scripting
|
||||
|
||||
|
|
|
@ -380,6 +380,7 @@ void ScriptEngine::Initialise()
|
|||
ScObject::Register(ctx);
|
||||
ScSmallSceneryObject::Register(ctx);
|
||||
ScPark::Register(ctx);
|
||||
ScParkMessage::Register(ctx);
|
||||
ScPlayer::Register(ctx);
|
||||
ScPlayerGroup::Register(ctx);
|
||||
ScRide::Register(ctx);
|
||||
|
|
Loading…
Reference in a new issue