Update: Translations from eints
[openttd-github.git] / src / script / api / script_town.cpp
blob3a4e3026678e39aa70f430a81625aa0b087bf7e9
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 */
8 /** @file script_town.cpp Implementation of ScriptTown. */
10 #include "../../stdafx.h"
11 #include "script_town.hpp"
12 #include "script_map.hpp"
13 #include "script_error.hpp"
14 #include "../../town.h"
15 #include "../../townname_func.h"
16 #include "../../string_func.h"
17 #include "../../strings_func.h"
18 #include "../../station_base.h"
19 #include "../../landscape.h"
20 #include "../../town_cmd.h"
21 #include "table/strings.h"
23 #include "../../safeguards.h"
25 /* static */ SQInteger ScriptTown::GetTownCount()
27 return ::Town::GetNumItems();
30 /* static */ bool ScriptTown::IsValidTown(TownID town_id)
32 return ::Town::IsValidID(town_id);
35 /* static */ std::optional<std::string> ScriptTown::GetName(TownID town_id)
37 if (!IsValidTown(town_id)) return std::nullopt;
39 ::SetDParam(0, town_id);
40 return GetString(STR_TOWN_NAME);
43 /* static */ bool ScriptTown::SetName(TownID town_id, Text *name)
45 CCountedPtr<Text> counter(name);
47 EnforceDeityMode(false);
48 EnforcePrecondition(false, IsValidTown(town_id));
49 std::string text;
50 if (name != nullptr) {
51 text = name->GetDecodedText();
52 EnforcePreconditionCustomError(false, ::Utf8StringLength(text) < MAX_LENGTH_TOWN_NAME_CHARS, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG);
55 return ScriptObject::Command<CMD_RENAME_TOWN>::Do(town_id, text);
58 /* static */ bool ScriptTown::SetText(TownID town_id, Text *text)
60 CCountedPtr<Text> counter(text);
62 EnforceDeityMode(false);
63 EnforcePrecondition(false, IsValidTown(town_id));
65 return ScriptObject::Command<CMD_TOWN_SET_TEXT>::Do(town_id, text != nullptr ? text->GetEncodedText() : std::string{});
68 /* static */ SQInteger ScriptTown::GetPopulation(TownID town_id)
70 if (!IsValidTown(town_id)) return -1;
71 const Town *t = ::Town::Get(town_id);
72 return t->cache.population;
75 /* static */ SQInteger ScriptTown::GetHouseCount(TownID town_id)
77 if (!IsValidTown(town_id)) return -1;
78 const Town *t = ::Town::Get(town_id);
79 return t->cache.num_houses;
82 /* static */ TileIndex ScriptTown::GetLocation(TownID town_id)
84 if (!IsValidTown(town_id)) return INVALID_TILE;
85 const Town *t = ::Town::Get(town_id);
86 return t->xy;
89 /* static */ SQInteger ScriptTown::GetLastMonthProduction(TownID town_id, CargoID cargo_id)
91 if (!IsValidTown(town_id)) return -1;
92 if (!ScriptCargo::IsValidCargo(cargo_id)) return -1;
94 const Town *t = ::Town::Get(town_id);
96 return t->supplied[cargo_id].old_max;
99 /* static */ SQInteger ScriptTown::GetLastMonthSupplied(TownID town_id, CargoID cargo_id)
101 if (!IsValidTown(town_id)) return -1;
102 if (!ScriptCargo::IsValidCargo(cargo_id)) return -1;
104 const Town *t = ::Town::Get(town_id);
106 return t->supplied[cargo_id].old_act;
109 /* static */ SQInteger ScriptTown::GetLastMonthTransportedPercentage(TownID town_id, CargoID cargo_id)
111 if (!IsValidTown(town_id)) return -1;
112 if (!ScriptCargo::IsValidCargo(cargo_id)) return -1;
114 const Town *t = ::Town::Get(town_id);
115 return ::ToPercent8(t->GetPercentTransported(cargo_id));
118 /* static */ SQInteger ScriptTown::GetLastMonthReceived(TownID town_id, ScriptCargo::TownEffect towneffect_id)
120 if (!IsValidTown(town_id)) return -1;
121 if (!ScriptCargo::IsValidTownEffect(towneffect_id)) return -1;
123 const Town *t = ::Town::Get(town_id);
125 return t->received[towneffect_id].old_act;
128 /* static */ bool ScriptTown::SetCargoGoal(TownID town_id, ScriptCargo::TownEffect towneffect_id, SQInteger goal)
130 EnforceDeityMode(false);
131 EnforcePrecondition(false, IsValidTown(town_id));
132 EnforcePrecondition(false, ScriptCargo::IsValidTownEffect(towneffect_id));
134 goal = Clamp<SQInteger>(goal, 0, UINT32_MAX);
136 return ScriptObject::Command<CMD_TOWN_CARGO_GOAL>::Do(town_id, (::TownAcceptanceEffect)towneffect_id, goal);
139 /* static */ SQInteger ScriptTown::GetCargoGoal(TownID town_id, ScriptCargo::TownEffect towneffect_id)
141 if (!IsValidTown(town_id)) return -1;
142 if (!ScriptCargo::IsValidTownEffect(towneffect_id)) return -1;
144 const Town *t = ::Town::Get(town_id);
146 switch (t->goal[towneffect_id]) {
147 case TOWN_GROWTH_WINTER:
148 if (TileHeight(t->xy) >= GetSnowLine() && t->cache.population > 90) return 1;
149 return 0;
151 case TOWN_GROWTH_DESERT:
152 if (GetTropicZone(t->xy) == TROPICZONE_DESERT && t->cache.population > 60) return 1;
153 return 0;
155 default: return t->goal[towneffect_id];
159 /* static */ bool ScriptTown::SetGrowthRate(TownID town_id, SQInteger days_between_town_growth)
161 EnforceDeityMode(false);
162 EnforcePrecondition(false, IsValidTown(town_id));
163 uint16_t growth_rate;
164 switch (days_between_town_growth) {
165 case TOWN_GROWTH_NORMAL:
166 growth_rate = 0;
167 break;
169 case TOWN_GROWTH_NONE:
170 growth_rate = TOWN_GROWTH_RATE_NONE;
171 break;
173 default:
174 EnforcePrecondition(false, (days_between_town_growth * ::Ticks::DAY_TICKS / ::Ticks::TOWN_GROWTH_TICKS) <= MAX_TOWN_GROWTH_TICKS);
175 /* Don't use growth_rate 0 as it means GROWTH_NORMAL */
176 growth_rate = std::max<SQInteger>(days_between_town_growth * ::Ticks::DAY_TICKS, 2u) - 1;
177 break;
180 return ScriptObject::Command<CMD_TOWN_GROWTH_RATE>::Do(town_id, growth_rate);
183 /* static */ SQInteger ScriptTown::GetGrowthRate(TownID town_id)
185 if (!IsValidTown(town_id)) return -1;
187 const Town *t = ::Town::Get(town_id);
189 if (t->growth_rate == TOWN_GROWTH_RATE_NONE) return TOWN_GROWTH_NONE;
191 return RoundDivSU(t->growth_rate + 1, ::Ticks::DAY_TICKS);
194 /* static */ SQInteger ScriptTown::GetDistanceManhattanToTile(TownID town_id, TileIndex tile)
196 return ScriptMap::DistanceManhattan(tile, GetLocation(town_id));
199 /* static */ SQInteger ScriptTown::GetDistanceSquareToTile(TownID town_id, TileIndex tile)
201 return ScriptMap::DistanceSquare(tile, GetLocation(town_id));
204 /* static */ bool ScriptTown::IsWithinTownInfluence(TownID town_id, TileIndex tile)
206 if (!IsValidTown(town_id)) return false;
208 const Town *t = ::Town::Get(town_id);
209 return ((uint32_t)GetDistanceSquareToTile(town_id, tile) <= t->cache.squared_town_zone_radius[HZB_TOWN_EDGE]);
212 /* static */ bool ScriptTown::HasStatue(TownID town_id)
214 EnforceCompanyModeValid(false);
215 if (!IsValidTown(town_id)) return false;
217 return ::HasBit(::Town::Get(town_id)->statues, ScriptObject::GetCompany());
220 /* static */ bool ScriptTown::IsCity(TownID town_id)
222 if (!IsValidTown(town_id)) return false;
224 return ::Town::Get(town_id)->larger_town;
227 /* static */ SQInteger ScriptTown::GetRoadReworkDuration(TownID town_id)
229 if (!IsValidTown(town_id)) return -1;
231 return ::Town::Get(town_id)->road_build_months;
234 /* static */ SQInteger ScriptTown::GetFundBuildingsDuration(TownID town_id)
236 if (!IsValidTown(town_id)) return -1;
238 return ::Town::Get(town_id)->fund_buildings_months;
241 /* static */ ScriptCompany::CompanyID ScriptTown::GetExclusiveRightsCompany(TownID town_id)
243 EnforceCompanyModeValid(ScriptCompany::COMPANY_INVALID);
244 if (!IsValidTown(town_id)) return ScriptCompany::COMPANY_INVALID;
246 return (ScriptCompany::CompanyID)(int8_t)::Town::Get(town_id)->exclusivity;
249 /* static */ SQInteger ScriptTown::GetExclusiveRightsDuration(TownID town_id)
251 if (!IsValidTown(town_id)) return -1;
253 return ::Town::Get(town_id)->exclusive_counter;
256 /* static */ bool ScriptTown::IsActionAvailable(TownID town_id, TownAction town_action)
258 EnforceCompanyModeValid(false);
259 if (!IsValidTown(town_id)) return false;
261 return HasBit(::GetMaskOfTownActions(ScriptObject::GetCompany(), ::Town::Get(town_id)), town_action);
264 /* static */ bool ScriptTown::PerformTownAction(TownID town_id, TownAction town_action)
266 EnforceCompanyModeValid(false);
267 EnforcePrecondition(false, IsValidTown(town_id));
268 EnforcePrecondition(false, IsActionAvailable(town_id, town_action));
270 return ScriptObject::Command<CMD_DO_TOWN_ACTION>::Do(town_id, town_action);
273 /* static */ bool ScriptTown::ExpandTown(TownID town_id, SQInteger houses)
275 EnforceDeityMode(false);
276 EnforcePrecondition(false, IsValidTown(town_id));
277 EnforcePrecondition(false, houses > 0);
279 houses = std::min<SQInteger>(houses, UINT32_MAX);
281 return ScriptObject::Command<CMD_EXPAND_TOWN>::Do(town_id, houses);
284 /* static */ bool ScriptTown::FoundTown(TileIndex tile, TownSize size, bool city, RoadLayout layout, Text *name)
286 CCountedPtr<Text> counter(name);
288 EnforceDeityOrCompanyModeValid(false);
289 EnforcePrecondition(false, ScriptCompanyMode::IsDeity() || _settings_game.economy.found_town != TF_FORBIDDEN);
290 EnforcePrecondition(false, ::IsValidTile(tile));
291 EnforcePrecondition(false, size == TOWN_SIZE_SMALL || size == TOWN_SIZE_MEDIUM || size == TOWN_SIZE_LARGE)
292 EnforcePrecondition(false, ScriptCompanyMode::IsDeity() || size != TOWN_SIZE_LARGE);
293 if (ScriptCompanyMode::IsDeity() || _settings_game.economy.found_town == TF_CUSTOM_LAYOUT) {
294 EnforcePrecondition(false, layout >= ROAD_LAYOUT_ORIGINAL && layout <= ROAD_LAYOUT_RANDOM);
295 } else {
296 /* The layout parameter is ignored for AIs when custom layouts is disabled. */
297 layout = (RoadLayout) (uint8_t)_settings_game.economy.town_layout;
300 std::string text;
301 if (name != nullptr) {
302 text = name->GetDecodedText();
303 EnforcePreconditionCustomError(false, ::Utf8StringLength(text) < MAX_LENGTH_TOWN_NAME_CHARS, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG);
305 uint32_t townnameparts;
306 if (!GenerateTownName(ScriptObject::GetRandomizer(), &townnameparts)) {
307 ScriptObject::SetLastError(ScriptError::ERR_NAME_IS_NOT_UNIQUE);
308 return false;
311 return ScriptObject::Command<CMD_FOUND_TOWN>::Do(tile, (::TownSize)size, city, (::TownLayout)layout, false, townnameparts, text);
314 /* static */ ScriptTown::TownRating ScriptTown::GetRating(TownID town_id, ScriptCompany::CompanyID company_id)
316 if (!IsValidTown(town_id)) return TOWN_RATING_INVALID;
317 ScriptCompany::CompanyID company = ScriptCompany::ResolveCompanyID(company_id);
318 if (company == ScriptCompany::COMPANY_INVALID) return TOWN_RATING_INVALID;
320 const Town *t = ::Town::Get(town_id);
321 if (!HasBit(t->have_ratings, company)) {
322 return TOWN_RATING_NONE;
323 } else if (t->ratings[company] <= RATING_APPALLING) {
324 return TOWN_RATING_APPALLING;
325 } else if (t->ratings[company] <= RATING_VERYPOOR) {
326 return TOWN_RATING_VERY_POOR;
327 } else if (t->ratings[company] <= RATING_POOR) {
328 return TOWN_RATING_POOR;
329 } else if (t->ratings[company] <= RATING_MEDIOCRE) {
330 return TOWN_RATING_MEDIOCRE;
331 } else if (t->ratings[company] <= RATING_GOOD) {
332 return TOWN_RATING_GOOD;
333 } else if (t->ratings[company] <= RATING_VERYGOOD) {
334 return TOWN_RATING_VERY_GOOD;
335 } else if (t->ratings[company] <= RATING_EXCELLENT) {
336 return TOWN_RATING_EXCELLENT;
337 } else {
338 return TOWN_RATING_OUTSTANDING;
342 /* static */ SQInteger ScriptTown::GetDetailedRating(TownID town_id, ScriptCompany::CompanyID company_id)
344 if (!IsValidTown(town_id)) return TOWN_RATING_INVALID;
345 ScriptCompany::CompanyID company = ScriptCompany::ResolveCompanyID(company_id);
346 if (company == ScriptCompany::COMPANY_INVALID) return TOWN_RATING_INVALID;
348 const Town *t = ::Town::Get(town_id);
349 return t->ratings[company];
352 /* static */ bool ScriptTown::ChangeRating(TownID town_id, ScriptCompany::CompanyID company_id, SQInteger delta)
354 EnforceDeityMode(false);
355 EnforcePrecondition(false, IsValidTown(town_id));
356 ScriptCompany::CompanyID company = ScriptCompany::ResolveCompanyID(company_id);
357 EnforcePrecondition(false, company != ScriptCompany::COMPANY_INVALID);
359 const Town *t = ::Town::Get(town_id);
360 int16_t new_rating = Clamp(t->ratings[company] + delta, RATING_MINIMUM, RATING_MAXIMUM);
361 if (new_rating == t->ratings[company]) return false;
363 return ScriptObject::Command<CMD_TOWN_RATING>::Do(town_id, (::CompanyID)company_id, new_rating);
366 /* static */ SQInteger ScriptTown::GetAllowedNoise(TownID town_id)
368 if (!IsValidTown(town_id)) return -1;
370 const Town *t = ::Town::Get(town_id);
371 if (_settings_game.economy.station_noise_level) {
372 return t->MaxTownNoise() - t->noise_reached;
375 int num = 0;
376 for (const Station *st : Station::Iterate()) {
377 if (st->town == t && (st->facilities & FACIL_AIRPORT) && st->airport.type != AT_OILRIG) num++;
379 return std::max(0, 2 - num);
382 /* static */ ScriptTown::RoadLayout ScriptTown::GetRoadLayout(TownID town_id)
384 if (!IsValidTown(town_id)) return ROAD_LAYOUT_INVALID;
386 return (ScriptTown::RoadLayout)((TownLayout)::Town::Get(town_id)->layout);