mirror of https://github.com/OpenRCT2/OpenRCT2.git
350 lines
8.2 KiB
C
350 lines
8.2 KiB
C
#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 "../audio/audio.h"
|
|
#include "../game.h"
|
|
#include "../localisation/date.h"
|
|
#include "../scenario.h"
|
|
#include "sprite.h"
|
|
|
|
enum {
|
|
DUCK_STATE_FLY_TO_WATER,
|
|
DUCK_STATE_SWIM,
|
|
DUCK_STATE_DRINK,
|
|
DUCK_STATE_DOUBLE_DRINK,
|
|
DUCK_STATE_FLY_AWAY
|
|
};
|
|
|
|
static void duck_update_fly_to_water(rct_duck *duck);
|
|
static void duck_update_swim(rct_duck *duck);
|
|
static void duck_update_drink(rct_duck *duck);
|
|
static void duck_update_double_drink(rct_duck *duck);
|
|
static void duck_update_fly_away(rct_duck *duck);
|
|
|
|
// rct2: 0x009A3B04
|
|
static const rct_xy16 duck_move_offset[] = {
|
|
{ -1, 0 },
|
|
{ 0, 1 },
|
|
{ 1, 0 },
|
|
{ 0, -1 }
|
|
};
|
|
|
|
/** rct2: 0x0097F06C */
|
|
static const uint8 duck_fly_to_water_animation[] = {
|
|
8, 9, 10, 11, 12, 13
|
|
};
|
|
|
|
/** rct2: 0x0097F072 */
|
|
static const uint8 duck_swim_animation[] = {
|
|
0
|
|
};
|
|
|
|
// rct2: 0x0097F073
|
|
static const uint8 duck_drink_animation[] = {
|
|
1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0xFF
|
|
};
|
|
|
|
// rct2: 0x0097F08C
|
|
static const uint8 duck_double_drink_animation[] = {
|
|
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6,
|
|
6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 0, 0, 0, 0, 0xFF
|
|
};
|
|
|
|
/** rct2: 0x0097F0B4 */
|
|
static const uint8 duck_fly_away_animation[] = {
|
|
8, 9, 10, 11, 12, 13
|
|
};
|
|
|
|
/** rct2: 0x0097F058 */
|
|
const uint8 * duck_animations[] = {
|
|
duck_fly_to_water_animation, // DUCK_STATE_FLY_TO_WATER
|
|
duck_swim_animation, // DUCK_STATE_SWIM
|
|
duck_drink_animation, // DUCK_STATE_DRINK
|
|
duck_double_drink_animation, // DUCK_STATE_DOUBLE_DRINK
|
|
duck_fly_away_animation, // DUCK_STATE_FLY_AWAY
|
|
};
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x0067440F
|
|
*/
|
|
void create_duck(int targetX, int targetY)
|
|
{
|
|
rct_sprite* sprite = create_sprite(2);
|
|
if (sprite != NULL) {
|
|
sprite->duck.sprite_identifier = SPRITE_IDENTIFIER_MISC;
|
|
sprite->duck.misc_identifier = SPRITE_MISC_DUCK;
|
|
sprite->duck.var_14 = 9;
|
|
sprite->duck.var_09 = 0xC;
|
|
sprite->duck.var_15 = 9;
|
|
int offset_xy = scenario_rand() & 0x1E;
|
|
targetX += offset_xy;
|
|
targetY += offset_xy;
|
|
sprite->duck.target_x = targetX;
|
|
sprite->duck.target_y = targetY;
|
|
uint8 direction = scenario_rand() & 3;
|
|
switch (direction) {
|
|
case 0:
|
|
targetX = 8191 - (scenario_rand() & 0x3F);
|
|
break;
|
|
case 1:
|
|
targetY = scenario_rand() & 0x3F;
|
|
break;
|
|
case 2:
|
|
targetX = scenario_rand() & 0x3F;
|
|
break;
|
|
case 3:
|
|
targetY = 8191 - (scenario_rand() & 0x3F);
|
|
break;
|
|
}
|
|
sprite->duck.sprite_direction = direction << 3;
|
|
sprite_move(targetX, targetY, 496, sprite);
|
|
sprite->duck.state = DUCK_STATE_FLY_TO_WATER;
|
|
sprite->duck.frame = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006740E8
|
|
*/
|
|
void duck_update(rct_duck *duck)
|
|
{
|
|
switch (duck->state) {
|
|
case DUCK_STATE_FLY_TO_WATER:
|
|
duck_update_fly_to_water(duck);
|
|
break;
|
|
case DUCK_STATE_SWIM:
|
|
duck_update_swim(duck);
|
|
break;
|
|
case DUCK_STATE_DRINK:
|
|
duck_update_drink(duck);
|
|
break;
|
|
case DUCK_STATE_DOUBLE_DRINK:
|
|
duck_update_double_drink(duck);
|
|
break;
|
|
case DUCK_STATE_FLY_AWAY:
|
|
duck_update_fly_away(duck);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void duck_invalidate(rct_duck *duck)
|
|
{
|
|
invalidate_sprite_0((rct_sprite*)duck);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674108
|
|
*/
|
|
static void duck_update_fly_to_water(rct_duck *duck)
|
|
{
|
|
if (gCurrentTicks & 3)
|
|
return;
|
|
|
|
duck->frame++;
|
|
if (duck->frame >= countof(duck_fly_to_water_animation))
|
|
duck->frame = 0;
|
|
|
|
duck_invalidate(duck);
|
|
int manhattanDistance = abs(duck->target_x - duck->x) + abs(duck->target_y - duck->y);
|
|
int direction = duck->sprite_direction >> 3;
|
|
int x = duck->x + duck_move_offset[direction].x;
|
|
int y = duck->y + duck_move_offset[direction].y;
|
|
int manhattanDistanceN = abs(duck->target_x - x) + abs(duck->target_y - y);
|
|
|
|
rct_map_element *mapElement = map_get_surface_element_at(duck->target_x >> 5, duck->target_y >> 5);
|
|
int waterHeight = mapElement->properties.surface.terrain & 0x1F;
|
|
if (waterHeight == 0) {
|
|
duck->state = DUCK_STATE_FLY_AWAY;
|
|
duck_update_fly_away(duck);
|
|
return;
|
|
}
|
|
waterHeight <<= 4;
|
|
int z = abs(duck->z - waterHeight);
|
|
|
|
if (manhattanDistanceN <= manhattanDistance) {
|
|
if (z > manhattanDistanceN) {
|
|
z = duck->z - 2;
|
|
if (waterHeight >= duck->z)
|
|
z += 4;
|
|
|
|
duck->frame = 1;
|
|
} else {
|
|
z = duck->z;
|
|
}
|
|
sprite_move(x, y, z, (rct_sprite*)duck);
|
|
duck_invalidate(duck);
|
|
} else {
|
|
if (z > 4) {
|
|
duck->state = DUCK_STATE_FLY_AWAY;
|
|
duck_update_fly_away(duck);
|
|
} else {
|
|
duck->state = DUCK_STATE_SWIM;
|
|
duck->frame = 0;
|
|
duck_update_swim(duck);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674282
|
|
*/
|
|
static void duck_update_swim(rct_duck *duck)
|
|
{
|
|
if ((gCurrentTicks + duck->sprite_index) & 3)
|
|
return;
|
|
|
|
uint32 randomNumber = scenario_rand();
|
|
if ((randomNumber & 0xFFFF) < 0x666) {
|
|
if (randomNumber & 0x80000000) {
|
|
duck->state = DUCK_STATE_DOUBLE_DRINK;
|
|
duck->frame = -1;
|
|
duck_update_double_drink(duck);
|
|
} else {
|
|
duck->state = DUCK_STATE_DRINK;
|
|
duck->frame = -1;
|
|
duck_update_drink(duck);
|
|
}
|
|
return;
|
|
}
|
|
|
|
int currentMonth = date_get_month(gDateMonthsElapsed);
|
|
if (currentMonth >= MONTH_SEPTEMBER && (randomNumber >> 16) < 218) {
|
|
duck->state = DUCK_STATE_FLY_AWAY;
|
|
duck_update_fly_away(duck);
|
|
return;
|
|
}
|
|
|
|
duck_invalidate(duck);
|
|
int landZ = map_element_height(duck->x, duck->y);
|
|
int waterZ = (landZ >> 16) & 0xFFFF;
|
|
landZ &= 0xFFFF;
|
|
|
|
if (duck->z < landZ || waterZ == 0) {
|
|
duck->state = DUCK_STATE_FLY_AWAY;
|
|
duck_update_fly_away(duck);
|
|
return;
|
|
}
|
|
|
|
duck->z = waterZ;
|
|
randomNumber = scenario_rand();
|
|
if ((randomNumber & 0xFFFF) <= 0xAAA) {
|
|
randomNumber >>= 16;
|
|
duck->sprite_direction = randomNumber & 0x18;
|
|
}
|
|
|
|
int direction = duck->sprite_direction >> 3;
|
|
int x = duck->x + duck_move_offset[direction].x;
|
|
int y = duck->y + duck_move_offset[direction].y;
|
|
landZ = map_element_height(x, y);
|
|
waterZ = (landZ >> 16) & 0xFFFF;
|
|
landZ &= 0xFFFF;
|
|
|
|
if (duck->z < landZ || duck->z != waterZ)
|
|
return;
|
|
|
|
sprite_move(x, y, waterZ, (rct_sprite*)duck);
|
|
duck_invalidate(duck);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674357
|
|
*/
|
|
static void duck_update_drink(rct_duck *duck)
|
|
{
|
|
duck->frame++;
|
|
if (duck_drink_animation[duck->frame] == 0xFF) {
|
|
duck->state = DUCK_STATE_SWIM;
|
|
duck->frame = 0;
|
|
duck_update_swim(duck);
|
|
} else {
|
|
duck_invalidate(duck);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674372
|
|
*/
|
|
static void duck_update_double_drink(rct_duck *duck)
|
|
{
|
|
duck->frame++;
|
|
if (duck_double_drink_animation[duck->frame] == 0xFF) {
|
|
duck->state = DUCK_STATE_SWIM;
|
|
duck->frame = 0;
|
|
duck_update_swim(duck);
|
|
} else {
|
|
duck_invalidate(duck);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x0067438D
|
|
*/
|
|
static void duck_update_fly_away(rct_duck *duck)
|
|
{
|
|
if (gCurrentTicks & 3)
|
|
return;
|
|
|
|
duck->frame++;
|
|
if (duck->frame >= countof(duck_fly_away_animation))
|
|
duck->frame = 0;
|
|
|
|
duck_invalidate(duck);
|
|
int direction = duck->sprite_direction >> 3;
|
|
int x = duck->x + (duck_move_offset[direction].x * 2);
|
|
int y = duck->y + (duck_move_offset[direction].y * 2);
|
|
if (x < 0 || y < 0 || x >= (32 * 256) || y >= (32 * 256)) {
|
|
sprite_remove((rct_sprite*)duck);
|
|
return;
|
|
}
|
|
|
|
int z = min(duck->z + 2, 496);
|
|
sprite_move(x, y, z, (rct_sprite*)duck);
|
|
duck_invalidate(duck);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006E895D
|
|
*/
|
|
void duck_press(rct_duck *duck)
|
|
{
|
|
audio_play_sound_at_location(SOUND_QUACK, duck->x, duck->y, duck->z);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x00674576
|
|
*/
|
|
void duck_remove_all()
|
|
{
|
|
rct_unk_sprite* sprite;
|
|
uint16 spriteIndex, nextSpriteIndex;
|
|
|
|
for (spriteIndex = gSpriteListHead[SPRITE_LIST_MISC]; spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) {
|
|
sprite = &(get_sprite(spriteIndex)->unknown);
|
|
nextSpriteIndex = sprite->next;
|
|
if (sprite->misc_identifier == SPRITE_MISC_DUCK)
|
|
sprite_remove((rct_sprite*)sprite);
|
|
}
|
|
}
|