Changeset - r24482:081733badf2c
[Not reviewed]
master
0 11 0
Pavel Stupnikov - 3 years ago 2020-12-22 13:29:48
dp@dpointer.org
Feature: Set exclusive access to industry from GS (#8115)
11 files changed with 137 insertions and 5 deletions:
0 comments (0 inline, 0 general)
src/economy.cpp
Show inline comments
 
@@ -1021,48 +1021,50 @@ static uint DeliverGoodsToIndustry(const
 
	/* Find the nearest industrytile to the station sign inside the catchment area, whose industry accepts the cargo.
 
	 * This fails in three cases:
 
	 *  1) The station accepts the cargo because there are enough houses around it accepting the cargo.
 
	 *  2) The industries in the catchment area temporarily reject the cargo, and the daily station loop has not yet updated station acceptance.
 
	 *  3) The results of callbacks CBID_INDUSTRY_REFUSE_CARGO and CBID_INDTILE_CARGO_ACCEPTANCE are inconsistent. (documented behaviour)
 
	 */
 

	
 
	uint accepted = 0;
 

	
 
	for (Industry *ind : st->industries_near) {
 
		if (num_pieces == 0) break;
 

	
 
		if (ind->index == source) continue;
 

	
 
		uint cargo_index;
 
		for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
 
			if (cargo_type == ind->accepts_cargo[cargo_index]) break;
 
		}
 
		/* Check if matching cargo has been found */
 
		if (cargo_index >= lengthof(ind->accepts_cargo)) continue;
 

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

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

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

	
 
		uint amount = min(num_pieces, 0xFFFFU - ind->incoming_cargo_waiting[cargo_index]);
 
		ind->incoming_cargo_waiting[cargo_index] += amount;
 
		ind->last_cargo_accepted_at[cargo_index] = _date;
 
		num_pieces -= amount;
 
		accepted += amount;
 

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

	
 
	return accepted;
 
}
 

	
 
/**
 
 * Delivers goods to industries/towns and calculates the payment
 
 * @param num_pieces amount of cargo delivered
 
 * @param cargo_type the type of cargo that is delivered
 
 * @param dest Station the cargo has been unloaded
 
 * @param source_tile The origin of the cargo for distance calculation
 
 * @param days_in_transit Travel time
 
 * @param company The company delivering the cargo
src/economy_func.h
Show inline comments
 
@@ -8,45 +8,45 @@
 
/** @file economy_func.h Functions related to the economy. */
 

	
 
#ifndef ECONOMY_FUNC_H
 
#define ECONOMY_FUNC_H
 

	
 
#include "economy_type.h"
 
#include "station_type.h"
 
#include "cargo_type.h"
 
#include "vehicle_type.h"
 
#include "company_type.h"
 

	
 
void ResetPriceBaseMultipliers();
 
void SetPriceBaseMultiplier(Price price, int factor);
 

	
 
extern const ScoreInfo _score_info[];
 
extern int64 _score_part[MAX_COMPANIES][SCORE_END];
 
extern Economy _economy;
 
/* Prices and also the fractional part. */
 
extern Prices _price;
 

	
 
int UpdateCompanyRatingAndValue(Company *c, bool update);
 
void StartupIndustryDailyChanges(bool init_counter);
 

	
 
Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
 
uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations);
 
uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations, Owner exclusivity = INVALID_OWNER);
 

	
 
void PrepareUnload(Vehicle *front_v);
 
void LoadUnloadStation(Station *st);
 

	
 
Money GetPrice(Price index, uint cost_factor, const struct GRFFile *grf_file, int shift = 0);
 

	
 
void InitializeEconomy();
 
void RecomputePrices();
 
bool AddInflation(bool check_year = true);
 

	
 
/**
 
 * Is the economy in recession?
 
 * @return \c True if economy is in recession, \c false otherwise.
 
 */
 
static inline bool EconomyIsInRecession()
 
{
 
	return _economy.fluct <= 0;
 
}
 

	
 
#endif /* ECONOMY_FUNC_H */
src/industry.h
Show inline comments
 
@@ -69,48 +69,50 @@ struct Industry : IndustryPool::PoolItem
 
	CargoID accepts_cargo[INDUSTRY_NUM_INPUTS];            ///< 16 input cargo slots
 
	uint16 this_month_production[INDUSTRY_NUM_OUTPUTS];    ///< stats of this month's production per cargo
 
	uint16 this_month_transported[INDUSTRY_NUM_OUTPUTS];   ///< stats of this month's transport per cargo
 
	byte last_month_pct_transported[INDUSTRY_NUM_OUTPUTS]; ///< percentage transported per cargo in the last full month
 
	uint16 last_month_production[INDUSTRY_NUM_OUTPUTS];    ///< total units produced per cargo in the last full month
 
	uint16 last_month_transported[INDUSTRY_NUM_OUTPUTS];   ///< total units transported per cargo in the last full month
 
	uint16 counter;                                        ///< used for animation and/or production (if available cargo)
 

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

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

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

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

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

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

	
 
	void RecomputeProductionMultipliers();
 

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

	
 
	inline int GetCargoProducedIndex(CargoID cargo) const
 
	{
 
		if (cargo == CT_INVALID) return -1;
 
		const CargoID *pos = std::find(this->produced_cargo, endof(this->produced_cargo), cargo);
src/industry_cmd.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file industry_cmd.cpp Handling of industry tiles. */
 

	
 
#include "stdafx.h"
 
#include "clear_map.h"
 
#include "industry.h"
 
#include "station_base.h"
 
#include "landscape.h"
 
#include "viewport_func.h"
 
#include "command_func.h"
 
#include "town.h"
 
#include "news_func.h"
 
#include "cheat_type.h"
 
#include "company_base.h"
 
#include "genworld.h"
 
#include "tree_map.h"
 
#include "newgrf_cargo.h"
 
#include "newgrf_debug.h"
 
#include "newgrf_industrytiles.h"
 
#include "autoslope.h"
 
#include "water.h"
 
#include "strings_func.h"
 
#include "window_func.h"
 
#include "date_func.h"
 
#include "vehicle_func.h"
 
#include "sound_func.h"
 
#include "animated_tile_func.h"
 
#include "effectvehicle_func.h"
 
#include "effectvehicle_base.h"
 
#include "ai/ai.hpp"
 
#include "core/pool_func.hpp"
 
#include "subsidy_func.h"
 
#include "core/backup_type.hpp"
 
#include "object_base.h"
 
#include "game/game.hpp"
 
#include "error.h"
 

	
 
#include "table/strings.h"
 
@@ -518,49 +519,49 @@ static CommandCost ClearTile_Industry(Ti
 
	return CommandCost(EXPENSES_CONSTRUCTION, indspec->GetRemovalCost());
 
}
 

	
 
/**
 
 * 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 = min(i->produced_cargo_waiting[j], 255);
 
		if (cw > indspec->minimal_cargo && i->produced_cargo[j] != CT_INVALID) {
 
			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, ST_INDUSTRY, i->index, &i->stations_near);
 
			uint am = MoveGoodsToStation(i->produced_cargo[j], cw, ST_INDUSTRY, i->index, &i->stations_near, i->exclusive_consumer);
 
			i->this_month_transported[j] += am;
 

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

	
 
	return moved_cargo;
 
}
 

	
 

	
 
static void AnimateTile_Industry(TileIndex tile)
 
{
 
	IndustryGfx gfx = GetIndustryGfx(tile);
 

	
 
	if (GetIndustryTileSpec(gfx)->animation.status != ANIM_STATUS_NO_ANIMATION) {
 
		AnimateNewIndustryTile(tile);
 
		return;
 
	}
 

	
 
	switch (gfx) {
 
	case GFX_SUGAR_MINE_SIEVE:
 
		if ((_tick_counter & 1) == 0) {
 
			byte m = GetAnimationFrame(tile) + 1;
 

	
 
@@ -925,48 +926,51 @@ static void TileLoop_Industry(TileIndex 
 
		break;
 

	
 
	case GFX_SUGAR_MINE_SIEVE:
 
		if (Chance16(1, 3)) AddAnimatedTile(tile);
 
		break;
 
	}
 
}
 

	
 
static bool ClickTile_Industry(TileIndex tile)
 
{
 
	ShowIndustryViewWindow(GetIndustryIndex(tile));
 
	return true;
 
}
 

	
 
static TrackStatus GetTileTrackStatus_Industry(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
 
{
 
	return 0;
 
}
 

	
 
static void ChangeTileOwner_Industry(TileIndex tile, Owner old_owner, Owner new_owner)
 
{
 
	/* If the founder merges, the industry was created by the merged company */
 
	Industry *i = Industry::GetByTile(tile);
 
	if (i->founder == old_owner) i->founder = (new_owner == INVALID_OWNER) ? OWNER_NONE : new_owner;
 

	
 
	if (i->exclusive_supplier == old_owner) i->exclusive_supplier = new_owner;
 
	if (i->exclusive_consumer == old_owner) i->exclusive_consumer = new_owner;
 
}
 

	
 
/**
 
 * Check whether the tile is a forest.
 
 * @param tile the tile to investigate.
 
 * @return true if and only if the tile is a forest
 
 */
 
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;
 
	}
 

	
 
	return false;
 
@@ -1743,48 +1747,51 @@ static void DoCreateNewIndustry(Industry
 
		}
 
	}
 

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

	
 
	uint16 r = Random();
 
	i->random_colour = GB(r, 0, 4);
 
	i->counter = GB(r, 4, 12);
 
	i->random = initial_random_bits;
 
	i->was_cargo_delivered = false;
 
	i->last_prod_year = _cur_year;
 
	i->founder = founder;
 
	i->ctlflags = INDCTL_NONE;
 

	
 
	i->construction_date = _date;
 
	i->construction_type = (_game_mode == GM_EDITOR) ? ICT_SCENARIO_EDITOR :
 
			(_generating_world ? ICT_MAP_GENERATION : ICT_NORMAL_GAMEPLAY);
 

	
 
	/* Adding 1 here makes it conform to specs of var44 of varaction2 for industries
 
	 * 0 = created prior of newindustries
 
	 * else, chosen layout + 1 */
 
	i->selected_layout = (byte)(layout_index + 1);
 

	
 
	i->exclusive_supplier = INVALID_OWNER;
 
	i->exclusive_consumer = INVALID_OWNER;
 

	
 
	i->prod_level = PRODLEVEL_DEFAULT;
 

	
 
	/* Call callbacks after the regular fields got initialised. */
 

	
 
	if (HasBit(indspec->callback_mask, CBM_IND_PROD_CHANGE_BUILD)) {
 
		uint16 res = GetIndustryCallback(CBID_INDUSTRY_PROD_CHANGE_BUILD, 0, Random(), i, type, INVALID_TILE);
 
		if (res != CALLBACK_FAILED) {
 
			if (res < PRODLEVEL_MINIMUM || res > PRODLEVEL_MAXIMUM) {
 
				ErrorUnknownCallbackResult(indspec->grf_prop.grffile->grfid, CBID_INDUSTRY_PROD_CHANGE_BUILD, res);
 
			} else {
 
				i->prod_level = res;
 
				i->RecomputeProductionMultipliers();
 
			}
 
		}
 
	}
 

	
 
	if (_generating_world) {
 
		if (HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) {
 
			IndustryProductionCallback(i, 1);
 
			for (size_t ci = 0; ci < lengthof(i->last_month_production); ci++) {
 
				i->last_month_production[ci] = i->produced_cargo_waiting[ci] * 8;
 
				i->produced_cargo_waiting[ci] = 0;
 
			}
 
		}
 
@@ -2037,71 +2044,94 @@ CommandCost CmdBuildIndustry(TileIndex t
 
			layout = (layout + 1) % num_layouts;
 
			ret = CreateNewIndustryHelper(tile, it, flags, indspec, layout, random_var8f, random_initial_bits, _current_company, _current_company == OWNER_DEITY ? IACT_RANDOMCREATION : IACT_USERCREATION, &ind);
 
			if (ret.Succeeded()) break;
 
		}
 

	
 
		/* If it still failed, there's no suitable layout to build here, return the error */
 
		if (ret.Failed()) return ret;
 
	}
 

	
 
	if ((flags & DC_EXEC) && ind != nullptr && _game_mode != GM_EDITOR) {
 
		AdvertiseIndustryOpening(ind);
 
	}
 

	
 
	return CommandCost(EXPENSES_OTHER, indspec->GetConstructionCost());
 
}
 

	
 
/**
 
 * Change industry properties
 
 * @param tile Unused.
 
 * @param flags Type of operation.
 
 * @param p1 IndustryID
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit 0 - 7) - action to perform:
 
 *                      0 = set control flags
 
 *                      1 = set exclusive supplier
 
 *                      2 = set exclusive consumer
 
 * - p2 = (bit 8 - 15) - IndustryControlFlags
 
 *                       (only used with set control flags)
 
 * - p2 = (bit 16 - 23) - CompanyID to set or INVALID_OWNER (available to everyone) or
 
 *                        OWNER_NONE (neutral stations only) or OWNER_DEITY (no one)
 
 *                        (only used with set exclusive supplier / consumer)
 
 * @param text unused
 
 * @return Empty cost or an error.
 
 */
 
CommandCost CmdIndustryCtrl(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	if (_current_company != OWNER_DEITY) return CMD_ERROR;
 

	
 
	Industry *ind = Industry::GetIfValid(p1);
 
	if (ind == nullptr) return CMD_ERROR;
 

	
 
	uint8 action = GB(p2, 0, 8);
 

	
 
	switch (action) {
 
		case 0: {
 
			IndustryControlFlags ctlflags = (IndustryControlFlags)GB(p2, 8, 8) & INDCTL_MASK;
 

	
 
			if (flags & DC_EXEC) ind->ctlflags = ctlflags;
 

	
 
			break;
 
		}
 

	
 
		case 1:
 
		case 2: {
 
			Owner company_id = (Owner)GB(p2, 16, 8);
 

	
 
			if (company_id != OWNER_NONE && company_id != INVALID_OWNER && company_id != OWNER_DEITY
 
				&& !Company::IsValidID(company_id)) return CMD_ERROR;
 

	
 
			if (flags & DC_EXEC) {
 
				if (action == 1) {
 
					ind->exclusive_supplier = company_id;
 
				} else {
 
					ind->exclusive_consumer = company_id;
 
				}
 
			}
 

	
 
			break;
 
		}
 

	
 
		default:
 
			NOT_REACHED();
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Create a new industry of random layout.
 
 * @param tile The location to build the industry.
 
 * @param type The industry type to build.
 
 * @param creation_type The circumstances the industry is created under.
 
 * @return the created industry or nullptr if it failed.
 
 */
 
static Industry *CreateNewIndustry(TileIndex tile, IndustryType type, IndustryAvailabilityCallType creation_type)
 
{
 
	const IndustrySpec *indspec = GetIndustrySpec(type);
 

	
 
	uint32 seed = Random();
 
	uint32 seed2 = Random();
 
	Industry *i = nullptr;
 
	size_t layout_index = RandomRange((uint32)indspec->layouts.size());
 
	CommandCost ret = CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, layout_index, seed, GB(seed2, 0, 16), OWNER_NONE, creation_type, &i);
 
	assert(i != nullptr || ret.Failed());
src/saveload/afterload.cpp
Show inline comments
 
@@ -3104,48 +3104,56 @@ bool AfterLoadGame()
 
			}
 
			/* Add docks and oilrigs to Station::ship_station. */
 
			if (IsTileType(t, MP_STATION)) {
 
				if (IsDock(t) || IsOilRig(t)) Station::GetByTile(t)->ship_station.Add(t);
 
			}
 
		}
 
	}
 

	
 
	if (IsSavegameVersionUntil(SLV_ENDING_YEAR)) {
 
		/* Update station docking tiles. Was only needed for pre-SLV_MULTITLE_DOCKS
 
		 * savegames, but a bug in docking tiles touched all savegames between
 
		 * SLV_MULTITILE_DOCKS and SLV_ENDING_YEAR. */
 
		for (Station *st : Station::Iterate()) {
 
			if (st->ship_station.tile != INVALID_TILE) UpdateStationDockingTiles(st);
 
		}
 

	
 
		/* Reset roadtype/streetcartype info for non-road bridges. */
 
		for (TileIndex t = 0; t < map_size; t++) {
 
			if (IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) != TRANSPORT_ROAD) {
 
				SetRoadTypes(t, INVALID_ROADTYPE, INVALID_ROADTYPE);
 
			}
 
		}
 
	}
 

	
 
	/* Make sure all industries exclusive supplier/consumer set correctly. */
 
	if (IsSavegameVersionBefore(SLV_GS_INDUSTRY_CONTROL)) {
 
		for (Industry *i : Industry::Iterate()) {
 
			i->exclusive_supplier = INVALID_OWNER;
 
			i->exclusive_consumer = INVALID_OWNER;
 
		}
 
	}
 

	
 
	/* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */
 
	Station::RecomputeCatchmentForAll();
 

	
 
	/* Station acceptance is some kind of cache */
 
	if (IsSavegameVersionBefore(SLV_127)) {
 
		for (Station *st : Station::Iterate()) UpdateStationAcceptance(st, false);
 
	}
 

	
 
	/* Road stops is 'only' updating some caches */
 
	AfterLoadRoadStops();
 
	AfterLoadLabelMaps();
 
	AfterLoadCompanyStats();
 
	AfterLoadStoryBook();
 

	
 
	GamelogPrintDebug(1);
 

	
 
	InitializeWindowsAndCaches();
 
	/* Restore the signals */
 
	ResetSignalHandlers();
 

	
 
	AfterLoadLinkGraphs();
 
	return true;
 
}
 

	
src/saveload/industry_sl.cpp
Show inline comments
 
@@ -43,48 +43,50 @@ static const SaveLoad _industry_desc[] =
 
	SLE_CONDARR(Industry, this_month_transported,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8,   2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_month_pct_transported, SLE_UINT8,  16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, last_month_production,      SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_month_production,      SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDARR(Industry, last_month_transported,     SLE_UINT16,  2,               SL_MIN_VERSION, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_month_transported,     SLE_UINT16, 16,             SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 

	
 
	    SLE_VAR(Industry, counter,                    SLE_UINT16),
 

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

	
 
	SLE_CONDVAR(Industry, founder,                    SLE_UINT8,                 SLV_70, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, construction_date,          SLE_INT32,                 SLV_70, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, construction_type,          SLE_UINT8,                 SLV_70, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, last_cargo_accepted_at[0],  SLE_INT32,                 SLV_70, SLV_EXTEND_INDUSTRY_CARGO_SLOTS),
 
	SLE_CONDARR(Industry, last_cargo_accepted_at,     SLE_INT32, 16,            SLV_EXTEND_INDUSTRY_CARGO_SLOTS, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, selected_layout,            SLE_UINT8,                 SLV_73, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, exclusive_supplier,         SLE_UINT8,                 SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
 
	SLE_CONDVAR(Industry, exclusive_consumer,         SLE_UINT8,                 SLV_GS_INDUSTRY_CONTROL, SL_MAX_VERSION),
 

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

	
 
	SLE_CONDNULL(1, SLV_82, SLV_197), // random_triggers
 
	SLE_CONDVAR(Industry, random,                     SLE_UINT16,                SLV_82, SL_MAX_VERSION),
 

	
 
	SLE_CONDNULL(32, SLV_2, SLV_144), // old reserved space
 

	
 
	SLE_END()
 
};
 

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

	
 
static void Save_IIDS()
 
{
 
	Save_NewGRFMapping(_industry_mngr);
src/saveload/saveload.h
Show inline comments
 
@@ -299,49 +299,49 @@ enum SaveLoadVersion : uint16 {
 
	SLV_ROAD_TYPES,                         ///< 214  PR#6811 NewGRF road types.
 

	
 
	SLV_SCRIPT_MEMLIMIT,                    ///< 215  PR#7516 Limit on AI/GS memory consumption.
 
	SLV_MULTITILE_DOCKS,                    ///< 216  PR#7380 Multiple docks per station.
 
	SLV_TRADING_AGE,                        ///< 217  PR#7780 Configurable company trading age.
 
	SLV_ENDING_YEAR,                        ///< 218  PR#7747 v1.10 Configurable ending year.
 
	SLV_REMOVE_TOWN_CARGO_CACHE,            ///< 219  PR#8258 Remove town cargo acceptance and production caches.
 

	
 
	/* Patchpacks for a while considered it a good idea to jump a few versions
 
	 * above our version for their savegames. But as time continued, this gap
 
	 * has been closing, up to the point we would start to reuse versions from
 
	 * their patchpacks. This is not a problem from our perspective: the
 
	 * savegame will simply fail to load because they all contain chunks we
 
	 * cannot digest. But, this gives for ugly errors. As we have plenty of
 
	 * versions anyway, we simply skip the versions we know belong to
 
	 * patchpacks. This way we can present the user with a clean error
 
	 * indicate he is loading a savegame from a patchpack.
 
	 * For future patchpack creators: please follow a system like JGRPP, where
 
	 * the version is masked with 0x8000, and the true version is stored in
 
	 * its own chunk with feature toggles.
 
	 */
 
	SLV_START_PATCHPACKS,                   ///< 220  First known patchpack to use a version just above ours.
 
	SLV_END_PATCHPACKS = 286,               ///< 286  Last known patchpack to use a version just above ours.
 

	
 
	SLV_GS_INDUSTRY_CONTROL,                ///< 287  PR#7912 GS industry control.
 
	SLV_GS_INDUSTRY_CONTROL,                ///< 287  PR#7912 and PR#8115 GS industry control.
 

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

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

	
 
/** Deals with the type of the savegame, independent of extension */
 
struct FileToSaveLoad {
 
	SaveLoadOperation file_op;           ///< File operation to perform.
 
	DetailedFileType detail_ftype;   ///< Concrete file type (PNG, BMP, old save, etc).
 
	AbstractFileType abstract_ftype; ///< Abstract type of file (scenario, heightmap, etc).
 
	char name[MAX_PATH];             ///< Name of the file.
 
	char title[255];                 ///< Internal name of the game.
 

	
 
	void SetMode(FiosType ft);
 
	void SetMode(SaveLoadOperation fop, AbstractFileType aft, DetailedFileType dft);
 
	void SetName(const char *name);
 
	void SetTitle(const char *title);
 
};
src/script/api/game_changelog.hpp
Show inline comments
 
@@ -2,50 +2,54 @@
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/**
 
 * @file game_changelog.hpp Lists all changes / additions to the API.
 
 *
 
 * Only new / renamed / deleted api functions will be listed here. A list of
 
 * bug fixes can be found in the normal changelog. Note that removed API
 
 * functions may still be available if you return an older API version
 
 * in GetAPIVersion() in info.nut.
 
 *
 
 * \b 1.11.0
 
 *
 
 * This version is not yet released. The following changes are not set in stone yet.
 
 *
 
 * API additions:
 
 * \li GSEventStoryPageButtonClick
 
 * \li GSEventStoryPageTileSelect
 
 * \li GSEventStoryPageVehicleSelect
 
 * \li GSIndustry::GetCargoLastAcceptedDate
 
 * \li GSIndustry::GetControlFlags
 
 * \li GSIndustry::GetExclusiveConsumer
 
 * \li GSIndustry::GetExclusiveSupplier
 
 * \li GSIndustry::GetLastProductionYear
 
 * \li GSIndustry::SetControlFlags
 
 * \li GSIndustry::SetExclusiveConsumer
 
 * \li GSIndustry::SetExclusiveSupplier
 
 * \li GSStoryPage::MakePushButtonReference
 
 * \li GSStoryPage::MakeTileButtonReference
 
 * \li GSStoryPage::MakeVehicleButtonReference
 
 * \li GSPriorityQueue
 
 *
 
 * \b 1.10.0
 
 *
 
 * API additions:
 
 * \li GSVehicle::BuildVehicleWithRefit
 
 * \li GSVehicle::GetBuildWithRefitCapacity
 
 * \li GSRoad::GetName
 
 * \li GSRoad::RoadVehCanRunOnRoad
 
 * \li GSRoad::RoadVehHasPowerOnRoad
 
 * \li GSRoad::ConvertRoadType
 
 * \li GSRoad::GetMaxSpeed
 
 * \li GSEngine::EnableForCompany
 
 * \li GSEngine::DisableForCompany
 
 *
 
 * \b 1.9.0
 
 *
 
 * API additions:
 
 * \li GSAirport::GetMonthlyMaintenanceCost
 
 * \li GSClient
 
 * \li GSClientList
src/script/api/script_industry.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file script_industry.cpp Implementation of ScriptIndustry. */
 

	
 
#include "../../stdafx.h"
 
#include "script_industry.hpp"
 
#include "script_cargo.hpp"
 
#include "script_company.hpp"
 
#include "script_error.hpp"
 
#include "script_map.hpp"
 
#include "../../company_base.h"
 
#include "../../industry.h"
 
#include "../../strings_func.h"
 
#include "../../station_base.h"
 
#include "../../newgrf_industries.h"
 
#include "table/strings.h"
 
#include <numeric>
 

	
 
#include "../../safeguards.h"
 

	
 
/* static */ int32 ScriptIndustry::GetIndustryCount()
 
{
 
	return (int32)::Industry::GetNumItems();
 
}
 

	
 
/* static */ bool ScriptIndustry::IsValidIndustry(IndustryID industry_id)
 
{
 
	return ::Industry::IsValidID(industry_id);
 
}
 

	
 
/* static */ IndustryID ScriptIndustry::GetIndustryID(TileIndex tile)
 
{
 
	if (!::IsValidTile(tile) || !::IsTileType(tile, MP_INDUSTRY)) return INVALID_INDUSTRY;
 
	return ::GetIndustryIndex(tile);
 
}
 
@@ -220,24 +223,62 @@ ScriptDate::Date ScriptIndustry::GetCarg
 

	
 
	if (cargo_type == CT_INVALID) {
 
		return (ScriptDate::Date)std::accumulate(std::begin(i->last_cargo_accepted_at), std::end(i->last_cargo_accepted_at), 0, [](Date a, 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];
 
	}
 
}
 

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

	
 
bool ScriptIndustry::SetControlFlags(IndustryID industry_id, uint32 control_flags)
 
{
 
	if (ScriptObject::GetCompany() != OWNER_DEITY) return false;
 
	if (!IsValidIndustry(industry_id)) return false;
 

	
 
	return ScriptObject::DoCommand(0, industry_id, 0 | ((control_flags & ::INDCTL_MASK) << 8), CMD_INDUSTRY_CTRL);
 
}
 

	
 
/* static */ ScriptCompany::CompanyID ScriptIndustry::GetExclusiveSupplier(IndustryID industry_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return ScriptCompany::COMPANY_INVALID;
 

	
 
	auto company_id = ::Industry::Get(industry_id)->exclusive_supplier;
 
	if (!::Company::IsValidID(company_id)) return ScriptCompany::COMPANY_INVALID;
 

	
 
	return (ScriptCompany::CompanyID)((byte)company_id);
 
}
 

	
 
/* static */ bool ScriptIndustry::SetExclusiveSupplier(IndustryID industry_id, ScriptCompany::CompanyID company_id)
 
{
 
	EnforcePrecondition(false, IsValidIndustry(industry_id));
 

	
 
	auto company = ScriptCompany::ResolveCompanyID(company_id);
 
	::Owner owner = (company == ScriptCompany::COMPANY_INVALID ? ::INVALID_OWNER : (::Owner)company);
 
	return ScriptObject::DoCommand(0, industry_id, 1 | (((uint8)owner) << 16), CMD_INDUSTRY_CTRL);
 
}
 

	
 
/* static */ ScriptCompany::CompanyID ScriptIndustry::GetExclusiveConsumer(IndustryID industry_id)
 
{
 
	if (!IsValidIndustry(industry_id)) return ScriptCompany::COMPANY_INVALID;
 

	
 
	auto company_id = ::Industry::Get(industry_id)->exclusive_consumer;
 
	if (!::Company::IsValidID(company_id)) return ScriptCompany::COMPANY_INVALID;
 

	
 
	return (ScriptCompany::CompanyID)((byte)company_id);
 
}
 

	
 
/* static */ bool ScriptIndustry::SetExclusiveConsumer(IndustryID industry_id, ScriptCompany::CompanyID company_id)
 
{
 
	EnforcePrecondition(false, IsValidIndustry(industry_id));
 

	
 
	auto company = ScriptCompany::ResolveCompanyID(company_id);
 
	::Owner owner = (company == ScriptCompany::COMPANY_INVALID ? ::INVALID_OWNER : (::Owner)company);
 
	return ScriptObject::DoCommand(0, industry_id, 2 | (((uint8)owner) << 16), CMD_INDUSTRY_CTRL);
 
}
src/script/api/script_industry.hpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file script_industry.hpp Everything to query and build industries. */
 

	
 
#ifndef SCRIPT_INDUSTRY_HPP
 
#define SCRIPT_INDUSTRY_HPP
 

	
 
#include "script_company.hpp"
 
#include "script_date.hpp"
 
#include "script_object.hpp"
 
#include "script_date.hpp"
 
#include "../../industry.h"
 

	
 
/**
 
 * Class that handles all industry related functions.
 
 * @api ai game
 
 */
 
class ScriptIndustry : public ScriptObject {
 
public:
 
	/** Ways for an industry to accept a cargo. */
 
	enum CargoAcceptState {
 
		CAS_NOT_ACCEPTED, ///< The CargoID is not accepted by this industry.
 
		CAS_ACCEPTED,     ///< The industry currently accepts this CargoID.
 
		CAS_TEMP_REFUSED, ///< The industry temporarily refuses to accept this CargoID but may do so again in the future.
 
	};
 

	
 
	/**
 
	 * Control flags for industry
 
	 * @api -ai
 
	 */
 
	enum IndustryControlFlags {
 
		/**
 
		 * When industry production change is evaluated, rolls to decrease are ignored.
 
		 * This also prevents industry closure due to production dropping to the lowest level.
 
		 */
 
@@ -238,27 +239,68 @@ public:
 
	 * @return Date the industry last received cargo from a delivery, or ScriptDate::DATE_INVALID on error.
 
	 * @api -ai
 
	 */
 
	static ScriptDate::Date GetCargoLastAcceptedDate(IndustryID industry_id, CargoID cargo_type);
 

	
 
	/**
 
	 * Get the current control flags for an industry.
 
	 * @param industry_id The index of the industry.
 
	 * @pre IsValidIndustry(industry_id).
 
	 * @return Bit flags of the IndustryControlFlags enumeration.
 
	 * @api -ai
 
	 */
 
	static uint32 GetControlFlags(IndustryID industry_id);
 

	
 
	/**
 
	 * Change the control flags for an industry.
 
	 * @param industry_id The index of the industry.
 
	 * @param control_flags New flags as a combination of IndustryControlFlags values.
 
	 * @pre IsValidIndustry(industry_id).
 
	 * @pre No ScriptCompanyMode may be in scope.
 
	 * @return True if the action succeeded.
 
	 * @api -ai
 
	 */
 
	static bool SetControlFlags(IndustryID industry_id, uint32 control_flags);
 

	
 
	/**
 
	 * Find out which company currently has the exclusive rights to deliver cargo to the industry.
 
	 * @param industry_id The index of the industry.
 
	 * @pre IsValidIndustry(industry_id).
 
	 * @return The company that has the exclusive rights. The value
 
	 *         ScriptCompany::COMPANY_INVALID means that there are currently no
 
	 *         exclusive rights given out to anyone.
 
	 */
 
	static ScriptCompany::CompanyID GetExclusiveSupplier(IndustryID industry_id);
 

	
 
	/**
 
	 * Sets or resets the company that has exclusive right to deliver cargo to the industry.
 
	 * @param industry_id The index of the industry.
 
	 * @param company_id The company to set (ScriptCompany::COMPANY_INVALID to reset).
 
	 * @pre IsValidIndustry(industry_id).
 
	 * @return True if the action succeeded.
 
	 * @api -ai
 
	 */
 
	static bool SetExclusiveSupplier(IndustryID industry_id, ScriptCompany::CompanyID company_id);
 

	
 
	/**
 
	 * Find out which company currently has the exclusive rights to take cargo from the industry.
 
	 * @param industry_id The index of the industry.
 
	 * @pre IsValidIndustry(industry_id).
 
	 * @return The company that has the exclusive rights. The value
 
	 *         ScriptCompany::COMPANY_SPECTATOR means that there are currently no
 
	 *         exclusive rights given out to anyone.
 
	 */
 
	static ScriptCompany::CompanyID GetExclusiveConsumer(IndustryID industry_id);
 

	
 
	/**
 
	 * Sets or resets the company that has exclusive right to take cargo from the industry.
 
	 * @param industry_id The index of the industry.
 
	 * @param company_id The company to set (ScriptCompany::COMPANY_INVALID to reset).
 
	 * @pre IsValidIndustry(industry_id).
 
	 * @return True if the action succeeded.
 
	 * @api -ai
 
	 */
 
	static bool SetExclusiveConsumer(IndustryID industry_id, ScriptCompany::CompanyID company_id);
 

	
 
};
 

	
 
#endif /* SCRIPT_INDUSTRY_HPP */
src/station_cmd.cpp
Show inline comments
 
@@ -3992,59 +3992,60 @@ const StationList *StationFinder::GetSta
 
}
 

	
 

	
 
static bool CanMoveGoodsToStation(const Station *st, CargoID type)
 
{
 
	/* Is the station reserved exclusively for somebody else? */
 
	if (st->owner != OWNER_NONE && st->town->exclusive_counter > 0 && st->town->exclusivity != st->owner) return false;
 

	
 
	/* Lowest possible rating, better not to give cargo anymore. */
 
	if (st->goods[type].rating == 0) return false;
 

	
 
	/* Selectively servicing stations, and not this one. */
 
	if (_settings_game.order.selectgoods && !st->goods[type].HasVehicleEverTriedLoading()) return false;
 

	
 
	if (IsCargoInClass(type, CC_PASSENGERS)) {
 
		/* Passengers are never served by just a truck stop. */
 
		if (st->facilities == FACIL_TRUCK_STOP) return false;
 
	} else {
 
		/* Non-passengers are never served by just a bus stop. */
 
		if (st->facilities == FACIL_BUS_STOP) return false;
 
	}
 
	return true;
 
}
 

	
 
uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations)
 
uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations, Owner exclusivity)
 
{
 
	/* Return if nothing to do. Also the rounding below fails for 0. */
 
	if (all_stations->empty()) return 0;
 
	if (amount == 0) return 0;
 

	
 
	Station *first_station = nullptr;
 
	typedef std::pair<Station *, uint> StationInfo;
 
	std::vector<StationInfo> used_stations;
 

	
 
	for (Station *st : *all_stations) {
 
		if (exclusivity != INVALID_OWNER && exclusivity != st->owner) continue;
 
		if (!CanMoveGoodsToStation(st, type)) continue;
 

	
 
		/* Avoid allocating a vector if there is only one station to significantly
 
		 * improve performance in this common case. */
 
		if (first_station == nullptr) {
 
			first_station = st;
 
			continue;
 
		}
 
		if  (used_stations.empty()) {
 
			used_stations.reserve(2);
 
			used_stations.emplace_back(std::make_pair(first_station, 0));
 
		}
 
		used_stations.emplace_back(std::make_pair(st, 0));
 
	}
 

	
 
	/* no stations around at all? */
 
	if (first_station == nullptr) return 0;
 

	
 
	if (used_stations.empty()) {
 
		/* only one station around */
 
		amount *= first_station->goods[type].rating + 1;
 
		return UpdateStationWaiting(first_station, type, amount, source_type, source_id);
 
	}
 

	
0 comments (0 inline, 0 general)