mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-12 10:30:28 +00:00
There are situations that aren't solvable (because of different language files), so if the user really wants to have duplicated name, allow him to do so. It solves desyncs between server and clients using different languages. It behaves the same in SP and MP, so users won't see the different behaviour as a bug (and even checking in SP could be worked around by the user).
631 lines
17 KiB
C++
631 lines
17 KiB
C++
/* $Id$ */
|
|
|
|
/** @file engine.cpp Base for all engine handling. */
|
|
|
|
#include "stdafx.h"
|
|
#include "openttd.h"
|
|
#include "debug.h"
|
|
#include "company_base.h"
|
|
#include "company_func.h"
|
|
#include "command_func.h"
|
|
#include "news_func.h"
|
|
#include "variables.h"
|
|
#include "train.h"
|
|
#include "aircraft.h"
|
|
#include "newgrf_cargo.h"
|
|
#include "newgrf_engine.h"
|
|
#include "group.h"
|
|
#include "strings_func.h"
|
|
#include "gfx_func.h"
|
|
#include "functions.h"
|
|
#include "window_func.h"
|
|
#include "date_func.h"
|
|
#include "autoreplace_gui.h"
|
|
#include "string_func.h"
|
|
#include "settings_type.h"
|
|
#include "oldpool_func.h"
|
|
#include "core/alloc_func.hpp"
|
|
#include "vehicle_func.h"
|
|
|
|
#include "table/strings.h"
|
|
#include "table/engines.h"
|
|
|
|
DEFINE_OLD_POOL_GENERIC(Engine, Engine)
|
|
|
|
/** Year that engine aging stops. Engines will not reduce in reliability
|
|
* and no more engines will be introduced */
|
|
Year _year_engine_aging_stops;
|
|
|
|
/** Number of engines of each vehicle type in original engine data */
|
|
const uint8 _engine_counts[4] = {
|
|
lengthof(_orig_rail_vehicle_info),
|
|
lengthof(_orig_road_vehicle_info),
|
|
lengthof(_orig_ship_vehicle_info),
|
|
lengthof(_orig_aircraft_vehicle_info),
|
|
};
|
|
|
|
/** Offset of the first engine of each vehicle type in original engine data */
|
|
const uint8 _engine_offsets[4] = {
|
|
0,
|
|
lengthof(_orig_rail_vehicle_info),
|
|
lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info),
|
|
lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info),
|
|
};
|
|
|
|
Engine::Engine() :
|
|
name(NULL),
|
|
overrides_count(0),
|
|
overrides(NULL)
|
|
{
|
|
}
|
|
|
|
Engine::Engine(VehicleType type, EngineID base)
|
|
{
|
|
this->type = type;
|
|
this->internal_id = base;
|
|
this->list_position = base;
|
|
|
|
/* Check if this base engine is within the original engine data range */
|
|
if (base >= _engine_counts[type]) {
|
|
/* Mark engine as valid anyway */
|
|
this->info.climates = 0x80;
|
|
/* Set model life to maximum to make wagons available */
|
|
this->info.base_life = 0xFF;
|
|
return;
|
|
}
|
|
|
|
/* Copy the original engine info for this slot */
|
|
this->info = _orig_engine_info[_engine_offsets[type] + base];
|
|
|
|
/* Copy the original engine data for this slot */
|
|
switch (type) {
|
|
default: NOT_REACHED();
|
|
|
|
case VEH_TRAIN:
|
|
this->u.rail = _orig_rail_vehicle_info[base];
|
|
this->image_index = this->u.rail.image_index;
|
|
this->info.string_id = STR_8000_KIRBY_PAUL_TANK_STEAM + base;
|
|
|
|
/* Set the default model life of original wagons to "infinite" */
|
|
if (this->u.rail.railveh_type == RAILVEH_WAGON) this->info.base_life = 0xFF;
|
|
|
|
break;
|
|
|
|
case VEH_ROAD:
|
|
this->u.road = _orig_road_vehicle_info[base];
|
|
this->image_index = this->u.road.image_index;
|
|
this->info.string_id = STR_8074_MPS_REGAL_BUS + base;
|
|
break;
|
|
|
|
case VEH_SHIP:
|
|
this->u.ship = _orig_ship_vehicle_info[base];
|
|
this->image_index = this->u.ship.image_index;
|
|
this->info.string_id = STR_80CC_MPS_OIL_TANKER + base;
|
|
break;
|
|
|
|
case VEH_AIRCRAFT:
|
|
this->u.air = _orig_aircraft_vehicle_info[base];
|
|
this->image_index = this->u.air.image_index;
|
|
this->info.string_id = STR_80D7_SAMPSON_U52 + base;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Engine::~Engine()
|
|
{
|
|
UnloadWagonOverrides(this);
|
|
free(this->name);
|
|
}
|
|
|
|
/** Sets cached values in Company::num_vehicles and Group::num_vehicles
|
|
*/
|
|
void SetCachedEngineCounts()
|
|
{
|
|
uint engines = GetEnginePoolSize();
|
|
|
|
/* Set up the engine count for all companies */
|
|
Company *c;
|
|
FOR_ALL_COMPANIES(c) {
|
|
free(c->num_engines);
|
|
c->num_engines = CallocT<EngineID>(engines);
|
|
}
|
|
|
|
/* Recalculate */
|
|
Group *g;
|
|
FOR_ALL_GROUPS(g) {
|
|
free(g->num_engines);
|
|
g->num_engines = CallocT<EngineID>(engines);
|
|
}
|
|
|
|
const Vehicle *v;
|
|
FOR_ALL_VEHICLES(v) {
|
|
if (!IsEngineCountable(v)) continue;
|
|
|
|
assert(v->engine_type < engines);
|
|
|
|
GetCompany(v->owner)->num_engines[v->engine_type]++;
|
|
|
|
if (v->group_id == DEFAULT_GROUP) continue;
|
|
|
|
g = GetGroup(v->group_id);
|
|
assert(v->type == g->vehicle_type);
|
|
assert(v->owner == g->owner);
|
|
|
|
g->num_engines[v->engine_type]++;
|
|
}
|
|
}
|
|
|
|
void SetupEngines()
|
|
{
|
|
_Engine_pool.CleanPool();
|
|
_Engine_pool.AddBlockToPool();
|
|
|
|
for (uint i = 0; i < lengthof(_orig_rail_vehicle_info); i++) new Engine(VEH_TRAIN, i);
|
|
for (uint i = 0; i < lengthof(_orig_road_vehicle_info); i++) new Engine(VEH_ROAD, i);
|
|
for (uint i = 0; i < lengthof(_orig_ship_vehicle_info); i++) new Engine(VEH_SHIP, i);
|
|
for (uint i = 0; i < lengthof(_orig_aircraft_vehicle_info); i++) new Engine(VEH_AIRCRAFT, i);
|
|
}
|
|
|
|
|
|
void ShowEnginePreviewWindow(EngineID engine);
|
|
|
|
void DeleteCustomEngineNames()
|
|
{
|
|
Engine *e;
|
|
FOR_ALL_ENGINES(e) {
|
|
free(e->name);
|
|
e->name = NULL;
|
|
}
|
|
|
|
_vehicle_design_names &= ~1;
|
|
}
|
|
|
|
void LoadCustomEngineNames()
|
|
{
|
|
/* XXX: not done */
|
|
DEBUG(misc, 1, "LoadCustomEngineNames: not done");
|
|
}
|
|
|
|
/* Determine if an engine type is a wagon (and not a loco) */
|
|
static bool IsWagon(EngineID index)
|
|
{
|
|
const Engine *e = GetEngine(index);
|
|
return e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON;
|
|
}
|
|
|
|
static void CalcEngineReliability(Engine *e)
|
|
{
|
|
uint age = e->age;
|
|
|
|
/* Check for early retirement */
|
|
if (e->company_avail != 0 && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
|
|
int retire_early = e->info.retire_early;
|
|
uint retire_early_max_age = max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
|
|
if (retire_early != 0 && age >= retire_early_max_age) {
|
|
/* Early retirement is enabled and we're past the date... */
|
|
e->company_avail = 0;
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
|
}
|
|
}
|
|
|
|
if (age < e->duration_phase_1) {
|
|
uint start = e->reliability_start;
|
|
e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
|
|
} else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
|
|
/* We are at the peak of this engines life. It will have max reliability.
|
|
* This is also true if the engines never expire. They will not go bad over time */
|
|
e->reliability = e->reliability_max;
|
|
} else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
|
|
uint max = e->reliability_max;
|
|
e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
|
|
} else {
|
|
/* time's up for this engine.
|
|
* We will now completely retire this design */
|
|
e->company_avail = 0;
|
|
e->reliability = e->reliability_final;
|
|
/* Kick this engine out of the lists */
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
|
}
|
|
InvalidateWindowClasses(WC_BUILD_VEHICLE); // Update to show the new reliability
|
|
InvalidateWindowClasses(WC_REPLACE_VEHICLE);
|
|
}
|
|
|
|
void SetYearEngineAgingStops()
|
|
{
|
|
/* Determine last engine aging year, default to 2050 as previously. */
|
|
_year_engine_aging_stops = 2050;
|
|
|
|
const Engine *e;
|
|
FOR_ALL_ENGINES(e) {
|
|
const EngineInfo *ei = &e->info;
|
|
|
|
/* Exclude certain engines */
|
|
if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) continue;
|
|
if (e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON) continue;
|
|
|
|
/* Base year ending date on half the model life */
|
|
YearMonthDay ymd;
|
|
ConvertDateToYMD(ei->base_intro + (ei->lifelength * 366) / 2, &ymd);
|
|
|
|
_year_engine_aging_stops = max(_year_engine_aging_stops, ymd.year);
|
|
}
|
|
}
|
|
|
|
void StartupEngines()
|
|
{
|
|
Engine *e;
|
|
/* Aging of vehicles stops, so account for that when starting late */
|
|
const Date aging_date = min(_date, ConvertYMDToDate(_year_engine_aging_stops, 0, 1));
|
|
|
|
FOR_ALL_ENGINES(e) {
|
|
const EngineInfo *ei = &e->info;
|
|
uint32 r;
|
|
|
|
e->age = 0;
|
|
e->flags = 0;
|
|
e->company_avail = 0;
|
|
|
|
/* The magic value of 729 days below comes from the NewGRF spec. If the
|
|
* base intro date is before 1922 then the random number of days is not
|
|
* added. */
|
|
r = Random();
|
|
e->intro_date = ei->base_intro <= ConvertYMDToDate(1922, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
|
|
if (e->intro_date <= _date) {
|
|
e->age = (aging_date - e->intro_date) >> 5;
|
|
e->company_avail = (CompanyMask)-1;
|
|
e->flags |= ENGINE_AVAILABLE;
|
|
}
|
|
|
|
e->reliability_start = GB(r, 16, 14) + 0x7AE0;
|
|
r = Random();
|
|
e->reliability_max = GB(r, 0, 14) + 0xBFFF;
|
|
e->reliability_final = GB(r, 16, 14) + 0x3FFF;
|
|
|
|
r = Random();
|
|
e->duration_phase_1 = GB(r, 0, 5) + 7;
|
|
e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
|
|
e->duration_phase_3 = GB(r, 9, 7) + 120;
|
|
|
|
e->reliability_spd_dec = ei->decay_speed << 2;
|
|
|
|
CalcEngineReliability(e);
|
|
|
|
e->lifelength = ei->lifelength + _settings_game.vehicle.extend_vehicle_life;
|
|
|
|
/* prevent certain engines from ever appearing. */
|
|
if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) {
|
|
e->flags |= ENGINE_AVAILABLE;
|
|
e->company_avail = 0;
|
|
}
|
|
}
|
|
|
|
/* Update the bitmasks for the vehicle lists */
|
|
Company *c;
|
|
FOR_ALL_COMPANIES(c) {
|
|
c->avail_railtypes = GetCompanyRailtypes(c->index);
|
|
c->avail_roadtypes = GetCompanyRoadtypes(c->index);
|
|
}
|
|
}
|
|
|
|
static void AcceptEnginePreview(EngineID eid, CompanyID company)
|
|
{
|
|
Engine *e = GetEngine(eid);
|
|
Company *c = GetCompany(company);
|
|
|
|
SetBit(e->company_avail, company);
|
|
if (e->type == VEH_TRAIN) {
|
|
const RailVehicleInfo *rvi = RailVehInfo(eid);
|
|
|
|
assert(rvi->railtype < RAILTYPE_END);
|
|
SetBit(c->avail_railtypes, rvi->railtype);
|
|
} else if (e->type == VEH_ROAD) {
|
|
SetBit(c->avail_roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
|
|
}
|
|
|
|
e->preview_company_rank = 0xFF;
|
|
if (company == _local_company) {
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
|
}
|
|
}
|
|
|
|
static CompanyID GetBestCompany(uint8 pp)
|
|
{
|
|
const Company *c;
|
|
int32 best_hist;
|
|
CompanyID best_company;
|
|
CompanyMask mask = 0;
|
|
|
|
do {
|
|
best_hist = -1;
|
|
best_company = INVALID_COMPANY;
|
|
FOR_ALL_COMPANIES(c) {
|
|
if (c->block_preview == 0 && !HasBit(mask, c->index) &&
|
|
c->old_economy[0].performance_history > best_hist) {
|
|
best_hist = c->old_economy[0].performance_history;
|
|
best_company = c->index;
|
|
}
|
|
}
|
|
|
|
if (best_company == INVALID_COMPANY) return INVALID_COMPANY;
|
|
|
|
SetBit(mask, best_company);
|
|
} while (--pp != 0);
|
|
|
|
return best_company;
|
|
}
|
|
|
|
void EnginesDailyLoop()
|
|
{
|
|
if (_cur_year >= _year_engine_aging_stops) return;
|
|
|
|
Engine *e;
|
|
FOR_ALL_ENGINES(e) {
|
|
EngineID i = e->index;
|
|
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
|
|
if (e->flags & ENGINE_OFFER_WINDOW_OPEN) {
|
|
if (e->preview_company_rank != 0xFF && !--e->preview_wait) {
|
|
e->flags &= ~ENGINE_OFFER_WINDOW_OPEN;
|
|
DeleteWindowById(WC_ENGINE_PREVIEW, i);
|
|
e->preview_company_rank++;
|
|
}
|
|
} else if (e->preview_company_rank != 0xFF) {
|
|
CompanyID best_company = GetBestCompany(e->preview_company_rank);
|
|
|
|
if (best_company == INVALID_COMPANY) {
|
|
e->preview_company_rank = 0xFF;
|
|
continue;
|
|
}
|
|
|
|
if (!IsHumanCompany(best_company)) {
|
|
/* XXX - TTDBUG: TTD has a bug here ???? */
|
|
AcceptEnginePreview(i, best_company);
|
|
} else {
|
|
e->flags |= ENGINE_OFFER_WINDOW_OPEN;
|
|
e->preview_wait = 20;
|
|
if (IsInteractiveCompany(best_company)) ShowEnginePreviewWindow(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Accept an engine prototype. XXX - it is possible that the top-company
|
|
* changes while you are waiting to accept the offer? Then it becomes invalid
|
|
* @param tile unused
|
|
* @param flags operation to perfom
|
|
* @param p1 engine-prototype offered
|
|
* @param p2 unused
|
|
*/
|
|
CommandCost CmdWantEnginePreview(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text)
|
|
{
|
|
Engine *e;
|
|
|
|
if (!IsEngineIndex(p1)) return CMD_ERROR;
|
|
e = GetEngine(p1);
|
|
if (GetBestCompany(e->preview_company_rank) != _current_company) return CMD_ERROR;
|
|
|
|
if (flags & DC_EXEC) AcceptEnginePreview(p1, _current_company);
|
|
|
|
return CommandCost();
|
|
}
|
|
|
|
StringID GetEngineCategoryName(EngineID engine);
|
|
|
|
static void NewVehicleAvailable(Engine *e)
|
|
{
|
|
Vehicle *v;
|
|
Company *c;
|
|
EngineID index = e->index;
|
|
|
|
/* In case the company didn't build the vehicle during the intro period,
|
|
* prevent that company from getting future intro periods for a while. */
|
|
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
|
|
FOR_ALL_COMPANIES(c) {
|
|
uint block_preview = c->block_preview;
|
|
|
|
if (!HasBit(e->company_avail, c->index)) continue;
|
|
|
|
/* We assume the user did NOT build it.. prove me wrong ;) */
|
|
c->block_preview = 20;
|
|
|
|
FOR_ALL_VEHICLES(v) {
|
|
if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP ||
|
|
(v->type == VEH_AIRCRAFT && IsNormalAircraft(v))) {
|
|
if (v->owner == c->index && v->engine_type == index) {
|
|
/* The user did prove me wrong, so restore old value */
|
|
c->block_preview = block_preview;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE;
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
|
|
|
/* Now available for all companies */
|
|
e->company_avail = (CompanyMask)-1;
|
|
|
|
/* Do not introduce new rail wagons */
|
|
if (IsWagon(index)) return;
|
|
|
|
if (e->type == VEH_TRAIN) {
|
|
/* maybe make another rail type available */
|
|
RailType railtype = e->u.rail.railtype;
|
|
assert(railtype < RAILTYPE_END);
|
|
FOR_ALL_COMPANIES(c) SetBit(c->avail_railtypes, railtype);
|
|
} else if (e->type == VEH_ROAD) {
|
|
/* maybe make another road type available */
|
|
FOR_ALL_COMPANIES(c) SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
|
|
}
|
|
|
|
SetDParam(0, GetEngineCategoryName(index));
|
|
SetDParam(1, index);
|
|
AddNewsItem(STR_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NS_NEW_VEHICLES, index, 0);
|
|
}
|
|
|
|
void EnginesMonthlyLoop()
|
|
{
|
|
if (_cur_year < _year_engine_aging_stops) {
|
|
Engine *e;
|
|
FOR_ALL_ENGINES(e) {
|
|
/* Age the vehicle */
|
|
if (e->flags & ENGINE_AVAILABLE && e->age != 0xFFFF) {
|
|
e->age++;
|
|
CalcEngineReliability(e);
|
|
}
|
|
|
|
if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + 365)) {
|
|
/* Introduce it to all companies */
|
|
NewVehicleAvailable(e);
|
|
} else if (!(e->flags & (ENGINE_AVAILABLE|ENGINE_EXCLUSIVE_PREVIEW)) && _date >= e->intro_date) {
|
|
/* Introduction date has passed.. show introducing dialog to one companies. */
|
|
e->flags |= ENGINE_EXCLUSIVE_PREVIEW;
|
|
|
|
/* Do not introduce new rail wagons */
|
|
if (!IsWagon(e->index))
|
|
e->preview_company_rank = 1; // Give to the company with the highest rating.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool IsUniqueEngineName(const char *name)
|
|
{
|
|
const Engine *e;
|
|
|
|
FOR_ALL_ENGINES(e) {
|
|
if (e->name != NULL && strcmp(e->name, name) == 0) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Rename an engine.
|
|
* @param tile unused
|
|
* @param flags operation to perfom
|
|
* @param p1 engine ID to rename
|
|
* @param p2 unused
|
|
*/
|
|
CommandCost CmdRenameEngine(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text)
|
|
{
|
|
if (!IsEngineIndex(p1)) return CMD_ERROR;
|
|
|
|
bool reset = StrEmpty(text);
|
|
|
|
if (!reset) {
|
|
if (strlen(text) >= MAX_LENGTH_ENGINE_NAME_BYTES) return CMD_ERROR;
|
|
if (!IsUniqueEngineName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
|
|
}
|
|
|
|
if (flags & DC_EXEC) {
|
|
Engine *e = GetEngine(p1);
|
|
free(e->name);
|
|
|
|
if (reset) {
|
|
e->name = NULL;
|
|
/* if we removed the last custom name, disable the 'Save custom names' button */
|
|
_vehicle_design_names &= ~1;
|
|
FOR_ALL_ENGINES(e) {
|
|
if (e->name != NULL) {
|
|
_vehicle_design_names |= 1;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
e->name = strdup(text);
|
|
_vehicle_design_names |= 3;
|
|
}
|
|
|
|
MarkWholeScreenDirty();
|
|
}
|
|
|
|
return CommandCost();
|
|
}
|
|
|
|
|
|
/** Check if an engine is buildable.
|
|
* @param engine index of the engine to check.
|
|
* @param type the type the engine should be.
|
|
* @param company index of the company.
|
|
* @return True if an engine is valid, of the specified type, and buildable by
|
|
* the given company.
|
|
*/
|
|
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
|
|
{
|
|
/* check if it's an engine that is in the engine array */
|
|
if (!IsEngineIndex(engine)) return false;
|
|
|
|
const Engine *e = GetEngine(engine);
|
|
|
|
/* check if it's an engine of specified type */
|
|
if (e->type != type) return false;
|
|
|
|
/* check if it's available */
|
|
if (!HasBit(e->company_avail, company)) return false;
|
|
|
|
if (type == VEH_TRAIN) {
|
|
/* Check if the rail type is available to this company */
|
|
const Company *c = GetCompany(company);
|
|
if (!HasBit(c->avail_railtypes, RailVehInfo(engine)->railtype)) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if an engine is refittable.
|
|
* @param engine index of the engine to check.
|
|
* @return true if the engine is refittable.
|
|
*/
|
|
bool IsEngineRefittable(EngineID engine)
|
|
{
|
|
/* check if it's an engine that is in the engine array */
|
|
if (!IsEngineIndex(engine)) return false;
|
|
|
|
const Engine *e = GetEngine(engine);
|
|
|
|
if (e->type == VEH_SHIP && !e->u.ship.refittable) return false;
|
|
|
|
const EngineInfo *ei = &e->info;
|
|
if (ei->refit_mask == 0) return false;
|
|
|
|
/* Are there suffixes?
|
|
* Note: This does not mean the suffixes are actually available for every consist at any time. */
|
|
if (HasBit(ei->callbackmask, CBM_VEHICLE_CARGO_SUFFIX)) return true;
|
|
|
|
/* Is there any cargo except the default cargo? */
|
|
CargoID default_cargo = GetEngineCargoType(engine);
|
|
return default_cargo != CT_INVALID && ei->refit_mask != 1U << default_cargo;
|
|
}
|
|
|
|
/** Get the default cargo type for a certain engine type
|
|
* @param engine The ID to get the cargo for
|
|
* @return The cargo type. CT_INVALID means no cargo capacity
|
|
*/
|
|
CargoID GetEngineCargoType(EngineID engine)
|
|
{
|
|
assert(IsEngineIndex(engine));
|
|
|
|
switch (GetEngine(engine)->type) {
|
|
case VEH_TRAIN:
|
|
if (RailVehInfo(engine)->capacity == 0) return CT_INVALID;
|
|
return RailVehInfo(engine)->cargo_type;
|
|
|
|
case VEH_ROAD:
|
|
if (RoadVehInfo(engine)->capacity == 0) return CT_INVALID;
|
|
return RoadVehInfo(engine)->cargo_type;
|
|
|
|
case VEH_SHIP:
|
|
if (ShipVehInfo(engine)->capacity == 0) return CT_INVALID;
|
|
return ShipVehInfo(engine)->cargo_type;
|
|
|
|
case VEH_AIRCRAFT:
|
|
/* all aircraft starts as passenger planes with cargo capacity */
|
|
return CT_PASSENGERS;
|
|
|
|
default: NOT_REACHED(); return CT_INVALID;
|
|
}
|
|
}
|