Update to 1.10.0-beta1

This commit is contained in:
dP
2019-10-31 22:24:28 +03:00
parent b84a475e14
commit 599ccf0c2b
1470 changed files with 354219 additions and 16795 deletions

View File

@@ -10,14 +10,17 @@
/** @file town_cmd.cpp Handling of town tiles. */
#include "stdafx.h"
#include "road.h"
#include "road_internal.h" /* Cleaning up road bits */
#include "road_cmd.h"
#include "landscape.h"
#include "viewport_func.h"
#include "viewport_kdtree.h"
#include "cmd_helper.h"
#include "command_func.h"
#include "industry.h"
#include "station_base.h"
#include "station_kdtree.h"
#include "company_base.h"
#include "news_func.h"
#include "error.h"
@@ -38,6 +41,7 @@
#include "subsidy_func.h"
#include "core/pool_func.hpp"
#include "town.h"
#include "town_kdtree.h"
#include "townname_func.h"
#include "core/random_func.hpp"
#include "core/backup_type.hpp"
@@ -59,6 +63,45 @@ CargoTypes _town_cargoes_accepted; ///< Bitmap of all cargoes accepted by houses
TownPool _town_pool("Town");
INSTANTIATE_POOL_METHODS(Town)
TownKdtree _town_kdtree(&Kdtree_TownXYFunc);
void RebuildTownKdtree()
{
std::vector<TownID> townids;
Town *town;
FOR_ALL_TOWNS(town) {
townids.push_back(town->index);
}
_town_kdtree.Build(townids.begin(), townids.end());
}
/**
* Check if a town 'owns' a bridge.
* Bridges to not directly have an owner, so we check the tiles adjacent to the bridge ends.
* If either adjacent tile belongs to the town then it will be assumed that the town built
* the bridge.
* @param tile Bridge tile to test
* @param t Town we are interested in
* @return true if town 'owns' a bridge.
*/
static bool TestTownOwnsBridge(TileIndex tile, const Town *t)
{
if (!IsTileOwner(tile, OWNER_TOWN)) return false;
TileIndex adjacent = tile + TileOffsByDiagDir(ReverseDiagDir(GetTunnelBridgeDirection(tile)));
bool town_owned = IsTileType(adjacent, MP_ROAD) && IsTileOwner(adjacent, OWNER_TOWN) && GetTownIndex(adjacent) == t->index;
if (!town_owned) {
/* Or other adjacent road */
TileIndex adjacent = tile + TileOffsByDiagDir(ReverseDiagDir(GetTunnelBridgeDirection(GetOtherTunnelBridgeEnd(tile))));
town_owned = IsTileType(adjacent, MP_ROAD) && IsTileOwner(adjacent, OWNER_TOWN) && GetTownIndex(adjacent) == t->index;
}
return town_owned;
}
Town::~Town()
{
free(this->name);
@@ -90,7 +133,7 @@ Town::~Town()
break;
case MP_TUNNELBRIDGE:
assert(!IsTileOwner(tile, OWNER_TOWN) || ClosestTownFromTile(tile, UINT_MAX) != this);
assert(!TestTownOwnsBridge(tile, this));
break;
default:
@@ -121,7 +164,7 @@ void Town::PostDestructor(size_t index)
/* Give objects a new home! */
Object *o;
FOR_ALL_OBJECTS(o) {
if (o->town == NULL) o->town = CalcClosestTownFromTile(o->location.tile, UINT_MAX);
if (o->town == nullptr) o->town = CalcClosestTownFromTile(o->location.tile, UINT_MAX);
}
}
@@ -135,16 +178,16 @@ void Town::InitializeLayout(TownLayout layout)
return;
}
this->layout = TileHash(TileX(this->xy), TileY(this->xy)) % (NUM_TLS - 1);
this->layout = static_cast<TownLayout>(TileHash(TileX(this->xy), TileY(this->xy)) % (NUM_TLS - 1));
}
/**
* Return a random valid town.
* @return random town, NULL if there are no towns
* @return random town, nullptr if there are no towns
*/
/* static */ Town *Town::GetRandom()
{
if (Town::GetNumItems() == 0) return NULL;
if (Town::GetNumItems() == 0) return nullptr;
int num = RandomRange((uint16)Town::GetNumItems());
size_t index = MAX_UVALUE(size_t);
@@ -217,7 +260,7 @@ static void DrawTile_Town(TileInfo *ti)
/* Houses don't necessarily need new graphics. If they don't have a
* spritegroup associated with them, then the sprite for the substitute
* house id is drawn instead. */
if (HouseSpec::Get(house_id)->grf_prop.spritegroup[0] != NULL) {
if (HouseSpec::Get(house_id)->grf_prop.spritegroup[0] != nullptr) {
DrawNewHouseTile(ti, house_id);
return;
} else {
@@ -274,7 +317,7 @@ static Foundation GetFoundation_Town(TileIndex tile, Slope tileh)
*/
if (hid >= NEW_HOUSE_OFFSET) {
const HouseSpec *hs = HouseSpec::Get(hid);
if (hs->grf_prop.spritegroup[0] != NULL && HasBit(hs->callback_mask, CBM_HOUSE_DRAW_FOUNDATIONS)) {
if (hs->grf_prop.spritegroup[0] != nullptr && HasBit(hs->callback_mask, CBM_HOUSE_DRAW_FOUNDATIONS)) {
uint32 callback_res = GetHouseCallback(CBID_HOUSE_DRAW_FOUNDATIONS, 0, 0, hid, Town::GetByTile(tile), tile);
if (callback_res != CALLBACK_FAILED && !ConvertBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DRAW_FOUNDATIONS, callback_res)) return FOUNDATION_NONE;
}
@@ -341,30 +384,9 @@ static void AnimateTile_Town(TileIndex tile)
*/
static bool IsCloseToTown(TileIndex tile, uint dist)
{
/* On a large map with many towns, it may be faster to check the surroundings of the tile.
* An iteration in TILE_AREA_LOOP() is generally 2 times faster than one in FOR_ALL_TOWNS(). */
if (Town::GetNumItems() > (size_t) (dist * dist * 2)) {
const int tx = TileX(tile);
const int ty = TileY(tile);
TileArea tile_area = TileArea(
TileXY(max(0, tx - (int) dist), max(0, ty - (int) dist)),
TileXY(min(MapMaxX(), tx + (int) dist), min(MapMaxY(), ty + (int) dist))
);
TILE_AREA_LOOP(atile, tile_area) {
if (GetTileType(atile) == MP_HOUSE) {
Town *t = Town::GetByTile(atile);
if (DistanceManhattan(tile, t->xy) < dist) return true;
}
}
return false;
}
const Town *t;
FOR_ALL_TOWNS(t) {
if (DistanceManhattan(tile, t->xy) < dist) return true;
}
return false;
if (_town_kdtree.Count() == 0) return false;
Town *t = Town::Get(_town_kdtree.FindNearest(TileX(tile), TileY(tile)));
return DistanceManhattan(tile, t->xy) < dist;
}
/**
@@ -402,7 +424,7 @@ static void ChangePopulation(Town *t, int mod)
{
t->cache.population += mod;
InvalidateWindowData(WC_TOWN_VIEW, t->index); // Cargo requirements may appear/vanish for small populations
t->UpdateVirtCoord();
if (_settings_client.gui.population_in_label) t->UpdateVirtCoord();
InvalidateWindowData(WC_TOWN_DIRECTORY, 0, 1);
}
@@ -421,6 +443,22 @@ uint32 GetWorldPopulation()
return pop;
}
/**
* Remove stations from nearby station list if a town is no longer in the catchment area of each.
* @param t Town to work on
*/
static void RemoveNearbyStations(Town *t)
{
for (StationList::iterator it = t->stations_near.begin(); it != t->stations_near.end(); /* incremented inside loop */) {
const Station *st = *it;
if (!st->CatchmentCoversTown(t->index)) {
it = t->stations_near.erase(it);
} else {
++it;
}
}
}
/**
* Helper function for house completion stages progression
* @param tile TileIndex of the house (or parts of it) to "grow"
@@ -511,24 +549,58 @@ static void TileLoop_Town(TileIndex tile)
t->supplied[cs->Index()].new_act += moved;
}
} else {
if (GB(r, 0, 8) < hs->population) {
uint amt = GB(r, 0, 8) / 8 + 1;
switch (_settings_game.economy.town_cargogen_mode) {
case TCGM_ORIGINAL:
/* Original (quadratic) cargo generation algorithm */
if (GB(r, 0, 8) < hs->population) {
uint amt = GB(r, 0, 8) / 8 + 1;
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_PASSENGERS].new_max += amt;
t->supplied[CT_PASSENGERS].new_act += MoveGoodsToStation(CT_PASSENGERS, amt, ST_TOWN, t->index, stations.GetStations());
}
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_PASSENGERS].new_max += amt;
t->supplied[CT_PASSENGERS].new_act += MoveGoodsToStation(CT_PASSENGERS, amt, ST_TOWN, t->index, stations.GetStations());
}
if (GB(r, 8, 8) < hs->mail_generation) {
uint amt = GB(r, 8, 8) / 8 + 1;
if (GB(r, 8, 8) < hs->mail_generation) {
uint amt = GB(r, 8, 8) / 8 + 1;
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_MAIL].new_max += amt;
t->supplied[CT_MAIL].new_act += MoveGoodsToStation(CT_MAIL, amt, ST_TOWN, t->index, stations.GetStations());
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_MAIL].new_max += amt;
t->supplied[CT_MAIL].new_act += MoveGoodsToStation(CT_MAIL, amt, ST_TOWN, t->index, stations.GetStations());
}
break;
case TCGM_BITCOUNT:
/* Binomial distribution per tick, by a series of coin flips */
/* Reduce generation rate to a 1/4, using tile bits to spread out distribution.
* As tick counter is incremented by 256 between each call, we ignore the lower 8 bits. */
if (GB(_tick_counter, 8, 2) == GB(tile, 0, 2)) {
/* Make a bitmask with up to 32 bits set, one for each potential pax */
int genmax = (hs->population + 7) / 8;
uint32 genmask = (genmax >= 32) ? 0xFFFFFFFF : ((1 << genmax) - 1);
/* Mask random value by potential pax and count number of actual pax */
uint amt = CountBits(r & genmask);
/* Adjust and apply */
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_PASSENGERS].new_max += amt;
t->supplied[CT_PASSENGERS].new_act += MoveGoodsToStation(CT_PASSENGERS, amt, ST_TOWN, t->index, stations.GetStations());
/* Do the same for mail, with a fresh random */
r = Random();
genmax = (hs->mail_generation + 7) / 8;
genmask = (genmax >= 32) ? 0xFFFFFFFF : ((1 << genmax) - 1);
amt = CountBits(r & genmask);
if (EconomyIsInRecession()) amt = (amt + 1) >> 1;
t->supplied[CT_MAIL].new_max += amt;
t->supplied[CT_MAIL].new_act += MoveGoodsToStation(CT_MAIL, amt, ST_TOWN, t->index, stations.GetStations());
}
break;
default:
NOT_REACHED();
}
}
Backup<CompanyByte> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
Backup<CompanyID> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
if ((hs->building_flags & BUILDING_HAS_1_TILE) &&
HasBit(t->flags, TOWN_IS_GROWING) &&
@@ -540,7 +612,11 @@ static void TileLoop_Town(TileIndex tile)
ClearTownHouse(t, tile);
/* Rebuild with another house? */
if (GB(r, 24, 8) >= 12) BuildTownHouse(t, tile);
if (GB(r, 24, 8) < 12 || !BuildTownHouse(t, tile))
{
/* House wasn't replaced, so remove it */
if (!_generating_world) RemoveNearbyStations(t);
}
}
cur_company.Restore();
@@ -569,6 +645,7 @@ static CommandCost ClearTile_Town(TileIndex tile, DoCommandFlag flags)
ChangeTownRating(t, -rating, RATING_HOUSE_MINIMUM, flags);
if (flags & DC_EXEC) {
ClearTownHouse(t, tile);
RemoveNearbyStations(t);
}
return cost;
@@ -676,7 +753,7 @@ static void GetTileDesc_Town(TileIndex tile, TileDesc *td)
td->str = STR_LAI_TOWN_INDUSTRY_DESCRIPTION_UNDER_CONSTRUCTION;
}
if (hs->grf_prop.grffile != NULL) {
if (hs->grf_prop.grffile != nullptr) {
const GRFConfig *gc = GetGRFConfig(hs->grf_prop.grffile->grfid);
td->grf = gc->GetName();
}
@@ -815,7 +892,38 @@ static RoadBits GetTownRoadBits(TileIndex tile)
{
if (IsRoadDepotTile(tile) || IsStandardRoadStopTile(tile)) return ROAD_NONE;
return GetAnyRoadBits(tile, ROADTYPE_ROAD, true);
return GetAnyRoadBits(tile, RTT_ROAD, true);
}
RoadType GetTownRoadType(const Town *t)
{
RoadType best_rt = ROADTYPE_ROAD;
const RoadTypeInfo *best = nullptr;
const uint16 assume_max_speed = 50;
for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) {
if (RoadTypeIsTram(rt)) continue;
const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
/* Unused road type. */
if (rti->label == 0) continue;
/* Can town build this road. */
if (!HasBit(rti->flags, ROTF_TOWN_BUILD)) continue;
/* Not yet introduced at this date. */
if (IsInsideMM(rti->introduction_date, 0, MAX_DAY) && rti->introduction_date > _date) continue;
if (best != nullptr) {
if ((rti->max_speed == 0 ? assume_max_speed : rti->max_speed) < (best->max_speed == 0 ? assume_max_speed : best->max_speed)) continue;
}
best_rt = rt;
best = rti;
}
return best_rt;
}
/**
@@ -874,7 +982,8 @@ static bool IsRoadAllowedHere(Town *t, TileIndex tile, DiagDirection dir)
/* No, try if we are able to build a road piece there.
* If that fails clear the land, and if that fails exit.
* This is to make sure that we can build a road here later. */
if (DoCommand(tile, ((dir == DIAGDIR_NW || dir == DIAGDIR_SE) ? ROAD_Y : ROAD_X), 0, DC_AUTO, CMD_BUILD_ROAD).Failed() &&
RoadType rt = GetTownRoadType(t);
if (DoCommand(tile, ((dir == DIAGDIR_NW || dir == DIAGDIR_SE) ? ROAD_Y : ROAD_X) | (rt << 4), 0, DC_AUTO, CMD_BUILD_ROAD).Failed() &&
DoCommand(tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR).Failed()) {
return false;
}
@@ -1042,7 +1151,8 @@ static bool GrowTownWithExtraHouse(Town *t, TileIndex tile)
*/
static bool GrowTownWithRoad(const Town *t, TileIndex tile, RoadBits rcmd)
{
if (DoCommand(tile, rcmd, t->index, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded()) {
RoadType rt = GetTownRoadType(t);
if (DoCommand(tile, rcmd | (rt << 4), t->index, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD).Succeeded()) {
_grow_town_result = GROWTH_SUCCEED;
return true;
}
@@ -1105,8 +1215,9 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi
byte bridge_type = RandomRange(MAX_BRIDGES - 1);
/* Can we actually build the bridge? */
if (DoCommand(tile, bridge_tile, bridge_type | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE).Succeeded()) {
DoCommand(tile, bridge_tile, bridge_type | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE);
RoadType rt = GetTownRoadType(t);
if (DoCommand(tile, bridge_tile, bridge_type | rt << 8 | TRANSPORT_ROAD << 15, CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE).Succeeded()) {
DoCommand(tile, bridge_tile, bridge_type | rt << 8 | TRANSPORT_ROAD << 15, DC_EXEC | CommandFlagsToDCFlags(GetCommandFlags(CMD_BUILD_BRIDGE)), CMD_BUILD_BRIDGE);
_grow_town_result = GROWTH_SUCCEED;
return true;
}
@@ -1115,6 +1226,37 @@ static bool GrowTownWithBridge(const Town *t, const TileIndex tile, const DiagDi
return false;
}
/**
* Checks whether at least one surrounding roads allows to build a house here
*
* @param t the tile where the house will be built
* @return true if at least one surrounding roadtype allows building houses here
*/
static inline bool RoadTypesAllowHouseHere(TileIndex t)
{
static const TileIndexDiffC tiles[] = { {-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1} };
bool allow = false;
for (const TileIndexDiffC *ptr = tiles; ptr != endof(tiles); ++ptr) {
TileIndex cur_tile = t + ToTileIndexDiff(*ptr);
if (!IsValidTile(cur_tile)) continue;
if (!(IsTileType(cur_tile, MP_ROAD) || IsTileType(cur_tile, MP_STATION))) continue;
allow = true;
RoadType road_rt = GetRoadTypeRoad(cur_tile);
RoadType tram_rt = GetRoadTypeTram(cur_tile);
if (road_rt != INVALID_ROADTYPE && !HasBit(GetRoadTypeInfo(road_rt)->flags, ROTF_NO_HOUSES)) return true;
if (tram_rt != INVALID_ROADTYPE && !HasBit(GetRoadTypeInfo(tram_rt)->flags, ROTF_NO_HOUSES)) return true;
}
/* If no road was found surrounding the tile we can allow building the house since there is
* nothing which forbids it, if a road was found but the execution reached this point, then
* all the found roads don't allow houses to be built */
return !allow;
}
/**
* Grows the given town.
* There are at the moment 3 possible way's for
@@ -1293,6 +1435,8 @@ static void GrowTownInTile(TileIndex *tile_ptr, RoadBits cur_rb, DiagDirection t
}
}
allow_house &= RoadTypesAllowHouseHere(house_tile);
if (allow_house) {
/* Build a house, but not if there already is a house there. */
if (!IsTileType(house_tile, MP_HOUSE)) {
@@ -1432,14 +1576,14 @@ static bool GrowTownAtRoad(Town *t, TileIndex tile)
}
tile = TileAddByDiagDir(tile, target_dir);
if (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile) && HasTileRoadType(tile, ROADTYPE_ROAD)) {
if (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile) && HasTileRoadType(tile, RTT_ROAD)) {
/* Don't allow building over roads of other cities */
if (IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN) && Town::GetByTile(tile) != t) {
if (IsRoadOwner(tile, RTT_ROAD, OWNER_TOWN) && Town::GetByTile(tile) != t) {
return false;
} else if (IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_NONE) && _game_mode == GM_EDITOR) {
} else if (IsRoadOwner(tile, RTT_ROAD, OWNER_NONE) && _game_mode == GM_EDITOR) {
/* If we are in the SE, and this road-piece has no town owner yet, it just found an
* owner :) (happy happy happy road now) */
SetRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN);
SetRoadOwner(tile, RTT_ROAD, OWNER_TOWN);
SetTownIndex(tile, t->index);
}
}
@@ -1490,7 +1634,7 @@ static bool GrowTown(Town *t)
};
/* Current "company" is a town */
Backup<CompanyByte> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
Backup<CompanyID> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
TileIndex tile = t->xy; // The tile we are working with ATM
@@ -1513,7 +1657,8 @@ static bool GrowTown(Town *t)
/* Only work with plain land that not already has a house */
if (!IsTileType(tile, MP_HOUSE) && IsTileFlat(tile)) {
if (DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_LANDSCAPE_CLEAR).Succeeded()) {
DoCommand(tile, GenRandomRoadBits(), t->index, DC_EXEC | DC_AUTO, CMD_BUILD_ROAD);
RoadType rt = GetTownRoadType(t);
DoCommand(tile, GenRandomRoadBits() | (rt << 4), t->index, DC_EXEC | DC_AUTO, CMD_BUILD_ROAD);
cur_company.Restore();
return true;
}
@@ -1601,16 +1746,19 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize
* similar towns they're unlikely to grow all in one tick */
t->grow_counter = t->index % TOWN_GROWTH_TICKS;
t->growth_rate = TownTicksToGameTicks(250);
t->show_zone = false;
_town_kdtree.Insert(t->index);
/* Set the default cargo requirement for town growth */
switch (_settings_game.game_creation.landscape) {
case LT_ARCTIC:
if (FindFirstCargoWithTownEffect(TE_FOOD) != NULL) t->goal[TE_FOOD] = TOWN_GROWTH_WINTER;
if (FindFirstCargoWithTownEffect(TE_FOOD) != nullptr) t->goal[TE_FOOD] = TOWN_GROWTH_WINTER;
break;
case LT_TROPIC:
if (FindFirstCargoWithTownEffect(TE_FOOD) != NULL) t->goal[TE_FOOD] = TOWN_GROWTH_DESERT;
if (FindFirstCargoWithTownEffect(TE_WATER) != NULL) t->goal[TE_WATER] = TOWN_GROWTH_DESERT;
if (FindFirstCargoWithTownEffect(TE_FOOD) != nullptr) t->goal[TE_FOOD] = TOWN_GROWTH_DESERT;
if (FindFirstCargoWithTownEffect(TE_WATER) != nullptr) t->goal[TE_WATER] = TOWN_GROWTH_DESERT;
break;
}
@@ -1636,6 +1784,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize
t->townnameparts = townnameparts;
t->UpdateVirtCoord();
_viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeTown(t->index));
InvalidateWindowData(WC_TOWN_DIRECTORY, 0, 0);
t->InitializeLayout(layout);
@@ -1697,7 +1846,7 @@ static bool IsUniqueTownName(const char *name)
const Town *t;
FOR_ALL_TOWNS(t) {
if (t->name != NULL && strcmp(t->name, name) == 0) return false;
if (t->name != nullptr && strcmp(t->name, name) == 0) return false;
}
return true;
@@ -1778,7 +1927,7 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
Town *t;
if (random) {
t = CreateRandomTown(20, townnameparts, size, city, layout);
if (t == NULL) {
if (t == nullptr) {
cost = CommandCost(STR_ERROR_NO_SPACE_FOR_TOWN);
} else {
_new_town_id = t->index;
@@ -1790,13 +1939,13 @@ CommandCost CmdFoundTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
UpdateNearestTownForRoadTiles(false);
old_generating_world.Restore();
if (t != NULL && !StrEmpty(text)) {
if (t != nullptr && !StrEmpty(text)) {
t->name = stredup(text);
t->UpdateVirtCoord();
}
if (_game_mode != GM_EDITOR) {
/* 't' can't be NULL since 'random' is false outside scenedit */
/* 't' can't be nullptr since 'random' is false outside scenedit */
assert(!random);
if (_current_company == OWNER_DEITY) {
@@ -1925,7 +2074,7 @@ static TileIndex FindNearestGoodCoastalTownSpot(TileIndex tile, TownLayout layou
SpotData sp = { INVALID_TILE, 0, layout };
TileIndex coast = tile;
if (CircularTileSearch(&coast, 40, FindNearestEmptyLand, NULL)) {
if (CircularTileSearch(&coast, 40, FindNearestEmptyLand, nullptr)) {
CircularTileSearch(&coast, 10, FindFurthestFromWater, &sp);
return sp.tile;
}
@@ -1938,7 +2087,7 @@ static Town *CreateRandomTown(uint attempts, uint32 townnameparts, TownSize size
{
assert(_game_mode == GM_EDITOR || _generating_world); // These are the preconditions for CMD_DELETE_TOWN
if (!Town::CanAllocateItem()) return NULL;
if (!Town::CanAllocateItem()) return nullptr;
do {
/* Generate a tile index not too close from the edge */
@@ -1963,7 +2112,7 @@ static Town *CreateRandomTown(uint attempts, uint32 townnameparts, TownSize size
* placement is so bad it couldn't grow at all */
if (t->cache.population > 0) return t;
Backup<CompanyByte> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
Backup<CompanyID> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
CommandCost rc = DoCommand(t->xy, t->index, 0, DC_EXEC, CMD_DELETE_TOWN);
cur_company.Restore();
assert(rc.Succeeded());
@@ -1975,7 +2124,7 @@ static Town *CreateRandomTown(uint attempts, uint32 townnameparts, TownSize size
assert(Town::CanAllocateItem());
} while (--attempts != 0);
return NULL;
return nullptr;
}
static const byte _num_initial_towns[4] = {5, 11, 23, 46}; // very low, low, normal, high
@@ -2007,17 +2156,20 @@ bool GenerateTowns(TownLayout layout)
/* Get a unique name for the town. */
if (!GenerateTownName(&townnameparts, &town_names)) continue;
/* try 20 times to create a random-sized town for the first loop. */
if (CreateRandomTown(20, townnameparts, TSZ_RANDOM, city, layout) != NULL) current_number++; // If creation was successful, raise a flag.
if (CreateRandomTown(20, townnameparts, TSZ_RANDOM, city, layout) != nullptr) current_number++; // If creation was successful, raise a flag.
} while (--total);
town_names.clear();
/* Build the town k-d tree again to make sure it's well balanced */
RebuildTownKdtree();
if (current_number != 0) return true;
/* If current_number is still zero at this point, it means that not a single town has been created.
* So give it a last try, but now more aggressive */
if (GenerateTownName(&townnameparts) &&
CreateRandomTown(10000, townnameparts, TSZ_RANDOM, _settings_game.economy.larger_towns != 0, layout) != NULL) {
CreateRandomTown(10000, townnameparts, TSZ_RANDOM, _settings_game.economy.larger_towns != 0, layout) != nullptr) {
return true;
}
@@ -2092,6 +2244,8 @@ static void MakeTownHouse(TileIndex t, Town *town, byte counter, byte stage, Hou
if (size & BUILDING_2_TILES_Y) ClearMakeHouseTile(t + TileDiffXY(0, 1), town, counter, stage, ++type, random_bits);
if (size & BUILDING_2_TILES_X) ClearMakeHouseTile(t + TileDiffXY(1, 0), town, counter, stage, ++type, random_bits);
if (size & BUILDING_HAS_4_TILES) ClearMakeHouseTile(t + TileDiffXY(1, 1), town, counter, stage, ++type, random_bits);
if (!_generating_world) FindStationsAroundTiles(TileArea(t, (size & BUILDING_2_TILES_X) ? 2 : 1, (size & BUILDING_2_TILES_Y) ? 2 : 1), &town->stations_near, false);
}
@@ -2108,6 +2262,9 @@ static inline bool CanBuildHouseHere(TileIndex tile, bool noslope)
Slope slope = GetTileSlope(tile);
if ((noslope && slope != SLOPE_FLAT) || IsSteepSlope(slope)) return false;
/* at least one RoadTypes allow building the house here? */
if (!RoadTypesAllowHouseHere(tile)) return false;
/* building under a bridge? */
if (IsBridgeAbove(tile)) return false;
@@ -2524,7 +2681,7 @@ void ClearTownHouse(Town *t, TileIndex tile)
CommandCost CmdRenameTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
Town *t = Town::GetIfValid(p1);
if (t == NULL) return CMD_ERROR;
if (t == nullptr) return CMD_ERROR;
bool reset = StrEmpty(text);
@@ -2535,7 +2692,7 @@ CommandCost CmdRenameTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (flags & DC_EXEC) {
free(t->name);
t->name = reset ? NULL : stredup(text);
t->name = reset ? nullptr : stredup(text);
t->UpdateVirtCoord();
InvalidateWindowData(WC_TOWN_DIRECTORY, 0, 1);
@@ -2555,7 +2712,7 @@ const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect)
FOR_ALL_CARGOSPECS(cs) {
if (cs->town_effect == effect) return cs;
}
return NULL;
return nullptr;
}
/**
@@ -2578,11 +2735,11 @@ CommandCost CmdTownCargoGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
uint16 index = GB(p1, 0, 16);
Town *t = Town::GetIfValid(index);
if (t == NULL) return CMD_ERROR;
if (t == nullptr) return CMD_ERROR;
/* Validate if there is a cargo which is the requested TownEffect */
const CargoSpec *cargo = FindFirstCargoWithTownEffect(te);
if (cargo == NULL) return CMD_ERROR;
if (cargo == nullptr) return CMD_ERROR;
if (flags & DC_EXEC) {
t->goal[te] = p2;
@@ -2606,11 +2763,11 @@ CommandCost CmdTownSetText(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
{
if (_current_company != OWNER_DEITY) return CMD_ERROR;
Town *t = Town::GetIfValid(p1);
if (t == NULL) return CMD_ERROR;
if (t == nullptr) return CMD_ERROR;
if (flags & DC_EXEC) {
free(t->text);
t->text = StrEmpty(text) ? NULL : stredup(text);
t->text = StrEmpty(text) ? nullptr : stredup(text);
InvalidateWindowData(WC_TOWN_VIEW, p1);
}
@@ -2632,7 +2789,7 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui
if (GB(p2, 16, 16) != 0) return CMD_ERROR;
Town *t = Town::GetIfValid(p1);
if (t == NULL) return CMD_ERROR;
if (t == nullptr) return CMD_ERROR;
if (flags & DC_EXEC) {
if (p2 == 0) {
@@ -2670,7 +2827,7 @@ CommandCost CmdExpandTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
{
if (_game_mode != GM_EDITOR && _current_company != OWNER_DEITY) return CMD_ERROR;
Town *t = Town::GetIfValid(p1);
if (t == NULL) return CMD_ERROR;
if (t == nullptr) return CMD_ERROR;
if (flags & DC_EXEC) {
/* The more houses, the faster we grow */
@@ -2710,7 +2867,7 @@ CommandCost CmdDeleteTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
{
if (_game_mode != GM_EDITOR && !_generating_world) return CMD_ERROR;
Town *t = Town::GetIfValid(p1);
if (t == NULL) return CMD_ERROR;
if (t == nullptr) return CMD_ERROR;
/* Stations refer to towns. */
const Station *st;
@@ -2730,7 +2887,18 @@ CommandCost CmdDeleteTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (d->town == t) return CMD_ERROR;
}
/* Check all tiles for town ownership. */
/* Check all tiles for town ownership. First check for bridge tiles, as
* these do not directly have an owner so we need to check adjacent
* tiles. This won't work correctly in the same loop if the adjacent
* tile was already deleted earlier in the loop. */
for (TileIndex tile = 0; tile < MapSize(); ++tile) {
if (IsTileType(tile, MP_TUNNELBRIDGE) && TestTownOwnsBridge(tile, t)) {
CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
}
}
/* Check all remaining tiles for town ownership. */
for (TileIndex tile = 0; tile < MapSize(); ++tile) {
bool try_clear = false;
switch (GetTileType(tile)) {
@@ -2738,10 +2906,6 @@ CommandCost CmdDeleteTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
try_clear = HasTownOwnedRoad(tile) && GetTownIndex(tile) == t->index;
break;
case MP_TUNNELBRIDGE:
try_clear = IsTileOwner(tile, OWNER_TOWN) && ClosestTownFromTile(tile, UINT_MAX) == t;
break;
case MP_HOUSE:
try_clear = GetTownIndex(tile) == t->index;
break;
@@ -2762,7 +2926,7 @@ CommandCost CmdDeleteTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
try_clear = true;
} else {
/* Tell to find a new town. */
if (flags & DC_EXEC) o->town = NULL;
if (flags & DC_EXEC) o->town = nullptr;
}
}
}
@@ -2778,7 +2942,11 @@ CommandCost CmdDeleteTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
/* The town destructor will delete the other things related to the town. */
if (flags & DC_EXEC) delete t;
if (flags & DC_EXEC) {
_town_kdtree.Remove(t->index);
_viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeTown(t->index));
delete t;
}
return CommandCost();
}
@@ -2845,7 +3013,7 @@ static CommandCost TownActionRoadRebuild(Town *t, DoCommandFlag flags)
*/
static bool TryClearTile(TileIndex tile)
{
Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
Backup<CompanyID> cur_company(_current_company, OWNER_NONE, FILE_LINE);
CommandCost r = DoCommand(tile, 0, 0, DC_NONE, CMD_LANDSCAPE_CLEAR);
cur_company.Restore();
return r.Succeeded();
@@ -2917,7 +3085,7 @@ static CommandCost TownActionBuildStatue(Town *t, DoCommandFlag flags)
if (!CircularTileSearch(&tile, 9, SearchTileForStatue, &statue_data)) return_cmd_error(STR_ERROR_STATUE_NO_SUITABLE_PLACE);
if (flags & DC_EXEC) {
Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
Backup<CompanyID> cur_company(_current_company, OWNER_NONE, FILE_LINE);
DoCommand(statue_data.best_position, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
cur_company.Restore();
BuildObject(OBJECT_STATUE, statue_data.best_position, _current_company, t);
@@ -3029,7 +3197,7 @@ static TownActionProc * const _town_action_proc[] = {
/**
* Get a list of available actions to do at a town.
* @param nump if not NULL add put the number of available actions in it
* @param nump if not nullptr add put the number of available actions in it
* @param cid the company that is querying the town
* @param t the town that is queried
* @return bitmasked value of enabled actions
@@ -3072,7 +3240,7 @@ uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t)
}
}
if (nump != NULL) *nump = num;
if (nump != nullptr) *nump = num;
return buttons;
}
@@ -3090,9 +3258,9 @@ uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t)
CommandCost CmdDoTownAction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
Town *t = Town::GetIfValid(p1);
if (t == NULL || p2 >= lengthof(_town_action_proc)) return CMD_ERROR;
if (t == nullptr || p2 >= lengthof(_town_action_proc)) return CMD_ERROR;
if (!HasBit(GetMaskOfTownActions(NULL, _current_company, t), p2)) return CMD_ERROR;
if (!HasBit(GetMaskOfTownActions(nullptr, _current_company, t), p2)) return CMD_ERROR;
CommandCost cost(EXPENSES_OTHER, _price[PR_TOWN_ACTION] * _town_action_costs[p2] >> 8);
@@ -3106,6 +3274,21 @@ CommandCost CmdDoTownAction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
return cost;
}
template <typename Func>
static void ForAllStationsNearTown(Town *t, Func func)
{
/* Ideally the search radius should be close to the actual town zone 0 radius.
* The true radius is not stored or calculated anywhere, only the squared radius. */
/* The efficiency of this search might be improved for large towns and many stations on the map,
* by using an integer square root approximation giving a value not less than the true square root. */
uint search_radius = t->cache.squared_town_zone_radius[0] / 2;
ForAllStationsRadius(t->xy, search_radius, [&](const Station * st) {
if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) {
func(st);
}
});
}
static void UpdateTownRating(Town *t)
{
/* Increase company ratings if they're low */
@@ -3116,22 +3299,19 @@ static void UpdateTownRating(Town *t)
}
}
const Station *st;
FOR_ALL_STATIONS(st) {
if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) {
if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
if (Company::IsValidID(st->owner)) {
int new_rating = t->ratings[st->owner] + RATING_STATION_UP_STEP;
t->ratings[st->owner] = min(new_rating, INT16_MAX); // do not let it overflow
}
} else {
if (Company::IsValidID(st->owner)) {
int new_rating = t->ratings[st->owner] + RATING_STATION_DOWN_STEP;
t->ratings[st->owner] = max(new_rating, INT16_MIN);
}
ForAllStationsNearTown(t, [&](const Station *st) {
if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
if (Company::IsValidID(st->owner)) {
int new_rating = t->ratings[st->owner] + RATING_STATION_UP_STEP;
t->ratings[st->owner] = min(new_rating, INT16_MAX); // do not let it overflow
}
} else {
if (Company::IsValidID(st->owner)) {
int new_rating = t->ratings[st->owner] + RATING_STATION_DOWN_STEP;
t->ratings[st->owner] = max(new_rating, INT16_MIN);
}
}
}
});
/* clamp all ratings to valid values */
for (uint i = 0; i < MAX_COMPANIES; i++) {
@@ -3166,14 +3346,11 @@ static void UpdateTownGrowCounter(Town *t, uint16 prev_growth_rate)
static int CountActiveStations(Town *t)
{
int n = 0;
const Station *st;
FOR_ALL_STATIONS(st) {
if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) {
if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
n++;
}
ForAllStationsNearTown(t, [&](const Station * st) {
if (st->time_since_load <= 20 || st->time_since_unload <= 20) {
n++;
}
}
});
return n;
}
@@ -3285,7 +3462,7 @@ CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlag flags
if (!Company::IsValidID(_current_company) || (flags & DC_NO_TEST_TOWN_RATING)) return CommandCost();
Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority);
if (t == NULL) return CommandCost();
if (t == nullptr) return CommandCost();
if (t->ratings[_current_company] > RATING_VERYPOOR) return CommandCost();
@@ -3297,32 +3474,25 @@ CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlag flags
* Return the town closest to the given tile within \a threshold.
* @param tile Starting point of the search.
* @param threshold Biggest allowed distance to the town.
* @return Closest town to \a tile within \a threshold, or \c NULL if there is no such town.
* @return Closest town to \a tile within \a threshold, or \c nullptr if there is no such town.
*
* @note This function only uses distance, the #ClosestTownFromTile function also takes town ownership into account.
*/
Town *CalcClosestTownFromTile(TileIndex tile, uint threshold)
{
Town *t;
uint best = threshold;
Town *best_town = NULL;
if (Town::GetNumItems() == 0) return nullptr;
FOR_ALL_TOWNS(t) {
uint dist = DistanceManhattan(tile, t->xy);
if (dist < best) {
best = dist;
best_town = t;
}
}
return best_town;
TownID tid = _town_kdtree.FindNearest(TileX(tile), TileY(tile));
Town *town = Town::Get(tid);
if (DistanceManhattan(tile, town->xy) < threshold) return town;
return nullptr;
}
/**
* Return the town closest (in distance or ownership) to a given tile, within a given threshold.
* @param tile Starting point of the search.
* @param threshold Biggest allowed distance to the town.
* @return Closest town to \a tile within \a threshold, or \c NULL if there is no such town.
* @return Closest town to \a tile within \a threshold, or \c nullptr if there is no such town.
*
* @note If you only care about distance, you can use the #CalcClosestTownFromTile function.
*/
@@ -3339,13 +3509,13 @@ Town *ClosestTownFromTile(TileIndex tile, uint threshold)
/* in the case we are generating "many random towns", this value may be INVALID_TOWN */
if (_generating_world) return CalcClosestTownFromTile(tile, threshold);
assert(Town::GetNumItems() == 0);
return NULL;
return nullptr;
}
assert(Town::IsValidID(tid));
Town *town = Town::Get(tid);
if (DistanceManhattan(tile, town->xy) >= threshold) town = NULL;
if (DistanceManhattan(tile, town->xy) >= threshold) town = nullptr;
return town;
}
@@ -3360,7 +3530,7 @@ Town *ClosestTownFromTile(TileIndex tile, uint threshold)
}
static bool _town_rating_test = false; ///< If \c true, town rating is in test-mode.
static SmallMap<const Town *, int, 4> _town_test_ratings; ///< Map of towns to modified ratings, while in town rating test-mode.
static SmallMap<const Town *, int> _town_test_ratings; ///< Map of towns to modified ratings, while in town rating test-mode.
/**
* Switch the town rating to test-mode, to allow commands to be tested without affecting current ratings.
@@ -3372,7 +3542,7 @@ void SetTownRatingTestMode(bool mode)
static int ref_count = 0; // Number of times test-mode is switched on.
if (mode) {
if (ref_count == 0) {
_town_test_ratings.Clear();
_town_test_ratings.clear();
}
ref_count++;
} else {
@@ -3408,7 +3578,7 @@ static int GetRating(const Town *t)
void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags)
{
/* if magic_bulldozer cheat is active, town doesn't penalize for removing stuff */
if (t == NULL || (flags & DC_NO_MODIFY_TOWN_RATING) ||
if (t == nullptr || (flags & DC_NO_MODIFY_TOWN_RATING) ||
!Company::IsValidID(_current_company) ||
(_cheats.magic_bulldozer.value && add < 0)) {
return;
@@ -3445,7 +3615,7 @@ void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags)
CommandCost CheckforTownRating(DoCommandFlag flags, Town *t, TownRatingCheckType type)
{
/* if magic_bulldozer cheat is active, town doesn't restrict your destructive actions */
if (t == NULL || !Company::IsValidID(_current_company) ||
if (t == nullptr || !Company::IsValidID(_current_company) ||
_cheats.magic_bulldozer.value || (flags & DC_NO_TEST_TOWN_RATING)) {
return CommandCost();
}
@@ -3538,12 +3708,12 @@ extern const TileTypeProcs _tile_type_town_procs = {
AddAcceptedCargo_Town, // add_accepted_cargo_proc
GetTileDesc_Town, // get_tile_desc_proc
GetTileTrackStatus_Town, // get_tile_track_status_proc
NULL, // click_tile_proc
nullptr, // click_tile_proc
AnimateTile_Town, // animate_tile_proc
TileLoop_Town, // tile_loop_proc
ChangeTileOwner_Town, // change_tile_owner_proc
AddProducedCargo_Town, // add_produced_cargo_proc
NULL, // vehicle_enter_tile_proc
nullptr, // vehicle_enter_tile_proc
GetFoundation_Town, // get_foundation_proc
TerraformTile_Town, // terraform_tile_proc
};