2005-07-24 14:12:37 +00:00
|
|
|
/* $Id$ */
|
|
|
|
|
2008-05-06 15:11:33 +00:00
|
|
|
/** @file engine.cpp Base for all engine handling. */
|
2007-02-23 18:55:07 +00:00
|
|
|
|
2004-08-09 17:04:08 +00:00
|
|
|
#include "stdafx.h"
|
2005-02-05 15:58:59 +00:00
|
|
|
#include "debug.h"
|
2008-09-30 20:51:04 +00:00
|
|
|
#include "company_func.h"
|
2007-12-21 21:50:46 +00:00
|
|
|
#include "command_func.h"
|
2008-03-28 08:53:36 +00:00
|
|
|
#include "news_func.h"
|
2005-07-21 22:15:02 +00:00
|
|
|
#include "variables.h"
|
2007-01-27 12:29:55 +00:00
|
|
|
#include "aircraft.h"
|
2008-04-29 21:31:29 +00:00
|
|
|
#include "newgrf_engine.h"
|
2007-05-19 09:40:18 +00:00
|
|
|
#include "group.h"
|
2007-12-21 19:49:27 +00:00
|
|
|
#include "strings_func.h"
|
2008-01-09 09:57:48 +00:00
|
|
|
#include "gfx_func.h"
|
2009-01-31 20:16:06 +00:00
|
|
|
#include "core/random_func.hpp"
|
2007-12-25 11:26:07 +00:00
|
|
|
#include "window_func.h"
|
2007-12-26 13:50:40 +00:00
|
|
|
#include "date_func.h"
|
2008-01-07 09:19:53 +00:00
|
|
|
#include "autoreplace_gui.h"
|
2008-01-07 14:23:25 +00:00
|
|
|
#include "string_func.h"
|
2008-04-06 23:49:45 +00:00
|
|
|
#include "oldpool_func.h"
|
2009-01-12 17:11:45 +00:00
|
|
|
#include "ai/ai.hpp"
|
2008-05-13 21:36:09 +00:00
|
|
|
#include "vehicle_func.h"
|
2009-01-31 20:16:06 +00:00
|
|
|
#include "settings_type.h"
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-01-13 01:21:35 +00:00
|
|
|
#include "table/strings.h"
|
|
|
|
#include "table/engines.h"
|
|
|
|
|
2008-04-29 21:31:29 +00:00
|
|
|
DEFINE_OLD_POOL_GENERIC(Engine, Engine)
|
2005-12-15 17:55:59 +00:00
|
|
|
|
2009-01-08 21:48:59 +00:00
|
|
|
/** Year that engine aging stops. Engines will not reduce in reliability
|
|
|
|
* and no more engines will be introduced */
|
|
|
|
Year _year_engine_aging_stops;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-04-29 21:31:29 +00:00
|
|
|
/** 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)
|
2008-01-08 18:25:51 +00:00
|
|
|
{
|
2008-04-29 21:31:29 +00:00
|
|
|
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;
|
2008-12-16 18:09:40 +00:00
|
|
|
/* Set model life to maximum to make wagons available */
|
|
|
|
this->info.base_life = 0xFF;
|
2008-04-29 21:31:29 +00:00
|
|
|
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;
|
2008-12-16 18:09:40 +00:00
|
|
|
|
|
|
|
/* Set the default model life of original wagons to "infinite" */
|
|
|
|
if (this->u.rail.railveh_type == RAILVEH_WAGON) this->info.base_life = 0xFF;
|
|
|
|
|
2008-04-29 21:31:29 +00:00
|
|
|
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;
|
2008-01-12 19:58:06 +00:00
|
|
|
}
|
2008-01-08 18:25:51 +00:00
|
|
|
}
|
|
|
|
|
2008-04-29 21:31:29 +00:00
|
|
|
Engine::~Engine()
|
|
|
|
{
|
|
|
|
UnloadWagonOverrides(this);
|
|
|
|
free(this->name);
|
|
|
|
}
|
|
|
|
|
2009-01-24 20:14:15 +00:00
|
|
|
Money Engine::GetRunningCost() const
|
|
|
|
{
|
|
|
|
switch (this->type) {
|
|
|
|
case VEH_ROAD:
|
|
|
|
return this->u.road.running_cost * GetPriceByIndex(this->u.road.running_cost_class) >> 8;
|
|
|
|
|
|
|
|
case VEH_TRAIN:
|
|
|
|
return GetEngineProperty(this->index, 0x0D, this->u.rail.running_cost) * GetPriceByIndex(this->u.rail.running_cost_class) >> 8;
|
|
|
|
|
|
|
|
case VEH_SHIP:
|
|
|
|
return GetEngineProperty(this->index, 0x0F, this->u.ship.running_cost) * _price.ship_running >> 8;
|
|
|
|
|
|
|
|
case VEH_AIRCRAFT:
|
|
|
|
return GetEngineProperty(this->index, 0x0E, this->u.air.running_cost) * _price.aircraft_running >> 8;
|
|
|
|
|
|
|
|
default: NOT_REACHED();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-25 00:57:03 +00:00
|
|
|
Money Engine::GetCost() const
|
|
|
|
{
|
|
|
|
switch (this->type) {
|
|
|
|
case VEH_ROAD:
|
|
|
|
return GetEngineProperty(this->index, 0x11, this->u.road.cost_factor) * (_price.roadveh_base >> 3) >> 5;
|
|
|
|
|
|
|
|
case VEH_TRAIN:
|
|
|
|
if (this->u.rail.railveh_type == RAILVEH_WAGON) {
|
|
|
|
return (GetEngineProperty(this->index, 0x17, this->u.rail.cost_factor) * _price.build_railwagon) >> 8;
|
|
|
|
} else {
|
|
|
|
return GetEngineProperty(this->index, 0x17, this->u.rail.cost_factor) * (_price.build_railvehicle >> 3) >> 5;
|
|
|
|
}
|
|
|
|
case VEH_SHIP:
|
|
|
|
return GetEngineProperty(this->index, 0x0A, this->u.ship.cost_factor) * (_price.ship_base >> 3) >> 5;
|
|
|
|
|
|
|
|
case VEH_AIRCRAFT:
|
|
|
|
return GetEngineProperty(this->index, 0x0B, this->u.air.cost_factor) * (_price.aircraft_base >> 3) >> 5;
|
|
|
|
|
|
|
|
default: NOT_REACHED();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-01 16:10:06 +00:00
|
|
|
/**
|
|
|
|
* Returns max speed for display purposes
|
2009-02-01 17:14:39 +00:00
|
|
|
* @return max speed in km-ish/h
|
2009-02-01 16:10:06 +00:00
|
|
|
*/
|
|
|
|
uint Engine::GetDisplayMaxSpeed() const
|
|
|
|
{
|
|
|
|
switch (this->type) {
|
|
|
|
case VEH_TRAIN:
|
2009-02-01 17:14:39 +00:00
|
|
|
return GetEngineProperty(this->index, 0x09, this->u.rail.max_speed);
|
2009-02-01 16:10:06 +00:00
|
|
|
|
|
|
|
case VEH_ROAD:
|
2009-02-01 17:14:39 +00:00
|
|
|
return this->u.road.max_speed / 2;
|
2009-02-01 16:10:06 +00:00
|
|
|
|
|
|
|
case VEH_SHIP:
|
2009-02-01 17:14:39 +00:00
|
|
|
return GetEngineProperty(this->index, 0x0B, this->u.ship.max_speed) / 2;
|
2009-02-01 16:10:06 +00:00
|
|
|
|
|
|
|
case VEH_AIRCRAFT:
|
2009-02-01 17:14:39 +00:00
|
|
|
return this->u.air.max_speed;
|
2009-02-01 16:10:06 +00:00
|
|
|
|
|
|
|
default: NOT_REACHED();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint Engine::GetPower() const
|
|
|
|
{
|
|
|
|
/* Currently only trains have 'power' */
|
|
|
|
switch (this->type) {
|
|
|
|
case VEH_TRAIN:
|
|
|
|
return GetEngineProperty(this->index, 0x0B, this->u.rail.power);
|
|
|
|
|
|
|
|
default: NOT_REACHED();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the weight for display purposes.
|
|
|
|
* For dual-headed train-engines this is the weight of both heads
|
|
|
|
* @return weight in display units metric tons
|
|
|
|
*/
|
|
|
|
uint Engine::GetDisplayWeight() const
|
|
|
|
{
|
|
|
|
/* Currently only trains have 'weight' */
|
|
|
|
switch (this->type) {
|
|
|
|
case VEH_TRAIN:
|
|
|
|
return GetEngineProperty(this->index, 0x16, this->u.rail.weight) << (this->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 1 : 0);
|
|
|
|
|
|
|
|
default: NOT_REACHED();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/** Sets cached values in Company::num_vehicles and Group::num_vehicles
|
2008-05-14 15:29:48 +00:00
|
|
|
*/
|
2008-05-13 21:36:09 +00:00
|
|
|
void SetCachedEngineCounts()
|
|
|
|
{
|
|
|
|
uint engines = GetEnginePoolSize();
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/* Set up the engine count for all companies */
|
|
|
|
Company *c;
|
|
|
|
FOR_ALL_COMPANIES(c) {
|
|
|
|
free(c->num_engines);
|
|
|
|
c->num_engines = CallocT<EngineID>(engines);
|
2008-05-13 21:36:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
GetCompany(v->owner)->num_engines[v->engine_type]++;
|
2008-05-13 21:36:09 +00:00
|
|
|
|
|
|
|
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]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-29 21:31:29 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-01-08 18:25:51 +00:00
|
|
|
|
2005-10-01 12:43:34 +00:00
|
|
|
void ShowEnginePreviewWindow(EngineID engine);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-06-05 12:08:20 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2004-08-09 17:04:08 +00:00
|
|
|
static void CalcEngineReliability(Engine *e)
|
|
|
|
{
|
|
|
|
uint age = e->age;
|
|
|
|
|
2007-05-10 23:10:23 +00:00
|
|
|
/* Check for early retirement */
|
2008-12-16 18:09:40 +00:00
|
|
|
if (e->company_avail != 0 && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
|
2008-04-29 21:31:29 +00:00
|
|
|
int retire_early = e->info.retire_early;
|
2008-02-08 15:34:54 +00:00
|
|
|
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) {
|
2007-05-10 23:10:23 +00:00
|
|
|
/* Early retirement is enabled and we're past the date... */
|
2008-09-30 20:39:50 +00:00
|
|
|
e->company_avail = 0;
|
2007-05-10 23:10:23 +00:00
|
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-09 17:04:08 +00:00
|
|
|
if (age < e->duration_phase_1) {
|
|
|
|
uint start = e->reliability_start;
|
|
|
|
e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
|
2008-12-16 18:09:40 +00:00
|
|
|
} else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
|
2006-10-07 15:04:22 +00:00
|
|
|
/* 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 */
|
2004-08-09 17:04:08 +00:00
|
|
|
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 {
|
2006-10-07 15:04:22 +00:00
|
|
|
/* time's up for this engine.
|
|
|
|
* We will now completely retire this design */
|
2008-09-30 20:39:50 +00:00
|
|
|
e->company_avail = 0;
|
2004-08-09 17:04:08 +00:00
|
|
|
e->reliability = e->reliability_final;
|
2007-02-06 11:11:12 +00:00
|
|
|
/* Kick this engine out of the lists */
|
|
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
2006-10-07 15:04:22 +00:00
|
|
|
InvalidateWindowClasses(WC_BUILD_VEHICLE); // Update to show the new reliability
|
2007-02-06 11:11:12 +00:00
|
|
|
InvalidateWindowClasses(WC_REPLACE_VEHICLE);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2009-01-08 21:48:59 +00:00
|
|
|
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;
|
2009-01-13 22:58:03 +00:00
|
|
|
ConvertDateToYMD(ei->base_intro + (ei->lifelength * DAYS_IN_LEAP_YEAR) / 2, &ymd);
|
2009-01-08 21:48:59 +00:00
|
|
|
|
|
|
|
_year_engine_aging_stops = max(_year_engine_aging_stops, ymd.year);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
void StartupOneEngine(Engine *e, Date aging_date)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2009-01-23 02:35:17 +00:00
|
|
|
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;
|
|
|
|
}
|
2005-01-02 17:23:04 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
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;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
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;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
e->reliability_spd_dec = ei->decay_speed << 2;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
CalcEngineReliability(e);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
e->lifelength = ei->lifelength + _settings_game.vehicle.extend_vehicle_life;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
/* prevent certain engines from ever appearing. */
|
|
|
|
if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) {
|
|
|
|
e->flags |= ENGINE_AVAILABLE;
|
|
|
|
e->company_avail = 0;
|
|
|
|
}
|
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
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));
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2009-01-23 02:35:17 +00:00
|
|
|
FOR_ALL_ENGINES(e) {
|
|
|
|
StartupOneEngine(e, aging_date);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
2008-08-28 19:53:25 +00:00
|
|
|
|
|
|
|
/* Update the bitmasks for the vehicle lists */
|
2008-09-30 20:39:50 +00:00
|
|
|
Company *c;
|
|
|
|
FOR_ALL_COMPANIES(c) {
|
|
|
|
c->avail_railtypes = GetCompanyRailtypes(c->index);
|
|
|
|
c->avail_roadtypes = GetCompanyRoadtypes(c->index);
|
2008-08-28 19:53:25 +00:00
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
static void AcceptEnginePreview(EngineID eid, CompanyID company)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2007-01-24 07:14:09 +00:00
|
|
|
Engine *e = GetEngine(eid);
|
2008-09-30 20:39:50 +00:00
|
|
|
Company *c = GetCompany(company);
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
SetBit(e->company_avail, company);
|
2007-03-08 16:27:54 +00:00
|
|
|
if (e->type == VEH_TRAIN) {
|
2007-01-24 07:14:09 +00:00
|
|
|
const RailVehicleInfo *rvi = RailVehInfo(eid);
|
|
|
|
|
|
|
|
assert(rvi->railtype < RAILTYPE_END);
|
2008-09-30 20:39:50 +00:00
|
|
|
SetBit(c->avail_railtypes, rvi->railtype);
|
2007-05-25 08:13:01 +00:00
|
|
|
} else if (e->type == VEH_ROAD) {
|
2008-09-30 20:39:50 +00:00
|
|
|
SetBit(c->avail_roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
|
2007-01-24 07:14:09 +00:00
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
e->preview_company_rank = 0xFF;
|
|
|
|
if (company == _local_company) {
|
2007-02-06 11:11:12 +00:00
|
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
2006-10-07 14:30:13 +00:00
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
static CompanyID GetBestCompany(uint8 pp)
|
2005-05-11 00:00:27 +00:00
|
|
|
{
|
2008-09-30 20:39:50 +00:00
|
|
|
const Company *c;
|
2005-05-11 00:00:27 +00:00
|
|
|
int32 best_hist;
|
2008-09-30 20:39:50 +00:00
|
|
|
CompanyID best_company;
|
2008-12-23 21:03:43 +00:00
|
|
|
CompanyMask mask = 0;
|
2005-05-11 00:00:27 +00:00
|
|
|
|
2008-02-15 21:05:46 +00:00
|
|
|
do {
|
2005-05-11 00:00:27 +00:00
|
|
|
best_hist = -1;
|
2008-09-30 20:39:50 +00:00
|
|
|
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;
|
2005-05-11 00:00:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
if (best_company == INVALID_COMPANY) return INVALID_COMPANY;
|
2005-05-11 00:00:27 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
SetBit(mask, best_company);
|
2008-02-25 02:09:04 +00:00
|
|
|
} while (--pp != 0);
|
2005-05-11 00:00:27 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
return best_company;
|
2005-05-11 00:00:27 +00:00
|
|
|
}
|
|
|
|
|
2007-03-07 11:47:46 +00:00
|
|
|
void EnginesDailyLoop()
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2009-01-08 21:48:59 +00:00
|
|
|
if (_cur_year >= _year_engine_aging_stops) return;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-04-29 21:31:29 +00:00
|
|
|
Engine *e;
|
|
|
|
FOR_ALL_ENGINES(e) {
|
|
|
|
EngineID i = e->index;
|
2007-02-28 17:06:22 +00:00
|
|
|
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
|
|
|
|
if (e->flags & ENGINE_OFFER_WINDOW_OPEN) {
|
2008-09-30 20:39:50 +00:00
|
|
|
if (e->preview_company_rank != 0xFF && !--e->preview_wait) {
|
2007-02-28 17:06:22 +00:00
|
|
|
e->flags &= ~ENGINE_OFFER_WINDOW_OPEN;
|
2004-08-09 17:04:08 +00:00
|
|
|
DeleteWindowById(WC_ENGINE_PREVIEW, i);
|
2008-09-30 20:39:50 +00:00
|
|
|
e->preview_company_rank++;
|
2004-09-10 19:02:27 +00:00
|
|
|
}
|
2008-09-30 20:39:50 +00:00
|
|
|
} else if (e->preview_company_rank != 0xFF) {
|
|
|
|
CompanyID best_company = GetBestCompany(e->preview_company_rank);
|
2005-05-11 00:00:27 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
if (best_company == INVALID_COMPANY) {
|
|
|
|
e->preview_company_rank = 0xFF;
|
2005-05-11 00:00:27 +00:00
|
|
|
continue;
|
|
|
|
}
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
e->flags |= ENGINE_OFFER_WINDOW_OPEN;
|
|
|
|
e->preview_wait = 20;
|
|
|
|
AI::NewEvent(best_company, new AIEventEnginePreview(i));
|
|
|
|
if (IsInteractiveCompany(best_company)) ShowEnginePreviewWindow(i);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/** Accept an engine prototype. XXX - it is possible that the top-company
|
2005-05-11 00:00:27 +00:00
|
|
|
* changes while you are waiting to accept the offer? Then it becomes invalid
|
2006-04-10 07:15:58 +00:00
|
|
|
* @param tile unused
|
2007-04-17 20:23:13 +00:00
|
|
|
* @param flags operation to perfom
|
2005-05-11 00:00:27 +00:00
|
|
|
* @param p1 engine-prototype offered
|
|
|
|
* @param p2 unused
|
|
|
|
*/
|
2009-02-09 21:20:05 +00:00
|
|
|
CommandCost CmdWantEnginePreview(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2005-05-11 00:00:27 +00:00
|
|
|
Engine *e;
|
|
|
|
|
2006-02-01 06:32:03 +00:00
|
|
|
if (!IsEngineIndex(p1)) return CMD_ERROR;
|
2005-06-07 18:13:49 +00:00
|
|
|
e = GetEngine(p1);
|
2008-09-30 20:39:50 +00:00
|
|
|
if (GetBestCompany(e->preview_company_rank) != _current_company) return CMD_ERROR;
|
2005-05-11 00:00:27 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
if (flags & DC_EXEC) AcceptEnginePreview(p1, _current_company);
|
2005-05-11 00:00:27 +00:00
|
|
|
|
2007-06-18 19:53:50 +00:00
|
|
|
return CommandCost();
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2008-05-13 10:17:04 +00:00
|
|
|
StringID GetEngineCategoryName(EngineID engine);
|
|
|
|
|
2004-11-14 19:44:06 +00:00
|
|
|
static void NewVehicleAvailable(Engine *e)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
|
|
|
Vehicle *v;
|
2008-09-30 20:39:50 +00:00
|
|
|
Company *c;
|
2008-04-29 21:31:29 +00:00
|
|
|
EngineID index = e->index;
|
2004-08-09 17:04:08 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/* In case the company didn't build the vehicle during the intro period,
|
|
|
|
* prevent that company from getting future intro periods for a while. */
|
2007-02-28 17:06:22 +00:00
|
|
|
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
|
2008-09-30 20:39:50 +00:00
|
|
|
FOR_ALL_COMPANIES(c) {
|
|
|
|
uint block_preview = c->block_preview;
|
2005-01-06 22:31:58 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
if (!HasBit(e->company_avail, c->index)) continue;
|
2004-09-10 19:02:27 +00:00
|
|
|
|
2005-01-06 22:31:58 +00:00
|
|
|
/* We assume the user did NOT build it.. prove me wrong ;) */
|
2008-09-30 20:39:50 +00:00
|
|
|
c->block_preview = 20;
|
2005-01-06 22:31:58 +00:00
|
|
|
|
|
|
|
FOR_ALL_VEHICLES(v) {
|
2007-03-08 16:27:54 +00:00
|
|
|
if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP ||
|
|
|
|
(v->type == VEH_AIRCRAFT && IsNormalAircraft(v))) {
|
2008-09-30 20:39:50 +00:00
|
|
|
if (v->owner == c->index && v->engine_type == index) {
|
2005-01-06 22:31:58 +00:00
|
|
|
/* The user did prove me wrong, so restore old value */
|
2008-09-30 20:39:50 +00:00
|
|
|
c->block_preview = block_preview;
|
2005-01-06 22:31:58 +00:00
|
|
|
break;
|
|
|
|
}
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-28 17:06:22 +00:00
|
|
|
e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE;
|
2007-02-06 11:11:12 +00:00
|
|
|
AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
|
2004-08-23 07:50:01 +00:00
|
|
|
|
2008-09-30 20:39:50 +00:00
|
|
|
/* Now available for all companies */
|
|
|
|
e->company_avail = (CompanyMask)-1;
|
2004-08-23 07:50:01 +00:00
|
|
|
|
2007-02-23 18:55:07 +00:00
|
|
|
/* Do not introduce new rail wagons */
|
2005-11-14 19:48:04 +00:00
|
|
|
if (IsWagon(index)) return;
|
2004-08-23 07:50:01 +00:00
|
|
|
|
2008-01-08 18:16:26 +00:00
|
|
|
if (e->type == VEH_TRAIN) {
|
2007-02-23 18:55:07 +00:00
|
|
|
/* maybe make another rail type available */
|
2008-04-29 21:31:29 +00:00
|
|
|
RailType railtype = e->u.rail.railtype;
|
2007-01-24 07:14:09 +00:00
|
|
|
assert(railtype < RAILTYPE_END);
|
2008-09-30 20:39:50 +00:00
|
|
|
FOR_ALL_COMPANIES(c) SetBit(c->avail_railtypes, railtype);
|
2008-01-08 18:16:26 +00:00
|
|
|
} else if (e->type == VEH_ROAD) {
|
2007-05-25 08:47:40 +00:00
|
|
|
/* maybe make another road type available */
|
2008-09-30 20:39:50 +00:00
|
|
|
FOR_ALL_COMPANIES(c) SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
|
2007-05-25 08:47:40 +00:00
|
|
|
}
|
2008-05-13 10:17:04 +00:00
|
|
|
|
2009-01-12 17:11:45 +00:00
|
|
|
AI::BroadcastNewEvent(new AIEventEngineAvailable(index));
|
|
|
|
|
2008-05-13 10:17:04 +00:00
|
|
|
SetDParam(0, GetEngineCategoryName(index));
|
|
|
|
SetDParam(1, index);
|
2008-05-15 13:39:36 +00:00
|
|
|
AddNewsItem(STR_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NS_NEW_VEHICLES, index, 0);
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
2007-03-07 11:47:46 +00:00
|
|
|
void EnginesMonthlyLoop()
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2009-01-08 21:48:59 +00:00
|
|
|
if (_cur_year < _year_engine_aging_stops) {
|
2008-01-24 10:47:44 +00:00
|
|
|
Engine *e;
|
|
|
|
FOR_ALL_ENGINES(e) {
|
2007-02-23 18:55:07 +00:00
|
|
|
/* Age the vehicle */
|
2005-07-30 18:04:49 +00:00
|
|
|
if (e->flags & ENGINE_AVAILABLE && e->age != 0xFFFF) {
|
2004-08-09 17:04:08 +00:00
|
|
|
e->age++;
|
|
|
|
CalcEngineReliability(e);
|
|
|
|
}
|
|
|
|
|
2009-01-13 22:58:03 +00:00
|
|
|
if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + DAYS_IN_YEAR)) {
|
2008-09-30 20:39:50 +00:00
|
|
|
/* Introduce it to all companies */
|
2004-08-09 17:04:08 +00:00
|
|
|
NewVehicleAvailable(e);
|
2007-02-28 17:06:22 +00:00
|
|
|
} else if (!(e->flags & (ENGINE_AVAILABLE|ENGINE_EXCLUSIVE_PREVIEW)) && _date >= e->intro_date) {
|
2008-09-30 20:39:50 +00:00
|
|
|
/* Introduction date has passed.. show introducing dialog to one companies. */
|
2007-02-28 17:06:22 +00:00
|
|
|
e->flags |= ENGINE_EXCLUSIVE_PREVIEW;
|
2004-09-15 06:51:23 +00:00
|
|
|
|
2007-02-23 18:55:07 +00:00
|
|
|
/* Do not introduce new rail wagons */
|
2008-04-29 21:31:29 +00:00
|
|
|
if (!IsWagon(e->index))
|
2008-09-30 20:39:50 +00:00
|
|
|
e->preview_company_rank = 1; // Give to the company with the highest rating.
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-06-27 20:53:25 +00:00
|
|
|
static bool IsUniqueEngineName(const char *name)
|
|
|
|
{
|
2008-04-29 21:31:29 +00:00
|
|
|
const Engine *e;
|
2009-01-10 15:54:07 +00:00
|
|
|
|
2008-04-29 21:31:29 +00:00
|
|
|
FOR_ALL_ENGINES(e) {
|
2009-01-10 15:54:07 +00:00
|
|
|
if (e->name != NULL && strcmp(e->name, name) == 0) return false;
|
2007-06-27 20:53:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-05-11 00:00:27 +00:00
|
|
|
/** Rename an engine.
|
2006-04-10 07:15:58 +00:00
|
|
|
* @param tile unused
|
2007-04-17 20:23:13 +00:00
|
|
|
* @param flags operation to perfom
|
2005-05-11 00:00:27 +00:00
|
|
|
* @param p1 engine ID to rename
|
|
|
|
* @param p2 unused
|
|
|
|
*/
|
2009-02-09 21:20:05 +00:00
|
|
|
CommandCost CmdRenameEngine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
2004-08-09 17:04:08 +00:00
|
|
|
{
|
2008-08-13 06:22:04 +00:00
|
|
|
if (!IsEngineIndex(p1)) return CMD_ERROR;
|
2007-06-27 20:53:25 +00:00
|
|
|
|
2008-12-28 14:37:19 +00:00
|
|
|
bool reset = StrEmpty(text);
|
2008-09-15 19:02:50 +00:00
|
|
|
|
|
|
|
if (!reset) {
|
2008-12-28 14:37:19 +00:00
|
|
|
if (strlen(text) >= MAX_LENGTH_ENGINE_NAME_BYTES) return CMD_ERROR;
|
|
|
|
if (!IsUniqueEngineName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
|
2008-09-15 19:02:50 +00:00
|
|
|
}
|
2005-05-11 00:00:27 +00:00
|
|
|
|
2004-08-09 17:04:08 +00:00
|
|
|
if (flags & DC_EXEC) {
|
2008-01-12 19:58:06 +00:00
|
|
|
Engine *e = GetEngine(p1);
|
|
|
|
free(e->name);
|
2008-09-15 19:02:50 +00:00
|
|
|
|
|
|
|
if (reset) {
|
|
|
|
e->name = NULL;
|
|
|
|
} else {
|
2008-12-28 14:37:19 +00:00
|
|
|
e->name = strdup(text);
|
2008-09-15 19:02:50 +00:00
|
|
|
}
|
|
|
|
|
2004-08-09 17:04:08 +00:00
|
|
|
MarkWholeScreenDirty();
|
|
|
|
}
|
|
|
|
|
2007-06-18 19:53:50 +00:00
|
|
|
return CommandCost();
|
2004-08-09 17:04:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-04 11:28:12 +00:00
|
|
|
/** Check if an engine is buildable.
|
2008-09-30 20:39:50 +00:00
|
|
|
* @param engine index of the engine to check.
|
|
|
|
* @param type the type the engine should be.
|
|
|
|
* @param company index of the company.
|
2008-02-04 11:28:12 +00:00
|
|
|
* @return True if an engine is valid, of the specified type, and buildable by
|
2008-09-30 20:39:50 +00:00
|
|
|
* the given company.
|
2006-01-12 15:52:18 +00:00
|
|
|
*/
|
2008-09-30 20:39:50 +00:00
|
|
|
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
|
2006-01-12 15:52:18 +00:00
|
|
|
{
|
2007-02-23 18:55:07 +00:00
|
|
|
/* check if it's an engine that is in the engine array */
|
2006-01-12 15:52:18 +00:00
|
|
|
if (!IsEngineIndex(engine)) return false;
|
|
|
|
|
2008-02-04 11:28:12 +00:00
|
|
|
const Engine *e = GetEngine(engine);
|
2006-01-12 15:52:18 +00:00
|
|
|
|
2007-02-23 18:55:07 +00:00
|
|
|
/* check if it's an engine of specified type */
|
2006-01-12 15:52:18 +00:00
|
|
|
if (e->type != type) return false;
|
|
|
|
|
2007-02-23 18:55:07 +00:00
|
|
|
/* check if it's available */
|
2008-09-30 20:39:50 +00:00
|
|
|
if (!HasBit(e->company_avail, company)) return false;
|
2006-01-12 15:52:18 +00:00
|
|
|
|
2007-04-26 07:24:19 +00:00
|
|
|
if (type == VEH_TRAIN) {
|
2008-09-30 20:39:50 +00:00
|
|
|
/* 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;
|
2007-04-26 07:24:19 +00:00
|
|
|
}
|
|
|
|
|
2006-01-12 15:52:18 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-12-16 22:02:12 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2007-04-20 16:56:55 +00:00
|
|
|
/** 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;
|
|
|
|
}
|
|
|
|
}
|