diff options
Diffstat (limited to 'src/station_gui.cpp')
-rw-r--r-- | src/station_gui.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/station_gui.cpp b/src/station_gui.cpp index 2873b13be..1fa339259 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<StationViewWindow>(&_station_view_desc, station); } + +static SmallVector<StationID, 8> _stations_nearby_list; +static SmallMap<TileIndex, StationID, 8> _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<TileIndex, StationID> *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<uint>(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); + } +} |