summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/articulated_vehicles.cpp60
-rw-r--r--src/articulated_vehicles.h2
-rw-r--r--src/lang/english.txt3
-rw-r--r--src/newgrf_config.h1
-rw-r--r--src/roadveh_cmd.cpp5
-rw-r--r--src/train_cmd.cpp22
-rw-r--r--src/vehicle.cpp37
-rw-r--r--src/vehicle_func.h2
8 files changed, 114 insertions, 18 deletions
diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp
index 4729b5a55..2a6fb6b2c 100644
--- a/src/articulated_vehicles.cpp
+++ b/src/articulated_vehicles.cpp
@@ -9,6 +9,8 @@
#include "newgrf_engine.h"
#include "vehicle_func.h"
+#include "table/strings.h"
+
static const uint MAX_ARTICULATED_PARTS = 100; ///< Maximum of articulated parts per vehicle, i.e. when to abort calling the articulated vehicle callback.
uint CountArticulatedParts(EngineID engine_type, bool purchase_window)
@@ -238,6 +240,64 @@ bool IsArticulatedVehicleCarryingDifferentCargos(const Vehicle *v, CargoID *carg
return false;
}
+/**
+ * Checks whether the specs of freshly build articulated vehicles are consistent with the information specified in the purchase list.
+ * Only essential information is checked to leave room for magic tricks/workarounds to grfcoders.
+ * It checks:
+ * For autoreplace/-renew:
+ * - Default cargo type (without capacity)
+ * - intersection and union of refit masks.
+ */
+void CheckConsistencyOfArticulatedVehicle(const Vehicle *v)
+{
+ const Engine *engine = GetEngine(v->engine_type);
+
+ uint32 purchase_refit_union = GetUnionOfArticulatedRefitMasks(v->engine_type, v->type, true);
+ uint32 purchase_refit_intersection = GetIntersectionOfArticulatedRefitMasks(v->engine_type, v->type, true);
+ uint16 *purchase_default_capacity = GetCapacityOfArticulatedParts(v->engine_type, v->type);
+
+ uint32 real_refit_union = 0;
+ uint32 real_refit_intersection = UINT_MAX;
+ uint16 real_default_capacity[NUM_CARGO];
+ memset(real_default_capacity, 0, sizeof(real_default_capacity));
+
+ do {
+ uint32 refit_mask = GetAvailableVehicleCargoTypes(v->engine_type, v->type, true);
+ real_refit_union |= refit_mask;
+ if (refit_mask != 0) real_refit_intersection &= refit_mask;
+
+ assert(v->cargo_type < NUM_CARGO);
+ real_default_capacity[v->cargo_type] += v->cargo_cap;
+
+ switch (v->type) {
+ case VEH_TRAIN:
+ v = (EngineHasArticPart(v) ? GetNextArticPart(v) : NULL);
+ break;
+
+ case VEH_ROAD:
+ v = (RoadVehHasArticPart(v) ? v->Next() : NULL);
+ break;
+
+ default:
+ v = NULL;
+ break;
+ }
+ } while (v != NULL);
+
+ /* Check whether the vehicle carries more cargos than expected */
+ bool carries_more = false;
+ for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
+ if (real_default_capacity[cid] != 0 && purchase_default_capacity[cid] == 0) {
+ carries_more = true;
+ break;
+ }
+ }
+
+ /* show a warning once for each GRF after each game load */
+ if (real_refit_union != purchase_refit_union || real_refit_intersection != purchase_refit_intersection || carries_more) {
+ ShowNewGrfVehicleError(engine->index, STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_ARTICULATED_CARGO, GBUG_VEH_REFIT, false);
+ }
+}
void AddArticulatedParts(Vehicle **vl, VehicleType type)
{
diff --git a/src/articulated_vehicles.h b/src/articulated_vehicles.h
index 02e546364..b26cc2870 100644
--- a/src/articulated_vehicles.h
+++ b/src/articulated_vehicles.h
@@ -15,5 +15,7 @@ uint32 GetUnionOfArticulatedRefitMasks(EngineID engine, VehicleType type, bool i
uint32 GetIntersectionOfArticulatedRefitMasks(EngineID engine, VehicleType type, bool include_initial_cargo_type);
bool IsArticulatedVehicleCarryingDifferentCargos(const Vehicle *v, CargoID *cargo_type);
bool IsArticulatedVehicleRefittable(EngineID engine);
+void CheckConsistencyOfArticulatedVehicle(const Vehicle *v);
+
#endif /* ARTICULATED_VEHICLES_H */
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 588964860..739ea4c90 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -3170,6 +3170,9 @@ STR_NEWGRF_BROKEN :{WHITE}Behaviou
STR_NEWGRF_BROKEN_VEHICLE_LENGTH :{WHITE}It changes vehicle length for '{1:ENGINE}' when not inside a depot.
STR_BROKEN_VEHICLE_LENGTH :{WHITE}Train '{VEHICLE}' belonging to '{COMPANY}' has invalid length. It is probably caused by problems with NewGRFs. Game may desync or crash.
+STR_NEWGRF_BUGGY :{WHITE}NewGRF '{0:RAW_STRING}' provides incorrect information.
+STR_NEWGRF_BUGGY_ARTICULATED_CARGO :{WHITE}Cargo/refit information for '{1:ENGINE}' differs from purchase list after construction. This might cause autorenew/-replace to fail refitting correctly.
+
STR_LOADGAME_REMOVED_TRAMS :{WHITE}Game was saved in version without tram support. All trams have been removed.
STR_CURRENCY_WINDOW :{WHITE}Custom currency
diff --git a/src/newgrf_config.h b/src/newgrf_config.h
index 4dedd5b29..aba8dc330 100644
--- a/src/newgrf_config.h
+++ b/src/newgrf_config.h
@@ -31,6 +31,7 @@ enum GRFStatus {
/** Encountered GRF bugs */
enum GRFBugs {
GBUG_VEH_LENGTH, ///< Length of rail vehicle changes when not inside a depot
+ GBUG_VEH_REFIT, ///< Articulated vehicles carry different cargos resp. are differently refittable than specified in purchase list
};
/** Status of post-gameload GRF compatibility check */
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index fa68a9247..d7d5b927d 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -275,10 +275,13 @@ CommandCost CmdBuildRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
InvalidateWindow(WC_COMPANY, v->owner);
- if (IsLocalCompany())
+ if (IsLocalCompany()) {
InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Road window
+ }
GetCompany(_current_company)->num_engines[p1]++;
+
+ CheckConsistencyOfArticulatedVehicle(v);
}
return cost;
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index 8cccb6763..eede8507d 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -178,23 +178,7 @@ static void RailVehicleLengthChanged(const Vehicle *u)
uint32 grfid = engine->grffile->grfid;
GRFConfig *grfconfig = GetGRFConfig(grfid);
if (GamelogGRFBugReverse(grfid, engine->internal_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
- SetBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH);
- SetDParamStr(0, grfconfig->name);
- SetDParam(1, u->engine_type);
- ShowErrorMessage(STR_NEWGRF_BROKEN_VEHICLE_LENGTH, STR_NEWGRF_BROKEN, 0, 0);
-
- /* debug output */
- char buffer[512];
-
- SetDParamStr(0, grfconfig->name);
- GetString(buffer, STR_NEWGRF_BROKEN, lastof(buffer));
- DEBUG(grf, 0, "%s", buffer + 3);
-
- SetDParam(1, u->engine_type);
- GetString(buffer, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, lastof(buffer));
- DEBUG(grf, 0, "%s", buffer + 3);
-
- if (!_networking) _pause_game = -1;
+ ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
}
}
@@ -678,6 +662,8 @@ static CommandCost CmdBuildRailWagon(EngineID engine, TileIndex tile, DoCommandF
InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Train window
}
GetCompany(_current_company)->num_engines[engine]++;
+
+ CheckConsistencyOfArticulatedVehicle(v);
}
return value;
@@ -857,6 +843,8 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1,
}
GetCompany(_current_company)->num_engines[p1]++;
+
+ CheckConsistencyOfArticulatedVehicle(v);
}
return value;
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 0f62292b7..bdc6d0c6b 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -3,7 +3,9 @@
/** @file vehicle.cpp Base implementations of all vehicles. */
#include "stdafx.h"
+#include "gui.h"
#include "openttd.h"
+#include "debug.h"
#include "roadveh.h"
#include "ship.h"
#include "spritecache.h"
@@ -36,6 +38,7 @@
#include "core/smallmap_type.hpp"
#include "depot_func.h"
#include "settings_type.h"
+#include "network/network.h"
#include "table/sprites.h"
#include "table/strings.h"
@@ -101,6 +104,40 @@ bool Vehicle::NeedsAutomaticServicing() const
return NeedsServicing();
}
+/**
+ * Displays a "NewGrf Bug" error message for a engine, and pauses the game if not networking.
+ * @param engine The engine that caused the problem
+ * @param part1 Part 1 of the error message, taking the grfname as parameter 1
+ * @param part2 Part 2 of the error message, taking the engine as parameter 2
+ * @param bug_type Flag to check and set in grfconfig
+ * @param critical Shall the "OpenTTD might crash"-message be shown when the player tries to unpause?
+ */
+void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical)
+{
+ const Engine *e = GetEngine(engine);
+ uint32 grfid = e->grffile->grfid;
+ GRFConfig *grfconfig = GetGRFConfig(grfid);
+
+ if (!HasBit(grfconfig->grf_bugs, bug_type)) {
+ SetBit(grfconfig->grf_bugs, bug_type);
+ SetDParamStr(0, grfconfig->name);
+ SetDParam(1, engine);
+ ShowErrorMessage(part2, part1, 0, 0);
+ if (!_networking) _pause_game = (critical ? -1 : 1);
+ }
+
+ /* debug output */
+ char buffer[512];
+
+ SetDParamStr(0, grfconfig->name);
+ GetString(buffer, part1, lastof(buffer));
+ DEBUG(grf, 0, "%s", buffer + 3);
+
+ SetDParam(1, engine);
+ GetString(buffer, part2, lastof(buffer));
+ DEBUG(grf, 0, "%s", buffer + 3);
+}
+
StringID VehicleInTheWayErrMsg(const Vehicle *v)
{
switch (v->type) {
diff --git a/src/vehicle_func.h b/src/vehicle_func.h
index 4bbdb8f6b..0e9e1eded 100644
--- a/src/vehicle_func.h
+++ b/src/vehicle_func.h
@@ -14,6 +14,7 @@
#include "vehicle_type.h"
#include "engine_type.h"
#include "transport_type.h"
+#include "newgrf_config.h"
#define is_custom_sprite(x) (x >= 0xFD)
#define IS_CUSTOM_FIRSTHEAD_SPRITE(x) (x == 0xFD)
@@ -46,6 +47,7 @@ void ViewportAddVehicles(DrawPixelInfo *dpi);
SpriteID GetRotorImage(const Vehicle *v);
+void ShowNewGrfVehicleError(EngineID engine, StringID part1, StringID part2, GRFBugs bug_type, bool critical);
StringID VehicleInTheWayErrMsg(const Vehicle *v);
bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile, const Vehicle *ignore = NULL);