Merge branch 'polyline_track_tool' into jgrpp
# Conflicts: # src/rail_cmd.cpp # src/rail_gui.cpp # src/viewport.cpp # src/viewport_func.h
This commit is contained in:
@@ -24,6 +24,7 @@
|
||||
#include "cmd_helper.h"
|
||||
#include "tunnelbridge_map.h"
|
||||
#include "road_gui.h"
|
||||
#include "tilehighlight_func.h"
|
||||
|
||||
#include "widgets/bridge_widget.h"
|
||||
|
||||
@@ -72,6 +73,8 @@ void CcBuildBridge(const CommandCost &result, TileIndex end_tile, uint32 p1, uin
|
||||
DiagDirection start_direction = ReverseDiagDir(GetTunnelBridgeDirection(p1));
|
||||
ConnectRoadToStructure(p1, start_direction);
|
||||
}
|
||||
|
||||
StoreRailPlacementEndpoints(p1, end_tile, (TileX(p1) == TileX(end_tile)) ? TRACK_Y : TRACK_X, false);
|
||||
}
|
||||
|
||||
/** Window class for handling the bridge-build GUI. */
|
||||
|
@@ -2448,6 +2448,7 @@ STR_RAIL_TOOLBAR_MAGLEV_CONSTRUCTION_CAPTION :Maglev Construc
|
||||
|
||||
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK :{BLACK}Build railway track. Ctrl toggles build/remove for railway construction. Shift toggles building/showing cost estimate
|
||||
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL :{BLACK}Build railway track using the Autorail mode. Ctrl toggles build/remove for railway construction. Shift toggles building/showing cost estimate
|
||||
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_POLYRAIL :{BLACK}Build railway track using the Polyline mode. Ctrl toggles build/remove for railway construction. Shift toggles building/showing cost estimate
|
||||
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_TRAIN_DEPOT_FOR_BUILDING :{BLACK}Build train depot (for buying and servicing trains). Shift toggles building/showing cost estimate
|
||||
STR_RAIL_TOOLBAR_TOOLTIP_CONVERT_RAIL_TO_WAYPOINT :{BLACK}Convert rail to waypoint. Ctrl enables joining waypoints. Shift toggles building/showing cost estimate
|
||||
STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_STATION :{BLACK}Build railway station. Ctrl enables joining stations. Shift toggles building/showing cost estimate
|
||||
|
@@ -121,6 +121,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
|
||||
InitializeEconomy();
|
||||
|
||||
ResetObjectToPlace();
|
||||
ResetRailPlacementSnapping();
|
||||
|
||||
GamelogReset();
|
||||
GamelogStartAction(GLAT_START);
|
||||
|
@@ -49,6 +49,7 @@ typedef SmallVector<Train *, 16> TrainList;
|
||||
RailtypeInfo _railtypes[RAILTYPE_END];
|
||||
RailType _sorted_railtypes[RAILTYPE_END];
|
||||
uint8 _sorted_railtypes_size;
|
||||
TileIndex _rail_track_endtile; ///< The end of a rail track; as hidden return from the rail build/remove command for GUI purposes.
|
||||
|
||||
assert_compile(sizeof(_original_railtypes) <= sizeof(_railtypes));
|
||||
|
||||
@@ -442,6 +443,8 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
||||
Track track = Extract<Track, 0, 3>(p2);
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
|
||||
_rail_track_endtile = INVALID_TILE;
|
||||
|
||||
if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
|
||||
|
||||
Slope tileh = GetTileSlope(tile);
|
||||
@@ -458,7 +461,10 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
||||
|
||||
ret = CheckTrackCombination(tile, trackbit, flags);
|
||||
if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
|
||||
if (ret.Failed()) return ret;
|
||||
if (ret.Failed()) {
|
||||
if (ret.GetErrorMessage() == STR_ERROR_ALREADY_BUILT) _rail_track_endtile = tile;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
|
||||
if (ret.Failed()) return ret;
|
||||
@@ -549,6 +555,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
||||
}
|
||||
|
||||
if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) {
|
||||
_rail_track_endtile = tile;
|
||||
return_cmd_error(STR_ERROR_ALREADY_BUILT);
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
@@ -588,6 +595,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
||||
}
|
||||
|
||||
cost.AddCost(RailBuildCost(railtype));
|
||||
_rail_track_endtile = tile;
|
||||
return cost;
|
||||
}
|
||||
|
||||
@@ -606,6 +614,8 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
bool crossing = false;
|
||||
|
||||
_rail_track_endtile = INVALID_TILE;
|
||||
|
||||
if (!ValParamTrackOrientation(track)) return CMD_ERROR;
|
||||
TrackBits trackbit = TrackToTrackBits(track);
|
||||
|
||||
@@ -733,6 +743,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
||||
if (v != NULL) TryPathReserve(v, true);
|
||||
}
|
||||
|
||||
_rail_track_endtile = tile;
|
||||
return cost;
|
||||
}
|
||||
|
||||
@@ -867,6 +878,8 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint3
|
||||
bool remove = HasBit(p2, 8);
|
||||
bool fail_if_obstacle = HasBit(p2, 9);
|
||||
|
||||
_rail_track_endtile = INVALID_TILE;
|
||||
|
||||
if ((!remove && !ValParamRailtype(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
|
||||
if (p1 >= MapSize()) return CMD_ERROR;
|
||||
TileIndex end_tile = p1;
|
||||
@@ -878,10 +891,12 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint3
|
||||
bool had_success = false;
|
||||
CommandCost last_error = CMD_ERROR;
|
||||
for (;;) {
|
||||
TileIndex last_endtile = _rail_track_endtile;
|
||||
CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
|
||||
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
if (_rail_track_endtile == INVALID_TILE) _rail_track_endtile = last_endtile;
|
||||
if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
|
||||
if (fail_if_obstacle) return last_error;
|
||||
break;
|
||||
|
125
src/rail_gui.cpp
125
src/rail_gui.cpp
@@ -57,9 +57,14 @@ static SignalVariant _cur_signal_variant; ///< set the signal variant (for si
|
||||
static SignalType _cur_signal_type; ///< set the signal type (for signal GUI)
|
||||
static uint _cur_signal_button; ///< set the signal button (for signal GUI)
|
||||
|
||||
extern TileIndex _rail_track_endtile; // rail_cmd.cpp
|
||||
|
||||
/* Map the setting: default_signal_type to the corresponding signal type */
|
||||
static const SignalType _default_signal_type[] = {SIGTYPE_NORMAL, SIGTYPE_PBS, SIGTYPE_PBS_ONEWAY};
|
||||
|
||||
static const int HOTKEY_POLYRAIL = 0x1000;
|
||||
static const int HOTKEY_NEW_POLYRAIL = 0x1001;
|
||||
|
||||
struct RailStationGUISettings {
|
||||
Axis orientation; ///< Currently selected rail station orientation
|
||||
|
||||
@@ -96,13 +101,21 @@ void CcPlaySound1E(const CommandCost &result, TileIndex tile, uint32 p1, uint32
|
||||
if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_20_SPLAT_RAIL, tile);
|
||||
}
|
||||
|
||||
static void GenericPlaceRail(TileIndex tile, int cmd)
|
||||
static CommandContainer GenericPlaceRailCmd(TileIndex tile, Track track)
|
||||
{
|
||||
DoCommandP(tile, _cur_railtype, cmd,
|
||||
CommandContainer ret = {
|
||||
tile, // tile
|
||||
_cur_railtype, // p1
|
||||
track, // p2
|
||||
_remove_button_clicked ?
|
||||
CMD_REMOVE_SINGLE_RAIL | CMD_MSG(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK) :
|
||||
CMD_BUILD_SINGLE_RAIL | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK),
|
||||
CcPlaySound1E);
|
||||
CMD_BUILD_SINGLE_RAIL | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK), // cmd
|
||||
CcPlaySound1E, // callback
|
||||
0,
|
||||
"" // text
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,6 +311,7 @@ void CcBuildRailTunnel(const CommandCost &result, TileIndex tile, uint32 p1, uin
|
||||
if (result.Succeeded()) {
|
||||
if (_settings_client.sound.confirm) SndPlayTileFx(SND_20_SPLAT_RAIL, tile);
|
||||
if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
|
||||
StoreRailPlacementEndpoints(tile, _build_tunnel_endtile, TileX(tile) == TileX(_build_tunnel_endtile) ? TRACK_Y : TRACK_X, false);
|
||||
} else {
|
||||
SetRedErrorSquare(_build_tunnel_endtile);
|
||||
}
|
||||
@@ -327,7 +341,7 @@ static bool RailToolbar_CtrlChanged(Window *w)
|
||||
|
||||
/* allow ctrl to switch remove mode only for these widgets */
|
||||
for (uint i = WID_RAT_BUILD_NS; i <= WID_RAT_BUILD_STATION; i++) {
|
||||
if ((i <= WID_RAT_AUTORAIL || i >= WID_RAT_BUILD_WAYPOINT) && w->IsWidgetLowered(i)) {
|
||||
if ((i <= WID_RAT_POLYRAIL || i >= WID_RAT_BUILD_WAYPOINT) && w->IsWidgetLowered(i)) {
|
||||
ToggleRailButton_Remove(w);
|
||||
return true;
|
||||
}
|
||||
@@ -371,25 +385,46 @@ static void BuildRailClick_Remove(Window *w)
|
||||
}
|
||||
}
|
||||
|
||||
static void DoRailroadTrack(int mode)
|
||||
static CommandContainer DoRailroadTrackCmd(TileIndex start_tile, TileIndex end_tile, Track track)
|
||||
{
|
||||
DoCommandP(TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), _cur_railtype | (mode << 5),
|
||||
CommandContainer ret = {
|
||||
start_tile, // tile
|
||||
end_tile, // p1
|
||||
_cur_railtype | (track << 5), // p2
|
||||
_remove_button_clicked ?
|
||||
CMD_REMOVE_RAILROAD_TRACK | CMD_MSG(STR_ERROR_CAN_T_REMOVE_RAILROAD_TRACK) :
|
||||
CMD_BUILD_RAILROAD_TRACK | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK),
|
||||
CcPlaySound1E);
|
||||
CMD_BUILD_RAILROAD_TRACK | CMD_MSG(STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK), // cmd
|
||||
CcPlaySound1E, // callback
|
||||
0,
|
||||
"" // text
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void HandleAutodirPlacement()
|
||||
{
|
||||
int trackstat = _thd.drawstyle & HT_DIR_MASK; // 0..5
|
||||
Track track = (Track)(_thd.drawstyle & HT_DIR_MASK); // 0..5
|
||||
TileIndex start_tile = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
|
||||
TileIndex end_tile = TileVirtXY(_thd.selend.x, _thd.selend.y);
|
||||
|
||||
if (_thd.drawstyle & HT_RAIL) { // one tile case
|
||||
GenericPlaceRail(TileVirtXY(_thd.selend.x, _thd.selend.y), trackstat);
|
||||
return;
|
||||
CommandContainer cmd = (_thd.drawstyle & HT_RAIL) ?
|
||||
GenericPlaceRailCmd(end_tile, track) : // one tile case
|
||||
DoRailroadTrackCmd(start_tile, end_tile, track); // multitile selection
|
||||
|
||||
/* When overbuilding existing tracks in polyline mode we just want to move the
|
||||
* snap point without altering the user with the "already built" error. Don't
|
||||
* execute the command right away, firstly check if tracks are being overbuilt. */
|
||||
if (!(_thd.place_mode & HT_POLY) || _shift_pressed ||
|
||||
DoCommand(&cmd, DC_AUTO | DC_NO_WATER).GetErrorMessage() != STR_ERROR_ALREADY_BUILT) {
|
||||
/* place tracks */
|
||||
if (!DoCommandP(&cmd)) return;
|
||||
}
|
||||
|
||||
DoRailroadTrack(trackstat);
|
||||
/* save new snap points for the polyline tool */
|
||||
if (!_shift_pressed && _rail_track_endtile != INVALID_TILE) {
|
||||
StoreRailPlacementEndpoints(start_tile, _rail_track_endtile, track, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,6 +518,7 @@ struct BuildRailToolbarWindow : Window {
|
||||
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_EW)->widget_data = rti->gui_sprites.build_ew_rail;
|
||||
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_Y)->widget_data = rti->gui_sprites.build_y_rail;
|
||||
this->GetWidget<NWidgetCore>(WID_RAT_AUTORAIL)->widget_data = rti->gui_sprites.auto_rail;
|
||||
this->GetWidget<NWidgetCore>(WID_RAT_POLYRAIL)->widget_data = rti->gui_sprites.auto_rail;
|
||||
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_DEPOT)->widget_data = rti->gui_sprites.build_depot;
|
||||
this->GetWidget<NWidgetCore>(WID_RAT_CONVERT_RAIL)->widget_data = rti->gui_sprites.convert_rail;
|
||||
this->GetWidget<NWidgetCore>(WID_RAT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel;
|
||||
@@ -511,6 +547,7 @@ struct BuildRailToolbarWindow : Window {
|
||||
case WID_RAT_BUILD_EW:
|
||||
case WID_RAT_BUILD_Y:
|
||||
case WID_RAT_AUTORAIL:
|
||||
case WID_RAT_POLYRAIL:
|
||||
case WID_RAT_BUILD_WAYPOINT:
|
||||
case WID_RAT_BUILD_STATION:
|
||||
case WID_RAT_BUILD_SIGNALS:
|
||||
@@ -542,6 +579,15 @@ struct BuildRailToolbarWindow : Window {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void DrawWidget(const Rect &r, int widget) const
|
||||
{
|
||||
if (widget == WID_RAT_POLYRAIL) {
|
||||
Dimension d = GetSpriteSize(SPR_BLOT);
|
||||
uint offset = this->IsWidgetLowered(WID_RAT_POLYRAIL) ? 1 : 0;
|
||||
DrawSprite(SPR_BLOT, PALETTE_TO_GREY, (r.left + r.right - d.width) / 2 + offset, (r.top + r.bottom - d.height) / 2 + offset);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void OnClick(Point pt, int widget, int click_count)
|
||||
{
|
||||
if (widget < WID_RAT_BUILD_NS) return;
|
||||
@@ -573,6 +619,40 @@ struct BuildRailToolbarWindow : Window {
|
||||
this->last_user_action = widget;
|
||||
break;
|
||||
|
||||
case WID_RAT_POLYRAIL: {
|
||||
bool was_snap = CurrentlySnappingRailPlacement();
|
||||
bool was_open = this->IsWidgetLowered(WID_RAT_POLYRAIL);
|
||||
bool do_snap;
|
||||
bool do_open;
|
||||
/* "polyrail" hotkey - activate polyline tool in snapping mode, close the tool if snapping mode is already active
|
||||
* "new_polyrail" hotkey - activate polyline tool in non-snapping (new line) mode, close the tool if non-snapping mode is already active
|
||||
* button ctrl-clicking - switch between snapping and non-snapping modes, open the tool in non-snapping mode if it is closed
|
||||
* button clicking - open the tool in non-snapping mode, close the tool if it is opened */
|
||||
if (this->last_user_action == HOTKEY_POLYRAIL) {
|
||||
do_snap = true;
|
||||
do_open = !was_open || !was_snap;
|
||||
} else if (this->last_user_action == HOTKEY_NEW_POLYRAIL) {
|
||||
do_snap = false;
|
||||
do_open = !was_open || was_snap;
|
||||
} else if (_ctrl_pressed) {
|
||||
do_snap = !was_open || !was_snap;
|
||||
do_open = true;
|
||||
} else {
|
||||
do_snap = false;
|
||||
do_open = !was_open;
|
||||
}
|
||||
/* close the tool explicitly so it can be re-opened in different snapping mode */
|
||||
if (was_open) ResetObjectToPlace();
|
||||
/* open the tool in desired mode */
|
||||
if (do_open && HandlePlacePushButton(this, WID_RAT_POLYRAIL, GetRailTypeInfo(railtype)->cursor.autorail, do_snap ? (HT_RAIL | HT_POLY) : (HT_RAIL | HT_NEW_POLY))) {
|
||||
/* if we are re-opening the tool but we couldn't switch the snapping
|
||||
* then close the tool instead of appearing to be doing nothing */
|
||||
if (was_open && do_snap != CurrentlySnappingRailPlacement()) ResetObjectToPlace();
|
||||
}
|
||||
this->last_user_action = WID_RAT_POLYRAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_RAT_DEMOLISH:
|
||||
HandlePlacePushButton(this, WID_RAT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL);
|
||||
this->last_user_action = widget;
|
||||
@@ -637,7 +717,15 @@ struct BuildRailToolbarWindow : Window {
|
||||
virtual EventState OnHotkey(int hotkey)
|
||||
{
|
||||
MarkTileDirtyByTile(TileVirtXY(_thd.pos.x, _thd.pos.y)); // redraw tile selection
|
||||
return Window::OnHotkey(hotkey);
|
||||
|
||||
if (hotkey == HOTKEY_POLYRAIL || hotkey == HOTKEY_NEW_POLYRAIL) {
|
||||
/* Indicate to the OnClick that the action comes from a hotkey rather
|
||||
* then from a click and that the CTRL state should be ignored. */
|
||||
this->last_user_action = hotkey;
|
||||
hotkey = WID_RAT_POLYRAIL;
|
||||
}
|
||||
|
||||
return this->Window::OnHotkey(hotkey);
|
||||
}
|
||||
|
||||
virtual void OnPlaceObject(Point pt, TileIndex tile)
|
||||
@@ -660,6 +748,7 @@ struct BuildRailToolbarWindow : Window {
|
||||
break;
|
||||
|
||||
case WID_RAT_AUTORAIL:
|
||||
case WID_RAT_POLYRAIL:
|
||||
VpStartPlaceSizing(tile, VPM_RAILDIRS, DDSP_PLACE_RAIL);
|
||||
break;
|
||||
|
||||
@@ -807,6 +896,8 @@ static EventState RailToolbarGlobalHotkeys(int hotkey)
|
||||
}
|
||||
|
||||
const uint16 _railtoolbar_autorail_keys[] = {'5', 'A' | WKC_GLOBAL_HOTKEY, 0};
|
||||
const uint16 _railtoolbar_polyrail_keys[] = {'5' | WKC_CTRL, 'A' | WKC_CTRL | WKC_GLOBAL_HOTKEY, 0};
|
||||
const uint16 _railtoolbar_new_poly_keys[] = {'5' | WKC_CTRL | WKC_SHIFT, 'A' | WKC_CTRL | WKC_SHIFT | WKC_GLOBAL_HOTKEY, 0};
|
||||
|
||||
static Hotkey railtoolbar_hotkeys[] = {
|
||||
Hotkey('1', "build_ns", WID_RAT_BUILD_NS),
|
||||
@@ -814,6 +905,8 @@ static Hotkey railtoolbar_hotkeys[] = {
|
||||
Hotkey('3', "build_ew", WID_RAT_BUILD_EW),
|
||||
Hotkey('4', "build_y", WID_RAT_BUILD_Y),
|
||||
Hotkey(_railtoolbar_autorail_keys, "autorail", WID_RAT_AUTORAIL),
|
||||
Hotkey(_railtoolbar_polyrail_keys, "polyrail", HOTKEY_POLYRAIL),
|
||||
Hotkey(_railtoolbar_new_poly_keys, "new_polyrail", HOTKEY_NEW_POLYRAIL),
|
||||
Hotkey('6', "demolish", WID_RAT_DEMOLISH),
|
||||
Hotkey('7', "depot", WID_RAT_BUILD_DEPOT),
|
||||
Hotkey('8', "waypoint", WID_RAT_BUILD_WAYPOINT),
|
||||
@@ -844,6 +937,8 @@ static const NWidgetPart _nested_build_rail_widgets[] = {
|
||||
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_RAIL_NW, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TRACK),
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_AUTORAIL),
|
||||
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTORAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_AUTORAIL),
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_POLYRAIL),
|
||||
SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTORAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_POLYRAIL),
|
||||
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetDataTip(0x0, STR_NULL), EndContainer(),
|
||||
|
||||
|
@@ -908,6 +908,7 @@ void SQGSWindow_Register(Squirrel *engine)
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RAT_BUILD_EW, "WID_RAT_BUILD_EW");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RAT_BUILD_Y, "WID_RAT_BUILD_Y");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RAT_AUTORAIL, "WID_RAT_AUTORAIL");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RAT_POLYRAIL, "WID_RAT_POLYRAIL");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RAT_DEMOLISH, "WID_RAT_DEMOLISH");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RAT_BUILD_DEPOT, "WID_RAT_BUILD_DEPOT");
|
||||
SQGSWindow.DefSQConst(engine, ScriptWindow::WID_RAT_BUILD_WAYPOINT, "WID_RAT_BUILD_WAYPOINT");
|
||||
|
@@ -1996,6 +1996,7 @@ public:
|
||||
WID_RAT_BUILD_EW = ::WID_RAT_BUILD_EW, ///< Build rail along the game view X axis.
|
||||
WID_RAT_BUILD_Y = ::WID_RAT_BUILD_Y, ///< Build rail along the game grid Y axis.
|
||||
WID_RAT_AUTORAIL = ::WID_RAT_AUTORAIL, ///< Autorail tool.
|
||||
WID_RAT_POLYRAIL = ::WID_RAT_POLYRAIL, ///< Polyline rail tool.
|
||||
WID_RAT_DEMOLISH = ::WID_RAT_DEMOLISH, ///< Destroy something with dynamite!
|
||||
WID_RAT_BUILD_DEPOT = ::WID_RAT_BUILD_DEPOT, ///< Build a depot.
|
||||
WID_RAT_BUILD_WAYPOINT = ::WID_RAT_BUILD_WAYPOINT, ///< Build a waypoint.
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "gfx_type.h"
|
||||
#include "tilehighlight_type.h"
|
||||
#include "track_type.h"
|
||||
|
||||
void PlaceProc_DemolishArea(TileIndex tile);
|
||||
bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_tile, TileIndex end_tile);
|
||||
@@ -30,6 +31,10 @@ void VpSetPlaceSizingLimit(int limit);
|
||||
|
||||
void UpdateTileSelection();
|
||||
|
||||
void StoreRailPlacementEndpoints(TileIndex start_tile, TileIndex end_tile, Track start_track, bool bidirectional = true);
|
||||
void ResetRailPlacementSnapping();
|
||||
bool CurrentlySnappingRailPlacement();
|
||||
|
||||
extern TileHighlightData _thd;
|
||||
|
||||
#endif /* TILEHIGHLIGHT_FUNC_H */
|
||||
|
@@ -28,6 +28,8 @@ enum HighLightStyle {
|
||||
HT_RAIL = 0x080, ///< autorail (one piece), lower bits: direction
|
||||
HT_VEHICLE = 0x100, ///< vehicle is accepted as target as well (bitmask)
|
||||
HT_DIAGONAL = 0x200, ///< Also allow 'diagonal rectangles'. Only usable in combination with #HT_RECT or #HT_POINT.
|
||||
HT_POLY = 0x400, ///< polyline mode; connect highlighted track with previous one
|
||||
HT_NEW_POLY = 0xC00, ///< start completly new polyline; implies #HT_POLY
|
||||
HT_DRAG_MASK = 0x0F8, ///< Mask for the tile drag-type modes.
|
||||
|
||||
/* lower bits (used with HT_LINE and HT_RAIL):
|
||||
@@ -54,11 +56,15 @@ struct TileHighlightData {
|
||||
|
||||
Point new_pos; ///< New value for \a pos; used to determine whether to redraw the selection.
|
||||
Point new_size; ///< New value for \a size; used to determine whether to redraw the selection.
|
||||
Point new_offs; ///< New value for \a offs; used to determine whether to redraw the selection.
|
||||
Point new_outersize; ///< New value for \a outersize; used to determine whether to redraw the selection.
|
||||
byte dirty; ///< Whether the build station window needs to redraw due to the changed selection.
|
||||
|
||||
Point selstart; ///< The location where the dragging started.
|
||||
Point selend; ///< The location where the drag currently ends.
|
||||
Point selstart2; ///< The location where the second segment of a polyline track starts.
|
||||
Point selend2; ///< The location where the second segment of a polyline track ends.
|
||||
HighLightStyle dir2; ///< Direction of the second segment of a polyline track, HT_DIR_END if second segment is not selected. HT_LINE drawstyle.
|
||||
byte sizelimit; ///< Whether the selection is limited in length, and what the maximum length is.
|
||||
|
||||
HighLightStyle drawstyle; ///< Lower bits 0-3 are reserved for detailed highlight information.
|
||||
|
705
src/viewport.cpp
705
src/viewport.cpp
@@ -70,6 +70,8 @@
|
||||
#include "smallmap_colours.h"
|
||||
#include "table/tree_land.h"
|
||||
#include "blitter/32bpp_base.hpp"
|
||||
#include "core/math_func.hpp"
|
||||
#include "core/smallvec_type.hpp"
|
||||
#include "landscape.h"
|
||||
#include "viewport_func.h"
|
||||
#include "station_base.h"
|
||||
@@ -166,6 +168,34 @@ typedef SmallVector<ChildScreenSpriteToDraw, 16> ChildScreenSpriteToDrawVector;
|
||||
typedef std::list<std::pair<int, OrderType> > RankOrderTypeList;
|
||||
typedef std::map<TileIndex, RankOrderTypeList> RouteStepsMap;
|
||||
|
||||
enum RailSnapMode {
|
||||
RSM_NO_SNAP,
|
||||
RSM_SNAP_TO_TILE,
|
||||
RSM_SNAP_TO_RAIL,
|
||||
};
|
||||
|
||||
/**
|
||||
* Snapping point for a track.
|
||||
*
|
||||
* Point where a track (rail/road/other) can be snapped to while selecting tracks with polyline
|
||||
* tool (HT_POLY). Besides of x/y coordinates expressed in tile "units" it contains a set of
|
||||
* allowed line directions.
|
||||
*/
|
||||
struct LineSnapPoint : Point {
|
||||
uint8 dirs; ///< Allowed line directions, set of #Direction bits.
|
||||
};
|
||||
|
||||
typedef SmallVector<LineSnapPoint, 4> LineSnapPoints; ///< Set of snapping points
|
||||
|
||||
/** Coordinates of a polyline track made of 2 connected line segments. */
|
||||
struct Polyline {
|
||||
Point start; ///< The point where the first segment starts (as given in LineSnapPoint).
|
||||
Direction first_dir; ///< Direction of the first line segment.
|
||||
uint first_len; ///< Length of the first segment - number of track pieces.
|
||||
Direction second_dir; ///< Direction of the second line segment.
|
||||
uint second_len; ///< Length of the second segment - number of track pieces.
|
||||
};
|
||||
|
||||
/** Data structure storing rendering information */
|
||||
struct ViewportDrawer {
|
||||
DrawPixelInfo dpi;
|
||||
@@ -231,6 +261,16 @@ static VpSpriteSorter _vp_sprite_sorter = NULL;
|
||||
|
||||
const byte *_pal2trsp_remap_ptr = NULL;
|
||||
|
||||
static RailSnapMode _rail_snap_mode = RSM_NO_SNAP; ///< Type of rail track snapping (polyline tool).
|
||||
static LineSnapPoints _tile_snap_points; ///< Tile to which a rail track will be snapped to (polyline tool).
|
||||
static LineSnapPoints _rail_snap_points; ///< Set of points where a rail track will be snapped to (polyline tool).
|
||||
static LineSnapPoint _current_snap_lock; ///< Start point and direction at which selected track is locked on currently (while dragging in polyline mode).
|
||||
|
||||
static RailSnapMode GetRailSnapMode();
|
||||
static void SetRailSnapMode(RailSnapMode mode);
|
||||
static TileIndex GetRailSnapTile();
|
||||
static void SetRailSnapTile(TileIndex tile);
|
||||
|
||||
static Point MapXYZToViewport(const ViewPort *vp, int x, int y, int z)
|
||||
{
|
||||
Point p = RemapCoords(x, y, z);
|
||||
@@ -867,13 +907,17 @@ static bool IsInRangeInclusive(int begin, int end, int check)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a point is inside the selected a diagonal rectangle given by _thd.size and _thd.pos
|
||||
* Checks whether a point is inside the selected rectangle given by _thd.size, _thd.pos and _thd.diagonal
|
||||
* @param x The x coordinate of the point to be checked.
|
||||
* @param y The y coordinate of the point to be checked.
|
||||
* @return True if the point is inside the rectangle, else false.
|
||||
*/
|
||||
bool IsInsideRotatedRectangle(int x, int y)
|
||||
static bool IsInsideSelectedRectangle(int x, int y)
|
||||
{
|
||||
if (!_thd.diagonal) {
|
||||
return IsInsideBS(x, _thd.pos.x, _thd.size.x) && IsInsideBS(y, _thd.pos.y, _thd.size.y);
|
||||
}
|
||||
|
||||
int dist_a = (_thd.size.x + _thd.size.y); // Rotated coordinate system for selected rectangle.
|
||||
int dist_b = (_thd.size.x - _thd.size.y); // We don't have to divide by 2. It's all relative!
|
||||
int a = ((x - _thd.pos.x) + (y - _thd.pos.y)); // Rotated coordinate system for the point under scrutiny.
|
||||
@@ -990,34 +1034,26 @@ static void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal)
|
||||
DrawSelectionSprite(sel, pal, ti, 7, FOUNDATION_PART_NORMAL);
|
||||
}
|
||||
|
||||
static bool IsPartOfAutoLine(int px, int py)
|
||||
static HighLightStyle GetPartOfAutoLine(int px, int py, const Point &selstart, const Point &selend, HighLightStyle dir)
|
||||
{
|
||||
px -= _thd.selstart.x;
|
||||
py -= _thd.selstart.y;
|
||||
if (!IsInRangeInclusive(selstart.x & ~TILE_UNIT_MASK, selend.x & ~TILE_UNIT_MASK, px)) return HT_DIR_END;
|
||||
if (!IsInRangeInclusive(selstart.y & ~TILE_UNIT_MASK, selend.y & ~TILE_UNIT_MASK, py)) return HT_DIR_END;
|
||||
|
||||
if ((_thd.drawstyle & HT_DRAG_MASK) != HT_LINE) return false;
|
||||
px -= selstart.x & ~TILE_UNIT_MASK;
|
||||
py -= selstart.y & ~TILE_UNIT_MASK;
|
||||
|
||||
switch (_thd.drawstyle & HT_DIR_MASK) {
|
||||
case HT_DIR_X: return py == 0; // x direction
|
||||
case HT_DIR_Y: return px == 0; // y direction
|
||||
case HT_DIR_HU: return px == -py || px == -py - 16; // horizontal upper
|
||||
case HT_DIR_HL: return px == -py || px == -py + 16; // horizontal lower
|
||||
case HT_DIR_VL: return px == py || px == py + 16; // vertical left
|
||||
case HT_DIR_VR: return px == py || px == py - 16; // vertical right
|
||||
default:
|
||||
NOT_REACHED();
|
||||
}
|
||||
switch (dir) {
|
||||
case HT_DIR_X: return (py == 0) ? HT_DIR_X : HT_DIR_END;
|
||||
case HT_DIR_Y: return (px == 0) ? HT_DIR_Y : HT_DIR_END;
|
||||
case HT_DIR_HU: return (px == -py) ? HT_DIR_HU : (px == -py - (int)TILE_SIZE) ? HT_DIR_HL : HT_DIR_END;
|
||||
case HT_DIR_HL: return (px == -py) ? HT_DIR_HL : (px == -py + (int)TILE_SIZE) ? HT_DIR_HU : HT_DIR_END;
|
||||
case HT_DIR_VL: return (px == py) ? HT_DIR_VL : (px == py + (int)TILE_SIZE) ? HT_DIR_VR : HT_DIR_END;
|
||||
case HT_DIR_VR: return (px == py) ? HT_DIR_VR : (px == py - (int)TILE_SIZE) ? HT_DIR_VL : HT_DIR_END;
|
||||
default: NOT_REACHED(); break;
|
||||
}
|
||||
|
||||
/* [direction][side] */
|
||||
static const HighLightStyle _autorail_type[6][2] = {
|
||||
{ HT_DIR_X, HT_DIR_X },
|
||||
{ HT_DIR_Y, HT_DIR_Y },
|
||||
{ HT_DIR_HU, HT_DIR_HL },
|
||||
{ HT_DIR_HL, HT_DIR_HU },
|
||||
{ HT_DIR_VL, HT_DIR_VR },
|
||||
{ HT_DIR_VR, HT_DIR_VL }
|
||||
};
|
||||
return HT_DIR_END;
|
||||
}
|
||||
|
||||
#include "table/autorail.h"
|
||||
|
||||
@@ -1025,18 +1061,18 @@ static const HighLightStyle _autorail_type[6][2] = {
|
||||
* Draws autorail highlights.
|
||||
*
|
||||
* @param *ti TileInfo Tile that is being drawn
|
||||
* @param autorail_type Offset into _AutorailTilehSprite[][]
|
||||
* @param autorail_type \c HT_DIR_XXX, offset into _AutorailTilehSprite[][]
|
||||
* @param pal Palette to use, -1 to autodetect
|
||||
*/
|
||||
static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
|
||||
static void DrawAutorailSelection(const TileInfo *ti, HighLightStyle autorail_type, PaletteID pal = -1)
|
||||
{
|
||||
SpriteID image;
|
||||
PaletteID pal;
|
||||
int offset;
|
||||
|
||||
FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
|
||||
Slope autorail_tileh = RemoveHalftileSlope(ti->tileh);
|
||||
if (IsHalftileSlope(ti->tileh)) {
|
||||
static const uint _lower_rail[4] = { 5U, 2U, 4U, 3U };
|
||||
static const HighLightStyle _lower_rail[CORNER_END] = { HT_DIR_VR, HT_DIR_HU, HT_DIR_VL, HT_DIR_HL }; // CORNER_W, CORNER_S, CORNER_E, CORNER_N
|
||||
Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh);
|
||||
if (autorail_type != _lower_rail[halftile_corner]) {
|
||||
foundation_part = FOUNDATION_PART_HALFTILE;
|
||||
@@ -1045,16 +1081,17 @@ static void DrawAutorailSelection(const TileInfo *ti, uint autorail_type)
|
||||
}
|
||||
}
|
||||
|
||||
assert(autorail_type < HT_DIR_END);
|
||||
offset = _AutorailTilehSprite[autorail_tileh][autorail_type];
|
||||
if (offset >= 0) {
|
||||
image = SPR_AUTORAIL_BASE + offset;
|
||||
pal = PAL_NONE;
|
||||
if (pal == (PaletteID)-1) pal = _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE;
|
||||
} else {
|
||||
image = SPR_AUTORAIL_BASE - offset;
|
||||
pal = PALETTE_SEL_TILE_RED;
|
||||
if (pal == (PaletteID)-1) pal = PALETTE_SEL_TILE_RED;
|
||||
}
|
||||
|
||||
DrawSelectionSprite(image, _thd.make_square_red ? PALETTE_SEL_TILE_RED : pal, ti, 7, foundation_part);
|
||||
DrawSelectionSprite(image, pal, ti, 7, foundation_part);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1067,21 +1104,25 @@ static void DrawTileSelection(const TileInfo *ti)
|
||||
bool is_redsq = _thd.redsq == ti->tile;
|
||||
if (is_redsq) DrawTileSelectionRect(ti, PALETTE_TILE_RED_PULSATING);
|
||||
|
||||
/* No tile selection active? */
|
||||
if ((_thd.drawstyle & HT_DRAG_MASK) == HT_NONE) return;
|
||||
switch (_thd.drawstyle & HT_DRAG_MASK) {
|
||||
default: break; // No tile selection active?
|
||||
|
||||
if (_thd.diagonal) { // We're drawing a 45 degrees rotated (diagonal) rectangle
|
||||
if (IsInsideRotatedRectangle((int)ti->x, (int)ti->y)) goto draw_inner;
|
||||
return;
|
||||
case HT_RECT:
|
||||
if (!is_redsq) {
|
||||
if (IsInsideSelectedRectangle(ti->x, ti->y)) {
|
||||
DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
|
||||
} else if (_thd.outersize.x > 0 &&
|
||||
/* Check if it's inside the outer area? */
|
||||
IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
|
||||
IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
|
||||
/* Draw a blue rect. */
|
||||
DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Inside the inner area? */
|
||||
if (IsInsideBS(ti->x, _thd.pos.x, _thd.size.x) &&
|
||||
IsInsideBS(ti->y, _thd.pos.y, _thd.size.y)) {
|
||||
draw_inner:
|
||||
if (_thd.drawstyle & HT_RECT) {
|
||||
if (!is_redsq) DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE);
|
||||
} else if (_thd.drawstyle & HT_POINT) {
|
||||
case HT_POINT:
|
||||
if (IsInsideSelectedRectangle(ti->x, ti->y)) {
|
||||
/* Figure out the Z coordinate for the single dot. */
|
||||
int z = 0;
|
||||
FoundationPart foundation_part = FOUNDATION_PART_NORMAL;
|
||||
@@ -1098,35 +1139,26 @@ draw_inner:
|
||||
}
|
||||
}
|
||||
DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part);
|
||||
} else if (_thd.drawstyle & HT_RAIL) {
|
||||
/* autorail highlight piece under cursor */
|
||||
HighLightStyle type = _thd.drawstyle & HT_DIR_MASK;
|
||||
assert(type < HT_DIR_END);
|
||||
DrawAutorailSelection(ti, _autorail_type[type][0]);
|
||||
} else if (IsPartOfAutoLine(ti->x, ti->y)) {
|
||||
/* autorail highlighting long line */
|
||||
HighLightStyle dir = _thd.drawstyle & HT_DIR_MASK;
|
||||
uint side;
|
||||
|
||||
if (dir == HT_DIR_X || dir == HT_DIR_Y) {
|
||||
side = 0;
|
||||
} else {
|
||||
TileIndex start = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
|
||||
side = Delta(Delta(TileX(start), TileX(ti->tile)), Delta(TileY(start), TileY(ti->tile)));
|
||||
}
|
||||
break;
|
||||
|
||||
DrawAutorailSelection(ti, _autorail_type[dir][side]);
|
||||
}
|
||||
return;
|
||||
case HT_RAIL:
|
||||
if (ti->tile == TileVirtXY(_thd.pos.x, _thd.pos.y)) {
|
||||
assert((_thd.drawstyle & HT_DIR_MASK) < HT_DIR_END);
|
||||
DrawAutorailSelection(ti, _thd.drawstyle & HT_DIR_MASK);
|
||||
}
|
||||
break;
|
||||
|
||||
/* Check if it's inside the outer area? */
|
||||
if (!is_redsq && _thd.outersize.x > 0 &&
|
||||
IsInsideBS(ti->x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
|
||||
IsInsideBS(ti->y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) {
|
||||
/* Draw a blue rect. */
|
||||
DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE);
|
||||
return;
|
||||
case HT_LINE: {
|
||||
HighLightStyle type = GetPartOfAutoLine(ti->x, ti->y, _thd.selstart, _thd.selend, _thd.drawstyle & HT_DIR_MASK);
|
||||
if (type < HT_DIR_END) {
|
||||
DrawAutorailSelection(ti, type);
|
||||
} else if (_thd.dir2 < HT_DIR_END) {
|
||||
type = GetPartOfAutoLine(ti->x, ti->y, _thd.selstart2, _thd.selend2, _thd.dir2);
|
||||
if (type < HT_DIR_END) DrawAutorailSelection(ti, type, PALETTE_SEL_TILE_BLUE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3031,7 +3063,7 @@ static void SetSelectionTilesDirty()
|
||||
int x_start = _thd.pos.x;
|
||||
int y_start = _thd.pos.y;
|
||||
|
||||
if (_thd.outersize.x != 0) {
|
||||
if (_thd.outersize.x != 0 || _thd.outersize.y != 0) {
|
||||
x_size += _thd.outersize.x;
|
||||
x_start += _thd.offs.x;
|
||||
y_size += _thd.outersize.y;
|
||||
@@ -3273,7 +3305,7 @@ bool HandleViewportDoubleClicked(Window *w, int x, int y)
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleViewportClicked(const ViewPort *vp, int x, int y)
|
||||
bool HandleViewportClicked(const ViewPort *vp, int x, int y, bool double_click)
|
||||
{
|
||||
/* No click in smallmap mode except for plan making. */
|
||||
if (vp->zoom >= ZOOM_LVL_DRAW_MAP && !(_thd.place_mode == HT_POINT && _thd.select_proc == DDSP_DRAW_PLANLINE)) return true;
|
||||
@@ -3286,6 +3318,18 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y)
|
||||
|
||||
/* Vehicle placement mode already handled above. */
|
||||
if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) {
|
||||
if (_thd.place_mode & HT_POLY) {
|
||||
/* In polyline mode double-clicking on a single white line, finishes current polyline.
|
||||
* If however the user double-clicks on a line that has a white and a blue section,
|
||||
* both lines (white and blue) will be constructed consecutively. */
|
||||
static bool stop_snap_on_double_click = false;
|
||||
if (double_click && stop_snap_on_double_click) {
|
||||
SetRailSnapMode(RSM_NO_SNAP);
|
||||
return true;
|
||||
}
|
||||
stop_snap_on_double_click = !(_thd.drawstyle & HT_LINE) || (_thd.dir2 == HT_DIR_END);
|
||||
}
|
||||
|
||||
PlaceObject();
|
||||
return true;
|
||||
}
|
||||
@@ -3413,8 +3457,8 @@ void SetTileSelectSize(int w, int h)
|
||||
|
||||
void SetTileSelectBigSize(int ox, int oy, int sx, int sy)
|
||||
{
|
||||
_thd.offs.x = ox * TILE_SIZE;
|
||||
_thd.offs.y = oy * TILE_SIZE;
|
||||
_thd.new_offs.x = ox * TILE_SIZE;
|
||||
_thd.new_offs.y = oy * TILE_SIZE;
|
||||
_thd.new_outersize.x = sx * TILE_SIZE;
|
||||
_thd.new_outersize.y = sy * TILE_SIZE;
|
||||
}
|
||||
@@ -3454,7 +3498,36 @@ Window *TileHighlightData::GetCallbackWnd()
|
||||
return FindWindowById(this->window_class, this->window_number);
|
||||
}
|
||||
|
||||
static HighLightStyle CalcPolyrailDrawstyle(Point pt, bool dragging);
|
||||
|
||||
static inline void CalcNewPolylineOutersize()
|
||||
{
|
||||
/* use the 'outersize' to mark the second (blue) part of a polyline selection */
|
||||
if (_thd.dir2 < HT_DIR_END) {
|
||||
/* get bounds of the second part */
|
||||
int outer_x1 = _thd.selstart2.x & ~TILE_UNIT_MASK;
|
||||
int outer_y1 = _thd.selstart2.y & ~TILE_UNIT_MASK;
|
||||
int outer_x2 = _thd.selend2.x & ~TILE_UNIT_MASK;
|
||||
int outer_y2 = _thd.selend2.y & ~TILE_UNIT_MASK;
|
||||
if (outer_x1 > outer_x2) Swap(outer_x1, outer_x2);
|
||||
if (outer_y1 > outer_y2) Swap(outer_y1, outer_y2);
|
||||
/* include the first part */
|
||||
outer_x1 = min<int>(outer_x1, _thd.new_pos.x);
|
||||
outer_y1 = min<int>(outer_y1, _thd.new_pos.y);
|
||||
outer_x2 = max<int>(outer_x2, _thd.new_pos.x + _thd.new_size.x - TILE_SIZE);
|
||||
outer_y2 = max<int>(outer_y2, _thd.new_pos.y + _thd.new_size.y - TILE_SIZE);
|
||||
/* write new values */
|
||||
_thd.new_offs.x = outer_x1 - _thd.new_pos.x;
|
||||
_thd.new_offs.y = outer_y1 - _thd.new_pos.y;
|
||||
_thd.new_outersize.x = outer_x2 - outer_x1 + TILE_SIZE - _thd.new_size.x;
|
||||
_thd.new_outersize.y = outer_y2 - outer_y1 + TILE_SIZE - _thd.new_size.y;
|
||||
} else {
|
||||
_thd.new_offs.x = 0;
|
||||
_thd.new_offs.y = 0;
|
||||
_thd.new_outersize.x = 0;
|
||||
_thd.new_outersize.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates tile highlighting for all cases.
|
||||
@@ -3511,10 +3584,42 @@ void UpdateTileSelection()
|
||||
y1 += TILE_SIZE / 2;
|
||||
break;
|
||||
case HT_RAIL:
|
||||
case HT_LINE:
|
||||
/* HT_POLY */
|
||||
if (_thd.place_mode & HT_POLY) {
|
||||
RailSnapMode snap_mode = GetRailSnapMode();
|
||||
if (snap_mode == RSM_NO_SNAP ||
|
||||
(snap_mode == RSM_SNAP_TO_TILE && GetRailSnapTile() == TileVirtXY(pt.x, pt.y))) {
|
||||
new_drawstyle = GetAutorailHT(pt.x, pt.y);
|
||||
_thd.new_offs.x = 0;
|
||||
_thd.new_offs.y = 0;
|
||||
_thd.new_outersize.x = 0;
|
||||
_thd.new_outersize.y = 0;
|
||||
_thd.dir2 = HT_DIR_END;
|
||||
} else {
|
||||
new_drawstyle = CalcPolyrailDrawstyle(pt, false);
|
||||
if (new_drawstyle != HT_NONE) {
|
||||
x1 = _thd.selstart.x & ~TILE_UNIT_MASK;
|
||||
y1 = _thd.selstart.y & ~TILE_UNIT_MASK;
|
||||
int x2 = _thd.selend.x & ~TILE_UNIT_MASK;
|
||||
int y2 = _thd.selend.y & ~TILE_UNIT_MASK;
|
||||
if (x1 > x2) Swap(x1, x2);
|
||||
if (y1 > y2) Swap(y1, y2);
|
||||
_thd.new_pos.x = x1;
|
||||
_thd.new_pos.y = y1;
|
||||
_thd.new_size.x = x2 - x1 + TILE_SIZE;
|
||||
_thd.new_size.y = y2 - y1 + TILE_SIZE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* HT_RAIL */
|
||||
if (_thd.place_mode & HT_RAIL) {
|
||||
/* Draw one highlighted tile in any direction */
|
||||
new_drawstyle = GetAutorailHT(pt.x, pt.y);
|
||||
break;
|
||||
case HT_LINE:
|
||||
}
|
||||
/* HT_LINE */
|
||||
switch (_thd.place_mode & HT_DIR_MASK) {
|
||||
case HT_DIR_X: new_drawstyle = HT_LINE | HT_DIR_X; break;
|
||||
case HT_DIR_Y: new_drawstyle = HT_LINE | HT_DIR_Y; break;
|
||||
@@ -3533,6 +3638,8 @@ void UpdateTileSelection()
|
||||
}
|
||||
_thd.selstart.x = x1 & ~TILE_UNIT_MASK;
|
||||
_thd.selstart.y = y1 & ~TILE_UNIT_MASK;
|
||||
_thd.selend.x = x1;
|
||||
_thd.selend.y = y1;
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED();
|
||||
@@ -3543,10 +3650,13 @@ void UpdateTileSelection()
|
||||
}
|
||||
}
|
||||
|
||||
if (new_drawstyle & HT_LINE) CalcNewPolylineOutersize();
|
||||
|
||||
/* redraw selection */
|
||||
if (_thd.drawstyle != new_drawstyle ||
|
||||
_thd.pos.x != _thd.new_pos.x || _thd.pos.y != _thd.new_pos.y ||
|
||||
_thd.size.x != _thd.new_size.x || _thd.size.y != _thd.new_size.y ||
|
||||
_thd.offs.x != _thd.new_offs.x || _thd.offs.y != _thd.new_offs.y ||
|
||||
_thd.outersize.x != _thd.new_outersize.x ||
|
||||
_thd.outersize.y != _thd.new_outersize.y ||
|
||||
_thd.diagonal != new_diagonal) {
|
||||
@@ -3556,6 +3666,7 @@ void UpdateTileSelection()
|
||||
_thd.drawstyle = new_drawstyle;
|
||||
_thd.pos = _thd.new_pos;
|
||||
_thd.size = _thd.new_size;
|
||||
_thd.offs = _thd.new_offs;
|
||||
_thd.outersize = _thd.new_outersize;
|
||||
_thd.diagonal = new_diagonal;
|
||||
_thd.dirty = 0xff;
|
||||
@@ -3605,6 +3716,11 @@ void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDrag
|
||||
} else if (_thd.place_mode & (HT_RAIL | HT_LINE)) {
|
||||
_thd.place_mode = HT_SPECIAL | others;
|
||||
_thd.next_drawstyle = _thd.drawstyle | others;
|
||||
_current_snap_lock.x = -1;
|
||||
if ((_thd.place_mode & HT_POLY) != 0 && GetRailSnapMode() == RSM_NO_SNAP) {
|
||||
SetRailSnapMode(RSM_SNAP_TO_TILE);
|
||||
SetRailSnapTile(tile);
|
||||
}
|
||||
} else {
|
||||
_thd.place_mode = HT_SPECIAL | others;
|
||||
_thd.next_drawstyle = HT_POINT | others;
|
||||
@@ -3796,8 +3912,32 @@ static int CalcHeightdiff(HighLightStyle style, uint distance, TileIndex start_t
|
||||
return (int)(h1 - h0) * TILE_HEIGHT_STEP;
|
||||
}
|
||||
|
||||
static void ShowLengthMeasurement(HighLightStyle style, TileIndex start_tile, TileIndex end_tile, TooltipCloseCondition close_cond = TCC_LEFT_CLICK, bool show_single_tile_length = false)
|
||||
{
|
||||
static const StringID measure_strings_length[] = {STR_NULL, STR_MEASURE_LENGTH, STR_MEASURE_LENGTH_HEIGHTDIFF};
|
||||
|
||||
if (_settings_client.gui.measure_tooltip) {
|
||||
uint distance = DistanceManhattan(start_tile, end_tile) + 1;
|
||||
byte index = 0;
|
||||
uint64 params[2];
|
||||
|
||||
if (show_single_tile_length || distance != 1) {
|
||||
int heightdiff = CalcHeightdiff(style, distance, start_tile, end_tile);
|
||||
/* If we are showing a tooltip for horizontal or vertical drags,
|
||||
* 2 tiles have a length of 1. To bias towards the ceiling we add
|
||||
* one before division. It feels more natural to count 3 lengths as 2 */
|
||||
if ((style & HT_DIR_MASK) != HT_DIR_X && (style & HT_DIR_MASK) != HT_DIR_Y) {
|
||||
distance = CeilDiv(distance, 2);
|
||||
}
|
||||
|
||||
params[index++] = distance;
|
||||
if (heightdiff != 0) params[index++] = heightdiff;
|
||||
}
|
||||
|
||||
ShowMeasurementTooltips(measure_strings_length[index], index, params, close_cond);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for underflowing the map.
|
||||
* @param test the variable to test for underflowing
|
||||
@@ -3827,6 +3967,170 @@ static void CheckOverflow(int &test, int &other, int max, int mult)
|
||||
test = max;
|
||||
}
|
||||
|
||||
static const uint X_DIRS = (1 << DIR_NE) | (1 << DIR_SW);
|
||||
static const uint Y_DIRS = (1 << DIR_SE) | (1 << DIR_NW);
|
||||
static const uint HORZ_DIRS = (1 << DIR_W) | (1 << DIR_E);
|
||||
static const uint VERT_DIRS = (1 << DIR_N) | (1 << DIR_S);
|
||||
|
||||
Trackdir PointDirToTrackdir(const Point &pt, Direction dir)
|
||||
{
|
||||
Trackdir ret;
|
||||
|
||||
if (IsDiagonalDirection(dir)) {
|
||||
ret = DiagDirToDiagTrackdir(DirToDiagDir(dir));
|
||||
} else {
|
||||
int x = pt.x & TILE_UNIT_MASK;
|
||||
int y = pt.y & TILE_UNIT_MASK;
|
||||
int ns = x + y;
|
||||
int we = y - x;
|
||||
if (HasBit(HORZ_DIRS, dir)) {
|
||||
ret = TrackDirectionToTrackdir(ns < (int)TILE_SIZE ? TRACK_UPPER : TRACK_LOWER, dir);
|
||||
} else {
|
||||
ret = TrackDirectionToTrackdir(we < 0 ? TRACK_LEFT : TRACK_RIGHT, dir);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool FindPolyline(const Point &pt, const LineSnapPoint &start, Polyline *ret)
|
||||
{
|
||||
/* relative coordinats of the mouse point (offset against the snap point) */
|
||||
int x = pt.x - start.x;
|
||||
int y = pt.y - start.y;
|
||||
int we = y - x;
|
||||
int ns = x + y;
|
||||
|
||||
/* in-tile alignment of the snap point (there are two variants: [0, 8] or [8, 0]) */
|
||||
uint align_x = start.x & TILE_UNIT_MASK;
|
||||
uint align_y = start.y & TILE_UNIT_MASK;
|
||||
assert((align_x == TILE_SIZE / 2 && align_y == 0 && !(start.dirs & X_DIRS)) || (align_x == 0 && align_y == TILE_SIZE / 2 && !(start.dirs & Y_DIRS)));
|
||||
|
||||
/* absolute distance between points (in tiles) */
|
||||
uint d_x = abs(RoundDivSU(x < 0 ? x - align_y : x + align_y, TILE_SIZE));
|
||||
uint d_y = abs(RoundDivSU(y < 0 ? y - align_x : y + align_x, TILE_SIZE));
|
||||
uint d_ns = abs(RoundDivSU(ns, TILE_SIZE));
|
||||
uint d_we = abs(RoundDivSU(we, TILE_SIZE));
|
||||
|
||||
/* Find on which quadrant is the mouse point (reltively to the snap point).
|
||||
* Numeration (clockwise like in Direction):
|
||||
* ortho diag
|
||||
* \ 2 / 2 | 3
|
||||
* \ / --+---> [we]
|
||||
* 1 X 3 1 | 0
|
||||
* / \ v
|
||||
* [x] 0 [y] [ns] */
|
||||
uint ortho_quadrant = 2 * (x < 0) + ((x < 0) != (y < 0)); // implicit cast: false/true --> 0/1
|
||||
uint diag_quadrant = 2 * (ns < 0) + ((ns < 0) != (we < 0));
|
||||
|
||||
/* direction from the snap point to the mouse point */
|
||||
Direction ortho_line_dir = ChangeDir(DIR_S, (DirDiff)(2 * ortho_quadrant)); // DIR_S is the middle of the ortho quadrant no. 0
|
||||
Direction diag_line_dir = ChangeDir(DIR_SE, (DirDiff)(2 * diag_quadrant)); // DIR_SE is the middle of the diag quadrant no. 0
|
||||
if (!HasBit(start.dirs, ortho_line_dir) && !HasBit(start.dirs, diag_line_dir)) return false;
|
||||
|
||||
/* length of booth segments of auto line (choosing orthogonal direction first) */
|
||||
uint ortho_len = 0, ortho_len2 = 0;
|
||||
if (HasBit(start.dirs, ortho_line_dir)) {
|
||||
bool is_len_even = (align_x != 0) ? d_x >= d_y : d_x <= d_y;
|
||||
ortho_len = 2 * min(d_x, d_y) - (int)is_len_even;
|
||||
assert((int)ortho_len >= 0);
|
||||
if (d_ns == 0 || d_we == 0) { // just single segment?
|
||||
ortho_len++;
|
||||
} else {
|
||||
ortho_len2 = abs((int)d_x - (int)d_y) + (int)is_len_even;
|
||||
}
|
||||
}
|
||||
|
||||
/* length of booth segments of auto line (choosing diagonal direction first) */
|
||||
uint diag_len = 0, diag_len2 = 0;
|
||||
if (HasBit(start.dirs, diag_line_dir)) {
|
||||
if (d_x == 0 || d_y == 0) { // just single segment?
|
||||
diag_len = d_x + d_y;
|
||||
} else {
|
||||
diag_len = min(d_ns, d_we);
|
||||
diag_len2 = d_x + d_y - diag_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* choose the best variant */
|
||||
if (ortho_len != 0 && diag_len != 0) {
|
||||
/* in the first place, choose this line whose first segment ends up closer
|
||||
* to the mouse point (thus the second segment is shorter) */
|
||||
int cmp = ortho_len2 - diag_len2;
|
||||
/* if equeal, choose the shorter line */
|
||||
if (cmp == 0) cmp = ortho_len - diag_len;
|
||||
/* finally look at small "units" and choose the line which is closer to the mouse point */
|
||||
if (cmp == 0) cmp = min(abs(we), abs(ns)) - min(abs(x), abs(y));
|
||||
/* based on comparison, disable one of variants */
|
||||
if (cmp > 0) {
|
||||
ortho_len = 0;
|
||||
} else {
|
||||
diag_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* store results */
|
||||
if (ortho_len != 0) {
|
||||
ret->first_dir = ortho_line_dir;
|
||||
ret->first_len = ortho_len;
|
||||
ret->second_dir = (ortho_len2 != 0) ? diag_line_dir : INVALID_DIR;
|
||||
ret->second_len = ortho_len2;
|
||||
} else if (diag_len != 0) {
|
||||
ret->first_dir = diag_line_dir;
|
||||
ret->first_len = diag_len;
|
||||
ret->second_dir = (diag_len2 != 0) ? ortho_line_dir : INVALID_DIR;
|
||||
ret->second_len = diag_len2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
ret->start = start;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate squared euclidean distance between two points.
|
||||
* @param a the first point
|
||||
* @param b the second point
|
||||
* @return |b - a| ^ 2
|
||||
*/
|
||||
static inline uint SqrDist(const Point &a, const Point &b)
|
||||
{
|
||||
return (b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y);
|
||||
}
|
||||
|
||||
static LineSnapPoint *FindBestPolyline(const Point &pt, LineSnapPoint *snap_points, uint num_points, Polyline *ret)
|
||||
{
|
||||
/* Find the best polyline (a pair of two lines - the white one and the blue
|
||||
* one) led from any of saved snap points to the mouse cursor. */
|
||||
|
||||
LineSnapPoint *best_snap_point = NULL; // the best polyline we found so far is led from this snap point
|
||||
|
||||
for (int i = 0; i < (int)num_points; i++) {
|
||||
/* try to fit a polyline */
|
||||
Polyline polyline;
|
||||
if (!FindPolyline(pt, snap_points[i], &polyline)) continue; // skip non-matching snap points
|
||||
/* check whether we've found a better polyline */
|
||||
if (best_snap_point != NULL) {
|
||||
/* firstly choose shorter polyline (the one with smaller amount of
|
||||
* track pieces composing booth the white and the blue line) */
|
||||
uint cur_len = polyline.first_len + polyline.second_len;
|
||||
uint best_len = ret->first_len + ret->second_len;
|
||||
if (cur_len > best_len) continue;
|
||||
/* secondly choose that polyline which has longer first (white) line */
|
||||
if (cur_len == best_len && polyline.first_len < ret->first_len) continue;
|
||||
/* finally check euclidean distance to snap points and choose the
|
||||
* one which is closer */
|
||||
if (cur_len == best_len && polyline.first_len == ret->first_len && SqrDist(pt, snap_points[i]) >= SqrDist(pt, *best_snap_point)) continue;
|
||||
}
|
||||
/* save the found polyline */
|
||||
*ret = polyline;
|
||||
best_snap_point = &snap_points[i];
|
||||
}
|
||||
|
||||
return best_snap_point;
|
||||
}
|
||||
|
||||
/** while dragging */
|
||||
static void CalcRaildirsDrawstyle(int x, int y, int method)
|
||||
{
|
||||
@@ -4013,32 +4317,83 @@ static void CalcRaildirsDrawstyle(int x, int y, int method)
|
||||
}
|
||||
}
|
||||
|
||||
if (_settings_client.gui.measure_tooltip) {
|
||||
TileIndex t0 = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
|
||||
TileIndex t1 = TileVirtXY(x, y);
|
||||
uint distance = DistanceManhattan(t0, t1) + 1;
|
||||
byte index = 0;
|
||||
uint64 params[2];
|
||||
|
||||
if (distance != 1) {
|
||||
int heightdiff = CalcHeightdiff(b, distance, t0, t1);
|
||||
/* If we are showing a tooltip for horizontal or vertical drags,
|
||||
* 2 tiles have a length of 1. To bias towards the ceiling we add
|
||||
* one before division. It feels more natural to count 3 lengths as 2 */
|
||||
if ((b & HT_DIR_MASK) != HT_DIR_X && (b & HT_DIR_MASK) != HT_DIR_Y) {
|
||||
distance = CeilDiv(distance, 2);
|
||||
}
|
||||
|
||||
params[index++] = distance;
|
||||
if (heightdiff != 0) params[index++] = heightdiff;
|
||||
}
|
||||
|
||||
ShowMeasurementTooltips(measure_strings_length[index], index, params);
|
||||
}
|
||||
|
||||
_thd.selend.x = x;
|
||||
_thd.selend.y = y;
|
||||
_thd.dir2 = HT_DIR_END;
|
||||
_thd.next_drawstyle = b;
|
||||
|
||||
ShowLengthMeasurement(b, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
|
||||
}
|
||||
|
||||
static HighLightStyle CalcPolyrailDrawstyle(Point pt, bool dragging)
|
||||
{
|
||||
RailSnapMode snap_mode = GetRailSnapMode();
|
||||
|
||||
/* are we only within one tile? */
|
||||
if (snap_mode == RSM_SNAP_TO_TILE && GetRailSnapTile() == TileVirtXY(pt.x, pt.y)) {
|
||||
_thd.selend.x = pt.x;
|
||||
_thd.selend.y = pt.y;
|
||||
return GetAutorailHT(pt.x, pt.y);
|
||||
}
|
||||
|
||||
/* find the best track */
|
||||
Polyline line;
|
||||
|
||||
bool lock_snapping = dragging && snap_mode == RSM_SNAP_TO_RAIL;
|
||||
if (!lock_snapping) _current_snap_lock.x = -1;
|
||||
|
||||
const LineSnapPoint *snap_point;
|
||||
if (_current_snap_lock.x != -1) {
|
||||
snap_point = FindBestPolyline(pt, &_current_snap_lock, 1, &line);
|
||||
} else if (snap_mode == RSM_SNAP_TO_TILE) {
|
||||
snap_point = FindBestPolyline(pt, _tile_snap_points.Begin(), _tile_snap_points.Length(), &line);
|
||||
} else {
|
||||
assert(snap_mode == RSM_SNAP_TO_RAIL);
|
||||
snap_point = FindBestPolyline(pt, _rail_snap_points.Begin(), _rail_snap_points.Length(), &line);
|
||||
}
|
||||
|
||||
if (snap_point == NULL) return HT_NONE; // no match
|
||||
|
||||
if (lock_snapping && _current_snap_lock.x == -1) {
|
||||
/* lock down the snap point */
|
||||
_current_snap_lock = *snap_point;
|
||||
_current_snap_lock.dirs &= (1 << line.first_dir) | (1 << ReverseDir(line.first_dir));
|
||||
}
|
||||
|
||||
TileIndexDiffC first_dir = TileIndexDiffCByDir(line.first_dir);
|
||||
_thd.selstart.x = line.start.x;
|
||||
_thd.selstart.y = line.start.y;
|
||||
_thd.selend.x = _thd.selstart.x + line.first_len * first_dir.x * (IsDiagonalDirection(line.first_dir) ? TILE_SIZE : TILE_SIZE / 2);
|
||||
_thd.selend.y = _thd.selstart.y + line.first_len * first_dir.y * (IsDiagonalDirection(line.first_dir) ? TILE_SIZE : TILE_SIZE / 2);
|
||||
_thd.selstart2.x = _thd.selend.x;
|
||||
_thd.selstart2.y = _thd.selend.y;
|
||||
_thd.selstart.x += first_dir.x;
|
||||
_thd.selstart.y += first_dir.y;
|
||||
_thd.selend.x -= first_dir.x;
|
||||
_thd.selend.y -= first_dir.y;
|
||||
Trackdir seldir = PointDirToTrackdir(_thd.selstart, line.first_dir);
|
||||
_thd.selstart.x &= ~TILE_UNIT_MASK;
|
||||
_thd.selstart.y &= ~TILE_UNIT_MASK;
|
||||
|
||||
if (line.second_len != 0) {
|
||||
TileIndexDiffC second_dir = TileIndexDiffCByDir(line.second_dir);
|
||||
_thd.selend2.x = _thd.selstart2.x + line.second_len * second_dir.x * (IsDiagonalDirection(line.second_dir) ? TILE_SIZE : TILE_SIZE / 2);
|
||||
_thd.selend2.y = _thd.selstart2.y + line.second_len * second_dir.y * (IsDiagonalDirection(line.second_dir) ? TILE_SIZE : TILE_SIZE / 2);
|
||||
_thd.selstart2.x += second_dir.x;
|
||||
_thd.selstart2.y += second_dir.y;
|
||||
_thd.selend2.x -= second_dir.x;
|
||||
_thd.selend2.y -= second_dir.y;
|
||||
Trackdir seldir2 = PointDirToTrackdir(_thd.selstart2, line.second_dir);
|
||||
_thd.selstart2.x &= ~TILE_UNIT_MASK;
|
||||
_thd.selstart2.y &= ~TILE_UNIT_MASK;
|
||||
_thd.dir2 = (HighLightStyle)TrackdirToTrack(seldir2);
|
||||
} else {
|
||||
_thd.dir2 = HT_DIR_END;
|
||||
}
|
||||
|
||||
HighLightStyle ret = HT_LINE | (HighLightStyle)TrackdirToTrack(seldir);
|
||||
ShowLengthMeasurement(ret, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y), TCC_HOVER, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4058,6 +4413,12 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_thd.place_mode & HT_POLY) && GetRailSnapMode() != RSM_NO_SNAP) {
|
||||
Point pt = { x, y };
|
||||
_thd.next_drawstyle = CalcPolyrailDrawstyle(pt, true);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Special handling of drag in any (8-way) direction */
|
||||
if (method & (VPM_RAILDIRS | VPM_SIGNALDIRS)) {
|
||||
_thd.selend.x = x;
|
||||
@@ -4110,27 +4471,12 @@ calc_heightdiff_single_direction:;
|
||||
x = sx + Clamp(x - sx, -limit, limit);
|
||||
y = sy + Clamp(y - sy, -limit, limit);
|
||||
}
|
||||
if (_settings_client.gui.measure_tooltip) {
|
||||
TileIndex t0 = TileVirtXY(sx, sy);
|
||||
TileIndex t1 = TileVirtXY(x, y);
|
||||
uint distance = DistanceManhattan(t0, t1) + 1;
|
||||
byte index = 0;
|
||||
uint64 params[2];
|
||||
|
||||
if (distance != 1) {
|
||||
/* With current code passing a HT_LINE style to calculate the height
|
||||
* difference is enough. However if/when a point-tool is created
|
||||
* with this method, function should be called with new_style (below)
|
||||
* instead of HT_LINE | style case HT_POINT is handled specially
|
||||
* new_style := (_thd.next_drawstyle & HT_RECT) ? HT_LINE | style : _thd.next_drawstyle; */
|
||||
int heightdiff = CalcHeightdiff(HT_LINE | style, 0, t0, t1);
|
||||
|
||||
params[index++] = distance;
|
||||
if (heightdiff != 0) params[index++] = heightdiff;
|
||||
}
|
||||
|
||||
ShowMeasurementTooltips(measure_strings_length[index], index, params);
|
||||
}
|
||||
ShowLengthMeasurement(HT_LINE | style, TileVirtXY(sx, sy), TileVirtXY(x, y));
|
||||
break;
|
||||
|
||||
case VPM_A_B_LINE: { // drag an A to B line
|
||||
@@ -4248,6 +4594,7 @@ calc_heightdiff_single_direction:;
|
||||
|
||||
_thd.selend.x = x;
|
||||
_thd.selend.y = y;
|
||||
_thd.dir2 = HT_DIR_END;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4265,11 +4612,10 @@ EventState VpHandlePlaceSizingDrag()
|
||||
return ES_HANDLED;
|
||||
}
|
||||
|
||||
/* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
|
||||
if (_left_button_down) {
|
||||
/* While dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ).
|
||||
* Do it even if the button is no longer pressed to make sure that OnPlaceDrag was called at least once. */
|
||||
w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
|
||||
return ES_HANDLED;
|
||||
}
|
||||
if (_left_button_down) return ES_HANDLED;
|
||||
|
||||
/* mouse button released..
|
||||
* keep the selected tool, but reset it to the original mode. */
|
||||
@@ -4280,14 +4626,18 @@ EventState VpHandlePlaceSizingDrag()
|
||||
} else if (_thd.select_method & VPM_SIGNALDIRS) {
|
||||
_thd.place_mode = HT_RECT | others;
|
||||
} else if (_thd.select_method & VPM_RAILDIRS) {
|
||||
_thd.place_mode = (_thd.select_method & ~VPM_RAILDIRS) ? _thd.next_drawstyle : (HT_RAIL | others);
|
||||
_thd.place_mode = (_thd.select_method & ~VPM_RAILDIRS ? _thd.next_drawstyle : HT_RAIL) | others;
|
||||
} else {
|
||||
_thd.place_mode = HT_POINT | others;
|
||||
}
|
||||
SetTileSelectSize(1, 1);
|
||||
|
||||
w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
|
||||
if (_thd.place_mode & HT_POLY) {
|
||||
if (GetRailSnapMode() == RSM_SNAP_TO_TILE) SetRailSnapMode(RSM_NO_SNAP);
|
||||
if (_thd.drawstyle == HT_NONE) return ES_HANDLED;
|
||||
}
|
||||
|
||||
w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
|
||||
return ES_HANDLED;
|
||||
}
|
||||
|
||||
@@ -4349,6 +4699,10 @@ void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowC
|
||||
VpStartPreSizing();
|
||||
}
|
||||
|
||||
if (mode & HT_POLY) {
|
||||
SetRailSnapMode((mode & HT_NEW_POLY) == HT_NEW_POLY ? RSM_NO_SNAP : RSM_SNAP_TO_RAIL);
|
||||
}
|
||||
|
||||
if ((icon & ANIMCURSOR_FLAG) != 0) {
|
||||
SetAnimatedMouseCursor(_animcursors[icon & ~ANIMCURSOR_FLAG]);
|
||||
} else {
|
||||
@@ -4410,3 +4764,116 @@ void InitializeSpriteSorter()
|
||||
}
|
||||
assert(_vp_sprite_sorter != NULL);
|
||||
}
|
||||
|
||||
static LineSnapPoint LineSnapPointAtRailTrackEndpoint(TileIndex tile, DiagDirection exit_dir, bool bidirectional)
|
||||
{
|
||||
LineSnapPoint ret;
|
||||
ret.x = (TILE_SIZE / 2) * (uint)(2 * TileX(tile) + TileIndexDiffCByDiagDir(exit_dir).x + 1);
|
||||
ret.y = (TILE_SIZE / 2) * (uint)(2 * TileY(tile) + TileIndexDiffCByDiagDir(exit_dir).y + 1);
|
||||
|
||||
ret.dirs = 0;
|
||||
SetBit(ret.dirs, DiagDirToDir(exit_dir));
|
||||
SetBit(ret.dirs, ChangeDir(DiagDirToDir(exit_dir), DIRDIFF_45LEFT));
|
||||
SetBit(ret.dirs, ChangeDir(DiagDirToDir(exit_dir), DIRDIFF_45RIGHT));
|
||||
if (bidirectional) ret.dirs |= ROR<uint8>(ret.dirs, DIRDIFF_REVERSE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the position of lastly built rail track; for highlighting purposes.
|
||||
*
|
||||
* In "polyline" highlighting mode, the stored end point will be used as a snapping point for new
|
||||
* tracks allowing to place multi-segment polylines.
|
||||
*
|
||||
* @param start_tile tile where the track starts
|
||||
* @param end_tile tile where the track ends
|
||||
* @param start_track track piece on the start_tile
|
||||
* @param bidirectional_exit whether to allow to highlight next track in any direction; otherwise new track will have to fallow the stored one (usefull when placing tunnels and bridges)
|
||||
*/
|
||||
void StoreRailPlacementEndpoints(TileIndex start_tile, TileIndex end_tile, Track start_track, bool bidirectional_exit)
|
||||
{
|
||||
if (start_tile != INVALID_TILE && end_tile != INVALID_TILE) {
|
||||
/* calculate trackdirs at booth ends of the track */
|
||||
Trackdir exit_trackdir_at_start = TrackToTrackdir(start_track);
|
||||
Trackdir exit_trackdir_at_end = ReverseTrackdir(TrackToTrackdir(start_track));
|
||||
if (start_tile != end_tile) { // multi-tile case
|
||||
/* determine proper direction (pointing outside of the track) */
|
||||
uint distance = DistanceManhattan(start_tile, end_tile);
|
||||
if (distance > DistanceManhattan(TileAddByDiagDir(start_tile, TrackdirToExitdir(exit_trackdir_at_start)), end_tile)) {
|
||||
Swap(exit_trackdir_at_start, exit_trackdir_at_end);
|
||||
}
|
||||
/* determine proper track on the end tile - switch between upper/lower or left/right based on the length */
|
||||
if (distance % 2 != 0) exit_trackdir_at_end = NextTrackdir(exit_trackdir_at_end);
|
||||
}
|
||||
|
||||
LineSnapPoint snap_start = LineSnapPointAtRailTrackEndpoint(start_tile, TrackdirToExitdir(exit_trackdir_at_start), bidirectional_exit);
|
||||
LineSnapPoint snap_end = LineSnapPointAtRailTrackEndpoint(end_tile, TrackdirToExitdir(exit_trackdir_at_end), bidirectional_exit);
|
||||
/* Find if we already had these coordinates before. */
|
||||
LineSnapPoint *snap;
|
||||
bool had_start = false;
|
||||
bool had_end = false;
|
||||
for (snap = _rail_snap_points.Begin(); snap != _rail_snap_points.End(); snap++) {
|
||||
had_start |= (snap->x == snap_start.x && snap->y == snap_start.y);
|
||||
had_end |= (snap->x == snap_end.x && snap->y == snap_end.y);
|
||||
}
|
||||
/* Create new snap point set. */
|
||||
if (had_start && had_end) {
|
||||
/* just stop snaping, don't forget snap points */
|
||||
SetRailSnapMode(RSM_NO_SNAP);
|
||||
} else {
|
||||
/* include only new points */
|
||||
_rail_snap_points.Clear();
|
||||
if (!had_start) *_rail_snap_points.Append() = snap_start;
|
||||
if (!had_end) *_rail_snap_points.Append() = snap_end;
|
||||
SetRailSnapMode(RSM_SNAP_TO_RAIL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CurrentlySnappingRailPlacement()
|
||||
{
|
||||
return (_thd.place_mode & HT_POLY) && GetRailSnapMode() == RSM_SNAP_TO_RAIL;
|
||||
}
|
||||
|
||||
static RailSnapMode GetRailSnapMode()
|
||||
{
|
||||
if (_rail_snap_mode == RSM_SNAP_TO_TILE && _tile_snap_points.Length() == 0) return RSM_NO_SNAP;
|
||||
if (_rail_snap_mode == RSM_SNAP_TO_RAIL && _rail_snap_points.Length() == 0) return RSM_NO_SNAP;
|
||||
return _rail_snap_mode;
|
||||
}
|
||||
|
||||
static void SetRailSnapMode(RailSnapMode mode)
|
||||
{
|
||||
_rail_snap_mode = mode;
|
||||
|
||||
if ((_thd.place_mode & HT_POLY) && (GetRailSnapMode() == RSM_NO_SNAP)) {
|
||||
SetTileSelectSize(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static TileIndex GetRailSnapTile()
|
||||
{
|
||||
if (_tile_snap_points.Length() == 0) return INVALID_TILE;
|
||||
return TileVirtXY(_tile_snap_points[DIAGDIR_NE].x, _tile_snap_points[DIAGDIR_NE].y);
|
||||
}
|
||||
|
||||
static void SetRailSnapTile(TileIndex tile)
|
||||
{
|
||||
_tile_snap_points.Clear();
|
||||
if (tile == INVALID_TILE) return;
|
||||
|
||||
for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
|
||||
LineSnapPoint *point = _tile_snap_points.Append();
|
||||
*point = LineSnapPointAtRailTrackEndpoint(tile, dir, false);
|
||||
point->dirs = ROR<uint8>(point->dirs, DIRDIFF_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
void ResetRailPlacementSnapping()
|
||||
{
|
||||
_rail_snap_mode = RSM_NO_SNAP;
|
||||
_tile_snap_points.Clear();
|
||||
_rail_snap_points.Clear();
|
||||
_current_snap_lock.x = -1;
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ void StartSpriteCombine();
|
||||
void EndSpriteCombine();
|
||||
|
||||
bool HandleViewportDoubleClicked(Window *w, int x, int y);
|
||||
bool HandleViewportClicked(const ViewPort *vp, int x, int y);
|
||||
bool HandleViewportClicked(const ViewPort *vp, int x, int y, bool double_click);
|
||||
void SetRedErrorSquare(TileIndex tile);
|
||||
void SetTileSelectSize(int w, int h);
|
||||
void SetTileSelectBigSize(int ox, int oy, int sx, int sy);
|
||||
|
@@ -21,6 +21,7 @@ enum RailToolbarWidgets {
|
||||
WID_RAT_BUILD_EW, ///< Build rail along the game view X axis.
|
||||
WID_RAT_BUILD_Y, ///< Build rail along the game grid Y axis.
|
||||
WID_RAT_AUTORAIL, ///< Autorail tool.
|
||||
WID_RAT_POLYRAIL, ///< Polyline rail tool.
|
||||
WID_RAT_DEMOLISH, ///< Destroy something with dynamite!
|
||||
WID_RAT_BUILD_DEPOT, ///< Build a depot.
|
||||
WID_RAT_BUILD_WAYPOINT, ///< Build a waypoint.
|
||||
|
@@ -2846,7 +2846,7 @@ static void MouseLoop(MouseClick click, int mousewheel)
|
||||
/* FALL THROUGH */
|
||||
case MC_LEFT:
|
||||
DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
|
||||
if (!HandleViewportClicked(vp, x, y) &&
|
||||
if (!HandleViewportClicked(vp, x, y, click == MC_DOUBLE_LEFT) &&
|
||||
!(w->flags & WF_DISABLE_VP_SCROLL) &&
|
||||
_settings_client.gui.left_mouse_btn_scrolling) {
|
||||
_scrolling_viewport = w;
|
||||
|
Reference in New Issue
Block a user