|
@@ -1270,384 +1270,389 @@ static bool CheckIfIndustryIsAllowed(Til
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static bool CheckCanTerraformSurroundingTiles(TileIndex tile, uint height, int internal)
|
|
|
{
|
|
|
int size_x, size_y;
|
|
|
uint curh;
|
|
|
|
|
|
size_x = 2;
|
|
|
size_y = 2;
|
|
|
|
|
|
/* Check if we don't leave the map */
|
|
|
if (TileX(tile) == 0 || TileY(tile) == 0 || GetTileType(tile) == MP_VOID) return false;
|
|
|
|
|
|
tile += TileDiffXY(-1, -1);
|
|
|
BEGIN_TILE_LOOP(tile_walk, size_x, size_y, tile) {
|
|
|
curh = TileHeight(tile_walk);
|
|
|
/* Is the tile clear? */
|
|
|
if ((GetTileType(tile_walk) != MP_CLEAR) && (GetTileType(tile_walk) != MP_TREES))
|
|
|
return false;
|
|
|
|
|
|
/* Don't allow too big of a change if this is the sub-tile check */
|
|
|
if (internal != 0 && delta(curh, height) > 1) return false;
|
|
|
|
|
|
/* Different height, so the surrounding tiles of this tile
|
|
|
* has to be correct too (in level, or almost in level)
|
|
|
* else you get a chain-reaction of terraforming. */
|
|
|
if (internal == 0 && curh != height) {
|
|
|
if (!CheckCanTerraformSurroundingTiles(tile_walk + TileDiffXY(-1, -1), height, internal + 1))
|
|
|
return false;
|
|
|
}
|
|
|
} END_TILE_LOOP(tile_walk, size_x, size_y, tile);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* This function tries to flatten out the land below an industry, without
|
|
|
* damaging the surroundings too much.
|
|
|
*/
|
|
|
static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, uint32 flags, const IndustryTileTable* it, int type)
|
|
|
{
|
|
|
const int MKEND = -0x80; // used for last element in an IndustryTileTable (see build_industry.h)
|
|
|
int max_x = 0;
|
|
|
int max_y = 0;
|
|
|
TileIndex cur_tile;
|
|
|
uint size_x, size_y;
|
|
|
uint h, curh;
|
|
|
|
|
|
/* Finds dimensions of largest variant of this industry */
|
|
|
do {
|
|
|
if (it->gfx == 0xFF) continue; // FF been a marquer for a check on clear water, skip it
|
|
|
if (it->ti.x > max_x) max_x = it->ti.x;
|
|
|
if (it->ti.y > max_y) max_y = it->ti.y;
|
|
|
} while ((++it)->ti.x != MKEND);
|
|
|
|
|
|
/* Remember level height */
|
|
|
h = TileHeight(tile);
|
|
|
|
|
|
/* Check that all tiles in area and surrounding are clear
|
|
|
* this determines that there are no obstructing items */
|
|
|
cur_tile = tile + TileDiffXY(-1, -1);
|
|
|
size_x = max_x + 4;
|
|
|
size_y = max_y + 4;
|
|
|
|
|
|
/* Check if we don't leave the map */
|
|
|
if (TileX(cur_tile) == 0 || TileY(cur_tile) == 0 || TileX(cur_tile) + size_x >= MapMaxX() || TileY(cur_tile) + size_y >= MapMaxY()) return false;
|
|
|
|
|
|
/* _current_player is OWNER_NONE for randomly generated industries and in editor, or the player who funded or prospected the industry.
|
|
|
* Perform terraforming as OWNER_TOWN to disable autoslope. */
|
|
|
PlayerID old_player = _current_player;
|
|
|
_current_player = OWNER_TOWN;
|
|
|
|
|
|
BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
|
|
|
curh = TileHeight(tile_walk);
|
|
|
if (curh != h) {
|
|
|
/* This tile needs terraforming. Check if we can do that without
|
|
|
* damaging the surroundings too much. */
|
|
|
if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) {
|
|
|
_current_player = old_player;
|
|
|
return false;
|
|
|
}
|
|
|
/* 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 (CmdFailed(DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND))) {
|
|
|
_current_player = old_player;
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
} END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile)
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
/* Terraform the land under the industry */
|
|
|
BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
|
|
|
curh = TileHeight(tile_walk);
|
|
|
while (curh != h) {
|
|
|
/* 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);
|
|
|
curh += (curh > h) ? -1 : 1;
|
|
|
}
|
|
|
} END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile)
|
|
|
}
|
|
|
|
|
|
_current_player = old_player;
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
static bool CheckIfTooCloseToIndustry(TileIndex tile, int type)
|
|
|
{
|
|
|
const IndustrySpec *indspec = GetIndustrySpec(type);
|
|
|
const Industry *i;
|
|
|
|
|
|
/* accepting industries won't be close, not even with patch */
|
|
|
if (_patches.same_industry_close && indspec->accepts_cargo[0] == CT_INVALID)
|
|
|
return true;
|
|
|
|
|
|
FOR_ALL_INDUSTRIES(i) {
|
|
|
/* check if an industry that accepts the same goods is nearby */
|
|
|
if (DistanceMax(tile, i->xy) <= 14 &&
|
|
|
indspec->accepts_cargo[0] != CT_INVALID &&
|
|
|
indspec->accepts_cargo[0] == i->accepts_cargo[0] && (
|
|
|
_game_mode != GM_EDITOR ||
|
|
|
!_patches.same_industry_close ||
|
|
|
!_patches.multiple_industry_per_town
|
|
|
)) {
|
|
|
_error_message = STR_INDUSTRY_TOO_CLOSE;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/* check "not close to" field. */
|
|
|
if ((i->type == indspec->conflicting[0] || i->type == indspec->conflicting[1] || i->type == indspec->conflicting[2]) &&
|
|
|
DistanceMax(tile, i->xy) <= 14) {
|
|
|
_error_message = STR_INDUSTRY_TOO_CLOSE;
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const IndustryTileTable *it, byte layout, const Town *t, Owner owner)
|
|
|
{
|
|
|
const IndustrySpec *indspec = GetIndustrySpec(type);
|
|
|
uint32 r;
|
|
|
uint j;
|
|
|
|
|
|
i->xy = tile;
|
|
|
i->width = i->height = 0;
|
|
|
i->type = type;
|
|
|
IncIndustryTypeCount(type);
|
|
|
|
|
|
i->produced_cargo[0] = indspec->produced_cargo[0];
|
|
|
i->produced_cargo[1] = indspec->produced_cargo[1];
|
|
|
i->accepts_cargo[0] = indspec->accepts_cargo[0];
|
|
|
i->accepts_cargo[1] = indspec->accepts_cargo[1];
|
|
|
i->accepts_cargo[2] = indspec->accepts_cargo[2];
|
|
|
i->production_rate[0] = indspec->production_rate[0];
|
|
|
i->production_rate[1] = indspec->production_rate[1];
|
|
|
|
|
|
if (_patches.smooth_economy) {
|
|
|
i->production_rate[0] = min((RandomRange(256) + 128) * i->production_rate[0] >> 8 , 255);
|
|
|
i->production_rate[1] = min((RandomRange(256) + 128) * i->production_rate[1] >> 8 , 255);
|
|
|
}
|
|
|
|
|
|
i->town = t;
|
|
|
i->owner = owner;
|
|
|
|
|
|
r = Random();
|
|
|
i->random_color = GB(r, 8, 4);
|
|
|
i->counter = GB(r, 0, 12);
|
|
|
i->produced_cargo_waiting[0] = 0;
|
|
|
i->produced_cargo_waiting[1] = 0;
|
|
|
i->incoming_cargo_waiting[0] = 0;
|
|
|
i->incoming_cargo_waiting[1] = 0;
|
|
|
i->incoming_cargo_waiting[2] = 0;
|
|
|
i->this_month_production[0] = 0;
|
|
|
i->this_month_production[1] = 0;
|
|
|
i->this_month_transported[0] = 0;
|
|
|
i->this_month_transported[1] = 0;
|
|
|
i->last_month_pct_transported[0] = 0;
|
|
|
i->last_month_pct_transported[1] = 0;
|
|
|
i->last_month_transported[0] = 0;
|
|
|
i->last_month_transported[1] = 0;
|
|
|
i->was_cargo_delivered = false;
|
|
|
i->last_prod_year = _cur_year;
|
|
|
i->last_month_production[0] = i->production_rate[0] * 8;
|
|
|
i->last_month_production[1] = i->production_rate[1] * 8;
|
|
|
i->founder = _current_player;
|
|
|
|
|
|
if (HASBIT(indspec->callback_flags, CBM_IND_DECIDE_COLOUR)) {
|
|
|
uint16 res = GetIndustryCallback(CBID_INDUSTRY_DECIDE_COLOUR, 0, 0, i, type, INVALID_TILE);
|
|
|
if (res != CALLBACK_FAILED) i->random_color = GB(res, 0, 4);
|
|
|
}
|
|
|
|
|
|
if (HASBIT(indspec->callback_flags, CBM_IND_INPUT_CARGO_TYPES)) {
|
|
|
for (j = 0; j < lengthof(i->accepts_cargo); j++) i->accepts_cargo[j] = CT_INVALID;
|
|
|
for (j = 0; j < lengthof(i->accepts_cargo); j++) {
|
|
|
uint16 res = GetIndustryCallback(CBID_INDUSTRY_INPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE);
|
|
|
if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break;
|
|
|
i->accepts_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (HASBIT(indspec->callback_flags, CBM_IND_OUTPUT_CARGO_TYPES)) {
|
|
|
for (j = 0; j < lengthof(i->produced_cargo); j++) i->produced_cargo[j] = CT_INVALID;
|
|
|
for (j = 0; j < lengthof(i->produced_cargo); j++) {
|
|
|
uint16 res = GetIndustryCallback(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE);
|
|
|
if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break;
|
|
|
i->produced_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
i->construction_date = _date;
|
|
|
i->construction_type = (_game_mode == GM_EDITOR) ? ICT_SCENARIO_EDITOR :
|
|
|
(_generating_world ? ICT_MAP_GENERATION : ICT_NORMAL_GAMEPLAY);
|
|
|
|
|
|
/* Adding 1 here makes it conform to specs of var44 of varaction2 for industries
|
|
|
* 0 = created prior of newindustries
|
|
|
* else, chosen layout + 1 */
|
|
|
i->selected_layout = layout + 1;
|
|
|
|
|
|
if (!_generating_world) i->last_month_production[0] = i->last_month_production[1] = 0;
|
|
|
|
|
|
i->prod_level = 0x10;
|
|
|
|
|
|
do {
|
|
|
TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
|
|
|
|
|
|
if (it->gfx != GFX_WATERTILE_SPECIALCHECK) {
|
|
|
byte size;
|
|
|
|
|
|
size = it->ti.x;
|
|
|
if (size > i->width) i->width = size;
|
|
|
size = it->ti.y;
|
|
|
if (size > i->height)i->height = size;
|
|
|
|
|
|
DoCommand(cur_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
|
|
|
|
|
|
MakeIndustry(cur_tile, i->index, it->gfx);
|
|
|
if (_generating_world) {
|
|
|
SetIndustryConstructionCounter(cur_tile, 3);
|
|
|
SetIndustryConstructionStage(cur_tile, 2);
|
|
|
}
|
|
|
}
|
|
|
} while ((++it)->ti.x != -0x80);
|
|
|
|
|
|
i->width++;
|
|
|
i->height++;
|
|
|
|
|
|
if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) {
|
|
|
for (j = 0; j != 50; j++) PlantRandomFarmField(i);
|
|
|
}
|
|
|
_industry_sort_dirty = true;
|
|
|
InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0);
|
|
|
}
|
|
|
|
|
|
/** Helper function for Build/Fund an industry
|
|
|
* @param tile tile where industry is built
|
|
|
* @param type of industry to build
|
|
|
* @param flags of operations to conduct
|
|
|
* @param indspec pointer to industry specifications
|
|
|
* @param itspec_index the index of the itsepc to build/fund
|
|
|
* @return the pointer of the newly created industry, or NULL if it failed
|
|
|
*/
|
|
|
static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, uint32 flags, const IndustrySpec *indspec, uint itspec_index)
|
|
|
{
|
|
|
const IndustryTileTable *it = indspec->table[itspec_index];
|
|
|
bool custom_shape_check = false;
|
|
|
|
|
|
if (!CheckIfIndustryTilesAreFree(tile, it, type, &custom_shape_check)) return NULL;
|
|
|
|
|
|
if (HASBIT(GetIndustrySpec(type)->callback_flags, CBM_IND_LOCATION)) {
|
|
|
if (!CheckIfCallBackAllowsCreation(tile, type, itspec_index)) return NULL;
|
|
|
} else {
|
|
|
if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL;
|
|
|
}
|
|
|
|
|
|
if (!custom_shape_check && _patches.land_generator == LG_TERRAGENESIS && _generating_world && !CheckIfCanLevelIndustryPlatform(tile, 0, it, type)) return NULL;
|
|
|
if (!CheckIfTooCloseToIndustry(tile, type)) return NULL;
|
|
|
|
|
|
const Town *t = CheckMultipleIndustryInTown(tile, type);
|
|
|
if (t == NULL) return NULL;
|
|
|
|
|
|
if (!CheckIfIndustryIsAllowed(tile, type, t)) return NULL;
|
|
|
if (!CheckSuitableIndustryPos(tile)) return NULL;
|
|
|
|
|
|
Industry *i = new Industry(tile);
|
|
|
if (i == NULL) return NULL;
|
|
|
AutoPtrT<Industry> i_auto_delete = i;
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
if (!custom_shape_check) CheckIfCanLevelIndustryPlatform(tile, DC_EXEC, it, type);
|
|
|
DoCreateNewIndustry(i, tile, type, it, itspec_index, t, OWNER_NONE);
|
|
|
i_auto_delete.Detach();
|
|
|
}
|
|
|
|
|
|
return i;
|
|
|
}
|
|
|
|
|
|
/** Build/Fund an industry
|
|
|
* @param tile tile where industry is built
|
|
|
* @param flags of operations to conduct
|
|
|
* @param p1 industry type see build_industry.h and see industry.h
|
|
|
* @param p2 unused
|
|
|
* @return index of the newly create industry, or CMD_ERROR if it failed
|
|
|
*/
|
|
|
CommandCost CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
|
|
{
|
|
|
int num;
|
|
|
const IndustryTileTable * const *itt;
|
|
|
const IndustryTileTable *it;
|
|
|
const IndustrySpec *indspec;
|
|
|
|
|
|
SET_EXPENSES_TYPE(EXPENSES_OTHER);
|
|
|
|
|
|
indspec = GetIndustrySpec(p1);
|
|
|
|
|
|
/* Check if the to-be built/founded industry is available for this climate. */
|
|
|
if (!indspec->enabled) {
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
/* If the patch for raw-material industries is not on, you cannot build raw-material industries.
|
|
|
* Raw material industries are industries that do not accept cargo (at least for now) */
|
|
|
if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 0 && indspec->IsRawIndustry()) {
|
|
|
return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && indspec->IsRawIndustry()) {
|
|
|
if (flags & DC_EXEC) {
|
|
|
/* Prospecting has a chance to fail, however we cannot guarantee that something can
|
|
|
* be built on the map, so the chance gets lower when the map is fuller, but there
|
|
|
* is nothing we can really do about that. */
|
|
|
if (Random() <= indspec->prospecting_chance) {
|
|
|
for (int i = 0; i < 5000; i++) {
|
|
|
uint tilespec_index = RandomRange(indspec->num_table);
|
|
|
const Industry *ind = CreateNewIndustryHelper(RandomTile(), p1, flags, indspec, tilespec_index);
|
|
|
if (ind != NULL) {
|
|
|
SetDParam(0, indspec->name);
|
|
|
SetDParam(1, ind->town->index);
|
|
|
AddNewsItem(indspec->new_industry_text,
|
|
|
NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_OPENCLOSE, 0), ind->xy, 0);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
num = indspec->num_table;
|
|
|
itt = indspec->table;
|
|
|
|
|
|
|
|
|
do {
|
|
|
if (--num < 0) return_cmd_error(STR_0239_SITE_UNSUITABLE);
|
|
|
} while (!CheckIfIndustryTilesAreFree(tile, it = itt[num], p1));
|
|
|
|
|
|
if (CreateNewIndustryHelper(tile, p1, flags, indspec, num) == NULL) return CMD_ERROR;
|
|
|
}
|
|
|
|
|
|
return CommandCost(indspec->GetConstructionCost());
|
|
|
}
|
|
|
|
|
|
|
|
|
Industry *CreateNewIndustry(TileIndex tile, IndustryType type)
|
|
|
{
|
|
|
const IndustrySpec *indspec = GetIndustrySpec(type);
|
|
|
|
|
|
return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, RandomRange(indspec->num_table));
|
|
|
}
|
|
|
|
|
|
enum {
|
|
|
NB_NUMOFINDUSTRY = 11,
|
|
|
NB_DIFFICULTY_LEVEL = 5,
|
|
|
};
|
|
|
|
|
|
static const byte _numof_industry_table[NB_DIFFICULTY_LEVEL][NB_NUMOFINDUSTRY] = {
|
|
|
/* difficulty settings for number of industries */
|
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //none
|
|
|
{0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, //very low
|
|
|
{0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5}, //low
|
|
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, //normal
|
|
|
{0, 2, 3, 4, 6, 7, 8, 9, 10, 10, 10}, //high
|
|
|
};
|
|
|
|
|
|
/** This function is the one who really do the creation work
|
|
|
* of random industries during game creation
|