(svn r14919) -Feature: distant joining of stations (Frostregen and PhilSophus)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user