Initial support for multi-part (pseudo-articulated) ships

pull/507/head
Jonathan G Rennison 1 year ago
parent 5718730d68
commit 211fdd62b2

@ -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();
gcache->first_engine = v->engine_type; // Needs to be set before first callback
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:
Ship::From(v)->UpdateCache();
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,24 +69,85 @@ void DrawShipDetails(const Vehicle *v, const Rect &r)
DrawString(r.left, r.right, y, STR_VEHICLE_INFO_BUILT_VALUE);
y += FONT_HEIGHT_NORMAL;
SetDParam(0, v->cargo_type);
SetDParam(1, v->cargo_cap);
SetDParam(4, GetCargoSubtypeText(v));
DrawString(r.left, r.right, y, STR_VEHICLE_INFO_CAPACITY);
y += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
StringID str = STR_VEHICLE_DETAILS_CARGO_EMPTY;
if (v->cargo.StoredCount() > 0) {
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.StoredCount());
SetDParam(2, v->cargo.Source());
str = STR_VEHICLE_DETAILS_CARGO_FROM;
SetDParam(1, v->cargo_cap);
SetDParam(4, GetCargoSubtypeText(v));
DrawString(r.left, r.right, y, STR_VEHICLE_INFO_CAPACITY);
y += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_normal;
StringID str = STR_VEHICLE_DETAILS_CARGO_EMPTY;
if (v->cargo.StoredCount() > 0) {
SetDParam(0, v->cargo_type);
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;
}
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;
}

Loading…
Cancel
Save