Changeset - r27438:22e72ba90974
[Not reviewed]
master
0 16 0
PeterN - 16 months ago 2023-05-25 20:25:46
peter1138@openttd.org
Change: Reorganise industry accept/produce arrays. (#10853)

Use a array of struct for each cargo instead of an array for each statistic.
This makes iterating for acceptance and production much simpler.
pct_transported is now calculated when needed.
16 files changed with 531 insertions and 359 deletions:
0 comments (0 inline, 0 general)
src/economy.cpp
Show inline comments
 
@@ -1008,39 +1008,39 @@ static uint DeliverGoodsToIndustry(const
 
	 *  2) The industries in the catchment area temporarily reject the cargo, and the daily station loop has not yet updated station acceptance.
 
	 *  3) The results of callbacks CBID_INDUSTRY_REFUSE_CARGO and CBID_INDTILE_CARGO_ACCEPTANCE are inconsistent. (documented behaviour)
 
	 */
 

	
 
	uint accepted = 0;
 

	
 
	for (const auto &i : st->industries_near) {
 
		if (num_pieces == 0) break;
 

	
 
		Industry *ind = i.industry;
 
		if (ind->index == source) continue;
 

	
 
		int cargo_index = ind->GetCargoAcceptedIndex(cargo_type);
 
		auto it = ind->GetCargoAccepted(cargo_type);
 
		/* Check if matching cargo has been found */
 
		if (cargo_index < 0) continue;
 
		if (it == std::end(ind->accepted)) continue;
 

	
 
		/* Check if industry temporarily refuses acceptance */
 
		if (IndustryTemporarilyRefusesCargo(ind, cargo_type)) continue;
 

	
 
		if (ind->exclusive_supplier != INVALID_OWNER && ind->exclusive_supplier != st->owner) continue;
 

	
 
		/* Insert the industry into _cargo_delivery_destinations, if not yet contained */
 
		include(_cargo_delivery_destinations, ind);
 

	
 
		uint amount = std::min(num_pieces, 0xFFFFu - ind->incoming_cargo_waiting[cargo_index]);
 
		ind->incoming_cargo_waiting[cargo_index] += amount;
 
		ind->last_cargo_accepted_at[cargo_index] = TimerGameCalendar::date;
 
		uint amount = std::min(num_pieces, 0xFFFFu - it->waiting);
 
		it->waiting += amount;
 
		it->last_accepted = TimerGameCalendar::date;
 
		num_pieces -= amount;
 
		accepted += amount;
 

	
 
		/* Update the cargo monitor. */
 
		AddCargoDelivery(cargo_type, company, amount, SourceType::Industry, source, st, ind->index);
 
	}
 

	
 
	return accepted;
 
}
 

	
 
/**
 
 * Delivers goods to industries/towns and calculates the payment
 
@@ -1110,33 +1110,32 @@ static void TriggerIndustryProduction(In
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 
	uint16 callback = indspec->callback_mask;
 

	
 
	i->was_cargo_delivered = true;
 

	
 
	if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) {
 
		if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) {
 
			IndustryProductionCallback(i, 0);
 
		} else {
 
			SetWindowDirty(WC_INDUSTRY_VIEW, i->index);
 
		}
 
	} else {
 
		for (uint ci_in = 0; ci_in < lengthof(i->incoming_cargo_waiting); ci_in++) {
 
			uint cargo_waiting = i->incoming_cargo_waiting[ci_in];
 
			if (cargo_waiting == 0) continue;
 
		for (auto ita = std::begin(i->accepted); ita != std::end(i->accepted); ++ita) {
 
			if (ita->waiting == 0) continue;
 

	
 
			for (uint ci_out = 0; ci_out < lengthof(i->produced_cargo_waiting); ci_out++) {
 
				i->produced_cargo_waiting[ci_out] = ClampTo<uint16_t>(i->produced_cargo_waiting[ci_out] + (cargo_waiting * indspec->input_cargo_multiplier[ci_in][ci_out] / 256));
 
			for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
 
				itp->waiting = ClampTo<uint16_t>(itp->waiting + (ita->waiting * indspec->input_cargo_multiplier[ita - std::begin(i->accepted)][itp - std::begin(i->produced)] / 256));
 
			}
 

	
 
			i->incoming_cargo_waiting[ci_in] = 0;
 
			ita->waiting = 0;
 
		}
 
	}
 

	
 
	TriggerIndustry(i, INDUSTRY_TRIGGER_RECEIVED_CARGO);
 
	StartStopIndustryTileAnimation(i, IAT_INDUSTRY_RECEIVED_CARGO);
 
}
 

	
 
/**
 
 * Makes us a new cargo payment helper.
 
 * @param front The front of the train
 
 */
 
CargoPayment::CargoPayment(Vehicle *front) :
src/industry.h
Show inline comments
 
@@ -47,120 +47,137 @@ enum IndustryControlFlags : byte {
 
	INDCTL_NO_PRODUCTION_DECREASE = 1 << 0,
 
	/** When industry production change is evaluated, rolls to increase are ignored. */
 
	INDCTL_NO_PRODUCTION_INCREASE = 1 << 1,
 
	/**
 
	 * Industry can not close regardless of production level or time since last delivery.
 
	 * This does not prevent a closure already announced. */
 
	INDCTL_NO_CLOSURE             = 1 << 2,
 
	/** Mask of all flags set */
 
	INDCTL_MASK = INDCTL_NO_PRODUCTION_DECREASE | INDCTL_NO_PRODUCTION_INCREASE | INDCTL_NO_CLOSURE,
 
};
 
DECLARE_ENUM_AS_BIT_SET(IndustryControlFlags);
 

	
 
static const int THIS_MONTH = 0;
 
static const int LAST_MONTH = 1;
 

	
 
/**
 
 * Defines the internal data of a functional industry.
 
 */
 
struct Industry : IndustryPool::PoolItem<&_industry_pool> {
 
	struct ProducedHistory {
 
		uint16_t production; ///< Total produced
 
		uint16_t transported; ///< Total transported
 

	
 
		uint8_t PctTransported() const
 
		{
 
			if (this->production == 0) return 0;
 
			return ClampTo<uint8_t>(this->transported * 256 / this->production);
 
		}
 
	};
 

	
 
	struct ProducedCargo {
 
		CargoID cargo; ///< Cargo type
 
		uint16_t waiting; ///< Amount of cargo produced
 
		uint8_t rate; ///< Production rate
 
		std::array<ProducedHistory, 2> history; ///< History of cargo produced and transported
 
	};
 

	
 
	struct AcceptedCargo {
 
		CargoID cargo; ///< Cargo type
 
		uint16_t waiting; ///< Amount of cargo waiting to processed
 
		TimerGameCalendar::Date last_accepted; ///< Last day cargo was accepted by this industry
 
	};
 

	
 
	using ProducedCargoArray = std::array<ProducedCargo, INDUSTRY_NUM_OUTPUTS>;
 
	using AcceptedCargoArray = std::array<AcceptedCargo, INDUSTRY_NUM_INPUTS>;
 

	
 
	TileArea location;                                     ///< Location of the industry
 
	Town *town;                                            ///< Nearest town
 
	Station *neutral_station;                              ///< Associated neutral station
 
	CargoID produced_cargo[INDUSTRY_NUM_OUTPUTS];          ///< 16 production cargo slots
 
	uint16 produced_cargo_waiting[INDUSTRY_NUM_OUTPUTS];   ///< amount of cargo produced per cargo
 
	uint16 incoming_cargo_waiting[INDUSTRY_NUM_INPUTS];    ///< incoming cargo waiting to be processed
 
	byte production_rate[INDUSTRY_NUM_OUTPUTS];            ///< production rate for each cargo
 
	ProducedCargoArray produced; ///< INDUSTRY_NUM_OUTPUTS production cargo slots
 
	AcceptedCargoArray accepted; ///< INDUSTRY_NUM_INPUTS input cargo slots
 
	byte prod_level;                                       ///< general production level
 
	CargoID accepts_cargo[INDUSTRY_NUM_INPUTS];            ///< 16 input cargo slots
 
	uint16 this_month_production[INDUSTRY_NUM_OUTPUTS];    ///< stats of this month's production per cargo
 
	uint16 this_month_transported[INDUSTRY_NUM_OUTPUTS];   ///< stats of this month's transport per cargo
 
	byte last_month_pct_transported[INDUSTRY_NUM_OUTPUTS]; ///< percentage transported per cargo in the last full month
 
	uint16 last_month_production[INDUSTRY_NUM_OUTPUTS];    ///< total units produced per cargo in the last full month
 
	uint16 last_month_transported[INDUSTRY_NUM_OUTPUTS];   ///< total units transported per cargo in the last full month
 
	uint16 counter;                                        ///< used for animation and/or production (if available cargo)
 

	
 
	IndustryType type;             ///< type of industry.
 
	Owner owner;                   ///< owner of the industry.  Which SHOULD always be (imho) OWNER_NONE
 
	byte random_colour;            ///< randomized colour of the industry, for display purpose
 
	TimerGameCalendar::Year last_prod_year; ///< last year of production
 
	byte was_cargo_delivered;      ///< flag that indicate this has been the closest industry chosen for cargo delivery by a station. see DeliverGoodsToIndustry
 
	IndustryControlFlags ctlflags; ///< flags overriding standard behaviours
 

	
 
	PartOfSubsidy part_of_subsidy; ///< NOSAVE: is this industry a source/destination of a subsidy?
 
	StationList stations_near;     ///< NOSAVE: List of nearby stations.
 
	mutable std::string cached_name; ///< NOSAVE: Cache of the resolved name of the industry
 

	
 
	Owner founder;                 ///< Founder of the industry
 
	TimerGameCalendar::Date construction_date; ///< Date of the construction of the industry
 
	uint8 construction_type;       ///< Way the industry was constructed (@see IndustryConstructionType)
 
	TimerGameCalendar::Date last_cargo_accepted_at[INDUSTRY_NUM_INPUTS]; ///< Last day each cargo type was accepted by this industry
 
	byte selected_layout;          ///< Which tile layout was used when creating the industry
 
	Owner exclusive_supplier;      ///< Which company has exclusive rights to deliver cargo (INVALID_OWNER = anyone)
 
	Owner exclusive_consumer;      ///< Which company has exclusive rights to take cargo (INVALID_OWNER = anyone)
 
	std::string text;              ///< General text with additional information.
 

	
 
	uint16 random;                 ///< Random value used for randomisation of all kinds of things
 

	
 
	PersistentStorage *psa;        ///< Persistent storage for NewGRF industries.
 

	
 
	Industry(TileIndex tile = INVALID_TILE) : location(tile, 0, 0) {}
 
	~Industry();
 

	
 
	void RecomputeProductionMultipliers();
 

	
 
	/**
 
	 * Check if a given tile belongs to this industry.
 
	 * @param tile The tile to check.
 
	 * @return True if the tile is part of this industry.
 
	 */
 
	inline bool TileBelongsToIndustry(TileIndex tile) const
 
	{
 
		return IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == this->index;
 
	}
 

	
 
	inline int GetCargoProducedIndex(CargoID cargo) const
 
	inline ProducedCargoArray::iterator GetCargoProduced(CargoID cargo)
 
	{
 
		if (!IsValidCargoID(cargo)) return -1;
 
		const CargoID *pos = std::find(this->produced_cargo, endof(this->produced_cargo), cargo);
 
		if (pos == endof(this->produced_cargo)) return -1;
 
		return pos - this->produced_cargo;
 
		if (!IsValidCargoID(cargo)) return std::end(this->produced);
 
		return std::find_if(std::begin(this->produced), std::end(this->produced), [&cargo](const auto &p) { return p.cargo == cargo; });
 
	}
 

	
 
	inline int GetCargoAcceptedIndex(CargoID cargo) const
 
	inline AcceptedCargoArray::iterator GetCargoAccepted(CargoID cargo)
 
	{
 
		if (!IsValidCargoID(cargo)) return -1;
 
		const CargoID *pos = std::find(this->accepts_cargo, endof(this->accepts_cargo), cargo);
 
		if (pos == endof(this->accepts_cargo)) return -1;
 
		return pos - this->accepts_cargo;
 
		if (!IsValidCargoID(cargo)) return std::end(this->accepted);
 
		return std::find_if(std::begin(this->accepted), std::end(this->accepted), [&cargo](const auto &a) { return a.cargo == cargo; });
 
	}
 

	
 
	/** Test if this industry accepts any cargo.
 
	 * @return true iff the industry accepts any cargo.
 
	 */
 
	bool IsCargoAccepted() const { return std::any_of(std::begin(this->accepts_cargo), std::end(this->accepts_cargo), [](const auto &cargo) { return IsValidCargoID(cargo); }); }
 
	bool IsCargoAccepted() const { return std::any_of(std::begin(this->accepted), std::end(this->accepted), [](const auto &a) { return IsValidCargoID(a.cargo); }); }
 

	
 
	/** Test if this industry produces any cargo.
 
	 * @return true iff the industry produces any cargo.
 
	 */
 
	bool IsCargoProduced() const { return std::any_of(std::begin(this->produced_cargo), std::end(this->produced_cargo), [](const auto &cargo) { return IsValidCargoID(cargo); }); }
 
	bool IsCargoProduced() const { return std::any_of(std::begin(this->produced), std::end(this->produced), [](const auto &p) { return IsValidCargoID(p.cargo); }); }
 

	
 
	/** Test if this industry accepts a specific cargo.
 
	 * @param cargo Cargo type to test.
 
	 * @return true iff the industry accepts the given cargo type.
 
	 */
 
	bool IsCargoAccepted(CargoID cargo) const { return std::any_of(std::begin(this->accepts_cargo), std::end(this->accepts_cargo), [&cargo](const auto &cid) { return cid == cargo; }); }
 
	bool IsCargoAccepted(CargoID cargo) const { return std::any_of(std::begin(this->accepted), std::end(this->accepted), [&cargo](const auto &a) { return a.cargo == cargo; }); }
 

	
 
	/** Test if this industry produces a specific cargo.
 
	 * @param cargo Cargo type to test.
 
	 * @return true iff the industry produces the given cargo types.
 
	 */
 
	bool IsCargoProduced(CargoID cargo) const { return std::any_of(std::begin(this->produced_cargo), std::end(this->produced_cargo), [&cargo](const auto &cid) { return cid == cargo; }); }
 
	bool IsCargoProduced(CargoID cargo) const { return std::any_of(std::begin(this->produced), std::end(this->produced), [&cargo](const auto &p) { return p.cargo == cargo; }); }
 

	
 
	/**
 
	 * Get the industry of the given tile
 
	 * @param tile the tile to get the industry from
 
	 * @pre IsTileType(t, MP_INDUSTRY)
 
	 * @return the industry
 
	 */
 
	static inline Industry *GetByTile(TileIndex tile)
 
	{
 
		return Industry::Get(GetIndustryIndex(tile));
 
	}
 

	
src/industry_cmd.cpp
Show inline comments
 
@@ -411,31 +411,31 @@ static void AddAcceptedCargo_Industry(Ti
 
	IndustryGfx gfx = GetIndustryGfx(tile);
 
	const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
 
	const Industry *ind = Industry::GetByTile(tile);
 

	
 
	/* Starting point for acceptance */
 
	CargoID accepts_cargo[lengthof(itspec->accepts_cargo)];
 
	int8 cargo_acceptance[lengthof(itspec->acceptance)];
 
	MemCpyT(accepts_cargo, itspec->accepts_cargo, lengthof(accepts_cargo));
 
	MemCpyT(cargo_acceptance, itspec->acceptance, lengthof(cargo_acceptance));
 

	
 
	if (itspec->special_flags & INDTILE_SPECIAL_ACCEPTS_ALL_CARGO) {
 
		/* Copy all accepted cargoes from industry itself */
 
		for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) {
 
			CargoID *pos = std::find(accepts_cargo, endof(accepts_cargo), ind->accepts_cargo[i]);
 
		for (const auto &a : ind->accepted) {
 
			CargoID *pos = std::find(accepts_cargo, endof(accepts_cargo), a.cargo);
 
			if (pos == endof(accepts_cargo)) {
 
				/* Not found, insert */
 
				pos = std::find(accepts_cargo, endof(accepts_cargo), CT_INVALID);
 
				if (pos == endof(accepts_cargo)) continue; // nowhere to place, give up on this one
 
				*pos = ind->accepts_cargo[i];
 
				*pos = a.cargo;
 
			}
 
			cargo_acceptance[pos - accepts_cargo] += 8;
 
		}
 
	}
 

	
 
	if (HasBit(itspec->callback_mask, CBM_INDT_ACCEPT_CARGO)) {
 
		/* Try callback for accepts list, if success override all existing accepts */
 
		uint16 res = GetIndustryTileCallback(CBID_INDTILE_ACCEPT_CARGO, 0, 0, gfx, Industry::GetByTile(tile), tile);
 
		if (res != CALLBACK_FAILED) {
 
			MemSetT(accepts_cargo, CT_INVALID, lengthof(accepts_cargo));
 
			for (uint i = 0; i < 3; i++) accepts_cargo[i] = GetCargoTranslation(GB(res, i * 5, 5), itspec->grf_prop.grffile);
 
		}
 
@@ -515,36 +515,36 @@ static CommandCost ClearTile_Industry(Ti
 

	
 
/**
 
 * Move produced cargo from industry to nearby stations.
 
 * @param tile Industry tile
 
 * @return true if any cargo was moved.
 
 */
 
static bool TransportIndustryGoods(TileIndex tile)
 
{
 
	Industry *i = Industry::GetByTile(tile);
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 
	bool moved_cargo = false;
 

	
 
	for (uint j = 0; j < lengthof(i->produced_cargo_waiting); j++) {
 
		uint cw = ClampTo<uint8_t>(i->produced_cargo_waiting[j]);
 
		if (cw > indspec->minimal_cargo && IsValidCargoID(i->produced_cargo[j])) {
 
			i->produced_cargo_waiting[j] -= cw;
 
	for (auto &p : i->produced) {
 
		uint cw = ClampTo<uint8_t>(p.waiting);
 
		if (cw > indspec->minimal_cargo && IsValidCargoID(p.cargo)) {
 
			p.waiting -= cw;
 

	
 
			/* fluctuating economy? */
 
			if (EconomyIsInRecession()) cw = (cw + 1) / 2;
 

	
 
			i->this_month_production[j] += cw;
 

	
 
			uint am = MoveGoodsToStation(i->produced_cargo[j], cw, SourceType::Industry, i->index, &i->stations_near, i->exclusive_consumer);
 
			i->this_month_transported[j] += am;
 
			p.history[THIS_MONTH].production += cw;
 

	
 
			uint am = MoveGoodsToStation(p.cargo, cw, SourceType::Industry, i->index, &i->stations_near, i->exclusive_consumer);
 
			p.history[THIS_MONTH].transported += am;
 

	
 
			moved_cargo |= (am != 0);
 
		}
 
	}
 

	
 
	return moved_cargo;
 
}
 

	
 
static void AnimateSugarSieve(TileIndex tile)
 
{
 
	byte m = GetAnimationFrame(tile) + 1;
 

	
 
@@ -972,30 +972,25 @@ static void ChangeTileOwner_Industry(Til
 
 */
 
bool IsTileForestIndustry(TileIndex tile)
 
{
 
	/* Check for industry tile */
 
	if (!IsTileType(tile, MP_INDUSTRY)) return false;
 

	
 
	const Industry *ind = Industry::GetByTile(tile);
 

	
 
	/* Check for organic industry (i.e. not processing or extractive) */
 
	if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_ORGANIC) == 0) return false;
 

	
 
	/* Check for wood production */
 
	for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
 
		/* The industry produces wood. */
 
		if (IsValidCargoID(ind->produced_cargo[i]) && CargoSpec::Get(ind->produced_cargo[i])->label == 'WOOD') return true;
 
	}
 

	
 
	return false;
 
	return std::any_of(std::begin(ind->produced), std::end(ind->produced), [](const auto &p) { return IsValidCargoID(p.cargo) && CargoSpec::Get(p.cargo)->label == 'WOOD'; });
 
}
 

	
 
static const byte _plantfarmfield_type[] = {1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6};
 

	
 
/**
 
 * Check whether the tile can be replaced by a farm field.
 
 * @param tile the tile to investigate.
 
 * @param allow_fields if true, the method will return true even if
 
 * the tile is a farm tile, otherwise the tile may not be a farm tile
 
 * @return true if the tile can become a farm field
 
 */
 
static bool IsSuitableForFarmField(TileIndex tile, bool allow_fields)
 
@@ -1125,57 +1120,54 @@ static bool SearchLumberMillTrees(TileIn
 
 */
 
static void ChopLumberMillTrees(Industry *i)
 
{
 
	/* We only want to cut trees if all tiles are completed. */
 
	for (TileIndex tile_cur : i->location) {
 
		if (i->TileBelongsToIndustry(tile_cur)) {
 
			if (!IsIndustryCompleted(tile_cur)) return;
 
		}
 
	}
 

	
 
	TileIndex tile = i->location.tile;
 
	if (CircularTileSearch(&tile, 40, SearchLumberMillTrees, nullptr)) { // 40x40 tiles  to search.
 
		i->produced_cargo_waiting[0] = ClampTo<uint16_t>(i->produced_cargo_waiting[0] + 45); // Found a tree, add according value to waiting cargo.
 
		i->produced[0].waiting = ClampTo<uint16_t>(i->produced[0].waiting + 45); // Found a tree, add according value to waiting cargo.
 
	}
 
}
 

	
 
static void ProduceIndustryGoods(Industry *i)
 
{
 
	const IndustrySpec *indsp = GetIndustrySpec(i->type);
 

	
 
	/* play a sound? */
 
	if ((i->counter & 0x3F) == 0) {
 
		uint32 r;
 
		if (Chance16R(1, 14, r) && indsp->number_of_sounds != 0 && _settings_client.sound.ambient) {
 
			for (size_t j = 0; j < lengthof(i->last_month_production); j++) {
 
				if (i->last_month_production[j] > 0) {
 
					/* Play sound since last month had production */
 
					SndPlayTileFx(
 
						(SoundFx)(indsp->random_sounds[((r >> 16) * indsp->number_of_sounds) >> 16]),
 
						i->location.tile);
 
					break;
 
				}
 
			if (std::any_of(std::begin(i->produced), std::end(i->produced), [](const auto &p) { return p.history[LAST_MONTH].production > 0; })) {
 
				/* Play sound since last month had production */
 
				SndPlayTileFx(
 
					(SoundFx)(indsp->random_sounds[((r >> 16) * indsp->number_of_sounds) >> 16]),
 
					i->location.tile);
 
			}
 
		}
 
	}
 

	
 
	i->counter--;
 

	
 
	/* produce some cargo */
 
	if ((i->counter % INDUSTRY_PRODUCE_TICKS) == 0) {
 
		if (HasBit(indsp->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) IndustryProductionCallback(i, 1);
 

	
 
		IndustryBehaviour indbehav = indsp->behaviour;
 
		for (size_t j = 0; j < lengthof(i->produced_cargo_waiting); j++) {
 
			i->produced_cargo_waiting[j] = ClampTo<uint16_t>(i->produced_cargo_waiting[j] + i->production_rate[j]);
 
		for (auto &p : i->produced) {
 
			p.waiting = ClampTo<uint16_t>(p.waiting + p.rate);
 
		}
 

	
 
		if ((indbehav & INDUSTRYBEH_PLANT_FIELDS) != 0) {
 
			uint16 cb_res = CALLBACK_FAILED;
 
			if (HasBit(indsp->callback_mask, CBM_IND_SPECIAL_EFFECT)) {
 
				cb_res = GetIndustryCallback(CBID_INDUSTRY_SPECIAL_EFFECT, Random(), 0, i, i->type, i->location.tile);
 
			}
 

	
 
			bool plant;
 
			if (cb_res != CALLBACK_FAILED) {
 
				plant = ConvertBooleanCallback(indsp->grf_prop.grffile, CBID_INDUSTRY_SPECIAL_EFFECT, cb_res);
 
			} else {
 
@@ -1753,40 +1745,39 @@ static void PopulateStationsNearby(Indus
 
 * @param t                   Nearest town.
 
 * @param founder             Founder of the industry; OWNER_NONE in case of random construction.
 
 * @param initial_random_bits Random bits for the industry.
 
 */
 
static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type, const IndustryTileLayout &layout, size_t layout_index, Town *t, Owner founder, uint16 initial_random_bits)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(type);
 

	
 
	i->location = TileArea(tile, 1, 1);
 
	i->type = type;
 
	Industry::IncIndustryTypeCount(type);
 

	
 
	MemCpyT(i->produced_cargo,  indspec->produced_cargo,  lengthof(i->produced_cargo));
 
	MemCpyT(i->production_rate, indspec->production_rate, lengthof(i->production_rate));
 
	MemCpyT(i->accepts_cargo,   indspec->accepts_cargo,   lengthof(i->accepts_cargo));
 

	
 
	MemSetT(i->produced_cargo_waiting,     0, lengthof(i->produced_cargo_waiting));
 
	MemSetT(i->this_month_production,      0, lengthof(i->this_month_production));
 
	MemSetT(i->this_month_transported,     0, lengthof(i->this_month_transported));
 
	MemSetT(i->last_month_pct_transported, 0, lengthof(i->last_month_pct_transported));
 
	MemSetT(i->last_month_transported,     0, lengthof(i->last_month_transported));
 
	MemSetT(i->incoming_cargo_waiting,     0, lengthof(i->incoming_cargo_waiting));
 
	MemSetT(i->last_cargo_accepted_at,     0, lengthof(i->last_cargo_accepted_at));
 
	for (auto it = std::begin(i->produced); it != std::end(i->produced); ++it) {
 
		size_t index = it - std::begin(i->produced);
 
		it->cargo = indspec->produced_cargo[index];
 
		it->rate = indspec->production_rate[index];
 
	}
 

	
 
	for (auto it = std::begin(i->accepted); it != std::end(i->accepted); ++it) {
 
		size_t index = it - std::begin(i->accepted);
 
		it->cargo = indspec->accepts_cargo[index];
 
	}
 

	
 
	/* Randomize inital production if non-original economy is used and there are no production related callbacks. */
 
	if (!indspec->UsesOriginalEconomy()) {
 
		for (size_t ci = 0; ci < lengthof(i->production_rate); ci++) {
 
			i->production_rate[ci] = ClampTo<byte>((RandomRange(256) + 128) * i->production_rate[ci] >> 8);
 
		for (auto &p : i->produced) {
 
			p.rate = ClampTo<byte>((RandomRange(256) + 128) * p.rate >> 8);
 
		}
 
	}
 

	
 
	i->town = t;
 
	i->owner = OWNER_NONE;
 

	
 
	uint16 r = Random();
 
	i->random_colour = GB(r, 0, 4);
 
	i->counter = GB(r, 4, 12);
 
	i->random = initial_random_bits;
 
	i->was_cargo_delivered = false;
 
	i->last_prod_year = TimerGameCalendar::year;
 
@@ -1815,102 +1806,102 @@ static void DoCreateNewIndustry(Industry
 
			if (res < PRODLEVEL_MINIMUM || res > PRODLEVEL_MAXIMUM) {
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_PROD_CHANGE_BUILD, res);
 
			} else {
 
				i->prod_level = res;
 
				i->RecomputeProductionMultipliers();
 
			}
 
		}
 
	}
 

	
 
	if (_generating_world) {
 
		if (HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) {
 
			IndustryProductionCallback(i, 1);
 
			for (size_t ci = 0; ci < lengthof(i->last_month_production); ci++) {
 
				i->last_month_production[ci] = i->produced_cargo_waiting[ci] * 8;
 
				i->produced_cargo_waiting[ci] = 0;
 
			for (auto &p : i->produced) {
 
				p.history[LAST_MONTH].production = p.waiting * 8;
 
				p.waiting = 0;
 
			}
 
		}
 

	
 
		for (size_t ci = 0; ci < lengthof(i->last_month_production); ci++) {
 
			i->last_month_production[ci] += i->production_rate[ci] * 8;
 
		for (auto &p : i->produced) {
 
			p.history[LAST_MONTH].production += p.rate * 8;
 
		}
 
	}
 

	
 
	if (HasBit(indspec->callback_mask, CBM_IND_DECIDE_COLOUR)) {
 
		uint16 res = GetIndustryCallback(CBID_INDUSTRY_DECIDE_COLOUR, 0, 0, i, type, INVALID_TILE);
 
		if (res != CALLBACK_FAILED) {
 
			if (GB(res, 4, 11) != 0) ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_DECIDE_COLOUR, res);
 
			i->random_colour = GB(res, 0, 4);
 
		}
 
	}
 

	
 
	if (HasBit(indspec->callback_mask, CBM_IND_INPUT_CARGO_TYPES)) {
 
		/* Clear all input cargo types */
 
		for (uint j = 0; j < lengthof(i->accepts_cargo); j++) i->accepts_cargo[j] = CT_INVALID;
 
		for (auto &a : i->accepted) a.cargo = CT_INVALID;
 
		/* Query actual types */
 
		uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->accepts_cargo) : 3;
 
		uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->accepted.size()) : 3;
 
		for (uint j = 0; j < maxcargoes; 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;
 
			if (indspec->grf_prop.grffile->grf_version >= 8 && res >= 0x100) {
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			CargoID cargo = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
 
			/* Industries without "unlimited" cargo types support depend on the specific order/slots of cargo types.
 
			 * They need to be able to blank out specific slots without aborting the callback sequence,
 
			 * and solve this by returning undefined cargo indexes. Skip these. */
 
			if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue;
 
			/* Verify valid cargo */
 
			if (std::find(indspec->accepts_cargo, endof(indspec->accepts_cargo), cargo) == endof(indspec->accepts_cargo)) {
 
				/* Cargo not in spec, error in NewGRF */
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			if (std::find(i->accepts_cargo, i->accepts_cargo + j, cargo) != i->accepts_cargo + j) {
 
			if (std::any_of(std::begin(i->accepted), std::begin(i->accepted) + j, [&cargo](const auto &a) { return a.cargo == cargo; })) {
 
				/* Duplicate cargo */
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			i->accepts_cargo[j] = cargo;
 
			i->accepted[j].cargo = cargo;
 
		}
 
	}
 

	
 
	if (HasBit(indspec->callback_mask, CBM_IND_OUTPUT_CARGO_TYPES)) {
 
		/* Clear all output cargo types */
 
		for (uint j = 0; j < lengthof(i->produced_cargo); j++) i->produced_cargo[j] = CT_INVALID;
 
		for (auto &p : i->produced) p.cargo = CT_INVALID;
 
		/* Query actual types */
 
		uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->produced_cargo) : 2;
 
		uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? static_cast<uint>(i->produced.size()) : 2;
 
		for (uint j = 0; j < maxcargoes; 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;
 
			if (indspec->grf_prop.grffile->grf_version >= 8 && res >= 0x100) {
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			CargoID cargo = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
 
			/* Allow older GRFs to skip slots. */
 
			if (!IsValidCargoID(cargo) && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue;
 
			/* Verify valid cargo */
 
			if (std::find(indspec->produced_cargo, endof(indspec->produced_cargo), cargo) == endof(indspec->produced_cargo)) {
 
				/* Cargo not in spec, error in NewGRF */
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			if (std::find(i->produced_cargo, i->produced_cargo + j, cargo) != i->produced_cargo + j) {
 
			if (std::any_of(std::begin(i->produced), std::begin(i->produced) + j, [&cargo](const auto &p) { return p.cargo == cargo; })) {
 
				/* Duplicate cargo */
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			i->produced_cargo[j] = cargo;
 
			i->produced[j].cargo = cargo;
 
		}
 
	}
 

	
 
	/* Plant the tiles */
 

	
 
	for (const IndustryTileLayoutTile &it : layout) {
 
		TileIndex cur_tile = tile + ToTileIndexDiff(it.ti);
 

	
 
		if (it.gfx != GFX_WATERTILE_SPECIALCHECK) {
 
			i->location.Add(cur_tile);
 

	
 
			WaterClass wc = (IsWaterTile(cur_tile) ? GetWaterClass(cur_tile) : WATER_CLASS_INVALID);
 
@@ -2402,54 +2393,48 @@ void GenerateIndustries()
 
		assert(industry_probs[it] > 0);
 
		PlaceInitialIndustry(it, false);
 
	}
 
	_industry_builder.Reset();
 
}
 

	
 
/**
 
 * Monthly update of industry statistics.
 
 * @param i Industry to update.
 
 */
 
static void UpdateIndustryStatistics(Industry *i)
 
{
 
	for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
		if (IsValidCargoID(i->produced_cargo[j])) {
 
			byte pct = 0;
 
			if (i->this_month_production[j] != 0) {
 
				i->last_prod_year = TimerGameCalendar::year;
 
				pct = ClampTo<byte>(i->this_month_transported[j] * 256 / i->this_month_production[j]);
 
			}
 
			i->last_month_pct_transported[j] = pct;
 

	
 
			i->last_month_production[j] = i->this_month_production[j];
 
			i->this_month_production[j] = 0;
 

	
 
			i->last_month_transported[j] = i->this_month_transported[j];
 
			i->this_month_transported[j] = 0;
 
	for (auto &p : i->produced) {
 
		if (IsValidCargoID(p.cargo)) {
 
			if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameCalendar::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;
 
		}
 
	}
 
}
 

	
 
/**
 
 * 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->UsesOriginalEconomy());
 

	
 
	/* Rates are rounded up, so e.g. oilrig always produces some passengers */
 
	for (size_t i = 0; i < lengthof(this->production_rate); i++) {
 
		this->production_rate[i] = ClampTo<byte>(CeilDiv(indspec->production_rate[i] * this->prod_level, PRODLEVEL_DEFAULT));
 
	for (auto &p : this->produced) {
 
		p.rate = ClampTo<uint8_t>(CeilDiv(indspec->production_rate[&p - this->produced.data()] * this->prod_level, PRODLEVEL_DEFAULT));
 
	}
 
}
 

	
 
void Industry::FillCachedName() const
 
{
 
	char buf[256];
 
	int64 args_array[] = { this->index };
 
	StringParameters tmp_params(args_array);
 
	char *end = GetStringWithArgs(buf, STR_INDUSTRY_NAME, &tmp_params, lastof(buf));
 
	this->cached_name.assign(buf, end);
 
}
 

	
 
@@ -2764,81 +2749,81 @@ static void ChangeIndustryProduction(Ind
 
		if (monthly == original_economy) return;
 
		if (!original_economy && _settings_game.economy.type == ET_FROZEN) return;
 
		if (indspec->life_type == INDUSTRYLIFE_BLACK_HOLE) return;
 
	}
 

	
 
	if (standard || (!callback_enabled && (indspec->life_type & (INDUSTRYLIFE_ORGANIC | INDUSTRYLIFE_EXTRACTIVE)) != 0)) {
 
		/* decrease or increase */
 
		bool only_decrease = (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _settings_game.game_creation.landscape == LT_TEMPERATE;
 

	
 
		if (original_economy) {
 
			if (only_decrease || Chance16(1, 3)) {
 
				/* If more than 60% transported, 66% chance of increase, else 33% chance of increase */
 
				if (!only_decrease && (i->last_month_pct_transported[0] > PERCENT_TRANSPORTED_60) != Chance16(1, 3)) {
 
				if (!only_decrease && (i->produced[0].history[LAST_MONTH].PctTransported() > PERCENT_TRANSPORTED_60) != Chance16(1, 3)) {
 
					mul = 1; // Increase production
 
				} else {
 
					div = 1; // Decrease production
 
				}
 
			}
 
		} else if (_settings_game.economy.type == ET_SMOOTH) {
 
			closeit = !(i->ctlflags & (INDCTL_NO_CLOSURE | INDCTL_NO_PRODUCTION_DECREASE));
 
			for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
				if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
			for (auto &p : i->produced) {
 
				if (!IsValidCargoID(p.cargo)) continue;
 
				uint32 r = Random();
 
				int old_prod, new_prod, percent;
 
				/* If over 60% is transported, mult is 1, else mult is -1. */
 
				int mult = (i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_60) ? 1 : -1;
 

	
 
				new_prod = old_prod = i->production_rate[j];
 
				int mult = (p.history[LAST_MONTH].PctTransported() > PERCENT_TRANSPORTED_60) ? 1 : -1;
 

	
 
				new_prod = old_prod = p.rate;
 

	
 
				/* For industries with only_decrease flags (temperate terrain Oil Wells),
 
				 * the multiplier will always be -1 so they will only decrease. */
 
				if (only_decrease) {
 
					mult = -1;
 
				/* For normal industries, if over 60% is transported, 33% chance for decrease.
 
				 * Bonus for very high station ratings (over 80%): 16% chance for decrease. */
 
				} else if (Chance16I(1, ((i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_80) ? 6 : 3), r)) {
 
				} else if (Chance16I(1, ((p.history[LAST_MONTH].PctTransported() > PERCENT_TRANSPORTED_80) ? 6 : 3), r)) {
 
					mult *= -1;
 
				}
 

	
 
				/* 4.5% chance for 3-23% (or 1 unit for very low productions) production change,
 
				 * determined by mult value. If mult = 1 prod. increases, else (-1) it decreases. */
 
				if (Chance16I(1, 22, r >> 16)) {
 
					new_prod += mult * (std::max(((RandomRange(50) + 10) * old_prod) >> 8, 1U));
 
				}
 

	
 
				/* Prevent production to overflow or Oil Rig passengers to be over-"produced" */
 
				new_prod = Clamp(new_prod, 1, 255);
 
				if (i->produced_cargo[j] == CT_PASSENGERS && !(indspec->behaviour & INDUSTRYBEH_NO_PAX_PROD_CLAMP)) {
 
				if (p.cargo == CT_PASSENGERS && !(indspec->behaviour & INDUSTRYBEH_NO_PAX_PROD_CLAMP)) {
 
					new_prod = Clamp(new_prod, 0, 16);
 
				}
 

	
 
				/* If override flags are set, prevent actually changing production if any was decided on */
 
				if ((i->ctlflags & INDCTL_NO_PRODUCTION_DECREASE) && new_prod < old_prod) continue;
 
				if ((i->ctlflags & INDCTL_NO_PRODUCTION_INCREASE) && new_prod > old_prod) continue;
 

	
 
				/* Do not stop closing the industry when it has the lowest possible production rate */
 
				if (new_prod == old_prod && old_prod > 1) {
 
					closeit = false;
 
					continue;
 
				}
 

	
 
				percent = (old_prod == 0) ? 100 : (new_prod * 100 / old_prod - 100);
 
				i->production_rate[j] = new_prod;
 
				p.rate = new_prod;
 

	
 
				/* Close the industry when it has the lowest possible production rate */
 
				if (new_prod > 1) closeit = false;
 

	
 
				if (abs(percent) >= 10) {
 
					ReportNewsProductionChangeIndustry(i, i->produced_cargo[j], percent);
 
					ReportNewsProductionChangeIndustry(i, p.cargo, percent);
 
				}
 
			}
 
		}
 
	}
 

	
 
	/* If override flags are set, prevent actually changing production if any was decided on */
 
	if ((i->ctlflags & INDCTL_NO_PRODUCTION_DECREASE) && (div > 0 || increment < 0)) return;
 
	if ((i->ctlflags & INDCTL_NO_PRODUCTION_INCREASE) && (mul > 0 || increment > 0)) return;
 

	
 
	if (!callback_enabled && (indspec->life_type & INDUSTRYLIFE_PROCESSING)) {
 
		if (TimerGameCalendar::year - i->last_prod_year >= PROCESSING_INDUSTRY_ABANDONMENT_YEARS && Chance16(1, original_economy ? 2 : 180)) {
 
			closeit = true;
src/industry_gui.cpp
Show inline comments
 
@@ -180,24 +180,51 @@ static inline void GetAllCargoSuffixes(C
 
				break;
 
			case CARGOSUFFIX_IN:
 
				if (IsValidCargoID(cargoes[0])) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (IsValidCargoID(cargoes[1])) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
 
				if (IsValidCargoID(cargoes[2])) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
 
				break;
 
			default:
 
				NOT_REACHED();
 
		}
 
	}
 
}
 

	
 
/**
 
 * Gets the strings to display after the cargo of industries (using callback 37)
 
 * @param use_input get suffixes for output cargo or input cargo?
 
 * @param cst the cargo suffix type (for which window is it requested). @see CargoSuffixType
 
 * @param ind the industry (nullptr if in fund window)
 
 * @param ind_type the industry type
 
 * @param indspec the industry spec
 
 * @param cargo cargotype. for CT_INVALID no suffix will be determined
 
 * @param slot accepts/produced slot number, used for old-style 3-in/2-out industries.
 
 * @param suffix is filled with the suffix
 
 */
 
void GetCargoSuffix(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, CargoID cargo, uint8_t slot, CargoSuffix &suffix)
 
{
 
	suffix.text[0] = '\0';
 
	suffix.display = CSD_CARGO;
 
	if (!IsValidCargoID(cargo)) return;
 
	if (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) {
 
		byte local_id = indspec->grf_prop.grffile->cargo_map[cargo]; // should we check the value for valid?
 
		uint cargotype = local_id << 16 | use_input;
 
		GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffix);
 
	} else if (use_input == CARGOSUFFIX_IN) {
 
		if (slot < 3) GetCargoSuffix(slot, cst, ind, ind_type, indspec, suffix);
 
	} else if (use_input == CARGOSUFFIX_OUT) {
 
		if (slot < 2) GetCargoSuffix(slot + 3, cst, ind, ind_type, indspec, suffix);
 
	}
 
}
 

	
 
std::array<IndustryType, NUM_INDUSTRYTYPES> _sorted_industry_types; ///< Industry types sorted by name.
 

	
 
/** Sort industry types by their name. */
 
static bool IndustryTypeNameSorter(const IndustryType &a, const IndustryType &b)
 
{
 
	int r = StrNaturalCompare(GetString(GetIndustrySpec(a)->name), GetString(GetIndustrySpec(b)->name)); // Sort by name (natural sorting).
 

	
 
	/* If the names are equal, sort by industry type. */
 
	return (r != 0) ? r < 0 : (a < b);
 
}
 

	
 
/**
 
@@ -831,87 +858,91 @@ public:
 
		bool rtl = _current_text_dir == TD_RTL;
 
		Industry *i = Industry::Get(this->window_number);
 
		const IndustrySpec *ind = GetIndustrySpec(i->type);
 
		Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
 
		bool first = true;
 
		bool has_accept = false;
 

	
 
		if (i->prod_level == PRODLEVEL_CLOSURE) {
 
			DrawString(ir, STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE);
 
			ir.top += FONT_HEIGHT_NORMAL + WidgetDimensions::scaled.vsep_wide;
 
		}
 

	
 
		CargoSuffix cargo_suffix[lengthof(i->accepts_cargo)];
 
		GetAllCargoSuffixes(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, i->accepts_cargo, cargo_suffix);
 
		bool stockpiling = HasBit(ind->callback_mask, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_mask, CBM_IND_PRODUCTION_256_TICKS);
 

	
 
		for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
 
			if (!IsValidCargoID(i->accepts_cargo[j])) continue;
 
		for (const auto &a : i->accepted) {
 
			if (!IsValidCargoID(a.cargo)) continue;
 
			has_accept = true;
 
			if (first) {
 
				DrawString(ir, STR_INDUSTRY_VIEW_REQUIRES);
 
				ir.top += FONT_HEIGHT_NORMAL;
 
				first = false;
 
			}
 
			SetDParam(0, CargoSpec::Get(i->accepts_cargo[j])->name);
 
			SetDParam(1, i->accepts_cargo[j]);
 
			SetDParam(2, i->incoming_cargo_waiting[j]);
 

	
 
			CargoSuffix suffix;
 
			GetCargoSuffix(CARGOSUFFIX_IN, CST_VIEW, i, i->type, ind, a.cargo, &a - i->accepted.data(), suffix);
 

	
 
			SetDParam(0, CargoSpec::Get(a.cargo)->name);
 
			SetDParam(1, a.cargo);
 
			SetDParam(2, a.waiting);
 
			SetDParamStr(3, "");
 
			StringID str = STR_NULL;
 
			switch (cargo_suffix[j].display) {
 
			switch (suffix.display) {
 
				case CSD_CARGO_AMOUNT_TEXT:
 
					SetDParamStr(3, cargo_suffix[j].text);
 
					SetDParamStr(3, suffix.text);
 
					FALLTHROUGH;
 
				case CSD_CARGO_AMOUNT:
 
					str = stockpiling ? STR_INDUSTRY_VIEW_ACCEPT_CARGO_AMOUNT : STR_INDUSTRY_VIEW_ACCEPT_CARGO;
 
					break;
 

	
 
				case CSD_CARGO_TEXT:
 
					SetDParamStr(3, cargo_suffix[j].text);
 
					SetDParamStr(3, suffix.text);
 
					FALLTHROUGH;
 
				case CSD_CARGO:
 
					str = STR_INDUSTRY_VIEW_ACCEPT_CARGO;
 
					break;
 

	
 
				default:
 
					NOT_REACHED();
 
			}
 
			DrawString(ir.Indent(WidgetDimensions::scaled.hsep_indent, rtl), str);
 
			ir.top += FONT_HEIGHT_NORMAL;
 
		}
 

	
 
		GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, i->produced_cargo, cargo_suffix);
 
		int line_height = this->editable == EA_RATE ? this->cheat_line_height : FONT_HEIGHT_NORMAL;
 
		int text_y_offset = (line_height - FONT_HEIGHT_NORMAL) / 2;
 
		int button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
 
		first = true;
 
		for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
			if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
		for (const auto &p : i->produced) {
 
			if (!IsValidCargoID(p.cargo)) continue;
 
			if (first) {
 
				if (has_accept) ir.top += WidgetDimensions::scaled.vsep_wide;
 
				DrawString(ir, STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE);
 
				ir.top += FONT_HEIGHT_NORMAL;
 
				if (this->editable == EA_RATE) this->production_offset_y = ir.top;
 
				first = false;
 
			}
 

	
 
			SetDParam(0, i->produced_cargo[j]);
 
			SetDParam(1, i->last_month_production[j]);
 
			SetDParamStr(2, cargo_suffix[j].text);
 
			SetDParam(3, ToPercent8(i->last_month_pct_transported[j]));
 
			CargoSuffix suffix;
 
			GetCargoSuffix(CARGOSUFFIX_OUT, CST_VIEW, i, i->type, ind, p.cargo, &p - i->produced.data(), suffix);
 

	
 
			SetDParam(0, p.cargo);
 
			SetDParam(1, p.history[LAST_MONTH].production);
 
			SetDParamStr(2, suffix.text);
 
			SetDParam(3, ToPercent8(p.history[LAST_MONTH].PctTransported()));
 
			DrawString(ir.Indent(WidgetDimensions::scaled.hsep_indent + (this->editable == EA_RATE ? SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal : 0), rtl).Translate(0, text_y_offset), STR_INDUSTRY_VIEW_TRANSPORTED);
 
			/* Let's put out those buttons.. */
 
			if (this->editable == EA_RATE) {
 
				DrawArrowButtons(ir.Indent(WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + j) ? this->clicked_button : 0,
 
						i->production_rate[j] > 0, i->production_rate[j] < 255);
 
				DrawArrowButtons(ir.Indent(WidgetDimensions::scaled.hsep_indent, rtl).WithWidth(SETTING_BUTTON_WIDTH, rtl).left, ir.top + button_y_offset, COLOUR_YELLOW, (this->clicked_line == IL_RATE1 + (&p - i->produced.data())) ? this->clicked_button : 0,
 
						p.rate > 0, p.rate < 255);
 
			}
 
			ir.top += line_height;
 
		}
 

	
 
		/* Display production multiplier if editable */
 
		if (this->editable == EA_MULTIPLIER) {
 
			line_height = this->cheat_line_height;
 
			text_y_offset = (line_height - FONT_HEIGHT_NORMAL) / 2;
 
			button_y_offset = (line_height - SETTING_BUTTON_HEIGHT) / 2;
 
			ir.top += WidgetDimensions::scaled.vsep_wide;
 
			this->production_offset_y = ir.top;
 
			SetDParam(0, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT));
 
@@ -971,29 +1002,29 @@ public:
 
				InfoLine line = IL_NONE;
 

	
 
				switch (this->editable) {
 
					case EA_NONE: break;
 

	
 
					case EA_MULTIPLIER:
 
						if (IsInsideBS(pt.y, this->production_offset_y, this->cheat_line_height)) line = IL_MULTIPLIER;
 
						break;
 

	
 
					case EA_RATE:
 
						if (pt.y >= this->production_offset_y) {
 
							int row = (pt.y - this->production_offset_y) / this->cheat_line_height;
 
							for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
 
								if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
							for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
 
								if (!IsValidCargoID(itp->cargo)) continue;
 
								row--;
 
								if (row < 0) {
 
									line = (InfoLine)(IL_RATE1 + j);
 
									line = (InfoLine)(IL_RATE1 + (itp - std::begin(i->produced)));
 
									break;
 
								}
 
							}
 
						}
 
						break;
 
				}
 
				if (line == IL_NONE) return;
 

	
 
				bool rtl = _current_text_dir == TD_RTL;
 
				Rect r = this->GetWidget<NWidgetBase>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.framerect).Indent(WidgetDimensions::scaled.hsep_indent, rtl);
 

	
 
				if (r.WithWidth(SETTING_BUTTON_WIDTH, rtl).Contains(pt)) {
 
@@ -1003,53 +1034,53 @@ public:
 
						case EA_MULTIPLIER:
 
							if (decrease) {
 
								if (i->prod_level <= PRODLEVEL_MINIMUM) return;
 
								i->prod_level = static_cast<byte>(std::max<uint>(i->prod_level / 2, PRODLEVEL_MINIMUM));
 
							} else {
 
								if (i->prod_level >= PRODLEVEL_MAXIMUM) return;
 
								i->prod_level = static_cast<byte>(std::min<uint>(i->prod_level * 2, PRODLEVEL_MAXIMUM));
 
							}
 
							break;
 

	
 
						case EA_RATE:
 
							if (decrease) {
 
								if (i->production_rate[line - IL_RATE1] <= 0) return;
 
								i->production_rate[line - IL_RATE1] = std::max(i->production_rate[line - IL_RATE1] / 2, 0);
 
								if (i->produced[line - IL_RATE1].rate <= 0) return;
 
								i->produced[line - IL_RATE1].rate = std::max(i->produced[line - IL_RATE1].rate / 2, 0);
 
							} else {
 
								if (i->production_rate[line - IL_RATE1] >= 255) return;
 
								if (i->produced[line - IL_RATE1].rate >= 255) return;
 
								/* a zero production industry is unlikely to give anything but zero, so push it a little bit */
 
								int new_prod = i->production_rate[line - IL_RATE1] == 0 ? 1 : i->production_rate[line - IL_RATE1] * 2;
 
								i->production_rate[line - IL_RATE1] = ClampTo<byte>(new_prod);
 
								int new_prod = i->produced[line - IL_RATE1].rate == 0 ? 1 : i->produced[line - IL_RATE1].rate * 2;
 
								i->produced[line - IL_RATE1].rate = ClampTo<byte>(new_prod);
 
							}
 
							break;
 

	
 
						default: NOT_REACHED();
 
					}
 

	
 
					UpdateIndustryProduction(i);
 
					this->SetDirty();
 
					this->SetTimeout();
 
					this->clicked_line = line;
 
					this->clicked_button = (decrease ^ rtl) ? 1 : 2;
 
				} else if (r.Indent(SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_normal, rtl).Contains(pt)) {
 
					/* clicked the text */
 
					this->editbox_line = line;
 
					switch (this->editable) {
 
						case EA_MULTIPLIER:
 
							SetDParam(0, RoundDivSU(i->prod_level * 100, PRODLEVEL_DEFAULT));
 
							ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION_LEVEL, 10, this, CS_ALPHANUMERAL, QSF_NONE);
 
							break;
 

	
 
						case EA_RATE:
 
							SetDParam(0, i->production_rate[line - IL_RATE1] * 8);
 
							SetDParam(0, i->produced[line - IL_RATE1].rate * 8);
 
							ShowQueryString(STR_JUST_INT, STR_CONFIG_GAME_PRODUCTION, 10, this, CS_ALPHANUMERAL, QSF_NONE);
 
							break;
 

	
 
						default: NOT_REACHED();
 
					}
 
				}
 
				break;
 
			}
 

	
 
			case WID_IV_GOTO: {
 
				Industry *i = Industry::Get(this->window_number);
 
				if (_ctrl_pressed) {
 
@@ -1090,25 +1121,25 @@ public:
 
		if (StrEmpty(str)) return;
 

	
 
		Industry *i = Industry::Get(this->window_number);
 
		uint value = atoi(str);
 
		switch (this->editbox_line) {
 
			case IL_NONE: NOT_REACHED();
 

	
 
			case IL_MULTIPLIER:
 
				i->prod_level = ClampU(RoundDivSU(value * PRODLEVEL_DEFAULT, 100), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
 
				break;
 

	
 
			default:
 
				i->production_rate[this->editbox_line - IL_RATE1] = ClampU(RoundDivSU(value, 8), 0, 255);
 
				i->produced[this->editbox_line - IL_RATE1].rate = ClampU(RoundDivSU(value, 8), 0, 255);
 
				break;
 
		}
 
		UpdateIndustryProduction(i);
 
		this->SetDirty();
 
	}
 

	
 
	/**
 
	 * Some data on this window has become invalid.
 
	 * @param data Information about the changed data.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	void OnInvalidateData(int data = 0, bool gui_scope = true) override
 
@@ -1130,27 +1161,27 @@ public:
 

	
 
	void ShowNewGRFInspectWindow() const override
 
	{
 
		::ShowNewGRFInspectWindow(GSF_INDUSTRIES, this->window_number);
 
	}
 
};
 

	
 
static void UpdateIndustryProduction(Industry *i)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 
	if (indspec->UsesOriginalEconomy()) i->RecomputeProductionMultipliers();
 

	
 
	for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
		if (IsValidCargoID(i->produced_cargo[j])) {
 
			i->last_month_production[j] = 8 * i->production_rate[j];
 
	for (auto &p : i->produced) {
 
		if (IsValidCargoID(p.cargo)) {
 
			p.history[LAST_MONTH].production = 8 * p.rate;
 
		}
 
	}
 
}
 

	
 
/** Widget definition of the view industry gui */
 
static const NWidgetPart _nested_industry_view_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_CREAM),
 
		NWidget(WWT_CAPTION, COLOUR_CREAM, WID_IV_CAPTION), SetDataTip(STR_INDUSTRY_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
 
		NWidget(WWT_PUSHIMGBTN, COLOUR_CREAM, WID_IV_GOTO), SetMinimalSize(12, 14), SetDataTip(SPR_GOTO_LOCATION, STR_INDUSTRY_VIEW_LOCATION_TOOLTIP),
 
		NWidget(WWT_DEBUGBOX, COLOUR_CREAM),
 
		NWidget(WWT_SHADEBOX, COLOUR_CREAM),
 
@@ -1406,57 +1437,55 @@ protected:
 
		this->vscroll->SetCount(this->industries.size()); // Update scrollbar as well.
 

	
 
		this->SetDirty();
 
	}
 

	
 
	/**
 
	 * Returns percents of cargo transported if industry produces this cargo, else -1
 
	 *
 
	 * @param i industry to check
 
	 * @param id cargo slot
 
	 * @return percents of cargo transported, or -1 if industry doesn't use this cargo slot
 
	 */
 
	static inline int GetCargoTransportedPercentsIfValid(const Industry *i, uint id)
 
	static inline int GetCargoTransportedPercentsIfValid(const Industry::ProducedCargo &p)
 
	{
 
		assert(id < lengthof(i->produced_cargo));
 

	
 
		if (!IsValidCargoID(i->produced_cargo[id])) return -1;
 
		return ToPercent8(i->last_month_pct_transported[id]);
 
		if (!IsValidCargoID(p.cargo)) return -1;
 
		return ToPercent8(p.history[LAST_MONTH].PctTransported());
 
	}
 

	
 
	/**
 
	 * Returns value representing industry's transported cargo
 
	 *  percentage for industry sorting
 
	 *
 
	 * @param i industry to check
 
	 * @return value used for sorting
 
	 */
 
	static int GetCargoTransportedSortValue(const Industry *i)
 
	{
 
		CargoID filter = IndustryDirectoryWindow::produced_cargo_filter;
 
		if (filter == CF_NONE) return 0;
 

	
 
		int percentage = 0, produced_cargo_count = 0;
 
		for (uint id = 0; id < lengthof(i->produced_cargo); id++) {
 
		for (const auto &p : i->produced) {
 
			if (filter == CF_ANY) {
 
				int transported = GetCargoTransportedPercentsIfValid(i, id);
 
				int transported = GetCargoTransportedPercentsIfValid(p);
 
				if (transported != -1) {
 
					produced_cargo_count++;
 
					percentage += transported;
 
				}
 
				if (produced_cargo_count == 0 && id == lengthof(i->produced_cargo) - 1 && percentage == 0) {
 
				if (produced_cargo_count == 0 && &p == &i->produced.back() && percentage == 0) {
 
					return transported;
 
				}
 
			} else if (filter == i->produced_cargo[id]) {
 
				return GetCargoTransportedPercentsIfValid(i, id);
 
			} else if (filter == p.cargo) {
 
				return GetCargoTransportedPercentsIfValid(p);
 
			}
 
		}
 

	
 
		if (produced_cargo_count == 0) return percentage;
 
		return percentage / produced_cargo_count;
 
	}
 

	
 
	/** Sort industries by name */
 
	static bool IndustryNameSorter(const Industry * const &a, const Industry * const &b)
 
	{
 
		int r = StrNaturalCompare(a->GetCachedName(), b->GetCachedName()); // Sort by name (natural sorting).
 
		if (r == 0) return a->index < b->index;
 
@@ -1472,31 +1501,31 @@ protected:
 
		while (it_b != NUM_INDUSTRYTYPES && b->type != _sorted_industry_types[it_b]) it_b++;
 
		int r = it_a - it_b;
 
		return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
 
	}
 

	
 
	/** Sort industries by production and name */
 
	static bool IndustryProductionSorter(const Industry * const &a, const Industry * const &b)
 
	{
 
		CargoID filter = IndustryDirectoryWindow::produced_cargo_filter;
 
		if (filter == CF_NONE) return IndustryTypeSorter(a, b);
 

	
 
		uint prod_a = 0, prod_b = 0;
 
		for (uint i = 0; i < lengthof(a->produced_cargo); i++) {
 
		for (auto ita = std::begin(a->produced), itb = std::begin(b->produced); ita != std::end(a->produced) && itb != std::end(b->produced); ++ita, ++itb) {
 
			if (filter == CF_ANY) {
 
				if (IsValidCargoID(a->produced_cargo[i])) prod_a += a->last_month_production[i];
 
				if (IsValidCargoID(b->produced_cargo[i])) prod_b += b->last_month_production[i];
 
				if (IsValidCargoID(ita->cargo)) prod_a += ita->history[LAST_MONTH].production;
 
				if (IsValidCargoID(itb->cargo)) prod_b += ita->history[LAST_MONTH].production;
 
			} else {
 
				if (a->produced_cargo[i] == filter) prod_a += a->last_month_production[i];
 
				if (b->produced_cargo[i] == filter) prod_b += b->last_month_production[i];
 
				if (ita->cargo == filter) prod_a += ita->history[LAST_MONTH].production;
 
				if (itb->cargo == filter) prod_b += itb->history[LAST_MONTH].production;
 
			}
 
		}
 
		int r = prod_a - prod_b;
 

	
 
		return (r == 0) ? IndustryTypeSorter(a, b) : r < 0;
 
	}
 

	
 
	/** Sort industries by transported cargo and name */
 
	static bool IndustryTransportedCargoSorter(const Industry * const &a, const Industry * const &b)
 
	{
 
		int r = GetCargoTransportedSortValue(a) - GetCargoTransportedSortValue(b);
 
		return (r == 0) ? IndustryNameSorter(a, b) : r < 0;
 
@@ -1506,39 +1535,39 @@ protected:
 
	 * Get the StringID to draw and set the appropriate DParams.
 
	 * @param i the industry to get the StringID of.
 
	 * @return the StringID.
 
	 */
 
	StringID GetIndustryString(const Industry *i) const
 
	{
 
		const IndustrySpec *indsp = GetIndustrySpec(i->type);
 
		byte p = 0;
 

	
 
		/* Industry name */
 
		SetDParam(p++, i->index);
 

	
 
		static CargoSuffix cargo_suffix[lengthof(i->produced_cargo)];
 
		GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
 
		static CargoSuffix cargo_suffix[INDUSTRY_NUM_OUTPUTS];
 

	
 
		/* Get industry productions (CargoID, production, suffix, transported) */
 
		struct CargoInfo {
 
			CargoID cargo_id;
 
			uint16 production;
 
			const char *suffix;
 
			uint transported;
 
		};
 
		std::vector<CargoInfo> cargos;
 

	
 
		for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
			if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
			cargos.push_back({ i->produced_cargo[j], i->last_month_production[j], cargo_suffix[j].text.c_str(), ToPercent8(i->last_month_pct_transported[j]) });
 
		for (auto itp = std::begin(i->produced); itp != std::end(i->produced); ++itp) {
 
			if (!IsValidCargoID(itp->cargo)) continue;
 
			GetCargoSuffix(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, itp->cargo, itp - std::begin(i->produced), cargo_suffix[itp - std::begin(i->produced)]);
 
			cargos.push_back({ itp->cargo, itp->history[LAST_MONTH].production, cargo_suffix[itp - std::begin(i->produced)].text.c_str(), ToPercent8(itp->history[LAST_MONTH].PctTransported()) });
 
		}
 

	
 
		switch (static_cast<IndustryDirectoryWindow::SorterType>(this->industries.SortType())) {
 
			case IndustryDirectoryWindow::SorterType::ByName:
 
			case IndustryDirectoryWindow::SorterType::ByType:
 
			case IndustryDirectoryWindow::SorterType::ByProduction:
 
				/* Sort by descending production, then descending transported */
 
				std::sort(cargos.begin(), cargos.end(), [](const CargoInfo &a, const CargoInfo &b) {
 
					if (a.production != b.production) return a.production > b.production;
 
					return a.transported > b.transported;
 
				});
 
				break;
src/newgrf_industries.cpp
Show inline comments
 
@@ -205,27 +205,27 @@ static uint32 GetCountAndDistanceOfClose
 
		*available = false;
 
		return UINT_MAX;
 
	}
 

	
 
	switch (variable) {
 
		case 0x40:
 
		case 0x41:
 
		case 0x42: { // waiting cargo, but only if those two callback flags are set
 
			uint16 callback = indspec->callback_mask;
 
			if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) {
 
				if ((indspec->behaviour & INDUSTRYBEH_PROD_MULTI_HNDLING) != 0) {
 
					if (this->industry->prod_level == 0) return 0;
 
					return ClampTo<uint16>(this->industry->incoming_cargo_waiting[variable - 0x40] / this->industry->prod_level);
 
					return ClampTo<uint16>(this->industry->accepted[variable - 0x40].waiting / this->industry->prod_level);
 
				} else {
 
					return ClampTo<uint16>(this->industry->incoming_cargo_waiting[variable - 0x40]);
 
					return ClampTo<uint16>(this->industry->accepted[variable - 0x40].waiting);
 
				}
 
			} else {
 
				return 0;
 
			}
 
		}
 

	
 
		/* Manhattan distance of closes dry/water tile */
 
		case 0x43:
 
			if (this->tile == INVALID_TILE) break;
 
			return GetClosestWaterDistance(this->tile, (indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) == 0);
 

	
 
		/* Layout number */
 
@@ -308,113 +308,113 @@ static uint32 GetCountAndDistanceOfClose
 
			return GetCountAndDistanceOfClosestInstance(parameter, layout_filter, town_filter, this->industry);
 
		}
 

	
 
		case 0x69:
 
		case 0x6A:
 
		case 0x6B:
 
		case 0x6C:
 
		case 0x6D:
 
		case 0x70:
 
		case 0x71: {
 
			CargoID cargo = GetCargoTranslation(parameter, this->ro.grffile);
 
			if (!IsValidCargoID(cargo)) return 0;
 
			int index = this->industry->GetCargoProducedIndex(cargo);
 
			if (index < 0) return 0; // invalid cargo
 
			auto it = this->industry->GetCargoProduced(cargo);
 
			if (it == std::end(this->industry->produced)) return 0; // invalid cargo
 
			switch (variable) {
 
				case 0x69: return this->industry->produced_cargo_waiting[index];
 
				case 0x6A: return this->industry->this_month_production[index];
 
				case 0x6B: return this->industry->this_month_transported[index];
 
				case 0x6C: return this->industry->last_month_production[index];
 
				case 0x6D: return this->industry->last_month_transported[index];
 
				case 0x70: return this->industry->production_rate[index];
 
				case 0x71: return this->industry->last_month_pct_transported[index];
 
				case 0x69: return it->waiting;
 
				case 0x6A: return it->history[THIS_MONTH].production;
 
				case 0x6B: return it->history[THIS_MONTH].transported;
 
				case 0x6C: return it->history[LAST_MONTH].production;
 
				case 0x6D: return it->history[LAST_MONTH].transported;
 
				case 0x70: return it->rate;
 
				case 0x71: return it->history[LAST_MONTH].PctTransported();
 
				default: NOT_REACHED();
 
			}
 
		}
 

	
 

	
 
		case 0x6E:
 
		case 0x6F: {
 
			CargoID cargo = GetCargoTranslation(parameter, this->ro.grffile);
 
			if (!IsValidCargoID(cargo)) return 0;
 
			int index = this->industry->GetCargoAcceptedIndex(cargo);
 
			if (index < 0) return 0; // invalid cargo
 
			if (variable == 0x6E) return this->industry->last_cargo_accepted_at[index];
 
			if (variable == 0x6F) return this->industry->incoming_cargo_waiting[index];
 
			auto it = this->industry->GetCargoAccepted(cargo);
 
			if (it == std::end(this->industry->accepted)) return 0; // invalid cargo
 
			if (variable == 0x6E) return it->last_accepted;
 
			if (variable == 0x6F) return it->waiting;
 
			NOT_REACHED();
 
		}
 

	
 
		/* Get a variable from the persistent storage */
 
		case 0x7C: return (this->industry->psa != nullptr) ? this->industry->psa->GetValue(parameter) : 0;
 

	
 
		/* Industry structure access*/
 
		case 0x80: return this->industry->location.tile;
 
		case 0x81: return GB(this->industry->location.tile, 8, 8);
 
		/* Pointer to the town the industry is associated with */
 
		case 0x82: return this->industry->town->index;
 
		case 0x83:
 
		case 0x84:
 
		case 0x85: Debug(grf, 0, "NewGRFs shouldn't be doing pointer magic"); break; // not supported
 
		case 0x86: return this->industry->location.w;
 
		case 0x87: return this->industry->location.h;// xy dimensions
 

	
 
		case 0x88:
 
		case 0x89: return this->industry->produced_cargo[variable - 0x88];
 
		case 0x8A: return this->industry->produced_cargo_waiting[0];
 
		case 0x8B: return GB(this->industry->produced_cargo_waiting[0], 8, 8);
 
		case 0x8C: return this->industry->produced_cargo_waiting[1];
 
		case 0x8D: return GB(this->industry->produced_cargo_waiting[1], 8, 8);
 
		case 0x89: return this->industry->produced[variable - 0x88].cargo;
 
		case 0x8A: return this->industry->produced[0].waiting;
 
		case 0x8B: return GB(this->industry->produced[0].waiting, 8, 8);
 
		case 0x8C: return this->industry->produced[1].waiting;
 
		case 0x8D: return GB(this->industry->produced[1].waiting, 8, 8);
 
		case 0x8E:
 
		case 0x8F: return this->industry->production_rate[variable - 0x8E];
 
		case 0x8F: return this->industry->produced[variable - 0x8E].rate;
 
		case 0x90:
 
		case 0x91:
 
		case 0x92: return this->industry->accepts_cargo[variable - 0x90];
 
		case 0x92: return this->industry->accepted[variable - 0x90].cargo;
 
		case 0x93: return this->industry->prod_level;
 
		/* amount of cargo produced so far THIS month. */
 
		case 0x94: return this->industry->this_month_production[0];
 
		case 0x95: return GB(this->industry->this_month_production[0], 8, 8);
 
		case 0x96: return this->industry->this_month_production[1];
 
		case 0x97: return GB(this->industry->this_month_production[1], 8, 8);
 
		case 0x94: return this->industry->produced[0].history[THIS_MONTH].production;
 
		case 0x95: return GB(this->industry->produced[0].history[THIS_MONTH].production, 8, 8);
 
		case 0x96: return this->industry->produced[1].history[THIS_MONTH].production;
 
		case 0x97: return GB(this->industry->produced[1].history[THIS_MONTH].production, 8, 8);
 
		/* amount of cargo transported so far THIS month. */
 
		case 0x98: return this->industry->this_month_transported[0];
 
		case 0x99: return GB(this->industry->this_month_transported[0], 8, 8);
 
		case 0x9A: return this->industry->this_month_transported[1];
 
		case 0x9B: return GB(this->industry->this_month_transported[1], 8, 8);
 
		case 0x98: return this->industry->produced[0].history[THIS_MONTH].transported;
 
		case 0x99: return GB(this->industry->produced[0].history[THIS_MONTH].transported, 8, 8);
 
		case 0x9A: return this->industry->produced[1].history[THIS_MONTH].transported;
 
		case 0x9B: return GB(this->industry->produced[1].history[THIS_MONTH].transported, 8, 8);
 
		/* fraction of cargo transported LAST month. */
 
		case 0x9C:
 
		case 0x9D: return this->industry->last_month_pct_transported[variable - 0x9C];
 
		case 0x9D: return this->industry->produced[variable - 0x9C].history[LAST_MONTH].PctTransported();
 
		/* amount of cargo produced LAST month. */
 
		case 0x9E: return this->industry->last_month_production[0];
 
		case 0x9F: return GB(this->industry->last_month_production[0], 8, 8);
 
		case 0xA0: return this->industry->last_month_production[1];
 
		case 0xA1: return GB(this->industry->last_month_production[1], 8, 8);
 
		case 0x9E: return this->industry->produced[0].history[LAST_MONTH].production;
 
		case 0x9F: return GB(this->industry->produced[0].history[LAST_MONTH].production, 8, 8);
 
		case 0xA0: return this->industry->produced[1].history[LAST_MONTH].production;
 
		case 0xA1: return GB(this->industry->produced[1].history[LAST_MONTH].production, 8, 8);
 
		/* amount of cargo transported last month. */
 
		case 0xA2: return this->industry->last_month_transported[0];
 
		case 0xA3: return GB(this->industry->last_month_transported[0], 8, 8);
 
		case 0xA4: return this->industry->last_month_transported[1];
 
		case 0xA5: return GB(this->industry->last_month_transported[1], 8, 8);
 
		case 0xA2: return this->industry->produced[0].history[LAST_MONTH].transported;
 
		case 0xA3: return GB(this->industry->produced[0].history[LAST_MONTH].transported, 8, 8);
 
		case 0xA4: return this->industry->produced[1].history[LAST_MONTH].transported;
 
		case 0xA5: return GB(this->industry->produced[1].history[LAST_MONTH].transported, 8, 8);
 

	
 
		case 0xA6: return indspec->grf_prop.local_id;
 
		case 0xA7: return this->industry->founder;
 
		case 0xA8: return this->industry->random_colour;
 
		case 0xA9: return ClampTo<uint8_t>(this->industry->last_prod_year - ORIGINAL_BASE_YEAR);
 
		case 0xAA: return this->industry->counter;
 
		case 0xAB: return GB(this->industry->counter, 8, 8);
 
		case 0xAC: return this->industry->was_cargo_delivered;
 

	
 
		case 0xB0: return ClampTo<uint16_t>(this->industry->construction_date - DAYS_TILL_ORIGINAL_BASE_YEAR); // Date when built since 1920 (in days)
 
		case 0xB3: return this->industry->construction_type; // Construction type
 
		case 0xB4: {
 
			TimerGameCalendar::Date *latest = std::max_element(this->industry->last_cargo_accepted_at, endof(this->industry->last_cargo_accepted_at));
 
			return ClampTo<uint16_t>((*latest) - DAYS_TILL_ORIGINAL_BASE_YEAR); // Date last cargo accepted since 1920 (in days)
 
			auto it = std::max_element(std::begin(this->industry->accepted), std::end(this->industry->accepted), [](const auto &a, const auto &b) { return a.last_accepted < b.last_accepted; });
 
			return ClampTo<uint16_t>(it->last_accepted - DAYS_TILL_ORIGINAL_BASE_YEAR); // Date last cargo accepted since 1920 (in days)
 
		}
 
	}
 

	
 
	Debug(grf, 1, "Unhandled industry variable 0x{:X}", variable);
 

	
 
	*available = false;
 
	return UINT_MAX;
 
}
 

	
 
/* virtual */ uint32 IndustriesScopeResolver::GetRandomBits() const
 
{
 
	return this->industry != nullptr ? this->industry->random : 0;
 
@@ -634,40 +634,40 @@ void IndustryProductionCallback(Industry
 
			SetDParam(2, ind->location.tile);
 
			ShowErrorMessage(STR_NEWGRF_BUGGY, STR_NEWGRF_BUGGY_INVALID_CARGO_PRODUCTION_CALLBACK, WL_WARNING);
 

	
 
			/* abort the function early, this error isn't critical and will allow the game to continue to run */
 
			break;
 
		}
 

	
 
		bool deref = (group->version >= 1);
 

	
 
		if (group->version < 2) {
 
			/* Callback parameters map directly to industry cargo slot indices */
 
			for (uint i = 0; i < group->num_input; i++) {
 
				ind->incoming_cargo_waiting[i] = ClampTo<uint16_t>(ind->incoming_cargo_waiting[i] - DerefIndProd(group->subtract_input[i], deref) * multiplier);
 
				ind->accepted[i].waiting = ClampTo<uint16_t>(ind->accepted[i].waiting - DerefIndProd(group->subtract_input[i], deref) * multiplier);
 
			}
 
			for (uint i = 0; i < group->num_output; i++) {
 
				ind->produced_cargo_waiting[i] = ClampTo<uint16_t>(ind->produced_cargo_waiting[i] + std::max(DerefIndProd(group->add_output[i], deref), 0) * multiplier);
 
				ind->produced[i].waiting = ClampTo<uint16_t>(ind->produced[i].waiting + std::max(DerefIndProd(group->add_output[i], deref), 0) * multiplier);
 
			}
 
		} else {
 
			/* Callback receives list of cargos to apply for, which need to have their cargo slots in industry looked up */
 
			for (uint i = 0; i < group->num_input; i++) {
 
				int cargo_index = ind->GetCargoAcceptedIndex(group->cargo_input[i]);
 
				if (cargo_index < 0) continue;
 
				ind->incoming_cargo_waiting[cargo_index] = ClampTo<uint16_t>(ind->incoming_cargo_waiting[cargo_index] - DerefIndProd(group->subtract_input[i], deref) * multiplier);
 
				auto it = ind->GetCargoAccepted(group->cargo_input[i]);
 
				if (it == std::end(ind->accepted)) continue;
 
				it->waiting = ClampTo<uint16_t>(it->waiting - DerefIndProd(group->subtract_input[i], deref) * multiplier);
 
			}
 
			for (uint i = 0; i < group->num_output; i++) {
 
				int cargo_index = ind->GetCargoProducedIndex(group->cargo_output[i]);
 
				if (cargo_index < 0) continue;
 
				ind->produced_cargo_waiting[cargo_index] = ClampTo<uint16_t>(ind->produced_cargo_waiting[cargo_index] + std::max(DerefIndProd(group->add_output[i], deref), 0) * multiplier);
 
				auto it = ind->GetCargoProduced(group->cargo_output[i]);
 
				if (it == std::end(ind->produced)) continue;
 
				it->waiting = ClampTo<uint16_t>(it->waiting + std::max(DerefIndProd(group->add_output[i], deref), 0) * multiplier);
 
			}
 
		}
 

	
 
		int32 again = DerefIndProd(group->again, deref);
 
		if (again == 0) break;
 

	
 
		SB(object.callback_param2, 24, 8, again);
 
	}
 

	
 
	SetWindowDirty(WC_INDUSTRY_VIEW, ind->index);
 
}
 

	
src/saveload/afterload.cpp
Show inline comments
 
@@ -1691,29 +1691,29 @@ bool AfterLoadGame()
 
		for (Station *st : Station::Iterate()) {
 
			for (CargoID c = 0; c < NUM_CARGO; c++) {
 
				st->goods[c].last_speed = 0;
 
				if (st->goods[c].cargo.AvailableCount() != 0) SetBit(st->goods[c].status, GoodsEntry::GES_RATING);
 
			}
 
		}
 
	}
 

	
 
	if (IsSavegameVersionBefore(SLV_78)) {
 
		uint j;
 
		for (Industry * i : Industry::Iterate()) {
 
			const IndustrySpec *indsp = GetIndustrySpec(i->type);
 
			for (j = 0; j < lengthof(i->produced_cargo); j++) {
 
				i->produced_cargo[j] = indsp->produced_cargo[j];
 
			for (j = 0; j < lengthof(i->produced); j++) {
 
				i->produced[j].cargo = indsp->produced_cargo[j];
 
			}
 
			for (j = 0; j < lengthof(i->accepts_cargo); j++) {
 
				i->accepts_cargo[j] = indsp->accepts_cargo[j];
 
			for (j = 0; j < lengthof(i->accepted); j++) {
 
				i->accepted[j].cargo = indsp->accepts_cargo[j];
 
			}
 
		}
 
	}
 

	
 
	/* Before version 81, the density of grass was always stored as zero, and
 
	 * grassy trees were always drawn fully grassy. Furthermore, trees on rough
 
	 * land used to have zero density, now they have full density. Therefore,
 
	 * make all grassy/rough land trees have a density of 3. */
 
	if (IsSavegameVersionBefore(SLV_81)) {
 
		for (auto t : Map::Iterate()) {
 
			if (GetTileType(t) == MP_TREES) {
 
				TreeGround groundType = (TreeGround)GB(t.m2(), 4, 2);
 
@@ -2997,45 +2997,41 @@ bool AfterLoadGame()
 
			if (t->growth_rate & 0x8000) SetBit(t->flags, TOWN_CUSTOM_GROWTH);
 
			if (t->growth_rate != TOWN_GROWTH_RATE_NONE) {
 
				t->growth_rate = TownTicksToGameTicks(t->growth_rate & ~0x8000);
 
			}
 
			/* Add t->index % TOWN_GROWTH_TICKS to spread growth across ticks. */
 
			t->grow_counter = TownTicksToGameTicks(t->grow_counter) + t->index % TOWN_GROWTH_TICKS;
 
		}
 
	}
 

	
 
	if (IsSavegameVersionBefore(SLV_EXTEND_INDUSTRY_CARGO_SLOTS)) {
 
		/* Make sure added industry cargo slots are cleared */
 
		for (Industry *i : Industry::Iterate()) {
 
			for (size_t ci = 2; ci < lengthof(i->produced_cargo); ci++) {
 
				i->produced_cargo[ci] = CT_INVALID;
 
				i->produced_cargo_waiting[ci] = 0;
 
				i->production_rate[ci] = 0;
 
				i->last_month_production[ci] = 0;
 
				i->last_month_transported[ci] = 0;
 
				i->last_month_pct_transported[ci] = 0;
 
				i->this_month_production[ci] = 0;
 
				i->this_month_transported[ci] = 0;
 
			for (auto it = std::begin(i->produced) + 2; it != std::end(i->produced); ++it) {
 
				it->cargo = CT_INVALID;
 
				it->waiting = 0;
 
				it->rate = 0;
 
				it->history = {};
 
			}
 
			for (size_t ci = 3; ci < lengthof(i->accepts_cargo); ci++) {
 
				i->accepts_cargo[ci] = CT_INVALID;
 
				i->incoming_cargo_waiting[ci] = 0;
 
			for (auto it = std::begin(i->accepted) + 3; it != std::end(i->accepted); ++it) {
 
				it->cargo = CT_INVALID;
 
				it->waiting = 0;
 
			}
 
			/* Make sure last_cargo_accepted_at is copied to elements for every valid input cargo.
 
			 * The loading routine should put the original singular value into the first array element. */
 
			for (size_t ci = 0; ci < lengthof(i->accepts_cargo); ci++) {
 
				if (IsValidCargoID(i->accepts_cargo[ci])) {
 
					i->last_cargo_accepted_at[ci] = i->last_cargo_accepted_at[0];
 
			for (auto &a : i->accepted) {
 
				if (IsValidCargoID(a.cargo)) {
 
					a.last_accepted = i->accepted[0].last_accepted;
 
				} else {
 
					i->last_cargo_accepted_at[ci] = 0;
 
					a.last_accepted = 0;
 
				}
 
			}
 
		}
 
	}
 

	
 
	if (IsSavegameVersionBefore(SLV_SHIPS_STOP_IN_LOCKS)) {
 
		/* Move ships from lock slope to upper or lower position. */
 
		for (Ship *s : Ship::Iterate()) {
 
			/* Suitable tile? */
 
			if (!IsTileType(s->tile, MP_WATER) || !IsLock(s->tile) || GetLockPart(s->tile) != LOCK_PART_MIDDLE) continue;
 

	
 
			/* We don't need to adjust position when at the tile centre */
src/saveload/compat/industry_sl_compat.h
Show inline comments
 
@@ -3,42 +3,60 @@
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file industry_sl_compat.h Loading of industry chunks before table headers were added. */
 

	
 
#ifndef SAVELOAD_COMPAT_INDUSTRY_H
 
#define SAVELOAD_COMPAT_INDUSTRY_H
 

	
 
#include "../saveload.h"
 

	
 
const SaveLoadCompat _industry_accepts_sl_compat[] = {
 
	SLC_VAR("cargo"),
 
	SLC_VAR("waiting"),
 
	SLC_VAR("last_accepted"),
 
};
 

	
 
const SaveLoadCompat _industry_produced_history_sl_compat[] = {
 
	SLC_VAR("production"),
 
	SLC_VAR("transported"),
 
};
 

	
 
const SaveLoadCompat _industry_produced_sl_compat[] = {
 
	SLC_VAR("cargo"),
 
	SLC_VAR("waiting"),
 
	SLC_VAR("rate"),
 
};
 

	
 
/** Original field order for _industry_desc. */
 
const SaveLoadCompat _industry_sl_compat[] = {
 
	SLC_VAR("location.tile"),
 
	SLC_VAR("location.w"),
 
	SLC_VAR("location.h"),
 
	SLC_VAR("town"),
 
	SLC_VAR("neutral_station"),
 
	SLC_NULL(2, SL_MIN_VERSION, SLV_61),
 
	SLC_VAR("produced_cargo"),
 
	SLC_VAR("incoming_cargo_waiting"),
 
	SLC_VAR("produced_cargo_waiting"),
 
	SLC_VAR("production_rate"),
 
	SLC_NULL(3, SL_MIN_VERSION, SLV_61),
 
	SLC_VAR("accepts_cargo"),
 
	SLC_VAR("prod_level"),
 
	SLC_VAR("this_month_production"),
 
	SLC_VAR("this_month_transported"),
 
	SLC_VAR("last_month_pct_transported"),
 
	SLC_NULL(2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLC_NULL(16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLC_VAR("last_month_production"),
 
	SLC_VAR("last_month_transported"),
 
	SLC_VAR("counter"),
 
	SLC_VAR("type"),
 
	SLC_VAR("owner"),
 
	SLC_VAR("random_colour"),
 
	SLC_VAR("last_prod_year"),
 
	SLC_VAR("was_cargo_delivered"),
 
	SLC_VAR("ctlflags"),
 
	SLC_VAR("founder"),
 
	SLC_VAR("construction_date"),
 
	SLC_VAR("construction_type"),
src/saveload/industry_sl.cpp
Show inline comments
 
@@ -10,112 +10,248 @@
 
#include "../stdafx.h"
 

	
 
#include "saveload.h"
 
#include "compat/industry_sl_compat.h"
 

	
 
#include "../industry.h"
 
#include "newgrf_sl.h"
 

	
 
#include "../safeguards.h"
 

	
 
static OldPersistentStorage _old_ind_persistent_storage;
 

	
 
class SlIndustryAccepted : public DefaultSaveLoadHandler<SlIndustryAccepted, Industry> {
 
public:
 
	inline static const SaveLoad description[] = {
 
		 SLE_VAR(Industry::AcceptedCargo, cargo, SLE_UINT8),
 
		 SLE_VAR(Industry::AcceptedCargo, waiting, SLE_UINT16),
 
		 SLE_VAR(Industry::AcceptedCargo, last_accepted, SLE_INT32),
 
	};
 
	inline const static SaveLoadCompatTable compat_description = _industry_accepts_sl_compat;
 

	
 
	void Save(Industry *i) const override
 
	{
 
		SlSetStructListLength(i->accepted.size());
 

	
 
		for (auto &a : i->accepted) {
 
			SlObject(&a, this->GetDescription());
 
		}
 
	}
 

	
 
	void Load(Industry *i) const override
 
	{
 
		size_t len = SlGetStructListLength(i->accepted.size());
 

	
 
		for (auto &a : i->accepted) {
 
			if (--len > i->accepted.size()) break; // unsigned so wraps after hitting zero.
 
			SlObject(&a, this->GetDescription());
 
		}
 
	}
 

	
 
	/* Old array structure used for savegames before SLV_INDUSTRY_CARGO_REORGANISE. */
 
	static CargoID old_cargo[INDUSTRY_NUM_INPUTS];
 
	static uint16_t old_waiting[INDUSTRY_NUM_INPUTS];
 
	static TimerGameCalendar::Date old_last_accepted[INDUSTRY_NUM_INPUTS];
 
};
 

	
 
/* static */ CargoID SlIndustryAccepted::old_cargo[INDUSTRY_NUM_INPUTS];
 
/* static */ uint16_t SlIndustryAccepted::old_waiting[INDUSTRY_NUM_INPUTS];
 
/* static */ TimerGameCalendar::Date SlIndustryAccepted::old_last_accepted[INDUSTRY_NUM_INPUTS];
 

	
 
class SlIndustryProducedHistory : public DefaultSaveLoadHandler<SlIndustryProducedHistory, Industry::ProducedCargo> {
 
public:
 
	inline static const SaveLoad description[] = {
 
		 SLE_VAR(Industry::ProducedHistory, production, SLE_UINT16),
 
		 SLE_VAR(Industry::ProducedHistory, transported, SLE_UINT16),
 
	};
 
	inline const static SaveLoadCompatTable compat_description = _industry_produced_history_sl_compat;
 

	
 
	void Save(Industry::ProducedCargo *p) const override
 
	{
 
		SlSetStructListLength(p->history.size());
 

	
 
		for (auto &h : p->history) {
 
			SlObject(&h, this->GetDescription());
 
		}
 
	}
 

	
 
	void Load(Industry::ProducedCargo *p) const override
 
	{
 
		size_t len = SlGetStructListLength(p->history.size());
 

	
 
		for (auto &h : p->history) {
 
			if (--len > p->history.size()) break; // unsigned so wraps after hitting zero.
 
			SlObject(&h, this->GetDescription());
 
		}
 
	}
 
};
 

	
 
class SlIndustryProduced : public DefaultSaveLoadHandler<SlIndustryProduced, Industry> {
 
public:
 
	inline static const SaveLoad description[] = {
 
		 SLE_VAR(Industry::ProducedCargo, cargo, SLE_UINT8),
 
		 SLE_VAR(Industry::ProducedCargo, waiting, SLE_UINT16),
 
		 SLE_VAR(Industry::ProducedCargo, rate, SLE_UINT8),
 
		SLEG_STRUCTLIST("history", SlIndustryProducedHistory),
 
	};
 
	inline const static SaveLoadCompatTable compat_description = _industry_produced_sl_compat;
 

	
 
	void Save(Industry *i) const override
 
	{
 
		SlSetStructListLength(i->produced.size());
 

	
 
		for (auto &p : i->produced) {
 
			SlObject(&p, this->GetDescription());
 
		}
 
	}
 

	
 
	void Load(Industry *i) const override
 
	{
 
		size_t len = SlGetStructListLength(i->produced.size());
 

	
 
		for (auto &p : i->produced) {
 
			if (--len > i->produced.size()) break; // unsigned so wraps after hitting zero.
 
			SlObject(&p, this->GetDescription());
 
		}
 
	}
 

	
 
	/* Old array structure used for savegames before SLV_INDUSTRY_CARGO_REORGANISE. */
 
	static CargoID old_cargo[INDUSTRY_NUM_OUTPUTS];
 
	static uint16_t old_waiting[INDUSTRY_NUM_OUTPUTS];
 
	static uint8_t old_rate[INDUSTRY_NUM_OUTPUTS];
 
	static uint16_t old_this_month_production[INDUSTRY_NUM_OUTPUTS];
 
	static uint16_t old_this_month_transported[INDUSTRY_NUM_OUTPUTS];
 
	static uint16_t old_last_month_production[INDUSTRY_NUM_OUTPUTS];
 
	static uint16_t old_last_month_transported[INDUSTRY_NUM_OUTPUTS];
 
};
 

	
 
/* static */ CargoID SlIndustryProduced::old_cargo[INDUSTRY_NUM_OUTPUTS];
 
/* static */ uint16_t SlIndustryProduced::old_waiting[INDUSTRY_NUM_OUTPUTS];
 
/* static */ uint8_t SlIndustryProduced::old_rate[INDUSTRY_NUM_OUTPUTS];
 
/* static */ uint16_t SlIndustryProduced::old_this_month_production[INDUSTRY_NUM_OUTPUTS];
 
/* static */ uint16_t SlIndustryProduced::old_this_month_transported[INDUSTRY_NUM_OUTPUTS];
 
/* static */ uint16_t SlIndustryProduced::old_last_month_production[INDUSTRY_NUM_OUTPUTS];
 
/* static */ uint16_t SlIndustryProduced::old_last_month_transported[INDUSTRY_NUM_OUTPUTS];
 

	
 
static const SaveLoad _industry_desc[] = {
 
	SLE_CONDVAR(Industry, location.tile,              SLE_FILE_U16 | SLE_VAR_U32,  SL_MIN_VERSION, SLV_6),
 
	SLE_CONDVAR(Industry, location.tile,              SLE_UINT32,                  SLV_6, SL_MAX_VERSION),
 
	    SLE_VAR(Industry, location.w,                 SLE_FILE_U8 | SLE_VAR_U16),
 
	    SLE_VAR(Industry, location.h,                 SLE_FILE_U8 | SLE_VAR_U16),
 
	    SLE_REF(Industry, town,                       REF_TOWN),
 
	SLE_CONDREF(Industry, neutral_station,            REF_STATION,                SLV_SERVE_NEUTRAL_INDUSTRIES, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, produced_cargo,             SLE_UINT8,   2,              SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, produced_cargo,             SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, incoming_cargo_waiting,     SLE_UINT16,  3,              SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, incoming_cargo_waiting,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, produced_cargo_waiting,     SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, produced_cargo_waiting,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, production_rate,            SLE_UINT8,   2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, production_rate,            SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, accepts_cargo,              SLE_UINT8,   3,              SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, accepts_cargo,              SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLEG_CONDARR("produced_cargo",             SlIndustryProduced::old_cargo,                  SLE_UINT8,   2, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("produced_cargo",             SlIndustryProduced::old_cargo,                  SLE_UINT8,  16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLEG_CONDARR("incoming_cargo_waiting",     SlIndustryAccepted::old_waiting,                SLE_UINT16,  3, SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("incoming_cargo_waiting",     SlIndustryAccepted::old_waiting,                SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLEG_CONDARR("produced_cargo_waiting",     SlIndustryProduced::old_waiting,                SLE_UINT16,  2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("produced_cargo_waiting",     SlIndustryProduced::old_waiting,                SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLEG_CONDARR("production_rate",            SlIndustryProduced::old_rate,                   SLE_UINT8,   2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("production_rate",            SlIndustryProduced::old_rate,                   SLE_UINT8,  16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLEG_CONDARR("accepts_cargo",              SlIndustryAccepted::old_cargo,                  SLE_UINT8,   3, SLV_78, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("accepts_cargo",              SlIndustryAccepted::old_cargo,                  SLE_UINT8,  16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	    SLE_VAR(Industry, prod_level,                 SLE_UINT8),
 
	SLE_CONDARR(Industry, this_month_production,      SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, this_month_production,      SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, this_month_transported,     SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, this_month_transported,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8,   2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, last_month_production,      SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_month_production,      SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, last_month_transported,     SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_month_transported,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLEG_CONDARR("this_month_production",      SlIndustryProduced::old_this_month_production,  SLE_UINT16,  2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("this_month_production",      SlIndustryProduced::old_this_month_production,  SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLEG_CONDARR("this_month_transported",     SlIndustryProduced::old_this_month_transported, SLE_UINT16,  2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("this_month_transported",     SlIndustryProduced::old_this_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLEG_CONDARR("last_month_production",      SlIndustryProduced::old_last_month_production,  SLE_UINT16,  2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("last_month_production",      SlIndustryProduced::old_last_month_production,  SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLEG_CONDARR("last_month_transported",     SlIndustryProduced::old_last_month_transported, SLE_UINT16,  2, SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("last_month_transported",     SlIndustryProduced::old_last_month_transported, SLE_UINT16, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 

	
 
	    SLE_VAR(Industry, counter,                    SLE_UINT16),
 

	
 
	    SLE_VAR(Industry, type,                       SLE_UINT8),
 
	    SLE_VAR(Industry, owner,                      SLE_UINT8),
 
	    SLE_VAR(Industry, random_colour,              SLE_UINT8),
 
	SLE_CONDVAR(Industry, last_prod_year,             SLE_FILE_U8 | SLE_VAR_I32,  SL_MIN_VERSION, SLV_31),
 
	SLE_CONDVAR(Industry, last_prod_year,             SLE_INT32,                 SLV_31, SL_MAX_VERSION),
 
	    SLE_VAR(Industry, was_cargo_delivered,        SLE_UINT8),
 
	SLE_CONDVAR(Industry, ctlflags,                   SLE_UINT8,                 SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Industry, founder,                    SLE_UINT8,                 SLV_70, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, construction_date,          SLE_INT32,                 SLV_70, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, construction_type,          SLE_UINT8,                 SLV_70, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, last_cargo_accepted_at[0],  SLE_INT32,                 SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_cargo_accepted_at,     SLE_INT32, 16,            SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLEG_CONDVAR("last_cargo_accepted_at[0]",  SlIndustryAccepted::old_last_accepted[0], SLE_INT32,     SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLEG_CONDARR("last_cargo_accepted_at",     SlIndustryAccepted::old_last_accepted,    SLE_INT32, 16, SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SLV_INDUSTRY_CARGO_REORGANISE),
 
	SLE_CONDVAR(Industry, selected_layout,            SLE_UINT8,                 SLV_73, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, exclusive_supplier,         SLE_UINT8,                 SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, exclusive_consumer,         SLE_UINT8,                 SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
 

	
 
	SLEG_CONDARR("storage", _old_ind_persistent_storage.storage, SLE_UINT32, 16, SLV_76, SLV_161),
 
	SLE_CONDREF(Industry, psa,                        REF_STORAGE,              SLV_161, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(Industry, random,                     SLE_UINT16,                SLV_82, SL_MAX_VERSION),
 
	SLE_CONDSSTR(Industry, text,     SLE_STR | SLF_ALLOW_CONTROL,     SLV_INDUSTRY_TEXT, SL_MAX_VERSION),
 

	
 
	SLEG_CONDSTRUCTLIST("accepted", SlIndustryAccepted,                          SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION),
 
	SLEG_CONDSTRUCTLIST("produced", SlIndustryProduced,                          SLV_INDUSTRY_CARGO_REORGANISE, SL_MAX_VERSION),
 
};
 

	
 
struct INDYChunkHandler : ChunkHandler {
 
	INDYChunkHandler() : ChunkHandler('INDY', CH_TABLE) {}
 

	
 
	void Save() const override
 
	{
 
		SlTableHeader(_industry_desc);
 

	
 
		/* Write the industries */
 
		for (Industry *ind : Industry::Iterate()) {
 
			SlSetArrayIndex(ind->index);
 
			SlObject(ind, _industry_desc);
 
		}
 
	}
 

	
 
	void LoadMoveAcceptsProduced(Industry *i) const
 
	{
 
		for (uint j = 0; j != INDUSTRY_NUM_INPUTS; ++j) {
 
			auto &a = i->accepted[j];
 
			a.cargo = SlIndustryAccepted::old_cargo[j];
 
			a.waiting = SlIndustryAccepted::old_waiting[j];
 
			a.last_accepted = SlIndustryAccepted::old_last_accepted[j];
 
		}
 

	
 
		for (uint j = 0; j != INDUSTRY_NUM_OUTPUTS; ++j) {
 
			auto &p = i->produced[j];
 
			p.cargo = SlIndustryProduced::old_cargo[j];
 
			p.waiting = SlIndustryProduced::old_waiting[j];
 
			p.rate = SlIndustryProduced::old_rate[j];
 
			p.history[THIS_MONTH].production = SlIndustryProduced::old_this_month_production[j];
 
			p.history[THIS_MONTH].transported = SlIndustryProduced::old_this_month_transported[j];
 
			p.history[LAST_MONTH].production = SlIndustryProduced::old_last_month_production[j];
 
			p.history[LAST_MONTH].transported = SlIndustryProduced::old_last_month_transported[j];
 
		}
 
	}
 

	
 
	void Load() const override
 
	{
 
		const std::vector<SaveLoad> slt = SlCompatTableHeader(_industry_desc, _industry_sl_compat);
 

	
 
		int index;
 

	
 
		Industry::ResetIndustryCounts();
 

	
 
		while ((index = SlIterateArray()) != -1) {
 
			Industry *i = new (index) Industry();
 
			SlObject(i, slt);
 

	
 
			/* Before savegame version 161, persistent storages were not stored in a pool. */
 
			if (IsSavegameVersionBefore(SLV_161) && !IsSavegameVersionBefore(SLV_76)) {
 
				/* Store the old persistent storage. The GRFID will be added later. */
 
				assert(PersistentStorage::CanAllocateItem());
 
				i->psa = new PersistentStorage(0, 0, 0);
 
				memcpy(i->psa->storage, _old_ind_persistent_storage.storage, sizeof(_old_ind_persistent_storage.storage));
 
			}
 
			if (IsSavegameVersionBefore(SLV_INDUSTRY_CARGO_REORGANISE)) LoadMoveAcceptsProduced(i);
 
			Industry::IncIndustryTypeCount(i->type);
 
		}
 
	}
 

	
 
	void FixPointers() const override
 
	{
 
		for (Industry *i : Industry::Iterate()) {
 
			SlObject(i, _industry_desc);
 
		}
 
	}
 
};
 

	
src/saveload/oldloader_sl.cpp
Show inline comments
 
@@ -786,48 +786,47 @@ static bool LoadOldStation(LoadgameState
 
	}
 

	
 
	return true;
 
}
 

	
 
static const OldChunks industry_chunk[] = {
 
	OCL_SVAR(   OC_TILE, Industry, location.tile ),
 
	OCL_VAR ( OC_UINT32,   1, &_old_town_index ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Industry, location.w ),
 
	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Industry, location.h ),
 
	OCL_NULL( 2 ),  ///< used to be industry's produced_cargo
 

	
 
	OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced_cargo_waiting[0] ),
 
	OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced_cargo_waiting[1] ),
 
	OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced_cargo_waiting[0] ),
 
	OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced_cargo_waiting[1] ),
 
	OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[0].waiting ),
 
	OCL_SVAR( OC_TTD | OC_UINT16, Industry, produced[1].waiting ),
 
	OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[0].waiting ),
 
	OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, produced[1].waiting ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, production_rate[0] ),
 
	OCL_SVAR(  OC_UINT8, Industry, production_rate[1] ),
 
	OCL_SVAR(  OC_UINT8, Industry, produced[0].rate ),
 
	OCL_SVAR(  OC_UINT8, Industry, produced[1].rate ),
 

	
 
	OCL_NULL( 3 ),  ///< used to be industry's accepts_cargo
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, prod_level ),
 

	
 
	OCL_SVAR( OC_UINT16, Industry, this_month_production[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, this_month_production[1] ),
 
	OCL_SVAR( OC_UINT16, Industry, this_month_transported[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, this_month_transported[1] ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].production ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[1].history[THIS_MONTH].production ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[0].history[THIS_MONTH].transported ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[1].history[THIS_MONTH].transported ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, last_month_pct_transported[0] ),
 
	OCL_SVAR(  OC_UINT8, Industry, last_month_pct_transported[1] ),
 
	OCL_NULL( 2 ), ///< last_month_pct_transported, now computed on the fly
 

	
 
	OCL_SVAR( OC_UINT16, Industry, last_month_production[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, last_month_production[1] ),
 
	OCL_SVAR( OC_UINT16, Industry, last_month_transported[0] ),
 
	OCL_SVAR( OC_UINT16, Industry, last_month_transported[1] ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].production ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[1].history[LAST_MONTH].production ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[0].history[LAST_MONTH].transported ),
 
	OCL_SVAR( OC_UINT16, Industry, produced[1].history[LAST_MONTH].transported ),
 

	
 
	OCL_SVAR(  OC_UINT8, Industry, type ),
 
	OCL_SVAR( OC_TTO | OC_FILE_U8 | OC_VAR_U16, Industry, counter ),
 
	OCL_SVAR(  OC_UINT8, Industry, owner ),
 
	OCL_SVAR(  OC_UINT8, Industry, random_colour ),
 
	OCL_SVAR( OC_TTD | OC_FILE_U8 | OC_VAR_I32, Industry, last_prod_year ),
 
	OCL_SVAR( OC_TTD | OC_UINT16, Industry, counter ),
 
	OCL_SVAR( OC_TTD | OC_UINT8, Industry, was_cargo_delivered ),
 

	
 
	OCL_CNULL( OC_TTD, 9 ), ///< Random junk at the end of this chunk
 

	
 
	OCL_END()
src/saveload/saveload.h
Show inline comments
 
@@ -347,24 +347,26 @@ enum SaveLoadVersion : uint16 {
 
	SLV_VELOCITY_NAUTICAL,                  ///< 305  PR#10594 Separation of land and nautical velocity (knots!)
 
	SLV_CONSISTENT_PARTIAL_Z,               ///< 306  PR#10570 Conversion from an inconsistent partial Z calculation for slopes, to one that is (more) consistent.
 
	SLV_MORE_CARGO_AGE,                     ///< 307  PR#10596 Track cargo age for a longer period.
 
	SLV_LINKGRAPH_SECONDS,                  ///< 308  PR#10610 Store linkgraph update intervals in seconds instead of days.
 
	SLV_AI_START_DATE,                      ///< 309  PR#10653 Removal of individual AI start dates and added a generic one.
 

	
 
	SLV_EXTEND_VEHICLE_RANDOM,              ///< 310  PR#10701 Extend vehicle random bits.
 
	SLV_EXTEND_ENTITY_MAPPING,              ///< 311  PR#10672 Extend entity mapping range.
 
	SLV_DISASTER_VEH_STATE,                 ///< 312  PR#10798 Explicit storage of disaster vehicle state.
 
	SLV_SAVEGAME_ID,                        ///< 313  PR#10719 Add an unique ID to every savegame (used to deduplicate surveys).
 
	SLV_STRING_GAMELOG,                     ///< 314  PR#10801 Use std::string in gamelog.
 

	
 
	SLV_INDUSTRY_CARGO_REORGANISE,          ///< 315  PR#10853 Industry accepts/produced data reorganised.
 

	
 
	SL_MAX_VERSION,                         ///< Highest possible saveload version
 
};
 

	
 
/** Save or load result codes. */
 
enum SaveOrLoadResult {
 
	SL_OK     = 0, ///< completed successfully
 
	SL_ERROR  = 1, ///< error that was caught before internal structures were modified
 
	SL_REINIT = 2, ///< error that was caught in the middle of updating game state, need to clear it. (can only happen during load)
 
};
 

	
 
/** Deals with the type of the savegame, independent of extension */
 
struct FileToSaveLoad {
src/script/api/script_cargolist.cpp
Show inline comments
 
@@ -20,41 +20,39 @@
 
ScriptCargoList::ScriptCargoList()
 
{
 
	for (const CargoSpec *cs : CargoSpec::Iterate()) {
 
		this->AddItem(cs->Index());
 
	}
 
}
 

	
 
ScriptCargoList_IndustryAccepting::ScriptCargoList_IndustryAccepting(IndustryID industry_id)
 
{
 
	if (!ScriptIndustry::IsValidIndustry(industry_id)) return;
 

	
 
	Industry *ind = ::Industry::Get(industry_id);
 
	for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) {
 
		CargoID cargo_id = ind->accepts_cargo[i];
 
		if (::IsValidCargoID(cargo_id)) {
 
			this->AddItem(cargo_id);
 
	for (const auto &a : ind->accepted) {
 
		if (::IsValidCargoID(a.cargo)) {
 
			this->AddItem(a.cargo);
 
		}
 
	}
 
}
 

	
 
ScriptCargoList_IndustryProducing::ScriptCargoList_IndustryProducing(IndustryID industry_id)
 
{
 
	if (!ScriptIndustry::IsValidIndustry(industry_id)) return;
 

	
 
	Industry *ind = ::Industry::Get(industry_id);
 
	for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
 
		CargoID cargo_id = ind->produced_cargo[i];
 
		if (::IsValidCargoID(cargo_id)) {
 
			this->AddItem(cargo_id);
 
	for (const auto &p : ind->produced) {
 
		if (::IsValidCargoID(p.cargo)) {
 
			this->AddItem(p.cargo);
 
		}
 
	}
 
}
 

	
 
ScriptCargoList_StationAccepting::ScriptCargoList_StationAccepting(StationID station_id)
 
{
 
	if (!ScriptStation::IsValidStation(station_id)) return;
 

	
 
	Station *st = ::Station::Get(station_id);
 
	for (CargoID i = 0; i < NUM_CARGO; i++) {
 
		if (HasBit(st->goods[i].status, GoodsEntry::GES_ACCEPTANCE)) this->AddItem(i);
 
	}
src/script/api/script_industry.cpp
Show inline comments
 
@@ -71,67 +71,67 @@
 
	if (IndustryTemporarilyRefusesCargo(i, cargo_id)) return CAS_TEMP_REFUSED;
 

	
 
	return CAS_ACCEPTED;
 
}
 

	
 
/* static */ SQInteger ScriptIndustry::GetStockpiledCargo(IndustryID industry_id, CargoID cargo_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return -1;
 
	if (!ScriptCargo::IsValidCargo(cargo_id)) return -1;
 

	
 
	Industry *i = ::Industry::Get(industry_id);
 

	
 
	int j = i->GetCargoAcceptedIndex(cargo_id);
 
	if (j < 0) return -1;
 
	auto it = i->GetCargoAccepted(cargo_id);
 
	if (it == std::end(i->accepted)) return -1;
 

	
 
	return i->incoming_cargo_waiting[j];
 
	return it->waiting;
 
}
 

	
 
/* static */ SQInteger ScriptIndustry::GetLastMonthProduction(IndustryID industry_id, CargoID cargo_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return -1;
 
	if (!ScriptCargo::IsValidCargo(cargo_id)) return -1;
 

	
 
	const Industry *i = ::Industry::Get(industry_id);
 
	Industry *i = ::Industry::Get(industry_id);
 

	
 
	int j = i->GetCargoProducedIndex(cargo_id);
 
	if (j < 0) return -1;
 
	auto it = i->GetCargoProduced(cargo_id);
 
	if (it == std::end(i->produced)) return -1;
 

	
 
	return i->last_month_production[j];
 
	return it->history[LAST_MONTH].production;
 
}
 

	
 
/* static */ SQInteger ScriptIndustry::GetLastMonthTransported(IndustryID industry_id, CargoID cargo_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return -1;
 
	if (!ScriptCargo::IsValidCargo(cargo_id)) return -1;
 

	
 
	const Industry *i = ::Industry::Get(industry_id);
 
	Industry *i = ::Industry::Get(industry_id);
 

	
 
	int j = i->GetCargoProducedIndex(cargo_id);
 
	if (j < 0) return -1;
 
	auto it = i->GetCargoProduced(cargo_id);
 
	if (it == std::end(i->produced)) return -1;
 

	
 
	return i->last_month_transported[j];
 
	return it->history[LAST_MONTH].transported;
 
}
 

	
 
/* static */ SQInteger ScriptIndustry::GetLastMonthTransportedPercentage(IndustryID industry_id, CargoID cargo_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return -1;
 
	if (!ScriptCargo::IsValidCargo(cargo_id)) return -1;
 

	
 
	const Industry *i = ::Industry::Get(industry_id);
 
	Industry *i = ::Industry::Get(industry_id);
 

	
 
	int j = i->GetCargoProducedIndex(cargo_id);
 
	if (j < 0) return -1;
 
	auto it = i->GetCargoProduced(cargo_id);
 
	if (it == std::end(i->produced)) return -1;
 

	
 
	return ::ToPercent8(i->last_month_pct_transported[j]);
 
	return ::ToPercent8(it->history[LAST_MONTH].PctTransported());
 
}
 

	
 
/* static */ TileIndex ScriptIndustry::GetLocation(IndustryID industry_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return INVALID_TILE;
 

	
 
	return ::Industry::Get(industry_id)->location.tile;
 
}
 

	
 
/* static */ SQInteger ScriptIndustry::GetAmountOfStationsAround(IndustryID industry_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return -1;
 
@@ -216,29 +216,30 @@
 
{
 
	Industry *i = Industry::GetIfValid(industry_id);
 
	if (i == nullptr) return 0;
 
	return i->last_prod_year;
 
}
 

	
 
/* static */ ScriptDate::Date ScriptIndustry::GetCargoLastAcceptedDate(IndustryID industry_id, CargoID cargo_type)
 
{
 
	Industry *i = Industry::GetIfValid(industry_id);
 
	if (i == nullptr) return ScriptDate::DATE_INVALID;
 

	
 
	if (!::IsValidCargoID(cargo_type)) {
 
		return (ScriptDate::Date)std::accumulate(std::begin(i->last_cargo_accepted_at), std::end(i->last_cargo_accepted_at), 0, [](TimerGameCalendar::Date a, TimerGameCalendar::Date b) { return std::max(a, b); });
 
		auto it = std::max_element(std::begin(i->accepted), std::end(i->accepted), [](const auto &a, const auto &b) { return a.last_accepted < b.last_accepted; });
 
		return (ScriptDate::Date)it->last_accepted;
 
	} else {
 
		int index = i->GetCargoAcceptedIndex(cargo_type);
 
		if (index < 0) return ScriptDate::DATE_INVALID;
 
		return (ScriptDate::Date)i->last_cargo_accepted_at[index];
 
		auto it = i->GetCargoAccepted(cargo_type);
 
		if (it == std::end(i->accepted)) return ScriptDate::DATE_INVALID;
 
		return (ScriptDate::Date)it->last_accepted;
 
	}
 
}
 

	
 
/* static */ SQInteger ScriptIndustry::GetControlFlags(IndustryID industry_id)
 
{
 
	Industry *i = Industry::GetIfValid(industry_id);
 
	if (i == nullptr) return 0;
 
	return i->ctlflags;
 
}
 

	
 
/* static */ bool ScriptIndustry::SetControlFlags(IndustryID industry_id, SQInteger control_flags)
 
{
src/script/api/script_tilelist.cpp
Show inline comments
 
@@ -86,31 +86,25 @@ ScriptTileList_IndustryAccepting::Script
 
	if (!i->IsCargoAccepted()) return;
 

	
 
	if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED;
 

	
 
	BitmapTileArea bta(TileArea(i->location).Expand(radius));
 
	FillIndustryCatchment(i, radius, bta);
 

	
 
	BitmapTileIterator it(bta);
 
	for (TileIndex cur_tile = it; cur_tile != INVALID_TILE; cur_tile = ++it) {
 
		/* Only add the tile if it accepts the cargo (sometimes just 1 tile of an
 
		 *  industry triggers the acceptance). */
 
		CargoArray acceptance = ::GetAcceptanceAroundTiles(cur_tile, 1, 1, radius);
 
		{
 
			bool cargo_accepts = false;
 
			for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
 
				if (::IsValidCargoID(i->accepts_cargo[j]) && acceptance[i->accepts_cargo[j]] != 0) cargo_accepts = true;
 
			}
 
			if (!cargo_accepts) continue;
 
		}
 
		if (std::none_of(std::begin(i->accepted), std::end(i->accepted), [&acceptance](const auto &a) { return ::IsValidCargoID(a.cargo) && acceptance[a.cargo] != 0; })) continue;
 

	
 
		this->AddTile(cur_tile);
 
	}
 
}
 

	
 
ScriptTileList_IndustryProducing::ScriptTileList_IndustryProducing(IndustryID industry_id, SQInteger radius)
 
{
 
	if (!ScriptIndustry::IsValidIndustry(industry_id) || radius <= 0) return;
 

	
 
	const Industry *i = ::Industry::Get(industry_id);
 

	
 
	/* Check if this industry is only served by its neutral station */
src/station_cmd.cpp
Show inline comments
 
@@ -161,29 +161,29 @@ static int CountMapSquareAround(TileInde
 
 * @return true if and only if the tile is a mine
 
 */
 
static bool CMSAMine(TileIndex tile)
 
{
 
	/* No industry */
 
	if (!IsTileType(tile, MP_INDUSTRY)) return false;
 

	
 
	const Industry *ind = Industry::GetByTile(tile);
 

	
 
	/* No extractive industry */
 
	if ((GetIndustrySpec(ind->type)->life_type & INDUSTRYLIFE_EXTRACTIVE) == 0) return false;
 

	
 
	for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
 
	for (const auto &p : ind->produced) {
 
		/* The industry extracts something non-liquid, i.e. no oil or plastic, so it is a mine.
 
		 * Also the production of passengers and mail is ignored. */
 
		if (IsValidCargoID(ind->produced_cargo[i]) &&
 
				(CargoSpec::Get(ind->produced_cargo[i])->classes & (CC_LIQUID | CC_PASSENGERS | CC_MAIL)) == 0) {
 
		if (IsValidCargoID(p.cargo) &&
 
				(CargoSpec::Get(p.cargo)->classes & (CC_LIQUID | CC_PASSENGERS | CC_MAIL)) == 0) {
 
			return true;
 
		}
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Check whether the tile is water.
 
 * @param tile the tile to investigate.
 
 * @return true if and only if the tile is a water tile
 
 */
 
@@ -523,27 +523,26 @@ CargoArray GetProductionAroundTiles(Tile
 
		if (IsTileType(tile, MP_INDUSTRY)) industries.insert(GetIndustryIndex(tile));
 
		AddProducedCargo(tile, produced);
 
	}
 

	
 
	/* Loop over the seen industries. They produce cargo for
 
	 * anything that is within 'rad' of any one of their tiles.
 
	 */
 
	for (IndustryID industry : industries) {
 
		const Industry *i = Industry::Get(industry);
 
		/* Skip industry with neutral station */
 
		if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) continue;
 

	
 
		for (uint j = 0; j < lengthof(i->produced_cargo); j++) {
 
			CargoID cargo = i->produced_cargo[j];
 
			if (IsValidCargoID(cargo)) produced[cargo]++;
 
		for (const auto &p : i->produced) {
 
			if (IsValidCargoID(p.cargo)) produced[p.cargo]++;
 
		}
 
	}
 

	
 
	return produced;
 
}
 

	
 
/**
 
 * Get the acceptance of cargoes around the tile in 1/8.
 
 * @param center_tile Center of the search area
 
 * @param w X extent of area
 
 * @param h Y extent of area
 
 * @param rad Search radius in addition to given area
src/subsidy.cpp
Show inline comments
 
@@ -373,39 +373,38 @@ bool FindSubsidyIndustryCargoRoute()
 

	
 
	SourceType src_type = SourceType::Industry;
 

	
 
	/* Select a random industry. */
 
	const Industry *src_ind = Industry::GetRandom();
 
	if (src_ind == nullptr) return false;
 

	
 
	uint trans, total;
 

	
 
	CargoID cid;
 

	
 
	/* Randomize cargo type */
 
	int num_cargos = 0;
 
	uint cargo_index;
 
	for (cargo_index = 0; cargo_index < lengthof(src_ind->produced_cargo); cargo_index++) {
 
		if (IsValidCargoID(src_ind->produced_cargo[cargo_index])) num_cargos++;
 
	}
 
	int num_cargos = std::count_if(std::begin(src_ind->produced), std::end(src_ind->produced), [](const auto &p) { return IsValidCargoID(p.cargo); });
 
	if (num_cargos == 0) return false; // industry produces nothing
 
	int cargo_num = RandomRange(num_cargos) + 1;
 
	for (cargo_index = 0; cargo_index < lengthof(src_ind->produced_cargo); cargo_index++) {
 
		if (IsValidCargoID(src_ind->produced_cargo[cargo_index])) cargo_num--;
 

	
 
	auto it = std::begin(src_ind->produced);
 
	for (/* nothing */; it != std::end(src_ind->produced); ++it) {
 
		if (IsValidCargoID(it->cargo)) cargo_num--;
 
		if (cargo_num == 0) break;
 
	}
 
	assert(cargo_num == 0); // indicates loop didn't break as intended
 
	cid = src_ind->produced_cargo[cargo_index];
 
	trans = src_ind->last_month_pct_transported[cargo_index];
 
	total = src_ind->last_month_production[cargo_index];
 
	assert(it != std::end(src_ind->produced)); // indicates loop didn't end as intended
 

	
 
	cid = it->cargo;
 
	trans = it->history[LAST_MONTH].PctTransported();
 
	total = it->history[LAST_MONTH].production;
 

	
 
	/* Quit if no production in this industry
 
	 * or if the pct transported is already large enough
 
	 * or if the cargo is automatically distributed */
 
	if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED ||
 
			!IsValidCargoID(cid) ||
 
			_settings_game.linkgraph.GetDistributionType(cid) != DT_MANUAL) {
 
		return false;
 
	}
 

	
 
	SourceID src = src_ind->index;
 

	
src/table/newgrf_debug_data.h
Show inline comments
 
@@ -266,56 +266,56 @@ class NIHIndustryTile : public NIHelper 
 

	
 
static const NIFeature _nif_industrytile = {
 
	nullptr,
 
	_nic_industrytiles,
 
	_niv_industrytiles,
 
	new NIHIndustryTile(),
 
};
 

	
 

	
 
/*** NewGRF industries ***/
 

	
 
static const NIProperty _nip_industries[] = {
 
	NIP(0x25, Industry, produced_cargo[ 0], NIT_CARGO, "produced cargo 0"),
 
	NIP(0x25, Industry, produced_cargo[ 1], NIT_CARGO, "produced cargo 1"),
 
	NIP(0x25, Industry, produced_cargo[ 2], NIT_CARGO, "produced cargo 2"),
 
	NIP(0x25, Industry, produced_cargo[ 3], NIT_CARGO, "produced cargo 3"),
 
	NIP(0x25, Industry, produced_cargo[ 4], NIT_CARGO, "produced cargo 4"),
 
	NIP(0x25, Industry, produced_cargo[ 5], NIT_CARGO, "produced cargo 5"),
 
	NIP(0x25, Industry, produced_cargo[ 6], NIT_CARGO, "produced cargo 6"),
 
	NIP(0x25, Industry, produced_cargo[ 7], NIT_CARGO, "produced cargo 7"),
 
	NIP(0x25, Industry, produced_cargo[ 8], NIT_CARGO, "produced cargo 8"),
 
	NIP(0x25, Industry, produced_cargo[ 9], NIT_CARGO, "produced cargo 9"),
 
	NIP(0x25, Industry, produced_cargo[10], NIT_CARGO, "produced cargo 10"),
 
	NIP(0x25, Industry, produced_cargo[11], NIT_CARGO, "produced cargo 11"),
 
	NIP(0x25, Industry, produced_cargo[12], NIT_CARGO, "produced cargo 12"),
 
	NIP(0x25, Industry, produced_cargo[13], NIT_CARGO, "produced cargo 13"),
 
	NIP(0x25, Industry, produced_cargo[14], NIT_CARGO, "produced cargo 14"),
 
	NIP(0x25, Industry, produced_cargo[15], NIT_CARGO, "produced cargo 15"),
 
	NIP(0x26, Industry, accepts_cargo[ 0],  NIT_CARGO, "accepted cargo 0"),
 
	NIP(0x26, Industry, accepts_cargo[ 1],  NIT_CARGO, "accepted cargo 1"),
 
	NIP(0x26, Industry, accepts_cargo[ 2],  NIT_CARGO, "accepted cargo 2"),
 
	NIP(0x26, Industry, accepts_cargo[ 3],  NIT_CARGO, "accepted cargo 3"),
 
	NIP(0x26, Industry, accepts_cargo[ 4],  NIT_CARGO, "accepted cargo 4"),
 
	NIP(0x26, Industry, accepts_cargo[ 5],  NIT_CARGO, "accepted cargo 5"),
 
	NIP(0x26, Industry, accepts_cargo[ 6],  NIT_CARGO, "accepted cargo 6"),
 
	NIP(0x26, Industry, accepts_cargo[ 7],  NIT_CARGO, "accepted cargo 7"),
 
	NIP(0x26, Industry, accepts_cargo[ 8],  NIT_CARGO, "accepted cargo 8"),
 
	NIP(0x26, Industry, accepts_cargo[ 9],  NIT_CARGO, "accepted cargo 9"),
 
	NIP(0x26, Industry, accepts_cargo[10],  NIT_CARGO, "accepted cargo 10"),
 
	NIP(0x26, Industry, accepts_cargo[11],  NIT_CARGO, "accepted cargo 11"),
 
	NIP(0x26, Industry, accepts_cargo[12],  NIT_CARGO, "accepted cargo 12"),
 
	NIP(0x26, Industry, accepts_cargo[13],  NIT_CARGO, "accepted cargo 13"),
 
	NIP(0x26, Industry, accepts_cargo[14],  NIT_CARGO, "accepted cargo 14"),
 
	NIP(0x26, Industry, accepts_cargo[15],  NIT_CARGO, "accepted cargo 15"),
 
	NIP(0x25, Industry, produced[ 0].cargo, NIT_CARGO, "produced cargo 0"),
 
	NIP(0x25, Industry, produced[ 1].cargo, NIT_CARGO, "produced cargo 1"),
 
	NIP(0x25, Industry, produced[ 2].cargo, NIT_CARGO, "produced cargo 2"),
 
	NIP(0x25, Industry, produced[ 3].cargo, NIT_CARGO, "produced cargo 3"),
 
	NIP(0x25, Industry, produced[ 4].cargo, NIT_CARGO, "produced cargo 4"),
 
	NIP(0x25, Industry, produced[ 5].cargo, NIT_CARGO, "produced cargo 5"),
 
	NIP(0x25, Industry, produced[ 6].cargo, NIT_CARGO, "produced cargo 6"),
 
	NIP(0x25, Industry, produced[ 7].cargo, NIT_CARGO, "produced cargo 7"),
 
	NIP(0x25, Industry, produced[ 8].cargo, NIT_CARGO, "produced cargo 8"),
 
	NIP(0x25, Industry, produced[ 9].cargo, NIT_CARGO, "produced cargo 9"),
 
	NIP(0x25, Industry, produced[10].cargo, NIT_CARGO, "produced cargo 10"),
 
	NIP(0x25, Industry, produced[11].cargo, NIT_CARGO, "produced cargo 11"),
 
	NIP(0x25, Industry, produced[12].cargo, NIT_CARGO, "produced cargo 12"),
 
	NIP(0x25, Industry, produced[13].cargo, NIT_CARGO, "produced cargo 13"),
 
	NIP(0x25, Industry, produced[14].cargo, NIT_CARGO, "produced cargo 14"),
 
	NIP(0x25, Industry, produced[15].cargo, NIT_CARGO, "produced cargo 15"),
 
	NIP(0x26, Industry, accepted[ 0].cargo, NIT_CARGO, "accepted cargo 0"),
 
	NIP(0x26, Industry, accepted[ 1].cargo, NIT_CARGO, "accepted cargo 1"),
 
	NIP(0x26, Industry, accepted[ 2].cargo, NIT_CARGO, "accepted cargo 2"),
 
	NIP(0x26, Industry, accepted[ 3].cargo, NIT_CARGO, "accepted cargo 3"),
 
	NIP(0x26, Industry, accepted[ 4].cargo, NIT_CARGO, "accepted cargo 4"),
 
	NIP(0x26, Industry, accepted[ 5].cargo, NIT_CARGO, "accepted cargo 5"),
 
	NIP(0x26, Industry, accepted[ 6].cargo, NIT_CARGO, "accepted cargo 6"),
 
	NIP(0x26, Industry, accepted[ 7].cargo, NIT_CARGO, "accepted cargo 7"),
 
	NIP(0x26, Industry, accepted[ 8].cargo, NIT_CARGO, "accepted cargo 8"),
 
	NIP(0x26, Industry, accepted[ 9].cargo, NIT_CARGO, "accepted cargo 9"),
 
	NIP(0x26, Industry, accepted[10].cargo, NIT_CARGO, "accepted cargo 10"),
 
	NIP(0x26, Industry, accepted[11].cargo, NIT_CARGO, "accepted cargo 11"),
 
	NIP(0x26, Industry, accepted[12].cargo, NIT_CARGO, "accepted cargo 12"),
 
	NIP(0x26, Industry, accepted[13].cargo, NIT_CARGO, "accepted cargo 13"),
 
	NIP(0x26, Industry, accepted[14].cargo, NIT_CARGO, "accepted cargo 14"),
 
	NIP(0x26, Industry, accepted[15].cargo, NIT_CARGO, "accepted cargo 15"),
 
	NIP_END()
 
};
 

	
 
#define NICI(cb_id, bit) NIC(cb_id, IndustrySpec, callback_mask, bit)
 
static const NICallback _nic_industries[] = {
 
	NICI(CBID_INDUSTRY_PROBABILITY,          CBM_IND_PROBABILITY),
 
	NICI(CBID_INDUSTRY_LOCATION,             CBM_IND_LOCATION),
 
	NICI(CBID_INDUSTRY_PRODUCTION_CHANGE,    CBM_IND_PRODUCTION_CHANGE),
 
	NICI(CBID_INDUSTRY_MONTHLYPROD_CHANGE,   CBM_IND_MONTHLYPROD_CHANGE),
 
	NICI(CBID_INDUSTRY_CARGO_SUFFIX,         CBM_IND_CARGO_SUFFIX),
 
	NICI(CBID_INDUSTRY_FUND_MORE_TEXT,       CBM_IND_FUND_MORE_TEXT),
 
	NICI(CBID_INDUSTRY_WINDOW_MORE_TEXT,     CBM_IND_WINDOW_MORE_TEXT),
0 comments (0 inline, 0 general)