mirror of https://github.com/OpenTTD/OpenTTD
Feature: Add the ability to clone an area with structures
parent
fa82dd6096
commit
c39d3f6053
Binary file not shown.
|
@ -1 +1 @@
|
|||
4f03553f614a06d86dc06376db3353c7
|
||||
24445cdf8f7f93cfe1fa864252bda7b7
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -4,7 +4,7 @@
|
|||
// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
-1 * 0 0C "OpenTTD GUI graphics"
|
||||
-1 * 3 05 15 \b 191 // OPENTTD_SPRITE_COUNT
|
||||
-1 * 3 05 15 \b 195 // OPENTTD_SPRITE_COUNT
|
||||
-1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal
|
||||
-1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal
|
||||
-1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal
|
||||
|
@ -196,3 +196,7 @@
|
|||
-1 sprites/openttdgui.png 8bpp 567 440 12 10 0 0 normal
|
||||
-1 sprites/openttdgui.png 8bpp 581 440 10 10 0 0 normal
|
||||
-1 sprites/openttdgui.png 8bpp 593 440 10 10 0 0 normal
|
||||
-1 sprites/clone_area.png 8bpp 3 9 20 20 0 0 normal
|
||||
-1 sprites/clone_area.png 8bpp 35 9 20 20 0 0 normal
|
||||
-1 sprites/clone_area.png 8bpp 67 9 32 32 0 0 normal
|
||||
-1 sprites/clone_area.png 8bpp 115 9 32 32 0 0 normal
|
||||
|
|
|
@ -94,6 +94,8 @@ add_files(
|
|||
clear_cmd.cpp
|
||||
clear_func.h
|
||||
clear_map.h
|
||||
clone_area_cmd.cpp
|
||||
clone_area_cmd.h
|
||||
command.cpp
|
||||
command_func.h
|
||||
command_type.h
|
||||
|
|
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file clone_area_cmd.cpp Commands related to clone area. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "command_func.h"
|
||||
#include "viewport_func.h"
|
||||
#include "company_base.h"
|
||||
#include "clone_area_cmd.h"
|
||||
#include "newgrf_station.h"
|
||||
#include "safeguards.h"
|
||||
#include "road.h"
|
||||
#include "road_internal.h"
|
||||
#include "depot_base.h"
|
||||
#include "tunnelbridge_map.h"
|
||||
#include "sound_func.h"
|
||||
#include "rail_cmd.h"
|
||||
#include "tunnelbridge_cmd.h"
|
||||
#include "station_cmd.h"
|
||||
#include "terraform_cmd.h"
|
||||
|
||||
TileIndex selected_tile;
|
||||
TileIndex selected_start_tile;
|
||||
bool selected_diagonal;
|
||||
|
||||
/**
|
||||
* Rotate by an angle using the formula for rotating a point on a plane.
|
||||
*
|
||||
* @param point Rotating point.
|
||||
* @param angle Rotate an angle.
|
||||
*/
|
||||
TileIndexDiffC Rotate(TileIndexDiffC point, DiagDirDiff angle) {
|
||||
switch (angle) {
|
||||
case DIAGDIRDIFF_90LEFT: return {int16_t(-point.y), point.x};
|
||||
case DIAGDIRDIFF_REVERSE: return {int16_t(-point.x), int16_t(-point.y)};
|
||||
case DIAGDIRDIFF_90RIGHT: return {point.y, int16_t(-point.x)};
|
||||
default: return point;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusting the position of the rotated point, since the tile has four corners.
|
||||
*
|
||||
* @param rotated Rotated point setting.
|
||||
* @param angle Angle of rotation.
|
||||
*/
|
||||
TileIndexDiffC FixAfterRotate(TileIndexDiffC rotated, DiagDirDiff angle) {
|
||||
switch (angle) {
|
||||
case DIAGDIRDIFF_90LEFT:
|
||||
rotated.x -= 1;
|
||||
break;
|
||||
case DIAGDIRDIFF_REVERSE:
|
||||
rotated.x -= 1;
|
||||
rotated.y -= 1;
|
||||
break;
|
||||
case DIAGDIRDIFF_90RIGHT:
|
||||
rotated.y -= 1;
|
||||
break;
|
||||
default:
|
||||
return rotated;
|
||||
}
|
||||
return rotated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate Track by an angle using the formula for rotating a point on a plane.
|
||||
*
|
||||
* @param track Rotating track.
|
||||
* @param angle Rotate an angle.
|
||||
*/
|
||||
Track Rotate(Track track, DiagDirDiff angle) {
|
||||
switch (angle) {
|
||||
case DIAGDIRDIFF_90LEFT:
|
||||
if (track == TRACK_X || track == TRACK_Y) {
|
||||
return TrackToOppositeTrack(track);
|
||||
}
|
||||
switch (track) {
|
||||
case TRACK_UPPER: return TRACK_LEFT;
|
||||
case TRACK_LOWER: return TRACK_RIGHT;
|
||||
case TRACK_LEFT: return TRACK_LOWER;
|
||||
case TRACK_RIGHT: return TRACK_UPPER;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case DIAGDIRDIFF_REVERSE:
|
||||
if (track != TRACK_X && track != TRACK_Y) {
|
||||
return TrackToOppositeTrack(track);
|
||||
}
|
||||
return track;
|
||||
break;
|
||||
case DIAGDIRDIFF_90RIGHT:
|
||||
if (track == TRACK_X || track == TRACK_Y) {
|
||||
return TrackToOppositeTrack(track);
|
||||
}
|
||||
switch (track) {
|
||||
case TRACK_UPPER: return TRACK_RIGHT;
|
||||
case TRACK_LOWER: return TRACK_LEFT;
|
||||
case TRACK_LEFT: return TRACK_UPPER;
|
||||
case TRACK_RIGHT: return TRACK_LOWER;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return track;
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate DiagDirection by an angle using the formula for rotating a point on a plane.
|
||||
*
|
||||
* @param dir Rotating DiagDirection.
|
||||
* @param angle Rotate an angle.
|
||||
*/
|
||||
DiagDirection Rotate(DiagDirection dir, DiagDirDiff angle) {
|
||||
switch (angle) {
|
||||
case DIAGDIRDIFF_90LEFT:
|
||||
dir = (DiagDirection)(dir - 1);
|
||||
if (dir == INVALID_DIAGDIR) {
|
||||
dir = DIAGDIR_NW;
|
||||
}
|
||||
break;
|
||||
case DIAGDIRDIFF_REVERSE:
|
||||
dir = (DiagDirection)(dir ^ 2);
|
||||
break;
|
||||
case DIAGDIRDIFF_90RIGHT:
|
||||
dir = (DiagDirection)(dir + 1);
|
||||
if (dir == DIAGDIR_END) {
|
||||
dir = DIAGDIR_NE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return dir;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate Axis by an angle using the formula for rotating a point on a plane.
|
||||
*
|
||||
* @param axis Rotating axis.
|
||||
* @param angle Rotate an angle.
|
||||
*/
|
||||
Axis Rotate(Axis axis, DiagDirDiff angle) {
|
||||
if (angle == DIAGDIRDIFF_90LEFT || angle == DIAGDIRDIFF_90RIGHT) {
|
||||
return (Axis)(axis ^ 1);
|
||||
}
|
||||
return axis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finding an angle using the formula for the angle between two straight lines
|
||||
*
|
||||
* @param first First line vector.
|
||||
* @param second Second line vector.
|
||||
*/
|
||||
DiagDirDiff AngleBetweenTwoLines(TileIndexDiffC first, TileIndexDiffC second) {
|
||||
if (second.x == 0 && second.y == 0) {
|
||||
return DIAGDIRDIFF_SAME;
|
||||
}
|
||||
|
||||
first.x = first.x > 0 ? 1 : -1;
|
||||
first.y = first.y > 0 ? 1 : -1;
|
||||
second.x = second.x > 0 ? 1 : -1;
|
||||
second.y = second.y > 0 ? 1 : -1;
|
||||
|
||||
int16_t value = first.x * second.x + first.y * second.y;
|
||||
if (value > 0) {
|
||||
return DIAGDIRDIFF_SAME;
|
||||
} else if (value < 0) {
|
||||
return DIAGDIRDIFF_REVERSE;
|
||||
}
|
||||
TileIndexDiffC third = Rotate(first, DIAGDIRDIFF_90LEFT);
|
||||
|
||||
value = third.x * second.x + third.y * second.y;
|
||||
if (value > 0) {
|
||||
return DIAGDIRDIFF_90LEFT;
|
||||
}
|
||||
return DIAGDIRDIFF_90RIGHT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Command callback. If the region cannot be inserted, an error message will be displayed.
|
||||
* @param result Result of the command.
|
||||
* @param tile Tile where the industry is placed.
|
||||
*/
|
||||
void CcCloneArea(Commands, const CommandCost &result, Money, TileIndex tile)
|
||||
{
|
||||
if (result.Succeeded()) {
|
||||
if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, tile);
|
||||
} else {
|
||||
SetRedErrorSquare(tile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the selected area on the map to copy
|
||||
* @param flags for this command type
|
||||
* @param tile end tile of area-drag
|
||||
* @param start_tile start tile of area drag
|
||||
* @param diagonal Whether to use the Orthogonal (false) or Diagonal (true) iterator.
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
std::tuple<CommandCost, Money, TileIndex> CmdCloneAreaCopy([[maybe_unused]] DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal)
|
||||
{
|
||||
if (start_tile >= Map::Size()) return { CMD_ERROR, 0, INVALID_TILE };
|
||||
|
||||
selected_tile = tile;
|
||||
selected_start_tile = start_tile;
|
||||
selected_diagonal = diagonal;
|
||||
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
return { cost, 0, tile };
|
||||
}
|
||||
|
||||
/**
|
||||
* Paste the selected area on the map
|
||||
* @param flags for this command type
|
||||
* @param tile end tile of area-drag
|
||||
* @param start_tile start tile of area drag
|
||||
* @param diagonal Whether to use the Orthogonal (false) or Diagonal (true) iterator.
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
std::tuple<CommandCost, Money, TileIndex> CmdCloneAreaPaste(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal)
|
||||
{
|
||||
if (start_tile >= Map::Size()) return { CMD_ERROR, 0, INVALID_TILE };
|
||||
|
||||
Money money = GetAvailableMoneyForCommand();
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
CommandCost last_error(STR_ERROR_ALREADY_BUILT);
|
||||
bool had_success = false;
|
||||
bool terraform_problem = false;
|
||||
TileIndex terraform_problem_tile = INVALID_TILE;
|
||||
CommandCost terraform_error(STR_ERROR_ALREADY_BUILT);
|
||||
|
||||
const Company *c = Company::GetIfValid(_current_company);
|
||||
int limit = (c == nullptr ? INT32_MAX : GB(c->terraform_limit, 16, 16));
|
||||
if (limit == 0) return { CommandCost(STR_ERROR_TERRAFORM_LIMIT_REACHED), 0, INVALID_TILE };
|
||||
|
||||
TileIndexDiffC origin_direction = TileIndexToTileIndexDiffC(selected_start_tile, selected_tile);
|
||||
TileIndexDiffC dest_direction = TileIndexToTileIndexDiffC(start_tile, tile);
|
||||
DiagDirDiff angle = AngleBetweenTwoLines(origin_direction, dest_direction);
|
||||
|
||||
int16_t position_selected_tile_x = TileX(selected_start_tile);
|
||||
int16_t position_selected_tile_y = TileY(selected_start_tile);
|
||||
int16_t dest_position_x = TileX(start_tile);
|
||||
int16_t dest_position_y = TileY(start_tile);
|
||||
uint origin_main_height = TileHeight(selected_start_tile);
|
||||
uint dest_main_height = TileHeight(start_tile);
|
||||
uint difference_height = dest_main_height - origin_main_height;
|
||||
|
||||
TileIndexDiffC dest_point;
|
||||
|
||||
TileIndex error_tile = INVALID_TILE;
|
||||
std::unique_ptr<TileIterator> iter = TileIterator::Create(selected_start_tile, selected_tile, selected_diagonal);
|
||||
for (; *iter != INVALID_TILE; ++(*iter)) {
|
||||
TileIndex iter_tile = *iter;
|
||||
|
||||
dest_point.x = TileX(iter_tile) - position_selected_tile_x;
|
||||
dest_point.y = TileY(iter_tile) - position_selected_tile_y;
|
||||
dest_point = Rotate(dest_point, angle);
|
||||
dest_point.x += dest_position_x;
|
||||
dest_point.y += dest_position_y;
|
||||
|
||||
TileIndex dest_tile = TileXY(dest_point.x, dest_point.y);
|
||||
|
||||
uint origin_height = TileHeight(iter_tile) + difference_height;
|
||||
uint dest_height = TileHeight(dest_tile);
|
||||
|
||||
while (dest_height != origin_height) {
|
||||
CommandCost ret;
|
||||
std::tie(ret, std::ignore, error_tile) = Command<CMD_TERRAFORM_LAND>::Do(flags & ~DC_EXEC, dest_tile, SLOPE_N, dest_height <= origin_height);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
|
||||
if (!terraform_problem) {
|
||||
terraform_problem_tile = error_tile;
|
||||
terraform_error = last_error;
|
||||
terraform_problem = true;
|
||||
}
|
||||
|
||||
/* Did we reach the limit? */
|
||||
if (ret.GetErrorMessage() == STR_ERROR_TERRAFORM_LIMIT_REACHED) limit = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
money -= ret.GetCost();
|
||||
if (money < 0) {
|
||||
return { cost, ret.GetCost(), error_tile };
|
||||
}
|
||||
Command<CMD_TERRAFORM_LAND>::Do(flags, dest_tile, SLOPE_N, dest_height <= origin_height);
|
||||
} else {
|
||||
/* When we're at the terraform limit we better bail (unneeded) testing as well.
|
||||
* This will probably cause the terraforming cost to be underestimated, but only
|
||||
* when it's near the terraforming limit. Even then, the estimation is
|
||||
* completely off due to it basically counting terraforming double, so it being
|
||||
* cut off earlier might even give a better estimate in some cases. */
|
||||
if (--limit <= 0) {
|
||||
had_success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cost.AddCost(ret);
|
||||
dest_height += (dest_height > origin_height) ? -1 : 1;
|
||||
had_success = true;
|
||||
}
|
||||
if (limit <= 0) break;
|
||||
}
|
||||
|
||||
if (terraform_problem) {
|
||||
return { terraform_error, 0, terraform_problem_tile };
|
||||
}
|
||||
|
||||
CommandCost ret;
|
||||
std::tie(ret, std::ignore, error_tile) = CmdCloneAreaPasteProperty(flags, tile, start_tile, diagonal);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
} else {
|
||||
had_success = true;
|
||||
}
|
||||
if (flags & DC_EXEC) {
|
||||
money -= ret.GetCost();
|
||||
if (money < 0) {
|
||||
return { cost, ret.GetCost(), error_tile };
|
||||
}
|
||||
}
|
||||
|
||||
CommandCost cc_ret = had_success ? cost : last_error;
|
||||
return { cc_ret, 0, cc_ret.Succeeded() ? tile : error_tile };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert structures into the selected area on the map.
|
||||
* @param flags operation to perform
|
||||
* @param tile end tile of rail conversion drag
|
||||
* @param area_start start tile of drag
|
||||
* @param diagonal build diagonally or not.
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
std::tuple<CommandCost, Money, TileIndex> CmdCloneAreaPasteProperty(DoCommandFlag flags, TileIndex tile, TileIndex area_start, [[maybe_unused]] bool diagonal)
|
||||
{
|
||||
if (area_start >= Map::Size()) return { CMD_ERROR, 0, INVALID_TILE };
|
||||
|
||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||
CommandCost last_error(INVALID_STRING_ID);
|
||||
bool had_success = false;
|
||||
bool auto_remove_signals = true;
|
||||
bool signals_copy = true;
|
||||
CommandCost error = CommandCost(STR_ERROR_CAN_T_BUILD_HERE);
|
||||
|
||||
TileIndexDiffC origin_direction = TileIndexToTileIndexDiffC(selected_start_tile, selected_tile);
|
||||
TileIndexDiffC dest_direction = TileIndexToTileIndexDiffC(area_start, tile);
|
||||
|
||||
DiagDirDiff angle = AngleBetweenTwoLines(origin_direction, dest_direction);
|
||||
uint position_selected_tile_x = TileX(selected_start_tile);
|
||||
uint position_selected_tile_y = TileY(selected_start_tile);
|
||||
int16_t dest_position_x = TileX(area_start);
|
||||
int16_t dest_position_y = TileY(area_start);
|
||||
|
||||
TileIndexDiffC dest_point;
|
||||
TileIndex iter_tile;
|
||||
TileIndex error_tile = INVALID_TILE;
|
||||
|
||||
uint x_max = std::max(position_selected_tile_x, TileX(selected_tile)) - 1;
|
||||
uint y_max = std::max(position_selected_tile_y, TileY(selected_tile)) - 1;
|
||||
|
||||
std::map<StationID, StationID> station_map;
|
||||
std::unique_ptr<TileIterator> iter = TileIterator::Create(selected_start_tile, selected_tile, selected_diagonal);
|
||||
for (; (iter_tile = *iter) != INVALID_TILE; ++(*iter)) {
|
||||
if (TileX(iter_tile) > x_max || TileY(iter_tile) > y_max) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TileType iter_tile_type = GetTileType(iter_tile);
|
||||
switch (iter_tile_type) {
|
||||
case MP_RAILWAY:
|
||||
break;
|
||||
case MP_STATION:
|
||||
if (!HasStationRail(iter_tile)) continue;
|
||||
break;
|
||||
case MP_ROAD:
|
||||
if (!IsLevelCrossing(iter_tile)) continue;
|
||||
break;
|
||||
case MP_TUNNELBRIDGE:
|
||||
if (GetTunnelBridgeTransportType(iter_tile) != TRANSPORT_RAIL) continue;
|
||||
break;
|
||||
default: continue;
|
||||
}
|
||||
|
||||
dest_point.x = TileX(iter_tile) - position_selected_tile_x;
|
||||
dest_point.y = TileY(iter_tile) - position_selected_tile_y;
|
||||
dest_point = Rotate(dest_point, angle);
|
||||
dest_point = FixAfterRotate(dest_point, angle);
|
||||
dest_point.x += dest_position_x;
|
||||
dest_point.y += dest_position_y;
|
||||
TileIndex dest_tile = TileXY(dest_point.x, dest_point.y);
|
||||
|
||||
CommandCost ret = CheckTileOwnership(iter_tile);
|
||||
if (ret.Failed()) {
|
||||
error = ret;
|
||||
continue;
|
||||
}
|
||||
|
||||
RailType railtype2;
|
||||
DiagDirection entrance_dir;
|
||||
if (iter_tile_type == MP_RAILWAY) {
|
||||
switch (GetRailTileType(iter_tile)) {
|
||||
case RAIL_TILE_DEPOT:
|
||||
railtype2 = GetRailType(iter_tile);
|
||||
entrance_dir = GetRailDepotDirection(iter_tile);
|
||||
entrance_dir = Rotate(entrance_dir, angle);
|
||||
ret = Command<CMD_BUILD_TRAIN_DEPOT>::Do(flags, dest_tile, railtype2, entrance_dir);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
} else {
|
||||
had_success = true;
|
||||
cost.AddCost(ret);
|
||||
}
|
||||
break;
|
||||
default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
|
||||
RailType rail_type = GetRailType(iter_tile);
|
||||
TrackBits trackBits = GetTrackBits(iter_tile);
|
||||
Track track;
|
||||
while ((track = RemoveFirstTrack(&trackBits)) != INVALID_TRACK) {
|
||||
Track track_dest = Rotate(track, angle);
|
||||
ret = Command<CMD_BUILD_SINGLE_RAIL>::Do(flags, dest_tile, rail_type, track_dest, auto_remove_signals);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
} else {
|
||||
had_success = true;
|
||||
cost.AddCost(ret);
|
||||
}
|
||||
|
||||
if (signals_copy) {
|
||||
if (HasSignalOnTrack(iter_tile, track)) {
|
||||
SignalType sigtype = GetSignalType(iter_tile, track);
|
||||
SignalVariant sigvar = GetSignalVariant(iter_tile, track);
|
||||
ret = Command<CMD_BUILD_SINGLE_SIGNAL>::Do(flags, dest_tile, track_dest, sigtype, sigvar, false, false, false, SIGTYPE_BLOCK, SIGTYPE_BLOCK, 0, 0);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
} else {
|
||||
had_success = true;
|
||||
cost.AddCost(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (iter_tile_type == MP_TUNNELBRIDGE) {
|
||||
if (IsTunnel(iter_tile)) {
|
||||
RailType rail_type = GetRailType(iter_tile);
|
||||
ret = Command<CMD_BUILD_TUNNEL>::Do(flags, dest_tile, TRANSPORT_RAIL, rail_type);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
} else {
|
||||
had_success = true;
|
||||
cost.AddCost(ret);
|
||||
}
|
||||
} else {
|
||||
RailType rail_type = GetRailType(iter_tile);
|
||||
BridgeType type = GetBridgeType(iter_tile);
|
||||
TileIndex endIterTile = GetOtherTunnelBridgeEnd(iter_tile);
|
||||
|
||||
TileIndexDiffC end_dest_point;
|
||||
end_dest_point.x = TileX(endIterTile) - position_selected_tile_x;
|
||||
end_dest_point.y = TileY(endIterTile) - position_selected_tile_y;
|
||||
end_dest_point = Rotate(end_dest_point, angle);
|
||||
end_dest_point = FixAfterRotate(end_dest_point, angle);
|
||||
end_dest_point.x += dest_position_x;
|
||||
end_dest_point.y += dest_position_y;
|
||||
TileIndex end_dest_tile = TileXY(end_dest_point.x, end_dest_point.y);
|
||||
|
||||
ret = Command<CMD_BUILD_BRIDGE>::Do(flags, end_dest_tile, dest_tile, TRANSPORT_RAIL, type, rail_type);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
} else {
|
||||
had_success = true;
|
||||
cost.AddCost(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iter_tile_type == MP_STATION) {
|
||||
if (HasStationRail(iter_tile)) {
|
||||
RailType rail_type = GetRailType(iter_tile);
|
||||
StationID origin_station_id = GetStationIndex(iter_tile);
|
||||
StationID dest_station_id;
|
||||
if (station_map.contains(origin_station_id)) {
|
||||
dest_station_id = station_map[origin_station_id];
|
||||
} else {
|
||||
dest_station_id = NEW_STATION;
|
||||
}
|
||||
Axis origin_axis = GetRailStationAxis(iter_tile);
|
||||
Axis dest_axis = Rotate(origin_axis, angle);
|
||||
ret = Command<CMD_BUILD_RAIL_STATION>::Do(flags, dest_tile, rail_type, dest_axis, 1, 1, STAT_CLASS_DFLT, STATION_RAIL, dest_station_id, false);
|
||||
if (ret.Failed()) {
|
||||
last_error = ret;
|
||||
} else {
|
||||
had_success = true;
|
||||
cost.AddCost(ret);
|
||||
if (flags & DC_EXEC) {
|
||||
if (dest_station_id == NEW_STATION) {
|
||||
dest_station_id = GetStationIndex(dest_tile);
|
||||
station_map[origin_station_id] = dest_station_id;
|
||||
}
|
||||
StationGfx station_gfx = GetStationGfx(iter_tile);
|
||||
if (origin_axis != dest_axis) {
|
||||
ToggleBit(station_gfx, 0);
|
||||
}
|
||||
if (angle == DIAGDIRDIFF_90RIGHT || angle == DIAGDIRDIFF_REVERSE) {
|
||||
ToggleBit(station_gfx, 1);
|
||||
}
|
||||
SetStationGfx(dest_tile, station_gfx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CommandCost cc_ret = had_success ? cost : last_error;
|
||||
return { cc_ret, 0, cc_ret.Succeeded() ? tile : error_tile };
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file clone_area_cmd.h Command definitions related to cloning area. */
|
||||
|
||||
#ifndef CLONE_AREA_CMD_H
|
||||
#define CLONE_AREA_CMD_H
|
||||
std::tuple<CommandCost, Money, TileIndex> CmdCloneAreaCopy(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal);
|
||||
std::tuple<CommandCost, Money, TileIndex> CmdCloneAreaPaste(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal);
|
||||
std::tuple<CommandCost, Money, TileIndex> CmdCloneAreaPasteProperty(DoCommandFlag flags, TileIndex tile, TileIndex area_start, bool diagonal);
|
||||
|
||||
DEF_CMD_TRAIT(CMD_CLONE_AREA_COPY, CmdCloneAreaCopy, CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
DEF_CMD_TRAIT(CMD_CLONE_AREA_PASTE, CmdCloneAreaPaste, CMD_ALL_TILES | CMD_AUTO | CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||
|
||||
void CcCloneArea(Commands cmd, const CommandCost &result, Money, TileIndex tile);
|
||||
|
||||
#endif /* CLONE_AREA_CMD_H */
|
|
@ -58,6 +58,7 @@
|
|||
#include "waypoint_cmd.h"
|
||||
#include "misc/endian_buffer.hpp"
|
||||
#include "string_func.h"
|
||||
#include "clone_area_cmd.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
|
||||
|
|
|
@ -317,6 +317,8 @@ enum Commands : uint16_t {
|
|||
CMD_STORY_PAGE_BUTTON, ///< selection via story page button
|
||||
|
||||
CMD_LEVEL_LAND, ///< level land
|
||||
CMD_CLONE_AREA_COPY, ///< Clone land
|
||||
CMD_CLONE_AREA_PASTE, ///< Clone land
|
||||
|
||||
CMD_BUILD_LOCK, ///< build a lock
|
||||
|
||||
|
|
|
@ -2966,6 +2966,8 @@ STR_LANDSCAPING_TOOLTIP_LOWER_A_CORNER_OF_LAND :{BLACK}Lower a
|
|||
STR_LANDSCAPING_TOOLTIP_RAISE_A_CORNER_OF_LAND :{BLACK}Raise a corner of land. Click+Drag to raise the first selected corner and level the selected area to the new corner height. Ctrl+Click+Drag to select the area diagonally. Also press Shift to show cost estimate only
|
||||
STR_LANDSCAPING_LEVEL_LAND_TOOLTIP :{BLACK}Level an area of land to the height of the first selected corner. Ctrl+Click+Drag to select the area diagonally. Also press Shift to show cost estimate only
|
||||
STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Purchase land for future use. Ctrl+Click+Drag to select the area diagonally. Also press Shift to show cost estimate only
|
||||
STR_LANDSCAPING_TOOLTIP_CLONE_AREA_COPY :{BLACK}Copy Area
|
||||
STR_LANDSCAPING_TOOLTIP_CLONE_AREA_PASTE :{BLACK}Paste Area
|
||||
|
||||
# Object construction window
|
||||
STR_OBJECT_BUILD_CAPTION :{WHITE}Object Selection
|
||||
|
@ -4971,6 +4973,8 @@ STR_ERROR_TREE_PLANT_LIMIT_REACHED :{WHITE}... tree
|
|||
STR_ERROR_NAME_MUST_BE_UNIQUE :{WHITE}Name must be unique
|
||||
STR_ERROR_GENERIC_OBJECT_IN_THE_WAY :{WHITE}{1:STRING} in the way
|
||||
STR_ERROR_NOT_ALLOWED_WHILE_PAUSED :{WHITE}Not allowed while paused
|
||||
STR_ERROR_CAN_T_CLONE_AREA_COPY :{WHITE}Can't Copy this area...
|
||||
STR_ERROR_CAN_T_CLONE_AREA_PASTE :{WHITE}Can't Paste this area...
|
||||
|
||||
# Local authority errors
|
||||
STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS :{WHITE}{TOWN} local authority refuses to allow this
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "../story_cmd.h"
|
||||
#include "../subsidy_cmd.h"
|
||||
#include "../terraform_cmd.h"
|
||||
#include "../clone_area_cmd.h"
|
||||
#include "../timetable_cmd.h"
|
||||
#include "../town_cmd.h"
|
||||
#include "../train_cmd.h"
|
||||
|
@ -74,6 +75,7 @@ static constexpr auto _callback_tuple = std::make_tuple(
|
|||
&CcPlaySound_CONSTRUCTION_RAIL,
|
||||
&CcStation,
|
||||
&CcTerraform,
|
||||
&CcCloneArea,
|
||||
&CcAI,
|
||||
&CcCloneVehicle,
|
||||
&CcCreateGroup,
|
||||
|
|
|
@ -140,6 +140,8 @@ public:
|
|||
SPBC_RAISELAND = ::SPBC_RAISELAND,
|
||||
SPBC_PICKSTATION = ::SPBC_PICKSTATION,
|
||||
SPBC_BUILDSIGNALS = ::SPBC_BUILDSIGNALS,
|
||||
SPBC_CLONE_AREA_COPY = ::SPBC_CLONE_AREA_COPY,
|
||||
SPBC_CLONE_AREA_PASTE = ::SPBC_CLONE_AREA_PASTE,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -98,6 +98,8 @@ enum StoryPageButtonCursor : uint8_t {
|
|||
SPBC_CLONE_ROADVEH,
|
||||
SPBC_CLONE_SHIP,
|
||||
SPBC_CLONE_AIRPLANE,
|
||||
SPBC_CLONE_AREA_COPY,
|
||||
SPBC_CLONE_AREA_PASTE,
|
||||
SPBC_DEMOLISH,
|
||||
SPBC_LOWERLAND,
|
||||
SPBC_RAISELAND,
|
||||
|
|
|
@ -1033,6 +1033,8 @@ static CursorID TranslateStoryPageButtonCursor(StoryPageButtonCursor cursor)
|
|||
case SPBC_CLONE_ROADVEH: return SPR_CURSOR_CLONE_ROADVEH;
|
||||
case SPBC_CLONE_SHIP: return SPR_CURSOR_CLONE_SHIP;
|
||||
case SPBC_CLONE_AIRPLANE: return SPR_CURSOR_CLONE_AIRPLANE;
|
||||
case SPBC_CLONE_AREA_COPY: return SPR_CURSOR_CLONE_AREA_COPY;
|
||||
case SPBC_CLONE_AREA_PASTE: return SPR_CURSOR_CLONE_AREA_PASTE;
|
||||
case SPBC_DEMOLISH: return ANIMCURSOR_DEMOLISH;
|
||||
case SPBC_LOWERLAND: return ANIMCURSOR_LOWERLAND;
|
||||
case SPBC_RAISELAND: return ANIMCURSOR_RAISELAND;
|
||||
|
|
|
@ -54,7 +54,7 @@ static const SpriteID SPR_LARGE_SMALL_WINDOW = 682;
|
|||
|
||||
/** Extra graphic spritenumbers */
|
||||
static const SpriteID SPR_OPENTTD_BASE = 4896;
|
||||
static const uint16_t OPENTTD_SPRITE_COUNT = 191;
|
||||
static const uint16_t OPENTTD_SPRITE_COUNT = 195;
|
||||
|
||||
/* Halftile-selection sprites */
|
||||
static const SpriteID SPR_HALFTILE_SELECTION_FLAT = SPR_OPENTTD_BASE;
|
||||
|
@ -174,6 +174,11 @@ static const SpriteID SPR_PLAYER_HOST = SPR_OPENTTD_BASE + 190;
|
|||
|
||||
static const SpriteID SPR_IMG_CARGOFLOW = SPR_OPENTTD_BASE + 174;
|
||||
|
||||
static const SpriteID SPR_IMG_CLONE_AREA_COPY = SPR_OPENTTD_BASE + 191;
|
||||
static const SpriteID SPR_IMG_CLONE_AREA_PASTE = SPR_OPENTTD_BASE + 192;
|
||||
static const CursorID SPR_CURSOR_CLONE_AREA_COPY = SPR_OPENTTD_BASE + 193;
|
||||
static const CursorID SPR_CURSOR_CLONE_AREA_PASTE = SPR_OPENTTD_BASE + 194;
|
||||
|
||||
static const SpriteID SPR_SIGNALS_BASE = SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT;
|
||||
static const uint16_t PRESIGNAL_SPRITE_COUNT = 48;
|
||||
static const uint16_t PRESIGNAL_AND_SEMAPHORE_SPRITE_COUNT = 112;
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "landscape_cmd.h"
|
||||
#include "terraform_cmd.h"
|
||||
#include "object_cmd.h"
|
||||
#include "clone_area_cmd.h"
|
||||
|
||||
#include "widgets/terraform_widget.h"
|
||||
|
||||
|
@ -131,6 +132,12 @@ bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_t
|
|||
case DDSP_LEVEL_AREA:
|
||||
Command<CMD_LEVEL_LAND>::Post(STR_ERROR_CAN_T_LEVEL_LAND_HERE, CcTerraform, end_tile, start_tile, _ctrl_pressed, LM_LEVEL);
|
||||
break;
|
||||
case DDSP_CLONE_AREA_COPY:
|
||||
Command<CMD_CLONE_AREA_COPY>::Post(STR_ERROR_CAN_T_CLONE_AREA_COPY, CcCloneArea, end_tile, start_tile, _ctrl_pressed);
|
||||
break;
|
||||
case DDSP_CLONE_AREA_PASTE:
|
||||
Command<CMD_CLONE_AREA_PASTE>::Post(STR_ERROR_CAN_T_CLONE_AREA_PASTE, CcCloneArea, end_tile, start_tile, _ctrl_pressed);
|
||||
break;
|
||||
case DDSP_CREATE_ROCKS:
|
||||
GenerateRockyArea(end_tile, start_tile);
|
||||
break;
|
||||
|
@ -162,6 +169,7 @@ struct TerraformToolbarWindow : Window {
|
|||
/* This is needed as we like to have the tree available on OnInit. */
|
||||
this->CreateNestedTree();
|
||||
this->FinishInitNested(window_number);
|
||||
this->DisableWidget(WID_TT_CLONE_AREA_PASTE);
|
||||
this->last_user_action = INVALID_WID_TT;
|
||||
}
|
||||
|
||||
|
@ -196,6 +204,16 @@ struct TerraformToolbarWindow : Window {
|
|||
this->last_user_action = widget;
|
||||
break;
|
||||
|
||||
case WID_TT_CLONE_AREA_COPY: // Copy area button
|
||||
HandlePlacePushButton(this, WID_TT_CLONE_AREA_COPY, SPR_CURSOR_CLONE_AREA_COPY, HT_POINT | HT_DIAGONAL);
|
||||
this->last_user_action = widget;
|
||||
break;
|
||||
|
||||
case WID_TT_CLONE_AREA_PASTE: // Paste area button
|
||||
HandlePlacePushButton(this, WID_TT_CLONE_AREA_PASTE, SPR_CURSOR_CLONE_AREA_PASTE, HT_POINT | HT_DIAGONAL);
|
||||
this->last_user_action = widget;
|
||||
break;
|
||||
|
||||
case WID_TT_DEMOLISH: // Demolish aka dynamite button
|
||||
HandlePlacePushButton(this, WID_TT_DEMOLISH, ANIMCURSOR_DEMOLISH, HT_RECT | HT_DIAGONAL);
|
||||
this->last_user_action = widget;
|
||||
|
@ -238,6 +256,14 @@ struct TerraformToolbarWindow : Window {
|
|||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_LEVEL_AREA);
|
||||
break;
|
||||
|
||||
case WID_TT_CLONE_AREA_COPY: // Clone area button
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CLONE_AREA_COPY);
|
||||
break;
|
||||
|
||||
case WID_TT_CLONE_AREA_PASTE: // Clone area button
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CLONE_AREA_PASTE);
|
||||
break;
|
||||
|
||||
case WID_TT_DEMOLISH: // Demolish aka dynamite button
|
||||
PlaceProc_DemolishArea(tile);
|
||||
break;
|
||||
|
@ -275,6 +301,11 @@ struct TerraformToolbarWindow : Window {
|
|||
case DDSP_RAISE_AND_LEVEL_AREA:
|
||||
case DDSP_LOWER_AND_LEVEL_AREA:
|
||||
case DDSP_LEVEL_AREA:
|
||||
case DDSP_CLONE_AREA_PASTE:
|
||||
GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
|
||||
break;
|
||||
case DDSP_CLONE_AREA_COPY:
|
||||
this->CloneAreaPasteWidgetEnable(true);
|
||||
GUIPlaceProcDragXY(select_proc, start_tile, end_tile);
|
||||
break;
|
||||
case DDSP_BUILD_OBJECT:
|
||||
|
@ -296,6 +327,13 @@ struct TerraformToolbarWindow : Window {
|
|||
this->RaiseButtons();
|
||||
}
|
||||
|
||||
void CloneAreaPasteWidgetEnable(bool value)
|
||||
{
|
||||
this->SetWidgetDisabledState(WID_TT_CLONE_AREA_PASTE, !value);
|
||||
this->RaiseWidget(WID_TT_CLONE_AREA_PASTE);
|
||||
this->SetWidgetDirty(WID_TT_CLONE_AREA_PASTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for global hotkeys of the TerraformToolbarWindow.
|
||||
* @param hotkey Hotkey
|
||||
|
@ -345,6 +383,12 @@ static constexpr NWidgetPart _nested_terraform_widgets[] = {
|
|||
SetFill(0, 1), SetDataTip(SPR_IMG_PLANTTREES, STR_SCENEDIT_TOOLBAR_PLANT_TREES),
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_PLACE_SIGN), SetMinimalSize(22, 22),
|
||||
SetFill(0, 1), SetDataTip(SPR_IMG_SIGN, STR_SCENEDIT_TOOLBAR_PLACE_SIGN),
|
||||
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_CLONE_AREA_COPY), SetMinimalSize(22, 22),
|
||||
SetFill(0, 1), SetDataTip(SPR_IMG_CLONE_AREA_COPY, STR_LANDSCAPING_TOOLTIP_CLONE_AREA_COPY),
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_TT_CLONE_AREA_PASTE), SetMinimalSize(22, 22),
|
||||
SetFill(0, 1), SetDataTip(SPR_IMG_CLONE_AREA_PASTE, STR_LANDSCAPING_TOOLTIP_CLONE_AREA_PASTE),
|
||||
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_TT_SHOW_PLACE_OBJECT),
|
||||
NWidget(WWT_PUSHIMGBTN, COLOUR_DARK_GREEN, WID_TT_PLACE_OBJECT), SetMinimalSize(22, 22),
|
||||
SetFill(0, 1), SetDataTip(SPR_IMG_TRANSMITTER, STR_SCENEDIT_TOOLBAR_PLACE_OBJECT),
|
||||
|
|
|
@ -120,6 +120,8 @@ enum ViewportDragDropSelectionProcess {
|
|||
DDSP_PLANT_TREES, ///< Plant trees
|
||||
DDSP_BUILD_BRIDGE, ///< Bridge placement
|
||||
DDSP_BUILD_OBJECT, ///< Build an object
|
||||
DDSP_CLONE_AREA_COPY, ///< Copy area
|
||||
DDSP_CLONE_AREA_PASTE, ///< Paste area
|
||||
|
||||
/* Rail specific actions */
|
||||
DDSP_PLACE_RAIL, ///< Rail placement
|
||||
|
|
|
@ -22,6 +22,8 @@ enum TerraformToolbarWidgets : WidgetID {
|
|||
WID_TT_PLANT_TREES, ///< Plant trees button (note: opens separate window, no place-push-button).
|
||||
WID_TT_PLACE_SIGN, ///< Place sign button.
|
||||
WID_TT_PLACE_OBJECT, ///< Place object button.
|
||||
WID_TT_CLONE_AREA_COPY, ///< Copy area button.
|
||||
WID_TT_CLONE_AREA_PASTE, ///< Paste area button.
|
||||
|
||||
INVALID_WID_TT = -1,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue