|
|
/* $Id$ */
|
|
|
|
|
|
/*
|
|
|
* This file is part of OpenTTD.
|
|
|
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
|
|
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
|
|
*/
|
|
|
|
|
|
/** @file autoreplace_cmd.cpp Deals with autoreplace execution but not the setup */
|
|
|
|
|
|
#include "stdafx.h"
|
|
|
#include "company_func.h"
|
|
|
#include "train.h"
|
|
|
#include "command_func.h"
|
|
|
#include "engine_func.h"
|
|
|
#include "vehicle_func.h"
|
|
|
#include "autoreplace_func.h"
|
|
|
#include "autoreplace_gui.h"
|
|
|
#include "articulated_vehicles.h"
|
|
|
#include "core/random_func.hpp"
|
|
|
|
|
|
#include "table/strings.h"
|
|
|
|
|
|
#include "safeguards.h"
|
|
|
|
|
|
extern void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index);
|
|
|
extern void ChangeVehicleNews(VehicleID from_index, VehicleID to_index);
|
|
|
extern void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index);
|
|
|
|
|
|
/**
|
|
|
* Figure out if two engines got at least one type of cargo in common (refitting if needed)
|
|
|
* @param engine_a one of the EngineIDs
|
|
|
* @param engine_b the other EngineID
|
|
|
* @param type the type of the engines
|
|
|
* @return true if they can both carry the same type of cargo (or at least one of them got no capacity at all)
|
|
|
*/
|
|
|
static bool EnginesHaveCargoInCommon(EngineID engine_a, EngineID engine_b)
|
|
|
{
|
|
|
CargoTypes available_cargoes_a = GetUnionOfArticulatedRefitMasks(engine_a, true);
|
|
|
CargoTypes available_cargoes_b = GetUnionOfArticulatedRefitMasks(engine_b, true);
|
|
|
return (available_cargoes_a == 0 || available_cargoes_b == 0 || (available_cargoes_a & available_cargoes_b) != 0);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Checks some basic properties whether autoreplace is allowed
|
|
|
* @param from Origin engine
|
|
|
* @param to Destination engine
|
|
|
* @param company Company to check for
|
|
|
* @return true if autoreplace is allowed
|
|
|
*/
|
|
|
bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
|
|
|
{
|
|
|
assert(Engine::IsValidID(from) && Engine::IsValidID(to));
|
|
|
|
|
|
/* we can't replace an engine into itself (that would be autorenew) */
|
|
|
if (from == to) return false;
|
|
|
|
|
|
const Engine *e_from = Engine::Get(from);
|
|
|
const Engine *e_to = Engine::Get(to);
|
|
|
VehicleType type = e_from->type;
|
|
|
|
|
|
/* check that the new vehicle type is available to the company and its type is the same as the original one */
|
|
|
if (!IsEngineBuildable(to, type, company)) return false;
|
|
|
|
|
|
switch (type) {
|
|
|
case VEH_TRAIN: {
|
|
|
/* make sure the railtypes are compatible */
|
|
|
if ((GetRailTypeInfo(e_from->u.rail.railtype)->compatible_railtypes & GetRailTypeInfo(e_to->u.rail.railtype)->compatible_railtypes) == 0) return false;
|
|
|
|
|
|
/* make sure we do not replace wagons with engines or vice versa */
|
|
|
if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.railveh_type == RAILVEH_WAGON)) return false;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case VEH_ROAD:
|
|
|
/* make sure that we do not replace a tram with a normal road vehicles or vice versa */
|
|
|
if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false;
|
|
|
break;
|
|
|
|
|
|
case VEH_AIRCRAFT:
|
|
|
/* make sure that we do not replace a plane with a helicopter or vice versa */
|
|
|
if ((e_from->u.air.subtype & AIR_CTOL) != (e_to->u.air.subtype & AIR_CTOL)) return false;
|
|
|
break;
|
|
|
|
|
|
default: break;
|
|
|
}
|
|
|
|
|
|
/* the engines needs to be able to carry the same cargo */
|
|
|
return EnginesHaveCargoInCommon(from, to);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Check the capacity of all vehicles in a chain and spread cargo if needed.
|
|
|
* @param v The vehicle to check.
|
|
|
* @pre You can only do this if the consist is not loading or unloading. It
|
|
|
* must not carry reserved cargo, nor cargo to be unloaded or transferred.
|
|
|
*/
|
|
|
void CheckCargoCapacity(Vehicle *v)
|
|
|
{
|
|
|
assert(v == NULL || v->First() == v);
|
|
|
|
|
|
for (Vehicle *src = v; src != NULL; src = src->Next()) {
|
|
|
assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
|
|
|
|
|
/* Do we need to more cargo away? */
|
|
|
if (src->cargo.TotalCount() <= src->cargo_cap) continue;
|
|
|
|
|
|
/* We need to move a particular amount. Try that on the other vehicles. */
|
|
|
uint to_spread = src->cargo.TotalCount() - src->cargo_cap;
|
|
|
for (Vehicle *dest = v; dest != NULL && to_spread != 0; dest = dest->Next()) {
|
|
|
assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
|
|
if (dest->cargo.TotalCount() >= dest->cargo_cap || dest->cargo_type != src->cargo_type) continue;
|
|
|
|
|
|
uint amount = min(to_spread, dest->cargo_cap - dest->cargo.TotalCount());
|
|
|
src->cargo.Shift(amount, &dest->cargo);
|
|
|
to_spread -= amount;
|
|
|
}
|
|
|
|
|
|
/* Any left-overs will be thrown away, but not their feeder share. */
|
|
|
if (src->cargo_cap < src->cargo.TotalCount()) src->cargo.Truncate(src->cargo.TotalCount() - src->cargo_cap);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Transfer cargo from a single (articulated )old vehicle to the new vehicle chain
|
|
|
* @param old_veh Old vehicle that will be sold
|
|
|
* @param new_head Head of the completely constructed new vehicle chain
|
|
|
* @param part_of_chain The vehicle is part of a train
|
|
|
* @pre You can only do this if both consists are not loading or unloading.
|
|
|
* They must not carry reserved cargo, nor cargo to be unloaded or
|