Changeset - r27421:e8c2cdc1e8e6
[Not reviewed]
src/articulated_vehicles.cpp
Show inline comments
 
@@ -99,25 +99,25 @@ uint CountArticulatedParts(EngineID engi
 

	
 
/**
 
 * Returns the default (non-refitted) capacity of a specific EngineID.
 
 * @param engine the EngineID of interest
 
 * @param cargo_type returns the default cargo type, if needed
 
 * @return capacity
 
 */
 
static inline uint16 GetVehicleDefaultCapacity(EngineID engine, CargoID *cargo_type)
 
{
 
	const Engine *e = Engine::Get(engine);
 
	CargoID cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
 
	if (cargo_type != nullptr) *cargo_type = cargo;
 
	if (cargo == CT_INVALID) return 0;
 
	if (!IsValidCargoID(cargo)) return 0;
 
	return e->GetDisplayDefaultCapacity();
 
}
 

	
 
/**
 
 * Returns all cargoes a vehicle can carry.
 
 * @param engine the EngineID of interest
 
 * @param include_initial_cargo_type if true the default cargo type of the vehicle is included; if false only the refit_mask
 
 * @return bit set of CargoIDs
 
 */
 
static inline CargoTypes GetAvailableVehicleCargoTypes(EngineID engine, bool include_initial_cargo_type)
 
{
 
	const Engine *e = Engine::Get(engine);
 
@@ -242,26 +242,26 @@ CargoTypes GetIntersectionOfArticulatedR
 
/**
 
 * Tests if all parts of an articulated vehicle are refitted to the same cargo.
 
 * Note: Vehicles not carrying anything are ignored
 
 * @param v the first vehicle in the chain
 
 * @param cargo_type returns the common CargoID if needed. (CT_INVALID if no part is carrying something or they are carrying different things)
 
 * @return true if some parts are carrying different cargoes, false if all parts are carrying the same (nothing is also the same)
 
 */
 
bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *cargo_type)
 
{
 
	CargoID first_cargo = CT_INVALID;
 

	
 
	do {
 
		if (v->cargo_type != CT_INVALID && v->GetEngine()->CanCarryCargo()) {
 
			if (first_cargo == CT_INVALID) first_cargo = v->cargo_type;
 
		if (IsValidCargoID(v->cargo_type) && v->GetEngine()->CanCarryCargo()) {
 
			if (!IsValidCargoID(first_cargo)) first_cargo = v->cargo_type;
 
			if (first_cargo != v->cargo_type) {
 
				if (cargo_type != nullptr) *cargo_type = CT_INVALID;
 
				return true;
 
			}
 
		}
 

	
 
		v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr;
 
	} while (v != nullptr);
 

	
 
	if (cargo_type != nullptr) *cargo_type = first_cargo;
 
	return false;
 
}
src/autoreplace_cmd.cpp
Show inline comments
 
@@ -229,25 +229,25 @@ static int GetIncompatibleRefitOrderIdFo
 
 *    CT_INVALID is returned when both old and new vehicle got cargo capacity and refitting the new one to the old one's cargo type isn't possible
 
 */
 
static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool part_of_chain)
 
{
 
	CargoTypes available_cargo_types, union_mask;
 
	GetArticulatedRefitMasks(engine_type, true, &union_mask, &available_cargo_types);
 

	
 
	if (union_mask == 0) return CT_NO_REFIT; // Don't try to refit an engine with no cargo capacity
 

	
 
	CargoID cargo_type;
 
	if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) return CT_INVALID; // We cannot refit to mixed cargoes in an automated way
 

	
 
	if (cargo_type == CT_INVALID) {
 
	if (!IsValidCargoID(cargo_type)) {
 
		if (v->type != VEH_TRAIN) return CT_NO_REFIT; // If the vehicle does not carry anything at all, every replacement is fine.
 

	
 
		if (!part_of_chain) return CT_NO_REFIT;
 

	
 
		/* the old engine didn't have cargo capacity, but the new one does
 
		 * now we will figure out what cargo the train is carrying and refit to fit this */
 

	
 
		for (v = v->First(); v != nullptr; v = v->Next()) {
 
			if (!v->GetEngine()->CanCarryCargo()) continue;
 
			/* Now we found a cargo type being carried on the train and we will see if it is possible to carry to this one */
 
			if (HasBit(available_cargo_types, v->cargo_type)) return v->cargo_type;
 
		}
 
@@ -312,25 +312,25 @@ static CommandCost BuildReplacementVehic
 
{
 
	*new_vehicle = nullptr;
 

	
 
	/* Shall the vehicle be replaced? */
 
	const Company *c = Company::Get(_current_company);
 
	EngineID e;
 
	CommandCost cost = GetNewEngineType(old_veh, c, true, e);
 
	if (cost.Failed()) return cost;
 
	if (e == INVALID_ENGINE) return CommandCost(); // neither autoreplace is set, nor autorenew is triggered
 

	
 
	/* Does it need to be refitted */
 
	CargoID refit_cargo = GetNewCargoTypeForReplace(old_veh, e, part_of_chain);
 
	if (refit_cargo == CT_INVALID) {
 
	if (!IsValidCargoID(refit_cargo)) {
 
		if (!IsLocalCompany()) return CommandCost();
 

	
 
		SetDParam(0, old_veh->index);
 

	
 
		int order_id = GetIncompatibleRefitOrderIdForAutoreplace(old_veh, e);
 
		if (order_id != -1) {
 
			/* Orders contained a refit order that is incompatible with the new vehicle. */
 
			SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT);
 
			SetDParam(2, order_id + 1); // 1-based indexing for display
 
		} else {
 
			/* Current cargo is incompatible with the new vehicle. */
 
			SetDParam(1, STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO);
src/build_vehicle_gui.cpp
Show inline comments
 
@@ -1319,25 +1319,25 @@ struct BuildVehicleWindow : Window {
 

	
 
		if (this->sel_engine == INVALID_ENGINE) return;
 

	
 
		const Engine *e = Engine::Get(this->sel_engine);
 

	
 
		if (!this->listview_mode) {
 
			/* Query for cost and refitted capacity */
 
			auto [ret, veh_id, refit_capacity, refit_mail, cargo_capacities] = Command<CMD_BUILD_VEHICLE>::Do(DC_QUERY_COST, this->window_number, this->sel_engine, true, cargo, INVALID_CLIENT_ID);
 
			if (ret.Succeeded()) {
 
				this->te.cost          = ret.GetCost() - e->GetCost();
 
				this->te.capacity      = refit_capacity;
 
				this->te.mail_capacity = refit_mail;
 
				this->te.cargo         = (cargo == CT_INVALID) ? e->GetDefaultCargoType() : cargo;
 
				this->te.cargo         = !IsValidCargoID(cargo) ? e->GetDefaultCargoType() : cargo;
 
				this->te.all_capacities = cargo_capacities;
 
				return;
 
			}
 
		}
 

	
 
		/* Purchase test was not possible or failed, fill in the defaults instead. */
 
		this->te.cost     = 0;
 
		this->te.FillDefaultCapacities(e);
 
	}
 

	
 
	void OnInit() override
 
	{
src/cargotype.cpp
Show inline comments
 
@@ -79,25 +79,25 @@ void SetupCargoForClimate(LandscapeID l)
 
}
 

	
 
/**
 
 * Get the cargo ID of a default cargo, if present.
 
 * @param l Landscape
 
 * @param ct Default cargo type.
 
 * @return ID number if the cargo exists, else #CT_INVALID
 
 */
 
CargoID GetDefaultCargoID(LandscapeID l, CargoType ct)
 
{
 
	assert(l < lengthof(_default_climate_cargo));
 

	
 
	if (ct == CT_INVALID) return CT_INVALID;
 
	if (!IsValidCargoType(ct)) return CT_INVALID;
 

	
 
	assert(ct < lengthof(_default_climate_cargo[0]));
 
	CargoLabel cl = _default_climate_cargo[l][ct];
 
	/* Bzzt: check if cl is just an index into the cargo table */
 
	if (cl < lengthof(_default_cargo)) {
 
		cl = _default_cargo[cl].label;
 
	}
 

	
 
	return GetCargoIDByLabel(cl);
 
}
 

	
 
/**
src/engine.cpp
Show inline comments
 
@@ -175,25 +175,25 @@ bool Engine::CanCarryCargo() const
 
			break;
 

	
 
		case VEH_ROAD:
 
			if (this->u.road.capacity == 0) return false;
 
			break;
 

	
 
		case VEH_SHIP:
 
		case VEH_AIRCRAFT:
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
	return this->GetDefaultCargoType() != CT_INVALID;
 
	return IsValidCargoID(this->GetDefaultCargoType());
 
}
 

	
 

	
 
/**
 
 * Determines capacity of a given vehicle from scratch.
 
 * For aircraft the main capacity is determined. Mail might be present as well.
 
 * @param v Vehicle of interest; nullptr in purchase list
 
 * @param mail_capacity returns secondary cargo (mail) capacity of aircraft
 
 * @return Capacity
 
 */
 
uint Engine::DetermineCapacity(const Vehicle *v, uint16 *mail_capacity) const
 
{
 
@@ -1247,25 +1247,25 @@ bool IsEngineRefittable(EngineID engine)
 

	
 
	const EngineInfo *ei = &e->info;
 
	if (ei->refit_mask == 0) return false;
 

	
 
	/* Are there suffixes?
 
	 * Note: This does not mean the suffixes are actually available for every consist at any time. */
 
	if (HasBit(ei->callback_mask, CBM_VEHICLE_CARGO_SUFFIX)) return true;
 

	
 
	/* Is there any cargo except the default cargo? */
 
	CargoID default_cargo = e->GetDefaultCargoType();
 
	CargoTypes default_cargo_mask = 0;
 
	SetBit(default_cargo_mask, default_cargo);
 
	return default_cargo != CT_INVALID && ei->refit_mask != default_cargo_mask;
 
	return IsValidCargoID(default_cargo) && ei->refit_mask != default_cargo_mask;
 
}
 

	
 
/**
 
 * Check for engines that have an appropriate availability.
 
 */
 
void CheckEngines()
 
{
 
	TimerGameCalendar::Date min_date = INT32_MAX;
 

	
 
	for (const Engine *e : Engine::Iterate()) {
 
		if (!e->IsEnabled()) continue;
 

	
src/industry.h
Show inline comments
 
@@ -108,33 +108,33 @@ struct Industry : IndustryPool::PoolItem
 
	/**
 
	 * 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
 
	{
 
		if (cargo == CT_INVALID) return -1;
 
		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;
 
	}
 

	
 
	inline int GetCargoAcceptedIndex(CargoID cargo) const
 
	{
 
		if (cargo == CT_INVALID) return -1;
 
		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;
 
	}
 

	
 
	/**
 
	 * 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)
src/industry_cmd.cpp
Show inline comments
 
@@ -443,25 +443,25 @@ static void AddAcceptedCargo_Industry(Ti
 

	
 
	if (HasBit(itspec->callback_mask, CBM_INDT_CARGO_ACCEPTANCE)) {
 
		/* Try callback for acceptance list, if success override all existing acceptance */
 
		uint16 res = GetIndustryTileCallback(CBID_INDTILE_CARGO_ACCEPTANCE, 0, 0, gfx, Industry::GetByTile(tile), tile);
 
		if (res != CALLBACK_FAILED) {
 
			MemSetT(cargo_acceptance, 0, lengthof(cargo_acceptance));
 
			for (uint i = 0; i < 3; i++) cargo_acceptance[i] = GB(res, i * 4, 4);
 
		}
 
	}
 

	
 
	for (byte i = 0; i < lengthof(itspec->accepts_cargo); i++) {
 
		CargoID a = accepts_cargo[i];
 
		if (a == CT_INVALID || cargo_acceptance[i] <= 0) continue; // work only with valid cargoes
 
		if (!IsValidCargoID(a) || cargo_acceptance[i] <= 0) continue; // work only with valid cargoes
 

	
 
		/* Add accepted cargo */
 
		acceptance[a] += cargo_acceptance[i];
 

	
 
		/* Maybe set 'always accepted' bit (if it's not set already) */
 
		if (HasBit(*always_accepted, a)) continue;
 

	
 
		bool accepts = false;
 
		for (uint cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
 
			/* Test whether the industry itself accepts the cargo type */
 
			if (ind->accepts_cargo[cargo_index] == a) {
 
				accepts = true;
 
@@ -525,25 +525,25 @@ 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 && i->produced_cargo[j] != CT_INVALID) {
 
		if (cw > indspec->minimal_cargo && IsValidCargoID(i->produced_cargo[j])) {
 
			i->produced_cargo_waiting[j] -= 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;
 

	
 
			moved_cargo |= (am != 0);
 
		}
 
@@ -982,25 +982,25 @@ 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 (ind->produced_cargo[i] != CT_INVALID && CargoSpec::Get(ind->produced_cargo[i])->label == 'WOOD') return true;
 
		if (IsValidCargoID(ind->produced_cargo[i]) && CargoSpec::Get(ind->produced_cargo[i])->label == 'WOOD') return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
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
 
@@ -1858,25 +1858,25 @@ static void DoCreateNewIndustry(Industry
 
		uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->accepts_cargo) : 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 (cargo == CT_INVALID && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue;
 
			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) {
 
				/* Duplicate cargo */
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_INPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			i->accepts_cargo[j] = cargo;
 
@@ -1888,25 +1888,25 @@ static void DoCreateNewIndustry(Industry
 
		for (uint j = 0; j < lengthof(i->produced_cargo); j++) i->produced_cargo[j] = CT_INVALID;
 
		/* Query actual types */
 
		uint maxcargoes = (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) ? lengthof(i->produced_cargo) : 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 (cargo == CT_INVALID && !(indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED)) continue;
 
			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) {
 
				/* Duplicate cargo */
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_OUTPUT_CARGO_TYPES, res);
 
				break;
 
			}
 
			i->produced_cargo[j] = cargo;
 
@@ -2411,25 +2411,25 @@ void GenerateIndustries()
 
		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 (i->produced_cargo[j] != CT_INVALID) {
 
		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;
 
@@ -2613,25 +2613,25 @@ static bool CheckIndustryCloseDownProtec
 

	
 
/**
 
 * Can given cargo type be accepted or produced by the industry?
 
 * @param cargo: Cargo type
 
 * @param ind: Industry
 
 * @param *c_accepts: Pointer to boolean for acceptance of cargo
 
 * @param *c_produces: Pointer to boolean for production of cargo
 
 * @return: \c *c_accepts is set when industry accepts the cargo type,
 
 *          \c *c_produces is set when the industry produces the cargo type
 
 */
 
static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accepts, bool *c_produces)
 
{
 
	if (cargo == CT_INVALID) return;
 
	if (!IsValidCargoID(cargo)) return;
 

	
 
	/* Check for acceptance of cargo */
 
	for (byte j = 0; j < lengthof(ind->accepts_cargo); j++) {
 
		if (cargo == ind->accepts_cargo[j] && !IndustryTemporarilyRefusesCargo(ind, cargo)) {
 
			*c_accepts = true;
 
			break;
 
		}
 
	}
 

	
 
	/* Check for produced cargo */
 
	for (byte j = 0; j < lengthof(ind->produced_cargo); j++) {
 
		if (cargo == ind->produced_cargo[j]) {
 
@@ -2791,25 +2791,25 @@ static void ChangeIndustryProduction(Ind
 
		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)) {
 
					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 (i->produced_cargo[j] == CT_INVALID) continue;
 
				if (!IsValidCargoID(i->produced_cargo[j])) 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];
 

	
 
				/* 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.
src/industry_gui.cpp
Show inline comments
 
@@ -149,48 +149,48 @@ enum CargoSuffixInOut {
 
 * @param indspec the industry spec
 
 * @param cargoes array with cargotypes. for CT_INVALID no suffix will be determined
 
 * @param suffixes is filled with the suffixes
 
 */
 
template <typename TC, typename TS>
 
static inline void GetAllCargoSuffixes(CargoSuffixInOut use_input, CargoSuffixType cst, const Industry *ind, IndustryType ind_type, const IndustrySpec *indspec, const TC &cargoes, TS &suffixes)
 
{
 
	static_assert(lengthof(cargoes) <= lengthof(suffixes));
 

	
 
	if (indspec->behaviour & INDUSTRYBEH_CARGOTYPES_UNLIMITED) {
 
		/* Reworked behaviour with new many-in-many-out scheme */
 
		for (uint j = 0; j < lengthof(suffixes); j++) {
 
			if (cargoes[j] != CT_INVALID) {
 
			if (IsValidCargoID(cargoes[j])) {
 
				byte local_id = indspec->grf_prop.grffile->cargo_map[cargoes[j]]; // should we check the value for valid?
 
				uint cargotype = local_id << 16 | use_input;
 
				GetCargoSuffix(cargotype, cst, ind, ind_type, indspec, suffixes[j]);
 
			} else {
 
				suffixes[j].text[0] = '\0';
 
				suffixes[j].display = CSD_CARGO;
 
			}
 
		}
 
	} else {
 
		/* Compatible behaviour with old 3-in-2-out scheme */
 
		for (uint j = 0; j < lengthof(suffixes); j++) {
 
			suffixes[j].text[0] = '\0';
 
			suffixes[j].display = CSD_CARGO;
 
		}
 
		switch (use_input) {
 
			case CARGOSUFFIX_OUT:
 
				if (cargoes[0] != CT_INVALID) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (cargoes[1] != CT_INVALID) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
 
				if (IsValidCargoID(cargoes[0])) GetCargoSuffix(3, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (IsValidCargoID(cargoes[1])) GetCargoSuffix(4, cst, ind, ind_type, indspec, suffixes[1]);
 
				break;
 
			case CARGOSUFFIX_IN:
 
				if (cargoes[0] != CT_INVALID) GetCargoSuffix(0, cst, ind, ind_type, indspec, suffixes[0]);
 
				if (cargoes[1] != CT_INVALID) GetCargoSuffix(1, cst, ind, ind_type, indspec, suffixes[1]);
 
				if (cargoes[2] != CT_INVALID) GetCargoSuffix(2, cst, ind, ind_type, indspec, suffixes[2]);
 
				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();
 
		}
 
	}
 
}
 

	
 
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)
 
{
 
@@ -337,25 +337,25 @@ class BuildIndustryWindow : public Windo
 
	 * @param cargo_suffix Array of suffixes to attach to each cargo
 
	 * @param cargolistlen Length of arrays
 
	 * @param prefixstr    String to use for the first item
 
	 * @return A formatted raw string
 
	 */
 
	std::string MakeCargoListString(const CargoID *cargolist, const CargoSuffix *cargo_suffix, int cargolistlen, StringID prefixstr) const
 
	{
 
		std::string cargostring;
 
		int numcargo = 0;
 
		int firstcargo = -1;
 

	
 
		for (int j = 0; j < cargolistlen; j++) {
 
			if (cargolist[j] == CT_INVALID) continue;
 
			if (!IsValidCargoID(cargolist[j])) continue;
 
			numcargo++;
 
			if (firstcargo < 0) {
 
				firstcargo = j;
 
				continue;
 
			}
 
			SetDParam(0, CargoSpec::Get(cargolist[j])->name);
 
			SetDParamStr(1, cargo_suffix[j].text);
 
			cargostring += GetString(STR_INDUSTRY_VIEW_CARGO_LIST_EXTENSION);
 
		}
 

	
 
		if (numcargo > 0) {
 
			SetDParam(0, CargoSpec::Get(cargolist[firstcargo])->name);
 
@@ -836,25 +836,25 @@ public:
 
		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 (i->accepts_cargo[j] == CT_INVALID) continue;
 
			if (!IsValidCargoID(i->accepts_cargo[j])) 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]);
 
			SetDParamStr(3, "");
 
			StringID str = STR_NULL;
 
			switch (cargo_suffix[j].display) {
 
@@ -876,25 +876,25 @@ public:
 
					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 (i->produced_cargo[j] == CT_INVALID) continue;
 
			if (!IsValidCargoID(i->produced_cargo[j])) 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]));
 
@@ -972,25 +972,25 @@ public:
 

	
 
				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 (i->produced_cargo[j] == CT_INVALID) continue;
 
								if (!IsValidCargoID(i->produced_cargo[j])) continue;
 
								row--;
 
								if (row < 0) {
 
									line = (InfoLine)(IL_RATE1 + j);
 
									break;
 
								}
 
							}
 
						}
 
						break;
 
				}
 
				if (line == IL_NONE) return;
 

	
 
				bool rtl = _current_text_dir == TD_RTL;
 
@@ -1131,25 +1131,25 @@ 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 (i->produced_cargo[j] != CT_INVALID) {
 
		if (IsValidCargoID(i->produced_cargo[j])) {
 
			i->last_month_production[j] = 8 * i->production_rate[j];
 
		}
 
	}
 
}
 

	
 
/** 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),
 
@@ -1235,44 +1235,44 @@ static bool CDECL CargoFilter(const Indu
 
	auto accepted_cargo = cargoes.first;
 
	auto produced_cargo = cargoes.second;
 

	
 
	bool accepted_cargo_matches;
 

	
 
	switch (accepted_cargo) {
 
		case CF_ANY:
 
			accepted_cargo_matches = true;
 
			break;
 

	
 
		case CF_NONE:
 
			accepted_cargo_matches = std::all_of(std::begin((*industry)->accepts_cargo), std::end((*industry)->accepts_cargo), [](CargoID cargo) {
 
				return cargo == CT_INVALID;
 
				return !IsValidCargoID(cargo);
 
			});
 
			break;
 

	
 
		default:
 
			const auto &ac = (*industry)->accepts_cargo;
 
			accepted_cargo_matches = std::find(std::begin(ac), std::end(ac), accepted_cargo) != std::end(ac);
 
			break;
 
	}
 

	
 
	bool produced_cargo_matches;
 

	
 
	switch (produced_cargo) {
 
		case CF_ANY:
 
			produced_cargo_matches = true;
 
			break;
 

	
 
		case CF_NONE:
 
			produced_cargo_matches = std::all_of(std::begin((*industry)->produced_cargo), std::end((*industry)->produced_cargo), [](CargoID cargo) {
 
				return cargo == CT_INVALID;
 
				return !IsValidCargoID(cargo);
 
			});
 
			break;
 

	
 
		default:
 
			const auto &pc = (*industry)->produced_cargo;
 
			produced_cargo_matches = std::find(std::begin(pc), std::end(pc), produced_cargo) != std::end(pc);
 
			break;
 
	}
 

	
 
	return accepted_cargo_matches && produced_cargo_matches;
 
}
 

	
 
@@ -1416,25 +1416,25 @@ protected:
 

	
 
	/**
 
	 * 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)
 
	{
 
		assert(id < lengthof(i->produced_cargo));
 

	
 
		if (i->produced_cargo[id] == CT_INVALID) return -1;
 
		if (!IsValidCargoID(i->produced_cargo[id])) return -1;
 
		return ToPercent8(i->last_month_pct_transported[id]);
 
	}
 

	
 
	/**
 
	 * 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)
 
	{
 
@@ -1480,26 +1480,26 @@ protected:
 
		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++) {
 
			if (filter == CF_ANY) {
 
				if (a->produced_cargo[i] != CT_INVALID) prod_a += a->last_month_production[i];
 
				if (b->produced_cargo[i] != CT_INVALID) prod_b += b->last_month_production[i];
 
				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];
 
			} 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];
 
			}
 
		}
 
		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)
 
@@ -1525,25 +1525,25 @@ protected:
 
		GetAllCargoSuffixes(CARGOSUFFIX_OUT, CST_DIR, i, i->type, indsp, i->produced_cargo, cargo_suffix);
 

	
 
		/* 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 (i->produced_cargo[j] == CT_INVALID) continue;
 
			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]) });
 
		}
 

	
 
		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;
 
				});
 
@@ -1960,77 +1960,77 @@ struct CargoesField {
 
		MemSetT(this->u.industry.other_produced, INVALID_CARGO, MAX_CARGOES);
 
	}
 

	
 
	/**
 
	 * Connect a cargo from an industry to the #CFT_CARGO column.
 
	 * @param cargo Cargo to connect.
 
	 * @param producer Cargo is produced (if \c false, cargo is assumed to be accepted).
 
	 * @return Horizontal connection index, or \c -1 if not accepted at all.
 
	 */
 
	int ConnectCargo(CargoID cargo, bool producer)
 
	{
 
		assert(this->type == CFT_CARGO);
 
		if (cargo == INVALID_CARGO) return -1;
 
		if (!IsValidCargoID(cargo)) return -1;
 

	
 
		/* Find the vertical cargo column carrying the cargo. */
 
		int column = -1;
 
		for (int i = 0; i < this->u.cargo.num_cargoes; i++) {
 
			if (cargo == this->u.cargo.vertical_cargoes[i]) {
 
				column = i;
 
				break;
 
			}
 
		}
 
		if (column < 0) return -1;
 

	
 
		if (producer) {
 
			assert(this->u.cargo.supp_cargoes[column] == INVALID_CARGO);
 
			assert(!IsValidCargoID(this->u.cargo.supp_cargoes[column]));
 
			this->u.cargo.supp_cargoes[column]  = column;
 
		} else {
 
			assert(this->u.cargo.cust_cargoes[column] == INVALID_CARGO);
 
			assert(!IsValidCargoID(this->u.cargo.cust_cargoes[column]));
 
			this->u.cargo.cust_cargoes[column] = column;
 
		}
 
		return column;
 
	}
 

	
 
	/**
 
	 * Does this #CFT_CARGO field have a horizontal connection?
 
	 * @return \c true if a horizontal connection exists, \c false otherwise.
 
	 */
 
	bool HasConnection()
 
	{
 
		assert(this->type == CFT_CARGO);
 

	
 
		for (uint i = 0; i < MAX_CARGOES; i++) {
 
			if (this->u.cargo.supp_cargoes[i] != INVALID_CARGO) return true;
 
			if (this->u.cargo.cust_cargoes[i] != INVALID_CARGO) return true;
 
			if (IsValidCargoID(this->u.cargo.supp_cargoes[i])) return true;
 
			if (IsValidCargoID(this->u.cargo.cust_cargoes[i])) return true;
 
		}
 
		return false;
 
	}
 

	
 
	/**
 
	 * Make a piece of cargo column.
 
	 * @param cargoes    Array of #CargoID (may contain #INVALID_CARGO).
 
	 * @param length     Number of cargoes in \a cargoes.
 
	 * @param count      Number of cargoes to display (should be at least the number of valid cargoes, or \c -1 to let the method compute it).
 
	 * @param top_end    This is the first cargo field of this column.
 
	 * @param bottom_end This is the last cargo field of this column.
 
	 * @note #supp_cargoes and #cust_cargoes should be filled in later.
 
	 */
 
	void MakeCargo(const CargoID *cargoes, uint length, int count = -1, bool top_end = false, bool bottom_end = false)
 
	{
 
		this->type = CFT_CARGO;
 
		uint i;
 
		uint num = 0;
 
		for (i = 0; i < MAX_CARGOES && i < length; i++) {
 
			if (cargoes[i] != INVALID_CARGO) {
 
			if (IsValidCargoID(cargoes[i])) {
 
				this->u.cargo.vertical_cargoes[num] = cargoes[i];
 
				num++;
 
			}
 
		}
 
		this->u.cargo.num_cargoes = (count < 0) ? num : count;
 
		for (; num < MAX_CARGOES; num++) this->u.cargo.vertical_cargoes[num] = INVALID_CARGO;
 
		this->u.cargo.top_end = top_end;
 
		this->u.cargo.bottom_end = bottom_end;
 
		MemSetT(this->u.cargo.supp_cargoes, INVALID_CARGO, MAX_CARGOES);
 
		MemSetT(this->u.cargo.cust_cargoes, INVALID_CARGO, MAX_CARGOES);
 
	}
 

	
 
@@ -2119,31 +2119,31 @@ struct CargoesField {
 

	
 
				/* Draw the other_produced/other_accepted cargoes. */
 
				const CargoID *other_right, *other_left;
 
				if (_current_text_dir == TD_RTL) {
 
					other_right = this->u.industry.other_accepted;
 
					other_left  = this->u.industry.other_produced;
 
				} else {
 
					other_right = this->u.industry.other_produced;
 
					other_left  = this->u.industry.other_accepted;
 
				}
 
				ypos1 += CargoesField::cargo_border.height + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2;
 
				for (uint i = 0; i < CargoesField::max_cargoes; i++) {
 
					if (other_right[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(other_right[i])) {
 
						const CargoSpec *csp = CargoSpec::Get(other_right[i]);
 
						int xp = xpos + industry_width + CargoesField::cargo_stub.width;
 
						DrawHorConnection(xpos + industry_width, xp - 1, ypos1, csp);
 
						GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
 
					}
 
					if (other_left[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(other_left[i])) {
 
						const CargoSpec *csp = CargoSpec::Get(other_left[i]);
 
						int xp = xpos - CargoesField::cargo_stub.width;
 
						DrawHorConnection(xp + 1, xpos - 1, ypos1, csp);
 
						GfxDrawLine(xp, ypos1, xp, ypos1 + CargoesField::cargo_line.height - 1, CARGO_LINE_COLOUR);
 
					}
 
					ypos1 += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height;
 
				}
 
				break;
 
			}
 

	
 
			case CFT_CARGO: {
 
				int cargo_base = this->GetCargoBase(xpos);
 
@@ -2163,55 +2163,55 @@ struct CargoesField {
 
				}
 

	
 
				const CargoID *hor_left, *hor_right;
 
				if (_current_text_dir == TD_RTL) {
 
					hor_left  = this->u.cargo.cust_cargoes;
 
					hor_right = this->u.cargo.supp_cargoes;
 
				} else {
 
					hor_left  = this->u.cargo.supp_cargoes;
 
					hor_right = this->u.cargo.cust_cargoes;
 
				}
 
				ypos += CargoesField::cargo_border.height + vert_inter_industry_space / 2 + (FONT_HEIGHT_NORMAL - CargoesField::cargo_line.height) / 2;
 
				for (uint i = 0; i < MAX_CARGOES; i++) {
 
					if (hor_left[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(hor_left[i])) {
 
						int col = hor_left[i];
 
						int dx = 0;
 
						const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
 
						for (; col > 0; col--) {
 
							int lf = cargo_base + col * CargoesField::cargo_line.width + (col - 1) * CargoesField::cargo_space.width;
 
							DrawHorConnection(lf, lf + CargoesField::cargo_space.width - dx, ypos, csp);
 
							dx = 1;
 
						}
 
						DrawHorConnection(xpos, cargo_base - dx, ypos, csp);
 
					}
 
					if (hor_right[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(hor_right[i])) {
 
						int col = hor_right[i];
 
						int dx = 0;
 
						const CargoSpec *csp = CargoSpec::Get(this->u.cargo.vertical_cargoes[col]);
 
						for (; col < this->u.cargo.num_cargoes - 1; col++) {
 
							int lf = cargo_base + (col + 1) * CargoesField::cargo_line.width + col * CargoesField::cargo_space.width;
 
							DrawHorConnection(lf + dx - 1, lf + CargoesField::cargo_space.width - 1, ypos, csp);
 
							dx = 1;
 
						}
 
						DrawHorConnection(cargo_base + col * CargoesField::cargo_space.width + (col + 1) * CargoesField::cargo_line.width - 1 + dx, xpos + CargoesField::cargo_field_width - 1, ypos, csp);
 
					}
 
					ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height;
 
				}
 
				break;
 
			}
 

	
 
			case CFT_CARGO_LABEL:
 
				ypos += CargoesField::cargo_border.height + vert_inter_industry_space / 2;
 
				for (uint i = 0; i < MAX_CARGOES; i++) {
 
					if (this->u.cargo_label.cargoes[i] != INVALID_CARGO) {
 
					if (IsValidCargoID(this->u.cargo_label.cargoes[i])) {
 
						const CargoSpec *csp = CargoSpec::Get(this->u.cargo_label.cargoes[i]);
 
						DrawString(xpos + WidgetDimensions::scaled.framerect.left, xpos + industry_width - 1 - WidgetDimensions::scaled.framerect.right, ypos, csp->name, TC_WHITE,
 
								(this->u.cargo_label.left_align) ? SA_LEFT : SA_RIGHT);
 
					}
 
					ypos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.height;
 
				}
 
				break;
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
	}
 
@@ -2239,48 +2239,48 @@ struct CargoesField {
 

	
 
		int vpos = vert_inter_industry_space / 2 + CargoesField::cargo_border.width;
 
		uint row;
 
		for (row = 0; row < MAX_CARGOES; row++) {
 
			if (pt.y < vpos) return INVALID_CARGO;
 
			if (pt.y < vpos + FONT_HEIGHT_NORMAL) break;
 
			vpos += FONT_HEIGHT_NORMAL + CargoesField::cargo_space.width;
 
		}
 
		if (row == MAX_CARGOES) return INVALID_CARGO;
 

	
 
		/* row = 0 -> at first horizontal row, row = 1 -> second horizontal row, 2 = 3rd horizontal row. */
 
		if (col == 0) {
 
			if (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
 
			if (IsValidCargoID(this->u.cargo.supp_cargoes[row])) return this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]];
 
			if (left != nullptr) {
 
				if (left->type == CFT_INDUSTRY) return left->u.industry.other_produced[row];
 
				if (left->type == CFT_CARGO_LABEL && !left->u.cargo_label.left_align) return left->u.cargo_label.cargoes[row];
 
			}
 
			return INVALID_CARGO;
 
		}
 
		if (col == this->u.cargo.num_cargoes) {
 
			if (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
 
			if (IsValidCargoID(this->u.cargo.cust_cargoes[row])) return this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]];
 
			if (right != nullptr) {
 
				if (right->type == CFT_INDUSTRY) return right->u.industry.other_accepted[row];
 
				if (right->type == CFT_CARGO_LABEL && right->u.cargo_label.left_align) return right->u.cargo_label.cargoes[row];
 
			}
 
			return INVALID_CARGO;
 
		}
 
		if (row >= col) {
 
			/* Clicked somewhere in-between vertical cargo connection.
 
			 * Since the horizontal connection is made in the same order as the vertical list, the above condition
 
			 * ensures we are left-below the main diagonal, thus at the supplying side.
 
			 */
 
			return (this->u.cargo.supp_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
 
			return (IsValidCargoID(this->u.cargo.supp_cargoes[row])) ? this->u.cargo.vertical_cargoes[this->u.cargo.supp_cargoes[row]] : INVALID_CARGO;
 
		} else {
 
			/* Clicked at a customer connection. */
 
			return (this->u.cargo.cust_cargoes[row] != INVALID_CARGO) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
 
			return (IsValidCargoID(this->u.cargo.cust_cargoes[row])) ? this->u.cargo.vertical_cargoes[this->u.cargo.cust_cargoes[row]] : INVALID_CARGO;
 
		}
 
	}
 

	
 
	/**
 
	 * Decide what cargo the user clicked in the cargo label field.
 
	 * @param pt Click position in the cargo label field.
 
	 * @return Cargo clicked at, or #INVALID_CARGO if none.
 
	 */
 
	CargoID CargoLabelClickedAt(Point pt) const
 
	{
 
		assert(this->type == CFT_CARGO_LABEL);
 

	
 
@@ -2352,25 +2352,25 @@ struct CargoesRow {
 
			CargoID others[MAX_CARGOES]; // Produced cargoes not carried in the cargo column.
 
			int other_count = 0;
 

	
 
			const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
 
			assert(CargoesField::max_cargoes <= lengthof(indsp->produced_cargo));
 
			for (uint i = 0; i < CargoesField::max_cargoes; i++) {
 
				int col = cargo_fld->ConnectCargo(indsp->produced_cargo[i], true);
 
				if (col < 0) others[other_count++] = indsp->produced_cargo[i];
 
			}
 

	
 
			/* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
 
			for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
 
				if (cargo_fld->u.cargo.supp_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_produced[i] = others[--other_count];
 
				if (!IsValidCargoID(cargo_fld->u.cargo.supp_cargoes[i])) ind_fld->u.industry.other_produced[i] = others[--other_count];
 
			}
 
		} else {
 
			/* Houses only display what is demanded. */
 
			for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
 
				CargoID cid = cargo_fld->u.cargo.vertical_cargoes[i];
 
				if (cid == CT_PASSENGERS || cid == CT_MAIL) cargo_fld->ConnectCargo(cid, true);
 
			}
 
		}
 
	}
 

	
 
	/**
 
	 * Construct a #CFT_CARGO_LABEL field.
 
@@ -2410,25 +2410,25 @@ struct CargoesRow {
 
			CargoID others[MAX_CARGOES]; // Accepted cargoes not carried in the cargo column.
 
			int other_count = 0;
 

	
 
			const IndustrySpec *indsp = GetIndustrySpec(ind_fld->u.industry.ind_type);
 
			assert(CargoesField::max_cargoes <= lengthof(indsp->accepts_cargo));
 
			for (uint i = 0; i < CargoesField::max_cargoes; i++) {
 
				int col = cargo_fld->ConnectCargo(indsp->accepts_cargo[i], false);
 
				if (col < 0) others[other_count++] = indsp->accepts_cargo[i];
 
			}
 

	
 
			/* Allocate other cargoes in the empty holes of the horizontal cargo connections. */
 
			for (uint i = 0; i < CargoesField::max_cargoes && other_count > 0; i++) {
 
				if (cargo_fld->u.cargo.cust_cargoes[i] == INVALID_CARGO) ind_fld->u.industry.other_accepted[i] = others[--other_count];
 
				if (!IsValidCargoID(cargo_fld->u.cargo.cust_cargoes[i])) ind_fld->u.industry.other_accepted[i] = others[--other_count];
 
			}
 
		} else {
 
			/* Houses only display what is demanded. */
 
			for (uint i = 0; i < cargo_fld->u.cargo.num_cargoes; i++) {
 
				for (uint h = 0; h < NUM_HOUSES; h++) {
 
					HouseSpec *hs = HouseSpec::Get(h);
 
					if (!hs->enabled) continue;
 

	
 
					for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
 
						if (hs->cargo_acceptance[j] > 0 && cargo_fld->u.cargo.vertical_cargoes[i] == hs->accepts_cargo[j]) {
 
							cargo_fld->ConnectCargo(cargo_fld->u.cargo.vertical_cargoes[i], false);
 
							goto next_cargo;
 
@@ -2593,66 +2593,66 @@ struct IndustryCargoesWindow : public Wi
 

	
 
	/**
 
	 * Do the two sets of cargoes have a valid cargo in common?
 
	 * @param cargoes1 Base address of the first cargo array.
 
	 * @param length1  Number of cargoes in the first cargo array.
 
	 * @param cargoes2 Base address of the second cargo array.
 
	 * @param length2  Number of cargoes in the second cargo array.
 
	 * @return Arrays have at least one valid cargo in common.
 
	 */
 
	static bool HasCommonValidCargo(const CargoID *cargoes1, uint length1, const CargoID *cargoes2, uint length2)
 
	{
 
		while (length1 > 0) {
 
			if (*cargoes1 != INVALID_CARGO) {
 
			if (IsValidCargoID(*cargoes1)) {
 
				for (uint i = 0; i < length2; i++) if (*cargoes1 == cargoes2[i]) return true;
 
			}
 
			cargoes1++;
 
			length1--;
 
		}
 
		return false;
 
	}
 

	
 
	/**
 
	 * Can houses be used to supply one of the cargoes?
 
	 * @param cargoes Base address of the cargo array.
 
	 * @param length  Number of cargoes in the array.
 
	 * @return Houses can supply at least one of the cargoes.
 
	 */
 
	static bool HousesCanSupply(const CargoID *cargoes, uint length)
 
	{
 
		for (uint i = 0; i < length; i++) {
 
			if (cargoes[i] == INVALID_CARGO) continue;
 
			if (!IsValidCargoID(cargoes[i])) continue;
 
			if (cargoes[i] == CT_PASSENGERS || cargoes[i] == CT_MAIL) return true;
 
		}
 
		return false;
 
	}
 

	
 
	/**
 
	 * Can houses be used as customers of the produced cargoes?
 
	 * @param cargoes Base address of the cargo array.
 
	 * @param length  Number of cargoes in the array.
 
	 * @return Houses can accept at least one of the cargoes.
 
	 */
 
	static bool HousesCanAccept(const CargoID *cargoes, uint length)
 
	{
 
		HouseZones climate_mask;
 
		switch (_settings_game.game_creation.landscape) {
 
			case LT_TEMPERATE: climate_mask = HZ_TEMP; break;
 
			case LT_ARCTIC:    climate_mask = HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; break;
 
			case LT_TROPIC:    climate_mask = HZ_SUBTROPIC; break;
 
			case LT_TOYLAND:   climate_mask = HZ_TOYLND; break;
 
			default: NOT_REACHED();
 
		}
 
		for (uint i = 0; i < length; i++) {
 
			if (cargoes[i] == INVALID_CARGO) continue;
 
			if (!IsValidCargoID(cargoes[i])) continue;
 

	
 
			for (uint h = 0; h < NUM_HOUSES; h++) {
 
				HouseSpec *hs = HouseSpec::Get(h);
 
				if (!hs->enabled || !(hs->building_availability & climate_mask)) continue;
 

	
 
				for (uint j = 0; j < lengthof(hs->accepts_cargo); j++) {
 
					if (hs->cargo_acceptance[j] > 0 && cargoes[i] == hs->accepts_cargo[j]) return true;
 
				}
 
			}
 
		}
 
		return false;
 
	}
 
@@ -3003,31 +3003,31 @@ struct IndustryCargoesWindow : public Wi
 
				if (!CalculatePositionInWidget(pt, &fieldxy, &xy)) return;
 

	
 
				const CargoesField *fld = this->fields[fieldxy.y].columns + fieldxy.x;
 
				switch (fld->type) {
 
					case CFT_INDUSTRY:
 
						if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES) this->ComputeIndustryDisplay(fld->u.industry.ind_type);
 
						break;
 

	
 
					case CFT_CARGO: {
 
						CargoesField *lft = (fieldxy.x > 0) ? this->fields[fieldxy.y].columns + fieldxy.x - 1 : nullptr;
 
						CargoesField *rgt = (fieldxy.x < 4) ? this->fields[fieldxy.y].columns + fieldxy.x + 1 : nullptr;
 
						CargoID cid = fld->CargoClickedAt(lft, rgt, xy);
 
						if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
 
						if (IsValidCargoID(cid)) this->ComputeCargoDisplay(cid);
 
						break;
 
					}
 

	
 
					case CFT_CARGO_LABEL: {
 
						CargoID cid = fld->CargoLabelClickedAt(xy);
 
						if (cid != INVALID_CARGO) this->ComputeCargoDisplay(cid);
 
						if (IsValidCargoID(cid)) this->ComputeCargoDisplay(cid);
 
						break;
 
					}
 

	
 
					default:
 
						break;
 
				}
 
				break;
 
			}
 

	
 
			case WID_IC_NOTIFY:
 
				this->ToggleWidgetLoweredState(WID_IC_NOTIFY);
 
				this->SetWidgetDirty(WID_IC_NOTIFY);
 
@@ -3104,25 +3104,25 @@ struct IndustryCargoesWindow : public Wi
 
				break;
 
			}
 

	
 
			case CFT_INDUSTRY:
 
				if (fld->u.industry.ind_type < NUM_INDUSTRYTYPES && (this->ind_cargo >= NUM_INDUSTRYTYPES || fieldxy.x != 2)) {
 
					GuiShowTooltips(this, STR_INDUSTRY_CARGOES_INDUSTRY_TOOLTIP, 0, nullptr, close_cond);
 
				}
 
				return true;
 

	
 
			default:
 
				break;
 
		}
 
		if (cid != INVALID_CARGO && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
 
		if (IsValidCargoID(cid) && (this->ind_cargo < NUM_INDUSTRYTYPES || cid != this->ind_cargo - NUM_INDUSTRYTYPES)) {
 
			const CargoSpec *csp = CargoSpec::Get(cid);
 
			uint64 params[5];
 
			params[0] = csp->name;
 
			GuiShowTooltips(this, STR_INDUSTRY_CARGOES_CARGO_TOOLTIP, 1, params, close_cond);
 
			return true;
 
		}
 

	
 
		return false;
 
	}
 

	
 
	void OnResize() override
 
	{
src/newgrf.cpp
Show inline comments
 
@@ -935,25 +935,25 @@ static bool ReadSpriteLayout(ByteReader 
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Translate the refit mask. refit_mask is uint32 as it has not been mapped to CargoTypes.
 
 */
 
static CargoTypes TranslateRefitMask(uint32 refit_mask)
 
{
 
	CargoTypes result = 0;
 
	for (uint8 bit : SetBitIterator(refit_mask)) {
 
		CargoID cargo = GetCargoTranslation(bit, _cur.grffile, true);
 
		if (cargo != CT_INVALID) SetBit(result, cargo);
 
		if (IsValidCargoID(cargo)) SetBit(result, cargo);
 
	}
 
	return result;
 
}
 

	
 
/**
 
 * Converts TTD(P) Base Price pointers into the enum used by OTTD
 
 * See http://wiki.ttdpatch.net/tiki-index.php?page=BaseCosts
 
 * @param base_pointer TTD(P) Base Price Pointer
 
 * @param error_location Function name for grf error messages
 
 * @param[out] index If \a base_pointer is valid, \a index is assigned to the matching price; else it is left unchanged
 
 */
 
static void ConvertTTDBasePrice(uint32 base_pointer, const char *error_location, Price *index)
 
@@ -1297,26 +1297,25 @@ static ChangeInfoResult RailVehicleChang
 
				ei->cargo_age_period = buf->ReadWord();
 
				break;
 

	
 
			case 0x2C:   // CTT refit include list
 
			case 0x2D: { // CTT refit exclude list
 
				uint8 count = buf->ReadByte();
 
				_gted[e->index].UpdateRefittability(prop == 0x2C && count != 0);
 
				if (prop == 0x2C) _gted[e->index].defaultcargo_grf = _cur.grffile;
 
				CargoTypes &ctt = prop == 0x2C ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
 
				ctt = 0;
 
				while (count--) {
 
					CargoID ctype = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
 
					if (ctype == CT_INVALID) continue;
 
					SetBit(ctt, ctype);
 
					if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
 
				}
 
				break;
 
			}
 

	
 
			case PROP_TRAIN_CURVE_SPEED_MOD: // 0x2E Curve speed modifier
 
				rvi->curve_speed_mod = buf->ReadWord();
 
				break;
 

	
 
			case 0x2F: // Engine variant
 
				ei->variant_id = buf->ReadWord();
 
				break;
 

	
 
@@ -1507,26 +1506,25 @@ static ChangeInfoResult RoadVehicleChang
 
				rvi->shorten_factor = buf->ReadByte();
 
				break;
 

	
 
			case 0x24:   // CTT refit include list
 
			case 0x25: { // CTT refit exclude list
 
				uint8 count = buf->ReadByte();
 
				_gted[e->index].UpdateRefittability(prop == 0x24 && count != 0);
 
				if (prop == 0x24) _gted[e->index].defaultcargo_grf = _cur.grffile;
 
				CargoTypes &ctt = prop == 0x24 ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
 
				ctt = 0;
 
				while (count--) {
 
					CargoID ctype = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
 
					if (ctype == CT_INVALID) continue;
 
					SetBit(ctt, ctype);
 
					if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
 
				}
 
				break;
 
			}
 

	
 
			case 0x26: // Engine variant
 
				ei->variant_id = buf->ReadWord();
 
				break;
 

	
 
			case 0x27: // Extra miscellaneous flags
 
				ei->extra_flags = static_cast<ExtraEngineFlags>(buf->ReadDWord());
 
				break;
 

	
 
@@ -1691,26 +1689,25 @@ static ChangeInfoResult ShipVehicleChang
 
				ei->cargo_age_period = buf->ReadWord();
 
				break;
 

	
 
			case 0x1E:   // CTT refit include list
 
			case 0x1F: { // CTT refit exclude list
 
				uint8 count = buf->ReadByte();
 
				_gted[e->index].UpdateRefittability(prop == 0x1E && count != 0);
 
				if (prop == 0x1E) _gted[e->index].defaultcargo_grf = _cur.grffile;
 
				CargoTypes &ctt = prop == 0x1E ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
 
				ctt = 0;
 
				while (count--) {
 
					CargoID ctype = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
 
					if (ctype == CT_INVALID) continue;
 
					SetBit(ctt, ctype);
 
					if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
 
				}
 
				break;
 
			}
 

	
 
			case 0x20: // Engine variant
 
				ei->variant_id = buf->ReadWord();
 
				break;
 

	
 
			case 0x21: // Extra miscellaneous flags
 
				ei->extra_flags = static_cast<ExtraEngineFlags>(buf->ReadDWord());
 
				break;
 

	
 
@@ -1853,26 +1850,25 @@ static ChangeInfoResult AircraftVehicleC
 
				ei->cargo_age_period = buf->ReadWord();
 
				break;
 

	
 
			case 0x1D:   // CTT refit include list
 
			case 0x1E: { // CTT refit exclude list
 
				uint8 count = buf->ReadByte();
 
				_gted[e->index].UpdateRefittability(prop == 0x1D && count != 0);
 
				if (prop == 0x1D) _gted[e->index].defaultcargo_grf = _cur.grffile;
 
				CargoTypes &ctt = prop == 0x1D ? _gted[e->index].ctt_include_mask : _gted[e->index].ctt_exclude_mask;
 
				ctt = 0;
 
				while (count--) {
 
					CargoID ctype = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
 
					if (ctype == CT_INVALID) continue;
 
					SetBit(ctt, ctype);
 
					if (IsValidCargoID(ctype)) SetBit(ctt, ctype);
 
				}
 
				break;
 
			}
 

	
 
			case PROP_AIRCRAFT_RANGE: // 0x1F Max aircraft range
 
				avi->max_range = buf->ReadWord();
 
				break;
 

	
 
			case 0x20: // Engine variant
 
				ei->variant_id = buf->ReadWord();
 
				break;
 

	
 
@@ -2538,43 +2534,43 @@ static ChangeInfoResult TownHouseChangeI
 

	
 
			case 0x1E: { // Accepted cargo types
 
				uint32 cargotypes = buf->ReadDWord();
 

	
 
				/* Check if the cargo types should not be changed */
 
				if (cargotypes == 0xFFFFFFFF) break;
 

	
 
				for (uint j = 0; j < 3; j++) {
 
					/* Get the cargo number from the 'list' */
 
					uint8 cargo_part = GB(cargotypes, 8 * j, 8);
 
					CargoID cargo = GetCargoTranslation(cargo_part, _cur.grffile);
 

	
 
					if (cargo == CT_INVALID) {
 
					if (!IsValidCargoID(cargo)) {
 
						/* Disable acceptance of invalid cargo type */
 
						housespec->cargo_acceptance[j] = 0;
 
					} else {
 
						housespec->accepts_cargo[j] = cargo;
 
					}
 
				}
 
				break;
 
			}
 

	
 
			case 0x1F: // Minimum life span
 
				housespec->minimum_life = buf->ReadByte();
 
				break;
 

	
 
			case 0x20: { // Cargo acceptance watch list
 
				byte count = buf->ReadByte();
 
				for (byte j = 0; j < count; j++) {
 
					CargoID cargo = GetCargoTranslation(buf->ReadByte(), _cur.grffile);
 
					if (cargo != CT_INVALID) SetBit(housespec->watched_cargoes, cargo);
 
					if (IsValidCargoID(cargo)) SetBit(housespec->watched_cargoes, cargo);
 
				}
 
				break;
 
			}
 

	
 
			case 0x21: // long introduction year
 
				housespec->min_year = buf->ReadWord();
 
				break;
 

	
 
			case 0x22: // long maximum year
 
				housespec->max_year = buf->ReadWord();
 
				break;
 

	
 
@@ -5448,47 +5444,47 @@ static void NewSpriteGroup(ByteReader *b
 
						}
 
						group->again = buf->ReadByte();
 
					} else if (type == 2) {
 
						group->num_input = buf->ReadByte();
 
						if (group->num_input > lengthof(group->subtract_input)) {
 
							GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
 
							error->data = "too many inputs (max 16)";
 
							return;
 
						}
 
						for (uint i = 0; i < group->num_input; i++) {
 
							byte rawcargo = buf->ReadByte();
 
							CargoID cargo = GetCargoTranslation(rawcargo, _cur.grffile);
 
							if (cargo == CT_INVALID) {
 
							if (!IsValidCargoID(cargo)) {
 
								/* The mapped cargo is invalid. This is permitted at this point,
 
								 * as long as the result is not used. Mark it invalid so this
 
								 * can be tested later. */
 
								group->version = 0xFF;
 
							} else if (std::find(group->cargo_input, group->cargo_input + i, cargo) != group->cargo_input + i) {
 
								GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
 
								error->data = "duplicate input cargo";
 
								return;
 
							}
 
							group->cargo_input[i] = cargo;
 
							group->subtract_input[i] = buf->ReadByte();
 
						}
 
						group->num_output = buf->ReadByte();
 
						if (group->num_output > lengthof(group->add_output)) {
 
							GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
 
							error->data = "too many outputs (max 16)";
 
							return;
 
						}
 
						for (uint i = 0; i < group->num_output; i++) {
 
							byte rawcargo = buf->ReadByte();
 
							CargoID cargo = GetCargoTranslation(rawcargo, _cur.grffile);
 
							if (cargo == CT_INVALID) {
 
							if (!IsValidCargoID(cargo)) {
 
								/* Mark this result as invalid to use */
 
								group->version = 0xFF;
 
							} else if (std::find(group->cargo_output, group->cargo_output + i, cargo) != group->cargo_output + i) {
 
								GRFError *error = DisableGrf(STR_NEWGRF_ERROR_INDPROD_CALLBACK);
 
								error->data = "duplicate output cargo";
 
								return;
 
							}
 
							group->cargo_output[i] = cargo;
 
							group->add_output[i] = buf->ReadByte();
 
						}
 
						group->again = buf->ReadByte();
 
					} else {
 
@@ -5544,25 +5540,25 @@ static CargoID TranslateCargo(uint8 feat
 
		GrfMsg(1, "TranslateCargo: Cargo type {} out of range (max {}), skipping.", ctype, (unsigned int)_cur.grffile->cargo_list.size() - 1);
 
		return CT_INVALID;
 
	}
 

	
 
	/* Look up the cargo label from the translation table */
 
	CargoLabel cl = _cur.grffile->cargo_list[ctype];
 
	if (cl == 0) {
 
		GrfMsg(5, "TranslateCargo: Cargo type {} not available in this climate, skipping.", ctype);
 
		return CT_INVALID;
 
	}
 

	
 
	ctype = GetCargoIDByLabel(cl);
 
	if (ctype == CT_INVALID) {
 
	if (!IsValidCargoID(ctype)) {
 
		GrfMsg(5, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' unsupported, skipping.", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8));
 
		return CT_INVALID;
 
	}
 

	
 
	GrfMsg(6, "TranslateCargo: Cargo '{:c}{:c}{:c}{:c}' mapped to cargo type {}.", GB(cl, 24, 8), GB(cl, 16, 8), GB(cl, 8, 8), GB(cl, 0, 8), ctype);
 
	return ctype;
 
}
 

	
 

	
 
static bool IsValidGroupID(uint16 groupid, const char *function)
 
{
 
	if (groupid > MAX_SPRITEGROUP || _cur.spritegroups[groupid] == nullptr) {
 
@@ -5614,25 +5610,25 @@ static void VehicleMapSpriteGroup(ByteRe
 
		if (!wagover) last_engines[i] = engines[i];
 
	}
 

	
 
	uint8 cidcount = buf->ReadByte();
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "VehicleMapSpriteGroup")) continue;
 

	
 
		GrfMsg(8, "VehicleMapSpriteGroup: * [{}] Cargo type 0x{:X}, group id 0x{:02X}", c, ctype, groupid);
 

	
 
		ctype = TranslateCargo(feature, ctype);
 
		if (ctype == CT_INVALID) continue;
 
		if (!IsValidCargoID(ctype)) continue;
 

	
 
		for (uint i = 0; i < idcount; i++) {
 
			EngineID engine = engines[i];
 

	
 
			GrfMsg(7, "VehicleMapSpriteGroup: [{}] Engine {}...", i, engine);
 

	
 
			if (wagover) {
 
				SetWagonOverrideSprites(engine, ctype, _cur.spritegroups[groupid], last_engines, last_engines_count);
 
			} else {
 
				SetCustomEngineSprites(engine, ctype, _cur.spritegroups[groupid]);
 
			}
 
		}
 
@@ -5693,25 +5689,25 @@ static void StationMapSpriteGroup(ByteRe
 
	stations.reserve(idcount);
 
	for (uint i = 0; i < idcount; i++) {
 
		stations.push_back(buf->ReadExtendedByte());
 
	}
 

	
 
	uint8 cidcount = buf->ReadByte();
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "StationMapSpriteGroup")) continue;
 

	
 
		ctype = TranslateCargo(GSF_STATIONS, ctype);
 
		if (ctype == CT_INVALID) continue;
 
		if (!IsValidCargoID(ctype)) continue;
 

	
 
		for (auto &station : stations) {
 
			StationSpec *statspec = station >= _cur.grffile->stations.size() ? nullptr : _cur.grffile->stations[station].get();
 

	
 
			if (statspec == nullptr) {
 
				GrfMsg(1, "StationMapSpriteGroup: Station {} undefined, skipping", station);
 
				continue;
 
			}
 

	
 
			statspec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid];
 
		}
 
	}
 
@@ -5874,25 +5870,25 @@ static void ObjectMapSpriteGroup(ByteRea
 
	objects.reserve(idcount);
 
	for (uint i = 0; i < idcount; i++) {
 
		objects.push_back(buf->ReadExtendedByte());
 
	}
 

	
 
	uint8 cidcount = buf->ReadByte();
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "ObjectMapSpriteGroup")) continue;
 

	
 
		ctype = TranslateCargo(GSF_OBJECTS, ctype);
 
		if (ctype == CT_INVALID) continue;
 
		if (!IsValidCargoID(ctype)) continue;
 

	
 
		for (auto &object : objects) {
 
			ObjectSpec *spec = object >= _cur.grffile->objectspec.size() ? nullptr : _cur.grffile->objectspec[object].get();
 

	
 
			if (spec == nullptr) {
 
				GrfMsg(1, "ObjectMapSpriteGroup: Object {} undefined, skipping", object);
 
				continue;
 
			}
 

	
 
			spec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid];
 
		}
 
	}
 
@@ -6060,25 +6056,25 @@ static void RoadStopMapSpriteGroup(ByteR
 
	roadstops.reserve(idcount);
 
	for (uint i = 0; i < idcount; i++) {
 
		roadstops.push_back(buf->ReadExtendedByte());
 
	}
 

	
 
	uint8 cidcount = buf->ReadByte();
 
	for (uint c = 0; c < cidcount; c++) {
 
		uint8 ctype = buf->ReadByte();
 
		uint16 groupid = buf->ReadWord();
 
		if (!IsValidGroupID(groupid, "RoadStopMapSpriteGroup")) continue;
 

	
 
		ctype = TranslateCargo(GSF_ROADSTOPS, ctype);
 
		if (ctype == CT_INVALID) continue;
 
		if (!IsValidCargoID(ctype)) continue;
 

	
 
		for (auto &roadstop : roadstops) {
 
			RoadStopSpec *roadstopspec = roadstop >= _cur.grffile->roadstops.size() ? nullptr : _cur.grffile->roadstops[roadstop].get();
 

	
 
			if (roadstopspec == nullptr) {
 
				GrfMsg(1, "RoadStopMapSpriteGroup: Road stop {} undefined, skipping", roadstop);
 
				continue;
 
			}
 

	
 
			roadstopspec->grf_prop.spritegroup[ctype] = _cur.spritegroups[groupid];
 
		}
 
	}
 
@@ -6835,27 +6831,27 @@ static void SkipIf(ByteReader *buf)
 
		GrfMsg(7, "SkipIf: Param {} undefined, skipping test", param);
 
		return;
 
	}
 

	
 
	GrfMsg(7, "SkipIf: Test condtype {}, param 0x{:02X}, condval 0x{:08X}", condtype, param, cond_val);
 

	
 
	/* condtypes that do not use 'param' are always valid.
 
	 * condtypes that use 'param' are either not valid for param 0x88, or they are only valid for param 0x88.
 
	 */
 
	if (condtype >= 0x0B) {
 
		/* Tests that ignore 'param' */
 
		switch (condtype) {
 
			case 0x0B: result = GetCargoIDByLabel(BSWAP32(cond_val)) == CT_INVALID;
 
				break;
 
			case 0x0C: result = GetCargoIDByLabel(BSWAP32(cond_val)) != CT_INVALID;
 
			case 0x0B: result = !IsValidCargoID(GetCargoIDByLabel(BSWAP32(cond_val)));
 
				break;
 
			case 0x0C: result = IsValidCargoID(GetCargoIDByLabel(BSWAP32(cond_val)));
 
				break;
 
			case 0x0D: result = GetRailTypeByLabel(BSWAP32(cond_val)) == INVALID_RAILTYPE;
 
				break;
 
			case 0x0E: result = GetRailTypeByLabel(BSWAP32(cond_val)) != INVALID_RAILTYPE;
 
				break;
 
			case 0x0F: {
 
				RoadType rt = GetRoadTypeByLabel(BSWAP32(cond_val));
 
				result = rt == INVALID_ROADTYPE || !RoadTypeIsRoad(rt);
 
				break;
 
			}
 
			case 0x10: {
 
				RoadType rt = GetRoadTypeByLabel(BSWAP32(cond_val));
 
@@ -8936,25 +8932,25 @@ GRFFile::~GRFFile()
 
	delete[] this->language_map;
 
}
 

	
 

	
 
/**
 
 * Precalculate refit masks from cargo classes for all vehicles.
 
 */
 
static void CalculateRefitMasks()
 
{
 
	CargoTypes original_known_cargoes = 0;
 
	for (int ct = 0; ct != NUM_ORIGINAL_CARGO; ++ct) {
 
		CargoID cid = GetDefaultCargoID(_settings_game.game_creation.landscape, static_cast<CargoType>(ct));
 
		if (cid != CT_INVALID) SetBit(original_known_cargoes, cid);
 
		if (IsValidCargoID(cid)) SetBit(original_known_cargoes, cid);
 
	}
 

	
 
	for (Engine *e : Engine::Iterate()) {
 
		EngineID engine = e->index;
 
		EngineInfo *ei = &e->info;
 
		bool only_defaultcargo; ///< Set if the vehicle shall carry only the default cargo
 

	
 
		/* If the NewGRF did not set any cargo properties, we apply default values. */
 
		if (_gted[engine].defaultcargo_grf == nullptr) {
 
			/* If the vehicle has any capacity, apply the default refit masks */
 
			if (e->type != VEH_TRAIN || e->u.rail.capacity != 0) {
 
				static constexpr byte T = 1 << LT_TEMPERATE;
 
@@ -9026,25 +9022,25 @@ static void CalculateRefitMasks()
 
						_gted[engine].cargo_disallowed = drm.cargo_disallowed;
 
						break;
 
					}
 

	
 
					/* All original cargoes have specialised vehicles, so exclude them */
 
					_gted[engine].ctt_exclude_mask = original_known_cargoes;
 
				}
 
			}
 
			_gted[engine].UpdateRefittability(_gted[engine].cargo_allowed != 0);
 

	
 
			/* Translate cargo_type using the original climate-specific cargo table. */
 
			ei->cargo_type = GetDefaultCargoID(_settings_game.game_creation.landscape, static_cast<CargoType>(ei->cargo_type));
 
			if (ei->cargo_type != CT_INVALID) ClrBit(_gted[engine].ctt_exclude_mask, ei->cargo_type);
 
			if (IsValidCargoID(ei->cargo_type)) ClrBit(_gted[engine].ctt_exclude_mask, ei->cargo_type);
 
		}
 

	
 
		/* Compute refittability */
 
		{
 
			CargoTypes mask = 0;
 
			CargoTypes not_mask = 0;
 
			CargoTypes xor_mask = ei->refit_mask;
 

	
 
			/* If the original masks set by the grf are zero, the vehicle shall only carry the default cargo.
 
			 * Note: After applying the translations, the vehicle may end up carrying no defined cargo. It becomes unavailable in that case. */
 
			only_defaultcargo = _gted[engine].refittability != GRFTempEngineData::NONEMPTY;
 

	
 
@@ -9055,63 +9051,63 @@ static void CalculateRefitMasks()
 
					if (_gted[engine].cargo_disallowed & cs->classes) SetBit(not_mask, cs->Index());
 
				}
 
			}
 

	
 
			ei->refit_mask = ((mask & ~not_mask) ^ xor_mask) & _cargo_mask;
 

	
 
			/* Apply explicit refit includes/excludes. */
 
			ei->refit_mask |= _gted[engine].ctt_include_mask;
 
			ei->refit_mask &= ~_gted[engine].ctt_exclude_mask;
 
		}
 

	
 
		/* Clear invalid cargoslots (from default vehicles or pre-NewCargo GRFs) */
 
		if (ei->cargo_type != CT_INVALID && !HasBit(_cargo_mask, ei->cargo_type)) ei->cargo_type = CT_INVALID;
 
		if (IsValidCargoID(ei->cargo_type) && !HasBit(_cargo_mask, ei->cargo_type)) ei->cargo_type = CT_INVALID;
 

	
 
		/* Ensure that the vehicle is either not refittable, or that the default cargo is one of the refittable cargoes.
 
		 * Note: Vehicles refittable to no cargo are handle differently to vehicle refittable to a single cargo. The latter might have subtypes. */
 
		if (!only_defaultcargo && (e->type != VEH_SHIP || e->u.ship.old_refittable) && ei->cargo_type != CT_INVALID && !HasBit(ei->refit_mask, ei->cargo_type)) {
 
		if (!only_defaultcargo && (e->type != VEH_SHIP || e->u.ship.old_refittable) && IsValidCargoID(ei->cargo_type) && !HasBit(ei->refit_mask, ei->cargo_type)) {
 
			ei->cargo_type = CT_INVALID;
 
		}
 

	
 
		/* Check if this engine's cargo type is valid. If not, set to the first refittable
 
		 * cargo type. Finally disable the vehicle, if there is still no cargo. */
 
		if (ei->cargo_type == CT_INVALID && ei->refit_mask != 0) {
 
		if (!IsValidCargoID(ei->cargo_type) && ei->refit_mask != 0) {
 
			/* Figure out which CTT to use for the default cargo, if it is 'first refittable'. */
 
			const uint8 *cargo_map_for_first_refittable = nullptr;
 
			{
 
				const GRFFile *file = _gted[engine].defaultcargo_grf;
 
				if (file == nullptr) file = e->GetGRF();
 
				if (file != nullptr && file->grf_version >= 8 && file->cargo_list.size() != 0) {
 
					cargo_map_for_first_refittable = file->cargo_map;
 
				}
 
			}
 

	
 
			if (cargo_map_for_first_refittable != nullptr) {
 
				/* Use first refittable cargo from cargo translation table */
 
				byte best_local_slot = 0xFF;
 
				for (CargoID cargo_type : SetCargoBitIterator(ei->refit_mask)) {
 
					byte local_slot = cargo_map_for_first_refittable[cargo_type];
 
					if (local_slot < best_local_slot) {
 
						best_local_slot = local_slot;
 
						ei->cargo_type = cargo_type;
 
					}
 
				}
 
			}
 

	
 
			if (ei->cargo_type == CT_INVALID) {
 
			if (!IsValidCargoID(ei->cargo_type)) {
 
				/* Use first refittable cargo slot */
 
				ei->cargo_type = (CargoID)FindFirstBit(ei->refit_mask);
 
			}
 
		}
 
		if (ei->cargo_type == CT_INVALID) ei->climates = 0;
 
		if (!IsValidCargoID(ei->cargo_type)) ei->climates = 0;
 

	
 
		/* Clear refit_mask for not refittable ships */
 
		if (e->type == VEH_SHIP && !e->u.ship.old_refittable) {
 
			ei->refit_mask = 0;
 
		}
 
	}
 
}
 

	
 
/** Set to use the correct action0 properties for each canal feature */
 
static void FinaliseCanals()
 
{
 
	for (uint i = 0; i < CF_END; i++) {
src/newgrf_debug_gui.cpp
Show inline comments
 
@@ -494,25 +494,25 @@ struct NewGRFInspectWindow : Window {
 
					case 4: value = *(const uint32 *)ptr; break;
 
					default: NOT_REACHED();
 
				}
 

	
 
				StringID string;
 
				SetDParam(0, value);
 
				switch (nip->type) {
 
					case NIT_INT:
 
						string = STR_JUST_INT;
 
						break;
 

	
 
					case NIT_CARGO:
 
						string = value != INVALID_CARGO ? CargoSpec::Get(value)->name : STR_QUANTITY_N_A;
 
						string = IsValidCargoID(value) ? CargoSpec::Get(value)->name : STR_QUANTITY_N_A;
 
						break;
 

	
 
					default:
 
						NOT_REACHED();
 
				}
 

	
 
				char buffer[64];
 
				GetString(buffer, string, lastof(buffer));
 
				this->DrawString(r, i++, fmt::format("  {:02x}: {} ({})", nip->prop, buffer, nip->name));
 
			}
 
		}
 

	
src/newgrf_engine.cpp
Show inline comments
 
@@ -955,25 +955,25 @@ static uint32 VehicleGetVariable(Vehicle
 
}
 

	
 
/* virtual */ uint32 VehicleScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const
 
{
 
	if (this->v == nullptr) {
 
		/* Vehicle does not exist, so we're in a purchase list */
 
		switch (variable) {
 
			case 0x43: return GetCompanyInfo(_current_company, LiveryHelper(this->self_type, nullptr)); // Owner information
 
			case 0x46: return 0;               // Motion counter
 
			case 0x47: { // Vehicle cargo info
 
				const Engine *e = Engine::Get(this->self_type);
 
				CargoID cargo_type = e->GetDefaultCargoType();
 
				if (cargo_type != CT_INVALID) {
 
				if (IsValidCargoID(cargo_type)) {
 
					const CargoSpec *cs = CargoSpec::Get(cargo_type);
 
					return (cs->classes << 16) | (cs->weight << 8) | this->ro.grffile->cargo_map[cargo_type];
 
				} else {
 
					return 0x000000FF;
 
				}
 
			}
 
			case 0x48: return Engine::Get(this->self_type)->flags; // Vehicle Type Info
 
			case 0x49: return TimerGameCalendar::year; // 'Long' format build year
 
			case 0x4B: return TimerGameCalendar::date; // Long date of last service
 
			case 0x92: return ClampTo<uint16_t>(TimerGameCalendar::date - DAYS_TILL_ORIGINAL_BASE_YEAR); // Date of last service
 
			case 0x93: return GB(ClampTo<uint16_t>(TimerGameCalendar::date - DAYS_TILL_ORIGINAL_BASE_YEAR), 8, 8);
 
			case 0xC4: return Clamp(TimerGameCalendar::year, ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR) - ORIGINAL_BASE_YEAR; // Build year
src/newgrf_house.cpp
Show inline comments
 
@@ -331,25 +331,25 @@ static uint32 GetDistanceFromNearbyHouse
 
		/* Land info for nearby tiles. */
 
		case 0x62: return GetNearbyTileInformation(parameter, this->tile, this->ro.grffile->grf_version >= 8);
 

	
 
		/* Current animation frame of nearby house tiles */
 
		case 0x63: {
 
			TileIndex testtile = GetNearbyTile(parameter, this->tile);
 
			return IsTileType(testtile, MP_HOUSE) ? GetAnimationFrame(testtile) : 0;
 
		}
 

	
 
		/* Cargo acceptance history of nearby stations */
 
		case 0x64: {
 
			CargoID cid = GetCargoTranslation(parameter, this->ro.grffile);
 
			if (cid == CT_INVALID) return 0;
 
			if (!IsValidCargoID(cid)) return 0;
 

	
 
			/* Extract tile offset. */
 
			int8 x_offs = GB(GetRegister(0x100), 0, 8);
 
			int8 y_offs = GB(GetRegister(0x100), 8, 8);
 
			TileIndex testtile = Map::WrapToMap(this->tile + TileDiffXY(x_offs, y_offs));
 

	
 
			StationFinder stations(TileArea(testtile, 1, 1));
 
			const StationList *sl = stations.GetStations();
 

	
 
			/* Collect acceptance stats. */
 
			uint32 res = 0;
 
			for (Station *st : *sl) {
src/newgrf_industries.cpp
Show inline comments
 
@@ -307,44 +307,44 @@ 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 (cargo == CT_INVALID) return 0;
 
			if (!IsValidCargoID(cargo)) return 0;
 
			int index = this->industry->GetCargoProducedIndex(cargo);
 
			if (index < 0) 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];
 
				default: NOT_REACHED();
 
			}
 
		}
 

	
 

	
 
		case 0x6E:
 
		case 0x6F: {
 
			CargoID cargo = GetCargoTranslation(parameter, this->ro.grffile);
 
			if (cargo == CT_INVALID) return 0;
 
			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];
 
			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;
src/newgrf_roadstop.cpp
Show inline comments
 
@@ -337,25 +337,25 @@ void TriggerRoadStopAnimation(BaseStatio
 
	/* Get Station if it wasn't supplied */
 
	if (st == nullptr) st = BaseStation::GetByTile(trigger_tile);
 

	
 
	/* Check the cached animation trigger bitmask to see if we need
 
	 * to bother with any further processing. */
 
	if (!HasBit(st->cached_roadstop_anim_triggers, trigger)) return;
 

	
 
	uint16 random_bits = Random();
 
	auto process_tile = [&](TileIndex cur_tile) {
 
		const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
 
		if (ss != nullptr && HasBit(ss->animation.triggers, trigger)) {
 
			CargoID cargo;
 
			if (cargo_type == CT_INVALID) {
 
			if (!IsValidCargoID(cargo_type)) {
 
				cargo = CT_INVALID;
 
			} else {
 
				cargo = ss->grf_prop.grffile->cargo_map[cargo_type];
 
			}
 
			RoadStopAnimationBase::ChangeAnimationFrame(CBID_STATION_ANIM_START_STOP, ss, st, cur_tile, (random_bits << 16) | Random(), (uint8)trigger | (cargo << 8));
 
		}
 
	};
 

	
 
	if (trigger == SAT_NEW_CARGO || trigger == SAT_CARGO_TAKEN || trigger == SAT_250_TICKS) {
 
		for (const RoadStopTileData &tile_data : st->custom_roadstop_tile_data) {
 
			process_tile(tile_data.tile);
 
		}
 
@@ -370,25 +370,25 @@ void TriggerRoadStopAnimation(BaseStatio
 
 * @param st the station being triggered
 
 * @param tile the exact tile of the station that should be triggered
 
 * @param trigger trigger type
 
 * @param cargo_type cargo type causing the trigger
 
 */
 
void TriggerRoadStopRandomisation(Station *st, TileIndex tile, RoadStopRandomTrigger trigger, CargoID cargo_type)
 
{
 
	if (st == nullptr) st = Station::GetByTile(tile);
 

	
 
	/* Check the cached cargo trigger bitmask to see if we need
 
	 * to bother with any further processing. */
 
	if (st->cached_roadstop_cargo_triggers == 0) return;
 
	if (cargo_type != CT_INVALID && !HasBit(st->cached_roadstop_cargo_triggers, cargo_type)) return;
 
	if (IsValidCargoID(cargo_type) && !HasBit(st->cached_roadstop_cargo_triggers, cargo_type)) return;
 

	
 
	SetBit(st->waiting_triggers, trigger);
 

	
 
	uint32 whole_reseed = 0;
 

	
 
	CargoTypes empty_mask = 0;
 
	if (trigger == RSRT_CARGO_TAKEN) {
 
		/* Create a bitmask of completely empty cargo types to be matched */
 
		for (CargoID i = 0; i < NUM_CARGO; i++) {
 
			if (st->goods[i].cargo.TotalCount() == 0) {
 
				SetBit(empty_mask, i);
 
			}
 
@@ -397,25 +397,25 @@ void TriggerRoadStopRandomisation(Statio
 

	
 
	uint32 used_triggers = 0;
 
	auto process_tile = [&](TileIndex cur_tile) {
 
		const RoadStopSpec *ss = GetRoadStopSpec(cur_tile);
 
		if (ss == nullptr) return;
 

	
 
		/* Cargo taken "will only be triggered if all of those
 
		 * cargo types have no more cargo waiting." */
 
		if (trigger == RSRT_CARGO_TAKEN) {
 
			if ((ss->cargo_triggers & ~empty_mask) != 0) return;
 
		}
 

	
 
		if (cargo_type == CT_INVALID || HasBit(ss->cargo_triggers, cargo_type)) {
 
		if (!IsValidCargoID(cargo_type) || HasBit(ss->cargo_triggers, cargo_type)) {
 
			RoadStopResolverObject object(ss, st, cur_tile, INVALID_ROADTYPE, GetStationType(cur_tile), GetStationGfx(cur_tile));
 
			object.waiting_triggers = st->waiting_triggers;
 

	
 
			const SpriteGroup *group = object.Resolve();
 
			if (group == nullptr) return;
 

	
 
			used_triggers |= object.used_triggers;
 

	
 
			uint32 reseed = object.GetReseedSum();
 
			if (reseed != 0) {
 
				whole_reseed |= reseed;
 
				reseed >>= 16;
src/newgrf_station.cpp
Show inline comments
 
@@ -403,25 +403,25 @@ uint32 Station::GetNewGRFVariable(const 
 
		case 0x8A: return this->had_vehicle_of_type;
 
		case 0xF1: return (this->airport.tile != INVALID_TILE) ? this->airport.GetSpec()->ttd_airport_type : ATP_TTDP_LARGE;
 
		case 0xF2: return (this->truck_stops != nullptr) ? this->truck_stops->status : 0;
 
		case 0xF3: return (this->bus_stops != nullptr)   ? this->bus_stops->status   : 0;
 
		case 0xF6: return this->airport.flags;
 
		case 0xF7: return GB(this->airport.flags, 8, 8);
 
	}
 

	
 
	/* Handle cargo variables with parameter, 0x60 to 0x65 and 0x69 */
 
	if ((variable >= 0x60 && variable <= 0x65) || variable == 0x69) {
 
		CargoID c = GetCargoTranslation(parameter, object.grffile);
 

	
 
		if (c == CT_INVALID) {
 
		if (!IsValidCargoID(c)) {
 
			switch (variable) {
 
				case 0x62: return 0xFFFFFFFF;
 
				case 0x64: return 0xFF00;
 
				default:   return 0;
 
			}
 
		}
 
		const GoodsEntry *ge = &this->goods[c];
 

	
 
		switch (variable) {
 
			case 0x60: return std::min(ge->cargo.TotalCount(), 4095u);
 
			case 0x61: return ge->HasVehicleEverTriedLoading() ? ge->time_since_pickup : 0;
 
			case 0x62: return ge->HasRating() ? ge->rating : 0xFFFFFFFF;
 
@@ -922,25 +922,25 @@ void TriggerStationAnimation(BaseStation
 
	 * to bother with any further processing. */
 
	if (!HasBit(st->cached_anim_triggers, trigger)) return;
 

	
 
	uint16 random_bits = Random();
 
	ETileArea area = ETileArea(st, trigger_tile, tas[trigger]);
 

	
 
	/* Check all tiles over the station to check if the specindex is still in use */
 
	for (TileIndex tile : area) {
 
		if (st->TileBelongsToRailStation(tile)) {
 
			const StationSpec *ss = GetStationSpec(tile);
 
			if (ss != nullptr && HasBit(ss->animation.triggers, trigger)) {
 
				CargoID cargo;
 
				if (cargo_type == CT_INVALID) {
 
				if (!IsValidCargoID(cargo_type)) {
 
					cargo = CT_INVALID;
 
				} else {
 
					cargo = ss->grf_prop.grffile->cargo_map[cargo_type];
 
				}
 
				StationAnimationBase::ChangeAnimationFrame(CBID_STATION_ANIM_START_STOP, ss, st, tile, (random_bits << 16) | GB(Random(), 0, 16), (uint8)trigger | (cargo << 8));
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Trigger station randomisation
 
@@ -953,25 +953,25 @@ void TriggerStationRandomisation(Station
 
{
 
	/* List of coverage areas for each animation trigger */
 
	static const TriggerArea tas[] = {
 
		TA_WHOLE, TA_WHOLE, TA_PLATFORM, TA_PLATFORM, TA_PLATFORM, TA_PLATFORM
 
	};
 

	
 
	/* Get Station if it wasn't supplied */
 
	if (st == nullptr) st = Station::GetByTile(trigger_tile);
 

	
 
	/* Check the cached cargo trigger bitmask to see if we need
 
	 * to bother with any further processing. */
 
	if (st->cached_cargo_triggers == 0) return;
 
	if (cargo_type != CT_INVALID && !HasBit(st->cached_cargo_triggers, cargo_type)) return;
 
	if (IsValidCargoID(cargo_type) && !HasBit(st->cached_cargo_triggers, cargo_type)) return;
 

	
 
	uint32 whole_reseed = 0;
 
	ETileArea area = ETileArea(st, trigger_tile, tas[trigger]);
 

	
 
	CargoTypes empty_mask = 0;
 
	if (trigger == SRT_CARGO_TAKEN) {
 
		/* Create a bitmask of completely empty cargo types to be matched */
 
		for (CargoID i = 0; i < NUM_CARGO; i++) {
 
			if (st->goods[i].cargo.TotalCount() == 0) {
 
				SetBit(empty_mask, i);
 
			}
 
		}
 
@@ -984,25 +984,25 @@ void TriggerStationRandomisation(Station
 
	/* Check all tiles over the station to check if the specindex is still in use */
 
	for (TileIndex tile : area) {
 
		if (st->TileBelongsToRailStation(tile)) {
 
			const StationSpec *ss = GetStationSpec(tile);
 
			if (ss == nullptr) continue;
 

	
 
			/* Cargo taken "will only be triggered if all of those
 
			 * cargo types have no more cargo waiting." */
 
			if (trigger == SRT_CARGO_TAKEN) {
 
				if ((ss->cargo_triggers & ~empty_mask) != 0) continue;
 
			}
 

	
 
			if (cargo_type == CT_INVALID || HasBit(ss->cargo_triggers, cargo_type)) {
 
			if (!IsValidCargoID(cargo_type) || HasBit(ss->cargo_triggers, cargo_type)) {
 
				StationResolverObject object(ss, st, tile, CBID_RANDOM_TRIGGER, 0);
 
				object.waiting_triggers = st->waiting_triggers;
 

	
 
				const SpriteGroup *group = object.Resolve();
 
				if (group == nullptr) continue;
 

	
 
				used_triggers |= object.used_triggers;
 

	
 
				uint32 reseed = object.GetReseedSum();
 
				if (reseed != 0) {
 
					whole_reseed |= reseed;
 
					reseed >>= 16;
src/saveload/afterload.cpp
Show inline comments
 
@@ -3014,25 +3014,25 @@ bool AfterLoadGame()
 
				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 (size_t ci = 3; ci < lengthof(i->accepts_cargo); ci++) {
 
				i->accepts_cargo[ci] = CT_INVALID;
 
				i->incoming_cargo_waiting[ci] = 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 (i->accepts_cargo[ci] != CT_INVALID) {
 
				if (IsValidCargoID(i->accepts_cargo[ci])) {
 
					i->last_cargo_accepted_at[ci] = i->last_cargo_accepted_at[0];
 
				} else {
 
					i->last_cargo_accepted_at[ci] = 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? */
src/saveload/oldloader_sl.cpp
Show inline comments
 
@@ -1431,25 +1431,25 @@ static const OldChunks subsidy_chunk[] =
 
	OCL_SVAR(  OC_UINT8, Subsidy, cargo_type ),
 
	OCL_SVAR(  OC_UINT8, Subsidy, remaining ),
 
	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, Subsidy, src ),
 
	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, Subsidy, dst ),
 

	
 
	OCL_END()
 
};
 

	
 
static bool LoadOldSubsidy(LoadgameState *ls, int num)
 
{
 
	Subsidy *s = new (num) Subsidy();
 
	bool ret = LoadChunk(ls, s, subsidy_chunk);
 
	if (s->cargo_type == CT_INVALID) delete s;
 
	if (!IsValidCargoID(s->cargo_type)) delete s;
 
	return ret;
 
}
 

	
 
static const OldChunks game_difficulty_chunk[] = {
 
	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, max_no_competitors ),
 
	OCL_NULL( 2), // competitor_start_time
 
	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, number_towns ),
 
	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, industry_density ),
 
	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, DifficultySettings, max_loan ),
 
	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, initial_interest ),
 
	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, vehicle_costs ),
 
	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, competitor_speed ),
src/script/api/script_cargolist.cpp
Show inline comments
 
@@ -22,38 +22,38 @@ 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 (cargo_id != CT_INVALID) {
 
		if (::IsValidCargoID(cargo_id)) {
 
			this->AddItem(cargo_id);
 
		}
 
	}
 
}
 

	
 
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 (cargo_id != CT_INVALID) {
 
		if (::IsValidCargoID(cargo_id)) {
 
			this->AddItem(cargo_id);
 
		}
 
	}
 
}
 

	
 
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
 
@@ -225,25 +225,25 @@
 
/* static */ SQInteger ScriptIndustry::GetLastProductionYear(IndustryID industry_id)
 
{
 
	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 (cargo_type == CT_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); });
 
	} else {
 
		int index = i->GetCargoAcceptedIndex(cargo_type);
 
		if (index < 0) return ScriptDate::DATE_INVALID;
 
		return (ScriptDate::Date)i->last_cargo_accepted_at[index];
 
	}
 
}
 

	
 
/* static */ SQInteger ScriptIndustry::GetControlFlags(IndustryID industry_id)
 
{
 
	Industry *i = Industry::GetIfValid(industry_id);
 
	if (i == nullptr) return 0;
src/script/api/script_industrytype.cpp
Show inline comments
 
@@ -63,39 +63,39 @@
 

	
 
	return GetString(::GetIndustrySpec(industry_type)->name);
 
}
 

	
 
/* static */ ScriptList *ScriptIndustryType::GetProducedCargo(IndustryType industry_type)
 
{
 
	if (!IsValidIndustryType(industry_type)) return nullptr;
 

	
 
	const IndustrySpec *ins = ::GetIndustrySpec(industry_type);
 

	
 
	ScriptList *list = new ScriptList();
 
	for (size_t i = 0; i < lengthof(ins->produced_cargo); i++) {
 
		if (ins->produced_cargo[i] != CT_INVALID) list->AddItem(ins->produced_cargo[i]);
 
		if (::IsValidCargoID(ins->produced_cargo[i])) list->AddItem(ins->produced_cargo[i]);
 
	}
 

	
 
	return list;
 
}
 

	
 
/* static */ ScriptList *ScriptIndustryType::GetAcceptedCargo(IndustryType industry_type)
 
{
 
	if (!IsValidIndustryType(industry_type)) return nullptr;
 

	
 
	const IndustrySpec *ins = ::GetIndustrySpec(industry_type);
 

	
 
	ScriptList *list = new ScriptList();
 
	for (size_t i = 0; i < lengthof(ins->accepts_cargo); i++) {
 
		if (ins->accepts_cargo[i] != CT_INVALID) list->AddItem(ins->accepts_cargo[i]);
 
		if (::IsValidCargoID(ins->accepts_cargo[i])) list->AddItem(ins->accepts_cargo[i]);
 
	}
 

	
 
	return list;
 
}
 

	
 
/* static */ bool ScriptIndustryType::CanBuildIndustry(IndustryType industry_type)
 
{
 
	if (!IsValidIndustryType(industry_type)) return false;
 

	
 
	const bool deity = ScriptCompanyMode::IsDeity();
 
	if (::GetIndustryProbabilityCallback(industry_type, deity ? IACT_RANDOMCREATION : IACT_USERCREATION, 1) == 0) return false;
 
	if (deity) return true;
src/script/api/script_tilelist.cpp
Show inline comments
 
@@ -77,64 +77,64 @@ ScriptTileList_IndustryAccepting::Script
 
{
 
	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 */
 
	if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) return;
 

	
 
	/* Check if this industry accepts anything */
 
	{
 
		bool cargo_accepts = false;
 
		for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
 
			if (i->accepts_cargo[j] != CT_INVALID) cargo_accepts = true;
 
			if (::IsValidCargoID(i->accepts_cargo[j])) cargo_accepts = true;
 
		}
 
		if (!cargo_accepts) 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 (i->accepts_cargo[j] != CT_INVALID && acceptance[i->accepts_cargo[j]] != 0) cargo_accepts = true;
 
				if (::IsValidCargoID(i->accepts_cargo[j]) && acceptance[i->accepts_cargo[j]] != 0) cargo_accepts = true;
 
			}
 
			if (!cargo_accepts) 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 */
 
	if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) return;
 

	
 
	/* Check if this industry produces anything */
 
	bool cargo_produces = false;
 
	for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
 
		if (i->produced_cargo[j] != CT_INVALID) cargo_produces = true;
 
		if (::IsValidCargoID(i->produced_cargo[j])) cargo_produces = true;
 
	}
 
	if (!cargo_produces) 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) {
 
		this->AddTile(cur_tile);
 
	}
src/script/api/script_vehicle.cpp
Show inline comments
 
@@ -66,25 +66,25 @@
 
/* static */ SQInteger ScriptVehicle::GetLength(VehicleID vehicle_id)
 
{
 
	if (!IsValidVehicle(vehicle_id)) return -1;
 

	
 
	const Vehicle *v = ::Vehicle::Get(vehicle_id);
 
	return v->IsGroundVehicle() ? v->GetGroundVehicleCache()->cached_total_length : -1;
 
}
 

	
 
/* static */ VehicleID ScriptVehicle::_BuildVehicleInternal(TileIndex depot, EngineID engine_id, CargoID cargo)
 
{
 
	EnforceCompanyModeValid(VEHICLE_INVALID);
 
	EnforcePrecondition(VEHICLE_INVALID, ScriptEngine::IsBuildable(engine_id));
 
	EnforcePrecondition(VEHICLE_INVALID, cargo == CT_INVALID || ScriptCargo::IsValidCargo(cargo));
 
	EnforcePrecondition(VEHICLE_INVALID, !::IsValidCargoID(cargo) || ScriptCargo::IsValidCargo(cargo));
 

	
 
	::VehicleType type = ::Engine::Get(engine_id)->type;
 

	
 
	EnforcePreconditionCustomError(VEHICLE_INVALID, !ScriptGameSettings::IsDisabledVehicleType((ScriptVehicle::VehicleType)type), ScriptVehicle::ERR_VEHICLE_BUILD_DISABLED);
 

	
 
	if (!ScriptObject::Command<CMD_BUILD_VEHICLE>::Do(&ScriptInstance::DoCommandReturnVehicleID, depot, engine_id, true, cargo, INVALID_CLIENT_ID)) return VEHICLE_INVALID;
 

	
 
	/* In case of test-mode, we return VehicleID 0 */
 
	return 0;
 
}
 

	
 
/* static */ VehicleID ScriptVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
src/station.cpp
Show inline comments
 
@@ -398,25 +398,25 @@ void Station::AddIndustryToDeliver(Indus
 
	if (pos != this->industries_near.end()) {
 
		if (pos->distance > distance) {
 
			auto node = this->industries_near.extract(pos);
 
			node.value().distance = distance;
 
			this->industries_near.insert(std::move(node));
 
		}
 
		return;
 
	}
 

	
 
	/* Include only industries that can accept cargo */
 
	uint cargo_index;
 
	for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
 
		if (ind->accepts_cargo[cargo_index] != CT_INVALID) break;
 
		if (IsValidCargoID(ind->accepts_cargo[cargo_index])) break;
 
	}
 
	if (cargo_index >= lengthof(ind->accepts_cargo)) return;
 

	
 
	this->industries_near.insert(IndustryListEntry{distance, ind});
 
}
 

	
 
/**
 
 * Remove nearby industry from station's industries_near list.
 
 * @param ind  Industry
 
 */
 
void Station::RemoveIndustryToDeliver(Industry *ind) {
 
	auto pos = std::find_if(this->industries_near.begin(), this->industries_near.end(), [&](const IndustryListEntry &e) { return e.industry->index == ind->index; });
src/station_cmd.cpp
Show inline comments
 
@@ -164,25 +164,25 @@ 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++) {
 
		/* 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 (ind->produced_cargo[i] != CT_INVALID &&
 
		if (IsValidCargoID(ind->produced_cargo[i]) &&
 
				(CargoSpec::Get(ind->produced_cargo[i])->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
 
@@ -525,25 +525,25 @@ CargoArray GetProductionAroundTiles(Tile
 
	}
 

	
 
	/* 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 (cargo != CT_INVALID) produced[cargo]++;
 
			if (IsValidCargoID(cargo)) produced[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/strings.cpp
Show inline comments
 
@@ -1194,27 +1194,27 @@ static char *FormatString(char *buff, co
 
					default: {
 
						StringParameters tmp_params(*args, 1);
 
						buff = GetStringWithArgs(buff, cargo_str, &tmp_params, last);
 
						break;
 
					}
 
				}
 
				break;
 
			}
 

	
 
			case SCC_CARGO_LONG: { // {CARGO_LONG}
 
				/* First parameter is cargo type, second parameter is cargo count */
 
				CargoID cargo = args->GetInt32(SCC_CARGO_LONG);
 
				if (cargo != CT_INVALID && cargo >= CargoSpec::GetArraySize()) break;
 
				if (IsValidCargoID(cargo) && cargo >= CargoSpec::GetArraySize()) break;
 

	
 
				StringID cargo_str = (cargo == CT_INVALID) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
 
				StringID cargo_str = !IsValidCargoID(cargo) ? STR_QUANTITY_N_A : CargoSpec::Get(cargo)->quantifier;
 
				StringParameters tmp_args(*args, 1);
 
				buff = GetStringWithArgs(buff, cargo_str, &tmp_args, last);
 
				break;
 
			}
 

	
 
			case SCC_CARGO_LIST: { // {CARGO_LIST}
 
				CargoTypes cmask = args->GetInt64(SCC_CARGO_LIST);
 
				bool first = true;
 

	
 
				for (const auto &cs : _sorted_cargo_specs) {
 
					if (!HasBit(cmask, cs->Index())) continue;
 

	
src/subsidy.cpp
Show inline comments
 
@@ -379,42 +379,42 @@ bool FindSubsidyIndustryCargoRoute()
 
	/* 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 (src_ind->produced_cargo[cargo_index] != CT_INVALID) num_cargos++;
 
		if (IsValidCargoID(src_ind->produced_cargo[cargo_index])) num_cargos++;
 
	}
 
	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 (src_ind->produced_cargo[cargo_index] != CT_INVALID) cargo_num--;
 
		if (IsValidCargoID(src_ind->produced_cargo[cargo_index])) 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];
 

	
 
	/* 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 ||
 
			cid == CT_INVALID ||
 
			!IsValidCargoID(cid) ||
 
			_settings_game.linkgraph.GetDistributionType(cid) != DT_MANUAL) {
 
		return false;
 
	}
 

	
 
	SourceID src = src_ind->index;
 

	
 
	return FindSubsidyCargoDestination(cid, src_type, src);
 
}
 

	
 
/**
 
 * Tries to find a suitable destination for the given source and cargo.
 
 * @param cid      Subsidized cargo.
src/town_cmd.cpp
Show inline comments
 
@@ -552,25 +552,25 @@ static void TileLoop_Town(TileIndex tile
 
	Town *t = Town::GetByTile(tile);
 
	uint32 r = Random();
 

	
 
	StationFinder stations(TileArea(tile, 1, 1));
 

	
 
	if (HasBit(hs->callback_mask, CBM_HOUSE_PRODUCE_CARGO)) {
 
		for (uint i = 0; i < 256; i++) {
 
			uint16 callback = GetHouseCallback(CBID_HOUSE_PRODUCE_CARGO, i, r, house_id, t, tile);
 

	
 
			if (callback == CALLBACK_FAILED || callback == CALLBACK_HOUSEPRODCARGO_END) break;
 

	
 
			CargoID cargo = GetCargoTranslation(GB(callback, 8, 7), hs->grf_prop.grffile);
 
			if (cargo == CT_INVALID) continue;
 
			if (!IsValidCargoID(cargo)) continue;
 

	
 
			uint amt = GB(callback, 0, 8);
 
			if (amt == 0) continue;
 

	
 
			uint moved = MoveGoodsToStation(cargo, amt, SourceType::Town, t->index, stations.GetStations());
 

	
 
			const CargoSpec *cs = CargoSpec::Get(cargo);
 
			t->supplied[cs->Index()].new_max += amt;
 
			t->supplied[cs->Index()].new_act += moved;
 
		}
 
	} else {
 
		switch (_settings_game.economy.town_cargogen_mode) {
 
@@ -697,40 +697,40 @@ static void AddProducedCargo_Town(TileIn
 
	HouseID house_id = GetHouseType(tile);
 
	const HouseSpec *hs = HouseSpec::Get(house_id);
 
	Town *t = Town::GetByTile(tile);
 

	
 
	if (HasBit(hs->callback_mask, CBM_HOUSE_PRODUCE_CARGO)) {
 
		for (uint i = 0; i < 256; i++) {
 
			uint16 callback = GetHouseCallback(CBID_HOUSE_PRODUCE_CARGO, i, 0, house_id, t, tile);
 

	
 
			if (callback == CALLBACK_FAILED || callback == CALLBACK_HOUSEPRODCARGO_END) break;
 

	
 
			CargoID cargo = GetCargoTranslation(GB(callback, 8, 7), hs->grf_prop.grffile);
 

	
 
			if (cargo == CT_INVALID) continue;
 
			if (!IsValidCargoID(cargo)) continue;
 
			produced[cargo]++;
 
		}
 
	} else {
 
		if (hs->population > 0) {
 
			produced[CT_PASSENGERS]++;
 
		}
 
		if (hs->mail_generation > 0) {
 
			produced[CT_MAIL]++;
 
		}
 
	}
 
}
 

	
 
static inline void AddAcceptedCargoSetMask(CargoID cargo, uint amount, CargoArray &acceptance, CargoTypes *always_accepted)
 
{
 
	if (cargo == CT_INVALID || amount == 0) return;
 
	if (!IsValidCargoID(cargo) || amount == 0) return;
 
	acceptance[cargo] += amount;
 
	SetBit(*always_accepted, cargo);
 
}
 

	
 
static void AddAcceptedCargo_Town(TileIndex tile, CargoArray &acceptance, CargoTypes *always_accepted)
 
{
 
	const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
 
	CargoID accepts[lengthof(hs->accepts_cargo)];
 

	
 
	/* Set the initial accepted cargo types */
 
	for (uint8 i = 0; i < lengthof(accepts); i++) {
 
		accepts[i] = hs->accepts_cargo[i];
src/train_gui.cpp
Show inline comments
 
@@ -200,25 +200,25 @@ static CargoSummary _cargo_summary;
 
 * @param y     The y coordinate
 
 */
 
static void TrainDetailsCargoTab(const CargoSummaryItem *item, int left, int right, int y)
 
{
 
	StringID str;
 
	if (item->amount > 0) {
 
		SetDParam(0, item->cargo);
 
		SetDParam(1, item->amount);
 
		SetDParam(2, item->source);
 
		SetDParam(3, _settings_game.vehicle.freight_trains);
 
		str = FreightWagonMult(item->cargo) > 1 ? STR_VEHICLE_DETAILS_CARGO_FROM_MULT : STR_VEHICLE_DETAILS_CARGO_FROM;
 
	} else {
 
		str = item->cargo == INVALID_CARGO ? STR_QUANTITY_N_A : STR_VEHICLE_DETAILS_CARGO_EMPTY;
 
		str = !IsValidCargoID(item->cargo) ? STR_QUANTITY_N_A : STR_VEHICLE_DETAILS_CARGO_EMPTY;
 
	}
 

	
 
	DrawString(left, right, y, str, TC_LIGHT_BLUE);
 
}
 

	
 
/**
 
 * Draw the details info tab for the given vehicle at the given position
 
 *
 
 * @param v     current vehicle
 
 * @param left  The left most coordinate to draw
 
 * @param right The right most coordinate to draw
 
 * @param y     The y coordinate
 
@@ -239,25 +239,25 @@ static void TrainDetailsInfoTab(const Ve
 

	
 
/**
 
 * Draw the details capacity tab for the given vehicle at the given position
 
 *
 
 * @param item  Data to draw
 
 * @param left  The left most coordinate to draw
 
 * @param right The right most coordinate to draw
 
 * @param y     The y coordinate
 
 */
 
static void TrainDetailsCapacityTab(const CargoSummaryItem *item, int left, int right, int y)
 
{
 
	StringID str;
 
	if (item->cargo != INVALID_CARGO) {
 
	if (IsValidCargoID(item->cargo)) {
 
		SetDParam(0, item->cargo);
 
		SetDParam(1, item->capacity);
 
		SetDParam(4, item->subtype);
 
		SetDParam(5, _settings_game.vehicle.freight_trains);
 
		str = FreightWagonMult(item->cargo) > 1 ? STR_VEHICLE_INFO_CAPACITY_MULT : STR_VEHICLE_INFO_CAPACITY;
 
	} else {
 
		/* Draw subtype only */
 
		SetDParam(0, item->subtype);
 
		str = STR_VEHICLE_INFO_NO_CAPACITY;
 
	}
 
	DrawString(left, right, y, str);
 
}
 
@@ -267,25 +267,25 @@ static void TrainDetailsCapacityTab(cons
 
 * @param v Vehicle to process
 
 * @param summary Space for the result
 
 */
 
static void GetCargoSummaryOfArticulatedVehicle(const Train *v, CargoSummary *summary)
 
{
 
	summary->clear();
 
	do {
 
		if (!v->GetEngine()->CanCarryCargo()) continue;
 

	
 
		CargoSummaryItem new_item;
 
		new_item.cargo = v->cargo_cap > 0 ? v->cargo_type : INVALID_CARGO;
 
		new_item.subtype = GetCargoSubtypeText(v);
 
		if (new_item.cargo == INVALID_CARGO && new_item.subtype == STR_EMPTY) continue;
 
		if (!IsValidCargoID(new_item.cargo) && new_item.subtype == STR_EMPTY) continue;
 

	
 
		auto item = std::find(summary->begin(), summary->end(), new_item);
 
		if (item == summary->end()) {
 
			summary->emplace_back();
 
			item = summary->end() - 1;
 
			item->cargo = new_item.cargo;
 
			item->subtype = new_item.subtype;
 
			item->capacity = 0;
 
			item->amount = 0;
 
			item->source = INVALID_STATION;
 
		}
 

	
src/vehicle.cpp
Show inline comments
 
@@ -223,25 +223,25 @@ bool Vehicle::NeedsServicing() const
 
		if (replace_when_old && !v->NeedsAutorenewing(c, false)) continue;
 

	
 
		/* Check refittability */
 
		CargoTypes available_cargo_types, union_mask;
 
		GetArticulatedRefitMasks(new_engine, true, &union_mask, &available_cargo_types);
 
		/* Is there anything to refit? */
 
		if (union_mask != 0) {
 
			CargoID cargo_type;
 
			/* We cannot refit to mixed cargoes in an automated way */
 
			if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue;
 

	
 
			/* Did the old vehicle carry anything? */
 
			if (cargo_type != CT_INVALID) {
 
			if (IsValidCargoID(cargo_type)) {
 
				/* We can't refit the vehicle to carry the cargo we want */
 
				if (!HasBit(available_cargo_types, cargo_type)) continue;
 
			}
 
		}
 

	
 
		/* Check money.
 
		 * We want 2*(the price of the new vehicle) without looking at the value of the vehicle we are going to sell. */
 
		pending_replace = true;
 
		needed_money += 2 * Engine::Get(new_engine)->GetCost();
 
		if (needed_money > c->money) return false;
 
	}
 

	
 
@@ -1875,26 +1875,26 @@ LiveryScheme GetEngineLiveryScheme(Engin
 
	const Engine *e = Engine::Get(engine_type);
 
	switch (e->type) {
 
		default: NOT_REACHED();
 
		case VEH_TRAIN:
 
			if (v != nullptr && parent_engine_type != INVALID_ENGINE && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) {
 
				/* Wagonoverrides use the colour scheme of the front engine.
 
				 * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
 
				engine_type = parent_engine_type;
 
				e = Engine::Get(engine_type);
 
				/* Note: Luckily cargo_type is not needed for engines */
 
			}
 

	
 
			if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
 
			if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 
			if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType();
 
			if (!IsValidCargoID(cargo_type)) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 
			if (e->u.rail.railveh_type == RAILVEH_WAGON) {
 
				if (!CargoSpec::Get(cargo_type)->is_freight) {
 
					if (parent_engine_type == INVALID_ENGINE) {
 
						return LS_PASSENGER_WAGON_STEAM;
 
					} else {
 
						bool is_mu = HasBit(EngInfo(parent_engine_type)->misc_flags, EF_RAIL_IS_MU);
 
						switch (RailVehInfo(parent_engine_type)->engclass) {
 
							default: NOT_REACHED();
 
							case EC_STEAM:    return LS_PASSENGER_WAGON_STEAM;
 
							case EC_DIESEL:   return is_mu ? LS_DMU : LS_PASSENGER_WAGON_DIESEL;
 
							case EC_ELECTRIC: return is_mu ? LS_EMU : LS_PASSENGER_WAGON_ELECTRIC;
 
							case EC_MONORAIL: return LS_PASSENGER_WAGON_MONORAIL;
 
@@ -1915,39 +1915,39 @@ LiveryScheme GetEngineLiveryScheme(Engin
 
					case EC_MONORAIL: return LS_MONORAIL;
 
					case EC_MAGLEV:   return LS_MAGLEV;
 
				}
 
			}
 

	
 
		case VEH_ROAD:
 
			/* Always use the livery of the front */
 
			if (v != nullptr && parent_engine_type != INVALID_ENGINE) {
 
				engine_type = parent_engine_type;
 
				e = Engine::Get(engine_type);
 
				cargo_type = v->First()->cargo_type;
 
			}
 
			if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
 
			if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 
			if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType();
 
			if (!IsValidCargoID(cargo_type)) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 

	
 
			/* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
 
			if (HasBit(e->info.misc_flags, EF_ROAD_TRAM)) {
 
				/* Tram */
 
				return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_TRAM : LS_FREIGHT_TRAM;
 
			} else {
 
				/* Bus or truck */
 
				return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_BUS : LS_TRUCK;
 
			}
 

	
 
		case VEH_SHIP:
 
			if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
 
			if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 
			if (!IsValidCargoID(cargo_type)) cargo_type = e->GetDefaultCargoType();
 
			if (!IsValidCargoID(cargo_type)) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 
			return IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
 

	
 
		case VEH_AIRCRAFT:
 
			switch (e->u.air.subtype) {
 
				case AIR_HELI: return LS_HELICOPTER;
 
				case AIR_CTOL: return LS_SMALL_PLANE;
 
				case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
 
				default: NOT_REACHED();
 
			}
 
	}
 
}
 

	
src/vehicle_cmd.cpp
Show inline comments
 
@@ -86,34 +86,34 @@ const StringID _send_to_depot_msg_table[
 
 */
 
std::tuple<CommandCost, VehicleID, uint, uint16, CargoArray> CmdBuildVehicle(DoCommandFlag flags, TileIndex tile, EngineID eid, bool use_free_vehicles, CargoID cargo, ClientID client_id)
 
{
 
	/* Elementary check for valid location. */
 
	if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
 

	
 
	VehicleType type = GetDepotVehicleType(tile);
 

	
 
	/* Validate the engine type. */
 
	if (!IsEngineBuildable(eid, type, _current_company)) return { CommandCost(STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE + type), INVALID_VEHICLE, 0, 0, {} };
 

	
 
	/* Validate the cargo type. */
 
	if (cargo >= NUM_CARGO && cargo != CT_INVALID) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
 
	if (cargo >= NUM_CARGO && IsValidCargoID(cargo)) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
 

	
 
	const Engine *e = Engine::Get(eid);
 
	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
 

	
 
	/* Engines without valid cargo should not be available */
 
	CargoID default_cargo = e->GetDefaultCargoType();
 
	if (default_cargo == CT_INVALID) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
 
	if (!IsValidCargoID(default_cargo)) return { CMD_ERROR, INVALID_VEHICLE, 0, 0, {} };
 

	
 
	bool refitting = cargo != CT_INVALID && cargo != default_cargo;
 
	bool refitting = IsValidCargoID(cargo) && cargo != default_cargo;
 

	
 
	/* Check whether the number of vehicles we need to build can be built according to pool space. */
 
	uint num_vehicles;
 
	switch (type) {
 
		case VEH_TRAIN:    num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break;
 
		case VEH_ROAD:     num_vehicles = 1 + CountArticulatedParts(eid, false); break;
 
		case VEH_SHIP:     num_vehicles = 1; break;
 
		case VEH_AIRCRAFT: num_vehicles = e->u.air.subtype & AIR_CTOL ? 2 : 3; break;
 
		default: NOT_REACHED(); // Safe due to IsDepotTile()
 
	}
 
	if (!Vehicle::CanAllocateItem(num_vehicles)) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), INVALID_VEHICLE, 0, 0, {} };
 

	
 
@@ -942,25 +942,25 @@ std::tuple<CommandCost, VehicleID> CmdCl
 
					if (cost.Succeeded()) total_cost.AddCost(cost);
 
				}
 

	
 
				if (w->IsGroundVehicle() && w->HasArticulatedPart()) {
 
					w = w->GetNextArticulatedPart();
 
				} else {
 
					break;
 
				}
 
			} else {
 
				const Engine *e = v->GetEngine();
 
				CargoID initial_cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
 

	
 
				if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) {
 
				if (v->cargo_type != initial_cargo && IsValidCargoID(initial_cargo)) {
 
					bool dummy;
 
					total_cost.AddCost(GetRefitCost(nullptr, v->engine_type, v->cargo_type, v->cargo_subtype, &dummy));
 
				}
 
			}
 

	
 
			if (v->IsGroundVehicle() && v->HasArticulatedPart()) {
 
				v = v->GetNextArticulatedPart();
 
			} else {
 
				break;
 
			}
 
		} while (v != nullptr);
 

	
0 comments (0 inline, 0 general)