src/disaster_cmd.cpp
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5643 3778051e8095
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "functions.h"
       
     6 #include "industry_map.h"
       
     7 #include "station_map.h"
       
     8 #include "table/strings.h"
       
     9 #include "map.h"
       
    10 #include "tile.h"
       
    11 #include "vehicle.h"
       
    12 #include "command.h"
       
    13 #include "news.h"
       
    14 #include "station.h"
       
    15 #include "waypoint.h"
       
    16 #include "town.h"
       
    17 #include "industry.h"
       
    18 #include "player.h"
       
    19 #include "airport.h"
       
    20 #include "sound.h"
       
    21 #include "variables.h"
       
    22 #include "table/sprites.h"
       
    23 #include "date.h"
       
    24 
       
    25 static void DisasterClearSquare(TileIndex tile)
       
    26 {
       
    27 	if (!EnsureNoVehicle(tile)) return;
       
    28 
       
    29 	switch (GetTileType(tile)) {
       
    30 		case MP_RAILWAY:
       
    31 			if (IsHumanPlayer(GetTileOwner(tile)) && !IsRailWaypoint(tile)) {
       
    32 				PlayerID p = _current_player;
       
    33 				_current_player = OWNER_WATER;
       
    34 				DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
       
    35 				_current_player = p;
       
    36 			}
       
    37 			break;
       
    38 
       
    39 		case MP_HOUSE: {
       
    40 			PlayerID p = _current_player;
       
    41 			_current_player = OWNER_NONE;
       
    42 			DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
       
    43 			_current_player = p;
       
    44 			break;
       
    45 		}
       
    46 
       
    47 		case MP_TREES:
       
    48 		case MP_CLEAR:
       
    49 			DoClearSquare(tile);
       
    50 			break;
       
    51 
       
    52 		default:
       
    53 			break;
       
    54 	}
       
    55 }
       
    56 
       
    57 static const SpriteID _disaster_images_1[] = {0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41, 0xF41};
       
    58 static const SpriteID _disaster_images_2[] = {0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44, 0xF44};
       
    59 static const SpriteID _disaster_images_3[] = {0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E, 0xF4E};
       
    60 static const SpriteID _disaster_images_4[] = {0xF46, 0xF46, 0xF47, 0xF47, 0xF48, 0xF48, 0xF49, 0xF49};
       
    61 static const SpriteID _disaster_images_5[] = {0xF4A, 0xF4A, 0xF4B, 0xF4B, 0xF4C, 0xF4C, 0xF4D, 0xF4D};
       
    62 static const SpriteID _disaster_images_6[] = {0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50, 0xF50};
       
    63 static const SpriteID _disaster_images_7[] = {0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51, 0xF51};
       
    64 static const SpriteID _disaster_images_8[] = {0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52, 0xF52};
       
    65 static const SpriteID _disaster_images_9[] = {0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E, 0xF3E};
       
    66 
       
    67 static const SpriteID * const _disaster_images[] = {
       
    68 	_disaster_images_1, _disaster_images_1,
       
    69 	_disaster_images_2, _disaster_images_2,
       
    70 	_disaster_images_3, _disaster_images_3,
       
    71 	_disaster_images_8, _disaster_images_8, _disaster_images_9,
       
    72 	_disaster_images_6, _disaster_images_6,
       
    73 	_disaster_images_7, _disaster_images_7,
       
    74 	_disaster_images_4, _disaster_images_5,
       
    75 };
       
    76 
       
    77 static void DisasterVehicleUpdateImage(Vehicle *v)
       
    78 {
       
    79 	int img = v->u.disaster.image_override;
       
    80 	if (img == 0)
       
    81 		img = _disaster_images[v->subtype][v->direction];
       
    82 	v->cur_image = img;
       
    83 }
       
    84 
       
    85 static void InitializeDisasterVehicle(Vehicle* v, int x, int y, byte z, Direction direction, byte subtype)
       
    86 {
       
    87 	v->type = VEH_Disaster;
       
    88 	v->x_pos = x;
       
    89 	v->y_pos = y;
       
    90 	v->z_pos = z;
       
    91 	v->tile = TileVirtXY(x, y);
       
    92 	v->direction = direction;
       
    93 	v->subtype = subtype;
       
    94 	v->x_offs = -1;
       
    95 	v->y_offs = -1;
       
    96 	v->sprite_width = 2;
       
    97 	v->sprite_height = 2;
       
    98 	v->z_height = 5;
       
    99 	v->owner = OWNER_NONE;
       
   100 	v->vehstatus = VS_UNCLICKABLE;
       
   101 	v->u.disaster.image_override = 0;
       
   102 	v->current_order.type = OT_NOTHING;
       
   103 	v->current_order.flags = 0;
       
   104 	v->current_order.dest = 0;
       
   105 
       
   106 	DisasterVehicleUpdateImage(v);
       
   107 	VehiclePositionChanged(v);
       
   108 	BeginVehicleMove(v);
       
   109 	EndVehicleMove(v);
       
   110 }
       
   111 
       
   112 static void DeleteDisasterVeh(Vehicle *v)
       
   113 {
       
   114 	DeleteVehicleChain(v);
       
   115 }
       
   116 
       
   117 static void SetDisasterVehiclePos(Vehicle *v, int x, int y, byte z)
       
   118 {
       
   119 	Vehicle *u;
       
   120 
       
   121 	BeginVehicleMove(v);
       
   122 	v->x_pos = x;
       
   123 	v->y_pos = y;
       
   124 	v->z_pos = z;
       
   125 	v->tile = TileVirtXY(x, y);
       
   126 
       
   127 	DisasterVehicleUpdateImage(v);
       
   128 	VehiclePositionChanged(v);
       
   129 	EndVehicleMove(v);
       
   130 
       
   131 	if ( (u=v->next) != NULL) {
       
   132 		int safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE);
       
   133 		int safe_y = clamp(y - 1, 0, MapMaxY() * TILE_SIZE);
       
   134 		BeginVehicleMove(u);
       
   135 
       
   136 		u->x_pos = x;
       
   137 		u->y_pos = y - 1 - (max(z - GetSlopeZ(safe_x, safe_y), 0) >> 3);
       
   138 		safe_y = clamp(u->y_pos, 0, MapMaxY() * TILE_SIZE);
       
   139 		u->z_pos = GetSlopeZ(safe_x, safe_y);
       
   140 		u->direction = v->direction;
       
   141 
       
   142 		DisasterVehicleUpdateImage(u);
       
   143 		VehiclePositionChanged(u);
       
   144 		EndVehicleMove(u);
       
   145 
       
   146 		if ( (u=u->next) != NULL) {
       
   147 			BeginVehicleMove(u);
       
   148 			u->x_pos = x;
       
   149 			u->y_pos = y;
       
   150 			u->z_pos = z + 5;
       
   151 			VehiclePositionChanged(u);
       
   152 			EndVehicleMove(u);
       
   153 		}
       
   154 	}
       
   155 }
       
   156 
       
   157 
       
   158 static void DisasterTick_Zeppeliner(Vehicle *v)
       
   159 {
       
   160 	GetNewVehiclePosResult gp;
       
   161 	Station *st;
       
   162 	int x,y;
       
   163 	byte z;
       
   164 	TileIndex tile;
       
   165 
       
   166 	++v->tick_counter;
       
   167 
       
   168 	if (v->current_order.dest < 2) {
       
   169 		if (v->tick_counter&1)
       
   170 			return;
       
   171 
       
   172 		GetNewVehiclePos(v, &gp);
       
   173 
       
   174 		SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   175 
       
   176 		if (v->current_order.dest == 1) {
       
   177 			if (++v->age == 38) {
       
   178 				v->current_order.dest = 2;
       
   179 				v->age = 0;
       
   180 			}
       
   181 
       
   182 			if ((v->tick_counter&7)==0) {
       
   183 				CreateEffectVehicleRel(v, 0, -17, 2, EV_SMOKE);
       
   184 			}
       
   185 		} else if (v->current_order.dest == 0) {
       
   186 			tile = v->tile; /**/
       
   187 
       
   188 			if (IsValidTile(tile) &&
       
   189 					IsTileType(tile, MP_STATION) &&
       
   190 					IsAirport(tile) &&
       
   191 					IsHumanPlayer(GetTileOwner(tile))) {
       
   192 				v->current_order.dest = 1;
       
   193 				v->age = 0;
       
   194 
       
   195 				SetDParam(0, GetStationIndex(tile));
       
   196 				AddNewsItem(STR_B000_ZEPPELIN_DISASTER_AT,
       
   197 					NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
       
   198 					v->index,
       
   199 					0);
       
   200 			}
       
   201 		}
       
   202 		if (v->y_pos >= ((int)MapSizeY() + 9) * TILE_SIZE - 1)
       
   203 			DeleteDisasterVeh(v);
       
   204 		return;
       
   205 	}
       
   206 
       
   207 	if (v->current_order.dest > 2) {
       
   208 		if (++v->age <= 13320)
       
   209 			return;
       
   210 
       
   211 		tile = v->tile; /**/
       
   212 
       
   213 		if (IsValidTile(tile) &&
       
   214 				IsTileType(tile, MP_STATION) &&
       
   215 				IsAirport(tile) &&
       
   216 				IsHumanPlayer(GetTileOwner(tile))) {
       
   217 			st = GetStationByTile(tile);
       
   218 			CLRBITS(st->airport_flags, RUNWAY_IN_block);
       
   219 		}
       
   220 
       
   221 		SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos);
       
   222 		DeleteDisasterVeh(v);
       
   223 		return;
       
   224 	}
       
   225 
       
   226 	x = v->x_pos;
       
   227 	y = v->y_pos;
       
   228 	z = GetSlopeZ(x,y);
       
   229 	if (z < v->z_pos)
       
   230 		z = v->z_pos - 1;
       
   231 	SetDisasterVehiclePos(v, x, y, z);
       
   232 
       
   233 	if (++v->age == 1) {
       
   234 		CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
       
   235 		SndPlayVehicleFx(SND_12_EXPLOSION, v);
       
   236 		v->u.disaster.image_override = SPR_BLIMP_CRASHING;
       
   237 	} else if (v->age == 70) {
       
   238 		v->u.disaster.image_override = SPR_BLIMP_CRASHED;
       
   239 	} else if (v->age <= 300) {
       
   240 		if (!(v->tick_counter&7)) {
       
   241 			uint32 r = Random();
       
   242 
       
   243 			CreateEffectVehicleRel(v,
       
   244 				GB(r, 0, 4) - 7,
       
   245 				GB(r, 4, 4) - 7,
       
   246 				GB(r, 8, 3) + 5,
       
   247 				EV_EXPLOSION_SMALL);
       
   248 		}
       
   249 	} else if (v->age == 350) {
       
   250 		v->current_order.dest = 3;
       
   251 		v->age = 0;
       
   252 	}
       
   253 
       
   254 	tile = v->tile;/**/
       
   255 	if (IsValidTile(tile) &&
       
   256 			IsTileType(tile, MP_STATION) &&
       
   257 			IsAirport(tile) &&
       
   258 			IsHumanPlayer(GetTileOwner(tile))) {
       
   259 		st = GetStationByTile(tile);
       
   260 		SETBITS(st->airport_flags, RUNWAY_IN_block);
       
   261 	}
       
   262 }
       
   263 
       
   264 // UFO starts in the middle, and flies around a bit until it locates
       
   265 // a road vehicle which it targets.
       
   266 static void DisasterTick_UFO(Vehicle *v)
       
   267 {
       
   268 	GetNewVehiclePosResult gp;
       
   269 	Vehicle *u;
       
   270 	uint dist;
       
   271 	byte z;
       
   272 
       
   273 	v->u.disaster.image_override = (++v->tick_counter & 8) ? SPR_UFO_SMALL_SCOUT_DARKER : SPR_UFO_SMALL_SCOUT;
       
   274 
       
   275 	if (v->current_order.dest == 0) {
       
   276 // fly around randomly
       
   277 		int x = TileX(v->dest_tile) * TILE_SIZE;
       
   278 		int y = TileY(v->dest_tile) * TILE_SIZE;
       
   279 		if (abs(x - v->x_pos) + abs(y - v->y_pos) >= TILE_SIZE) {
       
   280 			v->direction = GetDirectionTowards(v, x, y);
       
   281 			GetNewVehiclePos(v, &gp);
       
   282 			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   283 			return;
       
   284 		}
       
   285 		if (++v->age < 6) {
       
   286 			v->dest_tile = RandomTile();
       
   287 			return;
       
   288 		}
       
   289 		v->current_order.dest = 1;
       
   290 
       
   291 		FOR_ALL_VEHICLES(u) {
       
   292 			if (u->type == VEH_Road && IsHumanPlayer(u->owner)) {
       
   293 				v->dest_tile = u->index;
       
   294 				v->age = 0;
       
   295 				return;
       
   296 			}
       
   297 		}
       
   298 
       
   299 		DeleteDisasterVeh(v);
       
   300 	} else {
       
   301 // target a vehicle
       
   302 		u = GetVehicle(v->dest_tile);
       
   303 		if (u->type != VEH_Road) {
       
   304 			DeleteDisasterVeh(v);
       
   305 			return;
       
   306 		}
       
   307 
       
   308 		dist = abs(v->x_pos - u->x_pos) + abs(v->y_pos - u->y_pos);
       
   309 
       
   310 		if (dist < TILE_SIZE && !(u->vehstatus&VS_HIDDEN) && u->breakdown_ctr==0) {
       
   311 			u->breakdown_ctr = 3;
       
   312 			u->breakdown_delay = 140;
       
   313 		}
       
   314 
       
   315 		v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
       
   316 		GetNewVehiclePos(v, &gp);
       
   317 
       
   318 		z = v->z_pos;
       
   319 		if (dist <= TILE_SIZE && z > u->z_pos) z--;
       
   320 		SetDisasterVehiclePos(v, gp.x, gp.y, z);
       
   321 
       
   322 		if (z <= u->z_pos && (u->vehstatus&VS_HIDDEN)==0) {
       
   323 			v->age++;
       
   324 			if (u->u.road.crashed_ctr == 0) {
       
   325 				u->u.road.crashed_ctr++;
       
   326 				u->vehstatus |= VS_CRASHED;
       
   327 
       
   328 				AddNewsItem(STR_B001_ROAD_VEHICLE_DESTROYED,
       
   329 					NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
       
   330 					u->index,
       
   331 					0);
       
   332 			}
       
   333 		}
       
   334 
       
   335 // destroy?
       
   336 		if (v->age > 50) {
       
   337 			CreateEffectVehicleRel(v, 0, 7, 8, EV_EXPLOSION_LARGE);
       
   338 			SndPlayVehicleFx(SND_12_EXPLOSION, v);
       
   339 			DeleteDisasterVeh(v);
       
   340 		}
       
   341 	}
       
   342 }
       
   343 
       
   344 static void DestructIndustry(Industry *i)
       
   345 {
       
   346 	TileIndex tile;
       
   347 
       
   348 	for (tile = 0; tile != MapSize(); tile++) {
       
   349 		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == i->index) {
       
   350 			ResetIndustryConstructionStage(tile);
       
   351 			MarkTileDirtyByTile(tile);
       
   352 		}
       
   353 	}
       
   354 }
       
   355 
       
   356 // Airplane which destroys an oil refinery
       
   357 static void DisasterTick_2(Vehicle *v)
       
   358 {
       
   359 	GetNewVehiclePosResult gp;
       
   360 
       
   361 	v->tick_counter++;
       
   362 	v->u.disaster.image_override =
       
   363 		(v->current_order.dest == 1 && v->tick_counter & 4) ? SPR_F_15_FIRING : 0;
       
   364 
       
   365 	GetNewVehiclePos(v, &gp);
       
   366 	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   367 
       
   368 	if (gp.x < -160) {
       
   369 		DeleteDisasterVeh(v);
       
   370 		return;
       
   371 	}
       
   372 
       
   373 	if (v->current_order.dest == 2) {
       
   374 		if (!(v->tick_counter&3)) {
       
   375 			Industry *i = GetIndustry(v->dest_tile);
       
   376 			int x = TileX(i->xy) * TILE_SIZE;
       
   377 			int y = TileY(i->xy) * TILE_SIZE;
       
   378 			uint32 r = Random();
       
   379 
       
   380 			CreateEffectVehicleAbove(
       
   381 				GB(r,  0, 6) + x,
       
   382 				GB(r,  6, 6) + y,
       
   383 				GB(r, 12, 4),
       
   384 				EV_EXPLOSION_SMALL);
       
   385 
       
   386 			if (++v->age >= 55)
       
   387 				v->current_order.dest = 3;
       
   388 		}
       
   389 	} else if (v->current_order.dest == 1) {
       
   390 		if (++v->age == 112) {
       
   391 			Industry *i;
       
   392 
       
   393 			v->current_order.dest = 2;
       
   394 			v->age = 0;
       
   395 
       
   396 			i = GetIndustry(v->dest_tile);
       
   397 			DestructIndustry(i);
       
   398 
       
   399 			SetDParam(0, i->town->index);
       
   400 			AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy, 0);
       
   401 			SndPlayTileFx(SND_12_EXPLOSION, i->xy);
       
   402 		}
       
   403 	} else if (v->current_order.dest == 0) {
       
   404 		int x,y;
       
   405 		TileIndex tile;
       
   406 		uint ind;
       
   407 
       
   408 		x = v->x_pos - 15 * TILE_SIZE;
       
   409 		y = v->y_pos;
       
   410 
       
   411 		if ( (uint)x > MapMaxX() * TILE_SIZE - 1)
       
   412 			return;
       
   413 
       
   414 		tile = TileVirtXY(x, y);
       
   415 		if (!IsTileType(tile, MP_INDUSTRY))
       
   416 			return;
       
   417 
       
   418 		ind = GetIndustryIndex(tile);
       
   419 		v->dest_tile = ind;
       
   420 
       
   421 		if (GetIndustry(ind)->type == IT_OIL_REFINERY) {
       
   422 			v->current_order.dest = 1;
       
   423 			v->age = 0;
       
   424 		}
       
   425 	}
       
   426 }
       
   427 
       
   428 // Helicopter which destroys a factory
       
   429 static void DisasterTick_3(Vehicle *v)
       
   430 {
       
   431 	GetNewVehiclePosResult gp;
       
   432 
       
   433 	v->tick_counter++;
       
   434 	v->u.disaster.image_override =
       
   435 		(v->current_order.dest == 1 && v->tick_counter & 4) ? SPR_AH_64A_FIRING : 0;
       
   436 
       
   437 	GetNewVehiclePos(v, &gp);
       
   438 	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   439 
       
   440 	if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
       
   441 		DeleteDisasterVeh(v);
       
   442 		return;
       
   443 	}
       
   444 
       
   445 	if (v->current_order.dest == 2) {
       
   446 		if (!(v->tick_counter&3)) {
       
   447 			Industry *i = GetIndustry(v->dest_tile);
       
   448 			int x = TileX(i->xy) * TILE_SIZE;
       
   449 			int y = TileY(i->xy) * TILE_SIZE;
       
   450 			uint32 r = Random();
       
   451 
       
   452 			CreateEffectVehicleAbove(
       
   453 				GB(r,  0, 6) + x,
       
   454 				GB(r,  6, 6) + y,
       
   455 				GB(r, 12, 4),
       
   456 				EV_EXPLOSION_SMALL);
       
   457 
       
   458 			if (++v->age >= 55)
       
   459 				v->current_order.dest = 3;
       
   460 		}
       
   461 	} else if (v->current_order.dest == 1) {
       
   462 		if (++v->age == 112) {
       
   463 			Industry *i;
       
   464 
       
   465 			v->current_order.dest = 2;
       
   466 			v->age = 0;
       
   467 
       
   468 			i = GetIndustry(v->dest_tile);
       
   469 			DestructIndustry(i);
       
   470 
       
   471 			SetDParam(0, i->town->index);
       
   472 			AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy, 0);
       
   473 			SndPlayTileFx(SND_12_EXPLOSION, i->xy);
       
   474 		}
       
   475 	} else if (v->current_order.dest == 0) {
       
   476 		int x,y;
       
   477 		TileIndex tile;
       
   478 		uint ind;
       
   479 
       
   480 		x = v->x_pos - 15 * TILE_SIZE;
       
   481 		y = v->y_pos;
       
   482 
       
   483 		if ( (uint)x > MapMaxX() * TILE_SIZE - 1)
       
   484 			return;
       
   485 
       
   486 		tile = TileVirtXY(x, y);
       
   487 		if (!IsTileType(tile, MP_INDUSTRY))
       
   488 			return;
       
   489 
       
   490 		ind = GetIndustryIndex(tile);
       
   491 		v->dest_tile = ind;
       
   492 
       
   493 		if (GetIndustry(ind)->type == IT_FACTORY) {
       
   494 			v->current_order.dest = 1;
       
   495 			v->age = 0;
       
   496 		}
       
   497 	}
       
   498 }
       
   499 
       
   500 // Helicopter rotor blades
       
   501 static void DisasterTick_3b(Vehicle *v)
       
   502 {
       
   503 	if (++v->tick_counter & 1)
       
   504 		return;
       
   505 
       
   506 	if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
       
   507 
       
   508 	VehiclePositionChanged(v);
       
   509 	BeginVehicleMove(v);
       
   510 	EndVehicleMove(v);
       
   511 }
       
   512 
       
   513 // Big UFO which lands on a piece of rail.
       
   514 // Will be shot down by a plane
       
   515 static void DisasterTick_4(Vehicle *v)
       
   516 {
       
   517 	GetNewVehiclePosResult gp;
       
   518 	byte z;
       
   519 	Vehicle *u,*w;
       
   520 	Town *t;
       
   521 	TileIndex tile;
       
   522 	TileIndex tile_org;
       
   523 
       
   524 	v->tick_counter++;
       
   525 
       
   526 	if (v->current_order.dest == 1) {
       
   527 		int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
       
   528 		int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
       
   529 		if (abs(v->x_pos - x) + abs(v->y_pos - y) >= 8) {
       
   530 			v->direction = GetDirectionTowards(v, x, y);
       
   531 
       
   532 			GetNewVehiclePos(v, &gp);
       
   533 			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   534 			return;
       
   535 		}
       
   536 
       
   537 		z = GetSlopeZ(v->x_pos, v->y_pos);
       
   538 		if (z < v->z_pos) {
       
   539 			SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
       
   540 			return;
       
   541 		}
       
   542 
       
   543 		v->current_order.dest = 2;
       
   544 
       
   545 		FOR_ALL_VEHICLES(u) {
       
   546 			if (u->type == VEH_Train || u->type == VEH_Road) {
       
   547 				if (abs(u->x_pos - v->x_pos) + abs(u->y_pos - v->y_pos) <= 12 * TILE_SIZE) {
       
   548 					u->breakdown_ctr = 5;
       
   549 					u->breakdown_delay = 0xF0;
       
   550 				}
       
   551 			}
       
   552 		}
       
   553 
       
   554 		t = ClosestTownFromTile(v->dest_tile, (uint)-1);
       
   555 		SetDParam(0, t->index);
       
   556 		AddNewsItem(STR_B004_UFO_LANDS_NEAR,
       
   557 			NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ACCIDENT, 0),
       
   558 			v->tile,
       
   559 			0);
       
   560 
       
   561 		u = ForceAllocateSpecialVehicle();
       
   562 		if (u == NULL) {
       
   563 			DeleteDisasterVeh(v);
       
   564 			return;
       
   565 		}
       
   566 
       
   567 		InitializeDisasterVehicle(u, -6 * TILE_SIZE, v->y_pos, 135, DIR_SW, 11);
       
   568 		u->u.disaster.unk2 = v->index;
       
   569 
       
   570 		w = ForceAllocateSpecialVehicle();
       
   571 		if (w == NULL)
       
   572 			return;
       
   573 
       
   574 		u->next = w;
       
   575 		InitializeDisasterVehicle(w, -6 * TILE_SIZE, v->y_pos, 0, DIR_SW, 12);
       
   576 		w->vehstatus |= VS_SHADOW;
       
   577 	} else if (v->current_order.dest < 1) {
       
   578 
       
   579 		int x = TileX(v->dest_tile) * TILE_SIZE;
       
   580 		int y = TileY(v->dest_tile) * TILE_SIZE;
       
   581 		if (abs(x - v->x_pos) + abs(y - v->y_pos) >= TILE_SIZE) {
       
   582 			v->direction = GetDirectionTowards(v, x, y);
       
   583 			GetNewVehiclePos(v, &gp);
       
   584 			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   585 			return;
       
   586 		}
       
   587 
       
   588 		if (++v->age < 6) {
       
   589 			v->dest_tile = RandomTile();
       
   590 			return;
       
   591 		}
       
   592 		v->current_order.dest = 1;
       
   593 
       
   594 		tile_org = tile = RandomTile();
       
   595 		do {
       
   596 			if (IsTileType(tile, MP_RAILWAY) &&
       
   597 					IsPlainRailTile(tile) &&
       
   598 					IsHumanPlayer(GetTileOwner(tile))) {
       
   599 				break;
       
   600 			}
       
   601 			tile = TILE_MASK(tile+1);
       
   602 		} while (tile != tile_org);
       
   603 		v->dest_tile = tile;
       
   604 		v->age = 0;
       
   605 	} else {
       
   606 		return;
       
   607 	}
       
   608 }
       
   609 
       
   610 // The plane which will shoot down the UFO
       
   611 static void DisasterTick_4b(Vehicle *v)
       
   612 {
       
   613 	GetNewVehiclePosResult gp;
       
   614 	Vehicle *u;
       
   615 	int i;
       
   616 
       
   617 	v->tick_counter++;
       
   618 
       
   619 	GetNewVehiclePos(v, &gp);
       
   620 	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   621 
       
   622 	if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
       
   623 		DeleteDisasterVeh(v);
       
   624 		return;
       
   625 	}
       
   626 
       
   627 	if (v->current_order.dest == 0) {
       
   628 		u = GetVehicle(v->u.disaster.unk2);
       
   629 		if (abs(v->x_pos - u->x_pos) > TILE_SIZE)
       
   630 			return;
       
   631 		v->current_order.dest = 1;
       
   632 
       
   633 		CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
       
   634 		SndPlayVehicleFx(SND_12_EXPLOSION, u);
       
   635 
       
   636 		DeleteDisasterVeh(u);
       
   637 
       
   638 		for (i = 0; i != 80; i++) {
       
   639 			uint32 r = Random();
       
   640 			CreateEffectVehicleAbove(
       
   641 				GB(r, 0, 6) + v->x_pos - 32,
       
   642 				GB(r, 5, 6) + v->y_pos - 32,
       
   643 				0,
       
   644 				EV_EXPLOSION_SMALL);
       
   645 		}
       
   646 
       
   647 		BEGIN_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
       
   648 			tile = TILE_MASK(tile);
       
   649 			DisasterClearSquare(tile);
       
   650 		END_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
       
   651 	}
       
   652 }
       
   653 
       
   654 // Submarine handler
       
   655 static void DisasterTick_5_and_6(Vehicle *v)
       
   656 {
       
   657 	uint32 r;
       
   658 	GetNewVehiclePosResult gp;
       
   659 	TileIndex tile;
       
   660 
       
   661 	v->tick_counter++;
       
   662 
       
   663 	if (++v->age > 8880) {
       
   664 		VehiclePositionChanged(v);
       
   665 		BeginVehicleMove(v);
       
   666 		EndVehicleMove(v);
       
   667 		DeleteVehicle(v);
       
   668 		return;
       
   669 	}
       
   670 
       
   671 	if (!(v->tick_counter & 1)) return;
       
   672 
       
   673 	tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
       
   674 	if (IsValidTile(tile) &&
       
   675 			(r=GetTileTrackStatus(tile,TRANSPORT_WATER),(byte)(r+(r >> 8)) == 0x3F) &&
       
   676 			!CHANCE16(1,90)) {
       
   677 		GetNewVehiclePos(v, &gp);
       
   678 		SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
       
   679 		return;
       
   680 	}
       
   681 
       
   682 	v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
       
   683 }
       
   684 
       
   685 
       
   686 static void DisasterTick_NULL(Vehicle *v) {}
       
   687 typedef void DisasterVehicleTickProc(Vehicle *v);
       
   688 
       
   689 static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
       
   690 	DisasterTick_Zeppeliner,DisasterTick_NULL,
       
   691 	DisasterTick_UFO,DisasterTick_NULL,
       
   692 	DisasterTick_2,DisasterTick_NULL,
       
   693 	DisasterTick_3,DisasterTick_NULL,DisasterTick_3b,
       
   694 	DisasterTick_4,DisasterTick_NULL,
       
   695 	DisasterTick_4b,DisasterTick_NULL,
       
   696 	DisasterTick_5_and_6,
       
   697 	DisasterTick_5_and_6,
       
   698 };
       
   699 
       
   700 
       
   701 void DisasterVehicle_Tick(Vehicle *v)
       
   702 {
       
   703 	_disastervehicle_tick_procs[v->subtype](v);
       
   704 }
       
   705 
       
   706 
       
   707 void OnNewDay_DisasterVehicle(Vehicle *v)
       
   708 {
       
   709 	// not used
       
   710 }
       
   711 
       
   712 typedef void DisasterInitProc(void);
       
   713 
       
   714 // Zeppeliner which crashes on a small airport
       
   715 static void Disaster0_Init(void)
       
   716 {
       
   717 	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
       
   718 	Station *st;
       
   719 	int x;
       
   720 
       
   721 	if (v == NULL) return;
       
   722 
       
   723 	/* Pick a random place, unless we find a small airport */
       
   724 	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
       
   725 
       
   726 	FOR_ALL_STATIONS(st) {
       
   727 		if (st->airport_tile != 0 &&
       
   728 				st->airport_type <= 1 &&
       
   729 				IsHumanPlayer(st->owner)) {
       
   730 			x = (TileX(st->xy) + 2) * TILE_SIZE;
       
   731 			break;
       
   732 		}
       
   733 	}
       
   734 
       
   735 	InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, 0);
       
   736 
       
   737 	// Allocate shadow too?
       
   738 	u = ForceAllocateSpecialVehicle();
       
   739 	if (u != NULL) {
       
   740 		v->next = u;
       
   741 		InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, 1);
       
   742 		u->vehstatus |= VS_SHADOW;
       
   743 	}
       
   744 }
       
   745 
       
   746 static void Disaster1_Init(void)
       
   747 {
       
   748 	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
       
   749 	int x;
       
   750 
       
   751 	if (v == NULL) return;
       
   752 
       
   753 	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
       
   754 
       
   755 	InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, 2);
       
   756 	v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
       
   757 	v->age = 0;
       
   758 
       
   759 	// Allocate shadow too?
       
   760 	u = ForceAllocateSpecialVehicle();
       
   761 	if (u != NULL) {
       
   762 		v->next = u;
       
   763 		InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, 3);
       
   764 		u->vehstatus |= VS_SHADOW;
       
   765 	}
       
   766 }
       
   767 
       
   768 static void Disaster2_Init(void)
       
   769 {
       
   770 	Industry *i, *found;
       
   771 	Vehicle *v,*u;
       
   772 	int x,y;
       
   773 
       
   774 	found = NULL;
       
   775 
       
   776 	FOR_ALL_INDUSTRIES(i) {
       
   777 		if (i->type == IT_OIL_REFINERY &&
       
   778 				(found == NULL || CHANCE16(1, 2))) {
       
   779 			found = i;
       
   780 		}
       
   781 	}
       
   782 
       
   783 	if (found == NULL) return;
       
   784 
       
   785 	v = ForceAllocateSpecialVehicle();
       
   786 	if (v == NULL) return;
       
   787 
       
   788 	x = (MapSizeX() + 9) * TILE_SIZE - 1;
       
   789 	y = TileY(found->xy) * TILE_SIZE + 37;
       
   790 
       
   791 	InitializeDisasterVehicle(v, x, y, 135, DIR_NE, 4);
       
   792 
       
   793 	u = ForceAllocateSpecialVehicle();
       
   794 	if (u != NULL) {
       
   795 		v->next = u;
       
   796 		InitializeDisasterVehicle(u, x, y, 0, DIR_SE, 5);
       
   797 		u->vehstatus |= VS_SHADOW;
       
   798 	}
       
   799 }
       
   800 
       
   801 static void Disaster3_Init(void)
       
   802 {
       
   803 	Industry *i, *found;
       
   804 	Vehicle *v,*u,*w;
       
   805 	int x,y;
       
   806 
       
   807 	found = NULL;
       
   808 
       
   809 	FOR_ALL_INDUSTRIES(i) {
       
   810 		if (i->type == IT_FACTORY &&
       
   811 				(found==NULL || CHANCE16(1,2))) {
       
   812 			found = i;
       
   813 		}
       
   814 	}
       
   815 
       
   816 	if (found == NULL) return;
       
   817 
       
   818 	v = ForceAllocateSpecialVehicle();
       
   819 	if (v == NULL) return;
       
   820 
       
   821 	x = -16 * TILE_SIZE;
       
   822 	y = TileY(found->xy) * TILE_SIZE + 37;
       
   823 
       
   824 	InitializeDisasterVehicle(v, x, y, 135, DIR_SW, 6);
       
   825 
       
   826 	u = ForceAllocateSpecialVehicle();
       
   827 	if (u != NULL) {
       
   828 		v->next = u;
       
   829 		InitializeDisasterVehicle(u, x, y, 0, DIR_SW, 7);
       
   830 		u->vehstatus |= VS_SHADOW;
       
   831 
       
   832 		w = ForceAllocateSpecialVehicle();
       
   833 		if (w != NULL) {
       
   834 			u->next = w;
       
   835 			InitializeDisasterVehicle(w, x, y, 140, DIR_SW, 8);
       
   836 		}
       
   837 	}
       
   838 }
       
   839 
       
   840 static void Disaster4_Init(void)
       
   841 {
       
   842 	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
       
   843 	int x,y;
       
   844 
       
   845 	if (v == NULL) return;
       
   846 
       
   847 	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
       
   848 
       
   849 	y = MapMaxX() * TILE_SIZE - 1;
       
   850 	InitializeDisasterVehicle(v, x, y, 135, DIR_NW, 9);
       
   851 	v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
       
   852 	v->age = 0;
       
   853 
       
   854 	// Allocate shadow too?
       
   855 	u = ForceAllocateSpecialVehicle();
       
   856 	if (u != NULL) {
       
   857 		v->next = u;
       
   858 		InitializeDisasterVehicle(u, x, y, 0, DIR_NW, 10);
       
   859 		u->vehstatus |= VS_SHADOW;
       
   860 	}
       
   861 }
       
   862 
       
   863 // Submarine type 1
       
   864 static void Disaster5_Init(void)
       
   865 {
       
   866 	Vehicle *v = ForceAllocateSpecialVehicle();
       
   867 	int x,y;
       
   868 	Direction dir;
       
   869 	uint32 r;
       
   870 
       
   871 	if (v == NULL) return;
       
   872 
       
   873 	r = Random();
       
   874 	x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
       
   875 
       
   876 	if (r & 0x80000000) {
       
   877 		y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
       
   878 		dir = DIR_NW;
       
   879 	} else {
       
   880 		y = TILE_SIZE / 2;
       
   881 		dir = DIR_SE;
       
   882 	}
       
   883 	InitializeDisasterVehicle(v, x, y, 0, dir, 13);
       
   884 	v->age = 0;
       
   885 }
       
   886 
       
   887 // Submarine type 2
       
   888 static void Disaster6_Init(void)
       
   889 {
       
   890 	Vehicle *v = ForceAllocateSpecialVehicle();
       
   891 	int x,y;
       
   892 	Direction dir;
       
   893 	uint32 r;
       
   894 
       
   895 	if (v == NULL) return;
       
   896 
       
   897 	r = Random();
       
   898 	x = TileX(r) * TILE_SIZE + TILE_SIZE / 2;
       
   899 
       
   900 	if (r & 0x80000000) {
       
   901 		y = MapMaxX() * TILE_SIZE - TILE_SIZE / 2 - 1;
       
   902 		dir = DIR_NW;
       
   903 	} else {
       
   904 		y = TILE_SIZE / 2;
       
   905 		dir = DIR_SE;
       
   906 	}
       
   907 	InitializeDisasterVehicle(v, x, y, 0, dir, 14);
       
   908 	v->age = 0;
       
   909 }
       
   910 
       
   911 static void Disaster7_Init(void)
       
   912 {
       
   913 	int index = GB(Random(), 0, 4);
       
   914 	uint m;
       
   915 
       
   916 	for (m = 0; m < 15; m++) {
       
   917 		const Industry* i;
       
   918 
       
   919 		FOR_ALL_INDUSTRIES(i) {
       
   920 			if (i->type == IT_COAL_MINE && --index < 0) {
       
   921 				SetDParam(0, i->town->index);
       
   922 				AddNewsItem(STR_B005_COAL_MINE_SUBSIDENCE_LEAVES,
       
   923 					NEWS_FLAGS(NM_THIN,NF_VIEWPORT|NF_TILE,NT_ACCIDENT,0), i->xy + TileDiffXY(1, 1), 0);
       
   924 
       
   925 				{
       
   926 					TileIndex tile = i->xy;
       
   927 					TileIndexDiff step = TileOffsByDiagDir(GB(Random(), 0, 2));
       
   928 					uint n;
       
   929 
       
   930 					for (n = 0; n < 30; n++) {
       
   931 						DisasterClearSquare(tile);
       
   932 						tile = TILE_MASK(tile + step);
       
   933 					}
       
   934 				}
       
   935 				return;
       
   936 			}
       
   937 		}
       
   938 	}
       
   939 }
       
   940 
       
   941 static DisasterInitProc * const _disaster_initprocs[] = {
       
   942 	Disaster0_Init,
       
   943 	Disaster1_Init,
       
   944 	Disaster2_Init,
       
   945 	Disaster3_Init,
       
   946 	Disaster4_Init,
       
   947 	Disaster5_Init,
       
   948 	Disaster6_Init,
       
   949 	Disaster7_Init,
       
   950 };
       
   951 
       
   952 static const struct {
       
   953 	Year min;
       
   954 	Year max;
       
   955 } _dis_years[] = {
       
   956 	{ 1930, 1955 },
       
   957 	{ 1940, 1970 },
       
   958 	{ 1960, 1990 },
       
   959 	{ 1970, 2000 },
       
   960 	{ 2000, 2100 },
       
   961 	{ 1940, 1965 },
       
   962 	{ 1975, 2010 },
       
   963 	{ 1950, 1985 }
       
   964 };
       
   965 
       
   966 
       
   967 static void DoDisaster(void)
       
   968 {
       
   969 	byte buf[lengthof(_dis_years)];
       
   970 	uint i;
       
   971 	uint j;
       
   972 
       
   973 	j = 0;
       
   974 	for (i = 0; i != lengthof(_dis_years); i++) {
       
   975 		if (_cur_year >= _dis_years[i].min && _cur_year < _dis_years[i].max) buf[j++] = i;
       
   976 	}
       
   977 
       
   978 	if (j == 0) return;
       
   979 
       
   980 	_disaster_initprocs[buf[RandomRange(j)]]();
       
   981 }
       
   982 
       
   983 
       
   984 static void ResetDisasterDelay(void)
       
   985 {
       
   986 	_disaster_delay = GB(Random(), 0, 9) + 730;
       
   987 }
       
   988 
       
   989 void DisasterDailyLoop(void)
       
   990 {
       
   991 	if (--_disaster_delay != 0) return;
       
   992 
       
   993 	ResetDisasterDelay();
       
   994 
       
   995 	if (_opt.diff.disasters != 0) DoDisaster();
       
   996 }
       
   997 
       
   998 void StartupDisasters(void)
       
   999 {
       
  1000 	ResetDisasterDelay();
       
  1001 }