diff --git a/distribution/changelog.txt b/distribution/changelog.txt index f853c550ea..6ac0d9697b 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -8,6 +8,7 @@ - Feature: [#5113] Entertainers are now hired with a random costume. - Improved: [#4847] Guest / staff pathfinding. - Improved: [#4938] Checksum calculations speeded up. +- Improved: [#5007] Vehicles and functioning rides are now imported when loading SC4 / SV4 parks. - Improved: Guests and staff are now imported when loading SC4 / SV4 parks. - Fix: [#4571] Only start autosave timer after update or game command. - Fix: [#4584] Junior Coaster diagonal flat-to-steep slopes not drawn. diff --git a/src/openrct2/game.c b/src/openrct2/game.c index 1a0566266a..152be822cb 100644 --- a/src/openrct2/game.c +++ b/src/openrct2/game.c @@ -729,14 +729,20 @@ void game_convert_strings_to_utf8() } } - // News items - for (sint32 i = 0; i < MAX_NEWS_ITEMS; i++) { - NewsItem *newsItem = news_item_get(i); + // News items + game_convert_news_items_to_utf8(); - if (!str_is_null_or_empty(newsItem->Text)) { - rct2_to_utf8_self(newsItem->Text, sizeof(newsItem->Text)); - } - } +} + +void game_convert_news_items_to_utf8() +{ + for (sint32 i = 0; i < MAX_NEWS_ITEMS; i++) { + NewsItem *newsItem = news_item_get(i); + + if (!str_is_null_or_empty(newsItem->Text)) { + rct2_to_utf8_self(newsItem->Text, sizeof(newsItem->Text)); + } + } } /** diff --git a/src/openrct2/game.h b/src/openrct2/game.h index bf13755535..8c70bbb44d 100644 --- a/src/openrct2/game.h +++ b/src/openrct2/game.h @@ -181,6 +181,7 @@ void rct2_exit(); void rct2_exit_reason(rct_string_id title, rct_string_id body); void game_autosave(); void game_convert_strings_to_utf8(); +void game_convert_news_items_to_utf8(); void game_convert_strings_to_rct2(rct_s6_data *s6); void game_fix_save_vars(); bool game_load_save_or_scenario(const utf8 * path); diff --git a/src/openrct2/peep/peep.c b/src/openrct2/peep/peep.c index a3036b87e5..06eace2aa3 100644 --- a/src/openrct2/peep/peep.c +++ b/src/openrct2/peep/peep.c @@ -4650,7 +4650,7 @@ static bool peep_update_fixing_sub_state_11(bool firstRun, rct_peep *peep, rct_r peep_update_action(&tmp_x, &tmp_y, &tmp_xy_distance, peep); if (peep->action_frame == 0x28) { - ride->mechanic_status = 4; + ride->mechanic_status = RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES; ride->window_invalidate_flags |= RIDE_INVALIDATE_RIDE_MAINTENANCE; } diff --git a/src/openrct2/rct1.h b/src/openrct2/rct1.h index 4216a4dfd9..a13654abcc 100644 --- a/src/openrct2/rct1.h +++ b/src/openrct2/rct1.h @@ -27,8 +27,9 @@ #include "world/map.h" #include "world/sprite.h" -#define RCT1_MAX_MAP_ELEMENTS 0xC000 -#define RCT1_MAX_SPRITES 5000 +#define RCT1_MAX_MAP_ELEMENTS 0xC000 +#define RCT1_MAX_SPRITES 5000 +#define RCT1_MAX_VEHICLES_PER_RIDE 12 #pragma pack(push, 1) typedef struct rct1_entrance { @@ -52,7 +53,7 @@ typedef struct rct1_ride { struct { colour_t body; colour_t trim; - } vehicle_colours[12]; + } vehicle_colours[RCT1_MAX_VEHICLES_PER_RIDE]; colour_t track_primary_colour; colour_t track_secondary_colour; colour_t track_support_colour; @@ -70,7 +71,7 @@ typedef struct rct1_ride { uint16 exit[4]; uint16 last_peep_in_queue[4]; uint8 num_peeps_in_queue[4]; - uint16 vehicles[12]; + uint16 vehicles[RCT1_MAX_VEHICLES_PER_RIDE]; uint8 depart_flags; uint8 num_stations; uint8 num_trains; @@ -156,11 +157,29 @@ typedef struct rct1_ride { uint8 popularity_time_out; uint8 popularity_next; uint8 num_riders; - uint8 unk_10C[36]; - sint16 build_date; - money16 upkeep_cost; - uint8 unk_134[15]; - uint8 breakdown_reason; + uint8 music_tune_id; // 0x10C + uint8 slide_in_use; // 0x10D + union { + uint16 slide_peep; // 0x10E + uint16 maze_tiles; // 0x10E + }; + uint8 pad_110[0xE]; + uint8 slide_peep_t_shirt_colour;// 0x11E + uint8 pad_11F[0x7]; + uint8 spiral_slide_progress; // 0x126 + uint8 pad_127[0x9]; + sint16 build_date; // 0x130 + money16 upkeep_cost; // 0x131 + uint16 race_winner; // 0x132 + uint8 unk_134[2]; + uint32 music_position; // 0x138 + uint8 breakdown_reason_pending; // 0x13C + uint8 mechanic_status; // 0x13D + uint16 mechanic; // 0x13E + uint8 inspection_station; // 0x140 + uint8 broken_vehicle; // 0x141 + uint8 broken_car; // 0x142 + uint8 breakdown_reason; // 0x143 uint8 unk_144[2]; uint16 reliability; uint8 unreliability_factor; @@ -213,6 +232,122 @@ typedef struct rct1_unk_sprite { uint8 var_71; } rct1_unk_sprite; +typedef struct rct1_vehicle { + uint8 sprite_identifier; // 0x00 + uint8 is_child; // 0x01 + uint16 next_in_quadrant; // 0x02 + uint16 next; // 0x04 + uint16 previous; // 0x06 + uint8 linked_list_type_offset; // 0x08 Valid values are SPRITE_LINKEDLIST_OFFSET_... + // Height from center of sprite to bottom + uint8 sprite_height_negative; // 0x09 + uint16 sprite_index; // 0x0A + uint16 flags; // 0x0C + sint16 x; // 0x0E + sint16 y; // 0x10 + sint16 z; // 0x12 + // Width from center of sprite to edge + uint8 sprite_width; // 0x14 + // Height from center of sprite to top + uint8 sprite_height_positive; // 0x15 + sint16 sprite_left; // 0x16 + sint16 sprite_top; // 0x18 + sint16 sprite_right; // 0x1A + sint16 sprite_bottom; // 0x1C + uint8 sprite_direction; // 0x1E + uint8 vehicle_sprite_type; // 0x1F + uint8 bank_rotation; // 0x20 + uint8 pad_21[3]; + sint32 remaining_distance; // 0x24 + sint32 velocity; // 0x28 + sint32 acceleration; // 0x2C + uint8 ride; // 0x30 + uint8 vehicle_type; // 0x31 + rct_vehicle_colour colours; // 0x32 + union { + uint16 track_progress; // 0x34 + struct { + sint8 var_34; + uint8 var_35; + }; + }; + union { + sint16 track_direction; // 0x36 (0000 0000 0000 0011) + sint16 track_type; // 0x36 (0000 0011 1111 1100) + rct_xy8 boat_location; // 0x36 + }; + uint16 track_x; // 0x38 + uint16 track_y; // 0x3A + uint16 track_z; // 0x3C + uint16 next_vehicle_on_train; // 0x3E + + // The previous vehicle on the same train or the last vehicle on the previous or only train. + uint16 prev_vehicle_on_ride; // 0x40 + + // The next vehicle on the same train or the first vehicle on the next or only train + uint16 next_vehicle_on_ride; // 0x42 + + uint16 var_44; + uint16 friction; // 0x46 + uint16 update_flags; // 0x48 + uint8 var_4A; + uint8 current_station; // 0x4B + union { + sint16 swinging_car_var_0; // 0x4C + sint16 current_time; // 0x4C + struct { + sint8 ferris_wheel_var_0; // 0x4C + sint8 ferris_wheel_var_1; // 0x4D + }; + }; + sint16 var_4E; + uint8 status; // 0x50 + uint8 sub_state; // 0x51 + uint16 peep[32]; // 0x52 + uint8 peep_tshirt_colours[32]; // 0x92 + uint8 num_seats; // 0xB2 + uint8 num_peeps; // 0xB3 + uint8 next_free_seat; // 0xB4 + uint8 restraints_position; // 0xB5 0 == Close, 255 == Open + sint16 var_B6; + uint16 var_B8; + uint8 var_BA; + uint8 sound1_id; // 0xBB + uint8 sound1_volume; // 0xBC + uint8 sound2_id; // 0xBD + uint8 sound2_volume; // 0xBE + sint8 var_BF; + union { + uint16 var_C0; + uint16 time_waiting; // 0xC0 + uint16 cable_lift_target; // 0xC0 + }; + uint8 speed; // 0xC2 + uint8 powered_acceleration; // 0xC3 + uint8 var_C4; + uint8 var_C5; + uint8 pad_C6[0x2]; + uint16 var_C8; + uint16 var_CA; + uint8 scream_sound_id; // 0xCC + uint8 var_CD; + union { + uint8 var_CE; + uint8 num_laps; // 0xCE + }; + union { + uint8 var_CF; + uint8 brake_speed; // 0xCF + }; + uint16 lost_time_out; // 0xD0 + sint8 vertical_drop_countdown; // 0xD1 + uint8 var_D3; + uint8 mini_golf_current_animation; + uint8 mini_golf_flags; // 0xD5 + uint8 ride_subtype; // 0xD6 + uint8 colours_extended; // 0xD7 +} rct1_vehicle; + typedef struct rct1_peep { uint8 sprite_identifier; // 0x00 uint8 misc_identifier; // 0x01 @@ -405,6 +540,7 @@ enum RCT1_PEEP_SPRITE_TYPE { typedef union rct1_sprite { uint8 pad_00[0x100]; rct1_unk_sprite unknown; + rct1_vehicle vehicle; rct1_peep peep; rct_litter litter; rct_balloon balloon; @@ -1003,7 +1139,54 @@ enum { RCT1_PARK_FLAGS_LOCK_REAL_NAMES_OPTION = (1 << 15), }; +enum { + STEEL_RC_FRONT = 0, + STEEL_RC_CARRIAGE = 1, + WOODEN_RC_TRAIN = 2, + MONORAIL_CARRIAGE = 10, + MONORAIL_FRONT = 11, + MONORAIL_BACK = 12, + MINIATURE_RAILWAY_TENDER = 15, + MINIATURE_RAILWAY_LOCOMOTIVE = 16, + MINIATURE_RAILWAY_CARRIAGE = 17, + MINE_TRAIN_FRONT = 35, + MINE_TRAIN_CARRIAGE = 36, + CORKSCREW_RC_FRONT = 38, + CORKSCREW_RC_CARRIAGE = 39, + GHOST_TRAIN_CAR = 63, + TWISTER_RC_SPOILER = 64, + TWISTER_RC_CARRIAGE = 65, + GHOST_TRAIN_INVISIBLE = 66, + ARTICULATED_RC_FRONT = 67, + ARTICULATED_RC_CARRIAGE = 68, + MINIATURE_RAILWAY_CARRIAGE_COVERED = 71, + STANDUP_TWISTER_RC_CARRIAGE = 72, + REVERSER_RC_CAR = 79, + REVERSER_RC_BOGIE = 80, + MINIGOLF_PLAYER = 81, + MINIGOLF_BALL = 82, + HYPERCOASTER_FRONT = 96, + HYPERCOASTER_CARRIAGE = 97, + INVERTED_4_ACROSS_CARRIAGE = 98, + WATER_COASTER_BOAT = 99, + WATER_COASTER_INVISIBLE = 101, + RIVER_RAFT = 103, +}; + +enum { + COPY_COLOUR_1 = -1, + COPY_COLOUR_2 = -2, +}; + +enum { + RCT1_WATER_CYAN, + RCT1_WATER_ORANGE +}; + #define RCT1_MAX_STATIONS 4 +#define RCT1_MAX_TRAINS_PER_RIDE 12 +#define RCT1_MAX_MAP_SIZE 128 +#define RCT1_MAX_RIDES_IN_PARK 128 extern const uint8 gRideCategories[0x60]; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 0e7aae7e7c..c5b70cff60 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -23,8 +23,8 @@ #include "../core/Path.hpp" #include "../core/String.hpp" #include "../core/Util.hpp" -#include "../scenario/ScenarioSources.h" #include "../object/ObjectManager.h" +#include "../scenario/ScenarioSources.h" #include "S4Importer.h" #include "Tables.h" @@ -44,6 +44,8 @@ extern "C" #include "../peep/peep.h" #include "../peep/staff.h" #include "../rct1.h" + #include "../ride/ride_data.h" + #include "../ride/track.h" #include "../util/sawyercoding.h" #include "../util/util.h" #include "../world/climate.h" @@ -51,7 +53,6 @@ extern "C" #include "../world/map_animation.h" #include "../world/park.h" #include "../world/scenery.h" - #include "../ride/ride_data.h" } class EntryList @@ -102,6 +103,7 @@ private: EntryList _pathEntries; EntryList _pathAdditionEntries; EntryList _sceneryGroupEntries; + EntryList _waterEntry; // Lookup tables for converting from RCT1 hard coded types to the new dynamic object entries uint8 _rideTypeToRideEntryMap[96]; @@ -157,8 +159,12 @@ public: ImportScenarioNameDetails(); ImportScenarioObjective(); ImportSavedView(); + FixLandOwnership(); + CountBlockSections(); - game_convert_strings_to_utf8(); + // Importing the strings is done later on, although that approach needs looking at. + //game_convert_strings_to_utf8(); + game_convert_news_items_to_utf8(); map_count_remaining_land_rights(); } @@ -198,6 +204,7 @@ private: AddAvailableEntriesFromMap(); AddAvailableEntriesFromRides(); AddAvailableEntriesFromSceneryGroups(); + AddEntryForWater(); } void AddDefaultEntries() @@ -381,6 +388,22 @@ private: } } + void AddEntryForWater() + { + const char * entryName; + + if (_s4.game_version < FILE_VERSION_RCT1_LL) + { + entryName = RCT1::GetWaterObject(RCT1_WATER_CYAN); + } + else + { + entryName = RCT1::GetWaterObject(_s4.water_colour); + } + + _waterEntry.GetOrAddEntry(entryName); + } + void AddEntryForRideType(uint8 rideType) { assert(rideType < Util::CountOf(_rideTypeToRideEntryMap)); @@ -512,7 +535,17 @@ private: { memset(dst, 0, sizeof(rct_ride)); - dst->type = RCT1::GetRideType(src->type); + // This is a pecularity of this exact version number, which only Heide-Park seems to use. + if (_s4.game_version == 110018 && src->type == RCT1_RIDE_TYPE_INVERTED_ROLLER_COASTER) + { + dst->type = RIDE_TYPE_COMPACT_INVERTED_COASTER; + } + else + { + dst->type = RCT1::GetRideType(src->type); + } + + if (RCT1::RideTypeUsesVehicles(src->type)) { dst->subtype = _vehicleTypeToRideEntryMap[src->vehicle_type]; @@ -523,16 +556,21 @@ private: } rct_ride_entry * rideEntry = get_ride_entry(dst->subtype); - Guard::Assert(rideEntry != nullptr && rideEntry != (rct_ride_entry*)-1); + // This can happen with hacked parks + if (rideEntry == nullptr || rideEntry == (rct_ride_entry*)-1) + { + dst = nullptr; + return; + } // Ride name dst->name = 0; if (is_user_string_id(src->name)) { - const char * rideName = GetUserString(src->name); - if (rideName[0] != 0) + std::string rideName = GetUserString(src->name); + if (!rideName.empty()) { - rct_string_id rideNameStringId = user_string_allocate(4, rideName); + rct_string_id rideNameStringId = user_string_allocate(4, rideName.c_str()); if (rideNameStringId != 0) { dst->name = rideNameStringId; @@ -548,23 +586,18 @@ private: args[1] = src->name_argument_number; } - // We can't convert vehicles yet so just close the ride - dst->status = RIDE_STATUS_CLOSED; + dst->status = src->status; // Flags - if (src->lifecycle_flags & RIDE_LIFECYCLE_ON_RIDE_PHOTO) dst->lifecycle_flags |= RIDE_LIFECYCLE_ON_RIDE_PHOTO; - if (src->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE) dst->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE; - if (src->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) dst->lifecycle_flags |= RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; - if (src->lifecycle_flags & RIDE_LIFECYCLE_EVER_BEEN_OPENED) dst->lifecycle_flags |= RIDE_LIFECYCLE_EVER_BEEN_OPENED; - if (src->lifecycle_flags & RIDE_LIFECYCLE_TEST_IN_PROGRESS) dst->lifecycle_flags |= RIDE_LIFECYCLE_TEST_IN_PROGRESS; - if (src->lifecycle_flags & RIDE_LIFECYCLE_CRASHED) dst->lifecycle_flags |= RIDE_LIFECYCLE_CRASHED; - if (src->lifecycle_flags & RIDE_LIFECYCLE_TESTED) dst->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; - if (_gameVersion >= FILE_VERSION_RCT1_AA) + dst->lifecycle_flags = src->lifecycle_flags; + // These flags were not in the base game + if (_gameVersion == FILE_VERSION_RCT1) { - if (src->lifecycle_flags & RIDE_LIFECYCLE_MUSIC) dst->lifecycle_flags |= RIDE_LIFECYCLE_MUSIC; + dst->lifecycle_flags &= ~RIDE_LIFECYCLE_MUSIC; + dst->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE; + dst->lifecycle_flags &= ~RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK; } - //dst->lifecycle_flags = src->lifecycle_flags; // Station dst->overall_view = src->overall_view; @@ -575,25 +608,30 @@ private: dst->station_length[i] = src->station_length[i]; dst->station_depart[i] = src->station_light[i]; - // Use src->station_depart[i] when we import with guests and vehicles intact - dst->train_at_station[i] = 0xFF; + dst->train_at_station[i] = src->station_depart[i]; dst->entrances[i] = src->entrance[i]; dst->exits[i] = src->exit[i]; dst->queue_time[i] = src->queue_time[i]; - dst->last_peep_in_queue[i] = SPRITE_INDEX_NULL; + dst->last_peep_in_queue[i] = src->last_peep_in_queue[i]; dst->queue_length[i] = src->num_peeps_in_queue[i]; } dst->num_stations = src->num_stations; - for (sint32 i = 0; i < 32; i++) + // Vehicle links (indexes converted later) + for (sint32 i = 0; i < RCT1_MAX_VEHICLES_PER_RIDE; i++) + { + dst->vehicles[i] = src->vehicles[i]; + } + for (sint32 i = RCT1_MAX_VEHICLES_PER_RIDE; i < 32; i++) { dst->vehicles[i] = SPRITE_INDEX_NULL; } + dst->num_vehicles = src->num_trains; dst->num_cars_per_train = src->num_cars_per_train + rideEntry->zero_cars; dst->proposed_num_vehicles = src->num_trains; - dst->max_trains = 32; + dst->max_trains = src->max_trains; dst->proposed_num_cars_per_train = src->num_cars_per_train + rideEntry->zero_cars; dst->special_track_elements = src->special_track_elements; dst->num_sheltered_sections = src->num_sheltered_sections; @@ -642,46 +680,7 @@ private: dst->mode = src->operating_mode; } - // Colours - dst->colour_scheme_type = src->colour_scheme; - if (_gameVersion == FILE_VERSION_RCT1) - { - dst->track_colour_main[0] = RCT1::GetColour(src->track_primary_colour); - dst->track_colour_additional[0] = RCT1::GetColour(src->track_secondary_colour); - dst->track_colour_supports[0] = RCT1::GetColour(src->track_support_colour); - - // Balloons were always blue in the original RCT. - if (src->type == RCT1_RIDE_TYPE_BALLOON_STALL) - { - dst->track_colour_main[0] = COLOUR_LIGHT_BLUE; - } - } - else - { - for (sint32 i = 0; i < 4; i++) - { - dst->track_colour_main[i] = RCT1::GetColour(src->track_colour_main[i]); - dst->track_colour_additional[i] = RCT1::GetColour(src->track_colour_additional[i]); - dst->track_colour_supports[i] = RCT1::GetColour(src->track_colour_supports[i]); - } - // Entrance styles were introduced with AA. They correspond directly with those in RCT2. - dst->entrance_style = src->entrance_style; - } - - if (_gameVersion < FILE_VERSION_RCT1_LL && dst->type == RIDE_TYPE_MERRY_GO_ROUND) - { - // The merry-go-round in pre-LL versions was always yellow with red - dst->vehicle_colours[0].body_colour = COLOUR_YELLOW; - dst->vehicle_colours[0].trim_colour = COLOUR_BRIGHT_RED; - } - else - { - for (sint32 i = 0; i < 12; i++) - { - dst->vehicle_colours[i].body_colour = RCT1::GetColour(src->vehicle_colours[i].body); - dst->vehicle_colours[i].trim_colour = RCT1::GetColour(src->vehicle_colours[i].trim); - } - } + SetRideColourScheme(dst, src); // Maintenance dst->build_date = src->build_date; @@ -690,6 +689,13 @@ private: dst->reliability = src->reliability; dst->unreliability_factor = src->unreliability_factor; dst->breakdown_reason = src->breakdown_reason; + dst->mechanic_status = src->mechanic_status; + dst->mechanic = src->mechanic; + dst->breakdown_reason = src->breakdown_reason; + dst->breakdown_reason_pending = src->breakdown_reason_pending; + dst->inspection_station = src->inspection_station; + //dst->broken_car? + //dst->broken_vehicle? // Measurement data dst->excitement = src->excitement; @@ -729,6 +735,11 @@ private: dst->current_test_segment = src->current_test_segment; dst->current_test_station = 0xFF; dst->average_speed_test_timeout = src->average_speed_test_timeout; + dst->slide_in_use = src->slide_in_use; + dst->slide_peep_t_shirt_colour = RCT1::GetColour(src->slide_peep_t_shirt_colour); + dst->spiral_slide_progress = src->spiral_slide_progress; + // Doubles as slide_peep + dst->maze_tiles = src->maze_tiles; // Finance / customers dst->upkeep_cost = src->upkeep_cost; @@ -751,6 +762,109 @@ private: dst->music_tune_id = 255; } + void SetRideColourScheme(rct_ride * dst, rct1_ride * src) + { + // Colours + dst->colour_scheme_type = src->colour_scheme; + if (_gameVersion == FILE_VERSION_RCT1) + { + dst->track_colour_main[0] = RCT1::GetColour(src->track_primary_colour); + dst->track_colour_additional[0] = RCT1::GetColour(src->track_secondary_colour); + dst->track_colour_supports[0] = RCT1::GetColour(src->track_support_colour); + + // Balloons were always blue in the original RCT. + if (src->type == RCT1_RIDE_TYPE_BALLOON_STALL) + { + dst->track_colour_main[0] = COLOUR_LIGHT_BLUE; + } + else if (src->type == RCT1_RIDE_TYPE_RIVER_RAPIDS) + { + dst->track_colour_main[0] = COLOUR_WHITE; + } + } + else + { + for (int i = 0; i < 4; i++) + { + dst->track_colour_main[i] = RCT1::GetColour(src->track_colour_main[i]); + dst->track_colour_additional[i] = RCT1::GetColour(src->track_colour_additional[i]); + dst->track_colour_supports[i] = RCT1::GetColour(src->track_colour_supports[i]); + } + // Entrance styles were introduced with AA. They correspond directly with those in RCT2. + dst->entrance_style = src->entrance_style; + } + + if (_gameVersion < FILE_VERSION_RCT1_LL && dst->type == RIDE_TYPE_MERRY_GO_ROUND) + { + // The merry-go-round in pre-LL versions was always yellow with red + dst->vehicle_colours[0].body_colour = COLOUR_YELLOW; + dst->vehicle_colours[0].trim_colour = COLOUR_BRIGHT_RED; + } + else + { + for (int i = 0; i < RCT1_MAX_TRAINS_PER_RIDE; i++) + { + // RCT1 had no third colour + RCT1::RCT1VehicleColourSchemeCopyDescriptor colourSchemeCopyDescriptor = RCT1::GetColourSchemeCopyDescriptor(src->vehicle_type); + if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_1) + { + dst->vehicle_colours[i].body_colour = RCT1::GetColour(src->vehicle_colours[i].body); + } + else if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_2) + { + dst->vehicle_colours[i].body_colour = RCT1::GetColour(src->vehicle_colours[i].trim); + } + else + { + dst->vehicle_colours[i].body_colour = colourSchemeCopyDescriptor.colour1; + } + + if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_1) + { + dst->vehicle_colours[i].trim_colour = RCT1::GetColour(src->vehicle_colours[i].body); + } + else if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_2) + { + dst->vehicle_colours[i].trim_colour = RCT1::GetColour(src->vehicle_colours[i].trim); + } + else + { + dst->vehicle_colours[i].trim_colour = colourSchemeCopyDescriptor.colour2; + } + + if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_1) + { + dst->vehicle_colours_extended[i] = RCT1::GetColour(src->vehicle_colours[i].body); + } + else if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_2) + { + dst->vehicle_colours_extended[i] = RCT1::GetColour(src->vehicle_colours[i].trim); + } + else + { + dst->vehicle_colours_extended[i] = colourSchemeCopyDescriptor.colour3; + } + } + } + } + + void FixRideVehicleLinks(const uint16 * spriteIndexMap) + { + uint8 i; + rct_ride * ride; + FOR_ALL_RIDES(i, ride) + { + for (uint8 j = 0; j < Util::CountOf(ride->vehicles); j++) + { + uint16 originalIndex = ride->vehicles[j]; + if (originalIndex != SPRITE_INDEX_NULL) + { + ride->vehicles[j] = spriteIndexMap[originalIndex]; + } + } + } + } + void FixNumPeepsInQueue() { sint32 i; @@ -776,37 +890,265 @@ private: void ImportRideMeasurement(rct_ride_measurement * dst, rct_ride_measurement * src) { - // Not yet supported - // *dst = *src; - // for (sint32 i = 0; i < RIDE_MEASUREMENT_MAX_ITEMS; i++) - // { - // dst->altitude[i] /= 2; - // } + *dst = *src; + for (sint32 i = 0; i < RIDE_MEASUREMENT_MAX_ITEMS; i++) + { + dst->velocity[i] /= 2; + dst->altitude[i] /= 2; + dst->vertical[i] /= 2; + dst->lateral[i] /= 2; + } } void ImportSprites() { + ImportVehicles(); ImportPeeps(); ImportLitter(); ImportMiscSprites(); } - void ImportPeeps() + void ImportVehicles() { - for (size_t i = 0; i < RCT1_MAX_SPRITES; i++) + std::vector vehicles; + uint16 spriteIndexMap[RCT1_MAX_SPRITES]; + for (int i = 0; i < RCT1_MAX_SPRITES; i++) { - if (_s4.sprites[i].unknown.sprite_identifier == SPRITE_IDENTIFIER_PEEP) + spriteIndexMap[i] = SPRITE_INDEX_NULL; + if (_s4.sprites[i].unknown.sprite_identifier == SPRITE_IDENTIFIER_VEHICLE) { - rct1_peep *srcPeep = &_s4.sprites[i].peep; - if (srcPeep->x != MAP_LOCATION_NULL || srcPeep->state == PEEP_STATE_ON_RIDE) + rct1_vehicle * srcVehicle = &_s4.sprites[i].vehicle; + if (srcVehicle->x != SPRITE_LOCATION_NULL) { - rct_peep *peep = (rct_peep*)create_sprite(SPRITE_IDENTIFIER_PEEP); - move_sprite_to_list((rct_sprite*)peep, SPRITE_LIST_PEEP * 2); + rct_vehicle * vehicle = (rct_vehicle *)create_sprite(SPRITE_IDENTIFIER_VEHICLE); + spriteIndexMap[i] = vehicle->sprite_index; + vehicles.push_back(vehicle); - ImportPeep(peep, srcPeep); + ImportVehicle(vehicle, srcVehicle); + + // If vehicle is the first car on a train add to train list + if (!vehicle->is_child) + { + move_sprite_to_list((rct_sprite *)vehicle, SPRITE_LIST_TRAIN * 2); + } } } } + for (auto vehicle : vehicles) + { + FixVehicleLinks(vehicle, spriteIndexMap); + } + FixRideVehicleLinks(spriteIndexMap); + } + + void ImportVehicle(rct_vehicle * dst, rct1_vehicle * src) + { + rct_ride * ride = get_ride(src->ride); + uint8 vehicleEntryIndex = RCT1::GetVehicleSubEntryIndex(src->vehicle_type); + + dst->sprite_identifier = SPRITE_IDENTIFIER_VEHICLE; + dst->ride = src->ride; + dst->ride_subtype = ride->subtype; + + dst->vehicle_type = vehicleEntryIndex; + dst->is_child = src->is_child; + dst->var_44 = src->var_44; + dst->remaining_distance = src->remaining_distance; + + // Properties from vehicle entry + dst->sprite_width = src->sprite_width; + dst->sprite_height_negative = src->sprite_height_negative; + dst->sprite_height_positive = src->sprite_height_positive; + dst->sprite_direction = src->sprite_direction; + + dst->sprite_left = src->sprite_left; + dst->sprite_top = src->sprite_top; + dst->sprite_right = src->sprite_right; + dst->sprite_bottom = src->sprite_bottom; + + dst->friction = src->friction; + dst->num_seats = src->num_seats; + dst->speed = src->speed; + dst->powered_acceleration = src->powered_acceleration; + dst->brake_speed = src->brake_speed; + + dst->velocity = src->velocity; + dst->acceleration = src->acceleration; + dst->var_4A = src->var_4A; + dst->swinging_car_var_0 = src->swinging_car_var_0; + dst->var_4E = src->var_4E; + dst->restraints_position = src->restraints_position; + dst->var_BA = src->var_BA; + dst->var_BF = src->var_BF; + dst->var_B6 = src->var_B6; + dst->var_B8 = src->var_B8; + dst->sound1_id = 0xFF; + dst->sound2_id = 0xFF; + dst->var_C0 = src->var_C0; + dst->var_C4 = src->var_C4; + dst->var_C5 = src->var_C5; + dst->var_C8 = src->var_C8; + dst->var_CA = src->var_CA; + dst->var_CE = src->var_CE; + dst->var_D3 = src->var_D3; + dst->scream_sound_id = 255; + dst->vehicle_sprite_type = src->vehicle_sprite_type; + dst->bank_rotation = src->bank_rotation; + + // Seat rotation was not in RCT1 + dst->target_seat_rotation = 4; + dst->seat_rotation = 4; + + // Vehicle links (indexes converted later) + dst->prev_vehicle_on_ride = src->prev_vehicle_on_ride; + dst->next_vehicle_on_ride = src->next_vehicle_on_ride; + dst->next_vehicle_on_train = src->next_vehicle_on_train; + + // Guests (indexes converted later) + for (int i = 0; i < 32; i++) + { + uint16 spriteIndex = src->peep[i]; + dst->peep[i] = spriteIndex; + if (spriteIndex != SPRITE_INDEX_NULL) + { + dst->peep_tshirt_colours[i] = RCT1::GetColour(src->peep_tshirt_colours[i]); + } + } + + dst->var_CD = src->var_CD; + dst->track_x = src->track_x; + dst->track_y = src->track_y; + dst->track_z = src->track_z; + dst->current_station = src->current_station; + dst->track_type = src->track_type; + dst->track_progress = src->track_progress; + dst->vertical_drop_countdown = src->vertical_drop_countdown; + dst->status = src->status; + dst->sub_state = src->sub_state; + dst->update_flags = src->update_flags; + + SetVehicleColours(dst, src); + + dst->mini_golf_current_animation = src->mini_golf_current_animation; + dst->mini_golf_flags = src->mini_golf_flags; + + sprite_move(src->x, src->y, src->z, (rct_sprite *)dst); + invalidate_sprite_2((rct_sprite *)dst); + + dst->num_peeps = src->num_peeps; + dst->next_free_seat = src->next_free_seat; + } + + void SetVehicleColours(rct_vehicle * dst, rct1_vehicle * src) + { + rct1_ride * srcRide = &_s4.rides[src->ride]; + uint8 vehicleTypeIndex = srcRide->vehicle_type; + RCT1::RCT1VehicleColourSchemeCopyDescriptor colourSchemeCopyDescriptor = RCT1::GetColourSchemeCopyDescriptor(vehicleTypeIndex); + + // RCT1 had no third colour + if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_1) + { + dst->colours.body_colour = RCT1::GetColour(src->colours.body_colour); + } + else if (colourSchemeCopyDescriptor.colour1 == COPY_COLOUR_2) + { + dst->colours.body_colour = RCT1::GetColour(src->colours.trim_colour); + } + else + { + dst->colours.body_colour = colourSchemeCopyDescriptor.colour1; + } + + if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_1) + { + dst->colours.trim_colour = RCT1::GetColour(src->colours.body_colour); + } + else if (colourSchemeCopyDescriptor.colour2 == COPY_COLOUR_2) + { + dst->colours.trim_colour = RCT1::GetColour(src->colours.trim_colour); + } + else + { + dst->colours.trim_colour = colourSchemeCopyDescriptor.colour2; + } + + if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_1) + { + dst->colours_extended = RCT1::GetColour(src->colours.body_colour); + } + else if (colourSchemeCopyDescriptor.colour3 == COPY_COLOUR_2) + { + dst->colours_extended = RCT1::GetColour(src->colours.trim_colour); + } + else + { + dst->colours_extended = colourSchemeCopyDescriptor.colour3; + } + } + + void FixVehicleLinks(rct_vehicle * vehicle, const uint16 * spriteIndexMap) + { + if (vehicle->prev_vehicle_on_ride != SPRITE_INDEX_NULL) + { + vehicle->prev_vehicle_on_ride = spriteIndexMap[vehicle->prev_vehicle_on_ride]; + } + if (vehicle->next_vehicle_on_ride != SPRITE_INDEX_NULL) + { + vehicle->next_vehicle_on_ride = spriteIndexMap[vehicle->next_vehicle_on_ride]; + } + if (vehicle->next_vehicle_on_train != SPRITE_INDEX_NULL) + { + vehicle->next_vehicle_on_train = spriteIndexMap[vehicle->next_vehicle_on_train]; + } + } + + void FixVehiclePeepLinks(rct_vehicle * vehicle, const uint16 * spriteIndexMap) + { + for (int i = 0; i < 32; i++) + { + vehicle->peep[i] = MapSpriteIndex(vehicle->peep[i], spriteIndexMap); + } + } + + void ImportPeeps() + { + uint16 spriteIndexMap[RCT1_MAX_SPRITES]; + for (size_t i = 0; i < RCT1_MAX_SPRITES; i++) + { + spriteIndexMap[i] = SPRITE_INDEX_NULL; + if (_s4.sprites[i].unknown.sprite_identifier == SPRITE_IDENTIFIER_PEEP) + { + rct1_peep * srcPeep = &_s4.sprites[i].peep; + rct_peep * peep = (rct_peep*)create_sprite(SPRITE_IDENTIFIER_PEEP); + move_sprite_to_list((rct_sprite*)peep, SPRITE_LIST_PEEP * 2); + spriteIndexMap[i] = peep->sprite_index; + + ImportPeep(peep, srcPeep); + } + } + for (size_t i = 0; i < MAX_SPRITES; i++) + { + rct_sprite * sprite = get_sprite(i); + if (sprite->unknown.sprite_identifier == SPRITE_IDENTIFIER_VEHICLE) + { + rct_vehicle * vehicle = (rct_vehicle *)sprite; + FixVehiclePeepLinks(vehicle, spriteIndexMap); + } + } + + int i; + rct_ride *ride; + rct_peep *peep; + + FOR_ALL_RIDES(i, ride) + { + FixRidePeepLinks(ride, spriteIndexMap); + } + + FOR_ALL_GUESTS(i, peep) + { + FixPeepNextInQueue(peep, spriteIndexMap); + } } void ImportPeep(rct_peep * dst, rct1_peep * src) @@ -836,10 +1178,10 @@ private: dst->name_string_idx = src->name_string_idx; if (is_user_string_id(src->name_string_idx)) { - const char * peepName = GetUserString(src->name_string_idx); - if (peepName[0] != 0) + std::string peepName = GetUserString(src->name_string_idx); + if (!peepName.empty()) { - rct_string_id peepNameStringId = user_string_allocate(4, peepName); + rct_string_id peepNameStringId = user_string_allocate(4, peepName.c_str()); if (peepNameStringId != 0) { dst->name_string_idx = peepNameStringId; @@ -849,18 +1191,16 @@ private: dst->outside_of_park = src->outside_of_park; - // We cannot yet import peeps that are on a ride properly. Move these to a safe place. - switch(src->state) { - case PEEP_STATE_ON_RIDE: - case PEEP_STATE_QUEUING_FRONT: - case PEEP_STATE_LEAVING_RIDE: - case PEEP_STATE_ENTERING_RIDE: - dst->state = PEEP_STATE_FALLING; - peep_autoposition(dst); - break; - default: - dst->state = src->state; - } + dst->state = src->state; + dst->sub_state = src->sub_state; + dst->next_x = src->next_x; + dst->next_y = src->next_y; + dst->next_z = src->next_z / 2; + dst->next_var_29 = src->next_var_29; + dst->var_37 = src->var_37; + dst->var_42 = src->var_42; + dst->var_73 = src->var_73; + dst->var_EF = src->var_EF; dst->type = src->type; @@ -882,6 +1222,7 @@ private: dst->destination_x = src->destination_x; dst->destination_y = src->destination_y; dst->destination_tolerence = src->destination_tolerence; + dst->direction = src->direction; dst->energy = src->energy; dst->energy_growth_rate = src->energy_growth_rate; @@ -909,7 +1250,7 @@ private: dst->time_on_ride = src->time_on_ride; dst->days_in_queue = src->days_in_queue; - dst->interactionRideIndex = 0xFF; + dst->interactionRideIndex = src->interactionRideIndex; dst->id = src->id; dst->cash_in_pocket = src->cash_in_pocket; @@ -948,7 +1289,8 @@ private: dst->thoughts[i] = src->thoughts[i]; } - dst->previous_ride = 0xFF; + dst->previous_ride = src->previous_ride; + dst->previous_ride_time_out = src->previous_ride_time_out; dst->var_C4 = 0; dst->guest_heading_to_ride_id = src->guest_heading_to_ride_id; @@ -978,27 +1320,25 @@ private: void FixRidePeepLinks(rct_ride * ride, const uint16 * spriteIndexMap) { - for (int i = 0; i < RCT1_MAX_STATIONS; i++) + for (sint32 i = 0; i < RCT1_MAX_STATIONS; i++) { - uint16 originalSpriteIndex = ride->last_peep_in_queue[i]; - if (originalSpriteIndex != SPRITE_INDEX_NULL) - { - ride->last_peep_in_queue[i] = spriteIndexMap[originalSpriteIndex]; - } + ride->last_peep_in_queue[i] = MapSpriteIndex(ride->last_peep_in_queue[i], spriteIndexMap); + } + ride->mechanic = MapSpriteIndex(ride->mechanic, spriteIndexMap); + if (ride->type == RIDE_TYPE_SPIRAL_SLIDE) + { + ride->slide_peep = MapSpriteIndex(ride->slide_peep, spriteIndexMap); } } void FixPeepNextInQueue(rct_peep * peep, const uint16 * spriteIndexMap) { - uint16 originalSpriteIndex = peep->next_in_queue; - if (originalSpriteIndex != SPRITE_INDEX_NULL) - { - peep->next_in_queue = spriteIndexMap[originalSpriteIndex]; - } + peep->next_in_queue = MapSpriteIndex(peep->next_in_queue, spriteIndexMap); } void ImportLitter() { + for (size_t i = 0; i < RCT1_MAX_SPRITES; i++) { if (_s4.sprites[i].unknown.sprite_identifier == SPRITE_IDENTIFIER_LITTER) @@ -1038,14 +1378,13 @@ private: dst->sprite_identifier = src->sprite_identifier; dst->misc_identifier = src->misc_identifier; dst->flags = src->flags; - dst->x = src->x; - dst->y = src->y; - dst->z = src->z; dst->sprite_direction = src->sprite_direction; dst->sprite_width = src->sprite_width; dst->sprite_height_negative = src->sprite_height_negative; dst->sprite_height_positive = src->sprite_height_positive; + sprite_move(src->x, src->y, src->z, (rct_sprite*)dst); + switch (src->misc_identifier) { case SPRITE_MISC_STEAM_PARTICLE: ImportSteamParticle((rct_steam_particle *) dst, (rct_steam_particle *) src); @@ -1119,6 +1458,23 @@ private: dst->state = src->state; } + uint16 MapSpriteIndex(uint16 originalSpriteIndex, const uint16 * spriteIndexMap) + { + uint16 newSpriteIndex = SPRITE_INDEX_NULL; + if (originalSpriteIndex != SPRITE_INDEX_NULL) + { + if (originalSpriteIndex >= RCT1_MAX_SPRITES) + { + log_warning("Incorrect sprite index: %d", originalSpriteIndex); + } + else + { + newSpriteIndex = spriteIndexMap[originalSpriteIndex]; + } + } + return newSpriteIndex; + } + void ImportPeepSpawns() { for (size_t i = 0; i < 2; i++) @@ -1199,7 +1555,7 @@ private: "BN9 " })); LoadObjects(OBJECT_TYPE_PARK_ENTRANCE, std::vector({ "PKENT1 " })); - LoadObjects(OBJECT_TYPE_WATER, std::vector({ "WTRCYAN " })); + LoadObjects(OBJECT_TYPE_WATER, _waterEntry); } void LoadObjects(uint8 objectType, const EntryList &entries) @@ -1232,10 +1588,10 @@ private: void ImportMapElements() { - memcpy(gMapElements, _s4.map_elements, RCT1_MAX_MAP_ELEMENTS * sizeof(rct_map_element)); + Memory::Copy(gMapElements, _s4.map_elements, RCT1_MAX_MAP_ELEMENTS * sizeof(rct_map_element)); ClearExtraTileEntries(); - FixColours(); - FixZ(); + FixSceneryColours(); + FixMapElementZ(); FixPaths(); FixWalls(); FixBanners(); @@ -1399,17 +1755,17 @@ private: void ImportParkName() { - const char * parkName = _s4.scenario_name; + std::string parkName = std::string(_s4.scenario_name); if (is_user_string_id((rct_string_id)_s4.park_name_string_index)) { - const char * userString = GetUserString(_s4.park_name_string_index); - if (userString[0] != '\0') + std::string userString = GetUserString(_s4.park_name_string_index); + if (!userString.empty()) { parkName = userString; } } - rct_string_id stringId = user_string_allocate(4, parkName); + rct_string_id stringId = user_string_allocate(4, parkName.c_str()); if (stringId != 0) { gParkName = stringId; @@ -1460,7 +1816,7 @@ private: dst->Ticks = src->Ticks; dst->MonthYear = src->MonthYear; dst->Day = src->Day; - memcpy(dst->Text, src->Text, sizeof(src->Text)); + Memory::Copy(dst->Text, src->Text, sizeof(src->Text)); } // Initial guest status @@ -1506,10 +1862,10 @@ private: void ImportScenarioNameDetails() { - String::Set(gS6Info.name, sizeof(gS6Info.name), _s4.scenario_name); - String::Set(gS6Info.details, sizeof(gS6Info.details), ""); + std::string name = String::ToStd(_s4.scenario_name); + std::string details; - sint32 scNumber = GetSCNumber(); + sint32 scNumber = _s4.scenario_slot_index; if (scNumber != -1) { source_desc sourceDesc; @@ -1520,15 +1876,20 @@ private: { if (localisedStringIds[0] != STR_NONE) { - String::Set(gS6Info.name, sizeof(gS6Info.name), language_get_string(localisedStringIds[0])); + name = String::ToStd(language_get_string(localisedStringIds[0])); } if (localisedStringIds[2] != STR_NONE) { - String::Set(gS6Info.details, sizeof(gS6Info.details), language_get_string(localisedStringIds[2])); + details = String::ToStd(language_get_string(localisedStringIds[2])); } } } } + + String::Set(gS6Info.name, sizeof(gS6Info.name), name.c_str()); + String::Set(gS6Info.details, sizeof(gS6Info.details), details.c_str()); + String::Set(gScenarioName, sizeof(gScenarioName), name.c_str()); + String::Set(gScenarioDetails, sizeof(gScenarioDetails), details.c_str()); } void ImportScenarioObjective() @@ -1566,17 +1927,17 @@ private: rct_map_element * * tilePointer = gMapElementTilePointers; // 128 rows of map data from RCT1 map - for (sint32 x = 0; x < 128; x++) + for (sint32 x = 0; x < RCT1_MAX_MAP_SIZE; x++) { // Assign the first half of this row - for (sint32 y = 0; y < 128; y++) + for (sint32 y = 0; y < RCT1_MAX_MAP_SIZE; y++) { *tilePointer++ = mapElement; do { } while (!map_element_is_last_for_tile(mapElement++)); } // Fill the rest of the row with blank tiles - for (sint32 y = 0; y < 128; y++) + for (sint32 y = 0; y < RCT1_MAX_MAP_SIZE; y++) { nextFreeMapElement->type = MAP_ELEMENT_TYPE_SURFACE; nextFreeMapElement->flags = MAP_ELEMENT_FLAG_LAST_TILE; @@ -1607,35 +1968,9 @@ private: gNextFreeMapElement = nextFreeMapElement; } - void FixColours() + void FixSceneryColours() { colour_t colour; - - // The following code would be worth doing if we were able to import sprites - // for (size_t i = 0; i < MAX_SPRITES; i++) - // { - // rct_unk_sprite * sprite = &(g_sprite_list[i].unknown); - // switch (sprite->sprite_identifier) { - // case SPRITE_IDENTIFIER_PEEP: - // { - // rct_peep * peep = (rct_peep*)sprite; - // peep->tshirt_colour = RCT1ColourConversionTable[peep->tshirt_colour]; - // peep->trousers_colour = RCT1ColourConversionTable[peep->trousers_colour]; - // peep->balloon_colour = RCT1ColourConversionTable[peep->balloon_colour]; - // peep->umbrella_colour = RCT1ColourConversionTable[peep->umbrella_colour]; - // peep->hat_colour = RCT1ColourConversionTable[peep->hat_colour]; - // break; - // } - // case SPRITE_IDENTIFIER_MISC: - // { - // rct_balloon * balloon = (rct_balloon*)sprite; - // balloon->colour = RCT1ColourConversionTable[balloon->colour]; - // balloon->var_2D = RCT1ColourConversionTable[balloon->var_2D]; - // break; - // } - // } - // } - rct_map_element * mapElement = gMapElements; while (mapElement < gNextFreeMapElement) { @@ -1683,7 +2018,7 @@ private: } } - void FixZ() + void FixMapElementZ() { rct_map_element * mapElement = gMapElements; while (mapElement < gNextFreeMapElement) @@ -1757,9 +2092,9 @@ private: void FixWalls() { - for (sint32 x = 0; x < 128; x++) + for (sint32 x = 0; x < RCT1_MAX_MAP_SIZE; x++) { - for (sint32 y = 0; y < 128; y++) + for (sint32 y = 0; y < RCT1_MAX_MAP_SIZE; y++) { rct_map_element * mapElement = map_get_first_element_at(x, y); do @@ -1836,9 +2171,9 @@ private: void FixBanners() { - for (sint32 x = 0; x < 128; x++) + for (sint32 x = 0; x < RCT1_MAX_MAP_SIZE; x++) { - for (sint32 y = 0; y < 128; y++) + for (sint32 y = 0; y < RCT1_MAX_MAP_SIZE; y++) { rct_map_element * mapElement = map_get_first_element_at(x, y); do @@ -1864,10 +2199,10 @@ private: dst->string_idx = STR_DEFAULT_SIGN; if (is_user_string_id(src->string_idx)) { - const char * bannerText = GetUserString(src->string_idx); - if (!String::IsNullOrEmpty(bannerText)) + std::string bannerText = GetUserString(src->string_idx); + if (!bannerText.empty()) { - rct_string_id bannerTextStringId = user_string_allocate(128, bannerText); + rct_string_id bannerTextStringId = user_string_allocate(128, bannerText.c_str()); if (bannerTextStringId != 0) { dst->string_idx = bannerTextStringId; @@ -1950,6 +2285,7 @@ private: case OBJECT_TYPE_PATHS: return &_pathEntries; case OBJECT_TYPE_PATH_BITS: return &_pathAdditionEntries; case OBJECT_TYPE_SCENERY_SETS: return &_sceneryGroupEntries; + case OBJECT_TYPE_WATER: return &_waterEntry; } return nullptr; } @@ -1969,39 +2305,72 @@ private: } } - sint32 GetSCNumber() + std::string GetUserString(rct_string_id stringId) { - const utf8 * fileName = Path::GetFileName(_s4Path); - if (tolower(fileName[0]) == 's' && tolower(fileName[1]) == 'c') { - constexpr size_t maxDigits = 7; - utf8 digitBuffer[maxDigits + 1]; - utf8 * dst = digitBuffer; - const utf8 * src = fileName + 2; - for (size_t i = 0; i < maxDigits && *src != '.'; i++) - { - *dst++ = *src++; - } - *dst++ = 0; + utf8 buffer[128] = { 0 }; + const char * originalString = _s4.string_table[(stringId - 0x8000) % 1024]; + rct2_to_utf8(buffer, originalString); + return std::string(buffer); + } - if (digitBuffer[0] == '0' && digitBuffer[1] == '\0') - { - return 0; - } - else - { - sint32 digits = atoi(digitBuffer); - return digits == 0 ? -1 : digits; - } - } - else - { - return -1; + void FixLandOwnership() + { + rct_map_element * currentElement; + + switch(_s4.scenario_slot_index) { + case SC_KATIES_DREAMLAND: + currentElement = map_get_surface_element_at(74, 70); + currentElement->properties.surface.ownership |= OWNERSHIP_AVAILABLE; + currentElement = map_get_surface_element_at(75, 70); + currentElement->properties.surface.ownership |= OWNERSHIP_AVAILABLE; + currentElement = map_get_surface_element_at(76, 70); + currentElement->properties.surface.ownership |= OWNERSHIP_AVAILABLE; + currentElement = map_get_surface_element_at(77, 73); + currentElement->properties.surface.ownership |= OWNERSHIP_AVAILABLE; + currentElement = map_get_surface_element_at(80, 77); + currentElement->properties.surface.ownership |= OWNERSHIP_AVAILABLE; + break; } } - const char * GetUserString(rct_string_id stringId) + /** + * Counts the block sections. The reason this iterates over the map is to avoid getting into infinite loops, + * which can happen with hacked parks. + */ + void CountBlockSections() { - return _s4.string_table[(stringId - 0x8000) % 1024]; + for (sint32 x = 0; x < RCT1_MAX_MAP_SIZE; x++) + { + for (sint32 y = 0; y < RCT1_MAX_MAP_SIZE; y++) + { + rct_map_element * mapElement = map_get_first_element_at(x, y); + do + { + if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_TRACK) + { + // Lift hill tops are the only pieces present in RCT1 that can count as a block brake. + if (!track_element_is_lift_hill(mapElement)) + continue; + + uint8 trackType = mapElement->properties.track.type; + switch (trackType) { + case TRACK_ELEM_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_60_DEG_UP_TO_FLAT: + case TRACK_ELEM_DIAG_25_DEG_UP_TO_FLAT: + case TRACK_ELEM_DIAG_60_DEG_UP_TO_FLAT: + break; + default: + continue; + } + + uint8 rideIndex = mapElement->properties.track.ride_index; + rct_ride * ride = get_ride(rideIndex); + ride->num_block_brakes++; + } + } + while (!map_element_is_last_for_tile(mapElement++)); + } + } } }; diff --git a/src/openrct2/rct1/Tables.h b/src/openrct2/rct1/Tables.h index bbe18cae15..f5963f02e1 100644 --- a/src/openrct2/rct1/Tables.h +++ b/src/openrct2/rct1/Tables.h @@ -24,15 +24,21 @@ namespace RCT1 { + struct RCT1VehicleColourSchemeCopyDescriptor { + sint8 colour1, colour2, colour3; + }; + colour_t GetColour(colour_t colour); uint8 GetPeepSpriteType(uint8 rct1SpriteType); uint8 GetTerrain(uint8 terrain); uint8 GetTerrainEdge(uint8 terrainEdge); uint8 GetRideType(uint8 rideType); + RCT1VehicleColourSchemeCopyDescriptor GetColourSchemeCopyDescriptor(uint8 vehicleType); bool RideTypeUsesVehicles(uint8 rideType); bool PathIsQueue(uint8 pathType); uint8 NormalisePathAddition(uint8 pathAdditionType); + uint8 GetVehicleSubEntryIndex(uint8 vehicleSubEntry); const char * GetRideTypeObject(uint8 rideType); const char * GetVehicleObject(uint8 vehicleType); @@ -42,6 +48,7 @@ namespace RCT1 const char * GetPathObject(uint8 pathType); const char * GetPathAddtionObject(uint8 pathAdditionType); const char * GetSceneryGroupObject(uint8 sceneryGroupType); + const char * GetWaterObject(uint8 waterType); const std::vector GetSceneryObjects(uint8 sceneryType); diff --git a/src/openrct2/rct1/tables.cpp b/src/openrct2/rct1/tables.cpp index 98234776fe..4e25f0e716 100644 --- a/src/openrct2/rct1/tables.cpp +++ b/src/openrct2/rct1/tables.cpp @@ -67,7 +67,11 @@ namespace RCT1 COLOUR_BRIGHT_YELLOW, COLOUR_ICY_BLUE }; - Guard::ArgumentInRange(colour, 0, Util::CountOf(map), "Unsupported RCT1 colour."); + if (colour < 0 || colour >= Util::CountOf(map)) + { + log_warning("Unsupported RCT1 colour."); + return COLOUR_BLACK; + } return map[colour]; } @@ -75,39 +79,47 @@ namespace RCT1 { static const uint8 map[] = { - PEEP_SPRITE_TYPE_NORMAL, - PEEP_SPRITE_TYPE_HANDYMAN, - PEEP_SPRITE_TYPE_MECHANIC, - PEEP_SPRITE_TYPE_SECURITY, - PEEP_SPRITE_TYPE_ENTERTAINER_PANDA, - PEEP_SPRITE_TYPE_ENTERTAINER_TIGER, - PEEP_SPRITE_TYPE_ENTERTAINER_ELEPHANT, - PEEP_SPRITE_TYPE_ENTERTAINER_ROMAN, - PEEP_SPRITE_TYPE_ENTERTAINER_GORILLA, - PEEP_SPRITE_TYPE_ENTERTAINER_SNOWMAN, - PEEP_SPRITE_TYPE_ENTERTAINER_KNIGHT, - PEEP_SPRITE_TYPE_ENTERTAINER_ASTRONAUT, - PEEP_SPRITE_TYPE_NORMAL, // Not used - PEEP_SPRITE_TYPE_NORMAL, // Not used - PEEP_SPRITE_TYPE_NORMAL, // Not used - PEEP_SPRITE_TYPE_NORMAL, // Not used - PEEP_SPRITE_TYPE_BALLOON, - PEEP_SPRITE_TYPE_CANDYFLOSS, - PEEP_SPRITE_TYPE_UMBRELLA, - PEEP_SPRITE_TYPE_PIZZA, - PEEP_SPRITE_TYPE_SECURITY_ALT, - PEEP_SPRITE_TYPE_POPCORN, - PEEP_SPRITE_TYPE_ARMS_CROSSED, - PEEP_SPRITE_TYPE_HEAD_DOWN, - PEEP_SPRITE_TYPE_NAUSEOUS, - PEEP_SPRITE_TYPE_VERY_NAUSEOUS, - PEEP_SPRITE_TYPE_REQUIRE_BATHROOM, - PEEP_SPRITE_TYPE_HAT, - PEEP_SPRITE_TYPE_BURGER, - PEEP_SPRITE_TYPE_TENTACLE, - PEEP_SPRITE_TYPE_TOFFEE_APPLE + PEEP_SPRITE_TYPE_NORMAL, // 0x00 + PEEP_SPRITE_TYPE_HANDYMAN, // 0x01 + PEEP_SPRITE_TYPE_MECHANIC, // 0x02 + PEEP_SPRITE_TYPE_SECURITY, // 0x03 + PEEP_SPRITE_TYPE_ENTERTAINER_PANDA, // 0x04 + PEEP_SPRITE_TYPE_ENTERTAINER_TIGER, // 0x05 + PEEP_SPRITE_TYPE_ENTERTAINER_ELEPHANT, // 0x06 + PEEP_SPRITE_TYPE_ENTERTAINER_ROMAN, // 0x07 + PEEP_SPRITE_TYPE_ENTERTAINER_GORILLA, // 0x08 + PEEP_SPRITE_TYPE_ENTERTAINER_SNOWMAN, // 0x09 + PEEP_SPRITE_TYPE_ENTERTAINER_KNIGHT, // 0x0A + PEEP_SPRITE_TYPE_ENTERTAINER_ASTRONAUT, // 0x0B + PEEP_SPRITE_TYPE_ICE_CREAM, // 0x0C + PEEP_SPRITE_TYPE_FRIES, // 0x0D + PEEP_SPRITE_TYPE_BURGER, // 0x0E + PEEP_SPRITE_TYPE_DRINK, // 0x0F + PEEP_SPRITE_TYPE_BALLOON, // 0x10 + PEEP_SPRITE_TYPE_CANDYFLOSS, // 0x11 + PEEP_SPRITE_TYPE_UMBRELLA, // 0x12 + PEEP_SPRITE_TYPE_PIZZA, // 0x13 + PEEP_SPRITE_TYPE_SECURITY_ALT, // 0x14 + PEEP_SPRITE_TYPE_POPCORN, // 0x15 + PEEP_SPRITE_TYPE_ARMS_CROSSED, // 0x16 + PEEP_SPRITE_TYPE_HEAD_DOWN, // 0x17 + PEEP_SPRITE_TYPE_NAUSEOUS, // 0x18 + PEEP_SPRITE_TYPE_VERY_NAUSEOUS, // 0x19 + PEEP_SPRITE_TYPE_REQUIRE_BATHROOM, // 0x1A + PEEP_SPRITE_TYPE_HAT, // 0x1B + PEEP_SPRITE_TYPE_HOT_DOG, // 0x1C + PEEP_SPRITE_TYPE_TENTACLE, // 0x1D + PEEP_SPRITE_TYPE_TOFFEE_APPLE, // 0x1E + PEEP_SPRITE_TYPE_DONUT, // 0x1F + PEEP_SPRITE_TYPE_COFFEE, // 0x20 + PEEP_SPRITE_TYPE_CHICKEN, // 0x21 + PEEP_SPRITE_TYPE_LEMONADE, // 0x22 }; - Guard::ArgumentInRange(rct1SpriteType, 0, Util::CountOf(map), "Unsupported RCT1 sprite type."); + if (rct1SpriteType < 0 || rct1SpriteType > Util::CountOf(map)) + { + log_warning("Unsupported RCT1 peep sprite type: %d.", rct1SpriteType); + return PEEP_SPRITE_TYPE_NORMAL; + } return map[rct1SpriteType]; } @@ -256,6 +268,105 @@ namespace RCT1 return map[rideType]; } + RCT1VehicleColourSchemeCopyDescriptor GetColourSchemeCopyDescriptor(uint8 vehicleType) + { + static RCT1VehicleColourSchemeCopyDescriptor map[89] = + { + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_STEEL_ROLLER_COASTER_TRAIN = 0, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_STEEL_ROLLER_COASTER_TRAIN_BACKWARDS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_WOODEN_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_INVERTED_COASTER_TRAIN, // Not in RCT2 + { COPY_COLOUR_1, COPY_COLOUR_2, COPY_COLOUR_2 }, // RCT1_VEHICLE_TYPE_SUSPENDED_SWINGING_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_LADYBIRD_CARS, + { COPY_COLOUR_1, COPY_COLOUR_1, COPY_COLOUR_2 }, // RCT1_VEHICLE_TYPE_STANDUP_ROLLER_COASTER_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SPINNING_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SINGLE_PERSON_SWINGING_CHAIRS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SWANS_PEDAL_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_DARK_BLUE }, // RCT1_VEHICLE_TYPE_LARGE_MONORAIL_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CANOES, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_ROWING_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_STEAM_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COPY_COLOUR_2 }, // RCT1_VEHICLE_TYPE_WOODEN_MOUSE_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_BUMPER_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_WOODEN_ROLLER_COASTER_TRAIN_BACKWARDS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_ROCKET_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_HORSES, // Steeplechase + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SPORTSCARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_LYING_DOWN_SWINGING_CARS, // Inverted single-rail + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_WOODEN_MINE_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SUSPENDED_SWINGING_AIRPLANE_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SMALL_MONORAIL_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_WATER_TRICYCLES, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_LAUNCHED_FREEFALL_CAR, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_BOBSLEIGH_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_DINGHIES, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_ROTATING_CABIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_MINE_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CHAIRLIFT_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CORKSCREW_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_MOTORBIKES, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_RACING_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_TRUCKS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_GO_KARTS, + { COPY_COLOUR_1, COLOUR_BLACK, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_RAPIDS_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_LOG_FLUME_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_DODGEMS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SWINGING_SHIP, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SWINGING_INVERTER_SHIP, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_MERRY_GO_ROUND, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_FERRIS_WHEEL, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SIMULATOR_POD, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CINEMA_BUILDING, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_TOPSPIN_CAR, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SPACE_RINGS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_REVERSE_FREEFALL_ROLLER_COASTER_CAR, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_VERTICAL_ROLLER_COASTER_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CAT_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_TWIST_ARMS_AND_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_HAUNTED_HOUSE_BUILDING, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_LOG_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CIRCUS_TENT, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_GHOST_TRAIN_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_STEEL_TWISTER_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_WOODEN_TWISTER_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_WOODEN_SIDE_FRICTION_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_VINTAGE_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_STEAM_TRAIN_COVERED_CARS, + { COPY_COLOUR_1, COLOUR_BLACK, COPY_COLOUR_2 }, // RCT1_VEHICLE_TYPE_STAND_UP_STEEL_TWISTER_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_FLOORLESS_STEEL_TWISTER_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_STEEL_MOUSE_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CHAIRLIFT_CARS_ALTERNATIVE, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SUSPENDED_MONORAIL_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_HELICOPTER_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_VIRGINIA_REEL_TUBS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_REVERSER_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_GOLFERS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_RIVER_RIDE_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_FLYING_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_NON_LOOPING_STEEL_TWISTER_ROLLER_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_HEARTLINE_TWISTER_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_HEARTLINE_TWISTER_CARS_REVERSED, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_RESERVED, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_ROTODROP_CAR, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_FLYING_SAUCERS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_CROOKED_HOUSE_BUILDING, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_BICYCLES, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_HYPERCOASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_4_ACROSS_INVERTED_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_WATER_COASTER_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_FACEOFF_CARS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_JET_SKIS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_RAFT_BOATS, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_AMERICAN_STYLE_STEAM_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_AIR_POWERED_COASTER_TRAIN, + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_SUSPENDED_WILD_MOUSE_CARS, // Inverted Hairpin in RCT2 + { COPY_COLOUR_1, COPY_COLOUR_2, COLOUR_BLACK }, // RCT1_VEHICLE_TYPE_ENTERPRISE_WHEEL + }; + + Guard::ArgumentInRange(vehicleType, 0, Util::CountOf(map), "Unsupported RCT1 vehicle type."); + return map[vehicleType]; + } + bool RideTypeUsesVehicles(uint8 rideType) { switch (rideType) { @@ -314,6 +425,270 @@ namespace RCT1 return pathAdditionType; } + uint8 GetVehicleSubEntryIndex(uint8 vehicleSubEntry) + { + static const uint8 map[] = + { + 0, // STEEL_RC_FRONT + 1, // STEEL_RC_CARRIAGE + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // MONORAIL_CARRIAGE + 1, // MONORAIL_FRONT + 2, // MONORAIL_BACK + 0, + 0, + 1, // MINIATURE_RAILWAY_TENDER + 0, // MINIATURE_RAILWAY_LOCOMOTIVE + 2, // MINIATURE_RAILWAY_CARRIAGE + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // MINE_TRAIN_FRONT + 1, // MINE_TRAIN_CARRIAGE + 0, + 0, // CORKSCREW_RC_FRONT + 1, // CORKSCREW_RC_CARRIAGE + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // GHOST_TRAIN_CAR + 1, // TWISTER_RC_SPOILER + 0, // TWISTER_RC_CARRIAGE + 1, // GHOST_TRAIN_INVISIBLE + 0, // ARTICULATED_RC_FRONT + 1, // ARTICULATED_RC_CARRIAGE + 0, + 0, + 2, // MINIATURE_RAILWAY_CARRIAGE_COVERED + 0, // STANDUP_TWISTER_RC_CARRIAGE + 0, + 0, + 0, + 0, + 0, + 0, + 0, // REVERSER_RC_CAR + 2, // REVERSER_RC_BOGIE + 1, // MINIGOLF_PLAYER + 0, // MINIGOLF_BALL + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, // HYPERCOASTER_FRONT + 1, // HYPERCOASTER_CARRIAGE + 0, // INVERTED_4_ACROSS_CARRIAGE + 0, // WATER_COASTER_BOAT + 0, + 1, // WATER_COASTER_INVISIBLE + 0, + 0, // RIVER_RAFT + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }; + return map[vehicleSubEntry]; + } + const char * GetRideTypeObject(uint8 rideType) { static const char * map[] = @@ -997,9 +1372,7 @@ namespace RCT1 return map[sceneryGroupType]; } -#pragma warning(push) -#pragma warning(disable : 4505) // 'identifier' unreferenced local function has been removed - static const char * GetWaterObject(uint8 waterType) + const char * GetWaterObject(uint8 waterType) { static const char * map[] = { @@ -1008,7 +1381,6 @@ namespace RCT1 }; return map[waterType]; } -#pragma warning(pop) const std::vector GetPreferedRideEntryOrder(uint8 rideType) { diff --git a/src/openrct2/ride/ride.c b/src/openrct2/ride/ride.c index b10c492ef6..c270f59550 100644 --- a/src/openrct2/ride/ride.c +++ b/src/openrct2/ride/ride.c @@ -2498,7 +2498,7 @@ static void ride_breakdown_status_update(sint32 rideIndex) if ( !(ride->not_fixed_timeout & 15) && ride->mechanic_status != RIDE_MECHANIC_STATUS_FIXING && - ride->mechanic_status != RIDE_MECHANIC_STATUS_4 + ride->mechanic_status != RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES ) { set_format_arg(0, rct_string_id, ride->name); set_format_arg(2, uint32, ride->name_arguments); @@ -2546,8 +2546,11 @@ static void ride_mechanic_status_update(sint32 rideIndex, sint32 mechanicStatus) ride_call_closest_mechanic(rideIndex); break; case RIDE_MECHANIC_STATUS_HEADING: - mechanic = &(get_sprite(ride->mechanic)->peep); - if ( + mechanic = NULL; + if (ride->mechanic != SPRITE_INDEX_NULL) { + mechanic = &(get_sprite(ride->mechanic)->peep); + } + if (mechanic == NULL || !peep_is_mechanic(mechanic) || (mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && mechanic->state != PEEP_STATE_ANSWERING) || mechanic->current_ride != rideIndex @@ -2558,8 +2561,11 @@ static void ride_mechanic_status_update(sint32 rideIndex, sint32 mechanicStatus) } break; case RIDE_MECHANIC_STATUS_FIXING: - mechanic = &(get_sprite(ride->mechanic)->peep); - if ( + mechanic = NULL; + if (ride->mechanic != SPRITE_INDEX_NULL) { + mechanic = &(get_sprite(ride->mechanic)->peep); + } + if (mechanic == NULL || !peep_is_mechanic(mechanic) || ( mechanic->state != PEEP_STATE_HEADING_TO_INSPECTION && @@ -2702,8 +2708,8 @@ rct_peep *ride_get_assigned_mechanic(rct_ride *ride) if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN) { if ( ride->mechanic_status == RIDE_MECHANIC_STATUS_HEADING || - ride->mechanic_status == 3 || - ride->mechanic_status == 4 + ride->mechanic_status == RIDE_MECHANIC_STATUS_FIXING || + ride->mechanic_status == RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES ) { rct_peep *peep = &(get_sprite(ride->mechanic)->peep); if (peep_is_mechanic(peep)) diff --git a/src/openrct2/ride/ride.h b/src/openrct2/ride/ride.h index bc34559338..5904753bac 100644 --- a/src/openrct2/ride/ride.h +++ b/src/openrct2/ride/ride.h @@ -662,7 +662,7 @@ enum { RIDE_MECHANIC_STATUS_CALLING, RIDE_MECHANIC_STATUS_HEADING, RIDE_MECHANIC_STATUS_FIXING, - RIDE_MECHANIC_STATUS_4 + RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES }; enum { diff --git a/src/openrct2/ride/shops/facility.c b/src/openrct2/ride/shops/facility.c index e8fa0cacb5..6e4bac4514 100644 --- a/src/openrct2/ride/shops/facility.c +++ b/src/openrct2/ride/shops/facility.c @@ -37,6 +37,12 @@ static void facility_paint_setup(uint8 rideIndex, uint8 trackSequence, uint8 dir rct_ride_entry *rideEntry = get_ride_entry(ride->subtype); rct_ride_entry_vehicle *firstVehicleEntry = &rideEntry->vehicles[0]; + if (rideEntry == NULL || firstVehicleEntry == NULL) + { + log_error("Error drawing facility, rideEntry or firstVehicleEntry is NULL."); + return; + } + uint32 imageId = gTrackColours[SCHEME_TRACK]; imageId |= firstVehicleEntry->base_image_id; imageId += (direction + 2) & 3; diff --git a/src/openrct2/ride/shops/shop.c b/src/openrct2/ride/shops/shop.c index 39845c7672..e5ad3f7e66 100644 --- a/src/openrct2/ride/shops/shop.c +++ b/src/openrct2/ride/shops/shop.c @@ -37,6 +37,12 @@ static void shop_paint_setup(uint8 rideIndex, uint8 trackSequence, uint8 directi rct_ride_entry *rideEntry = get_ride_entry(ride->subtype); rct_ride_entry_vehicle *firstVehicleEntry = &rideEntry->vehicles[0]; + if (rideEntry == NULL || firstVehicleEntry == NULL) + { + log_error("Error drawing shop, rideEntry or firstVehicleEntry is NULL."); + return; + } + uint32 imageId = gTrackColours[SCHEME_TRACK]; if (imageId & 0x80000000) { imageId &= 0x60FFFFFF; diff --git a/src/openrct2/ride/track_design.c b/src/openrct2/ride/track_design.c index 71412d336c..949d011ff5 100644 --- a/src/openrct2/ride/track_design.c +++ b/src/openrct2/ride/track_design.c @@ -188,7 +188,7 @@ static rct_track_td6 * track_design_open_from_td4(uint8 *src, size_t srcLength) td6->version_and_colour_scheme = td4->version_and_colour_scheme; // Vehicle colours - for (sint32 i = 0; i < 12; i++) { + for (sint32 i = 0; i < RCT1_MAX_VEHICLES_PER_RIDE; i++) { td6->vehicle_colours[i].body_colour = rct1_get_colour(td4->vehicle_colours[i].body_colour); td6->vehicle_colours[i].trim_colour = rct1_get_colour(td4->vehicle_colours[i].trim_colour); @@ -198,7 +198,7 @@ static rct_track_td6 * track_design_open_from_td4(uint8 *src, size_t srcLength) } } // Set remaining vehicles to same colour as first vehicle - for (sint32 i = 12; i < 32; i++) { + for (sint32 i = RCT1_MAX_VEHICLES_PER_RIDE; i < 32; i++) { td6->vehicle_colours[i] = td6->vehicle_colours[0]; } // Set additional colour to trim colour for all vehicles diff --git a/src/openrct2/ride/vehicle.c b/src/openrct2/ride/vehicle.c index f5b614edd3..2bd4d19877 100644 --- a/src/openrct2/ride/vehicle.c +++ b/src/openrct2/ride/vehicle.c @@ -3344,7 +3344,7 @@ static void vehicle_update_arriving(rct_vehicle* vehicle) if (ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN && ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE && ride->inspection_station == vehicle->current_station && - ride->mechanic_status != RIDE_MECHANIC_STATUS_4 + ride->mechanic_status != RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES ) { unkF64E35 = 0; } @@ -7492,7 +7492,7 @@ loc_6DAEB9: if (!( ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN && ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE && - ride->mechanic_status == RIDE_MECHANIC_STATUS_4 + ride->mechanic_status == RIDE_MECHANIC_STATUS_HAS_FIXED_STATION_BRAKES )) { regs.eax = vehicle->brake_speed << 16; if (regs.eax < _vehicleVelocityF64E08) { @@ -7507,15 +7507,9 @@ loc_6DAEB9: } } else if (trackType == TRACK_ELEM_BOOSTER && ride->type != RIDE_TYPE_WILD_MOUSE) { - if (!( - ride->lifecycle_flags & RIDE_LIFECYCLE_BROKEN_DOWN && - ride->breakdown_reason_pending == BREAKDOWN_BRAKES_FAILURE && - ride->mechanic_status == RIDE_MECHANIC_STATUS_4 - )) { - regs.eax = (vehicle->brake_speed << 16); - if (regs.eax > _vehicleVelocityF64E08) { - vehicle->acceleration = RideProperties[ride->type].acceleration << 16; //_vehicleVelocityF64E08 * 1.2; - } + regs.eax = (vehicle->brake_speed << 16); + if (regs.eax > _vehicleVelocityF64E08) { + vehicle->acceleration = RideProperties[ride->type].acceleration << 16; //_vehicleVelocityF64E08 * 1.2; } } diff --git a/src/openrct2/util/sawyercoding.c b/src/openrct2/util/sawyercoding.c index 2c7e9cc5fa..80e1867f18 100644 --- a/src/openrct2/util/sawyercoding.c +++ b/src/openrct2/util/sawyercoding.c @@ -553,7 +553,7 @@ sint32 sawyercoding_detect_file_type(const uint8 *src, size_t length) sint32 sawyercoding_detect_rct1_version(sint32 gameVersion) { sint32 fileType = (gameVersion) > 0 ? FILE_TYPE_SV4 : FILE_TYPE_SC4; - gameVersion=abs(gameVersion); + gameVersion = abs(gameVersion); if (gameVersion >= 108000 && gameVersion < 110000) return (FILE_VERSION_RCT1 | fileType);