diff --git a/data/language/en-GB.txt b/data/language/en-GB.txt index 7c55790b27..5fefe5deaa 100644 --- a/data/language/en-GB.txt +++ b/data/language/en-GB.txt @@ -4430,7 +4430,7 @@ STR_6118 :A gentle roller coaster for people who haven't yet got the courage STR_6119 :A cheap and easy to build roller coaster, but with a limited height STR_6120 :{BABYBLUE}New vehicle now available for {STRINGID}:{NEWLINE}{STRINGID} STR_6121 :{SMALLFONT}{BLACK}Extends the park's land rights all the way to the edges of the map - +STR_6122 :There are not enough roller coasters in this scenario! ############# # Scenarios # diff --git a/distribution/changelog.txt b/distribution/changelog.txt index dada835cb8..c3eec2ef73 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -13,6 +13,7 @@ - Fix: [#259] Money making glitch involving swamps (original bug) - Fix: [#441] Construction rights over entrance path erased (original bug) - Fix: [#578] Ride ghosts show up in ride list during construction (original bug) +- Fix: [#597] 'Finish 5 roller coasters' goal not properly checked (original bug) - Fix: [#739] Crocodile Ride (Log Flume) never allows more than five boats (original bug) - Fix: [#837] Can't move windows on title screen to where the toolbar would be (original bug) - Fix: [#1705] Time Twister's Medieval entrance has incorrect scrolling (original bug) diff --git a/src/openrct2/localisation/string_ids.h b/src/openrct2/localisation/string_ids.h index d5a88ca4cf..54984fa6da 100644 --- a/src/openrct2/localisation/string_ids.h +++ b/src/openrct2/localisation/string_ids.h @@ -3789,6 +3789,8 @@ enum { STR_CHEAT_OWN_ALL_LAND_TIP = 6121, + STR_NOT_ENOUGH_ROLLER_COASTERS = 6122, + // Have to include resource strings (from scenarios and objects) for the time being now that language is partially working STR_COUNT = 32768 }; diff --git a/src/openrct2/ride/track.c b/src/openrct2/ride/track.c index 8b8413955e..796dd4533c 100644 --- a/src/openrct2/ride/track.c +++ b/src/openrct2/ride/track.c @@ -945,7 +945,7 @@ static money32 track_place(sint32 rideIndex, sint32 type, sint32 originX, sint32 uint64 enabledTrackPieces = rideEntry->enabledTrackPieces & RideTypePossibleTrackConfigurations[ride->type]; uint32 rideTypeFlags = RideProperties[ride->type].flags; - if ((ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) && type == 1) { + if ((ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) && type == TRACK_ELEM_END_STATION) { gGameCommandErrorText = STR_NOT_ALLOWED_TO_MODIFY_STATION; return MONEY32_UNDEFINED; } @@ -1508,7 +1508,7 @@ static money32 track_remove(uint8 type, uint8 sequence, sint16 originX, sint16 o return MONEY32_UNDEFINED; } - if (mapElement->flags & (1 << 6)){ + if (mapElement->flags & MAP_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE){ gGameCommandErrorText = STR_YOU_ARE_NOT_ALLOWED_TO_REMOVE_THIS_SECTION; return MONEY32_UNDEFINED; } diff --git a/src/openrct2/scenario/scenario.c b/src/openrct2/scenario/scenario.c index de0931bb1c..939fec7261 100644 --- a/src/openrct2/scenario/scenario.c +++ b/src/openrct2/scenario/scenario.c @@ -660,38 +660,78 @@ uint32 scenario_rand_max(uint32 max) * Prepare rides, for the finish five rollercoasters objective. * rct2: 0x006788F7 */ -static void scenario_prepare_rides_for_save() +static bool scenario_prepare_rides_for_save() { sint32 isFiveCoasterObjective = gScenarioObjectiveType == OBJECTIVE_FINISH_5_ROLLERCOASTERS; + sint32 i; + rct_ride * ride; + uint8 rcs = 0; - // Set all existing track to be indestructible + FOR_ALL_RIDES(i, ride) + { + const rct_ride_entry * rideEntry = get_ride_entry(ride->subtype); + + // If there are more than 5 roller coasters, only mark the first five. + if (isFiveCoasterObjective && + rideEntry != NULL && + ((rideEntry->category[0] == RIDE_GROUP_ROLLERCOASTER || rideEntry->category[1] == RIDE_GROUP_ROLLERCOASTER) && + rcs < 5)) + { + ride->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; + rcs++; + } + else + { + ride->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; + } + } + + if (isFiveCoasterObjective && rcs < 5) + { + gGameCommandErrorText = STR_NOT_ENOUGH_ROLLER_COASTERS; + return false; + } + + bool markTrackAsIndestructible; map_element_iterator it; 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)); + do + { + if (map_element_get_type(it.element) == MAP_ELEMENT_TYPE_TRACK) + { + markTrackAsIndestructible = false; - // Set all existing rides to have indestructible track - sint32 i; - rct_ride *ride; - FOR_ALL_RIDES(i, ride) { - if (isFiveCoasterObjective) - ride->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; - else - ride->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; + if (isFiveCoasterObjective) + { + ride = get_ride(it.element->properties.track.ride_index); + + // In the previous step, this flag was set on the first five roller coasters. + if (ride != NULL && ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) + { + markTrackAsIndestructible = true; + } + } + + if (markTrackAsIndestructible) + { + it.element->flags |= MAP_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE; + } + else + { + it.element->flags &= ~MAP_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE; + } + } } + while (map_element_iterator_next(&it)); + + return true; } /** * * rct2: 0x006726C7 */ -sint32 scenario_prepare_for_save() +bool scenario_prepare_for_save() { gS6Info.entry.flags = 255; @@ -712,7 +752,11 @@ sint32 scenario_prepare_for_save() gS6Info.objective_arg_2 = gScenarioObjectiveCurrency; gS6Info.objective_arg_3 = gScenarioObjectiveNumGuests; - scenario_prepare_rides_for_save(); + // This can return false if the goal is 'Finish 5 roller coaster' and there are too few. + if (!scenario_prepare_rides_for_save()) + { + return false; + } if (gScenarioObjectiveType == OBJECTIVE_GUESTS_AND_RATING) gParkFlags |= PARK_FLAGS_PARK_OPEN; @@ -720,7 +764,7 @@ sint32 scenario_prepare_for_save() // Fix #2385: saved scenarios did not initialise temperatures to selected climate climate_reset(gClimate); - return 1; + return true; } /** @@ -972,15 +1016,25 @@ static void scenario_objective_check_finish_5_rollercoasters() { 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. + // Originally, this did not check for null rides, neither did it check if + // the rides are even rollercoasters, never mind the right rollercoasters to be finished. sint32 i; - rct_ride* ride; + rct_ride * ride; sint32 rcs = 0; FOR_ALL_RIDES(i, ride) - if (ride->status != RIDE_STATUS_CLOSED && ride->excitement >= objectiveRideExcitement) + { + const rct_ride_entry * rideEntry = get_ride_entry(ride->subtype); + if (rideEntry == NULL) + { + continue; + } + + if (ride->status != RIDE_STATUS_CLOSED && + ride->excitement >= objectiveRideExcitement && + (ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) && // Set on partially finished coasters + (rideEntry->category[0] == RIDE_GROUP_ROLLERCOASTER || rideEntry->category[1] == RIDE_GROUP_ROLLERCOASTER)) rcs++; + } if (rcs >= 5) scenario_success(); diff --git a/src/openrct2/scenario/scenario.h b/src/openrct2/scenario/scenario.h index 05939d1dae..fa76197933 100644 --- a/src/openrct2/scenario/scenario.h +++ b/src/openrct2/scenario/scenario.h @@ -405,7 +405,7 @@ uint32 scenario_rand(); uint32 scenario_rand_max(uint32 max); -sint32 scenario_prepare_for_save(); +bool scenario_prepare_for_save(); sint32 scenario_save(const utf8 * path, sint32 flags); void scenario_remove_trackless_rides(rct_s6_data *s6); void scenario_fix_ghosts(rct_s6_data *s6); diff --git a/src/openrct2/windows/editor_objective_options.c b/src/openrct2/windows/editor_objective_options.c index f232677d3d..c097a71808 100644 --- a/src/openrct2/windows/editor_objective_options.c +++ b/src/openrct2/windows/editor_objective_options.c @@ -440,62 +440,58 @@ static void window_editor_objective_options_main_resize(rct_window *w) static void window_editor_objective_options_show_objective_dropdown(rct_window *w) { - sint32 i, numItems, objectiveType; + sint32 numItems = 0, objectiveType; rct_widget *dropdownWidget; uint32 parkFlags; dropdownWidget = &w->widgets[WIDX_OBJECTIVE]; parkFlags = gParkFlags; - numItems = 0; - if (!(parkFlags & PARK_FLAGS_NO_MONEY_SCENARIO)) { - numItems += 2; + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_HAVE_FUN; + numItems++; + + if (!(parkFlags & PARK_FLAGS_NO_MONEY_SCENARIO)) + { + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_NUMBER_OF_GUESTS_AT_A_GIVEN_DATE; + numItems++; + + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_MONTHLY_PROFIT_FROM_FOOD_MERCHANDISE; + numItems++; + + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_REPAY_LOAN_AND_ACHIEVE_A_GIVEN_PARK_VALUE; + numItems++; + + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_PARK_VALUE_AT_A_GIVEN_DATE; + numItems++; + if (parkFlags & PARK_FLAGS_PARK_FREE_ENTRY) + { + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_MONTHLY_INCOME_FROM_RIDE_TICKETS; numItems++; - } - - numItems += 5; - - i = 0; - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_HAVE_FUN; - i++; - - if (!(parkFlags & PARK_FLAGS_NO_MONEY_SCENARIO)) { - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_NUMBER_OF_GUESTS_AT_A_GIVEN_DATE; - i++; - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_MONTHLY_PROFIT_FROM_FOOD_MERCHANDISE; - i++; - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_REPAY_LOAN_AND_ACHIEVE_A_GIVEN_PARK_VALUE; - i++; - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_PARK_VALUE_AT_A_GIVEN_DATE; - i++; - if (parkFlags & PARK_FLAGS_PARK_FREE_ENTRY) { - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_MONTHLY_INCOME_FROM_RIDE_TICKETS; - i++; } } - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_NUMBER_OF_GUESTS_IN_PARK; - i++; + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_NUMBER_OF_GUESTS_IN_PARK; + numItems++; - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_BUILD_10_ROLLER_COASTERS; - i++; + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_BUILD_10_ROLLER_COASTERS; + numItems++; - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_BUILD_10_ROLLER_COASTERS_OF_A_GIVEN_LENGTH; - i++; + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_BUILD_10_ROLLER_COASTERS_OF_A_GIVEN_LENGTH; + numItems++; - gDropdownItemsFormat[i] = STR_DROPDOWN_MENU_LABEL; - gDropdownItemsArgs[i] = STR_OBJECTIVE_DROPDOWN_FINISH_BUILDING_5_ROLLER_COASTERS; - i++; + gDropdownItemsFormat[numItems] = STR_DROPDOWN_MENU_LABEL; + gDropdownItemsArgs[numItems] = STR_OBJECTIVE_DROPDOWN_FINISH_BUILDING_5_ROLLER_COASTERS; + numItems++; window_dropdown_show_text_custom_width( w->x + dropdownWidget->left, @@ -509,9 +505,11 @@ static void window_editor_objective_options_show_objective_dropdown(rct_window * ); objectiveType = gScenarioObjectiveType; - for (i = 0; i < numItems; i++) { - if (gDropdownItemsArgs[i] - STR_OBJECTIVE_DROPDOWN_NONE == objectiveType) { - dropdown_set_checked(i, true); + for (sint32 j = 0; j < numItems; j++) + { + if (gDropdownItemsArgs[j] - STR_OBJECTIVE_DROPDOWN_NONE == objectiveType) + { + dropdown_set_checked(j, true); break; } } diff --git a/src/openrct2/world/map.h b/src/openrct2/world/map.h index d20d2bde38..0890b97855 100644 --- a/src/openrct2/world/map.h +++ b/src/openrct2/world/map.h @@ -155,7 +155,7 @@ enum { MAP_ELEMENT_FLAG_GHOST = (1 << 4), MAP_ELEMENT_FLAG_BROKEN = (1 << 5), MAP_ELEMENT_FLAG_BLOCK_BRAKE_CLOSED = (1 << 5), - MAP_ELEMENT_FLAG_CANNOT_REMOVE_TRACK = (1 << 6), + MAP_ELEMENT_FLAG_INDESTRUCTIBLE_TRACK_PIECE = (1 << 6), MAP_ELEMENT_FLAG_LAST_TILE = (1 << 7) };