OpenTTD Source 20260711-master-g3fb3006dff
newgrf_house.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 "debug.h"
12#include "landscape.h"
13#include "newgrf_badge.h"
14#include "newgrf_house.h"
15#include "newgrf_spritegroup.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"
23#include "newgrf_cargo.h"
24#include "station_base.h"
25
26#include "safeguards.h"
27
28static BuildingCounts<uint32_t> _building_counts{};
29static std::vector<HouseClassMapping> _class_mapping{};
30
31HouseOverrideManager _house_mngr(NEW_HOUSE_OFFSET, NUM_HOUSES, INVALID_HOUSE_ID);
32
38static const GRFFile *GetHouseSpecGrf(HouseID house_id)
39{
40 const HouseSpec *hs = HouseSpec::Get(house_id);
41 return (hs != nullptr) ? hs->grf_prop.grffile : nullptr;
42}
43
44extern const HouseSpec _original_house_specs[NEW_HOUSE_OFFSET];
45std::vector<HouseSpec> _house_specs;
46
51std::vector<HouseSpec> &HouseSpec::Specs()
52{
53 return _house_specs;
54}
55
61{
62 return static_cast<HouseID>(this - _house_specs.data());
63}
64
70HouseSpec *HouseSpec::Get(size_t house_id)
71{
72 /* Empty house if index is out of range -- this might happen if NewGRFs are changed. */
73 static HouseSpec empty = {};
74
75 assert(house_id < NUM_HOUSES);
76 if (house_id >= _house_specs.size()) return &empty;
77 return &_house_specs[house_id];
78}
79
82{
83 _house_specs.clear();
84 _house_specs.reserve(std::size(_original_house_specs));
85
86 ResetHouseClassIDs();
87
88 /* Copy default houses. */
89 _house_specs.insert(std::end(_house_specs), std::begin(_original_house_specs), std::end(_original_house_specs));
90
91 /* Reset any overrides that have been set. */
92 _house_mngr.ResetOverride();
93}
94
109 CallbackID callback, uint32_t param1, uint32_t param2,
110 bool not_yet_constructed, uint8_t initial_random_bits, CargoTypes watched_cargo_triggers, int view)
112 house_scope(*this, house_id, tile, town, not_yet_constructed, initial_random_bits, watched_cargo_triggers, view),
113 town_scope(*this, town, not_yet_constructed) // Don't access StorePSA if house is not yet constructed.
114{
115 /* Tile must be valid and a house tile, unless not yet constructed in which case it may also be INVALID_TILE. */
116 assert((IsValidTile(tile) && (not_yet_constructed || IsTileType(tile, TileType::House))) || (not_yet_constructed && tile == INVALID_TILE));
117
118 this->root_spritegroup = HouseSpec::Get(house_id)->grf_prop.GetSpriteGroup(!not_yet_constructed);
119}
120
125
127{
128 return HouseSpec::Get(this->house_scope.house_id)->grf_prop.local_id;
129}
130
131void ResetHouseClassIDs()
132{
133 _class_mapping.clear();
134
135 /* Add initial entry for HOUSE_NO_CLASS. */
136 _class_mapping.emplace_back();
137}
138
145HouseClassID AllocateHouseClassID(uint8_t grf_class_id, GrfID grfid)
146{
147 /* Start from 1 because 0 means that no class has been assigned. */
148 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; });
149
150 /* HouseClass not found, allocate a new one. */
151 if (it == std::end(_class_mapping)) it = _class_mapping.insert(it, {.grfid = grfid, .class_id = grf_class_id});
152
153 return static_cast<HouseClassID>(std::distance(std::begin(_class_mapping), it));
154}
155
161{
162 t->cache.building_counts.id_count.clear();
163 t->cache.building_counts.class_count.clear();
164 t->cache.building_counts.id_count.resize(HouseSpec::Specs().size());
165 t->cache.building_counts.class_count.resize(_class_mapping.size());
166}
167
172{
173 _building_counts.id_count.clear();
174 _building_counts.class_count.clear();
175 _building_counts.id_count.resize(HouseSpec::Specs().size());
176 _building_counts.class_count.resize(_class_mapping.size());
177
178 for (Town *t : Town::Iterate()) {
180 }
181}
182
187std::span<const uint> GetBuildingHouseIDCounts()
188{
189 return _building_counts.id_count;
190}
191
199{
200 HouseClassID class_id = HouseSpec::Get(house_id)->class_id;
201
202 t->cache.building_counts.id_count[house_id]++;
203 _building_counts.id_count[house_id]++;
204
205 if (class_id == HOUSE_NO_CLASS) return;
206
207 t->cache.building_counts.class_count[class_id]++;
208 _building_counts.class_count[class_id]++;
209}
210
218{
219 HouseClassID class_id = HouseSpec::Get(house_id)->class_id;
220
221 if (t->cache.building_counts.id_count[house_id] > 0) t->cache.building_counts.id_count[house_id]--;
222 if (_building_counts.id_count[house_id] > 0) _building_counts.id_count[house_id]--;
223
224 if (class_id == HOUSE_NO_CLASS) return;
225
226 if (t->cache.building_counts.class_count[class_id] > 0) t->cache.building_counts.class_count[class_id]--;
227 if (_building_counts.class_count[class_id] > 0) _building_counts.class_count[class_id]--;
228}
229
230/* virtual */ uint32_t HouseScopeResolver::GetRandomBits() const
231{
232 /* Note: Towns build houses over houses. So during construction checks 'tile' may be a valid but unrelated house. */
234}
235
236/* virtual */ uint32_t HouseScopeResolver::GetRandomTriggers() const
237{
238 /* Note: Towns build houses over houses. So during construction checks 'tile' may be a valid but unrelated house. */
239 return this->not_yet_constructed ? 0 : GetHouseRandomTriggers(this->tile).base();
240}
241
242static uint32_t GetNumHouses(HouseID house_id, const Town *town)
243{
244 HouseClassID class_id = HouseSpec::Get(house_id)->class_id;
245
246 uint8_t map_id_count = ClampTo<uint8_t>(_building_counts.id_count[house_id]);
247 uint8_t map_class_count = ClampTo<uint8_t>(_building_counts.class_count[class_id]);
248 uint8_t town_id_count = ClampTo<uint8_t>(town->cache.building_counts.id_count[house_id]);
249 uint8_t town_class_count = ClampTo<uint8_t>(town->cache.building_counts.class_count[class_id]);
250
251 return map_class_count << 24 | town_class_count << 16 | map_id_count << 8 | town_id_count;
252}
253
261static uint32_t GetNearbyTileInformation(uint8_t parameter, TileIndex tile, bool grf_version8)
262{
263 tile = GetNearbyTile(parameter, tile);
264 return GetNearbyTileInformation(tile, grf_version8);
265}
266
277static uint32_t GetDistanceFromNearbyHouse(uint8_t parameter, TileIndex start_tile, HouseID start_house)
278{
279 uint8_t searchtype = GB(parameter, 6, 2);
280 uint8_t searchradius = GB(parameter, 0, 6);
281 if (searchradius < 1) return 0; // do not use a too low radius
282
283 const auto *start_hs = HouseSpec::Get(start_house);
284 const auto start_north_tile = start_tile + GetHouseNorthPart(start_house); // modifies 'start_house'!
285
286 for (auto tile : SpiralTileSequence(start_tile, 2 * searchradius + 1)) {
287 if (!IsTileType(tile, TileType::House)) continue;
288 HouseID house = GetHouseType(tile);
289 const HouseSpec *hs = HouseSpec::Get(house);
290 if (!hs->grf_prop.HasGrfFile()) continue; // must be one from a grf file
291 if (start_north_tile == tile + GetHouseNorthPart(house)) continue; // Always ignore origin house
292
293 bool match;
294 switch (searchtype) {
295 case 0:
296 match = hs->grf_prop.local_id == start_hs->grf_prop.local_id && // same local id as the one requested
297 hs->grf_prop.grfid == start_hs->grf_prop.grfid; // from the same grf
298 break;
299 case 1:
300 match = hs->class_id == start_hs->class_id && // same classid as the one requested
301 hs->grf_prop.grfid == start_hs->grf_prop.grfid; // from the same grf
302 break;
303 case 2:
304 match = hs->grf_prop.grfid == start_hs->grf_prop.grfid; // from the same grf
305 break;
306 default:
307 return 0;
308 }
309 if (match) return DistanceManhattan(tile, start_tile);
310 }
311 return 0;
312}
313
314/* virtual */ uint32_t HouseScopeResolver::GetVariable(uint8_t variable, [[maybe_unused]] uint32_t parameter, bool &available) const
315{
316 if (this->tile == INVALID_TILE) {
317 /* House does not yet exist, nor is it being planned to exist. Provide some default values instead. */
318 switch (variable) {
319 case 0x40: return TOWN_HOUSE_COMPLETED | this->view << 2; /* Construction stage. */
320 case 0x41: return 0;
321 case 0x42: return 0;
322 case 0x43: return 0;
323 case 0x44: return 0;
324 case 0x45: return _generating_world ? 1 : 0;
325 case 0x46: return 0;
326 case 0x47: return 0;
327 case 0x60: return 0;
328 case 0x61: return 0;
329 case 0x62: return 0;
330 case 0x63: return 0;
331 case 0x64: return 0;
332 case 0x65: return 0;
333 case 0x66: return 0xFFFFFFFF; /* Class and ID of nearby house. */
334 case 0x67: return 0;
335
336 case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, HouseSpec::Get(this->house_id)->badges, parameter);
337 }
338
339 Debug(grf, 1, "Unhandled house variable 0x{:X}", variable);
340 available = false;
341 return UINT_MAX;
342 }
343
344 switch (variable) {
345 /* Construction stage. */
346 case 0x40: return (IsTileType(this->tile, TileType::House) ? GetHouseBuildingStage(this->tile) : 0) | TileHash2Bit(TileX(this->tile), TileY(this->tile)) << 2;
347
348 /* Building age. */
349 case 0x41: return IsTileType(this->tile, TileType::House) ? GetHouseAge(this->tile).base() : 0;
350
351 /* Town zone */
352 case 0x42: return to_underlying(GetTownRadiusGroup(this->town, this->tile));
353
354 /* Terrain type */
355 case 0x43: return GetTerrainType(this->tile);
356
357 /* Number of this type of building on the map. */
358 case 0x44: return GetNumHouses(this->house_id, this->town);
359
360 /* Whether the town is being created or just expanded. */
361 case 0x45: return _generating_world ? 1 : 0;
362
363 /* Current animation frame. */
364 case 0x46: return IsTileType(this->tile, TileType::House) ? GetAnimationFrame(this->tile) : 0;
365
366 /* Position of the house */
367 case 0x47: return TileY(this->tile) << 16 | TileX(this->tile);
368
369 /* Building counts for old houses with id = parameter. */
370 case 0x60: return parameter < NEW_HOUSE_OFFSET ? GetNumHouses(parameter, this->town) : 0;
371
372 /* Building counts for new houses with id = parameter. */
373 case 0x61: {
374 const HouseSpec *hs = HouseSpec::Get(this->house_id);
375 if (!hs->grf_prop.HasGrfFile()) return 0;
376
377 HouseID new_house = _house_mngr.GetID(parameter, hs->grf_prop.grfid);
378 return new_house == INVALID_HOUSE_ID ? 0 : GetNumHouses(new_house, this->town);
379 }
380
381 /* Land info for nearby tiles. */
382 case 0x62: return GetNearbyTileInformation(parameter, this->tile, this->ro.grffile->grf_version >= 8);
383
384 /* Current animation frame of nearby house tiles */
385 case 0x63: {
386 TileIndex testtile = GetNearbyTile(parameter, this->tile);
387 return IsTileType(testtile, TileType::House) ? GetAnimationFrame(testtile) : 0;
388 }
389
390 /* Cargo acceptance history of nearby stations */
391 case 0x64: {
392 CargoType cargo_type = GetCargoTranslation(parameter, this->ro.grffile);
393 if (!IsValidCargoType(cargo_type)) return 0;
394
395 /* Extract tile offset. */
396 int8_t x_offs = GB(this->ro.GetRegister(0x100), 0, 8);
397 int8_t y_offs = GB(this->ro.GetRegister(0x100), 8, 8);
398 TileIndex testtile = Map::WrapToMap(this->tile + TileDiffXY(x_offs, y_offs));
399
400 StationFinder stations(TileArea(testtile, 1, 1));
401
402 /* Collect acceptance stats. */
403 uint32_t res = 0;
404 for (Station *st : stations.GetStations()) {
405 res |= st->goods[cargo_type].ConvertState();
406 }
407
408 /* Cargo triggered CB 148? */
409 if (this->watched_cargo_triggers.Test(cargo_type)) SetBit(res, 4);
410
411 return res;
412 }
413
414 /* Distance test for some house types */
415 case 0x65: return GetDistanceFromNearbyHouse(parameter, this->tile, this->house_id);
416
417 /* Class and ID of nearby house tile */
418 case 0x66: {
419 TileIndex testtile = GetNearbyTile(parameter, this->tile);
420 if (!IsTileType(testtile, TileType::House)) return 0xFFFFFFFF;
421 HouseID nearby_house_id = GetHouseType(testtile);
422 HouseSpec *hs = HouseSpec::Get(nearby_house_id);
423 /* Information about the grf local classid if the house has a class */
424 uint houseclass = 0;
425 if (hs->class_id != HOUSE_NO_CLASS) {
426 houseclass = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8;
427 houseclass |= _class_mapping[hs->class_id].class_id;
428 }
429 /* old house type or grf-local houseid */
430 uint local_houseid = 0;
431 if (nearby_house_id < NEW_HOUSE_OFFSET) {
432 local_houseid = nearby_house_id;
433 } else {
434 local_houseid = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8;
435 local_houseid |= ClampTo<uint8_t>(hs->grf_prop.local_id); // Spec only allows 8 bits, so all local-ids above 254 are clamped.
436 }
437 return houseclass << 16 | local_houseid;
438 }
439
440 /* GRFID of nearby house tile */
441 case 0x67: {
442 TileIndex testtile = GetNearbyTile(parameter, this->tile);
443 if (!IsTileType(testtile, TileType::House)) return 0xFFFFFFFF;
444 HouseID house_id = GetHouseType(testtile);
445 if (house_id < NEW_HOUSE_OFFSET) return 0;
446 /* Checking the grffile information via HouseSpec doesn't work
447 * in case the newgrf was removed. */
448 return _house_mngr.GetGRFID(house_id);
449 }
450
451 case 0x7A: return GetBadgeVariableResult(*this->ro.grffile, HouseSpec::Get(this->house_id)->badges, parameter);
452 }
453
454 Debug(grf, 1, "Unhandled house variable 0x{:X}", variable);
455
456 available = false;
457 return UINT_MAX;
458}
459
475uint16_t GetHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, HouseID house_id, Town *town, TileIndex tile, std::span<int32_t> regs100,
476 bool not_yet_constructed, uint8_t initial_random_bits, CargoTypes watched_cargo_triggers, int view)
477{
478 HouseResolverObject object(house_id, tile, town, callback, param1, param2,
479 not_yet_constructed, initial_random_bits, watched_cargo_triggers, view);
480 return object.ResolveCallback(regs100);
481}
482
483static void DrawTileLayout(const TileInfo *ti, const DrawTileSpriteSpan &dts, uint8_t stage, HouseID house_id)
484{
485 const HouseSpec *hs = HouseSpec::Get(house_id);
486 PaletteID palette = GetColourPalette(hs->random_colour[TileHash2Bit(ti->x, ti->y)]);
488 uint16_t callback = GetHouseCallback(CBID_HOUSE_COLOUR, 0, 0, house_id, Town::GetByTile(ti->tile), ti->tile);
489 if (callback != CALLBACK_FAILED) {
490 /* If bit 14 is set, we should use a 2cc colour map, else use the callback value. */
491 palette = HasBit(callback, 14) ? GB(callback, 0, 8) + SPR_2CCMAP_BASE : callback;
492 }
493 }
494
495 SpriteID image = dts.ground.sprite;
496 PaletteID pal = dts.ground.pal;
497
498 if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) image += stage;
499 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += stage;
500
501 if (GB(image, 0, SPRITE_WIDTH) != 0) {
502 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
503 }
504
505 DrawNewGRFTileSeq(ti, &dts, TransparencyOption::Houses, stage, palette);
506}
507
508void DrawNewHouseTile(TileInfo *ti, HouseID house_id)
509{
510 const HouseSpec *hs = HouseSpec::Get(house_id);
511
512 if (ti->tileh != SLOPE_FLAT) {
513 bool draw_old_one = true;
515 /* Called to determine the type (if any) of foundation to draw for the house tile */
516 uint32_t callback_res = GetHouseCallback(CBID_HOUSE_DRAW_FOUNDATIONS, 0, 0, house_id, Town::GetByTile(ti->tile), ti->tile);
517 if (callback_res != CALLBACK_FAILED) draw_old_one = ConvertBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DRAW_FOUNDATIONS, callback_res);
518 }
519
520 if (draw_old_one) DrawFoundation(ti, Foundation::Leveled);
521 }
522
523 HouseResolverObject object(house_id, ti->tile, Town::GetByTile(ti->tile));
524
525 const auto *group = object.Resolve<TileLayoutSpriteGroup>();
526 if (group != nullptr) {
527 /* Limit the building stage to the number of stages supplied. */
528 uint8_t stage = GetHouseBuildingStage(ti->tile);
529 auto processor = group->ProcessRegisters(object, &stage);
530 auto dts = processor.GetLayout();
531 DrawTileLayout(ti, dts, stage, house_id);
532 }
533}
534
543void DrawNewHouseTileInGUI(int x, int y, const HouseSpec *spec, HouseID house_id, int view)
544{
545 HouseResolverObject object(house_id, INVALID_TILE, nullptr, CBID_NO_CALLBACK, 0, 0, true, view);
546 const auto *group = object.Resolve<TileLayoutSpriteGroup>();
547 if (group == nullptr) return;
548
549 uint8_t stage = TOWN_HOUSE_COMPLETED;
550 auto processor = group->ProcessRegisters(object, &stage);
551 auto dts = processor.GetLayout();
552
553 PaletteID palette = GetColourPalette(spec->random_colour[0]);
555 uint16_t callback = GetHouseCallback(CBID_HOUSE_COLOUR, 0, 0, house_id, nullptr, INVALID_TILE, {}, true, view);
556 if (callback != CALLBACK_FAILED) {
557 /* If bit 14 is set, we should use a 2cc colour map, else use the callback value. */
558 palette = HasBit(callback, 14) ? GB(callback, 0, 8) + SPR_2CCMAP_BASE : callback;
559 }
560 }
561
562 SpriteID image = dts.ground.sprite;
563 PaletteID pal = dts.ground.pal;
564
565 if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) image += stage;
566 if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += stage;
567
568 if (GB(image, 0, SPRITE_WIDTH) != 0) {
569 DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y);
570 }
571
572 DrawNewGRFTileSeqInGUI(x, y, &dts, stage, palette);
573}
574
575/* Simple wrapper for GetHouseCallback to keep the animation unified. */
576static uint16_t GetSimpleHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, const HouseSpec *spec, Town *town, TileIndex tile, CargoTypes extra_data)
577{
578 return GetHouseCallback(callback, param1, param2, spec - HouseSpec::Get(0), town, tile, {}, false, 0, extra_data);
579}
580
582struct HouseAnimationBase : public AnimationBase<HouseAnimationBase, HouseSpec, Town, CargoTypes, GetSimpleHouseCallback, TileAnimationFrameAnimationHelper<Town> > {
583 static constexpr CallbackID cb_animation_speed = CBID_HOUSE_ANIMATION_SPEED;
584 static constexpr CallbackID cb_animation_next_frame = CBID_HOUSE_ANIMATION_NEXT_FRAME;
585
586 static constexpr HouseCallbackMask cbm_animation_speed = HouseCallbackMask::AnimationSpeed;
587 static constexpr HouseCallbackMask cbm_animation_next_frame = HouseCallbackMask::AnimationNextFrame;
588};
589
590void AnimateNewHouseTile(TileIndex tile)
591{
592 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
593 if (hs == nullptr) return;
594
596}
597
598void TriggerHouseAnimation_ConstructionStageChanged(TileIndex tile, bool first_call)
599{
600 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
601
603 HouseAnimationBase::ChangeAnimationFrame(CBID_HOUSE_ANIMATION_TRIGGER_CONSTRUCTION_STAGE_CHANGED, hs, Town::GetByTile(tile), tile, Random(), first_call ? 1 : 0);
604 }
605}
606
607bool CanDeleteHouse(TileIndex tile)
608{
609 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
610
611 /* Humans are always allowed to remove buildings, as is water and disasters and
612 * anyone using the scenario editor. */
614 return true;
615 }
616
618 uint16_t callback_res = GetHouseCallback(CBID_HOUSE_DENY_DESTRUCTION, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
619 return (callback_res == CALLBACK_FAILED || !ConvertBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DENY_DESTRUCTION, callback_res));
620 } else {
621 return !IsHouseProtected(tile);
622 }
623}
624
631static void TriggerHouseAnimation_TileLoop(TileIndex tile, bool sync, uint16_t random_bits)
632{
633 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
634
635 /* Check whether the matching trigger is enabled */
638 uint32_t param = sync ? (GB(Random(), 0, 16) | random_bits << 16) : Random();
640 }
641}
642
643bool NewHouseTileLoop(TileIndex tile)
644{
645 const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile));
646
647 if (GetHouseProcessingTime(tile) > 0) {
649 return true;
650 }
651
652 TriggerHouseRandomisation(tile, HouseRandomTrigger::TileLoop);
653 if (hs->building_flags.Any(BUILDING_HAS_1_TILE)) TriggerHouseRandomisation(tile, HouseRandomTrigger::TileLoopNorth);
654
655 /* Call the unsynchronized tile loop trigger */
656 TriggerHouseAnimation_TileLoop(tile, false, 0);
657
658 /* Call the synchronized tile loop trigger, if this is the north tile */
659 if (hs->building_flags.Any(BUILDING_HAS_1_TILE)) {
660 uint16_t random = GB(Random(), 0, 16);
661 TriggerHouseAnimation_TileLoop(tile, true, random);
662 if (hs->building_flags.Any(BUILDING_2_TILES_Y)) TriggerHouseAnimation_TileLoop(TileAddXY(tile, 0, 1), true, random);
663 if (hs->building_flags.Any(BUILDING_2_TILES_X)) TriggerHouseAnimation_TileLoop(TileAddXY(tile, 1, 0), true, random);
664 if (hs->building_flags.Any(BUILDING_HAS_4_TILES)) TriggerHouseAnimation_TileLoop(TileAddXY(tile, 1, 1), true, random);
665 }
666
667 /* Check callback 21, which determines if a house should be destroyed. */
669 uint16_t callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, 0, GetHouseType(tile), Town::GetByTile(tile), tile);
670 if (callback_res != CALLBACK_FAILED && Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_DESTRUCTION, callback_res)) {
671 ClearTownHouse(Town::GetByTile(tile), tile);
672 return false;
673 }
674 }
675
678 return true;
679}
680
681static void DoTriggerHouseRandomisation(TileIndex tile, HouseRandomTrigger trigger, uint8_t base_random, bool first)
682{
683 /* We can't trigger a non-existent building... */
684 assert(IsTileType(tile, TileType::House));
685
686 HouseID hid = GetHouseType(tile);
687 HouseSpec *hs = HouseSpec::Get(hid);
688
689 if (!hs->grf_prop.HasSpriteGroups()) return;
690
691 HouseResolverObject object(hid, tile, Town::GetByTile(tile), CBID_RANDOM_TRIGGER);
692 auto waiting_random_triggers = GetHouseRandomTriggers(tile);
693 waiting_random_triggers.Set(trigger);
694 SetHouseRandomTriggers(tile, waiting_random_triggers); // store now for var 5F
695 object.SetWaitingRandomTriggers(waiting_random_triggers);
696
697 object.ResolveRerandomisation();
698
699 /* Store remaining triggers. */
700 waiting_random_triggers.Reset(object.GetUsedRandomTriggers());
701 SetHouseRandomTriggers(tile, waiting_random_triggers);
702
703 /* Rerandomise bits. Scopes other than SELF are invalid for houses. For bug-to-bug-compatibility with TTDP we ignore the scope. */
704 uint8_t new_random_bits = Random();
705 uint8_t random_bits = GetHouseRandomBits(tile);
706 uint32_t reseed = object.GetReseedSum();
707 random_bits &= ~reseed;
708 random_bits |= (first ? new_random_bits : base_random) & reseed;
709 SetHouseRandomBits(tile, random_bits);
710
711 switch (trigger) {
713 /* Random value already set. */
714 break;
715
717 if (!first) {
718 /* The top tile is marked dirty by the usual TileLoop */
720 break;
721 }
722 /* Random value of first tile already set. */
723 if (hs->building_flags.Any(BUILDING_2_TILES_Y)) DoTriggerHouseRandomisation(TileAddXY(tile, 0, 1), trigger, random_bits, false);
724 if (hs->building_flags.Any(BUILDING_2_TILES_X)) DoTriggerHouseRandomisation(TileAddXY(tile, 1, 0), trigger, random_bits, false);
725 if (hs->building_flags.Any(BUILDING_HAS_4_TILES)) DoTriggerHouseRandomisation(TileAddXY(tile, 1, 1), trigger, random_bits, false);
726 break;
727 }
728}
729
730void TriggerHouseRandomisation(TileIndex t, HouseRandomTrigger trigger)
731{
732 DoTriggerHouseRandomisation(t, trigger, 0, true);
733}
734
742static void DoTriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, TileIndex origin, CargoTypes trigger_cargoes, uint16_t random)
743{
744 TileIndexDiffC diff = TileIndexToTileIndexDiffC(origin, tile);
745 uint32_t cb_info = random << 16 | (uint8_t)diff.y << 8 | (uint8_t)diff.x;
747}
748
756{
757 assert(IsTileType(tile, TileType::House));
758 HouseID id = GetHouseType(tile);
759 const HouseSpec *hs = HouseSpec::Get(id);
760
761 trigger_cargoes = trigger_cargoes & hs->watched_cargoes;
762 /* None of the trigger cargoes is watched? */
763 if (trigger_cargoes.None()) return;
764
765 /* Same random value for all tiles of a multi-tile house. */
766 uint16_t r = Random();
767
768 /* Do the callback, start at northern tile. */
769 TileIndex north = tile + GetHouseNorthPart(id);
770 hs = HouseSpec::Get(id);
771
772 DoTriggerHouseAnimation_WatchedCargoAccepted(north, tile, trigger_cargoes, r);
773 if (hs->building_flags.Any(BUILDING_2_TILES_Y)) DoTriggerHouseAnimation_WatchedCargoAccepted(TileAddXY(north, 0, 1), tile, trigger_cargoes, r);
774 if (hs->building_flags.Any(BUILDING_2_TILES_X)) DoTriggerHouseAnimation_WatchedCargoAccepted(TileAddXY(north, 1, 0), tile, trigger_cargoes, r);
775 if (hs->building_flags.Any(BUILDING_HAS_4_TILES)) DoTriggerHouseAnimation_WatchedCargoAccepted(TileAddXY(north, 1, 1), tile, trigger_cargoes, r);
776}
777
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
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
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr Tstorage base() const noexcept
Retrieve the raw value behind this bit set.
constexpr bool None() const
Test if none of the values are set.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Generate TileIndices around a center tile or tile area, with increasing distance.
Structure contains cached list of stations nearby.
const StationList & GetStations()
Run a tile loop to find stations around a tile, on demand.
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
static constexpr Owner OWNER_NONE
The tile has no ownership.
static constexpr Owner OWNER_WATER
The tile/execution is done by "water".
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
bool _generating_world
Whether we are generating the map or not.
Definition genworld.cpp:74
Functions related to world/map generation.
void DrawSprite(SpriteID img, PaletteID pal, int x, int y, const SubSprite *sub, ZoomLevel zoom)
Draw a sprite, not in a viewport.
Definition gfx.cpp:1037
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:18
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
@ SynchronisedCallback1B
synchronized callback 1B will be performed, on multi tile houses
Definition house.h:99
@ Callback1ARandomBits
callback 1A needs random bits
Definition house.h:100
static const HouseID NUM_HOUSES
Total number of houses.
Definition house.h:29
static const HouseID NEW_HOUSE_OFFSET
Offset for new houses.
Definition house.h:28
static const uint8_t TOWN_HOUSE_COMPLETED
Simple value that indicates the house has reached the final stage of construction.
Definition house.h:25
EnumBitSet< HouseRandomTrigger, uint8_t > HouseRandomTriggers
Bitset of HouseRandomTrigger elements.
Definition house_type.h:32
HouseRandomTrigger
Randomisation triggers for houses.
Definition house_type.h:21
@ TileLoop
The tile of the house has been triggered during the tileloop.
Definition house_type.h:23
@ TileLoopNorth
The top tile of a (multitile) building has been triggered during and all the tileloop other tiles of ...
Definition house_type.h:28
uint16_t HouseClassID
Classes of houses.
Definition house_type.h:16
uint16_t HouseID
OpenTTD ID of house types.
Definition house_type.h:15
void DrawFoundation(TileInfo *ti, Foundation f)
Draw foundation f at tile ti.
Functions related to OTTD's landscape.
uint DistanceManhattan(TileIndex t0, TileIndex t1)
Gets the Manhattan distance between the two given tiles.
Definition map.cpp:169
TileIndex TileAddXY(TileIndex tile, int x, int y)
Adds a given offset to a tile.
Definition map_func.h:474
TileIndexDiff TileDiffXY(int x, int y)
Calculates an offset for the given coordinate(-offset).
Definition map_func.h:392
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:429
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:419
TileIndexDiffC TileIndexToTileIndexDiffC(TileIndex tile_a, TileIndex tile_b)
Returns the diff between two tiles.
Definition map_func.h:535
constexpr To ClampTo(From value)
Clamp the given value down to lie within the requested type.
GrfSpecFeature
Definition newgrf.h:78
@ Houses
Houses feature.
Definition newgrf.h:86
Function implementations related to NewGRF animation.
uint32_t GetBadgeVariableResult(const GRFFile &grffile, std::span< const BadgeID > badges, uint32_t parameter)
Test for a matching badge in a list of badges, returning the number of matching bits.
Functions related to NewGRF badges.
CallbackID
List of implemented NewGRF callbacks.
@ CBID_HOUSE_ANIMATION_TRIGGER_WATCHED_CARGO_ACCEPTED
Called when a cargo type specified in property 20 is accepted.
@ CBID_HOUSE_ANIMATION_TRIGGER_TILE_LOOP
Called for periodically starting or stopping the animation.
@ CBID_HOUSE_COLOUR
Called to determine the colour of a town building.
@ CBID_HOUSE_DRAW_FOUNDATIONS
Called to determine the type (if any) of foundation to draw for house tile.
@ CBID_NO_CALLBACK
Set when using the callback resolve system, but not to resolve a callback.
@ CBID_HOUSE_ANIMATION_SPEED
Called to indicate how long the current animation frame should last.
@ CBID_HOUSE_ANIMATION_NEXT_FRAME
Determine the next animation frame for a house.
@ CBID_HOUSE_DENY_DESTRUCTION
Called to determine whether a town building can be destroyed.
@ CBID_RANDOM_TRIGGER
Set when calling a randomizing trigger (almost undocumented).
@ CBID_HOUSE_ANIMATION_TRIGGER_CONSTRUCTION_STAGE_CHANGED
Called whenever the construction stage of a house changes.
@ CBID_HOUSE_DESTRUCTION
Called periodically to determine if a house should be destroyed.
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
HouseCallbackMask
Callback masks for houses.
@ Destruction
trigger destruction of building
@ DrawFoundations
decides if default foundations need to be drawn
@ AnimationNextFrame
decides next animation frame
@ DenyDestruction
conditional protection
@ AnimationSpeed
decides animation speed
@ Colour
decide the colour of the building
@ AnimationTriggerConstructionStageChanged
change animation when construction stage changes
@ AnimationTriggerTileLoop
periodically start/stop the animation
CargoType GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoType.
Cargo support for NewGRFs.
bool Convert8bitBooleanCallback(const GRFFile *grffile, uint16_t cbid, uint16_t cb_res)
Converts a callback result into a boolean.
uint32_t GetNearbyTileInformation(TileIndex tile, bool grf_version8)
Common part of station var 0x67, house var 0x62, indtile var 0x60, industry var 0x62.
bool ConvertBooleanCallback(const GRFFile *grffile, uint16_t cbid, uint16_t cb_res)
Converts a callback result into a boolean.
uint32_t GetTerrainType(TileIndex tile, TileContext context)
Function used by houses (and soon industries) to get information on type of "terrain" the tile it is ...
TileIndex GetNearbyTile(uint8_t parameter, TileIndex tile, bool signed_offsets, Axis axis)
Get the tile at the given offset.
void DecreaseBuildingCount(Town *t, HouseID house_id)
DecreaseBuildingCount() Decrease the number of a building when it is deleted.
void ResetHouses()
Reset and initialise house specs.
void IncreaseBuildingCount(Town *t, HouseID house_id)
IncreaseBuildingCount() Increase the count of a building when it has been added by a town.
static void DoTriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, TileIndex origin, CargoTypes trigger_cargoes, uint16_t random)
Run the watched cargo accepted callback for a single house tile.
static void TriggerHouseAnimation_TileLoop(TileIndex tile, bool sync, uint16_t random_bits)
Call the tile loop animation trigger for houses, if enabled.
std::span< const uint > GetBuildingHouseIDCounts()
Get read-only span of total HouseID building counts.
void DrawNewHouseTileInGUI(int x, int y, const HouseSpec *spec, HouseID house_id, int view)
Draw representation of a house tile for GUI purposes.
uint16_t GetHouseCallback(CallbackID callback, uint32_t param1, uint32_t param2, HouseID house_id, Town *town, TileIndex tile, std::span< int32_t > regs100, bool not_yet_constructed, uint8_t initial_random_bits, CargoTypes watched_cargo_triggers, int view)
Get the result of a house callback.
static uint32_t GetDistanceFromNearbyHouse(uint8_t parameter, TileIndex start_tile, HouseID start_house)
This function will activate a search around a central tile, looking for some houses that fit the requ...
HouseClassID AllocateHouseClassID(uint8_t grf_class_id, GrfID grfid)
Allocate a house class for the given NewGRF and ID.
void InitializeBuildingCounts()
Initialise global building counts and all town building counts.
void TriggerHouseAnimation_WatchedCargoAccepted(TileIndex tile, CargoTypes trigger_cargoes)
Run watched cargo accepted callback for a house.
static const GRFFile * GetHouseSpecGrf(HouseID house_id)
Retrieve the grf file associated with a house.
static uint32_t GetNearbyTileInformation(uint8_t parameter, TileIndex tile, bool grf_version8)
Get information about a nearby tile.
Functions related to NewGRF houses.
Functions related to NewGRF provided sounds.
Action 2 handling.
Functions to handle the town part of NewGRF towns.
uint32_t GrfID
The unique identifier of a NewGRF.
Definition newgrf_type.h:15
@ Editor
In the scenario editor.
Definition openttd.h:21
A number of safeguards to prevent using unsafe methods.
@ SLOPE_FLAT
a flat tile
Definition slope_type.h:54
@ Leveled
The tile is leveled up to a flat slope.
Definition slope_type.h:100
static PaletteID GetColourPalette(Colours colour)
Get recolour palette for a colour.
Definition sprite.h:221
void DrawNewGRFTileSeq(const struct TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, uint32_t stage, PaletteID default_palette)
Draw NewGRF industrytile or house sprite layout.
Definition sprite.h:159
void DrawNewGRFTileSeqInGUI(int x, int y, const DrawTileSprites *dts, uint32_t stage, PaletteID default_palette)
Draw NewGRF object in GUI.
Definition sprite.h:172
PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, PaletteID default_pal)
Applies PALETTE_MODIFIER_COLOUR to a palette entry of a ground sprite.
Definition sprite.h:207
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
Definition sprites.h:1726
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
Definition sprites.h:1716
Base classes/functions for stations.
Definition of base types and functions in a cross-platform compatible way.
Helper class for a unified approach to NewGRF animation.
static void ChangeAnimationFrame(CallbackID cb, const HouseSpec *spec, Town *obj, TileIndex tile, uint32_t random_bits, uint32_t trigger, CargoTypes extra_data={})
static void AnimateTile(const HouseSpec *spec, Town *obj, TileIndex tile, bool random_animation, CargoTypes extra_data={})
static bool IsValidHumanID(auto index)
Is this company a valid company, not controlled by a NoAI program?
T x
X coordinate.
T y
Y coordinate.
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:76
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:56
GrfID grfid
grfid that introduced this entity.
const struct GRFFile * grffile
grf file that introduced this entity
uint16_t local_id
id defined by the grf file for this entity
bool HasGrfFile() const
Test if this entity was introduced by NewGRF.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:124
Helper class for animation control.
Makes class IDs unique to each GRF file.
GrfID grfid
The GRF ID of the file this class belongs to.
uint8_t class_id
The class id within the grf file.
Resolver object to be used for houses (feature 07 spritegroups).
HouseResolverObject(HouseID house_id, TileIndex tile, Town *town, CallbackID callback=CBID_NO_CALLBACK, uint32_t param1=0, uint32_t param2=0, bool not_yet_constructed=false, uint8_t initial_random_bits=0, CargoTypes watched_cargo_triggers={}, int view=0)
Construct a resolver for a house.
uint32_t GetDebugID() const override
Get an identifier for the item being resolved.
GrfSpecFeature GetFeature() const override
Get the feature number being resolved for.
int view
View when house does yet exist.
Town * town
Town of this house.
TileIndex tile
Tile of this house.
CargoTypes watched_cargo_triggers
Cargo types that triggered the watched cargo callback.
uint32_t GetVariable(uint8_t variable, uint32_t parameter, bool &available) const override
Get a variable value.
uint16_t initial_random_bits
Random bits during construction checks.
uint32_t GetRandomBits() const override
Get a few random bits.
HouseID house_id
Type of house being queried.
uint32_t GetRandomTriggers() const override
Get the triggers.
bool not_yet_constructed
True for construction check.
SubstituteGRFFileProps grf_prop
Properties related the the grf file.
Definition house.h:122
CargoTypes watched_cargoes
Cargo types watched for acceptance.
Definition house.h:131
static HouseSpec * Get(size_t house_id)
Get the spec for a house ID.
BuildingFlags building_flags
some flags that describe the house (size, stadium etc...)
Definition house.h:117
uint8_t processing_time
Periodic refresh multiplier.
Definition house.h:129
HouseCallbackMasks callback_mask
Bitmask of house callbacks that have to be called.
Definition house.h:123
HouseExtraFlags extra_flags
some more flags
Definition house.h:126
HouseID Index() const
Gets the index of this spec.
HouseClassID class_id
defines the class this house has (not grf file based)
Definition house.h:127
Colours random_colour[4]
4 "random" colours
Definition house.h:124
static std::vector< HouseSpec > & Specs()
Get a reference to all HouseSpecs.
static TileIndex WrapToMap(TileIndex tile)
'Wraps' the given "tile" so it is within the map.
Definition map_func.h:320
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:23
PaletteID pal
The palette (use PAL_NONE) if not needed).
Definition gfx_type.h:24
static Pool::IterateWrapper< Town > Iterate(size_t from=0)
CallbackID callback
Callback being resolved.
const SpriteGroup * root_spritegroup
Root SpriteGroup to use for resolving.
ResolverObject & ro
Surrounding resolver object.
Specialization of ResolverObject with type-safe access to RandomTriggers.
const struct SpriteGroup * GetSpriteGroup(bool entity_exists) const
Get the standard sprite group.
bool HasSpriteGroups() const
Check whether the entity has sprite groups.
Station data structure.
A pair-construct of a TileIndexDiff.
Definition map_type.h:31
int16_t x
The x value of the coordinate.
Definition map_type.h:32
int16_t y
The y value of the coordinate.
Definition map_type.h:33
Tile information, used while rendering the tile.
Definition tile_cmd.h:34
Slope tileh
Slope of the tile.
Definition tile_cmd.h:35
TileIndex tile
Tile index.
Definition tile_cmd.h:36
Action 2 sprite layout for houses, industry tiles, objects and airport tiles.
BuildingCounts< uint16_t > building_counts
The number of each type of building in the town.
Definition town.h:58
Town data structure.
Definition town.h:64
TownCache cache
Container for all cacheable data.
Definition town.h:67
static bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
uint8_t GetAnimationFrame(Tile t)
Get the current animation frame.
Definition tile_map.h:250
bool IsValidTile(Tile tile)
Checks if a tile is valid.
Definition tile_map.h:161
uint TileHash2Bit(uint x, uint y)
Get the last two bits of the TileHash from a tile position.
Definition tile_map.h:342
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.
Base of the town class.
TileIndexDiff GetHouseNorthPart(HouseID &house)
Determines if a given HouseID is part of a multitile house.
void ClearTownHouse(Town *t, TileIndex tile)
Clear a town house.
HouseZone GetTownRadiusGroup(const Town *t, TileIndex tile)
Returns the bit corresponding to the town zone of the specified tile.
HouseID GetHouseType(Tile t)
Get the type of this house, which is an index into the house spec array.
Definition town_map.h:60
void SetHouseRandomBits(Tile t, uint8_t random)
Set the random bits for this house.
Definition town_map.h:284
uint8_t GetHouseRandomBits(Tile t)
Get the random bits for this house.
Definition town_map.h:297
TimerGameCalendar::Year GetHouseAge(Tile t)
Get the age of the house.
Definition town_map.h:271
void DecHouseProcessingTime(Tile t)
Decrease the amount of time remaining before the tile loop processes this tile.
Definition town_map.h:358
uint8_t GetHouseBuildingStage(Tile t)
House Construction Scheme.
Definition town_map.h:205
void SetHouseRandomTriggers(Tile t, HouseRandomTriggers triggers)
Set the activated triggers bits for this house.
Definition town_map.h:310
HouseRandomTriggers GetHouseRandomTriggers(Tile t)
Get the already activated triggers bits for this house.
Definition town_map.h:323
void SetHouseProcessingTime(Tile t, uint8_t time)
Set the amount of time remaining before the tile loop processes this tile.
Definition town_map.h:347
bool IsHouseProtected(Tile t)
Check if the house is protected from removal by towns.
Definition town_map.h:82
uint8_t GetHouseProcessingTime(Tile t)
Get the amount of time remaining before the tile loop processes this tile.
Definition town_map.h:335
@ Houses
town buildings
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:579