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/>.
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:
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
30 #include "station_base.h"
31 #include "command_func.h"
32 #include "news_func.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 "sound_func.h"
40 #include "effectvehicle_func.h"
43 #include "game/game.hpp"
44 #include "company_base.h"
45 #include "core/random_func.hpp"
46 #include "core/backup_type.hpp"
48 #include "table/strings.h"
50 /** Delay counter for considering the next disaster. */
51 uint16 _disaster_delay
;
53 enum DisasterSubType
{
66 ST_BIG_UFO_DESTROYER_SHADOW
,
71 static const uint INITIAL_DISASTER_VEHICLE_ZPOS
= 135; ///< Initial Z position of flying disaster vehicles.
73 static void DisasterClearSquare(TileIndex tile
)
75 if (EnsureNoVehicleOnGround(tile
).Failed()) return;
77 switch (GetTileType(tile
)) {
79 if (Company::IsHumanID(GetTileOwner(tile
)) && !IsRailDepot(tile
)) {
80 Backup
<CompanyByte
> cur_company(_current_company
, OWNER_WATER
, FILE_LINE
);
81 DoCommand(tile
, 0, 0, DC_EXEC
, CMD_LANDSCAPE_CLEAR
);
82 cur_company
.Restore();
84 /* update signals in buffer */
85 UpdateSignalsInBuffer();
90 Backup
<CompanyByte
> cur_company(_current_company
, OWNER_NONE
, FILE_LINE
);
91 DoCommand(tile
, 0, 0, DC_EXEC
, CMD_LANDSCAPE_CLEAR
);
92 cur_company
.Restore();
106 static const SpriteID _disaster_images_1
[] = {SPR_BLIMP
, SPR_BLIMP
, SPR_BLIMP
, SPR_BLIMP
, SPR_BLIMP
, SPR_BLIMP
, SPR_BLIMP
, SPR_BLIMP
};
107 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
};
108 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
};
109 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
};
110 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
};
111 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
};
112 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
};
113 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
};
114 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
};
116 static const SpriteID
* const _disaster_images
[] = {
117 _disaster_images_1
, _disaster_images_1
, ///< zeppeliner and zeppeliner shadow
118 _disaster_images_2
, _disaster_images_2
, ///< small ufo and small ufo shadow
119 _disaster_images_3
, _disaster_images_3
, ///< combat aircraft and shadow
120 _disaster_images_8
, _disaster_images_8
, _disaster_images_9
, ///< combat helicopter, shadow and rotor
121 _disaster_images_6
, _disaster_images_6
, ///< big ufo and shadow
122 _disaster_images_7
, _disaster_images_7
, ///< skyranger and shadow
123 _disaster_images_4
, _disaster_images_5
, ///< small and big submarine sprites
126 static void DisasterVehicleUpdateImage(DisasterVehicle
*v
)
128 SpriteID img
= v
->image_override
;
129 if (img
== 0) img
= _disaster_images
[v
->subtype
][v
->direction
];
134 * Initialize a disaster vehicle. These vehicles are of type VEH_DISASTER, are unclickable
135 * and owned by nobody
137 static void InitializeDisasterVehicle(DisasterVehicle
*v
, int x
, int y
, int z
, Direction direction
, byte subtype
)
142 v
->tile
= TileVirtXY(x
, y
);
143 v
->direction
= direction
;
144 v
->subtype
= subtype
;
145 v
->UpdateDeltaXY(INVALID_DIR
);
146 v
->owner
= OWNER_NONE
;
147 v
->vehstatus
= VS_UNCLICKABLE
;
148 v
->image_override
= 0;
149 v
->current_order
.Free();
151 DisasterVehicleUpdateImage(v
);
152 VehicleUpdatePositionAndViewport(v
);
155 static void SetDisasterVehiclePos(DisasterVehicle
*v
, int x
, int y
, int z
)
160 v
->tile
= TileVirtXY(x
, y
);
162 DisasterVehicleUpdateImage(v
);
163 VehicleUpdatePositionAndViewport(v
);
165 DisasterVehicle
*u
= v
->Next();
167 int safe_x
= Clamp(x
, 0, MapMaxX() * TILE_SIZE
);
168 int safe_y
= Clamp(y
- 1, 0, MapMaxY() * TILE_SIZE
);
171 u
->y_pos
= y
- 1 - (max(z
- GetSlopePixelZ(safe_x
, safe_y
), 0) >> 3);
172 safe_y
= Clamp(u
->y_pos
, 0, MapMaxY() * TILE_SIZE
);
173 u
->z_pos
= GetSlopePixelZ(safe_x
, safe_y
);
174 u
->direction
= v
->direction
;
176 DisasterVehicleUpdateImage(u
);
177 VehicleUpdatePositionAndViewport(u
);
179 if ((u
= u
->Next()) != NULL
) {
183 VehicleUpdatePositionAndViewport(u
);
189 * Zeppeliner handling, v->current_order.dest states:
190 * 0: Zeppeliner initialization has found a small airport, go there and crash
191 * 1: Create crash and animate falling down for extra dramatic effect
192 * 2: Create more smoke and leave debris on ground
193 * 2: Clear the runway after some time and remove crashed zeppeliner
194 * If not airport was found, only state 0 is reached until zeppeliner leaves map
196 static bool DisasterTick_Zeppeliner(DisasterVehicle
*v
)
200 if (v
->current_order
.GetDestination() < 2) {
201 if (HasBit(v
->tick_counter
, 0)) return true;
203 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
205 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, v
->z_pos
);
207 if (v
->current_order
.GetDestination() == 1) {
208 if (++v
->age
== 38) {
209 v
->current_order
.SetDestination(2);
213 if (GB(v
->tick_counter
, 0, 3) == 0) CreateEffectVehicleRel(v
, 0, -17, 2, EV_CRASH_SMOKE
);
215 } else if (v
->current_order
.GetDestination() == 0) {
216 if (IsValidTile(v
->tile
) && IsAirportTile(v
->tile
)) {
217 v
->current_order
.SetDestination(1);
220 SetDParam(0, GetStationIndex(v
->tile
));
221 AddVehicleNewsItem(STR_NEWS_DISASTER_ZEPPELIN
, NT_ACCIDENT
, v
->index
); // Delete the news, when the zeppelin is gone
222 AI::NewEvent(GetTileOwner(v
->tile
), new ScriptEventDisasterZeppelinerCrashed(GetStationIndex(v
->tile
)));
226 if (v
->y_pos
>= (int)((MapSizeY() + 9) * TILE_SIZE
- 1)) {
234 if (v
->current_order
.GetDestination() > 2) {
235 if (++v
->age
<= 13320) return true;
237 if (IsValidTile(v
->tile
) && IsAirportTile(v
->tile
)) {
238 Station
*st
= Station::GetByTile(v
->tile
);
239 CLRBITS(st
->airport
.flags
, RUNWAY_IN_block
);
240 AI::NewEvent(GetTileOwner(v
->tile
), new ScriptEventDisasterZeppelinerCleared(st
->index
));
243 SetDisasterVehiclePos(v
, v
->x_pos
, v
->y_pos
, v
->z_pos
);
250 int z
= GetSlopePixelZ(x
, y
);
251 if (z
< v
->z_pos
) z
= v
->z_pos
- 1;
252 SetDisasterVehiclePos(v
, x
, y
, z
);
255 CreateEffectVehicleRel(v
, 0, 7, 8, EV_EXPLOSION_LARGE
);
256 if (_settings_client
.sound
.disaster
) SndPlayVehicleFx(SND_12_EXPLOSION
, v
);
257 v
->image_override
= SPR_BLIMP_CRASHING
;
258 } else if (v
->age
== 70) {
259 v
->image_override
= SPR_BLIMP_CRASHED
;
260 } else if (v
->age
<= 300) {
261 if (GB(v
->tick_counter
, 0, 3) == 0) {
264 CreateEffectVehicleRel(v
,
270 } else if (v
->age
== 350) {
271 v
->current_order
.SetDestination(3);
275 if (IsValidTile(v
->tile
) && IsAirportTile(v
->tile
)) {
276 SETBITS(Station::GetByTile(v
->tile
)->airport
.flags
, RUNWAY_IN_block
);
283 * (Small) Ufo handling, v->current_order.dest states:
284 * 0: Fly around to the middle of the map, then randomly, after a while target a road vehicle
285 * 1: Home in on a road vehicle and crash it >:)
286 * If not road vehicle was found, only state 0 is used and Ufo disappears after a while
288 static bool DisasterTick_Ufo(DisasterVehicle
*v
)
290 v
->image_override
= (HasBit(++v
->tick_counter
, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER
: SPR_UFO_SMALL_SCOUT
;
292 if (v
->current_order
.GetDestination() == 0) {
293 /* Fly around randomly */
294 int x
= TileX(v
->dest_tile
) * TILE_SIZE
;
295 int y
= TileY(v
->dest_tile
) * TILE_SIZE
;
296 if (Delta(x
, v
->x_pos
) + Delta(y
, v
->y_pos
) >= (int)TILE_SIZE
) {
297 v
->direction
= GetDirectionTowards(v
, x
, y
);
298 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
299 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, v
->z_pos
);
303 v
->dest_tile
= RandomTile();
306 v
->current_order
.SetDestination(1);
308 uint n
= 0; // Total number of targetable road vehicles.
310 FOR_ALL_ROADVEHICLES(u
) {
311 if (u
->IsFrontEngine()) n
++;
315 /* If there are no targetable road vehicles, destroy the UFO. */
320 n
= RandomRange(n
); // Choose one of them.
321 FOR_ALL_ROADVEHICLES(u
) {
322 /* Find (n+1)-th road vehicle. */
323 if (u
->IsFrontEngine() && (n
-- == 0)) break;
327 v
->dest_tile
= u
->index
;
331 /* Target a vehicle */
332 RoadVehicle
*u
= RoadVehicle::Get(v
->dest_tile
);
333 assert(u
!= NULL
&& u
->type
== VEH_ROAD
&& u
->IsFrontEngine());
335 uint dist
= Delta(v
->x_pos
, u
->x_pos
) + Delta(v
->y_pos
, u
->y_pos
);
337 if (dist
< TILE_SIZE
&& !(u
->vehstatus
& VS_HIDDEN
) && u
->breakdown_ctr
== 0) {
338 u
->breakdown_ctr
= 3;
339 u
->breakdown_delay
= 140;
342 v
->direction
= GetDirectionTowards(v
, u
->x_pos
, u
->y_pos
);
343 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
346 if (dist
<= TILE_SIZE
&& z
> u
->z_pos
) z
--;
347 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, z
);
349 if (z
<= u
->z_pos
&& (u
->vehstatus
& VS_HIDDEN
) == 0) {
351 if (u
->crashed_ctr
== 0) {
354 AddVehicleNewsItem(STR_NEWS_DISASTER_SMALL_UFO
, NT_ACCIDENT
, u
->index
); // delete the news, when the roadvehicle is gone
356 AI::NewEvent(u
->owner
, new ScriptEventVehicleCrashed(u
->index
, u
->tile
, ScriptEventVehicleCrashed::CRASH_RV_UFO
));
357 Game::NewEvent(new ScriptEventVehicleCrashed(u
->index
, u
->tile
, ScriptEventVehicleCrashed::CRASH_RV_UFO
));
363 CreateEffectVehicleRel(v
, 0, 7, 8, EV_EXPLOSION_LARGE
);
364 if (_settings_client
.sound
.disaster
) SndPlayVehicleFx(SND_12_EXPLOSION
, v
);
373 static void DestructIndustry(Industry
*i
)
375 for (TileIndex tile
= 0; tile
!= MapSize(); tile
++) {
376 if (i
->TileBelongsToIndustry(tile
)) {
377 ResetIndustryConstructionStage(tile
);
378 MarkTileDirtyByTile(tile
);
384 * Aircraft handling, v->current_order.dest states:
385 * 0: Fly towards the targeted industry
386 * 1: If within 15 tiles, fire away rockets and destroy industry
387 * 2: Industry explosions
388 * 3: Fly out of the map
389 * If the industry was removed in the meantime just fly to the end of the map.
390 * @param v The disaster vehicle.
391 * @param image_override The image at the time the aircraft is firing.
392 * @param leave_at_top True iff the vehicle leaves the map at the north side.
393 * @param news_message The string that's used as news message.
394 * @param industry_flag Only attack industries that have this flag set.
396 static bool DisasterTick_Aircraft(DisasterVehicle
*v
, uint16 image_override
, bool leave_at_top
, StringID news_message
, IndustryBehaviour industry_flag
)
399 v
->image_override
= (v
->current_order
.GetDestination() == 1 && HasBit(v
->tick_counter
, 2)) ? image_override
: 0;
401 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
402 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, v
->z_pos
);
404 if ((leave_at_top
&& gp
.x
< (-10 * (int)TILE_SIZE
)) || (!leave_at_top
&& gp
.x
> (int)(MapSizeX() * TILE_SIZE
+ 9 * TILE_SIZE
) - 1)) {
409 if (v
->current_order
.GetDestination() == 2) {
410 if (GB(v
->tick_counter
, 0, 2) == 0) {
411 Industry
*i
= Industry::Get(v
->dest_tile
); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
412 int x
= TileX(i
->location
.tile
) * TILE_SIZE
;
413 int y
= TileY(i
->location
.tile
) * TILE_SIZE
;
416 CreateEffectVehicleAbove(
422 if (++v
->age
>= 55) v
->current_order
.SetDestination(3);
424 } else if (v
->current_order
.GetDestination() == 1) {
425 if (++v
->age
== 112) {
426 v
->current_order
.SetDestination(2);
429 Industry
*i
= Industry::Get(v
->dest_tile
); // Industry destructor calls ReleaseDisastersTargetingIndustry, so this is valid
432 SetDParam(0, i
->town
->index
);
433 AddIndustryNewsItem(news_message
, NT_ACCIDENT
, i
->index
); // delete the news, when the industry closes
434 if (_settings_client
.sound
.disaster
) SndPlayTileFx(SND_12_EXPLOSION
, i
->location
.tile
);
436 } else if (v
->current_order
.GetDestination() == 0) {
437 int x
= v
->x_pos
+ ((leave_at_top
? -15 : 15) * TILE_SIZE
);
440 if ((uint
)x
> MapMaxX() * TILE_SIZE
- 1) return true;
442 TileIndex tile
= TileVirtXY(x
, y
);
443 if (!IsTileType(tile
, MP_INDUSTRY
)) return true;
445 IndustryID ind
= GetIndustryIndex(tile
);
448 if (GetIndustrySpec(Industry::Get(ind
)->type
)->behaviour
& industry_flag
) {
449 v
->current_order
.SetDestination(1);
457 /** Airplane handling. */
458 static bool DisasterTick_Airplane(DisasterVehicle
*v
)
460 return DisasterTick_Aircraft(v
, SPR_F_15_FIRING
, true, STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY
, INDUSTRYBEH_AIRPLANE_ATTACKS
);
463 /** Helicopter handling. */
464 static bool DisasterTick_Helicopter(DisasterVehicle
*v
)
466 return DisasterTick_Aircraft(v
, SPR_AH_64A_FIRING
, false, STR_NEWS_DISASTER_HELICOPTER_FACTORY
, INDUSTRYBEH_CHOPPER_ATTACKS
);
469 /** Helicopter rotor blades; keep these spinning */
470 static bool DisasterTick_Helicopter_Rotors(DisasterVehicle
*v
)
473 if (HasBit(v
->tick_counter
, 0)) return true;
475 if (++v
->cur_image
> SPR_ROTOR_MOVING_3
) v
->cur_image
= SPR_ROTOR_MOVING_1
;
477 VehicleUpdatePositionAndViewport(v
);
483 * (Big) Ufo handling, v->current_order.dest states:
484 * 0: Fly around to the middle of the map, then randomly for a while and home in on a piece of rail
485 * 1: Land there and breakdown all trains in a radius of 12 tiles; and now we wait...
486 * because as soon as the Ufo lands, a fighter jet, a Skyranger, is called to clear up the mess
488 static bool DisasterTick_Big_Ufo(DisasterVehicle
*v
)
492 if (v
->current_order
.GetDestination() == 1) {
493 int x
= TileX(v
->dest_tile
) * TILE_SIZE
+ TILE_SIZE
/ 2;
494 int y
= TileY(v
->dest_tile
) * TILE_SIZE
+ TILE_SIZE
/ 2;
495 if (Delta(v
->x_pos
, x
) + Delta(v
->y_pos
, y
) >= 8) {
496 v
->direction
= GetDirectionTowards(v
, x
, y
);
498 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
499 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, v
->z_pos
);
503 if (!IsValidTile(v
->dest_tile
)) {
504 /* Make sure we don't land outside the map. */
509 int z
= GetSlopePixelZ(v
->x_pos
, v
->y_pos
);
511 SetDisasterVehiclePos(v
, v
->x_pos
, v
->y_pos
, v
->z_pos
- 1);
515 v
->current_order
.SetDestination(2);
518 FOR_ALL_VEHICLES(target
) {
519 if (target
->IsGroundVehicle()) {
520 if (Delta(target
->x_pos
, v
->x_pos
) + Delta(target
->y_pos
, v
->y_pos
) <= 12 * (int)TILE_SIZE
) {
521 target
->breakdown_ctr
= 5;
522 target
->breakdown_delay
= 0xF0;
527 Town
*t
= ClosestTownFromTile(v
->dest_tile
, UINT_MAX
);
528 SetDParam(0, t
->index
);
529 AddTileNewsItem(STR_NEWS_DISASTER_BIG_UFO
, NT_ACCIDENT
, v
->tile
);
531 if (!Vehicle::CanAllocateItem(2)) {
535 DisasterVehicle
*u
= new DisasterVehicle();
537 InitializeDisasterVehicle(u
, -6 * (int)TILE_SIZE
, v
->y_pos
, INITIAL_DISASTER_VEHICLE_ZPOS
, DIR_SW
, ST_BIG_UFO_DESTROYER
);
538 u
->big_ufo_destroyer_target
= v
->index
;
540 DisasterVehicle
*w
= new DisasterVehicle();
543 InitializeDisasterVehicle(w
, -6 * (int)TILE_SIZE
, v
->y_pos
, 0, DIR_SW
, ST_BIG_UFO_DESTROYER_SHADOW
);
544 w
->vehstatus
|= VS_SHADOW
;
545 } else if (v
->current_order
.GetDestination() == 0) {
546 int x
= TileX(v
->dest_tile
) * TILE_SIZE
;
547 int y
= TileY(v
->dest_tile
) * TILE_SIZE
;
548 if (Delta(x
, v
->x_pos
) + Delta(y
, v
->y_pos
) >= (int)TILE_SIZE
) {
549 v
->direction
= GetDirectionTowards(v
, x
, y
);
550 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
551 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, v
->z_pos
);
556 v
->dest_tile
= RandomTile();
559 v
->current_order
.SetDestination(1);
561 TileIndex tile_org
= RandomTile();
562 TileIndex tile
= tile_org
;
564 if (IsPlainRailTile(tile
) &&
565 Company::IsHumanID(GetTileOwner(tile
))) {
568 tile
= TILE_MASK(tile
+ 1);
569 } while (tile
!= tile_org
);
578 * Skyranger destroying (Big) Ufo handling, v->current_order.dest states:
579 * 0: Home in on landed Ufo and shoot it down
581 static bool DisasterTick_Big_Ufo_Destroyer(DisasterVehicle
*v
)
585 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
586 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, v
->z_pos
);
588 if (gp
.x
> (int)(MapSizeX() * TILE_SIZE
+ 9 * TILE_SIZE
) - 1) {
593 if (v
->current_order
.GetDestination() == 0) {
594 Vehicle
*u
= Vehicle::Get(v
->big_ufo_destroyer_target
);
595 if (Delta(v
->x_pos
, u
->x_pos
) > (int)TILE_SIZE
) return true;
596 v
->current_order
.SetDestination(1);
598 CreateEffectVehicleRel(u
, 0, 7, 8, EV_EXPLOSION_LARGE
);
599 if (_settings_client
.sound
.disaster
) SndPlayVehicleFx(SND_12_EXPLOSION
, u
);
603 for (int i
= 0; i
!= 80; i
++) {
605 CreateEffectVehicleAbove(
606 GB(r
, 0, 6) + v
->x_pos
- 32,
607 GB(r
, 5, 6) + v
->y_pos
- 32,
612 for (int dy
= -3; dy
< 3; dy
++) {
613 for (int dx
= -3; dx
< 3; dx
++) {
614 TileIndex tile
= TileAddWrap(v
->tile
, dx
, dy
);
615 if (tile
!= INVALID_TILE
) DisasterClearSquare(tile
);
624 * Submarine, v->current_order.dest states:
625 * Unused, just float around aimlessly and pop up at different places, turning around
627 static bool DisasterTick_Submarine(DisasterVehicle
*v
)
631 if (++v
->age
> 8880) {
636 if (!HasBit(v
->tick_counter
, 0)) return true;
638 TileIndex tile
= v
->tile
+ TileOffsByDiagDir(DirToDiagDir(v
->direction
));
639 if (IsValidTile(tile
)) {
640 TrackBits trackbits
= TrackStatusToTrackBits(GetTileTrackStatus(tile
, TRANSPORT_WATER
, 0));
641 if (trackbits
== TRACK_BIT_ALL
&& !Chance16(1, 90)) {
642 GetNewVehiclePosResult gp
= GetNewVehiclePos(v
);
643 SetDisasterVehiclePos(v
, gp
.x
, gp
.y
, v
->z_pos
);
648 v
->direction
= ChangeDir(v
->direction
, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT
: DIRDIFF_90LEFT
);
654 static bool DisasterTick_NULL(DisasterVehicle
*v
)
659 typedef bool DisasterVehicleTickProc(DisasterVehicle
*v
);
661 static DisasterVehicleTickProc
* const _disastervehicle_tick_procs
[] = {
662 DisasterTick_Zeppeliner
, DisasterTick_NULL
,
663 DisasterTick_Ufo
, DisasterTick_NULL
,
664 DisasterTick_Airplane
, DisasterTick_NULL
,
665 DisasterTick_Helicopter
, DisasterTick_NULL
, DisasterTick_Helicopter_Rotors
,
666 DisasterTick_Big_Ufo
, DisasterTick_NULL
, DisasterTick_Big_Ufo_Destroyer
,
668 DisasterTick_Submarine
,
669 DisasterTick_Submarine
,
673 bool DisasterVehicle::Tick()
675 return _disastervehicle_tick_procs
[this->subtype
](this);
678 typedef void DisasterInitProc();
682 * Zeppeliner which crashes on a small airport if one found,
683 * otherwise crashes on a random tile
685 static void Disaster_Zeppeliner_Init()
687 if (!Vehicle::CanAllocateItem(2)) return;
689 /* Pick a random place, unless we find a small airport */
690 int x
= TileX(Random()) * TILE_SIZE
+ TILE_SIZE
/ 2;
693 FOR_ALL_STATIONS(st
) {
694 if (st
->airport
.tile
!= INVALID_TILE
&& (st
->airport
.type
== AT_SMALL
|| st
->airport
.type
== AT_LARGE
)) {
695 x
= (TileX(st
->airport
.tile
) + 2) * TILE_SIZE
;
700 DisasterVehicle
*v
= new DisasterVehicle();
701 InitializeDisasterVehicle(v
, x
, 0, INITIAL_DISASTER_VEHICLE_ZPOS
, DIR_SE
, ST_ZEPPELINER
);
703 /* Allocate shadow */
704 DisasterVehicle
*u
= new DisasterVehicle();
706 InitializeDisasterVehicle(u
, x
, 0, 0, DIR_SE
, ST_ZEPPELINER_SHADOW
);
707 u
->vehstatus
|= VS_SHADOW
;
712 * Ufo which flies around aimlessly from the middle of the map a bit
713 * until it locates a road vehicle which it targets and then destroys
715 static void Disaster_Small_Ufo_Init()
717 if (!Vehicle::CanAllocateItem(2)) return;
719 DisasterVehicle
*v
= new DisasterVehicle();
720 int x
= TileX(Random()) * TILE_SIZE
+ TILE_SIZE
/ 2;
722 InitializeDisasterVehicle(v
, x
, 0, INITIAL_DISASTER_VEHICLE_ZPOS
, DIR_SE
, ST_SMALL_UFO
);
723 v
->dest_tile
= TileXY(MapSizeX() / 2, MapSizeY() / 2);
726 /* Allocate shadow */
727 DisasterVehicle
*u
= new DisasterVehicle();
729 InitializeDisasterVehicle(u
, x
, 0, 0, DIR_SE
, ST_SMALL_UFO_SHADOW
);
730 u
->vehstatus
|= VS_SHADOW
;
734 /* Combat airplane which destroys an oil refinery */
735 static void Disaster_Airplane_Init()
737 if (!Vehicle::CanAllocateItem(2)) return;
739 Industry
*i
, *found
= NULL
;
741 FOR_ALL_INDUSTRIES(i
) {
742 if ((GetIndustrySpec(i
->type
)->behaviour
& INDUSTRYBEH_AIRPLANE_ATTACKS
) &&
743 (found
== NULL
|| Chance16(1, 2))) {
748 if (found
== NULL
) return;
750 DisasterVehicle
*v
= new DisasterVehicle();
752 /* Start from the bottom (south side) of the map */
753 int x
= (MapSizeX() + 9) * TILE_SIZE
- 1;
754 int y
= TileY(found
->location
.tile
) * TILE_SIZE
+ 37;
756 InitializeDisasterVehicle(v
, x
, y
, INITIAL_DISASTER_VEHICLE_ZPOS
, DIR_NE
, ST_AIRPLANE
);
758 DisasterVehicle
*u
= new DisasterVehicle();
760 InitializeDisasterVehicle(u
, x
, y
, 0, DIR_SE
, ST_AIRPLANE_SHADOW
);
761 u
->vehstatus
|= VS_SHADOW
;
765 /** Combat helicopter that destroys a factory */
766 static void Disaster_Helicopter_Init()
768 if (!Vehicle::CanAllocateItem(3)) return;
770 Industry
*i
, *found
= NULL
;
772 FOR_ALL_INDUSTRIES(i
) {
773 if ((GetIndustrySpec(i
->type
)->behaviour
& INDUSTRYBEH_CHOPPER_ATTACKS
) &&
774 (found
== NULL
|| Chance16(1, 2))) {
779 if (found
== NULL
) return;
781 DisasterVehicle
*v
= new DisasterVehicle();
783 int x
= -16 * (int)TILE_SIZE
;
784 int y
= TileY(found
->location
.tile
) * TILE_SIZE
+ 37;
786 InitializeDisasterVehicle(v
, x
, y
, INITIAL_DISASTER_VEHICLE_ZPOS
, DIR_SW
, ST_HELICOPTER
);
788 DisasterVehicle
*u
= new DisasterVehicle();
790 InitializeDisasterVehicle(u
, x
, y
, 0, DIR_SW
, ST_HELICOPTER_SHADOW
);
791 u
->vehstatus
|= VS_SHADOW
;
793 DisasterVehicle
*w
= new DisasterVehicle();
795 InitializeDisasterVehicle(w
, x
, y
, 140, DIR_SW
, ST_HELICOPTER_ROTORS
);
799 /* Big Ufo which lands on a piece of rail and will consequently be shot
800 * down by a combat airplane, destroying the surroundings */
801 static void Disaster_Big_Ufo_Init()
803 if (!Vehicle::CanAllocateItem(2)) return;
805 DisasterVehicle
*v
= new DisasterVehicle();
806 int x
= TileX(Random()) * TILE_SIZE
+ TILE_SIZE
/ 2;
807 int y
= MapMaxX() * TILE_SIZE
- 1;
809 InitializeDisasterVehicle(v
, x
, y
, INITIAL_DISASTER_VEHICLE_ZPOS
, DIR_NW
, ST_BIG_UFO
);
810 v
->dest_tile
= TileXY(MapSizeX() / 2, MapSizeY() / 2);
813 /* Allocate shadow */
814 DisasterVehicle
*u
= new DisasterVehicle();
816 InitializeDisasterVehicle(u
, x
, y
, 0, DIR_NW
, ST_BIG_UFO_SHADOW
);
817 u
->vehstatus
|= VS_SHADOW
;
821 static void Disaster_Submarine_Init(DisasterSubType subtype
)
823 if (!Vehicle::CanAllocateItem()) return;
828 int x
= TileX(r
) * TILE_SIZE
+ TILE_SIZE
/ 2;
831 y
= MapMaxY() * TILE_SIZE
- TILE_SIZE
/ 2 - 1;
835 if (_settings_game
.construction
.freeform_edges
) y
+= TILE_SIZE
;
838 if (!IsWaterTile(TileVirtXY(x
, y
))) return;
840 DisasterVehicle
*v
= new DisasterVehicle();
841 InitializeDisasterVehicle(v
, x
, y
, 0, dir
, subtype
);
845 /* Curious submarine #1, just floats around */
846 static void Disaster_Small_Submarine_Init()
848 Disaster_Submarine_Init(ST_SMALL_SUBMARINE
);
852 /* Curious submarine #2, just floats around */
853 static void Disaster_Big_Submarine_Init()
855 Disaster_Submarine_Init(ST_BIG_SUBMARINE
);
860 * Coal mine catastrophe, destroys a stretch of 30 tiles of
861 * land in a certain direction
863 static void Disaster_CoalMine_Init()
865 int index
= GB(Random(), 0, 4);
868 for (m
= 0; m
< 15; m
++) {
871 FOR_ALL_INDUSTRIES(i
) {
872 if ((GetIndustrySpec(i
->type
)->behaviour
& INDUSTRYBEH_CAN_SUBSIDENCE
) && --index
< 0) {
873 SetDParam(0, i
->town
->index
);
874 AddTileNewsItem(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE
, NT_ACCIDENT
, i
->location
.tile
+ TileDiffXY(1, 1)); // keep the news, even when the mine closes
877 TileIndex tile
= i
->location
.tile
;
878 TileIndexDiff step
= TileOffsByDiagDir((DiagDirection
)GB(Random(), 0, 2));
880 for (uint n
= 0; n
< 30; n
++) {
881 DisasterClearSquare(tile
);
883 if (!IsValidTile(tile
)) break;
893 DisasterInitProc
*init_proc
; ///< The init function for this disaster.
894 Year min_year
; ///< The first year this disaster will occur.
895 Year max_year
; ///< The last year this disaster will occur.
898 static const Disaster _disasters
[] = {
899 {Disaster_Zeppeliner_Init
, 1930, 1955}, // zeppeliner
900 {Disaster_Small_Ufo_Init
, 1940, 1970}, // ufo (small)
901 {Disaster_Airplane_Init
, 1960, 1990}, // airplane
902 {Disaster_Helicopter_Init
, 1970, 2000}, // helicopter
903 {Disaster_Big_Ufo_Init
, 2000, 2100}, // ufo (big)
904 {Disaster_Small_Submarine_Init
, 1940, 1965}, // submarine (small)
905 {Disaster_Big_Submarine_Init
, 1975, 2010}, // submarine (big)
906 {Disaster_CoalMine_Init
, 1950, 1985}, // coalmine
909 static void DoDisaster()
911 byte buf
[lengthof(_disasters
)];
914 for (size_t i
= 0; i
!= lengthof(_disasters
); i
++) {
915 if (_cur_year
>= _disasters
[i
].min_year
&& _cur_year
< _disasters
[i
].max_year
) buf
[j
++] = (byte
)i
;
920 _disasters
[buf
[RandomRange(j
)]].init_proc();
924 static void ResetDisasterDelay()
926 _disaster_delay
= GB(Random(), 0, 9) + 730;
929 void DisasterDailyLoop()
931 if (--_disaster_delay
!= 0) return;
933 ResetDisasterDelay();
935 if (_settings_game
.difficulty
.disasters
!= 0) DoDisaster();
938 void StartupDisasters()
940 ResetDisasterDelay();
944 * Marks all disasters targeting this industry in such a way
945 * they won't call Industry::Get(v->dest_tile) on invalid industry anymore.
946 * @param i deleted industry
948 void ReleaseDisastersTargetingIndustry(IndustryID i
)
951 FOR_ALL_DISASTERVEHICLES(v
) {
952 /* primary disaster vehicles that have chosen target */
953 if (v
->subtype
== ST_AIRPLANE
|| v
->subtype
== ST_HELICOPTER
) {
954 /* if it has chosen target, and it is this industry (yes, dest_tile is IndustryID here), set order to "leaving map peacefully" */
955 if (v
->current_order
.GetDestination() > 0 && v
->dest_tile
== i
) v
->current_order
.SetDestination(3);
961 * Notify disasters that we are about to delete a vehicle. So make them head elsewhere.
962 * @param vehicle deleted vehicle
964 void ReleaseDisastersTargetingVehicle(VehicleID vehicle
)
967 FOR_ALL_DISASTERVEHICLES(v
) {
968 /* primary disaster vehicles that have chosen target */
969 if (v
->subtype
== ST_SMALL_UFO
) {
970 if (v
->current_order
.GetDestination() != 0 && v
->dest_tile
== vehicle
) {
971 /* Revert to target-searching */
972 v
->current_order
.SetDestination(0);
973 v
->dest_tile
= RandomTile();
974 v
->z_pos
= INITIAL_DISASTER_VEHICLE_ZPOS
;
981 void DisasterVehicle::UpdateDeltaXY(Direction direction
)