From 72e625c5a3358b83a4deaba9b830996641f338ea Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 16 Apr 2016 23:56:09 +0100 Subject: [PATCH] import research list as best as possible --- src/management/research.c | 18 ++++ src/management/research.h | 3 + src/rct1.c | 217 +++++++++++++++++++++++++++++++------- src/rct1.h | 77 +++++++++++++- src/scenario.h | 7 +- 5 files changed, 279 insertions(+), 43 deletions(-) diff --git a/src/management/research.c b/src/management/research.c index fe9de1bd54..db17ceca8d 100644 --- a/src/management/research.c +++ b/src/management/research.c @@ -576,3 +576,21 @@ void game_command_set_research_funding(int* eax, int* ebx, int* ecx, int* edx, i *ebx = 0; } + +void research_insert_ride_entry(uint8 entryIndex, bool researched) +{ + rct_ride_entry *rideEntry = get_ride_entry(entryIndex); + uint8 category = rideEntry->category[0]; + for (int i = 0; i < 3; i++) { + uint8 rideType = rideEntry->ride_type[i]; + if (rideType != 255) { + research_insert(researched, 0x10000 | (rideType << 8) | entryIndex, category); + } + } +} + +void research_insert_scenery_group_entry(uint8 entryIndex, bool researched) +{ + rct_scenery_set_entry *scenerySetEntry = g_scenerySetEntries[entryIndex]; + research_insert(researched, entryIndex, RESEARCH_CATEGORY_SCENERYSET); +} diff --git a/src/management/research.h b/src/management/research.h index f8cf061082..737efea96c 100644 --- a/src/management/research.h +++ b/src/management/research.h @@ -85,4 +85,7 @@ void research_finish_item(sint32 entryIndex); void research_insert(int researched, int entryIndex, int category); void research_remove(sint32 entryIndex); +void research_insert_ride_entry(uint8 entryIndex, bool researched); +void research_insert_scenery_group_entry(uint8 entryIndex, bool researched); + #endif diff --git a/src/rct1.c b/src/rct1.c index 1906438485..60b1cc9eeb 100644 --- a/src/rct1.c +++ b/src/rct1.c @@ -222,7 +222,6 @@ void rct1_fix_landscape() rct1_load_default_objects(); reset_loaded_objects(); rct1_fix_walls(); - rct1_fix_scenery(); rct1_fix_terrain(); rct1_fix_entrance_positions(); rct1_reset_research(); @@ -390,34 +389,6 @@ static void rct1_fix_terrain() } } -/** - * - * rct2: 0x006A2956 - */ -static void rct1_fix_scenery() -{ - rct_map_element *element; - map_element_iterator it; - - map_element_iterator_begin(&it); - while (map_element_iterator_next(&it)) { - element = it.element; - - if (map_element_get_type(element) != MAP_ELEMENT_TYPE_SCENERY) - continue; - - switch (element->properties.scenery.type) { - case 157: // TGE1 (Geometric Sculpture) - case 162: // TGE2 (Geometric Sculpture) - case 168: // TGE3 (Geometric Sculpture) - case 170: // TGE4 (Geometric Sculpture) - case 171: // TGE5 (Geometric Sculpture) - element->properties.scenery.colour_2 = COLOUR_WHITE; - break; - } - } -} - /** * This isn't really RCT1 specific anymore. * rct2: 0x006A2A68 @@ -671,6 +642,17 @@ static void rct1_fix_colours() colour = RCT1ColourConversionTable[mapElement->properties.scenery.colour_1 & 0x1F]; mapElement->properties.scenery.colour_1 &= 0xE0; mapElement->properties.scenery.colour_1 |= colour; + + // Copied from [rct2: 0x006A2956] + switch (mapElement->properties.scenery.type) { + case 157: // TGE1 (Geometric Sculpture) + case 162: // TGE2 (Geometric Sculpture) + case 168: // TGE3 (Geometric Sculpture) + case 170: // TGE4 (Geometric Sculpture) + case 171: // TGE5 (Geometric Sculpture) + mapElement->properties.scenery.colour_2 = COLOUR_WHITE; + break; + } break; case MAP_ELEMENT_TYPE_FENCE: colour = RCT1ColourConversionTable[ @@ -1684,14 +1666,29 @@ static const rct_object_entry RCT1DefaultObjectsPathBits[] = { static const rct_object_entry RCT1DefaultObjectsSceneryGroup[] = { { 0x00000087, { "SCGTREES" }, 0 }, + { 0x00000087, { "SCGPATHX" }, 0 }, { 0x00000087, { "SCGSHRUB" }, 0 }, { 0x00000087, { "SCGGARDN" }, 0 }, - { 0x00000087, { "SCGPATHX" }, 0 }, { 0x00000087, { "SCGFENCE" }, 0 }, - { 0x00000087, { "SCGMART " }, 0 }, - { 0x00000087, { "SCGWOND " }, 0 }, - { 0x00000087, { "SCGSNOW " }, 0 }, - { 0x00000087, { "SCGWALLS" }, 0 } + { 0x00000087, { "SCGWALLS" }, 0 }, + + { 0x00000087, { "SCGMINE " }, 0 }, // RCT1_SCENERY_THEME_MINE_THEME + { 0x00000087, { "SCGCLASS" }, 0 }, // RCT1_SCENERY_THEME_CLASSICAL_ROMAN + { 0x00000087, { "SCGEGYPT" }, 0 }, // RCT1_SCENERY_THEME_EGYPTIAN + { 0x00000087, { "SCGMART " }, 0 }, // RCT1_SCENERY_THEME_MARTIAN + { 0x00000087, { "SCGWOND " }, 0 }, // RCT1_SCENERY_THEME_TOYLAND + { 0x00000087, { "SCGJURAS" }, 0 }, // RCT1_SCENERY_THEME_JURASSIC + { 0x00000087, { "SCGSPOOK" }, 0 }, // RCT1_SCENERY_THEME_GRAVEYARD + { 0x00000087, { "SCGJUNGL" }, 0 }, // RCT1_SCENERY_THEME_JUNGLE + { 0x00000087, { "SCGABSTR" }, 0 }, // RCT1_SCENERY_THEME_ABSTRACT + { 0x00000087, { "SCGSNOW " }, 0 }, // RCT1_SCENERY_THEME_SNOW_ICE + { 0x00000087, { "SCGMEDIE" }, 0 }, // RCT1_SCENERY_THEME_MEDIEVAL + { 0x00000087, { "SCGSPACE" }, 0 }, // RCT1_SCENERY_THEME_SPACE + { 0x00000087, { "SCGHALLO" }, 0 }, // RCT1_SCENERY_THEME_CREEPY + + // Not enough space to fit these last two themes (think about doing dynamic list) + // { 0x00000087, { "SCGURBAN" }, 0 }, // RCT1_SCENERY_THEME_URBAN + // { 0x00000087, { "SCGORIEN" }, 0 }, // RCT1_SCENERY_THEME_PAGODA }; static const rct_object_entry RCT1DefaultObjectsParkEntrance[] = { @@ -2011,6 +2008,27 @@ bool rideTypeShouldLoseSeparateFlag(rct_ride_entry *ride) return remove_flag; } +const uint8 RCT1SceneryGroupConvertTable[] = { + 255, // RCT1_SCENERY_THEME_GENERAL + 6, // RCT1_SCENERY_THEME_MINE_THEME + 7, // RCT1_SCENERY_THEME_CLASSICAL_ROMAN + 8, // RCT1_SCENERY_THEME_EGYPTIAN + 9, // RCT1_SCENERY_THEME_MARTIAN + 255, // RCT1_SCENERY_THEME_JUMPING_FOUNTAINS + 10, // RCT1_SCENERY_THEME_WONDERLAND + 11, // RCT1_SCENERY_THEME_JURASSIC + 12, // RCT1_SCENERY_THEME_SPOOKY + 13, // RCT1_SCENERY_THEME_JUNGLE + 14, // RCT1_SCENERY_THEME_ABSTRACT + 255, // RCT1_SCENERY_THEME_GARDEN_CLOCK + 15, // RCT1_SCENERY_THEME_SNOW_ICE + 16, // RCT1_SCENERY_THEME_MEDIEVAL + 17, // RCT1_SCENERY_THEME_SPACE + 18, // RCT1_SCENERY_THEME_CREEPY + 255, // RCT1_SCENERY_THEME_URBAN + 255 // RCT1_SCENERY_THEME_PAGODA +}; + #pragma endregion #pragma region RCT1 Scenario / Saved Game Import @@ -2192,7 +2210,6 @@ static void rct1_import_map_elements(rct1_s4 *s4) rct1_fix_paths(); rct1_fix_walls(); rct1_fix_banners(s4); - rct1_fix_scenery(); rct1_fix_terrain(); rct1_fix_entrance_positions(); @@ -2363,6 +2380,134 @@ static void rct1_import_ride(rct1_s4 *s4, rct_ride *dst, rct1_ride *src) dst->excitement = (ride_rating)-1; } +static uint8 rct1_convert_vehicle_to_ride_entry_index(uint8 rct1RideType, uint8 vehicle) +{ + uint8 rideEntryIndex = rct1RideType; + if (vehicle < countof(RCT1AlternativeVehicleMappings)) { + vehicle = RCT1AlternativeVehicleMappings[vehicle]; + if (vehicle != USE_DEFAULT_VEHICLE) { + rideEntryIndex = vehicle; + } + } + return rideEntryIndex; +} + +static bool _rct1ResearchRideEntryUsed[128]; +static bool _rct1ResearchRideTypeUsed[128]; + +static void rct1_research_insert_vehicle(const rct1_research_item *researchItem, bool researched) +{ + uint8 rct1RideType = researchItem->related_ride; + uint8 vehicle = researchItem->item; + + uint8 rideEntryIndex = rct1_convert_vehicle_to_ride_entry_index(rct1RideType, vehicle); + if (!_rct1ResearchRideEntryUsed[rideEntryIndex]) { + _rct1ResearchRideEntryUsed[rideEntryIndex] = true; + research_insert_ride_entry(rideEntryIndex, researched); + } +} + +/** + * In RollerCoaster Tycoon 1 the research was divided up into scenery themes, + * rides, vehicles for rides and special track pieces for rides. Currently + * OpenRCT2 does not support researching of special track pieces and whether + * vehicles are research-able depends on the vehicle object. + * + * TODO We might probably want to sort the researched items by the prefered + * RCT1 order so the right image shows in the new ride window. This is + * only relevant if RCT1 ride sorting is turned off. + */ +static void rct1_import_research_list(const rct1_s4 *s4) +{ + int maxResearchItems = countof(s4->research_items); + const rct1_research_item *researchItems = s4->research_items; + + // Loopy Landscapes stores research items in a different place + int gameVersion = sawyercoding_detect_rct1_version(s4->game_version) & FILE_VERSION_MASK; + if (gameVersion == FILE_VERSION_RCT1_LL) { + maxResearchItems = countof(s4->research_items_LL); + researchItems = s4->research_items_LL; + } + + // Initialise the "seen" tables + memset(_rct1ResearchRideEntryUsed, 0, sizeof(_rct1ResearchRideEntryUsed)); + memset(_rct1ResearchRideTypeUsed, 0, sizeof(_rct1ResearchRideTypeUsed)); + + // The first six scenery groups are always available + for (int i = 0; i < 6; i++) { + research_insert_scenery_group_entry(i, true); + } + + bool researched = true; + for (int i = 0; i < maxResearchItems; i++) { + const rct1_research_item *researchItem = &researchItems[i]; + if (researchItem->item == RCT1_RESEARCH_END_AVAILABLE) { + researched = false; + } else if (researchItem->item == RCT1_RESEARCH_END_RESEARCHABLE || researchItem->item == RCT1_RESEARCH_END) { + break; + } + + switch (researchItem->category) { + case RCT1_RESEARCH_CATEGORY_THEME: + { + uint8 rct1SceneryTheme = researchItem->item; + uint8 sceneryGroupEntryIndex = RCT1SceneryGroupConvertTable[rct1SceneryTheme]; + if (sceneryGroupEntryIndex != 255) { + research_insert_scenery_group_entry(sceneryGroupEntryIndex, researched); + } + break; + } + case RCT1_RESEARCH_CATEGORY_RIDE: + { + uint8 rct1RideType = researchItem->item; + uint8 rideEntryIndex = rct1RideType; + rct_ride_entry *rideEntry = get_ride_entry(rideEntryIndex); + + // Add all vehicles for this ride type that are researched or before this research item + uint32 numVehicles = 0; + for (int j = 0; j < maxResearchItems; j++) { + const rct1_research_item *researchItem2 = &researchItems[j]; + if (researchItem2->item == RCT1_RESEARCH_END_AVAILABLE) { + break; + } + + if (researchItem->category == RCT1_RESEARCH_CATEGORY_VEHICLE && + researchItem->related_ride == rct1RideType + ) { + // Only add the vehicles that were listed before this ride, otherwise we might + // change the research order + if (j < i) { + rct1_research_insert_vehicle(researchItem, researched); + } + numVehicles++; + } + } + + if (numVehicles == 0) { + // No vehicles found so just add the default for this ride + if (!_rct1ResearchRideEntryUsed[rideEntryIndex]) { + _rct1ResearchRideEntryUsed[rideEntryIndex] = true; + research_insert_ride_entry(rideEntryIndex, researched); + } + } + break; + } + case RCT1_RESEARCH_CATEGORY_VEHICLE: + // Only add vehicle if the related ride has been seen, this to make sure that vehicles + // are researched only after the ride has been researched + if (_rct1ResearchRideTypeUsed[researchItem->related_ride]) { + rct1_research_insert_vehicle(researchItem, researched); + } + break; + case RCT1_RESEARCH_CATEGORY_SPECIAL: + // Not supported + break; + } + } + + research_remove_non_separate_vehicle_types(); +} + static void rct1_import_s4_properly(rct1_s4 *s4) { int mapSize = s4->map_size == 0 ? 128 : s4->map_size; @@ -2396,7 +2541,7 @@ static void rct1_import_s4_properly(rct1_s4 *s4) // Fix object availability research_reset_items(); - research_populate_list_researched(); + rct1_import_research_list(s4); // Map elements rct1_import_map_elements(s4); diff --git a/src/rct1.h b/src/rct1.h index d2fc760ec9..591b74fd01 100644 --- a/src/rct1.h +++ b/src/rct1.h @@ -165,6 +165,14 @@ typedef struct { uint8 unk_17A[230]; } rct1_ride; +typedef struct { + uint8 item; + uint8 related_ride; + uint8 category; + uint8 flags; + uint8 expenditure_area; +} rct1_research_item; + /** * RCT1,AA,LL scenario / saved game structure. * size: 0x1F850C @@ -226,7 +234,7 @@ typedef struct { uint8 last_research_ride; uint8 last_research_category; uint8 last_research_flag; - rct_research_item research_items[200]; + rct1_research_item research_items[200]; uint8 next_research_item; uint8 next_research_ride; uint8 next_research_category; @@ -291,7 +299,7 @@ typedef struct { uint8 unk_199C96[3]; uint8 water_colour; uint16 unk_199C9A; - rct_research_item research_items_LL[180]; + rct1_research_item research_items_LL[180]; uint8 unk_19A020[5468]; rct_banner banners[100]; char string_table[1024][32]; @@ -527,6 +535,71 @@ enum{ }; +enum { + RCT1_SCENERY_THEME_GENERAL, + RCT1_SCENERY_THEME_MINE, + RCT1_SCENERY_THEME_CLASSICAL_ROMAN, + RCT1_SCENERY_THEME_EGYPTIAN, + RCT1_SCENERY_THEME_MARTIAN, + RCT1_SCENERY_THEME_JUMPING_FOUNTAINS, // Single researchable scenery item + RCT1_SCENERY_THEME_WONDERLAND, + RCT1_SCENERY_THEME_JURASSIC, + RCT1_SCENERY_THEME_SPOOKY, + RCT1_SCENERY_THEME_JUNGLE, + RCT1_SCENERY_THEME_ABSTRACT, + RCT1_SCENERY_THEME_GARDEN_CLOCK, // Single researchable scenery item + RCT1_SCENERY_THEME_SNOW_ICE, + RCT1_SCENERY_THEME_MEDIEVAL, + RCT1_SCENERY_THEME_SPACE, + RCT1_SCENERY_THEME_CREEPY, + RCT1_SCENERY_THEME_URBAN, + RCT1_SCENERY_THEME_PAGODA, +}; + +enum { + RCT1_RESEARCH_END_AVAILABLE = 0xFF, + RCT1_RESEARCH_END_RESEARCHABLE = 0xFE, + RCT1_RESEARCH_END = 0xFD, +}; + +enum { + RCT1_RESEARCH_CATEGORY_THEME, + RCT1_RESEARCH_CATEGORY_RIDE, + RCT1_RESEARCH_CATEGORY_VEHICLE, + RCT1_RESEARCH_CATEGORY_SPECIAL, +}; + +enum { + RCT1_RESEARCH_EXPENDITURE_ROLLERCOASTERS, + RCT1_RESEARCH_EXPENDITURE_THRILL_RIDES, + RCT1_RESEARCH_EXPENDITURE_GENTLE_TRANSPORT_RIDES, + RCT1_RESEARCH_EXPENDITURE_SHOPS, + RCT1_RESEARCH_EXPENDITURE_SCENERY_THEMEING, + RCT1_RESEARCH_EXPENDITURE_RIDE_IMPROVEMENTS, +}; + +// Unconfirmed special track elements for research +enum { + RCT1_RESEARCH_SPECIAL_BANKED_CURVES = 0x06, + RCT1_RESEARCH_SPECIAL_VERTICAL_LOOP = 0x07, + RCT1_RESEARCH_SPECIAL_STEEP_TWIST = 0x0C, + RCT1_RESEARCH_SPECIAL_INLINE_TWIST = 0x11, + RCT1_RESEARCH_SPECIAL_HALF_LOOP = 0x12, + RCT1_RESEARCH_SPECIAL_CORKSCREW = 0x13, + RCT1_RESEARCH_SPECIAL_BANKED_HELIX_A = 0x15, + RCT1_RESEARCH_SPECIAL_BANKED_HELIX_B = 0x16, + RCT1_RESEARCH_SPECIAL_HELIX = 0x17, + RCT1_RESEARCH_SPECIAL_ON_RIDE_PHOTO = 0x1A, + RCT1_RESEARCH_SPECIAL_WATER_SPLASH = 0x1B, + RCT1_RESEARCH_SPECIAL_VERTICAL_DROP = 0x1C, + RCT1_RESEARCH_SPECIAL_BARREL_ROLL = 0x1D, + RCT1_RESEARCH_SPECIAL_LAUNCHED_LIFT_HILL = 0x1E, + RCT1_RESEARCH_SPECIAL_LARGE_LOOP_AND_HALF = 0x1F, + RCT1_RESEARCH_SPECIAL_REVERSER_TURNTABLE = 0x21, + RCT1_RESEARCH_SPECIAL_HEARTLINE_ROLL = 0x22, + RCT1_RESEARCH_SPECIAL_REVERSING_SECTIONS = 0x23, +}; + typedef struct{ uint8 type; // 0x00 uint8 vehicle_type; // 0x01 diff --git a/src/scenario.h b/src/scenario.h index bb480a41c0..500f9ab6c1 100644 --- a/src/scenario.h +++ b/src/scenario.h @@ -168,11 +168,8 @@ typedef struct { uint8 guest_count_change_modifier; uint8 current_research_level; uint8 pad_01357400[4]; - uint32 dword_01357404; - uint32 dword_01357408; - uint32 dword_0135740C; - uint32 dword_01357410[5]; - uint32 dword_01357424[8]; + uint32 ride_types_researched[8]; + uint32 ride_entries_researched[8]; uint32 dword_01357444[128]; uint32 dword_01357644[128];