mirror of
https://github.com/OpenTTD/OpenTTD
synced 2026-01-19 02:12:37 +01:00
Feature: Allow placing an area of 1x1 houses (#14708)
This commit is contained in:
@@ -299,6 +299,7 @@ enum Commands : uint8_t {
|
||||
CMD_EXPAND_TOWN, ///< expand a town
|
||||
CMD_DELETE_TOWN, ///< delete a town
|
||||
CMD_PLACE_HOUSE, ///< place a house
|
||||
CMD_PLACE_HOUSE_AREA, ///< place an area of houses
|
||||
|
||||
CMD_ORDER_REFIT, ///< change the refit information of an order (for "goto depot" )
|
||||
CMD_CLONE_ORDER, ///< clone (and share) an order
|
||||
|
||||
@@ -105,7 +105,7 @@ struct CompanyProperties {
|
||||
uint32_t terraform_limit = 0; ///< Amount of tileheights we can (still) terraform (times 65536).
|
||||
uint32_t clear_limit = 0; ///< Amount of tiles we can (still) clear (times 65536).
|
||||
uint32_t tree_limit = 0; ///< Amount of trees we can (still) plant (times 65536).
|
||||
uint32_t build_object_limit = 0; ///< Amount of tiles we can (still) build objects on (times 65536). Also applies to buying land.
|
||||
uint32_t build_object_limit = 0; ///< Amount of tiles we can (still) build objects on (times 65536). Also applies to buying land and placing houses.
|
||||
|
||||
/**
|
||||
* If \c true, the company is (also) controlled by the computer (a NoAI program).
|
||||
|
||||
@@ -3000,6 +3000,59 @@ CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, b
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct multiple houses in an area
|
||||
* @param flags Type of operation.
|
||||
* @param tile End tile of area dragging.
|
||||
* @param start_tile Start tile of area dragging.
|
||||
* @param HouseID The HouseID of the house spec.
|
||||
* @param is_protected Whether the house is protected from the town upgrading it.
|
||||
* @param replace Whether we can replace existing houses.
|
||||
* @param diagonal Whether to use the Diagonal or Orthogonal tile iterator.
|
||||
* @return Empty cost or an error.
|
||||
*/
|
||||
CommandCost CmdPlaceHouseArea(DoCommandFlags flags, TileIndex tile, TileIndex start_tile, HouseID house, bool is_protected, bool replace, bool diagonal)
|
||||
{
|
||||
if (start_tile >= Map::Size()) return CMD_ERROR;
|
||||
|
||||
if (_game_mode != GM_EDITOR && _settings_game.economy.place_houses == PH_FORBIDDEN) return CMD_ERROR;
|
||||
|
||||
if (Town::GetNumItems() == 0) return CommandCost(STR_ERROR_MUST_FOUND_TOWN_FIRST);
|
||||
|
||||
if (static_cast<size_t>(house) >= HouseSpec::Specs().size()) return CMD_ERROR;
|
||||
const HouseSpec *hs = HouseSpec::Get(house);
|
||||
if (!hs->enabled) return CMD_ERROR;
|
||||
|
||||
/* Only allow placing an area of 1x1 houses. */
|
||||
if (!hs->building_flags.Test(BuildingFlag::Size1x1)) return CMD_ERROR;
|
||||
|
||||
/* Use the built object limit to rate limit house placement. */
|
||||
const Company *c = Company::GetIfValid(_current_company);
|
||||
int limit = (c == nullptr ? INT32_MAX : GB(c->build_object_limit, 16, 16));
|
||||
|
||||
CommandCost last_error = CMD_ERROR;
|
||||
bool had_success = false;
|
||||
|
||||
std::unique_ptr<TileIterator> iter = TileIterator::Create(tile, start_tile, diagonal);
|
||||
for (; *iter != INVALID_TILE; ++(*iter)) {
|
||||
TileIndex t = *iter;
|
||||
CommandCost ret = Command<CMD_PLACE_HOUSE>::Do(DoCommandFlags{flags}.Reset(DoCommandFlag::Execute), t, house, is_protected, replace);
|
||||
|
||||
/* If we've reached the limit, stop building (or testing). */
|
||||
if (c != nullptr && limit-- <= 0) break;
|
||||
|
||||
if (ret.Failed()) {
|
||||
last_error = std::move(ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flags.Test(DoCommandFlag::Execute)) Command<CMD_PLACE_HOUSE>::Do(flags, t, house, is_protected, replace);
|
||||
had_success = true;
|
||||
}
|
||||
|
||||
return had_success ? CommandCost{} : last_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data structures when a house is removed
|
||||
* @param tile Tile of the house
|
||||
|
||||
@@ -28,6 +28,7 @@ CommandCost CmdTownSetText(DoCommandFlags flags, TownID town_id, const EncodedSt
|
||||
CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_amount, TownExpandModes modes);
|
||||
CommandCost CmdDeleteTown(DoCommandFlags flags, TownID town_id);
|
||||
CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, bool house_protected, bool replace);
|
||||
CommandCost CmdPlaceHouseArea(DoCommandFlags flags, TileIndex tile, TileIndex start_tile, HouseID house, bool is_protected, bool replace, bool diagonal);
|
||||
|
||||
DEF_CMD_TRAIT(CMD_FOUND_TOWN, CmdFoundTown, CommandFlags({CommandFlag::Deity, CommandFlag::NoTest}), CommandType::LandscapeConstruction) // founding random town can fail only in exec run
|
||||
DEF_CMD_TRAIT(CMD_RENAME_TOWN, CmdRenameTown, CommandFlags({CommandFlag::Deity, CommandFlag::Server}), CommandType::OtherManagement)
|
||||
@@ -39,6 +40,8 @@ DEF_CMD_TRAIT(CMD_TOWN_SET_TEXT, CmdTownSetText, CommandFlags({CommandFlag
|
||||
DEF_CMD_TRAIT(CMD_EXPAND_TOWN, CmdExpandTown, CommandFlags({CommandFlag::Deity}), CommandType::LandscapeConstruction)
|
||||
DEF_CMD_TRAIT(CMD_DELETE_TOWN, CmdDeleteTown, CommandFlags({CommandFlag::Offline}), CommandType::LandscapeConstruction)
|
||||
DEF_CMD_TRAIT(CMD_PLACE_HOUSE, CmdPlaceHouse, CommandFlags({CommandFlag::Deity}), CommandType::OtherManagement)
|
||||
DEF_CMD_TRAIT(CMD_PLACE_HOUSE_AREA, CmdPlaceHouseArea, CommandFlags({ CommandFlag::Deity }), CommandType::OtherManagement)
|
||||
|
||||
|
||||
CommandCallback CcFoundTown;
|
||||
void CcFoundRandomTown(Commands cmd, const CommandCost &result, Money, TownID town_id);
|
||||
|
||||
@@ -1798,7 +1798,28 @@ struct BuildHouseWindow : public PickerWindow {
|
||||
void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
|
||||
{
|
||||
const HouseSpec *spec = HouseSpec::Get(HousePickerCallbacks::sel_type);
|
||||
Command<CMD_PLACE_HOUSE>::Post(STR_ERROR_CAN_T_BUILD_HOUSE, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), BuildHouseWindow::house_protected, BuildHouseWindow::replace);
|
||||
|
||||
if (spec->building_flags.Test(BuildingFlag::Size1x1)) {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_PLACE_HOUSE);
|
||||
} else {
|
||||
Command<CMD_PLACE_HOUSE>::Post(STR_ERROR_CAN_T_BUILD_HOUSE, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), BuildHouseWindow::house_protected, BuildHouseWindow::replace);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlaceDrag(ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt) override
|
||||
{
|
||||
VpSelectTilesWithMethod(pt.x, pt.y, select_method);
|
||||
}
|
||||
|
||||
void OnPlaceMouseUp([[maybe_unused]] ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt, TileIndex start_tile, TileIndex end_tile) override
|
||||
{
|
||||
if (pt.x == -1) return;
|
||||
|
||||
assert(select_proc == DDSP_PLACE_HOUSE);
|
||||
|
||||
const HouseSpec *spec = HouseSpec::Get(HousePickerCallbacks::sel_type);
|
||||
Command<CMD_PLACE_HOUSE_AREA>::Post(STR_ERROR_CAN_T_BUILD_HOUSE, CcPlaySound_CONSTRUCTION_OTHER,
|
||||
end_tile, start_tile, spec->Index(), BuildHouseWindow::house_protected, BuildHouseWindow::replace, _ctrl_pressed);
|
||||
}
|
||||
|
||||
const IntervalTimer<TimerWindow> view_refresh_interval = {std::chrono::milliseconds(2500), [this](auto) {
|
||||
|
||||
@@ -124,6 +124,7 @@ enum ViewportDragDropSelectionProcess : uint8_t {
|
||||
DDSP_PLANT_TREES, ///< Plant trees
|
||||
DDSP_BUILD_BRIDGE, ///< Bridge placement
|
||||
DDSP_BUILD_OBJECT, ///< Build an object
|
||||
DDSP_PLACE_HOUSE, ///< Place a house
|
||||
|
||||
/* Rail specific actions */
|
||||
DDSP_PLACE_RAIL, ///< Rail placement
|
||||
|
||||
Reference in New Issue
Block a user