Tracerestrict: Adjust comment for GetTraceRestrictTypeProperties
[openttd-jgr.git] / src / newgrf_house.cpp
blob70f1b26d4d1c36598ab5b524d22dd2c02f595f6e
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 newgrf_house.cpp Implementation of NewGRF houses. */
10 #include "stdafx.h"
11 #include "debug.h"
12 #include "landscape.h"
13 #include "newgrf_house.h"
14 #include "newgrf_spritegroup.h"
15 #include "newgrf_text.h"
16 #include "newgrf_town.h"
17 #include "newgrf_sound.h"
18 #include "company_func.h"
19 #include "company_base.h"
20 #include "town.h"
21 #include "genworld.h"
22 #include "newgrf_animation_base.h"
23 #include "newgrf_cargo.h"
24 #include "station_base.h"
25 #include "newgrf_analysis.h"
26 #include "newgrf_extension.h"
28 #include "safeguards.h"
30 #include "table/strings.h"
32 static BuildingCounts<uint32_t> _building_counts{};
33 static std::vector<HouseClassMapping> _class_mapping{};
35 HouseOverrideManager _house_mngr(NEW_HOUSE_OFFSET, NUM_HOUSES, INVALID_HOUSE_ID);
37 /**
38 * Retrieve the grf file associated with a house.
39 * @param house_id House to query.
40 * @return The associated GRF file (may be \c nullptr).
42 static const GRFFile *GetHouseSpecGrf(HouseID house_id)
44 const HouseSpec *hs = HouseSpec::Get(house_id);
45 return (hs != nullptr) ? hs->grf_prop.grffile : nullptr;
48 extern const HouseSpec _original_house_specs[NEW_HOUSE_OFFSET];
49 std::vector<HouseSpec> _house_specs;
51 /**
52 * Get a reference to all HouseSpecs.
53 * @return Reference to vector of all HouseSpecs.
55 std::vector<HouseSpec> &HouseSpec::Specs()
57 return _house_specs;
60 /**
61 * Gets the index of this spec.
62 * @return The index.
64 HouseID HouseSpec::Index() const
66 return static_cast<HouseID>(this - _house_specs.data());
69 /**
70 * Get the spec for a house ID.
71 * @param house_id The ID of the house.
72 * @return The HouseSpec associated with the ID.
74 HouseSpec *HouseSpec::Get(size_t house_id)
76 /* Empty house if index is out of range -- this might happen if NewGRFs are changed. */
77 static HouseSpec empty = {};
79 assert(house_id < NUM_HOUSES);
80 if (house_id >= _house_specs.size()) return &empty;
81 return &_house_specs[house_id];
84 /* Reset and initialise house specs. */
85 void ResetHouses()
87 _house_specs.clear();
88 _house_specs.reserve(std::size(_original_house_specs));
90 ResetHouseClassIDs();
92 /* Copy default houses. */
93 _house_specs.insert(std::end(_house_specs), std::begin(_original_house_specs), std::end(_original_house_specs));
95 /* Reset any overrides that have been set. */
96 _house_mngr.ResetOverride();
99 /**
100 * Construct a resolver for a house.
101 * @param house_id House to query.
102 * @param tile %Tile containing the house.
103 * @param town %Town containing the house.
104 * @param callback Callback ID.
105 * @param param1 First parameter (var 10) of the callback.
106 * @param param2 Second parameter (var 18) of the callback.
107 * @param not_yet_constructed House is still under construction.
108 * @param initial_random_bits Random bits during construction checks.
109 * @param watched_cargo_triggers Cargo types that triggered the watched cargo callback.
111 HouseResolverObject::HouseResolverObject(HouseID house_id, TileIndex tile, Town *town,
112 CallbackID callback, uint32_t param1, uint32_t param2,
113 bool not_yet_constructed, uint8_t initial_random_bits, CargoTypes watched_cargo_triggers, int view)
114 : ResolverObject(GetHouseSpecGrf(house_id), callback, param1, param2),
115 house_scope(*this, house_id, tile, town, not_yet_constructed, initial_random_bits, watched_cargo_triggers, view),
116 town_scope(*this, town, not_yet_constructed) // Don't access StorePSA if house is not yet constructed.
118 /* Tile must be valid and a house tile, unless not yet constructed in which case it may also be INVALID_TILE. */
119 assert((IsValidTile(tile) && (not_yet_constructed || IsTileType(tile, MP_HOUSE))) || (not_yet_constructed && tile == INVALID_TILE));
121 this->root_spritegroup = HouseSpec::Get(house_id)->grf_prop.spritegroup[0];
124 GrfSpecFeature HouseResolverObject::GetFeature() const
126 return GSF_HOUSES;
129 uint32_t HouseResolverObject::GetDebugID() const
131 return HouseSpec::Get(this->house_scope.house_id)->grf_prop.local_id;
134 void ResetHouseClassIDs()
136 _class_mapping.clear();
138 /* Add initial entry for HOUSE_NO_CLASS. */
139 _class_mapping.emplace_back();
142 HouseClassID AllocateHouseClassID(uint8_t grf_class_id, uint32_t grfid)
144 /* Start from 1 because 0 means that no class has been assigned. */
145 auto it = std::find_if(std::next(std::begin(_class_mapping)), std::end(_class_mapping), [grf_class_id, grfid](const HouseClassMapping &map) { return map.class_id == grf_class_id && map.grfid == grfid; });
147 /* HouseClass not found, allocate a new one. */
148 if (it == std::end(_class_mapping)) it = _class_mapping.insert(it, {.grfid = grfid, .class_id = grf_class_id});
150 return static_cast<HouseClassID>(std::distance(std::begin(_class_mapping), it));
154 * Initialise building counts for a town.
155 * @param t Town cache to initialise.
157 void InitializeBuildingCounts(Town *t)
159 t->cache.building_counts.id_count.clear();
160 t->cache.building_counts.class_count.clear();
161 t->cache.building_counts.id_count.resize(HouseSpec::Specs().size());
162 t->cache.building_counts.class_count.resize(_class_mapping.size());
166 * Initialise global building counts and all town building counts.
168 void InitializeBuildingCounts()
170 _building_counts.id_count.clear();
171 _building_counts.class_count.clear();
172 _building_counts.id_count.resize(HouseSpec::Specs().size());
173 _building_counts.class_count.resize(_class_mapping.size());
175 for (Town *t : Town::Iterate()) {
176 InitializeBuildingCounts(t);
181 * Get read-only span of total HouseID building counts.
182 * @return span of HouseID building counts.
184 std::span<const uint> GetBuildingHouseIDCounts()
186 return _building_counts.id_count;
190 * IncreaseBuildingCount()
191 * Increase the count of a building when it has been added by a town.
192 * @param t The town that the building is being built in
193 * @param house_id The id of the house being added
195 void IncreaseBuildingCount(Town *t, HouseID house_id)
197 HouseClassID class_id = HouseSpec::Get(house_id)->class_id;
199 t->cache.building_counts.id_count[house_id]++;
200 _building_counts.id_count[house_id]++;
202 if (class_id == HOUSE_NO_CLASS) return;
204 t->cache.building_counts.class_count[class_id]++;
205 _building_counts.class_count[class_id]++;
209 * DecreaseBuildingCount()
210 * Decrease the number of a building when it is deleted.
211 * @param t The town that the building was built in
212 * @param house_id The id of the house being removed
214 void DecreaseBuildingCount(Town *t, HouseID house_id)
216 HouseClassID class_id = HouseSpec::Get(house_id)->class_id;
218 if (t->cache.building_counts.id_count[house_id] > 0) t->cache.building_counts.id_count[house_id]--;
219 if (_building_counts.id_count[house_id] > 0) _building_counts.id_count[house_id]--;
221 if (class_id == HOUSE_NO_CLASS) return;
223 if (t->cache.building_counts.class_count[class_id] > 0) t->cache.building_counts.class_count[class_id]--;
224 if (_building_counts.class_count[class_id] > 0) _building_counts.class_count[class_id]--;
227 /* virtual */ uint32_t HouseScopeResolver::GetRandomBits() const
229 /* Note: Towns build houses over houses. So during construction checks 'tile' may be a valid but unrelated house. */
230 return this->not_yet_constructed ? this->initial_random_bits : GetHouseRandomBits(this->tile);
233 /* virtual */ uint32_t HouseScopeResolver::GetTriggers() const
235 /* Note: Towns build houses over houses. So during construction checks 'tile' may be a valid but unrelated house. */
236 return this->not_yet_constructed ? 0 : GetHouseTriggers(this->tile);
239 static uint32_t GetNumHouses(HouseID house_id, const Town *town)
241 HouseClassID class_id = HouseSpec::Get(house_id)->class_id;
243 uint8_t map_id_count = ClampTo<uint8_t>(_building_counts.id_count[house_id]);
244 uint8_t map_class_count = ClampTo<uint8_t>(_building_counts.class_count[class_id]);
245 uint8_t town_id_count = ClampTo<uint8_t>(town->cache.building_counts.id_count[house_id]);
246 uint8_t town_class_count = ClampTo<uint8_t>(town->cache.building_counts.class_count[class_id]);
248 return map_class_count << 24 | town_class_count << 16 | map_id_count << 8 | town_id_count;
252 * Get information about a nearby tile.
253 * @param parameter from callback. It's in fact a pair of coordinates
254 * @param tile TileIndex from which the callback was initiated
255 * @param grf_version8 True, if we are dealing with a new NewGRF which uses GRF version >= 8.
256 * @return a construction of bits obeying the newgrf format
258 static uint32_t GetNearbyTileInformation(uint8_t parameter, TileIndex tile, bool grf_version8, uint32_t mask)
260 tile = GetNearbyTile(parameter, tile);
261 return GetNearbyTileInformation(tile, grf_version8, mask);
264 /** Structure with user-data for SearchNearbyHouseXXX - functions */
265 struct SearchNearbyHouseData {
266 const HouseSpec *hs; ///< Specs of the house that started the search.
267 TileIndex north_tile; ///< Northern tile of the house.
271 * Callback function to search a house by its HouseID
272 * @param tile TileIndex to be examined
273 * @param user_data SearchNearbyHouseData
274 * @return true or false, if found or not
276 static bool SearchNearbyHouseID(TileIndex tile, void *user_data)
278 if (IsTileType(tile, MP_HOUSE)) {
279 HouseID house = GetHouseType(tile); // tile been examined
280 const HouseSpec *hs = HouseSpec::Get(house);
281 if (hs->grf_prop.HasGrfFile()) { // must be one from a grf file
282 SearchNearbyHouseData *nbhd = (SearchNearbyHouseData *)user_data;
284 TileIndex north_tile = tile + GetHouseNorthPart(house); // modifies 'house'!
285 if (north_tile == nbhd->north_tile) return false; // Always ignore origin house
287 return hs->grf_prop.local_id == nbhd->hs->grf_prop.local_id && // same local id as the one requested
288 hs->grf_prop.grfid == nbhd->hs->grf_prop.grfid; // from the same grf
291 return false;
295 * Callback function to search a house by its classID
296 * @param tile TileIndex to be examined
297 * @param user_data SearchNearbyHouseData
298 * @return true or false, if found or not
300 static bool SearchNearbyHouseClass(TileIndex tile, void *user_data)
302 if (IsTileType(tile, MP_HOUSE)) {
303 HouseID house = GetHouseType(tile); // tile been examined
304 const HouseSpec *hs = HouseSpec::Get(house);
305 if (hs->grf_prop.HasGrfFile()) { // must be one from a grf file
306 SearchNearbyHouseData *nbhd = (SearchNearbyHouseData *)user_data;
308 TileIndex north_tile = tile + GetHouseNorthPart(house); // modifies 'house'!
309 if (north_tile == nbhd->north_tile) return false; // Always ignore origin house
311 return hs->class_id == nbhd->hs->class_id && // same classid as the one requested
312 hs->grf_prop.grfid == nbhd->hs->grf_prop.grfid; // from the same grf
315 return false;
319 * Callback function to search a house by its grfID
320 * @param tile TileIndex to be examined
321 * @param user_data SearchNearbyHouseData
322 * @return true or false, if found or not
324 static bool SearchNearbyHouseGRFID(TileIndex tile, void *user_data)
326 if (IsTileType(tile, MP_HOUSE)) {
327 HouseID house = GetHouseType(tile); // tile been examined
328 const HouseSpec *hs = HouseSpec::Get(house);
329 if (hs->grf_prop.HasGrfFile()) { // must be one from a grf file
330 SearchNearbyHouseData *nbhd = (SearchNearbyHouseData *)user_data;
332 TileIndex north_tile = tile + GetHouseNorthPart(house); // modifies 'house'!
333 if (north_tile == nbhd->north_tile) return false; // Always ignore origin house
335 return hs->grf_prop.grfid == nbhd->hs->grf_prop.grfid; // from the same grf
338 return false;
342 * This function will activate a search around a central tile, looking for some houses
343 * that fit the requested characteristics
344 * @param parameter that is given by the callback.
345 * bits 0..6 radius of the search
346 * bits 7..8 search type i.e.: 0 = houseID/ 1 = classID/ 2 = grfID
347 * @param tile TileIndex from which to start the search
348 * @param house the HouseID that is associated to the house, the callback is called for
349 * @return the Manhattan distance from the center tile, if any, and 0 if failure
351 static uint32_t GetDistanceFromNearbyHouse(uint8_t parameter, TileIndex tile, HouseID house)
353 static TestTileOnSearchProc * const search_procs[3] = {
354 SearchNearbyHouseID,
355 SearchNearbyHouseClass,
356 SearchNearbyHouseGRFID,
358 TileIndex found_tile = tile;
359 uint8_t searchtype = GB(parameter, 6, 2);
360 uint8_t searchradius = GB(parameter, 0, 6);
361 if (searchtype >= lengthof(search_procs)) return 0; // do not run on ill-defined code
362 if (searchradius < 1) return 0; // do not use a too low radius
364 SearchNearbyHouseData nbhd;
365 nbhd.hs = HouseSpec::Get(house);
366 nbhd.north_tile = tile + GetHouseNorthPart(house); // modifies 'house'!
368 /* Use a pointer for the tile to start the search. Will be required for calculating the distance*/
369 if (CircularTileSearch(&found_tile, 2 * searchradius + 1, search_procs[searchtype], &nbhd)) {
370 return DistanceManhattan(found_tile, tile);
372 return 0;
375 HouseID HouseScopeResolver::GetOtherHouseID(uint32_t parameter) const
377 const HouseSpec *hs = HouseSpec::Get(this->house_id);
378 if (!hs->grf_prop.HasGrfFile()) return INVALID_HOUSE_ID;
380 return _house_mngr.GetID(parameter, hs->grf_prop.grfid);
383 template <typename F>
384 uint32_t HouseScopeResolver::OtherHouseIDVariable(uint32_t parameter, F func) const
386 HouseID new_house = this->GetOtherHouseID(parameter);
387 return new_house == INVALID_HOUSE_ID ? 0 : func(new_house);
391 * @note Used by the resolver to get values for feature 07 deterministic spritegroups.
393 /* virtual */ uint32_t HouseScopeResolver::GetVariable(uint16_t variable, uint32_t parameter, GetVariableExtra &extra) const
395 if (this->tile == INVALID_TILE) {
396 /* House does not yet exist, nor is it being planned to exist. Provide some default values intead. */
397 switch (variable) {
398 case 0x40: return TOWN_HOUSE_COMPLETED | this->view << 2; /* Construction stage. */
399 case 0x41: return 0;
400 case 0x42: return 0;
401 case 0x43: return 0;
402 case 0x44: return 0;
403 case 0x45: return _generating_world ? 1 : 0;
404 case 0x46: return 0;
405 case 0x47: return 0;
406 case 0x60: return 0;
407 case 0x61: return 0;
408 case 0x62: return 0;
409 case 0x63: return 0;
410 case 0x64: return 0;
411 case 0x65: return 0;
412 case 0x66: return 0xFFFFFFFF; /* Class and ID of nearby house. */
413 case 0x67: return 0;
416 Debug(grf, 1, "Unhandled house variable 0x{:X}", variable);
417 extra.available = false;
418 return UINT_MAX;
421 switch (variable) {
422 /* Construction stage. */
423 case 0x40: return (IsTileType(this->tile, MP_HOUSE) ? GetHouseBuildingStage(this->tile) : 0) | TileHash2Bit(TileX(this->tile), TileY(this->tile)) << 2;
425 /* Building age. */
426 case 0x41: return IsTileType(this->tile, MP_HOUSE) ? GetHouseAge(this->tile).base() : 0;
428 /* Town zone */
429 case 0x42: return GetTownRadiusGroup(this->town, this->tile);
431 /* Terrain type */
432 case 0x43: return GetTerrainType(this->tile);
434 /* Number of this type of building on the map. */
435 case 0x44: return GetNumHouses(this->house_id, this->town);
437 case A2VRI_HOUSE_SAME_ID_MAP_COUNT: return _building_counts.id_count[this->house_id];
438 case A2VRI_HOUSE_SAME_CLASS_MAP_COUNT: return _building_counts.class_count[HouseSpec::Get(this->house_id)->class_id];
439 case A2VRI_HOUSE_SAME_ID_TOWN_COUNT: return this->town->cache.building_counts.id_count[this->house_id];
440 case A2VRI_HOUSE_SAME_CLASS_TOWN_COUNT: return this->town->cache.building_counts.class_count[HouseSpec::Get(this->house_id)->class_id];
442 /* Whether the town is being created or just expanded. */
443 case 0x45: return _generating_world ? 1 : 0;
445 /* Current animation frame. */
446 case 0x46: return IsTileType(this->tile, MP_HOUSE) ? GetAnimationFrame(this->tile) : 0;
448 /* Position of the house */
449 case 0x47: return TileY(this->tile) << 16 | (TileX(this->tile) & 0xFFFF);
451 /* Building counts for old houses with id = parameter. */
452 case 0x60: return parameter < NEW_HOUSE_OFFSET ? GetNumHouses(parameter, this->town) : 0;
454 case A2VRI_HOUSE_OTHER_OLD_ID_MAP_COUNT: return parameter < NEW_HOUSE_OFFSET ? _building_counts.id_count[parameter] : 0;
455 case A2VRI_HOUSE_OTHER_OLD_ID_TOWN_COUNT: return parameter < NEW_HOUSE_OFFSET ? this->town->cache.building_counts.id_count[parameter] : 0;
457 /* Building counts for new houses with id = parameter. */
458 case 0x61: return this->OtherHouseIDVariable(parameter, [&](HouseID new_house) { return GetNumHouses(new_house, this->town); });
460 case A2VRI_HOUSE_OTHER_ID_MAP_COUNT: return this->OtherHouseIDVariable(parameter, [&](HouseID new_house) { return _building_counts.id_count[new_house]; });
461 case A2VRI_HOUSE_OTHER_CLASS_MAP_COUNT: return this->OtherHouseIDVariable(parameter, [&](HouseID new_house) { return _building_counts.class_count[HouseSpec::Get(new_house)->class_id]; });
462 case A2VRI_HOUSE_OTHER_ID_TOWN_COUNT: return this->OtherHouseIDVariable(parameter, [&](HouseID new_house) { return this->town->cache.building_counts.id_count[new_house]; });
463 case A2VRI_HOUSE_OTHER_CLASS_TOWN_COUNT: return this->OtherHouseIDVariable(parameter, [&](HouseID new_house) { return this->town->cache.building_counts.class_count[HouseSpec::Get(new_house)->class_id]; });
465 /* Land info for nearby tiles. */
466 case 0x62: return GetNearbyTileInformation(parameter, this->tile, this->ro.grffile->grf_version >= 8, extra.mask);
468 /* Current animation frame of nearby house tiles */
469 case 0x63: {
470 TileIndex testtile = GetNearbyTile(parameter, this->tile);
471 return IsTileType(testtile, MP_HOUSE) ? GetAnimationFrame(testtile) : 0;
474 /* Cargo acceptance history of nearby stations */
475 case 0x64: {
476 CargoID cid = GetCargoTranslation(parameter, this->ro.grffile);
477 if (cid == INVALID_CARGO) return 0;
479 /* Extract tile offset. */
480 int8_t x_offs = GB(GetRegister(0x100), 0, 8);
481 int8_t y_offs = GB(GetRegister(0x100), 8, 8);
482 TileIndex testtile = TILE_MASK(this->tile + TileDiffXY(x_offs, y_offs));
484 StationFinder stations(TileArea(testtile, 1, 1));
486 /* Collect acceptance stats. */
487 uint32_t res = 0;
488 for (Station *st : stations.GetStations()) {
489 if (HasBit(st->goods[cid].status, GoodsEntry::GES_EVER_ACCEPTED)) SetBit(res, 0);
490 if (HasBit(st->goods[cid].status, GoodsEntry::GES_LAST_MONTH)) SetBit(res, 1);
491 if (HasBit(st->goods[cid].status, GoodsEntry::GES_CURRENT_MONTH)) SetBit(res, 2);
492 if (HasBit(st->goods[cid].status, GoodsEntry::GES_ACCEPTED_BIGTICK)) SetBit(res, 3);
495 /* Cargo triggered CB 148? */
496 if (HasBit(this->watched_cargo_triggers, cid)) SetBit(res, 4);
498 return res;
501 /* Distance test for some house types */
502 case 0x65: return GetDistanceFromNearbyHouse(parameter, this->tile, this->house_id);
504 /* Class and ID of nearby house tile */
505 case 0x66: {
506 TileIndex testtile = GetNearbyTile(parameter, this->tile);
507 if (!IsTileType(testtile, MP_HOUSE)) return 0xFFFFFFFF;
508 HouseID nearby_house_id = GetHouseType(testtile);
509 HouseSpec *hs = HouseSpec::Get(nearby_house_id);
510 /* Information about the grf local classid if the house has a class */
511 uint houseclass = 0;
512 if (hs->class_id != HOUSE_NO_CLASS) {
513 houseclass = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8;
514 houseclass |= _class_mapping[hs->class_id].class_id;
516 /* old house type or grf-local houseid */
517 uint local_houseid = 0;
518 if (nearby_house_id < NEW_HOUSE_OFFSET) {
519 local_houseid = nearby_house_id;
520 } else {
521 local_houseid = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8;
522 local_houseid |= ClampTo<uint8_t>(hs->grf_prop.local_id); // Spec only allows 8 bits, so all local-ids above 254 are clamped.
524 return houseclass << 16 | local_houseid;
527 /* GRFID of nearby house tile */
528 case 0x67: {
529 TileIndex testtile = GetNearbyTile(parameter, this->tile);
530 if (!IsTileType(testtile, MP_HOUSE)) return 0xFFFFFFFF;
531 HouseID house_id = GetHouseType(testtile);
532 if (house_id < NEW_HOUSE_OFFSET) return 0;
533 /* Checking the grffile information via HouseSpec doesn't work
534 * in case the newgrf was removed. */
535 return _house_mngr.GetGRFID(house_id);
539 Debug(grf, 1, "Unhandled house variable 0x{:X}", variable);
541 extra.available = false;
542 return UINT_MAX;
547 * @note Used by the resolver to get values for feature 07 deterministic spritegroups.
549 /* virtual */ uint32_t FakeHouseScopeResolver::GetVariable(uint16_t variable, uint32_t parameter, GetVariableExtra &extra) const
551 switch (variable) {
552 /* Construction stage. */
553 case 0x40: return TOWN_HOUSE_COMPLETED;
555 /* Building age. */
556 case 0x41: return 0;
558 /* Town zone */
559 case 0x42: return FindFirstBit<HouseZones>(HouseSpec::Get(this->house_id)->building_availability & HZ_ZONALL); // first available
561 /* Terrain type */
562 case 0x43: return _settings_game.game_creation.landscape == LT_ARCTIC && (HouseSpec::Get(house_id)->building_availability & (HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW)) == HZ_SUBARTC_ABOVE ? 4 : 0;
564 /* Number of this type of building on the map. */
565 case 0x44: return 0;
567 /* Whether the town is being created or just expanded. */
568 case 0x45: return 0;
570 /* Current animation frame. */
571 case 0x46: return 0;
573 /* Position of the house */
574 case 0x47: return 0xFFFFFFFF;
576 /* Building counts for old houses with id = parameter. */
577 case 0x60: return 0;
579 /* Building counts for new houses with id = parameter. */
580 case 0x61: return 0;
582 /* Land info for nearby tiles. */
583 case 0x62: return 0;
585 /* Current animation frame of nearby house tiles */
586 case 0x63: return 0;
588 /* Cargo acceptance history of nearby stations */
589 case 0x64: return 0;
591 /* Distance test for some house types */
592 case 0x65: return 0;
594 /* Class and ID of nearby house tile */
595 case 0x66: return 0xFFFFFFFF;
597 /* GRFID of nearby house tile */
598 case 0x67: return 0xFFFFFFFF;
601 Debug(grf, 1, "Unhandled house variable 0x{:X}", variable);
603 extra.available = false;
604 return UINT_MAX;
607 uint16_t GetHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, HouseID house_id, Town *town, TileIndex tile,
608 bool not_yet_constructed, uint8_t initial_random_bits, CargoTypes watched_cargo_triggers, int view)
610 HouseResolverObject object(house_id, tile, town, callback, param1, param2,
611 not_yet_constructed, initial_random_bits, watched_cargo_triggers, view);
612 return object.ResolveCallback();
616 * Get the name of a house.
617 * @param house House type.
618 * @param tile Tile where the house is located. INVALID_TILE to get the general name of houses of the given type.
619 * @return Name of the house.
621 StringID GetHouseName(HouseID house_id, TileIndex tile)
623 const HouseSpec *hs = HouseSpec::Get(house_id);
624 bool house_completed = (tile == INVALID_TILE) || IsHouseCompleted(tile);
625 Town *t = (tile == INVALID_TILE) ? nullptr : Town::GetByTile(tile);
627 uint16_t callback_res = GetHouseCallback(CBID_HOUSE_CUSTOM_NAME, house_completed ? 1 : 0, 0, house_id, t, tile);
628 if (callback_res != CALLBACK_FAILED && callback_res != 0x400) {
629 if (callback_res > 0x400) {
630 ErrorUnknownCallbackResult(hs->grf_prop.grfid, CBID_HOUSE_CUSTOM_NAME, callback_res);
631 } else {
632 StringID ret = GetGRFStringID(hs->grf_prop.grffile, 0xD000 + callback_res);
633 if (ret != STR_NULL && ret != STR_UNDEFINED) return ret;
637 return hs->building_name;
640 static inline PaletteID GetHouseColour(HouseID house_id, TileIndex tile = INVALID_TILE)
642 const HouseSpec *hs = HouseSpec::Get(house_id);
643 if (HasBit(hs->callback_mask, CBM_HOUSE_COLOUR)) {
644 Town *t = (tile != INVALID_TILE) ? Town::GetByTile(tile) : nullptr;
645 uint16_t callback = GetHouseCallback(CBID_HOUSE_COLOUR, 0, 0, house_id, t, tile);
646 if (callback != CALLBACK_FAILED) {
647 /* If bit 14 is set, we should use a 2cc colour map, else use the callback value. */
648 return HasBit(callback, 14) ? GB(callback, 0, 8) + SPR_2CCMAP_BASE : callback;
651 return GENERAL_SPRITE_COLOUR(hs->random_colour[TileHash2Bit(TileX(tile), TileY(tile))]);
654 static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, uint8_t stage, HouseID house_id)
656 const DrawTileSprites *dts = group->ProcessRegisters(&stage);
658 PaletteID palette = GetHouseColour(house_id, ti->tile);
660 SpriteID image = dts->ground.sprite;
661 PaletteID pal = dts->ground.pal;
663 if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) image += stage;
664 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += stage;
666 if (GB(image, 0, SPRITE_WIDTH) != 0) {
667 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
670 DrawNewGRFTileSeq(ti, dts, TO_HOUSES, stage, palette);
673 void DrawNewHouseTile(TileInfo *ti, HouseID house_id)
675 const HouseSpec *hs = HouseSpec::Get(house_id);
677 if (ti->tileh != SLOPE_FLAT) {
678 bool draw_old_one = true;
679 if (HasBit(hs->callback_mask, CBM_HOUSE_DRAW_FOUNDATIONS)) {
680 /* Called to determine the type (if any) of foundation to draw for the house tile */
681 uint32_t callback_res = GetHouseCallback(CBID_HOUSE_DRAW_FOUNDATIONS, 0, 0, house_id, Town::GetByTile(ti->tile), ti->tile);
682 if (callback_res != CALLBACK_FAILED) draw_old_one = ConvertBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DRAW_FOUNDATIONS, callback_res);
685 if (draw_old_one) DrawFoundation(ti, FOUNDATION_LEVELED);
688 HouseResolverObject object(house_id, ti->tile, Town::GetByTile(ti->tile));
690 const SpriteGroup *group = object.Resolve();
691 if (group != nullptr && group->type == SGT_TILELAYOUT) {
692 /* Limit the building stage to the number of stages supplied. */
693 const TileLayoutSpriteGroup *tlgroup = (const TileLayoutSpriteGroup *)group;
694 uint8_t stage = GetHouseBuildingStage(ti->tile);
695 DrawTileLayout(ti, tlgroup, stage, house_id);
699 /* Simple wrapper for GetHouseCallback to keep the animation unified. */
700 uint16_t GetSimpleHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, const HouseSpec *spec, Town *town, TileIndex tile, CargoTypes extra_data)
702 return GetHouseCallback(callback, param1, param2, spec - HouseSpec::Get(0), town, tile, false, 0, extra_data);
705 /** Helper class for animation control. */
706 struct HouseAnimationBase : public AnimationBase<HouseAnimationBase, HouseSpec, Town, CargoTypes, GetSimpleHouseCallback, TileAnimationFrameAnimationHelper<Town> > {
707 static const CallbackID cb_animation_speed = CBID_HOUSE_ANIMATION_SPEED;
708 static const CallbackID cb_animation_next_frame = CBID_HOUSE_ANIMATION_NEXT_FRAME;
710 static const HouseCallbackMask cbm_animation_speed = CBM_HOUSE_ANIMATION_SPEED;
711 static const HouseCallbackMask cbm_animation_next_frame = CBM_HOUSE_ANIMATION_NEXT_FRAME;
714 void AnimateNewHouseTile(TileIndex tile)
716 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
717 if (hs == nullptr) return;
719 HouseAnimationBase::AnimateTile(hs, Town::GetByTile(tile), tile, HasFlag(hs->extra_flags, CALLBACK_1A_RANDOM_BITS));
722 void AnimateNewHouseConstruction(TileIndex tile)
724 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
726 if (HasBit(hs->callback_mask, CBM_HOUSE_CONSTRUCTION_STATE_CHANGE)) {
727 HouseAnimationBase::ChangeAnimationFrame(CBID_HOUSE_CONSTRUCTION_STATE_CHANGE, hs, Town::GetByTile(tile), tile, 0, 0);
731 uint8_t GetNewHouseTileAnimationSpeed(TileIndex tile)
733 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
734 if (hs == nullptr) return 0;
736 return HouseAnimationBase::GetAnimationSpeed(hs);
740 * Check if GRF allows a given house to be constructed (callback 17)
741 * @param house_id house type
742 * @param tile tile where the house is about to be placed
743 * @param t town in which we are building
744 * @param random_bits feature random bits for the house
745 * @return false if callback 17 disallows construction, true in other cases
747 bool HouseAllowsConstruction(HouseID house_id, TileIndex tile, Town *t, uint8_t random_bits)
749 const HouseSpec *hs = HouseSpec::Get(house_id);
750 if (HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) {
751 uint16_t callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house_id, t, tile, true, random_bits);
752 if (callback_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_ALLOW_CONSTRUCTION, callback_res)) {
753 return false;
756 return true;
759 bool CanDeleteHouse(TileIndex tile)
761 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
763 /* Humans are always allowed to remove buildings, as is water and disasters and
764 * anyone using the scenario editor. */
765 if (Company::IsValidHumanID(_current_company) || _current_company == OWNER_WATER || _current_company == OWNER_NONE || _game_mode == GM_EDITOR || _generating_world) {
766 return true;
769 if (HasBit(hs->callback_mask, CBM_HOUSE_DENY_DESTRUCTION)) {
770 uint16_t callback_res = GetHouseCallback(CBID_HOUSE_DENY_DESTRUCTION, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
771 return (callback_res == CALLBACK_FAILED || !ConvertBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DENY_DESTRUCTION, callback_res));
772 } else {
773 return !(hs->extra_flags & BUILDING_IS_PROTECTED);
777 static void AnimationControl(TileIndex tile, uint16_t random_bits)
779 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
781 if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_START_STOP)) {
782 uint32_t param = (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) ? (GB(Random(), 0, 16) | random_bits << 16) : Random();
783 HouseAnimationBase::ChangeAnimationFrame(CBID_HOUSE_ANIMATION_START_STOP, hs, Town::GetByTile(tile), tile, param, 0);
787 bool NewHouseTileLoop(TileIndex tile)
789 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
791 if (GetHouseProcessingTime(tile) > 0) {
792 DecHouseProcessingTime(tile);
793 return true;
796 bool do_triggers = !(hs->ctrl_flags & HCF_NO_TRIGGERS);
798 if (do_triggers) {
799 TriggerHouse(tile, HOUSE_TRIGGER_TILE_LOOP);
800 if (hs->building_flags & BUILDING_HAS_1_TILE) TriggerHouse(tile, HOUSE_TRIGGER_TILE_LOOP_TOP);
803 if (HasBit(hs->callback_mask, CBM_HOUSE_ANIMATION_START_STOP)) {
804 /* If this house is marked as having a synchronised callback, all the
805 * tiles will have the callback called at once, rather than when the
806 * tile loop reaches them. This should only be enabled for the northern
807 * tile, or strange things will happen (here, and in TTDPatch). */
808 if (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) {
809 uint16_t random = GB(Random(), 0, 16);
811 if (hs->building_flags & BUILDING_HAS_1_TILE) AnimationControl(tile, random);
812 if (hs->building_flags & BUILDING_2_TILES_Y) AnimationControl(TileAddXY(tile, 0, 1), random);
813 if (hs->building_flags & BUILDING_2_TILES_X) AnimationControl(TileAddXY(tile, 1, 0), random);
814 if (hs->building_flags & BUILDING_HAS_4_TILES) AnimationControl(TileAddXY(tile, 1, 1), random);
815 } else {
816 AnimationControl(tile, 0);
820 /* Check callback 21, which determines if a house should be destroyed. */
821 if (HasBit(hs->callback_mask, CBM_HOUSE_DESTRUCTION)) {
822 Town *t = Town::GetByTile(tile);
823 uint16_t callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, 0, GetHouseType(tile), t, tile);
824 if (callback_res != CALLBACK_FAILED && Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DESTRUCTION, callback_res)) {
825 ClearTownHouse(t, tile);
826 return false;
830 SetHouseProcessingTime(tile, hs->processing_time);
831 if (do_triggers) MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
832 return true;
835 static void DoTriggerHouse(TileIndex tile, HouseTrigger trigger, uint8_t base_random, bool first)
837 /* We can't trigger a non-existent building... */
838 assert_tile(IsTileType(tile, MP_HOUSE), tile);
840 HouseID hid = GetHouseType(tile);
841 HouseSpec *hs = HouseSpec::Get(hid);
843 if (hs->grf_prop.spritegroup[0] == nullptr) return;
845 HouseResolverObject object(hid, tile, Town::GetByTile(tile), CBID_RANDOM_TRIGGER);
846 object.waiting_triggers = GetHouseTriggers(tile) | trigger;
847 SetHouseTriggers(tile, object.waiting_triggers); // store now for var 5F
849 const SpriteGroup *group = object.Resolve();
850 if (group == nullptr) return;
852 /* Store remaining triggers. */
853 SetHouseTriggers(tile, object.GetRemainingTriggers());
855 /* Rerandomise bits. Scopes other than SELF are invalid for houses. For bug-to-bug-compatibility with TTDP we ignore the scope. */
856 uint8_t new_random_bits = Random();
857 uint8_t random_bits = GetHouseRandomBits(tile);
858 uint32_t reseed = object.GetReseedSum();
859 random_bits &= ~reseed;
860 random_bits |= (first ? new_random_bits : base_random) & reseed;
861 SetHouseRandomBits(tile, random_bits);
863 switch (trigger) {
864 case HOUSE_TRIGGER_TILE_LOOP:
865 /* Random value already set. */
866 break;
868 case HOUSE_TRIGGER_TILE_LOOP_TOP:
869 if (!first) {
870 /* The top tile is marked dirty by the usual TileLoop */
871 MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
872 break;
874 /* Random value of first tile already set. */
875 if (hs->building_flags & BUILDING_2_TILES_Y) DoTriggerHouse(TileAddXY(tile, 0, 1), trigger, random_bits, false);
876 if (hs->building_flags & BUILDING_2_TILES_X) DoTriggerHouse(TileAddXY(tile, 1, 0), trigger, random_bits, false);
877 if (hs->building_flags & BUILDING_HAS_4_TILES) DoTriggerHouse(TileAddXY(tile, 1, 1), trigger, random_bits, false);
878 break;
882 void TriggerHouse(TileIndex t, HouseTrigger trigger)
884 DoTriggerHouse(t, trigger, 0, true);
888 * Run the watched cargo accepted callback for a single house tile.
889 * @param tile The house tile.
890 * @param origin The triggering tile.
891 * @param trigger_cargoes Cargo types that triggered the callback.
892 * @param random Random bits.
894 void DoWatchedCargoCallback(TileIndex tile, TileIndex origin, CargoTypes trigger_cargoes, uint16_t random)
896 TileIndexDiffC diff = TileIndexToTileIndexDiffC(origin, tile);
897 uint32_t cb_info = random << 16 | (uint8_t)diff.y << 8 | (uint8_t)diff.x;
898 HouseAnimationBase::ChangeAnimationFrame(CBID_HOUSE_WATCHED_CARGO_ACCEPTED, HouseSpec::Get(GetHouseType(tile)), Town::GetByTile(tile), tile, 0, cb_info, trigger_cargoes);
902 * Run watched cargo accepted callback for a house.
903 * @param tile House tile.
904 * @param trigger_cargoes Triggering cargo types.
905 * @pre IsTileType(t, MP_HOUSE)
907 void WatchedCargoCallback(TileIndex tile, CargoTypes trigger_cargoes)
909 assert_tile(IsTileType(tile, MP_HOUSE), tile);
910 HouseID id = GetHouseType(tile);
911 const HouseSpec *hs = HouseSpec::Get(id);
913 trigger_cargoes &= hs->watched_cargoes;
914 /* None of the trigger cargoes is watched? */
915 if (trigger_cargoes == 0) return;
917 /* Same random value for all tiles of a multi-tile house. */
918 uint16_t r = Random();
920 /* Do the callback, start at northern tile. */
921 TileIndex north = tile + GetHouseNorthPart(id);
922 hs = HouseSpec::Get(id);
924 DoWatchedCargoCallback(north, tile, trigger_cargoes, r);
925 if (hs->building_flags & BUILDING_2_TILES_Y) DoWatchedCargoCallback(TileAddXY(north, 0, 1), tile, trigger_cargoes, r);
926 if (hs->building_flags & BUILDING_2_TILES_X) DoWatchedCargoCallback(TileAddXY(north, 1, 0), tile, trigger_cargoes, r);
927 if (hs->building_flags & BUILDING_HAS_4_TILES) DoWatchedCargoCallback(TileAddXY(north, 1, 1), tile, trigger_cargoes, r);
930 void AnalyseHouseSpriteGroups()
932 for (uint i = 0; i < NUM_HOUSES; i++) {
933 HouseSpec *spec = HouseSpec::Get(i);
934 spec->ctrl_flags = HCF_NONE;
936 if (spec->grf_prop.spritegroup[0] == nullptr) {
937 spec->ctrl_flags |= HCF_NO_TRIGGERS;
938 continue;
941 FindRandomTriggerAnalyser analyser;
942 analyser.AnalyseGroup(spec->grf_prop.spritegroup[0]);
943 if (!analyser.found_trigger) {
944 spec->ctrl_flags |= HCF_NO_TRIGGERS;