diff --git a/docs/newgrf-additions-nml.html b/docs/newgrf-additions-nml.html
index 93f5e4d315..d1d9f40c5f 100644
--- a/docs/newgrf-additions-nml.html
+++ b/docs/newgrf-additions-nml.html
@@ -37,6 +37,7 @@
- Builtin functions
- Additional switch types
+ - Ship callbacks
- Railtype properties
- Railtype variables
- Roadtype properties
@@ -74,6 +75,15 @@
These require the more_varaction2_types feature. If this feature is not present, switches of these types will produce a CB_FAILED result.
+
+
+ The articulated_part callback is also available for ships.
+ Additional ship parts are not used for graphics, they are only used for additional cargo capacity, the default graphics chain is unused.
+ The default graphics chain for the primary vehicle may check the cargo states of the other ship parts if required.
+ Additional ship parts may be refitted individually.
+
This requires the multi_part_ships feature.
+
+
Property | Value range | Comment |
diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html
index f029e4c0d6..6c66438fe0 100644
--- a/docs/newgrf-additions.html
+++ b/docs/newgrf-additions.html
@@ -52,6 +52,7 @@
- Variational Action 2 - Railtypes
- Variational Action 2 - Objects
- Variational Action 2 - Signals (Feature 0E)
+ - Callbacks - Ships
- Action 3 - Objects
- Action 3 - Signals (Feature 0E)
- Action 14 - Type ID Mapping for Action 5
@@ -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 signals_define_style property.
Otherwise for signals using the default style, the value is 0.
- This is indicated by the feature name: action0_signals_style, version 1
+ This is indicated by the feature name: action0_signals_style, version 1.
+
+
+
+ Multi-part ships
+ Callback 16 - Articulated engine may also be used for ships.
+ This functions the same as for trains and road vehicles, and is enabled in the same way (bit 4 of ship property 12).
+ Additional ship parts are not used for graphics, they are only used for additional cargo capacity.
+ The graphics chain for the primary vehicle may check the cargo states of the other ship parts if required.
+ Additional ship parts may be refitted individually.
+
+ This is indicated by the feature name: multi_part_ships, version 1
diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp
index 7b035fe07e..a029c4b1a8 100644
--- a/src/articulated_vehicles.cpp
+++ b/src/articulated_vehicles.cpp
@@ -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();
diff --git a/src/engine.cpp b/src/engine.cpp
index 68c0a49a50..873c2eb4d0 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -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:
diff --git a/src/engine_base.h b/src/engine_base.h
index c9fd1d2ee4..9b9fb3aeb7 100644
--- a/src/engine_base.h
+++ b/src/engine_base.h
@@ -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.
diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp
index 91db2e0af1..ee9e2735ad 100644
--- a/src/newgrf_debug_gui.cpp
+++ b/src/newgrf_debug_gui.cpp
@@ -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(desc, wno, true);
w->SetCallerGRFID(grfid);
}
diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp
index 93a5ef2da9..617523b945 100644
--- a/src/newgrf_extension.cpp
+++ b/src/newgrf_extension.cpp
@@ -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(),
};
diff --git a/src/order_gui.cpp b/src/order_gui.cpp
index 679b22e62e..d7b5e0432f 100644
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -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;
}
diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp
index b775e28ba1..7144bf6215 100644
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -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;
diff --git a/src/ship.h b/src/ship.h
index 8e0fef4e42..97ad98aadd 100644
--- a/src/ship.h
+++ b/src/ship.h
@@ -43,7 +43,7 @@ struct Ship FINAL : public SpecializedVehicle {
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; }
diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp
index 7eb30f4893..500d1d9bbf 100644
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -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();
diff --git a/src/ship_gui.cpp b/src/ship_gui.cpp
index 492ba87436..7e5e6080e7 100644
--- a/src/ship_gui.cpp
+++ b/src/ship_gui.cpp
@@ -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;
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 0064bd9fb1..ccd719fd12 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -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);
}
}
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index 942c2cdb0b..caa88254f2 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -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
diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp
index 2d67f84dc4..5f631c63d8 100644
--- a/src/vehicle_cmd.cpp
+++ b/src/vehicle_cmd.cpp
@@ -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) {
/*
diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp
index 31c45d272d..8cbcdb1abd 100644
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -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(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;
}