|
@@ -10,12 +10,13 @@
|
|
|
#include "station_map.h"
|
|
|
#include "table/strings.h"
|
|
|
#include "strings.h"
|
|
|
#include "table/sprites.h"
|
|
|
#include "map.h"
|
|
|
#include "tile.h"
|
|
|
#include "train.h"
|
|
|
#include "landscape.h"
|
|
|
#include "viewport.h"
|
|
|
#include "command.h"
|
|
|
#include "industry.h"
|
|
|
#include "town.h"
|
|
|
#include "vehicle.h"
|
|
@@ -1853,12 +1854,140 @@ static bool CheckIndustryCloseDownProtec
|
|
|
|
|
|
/* oil wells (or the industries with that flag set) are always allowed to closedown */
|
|
|
if (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD && _opt.landscape == LT_TEMPERATE) return false;
|
|
|
return (indspec->behaviour & INDUSTRYBEH_CANCLOSE_LASTINSTANCE) == 0 && GetIndustryTypeCount(type) <= 1;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 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)
|
|
|
{
|
|
|
const IndustrySpec *indspec = GetIndustrySpec(ind->type);
|
|
|
|
|
|
/* Check for acceptance of cargo */
|
|
|
for (uint j = 0; j < lengthof(ind->accepts_cargo) && ind->accepts_cargo[j] != CT_INVALID; j++) {
|
|
|
if (cargo == ind->accepts_cargo[j]) {
|
|
|
if (HASBIT(indspec->callback_flags, CBM_IND_REFUSE_CARGO)) {
|
|
|
uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO,
|
|
|
0, GetReverseCargoTranslation(cargo, indspec->grf_prop.grffile),
|
|
|
ind, ind->type, ind->xy);
|
|
|
if (res == 0) continue;
|
|
|
}
|
|
|
*c_accepts = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Check for produced cargo */
|
|
|
for (uint j = 0; j < lengthof(ind->produced_cargo) && ind->produced_cargo[j] != CT_INVALID; j++) {
|
|
|
if (cargo == ind->produced_cargo[j]) {
|
|
|
*c_produces = true;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Compute who can service the industry.
|
|
|
*
|
|
|
* Here, 'can service' means that he/she has trains and stations close enough
|
|
|
* to the industry with the right cargo type and the right orders (ie has the
|
|
|
* technical means).
|
|
|
*
|
|
|
* @param ind: Industry being investigated.
|
|
|
*
|
|
|
* @return: 0 if nobody can service the industry, 2 if the local player can
|
|
|
* service the industry, and 1 otherwise (only competitors can service the
|
|
|
* industry)
|
|
|
*/
|
|
|
int WhoCanServiceIndustry(Industry* ind)
|
|
|
{
|
|
|
/* Find all stations within reach of the industry */
|
|
|
StationSet stations = FindStationsAroundIndustryTile(ind->xy, ind->width, ind->height);
|
|
|
|
|
|
if (stations.size() == 0) return 0; // No stations found at all => nobody services
|
|
|
|
|
|
const Vehicle *v;
|
|
|
int result = 0;
|
|
|
FOR_ALL_VEHICLES(v) {
|
|
|
/* Is it worthwhile to try this vehicle? */
|
|
|
if (v->owner != _local_player && result != 0) continue;
|
|
|
|
|
|
/* Check whether it accepts the right kind of cargo */
|
|
|
bool c_accepts = false;
|
|
|
bool c_produces = false;
|
|
|
if (v->type == VEH_TRAIN && IsFrontEngine(v)) {
|
|
|
const Vehicle *u = v;
|
|
|
BEGIN_ENUM_WAGONS(u)
|
|
|
CanCargoServiceIndustry(u->cargo_type, ind, &c_accepts, &c_produces);
|
|
|
END_ENUM_WAGONS(u)
|
|
|
} else if (v->type == VEH_ROAD || v->type == VEH_SHIP || v->type == VEH_AIRCRAFT) {
|
|
|
CanCargoServiceIndustry(v->cargo_type, ind, &c_accepts, &c_produces);
|
|
|
} else {
|
|
|
continue;
|
|
|
}
|
|
|
if (!c_accepts && !c_produces) continue; // Wrong cargo
|
|
|
|
|
|
/* Check orders of the vehicle.
|
|
|
* We cannot check the first of shared orders only, since the first vehicle in such a chain
|
|
|
* may have a different cargo type.
|
|
|
*/
|
|
|
const Order *o;
|
|
|
FOR_VEHICLE_ORDERS(v, o) {
|
|
|
if (o->type == OT_GOTO_STATION && !HASBIT(o->flags, OFB_TRANSFER)) {
|
|
|
/* Vehicle visits a station to load or unload */
|
|
|
Station *st = GetStation(o->dest);
|
|
|
if (!st->IsValid()) continue;
|
|
|
|
|
|
/* Same cargo produced by industry is dropped here => not serviced by vehicle v */
|
|
|
if (HASBIT(o->flags, OFB_UNLOAD) && !c_accepts) break;
|
|
|
|
|
|
if (stations.find(st) != stations.end()) {
|
|
|
if (v->owner == _local_player) return 2; // Player services industry
|
|
|
result = 1; // Competitor services industry
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Report news that industry production has changed significantly
|
|
|
*
|
|
|
* @param ind: Industry with changed production
|
|
|
* @param type: Cargo type that has changed
|
|
|
* @param percent: Percentage of change (>0 means increase, <0 means decrease)
|
|
|
*/
|
|
|
static void ReportNewsProductionChangeIndustry(Industry *ind, CargoID type, int percent)
|
|
|
{
|
|
|
NewsType nt;
|
|
|
|
|
|
switch (WhoCanServiceIndustry(ind)) {
|
|
|
case 0: nt = NT_INDUSTRY_NOBODY; break;
|
|
|
case 1: nt = NT_INDUSTRY_OTHER; break;
|
|
|
case 2: nt = NT_INDUSTRY_PLAYER; break;
|
|
|
default: NOT_REACHED(); break;
|
|
|
}
|
|
|
SetDParam(2, abs(percent));
|
|
|
SetDParam(0, GetCargo(type)->name);
|
|
|
SetDParam(1, ind->index);
|
|
|
AddNewsItem(
|
|
|
percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN,
|
|
|
NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, nt, 0),
|
|
|
ind->xy + TileDiffXY(1, 1), 0
|
|
|
);
|
|
|
}
|
|
|
|
|
|
/** Change industry production or do closure
|
|
|
* @param i Industry for which changes are performed
|
|
|
* @param monthly true if it's the monthly call, false if it's the random call
|
|
|
*/
|
|
|
static void ChangeIndustryProduction(Industry *i, bool monthly)
|
|
|
{
|
|
@@ -1906,13 +2035,12 @@ static void ChangeIndustryProduction(Ind
|
|
|
|
|
|
if (smooth_economy) {
|
|
|
closeit = true;
|
|
|
for (byte j = 0; j < 2 && i->produced_cargo[j] != CT_INVALID; j++){
|
|
|
uint32 r = Random();
|
|
|
int old_prod, new_prod, percent;
|
|
|
int mag;
|
|
|
|
|
|
new_prod = old_prod = i->production_rate[j];
|
|
|
|
|
|
if (CHANCE16I(20, 1024, r)) new_prod -= max(((RandomRange(50) + 10) * old_prod) >> 8, 1U);
|
|
|
/* Chance of increasing becomes better when more is transported */
|
|
|
if (CHANCE16I(20 + (i->last_month_pct_transported[j] * 20 >> 8), 1024, r >> 16) && !only_decrease) {
|
|
@@ -1929,22 +2057,14 @@ static void ChangeIndustryProduction(Ind
|
|
|
percent = (old_prod == 0) ? 100 : (new_prod * 100 / old_prod - 100);
|
|
|
i->production_rate[j] = new_prod;
|
|
|
|
|
|
/* Close the industry when it has the lowest possible production rate */
|
|
|
if (new_prod > 1) closeit = false;
|
|
|
|
|
|
mag = abs(percent);
|
|
|
if (mag >= 10) {
|
|
|
SetDParam(2, mag);
|
|
|
SetDParam(0, GetCargo(i->produced_cargo[j])->name);
|
|
|
SetDParam(1, i->index);
|
|
|
AddNewsItem(
|
|
|
percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN,
|
|
|
NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ECONOMY, 0),
|
|
|
i->xy + TileDiffXY(1, 1), 0
|
|
|
);
|
|
|
if (abs(percent) >= 10) {
|
|
|
ReportNewsProductionChangeIndustry(i, i->produced_cargo[j], percent);
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
if (only_decrease || CHANCE16(1, 3)) {
|
|
|
/* If you transport > 60%, 66% chance we increase, else 33% chance we increase */
|
|
|
if (!only_decrease && (i->last_month_pct_transported[0] > 153) != CHANCE16(1, 3)) {
|
|
@@ -1986,25 +2106,39 @@ static void ChangeIndustryProduction(Ind
|
|
|
if (closeit && !CheckIndustryCloseDownProtection(i->type)) {
|
|
|
i->prod_level = 0;
|
|
|
str = indspec->closure_text;
|
|
|
}
|
|
|
|
|
|
if (!suppress_message && str != STR_NULL) {
|
|
|
NewsType nt;
|
|
|
/* Compute news category */
|
|
|
if (closeit) {
|
|
|
nt = NT_OPENCLOSE;
|
|
|
} else {
|
|
|
switch (WhoCanServiceIndustry(i)) {
|
|
|
case 0: nt = NT_INDUSTRY_NOBODY; break;
|
|
|
case 1: nt = NT_INDUSTRY_OTHER; break;
|
|
|
case 2: nt = NT_INDUSTRY_PLAYER; break;
|
|
|
default: NOT_REACHED(); break;
|
|
|
}
|
|
|
}
|
|
|
/* Set parameters of news string */
|
|
|
if (str > STR_LAST_STRINGID) {
|
|
|
SetDParam(0, STR_TOWN);
|
|
|
SetDParam(1, i->town->index);
|
|
|
SetDParam(2, indspec->name);
|
|
|
} else if (closeit) {
|
|
|
SetDParam(0, STR_INDUSTRY_FORMAT);
|
|
|
SetDParam(1, i->town->index);
|
|
|
SetDParam(2, indspec->name);
|
|
|
} else {
|
|
|
SetDParam(0, i->index);
|
|
|
}
|
|
|
/* and report the news to the user */
|
|
|
AddNewsItem(str,
|
|
|
NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, closeit ? NT_OPENCLOSE : NT_ECONOMY, 0),
|
|
|
NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, nt, 0),
|
|
|
i->xy + TileDiffXY(1, 1), 0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void IndustryMonthlyLoop()
|
|
|
{
|
|
@@ -2081,13 +2215,13 @@ static CommandCost TerraformTile_Industr
|
|
|
/* Call callback 3C 'disable autosloping for industry tiles'. */
|
|
|
if (HASBIT(itspec->callback_flags, CBM_INDT_AUTOSLOPE)) {
|
|
|
/* If the callback fails, allow autoslope. */
|
|
|
uint16 res = GetIndustryTileCallback(CBID_INDUSTRY_AUTOSLOPE, 0, 0, gfx, GetIndustryByTile(tile), tile);
|
|
|
if ((res == 0) || (res == CALLBACK_FAILED)) return _price.terraform;
|
|
|
} else {
|
|
|
// allow autoslope
|
|
|
/* allow autoslope */
|
|
|
return _price.terraform;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
|
|
|
}
|