OpenRCT2/src/scenario.c

1016 lines
27 KiB
C

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include "audio/audio.h"
#include "cheats.h"
#include "config.h"
#include "game.h"
#include "world/climate.h"
#include "interface/viewport.h"
#include "localisation/date.h"
#include "localisation/localisation.h"
#include "management/award.h"
#include "management/finance.h"
#include "management/marketing.h"
#include "management/research.h"
#include "management/news_item.h"
#include "network/network.h"
#include "object.h"
#include "object_list.h"
#include "openrct2.h"
#include "peep/staff.h"
#include "platform/platform.h"
#include "ride/ride.h"
#include "scenario.h"
#include "ScenarioRepository.h"
#include "ScenarioSources.h"
#include "title.h"
#include "util/sawyercoding.h"
#include "util/util.h"
#include "world/map.h"
#include "world/park.h"
#include "world/scenery.h"
#include "world/sprite.h"
#include "world/water.h"
const rct_string_id ScenarioCategoryStringIds[SCENARIO_CATEGORY_COUNT] = {
STR_BEGINNER_PARKS,
STR_CHALLENGING_PARKS,
STR_EXPERT_PARKS,
STR_REAL_PARKS,
STR_OTHER_PARKS,
STR_DLC_PARKS,
STR_BUILD_YOUR_OWN_PARKS,
};
static char _scenarioPath[MAX_PATH];
const char *_scenarioFileName = "";
rct_s6_info gS6Info;
char gScenarioName[64];
char gScenarioDetails[256];
char gScenarioCompletedBy[32];
char gScenarioSavePath[MAX_PATH];
char gScenarioExpansionPacks[3256];
int gFirstTimeSave = 1;
uint16 gSavedAge;
uint32 gLastAutoSaveTick = 0;
#if defined(NO_RCT2)
uint32 gScenarioTicks;
#endif
uint32 gScenarioSrand0;
uint32 gScenarioSrand1;
uint8 gScenarioObjectiveType;
uint8 gScenarioObjectiveYear;
uint16 gScenarioObjectiveNumGuests;
money32 gScenarioObjectiveCurrency;
uint16 gScenarioParkRatingWarningDays;
money32 gScenarioCompletedCompanyValue;
money32 gScenarioCompanyValueRecord;
static int scenario_create_ducks();
static void scenario_objective_check();
/**
* Loads only the basic information from a scenario.
* rct2: 0x006761D6
*/
bool scenario_load_basic(const char *path, rct_s6_header *header, rct_s6_info *info)
{
log_verbose("loading scenario details, %s", path);
SDL_RWops* rw = SDL_RWFromFile(path, "rb");
if (rw != NULL) {
// Read first chunk
size_t loaded_size = sawyercoding_read_chunk_with_size(rw, (uint8*)header, sizeof(rct_s6_header));
if (loaded_size != sizeof(rct_s6_header)) {
log_error("Failed to read header from scenario %s", path);
SDL_RWclose(rw);
return false;
}
if (header->type == S6_TYPE_SCENARIO) {
// Read second chunk
loaded_size = sawyercoding_read_chunk_with_size(rw, (uint8*)info, sizeof(rct_s6_info));
SDL_RWclose(rw);
if (loaded_size != sizeof(rct_s6_info)) {
log_error("Failed to read info from scenario %s", path);
return false;
}
return true;
} else {
log_error("invalid scenario, %s", path);
SDL_RWclose(rw);
return false;
}
}
log_error("unable to open scenario, %s", path);
return false;
}
int scenario_load_and_play_from_path(const char *path)
{
window_close_construction_windows();
if (!scenario_load(path))
return 0;
reset_sprite_spatial_index();
reset_all_sprite_quadrant_placements();
size_t len = strnlen(path, MAX_PATH) + 1;
safe_strcpy(_scenarioPath, path, len);
if (len - 1 == MAX_PATH)
{
_scenarioPath[MAX_PATH - 1] = '\0';
log_warning("truncated string %s", _scenarioPath);
}
_scenarioFileName = path_get_filename(_scenarioPath);
gFirstTimeSave = 1;
log_verbose("starting scenario, %s", path);
scenario_begin();
if (network_get_mode() == NETWORK_MODE_SERVER) {
network_send_map();
}
if (network_get_mode() == NETWORK_MODE_CLIENT) {
network_close();
}
return 1;
}
void scenario_begin()
{
rct_window *mainWindow;
audio_stop_title_music();
gScreenFlags = SCREEN_FLAGS_PLAYING;
audio_stop_all_music_and_sounds();
viewport_init_all();
game_create_windows();
mainWindow = window_get_main();
mainWindow->viewport_target_sprite = -1;
mainWindow->saved_view_x = gSavedViewX;
mainWindow->saved_view_y = gSavedViewY;
uint8 zoomDifference = gSavedViewZoom - mainWindow->viewport->zoom;
mainWindow->viewport->zoom = gSavedViewZoom;
gCurrentRotation = gSavedViewRotation;
if (zoomDifference != 0) {
if (zoomDifference < 0) {
zoomDifference = -zoomDifference;
mainWindow->viewport->view_width >>= zoomDifference;
mainWindow->viewport->view_height >>= zoomDifference;
} else {
mainWindow->viewport->view_width <<= zoomDifference;
mainWindow->viewport->view_height <<= zoomDifference;
}
}
mainWindow->saved_view_x -= mainWindow->viewport->view_width >> 1;
mainWindow->saved_view_y -= mainWindow->viewport->view_height >> 1;
window_invalidate(mainWindow);
reset_all_sprite_quadrant_placements();
window_new_ride_init_vars();
// Set the scenario pseudo-random seeds
gScenarioSrand0 ^= platform_get_ticks();
gScenarioSrand1 ^= platform_get_ticks();
gWindowUpdateTicks = 0;
gParkFlags &= ~PARK_FLAGS_NO_MONEY;
if (gParkFlags & PARK_FLAGS_NO_MONEY_SCENARIO)
gParkFlags |= PARK_FLAGS_NO_MONEY;
sub_684AC3();
scenery_set_default_placement_configuration();
news_item_init_queue();
if (gScenarioObjectiveType != OBJECTIVE_NONE)
window_park_objective_open();
gParkRating = calculate_park_rating();
gParkValue = calculate_park_value();
gCompanyValue = calculate_company_value();
gHistoricalProfit = gInitialCash - gBankLoan;
gCashEncrypted = ENCRYPT_MONEY(gInitialCash);
safe_strcpy(gScenarioDetails, gS6Info.details, 256);
safe_strcpy(gScenarioName, gS6Info.name, 64);
{
utf8 normalisedName[64];
scenario_normalise_name(normalisedName, sizeof(normalisedName), gS6Info.name);
rct_string_id localisedStringIds[3];
if (language_get_localised_scenario_strings(normalisedName, localisedStringIds)) {
if (localisedStringIds[0] != STR_NONE) {
safe_strcpy(gScenarioName, language_get_string(localisedStringIds[0]), 32);
}
if (localisedStringIds[1] != STR_NONE) {
park_set_name(language_get_string(localisedStringIds[1]));
}
if (localisedStringIds[2] != STR_NONE) {
safe_strcpy(gScenarioDetails, language_get_string(localisedStringIds[2]), 256);
}
} else {
rct_stex_entry* stex = g_stexEntries[0];
if ((intptr_t)stex != -1) {
char *buffer = gCommonStringFormatBuffer;
// Set localised park name
format_string(buffer, 256, stex->park_name, 0);
park_set_name(buffer);
// Set localised scenario name
format_string(buffer, 256, stex->scenario_name, 0);
safe_strcpy(gScenarioName, buffer, 64);
// Set localised scenario details
format_string(buffer, 256, stex->details, 0);
safe_strcpy(gScenarioDetails, buffer, 256);
}
}
}
// Set the last saved game path
char parkName[128];
format_string(parkName, 128, gParkName, &gParkNameArgs);
platform_get_user_directory(gScenarioSavePath, "save", sizeof(gScenarioSavePath));
safe_strcat_path(gScenarioSavePath, parkName, sizeof(gScenarioSavePath));
path_append_extension(gScenarioSavePath, ".sv6", sizeof(gScenarioSavePath));
safe_strcpy(gRCT2AddressSavedGamesPath2, gRCT2AddressSavedGamesPath, MAX_PATH);
safe_strcat_path(gRCT2AddressSavedGamesPath2, gScenarioSavePath, MAX_PATH);
path_append_extension(gRCT2AddressSavedGamesPath2, ".SV6", MAX_PATH);
gCurrentExpenditure = 0;
gCurrentProfit = 0;
gWeeklyProfitAverageDividend = 0;
gWeeklyProfitAverageDivisor = 0;
gScenarioCompletedCompanyValue = MONEY32_UNDEFINED;
gTotalAdmissions = 0;
gTotalIncomeFromAdmissions = 0;
safe_strcpy(gScenarioCompletedBy, "?", sizeof(gScenarioCompletedBy));
park_reset_history();
finance_reset_history();
award_reset();
reset_all_ride_build_dates();
date_reset();
duck_remove_all();
park_calculate_size();
staff_reset_stats();
gLastEntranceStyle = RIDE_ENTRANCE_STYLE_PLAIN;
memset(gMarketingCampaignDaysLeft, 0, 20);
gParkRatingCasualtyPenalty = 0;
// Open park with free entry when there is no money
if (gParkFlags & PARK_FLAGS_NO_MONEY) {
gParkFlags |= PARK_FLAGS_PARK_OPEN;
gParkEntranceFee = 0;
}
gParkFlags |= PARK_FLAGS_18;
load_palette();
gfx_invalidate_screen();
gScreenAge = 0;
gGameSpeed = 1;
}
static void scenario_end()
{
rct_window* w;
window_close_by_class(WC_DROPDOWN);
for (w = g_window_list; w < gWindowNextSlot; w++){
if (!(w->flags & (WF_STICK_TO_BACK | WF_STICK_TO_FRONT)))
window_close(w);
}
window_park_objective_open();
}
void scenario_set_filename(const char *value)
{
substitute_path(_scenarioPath, sizeof(_scenarioPath), gRCT2AddressScenariosPath, value);
_scenarioFileName = path_get_filename(_scenarioPath);
}
/**
*
* rct2: 0x0066A752
*/
void scenario_failure()
{
gScenarioCompletedCompanyValue = 0x80000001;
scenario_end();
}
/**
*
* rct2: 0x0066A75E
*/
void scenario_success()
{
const money32 companyValue = gCompanyValue;
gScenarioCompletedCompanyValue = companyValue;
peep_applause();
if (scenario_repository_try_record_highscore(_scenarioFileName, companyValue, NULL))
{
// Allow name entry
gParkFlags |= PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT;
gScenarioCompanyValueRecord = companyValue;
}
scenario_end();
}
/**
*
* rct2: 0x006695E8
*/
void scenario_success_submit_name(const char *name)
{
if (scenario_repository_try_record_highscore(_scenarioFileName, gScenarioCompanyValueRecord, name))
{
safe_strcpy(gScenarioCompletedBy, name, 32);
}
gParkFlags &= ~PARK_FLAGS_SCENARIO_COMPLETE_NAME_INPUT;
}
/**
* Send a warning when entrance price is too high.
* rct2: 0x0066A80E
*/
static void scenario_entrance_fee_too_high_check()
{
uint16 x = 0, y = 0;
money16 totalRideValue = gTotalRideValue;
money16 max_fee = totalRideValue + (totalRideValue / 2);
if ((gParkFlags & PARK_FLAGS_PARK_OPEN) && park_get_entrance_fee() > max_fee) {
for (int i = 0; gParkEntranceX[i] != SPRITE_LOCATION_NULL; i++) {
x = gParkEntranceX[i] + 16;
y = gParkEntranceY[i] + 16;
}
uint32 packed_xy = (y << 16) | x;
if (gConfigNotifications.park_warnings) {
news_item_add_to_queue(NEWS_ITEM_BLANK, STR_ENTRANCE_FEE_TOO_HI, packed_xy);
}
}
}
void scenario_autosave_check()
{
// Milliseconds since last save
uint32 timeSinceSave = SDL_GetTicks() - gLastAutoSaveTick;
bool shouldSave = false;
switch (gConfigGeneral.autosave_frequency) {
case AUTOSAVE_EVERY_MINUTE:
shouldSave = timeSinceSave >= 1 * 60 * 1000;
break;
case AUTOSAVE_EVERY_5MINUTES:
shouldSave = timeSinceSave >= 5 * 60 * 1000;
break;
case AUTOSAVE_EVERY_15MINUTES:
shouldSave = timeSinceSave >= 15 * 60 * 1000;
break;
case AUTOSAVE_EVERY_30MINUTES:
shouldSave = timeSinceSave >= 30 * 60 * 1000;
break;
case AUTOSAVE_EVERY_HOUR:
shouldSave = timeSinceSave >= 60 * 60 * 1000;
break;
}
if (shouldSave) {
gLastAutoSaveTick = SDL_GetTicks();
game_autosave();
}
}
static void scenario_day_update()
{
finance_update_daily_profit();
peep_update_days_in_queue();
switch (gScenarioObjectiveType) {
case OBJECTIVE_10_ROLLERCOASTERS:
case OBJECTIVE_GUESTS_AND_RATING:
case OBJECTIVE_10_ROLLERCOASTERS_LENGTH:
case OBJECTIVE_FINISH_5_ROLLERCOASTERS:
case OBJECTIVE_REPLAY_LOAN_AND_PARK_VALUE:
scenario_objective_check();
break;
}
// Lower the casualty penalty
uint16 casualtyPenaltyModifier = (gParkFlags & PARK_FLAGS_NO_MONEY) ? 40 : 7;
gParkRatingCasualtyPenalty = max(0, gParkRatingCasualtyPenalty - casualtyPenaltyModifier);
gToolbarDirtyFlags |= BTM_TB_DIRTY_FLAG_DATE;
}
static void scenario_week_update()
{
int month = gDateMonthsElapsed & 7;
finance_pay_wages();
finance_pay_research();
finance_pay_interest();
marketing_update();
peep_problem_warnings_update();
ride_check_all_reachable();
ride_update_favourited_stat();
rct_water_type* water_type = (rct_water_type*)object_entry_groups[OBJECT_TYPE_WATER].chunks[0];
if (month <= MONTH_APRIL && (intptr_t)water_type != -1 && water_type->var_0E & 1) {
// 100 attempts at finding some water to create a few ducks at
for (int i = 0; i < 100; i++) {
if (scenario_create_ducks())
break;
}
}
park_update_histories();
park_calculate_size();
}
static void scenario_fortnight_update()
{
finance_pay_ride_upkeep();
}
static void scenario_month_update()
{
finance_shift_expenditure_table();
scenario_objective_check();
scenario_entrance_fee_too_high_check();
award_update_all();
}
static void scenario_update_daynight_cycle()
{
float currentDayNightCycle = gDayNightCycle;
gDayNightCycle = 0;
if (gScreenFlags == SCREEN_FLAGS_PLAYING && gConfigGeneral.day_night_cycle) {
float monthFraction = gDateMonthTicks / (float)0x10000;
if (monthFraction < (1 / 8.0f)) {
gDayNightCycle = 0.0f;
} else if (monthFraction < (3 / 8.0f)) {
gDayNightCycle = (monthFraction - (1 / 8.0f)) / (2 / 8.0f);
} else if (monthFraction < (5 / 8.0f)) {
gDayNightCycle = 1.0f;
} else if (monthFraction < (7 / 8.0f)) {
gDayNightCycle = 1.0f - ((monthFraction - (5 / 8.0f)) / (2 / 8.0f));
} else {
gDayNightCycle = 0.0f;
}
}
// Only update palette if day / night cycle has changed
if (gDayNightCycle != currentDayNightCycle) {
platform_update_palette(gGamePalette, 10, 236);
}
}
/**
* Scenario and finance related update iteration.
* rct2: 0x006C44B1
*/
void scenario_update()
{
if (!(gScreenFlags & ~SCREEN_FLAGS_PLAYING)) {
uint32 currentMonthTick = gDateMonthTicks;
uint32 nextMonthTick = currentMonthTick + 4;
uint8 currentMonth = gDateMonthsElapsed & 7;
uint8 currentDaysInMonth = (uint8)days_in_month[currentMonth];
if ((currentDaysInMonth * nextMonthTick) >> 16 != (currentDaysInMonth * currentMonthTick) >> 16) {
scenario_day_update();
}
if (nextMonthTick % 0x4000 == 0) {
scenario_week_update();
}
if (nextMonthTick % 0x8000 == 0) {
scenario_fortnight_update();
}
gDateMonthTicks = (uint16)nextMonthTick;
if (nextMonthTick >= 0x10000) {
gDateMonthsElapsed++;
scenario_month_update();
}
}
scenario_update_daynight_cycle();
}
/**
*
* rct2: 0x006744A9
*/
static int scenario_create_ducks()
{
int i, j, r, c, x, y, waterZ, centreWaterZ, x2, y2;
r = scenario_rand();
x = ((r >> 16) & 0xFFFF) & 0x7F;
y = (r & 0xFFFF) & 0x7F;
x = (x + 64) * 32;
y = (y + 64) * 32;
if (!map_is_location_in_park(x, y))
return 0;
centreWaterZ = (map_element_height(x, y) >> 16) & 0xFFFF;
if (centreWaterZ == 0)
return 0;
// Check 7x7 area around centre tile
x2 = x - (32 * 3);
y2 = y - (32 * 3);
c = 0;
for (i = 0; i < 7; i++) {
for (j = 0; j < 7; j++) {
waterZ = (map_element_height(x2, y2) >> 16) & 0xFFFF;
if (waterZ == centreWaterZ)
c++;
x2 += 32;
}
x2 -= 224;
y2 += 32;
}
// Must be at least 25 water tiles of the same height in 7x7 area
if (c < 25)
return 0;
// Set x, y to the centre of the tile
x += 16;
y += 16;
c = (scenario_rand() & 3) + 2;
for (i = 0; i < c; i++) {
r = scenario_rand();
x2 = (r >> 16) & 0x7F;
y2 = (r & 0xFFFF) & 0x7F;
create_duck(x + x2 - 64, y + y2 - 64);
}
return 1;
}
/**
*
* rct2: 0x006E37D2
*
* @return eax
*/
unsigned int scenario_rand()
{
#ifdef DEBUG_DESYNC
if (!gInUpdateCode) {
log_warning("scenario_rand called from outside game update");
assert(false);
}
#endif
uint32 originalSrand0 = gScenarioSrand0;
gScenarioSrand0 += ror32(gScenarioSrand1 ^ 0x1234567F, 7);
return gScenarioSrand1 = ror32(originalSrand0, 3);
}
unsigned int scenario_rand_max(unsigned int max)
{
if (max < 2) return 0;
if ((max & (max - 1)) == 0)
return scenario_rand() & (max - 1);
unsigned int rand, cap = ~((unsigned int)0) - (~((unsigned int)0) % max) - 1;
do {
rand = scenario_rand();
} while (rand > cap);
return rand % max;
}
/**
* Prepare rides, for the finish five rollercoasters objective.
* rct2: 0x006788F7
*/
static void scenario_prepare_rides_for_save()
{
int i;
rct_ride *ride;
map_element_iterator it;
int isFiveCoasterObjective = gScenarioObjectiveType == OBJECTIVE_FINISH_5_ROLLERCOASTERS;
// Set all existing track to be indestructible
map_element_iterator_begin(&it);
do {
if (map_element_get_type(it.element) == MAP_ELEMENT_TYPE_TRACK) {
if (isFiveCoasterObjective)
it.element->flags |= 0x40;
else
it.element->flags &= ~0x40;
}
} while (map_element_iterator_next(&it));
// Set all existing rides to have indestructible track
FOR_ALL_RIDES(i, ride) {
if (isFiveCoasterObjective)
ride->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK;
else
ride->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK;
}
}
/**
*
* rct2: 0x006726C7
*/
int scenario_prepare_for_save()
{
gS6Info.entry.flags = 255;
rct_stex_entry* stex = g_stexEntries[0];
if ((intptr_t)stex != -1) {
char buffer[256];
format_string(buffer, 256, stex->scenario_name, NULL);
safe_strcpy(gS6Info.name, buffer, sizeof(gS6Info.name));
memcpy(&gS6Info.entry, &object_entry_groups[OBJECT_TYPE_SCENARIO_TEXT].entries[0], sizeof(rct_object_entry));
}
if (gS6Info.name[0] == 0)
format_string(gS6Info.name, 64, gParkName, &gParkNameArgs);
gS6Info.objective_type = gScenarioObjectiveType;
gS6Info.objective_arg_1 = gScenarioObjectiveYear;
gS6Info.objective_arg_2 = gScenarioObjectiveCurrency;
gS6Info.objective_arg_3 = gScenarioObjectiveNumGuests;
scenario_prepare_rides_for_save();
if (gScenarioObjectiveType == OBJECTIVE_GUESTS_AND_RATING)
gParkFlags |= PARK_FLAGS_PARK_OPEN;
// Fix #2385: saved scenarios did not initialise temperatures to selected climate
climate_reset(gClimate);
return 1;
}
/**
*
* rct2: 0x006AA039
*/
static int scenario_write_available_objects(FILE *file)
{
const int totalEntries = OBJECT_ENTRY_COUNT;
const int bufferLength = totalEntries * sizeof(rct_object_entry);
// Initialise buffers
uint8 *buffer = malloc(bufferLength);
if (buffer == NULL) {
log_error("out of memory");
return 0;
}
uint8 *dstBuffer = malloc(bufferLength + sizeof(sawyercoding_chunk_header));
if (dstBuffer == NULL) {
free(buffer);
log_error("out of memory");
return 0;
}
// Write entries
rct_object_entry *dstEntry = (rct_object_entry*)buffer;
for (int i = 0; i < OBJECT_ENTRY_COUNT; i++) {
void *entryData = get_loaded_object_chunk(i);
if (entryData == (void*)-1) {
memset(dstEntry, 0xFF, sizeof(rct_object_entry));
} else {
*dstEntry = *get_loaded_object_entry(i);
}
dstEntry++;
}
// Write chunk
sawyercoding_chunk_header chunkHeader;
chunkHeader.encoding = CHUNK_ENCODING_ROTATE;
chunkHeader.length = bufferLength;
size_t encodedLength = sawyercoding_write_chunk_buffer(dstBuffer, buffer, chunkHeader);
fwrite(dstBuffer, encodedLength, 1, file);
// Free buffers
free(dstBuffer);
free(buffer);
return 1;
}
/**
* Modifies the given S6 data so that ghost elements, rides with no track elements or unused banners / user strings are saved.
*/
void scenario_fix_ghosts(rct_s6_data *s6)
{
// Remove all ghost elements
rct_map_element *destinationElement = s6->map_elements;
for (int y = 0; y < 256; y++) {
for (int x = 0; x < 256; x++) {
rct_map_element *originalElement = map_get_first_element_at(x, y);
do {
if (originalElement->flags & MAP_ELEMENT_FLAG_GHOST) {
int bannerIndex = map_element_get_banner_index(originalElement);
if (bannerIndex != -1) {
rct_banner *banner = &s6->banners[bannerIndex];
if (banner->type != BANNER_NULL) {
banner->type = BANNER_NULL;
if (is_user_string_id(banner->string_idx))
s6->custom_strings[(banner->string_idx % MAX_USER_STRINGS) * USER_STRING_MAX_LENGTH] = 0;
}
}
} else {
*destinationElement++ = *originalElement;
}
} while (!map_element_is_last_for_tile(originalElement++));
// Set last element flag in case the original last element was never added
(destinationElement - 1)->flags |= MAP_ELEMENT_FLAG_LAST_TILE;
}
}
}
void scenario_remove_trackless_rides(rct_s6_data *s6)
{
bool rideHasTrack[MAX_RIDES];
ride_all_has_any_track_elements(rideHasTrack);
for (int i = 0; i < MAX_RIDES; i++) {
rct_ride *ride = &s6->rides[i];
if (rideHasTrack[i] || ride->type == RIDE_TYPE_NULL) {
continue;
}
ride->type = RIDE_TYPE_NULL;
if (is_user_string_id(ride->name)) {
s6->custom_strings[(ride->name % MAX_USER_STRINGS) * USER_STRING_MAX_LENGTH] = 0;
}
}
}
static void scenario_objective_check_guests_by()
{
uint8 objectiveYear = gScenarioObjectiveYear;
sint16 parkRating = gParkRating;
sint16 guestsInPark = gNumGuestsInPark;
sint16 objectiveGuests = gScenarioObjectiveNumGuests;
sint16 currentMonthYear = gDateMonthsElapsed;
if (currentMonthYear == 8 * objectiveYear){
if (parkRating >= 600 && guestsInPark >= objectiveGuests)
scenario_success();
else
scenario_failure();
}
}
static void scenario_objective_check_park_value_by()
{
uint8 objectiveYear = gScenarioObjectiveYear;
sint16 currentMonthYear = gDateMonthsElapsed;
money32 objectiveParkValue = gScenarioObjectiveCurrency;
money32 parkValue = gParkValue;
if (currentMonthYear == 8 * objectiveYear) {
if (parkValue >= objectiveParkValue)
scenario_success();
else
scenario_failure();
}
}
/**
* Checks if there are 10 rollercoasters of different subtype with
* excitement >= 600 .
* rct2:
**/
static void scenario_objective_check_10_rollercoasters()
{
int i, rcs = 0;
uint8 type_already_counted[256];
rct_ride* ride;
memset(type_already_counted, 0, 256);
FOR_ALL_RIDES(i, ride) {
uint8 subtype_id = ride->subtype;
rct_ride_entry *rideType = get_ride_entry(subtype_id);
if (rideType == NULL) {
continue;
}
if (rideType != NULL &&
(rideType->category[0] == RIDE_GROUP_ROLLERCOASTER || rideType->category[1] == RIDE_GROUP_ROLLERCOASTER) &&
ride->status == RIDE_STATUS_OPEN &&
ride->excitement >= RIDE_RATING(6,00) && type_already_counted[subtype_id] == 0){
type_already_counted[subtype_id]++;
rcs++;
}
}
if (rcs >= 10)
scenario_success();
}
/**
*
* rct2: 0x0066A13C
*/
static void scenario_objective_check_guests_and_rating()
{
if (gParkRating < 700 && gDateMonthsElapsed >= 1) {
gScenarioParkRatingWarningDays++;
if (gScenarioParkRatingWarningDays == 1) {
if (gConfigNotifications.park_rating_warnings) {
news_item_add_to_queue(NEWS_ITEM_GRAPH, STR_PARK_RATING_WARNING_4_WEEKS_REMAINING, 0);
}
} else if (gScenarioParkRatingWarningDays == 8) {
if (gConfigNotifications.park_rating_warnings) {
news_item_add_to_queue(NEWS_ITEM_GRAPH, STR_PARK_RATING_WARNING_3_WEEKS_REMAINING, 0);
}
} else if (gScenarioParkRatingWarningDays == 15) {
if (gConfigNotifications.park_rating_warnings) {
news_item_add_to_queue(NEWS_ITEM_GRAPH, STR_PARK_RATING_WARNING_2_WEEKS_REMAINING, 0);
}
} else if (gScenarioParkRatingWarningDays == 22) {
if (gConfigNotifications.park_rating_warnings) {
news_item_add_to_queue(NEWS_ITEM_GRAPH, STR_PARK_RATING_WARNING_1_WEEK_REMAINING, 0);
}
} else if (gScenarioParkRatingWarningDays == 29) {
news_item_add_to_queue(NEWS_ITEM_GRAPH, STR_PARK_HAS_BEEN_CLOSED_DOWN, 0);
gParkFlags &= ~PARK_FLAGS_PARK_OPEN;
scenario_failure();
gGuestInitialHappiness = 50;
}
} else if (gScenarioCompletedCompanyValue != 0x80000001) {
gScenarioParkRatingWarningDays = 0;
}
if (gParkRating >= 700)
if (gNumGuestsInPark >= gScenarioObjectiveNumGuests)
scenario_success();
}
static void scenario_objective_check_monthly_ride_income()
{
money32 *expenditureLastMonth = &gExpenditureTable[1 * RCT_EXPENDITURE_TYPE_COUNT];
money32 lastMonthRideIncome = expenditureLastMonth[RCT_EXPENDITURE_TYPE_PARK_RIDE_TICKETS];
if (lastMonthRideIncome >= gScenarioObjectiveCurrency) {
scenario_success();
}
}
/**
* Checks if there are 10 rollercoasters of different subtype with
* excitement > 700 and a minimum length;
* rct2: 0x0066A6B5
*/
static void scenario_objective_check_10_rollercoasters_length()
{
int i, rcs = 0;
uint8 type_already_counted[256];
sint16 objective_length = gScenarioObjectiveNumGuests;
rct_ride* ride;
memset(type_already_counted, 0, 256);
FOR_ALL_RIDES(i, ride) {
uint8 subtype_id = ride->subtype;
rct_ride_entry *rideType = get_ride_entry(subtype_id);
if (rideType == NULL) {
continue;
}
if ((rideType->category[0] == RIDE_GROUP_ROLLERCOASTER || rideType->category[1] == RIDE_GROUP_ROLLERCOASTER) &&
ride->status == RIDE_STATUS_OPEN &&
ride->excitement >= RIDE_RATING(7,00) && type_already_counted[subtype_id] == 0){
if ((ride_get_total_length(ride) >> 16) > objective_length) {
type_already_counted[subtype_id]++;
rcs++;
}
}
}
if (rcs >= 10)
scenario_success();
}
static void scenario_objective_check_finish_5_rollercoasters()
{
int i;
rct_ride* ride;
money32 objectiveRideExcitement = gScenarioObjectiveCurrency;
// ORIGINAL BUG?:
// This does not check if the rides are even rollercoasters nevermind the right rollercoasters to be finished.
// It also did not exclude null rides.
int rcs = 0;
FOR_ALL_RIDES(i, ride)
if (ride->status != RIDE_STATUS_CLOSED && ride->excitement >= objectiveRideExcitement)
rcs++;
if (rcs >= 5)
scenario_success();
}
static void scenario_objective_check_replay_loan_and_park_value()
{
money32 objectiveParkValue = gScenarioObjectiveCurrency;
money32 parkValue = gParkValue;
money32 currentLoan = gBankLoan;
if (currentLoan <= 0 && parkValue >= objectiveParkValue)
scenario_success();
}
static void scenario_objective_check_monthly_food_income()
{
money32 *expenditureLastMonth = &gExpenditureTable[1 * RCT_EXPENDITURE_TYPE_COUNT];
sint32 lastMonthProfit =
expenditureLastMonth[RCT_EXPENDITURE_TYPE_SHOP_SHOP_SALES] +
expenditureLastMonth[RCT_EXPENDITURE_TYPE_SHOP_STOCK] +
expenditureLastMonth[RCT_EXPENDITURE_TYPE_FOODDRINK_SALES] +
expenditureLastMonth[RCT_EXPENDITURE_TYPE_FOODDRINK_STOCK];
if (lastMonthProfit >= gScenarioObjectiveCurrency) {
scenario_success();
}
}
/**
* Checks the win/lose conditions of the current objective.
* rct2: 0x0066A4B2
*/
static void scenario_objective_check()
{
if (gScenarioCompletedCompanyValue != MONEY32_UNDEFINED) {
return;
}
switch (gScenarioObjectiveType) {
case OBJECTIVE_GUESTS_BY:
scenario_objective_check_guests_by();
break;
case OBJECTIVE_PARK_VALUE_BY:
scenario_objective_check_park_value_by();
break;
case OBJECTIVE_10_ROLLERCOASTERS:
scenario_objective_check_10_rollercoasters();
break;
case OBJECTIVE_GUESTS_AND_RATING:
scenario_objective_check_guests_and_rating();
break;
case OBJECTIVE_MONTHLY_RIDE_INCOME:
scenario_objective_check_monthly_ride_income();
break;
case OBJECTIVE_10_ROLLERCOASTERS_LENGTH:
scenario_objective_check_10_rollercoasters_length();
break;
case OBJECTIVE_FINISH_5_ROLLERCOASTERS:
scenario_objective_check_finish_5_rollercoasters();
break;
case OBJECTIVE_REPLAY_LOAN_AND_PARK_VALUE:
scenario_objective_check_replay_loan_and_park_value();
break;
case OBJECTIVE_MONTHLY_FOOD_INCOME:
scenario_objective_check_monthly_food_income();
break;
}
}