Add: Command to build an individual house on a specific tile.

This commit is contained in:
Peter Nelson 2024-05-14 17:29:57 +01:00 committed by Peter Nelson
parent f901722066
commit d99c1337a2
3 changed files with 88 additions and 22 deletions

View File

@ -283,6 +283,7 @@ enum Commands : uint16_t {
CMD_TOWN_SET_TEXT, ///< set the custom text of a town
CMD_EXPAND_TOWN, ///< expand a town
CMD_DELETE_TOWN, ///< delete a town
CMD_PLACE_HOUSE, ///< place a house
CMD_ORDER_REFIT, ///< change the refit information of an order (for "goto depot" )
CMD_CLONE_ORDER, ///< clone (and share) an order

View File

@ -2665,6 +2665,39 @@ static bool CheckTownBuild2x2House(TileIndex *tile, Town *t, int maxz, bool nosl
return false;
}
/**
* Build a house at this tile.
* @param t The town the house will belong to.
* @param tile The tile to try building on.
* @param hs The @a HouseSpec of the house.
* @param house The @a HouseID of the house.
* @param random_bits The random data to be associated with the house.
*/
static void BuildTownHouse(Town *t, TileIndex tile, const HouseSpec *hs, HouseID house, uint8_t random_bits)
{
/* build the house */
t->cache.num_houses++;
uint8_t construction_counter = 0;
uint8_t construction_stage = 0;
if (_generating_world || _game_mode == GM_EDITOR) {
uint32_t construction_random = Random();
construction_stage = TOWN_HOUSE_COMPLETED;
if (Chance16(1, 7)) construction_stage = GB(construction_random, 0, 2);
if (construction_stage == TOWN_HOUSE_COMPLETED) {
ChangePopulation(t, hs->population);
} else {
construction_counter = GB(construction_random, 2, 2);
}
}
MakeTownHouse(tile, t, construction_counter, construction_stage, house, random_bits);
UpdateTownRadius(t);
UpdateTownGrowthRate(t);
}
/**
* Tries to build a house at this tile.
@ -2784,31 +2817,10 @@ static bool TryBuildTownHouse(Town *t, TileIndex tile)
if (callback_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_ALLOW_CONSTRUCTION, callback_res)) continue;
}
/* build the house */
t->cache.num_houses++;
/* Special houses that there can be only one of. */
t->flags |= oneof;
uint8_t construction_counter = 0;
uint8_t construction_stage = 0;
if (_generating_world || _game_mode == GM_EDITOR) {
uint32_t construction_random = Random();
construction_stage = TOWN_HOUSE_COMPLETED;
if (Chance16(1, 7)) construction_stage = GB(construction_random, 0, 2);
if (construction_stage == TOWN_HOUSE_COMPLETED) {
ChangePopulation(t, hs->population);
} else {
construction_counter = GB(construction_random, 2, 2);
}
}
MakeTownHouse(tile, t, construction_counter, construction_stage, house, random_bits);
UpdateTownRadius(t);
UpdateTownGrowthRate(t);
BuildTownHouse(t, tile, hs, house, random_bits);
return true;
}
@ -2816,6 +2828,56 @@ static bool TryBuildTownHouse(Town *t, TileIndex tile)
return false;
}
CommandCost CmdPlaceHouse(DoCommandFlag flags, TileIndex tile, HouseID house)
{
if (_game_mode != GM_EDITOR) return CMD_ERROR;
if (Town::GetNumItems() == 0) return_cmd_error(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;
if (TimerGameCalendar::year < hs->min_year || TimerGameCalendar::year > hs->max_year) return CMD_ERROR;
Town *t = ClosestTownFromTile(tile, UINT_MAX);
/* cannot build on these slopes... */
Slope slope = GetTileSlope(tile);
if (IsSteepSlope(slope)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
/* building under a bridge? */
if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
/* can we clear the land? */
CommandCost cost = Command<CMD_LANDSCAPE_CLEAR>::Do(DC_AUTO | DC_NO_WATER, tile);
if (!cost.Succeeded()) return cost;
int maxz = GetTileMaxZ(tile);
/* Make sure there is no slope? */
bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0;
if (noslope && slope != SLOPE_FLAT) return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
TileArea ta = tile;
if (hs->building_flags & TILE_SIZE_2x2) ta.Add(TileAddXY(tile, 1, 1));
if (hs->building_flags & TILE_SIZE_2x1) ta.Add(TileAddByDiagDir(tile, DIAGDIR_SW));
if (hs->building_flags & TILE_SIZE_1x2) ta.Add(TileAddByDiagDir(tile, DIAGDIR_SE));
/* Check additonal tiles covered by this house. */
for (const TileIndex &subtile : ta) {
cost = Command<CMD_LANDSCAPE_CLEAR>::Do(DC_AUTO | DC_NO_WATER, subtile);
if (!cost.Succeeded()) return cost;
if (!CheckBuildHouseSameZ(subtile, maxz, noslope)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
}
if (flags & DC_EXEC) {
BuildTownHouse(t, tile, hs, house, Random());
}
return CommandCost();
}
/**
* Update data structures when a house is removed
* @param tile Tile of the house

View File

@ -15,6 +15,7 @@
#include "town_type.h"
enum TownAcceptanceEffect : uint8_t;
using HouseID = uint16_t;
std::tuple<CommandCost, Money, TownID> CmdFoundTown(DoCommandFlag flags, TileIndex tile, TownSize size, bool city, TownLayout layout, bool random_location, uint32_t townnameparts, const std::string &text);
CommandCost CmdRenameTown(DoCommandFlag flags, TownID town_id, const std::string &text);
@ -25,6 +26,7 @@ CommandCost CmdTownCargoGoal(DoCommandFlag flags, TownID town_id, TownAcceptance
CommandCost CmdTownSetText(DoCommandFlag flags, TownID town_id, const std::string &text);
CommandCost CmdExpandTown(DoCommandFlag flags, TownID town_id, uint32_t grow_amount);
CommandCost CmdDeleteTown(DoCommandFlag flags, TownID town_id);
CommandCost CmdPlaceHouse(DoCommandFlag flags, TileIndex tile, HouseID house);
DEF_CMD_TRAIT(CMD_FOUND_TOWN, CmdFoundTown, CMD_DEITY | CMD_NO_TEST, CMDT_LANDSCAPE_CONSTRUCTION) // founding random town can fail only in exec run
DEF_CMD_TRAIT(CMD_RENAME_TOWN, CmdRenameTown, CMD_DEITY | CMD_SERVER, CMDT_OTHER_MANAGEMENT)
@ -35,6 +37,7 @@ DEF_CMD_TRAIT(CMD_TOWN_RATING, CmdTownRating, CMD_DEITY,
DEF_CMD_TRAIT(CMD_TOWN_SET_TEXT, CmdTownSetText, CMD_DEITY | CMD_STR_CTRL, CMDT_OTHER_MANAGEMENT)
DEF_CMD_TRAIT(CMD_EXPAND_TOWN, CmdExpandTown, CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_DELETE_TOWN, CmdDeleteTown, CMD_OFFLINE, CMDT_LANDSCAPE_CONSTRUCTION)
DEF_CMD_TRAIT(CMD_PLACE_HOUSE, CmdPlaceHouse, CMD_DEITY, CMDT_OTHER_MANAGEMENT)
CommandCallback CcFoundTown;
void CcFoundRandomTown(Commands cmd, const CommandCost &result, Money, TownID town_id);