Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codechange: Make HouseSpec list and cached building counts dynamic. #12287

Merged
merged 2 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/genworld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "void_map.h"
#include "town.h"
#include "newgrf.h"
#include "newgrf_house.h"
#include "core/random_func.hpp"
#include "core/backup_type.hpp"
#include "progress.h"
Expand Down Expand Up @@ -311,6 +312,7 @@ void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_setti

/* Load the right landscape stuff, and the NewGRFs! */
GfxLoadSprites();
InitializeBuildingCounts();
LoadStringWidthTable();

/* Re-init the windowing system */
Expand Down
15 changes: 3 additions & 12 deletions src/house.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,6 @@ static const HouseID INVALID_HOUSE_ID = 0xFFFF;

static const uint HOUSE_NUM_ACCEPTS = 16; ///< Max number of cargoes accepted by a tile

/**
* There can only be as many classes as there are new houses, plus one for
* NO_CLASS, as the original houses don't have classes.
*/
static const uint HOUSE_CLASS_MAX = NUM_HOUSES - NEW_HOUSE_OFFSET + 1;

enum BuildingFlags {
TILE_NO_FLAG = 0,
TILE_SIZE_1x1 = 1U << 0,
Expand Down Expand Up @@ -123,14 +117,11 @@ struct HouseSpec {
byte minimum_life; ///< The minimum number of years this house will survive before the town rebuilds it
CargoTypes watched_cargoes; ///< Cargo types watched for acceptance.

HouseID Index() const;
Money GetRemovalCost() const;

static inline HouseSpec *Get(size_t house_id)
{
assert(house_id < NUM_HOUSES);
extern HouseSpec _house_specs[];
return &_house_specs[house_id];
}
static std::vector<HouseSpec> &Specs();
static HouseSpec *Get(size_t house_id);
};

/**
Expand Down
18 changes: 8 additions & 10 deletions src/industry_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2504,12 +2504,11 @@ struct CargoesRow {
} else {
/* Houses only display what is demanded. */
for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
for (uint h = 0; h < NUM_HOUSES; h++) {
HouseSpec *hs = HouseSpec::Get(h);
if (!hs->enabled) continue;
for (const auto &hs : HouseSpec::Specs()) {
if (!hs.enabled) continue;

for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
if (hs.cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs.accepts_cargo[j]) {
cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
goto next_cargo;
}
Expand Down Expand Up @@ -2726,12 +2725,11 @@ struct IndustryCargoesWindow : public Window {
for (uint i = 0; i < length; i++) {
if (!IsValidCargoID(cargoes[i])) continue;

for (uint h = 0; h < NUM_HOUSES; h++) {
HouseSpec *hs = HouseSpec::Get(h);
if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
for (const auto &hs : HouseSpec::Specs()) {
if (!hs.enabled || !(hs.building_availability & climate_mask)) continue;

for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
for (uint j = 0; j < lengthof(hs.accepts_cargo); j++) {
if (hs.cargo_acceptance[j] > 0 && cargoes[i] == hs.accepts_cargo[j]) return true;
}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
InitializeTrees();
InitializeIndustries();
InitializeObjects();
InitializeBuildingCounts();

InitializeNPF();

Expand Down
20 changes: 9 additions & 11 deletions src/newgrf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9329,20 +9329,18 @@ static void EnsureEarlyHouse(HouseZones bitmask)
{
TimerGameCalendar::Year min_year = CalendarTime::MAX_YEAR;

for (int i = 0; i < NUM_HOUSES; i++) {
HouseSpec *hs = HouseSpec::Get(i);
if (hs == nullptr || !hs->enabled) continue;
if ((hs->building_availability & bitmask) != bitmask) continue;
if (hs->min_year < min_year) min_year = hs->min_year;
for (const auto &hs : HouseSpec::Specs()) {
if (!hs.enabled) continue;
if ((hs.building_availability & bitmask) != bitmask) continue;
if (hs.min_year < min_year) min_year = hs.min_year;
}

if (min_year == 0) return;

for (int i = 0; i < NUM_HOUSES; i++) {
HouseSpec *hs = HouseSpec::Get(i);
if (hs == nullptr || !hs->enabled) continue;
if ((hs->building_availability & bitmask) != bitmask) continue;
if (hs->min_year == min_year) hs->min_year = 0;
for (auto &hs : HouseSpec::Specs()) {
if (!hs.enabled) continue;
if ((hs.building_availability & bitmask) != bitmask) continue;
if (hs.min_year == min_year) hs.min_year = 0;
}
}

Expand Down Expand Up @@ -9382,7 +9380,7 @@ static void FinaliseHouseArray()
}
}

for (size_t i = 0; i < NUM_HOUSES; i++) {
for (size_t i = 0; i < HouseSpec::Specs().size(); i++) {
HouseSpec *hs = HouseSpec::Get(i);
const HouseSpec *next1 = (i + 1 < NUM_HOUSES ? HouseSpec::Get(i + 1) : nullptr);
const HouseSpec *next2 = (i + 2 < NUM_HOUSES ? HouseSpec::Get(i + 2) : nullptr);
Expand Down
6 changes: 5 additions & 1 deletion src/newgrf_commons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ void HouseOverrideManager::SetEntitySpec(const HouseSpec *hs)
return;
}

*HouseSpec::Get(house_id) = *hs;
auto &house_specs = HouseSpec::Specs();

/* Now that we know we can use the given id, copy the spec to its final destination. */
if (house_id >= house_specs.size()) house_specs.resize(house_id + 1);
house_specs[house_id] = *hs;

/* Now add the overrides. */
for (int i = 0; i < this->max_offset; i++) {
Expand Down
96 changes: 81 additions & 15 deletions src/newgrf_house.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@

#include "safeguards.h"

static BuildingCounts<uint32_t> _building_counts;
static std::array<HouseClassMapping, HOUSE_CLASS_MAX> _class_mapping;
static BuildingCounts<uint32_t> _building_counts{};
static std::vector<HouseClassMapping> _class_mapping{};

HouseOverrideManager _house_mngr(NEW_HOUSE_OFFSET, NUM_HOUSES, INVALID_HOUSE_ID);

Expand All @@ -40,6 +40,57 @@ static const GRFFile *GetHouseSpecGrf(HouseID house_id)
return (hs != nullptr) ? hs->grf_prop.grffile : nullptr;
}

extern const HouseSpec _original_house_specs[NEW_HOUSE_OFFSET];
std::vector<HouseSpec> _house_specs;

/**
* Get a reference to all HouseSpecs.
* @return Reference to vector of all HouseSpecs.
*/
std::vector<HouseSpec> &HouseSpec::Specs()
{
return _house_specs;
}

/**
* Gets the index of this spec.
* @return The index.
*/
HouseID HouseSpec::Index() const
{
return static_cast<HouseID>(this - _house_specs.data());
}

/**
* Get the spec for a house ID.
* @param house_id The ID of the house.
* @return The HouseSpec associated with the ID.
*/
HouseSpec *HouseSpec::Get(size_t house_id)
{
/* Empty house if index is out of range -- this might happen if NewGRFs are changed. */
static HouseSpec empty = {};

assert(house_id < NUM_HOUSES);
if (house_id >= _house_specs.size()) return &empty;
return &_house_specs[house_id];
}

/* Reset and initialise house specs. */
void ResetHouses()
{
_house_specs.clear();
_house_specs.reserve(std::size(_original_house_specs));

ResetHouseClassIDs();

/* Copy default houses. */
_house_specs.insert(std::end(_house_specs), std::begin(_original_house_specs), std::end(_original_house_specs));

/* Reset any overrides that have been set. */
_house_mngr.ResetOverride();
}

/**
* Construct a resolver for a house.
* @param house_id House to query.
Expand Down Expand Up @@ -74,32 +125,47 @@ uint32_t HouseResolverObject::GetDebugID() const

void ResetHouseClassIDs()
{
_class_mapping = {};
_class_mapping.clear();

/* Add initial entry for HOUSE_NO_CLASS. */
_class_mapping.emplace_back();
}

HouseClassID AllocateHouseClassID(byte grf_class_id, uint32_t grfid)
{
/* Start from 1 because 0 means that no class has been assigned. */
for (int i = 1; i != lengthof(_class_mapping); i++) {
HouseClassMapping *map = &_class_mapping[i];
auto it = std::find_if(std::next(std::begin(_class_mapping)), std::end(_class_mapping), [grf_class_id, grfid](const HouseClassMapping &map) { return map.class_id == grf_class_id && map.grfid == grfid; });

if (map->class_id == grf_class_id && map->grfid == grfid) return (HouseClassID)i;
/* HouseClass not found, allocate a new one. */
if (it == std::end(_class_mapping)) it = _class_mapping.insert(it, {.grfid = grfid, .class_id = grf_class_id});

if (map->class_id == 0 && map->grfid == 0) {
map->class_id = grf_class_id;
map->grfid = grfid;
return (HouseClassID)i;
}
}
return HOUSE_NO_CLASS;
return static_cast<HouseClassID>(std::distance(std::begin(_class_mapping), it));
}

/**
* Initialise building counts for a town.
* @param t Town cache to initialise.
*/
void InitializeBuildingCounts(Town *t)
{
t->cache.building_counts.id_count.clear();
t->cache.building_counts.class_count.clear();
t->cache.building_counts.id_count.resize(HouseSpec::Specs().size());
t->cache.building_counts.class_count.resize(_class_mapping.size());
}

/**
* Initialise global building counts and all town building counts.
*/
void InitializeBuildingCounts()
{
memset(&_building_counts, 0, sizeof(_building_counts));
_building_counts.id_count.clear();
_building_counts.class_count.clear();
_building_counts.id_count.resize(HouseSpec::Specs().size());
_building_counts.class_count.resize(_class_mapping.size());

for (Town *t : Town::Iterate()) {
memset(&t->cache.building_counts, 0, sizeof(t->cache.building_counts));
InitializeBuildingCounts(t);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/newgrf_house.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ void ResetHouseClassIDs();
HouseClassID AllocateHouseClassID(byte grf_class_id, uint32_t grfid);

void InitializeBuildingCounts();
void InitializeBuildingCounts(Town *t);
void IncreaseBuildingCount(Town *t, HouseID house_id);
void DecreaseBuildingCount(Town *t, HouseID house_id);

Expand Down
2 changes: 1 addition & 1 deletion src/table/town_land.h
Original file line number Diff line number Diff line change
Expand Up @@ -1817,7 +1817,7 @@ static_assert(lengthof(_town_draw_tile_data) == (NEW_HOUSE_OFFSET) * 4 * 4);
bf, ba, true, GRFFileProps(INVALID_HOUSE_ID), 0, {COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN, COLOUR_BEGIN}, \
16, NO_EXTRA_FLAG, HOUSE_NO_CLASS, {0, 2, 0, 0}, 0, 0, 0}
/** House specifications from original data */
static const HouseSpec _original_house_specs[] = {
extern const HouseSpec _original_house_specs[] = {
/**
* remove_rating_decrease
* | mail_generation
Expand Down
4 changes: 2 additions & 2 deletions src/town.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

template <typename T>
struct BuildingCounts {
T id_count[NUM_HOUSES];
T class_count[HOUSE_CLASS_MAX];
std::vector<T> id_count;
std::vector<T> class_count;
};

static const uint CUSTOM_TOWN_NUMBER_DIFFICULTY = 4; ///< value for custom town number in difficulty settings
Expand Down
53 changes: 18 additions & 35 deletions src/town_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1941,6 +1941,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32_t townnameparts, TownSi
UpdateTownRadius(t);
t->flags = 0;
t->cache.population = 0;
InitializeBuildingCounts(t);
/* Spread growth across ticks so even if there are many
* similar towns they're unlikely to grow all in one tick */
t->grow_counter = t->index % Ticks::TOWN_GROWTH_TICKS;
Expand Down Expand Up @@ -2657,31 +2658,28 @@ static bool BuildTownHouse(Town *t, TileIndex tile)
/* bits 0-4 are used
* bits 11-15 are used
* bits 5-10 are not used. */
HouseID houses[NUM_HOUSES];
uint num = 0;
uint probs[NUM_HOUSES];
static std::vector<std::pair<HouseID, uint>> probs;
probs.clear();

uint probability_max = 0;

/* Generate a list of all possible houses that can be built. */
for (uint i = 0; i < NUM_HOUSES; i++) {
const HouseSpec *hs = HouseSpec::Get(i);

for (const auto &hs : HouseSpec::Specs()) {
/* Verify that the candidate house spec matches the current tile status */
if ((~hs->building_availability & bitmask) != 0 || !hs->enabled || hs->grf_prop.override != INVALID_HOUSE_ID) continue;
if ((~hs.building_availability & bitmask) != 0 || !hs.enabled || hs.grf_prop.override != INVALID_HOUSE_ID) continue;

/* Don't let these counters overflow. Global counters are 32bit, there will never be that many houses. */
if (hs->class_id != HOUSE_NO_CLASS) {
if (hs.class_id != HOUSE_NO_CLASS) {
/* id_count is always <= class_count, so it doesn't need to be checked */
if (t->cache.building_counts.class_count[hs->class_id] == UINT16_MAX) continue;
if (t->cache.building_counts.class_count[hs.class_id] == UINT16_MAX) continue;
} else {
/* If the house has no class, check id_count instead */
if (t->cache.building_counts.id_count[i] == UINT16_MAX) continue;
if (t->cache.building_counts.id_count[hs.Index()] == UINT16_MAX) continue;
}

uint cur_prob = hs->probability;
uint cur_prob = hs.probability;
probability_max += cur_prob;
probs[num] = cur_prob;
houses[num++] = (HouseID)i;
probs.emplace_back(std::make_pair(hs.Index(), cur_prob));
}

TileIndex baseTile = tile;
Expand All @@ -2696,18 +2694,17 @@ static bool BuildTownHouse(Town *t, TileIndex tile)

uint r = RandomRange(probability_max);
uint i;
for (i = 0; i < num; i++) {
if (probs[i] > r) break;
r -= probs[i];
for (i = 0; i < probs.size(); i++) {
if (probs[i].second > r) break;
r -= probs[i].second;
}

HouseID house = houses[i];
probability_max -= probs[i];
HouseID house = probs[i].first;
probability_max -= probs[i].second;

/* remove tested house from the set */
num--;
houses[i] = houses[num];
probs[i] = probs[num];
probs[i] = probs.back();
probs.pop_back();

const HouseSpec *hs = HouseSpec::Get(house);

Expand Down Expand Up @@ -3969,17 +3966,3 @@ extern const TileTypeProcs _tile_type_town_procs = {
GetFoundation_Town, // get_foundation_proc
TerraformTile_Town, // terraform_tile_proc
};


HouseSpec _house_specs[NUM_HOUSES];

void ResetHouses()
{
ResetHouseClassIDs();

auto insert = std::copy(std::begin(_original_house_specs), std::end(_original_house_specs), std::begin(_house_specs));
std::fill(insert, std::end(_house_specs), HouseSpec{});

/* Reset any overrides that have been set. */
_house_mngr.ResetOverride();
}