mirror of https://github.com/OpenTTD/OpenTTD.git
Feature: Build objects by area
This commit is contained in:
parent
bd357656ba
commit
3d45bc4abe
|
@ -186,6 +186,7 @@ enum Commands : uint16 {
|
||||||
CMD_REMOVE_SIGNALS, ///< remove a signal
|
CMD_REMOVE_SIGNALS, ///< remove a signal
|
||||||
CMD_TERRAFORM_LAND, ///< terraform a tile
|
CMD_TERRAFORM_LAND, ///< terraform a tile
|
||||||
CMD_BUILD_OBJECT, ///< build an object
|
CMD_BUILD_OBJECT, ///< build an object
|
||||||
|
CMD_BUILD_OBJECT_AREA, ///< build an area of objects
|
||||||
CMD_BUILD_TUNNEL, ///< build a tunnel
|
CMD_BUILD_TUNNEL, ///< build a tunnel
|
||||||
|
|
||||||
CMD_REMOVE_FROM_RAIL_STATION, ///< remove a (rectangle of) tiles from a rail station
|
CMD_REMOVE_FROM_RAIL_STATION, ///< remove a (rectangle of) tiles from a rail station
|
||||||
|
|
|
@ -87,6 +87,7 @@ struct CompanyProperties {
|
||||||
uint32 terraform_limit; ///< Amount of tileheights we can (still) terraform (times 65536).
|
uint32 terraform_limit; ///< Amount of tileheights we can (still) terraform (times 65536).
|
||||||
uint32 clear_limit; ///< Amount of tiles we can (still) clear (times 65536).
|
uint32 clear_limit; ///< Amount of tiles we can (still) clear (times 65536).
|
||||||
uint32 tree_limit; ///< Amount of trees we can (still) plant (times 65536).
|
uint32 tree_limit; ///< Amount of trees we can (still) plant (times 65536).
|
||||||
|
uint32 build_object_limit; ///< Amount of tiles we can (still) build objects on (times 65536).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If \c true, the company is (also) controlled by the computer (a NoAI program).
|
* If \c true, the company is (also) controlled by the computer (a NoAI program).
|
||||||
|
@ -110,7 +111,7 @@ struct CompanyProperties {
|
||||||
face(0), money(0), money_fraction(0), current_loan(0), colour(0), block_preview(0),
|
face(0), money(0), money_fraction(0), current_loan(0), colour(0), block_preview(0),
|
||||||
location_of_HQ(0), last_build_coordinate(0), share_owners(), inaugurated_year(0),
|
location_of_HQ(0), last_build_coordinate(0), share_owners(), inaugurated_year(0),
|
||||||
months_of_bankruptcy(0), bankrupt_asked(0), bankrupt_timeout(0), bankrupt_value(0),
|
months_of_bankruptcy(0), bankrupt_asked(0), bankrupt_timeout(0), bankrupt_value(0),
|
||||||
terraform_limit(0), clear_limit(0), tree_limit(0), is_ai(false), engine_renew_list(nullptr) {}
|
terraform_limit(0), clear_limit(0), tree_limit(0), build_object_limit(0), is_ai(false), engine_renew_list(nullptr) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> {
|
struct Company : CompanyProperties, CompanyPool::PoolItem<&_company_pool> {
|
||||||
|
|
|
@ -63,9 +63,10 @@ Company::Company(uint16 name_1, bool is_ai)
|
||||||
this->name_1 = name_1;
|
this->name_1 = name_1;
|
||||||
this->location_of_HQ = INVALID_TILE;
|
this->location_of_HQ = INVALID_TILE;
|
||||||
this->is_ai = is_ai;
|
this->is_ai = is_ai;
|
||||||
this->terraform_limit = (uint32)_settings_game.construction.terraform_frame_burst << 16;
|
this->terraform_limit = (uint32)_settings_game.construction.terraform_frame_burst << 16;
|
||||||
this->clear_limit = (uint32)_settings_game.construction.clear_frame_burst << 16;
|
this->clear_limit = (uint32)_settings_game.construction.clear_frame_burst << 16;
|
||||||
this->tree_limit = (uint32)_settings_game.construction.tree_frame_burst << 16;
|
this->tree_limit = (uint32)_settings_game.construction.tree_frame_burst << 16;
|
||||||
|
this->build_object_limit = (uint32)_settings_game.construction.build_object_frame_burst << 16;
|
||||||
|
|
||||||
std::fill(this->share_owners.begin(), this->share_owners.end(), INVALID_OWNER);
|
std::fill(this->share_owners.begin(), this->share_owners.end(), INVALID_OWNER);
|
||||||
InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, INVALID_COMPANY);
|
InvalidateWindowData(WC_PERFORMANCE_DETAIL, 0, INVALID_COMPANY);
|
||||||
|
@ -267,9 +268,10 @@ void SubtractMoneyFromCompanyFract(CompanyID company, const CommandCost &cst)
|
||||||
void UpdateLandscapingLimits()
|
void UpdateLandscapingLimits()
|
||||||
{
|
{
|
||||||
for (Company *c : Company::Iterate()) {
|
for (Company *c : Company::Iterate()) {
|
||||||
c->terraform_limit = std::min<uint64>((uint64)c->terraform_limit + _settings_game.construction.terraform_per_64k_frames, (uint64)_settings_game.construction.terraform_frame_burst << 16);
|
c->terraform_limit = std::min<uint64>((uint64)c->terraform_limit + _settings_game.construction.terraform_per_64k_frames, (uint64)_settings_game.construction.terraform_frame_burst << 16);
|
||||||
c->clear_limit = std::min<uint64>((uint64)c->clear_limit + _settings_game.construction.clear_per_64k_frames, (uint64)_settings_game.construction.clear_frame_burst << 16);
|
c->clear_limit = std::min<uint64>((uint64)c->clear_limit + _settings_game.construction.clear_per_64k_frames, (uint64)_settings_game.construction.clear_frame_burst << 16);
|
||||||
c->tree_limit = std::min<uint64>((uint64)c->tree_limit + _settings_game.construction.tree_per_64k_frames, (uint64)_settings_game.construction.tree_frame_burst << 16);
|
c->tree_limit = std::min<uint64>((uint64)c->tree_limit + _settings_game.construction.tree_per_64k_frames, (uint64)_settings_game.construction.tree_frame_burst << 16);
|
||||||
|
c->build_object_limit = std::min<uint64>((uint64)c->build_object_limit + _settings_game.construction.build_object_per_64k_frames, (uint64)_settings_game.construction.build_object_frame_burst << 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -378,7 +378,7 @@ STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Road con
|
||||||
STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Tramway construction
|
STR_SCENEDIT_TOOLBAR_TRAM_CONSTRUCTION :{BLACK}Tramway construction
|
||||||
STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}Plant trees. Shift toggles building/showing cost estimate
|
STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}Plant trees. Shift toggles building/showing cost estimate
|
||||||
STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}Place sign
|
STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}Place sign
|
||||||
STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}Place object. Shift toggles building/showing cost estimate
|
STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}Place object. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate
|
||||||
|
|
||||||
# Scenario editor file menu
|
# Scenario editor file menu
|
||||||
###length 7
|
###length 7
|
||||||
|
@ -2816,7 +2816,7 @@ STR_LANDSCAPING_TOOLTIP_PURCHASE_LAND :{BLACK}Purchase
|
||||||
|
|
||||||
# Object construction window
|
# Object construction window
|
||||||
STR_OBJECT_BUILD_CAPTION :{WHITE}Object Selection
|
STR_OBJECT_BUILD_CAPTION :{WHITE}Object Selection
|
||||||
STR_OBJECT_BUILD_TOOLTIP :{BLACK}Select object to build. Shift toggles building/showing cost estimate
|
STR_OBJECT_BUILD_TOOLTIP :{BLACK}Select object to build. Ctrl selects the area diagonally. Shift toggles building/showing cost estimate
|
||||||
STR_OBJECT_BUILD_CLASS_TOOLTIP :{BLACK}Select class of the object to build
|
STR_OBJECT_BUILD_CLASS_TOOLTIP :{BLACK}Select class of the object to build
|
||||||
STR_OBJECT_BUILD_PREVIEW_TOOLTIP :{BLACK}Preview of the object
|
STR_OBJECT_BUILD_PREVIEW_TOOLTIP :{BLACK}Preview of the object
|
||||||
STR_OBJECT_BUILD_SIZE :{BLACK}Size: {GOLD}{NUM} x {NUM} tiles
|
STR_OBJECT_BUILD_SIZE :{BLACK}Size: {GOLD}{NUM} x {NUM} tiles
|
||||||
|
@ -4925,6 +4925,7 @@ STR_ERROR_OBJECT_IN_THE_WAY :{WHITE}Object i
|
||||||
STR_ERROR_COMPANY_HEADQUARTERS_IN :{WHITE}... company headquarters in the way
|
STR_ERROR_COMPANY_HEADQUARTERS_IN :{WHITE}... company headquarters in the way
|
||||||
STR_ERROR_CAN_T_PURCHASE_THIS_LAND :{WHITE}Can't purchase this land area...
|
STR_ERROR_CAN_T_PURCHASE_THIS_LAND :{WHITE}Can't purchase this land area...
|
||||||
STR_ERROR_YOU_ALREADY_OWN_IT :{WHITE}... you already own it!
|
STR_ERROR_YOU_ALREADY_OWN_IT :{WHITE}... you already own it!
|
||||||
|
STR_ERROR_BUILD_OBJECT_LIMIT_REACHED :{WHITE}... object construction limit reached
|
||||||
|
|
||||||
# Group related errors
|
# Group related errors
|
||||||
STR_ERROR_GROUP_CAN_T_CREATE :{WHITE}Can't create group...
|
STR_ERROR_GROUP_CAN_T_CREATE :{WHITE}Can't create group...
|
||||||
|
|
|
@ -311,6 +311,7 @@ CommandCost CmdBuildObject(DoCommandFlag flags, TileIndex tile, ObjectType type,
|
||||||
}
|
}
|
||||||
|
|
||||||
int hq_score = 0;
|
int hq_score = 0;
|
||||||
|
uint build_object_size = 1;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case OBJECT_TRANSMITTER:
|
case OBJECT_TRANSMITTER:
|
||||||
case OBJECT_LIGHTHOUSE:
|
case OBJECT_LIGHTHOUSE:
|
||||||
|
@ -349,20 +350,85 @@ CommandCost CmdBuildObject(DoCommandFlag flags, TileIndex tile, ObjectType type,
|
||||||
return CMD_ERROR;
|
return CMD_ERROR;
|
||||||
|
|
||||||
default: // i.e. NewGRF provided.
|
default: // i.e. NewGRF provided.
|
||||||
|
build_object_size = size_x * size_y;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Don't allow building more objects if the company has reached its limit. */
|
||||||
|
Company *c = Company::GetIfValid(_current_company);
|
||||||
|
if (c != nullptr && GB(c->build_object_limit, 16, 16) < build_object_size) {
|
||||||
|
return_cmd_error(STR_ERROR_BUILD_OBJECT_LIMIT_REACHED);
|
||||||
|
}
|
||||||
|
|
||||||
if (flags & DC_EXEC) {
|
if (flags & DC_EXEC) {
|
||||||
BuildObject(type, tile, _current_company == OWNER_DEITY ? OWNER_NONE : _current_company, nullptr, view);
|
BuildObject(type, tile, _current_company == OWNER_DEITY ? OWNER_NONE : _current_company, nullptr, view);
|
||||||
|
|
||||||
/* Make sure the HQ starts at the right size. */
|
/* Make sure the HQ starts at the right size. */
|
||||||
if (type == OBJECT_HQ) UpdateCompanyHQ(tile, hq_score);
|
if (type == OBJECT_HQ) UpdateCompanyHQ(tile, hq_score);
|
||||||
|
|
||||||
|
/* Subtract the tile from the build limit. */
|
||||||
|
if (c != nullptr) c->build_object_limit -= build_object_size << 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
cost.AddCost(ObjectSpec::Get(type)->GetBuildCost() * size_x * size_y);
|
cost.AddCost(spec->GetBuildCost() * build_object_size);
|
||||||
return cost;
|
return cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct multiple objects in an area
|
||||||
|
* @param flags of operation to conduct
|
||||||
|
* @param tile end tile of area dragging
|
||||||
|
* @param start_tile start tile of area dragging
|
||||||
|
* @param type the object type to build
|
||||||
|
* @param view the view for the object
|
||||||
|
* @param diagonal Whether to use the Orthogonal (0) or Diagonal (1) iterator.
|
||||||
|
* @return the cost of this operation or an error
|
||||||
|
*/
|
||||||
|
CommandCost CmdBuildObjectArea(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, ObjectType type, uint8 view, bool diagonal)
|
||||||
|
{
|
||||||
|
if (start_tile >= MapSize()) return CMD_ERROR;
|
||||||
|
|
||||||
|
if (type >= NUM_OBJECTS) return CMD_ERROR;
|
||||||
|
const ObjectSpec *spec = ObjectSpec::Get(type);
|
||||||
|
if (view >= spec->views) return CMD_ERROR;
|
||||||
|
|
||||||
|
if (spec->size != OBJECT_SIZE_1X1) return CMD_ERROR;
|
||||||
|
|
||||||
|
Money money = GetAvailableMoneyForCommand();
|
||||||
|
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||||
|
CommandCost last_error = CMD_ERROR;
|
||||||
|
bool had_success = false;
|
||||||
|
|
||||||
|
const Company *c = Company::GetIfValid(_current_company);
|
||||||
|
int limit = (c == nullptr ? INT32_MAX : GB(c->build_object_limit, 16, 16));
|
||||||
|
|
||||||
|
TileIterator *iter = diagonal ? (TileIterator *)new DiagonalTileIterator(tile, start_tile) : new OrthogonalTileIterator(tile, start_tile);
|
||||||
|
for (; *iter != INVALID_TILE; ++(*iter)) {
|
||||||
|
TileIndex t = *iter;
|
||||||
|
CommandCost ret = Command<CMD_BUILD_OBJECT>::Do(flags & ~DC_EXEC, t, type, view);
|
||||||
|
|
||||||
|
/* If we've reached the limit, stop building (or testing). */
|
||||||
|
if (c != nullptr && --limit <= 0) break;
|
||||||
|
|
||||||
|
if (ret.Failed()) {
|
||||||
|
last_error = ret;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
had_success = true;
|
||||||
|
if (flags & DC_EXEC) {
|
||||||
|
money -= ret.GetCost();
|
||||||
|
|
||||||
|
/* If we run out of money, stop building. */
|
||||||
|
if (ret.GetCost() > 0 && money < 0) break;
|
||||||
|
Command<CMD_BUILD_OBJECT>::Do(flags, t, type, view);
|
||||||
|
}
|
||||||
|
cost.AddCost(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete iter;
|
||||||
|
return had_success ? cost : last_error;
|
||||||
|
}
|
||||||
|
|
||||||
static Foundation GetFoundation_Object(TileIndex tile, Slope tileh);
|
static Foundation GetFoundation_Object(TileIndex tile, Slope tileh);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
#include "object_type.h"
|
#include "object_type.h"
|
||||||
|
|
||||||
CommandCost CmdBuildObject(DoCommandFlag flags, TileIndex tile, ObjectType type, uint8 view);
|
CommandCost CmdBuildObject(DoCommandFlag flags, TileIndex tile, ObjectType type, uint8 view);
|
||||||
|
CommandCost CmdBuildObjectArea(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, ObjectType type, uint8 view, bool diagonal);
|
||||||
|
|
||||||
DEF_CMD_TRAIT(CMD_BUILD_OBJECT, CmdBuildObject, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
|
DEF_CMD_TRAIT(CMD_BUILD_OBJECT, CmdBuildObject, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||||
|
DEF_CMD_TRAIT(CMD_BUILD_OBJECT_AREA, CmdBuildObjectArea, CMD_DEITY | CMD_NO_WATER | CMD_AUTO, CMDT_LANDSCAPE_CONSTRUCTION)
|
||||||
|
|
||||||
#endif /* OBJECT_CMD_H */
|
#endif /* OBJECT_CMD_H */
|
||||||
|
|
|
@ -458,7 +458,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_selected_object_index != -1) {
|
if (_selected_object_index != -1) {
|
||||||
SetObjectToPlaceWnd(SPR_CURSOR_TRANSMITTER, PAL_NONE, HT_RECT, this);
|
SetObjectToPlaceWnd(SPR_CURSOR_TRANSMITTER, PAL_NONE, HT_RECT | HT_DIAGONAL, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->UpdateButtons(_selected_object_class, _selected_object_index, _selected_object_view);
|
this->UpdateButtons(_selected_object_class, _selected_object_index, _selected_object_view);
|
||||||
|
@ -543,9 +543,38 @@ public:
|
||||||
|
|
||||||
void OnPlaceObject(Point pt, TileIndex tile) override
|
void OnPlaceObject(Point pt, TileIndex tile) override
|
||||||
{
|
{
|
||||||
ObjectClass *objclass = ObjectClass::Get(_selected_object_class);
|
const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index);
|
||||||
Command<CMD_BUILD_OBJECT>::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER,
|
|
||||||
tile, objclass->GetSpec(_selected_object_index)->Index(), _selected_object_view);
|
if (spec->size == OBJECT_SIZE_1X1) {
|
||||||
|
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_BUILD_OBJECT);
|
||||||
|
} else {
|
||||||
|
Command<CMD_BUILD_OBJECT>::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), _selected_object_view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt) override
|
||||||
|
{
|
||||||
|
VpSelectTilesWithMethod(pt.x, pt.y, select_method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) override
|
||||||
|
{
|
||||||
|
if (pt.x == -1) return;
|
||||||
|
|
||||||
|
switch (select_proc) {
|
||||||
|
default: NOT_REACHED();
|
||||||
|
case DDSP_BUILD_OBJECT:
|
||||||
|
if (!_settings_game.construction.freeform_edges) {
|
||||||
|
/* When end_tile is MP_VOID, the error tile will not be visible to the
|
||||||
|
* user. This happens when terraforming at the southern border. */
|
||||||
|
if (TileX(end_tile) == MapMaxX()) end_tile += TileDiffXY(-1, 0);
|
||||||
|
if (TileY(end_tile) == MapMaxY()) end_tile += TileDiffXY(0, -1);
|
||||||
|
}
|
||||||
|
const ObjectSpec *spec = ObjectClass::Get(_selected_object_class)->GetSpec(_selected_object_index);
|
||||||
|
Command<CMD_BUILD_OBJECT_AREA>::Post(STR_ERROR_CAN_T_BUILD_OBJECT, CcPlaySound_CONSTRUCTION_OTHER,
|
||||||
|
end_tile, start_tile, spec->Index(), _selected_object_view, (_ctrl_pressed ? true : false));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlaceObjectAbort() override
|
void OnPlaceObjectAbort() override
|
||||||
|
|
|
@ -355,6 +355,8 @@ struct ConstructionSettings {
|
||||||
uint16 clear_frame_burst; ///< how many tiles may, over a short period, be cleared?
|
uint16 clear_frame_burst; ///< how many tiles may, over a short period, be cleared?
|
||||||
uint32 tree_per_64k_frames; ///< how many trees may, over a long period, be planted per 65536 frames?
|
uint32 tree_per_64k_frames; ///< how many trees may, over a long period, be planted per 65536 frames?
|
||||||
uint16 tree_frame_burst; ///< how many trees may, over a short period, be planted?
|
uint16 tree_frame_burst; ///< how many trees may, over a short period, be planted?
|
||||||
|
uint32 build_object_per_64k_frames; ///< how many tiles may, over a long period, have objects built on them per 65536 frames?
|
||||||
|
uint16 build_object_frame_burst; ///< how many tiles may, over a short period, have objects built on them?
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Settings related to the AI. */
|
/** Settings related to the AI. */
|
||||||
|
|
|
@ -415,6 +415,24 @@ max = 1 << 15
|
||||||
interval = 1
|
interval = 1
|
||||||
cat = SC_EXPERT
|
cat = SC_EXPERT
|
||||||
|
|
||||||
|
[SDT_VAR]
|
||||||
|
var = construction.build_object_per_64k_frames
|
||||||
|
type = SLE_UINT32
|
||||||
|
def = 32 << 16
|
||||||
|
min = 0
|
||||||
|
max = 1 << 30
|
||||||
|
interval = 1
|
||||||
|
cat = SC_EXPERT
|
||||||
|
|
||||||
|
[SDT_VAR]
|
||||||
|
var = construction.build_object_frame_burst
|
||||||
|
type = SLE_UINT16
|
||||||
|
def = 2048
|
||||||
|
min = 0
|
||||||
|
max = 1 << 15
|
||||||
|
interval = 1
|
||||||
|
cat = SC_EXPERT
|
||||||
|
|
||||||
[SDT_BOOL]
|
[SDT_BOOL]
|
||||||
var = construction.autoslope
|
var = construction.autoslope
|
||||||
from = SLV_75
|
from = SLV_75
|
||||||
|
|
|
@ -123,6 +123,7 @@ enum ViewportDragDropSelectionProcess {
|
||||||
DDSP_CREATE_RIVER, ///< Create rivers
|
DDSP_CREATE_RIVER, ///< Create rivers
|
||||||
DDSP_PLANT_TREES, ///< Plant trees
|
DDSP_PLANT_TREES, ///< Plant trees
|
||||||
DDSP_BUILD_BRIDGE, ///< Bridge placement
|
DDSP_BUILD_BRIDGE, ///< Bridge placement
|
||||||
|
DDSP_BUILD_OBJECT, ///< Build an object
|
||||||
|
|
||||||
/* Rail specific actions */
|
/* Rail specific actions */
|
||||||
DDSP_PLACE_RAIL, ///< Rail placement
|
DDSP_PLACE_RAIL, ///< Rail placement
|
||||||
|
|
Loading…
Reference in New Issue