Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-09-28 02:33:49 +05:00
926 changed files with 37902 additions and 27369 deletions

View File

@@ -8,6 +8,8 @@
/** @file industry_cmd.cpp Handling of industry tiles. */
#include "stdafx.h"
#include "misc/history_type.hpp"
#include "misc/history_func.hpp"
#include "clear_map.h"
#include "industry.h"
#include "station_base.h"
@@ -66,7 +68,7 @@ void BuildOilRig(TileIndex tile);
static uint8_t _industry_sound_ctr;
static TileIndex _industry_sound_tile;
std::array<std::vector<IndustryID>, NUM_INDUSTRYTYPES> Industry::industries;
std::array<FlatSet<IndustryID>, NUM_INDUSTRYTYPES> Industry::industries;
IndustrySpec _industry_specs[NUM_INDUSTRYTYPES];
IndustryTileSpec _industry_tile_specs[NUM_INDUSTRYTILES];
@@ -195,8 +197,7 @@ Industry::~Industry()
delete this->psa;
auto &industries = Industry::industries[type];
auto it = std::ranges::lower_bound(industries, this->index);
industries.erase(it);
industries.erase(this->index);
DeleteIndustryNews(this->index);
CloseWindowById(WC_INDUSTRY_VIEW, this->index);
@@ -342,7 +343,7 @@ static void DrawTile_Industry(TileInfo *ti)
* DrawNewIndustry will return false if ever the resolver could not
* find any sprite to display. So in this case, we will jump on the
* substitute gfx instead. */
if (indts->grf_prop.GetSpriteGroup() != nullptr && DrawNewIndustryTile(ti, ind, gfx, indts)) {
if (indts->grf_prop.HasSpriteGroups() && DrawNewIndustryTile(ti, ind, gfx, indts)) {
return;
} else {
/* No sprite group (or no valid one) found, meaning no graphics associated.
@@ -369,7 +370,7 @@ static void DrawTile_Industry(TileInfo *ti)
if (image == SPR_FLAT_WATER_TILE && IsTileOnWater(ti->tile)) {
DrawWaterClassGround(ti);
} else {
DrawGroundSprite(image, GroundSpritePaletteTransform(image, dits->ground.pal, GENERAL_SPRITE_COLOUR(ind->random_colour)));
DrawGroundSprite(image, GroundSpritePaletteTransform(image, dits->ground.pal, GetColourPalette(ind->random_colour)));
}
/* If industries are transparent and invisible, do not draw the upper part */
@@ -378,14 +379,8 @@ static void DrawTile_Industry(TileInfo *ti)
/* Add industry on top of the ground? */
image = dits->building.sprite;
if (image != 0) {
AddSortableSpriteToDraw(image, SpriteLayoutPaletteTransform(image, dits->building.pal, GENERAL_SPRITE_COLOUR(ind->random_colour)),
ti->x + dits->subtile_x,
ti->y + dits->subtile_y,
dits->width,
dits->height,
dits->dz,
ti->z,
IsTransparencySet(TO_INDUSTRIES));
AddSortableSpriteToDraw(image, SpriteLayoutPaletteTransform(image, dits->building.pal, GetColourPalette(ind->random_colour)),
*ti, *dits, IsTransparencySet(TO_INDUSTRIES));
if (IsTransparencySet(TO_INDUSTRIES)) return;
}
@@ -411,7 +406,7 @@ static Foundation GetFoundation_Industry(TileIndex tile, Slope tileh)
*/
if (gfx >= NEW_INDUSTRYTILEOFFSET) {
const IndustryTileSpec *indts = GetIndustryTileSpec(gfx);
if (indts->grf_prop.GetSpriteGroup() != nullptr && indts->callback_mask.Test(IndustryTileCallbackMask::DrawFoundations)) {
if (indts->callback_mask.Test(IndustryTileCallbackMask::DrawFoundations)) {
uint32_t callback_res = GetIndustryTileCallback(CBID_INDTILE_DRAW_FOUNDATIONS, 0, 0, gfx, Industry::GetByTile(tile), tile);
if (callback_res != CALLBACK_FAILED && !ConvertBooleanCallback(indts->grf_prop.grffile, CBID_INDTILE_DRAW_FOUNDATIONS, callback_res)) return FOUNDATION_NONE;
}
@@ -702,7 +697,7 @@ static void AnimateTile_Industry(TileIndex tile)
{
IndustryGfx gfx = GetIndustryGfx(tile);
if (GetIndustryTileSpec(gfx)->animation.status != ANIM_STATUS_NO_ANIMATION) {
if (GetIndustryTileSpec(gfx)->animation.status != AnimationStatus::NoAnimation) {
AnimateNewIndustryTile(tile);
return;
}
@@ -712,7 +707,7 @@ static void AnimateTile_Industry(TileIndex tile)
if ((TimerGameTick::counter & 1) == 0) AnimateSugarSieve(tile);
break;
case GFX_TOFFEE_QUARY:
case GFX_TOFFEE_QUARRY:
if ((TimerGameTick::counter & 3) == 0) AnimateToffeeQuarry(tile);
break;
@@ -769,7 +764,7 @@ static void MakeIndustryTileBigger(TileIndex tile)
uint8_t stage = GetIndustryConstructionStage(tile) + 1;
SetIndustryConstructionCounter(tile, 0);
SetIndustryConstructionStage(tile, stage);
StartStopIndustryTileAnimation(tile, IAT_CONSTRUCTION_STATE_CHANGE);
TriggerIndustryTileAnimation_ConstructionStageChanged(tile, false);
if (stage == INDUSTRY_COMPLETED) SetIndustryCompleted(tile);
MarkTileDirtyByTile(tile);
@@ -804,7 +799,7 @@ static void MakeIndustryTileBigger(TileIndex tile)
case GFX_TOY_FACTORY:
case GFX_BUBBLE_CATCHER:
case GFX_TOFFEE_QUARY:
case GFX_TOFFEE_QUARRY:
SetAnimationFrame(tile, 0);
SetIndustryAnimationLoop(tile, 0);
break;
@@ -850,7 +845,7 @@ static void TileLoop_Industry(TileIndex tile)
* returning from TileLoop_Water. */
if (!IsTileType(tile, MP_INDUSTRY)) return;
TriggerIndustryTile(tile, INDTILE_TRIGGER_TILE_LOOP);
TriggerIndustryTileRandomisation(tile, IndustryRandomTrigger::TileLoop);
if (!IsIndustryCompleted(tile)) {
MakeIndustryTileBigger(tile);
@@ -859,7 +854,7 @@ static void TileLoop_Industry(TileIndex tile)
if (_game_mode == GM_EDITOR) return;
if (TransportIndustryGoods(tile) && !StartStopIndustryTileAnimation(Industry::GetByTile(tile), IAT_INDUSTRY_DISTRIBUTES_CARGO)) {
if (TransportIndustryGoods(tile) && !TriggerIndustryAnimation(Industry::GetByTile(tile), IndustryAnimationTrigger::CargoDistributed)) {
uint newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_production;
if (newgfx != INDUSTRYTILE_NOANIM) {
@@ -871,7 +866,7 @@ static void TileLoop_Industry(TileIndex tile)
}
}
if (StartStopIndustryTileAnimation(tile, IAT_TILELOOP)) return;
if (TriggerIndustryTileAnimation(tile, IndustryAnimationTrigger::TileLoop)) return;
IndustryGfx newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_next;
if (newgfx != INDUSTRYTILE_NOANIM) {
@@ -949,7 +944,7 @@ static void TileLoop_Industry(TileIndex tile)
TileLoopIndustry_BubbleGenerator(tile);
break;
case GFX_TOFFEE_QUARY:
case GFX_TOFFEE_QUARRY:
AddAnimatedTile(tile);
break;
@@ -1109,30 +1104,6 @@ void PlantRandomFarmField(const Industry *i)
if (tile != INVALID_TILE) PlantFarmField(tile, i->index);
}
/**
* Search callback function for ChopLumberMillTrees
* @param tile to test
* @return the result of the test
*/
static bool SearchLumberMillTrees(TileIndex tile, void *)
{
if (IsTileType(tile, MP_TREES) && GetTreeGrowth(tile) >= TreeGrowthStage::Grown) {
/* found a tree */
Backup<CompanyID> cur_company(_current_company, OWNER_NONE);
_industry_sound_ctr = 1;
_industry_sound_tile = tile;
if (_settings_client.sound.ambient) SndPlayTileFx(SND_38_LUMBER_MILL_1, tile);
Command<CMD_LANDSCAPE_CLEAR>::Do(DoCommandFlag::Execute, tile);
cur_company.Restore();
return true;
}
return false;
}
/**
* Perform a circular search around the Lumber Mill in order to find trees to cut
* @param i industry
@@ -1150,9 +1121,20 @@ static void ChopLumberMillTrees(Industry *i)
}
}
TileIndex tile = i->location.tile;
if (CircularTileSearch(&tile, 40, SearchLumberMillTrees, nullptr)) { // 40x40 tiles to search.
itp->waiting = ClampTo<uint16_t>(itp->waiting + ScaleByCargoScale(45, false)); // Found a tree, add according value to waiting cargo.
for (auto tile : SpiralTileSequence(i->location.tile, 40)) { // 40x40 tiles to search.
if (!IsTileType(tile, MP_TREES) || GetTreeGrowth(tile) < TreeGrowthStage::Grown) continue;
/* found a tree */
_industry_sound_ctr = 1;
_industry_sound_tile = tile;
if (_settings_client.sound.ambient) SndPlayTileFx(SND_38_LUMBER_MILL_1, tile);
AutoRestoreBackup<CompanyID> cur_company(_current_company, OWNER_NONE);
Command<CMD_LANDSCAPE_CLEAR>::Do(DoCommandFlag::Execute, tile);
/* Add according value to waiting cargo. */
itp->waiting = ClampTo<uint16_t>(itp->waiting + ScaleByCargoScale(45, false));
break;
}
}
@@ -1241,8 +1223,8 @@ static void ProduceIndustryGoods(Industry *i)
if (cut) ChopLumberMillTrees(i);
}
TriggerIndustry(i, INDUSTRY_TRIGGER_INDUSTRY_TICK);
StartStopIndustryTileAnimation(i, IAT_INDUSTRY_TICK);
TriggerIndustryRandomisation(i, IndustryRandomTrigger::IndustryTick);
TriggerIndustryAnimation(i, IndustryAnimationTrigger::IndustryTick);
}
}
@@ -1263,6 +1245,10 @@ void OnTick_Industry()
for (Industry *i : Industry::Iterate()) {
ProduceIndustryGoods(i);
if ((TimerGameTick::counter + i->index) % Ticks::DAY_TICKS == 0) {
for (auto &a : i->accepted) a.accumulated_waiting += a.waiting;
}
}
}
@@ -1779,7 +1765,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
i->type = type;
auto &industries = Industry::industries[type];
industries.emplace(std::ranges::lower_bound(industries, i->index), i->index);
industries.insert(i->index);
for (size_t index = 0; index < std::size(indspec->produced_cargo); ++index) {
if (!IsValidCargoType(indspec->produced_cargo[index])) break;
@@ -1855,6 +1841,8 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
for (auto &p : i->produced) {
p.history[LAST_MONTH].production += ScaleByCargoScale(p.rate * 8, false);
}
UpdateValidHistory(i->valid_history, HISTORY_YEAR, TimerGameEconomy::month);
}
if (indspec->callback_mask.Test(IndustryCallbackMask::DecideColour)) {
@@ -1961,7 +1949,16 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
/* it->gfx is stored in the map. But the translated ID cur_gfx is the interesting one */
IndustryGfx cur_gfx = GetTranslatedIndustryTileID(it.gfx);
const IndustryTileSpec *its = GetIndustryTileSpec(cur_gfx);
if (its->animation.status != ANIM_STATUS_NO_ANIMATION) AddAnimatedTile(cur_tile);
if (its->animation.status != AnimationStatus::NoAnimation) AddAnimatedTile(cur_tile);
}
}
/* Call callbacks after all tiles have been created. */
for (TileIndex cur_tile : i->location) {
if (i->TileBelongsToIndustry(cur_tile)) {
/* There are no shared random bits, consistent with "MakeIndustryTileBigger" in tile loop.
* So, trigger tiles individually */
TriggerIndustryTileAnimation_ConstructionStageChanged(cur_tile, true);
}
}
@@ -2538,22 +2535,48 @@ void GenerateIndustries()
_industry_builder.Reset();
}
template <>
Industry::ProducedHistory SumHistory(std::span<const Industry::ProducedHistory> history)
{
uint32_t production = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &p) { return r + p.production; });
uint32_t transported = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &p) { return r + p.transported; });
auto count = std::size(history);
return {.production = ClampTo<uint16_t>(production / count), .transported = ClampTo<uint16_t>(transported / count)};
}
template <>
Industry::AcceptedHistory SumHistory(std::span<const Industry::AcceptedHistory> history)
{
uint32_t accepted = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &a) { return r + a.accepted; });
uint32_t waiting = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &a) { return r + a.waiting; });;
auto count = std::size(history);
return {.accepted = ClampTo<uint16_t>(accepted / count), .waiting = ClampTo<uint16_t>(waiting / count)};
}
/**
* Monthly update of industry statistics.
* @param i Industry to update.
*/
static void UpdateIndustryStatistics(Industry *i)
{
auto month = TimerGameEconomy::month;
UpdateValidHistory(i->valid_history, HISTORY_YEAR, month);
for (auto &p : i->produced) {
if (IsValidCargoType(p.cargo)) {
if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameEconomy::year;
/* Move history from this month to last month. */
std::rotate(std::rbegin(p.history), std::rbegin(p.history) + 1, std::rend(p.history));
p.history[THIS_MONTH].production = 0;
p.history[THIS_MONTH].transported = 0;
RotateHistory(p.history, i->valid_history, HISTORY_YEAR, month);
}
}
for (auto &a : i->accepted) {
if (!IsValidCargoType(a.cargo)) continue;
if (a.history == nullptr) continue;
(*a.history)[THIS_MONTH].waiting = GetAndResetAccumulatedAverage<uint16_t>(a.accumulated_waiting);
RotateHistory(*a.history, i->valid_history, HISTORY_YEAR, month);
}
}
/**
@@ -2787,14 +2810,14 @@ int WhoCanServiceIndustry(Industry *ind)
* We cannot check the first of shared orders only, since the first vehicle in such a chain
* may have a different cargo type.
*/
for (const Order *o : v->Orders()) {
if (o->IsType(OT_GOTO_STATION) && !(o->GetUnloadType() & OUFB_TRANSFER)) {
for (const Order &o : v->Orders()) {
if (o.IsType(OT_GOTO_STATION) && !(o.GetUnloadType() & OUFB_TRANSFER)) {
/* Vehicle visits a station to load or unload */
Station *st = Station::Get(o->GetDestination().ToStationID());
Station *st = Station::Get(o.GetDestination().ToStationID());
assert(st != nullptr);
/* Same cargo produced by industry is dropped here => not serviced by vehicle v */
if ((o->GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break;
if ((o.GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break;
if (ind->stations_near.find(st) != ind->stations_near.end()) {
if (v->owner == _local_company) return 2; // Company services industry
@@ -2856,11 +2879,12 @@ static void ChangeIndustryProduction(Industry *i, bool monthly)
bool callback_enabled = indspec->callback_mask.Test(monthly ? IndustryCallbackMask::MonthlyProdChange : IndustryCallbackMask::ProductionChange);
if (callback_enabled) {
uint16_t res = GetIndustryCallback(monthly ? CBID_INDUSTRY_MONTHLYPROD_CHANGE : CBID_INDUSTRY_PRODUCTION_CHANGE, 0, Random(), i, i->type, i->location.tile);
std::array<int32_t, 1> regs100;
uint16_t res = GetIndustryCallback(monthly ? CBID_INDUSTRY_MONTHLYPROD_CHANGE : CBID_INDUSTRY_PRODUCTION_CHANGE, 0, Random(), i, i->type, i->location.tile, regs100);
if (res != CALLBACK_FAILED) { // failed callback means "do nothing"
suppress_message = HasBit(res, 7);
/* Get the custom message if any */
if (HasBit(res, 8)) str = MapGRFStringID(indspec->grf_prop.grfid, GRFStringID(GB(GetRegister(0x100), 0, 16)));
if (HasBit(res, 8)) str = MapGRFStringID(indspec->grf_prop.grfid, GRFStringID(GB(regs100[0], 0, 16)));
res = GB(res, 0, 4);
switch (res) {
default: NOT_REACHED();
@@ -2878,7 +2902,7 @@ static void ChangeIndustryProduction(Industry *i, bool monthly)
increment = res == 0x0D ? -1 : 1;
break;
case 0xF: // Set production to third byte of register 0x100
i->prod_level = Clamp(GB(GetRegister(0x100), 16, 8), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
i->prod_level = Clamp(GB(regs100[0], 16, 8), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
recalculate_multipliers = true;
break;
}
@@ -3053,7 +3077,7 @@ static void ChangeIndustryProduction(Industry *i, bool monthly)
* For small maps, it implies that less than one change per month is required, while on bigger maps,
* it would be way more. The daily loop handles those changes.
*/
static IntervalTimer<TimerGameEconomy> _economy_industries_daily({TimerGameEconomy::DAY, TimerGameEconomy::Priority::INDUSTRY}, [](auto)
static const IntervalTimer<TimerGameEconomy> _economy_industries_daily({TimerGameEconomy::DAY, TimerGameEconomy::Priority::INDUSTRY}, [](auto)
{
_economy.industry_daily_change_counter += _economy.industry_daily_increment;
@@ -3095,7 +3119,7 @@ static IntervalTimer<TimerGameEconomy> _economy_industries_daily({TimerGameEcono
InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, IDIWD_PRODUCTION_CHANGE);
});
static IntervalTimer<TimerGameEconomy> _economy_industries_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::INDUSTRY}, [](auto)
static const IntervalTimer<TimerGameEconomy> _economy_industries_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::INDUSTRY}, [](auto)
{
Backup<CompanyID> cur_company(_current_company, OWNER_NONE);
@@ -3247,6 +3271,7 @@ extern const TileTypeProcs _tile_type_industry_procs = {
nullptr, // vehicle_enter_tile_proc
GetFoundation_Industry, // get_foundation_proc
TerraformTile_Industry, // terraform_tile_proc
nullptr, // check_build_above_proc
};
bool IndustryCompare::operator() (const IndustryListEntry &lhs, const IndustryListEntry &rhs) const