diff --git a/src/misc.cpp b/src/misc.cpp index 871bf581c4..e2244c3623 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -110,7 +110,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin InitializeEconomy(); ResetObjectToPlace(); - ClearRailPlacementEndpoints(); + ResetRailPlacementSnapping(); GamelogReset(); GamelogStartAction(GLAT_START); diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 64781eddc6..54b1f24858 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -417,6 +417,8 @@ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, u Track track = Extract(p2); CommandCost cost(EXPENSES_CONSTRUCTION); + _rail_track_endtile = INVALID_TILE; + if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR; Slope tileh = GetTileSlope(tile); @@ -433,7 +435,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; @@ -522,6 +527,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 */ @@ -580,6 +586,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); @@ -840,6 +848,8 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint3 bool remove = HasBit(p2, 7); RailType railtype = Extract(p2); + _rail_track_endtile = INVALID_TILE; + if ((!remove && !ValParamRailtype(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR; if (p1 >= MapSize()) return CMD_ERROR; TileIndex end_tile = p1; @@ -851,10 +861,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 (HasBit(p2, 8)) return last_error; break; diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 53ff51e1c5..9da936eedf 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -57,6 +57,9 @@ 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 @@ -93,13 +96,20 @@ void CcPlaySound1E(const CommandCost &result, TileIndex tile, uint32 p1, uint32 if (result.Succeeded() && _settings_client.sound.confirm) SndPlayTileFx(SND_20_SPLAT_RAIL, tile); } -static bool GenericPlaceRail(TileIndex tile, Track track) +static CommandContainer GenericPlaceRailCmd(TileIndex tile, Track track) { - return DoCommandP(tile, _cur_railtype, track, - _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); + 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), // cmd + CcPlaySound1E, // callback + "" // text + }; + + return ret; } /** @@ -352,13 +362,20 @@ static void BuildRailClick_Remove(Window *w) } } -static bool DoRailroadTrack(TileIndex start_tile, TileIndex end_tile, Track track) +static CommandContainer DoRailroadTrackCmd(TileIndex start_tile, TileIndex end_tile, Track track) { - return DoCommandP(start_tile, end_tile, _cur_railtype | (track << 4), - _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); + CommandContainer ret = { + start_tile, // tile + end_tile, // p1 + _cur_railtype | (track << 4), // 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), // cmd + CcPlaySound1E, // callback + "" // text + }; + + return ret; } static void HandleAutodirPlacement() @@ -367,8 +384,21 @@ static void HandleAutodirPlacement() 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 ? GenericPlaceRail(end_tile, track) : DoRailroadTrack(start_tile, end_tile, track)) - && !_shift_pressed) { + 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; + } + + /* save new snap points for the polyline tool */ + if (!_shift_pressed && _rail_track_endtile != INVALID_TILE) { StoreRailPlacementEndpoints(start_tile, _rail_track_endtile, track, true); } } @@ -632,10 +662,39 @@ struct BuildRailToolbarWindow : Window { this->last_user_action = widget; break; - case WID_RAT_POLYRAIL: - HandlePlacePushButton(this, WID_RAT_POLYRAIL, GetRailTypeInfo(railtype)->cursor.autorail, HT_RAIL | HT_POLY); - this->last_user_action = widget; + 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); @@ -701,7 +760,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) @@ -877,7 +944,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_GLOBAL_HOTKEY | WKC_CTRL, 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), @@ -885,7 +953,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", WID_RAT_POLYRAIL), + 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), @@ -1819,7 +1888,6 @@ struct BuildRailDepotWindow : public PickerWindowBase { case WID_BRAD_DEPOT_SE: case WID_BRAD_DEPOT_SW: case WID_BRAD_DEPOT_NW: - case WID_BRAD_DEPOT_AUTO: this->RaiseWidget(_build_depot_direction + WID_BRAD_DEPOT_NE); _build_depot_direction = (DiagDirection)(widget - WID_BRAD_DEPOT_NE); this->LowerWidget(_build_depot_direction + WID_BRAD_DEPOT_NE); @@ -1857,9 +1925,6 @@ static const NWidgetPart _nested_build_depot_widgets[] = { EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(3, 0), SetFill(1, 0), EndContainer(), - NWidget(NWID_HORIZONTAL), SetPIP(2, 2, 2), - NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BRAD_DEPOT_AUTO), SetMinimalSize(134, 12), SetDataTip(STR_STATION_BUILD_ORIENTATION_AUTO, STR_BUILD_DEPOT_TRAIN_ORIENTATION_AUTO_TOOLTIP), - EndContainer(), NWidget(NWID_SPACER), SetMinimalSize(0, 3), EndContainer(), }; @@ -1980,7 +2045,7 @@ static void ShowBuildWaypointPicker(Window *parent) */ void InitializeRailGui() { - _build_depot_direction = (DiagDirection)(DIAGDIR_NW + 1); + _build_depot_direction = DIAGDIR_NW; } /** diff --git a/src/rev.cpp b/src/rev.cpp index 7bf02574a3..eb89f36ef5 100644 --- a/src/rev.cpp +++ b/src/rev.cpp @@ -1,4 +1,4 @@ -/* $Id: rev.cpp.in 26482 2014-04-23 20:13:33Z rubidium $ */ +/* $Id: rev.cpp.in 26440 2014-04-01 18:33:16Z frosch $ */ /* * This file is part of OpenTTD. @@ -39,7 +39,7 @@ bool IsReleasedVersion() * norev000 is for non-releases that are made on systems without * subversion or sources that are not a checkout of subversion. */ -const char _openttd_revision[] = "1.5.0-beta2"; +const char _openttd_revision[] = "1.4.0"; /** * The text version of OpenTTD's build date. @@ -72,11 +72,11 @@ const byte _openttd_revision_modified = 0; * final release will always have a lower version number than the released * version, thus making comparisons on specific revisions easy. */ -const uint32 _openttd_newgrf_version = 1 << 28 | 5 << 24 | 0 << 20 | 0 << 19 | (27170 & ((1 << 19) - 1)); +const uint32 _openttd_newgrf_version = 1 << 28 | 4 << 24 | 0 << 20 | 1 << 19 | (26440 & ((1 << 19) - 1)); #ifdef __MORPHOS__ /** * Variable used by MorphOS to show the version. */ -extern const char morphos_versions_tag[] = "$VER: OpenTTD 1.5.0-beta2 (16.03.15) OpenTTD Team [MorphOS, PowerPC]"; +extern const char morphos_versions_tag[] = "$VER: OpenTTD 1.4.0 (02.04.14) OpenTTD Team [MorphOS, PowerPC]"; #endif diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq index 9c5b524f9b..e9c30c05cd 100644 --- a/src/script/api/game/game_window.hpp.sq +++ b/src/script/api/game/game_window.hpp.sq @@ -904,6 +904,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"); diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp index d838eeaa0b..7b3971db5a 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -1982,6 +1982,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. diff --git a/src/tilehighlight_func.h b/src/tilehighlight_func.h index 4789426c76..144c990f6a 100644 --- a/src/tilehighlight_func.h +++ b/src/tilehighlight_func.h @@ -32,7 +32,8 @@ void VpSetPlaceSizingLimit(int limit); void UpdateTileSelection(); void StoreRailPlacementEndpoints(TileIndex start_tile, TileIndex end_tile, Track start_track, bool bidirectional = true); -void ClearRailPlacementEndpoints(); +void ResetRailPlacementSnapping(); +bool CurrentlySnappingRailPlacement(); extern TileHighlightData _thd; diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index 49f38479fa..c751a79c55 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -29,6 +29,7 @@ enum HighLightStyle { 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): @@ -63,7 +64,7 @@ struct TileHighlightData { 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. + 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. diff --git a/src/viewport.cpp b/src/viewport.cpp index 1c172bfff3..4e196f2df2 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -152,6 +152,12 @@ typedef SmallVector StringSpriteToDrawVector; typedef SmallVector ParentSpriteToDrawVector; typedef SmallVector ChildScreenSpriteToDrawVector; +enum RailSnapMode { + RSM_NO_SNAP, + RSM_SNAP_TO_TILE, + RSM_SNAP_TO_RAIL, +}; + /** * Snapping point for a track. * @@ -199,14 +205,22 @@ static void MarkViewportDirty(const ViewPort *vp, int left, int top, int right, static ViewportDrawer _vd; TileHighlightData _thd; -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 TileInfo *_cur_ti; bool _draw_bounding_boxes = false; bool _draw_dirty_blocks = false; uint _dirty_block_colour = 0; static VpSpriteSorter _vp_sprite_sorter = 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); @@ -1052,27 +1066,27 @@ static void DrawTileSelection(const TileInfo *ti) /* Draw a blue rect. */ DrawTileSelectionRect(ti, PALETTE_SEL_TILE_BLUE); } - } + } break; 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; - if (ti->tileh & SLOPE_N) { - z += TILE_HEIGHT; - if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT; - } - if (IsHalftileSlope(ti->tileh)) { - Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh); - if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT; - if (halftile_corner != CORNER_S) { - foundation_part = FOUNDATION_PART_HALFTILE; - if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT; + /* Figure out the Z coordinate for the single dot. */ + int z = 0; + FoundationPart foundation_part = FOUNDATION_PART_NORMAL; + if (ti->tileh & SLOPE_N) { + z += TILE_HEIGHT; + if (RemoveHalftileSlope(ti->tileh) == SLOPE_STEEP_N) z += TILE_HEIGHT; } - } - DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part); + if (IsHalftileSlope(ti->tileh)) { + Corner halftile_corner = GetHalftileSlopeCorner(ti->tileh); + if ((halftile_corner == CORNER_W) || (halftile_corner == CORNER_E)) z += TILE_HEIGHT; + if (halftile_corner != CORNER_S) { + foundation_part = FOUNDATION_PART_HALFTILE; + if (IsSteepSlope(ti->tileh)) z -= TILE_HEIGHT; + } + } + DrawSelectionSprite(_cur_dpi->zoom <= ZOOM_LVL_DETAIL ? SPR_DOT : SPR_DOT_SMALL, PAL_NONE, ti, z, foundation_part); } break; @@ -1087,12 +1101,13 @@ static void DrawTileSelection(const TileInfo *ti) 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.drawstyle & HT_POLY) && _thd.dir2 < HT_DIR_END) { + } else if (_thd.dir2 < HT_DIR_END) { + /* FIXME mb missing condition (_thd.drawstyle & HT_POLY) */ 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; - } + } } } @@ -1277,9 +1292,8 @@ static void ViewportAddTownNames(DrawPixelInfo *dpi) const Town *t; FOR_ALL_TOWNS(t) { ViewportAddString(dpi, ZOOM_LVL_OUT_16X, &t->cache.sign, - //_settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN, - //STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK, - t->Label(), t->SmallLabel(), STR_VIEWPORT_TOWN_TINY_BLACK, + _settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN, + STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK, t->index, t->cache.population); } } @@ -2098,8 +2112,7 @@ static bool CheckClickOnTown(const ViewPort *vp, int x, int y) const Town *t; FOR_ALL_TOWNS(t) { if (CheckClickOnViewportSign(vp, x, y, &t->cache.sign)) { - if(_ctrl_pressed) TownExecuteAction(t, 4); //build statue - else ShowTownViewWindow(t->index); + ShowTownViewWindow(t->index); return true; } } @@ -2194,15 +2207,21 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y, bool double_click) if (v != NULL && VehicleClicked(v)) return true; } - /* Double-clicking finishes current polyline and starts new one. */ - if (double_click && _settings_client.gui.polyrail_double_click && (_thd.place_mode & HT_POLY)) { - ClearRailPlacementEndpoints(); - SetTileSelectSize(1, 1); - return true; - } - /* 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; + /* FIXME _settings_client.gui.polyrail_double_click */ + 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; } @@ -2218,7 +2237,7 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y, bool double_click) v = v->First(); if (_ctrl_pressed && v->owner == _local_company) { if (_settings_client.gui.enable_ctrl_click_start_stop) - StartStopVehicle(v, true); + StartStopVehicle(v, true); } else { ShowVehicleViewWindow(v); } @@ -2440,9 +2459,6 @@ void UpdateTileSelection() _thd.new_size.x += TILE_SIZE; _thd.new_size.y += TILE_SIZE; } - if (_thd.place_mode & HT_POLY) { - CalcNewPolylineOutersize(); - } new_drawstyle = _thd.next_drawstyle; } } else if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) { @@ -2463,7 +2479,16 @@ void UpdateTileSelection() case HT_LINE: /* HT_POLY */ if (_thd.place_mode & HT_POLY) { - if (_rail_snap_points.Length() > 0) { + 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; @@ -2476,21 +2501,15 @@ void UpdateTileSelection() _thd.new_pos.y = y1; _thd.new_size.x = x2 - x1 + TILE_SIZE; _thd.new_size.y = y2 - y1 + TILE_SIZE; - CalcNewPolylineOutersize(); } - break; } - _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; + break; } /* HT_RAIL */ if (_thd.place_mode & HT_RAIL) { - /* Draw one highlighted tile in any direction */ - new_drawstyle = GetAutorailHT(pt.x, pt.y); - break; + /* Draw one highlighted tile in any direction */ + new_drawstyle = GetAutorailHT(pt.x, pt.y); + break; } /* HT_LINE */ switch (_thd.place_mode & HT_DIR_MASK) { @@ -2523,6 +2542,8 @@ 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 || @@ -2588,6 +2609,10 @@ void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDrag _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; @@ -2968,26 +2993,34 @@ static inline uint SqrDist(const Point &a, const Point &b) static LineSnapPoint *FindBestPolyline(const Point &pt, LineSnapPoint *snap_points, uint num_points, Polyline *ret) { - while (num_points > 0) { - /* run a single bubble sort loop to find the closest snap point (push it to the and of the array) */ - uint prev_dist = SqrDist(snap_points[0], pt); - for (uint i = 1; i < num_points; i++) { - uint next_dist = SqrDist(snap_points[i], pt); - if (prev_dist < next_dist) { - Swap(snap_points[i], snap_points[i - 1]); - } else { - prev_dist = next_dist; - } + /* 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; } - - /* try to fit a line */ - if (FindPolyline(pt, snap_points[num_points - 1], ret)) return &snap_points[num_points - 1]; - - /* repeat procedure for the rest of snap points */ - --num_points; + /* save the found polyline */ + *ret = polyline; + best_snap_point = &snap_points[i]; } - return NULL; + return best_snap_point; } /** while dragging */ @@ -3178,6 +3211,7 @@ static void CalcRaildirsDrawstyle(int x, int y, int method) _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)); @@ -3185,33 +3219,37 @@ static void CalcRaildirsDrawstyle(int x, int y, int method) 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; - HighLightStyle ret = HT_LINE | HT_POLY; + bool lock_snapping = dragging && snap_mode == RSM_SNAP_TO_RAIL; + if (!lock_snapping) _current_snap_lock.x = -1; - if (!dragging) { - _current_snap_lock.x = -1; - if (FindBestPolyline(pt, _rail_snap_points.Begin(), _rail_snap_points.Length(), &line) == NULL) ret = HT_NONE; // no match - } else if (_current_snap_lock.x != -1) { - if (FindBestPolyline(pt, &_current_snap_lock, 1, &line) == NULL) ret = HT_NONE; // no match + 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 { - const LineSnapPoint *snap_point = FindBestPolyline(pt, _rail_snap_points.Begin(), _rail_snap_points.Length(), &line); - if (snap_point == NULL) { - ret = HT_NONE; // no match - } else { - _current_snap_lock = *snap_point; - _current_snap_lock.dirs &= (1 << line.first_dir) | (1 << ReverseDir(line.first_dir)); // lock direction - } - } + assert(snap_mode == RSM_SNAP_TO_RAIL); + snap_point = FindBestPolyline(pt, _rail_snap_points.Begin(), _rail_snap_points.Length(), &line); + } - if (ret == HT_NONE) { - _thd.selstart.x = -1; - _thd.selend.x = -1; - _thd.selstart2.x = -1; - _thd.selend2.x = -1; - _thd.dir2 = HT_DIR_END; - return ret; + 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); @@ -3228,7 +3266,6 @@ static HighLightStyle CalcPolyrailDrawstyle(Point pt, bool dragging) Trackdir seldir = PointDirToTrackdir(_thd.selstart, line.first_dir); _thd.selstart.x &= ~TILE_UNIT_MASK; _thd.selstart.y &= ~TILE_UNIT_MASK; - ret |= (HighLightStyle)TrackdirToTrack(seldir); if (line.second_len != 0) { TileIndexDiffC second_dir = TileIndexDiffCByDir(line.second_dir); @@ -3246,6 +3283,7 @@ static HighLightStyle CalcPolyrailDrawstyle(Point pt, bool dragging) _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; } @@ -3267,7 +3305,7 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method) return; } - if ((_thd.place_mode & HT_POLY) && _rail_snap_points.Length() > 0) { + if ((_thd.place_mode & HT_POLY) && GetRailSnapMode() != RSM_NO_SNAP) { Point pt = { x, y }; _thd.next_drawstyle = CalcPolyrailDrawstyle(pt, true); return; @@ -3325,11 +3363,11 @@ calc_heightdiff_single_direction:; x = sx + Clamp(x - sx, -limit, limit); y = sy + Clamp(y - sy, -limit, limit); } - /* 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; */ + /* 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; */ ShowLengthMeasurement(HT_LINE | style, TileVirtXY(sx, sy), TileVirtXY(x, y)); break; @@ -3409,6 +3447,7 @@ calc_heightdiff_single_direction:; _thd.selend.x = x; _thd.selend.y = y; + _thd.dir2 = HT_DIR_END; } /** @@ -3426,11 +3465,10 @@ EventState VpHandlePlaceSizingDrag() return ES_HANDLED; } - /* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */ - if (_left_button_down) { - w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor()); - return ES_HANDLED; - } + /* 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()); + if (_left_button_down) return ES_HANDLED; /* mouse button released.. * keep the selected tool, but reset it to the original mode. */ @@ -3447,8 +3485,12 @@ EventState VpHandlePlaceSizingDrag() } 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; } @@ -3495,6 +3537,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 { @@ -3508,6 +3554,44 @@ void ResetObjectToPlace() SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); } +Point GetViewportStationMiddle(const ViewPort *vp, const Station *st) +{ + int x = TileX(st->xy) * TILE_SIZE; + int y = TileY(st->xy) * TILE_SIZE; + int z = GetSlopePixelZ(Clamp(x, 0, MapSizeX() * TILE_SIZE - 1), Clamp(y, 0, MapSizeY() * TILE_SIZE - 1)); + + Point p = RemapCoords(x, y, z); + p.x = UnScaleByZoom(p.x - vp->virtual_left, vp->zoom) + vp->left; + p.y = UnScaleByZoom(p.y - vp->virtual_top, vp->zoom) + vp->top; + return p; +} + +/** Helper class for getting the best sprite sorter. */ +struct ViewportSSCSS { + VpSorterChecker fct_checker; ///< The check function. + VpSpriteSorter fct_sorter; ///< The sorting function. +}; + +/** List of sorters ordered from best to worst. */ +static ViewportSSCSS _vp_sprite_sorters[] = { +#ifdef WITH_SSE + { &ViewportSortParentSpritesSSE41Checker, &ViewportSortParentSpritesSSE41 }, +#endif + { &ViewportSortParentSpritesChecker, &ViewportSortParentSprites } +}; + +/** Choose the "best" sprite sorter and set _vp_sprite_sorter. */ +void InitializeSpriteSorter() +{ + for (uint i = 0; i < lengthof(_vp_sprite_sorters); i++) { + if (_vp_sprite_sorters[i].fct_checker()) { + _vp_sprite_sorter = _vp_sprite_sorters[i].fct_sorter; + break; + } + } + assert(_vp_sprite_sorter != NULL); +} + static LineSnapPoint LineSnapPointAtRailTrackEndpoint(TileIndex tile, DiagDirection exit_dir, bool bidirectional) { LineSnapPoint ret; @@ -3554,57 +3638,69 @@ void StoreRailPlacementEndpoints(TileIndex start_tile, TileIndex end_tile, Track 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++) { - /* Coordinates found - remove the snap point as it was already used. */ - if (snap->x == snap_start.x && snap->y == snap_start.y) snap_start.dirs = 0; - if (snap->x == snap_end.x && snap->y == snap_end.y) snap_end.dirs = 0; + 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. */ - _rail_snap_points.Clear(); - if (snap_start.dirs != 0) *_rail_snap_points.Append() = snap_start; - if (snap_end.dirs != 0) *_rail_snap_points.Append() = snap_end; - } -} - -void ClearRailPlacementEndpoints() -{ - _rail_snap_points.Clear(); -} - -Point GetViewportStationMiddle(const ViewPort *vp, const Station *st) -{ - int x = TileX(st->xy) * TILE_SIZE; - int y = TileY(st->xy) * TILE_SIZE; - int z = GetSlopePixelZ(Clamp(x, 0, MapSizeX() * TILE_SIZE - 1), Clamp(y, 0, MapSizeY() * TILE_SIZE - 1)); - - Point p = RemapCoords(x, y, z); - p.x = UnScaleByZoom(p.x - vp->virtual_left, vp->zoom) + vp->left; - p.y = UnScaleByZoom(p.y - vp->virtual_top, vp->zoom) + vp->top; - return p; -} - -/** Helper class for getting the best sprite sorter. */ -struct ViewportSSCSS { - VpSorterChecker fct_checker; ///< The check function. - VpSpriteSorter fct_sorter; ///< The sorting function. -}; - -/** List of sorters ordered from best to worst. */ -static ViewportSSCSS _vp_sprite_sorters[] = { -#ifdef WITH_SSE - { &ViewportSortParentSpritesSSE41Checker, &ViewportSortParentSpritesSSE41 }, -#endif - { &ViewportSortParentSpritesChecker, &ViewportSortParentSprites } -}; - -/** Choose the "best" sprite sorter and set _vp_sprite_sorter. */ -void InitializeSpriteSorter() -{ - for (uint i = 0; i < lengthof(_vp_sprite_sorters); i++) { - if (_vp_sprite_sorters[i].fct_checker()) { - _vp_sprite_sorter = _vp_sprite_sorters[i].fct_sorter; - break; + 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); } } - assert(_vp_sprite_sorter != NULL); +} + +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(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; }