File diff r7649:de7392d40f90 → r7650:d12631d10c9c
src/industry_cmd.cpp
Show inline comments
 
@@ -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