maedhros@6658: /* $Id$ */ maedhros@6658: maedhros@6658: /** @file newgrf_house.cpp */ maedhros@6658: maedhros@6658: #include "stdafx.h" maedhros@6658: #include "openttd.h" maedhros@6658: #include "functions.h" maedhros@6658: #include "variables.h" maedhros@6658: #include "debug.h" maedhros@6658: #include "viewport.h" maedhros@6669: #include "landscape.h" maedhros@6658: #include "date.h" maedhros@6658: #include "town.h" maedhros@6658: #include "town_map.h" maedhros@6658: #include "sound.h" maedhros@6658: #include "sprite.h" maedhros@6658: #include "strings.h" maedhros@6658: #include "table/strings.h" maedhros@6658: #include "table/sprites.h" maedhros@6658: #include "table/town_land.h" maedhros@6658: #include "newgrf.h" maedhros@6658: #include "newgrf_house.h" maedhros@6658: #include "newgrf_spritegroup.h" maedhros@6658: #include "newgrf_callbacks.h" maedhros@6658: #include "newgrf_town.h" maedhros@6658: #include "newgrf_sound.h" belugas@7125: #include "newgrf_commons.h" belugas@8345: #include "transparency.h" maedhros@6658: maedhros@6658: static BuildingCounts _building_counts; maedhros@6658: static HouseClassMapping _class_mapping[HOUSE_CLASS_MAX]; maedhros@6658: belugas@7125: HouseOverrideManager _house_mngr(NEW_HOUSE_OFFSET, HOUSE_MAX, INVALID_HOUSE_ID); maedhros@6658: maedhros@6658: void CheckHouseIDs() maedhros@6658: { belugas@7961: InitializeBuildingCounts(); belugas@7961: maedhros@6658: for (TileIndex t = 0; t < MapSize(); t++) { maedhros@6658: HouseID house_id; maedhros@6658: maedhros@6658: if (!IsTileType(t, MP_HOUSE)) continue; maedhros@6658: maedhros@6658: house_id = GetHouseType(t); maedhros@6658: if (!GetHouseSpecs(house_id)->enabled && house_id >= NEW_HOUSE_OFFSET) { maedhros@6658: /* The specs for this type of house are not available any more, so maedhros@6658: * replace it with the substitute original house type. */ belugas@7961: house_id = _house_mngr.GetSubstituteID(house_id); belugas@7961: SetHouseType(t, house_id); maedhros@6658: } belugas@7961: IncreaseBuildingCount(GetTownByTile(t), house_id); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid) maedhros@6658: { maedhros@6658: /* Start from 1 because 0 means that no class has been assigned. */ maedhros@6658: for (int i = 1; i != lengthof(_class_mapping); i++) { maedhros@6658: HouseClassMapping *map = &_class_mapping[i]; maedhros@6658: maedhros@6658: if (map->class_id == grf_class_id && map->grfid == grfid) return (HouseClassID)i; maedhros@6658: maedhros@6658: if (map->class_id == 0 && map->grfid == 0) { maedhros@6658: map->class_id = grf_class_id; maedhros@6658: map->grfid = grfid; maedhros@6658: return (HouseClassID)i; maedhros@6658: } maedhros@6658: } maedhros@6658: return HOUSE_NO_CLASS; maedhros@6658: } maedhros@6658: maedhros@6658: void InitializeBuildingCounts() maedhros@6658: { maedhros@6658: memset(&_building_counts, 0, sizeof(_building_counts)); maedhros@6658: } maedhros@6658: maedhros@6658: /** maedhros@6658: * IncreaseBuildingCount() maedhros@6658: * Increase the count of a building when it has been added by a town. maedhros@6658: * @param t The town that the building is being built in maedhros@6658: * @param house_id The id of the house being added maedhros@6658: */ maedhros@6658: void IncreaseBuildingCount(Town *t, HouseID house_id) maedhros@6658: { maedhros@6658: HouseClassID class_id = GetHouseSpecs(house_id)->class_id; maedhros@6658: belugas@7410: if (!_loaded_newgrf_features.has_newhouses) return; maedhros@6658: maedhros@6658: /* If there are 255 buildings of this type in this town, there are also maedhros@6658: * at least that many houses of the same class in the town, and maedhros@6658: * therefore on the map as well. */ maedhros@6658: if (t->building_counts.id_count[house_id] == 255) return; maedhros@6658: maedhros@6658: t->building_counts.id_count[house_id]++; maedhros@6658: if (_building_counts.id_count[house_id] < 255) _building_counts.id_count[house_id]++; maedhros@6658: maedhros@6658: /* Similarly, if there are 255 houses of this class in this town, there maedhros@6658: * must be at least that number on the map too. */ maedhros@6658: if (class_id == HOUSE_NO_CLASS || t->building_counts.class_count[class_id] == 255) return; maedhros@6658: maedhros@6658: t->building_counts.class_count[class_id]++; maedhros@6658: if (_building_counts.class_count[class_id] < 255) _building_counts.class_count[class_id]++; maedhros@6658: } maedhros@6658: maedhros@6658: /** maedhros@6658: * DecreaseBuildingCount() maedhros@6658: * Decrease the number of a building when it is deleted. maedhros@6658: * @param t The town that the building was built in maedhros@6658: * @param house_id The id of the house being removed maedhros@6658: */ maedhros@6658: void DecreaseBuildingCount(Town *t, HouseID house_id) maedhros@6658: { maedhros@6658: HouseClassID class_id = GetHouseSpecs(house_id)->class_id; maedhros@6658: belugas@7410: if (!_loaded_newgrf_features.has_newhouses) return; maedhros@6658: maedhros@6658: if (t->building_counts.id_count[house_id] > 0) t->building_counts.id_count[house_id]--; maedhros@6658: if (_building_counts.id_count[house_id] > 0) _building_counts.id_count[house_id]--; maedhros@6658: maedhros@6658: if (class_id == HOUSE_NO_CLASS) return; maedhros@6658: maedhros@6658: if (t->building_counts.class_count[class_id] > 0) t->building_counts.class_count[class_id]--; maedhros@6658: if (_building_counts.class_count[class_id] > 0) _building_counts.class_count[class_id]--; maedhros@6658: } maedhros@6658: maedhros@6658: /** maedhros@6658: * AfterLoadCountBuildings() maedhros@6658: * maedhros@6658: * After a savegame has been loaded, count the number of buildings on the map. maedhros@6658: */ maedhros@6658: void AfterLoadCountBuildings() maedhros@6658: { belugas@7410: if (!_loaded_newgrf_features.has_newhouses) return; maedhros@6658: maedhros@6658: for (TileIndex t = 0; t < MapSize(); t++) { maedhros@6658: if (!IsTileType(t, MP_HOUSE)) continue; maedhros@6658: IncreaseBuildingCount(GetTownByTile(t), GetHouseType(t)); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: maedhros@6658: static uint32 HouseGetRandomBits(const ResolverObject *object) maedhros@6658: { maedhros@6658: const TileIndex tile = object->u.house.tile; maedhros@6658: return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseRandomBits(tile); maedhros@6658: } maedhros@6658: maedhros@6658: static uint32 HouseGetTriggers(const ResolverObject *object) maedhros@6658: { maedhros@6658: const TileIndex tile = object->u.house.tile; maedhros@6658: return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseTriggers(tile); maedhros@6658: } maedhros@6658: maedhros@6658: static void HouseSetTriggers(const ResolverObject *object, int triggers) maedhros@6658: { maedhros@6658: const TileIndex tile = object->u.house.tile; maedhros@6658: if (IsTileType(tile, MP_HOUSE)) SetHouseTriggers(tile, triggers); maedhros@6658: } maedhros@6658: maedhros@6658: static uint32 GetNumHouses(HouseID house_id, const Town *town) maedhros@6658: { maedhros@6658: uint8 map_id_count, town_id_count, map_class_count, town_class_count; maedhros@6658: HouseClassID class_id = GetHouseSpecs(house_id)->class_id; maedhros@6658: maedhros@6658: map_id_count = _building_counts.id_count[house_id]; maedhros@6658: map_class_count = _building_counts.class_count[class_id]; maedhros@6658: town_id_count = town->building_counts.id_count[house_id]; maedhros@6658: town_class_count = town->building_counts.class_count[class_id]; maedhros@6658: maedhros@6658: return map_class_count << 24 | town_class_count << 16 | map_id_count << 8 | town_id_count; maedhros@6658: } maedhros@6658: maedhros@6658: static uint32 GetGRFParameter(HouseID house_id, byte parameter) maedhros@6658: { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(house_id); maedhros@6658: const GRFFile *file = hs->grffile; maedhros@6658: maedhros@6658: if (parameter >= file->param_end) return 0; maedhros@6658: return file->param[parameter]; maedhros@6658: } maedhros@6658: belugas@7323: uint32 GetNearbyTileInformation(byte parameter, TileIndex tile) belugas@7323: { belugas@7323: uint32 tile_type; belugas@7323: belugas@7323: tile = GetNearbyTile(parameter, tile); belugas@7323: tile_type = GetTerrainType(tile) << 2 | (IsTileType(tile, MP_WATER) ? 1 : 0) << 1; belugas@7323: rubidium@8019: uint z; rubidium@8019: Slope tileh = GetTileSlope(tile, &z); rubidium@8019: return GetTileType(tile) << 24 | z << 16 | tile_type << 8 | tileh; belugas@7323: } belugas@7323: maedhros@6658: /** maedhros@6658: * HouseGetVariable(): maedhros@6658: * maedhros@6658: * Used by the resolver to get values for feature 07 deterministic spritegroups. maedhros@6658: */ maedhros@6658: static uint32 HouseGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) maedhros@6658: { maedhros@6658: const Town *town = object->u.house.town; maedhros@6658: TileIndex tile = object->u.house.tile; maedhros@6658: HouseID house_id = object->u.house.house_id; maedhros@6658: maedhros@6658: if (object->scope == VSG_SCOPE_PARENT) { maedhros@6658: return TownGetVariable(variable, parameter, available, town); maedhros@6658: } maedhros@6658: maedhros@6658: switch (variable) { maedhros@6658: /* Construction stage. */ maedhros@6658: case 0x40: return (IsTileType(tile, MP_HOUSE) ? GetHouseBuildingStage(tile) : 0) | OriginalTileRandomiser(TileX(tile), TileY(tile)) << 2; maedhros@6658: maedhros@6658: /* Building age. */ maedhros@6658: case 0x41: return clamp(_cur_year - GetHouseConstructionYear(tile), 0, 0xFF); maedhros@6658: maedhros@6658: /* Town zone */ maedhros@6658: case 0x42: return GetTownRadiusGroup(town, tile); maedhros@6658: maedhros@6658: /* Terrain type */ maedhros@6658: case 0x43: return GetTerrainType(tile); maedhros@6658: maedhros@6658: /* Number of this type of building on the map. */ maedhros@6658: case 0x44: return GetNumHouses(house_id, town); maedhros@6658: maedhros@6658: /* Whether the town is being created or just expanded. */ maedhros@6658: case 0x45: return _generating_world ? 1 : 0; maedhros@6658: maedhros@6658: /* Current animation frame. */ maedhros@6658: case 0x46: return IsTileType(tile, MP_HOUSE) ? GetHouseAnimationFrame(tile) : 0; maedhros@6658: maedhros@6658: maedhros@6658: /* Building counts for old houses with id = parameter. */ maedhros@6658: case 0x60: return GetNumHouses(parameter, town); maedhros@6658: maedhros@6658: /* Building counts for new houses with id = parameter. */ maedhros@6658: case 0x61: { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(house_id); maedhros@6658: if (hs->grffile == NULL) return 0; maedhros@6658: belugas@7125: HouseID new_house = _house_mngr.GetID(parameter, hs->grffile->grfid); maedhros@6658: return new_house == INVALID_HOUSE_ID ? 0 : GetNumHouses(new_house, town); maedhros@6658: } maedhros@6658: maedhros@6658: /* Land info for nearby tiles. */ belugas@7323: case 0x62: return GetNearbyTileInformation(parameter, tile); maedhros@6658: maedhros@6658: /* Read GRF parameter */ maedhros@6658: case 0x7F: return GetGRFParameter(object->u.house.house_id, parameter); maedhros@6658: } maedhros@6658: maedhros@6658: DEBUG(grf, 1, "Unhandled house property 0x%X", variable); maedhros@6658: maedhros@6658: *available = false; maedhros@6658: return UINT_MAX; maedhros@6658: } maedhros@6658: maedhros@6658: static const SpriteGroup *HouseResolveReal(const ResolverObject *object, const SpriteGroup *group) maedhros@6658: { maedhros@6658: /* Houses do not have 'real' groups */ maedhros@6658: return NULL; maedhros@6658: } maedhros@6658: maedhros@6658: /** maedhros@6658: * NewHouseResolver(): maedhros@6658: * maedhros@6658: * Returns a resolver object to be used with feature 07 spritegroups. maedhros@6658: */ maedhros@6658: static void NewHouseResolver(ResolverObject *res, HouseID house_id, TileIndex tile, Town *town) maedhros@6658: { maedhros@6658: res->GetRandomBits = HouseGetRandomBits; maedhros@6658: res->GetTriggers = HouseGetTriggers; maedhros@6658: res->SetTriggers = HouseSetTriggers; maedhros@6658: res->GetVariable = HouseGetVariable; maedhros@6658: res->ResolveReal = HouseResolveReal; maedhros@6658: maedhros@6658: res->u.house.tile = tile; maedhros@6658: res->u.house.town = town; maedhros@6658: res->u.house.house_id = house_id; maedhros@6658: rubidium@7823: res->callback = CBID_NO_CALLBACK; maedhros@6658: res->callback_param1 = 0; maedhros@6658: res->callback_param2 = 0; maedhros@6658: res->last_value = 0; maedhros@6658: res->trigger = 0; maedhros@6658: res->reseed = 0; maedhros@6658: } maedhros@6658: rubidium@7823: uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile) maedhros@6658: { maedhros@6658: ResolverObject object; maedhros@6658: const SpriteGroup *group; maedhros@6658: maedhros@6658: NewHouseResolver(&object, house_id, tile, town); maedhros@6658: object.callback = callback; maedhros@6658: object.callback_param1 = param1; peter1138@7138: object.callback_param2 = param2; maedhros@6658: maedhros@6658: group = Resolve(GetHouseSpecs(house_id)->spritegroup, &object); maedhros@6658: if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED; maedhros@6658: maedhros@6658: return group->g.callback.result; maedhros@6658: } maedhros@6658: maedhros@6658: void DrawTileLayout(const TileInfo *ti, const SpriteGroup *group, byte stage, HouseID house_id) maedhros@6658: { maedhros@6658: const DrawTileSprites *dts = group->g.layout.dts; maedhros@6658: const DrawTileSeqStruct *dtss; maedhros@6658: maedhros@6658: SpriteID image = dts->ground_sprite; maedhros@6658: SpriteID pal = dts->ground_pal; maedhros@6658: maedhros@8115: if (IS_CUSTOM_SPRITE(image)) image += stage; maedhros@8115: maedhros@6658: if (GB(image, 0, SPRITE_WIDTH) != 0) DrawGroundSprite(image, pal); maedhros@6658: maedhros@6658: foreach_draw_tile_seq(dtss, dts->seq) { maedhros@6658: if (GB(dtss->image, 0, SPRITE_WIDTH) == 0) continue; maedhros@6658: maedhros@8115: image = dtss->image; maedhros@6658: pal = dtss->pal; maedhros@6658: maedhros@8115: if (IS_CUSTOM_SPRITE(image)) image += stage; maedhros@8115: belugas@8345: if ((HASBIT(image, SPRITE_MODIFIER_OPAQUE) || !IsTransparencySet(TO_HOUSES)) && HASBIT(image, PALETTE_MODIFIER_COLOR)) { maedhros@6658: if (pal == 0) { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(house_id); rubidium@8138: if (HASBIT(hs->callback_mask, CBM_HOUSE_COLOUR)) { rubidium@8138: uint16 callback = GetHouseCallback(CBID_HOUSE_COLOUR, 0, 0, house_id, GetTownByTile(ti->tile), ti->tile); maedhros@6658: if (callback != CALLBACK_FAILED) { maedhros@6658: /* If bit 14 is set, we should use a 2cc colour map, else use the callback value. */ maedhros@6658: pal = HASBIT(callback, 14) ? GB(callback, 0, 8) + SPR_2CCMAP_BASE : callback; maedhros@6658: } maedhros@6658: } else { maedhros@6658: pal = hs->random_colour[OriginalTileRandomiser(ti->x, ti->y)] + PALETTE_RECOLOR_START; maedhros@6658: } maedhros@6658: } maedhros@6658: } else { maedhros@6658: pal = PAL_NONE; maedhros@6658: } maedhros@6658: maedhros@6658: if ((byte)dtss->delta_z != 0x80) { maedhros@6658: AddSortableSpriteToDraw( maedhros@6658: image, pal, maedhros@6658: ti->x + dtss->delta_x, ti->y + dtss->delta_y, maedhros@6658: dtss->size_x, dtss->size_y, rubidium@7829: dtss->size_z, ti->z + dtss->delta_z, belugas@8345: IsTransparencySet(TO_HOUSES) maedhros@6658: ); maedhros@6658: } else { belugas@8345: AddChildSpriteScreen(image, pal, (byte)dtss->delta_x, (byte)dtss->delta_y, IsTransparencySet(TO_HOUSES)); maedhros@6658: } maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: void DrawNewHouseTile(TileInfo *ti, HouseID house_id) maedhros@6658: { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(house_id); maedhros@6658: const SpriteGroup *group; maedhros@6658: ResolverObject object; maedhros@6658: rubidium@7831: if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); maedhros@6658: maedhros@6658: NewHouseResolver(&object, house_id, ti->tile, GetTownByTile(ti->tile)); maedhros@6658: maedhros@6658: group = Resolve(hs->spritegroup, &object); maedhros@6658: if (group == NULL || group->type != SGT_TILELAYOUT) { maedhros@6658: /* XXX: This is for debugging purposes really, and shouldn't stay. */ maedhros@6658: DrawGroundSprite(SPR_SHADOW_CELL, PAL_NONE); maedhros@6658: } else { maedhros@6658: /* Limit the building stage to the number of stages supplied. */ maedhros@6658: byte stage = GetHouseBuildingStage(ti->tile); maedhros@6658: stage = clamp(stage - 4 + group->g.layout.num_sprites, 0, group->g.layout.num_sprites - 1); maedhros@6658: DrawTileLayout(ti, group, stage, house_id); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: void AnimateNewHouseTile(TileIndex tile) maedhros@6658: { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); maedhros@6658: byte animation_speed = hs->animation_speed; maedhros@6658: bool frame_set_by_callback = false; maedhros@6658: rubidium@8138: if (HASBIT(hs->callback_mask, CBM_HOUSE_ANIMATION_SPEED)) { peter1138@7138: uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_SPEED, 0, 0, GetHouseType(tile), GetTownByTile(tile), tile); maedhros@6658: if (callback_res != CALLBACK_FAILED) animation_speed = clamp(callback_res & 0xFF, 2, 16); maedhros@6658: } maedhros@6658: maedhros@6658: /* An animation speed of 2 means the animation frame changes 4 ticks, and maedhros@6658: * increasing this value by one doubles the wait. 2 is the minimum value maedhros@6658: * allowed for animation_speed, which corresponds to 120ms, and 16 is the maedhros@6658: * maximum, corresponding to around 33 minutes. */ maedhros@6658: if (_tick_counter % (1 << animation_speed) != 0) return; maedhros@6658: maedhros@6658: byte frame = GetHouseAnimationFrame(tile); maedhros@6658: byte num_frames = GB(hs->animation_frames, 0, 7); maedhros@6658: rubidium@8138: if (HASBIT(hs->callback_mask, CBM_HOUSE_ANIMATION_NEXT_FRAME)) { maedhros@6658: uint32 param = (hs->extra_flags & CALLBACK_1A_RANDOM_BITS) ? Random() : 0; peter1138@7138: uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_NEXT_FRAME, param, 0, GetHouseType(tile), GetTownByTile(tile), tile); maedhros@6658: maedhros@6658: if (callback_res != CALLBACK_FAILED) { maedhros@6658: frame_set_by_callback = true; maedhros@6658: maedhros@6658: switch (callback_res & 0xFF) { maedhros@6658: case 0xFF: maedhros@6658: DeleteAnimatedTile(tile); maedhros@6658: break; maedhros@6658: case 0xFE: maedhros@6658: /* Carry on as normal. */ maedhros@6658: frame_set_by_callback = false; maedhros@6658: break; maedhros@6658: default: maedhros@6658: frame = callback_res & 0xFF; maedhros@6658: break; maedhros@6658: } maedhros@6658: maedhros@6658: /* If the lower 7 bits of the upper byte of the callback maedhros@6658: * result are not empty, it is a sound effect. */ maedhros@6658: if (GB(callback_res, 8, 7) != 0) PlayHouseSound(GB(callback_res, 8, 7), tile); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: if (!frame_set_by_callback) { maedhros@6658: if (frame < num_frames) { maedhros@6658: frame++; maedhros@6658: } else if (frame == num_frames && HASBIT(hs->animation_frames, 7)) { maedhros@6658: /* This animation loops, so start again from the beginning */ maedhros@6658: frame = 0; maedhros@6658: } else { maedhros@6658: /* This animation doesn't loop, so stay here */ maedhros@6658: DeleteAnimatedTile(tile); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: SetHouseAnimationFrame(tile, frame); maedhros@6658: MarkTileDirtyByTile(tile); maedhros@6658: } maedhros@6658: maedhros@6658: void ChangeHouseAnimationFrame(TileIndex tile, uint16 callback_result) maedhros@6658: { maedhros@6658: switch (callback_result & 0xFF) { maedhros@6658: case 0xFD: /* Do nothing. */ break; maedhros@6658: case 0xFE: AddAnimatedTile(tile); break; maedhros@6658: case 0xFF: DeleteAnimatedTile(tile); break; maedhros@6658: default: maedhros@6658: SetHouseAnimationFrame(tile, callback_result & 0xFF); maedhros@6658: AddAnimatedTile(tile); maedhros@6658: break; maedhros@6658: } maedhros@6658: /* If the lower 7 bits of the upper byte of the callback maedhros@6658: * result are not empty, it is a sound effect. */ maedhros@6658: if (GB(callback_result, 8, 7) != 0) PlayHouseSound(GB(callback_result, 8, 7), tile); maedhros@6658: } maedhros@6658: maedhros@6658: bool CanDeleteHouse(TileIndex tile) maedhros@6658: { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); maedhros@6658: maedhros@6658: /* Human players are always allowed to remove buildings, as is water and maedhros@6658: * anyone using the scenario editor. */ maedhros@6658: if ((IsValidPlayer(_current_player) && IsHumanPlayer(_current_player)) maedhros@6658: || _current_player == OWNER_WATER || _current_player == OWNER_NONE) return true; maedhros@6658: maedhros@6658: if (HASBIT(hs->callback_mask, CBM_HOUSE_DENY_DESTRUCTION)) { peter1138@7138: uint16 callback_res = GetHouseCallback(CBID_HOUSE_DENY_DESTRUCTION, 0, 0, GetHouseType(tile), GetTownByTile(tile), tile); maedhros@6658: return (callback_res == CALLBACK_FAILED || callback_res == 0); maedhros@6658: } else { maedhros@6658: return !(hs->extra_flags & BUILDING_IS_PROTECTED); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: static void AnimationControl(TileIndex tile, uint16 random_bits) maedhros@6658: { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); maedhros@6658: rubidium@8138: if (HASBIT(hs->callback_mask, CBM_HOUSE_ANIMATION_START_STOP)) { maedhros@6658: uint32 param = (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) ? (GB(Random(), 0, 16) | random_bits << 16) : Random(); peter1138@7138: uint16 callback_res = GetHouseCallback(CBID_HOUSE_ANIMATION_START_STOP, param, 0, GetHouseType(tile), GetTownByTile(tile), tile); maedhros@6658: maedhros@6658: if (callback_res != CALLBACK_FAILED) ChangeHouseAnimationFrame(tile, callback_res); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: bool NewHouseTileLoop(TileIndex tile) maedhros@6658: { maedhros@6658: const HouseSpec *hs = GetHouseSpecs(GetHouseType(tile)); maedhros@6658: maedhros@6658: if (GetHouseProcessingTime(tile) > 0) { maedhros@6658: DecHouseProcessingTime(tile); maedhros@6658: return true; maedhros@6658: } maedhros@6658: rubidium@8357: TriggerHouse(tile, HOUSE_TRIGGER_TILE_LOOP); rubidium@8357: TriggerHouse(tile, HOUSE_TRIGGER_TILE_LOOP_TOP); maedhros@6658: rubidium@8138: if (HASBIT(hs->callback_mask, CBM_HOUSE_ANIMATION_START_STOP)) { maedhros@6658: /* If this house is marked as having a synchronised callback, all the maedhros@6658: * tiles will have the callback called at once, rather than when the maedhros@6658: * tile loop reaches them. This should only be enabled for the northern maedhros@6658: * tile, or strange things will happen (here, and in TTDPatch). */ maedhros@6658: if (hs->extra_flags & SYNCHRONISED_CALLBACK_1B) { maedhros@6658: uint16 random = GB(Random(), 0, 16); maedhros@6658: maedhros@6658: if (hs->building_flags & BUILDING_HAS_1_TILE) AnimationControl(tile, random); maedhros@6658: if (hs->building_flags & BUILDING_2_TILES_Y) AnimationControl(TILE_ADDXY(tile, 0, 1), random); maedhros@6658: if (hs->building_flags & BUILDING_2_TILES_X) AnimationControl(TILE_ADDXY(tile, 1, 0), random); maedhros@6658: if (hs->building_flags & BUILDING_HAS_4_TILES) AnimationControl(TILE_ADDXY(tile, 1, 1), random); maedhros@6658: } else { maedhros@6658: AnimationControl(tile, 0); maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: /* Check callback 21, which determines if a house should be destroyed. */ maedhros@6658: if (HASBIT(hs->callback_mask, CBM_HOUSE_DESTRUCTION)) { peter1138@7138: uint16 callback_res = GetHouseCallback(CBID_HOUSE_DESTRUCTION, 0, 0, GetHouseType(tile), GetTownByTile(tile), tile); maedhros@6658: if (callback_res != CALLBACK_FAILED && callback_res > 0) { maedhros@6658: ClearTownHouse(GetTownByTile(tile), tile); maedhros@6658: return false; maedhros@6658: } maedhros@6658: } maedhros@6658: maedhros@6658: SetHouseProcessingTime(tile, hs->processing_time); maedhros@6658: return true; maedhros@6658: } rubidium@8357: rubidium@8357: static void DoTriggerHouse(TileIndex tile, HouseTrigger trigger, byte base_random, bool first) rubidium@8357: { rubidium@8357: ResolverObject object; rubidium@8357: rubidium@8357: /* We can't trigger a non-existent building... */ rubidium@8357: assert(IsTileType(tile, MP_HOUSE)); rubidium@8357: rubidium@8357: HouseID hid = GetHouseType(tile); rubidium@8357: HouseSpec *hs = GetHouseSpecs(hid); rubidium@8357: rubidium@8357: NewHouseResolver(&object, hid, tile, GetTownByTile(tile)); rubidium@8357: rubidium@8357: object.callback = CBID_RANDOM_TRIGGER; rubidium@8357: object.trigger = trigger; rubidium@8357: rubidium@8357: const SpriteGroup *group = Resolve(hs->spritegroup, &object); rubidium@8357: if (group == NULL) return; rubidium@8357: rubidium@8357: byte new_random_bits = Random(); rubidium@8357: byte random_bits = GetHouseRandomBits(tile); rubidium@8357: random_bits &= ~object.reseed; rubidium@8357: random_bits |= (first ? new_random_bits : base_random) & object.reseed; rubidium@8357: SetHouseRandomBits(tile, random_bits); rubidium@8357: rubidium@8357: switch (trigger) { rubidium@8357: case HOUSE_TRIGGER_TILE_LOOP: rubidium@8357: /* Random value already set. */ rubidium@8357: break; rubidium@8357: rubidium@8357: case HOUSE_TRIGGER_TILE_LOOP_TOP: rubidium@8357: if (!first) break; rubidium@8357: /* Random value of first tile already set. */ rubidium@8357: if (hs->building_flags & BUILDING_2_TILES_Y) DoTriggerHouse(TILE_ADDXY(tile, 0, 1), trigger, false, random_bits); rubidium@8357: if (hs->building_flags & BUILDING_2_TILES_X) DoTriggerHouse(TILE_ADDXY(tile, 1, 0), trigger, false, random_bits); rubidium@8357: if (hs->building_flags & BUILDING_HAS_4_TILES) DoTriggerHouse(TILE_ADDXY(tile, 1, 1), trigger, false, random_bits); rubidium@8357: break; rubidium@8357: } rubidium@8357: } rubidium@8357: rubidium@8357: void TriggerHouse(TileIndex t, HouseTrigger trigger) rubidium@8357: { rubidium@8357: DoTriggerHouse(t, trigger, true, 0); rubidium@8357: }