OpenTTD Source 20260731-master-g77ba2b244a
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
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 "script/api/script_event_types.hpp"
30#include "timer/timer.h"
32
33#include "table/strings.h"
34
35#include "safeguards.h"
36
37SubsidyPool _subsidy_pool("Subsidy");
39
40
45{
46 switch (this->type) {
47 case SourceType::Industry: return static_cast<IndustryID>(this->id);
48 case SourceType::Town: return static_cast<TownID>(this->id);
49 default: NOT_REACHED();
50 }
51}
52
58{
59 switch (this->type) {
60 case SourceType::Industry: return STR_INDUSTRY_NAME;
61 case SourceType::Town: return STR_TOWN_NAME;
62 default: NOT_REACHED();
63 }
64}
65
70void Subsidy::AwardTo(CompanyID company)
71{
72 assert(!this->IsAwarded());
73
74 this->awarded = company;
75 this->remaining = _settings_game.difficulty.subsidy_duration * CalendarTime::MONTHS_IN_YEAR;
76
77 std::string company_name = GetString(STR_COMPANY_NAME, company);
78
79 /* Add a news item */
80 const CargoSpec *cs = CargoSpec::Get(this->cargo_type);
81 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 AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, this->src.GetNewsReference(), this->dst.GetNewsReference());
83 AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index));
84 Game::NewEvent(new ScriptEventSubsidyAwarded(this->index));
85
86 InvalidateWindowData(WindowClass::SubsidyList, 0);
87}
88
94static inline void SetPartOfSubsidyFlag(Source source, PartOfSubsidy flag)
95{
96 switch (source.type) {
97 case SourceType::Industry: Industry::Get(source.ToIndustryID())->part_of_subsidy.Set(flag); return;
98 case SourceType::Town: Town::Get(source.ToTownID())->cache.part_of_subsidy.Set(flag); return;
99 default: NOT_REACHED();
100 }
101}
102
105{
106 for (Town *t : Town::Iterate()) t->cache.part_of_subsidy = {};
107
108 for (Industry *i : Industry::Iterate()) i->part_of_subsidy = {};
109
110 for (const Subsidy *s : Subsidy::Iterate()) {
113 }
114}
115
121{
122 bool dirty = false;
123
124 for (Subsidy *s : Subsidy::Iterate()) {
125 if (s->src == source || s->dst == source) {
126 delete s;
127 dirty = true;
128 }
129 }
130
131 if (dirty) {
132 InvalidateWindowData(WindowClass::SubsidyList, 0);
134 }
135}
136
144static bool CheckSubsidyDuplicate(CargoType cargo, Source src, Source dst)
145{
146 for (const Subsidy *s : Subsidy::Iterate()) {
147 if (s->cargo_type == cargo && s->src == src && s->dst == dst) {
148 return true;
149 }
150 }
151 return false;
152}
153
160static bool CheckSubsidyDistance(Source src, Source dst)
161{
162 TileIndex tile_src = (src.type == SourceType::Town) ? Town::Get(src.ToTownID())->xy : Industry::Get(src.ToIndustryID())->location.tile;
163 TileIndex tile_dst = (dst.type == SourceType::Town) ? Town::Get(dst.ToTownID())->xy : Industry::Get(dst.ToIndustryID())->location.tile;
164
165 return (DistanceManhattan(tile_src, tile_dst) <= SUBSIDY_MAX_DISTANCE);
166}
167
174void CreateSubsidy(CargoType cargo_type, Source src, Source dst)
175{
176 Subsidy *s = Subsidy::Create(cargo_type, src, dst, SUBSIDY_OFFER_MONTHS);
177
178 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
179 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);
183 AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
184 Game::NewEvent(new ScriptEventSubsidyOffer(s->index));
185
186 InvalidateWindowData(WindowClass::SubsidyList, 0);
187}
188
198{
199 if (!Subsidy::CanAllocateItem()) return CMD_ERROR;
200
202
203 if (cargo_type >= NUM_CARGO || !::CargoSpec::Get(cargo_type)->IsValid()) return CMD_ERROR;
204
205 switch (src.type) {
206 case SourceType::Town:
207 if (!Town::IsValidID(src.ToTownID())) return CMD_ERROR;
208 break;
210 if (!Industry::IsValidID(src.ToIndustryID())) return CMD_ERROR;
211 break;
212 default:
213 return CMD_ERROR;
214 }
215 switch (dst.type) {
216 case SourceType::Town:
217 if (!Town::IsValidID(dst.ToTownID())) return CMD_ERROR;
218 break;
220 if (!Industry::IsValidID(dst.ToIndustryID())) return CMD_ERROR;
221 break;
222 default:
223 return CMD_ERROR;
224 }
225
226 if (flags.Test(DoCommandFlag::Execute)) {
227 CreateSubsidy(cargo_type, src, dst);
228 }
229
230 return CommandCost();
231}
232
238{
239 if (!Subsidy::CanAllocateItem()) return false;
240
241 /* Pick a random TPE_PASSENGER type */
244
245 const Town *src = Town::GetRandom();
247 src->GetPercentTransported(cargo_type) > SUBSIDY_MAX_PCT_TRANSPORTED) {
248 return false;
249 }
250
251 const Town *dst = Town::GetRandom();
252 if (dst->cache.population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
253 return false;
254 }
255
256 if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return false;
257 if (CheckSubsidyDuplicate(cargo_type, {src->index, SourceType::Town}, {dst->index, SourceType::Town})) return false;
258
259 CreateSubsidy(cargo_type, {src->index, SourceType::Town}, {dst->index, SourceType::Town});
260
261 return true;
262}
263
264bool FindSubsidyCargoDestination(CargoType cargo_type, Source src);
265
266
272{
273 if (!Subsidy::CanAllocateItem()) return false;
274
275 /* Select a random town. */
276 const Town *src_town = Town::GetRandom();
277 if (src_town->cache.population < SUBSIDY_CARGO_MIN_POPULATION) return false;
278
279 /* Calculate the produced cargo of houses around town center. */
280 CargoArray town_cargo_produced{};
281 TileArea ta = TileArea(src_town->xy, 1, 1).Expand(SUBSIDY_TOWN_CARGO_RADIUS);
282 for (TileIndex tile : ta) {
283 if (IsTileType(tile, TileType::House)) {
284 AddProducedCargo(tile, town_cargo_produced);
285 }
286 }
287
288 /* Passenger subsidies are not handled here. */
290 town_cargo_produced[cs->Index()] = 0;
291 }
292
293 uint8_t cargo_count = town_cargo_produced.GetCount();
294
295 /* No cargo produced at all? */
296 if (cargo_count == 0) return false;
297
298 /* Choose a random cargo that is produced in the town. */
299 uint8_t cargo_number = RandomRange(cargo_count);
300 CargoType cargo_type{};
301 for (; cargo_type < NUM_CARGO; ++cargo_type) {
302 if (town_cargo_produced[cargo_type] > 0) {
303 if (cargo_number == 0) break;
304 cargo_number--;
305 }
306 }
307
308 /* Avoid using invalid NewGRF cargoes. */
309 if (!CargoSpec::Get(cargo_type)->IsValid()) return false;
310
311 /* Quit if the percentage transported is large enough. */
312 if (src_town->GetPercentTransported(cargo_type) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
313
314 return FindSubsidyCargoDestination(cargo_type, {src_town->index, SourceType::Town});
315}
316
322{
323 if (!Subsidy::CanAllocateItem()) return false;
324
325 /* Select a random industry. */
326 const Industry *src_ind = Industry::GetRandom();
327 if (src_ind == nullptr) return false;
328
329 uint trans, total;
330
331 CargoType cargo_type;
332
333 /* Randomize cargo type */
334 int num_cargos = std::ranges::count_if(src_ind->produced, [](const auto &p) { return IsValidCargoType(p.cargo); });
335 if (num_cargos == 0) return false; // industry produces nothing
336 int cargo_num = RandomRange(num_cargos) + 1;
337
338 auto it = std::begin(src_ind->produced);
339 for (/* nothing */; it != std::end(src_ind->produced); ++it) {
340 if (IsValidCargoType(it->cargo)) cargo_num--;
341 if (cargo_num == 0) break;
342 }
343 assert(it != std::end(src_ind->produced)); // indicates loop didn't end as intended
344
345 cargo_type = it->cargo;
346 trans = it->history[LAST_MONTH].PctTransported();
347 total = it->history[LAST_MONTH].production;
348
349 /* Quit if no production in this industry
350 * or if the pct transported is already large enough. */
351 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || !IsValidCargoType(cargo_type)) return false;
352
353 return FindSubsidyCargoDestination(cargo_type, {src_ind->index, SourceType::Industry});
354}
355
363{
364 /* Choose a random destination. */
366
367 switch (dst.type) {
368 case SourceType::Town: {
369 /* Select a random town. */
370 const Town *dst_town = Town::GetRandom();
371
372 /* Calculate cargo acceptance of houses around town center. */
373 CargoArray town_cargo_accepted{};
374 CargoTypes always_accepted{};
375 TileArea ta = TileArea(dst_town->xy, 1, 1).Expand(SUBSIDY_TOWN_CARGO_RADIUS);
376 for (TileIndex tile : ta) {
377 if (IsTileType(tile, TileType::House)) {
378 AddAcceptedCargo(tile, town_cargo_accepted, always_accepted);
379 }
380 }
381
382 /* Check if the town can accept this cargo. */
383 if (town_cargo_accepted[cargo_type] < 8) return false;
384
385 dst.SetIndex(dst_town->index);
386 break;
387 }
388
390 /* Select a random industry. */
391 const Industry *dst_ind = Industry::GetRandom();
392 if (dst_ind == nullptr) return false;
393
394 /* The industry must accept the cargo */
395 if (!dst_ind->IsCargoAccepted(cargo_type)) return false;
396
397 dst.SetIndex(dst_ind->index);
398 break;
399 }
400
401 default: NOT_REACHED();
402 }
403
404 /* Check that the source and the destination are not the same. */
405 if (src == dst) return false;
406
407 /* Check distance between source and destination. */
408 if (!CheckSubsidyDistance(src, dst)) return false;
409
410 /* Avoid duplicate subsidies. */
411 if (CheckSubsidyDuplicate(cargo_type, src, dst)) return false;
412
413 CreateSubsidy(cargo_type, src, dst);
414
415 return true;
416}
417
419static const IntervalTimer<TimerGameEconomy> _economy_subsidies_monthly({TimerGameEconomy::Trigger::Month, TimerGameEconomy::Priority::Subsidy}, [](auto)
420{
421 bool modified = false;
422
423 for (Subsidy *s : Subsidy::Iterate()) {
424 if (--s->remaining == 0) {
425 if (!s->IsAwarded()) {
426 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
427 EncodedString headline = GetEncodedString(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id);
428 AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference());
429 AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
430 Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
431 } else {
432 if (s->awarded == _local_company) {
433 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
434 EncodedString headline = GetEncodedString(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id);
435 AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference());
436 }
437 AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
438 Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
439 }
440 delete s;
441 modified = true;
442 }
443 }
444
445 if (modified) {
447 } else if (_settings_game.difficulty.subsidy_duration == 0) {
448 /* If subsidy duration is set to 0, subsidies are disabled, so bail out. */
449 return;
450 }
451
452 bool passenger_subsidy = false;
453 bool town_subsidy = false;
454 bool industry_subsidy = false;
455
456 int random_chance = RandomRange(16);
457
458 if (random_chance < 2) {
459 /* There is a 1/8 chance each month of generating a passenger subsidy. */
460 int n = 1000;
461
462 do {
463 passenger_subsidy = FindSubsidyPassengerRoute();
464 } while (!passenger_subsidy && n--);
465 } else if (random_chance == 2) {
466 /* Cargo subsidies with a town as a source have a 1/16 chance. */
467 int n = 1000;
468
469 do {
470 town_subsidy = FindSubsidyTownCargoRoute();
471 } while (!town_subsidy && n--);
472 } else if (random_chance == 3) {
473 /* Cargo subsidies with an industry as a source have a 1/16 chance. */
474 int n = 1000;
475
476 do {
477 industry_subsidy = FindSubsidyIndustryCargoRoute();
478 } while (!industry_subsidy && n--);
479 }
480
481 modified |= passenger_subsidy || town_subsidy || industry_subsidy;
482
483 if (modified) InvalidateWindowData(WindowClass::SubsidyList, 0);
484});
485
494bool CheckSubsidised(CargoType cargo_type, CompanyID company, Source src, const Station *st)
495{
496 /* If the source isn't subsidised, don't continue */
497 if (!src.IsValid()) return false;
498 switch (src.type) {
500 if (!Industry::Get(src.ToIndustryID())->part_of_subsidy.Test(PartOfSubsidy::Source)) return false;
501 break;
502 case SourceType::Town:
503 if (!Town::Get(src.ToTownID())->cache.part_of_subsidy.Test(PartOfSubsidy::Source)) return false;
504 break;
505 default: return false;
506 }
507
508 /* Remember all towns near this station (at least one house in its catchment radius)
509 * which are destination of subsidised path. Do that only if needed */
510 std::vector<const Town *> towns_near;
511 if (!st->rect.IsEmpty()) {
512 for (const Subsidy *s : Subsidy::Iterate()) {
513 /* Don't create the cache if there is no applicable subsidy with town as destination */
514 if (s->dst.type != SourceType::Town) continue;
515 if (s->cargo_type != cargo_type || s->src != src) continue;
516 if (s->IsAwarded() && s->awarded != company) continue;
517
519 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
520 if (!IsTileType(tile, TileType::House)) continue;
521 const Town *t = Town::GetByTile(tile);
523 }
524 break;
525 }
526 }
527
528 bool subsidised = false;
529
530 /* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
531 * Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
532 for (Subsidy *s : Subsidy::Iterate()) {
533 if (s->cargo_type == cargo_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
534 switch (s->dst.type) {
536 for (const auto &i : st->industries_near) {
537 if (s->dst.ToIndustryID() == i.industry->index) {
538 assert(i.industry->part_of_subsidy.Test(PartOfSubsidy::Destination));
539 subsidised = true;
540 if (!s->IsAwarded()) s->AwardTo(company);
541 }
542 }
543 break;
544 case SourceType::Town:
545 for (const Town *tp : towns_near) {
546 if (s->dst.ToTownID() == tp->index) {
547 assert(tp->cache.part_of_subsidy.Test(PartOfSubsidy::Destination));
548 subsidised = true;
549 if (!s->IsAwarded()) s->AwardTo(company);
550 }
551 }
552 break;
553 default:
554 NOT_REACHED();
555 }
556 }
557 }
558
559 return subsidised;
560}
Base functions for all AIs.
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
EnumBitSet< CargoType, uint64_t > CargoTypes
Bitset of CargoType elements.
Definition cargo_type.h:113
static constexpr CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:75
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
@ Passengers
Cargo behaves passenger-like for production.
Definition cargotype.h:38
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company=CompanyID::Invalid())
Broadcast a new event to all active AIs.
Definition ai_core.cpp:250
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.
static void NewEvent(class ScriptEvent *event)
Queue a new event for the game script.
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
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.
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:169
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:917
@ Subsidies
News about subsidies (announcements, expirations, acceptance).
Definition news_type.h:44
@ Normal
Normal news item. (Newspaper with text only).
Definition news_type.h:80
std::variant< std::monostate, TileIndex, VehicleID, StationID, IndustryID, TownID, EngineID > NewsReference
References to objects in news.
Definition news_type.h:74
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:61
@ Industry
Source/destination is an industry.
Definition source_type.h:21
@ Town
Source/destination is a town.
Definition source_type.h:22
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:424
Functions related to OTTD's strings.
StrongType::Typedef< uint32_t, struct StringIDTag, StrongType::Compare, StrongType::Integer > 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:118
uint GetCount() const
Get the amount of cargos that have an amount.
Definition cargo_type.h:133
Specification of a cargo type.
Definition cargotype.h:77
static EnumIndexArray< std::vector< const CargoSpec * >, TownProductionEffect, TownProductionEffect::End > town_production_cargoes
List of cargo specs for each Town Product Effect.
Definition cargotype.h:197
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:141
StringID name
Name of this type of cargo.
Definition cargotype.h:94
Defines the internal data of a functional industry.
Definition industry.h:64
static Industry * GetRandom()
Return a random valid industry.
bool IsCargoAccepted() const
Test if this industry accepts any cargo.
Definition industry.h:225
ProducedCargoes produced
produced cargo slots
Definition industry.h:112
OrthogonalTileArea & Expand(int rad)
Expand a tile area by rad tiles in each direction, keeping within map bounds.
Definition tilearea.cpp:123
static Pool::IterateWrapper< Town > Iterate(size_t from=0)
static T * Create(Targs &&... args)
static Industry * Get(auto index)
static bool CanAllocateItem(size_t n=1)
static bool IsValidID(auto index)
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:57
NewsReference GetNewsReference() const
Get the NewsReference for a subsidy Source.
Definition subsidy.cpp:44
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:70
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:54
PartsOfSubsidy part_of_subsidy
Is this town a source/destination of a subsidy?
Definition town.h:56
Town data structure.
Definition town.h:64
TileIndex xy
town center tile
Definition town.h:65
static Town * GetRandom()
Return a random valid town.
Definition town_cmd.cpp:205
TownCache cache
Container for all cacheable data.
Definition town.h:67
static bool CheckSubsidyDuplicate(CargoType cargo, Source src, Source dst)
Check whether a specific subsidy already exists.
Definition subsidy.cpp:144
CommandCost CmdCreateSubsidy(DoCommandFlags flags, CargoType cargo_type, Source src, Source dst)
Create a new subsidy.
Definition subsidy.cpp:197
static bool CheckSubsidyDistance(Source src, Source dst)
Checks if the source and destination of a subsidy are inside the distance limit.
Definition subsidy.cpp:160
static void SetPartOfSubsidyFlag(Source source, PartOfSubsidy flag)
Sets a flag indicating that given town/industry is part of subsidised route.
Definition subsidy.cpp:94
static const IntervalTimer< TimerGameEconomy > _economy_subsidies_monthly({TimerGameEconomy::Trigger::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;} bool passenger_subsidy=false;bool town_subsidy=false;bool industry_subsidy=false;int random_chance=RandomRange(16);if(random_chance< 2) { 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(WindowClass::SubsidyList, 0);})
Perform the economy monthly update of open subsidies, and try to create a new one.
SubsidyPool _subsidy_pool("Subsidy")
Pool for the subsidies.
bool FindSubsidyIndustryCargoRoute()
Tries to create a cargo subsidy with an industry as source.
Definition subsidy.cpp:321
bool FindSubsidyPassengerRoute()
Tries to create a passenger subsidy between two towns.
Definition subsidy.cpp:237
void DeleteSubsidyWith(Source source)
Delete the subsidies associated with a given cargo source type and id.
Definition subsidy.cpp:120
bool FindSubsidyTownCargoRoute()
Tries to create a cargo subsidy with a town as source.
Definition subsidy.cpp:271
bool FindSubsidyCargoDestination(CargoType cargo_type, Source src)
Tries to find a suitable destination for the given source and cargo.
Definition subsidy.cpp:362
void RebuildSubsidisedSourceAndDestinationCache()
Perform a full rebuild of the subsidies cache.
Definition subsidy.cpp:104
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:494
void CreateSubsidy(CargoType cargo_type, Source src, Source dst)
Creates a subsidy with the given parameters.
Definition subsidy.cpp:174
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.
void AddProducedCargo(TileIndex tile, CargoArray &produced)
Obtain the produced cargo of a tile.
Definition tile_cmd.h:257
void AddAcceptedCargo(TileIndex tile, CargoArray &acceptance, CargoTypes &always_accepted)
Obtain cargo acceptance of a tile.
Definition tile_cmd.h:245
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > > TileIndex
The index/ID of a Tile.
Definition tile_type.h:92
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:100
@ House
A house by a town.
Definition tile_type.h:52
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:3318
Window functions not directly related to making/drawing windows.