(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"
>
</File>
<File
RelativePath=".\..\src\subsidy_type.h"
>
</File>
<File
RelativePath=".\..\src\tar_type.h"
>

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

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

@ -29,6 +29,8 @@
* \li WaypointID was replaced by StationID. All WaypointIDs from previous
* savegames are invalid
* \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
* of their own constants
* \li AIVehicleList_Station now also works for waypoints

@ -4,6 +4,7 @@
#include "ai_subsidy.hpp"
#include "ai_date.hpp"
#include "ai_log.hpp"
#include "../../subsidy_base.h"
#include "../../station_base.h"
#include "../../cargotype.h"
@ -24,7 +25,7 @@
{
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)
@ -34,11 +35,7 @@
int year = AIDate::GetYear(AIDate::GetCurrentDate());
int month = AIDate::GetMonth(AIDate::GetCurrentDate());
if (IsAwarded(subsidy_id)) {
month += 24 - ::Subsidy::Get(subsidy_id)->age;
} else {
month += 12 - ::Subsidy::Get(subsidy_id)->age;
}
month += ::Subsidy::Get(subsidy_id)->remaining;
year += (month - 1) / 12;
month = ((month - 1) % 12) + 1;
@ -64,6 +61,11 @@
{
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;
}
@ -78,5 +80,10 @@
{
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;
}

@ -69,12 +69,12 @@ public:
/**
* Return the source TownID/IndustryID/StationID the subsidy is for.
* 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to.
* 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID.
* 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID.
* \li IsAwarded(subsidy_id) -> return INVALID_STATION.
* \li !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID.
* \li !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID.
* @param subsidy_id The SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id).
* @return One of TownID/IndustryID/StationID.
* @return One of TownID/IndustryID/INVALID_STATION.
*/
static int32 GetSource(SubsidyID subsidy_id);
@ -88,12 +88,12 @@ public:
/**
* Return the destination TownID/IndustryID/StationID the subsidy is for.
* 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to.
* 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID.
* 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID.
* \li IsAwarded(subsidy_id) -> return INVALID_STATION.
* \li !IsAwarded(subsidy_id) && DestinationIsTown(subsidy_id) -> return the TownID.
* \li !IsAwarded(subsidy_id) && !DestinationIsTown(subsidy_id) -> return the IndustryID.
* @param subsidy_id the SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id).
* @return One of TownID/IndustryID/StationID.
* @return One of TownID/IndustryID/INVALID_STATION.
*/
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 {
ST_INDUSTRY, ///< Source/destination is an industry
ST_TOWN, ///< Source/destination is a town
ST_STATION, ///< Source/destination is a station
ST_INDUSTRY, ///< Source/destination is an industry
ST_TOWN, ///< Source/destination is a town
ST_HEADQUARTERS, ///< Source/destination are company headquarters
};
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
#endif /* CARGO_TYPE_H */

@ -16,17 +16,33 @@ void InitializeCargoPackets()
_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);
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->loaded_at_xy = this->source_xy;
this->source = source;
this->count = count;
this->days_in_transit = 0;
this->feeder_share = 0;
// this->days_in_transit = 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->feeder_share = fs;
cp_new->source_type = cp->source_type;
cp_new->source_id = cp->source_id;
cp_new->count = count;
dest->packets.push_back(cp_new);

@ -9,6 +9,7 @@
#include "economy_type.h"
#include "tile_type.h"
#include "station_type.h"
#include "cargo_type.h"
#include <list>
typedef uint32 CargoPacketID;
@ -30,13 +31,16 @@ struct CargoPacket : CargoPacketPool::PoolItem<&_cargopacket_pool> {
uint16 count; ///< The amount of cargo in this packet
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
* @param source the source of the packet
* @param count the number of cargo entities to put in this packet
* @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 */
~CargoPacket() { }
@ -49,8 +53,11 @@ struct CargoPacket : CargoPacketPool::PoolItem<&_cargopacket_pool> {
*/
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
}
if (new_owner == INVALID_OWNER) {
Subsidy *s;
FOR_ALL_SUBSIDIES(s) {
if (s->IsAwarded() && Station::Get(s->dst)->owner == old_owner) {
s->cargo_type = CT_INVALID;
Subsidy *s;
FOR_ALL_SUBSIDIES(s) {
if (s->awarded == old_owner) {
if (new_owner == INVALID_OWNER) {
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
* @param num_pieces amount of cargo delivered
* @param source Originstation of the cargo
* @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
* 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);
/* Update company statistics */
company->cur_economy.delivered_cargo += num_pieces;
SetBit(company->cargo_types, cargo_type);
const Station *s_to = 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);
}
const Station *st = Station::Get(dest);
/* Increase town's counter for some special goods types */
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_WATER) s_to->town->new_act_water += num_pieces;
if (cs->town_effect == TE_FOOD) st->town->new_act_food += num_pieces;
if (cs->town_effect == TE_WATER) st->town->new_act_water += num_pieces;
/* Give the goods to the industry. */
DeliverGoodsToIndustry(s_to, cargo_type, num_pieces);
DeliverGoodsToIndustry(st, cargo_type, num_pieces);
/* 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 */
if (subsidised) {
if (CheckSubsidised(cargo_type, company->index, src_type, src, st)) {
switch (_settings_game.difficulty.subsidy_multiplier) {
case 0: profit += profit >> 1; break;
case 1: profit *= 2; break;
@ -1051,7 +1046,7 @@ void CargoPayment::PayFinalDelivery(CargoPacket *cp, uint count)
}
/* 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;
/* 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);
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 LoadUnloadStation(Station *st);

@ -19,6 +19,7 @@
#include "tile_type.h"
#include "company_type.h"
#include "strings_type.h"
#include "subsidy_type.h"
enum {
INVALID_INDUSTRY = 0xFFFF,
@ -125,6 +126,8 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
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
PartOfSubsidyByte part_of_subsidy; ///< NOSAVE: is this industry a source/destination of a subsidy?
OwnerByte 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)

@ -167,11 +167,12 @@ Industry::~Industry()
DecIndustryTypeCount(this->type);
DeleteSubsidyWith(ST_INDUSTRY, this->index);
DeleteIndustryNews(this->index);
DeleteWindowById(WC_INDUSTRY_VIEW, this->index);
InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0);
DeleteSubsidyWith(ST_INDUSTRY, this->index);
CargoPacket::InvalidateAllFrom(ST_INDUSTRY, this->index);
Station::RecomputeIndustriesNearForAll();
}
@ -479,7 +480,7 @@ static void TransportIndustryGoods(TileIndex tile)
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;
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_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_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_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_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_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_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 {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 {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 {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!
@ -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_NONE :{ORANGE}None
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
# Station list window

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

@ -14,6 +14,8 @@ static const SaveLoad _cargopacket_desc[] = {
SLE_VAR(CargoPacket, count, SLE_UINT16),
SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8),
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. */
SLE_CONDNULL(1, 0, 120),

@ -1468,7 +1468,7 @@ static bool LoadOldEngineName(LoadgameState *ls, int num)
static const OldChunks subsidy_chunk[] = {
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, dst ),

@ -41,7 +41,7 @@
#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

@ -9,11 +9,14 @@
static const SaveLoad _subsidies_desc[] = {
SLE_VAR(Subsidy, cargo_type, SLE_UINT8),
SLE_VAR(Subsidy, age, SLE_UINT8),
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_VAR(Subsidy, remaining, SLE_UINT8),
SLE_CONDVAR(Subsidy, awarded, SLE_UINT8, 125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, src_type, SLE_UINT8, 125, SL_MAX_VERSION),
SLE_CONDVAR(Subsidy, dst_type, SLE_UINT8, 125, 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()
};

@ -90,9 +90,6 @@ Station::~Station()
/* Now delete all orders that go to the station */
RemoveOrderFromAllVehicles(OT_GOTO_STATION, this->index);
/* Subsidies need removal as well */
DeleteSubsidyWith(ST_STATION, this->index);
/* Remove all news items */
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);
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. */
if (amount == 0) return 0;
@ -2968,7 +2968,7 @@ uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
if (st2 == NULL) {
/* only one station around */
uint moved = amount * best_rating1 / 256 + 1;
UpdateStationWaiting(st1, type, moved);
UpdateStationWaiting(st1, type, moved, source_type, source_id);
return moved;
}
@ -2987,13 +2987,13 @@ uint MoveGoodsToStation(TileIndex tile, int w, int h, CargoID type, uint amount)
if (t != 0) {
moved = t * best_rating1 / 256 + 1;
amount -= t;
UpdateStationWaiting(st1, type, moved);
UpdateStationWaiting(st1, type, moved, source_type, source_id);
}
if (amount != 0) {
amount = amount * best_rating2 / 256 + 1;
moved += amount;
UpdateStationWaiting(st2, type, amount);
UpdateStationWaiting(st2, type, amount, source_type, source_id);
}
return moved;

@ -22,27 +22,23 @@
/**
* Marks subsidy as awarded, creates news and AI event
* @param from source station
* @param to destination station
* @param company awarded company
*/
void Subsidy::AwardTo(StationID from, StationID to, CompanyID company)
void Subsidy::AwardTo(CompanyID company)
{
assert(!this->IsAwarded());
this->age = 12;
this->src_type = this->dst_type = ST_STATION;
this->src = from;
this->dst = to;
/* Add a news item */
Pair reftype = SetupSubsidyDecodeParam(this, 0);
InjectDParam(1);
this->awarded = company;
this->remaining = 12;
char *company_name = MallocT<char>(MAX_LENGTH_COMPANY_NAME_BYTES);
SetDParam(0, company);
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);
AddNewsItem(
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()
{
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;
@ -105,10 +104,6 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
reftype1 = NR_TOWN;
SetDParam(1, STR_TOWN_NAME);
break;
case ST_STATION:
reftype1 = NR_STATION;
SetDParam(1, s->src);
break;
default: NOT_REACHED();
}
SetDParam(2, s->src);
@ -122,10 +117,6 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
reftype2 = NR_TOWN;
SetDParam(4, STR_TOWN_NAME);
break;
case ST_STATION:
reftype2 = NR_STATION;
SetDParam(2, s->dst);
break;
default: NOT_REACHED();
}
SetDParam(5, s->dst);
@ -136,6 +127,42 @@ Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
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)
{
bool dirty = false;
@ -252,23 +279,20 @@ void SubsidyMonthlyLoop()
Subsidy *s;
FOR_ALL_SUBSIDIES(s) {
if (s->age == 12 - 1) {
Pair reftype = SetupSubsidyDecodeParam(s, 1);
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) {
if (--s->remaining == 0) {
if (!s->IsAwarded()) {
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;
AI::BroadcastNewEvent(new AIEventSubsidyExpired(s->Index()));
} else {
s->age++;
}
}
@ -306,9 +330,11 @@ void SubsidyMonthlyLoop()
}
add_subsidy:
if (!CheckSubsidyDuplicate(s)) {
s->age = 0;
s->remaining = 12;
Pair reftype = SetupSubsidyDecodeParam(s, 0);
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()));
modified = true;
break;
@ -321,51 +347,85 @@ no_add:;
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;
TileIndex xy;
/* If the source isn't subsidised, don't continue */
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 */
FOR_ALL_SUBSIDIES(s) {
if (s->cargo_type == cargo_type &&
s->IsAwarded() &&
s->src == from->index &&
s->dst == to->index) {
return true;
/* Remember all towns near this station (at least one house in its catchment radius)
* which are destination of subsidised path. Do that only if needed */
SmallVector<const Town *, 2> towns_near;
if (!st->rect.IsEmpty()) {
Subsidy *s;
FOR_ALL_SUBSIDIES(s) {
/* 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) {
if (s->cargo_type == cargo_type && !s->IsAwarded()) {
/* Check distance from source */
const CargoSpec *cs = CargoSpec::Get(cargo_type);
if (cs->town_effect == TE_PASSENGERS || cs->town_effect == TE_MAIL) {
xy = Town::Get(s->src)->xy;
} else {
xy = Industry::Get(s->src)->xy;
}
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;
if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
switch (s->dst_type) {
case ST_INDUSTRY:
for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
if (s->dst == (*ip)->index) {
assert((*ip)->part_of_subsidy & POS_DST);
subsidised = true;
if (!s->IsAwarded()) s->AwardTo(company);
}
}
break;
default:
xy = Industry::Get(s->dst)->xy;
case ST_TOWN:
for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
if (s->dst == (*tp)->index) {
assert((*tp)->part_of_subsidy & POS_DST);
subsidised = true;
if (!s->IsAwarded()) s->AwardTo(company);
}
}
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 "company_type.h"
#include "station_type.h"
typedef uint16 SubsidyID; ///< ID of a subsidy
#include "subsidy_type.h"
/** Struct about subsidies, offered and awarded */
struct 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
SourceTypeByte src_type; ///< Source of subsidised path
SourceTypeByte dst_type; ///< Destination of subsidised path
uint16 src; ///< Index of source. Either TownID, IndustryID or StationID, when awarded
uint16 dst; ///< Index of destination. Either TownID, IndustryID or StationID, when awarded
byte remaining; ///< Remaining months when this subsidy is valid
CompanyByte awarded; ///< Subsidy is awarded to this company; INVALID_COMPANY if it's not awarded to anyone
SourceTypeByte src_type; ///< Source of subsidised path (ST_INDUSTRY or ST_TOWN)
SourceTypeByte dst_type; ///< Destination of subsidised path (ST_INDUSTRY or ST_TOWN)
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
@ -26,10 +25,10 @@ struct Subsidy {
*/
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

@ -13,7 +13,9 @@
Pair SetupSubsidyDecodeParam(const struct Subsidy *s, bool mode);
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 RebuildSubsidisedSourceAndDestinationCache();
void DeleteSubsidy(struct Subsidy *s);
#endif /* SUBSIDY_FUNC_H */

@ -83,7 +83,6 @@ struct SubsidyListWindow : Window {
switch (s->src_type) {
case ST_INDUSTRY: xy = Industry::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();
}
@ -94,11 +93,9 @@ struct SubsidyListWindow : Window {
switch (s->dst_type) {
case ST_INDUSTRY: xy = Industry::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();
}
if (_ctrl_pressed) {
ShowExtraViewPortWindow(xy);
} else {
@ -129,7 +126,7 @@ struct SubsidyListWindow : Window {
if (!s->IsAwarded()) {
/* Displays the two offered towns */
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);
y += FONT_HEIGHT_NORMAL;
@ -150,8 +147,8 @@ struct SubsidyListWindow : Window {
FOR_ALL_SUBSIDIES(s) {
if (s->IsAwarded()) {
SetupSubsidyDecodeParam(s, 1);
SetDParam(3, Station::Get(s->dst)->owner);
SetDParam(4, _date - ymd.day + 768 - s->age * 32);
SetDParam(7, s->awarded);
SetDParam(8, _date - ymd.day + s->remaining * 32);
/* Displays the two connected stations */
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 "command_type.h"
#include "town_map.h"
#include "subsidy_type.h"
template <typename T>
struct BuildingCounts {
@ -107,6 +108,8 @@ struct Town : TownPool::PoolItem<&_town_pool> {
bool larger_town;
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. */
uint32 squared_town_zone_radius[HZB_END];

@ -99,7 +99,7 @@ Town::~Town()
}
DeleteSubsidyWith(ST_TOWN, this->index);
CargoPacket::InvalidateAllFrom(ST_TOWN, this->index);
MarkWholeScreenDirty();
}
@ -460,7 +460,7 @@ static void TileLoop_Town(TileIndex tile)
uint amt = GB(callback, 0, 8);
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);
switch (cs->town_effect) {
@ -484,7 +484,7 @@ static void TileLoop_Town(TileIndex tile)
if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
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) {
@ -492,7 +492,7 @@ static void TileLoop_Town(TileIndex tile)
if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
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 "landscape_type.h"
#include "unmovable.h"
#include "cargopacket.h"
#include "table/strings.h"
#include "table/sprites.h"
@ -62,6 +63,8 @@ static CommandCost DestroyCompanyHQ(CompanyID cid, DoCommandFlag flags)
DoClearSquare(t + TileDiffXY(1, 1));
c->location_of_HQ = INVALID_TILE; // reset HQ position
InvalidateWindow(WC_COMPANY, cid);
CargoPacket::InvalidateAllFrom(ST_HEADQUARTERS, cid);
}
/* 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))) {
uint amt = GB(r, 0, 8) / 8 / 4 + 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
@ -344,7 +347,7 @@ static void TileLoop_Unmovable(TileIndex tile)
if (GB(r, 8, 8) < (196 / 4 / (6 - level))) {
uint amt = GB(r, 8, 8) / 8 / 4 + 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