1
0
Fork 0

Feature: House placer mode to overbuild existing houses

Tyler Trahan 2025-07-20 19:09:44 -04:00
parent 7f792e9c5f
commit 632029e277
5 changed files with 49 additions and 27 deletions

View File

@ -2862,6 +2862,9 @@ STR_HOUSE_PICKER_PROTECT_TOOLTIP :Choose whether
STR_HOUSE_PICKER_PROTECT_OFF :Off STR_HOUSE_PICKER_PROTECT_OFF :Off
STR_HOUSE_PICKER_PROTECT_ON :On STR_HOUSE_PICKER_PROTECT_ON :On
STR_HOUSE_PICKER_OVERBUILD :Overbuild existing
STR_HOUSE_PICKER_OVERBUILD_TOOLTIP :Choose whether to automatically remove an existing house on the tile where this house is placed
STR_STATION_CLASS_DFLT :Default STR_STATION_CLASS_DFLT :Default
STR_STATION_CLASS_DFLT_STATION :Default station STR_STATION_CLASS_DFLT_STATION :Default station
STR_STATION_CLASS_DFLT_ROADSTOP :Default road stop STR_STATION_CLASS_DFLT_ROADSTOP :Default road stop

View File

@ -2902,9 +2902,10 @@ static bool TryBuildTownHouse(Town *t, TileIndex tile, TownExpandModes modes)
* @param tile Tile on which to place the house. * @param tile Tile on which to place the house.
* @param HouseID The HouseID of the house spec. * @param HouseID The HouseID of the house spec.
* @param is_protected Whether the house is protected from the town upgrading it. * @param is_protected Whether the house is protected from the town upgrading it.
* @param overbuild Whether to automatically demolish an existing house on this tile, if present.
* @return Empty cost or an error. * @return Empty cost or an error.
*/ */
CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, bool is_protected) CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, bool is_protected, bool overbuild)
{ {
if (_game_mode != GM_EDITOR && _settings_game.economy.place_houses == PH_FORBIDDEN) return CMD_ERROR; if (_game_mode != GM_EDITOR && _settings_game.economy.place_houses == PH_FORBIDDEN) return CMD_ERROR;
@ -2914,39 +2915,45 @@ CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, b
const HouseSpec *hs = HouseSpec::Get(house); const HouseSpec *hs = HouseSpec::Get(house);
if (!hs->enabled) return CMD_ERROR; if (!hs->enabled) return CMD_ERROR;
Town *t = ClosestTownFromTile(tile, UINT_MAX);
/* cannot build on these slopes... */
Slope slope = GetTileSlope(tile);
if (IsSteepSlope(slope)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
/* building under a bridge? */
if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
/* can we clear the land? */
CommandCost cost = Command<CMD_LANDSCAPE_CLEAR>::Do({DoCommandFlag::Auto, DoCommandFlag::NoWater}, tile);
if (!cost.Succeeded()) return cost;
int maxz = GetTileMaxZ(tile); int maxz = GetTileMaxZ(tile);
/* Make sure there is no slope? */ /* Check each tile of a multi-tile house. */
bool noslope = hs->building_flags.Test(BuildingFlag::NotSloped); TileArea ta(tile, 1, 1);
if (noslope && slope != SLOPE_FLAT) return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
TileArea ta = tile;
if (hs->building_flags.Test(BuildingFlag::Size2x2)) ta.Add(TileAddXY(tile, 1, 1)); if (hs->building_flags.Test(BuildingFlag::Size2x2)) ta.Add(TileAddXY(tile, 1, 1));
if (hs->building_flags.Test(BuildingFlag::Size2x1)) ta.Add(TileAddByDiagDir(tile, DIAGDIR_SW)); if (hs->building_flags.Test(BuildingFlag::Size2x1)) ta.Add(TileAddByDiagDir(tile, DIAGDIR_SW));
if (hs->building_flags.Test(BuildingFlag::Size1x2)) ta.Add(TileAddByDiagDir(tile, DIAGDIR_SE)); if (hs->building_flags.Test(BuildingFlag::Size1x2)) ta.Add(TileAddByDiagDir(tile, DIAGDIR_SE));
/* Check additional tiles covered by this house. */ for (const TileIndex subtile : ta) {
for (const TileIndex &subtile : ta) { /* Houses cannot be built on steep slopes. */
cost = Command<CMD_LANDSCAPE_CLEAR>::Do({DoCommandFlag::Auto, DoCommandFlag::NoWater}, subtile); Slope slope = GetTileSlope(subtile);
if (!cost.Succeeded()) return cost; if (IsSteepSlope(slope)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
if (!CheckBuildHouseSameZ(subtile, maxz, noslope)) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); /* Houses cannot be built under bridges. */
if (IsBridgeAbove(subtile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
/* Make sure there is no slope? */
bool noslope = hs->building_flags.Test(BuildingFlag::NotSloped);
if (noslope && slope != SLOPE_FLAT) return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
/* All tiles of a multi-tile house must have the same z-level. */
if (GetTileMaxZ(subtile) != maxz) return CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
/* We might be overbuilding an existing house, otherwise check if we can clear land. */
if (!(overbuild && GetTileType(subtile) == MP_HOUSE)) {
CommandCost cost = Command<CMD_LANDSCAPE_CLEAR>::Do({DoCommandFlag::Auto, DoCommandFlag::NoWater}, subtile);
if (!cost.Succeeded()) return cost;
}
} }
if (flags.Test(DoCommandFlag::Execute)) { if (flags.Test(DoCommandFlag::Execute)) {
/* If overbuilding, clear any existing houses first. */
if (overbuild) {
for (const TileIndex &subtile : ta) {
if (GetTileType(subtile) == MP_HOUSE) ClearTownHouse(Town::GetByTile(subtile), subtile);
}
}
Town *t = ClosestTownFromTile(tile, UINT_MAX);
bool house_completed = _settings_game.economy.place_houses == PH_ALLOWED_CONSTRUCTED; bool house_completed = _settings_game.economy.place_houses == PH_ALLOWED_CONSTRUCTED;
BuildTownHouse(t, tile, hs, house, Random(), house_completed, is_protected); BuildTownHouse(t, tile, hs, house, Random(), house_completed, is_protected);
} }

View File

@ -27,7 +27,7 @@ CommandCost CmdTownCargoGoal(DoCommandFlags flags, TownID town_id, TownAcceptanc
CommandCost CmdTownSetText(DoCommandFlags flags, TownID town_id, const EncodedString &text); CommandCost CmdTownSetText(DoCommandFlags flags, TownID town_id, const EncodedString &text);
CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_amount, TownExpandModes modes); CommandCost CmdExpandTown(DoCommandFlags flags, TownID town_id, uint32_t grow_amount, TownExpandModes modes);
CommandCost CmdDeleteTown(DoCommandFlags flags, TownID town_id); CommandCost CmdDeleteTown(DoCommandFlags flags, TownID town_id);
CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, bool house_protected); CommandCost CmdPlaceHouse(DoCommandFlags flags, TileIndex tile, HouseID house, bool house_protected, bool overbuild);
DEF_CMD_TRAIT(CMD_FOUND_TOWN, CmdFoundTown, CommandFlags({CommandFlag::Deity, CommandFlag::NoTest}), CMDT_LANDSCAPE_CONSTRUCTION) // founding random town can fail only in exec run DEF_CMD_TRAIT(CMD_FOUND_TOWN, CmdFoundTown, CommandFlags({CommandFlag::Deity, CommandFlag::NoTest}), CMDT_LANDSCAPE_CONSTRUCTION) // founding random town can fail only in exec run
DEF_CMD_TRAIT(CMD_RENAME_TOWN, CmdRenameTown, CommandFlags({CommandFlag::Deity, CommandFlag::Server}), CMDT_OTHER_MANAGEMENT) DEF_CMD_TRAIT(CMD_RENAME_TOWN, CmdRenameTown, CommandFlags({CommandFlag::Deity, CommandFlag::Server}), CMDT_OTHER_MANAGEMENT)

View File

@ -1629,6 +1629,7 @@ static CargoTypes GetProducedCargoOfHouse(const HouseSpec *hs)
struct BuildHouseWindow : public PickerWindow { struct BuildHouseWindow : public PickerWindow {
std::string house_info{}; std::string house_info{};
static inline bool house_protected; static inline bool house_protected;
static inline bool overbuild;
BuildHouseWindow(WindowDesc &desc, Window *parent) : PickerWindow(desc, parent, 0, HousePickerCallbacks::instance) BuildHouseWindow(WindowDesc &desc, Window *parent) : PickerWindow(desc, parent, 0, HousePickerCallbacks::instance)
{ {
@ -1743,6 +1744,14 @@ struct BuildHouseWindow : public PickerWindow {
this->SetDirty(); this->SetDirty();
break; break;
case WID_BH_OVERBUILD_TOGGLE:
BuildHouseWindow::overbuild = !BuildHouseWindow::overbuild;
this->SetWidgetLoweredState(WID_BH_OVERBUILD_TOGGLE, BuildHouseWindow::overbuild);
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
this->SetDirty();
break;
default: default:
this->PickerWindow::OnClick(pt, widget, click_count); this->PickerWindow::OnClick(pt, widget, click_count);
break; break;
@ -1768,6 +1777,8 @@ struct BuildHouseWindow : public PickerWindow {
this->SetWidgetLoweredState(WID_BH_PROTECT_OFF, !BuildHouseWindow::house_protected); this->SetWidgetLoweredState(WID_BH_PROTECT_OFF, !BuildHouseWindow::house_protected);
this->SetWidgetLoweredState(WID_BH_PROTECT_ON, BuildHouseWindow::house_protected); this->SetWidgetLoweredState(WID_BH_PROTECT_ON, BuildHouseWindow::house_protected);
this->SetWidgetLoweredState(WID_BH_OVERBUILD_TOGGLE, BuildHouseWindow::overbuild);
this->SetWidgetDisabledState(WID_BH_PROTECT_OFF, hasflag); this->SetWidgetDisabledState(WID_BH_PROTECT_OFF, hasflag);
this->SetWidgetDisabledState(WID_BH_PROTECT_ON, hasflag); this->SetWidgetDisabledState(WID_BH_PROTECT_ON, hasflag);
@ -1776,7 +1787,7 @@ struct BuildHouseWindow : public PickerWindow {
void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override void OnPlaceObject([[maybe_unused]] Point pt, TileIndex tile) override
{ {
const HouseSpec *spec = HouseSpec::Get(HousePickerCallbacks::sel_type); 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); Command<CMD_PLACE_HOUSE>::Post(STR_ERROR_CAN_T_BUILD_HOUSE, CcPlaySound_CONSTRUCTION_OTHER, tile, spec->Index(), BuildHouseWindow::house_protected, BuildHouseWindow::overbuild);
} }
const IntervalTimer<TimerWindow> view_refresh_interval = {std::chrono::milliseconds(2500), [this](auto) { const IntervalTimer<TimerWindow> view_refresh_interval = {std::chrono::milliseconds(2500), [this](auto) {
@ -1811,9 +1822,9 @@ static constexpr NWidgetPart _nested_build_house_widgets[] = {
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BH_PROTECT_OFF), SetMinimalSize(60, 12), SetStringTip(STR_HOUSE_PICKER_PROTECT_OFF, STR_HOUSE_PICKER_PROTECT_TOOLTIP), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BH_PROTECT_OFF), SetMinimalSize(60, 12), SetStringTip(STR_HOUSE_PICKER_PROTECT_OFF, STR_HOUSE_PICKER_PROTECT_TOOLTIP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BH_PROTECT_ON), SetMinimalSize(60, 12), SetStringTip(STR_HOUSE_PICKER_PROTECT_ON, STR_HOUSE_PICKER_PROTECT_TOOLTIP), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BH_PROTECT_ON), SetMinimalSize(60, 12), SetStringTip(STR_HOUSE_PICKER_PROTECT_ON, STR_HOUSE_PICKER_PROTECT_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BH_OVERBUILD_TOGGLE), SetMinimalSize(60, 12), SetStringTip(STR_HOUSE_PICKER_OVERBUILD, STR_HOUSE_PICKER_OVERBUILD_TOOLTIP),
EndContainer(), EndContainer(),
EndContainer(), EndContainer(),
EndContainer(), EndContainer(),
NWidgetFunction(MakePickerTypeWidgets), NWidgetFunction(MakePickerTypeWidgets),
EndContainer(), EndContainer(),

View File

@ -79,6 +79,7 @@ enum BuildHouseWidgets : WidgetID {
WID_BH_INFO, ///< Information panel of selected house. WID_BH_INFO, ///< Information panel of selected house.
WID_BH_PROTECT_OFF, ///< Button to protect the next house built. WID_BH_PROTECT_OFF, ///< Button to protect the next house built.
WID_BH_PROTECT_ON, ///< Button to not protect the next house built. WID_BH_PROTECT_ON, ///< Button to not protect the next house built.
WID_BH_OVERBUILD_TOGGLE, ///< Button to toggle overbuilding existing houses.
}; };
#endif /* WIDGETS_TOWN_WIDGET_H */ #endif /* WIDGETS_TOWN_WIDGET_H */