OpenTTD Source 20260401-master-g3efaeb0eea
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 "timer/timer.h"
31
32#include "table/strings.h"
33
34#include "safeguards.h"
35
36SubsidyPool _subsidy_pool("Subsidy");
38
39
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
69void Subsidy::AwardTo(CompanyID company)
70{
71 assert(!this->IsAwarded());
72
73 this->awarded = company;
74 this->remaining = _settings_game.difficulty.subsidy_duration * CalendarTime::MONTHS_IN_YEAR;
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);
81 AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, this->src.GetNewsReference(), this->dst.GetNewsReference());
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 = Subsidy::Create(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
196CommandCost CmdCreateSubsidy(DoCommandFlags flags, CargoType cargo_type, Source src, Source dst)
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, TileType::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()) return false;
309
310 /* Quit if the percentage transported is large enough. */
311 if (src_town->GetPercentTransported(cargo_type) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
312
313 return FindSubsidyCargoDestination(cargo_type, {src_town->index, SourceType::Town});
314}
315
321{
322 if (!Subsidy::CanAllocateItem()) return false;
323
324 /* Select a random industry. */
325 const Industry *src_ind = Industry::GetRandom();
326 if (src_ind == nullptr) return false;
327
328 uint trans, total;
329
330 CargoType cargo_type;
331
332 /* Randomize cargo type */
333 int num_cargos = std::ranges::count_if(src_ind->produced, [](const auto &p) { return IsValidCargoType(p.cargo); });
334 if (num_cargos == 0) return false; // industry produces nothing
335 int cargo_num = RandomRange(num_cargos) + 1;
336
337 auto it = std::begin(src_ind->produced);
338 for (/* nothing */; it != std::end(src_ind->produced); ++it) {
339 if (IsValidCargoType(it->cargo)) cargo_num--;
340 if (cargo_num == 0) break;
341 }
342 assert(it != std::end(src_ind->produced)); // indicates loop didn't end as intended
343
344 cargo_type = it->cargo;
345 trans = it->history[LAST_MONTH].PctTransported();
346 total = it->history[LAST_MONTH].production;
347
348 /* Quit if no production in this industry
349 * or if the pct transported is already large enough. */
350 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || !IsValidCargoType(cargo_type)) return false;
351
352 return FindSubsidyCargoDestination(cargo_type, {src_ind->index, SourceType::Industry});
353}
354
362{
363 /* Choose a random destination. */
365
366 switch (dst.type) {
367 case SourceType::Town: {
368 /* Select a random town. */
369 const Town *dst_town = Town::GetRandom();
370
371 /* Calculate cargo acceptance of houses around town center. */
372 CargoArray town_cargo_accepted{};
373 CargoTypes always_accepted{};
374 TileArea ta = TileArea(dst_town->xy, 1, 1).Expand(SUBSIDY_TOWN_CARGO_RADIUS);
375 for (TileIndex tile : ta) {
376 if (IsTileType(tile, TileType::House)) {
377 AddAcceptedCargo(tile, town_cargo_accepted, always_accepted);
378 }
379 }
380
381 /* Check if the town can accept this cargo. */
382 if (town_cargo_accepted[cargo_type] < 8) return false;
383
384 dst.SetIndex(dst_town->index);
385 break;
386 }
387
389 /* Select a random industry. */
390 const Industry *dst_ind = Industry::GetRandom();
391 if (dst_ind == nullptr) return false;
392
393 /* The industry must accept the cargo */
394 if (!dst_ind->IsCargoAccepted(cargo_type)) return false;
395
396 dst.SetIndex(dst_ind->index);
397 break;
398 }
399
400 default: NOT_REACHED();
401 }
402
403 /* Check that the source and the destination are not the same. */
404 if (src == dst) return false;
405
406 /* Check distance between source and destination. */
407 if (!CheckSubsidyDistance(src, dst)) return false;
408
409 /* Avoid duplicate subsidies. */
410 if (CheckSubsidyDuplicate(cargo_type, src, dst)) return false;
411
412 CreateSubsidy(cargo_type, src, dst);
413
414 return true;
415}
416
418static const IntervalTimer<TimerGameEconomy> _economy_subsidies_monthly({TimerGameEconomy::Trigger::Month, TimerGameEconomy::Priority::Subsidy}, [](auto)
419{
420 bool modified = false;
421
422 for (Subsidy *s : Subsidy::Iterate()) {
423 if (--s->remaining == 0) {
424 if (!s->IsAwarded()) {
425 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
426 EncodedString headline = GetEncodedString(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, cs->name, s->src.GetFormat(), s->src.id, s->dst.GetFormat(), s->dst.id);
427 AddNewsItem(std::move(headline), NewsType::Subsidies, NewsStyle::Normal, {}, s->src.GetNewsReference(), s->dst.GetNewsReference());
428 AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
429 Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
430 } else {
431 if (s->awarded == _local_company) {
432 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
433 EncodedString headline = GetEncodedString(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, 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 }
436 AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
437 Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
438 }
439 delete s;
440 modified = true;
441 }
442 }
443
444 if (modified) {
446 } else if (_settings_game.difficulty.subsidy_duration == 0) {
447 /* If subsidy duration is set to 0, subsidies are disabled, so bail out. */
448 return;
449 }
450
451 bool passenger_subsidy = false;
452 bool town_subsidy = false;
453 bool industry_subsidy = false;
454
455 int random_chance = RandomRange(16);
456
457 if (random_chance < 2) {
458 /* There is a 1/8 chance each month of generating a passenger subsidy. */
459 int n = 1000;
460
461 do {
462 passenger_subsidy = FindSubsidyPassengerRoute();
463 } while (!passenger_subsidy && n--);
464 } else if (random_chance == 2) {
465 /* Cargo subsidies with a town as a source have a 1/16 chance. */
466 int n = 1000;
467
468 do {
469 town_subsidy = FindSubsidyTownCargoRoute();
470 } while (!town_subsidy && n--);
471 } else if (random_chance == 3) {
472 /* Cargo subsidies with an industry as a source have a 1/16 chance. */
473 int n = 1000;
474
475 do {
476 industry_subsidy = FindSubsidyIndustryCargoRoute();
477 } while (!industry_subsidy && n--);
478 }
479
480 modified |= passenger_subsidy || town_subsidy || industry_subsidy;
481
482 if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
483});
484
493bool CheckSubsidised(CargoType cargo_type, CompanyID company, Source src, const Station *st)
494{
495 /* If the source isn't subsidised, don't continue */
496 if (!src.IsValid()) return false;
497 switch (src.type) {
499 if (!Industry::Get(src.ToIndustryID())->part_of_subsidy.Test(PartOfSubsidy::Source)) return false;
500 break;
501 case SourceType::Town:
502 if (!Town::Get(src.ToTownID())->cache.part_of_subsidy.Test(PartOfSubsidy::Source)) return false;
503 break;
504 default: return false;
505 }
506
507 /* Remember all towns near this station (at least one house in its catchment radius)
508 * which are destination of subsidised path. Do that only if needed */
509 std::vector<const Town *> towns_near;
510 if (!st->rect.IsEmpty()) {
511 for (const Subsidy *s : Subsidy::Iterate()) {
512 /* Don't create the cache if there is no applicable subsidy with town as destination */
513 if (s->dst.type != SourceType::Town) continue;
514 if (s->cargo_type != cargo_type || s->src != src) continue;
515 if (s->IsAwarded() && s->awarded != company) continue;
516
518 for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
519 if (!IsTileType(tile, TileType::House)) continue;
520 const Town *t = Town::GetByTile(tile);
522 }
523 break;
524 }
525 }
526
527 bool subsidised = false;
528
529 /* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
530 * Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
531 for (Subsidy *s : Subsidy::Iterate()) {
532 if (s->cargo_type == cargo_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
533 switch (s->dst.type) {
535 for (const auto &i : st->industries_near) {
536 if (s->dst.ToIndustryID() == i.industry->index) {
537 assert(i.industry->part_of_subsidy.Test(PartOfSubsidy::Destination));
538 subsidised = true;
539 if (!s->IsAwarded()) s->AwardTo(company);
540 }
541 }
542 break;
543 case SourceType::Town:
544 for (const Town *tp : towns_near) {
545 if (s->dst.ToTownID() == tp->index) {
546 assert(tp->cache.part_of_subsidy.Test(PartOfSubsidy::Destination));
547 subsidised = true;
548 if (!s->IsAwarded()) s->AwardTo(company);
549 }
550 }
551 break;
552 default:
553 NOT_REACHED();
554 }
555 }
556 }
557
558 return subsidised;
559}
Base functions for all AIs.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:108
static const CargoType NUM_CARGO
Maximum number of cargo types in a game.
Definition cargo_type.h:73
@ 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:255
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
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:914
@ 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.
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:115
uint GetCount() const
Get the amount of cargos that have an amount.
Definition cargo_type.h:130
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:138
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:25
Defines the internal data of a functional industry.
Definition industry.h:62
static Industry * GetRandom()
Return a random valid industry.
bool IsCargoAccepted() const
Test if this industry accepts any cargo.
Definition industry.h:223
ProducedCargoes produced
produced cargo slots
Definition industry.h:110
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 Industry * Get(auto index)
static bool CanAllocateItem(size_t n=1)
static bool IsValidID(auto index)
static T * Create(Targs &&... args)
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:53
PartsOfSubsidy part_of_subsidy
Is this town a source/destination of a subsidy?
Definition town.h:55
Town data structure.
Definition town.h:63
TileIndex xy
town center tile
Definition town.h:64
static Town * GetRandom()
Return a random valid town.
Definition town_cmd.cpp:204
TownCache cache
Container for all cacheable data.
Definition town.h:66
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 void SetPartOfSubsidyFlag(Source source, PartOfSubsidy flag)
Sets a flag indicating that given town/industry is part of subsidised route.
Definition subsidy.cpp:93
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(WC_SUBSIDIES_LIST, 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:320
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:361
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:493
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.
void AddProducedCargo(TileIndex tile, CargoArray &produced)
Obtain the produced cargo of a tile.
Definition tile_cmd.h:255
void AddAcceptedCargo(TileIndex tile, CargoArray &acceptance, CargoTypes &always_accepted)
Obtain cargo acceptance of a tile.
Definition tile_cmd.h:243
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:3322
Window functions not directly related to making/drawing windows.
@ WC_SUBSIDIES_LIST
Subsidies list; Window numbers: