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