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/>.
10 /** @file effectvehicle.cpp Implementation of everything generic to vehicles. */
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"
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
++;
41 static void ChimneySmokeInit(EffectVehicle
*v
)
44 v
->sprite_seq
.Set(SPR_CHIMNEY_SMOKE_0
+ GB(r
, 0, 3));
45 v
->progress
= GB(r
, 16, 3);
48 static bool ChimneySmokeTick(EffectVehicle
*v
)
50 if (v
->progress
> 0) {
53 TileIndex tile
= TileVirtXY(v
->x_pos
, v
->y_pos
);
54 if (!IsTileType(tile
, MP_INDUSTRY
)) {
59 if (!IncrementSprite(v
, SPR_CHIMNEY_SMOKE_7
)) {
60 v
->sprite_seq
.Set(SPR_CHIMNEY_SMOKE_0
);
63 v
->UpdatePositionAndViewport();
69 static void SteamSmokeInit(EffectVehicle
*v
)
71 v
->sprite_seq
.Set(SPR_STEAM_SMOKE_0
);
75 static bool SteamSmokeTick(EffectVehicle
*v
)
81 if ((v
->progress
& 7) == 0) {
86 if ((v
->progress
& 0xF) == 4) {
87 if (!IncrementSprite(v
, SPR_STEAM_SMOKE_4
)) {
94 if (moved
) v
->UpdatePositionAndViewport();
99 static void DieselSmokeInit(EffectVehicle
*v
)
101 v
->sprite_seq
.Set(SPR_DIESEL_SMOKE_0
);
105 static bool DieselSmokeTick(EffectVehicle
*v
)
109 if ((v
->progress
& 3) == 0) {
111 v
->UpdatePositionAndViewport();
112 } else if ((v
->progress
& 7) == 1) {
113 if (!IncrementSprite(v
, SPR_DIESEL_SMOKE_5
)) {
117 v
->UpdatePositionAndViewport();
123 static void ElectricSparkInit(EffectVehicle
*v
)
125 v
->sprite_seq
.Set(SPR_ELECTRIC_SPARK_0
);
129 static bool ElectricSparkTick(EffectVehicle
*v
)
131 if (v
->progress
< 2) {
136 if (!IncrementSprite(v
, SPR_ELECTRIC_SPARK_5
)) {
140 v
->UpdatePositionAndViewport();
146 static void SmokeInit(EffectVehicle
*v
)
148 v
->sprite_seq
.Set(SPR_SMOKE_0
);
152 static bool SmokeTick(EffectVehicle
*v
)
158 if ((v
->progress
& 3) == 0) {
163 if ((v
->progress
& 0xF) == 4) {
164 if (!IncrementSprite(v
, SPR_SMOKE_4
)) {
171 if (moved
) v
->UpdatePositionAndViewport();
176 static void ExplosionLargeInit(EffectVehicle
*v
)
178 v
->sprite_seq
.Set(SPR_EXPLOSION_LARGE_0
);
182 static bool ExplosionLargeTick(EffectVehicle
*v
)
185 if ((v
->progress
& 3) == 0) {
186 if (!IncrementSprite(v
, SPR_EXPLOSION_LARGE_F
)) {
190 v
->UpdatePositionAndViewport();
196 static void BreakdownSmokeInit(EffectVehicle
*v
)
198 v
->sprite_seq
.Set(SPR_BREAKDOWN_SMOKE_0
);
202 static bool BreakdownSmokeTick(EffectVehicle
*v
)
205 if ((v
->progress
& 7) == 0) {
206 if (!IncrementSprite(v
, SPR_BREAKDOWN_SMOKE_3
)) {
207 v
->sprite_seq
.Set(SPR_BREAKDOWN_SMOKE_0
);
209 v
->UpdatePositionAndViewport();
212 v
->animation_state
--;
213 if (v
->animation_state
== 0) {
221 static void ExplosionSmallInit(EffectVehicle
*v
)
223 v
->sprite_seq
.Set(SPR_EXPLOSION_SMALL_0
);
227 static bool ExplosionSmallTick(EffectVehicle
*v
)
230 if ((v
->progress
& 3) == 0) {
231 if (!IncrementSprite(v
, SPR_EXPLOSION_SMALL_B
)) {
235 v
->UpdatePositionAndViewport();
241 static void BulldozerInit(EffectVehicle
*v
)
243 v
->sprite_seq
.Set(SPR_BULLDOZER_NE
);
245 v
->animation_state
= 0;
246 v
->animation_substate
= 0;
249 struct BulldozerMovement
{
255 static const BulldozerMovement _bulldozer_movement
[] = {
278 static const struct {
288 static bool BulldozerTick(EffectVehicle
*v
)
291 if ((v
->progress
& 7) == 0) {
292 const BulldozerMovement
*b
= &_bulldozer_movement
[v
->animation_state
];
294 v
->sprite_seq
.Set(SPR_BULLDOZER_NE
+ b
->image
);
296 v
->x_pos
+= _inc_by_dir
[b
->direction
].x
;
297 v
->y_pos
+= _inc_by_dir
[b
->direction
].y
;
299 v
->animation_substate
++;
300 if (v
->animation_substate
>= b
->duration
) {
301 v
->animation_substate
= 0;
302 v
->animation_state
++;
303 if (v
->animation_state
== lengthof(_bulldozer_movement
)) {
308 v
->UpdatePositionAndViewport();
314 static void BubbleInit(EffectVehicle
*v
)
316 v
->sprite_seq
.Set(SPR_BUBBLE_GENERATE_0
);
321 struct BubbleMovement
{
328 #define MK(x, y, z, i) { x, y, z, i }
329 #define ME(i) { i, 4, 0, 0 }
331 static const BubbleMovement _bubble_float_sw
[] = {
340 static const BubbleMovement _bubble_float_ne
[] = {
348 static const BubbleMovement _bubble_float_se
[] = {
356 static const BubbleMovement _bubble_float_nw
[] = {
364 static const BubbleMovement _bubble_burst
[] = {
372 static const BubbleMovement _bubble_absorb
[] = {
462 static const BubbleMovement
* const _bubble_movement
[] = {
471 static bool BubbleTick(EffectVehicle
*v
)
476 if ((v
->progress
& 3) != 0) return true;
478 if (v
->spritenum
== 0) {
479 v
->sprite_seq
.seq
[0].sprite
++;
480 if (v
->sprite_seq
.seq
[0].sprite
< SPR_BUBBLE_GENERATE_3
) {
481 v
->UpdatePositionAndViewport();
484 if (v
->animation_substate
!= 0) {
485 v
->spritenum
= GB(Random(), 0, 2) + 1;
491 anim_state
= v
->animation_state
+ 1;
494 const BubbleMovement
*b
= &_bubble_movement
[v
->spritenum
- 1][anim_state
];
496 if (b
->y
== 4 && b
->x
== 0) {
501 if (b
->y
== 4 && b
->x
== 1) {
502 if (v
->z_pos
> 180 || Chance16I(1, 96, Random())) {
504 if (_settings_client
.sound
.ambient
) SndPlayVehicleFx(SND_2F_POP
, v
);
509 if (b
->y
== 4 && b
->x
== 2) {
513 if (_settings_client
.sound
.ambient
) SndPlayVehicleFx(SND_31_EXTRACT
, v
);
515 tile
= TileVirtXY(v
->x_pos
, v
->y_pos
);
516 if (IsTileType(tile
, MP_INDUSTRY
) && GetIndustryGfx(tile
) == GFX_BUBBLE_CATCHER
) AddAnimatedTile(tile
);
519 v
->animation_state
= anim_state
;
520 b
= &_bubble_movement
[v
->spritenum
- 1][anim_state
];
525 v
->sprite_seq
.Set(SPR_BUBBLE_0
+ b
->image
);
527 v
->UpdatePositionAndViewport();
533 typedef void EffectInitProc(EffectVehicle
*v
);
534 typedef bool EffectTickProc(EffectVehicle
*v
);
536 /** Functions to initialise an effect vehicle after construction. */
537 static EffectInitProc
* const _effect_init_procs
[] = {
538 ChimneySmokeInit
, // EV_CHIMNEY_SMOKE
539 SteamSmokeInit
, // EV_STEAM_SMOKE
540 DieselSmokeInit
, // EV_DIESEL_SMOKE
541 ElectricSparkInit
, // EV_ELECTRIC_SPARK
542 SmokeInit
, // EV_CRASH_SMOKE
543 ExplosionLargeInit
, // EV_EXPLOSION_LARGE
544 BreakdownSmokeInit
, // EV_BREAKDOWN_SMOKE
545 ExplosionSmallInit
, // EV_EXPLOSION_SMALL
546 BulldozerInit
, // EV_BULLDOZER
547 BubbleInit
, // EV_BUBBLE
548 SmokeInit
, // EV_BREAKDOWN_SMOKE_AIRCRAFT
549 SmokeInit
, // EV_COPPER_MINE_SMOKE
551 assert_compile(lengthof(_effect_init_procs
) == EV_END
);
553 /** Functions for controlling effect vehicles at each tick. */
554 static EffectTickProc
* const _effect_tick_procs
[] = {
555 ChimneySmokeTick
, // EV_CHIMNEY_SMOKE
556 SteamSmokeTick
, // EV_STEAM_SMOKE
557 DieselSmokeTick
, // EV_DIESEL_SMOKE
558 ElectricSparkTick
, // EV_ELECTRIC_SPARK
559 SmokeTick
, // EV_CRASH_SMOKE
560 ExplosionLargeTick
, // EV_EXPLOSION_LARGE
561 BreakdownSmokeTick
, // EV_BREAKDOWN_SMOKE
562 ExplosionSmallTick
, // EV_EXPLOSION_SMALL
563 BulldozerTick
, // EV_BULLDOZER
564 BubbleTick
, // EV_BUBBLE
565 SmokeTick
, // EV_BREAKDOWN_SMOKE_AIRCRAFT
566 SmokeTick
, // EV_COPPER_MINE_SMOKE
568 assert_compile(lengthof(_effect_tick_procs
) == EV_END
);
570 /** Transparency options affecting the effects. */
571 static const TransparencyOption _effect_transparency_options
[] = {
572 TO_INDUSTRIES
, // EV_CHIMNEY_SMOKE
573 TO_INVALID
, // EV_STEAM_SMOKE
574 TO_INVALID
, // EV_DIESEL_SMOKE
575 TO_INVALID
, // EV_ELECTRIC_SPARK
576 TO_INVALID
, // EV_CRASH_SMOKE
577 TO_INVALID
, // EV_EXPLOSION_LARGE
578 TO_INVALID
, // EV_BREAKDOWN_SMOKE
579 TO_INVALID
, // EV_EXPLOSION_SMALL
580 TO_INVALID
, // EV_BULLDOZER
581 TO_INDUSTRIES
, // EV_BUBBLE
582 TO_INVALID
, // EV_BREAKDOWN_SMOKE_AIRCRAFT
583 TO_INDUSTRIES
, // EV_COPPER_MINE_SMOKE
585 assert_compile(lengthof(_effect_transparency_options
) == EV_END
);
589 * Create an effect vehicle at a particular location.
590 * @param x The x location on the map.
591 * @param y The y location on the map.
592 * @param z The z location on the map.
593 * @param type The type of effect vehicle.
594 * @return The effect vehicle.
596 EffectVehicle
*CreateEffectVehicle(int x
, int y
, int z
, EffectVehicleType type
)
598 if (!Vehicle::CanAllocateItem()) return NULL
;
600 EffectVehicle
*v
= new EffectVehicle();
606 v
->UpdateDeltaXY(INVALID_DIR
);
607 v
->vehstatus
= VS_UNCLICKABLE
;
609 _effect_init_procs
[type
](v
);
611 v
->UpdatePositionAndViewport();
617 * Create an effect vehicle above a particular location.
618 * @param x The x location on the map.
619 * @param y The y location on the map.
620 * @param z The offset from the ground.
621 * @param type The type of effect vehicle.
622 * @return The effect vehicle.
624 EffectVehicle
*CreateEffectVehicleAbove(int x
, int y
, int z
, EffectVehicleType type
)
626 int safe_x
= Clamp(x
, 0, MapMaxX() * TILE_SIZE
);
627 int safe_y
= Clamp(y
, 0, MapMaxY() * TILE_SIZE
);
628 return CreateEffectVehicle(x
, y
, GetSlopePixelZ(safe_x
, safe_y
) + z
, type
);
632 * Create an effect vehicle above a particular vehicle.
633 * @param v The vehicle to base the position on.
634 * @param x The x offset to the vehicle.
635 * @param y The y offset to the vehicle.
636 * @param z The z offset to the vehicle.
637 * @param type The type of effect vehicle.
638 * @return The effect vehicle.
640 EffectVehicle
*CreateEffectVehicleRel(const Vehicle
*v
, int x
, int y
, int z
, EffectVehicleType type
)
642 return CreateEffectVehicle(v
->x_pos
+ x
, v
->y_pos
+ y
, v
->z_pos
+ z
, type
);
645 bool EffectVehicle::Tick()
647 return _effect_tick_procs
[this->subtype
](this);
650 void EffectVehicle::UpdateDeltaXY(Direction direction
)
660 * Determines the transparency option affecting the effect.
661 * @return Transparency option, or TO_INVALID if none.
663 TransparencyOption
EffectVehicle::GetTransparencyOption() const
665 return _effect_transparency_options
[this->subtype
];