diff options
author | peter1138 <peter1138@openttd.org> | 2019-04-06 07:46:15 +0100 |
---|---|---|
committer | Michael Lutz <michi@icosahedron.de> | 2019-05-01 21:36:27 +0200 |
commit | c02ef3e4564b7b54d49f0827d2d7625cbc38f335 (patch) | |
tree | 1c0ee62b6ce55124b247daaafa42300bfaa932e7 /src | |
parent | 21edf67f89c60351d5a0d84625455aa296b6b950 (diff) | |
download | openttd-c02ef3e4564b7b54d49f0827d2d7625cbc38f335.tar.xz |
Feature: Add NotRoadTypes (NRT)
Diffstat (limited to 'src')
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. |