|
|
@ -1182,83 +1182,17 @@ static CommandCost CheckNewTrain(const Train *dst, Train *src, bool move_chain,
|
|
|
|
return CommandCost();
|
|
|
|
return CommandCost();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** Move a rail vehicle around inside the depot.
|
|
|
|
/**
|
|
|
|
* @param tile unused
|
|
|
|
* Check/validate the new train(s)
|
|
|
|
* @param flags type of operation
|
|
|
|
* @param dst_head The destination chain of the to be moved vehicle
|
|
|
|
* Note: DC_AUTOREPLACE is set when autoreplace tries to undo its modifications or moves vehicles to temporary locations inside the depot.
|
|
|
|
* @param dst The destination for the to be moved vehicle
|
|
|
|
* @param p1 various bitstuffed elements
|
|
|
|
* @param src_head The source chain of the to be moved vehicle
|
|
|
|
* - p1 (bit 0 - 15) source vehicle index
|
|
|
|
* @param src The to be moved vehicle
|
|
|
|
* - p1 (bit 16 - 31) what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line
|
|
|
|
* @param move_chain Whether to move all vehicles after src or not
|
|
|
|
* @param p2 (bit 0) move all vehicles following the source vehicle
|
|
|
|
* @return possible error of this command.
|
|
|
|
* @param text unused
|
|
|
|
|
|
|
|
* @return the cost of this operation or an error
|
|
|
|
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
|
|
|
static CommandCost CheckNewTrainValidity(Train *dst_head, Train *dst, Train *src_head, Train *src, bool move_chain)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
VehicleID s = GB(p1, 0, 16);
|
|
|
|
|
|
|
|
VehicleID d = GB(p1, 16, 16);
|
|
|
|
|
|
|
|
bool move_chain = HasBit(p2, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Train *src = Train::GetIfValid(s);
|
|
|
|
|
|
|
|
if (src == NULL || !CheckOwnership(src->owner)) return CMD_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
|
|
|
|
|
|
|
|
if (src->vehstatus & VS_CRASHED) return CMD_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* if nothing is selected as destination, try and find a matching vehicle to drag to. */
|
|
|
|
|
|
|
|
Train *dst;
|
|
|
|
|
|
|
|
if (d == INVALID_VEHICLE) {
|
|
|
|
|
|
|
|
dst = src->IsEngine() ? NULL : FindGoodVehiclePos(src);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
dst = Train::GetIfValid(d);
|
|
|
|
|
|
|
|
if (dst == NULL || !CheckOwnership(dst->owner)) return CMD_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Do not allow appending to crashed vehicles, too */
|
|
|
|
|
|
|
|
if (dst->vehstatus & VS_CRASHED) return CMD_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* if an articulated part is being handled, deal with its parent vehicle */
|
|
|
|
|
|
|
|
src = src->GetFirstEnginePart();
|
|
|
|
|
|
|
|
if (dst != NULL) {
|
|
|
|
|
|
|
|
dst = dst->GetFirstEnginePart();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* don't move the same vehicle.. */
|
|
|
|
|
|
|
|
if (src == dst) return CommandCost();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* locate the head of the two chains */
|
|
|
|
|
|
|
|
Train *src_head = src->First();
|
|
|
|
|
|
|
|
Train *dst_head;
|
|
|
|
|
|
|
|
if (dst != NULL) {
|
|
|
|
|
|
|
|
dst_head = dst->First();
|
|
|
|
|
|
|
|
if (dst_head->tile != src_head->tile) return CMD_ERROR;
|
|
|
|
|
|
|
|
/* Now deal with articulated part of destination wagon */
|
|
|
|
|
|
|
|
dst = dst->GetLastEnginePart();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
dst_head = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (src->IsRearDualheaded()) return_cmd_error(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* when moving all wagons, we can't have the same src_head and dst_head */
|
|
|
|
|
|
|
|
if (move_chain && src_head == dst_head) return CommandCost();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((flags & DC_AUTOREPLACE) == 0) {
|
|
|
|
|
|
|
|
/* If the autoreplace flag is set we already are sure about
|
|
|
|
|
|
|
|
* the fact that the vehicle is stopped. Autoreplace also
|
|
|
|
|
|
|
|
* cannot add more units to a chain. As such it will never
|
|
|
|
|
|
|
|
* create a too long vehicle and thus there is no need to
|
|
|
|
|
|
|
|
* do the length checks for autoreplace. */
|
|
|
|
|
|
|
|
CommandCost ret = CheckNewTrainLength(dst_head, src_head, src, move_chain);
|
|
|
|
|
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* If the autoreplace flag is set we do not need to test for
|
|
|
|
|
|
|
|
* a new unit number because we are not going to create a
|
|
|
|
|
|
|
|
* new train. As such this can be skipped for autoreplace. */
|
|
|
|
|
|
|
|
ret = CheckNewTrain(dst, src, move_chain, flags);
|
|
|
|
|
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* Check whether the vehicles in the source chain are in the destination
|
|
|
|
* Check whether the vehicles in the source chain are in the destination
|
|
|
|
* chain. This can easily be done by checking whether the first vehicle
|
|
|
|
* chain. This can easily be done by checking whether the first vehicle
|
|
|
@ -1277,7 +1211,8 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
|
|
|
* one 'attaches' an already 'attached' vehicle causing more trouble
|
|
|
|
* one 'attaches' an already 'attached' vehicle causing more trouble
|
|
|
|
* than it actually solves (infinite loops and such).
|
|
|
|
* than it actually solves (infinite loops and such).
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
if (dst_head != NULL && !src_in_dst && (flags & DC_AUTOREPLACE) == 0) {
|
|
|
|
if (dst_head == NULL || src_in_dst) return CommandCost();
|
|
|
|
|
|
|
|
|
|
|
|
/* Forget everything, as everything is going to change */
|
|
|
|
/* Forget everything, as everything is going to change */
|
|
|
|
src->InvalidateNewGRFCacheOfChain();
|
|
|
|
src->InvalidateNewGRFCacheOfChain();
|
|
|
|
dst->InvalidateNewGRFCacheOfChain();
|
|
|
|
dst->InvalidateNewGRFCacheOfChain();
|
|
|
@ -1365,6 +1300,92 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
orig_tail->SetNext(NULL);
|
|
|
|
orig_tail->SetNext(NULL);
|
|
|
|
if (src_previous != NULL) src_previous->SetNext(src);
|
|
|
|
if (src_previous != NULL) src_previous->SetNext(src);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return CommandCost();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Move a rail vehicle around inside the depot.
|
|
|
|
|
|
|
|
* @param tile unused
|
|
|
|
|
|
|
|
* @param flags type of operation
|
|
|
|
|
|
|
|
* Note: DC_AUTOREPLACE is set when autoreplace tries to undo its modifications or moves vehicles to temporary locations inside the depot.
|
|
|
|
|
|
|
|
* @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
|
|
|
|
|
|
|
|
* @param text unused
|
|
|
|
|
|
|
|
* @return the cost of this operation or an error
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
VehicleID s = GB(p1, 0, 16);
|
|
|
|
|
|
|
|
VehicleID d = GB(p1, 16, 16);
|
|
|
|
|
|
|
|
bool move_chain = HasBit(p2, 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Train *src = Train::GetIfValid(s);
|
|
|
|
|
|
|
|
if (src == NULL || !CheckOwnership(src->owner)) return CMD_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Do not allow moving crashed vehicles inside the depot, it is likely to cause asserts later */
|
|
|
|
|
|
|
|
if (src->vehstatus & VS_CRASHED) return CMD_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* if nothing is selected as destination, try and find a matching vehicle to drag to. */
|
|
|
|
|
|
|
|
Train *dst;
|
|
|
|
|
|
|
|
if (d == INVALID_VEHICLE) {
|
|
|
|
|
|
|
|
dst = src->IsEngine() ? NULL : FindGoodVehiclePos(src);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
dst = Train::GetIfValid(d);
|
|
|
|
|
|
|
|
if (dst == NULL || !CheckOwnership(dst->owner)) return CMD_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Do not allow appending to crashed vehicles, too */
|
|
|
|
|
|
|
|
if (dst->vehstatus & VS_CRASHED) return CMD_ERROR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* if an articulated part is being handled, deal with its parent vehicle */
|
|
|
|
|
|
|
|
src = src->GetFirstEnginePart();
|
|
|
|
|
|
|
|
if (dst != NULL) {
|
|
|
|
|
|
|
|
dst = dst->GetFirstEnginePart();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* don't move the same vehicle.. */
|
|
|
|
|
|
|
|
if (src == dst) return CommandCost();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* locate the head of the two chains */
|
|
|
|
|
|
|
|
Train *src_head = src->First();
|
|
|
|
|
|
|
|
Train *dst_head;
|
|
|
|
|
|
|
|
if (dst != NULL) {
|
|
|
|
|
|
|
|
dst_head = dst->First();
|
|
|
|
|
|
|
|
if (dst_head->tile != src_head->tile) return CMD_ERROR;
|
|
|
|
|
|
|
|
/* Now deal with articulated part of destination wagon */
|
|
|
|
|
|
|
|
dst = dst->GetLastEnginePart();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
dst_head = NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (src->IsRearDualheaded()) return_cmd_error(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* when moving all wagons, we can't have the same src_head and dst_head */
|
|
|
|
|
|
|
|
if (move_chain && src_head == dst_head) return CommandCost();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ((flags & DC_AUTOREPLACE) == 0) {
|
|
|
|
|
|
|
|
/* If the autoreplace flag is set we already are sure about
|
|
|
|
|
|
|
|
* the fact that the vehicle is stopped. Autoreplace also
|
|
|
|
|
|
|
|
* cannot add more units to a chain. As such it will never
|
|
|
|
|
|
|
|
* create a too long vehicle and thus there is no need to
|
|
|
|
|
|
|
|
* do the length checks for autoreplace. */
|
|
|
|
|
|
|
|
CommandCost ret = CheckNewTrainLength(dst_head, src_head, src, move_chain);
|
|
|
|
|
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* If the autoreplace flag is set we do not need to test for
|
|
|
|
|
|
|
|
* a new unit number because we are not going to create a
|
|
|
|
|
|
|
|
* new train. As such this can be skipped for autoreplace. */
|
|
|
|
|
|
|
|
ret = CheckNewTrain(dst, src, move_chain, flags);
|
|
|
|
|
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* If the autoreplace flag is set we do not need to test for
|
|
|
|
|
|
|
|
* the validity because we are going to revert the train to
|
|
|
|
|
|
|
|
* its original state. As we can assume the original state
|
|
|
|
|
|
|
|
* was correct this can be skipped for autoreplace. */
|
|
|
|
|
|
|
|
ret = CheckNewTrainValidity(dst_head, dst, src_head, src, move_chain);
|
|
|
|
|
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* do it? */
|
|
|
|
/* do it? */
|
|
|
|