Fix ICU iterators on leading/trailing whitespace
[openttd/fttd.git] / src / disaster_cmd.cpp
blobb06408cb6c9e45b6ca35a67f0976b117e6d01149
1 /* $Id$ */
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 */
10 /**
11 * @file disaster_cmd.cpp
12 * All disaster/easter egg vehicles are handled here.
13 * The general flow of control for the disaster vehicles is as follows:
14 * <ol>
15 * <li>Initialize the disaster in a disaster specific way (eg start position,
16 * possible target, etc.) Disaster_XXX_Init() function
17 * <li>Add a subtype to a disaster, which is an index into the function array
18 * that handles the vehicle's ticks.
19 * <li>Run the disaster vehicles each tick until their target has been reached,
20 * this happens in the DisasterTick_XXX() functions. In here, a vehicle's
21 * state is kept by v->current_order.dest variable. Each achieved sub-target
22 * will increase this value, and the last one will remove the disaster itself
23 * </ol>
27 #include "stdafx.h"
29 #include "industry.h"
30 #include "station_base.h"
31 #include "command_func.h"
32 #include "news_func.h"
33 #include "town.h"
34 #include "company_func.h"
35 #include "strings_func.h"
36 #include "date_func.h"
37 #include "viewport_func.h"
38 #include "vehicle_func.h"
39 #include "signal_func.h"
40 #include "sound_func.h"
41 #include "effectvehicle_func.h"
42 #include "roadveh.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 "map/water.h"
50 #include "table/strings.h"
52 /** Delay counter for considering the next disaster. */
53 uint16 _disaster_delay;
55 enum DisasterSubType {
56 ST_ZEPPELINER,
57 ST_ZEPPELINER_SHADOW,
58 ST_SMALL_UFO,
59 ST_SMALL_UFO_SHADOW,
60 ST_AIRPLANE,
61 ST_AIRPLANE_SHADOW,
62 ST_HELICOPTER,
63 ST_HELICOPTER_SHADOW,
64 ST_HELICOPTER_ROTORS,
65 ST_BIG_UFO,
66 ST_BIG_UFO_SHADOW,
67 ST_BIG_UFO_DESTROYER,
68 ST_BIG_UFO_DESTROYER_SHADOW,
69 ST_SMALL_SUBMARINE,
70 ST_BIG_SUBMARINE,
73 static const uint INITIAL_DISASTER_VEHICLE_ZPOS = 135; ///< Initial Z position of flying disaster vehicles.
75 static void DisasterClearSquare(TileIndex tile)
77 if (EnsureNoVehicleOnGround(tile).Failed()) return;
79 if (IsHouseTile(tile)) {
80 Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
81 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
82 cur_company.Restore();
83 } else switch (GetTileType(tile)) {
84 case TT_RAILWAY:
85 if (Company::IsHumanID(GetTileOwner(tile))) {
86 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
87 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
88 cur_company.Restore();
90 /* update signals in buffer */
91 UpdateSignalsInBuffer();
93 break;
95 case TT_GROUND:
96 DoClearSquare(tile);
97 break;
99 default:
100 break;
104 static const SpriteID _disaster_images_1[] = {SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP};
105 static 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};
106 static 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};
107 static 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};
108 static 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};
109 static 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};
110 static 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};
111 static 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};
112 static 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};
114 static const SpriteID * const _disaster_images[] = {
115 _disaster_images_1, _disaster_images_1, ///< zeppeliner and zeppeliner shadow
116 _disaster_images_2, _disaster_images_2, ///< small ufo and small ufo shadow
117 _disaster_images_3, _disaster_images_3, ///< combat aircraft and shadow
118 _disaster_images_8, _disaster_images_8, _disaster_images_9, ///< combat helicopter, shadow and rotor
119 _disaster_images_6, _disaster_images_6, ///< big ufo and shadow
120 _disaster_images_7, _disaster_images_7, ///< skyranger and shadow
121 _disaster_images_4, _disaster_images_5, ///< small and big submarine sprites
124 static void DisasterVehicleUpdateImage(DisasterVehicle *v)
126 SpriteID img = v->image_override;
127 if (img == 0) img = _disaster_images[v->subtype][v->direction];
128 v->cur_image = img;
132 * Initialize a disaster vehicle. These vehicles are of type VEH_DISASTER, are unclickable
133 * and owned by nobody
135 static void InitializeDisasterVehicle(DisasterVehicle *v, int x, int y, int z, Direction direction, byte subtype)
137 v->x_pos = x;
138 v->y_pos = y;
139 v->z_pos = z;
140 v->tile = TileVirtXY(x, y);
141 v->direction = direction;
142 v->subtype = subtype;
143 v->UpdateDeltaXY(INVALID_DIR);
144 v->owner = OWNER_NONE;
145 v->vehstatus = VS_UNCLICKABLE;
146 v->image_override = 0;
147 v->current_order.Free();
149 DisasterVehicleUpdateImage(v);
150 VehicleUpdatePositionAndViewport(v);
153 static void SetDisasterVehiclePos(DisasterVehicle *v, int x, int y, int z)
155 v->x_pos = x;
156 v->y_pos = y;
157 v->z_pos = z;
158 v->tile = TileVirtXY(x, y);
160 DisasterVehicleUpdateImage(v);
161 VehicleUpdatePositionAndViewport(v);
163 DisasterVehicle *u = v->Next();
164 if (u != NULL) {
165 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
166 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
168 u->x_pos = x;
169 u->y_pos = y - 1 - (max(z - GetSlopePixelZ(safe_x, safe_y), 0) >> 3);
170 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
171 u->z_pos = GetSlopePixelZ(safe_x, safe_y);
172 u->direction = v->direction;
174 DisasterVehicleUpdateImage(u);
175 VehicleUpdatePositionAndViewport(u);
177 if ((u = u->Next()) != NULL) {
178 u->x_pos = x;
179 u->y_pos = y;
180 u->z_pos = z + 5;
181 VehicleUpdatePositionAndViewport(u);
186 static void SetDisasterVehiclePosAutoZ(DisasterVehicle *v, int z)
188 VehiclePos gp = GetNewVehiclePos(v);
190 SetDisasterVehiclePos(v, gp.x, gp.y, z);
193 static void SetDisasterVehiclePosAuto(DisasterVehicle *v)
195 SetDisasterVehiclePosAutoZ(v, v->z_pos);
199 * Zeppeliner handling, v->current_order.dest states:
200 * 0: Zeppeliner initialization has found a small airport, go there and crash
201 * 1: Create crash and animate falling down for extra dramatic effect
202 * 2: Create more smoke and leave debris on ground
203 * 2: Clear the runway after some time and remove crashed zeppeliner
204 * If not airport was found, only state 0 is reached until zeppeliner leaves map
206 static bool DisasterTick_Zeppeliner(DisasterVehicle *v)
208 v->tick_counter++;
210 if (v->current_order.GetDestination() < 2) {
211 if (HasBit(v->tick_counter, 0)) return true;
213 SetDisasterVehiclePosAuto(v);
215 if (v->current_order.GetDestination() == 1) {
216 if (++v->age == 38) {
217 v->current_order.SetDestination(2);
218 v->age = 0;
221 if (GB(v->tick_counter, 0, 3) == 0) CreateEffectVehicleRel(v, 0, -17, 2, EV_CRASH_SMOKE);
223 } else if (v->current_order.GetDestination() == 0) {
224 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
225 v->current_order.SetDestination(1);
226 v->age = 0;
228 SetDParam(0, GetStationIndex(v->tile));
229 AddVehicleNewsItem(STR_NEWS_DISASTER_ZEPPELIN, NT_ACCIDENT, v->index); // Delete the news, when the zeppelin is gone
230 AI::NewEvent(GetTileOwner(v->tile), new ScriptEventDisasterZeppelinerCrashed(GetStationIndex(v->tile)));
234 if (v->y_pos >= (int)((MapSizeY() + 9) * TILE_SIZE - 1)) {
235 delete v;
236 return false;
239 return true;
242 if (v->current_order.GetDestination() > 2) {
243 if (++v->age <= 13320) return true;
245 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
246 Station *st = Station::GetByTile(v->tile);
247 CLRBITS(st->airport.flags, RUNWAY_IN_block);
248 AI::NewEvent(GetTileOwner(v->tile), new ScriptEventDisasterZeppelinerCleared(st->index));
251 SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos);
252 delete v;
253 return false;
256 int x = v->x_pos;
257 int y = v->y_pos;
258 int z = GetSlopePixelZ(x, y);
259 if (z < v->z_pos) z = v->z_pos - 1;
260 SetDisasterVehiclePos(v, x, y, z);
262 if (++v->age == 1) {
263 CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
264 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
265 v->image_override = SPR_BLIMP_CRASHING;
266 } else if (v->age == 70) {
267 v->image_override = SPR_BLIMP_CRASHED;
268 } else if (v->age <= 300) {
269 if (GB(v->tick_counter, 0, 3) == 0) {
270 uint32 r = Random();
272 CreateEffectVehicleRel(v,
273 GB(r, 0, 4) - 7,
274 GB(r, 4, 4) - 7,
275 GB(r, 8, 3) + 5,
276 EV_EXPLOSION_SMALL);
278 } else if (v->age == 350) {
279 v->current_order.SetDestination(3);
280 v->age = 0;
283 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
284 SETBITS(Station::GetByTile(v->tile)->airport.flags, RUNWAY_IN_block);
287 return true;
291 * (Small) Ufo handling, v->current_order.dest states:
292 * 0: Fly around to the middle of the map, then randomly, after a while target a road vehicle
293 * 1: Home in on a road vehicle and crash it >:)
294 * If not road vehicle was found, only state 0 is used and Ufo disappears after a while
296 static bool DisasterTick_Ufo(DisasterVehicle *v)
298 v->image_override = (HasBit(++v->tick_counter, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
300 if (v->current_order.GetDestination() == 0) {
301 /* Fly around randomly */
302 int x = TileX(v->dest_tile) * TILE_SIZE;
303 int y = TileY(v->dest_tile) * TILE_SIZE;
304 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= (int)TILE_SIZE) {
305 v->direction = GetDirectionTowards(v, x, y);
306 SetDisasterVehiclePosAuto(v);
307 return true;
309 if (++v->age < 6) {
310 v->dest_tile = RandomTile();
311 return true;
313 v->current_order.SetDestination(1);
315 uint n = 0; // Total number of targetable road vehicles.
316 RoadVehicle *u;
317 FOR_ALL_ROADVEHICLES(u) {
318 if (u->IsFrontEngine()) n++;
321 if (n == 0) {
322 /* If there are no targetable road vehicles, destroy the UFO. */
323 delete v;
324 return false;
327 n = RandomRange(n); // Choose one of them.
328 FOR_ALL_ROADVEHICLES(u) {
329 /* Find (n+1)-th road vehicle. */
330 if (u->IsFrontEngine() && (n-- == 0)) break;
333 /* Target it. */
334 v->dest_tile = u->index;
335 v->age = 0;
336 return true;
337 } else {
338 /* Target a vehicle */
339 RoadVehicle *u = RoadVehicle::Get(v->dest_tile);
340 assert(u != NULL && u->type == VEH_ROAD && u->IsFrontEngine());
342 uint dist = Delta(v->x_pos, u->x_pos) + Delta(v->y_pos, u->y_pos);
344 if (dist < TILE_SIZE && !(u->vehstatus & VS_HIDDEN) && u->breakdown_ctr == 0) {
345 u->breakdown_ctr = 3;
346 u->breakdown_delay = 140;
349 v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
351 int z = v->z_pos;
352 if (dist <= TILE_SIZE && z > u->z_pos) z--;
353 SetDisasterVehiclePosAutoZ(v, z);
355 if (z <= u->z_pos && (u->vehstatus & VS_HIDDEN) == 0) {
356 v->age++;
357 if (u->crashed_ctr == 0) {
358 u->Crash();
360 AddVehicleNewsItem(STR_NEWS_DISASTER_SMALL_UFO, NT_ACCIDENT, u->index); // delete the news, when the roadvehicle is gone
362 AI::NewEvent(u->owner, new ScriptEventVehicleCrashed(u->index, u->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO));
363 Game::NewEvent(new ScriptEventVehicleCrashed(u->index, u->tile, ScriptEventVehicleCrashed::CRASH_RV_UFO));
367 /* Destroy? */
368 if (v->age > 50) {
369 CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
370 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
371 delete v;
372 return false;
376 return true;
379 static void DestructIndustry(Industry *i)
381 for (TileIndex tile = 0; tile != MapSize(); tile++) {
382 if (i->TileBelongsToIndustry(tile)) {
383 ResetIndustryConstructionStage(tile);
384 MarkTileDirtyByTile(tile);
390 * Aircraft handling, v->current_order.dest states:
391 * 0: Fly towards the targeted industry
392 * 1: If within 15 tiles, fire away rockets and destroy industry
393 * 2: Industry explosions
394 * 3: Fly out of the map
395 * If the industry was removed in the meantime just fly to the end of the map.
396 * @param v The disaster vehicle.
397 * @param image_override The image at the time the aircraft is firing.
398 * @param leave_at_top True iff the vehicle leaves the map at the north side.
399 * @param news_message The string that's used as news message.
400 * @param industry_flag Only attack industries that have this flag set.
402 static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16 image_override, bool leave_at_top, StringID news_message, IndustryBehaviour industry_flag)
404 v->tick_counter++;
405 v->image_override = (v->current_order.GetDestination() == 1 && HasBit(v->tick_counter, 2)) ? image_override : 0;
407 SetDisasterVehiclePosAuto(v);
409 if (leave_at_top ? (v->x_pos < (-10 * (int)TILE_SIZE)) : (v->x_pos > (int)(MapSizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1)) {
410 delete v;
411 return false;
414 if (v->current_order.GetDestination() == 2) {
415 if (GB(v->tick_counter, 0, 2) == 0) {
416 Industry *i = Industry::Get(v->dest_tile); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
417 int x = TileX(i->location.tile) * TILE_SIZE;
418 int y = TileY(i->location.tile) * TILE_SIZE;
419 uint32 r = Random();
421 CreateEffectVehicleAbove(
422 GB(r, 0, 6) + x,
423 GB(r, 6, 6) + y,
424 GB(r, 12, 4),
425 EV_EXPLOSION_SMALL);
427 if (++v->age >= 55) v->current_order.SetDestination(3);
429 } else if (v->current_order.GetDestination() == 1) {
430 if (++v->age == 112) {
431 v->current_order.SetDestination(2);
432 v->age = 0;
434 Industry *i = Industry::Get(v->dest_tile); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
435 DestructIndustry(i);
437 SetDParam(0, i->town->index);
438 AddIndustryNewsItem(news_message, NT_ACCIDENT, i->index); // delete the news, when the industry closes
439 if (_settings_client.sound.disaster) SndPlayTileFx(SND_12_EXPLOSION, i->location.tile);
441 } else if (v->current_order.GetDestination() == 0) {
442 int x = v->x_pos + ((leave_at_top ? -15 : 15) * TILE_SIZE);
443 int y = v->y_pos;
445 if ((uint)x > MapMaxX() * TILE_SIZE - 1) return true;
447 TileIndex tile = TileVirtXY(x, y);
448 if (!IsIndustryTile(tile)) return true;
450 IndustryID ind = GetIndustryIndex(tile);
451 v->dest_tile = ind;
453 if (GetIndustrySpec(Industry::Get(ind)->type)->behaviour & industry_flag) {
454 v->current_order.SetDestination(1);
455 v->age = 0;
459 return true;
462 /** Airplane handling. */
463 static bool DisasterTick_Airplane(DisasterVehicle *v)
465 return DisasterTick_Aircraft(v, SPR_F_15_FIRING, true, STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY, INDUSTRYBEH_AIRPLANE_ATTACKS);
468 /** Helicopter handling. */
469 static bool DisasterTick_Helicopter(DisasterVehicle *v)
471 return DisasterTick_Aircraft(v, SPR_AH_64A_FIRING, false, STR_NEWS_DISASTER_HELICOPTER_FACTORY, INDUSTRYBEH_CHOPPER_ATTACKS);
474 /** Helicopter rotor blades; keep these spinning */
475 static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
477 v->tick_counter++;
478 if (HasBit(v->tick_counter, 0)) return true;
480 if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
482 VehicleUpdatePositionAndViewport(v);
484 return true;
488 * (Big) Ufo handling, v->current_order.dest states:
489 * 0: Fly around to the middle of the map, then randomly for a while and home in on a piece of rail
490 * 1: Land there and breakdown all trains in a radius of 12 tiles; and now we wait...
491 * because as soon as the Ufo lands, a fighter jet, a Skyranger, is called to clear up the mess
493 static bool DisasterTick_Big_Ufo(DisasterVehicle *v)
495 v->tick_counter++;
497 if (v->current_order.GetDestination() == 1) {
498 int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
499 int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
500 if (Delta(v->x_pos, x) + Delta(v->y_pos, y) >= 8) {
501 v->direction = GetDirectionTowards(v, x, y);
503 SetDisasterVehiclePosAuto(v);
504 return true;
507 if (!IsValidTile(v->dest_tile)) {
508 /* Make sure we don't land outside the map. */
509 delete v;
510 return false;
513 int z = GetSlopePixelZ(v->x_pos, v->y_pos);
514 if (z < v->z_pos) {
515 SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
516 return true;
519 v->current_order.SetDestination(2);
521 Vehicle *target;
522 FOR_ALL_VEHICLES(target) {
523 if (target->IsGroundVehicle()) {
524 if (Delta(target->x_pos, v->x_pos) + Delta(target->y_pos, v->y_pos) <= 12 * (int)TILE_SIZE) {
525 target->breakdown_ctr = 5;
526 target->breakdown_delay = 0xF0;
531 Town *t = ClosestTownFromTile(v->dest_tile);
532 SetDParam(0, t->index);
533 AddTileNewsItem(STR_NEWS_DISASTER_BIG_UFO, NT_ACCIDENT, v->tile);
535 if (!Vehicle::CanAllocateItem(2)) {
536 delete v;
537 return false;
539 DisasterVehicle *u = new DisasterVehicle();
541 InitializeDisasterVehicle(u, -6 * (int)TILE_SIZE, v->y_pos, INITIAL_DISASTER_VEHICLE_ZPOS, DIR_SW, ST_BIG_UFO_DESTROYER);
542 u->big_ufo_destroyer_target = v->index;
544 DisasterVehicle *w = new DisasterVehicle();
546 u->SetNext(w);
547 InitializeDisasterVehicle(w, -6 * (int)TILE_SIZE, v->y_pos, 0, DIR_SW, ST_BIG_UFO_DESTROYER_SHADOW);
548 w->vehstatus |= VS_SHADOW;
549 } else if (v->current_order.GetDestination() == 0) {
550 int x = TileX(v->dest_tile) * TILE_SIZE;
551 int y = TileY(v->dest_tile) * TILE_SIZE;
552 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= (int)TILE_SIZE) {
553 v->direction = GetDirectionTowards(v, x, y);
554 SetDisasterVehiclePosAuto(v);
555 return true;
558 if (++v->age < 6) {
559 v->dest_tile = RandomTile();
560 return true;
562 v->current_order.SetDestination(1);
564 TileIndex tile_org = RandomTile();
565 TileIndex tile = tile_org;
566 do {
567 if (IsNormalRailTile(tile) &&
568 Company::IsHumanID(GetTileOwner(tile))) {
569 break;
571 tile = TILE_MASK(tile + 1);
572 } while (tile != tile_org);
573 v->dest_tile = tile;
574 v->age = 0;
577 return true;
581 * Skyranger destroying (Big) Ufo handling, v->current_order.dest states:
582 * 0: Home in on landed Ufo and shoot it down
584 static bool DisasterTick_Big_Ufo_Destroyer(DisasterVehicle *v)
586 v->tick_counter++;
588 SetDisasterVehiclePosAuto(v);
590 if (v->x_pos > (int)(MapSizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1) {
591 delete v;
592 return false;
595 if (v->current_order.GetDestination() == 0) {
596 Vehicle *u = Vehicle::Get(v->big_ufo_destroyer_target);
597 if (Delta(v->x_pos, u->x_pos) > (int)TILE_SIZE) return true;
598 v->current_order.SetDestination(1);
600 CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
601 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, u);
603 delete u;
605 for (int i = 0; i != 80; i++) {
606 uint32 r = Random();
607 CreateEffectVehicleAbove(
608 GB(r, 0, 6) + v->x_pos - 32,
609 GB(r, 5, 6) + v->y_pos - 32,
611 EV_EXPLOSION_SMALL);
614 for (int dy = -3; dy < 3; dy++) {
615 for (int dx = -3; dx < 3; dx++) {
616 TileIndex tile = TileAddWrap(v->tile, dx, dy);
617 if (tile != INVALID_TILE) DisasterClearSquare(tile);
622 return true;
626 * Submarine, v->current_order.dest states:
627 * Unused, just float around aimlessly and pop up at different places, turning around
629 static bool DisasterTick_Submarine(DisasterVehicle *v)
631 v->tick_counter++;
633 if (++v->age > 8880) {
634 delete v;
635 return false;
638 if (!HasBit(v->tick_counter, 0)) return true;
640 TileIndex tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
641 if (IsValidTile(tile)) {
642 TrackBits trackbits = TrackdirBitsToTrackBits(GetTileWaterwayStatus(tile));
643 if (trackbits == TRACK_BIT_ALL && !Chance16(1, 90)) {
644 SetDisasterVehiclePosAuto(v);
645 return true;
649 v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
651 return true;
655 static bool DisasterTick_NULL(DisasterVehicle *v)
657 return true;
660 typedef bool DisasterVehicleTickProc(DisasterVehicle *v);
662 static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
663 DisasterTick_Zeppeliner, DisasterTick_NULL,
664 DisasterTick_Ufo, DisasterTick_NULL,
665 DisasterTick_Airplane, DisasterTick_NULL,
666 DisasterTick_Helicopter, DisasterTick_NULL, DisasterTick_Helicopter_Rotors,
667 DisasterTick_Big_Ufo, DisasterTick_NULL, DisasterTick_Big_Ufo_Destroyer,
668 DisasterTick_NULL,
669 DisasterTick_Submarine,
670 DisasterTick_Submarine,
674 bool DisasterVehicle::Tick()
676 return _disastervehicle_tick_procs[this->subtype](this);
679 typedef void DisasterInitProc();
683 * Zeppeliner which crashes on a small airport if one found,
684 * otherwise crashes on a random tile
686 static void Disaster_Zeppeliner_Init()
688 if (!Vehicle::CanAllocateItem(2)) return;
690 /* Pick a random place, unless we find a small airport */
691 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
693 Station *st;
694 FOR_ALL_STATIONS(st) {
695 if (st->airport.tile != INVALID_TILE && (st->airport.type == AT_SMALL || st->airport.type == AT_LARGE)) {
696 x = (TileX(st->airport.tile) + 2) * TILE_SIZE;
697 break;
701 DisasterVehicle *v = new DisasterVehicle();
702 InitializeDisasterVehicle(v, x, 0, INITIAL_DISASTER_VEHICLE_ZPOS, DIR_SE, ST_ZEPPELINER);
704 /* Allocate shadow */
705 DisasterVehicle *u = new DisasterVehicle();
706 v->SetNext(u);
707 InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_ZEPPELINER_SHADOW);
708 u->vehstatus |= VS_SHADOW;
713 * Ufo which flies around aimlessly from the middle of the map a bit
714 * until it locates a road vehicle which it targets and then destroys
716 static void Disaster_Small_Ufo_Init()
718 if (!Vehicle::CanAllocateItem(2)) return;
720 DisasterVehicle *v = new DisasterVehicle();
721 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
723 InitializeDisasterVehicle(v, x, 0, INITIAL_DISASTER_VEHICLE_ZPOS, DIR_SE, ST_SMALL_UFO);
724 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
725 v->age = 0;
727 /* Allocate shadow */
728 DisasterVehicle *u = new DisasterVehicle();
729 v->SetNext(u);
730 InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_SMALL_UFO_SHADOW);
731 u->vehstatus |= VS_SHADOW;
735 /* Combat airplane which destroys an oil refinery */
736 static void Disaster_Airplane_Init()
738 if (!Vehicle::CanAllocateItem(2)) return;
740 Industry *i, *found = NULL;
742 FOR_ALL_INDUSTRIES(i) {
743 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) &&
744 (found == NULL || Chance16(1, 2))) {
745 found = i;
749 if (found == NULL) return;
751 DisasterVehicle *v = new DisasterVehicle();
753 /* Start from the bottom (south side) of the map */
754 int x = (MapSizeX() + 9) * TILE_SIZE - 1;
755 int y = TileY(found->location.tile) * TILE_SIZE + 37;
757 InitializeDisasterVehicle(v, x, y, INITIAL_DISASTER_VEHICLE_ZPOS, DIR_NE, ST_AIRPLANE);
759 DisasterVehicle *u = new DisasterVehicle();
760 v->SetNext(u);
761 InitializeDisasterVehicle(u, x, y, 0, DIR_SE, ST_AIRPLANE_SHADOW);
762 u->vehstatus |= VS_SHADOW;
766 /** Combat helicopter that destroys a factory */
767 static void Disaster_Helicopter_Init()
769 if (!Vehicle::CanAllocateItem(3)) return;
771 Industry *i, *found = NULL;
773 FOR_ALL_INDUSTRIES(i) {
774 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) &&
775 (found == NULL || Chance16(1, 2))) {
776 found = i;
780 if (found == NULL) return;
782 DisasterVehicle *v = new DisasterVehicle();
784 int x = -16 * (int)TILE_SIZE;
785 int y = TileY(found->location.tile) * TILE_SIZE + 37;
787 InitializeDisasterVehicle(v, x, y, INITIAL_DISASTER_VEHICLE_ZPOS, DIR_SW, ST_HELICOPTER);
789 DisasterVehicle *u = new DisasterVehicle();
790 v->SetNext(u);
791 InitializeDisasterVehicle(u, x, y, 0, DIR_SW, ST_HELICOPTER_SHADOW);
792 u->vehstatus |= VS_SHADOW;
794 DisasterVehicle *w = new DisasterVehicle();
795 u->SetNext(w);
796 InitializeDisasterVehicle(w, x, y, 140, DIR_SW, ST_HELICOPTER_ROTORS);
800 /* Big Ufo which lands on a piece of rail and will consequently be shot
801 * down by a combat airplane, destroying the surroundings */
802 static void Disaster_Big_Ufo_Init()
804 if (!Vehicle::CanAllocateItem(2)) return;
806 DisasterVehicle *v = new DisasterVehicle();
807 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
808 int y = MapMaxX() * TILE_SIZE - 1;
810 InitializeDisasterVehicle(v, x, y, INITIAL_DISASTER_VEHICLE_ZPOS, DIR_NW, ST_BIG_UFO);
811 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
812 v->age = 0;
814 /* Allocate shadow */
815 DisasterVehicle *u = new DisasterVehicle();
816 v->SetNext(u);
817 InitializeDisasterVehicle(u, x, y, 0, DIR_NW, ST_BIG_UFO_SHADOW);
818 u->vehstatus |= VS_SHADOW;
822 static void Disaster_Submarine_Init(DisasterSubType subtype)
824 if (!Vehicle::CanAllocateItem()) return;
826 int y;
827 Direction dir;
828 uint32 r = Random();
829 int x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
831 if (HasBit(r, 31)) {
832 y = MapMaxY() * TILE_SIZE - TILE_SIZE / 2 - 1;
833 dir = DIR_NW;
834 } else {
835 y = TILE_SIZE / 2;
836 if (_settings_game.construction.freeform_edges) y += TILE_SIZE;
837 dir = DIR_SE;
839 if (!IsPlainWaterTile(TileVirtXY(x, y))) return;
841 DisasterVehicle *v = new DisasterVehicle();
842 InitializeDisasterVehicle(v, x, y, 0, dir, subtype);
843 v->age = 0;
846 /* Curious submarine #1, just floats around */
847 static void Disaster_Small_Submarine_Init()
849 Disaster_Submarine_Init(ST_SMALL_SUBMARINE);
853 /* Curious submarine #2, just floats around */
854 static void Disaster_Big_Submarine_Init()
856 Disaster_Submarine_Init(ST_BIG_SUBMARINE);
861 * Coal mine catastrophe, destroys a stretch of 30 tiles of
862 * land in a certain direction
864 static void Disaster_CoalMine_Init()
866 int index = GB(Random(), 0, 4);
867 uint m;
869 for (m = 0; m < 15; m++) {
870 const Industry *i;
872 FOR_ALL_INDUSTRIES(i) {
873 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CAN_SUBSIDENCE) && --index < 0) {
874 SetDParam(0, i->town->index);
875 AddTileNewsItem(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE, NT_ACCIDENT, i->location.tile + TileDiffXY(1, 1)); // keep the news, even when the mine closes
878 TileIndex tile = i->location.tile;
879 TileIndexDiff step = TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2));
881 for (uint n = 0; n < 30; n++) {
882 DisasterClearSquare(tile);
883 tile += step;
884 if (!IsValidTile(tile)) break;
887 return;
893 struct Disaster {
894 DisasterInitProc *init_proc; ///< The init function for this disaster.
895 Year min_year; ///< The first year this disaster will occur.
896 Year max_year; ///< The last year this disaster will occur.
899 static const Disaster _disasters[] = {
900 {Disaster_Zeppeliner_Init, 1930, 1955}, // zeppeliner
901 {Disaster_Small_Ufo_Init, 1940, 1970}, // ufo (small)
902 {Disaster_Airplane_Init, 1960, 1990}, // airplane
903 {Disaster_Helicopter_Init, 1970, 2000}, // helicopter
904 {Disaster_Big_Ufo_Init, 2000, 2100}, // ufo (big)
905 {Disaster_Small_Submarine_Init, 1940, 1965}, // submarine (small)
906 {Disaster_Big_Submarine_Init, 1975, 2010}, // submarine (big)
907 {Disaster_CoalMine_Init, 1950, 1985}, // coalmine
910 static void DoDisaster()
912 byte buf[lengthof(_disasters)];
914 byte j = 0;
915 for (size_t i = 0; i != lengthof(_disasters); i++) {
916 if (_cur_year >= _disasters[i].min_year && _cur_year < _disasters[i].max_year) buf[j++] = (byte)i;
919 if (j == 0) return;
921 _disasters[buf[RandomRange(j)]].init_proc();
925 static void ResetDisasterDelay()
927 _disaster_delay = GB(Random(), 0, 9) + 730;
930 void DisasterDailyLoop()
932 if (--_disaster_delay != 0) return;
934 ResetDisasterDelay();
936 if (_settings_game.difficulty.disasters != 0) DoDisaster();
939 void StartupDisasters()
941 ResetDisasterDelay();
945 * Marks all disasters targeting this industry in such a way
946 * they won't call Industry::Get(v->dest_tile) on invalid industry anymore.
947 * @param i deleted industry
949 void ReleaseDisastersTargetingIndustry(IndustryID i)
951 DisasterVehicle *v;
952 FOR_ALL_DISASTERVEHICLES(v) {
953 /* primary disaster vehicles that have chosen target */
954 if (v->subtype == ST_AIRPLANE || v->subtype == ST_HELICOPTER) {
955 /* if it has chosen target, and it is this industry (yes, dest_tile is IndustryID here), set order to "leaving map peacefully" */
956 if (v->current_order.GetDestination() > 0 && v->dest_tile == i) v->current_order.SetDestination(3);
962 * Notify disasters that we are about to delete a vehicle. So make them head elsewhere.
963 * @param vehicle deleted vehicle
965 void ReleaseDisastersTargetingVehicle(VehicleID vehicle)
967 DisasterVehicle *v;
968 FOR_ALL_DISASTERVEHICLES(v) {
969 /* primary disaster vehicles that have chosen target */
970 if (v->subtype == ST_SMALL_UFO) {
971 if (v->current_order.GetDestination() != 0 && v->dest_tile == vehicle) {
972 /* Revert to target-searching */
973 v->current_order.SetDestination(0);
974 v->dest_tile = RandomTile();
975 v->z_pos = INITIAL_DISASTER_VEHICLE_ZPOS;
976 v->age = 0;
982 void DisasterVehicle::UpdateDeltaXY(Direction direction)
984 this->x_offs = -1;
985 this->y_offs = -1;
986 this->x_extent = 2;
987 this->y_extent = 2;
988 this->z_extent = 5;