OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

effectvehicle.cpp
Go to the documentation of this file.
1 /* $Id: effectvehicle.cpp 26863 2014-09-20 15:31:26Z rubidium $ */
2
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 */
9
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"
21
22 #include "safeguards.h"
23
24
25 static void ChimneySmokeInit(EffectVehicle *v)
26 {
27 uint32 r = Random();
28 v->cur_image = SPR_CHIMNEY_SMOKE_0 + GB(r, 0, 3);
29 v->progress = GB(r, 16, 3);
30 }
31
32 static bool ChimneySmokeTick(EffectVehicle *v)
33 {
34 if (v->progress > 0) {
35 v->progress--;
36 } else {
37 TileIndex tile = TileVirtXY(v->x_pos, v->y_pos);
38 if (!IsTileType(tile, MP_INDUSTRY)) {
39 delete v;
40 return false;
41 }
42
43 if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
44 v->cur_image++;
45 } else {
46 v->cur_image = SPR_CHIMNEY_SMOKE_0;
47 }
48 v->progress = 7;
49 v->UpdatePositionAndViewport();
50 }
51
52 return true;
53 }
54
55 static void SteamSmokeInit(EffectVehicle *v)
56 {
57 v->cur_image = SPR_STEAM_SMOKE_0;
58 v->progress = 12;
59 }
60
61 static bool SteamSmokeTick(EffectVehicle *v)
62 {
63 bool moved = false;
64
65 v->progress++;
66
67 if ((v->progress & 7) == 0) {
68 v->z_pos++;
69 moved = true;
70 }
71
72 if ((v->progress & 0xF) == 4) {
73 if (v->cur_image != SPR_STEAM_SMOKE_4) {
74 v->cur_image++;
75 } else {
76 delete v;
77 return false;
78 }
79 moved = true;
80 }
81
82 if (moved) v->UpdatePositionAndViewport();
83
84 return true;
85 }
86
87 static void DieselSmokeInit(EffectVehicle *v)
88 {
89 v->cur_image = SPR_DIESEL_SMOKE_0;
90 v->progress = 0;
91 }
92
93 static bool DieselSmokeTick(EffectVehicle *v)
94 {
95 v->progress++;
96
97 if ((v->progress & 3) == 0) {
98 v->z_pos++;
99 v->UpdatePositionAndViewport();
100 } else if ((v->progress & 7) == 1) {
101 if (v->cur_image != SPR_DIESEL_SMOKE_5) {
102 v->cur_image++;
103 v->UpdatePositionAndViewport();
104 } else {
105 delete v;
106 return false;
107 }
108 }
109
110 return true;
111 }
112
113 static void ElectricSparkInit(EffectVehicle *v)
114 {
115 v->cur_image = SPR_ELECTRIC_SPARK_0;
116 v->progress = 1;
117 }
118
119 static bool ElectricSparkTick(EffectVehicle *v)
120 {
121 if (v->progress < 2) {
122 v->progress++;
123 } else {
124 v->progress = 0;
125 if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
126 v->cur_image++;
127 v->UpdatePositionAndViewport();
128 } else {
129 delete v;
130 return false;
131 }
132 }
133
134 return true;
135 }
136
137 static void SmokeInit(EffectVehicle *v)
138 {
139 v->cur_image = SPR_SMOKE_0;
140 v->progress = 12;
141 }
142
143 static bool SmokeTick(EffectVehicle *v)
144 {
145 bool moved = false;
146
147 v->progress++;
148
149 if ((v->progress & 3) == 0) {
150 v->z_pos++;
151 moved = true;
152 }
153
154 if ((v->progress & 0xF) == 4) {
155 if (v->cur_image != SPR_SMOKE_4) {
156 v->cur_image++;
157 } else {
158 delete v;
159 return false;
160 }
161 moved = true;
162 }
163
164 if (moved) v->UpdatePositionAndViewport();
165
166 return true;
167 }
168
169 static void ExplosionLargeInit(EffectVehicle *v)
170 {
171 v->cur_image = SPR_EXPLOSION_LARGE_0;
172 v->progress = 0;
173 }
174
175 static bool ExplosionLargeTick(EffectVehicle *v)
176 {
177 v->progress++;
178 if ((v->progress & 3) == 0) {
179 if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
180 v->cur_image++;
181 v->UpdatePositionAndViewport();
182 } else {
183 delete v;
184 return false;
185 }
186 }
187
188 return true;
189 }
190
191 static void BreakdownSmokeInit(EffectVehicle *v)
192 {
193 v->cur_image = SPR_BREAKDOWN_SMOKE_0;
194 v->progress = 0;
195 }
196
197 static bool BreakdownSmokeTick(EffectVehicle *v)
198 {
199 v->progress++;
200 if ((v->progress & 7) == 0) {
201 if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
202 v->cur_image++;
203 } else {
204 v->cur_image = SPR_BREAKDOWN_SMOKE_0;
205 }
206 v->UpdatePositionAndViewport();
207 }
208
209 v->animation_state--;
210 if (v->animation_state == 0) {
211 delete v;
212 return false;
213 }
214
215 return true;
216 }
217
218 static void ExplosionSmallInit(EffectVehicle *v)
219 {
220 v->cur_image = SPR_EXPLOSION_SMALL_0;
221 v->progress = 0;
222 }
223
224 static bool ExplosionSmallTick(EffectVehicle *v)
225 {
226 v->progress++;
227 if ((v->progress & 3) == 0) {
228 if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
229 v->cur_image++;
230 v->UpdatePositionAndViewport();
231 } else {
232 delete v;
233 return false;
234 }
235 }
236
237 return true;
238 }
239
240 static void BulldozerInit(EffectVehicle *v)
241 {
242 v->cur_image = SPR_BULLDOZER_NE;
243 v->progress = 0;
244 v->animation_state = 0;
245 v->animation_substate = 0;
246 }
247
248 struct BulldozerMovement {
249 byte direction:2;
250 byte image:2;
251 byte duration:3;
252 };
253
254 static const BulldozerMovement _bulldozer_movement[] = {
255 { 0, 0, 4 },
256 { 3, 3, 4 },
257 { 2, 2, 7 },
258 { 0, 2, 7 },
259 { 1, 1, 3 },
260 { 2, 2, 7 },
261 { 0, 2, 7 },
262 { 1, 1, 3 },
263 { 2, 2, 7 },
264 { 0, 2, 7 },
265 { 3, 3, 6 },
266 { 2, 2, 6 },
267 { 1, 1, 7 },
268 { 3, 1, 7 },
269 { 0, 0, 3 },
270 { 1, 1, 7 },
271 { 3, 1, 7 },
272 { 0, 0, 3 },
273 { 1, 1, 7 },
274 { 3, 1, 7 }
275 };
276
277 static const struct {
278 int8 x;
279 int8 y;
280 } _inc_by_dir[] = {
281 { -1, 0 },
282 { 0, 1 },
283 { 1, 0 },
284 { 0, -1 }
285 };
286
287 static bool BulldozerTick(EffectVehicle *v)
288 {
289 v->progress++;
290 if ((v->progress & 7) == 0) {
291 const BulldozerMovement *b = &_bulldozer_movement[v->animation_state];
292
293 v->cur_image = SPR_BULLDOZER_NE + b->image;
294
295 v->x_pos += _inc_by_dir[b->direction].x;
296 v->y_pos += _inc_by_dir[b->direction].y;
297
298 v->animation_substate++;
299 if (v->animation_substate >= b->duration) {
300 v->animation_substate = 0;
301 v->animation_state++;
302 if (v->animation_state == lengthof(_bulldozer_movement)) {
303 delete v;
304 return false;
305 }
306 }
307 v->UpdatePositionAndViewport();
308 }
309
310 return true;
311 }
312
313 static void BubbleInit(EffectVehicle *v)
314 {
315 v->cur_image = SPR_BUBBLE_GENERATE_0;
316 v->spritenum = 0;
317 v->progress = 0;
318 }
319
320 struct BubbleMovement {
321 int8 x:4;
322 int8 y:4;
323 int8 z:4;
324 byte image:4;
325 };
326
327 #define MK(x, y, z, i) { x, y, z, i }
328 #define ME(i) { i, 4, 0, 0 }
329
330 static const BubbleMovement _bubble_float_sw[] = {
331 MK(0, 0, 1, 0),
332 MK(1, 0, 1, 1),
333 MK(0, 0, 1, 0),
334 MK(1, 0, 1, 2),
335 ME(1)
336 };
337
338
339 static const BubbleMovement _bubble_float_ne[] = {
340 MK( 0, 0, 1, 0),
341 MK(-1, 0, 1, 1),
342 MK( 0, 0, 1, 0),
343 MK(-1, 0, 1, 2),
344 ME(1)
345 };
346
347 static const BubbleMovement _bubble_float_se[] = {
348 MK(0, 0, 1, 0),
349 MK(0, 1, 1, 1),
350 MK(0, 0, 1, 0),
351 MK(0, 1, 1, 2),
352 ME(1)
353 };
354
355 static const BubbleMovement _bubble_float_nw[] = {
356 MK(0, 0, 1, 0),
357 MK(0, -1, 1, 1),
358 MK(0, 0, 1, 0),
359 MK(0, -1, 1, 2),
360 ME(1)
361 };
362
363 static const BubbleMovement _bubble_burst[] = {
364 MK(0, 0, 1, 2),
365 MK(0, 0, 1, 7),
366 MK(0, 0, 1, 8),
367 MK(0, 0, 1, 9),
368 ME(0)
369 };
370
371 static const BubbleMovement _bubble_absorb[] = {
372 MK(0, 0, 1, 0),
373 MK(0, 0, 1, 1),
374 MK(0, 0, 1, 0),
375 MK(0, 0, 1, 2),
376 MK(0, 0, 1, 0),
377 MK(0, 0, 1, 1),
378 MK(0, 0, 1, 0),
379 MK(0, 0, 1, 2),
380 MK(0, 0, 1, 0),
381 MK(0, 0, 1, 1),
382 MK(0, 0, 1, 0),
383 MK(0, 0, 1, 2),
384 MK(0, 0, 1, 0),
385 MK(0, 0, 1, 1),
386 MK(0, 0, 1, 0),
387 MK(0, 0, 1, 2),
388 MK(0, 0, 1, 0),
389 MK(0, 0, 1, 1),
390 MK(0, 0, 1, 0),
391 MK(0, 0, 1, 2),
392 MK(0, 0, 1, 0),
393 MK(0, 0, 1, 1),
394 MK(0, 0, 1, 0),
395 MK(0, 0, 1, 2),
396 MK(0, 0, 1, 0),
397 MK(0, 0, 1, 1),
398 MK(0, 0, 1, 0),
399 MK(0, 0, 1, 2),
400 MK(0, 0, 1, 0),
401 MK(0, 0, 1, 1),
402 MK(0, 0, 1, 0),
403 MK(0, 0, 1, 2),
404 MK(0, 0, 1, 0),
405 MK(0, 0, 1, 1),
406 MK(0, 0, 1, 0),
407 MK(0, 0, 1, 2),
408 MK(0, 0, 1, 0),
409 MK(0, 0, 1, 1),
410 MK(0, 0, 1, 0),
411 MK(0, 0, 1, 2),
412 MK(0, 0, 1, 0),
413 MK(0, 0, 1, 1),
414 MK(0, 0, 1, 0),
415 MK(0, 0, 1, 2),
416 MK(0, 0, 1, 0),
417 MK(0, 0, 1, 1),
418 MK(0, 0, 1, 0),
419 MK(0, 0, 1, 2),
420 MK(0, 0, 1, 0),
421 MK(0, 0, 1, 1),
422 MK(0, 0, 1, 0),
423 MK(0, 0, 1, 2),
424 MK(0, 0, 1, 0),
425 MK(0, 0, 1, 1),
426 MK(0, 0, 1, 0),
427 MK(0, 0, 1, 2),
428 MK(0, 0, 1, 0),
429 MK(0, 0, 1, 1),
430 MK(0, 0, 1, 0),
431 MK(0, 0, 1, 2),
432 MK(0, 0, 1, 0),
433 MK(0, 0, 1, 1),
434 MK(2, 1, 3, 0),
435 MK(1, 1, 3, 1),
436 MK(2, 1, 3, 0),
437 MK(1, 1, 3, 2),
438 MK(2, 1, 3, 0),
439 MK(1, 1, 3, 1),
440 MK(2, 1, 3, 0),
441 MK(1, 0, 1, 2),
442 MK(0, 0, 1, 0),
443 MK(1, 0, 1, 1),
444 MK(0, 0, 1, 0),
445 MK(1, 0, 1, 2),
446 MK(0, 0, 1, 0),
447 MK(1, 0, 1, 1),
448 MK(0, 0, 1, 0),
449 MK(1, 0, 1, 2),
450 ME(2),
451 MK(0, 0, 0, 0xA),
452 MK(0, 0, 0, 0xB),
453 MK(0, 0, 0, 0xC),
454 MK(0, 0, 0, 0xD),
455 MK(0, 0, 0, 0xE),
456 ME(0)
457 };
458 #undef ME
459 #undef MK
460
461 static const BubbleMovement * const _bubble_movement[] = {
462 _bubble_float_sw,
463 _bubble_float_ne,
464 _bubble_float_se,
465 _bubble_float_nw,
466 _bubble_burst,
467 _bubble_absorb,
468 };
469
470 static bool BubbleTick(EffectVehicle *v)
471 {
472 uint anim_state;
473
474 v->progress++;
475 if ((v->progress & 3) != 0) return true;
476
477 if (v->spritenum == 0) {
478 v->cur_image++;
479 if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
480 v->UpdatePositionAndViewport();
481 return true;
482 }
483 if (v->animation_substate != 0) {
484 v->spritenum = GB(Random(), 0, 2) + 1;
485 } else {
486 v->spritenum = 6;
487 }
488 anim_state = 0;
489 } else {
490 anim_state = v->animation_state + 1;
491 }
492
493 const BubbleMovement *b = &_bubble_movement[v->spritenum - 1][anim_state];
494
495 if (b->y == 4 && b->x == 0) {
496 delete v;
497 return false;
498 }
499
500 if (b->y == 4 && b->x == 1) {
501 if (v->z_pos > 180 || Chance16I(1, 96, Random())) {
502 v->spritenum = 5;
503 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_2F_POP, v);
504 }
505 anim_state = 0;
506 }
507
508 if (b->y == 4 && b->x == 2) {
509 TileIndex tile;
510
511 anim_state++;
512 if (_settings_client.sound.ambient) SndPlayVehicleFx(SND_31_EXTRACT, v);
513
514 tile = TileVirtXY(v->x_pos, v->y_pos);
515 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == GFX_BUBBLE_CATCHER) AddAnimatedTile(tile);
516 }
517
518 v->animation_state = anim_state;
519 b = &_bubble_movement[v->spritenum - 1][anim_state];
520
521 v->x_pos += b->x;
522 v->y_pos += b->y;
523 v->z_pos += b->z;
524 v->cur_image = SPR_BUBBLE_0 + b->image;
525
526 v->UpdatePositionAndViewport();
527
528 return true;
529 }
530
531
532 typedef void EffectInitProc(EffectVehicle *v);
533 typedef bool EffectTickProc(EffectVehicle *v);
534
536 static EffectInitProc * const _effect_init_procs[] = {
537 ChimneySmokeInit, // EV_CHIMNEY_SMOKE
538 SteamSmokeInit, // EV_STEAM_SMOKE
539 DieselSmokeInit, // EV_DIESEL_SMOKE
540 ElectricSparkInit, // EV_ELECTRIC_SPARK
541 SmokeInit, // EV_CRASH_SMOKE
542 ExplosionLargeInit, // EV_EXPLOSION_LARGE
543 BreakdownSmokeInit, // EV_BREAKDOWN_SMOKE
544 ExplosionSmallInit, // EV_EXPLOSION_SMALL
545 BulldozerInit, // EV_BULLDOZER
546 BubbleInit, // EV_BUBBLE
547 SmokeInit, // EV_BREAKDOWN_SMOKE_AIRCRAFT
548 SmokeInit, // EV_COPPER_MINE_SMOKE
549 };
550 assert_compile(lengthof(_effect_init_procs) == EV_END);
551
553 static EffectTickProc * const _effect_tick_procs[] = {
554 ChimneySmokeTick, // EV_CHIMNEY_SMOKE
555 SteamSmokeTick, // EV_STEAM_SMOKE
556 DieselSmokeTick, // EV_DIESEL_SMOKE
557 ElectricSparkTick, // EV_ELECTRIC_SPARK
558 SmokeTick, // EV_CRASH_SMOKE
559 ExplosionLargeTick, // EV_EXPLOSION_LARGE
560 BreakdownSmokeTick, // EV_BREAKDOWN_SMOKE
561 ExplosionSmallTick, // EV_EXPLOSION_SMALL
562 BulldozerTick, // EV_BULLDOZER
563 BubbleTick, // EV_BUBBLE
564 SmokeTick, // EV_BREAKDOWN_SMOKE_AIRCRAFT
565 SmokeTick, // EV_COPPER_MINE_SMOKE
566 };
567 assert_compile(lengthof(_effect_tick_procs) == EV_END);
568
570 static const TransparencyOption _effect_transparency_options[] = {
571 TO_INDUSTRIES, // EV_CHIMNEY_SMOKE
572 TO_INVALID, // EV_STEAM_SMOKE
573 TO_INVALID, // EV_DIESEL_SMOKE
574 TO_INVALID, // EV_ELECTRIC_SPARK
575 TO_INVALID, // EV_CRASH_SMOKE
576 TO_INVALID, // EV_EXPLOSION_LARGE
577 TO_INVALID, // EV_BREAKDOWN_SMOKE
578 TO_INVALID, // EV_EXPLOSION_SMALL
579 TO_INVALID, // EV_BULLDOZER
580 TO_INDUSTRIES, // EV_BUBBLE
581 TO_INVALID, // EV_BREAKDOWN_SMOKE_AIRCRAFT
582 TO_INDUSTRIES, // EV_COPPER_MINE_SMOKE
583 };
584 assert_compile(lengthof(_effect_transparency_options) == EV_END);
585
586
595 EffectVehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicleType type)
596 {
597 if (!Vehicle::CanAllocateItem()) return NULL;
598
599 EffectVehicle *v = new EffectVehicle();
600 v->subtype = type;
601 v->x_pos = x;
602 v->y_pos = y;
603 v->z_pos = z;
604 v->tile = 0;
605 v->UpdateDeltaXY(INVALID_DIR);
606 v->vehstatus = VS_UNCLICKABLE;
607
608 _effect_init_procs[type](v);
609
610 v->UpdatePositionAndViewport();
611
612 return v;
613 }
614
623 EffectVehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
624 {
625 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
626 int safe_y = Clamp(y, 0, MapMaxY() * TILE_SIZE);
627 return CreateEffectVehicle(x, y, GetSlopePixelZ(safe_x, safe_y) + z, type);
628 }
629
639 EffectVehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
640 {
641 return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
642 }
643
644 bool EffectVehicle::Tick()
645 {
646 return _effect_tick_procs[this->subtype](this);
647 }
648
649 void EffectVehicle::UpdateDeltaXY(Direction direction)
650 {
651 this->x_offs = 0;
652 this->y_offs = 0;
653 this->x_extent = 1;
654 this->y_extent = 1;
655 this->z_extent = 1;
656 }
657
662 TransparencyOption EffectVehicle::GetTransparencyOption() const
663 {
664 return _effect_transparency_options[this->subtype];
665 }