Add: Show house information in house placer.
[openttd-github.git] / src / effectvehicle.cpp
blob8fe7b2b012b4d93f768c212422e68f9e59da9ba5
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD 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, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file effectvehicle.cpp Implementation of everything generic to vehicles. */
10 #include "stdafx.h"
11 #include "landscape.h"
12 #include "core/random_func.hpp"
13 #include "industry_map.h"
14 #include "vehicle_func.h"
15 #include "sound_func.h"
16 #include "animated_tile_func.h"
17 #include "effectvehicle_func.h"
18 #include "effectvehicle_base.h"
20 #include "safeguards.h"
23 /**
24 * Increment the sprite unless it has reached the end of the animation.
25 * @param v Vehicle to increment sprite of.
26 * @param last Last sprite of animation.
27 * @return true if the sprite was incremented, false if the end was reached.
29 static bool IncrementSprite(EffectVehicle *v, SpriteID last)
31 if (v->sprite_cache.sprite_seq.seq[0].sprite != last) {
32 v->sprite_cache.sprite_seq.seq[0].sprite++;
33 return true;
34 } else {
35 return false;
39 static void ChimneySmokeInit(EffectVehicle *v)
41 uint32_t r = Random();
42 v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3));
43 v->progress = GB(r, 16, 3);
46 static bool ChimneySmokeTick(EffectVehicle *v)
48 if (v->progress > 0) {
49 v->progress--;
50 } else {
51 TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
52 if (!IsTileType(tile, MP_INDUSTRY)) {
53 delete v;
54 return false;
57 if (!IncrementSprite(v, SPR_CHIMNEY_SMOKE_7)) {
58 v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0);
60 v->progress = 7;
61 v->UpdatePositionAndViewport();
64 return true;
67 static void SteamSmokeInit(EffectVehicle *v)
69 v->sprite_cache.sprite_seq.Set(SPR_STEAM_SMOKE_0);
70 v->progress = 12;
73 static bool SteamSmokeTick(EffectVehicle *v)
75 bool moved = false;
77 v->progress++;
79 if ((v->progress & 7) == 0) {
80 v->z_pos++;
81 moved = true;
84 if ((v->progress & 0xF) == 4) {
85 if (!IncrementSprite(v, SPR_STEAM_SMOKE_4)) {
86 delete v;
87 return false;
89 moved = true;
92 if (moved) v->UpdatePositionAndViewport();
94 return true;
97 static void DieselSmokeInit(EffectVehicle *v)
99 v->sprite_cache.sprite_seq.Set(SPR_DIESEL_SMOKE_0);
100 v->progress = 0;
103 static bool DieselSmokeTick(EffectVehicle *v)
105 v->progress++;
107 if ((v->progress & 3) == 0) {
108 v->z_pos++;
109 v->UpdatePositionAndViewport();
110 } else if ((v->progress & 7) == 1) {
111 if (!IncrementSprite(v, SPR_DIESEL_SMOKE_5)) {
112 delete v;
113 return false;
115 v->UpdatePositionAndViewport();
118 return true;
121 static void ElectricSparkInit(EffectVehicle *v)
123 v->sprite_cache.sprite_seq.Set(SPR_ELECTRIC_SPARK_0);
124 v->progress = 1;
127 static bool ElectricSparkTick(EffectVehicle *v)
129 if (v->progress < 2) {
130 v->progress++;
131 } else {
132 v->progress = 0;
134 if (!IncrementSprite(v, SPR_ELECTRIC_SPARK_5)) {
135 delete v;
136 return false;
138 v->UpdatePositionAndViewport();
141 return true;
144 static void SmokeInit(EffectVehicle *v)
146 v->sprite_cache.sprite_seq.Set(SPR_SMOKE_0);
147 v->progress = 12;
150 static bool SmokeTick(EffectVehicle *v)
152 bool moved = false;
154 v->progress++;
156 if ((v->progress & 3) == 0) {
157 v->z_pos++;
158 moved = true;
161 if ((v->progress & 0xF) == 4) {
162 if (!IncrementSprite(v, SPR_SMOKE_4)) {
163 delete v;
164 return false;
166 moved = true;
169 if (moved) v->UpdatePositionAndViewport();
171 return true;
174 static void ExplosionLargeInit(EffectVehicle *v)
176 v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_LARGE_0);
177 v->progress = 0;
180 static bool ExplosionLargeTick(EffectVehicle *v)
182 v->progress++;
183 if ((v->progress & 3) == 0) {
184 if (!IncrementSprite(v, SPR_EXPLOSION_LARGE_F)) {
185 delete v;
186 return false;
188 v->UpdatePositionAndViewport();
191 return true;
194 static void BreakdownSmokeInit(EffectVehicle *v)
196 v->sprite_cache.sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
197 v->progress = 0;
200 static bool BreakdownSmokeTick(EffectVehicle *v)
202 v->progress++;
203 if ((v->progress & 7) == 0) {
204 if (!IncrementSprite(v, SPR_BREAKDOWN_SMOKE_3)) {
205 v->sprite_cache.sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
207 v->UpdatePositionAndViewport();
210 v->animation_state--;
211 if (v->animation_state == 0) {
212 delete v;
213 return false;
216 return true;
219 static void ExplosionSmallInit(EffectVehicle *v)
221 v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_SMALL_0);
222 v->progress = 0;
225 static bool ExplosionSmallTick(EffectVehicle *v)
227 v->progress++;
228 if ((v->progress & 3) == 0) {
229 if (!IncrementSprite(v, SPR_EXPLOSION_SMALL_B)) {
230 delete v;
231 return false;
233 v->UpdatePositionAndViewport();
236 return true;
239 static void BulldozerInit(EffectVehicle *v)
241 v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE);
242 v->progress = 0;
243 v->animation_state = 0;
244 v->animation_substate = 0;
247 struct BulldozerMovement {
248 uint8_t direction:2;
249 uint8_t image:2;
250 uint8_t duration:3;
253 static const BulldozerMovement _bulldozer_movement[] = {
254 { 0, 0, 4 },
255 { 3, 3, 4 },
256 { 2, 2, 7 },
257 { 0, 2, 7 },
258 { 1, 1, 3 },
259 { 2, 2, 7 },
260 { 0, 2, 7 },
261 { 1, 1, 3 },
262 { 2, 2, 7 },
263 { 0, 2, 7 },
264 { 3, 3, 6 },
265 { 2, 2, 6 },
266 { 1, 1, 7 },
267 { 3, 1, 7 },
268 { 0, 0, 3 },
269 { 1, 1, 7 },
270 { 3, 1, 7 },
271 { 0, 0, 3 },
272 { 1, 1, 7 },
273 { 3, 1, 7 }
276 static const struct {
277 int8_t x;
278 int8_t y;
279 } _inc_by_dir[] = {
280 { -1, 0 },
281 { 0, 1 },
282 { 1, 0 },
283 { 0, -1 }
286 static bool BulldozerTick(EffectVehicle *v)
288 v->progress++;
289 if ((v->progress & 7) == 0) {
290 const BulldozerMovement *b = &_bulldozer_movement[v->animation_state];
292 v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE + b->image);
294 v->x_pos += _inc_by_dir[b->direction].x;
295 v->y_pos += _inc_by_dir[b->direction].y;
297 v->animation_substate++;
298 if (v->animation_substate >= b->duration) {
299 v->animation_substate = 0;
300 v->animation_state++;
301 if (v->animation_state == lengthof(_bulldozer_movement)) {
302 delete v;
303 return false;
306 v->UpdatePositionAndViewport();
309 return true;
312 static void BubbleInit(EffectVehicle *v)
314 v->sprite_cache.sprite_seq.Set(SPR_BUBBLE_GENERATE_0);
315 v->spritenum = 0;
316 v->progress = 0;
319 struct BubbleMovement {
320 int8_t x:4;
321 int8_t y:4;
322 int8_t z:4;
323 uint8_t image:4;
326 #define MK(x, y, z, i) { x, y, z, i }
327 #define ME(i) { i, 4, 0, 0 }
329 static const BubbleMovement _bubble_float_sw[] = {
330 MK(0, 0, 1, 0),
331 MK(1, 0, 1, 1),
332 MK(0, 0, 1, 0),
333 MK(1, 0, 1, 2),
334 ME(1)
338 static const BubbleMovement _bubble_float_ne[] = {
339 MK( 0, 0, 1, 0),
340 MK(-1, 0, 1, 1),
341 MK( 0, 0, 1, 0),
342 MK(-1, 0, 1, 2),
343 ME(1)
346 static const BubbleMovement _bubble_float_se[] = {
347 MK(0, 0, 1, 0),
348 MK(0, 1, 1, 1),
349 MK(0, 0, 1, 0),
350 MK(0, 1, 1, 2),
351 ME(1)
354 static const BubbleMovement _bubble_float_nw[] = {
355 MK(0, 0, 1, 0),
356 MK(0, -1, 1, 1),
357 MK(0, 0, 1, 0),
358 MK(0, -1, 1, 2),
359 ME(1)
362 static const BubbleMovement _bubble_burst[] = {
363 MK(0, 0, 1, 2),
364 MK(0, 0, 1, 7),
365 MK(0, 0, 1, 8),
366 MK(0, 0, 1, 9),
367 ME(0)
370 static const BubbleMovement _bubble_absorb[] = {
371 MK(0, 0, 1, 0),
372 MK(0, 0, 1, 1),
373 MK(0, 0, 1, 0),
374 MK(0, 0, 1, 2),
375 MK(0, 0, 1, 0),
376 MK(0, 0, 1, 1),
377 MK(0, 0, 1, 0),
378 MK(0, 0, 1, 2),
379 MK(0, 0, 1, 0),
380 MK(0, 0, 1, 1),
381 MK(0, 0, 1, 0),
382 MK(0, 0, 1, 2),
383 MK(0, 0, 1, 0),
384 MK(0, 0, 1, 1),
385 MK(0, 0, 1, 0),
386 MK(0, 0, 1, 2),
387 MK(0, 0, 1, 0),
388 MK(0, 0, 1, 1),
389 MK(0, 0, 1, 0),
390 MK(0, 0, 1, 2),
391 MK(0, 0, 1, 0),
392 MK(0, 0, 1, 1),
393 MK(0, 0, 1, 0),
394 MK(0, 0, 1, 2),
395 MK(0, 0, 1, 0),
396 MK(0, 0, 1, 1),
397 MK(0, 0, 1, 0),
398 MK(0, 0, 1, 2),
399 MK(0, 0, 1, 0),
400 MK(0, 0, 1, 1),
401 MK(0, 0, 1, 0),
402 MK(0, 0, 1, 2),
403 MK(0, 0, 1, 0),
404 MK(0, 0, 1, 1),
405 MK(0, 0, 1, 0),
406 MK(0, 0, 1, 2),
407 MK(0, 0, 1, 0),
408 MK(0, 0, 1, 1),
409 MK(0, 0, 1, 0),
410 MK(0, 0, 1, 2),
411 MK(0, 0, 1, 0),
412 MK(0, 0, 1, 1),
413 MK(0, 0, 1, 0),
414 MK(0, 0, 1, 2),
415 MK(0, 0, 1, 0),
416 MK(0, 0, 1, 1),
417 MK(0, 0, 1, 0),
418 MK(0, 0, 1, 2),
419 MK(0, 0, 1, 0),
420 MK(0, 0, 1, 1),
421 MK(0, 0, 1, 0),
422 MK(0, 0, 1, 2),
423 MK(0, 0, 1, 0),
424 MK(0, 0, 1, 1),
425 MK(0, 0, 1, 0),
426 MK(0, 0, 1, 2),
427 MK(0, 0, 1, 0),
428 MK(0, 0, 1, 1),
429 MK(0, 0, 1, 0),
430 MK(0, 0, 1, 2),
431 MK(0, 0, 1, 0),
432 MK(0, 0, 1, 1),
433 MK(2, 1, 3, 0),
434 MK(1, 1, 3, 1),
435 MK(2, 1, 3, 0),
436 MK(1, 1, 3, 2),
437 MK(2, 1, 3, 0),
438 MK(1, 1, 3, 1),
439 MK(2, 1, 3, 0),
440 MK(1, 0, 1, 2),
441 MK(0, 0, 1, 0),
442 MK(1, 0, 1, 1),
443 MK(0, 0, 1, 0),
444 MK(1, 0, 1, 2),
445 MK(0, 0, 1, 0),
446 MK(1, 0, 1, 1),
447 MK(0, 0, 1, 0),
448 MK(1, 0, 1, 2),
449 ME(2),
450 MK(0, 0, 0, 0xA),
451 MK(0, 0, 0, 0xB),
452 MK(0, 0, 0, 0xC),
453 MK(0, 0, 0, 0xD),
454 MK(0, 0, 0, 0xE),
455 ME(0)
457 #undef ME
458 #undef MK
460 static const BubbleMovement * const _bubble_movement[] = {
461 _bubble_float_sw,
462 _bubble_float_ne,
463 _bubble_float_se,
464 _bubble_float_nw,
465 _bubble_burst,
466 _bubble_absorb,
469 static bool BubbleTick(EffectVehicle *v)
471 uint anim_state;
473 v->progress++;
474 if ((v->progress & 3) != 0) return true;
476 if (v->spritenum == 0) {
477 v->sprite_cache.sprite_seq.seq[0].sprite++;
478 if (v->sprite_cache.sprite_seq.seq[0].sprite < SPR_BUBBLE_GENERATE_3) {
479 v->UpdatePositionAndViewport();
480 return true;
482 if (v->animation_substate != 0) {
483 v->spritenum = GB(Random(), 0, 2) + 1;
484 } else {
485 v->spritenum = 6;
487 anim_state = 0;
488 } else {
489 anim_state = v->animation_state + 1;
492 const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][anim_state];
494 if (b->y == 4 && b->x == 0) {
495 delete v;
496 return false;
499 if (b->y == 4 && b->x == 1) {
500 if (v->z_pos > 180 || Chance16I(1, 96, Random())) {
501 v->spritenum = 5;
502 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_2F_BUBBLE_GENERATOR_FAIL, v);
504 anim_state = 0;
507 if (b->y == 4 && b->x == 2) {
508 TileIndex tile;
510 anim_state++;
511 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_31_BUBBLE_GENERATOR_SUCCESS, v);
513 tile = TileVirtXY(v->x_pos, v->y_pos);
514 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
517 v->animation_state = anim_state;
518 b = &_bubble_movement[v->spritenum - 1][anim_state];
520 v->x_pos += b->x;
521 v->y_pos += b->y;
522 v->z_pos += b->z;
523 v->sprite_cache.sprite_seq.Set(SPR_BUBBLE_0 + b->image);
525 v->UpdatePositionAndViewport();
527 return true;
530 struct EffectProcs {
531 using InitProc = void(EffectVehicle *);
532 using TickProc = bool(EffectVehicle *);
534 InitProc *init_proc; ///< Function to initialise an effect vehicle after construction.
535 TickProc *tick_proc; ///< Functions for controlling effect vehicles at each tick.
536 TransparencyOption transparency; ///< Transparency option affecting the effect.
538 constexpr EffectProcs(InitProc *init_proc, TickProc *tick_proc, TransparencyOption transparency)
539 : init_proc(init_proc), tick_proc(tick_proc), transparency(transparency) {}
542 /** Per-EffectVehicleType handling. */
543 static std::array<EffectProcs, EV_END> _effect_procs = {{
544 { ChimneySmokeInit, ChimneySmokeTick, TO_INDUSTRIES }, // EV_CHIMNEY_SMOKE
545 { SteamSmokeInit, SteamSmokeTick, TO_INVALID }, // EV_STEAM_SMOKE
546 { DieselSmokeInit, DieselSmokeTick, TO_INVALID }, // EV_DIESEL_SMOKE
547 { ElectricSparkInit, ElectricSparkTick, TO_INVALID }, // EV_ELECTRIC_SPARK
548 { SmokeInit, SmokeTick, TO_INVALID }, // EV_CRASH_SMOKE
549 { ExplosionLargeInit, ExplosionLargeTick, TO_INVALID }, // EV_EXPLOSION_LARGE
550 { BreakdownSmokeInit, BreakdownSmokeTick, TO_INVALID }, // EV_BREAKDOWN_SMOKE
551 { ExplosionSmallInit, ExplosionSmallTick, TO_INVALID }, // EV_EXPLOSION_SMALL
552 { BulldozerInit, BulldozerTick, TO_INVALID }, // EV_BULLDOZER
553 { BubbleInit, BubbleTick, TO_INDUSTRIES }, // EV_BUBBLE
554 { SmokeInit, SmokeTick, TO_INVALID }, // EV_BREAKDOWN_SMOKE_AIRCRAFT
555 { SmokeInit, SmokeTick, TO_INDUSTRIES }, // EV_COPPER_MINE_SMOKE
559 * Create an effect vehicle at a particular location.
560 * @param x The x location on the map.
561 * @param y The y location on the map.
562 * @param z The z location on the map.
563 * @param type The type of effect vehicle.
564 * @return The effect vehicle.
566 EffectVehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
568 if (!Vehicle::CanAllocateItem()) return nullptr;
570 EffectVehicle *v = new EffectVehicle();
571 v->subtype = type;
572 v->x_pos = x;
573 v->y_pos = y;
574 v->z_pos = z;
575 v->tile = 0;
576 v->UpdateDeltaXY();
577 v->vehstatus = VS_UNCLICKABLE;
579 _effect_procs[type].init_proc(v);
581 v->UpdatePositionAndViewport();
583 return v;
587 * Create an effect vehicle above a particular location.
588 * @param x The x location on the map.
589 * @param y The y location on the map.
590 * @param z The offset from the ground.
591 * @param type The type of effect vehicle.
592 * @return The effect vehicle.
594 EffectVehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
596 int safe_x = Clamp(x, 0, Map::MaxX() * TILE_SIZE);
597 int safe_y = Clamp(y, 0, Map::MaxY() * TILE_SIZE);
598 return CreateEffectVehicle(x, y, GetSlopePixelZ(safe_x, safe_y) + z, type);
602 * Create an effect vehicle above a particular vehicle.
603 * @param v The vehicle to base the position on.
604 * @param x The x offset to the vehicle.
605 * @param y The y offset to the vehicle.
606 * @param z The z offset to the vehicle.
607 * @param type The type of effect vehicle.
608 * @return The effect vehicle.
610 EffectVehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
612 return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
615 bool EffectVehicle::Tick()
617 return _effect_procs[this->subtype].tick_proc(this);
620 void EffectVehicle::UpdateDeltaXY()
622 this->x_offs = 0;
623 this->y_offs = 0;
624 this->x_extent = 1;
625 this->y_extent = 1;
626 this->z_extent = 1;
630 * Determines the transparency option affecting the effect.
631 * @return Transparency option, or TO_INVALID if none.
633 TransparencyOption EffectVehicle::GetTransparencyOption() const
635 return _effect_procs[this->subtype].transparency;