Changeset - r7497:84fb92567fc7
[Not reviewed]
master
0 10 0
rubidium - 17 years ago 2007-08-30 21:11:12
rubidium@openttd.org
(svn r11011) -Fix [FS#1129]: GetFirstVehicleInChain did change the game state while being marked const.
-Codechange: do not brute force determine the first vehicle in the chain or previous vehicle, but do it by properly accounting the previous and first pointers when updating the next pointer. This gives a performance increase of about 15% when there are a lot of vehicles in the game.
10 files changed with 101 insertions and 132 deletions:
0 comments (0 inline, 0 general)
src/autoreplace_cmd.cpp
Show inline comments
 
@@ -49,22 +49,22 @@ static void MoveVehicleCargo(Vehicle *de
 
	/*
 
	 * The of the train will be incorrect at this moment. This is due
 
	 * to the fact that removing the old wagon updates the weight of
 
	 * the complete train, which is without the weight of cargo we just
 
	 * moved back into some (of the) new wagon(s).
 
	 */
 
	if (dest->type == VEH_TRAIN) TrainConsistChanged(dest->first);
 
	if (dest->type == VEH_TRAIN) TrainConsistChanged(dest->First());
 
}
 

	
 
static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, const EngineID engine_type)
 
{
 
	const Order *o;
 
	const Vehicle *u;
 

	
 
	if (v->type == VEH_TRAIN) {
 
		u = GetFirstVehicleInChain(v);
 
		u = v->First();
 
	} else {
 
		u = v;
 
	}
 

	
 
	FOR_VEHICLE_ORDERS(u, o) {
 
		if (!(o->refit_cargo < NUM_CARGO)) continue;
 
@@ -101,13 +101,13 @@ static CargoID GetNewCargoTypeForReplace
 
	/* Below this line it's safe to assume that the vehicle in question is a train */
 

	
 
	if (v->cargo_cap != 0) return CT_INVALID; // trying to replace a vehicle with cargo capacity into another one with incompatible cargo type
 

	
 
	/* the old engine didn't have cargo capacity, but the new one does
 
	 * now we will figure out what cargo the train is carrying and refit to fit this */
 
	v = GetFirstVehicleInChain(v);
 
	v = v->First();
 
	do {
 
		if (v->cargo_cap == 0) continue;
 
		/* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */
 
		if (v->cargo_type == new_cargo_type) return CT_NO_REFIT;
 
		if (CanRefitTo(engine_type, v->cargo_type)) return v->cargo_type;
 
	} while ((v = v->Next()) != NULL);
 
@@ -197,15 +197,15 @@ static CommandCost ReplaceVehicle(Vehicl
 
		if (old_v->type == VEH_TRAIN && !IsFrontEngine(old_v)) {
 
			/* this is a railcar. We need to move the car into the train
 
			 * We add the new engine after the old one instead of replacing it. It will give the same result anyway when we
 
			 * sell the old engine in a moment
 
			 */
 
			/* Get the vehicle in front of the one we move out */
 
			Vehicle *front = GetPrevVehicleInChain(old_v);
 
			Vehicle *front = old_v->Previous();
 
			/* If the vehicle in front is the rear end of a dualheaded engine, then we need to use the one in front of that one */
 
			if (IsMultiheaded(front) && !IsTrainEngine(front)) front = GetPrevVehicleInChain(front);
 
			if (IsMultiheaded(front) && !IsTrainEngine(front)) front = front->Previous();
 
			/* Now we move the old one out of the train */
 
			DoCommand(0, (INVALID_VEHICLE << 16) | old_v->index, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
			/* Add the new vehicle */
 
			DoCommand(0, (front->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
		} else {
 
			// copy/clone the orders
 
@@ -233,13 +233,13 @@ static CommandCost ReplaceVehicle(Vehicl
 
				if (temp_v != NULL) {
 
					DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 
				}
 
			}
 
		}
 
		/* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */
 
		MoveVehicleCargo(new_v->type == VEH_TRAIN ? GetFirstVehicleInChain(new_v) : new_v, old_v);
 
		MoveVehicleCargo(new_v->type == VEH_TRAIN ? new_v->First() : new_v, old_v);
 

	
 
		// Get the name of the old vehicle if it has a custom name.
 
		if (!IsCustomName(old_v->string_id)) {
 
			vehicle_name[0] = '\0';
 
		} else {
 
			SetDParam(0, old_v->index);
src/depot_gui.cpp
Show inline comments
 
@@ -356,13 +356,13 @@ static int GetVehicleFromDepotWndPt(cons
 
			x += skip;
 

	
 
			/* find the vehicle in this row that was clicked */
 
			while (v != NULL && (x -= v->u.rail.cached_veh_length) >= 0) v = v->Next();
 

	
 
			/* if an articulated part was selected, find its parent */
 
			while (v != NULL && IsArticulatedPart(v)) v = GetPrevVehicleInChain(v);
 
			while (v != NULL && IsArticulatedPart(v)) v = v->Previous();
 

	
 
			d->wagon = v;
 

	
 
			return MODE_DRAG_VEHICLE;
 
			}
 
			break;
 
@@ -395,13 +395,13 @@ static void TrainDepotMoveVehicle(Vehicl
 

	
 
	if (v == wagon) return;
 

	
 
	if (wagon == NULL) {
 
		if (head != NULL) wagon = GetLastVehicleInChain(head);
 
	} else  {
 
		wagon = GetPrevVehicleInChain(wagon);
 
		wagon = wagon->Previous();
 
		if (wagon == NULL) return;
 
	}
 

	
 
	if (wagon == v) return;
 

	
 
	DoCommandP(v->tile, v->index + ((wagon == NULL ? INVALID_VEHICLE : wagon->index) << 16), _ctrl_pressed ? 1 : 0, NULL, CMD_MOVE_RAIL_VEHICLE | CMD_MSG(STR_8837_CAN_T_MOVE_VEHICLE));
 
@@ -472,13 +472,13 @@ static void HandleCloneVehClick(const Ve
 
{
 
	uint error_str;
 

	
 
	if (v == NULL) return;
 

	
 
	if (v->HasFront() && !v->IsPrimaryVehicle()) {
 
		v = GetFirstVehicleInChain(v);
 
		v = v->First();
 
		/* Do nothing when clicking on a train in depot with no loc attached */
 
		if (v->type == VEH_TRAIN && !IsFrontEngine(v)) return;
 
	}
 

	
 
	switch (v->type) {
 
		case VEH_TRAIN:    error_str = CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE); break;
src/newgrf_engine.cpp
Show inline comments
 
@@ -513,13 +513,13 @@ static uint32 VehicleGetVariable(const R
 

	
 
			{
 
				const Vehicle* u;
 
				byte chain_before = 0;
 
				byte chain_after  = 0;
 

	
 
				for (u = GetFirstVehicleInChain(v); u != v; u = u->Next()) {
 
				for (u = v->First(); u != v; u = u->Next()) {
 
					chain_before++;
 
					if (variable == 0x41 && u->engine_type != v->engine_type) chain_before = 0;
 
				}
 

	
 
				while (u->Next() != NULL && (variable == 0x40 || u->Next()->engine_type == v->engine_type)) {
 
					chain_after++;
 
@@ -607,13 +607,13 @@ static uint32 VehicleGetVariable(const R
 
			 * F - previous wagon to current wagon, 0 if vehicle is first
 
			 * B - current wagon to next wagon, 0 if wagon is last
 
			 * T - previous wagon to next wagon, 0 in an S-bend
 
			 */
 
			if (v->type != VEH_TRAIN) return 0;
 

	
 
			const Vehicle *u_p = GetPrevVehicleInChain(v);
 
			const Vehicle *u_p = v->Previous();
 
			const Vehicle *u_n = v->Next();
 
			DirDiff f = (u_p == NULL) ?  DIRDIFF_SAME : DirDifference(u_p->direction, v->direction);
 
			DirDiff b = (u_n == NULL) ?  DIRDIFF_SAME : DirDifference(v->direction, u_n->direction);
 
			DirDiff t = ChangeDirDiff(f, b);
 

	
 
			return ((t > DIRDIFF_REVERSE ? t | 8 : t) << 16) |
 
@@ -755,14 +755,14 @@ static uint32 VehicleGetVariable(const R
 
				case 0x66: return v->u.rail.railtype;
 
				case 0x73: return v->u.rail.cached_veh_length;
 
				case 0x74: return v->u.rail.cached_power;
 
				case 0x75: return GB(v->u.rail.cached_power,  8, 24);
 
				case 0x76: return GB(v->u.rail.cached_power, 16, 16);
 
				case 0x77: return GB(v->u.rail.cached_power, 24,  8);
 
				case 0x7C: return v->first->index;
 
				case 0x7D: return GB(v->first->index, 8, 8);
 
				case 0x7C: return v->First()->index;
 
				case 0x7D: return GB(v->First()->index, 8, 8);
 
				case 0x7F: return 0; // Used for vehicle reversing hack in TTDP
 
			}
 
			break;
 

	
 
		case VEH_ROAD:
 
			switch (variable - 0x80) {
 
@@ -801,13 +801,13 @@ static const SpriteGroup *VehicleResolve
 
	uint set;
 
	bool in_motion;
 

	
 
	if (v == NULL) return group->g.real.loading[0];
 

	
 
	if (v->type == VEH_TRAIN) {
 
		in_motion = GetFirstVehicleInChain(v)->current_order.type != OT_LOADING;
 
		in_motion = v->First()->current_order.type != OT_LOADING;
 
	} else {
 
		in_motion = v->current_order.type != OT_LOADING;
 
	}
 

	
 
	totalsets = in_motion ? group->g.real.num_loaded : group->g.real.num_loading;
 

	
 
@@ -829,13 +829,13 @@ static inline void NewVehicleResolver(Re
 
	res->GetTriggers   = &VehicleGetTriggers;
 
	res->SetTriggers   = &VehicleSetTriggers;
 
	res->GetVariable   = &VehicleGetVariable;
 
	res->ResolveReal   = &VehicleResolveReal;
 

	
 
	res->u.vehicle.self   = v;
 
	res->u.vehicle.parent = (v != NULL && v->HasFront()) ? GetFirstVehicleInChain(v) : v;
 
	res->u.vehicle.parent = (v != NULL && v->HasFront()) ? v->First() : v;
 

	
 
	res->u.vehicle.self_type = engine_type;
 

	
 
	res->info_view = false;
 

	
 
	res->callback        = CBID_NO_CALLBACK;
 
@@ -1030,13 +1030,13 @@ static void DoTriggerVehicle(Vehicle *v,
 
			/* Indexing part of vehicle random bits needs to be
 
			 * same for all triggered vehicles in the chain (to get
 
			 * all the random-cargo wagons carry the same cargo,
 
			 * i.e.), so we give them all the NEW_CARGO triggered
 
			 * vehicle's portion of random bits. */
 
			assert(first);
 
			DoTriggerVehicle((v->type == VEH_TRAIN) ? GetFirstVehicleInChain(v) : v, VEHICLE_TRIGGER_ANY_NEW_CARGO, new_random_bits, false);
 
			DoTriggerVehicle((v->type == VEH_TRAIN) ? v->First() : v, VEHICLE_TRIGGER_ANY_NEW_CARGO, new_random_bits, false);
 
			break;
 

	
 
		case VEHICLE_TRIGGER_DEPOT:
 
			/* We now trigger the next vehicle in chain recursively.
 
			 * The random bits portions may be different for each
 
			 * vehicle in chain. */
src/order_gui.cpp
Show inline comments
 
@@ -353,13 +353,13 @@ static Order GetOrderCmdFromTile(const V
 

	
 
static bool HandleOrderVehClick(const Vehicle *v, const Vehicle *u, Window *w)
 
{
 
	if (u->type != v->type) return false;
 

	
 
	if (u->HasFront() && !u->IsPrimaryVehicle()) {
 
		u = GetFirstVehicleInChain(u);
 
		u = u->First();
 
		if (!u->IsPrimaryVehicle()) return false;
 
	}
 

	
 
	// v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet
 
	// obviously if you press CTRL on a non-empty orders vehicle you know what you are doing
 
	if (v->num_orders != 0 && _ctrl_pressed == 0) return false;
src/roadveh_cmd.cpp
Show inline comments
 
@@ -138,14 +138,14 @@ byte GetRoadVehLength(const Vehicle *v)
 
void RoadVehUpdateCache(Vehicle *v)
 
{
 
	assert(v->type == VEH_ROAD);
 
	assert(IsRoadVehFront(v));
 

	
 
	for (Vehicle *u = v; u != NULL; u = u->Next()) {
 
		/* Update the v->first cache. */
 
		if (u->first == NULL) u->first = v;
 
		/* Check the v->first cache. */
 
		assert(u->First() == v);
 

	
 
		/* Update the 'first engine' */
 
		u->u.road.first_engine = (v == u) ? INVALID_ENGINE : v->engine_type;
 

	
 
		/* Update the length of the vehicle. */
 
		u->u.road.cached_veh_length = GetRoadVehLength(u);
 
@@ -256,13 +256,12 @@ CommandCost CmdBuildRoadVeh(TileIndex ti
 
		v->u.road.compatible_roadtypes = RoadTypeToRoadTypes(v->u.road.roadtype);
 
		v->u.road.cached_veh_length = GetRoadVehLength(v);
 

	
 
		v->vehicle_flags = 0;
 
		if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SETBIT(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
 

	
 
		v->first = NULL;
 
		v->cargo_cap = GetVehicleProperty(v, 0x0F, rvi->capacity);
 

	
 
		AddArticulatedParts(vl, VEH_ROAD);
 

	
 
		VehiclePositionChanged(v);
 

	
 
@@ -865,13 +864,13 @@ static void* EnumCheckRoadVehClose(Vehic
 

	
 
	return
 
		v->type == VEH_ROAD &&
 
		!v->IsInDepot() &&
 
		myabs(v->z_pos - rvf->veh->z_pos) < 6 &&
 
		v->direction == rvf->dir &&
 
		GetFirstVehicleInChain(rvf->veh) != GetFirstVehicleInChain(v) &&
 
		rvf->veh->First() != v->First() &&
 
		(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;
 
}
src/train_cmd.cpp
Show inline comments
 
@@ -158,14 +158,14 @@ void TrainConsistChanged(Vehicle* v)
 
	v->u.rail.cached_total_length = 0;
 
	v->u.rail.compatible_railtypes = 0;
 

	
 
	for (Vehicle *u = v; u != NULL; u = u->Next()) {
 
		const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
 

	
 
		/* Update the v->first cache. This is faster than having to brute force it later. */
 
		if (u->first == NULL) u->first = v;
 
		/* Check the v->first cache. */
 
		assert(u->First() == v);
 

	
 
		/* update the 'first engine' */
 
		u->u.rail.first_engine = v == u ? INVALID_ENGINE : first_engine;
 
		u->u.rail.railtype = rvi_u->railtype;
 

	
 
		if (IsTrainEngine(u)) first_engine = u->engine_type;
 
@@ -580,14 +580,14 @@ static CommandCost CmdBuildRailWagon(Eng
 

	
 
			AddArticulatedParts(vl, VEH_TRAIN);
 

	
 
			_new_vehicle_id = v->index;
 

	
 
			VehiclePositionChanged(v);
 
			TrainConsistChanged(GetFirstVehicleInChain(v));
 
			UpdateTrainGroupID(GetFirstVehicleInChain(v));
 
			TrainConsistChanged(v->First());
 
			UpdateTrainGroupID(v->First());
 

	
 
			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
			if (IsLocalPlayer()) {
 
				InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Train window
 
			}
 
			GetPlayer(_current_player)->num_engines[engine]++;
 
@@ -617,21 +617,21 @@ static CommandCost EstimateTrainCost(Eng
 
{
 
	return CommandCost(GetEngineProperty(engine, 0x17, rvi->base_cost) * (_price.build_railvehicle >> 3) >> 5);
 
}
 

	
 
static void AddRearEngineToMultiheadedTrain(Vehicle* v, Vehicle* u, bool building)
 
{
 
	u = new (u) Train();
 
	u->direction = v->direction;
 
	u->owner = v->owner;
 
	u->tile = v->tile;
 
	u->x_pos = v->x_pos;
 
	u->y_pos = v->y_pos;
 
	u->z_pos = v->z_pos;
 
	u->u.rail.track = TRACK_BIT_DEPOT;
 
	u->vehstatus = v->vehstatus & ~VS_STOPPED;
 
	u = new (u) Train();
 
	u->subtype = 0;
 
	SetMultiheaded(u);
 
	u->spritenum = v->spritenum + 1;
 
	u->cargo_type = v->cargo_type;
 
	u->cargo_subtype = v->cargo_subtype;
 
	u->cargo_cap = v->cargo_cap;
 
@@ -838,13 +838,12 @@ static Vehicle *UnlinkWagon(Vehicle *v, 
 
		return v;
 
	}
 

	
 
	Vehicle *u;
 
	for (u = first; GetNextVehicle(u) != v; u = GetNextVehicle(u)) {}
 
	GetLastEnginePart(u)->SetNext(GetNextVehicle(v));
 
	v->first = NULL; // we shouldn't point to the old first, since the vehicle isn't in that chain anymore
 
	return first;
 
}
 

	
 
static Vehicle *FindGoodVehiclePos(const Vehicle *src)
 
{
 
	Vehicle *dst;
 
@@ -869,17 +868,18 @@ static Vehicle *FindGoodVehiclePos(const
 
/*
 
 * add a vehicle v behind vehicle dest
 
 * use this function since it sets flags as needed
 
 */
 
static void AddWagonToConsist(Vehicle *v, Vehicle *dest)
 
{
 
	UnlinkWagon(v, GetFirstVehicleInChain(v));
 
	UnlinkWagon(v, v->First());
 
	if (dest == NULL) return;
 

	
 
	v->SetNext(dest->Next());
 
	Vehicle *next = dest->Next();
 
	dest->SetNext(v);
 
	v->SetNext(next);
 
	ClearFreeWagon(v);
 
	ClearFrontEngine(v);
 
}
 

	
 
/*
 
 * move around on the train so rear engines are placed correctly according to the other engines
 
@@ -930,25 +930,25 @@ CommandCost CmdMoveRailVehicle(TileIndex
 
		if (!IsValidVehicleID(d)) return CMD_ERROR;
 
		dst = GetVehicle(d);
 
		if (dst->type != VEH_TRAIN || !CheckOwnership(dst->owner)) return CMD_ERROR;
 
	}
 

	
 
	/* if an articulated part is being handled, deal with its parent vehicle */
 
	while (IsArticulatedPart(src)) src = GetPrevVehicleInChain(src);
 
	while (IsArticulatedPart(src)) src = src->Previous();
 
	if (dst != NULL) {
 
		while (IsArticulatedPart(dst)) dst = GetPrevVehicleInChain(dst);
 
		while (IsArticulatedPart(dst)) dst = dst->Previous();
 
	}
 

	
 
	/* don't move the same vehicle.. */
 
	if (src == dst) return CommandCost();
 

	
 
	/* locate the head of the two chains */
 
	Vehicle *src_head = GetFirstVehicleInChain(src);
 
	Vehicle *src_head = src->First();
 
	Vehicle *dst_head;
 
	if (dst != NULL) {
 
		dst_head = GetFirstVehicleInChain(dst);
 
		dst_head = dst->First();
 
		if (dst_head->tile != src_head->tile) return CMD_ERROR;
 
		/* Now deal with articulated part of destination wagon */
 
		dst = GetLastEnginePart(dst);
 
	} else {
 
		dst_head = NULL;
 
	}
 
@@ -1019,16 +1019,12 @@ CommandCost CmdMoveRailVehicle(TileIndex
 
			}
 
		}
 
	}
 

	
 
	/* do it? */
 
	if (flags & DC_EXEC) {
 
		/* clear the ->first cache */
 
		for (Vehicle *u = src_head; u != NULL; u = u->Next()) u->first = NULL;
 
		for (Vehicle *u = dst_head; u != NULL; u = u->Next()) u->first = NULL;
 

	
 
		/* If we move the front Engine and if the second vehicle is not an engine
 
		   add the whole vehicle to the DEFAULT_GROUP */
 
		if (IsFrontEngine(src) && !IsDefaultGroupID(src->group_id)) {
 
			const Vehicle *v = GetNextVehicle(src);
 

	
 
			if (v != NULL && !IsTrainEngine(v)) {
 
@@ -1098,19 +1094,18 @@ CommandCost CmdMoveRailVehicle(TileIndex
 

	
 
				for (v = src; GetNextVehicle(v) != NULL; v = GetNextVehicle(v));
 
				GetLastEnginePart(v)->SetNext(dst->Next());
 
			}
 
			dst->SetNext(src);
 
		}
 

	
 
		if (src->u.rail.other_multiheaded_part != NULL) {
 
			if (src->u.rail.other_multiheaded_part == src_head) {
 
				src_head = src_head->Next();
 
			}
 
			AddWagonToConsist(src->u.rail.other_multiheaded_part, src);
 
			/* previous line set the front engine to the old front. We need to clear that */
 
			src->u.rail.other_multiheaded_part->first = NULL;
 
		}
 

	
 
		/* If there is an engine behind first_engine we moved away, it should become new first_engine
 
		 * To do this, CmdMoveRailVehicle must be called once more
 
		 * we can't loop forever here because next time we reach this line we will have a front engine */
 
		if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) {
 
@@ -1215,14 +1210,14 @@ CommandCost CmdSellRailWagon(TileIndex t
 
	Vehicle *v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_TRAIN || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 

	
 
	while (IsArticulatedPart(v)) v = GetPrevVehicleInChain(v);
 
	Vehicle *first = GetFirstVehicleInChain(v);
 
	while (IsArticulatedPart(v)) v = v->Previous();
 
	Vehicle *first = v->First();
 

	
 
	/* make sure the vehicle is stopped in the depot */
 
	if (CheckTrainStoppedInDepot(first) < 0) {
 
		return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED);
 
	}
 

	
 
@@ -1259,15 +1254,12 @@ CommandCost CmdSellRailWagon(TileIndex t
 

	
 
			/* 2. We are selling the first engine, some special action might be required
 
			 * here, so take attention */
 
			if ((flags & DC_EXEC) && v == first) {
 
				Vehicle *new_f = GetNextVehicle(first);
 

	
 
				/* 2.1 If the first wagon is sold, update the first-> pointers to NULL */
 
				for (Vehicle *tmp = first; tmp != NULL; tmp = tmp->Next()) tmp->first = NULL;
 

	
 
				/* 2.2 If there are wagons present after the deleted front engine, check
 
				 * if the second wagon (which will be first) is an engine. If it is one,
 
				 * promote it as a new train, retaining the unitnumber, orders */
 
				if (new_f != NULL && IsTrainEngine(new_f)) {
 
					switch_engine = true;
 
					/* Copy important data from the front engine */
 
@@ -1630,13 +1622,13 @@ CommandCost CmdReverseTrainDirection(Til
 
		/* turn a single unit around */
 

	
 
		if (IsMultiheaded(v) || HASBIT(EngInfo(v->engine_type)->callbackmask, CBM_ARTIC_ENGINE)) {
 
			return_cmd_error(STR_ONLY_TURN_SINGLE_UNIT);
 
		}
 

	
 
		Vehicle *front = GetFirstVehicleInChain(v);
 
		Vehicle *front = v->First();
 
		/* make sure the vehicle is stopped in the depot */
 
		if (CheckTrainStoppedInDepot(front) < 0) {
 
			return_cmd_error(STR_881A_TRAINS_CAN_ONLY_BE_ALTERED);
 
		}
 

	
 
		if (flags & DC_EXEC) {
 
@@ -1774,13 +1766,13 @@ CommandCost CmdRefitRailVehicle(TileInde
 
		}
 
	} while ((v = v->Next()) != NULL && !only_this);
 

	
 
	_returned_refit_capacity = num;
 

	
 
	/* Update the train's cached variables */
 
	if (flags & DC_EXEC) TrainConsistChanged(GetFirstVehicleInChain(GetVehicle(p1)));
 
	if (flags & DC_EXEC) TrainConsistChanged(GetVehicle(p1)->First());
 

	
 
	return cost;
 
}
 

	
 
struct TrainFindDepotData {
 
	uint best_length;
 
@@ -2715,13 +2707,13 @@ static void *FindTrainCollideEnum(Vehicl
 
			v->type == VEH_TRAIN &&
 
			v->u.rail.track != TRACK_BIT_DEPOT &&
 
			myabs(v->z_pos - tcc->v->z_pos) < 6 &&
 
			myabs(v->x_pos - tcc->v->x_pos) < 6 &&
 
			myabs(v->y_pos - tcc->v->y_pos) < 6 ) {
 

	
 
		Vehicle *coll = GetFirstVehicleInChain(v);
 
		Vehicle *coll = v->First();
 

	
 
		/* it can't collide with its own wagons */
 
		if (tcc->v == coll ||
 
			(tcc->v->u.rail.track == TRACK_BIT_WORMHOLE && (tcc->v->direction & 2) != (v->direction & 2)))
 
			return NULL;
 

	
 
@@ -2804,13 +2796,13 @@ static void *CheckVehicleAtSignal(Vehicl
 

	
 
static void TrainController(Vehicle *v, bool update_image)
 
{
 
	Vehicle *prev;
 

	
 
	/* For every vehicle after and including the given vehicle */
 
	for (prev = GetPrevVehicleInChain(v); v != NULL; prev = v, v = v->Next()) {
 
	for (prev = v->Previous(); v != NULL; prev = v, v = v->Next()) {
 
		DiagDirection enterdir = DIAGDIR_BEGIN;
 
		bool update_signals = false;
 
		BeginVehicleMove(v);
 

	
 
		GetNewVehiclePosResult gp = GetNewVehiclePos(v);
 
		if (v->u.rail.track != TRACK_BIT_WORMHOLE) {
 
@@ -2942,13 +2934,13 @@ static void TrainController(Vehicle *v, 
 
				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
 

	
 
				if (!HASBIT(r, VETS_ENTERED_WORMHOLE)) {
 
					v->tile = gp.new_tile;
 

	
 
					if (GetTileRailType(gp.new_tile) != GetTileRailType(gp.old_tile)) {
 
						TrainPowerChanged(GetFirstVehicleInChain(v));
 
						TrainPowerChanged(v->First());
 
					}
 

	
 
					v->u.rail.track = chosen_track;
 
					assert(v->u.rail.track);
 
				}
 

	
src/vehicle.cpp
Show inline comments
 
@@ -206,23 +206,37 @@ void VehiclePositionChanged(Vehicle *v)
 
/** Called after load to update coordinates */
 
void AfterLoadVehicles()
 
{
 
	Vehicle *v;
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		/* Reinstate the previous pointer */
 
		if (v->Next() != NULL) v->Next()->previous = v;
 

	
 
		v->UpdateDeltaXY(v->direction);
 

	
 
		v->fill_percent_te_id = INVALID_TE_ID;
 
		v->first = NULL;
 
		if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE;
 
		if (v->type == VEH_ROAD)  v->u.road.first_engine = INVALID_ENGINE;
 

	
 
		v->cargo.InvalidateCache();
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		/* Fill the first pointers */
 
		if (v->Previous() == NULL) {
 
			for (Vehicle *u = v; u != NULL; u = u->Next()) {
 
				u->first = v;
 
			}
 
		}
 
	}
 

	
 
	FOR_ALL_VEHICLES(v) {
 
		assert(v->first != NULL);
 

	
 
		if (v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) {
 
			TrainConsistChanged(v);
 
		} else if (v->type == VEH_ROAD && IsRoadVehFront(v)) {
 
			RoadVehUpdateCache(v);
 
		}
 
	}
 
@@ -266,12 +280,13 @@ void AfterLoadVehicles()
 
Vehicle::Vehicle()
 
{
 
	this->type               = VEH_INVALID;
 
	this->left_coord         = INVALID_COORD;
 
	this->group_id           = DEFAULT_GROUP;
 
	this->fill_percent_te_id = INVALID_TE_ID;
 
	this->first              = this;
 
}
 

	
 
/**
 
 * Get a value for a vehicle's random_bits.
 
 * @return A random value from 0 to 255.
 
 */
 
@@ -463,84 +478,12 @@ void InitializeVehicles()
 
Vehicle *GetLastVehicleInChain(Vehicle *v)
 
{
 
	while (v->Next() != NULL) v = v->Next();
 
	return v;
 
}
 

	
 
/** Finds the previous vehicle in a chain, by a brute force search.
 
 * This old function is REALLY slow because it searches through all vehicles to
 
 * find the previous vehicle, but if v->first has not been set, then this function
 
 * will need to be used to find the previous one. This function should never be
 
 * called by anything but GetFirstVehicleInChain
 
 */
 
static Vehicle *GetPrevVehicleInChain_bruteforce(const Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	FOR_ALL_VEHICLES(u) if (u->type == v->type && u->Next() == v) return u;
 

	
 
	return NULL;
 
}
 

	
 
/** Find the previous vehicle in a chain, by using the v->first cache.
 
 * While this function is fast, it cannot be used in the GetFirstVehicleInChain
 
 * function, otherwise you'll end up in an infinite loop call
 
 */
 
Vehicle *GetPrevVehicleInChain(const Vehicle *v)
 
{
 
	Vehicle *u;
 
	assert(v != NULL);
 

	
 
	u = GetFirstVehicleInChain(v);
 

	
 
	/* Check to see if this is the first */
 
	if (v == u) return NULL;
 

	
 
	for (; u->Next() != v; u = u->Next()) assert(u->Next() != NULL);
 

	
 
	return u;
 
}
 

	
 
/** Finds the first vehicle in a chain.
 
 * This function reads out the v->first cache. Should the cache be dirty,
 
 * it determines the first vehicle in a chain, and updates the cache.
 
 */
 
Vehicle *GetFirstVehicleInChain(const Vehicle *v)
 
{
 
	Vehicle* u;
 

	
 
	assert(v != NULL);
 
	assert(v->type == VEH_TRAIN || v->type == VEH_ROAD);
 

	
 
	if (v->first != NULL) {
 
		if (v->type == VEH_TRAIN) {
 
			if (IsFrontEngine(v->first) || IsFreeWagon(v->first)) return v->first;
 
		} else {
 
			if (IsRoadVehFront(v->first)) return v->first;
 
		}
 

	
 
		DEBUG(misc, 0, "v->first cache faulty. We shouldn't be here, rebuilding cache!");
 
	}
 

	
 
	/* It is the fact (currently) that newly built vehicles do not have
 
	 * their ->first pointer set. When this is the case, go up to the
 
	 * first engine and set the pointers correctly. Also the first pointer
 
	 * is not saved in a savegame, so this has to be fixed up after loading */
 

	
 
	/* Find the 'locomotive' or the first wagon in a chain */
 
	while ((u = GetPrevVehicleInChain_bruteforce(v)) != NULL) v = u;
 

	
 
	/* Set the first pointer of all vehicles in that chain to the first wagon */
 
	if ((v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v))) ||
 
			(v->type == VEH_ROAD && IsRoadVehFront(v))) {
 
		for (u = (Vehicle *)v; u != NULL; u = u->Next()) u->first = (Vehicle *)v;
 
	}
 

	
 
	return (Vehicle*)v;
 
}
 

	
 
uint CountVehiclesInChain(const Vehicle* v)
 
{
 
	uint count = 0;
 
	do count++; while ((v = v->Next()) != NULL);
 
	return count;
 
}
 
@@ -2176,20 +2119,20 @@ uint8 CalcPercentVehicleFilled(Vehicle *
 

	
 
void VehicleEnterDepot(Vehicle *v)
 
{
 
	switch (v->type) {
 
		case VEH_TRAIN:
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v);
 
			if (!IsFrontEngine(v)) v = v->First();
 
			UpdateSignalsOnSegment(v->tile, GetRailDepotDirection(v->tile));
 
			v->load_unload_time_rem = 0;
 
			break;
 

	
 
		case VEH_ROAD:
 
			InvalidateWindowClasses(WC_ROADVEH_LIST);
 
			if (!IsRoadVehFront(v)) v = GetFirstVehicleInChain(v);
 
			if (!IsRoadVehFront(v)) v = v->First();
 
			break;
 

	
 
		case VEH_SHIP:
 
			InvalidateWindowClasses(WC_SHIPS_LIST);
 
			v->u.ship.state = TRACK_BIT_DEPOT;
 
			RecalcShipStuff(v);
 
@@ -3165,12 +3108,34 @@ void Vehicle::HandleLoading(bool mode)
 
	}
 

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

	
 
void Vehicle::SetNext(Vehicle *next)
 
{
 
	if (this->next != NULL) {
 
		/* We had an old next vehicle. Update the first and previous pointers */
 
		for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
 
			v->first = this->next;
 
		}
 
		this->next->previous = NULL;
 
	}
 

	
 
	this->next = next;
 

	
 
	if (this->next != NULL) {
 
		/* A new next vehicle. Update the first and previous pointers */
 
		if (this->next->previous != NULL) this->next->previous->next = NULL;
 
		this->next->previous = this;
 
		for (Vehicle *v = this->next; v != NULL; v = v->Next()) {
 
			v->first = this->first;
 
		}
 
	}
 
}
 

	
 
void SpecialVehicle::UpdateDeltaXY(Direction direction)
 
{
 
	this->x_offs        = 0;
 
	this->y_offs        = 0;
 
	this->sprite_width  = 1;
 
	this->sprite_height = 1;
src/vehicle.h
Show inline comments
 
@@ -216,24 +216,27 @@ struct VehicleShip {
 
};
 

	
 
struct Vehicle;
 
DECLARE_OLD_POOL(Vehicle, Vehicle, 9, 125)
 

	
 
struct SaveLoad;
 
extern const SaveLoad *GetVehicleDescription(VehicleType vt);
 
const SaveLoad *GetVehicleDescription(VehicleType vt);
 
void AfterLoadVehicles();
 

	
 
struct Vehicle : PoolItem<Vehicle, VehicleID, &_Vehicle_pool> {
 
	VehicleTypeByte type;    ///< Type of vehicle
 
	byte subtype;            // subtype (Filled with values from EffectVehicles/TrainSubTypes/AircraftSubTypes)
 

	
 
private:
 
	Vehicle *next;           // pointer to the next vehicle in the chain
 
	Vehicle *previous;       // NOSAVE: pointer to the previous vehicle in the chain
 
	Vehicle *first;          // NOSAVE: pointer to the first vehicle in the chain
 
public:
 
	friend const SaveLoad *GetVehicleDescription(VehicleType vt); // So we can use private/protected variables in the saveload code
 
	friend void AfterLoadVehicles();
 

	
 
	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?
 
@@ -469,20 +472,33 @@ public:
 
	inline bool IsValid() const { return this->type != VEH_INVALID; }
 

	
 
	/**
 
	 * Set the next vehicle of this vehicle.
 
	 * @param next the next vehicle. NULL removes the next vehicle.
 
	 */
 
	void SetNext(Vehicle *next) { this->next = next; }
 
	void SetNext(Vehicle *next);
 

	
 
	/**
 
	 * Get the next vehicle of this vehicle.
 
	 * @note articulated parts are also counted as vehicles.
 
	 * @return the next vehicle or NULL when there isn't a next vehicle.
 
	 */
 
	inline Vehicle *Next() const { return this->next; }
 

	
 
	/**
 
	 * Get the previous vehicle of this vehicle.
 
	 * @note articulated parts are also counted as vehicles.
 
	 * @return the previous vehicle or NULL when there isn't a previous vehicle.
 
	 */
 
	inline Vehicle *Previous() const { return this->previous; }
 

	
 
	/**
 
	 * Get the first vehicle of this vehicle chain.
 
	 * @return the first vehicle of the chain.
 
	 */
 
	inline Vehicle *First() const { return this->first; }
 
};
 

	
 
/**
 
 * This class 'wraps' Vehicle; you do not actually instantiate this class.
 
 * You create a Vehicle using AllocateVehicle, so it is added to the pool
 
 * and you reinitialize that to a Train using:
 
@@ -553,16 +569,13 @@ struct InvalidVehicle : public Vehicle {
 
#define IS_CUSTOM_SECONDHEAD_SPRITE(x) (x == 0xFE)
 

	
 
typedef void *VehicleFromPosProc(Vehicle *v, void *data);
 

	
 
void VehicleServiceInDepot(Vehicle *v);
 
void VehiclePositionChanged(Vehicle *v);
 
void AfterLoadVehicles();
 
Vehicle *GetLastVehicleInChain(Vehicle *v);
 
Vehicle *GetPrevVehicleInChain(const Vehicle *v);
 
Vehicle *GetFirstVehicleInChain(const Vehicle *v);
 
uint CountVehiclesInChain(const Vehicle *v);
 
bool IsEngineCountable(const Vehicle *v);
 
void DeleteVehicleChain(Vehicle *v);
 
void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc);
 
void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc);
 
void CallVehicleTicks();
src/viewport.cpp
Show inline comments
 
@@ -1783,19 +1783,19 @@ static void CheckClickOnLandscape(const 
 
	if (pt.x != -1) ClickTile(TileVirtXY(pt.x, pt.y));
 
}
 

	
 

	
 
static void SafeShowTrainViewWindow(const Vehicle* v)
 
{
 
	if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v);
 
	if (!IsFrontEngine(v)) v = v->First();
 
	ShowVehicleViewWindow(v);
 
}
 

	
 
static void SafeShowRoadVehViewWindow(const Vehicle *v)
 
{
 
	if (!IsRoadVehFront(v)) v = GetFirstVehicleInChain(v);
 
	if (!IsRoadVehFront(v)) v = v->First();
 
	ShowVehicleViewWindow(v);
 
}
 

	
 
static void Nop(const Vehicle *v) {}
 

	
 
typedef void OnVehicleClickProc(const Vehicle *v);
src/water_cmd.cpp
Show inline comments
 
@@ -655,13 +655,13 @@ static void FloodVehicle(Vehicle *v)
 
				const AirportFTAClass *airport = st->Airport();
 

	
 
				if (v->z_pos != airport->delta_z + 1) return;
 
			}
 
			Vehicle *u;
 

	
 
			if (v->type != VEH_AIRCRAFT) v = GetFirstVehicleInChain(v);
 
			if (v->type != VEH_AIRCRAFT) v = v->First();
 
			u = v;
 

	
 
			/* crash all wagons, and count passengers */
 
			BEGIN_ENUM_WAGONS(v)
 
				if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
 
				v->vehstatus |= VS_CRASHED;
0 comments (0 inline, 0 general)