00001
00002
00019 #include "stdafx.h"
00020 #include "openttd.h"
00021 #include "landscape.h"
00022
00023 #include "industry_map.h"
00024 #include "station_map.h"
00025 #include "command_func.h"
00026 #include "tile_cmd.h"
00027 #include "news.h"
00028 #include "station.h"
00029 #include "waypoint.h"
00030 #include "town.h"
00031 #include "industry.h"
00032 #include "player_func.h"
00033 #include "airport.h"
00034 #include "variables.h"
00035 #include "settings_type.h"
00036 #include "strings_func.h"
00037 #include "date_func.h"
00038 #include "functions.h"
00039 #include "vehicle_func.h"
00040 #include "vehicle_base.h"
00041 #include "sound_func.h"
00042 #include "roadveh.h"
00043
00044 #include "table/strings.h"
00045 #include "table/sprites.h"
00046
00047 enum DisasterSubType {
00048 ST_Zeppeliner,
00049 ST_Zeppeliner_Shadow,
00050 ST_Small_Ufo,
00051 ST_Small_Ufo_Shadow,
00052 ST_Airplane,
00053 ST_Airplane_Shadow,
00054 ST_Helicopter,
00055 ST_Helicopter_Shadow,
00056 ST_Helicopter_Rotors,
00057 ST_Big_Ufo,
00058 ST_Big_Ufo_Shadow,
00059 ST_Big_Ufo_Destroyer,
00060 ST_Big_Ufo_Destroyer_Shadow,
00061 ST_Small_Submarine,
00062 ST_Big_Submarine,
00063 };
00064
00065 static void DisasterClearSquare(TileIndex tile)
00066 {
00067 if (!EnsureNoVehicleOnGround(tile)) return;
00068
00069 switch (GetTileType(tile)) {
00070 case MP_RAILWAY:
00071 if (IsHumanPlayer(GetTileOwner(tile)) && !IsRailWaypoint(tile)) {
00072 PlayerID p = _current_player;
00073 _current_player = OWNER_WATER;
00074 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
00075 _current_player = p;
00076
00077
00078 UpdateSignalsInBuffer();
00079 }
00080 break;
00081
00082 case MP_HOUSE: {
00083 PlayerID p = _current_player;
00084 _current_player = OWNER_NONE;
00085 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
00086 _current_player = p;
00087 break;
00088 }
00089
00090 case MP_TREES:
00091 case MP_CLEAR:
00092 DoClearSquare(tile);
00093 break;
00094
00095 default:
00096 break;
00097 }
00098 }
00099
00100 static const SpriteID _disaster_images_1[] = {SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP};
00101 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};
00102 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};
00103 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};
00104 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};
00105 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};
00106 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};
00107 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};
00108 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};
00109
00110 static const SpriteID * const _disaster_images[] = {
00111 _disaster_images_1, _disaster_images_1,
00112 _disaster_images_2, _disaster_images_2,
00113 _disaster_images_3, _disaster_images_3,
00114 _disaster_images_8, _disaster_images_8, _disaster_images_9,
00115 _disaster_images_6, _disaster_images_6,
00116 _disaster_images_7, _disaster_images_7,
00117 _disaster_images_4, _disaster_images_5,
00118 };
00119
00120 static void DisasterVehicleUpdateImage(Vehicle *v)
00121 {
00122 SpriteID img = v->u.disaster.image_override;
00123 if (img == 0) img = _disaster_images[v->subtype][v->direction];
00124 v->cur_image = img;
00125 }
00126
00129 static void InitializeDisasterVehicle(Vehicle *v, int x, int y, byte z, Direction direction, byte subtype)
00130 {
00131 v->x_pos = x;
00132 v->y_pos = y;
00133 v->z_pos = z;
00134 v->tile = TileVirtXY(x, y);
00135 v->direction = direction;
00136 v->subtype = subtype;
00137 v->UpdateDeltaXY(INVALID_DIR);
00138 v->owner = OWNER_NONE;
00139 v->vehstatus = VS_UNCLICKABLE;
00140 v->u.disaster.image_override = 0;
00141 v->current_order.Free();
00142
00143 DisasterVehicleUpdateImage(v);
00144 VehiclePositionChanged(v);
00145 MarkSingleVehicleDirty(v);
00146 }
00147
00148 static void DeleteDisasterVeh(Vehicle *v)
00149 {
00150 DeleteVehicleChain(v);
00151 }
00152
00153 static void SetDisasterVehiclePos(Vehicle *v, int x, int y, byte z)
00154 {
00155 Vehicle *u;
00156
00157 BeginVehicleMove(v);
00158 v->x_pos = x;
00159 v->y_pos = y;
00160 v->z_pos = z;
00161 v->tile = TileVirtXY(x, y);
00162
00163 DisasterVehicleUpdateImage(v);
00164 VehiclePositionChanged(v);
00165 EndVehicleMove(v);
00166
00167 if ((u = v->Next()) != NULL) {
00168 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00169 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00170 BeginVehicleMove(u);
00171
00172 u->x_pos = x;
00173 u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0U) >> 3);
00174 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00175 u->z_pos = GetSlopeZ(safe_x, safe_y);
00176 u->direction = v->direction;
00177
00178 DisasterVehicleUpdateImage(u);
00179 VehiclePositionChanged(u);
00180 EndVehicleMove(u);
00181
00182 if ((u = u->Next()) != NULL) {
00183 BeginVehicleMove(u);
00184 u->x_pos = x;
00185 u->y_pos = y;
00186 u->z_pos = z + 5;
00187 VehiclePositionChanged(u);
00188 EndVehicleMove(u);
00189 }
00190 }
00191 }
00192
00201 static void DisasterTick_Zeppeliner(Vehicle *v)
00202 {
00203 Station *st;
00204 int x, y;
00205 byte z;
00206 TileIndex tile;
00207
00208 v->tick_counter++;
00209
00210 if (v->current_order.dest < 2) {
00211 if (HasBit(v->tick_counter, 0)) return;
00212
00213 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00214
00215 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00216
00217 if (v->current_order.dest == 1) {
00218 if (++v->age == 38) {
00219 v->current_order.dest = 2;
00220 v->age = 0;
00221 }
00222
00223 if (GB(v->tick_counter, 0, 3) == 0) CreateEffectVehicleRel(v, 0, -17, 2, EV_SMOKE);
00224
00225 } else if (v->current_order.dest == 0) {
00226 tile = v->tile;
00227
00228 if (IsValidTile(tile) &&
00229 IsTileType(tile, MP_STATION) &&
00230 IsAirport(tile) &&
00231 IsHumanPlayer(GetTileOwner(tile))) {
00232 v->current_order.dest = 1;
00233 v->age = 0;
00234
00235 SetDParam(0, GetStationIndex(tile));
00236 AddNewsItem(STR_B000_ZEPPELIN_DISASTER_AT,
00237 NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0),
00238 v->index,
00239 0);
00240 }
00241 }
00242
00243 if (v->y_pos >= ((int)MapSizeY() + 9) * TILE_SIZE - 1) DeleteDisasterVeh(v);
00244 return;
00245 }
00246
00247 if (v->current_order.dest > 2) {
00248 if (++v->age <= 13320) return;
00249
00250 tile = v->tile;
00251
00252 if (IsValidTile(tile) &&
00253 IsTileType(tile, MP_STATION) &&
00254 IsAirport(tile) &&
00255 IsHumanPlayer(GetTileOwner(tile))) {
00256 st = GetStationByTile(tile);
00257 CLRBITS(st->airport_flags, RUNWAY_IN_block);
00258 }
00259
00260 SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos);
00261 DeleteDisasterVeh(v);
00262 return;
00263 }
00264
00265 x = v->x_pos;
00266 y = v->y_pos;
00267 z = GetSlopeZ(x, y);
00268 if (z < v->z_pos) z = v->z_pos - 1;
00269 SetDisasterVehiclePos(v, x, y, z);
00270
00271 if (++v->age == 1) {
00272 CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
00273 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00274 v->u.disaster.image_override = SPR_BLIMP_CRASHING;
00275 } else if (v->age == 70) {
00276 v->u.disaster.image_override = SPR_BLIMP_CRASHED;
00277 } else if (v->age <= 300) {
00278 if (GB(v->tick_counter, 0, 3) == 0) {
00279 uint32 r = Random();
00280
00281 CreateEffectVehicleRel(v,
00282 GB(r, 0, 4) - 7,
00283 GB(r, 4, 4) - 7,
00284 GB(r, 8, 3) + 5,
00285 EV_EXPLOSION_SMALL);
00286 }
00287 } else if (v->age == 350) {
00288 v->current_order.dest = 3;
00289 v->age = 0;
00290 }
00291
00292 tile = v->tile;
00293 if (IsValidTile(tile) &&
00294 IsTileType(tile, MP_STATION) &&
00295 IsAirport(tile) &&
00296 IsHumanPlayer(GetTileOwner(tile))) {
00297 st = GetStationByTile(tile);
00298 SETBITS(st->airport_flags, RUNWAY_IN_block);
00299 }
00300 }
00301
00308 static void DisasterTick_Ufo(Vehicle *v)
00309 {
00310 Vehicle *u;
00311 uint dist;
00312 byte z;
00313
00314 v->u.disaster.image_override = (HasBit(++v->tick_counter, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
00315
00316 if (v->current_order.dest == 0) {
00317
00318 int x = TileX(v->dest_tile) * TILE_SIZE;
00319 int y = TileY(v->dest_tile) * TILE_SIZE;
00320 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= TILE_SIZE) {
00321 v->direction = GetDirectionTowards(v, x, y);
00322 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00323 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00324 return;
00325 }
00326 if (++v->age < 6) {
00327 v->dest_tile = RandomTile();
00328 return;
00329 }
00330 v->current_order.dest = 1;
00331
00332 FOR_ALL_VEHICLES(u) {
00333 if (u->type == VEH_ROAD && IsRoadVehFront(u) && IsHumanPlayer(u->owner)) {
00334 v->dest_tile = u->index;
00335 v->age = 0;
00336 return;
00337 }
00338 }
00339
00340 DeleteDisasterVeh(v);
00341 } else {
00342
00343 u = GetVehicle(v->dest_tile);
00344 if (u->type != VEH_ROAD || !IsRoadVehFront(u)) {
00345 DeleteDisasterVeh(v);
00346 return;
00347 }
00348
00349 dist = Delta(v->x_pos, u->x_pos) + Delta(v->y_pos, u->y_pos);
00350
00351 if (dist < TILE_SIZE && !(u->vehstatus & VS_HIDDEN) && u->breakdown_ctr == 0) {
00352 u->breakdown_ctr = 3;
00353 u->breakdown_delay = 140;
00354 }
00355
00356 v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
00357 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00358
00359 z = v->z_pos;
00360 if (dist <= TILE_SIZE && z > u->z_pos) z--;
00361 SetDisasterVehiclePos(v, gp.x, gp.y, z);
00362
00363 if (z <= u->z_pos && (u->vehstatus & VS_HIDDEN) == 0) {
00364 v->age++;
00365 if (u->u.road.crashed_ctr == 0) {
00366 u->u.road.crashed_ctr++;
00367
00368 AddNewsItem(STR_B001_ROAD_VEHICLE_DESTROYED,
00369 NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0),
00370 u->index,
00371 0);
00372
00373 for (Vehicle *w = u; w != NULL; w = w->Next()) {
00374 w->vehstatus |= VS_CRASHED;
00375 MarkSingleVehicleDirty(w);
00376 }
00377 }
00378 }
00379
00380
00381 if (v->age > 50) {
00382 CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
00383 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00384 DeleteDisasterVeh(v);
00385 }
00386 }
00387 }
00388
00389 static void DestructIndustry(Industry *i)
00390 {
00391 TileIndex tile;
00392
00393 for (tile = 0; tile != MapSize(); tile++) {
00394 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) {
00395 ResetIndustryConstructionStage(tile);
00396 MarkTileDirtyByTile(tile);
00397 }
00398 }
00399 }
00400
00409 static void DisasterTick_Airplane(Vehicle *v)
00410 {
00411 v->tick_counter++;
00412 v->u.disaster.image_override =
00413 (v->current_order.dest == 1 && HasBit(v->tick_counter, 2)) ? SPR_F_15_FIRING : 0;
00414
00415 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00416 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00417
00418 if (gp.x < (-10 * TILE_SIZE)) {
00419 DeleteDisasterVeh(v);
00420 return;
00421 }
00422
00423 if (v->current_order.dest == 2) {
00424 if (GB(v->tick_counter, 0, 2) == 0) {
00425 Industry *i = GetIndustry(v->dest_tile);
00426 int x = TileX(i->xy) * TILE_SIZE;
00427 int y = TileY(i->xy) * TILE_SIZE;
00428 uint32 r = Random();
00429
00430 CreateEffectVehicleAbove(
00431 GB(r, 0, 6) + x,
00432 GB(r, 6, 6) + y,
00433 GB(r, 12, 4),
00434 EV_EXPLOSION_SMALL);
00435
00436 if (++v->age >= 55) v->current_order.dest = 3;
00437 }
00438 } else if (v->current_order.dest == 1) {
00439 if (++v->age == 112) {
00440 Industry *i;
00441
00442 v->current_order.dest = 2;
00443 v->age = 0;
00444
00445 i = GetIndustry(v->dest_tile);
00446 DestructIndustry(i);
00447
00448 SetDParam(0, i->town->index);
00449 AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, 0), i->xy, 0);
00450 SndPlayTileFx(SND_12_EXPLOSION, i->xy);
00451 }
00452 } else if (v->current_order.dest == 0) {
00453 int x, y;
00454 TileIndex tile;
00455 uint ind;
00456
00457 x = v->x_pos - (15 * TILE_SIZE);
00458 y = v->y_pos;
00459
00460 if ( (uint)x > MapMaxX() * TILE_SIZE - 1) return;
00461
00462 tile = TileVirtXY(x, y);
00463 if (!IsTileType(tile, MP_INDUSTRY)) return;
00464
00465 ind = GetIndustryIndex(tile);
00466 v->dest_tile = ind;
00467
00468 if (GetIndustrySpec(GetIndustry(ind)->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) {
00469 v->current_order.dest = 1;
00470 v->age = 0;
00471 }
00472 }
00473 }
00474
00482 static void DisasterTick_Helicopter(Vehicle *v)
00483 {
00484 v->tick_counter++;
00485 v->u.disaster.image_override =
00486 (v->current_order.dest == 1 && HasBit(v->tick_counter, 2)) ? SPR_AH_64A_FIRING : 0;
00487
00488 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00489 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00490
00491 if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
00492 DeleteDisasterVeh(v);
00493 return;
00494 }
00495
00496 if (v->current_order.dest == 2) {
00497 if (GB(v->tick_counter, 0, 2) == 0) {
00498 Industry *i = GetIndustry(v->dest_tile);
00499 int x = TileX(i->xy) * TILE_SIZE;
00500 int y = TileY(i->xy) * TILE_SIZE;
00501 uint32 r = Random();
00502
00503 CreateEffectVehicleAbove(
00504 GB(r, 0, 6) + x,
00505 GB(r, 6, 6) + y,
00506 GB(r, 12, 4),
00507 EV_EXPLOSION_SMALL);
00508
00509 if (++v->age >= 55) v->current_order.dest = 3;
00510 }
00511 } else if (v->current_order.dest == 1) {
00512 if (++v->age == 112) {
00513 Industry *i;
00514
00515 v->current_order.dest = 2;
00516 v->age = 0;
00517
00518 i = GetIndustry(v->dest_tile);
00519 DestructIndustry(i);
00520
00521 SetDParam(0, i->town->index);
00522 AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, 0), i->xy, 0);
00523 SndPlayTileFx(SND_12_EXPLOSION, i->xy);
00524 }
00525 } else if (v->current_order.dest == 0) {
00526 int x, y;
00527 TileIndex tile;
00528 uint ind;
00529
00530 x = v->x_pos + (15 * TILE_SIZE);
00531 y = v->y_pos;
00532
00533 if ( (uint)x > MapMaxX() * TILE_SIZE - 1) return;
00534
00535 tile = TileVirtXY(x, y);
00536 if (!IsTileType(tile, MP_INDUSTRY)) return;
00537
00538 ind = GetIndustryIndex(tile);
00539 v->dest_tile = ind;
00540
00541 if (GetIndustrySpec(GetIndustry(ind)->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) {
00542 v->current_order.dest = 1;
00543 v->age = 0;
00544 }
00545 }
00546 }
00547
00549 static void DisasterTick_Helicopter_Rotors(Vehicle *v)
00550 {
00551 v->tick_counter++;
00552 if (HasBit(v->tick_counter, 0)) return;
00553
00554 if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
00555
00556 VehiclePositionChanged(v);
00557 MarkSingleVehicleDirty(v);
00558 }
00559
00566 static void DisasterTick_Big_Ufo(Vehicle *v)
00567 {
00568 byte z;
00569 Vehicle *u, *w;
00570 Town *t;
00571 TileIndex tile;
00572 TileIndex tile_org;
00573
00574 v->tick_counter++;
00575
00576 if (v->current_order.dest == 1) {
00577 int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
00578 int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
00579 if (Delta(v->x_pos, x) + Delta(v->y_pos, y) >= 8) {
00580 v->direction = GetDirectionTowards(v, x, y);
00581
00582 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00583 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00584 return;
00585 }
00586
00587 z = GetSlopeZ(v->x_pos, v->y_pos);
00588 if (z < v->z_pos) {
00589 SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
00590 return;
00591 }
00592
00593 v->current_order.dest = 2;
00594
00595 FOR_ALL_VEHICLES(u) {
00596 if (u->type == VEH_TRAIN || u->type == VEH_ROAD) {
00597 if (Delta(u->x_pos, v->x_pos) + Delta(u->y_pos, v->y_pos) <= 12 * TILE_SIZE) {
00598 u->breakdown_ctr = 5;
00599 u->breakdown_delay = 0xF0;
00600 }
00601 }
00602 }
00603
00604 t = ClosestTownFromTile(v->dest_tile, (uint)-1);
00605 SetDParam(0, t->index);
00606 AddNewsItem(STR_B004_UFO_LANDS_NEAR,
00607 NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, 0),
00608 v->tile,
00609 0);
00610
00611 u = new DisasterVehicle();
00612 if (u == NULL) {
00613 DeleteDisasterVeh(v);
00614 return;
00615 }
00616
00617 InitializeDisasterVehicle(u, -6 * TILE_SIZE, v->y_pos, 135, DIR_SW, ST_Big_Ufo_Destroyer);
00618 u->u.disaster.big_ufo_destroyer_target = v->index;
00619
00620 w = new DisasterVehicle();
00621 if (w == NULL) return;
00622
00623 u->SetNext(w);
00624 InitializeDisasterVehicle(w, -6 * TILE_SIZE, v->y_pos, 0, DIR_SW, ST_Big_Ufo_Destroyer_Shadow);
00625 w->vehstatus |= VS_SHADOW;
00626 } else if (v->current_order.dest == 0) {
00627 int x = TileX(v->dest_tile) * TILE_SIZE;
00628 int y = TileY(v->dest_tile) * TILE_SIZE;
00629 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= TILE_SIZE) {
00630 v->direction = GetDirectionTowards(v, x, y);
00631 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00632 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00633 return;
00634 }
00635
00636 if (++v->age < 6) {
00637 v->dest_tile = RandomTile();
00638 return;
00639 }
00640 v->current_order.dest = 1;
00641
00642 tile_org = tile = RandomTile();
00643 do {
00644 if (IsTileType(tile, MP_RAILWAY) &&
00645 IsPlainRailTile(tile) &&
00646 IsHumanPlayer(GetTileOwner(tile))) {
00647 break;
00648 }
00649 tile = TILE_MASK(tile + 1);
00650 } while (tile != tile_org);
00651 v->dest_tile = tile;
00652 v->age = 0;
00653 } else {
00654 return;
00655 }
00656 }
00657
00662 static void DisasterTick_Big_Ufo_Destroyer(Vehicle *v)
00663 {
00664 Vehicle *u;
00665 int i;
00666
00667 v->tick_counter++;
00668
00669 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00670 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00671
00672 if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
00673 DeleteDisasterVeh(v);
00674 return;
00675 }
00676
00677 if (v->current_order.dest == 0) {
00678 u = GetVehicle(v->u.disaster.big_ufo_destroyer_target);
00679 if (Delta(v->x_pos, u->x_pos) > TILE_SIZE) return;
00680 v->current_order.dest = 1;
00681
00682 CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
00683 SndPlayVehicleFx(SND_12_EXPLOSION, u);
00684
00685 DeleteDisasterVeh(u);
00686
00687 for (i = 0; i != 80; i++) {
00688 uint32 r = Random();
00689 CreateEffectVehicleAbove(
00690 GB(r, 0, 6) + v->x_pos - 32,
00691 GB(r, 5, 6) + v->y_pos - 32,
00692 0,
00693 EV_EXPLOSION_SMALL);
00694 }
00695
00696 BEGIN_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
00697 tile = TILE_MASK(tile);
00698 DisasterClearSquare(tile);
00699 END_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
00700 }
00701 }
00702
00707 static void DisasterTick_Submarine(Vehicle *v)
00708 {
00709 TileIndex tile;
00710
00711 v->tick_counter++;
00712
00713 if (++v->age > 8880) {
00714 VehiclePositionChanged(v);
00715 MarkSingleVehicleDirty(v);
00716 delete v;
00717 return;
00718 }
00719
00720 if (!HasBit(v->tick_counter, 0)) return;
00721
00722 tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
00723 if (IsValidTile(tile)) {
00724 TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
00725 if (trackbits == TRACK_BIT_ALL && !Chance16(1, 90)) {
00726 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00727 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00728 return;
00729 }
00730 }
00731
00732 v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
00733 }
00734
00735
00736 static void DisasterTick_NULL(Vehicle *v) {}
00737 typedef void DisasterVehicleTickProc(Vehicle *v);
00738
00739 static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
00740 DisasterTick_Zeppeliner, DisasterTick_NULL,
00741 DisasterTick_Ufo, DisasterTick_NULL,
00742 DisasterTick_Airplane, DisasterTick_NULL,
00743 DisasterTick_Helicopter, DisasterTick_NULL, DisasterTick_Helicopter_Rotors,
00744 DisasterTick_Big_Ufo, DisasterTick_NULL, DisasterTick_Big_Ufo_Destroyer,
00745 DisasterTick_NULL,
00746 DisasterTick_Submarine,
00747 DisasterTick_Submarine,
00748 };
00749
00750
00751 void DisasterVehicle::Tick()
00752 {
00753 _disastervehicle_tick_procs[this->subtype](this);
00754 }
00755
00756 typedef void DisasterInitProc();
00757
00758
00761 static void Disaster_Zeppeliner_Init()
00762 {
00763 Vehicle *v = new DisasterVehicle(), *u;
00764 Station *st;
00765 int x;
00766
00767 if (v == NULL) return;
00768
00769
00770 x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00771
00772 FOR_ALL_STATIONS(st) {
00773 if (st->airport_tile != 0 &&
00774 st->airport_type <= 1 &&
00775 IsHumanPlayer(st->owner)) {
00776 x = (TileX(st->xy) + 2) * TILE_SIZE;
00777 break;
00778 }
00779 }
00780
00781 InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_Zeppeliner);
00782
00783
00784 u = new DisasterVehicle();
00785 if (u != NULL) {
00786 v->SetNext(u);
00787 InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_Zeppeliner_Shadow);
00788 u->vehstatus |= VS_SHADOW;
00789 }
00790 }
00791
00792
00795 static void Disaster_Small_Ufo_Init()
00796 {
00797 Vehicle *v = new DisasterVehicle(), *u;
00798 int x;
00799
00800 if (v == NULL) return;
00801
00802 x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00803
00804 InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_Small_Ufo);
00805 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
00806 v->age = 0;
00807
00808
00809 u = new DisasterVehicle();
00810 if (u != NULL) {
00811 v->SetNext(u);
00812 InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_Small_Ufo_Shadow);
00813 u->vehstatus |= VS_SHADOW;
00814 }
00815 }
00816
00817
00818
00819 static void Disaster_Airplane_Init()
00820 {
00821 Industry *i, *found;
00822 Vehicle *v, *u;
00823 int x, y;
00824
00825 found = NULL;
00826
00827 FOR_ALL_INDUSTRIES(i) {
00828 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) &&
00829 (found == NULL || Chance16(1, 2))) {
00830 found = i;
00831 }
00832 }
00833
00834 if (found == NULL) return;
00835
00836 v = new DisasterVehicle();
00837 if (v == NULL) return;
00838
00839
00840 x = (MapSizeX() + 9) * TILE_SIZE - 1;
00841 y = TileY(found->xy) * TILE_SIZE + 37;
00842
00843 InitializeDisasterVehicle(v, x, y, 135, DIR_NE, ST_Airplane);
00844
00845 u = new DisasterVehicle();
00846 if (u != NULL) {
00847 v->SetNext(u);
00848 InitializeDisasterVehicle(u, x, y, 0, DIR_SE, ST_Airplane_Shadow);
00849 u->vehstatus |= VS_SHADOW;
00850 }
00851 }
00852
00853
00855 static void Disaster_Helicopter_Init()
00856 {
00857 Industry *i, *found;
00858 Vehicle *v, *u, *w;
00859 int x, y;
00860
00861 found = NULL;
00862
00863 FOR_ALL_INDUSTRIES(i) {
00864 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) &&
00865 (found == NULL || Chance16(1, 2))) {
00866 found = i;
00867 }
00868 }
00869
00870 if (found == NULL) return;
00871
00872 v = new DisasterVehicle();
00873 if (v == NULL) return;
00874
00875 x = -16 * TILE_SIZE;
00876 y = TileY(found->xy) * TILE_SIZE + 37;
00877
00878 InitializeDisasterVehicle(v, x, y, 135, DIR_SW, ST_Helicopter);
00879
00880 u = new DisasterVehicle();
00881 if (u != NULL) {
00882 v->SetNext(u);
00883 InitializeDisasterVehicle(u, x, y, 0, DIR_SW, ST_Helicopter_Shadow);
00884 u->vehstatus |= VS_SHADOW;
00885
00886 w = new DisasterVehicle();
00887 if (w != NULL) {
00888 u->SetNext(w);
00889 InitializeDisasterVehicle(w, x, y, 140, DIR_SW, ST_Helicopter_Rotors);
00890 }
00891 }
00892 }
00893
00894
00895
00896
00897 static void Disaster_Big_Ufo_Init()
00898 {
00899 Vehicle *v = new DisasterVehicle(), *u;
00900 int x, y;
00901
00902 if (v == NULL) return;
00903
00904 x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00905
00906 y = MapMaxX() * TILE_SIZE - 1;
00907 InitializeDisasterVehicle(v, x, y, 135, DIR_NW, ST_Big_Ufo);
00908 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
00909 v->age = 0;
00910
00911
00912 u = new DisasterVehicle();
00913 if (u != NULL) {
00914 v->SetNext(u);
00915 InitializeDisasterVehicle(u, x, y, 0, DIR_NW, ST_Big_Ufo_Shadow);
00916 u->vehstatus |= VS_SHADOW;
00917 }
00918 }
00919
00920
00921
00922 static void Disaster_Small_Submarine_Init()
00923 {
00924 Vehicle *v = new DisasterVehicle();
00925 int x, y;
00926 Direction dir;
00927 uint32 r;
00928
00929 if (v == NULL) return;
00930
00931 r = Random();
00932 x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
00933
00934 if (HasBit(r, 31)) {
00935 y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
00936 dir = DIR_NW;
00937 } else {
00938 y = TILE_SIZE / 2;
00939 dir = DIR_SE;
00940 }
00941 InitializeDisasterVehicle(v, x, y, 0, dir, ST_Small_Submarine);
00942 v->age = 0;
00943 }
00944
00945
00946
00947 static void Disaster_Big_Submarine_Init()
00948 {
00949 Vehicle *v = new DisasterVehicle();
00950 int x, y;
00951 Direction dir;
00952 uint32 r;
00953
00954 if (v == NULL) return;
00955
00956 r = Random();
00957 x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
00958
00959 if (HasBit(r, 31)) {
00960 y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
00961 dir = DIR_NW;
00962 } else {
00963 y = TILE_SIZE / 2;
00964 dir = DIR_SE;
00965 }
00966 InitializeDisasterVehicle(v, x, y, 0, dir, ST_Big_Submarine);
00967 v->age = 0;
00968 }
00969
00970
00973 static void Disaster_CoalMine_Init()
00974 {
00975 int index = GB(Random(), 0, 4);
00976 uint m;
00977
00978 for (m = 0; m < 15; m++) {
00979 const Industry *i;
00980
00981 FOR_ALL_INDUSTRIES(i) {
00982 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CAN_SUBSIDENCE) && --index < 0) {
00983 SetDParam(0, i->town->index);
00984 AddNewsItem(STR_B005_COAL_MINE_SUBSIDENCE_LEAVES,
00985 NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, 0), i->xy + TileDiffXY(1, 1), 0);
00986
00987 {
00988 TileIndex tile = i->xy;
00989 TileIndexDiff step = TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2));
00990 uint n;
00991
00992 for (n = 0; n < 30; n++) {
00993 DisasterClearSquare(tile);
00994 tile = TILE_MASK(tile + step);
00995 }
00996 }
00997 return;
00998 }
00999 }
01000 }
01001 }
01002
01003 static DisasterInitProc * const _disaster_initprocs[] = {
01004 Disaster_Zeppeliner_Init,
01005 Disaster_Small_Ufo_Init,
01006 Disaster_Airplane_Init,
01007 Disaster_Helicopter_Init,
01008 Disaster_Big_Ufo_Init,
01009 Disaster_Small_Submarine_Init,
01010 Disaster_Big_Submarine_Init,
01011 Disaster_CoalMine_Init,
01012 };
01013
01014 static const struct {
01015 Year min;
01016 Year max;
01017 } _dis_years[] = {
01018 { 1930, 1955 },
01019 { 1940, 1970 },
01020 { 1960, 1990 },
01021 { 1970, 2000 },
01022 { 2000, 2100 },
01023 { 1940, 1965 },
01024 { 1975, 2010 },
01025 { 1950, 1985 }
01026 };
01027
01028
01029 static void DoDisaster()
01030 {
01031 byte buf[lengthof(_dis_years)];
01032 uint i;
01033 uint j;
01034
01035 j = 0;
01036 for (i = 0; i != lengthof(_dis_years); i++) {
01037 if (_cur_year >= _dis_years[i].min && _cur_year < _dis_years[i].max) buf[j++] = i;
01038 }
01039
01040 if (j == 0) return;
01041
01042 _disaster_initprocs[buf[RandomRange(j)]]();
01043 }
01044
01045
01046 static void ResetDisasterDelay()
01047 {
01048 _disaster_delay = GB(Random(), 0, 9) + 730;
01049 }
01050
01051 void DisasterDailyLoop()
01052 {
01053 if (--_disaster_delay != 0) return;
01054
01055 ResetDisasterDelay();
01056
01057 if (_opt.diff.disasters != 0) DoDisaster();
01058 }
01059
01060 void StartupDisasters()
01061 {
01062 ResetDisasterDelay();
01063 }
01064
01065 void DisasterVehicle::UpdateDeltaXY(Direction direction)
01066 {
01067 this->x_offs = -1;
01068 this->y_offs = -1;
01069 this->sprite_width = 2;
01070 this->sprite_height = 2;
01071 this->z_height = 5;
01072 }