OpenTTD Source 20260218-master-g2123fca5ea
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
24
25
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)) {
65 if (Company::IsHumanID(GetTileOwner(tile)) && !IsRailDepot(tile)) {
67 Command<Commands::LandscapeClear>::Do(DoCommandFlag::Execute, tile);
68 cur_company.Restore();
69
70 /* update signals in buffer */
72 }
73 break;
74
75 case TileType::House: {
77 Command<Commands::LandscapeClear>::Do(DoCommandFlag::Execute, tile);
78 cur_company.Restore();
79 break;
80 }
81
82 case TileType::Trees:
83 case TileType::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
130{
132
133 this->x_pos = x;
134 this->y_pos = y;
135 switch (subtype) {
136 case ST_ZEPPELINER:
137 case ST_SMALL_UFO:
138 case ST_AIRPLANE:
139 case ST_HELICOPTER:
140 case ST_BIG_UFO:
142 GetAircraftFlightLevelBounds(this, &this->z_pos, nullptr);
143 break;
144
146 GetAircraftFlightLevelBounds(this, &this->z_pos, nullptr);
147 this->z_pos += ROTOR_Z_OFFSET;
148 break;
149
151 case ST_BIG_SUBMARINE:
152 this->z_pos = 0;
153 break;
154
161 this->z_pos = 0;
162 this->vehstatus.Set(VehState::Shadow);
163 break;
164 }
165
166 this->direction = direction;
167 this->tile = TileVirtXY(x, y);
168 this->subtype = subtype;
169 this->UpdateDeltaXY();
170 this->owner = OWNER_NONE;
171 this->image_override = 0;
172 this->state = 0;
173
174 this->UpdateImage();
176}
177
184void DisasterVehicle::UpdatePosition(int x, int y, int z)
185{
186 this->x_pos = x;
187 this->y_pos = y;
188 this->z_pos = z;
189 this->tile = TileVirtXY(x, y);
190
191 this->UpdateImage();
193
194 DisasterVehicle *u = this->Next();
195 if (u != nullptr) {
196 int safe_x = Clamp(x, 0, Map::MaxX() * TILE_SIZE);
197 int safe_y = Clamp(y - 1, 0, Map::MaxY() * TILE_SIZE);
198
199 u->x_pos = x;
200 u->y_pos = y - 1 - (std::max(z - GetSlopePixelZ(safe_x, safe_y), 0) >> 3);
201 safe_y = Clamp(u->y_pos, 0, Map::MaxY() * TILE_SIZE);
202 u->z_pos = GetSlopePixelZ(safe_x, safe_y);
203 u->direction = this->direction;
204
205 u->UpdateImage();
207
208 if ((u = u->Next()) != nullptr) {
209 u->x_pos = x;
210 u->y_pos = y;
211 u->z_pos = z + ROTOR_Z_OFFSET;
213 }
214 }
215}
216
227{
228 v->tick_counter++;
229
230 if (v->state < 2) {
231 if (HasBit(v->tick_counter, 0)) return true;
232
234
235 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
236
237 if (v->state == 1) {
238 if (++v->age == 38) {
239 v->state = 2;
241 }
242
243 if (GB(v->tick_counter, 0, 3) == 0) CreateEffectVehicleRel(v, 0, -17, 2, EV_CRASH_SMOKE);
244
245 } else if (v->state == 0) {
246 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
247 v->state = 1;
249
250 AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_ZEPPELIN, GetStationIndex(v->tile)), NewsType::Accident, v->tile);
251 AI::NewEvent(GetTileOwner(v->tile), new ScriptEventDisasterZeppelinerCrashed(GetStationIndex(v->tile)));
252 }
253 }
254
255 if (v->y_pos >= (int)((Map::SizeY() + 9) * TILE_SIZE - 1)) {
256 delete v;
257 return false;
258 }
259
260 return true;
261 }
262
263 if (v->state > 2) {
264 if (++v->age <= 13320) return true;
265
266 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
269 AI::NewEvent(GetTileOwner(v->tile), new ScriptEventDisasterZeppelinerCleared(st->index));
270 }
271
272 v->UpdatePosition(v->x_pos, v->y_pos, GetAircraftFlightLevel(v));
273 delete v;
274 return false;
275 }
276
277 int x = v->x_pos;
278 int y = v->y_pos;
279 int z = GetSlopePixelZ(x, y);
280 if (z < v->z_pos) z = v->z_pos - 1;
281 v->UpdatePosition(x, y, z);
282
283 if (++v->age == 1) {
285 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
286 v->image_override = SPR_BLIMP_CRASHING;
287 } else if (v->age == 70) {
288 v->image_override = SPR_BLIMP_CRASHED;
289 } else if (v->age <= 300) {
290 if (GB(v->tick_counter, 0, 3) == 0) {
291 uint32_t r = Random();
292
294 GB(r, 0, 4) - 7,
295 GB(r, 4, 4) - 7,
296 GB(r, 8, 3) + 5,
298 }
299 } else if (v->age == 350) {
300 v->state = 3;
302 }
303
304 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
306 }
307
308 return true;
309}
310
320{
321 ufo->image_override = (HasBit(++ufo->tick_counter, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
322
323 if (ufo->state == 0) {
324 /* Fly around randomly */
325 int x = TileX(ufo->dest_tile) * TILE_SIZE;
326 int y = TileY(ufo->dest_tile) * TILE_SIZE;
327 if (Delta(x, ufo->x_pos) + Delta(y, ufo->y_pos) >= (int)TILE_SIZE) {
328 ufo->direction = GetDirectionTowards(ufo, x, y);
330 ufo->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(ufo));
331 return true;
332 }
333 if (++ufo->age < 6) {
334 ufo->dest_tile = RandomTile();
335 return true;
336 }
337 ufo->state = 1;
338
339 uint n = 0; // Total number of targetable road vehicles.
340 for (const Company *c : Company::Iterate()) {
341 n += c->group_all[VEH_ROAD].num_vehicle;
342 }
343
344 if (n == 0) {
345 /* If there are no targetable road vehicles, destroy the UFO. */
346 delete ufo;
347 return false;
348 }
349
350 n = RandomRange(n); // Choose one of them.
351 for (RoadVehicle *u : RoadVehicle::Iterate()) {
352 /* Find (n+1)-th road vehicle. */
353 if (u->IsFrontEngine() && (n-- == 0)) {
354 if (u->crashed_ctr != 0 || u->disaster_vehicle != VehicleID::Invalid()) {
355 /* Targetted vehicle is crashed or already a target, destroy the UFO. */
356 delete ufo;
357 return false;
358 }
359 /* Target it. */
360 ufo->dest_tile = TileIndex{u->index.base()};
362 u->disaster_vehicle = ufo->index;
363 break;
364 }
365 }
366
367 return true;
368 } else {
369 /* Target a vehicle */
370 RoadVehicle *target = RoadVehicle::Get(ufo->dest_tile.base());
371 assert(target != nullptr && target->type == VEH_ROAD && target->IsFrontEngine());
372
373 uint dist = Delta(ufo->x_pos, target->x_pos) + Delta(ufo->y_pos, target->y_pos);
374
375 if (dist < TILE_SIZE && !target->vehstatus.Test(VehState::Hidden) && target->breakdown_ctr == 0) {
376 target->breakdown_ctr = 3;
377 target->breakdown_delay = 140;
378 }
379
380 ufo->direction = GetDirectionTowards(ufo, target->x_pos, target->y_pos);
382
383 int z = ufo->z_pos;
384 if (dist <= TILE_SIZE && z > target->z_pos) z--;
385 ufo->UpdatePosition(gp.x, gp.y, z);
386
387 /* If the vehicle is hidden in a depot or similar treat it as having "escaped" being crashed to avoid the Ufo looping forever,
388 * but we'll still explode the surrounding area ;) */
389 if (z <= target->z_pos) {
390 ufo->age++;
391 if (!target->vehstatus.Test(VehState::Hidden) && target->crashed_ctr == 0) {
392 uint victims = target->Crash();
393 target->disaster_vehicle = VehicleID::Invalid();
394
395 AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_SMALL_UFO), NewsType::Accident, target->tile);
396
397 AI::NewEvent(target->owner, new ScriptEventVehicleCrashed(target->index, target->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO, victims, target->owner));
398 Game::NewEvent(new ScriptEventVehicleCrashed(target->index, target->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO, victims, target->owner));
399 }
400 }
401
402 /* Destroy? */
403 if (ufo->age > 50) {
405 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, ufo);
406 delete ufo;
407 return false;
408 }
409 }
410
411 return true;
412}
413
414static void DestructIndustry(Industry *i)
415{
416 for (const auto tile : Map::Iterate()) {
417 if (i->TileBelongsToIndustry(tile)) {
420 }
421 }
422}
423
438static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16_t image_override, bool leave_at_top, StringID news_message, IndustryBehaviour behaviour)
439{
440 v->tick_counter++;
441 v->image_override = (v->state == 1 && HasBit(v->tick_counter, 2)) ? image_override : 0;
442
444 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
445
446 if ((leave_at_top && gp.x < (-10 * (int)TILE_SIZE)) || (!leave_at_top && gp.x > (int)(Map::SizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1)) {
447 delete v;
448 return false;
449 }
450
451 if (v->state == 2) {
452 if (GB(v->tick_counter, 0, 2) == 0) {
453 Industry *i = Industry::Get(v->dest_tile.base()); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
454 int x = TileX(i->location.tile) * TILE_SIZE;
455 int y = TileY(i->location.tile) * TILE_SIZE;
456 uint32_t r = Random();
457
459 GB(r, 0, 6) + x,
460 GB(r, 6, 6) + y,
461 GB(r, 12, 4),
463
464 if (++v->age >= 55) v->state = 3;
465 }
466 } else if (v->state == 1) {
467 if (++v->age == 112) {
468 v->state = 2;
470
471 Industry *i = Industry::Get(v->dest_tile.base()); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
472 DestructIndustry(i);
473
474 AddIndustryNewsItem(GetEncodedString(news_message, i->town->index), NewsType::Accident, i->index);
475 if (_settings_client.sound.disaster) SndPlayTileFx(SND_12_EXPLOSION, i->location.tile);
476 }
477 } else if (v->state == 0) {
478 int x = v->x_pos + ((leave_at_top ? -15 : 15) * TILE_SIZE);
479 int y = v->y_pos;
480
481 if ((uint)x > Map::MaxX() * TILE_SIZE - 1) return true;
482
483 TileIndex tile = TileVirtXY(x, y);
484 if (!IsTileType(tile, TileType::Industry)) return true;
485
486 IndustryID ind = GetIndustryIndex(tile);
487 v->dest_tile = TileIndex{ind.base()};
488
489 if (GetIndustrySpec(Industry::Get(ind)->type)->behaviour.Test(behaviour)) {
490 v->state = 1;
492 }
493 }
494
495 return true;
496}
497
500{
501 return DisasterTick_Aircraft(v, SPR_F_15_FIRING, true, STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY, IndustryBehaviour::AirplaneAttacks);
502}
503
506{
507 return DisasterTick_Aircraft(v, SPR_AH_64A_FIRING, false, STR_NEWS_DISASTER_HELICOPTER_FACTORY, IndustryBehaviour::ChopperAttacks);
508}
509
512{
513 v->tick_counter++;
514 if (HasBit(v->tick_counter, 0)) return true;
515
516 SpriteID &cur_image = v->sprite_cache.sprite_seq.seq[0].sprite;
517 if (++cur_image > SPR_ROTOR_MOVING_3) cur_image = SPR_ROTOR_MOVING_1;
518
520
521 return true;
522}
523
532{
533 v->tick_counter++;
534
535 if (v->state == 1) {
536 int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
537 int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
538 if (Delta(v->x_pos, x) + Delta(v->y_pos, y) >= 8) {
539 v->direction = GetDirectionTowards(v, x, y);
540
542 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
543 return true;
544 }
545
546 if (!IsValidTile(v->dest_tile)) {
547 /* Make sure we don't land outside the map. */
548 delete v;
549 return false;
550 }
551
552 int z = GetSlopePixelZ(v->x_pos, v->y_pos);
553 if (z < v->z_pos) {
554 v->UpdatePosition(v->x_pos, v->y_pos, v->z_pos - 1);
555 return true;
556 }
557
558 v->state = 2;
559
560 for (Vehicle *target : Vehicle::Iterate()) {
561 if (target->IsGroundVehicle()) {
562 if (Delta(target->x_pos, v->x_pos) + Delta(target->y_pos, v->y_pos) <= 12 * (int)TILE_SIZE) {
563 target->breakdown_ctr = 5;
564 target->breakdown_delay = 0xF0;
565 }
566 }
567 }
568
569 Town *t = ClosestTownFromTile(v->dest_tile, UINT_MAX);
570 AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_BIG_UFO, t->index), NewsType::Accident, v->tile);
571
572 if (!Vehicle::CanAllocateItem(2)) {
573 delete v;
574 return false;
575 }
578 u->SetNext(w);
579 } else if (v->state == 0) {
580 int x = TileX(v->dest_tile) * TILE_SIZE;
581 int y = TileY(v->dest_tile) * TILE_SIZE;
582 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= (int)TILE_SIZE) {
583 v->direction = GetDirectionTowards(v, x, y);
585 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
586 return true;
587 }
588
589 if (++v->age < 6) {
590 v->dest_tile = RandomTile();
591 return true;
592 }
593 v->state = 1;
594
595 const auto is_valid_target = [](const Train *t) {
596 return t->IsFrontEngine() // Only the engines
597 && Company::IsHumanID(t->owner) // Don't break AIs
598 && IsPlainRailTile(t->tile) // No tunnels
599 && !t->vehstatus.Test(VehState::Crashed); // Not crashed
600 };
601
602 uint n = 0; // Total number of targetable trains.
603 for (const Train *t : Train::Iterate()) {
604 if (is_valid_target(t)) n++;
605 }
606
607 if (n == 0) {
608 /* If there are no targetable trains, destroy the UFO. */
609 delete v;
610 return false;
611 }
612
613 n = RandomRange(n); // Choose one of them.
614 for (const Train *t : Train::Iterate()) {
615 /* Find (n+1)-th train. */
616 if (is_valid_target(t) && (n-- == 0)) {
617 /* Target it. */
618 v->dest_tile = t->tile;
620 break;
621 }
622 }
623 }
624
625 return true;
626}
627
634{
635 v->tick_counter++;
636
638 v->UpdatePosition(gp.x, gp.y, GetAircraftFlightLevel(v));
639
640 if (gp.x > (int)(Map::SizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1) {
641 delete v;
642 return false;
643 }
644
645 if (v->state == 0) {
647 if (Delta(v->x_pos, u->x_pos) > (int)TILE_SIZE) return true;
648 v->state = 1;
649
651 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, u);
652
653 delete u;
654
655 for (int i = 0; i != 80; i++) {
656 uint32_t r = Random();
658 GB(r, 0, 6) + v->x_pos - 32,
659 GB(r, 5, 6) + v->y_pos - 32,
660 0,
662 }
663
664 for (int dy = -3; dy < 3; dy++) {
665 for (int dx = -3; dx < 3; dx++) {
666 TileIndex tile = TileAddWrap(v->tile, dx, dy);
667 if (tile != INVALID_TILE) DisasterClearSquare(tile);
668 }
669 }
670 }
671
672 return true;
673}
674
681{
682 v->tick_counter++;
683
684 if (++v->age > 8880) {
685 delete v;
686 return false;
687 }
688
689 if (!HasBit(v->tick_counter, 0)) return true;
690
692 if (IsValidTile(tile)) {
694 if (trackbits == TRACK_BIT_ALL && !Chance16(1, 90)) {
696 v->UpdatePosition(gp.x, gp.y, v->z_pos);
697 return true;
698 }
699 }
700
701 v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
702
703 return true;
704}
705
706
708static bool DisasterTick_NULL([[maybe_unused]] DisasterVehicle *v)
709{
710 return true;
711}
712
719
720static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
729};
730
731
733{
734 return _disastervehicle_tick_procs[this->subtype](this);
735}
736
737typedef void DisasterInitProc();
738
739
745{
746 if (!Vehicle::CanAllocateItem(2)) return;
747
748 /* Pick a random place, unless we find an airport */
749 int x = TileX(RandomTile()) * TILE_SIZE + TILE_SIZE / 2;
750
751 for (const Station *st : Station::Iterate()) {
752 if (st->airport.tile != INVALID_TILE && (st->airport.type == AT_SMALL || st->airport.type == AT_LARGE)) {
753 x = (TileX(st->airport.tile) + 2) * TILE_SIZE;
754 break;
755 }
756 }
757
759 /* Allocate shadow */
761 v->SetNext(u);
762}
763
764
770{
771 if (!Vehicle::CanAllocateItem(2)) return;
772
773 int x = TileX(RandomTile()) * TILE_SIZE + TILE_SIZE / 2;
775 v->dest_tile = TileXY(Map::SizeX() / 2, Map::SizeY() / 2);
776
777 /* Allocate shadow */
779 v->SetNext(u);
780}
781
782
783/* Combat airplane which destroys an oil refinery */
784static void Disaster_Airplane_Init()
785{
786 if (!Vehicle::CanAllocateItem(2)) return;
787
788 Industry *found = nullptr;
789
790 for (Industry *i : Industry::Iterate()) {
792 (found == nullptr || Chance16(1, 2))) {
793 found = i;
794 }
795 }
796
797 if (found == nullptr) return;
798
799 /* Start from the bottom (south side) of the map */
800 int x = (Map::SizeX() + 9) * TILE_SIZE - 1;
801 int y = TileY(found->location.tile) * TILE_SIZE + 37;
802
805 v->SetNext(u);
806}
807
808
811{
812 if (!Vehicle::CanAllocateItem(3)) return;
813
814 Industry *found = nullptr;
815
816 for (Industry *i : Industry::Iterate()) {
818 (found == nullptr || Chance16(1, 2))) {
819 found = i;
820 }
821 }
822
823 if (found == nullptr) return;
824
825 int x = -16 * (int)TILE_SIZE;
826 int y = TileY(found->location.tile) * TILE_SIZE + 37;
827
830 v->SetNext(u);
831
833 u->SetNext(w);
834}
835
836
837/* Big Ufo which lands on a piece of rail and will consequently be shot
838 * down by a combat airplane, destroying the surroundings */
839static void Disaster_Big_Ufo_Init()
840{
841 if (!Vehicle::CanAllocateItem(2)) return;
842
843 int x = TileX(RandomTile()) * TILE_SIZE + TILE_SIZE / 2;
844 int y = Map::MaxX() * TILE_SIZE - 1;
845
847 v->dest_tile = TileXY(Map::SizeX() / 2, Map::SizeY() / 2);
848
849 /* Allocate shadow */
851 v->SetNext(u);
852}
853
854
860{
861 if (!Vehicle::CanAllocateItem()) return;
862
863 int y;
864 Direction dir;
865 uint32_t r = Random();
866 int x = TileX(RandomTileSeed(r)) * TILE_SIZE + TILE_SIZE / 2;
867
868 if (HasBit(r, 31)) {
869 y = Map::MaxY() * TILE_SIZE - TILE_SIZE / 2 - 1;
870 dir = DIR_NW;
871 } else {
872 y = TILE_SIZE / 2;
873 if (_settings_game.construction.freeform_edges) y += TILE_SIZE;
874 dir = DIR_SE;
875 }
876 if (!IsWaterTile(TileVirtXY(x, y))) return;
877
878 DisasterVehicle::Create(x, y, dir, subtype);
879}
880
881/* Curious submarine #1, just floats around */
882static void Disaster_Small_Submarine_Init()
883{
885}
886
887
888/* Curious submarine #2, just floats around */
889static void Disaster_Big_Submarine_Init()
890{
892}
893
894
900{
901 int index = GB(Random(), 0, 4);
902
903 for (uint m = 0; m < 15; m++) {
904 for (const Industry *i : Industry::Iterate()) {
906 AddTileNewsItem(GetEncodedString(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE, i->town->index), NewsType::Accident, i->location.tile + TileDiffXY(1, 1)); // keep the news, even when the mine closes
907
908 {
909 TileIndex tile = i->location.tile;
910 TileIndexDiff step = TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2));
911
912 for (uint n = 0; n < 30; n++) {
913 DisasterClearSquare(tile);
914 tile += step;
915 if (!IsValidTile(tile)) break;
916 }
917 }
918 return;
919 }
920 }
921 }
922}
923
926 DisasterInitProc *init_proc;
927 TimerGameCalendar::Year min_year;
928 TimerGameCalendar::Year max_year;
929};
930
932static const DisasterCreation _disasters[] = {
933 {Disaster_Zeppeliner_Init, TimerGameCalendar::Year{1930}, TimerGameCalendar::Year{1955}}, // zeppeliner
934 {Disaster_Small_Ufo_Init, TimerGameCalendar::Year{1940}, TimerGameCalendar::Year{1970}}, // ufo {small}
935 {Disaster_Airplane_Init, TimerGameCalendar::Year{1960}, TimerGameCalendar::Year{1990}}, // airplane
936 {Disaster_Helicopter_Init, TimerGameCalendar::Year{1970}, TimerGameCalendar::Year{2000}}, // helicopter
937 {Disaster_Big_Ufo_Init, TimerGameCalendar::Year{2000}, TimerGameCalendar::Year{2100}}, // ufo {big}
938 {Disaster_Small_Submarine_Init, TimerGameCalendar::Year{1940}, TimerGameCalendar::Year{1965}}, // submarine {small}
939 {Disaster_Big_Submarine_Init, TimerGameCalendar::Year{1975}, TimerGameCalendar::Year{2010}}, // submarine {big}
940 {Disaster_CoalMine_Init, TimerGameCalendar::Year{1950}, TimerGameCalendar::Year{1985}}, // coalmine
941};
942
944static void DoDisaster()
945{
946 std::vector<DisasterInitProc *> available_disasters;
947
948 for (auto &disaster : _disasters) {
949 if (TimerGameCalendar::year >= disaster.min_year && TimerGameCalendar::year < disaster.max_year) {
950 available_disasters.push_back(disaster.init_proc);
951 }
952 }
953
954 if (available_disasters.empty()) return;
955
956 available_disasters[RandomRange(static_cast<uint32_t>(available_disasters.size()))]();
957}
958
961{
962 _disaster_delay = GB(Random(), 0, 9) + 730;
963}
964
966static const IntervalTimer<TimerGameEconomy> _economy_disaster_daily({TimerGameEconomy::Trigger::Day, TimerGameEconomy::Priority::Disaster}, [](auto)
967{
968 if (--_disaster_delay != 0) return;
969
971
972 if (_settings_game.difficulty.disasters != 0) DoDisaster();
973});
974
977{
979}
980
987{
989 /* primary disaster vehicles that have chosen target */
990 if (v->subtype == ST_AIRPLANE || v->subtype == ST_HELICOPTER) {
991 /* if it has chosen target, and it is this industry (yes, dest_tile is IndustryID here), set order to "leaving map peacefully" */
992 if (v->state > 0 && v->dest_tile == i.base()) v->state = 3;
993 }
994 }
995}
996
1002{
1004 if (v == nullptr) return;
1005
1006 /* primary disaster vehicles that have chosen target */
1007 assert(v->subtype == ST_SMALL_UFO);
1008 assert(v->state != 0);
1009
1010 /* Revert to target-searching */
1011 v->state = 0;
1012 v->dest_tile = RandomTile();
1013 GetAircraftFlightLevelBounds(v, &v->z_pos, nullptr);
1015}
1016
1018{
1019 this->bounds = {{-1, -1, 0}, {2, 2, 5}, {}};
1020}
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:49
@ Zeppeliner
Block for the zeppeliner disaster vehicle.
Definition airport.h:129
@ RunwayIn
Runway used for landing (metropolitan / international / intercontinental airports).
Definition airport.h:100
@ 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.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:235
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Timpl & Reset()
Reset all bits.
static void NewEvent(class ScriptEvent *event)
Queue a new event for the 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
Functions related to commands.
@ Execute
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.
static constexpr Owner OWNER_NONE
The tile has no ownership.
static constexpr Owner 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 const DisasterCreation _disasters[]
Table per disaster subtype when to create disasters, and how to create them.
static void ResetDisasterDelay()
Resets the introduction of the next disaster.
static void DoDisaster()
Create a random disaster, if there is one available.
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 an airport,...
static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
Helicopter rotor blades; keep these spinning.
static const IntervalTimer< TimerGameEconomy > _economy_disaster_daily({TimerGameEconomy::Trigger::Day, TimerGameEconomy::Priority::Disaster}, [](auto) { if(--_disaster_delay !=0) return;ResetDisasterDelay();if(_settings_game.difficulty.disasters !=0) DoDisaster();})
Daily trigger to check whether to add a new disaster.
static bool DisasterTick_Airplane(DisasterVehicle *v)
Airplane handling.
static void Disaster_Zeppeliner_Init()
Zeppeliner which crashes on an airport if one found, otherwise crashes on a random tile.
static void Disaster_Submarine_Init(DisasterSubType subtype)
Initialise a submarine.
uint16_t _disaster_delay
Delay counter for considering the next disaster.
bool(DisasterVehicle *v) DisasterVehicleTickProc
Perform any actions for a given vehicle.
void ReleaseDisasterVehicle(VehicleID vehicle)
Notify disasters that we are about to delete a vehicle.
static bool DisasterTick_NULL(DisasterVehicle *v)
No-op vehicle tick.
static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16_t image_override, bool leave_at_top, StringID news_message, IndustryBehaviour behaviour)
Aircraft handling, v->state states: 0: Fly towards the targeted industry 1: If within 15 tiles,...
static bool DisasterTick_Ufo(DisasterVehicle *ufo)
(Small) Ufo handling, v->state states: 0: Fly around to the middle of the map, then randomly,...
void StartupDisasters()
Starts up disasters.
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.
@ AirplaneAttacks
can be exploded by a military airplane (oil refinery)
@ CanSubsidence
can cause a subsidence (coal mine, shaft that collapses)
@ ChopperAttacks
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.).
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:109
static TileIndex TileVirtXY(uint x, uint y)
Get a tile from the virtual XY-coordinate.
Definition map_func.h:409
TileIndex RandomTileSeed(uint32_t r)
Get a random tile out of a given seed.
Definition map_func.h:646
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:394
static TileIndex TileXY(uint x, uint y)
Returns the TileIndex of a coordinate.
Definition map_func.h:378
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:430
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:420
#define RandomTile()
Get a valid random tile.
Definition map_func.h:657
TileIndexDiff TileOffsByDiagDir(DiagDirection dir)
Convert a DiagDirection to a TileIndexDiff.
Definition map_func.h:575
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.
@ Accident
An accident or disaster has occurred.
Definition news_type.h:32
static bool IsRailDepot(Tile t)
Is this rail tile a rail depot?
Definition rail_map.h:95
static 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:61
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
void UpdateSignalsInBuffer()
Update signals in buffer Called from 'outside'.
Definition signal.cpp:573
Functions related to sound.
@ SND_12_EXPLOSION
16 == 0x10 Destruction, crashes, disasters, ...
Definition sound_type.h:64
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.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
AirportBlocks blocks
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.
static bool IsHumanID(auto index)
Is this company a company not controlled by a NoAI program?
Initialisation function and time period to run the different disasters.
TimerGameCalendar::Year min_year
The first year this disaster will occur.
DisasterInitProc * init_proc
The init function for this disaster.
TimerGameCalendar::Year max_year
The last year this disaster will occur.
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.
DisasterVehicle(VehicleID index)
For use by saveload.
void UpdatePosition(int x, int y, int z)
Update the position of the vehicle.
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.
Position information of a vehicle after it moved.
int y
x and y position of the vehicle after moving
IndustryBehaviours behaviour
How this industry will behave, and how others entities can use it.
Defines the internal data of a functional industry.
Definition industry.h:62
IndustryType type
type of industry.
Definition industry.h:115
Town * town
Nearest town.
Definition industry.h:107
TileArea location
Location of the industry.
Definition industry.h:106
bool TileBelongsToIndustry(TileIndex tile) const
Check if a given tile belongs to this industry.
Definition industry.h:148
static uint SizeX()
Get the size of the map along the X.
Definition map_func.h:264
static uint SizeY()
Get the size of the map along the Y.
Definition map_func.h:273
static IterateWrapper Iterate()
Returns an iterable ensemble of all Tiles.
Definition map_func.h:368
static uint MaxY()
Gets the maximum Y coordinate within the map, including TileType::Void.
Definition map_func.h:300
static uint MaxX()
Gets the maximum X coordinate within the map, including TileType::Void.
Definition map_func.h:291
VehicleSpriteSeq sprite_seq
Vehicle appearance.
TileIndex tile
The base tile of the area.
static Pool::IterateWrapper< Company > Iterate(size_t from=0)
static Vehicle * Get(auto index)
static bool CanAllocateItem(size_t n=1)
static T * Create(Targs &&... args)
static Vehicle * GetIfValid(auto 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
static Pool::IterateWrapper< Station > Iterate(size_t from=0)
SpecializedVehicle< DisasterVehicle, Type > SpecializedVehicleBase
Station data structure.
Airport airport
Tile area the airport covers.
Town data structure.
Definition town.h:63
'Train' is either a loco or a wagon.
Definition train.h:91
Vehicle data structure.
int32_t z_pos
z coordinate.
Direction direction
facing
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.
VehStates vehstatus
Status.
int32_t y_pos
y coordinate.
int32_t x_pos
x coordinate.
SpriteBounds bounds
Bounding box of vehicle.
bool IsFrontEngine() const
Check if the vehicle is a front engine.
TimerGameCalendar::Date age
Age in calendar days.
void SetNext(Vehicle *next)
Set the next vehicle of this vehicle.
Definition vehicle.cpp:2941
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:1757
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:1674
Owner owner
Which company owns the vehicle?
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
static TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
@ Industry
Part of an industry.
Definition tile_type.h:57
@ Railway
A tile with railway.
Definition tile_type.h:50
@ Trees
Tile with one or more trees.
Definition tile_type.h:53
@ House
A house by a town.
Definition tile_type.h:52
@ Clear
A tile without any structures, i.e. grass, rocks, farm fields etc.
Definition tile_type.h:49
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:528
GetNewVehiclePosResult GetNewVehiclePos(const Vehicle *v)
Get position information of a vehicle when moving one pixel in the direction it is facing.
Definition vehicle.cpp:1777
@ Unclickable
Vehicle is not clickable by the user (shadow vehicles).
@ Crashed
Vehicle is crashed.
@ Shadow
Vehicle is a shadow vehicle.
@ Hidden
Vehicle is not visible.
Functions related to vehicles.
PoolID< uint32_t, struct VehicleIDTag, 0xFF000, 0xFFFFF > VehicleID
The type all our vehicle IDs have.
@ VEH_ROAD
Road vehicle type.
Functions related to (drawing on) viewports.
bool IsWaterTile(Tile t)
Is it a water tile with plain water?
Definition water_map.h:192