2016-05-04 19:24:41 +02:00
|
|
|
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
|
2014-05-25 20:47:11 +02:00
|
|
|
/*****************************************************************************
|
|
|
|
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
|
|
|
|
*
|
2016-05-04 19:24:41 +02:00
|
|
|
* 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
|
2014-05-25 20:47:11 +02:00
|
|
|
*
|
|
|
|
* 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.
|
2016-05-04 19:24:41 +02:00
|
|
|
*
|
|
|
|
* A full copy of the GNU General Public License can be found in licence.txt
|
2014-05-25 20:47:11 +02:00
|
|
|
*****************************************************************************/
|
2016-05-04 19:24:41 +02:00
|
|
|
#pragma endregion
|
2014-05-25 20:47:11 +02:00
|
|
|
|
2014-10-06 18:36:58 +02:00
|
|
|
#include "../addresses.h"
|
2015-03-25 15:31:10 +01:00
|
|
|
#include "../audio/audio.h"
|
2016-03-20 05:25:19 +01:00
|
|
|
#include "../cheats.h"
|
2014-11-10 18:23:13 +01:00
|
|
|
#include "../interface/viewport.h"
|
2015-03-26 00:57:09 +01:00
|
|
|
#include "../localisation/date.h"
|
2015-03-31 04:23:40 +02:00
|
|
|
#include "../localisation/localisation.h"
|
2015-02-27 16:34:52 +01:00
|
|
|
#include "../scenario.h"
|
2015-03-24 17:48:38 +01:00
|
|
|
#include "fountain.h"
|
|
|
|
#include "sprite.h"
|
2014-05-25 20:47:11 +02:00
|
|
|
|
2014-06-29 15:51:18 +02:00
|
|
|
rct_sprite* g_sprite_list = RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_LIST, rct_sprite);
|
|
|
|
|
2015-12-29 21:08:14 +01:00
|
|
|
rct_sprite_entry* g_sprite_entries = RCT2_ADDRESS(RCT2_ADDRESS_SPRITE_ENTRIES, rct_sprite_entry);
|
|
|
|
|
2016-01-17 01:57:37 +01:00
|
|
|
uint16 sprite_get_first_in_quadrant(int x, int y)
|
2015-09-05 14:59:12 +02:00
|
|
|
{
|
|
|
|
int offset = ((x & 0x1FE0) << 3) | (y >> 5);
|
|
|
|
return RCT2_ADDRESS(0x00F1EF60, uint16)[offset];
|
|
|
|
}
|
|
|
|
|
2015-08-22 13:11:20 +02:00
|
|
|
static void invalidate_sprite_max_zoom(rct_sprite *sprite, int maxZoom)
|
2015-08-22 12:56:32 +02:00
|
|
|
{
|
2015-10-25 16:34:01 +01:00
|
|
|
if (sprite->unknown.sprite_left == SPRITE_LOCATION_NULL) return;
|
2014-11-10 18:23:13 +01:00
|
|
|
|
2016-04-14 23:53:00 +02:00
|
|
|
for (int i = 0; i < MAX_VIEWPORT_COUNT; i++) {
|
|
|
|
rct_viewport *viewport = &g_viewport_list[i];
|
|
|
|
if (viewport->width != 0 && viewport->zoom <= maxZoom) {
|
2015-08-22 12:56:32 +02:00
|
|
|
viewport_invalidate(
|
|
|
|
viewport,
|
|
|
|
sprite->unknown.sprite_left,
|
|
|
|
sprite->unknown.sprite_top,
|
|
|
|
sprite->unknown.sprite_right,
|
|
|
|
sprite->unknown.sprite_bottom
|
|
|
|
);
|
|
|
|
}
|
2014-11-10 18:23:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-26 00:57:09 +01:00
|
|
|
/**
|
2015-08-22 12:56:32 +02:00
|
|
|
* Invalidate the sprite if at closest zoom.
|
|
|
|
* rct2: 0x006EC60B
|
|
|
|
*/
|
|
|
|
void invalidate_sprite_0(rct_sprite* sprite)
|
|
|
|
{
|
|
|
|
invalidate_sprite_max_zoom(sprite, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invalidate sprite if at closest zoom or next zoom up from closest.
|
|
|
|
* rct2: 0x006EC53F
|
2015-03-26 00:57:09 +01:00
|
|
|
*/
|
2015-08-22 12:56:32 +02:00
|
|
|
void invalidate_sprite_1(rct_sprite *sprite)
|
2015-03-26 00:57:09 +01:00
|
|
|
{
|
2015-08-22 12:56:32 +02:00
|
|
|
invalidate_sprite_max_zoom(sprite, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-02-03 21:31:30 +01:00
|
|
|
* Invalidate sprite if not at furthest zoom.
|
|
|
|
* rct2: 0x006EC473
|
|
|
|
*
|
|
|
|
* @param sprite (esi)
|
|
|
|
*/
|
2015-08-22 12:56:32 +02:00
|
|
|
void invalidate_sprite_2(rct_sprite *sprite)
|
|
|
|
{
|
|
|
|
invalidate_sprite_max_zoom(sprite, 2);
|
2015-03-26 00:57:09 +01:00
|
|
|
}
|
|
|
|
|
2015-12-11 16:38:37 +01:00
|
|
|
/**
|
2014-06-20 16:31:28 +02:00
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x0069EB13
|
2014-06-20 16:31:28 +02:00
|
|
|
*/
|
|
|
|
void reset_sprite_list(){
|
2016-01-11 01:50:03 +01:00
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_SAVED_AGE, uint16) = 0;
|
2015-10-25 16:34:01 +01:00
|
|
|
memset(g_sprite_list, 0, sizeof(rct_sprite) * MAX_SPRITES);
|
2014-06-20 16:31:28 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < 6; ++i){
|
|
|
|
RCT2_ADDRESS(RCT2_ADDRESS_SPRITES_NEXT_INDEX, uint16)[i] = -1;
|
|
|
|
RCT2_ADDRESS(0x13573C8, uint16)[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
rct_sprite* previous_spr = (rct_sprite*)SPRITE_INDEX_NULL;
|
|
|
|
|
2014-06-29 15:51:18 +02:00
|
|
|
rct_sprite* spr = g_sprite_list;
|
2015-10-25 16:34:01 +01:00
|
|
|
for (int i = 0; i < MAX_SPRITES; ++i){
|
|
|
|
spr->unknown.sprite_identifier = SPRITE_IDENTIFIER_NULL;
|
2014-06-20 16:31:28 +02:00
|
|
|
spr->unknown.sprite_index = i;
|
|
|
|
spr->unknown.next = SPRITE_INDEX_NULL;
|
2014-09-05 19:31:25 +02:00
|
|
|
spr->unknown.linked_list_type_offset = 0;
|
2014-06-20 16:31:28 +02:00
|
|
|
|
|
|
|
if (previous_spr != (rct_sprite*)SPRITE_INDEX_NULL){
|
|
|
|
spr->unknown.previous = previous_spr->unknown.sprite_index;
|
|
|
|
previous_spr->unknown.next = i;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
spr->unknown.previous = SPRITE_INDEX_NULL;
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_NEXT_INDEX, uint16) = i;
|
|
|
|
}
|
|
|
|
previous_spr = spr;
|
|
|
|
spr++;
|
|
|
|
}
|
|
|
|
|
2015-10-25 16:34:01 +01:00
|
|
|
RCT2_GLOBAL(0x13573C8, uint16) = MAX_SPRITES;
|
2014-06-20 16:31:28 +02:00
|
|
|
|
2014-06-21 14:31:28 +02:00
|
|
|
reset_0x69EBE4();
|
|
|
|
}
|
|
|
|
|
2015-06-08 22:35:09 +02:00
|
|
|
/**
|
2015-12-11 16:38:37 +01:00
|
|
|
*
|
|
|
|
* rct2: 0x0069EBE4
|
2014-06-21 14:31:28 +02:00
|
|
|
* This function looks as though it sets some sort of order for sprites.
|
|
|
|
* Sprites can share thier position if this is the case.
|
|
|
|
*/
|
|
|
|
void reset_0x69EBE4(){
|
|
|
|
memset((uint16*)0xF1EF60, -1, 0x10001*2);
|
|
|
|
|
2014-06-29 15:51:18 +02:00
|
|
|
rct_sprite* spr = g_sprite_list;
|
2014-06-21 14:31:28 +02:00
|
|
|
for (; spr < (rct_sprite*)RCT2_ADDRESS_SPRITES_NEXT_INDEX; spr++){
|
|
|
|
|
2015-10-25 16:34:01 +01:00
|
|
|
if (spr->unknown.sprite_identifier != SPRITE_IDENTIFIER_NULL){
|
2014-06-21 14:31:28 +02:00
|
|
|
uint32 edi = spr->unknown.x;
|
2015-07-23 19:38:43 +02:00
|
|
|
if (spr->unknown.x == SPRITE_LOCATION_NULL){
|
2014-06-21 14:31:28 +02:00
|
|
|
edi = 0x10000;
|
2015-07-23 19:38:43 +02:00
|
|
|
} else {
|
2014-06-21 14:31:28 +02:00
|
|
|
int ecx = spr->unknown.y;
|
|
|
|
ecx >>= 5;
|
|
|
|
edi &= 0x1FE0;
|
|
|
|
edi <<= 3;
|
|
|
|
edi |= ecx;
|
|
|
|
}
|
|
|
|
uint16 ax = RCT2_ADDRESS(0xF1EF60,uint16)[edi];
|
|
|
|
RCT2_ADDRESS(0xF1EF60,uint16)[edi] = spr->unknown.sprite_index;
|
2015-03-09 23:06:08 +01:00
|
|
|
spr->unknown.next_in_quadrant = ax;
|
2014-06-21 14:31:28 +02:00
|
|
|
}
|
|
|
|
}
|
2014-09-04 23:21:17 +02:00
|
|
|
}
|
|
|
|
|
2015-06-08 22:35:09 +02:00
|
|
|
/**
|
|
|
|
* Clears all the unused sprite memory to zero. Probably so that it can be compressed better when saving.
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x0069EBA4
|
2015-06-08 22:35:09 +02:00
|
|
|
*/
|
|
|
|
void sprite_clear_all_unused()
|
|
|
|
{
|
|
|
|
rct_unk_sprite *sprite;
|
2015-06-09 02:24:02 +02:00
|
|
|
uint16 spriteIndex, nextSpriteIndex, previousSpriteIndex;
|
2015-10-20 20:16:30 +02:00
|
|
|
|
2015-06-08 22:35:09 +02:00
|
|
|
spriteIndex = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_NEXT_INDEX, uint16);
|
|
|
|
while (spriteIndex != SPRITE_INDEX_NULL) {
|
|
|
|
sprite = &g_sprite_list[spriteIndex].unknown;
|
|
|
|
nextSpriteIndex = sprite->next;
|
2015-06-09 02:24:02 +02:00
|
|
|
previousSpriteIndex = sprite->previous;
|
2015-06-08 22:35:09 +02:00
|
|
|
memset(sprite, 0, sizeof(rct_sprite));
|
|
|
|
sprite->sprite_identifier = SPRITE_IDENTIFIER_NULL;
|
|
|
|
sprite->next = nextSpriteIndex;
|
2015-06-09 02:24:02 +02:00
|
|
|
sprite->previous = previousSpriteIndex;
|
2015-06-08 22:35:09 +02:00
|
|
|
sprite->linked_list_type_offset = SPRITE_LINKEDLIST_OFFSET_NULL;
|
|
|
|
sprite->sprite_index = spriteIndex;
|
|
|
|
spriteIndex = nextSpriteIndex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-04 23:21:17 +02:00
|
|
|
/*
|
|
|
|
* rct2: 0x0069EC6B
|
2015-03-23 11:39:47 +01:00
|
|
|
* bl: if bl & 2 > 0, the sprite ends up in the MISC linked list.
|
2014-09-04 23:21:17 +02:00
|
|
|
*/
|
|
|
|
rct_sprite *create_sprite(uint8 bl)
|
|
|
|
{
|
2014-09-06 23:46:45 +02:00
|
|
|
SPRITE_LINKEDLIST_OFFSET linkedListTypeOffset = SPRITE_LINKEDLIST_OFFSET_UNKNOWN; // cl
|
2014-09-04 23:21:17 +02:00
|
|
|
|
2014-09-05 00:27:16 +02:00
|
|
|
if ((bl & 2) != 0)
|
2014-09-04 23:21:17 +02:00
|
|
|
{
|
|
|
|
// 69EC96;
|
2016-01-27 18:47:10 +01:00
|
|
|
sint16 cx = 0x12C - RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_COUNT_MISC, uint16);
|
2014-09-04 23:21:17 +02:00
|
|
|
if (cx >= RCT2_GLOBAL(0x13573C8, uint16))
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-03-23 11:39:47 +01:00
|
|
|
linkedListTypeOffset = SPRITE_LINKEDLIST_OFFSET_MISC;
|
2014-09-04 23:21:17 +02:00
|
|
|
}
|
|
|
|
else if (RCT2_GLOBAL(0x13573C8, uint16) <= 0)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-09-05 00:27:16 +02:00
|
|
|
rct_unk_sprite *sprite = &(g_sprite_list[RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_NEXT_INDEX, uint16)]).unknown;
|
2014-09-04 23:21:17 +02:00
|
|
|
|
2014-09-06 09:11:04 +02:00
|
|
|
move_sprite_to_list((rct_sprite *)sprite, (uint8)linkedListTypeOffset);
|
2014-09-04 23:21:17 +02:00
|
|
|
|
|
|
|
sprite->x = SPRITE_LOCATION_NULL;
|
|
|
|
sprite->y = SPRITE_LOCATION_NULL;
|
|
|
|
sprite->z = 0;
|
|
|
|
sprite->name_string_idx = 0;
|
2015-02-18 19:27:36 +01:00
|
|
|
sprite->sprite_width = 0x10;
|
|
|
|
sprite->sprite_height_negative = 0x14;
|
|
|
|
sprite->sprite_height_positive = 0x8;
|
2016-02-23 18:22:19 +01:00
|
|
|
sprite->flags = 0;
|
2014-11-10 18:23:13 +01:00
|
|
|
sprite->sprite_left = SPRITE_LOCATION_NULL;
|
2014-09-04 23:21:17 +02:00
|
|
|
|
2015-03-09 23:06:08 +01:00
|
|
|
sprite->next_in_quadrant = RCT2_ADDRESS(0xF1EF60, uint16)[0x10000];
|
|
|
|
RCT2_ADDRESS(0xF1EF60, uint16)[0x10000] = sprite->sprite_index;
|
2014-09-04 23:21:17 +02:00
|
|
|
|
2014-09-05 00:27:16 +02:00
|
|
|
return (rct_sprite*)sprite;
|
2014-09-05 19:31:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rct2: 0x0069ED0B
|
|
|
|
* This function moves a sprite to the specified sprite linked list.
|
2015-10-20 20:16:30 +02:00
|
|
|
* There are 5/6 of those, and cl specifies a pointer offset
|
2014-09-05 19:31:25 +02:00
|
|
|
* of the desired linked list in a uint16 array. Known valid values are
|
|
|
|
* 2, 4, 6, 8 or 10 (SPRITE_LINKEDLIST_OFFSET_...)
|
|
|
|
*/
|
|
|
|
void move_sprite_to_list(rct_sprite *sprite, uint8 cl)
|
|
|
|
{
|
2014-09-05 23:47:18 +02:00
|
|
|
rct_unk_sprite *unkSprite = &sprite->unknown;
|
2014-09-05 19:31:25 +02:00
|
|
|
|
|
|
|
// No need to move if the sprite is already in the desired list
|
|
|
|
if (unkSprite->linked_list_type_offset == cl)
|
|
|
|
return;
|
|
|
|
|
2015-10-20 20:16:30 +02:00
|
|
|
// If the sprite is currently the head of the list, the
|
2014-09-05 19:31:25 +02:00
|
|
|
// sprite following this one becomes the new head of the list.
|
|
|
|
if (unkSprite->previous == SPRITE_INDEX_NULL)
|
|
|
|
{
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_NEXT_INDEX + unkSprite->linked_list_type_offset, uint16) = unkSprite->next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Hook up sprite->previous->next to sprite->next, removing the sprite from its old list
|
|
|
|
g_sprite_list[unkSprite->previous].unknown.next = unkSprite->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Similarly, hook up sprite->next->previous to sprite->previous
|
|
|
|
if (unkSprite->next != SPRITE_INDEX_NULL)
|
|
|
|
{
|
|
|
|
g_sprite_list[unkSprite->next].unknown.previous = unkSprite->previous;
|
|
|
|
}
|
|
|
|
|
|
|
|
int oldListTypeOffset = unkSprite->linked_list_type_offset;
|
|
|
|
unkSprite->previous = SPRITE_INDEX_NULL; // We become the new head of the target list, so there's no previous sprite
|
|
|
|
unkSprite->linked_list_type_offset = cl;
|
|
|
|
|
|
|
|
unkSprite->next = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_NEXT_INDEX + cl, uint16); // This sprite's next sprite is the old head, since we're the new head
|
|
|
|
RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_NEXT_INDEX + cl, uint16) = unkSprite->sprite_index; // Store this sprite's index as head of its new list
|
|
|
|
|
|
|
|
if (unkSprite->next != SPRITE_INDEX_NULL)
|
|
|
|
{
|
|
|
|
// Fix the chain by settings sprite->next->previous to sprite_index
|
|
|
|
g_sprite_list[unkSprite->next].unknown.previous = unkSprite->sprite_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
// These globals are probably counters for each sprite list?
|
|
|
|
// Decrement old list counter, increment new list counter.
|
|
|
|
--(RCT2_GLOBAL(0x13573C8 + oldListTypeOffset, uint16));
|
|
|
|
++(RCT2_GLOBAL(0x13573C8 + cl, uint16));
|
2014-10-09 21:31:58 +02:00
|
|
|
}
|
|
|
|
|
2015-06-29 18:22:01 +02:00
|
|
|
/**
|
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x00673200
|
2015-06-29 18:22:01 +02:00
|
|
|
*/
|
2015-12-20 13:50:33 +01:00
|
|
|
static void sprite_steam_particle_update(rct_steam_particle *steam)
|
2015-06-29 18:22:01 +02:00
|
|
|
{
|
2015-12-20 13:50:33 +01:00
|
|
|
invalidate_sprite_2((rct_sprite*)steam);
|
|
|
|
|
|
|
|
int original_var24 = steam->var_24;
|
|
|
|
steam->var_24 += 0x5555;
|
|
|
|
if (steam->var_24 < 0x5555) {
|
|
|
|
sprite_move(
|
|
|
|
steam->x,
|
|
|
|
steam->y,
|
|
|
|
steam->z + 1,
|
|
|
|
(rct_sprite*)steam
|
|
|
|
);
|
2015-08-22 17:39:24 +02:00
|
|
|
}
|
2016-04-28 13:28:25 +02:00
|
|
|
steam->frame += 64;
|
|
|
|
if (steam->frame >= (56 * 64)) {
|
2015-12-20 13:50:33 +01:00
|
|
|
sprite_remove((rct_sprite*)steam);
|
2015-08-22 17:39:24 +02:00
|
|
|
}
|
2015-06-29 18:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2015-08-22 17:39:24 +02:00
|
|
|
* rct2: 0x0067363D
|
2015-06-29 18:22:01 +02:00
|
|
|
*/
|
2016-04-28 13:28:25 +02:00
|
|
|
void sprite_misc_explosion_cloud_create(int x, int y, int z)
|
2015-06-29 18:22:01 +02:00
|
|
|
{
|
2015-08-22 17:39:24 +02:00
|
|
|
rct_unk_sprite *sprite = (rct_unk_sprite*)create_sprite(2);
|
|
|
|
if (sprite != NULL) {
|
|
|
|
sprite->sprite_width = 44;
|
|
|
|
sprite->sprite_height_negative = 32;
|
|
|
|
sprite->sprite_height_positive = 34;
|
|
|
|
sprite->sprite_identifier = SPRITE_IDENTIFIER_MISC;
|
|
|
|
sprite_move(x, y, z + 4, (rct_sprite*)sprite);
|
2016-04-28 13:28:25 +02:00
|
|
|
sprite->misc_identifier = SPRITE_MISC_EXPLOSION_CLOUD;
|
|
|
|
sprite->frame = 0;
|
2015-08-22 17:39:24 +02:00
|
|
|
}
|
2015-06-29 18:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x00673385
|
2015-06-29 18:22:01 +02:00
|
|
|
*/
|
2016-04-28 13:28:25 +02:00
|
|
|
static void sprite_misc_explosion_cloud_update(rct_sprite * sprite)
|
2015-06-29 18:22:01 +02:00
|
|
|
{
|
2015-08-22 17:39:24 +02:00
|
|
|
invalidate_sprite_2(sprite);
|
2016-04-28 13:28:25 +02:00
|
|
|
sprite->unknown.frame += 128;
|
|
|
|
if (sprite->unknown.frame >= (36 * 128)) {
|
2015-08-22 17:39:24 +02:00
|
|
|
sprite_remove(sprite);
|
|
|
|
}
|
2015-06-29 18:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2015-08-22 17:39:24 +02:00
|
|
|
* rct2: 0x0067366B
|
2015-06-29 18:22:01 +02:00
|
|
|
*/
|
2016-04-28 13:28:25 +02:00
|
|
|
void sprite_misc_explosion_flare_create(int x, int y, int z)
|
2015-06-29 18:22:01 +02:00
|
|
|
{
|
2015-08-22 17:39:24 +02:00
|
|
|
rct_unk_sprite *sprite = (rct_unk_sprite*)create_sprite(2);
|
|
|
|
if (sprite != NULL) {
|
|
|
|
sprite->sprite_width = 25;
|
|
|
|
sprite->sprite_height_negative = 85;
|
|
|
|
sprite->sprite_height_positive = 8;
|
|
|
|
sprite->sprite_identifier = SPRITE_IDENTIFIER_MISC;
|
|
|
|
sprite_move(x, y, z + 4, (rct_sprite*)sprite);
|
2016-04-28 13:28:25 +02:00
|
|
|
sprite->misc_identifier = SPRITE_MISC_EXPLOSION_FLARE;
|
|
|
|
sprite->frame = 0;
|
2015-08-22 17:39:24 +02:00
|
|
|
}
|
2015-06-29 18:22:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x006733B4
|
2015-06-29 18:22:01 +02:00
|
|
|
*/
|
2016-04-28 13:28:25 +02:00
|
|
|
static void sprite_misc_explosion_flare_update(rct_sprite * sprite)
|
2015-06-29 18:22:01 +02:00
|
|
|
{
|
2015-08-22 17:39:24 +02:00
|
|
|
invalidate_sprite_2(sprite);
|
2016-04-28 13:28:25 +02:00
|
|
|
sprite->unknown.frame += 64;
|
|
|
|
if (sprite->unknown.frame >= (124 * 64)) {
|
2015-08-22 17:39:24 +02:00
|
|
|
sprite_remove(sprite);
|
|
|
|
}
|
2015-06-29 18:22:01 +02:00
|
|
|
}
|
|
|
|
|
2015-03-23 11:39:47 +01:00
|
|
|
/**
|
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x006731CD
|
2015-03-23 11:39:47 +01:00
|
|
|
*/
|
|
|
|
void sprite_misc_update(rct_sprite *sprite)
|
|
|
|
{
|
2015-03-24 17:48:38 +01:00
|
|
|
switch (sprite->unknown.misc_identifier) {
|
2015-12-20 13:50:33 +01:00
|
|
|
case SPRITE_MISC_STEAM_PARTICLE:
|
|
|
|
sprite_steam_particle_update((rct_steam_particle*)sprite);
|
2015-03-24 17:48:38 +01:00
|
|
|
break;
|
|
|
|
case SPRITE_MISC_MONEY_EFFECT:
|
2015-03-25 15:31:10 +01:00
|
|
|
money_effect_update(&sprite->money_effect);
|
2015-03-24 17:48:38 +01:00
|
|
|
break;
|
2015-08-22 17:39:24 +02:00
|
|
|
case SPRITE_MISC_CRASHED_VEHICLE_PARTICLE:
|
|
|
|
crashed_vehicle_particle_update((rct_crashed_vehicle_particle*)sprite);
|
2015-03-24 17:48:38 +01:00
|
|
|
break;
|
2016-04-28 13:28:25 +02:00
|
|
|
case SPRITE_MISC_EXPLOSION_CLOUD:
|
|
|
|
sprite_misc_explosion_cloud_update(sprite);
|
2015-03-24 17:48:38 +01:00
|
|
|
break;
|
2015-08-22 17:39:24 +02:00
|
|
|
case SPRITE_MISC_CRASH_SPLASH:
|
|
|
|
crash_splash_update((rct_crash_splash*)sprite);
|
2015-03-24 17:48:38 +01:00
|
|
|
break;
|
2016-04-28 13:28:25 +02:00
|
|
|
case SPRITE_MISC_EXPLOSION_FLARE:
|
|
|
|
sprite_misc_explosion_flare_update(sprite);
|
2015-03-24 17:48:38 +01:00
|
|
|
break;
|
|
|
|
case SPRITE_MISC_JUMPING_FOUNTAIN_WATER:
|
|
|
|
case SPRITE_MISC_JUMPING_FOUNTAIN_SNOW:
|
|
|
|
jumping_fountain_update(&sprite->jumping_fountain);
|
|
|
|
break;
|
|
|
|
case SPRITE_MISC_BALLOON:
|
|
|
|
balloon_update(&sprite->balloon);
|
|
|
|
break;
|
|
|
|
case SPRITE_MISC_DUCK:
|
|
|
|
duck_update(&sprite->duck);
|
|
|
|
break;
|
|
|
|
}
|
2015-03-23 11:39:47 +01:00
|
|
|
}
|
|
|
|
|
2014-10-09 21:31:58 +02:00
|
|
|
/**
|
|
|
|
*
|
2015-12-11 16:38:37 +01:00
|
|
|
* rct2: 0x00672AA4
|
2014-10-09 21:31:58 +02:00
|
|
|
*/
|
2015-03-23 11:39:47 +01:00
|
|
|
void sprite_misc_update_all()
|
2014-10-09 21:31:58 +02:00
|
|
|
{
|
2015-03-23 11:39:47 +01:00
|
|
|
rct_sprite *sprite;
|
|
|
|
uint16 spriteIndex;
|
|
|
|
|
|
|
|
spriteIndex = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_MISC, uint16);
|
|
|
|
while (spriteIndex != SPRITE_INDEX_NULL) {
|
|
|
|
sprite = &g_sprite_list[spriteIndex];
|
|
|
|
spriteIndex = sprite->unknown.next;
|
|
|
|
sprite_misc_update(sprite);
|
|
|
|
}
|
2014-09-20 17:18:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-12-11 16:38:37 +01:00
|
|
|
* Moves a sprite to a new location.
|
2014-09-20 17:18:02 +02:00
|
|
|
* rct2: 0x0069E9D3
|
2016-02-04 10:14:27 +01:00
|
|
|
*
|
|
|
|
* @param x (ax)
|
|
|
|
* @param y (cx)
|
|
|
|
* @param z (dx)
|
|
|
|
* @param sprite (esi)
|
2014-09-20 17:18:02 +02:00
|
|
|
*/
|
2015-06-03 20:00:16 +02:00
|
|
|
void sprite_move(sint16 x, sint16 y, sint16 z, rct_sprite* sprite){
|
2015-06-09 19:16:27 +02:00
|
|
|
if (x < 0 || y < 0 || x > 0x1FFF || y > 0x1FFF)
|
2015-10-25 16:34:01 +01:00
|
|
|
x = SPRITE_LOCATION_NULL;
|
2015-06-09 19:16:27 +02:00
|
|
|
|
2014-09-20 17:18:02 +02:00
|
|
|
int new_position = x;
|
2015-10-25 16:34:01 +01:00
|
|
|
if (x == SPRITE_LOCATION_NULL)new_position = 0x10000;
|
2014-09-20 17:18:02 +02:00
|
|
|
else{
|
|
|
|
new_position &= 0x1FE0;
|
|
|
|
new_position = (y >> 5) | (new_position << 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
int current_position = sprite->unknown.x;
|
2015-10-25 16:34:01 +01:00
|
|
|
if (sprite->unknown.x == SPRITE_LOCATION_NULL)current_position = 0x10000;
|
2014-09-20 17:18:02 +02:00
|
|
|
else{
|
|
|
|
current_position &= 0x1FE0;
|
|
|
|
current_position = (sprite->unknown.y >> 5) | (current_position << 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_position != current_position){
|
|
|
|
uint16* sprite_idx = &RCT2_ADDRESS(0xF1EF60, uint16)[current_position];
|
|
|
|
rct_sprite* sprite2 = &g_sprite_list[*sprite_idx];
|
|
|
|
while (sprite != sprite2){
|
2015-03-09 23:06:08 +01:00
|
|
|
sprite_idx = &sprite2->unknown.next_in_quadrant;
|
2014-09-20 17:18:02 +02:00
|
|
|
sprite2 = &g_sprite_list[*sprite_idx];
|
|
|
|
}
|
2015-03-09 23:06:08 +01:00
|
|
|
*sprite_idx = sprite->unknown.next_in_quadrant;
|
2014-09-20 17:18:02 +02:00
|
|
|
|
|
|
|
int temp_sprite_idx = RCT2_ADDRESS(0xF1EF60, uint16)[new_position];
|
|
|
|
RCT2_ADDRESS(0xF1EF60, uint16)[new_position] = sprite->unknown.sprite_index;
|
2015-03-09 23:06:08 +01:00
|
|
|
sprite->unknown.next_in_quadrant = temp_sprite_idx;
|
2014-09-20 17:18:02 +02:00
|
|
|
}
|
|
|
|
|
2015-10-25 16:34:01 +01:00
|
|
|
if (x == SPRITE_LOCATION_NULL){
|
|
|
|
sprite->unknown.sprite_left = SPRITE_LOCATION_NULL;
|
2014-09-20 17:18:02 +02:00
|
|
|
sprite->unknown.x = x;
|
|
|
|
sprite->unknown.y = y;
|
|
|
|
sprite->unknown.z = z;
|
|
|
|
return;
|
|
|
|
}
|
2015-06-03 20:00:16 +02:00
|
|
|
sint16 new_x = x, new_y = y, start_x = x;
|
2015-10-30 11:00:35 +01:00
|
|
|
switch (get_current_rotation()){
|
2014-09-20 17:18:02 +02:00
|
|
|
case 0:
|
|
|
|
new_x = new_y - new_x;
|
|
|
|
new_y = (new_y + start_x) / 2 - z;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
new_x = -new_y - new_x;
|
|
|
|
new_y = (new_y - start_x) / 2 - z;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
new_x = -new_y + new_x;
|
|
|
|
new_y = (-new_y - start_x) / 2 - z;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
new_x = new_y + new_x;
|
|
|
|
new_y = (-new_y + start_x) / 2 - z;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-18 19:27:36 +01:00
|
|
|
sprite->unknown.sprite_left = new_x - sprite->unknown.sprite_width;
|
|
|
|
sprite->unknown.sprite_right = new_x + sprite->unknown.sprite_width;
|
|
|
|
sprite->unknown.sprite_top = new_y - sprite->unknown.sprite_height_negative;
|
|
|
|
sprite->unknown.sprite_bottom = new_y + sprite->unknown.sprite_height_positive;
|
2014-09-20 17:18:02 +02:00
|
|
|
sprite->unknown.x = x;
|
|
|
|
sprite->unknown.y = y;
|
|
|
|
sprite->unknown.z = z;
|
2014-11-10 01:40:59 +01:00
|
|
|
}
|
|
|
|
|
2014-12-10 02:44:30 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x0069EDB6
|
|
|
|
*/
|
|
|
|
void sprite_remove(rct_sprite *sprite)
|
|
|
|
{
|
2015-08-31 17:20:56 +02:00
|
|
|
move_sprite_to_list(sprite, SPRITE_LINKEDLIST_OFFSET_NULL);
|
|
|
|
user_string_free(sprite->unknown.name_string_idx);
|
|
|
|
sprite->unknown.sprite_identifier = SPRITE_IDENTIFIER_NULL;
|
|
|
|
|
|
|
|
uint32 quadrantIndex = sprite->unknown.x;
|
|
|
|
if (sprite->unknown.x == SPRITE_LOCATION_NULL) {
|
|
|
|
quadrantIndex = 0x10000;
|
|
|
|
} else {
|
|
|
|
quadrantIndex = (floor2(sprite->unknown.x, 32) << 3) | (sprite->unknown.y >> 5);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 *spriteIndex = &RCT2_ADDRESS(0x00F1EF60, uint16)[quadrantIndex];
|
|
|
|
rct_sprite *quadrantSprite;
|
|
|
|
while ((quadrantSprite = &g_sprite_list[*spriteIndex]) != sprite) {
|
|
|
|
spriteIndex = &quadrantSprite->unknown.next_in_quadrant;
|
|
|
|
}
|
|
|
|
*spriteIndex = sprite->unknown.next_in_quadrant;
|
2015-06-28 18:45:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool litter_can_be_at(int x, int y, int z)
|
|
|
|
{
|
|
|
|
rct_map_element *mapElement;
|
|
|
|
|
|
|
|
if (!map_is_location_owned(x & 0xFFE0, y & 0xFFE0, z))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
mapElement = map_get_first_element_at(x >> 5, y >> 5);
|
|
|
|
do {
|
|
|
|
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_PATH)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int pathZ = mapElement->base_height * 8;
|
|
|
|
if (pathZ < z || pathZ >= z + 32)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (map_element_is_underground(mapElement))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
} while (!map_element_is_last_for_tile(mapElement++));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x0067375D
|
|
|
|
*/
|
|
|
|
void litter_create(int x, int y, int z, int direction, int type)
|
|
|
|
{
|
2016-03-20 05:25:19 +01:00
|
|
|
if (gCheatsDisableLittering)
|
|
|
|
return;
|
2015-06-28 18:45:19 +02:00
|
|
|
|
2015-10-17 00:26:55 +02:00
|
|
|
x += TileDirectionDelta[direction >> 3].x / 8;
|
|
|
|
y += TileDirectionDelta[direction >> 3].y / 8;
|
2015-06-28 18:45:19 +02:00
|
|
|
|
|
|
|
if (!litter_can_be_at(x, y, z))
|
|
|
|
return;
|
|
|
|
|
2016-03-20 05:25:19 +01:00
|
|
|
rct_litter *litter, *newestLitter;
|
|
|
|
uint16 spriteIndex, nextSpriteIndex;
|
|
|
|
uint32 newestLitterCreationTick;
|
|
|
|
|
2015-06-28 18:45:19 +02:00
|
|
|
if (RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_COUNT_LITTER, uint16) >= 500) {
|
|
|
|
newestLitter = NULL;
|
|
|
|
newestLitterCreationTick = 0;
|
|
|
|
for (spriteIndex = RCT2_GLOBAL(RCT2_ADDRESS_SPRITES_START_LITTER, uint16); spriteIndex != SPRITE_INDEX_NULL; spriteIndex = nextSpriteIndex) {
|
|
|
|
litter = &(g_sprite_list[spriteIndex].litter);
|
|
|
|
nextSpriteIndex = litter->next;
|
|
|
|
if (newestLitterCreationTick <= litter->creationTick) {
|
|
|
|
newestLitterCreationTick = litter->creationTick;
|
|
|
|
newestLitter = litter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newestLitter != NULL) {
|
2015-08-22 12:56:32 +02:00
|
|
|
invalidate_sprite_0((rct_sprite*)newestLitter);
|
2015-06-28 18:45:19 +02:00
|
|
|
sprite_remove((rct_sprite*)newestLitter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
litter = (rct_litter*)create_sprite(1);
|
|
|
|
if (litter == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
move_sprite_to_list((rct_sprite*)litter, SPRITE_LINKEDLIST_OFFSET_LITTER);
|
|
|
|
litter->sprite_direction = direction;
|
|
|
|
litter->sprite_width = 6;
|
|
|
|
litter->sprite_height_negative = 6;
|
|
|
|
litter->sprite_height_positive = 3;
|
|
|
|
litter->sprite_identifier = SPRITE_IDENTIFIER_LITTER;
|
|
|
|
litter->type = type;
|
|
|
|
sprite_move(x, y, z, (rct_sprite*)litter);
|
2015-08-22 12:56:32 +02:00
|
|
|
invalidate_sprite_0((rct_sprite*)litter);
|
2016-05-09 00:20:42 +02:00
|
|
|
litter->creationTick = gScenarioTicks;
|
2015-06-29 18:22:01 +02:00
|
|
|
}
|
|
|
|
|
2015-07-02 19:22:38 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* rct2: 0x006738E1
|
|
|
|
*/
|
2015-09-05 14:59:12 +02:00
|
|
|
void litter_remove_at(int x, int y, int z)
|
2015-07-02 19:22:38 +02:00
|
|
|
{
|
2015-09-05 14:59:12 +02:00
|
|
|
uint16 spriteIndex = sprite_get_first_in_quadrant(x, y);
|
|
|
|
while (spriteIndex != SPRITE_INDEX_NULL) {
|
|
|
|
rct_sprite *sprite = &g_sprite_list[spriteIndex];
|
|
|
|
uint16 nextSpriteIndex = sprite->unknown.next_in_quadrant;
|
|
|
|
if (sprite->unknown.linked_list_type_offset == SPRITE_LINKEDLIST_OFFSET_LITTER) {
|
|
|
|
rct_litter *litter = &sprite->litter;
|
|
|
|
|
|
|
|
if (abs(litter->z - z) <= 16) {
|
|
|
|
if (abs(litter->x - x) <= 8 && abs(litter->y - y) <= 8) {
|
|
|
|
invalidate_sprite_0(sprite);
|
|
|
|
sprite_remove(sprite);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spriteIndex = nextSpriteIndex;
|
|
|
|
}
|
2015-07-02 19:22:38 +02:00
|
|
|
}
|