OpenTTD Source 20250312-master-gcdcc6b491d
subsidy.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7
10#include "stdafx.h"
11#include "company_func.h"
12#include "industry.h"
13#include "town.h"
14#include "news_func.h"
15#include "ai/ai.hpp"
16#include "station_base.h"
17#include "strings_func.h"
18#include "window_func.h"
19#include "subsidy_base.h"
20#include "subsidy_func.h"
21#include "core/pool_func.hpp"
22#include "core/random_func.hpp"
24#include "game/game.hpp"
25#include "command_func.h"
26#include "string_func.h"
27#include "tile_cmd.h"
28#include "subsidy_cmd.h"
29#include "timer/timer.h"
31
32#include "table/strings.h"
33
34#include "safeguards.h"
35
38
39
43NewsReference Source::GetNewsReference() const
44{
45 switch (this->type) {
46 case SourceType::Industry: return static_cast<IndustryID>(this->id);
47 case SourceType::Town: return static_cast<TownID>(this->id);
48 default: NOT_REACHED();
49 }
50}
51
57{
58 switch (this->type) {
59 case SourceType::Industry: return STR_INDUSTRY_NAME;
60 case SourceType::Town: return STR_TOWN_NAME;
61 default: NOT_REACHED();
62 }
63}
64
70{
71 assert(!this->IsAwarded());
72
73 this->awarded = company;
75
76 std::string company_name = GetString(STR_COMPANY_NAME, company);
77
78 /* Add a news item */
79 const CargoSpec *cs = CargoSpec::Get(this->cargo_type);
80 EncodedString headline = GetEncodedString(STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier, std::move(company_name), cs->name, this->src.GetFormat(), this->src.id, this->dst.GetFormat(), this->dst.id, _settings_game.difficulty.subsidy_duration);
82 AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index));
83 Game::NewEvent(new ScriptEventSubsidyAwarded(this->index));
84
86}
87
93static inline void SetPartOfSubsidyFlag(Source source, PartOfSubsidy flag)
94{
95 switch (source.type) {
96 case SourceType::Industry: Industry::Get(source.ToIndustryID())->part_of_subsidy.Set(flag); return;
97 case SourceType::Town: Town::Get(source.ToTownID())->cache.part_of_subsidy.Set(flag); return;
98 default: NOT_REACHED();
99 }
100}
101
104{
105 for (Town *t : Town::Iterate()) t->cache.part_of_subsidy = {};
106
107 for (Industry *i : Industry::Iterate()) i->part_of_subsidy = {};
108
109 for (const Subsidy *s : Subsidy::Iterate()) {
112 }
113}
114
120{
121 bool dirty = false;
122
123 for (Subsidy *s : Subsidy::Iterate()) {
124 if (s->src == source || s->dst == source) {
125 delete s;
126 dirty = true;
127 }
128 }
129
130 if (dirty) {
133 }
134}
135
143static bool CheckSubsidyDuplicate(CargoType cargo, Source src, Source dst)
144{
145 for (const Subsidy *s : Subsidy::Iterate()) {
146 if (s->cargo_type == cargo && s->src == src && s->dst == dst) {
147 return true;
148 }
149 }
150 return false;
151}
152
159static bool CheckSubsidyDistance(Source src, Source dst)
160{
161 TileIndex tile_src = (src.type == SourceType::Town) ? Town::Get(src.ToTownID())->xy : Industry::Get(src.ToIndustryID())->location.tile;
162 TileIndex tile_dst = (dst.type == SourceType::Town) ? Town::Get(dst.ToTownID())->xy : Industry::Get(dst.ToIndustryID())->location.tile;
163
164 return (DistanceManhattan(tile_src, tile_dst) <= SUBSIDY_MAX_DISTANCE);
165}
166
173void CreateSubsidy(CargoType cargo_type, Source src, Source dst)
174{
175 Subsidy *s = new Subsidy(cargo_type, src, dst, SUBSIDY_OFFER_MONTHS);
176
177 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
178 EncodedString headline = GetEncodedString(STR_NEWS_SERVICE_SUBSIDY_OFFERED, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id, _settings_game.difficulty.subsidy_duration);
182 AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
183 Game::NewEvent(new ScriptEventSubsidyOffer(s->index));
184
186}
187
197{
198 if (!Subsidy::CanAllocateItem()) return CMD_ERROR;
199
201
202 if (cargo_type >= NUM_CARGO || !::CargoSpec::Get(cargo_type)->IsValid()) return CMD_ERROR;
203
204 switch (src.type) {
205 case SourceType::Town:
206 if (!Town::IsValidID(src.ToTownID())) return CMD_ERROR;
207 break;
209 if (!Industry::IsValidID(src.ToIndustryID())) return CMD_ERROR;
210 break;
211 default:
212 return CMD_ERROR;
213 }
214 switch (dst.type) {
215 case SourceType::Town:
216 if (!Town::IsValidID(dst.ToTownID())) return CMD_ERROR;
217 break;
219 if (!Industry::IsValidID(dst.ToIndustryID())) return CMD_ERROR;
220 break;
221 default:
222 return CMD_ERROR;
223 }
224
225 if (flags.Test(DoCommandFlag::Execute)) {
226 CreateSubsidy(cargo_type, src, dst);
227 }
228
229 return CommandCost();
230}
231
237{
238 if (!Subsidy::CanAllocateItem()) return false;
239
240 /* Pick a random TPE_PASSENGER type */
241 uint32_t r = RandomRange(static_cast<uint>(CargoSpec::town_production_cargoes[TPE_PASSENGERS].size()));
243
244 const Town *src = Town::GetRandom();
246 src->GetPercentTransported(cargo_type) > SUBSIDY_MAX_PCT_TRANSPORTED) {
247 return false;
248 }
249
250 const Town *dst = Town::GetRandom();
251 if (dst->cache.population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
252 return false;
253 }
254
255 if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return false;
256 if (CheckSubsidyDuplicate(cargo_type, {src->index, SourceType::Town}, {dst->index, SourceType::Town})) return false;
257
258 CreateSubsidy(cargo_type, {src->index, SourceType::Town}, {dst->index, SourceType::Town});
259
260 return true;
261}
262
263bool FindSubsidyCargoDestination(CargoType cargo_type, Source src);
264
265
271{
272 if (!Subsidy::CanAllocateItem()) return false;
273
274 /* Select a random town. */
275 const Town *src_town = Town::GetRandom();
276 if (src_town->cache.population < SUBSIDY_CARGO_MIN_POPULATION) return false;
277
278 /* Calculate the produced cargo of houses around town center. */
279 CargoArray town_cargo_produced{};
280 TileArea ta = TileArea(src_town->xy, 1, 1).Expand(SUBSIDY_TOWN_CARGO_RADIUS);
281 for (TileIndex tile : ta) {
282 if (IsTileType(tile, MP_HOUSE)) {
283 AddProducedCargo(tile, town_cargo_produced);
284 }
285 }
286
287 /* Passenger subsidies are not handled here. */
289 town_cargo_produced[cs->Index()] = 0;
290 }
291
292 uint8_t cargo_count = town_cargo_produced.GetCount();
293
294 /* No cargo produced at all? */
295 if (cargo_count == 0) return false;
296
297 /* Choose a random cargo that is produced in the town. */
298 uint8_t cargo_number = RandomRange(cargo_count);
299 CargoType cargo_type;
300 for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
301 if (town_cargo_produced[cargo_type] > 0) {
302 if (cargo_number == 0) break;
303 cargo_number--;
304 }
305 }
306
307 /* Avoid using invalid NewGRF cargoes. */
308 if (!CargoSpec::Get(cargo_type)->IsValid() ||
309 _settings_game.linkgraph.GetDistributionType(cargo_type) != DT_MANUAL) {
310 return false;
311 }
312
313 /* Quit if the percentage transported is large enough. */
314 if (src_town->GetPercentTransported(cargo_type) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
315
316 return FindSubsidyCargoDestination(cargo_type, {src_town->index, SourceType::Town});
317}
318
324{
325 if (!Subsidy::CanAllocateItem()) return false;
326
327 /* Select a random industry. */
328 const Industry *src_ind = Industry::GetRandom();
329 if (src_ind == nullptr) return false;
330
331 uint trans, total;
332
333 CargoType cargo_type;
334
335 /* Randomize cargo type */
336 int num_cargos = std::ranges::count_if(src_ind->produced, [](const auto &p) { return IsValidCargoType(p.cargo); });
337 if (num_cargos == 0) return false; // industry produces nothing
338 int cargo_num = RandomRange(num_cargos) + 1;
339
340 auto it = std::begin(src_ind->produced);
341 for (/* nothing */; it != std::end(src_ind->produced); ++it) {
342 if (IsValidCargoType(it->cargo)) cargo_num--;
343 if (cargo_num == 0) break;
344 }
345 assert(it != std::end(src_ind->produced)); // indicates loop didn't end as intended
346
347 cargo_type = it->cargo;
348 trans = it->history[LAST_MONTH].PctTransported();
349 total = it->history[LAST_MONTH].production;
350
351 /* Quit if no production in this industry
352 * or if the pct transported is already large enough
353 * or if the cargo is automatically distributed */
354 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED ||
355 !IsValidCargoType(cargo_type) ||
356 _settings_game.linkgraph.GetDistributionType(cargo_type) != DT_MANUAL) {
357 return false;
358 }
359
360 return FindSubsidyCargoDestination(cargo_type, {src_ind->index, SourceType::Industry});
361}
362
370{
371 /* Choose a random destination. */
373
374 switch (dst.type) {
375 case SourceType::Town: {
376 /* Select a random town. */
377 const Town *dst_town = Town::GetRandom();
378
379 /* Calculate cargo acceptance of houses around town center. */
380 CargoArray town_cargo_accepted{};
381 TileArea ta = TileArea(dst_town->xy, 1, 1).Expand(SUBSIDY_TOWN_CARGO_RADIUS);
382 for (TileIndex tile : ta) {
383 if (IsTileType(tile, MP_HOUSE)) {
384 AddAcceptedCargo(tile, town_cargo_accepted, nullptr);
385 }
386 }
387
388 /* Check if the town can accept this cargo. */
389 if (town_cargo_accepted[cargo_type] < 8) return false;
390
391 dst.SetIndex(dst_town->index);
392 break;
393 }
394
396 /* Select a random industry. */
397 const Industry *dst_ind = Industry::GetRandom();
398 if (dst_ind == nullptr) return false;
399
400 /* The industry must accept the cargo */
401 if (!dst_ind->IsCargoAccepted(cargo_type)) return false;
402
403 dst.SetIndex(dst_ind->index);
404 break;
405 }
406
407 default: NOT_REACHED();
408 }
409
410 /* Check that the source and the destination are not the same. */
411 if (src == dst) return false;
412
413 /* Check distance between source and destination. */
414 if (!CheckSubsidyDistance(src, dst)) return false;
415
416 /* Avoid duplicate subsidies. */
417 if (CheckSubsidyDuplicate(cargo_type, src, dst)) return false;
418
419 CreateSubsidy(cargo_type, src, dst);
420
421 return true;
422}
423
425static IntervalTimer<TimerGameEconomy> _economy_subsidies_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::SUBSIDY}, [](auto)
426{
427 bool modified = false;
428
429 for (Subsidy *s : Subsidy::Iterate()) {
430 if (--s->remaining == 0) {
431 if (!s->IsAwarded()) {
432 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
433 EncodedString headline = GetEncodedString(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id);
434 AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference());
435 AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
436 Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
437 } else {
438 if (s->awarded == _local_company) {
439 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
440 EncodedString headline = GetEncodedString(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id);
441 AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference());
442 }
443 AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
444 Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
445 }
446 delete s;
447 modified = true;
448 }
449 }
450
451 if (modified) {
454 /* If subsidy duration is set to 0, subsidies are disabled, so bail out. */
455 return;
460 /* Return early if there are no manually distributed cargoes and if we
461 * don't need to invalidate the subsidies window. */
462 return;
463 }
464
465 bool passenger_subsidy = false;
466 bool town_subsidy = false;
467 bool industry_subsidy = false;
468
469 int random_chance = RandomRange(16);
470
471 if (random_chance < 2 && _settings_game.linkgraph.distribution_pax == DT_MANUAL) {
472 /* There is a 1/8 chance each month of generating a passenger subsidy. */
473 int n = 1000;
474
475 do {
476 passenger_subsidy = FindSubsidyPassengerRoute();
477 } while (!passenger_subsidy && n--);
478 } else if (random_chance == 2) {
479 /* Cargo subsidies with a town as a source have a 1/16 chance. */
480 int n = 1000;
481
482 do {
483 town_subsidy = FindSubsidyTownCargoRoute();
484 } while (!town_subsidy && n--);
485 } else if (random_chance == 3) {
486 /* Cargo subsidies with an industry as a source have a 1/16 chance. */
487 int n = 1000;
488
489 do {
490 industry_subsidy = FindSubsidyIndustryCargoRoute();
491 } while (!industry_subsidy && n--);
492 }
493
494 modified |= passenger_subsidy || town_subsidy || industry_subsidy;
495
496 if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
497});
498
507bool CheckSubsidised(CargoType cargo_type, CompanyID company, Source src, const Station *st)
508{
509 /* If the source isn't subsidised, don't continue */
510 if (!src.IsValid()) return false;
511 switch (src.type) {
513 if (!Industry::Get(src.ToIndustryID())->part_of_subsidy.Test(PartOfSubsidy::Source)) return false;
514 break;
515 case SourceType::Town:
516 if (!Town::Get(src.ToTownID())->cache.part_of_subsidy.Test(PartOfSubsidy::Source)) return false;
517 break;
518 default: return false;
519 }
520
521 /* Remember all towns near this station (at least one house in its catchment radius)
522 * which are destination of subsidised path. Do that only if needed */
523 std::vector<const Town *> towns_near;
524 if (!st->rect.IsEmpty()) {
525 for (const Subsidy *s : Subsidy::Iterate()) {
526 /* Don't create the cache if there is no applicable subsidy with town as destination */
527 if (s->dst.type != SourceType::Town) continue;
528 if (s->cargo_type != cargo_type || s->src != src) continue;
529 if (s->IsAwarded() && s->awarded != company) continue;
530
532 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
533 if (!IsTileType(tile, MP_HOUSE)) continue;
534 const Town *t = Town::GetByTile(tile);
536 }
537 break;
538 }
539 }
540
541 bool subsidised = false;
542
543 /* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
544 * Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
545 for (Subsidy *s : Subsidy::Iterate()) {
546 if (s->cargo_type == cargo_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
547 switch (s->dst.type) {
549 for (const auto &i : st->industries_near) {
550 if (s->dst.ToIndustryID() == i.industry->index) {
551 assert(i.industry->part_of_subsidy.Test(PartOfSubsidy::Destination));
552 subsidised = true;
553 if (!s->IsAwarded()) s->AwardTo(company);
554 }
555 }
556 break;
557 case SourceType::Town:
558 for (const Town *tp : towns_near) {
559 if (s->dst.ToTownID() == tp->index) {
560 assert(tp->cache.part_of_subsidy.Test(PartOfSubsidy::Destination));
561 subsidised = true;
562 if (!s->IsAwarded()) s->AwardTo(company);
563 }
564 }
565 break;
566 default:
567 NOT_REACHED();
568 }
569 }
570 }
571
572 return subsidised;
573}
Base functions for all AIs.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:23
bool IsValidCargoType(CargoType t)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:106
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
@ TPE_PASSENGERS
Cargo behaves passenger-like for production.
Definition cargotype.h:37
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company=CompanyID::Invalid())
Broadcast a new event to all active AIs.
Definition ai_core.cpp:263
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Iterator to iterate over all tiles belonging to a bitmaptilearea.
Common return value for all commands.
Container for an encoded string, created by GetEncodedString.
Enum-as-bit-set wrapper.
static void NewEvent(class ScriptEvent *event)
Queue a new event for a Game Script.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
static constexpr int MONTHS_IN_YEAR
months per year
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
static constexpr Owner OWNER_DEITY
The object is owned by a superuser / goal script.
Some simple functions to help with accessing containers.
bool include(Container &container, typename Container::const_reference &item)
Helper function to append an item to a container if it is not already contained.
Base functions for all Games.
Base of all industries.
@ DT_MANUAL
Manual distribution. No link graph calculations are run.
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:142
Functions related to news.
void AddNewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1={}, NewsReference ref2={}, std::unique_ptr< NewsAllocatedData > &&data=nullptr, AdviceType advice_type=AdviceType::Invalid)
Add a new newsitem to be shown.
Definition news_gui.cpp:902
@ Subsidies
News about subsidies (announcements, expirations, acceptance)
@ Normal
Normal news item. (Newspaper with text only)
std::variant< std::monostate, TileIndex, VehicleID, StationID, IndustryID, TownID, EngineID > NewsReference
References to objects in news.
Definition news_type.h:73
Some methods of Pool are placed here in order to reduce compilation time and binary size.
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
Pseudo random number generator.
uint32_t RandomRange(uint32_t limit, const std::source_location location=std::source_location::current())
Pick a random number between 0 and limit - 1, inclusive.
bool Chance16(const uint32_t a, const uint32_t b, const std::source_location location=std::source_location::current())
Flips a coin with given probability.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:58
@ Industry
Source/destination is an industry.
@ Town
Source/destination is a town.
Base classes/functions for stations.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
std::string GetString(StringID string)
Resolve the given StringID into a std::string with formatting but no parameters.
Definition strings.cpp:426
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
StationRect rect
NOSAVE: Station spread out rectangle maintained by StationRect::xxx() functions.
Class for storing amounts of cargo.
Definition cargo_type.h:113
Specification of a cargo type.
Definition cargotype.h:74
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
StringID name
Name of this type of cargo.
Definition cargotype.h:91
static std::array< std::vector< const CargoSpec * >, NUM_TPE > town_production_cargoes
List of cargo specs for each Town Product Effect.
Definition cargotype.h:27
uint16_t subsidy_duration
duration of subsidies
uint8_t subsidy_multiplier
payment multiplier for subsidized deliveries
DifficultySettings difficulty
settings related to the difficulty
LinkGraphSettings linkgraph
settings for link graph calculations
Defines the internal data of a functional industry.
Definition industry.h:63
static Industry * GetRandom()
Return a random valid industry.
bool IsCargoAccepted() const
Test if this industry accepts any cargo.
Definition industry.h:207
ProducedCargoes produced
produced cargo slots
Definition industry.h:94
DistributionType distribution_mail
distribution type for mail
DistributionType distribution_default
distribution type for all other goods
DistributionType distribution_pax
distribution type for passengers
DistributionType distribution_armoured
distribution type for armoured cargo class
Represents the covered area of e.g.
OrthogonalTileArea & Expand(int rad)
Expand a tile area by rad tiles in each direction, keeping within map bounds.
Definition tilearea.cpp:123
Templated helper to make a PoolID a single POD value.
Definition pool_type.hpp:43
static Pool::IterateWrapper< Titem > Iterate(size_t from=0)
Returns an iterable ensemble of all valid Titem.
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
static bool IsValidID(auto index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
Base class for all pools.
A location from where cargo can come from (or go to).
Definition source_type.h:32
static constexpr SourceID Invalid
Invalid/unknown index of source.
Definition source_type.h:34
SourceID id
Index of industry/town/HQ, Source::Invalid if unknown/invalid.
Definition source_type.h:36
StringID GetFormat() const
Get the format string for a subsidy Source.
Definition subsidy.cpp:56
NewsReference GetNewsReference() const
Get the NewsReference for a subsidy Source.
Definition subsidy.cpp:43
SourceType type
Type of source_id.
Definition source_type.h:37
Station data structure.
IndustryList industries_near
Cached list of industries near the station that can accept cargo,.
BitmapTileArea catchment_tiles
NOSAVE: Set of individual tiles covered by catchment area.
Struct about subsidies, offered and awarded.
bool IsAwarded() const
Tests whether this subsidy has been awarded to someone.
CargoType cargo_type
Cargo type involved in this subsidy, INVALID_CARGO for invalid subsidy.
CompanyID awarded
Subsidy is awarded to this company; CompanyID::Invalid() if it's not awarded to anyone.
void AwardTo(CompanyID company)
Marks subsidy as awarded, creates news and AI event.
Definition subsidy.cpp:69
Source dst
Destination of subsidised path.
Source src
Source of subsidised path.
uint16_t remaining
Remaining months when this subsidy is valid.
uint32_t population
Current population of people.
Definition town.h:42
PartsOfSubsidy part_of_subsidy
Is this town a source/destination of a subsidy?
Definition town.h:44
Town data structure.
Definition town.h:52
TileIndex xy
town center tile
Definition town.h:53
static Town * GetRandom()
Return a random valid town.
Definition town_cmd.cpp:197
TownCache cache
Container for all cacheable data.
Definition town.h:55
static bool CheckSubsidyDuplicate(CargoType cargo, Source src, Source dst)
Check whether a specific subsidy already exists.
Definition subsidy.cpp:143
CommandCost CmdCreateSubsidy(DoCommandFlags flags, CargoType cargo_type, Source src, Source dst)
Create a new subsidy.
Definition subsidy.cpp:196
static bool CheckSubsidyDistance(Source src, Source dst)
Checks if the source and destination of a subsidy are inside the distance limit.
Definition subsidy.cpp:159
static IntervalTimer< TimerGameEconomy > _economy_subsidies_monthly({TimerGameEconomy::MONTH, TimerGameEconomy::Priority::SUBSIDY}, [](auto) { bool modified=false;for(Subsidy *s :Subsidy::Iterate()) { if(--s->remaining==0) { if(!s->IsAwarded()) { const CargoSpec *cs=CargoSpec::Get(s->cargo_type);EncodedString headline=GetEncodedString(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id);AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference());AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));} else { if(s->awarded==_local_company) { const CargoSpec *cs=CargoSpec::Get(s->cargo_type);EncodedString headline=GetEncodedString(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id);AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference());} AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));Game::NewEvent(new ScriptEventSubsidyExpired(s->index));} delete s;modified=true;} } if(modified) { RebuildSubsidisedSourceAndDestinationCache();} else if(_settings_game.difficulty.subsidy_duration==0) { return;} else if(_settings_game.linkgraph.distribution_pax !=DT_MANUAL &&_settings_game.linkgraph.distribution_mail !=DT_MANUAL &&_settings_game.linkgraph.distribution_armoured !=DT_MANUAL &&_settings_game.linkgraph.distribution_default !=DT_MANUAL) { return;} bool passenger_subsidy=false;bool town_subsidy=false;bool industry_subsidy=false;int random_chance=RandomRange(16);if(random_chance< 2 &&_settings_game.linkgraph.distribution_pax==DT_MANUAL) { int n=1000;do { passenger_subsidy=FindSubsidyPassengerRoute();} while(!passenger_subsidy &&n--);} else if(random_chance==2) { int n=1000;do { town_subsidy=FindSubsidyTownCargoRoute();} while(!town_subsidy &&n--);} else if(random_chance==3) { int n=1000;do { industry_subsidy=FindSubsidyIndustryCargoRoute();} while(!industry_subsidy &&n--);} modified|=passenger_subsidy||town_subsidy||industry_subsidy;if(modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);})
Perform the economy monthly update of open subsidies, and try to create a new one.
static void SetPartOfSubsidyFlag(Source source, PartOfSubsidy flag)
Sets a flag indicating that given town/industry is part of subsidised route.
Definition subsidy.cpp:93
SubsidyPool _subsidy_pool("Subsidy")
Pool for the subsidies.
bool FindSubsidyIndustryCargoRoute()
Tries to create a cargo subsidy with an industry as source.
Definition subsidy.cpp:323
bool FindSubsidyPassengerRoute()
Tries to create a passenger subsidy between two towns.
Definition subsidy.cpp:236
void DeleteSubsidyWith(Source source)
Delete the subsidies associated with a given cargo source type and id.
Definition subsidy.cpp:119
bool FindSubsidyTownCargoRoute()
Tries to create a cargo subsidy with a town as source.
Definition subsidy.cpp:270
bool FindSubsidyCargoDestination(CargoType cargo_type, Source src)
Tries to find a suitable destination for the given source and cargo.
Definition subsidy.cpp:369
void RebuildSubsidisedSourceAndDestinationCache()
Perform a full rebuild of the subsidies cache.
Definition subsidy.cpp:103
bool CheckSubsidised(CargoType cargo_type, CompanyID company, Source src, const Station *st)
Tests whether given delivery is subsidised and possibly awards the subsidy to delivering company.
Definition subsidy.cpp:507
void CreateSubsidy(CargoType cargo_type, Source src, Source dst)
Creates a subsidy with the given parameters.
Definition subsidy.cpp:173
Subsidy base class.
static const uint SUBSIDY_MAX_DISTANCE
Max. length of subsidised route (DistanceManhattan)
static const uint SUBSIDY_CARGO_MIN_POPULATION
Min. population of destination town for cargo route.
static const uint SUBSIDY_TOWN_CARGO_RADIUS
Extent of a tile area around town center when scanning for town cargo acceptance and production (6 ~=...
static const uint SUBSIDY_PAX_MIN_POPULATION
Min. population of towns for subsidised pax route.
static const uint SUBSIDY_MAX_PCT_TRANSPORTED
Subsidy will be created only for towns/industries with less % transported.
static const uint SUBSIDY_OFFER_MONTHS
Constants related to subsidies.
Command definitions related to subsidies.
Functions related to subsidies.
PartOfSubsidy
What part of a subsidy is something?
@ Destination
town/industry is destination of subsidised path
@ Source
town/industry is source of subsidised path
Generic 'commands' that can be performed on all tiles.
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
@ MP_HOUSE
A house by a town.
Definition tile_type.h:51
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Definition of Interval and OneShot timers.
Definition of the game-economy-timer.
Base of the town class.
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3224
Window functions not directly related to making/drawing windows.
@ WC_SUBSIDIES_LIST
Subsidies list; Window numbers: