1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9 #include "Particle.h"
10
11 #include "../audio/audio.h"
12 #include "../paint/sprite/Paint.Sprite.h"
13 #include "../scenario/Scenario.h"
14 #include "Sprite.h"
15
16 #include <iterator>
17
Is() const18 template<> bool EntityBase::Is<VehicleCrashParticle>() const
19 {
20 return Type == EntityType::CrashedVehicleParticle;
21 }
22
Is() const23 template<> bool EntityBase::Is<CrashSplashParticle>() const
24 {
25 return Type == EntityType::CrashSplash;
26 }
27 /**
28 *
29 * rct2: 0x006735A1
30 */
Create(rct_vehicle_colour colours,const CoordsXYZ & vehiclePos)31 void VehicleCrashParticle::Create(rct_vehicle_colour colours, const CoordsXYZ& vehiclePos)
32 {
33 VehicleCrashParticle* sprite = CreateEntity<VehicleCrashParticle>();
34 if (sprite != nullptr)
35 {
36 sprite->colour[0] = colours.body_colour;
37 sprite->colour[1] = colours.trim_colour;
38 sprite->sprite_width = 8;
39 sprite->sprite_height_negative = 8;
40 sprite->sprite_height_positive = 8;
41 sprite->MoveTo(vehiclePos);
42
43 sprite->frame = (scenario_rand() & 0xFF) * 12;
44 sprite->time_to_live = (scenario_rand() & 0x7F) + 140;
45 sprite->crashed_sprite_base = scenario_rand_max(static_cast<uint32_t>(std::size(vehicle_particle_base_sprites)));
46 sprite->acceleration_x = (static_cast<int16_t>(scenario_rand() & 0xFFFF)) * 4;
47 sprite->acceleration_y = (static_cast<int16_t>(scenario_rand() & 0xFFFF)) * 4;
48 sprite->acceleration_z = (scenario_rand() & 0xFFFF) * 4 + 0x10000;
49 sprite->velocity_x = 0;
50 sprite->velocity_y = 0;
51 sprite->velocity_z = 0;
52 }
53 }
54
55 /**
56 *
57 * rct2: 0x00673298
58 */
Update()59 void VehicleCrashParticle::Update()
60 {
61 Invalidate();
62 time_to_live--;
63 if (time_to_live == 0)
64 {
65 sprite_remove(this);
66 return;
67 }
68
69 // Apply gravity
70 acceleration_z -= 5041;
71
72 // Apply air resistance
73 acceleration_x -= (acceleration_x / 256);
74 acceleration_y -= (acceleration_y / 256);
75 acceleration_z -= (acceleration_z / 256);
76
77 // Update velocity and position
78 int32_t vx = velocity_x + acceleration_x;
79 int32_t vy = velocity_y + acceleration_y;
80 int32_t vz = velocity_z + acceleration_z;
81
82 CoordsXYZ newLoc = { x + (vx >> 16), y + (vy >> 16), z + (vz >> 16) };
83
84 velocity_x = vx & 0xFFFF;
85 velocity_y = vy & 0xFFFF;
86 velocity_z = vz & 0xFFFF;
87
88 // Check collision with land / water
89 int16_t landZ = tile_element_height(newLoc);
90 int16_t waterZ = tile_element_water_height(newLoc);
91
92 if (waterZ != 0 && z >= waterZ && newLoc.z <= waterZ)
93 {
94 // Splash
95 OpenRCT2::Audio::Play3D(OpenRCT2::Audio::SoundId::Water2, { x, y, waterZ });
96 CrashSplashParticle::Create({ x, y, waterZ });
97 sprite_remove(this);
98 return;
99 }
100
101 if (z >= landZ && newLoc.z <= landZ)
102 {
103 // Bounce
104 acceleration_z *= -1;
105 newLoc.z = landZ;
106 }
107 MoveTo(newLoc);
108
109 frame += 85;
110 if (frame >= 3072)
111 {
112 frame = 0;
113 }
114 }
115
116 /**
117 *
118 * rct2: 0x00673699
119 */
Create(const CoordsXYZ & splashPos)120 void CrashSplashParticle::Create(const CoordsXYZ& splashPos)
121 {
122 auto* sprite = CreateEntity<CrashSplashParticle>();
123 if (sprite != nullptr)
124 {
125 sprite->sprite_width = 33;
126 sprite->sprite_height_negative = 51;
127 sprite->sprite_height_positive = 16;
128 sprite->MoveTo(splashPos + CoordsXYZ{ 0, 0, 3 });
129 sprite->frame = 0;
130 }
131 }
132
133 /**
134 *
135 * rct2: 0x0067339D
136 */
Update()137 void CrashSplashParticle::Update()
138 {
139 Invalidate();
140 frame += 85;
141 if (frame >= 7168)
142 {
143 sprite_remove(this);
144 }
145 }
146
147 /**
148 *
149 * rct2: 0x006734B2
150 */
Create(const CoordsXYZ & coords)151 void SteamParticle::Create(const CoordsXYZ& coords)
152 {
153 auto surfaceElement = map_get_surface_element_at(coords);
154 if (surfaceElement != nullptr && coords.z > surfaceElement->GetBaseZ())
155 {
156 SteamParticle* steam = CreateEntity<SteamParticle>();
157 if (steam == nullptr)
158 return;
159
160 steam->sprite_width = 20;
161 steam->sprite_height_negative = 18;
162 steam->sprite_height_positive = 16;
163 steam->frame = 256;
164 steam->time_to_move = 0;
165 steam->MoveTo(coords);
166 }
167 }
168
169 /**
170 *
171 * rct2: 0x00673200
172 */
Update()173 void SteamParticle::Update()
174 {
175 // Move up 1 z every 3 ticks (Starts after 4 ticks)
176 Invalidate();
177 time_to_move++;
178 if (time_to_move >= 4)
179 {
180 time_to_move = 1;
181 MoveTo({ x, y, z + 1 });
182 }
183 frame += 64;
184 if (frame >= (56 * 64))
185 {
186 sprite_remove(this);
187 }
188 }
189
190 /**
191 *
192 * rct2: 0x0067363D
193 */
Create(const CoordsXYZ & cloudPos)194 void ExplosionCloud::Create(const CoordsXYZ& cloudPos)
195 {
196 auto* entity = CreateEntity<ExplosionCloud>();
197 if (entity != nullptr)
198 {
199 entity->sprite_width = 44;
200 entity->sprite_height_negative = 32;
201 entity->sprite_height_positive = 34;
202 entity->MoveTo(cloudPos + CoordsXYZ{ 0, 0, 4 });
203 entity->frame = 0;
204 }
205 }
206
207 /**
208 *
209 * rct2: 0x00673385
210 */
Update()211 void ExplosionCloud::Update()
212 {
213 Invalidate();
214 frame += 128;
215 if (frame >= (36 * 128))
216 {
217 sprite_remove(this);
218 }
219 }
220
221 /**
222 *
223 * rct2: 0x0067366B
224 */
Create(const CoordsXYZ & flarePos)225 void ExplosionFlare::Create(const CoordsXYZ& flarePos)
226 {
227 auto* entity = CreateEntity<ExplosionFlare>();
228 if (entity != nullptr)
229 {
230 entity->sprite_width = 25;
231 entity->sprite_height_negative = 85;
232 entity->sprite_height_positive = 8;
233 entity->MoveTo(flarePos + CoordsXYZ{ 0, 0, 4 });
234 entity->frame = 0;
235 }
236 }
237
238 /**
239 *
240 * rct2: 0x006733B4
241 */
Update()242 void ExplosionFlare::Update()
243 {
244 Invalidate();
245 frame += 64;
246 if (frame >= (124 * 64))
247 {
248 sprite_remove(this);
249 }
250 }
251