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