00001
00002
00003
00004
00005
00006
00007
00008
00009
00027 #include "stdafx.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 "strings_func.h"
00036 #include "date_func.h"
00037 #include "functions.h"
00038 #include "vehicle_func.h"
00039 #include "sound_func.h"
00040 #include "effectvehicle_func.h"
00041 #include "roadveh.h"
00042 #include "ai/ai.hpp"
00043 #include "company_base.h"
00044 #include "core/random_func.hpp"
00045 #include "core/backup_type.hpp"
00046
00047 #include "table/strings.h"
00048
00050 uint16 _disaster_delay;
00051
00052 enum DisasterSubType {
00053 ST_ZEPPELINER,
00054 ST_ZEPPELINER_SHADOW,
00055 ST_SMALL_UFO,
00056 ST_SMALL_UFO_SHADOW,
00057 ST_AIRPLANE,
00058 ST_AIRPLANE_SHADOW,
00059 ST_HELICOPTER,
00060 ST_HELICOPTER_SHADOW,
00061 ST_HELICOPTER_ROTORS,
00062 ST_BIG_UFO,
00063 ST_BIG_UFO_SHADOW,
00064 ST_BIG_UFO_DESTROYER,
00065 ST_BIG_UFO_DESTROYER_SHADOW,
00066 ST_SMALL_SUBMARINE,
00067 ST_BIG_SUBMARINE,
00068 };
00069
00070 static void DisasterClearSquare(TileIndex tile)
00071 {
00072 if (EnsureNoVehicleOnGround(tile).Failed()) return;
00073
00074 switch (GetTileType(tile)) {
00075 case MP_RAILWAY:
00076 if (Company::IsHumanID(GetTileOwner(tile))) {
00077 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
00078 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
00079 cur_company.Restore();
00080
00081
00082 UpdateSignalsInBuffer();
00083 }
00084 break;
00085
00086 case MP_HOUSE: {
00087 Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
00088 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
00089 cur_company.Restore();
00090 break;
00091 }
00092
00093 case MP_TREES:
00094 case MP_CLEAR:
00095 DoClearSquare(tile);
00096 break;
00097
00098 default:
00099 break;
00100 }
00101 }
00102
00103 static const SpriteID _disaster_images_1[] = {SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP, SPR_BLIMP};
00104 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};
00105 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};
00106 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};
00107 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};
00108 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};
00109 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};
00110 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};
00111 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};
00112
00113 static const SpriteID * const _disaster_images[] = {
00114 _disaster_images_1, _disaster_images_1,
00115 _disaster_images_2, _disaster_images_2,
00116 _disaster_images_3, _disaster_images_3,
00117 _disaster_images_8, _disaster_images_8, _disaster_images_9,
00118 _disaster_images_6, _disaster_images_6,
00119 _disaster_images_7, _disaster_images_7,
00120 _disaster_images_4, _disaster_images_5,
00121 };
00122
00123 static void DisasterVehicleUpdateImage(DisasterVehicle *v)
00124 {
00125 SpriteID img = v->image_override;
00126 if (img == 0) img = _disaster_images[v->subtype][v->direction];
00127 v->cur_image = img;
00128 }
00129
00134 static void InitializeDisasterVehicle(DisasterVehicle *v, int x, int y, byte z, Direction direction, byte subtype)
00135 {
00136 v->x_pos = x;
00137 v->y_pos = y;
00138 v->z_pos = z;
00139 v->tile = TileVirtXY(x, y);
00140 v->direction = direction;
00141 v->subtype = subtype;
00142 v->UpdateDeltaXY(INVALID_DIR);
00143 v->owner = OWNER_NONE;
00144 v->vehstatus = VS_UNCLICKABLE;
00145 v->image_override = 0;
00146 v->current_order.Free();
00147
00148 DisasterVehicleUpdateImage(v);
00149 VehicleMove(v, false);
00150 MarkSingleVehicleDirty(v);
00151 }
00152
00153 static void SetDisasterVehiclePos(DisasterVehicle *v, int x, int y, byte z)
00154 {
00155 v->x_pos = x;
00156 v->y_pos = y;
00157 v->z_pos = z;
00158 v->tile = TileVirtXY(x, y);
00159
00160 DisasterVehicleUpdateImage(v);
00161 VehicleMove(v, true);
00162
00163 DisasterVehicle *u = v->Next();
00164 if (u != NULL) {
00165 int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE);
00166 int safe_y = Clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
00167
00168 u->x_pos = x;
00169 u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0U) >> 3);
00170 safe_y = Clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
00171 u->z_pos = GetSlopeZ(safe_x, safe_y);
00172 u->direction = v->direction;
00173
00174 DisasterVehicleUpdateImage(u);
00175 VehicleMove(u, true);
00176
00177 if ((u = u->Next()) != NULL) {
00178 u->x_pos = x;
00179 u->y_pos = y;
00180 u->z_pos = z + 5;
00181 VehicleMove(u, true);
00182 }
00183 }
00184 }
00185
00194 static bool DisasterTick_Zeppeliner(DisasterVehicle *v)
00195 {
00196 v->tick_counter++;
00197
00198 if (v->current_order.GetDestination() < 2) {
00199 if (HasBit(v->tick_counter, 0)) return true;
00200
00201 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00202
00203 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00204
00205 if (v->current_order.GetDestination() == 1) {
00206 if (++v->age == 38) {
00207 v->current_order.SetDestination(2);
00208 v->age = 0;
00209 }
00210
00211 if (GB(v->tick_counter, 0, 3) == 0) CreateEffectVehicleRel(v, 0, -17, 2, EV_SMOKE);
00212
00213 } else if (v->current_order.GetDestination() == 0) {
00214 if (IsValidTile(v->tile) && IsAirportTile(v->tile)) {
00215 v->current_order.SetDestination(1);
00216 v->age = 0;
00217
00218 SetDParam(0, GetStationIndex(v->tile));
00219 AddVehicleNewsItem(STR_NEWS_DISASTER_ZEPPELIN,
00220 NS_ACCIDENT,
00221 v->index);
00222 AI::NewEvent(GetTileOwner(v->tile), new AIEventDisasterZeppelinerCrashed(GetStationIndex(v->tile)));
00223 }
00224 }
00225
00226 if (v->y_pos >= (int)((MapSizeY() + 9) * TILE_SIZE - 1)) {
00227 delete v;
00228 return false;
00229 }
00230
00231 return true;
00232 }
00233
00234 if (v->current_order.GetDestination() > 2) {
00235 if (++v->age <= 13320) return true;
00236
00237 if (IsValidTile(v->tile) && IsAirportTile(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) && IsAirportTile(v->tile)) {
00276 SETBITS(Station::GetByTile(v->tile)->airport.flags, RUNWAY_IN_block);
00277 }
00278
00279 return true;
00280 }
00281
00288 static bool DisasterTick_Ufo(DisasterVehicle *v)
00289 {
00290 v->image_override = (HasBit(++v->tick_counter, 3)) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
00291
00292 if (v->current_order.GetDestination() == 0) {
00293
00294 int x = TileX(v->dest_tile) * TILE_SIZE;
00295 int y = TileY(v->dest_tile) * TILE_SIZE;
00296 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= (int)TILE_SIZE) {
00297 v->direction = GetDirectionTowards(v, x, y);
00298 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00299 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00300 return true;
00301 }
00302 if (++v->age < 6) {
00303 v->dest_tile = RandomTile();
00304 return true;
00305 }
00306 v->current_order.SetDestination(1);
00307
00308 RoadVehicle *u;
00309 FOR_ALL_ROADVEHICLES(u) {
00310 if (u->IsRoadVehFront()) {
00311 v->dest_tile = u->index;
00312 v->age = 0;
00313 return true;
00314 }
00315 }
00316
00317 delete v;
00318 return false;
00319 } else {
00320
00321 RoadVehicle *u = RoadVehicle::Get(v->dest_tile);
00322 assert(u != NULL && u->type == VEH_ROAD && u->IsRoadVehFront());
00323
00324 uint dist = Delta(v->x_pos, u->x_pos) + Delta(v->y_pos, u->y_pos);
00325
00326 if (dist < TILE_SIZE && !(u->vehstatus & VS_HIDDEN) && u->breakdown_ctr == 0) {
00327 u->breakdown_ctr = 3;
00328 u->breakdown_delay = 140;
00329 }
00330
00331 v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
00332 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00333
00334 byte z = v->z_pos;
00335 if (dist <= TILE_SIZE && z > u->z_pos) z--;
00336 SetDisasterVehiclePos(v, gp.x, gp.y, z);
00337
00338 if (z <= u->z_pos && (u->vehstatus & VS_HIDDEN) == 0) {
00339 v->age++;
00340 if (u->crashed_ctr == 0) {
00341 u->Crash();
00342
00343 AddVehicleNewsItem(STR_NEWS_DISASTER_SMALL_UFO,
00344 NS_ACCIDENT,
00345 u->index);
00346
00347 AI::NewEvent(u->owner, new AIEventVehicleCrashed(u->index, u->tile, AIEventVehicleCrashed::CRASH_RV_UFO));
00348 }
00349 }
00350
00351
00352 if (v->age > 50) {
00353 CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
00354 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00355 delete v;
00356 return false;
00357 }
00358 }
00359
00360 return true;
00361 }
00362
00363 static void DestructIndustry(Industry *i)
00364 {
00365 for (TileIndex tile = 0; tile != MapSize(); tile++) {
00366 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) {
00367 ResetIndustryConstructionStage(tile);
00368 MarkTileDirtyByTile(tile);
00369 }
00370 }
00371 }
00372
00386 static bool DisasterTick_Aircraft(DisasterVehicle *v, uint16 image_override, bool leave_at_top, StringID news_message, IndustryBehaviour industry_flag)
00387 {
00388 v->tick_counter++;
00389 v->image_override = (v->current_order.GetDestination() == 1 && HasBit(v->tick_counter, 2)) ? image_override : 0;
00390
00391 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00392 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00393
00394 if ((leave_at_top && gp.x < (-10 * (int)TILE_SIZE)) || (!leave_at_top && gp.x > (int)(MapSizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1)) {
00395 delete v;
00396 return false;
00397 }
00398
00399 if (v->current_order.GetDestination() == 2) {
00400 if (GB(v->tick_counter, 0, 2) == 0) {
00401 Industry *i = Industry::Get(v->dest_tile);
00402 int x = TileX(i->location.tile) * TILE_SIZE;
00403 int y = TileY(i->location.tile) * TILE_SIZE;
00404 uint32 r = Random();
00405
00406 CreateEffectVehicleAbove(
00407 GB(r, 0, 6) + x,
00408 GB(r, 6, 6) + y,
00409 GB(r, 12, 4),
00410 EV_EXPLOSION_SMALL);
00411
00412 if (++v->age >= 55) v->current_order.SetDestination(3);
00413 }
00414 } else if (v->current_order.GetDestination() == 1) {
00415 if (++v->age == 112) {
00416 v->current_order.SetDestination(2);
00417 v->age = 0;
00418
00419 Industry *i = Industry::Get(v->dest_tile);
00420 DestructIndustry(i);
00421
00422 SetDParam(0, i->town->index);
00423 AddIndustryNewsItem(news_message, NS_ACCIDENT, i->index);
00424 SndPlayTileFx(SND_12_EXPLOSION, i->location.tile);
00425 }
00426 } else if (v->current_order.GetDestination() == 0) {
00427 int x = v->x_pos + ((leave_at_top ? -15 : 15) * TILE_SIZE);
00428 int y = v->y_pos;
00429
00430 if ((uint)x > MapMaxX() * TILE_SIZE - 1) return true;
00431
00432 TileIndex tile = TileVirtXY(x, y);
00433 if (!IsTileType(tile, MP_INDUSTRY)) return true;
00434
00435 IndustryID ind = GetIndustryIndex(tile);
00436 v->dest_tile = ind;
00437
00438 if (GetIndustrySpec(Industry::Get(ind)->type)->behaviour & industry_flag) {
00439 v->current_order.SetDestination(1);
00440 v->age = 0;
00441 }
00442 }
00443
00444 return true;
00445 }
00446
00448 static bool DisasterTick_Airplane(DisasterVehicle *v)
00449 {
00450 return DisasterTick_Aircraft(v, SPR_F_15_FIRING, true, STR_NEWS_DISASTER_AIRPLANE_OIL_REFINERY, INDUSTRYBEH_AIRPLANE_ATTACKS);
00451 }
00452
00454 static bool DisasterTick_Helicopter(DisasterVehicle *v)
00455 {
00456 return DisasterTick_Aircraft(v, SPR_AH_64A_FIRING, false, STR_NEWS_DISASTER_HELICOPTER_FACTORY, INDUSTRYBEH_CHOPPER_ATTACKS);
00457 }
00458
00460 static bool DisasterTick_Helicopter_Rotors(DisasterVehicle *v)
00461 {
00462 v->tick_counter++;
00463 if (HasBit(v->tick_counter, 0)) return true;
00464
00465 if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
00466
00467 VehicleMove(v, true);
00468
00469 return true;
00470 }
00471
00478 static bool DisasterTick_Big_Ufo(DisasterVehicle *v)
00479 {
00480 v->tick_counter++;
00481
00482 if (v->current_order.GetDestination() == 1) {
00483 int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
00484 int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
00485 if (Delta(v->x_pos, x) + Delta(v->y_pos, y) >= 8) {
00486 v->direction = GetDirectionTowards(v, x, y);
00487
00488 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00489 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00490 return true;
00491 }
00492
00493 if (!IsValidTile(v->dest_tile)) {
00494
00495 delete v;
00496 return false;
00497 }
00498
00499 byte z = GetSlopeZ(v->x_pos, v->y_pos);
00500 if (z < v->z_pos) {
00501 SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
00502 return true;
00503 }
00504
00505 v->current_order.SetDestination(2);
00506
00507 Vehicle *target;
00508 FOR_ALL_VEHICLES(target) {
00509 if (target->IsGroundVehicle()) {
00510 if (Delta(target->x_pos, v->x_pos) + Delta(target->y_pos, v->y_pos) <= 12 * (int)TILE_SIZE) {
00511 target->breakdown_ctr = 5;
00512 target->breakdown_delay = 0xF0;
00513 }
00514 }
00515 }
00516
00517 Town *t = ClosestTownFromTile(v->dest_tile, UINT_MAX);
00518 SetDParam(0, t->index);
00519 AddNewsItem(STR_NEWS_DISASTER_BIG_UFO,
00520 NS_ACCIDENT,
00521 NR_TILE,
00522 v->tile);
00523
00524 if (!Vehicle::CanAllocateItem(2)) {
00525 delete v;
00526 return false;
00527 }
00528 DisasterVehicle *u = new DisasterVehicle();
00529
00530 InitializeDisasterVehicle(u, -6 * (int)TILE_SIZE, v->y_pos, 135, DIR_SW, ST_BIG_UFO_DESTROYER);
00531 u->big_ufo_destroyer_target = v->index;
00532
00533 DisasterVehicle *w = new DisasterVehicle();
00534
00535 u->SetNext(w);
00536 InitializeDisasterVehicle(w, -6 * (int)TILE_SIZE, v->y_pos, 0, DIR_SW, ST_BIG_UFO_DESTROYER_SHADOW);
00537 w->vehstatus |= VS_SHADOW;
00538 } else if (v->current_order.GetDestination() == 0) {
00539 int x = TileX(v->dest_tile) * TILE_SIZE;
00540 int y = TileY(v->dest_tile) * TILE_SIZE;
00541 if (Delta(x, v->x_pos) + Delta(y, v->y_pos) >= (int)TILE_SIZE) {
00542 v->direction = GetDirectionTowards(v, x, y);
00543 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00544 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00545 return true;
00546 }
00547
00548 if (++v->age < 6) {
00549 v->dest_tile = RandomTile();
00550 return true;
00551 }
00552 v->current_order.SetDestination(1);
00553
00554 TileIndex tile_org = RandomTile();
00555 TileIndex tile = tile_org;
00556 do {
00557 if (IsPlainRailTile(tile) &&
00558 Company::IsHumanID(GetTileOwner(tile))) {
00559 break;
00560 }
00561 tile = TILE_MASK(tile + 1);
00562 } while (tile != tile_org);
00563 v->dest_tile = tile;
00564 v->age = 0;
00565 }
00566
00567 return true;
00568 }
00569
00574 static bool DisasterTick_Big_Ufo_Destroyer(DisasterVehicle *v)
00575 {
00576 v->tick_counter++;
00577
00578 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00579 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00580
00581 if (gp.x > (int)(MapSizeX() * TILE_SIZE + 9 * TILE_SIZE) - 1) {
00582 delete v;
00583 return false;
00584 }
00585
00586 if (v->current_order.GetDestination() == 0) {
00587 Vehicle *u = Vehicle::Get(v->big_ufo_destroyer_target);
00588 if (Delta(v->x_pos, u->x_pos) > (int)TILE_SIZE) return true;
00589 v->current_order.SetDestination(1);
00590
00591 CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
00592 SndPlayVehicleFx(SND_12_EXPLOSION, u);
00593
00594 delete u;
00595
00596 for (int i = 0; i != 80; i++) {
00597 uint32 r = Random();
00598 CreateEffectVehicleAbove(
00599 GB(r, 0, 6) + v->x_pos - 32,
00600 GB(r, 5, 6) + v->y_pos - 32,
00601 0,
00602 EV_EXPLOSION_SMALL);
00603 }
00604
00605 for (int dy = -3; dy < 3; dy++) {
00606 for (int dx = -3; dx < 3; dx++) {
00607 TileIndex tile = TileAddWrap(v->tile, dx, dy);
00608 if (tile != INVALID_TILE) DisasterClearSquare(tile);
00609 }
00610 }
00611 }
00612
00613 return true;
00614 }
00615
00620 static bool DisasterTick_Submarine(DisasterVehicle *v)
00621 {
00622 v->tick_counter++;
00623
00624 if (++v->age > 8880) {
00625 delete v;
00626 return false;
00627 }
00628
00629 if (!HasBit(v->tick_counter, 0)) return true;
00630
00631 TileIndex tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
00632 if (IsValidTile(tile)) {
00633 TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
00634 if (trackbits == TRACK_BIT_ALL && !Chance16(1, 90)) {
00635 GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00636 SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
00637 return true;
00638 }
00639 }
00640
00641 v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
00642
00643 return true;
00644 }
00645
00646
00647 static bool DisasterTick_NULL(DisasterVehicle *v)
00648 {
00649 return true;
00650 }
00651
00652 typedef bool DisasterVehicleTickProc(DisasterVehicle *v);
00653
00654 static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
00655 DisasterTick_Zeppeliner, DisasterTick_NULL,
00656 DisasterTick_Ufo, DisasterTick_NULL,
00657 DisasterTick_Airplane, DisasterTick_NULL,
00658 DisasterTick_Helicopter, DisasterTick_NULL, DisasterTick_Helicopter_Rotors,
00659 DisasterTick_Big_Ufo, DisasterTick_NULL, DisasterTick_Big_Ufo_Destroyer,
00660 DisasterTick_NULL,
00661 DisasterTick_Submarine,
00662 DisasterTick_Submarine,
00663 };
00664
00665
00666 bool DisasterVehicle::Tick()
00667 {
00668 return _disastervehicle_tick_procs[this->subtype](this);
00669 }
00670
00671 typedef void DisasterInitProc();
00672
00673
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
00708 static void Disaster_Small_Ufo_Init()
00709 {
00710 if (!Vehicle::CanAllocateItem(2)) return;
00711
00712 DisasterVehicle *v = new DisasterVehicle();
00713 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00714
00715 InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_SMALL_UFO);
00716 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
00717 v->age = 0;
00718
00719
00720 DisasterVehicle *u = new DisasterVehicle();
00721 v->SetNext(u);
00722 InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_SMALL_UFO_SHADOW);
00723 u->vehstatus |= VS_SHADOW;
00724 }
00725
00726
00727
00728 static void Disaster_Airplane_Init()
00729 {
00730 if (!Vehicle::CanAllocateItem(2)) return;
00731
00732 Industry *i, *found = NULL;
00733
00734 FOR_ALL_INDUSTRIES(i) {
00735 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) &&
00736 (found == NULL || Chance16(1, 2))) {
00737 found = i;
00738 }
00739 }
00740
00741 if (found == NULL) return;
00742
00743 DisasterVehicle *v = new DisasterVehicle();
00744
00745
00746 int x = (MapSizeX() + 9) * TILE_SIZE - 1;
00747 int y = TileY(found->location.tile) * TILE_SIZE + 37;
00748
00749 InitializeDisasterVehicle(v, x, y, 135, DIR_NE, ST_AIRPLANE);
00750
00751 DisasterVehicle *u = new DisasterVehicle();
00752 v->SetNext(u);
00753 InitializeDisasterVehicle(u, x, y, 0, DIR_SE, ST_AIRPLANE_SHADOW);
00754 u->vehstatus |= VS_SHADOW;
00755 }
00756
00757
00759 static void Disaster_Helicopter_Init()
00760 {
00761 if (!Vehicle::CanAllocateItem(3)) return;
00762
00763 Industry *i, *found = NULL;
00764
00765 FOR_ALL_INDUSTRIES(i) {
00766 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) &&
00767 (found == NULL || Chance16(1, 2))) {
00768 found = i;
00769 }
00770 }
00771
00772 if (found == NULL) return;
00773
00774 DisasterVehicle *v = new DisasterVehicle();
00775
00776 int x = -16 * (int)TILE_SIZE;
00777 int y = TileY(found->location.tile) * TILE_SIZE + 37;
00778
00779 InitializeDisasterVehicle(v, x, y, 135, DIR_SW, ST_HELICOPTER);
00780
00781 DisasterVehicle *u = new DisasterVehicle();
00782 v->SetNext(u);
00783 InitializeDisasterVehicle(u, x, y, 0, DIR_SW, ST_HELICOPTER_SHADOW);
00784 u->vehstatus |= VS_SHADOW;
00785
00786 DisasterVehicle *w = new DisasterVehicle();
00787 u->SetNext(w);
00788 InitializeDisasterVehicle(w, x, y, 140, DIR_SW, ST_HELICOPTER_ROTORS);
00789 }
00790
00791
00792
00793
00794 static void Disaster_Big_Ufo_Init()
00795 {
00796 if (!Vehicle::CanAllocateItem(2)) return;
00797
00798 DisasterVehicle *v = new DisasterVehicle();
00799 int x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
00800 int y = MapMaxX() * TILE_SIZE - 1;
00801
00802 InitializeDisasterVehicle(v, x, y, 135, DIR_NW, ST_BIG_UFO);
00803 v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
00804 v->age = 0;
00805
00806
00807 DisasterVehicle *u = new DisasterVehicle();
00808 v->SetNext(u);
00809 InitializeDisasterVehicle(u, x, y, 0, DIR_NW, ST_BIG_UFO_SHADOW);
00810 u->vehstatus |= VS_SHADOW;
00811 }
00812
00813
00814 static void Disaster_Submarine_Init(DisasterSubType subtype)
00815 {
00816 if (!Vehicle::CanAllocateItem()) return;
00817
00818 int y;
00819 Direction dir;
00820 uint32 r = Random();
00821 int x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
00822
00823 if (HasBit(r, 31)) {
00824 y = MapMaxY() * TILE_SIZE - TILE_SIZE / 2 - 1;
00825 dir = DIR_NW;
00826 } else {
00827 y = TILE_SIZE / 2;
00828 if (_settings_game.construction.freeform_edges) y += TILE_SIZE;
00829 dir = DIR_SE;
00830 }
00831 if (!IsWaterTile(TileVirtXY(x, y))) return;
00832
00833 DisasterVehicle *v = new DisasterVehicle();
00834 InitializeDisasterVehicle(v, x, y, 0, dir, subtype);
00835 v->age = 0;
00836 }
00837
00838
00839 static void Disaster_Small_Submarine_Init()
00840 {
00841 Disaster_Submarine_Init(ST_SMALL_SUBMARINE);
00842 }
00843
00844
00845
00846 static void Disaster_Big_Submarine_Init()
00847 {
00848 Disaster_Submarine_Init(ST_BIG_SUBMARINE);
00849 }
00850
00851
00856 static void Disaster_CoalMine_Init()
00857 {
00858 int index = GB(Random(), 0, 4);
00859 uint m;
00860
00861 for (m = 0; m < 15; m++) {
00862 const Industry *i;
00863
00864 FOR_ALL_INDUSTRIES(i) {
00865 if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_CAN_SUBSIDENCE) && --index < 0) {
00866 SetDParam(0, i->town->index);
00867 AddNewsItem(STR_NEWS_DISASTER_COAL_MINE_SUBSIDENCE,
00868 NS_ACCIDENT, NR_TILE, i->location.tile + TileDiffXY(1, 1));
00869
00870 {
00871 TileIndex tile = i->location.tile;
00872 TileIndexDiff step = TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2));
00873
00874 for (uint n = 0; n < 30; n++) {
00875 DisasterClearSquare(tile);
00876 tile += step;
00877 if (!IsValidTile(tile)) break;
00878 }
00879 }
00880 return;
00881 }
00882 }
00883 }
00884 }
00885
00886 struct Disaster {
00887 DisasterInitProc *init_proc;
00888 Year min_year;
00889 Year max_year;
00890 };
00891
00892 static const Disaster _disasters[] = {
00893 {Disaster_Zeppeliner_Init, 1930, 1955},
00894 {Disaster_Small_Ufo_Init, 1940, 1970},
00895 {Disaster_Airplane_Init, 1960, 1990},
00896 {Disaster_Helicopter_Init, 1970, 2000},
00897 {Disaster_Big_Ufo_Init, 2000, 2100},
00898 {Disaster_Small_Submarine_Init, 1940, 1965},
00899 {Disaster_Big_Submarine_Init, 1975, 2010},
00900 {Disaster_CoalMine_Init, 1950, 1985},
00901 };
00902
00903 static void DoDisaster()
00904 {
00905 byte buf[lengthof(_disasters)];
00906
00907 byte j = 0;
00908 for (size_t i = 0; i != lengthof(_disasters); i++) {
00909 if (_cur_year >= _disasters[i].min_year && _cur_year < _disasters[i].max_year) buf[j++] = (byte)i;
00910 }
00911
00912 if (j == 0) return;
00913
00914 _disasters[buf[RandomRange(j)]].init_proc();
00915 }
00916
00917
00918 static void ResetDisasterDelay()
00919 {
00920 _disaster_delay = GB(Random(), 0, 9) + 730;
00921 }
00922
00923 void DisasterDailyLoop()
00924 {
00925 if (--_disaster_delay != 0) return;
00926
00927 ResetDisasterDelay();
00928
00929 if (_settings_game.difficulty.disasters != 0) DoDisaster();
00930 }
00931
00932 void StartupDisasters()
00933 {
00934 ResetDisasterDelay();
00935 }
00936
00942 void ReleaseDisastersTargetingIndustry(IndustryID i)
00943 {
00944 DisasterVehicle *v;
00945 FOR_ALL_DISASTERVEHICLES(v) {
00946
00947 if (v->subtype == ST_AIRPLANE || v->subtype == ST_HELICOPTER) {
00948
00949 if (v->current_order.GetDestination() > 0 && v->dest_tile == i) v->current_order.SetDestination(3);
00950 }
00951 }
00952 }
00953
00958 void ReleaseDisastersTargetingVehicle(VehicleID vehicle)
00959 {
00960 DisasterVehicle *v;
00961 FOR_ALL_DISASTERVEHICLES(v) {
00962
00963 if (v->subtype == ST_SMALL_UFO) {
00964 if (v->current_order.GetDestination() != 0 && v->dest_tile == vehicle) {
00965
00966 v->current_order.SetDestination(0);
00967 v->dest_tile = RandomTile();
00968 v->z_pos = 135;
00969 v->age = 0;
00970 }
00971 }
00972 }
00973 }
00974
00975 void DisasterVehicle::UpdateDeltaXY(Direction direction)
00976 {
00977 this->x_offs = -1;
00978 this->y_offs = -1;
00979 this->x_extent = 2;
00980 this->y_extent = 2;
00981 this->z_extent = 5;
00982 }