diff --git a/src/world/fountain.c b/src/world/fountain.c index e7583f9a6b..7a533c3040 100644 --- a/src/world/fountain.c +++ b/src/world/fountain.c @@ -25,6 +25,37 @@ #include "scenery.h" #include "sprite.h" +enum { + PATTERN_CYCLIC_SQUARES, + PATTERN_CONTINUOUS_CHASERS, + PATTERN_BOUNCING_PAIRS, + PATTERN_SPROUTING_BLOOMS, + PATTERN_RACING_PAIRS, + PATTERN_SPLITTING_CHASERS, + PATTERN_DOPEY_JUMPERS, + PATTERN_FAST_RANDOM_CHASERS +}; + +enum { + FOUNTAIN_FLAG_FAST = 1 << 0, + FOUNTAIN_FLAG_GOTO_EDGE = 1 << 1, + FOUNTAIN_FLAG_SPLIT = 1 << 2, + FOUNTAIN_FLAG_TERMINATE = 1 << 3, + FOUNTAIN_FLAG_BOUNCE = 1 << 4, + FOUNTAIN_FLAG_7 = 1 << 7 +}; + +const rct_xy16 dword_97F000[] = { + { -32, 0 }, + { -32, -32 }, + { 0, 0 }, + { -32, 0 }, + { 0, 0 }, + { 0, -32 }, + { 0, -32 }, + { -32, -32 }, +}; + const rct_xy16 dword_97F020[] = { { 32, 0 }, { 0, 0 }, @@ -36,9 +67,32 @@ const rct_xy16 dword_97F020[] = { { 0, 32 } }; -const uint8 byte_97F040[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; -const uint8 byte_97F048[] = { 0, 0, 128, 128, 128, 128, 0, 0 }; -const uint8 byte_97F050[] = { 8, 3, 16, 5, 2, 7, 0, 1 }; +// rct2: 0x0097F040 +const uint8 _fountainDirections[] = { 0, 1, 2, 3, 0, 1, 2, 3 }; + +// rct2: 0x0097F048 +const uint8 _fountainDirectionFlags[] = { 0, 0, FOUNTAIN_FLAG_7, FOUNTAIN_FLAG_7, FOUNTAIN_FLAG_7, FOUNTAIN_FLAG_7, 0, 0 }; + +// rct2: 0x0097F050 +const uint8 _fountainPatternFlags[] = { + FOUNTAIN_FLAG_TERMINATE, // PATTERN_CYCLIC_SQUARES + FOUNTAIN_FLAG_FAST | FOUNTAIN_FLAG_GOTO_EDGE, // PATTERN_CONTINUOUS_CHASERS + FOUNTAIN_FLAG_BOUNCE, // PATTERN_BOUNCING_PAIRS + FOUNTAIN_FLAG_FAST | FOUNTAIN_FLAG_SPLIT, // PATTERN_SPROUTING_BLOOMS + FOUNTAIN_FLAG_GOTO_EDGE, // PATTERN_RACING_PAIRS + FOUNTAIN_FLAG_FAST | FOUNTAIN_FLAG_GOTO_EDGE | FOUNTAIN_FLAG_SPLIT, // PATTERN_SPLITTING_CHASERS + 0, // PATTERN_DOPEY_JUMPERS + FOUNTAIN_FLAG_FAST // PATTERN_FAST_RANDOM_CHASERS +}; + +static void jumping_fountain_continue(rct_jumping_fountain *jumpingFountain); +static bool is_jumping_fountain(int type, int x, int y, int z); + +static void jumping_fountain_goto_edge(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections); +static void jumping_fountain_bounce(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections); +static void jumping_fountain_split(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections); +static void jumping_fountain_random(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections); +static void jumping_fountain_create_next(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int direction); /** * @@ -49,9 +103,11 @@ void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement) { int i, randomIndex; int z = mapElement->base_height * 8; - uint32 ticks = (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 11) & 7; - switch (ticks) { - case 0: + + // Change pattern approximately every 51 seconds + int pattern = (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) >> 11) & 7; + switch (pattern) { + case PATTERN_CYCLIC_SQUARES: // 0, 1, 2, 3 for (i = 0; i < 4; i++) { jumping_fountain_create( @@ -59,13 +115,13 @@ void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement) x + dword_97F020[i].x, y + dword_97F020[i].y, z, - byte_97F040[i], - byte_97F048[i] | byte_97F050[ticks], + _fountainDirections[i], + _fountainDirectionFlags[i] | _fountainPatternFlags[pattern], 0 ); } break; - case 2: + case PATTERN_BOUNCING_PAIRS: // random [0, 2 or 1, 3] randomIndex = scenario_rand() & 1; for (i = randomIndex; i < 4; i += 2) { @@ -74,13 +130,13 @@ void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement) x + dword_97F020[i].x, y + dword_97F020[i].y, z, - byte_97F040[i], - byte_97F048[i] | byte_97F050[ticks], + _fountainDirections[i], + _fountainDirectionFlags[i] | _fountainPatternFlags[pattern], 0 ); } break; - case 4: + case PATTERN_RACING_PAIRS: // random [0 - 3 and 4 - 7] z = mapElement->base_height * 8; randomIndex = scenario_rand() & 3; @@ -89,8 +145,8 @@ void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement) x + dword_97F020[randomIndex].x, y + dword_97F020[randomIndex].y, z, - byte_97F040[randomIndex], - byte_97F048[randomIndex] | byte_97F050[ticks], + _fountainDirections[randomIndex], + _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0 ); randomIndex += 4; @@ -99,8 +155,8 @@ void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement) x + dword_97F020[randomIndex].x, y + dword_97F020[randomIndex].y, z, - byte_97F040[randomIndex], - byte_97F048[randomIndex] | byte_97F050[ticks], + _fountainDirections[randomIndex], + _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0 ); break; @@ -112,8 +168,8 @@ void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement) x + dword_97F020[randomIndex].x, y + dword_97F020[randomIndex].y, z, - byte_97F040[randomIndex], - byte_97F048[randomIndex] | byte_97F050[ticks], + _fountainDirections[randomIndex], + _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0 ); break; @@ -125,7 +181,7 @@ void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement) * rct2: 0x0067396A (water) * rct2: 0x006739A4 (snow) */ -void jumping_fountain_create(int type, int x, int y, int z, uint16 bl, uint16 bh, uint16 bp) +void jumping_fountain_create(int type, int x, int y, int z, int direction, int flags, int iteration) { rct_jumping_fountain *jumpingFountain; @@ -133,9 +189,10 @@ void jumping_fountain_create(int type, int x, int y, int z, uint16 bl, uint16 bh if (jumpingFountain == NULL) return; - jumpingFountain->var_46 = bp; - jumpingFountain->var_2E = (bh << 8) | bl; - jumpingFountain->sprite_direction = bl << 3; + jumpingFountain->iteration = iteration; + jumpingFountain->var_2E = direction; + jumpingFountain->flags = flags; + jumpingFountain->sprite_direction = direction << 3; jumpingFountain->var_14 = 33; jumpingFountain->var_09 = 36; jumpingFountain->var_15 = 12; @@ -154,12 +211,235 @@ void jumping_fountain_create(int type, int x, int y, int z, uint16 bl, uint16 bh */ void jumping_fountain_update(rct_jumping_fountain *jumpingFountain) { + int original_var_26a = jumpingFountain->var_26a; + jumpingFountain->var_26a += 160; + if (original_var_26a <= 255 - 160) + return; + + RCT2_CALLPROC_X(0x006EC60B, 0, 0, 0, 0, (int)jumpingFountain, 0, 0); + jumpingFountain->var_26b++; + switch (jumpingFountain->misc_identifier) { case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: - RCT2_CALLPROC_X(0x006733CB, 0, 0, 0, 0, (int)jumpingFountain, 0, 0); + if (jumpingFountain->var_26b == 11 && (jumpingFountain->flags & FOUNTAIN_FLAG_FAST)) + jumping_fountain_continue(jumpingFountain); + + if (jumpingFountain->var_26b == 16 && !(jumpingFountain->flags & FOUNTAIN_FLAG_FAST)) + jumping_fountain_continue(jumpingFountain); break; case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW: - RCT2_CALLPROC_X(0x00673407, 0, 0, 0, 0, (int)jumpingFountain, 0, 0); + if (jumpingFountain->var_26b == 16) + jumping_fountain_continue(jumpingFountain); break; } + + if (jumpingFountain->var_26b == 16) + sprite_remove((rct_sprite*)jumpingFountain); +} + +/** + * + * rct2: 0x006739DE (water) + * rct2: 0x00673BCC (snow) + */ +static void jumping_fountain_continue(rct_jumping_fountain *jumpingFountain) +{ + int direction = (jumpingFountain->sprite_direction >> 3) & 7; + int x = jumpingFountain->x + TileDirectionDelta[direction].x; + int y = jumpingFountain->y + TileDirectionDelta[direction].y; + int z = jumpingFountain->z; + + int type = jumpingFountain->misc_identifier == SPRITE_MISC_JUMPING_FOUNTAIN_SNOW ? + JUMPING_FOUNTAIN_TYPE_SNOW : + JUMPING_FOUNTAIN_TYPE_WATER; + + int availableDirections = 0; + for (int i = 0; i < 8; i++) { + if (is_jumping_fountain(type, x + dword_97F000[i].x, y + dword_97F000[i].y, z)) + availableDirections |= 1 << i; + } + + if (availableDirections == 0) + return; + + if (jumpingFountain->flags & FOUNTAIN_FLAG_TERMINATE) + return; + + if (jumpingFountain->flags & FOUNTAIN_FLAG_GOTO_EDGE) { + jumping_fountain_goto_edge(jumpingFountain, x, y, z, availableDirections); + return; + } + + if (jumpingFountain->flags & FOUNTAIN_FLAG_BOUNCE) { + jumping_fountain_bounce(jumpingFountain, x, y, z, availableDirections); + return; + } + + if (jumpingFountain->flags & FOUNTAIN_FLAG_SPLIT) { + jumping_fountain_split(jumpingFountain, x, y, z, availableDirections); + return; + } + + jumping_fountain_random(jumpingFountain, x, y, z, availableDirections); +} + +static bool is_jumping_fountain(int type, int x, int y, int z) +{ + z = z >> 3; + + int pathBitFlagMask = type == JUMPING_FOUNTAIN_TYPE_SNOW ? + PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW : + PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER; + + rct_map_element *mapElement = map_get_first_element_at(x >> 5, y >> 5); + do { + if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH) + continue; + if (mapElement->base_height != z) + continue; + if (mapElement->properties.path.additions & 0x80) + continue; + + int additions = mapElement->properties.path.additions & 0x0F; + if (additions == 0) + continue; + + rct_scenery_entry *sceneryEntry = g_pathBitSceneryEntries[additions - 1]; + if (!(sceneryEntry->path_bit.var_06 & pathBitFlagMask)) + continue; + + return true; + } while (!map_element_is_last_for_tile(mapElement++)); + + return false; +} + +/** + * + * rct: 0x00673B6E + */ +static void jumping_fountain_goto_edge(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections) +{ + int direction = (jumpingFountain->sprite_direction >> 3) << 1; + if (availableDirections & (1 << direction)) { + jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + return; + } + + direction++; + if (availableDirections & (1 << direction)) { + jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + return; + } + + uint32 randomIndex = scenario_rand(); + if ((randomIndex & 0xFFFF) < 0x3333) + return; + + if (jumpingFountain->flags & FOUNTAIN_FLAG_SPLIT) { + jumping_fountain_split(jumpingFountain, x, y, z, availableDirections); + return; + } + + direction = randomIndex & 7; + while (!(availableDirections & (1 << direction))) + direction = (direction + 1) & 7; + + jumping_fountain_create_next(jumpingFountain, x, y, z, direction); +} + +/** + * + * rct: 0x00673B45 + */ +static void jumping_fountain_bounce(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections) +{ + jumpingFountain->iteration++; + if (jumpingFountain->iteration >= 8) + return; + + int direction = ((jumpingFountain->sprite_direction >> 3) ^ 2) << 1; + if (availableDirections & (1 << direction)) { + jumping_fountain_create_next(jumpingFountain, x, y, z, direction); + return; + } + + direction++; + if (availableDirections & (1 << direction)) + jumping_fountain_create_next(jumpingFountain, x, y, z, direction); +} + +/** + * + * rct: 0x00673ACE + */ +static void jumping_fountain_split(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections) +{ + if (jumpingFountain->iteration >= 3) + return; + + int type = jumpingFountain->misc_identifier == SPRITE_MISC_JUMPING_FOUNTAIN_SNOW ? + JUMPING_FOUNTAIN_TYPE_SNOW : + JUMPING_FOUNTAIN_TYPE_WATER; + + int direction = ((jumpingFountain->sprite_direction >> 3) ^ 2) << 1; + availableDirections &= ~(1 << direction); + direction++; + availableDirections &= ~(1 << direction); + + for (direction = 0; direction < 8; direction++) { + if (availableDirections & (1 << direction)) { + jumping_fountain_create( + type, + x, y, z, + direction >> 1, + jumpingFountain->flags & ~FOUNTAIN_FLAG_7, + jumpingFountain->iteration + 1 + ); + } + direction++; + if (availableDirections & (1 << direction)) { + jumping_fountain_create( + type, + x, y, z, + direction >> 1, + jumpingFountain->flags | FOUNTAIN_FLAG_7, + jumpingFountain->iteration + 1 + ); + } + } +} + +/** + * + * rct: 0x00673AAC + */ +static void jumping_fountain_random(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int availableDirections) +{ + uint32 randomIndex = scenario_rand(); + if ((randomIndex & 0xFFFF) < 0x2000) + return; + + int direction = randomIndex & 7; + while (!(availableDirections & (1 << direction))) + direction = (direction + 1) & 7; + + jumping_fountain_create_next(jumpingFountain, x, y, z, direction); +} + +/** + * + * rct: 0x00673B45 + */ +static void jumping_fountain_create_next(rct_jumping_fountain *jumpingFountain, int x, int y, int z, int direction) +{ + int flags = jumpingFountain->flags & ~FOUNTAIN_FLAG_7; + if (direction & 1) + flags |= FOUNTAIN_FLAG_7; + + int type = jumpingFountain->misc_identifier == SPRITE_MISC_JUMPING_FOUNTAIN_SNOW ? + JUMPING_FOUNTAIN_TYPE_SNOW : + JUMPING_FOUNTAIN_TYPE_WATER; + + jumping_fountain_create(type, x, y, z, direction >> 1, flags, jumpingFountain->iteration); } \ No newline at end of file diff --git a/src/world/fountain.h b/src/world/fountain.h index 9703d6e4f1..d7b63fe148 100644 --- a/src/world/fountain.h +++ b/src/world/fountain.h @@ -31,7 +31,7 @@ enum { }; void jumping_fountain_begin(int type, int x, int y, rct_map_element *mapElement); -void jumping_fountain_create(int type, int x, int y, int z, uint16 bl, uint16 bh, uint16 bp); +void jumping_fountain_create(int type, int x, int y, int z, int direction, int flags, int iteration); void jumping_fountain_update(rct_jumping_fountain *jumpingFountain); #endif diff --git a/src/world/sprite.h b/src/world/sprite.h index b478e5c508..f9a43d111f 100644 --- a/src/world/sprite.h +++ b/src/world/sprite.h @@ -134,19 +134,29 @@ typedef struct { uint16 previous; // 0x06 uint8 linked_list_type_offset; // 0x08 Valid values are SPRITE_LINKEDLIST_OFFSET_... uint8 var_09; - uint8 pad_0A[0xA]; + uint8 pad_0A[0x4]; + sint16 x; // 0x0E + sint16 y; // 0x10 + sint16 z; // 0x12 uint8 var_14; uint8 var_15; uint8 pad_16[0x8]; uint8 sprite_direction; // 0x1E uint8 pad_1F[0x7]; - uint16 var_26; + union { + uint16 var_26; + struct { + uint8 var_26a; + uint8 var_26b; + }; + }; uint8 pad_28[0x6]; - uint16 var_2E; + uint8 var_2E; + uint8 flags; sint16 target_x; // 0x30 sint16 target_y; // 0x32 uint8 pad_34[0x12]; - uint16 var_46; + uint16 iteration; } rct_jumping_fountain; /**