OpenTTD
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;
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++;
100  } else if ((v->progress & 7) == 1) {
101  if (v->cur_image != SPR_DIESEL_SMOKE_5) {
102  v->cur_image++;
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++;
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++;
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  }
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++;
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 
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  }
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 
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) {
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 
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 
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 
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;
607 
608  _effect_init_procs[type](v);
609 
611 
612  return v;
613 }
614 
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 
645 {
646  return _effect_tick_procs[this->subtype](this);
647 }
648 
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 
663 {
664  return _effect_transparency_options[this->subtype];
665 }