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/>.
8 /** @file effectvehicle.cpp Implementation of everything generic to vehicles. */
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"
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
++;
39 static void ChimneySmokeInit(EffectVehicle
*v
)
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) {
51 TileIndex tile
= TileVirtXY(v
->x_pos
, v
->y_pos
);
52 if (!IsTileType(tile
, MP_INDUSTRY
)) {
57 if (!IncrementSprite(v
, SPR_CHIMNEY_SMOKE_7
)) {
58 v
->sprite_cache
.sprite_seq
.Set(SPR_CHIMNEY_SMOKE_0
);
61 v
->UpdatePositionAndViewport();
67 static void SteamSmokeInit(EffectVehicle
*v
)
69 v
->sprite_cache
.sprite_seq
.Set(SPR_STEAM_SMOKE_0
);
73 static bool SteamSmokeTick(EffectVehicle
*v
)
79 if ((v
->progress
& 7) == 0) {
84 if ((v
->progress
& 0xF) == 4) {
85 if (!IncrementSprite(v
, SPR_STEAM_SMOKE_4
)) {
92 if (moved
) v
->UpdatePositionAndViewport();
97 static void DieselSmokeInit(EffectVehicle
*v
)
99 v
->sprite_cache
.sprite_seq
.Set(SPR_DIESEL_SMOKE_0
);
103 static bool DieselSmokeTick(EffectVehicle
*v
)
107 if ((v
->progress
& 3) == 0) {
109 v
->UpdatePositionAndViewport();
110 } else if ((v
->progress
& 7) == 1) {
111 if (!IncrementSprite(v
, SPR_DIESEL_SMOKE_5
)) {
115 v
->UpdatePositionAndViewport();
121 static void ElectricSparkInit(EffectVehicle
*v
)
123 v
->sprite_cache
.sprite_seq
.Set(SPR_ELECTRIC_SPARK_0
);
127 static bool ElectricSparkTick(EffectVehicle
*v
)
129 if (v
->progress
< 2) {
134 if (!IncrementSprite(v
, SPR_ELECTRIC_SPARK_5
)) {
138 v
->UpdatePositionAndViewport();
144 static void SmokeInit(EffectVehicle
*v
)
146 v
->sprite_cache
.sprite_seq
.Set(SPR_SMOKE_0
);
150 static bool SmokeTick(EffectVehicle
*v
)
156 if ((v
->progress
& 3) == 0) {
161 if ((v
->progress
& 0xF) == 4) {
162 if (!IncrementSprite(v
, SPR_SMOKE_4
)) {
169 if (moved
) v
->UpdatePositionAndViewport();
174 static void ExplosionLargeInit(EffectVehicle
*v
)
176 v
->sprite_cache
.sprite_seq
.Set(SPR_EXPLOSION_LARGE_0
);
180 static bool ExplosionLargeTick(EffectVehicle
*v
)
183 if ((v
->progress
& 3) == 0) {
184 if (!IncrementSprite(v
, SPR_EXPLOSION_LARGE_F
)) {
188 v
->UpdatePositionAndViewport();
194 static void BreakdownSmokeInit(EffectVehicle
*v
)
196 v
->sprite_cache
.sprite_seq
.Set(SPR_BREAKDOWN_SMOKE_0
);
200 static bool BreakdownSmokeTick(EffectVehicle
*v
)
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) {
219 static void ExplosionSmallInit(EffectVehicle
*v
)
221 v
->sprite_cache
.sprite_seq
.Set(SPR_EXPLOSION_SMALL_0
);
225 static bool ExplosionSmallTick(EffectVehicle
*v
)
228 if ((v
->progress
& 3) == 0) {
229 if (!IncrementSprite(v
, SPR_EXPLOSION_SMALL_B
)) {
233 v
->UpdatePositionAndViewport();
239 static void BulldozerInit(EffectVehicle
*v
)
241 v
->sprite_cache
.sprite_seq
.Set(SPR_BULLDOZER_NE
);
243 v
->animation_state
= 0;
244 v
->animation_substate
= 0;
247 struct BulldozerMovement
{
253 static const BulldozerMovement _bulldozer_movement
[] = {
276 static const struct {
286 static bool BulldozerTick(EffectVehicle
*v
)
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
)) {
306 v
->UpdatePositionAndViewport();
312 static void BubbleInit(EffectVehicle
*v
)
314 v
->sprite_cache
.sprite_seq
.Set(SPR_BUBBLE_GENERATE_0
);
319 struct BubbleMovement
{
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
[] = {
338 static const BubbleMovement _bubble_float_ne
[] = {
346 static const BubbleMovement _bubble_float_se
[] = {
354 static const BubbleMovement _bubble_float_nw
[] = {
362 static const BubbleMovement _bubble_burst
[] = {
370 static const BubbleMovement _bubble_absorb
[] = {
460 static const BubbleMovement
* const _bubble_movement
[] = {
469 static bool BubbleTick(EffectVehicle
*v
)
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();
482 if (v
->animation_substate
!= 0) {
483 v
->spritenum
= GB(Random(), 0, 2) + 1;
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) {
499 if (b
->y
== 4 && b
->x
== 1) {
500 if (v
->z_pos
> 180 || Chance16I(1, 96, Random())) {
502 if (_settings_client
.sound
.ambient
) SndPlayVehicleFx(SND_2F_BUBBLE_GENERATOR_FAIL
, v
);
507 if (b
->y
== 4 && b
->x
== 2) {
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
];
523 v
->sprite_cache
.sprite_seq
.Set(SPR_BUBBLE_0
+ b
->image
);
525 v
->UpdatePositionAndViewport();
531 typedef void EffectInitProc(EffectVehicle
*v
);
532 typedef bool EffectTickProc(EffectVehicle
*v
);
534 /** Functions to initialise an effect vehicle after construction. */
535 static EffectInitProc
* const _effect_init_procs
[] = {
536 ChimneySmokeInit
, // EV_CHIMNEY_SMOKE
537 SteamSmokeInit
, // EV_STEAM_SMOKE
538 DieselSmokeInit
, // EV_DIESEL_SMOKE
539 ElectricSparkInit
, // EV_ELECTRIC_SPARK
540 SmokeInit
, // EV_CRASH_SMOKE
541 ExplosionLargeInit
, // EV_EXPLOSION_LARGE
542 BreakdownSmokeInit
, // EV_BREAKDOWN_SMOKE
543 ExplosionSmallInit
, // EV_EXPLOSION_SMALL
544 BulldozerInit
, // EV_BULLDOZER
545 BubbleInit
, // EV_BUBBLE
546 SmokeInit
, // EV_BREAKDOWN_SMOKE_AIRCRAFT
547 SmokeInit
, // EV_COPPER_MINE_SMOKE
549 static_assert(lengthof(_effect_init_procs
) == EV_END
);
551 /** Functions for controlling effect vehicles at each tick. */
552 static EffectTickProc
* const _effect_tick_procs
[] = {
553 ChimneySmokeTick
, // EV_CHIMNEY_SMOKE
554 SteamSmokeTick
, // EV_STEAM_SMOKE
555 DieselSmokeTick
, // EV_DIESEL_SMOKE
556 ElectricSparkTick
, // EV_ELECTRIC_SPARK
557 SmokeTick
, // EV_CRASH_SMOKE
558 ExplosionLargeTick
, // EV_EXPLOSION_LARGE
559 BreakdownSmokeTick
, // EV_BREAKDOWN_SMOKE
560 ExplosionSmallTick
, // EV_EXPLOSION_SMALL
561 BulldozerTick
, // EV_BULLDOZER
562 BubbleTick
, // EV_BUBBLE
563 SmokeTick
, // EV_BREAKDOWN_SMOKE_AIRCRAFT
564 SmokeTick
, // EV_COPPER_MINE_SMOKE
566 static_assert(lengthof(_effect_tick_procs
) == EV_END
);
568 /** Transparency options affecting the effects. */
569 static const TransparencyOption _effect_transparency_options
[] = {
570 TO_INDUSTRIES
, // EV_CHIMNEY_SMOKE
571 TO_INVALID
, // EV_STEAM_SMOKE
572 TO_INVALID
, // EV_DIESEL_SMOKE
573 TO_INVALID
, // EV_ELECTRIC_SPARK
574 TO_INVALID
, // EV_CRASH_SMOKE
575 TO_INVALID
, // EV_EXPLOSION_LARGE
576 TO_INVALID
, // EV_BREAKDOWN_SMOKE
577 TO_INVALID
, // EV_EXPLOSION_SMALL
578 TO_INVALID
, // EV_BULLDOZER
579 TO_INDUSTRIES
, // EV_BUBBLE
580 TO_INVALID
, // EV_BREAKDOWN_SMOKE_AIRCRAFT
581 TO_INDUSTRIES
, // EV_COPPER_MINE_SMOKE
583 static_assert(lengthof(_effect_transparency_options
) == EV_END
);
587 * Create an effect vehicle at 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 z location on the map.
591 * @param type The type of effect vehicle.
592 * @return The effect vehicle.
594 EffectVehicle
*CreateEffectVehicle(int x
, int y
, int z
, EffectVehicleType type
)
596 if (!Vehicle::CanAllocateItem()) return nullptr;
598 EffectVehicle
*v
= new EffectVehicle();
605 v
->vehstatus
= VS_UNCLICKABLE
;
607 _effect_init_procs
[type
](v
);
609 v
->UpdatePositionAndViewport();
615 * Create an effect vehicle above a particular location.
616 * @param x The x location on the map.
617 * @param y The y location on the map.
618 * @param z The offset from the ground.
619 * @param type The type of effect vehicle.
620 * @return The effect vehicle.
622 EffectVehicle
*CreateEffectVehicleAbove(int x
, int y
, int z
, EffectVehicleType type
)
624 int safe_x
= Clamp(x
, 0, MapMaxX() * TILE_SIZE
);
625 int safe_y
= Clamp(y
, 0, MapMaxY() * TILE_SIZE
);
626 return CreateEffectVehicle(x
, y
, GetSlopePixelZ(safe_x
, safe_y
) + z
, type
);
630 * Create an effect vehicle above a particular vehicle.
631 * @param v The vehicle to base the position on.
632 * @param x The x offset to the vehicle.
633 * @param y The y offset to the vehicle.
634 * @param z The z offset to the vehicle.
635 * @param type The type of effect vehicle.
636 * @return The effect vehicle.
638 EffectVehicle
*CreateEffectVehicleRel(const Vehicle
*v
, int x
, int y
, int z
, EffectVehicleType type
)
640 return CreateEffectVehicle(v
->x_pos
+ x
, v
->y_pos
+ y
, v
->z_pos
+ z
, type
);
643 bool EffectVehicle::Tick()
645 return _effect_tick_procs
[this->subtype
](this);
648 void EffectVehicle::UpdateDeltaXY()
658 * Determines the transparency option affecting the effect.
659 * @return Transparency option, or TO_INVALID if none.
661 TransparencyOption
EffectVehicle::GetTransparencyOption() const
663 return _effect_transparency_options
[this->subtype
];