Allow overriding town settings on a per-town basis

Add a setting for whether this is allowed for non-privileged
multiplayer clients
This commit is contained in:
Jonathan G Rennison
2022-10-23 22:07:51 +01:00
parent e1defedb2a
commit a8361cd608
14 changed files with 331 additions and 30 deletions

View File

@@ -67,7 +67,7 @@ static Rect _record_house_rect;
TownPool _town_pool("Town");
INSTANTIATE_POOL_METHODS(Town)
static bool CanFollowRoad(TileIndex tile, DiagDirection dir);
static bool CanFollowRoad(const Town *t, TileIndex tile, DiagDirection dir);
TownKdtree _town_kdtree(&Kdtree_TownXYFunc);
@@ -1311,7 +1311,7 @@ static bool CanRoadContinueIntoNextTile(const Town *t, const TileIndex tile, con
/* If the next tile is a railroad track, check if towns are allowed to build level crossings.
* If level crossing are not allowed, reject the construction. Else allow DoCommand to determine if the rail track is buildable. */
if (IsTileType(next_tile, MP_RAILWAY) && !_settings_game.economy.allow_town_level_crossings) return false;
if (IsTileType(next_tile, MP_RAILWAY) && !t->GetAllowBuildLevelCrossings()) return false;
/* If a road tile can be built, the construction is allowed. */
return DoCommand(next_tile, rcmd | (rt << 4), t->index, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded();
@@ -1452,7 +1452,8 @@ static bool GrowTownWithTunnel(const Town *t, const TileIndex tile, const DiagDi
{
assert(tunnel_dir < DIAGDIR_END);
if (_settings_game.economy.town_build_tunnels == TTM_FORBIDDEN) return false;
const TownTunnelMode tunnel_mode = t->GetBuildTunnelMode();
if (tunnel_mode == TTM_FORBIDDEN) return false;
Slope slope = GetTileSlope(tile);
@@ -1467,7 +1468,7 @@ static bool GrowTownWithTunnel(const Town *t, const TileIndex tile, const DiagDi
/* There are two conditions for building tunnels: Under a mountain and under an obstruction. */
if (CanRoadContinueIntoNextTile(t, tile, tunnel_dir)) {
if (_settings_game.economy.town_build_tunnels != TTM_ALLOWED) return false;
if (tunnel_mode != TTM_ALLOWED) return false;
/* Only tunnel under a mountain if the slope is continuous for at least 4 tiles. We want tunneling to be a last resort for large hills. */
TileIndex slope_tile = tile;
@@ -1571,8 +1572,8 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
* to say that this is the last iteration. */
_grow_town_result = GROWTH_SEARCH_STOPPED;
if (!_settings_game.economy.allow_town_roads && !_generating_world) return;
if (!_settings_game.economy.allow_town_level_crossings && IsTileType(tile, MP_RAILWAY)) return;
if (!t1->GetAllowBuildRoads() && !_generating_world) return;
if (!t1->GetAllowBuildLevelCrossings() && IsTileType(tile, MP_RAILWAY)) return;
if (!MayTownModifyRoad(tile)) return;
/* Remove hills etc */
@@ -1618,7 +1619,8 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
break;
}
if (_settings_game.economy.town_max_road_slope > 0 && ((rcmd == ROAD_X) || (rcmd == ROAD_Y))) {
const uint8 max_road_slope = t1->GetBuildMaxRoadSlope();
if (max_road_slope > 0 && ((rcmd == ROAD_X) || (rcmd == ROAD_Y))) {
/* Limit consecutive sloped road tiles */
auto get_road_slope = [rcmd](TileIndex t) -> Slope {
@@ -1633,7 +1635,7 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
const int delta = TileOffsByDiagDir(ReverseDiagDir(target_dir));
bool ok = false;
TileIndex t = tile;
for (uint i = 0; i < _settings_game.economy.town_max_road_slope; i++) {
for (uint i = 0; i < max_road_slope; i++) {
t += delta;
if (!IsValidTile(t) || !IsNormalRoadTile(t) || GetRoadBits(t, RTT_ROAD) != rcmd || get_road_slope(t) != slope) {
ok = true;
@@ -1653,7 +1655,7 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
* the fitting RoadBits */
_grow_town_result = GROWTH_SEARCH_STOPPED;
if (!_settings_game.economy.allow_town_roads && !_generating_world) return;
if (!t1->GetAllowBuildRoads() && !_generating_world) return;
switch (t1->layout) {
default: NOT_REACHED();
@@ -1691,7 +1693,7 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
target_bits = DiagDirToRoadBits(target_dir);
} while (!(cur_rb & target_bits));
cur_rb &= ~target_bits;
} while (!(target_dir == GetTunnelBridgeDirection(tile) || CanFollowRoad(tile, target_dir)));
} while (!(target_dir == GetTunnelBridgeDirection(tile) || CanFollowRoad(t1, tile, target_dir)));
if (target_dir == GetTunnelBridgeDirection(tile)) {
/* cross the bridge */
*tile_ptr = GetOtherTunnelBridgeEnd(tile);
@@ -1746,7 +1748,7 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
if (!IsValidTile(house_tile)) return;
if (target_dir != DIAGDIR_END && (_settings_game.economy.allow_town_roads || _generating_world)) {
if (target_dir != DIAGDIR_END && (t1->GetAllowBuildRoads() || _generating_world)) {
switch (t1->layout) {
default: NOT_REACHED();
@@ -1811,18 +1813,19 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
/**
* Checks whether a road can be followed or is a dead end, that can not be extended to the next tile.
* This only checks trivial but often cases.
* @param t Town doing the following
* @param tile Start tile for road.
* @param dir Direction for road to follow or build.
* @return true If road is or can be connected in the specified direction.
*/
static bool CanFollowRoad(TileIndex tile, DiagDirection dir)
static bool CanFollowRoad(const Town *t, TileIndex tile, DiagDirection dir)
{
TileIndex target_tile = tile + TileOffsByDiagDir(dir);
if (!IsValidTile(target_tile)) return false;
if (HasTileWaterGround(target_tile)) return false;
RoadBits target_rb = GetTownRoadBits(target_tile);
if (_settings_game.economy.allow_town_roads || _generating_world) {
if (t->GetAllowBuildRoads() || _generating_world) {
/* Check whether a road connection exists or can be build. */
switch (GetTileType(target_tile)) {
case MP_ROAD:
@@ -1922,7 +1925,7 @@ static bool GrowTownAtRoad(Town *t, TileIndex tile)
target_bits = DiagDirToRoadBits(target_dir);
} while (!(cur_rb & target_bits));
cur_rb &= ~target_bits;
} while (!CanFollowRoad(tile, target_dir));
} while (!CanFollowRoad(t, tile, target_dir));
}
tile = TileAddByDiagDir(tile, target_dir);
@@ -2001,7 +2004,7 @@ static bool GrowTown(Town *t)
/* No road available, try to build a random road block by
* clearing some land and then building a road there. */
if (_settings_game.economy.allow_town_roads || _generating_world) {
if (t->GetAllowBuildRoads() || _generating_world) {
tile = t->xy;
for (ptr = _town_coord_mod; ptr != endof(_town_coord_mod); ++ptr) {
/* Only work with plain land that not already has a house */
@@ -2789,7 +2792,7 @@ static inline CommandCost IsAnotherHouseTypeAllowedInTown(Town *t, HouseID house
static inline bool TownLayoutAllowsHouseHere(Town *t, const TileArea &ta)
{
/* Allow towns everywhere when we don't build roads */
if (!_settings_game.economy.allow_town_roads && !_generating_world) return true;
if (!t->GetAllowBuildRoads() && !_generating_world) return true;
TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, ta.tile);
@@ -3792,6 +3795,67 @@ CommandCost CmdDoTownAction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
return cost;
}
/**
* Override a town setting
* @param tile unused
* @param flags type of operation
* @param p1 town to do the action at
* @param p2 various bitstuffed elements
* - p2 = (bit 0 - 7) - what setting to change
* - p2 = (bit 8 - 15) - the data to modify
* - p2 = (bit 16) - whether to override the value, or use the default
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdOverrideTownSetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
if (_networking && !_settings_game.difficulty.override_town_settings_in_multiplayer) return CMD_ERROR;
Town *t = Town::GetIfValid(p1);
if (t == nullptr) return CMD_ERROR;
const uint8 setting = GB(p2, 0, 8);
const bool is_override = HasBit(p2, 16);
const uint8 value = GB(p2, 8, 8);
switch (setting) {
case TSOF_OVERRIDE_BUILD_ROADS:
case TSOF_OVERRIDE_BUILD_LEVEL_CROSSINGS:
if (is_override && value != 0 && value != 1) return CMD_ERROR;
break;
case TSOF_OVERRIDE_BUILD_TUNNELS:
if (is_override && value >= TTM_END) return CMD_ERROR;
break;
case TSOF_OVERRIDE_BUILD_INCLINED_ROADS:
if (is_override && value > 8) return CMD_ERROR;
break;
default:
return CMD_ERROR;
}
if (flags & DC_EXEC) {
SB(t->override_flags, setting, 1, is_override ? 1 : 0);
if (is_override) {
switch (setting) {
case TSOF_OVERRIDE_BUILD_ROADS:
case TSOF_OVERRIDE_BUILD_LEVEL_CROSSINGS:
SB(t->override_values, setting, 1, value & 1);
break;
case TSOF_OVERRIDE_BUILD_TUNNELS:
t->build_tunnels = (TownTunnelMode)value;
break;
case TSOF_OVERRIDE_BUILD_INCLINED_ROADS:
t->max_road_slope = value;
break;
default:
NOT_REACHED();
}
}
SetWindowDirty(WC_TOWN_AUTHORITY, p1);
}
return CommandCost();
}
template <typename Func>
static void ForAllStationsNearTown(Town *t, Func func)
{