Initial support for multi-part (pseudo-articulated) ships
This commit is contained in:
@@ -37,6 +37,7 @@
|
||||
<ul>
|
||||
<li><a href="#builtin-functions">Builtin functions</a></li>
|
||||
<li><a href="#switch-types">Additional switch types</a></li>
|
||||
<li><a href="#ship-callbacks">Ship callbacks</a></li>
|
||||
<li><a href="#railtype-properties">Railtype properties</a></li>
|
||||
<li><a href="#railtype-variables">Railtype variables</a></li>
|
||||
<li><a href="#roadtype-properties">Roadtype properties</a></li>
|
||||
@@ -74,6 +75,15 @@
|
||||
<p>These require the <span class="code">more_varaction2_types</span> feature. If this feature is not present, switches of these types will produce a CB_FAILED result.</p>
|
||||
</p>
|
||||
|
||||
<h3 id="ship-callbacks"><a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Vehicles#Vehicle_callbacks">Ship callbacks</a></h3>
|
||||
<p>
|
||||
The <span class="code">articulated_part</span> callback is also available for ships.<br />
|
||||
Additional ship parts are not used for graphics, they are only used for additional cargo capacity, the default graphics chain is unused.<br />
|
||||
The default graphics chain for the primary vehicle may check the cargo states of the other ship parts if required.<br />
|
||||
Additional ship parts may be refitted individually.
|
||||
<p>This requires the <span class="code">multi_part_ships</span> feature.</p>
|
||||
</p>
|
||||
|
||||
<h3 id="railtype-properties"><a href="https://newgrf-specs.tt-wiki.net/wiki/NML:Railtypes#Railtype_properties">Railtype properties</a></h3>
|
||||
<table>
|
||||
<tr><th>Property</th><th>Value range</th><th>Comment</th></tr>
|
||||
|
@@ -52,6 +52,7 @@
|
||||
<li><a href="#varaction2_railtypes">Variational Action 2 - Railtypes</a></li>
|
||||
<li><a href="#varaction2_object">Variational Action 2 - Objects</a></li>
|
||||
<li><a href="#varaction2_signals">Variational Action 2 - Signals (Feature 0E)</a></li>
|
||||
<li><a href="#callbacks_ships">Callbacks - Ships</a></li>
|
||||
<li><a href="#a3objects">Action 3 - Objects</a></li>
|
||||
<li><a href="#a3signals">Action 3 - Signals (Feature 0E)</a></li>
|
||||
<li><a href="#action5">Action 14 - Type ID Mapping for Action 5</a></li>
|
||||
@@ -888,7 +889,18 @@
|
||||
If the signal being drawn uses a custom signal style, the value is the signal style ID as set in the <a href="#signals_define_style">signals_define_style</a> property.<br />
|
||||
Otherwise for signals using the default style, the value is 0.
|
||||
</p>
|
||||
<p>This is indicated by the feature name: <font face="monospace">action0_signals_style</font>, version 1</p>
|
||||
<p>This is indicated by the feature name: <font face="monospace">action0_signals_style</font>, version 1.</p>
|
||||
<br />
|
||||
<br />
|
||||
<h3 id="callbacks_ships"><a href="https://newgrf-specs.tt-wiki.net/wiki/Callbacks">Callbacks - Ships</a></h3>
|
||||
<h4 id="callbacks_ships_articulated">Multi-part ships</h4>
|
||||
<p><b><a href="https://newgrf-specs.tt-wiki.net/wiki/Callbacks#Articulated_engine_.2816.29">Callback 16 - Articulated engine</a> may also be used for ships.</b><br />
|
||||
This functions the same as for trains and road vehicles, and is enabled in the same way (bit 4 of ship property 12).<br />
|
||||
Additional ship parts are not used for graphics, they are only used for additional cargo capacity.<br />
|
||||
The graphics chain for the primary vehicle may check the cargo states of the other ship parts if required.<br />
|
||||
Additional ship parts may be refitted individually.
|
||||
</p>
|
||||
<p>This is indicated by the feature name: <font face="monospace">multi_part_ships</font>, version 1</p>
|
||||
<br />
|
||||
<br />
|
||||
<h3 id="a3objects"><a href="https://newgrf-specs.tt-wiki.net/wiki/Action3">Action 3 - Objects</a></h3>
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "train.h"
|
||||
#include "roadveh.h"
|
||||
#include "ship.h"
|
||||
#include "vehicle_func.h"
|
||||
#include "engine_func.h"
|
||||
#include "company_func.h"
|
||||
@@ -178,7 +179,7 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine)
|
||||
uint16 cargo_capacity = GetVehicleDefaultCapacity(engine, &cargo_type);
|
||||
if (cargo_type < NUM_CARGO) capacity[cargo_type] = cargo_capacity;
|
||||
|
||||
if (!e->IsGroundVehicle()) return capacity;
|
||||
if (!e->IsArticulatedCallbackVehicleType()) return capacity;
|
||||
|
||||
if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return capacity;
|
||||
|
||||
@@ -203,7 +204,7 @@ bool IsArticulatedVehicleRefittable(EngineID engine)
|
||||
if (IsEngineRefittable(engine)) return true;
|
||||
|
||||
const Engine *e = Engine::Get(engine);
|
||||
if (!e->IsGroundVehicle()) return false;
|
||||
if (!e->IsArticulatedCallbackVehicleType()) return false;
|
||||
|
||||
if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return false;
|
||||
|
||||
@@ -231,7 +232,7 @@ void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type,
|
||||
*union_mask = veh_cargoes;
|
||||
*intersection_mask = (veh_cargoes != 0) ? veh_cargoes : ALL_CARGOTYPES;
|
||||
|
||||
if (!e->IsGroundVehicle()) return;
|
||||
if (!e->IsArticulatedCallbackVehicleType()) return;
|
||||
if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return;
|
||||
|
||||
for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) {
|
||||
@@ -365,8 +366,11 @@ void AddArticulatedParts(Vehicle *first)
|
||||
* and we run out of available vehicles, bail out. */
|
||||
if (!Vehicle::CanAllocateItem()) return;
|
||||
|
||||
GroundVehicleCache *gcache = v->GetGroundVehicleCache();
|
||||
GroundVehicleCache *gcache = nullptr;
|
||||
if (type == VEH_TRAIN || type == VEH_ROAD) {
|
||||
gcache = v->GetGroundVehicleCache();
|
||||
gcache->first_engine = v->engine_type; // Needs to be set before first callback
|
||||
}
|
||||
|
||||
const Engine *e_artic = Engine::Get(engine_type);
|
||||
switch (type) {
|
||||
@@ -424,26 +428,53 @@ void AddArticulatedParts(Vehicle *first)
|
||||
rv->SetArticulatedPart();
|
||||
break;
|
||||
}
|
||||
|
||||
case VEH_SHIP: {
|
||||
Ship *front = Ship::From(first);
|
||||
Ship *s = new Ship();
|
||||
v->SetNext(s);
|
||||
v = s;
|
||||
|
||||
s->direction = DIR_N;
|
||||
s->x_pos = 0;
|
||||
s->y_pos = 0;
|
||||
s->z_pos = 0;
|
||||
s->vehstatus = VS_HIDDEN | VS_UNCLICKABLE;
|
||||
s->subtype = (1 << GVSF_VIRTUAL);
|
||||
|
||||
if (e_artic->CanCarryCargo()) {
|
||||
s->cargo_type = e_artic->GetDefaultCargoType();
|
||||
s->cargo_cap = e_artic->u.ship.capacity; // Callback 36 is called when the consist is finished
|
||||
} else {
|
||||
s->cargo_type = front->cargo_type;
|
||||
s->cargo_cap = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* get common values from first engine */
|
||||
v->direction = first->direction;
|
||||
v->owner = first->owner;
|
||||
v->tile = first->tile;
|
||||
v->x_pos = first->x_pos;
|
||||
v->y_pos = first->y_pos;
|
||||
v->z_pos = first->z_pos;
|
||||
v->date_of_last_service = first->date_of_last_service;
|
||||
v->build_year = first->build_year;
|
||||
v->vehstatus = first->vehstatus & ~VS_STOPPED;
|
||||
|
||||
v->cargo_subtype = 0;
|
||||
v->max_age = 0;
|
||||
v->engine_type = engine_type;
|
||||
v->value = 0;
|
||||
v->sprite_seq.Set(SPR_IMG_QUERY);
|
||||
v->random_bits = VehicleRandomBits();
|
||||
|
||||
if (type == VEH_SHIP) continue;
|
||||
|
||||
v->direction = first->direction;
|
||||
v->tile = first->tile;
|
||||
v->x_pos = first->x_pos;
|
||||
v->y_pos = first->y_pos;
|
||||
v->z_pos = first->z_pos;
|
||||
v->vehstatus = first->vehstatus & ~VS_STOPPED;
|
||||
|
||||
v->sprite_seq.Set(SPR_IMG_QUERY);
|
||||
|
||||
if (flip_image) v->spritenum++;
|
||||
|
||||
v->UpdatePosition();
|
||||
|
@@ -188,7 +188,7 @@ bool Engine::CanCarryCargo() const
|
||||
|
||||
bool Engine::CanPossiblyCarryCargo() const
|
||||
{
|
||||
if (this->IsGroundVehicle() && HasBit(this->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return true;
|
||||
if (this->IsArticulatedCallbackVehicleType() && HasBit(this->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return true;
|
||||
|
||||
switch (this->type) {
|
||||
case VEH_TRAIN:
|
||||
|
@@ -162,6 +162,15 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
|
||||
return this->type == VEH_TRAIN || this->type == VEH_ROAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the vehicle type supports articulation.
|
||||
* @return True iff the vehicle is a train, road vehicle or ship.
|
||||
*/
|
||||
inline bool IsArticulatedCallbackVehicleType() const
|
||||
{
|
||||
return this->type == VEH_TRAIN || this->type == VEH_ROAD || this->type == VEH_SHIP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the NewGRF the engine is tied to.
|
||||
* This is the GRF providing the Action 3.
|
||||
|
@@ -352,7 +352,7 @@ struct NewGRFInspectWindow : Window {
|
||||
bool HasChainIndex() const
|
||||
{
|
||||
GrfSpecFeature f = GetFeatureNum(this->window_number);
|
||||
return f == GSF_TRAINS || f == GSF_ROADVEHICLES;
|
||||
return f == GSF_TRAINS || f == GSF_ROADVEHICLES || f == GSF_SHIPS;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,6 +416,10 @@ struct NewGRFInspectWindow : Window {
|
||||
case WID_NGRFI_VEH_CHAIN: {
|
||||
assert(this->HasChainIndex());
|
||||
GrfSpecFeature f = GetFeatureNum(this->window_number);
|
||||
if (f == GSF_SHIPS) {
|
||||
size->height = FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.framerect.Vertical();
|
||||
break;
|
||||
}
|
||||
size->height = std::max(size->height, GetVehicleImageCellSize((VehicleType)(VEH_TRAIN + (f - GSF_TRAINS)), EIT_IN_DEPOT).height + 2 + WidgetDimensions::scaled.bevel.Vertical());
|
||||
break;
|
||||
}
|
||||
@@ -457,6 +461,15 @@ struct NewGRFInspectWindow : Window {
|
||||
switch (widget) {
|
||||
case WID_NGRFI_VEH_CHAIN: {
|
||||
const Vehicle *v = Vehicle::Get(this->GetFeatureIndex());
|
||||
if (GetFeatureNum(this->window_number) == GSF_SHIPS) {
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
|
||||
char buffer[64];
|
||||
uint count = 0;
|
||||
for (const Vehicle *u = v->First(); u != nullptr; u = u->Next()) count++;
|
||||
seprintf(buffer, lastof(buffer), "Part %u of %u", this->chain_index + 1, count);
|
||||
::DrawString(ir.left, ir.right, ir.top, buffer, TC_BLACK);
|
||||
break;
|
||||
}
|
||||
int total_width = 0;
|
||||
int sel_start = 0;
|
||||
int sel_end = 0;
|
||||
@@ -1103,7 +1116,7 @@ void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index, const uint32 gr
|
||||
if (!IsNewGRFInspectable(feature, index)) return;
|
||||
|
||||
WindowNumber wno = GetInspectWindowNumber(feature, index);
|
||||
WindowDesc *desc = (feature == GSF_TRAINS || feature == GSF_ROADVEHICLES) ? &_newgrf_inspect_chain_desc : &_newgrf_inspect_desc;
|
||||
WindowDesc *desc = (feature == GSF_TRAINS || feature == GSF_ROADVEHICLES || feature == GSF_SHIPS) ? &_newgrf_inspect_chain_desc : &_newgrf_inspect_desc;
|
||||
NewGRFInspectWindow *w = AllocateWindowDescFront<NewGRFInspectWindow>(desc, wno, true);
|
||||
w->SetCallerGRFID(grfid);
|
||||
}
|
||||
|
@@ -66,6 +66,7 @@ extern const GRFFeatureInfo _grf_feature_list[] = {
|
||||
GRFFeatureInfo("town_uncapped_variables", 1),
|
||||
GRFFeatureInfo("town_zone_callback", 1, GFTOF_TOWN_ZONE_CALLBACK),
|
||||
GRFFeatureInfo("more_varaction2_types", 1, GFTOF_MORE_VARACTION2_TYPES),
|
||||
GRFFeatureInfo("multi_part_ships", 1),
|
||||
GRFFeatureInfo(),
|
||||
};
|
||||
|
||||
|
@@ -1727,7 +1727,7 @@ private:
|
||||
{
|
||||
this->can_do_refit = false;
|
||||
this->can_do_autorefit = false;
|
||||
for (const Vehicle *w = this->vehicle; w != nullptr; w = w->IsGroundVehicle() ? w->Next() : nullptr) {
|
||||
for (const Vehicle *w = this->vehicle; w != nullptr; w = w->IsArticulatedCallbackVehicleType() ? w->Next() : nullptr) {
|
||||
if (IsEngineRefittable(w->engine_type)) this->can_do_refit = true;
|
||||
if (HasBit(Engine::Get(w->engine_type)->info.misc_flags, EF_AUTO_REFIT)) this->can_do_autorefit = true;
|
||||
}
|
||||
|
@@ -257,8 +257,8 @@ void AfterLoadVehicles(bool part_of_load)
|
||||
/* Reinstate the previous pointer */
|
||||
if (v->Next() != nullptr) {
|
||||
v->Next()->previous = v;
|
||||
if (HasBit(v->subtype, GVSF_VIRTUAL) != HasBit(v->Next()->subtype, GVSF_VIRTUAL)) {
|
||||
SlErrorCorrupt("Mixed virtual/non-virtual vehicle consist");
|
||||
if (v->type == VEH_TRAIN && (HasBit(v->subtype, GVSF_VIRTUAL) != HasBit(v->Next()->subtype, GVSF_VIRTUAL))) {
|
||||
SlErrorCorrupt("Mixed virtual/non-virtual train consist");
|
||||
}
|
||||
}
|
||||
if (v->NextShared() != nullptr) v->NextShared()->previous_shared = v;
|
||||
@@ -457,7 +457,9 @@ void AfterLoadVehicles(bool part_of_load)
|
||||
}
|
||||
|
||||
case VEH_SHIP:
|
||||
if (Ship::From(v)->IsPrimaryVehicle()) {
|
||||
Ship::From(v)->UpdateCache();
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
|
@@ -43,7 +43,7 @@ struct Ship FINAL : public SpecializedVehicle<Ship, VEH_SHIP> {
|
||||
void UpdateDeltaXY() override;
|
||||
ExpensesType GetExpenseType(bool income) const override { return income ? EXPENSES_SHIP_REVENUE : EXPENSES_SHIP_RUN; }
|
||||
void PlayLeaveStationSound(bool force = false) const override;
|
||||
bool IsPrimaryVehicle() const override { return true; }
|
||||
bool IsPrimaryVehicle() const override { return this->Previous() == nullptr; }
|
||||
void GetImage(Direction direction, EngineImageType image_type, VehicleSpriteSeq *result) const override;
|
||||
Direction GetMapImageDirection() const { return this->rotation; }
|
||||
int GetDisplaySpeed() const override{ return this->cur_speed / 2; }
|
||||
|
@@ -37,6 +37,7 @@
|
||||
#include "industry.h"
|
||||
#include "industry_map.h"
|
||||
#include "core/checksum_func.hpp"
|
||||
#include "articulated_vehicles.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
@@ -220,9 +221,13 @@ void Ship::UpdateCache()
|
||||
this->vcache.cached_max_speed = svi->ApplyWaterClassSpeedFrac(raw_speed, is_ocean);
|
||||
|
||||
/* Update cargo aging period. */
|
||||
this->vcache.cached_cargo_age_period = GetVehicleProperty(this, PROP_SHIP_CARGO_AGE_PERIOD, EngInfo(this->engine_type)->cargo_age_period);
|
||||
for (Ship *u = this; u != nullptr; u = u->Next()) {
|
||||
u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_SHIP_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
|
||||
}
|
||||
|
||||
this->UpdateVisualEffect();
|
||||
|
||||
SetBit(this->vcache.cached_veh_flags, VCF_LAST_VISUAL_EFFECT);
|
||||
}
|
||||
|
||||
Money Ship::GetRunningCost() const
|
||||
@@ -245,6 +250,8 @@ Money Ship::GetRunningCost() const
|
||||
|
||||
void Ship::OnNewDay()
|
||||
{
|
||||
if (!this->IsPrimaryVehicle()) return;
|
||||
|
||||
if ((++this->day_counter & 7) == 0) {
|
||||
DecreaseVehicleValue(this);
|
||||
}
|
||||
@@ -253,6 +260,8 @@ void Ship::OnNewDay()
|
||||
|
||||
void Ship::OnPeriodic()
|
||||
{
|
||||
if (!this->IsPrimaryVehicle()) return;
|
||||
|
||||
CheckVehicleBreakdown(this);
|
||||
CheckIfShipNeedsService(this);
|
||||
|
||||
@@ -1155,6 +1164,7 @@ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, u
|
||||
|
||||
v->cargo_cap = e->DetermineCapacity(v);
|
||||
|
||||
AddArticulatedParts(v);
|
||||
v->InvalidateNewGRFCacheOfChain();
|
||||
|
||||
v->UpdatePosition();
|
||||
|
@@ -69,6 +69,65 @@ void DrawShipDetails(const Vehicle *v, const Rect &r)
|
||||
DrawString(r.left, r.right, y, STR_VEHICLE_INFO_BUILT_VALUE);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
|
||||
Money feeder_share = 0;
|
||||
|
||||
if (v->Next() != nullptr) {
|
||||
CargoArray max_cargo;
|
||||
StringID subtype_text[NUM_CARGO];
|
||||
char capacity[512];
|
||||
|
||||
memset(subtype_text, 0, sizeof(subtype_text));
|
||||
|
||||
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
|
||||
max_cargo[u->cargo_type] += u->cargo_cap;
|
||||
if (u->cargo_cap > 0) {
|
||||
StringID text = GetCargoSubtypeText(u);
|
||||
if (text != STR_EMPTY) subtype_text[u->cargo_type] = text;
|
||||
}
|
||||
}
|
||||
|
||||
GetString(capacity, STR_VEHICLE_DETAILS_TRAIN_ARTICULATED_RV_CAPACITY, lastof(capacity));
|
||||
|
||||
bool first = true;
|
||||
for (CargoID i = 0; i < NUM_CARGO; i++) {
|
||||
if (max_cargo[i] > 0) {
|
||||
char buffer[128];
|
||||
|
||||
SetDParam(0, i);
|
||||
SetDParam(1, max_cargo[i]);
|
||||
GetString(buffer, STR_JUST_CARGO, lastof(buffer));
|
||||
|
||||
if (!first) strecat(capacity, ", ", lastof(capacity));
|
||||
strecat(capacity, buffer, lastof(capacity));
|
||||
|
||||
if (subtype_text[i] != 0) {
|
||||
GetString(buffer, subtype_text[i], lastof(buffer));
|
||||
strecat(capacity, buffer, lastof(capacity));
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
DrawString(r.left, r.right, y, capacity, TC_BLUE);
|
||||
y += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
|
||||
|
||||
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
|
||||
if (u->cargo_cap == 0) continue;
|
||||
|
||||
StringID str = STR_VEHICLE_DETAILS_CARGO_EMPTY;
|
||||
if (u->cargo.StoredCount() > 0) {
|
||||
SetDParam(0, u->cargo_type);
|
||||
SetDParam(1, u->cargo.StoredCount());
|
||||
SetDParam(2, u->cargo.Source());
|
||||
str = STR_VEHICLE_DETAILS_CARGO_FROM;
|
||||
feeder_share += u->cargo.FeederShare();
|
||||
}
|
||||
DrawString(r.left, r.right, y, str);
|
||||
y += FONT_HEIGHT_NORMAL;
|
||||
}
|
||||
y += WidgetDimensions::scaled.vsep_normal;
|
||||
} else {
|
||||
SetDParam(0, v->cargo_type);
|
||||
SetDParam(1, v->cargo_cap);
|
||||
SetDParam(4, GetCargoSubtypeText(v));
|
||||
@@ -81,12 +140,14 @@ void DrawShipDetails(const Vehicle *v, const Rect &r)
|
||||
SetDParam(1, v->cargo.StoredCount());
|
||||
SetDParam(2, v->cargo.Source());
|
||||
str = STR_VEHICLE_DETAILS_CARGO_FROM;
|
||||
feeder_share += v->cargo.FeederShare();
|
||||
}
|
||||
DrawString(r.left, r.right, y, str);
|
||||
y += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
|
||||
}
|
||||
|
||||
/* Draw Transfer credits text */
|
||||
SetDParam(0, v->cargo.FeederShare());
|
||||
SetDParam(0, feeder_share);
|
||||
DrawString(r.left, r.right, y, STR_VEHICLE_INFO_FEEDER_CARGO_VALUE);
|
||||
y += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
|
||||
|
||||
|
@@ -1012,7 +1012,7 @@ bool Vehicle::IsEngineCountable() const
|
||||
return !this->IsArticulatedPart() && // tenders and other articulated parts
|
||||
!Train::From(this)->IsRearDualheaded(); // rear parts of multiheaded engines
|
||||
case VEH_ROAD: return RoadVehicle::From(this)->IsFrontEngine();
|
||||
case VEH_SHIP: return true;
|
||||
case VEH_SHIP: return Ship::From(this)->IsPrimaryVehicle();
|
||||
default: return false; // Only count company buildable vehicles
|
||||
}
|
||||
}
|
||||
@@ -1405,7 +1405,7 @@ void RebuildVehicleTickCaches()
|
||||
break;
|
||||
|
||||
case VEH_SHIP:
|
||||
_tick_ship_cache.push_back(Ship::From(v));
|
||||
if (v->Previous() == nullptr) _tick_ship_cache.push_back(Ship::From(v));
|
||||
break;
|
||||
|
||||
case VEH_EFFECT:
|
||||
@@ -1597,7 +1597,9 @@ void CallVehicleTicks()
|
||||
for (Ship *s : _tick_ship_cache) {
|
||||
v = s;
|
||||
if (!s->Ship::Tick()) continue;
|
||||
VehicleTickCargoAging(s);
|
||||
for (Ship *u = s; u != nullptr; u = u->Next()) {
|
||||
VehicleTickCargoAging(u);
|
||||
}
|
||||
if (!(s->vehstatus & VS_STOPPED)) VehicleTickMotion(s, s);
|
||||
}
|
||||
}
|
||||
|
@@ -576,6 +576,15 @@ public:
|
||||
return this->type == VEH_TRAIN || this->type == VEH_ROAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the vehicle type supports articulation.
|
||||
* @return True iff the vehicle is a train, road vehicle or ship.
|
||||
*/
|
||||
debug_inline bool IsArticulatedCallbackVehicleType() const
|
||||
{
|
||||
return this->type == VEH_TRAIN || this->type == VEH_ROAD || this->type == VEH_SHIP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the speed in km-ish/h that can be sent into SetDParam for string processing.
|
||||
* @return the vehicle's speed
|
||||
|
@@ -125,7 +125,7 @@ CommandCost CmdBuildVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
switch (type) {
|
||||
case VEH_TRAIN: num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break;
|
||||
case VEH_ROAD: num_vehicles = 1 + CountArticulatedParts(eid, false); break;
|
||||
case VEH_SHIP: num_vehicles = 1; break;
|
||||
case VEH_SHIP: num_vehicles = 1 + CountArticulatedParts(eid, false); break;
|
||||
case VEH_AIRCRAFT: num_vehicles = e->u.air.subtype & AIR_CTOL ? 2 : 3; break;
|
||||
default: NOT_REACHED(); // Safe due to IsDepotTile()
|
||||
}
|
||||
@@ -167,7 +167,7 @@ CommandCost CmdBuildVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
value.AddCost(CmdRefitVehicle(tile, flags, v->index, cargo | (1 << 16), nullptr));
|
||||
} else {
|
||||
/* Fill in non-refitted capacities */
|
||||
if (e->type == VEH_TRAIN || e->type == VEH_ROAD) {
|
||||
if (e->type == VEH_TRAIN || e->type == VEH_ROAD || e->type == VEH_SHIP) {
|
||||
_returned_vehicle_capacities = GetCapacityOfArticulatedParts(eid);
|
||||
_returned_refit_capacity = _returned_vehicle_capacities[default_cargo];
|
||||
_returned_mail_refit_capacity = 0;
|
||||
@@ -534,7 +534,7 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
}
|
||||
|
||||
/* Don't allow shadows and such to be refitted. */
|
||||
if (v != front && (v->type == VEH_SHIP || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
|
||||
if (v != front && (v->type == VEH_AIRCRAFT)) return CMD_ERROR;
|
||||
|
||||
/* Allow auto-refitting only during loading and normal refitting only in a depot. */
|
||||
if (!virtual_train_mode) {
|
||||
@@ -553,9 +553,9 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
byte new_subtype = GB(p2, 8, 8);
|
||||
if (new_cid >= NUM_CARGO) return CMD_ERROR;
|
||||
|
||||
/* For ships and aircraft there is always only one. */
|
||||
bool only_this = HasBit(p2, 25) || front->type == VEH_SHIP || front->type == VEH_AIRCRAFT;
|
||||
/* For aircraft there is always only one. */
|
||||
uint8 num_vehicles = GB(p2, 16, 8);
|
||||
bool only_this = HasBit(p2, 25) || front->type == VEH_AIRCRAFT || (front->type == VEH_SHIP && num_vehicles == 1);
|
||||
|
||||
CommandCost cost = RefitVehicle(v, only_this, num_vehicles, new_cid, new_subtype, flags, auto_refit);
|
||||
if (is_virtual_train && !(flags & DC_QUERY_COST)) cost.MultiplyCost(0);
|
||||
@@ -573,7 +573,7 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
|
||||
case VEH_SHIP:
|
||||
v->InvalidateNewGRFCacheOfChain();
|
||||
Ship::From(v)->UpdateCache();
|
||||
Ship::From(front)->UpdateCache();
|
||||
break;
|
||||
|
||||
case VEH_AIRCRAFT:
|
||||
@@ -591,7 +591,7 @@ CommandCost CmdRefitVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
InvalidateWindowClassesData(WC_DEPARTURES_BOARD, 0);
|
||||
}
|
||||
/* virtual vehicles get their cargo changed by the TemplateCreateWindow, so set this dirty instead of a depot window */
|
||||
if (HasBit(v->subtype, GVSF_VIRTUAL)) {
|
||||
if (HasBit(front->subtype, GVSF_VIRTUAL)) {
|
||||
SetWindowClassesDirty(WC_CREATE_TEMPLATE);
|
||||
} else {
|
||||
SetWindowDirty(WC_VEHICLE_DEPOT, front->tile);
|
||||
@@ -1618,8 +1618,8 @@ CommandCost CmdCloneVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
|
||||
}
|
||||
} while (v != nullptr);
|
||||
|
||||
if ((flags & DC_EXEC) && v->type == VEH_TRAIN) w = w->GetNextVehicle();
|
||||
} while (v->type == VEH_TRAIN && (v = v->GetNextVehicle()) != nullptr);
|
||||
if ((flags & DC_EXEC) && (v->type == VEH_TRAIN || v->type == VEH_SHIP)) w = w->GetNextVehicle();
|
||||
} while ((v->type == VEH_TRAIN || v->type == VEH_SHIP) && (v = v->GetNextVehicle()) != nullptr);
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
/*
|
||||
|
@@ -2823,9 +2823,9 @@ struct VehicleDetailsWindow : Window {
|
||||
}
|
||||
if (!gui_scope) return;
|
||||
const Vehicle *v = Vehicle::Get(this->window_number);
|
||||
if (v->type == VEH_ROAD) {
|
||||
if (v->type == VEH_ROAD || v->type == VEH_SHIP) {
|
||||
const NWidgetBase *nwid_info = this->GetWidget<NWidgetBase>(WID_VD_MIDDLE_DETAILS);
|
||||
uint aimed_height = this->GetRoadVehDetailsHeight(v);
|
||||
uint aimed_height = this->GetRoadOrShipVehDetailsHeight(v);
|
||||
/* If the number of articulated parts changes, the size of the window must change too. */
|
||||
if (aimed_height != nwid_info->current_y) {
|
||||
this->ReInit();
|
||||
@@ -2839,16 +2839,17 @@ struct VehicleDetailsWindow : Window {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the desired height for the road vehicle details panel.
|
||||
* Gets the desired height for the road vehicle and ship details panel.
|
||||
* @param v Road vehicle being shown.
|
||||
* @return Desired height in pixels.
|
||||
*/
|
||||
uint GetRoadVehDetailsHeight(const Vehicle *v)
|
||||
uint GetRoadOrShipVehDetailsHeight(const Vehicle *v)
|
||||
{
|
||||
uint desired_height;
|
||||
if (v->HasArticulatedPart()) {
|
||||
if (v->Next() != nullptr) {
|
||||
/* An articulated RV has its text drawn under the sprite instead of after it, hence 15 pixels extra. */
|
||||
desired_height = ScaleGUITrad(15) + 4 * FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal * 2;
|
||||
desired_height = 4 * FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal * 2;
|
||||
if (v->type == VEH_ROAD) desired_height += ScaleGUITrad(15);
|
||||
/* Add space for the cargo amount for each part. */
|
||||
for (const Vehicle *u = v; u != nullptr; u = u->Next()) {
|
||||
if (u->cargo_cap != 0) desired_height += FONT_HEIGHT_NORMAL;
|
||||
@@ -2948,11 +2949,8 @@ struct VehicleDetailsWindow : Window {
|
||||
const Vehicle *v = Vehicle::Get(this->window_number);
|
||||
switch (v->type) {
|
||||
case VEH_ROAD:
|
||||
size->height = this->GetRoadVehDetailsHeight(v) + padding.height;
|
||||
break;
|
||||
|
||||
case VEH_SHIP:
|
||||
size->height = 5 * FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal * 2 + padding.height;
|
||||
size->height = this->GetRoadOrShipVehDetailsHeight(v) + padding.height;
|
||||
break;
|
||||
|
||||
case VEH_AIRCRAFT:
|
||||
@@ -3489,7 +3487,7 @@ static bool IsVehicleRefitable(const Vehicle *v)
|
||||
|
||||
do {
|
||||
if (IsEngineRefittable(v->engine_type)) return true;
|
||||
} while (v->IsGroundVehicle() && (v = v->Next()) != nullptr);
|
||||
} while (v->IsArticulatedCallbackVehicleType() && (v = v->Next()) != nullptr);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user