|
|
/* $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;
|
|
@@ -192,97 +191,97 @@ static bool VerifyAutoreplaceRefitForOrd
|
|
|
/**
|
|
|
* Function to find what type of cargo to refit to when autoreplacing
|
|
|
* @param *v Original vehicle that is being replaced.
|
|
|
* @param engine_type The EngineID of the vehicle that is being replaced to
|
|
|
* @param part_of_chain The vehicle is part of a train
|
|
|
* @return The cargo type to replace to
|
|
|
* CT_NO_REFIT is returned if no refit is needed
|
|
|
* CT_INVALID is returned when both old and new vehicle got cargo capacity and refitting the new one to the old one's cargo type isn't possible
|
|
|
*/
|
|
|
static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
|
|
|
{
|
|
|
CargoTypes available_cargo_types, union_mask;
|
|
|
GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types);
|
|
|
|
|
|
if (union_mask == 0) return CT_NO_REFIT; // Don't try to refit an engine with no cargo capacity
|
|
|
|
|
|
CargoID cargo_type;
|
|
|
if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) return CT_INVALID; // We cannot refit to mixed cargoes in an automated way
|
|
|
|
|
|
if (cargo_type == CT_INVALID) {
|
|
|
if (v->type != VEH_TRAIN) return CT_NO_REFIT; // If the vehicle does not carry anything at all, every replacement is fine.
|
|
|
|
|
|
if (!part_of_chain) return CT_NO_REFIT;
|
|
|
|
|
|
/* 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 */
|
|
|
|
|
|
for (v = v->First(); v != NULL; v = v->Next()) {
|
|
|
if (!v->GetEngine()->CanCarryCargo()) 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 (HasBit(available_cargo_types, v->cargo_type)) return v->cargo_type;
|
|
|
}
|
|
|
|
|
|
return CT_NO_REFIT; // We failed to find a cargo type on the old vehicle and we will not refit the new one
|
|
|
} else {
|
|
|
if (!HasBit(available_cargo_types, cargo_type)) return CT_INVALID; // We can't refit the vehicle to carry the cargo we want
|
|
|
|
|
|
if (part_of_chain && !VerifyAutoreplaceRefitForOrders(v, engine_type)) return CT_INVALID; // Some refit orders lose their effect
|
|
|
|
|
|
return cargo_type;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Get the EngineID of the replacement for a vehicle
|
|
|
* @param v The vehicle to find a replacement for
|
|
|
* @param c The vehicle's owner (it's faster to forward the pointer than refinding it)
|
|
|
* @param always_replace Always replace, even if not old.
|
|
|
* @param [out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found
|
|
|
* @param[out] e the EngineID of the replacement. INVALID_ENGINE if no replacement is found
|
|
|
* @return Error if the engine to build is not available
|
|
|
*/
|
|
|
static CommandCost GetNewEngineType(const Vehicle *v, const Company *c, bool always_replace, EngineID &e)
|
|
|
{
|
|
|
assert(v->type != VEH_TRAIN || !v->IsArticulatedPart());
|
|
|
|
|
|
e = INVALID_ENGINE;
|
|
|
|
|
|
if (v->type == VEH_TRAIN && Train::From(v)->IsRearDualheaded()) {
|
|
|
/* we build the rear ends of multiheaded trains with the front ones */
|
|
|
return CommandCost();
|
|
|
}
|
|
|
|
|
|
bool replace_when_old;
|
|
|
e = EngineReplacementForCompany(c, v->engine_type, v->group_id, &replace_when_old);
|
|
|
if (!always_replace && replace_when_old && !v->NeedsAutorenewing(c, false)) e = INVALID_ENGINE;
|
|
|
|
|
|
/* Autoreplace, if engine is available */
|
|
|
if (e != INVALID_ENGINE && IsEngineBuildable(e, v->type, _current_company)) {
|
|
|
return CommandCost();
|
|
|
}
|
|
|
|
|
|
/* Autorenew if needed */
|
|
|
if (v->NeedsAutorenewing(c)) e = v->engine_type;
|
|
|
|
|
|
/* Nothing to do or all is fine? */
|
|
|
if (e == INVALID_ENGINE || IsEngineBuildable(e, v->type, _current_company)) return CommandCost();
|
|
|
|
|
|
/* The engine we need is not available. Report error to user */
|
|
|
return CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + v->type);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Builds and refits a replacement vehicle
|
|
|
* Important: The old vehicle is still in the original vehicle chain (used for determining the cargo when the old vehicle did not carry anything, but the new one does)
|
|
|
* @param old_veh A single (articulated/multiheaded) vehicle that shall be replaced.
|
|
|
* @param new_vehicle Returns the newly build and refitted vehicle
|
|
|
* @param part_of_chain The vehicle is part of a train
|
|
|
* @return cost or error
|
|
|
*/
|
|
|
static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain)
|
|
|
{
|
|
|
*new_vehicle = NULL;
|
|
|
|
|
|
/* Shall the vehicle be replaced? */
|
|
|
const Company *c = Company::Get(_current_company);
|
|
|
EngineID e;
|
|
|
CommandCost cost = GetNewEngineType(old_veh, c, true, e);
|