diff --git a/src/ride/ride.h b/src/ride/ride.h index a329bea947..77cf17829f 100644 --- a/src/ride/ride.h +++ b/src/ride/ride.h @@ -117,7 +117,8 @@ typedef struct { uint8 var_0D0; uint8 pad_0D1[0x3]; uint8 measurement_index; // 0x0D4 - uint8 pad_0D5[0x3]; + uint8 var_0D5; + uint8 pad_0D6[0x2]; sint32 max_speed; // 0x0D8 sint32 average_speed; // 0x0DC uint8 pad_0E0[0x4]; @@ -126,13 +127,21 @@ typedef struct { fixed16_2dp max_positive_vertical_g; // 0x0FC fixed16_2dp max_negative_vertical_g; // 0x0FE fixed16_2dp max_lateral_g; // 0x100 - uint8 pad_102[0x12]; - uint8 inversions; // 0x114 (???X XXXX) holes for mini golf + uint8 pad_102[0xC]; + uint16 var_10E; + uint16 var_110; + uint16 var_112; + union { + uint8 inversions; // 0x114 (???X XXXX) + uint8 holes; // 0x114 (???X XXXX) + }; uint8 drops; // 0x115 (??XX XXXX) uint8 pad_116; uint8 highest_drop_height; // 0x117 uint32 var_118; - uint8 pad_11C[0x08]; + uint8 pad_11C[0x02]; + uint8 var_11E; + uint8 pad_11F[0x05]; sint16 var_124; sint16 var_126; sint16 var_128; @@ -145,9 +154,14 @@ typedef struct { sint16 var_136; money16 price; // 0x138 uint8 pad_13A[0x06]; - ride_rating excitement; // 0x140 - ride_rating intensity; // 0x142 - ride_rating nausea; // 0x144 + union { + rating_tuple ratings; // 0x140 + struct { + ride_rating excitement; // 0x140 + ride_rating intensity; // 0x142 + ride_rating nausea; // 0x144 + }; + }; uint16 reliability; // 0x146 uint16 pad_148; uint16 var_14A; @@ -159,7 +173,9 @@ typedef struct { uint16 var_158; uint8 pad_15A; uint8 num_riders; // 0x15B - uint8 pad_15C[0x24]; + uint8 pad_15C[2]; + uint16 maze_tiles; + uint8 pad_160[0x20]; sint16 build_date; // 0x180 money16 upkeep_cost; // 0x182 uint16 race_winner; // 0x184 diff --git a/src/ride/ride_ratings.c b/src/ride/ride_ratings.c index dc90c724da..964538abba 100644 --- a/src/ride/ride_ratings.c +++ b/src/ride/ride_ratings.c @@ -19,54 +19,410 @@ *****************************************************************************/ #include "../addresses.h" +#include "../interface/window.h" +#include "../world/map.h" #include "ride.h" #include "ride_data.h" #include "ride_ratings.h" +enum { + RIDE_RATINGS_STATE_FIND_NEXT_RIDE, + RIDE_RATINGS_STATE_INITIALISE, + RIDE_RATINGS_STATE_2, + RIDE_RATINGS_STATE_CALCULATE, + RIDE_RATINGS_STATE_4, + RIDE_RATINGS_STATE_5 +}; + +typedef void (*ride_ratings_calculation)(rct_ride *ride); + +#define _rideRatingsState RCT2_GLOBAL(0x0138B591, uint8) +#define _rideRatingsCurrentRide RCT2_GLOBAL(0x0138B590, uint8) + +static const ride_ratings_calculation ride_ratings_calculate_func_table[91]; + +static void ride_ratings_update_state_0(); +static void ride_ratings_update_state_1(); +static void ride_ratings_update_state_2(); +static void ride_ratings_update_state_3(); +static void ride_ratings_update_state_4(); +static void ride_ratings_update_state_5(); +static void loc_6B5BB2(); +static void ride_ratings_calculate(rct_ride *ride); +static void ride_ratings_reliability_calculate(rct_ride *ride); + +static int sub_6C6402(rct_map_element *mapElement, int *x, int *y, int *z) +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + + eax = *x; + ecx = *y; + esi = (int)mapElement; + RCT2_CALLFUNC_X(0x006C6402, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + *x = *((uint16*)&eax); + *y = *((uint16*)&ecx); + *z = *((uint8*)&edx); + return 1; +} + +static int sub_6C60C2(rct_map_element *mapElement, int *x, int *y, int *z) +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + + eax = *x; + ecx = *y; + esi = (int)mapElement; + RCT2_CALLFUNC_X(0x006C6402, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + *x = *((uint16*)&eax); + *y = *((uint16*)&ecx); + *z = *((uint8*)&edx); + return 1; +} + /** + * * rct2: 0x006B5A2A */ void ride_ratings_update_all() { - RCT2_CALLPROC_EBPSAFE(0x006B5A2A); + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) + return; + + switch (_rideRatingsState) { + case RIDE_RATINGS_STATE_FIND_NEXT_RIDE: + ride_ratings_update_state_0(); + break; + case RIDE_RATINGS_STATE_INITIALISE: + ride_ratings_update_state_1(); + break; + case RIDE_RATINGS_STATE_2: + ride_ratings_update_state_2(); + break; + case RIDE_RATINGS_STATE_CALCULATE: + ride_ratings_update_state_3(); + break; + case RIDE_RATINGS_STATE_4: + ride_ratings_update_state_4(); + break; + case RIDE_RATINGS_STATE_5: + ride_ratings_update_state_5(); + break; + } } /** - * rct2: 0x0065C4D4 * - * Compute excitement, intensity, etc. for a crooked house ride. + * rct2: 0x006B5A5C */ -void crooked_house_excitement(rct_ride *ride) +static void ride_ratings_update_state_0() { - // Set lifecycle bits - ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; - ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS; - ride->var_198 = 5; - sub_655FD6(ride); + rct_ride *ride; - ride_rating excitement = RIDE_RATING(2,15); - ride_rating intensity = RIDE_RATING(0,62); - ride_rating nausea = RIDE_RATING(0,34); + _rideRatingsCurrentRide += 1; + if (_rideRatingsCurrentRide == 255) + _rideRatingsCurrentRide = 0; - // NB this should get marked out by the compiler, if it's smart. - excitement = apply_intensity_penalty(excitement, intensity); - rating_tuple tup = per_ride_rating_adjustments(ride, excitement, intensity, nausea); - excitement = tup.excitement; - intensity = tup.intensity; - nausea = tup.nausea; + ride = GET_RIDE(_rideRatingsCurrentRide); + if (ride->type != RIDE_TYPE_NULL && ride->status != RIDE_STATUS_CLOSED) + _rideRatingsState = RIDE_RATINGS_STATE_INITIALISE; +} - ride->excitement = excitement; - ride->intensity = intensity; - ride->nausea = nausea; +/** + * + * rct2: 0x006B5A94 + */ +static void ride_ratings_update_state_1() +{ + RCT2_GLOBAL(0x0138B594, uint16) = 0; + RCT2_GLOBAL(0x0138B596, uint16) = 0; + RCT2_GLOBAL(0x0138B598, uint16) = 0; + RCT2_GLOBAL(0x0138B59A, uint16) = 0; + RCT2_GLOBAL(0x0138B59C, uint16) = 0; + RCT2_GLOBAL(0x0138B59E, uint16) = 0; + RCT2_GLOBAL(0x0138B5A0, uint16) = 0; + RCT2_GLOBAL(0x0138B5A2, uint16) = 0; + RCT2_GLOBAL(0x0138B5A4, uint16) = 0; + RCT2_GLOBAL(0x0138B5A6, uint16) = 0; + RCT2_GLOBAL(0x0138B5A8, uint16) = 0; + RCT2_GLOBAL(0x0138B5AA, uint16) = 0; + RCT2_GLOBAL(0x0138B5AC, uint16) = 0; + RCT2_GLOBAL(0x0138B5AE, uint16) = 0; + RCT2_GLOBAL(0x0138B5B0, uint16) = 0; + RCT2_GLOBAL(0x0138B5B2, uint16) = 0; + RCT2_GLOBAL(0x0138B5B4, uint16) = 0; + RCT2_GLOBAL(0x0138B5B6, uint16) = 0; + RCT2_GLOBAL(0x0138B5B8, uint16) = 0; + RCT2_GLOBAL(0x0138B5BA, uint16) = 0; + RCT2_GLOBAL(0x0138B5BC, uint16) = 0; + RCT2_GLOBAL(0x0138B5BE, uint16) = 0; + RCT2_GLOBAL(0x0138B5C0, uint16) = 0; + RCT2_GLOBAL(0x0138B5C2, uint16) = 0; + RCT2_GLOBAL(0x0138B5C4, uint16) = 0; + RCT2_GLOBAL(0x0138B5C6, uint16) = 0; + RCT2_GLOBAL(0x0138B5C8, uint16) = 0; + RCT2_GLOBAL(0x0138B5CA, uint16) = 0; + RCT2_GLOBAL(0x0138B5CC, uint16) = 0; + _rideRatingsState = RIDE_RATINGS_STATE_2; + RCT2_GLOBAL(0x0138B5CE, uint16) = 0; + loc_6B5BB2(); +} - ride->upkeep_cost = compute_upkeep(ride); - // Upkeep flag? or a dirtiness flag - ride->var_14D |= 2; +/** + * + * rct2: 0x006B5BB2 + */ +static void loc_6B5BB2() +{ + rct_ride *ride; + int i, x, y, z; - // clear all bits except lowest 5 - ride->inversions &= 0x1F; - // set 6th,7th,8th bits - ride->inversions |= 0xE0; + ride = GET_RIDE(_rideRatingsCurrentRide); + if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) { + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + return; + } + + if (ride->type == RIDE_TYPE_MAZE) { + _rideRatingsState = RIDE_RATINGS_STATE_CALCULATE; + return; + } + + for (i = 0; i < 4; i++) { + if (ride->station_starts[i] != 0xFFFF) { + RCT2_GLOBAL(0x0138B5CE, uint16) &= ~1; + if (ride->entrances[i] == 0xFFFF) + RCT2_GLOBAL(0x0138B5CE, uint16) |= 1; + + x = (ride->station_starts[i] & 0xFF) * 32; + y = (ride->station_starts[i] >> 8) * 32; + z = ride->station_heights[i] * 8; + + RCT2_GLOBAL(0x0138B584, uint16) = x; + RCT2_GLOBAL(0x0138B586, uint16) = y; + RCT2_GLOBAL(0x0138B588, uint16) = z; + RCT2_GLOBAL(0x0138B592, uint8) = 255; + RCT2_GLOBAL(0x0138B58A, uint16) = x; + RCT2_GLOBAL(0x0138B58C, uint16) = y; + RCT2_GLOBAL(0x0138B58E, uint16) = z; + return; + } + } + + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; +} + +/** + * + * rct2: 0x006B5C66 + */ +static void ride_ratings_update_state_2() +{ + // sub_6C6402 returns a carry, CALLFUNC doesn't support this + // so have to wait for sub_6C60C2 to be decompiled + RCT2_CALLPROC_EBPSAFE(0x006B5C66); + return; + + rct_ride *ride; + rct_map_element *mapElement; + int x, y, z, trackType, entranceIndex; + + ride = GET_RIDE(_rideRatingsCurrentRide); + if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) { + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + return; + } + + x = RCT2_GLOBAL(0x0138B584, uint16) / 32; + y = RCT2_GLOBAL(0x0138B586, uint16) / 32; + z = RCT2_GLOBAL(0x0138B588, uint16) / 8; + + mapElement = TILE_MAP_ELEMENT_POINTER(y * 256 + x); + trackType = RCT2_GLOBAL(0x0138B592, uint8); + + do { + if ((mapElement->type & MAP_ELEMENT_TYPE_MASK) != MAP_ELEMENT_TYPE_TRACK) + continue; + if (mapElement->base_height != z) + continue; + + if (trackType == 255 || (!(mapElement->properties.track.sequence & 0x0F) && trackType == mapElement->properties.track.type)) { + if (trackType == 1) { + entranceIndex = (mapElement->properties.track.sequence >> 4) & 7; + RCT2_GLOBAL(0x0138B5CE, uint16) &= ~1; + if (ride->entrances[entranceIndex] == 0xFFFF) + RCT2_GLOBAL(0x0138B5CE, uint16) |= 1; + } + + RCT2_CALLPROC_X(0x006B5F9D, 0, 0, 0, 0, (int)mapElement, 0, 0); + + x = RCT2_GLOBAL(0x0138B584, uint16); + y = RCT2_GLOBAL(0x0138B586, uint16); + if (!sub_6C60C2(mapElement, &x, &y, &z)) { + _rideRatingsState = RIDE_RATINGS_STATE_4; + return; + } + + if (x == RCT2_GLOBAL(0x0138B58A, uint16) && y == RCT2_GLOBAL(0x0138B58C, uint16) && z == RCT2_GLOBAL(0x0138B58E, uint16)) { + _rideRatingsState = RIDE_RATINGS_STATE_CALCULATE; + return; + } + RCT2_GLOBAL(0x0138B584, uint16) = x; + RCT2_GLOBAL(0x0138B586, uint16) = y; + RCT2_GLOBAL(0x0138B588, uint16) = z; + RCT2_GLOBAL(0x0138B592, uint8) = mapElement->properties.track.type; + } + } while (!((mapElement++)->flags & MAP_ELEMENT_FLAG_LAST_TILE)); + + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; +} + +/** + * + * rct2: 0x006B5E4D + */ +static void ride_ratings_update_state_3() +{ + rct_ride *ride; + + ride = GET_RIDE(_rideRatingsCurrentRide); + if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) { + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + return; + } + + ride_ratings_calculate(ride); + RCT2_CALLPROC_X(0x00655F64, 0, 0, 0, 0, 0, (int)ride, 0); + ride_ratings_reliability_calculate(ride); + + window_invalidate_by_id(WC_RIDE, _rideRatingsCurrentRide); + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; +} + +/** + * + * rct2: 0x006B5BAB + */ +static void ride_ratings_update_state_4() +{ + _rideRatingsState = RIDE_RATINGS_STATE_5; + loc_6B5BB2(); +} + +/** + * + * rct2: 0x006B5D72 + */ +static void ride_ratings_update_state_5() +{ + // sub_6C6402 returns a carry, CALLFUNC doesn't support this + // so have to wait for sub_6C6402 to be decompiled + RCT2_CALLPROC_EBPSAFE(0x006B5D72); + return; + + rct_ride *ride; + rct_map_element *mapElement; + int x, y, z, trackType; + + ride = GET_RIDE(_rideRatingsCurrentRide); + if (ride->type == RIDE_TYPE_NULL || ride->status == RIDE_STATUS_CLOSED) { + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; + return; + } + + x = RCT2_GLOBAL(0x0138B584, uint16) / 32; + y = RCT2_GLOBAL(0x0138B586, uint16) / 32; + z = RCT2_GLOBAL(0x0138B588, uint16) / 8; + + mapElement = TILE_MAP_ELEMENT_POINTER(y * 256 + x); + trackType = RCT2_GLOBAL(0x0138B592, uint8); + + do { + if ((mapElement->type & MAP_ELEMENT_TYPE_MASK) != MAP_ELEMENT_TYPE_TRACK) + continue; + if (mapElement->base_height != z) + continue; + + if (trackType == 255 || trackType == mapElement->properties.track.type) { + RCT2_CALLPROC_X(0x006B5F9D, 0, 0, 0, 0, (int)mapElement, 0, 0); + + x = RCT2_GLOBAL(0x0138B584, uint16); + y = RCT2_GLOBAL(0x0138B586, uint16); + if (!sub_6C6402(mapElement, &x, &y, &z)) { + _rideRatingsState = RIDE_RATINGS_STATE_CALCULATE; + return; + } + + x >>= 16; + y >>= 16; + if (x == RCT2_GLOBAL(0x0138B58A, uint16) && y == RCT2_GLOBAL(0x0138B58C, uint16) && z == RCT2_GLOBAL(0x0138B58E, uint16)) { + _rideRatingsState = RIDE_RATINGS_STATE_CALCULATE; + return; + } + RCT2_GLOBAL(0x0138B584, uint16) = x; + RCT2_GLOBAL(0x0138B586, uint16) = y; + RCT2_GLOBAL(0x0138B588, uint16) = z; + RCT2_GLOBAL(0x0138B592, uint8) = mapElement->properties.track.type; + } + } while (!((mapElement++)->flags & MAP_ELEMENT_FLAG_LAST_TILE)); + + _rideRatingsState = RIDE_RATINGS_STATE_FIND_NEXT_RIDE; +} + +static void ride_ratings_calculate(rct_ride *ride) +{ + ride_ratings_calculation calcFunc; + + calcFunc = ride_ratings_calculate_func_table[ride->type]; + if (calcFunc == NULL) { + calcFunc = RCT2_ADDRESS(0x0097E050, ride_ratings_calculation)[ride->type]; + RCT2_CALLPROC_X((int)calcFunc, 0, 0, 0, 0, 0, (int)ride, 0); + } else { + calcFunc(ride); + } +} + +static void ride_ratings_reliability_calculate(rct_ride *ride) +{ + rct_ride *ride2; + int i, otherRidesOfSameType; + + if (ride->excitement == (ride_rating)0xFFFF) + return; + + int reliability = + (((ride->excitement * RCT2_GLOBAL(0x0097CD1E + (ride->type * 6), sint16)) * 32) >> 15) + + (((ride->intensity * RCT2_GLOBAL(0x0097CD20 + (ride->type * 6), sint16)) * 32) >> 15) + + (((ride->nausea * RCT2_GLOBAL(0x0097CD22 + (ride->type * 6), sint16)) * 32) >> 15); + + int monthsOld = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16) - ride->build_date; + + // New ride reward + if (monthsOld <= 12) { + reliability += 10; + if (monthsOld <= 4) + reliability += 20; + } + + // Old ride penalty + if (monthsOld >= 40) reliability -= reliability / 4; + if (monthsOld >= 64) reliability -= reliability / 4; + if (monthsOld < 200) { + if (monthsOld >= 88) reliability -= reliability / 4; + if (monthsOld >= 104) reliability -= reliability / 4; + if (monthsOld >= 120) reliability -= reliability / 2; + if (monthsOld >= 128) reliability -= reliability / 2; + } + + // Other ride of same type penalty + otherRidesOfSameType = 0; + FOR_ALL_RIDES(i, ride2) { + if (ride2->type == ride->type) + otherRidesOfSameType++; + } + if (otherRidesOfSameType > 1) + reliability -= reliability / 4; + + ride->reliability = max(0, reliability); } /** @@ -77,7 +433,7 @@ void crooked_house_excitement(rct_ride *ride) * inputs * - edi: ride ptr */ -uint16 compute_upkeep(rct_ride *ride) +static uint16 ride_compute_upkeep(rct_ride *ride) { // data stored at 0x0057E3A8, incrementing 18 bytes at a time uint16 upkeep = initialUpkeepCosts[ride->type]; @@ -170,89 +526,715 @@ uint16 compute_upkeep(rct_ride *ride) * - bp: nausea * - edi: ride ptr */ -rating_tuple per_ride_rating_adjustments(rct_ride *ride, ride_rating excitement, - ride_rating intensity, ride_rating nausea) +static void ride_ratings_apply_adjustments(rct_ride *ride, rating_tuple *ratings) { - // NB: The table here is allocated dynamically. Reading the exe will tell - // you nothing - rct_ride_type *rideType = gRideTypeList[ride->subtype]; + rct_ride_type *rideEntry; + + rideEntry = gRideTypeList[ride->subtype]; - // example value here: 12 (?) - excitement = excitement + ((excitement * rideType->excitement_multipler) >> 7); - intensity = intensity + ((intensity * rideType->intensity_multipler) >> 7); - nausea = nausea + ((nausea * rideType->nausea_multipler) >> 7); + // Apply ride entry multipliers + ratings->excitement += ((ratings->excitement * rideEntry->excitement_multipler) >> 7); + ratings->intensity += ((ratings->intensity * rideEntry->intensity_multipler ) >> 7); + ratings->nausea += ((ratings->nausea * rideEntry->nausea_multipler ) >> 7); // As far as I can tell, this flag detects whether the ride is a roller // coaster, or a log flume or rapids. Everything else it's not set. // more detail: https://gist.github.com/kevinburke/d951e74e678b235eef3e - uint16 ridetype_var = RCT2_GLOBAL(0x0097D4F2 + ride->type * 8, uint16); - if (ridetype_var & 0x80) { - uint16 ax = ride->total_air_time; - if (rideType->var_008 & 0x800) { - // 65e86e - ax = ax - 96; - if (ax >= 0) { - ax = ax >> 3; - excitement = excitement - ax; - ax = ax >> 1; - nausea = nausea - ax; + uint16 flags = RCT2_GLOBAL(0x0097D4F2 + ride->type * 8, uint16); + if (flags & 0x80) { + uint16 totalAirTime = ride->total_air_time; + if (rideEntry->var_008 & 0x800) { + totalAirTime -= 96; + if (totalAirTime >= 0) { + ratings->excitement -= totalAirTime / 8; + ratings->nausea -= totalAirTime / 16; } } else { - ax = ax >> 3; - excitement = excitement + ax; - ax = ax >> 1; - nausea += ax; + ratings->excitement += totalAirTime / 8; + ratings->nausea += totalAirTime / 16; } } - rating_tuple tup = { excitement, intensity, nausea }; - return tup; } /** - * rct2: 0x0065E7A3 - * - * inputs from x86 - * - bx: excitement - * - cx: intensity - * - bp: nausea - * - * returns: the excitement level, with intensity penalties applied + * Lowers excitment, the higher the intensity. + * rct2: 0x0065E7A3 */ -ride_rating apply_intensity_penalty(ride_rating excitement, ride_rating intensity) +static void ride_ratings_apply_intensity_penalty(rating_tuple *ratings) { - // intensity penalty - if (intensity >= 1000) { - excitement = excitement - (excitement >> 2); - } - if (intensity >= 1100) { - excitement = excitement - (excitement >> 2); - } - if (intensity >= 1200) { - excitement = excitement - (excitement >> 2); - } - if (intensity >= 1320) { - excitement = excitement - (excitement >> 2); - } - if (intensity >= 1450) { - excitement = excitement - (excitement >> 2); - } - return excitement; + static const ride_rating intensityBounds[] = { 1000, 1100, 1200, 1320, 1450 }; + int i; + + ride_rating excitement = ratings->excitement; + for (i = 0; i < countof(intensityBounds); i++) + if (ratings->intensity >= intensityBounds[i]) + excitement -= excitement / 4; + ratings->excitement = excitement; } /** - * rct2: 0x00655FD6 * - * Take ride property 1CD, make some modifications, store the modified value in - * property 198. + * rct2: 0x00655FD6 */ -void sub_655FD6(rct_ride *ride) +static void sub_655FD6(rct_ride *ride) { - uint8 al = ride->lift_hill_speed; - // No idea what this address is; maybe like compensation of some kind? The - // maximum possible value? - // List of ride names/values is here: - // https://gist.github.com/kevinburke/5eebcda14d94e6ee99c0 - al -= RCT2_ADDRESS(0x0097D7C9, uint8)[4 * ride->type]; - al = al << 1; - ride->var_198 += al; + ride->var_198 += (ride->lift_hill_speed - RCT2_ADDRESS(0x0097D7C9, uint8)[ride->type * 4]) * 2; } + +/** + * + * rct2: 0x0065E277 + */ +static int sub_65E277() +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + RCT2_CALLFUNC_X(0x0065E277, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + return ebx; +} + +/** + * + * rct2: 0x0065E72D + */ +static int sub_65E72D(rct_ride *ride) +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + edi = (int)ride; + RCT2_CALLFUNC_X(0x0065E277, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + return edx & 0xFFFF; +} + +/** + * + * rct2: 0x0065DDD1 + */ +static rating_tuple sub_65DDD1(rct_ride *ride) +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + edi = (int)ride; + RCT2_CALLFUNC_X(0x0065DDD1, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + rating_tuple rating = { ebx, ecx, ebp }; + return rating; +} + +/** + * + * rct2: 0x0065E1C2 + */ +static rating_tuple sub_65E1C2(rct_ride *ride) +{ + int eax, ebx, ecx, edx, esi, edi, ebp; + edi = (int)ride; + RCT2_CALLFUNC_X(0x0065E1C2, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + + rating_tuple rating = { ebx, ecx, ebp }; + return rating; +} + +/** + * + * rct2: 0x0065DCDC + */ +static rating_tuple ride_ratings_get_gforce_ratings(rct_ride *ride) +{ + rating_tuple result; + fixed16_2dp gforce; + + result.excitement = 0; + result.intensity = 0; + result.nausea = 0; + + // Apply maximum positive G force factor + result.excitement += (ride->max_positive_vertical_g * 5242) >> 16; + result.intensity += (ride->max_positive_vertical_g * 52428) >> 16; + result.nausea += (ride->max_positive_vertical_g * 17039) >> 16; + + // Apply maximum negative G force factor + gforce = ride->max_negative_vertical_g; + result.excitement += (clamp(-FIXED_2DP(2,50), gforce, FIXED_2DP(0,00)) * -15728) >> 16; + result.intensity += ((gforce - FIXED_2DP(1,00)) * -52428) >> 16; + result.nausea += ((gforce - FIXED_2DP(1,00)) * -14563) >> 16; + + // Apply lateral G force factor + result.excitement += (min(FIXED_2DP(1,50), ride->max_lateral_g) * 26214) >> 16; + result.intensity += (ride->max_lateral_g * 65536) >> 16; + result.nausea += (ride->max_lateral_g * 21845) >> 16; + + // Very high lateral G force penalty + if (ride->max_lateral_g > FIXED_2DP(2,80)) { + result.intensity += FIXED_2DP(3,75); + result.nausea += FIXED_2DP(2,00); + } + if (ride->max_lateral_g > FIXED_2DP(3,10)) { + result.excitement /= 2; + result.intensity += FIXED_2DP(8,50); + result.nausea += FIXED_2DP(4,00); + } + + return result; +} + +/** + * + * rct2: 0x0065E139 + */ +static rating_tuple ride_ratings_get_drop_ratings(rct_ride *ride) +{ + rating_tuple result; + int drops; + + result.excitement = 0; + result.intensity = 0; + result.nausea = 0; + + // Apply number of drops factor + drops = ride->drops & 0x3F; + result.excitement += (min(9, drops) * 728177) >> 16; + result.intensity += (drops * 928426) >> 16; + result.nausea += (drops * 655360) >> 16; + + // Apply highest drop factor + result.excitement += ((ride->highest_drop_height * 2) * 16000) >> 16; + result.intensity += ((ride->highest_drop_height * 2) * 32000) >> 16; + result.nausea += ((ride->highest_drop_height * 2) * 10240) >> 16; + + return result; +} + +/** + * Calculates a score based on the surrounding scenery. + * rct2: 0x0065E557 + */ +static int ride_ratings_get_scenery_score(rct_ride *ride) +{ + int i, x, y, z, xx, yy, type, numSceneryItems; + uint16 stationXY; + rct_map_element *mapElement; + + for (i = 0; i < 4; i++) { + stationXY = ride->station_starts[i]; + if (stationXY != 0xFFFF) + break; + } + if (i == 4) + return 0; + + if (ride->type == RIDE_TYPE_MAZE) + stationXY = ride->entrances[0]; + + x = stationXY & 0xFF; + y = stationXY >> 8; + z = map_element_height(x * 32, y * 32); + + // Check if station is underground, returns a fixed mediocre score since you can't have scenery underground + if (z > ride->station_heights[i] * 8) + return 40; + + // Count surrounding scenery items + numSceneryItems = 0; + for (yy = y - 5; yy <= y + 5; yy++) { + for (xx = x - 5; xx <= x + 5; xx++) { + // Count scenery items on this tile + mapElement = TILE_MAP_ELEMENT_POINTER(yy * 256 + xx); + do { + if (mapElement->flags & 0x10) + continue; + + type = mapElement->type & MAP_ELEMENT_TYPE_MASK; + if (type == MAP_ELEMENT_TYPE_SCENERY || type == MAP_ELEMENT_TYPE_SCENERY_MULTIPLE) + numSceneryItems++; + } while (!((mapElement++)->flags & MAP_ELEMENT_FLAG_LAST_TILE)); + } + } + + return min(numSceneryItems, 47) * 5; +} + +#pragma region Ride rating calculation functions + +static void ride_ratings_calculate_mine_train_coaster(rct_ride *ride) +{ + rating_tuple ratings, subRating; + int totalLength, time; + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + return; + + ride->var_198 = 16; + sub_655FD6(ride); + + // Base ratings + ratings.excitement = RIDE_RATING(2,90); + ratings.intensity = RIDE_RATING(2,30); + ratings.nausea = RIDE_RATING(2,10); + + // Apply length of ride factor + totalLength = (ride->length[0] + ride->length[1] + ride->length[2] + ride->length[3]) >> 16; + ratings.excitement += (min(6000, totalLength) * 764) >> 16; + + // Apply racing coaster factor + if (ride->depart_flags & RIDE_DEPART_SYNCHRONISE_WITH_ADJACENT_STATIONS) { + ratings.excitement += RIDE_RATING(0,40); + ratings.intensity += RIDE_RATING(0,05); + } + + // Apply length of train factor + ratings.excitement += ((ride->num_cars_per_train - 1) * 187245) >> 16; + + // Apply maximum speed factor + ratings.excitement += ((ride->max_speed >> 16) * 44281) >> 16; + ratings.intensity += ((ride->max_speed >> 16) * 88562) >> 16; + ratings.nausea += ((ride->max_speed >> 16) * 35424) >> 16; + + // Apply average speed factor + ratings.excitement += ((ride->average_speed >> 16) * 291271) >> 16; + ratings.intensity += ((ride->average_speed >> 16) * 436906) >> 16; + + // Apply ride duration factor + time = ride->time[0] + ride->time[1] + ride->time[2] + ride->time[3]; + ratings.excitement += (min(150, time) * 26214) >> 16; + + // Apply G forces factor + subRating = ride_ratings_get_gforce_ratings(ride); + ratings.excitement += (subRating.excitement * 40960) >> 16; + ratings.intensity += (subRating.intensity * 35746) >> 16; + ratings.nausea += (subRating.nausea * 49648) >> 16; + + // Apply ? + subRating = sub_65DDD1(ride); + ratings.excitement += (subRating.excitement * 29721) >> 16; + ratings.intensity += (subRating.intensity * 34767) >> 16; + ratings.nausea += (subRating.nausea * 45749) >> 16; + + // Apply drops factor + subRating = ride_ratings_get_drop_ratings(ride); + ratings.excitement += (subRating.excitement * 29127) >> 16; + ratings.intensity += (subRating.intensity * 46811) >> 16; + ratings.nausea += (subRating.nausea * 49152) >> 16; + + // Apply ? + subRating = sub_65E1C2(ride); + ratings.excitement += (subRating.excitement * 19275) >> 16; + ratings.intensity += (subRating.intensity * 32768) >> 16; + ratings.nausea += (subRating.nausea * 35108) >> 16; + + // Apply ? + ratings.excitement += (sub_65E277() * 21472) >> 16; + + ratings.excitement += (ride_ratings_get_scenery_score(ride) * 16732) >> 16; + + // Apply low highest drop penalty + if (ride->highest_drop_height < 8) { + ride->excitement /= 2; + ride->intensity /= 2; + ride->nausea /= 2; + } + + // Apply low max speed penalty + if (ride->max_speed < 0xA0000) { + ride->excitement /= 2; + ride->intensity /= 2; + ride->nausea /= 2; + } + + // Apply low maximum negative vertical G force penalty + if (ride->max_negative_vertical_g > FIXED_2DP(0,10)) { + ride->excitement /= 2; + ride->intensity /= 2; + ride->nausea /= 2; + } + + // Apply short ride penalty + if (ride->length[0] < 0x1720000) { + ride->excitement /= 2; + ride->intensity /= 2; + ride->nausea /= 2; + } + + // Apply low number of drops penalty + if ((ride->drops & 0x3F) < 2) { + ride->excitement /= 2; + ride->intensity /= 2; + ride->nausea /= 2; + } + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= sub_65E72D(ride) << 5; +} + +static void ride_ratings_calculate_maze(rct_ride *ride) +{ + rating_tuple ratings; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; + ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS; + ride->var_198 = 8; + sub_655FD6(ride); + + // Base ratings + ratings.excitement = RIDE_RATING(1,30); + ratings.intensity = RIDE_RATING(0,50); + ratings.nausea = RIDE_RATING(0,00); + + // Apply size factor + int unk = min(ride->maze_tiles, 100); + ratings.excitement += unk; + ratings.intensity += unk / 2; + + ratings.excitement += (ride_ratings_get_scenery_score(ride) * 22310) >> 16; + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= 0 << 5; +} + +static void ride_ratings_calculate_food_stall(rct_ride *ride) +{ + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; +} + +static void ride_ratings_calculate_drink_stall(rct_ride *ride) +{ + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; +} + +static void ride_ratings_calculate_shop(rct_ride *ride) +{ + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; +} + +static void ride_ratings_calculate_merry_go_round(rct_ride *ride) +{ + rating_tuple ratings; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; + ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS; + ride->var_198 = 16; + sub_655FD6(ride); + + int unk = ride->var_0D0 * 5; + ratings.excitement = unk + RIDE_RATING(0,60) + ((ride_ratings_get_scenery_score(ride) * 19521) >> 16); + ratings.intensity = unk + RIDE_RATING(0,15); + ratings.nausea = unk + RIDE_RATING(0,30); + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= 7 << 5; +} + +static void ride_ratings_calculate_information_kiosk(rct_ride *ride) +{ + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; +} + +static void ride_ratings_calculate_bathroom(rct_ride *ride) +{ + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; +} + +static void ride_ratings_calculate_ferris_wheel(rct_ride *ride) +{ + rating_tuple ratings; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; + ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS; + ride->var_198 = 16; + sub_655FD6(ride); + + int unk = ride->var_0D0 * 25; + ratings.excitement = unk + RIDE_RATING(0,60) + ((ride_ratings_get_scenery_score(ride) * 41831) >> 16); + ratings.intensity = unk + RIDE_RATING(0,25); + ratings.nausea = unk + RIDE_RATING(0,30); + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= 0 << 5; +} + +static void ride_ratings_calculate_elevator(rct_ride *ride) +{ + rating_tuple ratings; + int totalLength; + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + return; + + ride->var_198 = 15; + sub_655FD6(ride); + + // Base ratings + ratings.excitement = RIDE_RATING(1,11); + ratings.intensity = RIDE_RATING(0,35); + ratings.nausea = RIDE_RATING(0,30); + + // Apply length factor + totalLength = ride->length[0] + ride->length[1] + ride->length[2] + ride->length[3]; + ratings.excitement += ((totalLength >> 16) * 45875) >> 16; + ratings.excitement += (sub_65E277() * 11183) >> 16; + ratings.excitement += (ride_ratings_get_scenery_score(ride) * 83662) >> 16; + ratings.nausea += ((totalLength >> 16) * 26214) >> 16; + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= 7 << 5; + + if ((sub_65E72D(ride) >> 8) >= 5) + ride->excitement /= 4; +} + +static void ride_ratings_calculate_haunted_house(rct_ride *ride) +{ + rating_tuple ratings; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; + ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS; + ride->var_198 = 8; + sub_655FD6(ride); + + ratings.excitement = RIDE_RATING(3,41); + ratings.intensity = RIDE_RATING(1,53); + ratings.nausea = RIDE_RATING(0,10); + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= 0xE0; +} + +static void ride_ratings_calculate_mini_golf(rct_ride *ride) +{ + rating_tuple ratings, unkRating; + + // RCT2_CALLPROC_X(0x0065BF97, 0, 0, 0, 0, 0, (int)ride, 0); return; + + if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_TESTED)) + return; + + ride->var_198 = 0; + sub_655FD6(ride); + + // Base ratings + ratings.excitement = RIDE_RATING(1,50); + ratings.intensity = RIDE_RATING(0,90); + ratings.nausea = RIDE_RATING(0,00); + + // Apply length factor + int length = (ride->length[0] + ride->length[1] + ride->length[2] + ride->length[3]) >> 16; + ratings.excitement += (min(6000, length) * 873) >> 16; + + // Apply ? + unkRating = sub_65DDD1(ride); + ratings.excitement += (unkRating.excitement * 14860) >> 16; + + // Apply ? + unkRating = sub_65E1C2(ride); + ratings.excitement += (unkRating.excitement * 5140) >> 16; + ratings.intensity += (unkRating.intensity * 6553) >> 16; + ratings.nausea += (unkRating.nausea * 4681) >> 16; + + // Apply ? + ratings.excitement += (sub_65E277() * 15657) >> 16; + + ratings.excitement += (ride_ratings_get_scenery_score(ride) * 27887) >> 16; + + // Apply golf holes factor + ratings.excitement += (ride->holes & 0x1F) * 5; + + // Apply no golf holes penalty + if ((ride->inversions & 0x1F) == 0) { + ratings.excitement /= 8; + ratings.intensity /= 2; + ratings.nausea /= 2; + } + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= sub_65E72D(ride) << 5; +} + +static void ride_ratings_calculate_first_aid(rct_ride *ride) +{ + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; +} + +static void ride_ratings_calculate_crooked_house(rct_ride *ride) +{ + rating_tuple ratings; + + ride->lifecycle_flags |= RIDE_LIFECYCLE_TESTED; + ride->lifecycle_flags |= RIDE_LIFECYCLE_NO_RAW_STATS; + ride->var_198 = 5; + sub_655FD6(ride); + + ratings.excitement = RIDE_RATING(2,15); + ratings.intensity = RIDE_RATING(0,62); + ratings.nausea = RIDE_RATING(0,34); + + ride_ratings_apply_intensity_penalty(&ratings); + ride_ratings_apply_adjustments(ride, &ratings); + + ride->ratings = ratings; + + ride->upkeep_cost = ride_compute_upkeep(ride); + ride->var_14D |= 2; + + ride->inversions &= 0x1F; + ride->inversions |= 0xE0; +} + +#pragma endregion + +#pragma region Ride rating calculation function table + +// rct2: 0x0097E050 +static const ride_ratings_calculation ride_ratings_calculate_func_table[91] = { + NULL, // SPIRAL_ROLLER_COASTER + NULL, // STAND_UP_ROLLER_COASTER + NULL, // SUSPENDED_SWINGING_COASTER + NULL, // INVERTED_ROLLER_COASTER + NULL, // JUNIOR_ROLLER_COASTER + NULL, // MINIATURE_RAILWAY + NULL, // MONORAIL + NULL, // MINI_SUSPENDED_COASTER + NULL, // BUMPER_BOATS + NULL, // WOODEN_WILD_MOUSE + NULL, // STEEPLECHASE + NULL, // CAR_RIDE + NULL, // LAUNCHED_FREEFALL + NULL, // BOBSLEIGH_COASTER + NULL, // OBSERVATION_TOWER + NULL, // LOOPING_ROLLER_COASTER + NULL, // DINGHY_SLIDE + ride_ratings_calculate_mine_train_coaster, // MINE_TRAIN_COASTER + NULL, // CHAIRLIFT + NULL, // CORKSCREW_ROLLER_COASTER + ride_ratings_calculate_maze, // MAZE + NULL, // SPIRAL_SLIDE + NULL, // GO_KARTS + NULL, // LOG_FLUME + NULL, // RIVER_RAPIDS + NULL, // BUMPER_CARS + NULL, // PIRATE_SHIP + NULL, // SWINGING_INVERTER_SHIP + ride_ratings_calculate_food_stall, // FOOD_STALL + NULL, // 1D + ride_ratings_calculate_drink_stall, // DRINK_STALL + NULL, // 1F + ride_ratings_calculate_shop, // SHOP + ride_ratings_calculate_merry_go_round, // MERRY_GO_ROUND + NULL, // 22 + ride_ratings_calculate_information_kiosk, // INFORMATION_KIOSK + ride_ratings_calculate_bathroom, // BATHROOM + ride_ratings_calculate_ferris_wheel, // FERRIS_WHEEL + NULL, // MOTION_SIMULATOR + NULL, // 3D_CINEMA + NULL, // TOP_SPIN + NULL, // SPACE_RINGS + NULL, // REVERSE_FREEFALL_COASTER + ride_ratings_calculate_elevator, // ELEVATOR + NULL, // VERTICAL_DROP_ROLLER_COASTER + NULL, // ATM + NULL, // TWIST + ride_ratings_calculate_haunted_house, // HAUNTED_HOUSE + ride_ratings_calculate_first_aid, // FIRST_AID + NULL, // CIRCUS_SHOW + NULL, // GHOST_TRAIN + NULL, // TWISTER_ROLLER_COASTER + NULL, // WOODEN_ROLLER_COASTER + NULL, // SIDE_FRICTION_ROLLER_COASTER + NULL, // WILD_MOUSE + NULL, // MULTI_DIMENSION_ROLLER_COASTER + NULL, // 38 + NULL, // FLYING_ROLLER_COASTER + NULL, // 3A + NULL, // VIRGINIA_REEL + NULL, // SPLASH_BOATS + NULL, // MINI_HELICOPTERS + NULL, // LAY_DOWN_ROLLER_COASTER + NULL, // SUSPENDED_MONORAIL + NULL, // 40 + NULL, // REVERSER_ROLLER_COASTER + NULL, // HEARTLINE_TWISTER_COASTER + ride_ratings_calculate_mini_golf, // MINI_GOLF + NULL, // GIGA_COASTER + NULL, // ROTO_DROP + NULL, // FLYING_SAUCERS + ride_ratings_calculate_crooked_house, // CROOKED_HOUSE + NULL, // MONORAIL_CYCLES + NULL, // COMPACT_INVERTED_COASTER + NULL, // WATER_COASTER + NULL, // AIR_POWERED_VERTICAL_COASTER + NULL, // INVERTED_HAIRPIN_COASTER + NULL, // MAGIC_CARPET + NULL, // SUBMARINE_RIDE + NULL, // RIVER_RAFTS + NULL, // 50 + NULL, // ENTERPRISE + NULL, // 52 + NULL, // 53 + NULL, // 54 + NULL, // 55 + NULL, // INVERTED_IMPULSE_COASTER + NULL, // MINI_ROLLER_COASTER + NULL, // MINE_RIDE + NULL, // LIM_LAUNCHED_ROLLER_COASTER + NULL, // 90 +}; + +#pragma endregion \ No newline at end of file diff --git a/src/ride/ride_ratings.h b/src/ride/ride_ratings.h index b2f5509e5b..5baa99e2e9 100644 --- a/src/ride/ride_ratings.h +++ b/src/ride/ride_ratings.h @@ -25,11 +25,5 @@ #include "ride.h" void ride_ratings_update_all(); -void crooked_house_excitement(rct_ride *ride); -void sub_655FD6(rct_ride *ride); -ride_rating apply_intensity_penalty(ride_rating excitement, ride_rating intensity); -rating_tuple per_ride_rating_adjustments(rct_ride *ride, ride_rating excitement, - ride_rating intensity, ride_rating nausea); -uint16 compute_upkeep(rct_ride *ride); #endif \ No newline at end of file diff --git a/src/windows/ride.c b/src/windows/ride.c index 6cec49b004..013c0eb255 100644 --- a/src/windows/ride.c +++ b/src/windows/ride.c @@ -4602,7 +4602,7 @@ static void window_ride_measurements_paint() if (!(ride->lifecycle_flags & RIDE_LIFECYCLE_NO_RAW_STATS)) { if (ride->type == RIDE_TYPE_MINI_GOLF) { // Holes - holes = ride->inversions & 0x1F; + holes = ride->holes & 0x1F; gfx_draw_string_left(dpi, STR_HOLES, &holes, 0, x, y); y += 10; } else {