(svn r17113) -Change [FS#265][FS#2094][FS#2589]: apply the subsidy when subsidy's destination is in station's catchment area and cargo packets originate from subsidy's source

-Change [FS#1134]: subsidies aren't bound to stations after awarding anymore, they still apply to town or industry, no matter what station is used for loading and unloading. Awarded subsidies from older savegames are lost
-Change [NoAI]: due to these changes, AISubsidy::GetSource and AISubsidy::GetDestination now return STATION_INVALID for awarded subsidies
pull/155/head
smatz 15 years ago
parent eb5039f81e
commit 0502a6df42

@ -1487,6 +1487,10 @@
RelativePath=".\..\src\subsidy_func.h" RelativePath=".\..\src\subsidy_func.h"
> >
</File> </File>
<File
RelativePath=".\..\src\subsidy_type.h"
>
</File>
<File <File
RelativePath=".\..\src\tar_type.h" RelativePath=".\..\src\tar_type.h"
> >

@ -1484,6 +1484,10 @@
RelativePath=".\..\src\subsidy_func.h" RelativePath=".\..\src\subsidy_func.h"
> >
</File> </File>
<File
RelativePath=".\..\src\subsidy_type.h"
>
</File>
<File <File
RelativePath=".\..\src\tar_type.h" RelativePath=".\..\src\tar_type.h"
> >

@ -300,6 +300,7 @@ strings_func.h
strings_type.h strings_type.h
subsidy_base.h subsidy_base.h
subsidy_func.h subsidy_func.h
subsidy_type.h
tar_type.h tar_type.h
terraform_gui.h terraform_gui.h
textbuf_gui.h textbuf_gui.h

@ -29,6 +29,8 @@
* \li WaypointID was replaced by StationID. All WaypointIDs from previous * \li WaypointID was replaced by StationID. All WaypointIDs from previous
* savegames are invalid * savegames are invalid
* \li WAYPOINT_INVALID is now deprecated, use STATION_INVALID instead * \li WAYPOINT_INVALID is now deprecated, use STATION_INVALID instead
* \li AISubsidy::GetSource and AISubsidy::GetDestination return STATION_INVALID
* for awarded subsidies
* \li AIs can create subclasses of API classes and use API constants as part * \li AIs can create subclasses of API classes and use API constants as part
* of their own constants * of their own constants
* \li AIVehicleList_Station now also works for waypoints * \li AIVehicleList_Station now also works for waypoints

@ -4,6 +4,7 @@
#include "ai_subsidy.hpp" #include "ai_subsidy.hpp"
#include "ai_date.hpp" #include "ai_date.hpp"
#include "ai_log.hpp"
#include "../../subsidy_base.h" #include "../../subsidy_base.h"
#include "../../station_base.h" #include "../../station_base.h"
#include "../../cargotype.h" #include "../../cargotype.h"
@ -24,7 +25,7 @@
{ {
if (!IsAwarded(subsidy_id)) return AICompany::COMPANY_INVALID; if (!IsAwarded(subsidy_id)) return AICompany::COMPANY_INVALID;
return (AICompany::CompanyID)((byte)::Station::Get(::Subsidy::Get(subsidy_id)->src)->owner); return (AICompany::CompanyID)((byte)::Subsidy::Get(subsidy_id)->awarded);
} }
/* static */ int32 AISubsidy::GetExpireDate(SubsidyID subsidy_id) /* static */ int32 AISubsidy::GetExpireDate(SubsidyID subsidy_id)
@ -34,11 +35,7 @@
int year = AIDate::GetYear(AIDate::GetCurrentDate()); int year = AIDate::GetYear(AIDate::GetCurrentDate());
int month = AIDate::GetMonth(AIDate::GetCurrentDate()); int month = AIDate::GetMonth(AIDate::GetCurrentDate());
if (IsAwarded(subsidy_id)) { month += ::Subsidy::Get(subsidy_id)->remaining;
month += 24 - ::Subsidy::Get(subsidy_id)->age;
} else {
month += 12 - ::Subsidy::Get(subsidy_id)->age;
}
year += (month - 1) / 12; year += (month - 1) / 12;
month = ((month - 1) % 12) + 1; month = ((month - 1) % 12) + 1;
@ -64,6 +61,11 @@
{ {
if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION; if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION;
if (IsAwarded(subsidy_id)) {
AILog::Error("AISubsidy::GetSource returned INVALID_STATION due to internal changes in the Subsidy logic.");
return INVALID_STATION;
}
return ::Subsidy::Get(subsidy_id)->src; return ::Subsidy::Get(subsidy_id)->src;
} }
@ -78,5 +80,10 @@
{ {
if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION; if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION;
if (IsAwarded(subsidy_id)) {
AILog::Error("AISubsidy::GetDestination returned INVALID_STATION due to internal changes in the Subsidy logic.");
return INVALID_STATION;
}
return ::Subsidy::Get(subsidy_id)->dst; return ::Subsidy::Get(subsidy_id)->dst;
} }

@ -69,12 +69,12 @@ public:
/** /**
* Return the source TownID/IndustryID/StationID the subsidy is for. * Return the source TownID/IndustryID/StationID the subsidy is for.
* 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to. * \li IsAwarded(subsidy_id) -> return INVALID_STATION.
* 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID. * \li !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID.
* 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID. * \li !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID.
* @param subsidy_id The SubsidyID to check. * @param subsidy_id The SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id). * @pre IsValidSubsidy(subsidy_id).
* @return One of TownID/IndustryID/StationID. * @return One of TownID/IndustryID/INVALID_STATION.
*/ */
static int32 GetSource(SubsidyID subsidy_id); static int32 GetSource(SubsidyID subsidy_id);
@ -88,12 +88,12 @@ public:
/** /**
* Return the destination TownID/IndustryID/StationID the subsidy is for. * Return the destination TownID/IndustryID/StationID the subsidy is for.
* 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to. * \li IsAwarded(subsidy_id) -> return INVALID_STATION.
* 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID. * \li !IsAwarded(subsidy_id) && DestinationIsTown(subsidy_id) -> return the TownID.
* 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID. * \li !IsAwarded(subsidy_id) && !DestinationIsTown(subsidy_id) -> return the IndustryID.
* @param subsidy_id the SubsidyID to check. * @param subsidy_id the SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id). * @pre IsValidSubsidy(subsidy_id).
* @return One of TownID/IndustryID/StationID. * @return One of TownID/IndustryID/INVALID_STATION.
*/ */
static int32 GetDestination(SubsidyID subsidy_id); static int32 GetDestination(SubsidyID subsidy_id);
}; };

@ -85,15 +85,15 @@ public:
}; };
/** Types of subsidy source and destination */ /** Types of cargo source and destination */
enum SourceType { enum SourceType {
ST_INDUSTRY, ///< Source/destination is an industry ST_INDUSTRY, ///< Source/destination is an industry
ST_TOWN, ///< Source/destination is a town ST_TOWN, ///< Source/destination is a town
ST_STATION, ///< Source/destination is a station ST_HEADQUARTERS, ///< Source/destination are company headquarters
}; };
typedef SimpleTinyEnumT<SourceType, byte> SourceTypeByte; typedef SimpleTinyEnumT<SourceType, byte> SourceTypeByte;
typedef uint16 SourceID; ///< Contains either industry ID, town ID or station ID (or INVALID_SOURCE) typedef uint16 SourceID; ///< Contains either industry ID, town ID or company ID (or INVALID_SOURCE)
static const SourceID INVALID_SOURCE = 0xFFFF; ///< Invalid/unknown index of source static const SourceID INVALID_SOURCE = 0xFFFF; ///< Invalid/unknown index of source
#endif /* CARGO_TYPE_H */ #endif /* CARGO_TYPE_H */

@ -16,17 +16,33 @@ void InitializeCargoPackets()
_cargopacket_pool.CleanPool(); _cargopacket_pool.CleanPool();
} }
CargoPacket::CargoPacket(StationID source, uint16 count) CargoPacket::CargoPacket(StationID source, uint16 count, SourceType source_type, SourceID source_id)
{ {
if (source != INVALID_STATION) assert(count != 0); if (source != INVALID_STATION) assert(count != 0);
this->source = source; // this->feeder_share = 0; // no need to zero already zeroed data (by operator new)
this->source_xy = (source != INVALID_STATION) ? Station::Get(source)->xy : 0; this->source_xy = (source != INVALID_STATION) ? Station::Get(source)->xy : 0;
this->loaded_at_xy = this->source_xy; this->loaded_at_xy = this->source_xy;
this->source = source;
this->count = count; this->count = count;
this->days_in_transit = 0; // this->days_in_transit = 0;
this->feeder_share = 0;
this->source_type = source_type;
this->source_id = source_id;
}
/**
* Invalidates (sets source_id to INVALID_SOURCE) all cargo packets from given source
* @param src_type type of source
* @param src index of source
*/
/* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
{
CargoPacket *cp;
FOR_ALL_CARGOPACKETS(cp) {
if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
}
} }
/* /*
@ -149,6 +165,9 @@ bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta,
cp_new->days_in_transit = cp->days_in_transit; cp_new->days_in_transit = cp->days_in_transit;
cp_new->feeder_share = fs; cp_new->feeder_share = fs;
cp_new->source_type = cp->source_type;
cp_new->source_id = cp->source_id;
cp_new->count = count; cp_new->count = count;
dest->packets.push_back(cp_new); dest->packets.push_back(cp_new);

@ -9,6 +9,7 @@
#include "economy_type.h" #include "economy_type.h"
#include "tile_type.h" #include "tile_type.h"
#include "station_type.h" #include "station_type.h"
#include "cargo_type.h"
#include <list> #include <list>
typedef uint32 CargoPacketID; typedef uint32 CargoPacketID;
@ -30,13 +31,16 @@ struct CargoPacket : CargoPacketPool::PoolItem<&_cargopacket_pool> {
uint16 count; ///< The amount of cargo in this packet uint16 count; ///< The amount of cargo in this packet
byte days_in_transit; ///< Amount of days this packet has been in transit byte days_in_transit; ///< Amount of days this packet has been in transit
SourceTypeByte source_type; ///< Type of #source_id
SourceID source_id; ///< Index of source, INVALID_SOURCE if unknown/invalid
/** /**
* Creates a new cargo packet * Creates a new cargo packet
* @param source the source of the packet * @param source the source of the packet
* @param count the number of cargo entities to put in this packet * @param count the number of cargo entities to put in this packet
* @pre count != 0 || source == INVALID_STATION * @pre count != 0 || source == INVALID_STATION
*/ */
CargoPacket(StationID source = INVALID_STATION, uint16 count = 0); CargoPacket(StationID source = INVALID_STATION, uint16 count = 0, SourceType source_type = ST_INDUSTRY, SourceID source_id = INVALID_SOURCE);
/** Destroy the packet */ /** Destroy the packet */
~CargoPacket() { } ~CargoPacket() { }
@ -49,8 +53,11 @@ struct CargoPacket : CargoPacketPool::PoolItem<&_cargopacket_pool> {
*/ */
FORCEINLINE bool SameSource(const CargoPacket *cp) const FORCEINLINE bool SameSource(const CargoPacket *cp) const
{ {
return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit; return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit &&
this->source_type == cp->source_type && this->source_id == cp->source_id;
} }
static void InvalidateAllFrom(SourceType src_type, SourceID src);
}; };
/** /**

@ -328,11 +328,13 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
Company::Get(old_owner)->money = UINT64_MAX >> 2; // jackpot ;p Company::Get(old_owner)->money = UINT64_MAX >> 2; // jackpot ;p
} }
if (new_owner == INVALID_OWNER) { Subsidy *s;
Subsidy *s; FOR_ALL_SUBSIDIES(s) {
FOR_ALL_SUBSIDIES(s) { if (s->awarded == old_owner) {
if (s->IsAwarded() && Station::Get(s->dst)->owner == old_owner) { if (new_owner == INVALID_OWNER) {
s->cargo_type = CT_INVALID; DeleteSubsidy(s);
} else {
s->awarded = new_owner;
} }
} }
} }
@ -916,45 +918,38 @@ static void DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, int nu
/** /**
* Delivers goods to industries/towns and calculates the payment * Delivers goods to industries/towns and calculates the payment
* @param num_pieces amount of cargo delivered * @param num_pieces amount of cargo delivered
* @param source Originstation of the cargo
* @param dest Station the cargo has been unloaded * @param dest Station the cargo has been unloaded
* @param source_tile The origin of the cargo for distance calculation * @param source_tile The origin of the cargo for distance calculation
* @param days_in_transit Travel time * @param days_in_transit Travel time
* @param company The company delivering the cargo * @param company The company delivering the cargo
* The cargo is just added to the stockpile of the industry. It is due to the caller to trigger the industry's production machinery * @param src_type Type of source of cargo (industry, town, headquarters)
* @param src Index of source of cargo
* @return Revenue for delivering cargo
* @note The cargo is just added to the stockpile of the industry. It is due to the caller to trigger the industry's production machinery
*/ */
static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID source, StationID dest, TileIndex source_tile, byte days_in_transit, Company *company) static Money DeliverGoods(int num_pieces, CargoID cargo_type, StationID dest, TileIndex source_tile, byte days_in_transit, Company *company, SourceType src_type, SourceID src)
{ {
bool subsidised = false;
assert(num_pieces > 0); assert(num_pieces > 0);
/* Update company statistics */ /* Update company statistics */
company->cur_economy.delivered_cargo += num_pieces; company->cur_economy.delivered_cargo += num_pieces;
SetBit(company->cargo_types, cargo_type); SetBit(company->cargo_types, cargo_type);
const Station *s_to = Station::Get(dest); const Station *st = Station::Get(dest);
if (source != INVALID_STATION) {
const Station *s_from = Station::Get(source);
/* Check if a subsidy applies. */
subsidised = CheckSubsidised(s_from, s_to, cargo_type, company->index);
}
/* Increase town's counter for some special goods types */ /* Increase town's counter for some special goods types */
const CargoSpec *cs = CargoSpec::Get(cargo_type); const CargoSpec *cs = CargoSpec::Get(cargo_type);
if (cs->town_effect == TE_FOOD) s_to->town->new_act_food += num_pieces; if (cs->town_effect == TE_FOOD) st->town->new_act_food += num_pieces;
if (cs->town_effect == TE_WATER) s_to->town->new_act_water += num_pieces; if (cs->town_effect == TE_WATER) st->town->new_act_water += num_pieces;
/* Give the goods to the industry. */ /* Give the goods to the industry. */
DeliverGoodsToIndustry(s_to, cargo_type, num_pieces); DeliverGoodsToIndustry(st, cargo_type, num_pieces);
/* Determine profit */ /* Determine profit */
Money profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(source_tile, s_to->xy), days_in_transit, cargo_type); Money profit = GetTransportedGoodsIncome(num_pieces, DistanceManhattan(source_tile, st->xy), days_in_transit, cargo_type);
/* Modify profit if a subsidy is in effect */ /* Modify profit if a subsidy is in effect */
if (subsidised) { if (CheckSubsidised(cargo_type, company->index, src_type, src, st)) {
switch (_settings_game.difficulty.subsidy_multiplier) { switch (_settings_game.difficulty.subsidy_multiplier) {
case 0: profit += profit >> 1; break; case 0: profit += profit >> 1; break;
case 1: profit *= 2; break; case 1: profit *= 2; break;
@ -1051,7 +1046,7 @@ void CargoPayment::PayFinalDelivery(CargoPacket *cp, uint count)
} }
/* Handle end of route payment */ /* Handle end of route payment */
Money profit = DeliverGoods(count, this->ct, cp->source, this->current_station, cp->source_xy, cp->days_in_transit, this->owner); Money profit = DeliverGoods(count, this->ct, this->current_station, cp->source_xy, cp->days_in_transit, this->owner, cp->source_type, cp->source_id);
this->route_profit += profit; this->route_profit += profit;
/* The vehicle's profit is whatever route profit there is minus feeder shares. */ /* The vehicle's profit is whatever route profit there is minus feeder shares. */

@ -32,7 +32,7 @@ int UpdateCompanyRatingAndValue(Company *c, bool update);
void StartupIndustryDailyChanges(bool init_counter); void StartupIndustryDailyChanges(bool init_counter);
Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type); Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount); uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount, SourceType source_type, SourceID source_id);
void PrepareUnload(Vehicle *front_v); void PrepareUnload(Vehicle *front_v);
void LoadUnloadStation(Station *st); void LoadUnloadStation(Station *st);

@ -19,6 +19,7 @@
#include "tile_type.h" #include "tile_type.h"
#include "company_type.h" #include "company_type.h"
#include "strings_type.h" #include "strings_type.h"
#include "subsidy_type.h"
enum { enum {
INVALID_INDUSTRY = 0xFFFF, INVALID_INDUSTRY = 0xFFFF,
@ -125,6 +126,8 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
Year last_prod_year; ///< last year of production 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 byte was_cargo_delivered; ///< flag that indicate this has been the closest industry chosen for cargo delivery by a station. see DeliverGoodsToIndustry
PartOfSubsidyByte part_of_subsidy; ///< NOSAVE: is this industry a source/destination of a subsidy?
OwnerByte founder; ///< Founder of the industry OwnerByte founder; ///< Founder of the industry
Date construction_date; ///< Date of the construction of the industry Date construction_date; ///< Date of the construction of the industry
uint8 construction_type; ///< Way the industry was constructed (@see IndustryConstructionType) uint8 construction_type; ///< Way the industry was constructed (@see IndustryConstructionType)

@ -167,11 +167,12 @@ Industry::~Industry()
DecIndustryTypeCount(this->type); DecIndustryTypeCount(this->type);
DeleteSubsidyWith(ST_INDUSTRY, this->index);
DeleteIndustryNews(this->index); DeleteIndustryNews(this->index);
DeleteWindowById(WC_INDUSTRY_VIEW, this->index); DeleteWindowById(WC_INDUSTRY_VIEW, this->index);
InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0); InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0);
DeleteSubsidyWith(ST_INDUSTRY, this->index);
CargoPacket::InvalidateAllFrom(ST_INDUSTRY, this->index);
Station::RecomputeIndustriesNearForAll(); Station::RecomputeIndustriesNearForAll();
} }
@ -479,7 +480,7 @@ static void TransportIndustryGoods(TileIndex tile)
i->this_month_production[j] += cw; i->this_month_production[j] += cw;
uint am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[j], cw); uint am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[j], cw, ST_INDUSTRY, i->index);
i->this_month_transported[j] += am; i->this_month_transported[j] += am;
moved_cargo |= (am != 0); moved_cargo |= (am != 0);

@ -797,12 +797,12 @@ STR_NEWS_STATION_NOW_ACCEPTS_CARGO :{WHITE}{STATION
STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO :{WHITE}{STATION} now accepts {STRING} and {STRING} STR_NEWS_STATION_NOW_ACCEPTS_CARGO_AND_CARGO :{WHITE}{STATION} now accepts {STRING} and {STRING}
STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIGFONT}{BLACK}Offer of subsidy expired:{}{}{STRING} from {STRING2} to {STRING2} will now not attract a subsidy. STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIGFONT}{BLACK}Offer of subsidy expired:{}{}{STRING} from {STRING2} to {STRING2} will now not attract a subsidy.
STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIGFONT}{BLACK}Subsidy withdrawn:{}{}{STRING} service from {STATION} to {STATION} is no longer subsidised. STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIGFONT}{BLACK}Subsidy withdrawn:{}{}{STRING} service from {STRING2} to {STRING2} is no longer subsidised.
STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIGFONT}{BLACK}Service subsidy offered:{}{}First {STRING} service from {STRING2} to {STRING2} will attract a year's subsidy from the local authority! STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIGFONT}{BLACK}Service subsidy offered:{}{}First {STRING} service from {STRING2} to {STRING2} will attract a year's subsidy from the local authority!
STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STATION} to {STATION} will pay 50% extra for the next year! STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay 50% extra for the next year!
STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STATION} to {STATION} will pay double rates for the next year! STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay double rates for the next year!
STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STATION} to {STATION} will pay triple rates for the next year! STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay triple rates for the next year!
STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STATION} to {STATION} will pay quadruple rates for the next year! STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIGFONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} service from {STRING2} to {STRING2} will pay quadruple rates for the next year!
STR_NEWS_ROAD_REBUILDING :{BIGFONT}{BLACK}Traffic chaos in {TOWN}!{}{}Road rebuilding programme funded by {RAW_STRING} brings 6 months of misery to motorists! STR_NEWS_ROAD_REBUILDING :{BIGFONT}{BLACK}Traffic chaos in {TOWN}!{}{}Road rebuilding programme funded by {RAW_STRING} brings 6 months of misery to motorists!
@ -2397,7 +2397,7 @@ STR_SUBSIDIES_OFFERED_TITLE :{BLACK}Subsidie
STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} (by {DATE_SHORT}) STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} (by {DATE_SHORT})
STR_SUBSIDIES_NONE :{ORANGE}None STR_SUBSIDIES_NONE :{ORANGE}None
STR_SUBSIDIES_SUBSIDISED_TITLE :{BLACK}Services already subsidised: STR_SUBSIDIES_SUBSIDISED_TITLE :{BLACK}Services already subsidised:
STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} from {STATION} to {STATION}{YELLOW} ({COMPANY}{YELLOW}, until {DATE_SHORT}) STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({COMPANY}{YELLOW}, until {DATE_SHORT})
STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Click on service to centre view on industry/town STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Click on service to centre view on industry/town
# Station list window # Station list window

@ -32,6 +32,7 @@
#include "../economy_base.h" #include "../economy_base.h"
#include "../animated_tile_func.h" #include "../animated_tile_func.h"
#include "../subsidy_base.h" #include "../subsidy_base.h"
#include "../subsidy_func.h"
#include "table/strings.h" #include "table/strings.h"
@ -230,6 +231,7 @@ static bool InitializeWindowsAndCaches()
SetCachedEngineCounts(); SetCachedEngineCounts();
Station::RecomputeIndustriesNearForAll(); Station::RecomputeIndustriesNearForAll();
RebuildSubsidisedSourceAndDestinationCache();
/* Towns have a noise controlled number of airports system /* Towns have a noise controlled number of airports system
* So each airport's noise value must be added to the town->noise_reached value * So each airport's noise value must be added to the town->noise_reached value
@ -1868,17 +1870,15 @@ bool AfterLoadGame()
} }
} }
{ if (CheckSavegameVersion(125)) {
/* Delete invalid subsidies possibly present in old versions (but converted to new savegame) */ /* Convert old subsidies */
Subsidy *s; Subsidy *s;
FOR_ALL_SUBSIDIES(s) { FOR_ALL_SUBSIDIES(s) {
if (s->IsAwarded()) { /* Convert only nonawarded subsidies. The original source and destination town/industry
/* Station -> Station */ * anymore for awarded subsidies, so invalidate them. */
const Station *from = Station::GetIfValid(s->src); if (s->remaining < 12) {
const Station *to = Station::GetIfValid(s->dst); s->remaining = 12 - s->remaining; // convert "age" to "remaining"
s->src_type = s->dst_type = ST_STATION; s->awarded = INVALID_COMPANY; // not awarded to anyone
if (from != NULL && to != NULL && from->owner == to->owner && Company::IsValidID(from->owner)) continue;
} else {
const CargoSpec *cs = CargoSpec::Get(s->cargo_type); const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
switch (cs->town_effect) { switch (cs->town_effect) {
case TE_PASSENGERS: case TE_PASSENGERS:
@ -1901,6 +1901,7 @@ bool AfterLoadGame()
break; break;
} }
} }
/* Awarded subsidy or invalid source/destination, invalidate */
s->cargo_type = CT_INVALID; s->cargo_type = CT_INVALID;
} }
} }

@ -14,6 +14,8 @@ static const SaveLoad _cargopacket_desc[] = {
SLE_VAR(CargoPacket, count, SLE_UINT16), SLE_VAR(CargoPacket, count, SLE_UINT16),
SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8), SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8),
SLE_VAR(CargoPacket, feeder_share, SLE_INT64), SLE_VAR(CargoPacket, feeder_share, SLE_INT64),
SLE_CONDVAR(CargoPacket, source_type, SLE_UINT8, 125, SL_MAX_VERSION),
SLE_CONDVAR(CargoPacket, source_id, SLE_UINT16, 125, SL_MAX_VERSION),
/* Used to be paid_for, but that got changed. */ /* Used to be paid_for, but that got changed. */
SLE_CONDNULL(1, 0, 120), SLE_CONDNULL(1, 0, 120),

@ -1468,7 +1468,7 @@ static bool LoadOldEngineName(LoadgameState *ls, int num)
static const OldChunks subsidy_chunk[] = { static const OldChunks subsidy_chunk[] = {
OCL_SVAR( OC_UINT8, Subsidy, cargo_type ), OCL_SVAR( OC_UINT8, Subsidy, cargo_type ),
OCL_SVAR( OC_UINT8, Subsidy, age ), 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, src ),
OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, dst ), OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Subsidy, dst ),

@ -41,7 +41,7 @@
#include "saveload_internal.h" #include "saveload_internal.h"
extern const uint16 SAVEGAME_VERSION = 124; extern const uint16 SAVEGAME_VERSION = 125;
SavegameType _savegame_type; ///< type of savegame we are loading SavegameType _savegame_type; ///< type of savegame we are loading

@ -9,11 +9,14 @@
static const SaveLoad _subsidies_desc[] = { static const SaveLoad _subsidies_desc[] = {
SLE_VAR(Subsidy, cargo_type, SLE_UINT8), SLE_VAR(Subsidy, cargo_type, SLE_UINT8),
SLE_VAR(Subsidy, age, SLE_UINT8), SLE_VAR(Subsidy, remaining, SLE_UINT8),
SLE_CONDVAR(Subsidy, src, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), SLE_CONDVAR(Subsidy, awarded, SLE_UINT8, 125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, src, SLE_UINT16, 5, SL_MAX_VERSION), SLE_CONDVAR(Subsidy, src_type, SLE_UINT8, 125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, dst, SLE_FILE_U8 | SLE_VAR_U16, 0, 4), SLE_CONDVAR(Subsidy, dst_type, SLE_UINT8, 125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, dst, SLE_UINT16, 5, SL_MAX_VERSION), SLE_CONDVAR(Subsidy, src, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
SLE_CONDVAR(Subsidy, src, SLE_UINT16, 5, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, dst, SLE_FILE_U8 | SLE_VAR_U16, 0, 4),
SLE_CONDVAR(Subsidy, dst, SLE_UINT16, 5, SL_MAX_VERSION),
SLE_END() SLE_END()
}; };

@ -90,9 +90,6 @@ Station::~Station()
/* Now delete all orders that go to the station */ /* Now delete all orders that go to the station */
RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index); RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index);
/* Subsidies need removal as well */
DeleteSubsidyWith(ST_STATION, this->index);
/* Remove all news items */ /* Remove all news items */
DeleteStationNews(this->index); DeleteStationNews(this->index);

@ -2840,9 +2840,9 @@ void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint rad
} }
} }
static void UpdateStationWaiting(Station *st, CargoID type, uint amount) static void UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
{ {
st->goods[type].cargo.Append(new CargoPacket(st->index, amount)); st->goods[type].cargo.Append(new CargoPacket(st->index, amount, source_type, source_id));
SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP); SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type); StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
@ -2926,7 +2926,7 @@ void FindStationsAroundTiles(TileIndex tile, int w_prod, int h_prod, StationList
} }
} }
uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount) uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount, SourceType source_type, SourceID source_id)
{ {
/* Return if nothing to do. Also the rounding below fails for 0. */ /* Return if nothing to do. Also the rounding below fails for 0. */
if (amount == 0) return 0; if (amount == 0) return 0;
@ -2968,7 +2968,7 @@ uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
if (st2 == NULL) { if (st2 == NULL) {
/* only one station around */ /* only one station around */
uint moved = amount * best_rating1 / 256 + 1; uint moved = amount * best_rating1 / 256 + 1;
UpdateStationWaiting(st1, type, moved); UpdateStationWaiting(st1, type, moved, source_type, source_id);
return moved; return moved;
} }
@ -2987,13 +2987,13 @@ uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
if (t != 0) { if (t != 0) {
moved = t * best_rating1 / 256 + 1; moved = t * best_rating1 / 256 + 1;
amount -= t; amount -= t;
UpdateStationWaiting(st1, type, moved); UpdateStationWaiting(st1, type, moved, source_type, source_id);
} }
if (amount != 0) { if (amount != 0) {
amount = amount * best_rating2 / 256 + 1; amount = amount * best_rating2 / 256 + 1;
moved += amount; moved += amount;
UpdateStationWaiting(st2, type, amount); UpdateStationWaiting(st2, type, amount, source_type, source_id);
} }
return moved; return moved;

@ -22,27 +22,23 @@
/** /**
* Marks subsidy as awarded, creates news and AI event * Marks subsidy as awarded, creates news and AI event
* @param from source station
* @param to destination station
* @param company awarded company * @param company awarded company
*/ */
void Subsidy::AwardTo(StationID from, StationID to, CompanyID company) void Subsidy::AwardTo(CompanyID company)
{ {
assert(!this->IsAwarded()); assert(!this->IsAwarded());
this->age = 12; this->awarded = company;
this->src_type = this->dst_type = ST_STATION; this->remaining = 12;
this->src = from;
this->dst = to;
/* Add a news item */
Pair reftype = SetupSubsidyDecodeParam(this, 0);
InjectDParam(1);
char *company_name = MallocT<char>(MAX_LENGTH_COMPANY_NAME_BYTES); char *company_name = MallocT<char>(MAX_LENGTH_COMPANY_NAME_BYTES);
SetDParam(0, company); SetDParam(0, company);
GetString(company_name, STR_COMPANY_NAME, company_name + MAX_LENGTH_COMPANY_NAME_BYTES - 1); GetString(company_name, STR_COMPANY_NAME, company_name + MAX_LENGTH_COMPANY_NAME_BYTES - 1);
/* Add a news item */
Pair reftype = SetupSubsidyDecodeParam(this, 0);
InjectDParam(1);
SetDParamStr(0, company_name); SetDParamStr(0, company_name);
AddNewsItem( AddNewsItem(
STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
@ -62,7 +58,10 @@ void Subsidy::AwardTo(StationID from, StationID to, CompanyID company)
/* static */ Subsidy *Subsidy::AllocateItem() /* static */ Subsidy *Subsidy::AllocateItem()
{ {
for (Subsidy *s = Subsidy::array; s < endof(Subsidy::array); s++) { for (Subsidy *s = Subsidy::array; s < endof(Subsidy::array); s++) {
if (!s->IsValid()) return s; if (!s->IsValid()) {
s->awarded = INVALID_COMPANY;
return s;
}
} }
return NULL; return NULL;
@ -105,10 +104,6 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
reftype1 = NR_TOWN; reftype1 = NR_TOWN;
SetDParam(1, STR_TOWN_NAME); SetDParam(1, STR_TOWN_NAME);
break; break;
case ST_STATION:
reftype1 = NR_STATION;
SetDParam(1, s->src);
break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
SetDParam(2, s->src); SetDParam(2, s->src);
@ -122,10 +117,6 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
reftype2 = NR_TOWN; reftype2 = NR_TOWN;
SetDParam(4, STR_TOWN_NAME); SetDParam(4, STR_TOWN_NAME);
break; break;
case ST_STATION:
reftype2 = NR_STATION;
SetDParam(2, s->dst);
break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
SetDParam(5, s->dst); SetDParam(5, s->dst);
@ -136,6 +127,42 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
return p; return p;
} }
/**
* Sets a flag indicating that given town/industry is part of subsidised route.
* @param type is it a town or an industry?
* @param index index of town/industry
* @param flag flag to set
*/
static inline void SetPartOfSubsidyFlag(SourceType type, SourceID index, PartOfSubsidy flag)
{
switch (type) {
case ST_INDUSTRY: Industry::Get(index)->part_of_subsidy |= flag; return;
case ST_TOWN: Town::Get(index)->part_of_subsidy |= flag; return;
default: NOT_REACHED();
}
}
void RebuildSubsidisedSourceAndDestinationCache()
{
Town *t;
FOR_ALL_TOWNS(t) t->part_of_subsidy = POS_NONE;
Industry *i;
FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
const Subsidy *s;
FOR_ALL_SUBSIDIES(s) {
SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
}
}
void DeleteSubsidy(Subsidy *s)
{
s->cargo_type = CT_INVALID;
RebuildSubsidisedSourceAndDestinationCache();
}
void DeleteSubsidyWith(SourceType type, SourceID index) void DeleteSubsidyWith(SourceType type, SourceID index)
{ {
bool dirty = false; bool dirty = false;
@ -252,23 +279,20 @@ void SubsidyMonthlyLoop()
Subsidy *s; Subsidy *s;
FOR_ALL_SUBSIDIES(s) { FOR_ALL_SUBSIDIES(s) {
if (s->age == 12 - 1) { if (--s->remaining == 0) {
Pair reftype = SetupSubsidyDecodeParam(s, 1); if (!s->IsAwarded()) {
AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
s->cargo_type = CT_INVALID;
modified = true;
AI::BroadcastNewEvent(new AIEventSubsidyOfferExpired(s->Index()));
} else if (s->age == 2 * 12 - 1) {
Station *st = Station::Get(s->dst);
if (st->owner == _local_company) {
Pair reftype = SetupSubsidyDecodeParam(s, 1); Pair reftype = SetupSubsidyDecodeParam(s, 1);
AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
AI::BroadcastNewEvent(new AIEventSubsidyOfferExpired(s->Index()));
} else {
if (s->awarded == _local_company) {
Pair reftype = SetupSubsidyDecodeParam(s, 1);
AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
}
AI::BroadcastNewEvent(new AIEventSubsidyExpired(s->Index()));
} }
s->cargo_type = CT_INVALID; DeleteSubsidy(s);
modified = true; modified = true;
AI::BroadcastNewEvent(new AIEventSubsidyExpired(s->Index()));
} else {
s->age++;
} }
} }
@ -306,9 +330,11 @@ void SubsidyMonthlyLoop()
} }
add_subsidy: add_subsidy:
if (!CheckSubsidyDuplicate(s)) { if (!CheckSubsidyDuplicate(s)) {
s->age = 0; s->remaining = 12;
Pair reftype = SetupSubsidyDecodeParam(s, 0); Pair reftype = SetupSubsidyDecodeParam(s, 0);
AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst); AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
AI::BroadcastNewEvent(new AIEventSubsidyOffer(s->Index())); AI::BroadcastNewEvent(new AIEventSubsidyOffer(s->Index()));
modified = true; modified = true;
break; break;
@ -321,51 +347,85 @@ no_add:;
InvalidateWindow(WC_SUBSIDIES_LIST, 0); InvalidateWindow(WC_SUBSIDIES_LIST, 0);
} }
bool CheckSubsidised(const Station *from, const Station *to, CargoID cargo_type, CompanyID company) /**
* Tests whether given delivery is subsidised and possibly awards the subsidy to delivering company
* @param cargo_type type of cargo
* @param company company delivering the cargo
* @param src_type type of #src
* @param src index of source
* @param st station where the cargo is delivered to
* @return is the delivery subsidised?
*/
bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
{ {
Subsidy *s; /* If the source isn't subsidised, don't continue */
TileIndex xy; if (src == INVALID_SOURCE) return false;
switch (src_type) {
case ST_INDUSTRY:
if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
break;
case ST_TOWN:
if (!( Town::Get(src)->part_of_subsidy & POS_SRC)) return false;
break;
default: return false;
}
/* check if there is an already existing subsidy that applies to us */ /* Remember all towns near this station (at least one house in its catchment radius)
FOR_ALL_SUBSIDIES(s) { * which are destination of subsidised path. Do that only if needed */
if (s->cargo_type == cargo_type && SmallVector<const Town *, 2> towns_near;
s->IsAwarded() && if (!st->rect.IsEmpty()) {
s->src == from->index && Subsidy *s;
s->dst == to->index) { FOR_ALL_SUBSIDIES(s) {
return true; /* Don't create the cache if there is no applicable subsidy with town as destination */
if (s->dst_type != ST_TOWN) continue;
if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
if (s->IsAwarded() && s->awarded != company) continue;
Rect rect = st->GetCatchmentRect();
for (int y = rect.top; y <= rect.bottom; y++) {
for (int x = rect.left; x <= rect.right; x++) {
TileIndex tile = TileXY(x, y);
if (!IsTileType(tile, MP_HOUSE)) continue;
const Town *t = Town::GetByTile(tile);
if (t->part_of_subsidy & POS_DST) towns_near.Include(t);
}
}
break;
} }
} }
/* check if there's a new subsidy that applies.. */ bool subsidised = false;
/* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
* Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
Subsidy *s;
FOR_ALL_SUBSIDIES(s) { FOR_ALL_SUBSIDIES(s) {
if (s->cargo_type == cargo_type && !s->IsAwarded()) { if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
/* Check distance from source */ switch (s->dst_type) {
const CargoSpec *cs = CargoSpec::Get(cargo_type); case ST_INDUSTRY:
if (cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) { for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
xy = Town::Get(s->src)->xy; if (s->dst == (*ip)->index) {
} else { assert((*ip)->part_of_subsidy & POS_DST);
xy = Industry::Get(s->src)->xy; subsidised = true;
} if (!s->IsAwarded()) s->AwardTo(company);
if (DistanceMax(xy, from->xy) > 9) continue; }
}
/* Check distance from dest */
switch (cs->town_effect) {
case TE_PASSENGERS:
case TE_MAIL:
case TE_GOODS:
case TE_FOOD:
xy = Town::Get(s->dst)->xy;
break; break;
case ST_TOWN:
default: for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
xy = Industry::Get(s->dst)->xy; if (s->dst == (*tp)->index) {
assert((*tp)->part_of_subsidy & POS_DST);
subsidised = true;
if (!s->IsAwarded()) s->AwardTo(company);
}
}
break; break;
default:
NOT_REACHED();
} }
if (DistanceMax(xy, to->xy) > 9) continue;
s->AwardTo(from->index, to->index, company);
return true;
} }
} }
return false;
return subsidised;
} }

@ -7,18 +7,17 @@
#include "cargo_type.h" #include "cargo_type.h"
#include "company_type.h" #include "company_type.h"
#include "station_type.h" #include "subsidy_type.h"
typedef uint16 SubsidyID; ///< ID of a subsidy
/** Struct about subsidies, offered and awarded */ /** Struct about subsidies, offered and awarded */
struct Subsidy { struct Subsidy {
CargoID cargo_type; ///< Cargo type involved in this subsidy, CT_INVALID for invalid subsidy CargoID cargo_type; ///< Cargo type involved in this subsidy, CT_INVALID for invalid subsidy
byte age; ///< Subsidy age; < 12 is unawarded, >= 12 is awarded byte remaining; ///< Remaining months when this subsidy is valid
SourceTypeByte src_type; ///< Source of subsidised path CompanyByte awarded; ///< Subsidy is awarded to this company; INVALID_COMPANY if it's not awarded to anyone
SourceTypeByte dst_type; ///< Destination of subsidised path SourceTypeByte src_type; ///< Source of subsidised path (ST_INDUSTRY or ST_TOWN)
uint16 src; ///< Index of source. Either TownID, IndustryID or StationID, when awarded SourceTypeByte dst_type; ///< Destination of subsidised path (ST_INDUSTRY or ST_TOWN)
uint16 dst; ///< Index of destination. Either TownID, IndustryID or StationID, when awarded SourceID src; ///< Index of source. Either TownID or IndustryID
SourceID dst; ///< Index of destination. Either TownID or IndustryID
/** /**
* Tests whether this subsidy has been awarded to someone * Tests whether this subsidy has been awarded to someone
@ -26,10 +25,10 @@ struct Subsidy {
*/ */
FORCEINLINE bool IsAwarded() const FORCEINLINE bool IsAwarded() const
{ {
return this->age >= 12; return this->awarded != INVALID_COMPANY;
} }
void AwardTo(StationID from, StationID to, CompanyID company); void AwardTo(CompanyID company);
/** /**
* Determines index of this subsidy * Determines index of this subsidy

@ -13,7 +13,9 @@
Pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode); Pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode);
void DeleteSubsidyWith(SourceType type, SourceID index); void DeleteSubsidyWith(SourceType type, SourceID index);
bool CheckSubsidised(const Station *from, const Station *to, CargoID cargo_type, CompanyID company); bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st);
void SubsidyMonthlyHandler(); void SubsidyMonthlyHandler();
void RebuildSubsidisedSourceAndDestinationCache();
void DeleteSubsidy(struct Subsidy *s);
#endif /* SUBSIDY_FUNC_H */ #endif /* SUBSIDY_FUNC_H */

@ -83,7 +83,6 @@ struct SubsidyListWindow : Window {
switch (s->src_type) { switch (s->src_type) {
case ST_INDUSTRY: xy = Industry::Get(s->src)->xy; break; case ST_INDUSTRY: xy = Industry::Get(s->src)->xy; break;
case ST_TOWN: xy = Town::Get(s->src)->xy; break; case ST_TOWN: xy = Town::Get(s->src)->xy; break;
case ST_STATION: xy = Station::Get(s->src)->xy; break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
@ -94,11 +93,9 @@ struct SubsidyListWindow : Window {
switch (s->dst_type) { switch (s->dst_type) {
case ST_INDUSTRY: xy = Industry::Get(s->dst)->xy; break; case ST_INDUSTRY: xy = Industry::Get(s->dst)->xy; break;
case ST_TOWN: xy = Town::Get(s->dst)->xy; break; case ST_TOWN: xy = Town::Get(s->dst)->xy; break;
case ST_STATION: xy = Station::Get(s->dst)->xy; break;
default: NOT_REACHED(); default: NOT_REACHED();
} }
if (_ctrl_pressed) { if (_ctrl_pressed) {
ShowExtraViewPortWindow(xy); ShowExtraViewPortWindow(xy);
} else { } else {
@ -129,7 +126,7 @@ struct SubsidyListWindow : Window {
if (!s->IsAwarded()) { if (!s->IsAwarded()) {
/* Displays the two offered towns */ /* Displays the two offered towns */
SetupSubsidyDecodeParam(s, 1); SetupSubsidyDecodeParam(s, 1);
SetDParam(7, _date - ymd.day + 384 - s->age * 32); SetDParam(7, _date - ymd.day + s->remaining * 32);
DrawString(x + 2, right - 2, y, STR_SUBSIDIES_OFFERED_FROM_TO); DrawString(x + 2, right - 2, y, STR_SUBSIDIES_OFFERED_FROM_TO);
y += FONT_HEIGHT_NORMAL; y += FONT_HEIGHT_NORMAL;
@ -150,8 +147,8 @@ struct SubsidyListWindow : Window {
FOR_ALL_SUBSIDIES(s) { FOR_ALL_SUBSIDIES(s) {
if (s->IsAwarded()) { if (s->IsAwarded()) {
SetupSubsidyDecodeParam(s, 1); SetupSubsidyDecodeParam(s, 1);
SetDParam(3, Station::Get(s->dst)->owner); SetDParam(7, s->awarded);
SetDParam(4, _date - ymd.day + 768 - s->age * 32); SetDParam(8, _date - ymd.day + s->remaining * 32);
/* Displays the two connected stations */ /* Displays the two connected stations */
DrawString(x + 2, right - 2, y, STR_SUBSIDIES_SUBSIDISED_FROM_TO); DrawString(x + 2, right - 2, y, STR_SUBSIDIES_SUBSIDISED_FROM_TO);

@ -0,0 +1,22 @@
/* $Id$ */
/** @file subsidy_type.h basic types related to subsidies */
#ifndef SUBSIDY_TYPE_H
#define SUBSIDY_TYPE_H
#include "core/enum_type.hpp"
enum PartOfSubsidy {
POS_NONE = 0,
POS_SRC = 1 << 0, ///< bit 0 set -> town/industry is source of subsidised path
POS_DST = 1 << 1, ///< bit 1 set -> town/industry is destination of subsidised path
};
typedef SimpleTinyEnumT<PartOfSubsidy, byte> PartOfSubsidyByte;
DECLARE_ENUM_AS_BIT_SET(PartOfSubsidy);
typedef uint16 SubsidyID; ///< ID of a subsidy
struct Subsidy;
#endif /* SUBSIDY_TYPE_H */

@ -20,6 +20,7 @@
#include "map_type.h" #include "map_type.h"
#include "command_type.h" #include "command_type.h"
#include "town_map.h" #include "town_map.h"
#include "subsidy_type.h"
template <typename T> template <typename T>
struct BuildingCounts { struct BuildingCounts {
@ -107,6 +108,8 @@ struct Town : TownPool::PoolItem<&_town_pool> {
bool larger_town; bool larger_town;
TownLayoutByte layout; ///< town specific road layout TownLayoutByte layout; ///< town specific road layout
PartOfSubsidyByte part_of_subsidy; ///< NOSAVE: is this town a source/destination of a subsidy?
/* NOSAVE: UpdateTownRadius updates this given the house count. */ /* NOSAVE: UpdateTownRadius updates this given the house count. */
uint32 squared_town_zone_radius[HZB_END]; uint32 squared_town_zone_radius[HZB_END];

@ -99,7 +99,7 @@ Town::~Town()
} }
DeleteSubsidyWith(ST_TOWN, this->index); DeleteSubsidyWith(ST_TOWN, this->index);
CargoPacket::InvalidateAllFrom(ST_TOWN, this->index);
MarkWholeScreenDirty(); MarkWholeScreenDirty();
} }
@ -460,7 +460,7 @@ static void TileLoop_Town(TileIndex tile)
uint amt = GB(callback, 0, 8); uint amt = GB(callback, 0, 8);
if (amt == 0) continue; if (amt == 0) continue;
uint moved = MoveGoodsToStation(tile, 1, 1, cargo, amt); uint moved = MoveGoodsToStation(tile, 1, 1, cargo, amt, ST_TOWN, t->index);
const CargoSpec *cs = CargoSpec::Get(cargo); const CargoSpec *cs = CargoSpec::Get(cargo);
switch (cs->town_effect) { switch (cs->town_effect) {
@ -484,7 +484,7 @@ static void TileLoop_Town(TileIndex tile)
if (_economy.fluct <= 0) amt = (amt + 1) >> 1; if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
t->new_max_pass += amt; t->new_max_pass += amt;
t->new_act_pass += MoveGoodsToStation(tile, 1, 1, CT_PASSENGERS, amt); t->new_act_pass += MoveGoodsToStation(tile, 1, 1, CT_PASSENGERS, amt, ST_TOWN, t->index);
} }
if (GB(r, 8, 8) < hs->mail_generation) { if (GB(r, 8, 8) < hs->mail_generation) {
@ -492,7 +492,7 @@ static void TileLoop_Town(TileIndex tile)
if (_economy.fluct <= 0) amt = (amt + 1) >> 1; if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
t->new_max_mail += amt; t->new_max_mail += amt;
t->new_act_mail += MoveGoodsToStation(tile, 1, 1, CT_MAIL, amt); t->new_act_mail += MoveGoodsToStation(tile, 1, 1, CT_MAIL, amt, ST_TOWN, t->index);
} }
} }

@ -23,6 +23,7 @@
#include "cheat_type.h" #include "cheat_type.h"
#include "landscape_type.h" #include "landscape_type.h"
#include "unmovable.h" #include "unmovable.h"
#include "cargopacket.h"
#include "table/strings.h" #include "table/strings.h"
#include "table/sprites.h" #include "table/sprites.h"
@ -62,6 +63,8 @@ static CommandCost DestroyCompanyHQ(CompanyID cid, DoCommandFlag flags)
DoClearSquare(t + TileDiffXY(1, 1)); DoClearSquare(t + TileDiffXY(1, 1));
c->location_of_HQ = INVALID_TILE; // reset HQ position c->location_of_HQ = INVALID_TILE; // reset HQ position
InvalidateWindow(WC_COMPANY, cid); InvalidateWindow(WC_COMPANY, cid);
CargoPacket::InvalidateAllFrom(ST_HEADQUARTERS, cid);
} }
/* cost of relocating company is 1% of company value */ /* cost of relocating company is 1% of company value */
@ -335,7 +338,7 @@ static void TileLoop_Unmovable(TileIndex tile)
if (GB(r, 0, 8) < (256 / 4 / (6 - level))) { if (GB(r, 0, 8) < (256 / 4 / (6 - level))) {
uint amt = GB(r, 0, 8) / 8 / 4 + 1; uint amt = GB(r, 0, 8) / 8 / 4 + 1;
if (_economy.fluct <= 0) amt = (amt + 1) >> 1; if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
MoveGoodsToStation(tile, 2, 2, CT_PASSENGERS, amt); MoveGoodsToStation(tile, 2, 2, CT_PASSENGERS, amt, ST_HEADQUARTERS, GetTileOwner(tile));
} }
/* Top town building generates 90, HQ can make up to 196. The /* Top town building generates 90, HQ can make up to 196. The
@ -344,7 +347,7 @@ static void TileLoop_Unmovable(TileIndex tile)
if (GB(r, 8, 8) < (196 / 4 / (6 - level))) { if (GB(r, 8, 8) < (196 / 4 / (6 - level))) {
uint amt = GB(r, 8, 8) / 8 / 4 + 1; uint amt = GB(r, 8, 8) / 8 / 4 + 1;
if (_economy.fluct <= 0) amt = (amt + 1) >> 1; if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
MoveGoodsToStation(tile, 2, 2, CT_MAIL, amt); MoveGoodsToStation(tile, 2, 2, CT_MAIL, amt, ST_HEADQUARTERS, GetTileOwner(tile));
} }
} }

Loading…
Cancel
Save