summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorpeter1138 <peter1138@openttd.org>2019-04-06 07:46:15 +0100
committerMichael Lutz <michi@icosahedron.de>2019-05-01 21:36:27 +0200
commitc02ef3e4564b7b54d49f0827d2d7625cbc38f335 (patch)
tree1c0ee62b6ce55124b247daaafa42300bfaa932e7 /src
parent21edf67f89c60351d5a0d84625455aa296b6b950 (diff)
downloadopenttd-c02ef3e4564b7b54d49f0827d2d7625cbc38f335.tar.xz
Feature: Add NotRoadTypes (NRT)
Diffstat (limited to 'src')
-rw-r--r--src/autoreplace_cmd.cpp4
-rw-r--r--src/autoreplace_gui.cpp171
-rw-r--r--src/bridge_gui.cpp21
-rw-r--r--src/bridge_map.h15
-rw-r--r--src/build_vehicle_gui.cpp65
-rw-r--r--src/command.cpp3
-rw-r--r--src/command_type.h1
-rw-r--r--src/company_base.h3
-rw-r--r--src/company_cmd.cpp28
-rw-r--r--src/company_gui.cpp86
-rw-r--r--src/economy.cpp4
-rw-r--r--src/engine.cpp14
-rw-r--r--src/engine_gui.cpp4
-rw-r--r--src/engine_type.h2
-rw-r--r--src/ground_vehicle.cpp2
-rw-r--r--src/ground_vehicle.hpp2
-rw-r--r--src/lang/english.txt43
-rw-r--r--src/misc_gui.cpp24
-rw-r--r--src/newgrf.cpp372
-rw-r--r--src/newgrf.h16
-rw-r--r--src/newgrf_engine.cpp21
-rw-r--r--src/newgrf_roadtype.cpp143
-rw-r--r--src/newgrf_roadtype.h51
-rw-r--r--src/openttd.cpp2
-rw-r--r--src/pathfinder/follow_track.hpp26
-rw-r--r--src/pathfinder/npf/npf.cpp50
-rw-r--r--src/pathfinder/yapf/yapf_costrail.hpp2
-rw-r--r--src/pathfinder/yapf/yapf_road.cpp8
-rw-r--r--src/rail_cmd.cpp44
-rw-r--r--src/rail_gui.cpp6
-rw-r--r--src/road.cpp226
-rw-r--r--src/road.h316
-rw-r--r--src/road_cmd.cpp1143
-rw-r--r--src/road_func.h88
-rw-r--r--src/road_gui.cpp486
-rw-r--r--src/road_gui.h6
-rw-r--r--src/road_internal.h4
-rw-r--r--src/road_map.cpp6
-rw-r--r--src/road_map.h257
-rw-r--r--src/road_type.h29
-rw-r--r--src/roadstop.cpp2
-rw-r--r--src/roadveh.h13
-rw-r--r--src/roadveh_cmd.cpp75
-rw-r--r--src/saveload/afterload.cpp91
-rw-r--r--src/saveload/company_sl.cpp23
-rw-r--r--src/saveload/saveload.h1
-rw-r--r--src/saveload/vehicle_sl.cpp16
-rw-r--r--src/script/api/ai/ai_rail.hpp.sq2
-rw-r--r--src/script/api/ai/ai_road.hpp.sq13
-rw-r--r--src/script/api/ai_changelog.hpp8
-rw-r--r--src/script/api/game/game_rail.hpp.sq2
-rw-r--r--src/script/api/game/game_road.hpp.sq13
-rw-r--r--src/script/api/game/game_window.hpp.sq8
-rw-r--r--src/script/api/game_changelog.hpp8
-rw-r--r--src/script/api/script_bridge.cpp6
-rw-r--r--src/script/api/script_engine.cpp2
-rw-r--r--src/script/api/script_infrastructure.cpp6
-rw-r--r--src/script/api/script_rail.hpp2
-rw-r--r--src/script/api/script_road.cpp84
-rw-r--r--src/script/api/script_road.hpp72
-rw-r--r--src/script/api/script_station.cpp6
-rw-r--r--src/script/api/script_tile.cpp15
-rw-r--r--src/script/api/script_tunnel.cpp2
-rw-r--r--src/script/api/script_vehicle.cpp2
-rw-r--r--src/script/api/script_window.hpp11
-rw-r--r--src/smallmap_gui.cpp61
-rw-r--r--src/station.cpp2
-rw-r--r--src/station_cmd.cpp276
-rw-r--r--src/station_func.h1
-rw-r--r--src/station_map.h23
-rw-r--r--src/table/engines.h2
-rw-r--r--src/table/newgrf_debug_data.h47
-rw-r--r--src/table/railtypes.h2
-rw-r--r--src/table/road_land.h29
-rw-r--r--src/table/roadtypes.h183
-rw-r--r--src/table/sprites.h13
-rw-r--r--src/tile_cmd.h5
-rw-r--r--src/toolbar_gui.cpp124
-rw-r--r--src/town.h2
-rw-r--r--src/town_cmd.cpp92
-rw-r--r--src/tunnel_map.h8
-rw-r--r--src/tunnelbridge.h13
-rw-r--r--src/tunnelbridge_cmd.cpp374
-rw-r--r--src/vehicle.cpp9
-rw-r--r--src/vehicle_func.h2
-rw-r--r--src/viewport_type.h1
-rw-r--r--src/widgets/autoreplace_widget.h4
-rw-r--r--src/widgets/company_widget.h2
-rw-r--r--src/widgets/road_widget.h2
-rw-r--r--src/widgets/toolbar_widget.h2
90 files changed, 4348 insertions, 1208 deletions
diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp
index 45cf83f30..99f14f218 100644
--- a/src/autoreplace_cmd.cpp
+++ b/src/autoreplace_cmd.cpp
@@ -20,6 +20,7 @@
#include "articulated_vehicles.h"
#include "core/random_func.hpp"
#include "vehiclelist.h"
+#include "road.h"
#include "table/strings.h"
@@ -74,6 +75,9 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
}
case VEH_ROAD:
+ /* make sure the roadtypes are compatible */
+ if ((GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes & GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes) == ROADTYPES_NONE) return false;
+
/* make sure that we do not replace a tram with a normal road vehicles or vice versa */
if (HasBit(e_from->info.misc_flags, EF_ROAD_TRAM) != HasBit(e_to->info.misc_flags, EF_ROAD_TRAM)) return false;
break;
diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp
index c00d97b91..a1a152cf6 100644
--- a/src/autoreplace_gui.cpp
+++ b/src/autoreplace_gui.cpp
@@ -14,6 +14,7 @@
#include "vehicle_gui.h"
#include "newgrf_engine.h"
#include "rail.h"
+#include "road.h"
#include "strings_func.h"
#include "window_func.h"
#include "autoreplace_func.h"
@@ -24,6 +25,7 @@
#include "settings_func.h"
#include "core/geometry_func.hpp"
#include "rail_gui.h"
+#include "road_gui.h"
#include "widgets/dropdown_func.h"
#include "widgets/autoreplace_widget.h"
@@ -86,6 +88,7 @@ class ReplaceVehicleWindow : public Window {
bool descending_sort_order; ///< Order of sorting vehicles.
bool show_hidden_engines; ///< Whether to show the hidden engines.
RailType sel_railtype; ///< Type of rail tracks selected. #INVALID_RAILTYPE to show all.
+ RoadType sel_roadtype; ///< Type of road selected. #INVALID_ROADTYPE to show all.
Scrollbar *vscroll[2];
/**
@@ -127,7 +130,21 @@ class ReplaceVehicleWindow : public Window {
FOR_ALL_ENGINES_OF_TYPE(e, type) {
if (!draw_left && !this->show_hidden_engines && e->IsHidden(_local_company)) continue;
EngineID eid = e->index;
- if (type == VEH_TRAIN && !this->GenerateReplaceRailList(eid, draw_left, this->replace_engines)) continue; // special rules for trains
+ switch (type) {
+ case VEH_TRAIN:
+ if (!this->GenerateReplaceRailList(eid, draw_left, this->replace_engines)) continue; // special rules for trains
+ break;
+
+ case VEH_ROAD:
+ if (draw_left && this->sel_roadtype != INVALID_ROADTYPE) {
+ /* Ensure that the roadtype is specific to the selected one */
+ if (e->u.road.roadtype != this->sel_roadtype) continue;
+ }
+ break;
+
+ default:
+ break;
+ }
if (draw_left) {
const uint num_engines = GetGroupNumEngines(_local_company, this->sel_group, eid);
@@ -210,6 +227,7 @@ public:
ReplaceVehicleWindow(WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc)
{
this->sel_railtype = INVALID_RAILTYPE;
+ this->sel_roadtype = INVALID_ROADTYPE;
this->replace_engines = true; // start with locomotives (all other vehicles will not read this bool)
this->engines[0].ForceRebuild();
this->engines[1].ForceRebuild();
@@ -229,6 +247,11 @@ public:
widget->SetLowered(this->show_hidden_engines);
this->FinishInitNested(vehicletype);
+ if (vehicletype == VEH_TRAIN || vehicletype == VEH_ROAD) {
+ widget = this->GetWidget<NWidgetCore>(WID_RV_RAIL_ROAD_TYPE_DROPDOWN);
+ widget->tool_tip = STR_REPLACE_HELP_RAILTYPE + vehicletype;
+ }
+
this->sort_criteria = _engine_sort_last_criteria[vehicletype];
this->descending_sort_order = _engine_sort_last_order[vehicletype];
this->owner = _local_company;
@@ -287,13 +310,28 @@ public:
break;
}
- case WID_RV_TRAIN_RAILTYPE_DROPDOWN: {
+ case WID_RV_RAIL_ROAD_TYPE_DROPDOWN: {
Dimension d = {0, 0};
- for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
- const RailtypeInfo *rti = GetRailTypeInfo(rt);
- /* Skip rail type if it has no label */
- if (rti->label == 0) continue;
- d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text));
+ switch (this->window_number) {
+ case VEH_TRAIN:
+ for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
+ const RailtypeInfo *rti = GetRailTypeInfo(rt);
+ /* Skip rail type if it has no label */
+ if (rti->label == 0) continue;
+ d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text));
+ }
+ break;
+
+ case VEH_ROAD:
+ for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+ /* Skip road type if it has no label */
+ if (rti->label == 0) continue;
+ d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text));
+ }
+ break;
+
+ default: NOT_REACHED();
}
d.width += padding.width;
d.height += padding.height;
@@ -409,9 +447,18 @@ public:
* or The selected vehicle has no replacement set up */
this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == INVALID_ENGINE || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group));
- if (this->window_number == VEH_TRAIN) {
- /* Show the selected railtype in the pulldown menu */
- this->GetWidget<NWidgetCore>(WID_RV_TRAIN_RAILTYPE_DROPDOWN)->widget_data = sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(sel_railtype)->strings.replace_text;
+ switch (this->window_number) {
+ case VEH_TRAIN:
+ /* Show the selected railtype in the pulldown menu */
+ this->GetWidget<NWidgetCore>(WID_RV_RAIL_ROAD_TYPE_DROPDOWN)->widget_data = sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(sel_railtype)->strings.replace_text;
+ break;
+
+ case VEH_ROAD:
+ /* Show the selected roadtype in the pulldown menu */
+ this->GetWidget<NWidgetCore>(WID_RV_RAIL_ROAD_TYPE_DROPDOWN)->widget_data = sel_roadtype == INVALID_ROADTYPE ? STR_REPLACE_ALL_ROADTYPE : GetRoadTypeInfo(sel_roadtype)->strings.replace_text;
+ break;
+
+ default: break;
}
this->DrawWidgets();
@@ -472,8 +519,16 @@ public:
break;
}
- case WID_RV_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu
- ShowDropDownList(this, GetRailTypeDropDownList(true, true), sel_railtype, WID_RV_TRAIN_RAILTYPE_DROPDOWN);
+ case WID_RV_RAIL_ROAD_TYPE_DROPDOWN: // Rail/roadtype selection dropdown menu
+ switch (this->window_number) {
+ case VEH_TRAIN:
+ ShowDropDownList(this, GetRailTypeDropDownList(true, true), sel_railtype, WID_RV_RAIL_ROAD_TYPE_DROPDOWN);
+ break;
+
+ case VEH_ROAD:
+ ShowDropDownList(this, GetRoadTypeDropDownList(RTTB_ROAD | RTTB_TRAM, true, true), sel_roadtype, WID_RV_RAIL_ROAD_TYPE_DROPDOWN);
+ break;
+ }
break;
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length
@@ -533,10 +588,25 @@ public:
}
break;
- case WID_RV_TRAIN_RAILTYPE_DROPDOWN: {
- RailType temp = (RailType)index;
- if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
- sel_railtype = temp;
+ case WID_RV_RAIL_ROAD_TYPE_DROPDOWN:
+ switch (this->window_number) {
+ case VEH_TRAIN: {
+ RailType temp = (RailType)index;
+ if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
+ sel_railtype = temp;
+ break;
+ }
+
+ case VEH_ROAD: {
+ RoadType temp = (RoadType)index;
+ if (temp == sel_roadtype) return; // we didn't select a new one. No need to change anything
+ sel_roadtype = temp;
+ break;
+ }
+
+ default: NOT_REACHED();
+ }
+
/* Reset scrollbar positions */
this->vscroll[0]->SetPosition(0);
this->vscroll[1]->SetPosition(0);
@@ -546,7 +616,6 @@ public:
this->reset_sel_engine = true;
this->SetDirty();
break;
- }
case WID_RV_TRAIN_ENGINEWAGON_DROPDOWN: {
this->replace_engines = index != 0;
@@ -603,7 +672,7 @@ static const NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(NWID_VERTICAL),
NWidget(NWID_HORIZONTAL),
- NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0),
+ NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_RAIL_ROAD_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN), SetDataTip(STR_BLACK_STRING, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
@@ -648,6 +717,64 @@ static WindowDesc _replace_rail_vehicle_desc(
_nested_replace_rail_vehicle_widgets, lengthof(_nested_replace_rail_vehicle_widgets)
);
+static const NWidgetPart _nested_replace_road_vehicle_widgets[] = {
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_CLOSEBOX, COLOUR_GREY),
+ NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_SHADEBOX, COLOUR_GREY),
+ NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
+ NWidget(WWT_STICKYBOX, COLOUR_GREY),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
+ NWidget(WWT_PANEL, COLOUR_GREY),
+ NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
+ EndContainer(),
+ NWidget(WWT_PANEL, COLOUR_GREY),
+ NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
+ EndContainer(),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
+ NWidget(NWID_VERTICAL),
+ NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_RAIL_ROAD_TYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0),
+ NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
+ EndContainer(),
+ NWidget(NWID_VERTICAL),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1),
+ NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP),
+ NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
+ EndContainer(),
+ EndContainer(),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
+ NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR),
+ NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_LEFT_SCROLLBAR),
+ NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR),
+ NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_RV_RIGHT_SCROLLBAR),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
+ NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
+ NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
+ NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0),
+ EndContainer(),
+ NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
+ NWidget(WWT_RESIZEBOX, COLOUR_GREY),
+ EndContainer(),
+};
+
+static WindowDesc _replace_road_vehicle_desc(
+ WDP_AUTO, "replace_vehicle_road", 500, 140,
+ WC_REPLACE_VEHICLE, WC_NONE,
+ WDF_CONSTRUCTION,
+ _nested_replace_road_vehicle_widgets, lengthof(_nested_replace_road_vehicle_widgets)
+);
+
static const NWidgetPart _nested_replace_vehicle_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
@@ -710,5 +837,11 @@ static WindowDesc _replace_vehicle_desc(
void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
{
DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
- new ReplaceVehicleWindow(vehicletype == VEH_TRAIN ? &_replace_rail_vehicle_desc : &_replace_vehicle_desc, vehicletype, id_g);
+ WindowDesc *desc;
+ switch (vehicletype) {
+ case VEH_TRAIN: desc = &_replace_rail_vehicle_desc; break;
+ case VEH_ROAD: desc = &_replace_road_vehicle_desc; break;
+ default: desc = &_replace_vehicle_desc; break;
+ }
+ new ReplaceVehicleWindow(desc, vehicletype, id_g);
}
diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp
index b11290ae4..710c591ca 100644
--- a/src/bridge_gui.cpp
+++ b/src/bridge_gui.cpp
@@ -13,6 +13,7 @@
#include "error.h"
#include "command_func.h"
#include "rail.h"
+#include "road.h"
#include "strings_func.h"
#include "window_func.h"
#include "sound_func.h"
@@ -403,11 +404,25 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
Money infra_cost = 0;
switch (transport_type) {
- case TRANSPORT_ROAD:
- infra_cost = (bridge_len + 2) * _price[PR_BUILD_ROAD] * 2;
+ case TRANSPORT_ROAD: {
/* In case we add a new road type as well, we must be aware of those costs. */
- if (IsBridgeTile(start)) infra_cost *= CountBits(GetRoadTypes(start) | (RoadTypes)road_rail_type);
+ RoadType road_rt = INVALID_ROADTYPE;
+ RoadType tram_rt = INVALID_ROADTYPE;
+ if (IsBridgeTile(start)) {
+ road_rt = GetRoadTypeRoad(start);
+ tram_rt = GetRoadTypeTram(start);
+ }
+ if (RoadTypeIsRoad((RoadType)road_rail_type)) {
+ road_rt = (RoadType)road_rail_type;
+ } else {
+ tram_rt = (RoadType)road_rail_type;
+ }
+
+ if (road_rt != INVALID_ROADTYPE) infra_cost += (bridge_len + 2) * 2 * RoadBuildCost(road_rt);
+ if (tram_rt != INVALID_ROADTYPE) infra_cost += (bridge_len + 2) * 2 * RoadBuildCost(tram_rt);
+
break;
+ }
case TRANSPORT_RAIL: infra_cost = (bridge_len + 2) * RailBuildCost((RailType)road_rail_type); break;
default: break;
}
diff --git a/src/bridge_map.h b/src/bridge_map.h
index 4e70238c2..1461df13a 100644
--- a/src/bridge_map.h
+++ b/src/bridge_map.h
@@ -132,11 +132,11 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D
SetTileOwner(t, o);
_m[t].m2 = 0;
_m[t].m3 = 0;
- _m[t].m4 = 0;
+ _m[t].m4 = INVALID_ROADTYPE;
_m[t].m5 = 1 << 7 | tt << 2 | d;
SB(_me[t].m6, 2, 4, bridgetype);
_me[t].m7 = 0;
- _me[t].m8 = 0;
+ _me[t].m8 = INVALID_ROADTYPE << 6;
}
/**
@@ -147,14 +147,15 @@ static inline void MakeBridgeRamp(TileIndex t, Owner o, BridgeType bridgetype, D
* @param owner_tram the new owner of the tram on the bridge
* @param bridgetype the type of bridge this bridge ramp belongs to
* @param d the direction this ramp must be facing
- * @param rts the road types of the bridge
+ * @param road_rt the road type of the bridge
+ * @param tram_rt the tram type of the bridge
*/
-static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadTypes rts)
+static inline void MakeRoadBridgeRamp(TileIndex t, Owner o, Owner owner_road, Owner owner_tram, BridgeType bridgetype, DiagDirection d, RoadType road_rt, RoadType tram_rt)
{
MakeBridgeRamp(t, o, bridgetype, d, TRANSPORT_ROAD);
- SetRoadOwner(t, ROADTYPE_ROAD, owner_road);
- if (owner_tram != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, owner_tram);
- SetRoadTypes(t, rts);
+ SetRoadOwner(t, RTT_ROAD, owner_road);
+ if (owner_tram != OWNER_TOWN) SetRoadOwner(t, RTT_TRAM, owner_tram);
+ SetRoadTypes(t, road_rt, tram_rt);
}
/**
diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp
index 32310b9c3..ba3eaf4d0 100644
--- a/src/build_vehicle_gui.cpp
+++ b/src/build_vehicle_gui.cpp
@@ -1014,8 +1014,8 @@ void DisplayVehicleSortDropDown(Window *w, VehicleType vehicle_type, int selecte
struct BuildVehicleWindow : Window {
VehicleType vehicle_type; ///< Type of vehicles shown in the window.
union {
- RailType railtype; ///< Rail type to show, or #RAILTYPE_END.
- RoadTypes roadtypes; ///< Road type to show, or #ROADTYPES_ALL.
+ RailType railtype; ///< Rail type to show, or #INVALID_RAILTYPE.
+ RoadType roadtype; ///< Road type to show, or #INVALID_ROADTYPE.
} filter; ///< Filter to apply.
bool descending_sort_order; ///< Sort direction, @see _engine_sort_direction
byte sort_criteria; ///< Current sort criterium.
@@ -1050,7 +1050,8 @@ struct BuildVehicleWindow : Window {
BuildVehicleWindow(WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc)
{
this->vehicle_type = type;
- this->window_number = tile == INVALID_TILE ? (int)type : tile;
+ this->listview_mode = tile == INVALID_TILE;
+ this->window_number = this->listview_mode ? (int)type : tile;
this->sel_engine = INVALID_ENGINE;
@@ -1058,19 +1059,7 @@ struct BuildVehicleWindow : Window {
this->descending_sort_order = _engine_sort_last_order[type];
this->show_hidden_engines = _engine_sort_show_hidden_engines[type];
- switch (type) {
- default: NOT_REACHED();
- case VEH_TRAIN:
- this->filter.railtype = (tile == INVALID_TILE) ? RAILTYPE_END : GetRailType(tile);
- break;
- case VEH_ROAD:
- this->filter.roadtypes = (tile == INVALID_TILE) ? ROADTYPES_ALL : GetRoadTypes(tile);
- case VEH_SHIP:
- case VEH_AIRCRAFT:
- break;
- }
-
- this->listview_mode = (this->window_number <= VEH_END);
+ this->UpdateFilterByTile();
this->CreateNestedTree();
@@ -1114,6 +1103,36 @@ struct BuildVehicleWindow : Window {
}
}
+ /** Set the filter type according to the depot type */
+ void UpdateFilterByTile()
+ {
+ switch (this->vehicle_type) {
+ default: NOT_REACHED();
+ case VEH_TRAIN:
+ if (this->listview_mode) {
+ this->filter.railtype = INVALID_RAILTYPE;
+ } else {
+ this->filter.railtype = GetRailType(this->window_number);
+ }
+ break;
+
+ case VEH_ROAD:
+ if (this->listview_mode) {
+ this->filter.roadtype = INVALID_ROADTYPE;
+ } else {
+ this->filter.roadtype = GetRoadTypeRoad(this->window_number);
+ if (this->filter.roadtype == INVALID_ROADTYPE) {
+ this->filter.roadtype = GetRoadTypeTram(this->window_number);
+ }
+ }
+ break;
+
+ case VEH_SHIP:
+ case VEH_AIRCRAFT:
+ break;
+ }
+ }
+
/** Populate the filter list and set the cargo filter criteria. */
void SetCargoFilterArray()
{
@@ -1223,8 +1242,6 @@ struct BuildVehicleWindow : Window {
int num_engines = 0;
int num_wagons = 0;
- this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number);
-
this->eng_list.clear();
/* Make list of all available train engines and wagons.
@@ -1237,7 +1254,7 @@ struct BuildVehicleWindow : Window {
EngineID eid = e->index;
const RailVehicleInfo *rvi = &e->u.rail;
- if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
+ if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
/* Filter now! So num_engines and num_wagons is valid */
@@ -1280,7 +1297,8 @@ struct BuildVehicleWindow : Window {
if (!this->show_hidden_engines && e->IsHidden(_local_company)) continue;
EngineID eid = e->index;
if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
- if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue;
+ if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue;
+
this->eng_list.push_back(eid);
if (eid == this->sel_engine) sel_id = eid;
@@ -1338,6 +1356,10 @@ struct BuildVehicleWindow : Window {
void GenerateBuildList()
{
if (!this->eng_list.NeedRebuild()) return;
+
+ /* Update filter type in case the road/railtype of the depot got converted */
+ this->UpdateFilterByTile();
+
switch (this->vehicle_type) {
default: NOT_REACHED();
case VEH_TRAIN:
@@ -1460,6 +1482,9 @@ struct BuildVehicleWindow : Window {
if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
SetDParam(0, rti->strings.build_caption);
+ } else if (this->vehicle_type == VEH_ROAD && !this->listview_mode) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(this->filter.roadtype);
+ SetDParam(0, rti->strings.build_caption);
} else {
SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
}
diff --git a/src/command.cpp b/src/command.cpp
index 8a92c3478..d81e639b0 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -68,6 +68,8 @@ CommandProc CmdBuildRoad;
CommandProc CmdBuildRoadDepot;
+CommandProc CmdConvertRoad;
+
CommandProc CmdBuildAirport;
CommandProc CmdBuildDock;
@@ -235,6 +237,7 @@ static const Command _command_proc_table[] = {
DEF_CMD(CmdRemoveLongRoad, CMD_NO_TEST | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_LONG_ROAD; towns may disallow removing road bits (as they are connected) in test, but in exec they're removed and thus removing is allowed.
DEF_CMD(CmdBuildRoad, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD
DEF_CMD(CmdBuildRoadDepot, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_ROAD_DEPOT
+ DEF_CMD(CmdConvertRoad, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_CONVERT_ROAD
DEF_CMD(CmdBuildAirport, CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_AIRPORT
DEF_CMD(CmdBuildDock, CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_BUILD_DOCK
diff --git a/src/command_type.h b/src/command_type.h
index 95b6c0d62..e24467b0a 100644
--- a/src/command_type.h
+++ b/src/command_type.h
@@ -202,6 +202,7 @@ enum Commands {
CMD_REMOVE_LONG_ROAD, ///< remove a complete road (not a "half" one)
CMD_BUILD_ROAD, ///< build a "half" road
CMD_BUILD_ROAD_DEPOT, ///< build a road depot
+ CMD_CONVERT_ROAD, ///< convert a road type
CMD_BUILD_AIRPORT, ///< build an airport
diff --git a/src/company_base.h b/src/company_base.h
index 27ae96fce..728d4ffec 100644
--- a/src/company_base.h
+++ b/src/company_base.h
@@ -43,6 +43,9 @@ struct CompanyInfrastructure {
for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) total += this->rail[rt];
return total;
}
+
+ uint32 GetRoadTotal() const;
+ uint32 GetTramTotal() const;
};
typedef Pool<Company, CompanyID, 1, MAX_COMPANIES> CompanyPool;
diff --git a/src/company_cmd.cpp b/src/company_cmd.cpp
index fe4163acd..af7913e2c 100644
--- a/src/company_cmd.cpp
+++ b/src/company_cmd.cpp
@@ -562,7 +562,7 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY)
c->share_owners[0] = c->share_owners[1] = c->share_owners[2] = c->share_owners[3] = INVALID_OWNER;
c->avail_railtypes = GetCompanyRailtypes(c->index);
- c->avail_roadtypes = GetCompanyRoadtypes(c->index);
+ c->avail_roadtypes = GetCompanyRoadTypes(c->index);
c->inaugurated_year = _cur_year;
RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false, false); // create a random company manager face
@@ -1165,3 +1165,29 @@ int CompanyServiceInterval(const Company *c, VehicleType type)
case VEH_SHIP: return vds->servint_ships;
}
}
+
+/**
+ * Get total sum of all owned road bits.
+ * @return Combined total road road bits.
+ */
+uint32 CompanyInfrastructure::GetRoadTotal() const
+{
+ uint32 total = 0;
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ if (RoadTypeIsRoad(rt)) total += this->road[rt];
+ }
+ return total;
+}
+
+/**
+ * Get total sum of all owned tram bits.
+ * @return Combined total of tram road bits.
+ */
+uint32 CompanyInfrastructure::GetTramTotal() const
+{
+ uint32 total = 0;
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ if (RoadTypeIsTram(rt)) total += this->road[rt];
+ }
+ return total;
+}
diff --git a/src/company_gui.cpp b/src/company_gui.cpp
index b36ab959e..eaa7ad55a 100644
--- a/src/company_gui.cpp
+++ b/src/company_gui.cpp
@@ -30,6 +30,7 @@
#include "core/geometry_func.hpp"
#include "object_type.h"
#include "rail.h"
+#include "road.h"
#include "engine_base.h"
#include "window_func.h"
#include "road_func.h"
@@ -1783,6 +1784,10 @@ static const NWidgetPart _nested_company_infrastructure_widgets[] = {
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_ROAD_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
EndContainer(),
NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
+ NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
+ NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_TRAM_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_DESC), SetMinimalTextLines(2, 0), SetFill(1, 0),
NWidget(WWT_EMPTY, COLOUR_GREY, WID_CI_WATER_COUNT), SetMinimalTextLines(2, 0), SetFill(0, 1),
EndContainer(),
@@ -1819,7 +1824,7 @@ struct CompanyInfrastructureWindow : Window
void UpdateRailRoadTypes()
{
this->railtypes = RAILTYPES_NONE;
- this->roadtypes = ROADTYPES_ROAD; // Road is always available.
+ this->roadtypes = ROADTYPES_NONE;
/* Find the used railtypes. */
Engine *e;
@@ -1832,14 +1837,16 @@ struct CompanyInfrastructureWindow : Window
/* Get the date introduced railtypes as well. */
this->railtypes = AddDateIntroducedRailTypes(this->railtypes, MAX_DAY);
- /* Tram is only visible when there will be a tram. */
+ /* Find the used roadtypes. */
FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
- if (!HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue;
- this->roadtypes |= ROADTYPES_TRAM;
- break;
+ this->roadtypes |= GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes;
}
+
+ /* Get the date introduced roadtypes as well. */
+ this->roadtypes = AddDateIntroducedRoadTypes(this->roadtypes, MAX_DAY);
+ this->roadtypes &= ~_roadtypes_hidden_mask;
}
/** Get total infrastructure maintenance cost. */
@@ -1854,8 +1861,11 @@ struct CompanyInfrastructureWindow : Window
}
total += SignalMaintenanceCost(c->infrastructure.signal);
- if (HasBit(this->roadtypes, ROADTYPE_ROAD)) total += RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD]);
- if (HasBit(this->roadtypes, ROADTYPE_TRAM)) total += RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM]);
+ uint32 road_total = c->infrastructure.GetRoadTotal();
+ uint32 tram_total = c->infrastructure.GetTramTotal();
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ if (HasBit(this->roadtypes, rt)) total += RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total);
+ }
total += CanalMaintenanceCost(c->infrastructure.water);
total += StationMaintenanceCost(c->infrastructure.station);
@@ -1883,7 +1893,8 @@ struct CompanyInfrastructureWindow : Window
size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT).width);
- for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
+ RailType rt;
+ FOR_ALL_SORTED_RAILTYPES(rt) {
if (HasBit(this->railtypes, rt)) {
lines++;
SetDParam(0, GetRailTypeInfo(rt)->strings.name);
@@ -1899,18 +1910,19 @@ struct CompanyInfrastructureWindow : Window
break;
}
- case WID_CI_ROAD_DESC: {
- uint lines = 1;
+ case WID_CI_ROAD_DESC:
+ case WID_CI_TRAM_DESC: {
+ uint lines = 0;
- size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT).width);
+ size->width = max(size->width, GetStringBoundingBox(widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT).width);
- if (HasBit(this->roadtypes, ROADTYPE_ROAD)) {
- lines++;
- size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD).width + WD_FRAMERECT_LEFT);
- }
- if (HasBit(this->roadtypes, ROADTYPE_TRAM)) {
- lines++;
- size->width = max(size->width, GetStringBoundingBox(STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY).width + WD_FRAMERECT_LEFT);
+ RoadType rt;
+ FOR_ALL_SORTED_ROADTYPES(rt) {
+ if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
+ lines++;
+ SetDParam(0, GetRoadTypeInfo(rt)->strings.name);
+ size->width = max(size->width, GetStringBoundingBox(STR_WHITE_STRING).width + WD_FRAMERECT_LEFT);
+ }
}
size->height = max(size->height, lines * FONT_HEIGHT_NORMAL);
@@ -1930,6 +1942,7 @@ struct CompanyInfrastructureWindow : Window
case WID_CI_RAIL_COUNT:
case WID_CI_ROAD_COUNT:
+ case WID_CI_TRAM_COUNT:
case WID_CI_WATER_COUNT:
case WID_CI_STATION_COUNT:
case WID_CI_TOTAL: {
@@ -1943,9 +1956,12 @@ struct CompanyInfrastructureWindow : Window
}
max_val = max(max_val, c->infrastructure.signal);
max_cost = max(max_cost, SignalMaintenanceCost(c->infrastructure.signal));
+ uint32 road_total = c->infrastructure.GetRoadTotal();
+ uint32 tram_total = c->infrastructure.GetTramTotal();
for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
max_val = max(max_val, c->infrastructure.road[rt]);
- max_cost = max(max_cost, RoadMaintenanceCost(rt, c->infrastructure.road[rt]));
+ max_cost = max(max_cost, RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total));
+
}
max_val = max(max_val, c->infrastructure.water);
max_cost = max(max_cost, CanalMaintenanceCost(c->infrastructure.water));
@@ -2041,26 +2057,32 @@ struct CompanyInfrastructureWindow : Window
}
case WID_CI_ROAD_DESC:
- DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT);
-
- if (this->roadtypes != ROADTYPES_NONE) {
- if (HasBit(this->roadtypes, ROADTYPE_ROAD)) DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD);
- if (HasBit(this->roadtypes, ROADTYPE_TRAM)) DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY);
- } else {
- /* No valid roadtypes. */
- DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_COMPANY_VIEW_INFRASTRUCTURE_NONE);
+ case WID_CI_TRAM_DESC: {
+ DrawString(r.left, r.right, y, widget == WID_CI_ROAD_DESC ? STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT : STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT);
+
+ /* Draw name of each valid roadtype. */
+ RoadType rt;
+ FOR_ALL_SORTED_ROADTYPES(rt) {
+ if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_DESC)) {
+ SetDParam(0, GetRoadTypeInfo(rt)->strings.name);
+ DrawString(r.left + offs_left, r.right - offs_right, y += FONT_HEIGHT_NORMAL, STR_WHITE_STRING);
+ }
}
break;
+ }
case WID_CI_ROAD_COUNT:
- if (HasBit(this->roadtypes, ROADTYPE_ROAD)) {
- this->DrawCountLine(r, y, c->infrastructure.road[ROADTYPE_ROAD], RoadMaintenanceCost(ROADTYPE_ROAD, c->infrastructure.road[ROADTYPE_ROAD]));
- }
- if (HasBit(this->roadtypes, ROADTYPE_TRAM)) {
- this->DrawCountLine(r, y, c->infrastructure.road[ROADTYPE_TRAM], RoadMaintenanceCost(ROADTYPE_TRAM, c->infrastructure.road[ROADTYPE_TRAM]));
+ case WID_CI_TRAM_COUNT: {
+ uint32 road_tram_total = widget == WID_CI_ROAD_COUNT ? c->infrastructure.GetRoadTotal() : c->infrastructure.GetTramTotal();
+ RoadType rt;
+ FOR_ALL_SORTED_ROADTYPES(rt) {
+ if (HasBit(this->roadtypes, rt) && RoadTypeIsRoad(rt) == (widget == WID_CI_ROAD_COUNT)) {
+ this->DrawCountLine(r, y, c->infrastructure.road[rt], RoadMaintenanceCost(rt, c->infrastructure.road[rt], road_tram_total));
+ }
}
break;
+ }
case WID_CI_WATER_DESC:
DrawString(r.left, r.right, y, STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT);
diff --git a/src/economy.cpp b/src/economy.cpp
index ad48ce25e..b6778f9e5 100644
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -685,8 +685,10 @@ static void CompaniesGenStatistics()
if (c->infrastructure.rail[rt] != 0) cost.AddCost(RailMaintenanceCost(rt, c->infrastructure.rail[rt], rail_total));
}
cost.AddCost(SignalMaintenanceCost(c->infrastructure.signal));
+ uint32 road_total = c->infrastructure.GetRoadTotal();
+ uint32 tram_total = c->infrastructure.GetTramTotal();
for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
- if (c->infrastructure.road[rt] != 0) cost.AddCost(RoadMaintenanceCost(rt, c->infrastructure.road[rt]));
+ if (c->infrastructure.road[rt] != 0) cost.AddCost(RoadMaintenanceCost(rt, c->infrastructure.road[rt], RoadTypeIsRoad(rt) ? road_total : tram_total));
}
cost.AddCost(CanalMaintenanceCost(c->infrastructure.water));
cost.AddCost(StationMaintenanceCost(c->infrastructure.station));
diff --git a/src/engine.cpp b/src/engine.cpp
index 3e2a7d2d7..f9363b601 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -708,7 +708,7 @@ void StartupEngines()
Company *c;
FOR_ALL_COMPANIES(c) {
c->avail_railtypes = GetCompanyRailtypes(c->index);
- c->avail_roadtypes = GetCompanyRoadtypes(c->index);
+ c->avail_roadtypes = GetCompanyRoadTypes(c->index);
}
/* Invalidate any open purchase lists */
@@ -730,7 +730,8 @@ static void AcceptEnginePreview(EngineID eid, CompanyID company)
assert(e->u.rail.railtype < RAILTYPE_END);
c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date);
} else if (e->type == VEH_ROAD) {
- SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
+ assert(e->u.road.roadtype < ROADTYPE_END);
+ c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date);
}
e->preview_company = INVALID_COMPANY;
@@ -810,6 +811,7 @@ void EnginesDailyLoop()
Company *c;
FOR_ALL_COMPANIES(c) {
c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, _date);
+ c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes, _date);
}
if (_cur_year >= _year_engine_aging_stops) return;
@@ -951,7 +953,8 @@ static void NewVehicleAvailable(Engine *e)
FOR_ALL_COMPANIES(c) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date);
} else if (e->type == VEH_ROAD) {
/* maybe make another road type available */
- FOR_ALL_COMPANIES(c) SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
+ assert(e->u.road.roadtype < ROADTYPE_END);
+ FOR_ALL_COMPANIES(c) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, _date);
}
/* Only broadcast event if AIs are able to build this vehicle type. */
@@ -1098,6 +1101,11 @@ bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
const Company *c = Company::Get(company);
if (((GetRailTypeInfo(e->u.rail.railtype))->compatible_railtypes & c->avail_railtypes) == 0) return false;
}
+ if (type == VEH_ROAD && company != OWNER_DEITY) {
+ /* Check if the road type is available to this company */
+ const Company *c = Company::Get(company);
+ if ((GetRoadTypeInfo(e->u.road.roadtype)->powered_roadtypes & c->avail_roadtypes) == ROADTYPES_NONE) return false;
+ }
return true;
}
diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp
index eb14b5d8a..90cba6ed1 100644
--- a/src/engine_gui.cpp
+++ b/src/engine_gui.cpp
@@ -19,6 +19,7 @@
#include "vehicle_func.h"
#include "company_func.h"
#include "rail.h"
+#include "road.h"
#include "settings_type.h"
#include "train.h"
#include "roadveh.h"
@@ -41,7 +42,8 @@ StringID GetEngineCategoryName(EngineID engine)
const Engine *e = Engine::Get(engine);
switch (e->type) {
default: NOT_REACHED();
- case VEH_ROAD: return STR_ENGINE_PREVIEW_ROAD_VEHICLE;
+ case VEH_ROAD:
+ return GetRoadTypeInfo(e->u.road.roadtype)->strings.new_engine;
case VEH_AIRCRAFT: return STR_ENGINE_PREVIEW_AIRCRAFT;
case VEH_SHIP: return STR_ENGINE_PREVIEW_SHIP;
case VEH_TRAIN:
diff --git a/src/engine_type.h b/src/engine_type.h
index 1cc0452a5..6cc8dc68e 100644
--- a/src/engine_type.h
+++ b/src/engine_type.h
@@ -14,6 +14,7 @@
#include "economy_type.h"
#include "rail_type.h"
+#include "road_type.h"
#include "cargo_type.h"
#include "date_type.h"
#include "sound_type.h"
@@ -123,6 +124,7 @@ struct RoadVehicleInfo {
uint8 air_drag; ///< Coefficient of air drag
byte visual_effect; ///< Bitstuffed NewGRF visual effect data
byte shorten_factor; ///< length on main map for this type is 8 - shorten_factor
+ RoadType roadtype; ///< Road type
};
/**
diff --git a/src/ground_vehicle.cpp b/src/ground_vehicle.cpp
index b94fb0e7b..c269aeb79 100644
--- a/src/ground_vehicle.cpp
+++ b/src/ground_vehicle.cpp
@@ -28,7 +28,7 @@ void GroundVehicle<T, Type>::PowerChanged()
uint32 total_power = 0;
uint32 max_te = 0;
uint32 number_of_parts = 0;
- uint16 max_track_speed = v->GetDisplayMaxSpeed();
+ uint16 max_track_speed = this->vcache.cached_max_speed; // Max track speed in internal units.
for (const T *u = v; u != nullptr; u = u->Next()) {
uint32 current_power = u->GetPower() + u->GetPoweredPartPower(u);
diff --git a/src/ground_vehicle.hpp b/src/ground_vehicle.hpp
index 109257fe2..526865134 100644
--- a/src/ground_vehicle.hpp
+++ b/src/ground_vehicle.hpp
@@ -36,7 +36,7 @@ struct GroundVehicleCache {
uint16 cached_axle_resistance; ///< Resistance caused by the axles of the vehicle (valid only for the first engine).
/* Cached acceleration values, recalculated on load and each time a vehicle is added to/removed from the consist. */
- uint16 cached_max_track_speed; ///< Maximum consist speed limited by track type (valid only for the first engine).
+ uint16 cached_max_track_speed; ///< Maximum consist speed (in internal units) limited by track type (valid only for the first engine).
uint32 cached_power; ///< Total power of the consist (valid only for the first engine).
uint32 cached_air_drag; ///< Air drag coefficient of the vehicle (valid only for the first engine).
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 83a28d8cd..9641ecc04 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -341,6 +341,7 @@ STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN :{BLACK}Zoom the
STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT :{BLACK}Zoom the view out
STR_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Build railway track
STR_TOOLBAR_TOOLTIP_BUILD_ROADS :{BLACK}Build roads
+STR_TOOLBAR_TOOLTIP_BUILD_TRAMWAYS :{BLACK}Build tramways
STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS :{BLACK}Build ship docks
STR_TOOLBAR_TOOLTIP_BUILD_AIRPORTS :{BLACK}Build airports
STR_TOOLBAR_TOOLTIP_LANDSCAPING :{BLACK}Open the landscaping toolbar to raise/lower land, plant trees, etc.
@@ -361,6 +362,7 @@ STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION :{BLACK}Landscap
STR_SCENEDIT_TOOLBAR_TOWN_GENERATION :{BLACK}Town generation
STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION :{BLACK}Industry generation
STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Road construction
+STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Tramway construction
STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}Plant trees. Shift toggles building/showing cost estimate
STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}Place sign
STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}Place object. Shift toggles building/showing cost estimate
@@ -2434,6 +2436,11 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL :{BLACK}Build ro
STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL :{BLACK}Build tramway tunnel. Shift toggles building/showing cost estimate
STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD :{BLACK}Toggle build/remove for road construction
STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS :{BLACK}Toggle build/remove for tramway construction
+STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD :{BLACK}Convert/Upgrade the type of road. Shift toggles building/showing cost estimate
+STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM :{BLACK}Convert/Upgrade the type of tram. Shift toggles building/showing cost estimate
+
+STR_ROAD_NAME_ROAD :Road
+STR_ROAD_NAME_TRAM :Tramway
# Road depot construction window
STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION :{WHITE}Road Depot Orientation
@@ -2618,8 +2625,11 @@ STR_LAND_AREA_INFORMATION_NEWGRF_NAME :{BLACK}NewGRF:
STR_LAND_AREA_INFORMATION_CARGO_ACCEPTED :{BLACK}Cargo accepted: {LTBLUE}
STR_LAND_AREA_INFORMATION_CARGO_EIGHTS :({COMMA}/8 {STRING})
STR_LANG_AREA_INFORMATION_RAIL_TYPE :{BLACK}Rail type: {LTBLUE}{STRING}
+STR_LANG_AREA_INFORMATION_ROAD_TYPE :{BLACK}Road type: {LTBLUE}{STRING}
+STR_LANG_AREA_INFORMATION_TRAM_TYPE :{BLACK}Tram type: {LTBLUE}{STRING}
STR_LANG_AREA_INFORMATION_RAIL_SPEED_LIMIT :{BLACK}Rail speed limit: {LTBLUE}{VELOCITY}
STR_LANG_AREA_INFORMATION_ROAD_SPEED_LIMIT :{BLACK}Road speed limit: {LTBLUE}{VELOCITY}
+STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT :{BLACK}Tram speed limit: {LTBLUE}{VELOCITY}
# Description of land area of different tiles
STR_LAI_CLEAR_DESCRIPTION_ROCKS :Rocks
@@ -3359,8 +3369,7 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_CAPTION :{WHITE}Infrastr
STR_COMPANY_INFRASTRUCTURE_VIEW_RAIL_SECT :{GOLD}Rail pieces:
STR_COMPANY_INFRASTRUCTURE_VIEW_SIGNALS :{WHITE}Signals
STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD_SECT :{GOLD}Road pieces:
-STR_COMPANY_INFRASTRUCTURE_VIEW_ROAD :{WHITE}Road
-STR_COMPANY_INFRASTRUCTURE_VIEW_TRAMWAY :{WHITE}Tramway
+STR_COMPANY_INFRASTRUCTURE_VIEW_TRAM_SECT :{GOLD}Tram pieces:
STR_COMPANY_INFRASTRUCTURE_VIEW_WATER_SECT :{GOLD}Water tiles:
STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS :{WHITE}Canals
STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT :{GOLD}Stations:
@@ -3468,10 +3477,15 @@ STR_BUY_VEHICLE_TRAIN_ELRAIL_CAPTION :New Electric Ra
STR_BUY_VEHICLE_TRAIN_MONORAIL_CAPTION :New Monorail Vehicles
STR_BUY_VEHICLE_TRAIN_MAGLEV_CAPTION :New Maglev Vehicles
-STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :New Rail Vehicles
STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION :New Road Vehicles
+STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION :New Tram Vehicles
+
+############ range for vehicle availability starts
+STR_BUY_VEHICLE_TRAIN_ALL_CAPTION :New Rail Vehicles
+STR_BUY_VEHICLE_ROAD_VEHICLE_ALL_CAPTION :New Road Vehicles
STR_BUY_VEHICLE_SHIP_CAPTION :New Ships
STR_BUY_VEHICLE_AIRCRAFT_CAPTION :New Aircraft
+############ range for vehicle availability ends
STR_PURCHASE_INFO_COST_WEIGHT :{BLACK}Cost: {GOLD}{CURRENCY_LONG}{BLACK} Weight: {GOLD}{WEIGHT_SHORT}
STR_PURCHASE_INFO_COST_REFIT_WEIGHT :{BLACK}Cost: {GOLD}{CURRENCY_LONG}{BLACK} (Refit Cost: {GOLD}{CURRENCY_LONG}{BLACK}) Weight: {GOLD}{WEIGHT_SHORT}
@@ -3632,12 +3646,17 @@ STR_DEPOT_SELL_CONFIRMATION_TEXT :{YELLOW}You are
# Engine preview window
STR_ENGINE_PREVIEW_CAPTION :{WHITE}Message from vehicle manufacturer
STR_ENGINE_PREVIEW_MESSAGE :{GOLD}We have just designed a new {STRING} - would you be interested in a year's exclusive use of this vehicle, so we can see how it performs before making it universally available?
+
STR_ENGINE_PREVIEW_RAILROAD_LOCOMOTIVE :railway locomotive
+STR_ENGINE_PREVIEW_ELRAIL_LOCOMOTIVE :electrified railway locomotive
+STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :monorail locomotive
+STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :maglev locomotive
+
STR_ENGINE_PREVIEW_ROAD_VEHICLE :road vehicle
+STR_ENGINE_PREVIEW_TRAM_VEHICLE :tramway vehicle
+
STR_ENGINE_PREVIEW_AIRCRAFT :aircraft
STR_ENGINE_PREVIEW_SHIP :ship
-STR_ENGINE_PREVIEW_MONORAIL_LOCOMOTIVE :monorail locomotive
-STR_ENGINE_PREVIEW_MAGLEV_LOCOMOTIVE :maglev locomotive
STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER :{BLACK}Cost: {CURRENCY_LONG} Weight: {WEIGHT_SHORT}{}Speed: {VELOCITY} Power: {POWER}{}Running Cost: {CURRENCY_LONG}/yr{}Capacity: {CARGO_LONG}
STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER_MAX_TE :{BLACK}Cost: {CURRENCY_LONG} Weight: {WEIGHT_SHORT}{}Speed: {VELOCITY} Power: {POWER} Max. T.E.: {6:FORCE}{}Running Cost: {4:CURRENCY_LONG}/yr{}Capacity: {5:CARGO_LONG}
@@ -3676,14 +3695,19 @@ STR_REPLACE_ENGINE_WAGON_SELECT_HELP :{BLACK}Switch b
STR_REPLACE_ENGINES :Engines
STR_REPLACE_WAGONS :Wagons
STR_REPLACE_ALL_RAILTYPE :All rail vehicles
+STR_REPLACE_ALL_ROADTYPE :All road vehicles
STR_REPLACE_HELP_RAILTYPE :{BLACK}Choose the rail type you want to replace engines for
+STR_REPLACE_HELP_ROADTYPE :{BLACK}Choose the road type you want to replace engines for
STR_REPLACE_HELP_REPLACE_INFO_TAB :{BLACK}Displays which engine the left selected engine is being replaced with, if any
STR_REPLACE_RAIL_VEHICLES :Rail Vehicles
STR_REPLACE_ELRAIL_VEHICLES :Electrified Rail Vehicles
STR_REPLACE_MONORAIL_VEHICLES :Monorail Vehicles
STR_REPLACE_MAGLEV_VEHICLES :Maglev Vehicles
+STR_REPLACE_ROAD_VEHICLES :Road Vehicles
+STR_REPLACE_TRAM_VEHICLES :Tramway Vehicles
+
STR_REPLACE_REMOVE_WAGON :{BLACK}Wagon removal: {ORANGE}{STRING}
STR_REPLACE_REMOVE_WAGON_HELP :{BLACK}Make autoreplace keep the length of a train the same by removing wagons (starting at the front), if replacing the engine would make the train longer
@@ -4406,7 +4430,8 @@ STR_ERROR_MUST_REMOVE_SIGNALS_FIRST :{WHITE}Must rem
STR_ERROR_NO_SUITABLE_RAILROAD_TRACK :{WHITE}No suitable railway track
STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Must remove railway track first
STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Road is one way or blocked
-STR_ERROR_CROSSING_DISALLOWED :{WHITE}Level crossings not allowed for this rail type
+STR_ERROR_CROSSING_DISALLOWED_RAIL :{WHITE}Level crossings not allowed for this rail type
+STR_ERROR_CROSSING_DISALLOWED_ROAD :{WHITE}Level crossings not allowed for this road type
STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Can't build signals here...
STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Can't build railway track here...
STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK :{WHITE}Can't remove railway track from here...
@@ -4426,6 +4451,12 @@ STR_ERROR_CAN_T_REMOVE_ROAD_FROM :{WHITE}Can't re
STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM :{WHITE}Can't remove tramway from here...
STR_ERROR_THERE_IS_NO_ROAD :{WHITE}... there is no road
STR_ERROR_THERE_IS_NO_TRAMWAY :{WHITE}... there is no tramway
+STR_ERROR_CAN_T_CONVERT_ROAD :{WHITE}Can't convert road type here...
+STR_ERROR_CAN_T_CONVERT_TRAMWAY :{WHITE}Can't convert tram type here...
+STR_ERROR_NO_SUITABLE_ROAD :{WHITE}No suitable road
+STR_ERROR_NO_SUITABLE_TRAMWAY :{WHITE}No suitable tramway
+STR_ERROR_INCOMPATIBLE_ROAD :{WHITE}... incompatible road
+STR_ERROR_INCOMPATIBLE_TRAMWAY :{WHITE}... incompatible tramway
# Waterway construction errors
STR_ERROR_CAN_T_BUILD_CANALS :{WHITE}Can't build canals here...
diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp
index 86c17cdcc..353972d48 100644
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -166,7 +166,10 @@ public:
td.airport_tile_name = STR_NULL;
td.railtype = STR_NULL;
td.rail_speed = 0;
+ td.roadtype = STR_NULL;
td.road_speed = 0;
+ td.tramtype = STR_NULL;
+ td.tram_speed = 0;
td.grf = nullptr;
@@ -286,6 +289,13 @@ public:
line_nr++;
}
+ /* Road type name */
+ if (td.roadtype != STR_NULL) {
+ SetDParam(0, td.roadtype);
+ GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_ROAD_TYPE, lastof(this->landinfo_data[line_nr]));
+ line_nr++;
+ }
+
/* Road speed limit */
if (td.road_speed != 0) {
SetDParam(0, td.road_speed);
@@ -293,6 +303,20 @@ public:
line_nr++;
}
+ /* Tram type name */
+ if (td.tramtype != STR_NULL) {
+ SetDParam(0, td.tramtype);
+ GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_TRAM_TYPE, lastof(this->landinfo_data[line_nr]));
+ line_nr++;
+ }
+
+ /* Tram speed limit */
+ if (td.tram_speed != 0) {
+ SetDParam(0, td.tram_speed);
+ GetString(this->landinfo_data[line_nr], STR_LANG_AREA_INFORMATION_TRAM_SPEED_LIMIT, lastof(this->landinfo_data[line_nr]));
+ line_nr++;
+ }
+
/* NewGRF name */
if (td.grf != nullptr) {
SetDParamStr(0, td.grf);
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 759c556a7..82279ab99 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -49,6 +49,7 @@
#include "vehicle_func.h"
#include "language.h"
#include "vehicle_base.h"
+#include "road.h"
#include "table/strings.h"
#include "table/build_industry.h"
@@ -309,6 +310,7 @@ struct GRFTempEngineData {
uint16 cargo_allowed;
uint16 cargo_disallowed;
RailTypeLabel railtypelabel;
+ uint8 roadtramtype;
const GRFFile *defaultcargo_grf; ///< GRF defining the cargo translation table to use if the default cargo is the 'first refittable'.
Refittability refittability; ///< Did the newgrf set any refittability property? If not, default refittability will be applied.
bool prop27_set; ///< Did the NewGRF set property 27 (misc flags)?
@@ -1345,6 +1347,12 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint engine, int numinfo, int prop
RoadVehicleInfo *rvi = &e->u.road;
switch (prop) {
+ case 0x05: // Road/tram type
+ /* RoadTypeLabel is looked up later after the engine's road/tram
+ * flag is set, however 0 means the value has not been set. */
+ _gted[e->index].roadtramtype = buf->ReadByte() + 1;
+ break;
+
case 0x08: // Speed (1 unit is 0.5 kmh)
rvi->max_speed = buf->ReadByte();
break;
@@ -2615,6 +2623,12 @@ static ChangeInfoResult GlobalVarChangeInfo(uint gvid, int numinfo, int prop, By
case 0x12: // Rail type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes)
return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->railtype_list, "Rail type");
+ case 0x16: // Road type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes)
+ return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->roadtype_list, "Road type");
+
+ case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes)
+ return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->tramtype_list, "Tram type");
+
default:
break;
}
@@ -2830,6 +2844,12 @@ static ChangeInfoResult GlobalVarReserveInfo(uint gvid, int numinfo, int prop, B
case 0x12: // Rail type translation table; loading during both reservation and activation stage (in case it is selected depending on defined railtypes)
return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->railtype_list, "Rail type");
+ case 0x16: // Road type translation table; loading during both reservation and activation stage (in case it is selected depending on defined roadtypes)
+ return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->roadtype_list, "Road type");
+
+ case 0x17: // Tram type translation table; loading during both reservation and activation stage (in case it is selected depending on defined tramtypes)
+ return LoadTranslationTable(gvid, numinfo, buf, _cur.grffile->tramtype_list, "Tram type");
+
default:
break;
}
@@ -4368,6 +4388,228 @@ static ChangeInfoResult RailTypeReserveInfo(uint id, int numinfo, int prop, Byte
return ret;
}
+/**
+ * Define properties for roadtypes
+ * @param id ID of the roadtype.
+ * @param numinfo Number of subsequent IDs to change the property for.
+ * @param prop The property to change.
+ * @param buf The property value.
+ * @return ChangeInfoResult.
+ */
+static ChangeInfoResult RoadTypeChangeInfo(uint id, int numinfo, int prop, ByteReader *buf, RoadTramType rtt)
+{
+ ChangeInfoResult ret = CIR_SUCCESS;
+
+ extern RoadTypeInfo _roadtypes[ROADTYPE_END];
+ RoadType *type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map;
+
+ if (id + numinfo > ROADTYPE_END) {
+ grfmsg(1, "RoadTypeChangeInfo: Road type %u is invalid, max %u, ignoring", id + numinfo, ROADTYPE_END);
+ return CIR_INVALID_ID;
+ }
+
+ for (int i = 0; i < numinfo; i++) {
+ RoadType rt = type_map[id + i];
+ if (rt == INVALID_ROADTYPE) return CIR_INVALID_ID;
+
+ RoadTypeInfo *rti = &_roadtypes[rt];
+
+ switch (prop) {
+ case 0x08: // Label of road type
+ /* Skipped here as this is loaded during reservation stage. */
+ buf->ReadDWord();
+ break;
+
+ case 0x09: { // Toolbar caption of roadtype (sets name as well for backwards compatibility for grf ver < 8)
+ uint16 str = buf->ReadWord();
+ AddStringForMapping(str, &rti->strings.toolbar_caption);
+ break;
+ }
+
+ case 0x0A: // Menu text of roadtype
+ AddStringForMapping(buf->ReadWord(), &rti->strings.menu_text);
+ break;
+
+ case 0x0B: // Build window caption
+ AddStringForMapping(buf->ReadWord(), &rti->strings.build_caption);
+ break;
+
+ case 0x0C: // Autoreplace text
+ AddStringForMapping(buf->ReadWord(), &rti->strings.replace_text);
+ break;
+
+ case 0x0D: // New engine text
+ AddStringForMapping(buf->ReadWord(), &rti->strings.new_engine);
+ break;
+
+ case 0x0F: // Powered roadtype list
+ case 0x18: // Roadtype list required for date introduction
+ case 0x19: { // Introduced roadtype list
+ /* Road type compatibility bits are added to the existing bits
+ * to allow multiple GRFs to modify compatibility with the
+ * default road types. */
+ int n = buf->ReadByte();
+ for (int j = 0; j != n; j++) {
+ RoadTypeLabel label = buf->ReadDWord();
+ RoadType rt = GetRoadTypeByLabel(BSWAP32(label), false);
+ if (rt != INVALID_ROADTYPE) {
+ switch (prop) {
+ case 0x0F: SetBit(rti->powered_roadtypes, rt); break;
+ case 0x18: SetBit(rti->introduction_required_roadtypes, rt); break;
+ case 0x19: SetBit(rti->introduces_roadtypes, rt); break;
+ }
+ }
+ }
+ break;
+ }
+
+ case 0x10: // Road Type flags
+ rti->flags = (RoadTypeFlags)buf->ReadByte();
+ break;
+
+ case 0x13: // Construction cost factor
+ rti->cost_multiplier = buf->ReadWord();
+ break;
+
+ case 0x14: // Speed limit
+ rti->max_speed = buf->ReadWord();
+ break;
+
+ case 0x16: // Map colour
+ rti->map_colour = buf->ReadByte();
+ break;
+
+ case 0x17: // Introduction date
+ rti->introduction_date = buf->ReadDWord();
+ break;
+
+ case 0x1A: // Sort order
+ rti->sorting_order = buf->ReadByte();
+ break;
+
+ case 0x1B: // Name of roadtype
+ AddStringForMapping(buf->ReadWord(), &rti->strings.name);
+ break;
+
+ case 0x1C: // Maintenance cost factor
+ rti->maintenance_multiplier = buf->ReadWord();
+ break;
+
+ case 0x1D: // Alternate road type label list
+ /* Skipped here as this is loaded during reservation stage. */
+ for (int j = buf->ReadByte(); j != 0; j--) buf->ReadDWord();
+ break;
+
+ default:
+ ret = CIR_UNKNOWN;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static ChangeInfoResult RoadTypeChangeInfo(uint id, int numinfo, int prop, ByteReader *buf)
+{
+ return RoadTypeChangeInfo(id, numinfo, prop, buf, RTT_ROAD);
+}
+
+static ChangeInfoResult TramTypeChangeInfo(uint id, int numinfo, int prop, ByteReader *buf)
+{
+ return RoadTypeChangeInfo(id, numinfo, prop, buf, RTT_TRAM);
+}
+
+
+static ChangeInfoResult RoadTypeReserveInfo(uint id, int numinfo, int prop, ByteReader *buf, RoadTramType rtt)
+{
+ ChangeInfoResult ret = CIR_SUCCESS;
+
+ extern RoadTypeInfo _roadtypes[ROADTYPE_END];
+ RoadType *type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map;
+
+ if (id + numinfo > ROADTYPE_END) {
+ grfmsg(1, "RoadTypeReserveInfo: Road type %u is invalid, max %u, ignoring", id + numinfo, ROADTYPE_END);
+ return CIR_INVALID_ID;
+ }
+
+ for (int i = 0; i < numinfo; i++) {
+ switch (prop) {
+ case 0x08: { // Label of road type
+ RoadTypeLabel rtl = buf->ReadDWord();
+ rtl = BSWAP32(rtl);
+
+ RoadType rt = GetRoadTypeByLabel(rtl, false);
+ if (rt == INVALID_ROADTYPE) {
+ /* Set up new road type */
+ rt = AllocateRoadType(rtl, rtt);
+ } else if (GetRoadTramType(rt) != rtt) {
+ grfmsg(1, "RoadTypeReserveInfo: Road type %u is invalid type (road/tram), ignoring", id + numinfo);
+ return CIR_INVALID_ID;
+ }
+
+ type_map[id + i] = rt;
+ break;
+ }
+ case 0x09: // Toolbar caption of roadtype
+ case 0x0A: // Menu text
+ case 0x0B: // Build window caption
+ case 0x0C: // Autoreplace text
+ case 0x0D: // New loco
+ case 0x13: // Construction cost
+ case 0x14: // Speed limit
+ case 0x1B: // Name of roadtype
+ case 0x1C: // Maintenance cost factor
+ buf->ReadWord();
+ break;
+
+ case 0x1D: // Alternate road type label list
+ if (type_map[id + i] != INVALID_ROADTYPE) {
+ int n = buf->ReadByte();
+ for (int j = 0; j != n; j++) {
+ _roadtypes[type_map[id + i]].alternate_labels.push_back(BSWAP32(buf->ReadDWord()));
+ }
+ break;
+ }
+ grfmsg(1, "RoadTypeReserveInfo: Ignoring property 1D for road type %u because no label was set", id + i);
+ /* FALL THROUGH */
+
+ case 0x0F: // Powered roadtype list
+ case 0x18: // Roadtype list required for date introduction
+ case 0x19: // Introduced roadtype list
+ for (int j = buf->ReadByte(); j != 0; j--) buf->ReadDWord();
+ break;
+
+ case 0x10: // Road Type flags
+ case 0x12: // Station graphic
+ case 0x15: // Acceleration model
+ case 0x16: // Map colour
+ case 0x1A: // Sort order
+ buf->ReadByte();
+ break;
+
+ case 0x17: // Introduction date
+ buf->ReadDWord();
+ break;
+
+ default:
+ ret = CIR_UNKNOWN;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static ChangeInfoResult RoadTypeReserveInfo(uint id, int numinfo, int prop, ByteReader *buf)
+{
+ return RoadTypeReserveInfo(id, numinfo, prop, buf, RTT_ROAD);
+}
+
+static ChangeInfoResult TramTypeReserveInfo(uint id, int numinfo, int prop, ByteReader *buf)
+{
+ return RoadTypeReserveInfo(id, numinfo, prop, buf, RTT_TRAM);
+}
+
static ChangeInfoResult AirportTilesChangeInfo(uint airtid, int numinfo, int prop, ByteReader *buf)
{
ChangeInfoResult ret = CIR_SUCCESS;
@@ -4520,6 +4762,8 @@ static void FeatureChangeInfo(ByteReader *buf)
/* GSF_OBJECTS */ ObjectChangeInfo,
/* GSF_RAILTYPES */ RailTypeChangeInfo,
/* GSF_AIRPORTTILES */ AirportTilesChangeInfo,
+ /* GSF_ROADTYPES */ RoadTypeChangeInfo,
+ /* GSF_TRAMTYPES */ TramTypeChangeInfo,
};
uint8 feature = buf->ReadByte();
@@ -4593,7 +4837,7 @@ static void ReserveChangeInfo(ByteReader *buf)
{
uint8 feature = buf->ReadByte();
- if (feature != GSF_CARGOES && feature != GSF_GLOBALVAR && feature != GSF_RAILTYPES) return;
+ if (feature != GSF_CARGOES && feature != GSF_GLOBALVAR && feature != GSF_RAILTYPES && feature != GSF_ROADTYPES && feature != GSF_TRAMTYPES) return;
uint8 numprops = buf->ReadByte();
uint8 numinfo = buf->ReadByte();
@@ -4616,6 +4860,14 @@ static void ReserveChangeInfo(ByteReader *buf)
case GSF_RAILTYPES:
cir = RailTypeReserveInfo(index, numinfo, prop, buf);
break;
+
+ case GSF_ROADTYPES:
+ cir = RoadTypeReserveInfo(index, numinfo, prop, buf);
+ break;
+
+ case GSF_TRAMTYPES:
+ cir = TramTypeReserveInfo(index, numinfo, prop, buf);
+ break;
}
if (HandleChangeInfoResult("ReserveChangeInfo", cir, feature, prop)) return;
@@ -4928,6 +5180,8 @@ static void NewSpriteGroup(ByteReader *buf)
case GSF_CARGOES:
case GSF_AIRPORTS:
case GSF_RAILTYPES:
+ case GSF_ROADTYPES:
+ case GSF_TRAMTYPES:
{
byte num_loaded = type;
byte num_loading = buf->ReadByte();
@@ -5505,6 +5759,39 @@ static void RailTypeMapSpriteGroup(ByteReader *buf, uint8 idcount)
buf->ReadWord();
}
+static void RoadTypeMapSpriteGroup(ByteReader *buf, uint8 idcount, RoadTramType rtt)
+{
+ RoadType *type_map = (rtt == RTT_TRAM) ? _cur.grffile->tramtype_map : _cur.grffile->roadtype_map;
+
+ uint8 *roadtypes = AllocaM(uint8, idcount);
+ for (uint i = 0; i < idcount; i++) {
+ uint8 id = buf->ReadByte();
+ roadtypes[i] = id < ROADTYPE_END ? type_map[id] : INVALID_ROADTYPE;
+ }
+
+ uint8 cidcount = buf->ReadByte();
+ for (uint c = 0; c < cidcount; c++) {
+ uint8 ctype = buf->ReadByte();
+ uint16 groupid = buf->ReadWord();
+ if (!IsValidGroupID(groupid, "RoadTypeMapSpriteGroup")) continue;
+
+ if (ctype >= ROTSG_END) continue;
+
+ extern RoadTypeInfo _roadtypes[ROADTYPE_END];
+ for (uint i = 0; i < idcount; i++) {
+ if (roadtypes[i] != INVALID_ROADTYPE) {
+ RoadTypeInfo *rti = &_roadtypes[roadtypes[i]];
+
+ rti->grffile[ctype] = _cur.grffile;
+ rti->group[ctype] = _cur.spritegroups[groupid];
+ }
+ }
+ }
+
+ /* Roadtypes do not use the default group. */
+ buf->ReadWord();
+}
+
static void AirportMapSpriteGroup(ByteReader *buf, uint8 idcount)
{
uint8 *airports = AllocaM(uint8, idcount);
@@ -5655,6 +5942,14 @@ static void FeatureMapSpriteGroup(ByteReader *buf)
RailTypeMapSpriteGroup(buf, idcount);
break;
+ case GSF_ROADTYPES:
+ RoadTypeMapSpriteGroup(buf, idcount, RTT_ROAD);
+ break;
+
+ case GSF_TRAMTYPES:
+ RoadTypeMapSpriteGroup(buf, idcount, RTT_TRAM);
+ break;
+
case GSF_AIRPORTTILES:
AirportTileMapSpriteGroup(buf, idcount);
return;
@@ -5917,13 +6212,20 @@ static void GraphicsNew(ByteReader *buf)
/* Load <num> sprites starting from <replace>, then skip <skip_num> sprites. */
grfmsg(2, "GraphicsNew: Replacing sprites %d to %d of %s (type 0x%02X) at SpriteID 0x%04X", offset, offset + num - 1, action5_type->name, type, replace);
+ if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5;
+
+ if (type == 0x0B) {
+ static const SpriteID depot_with_track_offset = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_TRAMWAY_BASE;
+ static const SpriteID depot_no_track_offset = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_TRAMWAY_BASE;
+ if (offset <= depot_with_track_offset && offset + num > depot_with_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_WITH_TRACK;
+ if (offset <= depot_no_track_offset && offset + num > depot_no_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NO_TRACK;
+ }
+
for (; num > 0; num--) {
_cur.nfo_line++;
LoadNextSprite(replace == 0 ? _cur.spriteid++ : replace++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver);
}
- if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5;
-
_cur.skip_sprites = skip_num;
}
@@ -6349,6 +6651,14 @@ static void SkipIf(ByteReader *buf)
break;
case 0x0E: result = GetRailTypeByLabel(BSWAP32(cond_val)) != INVALID_RAILTYPE;
break;
+ case 0x0F: result = GetRoadTypeByLabel(BSWAP32(cond_val)) == INVALID_ROADTYPE;
+ break;
+ case 0x10: result = GetRoadTypeByLabel(BSWAP32(cond_val)) != INVALID_ROADTYPE;
+ break;
+ case 0x11: result = GetRoadTypeByLabel(BSWAP32(cond_val)) == INVALID_ROADTYPE;
+ break;
+ case 0x12: result = GetRoadTypeByLabel(BSWAP32(cond_val)) != INVALID_ROADTYPE;
+ break;
default: grfmsg(1, "SkipIf: Unsupported condition type %02X. Ignoring", condtype); return;
}
@@ -8269,6 +8579,9 @@ void ResetNewGRFData()
/* Reset rail type information */
ResetRailTypes();
+ /* Copy/reset original road type info data */
+ ResetRoadTypes();
+
/* Allocate temporary refit/cargo class data */
_gted = CallocT<GRFTempEngineData>(Engine::GetPoolSize());
@@ -8337,6 +8650,7 @@ void ResetNewGRFData()
_loaded_newgrf_features.has_newhouses = false;
_loaded_newgrf_features.has_newindustries = false;
_loaded_newgrf_features.shore = SHORE_REPLACE_NONE;
+ _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NONE;
/* Clear all GRF overrides */
_grf_id_overrides.clear();
@@ -8424,6 +8738,14 @@ GRFFile::GRFFile(const GRFConfig *config)
this->railtype_map[2] = RAILTYPE_MONO;
this->railtype_map[3] = RAILTYPE_MAGLEV;
+ /* Initialise road type map with default road types */
+ memset(this->roadtype_map, INVALID_ROADTYPE, sizeof(this->roadtype_map));
+ this->roadtype_map[0] = ROADTYPE_ROAD;
+
+ /* Initialise tram type map with default tram types */
+ memset(this->tramtype_map, INVALID_ROADTYPE, sizeof(this->tramtype_map));
+ this->tramtype_map[0] = ROADTYPE_TRAM;
+
/* Copy the initial parameter list
* 'Uninitialised' parameters are zeroed as that is their default value when dynamically creating them. */
assert_compile(lengthof(this->param) == lengthof(config->param) && lengthof(this->param) == 0x80);
@@ -9194,6 +9516,21 @@ static void ActivateOldShore()
}
/**
+ * Replocate the old tram depot sprites to the new position, if no new ones were loaded.
+ */
+static void ActivateOldTramDepot()
+{
+ if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK) {
+ DupSprite(SPR_ROAD_DEPOT + 0, SPR_TRAMWAY_DEPOT_NO_TRACK + 0); // use road depot graphics for "no tracks"
+ DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 1, SPR_TRAMWAY_DEPOT_NO_TRACK + 1);
+ DupSprite(SPR_ROAD_DEPOT + 2, SPR_TRAMWAY_DEPOT_NO_TRACK + 2); // use road depot graphics for "no tracks"
+ DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 3, SPR_TRAMWAY_DEPOT_NO_TRACK + 3);
+ DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 4, SPR_TRAMWAY_DEPOT_NO_TRACK + 4);
+ DupSprite(SPR_TRAMWAY_DEPOT_WITH_TRACK + 5, SPR_TRAMWAY_DEPOT_NO_TRACK + 5);
+ }
+}
+
+/**
* Decide whether price base multipliers of grfs shall apply globally or only to the grf specifying them
*/
static void FinalisePriceBaseMultipliers()
@@ -9371,8 +9708,12 @@ static void AfterLoadGRFs()
/* Load old shore sprites in new position, if they were replaced by ActionA */
ActivateOldShore();
+ /* Load old tram depot sprites in new position, if no new ones are present */
+ ActivateOldTramDepot();
+
/* Set up custom rail types */
InitRailTypes();
+ InitRoadTypes();
Engine *e;
FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
@@ -9380,6 +9721,31 @@ static void AfterLoadGRFs()
/* Set RV maximum speed from the mph/0.8 unit value */
e->u.road.max_speed = _gted[e->index].rv_max_speed * 4;
}
+
+ RoadTramType rtt = HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? RTT_TRAM : RTT_ROAD;
+
+ const GRFFile *file = e->GetGRF();
+ if (file == nullptr || _gted[e->index].roadtramtype == 0) {
+ e->u.road.roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
+ continue;
+ }
+
+ /* Remove +1 offset. */
+ _gted[e->index].roadtramtype--;
+
+ const std::vector<RoadTypeLabel> *list = (rtt == RTT_TRAM) ? &file->tramtype_list : &file->roadtype_list;
+ if (_gted[e->index].roadtramtype < list->size())
+ {
+ RoadTypeLabel rtl = (*list)[_gted[e->index].roadtramtype];
+ RoadType rt = GetRoadTypeByLabel(rtl);
+ if (rt != INVALID_ROADTYPE && GetRoadTramType(rt) == rtt) {
+ e->u.road.roadtype = rt;
+ continue;
+ }
+ }
+
+ /* Road type is not available, so disable this engine */
+ e->info.climates = 0;
}
FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
diff --git a/src/newgrf.h b/src/newgrf.h
index c36005b0f..5d76a3364 100644
--- a/src/newgrf.h
+++ b/src/newgrf.h
@@ -14,6 +14,7 @@
#include "cargotype.h"
#include "rail_type.h"
+#include "road_type.h"
#include "fileio_type.h"
#include "core/bitmath_func.hpp"
#include "core/alloc_type.hpp"
@@ -83,6 +84,8 @@ enum GrfSpecFeature {
GSF_OBJECTS,
GSF_RAILTYPES,
GSF_AIRPORTTILES,
+ GSF_ROADTYPES,
+ GSF_TRAMTYPES,
GSF_END,
GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope)
@@ -128,6 +131,12 @@ struct GRFFile : ZeroedMemoryAllocator {
std::vector<RailTypeLabel> railtype_list; ///< Railtype translation table
RailType railtype_map[RAILTYPE_END];
+ std::vector<RoadTypeLabel> roadtype_list; ///< Roadtype translation table (road)
+ RoadType roadtype_map[ROADTYPE_END];
+
+ std::vector<RoadTypeLabel> tramtype_list; ///, Roadtype translation table (tram)
+ RoadType tramtype_map[ROADTYPE_END];
+
CanalProperties canal_local_properties[CF_END]; ///< Canal properties as set by this NewGRF
struct LanguageMap *language_map; ///< Mappings related to the languages.
@@ -158,12 +167,19 @@ enum ShoreReplacement {
SHORE_REPLACE_ONLY_NEW, ///< Only corner-shores were loaded by Action5 (openttd(w/d).grf only).
};
+enum TramReplacement {
+ TRAMWAY_REPLACE_DEPOT_NONE, ///< No tram depot graphics were loaded.
+ TRAMWAY_REPLACE_DEPOT_WITH_TRACK, ///< Electrified depot graphics with tram track were loaded.
+ TRAMWAY_REPLACE_DEPOT_NO_TRACK, ///< Electrified depot graphics without tram track were loaded.
+};
+
struct GRFLoadedFeatures {
bool has_2CC; ///< Set if any vehicle is loaded which uses 2cc (two company colours).
uint64 used_liveries; ///< Bitmask of #LiveryScheme used by the defined engines.
bool has_newhouses; ///< Set if there are any newhouses loaded.
bool has_newindustries; ///< Set if there are any newindustries loaded.
ShoreReplacement shore; ///< In which way shore sprites were replaced.
+ TramReplacement tram; ///< In which way tram depots were replaced.
};
/**
diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp
index 3a4318873..955e4ac31 100644
--- a/src/newgrf_engine.cpp
+++ b/src/newgrf_engine.cpp
@@ -23,6 +23,7 @@
#include "station_base.h"
#include "company_base.h"
#include "newgrf_railtype.h"
+#include "newgrf_roadtype.h"
#include "ship.h"
#include "safeguards.h"
@@ -606,11 +607,21 @@ static uint32 VehicleGetVariable(Vehicle *v, const VehicleScopeResolver *object,
case 0x48: return v->GetEngine()->flags; // Vehicle Type Info
case 0x49: return v->build_year;
- case 0x4A: {
- if (v->type != VEH_TRAIN) return 0;
- RailType rt = GetTileRailType(v->tile);
- return (HasPowerOnRail(Train::From(v)->railtype, rt) ? 0x100 : 0) | GetReverseRailTypeTranslation(rt, object->ro.grffile);
- }
+ case 0x4A:
+ switch (v->type) {
+ case VEH_TRAIN: {
+ RailType rt = GetTileRailType(v->tile);
+ return (HasPowerOnRail(Train::From(v)->railtype, rt) ? 0x100 : 0) | GetReverseRailTypeTranslation(rt, object->ro.grffile);
+ }
+
+ case VEH_ROAD: {
+ RoadType rt = GetRoadType(v->tile, GetRoadTramType(RoadVehicle::From(v)->roadtype));
+ return 0x100 | GetReverseRoadTypeTranslation(rt, object->ro.grffile);
+ }
+
+ default:
+ return 0;
+ }
case 0x4B: // Long date of last service
return v->date_of_last_service;
diff --git a/src/newgrf_roadtype.cpp b/src/newgrf_roadtype.cpp
new file mode 100644
index 000000000..1fb8e5ec1
--- /dev/null
+++ b/src/newgrf_roadtype.cpp
@@ -0,0 +1,143 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file newgrf_roadtype.cpp NewGRF handling of road types. */
+
+#include "stdafx.h"
+#include "debug.h"
+#include "newgrf_roadtype.h"
+#include "date_func.h"
+#include "depot_base.h"
+#include "town.h"
+
+#include "safeguards.h"
+
+/* virtual */ uint32 RoadTypeScopeResolver::GetRandomBits() const
+{
+ uint tmp = CountBits(this->tile + (TileX(this->tile) + TileY(this->tile)) * TILE_SIZE);
+ return GB(tmp, 0, 2);
+}
+
+/* virtual */ uint32 RoadTypeScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const
+{
+ if (this->tile == INVALID_TILE) {
+ switch (variable) {
+ case 0x40: return 0;
+ case 0x41: return 0;
+ case 0x42: return 0;
+ case 0x43: return _date;
+ case 0x44: return HZB_TOWN_EDGE;
+ }
+ }
+
+ switch (variable) {
+ case 0x40: return GetTerrainType(this->tile, this->context);
+ case 0x41: return 0;
+ case 0x42: return IsLevelCrossingTile(this->tile) && IsCrossingBarred(this->tile);
+ case 0x43:
+ if (IsRoadDepotTile(this->tile)) return Depot::GetByTile(this->tile)->build_date;
+ return _date;
+ case 0x44: {
+ const Town *t = nullptr;
+ if (IsRoadDepotTile(this->tile)) {
+ t = Depot::GetByTile(this->tile)->town;
+ } else if (IsTileType(this->tile, MP_ROAD)) {
+ t = ClosestTownFromTile(this->tile, UINT_MAX);
+ }
+ return t != nullptr ? GetTownRadiusGroup(t, this->tile) : HZB_TOWN_EDGE;
+ }
+ }
+
+ DEBUG(grf, 1, "Unhandled road type tile variable 0x%X", variable);
+
+ *available = false;
+ return UINT_MAX;
+}
+
+/* virtual */ const SpriteGroup *RoadTypeResolverObject::ResolveReal(const RealSpriteGroup *group) const
+{
+ if (group->num_loading > 0) return group->loading[0];
+ if (group->num_loaded > 0) return group->loaded[0];
+ return nullptr;
+}
+
+/**
+ * Constructor of the roadtype scope resolvers.
+ * @param ro Surrounding resolver.
+ * @param tile %Tile containing the track. For track on a bridge this is the southern bridgehead.
+ * @param context Are we resolving sprites for the upper halftile, or on a bridge?
+ */
+RoadTypeScopeResolver::RoadTypeScopeResolver(ResolverObject &ro, TileIndex tile, TileContext context) : ScopeResolver(ro)
+{
+ this->tile = tile;
+ this->context = context;
+}
+
+/**
+ * Resolver object for road types.
+ * @param rti Roadtype. nullptr in NewGRF Inspect window.
+ * @param tile %Tile containing the track. For track on a bridge this is the southern bridgehead.
+ * @param context Are we resolving sprites for the upper halftile, or on a bridge?
+ * @param rtsg Roadpart of interest
+ * @param param1 Extra parameter (first parameter of the callback, except roadtypes do not have callbacks).
+ * @param param2 Extra parameter (second parameter of the callback, except roadtypes do not have callbacks).
+ */
+RoadTypeResolverObject::RoadTypeResolverObject(const RoadTypeInfo *rti, TileIndex tile, TileContext context, RoadTypeSpriteGroup rtsg, uint32 param1, uint32 param2)
+ : ResolverObject(rti != nullptr ? rti->grffile[rtsg] : nullptr, CBID_NO_CALLBACK, param1, param2), roadtype_scope(*this, tile, context)
+{
+ this->root_spritegroup = rti != nullptr ? rti->group[rtsg] : nullptr;
+}
+
+/**
+ * Get the sprite to draw for the given tile.
+ * @param rti The road type data (spec).
+ * @param tile The tile to get the sprite for.
+ * @param rtsg The type of sprite to draw.
+ * @param content Where are we drawing the tile?
+ * @param [out] num_results If not nullptr, return the number of sprites in the spriteset.
+ * @return The sprite to draw.
+ */
+SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context, uint *num_results)
+{
+ assert(rtsg < ROTSG_END);
+
+ if (rti->group[rtsg] == nullptr) return 0;
+
+ RoadTypeResolverObject object(rti, tile, context, rtsg);
+ const SpriteGroup *group = object.Resolve();
+ if (group == nullptr || group->GetNumResults() == 0) return 0;
+
+ if (num_results) *num_results = group->GetNumResults();
+
+ return group->GetResult();
+}
+
+/**
+ * Perform a reverse roadtype lookup to get the GRF internal ID.
+ * @param roadtype The global (OpenTTD) roadtype.
+ * @param grffile The GRF to do the lookup for.
+ * @return the GRF internal ID.
+ */
+uint8 GetReverseRoadTypeTranslation(RoadType roadtype, const GRFFile *grffile)
+{
+ /* No road type table present, return road type as-is */
+ if (grffile == nullptr) return roadtype;
+
+ const std::vector<RoadTypeLabel> *list = RoadTypeIsRoad(roadtype) ? &grffile->roadtype_list : &grffile->tramtype_list;
+ if (list->size() == 0) return roadtype;
+
+ /* Look for a matching road type label in the table */
+ RoadTypeLabel label = GetRoadTypeInfo(roadtype)->label;
+
+ int index = find_index(*list, label);
+ if (index >= 0) return index;
+
+ /* If not found, return as invalid */
+ return 0xFF;
+}
diff --git a/src/newgrf_roadtype.h b/src/newgrf_roadtype.h
new file mode 100644
index 000000000..2da7a82c1
--- /dev/null
+++ b/src/newgrf_roadtype.h
@@ -0,0 +1,51 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file newgrf_roadtype.h NewGRF handling of road types. */
+
+#ifndef NEWGRF_ROADTYPE_H
+#define NEWGRF_ROADTYPE_H
+
+#include "road.h"
+#include "newgrf_commons.h"
+#include "newgrf_spritegroup.h"
+
+/** Resolver for the railtype scope. */
+struct RoadTypeScopeResolver : public ScopeResolver {
+ TileIndex tile; ///< Tracktile. For track on a bridge this is the southern bridgehead.
+ TileContext context; ///< Are we resolving sprites for the upper halftile, or on a bridge?
+
+ RoadTypeScopeResolver(ResolverObject &ro, TileIndex tile, TileContext context);
+
+ /* virtual */ uint32 GetRandomBits() const;
+ /* virtual */ uint32 GetVariable(byte variable, uint32 parameter, bool *available) const;
+};
+
+/** Resolver object for road types. */
+struct RoadTypeResolverObject : public ResolverObject {
+ RoadTypeScopeResolver roadtype_scope; ///< Resolver for the roadtype scope.
+
+ RoadTypeResolverObject(const RoadTypeInfo *rti, TileIndex tile, TileContext context, RoadTypeSpriteGroup rtsg, uint32 param1 = 0, uint32 param2 = 0);
+
+ /* virtual */ ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0)
+ {
+ switch (scope) {
+ case VSG_SCOPE_SELF: return &this->roadtype_scope;
+ default: return ResolverObject::GetScope(scope, relative);
+ }
+ }
+
+ /* virtual */ const SpriteGroup *ResolveReal(const RealSpriteGroup *group) const;
+};
+
+SpriteID GetCustomRoadSprite(const RoadTypeInfo *rti, TileIndex tile, RoadTypeSpriteGroup rtsg, TileContext context = TCX_NORMAL, uint *num_results = nullptr);
+
+uint8 GetReverseRoadTypeTranslation(RoadType roadtype, const GRFFile *grffile);
+
+#endif /* NEWGRF_ROADTYPE_H */
diff --git a/src/openttd.cpp b/src/openttd.cpp
index 243302d61..8ca080989 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -53,6 +53,7 @@
#include "engine_func.h"
#include "core/random_func.hpp"
#include "rail_gui.h"
+#include "road_gui.h"
#include "core/backup_type.hpp"
#include "hotkeys.h"
#include "newgrf.h"
@@ -949,6 +950,7 @@ static void MakeNewGameDone()
SetLocalCompany(COMPANY_FIRST);
InitializeRailGUI();
+ InitializeRoadGUI();
/* We are the server, we start a new company (not dedicated),
* so set the default password *if* needed. */
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 999f7f94d..31a781650 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -32,7 +32,7 @@ struct CFollowTrackT
enum ErrorCode {
EC_NONE,
EC_OWNER,
- EC_RAIL_TYPE,
+ EC_RAIL_ROAD_TYPE,
EC_90DEG,
EC_NO_WAY,
EC_RESERVED,
@@ -60,6 +60,7 @@ struct CFollowTrackT
inline CFollowTrackT(Owner o, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = nullptr)
{
+ assert(IsRailTT());
m_veh = nullptr;
Init(o, railtype_override, pPerf);
}
@@ -92,7 +93,7 @@ struct CFollowTrackT
inline static TransportType TT() { return Ttr_type_; }
inline static bool IsWaterTT() { return TT() == TRANSPORT_WATER; }
inline static bool IsRailTT() { return TT() == TRANSPORT_RAIL; }
- inline bool IsTram() { return IsRoadTT() && HasBit(RoadVehicle::From(m_veh)->compatible_roadtypes, ROADTYPE_TRAM); }
+ inline bool IsTram() { return IsRoadTT() && RoadTypeIsTram(RoadVehicle::From(m_veh)->roadtype); }
inline static bool IsRoadTT() { return TT() == TRANSPORT_ROAD; }
inline static bool Allow90degTurns() { return T90deg_turns_allowed_; }
inline static bool DoTrackMasking() { return Tmask_reserved_tracks; }
@@ -103,7 +104,7 @@ struct CFollowTrackT
assert(IsTram()); // this function shouldn't be called in other cases
if (IsNormalRoadTile(tile)) {
- RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM);
+ RoadBits rb = GetRoadBits(tile, RTT_TRAM);
switch (rb) {
case ROAD_NW: return DIAGDIR_NW;
case ROAD_SW: return DIAGDIR_SW;
@@ -126,7 +127,7 @@ struct CFollowTrackT
m_err = EC_NONE;
assert(
((TrackStatusToTrackdirBits(
- GetTileTrackStatus(m_old_tile, TT(), (IsRoadTT() && m_veh != nullptr) ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)
+ GetTileTrackStatus(m_old_tile, TT(), (IsRoadTT() && m_veh != nullptr) ? (this->IsTram() ? RTT_TRAM : RTT_ROAD) : 0)
) & TrackdirToTrackdirBits(m_old_td)) != 0) ||
(IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR) // Disable the assertion for single tram bits
);
@@ -151,7 +152,7 @@ struct CFollowTrackT
if (IsRoadTT() && !IsTram() && TryReverse()) return true;
/* CanEnterNewTile already set a reason.
- * Do NOT overwrite it (important for example for EC_RAIL_TYPE).
+ * Do NOT overwrite it (important for example for EC_RAIL_ROAD_TYPE).
* Only set a reason if CanEnterNewTile was not called */
if (m_new_td_bits == TRACKDIR_BIT_NONE) m_err = EC_NO_WAY;
@@ -241,7 +242,7 @@ protected:
if (IsRailTT() && IsPlainRailTile(m_new_tile)) {
m_new_td_bits = (TrackdirBits)(GetTrackBits(m_new_tile) * 0x101);
} else {
- m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), IsRoadTT() ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0));
+ m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), IsRoadTT() ? (this->IsTram() ? RTT_TRAM : RTT_ROAD) : 0));
if (IsTram() && m_new_td_bits == TRACKDIR_BIT_NONE) {
/* GetTileTrackStatus() returns 0 for single tram bits.
@@ -350,7 +351,18 @@ protected:
RailType rail_type = GetTileRailType(m_new_tile);
if (!HasBit(m_railtypes, rail_type)) {
/* incompatible rail type */
- m_err = EC_RAIL_TYPE;
+ m_err = EC_RAIL_ROAD_TYPE;
+ return false;
+ }
+ }
+
+ /* road transport is possible only on compatible road types */
+ if (IsRoadTT()) {
+ const RoadVehicle *v = RoadVehicle::From(m_veh);
+ RoadType roadtype = GetRoadType(m_new_tile, GetRoadTramType(v->roadtype));
+ if (!HasBit(v->compatible_roadtypes, roadtype)) {
+ /* incompatible road type */
+ m_err = EC_RAIL_ROAD_TYPE;
return false;
}
}
diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp
index 36f66b5a9..2cf333890 100644
--- a/src/pathfinder/npf/npf.cpp
+++ b/src/pathfinder/npf/npf.cpp
@@ -43,6 +43,7 @@ struct AyStarUserData {
TransportType type;
RailTypes railtypes;
RoadTypes roadtypes;
+ uint subtype;
};
/** Indices into AyStarNode.userdata[] */
@@ -719,7 +720,7 @@ static DiagDirection GetDepotDirection(TileIndex tile, TransportType type)
static DiagDirection GetSingleTramBit(TileIndex tile)
{
if (IsNormalRoadTile(tile)) {
- RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM);
+ RoadBits rb = GetRoadBits(tile, RTT_TRAM);
switch (rb) {
case ROAD_NW: return DIAGDIR_NW;
case ROAD_SW: return DIAGDIR_SW;
@@ -747,7 +748,7 @@ static DiagDirection GetTileSingleEntry(TileIndex tile, TransportType type, uint
if (type == TRANSPORT_ROAD) {
if (IsStandardRoadStopTile(tile)) return GetRoadStopDir(tile);
- if (HasBit(subtype, ROADTYPE_TRAM)) return GetSingleTramBit(tile);
+ if ((RoadTramType)subtype == RTT_TRAM) return GetSingleTramBit(tile);
}
return INVALID_DIAGDIR;
@@ -785,13 +786,24 @@ static bool CanEnterTile(TileIndex tile, DiagDirection dir, AyStarUserData *user
if (!CanEnterTileOwnerCheck(user->owner, tile, dir)) return false;
/* check correct rail type (mono, maglev, etc) */
- if (user->type == TRANSPORT_RAIL) {
- RailType rail_type = GetTileRailType(tile);
- if (!HasBit(user->railtypes, rail_type)) return false;
+ switch (user->type) {
+ case TRANSPORT_RAIL: {
+ RailType rail_type = GetTileRailType(tile);
+ if (!HasBit(user->railtypes, rail_type)) return false;
+ break;
+ }
+
+ case TRANSPORT_ROAD: {
+ RoadType road_type = GetRoadType(tile, (RoadTramType)user->subtype);
+ if (!HasBit(user->roadtypes, road_type)) return false;
+ break;
+ }
+
+ default: break;
}
/* Depots, standard roadstops and single tram bits can only be entered from one direction */
- DiagDirection single_entry = GetTileSingleEntry(tile, user->type, user->roadtypes);
+ DiagDirection single_entry = GetTileSingleEntry(tile, user->type, user->subtype);
if (single_entry != INVALID_DIAGDIR && single_entry != ReverseDiagDir(dir)) return false;
return true;
@@ -813,7 +825,7 @@ static TrackdirBits GetDriveableTrackdirBits(TileIndex dst_tile, TileIndex src_t
{
TrackdirBits trackdirbits = TrackStatusToTrackdirBits(GetTileTrackStatus(dst_tile, type, subtype));
- if (trackdirbits == TRACKDIR_BIT_NONE && type == TRANSPORT_ROAD && HasBit(subtype, ROADTYPE_TRAM)) {
+ if (trackdirbits == TRACKDIR_BIT_NONE && type == TRANSPORT_ROAD && (RoadTramType)subtype == RTT_TRAM) {
/* GetTileTrackStatus() returns 0 for single tram bits.
* As we cannot change it there (easily) without breaking something, change it here */
switch (GetSingleTramBit(dst_tile)) {
@@ -863,7 +875,7 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current)
/* Information about the vehicle: TransportType (road/rail/water) and SubType (compatible rail/road types) */
TransportType type = user->type;
- uint subtype = user->roadtypes;
+ uint subtype = user->subtype;
/* Initialize to 0, so we can jump out (return) somewhere an have no neighbours */
aystar->num_neighbours = 0;
@@ -894,11 +906,11 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current)
/* We leave src_tile in src_exitdir and reach dst_tile */
dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(src_exitdir));
- if (dst_tile != INVALID_TILE && !CanEnterTile(dst_tile, src_exitdir, user)) dst_tile = INVALID_TILE;
+ if (dst_tile != INVALID_TILE && IsNormalRoadTile(dst_tile) && !CanEnterTile(dst_tile, src_exitdir, user)) dst_tile = INVALID_TILE;
if (dst_tile == INVALID_TILE) {
/* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */
- if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;
+ if (type != TRANSPORT_ROAD || (RoadTramType)subtype == RTT_TRAM) return;
dst_tile = src_tile;
src_trackdir = ReverseTrackdir(src_trackdir);
@@ -908,7 +920,7 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current)
if (trackdirbits == TRACKDIR_BIT_NONE) {
/* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */
- if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;
+ if (type != TRANSPORT_ROAD || (RoadTramType)subtype == RTT_TRAM) return;
dst_tile = src_tile;
src_trackdir = ReverseTrackdir(src_trackdir);
@@ -1120,7 +1132,7 @@ FindDepotData NPFRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_penal
{
Trackdir trackdir = v->GetVehicleTrackdir();
- AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes };
+ AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes, GetRoadTramType(v->roadtype) };
NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, INVALID_TILE, INVALID_TRACKDIR, false, nullptr, &user, 0, max_penalty);
if (ftd.best_bird_dist != 0) return FindDepotData();
@@ -1140,7 +1152,7 @@ Trackdir NPFRoadVehicleChooseTrack(const RoadVehicle *v, TileIndex tile, DiagDir
NPFFillWithOrderData(&fstd, v);
Trackdir trackdir = DiagDirToDiagTrackdir(enterdir);
- AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes };
+ AyStarUserData user = { v->owner, TRANSPORT_ROAD, RAILTYPES_NONE, v->compatible_roadtypes, GetRoadTramType(v->roadtype) };
NPFFoundTargetData ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, &user);
assert(ftd.best_trackdir != INVALID_TRACKDIR);
@@ -1163,7 +1175,7 @@ Track NPFShipChooseTrack(const Ship *v, bool &path_found)
NPFFillWithOrderData(&fstd, v);
- AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE };
+ AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE, 0 };
NPFFoundTargetData ftd = NPFRouteToStationOrTile(v->tile, trackdir, true, &fstd, &user);
assert(ftd.best_trackdir != INVALID_TRACKDIR);
@@ -1188,7 +1200,7 @@ bool NPFShipCheckReverse(const Ship *v)
assert(trackdir != INVALID_TRACKDIR);
assert(trackdir_rev != INVALID_TRACKDIR);
- AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE };
+ AyStarUserData user = { v->owner, TRANSPORT_WATER, RAILTYPES_NONE, ROADTYPES_NONE, 0 };
ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, false, v->tile, trackdir_rev, false, &fstd, &user);
/* If we didn't find anything, just keep on going straight ahead, otherwise take the reverse flag */
return ftd.best_bird_dist == 0 && NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE);
@@ -1206,7 +1218,7 @@ FindDepotData NPFTrainFindNearestDepot(const Train *v, int max_penalty)
fstd.reserve_path = false;
assert(trackdir != INVALID_TRACKDIR);
- AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE };
+ AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE, 0 };
NPFFoundTargetData ftd = NPFRouteToDepotBreadthFirstTwoWay(v->tile, trackdir, false, last->tile, trackdir_rev, false, &fstd, &user, NPF_INFINITE_PENALTY, max_penalty);
if (ftd.best_bird_dist != 0) return FindDepotData();
@@ -1235,7 +1247,7 @@ bool NPFTrainFindNearestSafeTile(const Train *v, TileIndex tile, Trackdir trackd
/* perform a breadth first search. Target is nullptr,
* since we are just looking for any safe tile...*/
- AyStarUserData user = { v->owner, TRANSPORT_RAIL, railtypes, ROADTYPES_NONE };
+ AyStarUserData user = { v->owner, TRANSPORT_RAIL, railtypes, ROADTYPES_NONE, 0 };
return NPFRouteInternal(&start1, true, nullptr, false, &fstd, NPFFindSafeTile, NPFCalcZero, &user, 0, true).res_okay;
}
@@ -1252,7 +1264,7 @@ bool NPFTrainCheckReverse(const Train *v)
assert(trackdir != INVALID_TRACKDIR);
assert(trackdir_rev != INVALID_TRACKDIR);
- AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE };
+ AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE, 0 };
ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, false, last->tile, trackdir_rev, false, &fstd, &user);
/* If we didn't find anything, just keep on going straight ahead, otherwise take the reverse flag */
return ftd.best_bird_dist == 0 && NPFGetFlag(&ftd.node, NPF_FLAG_REVERSE);
@@ -1266,7 +1278,7 @@ Track NPFTrainChooseTrack(const Train *v, bool &path_found, bool reserve_track,
PBSTileInfo origin = FollowTrainReservation(v);
assert(IsValidTrackdir(origin.trackdir));
- AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE };
+ AyStarUserData user = { v->owner, TRANSPORT_RAIL, v->compatible_railtypes, ROADTYPES_NONE, 0 };
NPFFoundTargetData ftd = NPFRouteToStationOrTile(origin.tile, origin.trackdir, true, &fstd, &user);
if (target != nullptr) {
diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp
index 6c1c3ca7a..9613fd079 100644
--- a/src/pathfinder/yapf/yapf_costrail.hpp
+++ b/src/pathfinder/yapf/yapf_costrail.hpp
@@ -496,7 +496,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
if (!tf_local.Follow(cur.tile, cur.td)) {
assert(tf_local.m_err != TrackFollower::EC_NONE);
/* Can't move to the next tile (EOL?). */
- if (tf_local.m_err == TrackFollower::EC_RAIL_TYPE) {
+ if (tf_local.m_err == TrackFollower::EC_RAIL_ROAD_TYPE) {
end_segment_reason |= ESRB_RAIL_TYPE;
} else {
end_segment_reason |= ESRB_DEAD_END;
diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp
index 9e206115f..3935ba18d 100644
--- a/src/pathfinder/yapf/yapf_road.cpp
+++ b/src/pathfinder/yapf/yapf_road.cpp
@@ -249,7 +249,7 @@ public:
} else {
m_dest_station = INVALID_STATION;
m_destTile = v->dest_tile;
- m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, v->compatible_roadtypes));
+ m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype)));
}
}
@@ -367,7 +367,7 @@ public:
/* our source tile will be the next vehicle tile (should be the given one) */
TileIndex src_tile = tile;
/* get available trackdirs on the start tile */
- TrackdirBits src_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes));
+ TrackdirBits src_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype)));
/* select reachable trackdirs only */
src_trackdirs &= DiagdirReachesTrackdirs(enterdir);
@@ -449,7 +449,7 @@ public:
/* set origin (tile, trackdir) */
TileIndex src_tile = v->tile;
Trackdir src_td = v->GetVehicleTrackdir();
- if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->compatible_roadtypes)), src_td)) {
+ if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, this->IsTram() ? RTT_TRAM : RTT_ROAD)), src_td)) {
/* sometimes the roadveh is not on the road (it resides on non-existing track)
* how should we handle that situation? */
return false;
@@ -529,7 +529,7 @@ FindDepotData YapfRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_dist
{
TileIndex tile = v->tile;
Trackdir trackdir = v->GetVehicleTrackdir();
- if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)), trackdir)) {
+ if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype))), trackdir)) {
return FindDepotData();
}
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 0804a4d14..ab87fc40f 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -512,41 +512,48 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
if (GetDisallowedRoadDirections(tile) != DRD_NONE) return_cmd_error(STR_ERROR_CROSSING_ON_ONEWAY_ROAD);
- if (RailNoLevelCrossings(railtype)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED);
+ if (RailNoLevelCrossings(railtype)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_RAIL);
- RoadTypes roadtypes = GetRoadTypes(tile);
- RoadBits road = GetRoadBits(tile, ROADTYPE_ROAD);
- RoadBits tram = GetRoadBits(tile, ROADTYPE_TRAM);
+ RoadType roadtype_road = GetRoadTypeRoad(tile);
+ RoadType roadtype_tram = GetRoadTypeTram(tile);
+
+ if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD);
+ if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD);
+
+ RoadBits road = GetRoadBits(tile, RTT_ROAD);
+ RoadBits tram = GetRoadBits(tile, RTT_TRAM);
if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) ||
(track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) {
- Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
- Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
+ Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
+ Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
/* Disallow breaking end-of-line of someone else
* so trams can still reverse on this tile. */
if (Company::IsValidID(tram_owner) && HasExactlyOneBit(tram)) {
CommandCost ret = CheckOwnership(tram_owner);
if (ret.Failed()) return ret;
}
- /* Crossings must always have a road... */
- uint num_new_road_pieces = 2 - CountBits(road);
- if (road == ROAD_NONE) road_owner = _current_company;
- roadtypes |= ROADTYPES_ROAD;
- /* ...but tram is not required. */
- uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0;
- cost.AddCost((num_new_road_pieces + num_new_tram_pieces) * _price[PR_BUILD_ROAD]);
+ uint num_new_road_pieces = (road != ROAD_NONE) ? 2 - CountBits(road) : 0;
+ if (num_new_road_pieces > 0) {
+ cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road));
+ }
+
+ uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0;
+ if (num_new_tram_pieces > 0) {
+ cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram));
+ }
if (flags & DC_EXEC) {
- MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile));
+ MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
UpdateLevelCrossing(tile, false);
Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
DirtyCompanyInfrastructureWindows(_current_company);
if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
- Company::Get(road_owner)->infrastructure.road[ROADTYPE_ROAD] += num_new_road_pieces;
+ Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces;
DirtyCompanyInfrastructureWindows(road_owner);
}
if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) {
- Company::Get(tram_owner)->infrastructure.road[ROADTYPE_TRAM] += num_new_tram_pieces;
+ Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
DirtyCompanyInfrastructureWindows(tram_owner);
}
}
@@ -644,10 +651,11 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
v = GetTrainForReservation(tile, track);
if (v != nullptr) FreeTrainTrackReservation(v);
}
+
owner = GetTileOwner(tile);
Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR;
DirtyCompanyInfrastructureWindows(owner);
- MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypes(tile), GetTownIndex(tile), GetRoadOwner(tile, ROADTYPE_ROAD), GetRoadOwner(tile, ROADTYPE_TRAM));
+ MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypeRoad(tile), GetRoadTypeTram(tile), GetTownIndex(tile), GetRoadOwner(tile, RTT_ROAD), GetRoadOwner(tile, RTT_TRAM));
DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile);
}
break;
@@ -1579,7 +1587,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
case MP_ROAD:
if (!IsLevelCrossing(tile)) continue;
if (RailNoLevelCrossings(totype)) {
- error.MakeError(STR_ERROR_CROSSING_DISALLOWED);
+ error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
continue;
}
break;
diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp
index 6d70686fd..b024625fa 100644
--- a/src/rail_gui.cpp
+++ b/src/rail_gui.cpp
@@ -2042,5 +2042,11 @@ DropDownList GetRailTypeDropDownList(bool for_replacement, bool all_option)
item->SetParam(1, rti->max_speed);
list.emplace_back(item);
}
+
+ if (list.size() == 0) {
+ /* Empty dropdowns are not allowed */
+ list.emplace_back(new DropDownListStringItem(STR_NONE, INVALID_RAILTYPE, true));
+ }
+
return list;
}
diff --git a/src/road.cpp b/src/road.cpp
index 3700629ef..8c9591ff1 100644
--- a/src/road.cpp
+++ b/src/road.cpp
@@ -19,6 +19,9 @@
#include "engine_base.h"
#include "date_func.h"
#include "landscape.h"
+#include "road.h"
+#include "road_func.h"
+#include "roadveh.h"
#include "safeguards.h"
@@ -72,7 +75,7 @@ RoadBits CleanUpRoadBits(const TileIndex tile, RoadBits org_rb)
/* Always connective */
connective = true;
} else {
- const RoadBits neighbor_rb = GetAnyRoadBits(neighbor_tile, ROADTYPE_ROAD) | GetAnyRoadBits(neighbor_tile, ROADTYPE_TRAM);
+ const RoadBits neighbor_rb = GetAnyRoadBits(neighbor_tile, RTT_ROAD) | GetAnyRoadBits(neighbor_tile, RTT_TRAM);
/* Accept only connective tiles */
connective = (neighbor_rb & mirrored_rb) != ROAD_NONE;
@@ -102,53 +105,236 @@ RoadBits CleanUpRoadBits(const TileIndex tile, RoadBits org_rb)
}
/**
- * Finds out, whether given company has all given RoadTypes available
+ * Finds out, whether given company has a given RoadType available for construction.
* @param company ID of company
- * @param rts RoadTypes to test
- * @return true if company has all requested RoadTypes available
+ * @param roadtypet RoadType to test
+ * @return true if company has the requested RoadType available
*/
-bool HasRoadTypesAvail(const CompanyID company, const RoadTypes rts)
+bool HasRoadTypeAvail(const CompanyID company, RoadType roadtype)
{
- RoadTypes avail_roadtypes;
-
if (company == OWNER_DEITY || company == OWNER_TOWN || _game_mode == GM_EDITOR || _generating_world) {
- avail_roadtypes = ROADTYPES_ROAD;
+ return true; // TODO: should there be a proper check?
} else {
- Company *c = Company::GetIfValid(company);
+ const Company *c = Company::GetIfValid(company);
if (c == nullptr) return false;
- avail_roadtypes = (RoadTypes)c->avail_roadtypes | ROADTYPES_ROAD; // road is available for always for everybody
+ return HasBit(c->avail_roadtypes & ~_roadtypes_hidden_mask, roadtype);
}
- return (rts & ~avail_roadtypes) == 0;
+}
+
+static RoadTypes GetMaskForRoadTramType(RoadTramType rtt)
+{
+ return rtt == RTT_TRAM ? _roadtypes_type : ~_roadtypes_type;
+}
+
+/**
+ * Test if any buildable RoadType is available for a company.
+ * @param company the company in question
+ * @return true if company has any RoadTypes available
+ */
+bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt)
+{
+ return (Company::Get(company)->avail_roadtypes & ~_roadtypes_hidden_mask & GetMaskForRoadTramType(rtt)) != ROADTYPES_NONE;
}
/**
* Validate functions for rail building.
- * @param rt road type to check.
+ * @param roadtype road type to check.
* @return true if the current company may build the road.
*/
-bool ValParamRoadType(const RoadType rt)
+bool ValParamRoadType(RoadType roadtype)
{
- return HasRoadTypesAvail(_current_company, RoadTypeToRoadTypes(rt));
+ return roadtype != INVALID_ROADTYPE && HasRoadTypeAvail(_current_company, roadtype);
+}
+
+/**
+ * Add the road types that are to be introduced at the given date.
+ * @param rt Roadtype
+ * @param current The currently available roadtypes.
+ * @param date The date for the introduction comparisons.
+ * @return The road types that should be available when date
+ * introduced road types are taken into account as well.
+ */
+RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date)
+{
+ RoadTypes rts = current;
+
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+ /* Unused road type. */
+ if (rti->label == 0) continue;
+
+ /* Not date introduced. */
+ if (!IsInsideMM(rti->introduction_date, 0, MAX_DAY)) continue;
+
+ /* Not yet introduced at this date. */
+ if (rti->introduction_date > date) continue;
+
+ /* Have we introduced all required roadtypes? */
+ RoadTypes required = rti->introduction_required_roadtypes;
+ if ((rts & required) != required) continue;
+
+ rts |= rti->introduces_roadtypes;
+ }
+
+ /* When we added roadtypes we need to run this method again; the added
+ * roadtypes might enable more rail types to become introduced. */
+ return rts == current ? rts : AddDateIntroducedRoadTypes(rts, date);
}
/**
* Get the road types the given company can build.
- * @param company the company to get the roadtypes for.
+ * @param company the company to get the road types for.
+ * @param introduces If true, include road types introduced by other road types
* @return the road types.
*/
-RoadTypes GetCompanyRoadtypes(CompanyID company)
+RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces)
{
- RoadTypes rt = ROADTYPES_NONE;
+ RoadTypes rts = ROADTYPES_NONE;
- Engine *e;
+ const Engine *e;
FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
const EngineInfo *ei = &e->info;
if (HasBit(ei->climates, _settings_game.game_creation.landscape) &&
(HasBit(e->company_avail, company) || _date >= e->intro_date + DAYS_IN_YEAR)) {
- SetBit(rt, HasBit(ei->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
+ const RoadVehicleInfo *rvi = &e->u.road;
+ assert(rvi->roadtype < ROADTYPE_END);
+ if (introduces) {
+ rts |= GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes;
+ } else {
+ SetBit(rts, rvi->roadtype);
+ }
+ }
+ }
+
+ if (introduces) return AddDateIntroducedRoadTypes(rts, _date);
+ return rts;
+}
+
+/**
+ * Get list of road types, regardless of company availability.
+ * @param introduces If true, include road types introduced by other road types
+ * @return the road types.
+ */
+RoadTypes GetRoadTypes(bool introduces)
+{
+ RoadTypes rts = ROADTYPES_NONE;
+
+ const Engine *e;
+ FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
+ const EngineInfo *ei = &e->info;
+ if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) continue;
+
+ const RoadVehicleInfo *rvi = &e->u.road;
+ assert(rvi->roadtype < ROADTYPE_END);
+ if (introduces) {
+ rts |= GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes;
+ } else {
+ SetBit(rts, rvi->roadtype);
}
}
- return rt;
+ if (introduces) return AddDateIntroducedRoadTypes(rts, MAX_DAY);
+ return rts;
+}
+
+/**
+ * Get the road type for a given label.
+ * @param label the roadtype label.
+ * @param allow_alternate_labels Search in the alternate label lists as well.
+ * @return the roadtype.
+ */
+RoadType GetRoadTypeByLabel(RoadTypeLabel label, bool allow_alternate_labels)
+{
+ /* Loop through each road type until the label is found */
+ for (RoadType r = ROADTYPE_BEGIN; r != ROADTYPE_END; r++) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(r);
+ if (rti->label == label) return r;
+ }
+
+ if (allow_alternate_labels) {
+ /* Test if any road type defines the label as an alternate. */
+ for (RoadType r = ROADTYPE_BEGIN; r != ROADTYPE_END; r++) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(r);
+ if (std::find(rti->alternate_labels.begin(), rti->alternate_labels.end(), label) != rti->alternate_labels.end()) return r;
+ }
+ }
+
+ /* No matching label was found, so it is invalid */
+ return INVALID_ROADTYPE;
+}
+
+/**
+ * Returns the available RoadSubTypes for the provided RoadType
+ * If the given company is valid then will be returned a list of the available sub types at the current date, while passing
+ * a deity company will make all the sub types available
+ * @param rt the RoadType to filter
+ * @param c the company ID to check the roadtype against
+ * @param any_date whether to return only currently introduced roadtypes or also future ones
+ * @returns the existing RoadSubTypes
+ */
+RoadTypes ExistingRoadTypes(CompanyID c)
+{
+ /* Check only players which can actually own vehicles, editor and gamescripts are considered deities */
+ if (c < OWNER_END) {
+ const Company *company = Company::GetIfValid(c);
+ if (company != nullptr) return company->avail_roadtypes;
+ }
+
+ RoadTypes known_roadtypes = ROADTYPES_NONE;
+
+ /* Find used roadtypes */
+ Engine *e;
+ FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
+ /* Check if the roadtype can be used in the current climate */
+ if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
+
+ /* Check whether available for all potential companies */
+ if (e->company_avail != (CompanyMask)-1) continue;
+
+ known_roadtypes |= GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes;
+ }
+
+ /* Get the date introduced roadtypes as well. */
+ known_roadtypes = AddDateIntroducedRoadTypes(known_roadtypes, MAX_DAY);
+
+ return known_roadtypes;
+}
+
+/**
+ * Check whether we can build infrastructure for the given RoadType. This to disable building stations etc. when
+ * you are not allowed/able to have the RoadType yet.
+ * @param roadtype the roadtype to check this for
+ * @param company the company id to check this for
+ * @param any_date to check only existing vehicles or if it is possible to build them in the future
+ * @return true if there is any reason why you may build the infrastructure for the given roadtype
+ */
+bool CanBuildRoadTypeInfrastructure(RoadType roadtype, CompanyID company)
+{
+ if (_game_mode != GM_EDITOR && !Company::IsValidID(company)) return false;
+ if (!_settings_client.gui.disable_unsuitable_building) return true;
+ if (!HasAnyRoadTypesAvail(company, GetRoadTramType(roadtype))) return false;
+
+ RoadTypes roadtypes = ExistingRoadTypes(company);
+
+ /* Check if the filtered roadtypes does have the roadtype we are checking for
+ * and if we can build new ones */
+ if (_settings_game.vehicle.max_roadveh > 0 && HasBit(roadtypes, roadtype)) {
+ /* Can we actually build the vehicle type? */
+ const Engine *e;
+ FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
+ if (!HasBit(e->company_avail, company)) continue;
+ if (HasPowerOnRoad(e->u.road.roadtype, roadtype) || HasPowerOnRoad(roadtype, e->u.road.roadtype)) return true;
+ }
+ return false;
+ }
+
+ /* We should be able to build infrastructure when we have the actual vehicle type */
+ const Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->type == VEH_ROAD && (company == OWNER_DEITY || v->owner == company) &&
+ HasBit(roadtypes, RoadVehicle::From(v)->roadtype) && HasPowerOnRoad(RoadVehicle::From(v)->roadtype, roadtype)) return true;
+ }
+
+ return false;
}
diff --git a/src/road.h b/src/road.h
new file mode 100644
index 000000000..c359fc779
--- /dev/null
+++ b/src/road.h
@@ -0,0 +1,316 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file road.h Road specific functions. */
+
+#ifndef ROAD_H
+#define ROAD_H
+
+#include "road_type.h"
+#include "gfx_type.h"
+#include "core/bitmath_func.hpp"
+#include "strings_type.h"
+#include "date_type.h"
+#include "core/enum_type.hpp"
+#include "newgrf.h"
+#include "economy_func.h"
+
+#include <vector>
+
+enum RoadTramType : bool {
+ RTT_ROAD,
+ RTT_TRAM,
+};
+
+enum RoadTramTypes : uint8 {
+ RTTB_ROAD = 1 << RTT_ROAD,
+ RTTB_TRAM = 1 << RTT_TRAM,
+};
+DECLARE_ENUM_AS_BIT_SET(RoadTramTypes)
+
+#define FOR_ALL_ROADTRAMTYPES(x) for (RoadTramType x : { RTT_ROAD, RTT_TRAM })
+
+/** Roadtype flags. Starts with RO instead of R because R is used for rails */
+enum RoadTypeFlags {
+ ROTF_CATENARY = 0, ///< Bit number for adding catenary
+ ROTF_NO_LEVEL_CROSSING, ///< Bit number for disabling level crossing
+ ROTF_NO_HOUSES, ///< Bit number for setting this roadtype as not house friendly
+ ROTF_HIDDEN, ///< Bit number for hidden from construction.
+ ROTF_TOWN_BUILD, ///< Bit number for allowing towns to build this roadtype.
+
+ ROTFB_NONE = 0, ///< All flags cleared.
+ ROTFB_CATENARY = 1 << ROTF_CATENARY, ///< Value for drawing a catenary.
+ ROTFB_NO_LEVEL_CROSSING = 1 << ROTF_NO_LEVEL_CROSSING, ///< Value for disabling a level crossing.
+ ROTFB_NO_HOUSES = 1 << ROTF_NO_HOUSES, ///< Value for for setting this roadtype as not house friendly.
+ ROTFB_HIDDEN = 1 << ROTF_HIDDEN, ///< Value for hidden from construction.
+ ROTFB_TOWN_BUILD = 1 << ROTF_TOWN_BUILD, ///< Value for allowing towns to build this roadtype.
+};
+DECLARE_ENUM_AS_BIT_SET(RoadTypeFlags)
+
+struct SpriteGroup;
+
+/** Sprite groups for a roadtype. */
+enum RoadTypeSpriteGroup {
+ ROTSG_CURSORS, ///< Optional: Cursor and toolbar icon images
+ ROTSG_OVERLAY, ///< Optional: Images for overlaying track
+ ROTSG_GROUND, ///< Required: Main group of ground images
+ ROTSG_reserved1, ///< Placeholder, if we need specific tunnel sprites.
+ ROTSG_CATENARY_FRONT, ///< Optional: Catenary front
+ ROTSG_CATENARY_BACK, ///< Optional: Catenary back
+ ROTSG_BRIDGE, ///< Required: Bridge surface images
+ ROTSG_reserved2, ///< Placeholder, if we need specific level crossing sprites.
+ ROTSG_DEPOT, ///< Optional: Depot images
+ ROTSG_reserved3, ///< Placeholder, if we add road fences (for highways).
+ ROTSG_ROADSTOP, ///< Required: Drive-in stop surface
+ ROTSG_END,
+};
+
+/** List of road type labels. */
+typedef std::vector<RoadTypeLabel> RoadTypeLabelList;
+
+class RoadTypeInfo {
+public:
+ /**
+ * struct containing the sprites for the road GUI. @note only sprites referred to
+ * directly in the code are listed
+ */
+ struct {
+ SpriteID build_x_road; ///< button for building single rail in X direction
+ SpriteID build_y_road; ///< button for building single rail in Y direction
+ SpriteID auto_road; ///< button for the autoroad construction
+ SpriteID build_depot; ///< button for building depots
+ SpriteID build_tunnel; ///< button for building a tunnel
+ SpriteID convert_road; ///< button for converting road types
+ } gui_sprites;
+
+ struct {
+ CursorID road_swne; ///< Cursor for building rail in X direction
+ CursorID road_nwse; ///< Cursor for building rail in Y direction
+ CursorID autoroad; ///< Cursor for autorail tool
+ CursorID depot; ///< Cursor for building a depot
+ CursorID tunnel; ///< Cursor for building a tunnel
+ SpriteID convert_road; ///< Cursor for converting road types
+ } cursor; ///< Cursors associated with the road type.
+
+ struct {
+ StringID name; ///< Name of this rail type.
+ StringID toolbar_caption; ///< Caption in the construction toolbar GUI for this rail type.
+ StringID menu_text; ///< Name of this rail type in the main toolbar dropdown.
+ StringID build_caption; ///< Caption of the build vehicle GUI for this rail type.
+ StringID replace_text; ///< Text used in the autoreplace GUI.
+ StringID new_engine; ///< Name of an engine for this type of road in the engine preview GUI.
+
+ StringID err_build_road; ///< Building a normal piece of road
+ StringID err_remove_road; ///< Removing a normal piece of road
+ StringID err_depot; ///< Building a depot
+ StringID err_build_station[2]; ///< Building a bus or truck station
+ StringID err_remove_station[2]; ///< Removing of a bus or truck station
+ StringID err_convert_road; ///< Converting a road type
+
+ StringID picker_title[2]; ///< Title for the station picker for bus or truck stations
+ StringID picker_tooltip[2]; ///< Tooltip for the station picker for bus or truck stations
+ } strings; ///< Strings associated with the rail type.
+
+ /** bitmask to the OTHER roadtypes on which a vehicle of THIS roadtype generates power */
+ RoadTypes powered_roadtypes;
+
+ /**
+ * Bit mask of road type flags
+ */
+ RoadTypeFlags flags;
+
+ /**
+ * Cost multiplier for building this road type
+ */
+ uint16 cost_multiplier;
+
+ /**
+ * Cost multiplier for maintenance of this road type
+ */
+ uint16 maintenance_multiplier;
+
+ /**
+ * Maximum speed for vehicles travelling on this road type
+ */
+ uint16 max_speed;
+
+ /**
+ * Unique 32 bit road type identifier
+ */
+ RoadTypeLabel label;
+
+ /**
+ * Road type labels this type provides in addition to the main label.
+ */
+ RoadTypeLabelList alternate_labels;
+
+ /**
+ * Colour on mini-map
+ */
+ byte map_colour;
+
+ /**
+ * Introduction date.
+ * When #INVALID_DATE or a vehicle using this roadtype gets introduced earlier,
+ * the vehicle's introduction date will be used instead for this roadtype.
+ * The introduction at this date is furthermore limited by the
+ * #introduction_required_types.
+ */
+ Date introduction_date;
+
+ /**
+ * Bitmask of roadtypes that are required for this roadtype to be introduced
+ * at a given #introduction_date.
+ */
+ RoadTypes introduction_required_roadtypes;
+
+ /**
+ * Bitmask of which other roadtypes are introduced when this roadtype is introduced.
+ */
+ RoadTypes introduces_roadtypes;
+
+ /**
+ * The sorting order of this roadtype for the toolbar dropdown.
+ */
+ byte sorting_order;
+
+ /**
+ * NewGRF providing the Action3 for the roadtype. nullptr if not available.
+ */
+ const GRFFile *grffile[ROTSG_END];
+
+ /**
+ * Sprite groups for resolving sprites
+ */
+ const SpriteGroup *group[ROTSG_END];
+
+ inline bool UsesOverlay() const
+ {
+ return this->group[ROTSG_GROUND] != nullptr;
+ }
+};
+
+extern RoadTypes _roadtypes_type;
+
+static inline bool RoadTypeIsRoad(RoadType roadtype)
+{
+ return !HasBit(_roadtypes_type, roadtype);
+}
+
+static inline bool RoadTypeIsTram(RoadType roadtype)
+{
+ return HasBit(_roadtypes_type, roadtype);
+}
+
+static inline RoadTramType GetRoadTramType(RoadType roadtype)
+{
+ return RoadTypeIsTram(roadtype) ? RTT_TRAM : RTT_ROAD;
+}
+
+static inline RoadTramType OtherRoadTramType(RoadTramType rtt)
+{
+ return rtt == RTT_ROAD ? RTT_TRAM : RTT_ROAD;
+}
+
+/**
+ * Returns a pointer to the Roadtype information for a given roadtype
+ * @param roadtype the road type which the information is requested for
+ * @return The pointer to the RoadTypeInfo
+ */
+static inline const RoadTypeInfo *GetRoadTypeInfo(RoadType roadtype)
+{
+ extern RoadTypeInfo _roadtypes[ROADTYPE_END];
+ assert(roadtype < ROADTYPE_END);
+ return &_roadtypes[roadtype];
+}
+
+/**
+ * Checks if an engine of the given RoadType got power on a tile with a given
+ * RoadType. This would normally just be an equality check, but for electrified
+ * roads (which also support non-electric vehicles).
+ * @return Whether the engine got power on this tile.
+ * @param enginetype The RoadType of the engine we are considering.
+ * @param tiletype The RoadType of the tile we are considering.
+ */
+static inline bool HasPowerOnRoad(RoadType enginetype, RoadType tiletype)
+{
+ return HasBit(GetRoadTypeInfo(enginetype)->powered_roadtypes, tiletype);
+}
+
+/**
+ * Returns the cost of building the specified roadtype.
+ * @param roadtype The roadtype being built.
+ * @return The cost multiplier.
+ */
+static inline Money RoadBuildCost(RoadType roadtype)
+{
+ assert(roadtype < ROADTYPE_END);
+ return (_price[PR_BUILD_ROAD] * GetRoadTypeInfo(roadtype)->cost_multiplier) >> 3;
+}
+
+/**
+ * Returns the cost of clearing the specified roadtype.
+ * @param roadtype The roadtype being removed.
+ * @return The cost.
+ */
+static inline Money RoadClearCost(RoadType roadtype)
+{
+ assert(roadtype < ROADTYPE_END);
+
+ /* Flat fee for removing road. */
+ if (RoadTypeIsRoad(roadtype)) return _price[PR_CLEAR_ROAD];
+
+ /* Clearing tram earns a little money, but also incurs the standard clear road cost,
+ * so no profit can be made. */
+ return _price[PR_CLEAR_ROAD] - RoadBuildCost(roadtype) * 3 / 4;
+}
+
+/**
+ * Calculates the cost of road conversion
+ * @param from The roadtype we are converting from
+ * @param to The roadtype we are converting to
+ * @return Cost per RoadBit
+ */
+static inline Money RoadConvertCost(RoadType from, RoadType to)
+{
+ /* Don't apply convert costs when converting to the same roadtype (ex. building a roadstop over existing road) */
+ if (from == to) return (Money)0;
+
+ /* Same cost as removing and then building. */
+ return RoadBuildCost(to) + RoadClearCost(from);
+}
+
+/**
+ * Test if road disallows level crossings
+ * @param roadtype The roadtype we are testing
+ * @return True iff the roadtype disallows level crossings
+ */
+static inline bool RoadNoLevelCrossing(RoadType roadtype)
+{
+ assert(roadtype < ROADTYPE_END);
+ return HasBit(GetRoadTypeInfo(roadtype)->flags, ROTF_NO_LEVEL_CROSSING);
+}
+
+RoadType GetRoadTypeByLabel(RoadTypeLabel label, bool allow_alternate_labels = true);
+
+void ResetRoadTypes();
+void InitRoadTypes();
+RoadType AllocateRoadType(RoadTypeLabel label, RoadTramType rtt);
+bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt);
+
+extern std::vector<RoadType> _sorted_roadtypes;
+extern RoadTypes _roadtypes_hidden_mask;
+
+/**
+ * Loop header for iterating over roadtypes, sorted by sortorder.
+ * @param var Roadtype.
+ */
+#define FOR_ALL_SORTED_ROADTYPES(var) for (uint8 index = 0; index < _sorted_roadtypes.size() && (var = _sorted_roadtypes[index], true) ; index++)
+
+#endif /* ROAD_H */
diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp
index e9edbf254..325dd0f6e 100644
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -11,6 +11,7 @@
#include "stdafx.h"
#include "cmd_helper.h"
+#include "road.h"
#include "road_internal.h"
#include "viewport_func.h"
#include "command_func.h"
@@ -31,15 +32,152 @@
#include "town.h"
#include "company_base.h"
#include "core/random_func.hpp"
+#include "newgrf_debug.h"
#include "newgrf_railtype.h"
+#include "newgrf_roadtype.h"
#include "date_func.h"
#include "genworld.h"
#include "company_gui.h"
+#include "road_func.h"
#include "table/strings.h"
+#include "table/roadtypes.h"
#include "safeguards.h"
+/** Helper type for lists/vectors of road vehicles */
+typedef std::vector<RoadVehicle *> RoadVehicleList;
+
+RoadTypeInfo _roadtypes[ROADTYPE_END];
+std::vector<RoadType> _sorted_roadtypes;
+RoadTypes _roadtypes_hidden_mask;
+
+/**
+ * Bitmap of road/tram types.
+ * Bit if set if a roadtype is tram.
+ */
+RoadTypes _roadtypes_type;
+
+/**
+ * Reset all road type information to its default values.
+ */
+void ResetRoadTypes()
+{
+ assert_compile(lengthof(_original_roadtypes) <= lengthof(_roadtypes));
+
+ uint i = 0;
+ for (; i < lengthof(_original_roadtypes); i++) _roadtypes[i] = _original_roadtypes[i];
+
+ static const RoadTypeInfo empty_roadtype = {
+ { 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, {}, {}, 0, {}, {} },
+ ROADTYPES_NONE, ROTFB_NONE, 0, 0, 0, 0,
+ RoadTypeLabelList(), 0, 0, ROADTYPES_NONE, ROADTYPES_NONE, 0,
+ {}, {} };
+ for (; i < lengthof(_roadtypes); i++) _roadtypes[i] = empty_roadtype;
+
+ _roadtypes_hidden_mask = ROADTYPES_NONE;
+ _roadtypes_type = ROADTYPES_TRAM;
+}
+
+void ResolveRoadTypeGUISprites(RoadTypeInfo *rti)
+{
+ SpriteID cursors_base = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_CURSORS);
+ if (cursors_base != 0) {
+ rti->gui_sprites.build_x_road = cursors_base + 0;
+ rti->gui_sprites.build_y_road = cursors_base + 1;
+ rti->gui_sprites.auto_road = cursors_base + 2;
+ rti->gui_sprites.build_depot = cursors_base + 3;
+ rti->gui_sprites.build_tunnel = cursors_base + 4;
+ rti->gui_sprites.convert_road = cursors_base + 5;
+ rti->cursor.road_swne = cursors_base + 6;
+ rti->cursor.road_nwse = cursors_base + 7;
+ rti->cursor.autoroad = cursors_base + 8;
+ rti->cursor.depot = cursors_base + 9;
+ rti->cursor.tunnel = cursors_base + 10;
+ rti->cursor.convert_road = cursors_base + 11;
+ }
+}
+
+/**
+ * Compare roadtypes based on their sorting order.
+ * @param first The roadtype to compare to.
+ * @param second The roadtype to compare.
+ * @return True iff the first should be sorted before the second.
+ */
+static bool CompareRoadTypes(const RoadType &first, const RoadType &second)
+{
+ if (RoadTypeIsRoad(first) == RoadTypeIsRoad(second)) {
+ return GetRoadTypeInfo(first)->sorting_order < GetRoadTypeInfo(second)->sorting_order;
+ }
+ return RoadTypeIsTram(first) < RoadTypeIsTram(second);
+}
+
+/**
+ * Resolve sprites of custom road types
+ */
+void InitRoadTypes()
+{
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ RoadTypeInfo *rti = &_roadtypes[rt];
+ ResolveRoadTypeGUISprites(rti);
+ if (HasBit(rti->flags, ROTF_HIDDEN)) SetBit(_roadtypes_hidden_mask, rt);
+ }
+
+ _sorted_roadtypes.clear();
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ if (_roadtypes[rt].label != 0 && !HasBit(_roadtypes_hidden_mask, rt)) {
+ _sorted_roadtypes.push_back(rt);
+ }
+ }
+ std::sort(_sorted_roadtypes.begin(), _sorted_roadtypes.end(), CompareRoadTypes);
+}
+
+/**
+ * Allocate a new road type label
+ */
+RoadType AllocateRoadType(RoadTypeLabel label, RoadTramType rtt)
+{
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ RoadTypeInfo *rti = &_roadtypes[rt];
+
+ if (rti->label == 0) {
+ /* Set up new road type */
+ *rti = _original_roadtypes[(rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD];
+ rti->label = label;
+ rti->alternate_labels.clear();
+ rti->flags = ROTFB_NONE;
+ rti->introduction_date = INVALID_DATE;
+
+ /* Make us compatible with ourself. */
+ rti->powered_roadtypes = (RoadTypes)(1ULL << rt);
+
+ /* We also introduce ourself. */
+ rti->introduces_roadtypes = (RoadTypes)(1ULL << rt);
+
+ /* Default sort order; order of allocation, but with some
+ * offsets so it's easier for NewGRF to pick a spot without
+ * changing the order of other (original) road types.
+ * The << is so you can place other roadtypes in between the
+ * other roadtypes, the 7 is to be able to place something
+ * before the first (default) road type. */
+ rti->sorting_order = rt << 2 | 7;
+
+ /* Set bitmap of road/tram types */
+ if (rtt == RTT_TRAM) {
+ SetBit(_roadtypes_type, rt);
+ } else {
+ ClrBit(_roadtypes_type, rt);
+ }
+
+ return rt;
+ }
+ }
+
+ return INVALID_ROADTYPE;
+}
+
/**
* Verify whether a road vehicle is available.
* @return \c true if at least one road vehicle is available, \c false if not
@@ -52,6 +190,23 @@ bool RoadVehiclesAreBuilt()
return false;
}
+/**
+ * Update road infrastructure counts for a company.
+ * @param rt Road type to update count of.
+ * @param o Owner of road piece.
+ * @param count Number of road pieces to adjust.
+ */
+void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count)
+{
+ if (rt == INVALID_ROADTYPE) return;
+
+ Company *c = Company::GetIfValid(o);
+ if (c == nullptr) return;
+
+ c->infrastructure.road[rt] += count;
+ DirtyCompanyInfrastructureWindows(c->index);
+}
+
/** Invalid RoadBits on slopes. */
static const RoadBits _invalid_tileh_slopes_road[2][15] = {
/* The inverse of the mixable RoadBits on a leveled slope */
@@ -113,7 +268,7 @@ static Foundation GetRoadFoundation(Slope tileh, RoadBits bits);
* @param town_check Shall the town rating checked/affected
* @return A succeeded command when it is allowed to remove the road bits, a failed command otherwise.
*/
-CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadType rt, DoCommandFlag flags, bool town_check)
+CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlag flags, bool town_check)
{
if (_game_mode == GM_EDITOR || remove == ROAD_NONE) return CommandCost();
@@ -121,7 +276,7 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R
* Towns are not be allowed to remove non "normal" road pieces, like tram
* tracks as that would result in trams that cannot turn. */
if (_current_company == OWNER_WATER ||
- (rt == ROADTYPE_ROAD && !Company::IsValidID(_current_company))) return CommandCost();
+ (rtt == RTT_ROAD && !Company::IsValidID(_current_company))) return CommandCost();
/* Only do the special processing if the road is owned
* by a town */
@@ -145,11 +300,11 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R
/* Get a bitmask of which neighbouring roads has a tile */
RoadBits n = ROAD_NONE;
- RoadBits present = GetAnyRoadBits(tile, rt);
- if ((present & ROAD_NE) && (GetAnyRoadBits(TILE_ADDXY(tile, -1, 0), rt) & ROAD_SW)) n |= ROAD_NE;
- if ((present & ROAD_SE) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, 1), rt) & ROAD_NW)) n |= ROAD_SE;
- if ((present & ROAD_SW) && (GetAnyRoadBits(TILE_ADDXY(tile, 1, 0), rt) & ROAD_NE)) n |= ROAD_SW;
- if ((present & ROAD_NW) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, -1), rt) & ROAD_SE)) n |= ROAD_NW;
+ RoadBits present = GetAnyRoadBits(tile, rtt);
+ if ((present & ROAD_NE) && (GetAnyRoadBits(TILE_ADDXY(tile, -1, 0), rtt) & ROAD_SW)) n |= ROAD_NE;
+ if ((present & ROAD_SE) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, 1), rtt) & ROAD_NW)) n |= ROAD_SE;
+ if ((present & ROAD_SW) && (GetAnyRoadBits(TILE_ADDXY(tile, 1, 0), rtt) & ROAD_NE)) n |= ROAD_SW;
+ if ((present & ROAD_NW) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, -1), rtt) & ROAD_SE)) n |= ROAD_NW;
int rating_decrease = RATING_ROAD_DOWN_STEP_EDGE;
/* If 0 or 1 bits are set in n, or if no bits that match the bits to remove,
@@ -177,11 +332,13 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R
* @param crossing_check should we check if there is a tram track when we are removing road from crossing?
* @param town_check should we check if the town allows removal?
*/
-static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits pieces, RoadType rt, bool crossing_check, bool town_check = true)
+static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits pieces, RoadTramType rtt, bool crossing_check, bool town_check = true)
{
- RoadTypes rts = GetRoadTypes(tile);
+ assert(pieces != ROAD_NONE);
+
+ RoadType existing_rt = MayHaveRoad(tile) ? GetRoadType(tile, rtt) : INVALID_ROADTYPE;
/* The tile doesn't have the given road type */
- if (!HasBit(rts, rt)) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
+ if (existing_rt == INVALID_ROADTYPE) return_cmd_error((rtt == RTT_TRAM) ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
switch (GetTileType(tile)) {
case MP_ROAD: {
@@ -209,37 +366,32 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
return CMD_ERROR;
}
- CommandCost ret = CheckAllowRemoveRoad(tile, pieces, GetRoadOwner(tile, rt), rt, flags, town_check);
+ CommandCost ret = CheckAllowRemoveRoad(tile, pieces, GetRoadOwner(tile, rtt), rtt, flags, town_check);
if (ret.Failed()) return ret;
if (!IsTileType(tile, MP_ROAD)) {
/* If it's the last roadtype, just clear the whole tile */
- if (rts == RoadTypeToRoadTypes(rt)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
+ if (GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
CommandCost cost(EXPENSES_CONSTRUCTION);
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
/* Removing any roadbit in the bridge axis removes the roadtype (that's the behaviour remove-long-roads needs) */
- if ((AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) & pieces) == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
+ if ((AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) & pieces) == ROAD_NONE) return_cmd_error((rtt == RTT_TRAM) ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
/* Pay for *every* tile of the bridge or tunnel */
uint len = GetTunnelBridgeLength(other_end, tile) + 2;
- cost.AddCost(len * 2 * _price[PR_CLEAR_ROAD]);
+ cost.AddCost(len * 2 * RoadClearCost(existing_rt));
if (flags & DC_EXEC) {
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- /* A full diagonal road tile has two road bits. */
- c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
- DirtyCompanyInfrastructureWindows(c->index);
- }
+ /* A full diagonal road tile has two road bits. */
+ UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
- SetRoadTypes(other_end, GetRoadTypes(other_end) & ~RoadTypeToRoadTypes(rt));
- SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt));
+ SetRoadType(other_end, rtt, INVALID_ROADTYPE);
+ SetRoadType(tile, rtt, INVALID_ROADTYPE);
/* If the owner of the bridge sells all its road, also move the ownership
* to the owner of the other roadtype, unless the bridge owner is a town. */
- RoadType other_rt = (rt == ROADTYPE_ROAD) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
- Owner other_owner = GetRoadOwner(tile, other_rt);
+ Owner other_owner = GetRoadOwner(tile, OtherRoadTramType(rtt));
if (!IsTileOwner(tile, other_owner) && !IsTileOwner(tile, OWNER_TOWN)) {
SetTileOwner(tile, other_owner);
SetTileOwner(other_end, other_owner);
@@ -255,15 +407,11 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
}
} else {
assert(IsDriveThroughStopTile(tile));
- cost.AddCost(_price[PR_CLEAR_ROAD] * 2);
+ cost.AddCost(RoadClearCost(existing_rt) * 2);
if (flags & DC_EXEC) {
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- /* A full diagonal road tile has two road bits. */
- c->infrastructure.road[rt] -= 2;
- DirtyCompanyInfrastructureWindows(c->index);
- }
- SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt));
+ /* A full diagonal road tile has two road bits. */
+ UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -2);
+ SetRoadType(tile, rtt, INVALID_ROADTYPE);
MarkTileDirtyByTile(tile);
}
}
@@ -279,8 +427,8 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
tileh = SlopeWithOneCornerRaised(GetHighestSlopeCorner(tileh));
}
- RoadBits present = GetRoadBits(tile, rt);
- const RoadBits other = GetOtherRoadBits(tile, rt);
+ RoadBits present = GetRoadBits(tile, rtt);
+ const RoadBits other = GetRoadBits(tile, OtherRoadTramType(rtt));
const Foundation f = GetRoadFoundation(tileh, present);
if (HasRoadWorks(tile) && _current_company != OWNER_WATER) return_cmd_error(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
@@ -295,7 +443,7 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
/* limit the bits to delete to the existing bits. */
pieces &= present;
- if (pieces == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
+ if (pieces == ROAD_NONE) return_cmd_error((rtt == RTT_TRAM) ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
/* Now set present what it will be after the remove */
present ^= pieces;
@@ -318,38 +466,34 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
}
}
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- c->infrastructure.road[rt] -= CountBits(pieces);
- DirtyCompanyInfrastructureWindows(c->index);
- }
+ UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -(int)CountBits(pieces));
if (present == ROAD_NONE) {
- RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt));
- if (rts == ROADTYPES_NONE) {
+ /* No other road type, just clear tile. */
+ if (GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) {
/* Includes MarkTileDirtyByTile() */
DoClearSquare(tile);
} else {
- if (rt == ROADTYPE_ROAD && IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) {
+ if (rtt == RTT_ROAD && IsRoadOwner(tile, rtt, OWNER_TOWN)) {
/* Update nearest-town index */
const Town *town = CalcClosestTownFromTile(tile);
SetTownIndex(tile, town == nullptr ? INVALID_TOWN : town->index);
}
- SetRoadBits(tile, ROAD_NONE, rt);
- SetRoadTypes(tile, rts);
+ SetRoadBits(tile, ROAD_NONE, rtt);
+ SetRoadType(tile, rtt, INVALID_ROADTYPE);
MarkTileDirtyByTile(tile);
}
} else {
/* When bits are removed, you *always* end up with something that
* is not a complete straight road tile. However, trams do not have
* onewayness, so they cannot remove it either. */
- if (rt != ROADTYPE_TRAM) SetDisallowedRoadDirections(tile, DRD_NONE);
- SetRoadBits(tile, present, rt);
+ if (rtt == RTT_ROAD) SetDisallowedRoadDirections(tile, DRD_NONE);
+ SetRoadBits(tile, present, rtt);
MarkTileDirtyByTile(tile);
}
}
- CommandCost cost(EXPENSES_CONSTRUCTION, CountBits(pieces) * _price[PR_CLEAR_ROAD]);
+ CommandCost cost(EXPENSES_CONSTRUCTION, CountBits(pieces) * RoadClearCost(existing_rt));
/* If we build a foundation we have to pay for it. */
if (f == FOUNDATION_NONE && GetRoadFoundation(tileh, present) != FOUNDATION_NONE) cost.AddCost(_price[PR_BUILD_FOUNDATION]);
@@ -361,21 +505,12 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
return CMD_ERROR;
}
- /* Don't allow road to be removed from the crossing when there is tram;
- * we can't draw the crossing without roadbits ;) */
- if (rt == ROADTYPE_ROAD && HasTileRoadType(tile, ROADTYPE_TRAM) && (flags & DC_EXEC || crossing_check)) return CMD_ERROR;
-
if (flags & DC_EXEC) {
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- /* A full diagonal road tile has two road bits. */
- c->infrastructure.road[rt] -= 2;
- DirtyCompanyInfrastructureWindows(c->index);
- }
+ /* A full diagonal road tile has two road bits. */
+ UpdateCompanyRoadInfrastructure(existing_rt, GetRoadOwner(tile, rtt), -2);
Track railtrack = GetCrossingRailTrack(tile);
- RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt));
- if (rts == ROADTYPES_NONE) {
+ if (GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) {
TrackBits tracks = GetCrossingRailBits(tile);
bool reserved = HasCrossingReservation(tile);
MakeRailNormal(tile, GetTileOwner(tile), tracks, GetRailType(tile));
@@ -383,18 +518,18 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec
/* Update rail count for level crossings. The plain track should still be accounted
* for, so only subtract the difference to the level crossing cost. */
- c = Company::GetIfValid(GetTileOwner(tile));
+ Company *c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) {
c->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR - 1;
DirtyCompanyInfrastructureWindows(c->index);
}
} else {
- SetRoadTypes(tile, rts);
+ SetRoadType(tile, rtt, INVALID_ROADTYPE);
}
MarkTileDirtyByTile(tile);
YapfNotifyTrackLayoutChange(tile, railtrack);
}
- return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROAD] * 2);
+ return CommandCost(EXPENSES_CONSTRUCTION, RoadClearCost(existing_rt) * 2);
}
default:
@@ -475,8 +610,8 @@ static CommandCost CheckRoadSlope(Slope tileh, RoadBits *pieces, RoadBits existi
* @param tile tile where to build road
* @param flags operation to perform
* @param p1 bit 0..3 road pieces to build (RoadBits)
- * bit 4..5 road type
- * bit 6..7 disallowed directions to toggle
+ * bit 4..9 road type
+ * bit 11..12 disallowed directions to toggle
* @param p2 the town that is building the road (0 if not applicable)
* @param text unused
* @return the cost of this operation or an error
@@ -511,12 +646,13 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
/* do not allow building 'zero' road bits, code wouldn't handle it */
if (pieces == ROAD_NONE) return CMD_ERROR;
- RoadType rt = Extract<RoadType, 4, 2>(p1);
- if (!IsValidRoadType(rt) || !ValParamRoadType(rt)) return CMD_ERROR;
+ RoadType rt = Extract<RoadType, 4, 6>(p1);
+ if (!ValParamRoadType(rt)) return CMD_ERROR;
- DisallowedRoadDirections toggle_drd = Extract<DisallowedRoadDirections, 6, 2>(p1);
+ DisallowedRoadDirections toggle_drd = Extract<DisallowedRoadDirections, 11, 2>(p1);
Slope tileh = GetTileSlope(tile);
+ RoadTramType rtt = GetRoadTramType(rt);
bool need_to_clear = false;
switch (GetTileType(tile)) {
@@ -525,21 +661,21 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
case ROAD_TILE_NORMAL: {
if (HasRoadWorks(tile)) return_cmd_error(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
- other_bits = GetOtherRoadBits(tile, rt);
- if (!HasTileRoadType(tile, rt)) break;
+ other_bits = GetRoadBits(tile, OtherRoadTramType(rtt));
+ if (!HasTileRoadType(tile, rtt)) break;
- existing = GetRoadBits(tile, rt);
+ existing = GetRoadBits(tile, rtt);
bool crossing = !IsStraightRoad(existing | pieces);
- if (rt != ROADTYPE_TRAM && (GetDisallowedRoadDirections(tile) != DRD_NONE || toggle_drd != DRD_NONE) && crossing) {
+ if (rtt == RTT_ROAD && (GetDisallowedRoadDirections(tile) != DRD_NONE || toggle_drd != DRD_NONE) && crossing) {
/* Junctions cannot be one-way */
return_cmd_error(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION);
}
if ((existing & pieces) == pieces) {
/* We only want to set the (dis)allowed road directions */
- if (toggle_drd != DRD_NONE && rt != ROADTYPE_TRAM) {
+ if (toggle_drd != DRD_NONE && rtt == RTT_ROAD) {
if (crossing) return_cmd_error(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION);
- Owner owner = GetRoadOwner(tile, ROADTYPE_ROAD);
+ Owner owner = GetRoadOwner(tile, rtt);
if (owner != OWNER_NONE) {
CommandCost ret = CheckOwnership(owner, tile);
if (ret.Failed()) return ret;
@@ -558,7 +694,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
/* Ignore half built tiles */
- if ((flags & DC_EXEC) && rt != ROADTYPE_TRAM && IsStraightRoad(existing)) {
+ if ((flags & DC_EXEC) && IsStraightRoad(existing)) {
SetDisallowedRoadDirections(tile, dis_new);
MarkTileDirtyByTile(tile);
}
@@ -568,8 +704,8 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
/* Disallow breaking end-of-line of someone else
* so trams can still reverse on this tile. */
- if (rt == ROADTYPE_TRAM && HasExactlyOneBit(existing)) {
- Owner owner = GetRoadOwner(tile, rt);
+ if (rtt == RTT_TRAM && HasExactlyOneBit(existing)) {
+ Owner owner = GetRoadOwner(tile, rtt);
if (Company::IsValidID(owner)) {
CommandCost ret = CheckOwnership(owner);
if (ret.Failed()) return ret;
@@ -579,15 +715,19 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
case ROAD_TILE_CROSSING:
+ if (RoadNoLevelCrossing(rt)) {
+ return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD);
+ }
+
other_bits = GetCrossingRoadBits(tile);
if (pieces & ComplementRoadBits(other_bits)) goto do_clear;
pieces = other_bits; // we need to pay for both roadbits
- if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
break;
case ROAD_TILE_DEPOT:
- if ((GetAnyRoadBits(tile, rt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
goto do_clear;
default: NOT_REACHED();
@@ -606,8 +746,12 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (GetRailTileType(tile) != RAIL_TILE_NORMAL) goto do_clear;
+ if (RoadNoLevelCrossing(rt)) {
+ return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD);
+ }
+
if (RailNoLevelCrossings(GetRailType(tile))) {
- return_cmd_error(STR_ERROR_CROSSING_DISALLOWED);
+ return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_RAIL);
}
Axis roaddir;
@@ -632,15 +776,11 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
Track railtrack = AxisToTrack(OtherAxis(roaddir));
YapfNotifyTrackLayoutChange(tile, railtrack);
/* Update company infrastructure counts. A level crossing has two road bits. */
- Company *c = Company::GetIfValid(company);
- if (c != nullptr) {
- c->infrastructure.road[rt] += 2;
- if (rt != ROADTYPE_ROAD) c->infrastructure.road[ROADTYPE_ROAD] += 2;
- DirtyCompanyInfrastructureWindows(company);
- }
+ UpdateCompanyRoadInfrastructure(rt, company, 2);
+
/* Update rail count for level crossings. The plain track is already
* counted, so only add the difference to the level crossing cost. */
- c = Company::GetIfValid(GetTileOwner(tile));
+ Company *c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) {
c->infrastructure.rail[GetRailType(tile)] += LEVELCROSSING_TRACKBIT_FACTOR - 1;
DirtyCompanyInfrastructureWindows(c->index);
@@ -648,23 +788,23 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
/* Always add road to the roadtypes (can't draw without it) */
bool reserved = HasBit(GetRailReservationTrackBits(tile), railtrack);
- MakeRoadCrossing(tile, company, company, GetTileOwner(tile), roaddir, GetRailType(tile), RoadTypeToRoadTypes(rt) | ROADTYPES_ROAD, p2);
+ MakeRoadCrossing(tile, company, company, GetTileOwner(tile), roaddir, GetRailType(tile), rtt == RTT_ROAD ? rt : INVALID_ROADTYPE, (rtt == RTT_TRAM) ? rt : INVALID_ROADTYPE, p2);
SetCrossingReservation(tile, reserved);
UpdateLevelCrossing(tile, false);
MarkTileDirtyByTile(tile);
}
- return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_ROAD] * (rt == ROADTYPE_ROAD ? 2 : 4));
+ return CommandCost(EXPENSES_CONSTRUCTION, 2 * RoadBuildCost(rt));
}
case MP_STATION: {
- if ((GetAnyRoadBits(tile, rt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
if (!IsDriveThroughStopTile(tile)) goto do_clear;
RoadBits curbits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(tile)));
if (pieces & ~curbits) goto do_clear;
pieces = curbits; // we need to pay for both roadbits
- if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
break;
}
@@ -672,7 +812,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) goto do_clear;
/* Only allow building the outern roadbit, so building long roads stops at existing bridges */
if (MirrorRoadBits(DiagDirToRoadBits(GetTunnelBridgeDirection(tile))) != pieces) goto do_clear;
- if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
+ if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT);
/* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */
CommandCost ret = TunnelBridgeIsFree(tile, GetOtherTunnelBridgeEnd(tile));
if (ret.Failed()) return ret;
@@ -713,15 +853,10 @@ do_clear:;
Slope slope = GetTileSlope(tile);
Foundation found_new = GetRoadFoundation(slope, pieces | existing);
- /* Test if all other roadtypes can be built at that foundation */
- for (RoadType rtest = ROADTYPE_ROAD; rtest < ROADTYPE_END; rtest++) {
- if (rtest != rt) { // check only other road types
- RoadBits bits = GetRoadBits(tile, rtest);
- /* do not check if there are not road bits of given type */
- if (bits != ROAD_NONE && GetRoadFoundation(slope, bits) != found_new) {
- return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
- }
- }
+ RoadBits bits = GetRoadBits(tile, OtherRoadTramType(rtt));
+ /* do not check if there are not road bits of given type */
+ if (bits != ROAD_NONE && GetRoadFoundation(slope, bits) != found_new) {
+ return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
}
}
}
@@ -729,6 +864,23 @@ do_clear:;
CommandCost ret = EnsureNoVehicleOnGround(tile);
if (ret.Failed()) return ret;
+ if (IsNormalRoadTile(tile)) {
+ /* If the road types don't match, try to convert only if vehicles of
+ * the new road type are not powered on the present road type and vehicles of
+ * the present road type are powered on the new road type. */
+ RoadType existing_rt = GetRoadType(tile, rtt);
+ if (existing_rt != INVALID_ROADTYPE && existing_rt != rt) {
+ if (HasPowerOnRoad(rt, existing_rt)) {
+ rt = existing_rt;
+ } else if (HasPowerOnRoad(existing_rt, rt)) {
+ CommandCost ret = DoCommand(tile, tile, rt, flags, CMD_CONVERT_ROAD);
+ if (ret.Failed()) return ret;
+ cost.AddCost(ret);
+ } else {
+ return CMD_ERROR;
+ }
+ }
+ }
}
uint num_pieces = (!need_to_clear && IsTileType(tile, MP_TUNNELBRIDGE)) ?
@@ -737,28 +889,28 @@ do_clear:;
/* Count pieces */
CountBits(pieces);
- cost.AddCost(num_pieces * _price[PR_BUILD_ROAD]);
+ cost.AddCost(num_pieces * RoadBuildCost(rt));
if (flags & DC_EXEC) {
switch (GetTileType(tile)) {
case MP_ROAD: {
- RoadTileType rtt = GetRoadTileType(tile);
- if (existing == ROAD_NONE || rtt == ROAD_TILE_CROSSING) {
- SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt));
- SetRoadOwner(tile, rt, company);
- if (rt == ROADTYPE_ROAD) SetTownIndex(tile, p2);
+ RoadTileType rttype = GetRoadTileType(tile);
+ if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) {
+ SetRoadType(tile, rtt, rt);
+ SetRoadOwner(tile, rtt, company);
+ if (rtt == RTT_ROAD) SetTownIndex(tile, p2);
}
- if (rtt != ROAD_TILE_CROSSING) SetRoadBits(tile, existing | pieces, rt);
+ if (rttype != ROAD_TILE_CROSSING) SetRoadBits(tile, existing | pieces, rtt);
break;
}
case MP_TUNNELBRIDGE: {
TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
- SetRoadTypes(other_end, GetRoadTypes(other_end) | RoadTypeToRoadTypes(rt));
- SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt));
- SetRoadOwner(other_end, rt, company);
- SetRoadOwner(tile, rt, company);
+ SetRoadType(other_end, rtt, rt);
+ SetRoadType(tile, rtt, rt);
+ SetRoadOwner(other_end, rtt, company);
+ SetRoadOwner(tile, rtt, company);
/* Mark tiles dirty that have been repaved */
if (IsBridge(tile)) {
@@ -770,26 +922,23 @@ do_clear:;
break;
}
- case MP_STATION:
+ case MP_STATION: {
assert(IsDriveThroughStopTile(tile));
- SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt));
- SetRoadOwner(tile, rt, company);
+ SetRoadType(tile, rtt, rt);
+ SetRoadOwner(tile, rtt, company);
break;
+ }
default:
- MakeRoadNormal(tile, pieces, RoadTypeToRoadTypes(rt), p2, company, company);
+ MakeRoadNormal(tile, pieces, (rtt == RTT_ROAD) ? rt : INVALID_ROADTYPE, (rtt == RTT_TRAM) ? rt : INVALID_ROADTYPE, p2, company, company);
break;
}
/* Update company infrastructure count. */
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- if (IsTileType(tile, MP_TUNNELBRIDGE)) num_pieces *= TUNNELBRIDGE_TRACKBIT_FACTOR;
- c->infrastructure.road[rt] += num_pieces;
- DirtyCompanyInfrastructureWindows(c->index);
- }
+ if (IsTileType(tile, MP_TUNNELBRIDGE)) num_pieces *= TUNNELBRIDGE_TRACKBIT_FACTOR;
+ UpdateCompanyRoadInfrastructure(rt, GetRoadOwner(tile, rtt), num_pieces);
- if (rt != ROADTYPE_TRAM && IsNormalRoadTile(tile)) {
+ if (rtt == RTT_ROAD && IsNormalRoadTile(tile)) {
existing |= pieces;
SetDisallowedRoadDirections(tile, IsStraightRoad(existing) ?
GetDisallowedRoadDirections(tile) ^ toggle_drd : DRD_NONE);
@@ -810,8 +959,14 @@ do_clear:;
static bool CanConnectToRoad(TileIndex tile, RoadType rt, DiagDirection dir)
{
tile += TileOffsByDiagDir(dir);
- if (!IsValidTile(tile)) return false;
- RoadBits bits = GetAnyRoadBits(tile, rt, false);
+ if (!IsValidTile(tile) || !MayHaveRoad(tile)) return false;
+
+ RoadTramType rtt = GetRoadTramType(rt);
+ RoadType existing = GetRoadType(tile, rtt);
+ if (existing == INVALID_ROADTYPE) return false;
+ if (!HasPowerOnRoad(existing, rt) && !HasPowerOnRoad(rt, existing)) return false;
+
+ RoadBits bits = GetAnyRoadBits(tile, rtt, false);
return (bits & DiagDirToRoadBits(ReverseDiagDir(dir))) != 0;
}
@@ -824,9 +979,9 @@ static bool CanConnectToRoad(TileIndex tile, RoadType rt, DiagDirection dir)
* - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1). Only used if bit 6 is set or if we are building a single tile
* - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2). Only used if bit 6 is set or if we are building a single tile
* - p2 = (bit 2) - direction: 0 = along x-axis, 1 = along y-axis (p2 & 4)
- * - p2 = (bit 3 + 4) - road type
- * - p2 = (bit 5) - set road direction
- * - p2 = (bit 6) - defines two different behaviors for this command:
+ * - p2 = (bit 3..8) - road type
+ * - p2 = (bit 10) - set road direction
+ * - p2 = (bit 11) - defines two different behaviors for this command:
* - 0 = Build up to an obstacle. Do not build the first and last roadbits unless they can be connected to something, or if we are building a single tile
* - 1 = Fail if an obstacle is found. Always take into account bit 0 and 1. This behavior is used for scripts
* @param text unused
@@ -837,10 +992,10 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
DisallowedRoadDirections drd = DRD_NORTHBOUND;
if (p1 >= MapSize()) return CMD_ERROR;
-
TileIndex end_tile = p1;
- RoadType rt = Extract<RoadType, 3, 2>(p2);
- if (!IsValidRoadType(rt) || !ValParamRoadType(rt)) return CMD_ERROR;
+
+ RoadType rt = Extract<RoadType, 3, 6>(p2);
+ if (!ValParamRoadType(rt)) return CMD_ERROR;
Axis axis = Extract<Axis, 2, 1>(p2);
/* Only drag in X or Y direction dictated by the direction variable */
@@ -861,7 +1016,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
* when you just 'click' on one tile to build them. */
if ((axis == AXIS_Y) == (start_tile == end_tile && HasBit(p2, 0) == HasBit(p2, 1))) drd ^= DRD_BOTH;
/* No disallowed direction bits have to be toggled */
- if (!HasBit(p2, 5)) drd = DRD_NONE;
+ if (!HasBit(p2, 10)) drd = DRD_NONE;
CommandCost cost(EXPENSES_CONSTRUCTION);
CommandCost last_error = CMD_ERROR;
@@ -869,7 +1024,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
bool had_bridge = false;
bool had_tunnel = false;
bool had_success = false;
- bool is_ai = HasBit(p2, 6);
+ bool is_ai = HasBit(p2, 11);
/* Start tile is the first tile clicked by the user. */
for (;;) {
@@ -889,7 +1044,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
if (tile == start_tile && HasBit(p2, 0)) bits &= DiagDirToRoadBits(dir);
}
- CommandCost ret = DoCommand(tile, drd << 6 | rt << 4 | bits, 0, flags, CMD_BUILD_ROAD);
+ CommandCost ret = DoCommand(tile, drd << 11 | rt << 4 | bits, 0, flags, CMD_BUILD_ROAD);
if (ret.Failed()) {
last_error = ret;
if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT) {
@@ -933,7 +1088,7 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p
* - p2 = (bit 0) - start tile starts in the 2nd half of tile (p2 & 1)
* - p2 = (bit 1) - end tile starts in the 2nd half of tile (p2 & 2)
* - p2 = (bit 2) - direction: 0 = along x-axis, 1 = along y-axis (p2 & 4)
- * - p2 = (bit 3 + 4) - road type
+ * - p2 = (bit 3 - 8) - road type
* @param text unused
* @return the cost of this operation or an error
*/
@@ -944,8 +1099,8 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32
if (p1 >= MapSize()) return CMD_ERROR;
TileIndex end_tile = p1;
- RoadType rt = Extract<RoadType, 3, 2>(p2);
- if (!IsValidRoadType(rt)) return CMD_ERROR;
+ RoadType rt = Extract<RoadType, 3, 6>(p2);
+ if (!ValParamRoadType(rt)) return CMD_ERROR;
Axis axis = Extract<Axis, 2, 1>(p2);
/* Only drag in X or Y direction dictated by the direction variable */
@@ -973,7 +1128,8 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32
/* try to remove the halves. */
if (bits != 0) {
- CommandCost ret = RemoveRoad(tile, flags & ~DC_EXEC, bits, rt, true);
+ RoadTramType rtt = GetRoadTramType(rt);
+ CommandCost ret = RemoveRoad(tile, flags & ~DC_EXEC, bits, rtt, true);
if (ret.Succeeded()) {
if (flags & DC_EXEC) {
money -= ret.GetCost();
@@ -981,7 +1137,7 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32
_additional_cash_required = DoCommand(start_tile, end_tile, p2, flags & ~DC_EXEC, CMD_REMOVE_LONG_ROAD).GetCost();
return cost;
}
- RemoveRoad(tile, flags, bits, rt, true, false);
+ RemoveRoad(tile, flags, bits, rtt, true, false);
}
cost.AddCost(ret);
had_success = true;
@@ -1004,7 +1160,7 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32
* @param tile tile where to build the depot
* @param flags operation to perform
* @param p1 bit 0..1 entrance direction (DiagDirection)
- * bit 2..3 road type
+ * bit 2..7 road type
* @param p2 unused
* @param text unused
* @return the cost of this operation or an error
@@ -1015,9 +1171,9 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32
CommandCost CmdBuildRoadDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
DiagDirection dir = Extract<DiagDirection, 0, 2>(p1);
- RoadType rt = Extract<RoadType, 2, 2>(p1);
- if (!IsValidRoadType(rt) || !ValParamRoadType(rt)) return CMD_ERROR;
+ RoadType rt = Extract<RoadType, 2, 6>(p1);
+ if (!ValParamRoadType(rt)) return CMD_ERROR;
CommandCost cost(EXPENSES_CONSTRUCTION);
@@ -1041,8 +1197,7 @@ CommandCost CmdBuildRoadDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
dep->build_date = _date;
/* A road depot has two road bits. */
- Company::Get(_current_company)->infrastructure.road[rt] += 2;
- DirtyCompanyInfrastructureWindows(_current_company);
+ UpdateCompanyRoadInfrastructure(rt, _current_company, 2);
MakeRoadDepot(tile, _current_company, dep->index, dir, rt);
MarkTileDirtyByTile(tile);
@@ -1066,7 +1221,9 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags)
Company *c = Company::GetIfValid(GetTileOwner(tile));
if (c != nullptr) {
/* A road depot has two road bits. */
- c->infrastructure.road[FIND_FIRST_BIT(GetRoadTypes(tile))] -= 2;
+ RoadType rt = GetRoadTypeRoad(tile);
+ if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile);
+ c->infrastructure.road[rt] -= 2;
DirtyCompanyInfrastructureWindows(c->index);
}
@@ -1084,11 +1241,12 @@ static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags)
RoadBits b = GetAllRoadBits(tile);
/* Clear the road if only one piece is on the tile OR we are not using the DC_AUTO flag */
- if ((HasExactlyOneBit(b) && GetRoadBits(tile, ROADTYPE_TRAM) == ROAD_NONE) || !(flags & DC_AUTO)) {
+ if ((HasExactlyOneBit(b) && GetRoadBits(tile, RTT_TRAM) == ROAD_NONE) || !(flags & DC_AUTO)) {
CommandCost ret(EXPENSES_CONSTRUCTION);
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- CommandCost tmp_ret = RemoveRoad(tile, flags, GetRoadBits(tile, rt), rt, true);
+ FOR_ALL_ROADTRAMTYPES(rtt) {
+ if (!MayHaveRoad(tile) || GetRoadType(tile, rtt) == INVALID_ROADTYPE) continue;
+
+ CommandCost tmp_ret = RemoveRoad(tile, flags, GetRoadBits(tile, rtt), rtt, true);
if (tmp_ret.Failed()) return tmp_ret;
ret.AddCost(tmp_ret);
}
@@ -1098,21 +1256,19 @@ static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags)
}
case ROAD_TILE_CROSSING: {
- RoadTypes rts = GetRoadTypes(tile);
CommandCost ret(EXPENSES_CONSTRUCTION);
if (flags & DC_AUTO) return_cmd_error(STR_ERROR_MUST_REMOVE_ROAD_FIRST);
/* Must iterate over the roadtypes in a reverse manner because
* tram tracks must be removed before the road bits. */
- RoadType rt = ROADTYPE_TRAM;
- do {
- if (HasBit(rts, rt)) {
- CommandCost tmp_ret = RemoveRoad(tile, flags, GetCrossingRoadBits(tile), rt, false);
- if (tmp_ret.Failed()) return tmp_ret;
- ret.AddCost(tmp_ret);
- }
- } while (rt-- != ROADTYPE_ROAD);
+ for (RoadTramType rtt : { RTT_TRAM, RTT_ROAD }) {
+ if (GetRoadType(tile, rtt) == INVALID_ROADTYPE) continue;
+
+ CommandCost tmp_ret = RemoveRoad(tile, flags, GetCrossingRoadBits(tile), rtt, false);
+ if (tmp_ret.Failed()) return tmp_ret;
+ ret.AddCost(tmp_ret);
+ }
if (flags & DC_EXEC) {
DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
@@ -1175,6 +1331,33 @@ const byte _road_sloped_sprites[14] = {
};
/**
+ * Get the sprite offset within a spritegroup.
+ * @param slope Slope
+ * @param bits Roadbits
+ * @return Offset for the sprite within the spritegroup.
+ */
+static uint GetRoadSpriteOffset(Slope slope, RoadBits bits)
+{
+ if (slope != SLOPE_FLAT) {
+ switch (slope) {
+ case SLOPE_NE: return 11;
+ case SLOPE_SE: return 12;
+ case SLOPE_SW: return 13;
+ case SLOPE_NW: return 14;
+ default: NOT_REACHED();
+ }
+ } else {
+ static const uint offsets[] = {
+ 0, 18, 17, 7,
+ 16, 0, 10, 5,
+ 15, 8, 1, 4,
+ 9, 3, 6, 2
+ };
+ return offsets[bits];
+ }
+}
+
+/**
* Should the road be drawn as a unpaved snow/desert road?
* By default, roads are always drawn as unpaved if they are on desert or
* above the snow line, but NewGRFs can override this for desert.
@@ -1191,15 +1374,13 @@ static bool DrawRoadAsSnowDesert(TileIndex tile, Roadside roadside)
}
/**
- * Draws the catenary for the given tile
- * @param ti information about the tile (slopes, height etc)
- * @param tram the roadbits for the tram
+ * Draws the catenary for the RoadType of the given tile
+ * @param ti information about the tile (slopes, height etc)
+ * @param rt road type to draw catenary for
+ * @param rb the roadbits for the tram
*/
-void DrawRoadCatenary(const TileInfo *ti, RoadBits tram)
+void DrawRoadTypeCatenary(const TileInfo *ti, RoadType rt, RoadBits rb)
{
- /* Do not draw catenary if it is invisible */
- if (IsInvisibilitySet(TO_CATENARY)) return;
-
/* Don't draw the catenary under a low bridge */
if (IsBridgeAbove(ti->tile) && !IsTransparencySet(TO_CATENARY)) {
int height = GetBridgeHeight(GetNorthernBridgeEnd(ti->tile));
@@ -1207,19 +1388,89 @@ void DrawRoadCatenary(const TileInfo *ti, RoadBits tram)
if (height <= GetTileMaxZ(ti->tile) + 1) return;
}
- SpriteID front;
- SpriteID back;
+ if (CountBits(rb) > 2) {
+ /* On junctions we check whether neighbouring tiles also have catenary, and possibly
+ * do not draw catenary towards those neighbours, which do not have catenary. */
+ RoadBits rb_new = ROAD_NONE;
+ for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
+ if (rb & DiagDirToRoadBits(dir)) {
+ TileIndex neighbour = TileAddByDiagDir(ti->tile, dir);
+ if (MayHaveRoad(neighbour)) {
+ RoadType rt_road = GetRoadTypeRoad(neighbour);
+ RoadType rt_tram = GetRoadTypeTram(neighbour);
+
+ if ((rt_road != INVALID_ROADTYPE && HasRoadCatenary(rt_road)) ||
+ (rt_tram != INVALID_ROADTYPE && HasRoadCatenary(rt_tram))) {
+ rb_new |= DiagDirToRoadBits(dir);
+ }
+ }
+ }
+ }
+ if (CountBits(rb_new) >= 2) rb = rb_new;
+ }
- if (ti->tileh != SLOPE_FLAT) {
+ const RoadTypeInfo* rti = GetRoadTypeInfo(rt);
+ SpriteID front = GetCustomRoadSprite(rti, ti->tile, ROTSG_CATENARY_FRONT);
+ SpriteID back = GetCustomRoadSprite(rti, ti->tile, ROTSG_CATENARY_BACK);
+
+ if (front != 0 || back != 0) {
+ if (front != 0) front += GetRoadSpriteOffset(ti->tileh, rb);
+ if (back != 0) back += GetRoadSpriteOffset(ti->tileh, rb);
+ } else if (ti->tileh != SLOPE_FLAT) {
back = SPR_TRAMWAY_BACK_WIRES_SLOPED + _road_sloped_sprites[ti->tileh - 1];
front = SPR_TRAMWAY_FRONT_WIRES_SLOPED + _road_sloped_sprites[ti->tileh - 1];
} else {
- back = SPR_TRAMWAY_BASE + _road_backpole_sprites_1[tram];
- front = SPR_TRAMWAY_BASE + _road_frontwire_sprites_1[tram];
+ back = SPR_TRAMWAY_BASE + _road_backpole_sprites_1[rb];
+ front = SPR_TRAMWAY_BASE + _road_frontwire_sprites_1[rb];
+ }
+
+ /* Catenary uses 1st company colour to help identify owner.
+ * For tiles with OWNER_TOWN or OWNER_NONE, recolour CC to grey as a neutral colour. */
+ Owner owner = GetRoadOwner(ti->tile, GetRoadTramType(rt));
+ PaletteID pal = (owner == OWNER_NONE || owner == OWNER_TOWN ? GENERAL_SPRITE_COLOUR(COLOUR_GREY) : COMPANY_SPRITE_COLOUR(owner));
+ if (back != 0) AddSortableSpriteToDraw(back, pal, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY));
+ if (front != 0) AddSortableSpriteToDraw(front, pal, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY));
+}
+
+/**
+ * Draws the catenary for the given tile
+ * @param ti information about the tile (slopes, height etc)
+ */
+void DrawRoadCatenary(const TileInfo *ti)
+{
+ RoadBits road = ROAD_NONE;
+ RoadBits tram = ROAD_NONE;
+
+ if (IsTileType(ti->tile, MP_ROAD)) {
+ if (IsNormalRoad(ti->tile)) {
+ road = GetRoadBits(ti->tile, RTT_ROAD);
+ tram = GetRoadBits(ti->tile, RTT_TRAM);
+ } else if (IsLevelCrossing(ti->tile)) {
+ tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y);
+ }
+ } else if (IsTileType(ti->tile, MP_STATION)) {
+ if (IsRoadStop(ti->tile)) {
+ if (IsDriveThroughStopTile(ti->tile)) {
+ Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
+ tram = road = (axis == AXIS_X ? ROAD_X : ROAD_Y);
+ } else {
+ tram = road = DiagDirToRoadBits(GetRoadStopDir(ti->tile));
+ }
+ }
+ } else {
+ // No road here, no catenary to draw
+ return;
}
- AddSortableSpriteToDraw(back, PAL_NONE, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY));
- AddSortableSpriteToDraw(front, PAL_NONE, ti->x, ti->y, 16, 16, TILE_HEIGHT + BB_HEIGHT_UNDER_BRIDGE, ti->z, IsTransparencySet(TO_CATENARY));
+ RoadType rt = GetRoadTypeRoad(ti->tile);
+ if (rt != INVALID_ROADTYPE && HasRoadCatenaryDrawn(rt)) {
+ DrawRoadTypeCatenary(ti, rt, road);
+ }
+
+ rt = GetRoadTypeTram(ti->tile);
+ if (rt != INVALID_ROADTYPE && HasRoadCatenaryDrawn(rt)) {
+ DrawRoadTypeCatenary(ti, rt, tram);
+ }
}
/**
@@ -1240,56 +1491,124 @@ static void DrawRoadDetail(SpriteID img, const TileInfo *ti, int dx, int dy, int
}
/**
- * Draw ground sprite and road pieces
+ * Draw road underlay and overlay sprites.
* @param ti TileInfo
+ * @param road_rti Road road type information
+ * @param tram_rti Tram road type information
+ * @param road_offset Road sprite offset (based on road bits)
+ * @param tram_offset Tram sprite offset (based on road bits)
*/
-static void DrawRoadBits(TileInfo *ti)
+void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rti, uint road_offset, uint tram_offset)
{
- RoadBits road = GetRoadBits(ti->tile, ROADTYPE_ROAD);
- RoadBits tram = GetRoadBits(ti->tile, ROADTYPE_TRAM);
-
- SpriteID image = 0;
- PaletteID pal = PAL_NONE;
+ /* Road underlay takes precedence over tram */
+ if (road_rti != nullptr) {
+ if (road_rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_GROUND);
+ DrawGroundSprite(ground + road_offset, pal);
+ }
+ } else {
+ if (tram_rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_GROUND);
+ DrawGroundSprite(ground + tram_offset, pal);
+ } else {
+ DrawGroundSprite(SPR_TRAMWAY_TRAM + tram_offset, pal);
+ }
+ }
- if (ti->tileh != SLOPE_FLAT) {
- DrawFoundation(ti, GetRoadFoundation(ti->tileh, road | tram));
+ /* Draw road overlay */
+ if (road_rti != nullptr) {
+ if (road_rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_OVERLAY);
+ if (ground != 0) DrawGroundSprite(ground + road_offset, pal);
+ }
+ }
- /* DrawFoundation() modifies ti.
- * Default sloped sprites.. */
- if (ti->tileh != SLOPE_FLAT) image = _road_sloped_sprites[ti->tileh - 1] + SPR_ROAD_SLOPE_START;
+ /* Draw tram overlay */
+ if (tram_rti != nullptr) {
+ if (tram_rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_OVERLAY);
+ if (ground != 0) DrawGroundSprite(ground + tram_offset, pal);
+ } else if (road_rti != nullptr) {
+ DrawGroundSprite(SPR_TRAMWAY_OVERLAY + tram_offset, pal);
+ }
}
+}
- if (image == 0) image = _road_tile_sprites_1[road != ROAD_NONE ? road : tram];
+/**
+ * Get ground sprite to draw for a road tile.
+ * @param ti TileInof
+ * @param roadside Road side type
+ * @param rti Road type info
+ * @param offset Road sprite offset
+ * @param[out] pal Palette to draw.
+ */
+static SpriteID GetRoadGroundSprite(const TileInfo *ti, Roadside roadside, const RoadTypeInfo *rti, uint offset, PaletteID *pal)
+{
+ /* Draw bare ground sprite if no road or road uses overlay system. */
+ if (rti == nullptr || rti->UsesOverlay()) {
+ if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
+ return SPR_FLAT_SNOW_DESERT_TILE + SlopeToSpriteOffset(ti->tileh);
+ }
- Roadside roadside = GetRoadside(ti->tile);
+ switch (roadside) {
+ case ROADSIDE_BARREN: *pal = PALETTE_TO_BARE_LAND;
+ return SPR_FLAT_GRASS_TILE + SlopeToSpriteOffset(ti->tileh);
+ case ROADSIDE_GRASS:
+ case ROADSIDE_GRASS_ROAD_WORKS: return SPR_FLAT_GRASS_TILE + SlopeToSpriteOffset(ti->tileh);
+ default: break; // Paved
+ }
+ }
+ /* Draw original road base sprite */
+ SpriteID image = SPR_ROAD_Y + offset;
if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
image += 19;
} else {
switch (roadside) {
- case ROADSIDE_BARREN: pal = PALETTE_TO_BARE_LAND; break;
+ case ROADSIDE_BARREN: *pal = PALETTE_TO_BARE_LAND; break;
case ROADSIDE_GRASS: break;
case ROADSIDE_GRASS_ROAD_WORKS: break;
default: image -= 19; break; // Paved
}
}
- DrawGroundSprite(image, pal);
+ return image;
+}
- /* For tram we overlay the road graphics with either tram tracks only
- * (when there is actual road beneath the trams) or with tram tracks
- * and some dirts which hides the road graphics */
- if (tram != ROAD_NONE) {
- if (ti->tileh != SLOPE_FLAT) {
- image = _road_sloped_sprites[ti->tileh - 1] + SPR_TRAMWAY_SLOPED_OFFSET;
- } else {
- image = _road_tile_sprites_1[tram] - SPR_ROAD_Y;
- }
- image += (road == ROAD_NONE) ? SPR_TRAMWAY_TRAM : SPR_TRAMWAY_OVERLAY;
- DrawGroundSprite(image, pal);
+/**
+ * Draw ground sprite and road pieces
+ * @param ti TileInfo
+ */
+static void DrawRoadBits(TileInfo *ti)
+{
+ RoadBits road = GetRoadBits(ti->tile, RTT_ROAD);
+ RoadBits tram = GetRoadBits(ti->tile, RTT_TRAM);
+
+ RoadType road_rt = GetRoadTypeRoad(ti->tile);
+ RoadType tram_rt = GetRoadTypeTram(ti->tile);
+ const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
+ const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
+
+ if (ti->tileh != SLOPE_FLAT) {
+ DrawFoundation(ti, GetRoadFoundation(ti->tileh, road | tram));
+ /* DrawFoundation() modifies ti. */
}
- if (road != ROAD_NONE) {
+ /* Determine sprite offsets */
+ uint road_offset = GetRoadSpriteOffset(ti->tileh, road);
+ uint tram_offset = GetRoadSpriteOffset(ti->tileh, tram);
+
+ /* Draw baseset underlay */
+ Roadside roadside = GetRoadside(ti->tile);
+
+ PaletteID pal = PAL_NONE;
+ SpriteID image = GetRoadGroundSprite(ti, roadside, road_rti, road == ROAD_NONE ? tram_offset : road_offset, &pal);
+ DrawGroundSprite(image, pal);
+
+ DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset);
+
+ /* Draw one way */
+ if (road_rti != nullptr) {
DisallowedRoadDirections drd = GetDisallowedRoadDirections(ti->tile);
if (drd != DRD_NONE) {
DrawGroundSpriteAt(SPR_ONEWAY_BASE + drd - 1 + ((road == ROAD_X) ? 0 : 3), PAL_NONE, 8, 8, GetPartialPixelZ(8, 8, ti->tileh));
@@ -1302,7 +1621,8 @@ static void DrawRoadBits(TileInfo *ti)
return;
}
- if (tram != ROAD_NONE) DrawRoadCatenary(ti, tram);
+ /* Draw road, tram catenary */
+ DrawRoadCatenary(ti);
/* Return if full detail is disabled, or we are zoomed fully out. */
if (!HasBit(_display_opt, DO_FULL_DETAIL) || _cur_dpi->zoom > ZOOM_LVL_DETAIL) return;
@@ -1337,41 +1657,38 @@ static void DrawTile_Road(TileInfo *ti)
case ROAD_TILE_CROSSING: {
if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
- PaletteID pal = PAL_NONE;
+ Axis axis = GetCrossingRailAxis(ti->tile);
+
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+ RoadType road_rt = GetRoadTypeRoad(ti->tile);
+ RoadType tram_rt = GetRoadTypeTram(ti->tile);
+ const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
+ const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
+
+ PaletteID pal = PAL_NONE;
+
+ /* Draw base ground */
if (rti->UsesOverlay()) {
- Axis axis = GetCrossingRailAxis(ti->tile);
- SpriteID road = SPR_ROAD_Y + axis;
+ SpriteID image = SPR_ROAD_Y + axis;
Roadside roadside = GetRoadside(ti->tile);
-
if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
- road += 19;
+ image += 19;
} else {
switch (roadside) {
case ROADSIDE_BARREN: pal = PALETTE_TO_BARE_LAND; break;
case ROADSIDE_GRASS: break;
- default: road -= 19; break; // Paved
+ default: image -= 19; break; // Paved
}
}
- DrawGroundSprite(road, pal);
-
- SpriteID rail = GetCustomRailSprite(rti, ti->tile, RTSG_CROSSING) + axis;
- /* Draw tracks, but draw PBS reserved tracks darker. */
- pal = (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasCrossingReservation(ti->tile)) ? PALETTE_CRASH : PAL_NONE;
- DrawGroundSprite(rail, pal);
-
- DrawRailTileSeq(ti, &_crossing_layout, TO_CATENARY, rail, 0, PAL_NONE);
+ DrawGroundSprite(image, pal);
} else {
- SpriteID image = rti->base_sprites.crossing;
-
- if (GetCrossingRoadAxis(ti->tile) == AXIS_X) image++;
+ SpriteID image = rti->base_sprites.crossing + axis;
if (IsCrossingBarred(ti->tile)) image += 2;
Roadside roadside = GetRoadside(ti->tile);
-
if (DrawRoadAsSnowDesert(ti->tile, roadside)) {
image += 8;
} else {
@@ -1383,18 +1700,31 @@ static void DrawTile_Road(TileInfo *ti)
}
DrawGroundSprite(image, pal);
-
- /* PBS debugging, draw reserved tracks darker */
- if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasCrossingReservation(ti->tile)) {
- DrawGroundSprite(GetCrossingRoadAxis(ti->tile) == AXIS_Y ? GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_x : GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_y, PALETTE_CRASH);
- }
}
- if (HasTileRoadType(ti->tile, ROADTYPE_TRAM)) {
- DrawGroundSprite(SPR_TRAMWAY_OVERLAY + (GetCrossingRoadAxis(ti->tile) ^ 1), pal);
- DrawRoadCatenary(ti, GetCrossingRoadBits(ti->tile));
+ DrawRoadOverlays(ti, pal, road_rti, tram_rti, axis, axis);
+
+ /* Draw rail/PBS overlay */
+ bool draw_pbs = _game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasCrossingReservation(ti->tile);
+ if (rti->UsesOverlay()) {
+ PaletteID pal = draw_pbs ? PALETTE_CRASH : PAL_NONE;
+ SpriteID rail = GetCustomRailSprite(rti, ti->tile, RTSG_CROSSING) + axis;
+ DrawGroundSprite(rail, pal);
+
+ DrawRailTileSeq(ti, &_crossing_layout, TO_CATENARY, rail, 0, PAL_NONE);
+ } else if (draw_pbs || tram_rti != nullptr || road_rti->UsesOverlay()) {
+ /* Add another rail overlay, unless there is only the base road sprite. */
+ PaletteID pal = draw_pbs ? PALETTE_CRASH : PAL_NONE;
+ SpriteID rail = GetCrossingRoadAxis(ti->tile) == AXIS_Y ? GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_x : GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_y;
+ DrawGroundSprite(rail, pal);
}
+
+ /* Draw road, tram catenary */
+ DrawRoadCatenary(ti);
+
+ /* Draw rail catenary */
if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
+
break;
}
@@ -1404,15 +1734,42 @@ static void DrawTile_Road(TileInfo *ti)
PaletteID palette = COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile));
- const DrawTileSprites *dts;
- if (HasTileRoadType(ti->tile, ROADTYPE_TRAM)) {
- dts = &_tram_depot[GetRoadDepotDirection(ti->tile)];
+ RoadType road_rt = GetRoadTypeRoad(ti->tile);
+ RoadType tram_rt = GetRoadTypeTram(ti->tile);
+ const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt == INVALID_ROADTYPE ? tram_rt : road_rt);
+
+ int relocation = GetCustomRoadSprite(rti, ti->tile, ROTSG_DEPOT);
+ bool default_gfx = relocation == 0;
+ if (default_gfx) {
+ if (HasBit(rti->flags, ROTF_CATENARY)) {
+ if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && road_rt == INVALID_ROADTYPE && !rti->UsesOverlay()) {
+ /* Sprites with track only work for default tram */
+ relocation = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_ROAD_DEPOT;
+ default_gfx = false;
+ } else {
+ /* Sprites without track are always better, if provided */
+ relocation = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_ROAD_DEPOT;
+ }
+ }
} else {
- dts = &_road_depot[GetRoadDepotDirection(ti->tile)];
+ relocation -= SPR_ROAD_DEPOT;
}
+ DiagDirection dir = GetRoadDepotDirection(ti->tile);
+ const DrawTileSprites *dts = &_road_depot[dir];
DrawGroundSprite(dts->ground.sprite, PAL_NONE);
- DrawOrigTileSeq(ti, dts, TO_BUILDINGS, palette);
+
+ if (default_gfx) {
+ uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir));
+ if (rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(rti, ti->tile, ROTSG_OVERLAY);
+ if (ground != 0) DrawGroundSprite(ground + offset, PAL_NONE);
+ } else if (road_rt == INVALID_ROADTYPE) {
+ DrawGroundSprite(SPR_TRAMWAY_OVERLAY + offset, PAL_NONE);
+ }
+ }
+
+ DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, palette);
break;
}
}
@@ -1429,10 +1786,39 @@ static void DrawTile_Road(TileInfo *ti)
void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt)
{
PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company);
- const DrawTileSprites *dts = (rt == ROADTYPE_TRAM) ? &_tram_depot[dir] : &_road_depot[dir];
+ const RoadTypeInfo* rti = GetRoadTypeInfo(rt);
+ int relocation = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_DEPOT);
+ bool default_gfx = relocation == 0;
+ if (default_gfx) {
+ if (HasBit(rti->flags, ROTF_CATENARY)) {
+ if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && RoadTypeIsTram(rt) && !rti->UsesOverlay()) {
+ /* Sprites with track only work for default tram */
+ relocation = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_ROAD_DEPOT;
+ default_gfx = false;
+ } else {
+ /* Sprites without track are always better, if provided */
+ relocation = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_ROAD_DEPOT;
+ }
+ }
+ } else {
+ relocation -= SPR_ROAD_DEPOT;
+ }
+
+ const DrawTileSprites *dts = &_road_depot[dir];
DrawSprite(dts->ground.sprite, PAL_NONE, x, y);
- DrawOrigTileSeqInGUI(x, y, dts, palette);
+
+ if (default_gfx) {
+ uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir));
+ if (rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY);
+ if (ground != 0) DrawSprite(ground + offset, PAL_NONE, x, y);
+ } else if (RoadTypeIsTram(rt)) {
+ DrawSprite(SPR_TRAMWAY_OVERLAY + offset, PAL_NONE, x, y);
+ }
+ }
+
+ DrawRailTileSeqInGUI(x, y, dts, relocation, 0, palette);
}
/**
@@ -1570,11 +1956,19 @@ static void TileLoop_Road(TileIndex tile)
if (_settings_game.economy.mod_road_rebuild) {
/* Generate a nicer town surface */
- const RoadBits old_rb = GetAnyRoadBits(tile, ROADTYPE_ROAD);
+ const RoadBits old_rb = GetAnyRoadBits(tile, RTT_ROAD);
const RoadBits new_rb = CleanUpRoadBits(tile, old_rb);
if (old_rb != new_rb) {
- RemoveRoad(tile, DC_EXEC | DC_AUTO | DC_NO_WATER, (old_rb ^ new_rb), ROADTYPE_ROAD, true);
+ RemoveRoad(tile, DC_EXEC | DC_AUTO | DC_NO_WATER, (old_rb ^ new_rb), RTT_ROAD, true);
+ }
+ }
+
+ /* Possibly change road type */
+ if (GetRoadOwner(tile, RTT_ROAD) == OWNER_TOWN) {
+ RoadType rt = GetTownRoadType(t);
+ if (rt != GetRoadTypeRoad(tile)) {
+ SetRoadType(tile, RTT_ROAD, rt);
}
}
@@ -1619,18 +2013,18 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u
if (IsLevelCrossing(tile)) trackdirbits = TrackBitsToTrackdirBits(GetCrossingRailBits(tile));
break;
- case TRANSPORT_ROAD:
- if ((GetRoadTypes(tile) & sub_mode) == 0) break;
+ case TRANSPORT_ROAD: {
+ RoadTramType rtt = (RoadTramType)sub_mode;
+ if (!HasTileRoadType(tile, rtt)) break;
switch (GetRoadTileType(tile)) {
case ROAD_TILE_NORMAL: {
const uint drd_to_multiplier[DRD_END] = { 0x101, 0x100, 0x1, 0x0 };
- RoadType rt = (RoadType)FindFirstBit(sub_mode);
- RoadBits bits = GetRoadBits(tile, rt);
+ RoadBits bits = GetRoadBits(tile, rtt);
/* no roadbit at this side of tile, return 0 */
if (side != INVALID_DIAGDIR && (DiagDirToRoadBits(side) & bits) == 0) break;
- uint multiplier = drd_to_multiplier[rt == ROADTYPE_TRAM ? DRD_NONE : GetDisallowedRoadDirections(tile)];
+ uint multiplier = drd_to_multiplier[(rtt == RTT_TRAM) ? DRD_NONE : GetDisallowedRoadDirections(tile)];
if (!HasRoadWorks(tile)) trackdirbits = (TrackdirBits)(_road_trackbits[bits] * multiplier);
break;
}
@@ -1656,6 +2050,7 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u
}
}
break;
+ }
default: break;
}
@@ -1679,13 +2074,25 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td)
Owner road_owner = INVALID_OWNER;
Owner tram_owner = INVALID_OWNER;
+ RoadType road_rt = GetRoadTypeRoad(tile);
+ RoadType tram_rt = GetRoadTypeTram(tile);
+ if (road_rt != INVALID_ROADTYPE) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
+ td->roadtype = rti->strings.name;
+ td->road_speed = rti->max_speed / 2;
+ road_owner = GetRoadOwner(tile, RTT_ROAD);
+ }
+ if (tram_rt != INVALID_ROADTYPE) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
+ td->tramtype = rti->strings.name;
+ td->tram_speed = rti->max_speed / 2;
+ tram_owner = GetRoadOwner(tile, RTT_TRAM);
+ }
+
switch (GetRoadTileType(tile)) {
case ROAD_TILE_CROSSING: {
td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_RAIL_LEVEL_CROSSING;
- RoadTypes rts = GetRoadTypes(tile);
rail_owner = GetTileOwner(tile);
- if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
- if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
td->railtype = rti->strings.name;
@@ -1696,15 +2103,11 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td)
case ROAD_TILE_DEPOT:
td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT;
- road_owner = GetTileOwner(tile); // Tile has only one owner, roadtype does not matter
td->build_date = Depot::GetByTile(tile)->build_date;
break;
default: {
- RoadTypes rts = GetRoadTypes(tile);
- td->str = (HasBit(rts, ROADTYPE_ROAD) ? _road_tile_strings[GetRoadside(tile)] : STR_LAI_ROAD_DESCRIPTION_TRAMWAY);
- if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
- if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
+ td->str = (road_rt != INVALID_ROADTYPE ? _road_tile_strings[GetRoadside(tile)] : STR_LAI_ROAD_DESCRIPTION_TRAMWAY);
break;
}
}
@@ -1774,14 +2177,15 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne
DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
} else {
/* A road depot has two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
- RoadType rt = (RoadType)FIND_FIRST_BIT(GetRoadTypes(tile));
+ RoadType rt = GetRoadTypeRoad(tile);
+ if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile);
Company::Get(old_owner)->infrastructure.road[rt] -= 2;
Company::Get(new_owner)->infrastructure.road[rt] += 2;
SetTileOwner(tile, new_owner);
- for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
- if (GetRoadOwner(tile, rt) == old_owner) {
- SetRoadOwner(tile, rt, new_owner);
+ FOR_ALL_ROADTRAMTYPES(rtt) {
+ if (GetRoadOwner(tile, rtt) == old_owner) {
+ SetRoadOwner(tile, rtt, new_owner);
}
}
}
@@ -1789,17 +2193,18 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne
return;
}
- for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
+ FOR_ALL_ROADTRAMTYPES(rtt) {
/* Update all roadtypes, no matter if they are present */
- if (GetRoadOwner(tile, rt) == old_owner) {
- if (HasTileRoadType(tile, rt)) {
+ if (GetRoadOwner(tile, rtt) == old_owner) {
+ RoadType rt = GetRoadType(tile, rtt);
+ if (rt != INVALID_ROADTYPE) {
/* A level crossing has two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
- uint num_bits = IsLevelCrossing(tile) ? 2 : CountBits(GetRoadBits(tile, rt));
+ uint num_bits = IsLevelCrossing(tile) ? 2 : CountBits(GetRoadBits(tile, rtt));
Company::Get(old_owner)->infrastructure.road[rt] -= num_bits;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_bits;
}
- SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
+ SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
}
}
@@ -1858,6 +2263,228 @@ static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
}
+/** Update power of road vehicle under which is the roadtype being converted */
+static Vehicle *UpdateRoadVehPowerProc(Vehicle *v, void *data)
+{
+ if (v->type != VEH_ROAD) return nullptr;
+
+ RoadVehicleList *affected_rvs = static_cast<RoadVehicleList*>(data);
+ include(*affected_rvs, RoadVehicle::From(v)->First());
+
+ return nullptr;
+}
+
+/**
+ * Checks the tile and returns whether the current player is allowed to convert the roadtype to another roadtype
+ * @param owner the tile owner.
+ * @param rtt Road/tram type.
+ * @return whether the road is convertible
+ */
+static bool CanConvertRoadType(Owner owner, RoadTramType rtt)
+{
+ return (owner == OWNER_NONE || (owner == OWNER_TOWN && rtt == RTT_ROAD));
+}
+
+/**
+ * Convert the ownership of the RoadType of the tile if applyable
+ * @param tile the tile of which convert ownership
+ * @param num_pieces the count of the roadbits to assign to the new owner
+ * @param owner the current owner of the RoadType
+ * @param from_type the old road type
+ * @param to_type the new road type
+ */
+static void ConvertRoadTypeOwner(TileIndex tile, uint num_pieces, Owner owner, RoadType from_type, RoadType to_type)
+{
+ // Scenario editor, maybe? Don't touch the owners when converting roadtypes...
+ if (_current_company >= MAX_COMPANIES) return;
+
+ // We can't get a company from invalid owners but we can get ownership of roads without an owner
+ if (owner >= MAX_COMPANIES && owner != OWNER_NONE) return;
+
+ Company *c;
+
+ switch (owner) {
+ case OWNER_NONE:
+ SetRoadOwner(tile, GetRoadTramType(to_type), (Owner)_current_company);
+ UpdateCompanyRoadInfrastructure(to_type, _current_company, num_pieces);
+ break;
+
+ default:
+ c = Company::Get(owner);
+ c->infrastructure.road[from_type] -= num_pieces;
+ c->infrastructure.road[to_type] += num_pieces;
+ DirtyCompanyInfrastructureWindows(c->index);
+ break;
+ }
+}
+
+/**
+ * Convert one road subtype to another.
+ * Not meant to convert from road to tram.
+ *
+ * @param tile end tile of road conversion drag
+ * @param flags operation to perform
+ * @param p1 start tile of drag
+ * @param p2 various bitstuffed elements:
+ * - p2 = (bit 0..5) new roadtype to convert to.
+ * @param text unused
+ * @return the cost of this operation or an error
+ */
+CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
+{
+ RoadType to_type = Extract<RoadType, 0, 6>(p2);
+
+ TileIndex area_start = p1;
+ TileIndex area_end = tile;
+
+ if (!ValParamRoadType(to_type)) return CMD_ERROR;
+ if (area_start >= MapSize()) return CMD_ERROR;
+
+ RoadVehicleList affected_rvs;
+ RoadTramType rtt = GetRoadTramType(to_type);
+
+ CommandCost cost(EXPENSES_CONSTRUCTION);
+ CommandCost error = CommandCost((rtt == RTT_TRAM) ? STR_ERROR_NO_SUITABLE_TRAMWAY : STR_ERROR_NO_SUITABLE_ROAD); // by default, there is no road to convert.
+
+ TileIterator *iter = new OrthogonalTileIterator(area_start, area_end);
+ for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
+ /* Is road present on tile? */
+ if (!MayHaveRoad(tile)) continue;
+
+ /* Converting to the same subtype? */
+ RoadType from_type = GetRoadType(tile, rtt);
+ if (from_type == INVALID_ROADTYPE || from_type == to_type) continue;
+
+ /* Check if there is any infrastructure on tile */
+ TileType tt = GetTileType(tile);
+ switch (tt) {
+ case MP_STATION:
+ if (!IsRoadStop(tile)) continue;
+ break;
+ case MP_ROAD:
+ if (IsLevelCrossing(tile) && RoadNoLevelCrossing(to_type)) {
+ error.MakeError(STR_ERROR_CROSSING_DISALLOWED_ROAD);
+ continue;
+ }
+ break;
+ case MP_TUNNELBRIDGE:
+ if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) continue;
+ break;
+ default: continue;
+ }
+
+ /* Trying to convert other's road */
+ Owner owner = GetRoadOwner(tile, rtt);
+ if (!CanConvertRoadType(owner, rtt)) {
+ CommandCost ret = CheckOwnership(owner, tile);
+ if (ret.Failed()) {
+ error = ret;
+ continue;
+ }
+ }
+
+ /* Vehicle on the tile when not converting normal <-> powered
+ * Tunnels and bridges have special check later */
+ if (tt != MP_TUNNELBRIDGE) {
+ if (!HasPowerOnRoad(from_type, to_type)) {
+ CommandCost ret = EnsureNoVehicleOnGround(tile);
+ if (ret.Failed()) {
+ error = ret;
+ continue;
+ }
+
+ if (rtt == RTT_ROAD && owner == OWNER_TOWN) {
+ error.MakeError(STR_ERROR_INCOMPATIBLE_ROAD);
+ continue;
+ }
+ }
+
+ uint num_pieces = CountBits(GetAnyRoadBits(tile, rtt));;
+ cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type));
+
+ if (flags & DC_EXEC) { // we can safely convert, too
+ /* Update the company infrastructure counters. */
+ if (!IsRoadStopTile(tile) && CanConvertRoadType(owner, rtt) && owner != OWNER_TOWN) {
+ ConvertRoadTypeOwner(tile, num_pieces, owner, from_type, to_type);
+ }
+
+ /* Perform the conversion */
+ SetRoadType(tile, rtt, to_type);
+ MarkTileDirtyByTile(tile);
+
+ /* update power of train on this tile */
+ FindVehicleOnPos(tile, &affected_rvs, &UpdateRoadVehPowerProc);
+
+ if (IsRoadDepotTile(tile)) {
+ /* Update build vehicle window related to this depot */
+ InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
+ InvalidateWindowData(WC_BUILD_VEHICLE, tile);
+ }
+ }
+ } else {
+ TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
+
+ /* If both ends of tunnel/bridge are in the range, do not try to convert twice -
+ * it would cause assert because of different test and exec runs */
+ if (endtile < tile) {
+ if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
+ }
+
+ /* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
+ if (!HasPowerOnRoad(from_type, to_type)) {
+ CommandCost ret = TunnelBridgeIsFree(tile, endtile);
+ if (ret.Failed()) {
+ error = ret;
+ continue;
+ }
+
+ if (rtt == RTT_ROAD && owner == OWNER_TOWN) {
+ error.MakeError(STR_ERROR_INCOMPATIBLE_ROAD);
+ continue;
+ }
+ }
+
+ /* There are 2 pieces on *every* tile of the bridge or tunnel */
+ uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * 2;
+ cost.AddCost(num_pieces * RoadConvertCost(from_type, to_type));
+
+ if (flags & DC_EXEC) {
+ /* Update the company infrastructure counters. */
+ if (CanConvertRoadType(owner, rtt) && owner != OWNER_TOWN) {
+ ConvertRoadTypeOwner(tile, num_pieces, owner, from_type, to_type);
+ ConvertRoadTypeOwner(endtile, num_pieces, owner, from_type, to_type);
+ SetTunnelBridgeOwner(tile, endtile, _current_company);
+ }
+
+ /* Perform the conversion */
+ SetRoadType(tile, rtt, to_type);
+ SetRoadType(endtile, rtt, to_type);
+
+ FindVehicleOnPos(tile, &affected_rvs, &UpdateRoadVehPowerProc);
+ FindVehicleOnPos(endtile, &affected_rvs, &UpdateRoadVehPowerProc);
+
+ if (IsBridge(tile)) {
+ MarkBridgeDirty(tile);
+ } else {
+ MarkTileDirtyByTile(tile);
+ MarkTileDirtyByTile(endtile);
+ }
+ }
+ }
+ }
+
+ if (flags & DC_EXEC) {
+ /* Roadtype changed, update roadvehicles as when entering different track */
+ for (RoadVehicle *v : affected_rvs) {
+ v->CargoChanged();
+ }
+ }
+
+ delete iter;
+ return (cost.GetCost() == 0) ? error : cost;
+}
+
+
/** Tile callback functions for road tiles */
extern const TileTypeProcs _tile_type_road_procs = {
DrawTile_Road, // draw_tile_proc
diff --git a/src/road_func.h b/src/road_func.h
index 06be7c4aa..ebb68de69 100644
--- a/src/road_func.h
+++ b/src/road_func.h
@@ -13,29 +13,9 @@
#define ROAD_FUNC_H
#include "core/bitmath_func.hpp"
-#include "road_type.h"
+#include "road.h"
#include "economy_func.h"
-
-/**
- * Iterate through each set RoadType in a RoadTypes value.
- * For more informations see FOR_EACH_SET_BIT_EX.
- *
- * @param var Loop index variable that stores fallowing set road type. Must be of type RoadType.
- * @param road_types The value to iterate through (any expression).
- *
- * @see FOR_EACH_SET_BIT_EX
- */
-#define FOR_EACH_SET_ROADTYPE(var, road_types) FOR_EACH_SET_BIT_EX(RoadType, var, RoadTypes, road_types)
-
-/**
- * Whether the given roadtype is valid.
- * @param rt the roadtype to check for validness
- * @return true if and only if valid
- */
-static inline bool IsValidRoadType(RoadType rt)
-{
- return rt == ROADTYPE_ROAD || rt == ROADTYPE_TRAM;
-}
+#include "transparency.h"
/**
* Whether the given roadtype is valid.
@@ -48,32 +28,6 @@ static inline bool IsValidRoadBits(RoadBits r)
}
/**
- * Maps a RoadType to the corresponding RoadTypes value
- *
- * @param rt the roadtype to get the roadtypes from
- * @return the roadtypes with the given roadtype
- */
-static inline RoadTypes RoadTypeToRoadTypes(RoadType rt)
-{
- assert(IsValidRoadType(rt));
- return (RoadTypes)(1 << rt);
-}
-
-/**
- * Returns the RoadTypes which are not present in the given RoadTypes
- *
- * This function returns the complement of a given RoadTypes.
- *
- * @param r The given RoadTypes
- * @return The complement of the given RoadTypes
- */
-static inline RoadTypes ComplementRoadTypes(RoadTypes r)
-{
- return (RoadTypes)(ROADTYPES_ALL ^ r);
-}
-
-
-/**
* Calculate the complement of a RoadBits value
*
* Simply flips all bits in the RoadBits value to get the complement
@@ -167,18 +121,44 @@ static inline RoadBits AxisToRoadBits(Axis a)
* Calculates the maintenance cost of a number of road bits.
* @param roadtype Road type to get the cost for.
* @param num Number of road bits.
+ * @param total_num Total number of road bits of all road/tram-types.
* @return Total cost.
*/
-static inline Money RoadMaintenanceCost(RoadType roadtype, uint32 num)
+static inline Money RoadMaintenanceCost(RoadType roadtype, uint32 num, uint32 total_num)
+{
+ assert(roadtype < ROADTYPE_END);
+ return (_price[PR_INFRASTRUCTURE_ROAD] * GetRoadTypeInfo(roadtype)->maintenance_multiplier * num * (1 + IntSqrt(total_num))) >> 12;
+}
+
+/**
+ * Test if a road type has catenary
+ * @param roadtype Road type to test
+ */
+static inline bool HasRoadCatenary(RoadType roadtype)
{
- assert(IsValidRoadType(roadtype));
- return (_price[PR_INFRASTRUCTURE_ROAD] * (roadtype == ROADTYPE_TRAM ? 3 : 2) * num * (1 + IntSqrt(num))) >> 9; // 2 bits fraction for the multiplier and 7 bits scaling.
+ assert(roadtype < ROADTYPE_END);
+ return HasBit(GetRoadTypeInfo(roadtype)->flags, ROTF_CATENARY);
}
-bool HasRoadTypesAvail(const CompanyID company, const RoadTypes rts);
-bool ValParamRoadType(const RoadType rt);
-RoadTypes GetCompanyRoadtypes(const CompanyID company);
+/**
+ * Test if we should draw road catenary
+ * @param roadtype Road type to test
+ */
+static inline bool HasRoadCatenaryDrawn(RoadType roadtype)
+{
+ return HasRoadCatenary(roadtype) && !IsInvisibilitySet(TO_CATENARY);
+}
+
+bool HasRoadTypeAvail(CompanyID company, RoadType roadtype);
+bool ValParamRoadType(RoadType roadtype);
+RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces = true);
+RoadTypes GetRoadTypes(bool introduces);
+RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date);
void UpdateLevelCrossing(TileIndex tile, bool sound = true);
+void UpdateCompanyRoadInfrastructure(RoadType rt, Owner o, int count);
+
+struct TileInfo;
+void DrawRoadOverlays(const TileInfo *ti, PaletteID pal, const RoadTypeInfo *road_rti, const RoadTypeInfo *tram_rit, uint road_offset, uint tram_offset);
#endif /* ROAD_FUNC_H */
diff --git a/src/road_gui.cpp b/src/road_gui.cpp
index ca3bd6d08..67132f093 100644
--- a/src/road_gui.cpp
+++ b/src/road_gui.cpp
@@ -29,6 +29,10 @@
#include "hotkeys.h"
#include "road_gui.h"
#include "zoom_func.h"
+#include "engine_base.h"
+#include "strings_func.h"
+#include "core/geometry_func.hpp"
+#include "date_func.h"
#include "widgets/road_widget.h"
@@ -110,52 +114,6 @@ void CcBuildRoadTunnel(const CommandCost &result, TileIndex start_tile, uint32 p
}
}
-/** Structure holding information per roadtype for several functions */
-struct RoadTypeInfo {
- StringID err_build_road; ///< Building a normal piece of road
- StringID err_remove_road; ///< Removing a normal piece of road
- StringID err_depot; ///< Building a depot
- StringID err_build_station[2]; ///< Building a bus or truck station
- StringID err_remove_station[2]; ///< Removing of a bus or truck station
-
- StringID picker_title[2]; ///< Title for the station picker for bus or truck stations
- StringID picker_tooltip[2]; ///< Tooltip for the station picker for bus or truck stations
-
- SpriteID cursor_nesw; ///< Cursor for building NE and SW bits
- SpriteID cursor_nwse; ///< Cursor for building NW and SE bits
- SpriteID cursor_autoroad; ///< Cursor for building autoroad
-};
-
-/** What errors/cursors must be shown for several types of roads */
-static const RoadTypeInfo _road_type_infos[] = {
- {
- STR_ERROR_CAN_T_BUILD_ROAD_HERE,
- STR_ERROR_CAN_T_REMOVE_ROAD_FROM,
- STR_ERROR_CAN_T_BUILD_ROAD_DEPOT,
- { STR_ERROR_CAN_T_BUILD_BUS_STATION, STR_ERROR_CAN_T_BUILD_TRUCK_STATION },
- { STR_ERROR_CAN_T_REMOVE_BUS_STATION, STR_ERROR_CAN_T_REMOVE_TRUCK_STATION },
- { STR_STATION_BUILD_BUS_ORIENTATION, STR_STATION_BUILD_TRUCK_ORIENTATION },
- { STR_STATION_BUILD_BUS_ORIENTATION_TOOLTIP, STR_STATION_BUILD_TRUCK_ORIENTATION_TOOLTIP },
-
- SPR_CURSOR_ROAD_NESW,
- SPR_CURSOR_ROAD_NWSE,
- SPR_CURSOR_AUTOROAD,
- },
- {
- STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE,
- STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM,
- STR_ERROR_CAN_T_BUILD_TRAM_DEPOT,
- { STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION },
- { STR_ERROR_CAN_T_REMOVE_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_REMOVE_CARGO_TRAM_STATION },
- { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION },
- { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION_TOOLTIP, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION_TOOLTIP },
-
- SPR_CURSOR_TRAMWAY_NESW,
- SPR_CURSOR_TRAMWAY_NWSE,
- SPR_CURSOR_AUTOTRAM,
- },
-};
-
/**
* If required, connects a new structure to an existing road or tram by building the missing roadbit.
* @param tile Tile containing the structure to connect.
@@ -166,7 +124,7 @@ void ConnectRoadToStructure(TileIndex tile, DiagDirection direction)
tile += TileOffsByDiagDir(direction);
/* if there is a roadpiece just outside of the station entrance, build a connecting route */
if (IsNormalRoadTile(tile)) {
- if (GetRoadBits(tile, _cur_roadtype) != ROAD_NONE) {
+ if (GetRoadBits(tile, GetRoadTramType(_cur_roadtype)) != ROAD_NONE) {
DoCommandP(tile, _cur_roadtype << 4 | DiagDirToRoadBits(ReverseDiagDir(direction)), 0, CMD_BUILD_ROAD);
}
}
@@ -190,9 +148,10 @@ void CcRoadDepot(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2
* bit 8..15: Length of the road stop.
* @param p2 bit 0: 0 For bus stops, 1 for truck stops.
* bit 1: 0 For normal stops, 1 for drive-through.
- * bit 2..3: The roadtypes.
- * bit 5: Allow stations directly adjacent to other stations.
- * bit 6..7: Entrance direction (#DiagDirection).
+ * bit 2: Allow stations directly adjacent to other stations.
+ * bit 3..4: Entrance direction (#DiagDirection) for normal stops.
+ * bit 3: #Axis of the road for drive-through stops.
+ * bit 5..9: The roadtype.
* bit 16..31: Station ID to join (NEW_STATION if build new one).
* @see CmdBuildRoadStop
*/
@@ -200,7 +159,7 @@ void CcRoadStop(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
{
if (result.Failed()) return;
- DiagDirection dir = (DiagDirection)GB(p2, 6, 2);
+ DiagDirection dir = (DiagDirection)GB(p2, 3, 2);
if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_SPLAT_OTHER, tile);
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
TileArea roadstop_area(tile, GB(p1, 0, 8), GB(p1, 8, 8));
@@ -216,8 +175,8 @@ void CcRoadStop(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
* @param start_tile First tile of the area.
* @param end_tile Last tile of the area.
* @param p2 bit 0: 0 For bus stops, 1 for truck stops.
- * bit 2..3: The roadtypes.
- * bit 5: Allow stations directly adjacent to other stations.
+ * bit 2: Allow stations directly adjacent to other stations.
+ * bit 5..10: The roadtypes.
* @param cmd Command to use.
* @see CcRoadStop()
*/
@@ -230,7 +189,7 @@ static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, uint32 p2, u
SetBit(p2, 1); // It's a drive-through stop.
ddir -= DIAGDIR_END; // Adjust picker result to actual direction.
}
- p2 |= ddir << 6; // Set the DiagDirecion into p2 bits 6 and 7.
+ p2 |= ddir << 3; // Set the DiagDirecion into p2 bits 3 and 4.
TileArea ta(start_tile, end_tile);
CommandContainer cmdcont = { ta.tile, (uint32)(ta.w | ta.h << 8), p2, cmd, CcRoadStop, "" };
@@ -309,15 +268,20 @@ static bool RoadToolbar_CtrlChanged(Window *w)
/** Road toolbar window handler. */
struct BuildRoadToolbarWindow : Window {
- int last_started_action; ///< Last started user action.
+ RoadType roadtype; ///< Road type to build.
+ const RoadTypeInfo *rti; ///< Informations about current road type
+ int last_started_action; ///< Last started user action.
BuildRoadToolbarWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
{
+ this->Initialize(_cur_roadtype);
this->InitNested(window_number);
- this->SetWidgetsDisabledState(true,
- WID_ROT_REMOVE,
- WID_ROT_ONE_WAY,
- WIDGET_LIST_END);
+ this->SetupRoadToolbar();
+ this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
+
+ if (RoadTypeIsRoad(this->roadtype)) {
+ this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true);
+ }
this->OnInvalidateData();
this->last_started_action = WIDGET_LIST_END;
@@ -340,7 +304,8 @@ struct BuildRoadToolbarWindow : Window {
{
if (!gui_scope) return;
- bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD);
+ if (_game_mode != GM_EDITOR && !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) delete this;
+ bool can_build = _game_mode != GM_EDITOR;
this->SetWidgetsDisabledState(!can_build,
WID_ROT_DEPOT,
WID_ROT_BUS_STATION,
@@ -353,6 +318,53 @@ struct BuildRoadToolbarWindow : Window {
}
}
+ void Initialize(RoadType roadtype)
+ {
+ assert(roadtype < ROADTYPE_END);
+ this->roadtype = roadtype;
+ this->rti = GetRoadTypeInfo(this->roadtype);
+ }
+
+ /**
+ * Configures the road toolbar for roadtype given
+ * @param roadtype the roadtype to display
+ */
+ void SetupRoadToolbar()
+ {
+ this->GetWidget<NWidgetCore>(WID_ROT_ROAD_X)->widget_data = rti->gui_sprites.build_x_road;
+ this->GetWidget<NWidgetCore>(WID_ROT_ROAD_Y)->widget_data = rti->gui_sprites.build_y_road;
+ this->GetWidget<NWidgetCore>(WID_ROT_AUTOROAD)->widget_data = rti->gui_sprites.auto_road;
+ if (_game_mode != GM_EDITOR) {
+ this->GetWidget<NWidgetCore>(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot;
+ }
+ this->GetWidget<NWidgetCore>(WID_ROT_CONVERT_ROAD)->widget_data = rti->gui_sprites.convert_road;
+ this->GetWidget<NWidgetCore>(WID_ROT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel;
+ }
+
+ /**
+ * Switch to another road type.
+ * @param roadtype New road type.
+ */
+ void ModifyRoadType(RoadType roadtype)
+ {
+ this->Initialize(roadtype);
+ this->SetupRoadToolbar();
+ this->ReInit();
+ }
+
+ void SetStringParameters(int widget) const override
+ {
+ if (widget == WID_ROT_CAPTION) {
+ if (this->rti->max_speed > 0) {
+ SetDParam(0, STR_TOOLBAR_RAILTYPE_VELOCITY);
+ SetDParam(1, this->rti->strings.toolbar_caption);
+ SetDParam(2, this->rti->max_speed / 2);
+ } else {
+ SetDParam(0, this->rti->strings.toolbar_caption);
+ }
+ }
+ }
+
/**
* Update the remove button lowered state of the road toolbar
*
@@ -365,8 +377,11 @@ struct BuildRoadToolbarWindow : Window {
* Both are only valid if they are able to apply as options. */
switch (clicked_widget) {
case WID_ROT_REMOVE:
- this->RaiseWidget(WID_ROT_ONE_WAY);
- this->SetWidgetDirty(WID_ROT_ONE_WAY);
+ if (RoadTypeIsRoad(this->roadtype)) {
+ this->RaiseWidget(WID_ROT_ONE_WAY);
+ this->SetWidgetDirty(WID_ROT_ONE_WAY);
+ }
+
break;
case WID_ROT_ONE_WAY:
@@ -376,30 +391,30 @@ struct BuildRoadToolbarWindow : Window {
case WID_ROT_BUS_STATION:
case WID_ROT_TRUCK_STATION:
- this->DisableWidget(WID_ROT_ONE_WAY);
+ if (RoadTypeIsRoad(this->roadtype)) this->DisableWidget(WID_ROT_ONE_WAY);
this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget));
break;
case WID_ROT_ROAD_X:
case WID_ROT_ROAD_Y:
case WID_ROT_AUTOROAD:
- this->SetWidgetsDisabledState(!this->IsWidgetLowered(clicked_widget),
- WID_ROT_REMOVE,
- WID_ROT_ONE_WAY,
- WIDGET_LIST_END);
+ this->SetWidgetDisabledState(WID_ROT_REMOVE, !this->IsWidgetLowered(clicked_widget));
+ if (RoadTypeIsRoad(this->roadtype)) {
+ this->SetWidgetDisabledState(WID_ROT_ONE_WAY, !this->IsWidgetLowered(clicked_widget));
+ }
break;
default:
/* When any other buttons than road/station, raise and
* disable the removal button */
- this->SetWidgetsDisabledState(true,
- WID_ROT_REMOVE,
- WID_ROT_ONE_WAY,
- WIDGET_LIST_END);
- this->SetWidgetsLoweredState(false,
- WID_ROT_REMOVE,
- WID_ROT_ONE_WAY,
- WIDGET_LIST_END);
+ this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
+ this->SetWidgetLoweredState(WID_ROT_REMOVE, false);
+
+ if (RoadTypeIsRoad(this->roadtype)) {
+ this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true);
+ this->SetWidgetLoweredState(WID_ROT_ONE_WAY, false);
+ }
+
break;
}
}
@@ -410,17 +425,17 @@ struct BuildRoadToolbarWindow : Window {
_one_way_button_clicked = false;
switch (widget) {
case WID_ROT_ROAD_X:
- HandlePlacePushButton(this, WID_ROT_ROAD_X, _road_type_infos[_cur_roadtype].cursor_nwse, HT_RECT);
+ HandlePlacePushButton(this, WID_ROT_ROAD_X, this->rti->cursor.road_nwse, HT_RECT);
this->last_started_action = widget;
break;
case WID_ROT_ROAD_Y:
- HandlePlacePushButton(this, WID_ROT_ROAD_Y, _road_type_infos[_cur_roadtype].cursor_nesw, HT_RECT);
+ HandlePlacePushButton(this, WID_ROT_ROAD_Y, this->rti->cursor.road_swne, HT_RECT);
this->last_started_action = widget;
break;
case WID_ROT_AUTOROAD:
- HandlePlacePushButton(this, WID_ROT_AUTOROAD, _road_type_infos[_cur_roadtype].cursor_autoroad, HT_RECT);
+ HandlePlacePushButton(this, WID_ROT_AUTOROAD, this->rti->cursor.autoroad, HT_RECT);
this->last_started_action = widget;
break;
@@ -430,15 +445,15 @@ struct BuildRoadToolbarWindow : Window {
break;
case WID_ROT_DEPOT:
- if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD)) return;
- if (HandlePlacePushButton(this, WID_ROT_DEPOT, SPR_CURSOR_ROAD_DEPOT, HT_RECT)) {
+ if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) return;
+ if (HandlePlacePushButton(this, WID_ROT_DEPOT, this->rti->cursor.depot, HT_RECT)) {
ShowRoadDepotPicker(this);
this->last_started_action = widget;
}
break;
case WID_ROT_BUS_STATION:
- if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD)) return;
+ if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) return;
if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT)) {
ShowRVStationPicker(this, ROADSTOP_BUS);
this->last_started_action = widget;
@@ -446,7 +461,7 @@ struct BuildRoadToolbarWindow : Window {
break;
case WID_ROT_TRUCK_STATION:
- if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD)) return;
+ if (_game_mode == GM_EDITOR || !CanBuildVehicleInfrastructure(VEH_ROAD, GetRoadTramType(this->roadtype))) return;
if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, HT_RECT)) {
ShowRVStationPicker(this, ROADSTOP_TRUCK);
this->last_started_action = widget;
@@ -466,7 +481,7 @@ struct BuildRoadToolbarWindow : Window {
break;
case WID_ROT_BUILD_TUNNEL:
- HandlePlacePushButton(this, WID_ROT_BUILD_TUNNEL, SPR_CURSOR_ROAD_TUNNEL, HT_SPECIAL);
+ HandlePlacePushButton(this, WID_ROT_BUILD_TUNNEL, this->rti->cursor.tunnel, HT_SPECIAL);
this->last_started_action = widget;
break;
@@ -478,6 +493,11 @@ struct BuildRoadToolbarWindow : Window {
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
break;
+ case WID_ROT_CONVERT_ROAD:
+ HandlePlacePushButton(this, WID_ROT_CONVERT_ROAD, this->rti->cursor.convert_road, HT_RECT);
+ this->last_started_action = widget;
+ break;
+
default: NOT_REACHED();
}
this->UpdateOptionWidgetStatus((RoadToolbarWidgets)widget);
@@ -493,7 +513,7 @@ struct BuildRoadToolbarWindow : Window {
void OnPlaceObject(Point pt, TileIndex tile) override
{
_remove_button_clicked = this->IsWidgetLowered(WID_ROT_REMOVE);
- _one_way_button_clicked = this->IsWidgetLowered(WID_ROT_ONE_WAY);
+ _one_way_button_clicked = RoadTypeIsRoad(this->roadtype) ? this->IsWidgetLowered(WID_ROT_ONE_WAY) : false;
switch (this->last_started_action) {
case WID_ROT_ROAD_X:
_place_road_flag = RF_DIR_X;
@@ -520,7 +540,7 @@ struct BuildRoadToolbarWindow : Window {
case WID_ROT_DEPOT:
DoCommandP(tile, _cur_roadtype << 2 | _road_depot_orientation, 0,
- CMD_BUILD_ROAD_DEPOT | CMD_MSG(_road_type_infos[_cur_roadtype].err_depot), CcRoadDepot);
+ CMD_BUILD_ROAD_DEPOT | CMD_MSG(this->rti->strings.err_depot), CcRoadDepot);
break;
case WID_ROT_BUS_STATION:
@@ -536,10 +556,14 @@ struct BuildRoadToolbarWindow : Window {
break;
case WID_ROT_BUILD_TUNNEL:
- DoCommandP(tile, RoadTypeToRoadTypes(_cur_roadtype) | (TRANSPORT_ROAD << 8), 0,
+ DoCommandP(tile, _cur_roadtype | (TRANSPORT_ROAD << 8), 0,
CMD_BUILD_TUNNEL | CMD_MSG(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE), CcBuildRoadTunnel);
break;
+ case WID_ROT_CONVERT_ROAD:
+ VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_ROAD);
+ break;
+
default: NOT_REACHED();
}
}
@@ -549,12 +573,13 @@ struct BuildRoadToolbarWindow : Window {
if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true);
this->RaiseButtons();
- this->SetWidgetsDisabledState(true,
- WID_ROT_REMOVE,
- WID_ROT_ONE_WAY,
- WIDGET_LIST_END);
+ this->SetWidgetDisabledState(WID_ROT_REMOVE, true);
this->SetWidgetDirty(WID_ROT_REMOVE);
- this->SetWidgetDirty(WID_ROT_ONE_WAY);
+
+ if (RoadTypeIsRoad(this->roadtype)) {
+ this->SetWidgetDisabledState(WID_ROT_ONE_WAY, true);
+ this->SetWidgetDirty(WID_ROT_ONE_WAY);
+ }
DeleteWindowById(WC_BUS_STATION, TRANSPORT_ROAD);
DeleteWindowById(WC_TRUCK_STATION, TRANSPORT_ROAD);
@@ -613,7 +638,7 @@ struct BuildRoadToolbarWindow : Window {
default: NOT_REACHED();
case DDSP_BUILD_BRIDGE:
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
- ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, RoadTypeToRoadTypes(_cur_roadtype));
+ ShowBuildBridgeWindow(start_tile, end_tile, TRANSPORT_ROAD, _cur_roadtype);
break;
case DDSP_DEMOLISH_AREA:
@@ -627,12 +652,16 @@ struct BuildRoadToolbarWindow : Window {
* Use the first three bits (0x07) if dir == Y
* else use the last 2 bits (X dir has
* not the 3rd bit set) */
+
+ /* Even if _cur_roadtype_id is a uint8 we only use 5 bits so
+ * we could ignore the last 3 bits and reuse them for other
+ * flags */
_place_road_flag = (RoadFlags)((_place_road_flag & RF_DIR_Y) ? (_place_road_flag & 0x07) : (_place_road_flag >> 3));
- DoCommandP(start_tile, end_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 5),
+ DoCommandP(start_tile, end_tile, _place_road_flag | (_cur_roadtype << 3) | (_one_way_button_clicked << 10),
_remove_button_clicked ?
- CMD_REMOVE_LONG_ROAD | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_road) :
- CMD_BUILD_LONG_ROAD | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_road), CcPlaySound_SPLAT_OTHER);
+ CMD_REMOVE_LONG_ROAD | CMD_MSG(this->rti->strings.err_remove_road) :
+ CMD_BUILD_LONG_ROAD | CMD_MSG(this->rti->strings.err_build_road), CcPlaySound_SPLAT_OTHER);
break;
case DDSP_BUILD_BUSSTOP:
@@ -640,9 +669,9 @@ struct BuildRoadToolbarWindow : Window {
if (this->IsWidgetLowered(WID_ROT_BUS_STATION)) {
if (_remove_button_clicked) {
TileArea ta(start_tile, end_tile);
- DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_BUS]), CcPlaySound_SPLAT_OTHER);
+ DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS, CMD_REMOVE_ROAD_STOP | CMD_MSG(this->rti->strings.err_remove_station[ROADSTOP_BUS]), CcPlaySound_SPLAT_OTHER);
} else {
- PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_BUS]));
+ PlaceRoadStop(start_tile, end_tile, _cur_roadtype << 5 | (_ctrl_pressed << 2) | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(this->rti->strings.err_build_station[ROADSTOP_BUS]));
}
}
break;
@@ -652,19 +681,23 @@ struct BuildRoadToolbarWindow : Window {
if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION)) {
if (_remove_button_clicked) {
TileArea ta(start_tile, end_tile);
- DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_TRUCK]), CcPlaySound_SPLAT_OTHER);
+ DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK, CMD_REMOVE_ROAD_STOP | CMD_MSG(this->rti->strings.err_remove_station[ROADSTOP_TRUCK]), CcPlaySound_SPLAT_OTHER);
} else {
- PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_TRUCK]));
+ PlaceRoadStop(start_tile, end_tile, _cur_roadtype << 5 | (_ctrl_pressed << 2) | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(this->rti->strings.err_build_station[ROADSTOP_TRUCK]));
}
}
break;
+
+ case DDSP_CONVERT_ROAD:
+ DoCommandP(end_tile, start_tile, _cur_roadtype, CMD_CONVERT_ROAD | CMD_MSG(rti->strings.err_convert_road), CcPlaySound_SPLAT_OTHER);
+ break;
}
}
}
void OnPlacePresize(Point pt, TileIndex tile) override
{
- DoCommand(tile, RoadTypeToRoadTypes(_cur_roadtype) | (TRANSPORT_ROAD << 8), 0, DC_AUTO, CMD_BUILD_TUNNEL);
+ DoCommand(tile, _cur_roadtype | (TRANSPORT_ROAD << 8), 0, DC_AUTO, CMD_BUILD_TUNNEL);
VpSetPresizeRange(tile, _build_tunnel_endtile == 0 ? tile : _build_tunnel_endtile);
}
@@ -674,34 +707,36 @@ struct BuildRoadToolbarWindow : Window {
return ES_NOT_HANDLED;
}
- static HotkeyList hotkeys;
+ static HotkeyList road_hotkeys;
+ static HotkeyList tram_hotkeys;
};
/**
* Handler for global hotkeys of the BuildRoadToolbarWindow.
* @param hotkey Hotkey
+ * @param last_build Last build road type
* @return ES_HANDLED if hotkey was accepted.
*/
-static EventState RoadToolbarGlobalHotkeys(int hotkey)
+static EventState RoadTramToolbarGlobalHotkeys(int hotkey, RoadType last_build)
{
- Window *w = nullptr;
- switch (_game_mode) {
- case GM_NORMAL: {
- extern RoadType _last_built_roadtype;
- w = ShowBuildRoadToolbar(_last_built_roadtype);
- break;
- }
+ Window *w = (_game_mode == GM_NORMAL) ? ShowBuildRoadToolbar(last_build) : ShowBuildRoadScenToolbar(last_build);
+ if (w == nullptr) return ES_NOT_HANDLED;
+ return w->OnHotkey(hotkey);
+}
- case GM_EDITOR:
- w = ShowBuildRoadScenToolbar();
- break;
+static EventState RoadToolbarGlobalHotkeys(int hotkey)
+{
+ if (_game_mode == GM_NORMAL && !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_ROAD)) return ES_NOT_HANDLED;
- default:
- break;
- }
+ extern RoadType _last_built_roadtype;
+ return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_roadtype);
+}
- if (w == nullptr) return ES_NOT_HANDLED;
- return w->OnHotkey(hotkey);
+static EventState TramToolbarGlobalHotkeys(int hotkey)
+{
+ if (_game_mode != GM_NORMAL || !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_TRAM)) return ES_NOT_HANDLED;
+ extern RoadType _last_built_tramtype;
+ return RoadTramToolbarGlobalHotkeys(hotkey, _last_built_tramtype);
}
static Hotkey roadtoolbar_hotkeys[] = {
@@ -716,15 +751,32 @@ static Hotkey roadtoolbar_hotkeys[] = {
Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE),
Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL),
Hotkey('R', "remove", WID_ROT_REMOVE),
+ Hotkey('C', "convert", WID_ROT_CONVERT_ROAD),
HOTKEY_LIST_END
};
-HotkeyList BuildRoadToolbarWindow::hotkeys("roadtoolbar", roadtoolbar_hotkeys, RoadToolbarGlobalHotkeys);
+HotkeyList BuildRoadToolbarWindow::road_hotkeys("roadtoolbar", roadtoolbar_hotkeys, RoadToolbarGlobalHotkeys);
+
+static Hotkey tramtoolbar_hotkeys[] = {
+ Hotkey('1', "build_x", WID_ROT_ROAD_X),
+ Hotkey('2', "build_y", WID_ROT_ROAD_Y),
+ Hotkey('3', "autoroad", WID_ROT_AUTOROAD),
+ Hotkey('4', "demolish", WID_ROT_DEMOLISH),
+ Hotkey('5', "depot", WID_ROT_DEPOT),
+ Hotkey('6', "bus_station", WID_ROT_BUS_STATION),
+ Hotkey('7', "truck_station", WID_ROT_TRUCK_STATION),
+ Hotkey('B', "bridge", WID_ROT_BUILD_BRIDGE),
+ Hotkey('T', "tunnel", WID_ROT_BUILD_TUNNEL),
+ Hotkey('R', "remove", WID_ROT_REMOVE),
+ Hotkey('C', "convert", WID_ROT_CONVERT_ROAD),
+ HOTKEY_LIST_END
+};
+HotkeyList BuildRoadToolbarWindow::tram_hotkeys("tramtoolbar", tramtoolbar_hotkeys, TramToolbarGlobalHotkeys);
static const NWidgetPart _nested_build_road_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
- NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
EndContainer(),
NWidget(NWID_HORIZONTAL),
@@ -751,6 +803,8 @@ static const NWidgetPart _nested_build_road_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD),
EndContainer(),
};
@@ -759,13 +813,13 @@ static WindowDesc _build_road_desc(
WC_BUILD_TOOLBAR, WC_NONE,
WDF_CONSTRUCTION,
_nested_build_road_widgets, lengthof(_nested_build_road_widgets),
- &BuildRoadToolbarWindow::hotkeys
+ &BuildRoadToolbarWindow::road_hotkeys
);
static const NWidgetPart _nested_build_tramway_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
- NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_ROAD_TOOLBAR_TRAM_CONSTRUCTION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
EndContainer(),
NWidget(NWID_HORIZONTAL),
@@ -784,13 +838,14 @@ static const NWidgetPart _nested_build_tramway_widgets[] = {
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRUCK_BAY, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
- NWidget(WWT_EMPTY, COLOUR_DARK_GREEN, WID_ROT_ONE_WAY), SetMinimalSize(0, 0),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE),
SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM),
EndContainer(),
};
@@ -799,7 +854,7 @@ static WindowDesc _build_tramway_desc(
WC_BUILD_TOOLBAR, WC_NONE,
WDF_CONSTRUCTION,
_nested_build_tramway_widgets, lengthof(_nested_build_tramway_widgets),
- &BuildRoadToolbarWindow::hotkeys
+ &BuildRoadToolbarWindow::tram_hotkeys
);
/**
@@ -812,16 +867,18 @@ static WindowDesc _build_tramway_desc(
Window *ShowBuildRoadToolbar(RoadType roadtype)
{
if (!Company::IsValidID(_local_company)) return nullptr;
- _cur_roadtype = roadtype;
+ if (!ValParamRoadType(roadtype)) return nullptr;
DeleteWindowByClass(WC_BUILD_TOOLBAR);
- return AllocateWindowDescFront<BuildRoadToolbarWindow>(roadtype == ROADTYPE_ROAD ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD);
+ _cur_roadtype = roadtype;
+
+ return AllocateWindowDescFront<BuildRoadToolbarWindow>(RoadTypeIsRoad(_cur_roadtype) ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD);
}
static const NWidgetPart _nested_build_road_scen_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
- NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
EndContainer(),
NWidget(NWID_HORIZONTAL),
@@ -842,6 +899,8 @@ static const NWidgetPart _nested_build_road_scen_widgets[] = {
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_TUNNEL),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_ROAD),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD),
EndContainer(),
};
@@ -850,17 +909,54 @@ static WindowDesc _build_road_scen_desc(
WC_SCEN_BUILD_TOOLBAR, WC_NONE,
WDF_CONSTRUCTION,
_nested_build_road_scen_widgets, lengthof(_nested_build_road_scen_widgets),
- &BuildRoadToolbarWindow::hotkeys
+ &BuildRoadToolbarWindow::road_hotkeys
+);
+
+static const NWidgetPart _nested_build_tramway_scen_widgets[] = {
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
+ NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_ROT_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
+ NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
+ EndContainer(),
+ NWidget(NWID_HORIZONTAL),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_X),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_X_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_ROAD_Y),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_TRAMWAY_Y_DIR, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_AUTOROAD),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
+ NWidget(WWT_PANEL, COLOUR_DARK_GREEN, -1), SetMinimalSize(0, 22), SetFill(1, 1), EndContainer(),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_BRIDGE),
+ SetFill(0, 1), SetMinimalSize(43, 22), SetDataTip(SPR_IMG_BRIDGE, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_BRIDGE),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUILD_TUNNEL),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_TUNNEL, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_TUNNEL),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_REMOVE),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_ROAD_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR_TRAMWAYS),
+ NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_CONVERT_ROAD),
+ SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_CONVERT_ROAD, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM),
+ EndContainer(),
+};
+
+static WindowDesc _build_tramway_scen_desc(
+ WDP_AUTO, "toolbar_tram_scen", 0, 0,
+ WC_SCEN_BUILD_TOOLBAR, WC_NONE,
+ WDF_CONSTRUCTION,
+ _nested_build_tramway_scen_widgets, lengthof(_nested_build_tramway_scen_widgets),
+ &BuildRoadToolbarWindow::tram_hotkeys
);
/**
* Show the road building toolbar in the scenario editor.
* @return The just opened toolbar, or \c nullptr if the toolbar was already open.
*/
-Window *ShowBuildRoadScenToolbar()
+Window *ShowBuildRoadScenToolbar(RoadType roadtype)
{
- _cur_roadtype = ROADTYPE_ROAD;
- return AllocateWindowDescFront<BuildRoadToolbarWindow>(&_build_road_scen_desc, TRANSPORT_ROAD);
+ DeleteWindowById(WC_SCEN_BUILD_TOOLBAR, TRANSPORT_ROAD);
+ _cur_roadtype = roadtype;
+
+ return AllocateWindowDescFront<BuildRoadToolbarWindow>(RoadTypeIsRoad(_cur_roadtype) ? &_build_road_scen_desc : &_build_tramway_scen_desc, TRANSPORT_ROAD);
}
struct BuildRoadDepotWindow : public PickerWindowBase {
@@ -869,7 +965,7 @@ struct BuildRoadDepotWindow : public PickerWindowBase {
this->CreateNestedTree();
this->LowerWidget(_road_depot_orientation + WID_BROD_DEPOT_NE);
- if ( _cur_roadtype == ROADTYPE_TRAM) {
+ if (RoadTypeIsTram(_cur_roadtype)) {
this->GetWidget<NWidgetCore>(WID_BROD_CAPTION)->widget_data = STR_BUILD_DEPOT_TRAM_ORIENTATION_CAPTION;
for (int i = WID_BROD_DEPOT_NE; i <= WID_BROD_DEPOT_NW; i++) this->GetWidget<NWidgetCore>(i)->tool_tip = STR_BUILD_DEPOT_TRAM_ORIENTATION_SELECT_TOOLTIP;
}
@@ -960,13 +1056,14 @@ struct BuildRoadStationWindow : public PickerWindowBase {
this->CreateNestedTree();
/* Trams don't have non-drivethrough stations */
- if (_cur_roadtype == ROADTYPE_TRAM && _road_station_picker_orientation < DIAGDIR_END) {
+ if (RoadTypeIsTram(_cur_roadtype) && _road_station_picker_orientation < DIAGDIR_END) {
_road_station_picker_orientation = DIAGDIR_END;
}
+ const RoadTypeInfo *rti = GetRoadTypeInfo(_cur_roadtype);
+ this->GetWidget<NWidgetCore>(WID_BROS_CAPTION)->widget_data = rti->strings.picker_title[rs];
- this->GetWidget<NWidgetCore>(WID_BROS_CAPTION)->widget_data = _road_type_infos[_cur_roadtype].picker_title[rs];
- for (uint i = (_cur_roadtype == ROADTYPE_TRAM ? WID_BROS_STATION_X : WID_BROS_STATION_NE); i < WID_BROS_LT_OFF; i++) {
- this->GetWidget<NWidgetCore>(i)->tool_tip = _road_type_infos[_cur_roadtype].picker_tooltip[rs];
+ for (uint i = RoadTypeIsTram(_cur_roadtype) ? WID_BROS_STATION_X : WID_BROS_STATION_NE; i < WID_BROS_LT_OFF; i++) {
+ this->GetWidget<NWidgetCore>(i)->tool_tip = rti->strings.picker_tooltip[rs];
}
this->LowerWidget(_road_station_picker_orientation + WID_BROS_STATION_NE);
@@ -1022,7 +1119,7 @@ struct BuildRoadStationWindow : public PickerWindowBase {
if (!IsInsideMM(widget, WID_BROS_STATION_NE, WID_BROS_STATION_Y + 1)) return;
StationType st = (this->window_class == WC_BUS_STATION) ? STATION_BUS : STATION_TRUCK;
- StationPickerDrawSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), st, INVALID_RAILTYPE, widget < WID_BROS_STATION_X ? ROADTYPE_ROAD : _cur_roadtype, widget - WID_BROS_STATION_NE);
+ StationPickerDrawSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), st, INVALID_RAILTYPE, _cur_roadtype, widget - WID_BROS_STATION_NE);
}
void OnClick(Point pt, int widget, int click_count) override
@@ -1149,7 +1246,7 @@ static WindowDesc _tram_station_picker_desc(
static void ShowRVStationPicker(Window *parent, RoadStopType rs)
{
- new BuildRoadStationWindow(_cur_roadtype == ROADTYPE_ROAD ? &_road_station_picker_desc : &_tram_station_picker_desc, parent, rs);
+ new BuildRoadStationWindow(RoadTypeIsRoad(_cur_roadtype) ? &_road_station_picker_desc : &_tram_station_picker_desc, parent, rs);
}
void InitializeRoadGui()
@@ -1157,3 +1254,118 @@ void InitializeRoadGui()
_road_depot_orientation = DIAGDIR_NW;
_road_station_picker_orientation = DIAGDIR_NW;
}
+
+/**
+ * I really don't know why rail_gui.cpp has this too, shouldn't be included in the other one?
+ */
+void InitializeRoadGUI()
+{
+ BuildRoadToolbarWindow *w = dynamic_cast<BuildRoadToolbarWindow *>(FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_ROAD));
+ if (w != nullptr) w->ModifyRoadType(_cur_roadtype);
+}
+
+DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement, bool all_option)
+{
+ RoadTypes used_roadtypes;
+ RoadTypes avail_roadtypes;
+
+ const Company *c = Company::Get(_local_company);
+
+ /* Find the used roadtypes. */
+ if (for_replacement) {
+ avail_roadtypes = GetCompanyRoadTypes(c->index, false);
+ used_roadtypes = GetRoadTypes(false);
+ } else {
+ avail_roadtypes = c->avail_roadtypes;
+ used_roadtypes = GetRoadTypes(true);
+ }
+
+ /* Filter listed road types */
+ if (!HasBit(rtts, RTT_ROAD)) used_roadtypes &= _roadtypes_type;
+ if (!HasBit(rtts, RTT_TRAM)) used_roadtypes &= ~_roadtypes_type;
+
+ DropDownList list;
+
+ if (all_option) {
+ list.emplace_back(new DropDownListStringItem(STR_REPLACE_ALL_ROADTYPE, INVALID_ROADTYPE, false));
+ }
+
+ Dimension d = { 0, 0 };
+ RoadType rt;
+ /* Get largest icon size, to ensure text is aligned on each menu item. */
+ if (!for_replacement) {
+ FOR_ALL_SORTED_ROADTYPES(rt) {
+ if (!HasBit(used_roadtypes, rt)) continue;
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+ d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road));
+ }
+ }
+
+ FOR_ALL_SORTED_ROADTYPES(rt) {
+ /* If it's not used ever, don't show it to the user. */
+ if (!HasBit(used_roadtypes, rt)) continue;
+
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+
+ DropDownListParamStringItem *item;
+ if (for_replacement) {
+ item = new DropDownListParamStringItem(rti->strings.replace_text, rt, !HasBit(avail_roadtypes, rt));
+ } else {
+ StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING;
+ DropDownListIconItem *iconitem = new DropDownListIconItem(rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt));
+ iconitem->SetDimension(d);
+ item = iconitem;
+ }
+ item->SetParam(0, rti->strings.menu_text);
+ item->SetParam(1, rti->max_speed / 2);
+ list.emplace_back(item);
+ }
+
+ if (list.size() == 0) {
+ /* Empty dropdowns are not allowed */
+ list.emplace_back(new DropDownListStringItem(STR_NONE, INVALID_ROADTYPE, true));
+ }
+
+ return list;
+}
+
+DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts)
+{
+ RoadTypes avail_roadtypes = GetRoadTypes(false);
+ avail_roadtypes = AddDateIntroducedRoadTypes(avail_roadtypes, _date);
+ RoadTypes used_roadtypes = GetRoadTypes(true);
+
+ /* Filter listed road types */
+ if (!HasBit(rtts, RTT_ROAD)) used_roadtypes &= _roadtypes_type;
+ if (!HasBit(rtts, RTT_TRAM)) used_roadtypes &= ~_roadtypes_type;
+
+ DropDownList list;
+
+ /* If it's not used ever, don't show it to the user. */
+ Dimension d = { 0, 0 };
+ RoadType rt;
+ FOR_ALL_SORTED_ROADTYPES(rt) {
+ if (!HasBit(used_roadtypes, rt)) continue;
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+ d = maxdim(d, GetSpriteSize(rti->gui_sprites.build_x_road));
+ }
+ FOR_ALL_SORTED_ROADTYPES(rt) {
+ if (!HasBit(used_roadtypes, rt)) continue;
+
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+
+ StringID str = rti->max_speed > 0 ? STR_TOOLBAR_RAILTYPE_VELOCITY : STR_JUST_STRING;
+ DropDownListIconItem *item = new DropDownListIconItem(rti->gui_sprites.build_x_road, PAL_NONE, str, rt, !HasBit(avail_roadtypes, rt));
+ item->SetDimension(d);
+ item->SetParam(0, rti->strings.menu_text);
+ item->SetParam(1, rti->max_speed);
+ list.emplace_back(item);
+ }
+
+ if (list.size() == 0) {
+ /* Empty dropdowns are not allowed */
+ list.emplace_back(new DropDownListStringItem(STR_NONE, -1, true));
+ }
+
+ return list;
+}
diff --git a/src/road_gui.h b/src/road_gui.h
index c56443c37..3f0b8f900 100644
--- a/src/road_gui.h
+++ b/src/road_gui.h
@@ -15,9 +15,13 @@
#include "road_type.h"
#include "tile_type.h"
#include "direction_type.h"
+#include "widgets/dropdown_type.h"
struct Window *ShowBuildRoadToolbar(RoadType roadtype);
-struct Window *ShowBuildRoadScenToolbar();
+struct Window *ShowBuildRoadScenToolbar(RoadType roadtype);
void ConnectRoadToStructure(TileIndex tile, DiagDirection direction);
+DropDownList GetRoadTypeDropDownList(RoadTramTypes rtts, bool for_replacement = false, bool all_option = false);
+DropDownList GetScenRoadTypeDropDownList(RoadTramTypes rtts);
+void InitializeRoadGUI();
#endif /* ROAD_GUI_H */
diff --git a/src/road_internal.h b/src/road_internal.h
index 8da909e94..79782e3bb 100644
--- a/src/road_internal.h
+++ b/src/road_internal.h
@@ -17,8 +17,8 @@
RoadBits CleanUpRoadBits(const TileIndex tile, RoadBits org_rb);
-CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadType rt, DoCommandFlag flags, bool town_check = true);
+CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, RoadTramType rtt, DoCommandFlag flags, bool town_check = true);
-void DrawRoadCatenary(const TileInfo *ti, RoadBits tram);
+void DrawRoadCatenary(const TileInfo *ti);
#endif /* ROAD_INTERNAL_H */
diff --git a/src/road_map.cpp b/src/road_map.cpp
index 4984117ba..1ab95043a 100644
--- a/src/road_map.cpp
+++ b/src/road_map.cpp
@@ -32,15 +32,15 @@
* @param straight_tunnel_bridge_entrance whether to return straight road bits for tunnels/bridges.
* @return the road bits of the given tile
*/
-RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge_entrance)
+RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance)
{
- if (!HasTileRoadType(tile, rt)) return ROAD_NONE;
+ if (!MayHaveRoad(tile) || !HasTileRoadType(tile, rtt)) return ROAD_NONE;
switch (GetTileType(tile)) {
case MP_ROAD:
switch (GetRoadTileType(tile)) {
default:
- case ROAD_TILE_NORMAL: return GetRoadBits(tile, rt);
+ case ROAD_TILE_NORMAL: return GetRoadBits(tile, rtt);
case ROAD_TILE_CROSSING: return GetCrossingRoadBits(tile);
case ROAD_TILE_DEPOT: return DiagDirToRoadBits(GetRoadDepotDirection(tile));
}
diff --git a/src/road_map.h b/src/road_map.h
index 49526d37f..f5600d590 100644
--- a/src/road_map.h
+++ b/src/road_map.h
@@ -27,6 +27,24 @@ enum RoadTileType {
};
/**
+ * Test whether a tile can have road/tram types.
+ * @param t Tile to query.
+ * @return true if tile can be queried about road/tram types.
+ */
+static inline bool MayHaveRoad(TileIndex t)
+{
+ switch (GetTileType(t)) {
+ case MP_ROAD:
+ case MP_STATION:
+ case MP_TUNNELBRIDGE:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/**
* Get the type of the road tile.
* @param t Tile to query.
* @pre IsTileType(t, MP_ROAD)
@@ -108,26 +126,11 @@ static inline bool IsRoadDepotTile(TileIndex t)
* @pre IsNormalRoad(t)
* @return The present road bits for the road type.
*/
-static inline RoadBits GetRoadBits(TileIndex t, RoadType rt)
+static inline RoadBits GetRoadBits(TileIndex t, RoadTramType rtt)
{
assert(IsNormalRoad(t));
- switch (rt) {
- default: NOT_REACHED();
- case ROADTYPE_ROAD: return (RoadBits)GB(_m[t].m5, 0, 4);
- case ROADTYPE_TRAM: return (RoadBits)GB(_m[t].m3, 0, 4);
- }
-}
-
-/**
- * Get all RoadBits set on a tile except from the given RoadType
- *
- * @param t The tile from which we want to get the RoadBits
- * @param rt The RoadType which we exclude from the querry
- * @return all set RoadBits of the tile which are not from the given RoadType
- */
-static inline RoadBits GetOtherRoadBits(TileIndex t, RoadType rt)
-{
- return GetRoadBits(t, rt == ROADTYPE_ROAD ? ROADTYPE_TRAM : ROADTYPE_ROAD);
+ if (rtt == RTT_TRAM) return (RoadBits)GB(_m[t].m3, 0, 4);
+ return (RoadBits)GB(_m[t].m5, 0, 4);
}
/**
@@ -138,7 +141,7 @@ static inline RoadBits GetOtherRoadBits(TileIndex t, RoadType rt)
*/
static inline RoadBits GetAllRoadBits(TileIndex tile)
{
- return GetRoadBits(tile, ROADTYPE_ROAD) | GetRoadBits(tile, ROADTYPE_TRAM);
+ return GetRoadBits(tile, RTT_ROAD) | GetRoadBits(tile, RTT_TRAM);
}
/**
@@ -148,96 +151,125 @@ static inline RoadBits GetAllRoadBits(TileIndex tile)
* @param rt Road type.
* @pre IsNormalRoad(t)
*/
-static inline void SetRoadBits(TileIndex t, RoadBits r, RoadType rt)
+static inline void SetRoadBits(TileIndex t, RoadBits r, RoadTramType rtt)
{
assert(IsNormalRoad(t)); // XXX incomplete
- switch (rt) {
- default: NOT_REACHED();
- case ROADTYPE_ROAD: SB(_m[t].m5, 0, 4, r); break;
- case ROADTYPE_TRAM: SB(_m[t].m3, 0, 4, r); break;
+ if (rtt == RTT_TRAM) {
+ SB(_m[t].m3, 0, 4, r);
+ } else {
+ SB(_m[t].m5, 0, 4, r);
}
}
+static inline RoadType GetRoadTypeRoad(TileIndex t)
+{
+ assert(MayHaveRoad(t));
+ return (RoadType)GB(_m[t].m4, 0, 6);
+}
+
+static inline RoadType GetRoadTypeTram(TileIndex t)
+{
+ assert(MayHaveRoad(t));
+ return (RoadType)GB(_me[t].m8, 6, 6);
+}
+
+static inline RoadType GetRoadType(TileIndex t, RoadTramType rtt)
+{
+ return (rtt == RTT_TRAM) ? GetRoadTypeTram(t) : GetRoadTypeRoad(t);
+}
+
/**
* Get the present road types of a tile.
* @param t The tile to query.
* @return Present road types.
*/
-static inline RoadTypes GetRoadTypes(TileIndex t)
+static inline RoadTypes GetPresentRoadTypes(TileIndex t)
{
- return (RoadTypes)GB(_me[t].m7, 6, 2);
+ RoadTypes result = ROADTYPES_NONE;
+ if (MayHaveRoad(t)) {
+ if (GetRoadTypeRoad(t) != INVALID_ROADTYPE) SetBit(result, GetRoadTypeRoad(t));
+ if (GetRoadTypeTram(t) != INVALID_ROADTYPE) SetBit(result, GetRoadTypeTram(t));
+ }
+ return result;
+}
+
+static inline bool HasRoadTypeRoad(TileIndex t)
+{
+ return GetRoadTypeRoad(t) != INVALID_ROADTYPE;
+}
+
+static inline bool HasRoadTypeTram(TileIndex t)
+{
+ return GetRoadTypeTram(t) != INVALID_ROADTYPE;
}
/**
- * Set the present road types of a tile.
- * @param t The tile to change.
- * @param rt The new road types.
+ * Check if a tile has a road or a tram road type.
+ * @param t The tile to check.
+ * @param tram True to check tram, false to check road.
+ * @return True if the tile has the specified road type.
*/
-static inline void SetRoadTypes(TileIndex t, RoadTypes rt)
+static inline bool HasTileRoadType(TileIndex t, RoadTramType rtt)
{
- assert(IsTileType(t, MP_ROAD) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE));
- SB(_me[t].m7, 6, 2, rt);
+ return GetRoadType(t, rtt) != INVALID_ROADTYPE;
}
/**
- * Check if a tile has a specific road type.
+ * Check if a tile has one of the specified road types.
* @param t The tile to check.
- * @param rt Road type to check.
- * @return True if the tile has the specified road type.
+ * @param rts Allowed road types.
+ * @return True if the tile has one of the specified road types.
*/
-static inline bool HasTileRoadType(TileIndex t, RoadType rt)
+static inline bool HasTileAnyRoadType(TileIndex t, RoadTypes rts)
{
- return HasBit(GetRoadTypes(t), rt);
+ if (!MayHaveRoad(t)) return false;
+ return (GetPresentRoadTypes(t) & rts);
}
/**
* Get the owner of a specific road type.
* @param t The tile to query.
- * @param rt The road type to get the owner of.
+ * @param rtt RoadTramType.
* @return Owner of the given road type.
*/
-static inline Owner GetRoadOwner(TileIndex t, RoadType rt)
-{
- assert(IsTileType(t, MP_ROAD) || IsTileType(t, MP_STATION) || IsTileType(t, MP_TUNNELBRIDGE));
- switch (rt) {
- default: NOT_REACHED();
- case ROADTYPE_ROAD: return (Owner)GB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5);
- case ROADTYPE_TRAM: {
- /* Trams don't need OWNER_TOWN, and remapping OWNER_NONE
- * to OWNER_TOWN makes it use one bit less */
- Owner o = (Owner)GB(_m[t].m3, 4, 4);
- return o == OWNER_TOWN ? OWNER_NONE : o;
- }
- }
+static inline Owner GetRoadOwner(TileIndex t, RoadTramType rtt)
+{
+ assert(MayHaveRoad(t));
+ if (rtt == RTT_ROAD) return (Owner)GB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5);
+
+ /* Trams don't need OWNER_TOWN, and remapping OWNER_NONE
+ * to OWNER_TOWN makes it use one bit less */
+ Owner o = (Owner)GB(_m[t].m3, 4, 4);
+ return o == OWNER_TOWN ? OWNER_NONE : o;
}
/**
* Set the owner of a specific road type.
* @param t The tile to change.
- * @param rt The road type to change the owner of.
+ * @param rtt RoadTramType.
* @param o New owner of the given road type.
*/
-static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o)
+static inline void SetRoadOwner(TileIndex t, RoadTramType rtt, Owner o)
{
- switch (rt) {
- default: NOT_REACHED();
- case ROADTYPE_ROAD: SB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5, o); break;
- case ROADTYPE_TRAM: SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o); break;
+ if (rtt == RTT_ROAD) {
+ SB(IsNormalRoadTile(t) ? _m[t].m1 : _me[t].m7, 0, 5, o);
+ } else {
+ SB(_m[t].m3, 4, 4, o == OWNER_NONE ? OWNER_TOWN : o);
}
}
/**
* Check if a specific road type is owned by an owner.
* @param t The tile to query.
- * @param rt The road type to compare the owner of.
+ * @param tram True to check tram, false to check road.
* @param o Owner to compare with.
* @pre HasTileRoadType(t, rt)
* @return True if the road type is owned by the given owner.
*/
-static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o)
+static inline bool IsRoadOwner(TileIndex t, RoadTramType rtt, Owner o)
{
- assert(HasTileRoadType(t, rt));
- return (GetRoadOwner(t, rt) == o);
+ assert(HasTileRoadType(t, rtt));
+ return (GetRoadOwner(t, rtt) == o);
}
/**
@@ -248,7 +280,7 @@ static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o)
*/
static inline bool HasTownOwnedRoad(TileIndex t)
{
- return HasTileRoadType(t, ROADTYPE_ROAD) && IsRoadOwner(t, ROADTYPE_ROAD, OWNER_TOWN);
+ return HasTileRoadType(t, RTT_ROAD) && IsRoadOwner(t, RTT_ROAD, OWNER_TOWN);
}
/** Which directions are disallowed ? */
@@ -539,29 +571,80 @@ static inline DiagDirection GetRoadDepotDirection(TileIndex t)
}
-RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge_entrance = false);
+RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false);
+/**
+ * Set the road road type of a tile.
+ * @param t The tile to change.
+ * @param rt The road type to set.
+ */
+static inline void SetRoadTypeRoad(TileIndex t, RoadType rt)
+{
+ assert(MayHaveRoad(t));
+ assert(rt == INVALID_ROADTYPE || RoadTypeIsRoad(rt));
+ SB(_m[t].m4, 0, 6, rt);
+}
+
+/**
+ * Set the tram road type of a tile.
+ * @param t The tile to change.
+ * @param rt The road type to set.
+ */
+static inline void SetRoadTypeTram(TileIndex t, RoadType rt)
+{
+ assert(MayHaveRoad(t));
+ assert(rt == INVALID_ROADTYPE || RoadTypeIsTram(rt));
+ SB(_me[t].m8, 6, 6, rt);
+}
+
+/**
+ * Set the road type of a tile.
+ * @param t The tile to change.
+ * @param rtt Set road or tram type.
+ * @param rt The road type to set.
+ */
+static inline void SetRoadType(TileIndex t, RoadTramType rtt, RoadType rt)
+{
+ if (rtt == RTT_TRAM) {
+ SetRoadTypeTram(t, rt);
+ } else {
+ SetRoadTypeRoad(t, rt);
+ }
+}
+
+/**
+ * Set the present road types of a tile.
+ * @param t The tile to change.
+ * @param road_rt The road roadtype to set for the tile.
+ * @param tram_rt The tram roadtype to set for the tile.
+ */
+static inline void SetRoadTypes(TileIndex t, RoadType road_rt, RoadType tram_rt)
+{
+ SetRoadTypeRoad(t, road_rt);
+ SetRoadTypeTram(t, tram_rt);
+}
/**
* Make a normal road tile.
- * @param t Tile to make a normal road.
- * @param bits Road bits to set for all present road types.
- * @param rot New present road types.
- * @param town Town ID if the road is a town-owned road.
- * @param road New owner of road.
- * @param tram New owner of tram tracks.
+ * @param t Tile to make a normal road.
+ * @param bits Road bits to set for all present road types.
+ * @param road_rt The road roadtype to set for the tile.
+ * @param tram_rt The tram roadtype to set for the tile.
+ * @param town Town ID if the road is a town-owned road.
+ * @param road New owner of road.
+ * @param tram New owner of tram tracks.
*/
-static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, TownID town, Owner road, Owner tram)
+static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadType road_rt, RoadType tram_rt, TownID town, Owner road, Owner tram)
{
SetTileType(t, MP_ROAD);
SetTileOwner(t, road);
_m[t].m2 = town;
- _m[t].m3 = (HasBit(rot, ROADTYPE_TRAM) ? bits : 0);
- _m[t].m4 = 0;
- _m[t].m5 = (HasBit(rot, ROADTYPE_ROAD) ? bits : 0) | ROAD_TILE_NORMAL << 6;
+ _m[t].m3 = (tram_rt != INVALID_ROADTYPE ? bits : 0);
+ _m[t].m5 = (road_rt != INVALID_ROADTYPE ? bits : 0) | ROAD_TILE_NORMAL << 6;
SB(_me[t].m6, 2, 4, 0);
- _me[t].m7 = rot << 6;
- SetRoadOwner(t, ROADTYPE_TRAM, tram);
+ _me[t].m7 = 0;
+ SetRoadTypes(t, road_rt, tram_rt);
+ SetRoadOwner(t, RTT_TRAM, tram);
}
/**
@@ -572,21 +655,23 @@ static inline void MakeRoadNormal(TileIndex t, RoadBits bits, RoadTypes rot, Tow
* @param rail New owner of the rail track.
* @param roaddir Axis of the road.
* @param rat New rail type.
- * @param rot New present road types.
+ * @param road_rt The road roadtype to set for the tile.
+ * @param tram_rt The tram roadtype to set for the tile.
* @param town Town ID if the road is a town-owned road.
*/
-static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadTypes rot, uint town)
+static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner rail, Axis roaddir, RailType rat, RoadType road_rt, RoadType tram_rt, uint town)
{
SetTileType(t, MP_ROAD);
SetTileOwner(t, rail);
_m[t].m2 = town;
_m[t].m3 = 0;
- _m[t].m4 = 0;
+ _m[t].m4 = INVALID_ROADTYPE;
_m[t].m5 = ROAD_TILE_CROSSING << 6 | roaddir;
SB(_me[t].m6, 2, 4, 0);
- _me[t].m7 = rot << 6 | road;
- _me[t].m8 = rat;
- SetRoadOwner(t, ROADTYPE_TRAM, tram);
+ _me[t].m7 = road;
+ _me[t].m8 = INVALID_ROADTYPE << 6 | rat;
+ SetRoadTypes(t, road_rt, tram_rt);
+ SetRoadOwner(t, RTT_TRAM, tram);
}
/**
@@ -594,7 +679,7 @@ static inline void MakeRoadCrossing(TileIndex t, Owner road, Owner tram, Owner r
* @param t Tile to make a level crossing.
* @param owner New owner of the depot.
* @param did New depot ID.
- * @param dir Direction of the depot exit.
+ * @param dir Direction of the depot exit.*
* @param rt Road type of the depot.
*/
static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDirection dir, RoadType rt)
@@ -603,11 +688,13 @@ static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDire
SetTileOwner(t, owner);
_m[t].m2 = did;
_m[t].m3 = 0;
- _m[t].m4 = 0;
+ _m[t].m4 = INVALID_ROADTYPE;
_m[t].m5 = ROAD_TILE_DEPOT << 6 | dir;
SB(_me[t].m6, 2, 4, 0);
- _me[t].m7 = RoadTypeToRoadTypes(rt) << 6 | owner;
- SetRoadOwner(t, ROADTYPE_TRAM, owner);
+ _me[t].m7 = owner;
+ _me[t].m8 = INVALID_ROADTYPE << 6;
+ SetRoadType(t, GetRoadTramType(rt), rt);
+ SetRoadOwner(t, RTT_TRAM, owner);
}
#endif /* ROAD_MAP_H */
diff --git a/src/road_type.h b/src/road_type.h
index 7056f6d62..f01d1d035 100644
--- a/src/road_type.h
+++ b/src/road_type.h
@@ -14,36 +14,37 @@
#include "core/enum_type.hpp"
+typedef uint32 RoadTypeLabel;
+
+static const RoadTypeLabel ROADTYPE_ROAD_LABEL = 'ROAD';
+static const RoadTypeLabel ROADTYPE_TRAM_LABEL = 'TRAM';
+
/**
* The different roadtypes we support
*
* @note currently only ROADTYPE_ROAD and ROADTYPE_TRAM are supported.
*/
enum RoadType {
- ROADTYPE_BEGIN = 0, ///< Used for iterations
- ROADTYPE_ROAD = 0, ///< Basic road type
- ROADTYPE_TRAM = 1, ///< Trams
- ROADTYPE_END, ///< Used for iterations
- INVALID_ROADTYPE = 0xFF, ///< flag for invalid roadtype
+ ROADTYPE_BEGIN = 0, ///< Used for iterations
+ ROADTYPE_ROAD = 0, ///< Basic road type
+ ROADTYPE_TRAM = 1, ///< Trams
+ ROADTYPE_END = 63, ///< Used for iterations
+ INVALID_ROADTYPE = 63, ///< flag for invalid roadtype
};
DECLARE_POSTFIX_INCREMENT(RoadType)
-template <> struct EnumPropsT<RoadType> : MakeEnumPropsT<RoadType, byte, ROADTYPE_BEGIN, ROADTYPE_END, INVALID_ROADTYPE, 2> {};
+template <> struct EnumPropsT<RoadType> : MakeEnumPropsT<RoadType, byte, ROADTYPE_BEGIN, ROADTYPE_END, INVALID_ROADTYPE, 6> {};
/**
- * The different roadtypes we support, but then a bitmask of them
- * @note currently only roadtypes with ROADTYPE_ROAD and ROADTYPE_TRAM are supported.
+ * The different roadtypes we support, but then a bitmask of them.
+ * @note Must be treated as a uint64 type, narrowing it causes bit membership tests to give wrong results.
*/
-enum RoadTypes : byte {
+enum RoadTypes : uint64 {
ROADTYPES_NONE = 0, ///< No roadtypes
ROADTYPES_ROAD = 1 << ROADTYPE_ROAD, ///< Road
ROADTYPES_TRAM = 1 << ROADTYPE_TRAM, ///< Trams
- ROADTYPES_ALL = ROADTYPES_ROAD | ROADTYPES_TRAM, ///< Road + trams
- ROADTYPES_END, ///< Used for iterations?
- INVALID_ROADTYPES = 0xFF, ///< Invalid roadtypes
+ INVALID_ROADTYPES = UINT64_MAX, ///< Invalid roadtypes
};
DECLARE_ENUM_AS_BIT_SET(RoadTypes)
-template <> struct EnumPropsT<RoadTypes> : MakeEnumPropsT<RoadTypes, byte, ROADTYPES_NONE, ROADTYPES_END, INVALID_ROADTYPES, 2> {};
-
/**
* Enumeration for the road parts on a tile.
diff --git a/src/roadstop.cpp b/src/roadstop.cpp
index acefa8362..694e33f0d 100644
--- a/src/roadstop.cpp
+++ b/src/roadstop.cpp
@@ -45,7 +45,7 @@ RoadStop *RoadStop::GetNextRoadStop(const RoadVehicle *v) const
{
for (RoadStop *rs = this->next; rs != nullptr; rs = rs->next) {
/* The vehicle cannot go to this roadstop (different roadtype) */
- if ((GetRoadTypes(rs->xy) & v->compatible_roadtypes) == ROADTYPES_NONE) continue;
+ if (!HasTileAnyRoadType(rs->xy, v->compatible_roadtypes)) continue;
/* The vehicle is articulated and can therefore not go to a standard road stop. */
if (IsStandardRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue;
diff --git a/src/roadveh.h b/src/roadveh.h
index 7c8f32d0f..02eb48c8c 100644
--- a/src/roadveh.h
+++ b/src/roadveh.h
@@ -16,7 +16,8 @@
#include "engine_base.h"
#include "cargotype.h"
#include "track_func.h"
-#include "road_type.h"
+#include "road.h"
+#include "road_map.h"
#include "newgrf_engine.h"
#include <deque>
@@ -115,8 +116,8 @@ struct RoadVehicle FINAL : public GroundVehicle<RoadVehicle, VEH_ROAD> {
uint16 crashed_ctr; ///< Animation counter when the vehicle has crashed. @see RoadVehIsCrashed
byte reverse_ctr;
- RoadType roadtype;
- RoadTypes compatible_roadtypes;
+ RoadType roadtype; //!< Roadtype of this vehicle.
+ RoadTypes compatible_roadtypes; //!< Roadtypes this consist is powered on.
/** We don't want GCC to zero our struct! It already is zeroed and has an index! */
RoadVehicle() : GroundVehicleBase() {}
@@ -244,7 +245,7 @@ protected: // These functions should not be called outside acceleration code.
{
/* Trams have a slightly greater friction coefficient than trains.
* The rest of road vehicles have bigger values. */
- uint32 coeff = (this->roadtype == ROADTYPE_TRAM) ? 40 : 75;
+ uint32 coeff = RoadTypeIsTram(this->roadtype) ? 40 : 75;
/* The friction coefficient increases with speed in a way that
* it doubles at 128 km/h, triples at 256 km/h and so on. */
return coeff * (128 + this->GetCurrentSpeed()) / 128;
@@ -274,7 +275,7 @@ protected: // These functions should not be called outside acceleration code.
*/
inline uint16 GetMaxTrackSpeed() const
{
- return 0;
+ return GetRoadTypeInfo(GetRoadType(this->tile, GetRoadTramType(this->roadtype)))->max_speed;
}
/**
@@ -283,7 +284,7 @@ protected: // These functions should not be called outside acceleration code.
*/
inline bool TileMayHaveSlopedTrack() const
{
- TrackStatus ts = GetTileTrackStatus(this->tile, TRANSPORT_ROAD, this->compatible_roadtypes);
+ TrackStatus ts = GetTileTrackStatus(this->tile, TRANSPORT_ROAD, GetRoadTramType(this->roadtype));
TrackBits trackbits = TrackStatusToTrackBits(ts);
return trackbits == TRACK_BIT_X || trackbits == TRACK_BIT_Y;
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 9f98c3e88..d323bc1ec 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -260,7 +260,10 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
*/
CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
{
- if (HasTileRoadType(tile, ROADTYPE_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
+ /* Check that the vehicle can drive on the road in question */
+ RoadType rt = e->u.road.roadtype;
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+ if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
if (flags & DC_EXEC) {
const RoadVehicleInfo *rvi = &e->u.road;
@@ -304,8 +307,8 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin
v->random_bits = VehicleRandomBits();
v->SetFrontEngine();
- v->roadtype = HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
- v->compatible_roadtypes = RoadTypeToRoadTypes(v->roadtype);
+ v->roadtype = rt;
+ v->compatible_roadtypes = rti->powered_roadtypes;
v->gcache.cached_veh_length = VEHICLE_LENGTH;
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
@@ -437,16 +440,16 @@ void RoadVehicle::UpdateDeltaXY()
*/
inline int RoadVehicle::GetCurrentMaxSpeed() const
{
- int max_speed = this->vcache.cached_max_speed;
+ int max_speed = this->gcache.cached_max_track_speed;
/* Limit speed to 50% while reversing, 75% in curves. */
for (const RoadVehicle *u = this; u != nullptr; u = u->Next()) {
if (_settings_game.vehicle.roadveh_acceleration_model == AM_REALISTIC) {
if (this->state <= RVSB_TRACKDIR_MASK && IsReversingRoadTrackdir((Trackdir)this->state)) {
- max_speed = this->vcache.cached_max_speed / 2;
+ max_speed = this->gcache.cached_max_track_speed / 2;
break;
} else if ((u->direction & 1) == 0) {
- max_speed = this->vcache.cached_max_speed * 3 / 4;
+ max_speed = this->gcache.cached_max_track_speed * 3 / 4;
}
}
@@ -691,7 +694,7 @@ static void RoadVehArrivesAt(const RoadVehicle *v, Station *st)
st->had_vehicle_of_type |= HVOT_BUS;
SetDParam(0, st->index);
AddVehicleNewsItem(
- v->roadtype == ROADTYPE_ROAD ? STR_NEWS_FIRST_BUS_ARRIVAL : STR_NEWS_FIRST_PASSENGER_TRAM_ARRIVAL,
+ RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_BUS_ARRIVAL : STR_NEWS_FIRST_PASSENGER_TRAM_ARRIVAL,
(v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
v->index,
st->index
@@ -705,7 +708,7 @@ static void RoadVehArrivesAt(const RoadVehicle *v, Station *st)
st->had_vehicle_of_type |= HVOT_TRUCK;
SetDParam(0, st->index);
AddVehicleNewsItem(
- v->roadtype == ROADTYPE_ROAD ? STR_NEWS_FIRST_TRUCK_ARRIVAL : STR_NEWS_FIRST_CARGO_TRAM_ARRIVAL,
+ RoadTypeIsRoad(v->roadtype) ? STR_NEWS_FIRST_TRUCK_ARRIVAL : STR_NEWS_FIRST_CARGO_TRAM_ARRIVAL,
(v->owner == _local_company) ? NT_ARRIVAL_COMPANY : NT_ARRIVAL_OTHER,
v->index,
st->index
@@ -783,7 +786,8 @@ static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data)
*/
static bool CheckRoadBlockedForOvertaking(OvertakeData *od)
{
- TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, od->v->compatible_roadtypes);
+ if (!HasTileAnyRoadType(od->tile, od->v->compatible_roadtypes)) return true;
+ TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, GetRoadTramType(od->v->roadtype));
TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts);
TrackdirBits red_signals = TrackStatusToRedSignals(ts); // barred level crossing
TrackBits trackbits = TrackdirBitsToTrackBits(trackdirbits);
@@ -803,7 +807,7 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u)
od.u = u;
/* Trams can't overtake other trams */
- if (v->roadtype == ROADTYPE_TRAM) return;
+ if (RoadTypeIsTram(v->roadtype)) return;
/* Don't overtake in stations */
if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return;
@@ -855,7 +859,7 @@ static void RoadZPosAffectSpeed(RoadVehicle *v, int old_z)
v->cur_speed = v->cur_speed * 232 / 256; // slow down by ~10%
} else {
uint16 spd = v->cur_speed + 2;
- if (spd <= v->vcache.cached_max_speed) v->cur_speed = spd;
+ if (spd <= v->gcache.cached_max_track_speed) v->cur_speed = spd;
}
}
@@ -884,12 +888,12 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
Trackdir best_track;
bool path_found = true;
- TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes);
+ TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype));
TrackdirBits red_signals = TrackStatusToRedSignals(ts); // crossing
TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts);
if (IsTileType(tile, MP_ROAD)) {
- if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->compatible_roadtypes) == 0)) {
+ if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir)) {
/* Road depot owned by another company or with the wrong orientation */
trackdirs = TRACKDIR_BIT_NONE;
}
@@ -932,10 +936,10 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
if (v->reverse_ctr != 0) {
bool reverse = true;
- if (v->roadtype == ROADTYPE_TRAM) {
+ if (RoadTypeIsTram(v->roadtype)) {
/* Trams may only reverse on a tile if it contains at least the straight
* trackbits or when it is a valid turning tile (i.e. one roadbit) */
- RoadBits rb = GetAnyRoadBits(tile, ROADTYPE_TRAM);
+ RoadBits rb = GetAnyRoadBits(tile, RTT_TRAM);
RoadBits straight = AxisToRoadBits(DiagDirToAxis(enterdir));
reverse = ((rb & straight) == straight) ||
(rb == DiagDirToRoadBits(enterdir));
@@ -1014,7 +1018,7 @@ static bool RoadVehLeaveDepot(RoadVehicle *v, bool first)
v->direction = DiagDirToDir(dir);
Trackdir tdir = DiagDirToDiagTrackdir(dir);
- const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir];
+ const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir];
int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF);
int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF);
@@ -1109,7 +1113,7 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl
};
RoadBits required = required_roadbits[dir & 0x07];
- if ((required & GetAnyRoadBits(tile, v->roadtype, true)) == ROAD_NONE) {
+ if ((required & GetAnyRoadBits(tile, GetRoadTramType(v->roadtype), true)) == ROAD_NONE) {
dir = INVALID_TRACKDIR;
}
@@ -1120,15 +1124,16 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl
* Can a tram track build without destruction on the given tile?
* @param c the company that would be building the tram tracks
* @param t the tile to build on.
+ * @param rt the tram type to build.
* @param r the road bits needed.
* @return true when a track track can be build on 't'
*/
-static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadBits r)
+static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadType rt, RoadBits r)
{
/* The 'current' company is not necessarily the owner of the vehicle. */
Backup<CompanyID> cur_company(_current_company, c, FILE_LINE);
- CommandCost ret = DoCommand(t, ROADTYPE_TRAM << 4 | r, 0, DC_NO_WATER, CMD_BUILD_ROAD);
+ CommandCost ret = DoCommand(t, rt << 4 | r, 0, DC_NO_WATER, CMD_BUILD_ROAD);
cur_company.Restore();
return ret.Succeeded();
@@ -1186,7 +1191,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
/* Get move position data for next frame.
* For a drive-through road stop use 'straight road' move data.
* In this case v->state is masked to give the road stop entry direction. */
- RoadDriveEntry rd = _road_drive_data[v->roadtype][(
+ RoadDriveEntry rd = _road_drive_data[GetRoadTramType(v->roadtype)][(
(HasBit(v->state, RVS_IN_DT_ROAD_STOP) ? v->state & RVSB_ROAD_STOP_TRACKDIR_MASK : v->state) +
(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1];
@@ -1196,7 +1201,11 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev)
if (v->IsFrontEngine()) {
/* If this is the front engine, look for the right path. */
- dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3));
+ if (HasTileAnyRoadType(tile, v->compatible_roadtypes)) {
+ dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3));
+ } else {
+ dir = _road_reverse_table[(DiagDirection)(rd.x & 3)];
+ }
} else {
dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false);
}
@@ -1214,7 +1223,7 @@ again:
v->overtaking = 0;
/* Turning around */
- if (v->roadtype == ROADTYPE_TRAM) {
+ if (RoadTypeIsTram(v->roadtype)) {
/* Determine the road bits the tram needs to be able to turn around
* using the 'big' corner loop. */
RoadBits needed;
@@ -1227,7 +1236,8 @@ again:
}
if ((v->Previous() != nullptr && v->Previous()->tile == tile) ||
(v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) &&
- (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE)) {
+ HasTileAnyRoadType(tile, v->compatible_roadtypes) &&
+ (needed & GetRoadBits(tile, RTT_TRAM)) != ROAD_NONE)) {
/*
* Taking the 'big' corner for trams only happens when:
* - The previous vehicle in this (articulated) tram chain is
@@ -1238,7 +1248,7 @@ again:
* going to cause the tram to split up.
* - Or the front of the tram can drive over the next tile.
*/
- } else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, needed) || ((~needed & GetAnyRoadBits(v->tile, ROADTYPE_TRAM, false)) == ROAD_NONE)) {
+ } else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, v->roadtype, needed) || ((~needed & GetAnyRoadBits(v->tile, RTT_TRAM, false)) == ROAD_NONE)) {
/*
* Taking the 'small' corner for trams only happens when:
* - We are not the from vehicle of an articulated tram.
@@ -1266,7 +1276,7 @@ again:
}
/* Get position data for first frame on the new tile */
- const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(dir + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking];
+ const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(dir + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking];
int x = TileX(tile) * TILE_SIZE + rdp[start_frame].x;
int y = TileY(tile) * TILE_SIZE + rdp[start_frame].y;
@@ -1318,9 +1328,18 @@ again:
}
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
+ TileIndex old_tile = v->tile;
+
v->tile = tile;
v->state = (byte)dir;
v->frame = start_frame;
+ RoadTramType rtt = GetRoadTramType(v->roadtype);
+ if (GetRoadType(old_tile, rtt) != GetRoadType(tile, rtt)) {
+ if (v->IsFrontEngine()) {
+ RoadVehUpdateCache(v);
+ }
+ v->First()->CargoChanged();
+ }
}
if (new_dir != v->direction) {
v->direction = new_dir;
@@ -1338,7 +1357,7 @@ again:
Trackdir dir;
uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME;
- if (v->roadtype == ROADTYPE_TRAM && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, ROADTYPE_TRAM, true))) {
+ if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) {
/*
* The tram is turning around with one tram 'roadbit'. This means that
* it is using the 'big' corner 'drive data'. However, to support the
@@ -1370,7 +1389,7 @@ again:
return false;
}
- const RoadDriveEntry *rdp = _road_drive_data[v->roadtype][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + dir];
+ const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + dir];
int x = TileX(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].x;
int y = TileY(v->tile) * TILE_SIZE + rdp[turn_around_start_frame].y;
@@ -1478,7 +1497,7 @@ again:
TileIndex next_tile = TileAddByDir(v->tile, v->direction);
/* Check if next inline bay is free and has compatible road. */
- if (RoadStop::IsDriveThroughRoadStopContinuation(v->tile, next_tile) && (GetRoadTypes(next_tile) & v->compatible_roadtypes) != 0) {
+ if (RoadStop::IsDriveThroughRoadStopContinuation(v->tile, next_tile) && HasTileAnyRoadType(next_tile, v->compatible_roadtypes)) {
v->frame++;
v->x_pos = x;
v->y_pos = y;
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index 6b273e026..662f5a77b 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -282,6 +282,12 @@ static void InitializeWindowsAndCaches()
(*it)->tile = t->xy;
}
}
+ RoadVehicle *rv;
+ FOR_ALL_ROADVEHICLES(rv) {
+ if (rv->IsFrontEngine()) {
+ rv->CargoChanged();
+ }
+ }
RecomputePrices();
@@ -461,8 +467,19 @@ static void FixOwnerOfRailTrack(TileIndex t)
if (IsLevelCrossingTile(t)) {
/* else change the crossing to normal road (road vehicles won't care) */
- MakeRoadNormal(t, GetCrossingRoadBits(t), GetRoadTypes(t), GetTownIndex(t),
- GetRoadOwner(t, ROADTYPE_ROAD), GetRoadOwner(t, ROADTYPE_TRAM));
+ Owner road = GetRoadOwner(t, RTT_ROAD);
+ Owner tram = GetRoadOwner(t, RTT_TRAM);
+ RoadBits bits = GetCrossingRoadBits(t);
+ bool hasroad = HasBit(_me[t].m7, 6);
+ bool hastram = HasBit(_me[t].m7, 7);
+
+ /* MakeRoadNormal */
+ SetTileType(t, MP_ROAD);
+ SetTileOwner(t, road);
+ _m[t].m3 = (hasroad ? bits : 0);
+ _m[t].m5 = (hastram ? bits : 0) | ROAD_TILE_NORMAL << 6;
+ SB(_me[t].m6, 2, 4, 0);
+ SetRoadOwner(t, RTT_TRAM, tram);
return;
}
@@ -1052,18 +1069,18 @@ bool AfterLoadGame()
break;
case ROAD_TILE_DEPOT: break;
}
- SetRoadTypes(t, ROADTYPES_ROAD);
+ SB(_me[t].m7, 6, 2, 1); // Set pre-NRT road type bits for conversion later.
break;
case MP_STATION:
- if (IsRoadStop(t)) SetRoadTypes(t, ROADTYPES_ROAD);
+ if (IsRoadStop(t)) SB(_me[t].m7, 6, 2, 1);
break;
case MP_TUNNELBRIDGE:
/* Middle part of "old" bridges */
if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break;
if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) {
- SetRoadTypes(t, ROADTYPES_ROAD);
+ SB(_me[t].m7, 6, 2, 1); // Set pre-NRT road type bits for conversion later.
}
break;
@@ -1079,7 +1096,7 @@ bool AfterLoadGame()
for (TileIndex t = 0; t < map_size; t++) {
switch (GetTileType(t)) {
case MP_ROAD:
- if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_me[t].m7, 5, 3));
+ if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_me[t].m7, 5, 3));
SB(_me[t].m7, 5, 1, GB(_m[t].m3, 7, 1)); // snow/desert
switch (GetRoadTileType(t)) {
default: SlErrorCorrupt("Invalid road tile type");
@@ -1112,7 +1129,7 @@ bool AfterLoadGame()
case MP_STATION:
if (!IsRoadStop(t)) break;
- if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_m[t].m3, 0, 3));
+ if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_m[t].m3, 0, 3));
SB(_me[t].m7, 0, 5, HasBit(_me[t].m6, 2) ? OWNER_TOWN : GetTileOwner(t));
SB(_m[t].m3, 4, 4, _m[t].m1);
_m[t].m4 = 0;
@@ -1121,7 +1138,7 @@ bool AfterLoadGame()
case MP_TUNNELBRIDGE:
if (old_bridge && IsBridge(t) && HasBit(_m[t].m5, 6)) break;
if (((old_bridge && IsBridge(t)) ? (TransportType)GB(_m[t].m5, 1, 2) : GetTunnelBridgeTransportType(t)) == TRANSPORT_ROAD) {
- if (fix_roadtypes) SetRoadTypes(t, (RoadTypes)GB(_m[t].m3, 0, 3));
+ if (fix_roadtypes) SB(_me[t].m7, 6, 2, (RoadTypes)GB(_m[t].m3, 0, 3));
Owner o = GetTileOwner(t);
SB(_me[t].m7, 0, 5, o); // road owner
@@ -1191,13 +1208,14 @@ bool AfterLoadGame()
} else {
TownID town = IsTileOwner(t, OWNER_TOWN) ? ClosestTownFromTile(t, UINT_MAX)->index : 0;
- MakeRoadNormal(
- t,
- axis == AXIS_X ? ROAD_Y : ROAD_X,
- ROADTYPES_ROAD,
- town,
- GetTileOwner(t), OWNER_NONE
- );
+ /* MakeRoadNormal */
+ SetTileType(t, MP_ROAD);
+ _m[t].m2 = town;
+ _m[t].m3 = 0;
+ _m[t].m5 = (axis == AXIS_X ? ROAD_Y : ROAD_X) | ROAD_TILE_NORMAL << 6;
+ SB(_me[t].m6, 2, 4, 0);
+ _me[t].m7 = 1 << 6;
+ SetRoadOwner(t, RTT_TRAM, OWNER_NONE);
}
} else {
if (GB(_m[t].m5, 3, 2) == 0) {
@@ -1252,6 +1270,35 @@ bool AfterLoadGame()
}
}
+ if (IsSavegameVersionBefore(SLV_ROAD_TYPES)) {
+ /* Add road subtypes */
+ for (TileIndex t = 0; t < map_size; t++) {
+ bool has_road = false;
+ switch (GetTileType(t)) {
+ case MP_ROAD:
+ has_road = true;
+ break;
+ case MP_STATION:
+ has_road = IsRoadStop(t);
+ break;
+ case MP_TUNNELBRIDGE:
+ has_road = GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD;
+ break;
+ default:
+ break;
+ }
+
+ if (has_road) {
+ RoadType road_rt = HasBit(_me[t].m7, 6) ? ROADTYPE_ROAD : INVALID_ROADTYPE;
+ RoadType tram_rt = HasBit(_me[t].m7, 7) ? ROADTYPE_TRAM : INVALID_ROADTYPE;
+
+ assert(road_rt != INVALID_ROADTYPE || tram_rt != INVALID_ROADTYPE);
+ SetRoadTypes(t, road_rt, tram_rt);
+ SB(_me[t].m7, 6, 2, 0); // Clear pre-NRT road type bits.
+ }
+ }
+ }
+
/* Elrails got added in rev 24 */
if (IsSavegameVersionBefore(SLV_24)) {
RailType min_rail = RAILTYPE_ELECTRIC;
@@ -1375,7 +1422,7 @@ bool AfterLoadGame()
Company *c;
FOR_ALL_COMPANIES(c) {
c->avail_railtypes = GetCompanyRailtypes(c->index);
- c->avail_roadtypes = GetCompanyRoadtypes(c->index);
+ c->avail_roadtypes = GetCompanyRoadTypes(c->index);
}
if (!IsSavegameVersionBefore(SLV_27)) AfterLoadStations();
@@ -1846,10 +1893,10 @@ bool AfterLoadGame()
}
} else if (IsTileType(t, MP_ROAD)) {
/* works for all RoadTileType */
- for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
+ FOR_ALL_ROADTRAMTYPES(rtt) {
/* update even non-existing road types to update tile owner too */
- Owner o = GetRoadOwner(t, rt);
- if (o < MAX_COMPANIES && !Company::IsValidID(o)) SetRoadOwner(t, rt, OWNER_NONE);
+ Owner o = GetRoadOwner(t, rtt);
+ if (o < MAX_COMPANIES && !Company::IsValidID(o)) SetRoadOwner(t, rtt, OWNER_NONE);
}
if (IsLevelCrossing(t)) {
if (!Company::IsValidID(GetTileOwner(t))) FixOwnerOfRailTrack(t);
@@ -2662,7 +2709,7 @@ bool AfterLoadGame()
if (rv->state == RVSB_IN_DEPOT || rv->state == RVSB_WORMHOLE) break;
- TrackStatus ts = GetTileTrackStatus(rv->tile, TRANSPORT_ROAD, rv->compatible_roadtypes);
+ TrackStatus ts = GetTileTrackStatus(rv->tile, TRANSPORT_ROAD, GetRoadTramType(rv->roadtype));
TrackBits trackbits = TrackStatusToTrackBits(ts);
/* Only X/Y tracks can be sloped. */
@@ -2871,8 +2918,8 @@ bool AfterLoadGame()
for (TileIndex t = 0; t < map_size; t++) {
if (!IsStandardRoadStopTile(t)) continue;
Owner o = GetTileOwner(t);
- SetRoadOwner(t, ROADTYPE_ROAD, o);
- SetRoadOwner(t, ROADTYPE_TRAM, o);
+ SetRoadOwner(t, RTT_ROAD, o);
+ SetRoadOwner(t, RTT_TRAM, o);
}
}
diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp
index 6c88db727..f934848e9 100644
--- a/src/saveload/company_sl.cpp
+++ b/src/saveload/company_sl.cpp
@@ -131,11 +131,12 @@ void AfterLoadCompanyStats()
}
/* Iterate all present road types as each can have a different owner. */
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rt));
+ FOR_ALL_ROADTRAMTYPES(rtt) {
+ RoadType rt = GetRoadType(tile, rtt);
+ if (rt == INVALID_ROADTYPE) continue;
+ c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt));
/* A level crossings and depots have two road bits. */
- if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rt)) : 2;
+ if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2;
}
break;
}
@@ -153,9 +154,10 @@ void AfterLoadCompanyStats()
case STATION_BUS:
case STATION_TRUCK: {
/* Iterate all present road types as each can have a different owner. */
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- c = Company::GetIfValid(GetRoadOwner(tile, rt));
+ FOR_ALL_ROADTRAMTYPES(rtt) {
+ RoadType rt = GetRoadType(tile, rtt);
+ if (rt == INVALID_ROADTYPE) continue;
+ c = Company::GetIfValid(GetRoadOwner(tile, rtt));
if (c != nullptr) c->infrastructure.road[rt] += 2; // A road stop has two road bits.
}
break;
@@ -210,9 +212,10 @@ void AfterLoadCompanyStats()
case TRANSPORT_ROAD: {
/* Iterate all present road types as each can have a different owner. */
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- c = Company::GetIfValid(GetRoadOwner(tile, rt));
+ FOR_ALL_ROADTRAMTYPES(rtt) {
+ RoadType rt = GetRoadType(tile, rtt);
+ if (rt == INVALID_ROADTYPE) continue;
+ c = Company::GetIfValid(GetRoadOwner(tile, rtt));
if (c != nullptr) c->infrastructure.road[rt] += len * 2; // A full diagonal road has two road bits.
}
break;
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 966de0c8a..7ac602a54 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -298,6 +298,7 @@ enum SaveLoadVersion : uint16 {
SLV_ROADVEH_PATH_CACHE, ///< 211 PR#7261 Add path cache for road vehicles.
SLV_REMOVE_OPF, ///< 212 PR#7245 Remove OPF.
SLV_TREES_WATER_CLASS, ///< 213 PR#7405 WaterClass update for tree tiles.
+ SLV_ROAD_TYPES, ///< 214 PR#6811 NewGRF road types.
SL_MAX_VERSION, ///< Highest possible saveload version
};
diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp
index c42e3938f..b08c1cb5e 100644
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -410,6 +410,14 @@ void AfterLoadVehicles(bool part_of_load)
RoadVehicle *rv = RoadVehicle::From(v);
if (rv->IsFrontEngine()) {
rv->gcache.last_speed = rv->cur_speed; // update displayed road vehicle speed
+
+ rv->roadtype = Engine::Get(rv->engine_type)->u.road.roadtype;
+ rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes;
+ for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) {
+ u->roadtype = rv->roadtype;
+ u->compatible_roadtypes = rv->compatible_roadtypes;
+ }
+
RoadVehUpdateCache(rv);
if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) {
rv->CargoChanged();
@@ -448,13 +456,7 @@ void AfterLoadVehicles(bool part_of_load)
FOR_ALL_VEHICLES(v) {
switch (v->type) {
- case VEH_ROAD: {
- RoadVehicle *rv = RoadVehicle::From(v);
- rv->roadtype = HasBit(EngInfo(v->First()->engine_type)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
- rv->compatible_roadtypes = RoadTypeToRoadTypes(rv->roadtype);
- FALLTHROUGH;
- }
-
+ case VEH_ROAD:
case VEH_TRAIN:
case VEH_SHIP:
v->GetImage(v->direction, EIT_ON_MAP, &v->sprite_seq);
diff --git a/src/script/api/ai/ai_rail.hpp.sq b/src/script/api/ai/ai_rail.hpp.sq
index ba9d7e656..d7e421669 100644
--- a/src/script/api/ai/ai_rail.hpp.sq
+++ b/src/script/api/ai/ai_rail.hpp.sq
@@ -56,7 +56,7 @@ void SQAIRail_Register(Squirrel *engine)
ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, ScriptRail::ERR_UNSUITABLE_TRACK);
ScriptError::RegisterErrorMap(STR_ERROR_THERE_ARE_NO_SIGNALS, ScriptRail::ERR_UNSUITABLE_TRACK);
ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_STATION, ScriptRail::ERR_UNSUITABLE_TRACK);
- ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING);
+ ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_RAIL, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING);
ScriptError::RegisterErrorMapString(ScriptRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD");
ScriptError::RegisterErrorMapString(ScriptRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK");
diff --git a/src/script/api/ai/ai_road.hpp.sq b/src/script/api/ai/ai_road.hpp.sq
index 3da4607c9..350fcdb7f 100644
--- a/src/script/api/ai/ai_road.hpp.sq
+++ b/src/script/api/ai/ai_road.hpp.sq
@@ -26,6 +26,8 @@ void SQAIRoad_Register(Squirrel *engine)
SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION");
SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD");
SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS");
+ SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING");
+ SQAIRoad.DefSQConst(engine, ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD");
SQAIRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_ROAD, "ROADTYPE_ROAD");
SQAIRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_TRAM, "ROADTYPE_TRAM");
SQAIRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_INVALID, "ROADTYPE_INVALID");
@@ -40,12 +42,19 @@ void SQAIRoad_Register(Squirrel *engine)
ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_DIRECTION, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION);
ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD);
ScriptError::RegisterErrorMap(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS);
+ ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_ROAD, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING);
+ ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD);
+ ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_TRAMWAY, ScriptRoad::ERR_UNSUITABLE_ROAD);
+ ScriptError::RegisterErrorMap(STR_ERROR_INCOMPATIBLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD);
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS");
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION");
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD");
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS");
+ ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING");
+ ScriptError::RegisterErrorMapString(ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD");
+ SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetName, "GetName", 2, ".i");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetRoadVehicleTypeForCargo, "GetRoadVehicleTypeForCargo", 2, ".i");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTile, "IsRoadTile", 2, ".i");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadDepotTile, "IsRoadDepotTile", 2, ".i");
@@ -54,6 +63,9 @@ void SQAIRoad_Register(Squirrel *engine)
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTypeAvailable, "IsRoadTypeAvailable", 2, ".i");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetCurrentRoadType, "GetCurrentRoadType", 1, ".");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::SetCurrentRoadType, "SetCurrentRoadType", 2, ".i");
+ SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehCanRunOnRoad, "RoadVehCanRunOnRoad", 3, ".ii");
+ SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehHasPowerOnRoad, "RoadVehHasPowerOnRoad", 3, ".ii");
+ SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::ConvertRoadType, "ConvertRoadType", 4, ".iii");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::HasRoadType, "HasRoadType", 3, ".ii");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::AreRoadTilesConnected, "AreRoadTilesConnected", 3, ".ii");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::CanBuildConnectedRoadParts, "CanBuildConnectedRoadParts", 5, ".iaii");
@@ -74,6 +86,7 @@ void SQAIRoad_Register(Squirrel *engine)
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadDepot, "RemoveRoadDepot", 2, ".i");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadStation, "RemoveRoadStation", 2, ".i");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetBuildCost, "GetBuildCost", 3, ".ii");
+ SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaxSpeed, "GetMaxSpeed", 2, ".i");
SQAIRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaintenanceCostFactor, "GetMaintenanceCostFactor", 2, ".i");
SQAIRoad.PostRegister(engine);
diff --git a/src/script/api/ai_changelog.hpp b/src/script/api/ai_changelog.hpp
index c6a790f86..89922d4b2 100644
--- a/src/script/api/ai_changelog.hpp
+++ b/src/script/api/ai_changelog.hpp
@@ -26,6 +26,11 @@
* \li AIGroup::GetSecondaryColour
* \li AIVehicle::BuildVehicleWithRefit
* \li AIVehicle::GetBuildWithRefitCapacity
+ * \li AIRoad::GetName
+ * \li AIRoad::RoadVehCanRunOnRoad
+ * \li AIRoad::RoadVehHasPowerOnRoad
+ * \li AIRoad::ConvertRoadType
+ * \li AIRoad::GetMaxSpeed
*
* \b 1.9.0
*
@@ -46,6 +51,9 @@
*
* No changes
*
+ * API additions:
+ * \li AIRoad::ERR_ROADTYPE_DISALLOWS_CROSSING
+ *
* \b 1.7.0 - 1.7.2
*
* No changes
diff --git a/src/script/api/game/game_rail.hpp.sq b/src/script/api/game/game_rail.hpp.sq
index c05736030..f145e8481 100644
--- a/src/script/api/game/game_rail.hpp.sq
+++ b/src/script/api/game/game_rail.hpp.sq
@@ -56,7 +56,7 @@ void SQGSRail_Register(Squirrel *engine)
ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, ScriptRail::ERR_UNSUITABLE_TRACK);
ScriptError::RegisterErrorMap(STR_ERROR_THERE_ARE_NO_SIGNALS, ScriptRail::ERR_UNSUITABLE_TRACK);
ScriptError::RegisterErrorMap(STR_ERROR_THERE_IS_NO_STATION, ScriptRail::ERR_UNSUITABLE_TRACK);
- ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING);
+ ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_RAIL, ScriptRail::ERR_RAILTYPE_DISALLOWS_CROSSING);
ScriptError::RegisterErrorMapString(ScriptRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD");
ScriptError::RegisterErrorMapString(ScriptRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK");
diff --git a/src/script/api/game/game_road.hpp.sq b/src/script/api/game/game_road.hpp.sq
index 51da4144e..650d31504 100644
--- a/src/script/api/game/game_road.hpp.sq
+++ b/src/script/api/game/game_road.hpp.sq
@@ -26,6 +26,8 @@ void SQGSRoad_Register(Squirrel *engine)
SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION");
SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD");
SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS");
+ SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING");
+ SQGSRoad.DefSQConst(engine, ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD");
SQGSRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_ROAD, "ROADTYPE_ROAD");
SQGSRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_TRAM, "ROADTYPE_TRAM");
SQGSRoad.DefSQConst(engine, ScriptRoad::ROADTYPE_INVALID, "ROADTYPE_INVALID");
@@ -40,12 +42,19 @@ void SQGSRoad_Register(Squirrel *engine)
ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_DIRECTION, ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION);
ScriptError::RegisterErrorMap(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD, ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD);
ScriptError::RegisterErrorMap(STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION, ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS);
+ ScriptError::RegisterErrorMap(STR_ERROR_CROSSING_DISALLOWED_ROAD, ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING);
+ ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD);
+ ScriptError::RegisterErrorMap(STR_ERROR_NO_SUITABLE_TRAMWAY, ScriptRoad::ERR_UNSUITABLE_ROAD);
+ ScriptError::RegisterErrorMap(STR_ERROR_INCOMPATIBLE_ROAD, ScriptRoad::ERR_UNSUITABLE_ROAD);
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS");
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION");
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD");
ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS");
+ ScriptError::RegisterErrorMapString(ScriptRoad::ERR_ROADTYPE_DISALLOWS_CROSSING, "ERR_ROADTYPE_DISALLOWS_CROSSING");
+ ScriptError::RegisterErrorMapString(ScriptRoad::ERR_UNSUITABLE_ROAD, "ERR_UNSUITABLE_ROAD");
+ SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetName, "GetName", 2, ".i");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetRoadVehicleTypeForCargo, "GetRoadVehicleTypeForCargo", 2, ".i");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTile, "IsRoadTile", 2, ".i");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadDepotTile, "IsRoadDepotTile", 2, ".i");
@@ -54,6 +63,9 @@ void SQGSRoad_Register(Squirrel *engine)
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::IsRoadTypeAvailable, "IsRoadTypeAvailable", 2, ".i");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetCurrentRoadType, "GetCurrentRoadType", 1, ".");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::SetCurrentRoadType, "SetCurrentRoadType", 2, ".i");
+ SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehCanRunOnRoad, "RoadVehCanRunOnRoad", 3, ".ii");
+ SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RoadVehHasPowerOnRoad, "RoadVehHasPowerOnRoad", 3, ".ii");
+ SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::ConvertRoadType, "ConvertRoadType", 4, ".iii");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::HasRoadType, "HasRoadType", 3, ".ii");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::AreRoadTilesConnected, "AreRoadTilesConnected", 3, ".ii");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::CanBuildConnectedRoadParts, "CanBuildConnectedRoadParts", 5, ".iaii");
@@ -74,6 +86,7 @@ void SQGSRoad_Register(Squirrel *engine)
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadDepot, "RemoveRoadDepot", 2, ".i");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::RemoveRoadStation, "RemoveRoadStation", 2, ".i");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetBuildCost, "GetBuildCost", 3, ".ii");
+ SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaxSpeed, "GetMaxSpeed", 2, ".i");
SQGSRoad.DefSQStaticMethod(engine, &ScriptRoad::GetMaintenanceCostFactor, "GetMaintenanceCostFactor", 2, ".i");
SQGSRoad.PostRegister(engine);
diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq
index 3d190f44f..7fa2f62af 100644
--- a/src/script/api/game/game_window.hpp.sq
+++ b/src/script/api/game/game_window.hpp.sq
@@ -227,8 +227,8 @@ void SQGSWindow_Register(Squirrel *engine)
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_START_REPLACE, "WID_RV_START_REPLACE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_INFO_TAB, "WID_RV_INFO_TAB");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_STOP_REPLACE, "WID_RV_STOP_REPLACE");
+ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_RAIL_ROAD_TYPE_DROPDOWN, "WID_RV_RAIL_ROAD_TYPE_DROPDOWN");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_TRAIN_ENGINEWAGON_DROPDOWN, "WID_RV_TRAIN_ENGINEWAGON_DROPDOWN");
- SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_TRAIN_RAILTYPE_DROPDOWN, "WID_RV_TRAIN_RAILTYPE_DROPDOWN");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RV_TRAIN_WAGONREMOVE_TOGGLE, "WID_RV_TRAIN_WAGONREMOVE_TOGGLE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BB_BACKGROUND, "WID_BB_BACKGROUND");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BAFD_QUESTION, "WID_BAFD_QUESTION");
@@ -385,6 +385,8 @@ void SQGSWindow_Register(Squirrel *engine)
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_RAIL_COUNT, "WID_CI_RAIL_COUNT");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_ROAD_DESC, "WID_CI_ROAD_DESC");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_ROAD_COUNT, "WID_CI_ROAD_COUNT");
+ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_TRAM_DESC, "WID_CI_TRAM_DESC");
+ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_TRAM_COUNT, "WID_CI_TRAM_COUNT");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_WATER_DESC, "WID_CI_WATER_DESC");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_WATER_COUNT, "WID_CI_WATER_COUNT");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_CI_STATION_DESC, "WID_CI_STATION_DESC");
@@ -996,6 +998,7 @@ void SQGSWindow_Register(Squirrel *engine)
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BRW_WAYPOINT_MATRIX, "WID_BRW_WAYPOINT_MATRIX");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BRW_WAYPOINT, "WID_BRW_WAYPOINT");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BRW_SCROLL, "WID_BRW_SCROLL");
+ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_CAPTION, "WID_ROT_CAPTION");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_ROAD_X, "WID_ROT_ROAD_X");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_ROAD_Y, "WID_ROT_ROAD_Y");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_AUTOROAD, "WID_ROT_AUTOROAD");
@@ -1007,6 +1010,7 @@ void SQGSWindow_Register(Squirrel *engine)
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_BUILD_BRIDGE, "WID_ROT_BUILD_BRIDGE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_BUILD_TUNNEL, "WID_ROT_BUILD_TUNNEL");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_REMOVE, "WID_ROT_REMOVE");
+ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ROT_CONVERT_ROAD, "WID_ROT_CONVERT_ROAD");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BROD_CAPTION, "WID_BROD_CAPTION");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BROD_DEPOT_NE, "WID_BROD_DEPOT_NE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_BROD_DEPOT_SE, "WID_BROD_DEPOT_SE");
@@ -1214,6 +1218,7 @@ void SQGSWindow_Register(Squirrel *engine)
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_BUILDING_TOOLS_START, "WID_TN_BUILDING_TOOLS_START");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_RAILS, "WID_TN_RAILS");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_ROADS, "WID_TN_ROADS");
+ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_TRAMS, "WID_TN_TRAMS");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_WATER, "WID_TN_WATER");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_AIR, "WID_TN_AIR");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_LANDSCAPE, "WID_TN_LANDSCAPE");
@@ -1237,6 +1242,7 @@ void SQGSWindow_Register(Squirrel *engine)
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_TOWN_GENERATE, "WID_TE_TOWN_GENERATE");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_INDUSTRY, "WID_TE_INDUSTRY");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_ROADS, "WID_TE_ROADS");
+ SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_TRAMS, "WID_TE_TRAMS");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_WATER, "WID_TE_WATER");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_TREES, "WID_TE_TREES");
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TE_SIGNS, "WID_TE_SIGNS");
diff --git a/src/script/api/game_changelog.hpp b/src/script/api/game_changelog.hpp
index 04bc893c1..3befcaf97 100644
--- a/src/script/api/game_changelog.hpp
+++ b/src/script/api/game_changelog.hpp
@@ -22,6 +22,11 @@
* API additions:
* \li GSVehicle::BuildVehicleWithRefit
* \li GSVehicle::GetBuildWithRefitCapacity
+ * \li GSRoad::GetName
+ * \li GSRoad::RoadVehCanRunOnRoad
+ * \li GSRoad::RoadVehHasPowerOnRoad
+ * \li GSRoad::ConvertRoadType
+ * \li GSRoad::GetMaxSpeed
*
* \b 1.9.0
*
@@ -42,6 +47,9 @@
*
* No changes
*
+ * API additions:
+ * \li GSRoad::ERR_ROADTYPE_DISALLOWS_CROSSING
+ *
* \b 1.7.0 - 1.7.2
*
* No changes
diff --git a/src/script/api/script_bridge.cpp b/src/script/api/script_bridge.cpp
index 03787be2a..d2d2a9c2d 100644
--- a/src/script/api/script_bridge.cpp
+++ b/src/script/api/script_bridge.cpp
@@ -83,7 +83,7 @@ static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance)
switch (vehicle_type) {
case ScriptVehicle::VT_ROAD:
type |= (TRANSPORT_ROAD << 15);
- type |= (::RoadTypeToRoadTypes((::RoadType)ScriptObject::GetRoadType()) << 8);
+ type |= (ScriptRoad::GetCurrentRoadType() << 8);
break;
case ScriptVehicle::VT_RAIL:
type |= (TRANSPORT_RAIL << 15);
@@ -114,7 +114,7 @@ static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance)
DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start);
DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
- return ScriptObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (ScriptObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, nullptr, &::_DoCommandReturnBuildBridge2);
+ return ScriptObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (ScriptRoad::GetCurrentRoadType() << 4), 0, CMD_BUILD_ROAD, nullptr, &::_DoCommandReturnBuildBridge2);
}
/* static */ bool ScriptBridge::_BuildBridgeRoad2()
@@ -126,7 +126,7 @@ static void _DoCommandReturnBuildBridge1(class ScriptInstance *instance)
DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start);
DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
- return ScriptObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (ScriptObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD);
+ return ScriptObject::DoCommand(end + ::TileOffsByDiagDir(dir_2), ::DiagDirToRoadBits(dir_1) | (ScriptRoad::GetCurrentRoadType() << 4), 0, CMD_BUILD_ROAD);
}
/* static */ bool ScriptBridge::RemoveBridge(TileIndex tile)
diff --git a/src/script/api/script_engine.cpp b/src/script/api/script_engine.cpp
index dfd15bee4..97f8c71df 100644
--- a/src/script/api/script_engine.cpp
+++ b/src/script/api/script_engine.cpp
@@ -224,7 +224,7 @@
if (!IsValidEngine(engine_id)) return ScriptRoad::ROADTYPE_INVALID;
if (GetVehicleType(engine_id) != ScriptVehicle::VT_ROAD) return ScriptRoad::ROADTYPE_INVALID;
- return HasBit(::EngInfo(engine_id)->misc_flags, EF_ROAD_TRAM) ? ScriptRoad::ROADTYPE_TRAM : ScriptRoad::ROADTYPE_ROAD;
+ return (ScriptRoad::RoadType)(uint)::RoadVehInfo(engine_id)->roadtype;
}
/* static */ ScriptRail::RailType ScriptEngine::GetRailType(EngineID engine_id)
diff --git a/src/script/api/script_infrastructure.cpp b/src/script/api/script_infrastructure.cpp
index d7da2747e..35febbd58 100644
--- a/src/script/api/script_infrastructure.cpp
+++ b/src/script/api/script_infrastructure.cpp
@@ -90,7 +90,8 @@
company = ScriptCompany::ResolveCompanyID(company);
if (company == ScriptCompany::COMPANY_INVALID || (::RoadType)roadtype >= ROADTYPE_END || !_settings_game.economy.infrastructure_maintenance) return 0;
- return ::RoadMaintenanceCost((::RoadType)roadtype, ::Company::Get((::CompanyID)company)->infrastructure.road[roadtype]);
+ const ::Company *c = ::Company::Get((::CompanyID)company);
+ return ::RoadMaintenanceCost((::RoadType)roadtype, c->infrastructure.road[roadtype], RoadTypeIsRoad((::RoadType)roadtype) ? c->infrastructure.GetRoadTotal() : c->infrastructure.GetTramTotal());
}
/* static */ Money ScriptInfrastructure::GetMonthlyInfrastructureCosts(ScriptCompany::CompanyID company, Infrastructure infra_type)
@@ -114,8 +115,9 @@
case INFRASTRUCTURE_ROAD: {
Money cost;
+ uint32 road_total = c->infrastructure.GetRoadTotal();
for (::RoadType rt = ::ROADTYPE_BEGIN; rt != ::ROADTYPE_END; rt++) {
- cost += RoadMaintenanceCost(rt, c->infrastructure.road[rt]);
+ cost += RoadMaintenanceCost(rt, c->infrastructure.road[rt], road_total);
}
return cost;
}
diff --git a/src/script/api/script_rail.hpp b/src/script/api/script_rail.hpp
index 7e2a59e7f..d52a7bfc4 100644
--- a/src/script/api/script_rail.hpp
+++ b/src/script/api/script_rail.hpp
@@ -36,7 +36,7 @@ public:
ERR_UNSUITABLE_TRACK, // [STR_ERROR_NO_SUITABLE_RAILROAD_TRACK, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, STR_ERROR_THERE_ARE_NO_SIGNALS, STR_ERROR_THERE_IS_NO_STATION]
/** This railtype cannot have crossings */
- ERR_RAILTYPE_DISALLOWS_CROSSING, // [STR_ERROR_CROSSING_DISALLOWED]
+ ERR_RAILTYPE_DISALLOWS_CROSSING, // [STR_ERROR_CROSSING_DISALLOWED_RAIL]
};
/**
diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp
index 74899f8bb..327978f6a 100644
--- a/src/script/api/script_road.cpp
+++ b/src/script/api/script_road.cpp
@@ -23,6 +23,13 @@
return ScriptCargo::HasCargoClass(cargo_type, ScriptCargo::CC_PASSENGERS) ? ROADVEHTYPE_BUS : ROADVEHTYPE_TRUCK;
}
+/* static */ char *ScriptRoad::GetName(RoadType road_type)
+{
+ if (!IsRoadTypeAvailable(road_type)) return nullptr;
+
+ return GetString(GetRoadTypeInfo((::RoadType)road_type)->strings.name);
+}
+
/* static */ bool ScriptRoad::IsRoadTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
@@ -37,7 +44,7 @@
if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT &&
- (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
+ HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
}
/* static */ bool ScriptRoad::IsRoadStationTile(TileIndex tile)
@@ -45,7 +52,7 @@
if (!::IsValidTile(tile)) return false;
if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
- return ::IsRoadStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
+ return ::IsRoadStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
}
/* static */ bool ScriptRoad::IsDriveThroughRoadStationTile(TileIndex tile)
@@ -53,12 +60,12 @@
if (!::IsValidTile(tile)) return false;
if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
- return ::IsDriveThroughStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
+ return ::IsDriveThroughStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
}
/* static */ bool ScriptRoad::IsRoadTypeAvailable(RoadType road_type)
{
- return ::IsValidRoadType((::RoadType)road_type) && ::HasRoadTypesAvail(ScriptObject::GetCompany(), ::RoadTypeToRoadTypes((::RoadType)road_type));
+ return (::RoadType)road_type < ROADTYPE_END && ::HasRoadTypeAvail(ScriptObject::GetCompany(), (::RoadType)road_type);
}
/* static */ ScriptRoad::RoadType ScriptRoad::GetCurrentRoadType()
@@ -73,11 +80,24 @@
ScriptObject::SetRoadType((::RoadType)road_type);
}
+/* static */ bool ScriptRoad::RoadVehCanRunOnRoad(RoadType engine_road_type, RoadType road_road_type)
+{
+ return RoadVehHasPowerOnRoad(engine_road_type, road_road_type);
+}
+
+/* static */ bool ScriptRoad::RoadVehHasPowerOnRoad(RoadType engine_road_type, RoadType road_road_type)
+{
+ if (!IsRoadTypeAvailable(engine_road_type)) return false;
+ if (!IsRoadTypeAvailable(road_road_type)) return false;
+
+ return ::HasPowerOnRoad((::RoadType)engine_road_type, (::RoadType)road_road_type);
+}
+
/* static */ bool ScriptRoad::HasRoadType(TileIndex tile, RoadType road_type)
{
if (!ScriptMap::IsValidTile(tile)) return false;
if (!IsRoadTypeAvailable(road_type)) return false;
- return ::GetAnyRoadBits(tile, (::RoadType)road_type, false) != ROAD_NONE;
+ return ::GetAnyRoadBits(tile, GetRoadTramType((::RoadType)road_type), false) != ROAD_NONE;
}
/* static */ bool ScriptRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2)
@@ -89,8 +109,9 @@
/* Tiles not neighbouring */
if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false;
- RoadBits r1 = ::GetAnyRoadBits(t1, ScriptObject::GetRoadType());
- RoadBits r2 = ::GetAnyRoadBits(t2, ScriptObject::GetRoadType());
+ RoadTramType rtt = GetRoadTramType(ScriptObject::GetRoadType());
+ RoadBits r1 = ::GetAnyRoadBits(t1, rtt); // TODO
+ RoadBits r2 = ::GetAnyRoadBits(t2, rtt); // TODO
uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3);
uint dir_2 = 2 ^ dir_1;
@@ -100,6 +121,16 @@
return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND);
}
+/* static */ bool ScriptRoad::ConvertRoadType(TileIndex start_tile, TileIndex end_tile, RoadType road_type)
+{
+ EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
+ EnforcePrecondition(false, ::IsValidTile(start_tile));
+ EnforcePrecondition(false, ::IsValidTile(end_tile));
+ EnforcePrecondition(false, IsRoadTypeAvailable(road_type));
+
+ return ScriptObject::DoCommand(start_tile, end_tile, (::RoadType)road_type, CMD_CONVERT_ROAD);
+}
+
/* Helper functions for ScriptRoad::CanBuildConnectedRoadParts(). */
/**
@@ -380,7 +411,7 @@ static bool NormaliseTileOffset(int32 *tile)
if (::IsNormalRoadTile(tile)) {
rb = ::GetAllRoadBits(tile);
} else {
- for (::RoadType rt = ::ROADTYPE_BEGIN; rt < ::ROADTYPE_END; rt++) rb |= ::GetAnyRoadBits(tile, rt);
+ rb = ::GetAnyRoadBits(tile, RTT_ROAD) | ::GetAnyRoadBits(tile, RTT_TRAM);
}
for (uint i = 0; i < lengthof(neighbours); i++) {
if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i];
@@ -392,15 +423,15 @@ static bool NormaliseTileOffset(int32 *tile)
/**
* Check whether one can reach (possibly by building) a road piece the center
* of the neighbouring tile. This includes roads and (drive through) stations.
- * @param rts The road type we want to know reachability for
+ * @param rt The road type we want to know reachability for
* @param start_tile The tile to "enter" the neighbouring tile.
* @param neighbour The direction to the neighbouring tile to "enter".
* @return true if and only if the tile is reachable.
*/
-static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, DiagDirection neighbour)
+static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagDirection neighbour)
{
TileIndex neighbour_tile = ::TileAddByDiagDir(start_tile, neighbour);
- if ((rts & ::GetRoadTypes(neighbour_tile)) == 0) return false;
+ if (!HasBit(::GetPresentRoadTypes(neighbour_tile), rt)) return false;
switch (::GetTileType(neighbour_tile)) {
case MP_ROAD:
@@ -422,13 +453,13 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia
if (!::IsValidTile(tile)) return false;
if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
- ::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType());
+ ::RoadType rt = (::RoadType)GetCurrentRoadType();
int32 neighbour = 0;
- if (TileX(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++;
- if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++;
- if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++;
- if (TileY(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NW)) neighbour++;
+ if (TileX(tile) > 0 && NeighbourHasReachableRoad(rt, tile, DIAGDIR_NE)) neighbour++;
+ if (NeighbourHasReachableRoad(rt, tile, DIAGDIR_SE)) neighbour++;
+ if (NeighbourHasReachableRoad(rt, tile, DIAGDIR_SW)) neighbour++;
+ if (TileY(tile) > 0 && NeighbourHasReachableRoad(rt, tile, DIAGDIR_NW)) neighbour++;
return neighbour;
}
@@ -460,10 +491,10 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia
EnforcePrecondition(false, ::IsValidTile(start));
EnforcePrecondition(false, ::IsValidTile(end));
EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
- EnforcePrecondition(false, !one_way || ScriptObject::GetRoadType() == ::ROADTYPE_ROAD);
+ EnforcePrecondition(false, !one_way || RoadTypeIsRoad(ScriptObject::GetRoadType()));
EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
- return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (ScriptObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 5) | 1 << 6, CMD_BUILD_LONG_ROAD);
+ return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (ScriptObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 10) | 1 << 11, CMD_BUILD_LONG_ROAD);
}
/* static */ bool ScriptRoad::BuildRoad(TileIndex start, TileIndex end)
@@ -520,11 +551,11 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia
entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
}
- uint p2 = station_id == ScriptStation::STATION_JOIN_ADJACENT ? 0 : 32;
+ uint p2 = station_id == ScriptStation::STATION_JOIN_ADJACENT ? 0 : 4;
p2 |= drive_through ? 2 : 0;
p2 |= road_veh_type == ROADVEHTYPE_TRUCK ? 1 : 0;
- p2 |= ::RoadTypeToRoadTypes(ScriptObject::GetRoadType()) << 2;
- p2 |= entrance_dir << 6;
+ p2 |= ScriptObject::GetRoadType() << 5;
+ p2 |= entrance_dir << 3;
p2 |= (ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
return ScriptObject::DoCommand(tile, 1 | 1 << 8, p2, CMD_BUILD_ROAD_STOP);
}
@@ -588,7 +619,7 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia
if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return -1;
switch (build_type) {
- case BT_ROAD: return ::GetPrice(PR_BUILD_ROAD, 1, nullptr);
+ case BT_ROAD: return ::RoadBuildCost((::RoadType)roadtype);
case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_ROAD, 1, nullptr);
case BT_BUS_STOP: return ::GetPrice(PR_BUILD_STATION_BUS, 1, nullptr);
case BT_TRUCK_STOP: return ::GetPrice(PR_BUILD_STATION_TRUCK, 1, nullptr);
@@ -596,9 +627,16 @@ static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, Dia
}
}
+/* static */ int32 ScriptRoad::GetMaxSpeed(RoadType road_type)
+{
+ if (!ScriptRoad::IsRoadTypeAvailable(road_type)) return 0;
+
+ return GetRoadTypeInfo((::RoadType)road_type)->max_speed;
+}
+
/* static */ uint16 ScriptRoad::GetMaintenanceCostFactor(RoadType roadtype)
{
if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return 0;
- return roadtype == ROADTYPE_TRAM ? 3 : 2;
+ return GetRoadTypeInfo((::RoadType)roadtype)->maintenance_multiplier;
}
diff --git a/src/script/api/script_road.hpp b/src/script/api/script_road.hpp
index ed4058f97..6da45acc2 100644
--- a/src/script/api/script_road.hpp
+++ b/src/script/api/script_road.hpp
@@ -36,16 +36,21 @@ public:
/** Drive through roads can't be build on town owned roads */
ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, // [STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD]
-
/** One way roads can't have junctions */
ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, // [STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION]
+
+ /** This roadtype cannot have crossings */
+ ERR_ROADTYPE_DISALLOWS_CROSSING, // [STR_ERROR_CROSSING_DISALLOWED_ROAD]
+
+ /** No suitable road could be found */
+ ERR_UNSUITABLE_ROAD, // [STR_ERROR_NO_SUITABLE_ROAD, STR_ERROR_NO_SUITABLE_TRAMWAY, STR_ERROR_INCOMPATIBLE_ROAD]
};
/**
* Types of road known to the game.
*/
enum RoadType {
- /* Note: these values represent part of the in-game RoadType enum */
+ /* Note: these values represent part of the in-game static values */
ROADTYPE_ROAD = ::ROADTYPE_ROAD, ///< Build road objects.
ROADTYPE_TRAM = ::ROADTYPE_TRAM, ///< Build tram objects.
@@ -72,6 +77,14 @@ public:
};
/**
+ * Get the name of a road type.
+ * @param road_type The road type to get the name of.
+ * @pre IsRoadTypeAvailable(road_type).
+ * @return The name the road type has.
+ */
+ static char *GetName(RoadType road_type);
+
+ /**
* Determines whether a busstop or a truckstop is needed to transport a certain cargo.
* @param cargo_type The cargo to test.
* @pre ScriptCargo::IsValidCargo(cargo_type).
@@ -138,6 +151,41 @@ public:
static void SetCurrentRoadType(RoadType road_type);
/**
+ * Check if a road vehicle built for a road type can run on another road type.
+ * @param engine_road_type The road type the road vehicle is built for.
+ * @param track_road_type The road type you want to check.
+ * @pre ScriptRoad::IsRoadTypeAvailable(engine_road_type).
+ * @pre ScriptRoad::IsRoadTypeAvailable(road_road_type).
+ * @return Whether a road vehicle built for 'engine_road_type' can run on 'road_road_type'.
+ */
+ static bool RoadVehCanRunOnRoad(ScriptRoad::RoadType engine_road_type, ScriptRoad::RoadType road_road_type);
+
+ /**
+ * Check if a road vehicle built for a road type has power on another road type.
+ * @param engine_road_type The road type the road vehicle is built for.
+ * @param road_road_type The road type you want to check.
+ * @pre ScriptRoad::IsRoadTypeAvailable(engine_road_type).
+ * @pre ScriptRoad::IsRoadTypeAvailable(road_road_type).
+ * @return Whether a road vehicle built for 'engine_road_type' has power on 'road_road_type'.
+ */
+ static bool RoadVehHasPowerOnRoad(ScriptRoad::RoadType engine_road_type, ScriptRoad::RoadType road_road_type);
+
+
+ /**
+ * Convert the road on all tiles within a rectangle to another RoadType.
+ * @param start_tile One corner of the rectangle.
+ * @param end_tile The opposite corner of the rectangle.
+ * @param road_type The RoadType you want to convert.
+ * @pre ScriptMap::IsValidTile(start_tile).
+ * @pre ScriptMap::IsValidTile(end_tile).
+ * @pre IsRoadTypeAvailable(road_type).
+ * @game @pre Valid ScriptCompanyMode active in scope.
+ * @exception ScriptRoad::ERR_UNSUITABLE_ROAD
+ * @return Whether at least some road has been converted successfully.
+ */
+ static bool ConvertRoadType(TileIndex start_tile, TileIndex end_tile, RoadType road_type);
+
+ /**
* Check if a given tile has RoadType.
* @param tile The tile to check.
* @param road_type The RoadType to check for.
@@ -482,16 +530,28 @@ public:
/**
* Get the baseprice of building a road-related object.
- * @param roadtype the roadtype that is build (on)
+ * @param roadtype the roadtype of the object to build
* @param build_type the type of object to build
- * @pre IsRoadTypeAvailable(railtype)
+ * @pre IsRoadTypeAvailable(roadtype)
* @return The baseprice of building the given object.
*/
static Money GetBuildCost(RoadType roadtype, BuildType build_type);
/**
- * Get the maintenance cost factor of a roadtype.
- * @param roadtype The roadtype to get the maintenance factor of.
+ * Get the maximum speed of road vehicles running on this roadtype.
+ * @param road_type The roadtype to get the maximum speed of.
+ * @pre IsRoadTypeAvailable(road_type)
+ * @return The maximum speed road vehicles can run on this roadtype
+ * or 0 if there is no limit.
+ * @note The speed is in OpenTTD's internal speed unit.
+ * This is mph / 0.8, which is roughly 0.5 km/h.
+ * To get km/h multiply this number by 2.01168.
+ */
+ static int32 GetMaxSpeed(RoadType road_type);
+
+ /**
+ * Get the maintenance cost factor of a road type.
+ * @param roadtype The road type to get the maintenance factor of.
* @pre IsRoadTypeAvailable(roadtype)
* @return Maintenance cost factor of the roadtype.
*/
diff --git a/src/script/api/script_station.cpp b/src/script/api/script_station.cpp
index 3b3bae33c..e5536c6e0 100644
--- a/src/script/api/script_station.cpp
+++ b/src/script/api/script_station.cpp
@@ -211,13 +211,11 @@ template<bool Tfrom, bool Tvia>
if (!IsValidStation(station_id)) return false;
if (!ScriptRoad::IsRoadTypeAvailable(road_type)) return false;
- ::RoadTypes r = RoadTypeToRoadTypes((::RoadType)road_type);
-
for (const RoadStop *rs = ::Station::Get(station_id)->GetPrimaryRoadStop(ROADSTOP_BUS); rs != nullptr; rs = rs->next) {
- if ((::GetRoadTypes(rs->xy) & r) != 0) return true;
+ if (HasBit(::GetPresentRoadTypes(rs->xy), (::RoadType)road_type)) return true;
}
for (const RoadStop *rs = ::Station::Get(station_id)->GetPrimaryRoadStop(ROADSTOP_TRUCK); rs != nullptr; rs = rs->next) {
- if ((::GetRoadTypes(rs->xy) & r) != 0) return true;
+ if (HasBit(::GetPresentRoadTypes(rs->xy), (::RoadType)road_type)) return true;
}
return false;
diff --git a/src/script/api/script_tile.cpp b/src/script/api/script_tile.cpp
index 136461762..3d10b7d51 100644
--- a/src/script/api/script_tile.cpp
+++ b/src/script/api/script_tile.cpp
@@ -33,12 +33,12 @@
case MP_WATER: return IsCoast(tile);
case MP_ROAD:
/* Tram bits aren't considered buildable */
- if (::GetRoadTypes(tile) != ROADTYPES_ROAD) return false;
+ if (::GetRoadTypeTram(tile) != INVALID_ROADTYPE) return false;
/* Depots and crossings aren't considered buildable */
if (::GetRoadTileType(tile) != ROAD_TILE_NORMAL) return false;
- if (!HasExactlyOneBit(::GetRoadBits(tile, ROADTYPE_ROAD))) return false;
- if (::IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) return true;
- if (::IsRoadOwner(tile, ROADTYPE_ROAD, ScriptObject::GetCompany())) return true;
+ if (!HasExactlyOneBit(::GetRoadBits(tile, RTT_ROAD))) return false;
+ if (::IsRoadOwner(tile, RTT_ROAD, OWNER_TOWN)) return true;
+ if (::IsRoadOwner(tile, RTT_ROAD, ScriptObject::GetCompany())) return true;
return false;
}
}
@@ -201,7 +201,12 @@
{
if (!::IsValidTile(tile)) return false;
- return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, UINT32_MAX)) != TRACKDIR_BIT_NONE;
+ if (transport_type == TRANSPORT_ROAD) {
+ return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 0)) != TRACKDIR_BIT_NONE ||
+ ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 1)) != TRACKDIR_BIT_NONE;
+ } else {
+ return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 0)) != TRACKDIR_BIT_NONE;
+ }
}
/* static */ int32 ScriptTile::GetCargoAcceptance(TileIndex tile, CargoID cargo_type, int width, int height, int radius)
diff --git a/src/script/api/script_tunnel.cpp b/src/script/api/script_tunnel.cpp
index a8bfcbfd3..ce94c17ee 100644
--- a/src/script/api/script_tunnel.cpp
+++ b/src/script/api/script_tunnel.cpp
@@ -90,7 +90,7 @@ static void _DoCommandReturnBuildTunnel1(class ScriptInstance *instance)
uint type = 0;
if (vehicle_type == ScriptVehicle::VT_ROAD) {
type |= (TRANSPORT_ROAD << 8);
- type |= ::RoadTypeToRoadTypes((::RoadType)ScriptObject::GetRoadType());
+ type |= ScriptRoad::GetCurrentRoadType();
} else {
type |= (TRANSPORT_RAIL << 8);
type |= ScriptRail::GetCurrentRailType();
diff --git a/src/script/api/script_vehicle.cpp b/src/script/api/script_vehicle.cpp
index 15bc68a97..e0d129ad8 100644
--- a/src/script/api/script_vehicle.cpp
+++ b/src/script/api/script_vehicle.cpp
@@ -401,7 +401,7 @@
if (!IsValidVehicle(vehicle_id)) return ScriptRoad::ROADTYPE_INVALID;
if (GetVehicleType(vehicle_id) != VT_ROAD) return ScriptRoad::ROADTYPE_INVALID;
- return (ScriptRoad::RoadType)(::RoadVehicle::Get(vehicle_id))->roadtype;
+ return (ScriptRoad::RoadType)(int)(::RoadVehicle::Get(vehicle_id))->roadtype;
}
/* static */ int32 ScriptVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo)
diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp
index 47c848888..58226ea5b 100644
--- a/src/script/api/script_window.hpp
+++ b/src/script/api/script_window.hpp
@@ -954,9 +954,11 @@ public:
WID_RV_INFO_TAB = ::WID_RV_INFO_TAB, ///< Info tab.
WID_RV_STOP_REPLACE = ::WID_RV_STOP_REPLACE, ///< Stop Replacing button.
+ /* Train/road only widgets */
+ WID_RV_RAIL_ROAD_TYPE_DROPDOWN = ::WID_RV_RAIL_ROAD_TYPE_DROPDOWN, ///< Dropdown menu about the rail/roadtype.
+
/* Train only widgets. */
WID_RV_TRAIN_ENGINEWAGON_DROPDOWN = ::WID_RV_TRAIN_ENGINEWAGON_DROPDOWN, ///< Dropdown to select engines and/or wagons.
- WID_RV_TRAIN_RAILTYPE_DROPDOWN = ::WID_RV_TRAIN_RAILTYPE_DROPDOWN, ///< Dropdown menu about the railtype.
WID_RV_TRAIN_WAGONREMOVE_TOGGLE = ::WID_RV_TRAIN_WAGONREMOVE_TOGGLE, ///< Button to toggle removing wagons.
};
@@ -1170,6 +1172,8 @@ public:
WID_CI_RAIL_COUNT = ::WID_CI_RAIL_COUNT, ///< Count of rail.
WID_CI_ROAD_DESC = ::WID_CI_ROAD_DESC, ///< Description of road.
WID_CI_ROAD_COUNT = ::WID_CI_ROAD_COUNT, ///< Count of road.
+ WID_CI_TRAM_DESC = ::WID_CI_TRAM_DESC, ///< Description of tram.
+ WID_CI_TRAM_COUNT = ::WID_CI_TRAM_COUNT, ///< Count of tram.
WID_CI_WATER_DESC = ::WID_CI_WATER_DESC, ///< Description of water.
WID_CI_WATER_COUNT = ::WID_CI_WATER_COUNT, ///< Count of water.
WID_CI_STATION_DESC = ::WID_CI_STATION_DESC, ///< Description of station.
@@ -2125,6 +2129,7 @@ public:
/** Widgets of the #BuildRoadToolbarWindow class. */
enum RoadToolbarWidgets {
/* Name starts with RO instead of R, because of collision with RailToolbarWidgets */
+ WID_ROT_CAPTION = ::WID_ROT_CAPTION, ///< Caption of the window
WID_ROT_ROAD_X = ::WID_ROT_ROAD_X, ///< Build road in x-direction.
WID_ROT_ROAD_Y = ::WID_ROT_ROAD_Y, ///< Build road in y-direction.
WID_ROT_AUTOROAD = ::WID_ROT_AUTOROAD, ///< Autorail.
@@ -2136,6 +2141,7 @@ public:
WID_ROT_BUILD_BRIDGE = ::WID_ROT_BUILD_BRIDGE, ///< Build bridge.
WID_ROT_BUILD_TUNNEL = ::WID_ROT_BUILD_TUNNEL, ///< Build tunnel.
WID_ROT_REMOVE = ::WID_ROT_REMOVE, ///< Remove road.
+ WID_ROT_CONVERT_ROAD = ::WID_ROT_CONVERT_ROAD, ///< Convert road.
};
/** Widgets of the #BuildRoadDepotWindow class. */
@@ -2435,6 +2441,7 @@ public:
WID_TN_BUILDING_TOOLS_START = ::WID_TN_BUILDING_TOOLS_START, ///< Helper for the offset of the building tools
WID_TN_RAILS = ::WID_TN_RAILS, ///< Rail building menu.
WID_TN_ROADS = ::WID_TN_ROADS, ///< Road building menu.
+ WID_TN_TRAMS = ::WID_TN_TRAMS, ///< Tram building menu.
WID_TN_WATER = ::WID_TN_WATER, ///< Water building toolbar.
WID_TN_AIR = ::WID_TN_AIR, ///< Airport building toolbar.
WID_TN_LANDSCAPE = ::WID_TN_LANDSCAPE, ///< Landscaping toolbar.
@@ -2462,11 +2469,11 @@ public:
WID_TE_TOWN_GENERATE = ::WID_TE_TOWN_GENERATE, ///< Town building window.
WID_TE_INDUSTRY = ::WID_TE_INDUSTRY, ///< Industry building window.
WID_TE_ROADS = ::WID_TE_ROADS, ///< Road building menu.
+ WID_TE_TRAMS = ::WID_TE_TRAMS, ///< Tram building menu.
WID_TE_WATER = ::WID_TE_WATER, ///< Water building toolbar.
WID_TE_TREES = ::WID_TE_TREES, ///< Tree building toolbar.
WID_TE_SIGNS = ::WID_TE_SIGNS, ///< Sign building.
WID_TE_DATE_PANEL = ::WID_TE_DATE_PANEL, ///< Container for the date widgets.
- /* The following three need to have the same actual widget number as the normal toolbar due to shared code. */
WID_TE_MUSIC_SOUND = ::WID_TE_MUSIC_SOUND, ///< Music/sound configuration menu.
WID_TE_HELP = ::WID_TE_HELP, ///< Help menu.
WID_TE_SWITCH_BAR = ::WID_TE_SWITCH_BAR, ///< Only available when toolbar has been split to switch between different subsets.
diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp
index aa1e04815..e9747400d 100644
--- a/src/smallmap_gui.cpp
+++ b/src/smallmap_gui.cpp
@@ -460,28 +460,51 @@ static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile, TileType t)
*/
static inline uint32 GetSmallMapRoutesPixels(TileIndex tile, TileType t)
{
- if (t == MP_STATION) {
- switch (GetStationType(tile)) {
- case STATION_RAIL: return MKCOLOUR_XXXX(PC_VERY_DARK_BROWN);
- case STATION_AIRPORT: return MKCOLOUR_XXXX(PC_RED);
- case STATION_TRUCK: return MKCOLOUR_XXXX(PC_ORANGE);
- case STATION_BUS: return MKCOLOUR_XXXX(PC_YELLOW);
- case STATION_DOCK: return MKCOLOUR_XXXX(PC_LIGHT_BLUE);
- default: return MKCOLOUR_FFFF;
+ switch (t) {
+ case MP_STATION:
+ switch (GetStationType(tile)) {
+ case STATION_RAIL: return MKCOLOUR_XXXX(PC_VERY_DARK_BROWN);
+ case STATION_AIRPORT: return MKCOLOUR_XXXX(PC_RED);
+ case STATION_TRUCK: return MKCOLOUR_XXXX(PC_ORANGE);
+ case STATION_BUS: return MKCOLOUR_XXXX(PC_YELLOW);
+ case STATION_DOCK: return MKCOLOUR_XXXX(PC_LIGHT_BLUE);
+ default: return MKCOLOUR_FFFF;
+ }
+
+ case MP_RAILWAY: {
+ AndOr andor = {
+ MKCOLOUR_0XX0(GetRailTypeInfo(GetRailType(tile))->map_colour),
+ _smallmap_contours_andor[t].mand
+ };
+
+ const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
+ return ApplyMask(cs->default_colour, &andor);
}
- } else if (t == MP_RAILWAY) {
- AndOr andor = {
- MKCOLOUR_0XX0(GetRailTypeInfo(GetRailType(tile))->map_colour),
- _smallmap_contours_andor[t].mand
- };
- const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
- return ApplyMask(cs->default_colour, &andor);
- }
+ case MP_ROAD: {
+ const RoadTypeInfo *rti = nullptr;
+ if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
+ rti = GetRoadTypeInfo(GetRoadTypeRoad(tile));
+ } else {
+ rti = GetRoadTypeInfo(GetRoadTypeTram(tile));
+ }
+ if (rti != nullptr) {
+ AndOr andor = {
+ MKCOLOUR_0XX0(rti->map_colour),
+ _smallmap_contours_andor[t].mand
+ };
+
+ const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
+ return ApplyMask(cs->default_colour, &andor);
+ }
+ FALLTHROUGH;
+ }
- /* Ground colour */
- const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
- return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
+ default:
+ /* Ground colour */
+ const SmallMapColourScheme *cs = &_heightmap_schemes[_settings_client.gui.smallmap_land_colour];
+ return ApplyMask(cs->default_colour, &_smallmap_contours_andor[t]);
+ }
}
/**
diff --git a/src/station.cpp b/src/station.cpp
index e8ae1bc20..096a3c373 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -189,7 +189,7 @@ RoadStop *Station::GetPrimaryRoadStop(const RoadVehicle *v) const
for (; rs != nullptr; rs = rs->next) {
/* The vehicle cannot go to this roadstop (different roadtype) */
- if ((GetRoadTypes(rs->xy) & v->compatible_roadtypes) == ROADTYPES_NONE) continue;
+ if (!HasTileAnyRoadType(rs->xy, v->compatible_roadtypes)) continue;
/* The vehicle is articulated and can therefore not go to a standard road stop. */
if (IsStandardRoadStopTile(rs->xy) && v->HasArticulatedPart()) continue;
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 4aa640127..0ba824c0d 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -41,6 +41,7 @@
#include "station_kdtree.h"
#include "roadstop_base.h"
#include "newgrf_railtype.h"
+#include "newgrf_roadtype.h"
#include "waypoint_base.h"
#include "waypoint_func.h"
#include "pbs.h"
@@ -934,10 +935,10 @@ static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag fl
* @param is_truck_stop True when building a truck stop, false otherwise.
* @param axis Axis of a drive-through road stop.
* @param station StationID to be queried and returned if available.
- * @param rts Road types to build.
+ * @param rt Road type to build.
* @return The cost in case of success, or an error code if it failed.
*/
-static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadTypes rts)
+static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadType rt)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
int allowed_z = -1;
@@ -988,47 +989,54 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags
}
}
- RoadTypes cur_rts = IsNormalRoadTile(cur_tile) ? GetRoadTypes(cur_tile) : ROADTYPES_NONE;
- uint num_roadbits = 0;
if (build_over_road) {
/* There is a road, check if we can build road+tram stop over it. */
- if (HasBit(cur_rts, ROADTYPE_ROAD)) {
- Owner road_owner = GetRoadOwner(cur_tile, ROADTYPE_ROAD);
+ RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
+ if (road_rt != INVALID_ROADTYPE) {
+ Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
if (road_owner == OWNER_TOWN) {
if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
} else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
CommandCost ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
- num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_ROAD));
+ uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
+
+ if (RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
+
+ cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
+ } else if (RoadTypeIsRoad(rt)) {
+ cost.AddCost(RoadBuildCost(rt) * 2);
}
if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD);
/* There is a tram, check if we can build road+tram stop over it. */
- if (HasBit(cur_rts, ROADTYPE_TRAM)) {
- Owner tram_owner = GetRoadOwner(cur_tile, ROADTYPE_TRAM);
+ RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
+ if (tram_rt != INVALID_ROADTYPE) {
+ Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
if (Company::IsValidID(tram_owner) &&
(!_settings_game.construction.road_stop_on_competitor_road ||
/* Disallow breaking end-of-line of someone else
* so trams can still reverse on this tile. */
- HasExactlyOneBit(GetRoadBits(cur_tile, ROADTYPE_TRAM)))) {
+ HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
CommandCost ret = CheckOwnership(tram_owner);
if (ret.Failed()) return ret;
}
- num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_TRAM));
- }
+ uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
- /* Take into account existing roadbits. */
- rts |= cur_rts;
+ if (RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
+
+ cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
+ } else if (RoadTypeIsTram(rt)) {
+ cost.AddCost(RoadBuildCost(rt) * 2);
+ }
} else {
ret = DoCommand(cur_tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
cost.AddCost(ret);
+ cost.AddCost(RoadBuildCost(rt) * 2);
}
-
- uint roadbits_to_build = CountBits(rts) * 2 - num_roadbits;
- cost.AddCost(_price[PR_BUILD_ROAD] * roadbits_to_build);
}
}
@@ -1783,10 +1791,10 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio
* bit 8..15: Length of the road stop.
* @param p2 bit 0: 0 For bus stops, 1 for truck stops.
* bit 1: 0 For normal stops, 1 for drive-through.
- * bit 2..3: The roadtypes.
- * bit 5: Allow stations directly adjacent to other stations.
- * bit 6..7: Entrance direction (#DiagDirection) for normal stops.
- * bit 6: #Axis of the road for drive-through stops.
+ * bit 2: Allow stations directly adjacent to other stations.
+ * bit 3..4: Entrance direction (#DiagDirection) for normal stops.
+ * bit 3: #Axis of the road for drive-through stops.
+ * bit 5..10: The roadtype.
* bit 16..31: Station ID to join (NEW_STATION if build new one).
* @param text Unused.
* @return The cost of this operation or an error.
@@ -1795,7 +1803,8 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
{
bool type = HasBit(p2, 0);
bool is_drive_through = HasBit(p2, 1);
- RoadTypes rts = Extract<RoadTypes, 2, 2>(p2);
+ RoadType rt = Extract<RoadType, 5, 6>(p2);
+ if (!ValParamRoadType(rt)) return CMD_ERROR;
StationID station_to_join = GB(p2, 16, 16);
bool reuse = (station_to_join != NEW_STATION);
if (!reuse) station_to_join = INVALID_STATION;
@@ -1815,20 +1824,18 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
if (distant_join && (!_settings_game.station.distant_join_stations || !Station::IsValidID(station_to_join))) return CMD_ERROR;
- if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR;
-
/* Trams only have drive through stops */
- if (!is_drive_through && HasBit(rts, ROADTYPE_TRAM)) return CMD_ERROR;
+ if (!is_drive_through && RoadTypeIsTram(rt)) return CMD_ERROR;
DiagDirection ddir;
Axis axis;
if (is_drive_through) {
/* By definition axis is valid, due to there being 2 axes and reading 1 bit. */
- axis = Extract<Axis, 6, 1>(p2);
+ axis = Extract<Axis, 3, 1>(p2);
ddir = AxisToDiagDir(axis);
} else {
/* By definition ddir is valid, due to there being 4 diagonal directions and reading 2 bits. */
- ddir = Extract<DiagDirection, 6, 2>(p2);
+ ddir = Extract<DiagDirection, 3, 2>(p2);
axis = DiagDirToAxis(ddir);
}
@@ -1838,12 +1845,12 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
/* Total road stop cost. */
CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[type ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
StationID est = INVALID_STATION;
- ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type, axis, &est, rts);
+ ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type, axis, &est, rt);
if (ret.Failed()) return ret;
cost.AddCost(ret);
Station *st = nullptr;
- ret = FindJoiningRoadStop(est, station_to_join, HasBit(p2, 5), roadstop_area, &st);
+ ret = FindJoiningRoadStop(est, station_to_join, HasBit(p2, 2), roadstop_area, &st);
if (ret.Failed()) return ret;
/* Check if this number of road stops can be allocated. */
@@ -1855,9 +1862,11 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
if (flags & DC_EXEC) {
/* Check every tile in the area. */
TILE_AREA_LOOP(cur_tile, roadstop_area) {
- RoadTypes cur_rts = GetRoadTypes(cur_tile);
- Owner road_owner = HasBit(cur_rts, ROADTYPE_ROAD) ? GetRoadOwner(cur_tile, ROADTYPE_ROAD) : _current_company;
- Owner tram_owner = HasBit(cur_rts, ROADTYPE_TRAM) ? GetRoadOwner(cur_tile, ROADTYPE_TRAM) : _current_company;
+ /* Get existing road types and owners before any tile clearing */
+ RoadType road_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_ROAD) : INVALID_ROADTYPE;
+ RoadType tram_rt = MayHaveRoad(cur_tile) ? GetRoadType(cur_tile, RTT_TRAM) : INVALID_ROADTYPE;
+ Owner road_owner = road_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_ROAD) : _current_company;
+ Owner tram_owner = tram_rt != INVALID_ROADTYPE ? GetRoadOwner(cur_tile, RTT_TRAM) : _current_company;
if (IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile)) {
RemoveRoadStop(cur_tile, flags);
@@ -1881,23 +1890,27 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
RoadStopType rs_type = type ? ROADSTOP_TRUCK : ROADSTOP_BUS;
if (is_drive_through) {
- /* Update company infrastructure counts. If the current tile is a normal
- * road tile, count only the new road bits needed to get a full diagonal road. */
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, cur_rts | rts) {
- Company *c = Company::GetIfValid(rt == ROADTYPE_ROAD ? road_owner : tram_owner);
- if (c != nullptr) {
- c->infrastructure.road[rt] += 2 - (IsNormalRoadTile(cur_tile) && HasBit(cur_rts, rt) ? CountBits(GetRoadBits(cur_tile, rt)) : 0);
- DirtyCompanyInfrastructureWindows(c->index);
- }
+ /* Update company infrastructure counts. If the current tile is a normal road tile, remove the old
+ * bits first. */
+ if (IsNormalRoadTile(cur_tile)) {
+ UpdateCompanyRoadInfrastructure(road_rt, road_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_ROAD)));
+ UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, -(int)CountBits(GetRoadBits(cur_tile, RTT_TRAM)));
}
- MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, rts | cur_rts, axis);
+ if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
+ if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
+
+ UpdateCompanyRoadInfrastructure(road_rt, road_owner, 2);
+ UpdateCompanyRoadInfrastructure(tram_rt, tram_owner, 2);
+
+ MakeDriveThroughRoadStop(cur_tile, st->owner, road_owner, tram_owner, st->index, rs_type, road_rt, tram_rt, axis);
road_stop->MakeDriveThrough();
} else {
+ if (road_rt == INVALID_ROADTYPE && RoadTypeIsRoad(rt)) road_rt = rt;
+ if (tram_rt == INVALID_ROADTYPE && RoadTypeIsTram(rt)) tram_rt = rt;
/* Non-drive-through stop never overbuild and always count as two road bits. */
- Company::Get(st->owner)->infrastructure.road[FIND_FIRST_BIT(rts)] += 2;
- MakeRoadStop(cur_tile, st->owner, st->index, rs_type, rts, ddir);
+ Company::Get(st->owner)->infrastructure.road[rt] += 2;
+ MakeRoadStop(cur_tile, st->owner, st->index, rs_type, road_rt, tram_rt, ddir);
}
Company::Get(st->owner)->infrastructure.station++;
@@ -1983,14 +1996,11 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags)
}
/* Update company infrastructure counts. */
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- c->infrastructure.road[rt] -= 2;
- DirtyCompanyInfrastructureWindows(c->index);
- }
+ FOR_ALL_ROADTRAMTYPES(rtt) {
+ RoadType rt = GetRoadType(tile, rtt);
+ UpdateCompanyRoadInfrastructure(rt, GetRoadOwner(tile, rtt), -2);
}
+
Company::Get(st->owner)->infrastructure.station--;
DirtyCompanyInfrastructureWindows(st->owner);
@@ -2064,16 +2074,16 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || (uint32)GetRoadStopType(cur_tile) != GB(p2, 0, 1)) continue;
/* Save information on to-be-restored roads before the stop is removed. */
- RoadTypes rts = ROADTYPES_NONE;
RoadBits road_bits = ROAD_NONE;
+ RoadType road_type[] = { INVALID_ROADTYPE, INVALID_ROADTYPE };
Owner road_owner[] = { OWNER_NONE, OWNER_NONE };
- assert_compile(lengthof(road_owner) == ROADTYPE_END);
if (IsDriveThroughStopTile(cur_tile)) {
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(cur_tile)) {
- road_owner[rt] = GetRoadOwner(cur_tile, rt);
+ FOR_ALL_ROADTRAMTYPES(rtt) {
+ road_type[rtt] = GetRoadType(cur_tile, rtt);
+ if (road_type[rtt] == INVALID_ROADTYPE) continue;
+ road_owner[rtt] = GetRoadOwner(cur_tile, rtt);
/* If we don't want to preserve our roads then restore only roads of others. */
- if (keep_drive_through_roads || road_owner[rt] != _current_company) SetBit(rts, rt);
+ if (!keep_drive_through_roads && road_owner[rtt] == _current_company) road_type[rtt] = INVALID_ROADTYPE;
}
road_bits = AxisToRoadBits(DiagDirToAxis(GetRoadStopDir(cur_tile)));
}
@@ -2087,19 +2097,14 @@ CommandCost CmdRemoveRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
had_success = true;
/* Restore roads. */
- if ((flags & DC_EXEC) && rts != ROADTYPES_NONE) {
- MakeRoadNormal(cur_tile, road_bits, rts, ClosestTownFromTile(cur_tile, UINT_MAX)->index,
- road_owner[ROADTYPE_ROAD], road_owner[ROADTYPE_TRAM]);
+ if ((flags & DC_EXEC) && (road_type[RTT_ROAD] != INVALID_ROADTYPE || road_type[RTT_TRAM] != INVALID_ROADTYPE)) {
+ MakeRoadNormal(cur_tile, road_bits, road_type[RTT_ROAD], road_type[RTT_TRAM], ClosestTownFromTile(cur_tile, UINT_MAX)->index,
+ road_owner[RTT_ROAD], road_owner[RTT_TRAM]);
/* Update company infrastructure counts. */
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, rts) {
- Company *c = Company::GetIfValid(GetRoadOwner(cur_tile, rt));
- if (c != nullptr) {
- c->infrastructure.road[rt] += CountBits(road_bits);
- DirtyCompanyInfrastructureWindows(c->index);
- }
- }
+ int count = CountBits(road_bits);
+ UpdateCompanyRoadInfrastructure(road_type[RTT_ROAD], road_owner[RTT_ROAD], count);
+ UpdateCompanyRoadInfrastructure(road_type[RTT_TRAM], road_owner[RTT_ROAD], count);
}
}
@@ -2701,7 +2706,6 @@ static void DrawTile_Station(TileInfo *ti)
const NewGRFSpriteLayout *layout = nullptr;
DrawTileSprites tmp_rail_layout;
const DrawTileSprites *t = nullptr;
- RoadTypes roadtypes;
int32 total_offset;
const RailtypeInfo *rti = nullptr;
uint32 relocation = 0;
@@ -2712,7 +2716,6 @@ static void DrawTile_Station(TileInfo *ti)
if (HasStationRail(ti->tile)) {
rti = GetRailTypeInfo(GetRailType(ti->tile));
- roadtypes = ROADTYPES_NONE;
total_offset = rti->GetRailtypeSpriteOffset();
if (IsCustomStationSpecIndex(ti->tile)) {
@@ -2739,7 +2742,6 @@ static void DrawTile_Station(TileInfo *ti)
}
}
} else {
- roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
total_offset = 0;
}
@@ -2924,10 +2926,30 @@ draw_default_foundation:
if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
- if (HasBit(roadtypes, ROADTYPE_TRAM)) {
- Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
- DrawGroundSprite((HasBit(roadtypes, ROADTYPE_ROAD) ? SPR_TRAMWAY_OVERLAY : SPR_TRAMWAY_TRAM) + (axis ^ 1), PAL_NONE);
- DrawRoadCatenary(ti, axis == AXIS_X ? ROAD_X : ROAD_Y);
+ if (IsRoadStop(ti->tile)) {
+ RoadType road_rt = GetRoadTypeRoad(ti->tile);
+ RoadType tram_rt = GetRoadTypeTram(ti->tile);
+ const RoadTypeInfo* road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
+ const RoadTypeInfo* tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
+
+ if (IsDriveThroughStopTile(ti->tile)) {
+ Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
+ uint sprite_offset = axis == AXIS_X ? 1 : 0;
+
+ DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
+ } else {
+ /* Non-drivethrough road stops are only valid for roads. */
+ assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
+
+ if (road_rti->UsesOverlay()) {
+ DiagDirection dir = GetRoadStopDir(ti->tile);
+ SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
+ DrawGroundSprite(ground + dir, PAL_NONE);
+ }
+ }
+
+ /* Draw road, tram catenary */
+ DrawRoadCatenary(ti);
}
if (IsRailWaypoint(ti->tile)) {
@@ -2960,8 +2982,29 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro
DrawSprite(img + total_offset, HasBit(img, PALETTE_MODIFIER_COLOUR) ? pal : PAL_NONE, x, y);
}
- if (roadtype == ROADTYPE_TRAM) {
- DrawSprite(SPR_TRAMWAY_TRAM + (t->ground.sprite == SPR_ROAD_PAVED_STRAIGHT_X ? 1 : 0), PAL_NONE, x, y);
+ if (roadtype != INVALID_ROADTYPE) {
+ const RoadTypeInfo* rti = GetRoadTypeInfo(roadtype);
+ if (image >= 4) {
+ /* Drive-through stop */
+ uint sprite_offset = 5 - image;
+
+ /* Road underlay takes precendence over tram */
+ if (rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND);
+ DrawSprite(ground + sprite_offset, PAL_NONE, x, y);
+
+ SpriteID overlay = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY);
+ if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
+ } else if (RoadTypeIsTram(roadtype)) {
+ DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
+ }
+ } else {
+ /* Drive-in stop */
+ if (RoadTypeIsRoad(roadtype) && rti->UsesOverlay()) {
+ SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP);
+ DrawSprite(ground + image, PAL_NONE, x, y);
+ }
+ }
}
/* Default waypoint has no railtype specific sprites */
@@ -2981,28 +3024,44 @@ static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
{
td->owner[0] = GetTileOwner(tile);
- if (IsDriveThroughStopTile(tile)) {
+
+ if (IsRoadStopTile(tile)) {
+ RoadType road_rt = GetRoadTypeRoad(tile);
+ RoadType tram_rt = GetRoadTypeTram(tile);
Owner road_owner = INVALID_OWNER;
Owner tram_owner = INVALID_OWNER;
- RoadTypes rts = GetRoadTypes(tile);
- if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
- if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
-
- /* Is there a mix of owners? */
- if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
- (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
- uint i = 1;
- if (road_owner != INVALID_OWNER) {
- td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
- td->owner[i] = road_owner;
- i++;
- }
- if (tram_owner != INVALID_OWNER) {
- td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
- td->owner[i] = tram_owner;
+ if (road_rt != INVALID_ROADTYPE) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
+ td->roadtype = rti->strings.name;
+ td->road_speed = rti->max_speed / 2;
+ road_owner = GetRoadOwner(tile, RTT_ROAD);
+ }
+
+ if (tram_rt != INVALID_ROADTYPE) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
+ td->tramtype = rti->strings.name;
+ td->tram_speed = rti->max_speed / 2;
+ tram_owner = GetRoadOwner(tile, RTT_TRAM);
+ }
+
+ if (IsDriveThroughStopTile(tile)) {
+ /* Is there a mix of owners? */
+ if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
+ (road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
+ uint i = 1;
+ if (road_owner != INVALID_OWNER) {
+ td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
+ td->owner[i] = road_owner;
+ i++;
+ }
+ if (tram_owner != INVALID_OWNER) {
+ td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
+ td->owner[i] = tram_owner;
+ }
}
}
}
+
td->build_date = BaseStation::GetByTile(tile)->build_date;
if (HasStationTileRail(tile)) {
@@ -3088,7 +3147,10 @@ static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode
break;
case TRANSPORT_ROAD:
- if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
+ if (IsRoadStop(tile)) {
+ RoadTramType rtt = (RoadTramType)sub_mode;
+ if (!HasTileRoadType(tile, rtt)) break;
+
DiagDirection dir = GetRoadStopDir(tile);
Axis axis = DiagDirToAxis(dir);
@@ -3982,15 +4044,16 @@ void DeleteOilRig(TileIndex tile)
static void ChangeTileOwner_Station(TileIndex tile, Owner old_owner, Owner new_owner)
{
if (IsRoadStopTile(tile)) {
- for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
+ FOR_ALL_ROADTRAMTYPES(rtt) {
/* Update all roadtypes, no matter if they are present */
- if (GetRoadOwner(tile, rt) == old_owner) {
- if (HasTileRoadType(tile, rt)) {
+ if (GetRoadOwner(tile, rtt) == old_owner) {
+ RoadType rt = GetRoadType(tile, rtt);
+ if (rt != INVALID_ROADTYPE) {
/* A drive-through road-stop has always two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */
Company::Get(old_owner)->infrastructure.road[rt] -= 2;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += 2;
}
- SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
+ SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
}
}
}
@@ -4071,17 +4134,16 @@ static bool CanRemoveRoadWithStop(TileIndex tile, DoCommandFlag flags)
/* Yeah... water can always remove stops, right? */
if (_current_company == OWNER_WATER) return true;
- RoadTypes rts = GetRoadTypes(tile);
- if (HasBit(rts, ROADTYPE_TRAM)) {
- Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
+ if (GetRoadTypeTram(tile) != INVALID_ROADTYPE) {
+ Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
if (tram_owner != OWNER_NONE && CheckOwnership(tram_owner).Failed()) return false;
}
- if (HasBit(rts, ROADTYPE_ROAD)) {
- Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
+ if (GetRoadTypeRoad(tile) != INVALID_ROADTYPE) {
+ Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
if (road_owner != OWNER_TOWN) {
if (road_owner != OWNER_NONE && CheckOwnership(road_owner).Failed()) return false;
} else {
- if (CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, ROADTYPE_ROAD), OWNER_TOWN, ROADTYPE_ROAD, flags).Failed()) return false;
+ if (CheckAllowRemoveRoad(tile, GetAnyRoadBits(tile, RTT_ROAD), OWNER_TOWN, RTT_ROAD, flags).Failed()) return false;
}
}
@@ -4102,8 +4164,8 @@ CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags)
case STATION_RAIL: return_cmd_error(STR_ERROR_MUST_DEMOLISH_RAILROAD);
case STATION_WAYPOINT: return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
case STATION_AIRPORT: return_cmd_error(STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST);
- case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
- case STATION_BUS: return_cmd_error(HasTileRoadType(tile, ROADTYPE_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
+ case STATION_TRUCK: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST);
+ case STATION_BUS: return_cmd_error(HasTileRoadType(tile, RTT_TRAM) ? STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST : STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST);
case STATION_BUOY: return_cmd_error(STR_ERROR_BUOY_IN_THE_WAY);
case STATION_DOCK: return_cmd_error(STR_ERROR_MUST_DEMOLISH_DOCK_FIRST);
case STATION_OILRIG:
diff --git a/src/station_func.h b/src/station_func.h
index bc18b038c..a6d082b93 100644
--- a/src/station_func.h
+++ b/src/station_func.h
@@ -18,6 +18,7 @@
#include "vehicle_type.h"
#include "economy_func.h"
#include "rail.h"
+#include "road.h"
#include "linkgraph/linkgraph_type.h"
#include "industry_type.h"
diff --git a/src/station_map.h b/src/station_map.h
index e591787e3..35766ec69 100644
--- a/src/station_map.h
+++ b/src/station_map.h
@@ -17,6 +17,7 @@
#include "water_map.h"
#include "station_func.h"
#include "rail.h"
+#include "road.h"
typedef byte StationGfx; ///< Index of station graphics. @see _station_display_datas
@@ -583,15 +584,16 @@ static inline void MakeRailWaypoint(TileIndex t, Owner o, StationID sid, Axis a,
* @param o the owner of the roadstop
* @param sid the station to which this tile belongs
* @param rst the type of roadstop to make this tile
- * @param rt the roadtypes on this tile
+ * @param road_rt the road roadtype on this tile
+ * @param tram_rt the tram roadtype on this tile
* @param d the direction of the roadstop
*/
-static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d)
+static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, DiagDirection d)
{
MakeStation(t, o, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), d);
- SetRoadTypes(t, rt);
- SetRoadOwner(t, ROADTYPE_ROAD, o);
- SetRoadOwner(t, ROADTYPE_TRAM, o);
+ SetRoadTypes(t, road_rt, tram_rt);
+ SetRoadOwner(t, RTT_ROAD, o);
+ SetRoadOwner(t, RTT_TRAM, o);
}
/**
@@ -602,15 +604,16 @@ static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopTyp
* @param tram the owner of the tram
* @param sid the station to which this tile belongs
* @param rst the type of roadstop to make this tile
- * @param rt the roadtypes on this tile
+ * @param road_rt the road roadtype on this tile
+ * @param tram_rt the tram roadtype on this tile
* @param a the direction of the roadstop
*/
-static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadTypes rt, Axis a)
+static inline void MakeDriveThroughRoadStop(TileIndex t, Owner station, Owner road, Owner tram, StationID sid, RoadStopType rst, RoadType road_rt, RoadType tram_rt, Axis a)
{
MakeStation(t, station, sid, (rst == ROADSTOP_BUS ? STATION_BUS : STATION_TRUCK), GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + a);
- SetRoadTypes(t, rt);
- SetRoadOwner(t, ROADTYPE_ROAD, road);
- SetRoadOwner(t, ROADTYPE_TRAM, tram);
+ SetRoadTypes(t, road_rt, tram_rt);
+ SetRoadOwner(t, RTT_ROAD, road);
+ SetRoadOwner(t, RTT_TRAM, tram);
}
/**
diff --git a/src/table/engines.h b/src/table/engines.h
index a5acd2843..7a40e4f7e 100644
--- a/src/table/engines.h
+++ b/src/table/engines.h
@@ -669,7 +669,7 @@ static const AircraftVehicleInfo _orig_aircraft_vehicle_info[] = {
* Tractive effort coefficient by default is the same as TTDPatch, 0.30*256=76
* Air drag value depends on the top speed of the vehicle.
*/
-#define ROV(a, b, c, d, e, f, g, h) { a, b, c, PR_RUNNING_ROADVEH, d, e, f, g, h, 76, 0, VE_DEFAULT, 0 }
+#define ROV(a, b, c, d, e, f, g, h) { a, b, c, PR_RUNNING_ROADVEH, d, e, f, g, h, 76, 0, VE_DEFAULT, 0, ROADTYPE_ROAD }
static const RoadVehicleInfo _orig_road_vehicle_info[] = {
/* image_index sfx max_speed power
* | cost_factor | | capacity |
diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h
index 0b3c92499..89c289355 100644
--- a/src/table/newgrf_debug_data.h
+++ b/src/table/newgrf_debug_data.h
@@ -11,6 +11,7 @@
#include "../newgrf_house.h"
#include "../newgrf_engine.h"
+#include "../newgrf_roadtype.h"
/* Helper for filling property tables */
#define NIP(prop, base, variable, type, name) { name, (ptrdiff_t)cpp_offsetof(base, variable), cpp_sizeof(base, variable), prop, type }
@@ -58,7 +59,7 @@ static const NIVariable _niv_vehicles[] = {
NIV(0x47, "vehicle cargo info"),
NIV(0x48, "vehicle type info"),
NIV(0x49, "year of construction"),
- NIV(0x4A, "current rail type info"),
+ NIV(0x4A, "current rail/road type info"),
NIV(0x4B, "long date of last service"),
NIV(0x4C, "current max speed"),
NIV(0x4D, "position in articulated vehicle"),
@@ -554,6 +555,48 @@ static const NIFeature _nif_town = {
new NIHTown(),
};
+/*** NewGRF road types ***/
+
+static const NIVariable _niv_roadtypes[] = {
+ NIV(0x40, "terrain type"),
+ NIV(0x41, "enhanced tunnels"),
+ NIV(0x42, "level crossing status"),
+ NIV(0x43, "construction date"),
+ NIV(0x44, "town zone"),
+ NIV_END()
+};
+
+class NIHRoadType : public NIHelper {
+ bool IsInspectable(uint index) const override { return true; }
+ uint GetParent(uint index) const override { return UINT32_MAX; }
+ const void *GetInstance(uint index) const override { return nullptr; }
+ const void *GetSpec(uint index) const override { return nullptr; }
+ void SetStringParameters(uint index) const override { this->SetObjectAtStringParameters(STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE, INVALID_STRING_ID, index); }
+ uint32 GetGRFID(uint index) const override { return 0; }
+
+ uint Resolve(uint index, uint var, uint param, bool *avail) const override
+ {
+ /* There is no unique GRFFile for the tile. Multiple GRFs can define different parts of the railtype.
+ * However, currently the NewGRF Debug GUI does not display variables depending on the GRF (like 0x7F) anyway. */
+ RoadTypeResolverObject ro(nullptr, index, TCX_NORMAL, ROTSG_END);
+ return ro.GetScope(VSG_SCOPE_SELF)->GetVariable(var, param, avail);
+ }
+};
+
+static const NIFeature _nif_roadtype = {
+ nullptr,
+ nullptr,
+ _niv_roadtypes,
+ new NIHRoadType(),
+};
+
+static const NIFeature _nif_tramtype = {
+ nullptr,
+ nullptr,
+ _niv_roadtypes,
+ new NIHRoadType(),
+};
+
/** Table with all NIFeatures. */
static const NIFeature * const _nifeatures[] = {
&_nif_vehicle, // GSF_TRAINS
@@ -574,6 +617,8 @@ static const NIFeature * const _nifeatures[] = {
&_nif_object, // GSF_OBJECTS
&_nif_railtype, // GSF_RAILTYPES
&_nif_airporttile, // GSF_AIRPORTTILES
+ &_nif_roadtype, // GSF_ROADTYPES
+ &_nif_tramtype, // GSF_TRAMTYPES
&_nif_town, // GSF_FAKE_TOWNS
};
assert_compile(lengthof(_nifeatures) == GSF_FAKE_END);
diff --git a/src/table/railtypes.h b/src/table/railtypes.h
index 73b73ff80..829b223b5 100644
--- a/src/table/railtypes.h
+++ b/src/table/railtypes.h
@@ -156,7 +156,7 @@ static const RailtypeInfo _original_railtypes[] = {
STR_RAIL_MENU_ELRAIL_CONSTRUCTION,
STR_BUY_VEHICLE_TRAIN_ELRAIL_CAPTION,
STR_REPLACE_ELRAIL_VEHICLES,
- STR_ENGINE_PREVIEW_RAILROAD_LOCOMOTIVE,
+ STR_ENGINE_PREVIEW_ELRAIL_LOCOMOTIVE,
},
/* Offset of snow tiles */
diff --git a/src/table/road_land.h b/src/table/road_land.h
index 19b8f5711..2b3a4177f 100644
--- a/src/table/road_land.h
+++ b/src/table/road_land.h
@@ -41,35 +41,6 @@ static const DrawTileSprites _road_depot[] = {
{ {0xA4A, PAL_NONE}, _road_depot_NW }
};
-static const DrawTileSeqStruct _tram_depot_NE[] = {
- TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x35) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 15, 16, 1)
- TILE_SEQ_END()
-};
-
-static const DrawTileSeqStruct _tram_depot_SE[] = {
- TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x31) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 0, 1, 16)
- TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x32) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 15, 0, 1, 16)
- TILE_SEQ_END()
-};
-
-static const DrawTileSeqStruct _tram_depot_SW[] = {
- TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x33) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 0, 16, 1)
- TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x34) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 0, 15, 16, 1)
- TILE_SEQ_END()
-};
-
-static const DrawTileSeqStruct _tram_depot_NW[] = {
- TILE_SEQ_LINE((SPR_TRAMWAY_BASE + 0x36) | (1 << PALETTE_MODIFIER_COLOUR), PAL_NONE, 15, 0, 1, 16)
- TILE_SEQ_END()
-};
-
-static const DrawTileSprites _tram_depot[] = {
- { {0xA4A, PAL_NONE}, _tram_depot_NE },
- { {0xA4A, PAL_NONE}, _tram_depot_SE },
- { {0xA4A, PAL_NONE}, _tram_depot_SW },
- { {0xA4A, PAL_NONE}, _tram_depot_NW }
-};
-
/* Sprite layout for level crossings. The SpriteIDs are actually offsets
* from the base SpriteID returned from the NewGRF sprite resolver. */
static const DrawTileSeqStruct _crossing_layout_ALL[] = {
diff --git a/src/table/roadtypes.h b/src/table/roadtypes.h
new file mode 100644
index 000000000..348cdb677
--- /dev/null
+++ b/src/table/roadtypes.h
@@ -0,0 +1,183 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @file roadtypes.h
+ * All the roadtype-specific information is stored here.
+ */
+
+#ifndef ROADTYPES_H
+#define ROADTYPES_H
+
+/**
+ * Global Roadtype definition
+ */
+static const RoadTypeInfo _original_roadtypes[] = {
+ /* Road */
+ {
+ /* GUI sprites */
+ {
+ SPR_IMG_ROAD_X_DIR,
+ SPR_IMG_ROAD_Y_DIR,
+ SPR_IMG_AUTOROAD,
+ SPR_IMG_ROAD_DEPOT,
+ SPR_IMG_ROAD_TUNNEL,
+ SPR_IMG_CONVERT_ROAD,
+ },
+
+ {
+ SPR_CURSOR_ROAD_NESW,
+ SPR_CURSOR_ROAD_NWSE,
+ SPR_CURSOR_AUTOROAD,
+ SPR_CURSOR_ROAD_DEPOT,
+ SPR_CURSOR_TUNNEL_RAIL,
+ SPR_CURSOR_CONVERT_ROAD,
+ },
+
+ /* strings */
+ {
+ STR_ROAD_NAME_ROAD,
+ STR_ROAD_TOOLBAR_ROAD_CONSTRUCTION_CAPTION,
+ STR_ROAD_MENU_ROAD_CONSTRUCTION,
+ STR_BUY_VEHICLE_ROAD_VEHICLE_CAPTION,
+ STR_REPLACE_ROAD_VEHICLES,
+ STR_ENGINE_PREVIEW_ROAD_VEHICLE,
+
+ STR_ERROR_CAN_T_BUILD_ROAD_HERE,
+ STR_ERROR_CAN_T_REMOVE_ROAD_FROM,
+ STR_ERROR_CAN_T_BUILD_ROAD_DEPOT,
+ { STR_ERROR_CAN_T_BUILD_BUS_STATION, STR_ERROR_CAN_T_BUILD_TRUCK_STATION },
+ { STR_ERROR_CAN_T_REMOVE_BUS_STATION, STR_ERROR_CAN_T_REMOVE_TRUCK_STATION },
+ STR_ERROR_CAN_T_CONVERT_ROAD,
+ { STR_STATION_BUILD_BUS_ORIENTATION, STR_STATION_BUILD_TRUCK_ORIENTATION },
+ { STR_STATION_BUILD_BUS_ORIENTATION_TOOLTIP, STR_STATION_BUILD_TRUCK_ORIENTATION_TOOLTIP },
+ },
+
+ /* Powered roadtypes */
+ ROADTYPES_ROAD,
+
+ /* flags */
+ ROTFB_TOWN_BUILD,
+
+ /* cost multiplier */
+ 8,
+
+ /* maintenance cost multiplier */
+ 16,
+
+ /* max speed */
+ 0,
+
+ /* road type label */
+ 'ROAD',
+
+ /* alternate labels */
+ RoadTypeLabelList(),
+
+ /* map colour */
+ 0x01,
+
+ /* introduction date */
+ MIN_YEAR,
+
+ /* roadtypes required for this to be introduced */
+ ROADTYPES_NONE,
+
+ /* introduction road types */
+ ROADTYPES_ROAD,
+
+ /* sort order */
+ 0x07,
+
+ { nullptr },
+ { nullptr },
+ },
+
+ /* Electrified Tram */
+ {
+ /* GUI sprites */
+ {
+ SPR_IMG_TRAMWAY_X_DIR,
+ SPR_IMG_TRAMWAY_Y_DIR,
+ SPR_IMG_AUTOTRAM,
+ SPR_IMG_ROAD_DEPOT,
+ SPR_IMG_ROAD_TUNNEL,
+ SPR_IMG_CONVERT_TRAM,
+ },
+
+ {
+ SPR_CURSOR_TRAMWAY_NESW,
+ SPR_CURSOR_TRAMWAY_NWSE,
+ SPR_CURSOR_AUTOTRAM,
+ SPR_CURSOR_ROAD_DEPOT,
+ SPR_CURSOR_TUNNEL_RAIL,
+ SPR_CURSOR_CONVERT_TRAM,
+ },
+
+ /* strings */
+ {
+ STR_ROAD_NAME_TRAM,
+ STR_ROAD_TOOLBAR_TRAM_CONSTRUCTION_CAPTION,
+ STR_ROAD_MENU_TRAM_CONSTRUCTION,
+ STR_BUY_VEHICLE_TRAM_VEHICLE_CAPTION,
+ STR_REPLACE_TRAM_VEHICLES,
+ STR_ENGINE_PREVIEW_TRAM_VEHICLE,
+
+ STR_ERROR_CAN_T_BUILD_TRAMWAY_HERE,
+ STR_ERROR_CAN_T_REMOVE_TRAMWAY_FROM,
+ STR_ERROR_CAN_T_BUILD_TRAM_DEPOT,
+ { STR_ERROR_CAN_T_BUILD_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_BUILD_CARGO_TRAM_STATION },
+ { STR_ERROR_CAN_T_REMOVE_PASSENGER_TRAM_STATION, STR_ERROR_CAN_T_REMOVE_CARGO_TRAM_STATION },
+ STR_ERROR_CAN_T_CONVERT_TRAMWAY,
+ { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION },
+ { STR_STATION_BUILD_PASSENGER_TRAM_ORIENTATION_TOOLTIP, STR_STATION_BUILD_CARGO_TRAM_ORIENTATION_TOOLTIP },
+ },
+
+ /* Powered roadtypes */
+ ROADTYPES_TRAM,
+
+ /* flags */
+ ROTFB_CATENARY | ROTFB_NO_HOUSES,
+
+ /* cost multiplier */
+ 16,
+
+ /* maintenance cost multiplier */
+ 24,
+
+ /* max speed */
+ 0,
+
+ /* road type label */
+ 'ELRL',
+
+ /* alternate labels */
+ RoadTypeLabelList(),
+
+ /* map colour */
+ 0x01,
+
+ /* introduction date */
+ INVALID_DATE,
+
+ /* roadtypes required for this to be introduced */
+ ROADTYPES_NONE,
+
+ /* introduction road types */
+ ROADTYPES_TRAM,
+
+ /* sort order */
+ 0x17,
+
+ { nullptr },
+ { nullptr },
+ },
+};
+
+#endif /* ROADTYPES_H */
diff --git a/src/table/sprites.h b/src/table/sprites.h
index a57326997..5f5aa4705 100644
--- a/src/table/sprites.h
+++ b/src/table/sprites.h
@@ -56,7 +56,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682;
/** Extra graphic spritenumbers */
static const SpriteID SPR_OPENTTD_BASE = 4896;
-static const uint16 OPENTTD_SPRITE_COUNT = 179;
+static const uint16 OPENTTD_SPRITE_COUNT = 184;
/* Halftile-selection sprites */
static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE;
@@ -273,13 +273,15 @@ static const SpriteID SPR_TRAMWAY_BUS_STOP_DT_X_W = SPR_TRAMWAY_BASE + 24;
static const SpriteID SPR_TRAMWAY_BUS_STOP_DT_X_E = SPR_TRAMWAY_BASE + 26;
static const SpriteID SPR_TRAMWAY_PAVED_STRAIGHT_Y = SPR_TRAMWAY_BASE + 46;
static const SpriteID SPR_TRAMWAY_PAVED_STRAIGHT_X = SPR_TRAMWAY_BASE + 47;
+static const SpriteID SPR_TRAMWAY_DEPOT_WITH_TRACK = SPR_TRAMWAY_BASE + 49;
static const SpriteID SPR_TRAMWAY_BACK_WIRES_STRAIGHT = SPR_TRAMWAY_BASE + 55;
static const SpriteID SPR_TRAMWAY_FRONT_WIRES_STRAIGHT = SPR_TRAMWAY_BASE + 56;
static const SpriteID SPR_TRAMWAY_BACK_WIRES_SLOPED = SPR_TRAMWAY_BASE + 72;
static const SpriteID SPR_TRAMWAY_FRONT_WIRES_SLOPED = SPR_TRAMWAY_BASE + 68;
static const SpriteID SPR_TRAMWAY_TUNNEL_WIRES = SPR_TRAMWAY_BASE + 80;
static const SpriteID SPR_TRAMWAY_BRIDGE = SPR_TRAMWAY_BASE + 107;
-static const uint16 TRAMWAY_SPRITE_COUNT = 113;
+static const SpriteID SPR_TRAMWAY_DEPOT_NO_TRACK = SPR_TRAMWAY_BASE + 113;
+static const uint16 TRAMWAY_SPRITE_COUNT = 119;
/** One way road sprites */
static const SpriteID SPR_ONEWAY_BASE = SPR_TRAMWAY_BASE + TRAMWAY_SPRITE_COUNT;
@@ -563,6 +565,7 @@ static const SpriteID SPR_ROAD_SLOPE_START = 1343;
static const SpriteID SPR_ROAD_Y_SNOW = 1351;
static const SpriteID SPR_ROAD_X_SNOW = 1352;
/* see _road_sloped_sprites_offset in road_cmd.cpp for offsets for sloped road tiles */
+static const SpriteID SPR_ROAD_DEPOT = 1408;
static const SpriteID SPR_EXCAVATION_X = 1414;
static const SpriteID SPR_EXCAVATION_Y = 1415;
@@ -1099,6 +1102,7 @@ static const SpriteID SPR_IMG_ZOOMIN = 735;
static const SpriteID SPR_IMG_ZOOMOUT = 736;
static const SpriteID SPR_IMG_BUILDRAIL = 727;
static const SpriteID SPR_IMG_BUILDROAD = 728;
+static const SpriteID SPR_IMG_BUILDTRAMS = SPR_OPENTTD_BASE + 179;
static const SpriteID SPR_IMG_BUILDWATER = 729;
static const SpriteID SPR_IMG_BUILDAIR = 730;
static const SpriteID SPR_IMG_LANDSCAPING = 4083;
@@ -1343,6 +1347,11 @@ static const SpriteID SPR_IMG_GOAL = SPR_OPENTTD_BASE + 171;
static const SpriteID SPR_IMG_GOAL_COMPLETED = SPR_OPENTTD_BASE + 172;
static const SpriteID SPR_IMG_GOAL_BROKEN_REF= SPR_OPENTTD_BASE + 173;
+static const SpriteID SPR_IMG_CONVERT_ROAD = SPR_OPENTTD_BASE + 180;
+static const CursorID SPR_CURSOR_CONVERT_ROAD = SPR_OPENTTD_BASE + 181;
+static const SpriteID SPR_IMG_CONVERT_TRAM = SPR_OPENTTD_BASE + 182;
+static const CursorID SPR_CURSOR_CONVERT_TRAM = SPR_OPENTTD_BASE + 183;
+
/* intro_gui.cpp, genworld_gui.cpp */
static const SpriteID SPR_SELECT_TEMPERATE = 4882;
static const SpriteID SPR_SELECT_TEMPERATE_PUSHED = 4883;
diff --git a/src/tile_cmd.h b/src/tile_cmd.h
index 855aa297d..80f8074ac 100644
--- a/src/tile_cmd.h
+++ b/src/tile_cmd.h
@@ -64,7 +64,10 @@ struct TileDesc {
uint64 dparam[2]; ///< Parameters of the \a str string
StringID railtype; ///< Type of rail on the tile.
uint16 rail_speed; ///< Speed limit of rail (bridges and track)
- uint16 road_speed; ///< Speed limit of road (bridges)
+ StringID roadtype; ///< Type of road on the tile.
+ uint16 road_speed; ///< Speed limit of road (bridges and track)
+ StringID tramtype; ///< Type of tram on the tile.
+ uint16 tram_speed; ///< Speed limit of tram (bridges and track)
};
/**
diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp
index 7cd5facf1..23c10fb72 100644
--- a/src/toolbar_gui.cpp
+++ b/src/toolbar_gui.cpp
@@ -17,6 +17,7 @@
#include "command_func.h"
#include "vehicle_gui.h"
#include "rail_gui.h"
+#include "road.h"
#include "road_gui.h"
#include "date_func.h"
#include "vehicle_func.h"
@@ -64,6 +65,7 @@ uint _toolbar_width = 0;
RailType _last_built_railtype;
RoadType _last_built_roadtype;
+RoadType _last_built_tramtype;
static ScreenshotType _confirmed_screenshot_type; ///< Screenshot type the current query is about to confirm.
@@ -893,22 +895,7 @@ static CallBackFunction MenuClickBuildRail(int index)
static CallBackFunction ToolbarBuildRoadClick(Window *w)
{
- const Company *c = Company::Get(_local_company);
- DropDownList list;
-
- /* Road is always visible and available. */
- list.emplace_back(new DropDownListIconItem(SPR_IMG_ROAD_X_DIR, PAL_NONE, STR_ROAD_MENU_ROAD_CONSTRUCTION, ROADTYPE_ROAD, false));
-
- /* Tram is only visible when there will be a tram, and available when that has been introduced. */
- Engine *e;
- FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
- if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue;
- if (!HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue;
-
- list.emplace_back(new DropDownListIconItem(SPR_IMG_TRAMWAY_X_DIR, PAL_NONE, STR_ROAD_MENU_TRAM_CONSTRUCTION, ROADTYPE_TRAM, !HasBit(c->avail_roadtypes, ROADTYPE_TRAM)));
- break;
- }
- ShowDropDownList(w, std::move(list), _last_built_roadtype, WID_TN_ROADS, 140, true, true);
+ ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TN_ROADS, 140, true, true);
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
return CBF_NONE;
}
@@ -926,6 +913,28 @@ static CallBackFunction MenuClickBuildRoad(int index)
return CBF_NONE;
}
+/* --- Tram button menu --- */
+
+static CallBackFunction ToolbarBuildTramClick(Window *w)
+{
+ ShowDropDownList(w, GetRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TN_TRAMS, 140, true, true);
+ if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
+ return CBF_NONE;
+}
+
+/**
+ * Handle click on the entry in the Build Tram menu.
+ *
+ * @param index RoadType to show the build toolbar for.
+ * @return #CBF_NONE
+ */
+static CallBackFunction MenuClickBuildTram(int index)
+{
+ _last_built_tramtype = (RoadType)index;
+ ShowBuildRoadToolbar(_last_built_tramtype);
+ return CBF_NONE;
+}
+
/* --- Water button menu --- */
static CallBackFunction ToolbarBuildWaterClick(Window *w)
@@ -1251,9 +1260,41 @@ static CallBackFunction ToolbarScenGenIndustry(Window *w)
static CallBackFunction ToolbarScenBuildRoadClick(Window *w)
{
- w->HandleButtonClick(WID_TE_ROADS);
+ ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_ROAD), _last_built_roadtype, WID_TE_ROADS, 140, true, true);
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
- ShowBuildRoadScenToolbar();
+ return CBF_NONE;
+}
+
+/**
+ * Handle click on the entry in the Build Road menu.
+ *
+ * @param index RoadType to show the build toolbar for.
+ * @return #CBF_NONE
+ */
+static CallBackFunction ToolbarScenBuildRoad(int index)
+{
+ _last_built_roadtype = (RoadType)index;
+ ShowBuildRoadScenToolbar(_last_built_roadtype);
+ return CBF_NONE;
+}
+
+static CallBackFunction ToolbarScenBuildTramClick(Window *w)
+{
+ ShowDropDownList(w, GetScenRoadTypeDropDownList(RTTB_TRAM), _last_built_tramtype, WID_TE_TRAMS, 140, true, true);
+ if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
+ return CBF_NONE;
+}
+
+/**
+ * Handle click on the entry in the Build Tram menu.
+ *
+ * @param index RoadType to show the build toolbar for.
+ * @return #CBF_NONE
+ */
+static CallBackFunction ToolbarScenBuildTram(int index)
+{
+ _last_built_tramtype = (RoadType)index;
+ ShowBuildRoadScenToolbar(_last_built_tramtype);
return CBF_NONE;
}
@@ -1311,12 +1352,13 @@ static MenuClickedProc * const _menu_clicked_procs[] = {
nullptr, // 20
MenuClickBuildRail, // 21
MenuClickBuildRoad, // 22
- MenuClickBuildWater, // 23
- MenuClickBuildAir, // 24
- MenuClickForest, // 25
- MenuClickMusicWindow, // 26
- MenuClickNewspaper, // 27
- MenuClickHelp, // 28
+ MenuClickBuildTram, // 23
+ MenuClickBuildWater, // 24
+ MenuClickBuildAir, // 25
+ MenuClickForest, // 26
+ MenuClickMusicWindow, // 27
+ MenuClickNewspaper, // 28
+ MenuClickHelp, // 29
};
/** Full blown container to make it behave exactly as we want :) */
@@ -1772,6 +1814,7 @@ class NWidgetMainToolbarContainer : public NWidgetToolbarContainer {
WID_TN_ZOOM_OUT,
WID_TN_RAILS,
WID_TN_ROADS,
+ WID_TN_TRAMS,
WID_TN_WATER,
WID_TN_AIR,
WID_TN_LANDSCAPE,
@@ -1832,6 +1875,7 @@ class NWidgetScenarioToolbarContainer : public NWidgetToolbarContainer {
WID_TE_TOWN_GENERATE,
WID_TE_INDUSTRY,
WID_TE_ROADS,
+ WID_TE_TRAMS,
WID_TE_WATER,
WID_TE_TREES,
WID_TE_SIGNS,
@@ -1851,6 +1895,7 @@ class NWidgetScenarioToolbarContainer : public NWidgetToolbarContainer {
WID_TE_TOWN_GENERATE,
WID_TE_INDUSTRY,
WID_TE_ROADS,
+ WID_TE_TRAMS,
WID_TE_WATER,
WID_TE_TREES,
WID_TE_SIGNS,
@@ -1864,6 +1909,7 @@ class NWidgetScenarioToolbarContainer : public NWidgetToolbarContainer {
WID_TE_TOWN_GENERATE,
WID_TE_INDUSTRY,
WID_TE_ROADS,
+ WID_TE_TRAMS,
WID_TE_WATER,
WID_TE_TREES,
WID_TE_SIGNS,
@@ -1939,6 +1985,7 @@ static ToolbarButtonProc * const _toolbar_button_procs[] = {
ToolbarZoomOutClick,
ToolbarBuildRailClick,
ToolbarBuildRoadClick,
+ ToolbarBuildTramClick,
ToolbarBuildWaterClick,
ToolbarBuildAirClick,
ToolbarForestClick,
@@ -1973,6 +2020,7 @@ enum MainToolbarHotkeys {
MTHK_ZOOM_OUT,
MTHK_BUILD_RAIL,
MTHK_BUILD_ROAD,
+ MTHK_BUILD_TRAM,
MTHK_BUILD_DOCKS,
MTHK_BUILD_AIRPORT,
MTHK_BUILD_TREES,
@@ -2017,7 +2065,7 @@ struct MainToolbarWindow : Window {
/* If spectator, disable all construction buttons
* ie : Build road, rail, ships, airports and landscaping
* Since enabled state is the default, just disable when needed */
- this->SetWidgetsDisabledState(_local_company == COMPANY_SPECTATOR, WID_TN_RAILS, WID_TN_ROADS, WID_TN_WATER, WID_TN_AIR, WID_TN_LANDSCAPE, WIDGET_LIST_END);
+ this->SetWidgetsDisabledState(_local_company == COMPANY_SPECTATOR, WID_TN_RAILS, WID_TN_ROADS, WID_TN_TRAMS, WID_TN_WATER, WID_TN_AIR, WID_TN_LANDSCAPE, WIDGET_LIST_END);
/* disable company list drop downs, if there are no companies */
this->SetWidgetsDisabledState(Company::GetNumItems() == 0, WID_TN_STATIONS, WID_TN_FINANCES, WID_TN_TRAINS, WID_TN_ROADVEHS, WID_TN_SHIPS, WID_TN_AIRCRAFT, WIDGET_LIST_END);
@@ -2025,6 +2073,8 @@ struct MainToolbarWindow : Window {
this->SetWidgetDisabledState(WID_TN_STORY, StoryPage::GetNumItems() == 0);
this->SetWidgetDisabledState(WID_TN_RAILS, !CanBuildVehicleInfrastructure(VEH_TRAIN));
+ this->SetWidgetDisabledState(WID_TN_ROADS, !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_ROAD));
+ this->SetWidgetDisabledState(WID_TN_TRAMS, !CanBuildVehicleInfrastructure(VEH_ROAD, RTT_TRAM));
this->SetWidgetDisabledState(WID_TN_AIR, !CanBuildVehicleInfrastructure(VEH_AIRCRAFT));
this->DrawWidgets();
@@ -2068,6 +2118,7 @@ struct MainToolbarWindow : Window {
case MTHK_ZOOM_OUT: ToolbarZoomOutClick(this); break;
case MTHK_BUILD_RAIL: if (CanBuildVehicleInfrastructure(VEH_TRAIN)) ShowBuildRailToolbar(_last_built_railtype); break;
case MTHK_BUILD_ROAD: ShowBuildRoadToolbar(_last_built_roadtype); break;
+ case MTHK_BUILD_TRAM: if (CanBuildVehicleInfrastructure(VEH_ROAD, RTT_TRAM)) ShowBuildRoadToolbar(_last_built_tramtype); break;
case MTHK_BUILD_DOCKS: ShowBuildDocksToolbar(); break;
case MTHK_BUILD_AIRPORT: if (CanBuildVehicleInfrastructure(VEH_AIRCRAFT)) ShowBuildAirToolbar(); break;
case MTHK_BUILD_TREES: ShowBuildTreesToolbar(); break;
@@ -2179,6 +2230,7 @@ static Hotkey maintoolbar_hotkeys[] = {
Hotkey(_maintoolbar_zoomout_keys, "zoomout", MTHK_ZOOM_OUT),
Hotkey(WKC_SHIFT | WKC_F7, "build_rail", MTHK_BUILD_RAIL),
Hotkey(WKC_SHIFT | WKC_F8, "build_road", MTHK_BUILD_ROAD),
+ Hotkey((uint16)0, "build_tram", MTHK_BUILD_TRAM),
Hotkey(WKC_SHIFT | WKC_F9, "build_docks", MTHK_BUILD_DOCKS),
Hotkey(WKC_SHIFT | WKC_F10, "build_airport", MTHK_BUILD_AIRPORT),
Hotkey(WKC_SHIFT | WKC_F11, "build_trees", MTHK_BUILD_TREES),
@@ -2224,6 +2276,7 @@ static NWidgetBase *MakeMainToolbar(int *biggest_index)
SPR_IMG_ZOOMOUT, // WID_TN_ZOOMOUT
SPR_IMG_BUILDRAIL, // WID_TN_RAILS
SPR_IMG_BUILDROAD, // WID_TN_ROADS
+ SPR_IMG_BUILDTRAMS, // WID_TN_TRAMS
SPR_IMG_BUILDWATER, // WID_TN_WATER
SPR_IMG_BUILDAIR, // WID_TN_AIR
SPR_IMG_LANDSCAPING, // WID_TN_LANDSCAPE
@@ -2282,14 +2335,15 @@ static MenuClickedProc * const _scen_toolbar_dropdown_procs[] = {
nullptr, // 11
nullptr, // 12
nullptr, // 13
- nullptr, // 14
- nullptr, // 15
+ ToolbarScenBuildRoad, // 14
+ ToolbarScenBuildTram, // 15
nullptr, // 16
nullptr, // 17
nullptr, // 18
- MenuClickMusicWindow, // 19
- MenuClickHelp, // 20
- nullptr, // 21
+ nullptr, // 19
+ MenuClickMusicWindow, // 20
+ MenuClickHelp, // 21
+ nullptr, // 22
};
static ToolbarButtonProc * const _scen_toolbar_button_procs[] = {
@@ -2308,6 +2362,7 @@ static ToolbarButtonProc * const _scen_toolbar_button_procs[] = {
ToolbarScenGenTown,
ToolbarScenGenIndustry,
ToolbarScenBuildRoadClick,
+ ToolbarScenBuildTramClick,
ToolbarScenBuildDocks,
ToolbarScenPlantTrees,
ToolbarScenPlaceSign,
@@ -2326,6 +2381,7 @@ enum MainToolbarEditorHotkeys {
MTEHK_GENTOWN,
MTEHK_GENINDUSTRY,
MTEHK_BUILD_ROAD,
+ MTEHK_BUILD_TRAM,
MTEHK_BUILD_DOCKS,
MTEHK_BUILD_TREES,
MTEHK_SIGN,
@@ -2366,6 +2422,8 @@ struct ScenarioEditorToolbarWindow : Window {
{
this->SetWidgetDisabledState(WID_TE_DATE_BACKWARD, _settings_game.game_creation.starting_year <= MIN_YEAR);
this->SetWidgetDisabledState(WID_TE_DATE_FORWARD, _settings_game.game_creation.starting_year >= MAX_YEAR);
+ this->SetWidgetDisabledState(WID_TE_ROADS, (GetRoadTypes(true) & ~_roadtypes_type) == ROADTYPES_NONE);
+ this->SetWidgetDisabledState(WID_TE_TRAMS, (GetRoadTypes(true) & _roadtypes_type) == ROADTYPES_NONE);
this->DrawWidgets();
}
@@ -2432,6 +2490,7 @@ struct ScenarioEditorToolbarWindow : Window {
case MTEHK_GENTOWN: ToolbarScenGenTown(this); break;
case MTEHK_GENINDUSTRY: ToolbarScenGenIndustry(this); break;
case MTEHK_BUILD_ROAD: ToolbarScenBuildRoadClick(this); break;
+ case MTEHK_BUILD_TRAM: ToolbarScenBuildTramClick(this); break;
case MTEHK_BUILD_DOCKS: ToolbarScenBuildDocks(this); break;
case MTEHK_BUILD_TREES: ToolbarScenPlantTrees(this); break;
case MTEHK_SIGN: cbf = ToolbarScenPlaceSign(this); break;
@@ -2535,6 +2594,7 @@ static Hotkey scenedit_maintoolbar_hotkeys[] = {
Hotkey(WKC_F5, "gen_town", MTEHK_GENTOWN),
Hotkey(WKC_F6, "gen_industry", MTEHK_GENINDUSTRY),
Hotkey(WKC_F7, "build_road", MTEHK_BUILD_ROAD),
+ Hotkey((uint16)0, "build_tram", MTEHK_BUILD_TRAM),
Hotkey(WKC_F8, "build_docks", MTEHK_BUILD_DOCKS),
Hotkey(WKC_F9, "build_trees", MTEHK_BUILD_TREES),
Hotkey(WKC_F10, "build_sign", MTEHK_SIGN),
@@ -2577,7 +2637,8 @@ static const NWidgetPart _nested_toolb_scen_inner_widgets[] = {
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_LAND_GENERATE), SetDataTip(SPR_IMG_LANDSCAPING, STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TOWN_GENERATE), SetDataTip(SPR_IMG_TOWN, STR_SCENEDIT_TOOLBAR_TOWN_GENERATION),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_INDUSTRY), SetDataTip(SPR_IMG_INDUSTRY, STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION),
- NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ROADS), SetDataTip(SPR_IMG_BUILDROAD, STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION),
+ NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_ROADS), SetDataTip(SPR_IMG_BUILDROAD, STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION),
+ NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_TRAMS), SetDataTip(SPR_IMG_BUILDTRAMS, STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_WATER), SetDataTip(SPR_IMG_BUILDWATER, STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TREES), SetDataTip(SPR_IMG_PLANTTREES, STR_SCENEDIT_TOOLBAR_PLANT_TREES),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_SIGNS), SetDataTip(SPR_IMG_SIGN, STR_SCENEDIT_TOOLBAR_PLACE_SIGN),
@@ -2609,6 +2670,7 @@ void AllocateToolbar()
{
/* Clean old GUI values; railtype is (re)set by rail_gui.cpp */
_last_built_roadtype = ROADTYPE_ROAD;
+ _last_built_tramtype = ROADTYPE_TRAM;
if (_game_mode == GM_EDITOR) {
new ScenarioEditorToolbarWindow(&_toolb_scen_desc);
diff --git a/src/town.h b/src/town.h
index 5f0f4992f..d669ccafb 100644
--- a/src/town.h
+++ b/src/town.h
@@ -299,4 +299,6 @@ static inline uint16 TownTicksToGameTicks(uint16 ticks) {
extern CargoTypes _town_cargoes_accepted;
+RoadType GetTownRoadType(const Town *t);
+
#endif /* TOWN_H */
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index f3acfb4a3..65032c1fb 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -10,6 +10,7 @@
/** @file town_cmd.cpp Handling of town tiles. */
#include "stdafx.h"
+#include "road.h"
#include "road_internal.h" /* Cleaning up road bits */
#include "road_cmd.h"
#include "landscape.h"
@@ -891,7 +892,38 @@ static RoadBits GetTownRoadBits(TileIndex tile)
{
if (IsRoadDepotTile(tile) || IsStandardRoadStopTile(tile)) return ROAD_NONE;
- return GetAnyRoadBits(tile, ROADTYPE_ROAD, true);
+ return GetAnyRoadBits(tile, RTT_ROAD, true);
+}
+
+RoadType GetTownRoadType(const Town *t)
+{
+ RoadType best_rt = ROADTYPE_ROAD;
+ const RoadTypeInfo *best = nullptr;
+ const uint16 assume_max_speed = 50;
+
+ for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
+ if (RoadTypeIsTram(rt)) continue;
+
+ const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
+
+ /* Unused road type. */
+ if (rti->label == 0) continue;
+
+ /* Can town build this road. */
+ if (!HasBit(rti->flags, ROTF_TOWN_BUILD)) continue;
+
+ /* Not yet introduced at this date. */
+ if (IsInsideMM(rti->introduction_date, 0, MAX_DAY) && rti->introduction_date > _date) continue;
+
+ if (best != nullptr) {
+ if ((rti->max_speed == 0 ? assume_max_speed : rti->max_speed) < (best->max_speed == 0 ? assume_max_speed : best->max_speed)) continue;
+ }
+
+ best_rt = rt;
+ best = rti;
+ }
+
+ return best_rt;
}
/**
@@ -950,7 +982,8 @@ static bool IsRoadAllowedHere(Town *t, TileIndex tile, DiagDirection dir)
/* No, try if we are able to build a road piece there.
* If that fails clear the land, and if that fails exit.
* This is to make sure that we can build a road here later. */
- if (DoCommand(tile, ((dir == DIAGDIR_NW || dir == DIAGDIR_SE) ? ROAD_Y : ROAD_X), 0, DC_AUTO, CMD_BUILD_ROAD).Failed() &&
+ RoadType rt = GetTownRoadType(t);
+ if (DoCommand(tile, ((dir == DIAGDIR_NW || dir == DIAGDIR_SE) ? ROAD_Y : ROAD_X) | (rt << 4), 0, DC_AUTO, CMD_BUILD_ROAD).Failed() &&
DoCommand(tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR).Failed()) {
return false;
}
@@ -1118,7 +1151,8 @@ static bool GrowTownWithExtraHouse(Town *t, TileIndex tile)
*/
static bool GrowTownWithRoad(const Town *t, TileIndex tile, RoadBits rcmd)
{
- if (DoCommand(tile, rcmd, t->index, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded()) {
+ RoadType rt = GetTownRoadType(t);
+ if (DoCommand(tile, rcmd | (rt << 4), t->index, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded()) {
_grow_town_result = GROWTH_SUCCEED;
return true;
}
@@ -1181,8 +1215,9 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi
byte bridge_type = RandomRange(MAX_BRIDGES - 1);
/* Can we actually build the bridge? */
- if (DoCommand(tile, bridge_tile, bridge_type | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE).Succeeded()) {
- DoCommand(tile, bridge_tile, bridge_type | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE);
+ RoadType rt = GetTownRoadType(t);
+ if (DoCommand(tile, bridge_tile, bridge_type | rt << 8 | TRANSPORT_ROAD << 15, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE).Succeeded()) {
+ DoCommand(tile, bridge_tile, bridge_type | rt << 8 | TRANSPORT_ROAD << 15, DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE);
_grow_town_result = GROWTH_SUCCEED;
return true;
}
@@ -1191,6 +1226,37 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi
return false;
}
+
+/**
+ * Checks whether at least one surrounding roads allows to build a house here
+ *
+ * @param t the tile where the house will be built
+ * @return true if at least one surrounding roadtype allows building houses here
+ */
+static inline bool RoadTypesAllowHouseHere(TileIndex t)
+{
+ static const TileIndexDiffC tiles[] = { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1} };
+ bool allow = false;
+
+ for (const TileIndexDiffC *ptr = tiles; ptr != endof(tiles); ++ptr) {
+ TileIndex cur_tile = t + ToTileIndexDiff(*ptr);
+ if (!IsValidTile(cur_tile)) continue;
+
+ if (!(IsTileType(cur_tile, MP_ROAD) || IsTileType(cur_tile, MP_STATION))) continue;
+ allow = true;
+
+ RoadType road_rt = GetRoadTypeRoad(cur_tile);
+ RoadType tram_rt = GetRoadTypeTram(cur_tile);
+ if (road_rt != INVALID_ROADTYPE && !HasBit(GetRoadTypeInfo(road_rt)->flags, ROTF_NO_HOUSES)) return true;
+ if (tram_rt != INVALID_ROADTYPE && !HasBit(GetRoadTypeInfo(tram_rt)->flags, ROTF_NO_HOUSES)) return true;
+ }
+
+ /* If no road was found surrounding the tile we can allow building the house since there is
+ * nothing which forbids it, if a road was found but the execution reached this point, then
+ * all the found roads don't allow houses to be built */
+ return !allow;
+}
+
/**
* Grows the given town.
* There are at the moment 3 possible way's for
@@ -1369,6 +1435,8 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
}
}
+ allow_house &= RoadTypesAllowHouseHere(house_tile);
+
if (allow_house) {
/* Build a house, but not if there already is a house there. */
if (!IsTileType(house_tile, MP_HOUSE)) {
@@ -1508,14 +1576,14 @@ static bool GrowTownAtRoad(Town *t, TileIndex tile)
}
tile = TileAddByDiagDir(tile, target_dir);
- if (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile) && HasTileRoadType(tile, ROADTYPE_ROAD)) {
+ if (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile) && HasTileRoadType(tile, RTT_ROAD)) {
/* Don't allow building over roads of other cities */
- if (IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN) && Town::GetByTile(tile) != t) {
+ if (IsRoadOwner(tile, RTT_ROAD, OWNER_TOWN) && Town::GetByTile(tile) != t) {
return false;
- } else if (IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_NONE) && _game_mode == GM_EDITOR) {
+ } else if (IsRoadOwner(tile, RTT_ROAD, OWNER_NONE) && _game_mode == GM_EDITOR) {
/* If we are in the SE, and this road-piece has no town owner yet, it just found an
* owner :) (happy happy happy road now) */
- SetRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN);
+ SetRoadOwner(tile, RTT_ROAD, OWNER_TOWN);
SetTownIndex(tile, t->index);
}
}
@@ -1589,7 +1657,8 @@ static bool GrowTown(Town *t)
/* Only work with plain land that not already has a house */
if (!IsTileType(tile, MP_HOUSE) && IsTileFlat(tile)) {
if (DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR).Succeeded()) {
- DoCommand(tile, GenRandomRoadBits(), t->index, DC_EXEC | DC_AUTO, CMD_BUILD_ROAD);
+ RoadType rt = GetTownRoadType(t);
+ DoCommand(tile, GenRandomRoadBits() | (rt << 4), t->index, DC_EXEC | DC_AUTO, CMD_BUILD_ROAD);
cur_company.Restore();
return true;
}
@@ -2192,6 +2261,9 @@ static inline bool CanBuildHouseHere(TileIndex tile, bool noslope)
Slope slope = GetTileSlope(tile);
if ((noslope && slope != SLOPE_FLAT) || IsSteepSlope(slope)) return false;
+ /* at least one RoadTypes allow building the house here? */
+ if (!RoadTypesAllowHouseHere(tile)) return false;
+
/* building under a bridge? */
if (IsBridgeAbove(tile)) return false;
diff --git a/src/tunnel_map.h b/src/tunnel_map.h
index d6f475d05..a1df77f3c 100644
--- a/src/tunnel_map.h
+++ b/src/tunnel_map.h
@@ -48,7 +48,7 @@ bool IsTunnelInWayDir(TileIndex tile, int z, DiagDirection dir);
* @param d the direction facing out of the tunnel
* @param r the road type used in the tunnel
*/
-static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTypes r)
+static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadType road_rt, RoadType tram_rt)
{
SetTileType(t, MP_TUNNELBRIDGE);
SetTileOwner(t, o);
@@ -59,9 +59,9 @@ static inline void MakeRoadTunnel(TileIndex t, Owner o, DiagDirection d, RoadTyp
SB(_me[t].m6, 2, 4, 0);
_me[t].m7 = 0;
_me[t].m8 = 0;
- SetRoadOwner(t, ROADTYPE_ROAD, o);
- if (o != OWNER_TOWN) SetRoadOwner(t, ROADTYPE_TRAM, o);
- SetRoadTypes(t, r);
+ SetRoadOwner(t, RTT_ROAD, o);
+ if (o != OWNER_TOWN) SetRoadOwner(t, RTT_TRAM, o);
+ SetRoadTypes(t, road_rt, tram_rt);
}
/**
diff --git a/src/tunnelbridge.h b/src/tunnelbridge.h
index 0a2c2293d..93603acb5 100644
--- a/src/tunnelbridge.h
+++ b/src/tunnelbridge.h
@@ -13,6 +13,7 @@
#define TUNNELBRIDGE_H
#include "map_func.h"
+#include "tile_map.h"
void MarkBridgeDirty(TileIndex begin, TileIndex end, DiagDirection direction, uint bridge_height);
void MarkBridgeDirty(TileIndex tile);
@@ -33,6 +34,18 @@ static inline uint GetTunnelBridgeLength(TileIndex begin, TileIndex end)
return abs(x2 + y2 - x1 - y1) - 1;
}
+/**
+ * Sets the ownership of the bridge/tunnel ramps
+ * @param begin The begin of the tunnel or bridge.
+ * @param end The end of the tunnel or bridge.
+ * @param owner The new owner to set
+ */
+static inline void SetTunnelBridgeOwner(TileIndex begin, TileIndex end, Owner owner)
+{
+ SetTileOwner(begin, owner);
+ SetTileOwner(end, owner);
+}
+
extern TileIndex _build_tunnel_endtile;
#endif /* TUNNELBRIDGE_H */
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index 78586b1a2..83cec4fd3 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -37,6 +37,7 @@
#include "pbs.h"
#include "company_base.h"
#include "newgrf_railtype.h"
+#include "newgrf_roadtype.h"
#include "object_base.h"
#include "water.h"
#include "company_gui.h"
@@ -230,7 +231,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
CompanyID company = _current_company;
RailType railtype = INVALID_RAILTYPE;
- RoadTypes roadtypes = ROADTYPES_NONE;
+ RoadType roadtype = INVALID_ROADTYPE;
/* unpack parameters */
BridgeType bridge_type = GB(p2, 0, 8);
@@ -242,8 +243,8 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
/* type of bridge */
switch (transport_type) {
case TRANSPORT_ROAD:
- roadtypes = Extract<RoadTypes, 8, 2>(p2);
- if (!HasExactlyOneBit(roadtypes) || !HasRoadTypesAvail(company, roadtypes)) return CMD_ERROR;
+ roadtype = Extract<RoadType, 8, 6>(p2);
+ if (!ValParamRoadType(roadtype)) return CMD_ERROR;
break;
case TRANSPORT_RAIL:
@@ -313,16 +314,41 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
CommandCost cost(EXPENSES_CONSTRUCTION);
Owner owner;
bool is_new_owner;
+ RoadType road_rt = INVALID_ROADTYPE;
+ RoadType tram_rt = INVALID_ROADTYPE;
if (IsBridgeTile(tile_start) && IsBridgeTile(tile_end) &&
GetOtherBridgeEnd(tile_start) == tile_end &&
GetTunnelBridgeTransportType(tile_start) == transport_type) {
/* Replace a current bridge. */
+ switch (transport_type) {
+ case TRANSPORT_RAIL:
+ /* Keep the reservation, the path stays valid. */
+ pbs_reservation = HasTunnelBridgeReservation(tile_start);
+ break;
+
+ case TRANSPORT_ROAD:
+ /* Do not remove road types when upgrading a bridge */
+ road_rt = GetRoadTypeRoad(tile_start);
+ tram_rt = GetRoadTypeTram(tile_start);
+ break;
+
+ default: break;
+ }
+
/* If this is a railway bridge, make sure the railtypes match. */
if (transport_type == TRANSPORT_RAIL && GetRailType(tile_start) != railtype) {
return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
}
+ /* If this is a road bridge, make sure the roadtype matches. */
+ if (transport_type == TRANSPORT_ROAD) {
+ RoadType existing_rt = RoadTypeIsRoad(roadtype) ? road_rt : tram_rt;
+ if (existing_rt != roadtype && existing_rt != INVALID_ROADTYPE) {
+ return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
+ }
+ }
+
/* Do not replace town bridges with lower speed bridges, unless in scenario editor. */
if (!(flags & DC_QUERY_COST) && IsTileOwner(tile_start, OWNER_TOWN) &&
GetBridgeSpec(bridge_type)->speed < GetBridgeSpec(GetBridgeType(tile_start))->speed &&
@@ -338,7 +364,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
}
/* Do not replace the bridge with the same bridge type. */
- if (!(flags & DC_QUERY_COST) && (bridge_type == GetBridgeType(tile_start)) && (transport_type != TRANSPORT_ROAD || (roadtypes & ~GetRoadTypes(tile_start)) == 0)) {
+ if (!(flags & DC_QUERY_COST) && (bridge_type == GetBridgeType(tile_start)) && (transport_type != TRANSPORT_ROAD || road_rt == roadtype || tram_rt == roadtype)) {
return_cmd_error(STR_ERROR_ALREADY_BUILT);
}
@@ -353,20 +379,6 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
/* If bridge belonged to bankrupt company, it has a new owner now */
is_new_owner = (owner == OWNER_NONE);
if (is_new_owner) owner = company;
-
- switch (transport_type) {
- case TRANSPORT_RAIL:
- /* Keep the reservation, the path stays valid. */
- pbs_reservation = HasTunnelBridgeReservation(tile_start);
- break;
-
- case TRANSPORT_ROAD:
- /* Do not remove road types when upgrading a bridge */
- roadtypes |= GetRoadTypes(tile_start);
- break;
-
- default: break;
- }
} else {
/* Build a new bridge. */
@@ -471,6 +483,13 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
is_new_owner = true;
}
+ bool hasroad = road_rt != INVALID_ROADTYPE;
+ bool hastram = tram_rt != INVALID_ROADTYPE;
+ if (transport_type == TRANSPORT_ROAD) {
+ if (RoadTypeIsRoad(roadtype)) road_rt = roadtype;
+ if (RoadTypeIsTram(roadtype)) tram_rt = roadtype;
+ }
+
/* do the drill? */
if (flags & DC_EXEC) {
DiagDirection dir = AxisToDiagDir(direction);
@@ -487,24 +506,26 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
break;
case TRANSPORT_ROAD: {
- RoadTypes prev_roadtypes = IsBridgeTile(tile_start) ? GetRoadTypes(tile_start) : ROADTYPES_NONE;
if (is_new_owner) {
/* Also give unowned present roadtypes to new owner */
- if (HasBit(prev_roadtypes, ROADTYPE_ROAD) && GetRoadOwner(tile_start, ROADTYPE_ROAD) == OWNER_NONE) ClrBit(prev_roadtypes, ROADTYPE_ROAD);
- if (HasBit(prev_roadtypes, ROADTYPE_TRAM) && GetRoadOwner(tile_start, ROADTYPE_TRAM) == OWNER_NONE) ClrBit(prev_roadtypes, ROADTYPE_TRAM);
+ if (hasroad && GetRoadOwner(tile_start, RTT_ROAD) == OWNER_NONE) hasroad = false;
+ if (hastram && GetRoadOwner(tile_start, RTT_TRAM) == OWNER_NONE) hastram = false;
}
if (c != nullptr) {
/* Add all new road types to the company infrastructure counter. */
- RoadType new_rt;
- FOR_EACH_SET_ROADTYPE(new_rt, roadtypes ^ prev_roadtypes) {
+ if (!hasroad && road_rt != INVALID_ROADTYPE) {
/* A full diagonal road tile has two road bits. */
- c->infrastructure.road[new_rt] += (bridge_len + 2) * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ c->infrastructure.road[road_rt] += (bridge_len + 2) * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
+ }
+ if (!hastram && tram_rt != INVALID_ROADTYPE) {
+ /* A full diagonal road tile has two road bits. */
+ c->infrastructure.road[tram_rt] += (bridge_len + 2) * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
}
}
- Owner owner_road = HasBit(prev_roadtypes, ROADTYPE_ROAD) ? GetRoadOwner(tile_start, ROADTYPE_ROAD) : company;
- Owner owner_tram = HasBit(prev_roadtypes, ROADTYPE_TRAM) ? GetRoadOwner(tile_start, ROADTYPE_TRAM) : company;
- MakeRoadBridgeRamp(tile_start, owner, owner_road, owner_tram, bridge_type, dir, roadtypes);
- MakeRoadBridgeRamp(tile_end, owner, owner_road, owner_tram, bridge_type, ReverseDiagDir(dir), roadtypes);
+ Owner owner_road = hasroad ? GetRoadOwner(tile_start, RTT_ROAD) : company;
+ Owner owner_tram = hastram ? GetRoadOwner(tile_start, RTT_TRAM) : company;
+ MakeRoadBridgeRamp(tile_start, owner, owner_road, owner_tram, bridge_type, dir, road_rt, tram_rt);
+ MakeRoadBridgeRamp(tile_end, owner, owner_road, owner_tram, bridge_type, ReverseDiagDir(dir), road_rt, tram_rt);
break;
}
@@ -538,7 +559,15 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
bridge_len += 2; // begin and end tiles/ramps
switch (transport_type) {
- case TRANSPORT_ROAD: cost.AddCost(bridge_len * _price[PR_BUILD_ROAD] * 2 * CountBits(roadtypes)); break;
+ case TRANSPORT_ROAD:
+ if (road_rt != INVALID_ROADTYPE) {
+ cost.AddCost(bridge_len * 2 * RoadBuildCost(road_rt));
+ }
+ if (tram_rt != INVALID_ROADTYPE) {
+ cost.AddCost(bridge_len * 2 * RoadBuildCost(tram_rt));
+ }
+ break;
+
case TRANSPORT_RAIL: cost.AddCost(bridge_len * RailBuildCost(railtype)); break;
default: break;
}
@@ -562,7 +591,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
* Build Tunnel.
* @param start_tile start tile of tunnel
* @param flags type of operation
- * @param p1 bit 0-5 railtype or roadtypes
+ * @param p1 bit 0-5 railtype or roadtype
* bit 8-9 transport type
* @param p2 unused
* @param text unused
@@ -573,9 +602,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
CompanyID company = _current_company;
TransportType transport_type = Extract<TransportType, 8, 2>(p1);
-
RailType railtype = INVALID_RAILTYPE;
- RoadTypes rts = ROADTYPES_NONE;
+ RoadType roadtype = INVALID_ROADTYPE;
_build_tunnel_endtile = 0;
switch (transport_type) {
case TRANSPORT_RAIL:
@@ -584,8 +612,8 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
break;
case TRANSPORT_ROAD:
- rts = Extract<RoadTypes, 0, 2>(p1);
- if (!HasExactlyOneBit(rts) || !HasRoadTypesAvail(company, rts)) return CMD_ERROR;
+ roadtype = Extract<RoadType, 0, 6>(p1);
+ if (!ValParamRoadType(roadtype)) return CMD_ERROR;
break;
default: return CMD_ERROR;
@@ -713,7 +741,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
/* Pay for the rail/road in the tunnel including entrances */
switch (transport_type) {
- case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * _price[PR_BUILD_ROAD] * 2); break;
+ case TRANSPORT_ROAD: cost.AddCost((tiles + 2) * RoadBuildCost(roadtype) * 2); break;
case TRANSPORT_RAIL: cost.AddCost((tiles + 2) * RailBuildCost(railtype)); break;
default: NOT_REACHED();
}
@@ -722,20 +750,17 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1,
Company *c = Company::GetIfValid(company);
uint num_pieces = (tiles + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
if (transport_type == TRANSPORT_RAIL) {
- if (!IsTunnelTile(start_tile) && c != nullptr) c->infrastructure.rail[railtype] += num_pieces;
+ if (c != nullptr) c->infrastructure.rail[railtype] += num_pieces;
MakeRailTunnel(start_tile, company, direction, railtype);
MakeRailTunnel(end_tile, company, ReverseDiagDir(direction), railtype);
AddSideToSignalBuffer(start_tile, INVALID_DIAGDIR, company);
YapfNotifyTrackLayoutChange(start_tile, DiagDirToDiagTrack(direction));
} else {
- if (c != nullptr) {
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, rts ^ (IsTunnelTile(start_tile) ? GetRoadTypes(start_tile) : ROADTYPES_NONE)) {
- c->infrastructure.road[rt] += num_pieces * 2; // A full diagonal road has two road bits.
- }
- }
- MakeRoadTunnel(start_tile, company, direction, rts);
- MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), rts);
+ if (c != nullptr) c->infrastructure.road[roadtype] += num_pieces * 2; // A full diagonal road has two road bits.
+ RoadType road_rt = RoadTypeIsRoad(roadtype) ? roadtype : INVALID_ROADTYPE;
+ RoadType tram_rt = RoadTypeIsTram(roadtype) ? roadtype : INVALID_ROADTYPE;
+ MakeRoadTunnel(start_tile, company, direction, road_rt, tram_rt);
+ MakeRoadTunnel(end_tile, company, ReverseDiagDir(direction), road_rt, tram_rt);
}
DirtyCompanyInfrastructureWindows(company);
}
@@ -756,12 +781,13 @@ static inline CommandCost CheckAllowRemoveTunnelBridge(TileIndex tile)
switch (GetTunnelBridgeTransportType(tile)) {
case TRANSPORT_ROAD: {
- RoadTypes rts = GetRoadTypes(tile);
+ RoadType road_rt = GetRoadTypeRoad(tile);
+ RoadType tram_rt = GetRoadTypeTram(tile);
Owner road_owner = _current_company;
Owner tram_owner = _current_company;
- if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
- if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
+ if (road_rt != INVALID_ROADTYPE) road_owner = GetRoadOwner(tile, RTT_ROAD);
+ if (tram_rt != INVALID_ROADTYPE) tram_owner = GetRoadOwner(tile, RTT_TRAM);
/* We can remove unowned road and if the town allows it */
if (road_owner == OWNER_TOWN && _current_company != OWNER_TOWN && !(_settings_game.construction.extra_dynamite || _cheats.magic_bulldozer.value)) {
@@ -856,15 +882,9 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags)
if (v != nullptr) TryPathReserve(v);
} else {
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- /* A full diagonal road tile has two road bits. */
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
- DirtyCompanyInfrastructureWindows(c->index);
- }
- }
+ /* A full diagonal road tile has two road bits. */
+ UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
+ UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
DoClearSquare(tile);
DoClearSquare(endtile);
@@ -928,15 +948,9 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags)
if (rail) {
if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
} else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) {
- RoadType rt;
- FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) {
- Company *c = Company::GetIfValid(GetRoadOwner(tile, rt));
- if (c != nullptr) {
- /* A full diagonal road tile has two road bits. */
- c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR;
- DirtyCompanyInfrastructureWindows(c->index);
- }
- }
+ /* A full diagonal road tile has two road bits. */
+ UpdateCompanyRoadInfrastructure(GetRoadTypeRoad(tile), GetRoadOwner(tile, RTT_ROAD), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
+ UpdateCompanyRoadInfrastructure(GetRoadTypeTram(tile), GetRoadOwner(tile, RTT_TRAM), -(int)(len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR));
} else { // Aqueduct
if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR;
}
@@ -1082,19 +1096,90 @@ static void DrawBridgePillars(const PalSpriteID *psid, const TileInfo *ti, Axis
}
/**
- * Draws the trambits over an already drawn (lower end) of a bridge.
- * @param x the x of the bridge
- * @param y the y of the bridge
- * @param z the z of the bridge
- * @param offset number representing whether to level or sloped and the direction
- * @param overlay do we want to still see the road?
- * @param head are we drawing bridge head?
+ * Draws the road and trambits over an already drawn (lower end) of a bridge.
+ * @param head_tile bridge head tile with roadtype information
+ * @param x the x of the bridge
+ * @param y the y of the bridge
+ * @param z the z of the bridge
+ * @param offset sprite offset identifying flat to sloped bridge tiles
+ * @param head are we drawing bridge head?
*/
-static void DrawBridgeTramBits(int x, int y, int z, int offset, bool overlay, bool head)
+static void DrawBridgeRoadBits(TileIndex head_tile, int x, int y, int z, int offset, bool head)
{
- static const SpriteID tram_offsets[2][6] = { { 107, 108, 109, 110, 111, 112 }, { 4, 5, 15, 16, 17, 18 } };
- static const SpriteID back_offsets[6] = { 95, 96, 99, 102, 100, 101 };
- static const SpriteID front_offsets[6] = { 97, 98, 103, 106, 104, 105 };
+ RoadType road_rt = GetRoadTypeRoad(head_tile);
+ RoadType tram_rt = GetRoadTypeTram(head_tile);
+ const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
+ const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
+
+ SpriteID seq_back[4] = { 0 };
+ bool trans_back[4] = { false };
+ SpriteID seq_front[4] = { 0 };
+ bool trans_front[4] = { false };
+
+ static const SpriteID overlay_offsets[6] = { 0, 1, 11, 12, 13, 14 };
+ static const SpriteID back_offsets[6] = { 95, 96, 99, 102, 100, 101 };
+ static const SpriteID front_offsets[6] = { 97, 98, 103, 106, 104, 105 };
+
+ if (head || !IsInvisibilitySet(TO_BRIDGES)) {
+ /* Road underlay takes precendence over tram */
+ trans_back[0] = !head && IsTransparencySet(TO_BRIDGES);
+ if (road_rti != nullptr) {
+ if (road_rti->UsesOverlay()) {
+ seq_back[0] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset;
+ }
+ } else if (tram_rti != nullptr) {
+ if (tram_rti->UsesOverlay()) {
+ seq_back[0] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_BRIDGE, head ? TCX_NORMAL : TCX_ON_BRIDGE) + offset;
+ } else {
+ seq_back[0] = SPR_TRAMWAY_BRIDGE + offset;
+ }
+ }
+
+ /* Draw road overlay */
+ trans_back[1] = !head && IsTransparencySet(TO_BRIDGES);
+ if (road_rti != nullptr) {
+ if (road_rti->UsesOverlay()) {
+ seq_back[1] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE);
+ if (seq_back[1] != 0) seq_back[1] += overlay_offsets[offset];
+ }
+ }
+
+ /* Draw tram overlay */
+ trans_back[2] = !head && IsTransparencySet(TO_BRIDGES);
+ if (tram_rti != nullptr) {
+ if (tram_rti->UsesOverlay()) {
+ seq_back[2] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_OVERLAY, head ? TCX_NORMAL : TCX_ON_BRIDGE);
+ if (seq_back[2] != 0) seq_back[2] += overlay_offsets[offset];
+ } else if (road_rti != nullptr) {
+ seq_back[2] = SPR_TRAMWAY_OVERLAY + overlay_offsets[offset];
+ }
+ }
+
+ /* Road catenary takes precendence over tram */
+ trans_back[3] = IsTransparencySet(TO_CATENARY);
+ trans_front[0] = IsTransparencySet(TO_CATENARY);
+ if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) {
+ seq_back[3] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_CATENARY_BACK, head ? TCX_NORMAL : TCX_ON_BRIDGE);
+ seq_front[0] = GetCustomRoadSprite(road_rti, head_tile, ROTSG_CATENARY_FRONT, head ? TCX_NORMAL : TCX_ON_BRIDGE);
+ if (seq_back[3] == 0 || seq_front[0] == 0) {
+ seq_back[3] = SPR_TRAMWAY_BASE + back_offsets[offset];
+ seq_front[0] = SPR_TRAMWAY_BASE + front_offsets[offset];
+ } else {
+ seq_back[3] += 23 + offset;
+ seq_front[0] += 23 + offset;
+ }
+ } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) {
+ seq_back[3] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_CATENARY_BACK, head ? TCX_NORMAL : TCX_ON_BRIDGE);
+ seq_front[0] = GetCustomRoadSprite(tram_rti, head_tile, ROTSG_CATENARY_FRONT, head ? TCX_NORMAL : TCX_ON_BRIDGE);
+ if (seq_back[3] == 0 || seq_front[0] == 0) {
+ seq_back[3] = SPR_TRAMWAY_BASE + back_offsets[offset];
+ seq_front[0] = SPR_TRAMWAY_BASE + front_offsets[offset];
+ } else {
+ seq_back[3] += 23 + offset;
+ seq_front[0] += 23 + offset;
+ }
+ }
+ }
static const uint size_x[6] = { 1, 16, 16, 1, 16, 1 };
static const uint size_y[6] = { 16, 1, 1, 16, 1, 16 };
@@ -1103,28 +1188,25 @@ static void DrawBridgeTramBits(int x, int y, int z, int offset, bool overlay, bo
/* The sprites under the vehicles are drawn as SpriteCombine. StartSpriteCombine() has already been called
* The bounding boxes here are the same as for bridge front/roof */
- if (head || !IsInvisibilitySet(TO_BRIDGES)) {
- AddSortableSpriteToDraw(SPR_TRAMWAY_BASE + tram_offsets[overlay][offset], PAL_NONE,
- x, y, size_x[offset], size_y[offset], 0x28, z,
- !head && IsTransparencySet(TO_BRIDGES));
- }
-
- /* Do not draw catenary if it is set invisible */
- if (!IsInvisibilitySet(TO_CATENARY)) {
- AddSortableSpriteToDraw(SPR_TRAMWAY_BASE + back_offsets[offset], PAL_NONE,
- x, y, size_x[offset], size_y[offset], 0x28, z,
- IsTransparencySet(TO_CATENARY));
+ for (uint i = 0; i < lengthof(seq_back); ++i) {
+ if (seq_back[i] != 0) {
+ AddSortableSpriteToDraw(seq_back[i], PAL_NONE,
+ x, y, size_x[offset], size_y[offset], 0x28, z,
+ trans_back[i]);
+ }
}
/* Start a new SpriteCombine for the front part */
EndSpriteCombine();
StartSpriteCombine();
- /* For sloped sprites the bounding box needs to be higher, as the pylons stop on a higher point */
- if (!IsInvisibilitySet(TO_CATENARY)) {
- AddSortableSpriteToDraw(SPR_TRAMWAY_BASE + front_offsets[offset], PAL_NONE,
- x, y, size_x[offset] + front_bb_offset_x[offset], size_y[offset] + front_bb_offset_y[offset], 0x28, z,
- IsTransparencySet(TO_CATENARY), front_bb_offset_x[offset], front_bb_offset_y[offset]);
+ for (uint i = 0; i < lengthof(seq_front); ++i) {
+ if (seq_front[i] != 0) {
+ AddSortableSpriteToDraw(seq_front[i], PAL_NONE,
+ x, y, size_x[offset] + front_bb_offset_x[offset], size_y[offset] + front_bb_offset_y[offset], 0x28, z,
+ trans_front[i],
+ front_bb_offset_x[offset], front_bb_offset_y[offset]);
+ }
}
}
@@ -1188,20 +1270,37 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
DrawGroundSprite(image, PAL_NONE);
if (transport_type == TRANSPORT_ROAD) {
- RoadTypes rts = GetRoadTypes(ti->tile);
-
- if (HasBit(rts, ROADTYPE_TRAM)) {
- static const SpriteID tunnel_sprites[2][4] = { { 28, 78, 79, 27 }, { 5, 76, 77, 4 } };
-
- DrawGroundSprite(SPR_TRAMWAY_BASE + tunnel_sprites[rts - ROADTYPES_TRAM][tunnelbridge_direction], PAL_NONE);
-
- /* Do not draw wires if they are invisible */
- if (!IsInvisibilitySet(TO_CATENARY)) {
- catenary = true;
- StartSpriteCombine();
- AddSortableSpriteToDraw(SPR_TRAMWAY_TUNNEL_WIRES + tunnelbridge_direction, PAL_NONE, ti->x, ti->y, BB_data[10], BB_data[11], TILE_HEIGHT, ti->z, IsTransparencySet(TO_CATENARY), BB_data[8], BB_data[9], BB_Z_SEPARATOR);
+ RoadType road_rt = GetRoadTypeRoad(ti->tile);
+ RoadType tram_rt = GetRoadTypeTram(ti->tile);
+ const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
+ const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
+ uint sprite_offset = DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? 1 : 0;
+
+ DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
+
+ /* Road catenary takes precendence over tram */
+ SpriteID catenary_sprite_base = 0;
+ if (road_rti != nullptr && HasRoadCatenaryDrawn(road_rt)) {
+ catenary_sprite_base = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_CATENARY_FRONT);
+ if (catenary_sprite_base == 0) {
+ catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES;
+ } else {
+ catenary_sprite_base += 19;
+ }
+ } else if (tram_rti != nullptr && HasRoadCatenaryDrawn(tram_rt)) {
+ catenary_sprite_base = GetCustomRoadSprite(tram_rti, ti->tile, ROTSG_CATENARY_FRONT);
+ if (catenary_sprite_base == 0) {
+ catenary_sprite_base = SPR_TRAMWAY_TUNNEL_WIRES;
+ } else {
+ catenary_sprite_base += 19;
}
}
+
+ if (catenary_sprite_base != 0) {
+ catenary = true;
+ StartSpriteCombine();
+ AddSortableSpriteToDraw(catenary_sprite_base + tunnelbridge_direction, PAL_NONE, ti->x, ti->y, BB_data[10], BB_data[11], TILE_HEIGHT, ti->z, IsTransparencySet(TO_CATENARY), BB_data[8], BB_data[9], BB_Z_SEPARATOR);
+ }
} else {
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
if (rti->UsesOverlay()) {
@@ -1294,20 +1393,18 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
AddSortableSpriteToDraw(psid->sprite, psid->pal, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 0 : 8, ti->z);
if (transport_type == TRANSPORT_ROAD) {
- RoadTypes rts = GetRoadTypes(ti->tile);
-
- if (HasBit(rts, ROADTYPE_TRAM)) {
- uint offset = tunnelbridge_direction;
- int z = ti->z;
- if (ti->tileh != SLOPE_FLAT) {
- offset = (offset + 1) & 1;
- z += TILE_HEIGHT;
- } else {
- offset += 2;
- }
- /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */
- DrawBridgeTramBits(ti->x, ti->y, z, offset, HasBit(rts, ROADTYPE_ROAD), true);
+ uint offset = tunnelbridge_direction;
+ int z = ti->z;
+ if (ti->tileh != SLOPE_FLAT) {
+ offset = (offset + 1) & 1;
+ z += TILE_HEIGHT;
+ } else {
+ offset += 2;
}
+
+ /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
+ DrawBridgeRoadBits(ti->tile, ti->x, ti->y, z, offset, true);
+
EndSpriteCombine();
} else if (transport_type == TRANSPORT_RAIL) {
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
@@ -1465,15 +1562,8 @@ void DrawBridgeMiddle(const TileInfo *ti)
psid++;
if (transport_type == TRANSPORT_ROAD) {
- RoadTypes rts = GetRoadTypes(rampsouth);
-
- if (HasBit(rts, ROADTYPE_TRAM)) {
- /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */
- DrawBridgeTramBits(x, y, bridge_z, axis ^ 1, HasBit(rts, ROADTYPE_ROAD), false);
- } else {
- EndSpriteCombine();
- StartSpriteCombine();
- }
+ /* DrawBridgeRoadBits() calls EndSpriteCombine() and StartSpriteCombine() */
+ DrawBridgeRoadBits(rampsouth, x, y, bridge_z, axis ^ 1, false);
} else if (transport_type == TRANSPORT_RAIL) {
const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(rampsouth));
if (rti->UsesOverlay() && !IsInvisibilitySet(TO_BRIDGES)) {
@@ -1593,9 +1683,20 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
Owner road_owner = INVALID_OWNER;
Owner tram_owner = INVALID_OWNER;
- RoadTypes rts = GetRoadTypes(tile);
- if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
- if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
+ RoadType road_rt = GetRoadTypeRoad(tile);
+ RoadType tram_rt = GetRoadTypeTram(tile);
+ if (road_rt != INVALID_ROADTYPE) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(road_rt);
+ td->roadtype = rti->strings.name;
+ td->road_speed = rti->max_speed / 2;
+ road_owner = GetRoadOwner(tile, RTT_ROAD);
+ }
+ if (tram_rt != INVALID_ROADTYPE) {
+ const RoadTypeInfo *rti = GetRoadTypeInfo(tram_rt);
+ td->tramtype = rti->strings.name;
+ td->tram_speed = rti->max_speed / 2;
+ tram_owner = GetRoadOwner(tile, RTT_TRAM);
+ }
/* Is there a mix of owners? */
if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
@@ -1624,7 +1725,9 @@ static void GetTileDesc_TunnelBridge(TileIndex tile, TileDesc *td)
}
}
} else if (tt == TRANSPORT_ROAD && !IsTunnel(tile)) {
- td->road_speed = GetBridgeSpec(GetBridgeType(tile))->speed;
+ uint16 spd = GetBridgeSpec(GetBridgeType(tile))->speed;
+ if (road_rt != INVALID_ROADTYPE && (td->road_speed == 0 || spd < td->road_speed)) td->road_speed = spd;
+ if (tram_rt != INVALID_ROADTYPE && (td->tram_speed == 0 || spd < td->tram_speed)) td->tram_speed = spd;
}
}
@@ -1660,7 +1763,7 @@ static void TileLoop_TunnelBridge(TileIndex tile)
static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
{
TransportType transport_type = GetTunnelBridgeTransportType(tile);
- if (transport_type != mode || (transport_type == TRANSPORT_ROAD && (GetRoadTypes(tile) & sub_mode) == 0)) return 0;
+ if (transport_type != mode || (transport_type == TRANSPORT_ROAD && !HasTileRoadType(tile, (RoadTramType)sub_mode))) return 0;
DiagDirection dir = GetTunnelBridgeDirection(tile);
if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0;
@@ -1674,17 +1777,18 @@ static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner
* don't want to update the infrastructure counts twice. */
uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0;
- for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) {
+ FOR_ALL_ROADTRAMTYPES(rtt) {
/* Update all roadtypes, no matter if they are present */
- if (GetRoadOwner(tile, rt) == old_owner) {
- if (HasBit(GetRoadTypes(tile), rt)) {
+ if (GetRoadOwner(tile, rtt) == old_owner) {
+ RoadType rt = GetRoadType(tile, rtt);
+ if (rt != INVALID_ROADTYPE) {
/* Update company infrastructure counts. A full diagonal road tile has two road bits.
* No need to dirty windows here, we'll redraw the whole screen anyway. */
Company::Get(old_owner)->infrastructure.road[rt] -= num_pieces * 2;
if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_pieces * 2;
}
- SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
+ SetRoadOwner(tile, rtt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner);
}
}
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 031b6f95e..6e26c7bfe 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -1757,7 +1757,7 @@ UnitID GetFreeUnitNumber(VehicleType type)
* @return true if there is any reason why you may build
* the infrastructure for the given vehicle type
*/
-bool CanBuildVehicleInfrastructure(VehicleType type)
+bool CanBuildVehicleInfrastructure(VehicleType type, byte subtype)
{
assert(IsCompanyBuildableVehicleType(type));
@@ -1770,7 +1770,10 @@ bool CanBuildVehicleInfrastructure(VehicleType type)
if (!HasAnyRailtypesAvail(_local_company)) return false;
max = _settings_game.vehicle.max_trains;
break;
- case VEH_ROAD: max = _settings_game.vehicle.max_roadveh; break;
+ case VEH_ROAD:
+ if (!HasAnyRoadTypesAvail(_local_company, (RoadTramType)subtype)) return false;
+ max = _settings_game.vehicle.max_roadveh;
+ break;
case VEH_SHIP: max = _settings_game.vehicle.max_ships; break;
case VEH_AIRCRAFT: max = _settings_game.vehicle.max_aircraft; break;
default: NOT_REACHED();
@@ -1781,6 +1784,7 @@ bool CanBuildVehicleInfrastructure(VehicleType type)
/* Can we actually build the vehicle type? */
const Engine *e;
FOR_ALL_ENGINES_OF_TYPE(e, type) {
+ if (type == VEH_ROAD && GetRoadTramType(e->u.road.roadtype) != (RoadTramType)subtype) continue;
if (HasBit(e->company_avail, _local_company)) return true;
}
return false;
@@ -1789,6 +1793,7 @@ bool CanBuildVehicleInfrastructure(VehicleType type)
/* We should be able to build infrastructure when we have the actual vehicle type */
const Vehicle *v;
FOR_ALL_VEHICLES(v) {
+ if (type == VEH_ROAD && GetRoadTramType(RoadVehicle::From(v)->roadtype) != (RoadTramType)subtype) continue;
if (v->owner == _local_company && v->type == type) return true;
}
diff --git a/src/vehicle_func.h b/src/vehicle_func.h
index e61f6e233..bdd961bef 100644
--- a/src/vehicle_func.h
+++ b/src/vehicle_func.h
@@ -71,7 +71,7 @@ UnitID GetFreeUnitNumber(VehicleType type);
void VehicleEnterDepot(Vehicle *v);
-bool CanBuildVehicleInfrastructure(VehicleType type);
+bool CanBuildVehicleInfrastructure(VehicleType type, byte subtype = 0);
/** Position information of a vehicle after it moved */
struct GetNewVehiclePosResult {
diff --git a/src/viewport_type.h b/src/viewport_type.h
index 74cd88d95..f5544679a 100644
--- a/src/viewport_type.h
+++ b/src/viewport_type.h
@@ -121,6 +121,7 @@ enum ViewportDragDropSelectionProcess {
DDSP_BUILD_TRUCKSTOP, ///< Road stop placement (trucks)
DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses)
DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks)
+ DDSP_CONVERT_ROAD, ///< Road conversion
};
diff --git a/src/widgets/autoreplace_widget.h b/src/widgets/autoreplace_widget.h
index 4b761ca45..8432b1a7f 100644
--- a/src/widgets/autoreplace_widget.h
+++ b/src/widgets/autoreplace_widget.h
@@ -34,9 +34,11 @@ enum ReplaceVehicleWidgets {
WID_RV_INFO_TAB, ///< Info tab.
WID_RV_STOP_REPLACE, ///< Stop Replacing button.
+ /* Train/road only widgets */
+ WID_RV_RAIL_ROAD_TYPE_DROPDOWN, ///< Dropdown menu about the rail/roadtype.
+
/* Train only widgets. */
WID_RV_TRAIN_ENGINEWAGON_DROPDOWN, ///< Dropdown to select engines and/or wagons.
- WID_RV_TRAIN_RAILTYPE_DROPDOWN, ///< Dropdown menu about the railtype.
WID_RV_TRAIN_WAGONREMOVE_TOGGLE, ///< Button to toggle removing wagons.
};
diff --git a/src/widgets/company_widget.h b/src/widgets/company_widget.h
index ceb81ae19..8b0d8ee0b 100644
--- a/src/widgets/company_widget.h
+++ b/src/widgets/company_widget.h
@@ -177,6 +177,8 @@ enum CompanyInfrastructureWidgets {
WID_CI_RAIL_COUNT, ///< Count of rail.
WID_CI_ROAD_DESC, ///< Description of road.
WID_CI_ROAD_COUNT, ///< Count of road.
+ WID_CI_TRAM_DESC, ///< Description of tram.
+ WID_CI_TRAM_COUNT, ///< Count of tram.
WID_CI_WATER_DESC, ///< Description of water.
WID_CI_WATER_COUNT, ///< Count of water.
WID_CI_STATION_DESC, ///< Description of station.
diff --git a/src/widgets/road_widget.h b/src/widgets/road_widget.h
index f022489e5..60068317a 100644
--- a/src/widgets/road_widget.h
+++ b/src/widgets/road_widget.h
@@ -15,6 +15,7 @@
/** Widgets of the #BuildRoadToolbarWindow class. */
enum RoadToolbarWidgets {
/* Name starts with RO instead of R, because of collision with RailToolbarWidgets */
+ WID_ROT_CAPTION, ///< Caption of the window
WID_ROT_ROAD_X, ///< Build road in x-direction.
WID_ROT_ROAD_Y, ///< Build road in y-direction.
WID_ROT_AUTOROAD, ///< Autorail.
@@ -26,6 +27,7 @@ enum RoadToolbarWidgets {
WID_ROT_BUILD_BRIDGE, ///< Build bridge.
WID_ROT_BUILD_TUNNEL, ///< Build tunnel.
WID_ROT_REMOVE, ///< Remove road.
+ WID_ROT_CONVERT_ROAD, ///< Convert road.
};
/** Widgets of the #BuildRoadDepotWindow class. */
diff --git a/src/widgets/toolbar_widget.h b/src/widgets/toolbar_widget.h
index 5f0c1b5a4..5b9d1e156 100644
--- a/src/widgets/toolbar_widget.h
+++ b/src/widgets/toolbar_widget.h
@@ -39,6 +39,7 @@ enum ToolbarNormalWidgets {
WID_TN_BUILDING_TOOLS_START, ///< Helper for the offset of the building tools
WID_TN_RAILS = WID_TN_BUILDING_TOOLS_START, ///< Rail building menu.
WID_TN_ROADS, ///< Road building menu.
+ WID_TN_TRAMS, ///< Tram building menu.
WID_TN_WATER, ///< Water building toolbar.
WID_TN_AIR, ///< Airport building toolbar.
WID_TN_LANDSCAPE, ///< Landscaping toolbar.
@@ -66,6 +67,7 @@ enum ToolbarEditorWidgets {
WID_TE_TOWN_GENERATE, ///< Town building window.
WID_TE_INDUSTRY, ///< Industry building window.
WID_TE_ROADS, ///< Road building menu.
+ WID_TE_TRAMS, ///< Tram building menu.
WID_TE_WATER, ///< Water building toolbar.
WID_TE_TREES, ///< Tree building toolbar.
WID_TE_SIGNS, ///< Sign building.