Merge remote-tracking branch 'upstream/master' into 13.0

This commit is contained in:
Pavel Stupnikov
2022-11-26 22:16:25 +04:00
508 changed files with 14617 additions and 9750 deletions

View File

@@ -40,8 +40,10 @@
#include "object_base.h"
#include "game/game.hpp"
#include "error.h"
#include "cmd_helper.h"
#include "string_func.h"
#include "industry_cmd.h"
#include "landscape_cmd.h"
#include "terraform_cmd.h"
#include "table/strings.h"
#include "table/industry_land.h"
@@ -199,7 +201,7 @@ Industry::~Industry()
CargoPacket::InvalidateAllFrom(ST_INDUSTRY, this->index);
for (Station *st : this->stations_near) {
st->industries_near.erase(this);
st->RemoveIndustryToDeliver(this);
}
citymania::UpdateIndustryHighlight();
}
@@ -1103,7 +1105,7 @@ static bool SearchLumberMillTrees(TileIndex tile, void *user_data)
_industry_sound_tile = tile;
if (_settings_client.sound.ambient) SndPlayTileFx(SND_38_LUMBER_MILL_1, tile);
DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC, tile);
cur_company.Restore();
return true;
@@ -1430,18 +1432,12 @@ bool IsSlopeRefused(Slope current, Slope refused)
* Are the tiles of the industry free?
* @param tile Position to check.
* @param layout Industry tiles table.
* @param layout_index The index of the layout to build/fund
* @param type Type of the industry.
* @param initial_random_bits The random bits the industry is going to have after construction.
* @param founder Industry founder
* @param creation_type The circumstances the industry is created under.
* @param[out] custom_shape_check Perform custom check for the site.
* @return Failed or succeeded command.
*/
static CommandCost CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileLayout &layout, size_t layout_index, int type, uint16 initial_random_bits, Owner founder, IndustryAvailabilityCallType creation_type, bool *custom_shape_check = nullptr)
static CommandCost CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileLayout &layout, IndustryType type)
{
bool refused_slope = false;
bool custom_shape = false;
IndustryBehaviour ind_behav = GetIndustrySpec(type)->behaviour;
for (const IndustryTileLayoutTile &it : layout) {
IndustryGfx gfx = GetTranslatedIndustryTileID(it.gfx);
@@ -1463,20 +1459,9 @@ static CommandCost CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTil
const IndustryTileSpec *its = GetIndustryTileSpec(gfx);
IndustryBehaviour ind_behav = GetIndustrySpec(type)->behaviour;
/* Perform land/water check if not disabled */
if (!HasBit(its->slopes_refused, 5) && ((HasTileWaterClass(cur_tile) && IsTileOnWater(cur_tile)) == !(ind_behav & INDUSTRYBEH_BUILT_ONWATER))) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
if (HasBit(its->callback_mask, CBM_INDT_SHAPE_CHECK)) {
custom_shape = true;
CommandCost ret = PerformIndustryTileSlopeCheck(tile, cur_tile, its, type, gfx, layout_index, initial_random_bits, founder, creation_type);
if (ret.Failed()) return ret;
} else {
Slope tileh = GetTileSlope(cur_tile);
refused_slope |= IsSlopeRefused(tileh, its->slopes_refused);
}
if ((ind_behav & (INDUSTRYBEH_ONLY_INTOWN | INDUSTRYBEH_TOWN1200_MORE)) || // Tile must be a house
((ind_behav & INDUSTRYBEH_ONLY_NEARTOWN) && IsTileType(cur_tile, MP_HOUSE))) { // Tile is allowed to be a house (and it is a house)
if (!IsTileType(cur_tile, MP_HOUSE)) {
@@ -1485,19 +1470,57 @@ static CommandCost CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTil
/* Clear the tiles as OWNER_TOWN to not affect town rating, and to not clear protected buildings */
Backup<CompanyID> cur_company(_current_company, OWNER_TOWN, FILE_LINE);
CommandCost ret = DoCommand(cur_tile, 0, 0, DC_NONE, CMD_LANDSCAPE_CLEAR);
CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(DC_NONE, cur_tile);
cur_company.Restore();
if (ret.Failed()) return ret;
} else {
/* Clear the tiles, but do not affect town ratings */
CommandCost ret = DoCommand(cur_tile, 0, 0, DC_AUTO | DC_NO_TEST_TOWN_RATING | DC_NO_MODIFY_TOWN_RATING, CMD_LANDSCAPE_CLEAR);
CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(DC_AUTO | DC_NO_TEST_TOWN_RATING | DC_NO_MODIFY_TOWN_RATING, cur_tile);
if (ret.Failed()) return ret;
}
}
}
return CommandCost();
}
/**
* Check slope requirements for industry tiles.
* @param tile Position to check.
* @param layout Industry tiles table.
* @param layout_index The index of the layout to build/fund
* @param type Type of the industry.
* @param initial_random_bits The random bits the industry is going to have after construction.
* @param founder Industry founder
* @param creation_type The circumstances the industry is created under.
* @param[out] custom_shape_check Perform custom check for the site.
* @return Failed or succeeded command.
*/
static CommandCost CheckIfIndustryTileSlopes(TileIndex tile, const IndustryTileLayout &layout, size_t layout_index, int type, uint16 initial_random_bits, Owner founder, IndustryAvailabilityCallType creation_type, bool *custom_shape_check = nullptr)
{
bool refused_slope = false;
bool custom_shape = false;
for (const IndustryTileLayoutTile &it : layout) {
IndustryGfx gfx = GetTranslatedIndustryTileID(it.gfx);
TileIndex cur_tile = TileAddWrap(tile, it.ti.x, it.ti.y);
assert(IsValidTile(cur_tile)); // checked before in CheckIfIndustryTilesAreFree
if (gfx != GFX_WATERTILE_SPECIALCHECK) {
const IndustryTileSpec *its = GetIndustryTileSpec(gfx);
if (HasBit(its->callback_mask, CBM_INDT_SHAPE_CHECK)) {
custom_shape = true;
CommandCost ret = PerformIndustryTileSlopeCheck(tile, cur_tile, its, type, gfx, layout_index, initial_random_bits, founder, creation_type);
if (ret.Failed()) return ret;
} else {
Slope tileh = GetTileSlope(cur_tile);
refused_slope |= IsSlopeRefused(tileh, its->slopes_refused);
}
}
}
if (custom_shape_check != nullptr) *custom_shape_check = custom_shape;
/* It is almost impossible to have a fully flat land in TG, so what we
@@ -1601,7 +1624,7 @@ static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, DoCommandFlag flags,
}
/* This is not 100% correct check, but the best we can do without modifying the map.
* What is missing, is if the difference in height is more than 1.. */
if (DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND).Failed()) {
if (std::get<0>(Command<CMD_TERRAFORM_LAND>::Do(flags & ~DC_EXEC, tile_walk, SLOPE_N, curh <= h)).Failed()) {
cur_company.Restore();
return false;
}
@@ -1616,7 +1639,7 @@ static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, DoCommandFlag flags,
/* We give the terraforming for free here, because we can't calculate
* exact cost in the test-round, and as we all know, that will cause
* a nice assert if they don't match ;) */
DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
Command<CMD_TERRAFORM_LAND>::Do(flags, tile_walk, SLOPE_N, curh <= h);
curh += (curh > h) ? -1 : 1;
}
}
@@ -1702,15 +1725,15 @@ static void PopulateStationsNearby(Industry *ind)
/* Industry has a neutral station. Use it and ignore any other nearby stations. */
ind->stations_near.insert(ind->neutral_station);
ind->neutral_station->industries_near.clear();
ind->neutral_station->industries_near.insert(ind);
ind->neutral_station->industries_near.insert(IndustryListEntry{0, ind});
return;
}
ForAllStationsAroundTiles(ind->location, [ind](Station *st, TileIndex tile) {
if (!IsTileType(tile, MP_INDUSTRY) || GetIndustryIndex(tile) != ind->index) return false;
ind->stations_near.insert(st);
st->AddIndustryToDeliver(ind);
return true;
st->AddIndustryToDeliver(ind, tile);
return false;
});
}
@@ -1886,7 +1909,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
WaterClass wc = (IsWaterTile(cur_tile) ? GetWaterClass(cur_tile) : WATER_CLASS_INVALID);
DoCommand(cur_tile, 0, 0, DC_EXEC | DC_NO_TEST_TOWN_RATING | DC_NO_MODIFY_TOWN_RATING, CMD_LANDSCAPE_CLEAR);
Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC | DC_NO_TEST_TOWN_RATING | DC_NO_MODIFY_TOWN_RATING, cur_tile);
MakeIndustry(cur_tile, i->index, it.gfx, Random(), wc);
@@ -1931,28 +1954,11 @@ static CommandCost CreateNewIndustryHelper(TileIndex tile, IndustryType type, Do
{
assert(layout_index < indspec->layouts.size());
const IndustryTileLayout &layout = indspec->layouts[layout_index];
bool custom_shape_check = false;
*ip = nullptr;
std::vector<ClearedObjectArea> object_areas(_cleared_object_areas);
CommandCost ret = CheckIfIndustryTilesAreFree(tile, layout, layout_index, type, random_initial_bits, founder, creation_type, &custom_shape_check);
_cleared_object_areas = object_areas;
if (ret.Failed()) return ret;
if (HasBit(GetIndustrySpec(type)->callback_mask, CBM_IND_LOCATION)) {
ret = CheckIfCallBackAllowsCreation(tile, type, layout_index, random_var8f, random_initial_bits, founder, creation_type);
} else {
ret = _check_new_industry_procs[indspec->check_proc](tile);
}
if (ret.Failed()) return ret;
if (!custom_shape_check && _settings_game.game_creation.land_generator == LG_TERRAGENESIS && _generating_world &&
!_ignore_restrictions && !CheckIfCanLevelIndustryPlatform(tile, DC_NO_WATER, layout, type)) {
return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
}
ret = CheckIfFarEnoughFromConflictingIndustry(tile, type);
/* 1. Cheap: Built-in checks on industry level. */
CommandCost ret = CheckIfFarEnoughFromConflictingIndustry(tile, type);
if (ret.Failed()) return ret;
Town *t = nullptr;
@@ -1963,6 +1969,30 @@ static CommandCost CreateNewIndustryHelper(TileIndex tile, IndustryType type, Do
ret = CheckIfIndustryIsAllowed(tile, type, t);
if (ret.Failed()) return ret;
/* 2. Built-in checks on industry tiles. */
std::vector<ClearedObjectArea> object_areas(_cleared_object_areas);
ret = CheckIfIndustryTilesAreFree(tile, layout, type);
_cleared_object_areas = object_areas;
if (ret.Failed()) return ret;
/* 3. NewGRF-defined checks on industry level. */
if (HasBit(GetIndustrySpec(type)->callback_mask, CBM_IND_LOCATION)) {
ret = CheckIfCallBackAllowsCreation(tile, type, layout_index, random_var8f, random_initial_bits, founder, creation_type);
} else {
ret = _check_new_industry_procs[indspec->check_proc](tile);
}
if (ret.Failed()) return ret;
/* 4. Expensive: NewGRF-defined checks on industry tiles. */
bool custom_shape_check = false;
ret = CheckIfIndustryTileSlopes(tile, layout, layout_index, type, random_initial_bits, founder, creation_type, &custom_shape_check);
if (ret.Failed()) return ret;
if (!custom_shape_check && _settings_game.game_creation.land_generator == LG_TERRAGENESIS && _generating_world &&
!_ignore_restrictions && !CheckIfCanLevelIndustryPlatform(tile, DC_NO_WATER, layout, type)) {
return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
}
if (!Industry::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_INDUSTRIES);
if (flags & DC_EXEC) {
@@ -2010,19 +2040,16 @@ bool CanBuildIndustryOnTile(IndustryType type, TileIndex tile) {
/**
* Build/Fund an industry
* @param tile tile where industry is built
* @param flags of operations to conduct
* @param p1 various bitstuffed elements
* - p1 = (bit 0 - 7) - industry type see build_industry.h and see industry.h
* - p1 = (bit 8 - 15) - first layout to try
* - p1 = (bit 16 ) - 0 = prospect, 1 = fund (only valid if current company is DEITY)
* @param p2 seed to use for desyncfree randomisations
* @param text unused
* @param tile tile where industry is built
* @param it industry type see build_industry.h and see industry.h
* @param first_layout first layout to try
* @param fund false = prospect, true = fund (only valid if current company is DEITY)
* @param seed seed to use for desyncfree randomisations
* @return the cost of this operation or an error
*/
CommandCost CmdBuildIndustry(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdBuildIndustry(DoCommandFlag flags, TileIndex tile, IndustryType it, uint32 first_layout, bool fund, uint32 seed)
{
IndustryType it = GB(p1, 0, 8);
if (it >= NUM_INDUSTRYTYPES) return CMD_ERROR;
const IndustrySpec *indspec = GetIndustrySpec(it);
@@ -2041,12 +2068,12 @@ CommandCost CmdBuildIndustry(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
}
Randomizer randomizer;
randomizer.SetSeed(p2);
uint16 random_initial_bits = GB(p2, 0, 16);
randomizer.SetSeed(seed);
uint16 random_initial_bits = GB(seed, 0, 16);
uint32 random_var8f = randomizer.Next();
size_t num_layouts = indspec->layouts.size();
CommandCost ret = CommandCost(STR_ERROR_SITE_UNSUITABLE);
const bool deity_prospect = _current_company == OWNER_DEITY && !HasBit(p1, 16);
const bool deity_prospect = _current_company == OWNER_DEITY && !fund;
Industry *ind = nullptr;
if (deity_prospect || (_game_mode != GM_EDITOR && _current_company != OWNER_DEITY && _settings_game.construction.raw_industry_construction == 2 && indspec->IsRawIndustry())) {
@@ -2076,7 +2103,7 @@ CommandCost CmdBuildIndustry(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
cur_company.Restore();
}
} else {
size_t layout = GB(p1, 8, 8);
size_t layout = first_layout;
if (layout >= num_layouts) return CMD_ERROR;
/* Check subsequently each layout, starting with the given layout in p1 */
@@ -2099,41 +2126,32 @@ CommandCost CmdBuildIndustry(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
/**
* Change industry properties
* @param tile Unused.
* @param flags Type of operation.
* @param p1 IndustryID
* @param p2 various bitstuffed elements
* - p2 = (bit 0 - 7) - IndustryAction to perform
* - p2 = (bit 8 - 15) - IndustryControlFlags
* (only used with set control flags)
* - p2 = (bit 16 - 23) - CompanyID to set or INVALID_OWNER (available to everyone) or
* OWNER_NONE (neutral stations only) or OWNER_DEITY (no one)
* (only used with set exclusive supplier / consumer)
* @param ind_id IndustryID
* @param action IndustryAction to perform
* @param ctlflags IndustryControlFlags (only used with set control flags)
* @param company_id CompanyID to set or INVALID_OWNER (available to everyone) or
* OWNER_NONE (neutral stations only) or OWNER_DEITY (no one)
* (only used with set exclusive supplier / consumer)
* @param text - Additional industry text (only used with set text action)
* @return Empty cost or an error.
*/
CommandCost CmdIndustryCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
CommandCost CmdIndustryCtrl(DoCommandFlag flags, IndustryID ind_id, IndustryAction action, IndustryControlFlags ctlflags, Owner company_id, const std::string &text)
{
if (_current_company != OWNER_DEITY) return CMD_ERROR;
Industry *ind = Industry::GetIfValid(p1);
Industry *ind = Industry::GetIfValid(ind_id);
if (ind == nullptr) return CMD_ERROR;
auto action = static_cast<IndustryAction>(GB(p2, 0, 8));
switch (action) {
case IndustryAction::SetControlFlags: {
IndustryControlFlags ctlflags = (IndustryControlFlags)GB(p2, 8, 8) & INDCTL_MASK;
if (flags & DC_EXEC) ind->ctlflags = ctlflags;
if (flags & DC_EXEC) ind->ctlflags = ctlflags & INDCTL_MASK;
break;
}
case IndustryAction::SetExclusiveSupplier:
case IndustryAction::SetExclusiveConsumer: {
Owner company_id = (Owner)GB(p2, 16, 8);
if (company_id != OWNER_NONE && company_id != INVALID_OWNER && company_id != OWNER_DEITY
&& !Company::IsValidID(company_id)) return CMD_ERROR;
@@ -3097,7 +3115,7 @@ static CommandCost TerraformTile_Industry(TileIndex tile, DoCommandFlag flags, i
}
}
}
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
}
extern const TileTypeProcs _tile_type_industry_procs = {
@@ -3117,7 +3135,8 @@ extern const TileTypeProcs _tile_type_industry_procs = {
TerraformTile_Industry, // terraform_tile_proc
};
bool IndustryCompare::operator() (const Industry *lhs, const Industry *rhs) const
bool IndustryCompare::operator() (const IndustryListEntry &lhs, const IndustryListEntry &rhs) const
{
return lhs->index < rhs->index;
/* Compare by distance first and use index as a tiebreaker. */
return std::tie(lhs.distance, lhs.industry->index) < std::tie(rhs.distance, rhs.industry->index);
}