Changeset - r2833:35ddad9f0bb4
[Not reviewed]
master
0 1 0
truelight - 19 years ago 2006-01-07 10:57:32
truelight@openttd.org
(svn r3381) -Fix: r3374 left one bug: allow moving around wagons in a 100 long train
1 file changed with 3 insertions and 1 deletions:
0 comments (0 inline, 0 general)
train_cmd.c
Show inline comments
 
@@ -816,385 +816,387 @@ int CheckTrainStoppedInDepot(const Vehic
 
	}
 

	
 
	return count;
 
}
 

	
 
/**
 
 * Unlink a rail wagon from the consist.
 
 * @param v Vehicle to remove.
 
 * @param first The first vehicle of the consist.
 
 * @return The first vehicle of the consist.
 
 */
 
static Vehicle *UnlinkWagon(Vehicle *v, Vehicle *first)
 
{
 
	Vehicle *u;
 

	
 
	// unlinking the first vehicle of the chain?
 
	if (v == first) {
 
		v = GetNextVehicle(v);
 
		if (v == NULL) return NULL;
 

	
 
		if (IsTrainWagon(v)) SetFreeWagon(v);
 

	
 
		return v;
 
	}
 

	
 
	for (u = first; GetNextVehicle(u) != v; u = GetNextVehicle(u)) {}
 
	GetLastEnginePart(u)->next = GetNextVehicle(v);
 
	return first;
 
}
 

	
 
static Vehicle *FindGoodVehiclePos(const Vehicle *src)
 
{
 
	Vehicle *dst;
 
	EngineID eng = src->engine_type;
 
	TileIndex tile = src->tile;
 

	
 
	FOR_ALL_VEHICLES(dst) {
 
		if (dst->type == VEH_Train && IsFreeWagon(dst) &&
 
				dst->tile == tile) {
 
			// check so all vehicles in the line have the same engine.
 
			Vehicle *v = dst;
 

	
 
			while (v->engine_type == eng) {
 
				v = v->next;
 
				if (v == NULL) return dst;
 
			}
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/*
 
 * 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));
 
	if (dest == NULL) return;
 

	
 
	v->next = dest->next;
 
	dest->next = v;
 
	ClearFreeWagon(v);
 
	ClearFrontEngine(v);
 
}
 

	
 
/*
 
 * move around on the train so rear engines are placed correctly according to the other engines
 
 * always call with the front engine
 
 */
 
static void NormaliseTrainConsist(Vehicle *v)
 
{
 
	Vehicle *u;
 

	
 
	if (IsFreeWagon(v)) return;
 

	
 
	assert(IsFrontEngine(v));
 

	
 
	for(; v != NULL; v = GetNextVehicle(v)) {
 
		if (!IsMultiheaded(v) || !IsTrainEngine(v)) continue;
 

	
 
		/* make sure that there are no free cars before next engine */
 
		for(u = v; u->next != NULL && !IsTrainEngine(u->next); u = u->next);
 

	
 
		if (u == v->u.rail.other_multiheaded_part) continue;
 
		AddWagonToConsist(v->u.rail.other_multiheaded_part, u);
 

	
 
	}
 
}
 

	
 
/** Move a rail vehicle around inside the depot.
 
 * @param x,y unused
 
 * @param p1 various bitstuffed elements
 
 * - p1 (bit  0 - 15) source vehicle index
 
 * - p1 (bit 16 - 31) what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line
 
 * @param p2 (bit 0) move all vehicles following the source vehicle
 
 */
 
int32 CmdMoveRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	VehicleID s = GB(p1, 0, 16);
 
	VehicleID d = GB(p1, 16, 16);
 
	Vehicle *src, *dst, *src_head, *dst_head;
 

	
 
	if (!IsVehicleIndex(s)) return CMD_ERROR;
 

	
 
	src = GetVehicle(s);
 

	
 
	if (src->type != VEH_Train) return CMD_ERROR;
 

	
 
	// if nothing is selected as destination, try and find a matching vehicle to drag to.
 
	if (d == INVALID_VEHICLE) {
 
		dst = NULL;
 
		if (!IsTrainEngine(src)) dst = FindGoodVehiclePos(src);
 
	} else {
 
		dst = GetVehicle(d);
 
	}
 

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

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

	
 
	/* the player must be the owner */
 
	if (!CheckOwnership(src->owner) || (dst != NULL && !CheckOwnership(dst->owner)))
 
		return CMD_ERROR;
 

	
 
	/* locate the head of the two chains */
 
	src_head = GetFirstVehicleInChain(src);
 
	dst_head = NULL;
 
	if (dst != NULL) {
 
		dst_head = GetFirstVehicleInChain(dst);
 
		// Now deal with articulated part of destination wagon
 
		dst = GetLastEnginePart(dst);
 
	}
 

	
 
	if (dst != NULL && IsMultiheaded(dst) && !IsTrainEngine(dst) && IsTrainWagon(src)) {
 
		/* We are moving a wagon to the rear part of a multiheaded engine */
 
		if (dst->next == NULL) {
 
			/* It's the last one, so we will add the wagon just before the rear engine */
 
			dst = GetPrevVehicleInChain(dst);
 
			/* Now if the vehicle we want to link to is the vehicle itself, drop out */
 
			if (dst == src) return CMD_ERROR;
 
			// if dst is NULL, it means that dst got a rear multiheaded engine as first engine. We can't use that
 
			if (dst == NULL) return CMD_ERROR;
 
		} else {
 
			/* there are more units on this train, so we will add the wagon after the next one*/
 
			dst = dst->next;
 
		}
 
	}
 

	
 
	if (IsTrainEngine(src) && dst_head != NULL) {
 
		/* we need to make sure that we didn't place it between a pair of multiheaded engines */
 
		Vehicle *u, *engine = NULL;
 

	
 
		for(u = dst_head; u != NULL; u = u->next) {
 
			if (IsTrainEngine(u) && IsMultiheaded(u) && u->u.rail.other_multiheaded_part != NULL) {
 
				engine = u;
 
			}
 
				if (engine != NULL && engine->u.rail.other_multiheaded_part == u) {
 
					engine = NULL;
 
				}
 
				if (u == dst) {
 
					if (engine != NULL) {
 
					dst = engine->u.rail.other_multiheaded_part;
 
					}
 
					break;
 
				}
 

	
 
		}
 
	}
 

	
 
	if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR);
 

	
 
	{
 
		int r, num = 0;
 

	
 
		r = CheckTrainStoppedInDepot(src_head);
 
		/* check if all vehicles in the source train are stopped inside a depot */
 
		if (r < 0) return CMD_ERROR;
 

	
 
		num += r;
 

	
 
		/* check if all the vehicles in the dest train are stopped */
 
		if (dst_head != NULL) {
 
			r = CheckTrainStoppedInDepot(dst_head);
 
			if (r < 0) return CMD_ERROR;
 

	
 
			num += r;
 
			/* If we move in the same vehicle, it is okay */
 
			if (dst_head != src_head)
 
				num += r;
 

	
 
			assert(dst_head->tile == src_head->tile);
 
		}
 

	
 
		/* Check that the length of the dest train is no longer than XXX vehicles */
 
		if (num > (_patches.mammoth_trains ? 100 : 9) && IsFrontEngine(dst_head))
 
			return_cmd_error(STR_8819_TRAIN_TOO_LONG);
 
	}
 

	
 
	// when moving all wagons, we can't have the same src_head and dst_head
 
	if (HASBIT(p2, 0) && src_head == dst_head) return 0;
 

	
 
	// moving a loco to a new line?, then we need to assign a unitnumber.
 
	if (dst == NULL && !IsFrontEngine(src) && IsTrainEngine(src)) {
 
		UnitID unit_num = GetFreeUnitNumber(VEH_Train);
 
		if (unit_num > _patches.max_trains)
 
			return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
 

	
 
		if (flags & DC_EXEC)
 
			src->unitnumber = unit_num;
 
	}
 

	
 

	
 
	/* do it? */
 
	if (flags & DC_EXEC) {
 
		/* clear the ->first cache */
 
		{
 
			Vehicle *u;
 

	
 
			for (u = src_head; u != NULL; u = u->next) u->first = NULL;
 
			for (u = dst_head; u != NULL; u = u->next) u->first = NULL;
 
		}
 

	
 
		if (HASBIT(p2, 0)) {
 
			// unlink ALL wagons
 
			if (src != src_head) {
 
				Vehicle *v = src_head;
 
				while (GetNextVehicle(v) != src) v = GetNextVehicle(v);
 
				GetLastEnginePart(v)->next = NULL;
 
			} else {
 
				src_head = NULL;
 
			}
 
		} else {
 
			// if moving within the same chain, dont use dst_head as it may get invalidated
 
			if (src_head == dst_head)
 
				dst_head = NULL;
 
			// unlink single wagon from linked list
 
			src_head = UnlinkWagon(src, src_head);
 
			GetLastEnginePart(src)->next = NULL;
 
		}
 

	
 
		if (dst == NULL) {
 
			// move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4.
 
			if (IsTrainEngine(src)) {
 
				if (!IsFrontEngine(src)) {
 
					// setting the type to 0 also involves setting up the orders field.
 
					SetFrontEngine(src);
 
					assert(src->orders == NULL);
 
					src->num_orders = 0;
 
				}
 
			} else {
 
				SetFreeWagon(src);
 
			}
 
			dst_head = src;
 
		} else {
 
			if (IsFrontEngine(src)) {
 
				// the vehicle was previously a loco. need to free the order list and delete vehicle windows etc.
 
				DeleteWindowById(WC_VEHICLE_VIEW, src->index);
 
				DeleteVehicleOrders(src);
 
			}
 

	
 
			ClearFrontEngine(src);
 
			ClearFreeWagon(src);
 
			src->unitnumber = 0; // doesn't occupy a unitnumber anymore.
 

	
 
			// link in the wagon(s) in the chain.
 
			{
 
				Vehicle *v;
 

	
 
				for (v = src; GetNextVehicle(v) != NULL; v = GetNextVehicle(v));
 
				GetLastEnginePart(v)->next = dst->next;
 
			}
 
			dst->next = 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);
 
		}
 

	
 
		if (HASBIT(p2, 0) && src_head != NULL && src_head != src) {
 
			/* if we stole a rear multiheaded engine, we better give it back to the front end */
 
			Vehicle *engine = NULL, *u;
 
			for (u = src_head; u != NULL; u = u->next) {
 
				if (IsMultiheaded(u)) {
 
					if (IsTrainEngine(u)) {
 
						engine = u;
 
						continue;
 
					}
 
					/* we got the rear engine to match with the front one */
 
					engine = NULL;
 
				}
 
			}
 
			if (engine != NULL && engine->u.rail.other_multiheaded_part != NULL) {
 
				AddWagonToConsist(engine->u.rail.other_multiheaded_part, engine);
 
				// previous line set the front engine to the old front. We need to clear that
 
				engine->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)) {
 
			CmdMoveRailVehicle(x, y, flags, src_head->index | (INVALID_VEHICLE << 16), 1);
 
			src_head = NULL;	// don't do anything more to this train since the new call will do it
 
		}
 

	
 
		if (src_head) {
 
			NormaliseTrainConsist(src_head);
 
			TrainConsistChanged(src_head);
 
			if (IsFrontEngine(src_head)) {
 
				UpdateTrainAcceleration(src_head);
 
				InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index);
 
				/* Update the refit button and window */
 
				InvalidateWindow(WC_VEHICLE_REFIT, src_head->index);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, src_head->index, 12);
 
			}
 
			/* Update the depot window */
 
			InvalidateWindow(WC_VEHICLE_DEPOT, src_head->tile);
 
		};
 

	
 
		if (dst_head) {
 
			NormaliseTrainConsist(dst_head);
 
			TrainConsistChanged(dst_head);
 
			if (IsFrontEngine(dst_head)) {
 
				UpdateTrainAcceleration(dst_head);
 
				InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index);
 
				/* Update the refit button and window */
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, dst_head->index, 12);
 
				InvalidateWindow(WC_VEHICLE_REFIT, dst_head->index);
 
			}
 
			/* Update the depot window */
 
			InvalidateWindow(WC_VEHICLE_DEPOT, dst_head->tile);
 
		}
 

	
 
		RebuildVehicleLists();
 
	}
 

	
 
	return 0;
 
}
 

	
 
/** Start/Stop a train.
 
 * @param x,y unused
 
 * @param p1 train to start/stop
 
 * @param p2 unused
 
 */
 
int32 CmdStartStopTrain(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
 
	if (!IsVehicleIndex(p1)) return CMD_ERROR;
 

	
 
	v = GetVehicle(p1);
 

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

	
 
	if (flags & DC_EXEC) {
 
		v->u.rail.days_since_order_progr = 0;
 
		v->vehstatus ^= VS_STOPPED;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
	}
 
	return 0;
 
}
 

	
 
/** Sell a (single) train wagon/engine.
 
 * @param x,y unused
 
 * @param p1 the wagon/engine index
 
 * @param p2 the selling mode
 
 * - p2 = 0: only sell the single dragged wagon/engine (and any belonging rear-engines)
 
 * - p2 = 1: sell the vehicle and all vehicles following it in the chain
 
             if the wagon is dragged, don't delete the possibly belonging rear-engine to some front
 
 * - p2 = 2: when selling attached locos, rearrange all vehicles after it to separate lines;
 
 *           all wagons of the same type will go on the same line. Used by the AI currently
 
 */
 
int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v, *tmp, *first;
 
	Vehicle *new_f = NULL;
 
	int32 cost = 0;
0 comments (0 inline, 0 general)