diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -13,6 +13,7 @@ #include "table/sprites.h" #include "map.h" #include "tile.h" +#include "train.h" #include "landscape.h" #include "viewport.h" #include "command.h" @@ -1856,6 +1857,134 @@ static bool CheckIndustryCloseDownProtec 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 @@ -1909,7 +2038,6 @@ static void ChangeIndustryProduction(Ind 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]; @@ -1932,16 +2060,8 @@ static void ChangeIndustryProduction(Ind /* 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 { @@ -1989,6 +2109,19 @@ static void ChangeIndustryProduction(Ind } 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); @@ -2000,8 +2133,9 @@ static void ChangeIndustryProduction(Ind } 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); } } @@ -2084,7 +2218,7 @@ static CommandCost TerraformTile_Industr 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; } }