File diff r23022:731ae1300799 → r23023:7b8669afd1db
src/autoreplace_cmd.cpp
Show inline comments
 
/* $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);