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  */
7 
8 /** @file effectvehicle.cpp Implementation of everything generic to vehicles. */
9 
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"
19 
20 #include "safeguards.h"
21 
22 
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.
28  */
IncrementSprite(EffectVehicle * v,SpriteID last)29 static bool IncrementSprite(EffectVehicle *v, SpriteID last)
30 {
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;
36 	}
37 }
38 
ChimneySmokeInit(EffectVehicle * v)39 static void ChimneySmokeInit(EffectVehicle *v)
40 {
41 	uint32 r = Random();
42 	v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3));
43 	v->progress = GB(r, 16, 3);
44 }
45 
ChimneySmokeTick(EffectVehicle * v)46 static bool ChimneySmokeTick(EffectVehicle *v)
47 {
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;
55 		}
56 
57 		if (!IncrementSprite(v, SPR_CHIMNEY_SMOKE_7)) {
58 			v->sprite_cache.sprite_seq.Set(SPR_CHIMNEY_SMOKE_0);
59 		}
60 		v->progress = 7;
61 		v->UpdatePositionAndViewport();
62 	}
63 
64 	return true;
65 }
66 
SteamSmokeInit(EffectVehicle * v)67 static void SteamSmokeInit(EffectVehicle *v)
68 {
69 	v->sprite_cache.sprite_seq.Set(SPR_STEAM_SMOKE_0);
70 	v->progress = 12;
71 }
72 
SteamSmokeTick(EffectVehicle * v)73 static bool SteamSmokeTick(EffectVehicle *v)
74 {
75 	bool moved = false;
76 
77 	v->progress++;
78 
79 	if ((v->progress & 7) == 0) {
80 		v->z_pos++;
81 		moved = true;
82 	}
83 
84 	if ((v->progress & 0xF) == 4) {
85 		if (!IncrementSprite(v, SPR_STEAM_SMOKE_4)) {
86 			delete v;
87 			return false;
88 		}
89 		moved = true;
90 	}
91 
92 	if (moved) v->UpdatePositionAndViewport();
93 
94 	return true;
95 }
96 
DieselSmokeInit(EffectVehicle * v)97 static void DieselSmokeInit(EffectVehicle *v)
98 {
99 	v->sprite_cache.sprite_seq.Set(SPR_DIESEL_SMOKE_0);
100 	v->progress = 0;
101 }
102 
DieselSmokeTick(EffectVehicle * v)103 static bool DieselSmokeTick(EffectVehicle *v)
104 {
105 	v->progress++;
106 
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;
114 		}
115 		v->UpdatePositionAndViewport();
116 	}
117 
118 	return true;
119 }
120 
ElectricSparkInit(EffectVehicle * v)121 static void ElectricSparkInit(EffectVehicle *v)
122 {
123 	v->sprite_cache.sprite_seq.Set(SPR_ELECTRIC_SPARK_0);
124 	v->progress = 1;
125 }
126 
ElectricSparkTick(EffectVehicle * v)127 static bool ElectricSparkTick(EffectVehicle *v)
128 {
129 	if (v->progress < 2) {
130 		v->progress++;
131 	} else {
132 		v->progress = 0;
133 
134 		if (!IncrementSprite(v, SPR_ELECTRIC_SPARK_5)) {
135 			delete v;
136 			return false;
137 		}
138 		v->UpdatePositionAndViewport();
139 	}
140 
141 	return true;
142 }
143 
SmokeInit(EffectVehicle * v)144 static void SmokeInit(EffectVehicle *v)
145 {
146 	v->sprite_cache.sprite_seq.Set(SPR_SMOKE_0);
147 	v->progress = 12;
148 }
149 
SmokeTick(EffectVehicle * v)150 static bool SmokeTick(EffectVehicle *v)
151 {
152 	bool moved = false;
153 
154 	v->progress++;
155 
156 	if ((v->progress & 3) == 0) {
157 		v->z_pos++;
158 		moved = true;
159 	}
160 
161 	if ((v->progress & 0xF) == 4) {
162 		if (!IncrementSprite(v, SPR_SMOKE_4)) {
163 			delete v;
164 			return false;
165 		}
166 		moved = true;
167 	}
168 
169 	if (moved) v->UpdatePositionAndViewport();
170 
171 	return true;
172 }
173 
ExplosionLargeInit(EffectVehicle * v)174 static void ExplosionLargeInit(EffectVehicle *v)
175 {
176 	v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_LARGE_0);
177 	v->progress = 0;
178 }
179 
ExplosionLargeTick(EffectVehicle * v)180 static bool ExplosionLargeTick(EffectVehicle *v)
181 {
182 	v->progress++;
183 	if ((v->progress & 3) == 0) {
184 		if (!IncrementSprite(v, SPR_EXPLOSION_LARGE_F)) {
185 			delete v;
186 			return false;
187 		}
188 		v->UpdatePositionAndViewport();
189 	}
190 
191 	return true;
192 }
193 
BreakdownSmokeInit(EffectVehicle * v)194 static void BreakdownSmokeInit(EffectVehicle *v)
195 {
196 	v->sprite_cache.sprite_seq.Set(SPR_BREAKDOWN_SMOKE_0);
197 	v->progress = 0;
198 }
199 
BreakdownSmokeTick(EffectVehicle * v)200 static bool BreakdownSmokeTick(EffectVehicle *v)
201 {
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);
206 		}
207 		v->UpdatePositionAndViewport();
208 	}
209 
210 	v->animation_state--;
211 	if (v->animation_state == 0) {
212 		delete v;
213 		return false;
214 	}
215 
216 	return true;
217 }
218 
ExplosionSmallInit(EffectVehicle * v)219 static void ExplosionSmallInit(EffectVehicle *v)
220 {
221 	v->sprite_cache.sprite_seq.Set(SPR_EXPLOSION_SMALL_0);
222 	v->progress = 0;
223 }
224 
ExplosionSmallTick(EffectVehicle * v)225 static bool ExplosionSmallTick(EffectVehicle *v)
226 {
227 	v->progress++;
228 	if ((v->progress & 3) == 0) {
229 		if (!IncrementSprite(v, SPR_EXPLOSION_SMALL_B)) {
230 			delete v;
231 			return false;
232 		}
233 		v->UpdatePositionAndViewport();
234 	}
235 
236 	return true;
237 }
238 
BulldozerInit(EffectVehicle * v)239 static void BulldozerInit(EffectVehicle *v)
240 {
241 	v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE);
242 	v->progress = 0;
243 	v->animation_state = 0;
244 	v->animation_substate = 0;
245 }
246 
247 struct BulldozerMovement {
248 	byte direction:2;
249 	byte image:2;
250 	byte duration:3;
251 };
252 
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 }
274 };
275 
276 static const struct {
277 	int8 x;
278 	int8 y;
279 } _inc_by_dir[] = {
280 	{ -1,  0 },
281 	{  0,  1 },
282 	{  1,  0 },
283 	{  0, -1 }
284 };
285 
BulldozerTick(EffectVehicle * v)286 static bool BulldozerTick(EffectVehicle *v)
287 {
288 	v->progress++;
289 	if ((v->progress & 7) == 0) {
290 		const BulldozerMovement *b = &_bulldozer_movement[v->animation_state];
291 
292 		v->sprite_cache.sprite_seq.Set(SPR_BULLDOZER_NE + b->image);
293 
294 		v->x_pos += _inc_by_dir[b->direction].x;
295 		v->y_pos += _inc_by_dir[b->direction].y;
296 
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;
304 			}
305 		}
306 		v->UpdatePositionAndViewport();
307 	}
308 
309 	return true;
310 }
311 
BubbleInit(EffectVehicle * v)312 static void BubbleInit(EffectVehicle *v)
313 {
314 	v->sprite_cache.sprite_seq.Set(SPR_BUBBLE_GENERATE_0);
315 	v->spritenum = 0;
316 	v->progress = 0;
317 }
318 
319 struct BubbleMovement {
320 	int8 x:4;
321 	int8 y:4;
322 	int8 z:4;
323 	byte image:4;
324 };
325 
326 #define MK(x, y, z, i) { x, y, z, i }
327 #define ME(i) { i, 4, 0, 0 }
328 
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)
335 };
336 
337 
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)
344 };
345 
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)
352 };
353 
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)
360 };
361 
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)
368 };
369 
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)
456 };
457 #undef ME
458 #undef MK
459 
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,
467 };
468 
BubbleTick(EffectVehicle * v)469 static bool BubbleTick(EffectVehicle *v)
470 {
471 	uint anim_state;
472 
473 	v->progress++;
474 	if ((v->progress & 3) != 0) return true;
475 
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;
481 		}
482 		if (v->animation_substate != 0) {
483 			v->spritenum = GB(Random(), 0, 2) + 1;
484 		} else {
485 			v->spritenum = 6;
486 		}
487 		anim_state = 0;
488 	} else {
489 		anim_state = v->animation_state + 1;
490 	}
491 
492 	const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][anim_state];
493 
494 	if (b->y == 4 && b->x == 0) {
495 		delete v;
496 		return false;
497 	}
498 
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);
503 		}
504 		anim_state = 0;
505 	}
506 
507 	if (b->y == 4 && b->x == 2) {
508 		TileIndex tile;
509 
510 		anim_state++;
511 		if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_31_BUBBLE_GENERATOR_SUCCESS, v);
512 
513 		tile = TileVirtXY(v->x_pos, v->y_pos);
514 		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
515 	}
516 
517 	v->animation_state = anim_state;
518 	b = &_bubble_movement[v->spritenum - 1][anim_state];
519 
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);
524 
525 	v->UpdatePositionAndViewport();
526 
527 	return true;
528 }
529 
530 
531 typedef void EffectInitProc(EffectVehicle *v);
532 typedef bool EffectTickProc(EffectVehicle *v);
533 
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
548 };
549 static_assert(lengthof(_effect_init_procs) == EV_END);
550 
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
565 };
566 static_assert(lengthof(_effect_tick_procs) == EV_END);
567 
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
582 };
583 static_assert(lengthof(_effect_transparency_options) == EV_END);
584 
585 
586 /**
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.
593  */
CreateEffectVehicle(int x,int y,int z,EffectVehicleType type)594 EffectVehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
595 {
596 	if (!Vehicle::CanAllocateItem()) return nullptr;
597 
598 	EffectVehicle *v = new EffectVehicle();
599 	v->subtype = type;
600 	v->x_pos = x;
601 	v->y_pos = y;
602 	v->z_pos = z;
603 	v->tile = 0;
604 	v->UpdateDeltaXY();
605 	v->vehstatus = VS_UNCLICKABLE;
606 
607 	_effect_init_procs[type](v);
608 
609 	v->UpdatePositionAndViewport();
610 
611 	return v;
612 }
613 
614 /**
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.
621  */
CreateEffectVehicleAbove(int x,int y,int z,EffectVehicleType type)622 EffectVehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
623 {
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);
627 }
628 
629 /**
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.
637  */
CreateEffectVehicleRel(const Vehicle * v,int x,int y,int z,EffectVehicleType type)638 EffectVehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
639 {
640 	return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
641 }
642 
Tick()643 bool EffectVehicle::Tick()
644 {
645 	return _effect_tick_procs[this->subtype](this);
646 }
647 
UpdateDeltaXY()648 void EffectVehicle::UpdateDeltaXY()
649 {
650 	this->x_offs        = 0;
651 	this->y_offs        = 0;
652 	this->x_extent      = 1;
653 	this->y_extent      = 1;
654 	this->z_extent      = 1;
655 }
656 
657 /**
658  * Determines the transparency option affecting the effect.
659  * @return Transparency option, or TO_INVALID if none.
660  */
GetTransparencyOption() const661 TransparencyOption EffectVehicle::GetTransparencyOption() const
662 {
663 	return _effect_transparency_options[this->subtype];
664 }
665