OpenTTD Source 20250205-master-gfd85ab1e2c
disaster_vehicle.cpp
Go to the documentation of this file.
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
26#include "stdafx.h"
27
28#include "aircraft.h"
29#include "disaster_vehicle.h"
30#include "industry.h"
31#include "station_base.h"
32#include "command_func.h"
33#include "news_func.h"
34#include "town.h"
35#include "company_func.h"
36#include "strings_func.h"
37#include "viewport_func.h"
38#include "vehicle_func.h"
39#include "sound_func.h"
40#include "effectvehicle_func.h"
41#include "roadveh.h"
42#include "train.h"
43#include "ai/ai.hpp"
44#include "game/game.hpp"
45#include "company_base.h"
46#include "core/random_func.hpp"
47#include "core/backup_type.hpp"
48#include "landscape_cmd.h"
49#include "timer/timer.h"
51
52#include "table/strings.h"
53
54#include "safeguards.h"
55
58
59static void DisasterClearSquare(TileIndex tile)
60{
61 if (EnsureNoVehicleOnGround(tile).Failed()) return;
62
63 switch (GetTileType(tile)) {
64 case MP_RAILWAY:
65 if (Company::IsHumanID(GetTileOwner(tile)) && !IsRailDepot(tile)) {
68 cur_company.Restore();
69
70 /* update signals in buffer */
72 }
73 break;
74
75 case MP_HOUSE: {
78 cur_company.Restore();
79 break;
80 }
81
82 case MP_TREES:
83 case MP_CLEAR:
84 DoClearSquare(tile);
85 break;
86
87 default:
88 break;
89 }
90}
91
92static const SpriteID _disaster_images_1[] = {SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP};
93static const SpriteID _disaster_images_2[] = {SPR_UFO_SMALL_SCOUT, SPR_UFO_SMALL_SCOUT, SPR_UFO_SMALL_SCOUT, SPR_UFO_SMALL_SCOUT, SPR_UFO_SMALL_SCOUT, SPR_UFO_SMALL_SCOUT, SPR_UFO_SMALL_SCOUT, SPR_UFO_SMALL_SCOUT};
94static const SpriteID _disaster_images_3[] = {SPR_F_15, SPR_F_15, SPR_F_15, SPR_F_15, SPR_F_15, SPR_F_15, SPR_F_15, SPR_F_15};
95static const SpriteID _disaster_images_4[] = {SPR_SUB_SMALL_NE, SPR_SUB_SMALL_NE, SPR_SUB_SMALL_SE, SPR_SUB_SMALL_SE, SPR_SUB_SMALL_SW, SPR_SUB_SMALL_SW, SPR_SUB_SMALL_NW, SPR_SUB_SMALL_NW};
96static const SpriteID _disaster_images_5[] = {SPR_SUB_LARGE_NE, SPR_SUB_LARGE_NE, SPR_SUB_LARGE_SE, SPR_SUB_LARGE_SE, SPR_SUB_LARGE_SW, SPR_SUB_LARGE_SW, SPR_SUB_LARGE_NW, SPR_SUB_LARGE_NW};
97static const SpriteID _disaster_images_6[] = {SPR_UFO_HARVESTER, SPR_UFO_HARVESTER, SPR_UFO_HARVESTER, SPR_UFO_HARVESTER, SPR_UFO_HARVESTER, SPR_UFO_HARVESTER, SPR_UFO_HARVESTER, SPR_UFO_HARVESTER};
98static const SpriteID _disaster_images_7[] = {SPR_XCOM_SKYRANGER, SPR_XCOM_SKYRANGER, SPR_XCOM_SKYRANGER, SPR_XCOM_SKYRANGER, SPR_XCOM_SKYRANGER, SPR_XCOM_SKYRANGER, SPR_XCOM_SKYRANGER, SPR_XCOM_SKYRANGER};
99static const SpriteID _disaster_images_8[] = {SPR_AH_64A, SPR_AH_64A, SPR_AH_64A, SPR_AH_64A, SPR_AH_64A, SPR_AH_64A, SPR_AH_64A, SPR_AH_64A};
100static const SpriteID _disaster_images_9[] = {SPR_ROTOR_MOVING_1, SPR_ROTOR_MOVING_1, SPR_ROTOR_MOVING_1, SPR_ROTOR_MOVING_1, SPR_ROTOR_MOVING_1, SPR_ROTOR_MOVING_1, SPR_ROTOR_MOVING_1, SPR_ROTOR_MOVING_1};
101
102static const SpriteID * const _disaster_images[] = {
103 _disaster_images_1, _disaster_images_1,
104 _disaster_images_2, _disaster_images_2,
105 _disaster_images_3, _disaster_images_3,
106 _disaster_images_8, _disaster_images_8, _disaster_images_9,
107 _disaster_images_6, _disaster_images_6,
108 _disaster_images_7, _disaster_images_7,
109 _disaster_images_4, _disaster_images_5,
110};
111
112void DisasterVehicle::UpdateImage()
113{
114 SpriteID img = this->image_override;
115 if (img == 0) img = _disaster_images[this->subtype][this->direction];
116 this->sprite_cache.sprite_seq.Set(img);
117}
118
127DisasterVehicle::DisasterVehicle(int x, int y, Direction direction, DisasterSubType subtype, VehicleID big_ufo_destroyer_target) :
128 SpecializedVehicleBase(), big_ufo_destroyer_target(big_ufo_destroyer_target)
129{
131
132 this->x_pos = x;
133 this->y_pos = y;
134 switch (subtype) {
135 case ST_ZEPPELINER:
136 case ST_SMALL_UFO:
137 case ST_AIRPLANE:
138 case ST_HELICOPTER:
139 case ST_BIG_UFO:
141 GetAircraftFlightLevelBounds(this, &this->z_pos, nullptr);
142 break;
143
145 GetAircraftFlightLevelBounds(this, &this->z_pos, nullptr);
146 this->z_pos += ROTOR_Z_OFFSET;
147 break;
148
150 case ST_BIG_SUBMARINE:
151 this->z_pos = 0;
152 break;
153
160 this->z_pos = 0;
161 this->vehstatus |= VS_SHADOW;
162 break;
163 }
164
165 this->direction = direction;
166 this->tile = TileVirtXY(x, y);
167 this->subtype = subtype;
168 this->UpdateDeltaXY();
169 this->owner = OWNER_NONE;
170 this->image_override = 0;
171 this->state = 0;
172
173 this->UpdateImage();
175}
176
183void DisasterVehicle::UpdatePosition(int x, int y, int z)
184{
185 this->x_pos = x;
186 this->y_pos = y;
187 this->z_pos = z;
188 this->tile = TileVirtXY(x, y);
189
190 this->UpdateImage();
192
193 DisasterVehicle *u = this->Next();
194 if (u != nullptr) {
195 int safe_x = Clamp(x, 0, Map::MaxX() * TILE_SIZE);
196 int safe_y = Clamp(y - 1, 0, Map::MaxY() * TILE_SIZE);
197
198 u->x_pos = x;
199 u->y_pos = y - 1 - (std::max(z - GetSlopePixelZ(safe_x, safe_y), 0) >> 3);
200 safe_y = Clamp(u->y_pos, 0, Map::MaxY() * TILE_SIZE);
201 u->z_pos = GetSlopePixelZ(safe_x, safe_y);
202 u->direction = this->direction;
203
204 u->UpdateImage();
206
207 if ((u = u->Next()) != nullptr) {
208 u->x_pos = x;
209 u->y_pos = y;
210 u->z_pos = z + ROTOR_Z_OFFSET;
212 }
213 }
214}
215
225{
226 v->tick_counter++;
227
228 if (v->state < 2) {
229 if (HasBit(v->tick_counter, 0)) return true;
230
232
233 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
234
235 if (v->state == 1) {
236 if (++v->age == 38) {
237 v->state = 2;
239 }
240
241 if (GB(v->tick_counter, 0, 3) == 0) CreateEffectVehicleRel(v, 0, -17, 2, EV_CRASH_SMOKE);
242
243 } else if (v->state == 0) {
244 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
245 v->state = 1;
247
249 AddTileNewsItem(STR_NEWS_DISASTER_ZEPPELIN, NT_ACCIDENT, v->tile);
250 AI::NewEvent(GetTileOwner(v->tile), new ScriptEventDisasterZeppelinerCrashed(GetStationIndex(v->tile)));
251 }
252 }
253
254 if (v->y_pos >= (int)((Map::SizeY() + 9) * TILE_SIZE - 1)) {
255 delete v;
256 return false;
257 }
258
259 return true;
260 }
261
262 if (v->state > 2) {
263 if (++v->age <= 13320) return true;
264
265 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
267 CLRBITS(st->airport.flags, RUNWAY_IN_block);
268 AI::NewEvent(GetTileOwner(v->tile), new ScriptEventDisasterZeppelinerCleared(st->index));
269 }
270
271 v->UpdatePosition(v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
272 delete v;
273 return false;
274 }
275
276 int x = v->x_pos;
277 int y = v->y_pos;
278 int z = GetSlopePixelZ(x, y);
279 if (z < v->z_pos) z = v->z_pos - 1;
280 v->UpdatePosition(x, y, z);
281
282 if (++v->age == 1) {
284 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
285 v->image_override = SPR_BLIMP_CRASHING;
286 } else if (v->age == 70) {
287 v->image_override = SPR_BLIMP_CRASHED;
288 } else if (v->age <= 300) {
289 if (GB(v->tick_counter, 0, 3) == 0) {
290 uint32_t r = Random();
291
293 GB(r, 0, 4) - 7,
294 GB(r, 4, 4) - 7,
295 GB(r, 8, 3) + 5,
297 }
298 } else if (v->age == 350) {
299 v->state = 3;
301 }
302
303 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
304 SETBITS(Station::GetByTile(v->tile)->airport.flags, RUNWAY_IN_block);
305 }
306
307 return true;
308}
309
317{
318 v->image_override = (HasBit(++v->tick_counter, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
319
320 if (v->state == 0) {
321 /* Fly around randomly */
322 int x = TileX(v->dest_tile) * TILE_SIZE;
323 int y = TileY(v->dest_tile) * TILE_SIZE;
324 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= (int)TILE_SIZE) {
325 v->direction = GetDirectionTowards(v, x, y);
327 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
328 return true;
329 }
330 if (++v->age < 6) {
331 v->dest_tile = RandomTile();
332 return true;
333 }
334 v->state = 1;
335
336 uint n = 0; // Total number of targetable road vehicles.
337 for (const Company *c : Company::Iterate()) {
338 n += c->group_all[VEH_ROAD].num_vehicle;
339 }
340
341 if (n == 0) {
342 /* If there are no targetable road vehicles, destroy the UFO. */
343 delete v;
344 return false;
345 }
346
347 n = RandomRange(n); // Choose one of them.
348 for (RoadVehicle *u : RoadVehicle::Iterate()) {
349 /* Find (n+1)-th road vehicle. */
350 if (u->IsFrontEngine() && (n-- == 0)) {
351 if (u->crashed_ctr != 0 || u->disaster_vehicle != INVALID_VEHICLE) {
352 /* Targetted vehicle is crashed or already a target, destroy the UFO. */
353 delete v;
354 return false;
355 }
356 /* Target it. */
357 v->dest_tile = TileIndex{u->index};
359 u->disaster_vehicle = v->index;
360 break;
361 }
362 }
363
364 return true;
365 } else {
366 /* Target a vehicle */
367 RoadVehicle *u = RoadVehicle::Get(v->dest_tile.base());
368 assert(u != nullptr && u->type == VEH_ROAD && u->IsFrontEngine());
369
370 uint dist = Delta(v->x_pos, u->x_pos) + Delta(v->y_pos, u->y_pos);
371
372 if (dist < TILE_SIZE && !(u->vehstatus & VS_HIDDEN) && u->breakdown_ctr == 0) {
373 u->breakdown_ctr = 3;
374 u->breakdown_delay = 140;
375 }
376
377 v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
379
380 int z = v->z_pos;
381 if (dist <= TILE_SIZE && z > u->z_pos) z--;
382 v->UpdatePosition(gp.x, gp.y, z);
383
384 if (z <= u->z_pos && (u->vehstatus & VS_HIDDEN) == 0) {
385 v->age++;
386 if (u->crashed_ctr == 0) {
387 uint victims = u->Crash();
389
390 AddTileNewsItem(STR_NEWS_DISASTER_SMALL_UFO, NT_ACCIDENT, u->tile);
391
392 AI::NewEvent(u->owner, new ScriptEventVehicleCrashed(u->index, u->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO, victims));
393 Game::NewEvent(new ScriptEventVehicleCrashed(u->index, u->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO, victims));
394 }
395 }
396
397 /* Destroy? */
398 if (v->age > 50) {
400 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
401 delete v;
402 return false;
403 }
404 }
405
406 return true;
407}
408
409static void DestructIndustry(Industry *i)
410{
411 for (const auto tile : Map::Iterate()) {
412 if (i->TileBelongsToIndustry(tile)) {
415 }
416 }
417}
418
432static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16_t image_override, bool leave_at_top, StringID news_message, IndustryBehaviour industry_flag)
433{
434 v->tick_counter++;
435 v->image_override = (v->state == 1 && HasBit(v->tick_counter, 2)) ? image_override : 0;
436
438 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
439
440 if ((leave_at_top && gp.x < (-10 * (int)TILE_SIZE)) || (!leave_at_top && gp.x > (int)(Map::SizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1)) {
441 delete v;
442 return false;
443 }
444
445 if (v->state == 2) {
446 if (GB(v->tick_counter, 0, 2) == 0) {
447 Industry *i = Industry::Get(v->dest_tile.base()); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
448 int x = TileX(i->location.tile) * TILE_SIZE;
449 int y = TileY(i->location.tile) * TILE_SIZE;
450 uint32_t r = Random();
451
453 GB(r, 0, 6) + x,
454 GB(r, 6, 6) + y,
455 GB(r, 12, 4),
457
458 if (++v->age >= 55) v->state = 3;
459 }
460 } else if (v->state == 1) {
461 if (++v->age == 112) {
462 v->state = 2;
464
465 Industry *i = Industry::Get(v->dest_tile.base()); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
466 DestructIndustry(i);
467
468 SetDParam(0, i->town->index);
469 AddIndustryNewsItem(news_message, NT_ACCIDENT, i->index);
471 }
472 } else if (v->state == 0) {
473 int x = v->x_pos + ((leave_at_top ? -15 : 15) * TILE_SIZE);
474 int y = v->y_pos;
475
476 if ((uint)x > Map::MaxX() * TILE_SIZE - 1) return true;
477
478 TileIndex tile = TileVirtXY(x, y);
479 if (!IsTileType(tile, MP_INDUSTRY)) return true;
480
481 IndustryID ind = GetIndustryIndex(tile);
482 v->dest_tile = TileIndex{ind};
483
484 if (GetIndustrySpec(Industry::Get(ind)->type)->behaviour & industry_flag) {
485 v->state = 1;
487 }
488 }
489
490 return true;
491}
492
495{
496 return DisasterTick_Aircraft(v, SPR_F_15_FIRING, true, STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY, INDUSTRYBEH_AIRPLANE_ATTACKS);
497}
498
501{
502 return DisasterTick_Aircraft(v, SPR_AH_64A_FIRING, false, STR_NEWS_DISASTER_HELICOPTER_FACTORY, INDUSTRYBEH_CHOPPER_ATTACKS);
503}
504
507{
508 v->tick_counter++;
509 if (HasBit(v->tick_counter, 0)) return true;
510
511 SpriteID &cur_image = v->sprite_cache.sprite_seq.seq[0].sprite;
512 if (++cur_image > SPR_ROTOR_MOVING_3) cur_image = SPR_ROTOR_MOVING_1;
513
515
516 return true;
517}
518
526{
527 v->tick_counter++;
528
529 if (v->state == 1) {
530 int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
531 int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
532 if (Delta(v->x_pos, x) + Delta(v->y_pos, y) >= 8) {
533 v->direction = GetDirectionTowards(v, x, y);
534
536 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
537 return true;
538 }
539
540 if (!IsValidTile(v->dest_tile)) {
541 /* Make sure we don't land outside the map. */
542 delete v;
543 return false;
544 }
545
546 int z = GetSlopePixelZ(v->x_pos, v->y_pos);
547 if (z < v->z_pos) {
548 v->UpdatePosition(v->x_pos, v->y_pos, v->z_pos - 1);
549 return true;
550 }
551
552 v->state = 2;
553
554 for (Vehicle *target : Vehicle::Iterate()) {
555 if (target->IsGroundVehicle()) {
556 if (Delta(target->x_pos, v->x_pos) + Delta(target->y_pos, v->y_pos) <= 12 * (int)TILE_SIZE) {
557 target->breakdown_ctr = 5;
558 target->breakdown_delay = 0xF0;
559 }
560 }
561 }
562
563 Town *t = ClosestTownFromTile(v->dest_tile, UINT_MAX);
564 SetDParam(0, t->index);
565 AddTileNewsItem(STR_NEWS_DISASTER_BIG_UFO, NT_ACCIDENT, v->tile);
566
567 if (!Vehicle::CanAllocateItem(2)) {
568 delete v;
569 return false;
570 }
573 u->SetNext(w);
574 } else if (v->state == 0) {
575 int x = TileX(v->dest_tile) * TILE_SIZE;
576 int y = TileY(v->dest_tile) * TILE_SIZE;
577 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= (int)TILE_SIZE) {
578 v->direction = GetDirectionTowards(v, x, y);
580 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
581 return true;
582 }
583
584 if (++v->age < 6) {
585 v->dest_tile = RandomTile();
586 return true;
587 }
588 v->state = 1;
589
590 const auto is_valid_target = [](const Train *t) {
591 return t->IsFrontEngine() // Only the engines
592 && Company::IsHumanID(t->owner) // Don't break AIs
593 && IsPlainRailTile(t->tile) // No tunnels
594 && (t->vehstatus & VS_CRASHED) == 0; // Not crashed
595 };
596
597 uint n = 0; // Total number of targetable trains.
598 for (const Train *t : Train::Iterate()) {
599 if (is_valid_target(t)) n++;
600 }
601
602 if (n == 0) {
603 /* If there are no targetable trains, destroy the UFO. */
604 delete v;
605 return false;
606 }
607
608 n = RandomRange(n); // Choose one of them.
609 for (const Train *t : Train::Iterate()) {
610 /* Find (n+1)-th train. */
611 if (is_valid_target(t) && (n-- == 0)) {
612 /* Target it. */
613 v->dest_tile = t->tile;
615 break;
616 }
617 }
618 }
619
620 return true;
621}
622
628{
629 v->tick_counter++;
630
632 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
633
634 if (gp.x > (int)(Map::SizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1) {
635 delete v;
636 return false;
637 }
638
639 if (v->state == 0) {
641 if (Delta(v->x_pos, u->x_pos) > (int)TILE_SIZE) return true;
642 v->state = 1;
643
645 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, u);
646
647 delete u;
648
649 for (int i = 0; i != 80; i++) {
650 uint32_t r = Random();
652 GB(r, 0, 6) + v->x_pos - 32,
653 GB(r, 5, 6) + v->y_pos - 32,
654 0,
656 }
657
658 for (int dy = -3; dy < 3; dy++) {
659 for (int dx = -3; dx < 3; dx++) {
660 TileIndex tile = TileAddWrap(v->tile, dx, dy);
661 if (tile != INVALID_TILE) DisasterClearSquare(tile);
662 }
663 }
664 }
665
666 return true;
667}
668
674{
675 v->tick_counter++;
676
677 if (++v->age > 8880) {
678 delete v;
679 return false;
680 }
681
682 if (!HasBit(v->tick_counter, 0)) return true;
683
685 if (IsValidTile(tile)) {
687 if (trackbits == TRACK_BIT_ALL && !Chance16(1, 90)) {
689 v->UpdatePosition(gp.x, gp.y, v->z_pos);
690 return true;
691 }
692 }
693
695
696 return true;
697}
698
699
700static bool DisasterTick_NULL(DisasterVehicle *)
701{
702 return true;
703}
704
705typedef bool DisasterVehicleTickProc(DisasterVehicle *v);
706
707static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
708 DisasterTick_Zeppeliner, DisasterTick_NULL,
709 DisasterTick_Ufo, DisasterTick_NULL,
710 DisasterTick_Airplane, DisasterTick_NULL,
713 DisasterTick_NULL,
716};
717
718
720{
721 return _disastervehicle_tick_procs[this->subtype](this);
722}
723
724typedef void DisasterInitProc();
725
726
732{
733 if (!Vehicle::CanAllocateItem(2)) return;
734
735 /* Pick a random place, unless we find a small airport */
736 int x = TileX(RandomTile()) * TILE_SIZE + TILE_SIZE / 2;
737
738 for (const Station *st : Station::Iterate()) {
739 if (st->airport.tile != INVALID_TILE && (st->airport.type == AT_SMALL || st->airport.type == AT_LARGE)) {
740 x = (TileX(st->airport.tile) + 2) * TILE_SIZE;
741 break;
742 }
743 }
744
746 /* Allocate shadow */
748 v->SetNext(u);
749}
750
751
757{
758 if (!Vehicle::CanAllocateItem(2)) return;
759
760 int x = TileX(RandomTile()) * TILE_SIZE + TILE_SIZE / 2;
762 v->dest_tile = TileXY(Map::SizeX() / 2, Map::SizeY() / 2);
763
764 /* Allocate shadow */
766 v->SetNext(u);
767}
768
769
770/* Combat airplane which destroys an oil refinery */
771static void Disaster_Airplane_Init()
772{
773 if (!Vehicle::CanAllocateItem(2)) return;
774
775 Industry *found = nullptr;
776
777 for (Industry *i : Industry::Iterate()) {
779 (found == nullptr || Chance16(1, 2))) {
780 found = i;
781 }
782 }
783
784 if (found == nullptr) return;
785
786 /* Start from the bottom (south side) of the map */
787 int x = (Map::SizeX() + 9) * TILE_SIZE - 1;
788 int y = TileY(found->location.tile) * TILE_SIZE + 37;
789
792 v->SetNext(u);
793}
794
795
798{
799 if (!Vehicle::CanAllocateItem(3)) return;
800
801 Industry *found = nullptr;
802
803 for (Industry *i : Industry::Iterate()) {
805 (found == nullptr || Chance16(1, 2))) {
806 found = i;
807 }
808 }
809
810 if (found == nullptr) return;
811
812 int x = -16 * (int)TILE_SIZE;
813 int y = TileY(found->location.tile) * TILE_SIZE + 37;
814
817 v->SetNext(u);
818
820 u->SetNext(w);
821}
822
823
824/* Big Ufo which lands on a piece of rail and will consequently be shot
825 * down by a combat airplane, destroying the surroundings */
826static void Disaster_Big_Ufo_Init()
827{
828 if (!Vehicle::CanAllocateItem(2)) return;
829
830 int x = TileX(RandomTile()) * TILE_SIZE + TILE_SIZE / 2;
831 int y = Map::MaxX() * TILE_SIZE - 1;
832
834 v->dest_tile = TileXY(Map::SizeX() / 2, Map::SizeY() / 2);
835
836 /* Allocate shadow */
838 v->SetNext(u);
839}
840
841
842static void Disaster_Submarine_Init(DisasterSubType subtype)
843{
844 if (!Vehicle::CanAllocateItem()) return;
845
846 int y;
847 Direction dir;
848 uint32_t r = Random();
849 int x = TileX(RandomTileSeed(r)) * TILE_SIZE + TILE_SIZE / 2;
850
851 if (HasBit(r, 31)) {
852 y = Map::MaxY() * TILE_SIZE - TILE_SIZE / 2 - 1;
853 dir = DIR_NW;
854 } else {
855 y = TILE_SIZE / 2;
857 dir = DIR_SE;
858 }
859 if (!IsWaterTile(TileVirtXY(x, y))) return;
860
861 new DisasterVehicle(x, y, dir, subtype);
862}
863
864/* Curious submarine #1, just floats around */
865static void Disaster_Small_Submarine_Init()
866{
867 Disaster_Submarine_Init(ST_SMALL_SUBMARINE);
868}
869
870
871/* Curious submarine #2, just floats around */
872static void Disaster_Big_Submarine_Init()
873{
874 Disaster_Submarine_Init(ST_BIG_SUBMARINE);
875}
876
877
883{
884 int index = GB(Random(), 0, 4);
885 uint m;
886
887 for (m = 0; m < 15; m++) {
888 for (const Industry *i : Industry::Iterate()) {
889 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CAN_SUBSIDENCE) && --index < 0) {
890 SetDParam(0, i->town->index);
891 AddTileNewsItem(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE, NT_ACCIDENT, i->location.tile + TileDiffXY(1, 1)); // keep the news, even when the mine closes
892
893 {
894 TileIndex tile = i->location.tile;
896
897 for (uint n = 0; n < 30; n++) {
898 DisasterClearSquare(tile);
899 tile += step;
900 if (!IsValidTile(tile)) break;
901 }
902 }
903 return;
904 }
905 }
906 }
907}
908
914
915static const Disaster _disasters[] = {
918 {Disaster_Airplane_Init, TimerGameCalendar::Year{1960}, TimerGameCalendar::Year{1990}}, // airplane
920 {Disaster_Big_Ufo_Init, TimerGameCalendar::Year{2000}, TimerGameCalendar::Year{2100}}, // ufo {big}
921 {Disaster_Small_Submarine_Init, TimerGameCalendar::Year{1940}, TimerGameCalendar::Year{1965}}, // submarine {small}
922 {Disaster_Big_Submarine_Init, TimerGameCalendar::Year{1975}, TimerGameCalendar::Year{2010}}, // submarine {big}
924};
925
926static void DoDisaster()
927{
928 std::vector<DisasterInitProc *> available_disasters;
929
930 for (auto &disaster : _disasters) {
931 if (TimerGameCalendar::year >= disaster.min_year && TimerGameCalendar::year < disaster.max_year) {
932 available_disasters.push_back(disaster.init_proc);
933 }
934 }
935
936 if (available_disasters.empty()) return;
937
938 available_disasters[RandomRange(static_cast<uint32_t>(available_disasters.size()))]();
939}
940
941
942static void ResetDisasterDelay()
943{
944 _disaster_delay = GB(Random(), 0, 9) + 730;
945}
946
947static IntervalTimer<TimerGameEconomy> _economy_disaster_daily({TimerGameEconomy::DAY, TimerGameEconomy::Priority::DISASTER}, [](auto)
948{
949 if (--_disaster_delay != 0) return;
950
951 ResetDisasterDelay();
952
953 if (_settings_game.difficulty.disasters != 0) DoDisaster();
954});
955
956void StartupDisasters()
957{
958 ResetDisasterDelay();
959}
960
967{
969 /* primary disaster vehicles that have chosen target */
970 if (v->subtype == ST_AIRPLANE || v->subtype == ST_HELICOPTER) {
971 /* if it has chosen target, and it is this industry (yes, dest_tile is IndustryID here), set order to "leaving map peacefully" */
972 if (v->state > 0 && v->dest_tile == (uint32_t)i) v->state = 3;
973 }
974 }
975}
976
982{
984 if (v == nullptr) return;
985
986 /* primary disaster vehicles that have chosen target */
987 assert(v->subtype == ST_SMALL_UFO);
988 assert(v->state != 0);
989
990 /* Revert to target-searching */
991 v->state = 0;
992 v->dest_tile = RandomTile();
993 GetAircraftFlightLevelBounds(v, &v->z_pos, nullptr);
995}
996
998{
999 this->x_offs = -1;
1000 this->y_offs = -1;
1001 this->x_extent = 2;
1002 this->y_extent = 2;
1003 this->z_extent = 5;
1004}
Base functions for all AIs.
Base for aircraft.
void GetAircraftFlightLevelBounds(const Vehicle *v, int *min, int *max)
Get the 'flight level' bounds, in pixels from 'z_pos' 0 for a particular vehicle for normal flight si...
static const int ROTOR_Z_OFFSET
Z Offset between helicopter- and rotorsprite.
Definition aircraft.h:48
@ AT_SMALL
Small airport.
Definition airport.h:29
@ AT_LARGE
Large airport.
Definition airport.h:30
Class for backupping variables and making sure they are restored later.
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
#define CLRBITS(x, y)
Clears several bits in a variable.
#define SETBITS(x, y)
Sets several bits in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:243
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
static Year year
Current year, starting at 0.
static constexpr TimerGame< struct Calendar >::Date MIN_DATE
The date on January 1, year 0.
Functions related to commands.
@ DC_EXEC
execute the given command
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
@ OWNER_NONE
The tile has no ownership.
@ OWNER_WATER
The tile/execution is done by "water".
Direction ChangeDir(Direction d, DirDiff delta)
Change a direction by a given difference.
DiagDirection DirToDiagDir(Direction dir)
Convert a Direction to a DiagDirection.
@ DIRDIFF_90LEFT
Angle of 90 degrees left.
@ DIRDIFF_90RIGHT
Angle of 90 degrees right.
Direction
Defines the 8 directions on the map.
@ DIR_SW
Southwest.
@ DIR_NW
Northwest.
@ DIR_SE
Southeast.
@ DIR_NE
Northeast.
DiagDirection
Enumeration for diagonal directions.
static bool DisasterTick_Big_Ufo_Destroyer(DisasterVehicle *v)
Skyranger destroying (Big) Ufo handling, v->state states: 0: Home in on landed Ufo and shoot it down.
static void Disaster_Small_Ufo_Init()
Ufo which flies around aimlessly from the middle of the map a bit until it locates a road vehicle whi...
static bool DisasterTick_Big_Ufo(DisasterVehicle *v)
(Big) Ufo handling, v->state states: 0: Fly around to the middle of the map, then randomly for a whil...
static void Disaster_Helicopter_Init()
Combat helicopter that destroys a factory.
static bool DisasterTick_Zeppeliner(DisasterVehicle *v)
Zeppeliner handling, v->state states: 0: Zeppeliner initialization has found a small airport,...
static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
Helicopter rotor blades; keep these spinning.
static bool DisasterTick_Ufo(DisasterVehicle *v)
(Small) Ufo handling, v->state states: 0: Fly around to the middle of the map, then randomly,...
static bool DisasterTick_Airplane(DisasterVehicle *v)
Airplane handling.
static void Disaster_Zeppeliner_Init()
Zeppeliner which crashes on a small airport if one found, otherwise crashes on a random tile.
uint16_t _disaster_delay
Delay counter for considering the next disaster.
void ReleaseDisasterVehicle(VehicleID vehicle)
Notify disasters that we are about to delete a vehicle.
static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16_t image_override, bool leave_at_top, StringID news_message, IndustryBehaviour industry_flag)
Aircraft handling, v->state states: 0: Fly towards the targeted industry 1: If within 15 tiles,...
static bool DisasterTick_Submarine(DisasterVehicle *v)
Submarine, v->state states: Unused, just float around aimlessly and pop up at different places,...
static bool DisasterTick_Helicopter(DisasterVehicle *v)
Helicopter handling.
void ReleaseDisastersTargetingIndustry(IndustryID i)
Marks all disasters targeting this industry in such a way they won't call Industry::Get(v->dest_tile)...
static void Disaster_CoalMine_Init()
Coal mine catastrophe, destroys a stretch of 30 tiles of land in a certain direction.
All disaster vehicles.
DisasterSubType
Different sub types of disaster vehicles.
@ ST_ZEPPELINER_SHADOW
Shadow of the zeppelin.
@ ST_HELICOPTER_SHADOW
Shadow of helicopter.
@ ST_SMALL_UFO_SHADOW
Shadow of small UFO.
@ ST_SMALL_UFO
Small UFO, tries to find a road vehicle to destroy.
@ ST_BIG_UFO
Big UFO, finds a piece of railroad to "park" on.
@ ST_BIG_UFO_SHADOW
Shadow of the big UFO.
@ ST_ZEPPELINER
Zeppelin, crashes at airports.
@ ST_BIG_UFO_DESTROYER_SHADOW
Shadow of the aircraft.
@ ST_BIG_UFO_DESTROYER
Aircraft the will bomb the big UFO.
@ ST_BIG_SUBMARINE
Big submarine, pops up in the oceans but doesn't do anything.
@ ST_SMALL_SUBMARINE
Small submarine, pops up in the oceans but doesn't do anything.
@ ST_HELICOPTER
Helicopter destroying a factory.
@ ST_AIRPLANE
Airplane destroying an oil refinery.
@ ST_HELICOPTER_ROTORS
Rotors of helicopter.
@ ST_AIRPLANE_SHADOW
Shadow of airplane.
EffectVehicle * CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular vehicle.
EffectVehicle * CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type)
Create an effect vehicle above a particular location.
Functions related to effect vehicles.
@ EV_CRASH_SMOKE
Smoke of disasters.
@ EV_EXPLOSION_SMALL
Various explosions.
@ EV_EXPLOSION_LARGE
Various explosions.
Base functions for all Games.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
Base of all industries.
const IndustrySpec * GetIndustrySpec(IndustryType thistype)
Accessor for array _industry_specs.
void ResetIndustryConstructionStage(Tile tile)
Reset the construction stage counter of the industry, as well as the completion bit.
IndustryID GetIndustryIndex(Tile t)
Get the industry ID of the given tile.
IndustryBehaviour
Various industry behaviours mostly to represent original TTD specialities.
@ INDUSTRYBEH_CAN_SUBSIDENCE
can cause a subsidence (coal mine, shaft that collapses)
@ INDUSTRYBEH_AIRPLANE_ATTACKS
can be exploded by a military airplane (oil refinery)
@ INDUSTRYBEH_CHOPPER_ATTACKS
can be exploded by a military helicopter (factory)
TrackStatus GetTileTrackStatus(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
Returns information about trackdirs and signal states.
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Command definitions related to landscape (slopes etc.).
@ Random
Randomise borders.
TileIndex TileAddWrap(TileIndex tile, int addx, int addy)
This function checks if we add addx/addy to tile, if we do wrap around the edges.
Definition map.cpp:94
static debug_inline TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:373
TileIndex RandomTileSeed(uint32_t r)
Get a random tile out of a given seed.
Definition map_func.h:653
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:389
#define RandomTile()
Get a valid random tile.
Definition map_func.h:664
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:570
static debug_inline TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:404
int32_t TileIndexDiff
An offset value between two tiles.
Definition map_type.h:23
constexpr T Delta(const T a, const T b)
Returns the (absolute) difference between two (scalar) variables.
constexpr T Clamp(const T a, const T min, const T max)
Clamp a value between an interval.
Definition math_func.hpp:79
Functions related to news.
@ NT_ACCIDENT
An accident or disaster has occurred.
Definition news_type.h:26
static debug_inline bool IsRailDepot(Tile t)
Is this rail tile a rail depot?
Definition rail_map.h:95
static debug_inline bool IsPlainRailTile(Tile t)
Checks whether the tile is a rail tile or rail tile with signals.
Definition rail_map.h:60
Pseudo random number generator.
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
bool Chance16(const uint32_t a, const uint32_t b, const std::source_location location=std::source_location::current())
Flips a coin with given probability.
Road vehicle states.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
void UpdateSignalsInBuffer()
Update signals in buffer Called from 'outside'.
Definition signal.cpp:576
Functions related to sound.
@ SND_12_EXPLOSION
16 == 0x10 Destruction, crashes, disasters, ...
Definition sound_type.h:63
Base classes/functions for stations.
bool IsAirportTile(Tile t)
Is this tile a station tile and an airport tile?
StationID GetStationIndex(Tile t)
Get StationID from a tile.
Definition station_map.h:28
Definition of base types and functions in a cross-platform compatible way.
void SetDParam(size_t n, uint64_t v)
Set a string parameter v at index n in the global string parameter array.
Definition strings.cpp:104
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
uint64_t flags
stores which blocks on the airport are taken. was 16 bit earlier on, then 32
Class to backup a specific variable and restore it later.
void Restore()
Restore the variable.
static BaseStation * GetByTile(TileIndex tile)
Get the base station belonging to a specific tile.
VehicleType type
Type of vehicle.
SoundSettings sound
sound effect settings
static bool IsHumanID(size_t index)
Is this company a company not controlled by a NoAI program?
bool freeform_edges
allow terraforming the tiles at the map edges
bool disasters
are disasters enabled
Disasters, like submarines, skyrangers and their shadows, belong to this class.
void UpdateDeltaXY() override
Updates the x and y offsets and the size of the sprite used for this vehicle.
SpriteID image_override
Override for the default disaster vehicle sprite.
void UpdatePosition(int x, int y, int z)
Update the position of the vehicle.
DisasterVehicle()
For use by saveload.
VehicleID big_ufo_destroyer_target
The big UFO that this destroyer is supposed to bomb.
bool Tick() override
Calls the tick handler of the vehicle.
uint16_t state
Action stage of the disaster vehicle.
DisasterInitProc * init_proc
The init function for this disaster.
TimerGameCalendar::Year min_year
The first year this disaster will occur.
TimerGameCalendar::Year max_year
The last year this disaster will occur.
ConstructionSettings construction
construction of things in-game
DifficultySettings difficulty
settings related to the difficulty
Position information of a vehicle after it moved.
int y
x and y position of the vehicle after moving
IndustryBehaviour behaviour
How this industry will behave, and how others entities can use it.
Defines the internal data of a functional industry.
Definition industry.h:66
IndustryType type
type of industry.
Definition industry.h:102
Town * town
Nearest town.
Definition industry.h:95
TileArea location
Location of the industry.
Definition industry.h:94
bool TileBelongsToIndustry(TileIndex tile) const
Check if a given tile belongs to this industry.
Definition industry.h:135
Size related data of the map.
Definition map_func.h:206
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:279
static debug_inline uint SizeX()
Get the size of the map along the X.
Definition map_func.h:270
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition map_func.h:306
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition map_func.h:297
VehicleSpriteSeq sprite_seq
Vehicle appearance.
TileIndex tile
The base tile of the area.
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
Tindex index
Index of this pool item.
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static Titem * Get(size_t index)
Returns Titem with given index.
Buses, trucks and trams belong to this class.
Definition roadveh.h:98
uint Crash(bool flooded=false) override
Common code executed for crashed ground vehicles.
uint16_t crashed_ctr
Animation counter when the vehicle has crashed.
Definition roadveh.h:105
VehicleID disaster_vehicle
NOSAVE: Disaster vehicle targetting this vehicle.
Definition roadveh.h:109
bool disaster
Play disaster and accident sounds.
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
Returns an iterable ensemble of all valid stations of type T.
Class defining several overloaded accessors so we don't have to cast vehicle types that often.
DisasterVehicle * Next() const
Get next vehicle in the chain.
static Pool::IterateWrapper< T > Iterate(size_t from=0)
Returns an iterable ensemble of all valid vehicles of type T.
static T * Get(size_t index)
Gets vehicle with given index.
Station data structure.
Airport airport
Tile area the airport covers.
Town data structure.
Definition town.h:52
'Train' is either a loco or a wagon.
Definition train.h:89
void Set(SpriteID sprite)
Assign a single sprite to the sequence.
Vehicle data structure.
int32_t z_pos
z coordinate.
Direction direction
facing
uint8_t x_extent
x-extent of vehicle bounding box
uint8_t subtype
subtype (Filled with values from AircraftSubType/DisasterSubType/EffectVehicleType/GroundVehicleSubty...
uint8_t breakdown_ctr
Counter for managing breakdown events.
uint8_t breakdown_delay
Counter for managing breakdown length.
uint8_t z_extent
z-extent of vehicle bounding box
int8_t y_offs
y offset for vehicle sprite
debug_inline bool IsFrontEngine() const
Check if the vehicle is a front engine.
int8_t x_offs
x offset for vehicle sprite
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
uint8_t y_extent
y-extent of vehicle bounding box
uint8_t vehstatus
Status.
TimerGameCalendar::Date age
Age in calendar days.
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition vehicle.cpp:2937
MutableSpriteCache sprite_cache
Cache of sprites and values related to recalculating them, see MutableSpriteCache.
void UpdatePositionAndViewport()
Update the position of the vehicle, and update the viewport.
Definition vehicle.cpp:1764
uint8_t tick_counter
Increased by one for each tick.
TileIndex tile
Current tile index.
TileIndex dest_tile
Heading for this tile.
void UpdatePosition()
Update the position of the vehicle.
Definition vehicle.cpp:1693
Owner owner
Which company owns the vehicle?
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
static debug_inline TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
static const uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
@ MP_TREES
Tile got trees.
Definition tile_type.h:52
@ MP_CLEAR
A tile without any structures, i.e. grass, rocks, farm fields etc.
Definition tile_type.h:48
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
@ MP_RAILWAY
A railway.
Definition tile_type.h:49
@ MP_INDUSTRY
Part of an industry.
Definition tile_type.h:56
Definition of Interval and OneShot timers.
Definition of the game-economy-timer.
Base of the town class.
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
TrackBits TrackStatusToTrackBits(TrackStatus ts)
Returns the present-track-information of a TrackStatus.
Definition track_func.h:363
TrackBits
Allow incrementing of Track variables.
Definition track_type.h:35
@ TRACK_BIT_ALL
All possible tracks.
Definition track_type.h:50
Base for the train class.
@ TRANSPORT_WATER
Transport over water.
CommandCost EnsureNoVehicleOnGround(TileIndex tile)
Ensure there is no vehicle at the ground at the given position.
Definition vehicle.cpp:546
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1784
@ VS_UNCLICKABLE
Vehicle is not clickable by the user (shadow vehicles).
@ VS_SHADOW
Vehicle is a shadow vehicle.
@ VS_HIDDEN
Vehicle is not visible.
@ VS_CRASHED
Vehicle is crashed.
Functions related to vehicles.
@ VEH_ROAD
Road vehicle type.
uint32_t VehicleID
The type all our vehicle IDs have.
static const VehicleID INVALID_VEHICLE
Constant representing a non-existing vehicle.
Functions related to (drawing on) viewports.
bool IsWaterTile(Tile t)
Is it a water tile with plain water?
Definition water_map.h:190