Refactor marketing to be a single vector

This commit is contained in:
Ted John 2019-02-16 20:39:41 +00:00
parent 261dd32697
commit 4013479094
11 changed files with 225 additions and 117 deletions

View file

@ -1127,8 +1127,6 @@ static void window_finances_marketing_update(rct_window* w)
*/
static void window_finances_marketing_invalidate(rct_window* w)
{
int32_t i;
if (w->widgets != _windowFinancesPageWidgets[WINDOW_FINANCES_PAGE_MARKETING])
{
w->widgets = _windowFinancesPageWidgets[WINDOW_FINANCES_PAGE_MARKETING];
@ -1138,11 +1136,7 @@ static void window_finances_marketing_invalidate(rct_window* w)
window_finances_set_pressed_tab(w);
// Count number of active campaigns
int32_t numActiveCampaigns = 0;
for (i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
if (gMarketingCampaignDaysLeft[i] != 0)
numActiveCampaigns++;
int32_t numActiveCampaigns = (int32_t)gMarketingCampaigns.size();
int32_t y = std::max(1, numActiveCampaigns) * LIST_ROW_HEIGHT + 92;
// Update group box positions
@ -1151,22 +1145,21 @@ static void window_finances_marketing_invalidate(rct_window* w)
// Update new campaign button visibility
y += 3;
for (i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
{
rct_widget* campaignButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i];
campaignButton->type = WWT_EMPTY;
if (gMarketingCampaignDaysLeft[i] != 0)
continue;
if (!marketing_is_campaign_type_applicable(i))
continue;
campaignButton->type = WWT_BUTTON;
campaignButton->top = y;
campaignButton->bottom = y + BUTTON_FACE_HEIGHT + 1;
y += BUTTON_FACE_HEIGHT + 2;
auto campaignButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i];
auto campaign = marketing_get_campaign(i);
if (campaign == nullptr && marketing_is_campaign_type_applicable(i))
{
campaignButton->type = WWT_BUTTON;
campaignButton->top = y;
campaignButton->bottom = y + BUTTON_FACE_HEIGHT + 1;
y += BUTTON_FACE_HEIGHT + 2;
}
else
{
campaignButton->type = WWT_EMPTY;
}
}
}
@ -1176,19 +1169,16 @@ static void window_finances_marketing_invalidate(rct_window* w)
*/
static void window_finances_marketing_paint(rct_window* w, rct_drawpixelinfo* dpi)
{
int32_t i, x, y, weeksRemaining;
Ride* ride;
window_draw_widgets(w, dpi);
window_finances_draw_tab_images(dpi, w);
x = w->x + 8;
y = w->y + 62;
int32_t x = w->x + 8;
int32_t y = w->y + 62;
int32_t noCampaignsActive = 1;
for (i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
{
if (gMarketingCampaignDaysLeft[i] == 0)
auto campaign = marketing_get_campaign(i);
if (campaign == nullptr)
continue;
noCampaignsActive = 0;
@ -1200,12 +1190,14 @@ static void window_finances_marketing_paint(rct_window* w, rct_drawpixelinfo* dp
{
case ADVERTISING_CAMPAIGN_RIDE_FREE:
case ADVERTISING_CAMPAIGN_RIDE:
ride = get_ride(gMarketingCampaignRideIndex[i]);
{
auto ride = get_ride(campaign->RideId);
set_format_arg(0, rct_string_id, ride->name);
set_format_arg(2, uint32_t, ride->name_arguments);
break;
}
case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE:
set_format_arg(0, rct_string_id, ShopItemStringIds[gMarketingCampaignRideIndex[i]].plural);
set_format_arg(0, rct_string_id, ShopItemStringIds[campaign->ShopItemType].plural);
break;
}
@ -1213,7 +1205,7 @@ static void window_finances_marketing_paint(rct_window* w, rct_drawpixelinfo* dp
gfx_draw_string_left_clipped(dpi, MarketingCampaignNames[i][1], gCommonFormatArgs, COLOUR_BLACK, x + 4, y, 296);
// Duration
weeksRemaining = (gMarketingCampaignDaysLeft[i] % 128);
uint16_t weeksRemaining = campaign->WeeksLeft;
gfx_draw_string_left(
dpi, weeksRemaining == 1 ? STR_1_WEEK_REMAINING : STR_X_WEEKS_REMAINING, &weeksRemaining, COLOUR_BLACK, x + 304, y);
@ -1228,20 +1220,18 @@ static void window_finances_marketing_paint(rct_window* w, rct_drawpixelinfo* dp
y += 34;
// Draw campaign button text
for (i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
for (int32_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
{
rct_widget* campaginButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i];
auto campaignButton = &_windowFinancesMarketingWidgets[WIDX_CAMPAIGN_1 + i];
if (campaignButton->type != WWT_EMPTY)
{
// Draw button text
money32 pricePerWeek = AdvertisingCampaignPricePerWeek[i];
gfx_draw_string_left(dpi, MarketingCampaignNames[i][0], nullptr, COLOUR_BLACK, x + 4, y);
gfx_draw_string_left(dpi, STR_MARKETING_PER_WEEK, &pricePerWeek, COLOUR_BLACK, x + 310, y);
if (campaginButton->type == WWT_EMPTY)
continue;
money32 pricePerWeek = AdvertisingCampaignPricePerWeek[i];
// Draw button text
gfx_draw_string_left(dpi, MarketingCampaignNames[i][0], nullptr, COLOUR_BLACK, x + 4, y);
gfx_draw_string_left(dpi, STR_MARKETING_PER_WEEK, &pricePerWeek, COLOUR_BLACK, x + 310, y);
y += BUTTON_FACE_HEIGHT + 2;
y += BUTTON_FACE_HEIGHT + 2;
}
}
}

View file

@ -68,8 +68,18 @@ public:
GameActionResult::Ptr Execute() const override
{
gMarketingCampaignDaysLeft[_type] = _numWeeks | CAMPAIGN_ACTIVE_FLAG;
gMarketingCampaignRideIndex[_type] = _item;
MarketingCampaign campaign{};
campaign.Type = _type;
campaign.WeeksLeft = _numWeeks;
if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE)
{
campaign.RideId = _item;
}
else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
{
campaign.ShopItemType = _item;
}
marketing_new_campaign(campaign);
// We are only interested in invalidating the finances (marketing) window
auto windowManager = OpenRCT2::GetContext()->GetUiContext()->GetWindowManager();

View file

@ -526,9 +526,9 @@ static int32_t cc_get(InteractiveConsole& console, const arguments_t& argv)
console.WriteFormatLine(
"guest_prefer_more_intense_rides %d", (gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES) != 0);
}
else if (argv[0] == "forbid_marketing_campagns")
else if (argv[0] == "forbid_marketing_campaigns")
{
console.WriteFormatLine("forbid_marketing_campagns %d", (gParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN) != 0);
console.WriteFormatLine("forbid_marketing_campaigns %d", (gParkFlags & PARK_FLAGS_FORBID_MARKETING_CAMPAIGN) != 0);
}
else if (argv[0] == "forbid_landscape_changes")
{
@ -757,10 +757,10 @@ static int32_t cc_set(InteractiveConsole& console, const arguments_t& argv)
SET_FLAG(gParkFlags, PARK_FLAGS_PREF_MORE_INTENSE_RIDES, int_val[0]);
console.Execute("get guest_prefer_more_intense_rides");
}
else if (argv[0] == "forbid_marketing_campagns" && invalidArguments(&invalidArgs, int_valid[0]))
else if (argv[0] == "forbid_marketing_campaigns" && invalidArguments(&invalidArgs, int_valid[0]))
{
SET_FLAG(gParkFlags, PARK_FLAGS_FORBID_MARKETING_CAMPAIGN, int_val[0]);
console.Execute("get forbid_marketing_campagns");
console.Execute("get forbid_marketing_campaigns");
}
else if (argv[0] == "forbid_landscape_changes" && invalidArguments(&invalidArgs, int_valid[0]))
{
@ -1542,7 +1542,7 @@ static constexpr const utf8* console_variable_table[] = {
"guest_initial_thirst",
"guest_prefer_less_intense_rides",
"guest_prefer_more_intense_rides",
"forbid_marketing_campagn",
"forbid_marketing_campaign",
"forbid_landscape_changes",
"forbid_tree_removal",
"forbid_high_construction",

View file

@ -34,16 +34,17 @@ static constexpr const int32_t AdvertisingCampaignGuestGenerationProbabilities[]
400, 300, 200, 200, 250, 200,
};
uint8_t gMarketingCampaignDaysLeft[20];
ride_id_t gMarketingCampaignRideIndex[22];
std::vector<MarketingCampaign> gMarketingCampaigns;
int32_t marketing_get_campaign_guest_generation_probability(int32_t campaign)
int32_t marketing_get_campaign_guest_generation_probability(int32_t campaignType)
{
int32_t probability = AdvertisingCampaignGuestGenerationProbabilities[campaign];
Ride* ride;
auto campaign = marketing_get_campaign(campaignType);
if (campaign == nullptr)
return 0;
// Lower probability of guest generation if price was already low
switch (campaign)
int32_t probability = AdvertisingCampaignGuestGenerationProbabilities[campaign->Type];
switch (campaign->Type)
{
case ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE:
if (park_get_entrance_fee() < MONEY(4, 00))
@ -54,65 +55,81 @@ int32_t marketing_get_campaign_guest_generation_probability(int32_t campaign)
probability /= 8;
break;
case ADVERTISING_CAMPAIGN_RIDE_FREE:
ride = get_ride(gMarketingCampaignRideIndex[campaign]);
{
auto ride = get_ride(campaign->RideId);
if (ride->price < MONEY(0, 30))
probability /= 8;
break;
}
}
return probability;
}
static void marketing_raise_finished_notification(const MarketingCampaign& campaign)
{
if (gConfigNotifications.park_marketing_campaign_finished)
{
// This sets the string parameters for the marketing types that have an argument.
if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE)
{
Ride* ride = get_ride(campaign.RideId);
set_format_arg(0, rct_string_id, ride->name);
set_format_arg(2, uint32_t, ride->name_arguments);
}
else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
{
set_format_arg(0, rct_string_id, ShopItemStringIds[campaign.ShopItemType].plural);
}
news_item_add_to_queue(NEWS_ITEM_MONEY, MarketingCampaignNames[campaign.Type][2], 0);
}
}
/**
* Update status of marketing campaigns and send produce a news item when they have finished.
* rct2: 0x0069E0C1
*/
void marketing_update()
{
for (int32_t campaign = 0; campaign < ADVERTISING_CAMPAIGN_COUNT; campaign++)
if (gCheatsNeverendingMarketing)
return;
for (auto it = gMarketingCampaigns.begin(); it != gMarketingCampaigns.end();)
{
if (gCheatsNeverendingMarketing)
continue;
int32_t active = (gMarketingCampaignDaysLeft[campaign] & CAMPAIGN_ACTIVE_FLAG) != 0;
if (gMarketingCampaignDaysLeft[campaign] == 0)
continue;
window_invalidate_by_class(WC_FINANCES);
// High bit marks the campaign as inactive, on first check the campaign is set active
// this makes campaigns run a full x weeks even when started in the middle of a week
gMarketingCampaignDaysLeft[campaign] &= ~CAMPAIGN_ACTIVE_FLAG;
if (active)
continue;
if (--gMarketingCampaignDaysLeft[campaign] != 0)
continue;
int32_t campaignItem = gMarketingCampaignRideIndex[campaign];
// This sets the string parameters for the marketing types that have an argument.
if (campaign == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign == ADVERTISING_CAMPAIGN_RIDE)
auto& campaign = *it;
if (campaign.Flags & MarketingCampaignFlags::FIRST_WEEK)
{
Ride* ride = get_ride(campaignItem);
set_format_arg(0, rct_string_id, ride->name);
set_format_arg(2, uint32_t, ride->name_arguments);
// This ensures the campaign is active for x full weeks if started within the
// middle of a week.
campaign.Flags &= ~MarketingCampaignFlags::FIRST_WEEK;
}
else if (campaign == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
else if (campaign.WeeksLeft > 0)
{
set_format_arg(0, rct_string_id, ShopItemStringIds[campaignItem].plural);
campaign.WeeksLeft--;
}
if (gConfigNotifications.park_marketing_campaign_finished)
if (campaign.WeeksLeft == 0)
{
news_item_add_to_queue(NEWS_ITEM_MONEY, MarketingCampaignNames[campaign][2], 0);
marketing_raise_finished_notification(campaign);
it = gMarketingCampaigns.erase(it);
}
else
{
++it;
}
}
window_invalidate_by_class(WC_FINANCES);
}
void marketing_set_guest_campaign(rct_peep* peep, int32_t campaign)
void marketing_set_guest_campaign(rct_peep* peep, int32_t campaignType)
{
switch (campaign)
auto campaign = marketing_get_campaign(campaignType);
if (campaign == nullptr)
return;
switch (campaign->Type)
{
case ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE:
peep->item_standard_flags |= PEEP_ITEM_VOUCHER;
@ -121,8 +138,8 @@ void marketing_set_guest_campaign(rct_peep* peep, int32_t campaign)
case ADVERTISING_CAMPAIGN_RIDE_FREE:
peep->item_standard_flags |= PEEP_ITEM_VOUCHER;
peep->voucher_type = VOUCHER_TYPE_RIDE_FREE;
peep->voucher_arguments = gMarketingCampaignRideIndex[campaign];
peep->guest_heading_to_ride_id = gMarketingCampaignRideIndex[campaign];
peep->voucher_arguments = campaign->RideId;
peep->guest_heading_to_ride_id = campaign->RideId;
peep->peep_is_lost_countdown = 240;
break;
case ADVERTISING_CAMPAIGN_PARK_ENTRY_HALF_PRICE:
@ -132,12 +149,12 @@ void marketing_set_guest_campaign(rct_peep* peep, int32_t campaign)
case ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE:
peep->item_standard_flags |= PEEP_ITEM_VOUCHER;
peep->voucher_type = VOUCHER_TYPE_FOOD_OR_DRINK_FREE;
peep->voucher_arguments = gMarketingCampaignRideIndex[campaign];
peep->voucher_arguments = campaign->ShopItemType;
break;
case ADVERTISING_CAMPAIGN_PARK:
break;
case ADVERTISING_CAMPAIGN_RIDE:
peep->guest_heading_to_ride_id = gMarketingCampaignRideIndex[campaign];
peep->guest_heading_to_ride_id = campaign->RideId;
peep->peep_is_lost_countdown = 240;
break;
}
@ -194,3 +211,29 @@ bool marketing_is_campaign_type_applicable(int32_t campaignType)
return true;
}
}
MarketingCampaign* marketing_get_campaign(int32_t campaignType)
{
for (auto& campaign : gMarketingCampaigns)
{
if (campaign.Type == campaignType)
{
return &campaign;
}
}
return nullptr;
}
void marketing_new_campaign(const MarketingCampaign& campaign)
{
// Do not allow same campaign twice, just overwrite
auto currentCampaign = marketing_get_campaign(campaign.Type);
if (currentCampaign != nullptr)
{
*currentCampaign = campaign;
}
else
{
gMarketingCampaigns.push_back(campaign);
}
}

View file

@ -13,6 +13,8 @@
#include "../common.h"
#include "../peep/Peep.h"
#include <vector>
enum
{
ADVERTISING_CAMPAIGN_PARK_ENTRY_FREE,
@ -37,11 +39,29 @@ enum
CAMPAIGN_ACTIVE_FLAG = (1 << 7)
};
struct MarketingCampaign
{
uint8_t Type{};
uint8_t WeeksLeft{};
uint8_t Flags{};
union
{
ride_id_t RideId{};
uint8_t ShopItemType;
};
};
namespace MarketingCampaignFlags
{
constexpr uint8_t FIRST_WEEK = 1 << 0;
}
extern const money16 AdvertisingCampaignPricePerWeek[ADVERTISING_CAMPAIGN_COUNT];
extern uint8_t gMarketingCampaignDaysLeft[20];
extern ride_id_t gMarketingCampaignRideIndex[22];
extern std::vector<MarketingCampaign> gMarketingCampaigns;
int32_t marketing_get_campaign_guest_generation_probability(int32_t campaign);
void marketing_update();
void marketing_set_guest_campaign(rct_peep* peep, int32_t campaign);
bool marketing_is_campaign_type_applicable(int32_t campaignType);
MarketingCampaign* marketing_get_campaign(int32_t campaignType);
void marketing_new_campaign(const MarketingCampaign& campaign);

View file

@ -1849,10 +1849,23 @@ private:
gTotalIncomeFromAdmissions = _s4.admission_total_income;
// TODO marketing campaigns not working
for (size_t i = 0; i < 6; i++)
for (size_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
{
gMarketingCampaignDaysLeft[i] = _s4.marketing_status[i];
gMarketingCampaignRideIndex[i] = _s4.marketing_assoc[i];
if (_s4.marketing_status[i] & CAMPAIGN_ACTIVE_FLAG)
{
MarketingCampaign campaign;
campaign.Type = (uint8_t)i;
campaign.WeeksLeft = _s4.marketing_status[i] & ~CAMPAIGN_ACTIVE_FLAG;
if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE)
{
campaign.RideId = _s4.marketing_assoc[i];
}
else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
{
campaign.ShopItemType = _s4.marketing_assoc[i];
}
gMarketingCampaigns.push_back(campaign);
}
}
}

View file

@ -277,8 +277,7 @@ void S6Exporter::Export()
// pad_013580FA
_s6.objective_currency = gScenarioObjectiveCurrency;
_s6.objective_guests = gScenarioObjectiveNumGuests;
std::memcpy(_s6.campaign_weeks_left, gMarketingCampaignDaysLeft, sizeof(_s6.campaign_weeks_left));
std::memcpy(_s6.campaign_ride_index, gMarketingCampaignRideIndex, sizeof(_s6.campaign_ride_index));
ExportMarketingCampaigns();
std::memcpy(_s6.balance_history, gCashHistory, sizeof(_s6.balance_history));
@ -746,6 +745,24 @@ void S6Exporter::ExportResearchList()
std::memcpy(_s6.research_items, gResearchItems, sizeof(_s6.research_items));
}
void S6Exporter::ExportMarketingCampaigns()
{
std::memset(_s6.campaign_weeks_left, 0, sizeof(_s6.campaign_weeks_left));
std::memset(_s6.campaign_ride_index, 0, sizeof(_s6.campaign_ride_index));
for (const auto& campaign : gMarketingCampaigns)
{
_s6.campaign_weeks_left[campaign.Type] = campaign.WeeksLeft;
if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE)
{
_s6.campaign_ride_index[campaign.Type] = campaign.RideId;
}
else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
{
_s6.campaign_ride_index[campaign.Type] = campaign.ShopItemType;
}
}
}
enum : uint32_t
{
S6_SAVE_FLAG_EXPORT = 1 << 0,

View file

@ -46,5 +46,6 @@ private:
void ExportResearchedRideEntries();
void ExportResearchedSceneryItems();
void ExportResearchList();
void ExportMarketingCampaigns();
void ExportPeepSpawns();
};

View file

@ -295,8 +295,7 @@ public:
// pad_013580FA
gScenarioObjectiveCurrency = _s6.objective_currency;
gScenarioObjectiveNumGuests = _s6.objective_guests;
std::memcpy(gMarketingCampaignDaysLeft, _s6.campaign_weeks_left, sizeof(_s6.campaign_weeks_left));
std::memcpy(gMarketingCampaignRideIndex, _s6.campaign_ride_index, sizeof(_s6.campaign_ride_index));
ImportMarketingCampaigns();
gCurrentExpenditure = _s6.current_expenditure;
gCurrentProfit = _s6.current_profit;
@ -1028,6 +1027,28 @@ public:
assert(false);
}
}
void ImportMarketingCampaigns()
{
for (size_t i = 0; i < ADVERTISING_CAMPAIGN_COUNT; i++)
{
if (_s6.campaign_weeks_left[i] & CAMPAIGN_ACTIVE_FLAG)
{
MarketingCampaign campaign{};
campaign.Type = (uint8_t)i;
campaign.WeeksLeft = _s6.campaign_weeks_left[i] & ~CAMPAIGN_ACTIVE_FLAG;
if (campaign.Type == ADVERTISING_CAMPAIGN_RIDE_FREE || campaign.Type == ADVERTISING_CAMPAIGN_RIDE)
{
campaign.RideId = _s6.campaign_ride_index[i];
}
else if (campaign.Type == ADVERTISING_CAMPAIGN_FOOD_OR_DRINK_FREE)
{
campaign.ShopItemType = _s6.campaign_ride_index[i];
}
gMarketingCampaigns.push_back(campaign);
}
}
}
};
std::unique_ptr<IParkImporter> ParkImporter::CreateS6(IObjectRepository& objectRepository)

View file

@ -162,7 +162,7 @@ void scenario_begin()
map_count_remaining_land_rights();
staff_reset_stats();
gLastEntranceStyle = 0;
std::fill_n(gMarketingCampaignDaysLeft, sizeof(gMarketingCampaignDaysLeft), 0x00);
gMarketingCampaigns.clear();
gParkRatingCasualtyPenalty = 0;
// Open park with free entry when there is no money

View file

@ -541,11 +541,7 @@ void Park::Initialise()
_guestGenerationProbability = 0;
gTotalRideValueForMoney = 0;
gResearchLastItem.rawValue = RESEARCHED_ITEMS_SEPARATOR;
for (size_t i = 0; i < 20; i++)
{
gMarketingCampaignDaysLeft[i] = 0;
}
gMarketingCampaigns.clear();
research_reset_items();
finance_init();
@ -967,15 +963,12 @@ void Park::GenerateGuests()
}
// Extra guests generated by advertising campaigns
for (int32_t campaign = 0; campaign < ADVERTISING_CAMPAIGN_COUNT; campaign++)
for (const auto& campaign : gMarketingCampaigns)
{
if (gMarketingCampaignDaysLeft[campaign] != 0)
// Random chance of guest generation
if ((int32_t)scenario_rand_max(0xFFFF) < marketing_get_campaign_guest_generation_probability(campaign.Type))
{
// Random chance of guest generation
if ((int32_t)(scenario_rand() & 0xFFFF) < marketing_get_campaign_guest_generation_probability(campaign))
{
GenerateGuestFromCampaign(campaign);
}
GenerateGuestFromCampaign(campaign.Type);
}
}
}