1 /* $Id: effectvehicle.cpp 24900 2013-01-08 22:46:42Z planetmaker $ */
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
++;
35 v
->UpdateSpriteSeqBound();
42 static void ChimneySmokeInit(EffectVehicle
*v
)
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) {
55 TileIndex tile
= TileVirtXY(v
->x_pos
, v
->y_pos
);
56 if (!IsTileType(tile
, MP_INDUSTRY
)) {
61 if (!IncrementSprite(v
, SPR_CHIMNEY_SMOKE_7
)) {
62 v
->sprite_seq
.Set(SPR_CHIMNEY_SMOKE_0
);
64 v
->UpdateSpriteSeqBound();
66 v
->UpdatePositionAndViewport();
72 static void SteamSmokeInit(EffectVehicle
*v
)
74 v
->sprite_seq
.Set(SPR_STEAM_SMOKE_0
);
75 v
->UpdateSpriteSeqBound();
79 static bool SteamSmokeTick(EffectVehicle
*v
)
85 if ((v
->progress
& 7) == 0) {
90 if ((v
->progress
& 0xF) == 4) {
91 if (!IncrementSprite(v
, SPR_STEAM_SMOKE_4
)) {
95 v
->UpdateSpriteSeqBound();
99 if (moved
) v
->UpdatePositionAndViewport();
104 static void DieselSmokeInit(EffectVehicle
*v
)
106 v
->sprite_seq
.Set(SPR_DIESEL_SMOKE_0
);
107 v
->UpdateSpriteSeqBound();
111 static bool DieselSmokeTick(EffectVehicle
*v
)
115 if ((v
->progress
& 3) == 0) {
117 v
->UpdatePositionAndViewport();
118 } else if ((v
->progress
& 7) == 1) {
119 if (!IncrementSprite(v
, SPR_DIESEL_SMOKE_5
)) {
123 v
->UpdateSpriteSeqBound();
124 v
->UpdatePositionAndViewport();
130 static void ElectricSparkInit(EffectVehicle
*v
)
132 v
->sprite_seq
.Set(SPR_ELECTRIC_SPARK_0
);
133 v
->UpdateSpriteSeqBound();
137 static bool ElectricSparkTick(EffectVehicle
*v
)
139 if (v
->progress
< 2) {
144 if (!IncrementSprite(v
, SPR_ELECTRIC_SPARK_5
)) {
148 v
->UpdateSpriteSeqBound();
149 v
->UpdatePositionAndViewport();
155 static void SmokeInit(EffectVehicle
*v
)
157 v
->sprite_seq
.Set(SPR_SMOKE_0
);
158 v
->UpdateSpriteSeqBound();
162 static bool SmokeTick(EffectVehicle
*v
)
168 if ((v
->progress
& 3) == 0) {
173 if ((v
->progress
& 0xF) == 4) {
174 if (!IncrementSprite(v
, SPR_SMOKE_4
)) {
178 v
->UpdateSpriteSeqBound();
182 if (moved
) v
->UpdatePositionAndViewport();
187 static void ExplosionLargeInit(EffectVehicle
*v
)
189 v
->sprite_seq
.Set(SPR_EXPLOSION_LARGE_0
);
190 v
->UpdateSpriteSeqBound();
194 static bool ExplosionLargeTick(EffectVehicle
*v
)
197 if ((v
->progress
& 3) == 0) {
198 if (!IncrementSprite(v
, SPR_EXPLOSION_LARGE_F
)) {
202 v
->UpdateSpriteSeqBound();
203 v
->UpdatePositionAndViewport();
209 static void BreakdownSmokeInit(EffectVehicle
*v
)
211 v
->sprite_seq
.Set(SPR_BREAKDOWN_SMOKE_0
);
212 v
->UpdateSpriteSeqBound();
216 static bool BreakdownSmokeTick(EffectVehicle
*v
)
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) {
236 static void ExplosionSmallInit(EffectVehicle
*v
)
238 v
->sprite_seq
.Set(SPR_EXPLOSION_SMALL_0
);
239 v
->UpdateSpriteSeqBound();
243 static bool ExplosionSmallTick(EffectVehicle
*v
)
246 if ((v
->progress
& 3) == 0) {
247 if (!IncrementSprite(v
, SPR_EXPLOSION_SMALL_B
)) {
251 v
->UpdateSpriteSeqBound();
252 v
->UpdatePositionAndViewport();
258 static void BulldozerInit(EffectVehicle
*v
)
260 v
->sprite_seq
.Set(SPR_BULLDOZER_NE
);
261 v
->UpdateSpriteSeqBound();
263 v
->animation_state
= 0;
264 v
->animation_substate
= 0;
267 struct BulldozerMovement
{
273 static const BulldozerMovement _bulldozer_movement
[] = {
296 static const struct {
306 static bool BulldozerTick(EffectVehicle
*v
)
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
)) {
327 v
->UpdatePositionAndViewport();
333 static void BubbleInit(EffectVehicle
*v
)
335 v
->sprite_seq
.Set(SPR_BUBBLE_GENERATE_0
);
336 v
->UpdateSpriteSeqBound();
341 struct BubbleMovement
{
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
[] = {
360 static const BubbleMovement _bubble_float_ne
[] = {
368 static const BubbleMovement _bubble_float_se
[] = {
376 static const BubbleMovement _bubble_float_nw
[] = {
384 static const BubbleMovement _bubble_burst
[] = {
392 static const BubbleMovement _bubble_absorb
[] = {
482 static const BubbleMovement
* const _bubble_movement
[] = {
491 static bool BubbleTick(EffectVehicle
*v
)
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();
505 if (v
->animation_substate
!= 0) {
506 v
->spritenum
= GB(Random(), 0, 2) + 1;
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) {
522 if (b
->y
== 4 && b
->x
== 1) {
523 if (v
->z_pos
> 180 || Chance16I(1, 96, Random())) {
525 if (_settings_client
.sound
.ambient
) SndPlayVehicleFx(SND_2F_POP
, v
);
530 if (b
->y
== 4 && b
->x
== 2) {
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
];
546 v
->sprite_seq
.Set(SPR_BUBBLE_0
+ b
->image
);
547 v
->UpdateSpriteSeqBound();
549 v
->UpdatePositionAndViewport();
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();
628 v
->UpdateDeltaXY(INVALID_DIR
);
629 v
->vehstatus
= VS_UNCLICKABLE
;
631 _effect_init_procs
[type
](v
);
633 v
->UpdatePositionAndViewport();
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
)
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
];