diff --git a/src/newgrf.cpp b/src/newgrf.cpp index c0507be74e..d276cdbba3 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4818,7 +4818,7 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, ByteR break; case 0x0C: // The draw mode - rs->draw_mode = (RoadStopDrawMode)buf.ReadByte(); + rs->draw_mode = static_cast(buf.ReadByte()); break; case 0x0D: // Cargo types for random triggers @@ -4843,7 +4843,7 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, ByteR break; case 0x12: // General flags - rs->flags = (uint8_t)buf.ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need one byte's worth of flags at present + rs->flags = (uint16_t)buf.ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need two byte's worth of flags at present break; case 0x15: // Cost multipliers diff --git a/src/newgrf_roadstop.cpp b/src/newgrf_roadstop.cpp index dc7f789710..9b1af78519 100644 --- a/src/newgrf_roadstop.cpp +++ b/src/newgrf_roadstop.cpp @@ -119,6 +119,15 @@ uint32_t RoadStopScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] u /* Animation frame */ case 0x49: return this->tile == INVALID_TILE ? 0 : this->st->GetRoadStopAnimationFrame(this->tile); + /* Misc info */ + case 0x50: { + uint32_t result = 0; + if (this->tile == INVALID_TILE) { + SetBit(result, 4); + } + return result; + } + /* Variables which use the parameter */ /* Variables 0x60 to 0x65 and 0x69 are handled separately below */ @@ -284,7 +293,19 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, SpriteID image = dts->ground.sprite; PaletteID pal = dts->ground.pal; - if (GB(image, 0, SPRITE_WIDTH) != 0) { + RoadStopDrawMode draw_mode; + if (HasBit(spec->flags, RSF_DRAW_MODE_REGISTER)) { + draw_mode = static_cast(GetRegister(0x100)); + } else { + draw_mode = spec->draw_mode; + } + + if (type == STATION_ROADWAYPOINT) { + DrawSprite(SPR_ROAD_PAVED_STRAIGHT_X, PAL_NONE, x, y); + if ((draw_mode & ROADSTOP_DRAW_MODE_WAYP_GROUND) && GB(image, 0, SPRITE_WIDTH) != 0) { + DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y); + } + } else if (GB(image, 0, SPRITE_WIDTH) != 0) { DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y); } @@ -293,7 +314,7 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, uint sprite_offset = 5 - view; /* Road underlay takes precedence over tram */ - if (spec->draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) { + if (type == STATION_ROADWAYPOINT || draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) { if (rti->UsesOverlay()) { SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND); DrawSprite(ground + sprite_offset, PAL_NONE, x, y); @@ -306,7 +327,7 @@ void DrawRoadStopTile(int x, int y, RoadType roadtype, const RoadStopSpec *spec, } } else { /* Bay stop */ - if ((spec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) && rti->UsesOverlay()) { + if ((draw_mode & ROADSTOP_DRAW_MODE_ROAD) && rti->UsesOverlay()) { SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP); DrawSprite(ground + view, PAL_NONE, x, y); } diff --git a/src/newgrf_roadstop.h b/src/newgrf_roadstop.h index c50e428a29..9144986ab3 100644 --- a/src/newgrf_roadstop.h +++ b/src/newgrf_roadstop.h @@ -62,6 +62,7 @@ enum RoadStopDrawMode : uint8_t { ROADSTOP_DRAW_MODE_NONE = 0, ROADSTOP_DRAW_MODE_ROAD = 1 << 0, ///< Bay stops: Draw the road itself ROADSTOP_DRAW_MODE_OVERLAY = 1 << 1, ///< Drive-through stops: Draw the road overlay, e.g. pavement + ROADSTOP_DRAW_MODE_WAYP_GROUND = 1 << 2, ///< Waypoints: Draw the sprite layout ground tile (on top of the road) }; DECLARE_ENUM_AS_BIT_SET(RoadStopDrawMode) @@ -72,6 +73,7 @@ enum RoadStopSpecFlags { RSF_NO_AUTO_ROAD_CONNECTION = 4, ///< No auto road connection. RSF_BUILD_MENU_ROAD_ONLY = 5, ///< Only show in the road build menu (not tram). RSF_BUILD_MENU_TRAM_ONLY = 6, ///< Only show in the tram build menu (not road). + RSF_DRAW_MODE_REGISTER = 8, ///< Read draw mode from register 0x100. }; enum RoadStopView { @@ -143,7 +145,7 @@ struct RoadStopSpec : NewGRFSpecBase { RoadStopAvailabilityType stop_type = ROADSTOPTYPE_ALL; RoadStopDrawMode draw_mode = ROADSTOP_DRAW_MODE_ROAD | ROADSTOP_DRAW_MODE_OVERLAY; uint8_t callback_mask = 0; - uint8_t flags = 0; + uint16_t flags = 0; CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing diff --git a/src/road_gui.cpp b/src/road_gui.cpp index be07a09574..0c10b97c46 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -32,6 +32,7 @@ #include "dropdown_func.h" #include "engine_base.h" #include "station_base.h" +#include "waypoint_base.h" #include "strings_func.h" #include "core/geometry_func.hpp" #include "station_cmd.h" @@ -51,6 +52,7 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs); static void ShowRoadDepotPicker(Window *parent); +static void ShowBuildRoadWaypointPicker(Window *parent); static bool _remove_button_clicked; static bool _one_way_button_clicked; @@ -64,6 +66,12 @@ static RoadType _cur_roadtype; static DiagDirection _road_depot_orientation; +struct RoadWaypointPickerSelection { + RoadStopClassID sel_class; ///< Selected road waypoint class. + uint16_t sel_type; ///< Selected road waypoint type within the class. +}; +static RoadWaypointPickerSelection _waypoint_gui; ///< Settings of the road waypoint picker. + struct RoadStopPickerSelection { RoadStopClassID sel_class; ///< Selected road stop class. uint16_t sel_type; ///< Selected road stop type within the class. @@ -536,8 +544,9 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_BUILD_WAYPOINT: + this->last_started_action = widget; if (HandlePlacePushButton(this, WID_ROT_BUILD_WAYPOINT, SPR_CURSOR_WAYPOINT, HT_RECT)) { - this->last_started_action = widget; + ShowBuildRoadWaypointPicker(this); } break; @@ -757,13 +766,12 @@ struct BuildRoadToolbarWindow : Window { TileArea ta(start_tile, end_tile); Axis axis = select_method == VPM_X_LIMITED ? AXIS_X : AXIS_Y; bool adjacent = _ctrl_pressed; - uint16_t waypoint_type = 0; auto proc = [=](bool test, StationID to_join) -> bool { if (test) { - return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, axis, ta.w, ta.h, ROADSTOP_CLASS_WAYP, waypoint_type, INVALID_STATION, adjacent).Succeeded(); + return Command::Do(CommandFlagsToDCFlags(GetCommandFlags()), ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, INVALID_STATION, adjacent).Succeeded(); } else { - return Command::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, axis, ta.w, ta.h, ROADSTOP_CLASS_WAYP, waypoint_type, to_join, adjacent); + return Command::Post(STR_ERROR_CAN_T_BUILD_ROAD_WAYPOINT, CcPlaySound_CONSTRUCTION_OTHER, ta.tile, axis, ta.w, ta.h, _waypoint_gui.sel_class, _waypoint_gui.sel_type, to_join, adjacent); } }; @@ -1589,10 +1597,126 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs) new BuildRoadStationWindow(RoadTypeIsRoad(_cur_roadtype) ? _road_station_picker_desc : _tram_station_picker_desc, parent, rs); } +class RoadWaypointPickerCallbacks : public PickerCallbacksNewGRFClass { +public: + RoadWaypointPickerCallbacks() : PickerCallbacksNewGRFClass("fav_road_waypoints") {} + + StringID GetClassTooltip() const override { return STR_PICKER_WAYPOINT_CLASS_TOOLTIP; } + StringID GetTypeTooltip() const override { return STR_PICKER_WAYPOINT_TYPE_TOOLTIP; } + + bool IsActive() const override + { + for (const auto &cls : RoadStopClass::Classes()) { + if (!IsWaypointClass(cls)) continue; + for (const auto *spec : cls.Specs()) { + if (spec != nullptr) return true; + } + } + return false; + } + + bool HasClassChoice() const override + { + return std::count_if(std::begin(RoadStopClass::Classes()), std::end(RoadStopClass::Classes()), IsWaypointClass) > 1; + } + + void Close(int) override { ResetObjectToPlace(); } + int GetSelectedClass() const override { return _waypoint_gui.sel_class; } + void SetSelectedClass(int id) const override { _waypoint_gui.sel_class = this->GetClassIndex(id); } + + StringID GetClassName(int id) const override + { + const auto *sc = GetClass(id); + if (!IsWaypointClass(*sc)) return INVALID_STRING_ID; + return sc->name; + } + + int GetSelectedType() const override { return _waypoint_gui.sel_type; } + void SetSelectedType(int id) const override { _waypoint_gui.sel_type = id; } + + StringID GetTypeName(int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + return (spec == nullptr) ? STR_STATION_CLASS_WAYP_WAYPOINT : spec->name; + } + + bool IsTypeAvailable(int cls_id, int id) const override + { + return IsRoadStopAvailable(this->GetSpec(cls_id, id), STATION_ROADWAYPOINT); + } + + void DrawType(int x, int y, int cls_id, int id) const override + { + const auto *spec = this->GetSpec(cls_id, id); + if (spec == nullptr) { + StationPickerDrawSprite(x, y, STATION_ROADWAYPOINT, INVALID_RAILTYPE, _cur_roadtype, RSV_DRIVE_THROUGH_X); + } else { + DrawRoadStopTile(x, y, _cur_roadtype, spec, STATION_ROADWAYPOINT, RSV_DRIVE_THROUGH_X); + } + } + + void FillUsedItems(std::set &items) override + { + for (const Waypoint *wp : Waypoint::Iterate()) { + if (wp->owner != _local_company || !HasBit(wp->waypoint_flags, WPF_ROAD)) continue; + items.insert({0, 0, ROADSTOP_CLASS_WAYP, 0}); // We would need to scan the map to find out if default is used. + for (const auto &sm : wp->roadstop_speclist) { + if (sm.spec == nullptr) continue; + items.insert({sm.grfid, sm.localidx, sm.spec->class_index, sm.spec->index}); + } + } + } + + static RoadWaypointPickerCallbacks instance; +}; +/* static */ RoadWaypointPickerCallbacks RoadWaypointPickerCallbacks::instance; + +struct BuildRoadWaypointWindow : public PickerWindow { + BuildRoadWaypointWindow(WindowDesc &desc, Window *parent) : PickerWindow(desc, parent, TRANSPORT_ROAD, RoadWaypointPickerCallbacks::instance) + { + this->ConstructWindow(); + this->InvalidateData(); + } + + static inline HotkeyList hotkeys{"buildroadwaypoint", { + Hotkey('F', "focus_filter_box", PCWHK_FOCUS_FILTER_BOX), + }}; +}; + +/** Nested widget definition for the build NewGRF road waypoint window */ +static constexpr NWidgetPart _nested_build_road_waypoint_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN), SetDataTip(STR_WAYPOINT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(WWT_SHADEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_DEFSIZEBOX, COLOUR_DARK_GREEN), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidgetFunction(MakePickerClassWidgets), + NWidgetFunction(MakePickerTypeWidgets), + EndContainer(), +}; + +static WindowDesc _build_road_waypoint_desc( + WDP_AUTO, "build_road_waypoint", 0, 0, + WC_BUILD_WAYPOINT, WC_BUILD_TOOLBAR, + WDF_CONSTRUCTION, + _nested_build_road_waypoint_widgets, + &BuildRoadWaypointWindow::hotkeys +); + +static void ShowBuildRoadWaypointPicker(Window *parent) +{ + if (!RoadWaypointPickerCallbacks::instance.IsActive()) return; + new BuildRoadWaypointWindow(_build_road_waypoint_desc, parent); +} + void InitializeRoadGui() { _road_depot_orientation = DIAGDIR_NW; _roadstop_gui.orientation = DIAGDIR_NW; + _waypoint_gui.sel_class = RoadStopClassID::ROADSTOP_CLASS_WAYP; + _waypoint_gui.sel_type = 0; } /** diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 5916075b4c..0dd4edb779 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3293,7 +3293,7 @@ draw_default_foundation: draw_ground = true; } - if (draw_ground && !IsStationRoadStop(ti->tile)) { + if (draw_ground && !IsAnyRoadStop(ti->tile)) { SpriteID image = t->ground.sprite; PaletteID pal = t->ground.pal; RailTrackOffset overlay_offset; @@ -3320,7 +3320,7 @@ draw_default_foundation: if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti); - if (IsStationRoadStop(ti->tile)) { + if (IsAnyRoadStop(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); @@ -3331,13 +3331,21 @@ draw_default_foundation: StationType type = GetStationType(ti->tile); const RoadStopSpec *stopspec = GetRoadStopSpec(ti->tile); + RoadStopDrawMode stop_draw_mode{}; if (stopspec != nullptr) { + stop_draw_mode = stopspec->draw_mode; int view = dir; if (IsDriveThroughStopTile(ti->tile)) view += 4; st = BaseStation::GetByTile(ti->tile); RoadStopResolverObject object(stopspec, st, ti->tile, INVALID_ROADTYPE, type, view); const SpriteGroup *group = object.Resolve(); if (group != nullptr && group->type == SGT_TILELAYOUT) { + if (HasBit(stopspec->flags, RSF_DRAW_MODE_REGISTER)) { + stop_draw_mode = static_cast(GetRegister(0x100)); + } + if (type == STATION_ROADWAYPOINT && (stop_draw_mode & ROADSTOP_DRAW_MODE_WAYP_GROUND)) { + draw_ground = true; + } t = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(nullptr); } } @@ -3354,14 +3362,15 @@ draw_default_foundation: } if (IsDriveThroughStopTile(ti->tile)) { - uint sprite_offset = axis == AXIS_X ? 1 : 0; - - DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset); + if (type != STATION_ROADWAYPOINT && (stopspec == nullptr || (stop_draw_mode & ROADSTOP_DRAW_MODE_OVERLAY) != 0)) { + 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 ((stopspec == nullptr || (stopspec->draw_mode & ROADSTOP_DRAW_MODE_ROAD) != 0) && road_rti->UsesOverlay()) { + if ((stopspec == nullptr || (stop_draw_mode & ROADSTOP_DRAW_MODE_ROAD) != 0) && road_rti->UsesOverlay()) { SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP); DrawGroundSprite(ground + dir, PAL_NONE); } @@ -3429,7 +3438,7 @@ void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, Ro } /* Default waypoint has no railtype specific sprites */ - DrawRailTileSeqInGUI(x, y, t, st == STATION_WAYPOINT ? 0 : total_offset, 0, pal); + DrawRailTileSeqInGUI(x, y, t, (st == STATION_WAYPOINT || st == STATION_ROADWAYPOINT) ? 0 : total_offset, 0, pal); } static int GetSlopePixelZ_Station(TileIndex tile, uint, uint, bool)