4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file script_town.cpp Implementation of ScriptTown. */
12 #include "../../stdafx.h"
13 #include "script_town.hpp"
14 #include "script_map.hpp"
15 #include "script_error.hpp"
16 #include "../../town.h"
17 #include "../../townname_func.h"
18 #include "../../string_func.h"
19 #include "../../strings_func.h"
20 #include "../../station_base.h"
21 #include "../../landscape.h"
22 #include "table/strings.h"
24 #include "../../safeguards.h"
26 /* static */ int32
ScriptTown::GetTownCount()
28 return (int32
)::Town::GetNumItems();
31 /* static */ bool ScriptTown::IsValidTown(TownID town_id
)
33 return ::Town::IsValidID(town_id
);
36 /* static */ char *ScriptTown::GetName(TownID town_id
)
38 if (!IsValidTown(town_id
)) return NULL
;
40 ::SetDParam(0, town_id
);
41 return GetString(STR_TOWN_NAME
);
44 /* static */ bool ScriptTown::SetName(TownID town_id
, Text
*name
)
46 CCountedPtr
<Text
> counter(name
);
48 const char *text
= NULL
;
50 text
= name
->GetDecodedText();
51 EnforcePreconditionEncodedText(false, text
);
52 EnforcePreconditionCustomError(false, ::Utf8StringLength(text
) < MAX_LENGTH_TOWN_NAME_CHARS
, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG
);
54 EnforcePrecondition(false, IsValidTown(town_id
));
56 return ScriptObject::DoCommand(0, town_id
, 0, CMD_RENAME_TOWN
, text
);
59 /* static */ bool ScriptTown::SetText(TownID town_id
, Text
*text
)
61 CCountedPtr
<Text
> counter(text
);
63 EnforcePrecondition(false, text
!= NULL
);
64 const char *encoded_text
= text
->GetEncodedText();
65 EnforcePreconditionEncodedText(false, encoded_text
);
66 EnforcePrecondition(false, IsValidTown(town_id
));
68 return ScriptObject::DoCommand(::Town::Get(town_id
)->xy
, town_id
, 0, CMD_TOWN_SET_TEXT
, encoded_text
);
71 /* static */ int32
ScriptTown::GetPopulation(TownID town_id
)
73 if (!IsValidTown(town_id
)) return -1;
74 const Town
*t
= ::Town::Get(town_id
);
75 return t
->cache
.population
;
78 /* static */ int32
ScriptTown::GetHouseCount(TownID town_id
)
80 if (!IsValidTown(town_id
)) return -1;
81 const Town
*t
= ::Town::Get(town_id
);
82 return t
->cache
.num_houses
;
85 /* static */ TileIndex
ScriptTown::GetLocation(TownID town_id
)
87 if (!IsValidTown(town_id
)) return INVALID_TILE
;
88 const Town
*t
= ::Town::Get(town_id
);
92 /* static */ int32
ScriptTown::GetLastMonthProduction(TownID town_id
, CargoID cargo_id
)
94 if (!IsValidTown(town_id
)) return -1;
95 if (!ScriptCargo::IsValidCargo(cargo_id
)) return -1;
97 const Town
*t
= ::Town::Get(town_id
);
99 return t
->supplied
[cargo_id
].old_max
;
102 /* static */ int32
ScriptTown::GetLastMonthSupplied(TownID town_id
, CargoID cargo_id
)
104 if (!IsValidTown(town_id
)) return -1;
105 if (!ScriptCargo::IsValidCargo(cargo_id
)) return -1;
107 const Town
*t
= ::Town::Get(town_id
);
109 return t
->supplied
[cargo_id
].old_act
;
112 /* static */ int32
ScriptTown::GetLastMonthTransportedPercentage(TownID town_id
, CargoID cargo_id
)
114 if (!IsValidTown(town_id
)) return -1;
115 if (!ScriptCargo::IsValidCargo(cargo_id
)) return -1;
117 const Town
*t
= ::Town::Get(town_id
);
118 return ::ToPercent8(t
->GetPercentTransported(cargo_id
));
121 /* static */ int32
ScriptTown::GetLastMonthReceived(TownID town_id
, ScriptCargo::TownEffect towneffect_id
)
123 if (!IsValidTown(town_id
)) return -1;
124 if (!ScriptCargo::IsValidTownEffect(towneffect_id
)) return -1;
126 const Town
*t
= ::Town::Get(town_id
);
128 return t
->received
[towneffect_id
].old_act
;
131 /* static */ bool ScriptTown::SetCargoGoal(TownID town_id
, ScriptCargo::TownEffect towneffect_id
, uint32 goal
)
133 EnforcePrecondition(false, IsValidTown(town_id
));
134 EnforcePrecondition(false, ScriptCargo::IsValidTownEffect(towneffect_id
));
136 return ScriptObject::DoCommand(::Town::Get(town_id
)->xy
, town_id
| (towneffect_id
<< 16), goal
, CMD_TOWN_CARGO_GOAL
);
139 /* static */ uint32
ScriptTown::GetCargoGoal(TownID town_id
, ScriptCargo::TownEffect towneffect_id
)
141 if (!IsValidTown(town_id
)) return UINT32_MAX
;
142 if (!ScriptCargo::IsValidTownEffect(towneffect_id
)) return UINT32_MAX
;
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;
151 case TOWN_GROWTH_DESERT
:
152 if (GetTropicZone(t
->xy
) == TROPICZONE_DESERT
&& t
->cache
.population
> 60) return 1;
155 default: return t
->goal
[towneffect_id
];
159 /* static */ bool ScriptTown::SetGrowthRate(TownID town_id
, uint32 days_between_town_growth
)
161 EnforcePrecondition(false, IsValidTown(town_id
));
163 switch (days_between_town_growth
) {
164 case TOWN_GROWTH_NORMAL
:
165 days_between_town_growth
= 0;
168 case TOWN_GROWTH_NONE
:
169 days_between_town_growth
= TOWN_GROW_RATE_CUSTOM_NONE
;
173 days_between_town_growth
= days_between_town_growth
* DAY_TICKS
/ TOWN_GROWTH_TICKS
;
174 EnforcePrecondition(false, days_between_town_growth
< TOWN_GROW_RATE_CUSTOM
);
175 if (days_between_town_growth
== 0) days_between_town_growth
= 1; // as fast as possible
179 return ScriptObject::DoCommand(::Town::Get(town_id
)->xy
, town_id
, days_between_town_growth
, CMD_TOWN_GROWTH_RATE
);
182 /* static */ int32
ScriptTown::GetGrowthRate(TownID town_id
)
184 if (!IsValidTown(town_id
)) return -1;
186 const Town
*t
= ::Town::Get(town_id
);
188 if (t
->growth_rate
== TOWN_GROW_RATE_CUSTOM_NONE
) return TOWN_GROWTH_NONE
;
190 return ((t
->growth_rate
& ~TOWN_GROW_RATE_CUSTOM
) * TOWN_GROWTH_TICKS
+ DAY_TICKS
) / DAY_TICKS
;
193 /* static */ int32
ScriptTown::GetDistanceManhattanToTile(TownID town_id
, TileIndex tile
)
195 return ScriptMap::DistanceManhattan(tile
, GetLocation(town_id
));
198 /* static */ int32
ScriptTown::GetDistanceSquareToTile(TownID town_id
, TileIndex tile
)
200 return ScriptMap::DistanceSquare(tile
, GetLocation(town_id
));
203 /* static */ bool ScriptTown::IsWithinTownInfluence(TownID town_id
, TileIndex tile
)
205 if (!IsValidTown(town_id
)) return false;
207 const Town
*t
= ::Town::Get(town_id
);
208 return ((uint32
)GetDistanceSquareToTile(town_id
, tile
) <= t
->cache
.squared_town_zone_radius
[0]);
211 /* static */ bool ScriptTown::HasStatue(TownID town_id
)
213 if (ScriptObject::GetCompany() == OWNER_DEITY
) return false;
214 if (!IsValidTown(town_id
)) return false;
216 return ::HasBit(::Town::Get(town_id
)->statues
, ScriptObject::GetCompany());
219 /* static */ bool ScriptTown::IsCity(TownID town_id
)
221 if (!IsValidTown(town_id
)) return false;
223 return ::Town::Get(town_id
)->larger_town
;
226 /* static */ int ScriptTown::GetRoadReworkDuration(TownID town_id
)
228 if (!IsValidTown(town_id
)) return -1;
230 return ::Town::Get(town_id
)->road_build_months
;
233 /* static */ int ScriptTown::GetFundBuildingsDuration(TownID town_id
)
235 if (!IsValidTown(town_id
)) return -1;
237 return ::Town::Get(town_id
)->fund_buildings_months
;
240 /* static */ ScriptCompany::CompanyID
ScriptTown::GetExclusiveRightsCompany(TownID town_id
)
242 if (ScriptObject::GetCompany() == OWNER_DEITY
) return ScriptCompany::COMPANY_INVALID
;
243 if (!IsValidTown(town_id
)) return ScriptCompany::COMPANY_INVALID
;
245 return (ScriptCompany::CompanyID
)(int8
)::Town::Get(town_id
)->exclusivity
;
248 /* static */ int32
ScriptTown::GetExclusiveRightsDuration(TownID town_id
)
250 if (!IsValidTown(town_id
)) return -1;
252 return ::Town::Get(town_id
)->exclusive_counter
;
255 /* static */ bool ScriptTown::IsActionAvailable(TownID town_id
, TownAction town_action
)
257 if (ScriptObject::GetCompany() == OWNER_DEITY
) return false;
258 if (!IsValidTown(town_id
)) return false;
260 return HasBit(::GetMaskOfTownActions(NULL
, ScriptObject::GetCompany(), ::Town::Get(town_id
)), town_action
);
263 /* static */ bool ScriptTown::PerformTownAction(TownID town_id
, TownAction town_action
)
265 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY
);
266 EnforcePrecondition(false, IsValidTown(town_id
));
267 EnforcePrecondition(false, IsActionAvailable(town_id
, town_action
));
269 return ScriptObject::DoCommand(::Town::Get(town_id
)->xy
, town_id
, town_action
, CMD_DO_TOWN_ACTION
);
272 /* static */ bool ScriptTown::ExpandTown(TownID town_id
, int houses
)
274 EnforcePrecondition(false, ScriptObject::GetCompany() == OWNER_DEITY
);
275 EnforcePrecondition(false, IsValidTown(town_id
));
276 EnforcePrecondition(false, houses
> 0);
278 return ScriptObject::DoCommand(::Town::Get(town_id
)->xy
, town_id
, houses
, CMD_EXPAND_TOWN
);
281 /* static */ bool ScriptTown::FoundTown(TileIndex tile
, TownSize size
, bool city
, RoadLayout layout
, Text
*name
)
283 CCountedPtr
<Text
> counter(name
);
285 EnforcePrecondition(false, ScriptObject::GetCompany() == OWNER_DEITY
|| _settings_game
.economy
.found_town
!= TF_FORBIDDEN
);
286 EnforcePrecondition(false, ::IsValidTile(tile
));
287 EnforcePrecondition(false, size
== TOWN_SIZE_SMALL
|| size
== TOWN_SIZE_MEDIUM
|| size
== TOWN_SIZE_LARGE
)
288 EnforcePrecondition(false, size
!= TOWN_SIZE_LARGE
|| ScriptObject::GetCompany() == OWNER_DEITY
);
289 if (ScriptObject::GetCompany() == OWNER_DEITY
|| _settings_game
.economy
.found_town
== TF_CUSTOM_LAYOUT
) {
290 EnforcePrecondition(false, layout
== ROAD_LAYOUT_ORIGINAL
|| layout
== ROAD_LAYOUT_BETTER_ROADS
|| layout
== ROAD_LAYOUT_2x2
|| layout
== ROAD_LAYOUT_3x3
);
292 /* The layout parameter is ignored for AIs when custom layouts is disabled. */
293 layout
= (RoadLayout
) (byte
)_settings_game
.economy
.town_layout
;
296 const char *text
= NULL
;
298 text
= name
->GetDecodedText();
299 EnforcePreconditionEncodedText(false, text
);
300 EnforcePreconditionCustomError(false, ::Utf8StringLength(text
) < MAX_LENGTH_TOWN_NAME_CHARS
, ScriptError::ERR_PRECONDITION_STRING_TOO_LONG
);
302 uint32 townnameparts
;
303 if (!GenerateTownName(&townnameparts
)) {
304 ScriptObject::SetLastError(ScriptError::ERR_NAME_IS_NOT_UNIQUE
);
308 return ScriptObject::DoCommand(tile
, size
| (city
? 1 << 2 : 0) | layout
<< 3, townnameparts
, CMD_FOUND_TOWN
, text
);
311 /* static */ ScriptTown::TownRating
ScriptTown::GetRating(TownID town_id
, ScriptCompany::CompanyID company_id
)
313 if (!IsValidTown(town_id
)) return TOWN_RATING_INVALID
;
314 ScriptCompany::CompanyID company
= ScriptCompany::ResolveCompanyID(company_id
);
315 if (company
== ScriptCompany::COMPANY_INVALID
) return TOWN_RATING_INVALID
;
317 const Town
*t
= ::Town::Get(town_id
);
318 if (!HasBit(t
->have_ratings
, company
)) {
319 return TOWN_RATING_NONE
;
320 } else if (t
->ratings
[company
] <= RATING_APPALLING
) {
321 return TOWN_RATING_APPALLING
;
322 } else if (t
->ratings
[company
] <= RATING_VERYPOOR
) {
323 return TOWN_RATING_VERY_POOR
;
324 } else if (t
->ratings
[company
] <= RATING_POOR
) {
325 return TOWN_RATING_POOR
;
326 } else if (t
->ratings
[company
] <= RATING_MEDIOCRE
) {
327 return TOWN_RATING_MEDIOCRE
;
328 } else if (t
->ratings
[company
] <= RATING_GOOD
) {
329 return TOWN_RATING_GOOD
;
330 } else if (t
->ratings
[company
] <= RATING_VERYGOOD
) {
331 return TOWN_RATING_VERY_GOOD
;
332 } else if (t
->ratings
[company
] <= RATING_EXCELLENT
) {
333 return TOWN_RATING_EXCELLENT
;
335 return TOWN_RATING_OUTSTANDING
;
339 /* static */ int ScriptTown::GetAllowedNoise(TownID town_id
)
341 if (!IsValidTown(town_id
)) return -1;
343 const Town
*t
= ::Town::Get(town_id
);
344 if (_settings_game
.economy
.station_noise_level
) {
345 return t
->MaxTownNoise() - t
->noise_reached
;
350 FOR_ALL_STATIONS(st
) {
351 if (st
->town
== t
&& (st
->facilities
& FACIL_AIRPORT
) && st
->airport
.type
!= AT_OILRIG
) num
++;
353 return max(0, 2 - num
);
356 /* static */ ScriptTown::RoadLayout
ScriptTown::GetRoadLayout(TownID town_id
)
358 if (!IsValidTown(town_id
)) return ROAD_LAYOUT_INVALID
;
360 return (ScriptTown::RoadLayout
)((TownLayout
)::Town::Get(town_id
)->layout
);