diff --git a/projects/openrct2.vcxproj b/projects/openrct2.vcxproj
index 956fd8efe2..47362b5448 100644
--- a/projects/openrct2.vcxproj
+++ b/projects/openrct2.vcxproj
@@ -107,6 +107,9 @@
+
+
+
diff --git a/projects/openrct2.vcxproj.filters b/projects/openrct2.vcxproj.filters
index 8321dae4a5..262a0ef02b 100644
--- a/projects/openrct2.vcxproj.filters
+++ b/projects/openrct2.vcxproj.filters
@@ -522,6 +522,15 @@
Source\Windows
+
+ Source\World
+
+
+ Source\World
+
+
+ Source\World
+
diff --git a/src/world/balloon.c b/src/world/balloon.c
new file mode 100644
index 0000000000..62de010dfe
--- /dev/null
+++ b/src/world/balloon.c
@@ -0,0 +1,83 @@
+#include "../audio/audio.h"
+#include "../scenario.h"
+#include "sprite.h"
+
+/**
+ *
+ * rct2: 0x006736C7
+ */
+void create_balloon(int x, int y, int z, int colour, uint8 bl)
+{
+ rct_sprite* sprite = create_sprite(2);
+ if (sprite != NULL) {
+ sprite->balloon.var_14 = 13;
+ sprite->balloon.var_09 = 22;
+ sprite->balloon.var_15 = 11;
+ sprite->balloon.sprite_identifier = SPRITE_IDENTIFIER_MISC;
+ sprite_move(x, y, z, sprite);
+ sprite->balloon.misc_identifier = SPRITE_MISC_BALLOON;
+ sprite->balloon.var_26 = 0;
+ sprite->balloon.colour = colour;
+ sprite->balloon.popped = bl;
+ }
+}
+
+void balloon_pop(rct_balloon *balloon)
+{
+ balloon->popped = 1;
+ balloon->var_26 = 0;
+ sound_play_panned(SOUND_BALLOON_POP, 0x8001, balloon->x, balloon->y, balloon->z);
+}
+
+/**
+ *
+ * rct: 0x0067342C
+ */
+void balloon_update(rct_balloon *balloon)
+{
+ invalidate_sprite_2((rct_sprite*)balloon);
+ if (balloon->popped == 1) {
+ balloon->var_26 += 256;
+ if (balloon->var_26 >= 1280)
+ sprite_remove((rct_sprite*)balloon);
+
+ return;
+ }
+
+ int original_var26a = balloon->var_26a;
+ balloon->var_26a += 85;
+ if (original_var26a < 255 - 85)
+ return;
+
+ balloon->var_26b++;
+ sprite_move(balloon->x, balloon->y, balloon->z + 1, (rct_sprite*)balloon);
+
+ int maxZ = 1967 - ((balloon->x ^ balloon->y) & 31);
+ if (balloon->z < maxZ)
+ return;
+
+ balloon_pop(balloon);
+}
+
+/**
+ *
+ * rct2: 0x006E88ED
+ */
+void balloon_press(rct_balloon *balloon)
+{
+ if (balloon->popped == 1)
+ return;
+
+ uint32 random = rand();
+ if ((balloon->var_0A & 7) || (random & 0xFFFF) < 0x2000) {
+ balloon_pop(balloon);
+ return;
+ }
+
+ sprite_move(
+ balloon->x + ((random & 0x80000000) ? -6 : 6),
+ balloon->y,
+ balloon->z,
+ (rct_sprite*)balloon
+ );
+}
diff --git a/src/world/duck.c b/src/world/duck.c
new file mode 100644
index 0000000000..9f75a65ac5
--- /dev/null
+++ b/src/world/duck.c
@@ -0,0 +1,309 @@
+#include "../addresses.h"
+#include "../audio/audio.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: 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, 255
+};
+
+// 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, 255
+};
+
+/**
+ *
+ * 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.var_26 = 0;
+ }
+}
+
+/**
+ *
+ * rct: 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);
+}
+
+/**
+ *
+ * rct: 0x00674108
+ */
+static void duck_update_fly_to_water(rct_duck *duck)
+{
+ if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3)
+ return;
+
+ duck->var_26++;
+ if (duck->var_26 >= 6)
+ duck->var_26 = 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->var_26 = 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->var_26 = 0;
+ duck_update_swim(duck);
+ }
+ }
+}
+
+/**
+ *
+ * rct: 0x00674282
+ */
+static void duck_update_swim(rct_duck *duck)
+{
+ if ((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) + duck->var_0A) & 3)
+ return;
+
+ uint32 randomNumber = scenario_rand();
+ if ((randomNumber & 0xFFFF) < 0x666) {
+ if (randomNumber & 0x80000000) {
+ duck->state = DUCK_STATE_DOUBLE_DRINK;
+ duck->var_26 = -1;
+ duck_update_double_drink(duck);
+ } else {
+ duck->state = DUCK_STATE_DRINK;
+ duck->var_26 = -1;
+ duck_update_drink(duck);
+ }
+ return;
+ }
+
+ int currentMonth = date_get_month(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16));
+ 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);
+}
+
+/**
+ *
+ * rct: 0x00674357
+ */
+static void duck_update_drink(rct_duck *duck)
+{
+ duck->var_26++;
+ if (duck_drink_animation[duck->var_26] == 255) {
+ duck->state = DUCK_STATE_SWIM;
+ duck->var_26 = 0;
+ duck_update_swim(duck);
+ } else {
+ duck_invalidate(duck);
+ }
+}
+
+/**
+ *
+ * rct: 0x00674372
+ */
+static void duck_update_double_drink(rct_duck *duck)
+{
+ duck->var_26++;
+ if (duck_double_drink_animation[duck->var_26] == 255) {
+ duck->state = DUCK_STATE_SWIM;
+ duck->var_26 = 0;
+ duck_update_swim(duck);
+ } else {
+ duck_invalidate(duck);
+ }
+}
+
+/**
+ *
+ * rct: 0x0067438D
+ */
+static void duck_update_fly_away(rct_duck *duck)
+{
+ if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3)
+ return;
+
+ duck->var_26++;
+ if (duck->var_26 >= 6)
+ duck->var_26 = 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 = z = min(duck->z + 2, 496);
+ sprite_move(x, y, z, (rct_sprite*)duck);
+ duck_invalidate(duck);
+}
+
+/**
+ *
+ * rct: 0x006E895D
+ */
+void duck_press(rct_duck *duck)
+{
+ sound_play_panned(SOUND_QUACK, 0x8001, duck->x, duck->y, duck->z);
+}
+
+/**
+ *
+ * rct: 0x00674576
+ */
+void duck_remove_all()
+{
+ rct_unk_sprite* sprite;
+ uint16 spriteIndex, nextSpriteIndex;
+
+ for (spriteIndex = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_MISC, uint16); spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) {
+ sprite = &(g_sprite_list[spriteIndex].unknown);
+ nextSpriteIndex = sprite->next;
+ if (sprite->misc_identifier == SPRITE_MISC_DUCK)
+ sprite_remove((rct_sprite*)sprite);
+ }
+}
diff --git a/src/world/money_effect.c b/src/world/money_effect.c
new file mode 100644
index 0000000000..132d6293a3
--- /dev/null
+++ b/src/world/money_effect.c
@@ -0,0 +1,109 @@
+#include "../localisation/localisation.h"
+#include "../interface/viewport.h"
+#include "../interface/window.h"
+#include "sprite.h"
+
+static const rct_xy16 _moneyEffectMoveOffset[] = {
+ { 1, -1 },
+ { 1, 1 },
+ { -1, 1 },
+ { -1, -1 }
+};
+
+/**
+ *
+ * rct: 0x0067351F
+ */
+void money_effect_create_at(money32 value, int x, int y, int z)
+{
+ rct_money_effect *moneyEffect;
+ rct_string_id stringId;
+ char buffer[128];
+
+ moneyEffect = (rct_money_effect*)create_sprite(2);
+ if (moneyEffect == NULL)
+ return;
+
+ moneyEffect->value = value;
+ moneyEffect->var_14 = 64;
+ moneyEffect->var_09 = 20;
+ moneyEffect->var_15 = 30;
+ moneyEffect->sprite_identifier = SPRITE_IDENTIFIER_MISC;
+ sprite_move(x, y, z, (rct_sprite*)moneyEffect);
+ moneyEffect->misc_identifier = SPRITE_MISC_MONEY_EFFECT;
+ moneyEffect->num_movements = 0;
+ moneyEffect->move_delay = 0;
+
+ stringId = 1388;
+ if (value < 0) {
+ value *= -1;
+ stringId = 1399;
+ }
+ format_string(buffer, stringId, &value);
+ RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224;
+ moneyEffect->offset_x = -(gfx_get_string_width(buffer) / 2);
+ moneyEffect->wiggle = 0;
+}
+
+/**
+ *
+ * rct: 0x0069C5D0
+ */
+void money_effect_create(money32 value)
+{
+ rct_window *mainWindow;
+ rct_viewport *mainViewport;
+ rct_xyz16 mapPosition;
+
+ mapPosition.x = RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_X, uint16);
+ mapPosition.y = RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Y, uint16);
+ mapPosition.z = RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint16);
+ if (mapPosition.x == (sint16)0x8000) {
+ mainWindow = window_get_main();
+ if (mainWindow == NULL)
+ return;
+
+ mainViewport = mainWindow->viewport;
+ screen_get_map_xy(
+ mainViewport->x + (mainViewport->width / 2),
+ mainViewport->y + (mainViewport->height / 2),
+ &mapPosition.x,
+ &mapPosition.y,
+ NULL
+ );
+ if (mapPosition.x == (sint16)0x8000)
+ return;
+
+ mapPosition.z = map_element_height(mapPosition.x, mapPosition.y) & 0xFFFF;
+ }
+ mapPosition.z += 10;
+ money_effect_create_at(-value, mapPosition.x, mapPosition.y, mapPosition.z);
+}
+
+/**
+ *
+ * rct: 0x00673232
+ */
+void money_effect_update(rct_money_effect *moneyEffect)
+{
+ invalidate_sprite_2((rct_sprite*)moneyEffect);
+ moneyEffect->wiggle++;
+ if (moneyEffect->wiggle >= 22)
+ moneyEffect->wiggle = 0;
+
+ moneyEffect->move_delay++;
+ if (moneyEffect->move_delay < 2)
+ return;
+
+ moneyEffect->move_delay = 0;
+ int x = moneyEffect->x + _moneyEffectMoveOffset[RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8)].x;
+ int y = moneyEffect->y + _moneyEffectMoveOffset[RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8)].y;
+ int z = moneyEffect->z;
+ sprite_move(x, y, z, (rct_sprite*)moneyEffect);
+
+ moneyEffect->num_movements++;
+ if (moneyEffect->num_movements < 55)
+ return;
+
+ sprite_remove((rct_sprite*)moneyEffect);
+}
diff --git a/src/world/sprite.c b/src/world/sprite.c
index 0b8570e5bb..de0cb76716 100644
--- a/src/world/sprite.c
+++ b/src/world/sprite.c
@@ -27,500 +27,9 @@
#include "fountain.h"
#include "sprite.h"
-enum {
- DUCK_STATE_FLY_TO_WATER,
- DUCK_STATE_SWIM,
- DUCK_STATE_DRINK,
- DUCK_STATE_DOUBLE_DRINK,
- DUCK_STATE_FLY_AWAY
-};
-
rct_sprite* g_sprite_list = RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite);
-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: 0x006736C7
- */
-void create_balloon(int x, int y, int z, int colour, uint8 bl)
-{
- rct_sprite* sprite = create_sprite(2);
- if (sprite != NULL)
- {
- sprite->balloon.var_14 = 13;
- sprite->balloon.var_09 = 22;
- sprite->balloon.var_15 = 11;
- sprite->balloon.sprite_identifier = SPRITE_IDENTIFIER_MISC;
- sprite_move(x, y, z, sprite);
- sprite->balloon.misc_identifier = SPRITE_MISC_BALLOON;
- sprite->balloon.var_26 = 0;
- sprite->balloon.colour = colour;
- sprite->balloon.popped = bl;
- }
-}
-
-void balloon_pop(rct_balloon *balloon)
-{
- balloon->popped = 1;
- balloon->var_26 = 0;
- sound_play_panned(SOUND_BALLOON_POP, 0x8001, balloon->x, balloon->y, balloon->z);
-}
-
-/**
- *
- * rct: 0x0067342C
- */
-void balloon_update(rct_balloon *balloon)
-{
- invalidate_sprite_2((rct_sprite*)balloon);
- if (balloon->popped == 1) {
- balloon->var_26 += 256;
- if (balloon->var_26 >= 1280)
- sprite_remove((rct_sprite*)balloon);
-
- return;
- }
-
- int original_var26a = balloon->var_26a;
- balloon->var_26a += 85;
- if (original_var26a < 255 - 85)
- return;
-
- balloon->var_26b++;
- sprite_move(balloon->x, balloon->y, balloon->z + 1, (rct_sprite*)balloon);
-
- int maxZ = 1967 - ((balloon->x ^ balloon->y) & 31);
- if (balloon->z < maxZ)
- return;
-
- balloon_pop(balloon);
-}
-
-/**
- *
- * rct2: 0x006E88ED
- */
-void balloon_press(rct_balloon *balloon)
-{
- if (balloon->popped == 1)
- return;
-
- uint32 random = scenario_rand();
- if ((balloon->var_0A & 7) || (random & 0xFFFF) < 0x2000) {
- balloon_pop(balloon);
- return;
- }
-
- sprite_move(
- balloon->x + ((random & 0x80000000) ? -6 : 6),
- balloon->y,
- balloon->z,
- (rct_sprite*)balloon
- );
-}
-
-// rct2: 0x009A3B04
-static const rct_xy16 duck_move_offset[] = {
- { -1, 0 },
- { 0, 1 },
- { 1, 0 },
- { 0, -1 }
-};
-
-// 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, 255
-};
-
-// 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, 255
-};
-
-/**
- *
- * 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.var_26 = 0;
- }
-}
-
-/**
- *
- * rct: 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);
-}
-
-/**
- *
- * rct: 0x00674108
- */
-static void duck_update_fly_to_water(rct_duck *duck)
-{
- if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3)
- return;
-
- duck->var_26++;
- if (duck->var_26 >= 6)
- duck->var_26 = 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->var_26 = 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->var_26 = 0;
- duck_update_swim(duck);
- }
- }
-}
-
-/**
- *
- * rct: 0x00674282
- */
-static void duck_update_swim(rct_duck *duck)
-{
- if ((RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) + duck->var_0A) & 3)
- return;
-
- uint32 randomNumber = scenario_rand();
- if ((randomNumber & 0xFFFF) < 0x666) {
- if (randomNumber & 0x80000000) {
- duck->state = DUCK_STATE_DOUBLE_DRINK;
- duck->var_26 = -1;
- duck_update_double_drink(duck);
- } else {
- duck->state = DUCK_STATE_DRINK;
- duck->var_26 = -1;
- duck_update_drink(duck);
- }
- return;
- }
-
- int currentMonth = date_get_month(RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_MONTH_YEAR, uint16));
- 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);
-}
-
-/**
- *
- * rct: 0x00674357
- */
-static void duck_update_drink(rct_duck *duck)
-{
- duck->var_26++;
- if (duck_drink_animation[duck->var_26] == 255) {
- duck->state = DUCK_STATE_SWIM;
- duck->var_26 = 0;
- duck_update_swim(duck);
- } else {
- duck_invalidate(duck);
- }
-}
-
-/**
- *
- * rct: 0x00674372
- */
-static void duck_update_double_drink(rct_duck *duck)
-{
- duck->var_26++;
- if (duck_double_drink_animation[duck->var_26] == 255) {
- duck->state = DUCK_STATE_SWIM;
- duck->var_26 = 0;
- duck_update_swim(duck);
- } else {
- duck_invalidate(duck);
- }
-}
-
-/**
- *
- * rct: 0x0067438D
- */
-static void duck_update_fly_away(rct_duck *duck)
-{
- if (RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_TICKS, uint32) & 3)
- return;
-
- duck->var_26++;
- if (duck->var_26 >= 6)
- duck->var_26 = 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 = z = min(duck->z + 2, 496);
- sprite_move(x, y, z, (rct_sprite*)duck);
- duck_invalidate(duck);
-}
-
-/**
- *
- * rct: 0x006E895D
- */
-void duck_press(rct_duck *duck)
-{
- sound_play_panned(SOUND_QUACK, 0x8001, duck->x, duck->y, duck->z);
-}
-
-/**
- *
- * rct: 0x00674576
- */
-void duck_remove_all()
-{
- rct_unk_sprite* sprite;
- uint16 spriteIndex, nextSpriteIndex;
-
- for (spriteIndex = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_MISC, uint16); spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) {
- sprite = &(g_sprite_list[spriteIndex].unknown);
- nextSpriteIndex = sprite->next;
- if (sprite->misc_identifier == SPRITE_MISC_DUCK)
- sprite_remove((rct_sprite*)sprite);
- }
-}
-
-static const rct_xy16 _moneyEffectMoveOffset[] = {
- { 1, -1 },
- { 1, 1 },
- { -1, 1 },
- { -1, -1 }
-};
-
-/**
- *
- * rct: 0x0067351F
- */
-void money_effect_create_at(money32 value, int x, int y, int z)
-{
- rct_money_effect *moneyEffect;
- rct_string_id stringId;
- char buffer[128];
-
- moneyEffect = (rct_money_effect*)create_sprite(2);
- if (moneyEffect == NULL)
- return;
-
- moneyEffect->value = value;
- moneyEffect->var_14 = 64;
- moneyEffect->var_09 = 20;
- moneyEffect->var_15 = 30;
- moneyEffect->sprite_identifier = SPRITE_IDENTIFIER_MISC;
- sprite_move(x, y, z, (rct_sprite*)moneyEffect);
- moneyEffect->misc_identifier = SPRITE_MISC_MONEY_EFFECT;
- moneyEffect->num_movements = 0;
- moneyEffect->move_delay = 0;
-
- stringId = 1388;
- if (value < 0) {
- value *= -1;
- stringId = 1399;
- }
- format_string(buffer, stringId, &value);
- RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_FONT_SPRITE_BASE, uint16) = 224;
- moneyEffect->offset_x = -(gfx_get_string_width(buffer) / 2);
- moneyEffect->wiggle = 0;
-}
-
-/**
- *
- * rct: 0x0069C5D0
- */
-void money_effect_create(money32 value)
-{
- rct_window *mainWindow;
- rct_viewport *mainViewport;
- rct_xyz16 mapPosition;
-
- mapPosition.x = RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_X, uint16);
- mapPosition.y = RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Y, uint16);
- mapPosition.z = RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint16);
- if (mapPosition.x == (sint16)0x8000) {
- mainWindow = window_get_main();
- if (mainWindow == NULL)
- return;
-
- mainViewport = mainWindow->viewport;
- screen_get_map_xy(
- mainViewport->x + (mainViewport->width / 2),
- mainViewport->y + (mainViewport->height / 2),
- &mapPosition.x,
- &mapPosition.y,
- NULL
- );
- if (mapPosition.x == (sint16)0x8000)
- return;
-
- mapPosition.z = map_element_height(mapPosition.x, mapPosition.y) & 0xFFFF;
- }
- mapPosition.z += 10;
- money_effect_create_at(-value, mapPosition.x, mapPosition.y, mapPosition.z);
-}
-
-/**
- *
- * rct: 0x00673232
- */
-void money_effect_update(rct_money_effect *moneyEffect)
-{
- invalidate_sprite_2((rct_sprite*)moneyEffect);
- moneyEffect->wiggle++;
- if (moneyEffect->wiggle >= 22)
- moneyEffect->wiggle = 0;
-
- moneyEffect->move_delay++;
- if (moneyEffect->move_delay < 2)
- return;
-
- moneyEffect->move_delay = 0;
- int x = moneyEffect->x + _moneyEffectMoveOffset[RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8)].x;
- int y = moneyEffect->y + _moneyEffectMoveOffset[RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8)].y;
- int z = moneyEffect->z;
- sprite_move(x, y, z, (rct_sprite*)moneyEffect);
-
- moneyEffect->num_movements++;
- if (moneyEffect->num_movements < 55)
- return;
-
- sprite_remove((rct_sprite*)moneyEffect);
-}
-
-void invalidate_sprite_max_zoom(rct_sprite *sprite, int maxZoom)
+static void invalidate_sprite_max_zoom(rct_sprite *sprite, int maxZoom)
{
if (sprite->unknown.sprite_left == (sint16)0x8000) return;
diff --git a/src/world/sprite.h b/src/world/sprite.h
index 2ba74fe34f..0e829b2922 100644
--- a/src/world/sprite.h
+++ b/src/world/sprite.h
@@ -243,12 +243,6 @@ enum {
// rct2: 0x010E63BC
extern rct_sprite* g_sprite_list;
-void create_balloon(int x, int y, int z, int colour, uint8 bl);
-void balloon_press(rct_balloon *balloon);
-void create_duck(int targetX, int targetY);
-void duck_press(rct_duck *duck);
-void duck_remove_all();
-void money_effect_create(money32 value);
rct_sprite *create_sprite(uint8 bl);
void reset_sprite_list();
void reset_0x69EBE4();
@@ -265,4 +259,25 @@ void sub_6738E1(int x, int y, int z);
void sprite_misc_3_create(int x, int y, int z);
void sprite_misc_5_create(int x, int y, int z);
+///////////////////////////////////////////////////////////////
+// Balloon
+///////////////////////////////////////////////////////////////
+void create_balloon(int x, int y, int z, int colour, uint8 bl);
+void balloon_update(rct_balloon *balloon);
+void balloon_press(rct_balloon *balloon);
+
+///////////////////////////////////////////////////////////////
+// Duck
+///////////////////////////////////////////////////////////////
+void create_duck(int targetX, int targetY);
+void duck_update(rct_duck *duck);
+void duck_press(rct_duck *duck);
+void duck_remove_all();
+
+///////////////////////////////////////////////////////////////
+// Money effect
+///////////////////////////////////////////////////////////////
+void money_effect_create(money32 value);
+void money_effect_update(rct_money_effect *moneyEffect);
+
#endif