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