Index: src/command.cpp =================================================================== --- src/command.cpp (revision 13025) +++ src/command.cpp (working copy) @@ -199,6 +199,8 @@ DEF_COMMAND(CmdChangeTimetable); DEF_COMMAND(CmdSetVehicleOnTime); DEF_COMMAND(CmdAutofillTimetable); + +DEF_COMMAND(CmdTerraformTileToHeight); #undef DEF_COMMAND /** @@ -348,6 +350,7 @@ {CmdChangeTimetable, 0}, /* CMD_CHANGE_TIMETABLE */ {CmdSetVehicleOnTime, 0}, /* CMD_SET_VEHICLE_ON_TIME */ {CmdAutofillTimetable, 0}, /* CMD_AUTOFILL_TIMETABLE */ + {CmdTerraformTileToHeight, CMD_AUTO}, /* CMD_TERRAFORM_TILE_TO_HEIGHT */ }; /*! @@ -546,6 +549,7 @@ notest = (cmd & 0xFF) == CMD_CLEAR_AREA || (cmd & 0xFF) == CMD_LEVEL_LAND || + (cmd & 0xFF) == CMD_TERRAFORM_TILE_TO_HEIGHT || (cmd & 0xFF) == CMD_REMOVE_LONG_ROAD || (cmd & 0xFF) == CMD_CLONE_VEHICLE; Index: src/command_type.h =================================================================== --- src/command_type.h (revision 13025) +++ src/command_type.h (working copy) @@ -280,6 +280,8 @@ CMD_CHANGE_TIMETABLE, ///< change the timetable for a vehicle CMD_SET_VEHICLE_ON_TIME, ///< set the vehicle on time feature (timetable) CMD_AUTOFILL_TIMETABLE, ///< autofill the timetable + + CMD_TERRAFORM_TILE_TO_HEIGHT, ///< set a single tile to target height }; /** Index: src/terraform_cmd.cpp =================================================================== --- src/terraform_cmd.cpp (revision 13025) +++ src/terraform_cmd.cpp (working copy) @@ -394,3 +394,59 @@ return (cost.GetCost() == 0) ? CMD_ERROR : cost; } + +/** Sets a single tile to target height (for raise/lower/level draw) + * @param tile tile to modify + * @param flags for this command type + * @param p1 target height (without modifier) + * @param p2 height modifier; eg raise (+1), lower (-1) or level (0) + * @return error or cost of terraforming + * @note don't confuse with CommandCost TerraformTileHeight! + */ +CommandCost CmdTerraformTileToHeight(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) +{ + _terraform_err_tile = 0; + + if (p1 >= MapSize()) return CMD_ERROR; + + /* Check range of destination height */ + if (p1 == MAX_TILE_HEIGHT && p2 == 1) return_cmd_error(STR_1004_TOO_HIGH); + if (p1 == 0 && p2 == (uint32(-1))) return_cmd_error(STR_1003_ALREADY_AT_SEA_LEVEL); + + uint curh = TileHeight(tile); // current height + uint h = p1 + p2; // target height + + CommandCost cost(EXPENSES_CONSTRUCTION); // the total cost for the operation + + /* if the tile already has the target height, return cost 0 */ + if ((p2 == 1 || curh <= h) && (p2 == uint32(-1) || curh >= h)) return cost; + + Money money = GetAvailableMoneyForCommand(); // the available money + _additional_cash_required = 0; // additional cash required if we run out of money + bool succeed = false; // did at least one sub-command succeed? + + /* the different comparators are needed because we don't want to lower with the raise tool and vice versa */ + while ((p2 != 1 && curh > h) || (p2 != uint32(-1) && curh < h)) { + /* first, calculate the outcome of one terraform step with a dry run */ + CommandCost ret = DoCommand(tile, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND); + if (CmdFailed(ret)) break; // the step failed + succeed = true; + + money -= ret.GetCost(); + /* if this command is executed and we have run out of money, add it to additional cash required */ + if ((flags & DC_EXEC) && money < 0) { + _additional_cash_required += ret.GetCost(); + } else { + /* if it's not executed OR we have enough money, add the sub-command's costs to total costs*/ + cost.AddCost(ret); + /* we have enough money, so now really execute the step */ + if (flags & DC_EXEC) DoCommand(tile, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); + } + /* the height has been lowered or raised */ + curh += (curh > h) ? -1 : 1; + } + + /* if we (at least partly) succeeded, return the costs, else an error */ + if (succeed) return cost; + return CMD_ERROR; +} Index: src/terraform_gui.cpp =================================================================== --- src/terraform_gui.cpp (revision 13025) +++ src/terraform_gui.cpp (working copy) @@ -102,6 +102,9 @@ if (success) SndPlayTileFx(SND_1F_SPLAT, end); } +static uint _draw_height; +static TileIndex _draw_tile; + /** * A central place to handle all X_AND_Y dragged GUI functions. * @param e WindowEvent variable holding in its higher bits (excluding the lower @@ -147,6 +150,44 @@ return true; } +/** + * A central place to handle all drag&draw GUI functions. + * @param e WindowEvent variable holding in its higher bits the type of action to be performed + * @return Returns true if the action was found and handled, and false otherwise. + **/ +bool GUIPlaceProcDraw(const WindowEvent *e) +{ + TileIndex end_tile = e->we.place.tile; + + if (end_tile == _draw_tile) return true; + + switch (e->we.place.select_proc) { + case DDSP_RAISE_AND_LEVEL_AREA: + if (TileHeight(end_tile) <= _draw_height) + DoCommandP(end_tile, _draw_height, 1, CcTerraform, CMD_TERRAFORM_TILE_TO_HEIGHT | CMD_MSG(STR_0808_CAN_T_RAISE_LAND_HERE)); + break; + case DDSP_LOWER_AND_LEVEL_AREA: + if (TileHeight(end_tile) >= _draw_height) + DoCommandP(end_tile, _draw_height, (uint32)-1, CcTerraform, CMD_TERRAFORM_TILE_TO_HEIGHT | CMD_MSG(STR_0809_CAN_T_LOWER_LAND_HERE)); + break; + default: + return false; + } + + _draw_tile = end_tile; + return true; +} + +/** + * Sets the current tile to (0, 0) and the target height in global variables. + * @param e WindowEvent + **/ +void StartDrawing(const WindowEvent *e) +{ + _draw_tile = TileXY(0, 0); + _draw_height = TileHeight(e->we.place.starttile); +} + typedef void OnButtonClick(Window *w); static const uint16 _terraform_keycodes[] = { @@ -342,10 +383,7 @@ _generating_world = true; // used to create green terraformed land if (_terraform_size == 1) { - StringID msg = - mode ? STR_0808_CAN_T_RAISE_LAND_HERE : STR_0809_CAN_T_LOWER_LAND_HERE; - - DoCommandP(tile, SLOPE_N, (uint32)mode, CcTerraform, CMD_TERRAFORM_LAND | CMD_MSG(msg)); + VpStartDrawing(tile, VPM_X_AND_Y, mode ? DDSP_RAISE_AND_LEVEL_AREA : DDSP_LOWER_AND_LEVEL_AREA); } else { SndPlayTileFx(SND_1F_SPLAT, tile); @@ -673,6 +711,15 @@ w->RaiseButtons(); w->SetDirty(); break; + + case WE_PLACE_DRAW: + if (e->we.place.pt.x == -1) break; + StartDrawing(e); + /* FALL THROUGH */ + case WE_DRAG_DRAW: + if (e->we.place.pt.x == -1) break; + GUIPlaceProcDraw(e); + break; } } Index: src/tilehighlight_func.h =================================================================== --- src/tilehighlight_func.h (revision 13025) +++ src/tilehighlight_func.h (working copy) @@ -13,6 +13,8 @@ typedef void PlaceProc(TileIndex tile); void PlaceProc_DemolishArea(TileIndex tile); bool GUIPlaceProcDragXY(const WindowEvent *e); +bool GUIPlaceProcDraw(const WindowEvent *e); +void StartDrawing(const WindowEvent *e); bool HandlePlacePushButton(Window *w, int widget, CursorID cursor, ViewportHighlightMode mode, PlaceProc *placeproc); void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, ViewportHighlightMode mode, Window *w); @@ -21,6 +23,7 @@ void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method); void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process); +void VpStartDrawing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process); void VpSetPresizeRange(TileIndex from, TileIndex to); void VpSetPlaceSizingLimit(int limit); Index: src/viewport.cpp =================================================================== --- src/viewport.cpp (revision 13025) +++ src/viewport.cpp (working copy) @@ -2260,6 +2260,49 @@ _special_mouse_mode = WSM_SIZING; } +/** start drawing */ +void VpStartDrawing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process) +{ + _thd.select_method = method; + _thd.select_proc = process; + _thd.selend.x = TileX(tile) * TILE_SIZE; + _thd.selstart.x = TileX(tile) * TILE_SIZE; + _thd.selend.y = TileY(tile) * TILE_SIZE; + _thd.selstart.y = TileY(tile) * TILE_SIZE; + + /* If place mode is Rectangle, placement starts from centre of a tile */ + if (_thd.place_mode == VHM_RECT) { + _thd.selend.x += TILE_SIZE / 2; + _thd.selend.y += TILE_SIZE / 2; + _thd.selstart.x += TILE_SIZE / 2; + _thd.selstart.y += TILE_SIZE / 2; + } + + _special_mouse_mode = WSM_DRAWING; + + Window *w; + WindowEvent e; + + e.we.place.select_method = method; + e.we.place.select_proc = process; + + + /* stop drawing mode if the window has been closed */ + w = FindWindowById(_thd.window_class, _thd.window_number); + if (w == NULL) { + ResetObjectToPlace(); + return; + } + + e.event = WE_PLACE_DRAW; + e.we.place.pt = GetTileBelowCursor(); + + e.we.place.tile = TileVirtXY(_thd.selend.x, _thd.selend.y); + e.we.place.starttile = TileVirtXY(_thd.selstart.x, _thd.selstart.y); + w->HandleWindowEvent(&e); + +} + void VpSetPlaceSizingLimit(int limit) { _thd.sizelimit = limit; @@ -2692,6 +2735,49 @@ _thd.selend.y = y; } +/** while drawing */ +bool VpHandleDrawingDrag() +{ + if (_special_mouse_mode != WSM_DRAWING) return true; + + WindowEvent e; + + e.we.place.select_method = _thd.select_method; + e.we.place.select_proc = _thd.select_proc; + + + /* stop drawing mode if the window has been closed */ + Window *w = FindWindowById(_thd.window_class, _thd.window_number); + if (w == NULL) { + ResetObjectToPlace(); + return false; + } + + /* while drawing execute the draw procedure of the corresponding window */ + if (_left_button_down) { + e.event = WE_DRAG_DRAW; + e.we.place.pt = GetTileBelowCursor(); + _thd.selend.x = e.we.place.pt.x; + _thd.selend.y = e.we.place.pt.y; + + /* If place mode is not Rectangle, correction needed */ + if (_thd.place_mode != VHM_RECT) { + _thd.selend.x += TILE_SIZE / 2; + _thd.selend.y += TILE_SIZE / 2; + } + + e.we.place.tile = TileVirtXY(_thd.selend.x, _thd.selend.y); + e.we.place.starttile = TileVirtXY(_thd.selstart.x, _thd.selstart.y); + w->HandleWindowEvent(&e); + return false; + } + + /* mouse button released.. */ + _special_mouse_mode = WSM_NONE; + + return false; +} + /** while dragging */ bool VpHandlePlaceSizingDrag() { Index: src/window.cpp =================================================================== --- src/window.cpp (revision 13025) +++ src/window.cpp (working copy) @@ -1793,6 +1793,7 @@ extern void UpdateTileSelection(); extern bool VpHandlePlaceSizingDrag(); +extern bool VpHandleDrawingDrag(); void MouseLoop(MouseClick click, int mousewheel) { @@ -1800,6 +1801,7 @@ HandlePlacePresize(); UpdateTileSelection(); if (!VpHandlePlaceSizingDrag()) return; + if (!VpHandleDrawingDrag()) return; if (!HandleDragDrop()) return; if (!HandleWindowDragging()) return; if (!HandleScrollbarScrolling()) return; Index: src/window_gui.h =================================================================== --- src/window_gui.h (revision 13025) +++ src/window_gui.h (working copy) @@ -136,6 +136,8 @@ WE_SCROLL, WE_INVALIDATE_DATA, ///< Notification that data displayed by the window is obsolete WE_CTRL_CHANGED, ///< CTRL key has changed state + WE_PLACE_DRAW, ///< Start of Drag&Draw + WE_DRAG_DRAW, ///< During Drag&Draw }; /** @@ -564,6 +566,7 @@ WSM_DRAGDROP = 1, WSM_SIZING = 2, WSM_PRESIZE = 3, + WSM_DRAWING = 4, }; Window *GetCallbackWnd();