diff --git a/src/ai/default/default.cpp b/src/ai/default/default.cpp index ab20b5952a..398fc2b6bd 100644 --- a/src/ai/default/default.cpp +++ b/src/ai/default/default.cpp @@ -1654,7 +1654,7 @@ static CommandCost AiDoBuildDefaultRailTrack(TileIndex tile, const AiDefaultBloc ret = DoCommand(c, railtype, p->attr, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRAIN_DEPOT); } else { // Station - ret = DoCommand(c, (p->attr & 1) | (p->attr >> 4) << 8 | (p->attr >> 1 & 7) << 16, railtype, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_RAILROAD_STATION); + ret = DoCommand(c, (railtype & 0x0f) | (p->attr & 1) << 4 | (p->attr >> 4) << 8 | (p->attr >> 1 & 7) << 16, (INVALID_STATION << 16), flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_RAILROAD_STATION); } if (CmdFailed(ret)) return CMD_ERROR; @@ -2679,10 +2679,10 @@ static CommandCost AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBloc } else if (p->mode == 1) { if (_want_road_truck_station) { // Truck station - ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); + ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); } else { // Bus station - ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_BUS, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); + ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_BUS | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP); } clear_town_stuff:; @@ -3405,7 +3405,7 @@ static CommandCost AiDoBuildDefaultAirportBlock(TileIndex tile, const AiDefaultB for (; p->mode == 0; p++) { if (!HasBit(avail_airports, p->attr)) return CMD_ERROR; - ret = DoCommand(TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)), p->attr, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_AIRPORT); + ret = DoCommand(TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)), p->attr, INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_AIRPORT); if (CmdFailed(ret)) return CMD_ERROR; total_cost.AddCost(ret); } diff --git a/src/ai/trolly/build.cpp b/src/ai/trolly/build.cpp index f6aee32397..e1f4b99805 100644 --- a/src/ai/trolly/build.cpp +++ b/src/ai/trolly/build.cpp @@ -43,12 +43,12 @@ bool AiNew_Build_CompanyHQ(Company *c, TileIndex tile) CommandCost AiNew_Build_Station(Company *c, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag) { if (type == AI_TRAIN) - return AI_DoCommand(tile, direction + (numtracks << 8) + (length << 16), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION); + return AI_DoCommand(tile, (direction << 4) + (numtracks << 8) + (length << 16), INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION); if (type == AI_BUS) - return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_BUS, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); + return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_BUS | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); - return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); + return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP); } diff --git a/src/airport_gui.cpp b/src/airport_gui.cpp index 945d196d68..eefc1f1f50 100644 --- a/src/airport_gui.cpp +++ b/src/airport_gui.cpp @@ -40,7 +40,11 @@ void CcBuildAirport(bool success, TileIndex tile, uint32 p1, uint32 p2) static void PlaceAirport(TileIndex tile) { - DoCommandP(tile, _selected_airport_type, _ctrl_pressed, CMD_BUILD_AIRPORT | CMD_MSG(STR_A001_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport); + uint32 p2 = _ctrl_pressed; + SB(p2, 16, 16, INVALID_STATION); // no station to join + + CommandContainer cmdcont = { tile, _selected_airport_type, p2, CMD_BUILD_AIRPORT | CMD_MSG(STR_A001_CAN_T_BUILD_AIRPORT_HERE), CcBuildAirport, "" }; + ShowSelectStationIfNeeded(cmdcont, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE); } @@ -124,6 +128,7 @@ struct BuildAirToolbarWindow : Window { this->RaiseButtons(); delete FindWindowById(WC_BUILD_STATION, 0); + delete FindWindowById(WC_SELECT_STATION, 0); } }; @@ -186,6 +191,11 @@ public: this->FindWindowPlacementAndResize(desc); } + virtual ~AirportPickerWindow() + { + DeleteWindowById(WC_SELECT_STATION, 0); + } + virtual void OnPaint() { int i; // airport enabling loop @@ -247,6 +257,7 @@ public: this->LowerWidget(_selected_airport_type + BAW_SMALL_AIRPORT); SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); break; case BAW_BTN_DONTHILIGHT: case BAW_BTN_DOHILIGHT: diff --git a/src/command.cpp b/src/command.cpp index 3956c2c883..2cc9446762 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -383,7 +383,7 @@ static int _docommand_recursive = 0; */ CommandCost DoCommand(const CommandContainer *container, uint32 flags) { - return DoCommand(container->tile, container->p1, container->p2, flags, container->cmd, container->text); + return DoCommand(container->tile, container->p1, container->p2, flags, container->cmd & CMD_ID_MASK, container->text); } /*! diff --git a/src/dock_gui.cpp b/src/dock_gui.cpp index 6c588bebe1..7cc13acdb6 100644 --- a/src/dock_gui.cpp +++ b/src/dock_gui.cpp @@ -47,7 +47,11 @@ void CcBuildCanal(bool success, TileIndex tile, uint32 p1, uint32 p2) static void PlaceDocks_Dock(TileIndex tile) { - DoCommandP(tile, _ctrl_pressed, 0, CMD_BUILD_DOCK | CMD_MSG(STR_9802_CAN_T_BUILD_DOCK_HERE), CcBuildDocks); + uint32 p2 = INVALID_STATION << 16; // no station to join + + /* tile is always the land tile, so need to evaluate _thd.pos */ + CommandContainer cmdcont = { tile, _ctrl_pressed, p2, CMD_BUILD_DOCK | CMD_MSG(STR_9802_CAN_T_BUILD_DOCK_HERE), CcBuildDocks, "" }; + ShowSelectStationIfNeeded(cmdcont, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE); } static void PlaceDocks_Depot(TileIndex tile) @@ -240,6 +244,7 @@ struct BuildDocksToolbarWindow : Window { delete FindWindowById(WC_BUILD_STATION, 0); delete FindWindowById(WC_BUILD_DEPOT, 0); + delete FindWindowById(WC_SELECT_STATION, 0); } virtual void OnPlacePresize(Point pt, TileIndex tile_from) @@ -334,6 +339,11 @@ public: this->FindWindowPlacementAndResize(desc); } + virtual ~BuildDocksStationWindow() + { + DeleteWindowById(WC_SELECT_STATION, 0); + } + virtual void OnPaint() { int rad = (_settings_game.station.modified_catchment) ? CA_DOCK : CA_UNMODIFIED; diff --git a/src/lang/english.txt b/src/lang/english.txt index 9c61c1f5e4..571d87edf0 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1027,6 +1027,7 @@ STR_CONFIG_PATCHES_MAMMOTHTRAINS :{LTBLUE}Enable STR_CONFIG_PATCHES_REALISTICACCEL :{LTBLUE}Enable realistic acceleration for trains: {ORANGE}{STRING1} STR_CONFIG_PATCHES_FORBID_90_DEG :{LTBLUE}Forbid trains and ships to make 90 deg turns: {ORANGE}{STRING1} {LTBLUE} (not with NTP) STR_CONFIG_PATCHES_JOINSTATIONS :{LTBLUE}Join train stations built next to each other: {ORANGE}{STRING1} +STR_CONFIG_PATCHES_DISTANT_JOIN_STATIONS :{LTBLUE}Allow to join stations not directly adjacent: {ORANGE}{STRING1} STR_CONFIG_PATCHES_IMPROVEDLOAD :{LTBLUE}Use improved loading algorithm: {ORANGE}{STRING1} STR_CONFIG_PATCHES_GRADUAL_LOADING :{LTBLUE}Load vehicles gradually: {ORANGE}{STRING1} STR_CONFIG_PATCHES_INFLATION :{LTBLUE}Inflation: {ORANGE}{STRING1} @@ -1721,6 +1722,8 @@ STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS :Railway track w STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS :Railway track with combo- and one-way path signals STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS :Railway track with path and one-way path signals STR_MUST_REMOVE_RAILWAY_STATION_FIRST :{WHITE}Must remove railway station first +STR_CREATE_SPLITTED_STATION :{YELLOW}Build a separate station +STR_SELECT_STATION_TO_JOIN :{BLACK}Join station diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 862707b1a9..cb7ac7856a 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -188,10 +188,15 @@ static void PlaceRail_Station(TileIndex tile) VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION); VpSetPlaceSizingLimit(_settings_game.station.station_spread); } else { - DoCommandP(tile, - _railstation.orientation | (_settings_client.gui.station_numtracks << 8) | (_settings_client.gui.station_platlength << 16) | (_ctrl_pressed << 24), - _cur_railtype | (_railstation.station_class << 8) | (_railstation.station_type << 16), - CMD_BUILD_RAILROAD_STATION | CMD_MSG(STR_100F_CAN_T_BUILD_RAILROAD_STATION), CcStation); + uint32 p1 = _cur_railtype | _railstation.orientation << 4 | _settings_client.gui.station_numtracks << 8 | _settings_client.gui.station_platlength << 16 | _ctrl_pressed << 24; + uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16; + + int w = _settings_client.gui.station_numtracks; + int h = _settings_client.gui.station_platlength; + if (!_railstation.orientation) Swap(w, h); + + CommandContainer cmdcont = { tile, p1, p2, CMD_BUILD_RAILROAD_STATION | CMD_MSG(STR_100F_CAN_T_BUILD_RAILROAD_STATION), CcStation, "" }; + ShowSelectStationIfNeeded(cmdcont, w, h); } } @@ -753,6 +758,7 @@ struct BuildRailToolbarWindow : Window { delete FindWindowById(WC_BUILD_SIGNAL, 0); delete FindWindowById(WC_BUILD_STATION, 0); delete FindWindowById(WC_BUILD_DEPOT, 0); + delete FindWindowById(WC_SELECT_STATION, 0); } virtual void OnPlacePresize(Point pt, TileIndex tile) @@ -873,12 +879,13 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) if (sy > ey) Swap(sy, ey); w = ex - sx + 1; h = ey - sy + 1; + if (_railstation.orientation == AXIS_X) Swap(w, h); + uint32 p1 = _cur_railtype | _railstation.orientation << 4 | _ctrl_pressed << 24; + uint32 p2 = _railstation.station_class | _railstation.station_type << 8 | INVALID_STATION << 16; - DoCommandP(TileXY(sx, sy), - _railstation.orientation | (w << 8) | (h << 16) | (_ctrl_pressed << 24), - _cur_railtype | (_railstation.station_class << 8) | (_railstation.station_type << 16), - CMD_BUILD_RAILROAD_STATION | CMD_MSG(STR_100F_CAN_T_BUILD_RAILROAD_STATION), CcStation); + CommandContainer cmdcont = { TileXY(sx, sy), p1 | w << 8 | h << 16, p2, CMD_BUILD_RAILROAD_STATION | CMD_MSG(STR_100F_CAN_T_BUILD_RAILROAD_STATION), CcStation, "" }; + ShowSelectStationIfNeeded(cmdcont, w, h); } struct BuildRailStationWindow : public PickerWindowBase { @@ -993,6 +1000,11 @@ public: } } + virtual ~BuildRailStationWindow() + { + DeleteWindowById(WC_SELECT_STATION, 0); + } + virtual void OnPaint() { bool newstations = _railstation.newstations; @@ -1094,6 +1106,7 @@ public: this->LowerWidget(_railstation.orientation + BRSW_PLATFORM_DIR_X); SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); break; case BRSW_PLATFORM_NUM_1: @@ -1127,6 +1140,7 @@ public: this->LowerWidget(_settings_client.gui.station_platlength + BRSW_PLATFORM_LEN_BEGIN); SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); break; } @@ -1161,6 +1175,7 @@ public: this->LowerWidget(_settings_client.gui.station_platlength + BRSW_PLATFORM_LEN_BEGIN); SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); break; } @@ -1194,6 +1209,7 @@ public: this->SetWidgetLoweredState(_settings_client.gui.station_platlength + BRSW_PLATFORM_LEN_BEGIN, !_settings_client.gui.station_dragdrop); SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); } break; case BRSW_HIGHLIGHT_OFF: @@ -1230,6 +1246,7 @@ public: SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); break; } } @@ -1250,6 +1267,7 @@ public: SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); } virtual void OnTick() diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 902649df75..fea1411a82 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -206,12 +206,14 @@ static void PlaceRoad_Depot(TileIndex tile) static void PlaceRoadStop(TileIndex tile, uint32 p2, uint32 cmd) { uint32 p1 = _road_station_picker_orientation; + SB(p2, 16, 16, INVALID_STATION); // no station to join if (p1 >= DIAGDIR_END) { SetBit(p2, 1); // It's a drive-through stop p1 -= DIAGDIR_END; // Adjust picker result to actual direction } - DoCommandP(tile, p1, p2, cmd, CcRoadDepot); + CommandContainer cmdcont = { tile, p1, p2, cmd, CcRoadDepot, "" }; + ShowSelectStationIfNeeded(cmdcont, 1, 1); } static void PlaceRoad_BusStation(TileIndex tile) @@ -528,6 +530,7 @@ struct BuildRoadToolbarWindow : Window { delete FindWindowById(WC_BUS_STATION, 0); delete FindWindowById(WC_TRUCK_STATION, 0); delete FindWindowById(WC_BUILD_DEPOT, 0); + delete FindWindowById(WC_SELECT_STATION, 0); } virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) @@ -835,6 +838,11 @@ public: this->FindWindowPlacementAndResize(desc); } + virtual ~BuildRoadStationWindow() + { + DeleteWindowById(WC_SELECT_STATION, 0); + } + virtual void OnPaint() { this->DrawWidgets(); @@ -883,6 +891,7 @@ public: this->LowerWidget(_road_station_picker_orientation + BRSW_STATION_NE); SndPlayFx(SND_15_BEEP); this->SetDirty(); + DeleteWindowById(WC_SELECT_STATION, 0); break; case BRSW_LT_OFF: diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 0a6585ce89..a2bc2e324d 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -42,7 +42,7 @@ #include -extern const uint16 SAVEGAME_VERSION = 105; +extern const uint16 SAVEGAME_VERSION = 106; SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/settings.cpp b/src/settings.cpp index abbeec8c0a..4e8eb6404e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1309,6 +1309,7 @@ const SettingDesc _patch_settings[] = { SDT_CONDBOOL(GameSettings, construction.road_stop_on_town_road, 47, SL_MAX_VERSION, 0, 0, true, STR_CONFIG_PATCHES_STOP_ON_TOWN_ROAD, NULL), SDT_CONDBOOL(GameSettings, station.adjacent_stations, 62, SL_MAX_VERSION, 0, 0, true, STR_CONFIG_PATCHES_ADJACENT_STATIONS, NULL), SDT_CONDBOOL(GameSettings, economy.station_noise_level, 96, SL_MAX_VERSION, 0, 0, false, STR_CONFIG_PATCHES_NOISE_LEVEL, InvalidateTownViewWindow), + SDT_CONDBOOL(GameSettings, station.distant_join_stations, 106, SL_MAX_VERSION, 0, 0, true, STR_CONFIG_PATCHES_DISTANT_JOIN_STATIONS, NULL), SDT_BOOL(GameSettings, economy.inflation, 0, 0, true, STR_CONFIG_PATCHES_INFLATION, NULL), SDT_VAR(GameSettings, construction.raw_industry_construction, SLE_UINT8, 0,MS, 0, 0, 2, 0, STR_CONFIG_PATCHES_RAW_INDUSTRY_CONSTRUCTION_METHOD, InvalidateBuildIndustryWindow), diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 94a9891f38..e7e5bb864e 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -671,6 +671,7 @@ static const char *_patches_stations[] = { "order.gradual_loading", "construction.road_stop_on_town_road", "station.adjacent_stations", + "station.distant_join_stations", "economy.station_noise_level", }; diff --git a/src/settings_type.h b/src/settings_type.h index c51a5130b1..9c0bf1ac77 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -328,6 +328,7 @@ struct StationSettings { bool join_stations; ///< allow joining of train stations bool nonuniform_stations; ///< allow nonuniform train stations bool adjacent_stations; ///< allow stations to be built directly adjacent to other stations + bool distant_join_stations; ///< allow to join non-adjacent stations bool always_small_airport; ///< always allow small airports byte station_spread; ///< amount a station may spread }; diff --git a/src/station.cpp b/src/station.cpp index df256adb36..bb961b07f7 100644 --- a/src/station.cpp +++ b/src/station.cpp @@ -104,6 +104,8 @@ Station::~Station() xy = INVALID_TILE; + InvalidateWindowData(WC_SELECT_STATION, 0, 0); + for (CargoID c = 0; c < NUM_CARGO; c++) { goods[c].cargo.Truncate(0); } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 076000a2a7..fa623ab7f9 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -908,23 +908,24 @@ static void GetStationLayout(byte *layout, int numtracks, int plat_len, const St * @param tile_org starting position of station dragging/placement * @param flags operation to perform * @param p1 various bitstuffed elements - * - p1 = (bit 0) - orientation (Axis) + * - p1 = (bit 0- 3) - railtype (p1 & 0xF) + * - p1 = (bit 4) - orientation (Axis) * - p1 = (bit 8-15) - number of tracks * - p1 = (bit 16-23) - platform length * - p1 = (bit 24) - allow stations directly adjacent to other stations. * @param p2 various bitstuffed elements - * - p2 = (bit 0- 3) - railtype (p2 & 0xF) - * - p2 = (bit 8-15) - custom station class - * - p2 = (bit 16-23) - custom station id + * - p2 = (bit 0- 7) - custom station class + * - p2 = (bit 8-15) - custom station id + * - p2 = (bit 16-31) - station ID to join (INVALID_STATION if build new one) */ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, uint32 p2, const char *text) { /* Does the authority allow this? */ if (!(flags & DC_NO_TOWN_RATING) && !CheckIfAuthorityAllows(tile_org)) return CMD_ERROR; - if (!ValParamRailtype((RailType)(p2 & 0xF))) return CMD_ERROR; + if (!ValParamRailtype((RailType)(p1 & 0xF))) return CMD_ERROR; /* unpack parameters */ - Axis axis = Extract(p1); + Axis axis = Extract(p1); uint numtracks = GB(p1, 8, 8); uint plat_len = GB(p1, 16, 8); @@ -937,6 +938,11 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, w_org = numtracks; } + StationID station_to_join = GB(p2, 16, 16); + bool distant_join = (station_to_join != INVALID_STATION); + + if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR; + if (h_org > _settings_game.station.station_spread || w_org > _settings_game.station.station_spread) return CMD_ERROR; /* these values are those that will be stored in train_tile and station_platforms */ @@ -959,7 +965,7 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, if (_settings_game.station.adjacent_stations) { if (est != INVALID_STATION) { - if (HasBit(p1, 24)) { + if (HasBit(p1, 24) && est != station_to_join) { /* You can't build an adjacent station over the top of one that * already exists. */ return_cmd_error(STR_MUST_REMOVE_RAILWAY_STATION_FIRST); @@ -982,6 +988,9 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, if (st == CHECK_STATIONS_ERR) return CMD_ERROR; } + /* Distant join */ + if (st == NULL && distant_join) st = GetStation(station_to_join); + /* See if there is a deleted station close to us. */ if (st == NULL) st = GetClosestDeletedStation(tile_org); @@ -1017,10 +1026,10 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, } /* Check if the given station class is valid */ - if (GB(p2, 8, 8) >= GetNumStationClasses()) return CMD_ERROR; + if (GB(p2, 0, 8) >= GetNumStationClasses()) return CMD_ERROR; /* Check if we can allocate a custom stationspec to this station */ - const StationSpec *statspec = GetCustomStationSpec((StationClassID)GB(p2, 8, 8), GB(p2, 16, 8)); + const StationSpec *statspec = GetCustomStationSpec((StationClassID)GB(p2, 0, 8), GB(p2, 8, 8)); int specindex = AllocateSpecToStation(statspec, st, flags & DC_EXEC); if (specindex == -1) return CMD_ERROR; @@ -1090,7 +1099,7 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, } } - MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, (RailType)GB(p2, 0, 4)); + MakeRailStation(tile, st->owner, st->index, axis, layout & ~1, (RailType)GB(p1, 0, 4)); SetCustomStationSpecIndex(tile, specindex); SetStationTileRandomBits(tile, GB(Random(), 0, 4)); SetStationAnimationFrame(tile, 0); @@ -1126,6 +1135,7 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1, st->MarkTilesDirty(false); UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); + InvalidateWindowData(WC_SELECT_STATION, 0, 0); InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_TRAINS); } @@ -1397,6 +1407,7 @@ static RoadStop **FindRoadStopSpot(bool truck_station, Station *st) * bit 1: 0 for normal, 1 for drive-through * bit 2..4: the roadtypes * bit 5: allow stations directly adjacent to other stations. + * bit 16..31: station ID to join (INVALID_STATION if build new one) */ CommandCost CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text) { @@ -1405,6 +1416,10 @@ CommandCost CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, bool build_over_road = is_drive_through && IsNormalRoadTile(tile); bool town_owned_road = false; RoadTypes rts = (RoadTypes)GB(p2, 2, 3); + StationID station_to_join = GB(p2, 16, 16); + bool distant_join = (station_to_join != INVALID_STATION); + + if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR; if (!AreValidRoadTypes(rts) || !HasRoadTypesAvail(_current_company, rts)) return CMD_ERROR; @@ -1462,6 +1477,9 @@ CommandCost CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, if (st == CHECK_STATIONS_ERR) return CMD_ERROR; } + /* Distant join */ + if (st == NULL && distant_join) st = GetStation(station_to_join); + /* Find a deleted station close to us */ if (st == NULL) st = GetClosestDeletedStation(tile); @@ -1518,6 +1536,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); + InvalidateWindowData(WC_SELECT_STATION, 0, 0); InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_ROADVEHS); } @@ -1818,11 +1837,17 @@ void UpdateAirportsNoise() * @param tile tile where airport will be built * @param flags operation to perform * @param p1 airport type, @see airport.h - * @param p2 (bit 0) - allow airports directly adjacent to other airports. + * @param p2 various bitstuffed elements + * - p2 = (bit 0) - allow airports directly adjacent to other airports. + * - p2 = (bit 16-31) - station ID to join (INVALID_STATION if build new one) */ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text) { bool airport_upgrade = true; + StationID station_to_join = GB(p2, 16, 16); + bool distant_join = (station_to_join != INVALID_STATION); + + if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR; /* Check if a valid, buildable airport was chosen for construction */ if (p1 > lengthof(_airport_sections) || !HasBit(GetValidAirports(), p1)) return CMD_ERROR; @@ -1875,6 +1900,9 @@ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, st = NULL; } + /* Distant join */ + if (st == NULL && distant_join) st = GetStation(station_to_join); + /* Find a deleted station close to us */ if (st == NULL) st = GetClosestDeletedStation(tile); @@ -1940,6 +1968,7 @@ CommandCost CmdBuildAirport(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); + InvalidateWindowData(WC_SELECT_STATION, 0, 0); InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_PLANES); @@ -2125,10 +2154,15 @@ static const byte _dock_h_chk[4] = { 1, 2, 1, 2 }; * @param tile tile where dock will be built * @param flags operation to perform * @param p1 (bit 0) - allow docks directly adjacent to other docks. - * @param p2 unused + * @param p2 bit 16-31: station ID to join (INVALID_STATION if build new one) */ CommandCost CmdBuildDock(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, const char *text) { + StationID station_to_join = GB(p2, 16, 16); + bool distant_join = (station_to_join != INVALID_STATION); + + if (distant_join && (!_settings_game.station.distant_join_stations || !IsValidStationID(station_to_join))) return CMD_ERROR; + DiagDirection direction = GetInclinedSlopeDirection(GetTileSlope(tile, NULL)); if (direction == INVALID_DIAGDIR) return_cmd_error(STR_304B_SITE_UNSUITABLE); direction = ReverseDiagDir(direction); @@ -2170,6 +2204,9 @@ CommandCost CmdBuildDock(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, con if (st == CHECK_STATIONS_ERR) return CMD_ERROR; } + /* Distant join */ + if (st == NULL && distant_join) st = GetStation(station_to_join); + /* Find a deleted station close to us */ if (st == NULL) st = GetClosestDeletedStation(tile); @@ -2212,6 +2249,7 @@ CommandCost CmdBuildDock(TileIndex tile, uint32 flags, uint32 p1, uint32 p2, con UpdateStationVirtCoordDirty(st); UpdateStationAcceptance(st, false); + InvalidateWindowData(WC_SELECT_STATION, 0, 0); InvalidateWindowData(WC_STATION_LIST, st->owner, 0); InvalidateWindowWidget(WC_STATION_VIEW, st->index, SVW_SHIPS); } diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 2873b13be4..1fa3392593 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -25,6 +25,13 @@ #include "gfx_func.h" #include "widgets/dropdown_func.h" #include "newgrf_cargo.h" +#include "map_func.h" +#include "settings_type.h" +#include "tile_map.h" +#include "station_map.h" +#include "tilehighlight_func.h" +#include "core/smallvec_type.hpp" +#include "core/smallmap_type.hpp" #include "string_func.h" #include "company_base.h" #include "sortlist_type.h" @@ -974,3 +981,266 @@ void ShowStationViewWindow(StationID station) { AllocateWindowDescFront(&_station_view_desc, station); } + +static SmallVector _stations_nearby_list; +static SmallMap _deleted_stations_nearby; + +/** Context for FindStationsNearby */ +struct FindNearbyStationContext { + TileIndex tile; ///< Base tile of station to be built + uint w; ///< Width of station to be built + uint h; ///< Height of station to be built +}; + +/** + * Add station on this tile to _stations_nearby_list if it's fully within the + * station spread. + * @param tile Tile just being checked + * @param user_data Pointer to FindNearbyStationContext context + */ +static bool AddNearbyStation(TileIndex tile, void *user_data) +{ + FindNearbyStationContext *ctx = (FindNearbyStationContext *)user_data; + + /* First check if there was a deleted station here */ + SmallPair *dst = _deleted_stations_nearby.Find(tile); + if (dst != _deleted_stations_nearby.End()) { + _stations_nearby_list.Include(dst->second); + return false; + } + + /* Check if own station and if we stay within station spread */ + if (!IsTileType(tile, MP_STATION)) return false; + + StationID sid = GetStationIndex(tile); + Station *st = GetStation(sid); + if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false; + + if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST)) { + *_stations_nearby_list.Append() = sid; + } + + return false; // We want to include *all* nearby stations +} + +/** + * Circulate around the to-be-built station to find stations we could join. + * Make sure that only stations are returned where joining wouldn't exceed + * station spread and are our own station. + * @param tile Base tile of the to-be-built station + * @param w Width of the to-be-built station + * @param h Height of the to-be-built station + * @param distant_join Search for adjacent stations (false) or stations fully + * within station spread + **/ +static const Station *FindStationsNearby(TileIndex tile, int w, int h, bool distant_join) +{ + FindNearbyStationContext ctx; + ctx.tile = tile; + ctx.w = w; + ctx.h = h; + + _stations_nearby_list.Clear(); + _deleted_stations_nearby.Clear(); + + /* Check the inside, to return, if we sit on another station */ + BEGIN_TILE_LOOP(t, w, h, tile) + if (t < MapSize() && IsTileType(t, MP_STATION)) return GetStationByTile(t); + END_TILE_LOOP(t, w, h, tile) + + /* Look for deleted stations */ + const Station *st; + FOR_ALL_STATIONS(st) { + if (st->facilities == 0 && st->owner == _local_company) { + /* Include only within station spread (yes, it is strictly less than) */ + if (max(DistanceMax(tile, st->xy), DistanceMax(TILE_ADDXY(tile, w - 1, h - 1), st->xy)) < _settings_game.station.station_spread) { + _deleted_stations_nearby.Insert(st->xy, st->index); + + /* Add the station when it's within where we're going to build */ + if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) && + IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) { + AddNearbyStation(st->xy, &ctx); + } + } + } + } + + /* Only search tiles where we have a chance to stay within the station spread. + * The complete check needs to be done in the callback as we don't know the + * extent of the found station, yet. */ + if (distant_join && min(w, h) >= _settings_game.station.station_spread) return NULL; + uint max_dist = distant_join ? _settings_game.station.station_spread - min(w, h) : 1; + + tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N)); + CircularTileSearch(&tile, max_dist, w, h, AddNearbyStation, &ctx); + + return NULL; +} + +enum JoinStationWidgets { + JSW_WIDGET_CLOSEBOX = 0, + JSW_WIDGET_CAPTION, + JSW_PANEL, + JSW_SCROLLBAR, + JSW_EMPTY, + JSW_RESIZEBOX, +}; + +static const Widget _select_station_widgets[] = { +{ WWT_CLOSEBOX, RESIZE_NONE, COLOUR_DARK_GREEN, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, +{ WWT_CAPTION, RESIZE_RIGHT, COLOUR_DARK_GREEN, 11, 199, 0, 13, STR_SELECT_STATION_TO_JOIN, STR_018C_WINDOW_TITLE_DRAG_THIS}, +{ WWT_PANEL, RESIZE_RB, COLOUR_DARK_GREEN, 0, 187, 14, 79, 0x0, STR_NULL}, +{ WWT_SCROLLBAR, RESIZE_LRB, COLOUR_DARK_GREEN, 188, 199, 14, 79, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST}, +{ WWT_PANEL, RESIZE_RTB, COLOUR_DARK_GREEN, 0, 187, 80, 91, 0x0, STR_NULL}, +{ WWT_RESIZEBOX, RESIZE_LRTB, COLOUR_DARK_GREEN, 188, 199, 80, 91, 0x0, STR_RESIZE_BUTTON}, +{ WIDGETS_END}, +}; + +struct SelectStationWindow : Window { + CommandContainer select_station_cmd; ///< Command to build new station + TileIndex tile; ///< Base tile of new station + int size_x; ///< Size in x direction of new station + int size_y; ///< Size in y direction of new station + + SelectStationWindow(const WindowDesc *desc, CommandContainer cmd) : + Window(desc, 0), + select_station_cmd(cmd), + tile(TileVirtXY(_thd.pos.x, _thd.pos.y)), + size_x(_thd.size.x / TILE_SIZE), + size_y(_thd.size.y / TILE_SIZE) + { + this->vscroll.cap = 6; + this->resize.step_height = 10; + _thd.lock_pos = true; + _thd.lock_size = true; + + FindStationsNearby(this->tile, this->size_x, this->size_y, true); + + this->FindWindowPlacementAndResize(desc); + } + + ~SelectStationWindow() + { + _thd.lock_pos = false; + _thd.lock_size = false; + } + + virtual void OnPaint() + { + SetVScrollCount(this, _stations_nearby_list.Length() + 1); + + this->DrawWidgets(); + + uint y = 17; + if (this->vscroll.pos == 0) { + DrawStringTruncated(3, y, STR_CREATE_SPLITTED_STATION, TC_FROMSTRING, this->widget[JSW_PANEL].right - 5); + y += 10; + } + + for (uint i = max(1, this->vscroll.pos); i <= _stations_nearby_list.Length(); ++i, y += 10) { + /* Don't draw anything if it extends past the end of the window. */ + if (i - this->vscroll.pos >= this->vscroll.cap) break; + + const Station *st = GetStation(_stations_nearby_list[i - 1]); + SetDParam(0, st->index); + SetDParam(1, st->facilities); + DrawStringTruncated(3, y, STR_3049_0, TC_FROMSTRING, this->widget[JSW_PANEL].right - 5); + } + } + + virtual void OnClick(Point pt, int widget) + { + if (widget != JSW_PANEL) return; + + uint32 st_index = (pt.y - 16) / 10 + this->vscroll.pos; + bool distant_join = (st_index > 0); + if (distant_join) st_index--; + + if (distant_join && st_index >= _stations_nearby_list.Length()) return; + + /* Insert station to be joined into stored command */ + SB(this->select_station_cmd.p2, 16, 16, + (distant_join ? _stations_nearby_list[st_index] : INVALID_STATION)); + + /* Execute stored Command */ + DoCommandP(&this->select_station_cmd); + + /* Close Window; this might cause double frees! */ + DeleteWindowById(WC_SELECT_STATION, 0); + } + + virtual void OnTick() + { + if (_thd.dirty & 2) { + _thd.dirty &= ~2; + this->SetDirty(); + } + } + + virtual void OnResize(Point new_size, Point delta) + { + this->vscroll.cap = (this->widget[JSW_PANEL].bottom - this->widget[JSW_PANEL].top) / 10; + } + + virtual void OnInvalidateData(int data) + { + FindStationsNearby(this->tile, this->size_x, this->size_y, true); + this->SetDirty(); + } +}; + +static const WindowDesc _select_station_desc = { + WDP_AUTO, WDP_AUTO, 200, 92, 200, 182, + WC_SELECT_STATION, WC_NONE, + WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE, + _select_station_widgets, +}; + + +/** + * Check whether we need to show the station selection window. + * @param cmd Command to build the station. + * @param w Width of the to-be-built station + * @param h Height of the to-be-built station + * @return whether we need to show the station selection window. + */ +static bool StationJoinerNeeded(CommandContainer cmd, int w, int h) +{ + if (CmdFailed(DoCommand(&cmd, DC_NO_WATER | DC_AUTO))) return false; + + /* Only show selection if distant join is enabled in the settings */ + if (!_settings_game.station.distant_join_stations) return false; + + /* If a window is already opened, we always return true */ + if (FindWindowById(WC_SELECT_STATION, 0) != NULL) return true; + + /* only show the popup, if we press ctrl */ + if (!_ctrl_pressed) return false; + + /* First test for adjacent station */ + FindStationsNearby(cmd.tile, w, h, false); + int neighbour_station_count = _stations_nearby_list.Length(); + /* Now test for stations fully within station spread */ + const Station *st = FindStationsNearby(cmd.tile, w, h, true); + if (_settings_game.station.adjacent_stations) { + return (neighbour_station_count == 0 || _stations_nearby_list.Length() > 1) && st == NULL; + } else { + return neighbour_station_count == 0 && _stations_nearby_list.Length() > 0 && st == NULL; + } +} + +/** + * Show the station selection window when needed. If not, build the station. + * @param cmd Command to build the station. + * @param w Width of the to-be-built station + * @param h Height of the to-be-built station + */ +void ShowSelectStationIfNeeded(CommandContainer cmd, int w, int h) +{ + if (StationJoinerNeeded(cmd, w, h)) { + if (BringWindowToFrontById(WC_SELECT_STATION, 0)) return; + new SelectStationWindow(&_select_station_desc, cmd); + } else { + DoCommandP(&cmd); + } +} diff --git a/src/station_gui.h b/src/station_gui.h index a2e108268d..1addc6ab9d 100644 --- a/src/station_gui.h +++ b/src/station_gui.h @@ -5,6 +5,8 @@ #ifndef STATION_GUI_H #define STATION_GUI_H +#include "command_type.h" + /** Enum for CompanyStations, referring to _company_stations_widgets */ enum StationListWidgets { SLW_CLOSEBOX = 0, ///< Close 'X' button @@ -57,4 +59,6 @@ enum StationCoverageType { int DrawStationCoverageAreaText(int sx, int sy, StationCoverageType sct, int rad, bool supplies); void CheckRedrawStationCoverage(const Window *w); +void ShowSelectStationIfNeeded(CommandContainer cmd, int w, int h); + #endif /* STATION_GUI_H */ diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index 1ad04d9ea2..59fd3237c0 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -71,6 +71,9 @@ struct TileHighlightData { ViewportPlaceMethod select_method; ViewportDragDropSelectionProcess select_proc; + bool lock_pos; //< If position changes are taken, or not + bool lock_size; //< If size changes are taken, or not + TileIndex redsq; }; diff --git a/src/viewport.cpp b/src/viewport.cpp index 88278309b1..4572665e4f 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2175,11 +2175,17 @@ void UpdateTileSelection() /* clear the old selection? */ if (_thd.drawstyle) SetSelectionTilesDirty(); - _thd.drawstyle = _thd.new_drawstyle; - _thd.pos = _thd.new_pos; - _thd.size = _thd.new_size; + if (!_thd.lock_pos) { + _thd.pos = _thd.new_pos; + _thd.drawstyle = _thd.new_drawstyle; + } + + if (!_thd.lock_size) { + _thd.size = _thd.new_size; + _thd.dirty = 0xff; + } + _thd.outersize = _thd.new_outersize; - _thd.dirty = 0xff; /* draw the new selection? */ if (_thd.new_drawstyle) SetSelectionTilesDirty(); @@ -2619,7 +2625,7 @@ calc_heightdiff_single_direction:; y = sy + Clamp(y - sy, -limit, limit); } /* Fallthrough */ case VPM_X_AND_Y: { /* drag an X by Y area */ - if (_settings_client.gui.measure_tooltip) { + if (_settings_client.gui.measure_tooltip && !_thd.lock_size) { static const StringID measure_strings_area[] = { STR_NULL, STR_NULL, STR_MEASURE_AREA, STR_MEASURE_AREA_HEIGHTDIFF }; @@ -2713,6 +2719,10 @@ void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, ViewportHighlightMode mode void SetObjectToPlace(CursorID icon, SpriteID pal, ViewportHighlightMode mode, WindowClass window_class, WindowNumber window_num) { + /* unlock position and size */ + _thd.lock_pos = false; + _thd.lock_size = false; + /* undo clicking on button and drag & drop */ if (_thd.place_mode != VHM_NONE || _special_mouse_mode == WSM_DRAGDROP) { Window *w = FindWindowById(_thd.window_class, _thd.window_number); diff --git a/src/window_type.h b/src/window_type.h index 64d05717a5..338031cec2 100644 --- a/src/window_type.h +++ b/src/window_type.h @@ -95,6 +95,7 @@ enum WindowClass { WC_COMPANY_PASSWORD_WINDOW, WC_OSK, WC_WAYPOINT_VIEW, + WC_SELECT_STATION, WC_INVALID = 0xFFFF };