#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers /***************************************************************************** * OpenRCT2, an open source clone of Roller Coaster Tycoon 2. * * OpenRCT2 is the work of many authors, a full list can be found in contributors.md * For more information, visit https://github.com/OpenRCT2/OpenRCT2 * * OpenRCT2 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * A full copy of the GNU General Public License can be found in licence.txt *****************************************************************************/ #pragma endregion #include "../game.h" #include "../scenario/scenario.h" #include "Fountain.h" #include "map.h" #include "scenery.h" #include "sprite.h" #include "footpath.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 }, { 0, 32 }, { 32, 32 }, { 32, 32 }, { 32, 0 }, { 0, 0 }, { 0, 32 } }; // 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(sint32 type, sint32 x, sint32 y, sint32 z); static void jumping_fountain_goto_edge(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections); static void jumping_fountain_bounce(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections); static void jumping_fountain_split(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections); static void jumping_fountain_random(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections); static void jumping_fountain_create_next(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 direction); /** * * rct2: 0x00673DBA (water) * rct2: 0x00673F51 (snow) */ void jumping_fountain_begin(sint32 type, sint32 x, sint32 y, rct_map_element *mapElement) { sint32 i, randomIndex; sint32 z = mapElement->base_height * 8; // Change pattern approximately every 51 seconds sint32 pattern = (gCurrentTicks >> 11) & 7; switch (pattern) { case PATTERN_CYCLIC_SQUARES: // 0, 1, 2, 3 for (i = 0; i < 4; i++) { jumping_fountain_create( type, x + dword_97F020[i].x, y + dword_97F020[i].y, z, _fountainDirections[i], _fountainDirectionFlags[i] | _fountainPatternFlags[pattern], 0 ); } break; case PATTERN_BOUNCING_PAIRS: // random [0, 2 or 1, 3] randomIndex = scenario_rand() & 1; for (i = randomIndex; i < 4; i += 2) { jumping_fountain_create( type, x + dword_97F020[i].x, y + dword_97F020[i].y, z, _fountainDirections[i], _fountainDirectionFlags[i] | _fountainPatternFlags[pattern], 0 ); } break; case PATTERN_RACING_PAIRS: // random [0 - 3 and 4 - 7] z = mapElement->base_height * 8; randomIndex = scenario_rand() & 3; jumping_fountain_create( type, x + dword_97F020[randomIndex].x, y + dword_97F020[randomIndex].y, z, _fountainDirections[randomIndex], _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0 ); randomIndex += 4; jumping_fountain_create( type, x + dword_97F020[randomIndex].x, y + dword_97F020[randomIndex].y, z, _fountainDirections[randomIndex], _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0 ); break; default: // random [0 - 7] randomIndex = scenario_rand() & 7; jumping_fountain_create( type, x + dword_97F020[randomIndex].x, y + dword_97F020[randomIndex].y, z, _fountainDirections[randomIndex], _fountainDirectionFlags[randomIndex] | _fountainPatternFlags[pattern], 0 ); break; } } /** * * rct2: 0x0067396A (water) * rct2: 0x006739A4 (snow) */ void jumping_fountain_create(sint32 type, sint32 x, sint32 y, sint32 z, sint32 direction, sint32 flags, sint32 iteration) { rct_jumping_fountain *jumpingFountain; jumpingFountain = (rct_jumping_fountain*)create_sprite(SPRITE_IDENTIFIER_MISC); if (jumpingFountain == NULL) return; jumpingFountain->iteration = iteration; jumpingFountain->var_2E = direction; jumpingFountain->fountain_flags = flags; jumpingFountain->sprite_direction = direction << 3; jumpingFountain->var_14 = 33; jumpingFountain->var_09 = 36; jumpingFountain->var_15 = 12; jumpingFountain->sprite_identifier = SPRITE_IDENTIFIER_MISC; sprite_move(x, y, z, (rct_sprite*)jumpingFountain); jumpingFountain->misc_identifier = type == JUMPING_FOUNTAIN_TYPE_SNOW ? SPRITE_MISC_JUMPING_FOUNTAIN_SNOW : SPRITE_MISC_JUMPING_FOUNTAIN_WATER; jumpingFountain->var_26 = 0; } /** * * rct2: 0x006733CB (water) * rct2: 0x00673407 (snow) */ void jumping_fountain_update(rct_jumping_fountain *jumpingFountain) { sint32 original_var_26a = jumpingFountain->var_26a; jumpingFountain->var_26a += 160; if (original_var_26a <= 255 - 160) return; invalidate_sprite_0((rct_sprite*)jumpingFountain); jumpingFountain->var_26b++; switch (jumpingFountain->misc_identifier) { case SPRITE_MISC_JUMPING_FOUNTAIN_WATER: if (jumpingFountain->var_26b == 11 && (jumpingFountain->fountain_flags & FOUNTAIN_FLAG_FAST)) jumping_fountain_continue(jumpingFountain); if (jumpingFountain->var_26b == 16 && !(jumpingFountain->fountain_flags & FOUNTAIN_FLAG_FAST)) jumping_fountain_continue(jumpingFountain); break; case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW: 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) { sint32 direction = (jumpingFountain->sprite_direction >> 3) & 7; sint32 x = jumpingFountain->x + TileDirectionDelta[direction].x; sint32 y = jumpingFountain->y + TileDirectionDelta[direction].y; sint32 z = jumpingFountain->z; sint32 type = jumpingFountain->misc_identifier == SPRITE_MISC_JUMPING_FOUNTAIN_SNOW ? JUMPING_FOUNTAIN_TYPE_SNOW : JUMPING_FOUNTAIN_TYPE_WATER; sint32 availableDirections = 0; for (sint32 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->fountain_flags & FOUNTAIN_FLAG_TERMINATE) return; if (jumpingFountain->fountain_flags & FOUNTAIN_FLAG_GOTO_EDGE) { jumping_fountain_goto_edge(jumpingFountain, x, y, z, availableDirections); return; } if (jumpingFountain->fountain_flags & FOUNTAIN_FLAG_BOUNCE) { jumping_fountain_bounce(jumpingFountain, x, y, z, availableDirections); return; } if (jumpingFountain->fountain_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(sint32 type, sint32 x, sint32 y, sint32 z) { z = z >> 3; sint32 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 (footpath_element_path_scenery_is_ghost(mapElement)) continue; if (!footpath_element_has_path_scenery(mapElement)) continue; uint8 additionIndex = footpath_element_get_path_scenery_index(mapElement); rct_scenery_entry *sceneryEntry = get_footpath_item_entry(additionIndex); if (!(sceneryEntry->path_bit.flags & pathBitFlagMask)) continue; return true; } while (!map_element_is_last_for_tile(mapElement++)); return false; } /** * * rct2: 0x00673B6E */ static void jumping_fountain_goto_edge(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections) { sint32 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->fountain_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); } /** * * rct2: 0x00673B45 */ static void jumping_fountain_bounce(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections) { jumpingFountain->iteration++; if (jumpingFountain->iteration >= 8) return; sint32 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); } /** * * rct2: 0x00673ACE */ static void jumping_fountain_split(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections) { if (jumpingFountain->iteration >= 3) return; sint32 type = jumpingFountain->misc_identifier == SPRITE_MISC_JUMPING_FOUNTAIN_SNOW ? JUMPING_FOUNTAIN_TYPE_SNOW : JUMPING_FOUNTAIN_TYPE_WATER; sint32 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->fountain_flags & ~FOUNTAIN_FLAG_7, jumpingFountain->iteration + 1 ); } direction++; if (availableDirections & (1 << direction)) { jumping_fountain_create( type, x, y, z, direction >> 1, jumpingFountain->fountain_flags | FOUNTAIN_FLAG_7, jumpingFountain->iteration + 1 ); } } } /** * * rct2: 0x00673AAC */ static void jumping_fountain_random(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 availableDirections) { uint32 randomIndex = scenario_rand(); if ((randomIndex & 0xFFFF) < 0x2000) return; sint32 direction = randomIndex & 7; while (!(availableDirections & (1 << direction))) direction = (direction + 1) & 7; jumping_fountain_create_next(jumpingFountain, x, y, z, direction); } /** * * rct2: 0x00673B45 */ static void jumping_fountain_create_next(rct_jumping_fountain *jumpingFountain, sint32 x, sint32 y, sint32 z, sint32 direction) { sint32 flags = jumpingFountain->fountain_flags & ~FOUNTAIN_FLAG_7; if (direction & 1) flags |= FOUNTAIN_FLAG_7; sint32 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); }