Changeset - r16449:ac672cccbcf8
[Not reviewed]
master
0 1 0
alberth - 14 years ago 2010-11-13 15:21:55
alberth@openttd.org
(svn r21175) -Add: Forced construction of missing industries.
1 file changed with 49 insertions and 21 deletions:
0 comments (0 inline, 0 general)
src/industry_cmd.cpp
Show inline comments
 
@@ -1846,62 +1846,68 @@ static Industry *CreateNewIndustry(TileI
 
 * @return Relative probability for the industry to appear.
 
 */
 
static uint32 GetScaledIndustryGenerationProbability(IndustryType it, bool *force_at_least_one)
 
{
 
	const IndustrySpec *ind_spc = GetIndustrySpec(it);
 
	uint32 chance = ind_spc->appear_creation[_settings_game.game_creation.landscape] * 16; // * 16 to increase precision
 
	if (!ind_spc->enabled || chance == 0 || ind_spc->num_table == 0 ||
 
			!CheckIfCallBackAllowsAvailability(it, IACT_MAPGENERATION) ||
 
			(_game_mode != GM_EDITOR && _settings_game.difficulty.number_industries == 0)) {
 
		*force_at_least_one = false;
 
		return 0;
 
	} else {
 
		/* We want industries appearing at coast to appear less often on bigger maps, as length of coast increases slower than map area.
 
		 * For simplicity we scale in both cases, though scaling the probabilities of all industries has no effect. */
 
		chance = (ind_spc->check_proc == CHECK_REFINERY || ind_spc->check_proc == CHECK_OIL_RIG) ? ScaleByMapSize1D(chance) : ScaleByMapSize(chance);
 

	
 
		*force_at_least_one = (chance > 0) && !(ind_spc->behaviour & INDUSTRYBEH_NOBUILT_MAPCREATION) && (_game_mode != GM_EDITOR);
 
		return chance;
 
	}
 
}
 

	
 
/**
 
 * Compute the probability for constructing a new industry during game play.
 
 * @param it Industry type to compute.
 
 * @param [out] min_number Minimal number of industries that should exist at the map.
 
 * @return Relative probability for the industry to appear.
 
 */
 
static uint16 GetIndustryGamePlayProbability(IndustryType it)
 
static uint16 GetIndustryGamePlayProbability(IndustryType it, byte *min_number)
 
{
 
	if (_settings_game.difficulty.number_industries == 0) return 0;
 
	if (_settings_game.difficulty.number_industries == 0) {
 
		*min_number = 0;
 
		return 0;
 
	}
 

	
 
	const IndustrySpec *ind_spc = GetIndustrySpec(it);
 
	byte chance = ind_spc->appear_ingame[_settings_game.game_creation.landscape];
 
	if (!ind_spc->enabled || chance == 0 || ind_spc->num_table == 0 ||
 
			((ind_spc->behaviour & INDUSTRYBEH_BEFORE_1950) && _cur_year > 1950) ||
 
			((ind_spc->behaviour & INDUSTRYBEH_AFTER_1960) && _cur_year < 1960) ||
 
			!CheckIfCallBackAllowsAvailability(it, IACT_RANDOMCREATION)) {
 
		*min_number = 0;
 
		return 0;
 
	}
 
	*min_number = (ind_spc->behaviour & INDUSTRYBEH_CANCLOSE_LASTINSTANCE) ? 1 : 0;
 
	return chance;
 
}
 

	
 
/**
 
 * Get wanted number of industries on the map.
 
 * @return Wanted number of industries at the map.
 
 */
 
static uint GetNumberOfIndustries()
 
{
 
	/* Number of industries on a 256x256 map. */
 
	static const uint16 numof_industry_table[] = {
 
		0,    // none
 
		10,   // very low
 
		25,   // low
 
		55,   // normal
 
		80,   // high
 
	};
 

	
 
	assert(_settings_game.difficulty.number_industries < lengthof(numof_industry_table));
 
	uint difficulty = (_game_mode != GM_EDITOR) ? _settings_game.difficulty.number_industries : 1;
 
	return ScaleByMapSize(numof_industry_table[difficulty]);
 
}
 

	
 
/**
 
@@ -1950,48 +1956,49 @@ static void PlaceInitialIndustry(Industr
 
	Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
 

	
 
	IncreaseGeneratingWorldProgress(GWP_INDUSTRY);
 
	PlaceIndustry(type, IACT_MAPGENERATION, try_hard);
 

	
 
	cur_company.Restore();
 
}
 

	
 
/**
 
 * Get total number of industries existing in the game.
 
 * @return Number of industries currently in the game.
 
 */
 
static uint GetCurrentTotalNumberOfIndustries()
 
{
 
	int total = 0;
 
	for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) total += Industry::GetIndustryTypeCount(it);
 
	return total;
 
}
 

	
 

	
 
/** Reset the entry. */
 
void IndustryTypeBuildData::Reset()
 
{
 
	this->probability  = 0;
 
	this->min_number   = 0;
 
	this->target_count = 0;
 
	this->max_wait     = 1;
 
	this->wait_count   = 0;
 
}
 

	
 
/** Completely reset the industry build data. */
 
void IndustryBuildData::Reset()
 
{
 
	this->wanted_inds = GetCurrentTotalNumberOfIndustries() << 16;
 

	
 
	for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
		this->builddata[it].Reset();
 
	}
 
}
 

	
 
/** Monthly update of industry build data. */
 
void IndustryBuildData::MonthlyLoop()
 
{
 
	static const int NEWINDS_PER_MONTH = 0x38000 / (10 * 12); // lower 16 bits is a float fraction, 3.5 industries per decade, divided by 10 * 12 months.
 
	if (_settings_game.difficulty.number_industries == 0) return; // 'no industries' setting,
 

	
 
	/* To prevent running out of unused industries for the player to connect,
 
	 * add a fraction of new industries each month, but only if the manager can keep up. */
 
	uint max_behind = 1 + min(99u, ScaleByMapSize(3)); // At most 2 industries for small maps, and 100 at the biggest map (about 6 months industry build attempts).
 
@@ -2072,132 +2079,153 @@ static void UpdateIndustryStatistics(Ind
 
			i->this_month_production[j] = 0;
 

	
 
			i->last_month_transported[j] = i->this_month_transported[j];
 
			i->this_month_transported[j] = 0;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Recompute #production_rate for current #prod_level.
 
 * This function is only valid when not using smooth economy.
 
 */
 
void Industry::RecomputeProductionMultipliers()
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(this->type);
 
	assert(!indspec->UsesSmoothEconomy());
 

	
 
	/* Rates are rounded up, so e.g. oilrig always produces some passengers */
 
	this->production_rate[0] = min(CeilDiv(indspec->production_rate[0] * this->prod_level, PRODLEVEL_DEFAULT), 0xFF);
 
	this->production_rate[1] = min(CeilDiv(indspec->production_rate[1] * this->prod_level, PRODLEVEL_DEFAULT), 0xFF);
 
}
 

	
 

	
 
/**
 
 * Set the #probability field for the industry type \a it for a running game.
 
 * Set the #probability and #min_number fields for the industry type \a it for a running game.
 
 * @param it Industry type.
 
 * @return The field has changed value.
 
 * @return At least one of the fields has changed value.
 
 */
 
bool IndustryTypeBuildData::GetIndustryTypeData(IndustryType it)
 
{
 
	uint32 probability = GetIndustryGamePlayProbability(it);
 
	bool changed = probability != this->probability;
 
	byte min_number;
 
	uint32 probability = GetIndustryGamePlayProbability(it, &min_number);
 
	bool changed = min_number != this->min_number || probability != this->probability;
 
	this->min_number = min_number;
 
	this->probability = probability;
 
	return changed;
 
}
 

	
 
/** Decide how many industries of each type are needed. */
 
void IndustryBuildData::SetupTargetCount()
 
{
 
	bool changed = false;
 
	uint num_planned = 0; // Number of industries planned in the industry build data.
 
	for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
		changed |= this->builddata[it].GetIndustryTypeData(it);
 
		num_planned += this->builddata[it].target_count;
 
	}
 
	uint total_amount = this->wanted_inds >> 16; // Desired total number of industries.
 
	changed |= num_planned != total_amount;
 
	if (!changed) return; // All industries are still the same, no need to re-randomize.
 

	
 
	/* Initialize the target counts. */
 
	uint force_build = 0;  // Number of industries that should always be available.
 
	uint32 total_prob = 0; // Sum of probabilities.
 
	for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
		this->builddata[it].target_count = 0;
 
		total_prob += this->builddata[it].probability;
 
		IndustryTypeBuildData *ibd = this->builddata + it;
 
		force_build += ibd->min_number;
 
		ibd->target_count = ibd->min_number;
 
		total_prob += ibd->probability;
 
	}
 

	
 
	/* Subtract forced industries from the number of industries available for construction. */
 
	total_amount = (total_amount <= force_build) ? 0 : total_amount - force_build;
 

	
 
	/* Assign number of industries that should be aimed for, by using the probability as a weight. */
 
	while (total_amount > 0) {
 
		uint32 r = RandomRange(total_prob);
 
		IndustryType it = 0;
 
		while (r >= this->builddata[it].probability) {
 
			r -= this->builddata[it].probability;
 
			it++;
 
			assert(it < NUM_INDUSTRYTYPES);
 
		}
 
		assert(this->builddata[it].probability > 0);
 
		this->builddata[it].target_count++;
 
		total_amount--;
 
	}
 
}
 

	
 
/**
 
 * Try to create a random industry, during gameplay
 
 */
 
void IndustryBuildData::TryBuildNewIndustry()
 
{
 
	this->SetupTargetCount();
 

	
 
	int missing = 0;       // Number of industries that need to be build.
 
	uint count = 0;        // Number of industry types eligible for build.
 
	uint32 total_prob = 0; // Sum of probabilities.
 
	IndustryType forced_build = NUM_INDUSTRYTYPES; // Industry type that should be forcibly build.
 
	for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
		int difference = this->builddata[it].target_count - Industry::GetIndustryTypeCount(it);
 
		missing += difference;
 
		if (this->builddata[it].wait_count > 0) continue; // This type may not be built now.
 
		if (difference > 0) {
 
			if (Industry::GetIndustryTypeCount(it) == 0 && this->builddata[it].min_number > 0) {
 
				/* An industry that should exist at least once, is not available. Force it, trying the most needed one first. */
 
				if (forced_build == NUM_INDUSTRYTYPES ||
 
						difference > this->builddata[forced_build].target_count - Industry::GetIndustryTypeCount(forced_build)) {
 
					forced_build = it;
 
				}
 
			}
 
			total_prob += difference;
 
			count++;
 
		}
 
	}
 

	
 
	if (EconomyIsInRecession() || missing <= 0 || total_prob == 0) count = 0; // Skip creation of an industry.
 
	if (EconomyIsInRecession() || (forced_build == NUM_INDUSTRYTYPES && (missing <= 0 || total_prob == 0))) count = 0; // Skip creation of an industry.
 

	
 
	if (count >= 1) {
 
		/* Pick a weighted random industry to build.
 
		/* If not forced, pick a weighted random industry to build.
 
		 * For the case that count == 1, there is no need to draw a random number. */
 
		IndustryType it;
 
		/* Select an industry type to build (weighted random). */
 
		uint32 r = 0; // Initialized to silence the compiler.
 
		if (count > 1) r = RandomRange(total_prob);
 
		for (it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
			if (this->builddata[it].wait_count > 0) continue; // Type may not be built now.
 
			int difference = this->builddata[it].target_count - Industry::GetIndustryTypeCount(it);
 
			if (difference <= 0) continue; // Too many of this kind.
 
			if (count == 1) break;
 
			if (r < (uint)difference) break;
 
			r -= difference;
 
		if (forced_build != NUM_INDUSTRYTYPES) {
 
			it = forced_build;
 
		} else {
 
			/* Non-forced, select an industry type to build (weighted random). */
 
			uint32 r = 0; // Initialized to silence the compiler.
 
			if (count > 1) r = RandomRange(total_prob);
 
			for (it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
				if (this->builddata[it].wait_count > 0) continue; // Type may not be built now.
 
				int difference = this->builddata[it].target_count - Industry::GetIndustryTypeCount(it);
 
				if (difference <= 0) continue; // Too many of this kind.
 
				if (count == 1) break;
 
				if (r < (uint)difference) break;
 
				r -= difference;
 
			}
 
			assert(it < NUM_INDUSTRYTYPES && this->builddata[it].target_count > Industry::GetIndustryTypeCount(it));
 
		}
 
		assert(it < NUM_INDUSTRYTYPES && this->builddata[it].target_count > Industry::GetIndustryTypeCount(it));
 

	
 
		/* Try to create the industry. */
 
		const Industry *ind = PlaceIndustry(it, IACT_RANDOMCREATION, false);
 
		if (ind == NULL) {
 
			this->builddata[it].wait_count = this->builddata[it].max_wait + 1; // Compensate for decrementing below.
 
			this->builddata[it].max_wait = min(1000, this->builddata[it].max_wait + 2);
 
		} else {
 
			AdvertiseIndustryOpening(ind);
 
			this->builddata[it].max_wait = max(this->builddata[it].max_wait / 2, 1); // Reduce waiting time of the industry type.
 
		}
 
	}
 

	
 
	/* Decrement wait counters. */
 
	for (IndustryType it = 0; it < NUM_INDUSTRYTYPES; it++) {
 
		if (this->builddata[it].wait_count > 0) this->builddata[it].wait_count--;
 
	}
 
}
 

	
 
/**
 
 * Protects an industry from closure if the appropriate flags and conditions are met
 
 * INDUSTRYBEH_CANCLOSE_LASTINSTANCE must be set (which, by default, it is not) and the
 
 * count of industries of this type must one (or lower) in order to be protected
 
 * against closure.
 
 * @param type IndustryType been queried
0 comments (0 inline, 0 general)