tron@2186: /* $Id$ */
tron@2186:
belugas@6117: /** @file disaster_cmd.cpp
Darkvater@5695: * All disaster/easter egg vehicles are handled here.
Darkvater@5695: * The general flow of control for the disaster vehicles is as follows:
Darkvater@5695: *
Darkvater@5695: * - Initialize the disaster in a disaster specific way (eg start position,
Darkvater@5695: * possible target, etc.) Disaster_XXX_Init() function
Darkvater@5695: *
- Add a subtype to a disaster, which is an index into the function array
Darkvater@5695: * that handles the vehicle's ticks.
Darkvater@5695: *
- Run the disaster vehicles each tick until their target has been reached,
Darkvater@5695: * this happens in the DisasterTick_XXX() functions. In here, a vehicle's
Darkvater@5695: * state is kept by v->current_order.dest variable. Each achieved sub-target
Darkvater@5695: * will increase this value, and the last one will remove the disaster itself
Darkvater@5695: *
Darkvater@5695: */
Darkvater@5695:
Darkvater@5695:
truelight@0: #include "stdafx.h"
Darkvater@1891: #include "openttd.h"
maedhros@6453: #include "landscape.h"
rubidium@8119:
tron@3314: #include "industry_map.h"
tron@3315: #include "station_map.h"
rubidium@8116: #include "command_func.h"
rubidium@8119: #include "tile_cmd.h"
rubidium@8763: #include "news_func.h"
rubidium@8785: #include "station_base.h"
Darkvater@2435: #include "waypoint.h"
truelight@0: #include "town.h"
truelight@0: #include "industry.h"
rubidium@8254: #include "player_func.h"
peter1138@3701: #include "airport.h"
tron@2159: #include "variables.h"
rubidium@8270: #include "settings_type.h"
rubidium@8114: #include "strings_func.h"
rubidium@8140: #include "date_func.h"
rubidium@8131: #include "functions.h"
rubidium@8144: #include "vehicle_func.h"
rubidium@8144: #include "vehicle_base.h"
rubidium@8157: #include "sound_func.h"
rubidium@9009: #include "effectvehicle_func.h"
truelight@0:
rubidium@8264: #include "table/strings.h"
rubidium@8264: #include "table/sprites.h"
rubidium@8264:
Darkvater@5695: enum DisasterSubType {
Darkvater@5700: ST_Zeppeliner,
Darkvater@5700: ST_Zeppeliner_Shadow,
Darkvater@5700: ST_Small_Ufo,
Darkvater@5700: ST_Small_Ufo_Shadow,
Darkvater@5700: ST_Airplane,
Darkvater@5700: ST_Airplane_Shadow,
Darkvater@5700: ST_Helicopter,
Darkvater@5700: ST_Helicopter_Shadow,
Darkvater@5700: ST_Helicopter_Rotors,
Darkvater@5700: ST_Big_Ufo,
Darkvater@5700: ST_Big_Ufo_Shadow,
Darkvater@5700: ST_Big_Ufo_Destroyer,
Darkvater@5700: ST_Big_Ufo_Destroyer_Shadow,
Darkvater@5700: ST_Small_Submarine,
Darkvater@5700: ST_Big_Submarine,
Darkvater@5695: };
Darkvater@5695:
tron@1048: static void DisasterClearSquare(TileIndex tile)
truelight@0: {
rubidium@7758: if (!EnsureNoVehicleOnGround(tile)) return;
truelight@0:
tron@1214: switch (GetTileType(tile)) {
tron@1048: case MP_RAILWAY:
Darkvater@4845: if (IsHumanPlayer(GetTileOwner(tile)) && !IsRailWaypoint(tile)) {
KUDr@3900: PlayerID p = _current_player;
KUDr@3900: _current_player = OWNER_WATER;
KUDr@3900: DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
KUDr@3900: _current_player = p;
smatz@8347:
smatz@8347: /* update signals in buffer */
smatz@8347: UpdateSignalsInBuffer();
KUDr@3900: }
tron@1048: break;
truelight@0:
tron@1048: case MP_HOUSE: {
Darkvater@2435: PlayerID p = _current_player;
tron@1048: _current_player = OWNER_NONE;
tron@3491: DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
tron@1048: _current_player = p;
tron@1048: break;
tron@1048: }
tron@1048:
tron@1048: case MP_TREES:
tron@1048: case MP_CLEAR:
truelight@0: DoClearSquare(tile);
tron@1048: break;
tron@1048:
tron@1048: default:
tron@1048: break;
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: static const SpriteID _disaster_images_1[] = {SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP};
Darkvater@5695: 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};
Darkvater@5695: 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};
Darkvater@5695: 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};
Darkvater@5695: 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};
Darkvater@5695: 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};
Darkvater@5695: 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};
Darkvater@5695: 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};
Darkvater@5695: 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};
truelight@0:
truelight@0: static const SpriteID * const _disaster_images[] = {
belugas@6125: _disaster_images_1, _disaster_images_1, ///< zeppeliner and zeppeliner shadow
belugas@6125: _disaster_images_2, _disaster_images_2, ///< small ufo and small ufo shadow
belugas@6125: _disaster_images_3, _disaster_images_3, ///< combat aircraft and shadow
belugas@6125: _disaster_images_8, _disaster_images_8, _disaster_images_9, ///< combat helicopter, shadow and rotor
belugas@6125: _disaster_images_6, _disaster_images_6, ///< big ufo and shadow
belugas@6125: _disaster_images_7, _disaster_images_7, ///< skyranger and shadow
belugas@6125: _disaster_images_4, _disaster_images_5, ///< small and big submarine sprites
truelight@0: };
truelight@0:
truelight@0: static void DisasterVehicleUpdateImage(Vehicle *v)
truelight@0: {
Darkvater@5695: SpriteID img = v->u.disaster.image_override;
Darkvater@5695: if (img == 0) img = _disaster_images[v->subtype][v->direction];
truelight@0: v->cur_image = img;
truelight@0: }
truelight@0:
rubidium@6259: /** Initialize a disaster vehicle. These vehicles are of type VEH_DISASTER, are unclickable
Darkvater@5695: * and owned by nobody */
Darkvater@5695: static void InitializeDisasterVehicle(Vehicle *v, int x, int y, byte z, Direction direction, byte subtype)
truelight@0: {
truelight@0: v->x_pos = x;
truelight@0: v->y_pos = y;
truelight@0: v->z_pos = z;
tron@1980: v->tile = TileVirtXY(x, y);
truelight@0: v->direction = direction;
truelight@0: v->subtype = subtype;
rubidium@6558: v->UpdateDeltaXY(INVALID_DIR);
truelight@0: v->owner = OWNER_NONE;
truelight@0: v->vehstatus = VS_UNCLICKABLE;
truelight@0: v->u.disaster.image_override = 0;
bjarni@6263: v->current_order.Free();
truelight@0:
truelight@0: DisasterVehicleUpdateImage(v);
truelight@0: VehiclePositionChanged(v);
smatz@8317: MarkSingleVehicleDirty(v);
truelight@0: }
truelight@0:
truelight@0: static void DeleteDisasterVeh(Vehicle *v)
truelight@0: {
truelight@0: DeleteVehicleChain(v);
truelight@0: }
truelight@0:
truelight@0: static void SetDisasterVehiclePos(Vehicle *v, int x, int y, byte z)
truelight@0: {
truelight@0: Vehicle *u;
truelight@0:
truelight@0: BeginVehicleMove(v);
truelight@0: v->x_pos = x;
truelight@0: v->y_pos = y;
truelight@0: v->z_pos = z;
tron@1980: v->tile = TileVirtXY(x, y);
truelight@0:
truelight@0: DisasterVehicleUpdateImage(v);
truelight@0: VehiclePositionChanged(v);
truelight@0: EndVehicleMove(v);
truelight@0:
rubidium@7492: if ((u = v->Next()) != NULL) {
skidd13@7922: int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
skidd13@7922: int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
truelight@0: BeginVehicleMove(u);
truelight@193:
truelight@0: u->x_pos = x;
celestar@5601: u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0U) >> 3);
skidd13@7922: safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
rubidium@4249: u->z_pos = GetSlopeZ(safe_x, safe_y);
truelight@0: u->direction = v->direction;
truelight@0:
truelight@0: DisasterVehicleUpdateImage(u);
truelight@0: VehiclePositionChanged(u);
truelight@0: EndVehicleMove(u);
truelight@0:
rubidium@7492: if ((u = u->Next()) != NULL) {
truelight@0: BeginVehicleMove(u);
truelight@0: u->x_pos = x;
truelight@0: u->y_pos = y;
truelight@0: u->z_pos = z + 5;
truelight@0: VehiclePositionChanged(u);
truelight@0: EndVehicleMove(u);
truelight@0: }
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: /**
Darkvater@5695: * Zeppeliner handling, v->current_order.dest states:
Darkvater@5695: * 0: Zeppeliner initialization has found a small airport, go there and crash
Darkvater@5695: * 1: Create crash and animate falling down for extra dramatic effect
Darkvater@5695: * 2: Create more smoke and leave debris on ground
Darkvater@5695: * 2: Clear the runway after some time and remove crashed zeppeliner
Darkvater@5695: * If not airport was found, only state 0 is reached until zeppeliner leaves map
Darkvater@5695: */
truelight@0: static void DisasterTick_Zeppeliner(Vehicle *v)
truelight@0: {
truelight@0: Station *st;
Darkvater@5695: int x, y;
truelight@0: byte z;
tron@1977: TileIndex tile;
truelight@0:
Darkvater@5695: v->tick_counter++;
truelight@0:
rubidium@8840: if (v->current_order.GetDestination() < 2) {
skidd13@7928: if (HasBit(v->tick_counter, 0)) return;
truelight@0:
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@193:
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
truelight@0:
rubidium@8840: if (v->current_order.GetDestination() == 1) {
truelight@0: if (++v->age == 38) {
rubidium@8840: v->current_order.SetDestination(2);
truelight@0: v->age = 0;
truelight@0: }
truelight@0:
Darkvater@5695: if (GB(v->tick_counter, 0, 3) == 0) CreateEffectVehicleRel(v, 0, -17, 2, EV_SMOKE);
Darkvater@5695:
rubidium@8840: } else if (v->current_order.GetDestination() == 0) {
Darkvater@5695: tile = v->tile;
truelight@0:
tron@1212: if (IsValidTile(tile) &&
tron@1212: IsTileType(tile, MP_STATION) &&
tron@3338: IsAirport(tile) &&
Darkvater@4845: IsHumanPlayer(GetTileOwner(tile))) {
rubidium@8840: v->current_order.SetDestination(1);
truelight@0: v->age = 0;
truelight@0:
tron@3315: SetDParam(0, GetStationIndex(tile));
truelight@0: AddNewsItem(STR_B000_ZEPPELIN_DISASTER_AT,
rubidium@9234: NS_ACCIDENT_VEHICLE,
truelight@0: v->index,
truelight@0: 0);
truelight@0: }
truelight@0: }
Darkvater@5695:
Darkvater@5695: if (v->y_pos >= ((int)MapSizeY() + 9) * TILE_SIZE - 1) DeleteDisasterVeh(v);
truelight@0: return;
truelight@193: }
truelight@193:
rubidium@8840: if (v->current_order.GetDestination() > 2) {
Darkvater@5695: if (++v->age <= 13320) return;
truelight@193:
Darkvater@5695: tile = v->tile;
truelight@0:
tron@1212: if (IsValidTile(tile) &&
tron@1212: IsTileType(tile, MP_STATION) &&
tron@3338: IsAirport(tile) &&
Darkvater@4845: IsHumanPlayer(GetTileOwner(tile))) {
tron@3315: st = GetStationByTile(tile);
truelight@0: CLRBITS(st->airport_flags, RUNWAY_IN_block);
truelight@0: }
truelight@0:
truelight@0: SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos);
truelight@0: DeleteDisasterVeh(v);
truelight@0: return;
truelight@0: }
truelight@0:
truelight@0: x = v->x_pos;
truelight@0: y = v->y_pos;
rubidium@6491: z = GetSlopeZ(x, y);
Darkvater@5695: if (z < v->z_pos) z = v->z_pos - 1;
truelight@0: SetDisasterVehiclePos(v, x, y, z);
truelight@0:
truelight@0: if (++v->age == 1) {
tron@1359: CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
tron@541: SndPlayVehicleFx(SND_12_EXPLOSION, v);
tron@2535: v->u.disaster.image_override = SPR_BLIMP_CRASHING;
truelight@0: } else if (v->age == 70) {
tron@2535: v->u.disaster.image_override = SPR_BLIMP_CRASHED;
truelight@0: } else if (v->age <= 300) {
Darkvater@5695: if (GB(v->tick_counter, 0, 3) == 0) {
truelight@0: uint32 r = Random();
truelight@0:
truelight@0: CreateEffectVehicleRel(v,
tron@2140: GB(r, 0, 4) - 7,
tron@2140: GB(r, 4, 4) - 7,
tron@2140: GB(r, 8, 3) + 5,
tron@1359: EV_EXPLOSION_SMALL);
truelight@0: }
truelight@0: } else if (v->age == 350) {
rubidium@8840: v->current_order.SetDestination(3);
truelight@0: v->age = 0;
truelight@0: }
truelight@0:
Darkvater@5695: tile = v->tile;
tron@1212: if (IsValidTile(tile) &&
tron@1212: IsTileType(tile, MP_STATION) &&
tron@3338: IsAirport(tile) &&
Darkvater@4845: IsHumanPlayer(GetTileOwner(tile))) {
tron@3315: st = GetStationByTile(tile);
truelight@0: SETBITS(st->airport_flags, RUNWAY_IN_block);
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: /**
Darkvater@5695: * (Small) Ufo handling, v->current_order.dest states:
Darkvater@5695: * 0: Fly around to the middle of the map, then randomly, after a while target a road vehicle
Darkvater@5695: * 1: Home in on a road vehicle and crash it >:)
Darkvater@5695: * If not road vehicle was found, only state 0 is used and Ufo disappears after a while
Darkvater@5695: */
Darkvater@5695: static void DisasterTick_Ufo(Vehicle *v)
truelight@0: {
truelight@0: Vehicle *u;
truelight@0: uint dist;
truelight@0: byte z;
truelight@0:
skidd13@7928: v->u.disaster.image_override = (HasBit(++v->tick_counter, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
truelight@193:
rubidium@8840: if (v->current_order.GetDestination() == 0) {
Darkvater@5695: /* Fly around randomly */
celestar@3421: int x = TileX(v->dest_tile) * TILE_SIZE;
celestar@3421: int y = TileY(v->dest_tile) * TILE_SIZE;
skidd13@7970: if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= TILE_SIZE) {
truelight@0: v->direction = GetDirectionTowards(v, x, y);
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
truelight@0: return;
truelight@0: }
truelight@0: if (++v->age < 6) {
ludde@2051: v->dest_tile = RandomTile();
truelight@0: return;
truelight@0: }
rubidium@8840: v->current_order.SetDestination(1);
truelight@0:
truelight@0: FOR_ALL_VEHICLES(u) {
rubidium@6259: if (u->type == VEH_ROAD && IsHumanPlayer(u->owner)) {
truelight@0: v->dest_tile = u->index;
truelight@0: v->age = 0;
truelight@0: return;
truelight@0: }
truelight@0: }
truelight@0:
truelight@0: DeleteDisasterVeh(v);
truelight@0: } else {
Darkvater@5695: /* Target a vehicle */
truelight@919: u = GetVehicle(v->dest_tile);
rubidium@6259: if (u->type != VEH_ROAD) {
truelight@0: DeleteDisasterVeh(v);
truelight@0: return;
truelight@0: }
truelight@0:
skidd13@7970: dist = Delta(v->x_pos, u->x_pos) + Delta(v->y_pos, u->y_pos);
truelight@0:
Darkvater@5695: if (dist < TILE_SIZE && !(u->vehstatus & VS_HIDDEN) && u->breakdown_ctr == 0) {
truelight@0: u->breakdown_ctr = 3;
truelight@0: u->breakdown_delay = 140;
truelight@0: }
truelight@0:
truelight@0: v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@193:
truelight@0: z = v->z_pos;
celestar@3421: if (dist <= TILE_SIZE && z > u->z_pos) z--;
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, z);
truelight@193:
rubidium@6491: if (z <= u->z_pos && (u->vehstatus & VS_HIDDEN) == 0) {
truelight@0: v->age++;
truelight@0: if (u->u.road.crashed_ctr == 0) {
truelight@0: u->u.road.crashed_ctr++;
truelight@0: u->vehstatus |= VS_CRASHED;
truelight@0:
truelight@0: AddNewsItem(STR_B001_ROAD_VEHICLE_DESTROYED,
rubidium@9234: NS_ACCIDENT_VEHICLE,
truelight@0: u->index,
truelight@0: 0);
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: /* Destroy? */
truelight@0: if (v->age > 50) {
tron@1359: CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
tron@541: SndPlayVehicleFx(SND_12_EXPLOSION, v);
truelight@0: DeleteDisasterVeh(v);
truelight@0: }
truelight@0: }
truelight@0: }
truelight@0:
truelight@0: static void DestructIndustry(Industry *i)
truelight@0: {
tron@1977: TileIndex tile;
truelight@0:
tron@2026: for (tile = 0; tile != MapSize(); tile++) {
tron@3314: if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) {
belugas@3495: ResetIndustryConstructionStage(tile);
truelight@0: MarkTileDirtyByTile(tile);
truelight@0: }
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: /**
Darkvater@5695: * Airplane handling, v->current_order.dest states:
Darkvater@5695: * 0: Fly towards the targetted oil refinery
Darkvater@5695: * 1: If within 15 tiles, fire away rockets and destroy industry
Darkvater@5695: * 2: Refinery explosions
Darkvater@5695: * 3: Fly out of the map
Darkvater@5695: * If the industry was removed in the meantime just fly to the end of the map
Darkvater@5695: */
Darkvater@5695: static void DisasterTick_Airplane(Vehicle *v)
truelight@0: {
truelight@0: v->tick_counter++;
tron@555: v->u.disaster.image_override =
rubidium@8840: (v->current_order.GetDestination() == 1 && HasBit(v->tick_counter, 2)) ? SPR_F_15_FIRING : 0;
truelight@193:
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
truelight@0:
Darkvater@5695: if (gp.x < (-10 * TILE_SIZE)) {
truelight@0: DeleteDisasterVeh(v);
truelight@0: return;
truelight@0: }
truelight@0:
rubidium@8840: if (v->current_order.GetDestination() == 2) {
Darkvater@5695: if (GB(v->tick_counter, 0, 2) == 0) {
truelight@919: Industry *i = GetIndustry(v->dest_tile);
celestar@3421: int x = TileX(i->xy) * TILE_SIZE;
celestar@3421: int y = TileY(i->xy) * TILE_SIZE;
truelight@0: uint32 r = Random();
truelight@0:
truelight@0: CreateEffectVehicleAbove(
tron@2140: GB(r, 0, 6) + x,
tron@2140: GB(r, 6, 6) + y,
tron@2140: GB(r, 12, 4),
tron@1359: EV_EXPLOSION_SMALL);
truelight@0:
rubidium@8840: if (++v->age >= 55) v->current_order.SetDestination(3);
truelight@0: }
rubidium@8840: } else if (v->current_order.GetDestination() == 1) {
truelight@0: if (++v->age == 112) {
truelight@0: Industry *i;
truelight@0:
rubidium@8840: v->current_order.SetDestination(2);
truelight@0: v->age = 0;
truelight@0:
truelight@919: i = GetIndustry(v->dest_tile);
truelight@0: DestructIndustry(i);
truelight@0:
tron@534: SetDParam(0, i->town->index);
rubidium@9234: AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NS_ACCIDENT_TILE, i->xy, 0);
tron@541: SndPlayTileFx(SND_12_EXPLOSION, i->xy);
truelight@0: }
rubidium@8840: } else if (v->current_order.GetDestination() == 0) {
Darkvater@5695: int x, y;
tron@1977: TileIndex tile;
tron@3314: uint ind;
truelight@0:
Darkvater@5695: x = v->x_pos - (15 * TILE_SIZE);
truelight@0: y = v->y_pos;
truelight@0:
Darkvater@5695: if ( (uint)x > MapMaxX() * TILE_SIZE - 1) return;
truelight@0:
tron@1980: tile = TileVirtXY(x, y);
Darkvater@5695: if (!IsTileType(tile, MP_INDUSTRY)) return;
truelight@0:
tron@3314: ind = GetIndustryIndex(tile);
tron@3314: v->dest_tile = ind;
truelight@0:
belugas@6390: if (GetIndustrySpec(GetIndustry(ind)->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) {
rubidium@8840: v->current_order.SetDestination(1);
truelight@0: v->age = 0;
truelight@0: }
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: /**
Darkvater@5695: * Helicopter handling, v->current_order.dest states:
Darkvater@5695: * 0: Fly towards the targetted factory
Darkvater@5695: * 1: If within 15 tiles, fire away rockets and destroy industry
Darkvater@5695: * 2: Factory explosions
Darkvater@5695: * 3: Fly out of the map
Darkvater@5695: */
Darkvater@5695: static void DisasterTick_Helicopter(Vehicle *v)
truelight@0: {
truelight@0: v->tick_counter++;
tron@555: v->u.disaster.image_override =
rubidium@8840: (v->current_order.GetDestination() == 1 && HasBit(v->tick_counter, 2)) ? SPR_AH_64A_FIRING : 0;
truelight@0:
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
truelight@0:
celestar@3421: if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
truelight@0: DeleteDisasterVeh(v);
truelight@0: return;
truelight@0: }
truelight@0:
rubidium@8840: if (v->current_order.GetDestination() == 2) {
Darkvater@5695: if (GB(v->tick_counter, 0, 2) == 0) {
truelight@919: Industry *i = GetIndustry(v->dest_tile);
celestar@3421: int x = TileX(i->xy) * TILE_SIZE;
celestar@3421: int y = TileY(i->xy) * TILE_SIZE;
truelight@0: uint32 r = Random();
truelight@0:
truelight@0: CreateEffectVehicleAbove(
tron@2140: GB(r, 0, 6) + x,
tron@2140: GB(r, 6, 6) + y,
tron@2140: GB(r, 12, 4),
tron@1359: EV_EXPLOSION_SMALL);
truelight@0:
rubidium@8840: if (++v->age >= 55) v->current_order.SetDestination(3);
truelight@0: }
rubidium@8840: } else if (v->current_order.GetDestination() == 1) {
truelight@0: if (++v->age == 112) {
truelight@0: Industry *i;
truelight@0:
rubidium@8840: v->current_order.SetDestination(2);
truelight@0: v->age = 0;
truelight@0:
truelight@919: i = GetIndustry(v->dest_tile);
truelight@0: DestructIndustry(i);
truelight@0:
tron@534: SetDParam(0, i->town->index);
rubidium@9234: AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NS_ACCIDENT_TILE, i->xy, 0);
tron@541: SndPlayTileFx(SND_12_EXPLOSION, i->xy);
truelight@0: }
rubidium@8840: } else if (v->current_order.GetDestination() == 0) {
Darkvater@5695: int x, y;
tron@1977: TileIndex tile;
tron@3314: uint ind;
truelight@0:
Darkvater@5680: x = v->x_pos + (15 * TILE_SIZE);
truelight@0: y = v->y_pos;
truelight@0:
Darkvater@5695: if ( (uint)x > MapMaxX() * TILE_SIZE - 1) return;
truelight@0:
tron@1980: tile = TileVirtXY(x, y);
Darkvater@5695: if (!IsTileType(tile, MP_INDUSTRY)) return;
truelight@0:
tron@3314: ind = GetIndustryIndex(tile);
tron@3314: v->dest_tile = ind;
truelight@0:
belugas@6390: if (GetIndustrySpec(GetIndustry(ind)->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) {
rubidium@8840: v->current_order.SetDestination(1);
truelight@0: v->age = 0;
truelight@0: }
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: /** Helicopter rotor blades; keep these spinning */
Darkvater@5695: static void DisasterTick_Helicopter_Rotors(Vehicle *v)
truelight@0: {
Darkvater@5695: v->tick_counter++;
skidd13@7928: if (HasBit(v->tick_counter, 0)) return;
truelight@0:
tron@2535: if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
truelight@0:
truelight@0: VehiclePositionChanged(v);
smatz@8317: MarkSingleVehicleDirty(v);
truelight@0: }
truelight@0:
Darkvater@5695: /**
Darkvater@5695: * (Big) Ufo handling, v->current_order.dest states:
Darkvater@5700: * 0: Fly around to the middle of the map, then randomly for a while and home in on a piece of rail
Darkvater@5700: * 1: Land there and breakdown all trains in a radius of 12 tiles; and now we wait...
Darkvater@5695: * because as soon as the Ufo lands, a fighter jet, a Skyranger, is called to clear up the mess
Darkvater@5695: */
Darkvater@5695: static void DisasterTick_Big_Ufo(Vehicle *v)
truelight@0: {
truelight@0: byte z;
Darkvater@5695: Vehicle *u, *w;
truelight@0: Town *t;
tron@1977: TileIndex tile;
tron@1977: TileIndex tile_org;
truelight@0:
truelight@0: v->tick_counter++;
truelight@0:
rubidium@8840: if (v->current_order.GetDestination() == 1) {
tron@3645: int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
tron@3645: int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
skidd13@7970: if (Delta(v->x_pos, x) + Delta(v->y_pos, y) >= 8) {
truelight@0: v->direction = GetDirectionTowards(v, x, y);
truelight@0:
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
truelight@0: return;
truelight@0: }
truelight@0:
truelight@0: z = GetSlopeZ(v->x_pos, v->y_pos);
truelight@0: if (z < v->z_pos) {
truelight@0: SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
truelight@0: return;
truelight@0: }
truelight@0:
rubidium@8840: v->current_order.SetDestination(2);
truelight@0:
truelight@0: FOR_ALL_VEHICLES(u) {
rubidium@6259: if (u->type == VEH_TRAIN || u->type == VEH_ROAD) {
skidd13@7970: if (Delta(u->x_pos, v->x_pos) + Delta(u->y_pos, v->y_pos) <= 12 * TILE_SIZE) {
truelight@0: u->breakdown_ctr = 5;
truelight@0: u->breakdown_delay = 0xF0;
truelight@0: }
truelight@0: }
truelight@0: }
truelight@0:
truelight@0: t = ClosestTownFromTile(v->dest_tile, (uint)-1);
tron@534: SetDParam(0, t->index);
truelight@0: AddNewsItem(STR_B004_UFO_LANDS_NEAR,
rubidium@9234: NS_ACCIDENT_TILE,
truelight@0: v->tile,
truelight@0: 0);
truelight@0:
rubidium@7398: u = new DisasterVehicle();
truelight@0: if (u == NULL) {
truelight@0: DeleteDisasterVeh(v);
truelight@0: return;
truelight@0: }
truelight@0:
Darkvater@5695: InitializeDisasterVehicle(u, -6 * TILE_SIZE, v->y_pos, 135, DIR_SW, ST_Big_Ufo_Destroyer);
rubidium@7334: u->u.disaster.big_ufo_destroyer_target = v->index;
truelight@0:
rubidium@7398: w = new DisasterVehicle();
Darkvater@5695: if (w == NULL) return;
truelight@0:
rubidium@7493: u->SetNext(w);
Darkvater@5695: InitializeDisasterVehicle(w, -6 * TILE_SIZE, v->y_pos, 0, DIR_SW, ST_Big_Ufo_Destroyer_Shadow);
tron@4175: w->vehstatus |= VS_SHADOW;
rubidium@8840: } else if (v->current_order.GetDestination() == 0) {
celestar@3421: int x = TileX(v->dest_tile) * TILE_SIZE;
celestar@3421: int y = TileY(v->dest_tile) * TILE_SIZE;
skidd13@7970: if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= TILE_SIZE) {
truelight@0: v->direction = GetDirectionTowards(v, x, y);
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
truelight@0: return;
truelight@0: }
truelight@0:
truelight@0: if (++v->age < 6) {
ludde@2051: v->dest_tile = RandomTile();
truelight@0: return;
truelight@0: }
rubidium@8840: v->current_order.SetDestination(1);
truelight@0:
ludde@2051: tile_org = tile = RandomTile();
truelight@0: do {
tron@1035: if (IsTileType(tile, MP_RAILWAY) &&
tron@3269: IsPlainRailTile(tile) &&
Darkvater@4845: IsHumanPlayer(GetTileOwner(tile))) {
tron@1901: break;
tron@3269: }
Darkvater@5695: tile = TILE_MASK(tile + 1);
truelight@0: } while (tile != tile_org);
truelight@0: v->dest_tile = tile;
truelight@0: v->age = 0;
tron@4077: } else {
truelight@0: return;
tron@4077: }
truelight@0: }
truelight@0:
Darkvater@5695: /**
Darkvater@5695: * Skyranger destroying (Big) Ufo handling, v->current_order.dest states:
Darkvater@5695: * 0: Home in on landed Ufo and shoot it down
Darkvater@5695: */
Darkvater@5695: static void DisasterTick_Big_Ufo_Destroyer(Vehicle *v)
truelight@0: {
truelight@0: Vehicle *u;
truelight@0: int i;
truelight@0:
truelight@0: v->tick_counter++;
truelight@0:
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
truelight@0: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
truelight@0:
celestar@3421: if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
truelight@0: DeleteDisasterVeh(v);
truelight@0: return;
truelight@0: }
truelight@0:
rubidium@8840: if (v->current_order.GetDestination() == 0) {
rubidium@7334: u = GetVehicle(v->u.disaster.big_ufo_destroyer_target);
skidd13@7970: if (Delta(v->x_pos, u->x_pos) > TILE_SIZE) return;
rubidium@8840: v->current_order.SetDestination(1);
truelight@0:
tron@1359: CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
tron@541: SndPlayVehicleFx(SND_12_EXPLOSION, u);
truelight@0:
truelight@0: DeleteDisasterVeh(u);
truelight@0:
tron@2952: for (i = 0; i != 80; i++) {
truelight@0: uint32 r = Random();
truelight@0: CreateEffectVehicleAbove(
tron@2140: GB(r, 0, 6) + v->x_pos - 32,
tron@2140: GB(r, 5, 6) + v->y_pos - 32,
truelight@0: 0,
tron@1359: EV_EXPLOSION_SMALL);
truelight@0: }
truelight@0:
tron@1981: BEGIN_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
truelight@0: tile = TILE_MASK(tile);
truelight@0: DisasterClearSquare(tile);
tron@1981: END_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: /**
Darkvater@5695: * Submarine, v->current_order.dest states:
Darkvater@5695: * Unused, just float around aimlessly and pop up at different places, turning around
Darkvater@5695: */
Darkvater@5695: static void DisasterTick_Submarine(Vehicle *v)
truelight@0: {
tron@1977: TileIndex tile;
truelight@0:
truelight@0: v->tick_counter++;
truelight@0:
truelight@0: if (++v->age > 8880) {
truelight@0: VehiclePositionChanged(v);
smatz@8317: MarkSingleVehicleDirty(v);
rubidium@7398: delete v;
truelight@0: return;
truelight@0: }
truelight@0:
skidd13@7928: if (!HasBit(v->tick_counter, 0)) return;
truelight@0:
Darkvater@4559: tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
Darkvater@5695: if (IsValidTile(tile)) {
frosch@8616: TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
frosch@8616: if (trackbits == TRACK_BIT_ALL && !Chance16(1, 90)) {
tron@6153: GetNewVehiclePosResult gp = GetNewVehiclePos(v);
Darkvater@5695: SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
Darkvater@5695: return;
Darkvater@5695: }
truelight@0: }
truelight@0:
tron@3160: v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
truelight@0: }
truelight@0:
truelight@0:
truelight@0: static void DisasterTick_NULL(Vehicle *v) {}
truelight@0: typedef void DisasterVehicleTickProc(Vehicle *v);
truelight@0:
truelight@0: static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
Darkvater@5695: DisasterTick_Zeppeliner, DisasterTick_NULL,
Darkvater@5695: DisasterTick_Ufo, DisasterTick_NULL,
Darkvater@5695: DisasterTick_Airplane, DisasterTick_NULL,
Darkvater@5695: DisasterTick_Helicopter, DisasterTick_NULL, DisasterTick_Helicopter_Rotors,
Darkvater@5695: DisasterTick_Big_Ufo, DisasterTick_NULL, DisasterTick_Big_Ufo_Destroyer,
Darkvater@5695: DisasterTick_NULL,
Darkvater@5695: DisasterTick_Submarine,
Darkvater@5695: DisasterTick_Submarine,
truelight@0: };
truelight@0:
truelight@0:
rubidium@7135: void DisasterVehicle::Tick()
truelight@0: {
rubidium@7135: _disastervehicle_tick_procs[this->subtype](this);
truelight@0: }
truelight@0:
rubidium@6247: typedef void DisasterInitProc();
truelight@0:
Darkvater@5695:
Darkvater@5695: /** Zeppeliner which crashes on a small airport if one found,
Darkvater@5695: * otherwise crashes on a random tile */
rubidium@6247: static void Disaster_Zeppeliner_Init()
truelight@0: {
rubidium@7398: Vehicle *v = new DisasterVehicle(), *u;
truelight@0: Station *st;
truelight@0: int x;
truelight@0:
tron@4077: if (v == NULL) return;
truelight@0:
tron@4077: /* Pick a random place, unless we find a small airport */
tron@3645: x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
truelight@919:
truelight@919: FOR_ALL_STATIONS(st) {
truelight@4346: if (st->airport_tile != 0 &&
truelight@0: st->airport_type <= 1 &&
Darkvater@4845: IsHumanPlayer(st->owner)) {
celestar@3421: x = (TileX(st->xy) + 2) * TILE_SIZE;
truelight@0: break;
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695: InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_Zeppeliner);
truelight@0:
Darkvater@5695: /* Allocate shadow too? */
rubidium@7398: u = new DisasterVehicle();
truelight@0: if (u != NULL) {
rubidium@7493: v->SetNext(u);
Darkvater@5695: InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_Zeppeliner_Shadow);
tron@4175: u->vehstatus |= VS_SHADOW;
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695:
Darkvater@5695: /** Ufo which flies around aimlessly from the middle of the map a bit
Darkvater@5695: * until it locates a road vehicle which it targets and then destroys */
rubidium@6247: static void Disaster_Small_Ufo_Init()
truelight@0: {
rubidium@7398: Vehicle *v = new DisasterVehicle(), *u;
truelight@0: int x;
truelight@0:
tron@4077: if (v == NULL) return;
truelight@0:
tron@3645: x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
truelight@0:
Darkvater@5695: InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_Small_Ufo);
tron@1981: v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
truelight@0: v->age = 0;
truelight@0:
Darkvater@5695: /* Allocate shadow too? */
rubidium@7398: u = new DisasterVehicle();
truelight@0: if (u != NULL) {
rubidium@7493: v->SetNext(u);
Darkvater@5695: InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_Small_Ufo_Shadow);
tron@4175: u->vehstatus |= VS_SHADOW;
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695:
Darkvater@5695: /* Combat airplane which destroys an oil refinery */
rubidium@6247: static void Disaster_Airplane_Init()
truelight@0: {
truelight@0: Industry *i, *found;
Darkvater@5695: Vehicle *v, *u;
Darkvater@5695: int x, y;
truelight@0:
truelight@830: found = NULL;
truelight@830:
truelight@830: FOR_ALL_INDUSTRIES(i) {
belugas@6390: if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) &&
skidd13@7967: (found == NULL || Chance16(1, 2))) {
truelight@0: found = i;
truelight@0: }
truelight@0: }
truelight@0:
tron@4077: if (found == NULL) return;
truelight@0:
rubidium@7398: v = new DisasterVehicle();
tron@4077: if (v == NULL) return;
truelight@0:
Darkvater@5695: /* Start from the bottom (south side) of the map */
celestar@3421: x = (MapSizeX() + 9) * TILE_SIZE - 1;
celestar@3421: y = TileY(found->xy) * TILE_SIZE + 37;
truelight@0:
Darkvater@5695: InitializeDisasterVehicle(v, x, y, 135, DIR_NE, ST_Airplane);
truelight@0:
rubidium@7398: u = new DisasterVehicle();
truelight@0: if (u != NULL) {
rubidium@7493: v->SetNext(u);
Darkvater@5695: InitializeDisasterVehicle(u, x, y, 0, DIR_SE, ST_Airplane_Shadow);
tron@4175: u->vehstatus |= VS_SHADOW;
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695:
Darkvater@5695: /** Combat helicopter that destroys a factory */
rubidium@6247: static void Disaster_Helicopter_Init()
truelight@0: {
truelight@0: Industry *i, *found;
Darkvater@5695: Vehicle *v, *u, *w;
Darkvater@5695: int x, y;
truelight@0:
truelight@830: found = NULL;
truelight@830:
truelight@830: FOR_ALL_INDUSTRIES(i) {
belugas@6390: if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) &&
skidd13@7967: (found == NULL || Chance16(1, 2))) {
truelight@0: found = i;
truelight@0: }
truelight@0: }
truelight@0:
tron@4077: if (found == NULL) return;
truelight@0:
rubidium@7398: v = new DisasterVehicle();
tron@4077: if (v == NULL) return;
truelight@0:
celestar@3421: x = -16 * TILE_SIZE;
celestar@3421: y = TileY(found->xy) * TILE_SIZE + 37;
truelight@0:
Darkvater@5695: InitializeDisasterVehicle(v, x, y, 135, DIR_SW, ST_Helicopter);
truelight@0:
rubidium@7398: u = new DisasterVehicle();
truelight@0: if (u != NULL) {
rubidium@7493: v->SetNext(u);
Darkvater@5695: InitializeDisasterVehicle(u, x, y, 0, DIR_SW, ST_Helicopter_Shadow);
tron@4175: u->vehstatus |= VS_SHADOW;
truelight@0:
rubidium@7398: w = new DisasterVehicle();
truelight@0: if (w != NULL) {
rubidium@7493: u->SetNext(w);
Darkvater@5695: InitializeDisasterVehicle(w, x, y, 140, DIR_SW, ST_Helicopter_Rotors);
truelight@0: }
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695:
Darkvater@5695: /* Big Ufo which lands on a piece of rail and will consequently be shot
Darkvater@5695: * down by a combat airplane, destroying the surroundings */
rubidium@6247: static void Disaster_Big_Ufo_Init()
truelight@0: {
rubidium@7398: Vehicle *v = new DisasterVehicle(), *u;
Darkvater@5695: int x, y;
truelight@0:
tron@2639: if (v == NULL) return;
truelight@0:
tron@3645: x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
truelight@0:
celestar@3421: y = MapMaxX() * TILE_SIZE - 1;
Darkvater@5695: InitializeDisasterVehicle(v, x, y, 135, DIR_NW, ST_Big_Ufo);
tron@1981: v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
truelight@0: v->age = 0;
truelight@0:
Darkvater@5695: /* Allocate shadow too? */
rubidium@7398: u = new DisasterVehicle();
truelight@0: if (u != NULL) {
rubidium@7493: v->SetNext(u);
Darkvater@5695: InitializeDisasterVehicle(u, x, y, 0, DIR_NW, ST_Big_Ufo_Shadow);
tron@4175: u->vehstatus |= VS_SHADOW;
truelight@0: }
truelight@0: }
truelight@0:
Darkvater@5695:
Darkvater@5695: /* Curious submarine #1, just floats around */
rubidium@6247: static void Disaster_Small_Submarine_Init()
Darkvater@5695: {
rubidium@7398: Vehicle *v = new DisasterVehicle();
Darkvater@5695: int x, y;
Darkvater@5695: Direction dir;
Darkvater@5695: uint32 r;
Darkvater@5695:
Darkvater@5695: if (v == NULL) return;
Darkvater@5695:
Darkvater@5695: r = Random();
Darkvater@5695: x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
Darkvater@5695:
skidd13@7928: if (HasBit(r, 31)) {
Darkvater@5695: y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
Darkvater@5695: dir = DIR_NW;
Darkvater@5695: } else {
Darkvater@5695: y = TILE_SIZE / 2;
Darkvater@5695: dir = DIR_SE;
Darkvater@5695: }
Darkvater@5695: InitializeDisasterVehicle(v, x, y, 0, dir, ST_Small_Submarine);
Darkvater@5695: v->age = 0;
Darkvater@5695: }
Darkvater@5695:
Darkvater@5695:
Darkvater@5695: /* Curious submarine #2, just floats around */
rubidium@6247: static void Disaster_Big_Submarine_Init()
truelight@0: {
rubidium@7398: Vehicle *v = new DisasterVehicle();
rubidium@6491: int x, y;
tron@3157: Direction dir;
truelight@0: uint32 r;
truelight@0:
tron@2639: if (v == NULL) return;
truelight@0:
truelight@0: r = Random();
tron@3645: x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
truelight@0:
skidd13@7928: if (HasBit(r, 31)) {
tron@3645: y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
tron@3157: dir = DIR_NW;
tron@3157: } else {
tron@3645: y = TILE_SIZE / 2;
tron@3157: dir = DIR_SE;
tron@3157: }
Darkvater@5695: InitializeDisasterVehicle(v, x, y, 0, dir, ST_Big_Submarine);
truelight@0: v->age = 0;
truelight@0: }
truelight@0:
truelight@0:
Darkvater@5695: /** Coal mine catastrophe, destroys a stretch of 30 tiles of
Darkvater@5695: * land in a certain direction */
rubidium@6247: static void Disaster_CoalMine_Init()
truelight@0: {
tron@2642: int index = GB(Random(), 0, 4);
tron@2648: uint m;
truelight@193:
tron@2648: for (m = 0; m < 15; m++) {
Darkvater@5695: const Industry *i;
tron@2989:
truelight@830: FOR_ALL_INDUSTRIES(i) {
belugas@6390: if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CAN_SUBSIDENCE) && --index < 0) {
tron@534: SetDParam(0, i->town->index);
truelight@193: AddNewsItem(STR_B005_COAL_MINE_SUBSIDENCE_LEAVES,
rubidium@9234: NS_ACCIDENT_TILE, i->xy + TileDiffXY(1, 1), 0);
truelight@193:
truelight@0: {
tron@1977: TileIndex tile = i->xy;
rubidium@7317: TileIndexDiff step = TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2));
tron@2648: uint n;
tron@1977:
Darkvater@2674: for (n = 0; n < 30; n++) {
truelight@0: DisasterClearSquare(tile);
truelight@0: tile = TILE_MASK(tile + step);
tron@2648: }
truelight@0: }
truelight@0: return;
truelight@0: }
truelight@0: }
tron@2648: }
truelight@0: }
truelight@0:
truelight@0: static DisasterInitProc * const _disaster_initprocs[] = {
Darkvater@5695: Disaster_Zeppeliner_Init,
Darkvater@5700: Disaster_Small_Ufo_Init,
Darkvater@5695: Disaster_Airplane_Init,
Darkvater@5695: Disaster_Helicopter_Init,
Darkvater@5695: Disaster_Big_Ufo_Init,
Darkvater@5700: Disaster_Small_Submarine_Init,
Darkvater@5700: Disaster_Big_Submarine_Init,
Darkvater@5695: Disaster_CoalMine_Init,
truelight@0: };
truelight@0:
tron@2655: static const struct {
rubidium@4293: Year min;
rubidium@4293: Year max;
tron@2655: } _dis_years[] = {
belugas@6125: { 1930, 1955 }, ///< zeppeliner
belugas@6125: { 1940, 1970 }, ///< ufo (small)
belugas@6125: { 1960, 1990 }, ///< airplane
belugas@6125: { 1970, 2000 }, ///< helicopter
belugas@6125: { 2000, 2100 }, ///< ufo (big)
belugas@6125: { 1940, 1965 }, ///< submarine (small)
belugas@6125: { 1975, 2010 }, ///< submarine (big)
belugas@6125: { 1950, 1985 } ///< coalmine
truelight@0: };
truelight@0:
truelight@0:
rubidium@6247: static void DoDisaster()
truelight@0: {
tron@2639: byte buf[lengthof(_dis_years)];
tron@2639: uint i;
tron@2639: uint j;
truelight@0:
tron@2639: j = 0;
tron@2639: for (i = 0; i != lengthof(_dis_years); i++) {
rubidium@4293: if (_cur_year >= _dis_years[i].min && _cur_year < _dis_years[i].max) buf[j++] = i;
truelight@0: }
truelight@0:
tron@2639: if (j == 0) return;
truelight@0:
tron@2637: _disaster_initprocs[buf[RandomRange(j)]]();
truelight@0: }
truelight@0:
truelight@0:
rubidium@6247: static void ResetDisasterDelay()
truelight@0: {
tron@2484: _disaster_delay = GB(Random(), 0, 9) + 730;
truelight@0: }
truelight@0:
rubidium@6247: void DisasterDailyLoop()
truelight@0: {
tron@2639: if (--_disaster_delay != 0) return;
truelight@0:
truelight@0: ResetDisasterDelay();
truelight@0:
tron@2639: if (_opt.diff.disasters != 0) DoDisaster();
truelight@0: }
truelight@0:
rubidium@6247: void StartupDisasters()
tron@1093: {
truelight@0: ResetDisasterDelay();
truelight@0: }
rubidium@6558:
rubidium@6558: void DisasterVehicle::UpdateDeltaXY(Direction direction)
rubidium@6558: {
rubidium@6558: this->x_offs = -1;
rubidium@6558: this->y_offs = -1;
frosch@8793: this->x_extent = 2;
frosch@8793: this->y_extent = 2;
frosch@8793: this->z_extent = 5;
rubidium@6558: }