Changeset - r7334:9402325cee09
[Not reviewed]
master
0 8 0
rubidium - 17 years ago 2007-07-26 15:37:19
rubidium@openttd.org
(svn r10697) -Codechange: give a more sensible names to some of the unkX variables.
8 files changed with 27 insertions and 27 deletions:
0 comments (0 inline, 0 general)
src/disaster_cmd.cpp
Show inline comments
 
@@ -418,445 +418,445 @@ static void DisasterTick_Airplane(Vehicl
 
			int y = TileY(i->xy) * TILE_SIZE;
 
			uint32 r = Random();
 

	
 
			CreateEffectVehicleAbove(
 
				GB(r,  0, 6) + x,
 
				GB(r,  6, 6) + y,
 
				GB(r, 12, 4),
 
				EV_EXPLOSION_SMALL);
 

	
 
			if (++v->age >= 55) v->current_order.dest = 3;
 
		}
 
	} else if (v->current_order.dest == 1) {
 
		if (++v->age == 112) {
 
			Industry *i;
 

	
 
			v->current_order.dest = 2;
 
			v->age = 0;
 

	
 
			i = GetIndustry(v->dest_tile);
 
			DestructIndustry(i);
 

	
 
			SetDParam(0, i->town->index);
 
			AddNewsItem(STR_B002_OIL_REFINERY_EXPLOSION, NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, 0), i->xy, 0);
 
			SndPlayTileFx(SND_12_EXPLOSION, i->xy);
 
		}
 
	} else if (v->current_order.dest == 0) {
 
		int x, y;
 
		TileIndex tile;
 
		uint ind;
 

	
 
		x = v->x_pos - (15 * TILE_SIZE);
 
		y = v->y_pos;
 

	
 
		if ( (uint)x > MapMaxX() * TILE_SIZE - 1) return;
 

	
 
		tile = TileVirtXY(x, y);
 
		if (!IsTileType(tile, MP_INDUSTRY)) return;
 

	
 
		ind = GetIndustryIndex(tile);
 
		v->dest_tile = ind;
 

	
 
		if (GetIndustrySpec(GetIndustry(ind)->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) {
 
			v->current_order.dest = 1;
 
			v->age = 0;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Helicopter handling, v->current_order.dest states:
 
 * 0: Fly towards the targetted factory
 
 * 1: If within 15 tiles, fire away rockets and destroy industry
 
 * 2: Factory explosions
 
 * 3: Fly out of the map
 
 */
 
static void DisasterTick_Helicopter(Vehicle *v)
 
{
 
	v->tick_counter++;
 
	v->u.disaster.image_override =
 
		(v->current_order.dest == 1 && HASBIT(v->tick_counter, 2)) ? SPR_AH_64A_FIRING : 0;
 

	
 
	GetNewVehiclePosResult gp = GetNewVehiclePos(v);
 
	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 

	
 
	if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
 
		DeleteDisasterVeh(v);
 
		return;
 
	}
 

	
 
	if (v->current_order.dest == 2) {
 
		if (GB(v->tick_counter, 0, 2) == 0) {
 
			Industry *i = GetIndustry(v->dest_tile);
 
			int x = TileX(i->xy) * TILE_SIZE;
 
			int y = TileY(i->xy) * TILE_SIZE;
 
			uint32 r = Random();
 

	
 
			CreateEffectVehicleAbove(
 
				GB(r,  0, 6) + x,
 
				GB(r,  6, 6) + y,
 
				GB(r, 12, 4),
 
				EV_EXPLOSION_SMALL);
 

	
 
			if (++v->age >= 55) v->current_order.dest = 3;
 
		}
 
	} else if (v->current_order.dest == 1) {
 
		if (++v->age == 112) {
 
			Industry *i;
 

	
 
			v->current_order.dest = 2;
 
			v->age = 0;
 

	
 
			i = GetIndustry(v->dest_tile);
 
			DestructIndustry(i);
 

	
 
			SetDParam(0, i->town->index);
 
			AddNewsItem(STR_B003_FACTORY_DESTROYED_IN_SUSPICIOUS, NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, 0), i->xy, 0);
 
			SndPlayTileFx(SND_12_EXPLOSION, i->xy);
 
		}
 
	} else if (v->current_order.dest == 0) {
 
		int x, y;
 
		TileIndex tile;
 
		uint ind;
 

	
 
		x = v->x_pos + (15 * TILE_SIZE);
 
		y = v->y_pos;
 

	
 
		if ( (uint)x > MapMaxX() * TILE_SIZE - 1) return;
 

	
 
		tile = TileVirtXY(x, y);
 
		if (!IsTileType(tile, MP_INDUSTRY)) return;
 

	
 
		ind = GetIndustryIndex(tile);
 
		v->dest_tile = ind;
 

	
 
		if (GetIndustrySpec(GetIndustry(ind)->type)->behaviour & INDUSTRYBEH_CHOPPER_ATTACKS) {
 
			v->current_order.dest = 1;
 
			v->age = 0;
 
		}
 
	}
 
}
 

	
 
/** Helicopter rotor blades; keep these spinning */
 
static void DisasterTick_Helicopter_Rotors(Vehicle *v)
 
{
 
	v->tick_counter++;
 
	if (HASBIT(v->tick_counter, 0)) return;
 

	
 
	if (++v->cur_image > SPR_ROTOR_MOVING_3) v->cur_image = SPR_ROTOR_MOVING_1;
 

	
 
	VehiclePositionChanged(v);
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 
}
 

	
 
/**
 
 * (Big) Ufo handling, v->current_order.dest states:
 
 * 0: Fly around to the middle of the map, then randomly for a while and home in on a piece of rail
 
 * 1: Land there and breakdown all trains in a radius of 12 tiles; and now we wait...
 
 *    because as soon as the Ufo lands, a fighter jet, a Skyranger, is called to clear up the mess
 
 */
 
static void DisasterTick_Big_Ufo(Vehicle *v)
 
{
 
	byte z;
 
	Vehicle *u, *w;
 
	Town *t;
 
	TileIndex tile;
 
	TileIndex tile_org;
 

	
 
	v->tick_counter++;
 

	
 
	if (v->current_order.dest == 1) {
 
		int x = TileX(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
 
		int y = TileY(v->dest_tile) * TILE_SIZE + TILE_SIZE / 2;
 
		if (delta(v->x_pos, x) + delta(v->y_pos, y) >= 8) {
 
			v->direction = GetDirectionTowards(v, x, y);
 

	
 
			GetNewVehiclePosResult gp = GetNewVehiclePos(v);
 
			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 
			return;
 
		}
 

	
 
		z = GetSlopeZ(v->x_pos, v->y_pos);
 
		if (z < v->z_pos) {
 
			SetDisasterVehiclePos(v, v->x_pos, v->y_pos, v->z_pos - 1);
 
			return;
 
		}
 

	
 
		v->current_order.dest = 2;
 

	
 
		FOR_ALL_VEHICLES(u) {
 
			if (u->type == VEH_TRAIN || u->type == VEH_ROAD) {
 
				if (delta(u->x_pos, v->x_pos) + delta(u->y_pos, v->y_pos) <= 12 * TILE_SIZE) {
 
					u->breakdown_ctr = 5;
 
					u->breakdown_delay = 0xF0;
 
				}
 
			}
 
		}
 

	
 
		t = ClosestTownFromTile(v->dest_tile, (uint)-1);
 
		SetDParam(0, t->index);
 
		AddNewsItem(STR_B004_UFO_LANDS_NEAR,
 
			NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ACCIDENT, 0),
 
			v->tile,
 
			0);
 

	
 
		u = ForceAllocateSpecialVehicle();
 
		if (u == NULL) {
 
			DeleteDisasterVeh(v);
 
			return;
 
		}
 

	
 
		InitializeDisasterVehicle(u, -6 * TILE_SIZE, v->y_pos, 135, DIR_SW, ST_Big_Ufo_Destroyer);
 
		u->u.disaster.unk2 = v->index;
 
		u->u.disaster.big_ufo_destroyer_target = v->index;
 

	
 
		w = ForceAllocateSpecialVehicle();
 
		if (w == NULL) return;
 

	
 
		u->next = w;
 
		InitializeDisasterVehicle(w, -6 * TILE_SIZE, v->y_pos, 0, DIR_SW, ST_Big_Ufo_Destroyer_Shadow);
 
		w->vehstatus |= VS_SHADOW;
 
	} else if (v->current_order.dest == 0) {
 
		int x = TileX(v->dest_tile) * TILE_SIZE;
 
		int y = TileY(v->dest_tile) * TILE_SIZE;
 
		if (delta(x, v->x_pos) + delta(y, v->y_pos) >= TILE_SIZE) {
 
			v->direction = GetDirectionTowards(v, x, y);
 
			GetNewVehiclePosResult gp = GetNewVehiclePos(v);
 
			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 
			return;
 
		}
 

	
 
		if (++v->age < 6) {
 
			v->dest_tile = RandomTile();
 
			return;
 
		}
 
		v->current_order.dest = 1;
 

	
 
		tile_org = tile = RandomTile();
 
		do {
 
			if (IsTileType(tile, MP_RAILWAY) &&
 
					IsPlainRailTile(tile) &&
 
					IsHumanPlayer(GetTileOwner(tile))) {
 
				break;
 
			}
 
			tile = TILE_MASK(tile + 1);
 
		} while (tile != tile_org);
 
		v->dest_tile = tile;
 
		v->age = 0;
 
	} else {
 
		return;
 
	}
 
}
 

	
 
/**
 
 * Skyranger destroying (Big) Ufo handling, v->current_order.dest states:
 
 * 0: Home in on landed Ufo and shoot it down
 
 */
 
static void DisasterTick_Big_Ufo_Destroyer(Vehicle *v)
 
{
 
	Vehicle *u;
 
	int i;
 

	
 
	v->tick_counter++;
 

	
 
	GetNewVehiclePosResult gp = GetNewVehiclePos(v);
 
	SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 

	
 
	if (gp.x > (int)MapSizeX() * TILE_SIZE + 9 * TILE_SIZE - 1) {
 
		DeleteDisasterVeh(v);
 
		return;
 
	}
 

	
 
	if (v->current_order.dest == 0) {
 
		u = GetVehicle(v->u.disaster.unk2);
 
		u = GetVehicle(v->u.disaster.big_ufo_destroyer_target);
 
		if (delta(v->x_pos, u->x_pos) > TILE_SIZE) return;
 
		v->current_order.dest = 1;
 

	
 
		CreateEffectVehicleRel(u, 0, 7, 8, EV_EXPLOSION_LARGE);
 
		SndPlayVehicleFx(SND_12_EXPLOSION, u);
 

	
 
		DeleteDisasterVeh(u);
 

	
 
		for (i = 0; i != 80; i++) {
 
			uint32 r = Random();
 
			CreateEffectVehicleAbove(
 
				GB(r, 0, 6) + v->x_pos - 32,
 
				GB(r, 5, 6) + v->y_pos - 32,
 
				0,
 
				EV_EXPLOSION_SMALL);
 
		}
 

	
 
		BEGIN_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
 
			tile = TILE_MASK(tile);
 
			DisasterClearSquare(tile);
 
		END_TILE_LOOP(tile, 6, 6, v->tile - TileDiffXY(3, 3))
 
	}
 
}
 

	
 
/**
 
 * Submarine, v->current_order.dest states:
 
 * Unused, just float around aimlessly and pop up at different places, turning around
 
 */
 
static void DisasterTick_Submarine(Vehicle *v)
 
{
 
	TileIndex tile;
 

	
 
	v->tick_counter++;
 

	
 
	if (++v->age > 8880) {
 
		VehiclePositionChanged(v);
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 
		DeleteVehicle(v);
 
		return;
 
	}
 

	
 
	if (!HASBIT(v->tick_counter, 0)) return;
 

	
 
	tile = v->tile + TileOffsByDiagDir(DirToDiagDir(v->direction));
 
	if (IsValidTile(tile)) {
 
		TrackdirBits r = (TrackdirBits)GetTileTrackStatus(tile, TRANSPORT_WATER, 0);
 

	
 
		if (TrackdirBitsToTrackBits(r) == TRACK_BIT_ALL && !CHANCE16(1, 90)) {
 
			GetNewVehiclePosResult gp = GetNewVehiclePos(v);
 
			SetDisasterVehiclePos(v, gp.x, gp.y, v->z_pos);
 
			return;
 
		}
 
	}
 

	
 
	v->direction = ChangeDir(v->direction, GB(Random(), 0, 1) ? DIRDIFF_90RIGHT : DIRDIFF_90LEFT);
 
}
 

	
 

	
 
static void DisasterTick_NULL(Vehicle *v) {}
 
typedef void DisasterVehicleTickProc(Vehicle *v);
 

	
 
static DisasterVehicleTickProc * const _disastervehicle_tick_procs[] = {
 
	DisasterTick_Zeppeliner, DisasterTick_NULL,
 
	DisasterTick_Ufo,        DisasterTick_NULL,
 
	DisasterTick_Airplane,   DisasterTick_NULL,
 
	DisasterTick_Helicopter, DisasterTick_NULL, DisasterTick_Helicopter_Rotors,
 
	DisasterTick_Big_Ufo,    DisasterTick_NULL, DisasterTick_Big_Ufo_Destroyer,
 
	DisasterTick_NULL,
 
	DisasterTick_Submarine,
 
	DisasterTick_Submarine,
 
};
 

	
 

	
 
void DisasterVehicle::Tick()
 
{
 
	_disastervehicle_tick_procs[this->subtype](this);
 
}
 

	
 

	
 
void OnNewDay_DisasterVehicle(Vehicle *v)
 
{
 
	// not used
 
}
 

	
 
typedef void DisasterInitProc();
 

	
 

	
 
/** Zeppeliner which crashes on a small airport if one found,
 
 * otherwise crashes on a random tile */
 
static void Disaster_Zeppeliner_Init()
 
{
 
	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
 
	Station *st;
 
	int x;
 

	
 
	if (v == NULL) return;
 

	
 
	/* Pick a random place, unless we find a small airport */
 
	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (st->airport_tile != 0 &&
 
				st->airport_type <= 1 &&
 
				IsHumanPlayer(st->owner)) {
 
			x = (TileX(st->xy) + 2) * TILE_SIZE;
 
			break;
 
		}
 
	}
 

	
 
	InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_Zeppeliner);
 

	
 
	/* Allocate shadow too? */
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_Zeppeliner_Shadow);
 
		u->vehstatus |= VS_SHADOW;
 
	}
 
}
 

	
 

	
 
/** Ufo which flies around aimlessly from the middle of the map a bit
 
 * until it locates a road vehicle which it targets and then destroys */
 
static void Disaster_Small_Ufo_Init()
 
{
 
	Vehicle *v = ForceAllocateSpecialVehicle(), *u;
 
	int x;
 

	
 
	if (v == NULL) return;
 

	
 
	x = TileX(Random()) * TILE_SIZE + TILE_SIZE / 2;
 

	
 
	InitializeDisasterVehicle(v, x, 0, 135, DIR_SE, ST_Small_Ufo);
 
	v->dest_tile = TileXY(MapSizeX() / 2, MapSizeY() / 2);
 
	v->age = 0;
 

	
 
	/* Allocate shadow too? */
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, 0, 0, DIR_SE, ST_Small_Ufo_Shadow);
 
		u->vehstatus |= VS_SHADOW;
 
	}
 
}
 

	
 

	
 
/* Combat airplane which destroys an oil refinery */
 
static void Disaster_Airplane_Init()
 
{
 
	Industry *i, *found;
 
	Vehicle *v, *u;
 
	int x, y;
 

	
 
	found = NULL;
 

	
 
	FOR_ALL_INDUSTRIES(i) {
 
		if ((GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_AIRPLANE_ATTACKS) &&
 
				(found == NULL || CHANCE16(1, 2))) {
 
			found = i;
 
		}
 
	}
 

	
 
	if (found == NULL) return;
 

	
 
	v = ForceAllocateSpecialVehicle();
 
	if (v == NULL) return;
 

	
 
	/* Start from the bottom (south side) of the map */
 
	x = (MapSizeX() + 9) * TILE_SIZE - 1;
 
	y = TileY(found->xy) * TILE_SIZE + 37;
 

	
 
	InitializeDisasterVehicle(v, x, y, 135, DIR_NE, ST_Airplane);
 

	
 
	u = ForceAllocateSpecialVehicle();
 
	if (u != NULL) {
 
		v->next = u;
 
		InitializeDisasterVehicle(u, x, y, 0, DIR_SE, ST_Airplane_Shadow);
 
		u->vehstatus |= VS_SHADOW;
 
	}
 
}
 

	
 

	
 
/** Combat helicopter that destroys a factory */
 
static void Disaster_Helicopter_Init()
 
{
 
	Industry *i, *found;
 
	Vehicle *v, *u, *w;
 
	int x, y;
 

	
 
	found = NULL;
 

	
src/industry_cmd.cpp
Show inline comments
 
@@ -507,385 +507,385 @@ static void AnimateTile_Industry(TileInd
 
			m = GetIndustryAnimationState(tile);
 
			if (m == 6) {
 
				SetIndustryAnimationState(tile, 0);
 
				DeleteAnimatedTile(tile);
 
			} else {
 
				SetIndustryAnimationState(tile, m + 1);
 
				MarkTileDirtyByTile(tile);
 
			}
 
		}
 
		break;
 

	
 
	case GFX_TOY_FACTORY:
 
		if ((_tick_counter & 1) == 0) {
 
			m = GetIndustryAnimationState(tile) + 1;
 

	
 
			switch (m) {
 
				case  1: SndPlayTileFx(SND_2C_MACHINERY, tile); break;
 
				case 23: SndPlayTileFx(SND_2B_COMEDY_HIT, tile); break;
 
				case 28: SndPlayTileFx(SND_2A_EXTRACT_AND_POP, tile); break;
 
				default:
 
					if (m >= 50) {
 
						int n = GetIndustryAnimationLoop(tile) + 1;
 
						m = 0;
 
						if (n >= 8) {
 
							n = 0;
 
							DeleteAnimatedTile(tile);
 
						}
 
						SetIndustryAnimationLoop(tile, n);
 
					}
 
			}
 

	
 
			SetIndustryAnimationState(tile, m);
 
			MarkTileDirtyByTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8:
 
		if ((_tick_counter & 3) == 0) {
 
			IndustryGfx gfx = GetIndustryGfx(tile);
 

	
 
			gfx = (gfx < 155) ? gfx + 1 : 148;
 
			SetIndustryGfx(tile, gfx);
 
			MarkTileDirtyByTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_OILWELL_ANIMATED_1:
 
	case GFX_OILWELL_ANIMATED_2:
 
	case GFX_OILWELL_ANIMATED_3:
 
		if ((_tick_counter & 7) == 0) {
 
			bool b = CHANCE16(1, 7);
 
			IndustryGfx gfx = GetIndustryGfx(tile);
 

	
 
			m = GetIndustryAnimationState(tile) + 1;
 
			if (m == 4 && (m = 0, ++gfx) == GFX_OILWELL_ANIMATED_3 + 1 && (gfx = GFX_OILWELL_ANIMATED_1, b)) {
 
				SetIndustryGfx(tile, GFX_OILWELL_NOT_ANIMATED);
 
				SetIndustryConstructionStage(tile, 3);
 
				DeleteAnimatedTile(tile);
 
			} else {
 
				SetIndustryAnimationState(tile, m);
 
				SetIndustryGfx(tile, gfx);
 
				MarkTileDirtyByTile(tile);
 
			}
 
		}
 
		break;
 

	
 
	case GFX_COAL_MINE_TOWER_ANIMATED:
 
	case GFX_COPPER_MINE_TOWER_ANIMATED:
 
	case GFX_GOLD_MINE_TOWER_ANIMATED: {
 
			int state = _tick_counter & 0x7FF;
 

	
 
			if ((state -= 0x400) < 0)
 
				return;
 

	
 
			if (state < 0x1A0) {
 
				if (state < 0x20 || state >= 0x180) {
 
					m = GetIndustryAnimationState(tile);
 
					if (!(m & 0x40)) {
 
						SetIndustryAnimationState(tile, m | 0x40);
 
						SndPlayTileFx(SND_0B_MINING_MACHINERY, tile);
 
					}
 
					if (state & 7)
 
						return;
 
				} else {
 
					if (state & 3)
 
						return;
 
				}
 
				m = (GetIndustryAnimationState(tile) + 1) | 0x40;
 
				if (m > 0xC2) m = 0xC0;
 
				SetIndustryAnimationState(tile, m);
 
				MarkTileDirtyByTile(tile);
 
			} else if (state >= 0x200 && state < 0x3A0) {
 
				int i;
 
				i = (state < 0x220 || state >= 0x380) ? 7 : 3;
 
				if (state & i)
 
					return;
 

	
 
				m = (GetIndustryAnimationState(tile) & 0xBF) - 1;
 
				if (m < 0x80) m = 0x82;
 
				SetIndustryAnimationState(tile, m);
 
				MarkTileDirtyByTile(tile);
 
			}
 
		} break;
 
	}
 
}
 

	
 
static void CreateIndustryEffectSmoke(TileIndex tile)
 
{
 
	uint x = TileX(tile) * TILE_SIZE;
 
	uint y = TileY(tile) * TILE_SIZE;
 
	uint z = GetTileMaxZ(tile);
 

	
 
	CreateEffectVehicle(x + 15, y + 14, z + 59, EV_CHIMNEY_SMOKE);
 
}
 

	
 
static void MakeIndustryTileBigger(TileIndex tile)
 
{
 
	byte cnt = GetIndustryConstructionCounter(tile) + 1;
 
	byte stage;
 

	
 
	if (cnt != 4) {
 
		SetIndustryConstructionCounter(tile, cnt);
 
		return;
 
	}
 

	
 
	stage = GetIndustryConstructionStage(tile) + 1;
 
	SetIndustryConstructionCounter(tile, 0);
 
	SetIndustryConstructionStage(tile, stage);
 
	StartStopIndustryTileAnimation(tile, IAT_CONSTRUCTION_STATE_CHANGE);
 
	if (stage == INDUSTRY_COMPLETED) SetIndustryCompleted(tile, true);
 

	
 
	MarkTileDirtyByTile(tile);
 

	
 
	if (!IsIndustryCompleted(tile)) return;
 

	
 
	IndustryGfx gfx = GetIndustryGfx(tile);
 
	if (gfx >= NEW_INDUSTRYTILEOFFSET) {
 
		/* New industry */
 
		const IndustryTileSpec *its = GetIndustryTileSpec(gfx);
 
		if (its->animation_info != 0xFFFF) AddAnimatedTile(tile);
 
		return;
 
	}
 

	
 
	switch (gfx) {
 
	case GFX_POWERPLANT_CHIMNEY:
 
		CreateIndustryEffectSmoke(tile);
 
		break;
 

	
 
	case GFX_OILRIG_1:
 
		if (GetIndustryGfx(tile + TileDiffXY(0, 1)) == GFX_OILRIG_1) BuildOilRig(tile);
 
		break;
 

	
 
	case GFX_TOY_FACTORY:
 
	case GFX_BUBBLE_CATCHER:
 
	case GFX_TOFFEE_QUARY:
 
		SetIndustryAnimationState(tile, 0);
 
		SetIndustryAnimationLoop(tile, 0);
 
		break;
 

	
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6:
 
	case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8:
 
		AddAnimatedTile(tile);
 
		break;
 
	}
 
}
 

	
 
static void TileLoopIndustry_BubbleGenerator(TileIndex tile)
 
{
 
	int dir;
 
	Vehicle *v;
 
	static const int8 _tileloop_ind_case_161[12] = {
 
		11,   0, -4, -14,
 
		-4, -10, -4,   1,
 
		49,  59, 60,  65,
 
	};
 

	
 
	SndPlayTileFx(SND_2E_EXTRACT_AND_POP, tile);
 

	
 
	dir = Random() & 3;
 

	
 
	v = CreateEffectVehicleAbove(
 
		TileX(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 0],
 
		TileY(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 4],
 
		_tileloop_ind_case_161[dir + 8],
 
		EV_BUBBLE
 
	);
 

	
 
	if (v != NULL) v->u.special.unk2 = dir;
 
	if (v != NULL) v->u.special.animation_substate = dir;
 
}
 

	
 
static void TileLoop_Industry(TileIndex tile)
 
{
 
	IndustryGfx newgfx;
 
	IndustryGfx gfx;
 

	
 
	if (!IsIndustryCompleted(tile)) {
 
		MakeIndustryTileBigger(tile);
 
		return;
 
	}
 

	
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	TransportIndustryGoods(tile);
 

	
 
	if (StartStopIndustryTileAnimation(tile, IAT_TILELOOP)) return;
 

	
 
	newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_next;
 
	if (newgfx != INDUSTRYTILE_NOANIM) {
 
		ResetIndustryConstructionStage(tile);
 
		SetIndustryGfx(tile, newgfx);
 
		MarkTileDirtyByTile(tile);
 
		return;
 
	}
 

	
 
	gfx = GetIndustryGfx(tile);
 

	
 
	switch (gfx) {
 
	case GFX_OILRIG_1: // coast line at oilrigs
 
	case GFX_OILRIG_2:
 
	case GFX_OILRIG_3:
 
	case GFX_OILRIG_4:
 
	case GFX_OILRIG_5:
 
		TileLoop_Water(tile);
 
		break;
 

	
 
	case GFX_COAL_MINE_TOWER_NOT_ANIMATED:
 
	case GFX_COPPER_MINE_TOWER_NOT_ANIMATED:
 
	case GFX_GOLD_MINE_TOWER_NOT_ANIMATED:
 
		if (!(_tick_counter & 0x400) && CHANCE16(1, 2)) {
 
			switch (gfx) {
 
				case GFX_COAL_MINE_TOWER_NOT_ANIMATED:   gfx = GFX_COAL_MINE_TOWER_ANIMATED;   break;
 
				case GFX_COPPER_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_ANIMATED; break;
 
				case GFX_GOLD_MINE_TOWER_NOT_ANIMATED:   gfx = GFX_GOLD_MINE_TOWER_ANIMATED;   break;
 
			}
 
			SetIndustryGfx(tile, gfx);
 
			SetIndustryAnimationState(tile, 0x80);
 
			AddAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_OILWELL_NOT_ANIMATED:
 
		if (CHANCE16(1, 6)) {
 
			SetIndustryGfx(tile, GFX_OILWELL_ANIMATED_1);
 
			SetIndustryAnimationState(tile, 0);
 
			AddAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_COAL_MINE_TOWER_ANIMATED:
 
	case GFX_COPPER_MINE_TOWER_ANIMATED:
 
	case GFX_GOLD_MINE_TOWER_ANIMATED:
 
		if (!(_tick_counter & 0x400)) {
 
			switch (gfx) {
 
				case GFX_COAL_MINE_TOWER_ANIMATED:   gfx = GFX_COAL_MINE_TOWER_NOT_ANIMATED;   break;
 
				case GFX_COPPER_MINE_TOWER_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_NOT_ANIMATED; break;
 
				case GFX_GOLD_MINE_TOWER_ANIMATED:   gfx = GFX_GOLD_MINE_TOWER_NOT_ANIMATED;   break;
 
			}
 
			SetIndustryGfx(tile, gfx);
 
			SetIndustryCompleted(tile, true);
 
			SetIndustryConstructionStage(tile, 3);
 
			DeleteAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_POWERPLANT_SPARKS:
 
		if (CHANCE16(1, 3)) {
 
			SndPlayTileFx(SND_0C_ELECTRIC_SPARK, tile);
 
			AddAnimatedTile(tile);
 
		}
 
		break;
 

	
 
	case GFX_COPPER_MINE_CHIMNEY:
 
		CreateEffectVehicleAbove(TileX(tile) * TILE_SIZE + 6, TileY(tile) * TILE_SIZE + 6, 43, EV_SMOKE);
 
		break;
 

	
 

	
 
	case GFX_TOY_FACTORY: {
 
			Industry *i = GetIndustryByTile(tile);
 
			if (i->was_cargo_delivered) {
 
				i->was_cargo_delivered = false;
 
				SetIndustryAnimationLoop(tile, 0);
 
				AddAnimatedTile(tile);
 
			}
 
		}
 
		break;
 

	
 
	case GFX_BUBBLE_GENERATOR:
 
		TileLoopIndustry_BubbleGenerator(tile);
 
		break;
 

	
 
	case GFX_TOFFEE_QUARY:
 
		AddAnimatedTile(tile);
 
		break;
 

	
 
	case GFX_SUGAR_MINE_SIEVE:
 
		if (CHANCE16(1, 3)) AddAnimatedTile(tile);
 
		break;
 
	}
 
}
 

	
 
static void ClickTile_Industry(TileIndex tile)
 
{
 
	ShowIndustryViewWindow(GetIndustryIndex(tile));
 
}
 

	
 
static uint32 GetTileTrackStatus_Industry(TileIndex tile, TransportType mode, uint sub_mode)
 
{
 
	return 0;
 
}
 

	
 
static void GetProducedCargo_Industry(TileIndex tile, CargoID *b)
 
{
 
	const IndustrySpec *i = GetIndustrySpec(GetIndustryByTile(tile)->type);
 

	
 
	b[0] = i->produced_cargo[0];
 
	b[1] = i->produced_cargo[1];
 
}
 

	
 
static void ChangeTileOwner_Industry(TileIndex tile, PlayerID old_player, PlayerID new_player)
 
{
 
	/* If the founder merges, the industry was created by the merged company */
 
	Industry *i = GetIndustryByTile(tile);
 
	if (i->founder == old_player) i->founder = (new_player == PLAYER_SPECTATOR) ? OWNER_NONE : new_player;
 
}
 

	
 
static const byte _plantfarmfield_type[] = {1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6};
 

	
 
static bool IsBadFarmFieldTile(TileIndex tile)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_CLEAR: return IsClearGround(tile, CLEAR_FIELDS) || IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT);
 
		case MP_TREES: return false;
 
		default:       return true;
 
	}
 
}
 

	
 
static bool IsBadFarmFieldTile2(TileIndex tile)
 
{
 
	switch (GetTileType(tile)) {
 
		case MP_CLEAR: return IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT);
 
		case MP_TREES: return false;
 
		default:       return true;
 
	}
 
}
 

	
 
static void SetupFarmFieldFence(TileIndex tile, int size, byte type, Axis direction)
 
{
 
	do {
 
		tile = TILE_MASK(tile);
 

	
 
		if (IsTileType(tile, MP_CLEAR) || IsTileType(tile, MP_TREES)) {
 
			byte or_ = type;
 

	
 
			if (or_ == 1 && CHANCE16(1, 7)) or_ = 2;
 

	
 
			if (direction == AXIS_X) {
 
				SetFenceSE(tile, or_);
 
			} else {
 
				SetFenceSW(tile, or_);
 
			}
 
		}
 

	
 
		tile += (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
 
	} while (--size);
 
}
 

	
 
static void PlantFarmField(TileIndex tile, IndustryID industry)
 
{
 
	uint size_x, size_y;
 
	uint32 r;
 
	uint count;
 
	uint counter;
 
	uint field_type;
 
	int type;
 

	
 
	if (_opt.landscape == LT_ARCTIC) {
 
		if (GetTileZ(tile) + TILE_HEIGHT * 2 >= GetSnowLine())
 
			return;
 
	}
 

	
src/oldloader.cpp
Show inline comments
 
@@ -864,395 +864,395 @@ static const OldChunks player_ai_chunk[]
 

	
 
	OCL_SVAR(  OC_UINT8, PlayerAI, start_dir_a ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, start_dir_b ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, cur_dir_a ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, cur_dir_b ),
 

	
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_tile_count ),
 

	
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[0] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[0] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[1] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[1] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[2] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[2] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[3] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[3] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[4] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[4] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[5] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[5] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[6] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[6] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[7] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[7] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[8] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[8] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[9] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[9] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[10] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[10] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[11] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[11] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[12] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[12] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[13] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[13] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[14] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[14] ),
 
	OCL_SVAR(   OC_TILE, PlayerAI, banned_tiles[15] ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, banned_val[15] ),
 

	
 
	OCL_SVAR(  OC_UINT8, PlayerAI, railtype_to_use ),
 
	OCL_SVAR(  OC_UINT8, PlayerAI, route_type_mask ),
 

	
 
	OCL_END()
 
};
 

	
 
static bool OldPlayerAI(LoadgameState *ls, int num)
 
{
 
	Player *p = GetPlayer(_current_player_id);
 

	
 
	return LoadChunk(ls, &p->ai, player_ai_chunk);
 
}
 

	
 
static const OldChunks player_chunk[] = {
 
	OCL_VAR ( OC_UINT16,   1, &_old_string_id ),
 
	OCL_SVAR( OC_UINT32, Player, name_2 ),
 
	OCL_SVAR( OC_UINT32, Player, face ),
 
	OCL_VAR ( OC_UINT16,   1, &_old_string_id_2 ),
 
	OCL_SVAR( OC_UINT32, Player, president_name_2 ),
 

	
 
	OCL_SVAR(  OC_INT32, Player, player_money ),
 
	OCL_SVAR(  OC_INT32, Player, current_loan ),
 

	
 
	OCL_SVAR(  OC_UINT8, Player, player_color ),
 
	OCL_SVAR(  OC_UINT8, Player, player_money_fraction ),
 
	OCL_SVAR(  OC_UINT8, Player, quarters_of_bankrupcy ),
 
	OCL_SVAR(  OC_UINT8, Player, bankrupt_asked ),
 
	OCL_SVAR( OC_UINT32, Player, bankrupt_value ),
 
	OCL_SVAR( OC_UINT16, Player, bankrupt_timeout ),
 

	
 
	OCL_SVAR( OC_FILE_U32 | OC_VAR_U16, Player, cargo_types ),
 

	
 
	OCL_CHUNK( 3, OldPlayerYearly ),
 
	OCL_CHUNK( 1, OldPlayerEconomy ),
 

	
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Player, inaugurated_year),
 
	OCL_SVAR(                  OC_TILE, Player, last_build_coordinate ),
 
	OCL_SVAR(                 OC_UINT8, Player, num_valid_stat_ent ),
 

	
 
	OCL_CHUNK( 1, OldPlayerAI ),
 

	
 
	OCL_SVAR(  OC_UINT8, Player, block_preview ),
 
	OCL_SVAR(  OC_UINT8, Player, ai.tick ),
 
	OCL_SVAR(  OC_UINT8, Player, avail_railtypes ),
 
	OCL_SVAR(   OC_TILE, Player, location_of_house ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[0] ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[1] ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[2] ),
 
	OCL_SVAR(  OC_UINT8, Player, share_owners[3] ),
 

	
 
	OCL_NULL( 8 ), ///< junk at end of chunk
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldPlayer(LoadgameState *ls, int num)
 
{
 
	Player *p = GetPlayer((PlayerID)num);
 

	
 
	_current_player_id = (PlayerID)num;
 

	
 
	if (!LoadChunk(ls, p, player_chunk)) return false;
 

	
 
	p->name_1 = RemapOldStringID(_old_string_id);
 
	p->president_name_1 = RemapOldStringID(_old_string_id_2);
 
	p->player_money = p->player_money;
 

	
 
	if (num == 0) {
 
		/* If the first player has no name, make sure we call it UNNAMED */
 
		if (p->name_1 == 0)
 
			p->name_1 = STR_SV_UNNAMED;
 
	} else {
 
		/* Beside some multiplayer maps (1 on 1), which we don't official support,
 
		all other players are an AI.. mark them as such */
 
		p->is_ai = true;
 
	}
 

	
 
	/* Sometimes it is better to not ask.. in old scenarios, the money
 
	was always 893288 pounds. In the newer versions this is correct,
 
	but correct for those oldies
 
	Ps: this also means that if you had exact 893288 pounds, you will go back
 
	to 10000.. this is a very VERY small chance ;) */
 
	if (p->player_money == 893288)
 
		p->player_money = p->current_loan = 100000;
 

	
 
	_player_colors[num] = p->player_color;
 
	p->inaugurated_year -= ORIGINAL_BASE_YEAR;
 
	if (p->location_of_house == 0xFFFF)
 
		p->location_of_house = 0;
 

	
 
	/* State 20 for AI players is sell vehicle. Since the AI struct is not
 
	 * really figured out as of now, p->ai.cur_veh; needed for 'sell vehicle'
 
	 * is NULL and the function will crash. To fix this, just change the state
 
	 * to some harmless state, like 'loop vehicle'; 1 */
 
	if (!IsHumanPlayer((PlayerID)num) && p->ai.state == 20) p->ai.state = 1;
 

	
 
	if (p->is_ai && (!_networking || _network_server) && _ai.enabled)
 
		AI_StartNewAI(p->index);
 

	
 
	return true;
 
}
 

	
 
static uint32 _old_order_ptr;
 
static uint16 _old_next_ptr;
 
static uint32 _current_vehicle_id;
 

	
 
static const OldChunks vehicle_train_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleRail, track ),
 
	OCL_SVAR(  OC_UINT8, VehicleRail, force_proceed ),
 
	OCL_SVAR( OC_UINT16, VehicleRail, crash_anim_pos ),
 
	OCL_SVAR(  OC_UINT8, VehicleRail, railtype ),
 

	
 
	OCL_NULL( 5 ), ///< Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_road_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, state ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, frame ),
 
	OCL_SVAR( OC_UINT16, VehicleRoad, blocked_ctr ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, overtaking ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, overtaking_ctr ),
 
	OCL_SVAR( OC_UINT16, VehicleRoad, crashed_ctr ),
 
	OCL_SVAR(  OC_UINT8, VehicleRoad, reverse_ctr ),
 

	
 
	OCL_NULL( 1 ), ///< Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_ship_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleShip, state ),
 

	
 
	OCL_NULL( 9 ), ///< Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_air_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, VehicleAir, pos ),
 
	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, VehicleAir, targetairport ),
 
	OCL_SVAR( OC_UINT16, VehicleAir, crashed_counter ),
 
	OCL_SVAR(  OC_UINT8, VehicleAir, state ),
 

	
 
	OCL_NULL( 5 ), ///< Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_special_chunk[] = {
 
	OCL_SVAR( OC_UINT16, VehicleSpecial, unk0 ),
 
	OCL_SVAR(  OC_UINT8, VehicleSpecial, unk2 ),
 
	OCL_SVAR( OC_UINT16, VehicleSpecial, animation_state ),
 
	OCL_SVAR(  OC_UINT8, VehicleSpecial, animation_substate ),
 

	
 
	OCL_NULL( 7 ), // Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_disaster_chunk[] = {
 
	OCL_SVAR( OC_UINT16, VehicleDisaster, image_override ),
 
	OCL_SVAR( OC_UINT16, VehicleDisaster, unk2 ),
 
	OCL_SVAR( OC_UINT16, VehicleDisaster, big_ufo_destroyer_target ),
 

	
 
	OCL_NULL( 6 ), ///< Junk
 

	
 
	OCL_END()
 
};
 

	
 
static const OldChunks vehicle_empty_chunk[] = {
 
	OCL_NULL( 10 ), ///< Junk
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldVehicleUnion(LoadgameState *ls, int num)
 
{
 
	Vehicle *v = GetVehicle(_current_vehicle_id);
 
	uint temp = ls->total_read;
 
	bool res;
 

	
 
	/* We changed the offset of the vehicle types, so fix it
 
	 * Basically v->type -= 0x10; would suffice, but play safely */
 
	switch (v->type) {
 
		default: NOT_REACHED();
 
		case 0x00 /*VEH_INVALID */: v = new (v) InvalidVehicle();  res = LoadChunk(ls, NULL,           vehicle_empty_chunk);    break;
 
		case 0x10 /*VEH_TRAIN   */: v = new (v) Train();           res = LoadChunk(ls, &v->u.rail,     vehicle_train_chunk);    break;
 
		case 0x11 /*VEH_ROAD    */: v = new (v) RoadVehicle();     res = LoadChunk(ls, &v->u.road,     vehicle_road_chunk);     break;
 
		case 0x12 /*VEH_SHIP    */: v = new (v) Ship();            res = LoadChunk(ls, &v->u.ship,     vehicle_ship_chunk);     break;
 
		case 0x13 /*VEH_AIRCRAFT*/: v = new (v) Aircraft();        res = LoadChunk(ls, &v->u.air,      vehicle_air_chunk);      break;
 
		case 0x14 /*VEH_SPECIAL */: v = new (v) SpecialVehicle();  res = LoadChunk(ls, &v->u.special,  vehicle_special_chunk);  break;
 
		case 0x15 /*VEH_DISASTER*/: v = new (v) DisasterVehicle(); res = LoadChunk(ls, &v->u.disaster, vehicle_disaster_chunk); break;
 
	}
 

	
 
	/* This chunk size should always be 10 bytes */
 
	if (ls->total_read - temp != 10) {
 
		DEBUG(oldloader, 0, "Assert failed in VehicleUnion: invalid chunk size");
 
		return false;
 
	}
 

	
 
	return res;
 
}
 

	
 
static uint16 _cargo_count;
 

	
 
static const OldChunks vehicle_chunk[] = {
 
	OCL_SVAR(  OC_UINT8, Vehicle, type ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, subtype ),
 

	
 
	OCL_NULL( 2 ),         ///< Hash, calculated automatically
 
	OCL_NULL( 2 ),         ///< Index, calculated automatically
 

	
 
	OCL_VAR ( OC_UINT32,   1, &_old_order_ptr ),
 
	OCL_VAR ( OC_UINT16,   1, &_old_order ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, num_orders ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, cur_order_index ),
 
	OCL_SVAR(   OC_TILE, Vehicle, dest_tile ),
 
	OCL_SVAR( OC_UINT16, Vehicle, load_unload_time_rem ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ),
 
	OCL_SVAR( OC_UINT16, Vehicle, service_interval ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, last_station_visited ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, tick_counter ),
 
	OCL_SVAR( OC_UINT16, Vehicle, max_speed ),
 

	
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, x_pos ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Vehicle, y_pos ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, z_pos ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, direction ),
 
	OCL_NULL( 2 ),         ///< x_offs and y_offs, calculated automatically
 
	OCL_NULL( 2 ),         ///< sprite_width and sprite_height, calculated automatically
 
	OCL_NULL( 1 ),         ///< z_height, calculated automatically
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, owner ),
 
	OCL_SVAR(   OC_TILE, Vehicle, tile ),
 
	OCL_SVAR( OC_UINT16, Vehicle, cur_image ),
 

	
 
	OCL_NULL( 8 ),        ///< Vehicle sprite box, calculated automatically
 

	
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U8, Vehicle, vehstatus ),
 
	OCL_SVAR( OC_UINT16, Vehicle, cur_speed ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, subspeed ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, acceleration ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, progress ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, cargo_type ),
 
	OCL_SVAR( OC_UINT16, Vehicle, cargo_cap ),
 
	OCL_VAR ( OC_UINT16, 1,       &_cargo_count ),
 
	OCL_VAR (  OC_UINT8, 1,       &_cargo_source ),
 
	OCL_VAR (  OC_UINT8, 1,       &_cargo_days ),
 

	
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, age ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, max_age ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_I32, Vehicle, build_year ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, unitnumber ),
 

	
 
	OCL_SVAR( OC_UINT16, Vehicle, engine_type ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, spritenum ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, day_counter ),
 

	
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdowns_since_last_service ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdown_ctr ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdown_delay ),
 
	OCL_SVAR(  OC_UINT8, Vehicle, breakdown_chance ),
 

	
 
	OCL_SVAR( OC_UINT16, Vehicle, reliability ),
 
	OCL_SVAR( OC_UINT16, Vehicle, reliability_spd_dec ),
 

	
 
	OCL_SVAR(  OC_INT32, Vehicle, profit_this_year ),
 
	OCL_SVAR(  OC_INT32, Vehicle, profit_last_year ),
 

	
 
	OCL_VAR ( OC_UINT16,   1, &_old_next_ptr ),
 

	
 
	OCL_SVAR( OC_UINT32, Vehicle, value ),
 

	
 
	OCL_VAR ( OC_UINT16,   1, &_old_string_id ),
 

	
 
	OCL_CHUNK( 1, LoadOldVehicleUnion ),
 

	
 
	OCL_NULL( 20 ), ///< Junk at end of struct (TTDPatch has some data in it)
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldVehicle(LoadgameState *ls, int num)
 
{
 
	uint i;
 

	
 
	/* Read the TTDPatch flags, because we need some info from it */
 
	ReadTTDPatchFlags();
 

	
 
	for (i = 0; i < _old_vehicle_multiplier; i++) {
 
		Vehicle *v;
 

	
 
		_current_vehicle_id = num * _old_vehicle_multiplier + i;
 

	
 
		if (!AddBlockIfNeeded(&_Vehicle_pool, _current_vehicle_id))
 
			error("Vehicles: failed loading savegame: too many vehicles");
 

	
 
		v = GetVehicle(_current_vehicle_id);
 
		if (!LoadChunk(ls, v, vehicle_chunk)) return false;
 

	
 
		/* This should be consistent, else we have a big problem... */
 
		if (v->index != _current_vehicle_id) {
 
			DEBUG(oldloader, 0, "Loading failed - vehicle-array is invalid");
 
			return false;
 
		}
 

	
 
		if (_old_order_ptr != 0 && _old_order_ptr != 0xFFFFFFFF) {
 
			v->orders = GetOrder(REMAP_ORDER_IDX(_old_order_ptr));
 
		}
 
		AssignOrder(&v->current_order, UnpackOldOrder(_old_order));
 

	
 
		/* For some reason we need to correct for this */
 
		switch (v->spritenum) {
 
			case 0xfd: break;
 
			case 0xff: v->spritenum = 0xfe; break;
 
			default:   v->spritenum >>= 1; break;
 
		}
 

	
 
		if (_old_next_ptr != 0xFFFF) v->next = GetVehicle(_old_next_ptr);
 

	
 
		v->string_id = RemapOldStringID(_old_string_id);
 

	
 
		/* Vehicle-subtype is different in TTD(Patch) */
 
		if (v->type == VEH_SPECIAL) v->subtype = v->subtype >> 1;
 

	
 
		if (_cargo_count != 0) {
 
			CargoPacket *cp = new CargoPacket((_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, _cargo_count);
 
			cp->days_in_transit = _cargo_days;
 
			v->cargo.Append(cp);
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
static const OldChunks sign_chunk[] = {
 
	OCL_SVAR( OC_UINT16, Sign, str ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, x ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I32, Sign, y ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_I8, Sign, z ),
 

	
 
	OCL_NULL( 6 ),         ///< Width of sign, no longer in use
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldSign(LoadgameState *ls, int num)
 
{
 
	if (!AddBlockIfNeeded(&_Sign_pool, num))
 
		error("Signs: failed loading savegame: too many signs");
 

	
 
	return LoadChunk(ls, GetSign(num), sign_chunk);
src/roadveh_cmd.cpp
Show inline comments
 
@@ -548,385 +548,385 @@ CommandCost CmdTurnRoadVeh(TileIndex til
 
	if (IsTunnelTile(v->tile) && DirToDiagDir(v->direction) == GetTunnelDirection(v->tile)) return CMD_ERROR;
 
	if (IsBridgeTile(v->tile) && DirToDiagDir(v->direction) == GetBridgeRampDirection(v->tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) v->u.road.reverse_ctr = 180;
 

	
 
	return CommandCost();
 
}
 

	
 

	
 
void RoadVehicle::MarkDirty()
 
{
 
	this->cur_image = this->GetImage(this->direction);
 
	MarkAllViewportsDirty(this->left_coord, this->top_coord, this->right_coord + 1, this->bottom_coord + 1);
 
}
 

	
 
void RoadVehicle::UpdateDeltaXY(Direction direction)
 
{
 
#define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
 
	static const uint32 _delta_xy_table[8] = {
 
		MKIT(3, 3, -1, -1),
 
		MKIT(3, 7, -1, -3),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(7, 3, -3, -1),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(3, 7, -1, -3),
 
		MKIT(3, 3, -1, -1),
 
		MKIT(7, 3, -3, -1),
 
	};
 
#undef MKIT
 

	
 
	uint32 x = _delta_xy_table[direction];
 
	this->x_offs        = GB(x,  0, 8);
 
	this->y_offs        = GB(x,  8, 8);
 
	this->sprite_width  = GB(x, 16, 8);
 
	this->sprite_height = GB(x, 24, 8);
 
	this->z_height      = 6;
 
}
 

	
 
static void ClearCrashedStation(Vehicle *v)
 
{
 
	RoadStop *rs = GetRoadStopByTile(v->tile, GetRoadStopType(v->tile));
 

	
 
	/* Mark the station entrance as not busy */
 
	rs->SetEntranceBusy(false);
 

	
 
	/* Free the parking bay */
 
	rs->FreeBay(HASBIT(v->u.road.state, RVS_USING_SECOND_BAY));
 
}
 

	
 
static void DeleteLastRoadVeh(Vehicle *v)
 
{
 
	Vehicle *u = v;
 
	for (; v->next != NULL; v = v->next) u = v;
 
	u->next = NULL;
 

	
 
	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 

	
 
	RebuildVehicleLists();
 
	InvalidateWindow(WC_COMPANY, v->owner);
 

	
 
	if (IsTileType(v->tile, MP_STATION)) ClearCrashedStation(v);
 

	
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 

	
 
	DeleteVehicle(v);
 
}
 

	
 
static byte SetRoadVehPosition(Vehicle *v, int x, int y)
 
{
 
	byte new_z, old_z;
 

	
 
	/* need this hint so it returns the right z coordinate on bridges. */
 
	v->x_pos = x;
 
	v->y_pos = y;
 
	new_z = GetSlopeZ(x, y);
 

	
 
	old_z = v->z_pos;
 
	v->z_pos = new_z;
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
	return old_z;
 
}
 

	
 
static void RoadVehSetRandomDirection(Vehicle *v)
 
{
 
	static const DirDiff delta[] = {
 
		DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
 
	};
 

	
 
	do {
 
		uint32 r = Random();
 

	
 
		v->direction = ChangeDir(v->direction, delta[r & 3]);
 
		BeginVehicleMove(v);
 
		v->UpdateDeltaXY(v->direction);
 
		v->cur_image = v->GetImage(v->direction);
 
		SetRoadVehPosition(v, v->x_pos, v->y_pos);
 
	} while ((v = v->next) != NULL);
 
}
 

	
 
static void RoadVehIsCrashed(Vehicle *v)
 
{
 
	v->u.road.crashed_ctr++;
 
	if (v->u.road.crashed_ctr == 2) {
 
		CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
 
	} else if (v->u.road.crashed_ctr <= 45) {
 
		if ((v->tick_counter & 7) == 0) RoadVehSetRandomDirection(v);
 
	} else if (v->u.road.crashed_ctr >= 2220 && !(v->tick_counter & 0x1F)) {
 
		DeleteLastRoadVeh(v);
 
	}
 
}
 

	
 
static void* EnumCheckRoadVehCrashTrain(Vehicle* v, void* data)
 
{
 
	const Vehicle* u = (Vehicle*)data;
 

	
 
	return
 
		v->type == VEH_TRAIN &&
 
		myabs(v->z_pos - u->z_pos) <= 6 &&
 
		myabs(v->x_pos - u->x_pos) <= 4 &&
 
		myabs(v->y_pos - u->y_pos) <= 4 ?
 
			v : NULL;
 
}
 

	
 
static void RoadVehCrash(Vehicle *v)
 
{
 
	uint16 pass = 1;
 

	
 
	v->u.road.crashed_ctr++;
 

	
 
	for (Vehicle *u = v; u != NULL; u = u->next) {
 
		if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count();
 

	
 
		u->vehstatus |= VS_CRASHED;
 

	
 
		MarkAllViewportsDirty(u->left_coord, u->top_coord, u->right_coord + 1, u->bottom_coord + 1);
 
	}
 

	
 
	ClearSlot(v);
 

	
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 

	
 
	SetDParam(0, pass);
 
	AddNewsItem(
 
		(pass == 1) ?
 
			STR_9031_ROAD_VEHICLE_CRASH_DRIVER : STR_9032_ROAD_VEHICLE_CRASH_DIE,
 
		NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ACCIDENT, 0),
 
		v->index,
 
		0
 
	);
 

	
 
	ModifyStationRatingAround(v->tile, v->owner, -160, 22);
 
	SndPlayVehicleFx(SND_12_EXPLOSION, v);
 
}
 

	
 
static void RoadVehCheckTrainCrash(Vehicle *v)
 
{
 
	for (Vehicle *u = v; u != NULL; u = u->next) {
 
		if (u->u.road.state == RVSB_WORMHOLE) continue;
 

	
 
		TileIndex tile = u->tile;
 

	
 
		if (!IsLevelCrossingTile(tile)) continue;
 

	
 
		if (VehicleFromPosXY(v->x_pos, v->y_pos, u, EnumCheckRoadVehCrashTrain) != NULL) {
 
			RoadVehCrash(v);
 
			return;
 
		}
 
	}
 
}
 

	
 
static void HandleBrokenRoadVeh(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_TOYLAND) ?
 
				SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
			if (u != NULL) u->u.special.animation_state = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if ((v->tick_counter & 1) == 0) {
 
		if (--v->breakdown_delay == 0) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
static void ProcessRoadVehOrder(Vehicle *v)
 
{
 
	const Order *order;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			/* Let a depot order in the orderlist interrupt. */
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return;
 
			if (v->current_order.flags & OF_SERVICE_IF_NEEDED &&
 
					!VehicleNeedsService(v)) {
 
				UpdateVehicleTimetable(v, true);
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING:
 
		case OT_LEAVESTATION:
 
			return;
 

	
 
		default: break;
 
	}
 

	
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	if (order == NULL) {
 
		v->current_order.Free();
 
		v->dest_tile = 0;
 
		ClearSlot(v);
 
		return;
 
	}
 

	
 
	if (order->type  == v->current_order.type &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest) {
 
		return;
 
	}
 

	
 
	v->current_order = *order;
 

	
 
	switch (order->type) {
 
		case OT_GOTO_STATION: {
 
			const RoadStop* rs;
 

	
 
			if (order->dest == v->last_station_visited) {
 
				v->last_station_visited = INVALID_STATION;
 
			}
 

	
 
			rs = GetStation(order->dest)->GetPrimaryRoadStop(
 
				IsCargoInClass(v->cargo_type, CC_PASSENGERS) ? RoadStop::BUS : RoadStop::TRUCK
 
			);
 

	
 
			TileIndex dest = INVALID_TILE;
 
			if (rs != NULL) {
 
				uint mindist = MAX_UVALUE(uint);
 

	
 
				for (; rs != NULL; rs = rs->next) {
 
					/* The vehicle cannot go to this roadstop */
 
					if ((GetRoadTypes(rs->xy) & v->u.road.compatible_roadtypes) == ROADTYPES_NONE) continue;
 

	
 
					uint dist = DistanceManhattan(v->tile, rs->xy);
 

	
 
					if (dist < mindist) {
 
						mindist = dist;
 
						dest = rs->xy;
 
					}
 
				}
 
			}
 

	
 
			if (dest != INVALID_TILE) {
 
					v->dest_tile = dest;
 
			} else {
 
				/* There is no stop left at the station, so don't even TRY to go there */
 
				v->cur_order_index++;
 
				v->dest_tile = 0;
 
			}
 
			break;
 
		}
 

	
 
		case OT_GOTO_DEPOT:
 
			v->dest_tile = GetDepot(order->dest)->xy;
 
			break;
 

	
 
		default:
 
			v->dest_tile = 0;
 
			break;
 
	}
 

	
 
	InvalidateVehicleOrder(v);
 
}
 

	
 
static void StartRoadVehSound(const Vehicle* v)
 
{
 
	if (!PlayVehicleSound(v, VSE_START)) {
 
		SoundFx s = RoadVehInfo(v->engine_type)->sfx;
 
		if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
 
			s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
 
		SndPlayVehicleFx(s, v);
 
	}
 
}
 

	
 
struct RoadVehFindData {
 
	int x;
 
	int y;
 
	const Vehicle* veh;
 
	Direction dir;
 
};
 

	
 
static void* EnumCheckRoadVehClose(Vehicle *v, void* data)
 
{
 
	static const int8 dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 };
 
	static const int8 dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 };
 

	
 
	const RoadVehFindData* rvf = (RoadVehFindData*)data;
 

	
 
	short x_diff = v->x_pos - rvf->x;
 
	short y_diff = v->y_pos - rvf->y;
 

	
 
	return
 
		v->type == VEH_ROAD &&
 
		!IsRoadVehInDepot(v) &&
 
		myabs(v->z_pos - rvf->veh->z_pos) < 6 &&
 
		v->direction == rvf->dir &&
 
		GetFirstVehicleInChain(rvf->veh) != GetFirstVehicleInChain(v) &&
 
		(dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) &&
 
		(dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) &&
 
		(dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) &&
 
		(dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0)) ?
 
			v : NULL;
 
}
 

	
 
static Vehicle* RoadVehFindCloseTo(Vehicle* v, int x, int y, Direction dir)
 
{
 
	RoadVehFindData rvf;
 
	Vehicle *u;
 

	
 
	if (v->u.road.reverse_ctr != 0) return NULL;
 

	
 
	rvf.x = x;
 
	rvf.y = y;
 
	rvf.dir = dir;
 
	rvf.veh = v;
 
	u = (Vehicle*)VehicleFromPosXY(x, y, &rvf, EnumCheckRoadVehClose);
 

	
 
	/* This code protects a roadvehicle from being blocked for ever
 
	 * If more than 1480 / 74 days a road vehicle is blocked, it will
 
	 * drive just through it. The ultimate backup-code of TTD.
 
	 * It can be disabled. */
 
	if (u == NULL) {
 
		v->u.road.blocked_ctr = 0;
 
		return NULL;
 
	}
 

	
 
	if (++v->u.road.blocked_ctr > 1480) return NULL;
 

	
 
	return u;
 
}
 

	
 
static void RoadVehArrivesAt(const Vehicle* v, Station* st)
 
{
 
	if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) {
 
		/* Check if station was ever visited before */
 
		if (!(st->had_vehicle_of_type & HVOT_BUS)) {
 
			uint32 flags;
 

	
 
			st->had_vehicle_of_type |= HVOT_BUS;
 
			SetDParam(0, st->index);
 
			flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
 
			AddNewsItem(
 
				v->u.road.roadtype == ROADTYPE_ROAD ? STR_902F_CITIZENS_CELEBRATE_FIRST : STR_902F_CITIZENS_CELEBRATE_FIRST_TRAM,
 
				flags,
 
				v->index,
 
				0);
 
		}
 
	} else {
 
		/* Check if station was ever visited before */
 
		if (!(st->had_vehicle_of_type & HVOT_TRUCK)) {
 
			uint32 flags;
 

	
 
			st->had_vehicle_of_type |= HVOT_TRUCK;
src/ship_cmd.cpp
Show inline comments
 
@@ -31,385 +31,385 @@
 
#include "newgrf_callbacks.h"
 
#include "newgrf_text.h"
 
#include "newgrf_sound.h"
 
#include "date.h"
 
#include "spritecache.h"
 

	
 
static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
 

	
 
static const TrackBits _ship_sometracks[4] = {
 
	TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_LEFT,  // 0x19, // DIAGDIR_NE
 
	TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT,  // 0x16, // DIAGDIR_SE
 
	TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT, // 0x25, // DIAGDIR_SW
 
	TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_RIGHT, // 0x2A, // DIAGDIR_NW
 
};
 

	
 
static TrackBits GetTileShipTrackStatus(TileIndex tile)
 
{
 
	uint32 r = GetTileTrackStatus(tile, TRANSPORT_WATER, 0);
 
	return TrackdirBitsToTrackBits((TrackdirBits)(TRACKDIR_BIT_MASK & (r | r >> 8)));
 
}
 

	
 
void DrawShipEngine(int x, int y, EngineID engine, SpriteID pal)
 
{
 
	int spritenum = ShipVehInfo(engine)->image_index;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleIcon(engine, DIR_W);
 

	
 
		if (sprite != 0) {
 
			DrawSprite(sprite, pal, x, y);
 
			return;
 
		}
 
		spritenum = orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
 
	}
 
	DrawSprite(6 + _ship_sprites[spritenum], pal, x, y);
 
}
 

	
 
/** Get the size of the sprite of a ship sprite heading west (used for lists)
 
 * @param engine The engine to get the sprite from
 
 * @param width The width of the sprite
 
 * @param height The height of the sprite
 
 */
 
void GetShipSpriteSize(EngineID engine, uint &width, uint &height)
 
{
 
	SpriteID spritenum = ShipVehInfo(engine)->image_index;
 
	SpriteID custom_sprite = 0;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		custom_sprite = GetCustomVehicleIcon(engine, DIR_W);
 
		spritenum = orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
 
	}
 
	if (custom_sprite == 0) {
 
		spritenum = 6 + _ship_sprites[spritenum];
 
	} else {
 
		spritenum = custom_sprite;
 
	}
 

	
 
	const Sprite *spr = GetSprite(spritenum);
 

	
 
	width  = spr->width;
 
	height = spr->height;
 
}
 

	
 
int Ship::GetImage(Direction direction) const
 
{
 
	int spritenum = this->spritenum;
 

	
 
	if (is_custom_sprite(spritenum)) {
 
		int sprite = GetCustomVehicleSprite(this, direction);
 

	
 
		if (sprite != 0) return sprite;
 
		spritenum = orig_ship_vehicle_info[this->engine_type - SHIP_ENGINES_INDEX].image_index;
 
	}
 
	return _ship_sprites[spritenum] + direction;
 
}
 

	
 
static const Depot* FindClosestShipDepot(const Vehicle* v)
 
{
 
	const Depot* depot;
 
	const Depot* best_depot = NULL;
 
	uint dist;
 
	uint best_dist = (uint)-1;
 
	TileIndex tile;
 
	TileIndex tile2 = v->tile;
 

	
 
	if (_patches.new_pathfinding_all) {
 
		NPFFoundTargetData ftd;
 
		Trackdir trackdir = GetVehicleTrackdir(v);
 
		ftd = NPFRouteToDepotTrialError(v->tile, trackdir, TRANSPORT_WATER, 0, v->owner, INVALID_RAILTYPE);
 
		if (ftd.best_bird_dist == 0) {
 
			best_depot = GetDepotByTile(ftd.node.tile); /* Found target */
 
		} else {
 
			best_depot = NULL; /* Did not find target */
 
		}
 
	} else {
 
		FOR_ALL_DEPOTS(depot) {
 
			tile = depot->xy;
 
			if (IsTileDepotType(tile, TRANSPORT_WATER) && IsTileOwner(tile, v->owner)) {
 
				dist = DistanceManhattan(tile, tile2);
 
				if (dist < best_dist) {
 
					best_dist = dist;
 
					best_depot = depot;
 
				}
 
			}
 
		}
 
	}
 
	return best_depot;
 
}
 

	
 
static void CheckIfShipNeedsService(Vehicle *v)
 
{
 
	const Depot* depot;
 

	
 
	if (_patches.servint_ships == 0) return;
 
	if (!VehicleNeedsService(v))     return;
 
	if (v->vehstatus & VS_STOPPED)   return;
 

	
 
	if (v->current_order.type == OT_GOTO_DEPOT &&
 
			v->current_order.flags & OF_HALT_IN_DEPOT)
 
		return;
 

	
 
	if (_patches.gotodepot && VehicleHasDepotOrders(v)) return;
 

	
 
	if (IsShipInDepot(v)) {
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	depot = FindClosestShipDepot(v);
 

	
 
	if (depot == NULL || DistanceManhattan(v->tile, depot->xy) > 12) {
 
		if (v->current_order.type == OT_GOTO_DEPOT) {
 
			v->current_order.type = OT_DUMMY;
 
			v->current_order.flags = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		}
 
		return;
 
	}
 

	
 
	if (v->current_order.type == OT_LOADING) v->LeaveStation();
 
	v->current_order.type = OT_GOTO_DEPOT;
 
	v->current_order.flags = OF_NON_STOP;
 
	v->current_order.dest = depot->index;
 
	v->dest_tile = depot->xy;
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
}
 

	
 
void OnNewDay_Ship(Vehicle *v)
 
{
 
	CommandCost cost;
 

	
 
	if ((++v->day_counter & 7) == 0)
 
		DecreaseVehicleValue(v);
 

	
 
	CheckVehicleBreakdown(v);
 
	AgeVehicle(v);
 
	CheckIfShipNeedsService(v);
 

	
 
	CheckOrders(v);
 

	
 
	if (v->vehstatus & VS_STOPPED) return;
 

	
 
	cost.AddCost(GetVehicleProperty(v, 0x0F, ShipVehInfo(v->engine_type)->running_cost) * _price.ship_running / 364);
 
	v->profit_this_year -= cost.GetCost() >> 8;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_SHIP_RUN);
 
	SubtractMoneyFromPlayerFract(v->owner, CommandCost(cost));
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	/* we need this for the profit */
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static void HandleBrokenShip(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_TOYLAND) ?
 
				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
			if (u != NULL) u->u.special.animation_state = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if (!(v->tick_counter & 1)) {
 
		if (!--v->breakdown_delay) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
void Ship::MarkDirty()
 
{
 
	this->cur_image = this->GetImage(this->direction);
 
	MarkAllViewportsDirty(this->left_coord, this->top_coord, this->right_coord + 1, this->bottom_coord + 1);
 
}
 

	
 
static void PlayShipSound(const Vehicle *v)
 
{
 
	if (!PlayVehicleSound(v, VSE_START)) {
 
		SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
 
	}
 
}
 

	
 
void Ship::PlayLeaveStationSound() const
 
{
 
	PlayShipSound(this);
 
}
 

	
 
static void ProcessShipOrder(Vehicle *v)
 
{
 
	const Order *order;
 

	
 
	switch (v->current_order.type) {
 
		case OT_GOTO_DEPOT:
 
			if (!(v->current_order.flags & OF_PART_OF_ORDERS)) return;
 
			if (v->current_order.flags & OF_SERVICE_IF_NEEDED &&
 
					!VehicleNeedsService(v)) {
 
				UpdateVehicleTimetable(v, true);
 
				v->cur_order_index++;
 
			}
 
			break;
 

	
 
		case OT_LOADING:
 
		case OT_LEAVESTATION:
 
			return;
 

	
 
		default: break;
 
	}
 

	
 
	if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
 

	
 
	order = GetVehicleOrder(v, v->cur_order_index);
 

	
 
	if (order == NULL) {
 
		v->current_order.Free();
 
		v->dest_tile = 0;
 
		return;
 
	}
 

	
 
	if (order->type  == v->current_order.type &&
 
			order->flags == v->current_order.flags &&
 
			order->dest  == v->current_order.dest &&
 
			(order->type != OT_GOTO_STATION || GetStation(order->dest)->dock_tile != 0))
 
		return;
 

	
 
	v->current_order = *order;
 

	
 
	if (order->type == OT_GOTO_STATION) {
 
		const Station *st;
 

	
 
		if (order->dest == v->last_station_visited)
 
			v->last_station_visited = INVALID_STATION;
 

	
 
		st = GetStation(order->dest);
 
		if (st->dock_tile != 0) {
 
			v->dest_tile = TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
 
		} else {
 
			v->cur_order_index++;
 
		}
 
	} else if (order->type == OT_GOTO_DEPOT) {
 
		v->dest_tile = GetDepot(order->dest)->xy;
 
	} else {
 
		v->dest_tile = 0;
 
	}
 

	
 
	InvalidateVehicleOrder(v);
 

	
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
void Ship::UpdateDeltaXY(Direction direction)
 
{
 
#define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
 
	static const uint32 _delta_xy_table[8] = {
 
		MKIT( 6,  6,  -3,  -3),
 
		MKIT( 6, 32,  -3, -16),
 
		MKIT( 6,  6,  -3,  -3),
 
		MKIT(32,  6, -16,  -3),
 
		MKIT( 6,  6,  -3,  -3),
 
		MKIT( 6, 32,  -3, -16),
 
		MKIT( 6,  6,  -3,  -3),
 
		MKIT(32,  6, -16,  -3),
 
	};
 
#undef MKIT
 

	
 
	uint32 x = _delta_xy_table[direction];
 
	this->x_offs        = GB(x,  0, 8);
 
	this->y_offs        = GB(x,  8, 8);
 
	this->sprite_width  = GB(x, 16, 8);
 
	this->sprite_height = GB(x, 24, 8);
 
	this->z_height      = 6;
 
}
 

	
 
void RecalcShipStuff(Vehicle *v)
 
{
 
	v->UpdateDeltaXY(v->direction);
 
	v->cur_image = v->GetImage(v->direction);
 
	v->MarkDirty();
 
	InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
}
 

	
 
static const TileIndexDiffC _ship_leave_depot_offs[] = {
 
	{-1,  0},
 
	{ 0, -1}
 
};
 

	
 
static void CheckShipLeaveDepot(Vehicle *v)
 
{
 
	TileIndex tile;
 
	Axis axis;
 
	uint m;
 

	
 
	if (!IsShipInDepot(v)) return;
 

	
 
	tile = v->tile;
 
	axis = GetShipDepotAxis(tile);
 

	
 
	/* Check first side */
 
	if (_ship_sometracks[axis] & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
 
		m = (axis == AXIS_X) ? 0x101 : 0x207;
 
	/* Check second side */
 
	} else if (_ship_sometracks[axis + 2] & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
 
		m = (axis == AXIS_X) ? 0x105 : 0x203;
 
	} else {
 
		return;
 
	}
 
	v->direction    = (Direction)GB(m, 0, 8);
 
	v->u.ship.state = (TrackBits)GB(m, 8, 8);
 
	v->vehstatus &= ~VS_HIDDEN;
 

	
 
	v->cur_speed = 0;
 
	RecalcShipStuff(v);
 

	
 
	PlayShipSound(v);
 
	VehicleServiceInDepot(v);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	InvalidateWindowClasses(WC_SHIPS_LIST);
 
}
 

	
 
static bool ShipAccelerate(Vehicle *v)
 
{
 
	uint spd;
 
	byte t;
 

	
 
	spd = min(v->cur_speed + 1, GetVehicleProperty(v, 0x0B, v->max_speed));
 

	
 
	/*updates statusbar only if speed have changed to save CPU time */
 
	if (spd != v->cur_speed) {
 
		v->cur_speed = spd;
 
		if (_patches.vehicle_speed)
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
	}
 

	
 
	/* Decrease somewhat when turning */
 
	if (!(v->direction & 1)) spd = spd * 3 / 4;
 

	
 
	if (spd == 0) return false;
 
	if ((byte)++spd == 0) return true;
 

	
 
	v->progress = (t = v->progress) - (byte)spd;
 

	
 
	return (t < v->progress);
 
}
 

	
 
static CommandCost EstimateShipCost(EngineID engine_type)
 
{
 
	return CommandCost(GetEngineProperty(engine_type, 0x0A, ShipVehInfo(engine_type)->base_cost) * (_price.ship_base >> 3) >> 5);
 
}
 

	
 
static void ShipArrivesAt(const Vehicle* v, Station* st)
 
{
src/train_cmd.cpp
Show inline comments
 
@@ -2949,385 +2949,385 @@ static void TrainController(Vehicle *v, 
 

	
 
				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
 

	
 
				v->direction = chosen_dir;
 
			}
 
		} else {
 
			/* In tunnel or on a bridge */
 
			if (!(v->vehstatus & VS_HIDDEN)) {
 
				v->cur_speed =
 
					min(v->cur_speed, GetBridge(GetBridgeType(v->tile))->speed);
 
			}
 

	
 
			if (!(IsTunnelTile(gp.new_tile) || IsBridgeTile(gp.new_tile)) || !HASBIT(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
 
				v->x_pos = gp.x;
 
				v->y_pos = gp.y;
 
				VehiclePositionChanged(v);
 
				if (!(v->vehstatus & VS_HIDDEN)) EndVehicleMove(v);
 
				continue;
 
			}
 
		}
 

	
 
		/* update image of train, as well as delta XY */
 
		Direction newdir = GetNewVehicleDirection(v, gp.x, gp.y);
 
		v->UpdateDeltaXY(newdir);
 
		if (update_image) v->cur_image = v->GetImage(newdir);
 

	
 
		v->x_pos = gp.x;
 
		v->y_pos = gp.y;
 

	
 
		/* update the Z position of the vehicle */
 
		byte old_z = AfterSetTrainPos(v, (gp.new_tile != gp.old_tile));
 

	
 
		if (prev == NULL) {
 
			/* This is the first vehicle in the train */
 
			AffectSpeedByZChange(v, old_z);
 
		}
 

	
 
		if (update_signals) {
 
			if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
 

	
 
			/* Signals can only change when the first
 
			 * (above) or the last vehicle moves. */
 
			if (v->next == NULL) TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
 
		}
 
	}
 
	return;
 

	
 
invalid_rail:
 
	/* We've reached end of line?? */
 
	if (prev != NULL) error("!Disconnecting train");
 

	
 
reverse_train_direction:
 
	v->load_unload_time_rem = 0;
 
	v->cur_speed = 0;
 
	v->subspeed = 0;
 
	ReverseTrainDirection(v);
 
}
 

	
 
extern TileIndex CheckTunnelBusy(TileIndex tile, uint *length);
 

	
 
/**
 
 * Deletes/Clears the last wagon of a crashed train. It takes the engine of the
 
 * train, then goes to the last wagon and deletes that. Each call to this function
 
 * will remove the last wagon of a crashed train. If this wagon was on a crossing,
 
 * or inside a tunnel, recalculate the signals as they might need updating
 
 * @param v the Vehicle of which last wagon is to be removed
 
 */
 
static void DeleteLastWagon(Vehicle *v)
 
{
 
	/* Go to the last wagon and delete the link pointing there
 
	 * *u is then the one-before-last wagon, and *v the last
 
	 * one which will physicially be removed */
 
	Vehicle *u = v;
 
	for (; v->next != NULL; v = v->next) u = v;
 
	u->next = NULL;
 

	
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 
	RebuildVehicleLists();
 
	InvalidateWindow(WC_COMPANY, v->owner);
 

	
 
	BeginVehicleMove(v);
 
	EndVehicleMove(v);
 

	
 
	DeleteVehicle(v);
 

	
 
	if (v->u.rail.track != TRACK_BIT_DEPOT && v->u.rail.track != TRACK_BIT_WORMHOLE)
 
		SetSignalsOnBothDir(v->tile, FIND_FIRST_BIT(v->u.rail.track));
 

	
 
	/* Check if the wagon was on a road/rail-crossing and disable it if no
 
	 * others are on it */
 
	DisableTrainCrossing(v->tile);
 

	
 
	if ((v->u.rail.track == TRACK_BIT_WORMHOLE && v->vehstatus & VS_HIDDEN)) { // inside a tunnel
 
		TileIndex endtile = CheckTunnelBusy(v->tile, NULL);
 

	
 
		if (endtile == INVALID_TILE) return; // tunnel is busy (error returned)
 

	
 
		switch (v->direction) {
 
			case 1:
 
			case 5:
 
				SetSignalsOnBothDir(v->tile, 0);
 
				SetSignalsOnBothDir(endtile, 0);
 
				break;
 

	
 
			case 3:
 
			case 7:
 
				SetSignalsOnBothDir(v->tile, 1);
 
				SetSignalsOnBothDir(endtile, 1);
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
	}
 
}
 

	
 
static void ChangeTrainDirRandomly(Vehicle *v)
 
{
 
	static const DirDiff delta[] = {
 
		DIRDIFF_45LEFT, DIRDIFF_SAME, DIRDIFF_SAME, DIRDIFF_45RIGHT
 
	};
 

	
 
	do {
 
		/* We don't need to twist around vehicles if they're not visible */
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			v->direction = ChangeDir(v->direction, delta[GB(Random(), 0, 2)]);
 
			BeginVehicleMove(v);
 
			v->UpdateDeltaXY(v->direction);
 
			v->cur_image = v->GetImage(v->direction);
 
			/* Refrain from updating the z position of the vehicle when on
 
			   a bridge, because AfterSetTrainPos will put the vehicle under
 
			   the bridge in that case */
 
			if (v->u.rail.track != TRACK_BIT_WORMHOLE) AfterSetTrainPos(v, false);
 
		}
 
	} while ((v = v->next) != NULL);
 
}
 

	
 
static void HandleCrashedTrain(Vehicle *v)
 
{
 
	int state = ++v->u.rail.crash_anim_pos;
 

	
 
	if (state == 4 && !(v->vehstatus & VS_HIDDEN)) {
 
		CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
 
	}
 

	
 
	uint32 r;
 
	if (state <= 200 && CHANCE16R(1, 7, r)) {
 
		int index = (r * 10 >> 16);
 

	
 
		Vehicle *u = v;
 
		do {
 
			if (--index < 0) {
 
				r = Random();
 

	
 
				CreateEffectVehicleRel(u,
 
					GB(r,  8, 3) + 2,
 
					GB(r, 16, 3) + 2,
 
					GB(r,  0, 3) + 5,
 
					EV_EXPLOSION_SMALL);
 
				break;
 
			}
 
		} while ((u = u->next) != NULL);
 
	}
 

	
 
	if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
 

	
 
	if (state >= 4440 && !(v->tick_counter&0x1F)) {
 
		DeleteLastWagon(v);
 
		InvalidateWindow(WC_REPLACE_VEHICLE, (v->group_id << 16) | VEH_TRAIN);
 
	}
 
}
 

	
 
static void HandleBrokenTrain(Vehicle *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_opt.landscape != LT_TOYLAND) ?
 
				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->u.special.unk0 = v->breakdown_delay * 2;
 
			if (u != NULL) u->u.special.animation_state = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if (!(v->tick_counter & 3)) {
 
		if (!--v->breakdown_delay) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
static const byte _breakdown_speeds[16] = {
 
	225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
 
};
 

	
 
static bool TrainCheckIfLineEnds(Vehicle *v)
 
{
 
	int t = v->breakdown_ctr;
 
	if (t > 1) {
 
		v->vehstatus |= VS_TRAIN_SLOWING;
 

	
 
		uint16 break_speed = _breakdown_speeds[GB(~t, 4, 4)];
 
		if (break_speed < v->cur_speed) v->cur_speed = break_speed;
 
	} else {
 
		v->vehstatus &= ~VS_TRAIN_SLOWING;
 
	}
 

	
 
	if (v->u.rail.track == TRACK_BIT_WORMHOLE) return true; // exit if inside a tunnel
 
	if (v->u.rail.track == TRACK_BIT_DEPOT) return true; // exit if inside a depot
 

	
 
	TileIndex tile = v->tile;
 

	
 
	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
		DiagDirection dir = IsTunnel(tile) ? GetTunnelDirection(tile) : GetBridgeRampDirection(tile);
 
		if (DiagDirToDir(dir) == v->direction) return true;
 
	}
 

	
 
	// depot?
 
	/* XXX -- When enabled, this makes it possible to crash trains of others
 
	     (by building a depot right against a station) */
 
/*	if (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_DEPOT_WAYPOINT)
 
		return true;*/
 

	
 
	/* Determine the non-diagonal direction in which we will exit this tile */
 
	DiagDirection dir = DirToDiagDir(v->direction);
 
	if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[dir]) {
 
		dir = ChangeDiagDir(dir, DIAGDIRDIFF_90LEFT);
 
	}
 
	/* Calculate next tile */
 
	tile += TileOffsByDiagDir(dir);
 
	// determine the track status on the next tile.
 
	uint32 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & _reachable_tracks[dir];
 

	
 
	/* Calc position within the current tile ?? */
 
	uint x = v->x_pos & 0xF;
 
	uint y = v->y_pos & 0xF;
 

	
 
	switch (v->direction) {
 
		case DIR_N : x = ~x + ~y + 24; break;
 
		case DIR_NW: x = y;            /* FALLTHROUGH */
 
		case DIR_NE: x = ~x + 16;      break;
 
		case DIR_E : x = ~x + y + 8;   break;
 
		case DIR_SE: x = y;            break;
 
		case DIR_S : x = x + y - 8;    break;
 
		case DIR_W : x = ~y + x + 8;   break;
 
		default: break;
 
	}
 

	
 
	if (GB(ts, 0, 16) != 0) {
 
		/* If we approach a rail-piece which we can't enter, or the back of a depot, don't enter it! */
 
		if (x + 4 >= TILE_SIZE &&
 
				(!CheckCompatibleRail(v, tile) ||
 
				(IsTileDepotType(tile, TRANSPORT_RAIL) &&
 
				GetRailDepotDirection(tile) == dir))) {
 
			v->cur_speed = 0;
 
			ReverseTrainDirection(v);
 
			return false;
 
		}
 
		if ((ts &= (ts >> 16)) == 0) {
 
			/* make a rail/road crossing red */
 
			if (IsLevelCrossingTile(tile)) {
 
				if (!IsCrossingBarred(tile)) {
 
					BarCrossing(tile);
 
					SndPlayVehicleFx(SND_0E_LEVEL_CROSSING, v);
 
					MarkTileDirtyByTile(tile);
 
				}
 
			}
 
			return true;
 
		}
 
	} else if (x + 4 >= TILE_SIZE) {
 
		v->cur_speed = 0;
 
		ReverseTrainDirection(v);
 
		return false;
 
	}
 

	
 
	/* slow down */
 
	v->vehstatus |= VS_TRAIN_SLOWING;
 
	uint16 break_speed = _breakdown_speeds[x & 0xF];
 
	if (!(v->direction & 1)) break_speed >>= 1;
 
	if (break_speed < v->cur_speed) v->cur_speed = break_speed;
 

	
 
	return true;
 
}
 

	
 
static void TrainLocoHandler(Vehicle *v, bool mode)
 
{
 
	/* train has crashed? */
 
	if (v->u.rail.crash_anim_pos != 0) {
 
		if (!mode) HandleCrashedTrain(v);
 
		return;
 
	}
 

	
 
	if (v->u.rail.force_proceed != 0) v->u.rail.force_proceed--;
 

	
 
	/* train is broken down? */
 
	if (v->breakdown_ctr != 0) {
 
		if (v->breakdown_ctr <= 2) {
 
			HandleBrokenTrain(v);
 
			return;
 
		}
 
		v->breakdown_ctr--;
 
	}
 

	
 
	if (HASBIT(v->u.rail.flags, VRF_REVERSING) && v->cur_speed == 0) {
 
		ReverseTrainDirection(v);
 
	}
 

	
 
	/* exit if train is stopped */
 
	if (v->vehstatus & VS_STOPPED && v->cur_speed == 0) return;
 

	
 
	if (ProcessTrainOrder(v)) {
 
		v->load_unload_time_rem = 0;
 
		v->cur_speed = 0;
 
		v->subspeed = 0;
 
		ReverseTrainDirection(v);
 
		return;
 
	}
 

	
 
	v->HandleLoading(mode);
 

	
 
	if (v->current_order.type == OT_LOADING) return;
 

	
 
	if (CheckTrainStayInDepot(v)) return;
 

	
 
	if (!mode) HandleLocomotiveSmokeCloud(v);
 

	
 
	int j = UpdateTrainSpeed(v);
 
	if (j == 0) {
 
		/* if the vehicle has speed 0, update the last_speed field. */
 
		if (v->cur_speed != 0) return;
 
	} else {
 
		TrainCheckIfLineEnds(v);
 

	
 
		do {
 
			TrainController(v, true);
 
			CheckTrainCollision(v);
 
			if (v->cur_speed <= 0x100)
 
				break;
 
		} while (--j != 0);
 
	}
 

	
 
	SetLastSpeed(v, v->cur_speed);
 
}
 

	
 

	
 
void Train::Tick()
 
{
 
	if (_age_cargo_skip_counter == 0) this->cargo.AgeCargo();
 

	
 
	this->tick_counter++;
 

	
 
	if (IsFrontEngine(this)) {
 
		this->current_order_time++;
 

	
 
		TrainLocoHandler(this, false);
 

	
 
		/* make sure vehicle wasn't deleted. */
 
		if (this->type == VEH_TRAIN && IsFrontEngine(this))
 
			TrainLocoHandler(this, true);
 
	} else if (IsFreeWagon(this) && HASBITS(this->vehstatus, VS_CRASHED)) {
 
		/* Delete flooded standalone wagon */
 
		if (++this->u.rail.crash_anim_pos >= 4400)
 
			DeleteVehicle(this);
 
	}
 
}
 

	
 
#define MAX_ACCEPTABLE_DEPOT_DIST 16
 

	
 
static void CheckIfTrainNeedsService(Vehicle *v)
 
{
 
	if (_patches.servint_trains == 0)                   return;
 
	if (!VehicleNeedsService(v))                        return;
src/vehicle.cpp
Show inline comments
 
@@ -924,671 +924,671 @@ static void ChimneySmokeTick(Vehicle *v)
 
{
 
	if (v->progress > 0) {
 
		v->progress--;
 
	} else {
 
		TileIndex tile;
 

	
 
		BeginVehicleMove(v);
 

	
 
		tile = TileVirtXY(v->x_pos, v->y_pos);
 
		if (!IsTileType(tile, MP_INDUSTRY)) {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
			return;
 
		}
 

	
 
		if (v->cur_image != SPR_CHIMNEY_SMOKE_7) {
 
			v->cur_image++;
 
		} else {
 
			v->cur_image = SPR_CHIMNEY_SMOKE_0;
 
		}
 
		v->progress = 7;
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void SteamSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_STEAM_SMOKE_0;
 
	v->progress = 12;
 
}
 

	
 
static void SteamSmokeTick(Vehicle *v)
 
{
 
	bool moved = false;
 

	
 
	BeginVehicleMove(v);
 

	
 
	v->progress++;
 

	
 
	if ((v->progress & 7) == 0) {
 
		v->z_pos++;
 
		moved = true;
 
	}
 

	
 
	if ((v->progress & 0xF) == 4) {
 
		if (v->cur_image != SPR_STEAM_SMOKE_4) {
 
			v->cur_image++;
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
			return;
 
		}
 
		moved = true;
 
	}
 

	
 
	if (moved) {
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void DieselSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_DIESEL_SMOKE_0;
 
	v->progress = 0;
 
}
 

	
 
static void DieselSmokeTick(Vehicle *v)
 
{
 
	v->progress++;
 

	
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		v->z_pos++;
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	} else if ((v->progress & 7) == 1) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_DIESEL_SMOKE_5) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void ElectricSparkInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_ELECTRIC_SPARK_0;
 
	v->progress = 1;
 
}
 

	
 
static void ElectricSparkTick(Vehicle *v)
 
{
 
	if (v->progress < 2) {
 
		v->progress++;
 
	} else {
 
		v->progress = 0;
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_ELECTRIC_SPARK_5) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void SmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_SMOKE_0;
 
	v->progress = 12;
 
}
 

	
 
static void SmokeTick(Vehicle *v)
 
{
 
	bool moved = false;
 

	
 
	BeginVehicleMove(v);
 

	
 
	v->progress++;
 

	
 
	if ((v->progress & 3) == 0) {
 
		v->z_pos++;
 
		moved = true;
 
	}
 

	
 
	if ((v->progress & 0xF) == 4) {
 
		if (v->cur_image != SPR_SMOKE_4) {
 
			v->cur_image++;
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
			return;
 
		}
 
		moved = true;
 
	}
 

	
 
	if (moved) {
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void ExplosionLargeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_EXPLOSION_LARGE_0;
 
	v->progress = 0;
 
}
 

	
 
static void ExplosionLargeTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_EXPLOSION_LARGE_F) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void BreakdownSmokeInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BREAKDOWN_SMOKE_0;
 
	v->progress = 0;
 
}
 

	
 
static void BreakdownSmokeTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 7) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_BREAKDOWN_SMOKE_3) {
 
			v->cur_image++;
 
		} else {
 
			v->cur_image = SPR_BREAKDOWN_SMOKE_0;
 
		}
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 

	
 
	v->u.special.unk0--;
 
	if (v->u.special.unk0 == 0) {
 
	v->u.special.animation_state--;
 
	if (v->u.special.animation_state == 0) {
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 
		DeleteVehicle(v);
 
	}
 
}
 

	
 
static void ExplosionSmallInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_EXPLOSION_SMALL_0;
 
	v->progress = 0;
 
}
 

	
 
static void ExplosionSmallTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 3) == 0) {
 
		BeginVehicleMove(v);
 
		if (v->cur_image != SPR_EXPLOSION_SMALL_B) {
 
			v->cur_image++;
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
		} else {
 
			EndVehicleMove(v);
 
			DeleteVehicle(v);
 
		}
 
	}
 
}
 

	
 
static void BulldozerInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BULLDOZER_NE;
 
	v->progress = 0;
 
	v->u.special.unk0 = 0;
 
	v->u.special.unk2 = 0;
 
	v->u.special.animation_state = 0;
 
	v->u.special.animation_substate = 0;
 
}
 

	
 
struct BulldozerMovement {
 
	byte direction:2;
 
	byte image:2;
 
	byte duration:3;
 
};
 

	
 
static const BulldozerMovement _bulldozer_movement[] = {
 
	{ 0, 0, 4 },
 
	{ 3, 3, 4 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 1, 1, 3 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 1, 1, 3 },
 
	{ 2, 2, 7 },
 
	{ 0, 2, 7 },
 
	{ 3, 3, 6 },
 
	{ 2, 2, 6 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 },
 
	{ 0, 0, 3 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 },
 
	{ 0, 0, 3 },
 
	{ 1, 1, 7 },
 
	{ 3, 1, 7 }
 
};
 

	
 
static const struct {
 
	int8 x;
 
	int8 y;
 
} _inc_by_dir[] = {
 
	{ -1,  0 },
 
	{  0,  1 },
 
	{  1,  0 },
 
	{  0, -1 }
 
};
 

	
 
static void BulldozerTick(Vehicle *v)
 
{
 
	v->progress++;
 
	if ((v->progress & 7) == 0) {
 
		const BulldozerMovement* b = &_bulldozer_movement[v->u.special.unk0];
 
		const BulldozerMovement* b = &_bulldozer_movement[v->u.special.animation_state];
 

	
 
		BeginVehicleMove(v);
 

	
 
		v->cur_image = SPR_BULLDOZER_NE + b->image;
 

	
 
		v->x_pos += _inc_by_dir[b->direction].x;
 
		v->y_pos += _inc_by_dir[b->direction].y;
 

	
 
		v->u.special.unk2++;
 
		if (v->u.special.unk2 >= b->duration) {
 
			v->u.special.unk2 = 0;
 
			v->u.special.unk0++;
 
			if (v->u.special.unk0 == lengthof(_bulldozer_movement)) {
 
		v->u.special.animation_substate++;
 
		if (v->u.special.animation_substate >= b->duration) {
 
			v->u.special.animation_substate = 0;
 
			v->u.special.animation_state++;
 
			if (v->u.special.animation_state == lengthof(_bulldozer_movement)) {
 
				EndVehicleMove(v);
 
				DeleteVehicle(v);
 
				return;
 
			}
 
		}
 
		VehiclePositionChanged(v);
 
		EndVehicleMove(v);
 
	}
 
}
 

	
 
static void BubbleInit(Vehicle *v)
 
{
 
	v->cur_image = SPR_BUBBLE_GENERATE_0;
 
	v->spritenum = 0;
 
	v->progress = 0;
 
}
 

	
 
struct BubbleMovement {
 
	int8 x:4;
 
	int8 y:4;
 
	int8 z:4;
 
	byte image:4;
 
};
 

	
 
#define MK(x, y, z, i) { x, y, z, i }
 
#define ME(i) { i, 4, 0, 0 }
 

	
 
static const BubbleMovement _bubble_float_sw[] = {
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	ME(1)
 
};
 

	
 

	
 
static const BubbleMovement _bubble_float_ne[] = {
 
	MK( 0, 0, 1, 0),
 
	MK(-1, 0, 1, 1),
 
	MK( 0, 0, 1, 0),
 
	MK(-1, 0, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_float_se[] = {
 
	MK(0, 0, 1, 0),
 
	MK(0, 1, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 1, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_float_nw[] = {
 
	MK(0,  0, 1, 0),
 
	MK(0, -1, 1, 1),
 
	MK(0,  0, 1, 0),
 
	MK(0, -1, 1, 2),
 
	ME(1)
 
};
 

	
 
static const BubbleMovement _bubble_burst[] = {
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 7),
 
	MK(0, 0, 1, 8),
 
	MK(0, 0, 1, 9),
 
	ME(0)
 
};
 

	
 
static const BubbleMovement _bubble_absorb[] = {
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(0, 0, 1, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 2),
 
	MK(2, 1, 3, 0),
 
	MK(1, 1, 3, 1),
 
	MK(2, 1, 3, 0),
 
	MK(1, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 1),
 
	MK(0, 0, 1, 0),
 
	MK(1, 0, 1, 2),
 
	ME(2),
 
	MK(0, 0, 0, 0xA),
 
	MK(0, 0, 0, 0xB),
 
	MK(0, 0, 0, 0xC),
 
	MK(0, 0, 0, 0xD),
 
	MK(0, 0, 0, 0xE),
 
	ME(0)
 
};
 
#undef ME
 
#undef MK
 

	
 
static const BubbleMovement * const _bubble_movement[] = {
 
	_bubble_float_sw,
 
	_bubble_float_ne,
 
	_bubble_float_se,
 
	_bubble_float_nw,
 
	_bubble_burst,
 
	_bubble_absorb,
 
};
 

	
 
static void BubbleTick(Vehicle *v)
 
{
 
	/*
 
	 * Warning: those effects can NOT use Random(), and have to use
 
	 *  InteractiveRandom(), because somehow someone forgot to save
 
	 *  spritenum to the savegame, and so it will cause desyncs in
 
	 *  multiplayer!! (that is: in ToyLand)
 
	 */
 
	uint et;
 
	const BubbleMovement *b;
 

	
 
	v->progress++;
 
	if ((v->progress & 3) != 0)
 
		return;
 

	
 
	BeginVehicleMove(v);
 

	
 
	if (v->spritenum == 0) {
 
		v->cur_image++;
 
		if (v->cur_image < SPR_BUBBLE_GENERATE_3) {
 
			VehiclePositionChanged(v);
 
			EndVehicleMove(v);
 
			return;
 
		}
 
		if (v->u.special.unk2 != 0) {
 
		if (v->u.special.animation_substate != 0) {
 
			v->spritenum = GB(InteractiveRandom(), 0, 2) + 1;
 
		} else {
 
			v->spritenum = 6;
 
		}
 
		et = 0;
 
	} else {
 
		et = v->engine_type + 1;
 
	}
 

	
 
	b = &_bubble_movement[v->spritenum - 1][et];
 

	
 
	if (b->y == 4 && b->x == 0) {
 
		EndVehicleMove(v);
 
		DeleteVehicle(v);
 
		return;
 
	}
 

	
 
	if (b->y == 4 && b->x == 1) {
 
		if (v->z_pos > 180 || CHANCE16I(1, 96, InteractiveRandom())) {
 
			v->spritenum = 5;
 
			SndPlayVehicleFx(SND_2F_POP, v);
 
		}
 
		et = 0;
 
	}
 

	
 
	if (b->y == 4 && b->x == 2) {
 
		TileIndex tile;
 

	
 
		et++;
 
		SndPlayVehicleFx(SND_31_EXTRACT, v);
 

	
 
		tile = TileVirtXY(v->x_pos, v->y_pos);
 
		if (IsTileType(tile, MP_INDUSTRY) && GetIndustryGfx(tile) == 0xA2) AddAnimatedTile(tile);
 
	}
 

	
 
	v->engine_type = et;
 
	b = &_bubble_movement[v->spritenum - 1][et];
 

	
 
	v->x_pos += b->x;
 
	v->y_pos += b->y;
 
	v->z_pos += b->z;
 
	v->cur_image = SPR_BUBBLE_0 + b->image;
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
}
 

	
 

	
 
typedef void EffectInitProc(Vehicle *v);
 
typedef void EffectTickProc(Vehicle *v);
 

	
 
static EffectInitProc * const _effect_init_procs[] = {
 
	ChimneySmokeInit,
 
	SteamSmokeInit,
 
	DieselSmokeInit,
 
	ElectricSparkInit,
 
	SmokeInit,
 
	ExplosionLargeInit,
 
	BreakdownSmokeInit,
 
	ExplosionSmallInit,
 
	BulldozerInit,
 
	BubbleInit,
 
};
 

	
 
static EffectTickProc * const _effect_tick_procs[] = {
 
	ChimneySmokeTick,
 
	SteamSmokeTick,
 
	DieselSmokeTick,
 
	ElectricSparkTick,
 
	SmokeTick,
 
	ExplosionLargeTick,
 
	BreakdownSmokeTick,
 
	ExplosionSmallTick,
 
	BulldozerTick,
 
	BubbleTick,
 
};
 

	
 

	
 
Vehicle *CreateEffectVehicle(int x, int y, int z, EffectVehicle type)
 
{
 
	Vehicle *v;
 

	
 
	v = ForceAllocateSpecialVehicle();
 
	if (v != NULL) {
 
		v = new (v) SpecialVehicle();
 
		v->subtype = type;
 
		v->x_pos = x;
 
		v->y_pos = y;
 
		v->z_pos = z;
 
		v->tile = 0;
 
		v->UpdateDeltaXY(INVALID_DIR);
 
		v->vehstatus = VS_UNCLICKABLE;
 

	
 
		_effect_init_procs[type](v);
 

	
 
		VehiclePositionChanged(v);
 
		BeginVehicleMove(v);
 
		EndVehicleMove(v);
 
	}
 
	return v;
 
}
 

	
 
Vehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicle type)
 
{
 
	int safe_x = clamp(x, 0, MapMaxX() * TILE_SIZE);
 
	int safe_y = clamp(y, 0, MapMaxY() * TILE_SIZE);
 
	return CreateEffectVehicle(x, y, GetSlopeZ(safe_x, safe_y) + z, type);
 
}
 

	
 
Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVehicle type)
 
{
 
	return CreateEffectVehicle(v->x_pos + x, v->y_pos + y, v->z_pos + z, type);
 
}
 

	
 
void SpecialVehicle::Tick()
 
{
 
	_effect_tick_procs[this->subtype](this);
 
}
 

	
 
Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y)
 
{
 
	Vehicle *found = NULL, *v;
 
	uint dist, best_dist = (uint)-1;
 

	
 
	if ( (uint)(x -= vp->left) >= (uint)vp->width ||
 
			 (uint)(y -= vp->top) >= (uint)vp->height)
 
				return NULL;
 

	
 
	x = ScaleByZoom(x, vp->zoom) + vp->virtual_left;
 
	y = ScaleByZoom(y, vp->zoom) + vp->virtual_top;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		if ((v->vehstatus & (VS_HIDDEN|VS_UNCLICKABLE)) == 0 &&
 
				x >= v->left_coord && x <= v->right_coord &&
 
				y >= v->top_coord && y <= v->bottom_coord) {
 

	
 
			dist = max(
 
				myabs( ((v->left_coord + v->right_coord)>>1) - x ),
 
				myabs( ((v->top_coord + v->bottom_coord)>>1) - y )
 
			);
 

	
 
			if (dist < best_dist) {
 
				found = v;
 
				best_dist = dist;
 
			}
 
		}
 
	}
 

	
 
	return found;
 
}
 

	
 

	
 
void DecreaseVehicleValue(Vehicle *v)
 
{
 
	v->value -= v->value >> 8;
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
}
 

	
 
static const byte _breakdown_chance[64] = {
 
	  3,   3,   3,   3,   3,   3,   3,   3,
 
	  4,   4,   5,   5,   6,   6,   7,   7,
 
	  8,   8,   9,   9,  10,  10,  11,  11,
 
	 12,  13,  13,  13,  13,  14,  15,  16,
 
	 17,  19,  21,  25,  28,  31,  34,  37,
 
	 40,  44,  48,  52,  56,  60,  64,  68,
 
	 72,  80,  90, 100, 110, 120, 130, 140,
 
	150, 170, 190, 210, 230, 250, 250, 250,
 
};
 

	
 
void CheckVehicleBreakdown(Vehicle *v)
 
{
 
	int rel, rel_old;
 
	uint32 r;
 
	int chance;
 

	
 
	/* decrease reliability */
 
	v->reliability = rel = max((rel_old = v->reliability) - v->reliability_spd_dec, 0);
 
	if ((rel_old >> 8) != (rel >> 8))
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 

	
 
	if (v->breakdown_ctr != 0 || v->vehstatus & VS_STOPPED ||
 
			v->cur_speed < 5 || _game_mode == GM_MENU) {
 
		return;
 
	}
 

	
 
	r = Random();
 

	
 
	/* increase chance of failure */
 
	chance = v->breakdown_chance + 1;
 
	if (CHANCE16I(1,25,r)) chance += 25;
 
	v->breakdown_chance = min(255, chance);
 

	
 
@@ -2853,424 +2853,424 @@ extern const SaveLoad _common_veh_desc[]
 
	SLE_CONDVAR(Vehicle, y_pos,                SLE_UINT32,                  6, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, z_pos,                SLE_UINT8),
 
	    SLE_VAR(Vehicle, direction,            SLE_UINT8),
 

	
 
	SLE_CONDNULL(2,                                                         0, 57),
 
	    SLE_VAR(Vehicle, spritenum,            SLE_UINT8),
 
	SLE_CONDNULL(5,                                                         0, 57),
 
	    SLE_VAR(Vehicle, engine_type,          SLE_UINT16),
 

	
 
	    SLE_VAR(Vehicle, max_speed,            SLE_UINT16),
 
	    SLE_VAR(Vehicle, cur_speed,            SLE_UINT16),
 
	    SLE_VAR(Vehicle, subspeed,             SLE_UINT8),
 
	    SLE_VAR(Vehicle, acceleration,         SLE_UINT8),
 
	    SLE_VAR(Vehicle, progress,             SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, vehstatus,            SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8  | SLE_VAR_U16,  0, 4),
 
	SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16,                  5, SL_MAX_VERSION),
 

	
 
	     SLE_VAR(Vehicle, cargo_type,           SLE_UINT8),
 
	 SLE_CONDVAR(Vehicle, cargo_subtype,        SLE_UINT8,                  35, SL_MAX_VERSION),
 
	SLEG_CONDVAR(         _cargo_days,          SLE_UINT8,                   0, 67),
 
	SLEG_CONDVAR(         _cargo_source,        SLE_FILE_U8  | SLE_VAR_U16,  0, 6),
 
	SLEG_CONDVAR(         _cargo_source,        SLE_UINT16,                  7, 67),
 
	SLEG_CONDVAR(         _cargo_source_xy,     SLE_UINT32,                 44, 67),
 
	     SLE_VAR(Vehicle, cargo_cap,            SLE_UINT16),
 
	SLEG_CONDVAR(         _cargo_count,         SLE_UINT16,                  0, 67),
 
	 SLE_CONDLST(Vehicle, cargo,                REF_CARGO_PACKET,           68, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Vehicle, day_counter,          SLE_UINT8),
 
	    SLE_VAR(Vehicle, tick_counter,         SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, cur_order_index,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, num_orders,           SLE_UINT8),
 

	
 
	/* This next line is for version 4 and prior compatibility.. it temporarily reads
 
	    type and flags (which were both 4 bits) into type. Later on this is
 
	    converted correctly */
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type), SLE_UINT8,                 0, 4),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 

	
 
	/* Orders for version 5 and on */
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, type),  SLE_UINT8,  5, SL_MAX_VERSION),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, flags), SLE_UINT8,  5, SL_MAX_VERSION),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest),  SLE_UINT16, 5, SL_MAX_VERSION),
 

	
 
	/* Refit in current order */
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_cargo),    SLE_UINT8, 36, SL_MAX_VERSION),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, refit_subtype),  SLE_UINT8, 36, SL_MAX_VERSION),
 

	
 
	/* Timetable in current order */
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, wait_time),      SLE_UINT16, 67, SL_MAX_VERSION),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, travel_time),    SLE_UINT16, 67, SL_MAX_VERSION),
 

	
 
	    SLE_REF(Vehicle, orders,               REF_ORDER),
 

	
 
	SLE_CONDVAR(Vehicle, age,                  SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, age,                  SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, max_age,              SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, max_age,              SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, date_of_last_service, SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, date_of_last_service, SLE_INT32,                  31, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, service_interval,     SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, service_interval,     SLE_INT32,                  31, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, reliability,          SLE_UINT16),
 
	    SLE_VAR(Vehicle, reliability_spd_dec,  SLE_UINT16),
 
	    SLE_VAR(Vehicle, breakdown_ctr,        SLE_UINT8),
 
	    SLE_VAR(Vehicle, breakdown_delay,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8),
 
	    SLE_VAR(Vehicle, breakdown_chance,     SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, build_year,           SLE_FILE_U8 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, build_year,           SLE_INT32,                 31, SL_MAX_VERSION),
 

	
 
	     SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16),
 
	SLEG_CONDVAR(         _cargo_paid_for,      SLE_UINT16,                45, SL_MAX_VERSION),
 
	 SLE_CONDVAR(Vehicle, vehicle_flags,        SLE_UINT8,                 40, SL_MAX_VERSION),
 

	
 
	 SLE_CONDVAR(Vehicle, profit_this_year,     SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
 
	 SLE_CONDVAR(Vehicle, profit_this_year,     SLE_INT64,                 65, SL_MAX_VERSION),
 
	 SLE_CONDVAR(Vehicle, profit_last_year,     SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
 
	 SLE_CONDVAR(Vehicle, profit_last_year,     SLE_INT64,                 65, SL_MAX_VERSION),
 
	SLEG_CONDVAR(         _cargo_feeder_share,  SLE_FILE_I32 | SLE_VAR_I64,51, 64),
 
	SLEG_CONDVAR(         _cargo_feeder_share,  SLE_INT64,                 65, 67),
 
	SLEG_CONDVAR(         _cargo_loaded_at_xy,  SLE_UINT32,                51, 67),
 
	 SLE_CONDVAR(Vehicle, value,                SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
 
	 SLE_CONDVAR(Vehicle, value,                SLE_INT64,                 65, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Vehicle, random_bits,          SLE_UINT8),
 
	    SLE_VAR(Vehicle, waiting_triggers,     SLE_UINT8),
 

	
 
	    SLE_REF(Vehicle, next_shared,          REF_VEHICLE),
 
	    SLE_REF(Vehicle, prev_shared,          REF_VEHICLE),
 

	
 
	SLE_CONDVAR(Vehicle, group_id,             SLE_UINT16,                60, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Vehicle, current_order_time,   SLE_UINT32,                67, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, lateness_counter,     SLE_INT32,                 67, SL_MAX_VERSION),
 

	
 
	/* reserve extra space in savegame here. (currently 10 bytes) */
 
	SLE_CONDNULL(10,                                                       2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 

	
 
static const SaveLoad _train_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_TRAIN),
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, crash_anim_pos),         SLE_UINT16),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, force_proceed),          SLE_UINT8),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, railtype),               SLE_UINT8),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, track),                  SLE_UINT8),
 

	
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags),                  SLE_UINT8,  2, SL_MAX_VERSION),
 
	SLE_CONDNULL(2, 2, 59),
 

	
 
	SLE_CONDNULL(2, 2, 19),
 
	/* reserve extra space in savegame here. (currently 11 bytes) */
 
	SLE_CONDNULL(11, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _roadveh_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_ROAD),
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, state),          SLE_UINT8),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, frame),          SLE_UINT8),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, blocked_ctr),    SLE_UINT16),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking),     SLE_UINT8),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, overtaking_ctr), SLE_UINT8),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, crashed_ctr),    SLE_UINT16),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, reverse_ctr),    SLE_UINT8),
 

	
 
	SLE_CONDREFX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot),     REF_ROADSTOPS, 6, SL_MAX_VERSION),
 
	SLE_CONDNULL(1,                                                                     6, SL_MAX_VERSION),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRoad, slot_age), SLE_UINT8,     6, SL_MAX_VERSION),
 
	/* reserve extra space in savegame here. (currently 16 bytes) */
 
	SLE_CONDNULL(16,                                                                    2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _ship_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_SHIP),
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleShip, state), SLE_UINT8),
 

	
 
	/* reserve extra space in savegame here. (currently 16 bytes) */
 
	SLE_CONDNULL(16, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _aircraft_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_AIRCRAFT),
 
	SLE_INCLUDEX(0, INC_VEHICLE_COMMON),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, crashed_counter), SLE_UINT16),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, pos),             SLE_UINT8),
 

	
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport),   SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, targetairport),   SLE_UINT16,                5, SL_MAX_VERSION),
 

	
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, state),           SLE_UINT8),
 

	
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleAir, previous_pos),    SLE_UINT8,                 2, SL_MAX_VERSION),
 

	
 
	/* reserve extra space in savegame here. (currently 15 bytes) */
 
	SLE_CONDNULL(15,                                                                                      2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _special_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_SPECIAL),
 

	
 
	    SLE_VAR(Vehicle, subtype,       SLE_UINT8),
 

	
 
	SLE_CONDVAR(Vehicle, tile,          SLE_FILE_U16 | SLE_VAR_U32, 0, 5),
 
	SLE_CONDVAR(Vehicle, tile,          SLE_UINT32,                 6, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_INT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_FILE_I16 | SLE_VAR_I32, 0, 5),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_INT32,                  6, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, z_pos,         SLE_UINT8),
 

	
 
	    SLE_VAR(Vehicle, cur_image,     SLE_UINT16),
 
	SLE_CONDNULL(5,                                                 0, 57),
 
	    SLE_VAR(Vehicle, progress,      SLE_UINT8),
 
	    SLE_VAR(Vehicle, vehstatus,     SLE_UINT8),
 

	
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleSpecial, unk0), SLE_UINT16),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleSpecial, unk2), SLE_UINT8),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleSpecial, animation_state),    SLE_UINT16),
 
	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleSpecial, animation_substate), SLE_UINT8),
 

	
 
	/* reserve extra space in savegame here. (currently 16 bytes) */
 
	SLE_CONDNULL(16, 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 
static const SaveLoad _disaster_desc[] = {
 
	SLE_WRITEBYTE(Vehicle, type, VEH_DISASTER),
 

	
 
	    SLE_REF(Vehicle, next,          REF_VEHICLE_OLD),
 

	
 
	    SLE_VAR(Vehicle, subtype,       SLE_UINT8),
 
	SLE_CONDVAR(Vehicle, tile,          SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, tile,          SLE_UINT32,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, dest_tile,     SLE_FILE_U16 | SLE_VAR_U32,  0, 5),
 
	SLE_CONDVAR(Vehicle, dest_tile,     SLE_UINT32,                  6, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_FILE_I16 | SLE_VAR_I32,  0, 5),
 
	SLE_CONDVAR(Vehicle, x_pos,         SLE_INT32,                   6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_FILE_I16 | SLE_VAR_I32,  0, 5),
 
	SLE_CONDVAR(Vehicle, y_pos,         SLE_INT32,                   6, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, z_pos,         SLE_UINT8),
 
	    SLE_VAR(Vehicle, direction,     SLE_UINT8),
 

	
 
	SLE_CONDNULL(5,                                                  0, 57),
 
	    SLE_VAR(Vehicle, owner,         SLE_UINT8),
 
	    SLE_VAR(Vehicle, vehstatus,     SLE_UINT8),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
 
	SLE_CONDVARX(cpp_offsetof(Vehicle, current_order) + cpp_offsetof(Order, dest), SLE_UINT16,                5, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Vehicle, cur_image,     SLE_UINT16),
 
	SLE_CONDVAR(Vehicle, age,           SLE_FILE_U16 | SLE_VAR_I32,  0, 30),
 
	SLE_CONDVAR(Vehicle, age,           SLE_INT32,                  31, SL_MAX_VERSION),
 
	    SLE_VAR(Vehicle, tick_counter,  SLE_UINT8),
 

	
 
	   SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, image_override), SLE_UINT16),
 
	   SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, unk2),           SLE_UINT16),
 
	   SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, image_override),           SLE_UINT16),
 
	   SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleDisaster, big_ufo_destroyer_target), SLE_UINT16),
 

	
 
	/* reserve extra space in savegame here. (currently 16 bytes) */
 
	SLE_CONDNULL(16,                                                 2, SL_MAX_VERSION),
 

	
 
	SLE_END()
 
};
 

	
 

	
 
static const void *_veh_descs[] = {
 
	_train_desc,
 
	_roadveh_desc,
 
	_ship_desc,
 
	_aircraft_desc,
 
	_special_desc,
 
	_disaster_desc,
 
};
 

	
 
/** Will be called when the vehicles need to be saved. */
 
static void Save_VEHS()
 
{
 
	Vehicle *v;
 
	/* Write the vehicles */
 
	FOR_ALL_VEHICLES(v) {
 
		SlSetArrayIndex(v->index);
 
		SlObject(v, (SaveLoad*)_veh_descs[v->type]);
 
	}
 
}
 

	
 
/** Will be called when vehicles need to be loaded. */
 
static void Load_VEHS()
 
{
 
	int index;
 
	Vehicle *v;
 

	
 
	_cargo_count = 0;
 

	
 
	while ((index = SlIterateArray()) != -1) {
 
		Vehicle *v;
 

	
 
		if (!AddBlockIfNeeded(&_Vehicle_pool, index))
 
			error("Vehicles: failed loading savegame: too many vehicles");
 

	
 
		v = GetVehicle(index);
 
		VehicleType vtype = (VehicleType)SlReadByte();
 

	
 
		switch (vtype) {
 
			case VEH_TRAIN:    v = new (v) Train();           break;
 
			case VEH_ROAD:     v = new (v) RoadVehicle();     break;
 
			case VEH_SHIP:     v = new (v) Ship();            break;
 
			case VEH_AIRCRAFT: v = new (v) Aircraft();        break;
 
			case VEH_SPECIAL:  v = new (v) SpecialVehicle();  break;
 
			case VEH_DISASTER: v = new (v) DisasterVehicle(); break;
 
			case VEH_INVALID:  v = new (v) InvalidVehicle();  break;
 
			default: NOT_REACHED();
 
		}
 

	
 
		SlObject(v, (SaveLoad*)_veh_descs[vtype]);
 

	
 
		if (_cargo_count != 0 && IsPlayerBuildableVehicleType(v)) {
 
			/* Don't construct the packet with station here, because that'll fail with old savegames */
 
			CargoPacket *cp = new CargoPacket();
 
			cp->source          = _cargo_source;
 
			cp->source_xy       = _cargo_source_xy;
 
			cp->count           = _cargo_count;
 
			cp->days_in_transit = _cargo_days;
 
			cp->feeder_share    = _cargo_feeder_share;
 
			cp->loaded_at_xy    = _cargo_loaded_at_xy;
 
			v->cargo.Append(cp);
 
		}
 

	
 
		/* Old savegames used 'last_station_visited = 0xFF' */
 
		if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF)
 
			v->last_station_visited = INVALID_STATION;
 

	
 
		if (CheckSavegameVersion(5)) {
 
			/* Convert the current_order.type (which is a mix of type and flags, because
 
			 *  in those versions, they both were 4 bits big) to type and flags */
 
			v->current_order.flags = (v->current_order.type & 0xF0) >> 4;
 
			v->current_order.type.m_val &= 0x0F;
 
		}
 

	
 
		/* Advanced vehicle lists got added */
 
		if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP;
 
	}
 

	
 
	/* Check for shared order-lists (we now use pointers for that) */
 
	if (CheckSavegameVersionOldStyle(5, 2)) {
 
		FOR_ALL_VEHICLES(v) {
 
			Vehicle *u;
 

	
 
			FOR_ALL_VEHICLES_FROM(u, v->index + 1) {
 
				/* If a vehicle has the same orders, add the link to eachother
 
				 *  in both vehicles */
 
				if (v->orders == u->orders) {
 
					v->next_shared = u;
 
					u->prev_shared = v;
 
					break;
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
extern const ChunkHandler _veh_chunk_handlers[] = {
 
	{ 'VEHS', Save_VEHS, Load_VEHS, CH_SPARSE_ARRAY | CH_LAST},
 
};
 

	
 
void Vehicle::BeginLoading()
 
{
 
	assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
 

	
 
	if (this->current_order.type == OT_GOTO_STATION &&
 
			this->current_order.dest == this->last_station_visited) {
 
		/* Arriving at the ordered station.
 
		 * Keep the load/unload flags, as we (obviously) still need them. */
 
		this->current_order.flags &= OF_FULL_LOAD | OF_UNLOAD | OF_TRANSFER;
 

	
 
		/* Furthermore add the Non Stop flag to mark that this station
 
		 * is the actual destination of the vehicle, which is (for example)
 
		 * necessary to be known for HandleTrainLoading to determine
 
		 * whether the train is lost or not; not marking a train lost
 
		 * that arrives at random stations is bad. */
 
		this->current_order.flags |= OF_NON_STOP;
 
		UpdateVehicleTimetable(this, true);
 
	} else {
 
		/* This is just an unordered intermediate stop */
 
		this->current_order.flags = 0;
 
	}
 

	
 
	current_order.type = OT_LOADING;
 
	GetStation(this->last_station_visited)->loading_vehicles.push_back(this);
 

	
 
	SET_EXPENSES_TYPE(this->GetExpenseType(true));
 
	VehiclePayment(this);
 

	
 
	InvalidateWindow(this->GetVehicleListWindowClass(), this->owner);
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, this->index, STATUS_BAR);
 
	InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
 
	InvalidateWindow(WC_STATION_VIEW, this->last_station_visited);
 

	
 
	GetStation(this->last_station_visited)->MarkTilesDirty(true);
 
	this->MarkDirty();
 
}
 

	
 
void Vehicle::LeaveStation()
 
{
 
	assert(current_order.type == OT_LOADING);
 

	
 
	/* Only update the timetable if the vehicle was supposed to stop here. */
 
	if (current_order.flags & OF_NON_STOP) UpdateVehicleTimetable(this, false);
 

	
 
	current_order.type = OT_LEAVESTATION;
 
	current_order.flags = 0;
 
	GetStation(this->last_station_visited)->loading_vehicles.remove(this);
 

	
 
	HideFillingPercent(this->fill_percent_te_id);
 
	this->fill_percent_te_id = INVALID_TE_ID;
 
}
 

	
 

	
 
void Vehicle::HandleLoading(bool mode)
 
{
 
	switch (this->current_order.type) {
 
		case OT_LOADING: {
 
			uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
 

	
 
			/* Not the first call for this tick, or still loading */
 
			if (mode || !HASBIT(this->vehicle_flags, VF_LOADING_FINISHED) ||
 
					(_patches.timetabling && this->current_order_time < wait_time)) return;
 

	
 
			this->PlayLeaveStationSound();
 

	
 
			Order b = this->current_order;
 
			this->LeaveStation();
 

	
 
			/* If this was not the final order, don't remove it from the list. */
 
			if (!(b.flags & OF_NON_STOP)) return;
 
			break;
 
		}
 

	
 
		case OT_DUMMY: break;
 

	
 
		default: return;
 
	}
 

	
 
	this->cur_order_index++;
 
	InvalidateVehicleOrder(this);
 
}
 

	
 

	
 
void SpecialVehicle::UpdateDeltaXY(Direction direction)
 
{
src/vehicle.h
Show inline comments
 
@@ -13,391 +13,391 @@
 
#include "texteff.hpp"
 

	
 
/** The returned bits of VehicleEnterTile. */
 
enum VehicleEnterTileStatus {
 
	VETS_ENTERED_STATION  = 1, ///< The vehicle entered a station
 
	VETS_ENTERED_WORMHOLE = 2, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel)
 
	VETS_CANNOT_ENTER     = 3, ///< The vehicle cannot enter the tile
 

	
 
	/**
 
	 * Shift the VehicleEnterTileStatus this many bits
 
	 * to the right to get the station ID when
 
	 * VETS_ENTERED_STATION is set
 
	 */
 
	VETS_STATION_ID_OFFSET = 8,
 

	
 
	/** Bit sets of the above specified bits */
 
	VETSB_CONTINUE         = 0,                          ///< The vehicle can continue normally
 
	VETSB_ENTERED_STATION  = 1 << VETS_ENTERED_STATION,  ///< The vehicle entered a station
 
	VETSB_ENTERED_WORMHOLE = 1 << VETS_ENTERED_WORMHOLE, ///< The vehicle either entered a bridge, tunnel or depot tile (this includes the last tile of the bridge/tunnel)
 
	VETSB_CANNOT_ENTER     = 1 << VETS_CANNOT_ENTER,     ///< The vehicle cannot enter the tile
 
};
 

	
 
/** Road vehicle states */
 
enum RoadVehicleStates {
 
	/*
 
	 * Lower 4 bits are used for vehicle track direction. (Trackdirs)
 
	 * When in a road stop (bit 5 or bit 6 set) these bits give the
 
	 * track direction of the entry to the road stop.
 
	 * As the entry direction will always be a diagonal
 
	 * direction (X_NE, Y_SE, X_SW or Y_NW) only bits 0 and 3
 
	 * are needed to hold this direction. Bit 1 is then used to show
 
	 * that the vehicle is using the second road stop bay.
 
	 * Bit 2 is then used for drive-through stops to show the vehicle
 
	 * is stopping at this road stop.
 
	 */
 

	
 
	/* Numeric values */
 
	RVSB_IN_DEPOT                = 0xFE,                      ///< The vehicle is in a depot
 
	RVSB_WORMHOLE                = 0xFF,                      ///< The vehicle is in a tunnel and/or bridge
 

	
 
	/* Bit numbers */
 
	RVS_USING_SECOND_BAY         =    1,                      ///< Only used while in a road stop
 
	RVS_IS_STOPPING              =    2,                      ///< Only used for drive-through stops. Vehicle will stop here
 
	RVS_DRIVE_SIDE               =    4,                      ///< Only used when retrieving move data
 
	RVS_IN_ROAD_STOP             =    5,                      ///< The vehicle is in a road stop
 
	RVS_IN_DT_ROAD_STOP          =    6,                      ///< The vehicle is in a drive-through road stop
 

	
 
	/* Bit sets of the above specified bits */
 
	RVSB_IN_ROAD_STOP            = 1 << RVS_IN_ROAD_STOP,     ///< The vehicle is in a road stop
 
	RVSB_IN_ROAD_STOP_END        = RVSB_IN_ROAD_STOP + TRACKDIR_END,
 
	RVSB_IN_DT_ROAD_STOP         = 1 << RVS_IN_DT_ROAD_STOP,  ///< The vehicle is in a drive-through road stop
 
	RVSB_IN_DT_ROAD_STOP_END     = RVSB_IN_DT_ROAD_STOP + TRACKDIR_END,
 

	
 
	RVSB_TRACKDIR_MASK           = 0x0F,                      ///< The mask used to extract track dirs
 
	RVSB_ROAD_STOP_TRACKDIR_MASK = 0x09                       ///< Only bits 0 and 3 are used to encode the trackdir for road stops
 
};
 

	
 
enum VehicleType {
 
	VEH_TRAIN,
 
	VEH_ROAD,
 
	VEH_SHIP,
 
	VEH_AIRCRAFT,
 
	VEH_SPECIAL,
 
	VEH_DISASTER,
 
	VEH_END,
 
	VEH_INVALID = 0xFF,
 
};
 
DECLARE_POSTFIX_INCREMENT(VehicleType);
 
template <> struct EnumPropsT<VehicleType> : MakeEnumPropsT<VehicleType, byte, VEH_TRAIN, VEH_END, VEH_INVALID> {};
 
typedef TinyEnumT<VehicleType> VehicleTypeByte;
 

	
 
enum VehStatus {
 
	VS_HIDDEN          = 0x01,
 
	VS_STOPPED         = 0x02,
 
	VS_UNCLICKABLE     = 0x04,
 
	VS_DEFPAL          = 0x08,
 
	VS_TRAIN_SLOWING   = 0x10,
 
	VS_SHADOW          = 0x20,
 
	VS_AIRCRAFT_BROKEN = 0x40,
 
	VS_CRASHED         = 0x80,
 
};
 

	
 
enum VehicleFlags {
 
	VF_LOADING_FINISHED,
 
	VF_CARGO_UNLOADING,
 
	VF_BUILT_AS_PROTOTYPE,
 
	VF_TIMETABLE_STARTED,  ///< Whether the vehicle has started running on the timetable yet.
 
	VF_AUTOFILL_TIMETABLE, ///< Whether the vehicle should fill in the timetable automatically.
 
};
 

	
 
/* Effect vehicle types */
 
enum EffectVehicle {
 
	EV_CHIMNEY_SMOKE   = 0,
 
	EV_STEAM_SMOKE     = 1,
 
	EV_DIESEL_SMOKE    = 2,
 
	EV_ELECTRIC_SPARK  = 3,
 
	EV_SMOKE           = 4,
 
	EV_EXPLOSION_LARGE = 5,
 
	EV_BREAKDOWN_SMOKE = 6,
 
	EV_EXPLOSION_SMALL = 7,
 
	EV_BULLDOZER       = 8,
 
	EV_BUBBLE          = 9
 
};
 

	
 
struct VehicleRail {
 
	uint16 last_speed; // NOSAVE: only used in UI
 
	uint16 crash_anim_pos;
 

	
 
	/* cached values, recalculated on load and each time a vehicle is added to/removed from the consist. */
 
	uint16 cached_max_speed;  // max speed of the consist. (minimum of the max speed of all vehicles in the consist)
 
	uint32 cached_power;      // total power of the consist.
 
	uint8 cached_veh_length;  // length of this vehicle in units of 1/8 of normal length, cached because this can be set by a callback
 
	uint16 cached_total_length; ///< Length of the whole train, valid only for first engine.
 

	
 
	/* cached values, recalculated when the cargo on a train changes (in addition to the conditions above) */
 
	uint32 cached_weight;     // total weight of the consist.
 
	uint32 cached_veh_weight; // weight of the vehicle.
 
	uint32 cached_max_te;     // max tractive effort of consist
 
	/**
 
	 * Position/type of visual effect.
 
	 * bit 0 - 3 = position of effect relative to vehicle. (0 = front, 8 = centre, 15 = rear)
 
	 * bit 4 - 5 = type of effect. (0 = default for engine class, 1 = steam, 2 = diesel, 3 = electric)
 
	 * bit     6 = disable visual effect.
 
	 * bit     7 = disable powered wagons.
 
	 */
 
	byte cached_vis_effect;
 

	
 
	/* NOSAVE: for wagon override - id of the first engine in train
 
	 * 0xffff == not in train */
 
	EngineID first_engine;
 

	
 
	TrackBitsByte track;
 
	byte force_proceed;
 
	RailTypeByte railtype;
 
	RailTypeMask compatible_railtypes;
 

	
 
	byte flags;
 

	
 
	/* Link between the two ends of a multiheaded engine */
 
	Vehicle *other_multiheaded_part;
 

	
 
	/* Cached wagon override spritegroup */
 
	const struct SpriteGroup *cached_override;
 
};
 

	
 
enum {
 
	VRF_REVERSING         = 0,
 

	
 
	/* used to calculate if train is going up or down */
 
	VRF_GOINGUP           = 1,
 
	VRF_GOINGDOWN         = 2,
 

	
 
	/* used to store if a wagon is powered or not */
 
	VRF_POWEREDWAGON      = 3,
 

	
 
	/* used to reverse the visible direction of the vehicle */
 
	VRF_REVERSE_DIRECTION = 4,
 

	
 
	/* used to mark train as lost because PF can't find the route */
 
	VRF_NO_PATH_TO_DESTINATION = 5,
 

	
 
	/* used to mark that electric train engine is allowed to run on normal rail */
 
	VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL = 6,
 
};
 

	
 
struct VehicleAir {
 
	uint16 crashed_counter;
 
	uint16 cached_max_speed;
 
	byte pos;
 
	byte previous_pos;
 
	StationID targetairport;
 
	byte state;
 
};
 

	
 
struct VehicleRoad {
 
	byte state;             ///< @see RoadVehicleStates
 
	byte frame;
 
	uint16 blocked_ctr;
 
	byte overtaking;
 
	byte overtaking_ctr;
 
	uint16 crashed_ctr;
 
	byte reverse_ctr;
 
	struct RoadStop *slot;
 
	byte slot_age;
 
	EngineID first_engine;
 
	byte cached_veh_length;
 

	
 
	RoadType roadtype;
 
	RoadTypes compatible_roadtypes;
 
};
 

	
 
struct VehicleSpecial {
 
	uint16 unk0;
 
	byte unk2;
 
	uint16 animation_state;
 
	byte animation_substate;
 
};
 

	
 
struct VehicleDisaster {
 
	uint16 image_override;
 
	uint16 unk2;
 
	VehicleID big_ufo_destroyer_target;
 
};
 

	
 
struct VehicleShip {
 
	TrackBitsByte state;
 
};
 

	
 

	
 
struct Vehicle {
 
	VehicleTypeByte type;    ///< Type of vehicle
 
	byte subtype;            // subtype (Filled with values from EffectVehicles/TrainSubTypes/AircraftSubTypes)
 

	
 
	VehicleID index;         // NOSAVE: Index in vehicle array
 

	
 
	Vehicle *next;           // next
 
	Vehicle *first;          // NOSAVE: pointer to the first vehicle in the chain
 
	Vehicle *depot_list;     //NOSAVE: linked list to tell what vehicles entered a depot during the last tick. Used by autoreplace
 

	
 
	StringID string_id;      // Displayed string
 

	
 
	UnitID unitnumber;       // unit number, for display purposes only
 
	PlayerByte owner;        // which player owns the vehicle?
 

	
 
	TileIndex tile;          // Current tile index
 
	TileIndex dest_tile;     // Heading for this tile
 

	
 
	int32 x_pos;             // coordinates
 
	int32 y_pos;
 
	byte z_pos;
 
	DirectionByte direction; // facing
 

	
 
	byte spritenum;          // currently displayed sprite index
 
	                         // 0xfd == custom sprite, 0xfe == custom second head sprite
 
	                         // 0xff == reserved for another custom sprite
 
	uint16 cur_image;        // sprite number for this vehicle
 
	byte sprite_width;       // width of vehicle sprite
 
	byte sprite_height;      // height of vehicle sprite
 
	byte z_height;           // z-height of vehicle sprite
 
	int8 x_offs;             // x offset for vehicle sprite
 
	int8 y_offs;             // y offset for vehicle sprite
 
	EngineID engine_type;
 

	
 
	TextEffectID fill_percent_te_id; // a text-effect id to a loading indicator object
 

	
 
	/* for randomized variational spritegroups
 
	 * bitmask used to resolve them; parts of it get reseeded when triggers
 
	 * of corresponding spritegroups get matched */
 
	byte random_bits;
 
	byte waiting_triggers;   // triggers to be yet matched
 

	
 
	uint16 max_speed;        // maximum speed
 
	uint16 cur_speed;        // current speed
 
	byte subspeed;           // fractional speed
 
	byte acceleration;       // used by train & aircraft
 
	byte progress;
 
	uint32 motion_counter;
 

	
 
	byte vehstatus;          // Status
 
	StationID last_station_visited;
 

	
 
	CargoID cargo_type;      // type of cargo this vehicle is carrying
 
	uint16 cargo_cap;        // total capacity
 
	byte cargo_subtype;      ///< Used for livery refits (NewGRF variations)
 
	CargoList cargo;         ///< The cargo this vehicle is carrying
 

	
 

	
 
	byte day_counter;        // increased by one for each day
 
	byte tick_counter;       // increased by one for each tick
 

	
 
	/* Begin Order-stuff */
 
	Order current_order;     ///< The current order (+ status, like: loading)
 
	VehicleOrderID cur_order_index; ///< The index to the current order
 

	
 
	Order *orders;           ///< Pointer to the first order for this vehicle
 
	VehicleOrderID num_orders;      ///< How many orders there are in the list
 

	
 
	Vehicle *next_shared;    ///< If not NULL, this points to the next vehicle that shared the order
 
	Vehicle *prev_shared;    ///< If not NULL, this points to the prev vehicle that shared the order
 
	/* End Order-stuff */
 

	
 
	/* Boundaries for the current position in the world and a next hash link.
 
	 * NOSAVE: All of those can be updated with VehiclePositionChanged() */
 
	int32 left_coord;
 
	int32 top_coord;
 
	int32 right_coord;
 
	int32 bottom_coord;
 
	Vehicle *next_hash;
 
	Vehicle *next_new_hash;
 
	Vehicle **old_new_hash;
 

	
 
	/* Related to age and service time */
 
	Date age;     // Age in days
 
	Date max_age; // Maximum age
 
	Date date_of_last_service;
 
	Date service_interval;
 
	uint16 reliability;
 
	uint16 reliability_spd_dec;
 
	byte breakdown_ctr;
 
	byte breakdown_delay;
 
	byte breakdowns_since_last_service;
 
	byte breakdown_chance;
 
	Year build_year;
 

	
 
	bool leave_depot_instantly; // NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace
 

	
 
	uint16 load_unload_time_rem;
 
	byte vehicle_flags;         // Used for gradual loading and other miscellaneous things (@see VehicleFlags enum)
 

	
 
	Money profit_this_year;
 
	Money profit_last_year;
 
	Money value;
 

	
 
	GroupID group_id;              ///< Index of group Pool array
 

	
 
	/* Used for timetabling. */
 
	uint32 current_order_time;     ///< How many ticks have passed since this order started.
 
	int32 lateness_counter;        ///< How many ticks late (or early if negative) this vehicle is.
 

	
 
	union {
 
		VehicleRail rail;
 
		VehicleAir air;
 
		VehicleRoad road;
 
		VehicleSpecial special;
 
		VehicleDisaster disaster;
 
		VehicleShip ship;
 
	} u;
 

	
 
	void BeginLoading();
 
	void LeaveStation();
 

	
 
	/**
 
	 * Handle the loading of the vehicle; when not it skips through dummy
 
	 * orders and does nothing in all other cases.
 
	 * @param mode is the non-first call for this vehicle in this tick?
 
	 */
 
	void HandleLoading(bool mode = false);
 

	
 
	/**
 
	 * An overriden version of new, so you can use the vehicle instance
 
	 * instead of a newly allocated piece of memory.
 
	 * @param size the size of the variable (unused)
 
	 * @param v    the vehicle to use as 'storage' backend
 
	 * @return the memory that is 'allocated'
 
	 */
 
	void *operator new(size_t size, Vehicle *v) { return v; }
 

	
 
	/**
 
	 * 'Free' the memory allocated by the overriden new.
 
	 * @param p the memory to 'free'
 
	 * @param v the vehicle that was given to 'new' on creation.
 
	 * @note This function isn't used (at the moment) and only added
 
	 *       to please some compiler.
 
	 */
 
	void operator delete(void *p, Vehicle *v) {}
 

	
 
	/**
 
	 * 'Free' the memory allocated by the overriden new.
 
	 * @param p the memory to 'free'
 
	 * @note This function isn't used (at the moment) and only added
 
	 *       as the above function was needed to please some compiler
 
	 *       which made it necessary to add this to please yet
 
	 *       another compiler...
 
	 */
 
	void operator delete(void *p) {}
 

	
 
	/** We want to 'destruct' the right class. */
 
	virtual ~Vehicle() {}
 

	
 
	/**
 
	 * Get a string 'representation' of the vehicle type.
 
	 * @return the string representation.
 
	 */
 
	virtual const char* GetTypeString() const = 0;
 

	
 
	/**
 
	 * Marks the vehicles to be redrawn and updates cached variables
 
	 */
 
	virtual void MarkDirty() {}
 

	
 
	/**
 
	 * Updates the x and y offsets and the size of the sprite used
 
	 * for this vehicle.
 
	 * @param direction the direction the vehicle is facing
 
	 */
 
	virtual void UpdateDeltaXY(Direction direction) {}
 

	
 
	/**
 
	 * Sets the expense type associated to this vehicle type
 
	 * @param income whether this is income or (running) expenses of the vehicle
 
	 */
 
	virtual ExpensesType GetExpenseType(bool income) const { return EXPENSES_OTHER; }
 

	
 
	/**
0 comments (0 inline, 0 general)