Fix crash when setting separation mode for vehicles with no orders list.
[openttd-joker.git] / src / effectvehicle.cpp
blob84370302f4f5fe5c8003e0889cb13cee29c04b3c
1 /* $Id: effectvehicle.cpp 24900 2013-01-08 22:46:42Z planetmaker $ */
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
10 /** @file effectvehicle.cpp Implementation of everything generic to vehicles. */
12 #include "stdafx.h"
13 #include "landscape.h"
14 #include "core/random_func.hpp"
15 #include "industry_map.h"
16 #include "vehicle_func.h"
17 #include "sound_func.h"
18 #include "animated_tile_func.h"
19 #include "effectvehicle_func.h"
20 #include "effectvehicle_base.h"
22 #include "safeguards.h"
25 /**
26 * Increment the sprite unless it has reached the end of the animation.
27 * @param v Vehicle to increment sprite of.
28 * @param last Last sprite of animation.
29 * @return true if the sprite was incremented, false if the end was reached.
31 static bool IncrementSprite(EffectVehicle *v, SpriteID last)
33 if (v->sprite_seq.seq[0].sprite != last) {
34 v->sprite_seq.seq[0].sprite++;
35 v->UpdateSpriteSeqBound();
36 return true;
37 } else {
38 return false;
42 static void ChimneySmokeInit(EffectVehicle *v)
44 uint32 r = Random();
45 v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3));
46 v->UpdateSpriteSeqBound();
47 v->progress = GB(r, 16, 3);
50 static bool ChimneySmokeTick(EffectVehicle *v)
52 if (v->progress > 0) {
53 v->progress--;
54 } else {
55 TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
56 if (!IsTileType(tile, MP_INDUSTRY)) {
57 delete v;
58 return false;
61 if (!IncrementSprite(v, SPR_CHIMNEY_SMOKE_7)) {
62 v->sprite_seq.Set(SPR_CHIMNEY_SMOKE_0);
64 v->UpdateSpriteSeqBound();
65 v->progress = 7;
66 v->UpdatePositionAndViewport();
69 return true;
72 static void SteamSmokeInit(EffectVehicle *v)
74 v->sprite_seq.Set(SPR_STEAM_SMOKE_0);
75 v->UpdateSpriteSeqBound();
76 v->progress = 12;
79 static bool SteamSmokeTick(EffectVehicle *v)
81 bool moved = false;
83 v->progress++;
85 if ((v->progress & 7) == 0) {
86 v->z_pos++;
87 moved = true;
90 if ((v->progress & 0xF) == 4) {
91 if (!IncrementSprite(v, SPR_STEAM_SMOKE_4)) {
92 delete v;
93 return false;
95 v->UpdateSpriteSeqBound();
96 moved = true;
99 if (moved) v->UpdatePositionAndViewport();
101 return true;
104 static void DieselSmokeInit(EffectVehicle *v)
106 v->sprite_seq.Set(SPR_DIESEL_SMOKE_0);
107 v->UpdateSpriteSeqBound();
108 v->progress = 0;
111 static bool DieselSmokeTick(EffectVehicle *v)
113 v->progress++;
115 if ((v->progress & 3) == 0) {
116 v->z_pos++;
117 v->UpdatePositionAndViewport();
118 } else if ((v->progress & 7) == 1) {
119 if (!IncrementSprite(v, SPR_DIESEL_SMOKE_5)) {
120 delete v;
121 return false;
123 v->UpdateSpriteSeqBound();
124 v->UpdatePositionAndViewport();
127 return true;
130 static void ElectricSparkInit(EffectVehicle *v)
132 v->sprite_seq.Set(SPR_ELECTRIC_SPARK_0);
133 v->UpdateSpriteSeqBound();
134 v->progress = 1;
137 static bool ElectricSparkTick(EffectVehicle *v)
139 if (v->progress < 2) {
140 v->progress++;
141 } else {
142 v->progress = 0;
144 if (!IncrementSprite(v, SPR_ELECTRIC_SPARK_5)) {
145 delete v;
146 return false;
148 v->UpdateSpriteSeqBound();
149 v->UpdatePositionAndViewport();
152 return true;
155 static void SmokeInit(EffectVehicle *v)
157 v->sprite_seq.Set(SPR_SMOKE_0);
158 v->UpdateSpriteSeqBound();
159 v->progress = 12;
162 static bool SmokeTick(EffectVehicle *v)
164 bool moved = false;
166 v->progress++;
168 if ((v->progress & 3) == 0) {
169 v->z_pos++;
170 moved = true;
173 if ((v->progress & 0xF) == 4) {
174 if (!IncrementSprite(v, SPR_SMOKE_4)) {
175 delete v;
176 return false;
178 v->UpdateSpriteSeqBound();
179 moved = true;
182 if (moved) v->UpdatePositionAndViewport();
184 return true;
187 static void ExplosionLargeInit(EffectVehicle *v)
189 v->sprite_seq.Set(SPR_EXPLOSION_LARGE_0);
190 v->UpdateSpriteSeqBound();
191 v->progress = 0;
194 static bool ExplosionLargeTick(EffectVehicle *v)
196 v->progress++;
197 if ((v->progress & 3) == 0) {
198 if (!IncrementSprite(v, SPR_EXPLOSION_LARGE_F)) {
199 delete v;
200 return false;
202 v->UpdateSpriteSeqBound();
203 v->UpdatePositionAndViewport();
206 return true;
209 static void BreakdownSmokeInit(EffectVehicle *v)
211 v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
212 v->UpdateSpriteSeqBound();
213 v->progress = 0;
216 static bool BreakdownSmokeTick(EffectVehicle *v)
218 v->progress++;
219 if ((v->progress & 7) == 0) {
220 if (!IncrementSprite(v, SPR_BREAKDOWN_SMOKE_3)) {
221 v->sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
223 v->UpdateSpriteSeqBound();
224 v->UpdatePositionAndViewport();
227 v->animation_state--;
228 if (v->animation_state == 0) {
229 delete v;
230 return false;
233 return true;
236 static void ExplosionSmallInit(EffectVehicle *v)
238 v->sprite_seq.Set(SPR_EXPLOSION_SMALL_0);
239 v->UpdateSpriteSeqBound();
240 v->progress = 0;
243 static bool ExplosionSmallTick(EffectVehicle *v)
245 v->progress++;
246 if ((v->progress & 3) == 0) {
247 if (!IncrementSprite(v, SPR_EXPLOSION_SMALL_B)) {
248 delete v;
249 return false;
251 v->UpdateSpriteSeqBound();
252 v->UpdatePositionAndViewport();
255 return true;
258 static void BulldozerInit(EffectVehicle *v)
260 v->sprite_seq.Set(SPR_BULLDOZER_NE);
261 v->UpdateSpriteSeqBound();
262 v->progress = 0;
263 v->animation_state = 0;
264 v->animation_substate = 0;
267 struct BulldozerMovement {
268 byte direction:2;
269 byte image:2;
270 byte duration:3;
273 static const BulldozerMovement _bulldozer_movement[] = {
274 { 0, 0, 4 },
275 { 3, 3, 4 },
276 { 2, 2, 7 },
277 { 0, 2, 7 },
278 { 1, 1, 3 },
279 { 2, 2, 7 },
280 { 0, 2, 7 },
281 { 1, 1, 3 },
282 { 2, 2, 7 },
283 { 0, 2, 7 },
284 { 3, 3, 6 },
285 { 2, 2, 6 },
286 { 1, 1, 7 },
287 { 3, 1, 7 },
288 { 0, 0, 3 },
289 { 1, 1, 7 },
290 { 3, 1, 7 },
291 { 0, 0, 3 },
292 { 1, 1, 7 },
293 { 3, 1, 7 }
296 static const struct {
297 int8 x;
298 int8 y;
299 } _inc_by_dir[] = {
300 { -1, 0 },
301 { 0, 1 },
302 { 1, 0 },
303 { 0, -1 }
306 static bool BulldozerTick(EffectVehicle *v)
308 v->progress++;
309 if ((v->progress & 7) == 0) {
310 const BulldozerMovement *b = &_bulldozer_movement[v->animation_state];
312 v->sprite_seq.Set(SPR_BULLDOZER_NE + b->image);
313 v->UpdateSpriteSeqBound();
315 v->x_pos += _inc_by_dir[b->direction].x;
316 v->y_pos += _inc_by_dir[b->direction].y;
318 v->animation_substate++;
319 if (v->animation_substate >= b->duration) {
320 v->animation_substate = 0;
321 v->animation_state++;
322 if (v->animation_state == lengthof(_bulldozer_movement)) {
323 delete v;
324 return false;
327 v->UpdatePositionAndViewport();
330 return true;
333 static void BubbleInit(EffectVehicle *v)
335 v->sprite_seq.Set(SPR_BUBBLE_GENERATE_0);
336 v->UpdateSpriteSeqBound();
337 v->spritenum = 0;
338 v->progress = 0;
341 struct BubbleMovement {
342 int8 x:4;
343 int8 y:4;
344 int8 z:4;
345 byte image:4;
348 #define MK(x, y, z, i) { x, y, z, i }
349 #define ME(i) { i, 4, 0, 0 }
351 static const BubbleMovement _bubble_float_sw[] = {
352 MK(0, 0, 1, 0),
353 MK(1, 0, 1, 1),
354 MK(0, 0, 1, 0),
355 MK(1, 0, 1, 2),
356 ME(1)
360 static const BubbleMovement _bubble_float_ne[] = {
361 MK( 0, 0, 1, 0),
362 MK(-1, 0, 1, 1),
363 MK( 0, 0, 1, 0),
364 MK(-1, 0, 1, 2),
365 ME(1)
368 static const BubbleMovement _bubble_float_se[] = {
369 MK(0, 0, 1, 0),
370 MK(0, 1, 1, 1),
371 MK(0, 0, 1, 0),
372 MK(0, 1, 1, 2),
373 ME(1)
376 static const BubbleMovement _bubble_float_nw[] = {
377 MK(0, 0, 1, 0),
378 MK(0, -1, 1, 1),
379 MK(0, 0, 1, 0),
380 MK(0, -1, 1, 2),
381 ME(1)
384 static const BubbleMovement _bubble_burst[] = {
385 MK(0, 0, 1, 2),
386 MK(0, 0, 1, 7),
387 MK(0, 0, 1, 8),
388 MK(0, 0, 1, 9),
389 ME(0)
392 static const BubbleMovement _bubble_absorb[] = {
393 MK(0, 0, 1, 0),
394 MK(0, 0, 1, 1),
395 MK(0, 0, 1, 0),
396 MK(0, 0, 1, 2),
397 MK(0, 0, 1, 0),
398 MK(0, 0, 1, 1),
399 MK(0, 0, 1, 0),
400 MK(0, 0, 1, 2),
401 MK(0, 0, 1, 0),
402 MK(0, 0, 1, 1),
403 MK(0, 0, 1, 0),
404 MK(0, 0, 1, 2),
405 MK(0, 0, 1, 0),
406 MK(0, 0, 1, 1),
407 MK(0, 0, 1, 0),
408 MK(0, 0, 1, 2),
409 MK(0, 0, 1, 0),
410 MK(0, 0, 1, 1),
411 MK(0, 0, 1, 0),
412 MK(0, 0, 1, 2),
413 MK(0, 0, 1, 0),
414 MK(0, 0, 1, 1),
415 MK(0, 0, 1, 0),
416 MK(0, 0, 1, 2),
417 MK(0, 0, 1, 0),
418 MK(0, 0, 1, 1),
419 MK(0, 0, 1, 0),
420 MK(0, 0, 1, 2),
421 MK(0, 0, 1, 0),
422 MK(0, 0, 1, 1),
423 MK(0, 0, 1, 0),
424 MK(0, 0, 1, 2),
425 MK(0, 0, 1, 0),
426 MK(0, 0, 1, 1),
427 MK(0, 0, 1, 0),
428 MK(0, 0, 1, 2),
429 MK(0, 0, 1, 0),
430 MK(0, 0, 1, 1),
431 MK(0, 0, 1, 0),
432 MK(0, 0, 1, 2),
433 MK(0, 0, 1, 0),
434 MK(0, 0, 1, 1),
435 MK(0, 0, 1, 0),
436 MK(0, 0, 1, 2),
437 MK(0, 0, 1, 0),
438 MK(0, 0, 1, 1),
439 MK(0, 0, 1, 0),
440 MK(0, 0, 1, 2),
441 MK(0, 0, 1, 0),
442 MK(0, 0, 1, 1),
443 MK(0, 0, 1, 0),
444 MK(0, 0, 1, 2),
445 MK(0, 0, 1, 0),
446 MK(0, 0, 1, 1),
447 MK(0, 0, 1, 0),
448 MK(0, 0, 1, 2),
449 MK(0, 0, 1, 0),
450 MK(0, 0, 1, 1),
451 MK(0, 0, 1, 0),
452 MK(0, 0, 1, 2),
453 MK(0, 0, 1, 0),
454 MK(0, 0, 1, 1),
455 MK(2, 1, 3, 0),
456 MK(1, 1, 3, 1),
457 MK(2, 1, 3, 0),
458 MK(1, 1, 3, 2),
459 MK(2, 1, 3, 0),
460 MK(1, 1, 3, 1),
461 MK(2, 1, 3, 0),
462 MK(1, 0, 1, 2),
463 MK(0, 0, 1, 0),
464 MK(1, 0, 1, 1),
465 MK(0, 0, 1, 0),
466 MK(1, 0, 1, 2),
467 MK(0, 0, 1, 0),
468 MK(1, 0, 1, 1),
469 MK(0, 0, 1, 0),
470 MK(1, 0, 1, 2),
471 ME(2),
472 MK(0, 0, 0, 0xA),
473 MK(0, 0, 0, 0xB),
474 MK(0, 0, 0, 0xC),
475 MK(0, 0, 0, 0xD),
476 MK(0, 0, 0, 0xE),
477 ME(0)
479 #undef ME
480 #undef MK
482 static const BubbleMovement * const _bubble_movement[] = {
483 _bubble_float_sw,
484 _bubble_float_ne,
485 _bubble_float_se,
486 _bubble_float_nw,
487 _bubble_burst,
488 _bubble_absorb,
491 static bool BubbleTick(EffectVehicle *v)
493 uint anim_state;
495 v->progress++;
496 if ((v->progress & 3) != 0) return true;
498 if (v->spritenum == 0) {
499 v->sprite_seq.seq[0].sprite++;
500 v->UpdateSpriteSeqBound();
501 if (v->sprite_seq.seq[0].sprite < SPR_BUBBLE_GENERATE_3) {
502 v->UpdatePositionAndViewport();
503 return true;
505 if (v->animation_substate != 0) {
506 v->spritenum = GB(Random(), 0, 2) + 1;
507 } else {
508 v->spritenum = 6;
510 anim_state = 0;
511 } else {
512 anim_state = v->animation_state + 1;
515 const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][anim_state];
517 if (b->y == 4 && b->x == 0) {
518 delete v;
519 return false;
522 if (b->y == 4 && b->x == 1) {
523 if (v->z_pos > 180 || Chance16I(1, 96, Random())) {
524 v->spritenum = 5;
525 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_2F_POP, v);
527 anim_state = 0;
530 if (b->y == 4 && b->x == 2) {
531 TileIndex tile;
533 anim_state++;
534 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_31_EXTRACT, v);
536 tile = TileVirtXY(v->x_pos, v->y_pos);
537 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
540 v->animation_state = anim_state;
541 b = &_bubble_movement[v->spritenum - 1][anim_state];
543 v->x_pos += b->x;
544 v->y_pos += b->y;
545 v->z_pos += b->z;
546 v->sprite_seq.Set(SPR_BUBBLE_0 + b->image);
547 v->UpdateSpriteSeqBound();
549 v->UpdatePositionAndViewport();
551 return true;
555 typedef void EffectInitProc(EffectVehicle *v);
556 typedef bool EffectTickProc(EffectVehicle *v);
558 /** Functions to initialise an effect vehicle after construction. */
559 static EffectInitProc * const _effect_init_procs[] = {
560 ChimneySmokeInit, // EV_CHIMNEY_SMOKE
561 SteamSmokeInit, // EV_STEAM_SMOKE
562 DieselSmokeInit, // EV_DIESEL_SMOKE
563 ElectricSparkInit, // EV_ELECTRIC_SPARK
564 SmokeInit, // EV_CRASH_SMOKE
565 ExplosionLargeInit, // EV_EXPLOSION_LARGE
566 BreakdownSmokeInit, // EV_BREAKDOWN_SMOKE
567 ExplosionSmallInit, // EV_EXPLOSION_SMALL
568 BulldozerInit, // EV_BULLDOZER
569 BubbleInit, // EV_BUBBLE
570 SmokeInit, // EV_BREAKDOWN_SMOKE_AIRCRAFT
571 SmokeInit, // EV_COPPER_MINE_SMOKE
573 assert_compile(lengthof(_effect_init_procs) == EV_END);
575 /** Functions for controlling effect vehicles at each tick. */
576 static EffectTickProc * const _effect_tick_procs[] = {
577 ChimneySmokeTick, // EV_CHIMNEY_SMOKE
578 SteamSmokeTick, // EV_STEAM_SMOKE
579 DieselSmokeTick, // EV_DIESEL_SMOKE
580 ElectricSparkTick, // EV_ELECTRIC_SPARK
581 SmokeTick, // EV_CRASH_SMOKE
582 ExplosionLargeTick, // EV_EXPLOSION_LARGE
583 BreakdownSmokeTick, // EV_BREAKDOWN_SMOKE
584 ExplosionSmallTick, // EV_EXPLOSION_SMALL
585 BulldozerTick, // EV_BULLDOZER
586 BubbleTick, // EV_BUBBLE
587 SmokeTick, // EV_BREAKDOWN_SMOKE_AIRCRAFT
588 SmokeTick, // EV_COPPER_MINE_SMOKE
590 assert_compile(lengthof(_effect_tick_procs) == EV_END);
592 /** Transparency options affecting the effects. */
593 static const TransparencyOption _effect_transparency_options[] = {
594 TO_INDUSTRIES, // EV_CHIMNEY_SMOKE
595 TO_INVALID, // EV_STEAM_SMOKE
596 TO_INVALID, // EV_DIESEL_SMOKE
597 TO_INVALID, // EV_ELECTRIC_SPARK
598 TO_INVALID, // EV_CRASH_SMOKE
599 TO_INVALID, // EV_EXPLOSION_LARGE
600 TO_INVALID, // EV_BREAKDOWN_SMOKE
601 TO_INVALID, // EV_EXPLOSION_SMALL
602 TO_INVALID, // EV_BULLDOZER
603 TO_INDUSTRIES, // EV_BUBBLE
604 TO_INVALID, // EV_BREAKDOWN_SMOKE_AIRCRAFT
605 TO_INDUSTRIES, // EV_COPPER_MINE_SMOKE
607 assert_compile(lengthof(_effect_transparency_options) == EV_END);
611 * Create an effect vehicle at a particular location.
612 * @param x The x location on the map.
613 * @param y The y location on the map.
614 * @param z The z location on the map.
615 * @param type The type of effect vehicle.
616 * @return The effect vehicle.
618 EffectVehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
620 if (!Vehicle::CanAllocateItem()) return nullptr;
622 EffectVehicle *v = new EffectVehicle();
623 v->subtype = type;
624 v->x_pos = x;
625 v->y_pos = y;
626 v->z_pos = z;
627 v->tile = 0;
628 v->UpdateDeltaXY(INVALID_DIR);
629 v->vehstatus = VS_UNCLICKABLE;
631 _effect_init_procs[type](v);
633 v->UpdatePositionAndViewport();
635 return v;
639 * Create an effect vehicle above a particular location.
640 * @param x The x location on the map.
641 * @param y The y location on the map.
642 * @param z The offset from the ground.
643 * @param type The type of effect vehicle.
644 * @return The effect vehicle.
646 EffectVehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
648 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
649 int safe_y = Clamp(y, 0, MapMaxY() * TILE_SIZE);
650 return CreateEffectVehicle(x, y, GetSlopePixelZ(safe_x, safe_y) + z, type);
654 * Create an effect vehicle above a particular vehicle.
655 * @param v The vehicle to base the position on.
656 * @param x The x offset to the vehicle.
657 * @param y The y offset to the vehicle.
658 * @param z The z offset to the vehicle.
659 * @param type The type of effect vehicle.
660 * @return The effect vehicle.
662 EffectVehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
664 return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
667 bool EffectVehicle::Tick()
669 return _effect_tick_procs[this->subtype](this);
672 void EffectVehicle::UpdateDeltaXY(Direction direction)
674 this->x_offs = 0;
675 this->y_offs = 0;
676 this->x_extent = 1;
677 this->y_extent = 1;
678 this->z_extent = 1;
682 * Determines the transparency option affecting the effect.
683 * @return Transparency option, or TO_INVALID if none.
685 TransparencyOption EffectVehicle::GetTransparencyOption() const
687 return _effect_transparency_options[this->subtype];