|
|
|
/*
|
|
|
|
* 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 "tracerestrict.h"
|
|
|
|
#include "core/random_func.hpp"
|
|
|
|
#include "vehiclelist.h"
|
|
|
|
#include "road.h"
|
|
|
|
#include "ai/ai.hpp"
|
|
|
|
#include "news_func.h"
|
|
|
|
#include "strings_func.h"
|
|
|
|
|
|
|
|
#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
|
|
|
|
* @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 the roadtypes are compatible */
|
|
|
|
if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false;
|
|
|
|
|
|
|
|
/* 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 == nullptr || v->First() == v);
|
|
|
|
|
|
|
|
for (Vehicle *src = v; src != nullptr; 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 != nullptr && 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 = std::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
|
|
|
|
* transferred.
|
|
|
|
*/
|
|
|
|
static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chain)
|
|
|
|
{
|
|
|
|
assert(!part_of_chain || new_head->IsPrimaryVehicle());
|
|
|
|
/* Loop through source parts */
|
|
|
|
for (Vehicle *src = old_veh; src != nullptr; src = src->Next()) {
|
|
|
|
assert(src->cargo.TotalCount() == src->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
|
|
|
if (!part_of_chain && src->type == VEH_TRAIN && src != old_veh && src != Train::From(old_veh)->other_multiheaded_part && !src->IsArticulatedPart()) {
|
|
|
|
/* Skip vehicles, which do not belong to old_veh */
|
|
|
|
src = src->GetLastEnginePart();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (src->cargo_type >= NUM_CARGO || src->cargo.TotalCount() == 0) continue;
|
|
|
|
|
|
|
|
/* Find free space in the new chain */
|
|
|
|
for (Vehicle *dest = new_head; dest != nullptr && src->cargo.TotalCount() > 0; dest = dest->Next()) {
|
|
|
|
assert(dest->cargo.TotalCount() == dest->cargo.ActionCount(VehicleCargoList::MTA_KEEP));
|
|
|
|
if (!part_of_chain && dest->type == VEH_TRAIN && dest != new_head && dest != Train::From(new_head)->other_multiheaded_part && !dest->IsArticulatedPart()) {
|
|
|
|
/* Skip vehicles, which do not belong to new_head */
|
|
|
|
dest = dest->GetLastEnginePart();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (dest->cargo_type != src->cargo_type) continue;
|
|
|
|
|
|
|
|
uint amount = std::min(src->cargo.TotalCount(), dest->cargo_cap - dest->cargo.TotalCount());
|
|
|
|
if (amount <= 0) continue;
|
|
|
|
|
|
|
|
src->cargo.Shift(amount, &dest->cargo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update train weight etc., the old vehicle will be sold anyway */
|
|
|
|
if (part_of_chain && new_head->type == VEH_TRAIN) Train::From(new_head)->ConsistChanged(CCF_LOADUNLOAD);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tests whether refit orders that applied to v will also apply to the new vehicle type
|
|
|
|
* @param v The vehicle to be replaced
|
|
|
|
* @param engine_type The type we want to replace with
|
|
|
|
* @return true iff all refit orders stay valid
|
|
|
|
*/
|
|
|
|
static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_type)
|
|
|
|
{
|
|
|
|
CargoTypes union_refit_mask_a = GetUnionOfArticulatedRefitMasks(v->engine_type, false);
|
|
|
|
CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
|
|
|
|
|
|
|
|
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
|
|
|
|
for (const Order *o : u->Orders()) {
|
|
|
|
if (!o->IsRefit() || o->IsAutoRefit()) continue;
|
|
|
|
CargoID cargo_type = o->GetRefitCargo();
|
|
|
|
|
|
|
|
if (!HasBit(union_refit_mask_a, cargo_type)) continue;
|
|
|
|
if (!HasBit(union_refit_mask_b, cargo_type)) return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the index of the first refit order that is incompatible with the requested engine type
|
|
|
|
* @param v The vehicle to be replaced
|
|
|
|
* @param engine_type The type we want to replace with
|
|
|
|
* @return index of the incompatible order or -1 if none were found
|
|
|
|
*/
|
|
|
|
static int GetIncompatibleRefitOrderIdForAutoreplace(const Vehicle *v, EngineID engine_type)
|
|
|
|
{
|
|
|
|
CargoTypes union_refit_mask = GetUnionOfArticulatedRefitMasks(engine_type, false);
|
|
|
|
|
|
|
|
const Order *o;
|
|
|
|
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
|
|
|
|
|
|
|
|
const OrderList *orders = u->orders.list;
|
|
|
|
if (orders == nullptr) return -1;
|
|
|
|
for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
|
|
|
|
o = orders->GetOrderAt(i);
|
|
|
|
if (!o->IsRefit()) continue;
|
|
|
|
if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 != nullptr; 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 same_type_only Only replace with same engine type.
|
|
|
|
* @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, bool same_type_only, 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!same_type_only) {
|
|
|
|
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
|
|
|
|
* @param same_type_only Only replace with same engine type.
|
|
|
|
* @return cost or error
|
|
|
|
*/
|
|
|
|
static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehicle, bool part_of_chain, bool same_type_only)
|
|
|
|
{
|
|
|
|
*new_vehicle = nullptr;
|
|
|
|
|
|
|
|
/* Shall the vehicle be replaced? */
|
|
|
|
const Company *c = Company::Get(_current_company);
|
|
|
|
EngineID e;
|
|
|
|
CommandCost cost = GetNewEngineType(old_veh, c, true, same_type_only, e);
|
|
|
|
if (cost.Failed()) return cost;
|
|
|
|
if (e == INVALID_ENGINE) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered
|
|
|
|
|
|
|
|
/* Does it need to be refitted */
|
|
|
|
CargoID refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain);
|
|
|
|
if (refit_cargo == CT_INVALID) {
|
|
|
|
if (!IsLocalCompany()) return CommandCost();
|
|
|
|
|
|
|
|
SetDParam(0, old_veh->index);
|
|
|
|
|
|
|
|
int order_id = GetIncompatibleRefitOrderIdForAutoreplace(old_veh, e);
|
|
|
|
if (order_id != -1) {
|
|
|
|
/* Orders contained a refit order that is incompatible with the new vehicle. */
|
|
|
|
SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT);
|
|
|
|
SetDParam(2, order_id + 1); // 1-based indexing for display
|
|
|
|
} else {
|
|
|
|
/* Current cargo is incompatible with the new vehicle. */
|
|
|
|
SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO);
|
|
|
|
SetDParam(2, CargoSpec::Get(old_veh->cargo_type)->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_AUTORENEW_FAILED, old_veh->index);
|
|
|
|
return CommandCost();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Build the new vehicle */
|
|
|
|
cost = DoCommand(old_veh->tile, e | (CT_INVALID << 24), 0, DC_EXEC | DC_AUTOREPLACE, GetCmdBuildVeh(old_veh));
|
|
|
|
if (cost.Failed()) return cost;
|
|
|
|
|
|
|
|
Vehicle *new_veh = Vehicle::Get(_new_vehicle_id);
|
|
|
|
*new_vehicle = new_veh;
|
|
|
|
|
|
|
|
/* Refit the vehicle if needed */
|
|
|
|
if (refit_cargo != CT_NO_REFIT) {
|
|
|
|
byte subtype = GetBestFittingSubType(old_veh, new_veh, refit_cargo);
|
|
|
|
|
|
|
|
cost.AddCost(DoCommand(0, new_veh->index, refit_cargo | (subtype << 8), DC_EXEC, GetCmdRefitVeh(new_veh)));
|
|
|
|
assert(cost.Succeeded()); // This should be ensured by GetNewCargoTypeForReplace()
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
|
|
|
|
if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) {
|
|
|
|
DoCommand(0, new_veh->index, true, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cost;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Issue a start/stop command
|
|
|
|
* @param v a vehicle
|
|
|
|
* @param evaluate_callback shall the start/stop callback be evaluated?
|
|
|
|
* @return success or error
|
|
|
|
*/
|
|
|
|
static inline CommandCost CmdStartStopVehicle(const Vehicle *v, bool evaluate_callback)
|
|
|
|
{
|
|
|
|
return DoCommand(0, v->index, evaluate_callback ? 1 : 0, DC_EXEC | DC_AUTOREPLACE, CMD_START_STOP_VEHICLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Issue a train vehicle move command
|
|
|
|
* @param v The vehicle to move
|
|
|
|
* @param after The vehicle to insert 'v' after, or nullptr to start new chain
|
|
|
|
* @param flags the command flags to use
|
|
|
|
* @param whole_chain move all vehicles following 'v' (true), or only 'v' (false)
|
|
|
|
* @return success or error
|
|
|
|
*/
|
|
|
|
static inline CommandCost CmdMoveVehicle(const Vehicle *v, const Vehicle *after, DoCommandFlag flags, bool whole_chain)
|
|
|
|
{
|
|
|
|
return DoCommand(0, v->index | (whole_chain ? 1 : 0) << 20, after != nullptr ? after->index : INVALID_VEHICLE, flags | DC_NO_CARGO_CAP_CHECK, CMD_MOVE_RAIL_VEHICLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy head specific things to the new vehicle chain after it was successfully constructed
|
|
|
|
* @param old_head The old front vehicle (no wagons attached anymore)
|
|
|
|
* @param new_head The new head of the completely replaced vehicle chain
|
|
|
|
* @param flags the command flags to use
|
|
|
|
*/
|
Squashed commit of the following:
commit b17f39a2016dc11a6a9815f398d690d82a6a59aa
Merge: 67b3190 3bb7c47
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Feb 12 19:44:34 2016 +0100
Merge branch 'merge/trunk27506' into dev
commit 3bb7c4768580198b7316bfeebc4b870d355439e8
Merge: 14929fe 9db36bd
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Feb 12 19:43:53 2016 +0100
Merge remote-tracking branch 'openttd/master' into merge/trunk27506
commit 14929fe3536e2aa5b4d6a43d0d55043da7a2f252
Merge: af15609 4b8c698
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Feb 10 22:14:25 2016 +0100
Merge branch 'master' into merge/trunk27506
commit 67b319060b4b88b72c94b0e0c2c9fdcf1c2fd95d
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sat Feb 28 20:17:13 2015 +0100
removed 2 unused function calls
commit af15609c938eb388dd507b16fb7b6d547c54c2da
Merge: 5465c88 b251ba3
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sat Feb 28 15:12:33 2015 +0100
Merge branch 'trunk' into merge_trunk
commit 5465c88c8016c5e7910570ab5795222e8348c703
Author: me <streen01@gmx.de>
Date: Sat Feb 28 10:59:41 2015 +0100
regenerated MSVS project files
forgot to do this, they still retained the old filenames
commit 0391455e29c5ed794fcd0f58c63ff98dc52685ac
Author: Thomas Schmidt <streen01@gmx.de>
Date: Thu Feb 26 16:53:05 2015 +0100
removed the patch files from this repo again
that was a rather dum idea, it made the difference patch between
branches trunk and tbtr huge.
the patch files are now being tracked again in the supplimentary repo
'tbtr_proj', that will keep this fork clean and creating diff-patches
will be much easier
commit 8395d40386c8d620c90fb4be66cf6679408ac975
Author: Thomas Schmidt <streen01@gmx.de>
Date: Thu Feb 26 16:27:40 2015 +0100
fix for reported bug by DC-1: crash in station gui
the template gui item was added to the drop-down list that was also
shown in a station gui, but there was no action present when this item
was selected in a station gui. per default the game would commit suicide
by called NOT_REACHED() at the default case of the according
switch-statement.
commit 833873245d33bd77105a82a584d9bec2362419bc
Merge: 39596be 8688c95
Author: Thomas Schmidt <streen01@gmx.de>
Date: Thu Feb 26 15:08:53 2015 +0100
Merge branch 'fix_disableTemplateOrderCheck' into tbtr
commit 8688c95a01ed5933a35a08597bbf45ff148f5a67
Author: Thomas Schmidt <streen01@gmx.de>
Date: Thu Feb 26 15:06:25 2015 +0100
added fix by DC-1
don't check the orders list of a virtual vehicle
commit 39596beff9a815a0f9b2ea3abe5d82c3ec5933e7
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 22 10:47:58 2015 +0100
added history of patches for the mod
commit b3ae74ac4e9143202a1fda1333a91c3716ebb21e
Merge: 9a601a1 ee756e1
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 22 10:03:04 2015 +0100
Merge branch 'tbtr' into merge_tbtr
commit ee756e1c2229534f1cc05edb97269b0c83ddde66
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Feb 17 22:25:50 2015 +0100
removed nonsensical comments + disabled code
commit e7d37f0500c56c84a36ce8b93eafb31f800e1086
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Feb 17 21:30:38 2015 +0100
added some missing renames in includes
commit 63c2b13766b077e4f2923f321e95d53356dee2db
Merge: e92e6ba 9752606
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Feb 17 21:22:11 2015 +0100
Merge branch 'feat_renameFiles' into merge_renameFiles
Conflicts:
src/tbtr_template_gui_create_virtualtrain.cpp
commit 975260643d212f8cac72485f2011011210622849
Author: Thomas Schmidt <streen01@gmx.de>
Date: Mon Feb 9 22:11:18 2015 +0100
replaced source file prefix: aaa -> tbtr
commit e92e6ba7089564886d17dd5c1fd8d85ea0ca4ac7
Merge: 62d2f80 ac16eab
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 8 15:02:19 2015 +0100
Merge branch 'rm_TODOs' into dev
Conflicts:
src/aaa_template_gui_main.cpp
commit 62d2f809edf170cfbeb0599822c4c3d4f9a1fefe
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 8 14:59:36 2015 +0100
i++ -> ++i
commit ac16eabc082f62b9fe2ef6c11a314f8e9a28c26b
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 8 14:34:36 2015 +0100
rm'ed TODOs
commit 22f642f32265882b8f99b409b517823991c08101
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 8 14:17:49 2015 +0100
rm TODO
yes, depends on the selected template because the button "Start
replacing" means, to start the replacement for the currently selected
group and template (create a templatereplacement object for this
combination)
commit 60d8192838e340a3cf6899979361c997df73b716
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 8 14:17:26 2015 +0100
rm'ed TODO: included task in TODO-list
commit 39e42674ac9f5ad5dd056b613e80ef4e754c1153
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Feb 8 11:19:36 2015 +0100
changed window class in use: WC_NONE -> WC_TEMPLATE_GUI_MAIN
commit cadfac96e21aeb862b75e0454197ddce89fb728c
Author: Thomas Schmidt <streen01@gmx.de>
Date: Mon Jan 26 23:18:29 2015 +0100
removed a weird call to deleteAllTmplReplacements
was a TODO task, it was set to delete all template replacements
belonging to group with id -1, which does not exist, ever
commit dc1058464c29f61b6197dec556ec468d1ff38451
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 25 23:27:03 2015 +0100
removed some TODOs
commit 7afeb17db512600424039099a0f4bd78882fcd8e
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 25 11:35:47 2015 +0100
removed all MYGUI comments
tried to replace them with useful comments where necessary
added a few new TODOs here and there
commit 6b9453224a77811062254e6bce7dac4074b829a8
Merge: 292a5aa 687bc4c
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 18 20:47:06 2015 +0100
Merge branch 'fix_compiler_warnings' into dev
commit 687bc4c34fbb9ddeaf15b4857b235a9709dd85be
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 18 20:43:26 2015 +0100
fixed all remaining warnings
commit ada08d7097772e325b7852fd058d8bad7036ae4d
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 18 18:25:45 2015 +0100
removed testing code that produced a warning
commit f3b1568384f36998aeb1fa51c1fab4cfb96c7f93
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 18 00:07:34 2015 +0100
removed unused variable REPLACEMENT_IN_PROGRESS
commit 5aa9098880070cfaa3d2815f445497b2886933f9
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 18 00:02:43 2015 +0100
removed variable 'mode' from ClickedOnVehicle()
member function of class TemplateCreateWindow
in the depot gui the mode variable is used to decide whether a vehicle
is started or dragged or ...
here, we only drag so the mode is never used
commit 292a5aa9dba9cf1d0003e84055fb95357f922454
Merge: 8f6df8c 2bb12bc
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Jan 14 23:41:29 2015 +0100
Merge branch 'feat_mergePatch0.4c' into dev
commit 2bb12bcf283cccc8869bf537b79b22f479cb7203
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Jan 14 23:32:04 2015 +0100
added vi's .swp files to .gitignore
commit aecf6f549b32f92342f8e0b65158bebef6270537
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Jan 13 20:15:25 2015 +0100
corrected UpdateViewport code
was VehicleUpdateViewport(Vehicle*, bool) before
is Vehicle::UpdateViewport(bool, bool) now
commit ae199283fd5ac0199cef1c4c980561122d030199
Author: Thomas Schmidt <streen01@gmx.de>
Date: Mon Jan 12 22:34:22 2015 +0100
updated code for EngineNumberSorter
commit 9735035c6dd4ded9bb76958722dc25e26ced5f05
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 11 18:36:17 2015 +0100
removed unused parameter 'part_of_chain'
from cargo movement code
commit b8b86e1f2592288ddcfb46a0a5d81c3257da60d3
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Jan 4 21:44:17 2015 +0100
Reimplemented moving of cargo
- uses the new shift function
- manages to spread the old cargo of replaced vehicles from a chain across the memebers of the newly constructed chain
some TODOs are left within the code and some testing needs to be done,
how this behaves when there is more than one vehicle being replaced
commit 0d76e1bfe10ef207ac5e4018976e9fba0b0bb25e
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sat Jan 3 01:05:54 2015 +0100
fixed saveload code for TemplateVehicle
commit ba0ea6975f48fe38c2b5376ebc83c23d6bb6151c
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 11:32:23 2015 +0100
final changes for the merge
- removed the WDF_UNLICK_BUTTON
- updated ctor calls to Window()
- disabled the engine number sorter
commit 9cc213335046b3febfe6649fde40b00e1bb43d5b
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 11:29:03 2015 +0100
disabled cargo movement during templrpl
need to reimplement this step since the cargo is now moved
packet-by-packet and not as a complete list from a vehicle onto another
vehicle anymore
commit 39743806d0156f8547670c525af0e59083dbcd49
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 11:16:54 2015 +0100
replaced cargo function 'Count'
- not available anymore: VehicleCargoList::Count()
- using StoredCount() for now, should check if this is the correct count
commit 9b240bbf9b2ee5659bbcb518e9e2767103861254
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 01:27:56 2015 +0100
final corrections for template_gui_create_virtualtrain
commit cf0d48d8fa052ff521e1fac0ec75d75107c9b76e
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 01:20:30 2015 +0100
disbabled usage of not-anymore-existing newgrf_engine.h::ListPositionOfEngine
commit 81da16b7f0c3ea2417b24707329d1d971a67e82e
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 01:09:53 2015 +0100
fixed typo in value WID_BV_SORT_ASSENDING_DESCENDING
commit c8f81a5c3df5ccf4858bda64a53979af510ccd87
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 01:09:25 2015 +0100
create_virtual_train: uint GetEngineListHeight not static
commit bd29d99f80bd824e28104f3bc839fc2a5abdd297
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 00:57:25 2015 +0100
template_gui_create: static WindowDesc not const
commit edee9c1c544845459102328209b98d424cfd3248
Author: Thomas Schmidt <streen01@gmx.de>
Date: Fri Jan 2 00:44:50 2015 +0100
updated call to Window::FinishInitNested
commit 25fc3cb7ed6db15f42bd3fdff9506621fbba3d72
Author: Thomas Schmidt <streen01@gmx.de>
Date: Thu Jan 1 23:56:48 2015 +0100
updated ctor calls for classes derived from Window
- first param in the constructor used to be const WindowDesc*, now it is
WindowDesc*
commit 54d710170f1ce9cf5539cd525744ca61f4089e7b
Author: Thomas Schmidt <streen01@gmx.de>
Date: Thu Jan 1 02:50:29 2015 +0100
updated constr calls to WindowDesc::WindowDesc
need a const char* at 2nd pos now
commit 7c954141f00666dec4c9559019a1a4af3b452372
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 02:30:02 2014 +0100
applied patch vehicle.cpp
commit aa12720049a3dfb1c2e02d453813bd567b67ff60
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 02:22:54 2014 +0100
applied patch vehicle_gui.cpp (failed hunks 2,4,5/6)
commit 2b8e70f15478072264f1e063418f8de0744a98e1
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 02:13:29 2014 +0100
applied patch train_cmd.cpp (failed hunk 1/8)
commit 47499523bf1ed0cce5fdf6cc2a7102e571dcb07d
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 02:07:00 2014 +0100
applied patch newgrf_engine.cpp
commit 7a40c62a7b5ab8059981270252a7def69eacb7d7
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 02:02:52 2014 +0100
applied patch vehicle_cmd.cpp (failed hunks 2,3/3)
commit 277839abd8cb7eb277e4ed6cb72e0f3da5b7e479
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 01:56:35 2014 +0100
applied patch saveload.h (failed hunk 1/1)
commit 7b64c87ad3dede6442a88dafa5aa8a6a3e0db812
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 01:53:56 2014 +0100
applied patch group_gui.cpp (failed hunks 2,3/4)
commit 8075261c526004e21534fa0ab80429132d5f634b
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 01:46:24 2014 +0100
applied patch vehiclelist.cpp (failed hunk 1/2)
commit b1c197c0f38e45fb50dad7f7e33f1438b150a34f
Author: Thomas Schmidt <streen01@gmx.de>
Date: Wed Dec 31 01:42:04 2014 +0100
applied patch train.h (failed hunk 1/3)
commit 81bfa209e92fa74387420cc85851767d2737c1b0
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch viewport.cpp.patch (file src/viewport.cpp)
commit 5c083054544eabac9260a75033198c665b169215
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch train_gui.cpp.patch (file src/train_gui.cpp)
commit 3c3534621c6b37530035faadfa092d70fed724c9
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch source.list.patch (file source.list)
commit 6bbb071431882d4bab43023f7194f96c824e78e5
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch saveload_internal.h.patch (file src/saveload/saveload_internal.h)
commit 158640eb786cc7867c9e689eb8a92a209e528a83
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch saveload.cpp.patch (file src/saveload/saveload.cpp)
commit e171ad716c126e98bc045f5ce574ac6161f3ab4f
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch openttd_vs90.vcproj.patch (file projects/openttd_vs90.vcproj)
commit b77486d89c12a80f73f088759da760abd0af7f49
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch openttd_vs80.vcproj.patch (file projects/openttd_vs80.vcproj)
commit 57f9c52fc580da51e20bd40f116fe66c9a0f3669
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch openttd_vs100.vcxproj.patch (file projects/openttd_vs100.vcxproj)
commit bda1f739a415600a7f522b1c7f9ca53fa7713ed3
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch openttd_vs100.vcxproj.filters.patch (file projects/openttd_vs100.vcxproj.filters)
commit ed96771b03e726e5cb56cac9a8328c3a1e63856b
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch newgrf_spritegroup.cpp.patch (file src/newgrf_spritegroup.cpp)
commit 3df57e0d855fef5f54be4fd8d25e231a7eb3c3f1
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:10 2014 +0100
applied patch group_cmd.cpp.patch (file src/group_cmd.cpp)
commit da31ca4b67d6993f127c6cecac717eb286ead4e6
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch english.txt.patch (file src/lang/english.txt)
commit ddc0af7139fccbae4060c70440f17c763e3bba96
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch depot_gui.cpp.patch (file src/depot_gui.cpp)
commit 88aca9db192c6a2b92185f56505caf8b91d23ab4
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch autoreplace_cmd.cpp.patch (file src/autoreplace_cmd.cpp)
commit 45ca80f7c9847ac3afe181a0badeb12bbbd5ed0d
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch articulated_vehicles.cpp.patch (file src/articulated_vehicles.cpp)
commit 44bd0bf2e77f366b61f96b0f4ca564f2e2e5814a
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch afterload.cpp.patch (file src/saveload/afterload.cpp)
commit 679f9b327f9d3f3bec327ae0266f289981972c85
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_veh_sl.cpp.patch (file src/saveload/aaa_template_veh_sl.cpp)
commit ebcec221ec7c1988e85ba458283ff362e034e6d5
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_vehicle.h.patch (file src/aaa_template_vehicle.h)
commit ad690e74b95d2aa07157b73834eef672c63ef901
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_vehicle_func.h.patch (file src/aaa_template_vehicle_func.h)
commit 5982153c369432fc694daaa91a06dfefeeb29485
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_vehicle_func.cpp.patch (file src/aaa_template_vehicle_func.cpp)
commit 773f889e165b013de96736fa380d5ab5c311b3dd
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_vehicle.cpp.patch (file src/aaa_template_vehicle.cpp)
commit 03af781d69a09863d3b76ee4911e5eecd90a7cf5
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_replacement_sl.cpp.patch (file src/saveload/aaa_template_replacement_sl.cpp)
commit ab6cb0562fd390d551670dbc27e0c3c94c8554db
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_gui_replaceall.cpp.patch (file src/aaa_template_gui_replaceall.cpp)
commit d88452a6195c55e39bceb3ea7689fc546c4eee6a
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:43:09 2014 +0100
applied patch aaa_template_gui_main.cpp.patch (file src/aaa_template_gui_main.cpp)
commit ab6ac687f355d400ad9ebc154e75477671a8e0fa
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:42:21 2014 +0100
applied patch aaa_template_gui_create_virtualtrain.cpp.patch (file src/aaa_template_gui_create_virtualtrain.cpp)
commit 288d14b9b145cb045b6a287d23cf3be4f2712ede
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:38:12 2014 +0100
applied patch aaa_template_gui_create.cpp.patch (file src/aaa_template_gui_create.cpp)
commit 5342db70e07fb7c1f3c41654abd2c6a4c51472c4
Author: Thomas Schmidt <streen01@gmx.de>
Date: Tue Dec 30 01:18:32 2014 +0100
applied aaa_* header files
commit 6f14e94a0ad715a33a2653cf6c12e1c2981ace8d
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 17:29:55 2014 +0100
applied vehicle_base.h.patch
commit b76a5ce921fab5d81b60755ce66db71e38664e9b
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 17:29:28 2014 +0100
applied window_type.h.patch
commit d33d738c7e3477de3f12affcc74c88194a61c442
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 17:28:30 2014 +0100
applied newgrf_engine.h.patch
commit 931fd1143706bc76aa145e1430645cb4496f9f4a
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 17:27:21 2014 +0100
applied vehicle_gui.h.patch
commit f6c4ab089dad5a4a01401e18cffa8f20e02f733e
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 17:00:52 2014 +0100
applied vehicle_gui_base.h.patch
commit 5f7378136758fcc4987791d264856169950cbfe2
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 12:34:23 2014 +0100
applied build_vehicle_widget.h.patch
commit 5c6fc73847a2f34573260721bb68c7b552d546bc
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 12:01:10 2014 +0100
applied autoreplace_func.h
commit 7636f27011841d01e5f954c855dfa0cf1859e0e0
Author: Thomas Schmidt <streen01@gmx.de>
Date: Sun Dec 28 12:01:00 2014 +0100
applied newgrf.h
Remove some spurious whitespace changes, update projects files.
9 years ago
|
|
|
CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoCommandFlag flags)
|
|
|
|
{
|
|
|
|
CommandCost cost = CommandCost();
|
|
|
|
|
|
|
|
/* Share orders */
|
|
|
|
if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, new_head->index | CO_SHARE << 30, old_head->index, DC_EXEC, CMD_CLONE_ORDER));
|
|
|
|
|
|
|
|
/* Copy group membership */
|
|
|
|
if (cost.Succeeded() && old_head != new_head) cost.AddCost(DoCommand(0, old_head->group_id, new_head->index, DC_EXEC, CMD_ADD_VEHICLE_GROUP));
|
|
|
|
|
|
|
|
/* Perform start/stop check whether the new vehicle suits newgrf restrictions etc. */
|
|
|
|
if (cost.Succeeded()) {
|
|
|
|
/* Start the vehicle, might be denied by certain things */
|
|
|
|
assert((new_head->vehstatus & VS_STOPPED) != 0);
|
|
|
|
cost.AddCost(CmdStartStopVehicle(new_head, true));
|
|
|
|
|
|
|
|
/* Stop the vehicle again, but do not care about evil newgrfs allowing starting but not stopping :p */
|
|
|
|
if (cost.Succeeded()) cost.AddCost(CmdStartStopVehicle(new_head, false));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Last do those things which do never fail (resp. we do not care about), but which are not undo-able */
|
|
|
|
if (cost.Succeeded() && old_head != new_head && (flags & DC_EXEC) != 0) {
|
|
|
|
/* Copy other things which cannot be copied by a command and which shall not stay resetted from the build vehicle command */
|
|
|
|
new_head->CopyVehicleConfigAndStatistics(old_head);
|
|
|
|
|
|
|
|
/* Switch vehicle windows/news to the new vehicle, so they are not closed/deleted when the old vehicle is sold */
|
|
|
|
ChangeVehicleViewports(old_head->index, new_head->index);
|
|
|
|
ChangeVehicleViewWindow(old_head->index, new_head->index);
|
|
|
|
ChangeVehicleNews(old_head->index, new_head->index);
|
|
|
|
|
|
|
|
if (old_head->type == VEH_TRAIN) {
|
|
|
|
Train::From(new_head)->speed_restriction = Train::From(old_head)->speed_restriction;
|
|
|
|
/* Transfer any acquired trace restrict slots to the new vehicle */
|
|
|
|
if (HasBit(Train::From(old_head)->flags, VRF_HAVE_SLOT)) {
|
|
|
|
TraceRestrictTransferVehicleOccupantInAllSlots(old_head->index, new_head->index);
|
|
|
|
ClrBit(Train::From(old_head)->flags, VRF_HAVE_SLOT);
|
|
|
|
SetBit(Train::From(new_head)->flags, VRF_HAVE_SLOT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cost;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replace a single unit in a free wagon chain
|
|
|
|
* @param single_unit vehicle to let autoreplace/renew operator on
|
|
|
|
* @param flags command flags
|
|
|
|
* @param nothing_to_do is set to 'false' when something was done (only valid when not failed)
|
|
|
|
* @param same_type_only Only replace with same engine type.
|
|
|
|
* @return cost or error
|
|
|
|
*/
|
|
|
|
static CommandCost ReplaceFreeUnit(Vehicle **single_unit, DoCommandFlag flags, bool *nothing_to_do, bool same_type_only)
|
|
|
|
{
|
|
|
|
Train *old_v = Train::From(*single_unit);
|
|
|
|
assert(!old_v->IsArticulatedPart() && !old_v->IsRearDualheaded());
|
|
|
|
|
|
|
|
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0);
|
|
|
|
|
|
|
|
/* Build and refit replacement vehicle */
|
|
|
|
Vehicle *new_v = nullptr;
|
|
|
|
cost.AddCost(BuildReplacementVehicle(old_v, &new_v, false, same_type_only));
|
|
|
|
|
|
|
|
/* Was a new vehicle constructed? */
|
|
|
|
if (cost.Succeeded() && new_v != nullptr) {
|
|
|
|
*nothing_to_do = false;
|
|
|
|
|
|
|
|
if ((flags & DC_EXEC) != 0) {
|
|
|
|
/* Move the new vehicle behind the old */
|
|
|
|
CmdMoveVehicle(new_v, old_v, DC_EXEC, false);
|
|
|
|
|
|
|
|
/* Take over cargo
|
|
|
|
* Note: We do only transfer cargo from the old to the new vehicle.
|
|
|
|
* I.e. we do not transfer remaining cargo to other vehicles.
|
|
|
|
* Else you would also need to consider moving cargo to other free chains,
|
|
|
|
* or doing the same in ReplaceChain(), which would be quite troublesome.
|
|
|
|
*/
|
|
|
|
TransferCargo(old_v, new_v, false);
|
|
|
|
|
|
|
|
*single_unit = new_v;
|
|
|
|
|
|
|
|
AI::NewEvent(old_v->owner, new ScriptEventVehicleAutoReplaced(old_v->index, new_v->index));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sell the old vehicle */
|
|
|
|
cost.AddCost(DoCommand(0, old_v->index, 0, flags, GetCmdSellVeh(old_v)));
|
|
|
|
|
|
|
|
/* If we are not in DC_EXEC undo everything */
|
|
|
|
if ((flags & DC_EXEC) == 0) {
|
|
|
|
DoCommand(0, new_v->index, 0, DC_EXEC, GetCmdSellVeh(new_v));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cost;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replace a whole vehicle chain
|
|
|
|
* @param chain vehicle chain to let autoreplace/renew operator on
|
|
|
|
* @param flags command flags
|
|
|
|
* @param wagon_removal remove wagons when the resulting chain occupies more tiles than the old did
|
|
|
|
* @param nothing_to_do is set to 'false' when something was done (only valid when not failed)
|
|
|
|
* @param same_type_only Only replace with same engine type.
|
|
|
|
* @return cost or error
|
|
|
|
*/
|
|
|
|
static CommandCost ReplaceChain(Vehicle **chain, DoCommandFlag flags, bool wagon_removal, bool *nothing_to_do, bool same_type_only)
|
|
|
|
{
|
|
|
|
Vehicle *old_head = *chain;
|
|
|
|
assert(old_head->IsPrimaryVehicle());
|
|
|
|
|
|
|
|
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0);
|
|
|
|
|
|
|
|
if (old_head->type == VEH_TRAIN) {
|
|
|
|
/* Store the length of the old vehicle chain, rounded up to whole tiles */
|
|
|
|
uint16 old_total_length = CeilDiv(Train::From(old_head)->gcache.cached_total_length, TILE_SIZE) * TILE_SIZE;
|
|
|
|
|
|
|
|
int num_units = 0; ///< Number of units in the chain
|
|
|
|
for (Train *w = Train::From(old_head); w != nullptr; w = w->GetNextUnit()) num_units++;
|
|
|
|
|
|
|
|
Train **old_vehs = CallocT<Train *>(num_units); ///< Will store vehicles of the old chain in their order
|
|
|
|
Train **new_vehs = CallocT<Train *>(num_units); ///< New vehicles corresponding to old_vehs or nullptr if no replacement
|
|
|
|
Money *new_costs = MallocT<Money>(num_units); ///< Costs for buying and refitting the new vehicles
|
|
|
|
|
|
|
|
/* Collect vehicles and build replacements
|
|
|
|
* Note: The replacement vehicles can only successfully build as long as the old vehicles are still in their chain */
|
|
|
|
int i;
|
|
|
|
Train *w;
|
|
|
|
for (w = Train::From(old_head), i = 0; w != nullptr; w = w->GetNextUnit(), i++) {
|
|
|
|
assert(i < num_units);
|
|
|
|
old_vehs[i] = w;
|
|
|
|
|
|
|
|
CommandCost ret = BuildReplacementVehicle(old_vehs[i], (Vehicle**)&new_vehs[i], true, same_type_only);
|
|
|
|
cost.AddCost(ret);
|
|
|
|
if (cost.Failed()) break;
|
|
|
|
|
|
|
|
new_costs[i] = ret.GetCost();
|
|
|
|
if (new_vehs[i] != nullptr) *nothing_to_do = false;
|
|
|
|
}
|
|
|
|
Train *new_head = (new_vehs[0] != nullptr ? new_vehs[0] : old_vehs[0]);
|
|
|
|
|
|
|
|
/* Note: When autoreplace has already failed here, old_vehs[] is not completely initialized. But it is also not needed. */
|
|
|
|
if (cost.Succeeded()) {
|
|
|
|
/* Separate the head, so we can start constructing the new chain */
|
|
|
|
Train *second = Train::From(old_head)->GetNextUnit();
|
|
|
|
if (second != nullptr) cost.AddCost(CmdMoveVehicle(second, nullptr, DC_EXEC | DC_AUTOREPLACE, true));
|
|
|
|
|
|
|
|
assert(Train::From(new_head)->GetNextUnit() == nullptr);
|
|
|
|
|
|
|
|
/* Append engines to the new chain
|
|
|
|
* We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
|
|
|
|
* That way we also have less trouble when exceeding the unitnumber limit.
|
|
|
|
* OTOH the vehicle attach callback is more expensive this way :s */
|
|
|
|
Train *last_engine = nullptr; ///< Shall store the last engine unit after this step
|
|
|
|
if (cost.Succeeded()) {
|
|
|
|
for (int i = num_units - 1; i > 0; i--) {
|
|
|
|
Train *append = (new_vehs[i] != nullptr ? new_vehs[i] : old_vehs[i]);
|
|
|
|
|
|
|
|
if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) continue;
|
|
|
|
|
|
|
|
if (new_vehs[i] != nullptr) {
|
|
|
|
/* Move the old engine to a separate row with DC_AUTOREPLACE. Else
|
|
|
|
* moving the wagon in front may fail later due to unitnumber limit.
|
|
|
|
* (We have to attach wagons without DC_AUTOREPLACE.) */
|
|
|
|
CmdMoveVehicle(old_vehs[i], nullptr, DC_EXEC | DC_AUTOREPLACE, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_engine == nullptr) last_engine = append;
|
|
|
|
cost.AddCost(CmdMoveVehicle(append, new_head, DC_EXEC, false));
|
|
|
|
if (cost.Failed()) break;
|
|
|
|
}
|
|
|
|
if (last_engine == nullptr) last_engine = new_head;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* When wagon removal is enabled and the new engines without any wagons are already longer than the old, we have to fail */
|
|
|
|
if (cost.Succeeded() && wagon_removal && new_head->gcache.cached_total_length > old_total_length) cost = CommandCost(STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT);
|
|
|
|
|
|
|
|
/* Append/insert wagons into the new vehicle chain
|
|
|
|
* We do this from back to front, so we can stop when wagon removal or maximum train length (i.e. from mammoth-train setting) is triggered.
|
|
|
|
*/
|
|
|
|
if (cost.Succeeded()) {
|
|
|
|
for (int i = num_units - 1; i > 0; i--) {
|
|
|
|
assert(last_engine != nullptr);
|
|
|
|
Vehicle *append = (new_vehs[i] != nullptr ? new_vehs[i] : old_vehs[i]);
|
|
|
|
|
|
|
|
if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) {
|
|
|
|
/* Insert wagon after 'last_engine' */
|
|
|
|
CommandCost res = CmdMoveVehicle(append, last_engine, DC_EXEC, false);
|
|
|
|
|
|
|
|
/* When we allow removal of wagons, either the move failing due
|
|
|
|
* to the train becoming too long, or the train becoming longer
|
|
|
|
* would move the vehicle to the empty vehicle chain. */
|
|
|
|
if (wagon_removal && (res.Failed() ? res.GetErrorMessage() == STR_ERROR_TRAIN_TOO_LONG : new_head->gcache.cached_total_length > old_total_length)) {
|
|
|
|
CmdMoveVehicle(append, nullptr, DC_EXEC | DC_AUTOREPLACE, false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cost.AddCost(res);
|
|
|
|
if (cost.Failed()) break;
|
|
|
|
} else {
|
|
|
|
/* We have reached 'last_engine', continue with the next engine towards the front */
|
|
|
|
assert(append == last_engine);
|
|
|
|
last_engine = last_engine->GetPrevUnit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sell superfluous new vehicles that could not be inserted. */
|
|
|
|
if (cost.Succeeded() && wagon_removal) {
|
|
|
|
assert(new_head->gcache.cached_total_length <= _settings_game.vehicle.max_train_length * TILE_SIZE);
|
|
|
|
for (int i = 1; i < num_units; i++) {
|
|
|
|
Vehicle *wagon = new_vehs[i];
|
|
|
|
if (wagon == nullptr) continue;
|
|
|
|
if (wagon->First() == new_head) break;
|
|
|
|
|
|
|
|
assert(RailVehInfo(wagon->engine_type)->railveh_type == RAILVEH_WAGON);
|
|
|
|
|
|
|
|
/* Sell wagon */
|
|
|
|
[[maybe_unused]] CommandCost ret = DoCommand(0, wagon->index, 0, DC_EXEC, GetCmdSellVeh(wagon));
|
|
|
|
assert(ret.Succeeded());
|
|
|
|
new_vehs[i] = nullptr;
|
|
|
|
|
|
|
|
/* Revert the money subtraction when the vehicle was built.
|
|
|
|
* This value is different from the sell value, esp. because of refitting */
|
|
|
|
cost.AddCost(-new_costs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The new vehicle chain is constructed, now take over orders and everything... */
|
|
|
|
if (cost.Succeeded()) cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
|
|
|
|
|
|
|
|
if (cost.Succeeded()) {
|
|
|
|
/* Success ! */
|
|
|
|
if ((flags & DC_EXEC) != 0 && new_head != old_head) {
|
|
|
|
*chain = new_head;
|
|
|
|
AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transfer cargo of old vehicles and sell them */
|
|
|
|
for (int i = 0; i < num_units; i++) {
|
|
|
|
Vehicle *w = old_vehs[i];
|
|
|
|
/* Is the vehicle again part of the new chain?
|
|
|
|
* Note: We cannot test 'new_vehs[i] != nullptr' as wagon removal might cause to remove both */
|
|
|
|
if (w->First() == new_head) continue;
|
|
|
|
|
|
|
|
if ((flags & DC_EXEC) != 0) TransferCargo(w, new_head, true);
|
|
|
|
|
|
|
|
if ((flags & DC_EXEC) != 0) {
|
|
|
|
old_vehs[i] = nullptr;
|
|
|
|
if (i == 0) old_head = nullptr;
|
|
|
|
}
|
|
|
|
/* Sell the vehicle.
|
|
|
|
* Note: This might temporarily construct new trains, so use DC_AUTOREPLACE to prevent
|
|
|
|
* it from failing due to engine limits. */
|
|
|
|
cost.AddCost(DoCommand(0, w->index, 0, flags | DC_AUTOREPLACE, GetCmdSellVeh(w)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & DC_EXEC) != 0) CheckCargoCapacity(new_head);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles.
|
|
|
|
* We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
|
|
|
|
* Note: The vehicle attach callback is disabled here :) */
|
|
|
|
if ((flags & DC_EXEC) == 0) {
|
|
|
|
/* Separate the head, so we can reattach the old vehicles */
|
|
|
|
Train *second = Train::From(old_head)->GetNextUnit();
|
|
|
|
if (second != nullptr) CmdMoveVehicle(second, nullptr, DC_EXEC | DC_AUTOREPLACE, true);
|
|
|
|
|
|
|
|
assert(Train::From(old_head)->GetNextUnit() == nullptr);
|
|
|
|
|
|
|
|
for (int i = num_units - 1; i > 0; i--) {
|
|
|
|
[[maybe_unused]] CommandCost ret = CmdMoveVehicle(old_vehs[i], old_head, DC_EXEC | DC_AUTOREPLACE, false);
|
|
|
|
assert(ret.Succeeded());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finally undo buying of new vehicles */
|
|
|
|
if ((flags & DC_EXEC) == 0) {
|
|
|
|
for (int i = num_units - 1; i >= 0; i--) {
|
|
|
|
if (new_vehs[i] != nullptr) {
|
|
|
|
DoCommand(0, new_vehs[i]->index, 0, DC_EXEC, GetCmdSellVeh(new_vehs[i]));
|
|
|
|
new_vehs[i] = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(old_vehs);
|
|
|
|
free(new_vehs);
|
|
|
|
free(new_costs);
|
|
|
|
} else {
|
|
|
|
/* Build and refit replacement vehicle */
|
|
|
|
Vehicle *new_head = nullptr;
|
|
|
|
cost.AddCost(BuildReplacementVehicle(old_head, &new_head, true, same_type_only));
|
|
|
|
|
|
|
|
/* Was a new vehicle constructed? */
|
|
|
|
if (cost.Succeeded() && new_head != nullptr) {
|
|
|
|
*nothing_to_do = false;
|
|
|
|
|
|
|
|
/* The new vehicle is constructed, now take over orders and everything... */
|
|
|
|
cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
|
|
|
|
|
|
|
|
if (cost.Succeeded()) {
|
|
|
|
/* The new vehicle is constructed, now take over cargo */
|
|
|
|
if ((flags & DC_EXEC) != 0) {
|
|
|
|
TransferCargo(old_head, new_head, true);
|
|
|
|
*chain = new_head;
|
|
|
|
|
|
|
|
AI::NewEvent(old_head->owner, new ScriptEventVehicleAutoReplaced(old_head->index, new_head->index));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sell the old vehicle */
|
|
|
|
cost.AddCost(DoCommand(0, old_head->index, 0, flags, GetCmdSellVeh(old_head)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we are not in DC_EXEC undo everything */
|
|
|
|
if ((flags & DC_EXEC) == 0) {
|
|
|
|
DoCommand(0, new_head->index, 0, DC_EXEC, GetCmdSellVeh(new_head));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cost;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Autoreplaces a vehicle
|
|
|
|
* Trains are replaced as a whole chain, free wagons in depot are replaced on their own
|
|
|
|
* @param tile not used
|
|
|
|
* @param flags type of operation
|
|
|
|
* @param p1 Index of vehicle
|
|
|
|
* @param p2 packed data
|
|
|
|
* - bit 0 = Autoreplace with same type only
|
|
|
|
* @param text unused
|
|
|
|
* @return the cost of this operation or an error
|
|
|
|
*/
|
|
|
|
CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
|
|
|
{
|
|
|
|
Vehicle *v = Vehicle::GetIfValid(p1);
|
|
|
|
if (v == nullptr) return CMD_ERROR;
|
|
|
|
|
|
|
|
CommandCost ret = CheckOwnership(v->owner);
|
|
|
|
if (ret.Failed()) return ret;
|
|
|
|
|
|
|
|
if (!v->IsChainInDepot()) return CMD_ERROR;
|
|
|
|
if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
|
|
|
|
|
|
|
|
bool free_wagon = false;
|
|
|
|
if (v->type == VEH_TRAIN) {
|
|
|
|
Train *t = Train::From(v);
|
|
|
|
if (t->IsArticulatedPart() || t->IsRearDualheaded()) return CMD_ERROR;
|
|
|
|
free_wagon = !t->IsFrontEngine();
|
|
|
|
if (free_wagon && t->First()->IsFrontEngine()) return CMD_ERROR;
|
|
|
|
} else {
|
|
|
|
if (!v->IsPrimaryVehicle()) return CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Company *c = Company::Get(_current_company);
|
|
|
|
bool wagon_removal = c->settings.renew_keep_length;
|
|
|
|
bool same_type_only = HasBit(p2, 0);
|
|
|
|
|
|
|
|
const Group *g = Group::GetIfValid(v->group_id);
|
|
|
|
if (g != nullptr) wagon_removal = HasBit(g->flags, GroupFlags::GF_REPLACE_WAGON_REMOVAL);
|
|
|
|
|
|
|
|
/* Test whether any replacement is set, before issuing a whole lot of commands that would end in nothing changed */
|
|
|
|
Vehicle *w = v;
|
|
|
|
bool any_replacements = false;
|
|
|
|
while (w != nullptr) {
|
|
|
|
EngineID e;
|
|
|
|
CommandCost cost = GetNewEngineType(w, c, false, same_type_only, e);
|
|
|
|
if (cost.Failed()) return cost;
|
|
|
|
any_replacements |= (e != INVALID_ENGINE);
|
|
|
|
w = (!free_wagon && w->type == VEH_TRAIN ? Train::From(w)->GetNextUnit() : nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES, 0);
|
|
|
|
bool nothing_to_do = true;
|
|
|
|
|
|
|
|
if (any_replacements) {
|
|
|
|
bool was_stopped = free_wagon || ((v->vehstatus & VS_STOPPED) != 0);
|
|
|
|
|
|
|
|
/* Stop the vehicle */
|
|
|
|
if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, true));
|
|
|
|
if (cost.Failed()) return cost;
|
|
|
|
|
|
|
|
assert(free_wagon || v->IsStoppedInDepot());
|
|
|
|
|
|
|
|
/* We have to construct the new vehicle chain to test whether it is valid.
|
|
|
|
* Vehicle construction needs random bits, so we have to save the random seeds
|
|
|
|
* to prevent desyncs and to replay newgrf callbacks during DC_EXEC */
|
|
|
|
SavedRandomSeeds saved_seeds;
|
|
|
|
SaveRandomSeeds(&saved_seeds);
|
|
|
|
if (free_wagon) {
|
|
|
|
cost.AddCost(ReplaceFreeUnit(&v, flags & ~DC_EXEC, ¬hing_to_do, same_type_only));
|
|
|
|
} else {
|
|
|
|
cost.AddCost(ReplaceChain(&v, flags & ~DC_EXEC, wagon_removal, ¬hing_to_do, same_type_only));
|
|
|
|
}
|
|
|
|
RestoreRandomSeeds(saved_seeds);
|
|
|
|
|
|
|
|
if (cost.Succeeded() && (flags & DC_EXEC) != 0) {
|
|
|
|
CommandCost ret;
|
|
|
|
if (free_wagon) {
|
|
|
|
ret = ReplaceFreeUnit(&v, flags, ¬hing_to_do, same_type_only);
|
|
|
|
} else {
|
|
|
|
ret = ReplaceChain(&v, flags, wagon_removal, ¬hing_to_do, same_type_only);
|
|
|
|
}
|
|
|
|
assert(ret.Succeeded() && ret.GetCost() == cost.GetCost());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Restart the vehicle */
|
|
|
|
if (!was_stopped) cost.AddCost(CmdStartStopVehicle(v, false));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cost.Succeeded() && nothing_to_do) cost = CommandCost(STR_ERROR_AUTOREPLACE_NOTHING_TO_DO);
|
|
|
|
return cost;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Change engine renewal parameters
|
|
|
|
* @param tile unused
|
|
|
|
* @param flags operation to perform
|
|
|
|
* @param p1 packed data
|
|
|
|
* - bit 0 = replace when engine gets old?
|
|
|
|
* - bits 16-31 = engine group
|
|
|
|
* @param p2 packed data
|
|
|
|
* - bits 0-15 = old engine type
|
|
|
|
* - bits 16-31 = new engine type
|
|
|
|
* @param text unused
|
|
|
|
* @return the cost of this operation or an error
|
|
|
|
*/
|
|
|
|
CommandCost CmdSetAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
|
|
|
{
|
|
|
|
Company *c = Company::GetIfValid(_current_company);
|
|
|
|
if (c == nullptr) return CMD_ERROR;
|
|
|
|
|
|
|
|
EngineID old_engine_type = GB(p2, 0, 16);
|
|
|
|
EngineID new_engine_type = GB(p2, 16, 16);
|
|
|
|
GroupID id_g = GB(p1, 16, 16);
|
|
|
|
CommandCost cost;
|
|
|
|
|
|
|
|
if (Group::IsValidID(id_g) ? Group::Get(id_g)->owner != _current_company : !IsAllGroupID(id_g) && !IsDefaultGroupID(id_g)) return CMD_ERROR;
|
|
|
|
if (!Engine::IsValidID(old_engine_type)) return CMD_ERROR;
|
|
|
|
|
|
|
|
if (new_engine_type != INVALID_ENGINE) {
|
|
|
|
if (!Engine::IsValidID(new_engine_type)) return CMD_ERROR;
|
|
|
|
if (!CheckAutoreplaceValidity(old_engine_type, new_engine_type, _current_company)) return CMD_ERROR;
|
|
|
|
|
|
|
|
cost = AddEngineReplacementForCompany(c, old_engine_type, new_engine_type, id_g, HasBit(p1, 0), flags);
|
|
|
|
} else {
|
|
|
|
cost = RemoveEngineReplacementForCompany(c, old_engine_type, id_g, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
|
GroupStatistics::UpdateAutoreplace(_current_company);
|
|
|
|
if (IsLocalCompany()) SetWindowDirty(WC_REPLACE_VEHICLE, Engine::Get(old_engine_type)->type);
|
|
|
|
|
|
|
|
const VehicleType vt = Engine::Get(old_engine_type)->type;
|
|
|
|
SetWindowDirty(GetWindowClassForVehicleType(vt), VehicleListIdentifier(VL_GROUP_LIST, vt, _current_company).Pack());
|
|
|
|
}
|
|
|
|
if ((flags & DC_EXEC) && IsLocalCompany()) InvalidateAutoreplaceWindow(old_engine_type, id_g);
|
|
|
|
|
|
|
|
return cost;
|
|
|
|
}
|
|
|
|
|