00001
00002
00003
00004
00005
00006
00007
00008
00009
00026 #include "stdafx.h"
00027 #include "landscape.h"
00028
00029 #include "industry.h"
00030 #include "station_base.h"
00031 #include "command_func.h"
00032 #include "news_func.h"
00033 #include "town.h"
00034 #include "company_func.h"
00035 #include "variables.h"
00036 #include "strings_func.h"
00037 #include "date_func.h"
00038 #include "functions.h"
00039 #include "vehicle_func.h"
00040 #include "sound_func.h"
00041 #include "effectvehicle_func.h"
00042 #include "roadveh.h"
00043 #include "ai/ai.hpp"
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 (Company::IsHumanID(GetTileOwner(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(DisasterVehicle *v)
00122 {
00123 SpriteID img = v->image_override;
00124 if (img == 0) img = _disaster_images[v->subtype][v->direction];
00125 v->cur_image = img;
00126 }
00127
00130 static void InitializeDisasterVehicle(DisasterVehicle *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->image_override = 0;
00142 v->current_order.Free();
00143
00144 DisasterVehicleUpdateImage(v);
00145 VehicleMove(v, false);
00146 MarkSingleVehicleDirty(v);
00147 }
00148
00149 static void SetDisasterVehiclePos(DisasterVehicle *v, int x, int y, byte z)
00150 {
00151 v->x_pos = x;
00152 v->y_pos = y;
00153 v->z_pos = z;
00154 v->tile = TileVirtXY(x, y);
00155
00156 DisasterVehicleUpdateImage(v);
00157 VehicleMove(v, true);
00158
00159 DisasterVehicle *u = v->Next();
00160 if (u != NULL) {
00161 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00162 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00163
00164 u->x_pos = x;
00165 u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0U) >> 3);
00166 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00167 u->z_pos = GetSlopeZ(safe_x, safe_y);
00168 u->direction = v->direction;
00169
00170 DisasterVehicleUpdateImage(u);
00171 VehicleMove(u, true);
00172
00173 if ((u = u->Next()) != NULL) {
00174 u->x_pos = x;
00175 u->y_pos = y;
00176 u->z_pos = z + 5;
00177 VehicleMove(u, true);
00178 }
00179 }
00180 }
00181
00190 static bool DisasterTick_Zeppeliner(DisasterVehicle *v)
00191 {
00192 v->tick_counter++;
00193
00194 if (v->current_order.GetDestination() < 2) {
00195 if (HasBit(v->tick_counter, 0)) return true;
00196
00197 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00198
00199 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00200
00201 if (v->current_order.GetDestination() == 1) {
00202 if (++v->age == 38) {
00203 v->current_order.SetDestination(2);
00204 v->age = 0;
00205 }
00206
00207 if (GB(v->tick_counter, 0, 3) == 0) CreateEffectVehicleRel(v, 0, -17, 2, EV_SMOKE);
00208
00209 } else if (v->current_order.GetDestination() == 0) {
00210 if (IsValidTile(v->tile) &&
00211 IsTileType(v->tile, MP_STATION) &&
00212 IsAirport(v->tile)) {
00213 v->current_order.SetDestination(1);
00214 v->age = 0;
00215
00216 SetDParam(0, GetStationIndex(v->tile));
00217 AddVehicleNewsItem(STR_NEWS_DISASTER_ZEPPELIN,
00218 NS_ACCIDENT,
00219 v->index);
00220 AI::NewEvent(GetTileOwner(v->tile), new AIEventDisasterZeppelinerCrashed(GetStationIndex(v->tile)));
00221 }
00222 }
00223
00224 if (v->y_pos >= ((int)MapSizeY() + 9) * TILE_SIZE - 1) {
00225 delete v;
00226 return false;
00227 }
00228
00229 return true;
00230 }
00231
00232 if (v->current_order.GetDestination() > 2) {
00233 if (++v->age <= 13320) return true;
00234
00235 if (IsValidTile(v->tile) &&
00236 IsTileType(v->tile, MP_STATION) &&
00237 IsAirport(v->tile)) {
00238 Station *st = Station::GetByTile(v->tile);
00239 CLRBITS(st->airport_flags, RUNWAY_IN_block);
00240 AI::NewEvent(GetTileOwner(v->tile), new AIEventDisasterZeppelinerCleared(st->index));
00241 }
00242
00243 SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos);
00244 delete v;
00245 return false;
00246 }
00247
00248 int x = v->x_pos;
00249 int y = v->y_pos;
00250 byte z = GetSlopeZ(x, y);
00251 if (z < v->z_pos) z = v->z_pos - 1;
00252 SetDisasterVehiclePos(v, x, y, z);
00253
00254 if (++v->age == 1) {
00255 CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
00256 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00257 v->image_override = SPR_BLIMP_CRASHING;
00258 } else if (v->age == 70) {
00259 v->image_override = SPR_BLIMP_CRASHED;
00260 } else if (v->age <= 300) {
00261 if (GB(v->tick_counter, 0, 3) == 0) {
00262 uint32 r = Random();
00263
00264 CreateEffectVehicleRel(v,
00265 GB(r, 0, 4) - 7,
00266 GB(r, 4, 4) - 7,
00267 GB(r, 8, 3) + 5,
00268 EV_EXPLOSION_SMALL);
00269 }
00270 } else if (v->age == 350) {
00271 v->current_order.SetDestination(3);
00272 v->age = 0;
00273 }
00274
00275 if (IsValidTile(v->tile) &&
00276 IsTileType(v->tile, MP_STATION) &&
00277 IsAirport(v->tile)) {
00278 SETBITS(Station::GetByTile(v->tile)->airport_flags, RUNWAY_IN_block);
00279 }
00280
00281 return true;
00282 }
00283
00290 static bool DisasterTick_Ufo(DisasterVehicle *v)
00291 {
00292 v->image_override = (HasBit(++v->tick_counter, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
00293
00294 if (v->current_order.GetDestination() == 0) {
00295
00296 int x = TileX(v->dest_tile) * TILE_SIZE;
00297 int y = TileY(v->dest_tile) * TILE_SIZE;
00298 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= TILE_SIZE) {
00299 v->direction = GetDirectionTowards(v, x, y);
00300 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00301 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00302 return true;
00303 }
00304 if (++v->age < 6) {
00305 v->dest_tile = RandomTile();
00306 return true;
00307 }
00308 v->current_order.SetDestination(1);
00309
00310 RoadVehicle *u;
00311 FOR_ALL_ROADVEHICLES(u) {
00312 if (u->IsRoadVehFront()) {
00313 v->dest_tile = u->index;
00314 v->age = 0;
00315 return true;
00316 }
00317 }
00318
00319 delete v;
00320 return false;
00321 } else {
00322
00323 RoadVehicle *u = RoadVehicle::Get(v->dest_tile);
00324 assert(u != NULL && u->type == VEH_ROAD && u->IsRoadVehFront());
00325
00326 uint dist = Delta(v->x_pos, u->x_pos) + Delta(v->y_pos, u->y_pos);
00327
00328 if (dist < TILE_SIZE && !(u->vehstatus & VS_HIDDEN) && u->breakdown_ctr == 0) {
00329 u->breakdown_ctr = 3;
00330 u->breakdown_delay = 140;
00331 }
00332
00333 v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
00334 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00335
00336 byte z = v->z_pos;
00337 if (dist <= TILE_SIZE && z > u->z_pos) z--;
00338 SetDisasterVehiclePos(v, gp.x, gp.y, z);
00339
00340 if (z <= u->z_pos && (u->vehstatus & VS_HIDDEN) == 0) {
00341 v->age++;
00342 if (u->crashed_ctr == 0) {
00343 u->Crash();
00344
00345 AddVehicleNewsItem(STR_NEWS_DISASTER_SMALL_UFO,
00346 NS_ACCIDENT,
00347 u->index);
00348
00349 AI::NewEvent(u->owner, new AIEventVehicleCrashed(u->index, u->tile, AIEventVehicleCrashed::CRASH_RV_UFO));
00350 }
00351 }
00352
00353
00354 if (v->age > 50) {
00355 CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
00356 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00357 delete v;
00358 return false;
00359 }
00360 }
00361
00362 return true;
00363 }
00364
00365 static void DestructIndustry(Industry *i)
00366 {
00367 for (TileIndex tile = 0; tile != MapSize(); tile++) {
00368 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) {
00369 ResetIndustryConstructionStage(tile);
00370 MarkTileDirtyByTile(tile);
00371 }
00372 }
00373 }
00374
00388 static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16 image_override, bool leave_at_top, StringID news_message, IndustryBehaviour industry_flag)
00389 {
00390 v->tick_counter++;
00391 v->image_override = (v->current_order.GetDestination() == 1 && HasBit(v->tick_counter, 2)) ? image_override : 0;
00392
00393 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00394 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00395
00396 if ((leave_at_top && gp.x < (-10 * TILE_SIZE)) || (!leave_at_top && gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1)) {
00397 delete v;
00398 return false;
00399 }
00400
00401 if (v->current_order.GetDestination() == 2) {
00402 if (GB(v->tick_counter, 0, 2) == 0) {
00403 Industry *i = Industry::Get(v->dest_tile);
00404 int x = TileX(i->xy) * TILE_SIZE;
00405 int y = TileY(i->xy) * TILE_SIZE;
00406 uint32 r = Random();
00407
00408 CreateEffectVehicleAbove(
00409 GB(r, 0, 6) + x,
00410 GB(r, 6, 6) + y,
00411 GB(r, 12, 4),
00412 EV_EXPLOSION_SMALL);
00413
00414 if (++v->age >= 55) v->current_order.SetDestination(3);
00415 }
00416 } else if (v->current_order.GetDestination() == 1) {
00417 if (++v->age == 112) {
00418 v->current_order.SetDestination(2);
00419 v->age = 0;
00420
00421 Industry *i = Industry::Get(v->dest_tile);
00422 DestructIndustry(i);
00423
00424 SetDParam(0, i->town->index);
00425 AddIndustryNewsItem(news_message, NS_ACCIDENT, i->index);
00426 SndPlayTileFx(SND_12_EXPLOSION, i->xy);
00427 }
00428 } else if (v->current_order.GetDestination() == 0) {
00429 int x = v->x_pos - (15 * TILE_SIZE);
00430 int y = v->y_pos;
00431
00432 if ((uint)x > MapMaxX() * TILE_SIZE - 1) return true;
00433
00434 TileIndex tile = TileVirtXY(x, y);
00435 if (!IsTileType(tile, MP_INDUSTRY)) return true;
00436
00437 IndustryID ind = GetIndustryIndex(tile);
00438 v->dest_tile = ind;
00439
00440 if (GetIndustrySpec(Industry::Get(ind)->type)->behaviour & industry_flag) {
00441 v->current_order.SetDestination(1);
00442 v->age = 0;
00443 }
00444 }
00445
00446 return true;
00447 }
00448
00450 static bool DisasterTick_Airplane(DisasterVehicle *v)
00451 {
00452 return DisasterTick_Aircraft(v, SPR_F_15_FIRING, true, STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY, INDUSTRYBEH_AIRPLANE_ATTACKS);
00453 }
00454
00456 static bool DisasterTick_Helicopter(DisasterVehicle *v)
00457 {
00458 return DisasterTick_Aircraft(v, SPR_AH_64A_FIRING, false, STR_NEWS_DISASTER_HELICOPTER_FACTORY, INDUSTRYBEH_CHOPPER_ATTACKS);
00459 }
00460
00462 static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
00463 {
00464 v->tick_counter++;
00465 if (HasBit(v->tick_counter, 0)) return true;
00466
00467 if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
00468
00469 VehicleMove(v, true);
00470
00471 return true;
00472 }
00473
00480 static bool DisasterTick_Big_Ufo(DisasterVehicle *v)
00481 {
00482 v->tick_counter++;
00483
00484 if (v->current_order.GetDestination() == 1) {
00485 int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
00486 int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
00487 if (Delta(v->x_pos, x) + Delta(v->y_pos, y) >= 8) {
00488 v->direction = GetDirectionTowards(v, x, y);
00489
00490 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00491 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00492 return true;
00493 }
00494
00495 if (!IsValidTile(v->dest_tile)) {
00496
00497 delete v;
00498 return false;
00499 }
00500
00501 byte z = GetSlopeZ(v->x_pos, v->y_pos);
00502 if (z < v->z_pos) {
00503 SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
00504 return true;
00505 }
00506
00507 v->current_order.SetDestination(2);
00508
00509 Vehicle *target;
00510 FOR_ALL_VEHICLES(target) {
00511 if (target->type == VEH_TRAIN || target->type == VEH_ROAD) {
00512 if (Delta(target->x_pos, v->x_pos) + Delta(target->y_pos, v->y_pos) <= 12 * TILE_SIZE) {
00513 target->breakdown_ctr = 5;
00514 target->breakdown_delay = 0xF0;
00515 }
00516 }
00517 }
00518
00519 Town *t = ClosestTownFromTile(v->dest_tile, UINT_MAX);
00520 SetDParam(0, t->index);
00521 AddNewsItem(STR_NEWS_DISASTER_BIG_UFO,
00522 NS_ACCIDENT,
00523 NR_TILE,
00524 v->tile);
00525
00526 if (!Vehicle::CanAllocateItem(2)) {
00527 delete v;
00528 return false;
00529 }
00530 DisasterVehicle *u = new DisasterVehicle();
00531
00532 InitializeDisasterVehicle(u, -6 * TILE_SIZE, v->y_pos, 135, DIR_SW, ST_BIG_UFO_DESTROYER);
00533 u->big_ufo_destroyer_target = v->index;
00534
00535 DisasterVehicle *w = new DisasterVehicle();
00536
00537 u->SetNext(w);
00538 InitializeDisasterVehicle(w, -6 * TILE_SIZE, v->y_pos, 0, DIR_SW, ST_BIG_UFO_DESTROYER_SHADOW);
00539 w->vehstatus |= VS_SHADOW;
00540 } else if (v->current_order.GetDestination() == 0) {
00541 int x = TileX(v->dest_tile) * TILE_SIZE;
00542 int y = TileY(v->dest_tile) * TILE_SIZE;
00543 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= TILE_SIZE) {
00544 v->direction = GetDirectionTowards(v, x, y);
00545 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00546 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00547 return true;
00548 }
00549
00550 if (++v->age < 6) {
00551 v->dest_tile = RandomTile();
00552 return true;
00553 }
00554 v->current_order.SetDestination(1);
00555
00556 TileIndex tile_org = RandomTile();
00557 TileIndex tile = tile_org;
00558 do {
00559 if (IsPlainRailTile(tile) &&
00560 Company::IsHumanID(GetTileOwner(tile))) {
00561 break;
00562 }
00563 tile = TILE_MASK(tile + 1);
00564 } while (tile != tile_org);
00565 v->dest_tile = tile;
00566 v->age = 0;
00567 }
00568
00569 return true;
00570 }
00571
00576 static bool DisasterTick_Big_Ufo_Destroyer(DisasterVehicle *v)
00577 {
00578 v->tick_counter++;
00579
00580 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00581 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00582
00583 if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
00584 delete v;
00585 return false;
00586 }
00587
00588 if (v->current_order.GetDestination() == 0) {
00589 Vehicle *u = Vehicle::Get(v->big_ufo_destroyer_target);
00590 if (Delta(v->x_pos, u->x_pos) > TILE_SIZE) return true;
00591 v->current_order.SetDestination(1);
00592
00593 CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
00594 SndPlayVehicleFx(SND_12_EXPLOSION, u);
00595
00596 delete u;
00597
00598 for (int i = 0; i != 80; i++) {
00599 uint32 r = Random();
00600 CreateEffectVehicleAbove(
00601 GB(r, 0, 6) + v->x_pos - 32,
00602 GB(r, 5, 6) + v->y_pos - 32,
00603 0,
00604 EV_EXPLOSION_SMALL);
00605 }
00606
00607 for (int dy = -3; dy < 3; dy++) {
00608 for (int dx = -3; dx < 3; dx++) {
00609 TileIndex tile = TileAddWrap(v->tile, dx, dy);
00610 if (tile != INVALID_TILE) DisasterClearSquare(tile);
00611 }
00612 }
00613 }
00614
00615 return true;
00616 }
00617
00622 static bool DisasterTick_Submarine(DisasterVehicle *v)
00623 {
00624 v->tick_counter++;
00625
00626 if (++v->age > 8880) {
00627 delete v;
00628 return false;
00629 }
00630
00631 if (!HasBit(v->tick_counter, 0)) return true;
00632
00633 TileIndex tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
00634 if (IsValidTile(tile)) {
00635 TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
00636 if (trackbits == TRACK_BIT_ALL && !Chance16(1, 90)) {
00637 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00638 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00639 return true;
00640 }
00641 }
00642
00643 v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
00644
00645 return true;
00646 }
00647
00648
00649 static bool DisasterTick_NULL(DisasterVehicle *v)
00650 {
00651 return true;
00652 }
00653
00654 typedef bool DisasterVehicleTickProc(DisasterVehicle *v);
00655
00656 static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
00657 DisasterTick_Zeppeliner, DisasterTick_NULL,
00658 DisasterTick_Ufo, DisasterTick_NULL,
00659 DisasterTick_Airplane, DisasterTick_NULL,
00660 DisasterTick_Helicopter, DisasterTick_NULL, DisasterTick_Helicopter_Rotors,
00661 DisasterTick_Big_Ufo, DisasterTick_NULL, DisasterTick_Big_Ufo_Destroyer,
00662 DisasterTick_NULL,
00663 DisasterTick_Submarine,
00664 DisasterTick_Submarine,
00665 };
00666
00667
00668 bool DisasterVehicle::Tick()
00669 {
00670 return _disastervehicle_tick_procs[this->subtype](this);
00671 }
00672
00673 typedef void DisasterInitProc();
00674
00675
00678 static void Disaster_Zeppeliner_Init()
00679 {
00680 if (!Vehicle::CanAllocateItem(2)) return;
00681
00682
00683 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00684
00685 Station *st;
00686 FOR_ALL_STATIONS(st) {
00687 if (st->airport_tile != INVALID_TILE && (st->airport_type == AT_SMALL || st->airport_type == AT_LARGE)) {
00688 x = (TileX(st->airport_tile) + 2) * TILE_SIZE;
00689 break;
00690 }
00691 }
00692
00693 DisasterVehicle *v = new DisasterVehicle();
00694 InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_ZEPPELINER);
00695
00696
00697 DisasterVehicle *u = new DisasterVehicle();
00698 v->SetNext(u);
00699 InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_ZEPPELINER_SHADOW);
00700 u->vehstatus |= VS_SHADOW;
00701 }
00702
00703
00706 static void Disaster_Small_Ufo_Init()
00707 {
00708 if (!Vehicle::CanAllocateItem(2)) return;
00709
00710 DisasterVehicle *v = new DisasterVehicle();
00711 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00712
00713 InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_SMALL_UFO);
00714 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
00715 v->age = 0;
00716
00717
00718 DisasterVehicle *u = new DisasterVehicle();
00719 v->SetNext(u);
00720 InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_SMALL_UFO_SHADOW);
00721 u->vehstatus |= VS_SHADOW;
00722 }
00723
00724
00725
00726 static void Disaster_Airplane_Init()
00727 {
00728 if (!Vehicle::CanAllocateItem(2)) return;
00729
00730 Industry *i, *found = NULL;
00731
00732 FOR_ALL_INDUSTRIES(i) {
00733 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) &&
00734 (found == NULL || Chance16(1, 2))) {
00735 found = i;
00736 }
00737 }
00738
00739 if (found == NULL) return;
00740
00741 DisasterVehicle *v = new DisasterVehicle();
00742
00743
00744 int x = (MapSizeX() + 9) * TILE_SIZE - 1;
00745 int y = TileY(found->xy) * TILE_SIZE + 37;
00746
00747 InitializeDisasterVehicle(v, x, y, 135, DIR_NE, ST_AIRPLANE);
00748
00749 DisasterVehicle *u = new DisasterVehicle();
00750 v->SetNext(u);
00751 InitializeDisasterVehicle(u, x, y, 0, DIR_SE, ST_AIRPLANE_SHADOW);
00752 u->vehstatus |= VS_SHADOW;
00753 }
00754
00755
00757 static void Disaster_Helicopter_Init()
00758 {
00759 if (!Vehicle::CanAllocateItem(3)) return;
00760
00761 Industry *i, *found = NULL;
00762
00763 FOR_ALL_INDUSTRIES(i) {
00764 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) &&
00765 (found == NULL || Chance16(1, 2))) {
00766 found = i;
00767 }
00768 }
00769
00770 if (found == NULL) return;
00771
00772 DisasterVehicle *v = new DisasterVehicle();
00773
00774 int x = -16 * TILE_SIZE;
00775 int y = TileY(found->xy) * TILE_SIZE + 37;
00776
00777 InitializeDisasterVehicle(v, x, y, 135, DIR_SW, ST_HELICOPTER);
00778
00779 DisasterVehicle *u = new DisasterVehicle();
00780 v->SetNext(u);
00781 InitializeDisasterVehicle(u, x, y, 0, DIR_SW, ST_HELICOPTER_SHADOW);
00782 u->vehstatus |= VS_SHADOW;
00783
00784 DisasterVehicle *w = new DisasterVehicle();
00785 u->SetNext(w);
00786 InitializeDisasterVehicle(w, x, y, 140, DIR_SW, ST_HELICOPTER_ROTORS);
00787 }
00788
00789
00790
00791
00792 static void Disaster_Big_Ufo_Init()
00793 {
00794 if (!Vehicle::CanAllocateItem(2)) return;
00795
00796 DisasterVehicle *v = new DisasterVehicle();
00797 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00798 int y = MapMaxX() * TILE_SIZE - 1;
00799
00800 InitializeDisasterVehicle(v, x, y, 135, DIR_NW, ST_BIG_UFO);
00801 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
00802 v->age = 0;
00803
00804
00805 DisasterVehicle *u = new DisasterVehicle();
00806 v->SetNext(u);
00807 InitializeDisasterVehicle(u, x, y, 0, DIR_NW, ST_BIG_UFO_SHADOW);
00808 u->vehstatus |= VS_SHADOW;
00809 }
00810
00811
00812 static void Disaster_Submarine_Init(DisasterSubType subtype)
00813 {
00814 if (!Vehicle::CanAllocateItem()) return;
00815
00816 int y;
00817 Direction dir;
00818 uint32 r = Random();
00819 int x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
00820
00821 if (HasBit(r, 31)) {
00822 y = MapMaxY() * TILE_SIZE - TILE_SIZE / 2 - 1;
00823 dir = DIR_NW;
00824 } else {
00825 y = TILE_SIZE / 2;
00826 if (_settings_game.construction.freeform_edges) y += TILE_SIZE;
00827 dir = DIR_SE;
00828 }
00829 if (!IsWaterTile(TileVirtXY(x, y))) return;
00830
00831 DisasterVehicle *v = new DisasterVehicle();
00832 InitializeDisasterVehicle(v, x, y, 0, dir, subtype);
00833 v->age = 0;
00834 }
00835
00836
00837 static void Disaster_Small_Submarine_Init()
00838 {
00839 Disaster_Submarine_Init(ST_SMALL_SUBMARINE);
00840 }
00841
00842
00843
00844 static void Disaster_Big_Submarine_Init()
00845 {
00846 Disaster_Submarine_Init(ST_BIG_SUBMARINE);
00847 }
00848
00849
00852 static void Disaster_CoalMine_Init()
00853 {
00854 int index = GB(Random(), 0, 4);
00855 uint m;
00856
00857 for (m = 0; m < 15; m++) {
00858 const Industry *i;
00859
00860 FOR_ALL_INDUSTRIES(i) {
00861 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CAN_SUBSIDENCE) && --index < 0) {
00862 SetDParam(0, i->town->index);
00863 AddNewsItem(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE,
00864 NS_ACCIDENT, NR_TILE, i->xy + TileDiffXY(1, 1));
00865
00866 {
00867 TileIndex tile = i->xy;
00868 TileIndexDiff step = TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2));
00869
00870 for (uint n = 0; n < 30; n++) {
00871 DisasterClearSquare(tile);
00872 tile += step;
00873 if (!IsValidTile(tile)) break;
00874 }
00875 }
00876 return;
00877 }
00878 }
00879 }
00880 }
00881
00882 struct Disaster {
00883 DisasterInitProc *init_proc;
00884 Year min_year;
00885 Year max_year;
00886 };
00887
00888 static const Disaster _disasters[] = {
00889 {Disaster_Zeppeliner_Init, 1930, 1955},
00890 {Disaster_Small_Ufo_Init, 1940, 1970},
00891 {Disaster_Airplane_Init, 1960, 1990},
00892 {Disaster_Helicopter_Init, 1970, 2000},
00893 {Disaster_Big_Ufo_Init, 2000, 2100},
00894 {Disaster_Small_Submarine_Init, 1940, 1965},
00895 {Disaster_Big_Submarine_Init, 1975, 2010},
00896 {Disaster_CoalMine_Init, 1950, 1985},
00897 };
00898
00899 static void DoDisaster()
00900 {
00901 byte buf[lengthof(_disasters)];
00902
00903 byte j = 0;
00904 for (size_t i = 0; i != lengthof(_disasters); i++) {
00905 if (_cur_year >= _disasters[i].min_year && _cur_year < _disasters[i].max_year) buf[j++] = (byte)i;
00906 }
00907
00908 if (j == 0) return;
00909
00910 _disasters[buf[RandomRange(j)]].init_proc();
00911 }
00912
00913
00914 static void ResetDisasterDelay()
00915 {
00916 _disaster_delay = GB(Random(), 0, 9) + 730;
00917 }
00918
00919 void DisasterDailyLoop()
00920 {
00921 if (--_disaster_delay != 0) return;
00922
00923 ResetDisasterDelay();
00924
00925 if (_settings_game.difficulty.disasters != 0) DoDisaster();
00926 }
00927
00928 void StartupDisasters()
00929 {
00930 ResetDisasterDelay();
00931 }
00932
00937 void ReleaseDisastersTargetingIndustry(IndustryID i)
00938 {
00939 DisasterVehicle *v;
00940 FOR_ALL_DISASTERVEHICLES(v) {
00941
00942 if (v->subtype == ST_AIRPLANE || v->subtype == ST_HELICOPTER) {
00943
00944 if (v->current_order.GetDestination() > 0 && v->dest_tile == i) v->current_order.SetDestination(3);
00945 }
00946 }
00947 }
00948
00952 void ReleaseDisastersTargetingVehicle(VehicleID vehicle)
00953 {
00954 DisasterVehicle *v;
00955 FOR_ALL_DISASTERVEHICLES(v) {
00956
00957 if (v->subtype == ST_SMALL_UFO) {
00958 if (v->current_order.GetDestination() != 0 && v->dest_tile == vehicle) {
00959
00960 v->current_order.SetDestination(0);
00961 v->dest_tile = RandomTile();
00962 v->z_pos = 135;
00963 v->age = 0;
00964 }
00965 }
00966 }
00967 }
00968
00969 void DisasterVehicle::UpdateDeltaXY(Direction direction)
00970 {
00971 this->x_offs = -1;
00972 this->y_offs = -1;
00973 this->x_extent = 2;
00974 this->y_extent = 2;
00975 this->z_extent = 5;
00976 }