diff --git a/source.list b/source.list index df35cdd26e..c9834dcf31 100644 --- a/source.list +++ b/source.list @@ -367,6 +367,7 @@ tilematrix_type.hpp timetable.h toolbar_gui.h town.h +town_gui.h town_type.h townname_func.h townname_type.h diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp index 2538d48ea7..df9142462b 100644 --- a/src/core/math_func.hpp +++ b/src/core/math_func.hpp @@ -346,33 +346,6 @@ static inline int RoundDivSU(int a, uint b) } } -/** - * Test if sqrt(a) <= sqrt(b) + sqrt(c) - * - * This function can tell you what's the relation between some of your values - * even thought you know only squares of them. Useful when comparing euclidean - * distances, it's easier to calculate them squared (#DistanceSquare) e.g. - * having a squared town radius R and squared distance D, to tell if the distance - * is further then N tiles beyond the town you may check if !SqrtCmp(D, R, N * N). - * - * @param a first value squared - * @param b second value squared - * @param c third value squared - * @return sqrt(a) <= sqrt(b) + sqrt(c) - * - * @pre 4 * b * c <= UINT32_MAX - */ -inline bool SqrtCmp(uint32 a, uint32 b, uint32 c) -{ - assert(c == 0 || b <= UINT32_MAX / 4 / c); - - /* we can square the inequality twice to get rid of square roots - * but some edge case must be checked first */ - if (a <= b + c) return true; - uint32 d = a - (b + c); - return d <= UINT16_MAX && d * d <= 4 * b * c; -} - uint32 IntSqrt(uint32 num); #endif /* MATH_FUNC_HPP */ diff --git a/src/economy_type.h b/src/economy_type.h index 30d3cbff5f..7e7a572413 100644 --- a/src/economy_type.h +++ b/src/economy_type.h @@ -137,7 +137,6 @@ enum Price { PR_INFRASTRUCTURE_WATER, PR_INFRASTRUCTURE_STATION, PR_INFRASTRUCTURE_AIRPORT, - PR_BUILD_HOUSE, PR_END, INVALID_PRICE = 0xFF diff --git a/src/gui.h b/src/gui.h index 93fba824b2..39f1ea661e 100644 --- a/src/gui.h +++ b/src/gui.h @@ -65,7 +65,6 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo void ShowBuildIndustryWindow(); void ShowFoundTownWindow(); -void ShowBuildHousePicker(); void ShowMusicWindow(); #endif /* GUI_H */ diff --git a/src/house.h b/src/house.h index 2679f303c3..17aee20c7f 100644 --- a/src/house.h +++ b/src/house.h @@ -31,9 +31,6 @@ static const HouseID NEW_HOUSE_OFFSET = 110; ///< Offset for new houses. static const HouseID NUM_HOUSES = 512; ///< Total number of houses. static const HouseID INVALID_HOUSE_ID = 0xFFFF; -static const HouseVariant HOUSE_NO_VARIANT = 0; ///< No particular house variant. -static const HouseVariant HOUSE_FIRST_VARIANT = 1; ///< First possible house variant. - /** * There can only be as many classes as there are new houses, plus one for * NO_CLASS, as the original houses don't have classes. @@ -89,13 +86,11 @@ enum HouseZones { ///< Bit Value Meaning DECLARE_ENUM_AS_BIT_SET(HouseZones) enum HouseExtraFlags { - NO_EXTRA_FLAG = 0, - BUILDING_IS_HISTORICAL = 1U << 0, ///< this house will only appear during town generation in random games, thus the historical - BUILDING_IS_PROTECTED = 1U << 1, ///< towns and AI will not remove this house, while human players will be able to - SYNCHRONISED_CALLBACK_1B = 1U << 2, ///< synchronized callback 1B will be performed, on multi tile houses - CALLBACK_1A_RANDOM_BITS = 1U << 3, ///< callback 1A needs random bits - DISALLOW_BUILDING_BY_COMPANIES = 1U << 4, ///< disallow placing manually by companies (excludes scenario editor, game scripts) - DISALLOW_BUILDING_MANUALLY = 1U << 5, ///< disallow placing manually + NO_EXTRA_FLAG = 0, + BUILDING_IS_HISTORICAL = 1U << 0, ///< this house will only appear during town generation in random games, thus the historical + BUILDING_IS_PROTECTED = 1U << 1, ///< towns and AI will not remove this house, while human players will be able to + SYNCHRONISED_CALLBACK_1B = 1U << 2, ///< synchronized callback 1B will be performed, on multi tile houses + CALLBACK_1A_RANDOM_BITS = 1U << 3, ///< callback 1A needs random bits }; DECLARE_ENUM_AS_BIT_SET(HouseExtraFlags) @@ -106,7 +101,6 @@ struct HouseSpec { Year max_year; ///< last year it can be built byte population; ///< population (Zero on other tiles in multi tile house.) byte removal_cost; ///< cost multiplier for removing it - uint16 construction_cost; ///< cost multiplier for building it StringID building_name; ///< building name uint16 remove_rating_decrease; ///< rating decrease if removed byte mail_generation; ///< mail generation multiplier (tile based, as the acceptances below) @@ -120,7 +114,6 @@ struct HouseSpec { GRFFileProps grf_prop; ///< Properties related the the grf file uint16 callback_mask; ///< Bitmask of house callbacks that have to be called byte random_colour[4]; ///< 4 "random" colours - byte num_variants; ///< total number of house variants, 0 - variants disabled byte probability; ///< Relative probability of appearing (16 is the standard value) HouseExtraFlags extra_flags; ///< some more flags HouseClassID class_id; ///< defines the class this house has (not grf file based) @@ -129,7 +122,6 @@ struct HouseSpec { byte minimum_life; ///< The minimum number of years this house will survive before the town rebuilds it uint32 watched_cargoes; ///< Cargo types watched for acceptance. - Money GetConstructionCost() const; Money GetRemovalCost() const; static inline HouseSpec *Get(size_t house_id) @@ -151,16 +143,9 @@ static inline HouseID GetTranslatedHouseID(HouseID hid) return hs->grf_prop.override == INVALID_HOUSE_ID ? hid : hs->grf_prop.override; } -StringID GetHouseName(HouseID house_id, TileIndex tile, HouseVariant variant = HOUSE_NO_VARIANT); -void DrawHouseImage(HouseID house_id, int left, int top, int right, int bottom, HouseImageType image_type, HouseVariant variant = HOUSE_NO_VARIANT); -void AddProducedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &produced, HouseVariant variant = HOUSE_NO_VARIANT); -void AddAcceptedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &acceptance, uint32 *always_accepted, HouseVariant variant = HOUSE_NO_VARIANT); -HouseZones CurrentClimateHouseZones(int altitude = -1); - -CommandCost CheckHouseDistanceFromTown(const Town *t, TileIndex tile, bool allow_outside); -CommandCost IsHouseTypeAllowed(HouseID house, HouseZones availability, bool allow_historical); -CommandCost CheckFlatLandHouse(HouseID house, TileIndex tile); - -uint16 DefaultHouseCostBaseMultiplier(uint16 callback_mask, byte population, byte mail_generation, byte cargo_acceptance1, byte cargo_acceptance2, byte cargo_acceptance3, CargoID accepts_cargo1, CargoID accepts_cargo2, CargoID accepts_cargo3); +StringID GetHouseName(HouseID house, TileIndex tile = INVALID_TILE); +void DrawHouseImage(HouseID house_id, int left, int top, int right, int bottom); +void AddProducedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &produced); +void AddAcceptedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &acceptance, uint32 *always_accepted = NULL); #endif /* HOUSE_H */ diff --git a/src/house_type.h b/src/house_type.h index 20326b4b86..9c9c41702c 100644 --- a/src/house_type.h +++ b/src/house_type.h @@ -14,15 +14,7 @@ typedef uint16 HouseID; ///< OpenTTD ID of house types. typedef uint16 HouseClassID; ///< Classes of houses. -typedef byte HouseVariant; ///< House variant. struct HouseSpec; -/** Visualization contexts of town houses. */ -enum HouseImageType { - HIT_HOUSE_TILE = 0, ///< Real house that exists on the game map (certain house tile). - HIT_GUI_HOUSE_PREVIEW = 1, ///< GUI preview of a house. - HIT_GUI_HOUSE_LIST = 2, ///< Same as #HIT_GUI_HOUSE_PREVIEW but the house is being drawn in the GUI list of houses, not in the full house preview. -}; - #endif /* HOUSE_TYPE_H */ diff --git a/src/lang/english.txt b/src/lang/english.txt index 2a0192a516..13a30831c8 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -359,6 +359,7 @@ STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION :{BLACK}Road con STR_SCENEDIT_TOOLBAR_PLANT_TREES :{BLACK}Plant trees. Shift toggles building/showing cost estimate STR_SCENEDIT_TOOLBAR_PLACE_SIGN :{BLACK}Place sign STR_SCENEDIT_TOOLBAR_PLACE_OBJECT :{BLACK}Place object. Shift toggles building/showing cost estimate +STR_SCENEDIT_TOOLBAR_PLACE_HOUSE :{BLACK}Place house ############ range for SE file menu starts STR_SCENEDIT_FILE_MENU_SAVE_SCENARIO :Save scenario @@ -404,7 +405,6 @@ STR_MAP_MENU_SIGN_LIST :Sign list ############ range for town menu starts STR_TOWN_MENU_TOWN_DIRECTORY :Town directory STR_TOWN_MENU_FOUND_TOWN :Found town -STR_TOWN_MENU_FUND_HOUSE :Fund new house ############ range ends here ############ range for subsidies menu starts @@ -1235,8 +1235,6 @@ STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS :Allow funding b STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT :Allow companies to give money to towns for funding new houses STR_CONFIG_SETTING_ALLOW_FUND_ROAD :Allow funding local road reconstruction: {STRING2} STR_CONFIG_SETTING_ALLOW_FUND_ROAD_HELPTEXT :Allow companies to give money to towns for road re-construction to sabotage road-based services in the town -STR_CONFIG_SETTING_ALLOW_PLACING_HOUSES :Allow placing houses in-game: {STRING2} -STR_CONFIG_SETTING_ALLOW_PLACING_HOUSES_HELPTEXT :Allow placing houses manually also during gameplay, not just in scenario editor STR_CONFIG_SETTING_ALLOW_GIVE_MONEY :Allow sending money to other companies: {STRING2} STR_CONFIG_SETTING_ALLOW_GIVE_MONEY_HELPTEXT :Allow transfer of money between companies in multiplayer mode STR_CONFIG_SETTING_FREIGHT_TRAINS :Weight multiplier for freight to simulate heavy trains: {STRING2} @@ -2489,33 +2487,24 @@ STR_OBJECT_BUILD_SIZE :{BLACK}Size: {G STR_OBJECT_CLASS_LTHS :Lighthouses STR_OBJECT_CLASS_TRNS :Transmitters -#Town select window -STR_SELECT_TOWN_CAPTION :{WHITE}Select town -STR_SELECT_TOWN_LIST_TOWN_ZONE :{WHITE}{TOWN}{BLACK} (zone {NUM}) -STR_SELECT_TOWN_LIST_TOWN_OUTSIDE :{WHITE}{TOWN}{BLACK} (outside) - -# House construction window -STR_HOUSE_BUILD_CAPTION :{WHITE}{STRING1} -STR_HOUSE_BUILD_CAPTION_DEFAULT_TEXT :House Selection +#House construction window (for SE only) +STR_HOUSE_BUILD_CAPTION :{WHITE}House Selection +STR_HOUSE_BUILD_CUSTOM_CAPTION :{WHITE}{RAW_STRING} STR_HOUSE_BUILD_HOUSESET_LIST_TOOLTIP :{BLACK}Select set of houses STR_HOUSE_BUILD_SELECT_HOUSE_TOOLTIP :{BLACK}Select house to build STR_HOUSE_BUILD_HOUSE_NAME :{GOLD}{STRING1} STR_HOUSE_BUILD_HISTORICAL_BUILDING :{GOLD}(historical building) STR_HOUSE_BUILD_HOUSE_POPULATION :{BLACK}Population: {GOLD}{NUM} STR_HOUSE_BUILD_HOUSE_ZONES :{BLACK}House zones: {STRING1} {STRING1} {STRING1} {STRING1} {STRING1} -STR_HOUSE_BUILD_HOUSE_ZONE_GOOD :{GOLD}{NUM} -STR_HOUSE_BUILD_HOUSE_ZONE_BAD :{GRAY}{NUM} -STR_HOUSE_BUILD_HOUSE_ZONE_GOOD_HIGHLIGHTED :{WHITE}{NUM} -STR_HOUSE_BUILD_HOUSE_ZONE_BAD_HIGHLIGHTED :{RED}{NUM} +STR_HOUSE_BUILD_HOUSE_ZONE_DISABLED :{GRAY}{NUM} +STR_HOUSE_BUILD_HOUSE_ZONE_ENABLED :{GOLD}{NUM} STR_HOUSE_BUILD_LANDSCAPE :{BLACK}Landscape: {STRING} STR_HOUSE_BUILD_LANDSCAPE_ABOVE_OR_BELOW_SNOWLINE :{GOLD}above or below snowline -STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE_GOOD :{GOLD}only above snowline -STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE_BAD :{RED}only above snowline -STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE_GOOD :{GOLD}only below snowline -STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE_BAD :{RED}only below snowline +STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE :{GOLD}only above snowline +STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE :{GOLD}only below snowline STR_HOUSE_BUILD_YEARS :{BLACK}Years: {STRING1}{GOLD} - {STRING1} -STR_HOUSE_BUILD_YEAR_GOOD :{GOLD}{NUM} -STR_HOUSE_BUILD_YEAR_BAD :{RED}{NUM} +STR_HOUSE_BUILD_YEARS_BAD_YEAR :{RED}{NUM} +STR_HOUSE_BUILD_YEARS_GOOD_YEAR :{GOLD}{NUM} STR_HOUSE_BUILD_SUPPLIED_CARGO :{BLACK}Supplies: {GOLD}{CARGO_LIST} STR_HOUSE_BUILD_ACCEPTED_CARGO :{BLACK}Accepts: {GOLD}{RAW_STRING} STR_HOUSE_BUILD_CARGO_FIRST :{STRING2} @@ -2524,6 +2513,10 @@ STR_HOUSE_BUILD_CARGO_VALUE_JUST_NAME :{1:STRING} STR_HOUSE_BUILD_CARGO_VALUE_EIGHTS :({COMMA}/8 {STRING}) STR_BASIC_HOUSE_SET_NAME :Basic houses +#Town select window (for SE only) +STR_SELECT_TOWN_CAPTION :{WHITE}Select town +STR_SELECT_TOWN_LIST_ITEM :{BLACK}{TOWN} + # Tree planting window (last two for SE only) STR_PLANT_TREE_CAPTION :{WHITE}Trees STR_PLANT_TREE_TOOLTIP :{BLACK}Select tree type to plant. If the tile already has a tree, this will add more trees of mixed types independent of the selected type @@ -4214,9 +4207,14 @@ STR_ERROR_ROAD_WORKS_IN_PROGRESS :{WHITE}Road wor STR_ERROR_TOWN_CAN_T_DELETE :{WHITE}Can't delete this town...{}A station or depot is referring to the town or a town owned tile can't be removed STR_ERROR_STATUE_NO_SUITABLE_PLACE :{WHITE}... there is no suitable place for a statue in the centre of this town STR_ERROR_BUILDING_NOT_ALLOWED_IN_THIS_TOWN_ZONE :{WHITE}... not allowed in this town zone. -STR_ERROR_ONLY_ONE_CHURCH_PER_TOWN :{WHITE}... only one church per town is allowed. -STR_ERROR_ONLY_ONE_STADIUM_PER_TOWN :{WHITE}... only one stadium per town is allowed. -STR_ERROR_TOO_FAR_FROM_TOWN :{WHITE}... too far from town. +STR_ERROR_BUILDING_NOT_ALLOWED_ABOVE_SNOW_LINE :{WHITE}... not allowed above the snow line. +STR_ERROR_BUILDING_NOT_ALLOWED_BELOW_SNOW_LINE :{WHITE}... not allowed below the snow line. +STR_ERROR_TOO_MANY_HOUSE_SETS :{WHITE}... too many house sets in the town +STR_ERROR_TOO_MANY_HOUSE_TYPES :{WHITE}... too many house types in the town +STR_ERROR_BUILDING_IS_TOO_OLD :{WHITE}... building is too old. +STR_ERROR_BUILDING_IS_TOO_MODERN :{WHITE}... building is too modern. +STR_ERROR_ONLY_ONE_BUILDING_ALLOWED_PER_TOWN :{WHITE}... only one building of this type is allowed in a town. +STR_ERROR_BUILDING_NOT_ALLOWED :{WHITE}... the building is not allowed. # Industry related errors STR_ERROR_TOO_MANY_INDUSTRIES :{WHITE}... too many industries diff --git a/src/misc.cpp b/src/misc.cpp index 1702b6cd40..d9d506993f 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -92,7 +92,6 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin InitializeTrees(); InitializeIndustries(); InitializeObjects(); - InitializeHouses(); InitializeBuildingCounts(); InitializeNPF(); diff --git a/src/newgrf.cpp b/src/newgrf.cpp index bb16df4ab4..b9c1bf89e3 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -2353,7 +2353,6 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, Byt housespec->grf_prop.local_id = hid + i; housespec->grf_prop.subst_id = subs_id; housespec->grf_prop.grffile = _cur.grffile; - housespec->construction_cost = 0xFFFF; housespec->random_colour[0] = 0x04; // those 4 random colours are the base colour housespec->random_colour[1] = 0x08; // for all new houses housespec->random_colour[2] = 0x0C; // they stand for red, blue, orange and green @@ -2520,14 +2519,6 @@ static ChangeInfoResult TownHouseChangeInfo(uint hid, int numinfo, int prop, Byt housespec->max_year = buf->ReadWord(); break; - case 0x23: // Build cost multiplier - housespec->construction_cost = buf->ReadDWord(); - break; - - case 0x24: - housespec->num_variants = buf->ReadByte(); - break; - default: ret = CIR_UNKNOWN; break; @@ -8592,13 +8583,6 @@ static void FinaliseHouseArray() * building_flags to zero here to make sure any house following * this one in the pool is properly handled as 1x1 house. */ hs->building_flags = TILE_NO_FLAG; - } else if (hs->enabled && (hs->building_flags && BUILDING_HAS_1_TILE)) { - if (hs->construction_cost == 0xFFFF) { - hs->construction_cost = DefaultHouseCostBaseMultiplier( - hs->callback_mask, hs->population, hs->mail_generation, - hs->cargo_acceptance[0], hs->cargo_acceptance[1], hs->cargo_acceptance[2], - hs->accepts_cargo[0], hs->accepts_cargo[1], hs->accepts_cargo[2]); - } } } diff --git a/src/newgrf_callbacks.h b/src/newgrf_callbacks.h index 549dcb4f31..2c30241f07 100644 --- a/src/newgrf_callbacks.h +++ b/src/newgrf_callbacks.h @@ -281,9 +281,6 @@ enum CallbackID { /** Called to spawn visual effects for vehicles. */ CBID_VEHICLE_SPAWN_VISUAL_EFFECT = 0x160, // 15 bit callback - - /** Called to set house variant through animation control. */ - CBID_HOUSE_SETUP_VARIANT = 0x161, // 15 bit callback }; /** @@ -329,7 +326,6 @@ enum HouseCallbackMask { CBM_HOUSE_DENY_DESTRUCTION = 10, ///< conditional protection CBM_HOUSE_DRAW_FOUNDATIONS = 11, ///< decides if default foundations need to be drawn CBM_HOUSE_AUTOSLOPE = 12, ///< decides allowance of autosloping - CBM_HOUSE_SETUP_VARIANT = 13, ///< set house variant through animation control }; /** diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index 8f0ec3e455..2d7c666607 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -17,7 +17,6 @@ #include "newgrf_text.h" #include "newgrf_town.h" #include "newgrf_sound.h" -#include "command_func.h" #include "company_func.h" #include "company_base.h" #include "town.h" @@ -26,9 +25,6 @@ #include "newgrf_cargo.h" #include "station_base.h" -#include -#include - #include "safeguards.h" #include "table/strings.h" @@ -38,56 +34,17 @@ static HouseClassMapping _class_mapping[HOUSE_CLASS_MAX]; HouseOverrideManager _house_mngr(NEW_HOUSE_OFFSET, NUM_HOUSES, INVALID_HOUSE_ID); -/** How a house is being placed. */ -enum TownExpansionBits { - TEB_NONE = 0, ///< House is already placed or it's a GUI house, not placing currently. - TEB_CREATING_TOWN = 1 << 0, ///< House is being placed while a town is being created. - TEB_EXPANDING_TOWN = 1 << 1, ///< House is being placed while a town is expanding. - TEB_PLACING_MANUALLY = 1 << 2, ///< House is being placed manually. -}; - -static std::vector _gui_house_cache; - -void InitializeHouses() -{ - _gui_house_cache.clear(); -} - -/** - * Get animation frame for a GUI house. - * @param house House to query. - * @param variant House variant to get the frame for. - * @param image_type House image type. - * @return Animation frame to be used when drawing the house. - */ -static byte CacheGetGUIHouseAnimFrame(HouseID house, HouseVariant variant, HouseImageType image_type) -{ - if (image_type == HIT_HOUSE_TILE) return 0; - - if (!HasBit(HouseSpec::Get(house)->callback_mask, CBM_HOUSE_SETUP_VARIANT)) return 0; - - uint32 key = house << 23 | variant << 15 | image_type << 13; - std::vector::iterator pos = std::lower_bound(_gui_house_cache.begin(), _gui_house_cache.end(), key); - if (pos != _gui_house_cache.end() && ((*pos) & ~0xFF) == key) return (byte)*pos; - - uint16 callback_result = GetHouseCallback(CBID_HOUSE_SETUP_VARIANT, 0, variant, house, NULL, INVALID_TILE, image_type, variant); - byte frame = (callback_result < 0xFD) ? callback_result : 0; - _gui_house_cache.insert(pos, key | frame); - return frame; -} - /** * Constructor of a house scope resolver. * @param ro Surrounding resolver. * @param house_id House type being queried. * @param tile %Tile containing the house. * @param town %Town containing the house. - * @param placing Whether the houe is being placed currently (house construction check or house variant setup). * @param not_yet_constructed House is still under construction. * @param initial_random_bits Random bits during construction checks. * @param watched_cargo_triggers Cargo types that triggered the watched cargo callback. */ -HouseScopeResolver::HouseScopeResolver(ResolverObject &ro, HouseID house_id, TileIndex tile, Town *town, bool placing, +HouseScopeResolver::HouseScopeResolver(ResolverObject &ro, HouseID house_id, TileIndex tile, Town *town, bool not_yet_constructed, uint8 initial_random_bits, uint32 watched_cargo_triggers) : ScopeResolver(ro) { @@ -111,14 +68,14 @@ static const GRFFile *GetHouseSpecGrf(HouseID house_id) } /** - * Construct a resolver for a house tile. + * Construct a resolver for a house. * @param house_id House to query. - * @param tile %Tile containing the house. #INVALID_TILE to query a GUI house rather then a certain house tile. - * @param town %Town containing the house. \c NULL if querying a GUI house. + * @param tile %Tile containing the house. INVALID_TILE to query a house type rather then a certian house tile. + * @param town %Town containing the house. * @param callback Callback ID. * @param param1 First parameter (var 10) of the callback. * @param param2 Second parameter (var 18) of the callback. - * @param not_yet_constructed House is still under construction (do not use for GUI houses). + * @param not_yet_constructed House is still under construction. * @param initial_random_bits Random bits during construction checks. * @param watched_cargo_triggers Cargo types that triggered the watched cargo callback. */ @@ -127,35 +84,17 @@ HouseResolverObject::HouseResolverObject(HouseID house_id, TileIndex tile, Town bool not_yet_constructed, uint8 initial_random_bits, uint32 watched_cargo_triggers) : ResolverObject(GetHouseSpecGrf(house_id), callback, param1, param2) { - assert(not_yet_constructed ? IsValidTile(tile) : IsTileType(tile, MP_HOUSE)); + assert((tile != INVALID_TILE) == (town != NULL)); + assert(tile == INVALID_TILE || (not_yet_constructed ? IsValidTile(tile) : GetHouseType(tile) == house_id && Town::GetByTile(tile) == town)); - this->house_scope = new HouseScopeResolver(*this, house_id, tile, town, not_yet_constructed || callback == CBID_HOUSE_SETUP_VARIANT, not_yet_constructed, initial_random_bits, watched_cargo_triggers); - this->town_scope = new TownScopeResolver(*this, town, not_yet_constructed); // Don't access StorePSA if house is not yet constructed. - this->root_spritegroup = HouseSpec::Get(house_id)->grf_prop.spritegroup[0]; -} + this->house_scope = (tile != INVALID_TILE) ? + (ScopeResolver*)new HouseScopeResolver(*this, house_id, tile, town, not_yet_constructed, initial_random_bits, watched_cargo_triggers) : + (ScopeResolver*)new FakeHouseScopeResolver(*this, house_id); -/** - * Construct a resolver for a GUI house. - * @param house_id House to query. - * @param variant House variant. - * @param image_type House context. - * @param callback Callback ID. - * @param param1 First parameter (var 10) of the callback. - * @param param2 Second parameter (var 18) of the callback. - */ -HouseResolverObject::HouseResolverObject(HouseID house_id, HouseVariant variant, HouseImageType image_type, - CallbackID callback, uint32 param1, uint32 param2) - : ResolverObject(GetHouseSpecGrf(house_id), callback, param1, param2) -{ - assert(image_type != HIT_HOUSE_TILE); + this->town_scope = (town != NULL) ? + (ScopeResolver*)new TownScopeResolver(*this, town, not_yet_constructed) : // Don't access StorePSA if house is not yet constructed. + (ScopeResolver*)new FakeTownScopeResolver(*this); - byte anim_frame = 0; - if (callback != CBID_HOUSE_SETUP_VARIANT) { // prevent endless recursion from CacheGetGUIHouseAnimFrame - anim_frame = CacheGetGUIHouseAnimFrame(house_id, variant, image_type); - } - - this->house_scope = new FakeHouseScopeResolver(*this, house_id, image_type, anim_frame); - this->town_scope = new FakeTownScopeResolver(*this); this->root_spritegroup = HouseSpec::Get(house_id)->grf_prop.spritegroup[0]; } @@ -391,26 +330,6 @@ static uint32 GetDistanceFromNearbyHouse(uint8 parameter, TileIndex tile, HouseI return 0; } -static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) -{ - const HouseSpec *hs = HouseSpec::Get(house); - /* Information about the grf local classid if the house has a class */ - uint houseclass = 0; - if (hs->class_id != HOUSE_NO_CLASS) { - houseclass = (is_own_house ? 1 : 2) << 8; - houseclass |= _class_mapping[hs->class_id].class_id; - } - /* old house type or grf-local houseid */ - uint local_houseid = 0; - if (house < NEW_HOUSE_OFFSET) { - local_houseid = house; - } else { - local_houseid = (is_own_house ? 1 : 2) << 8; - local_houseid |= hs->grf_prop.local_id; - } - return houseclass << 16 | local_houseid; -} - /** * @note Used by the resolver to get values for feature 07 deterministic spritegroups. */ @@ -432,12 +351,8 @@ static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) /* Number of this type of building on the map. */ case 0x44: return GetNumHouses(this->house_id, this->town); - /* Whether the town is being created or just expanded and whether the house is being placed manually. */ - case 0x45: - if (!this->placing) return TEB_NONE; - if (_current_company != OWNER_TOWN) return TEB_PLACING_MANUALLY; - if (_generating_world) return TEB_CREATING_TOWN; - return TEB_EXPANDING_TOWN; + /* Whether the town is being created or just expanded. */ + case 0x45: return _generating_world ? 1 : 0; /* Current animation frame. */ case 0x46: return IsTileType(this->tile, MP_HOUSE) ? GetAnimationFrame(this->tile) : 0; @@ -502,8 +417,22 @@ static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) case 0x66: { TileIndex testtile = GetNearbyTile(parameter, this->tile); if (!IsTileType(testtile, MP_HOUSE)) return 0xFFFFFFFF; - HouseID test_id = GetHouseType(testtile); - return GetHouseIDClassInfo(test_id, GetHouseSpecGrf(test_id) == this->ro.grffile); + HouseSpec *hs = HouseSpec::Get(GetHouseType(testtile)); + /* Information about the grf local classid if the house has a class */ + uint houseclass = 0; + if (hs->class_id != HOUSE_NO_CLASS) { + houseclass = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8; + houseclass |= _class_mapping[hs->class_id].class_id; + } + /* old house type or grf-local houseid */ + uint local_houseid = 0; + if (this->house_id < NEW_HOUSE_OFFSET) { + local_houseid = this->house_id; + } else { + local_houseid = (hs->grf_prop.grffile == this->ro.grffile ? 1 : 2) << 8; + local_houseid |= hs->grf_prop.local_id; + } + return houseclass << 16 | local_houseid; } /* GRFID of nearby house tile */ @@ -516,10 +445,6 @@ static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) * in case the newgrf was removed. */ return _house_mngr.GetGRFID(house_id); } - - /* Visualization context of the house. */ - case 0x68: - return HIT_HOUSE_TILE; } DEBUG(grf, 1, "Unhandled house variable 0x%X", variable); @@ -542,19 +467,19 @@ static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) case 0x41: return 0; /* Town zone */ - case 0x42: return FindFirstBit(HouseSpec::Get(this->house_id)->building_availability & HZ_ZONALL); // last available + case 0x42: return FIND_FIRST_BIT(HouseSpec::Get(this->house_id)->building_availability & HZ_ZONALL); // first available /* Terrain type */ 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; /* Number of this type of building on the map. */ - case 0x44: return 0x01010101; + case 0x44: return 0; - /* Whether the town is being created or just expanded and whether the house is being placed manually. */ - case 0x45: return TEB_NONE; + /* Whether the town is being created or just expanded. */ + case 0x45: return 0; /* Current animation frame. */ - case 0x46: return this->anim_frame; + case 0x46: return 0; /* Position of the house */ case 0x47: return 0xFFFFFFFF; @@ -563,13 +488,7 @@ static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) case 0x60: return 0; /* Building counts for new houses with id = parameter. */ - case 0x61: { - HouseID test_north = _house_mngr.GetID(parameter, this->ro.grffile->grfid); - GetHouseNorthPart(test_north); - HouseID cur_north = this->house_id; - GetHouseNorthPart(cur_north); - return test_north == cur_north ? 1 : 0; - } + case 0x61: return 0; /* Land info for nearby tiles. */ case 0x62: return 0; @@ -584,20 +503,10 @@ static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) case 0x65: return 0; /* Class and ID of nearby house tile */ - case 0x66: { - HouseID nearby_house = this->GetHouseNearbyPart(parameter); - if (nearby_house == INVALID_HOUSE_ID) return 0xFFFFFFFF; - return GetHouseIDClassInfo(nearby_house, true); - } + case 0x66: return 0xFFFFFFFF; /* GRFID of nearby house tile */ - case 0x67: - if (this->GetHouseNearbyPart(parameter) == INVALID_HOUSE_ID) return 0xFFFFFFFF; - return this->ro.grffile->grfid; - - /* Visualization context of the house. */ - case 0x68: - return this->image_type; + case 0x67: return 0xFFFFFFFF; } DEBUG(grf, 1, "Unhandled house variable 0x%X", variable); @@ -606,66 +515,27 @@ static uint32 GetHouseIDClassInfo(HouseID house, bool is_own_house) return UINT_MAX; } -HouseID FakeHouseScopeResolver::GetHouseNearbyPart(byte offset) const +uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile, + bool not_yet_constructed, uint8 initial_random_bits, uint32 watched_cargo_triggers) { - if (offset == 0) return this->house_id; - - int8 x = GB(offset, 0, 4); - int8 y = GB(offset, 4, 4); - if (x >= 8) x -= 16; - if (y >= 8) y -= 16; - - HouseID house = this->house_id; - TileIndexDiffC diff = GetHouseNorthPartDiffC(house); // modifies 'house'! - x -= diff.x; - y -= diff.y; - if (!IsInsideBS(x, 0, 2) || !IsInsideBS(y, 0, 2)) return INVALID_HOUSE_ID; - - BuildingFlags flags = HouseSpec::Get(house)->building_flags; - if (x > 0 && !(flags & BUILDING_2_TILES_X)) return INVALID_HOUSE_ID; - if (y > 0 && !(flags & BUILDING_2_TILES_Y)) return INVALID_HOUSE_ID; - - house += x + y; - if (flags & TILE_SIZE_2x2) house += y; - return house; -} - -/** - * Perform a house callback. - * - * Callback can be done for a certain house tile or for a GUI house. - * - * @param callback The callback to perform. - * @param param1 The first parameter. - * @param param2 The second parameter. - * @param house_id The house to do the callback for. - * @param town The town the house is located in, \c NULL to query a town-less house (GUI houses). - * @param tile The tile associated with the callback, #INVALID_TILE when querying a tile-less house (GUI houses). - * @param image_type House context. - * @param variant House variant (GUI houses only) - * @return The callback result. - */ -uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile, HouseImageType image_type, HouseVariant variant) -{ - return (image_type == HIT_HOUSE_TILE) ? - HouseResolverObject(house_id, tile, town, callback, param1, param2).ResolveCallback() : - HouseResolverObject(house_id, variant, image_type, callback, param1, param2).ResolveCallback(); + HouseResolverObject object(house_id, tile, town, callback, param1, param2, + not_yet_constructed, initial_random_bits, watched_cargo_triggers); + return object.ResolveCallback(); } /** * Get the name of a house. - * @param house_id House type. - * @param tile Tile where the house is located. #INVALID_TILE to get general name (GUI houses). - * @param variant GUI house variant, only when \c tile is #INVALID_TILE. + * @param house House type. + * @param tile Tile where the house is located. INVALID_TILE to get the general name of houses of the given type. * @return Name of the house. */ -StringID GetHouseName(HouseID house_id, TileIndex tile, HouseVariant variant) +StringID GetHouseName(HouseID house_id, TileIndex tile) { const HouseSpec *hs = HouseSpec::Get(house_id); + bool house_completed = (tile == INVALID_TILE) || IsHouseCompleted(tile); + Town *t = (tile == INVALID_TILE) ? NULL : Town::GetByTile(tile); - uint16 callback_res = (tile != INVALID_TILE) ? - GetHouseCallback(CBID_HOUSE_CUSTOM_NAME, IsHouseCompleted(tile) ? 1 : 0, 0, house_id, Town::GetByTile(tile), tile, HIT_HOUSE_TILE) : - GetHouseCallback(CBID_HOUSE_CUSTOM_NAME, 0, 0, house_id, NULL, INVALID_TILE, HIT_GUI_HOUSE_PREVIEW, variant); + uint16 callback_res = GetHouseCallback(CBID_HOUSE_CUSTOM_NAME, house_completed ? 1 : 0, 0, house_id, t, tile); if (callback_res != CALLBACK_FAILED && callback_res != 0x400) { if (callback_res > 0x400) { ErrorUnknownCallbackResult(hs->grf_prop.grffile->grfid, CBID_HOUSE_CUSTOM_NAME, callback_res); @@ -678,21 +548,12 @@ StringID GetHouseName(HouseID house_id, TileIndex tile, HouseVariant variant) return hs->building_name; } -/** - * Get colour palette to be used to draw a house. - * - * @param house_id Type of the house. - * @param image_type Context in which the house is being drawn. - * @param tile Tile where the house is located. #INVALID_TILE when drawing houses in GUI. - * @param variant GUI house variant, only when \c tile is #INVALID_TILE. - * @return The palette. - */ -static PaletteID GetHouseColour(HouseID house_id, HouseImageType image_type, TileIndex tile = INVALID_TILE, HouseVariant variant = HOUSE_NO_VARIANT) +static inline PaletteID GetHouseColour(HouseID house_id, TileIndex tile = INVALID_TILE) { const HouseSpec *hs = HouseSpec::Get(house_id); if (HasBit(hs->callback_mask, CBM_HOUSE_COLOUR)) { Town *t = (tile != INVALID_TILE) ? Town::GetByTile(tile) : NULL; - uint16 callback = GetHouseCallback(CBID_HOUSE_COLOUR, 0, 0, house_id, t, tile, image_type, variant); + uint16 callback = GetHouseCallback(CBID_HOUSE_COLOUR, 0, 0, house_id, t, tile); if (callback != CALLBACK_FAILED) { /* If bit 14 is set, we should use a 2cc colour map, else use the callback value. */ return HasBit(callback, 14) ? GB(callback, 0, 8) + SPR_2CCMAP_BASE : callback; @@ -705,7 +566,7 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou { const DrawTileSprites *dts = group->ProcessRegisters(&stage); - PaletteID palette = GetHouseColour(house_id, HIT_HOUSE_TILE, ti->tile); + PaletteID palette = GetHouseColour(house_id, ti->tile); SpriteID image = dts->ground.sprite; PaletteID pal = dts->ground.pal; @@ -720,12 +581,12 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou DrawNewGRFTileSeq(ti, dts, TO_HOUSES, stage, palette); } -static void DrawTileLayoutInGUI(int x, int y, const TileLayoutSpriteGroup *group, HouseID house_id, HouseVariant variant, HouseImageType image_type, bool ground) +static void DrawTileLayoutInGUI(int x, int y, const TileLayoutSpriteGroup *group, HouseID house_id, bool ground) { byte stage = TOWN_HOUSE_COMPLETED; const DrawTileSprites *dts = group->ProcessRegisters(&stage); - PaletteID palette = GetHouseColour(house_id, image_type, INVALID_TILE, variant); + PaletteID palette = GetHouseColour(house_id); if (ground) { PalSpriteID image = dts->ground; @@ -766,20 +627,19 @@ void DrawNewHouseTile(TileInfo *ti, HouseID house_id) } } -void DrawNewHouseTileInGUI(int x, int y, HouseID house_id, HouseVariant variant, HouseImageType image_type, bool ground) +void DrawNewHouseTileInGUI(int x, int y, HouseID house_id, bool ground) { - HouseResolverObject object(house_id, variant, image_type); + HouseResolverObject object(house_id); const SpriteGroup *group = object.Resolve(); if (group != NULL && group->type == SGT_TILELAYOUT) { - DrawTileLayoutInGUI(x, y, (const TileLayoutSpriteGroup*)group, house_id, variant, image_type, ground); + DrawTileLayoutInGUI(x, y, (const TileLayoutSpriteGroup*)group, house_id, ground); } } /* Simple wrapper for GetHouseCallback to keep the animation unified. */ uint16 GetSimpleHouseCallback(CallbackID callback, uint32 param1, uint32 param2, const HouseSpec *spec, Town *town, TileIndex tile, uint32 extra_data) { - HouseResolverObject object(spec - HouseSpec::Get(0), tile, town, callback, param1, param2, false, 0, extra_data); - return object.ResolveCallback(); + return GetHouseCallback(callback, param1, param2, spec - HouseSpec::Get(0), town, tile, false, 0, extra_data); } /** Helper class for animation control. */ @@ -799,15 +659,6 @@ void AnimateNewHouseTile(TileIndex tile) HouseAnimationBase::AnimateTile(hs, Town::GetByTile(tile), tile, HasBit(hs->extra_flags, CALLBACK_1A_RANDOM_BITS)); } -void AnimateNewHouseSetupVariant(TileIndex tile, byte variant) -{ - const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile)); - - if (HasBit(hs->callback_mask, CBM_HOUSE_SETUP_VARIANT)) { - HouseAnimationBase::ChangeAnimationFrame(CBID_HOUSE_SETUP_VARIANT, hs, Town::GetByTile(tile), tile, 0, variant); - } -} - void AnimateNewHouseConstruction(TileIndex tile) { const HouseSpec *hs = HouseSpec::Get(GetHouseType(tile)); @@ -820,29 +671,21 @@ void AnimateNewHouseConstruction(TileIndex tile) /** * Check if GRF allows a given house to be constructed (callback 17) * @param house_id house type - * @param variant house variant * @param tile tile where the house is about to be placed * @param t town in which we are building * @param random_bits feature random bits for the house * @return false if callback 17 disallows construction, true in other cases */ -CommandCost HouseAllowsConstruction(HouseID house_id, HouseVariant variant, TileIndex tile, Town *t, byte random_bits) +bool HouseAllowsConstruction(HouseID house_id, TileIndex tile, Town *t, byte random_bits) { const HouseSpec *hs = HouseSpec::Get(house_id); - if (variant > hs->num_variants) return CMD_ERROR; - - if (!HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) return CommandCost(); - - HouseResolverObject object(house_id, tile, t, CBID_HOUSE_ALLOW_CONSTRUCTION, 0, variant, true, random_bits); - uint16 callback_res = object.ResolveCallback(); - if (callback_res == CALLBACK_FAILED) return CommandCost(); - - if (hs->grf_prop.grffile->grf_version < 9) { - if (Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_ALLOW_CONSTRUCTION, callback_res)) return CommandCost(); - return CommandCost(STR_ERROR_SITE_UNSUITABLE); + if (HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) { + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house_id, t, tile, true, random_bits); + if (callback_res != CALLBACK_FAILED && !Convert8bitBooleanCallback(hs->grf_prop.grffile, CBID_HOUSE_ALLOW_CONSTRUCTION, callback_res)) { + return false; + } } - - return GetErrorMessageFromLocationCallbackResult(callback_res, hs->grf_prop.grffile, STR_ERROR_SITE_UNSUITABLE); + return true; } bool CanDeleteHouse(TileIndex tile) diff --git a/src/newgrf_house.h b/src/newgrf_house.h index 1b3fa12687..a1bf0d79ca 100644 --- a/src/newgrf_house.h +++ b/src/newgrf_house.h @@ -14,7 +14,7 @@ #include "newgrf_callbacks.h" #include "tile_cmd.h" -#include "house.h" +#include "house_type.h" #include "newgrf_spritegroup.h" #include "newgrf_town.h" @@ -23,12 +23,11 @@ struct HouseScopeResolver : public ScopeResolver { HouseID house_id; ///< Type of house being queried. TileIndex tile; ///< Tile of this house. Town *town; ///< Town of this house. - bool placing; ///< True if the house is being placed currently. bool not_yet_constructed; ///< True for construction check. uint16 initial_random_bits; ///< Random bits during construction checks. uint32 watched_cargo_triggers; ///< Cargo types that triggered the watched cargo callback. - HouseScopeResolver(ResolverObject &ro, HouseID house_id, TileIndex tile, Town *town, bool placing, + HouseScopeResolver(ResolverObject &ro, HouseID house_id, TileIndex tile, Town *town, bool not_yet_constructed, uint8 initial_random_bits, uint32 watched_cargo_triggers); /* virtual */ uint32 GetRandomBits() const; @@ -50,17 +49,12 @@ struct HouseScopeResolver : public ScopeResolver { */ struct FakeHouseScopeResolver : public ScopeResolver { HouseID house_id; ///< Type of house being queried. - HouseImageType image_type; ///< Context. - byte anim_frame; ///< House animation frame. - FakeHouseScopeResolver(ResolverObject &ro, HouseID house_id, HouseImageType image_type, byte anim_frame) - : ScopeResolver(ro), house_id(house_id), image_type(image_type), anim_frame(anim_frame) + FakeHouseScopeResolver(ResolverObject &ro, HouseID house_id) + : ScopeResolver(ro), house_id(house_id) { } /* virtual */ uint32 GetVariable(byte variable, uint32 parameter, bool *available) const; - -private: - HouseID GetHouseNearbyPart(byte offset) const; }; /** Resolver object to be used for houses (feature 07 spritegroups). */ @@ -68,13 +62,10 @@ struct HouseResolverObject : public ResolverObject { ScopeResolver *house_scope; ScopeResolver *town_scope; - HouseResolverObject(HouseID house_id, TileIndex tile, Town *town, + HouseResolverObject(HouseID house_id, TileIndex tile = INVALID_TILE, Town *town = NULL, CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0, bool not_yet_constructed = false, uint8 initial_random_bits = 0, uint32 watched_cargo_triggers = 0); - HouseResolverObject(HouseID house_id, HouseVariant variant, HouseImageType image_type = HIT_GUI_HOUSE_PREVIEW, - CallbackID callback = CBID_NO_CALLBACK, uint32 param1 = 0, uint32 param2 = 0); - /* virtual */ ~HouseResolverObject(); /* virtual */ ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) @@ -107,21 +98,20 @@ struct HouseClassMapping { HouseClassID AllocateHouseClassID(byte grf_class_id, uint32 grfid); -void InitializeHouses(); - void InitializeBuildingCounts(); void IncreaseBuildingCount(Town *t, HouseID house_id); void DecreaseBuildingCount(Town *t, HouseID house_id); void DrawNewHouseTile(TileInfo *ti, HouseID house_id); -void DrawNewHouseTileInGUI(int x, int y, HouseID house_id, HouseVariant variant, HouseImageType image_type, bool ground); +void DrawNewHouseTileInGUI(int x, int y, HouseID house_id, bool ground); void AnimateNewHouseTile(TileIndex tile); void AnimateNewHouseConstruction(TileIndex tile); -void AnimateNewHouseSetupVariant(TileIndex tile, HouseVariant variant); -uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile, HouseImageType image_type = HIT_HOUSE_TILE, HouseVariant variant = HOUSE_NO_VARIANT); +uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town = NULL, TileIndex tile = INVALID_TILE, + bool not_yet_constructed = false, uint8 initial_random_bits = 0, uint32 watched_cargo_triggers = 0); void WatchedCargoCallback(TileIndex tile, uint32 trigger_cargoes); -CommandCost HouseAllowsConstruction(HouseID house_id, HouseVariant variant, TileIndex tile, Town *t, byte random_bits); + +bool HouseAllowsConstruction(HouseID house_id, TileIndex tile, Town *t, byte random_bits); bool CanDeleteHouse(TileIndex tile); bool NewHouseTileLoop(TileIndex tile); diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq index 566337c351..ff23f77c78 100644 --- a/src/script/api/game/game_window.hpp.sq +++ b/src/script/api/game/game_window.hpp.sq @@ -1141,6 +1141,7 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ETT_PLACE_ROCKS, "WID_ETT_PLACE_ROCKS"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ETT_PLACE_DESERT, "WID_ETT_PLACE_DESERT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ETT_PLACE_OBJECT, "WID_ETT_PLACE_OBJECT"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ETT_PLACE_HOUSE, "WID_ETT_PLACE_HOUSE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ETT_BUTTONS_END, "WID_ETT_BUTTONS_END"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ETT_INCREASE_SIZE, "WID_ETT_INCREASE_SIZE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ETT_DECREASE_SIZE, "WID_ETT_DECREASE_SIZE"); @@ -1251,9 +1252,6 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TF_LAYOUT_GRID2, "WID_TF_LAYOUT_GRID2"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TF_LAYOUT_GRID3, "WID_TF_LAYOUT_GRID3"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TF_LAYOUT_RANDOM, "WID_TF_LAYOUT_RANDOM"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ST_CAPTION, "WID_ST_CAPTION"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ST_PANEL, "WID_ST_PANEL"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ST_SCROLLBAR, "WID_ST_SCROLLBAR"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_CAPTION, "WID_HP_CAPTION"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_MAIN_PANEL_SEL, "WID_HP_MAIN_PANEL_SEL"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_SETS_SEL, "WID_HP_HOUSE_SETS_SEL"); @@ -1262,10 +1260,6 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_SELECT_SCROLL, "WID_HP_HOUSE_SELECT_SCROLL"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_SELECT, "WID_HP_HOUSE_SELECT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_PREVIEW, "WID_HP_HOUSE_PREVIEW"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_PREV_VARIANT_SEL, "WID_HP_PREV_VARIANT_SEL"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_PREV_VARIANT, "WID_HP_PREV_VARIANT"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_NEXT_VARIANT_SEL, "WID_HP_NEXT_VARIANT_SEL"); - SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_NEXT_VARIANT, "WID_HP_NEXT_VARIANT"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_NAME, "WID_HP_HOUSE_NAME"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HISTORICAL_BUILDING, "WID_HP_HISTORICAL_BUILDING"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_POPULATION, "WID_HP_HOUSE_POPULATION"); @@ -1275,6 +1269,9 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_YEARS, "WID_HP_HOUSE_YEARS"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_ACCEPTANCE, "WID_HP_HOUSE_ACCEPTANCE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_HP_HOUSE_SUPPLY, "WID_HP_HOUSE_SUPPLY"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ST_CAPTION, "WID_ST_CAPTION"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ST_PANEL, "WID_ST_PANEL"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_ST_SCROLLBAR, "WID_ST_SCROLLBAR"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_BEGIN, "WID_TT_BEGIN"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_SIGNS, "WID_TT_SIGNS"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TT_TREES, "WID_TT_TREES"); diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp index d79b64dc06..9b41adf605 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -2342,6 +2342,7 @@ public: WID_ETT_PLACE_ROCKS = ::WID_ETT_PLACE_ROCKS, ///< Place rocks button. WID_ETT_PLACE_DESERT = ::WID_ETT_PLACE_DESERT, ///< Place desert button (in tropical climate). WID_ETT_PLACE_OBJECT = ::WID_ETT_PLACE_OBJECT, ///< Place transmitter button. + WID_ETT_PLACE_HOUSE = ::WID_ETT_PLACE_HOUSE, ///< Place house button. WID_ETT_BUTTONS_END = ::WID_ETT_BUTTONS_END, ///< End of pushable buttons. WID_ETT_INCREASE_SIZE = ::WID_ETT_INCREASE_SIZE, ///< Upwards arrow button to increase terraforming size. WID_ETT_DECREASE_SIZE = ::WID_ETT_DECREASE_SIZE, ///< Downwards arrow button to decrease terraforming size. @@ -2486,13 +2487,6 @@ public: WID_TF_LAYOUT_RANDOM = ::WID_TF_LAYOUT_RANDOM, ///< Selection for a randomly chosen town layout. }; - /** Widgets of the #SelectTownWindow class. */ - enum SelectTownWidgets { - WID_ST_CAPTION = ::WID_ST_CAPTION, ///< Caption of the window. - WID_ST_PANEL = ::WID_ST_PANEL, ///< Main panel. - WID_ST_SCROLLBAR = ::WID_ST_SCROLLBAR, ///< Scrollbar of the panel. - }; - /** Widgets of the #HousePickerWindow class. */ enum HousePickerWidgets { WID_HP_CAPTION = ::WID_HP_CAPTION, @@ -2503,10 +2497,6 @@ public: WID_HP_HOUSE_SELECT_SCROLL = ::WID_HP_HOUSE_SELECT_SCROLL, ///< Scrollbar associated with the house matrix. WID_HP_HOUSE_SELECT = ::WID_HP_HOUSE_SELECT, ///< Panels with house images in the house matrix. WID_HP_HOUSE_PREVIEW = ::WID_HP_HOUSE_PREVIEW, ///< House preview panel. - WID_HP_PREV_VARIANT_SEL = ::WID_HP_PREV_VARIANT_SEL, ///< Selection widget to show/hide the prev variant buttons. - WID_HP_PREV_VARIANT = ::WID_HP_PREV_VARIANT, ///< Prev variant button. - WID_HP_NEXT_VARIANT_SEL = ::WID_HP_NEXT_VARIANT_SEL, ///< Selection widget to show/hide the next variant buttons. - WID_HP_NEXT_VARIANT = ::WID_HP_NEXT_VARIANT, ///< Next variant button. WID_HP_HOUSE_NAME = ::WID_HP_HOUSE_NAME, ///< House name display. WID_HP_HISTORICAL_BUILDING = ::WID_HP_HISTORICAL_BUILDING, ///< "Historical building" label. WID_HP_HOUSE_POPULATION = ::WID_HP_HOUSE_POPULATION, ///< House population display. @@ -2518,6 +2508,13 @@ public: WID_HP_HOUSE_SUPPLY = ::WID_HP_HOUSE_SUPPLY, ///< Cargo supplied. }; + /** Widgets of the #SelectTownWindow class. */ + enum SelectTownWidgets { + WID_ST_CAPTION = ::WID_ST_CAPTION, ///< Caption of the window. + WID_ST_PANEL = ::WID_ST_PANEL, ///< Main panel. + WID_ST_SCROLLBAR = ::WID_ST_SCROLLBAR, ///< Scrollbar of the panel. + }; + /* automatically generated from ../../widgets/transparency_widget.h */ /** Widgets of the #TransparenciesWindow class. */ enum TransparencyToolbarWidgets { diff --git a/src/script/api/template/template_window.hpp.sq b/src/script/api/template/template_window.hpp.sq index 76371b605b..445e90fe4e 100644 --- a/src/script/api/template/template_window.hpp.sq +++ b/src/script/api/template/template_window.hpp.sq @@ -233,10 +233,10 @@ namespace SQConvert { template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::TownViewWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::TownFoundingWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::TownFoundingWidgets)tmp; } template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::TownFoundingWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } - template <> inline ScriptWindow::SelectTownWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::SelectTownWidgets)tmp; } - template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::SelectTownWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::HousePickerWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::HousePickerWidgets)tmp; } template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::HousePickerWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline ScriptWindow::SelectTownWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::SelectTownWidgets)tmp; } + template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::SelectTownWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::TransparencyToolbarWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::TransparencyToolbarWidgets)tmp; } template <> inline int Return(HSQUIRRELVM vm, ScriptWindow::TransparencyToolbarWidgets res) { sq_pushinteger(vm, (int32)res); return 1; } template <> inline ScriptWindow::BuildTreesWidgets GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (ScriptWindow::BuildTreesWidgets)tmp; } diff --git a/src/settings.cpp b/src/settings.cpp index 8f4baa5fd1..e6754bb905 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1057,14 +1057,6 @@ static bool TownFoundingChanged(int32 p1) return true; } -static bool AllowPlacingHousesChanged(int32 p1) -{ - if (_game_mode != GM_EDITOR && !_settings_game.economy.allow_placing_houses) { - DeleteWindowById(WC_BUILD_HOUSE, 0); - } - return true; -} - static bool InvalidateVehTimetableWindow(int32 p1) { InvalidateWindowClassesData(WC_VEHICLE_TIMETABLE, VIWD_MODIFY_ORDERS); diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 9dc562fb70..0652d1bd81 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1695,7 +1695,6 @@ static SettingsContainer &GetSettingsTree() towns->Add(new SettingEntry("economy.allow_town_roads")); towns->Add(new SettingEntry("economy.allow_town_level_crossings")); towns->Add(new SettingEntry("economy.found_town")); - towns->Add(new SettingEntry("economy.allow_placing_houses")); } SettingsPage *industries = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES)); diff --git a/src/settings_type.h b/src/settings_type.h index 729129765c..41366a7719 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -487,7 +487,6 @@ struct EconomySettings { bool station_noise_level; ///< build new airports when the town noise level is still within accepted limits uint16 town_noise_population[3]; ///< population to base decision on noise evaluation (@see town_council_tolerance) bool allow_town_level_crossings; ///< towns are allowed to build level crossings - bool allow_placing_houses; ///< whether to allow placing houses manually also during gameplay, not only in scenario editor bool infrastructure_maintenance; ///< enable monthly maintenance fee for owner infrastructure }; diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index a332037c2f..b7d0c92024 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -159,7 +159,6 @@ static const NIFeature _nif_station = { #define NICH(cb_id, bit) NIC(cb_id, HouseSpec, callback_mask, bit) static const NICallback _nic_house[] = { NICH(CBID_HOUSE_ALLOW_CONSTRUCTION, CBM_HOUSE_ALLOW_CONSTRUCTION), - NICH(CBID_HOUSE_SETUP_VARIANT, CBM_HOUSE_SETUP_VARIANT), NICH(CBID_HOUSE_ANIMATION_NEXT_FRAME, CBM_HOUSE_ANIMATION_NEXT_FRAME), NICH(CBID_HOUSE_ANIMATION_START_STOP, CBM_HOUSE_ANIMATION_START_STOP), NICH(CBID_HOUSE_CONSTRUCTION_STATE_CHANGE, CBM_HOUSE_CONSTRUCTION_STATE_CHANGE), diff --git a/src/table/pricebase.h b/src/table/pricebase.h index 45d692e8f8..9dc2ee2ba7 100644 --- a/src/table/pricebase.h +++ b/src/table/pricebase.h @@ -81,6 +81,5 @@ extern const PriceBaseSpec _price_base_specs[] = { { 8, PCAT_RUNNING, GSF_END, PR_BUILD_CANAL }, ///< PR_INFRASTRUCTURE_WATER { 100, PCAT_RUNNING, GSF_END, PR_STATION_VALUE }, ///< PR_INFRASTRUCTURE_STATION { 5000, PCAT_RUNNING, GSF_END, PR_BUILD_STATION_AIRPORT}, ///< PR_INFRASTRUCTURE_AIRPORT - {1000000, PCAT_CONSTRUCTION, GSF_END, PR_BUILD_INDUSTRY }, ///< PR_BUILD_HOUSE }; assert_compile(lengthof(_price_base_specs) == PR_END); diff --git a/src/table/settings.ini b/src/table/settings.ini index 42501b27e0..f314f21e92 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -25,7 +25,6 @@ static bool TrainSlopeSteepnessChanged(int32 p1); static bool RoadVehSlopeSteepnessChanged(int32 p1); static bool DragSignalsDensityChanged(int32); static bool TownFoundingChanged(int32 p1); -static bool AllowPlacingHousesChanged(int32 p1); static bool DifficultyNoiseChange(int32 i); static bool MaxNoAIsChange(int32 i); static bool CheckRoadSide(int p1); @@ -618,17 +617,6 @@ def = true str = STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS strhelp = STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS_HELPTEXT -; TODO: add this setting to savegames -[SDT_BOOL] -base = GameSettings -var = economy.allow_placing_houses -flags = SLF_NOT_IN_SAVE -def = false -str = STR_CONFIG_SETTING_ALLOW_PLACING_HOUSES -strhelp = STR_CONFIG_SETTING_ALLOW_PLACING_HOUSES_HELPTEXT -proc = AllowPlacingHousesChanged -cat = SC_BASIC - ; link graph [SDT_VAR] diff --git a/src/table/town_land.h b/src/table/town_land.h index 4e524ec0a0..e4098334ac 100644 --- a/src/table/town_land.h +++ b/src/table/town_land.h @@ -1812,8 +1812,8 @@ assert_compile(lengthof(_town_draw_tile_data) == (NEW_HOUSE_OFFSET) * 4 * 4); * @see HouseSpec */ #define MS(mnd, mxd, p, rc, bn, rr, mg, ca1, ca2, ca3, bf, ba, cg1, cg2, cg3) \ - {mnd, mxd, p, rc, DefaultHouseCostBaseMultiplier(0, p, mg, ca1, ca2, ca3, cg1, cg2, cg3), bn, rr, mg, {ca1, ca2, ca3}, {cg1, cg2, cg3}, bf, ba, true, \ - GRFFileProps(INVALID_HOUSE_ID), 0, {0, 0, 0, 0}, 0, 16, NO_EXTRA_FLAG, HOUSE_NO_CLASS, {0, 2, 0, 0}, 0, 0, 0} + {mnd, mxd, p, rc, bn, rr, mg, {ca1, ca2, ca3}, {cg1, cg2, cg3}, bf, ba, true, \ + GRFFileProps(INVALID_HOUSE_ID), 0, {0, 0, 0, 0}, 16, NO_EXTRA_FLAG, HOUSE_NO_CLASS, {0, 2, 0, 0}, 0, 0, 0} /** House specifications from original data */ static const HouseSpec _original_house_specs[] = { /** diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp index 7a319d66fc..99b6ad2ec6 100644 --- a/src/terraform_gui.cpp +++ b/src/terraform_gui.cpp @@ -32,6 +32,7 @@ #include "hotkeys.h" #include "engine_base.h" #include "terraform_gui.h" +#include "town_gui.h" #include "zoom_func.h" #include "widgets/terraform_widget.h" @@ -465,6 +466,9 @@ static const NWidgetPart _nested_scen_edit_land_gen_widgets[] = { NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_ETT_PLACE_OBJECT), SetMinimalSize(23, 22), SetFill(0, 1), SetDataTip(SPR_IMG_TRANSMITTER, STR_SCENEDIT_TOOLBAR_PLACE_OBJECT), NWidget(NWID_SPACER), SetFill(1, 0), + NWidget(WWT_IMGBTN, COLOUR_GREY, WID_ETT_PLACE_HOUSE), SetMinimalSize(23, 22), + SetFill(0, 1), SetDataTip(SPR_IMG_TOWN, STR_SCENEDIT_TOOLBAR_PLACE_HOUSE), + NWidget(NWID_SPACER), SetFill(1, 0), EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(NWID_SPACER), SetFill(1, 0), @@ -609,6 +613,10 @@ struct ScenarioEditorLandscapeGenerationWindow : Window { ShowBuildObjectPicker(); break; + case WID_ETT_PLACE_HOUSE: // Place house button + ShowBuildHousePicker(); + break; + case WID_ETT_INCREASE_SIZE: case WID_ETT_DECREASE_SIZE: { // Increase/Decrease terraform size int size = (widget == WID_ETT_INCREASE_SIZE) ? 1 : -1; @@ -730,6 +738,7 @@ static Hotkey terraform_editor_hotkeys[] = { Hotkey('R', "rocky", WID_ETT_PLACE_ROCKS), Hotkey('T', "desert", WID_ETT_PLACE_DESERT), Hotkey('O', "object", WID_ETT_PLACE_OBJECT), + Hotkey('H', "house", WID_ETT_PLACE_HOUSE), HOTKEY_LIST_END }; diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp index ef785f4d05..45d751dc56 100644 --- a/src/toolbar_gui.cpp +++ b/src/toolbar_gui.cpp @@ -497,28 +497,9 @@ static CallBackFunction MenuClickMap(int index) /* --- Town button menu --- */ -enum TownMenuEntries { - TME_SHOW_TOWNDIRECTORY = 0, - TME_SHOW_FOUNDTOWN, - TME_SHOW_BUILDHOUSE, -}; - static CallBackFunction ToolbarTownClick(Window *w) { - DropDownList *list = new DropDownList(); - *list->Append() = new DropDownListStringItem(STR_TOWN_MENU_TOWN_DIRECTORY, TME_SHOW_TOWNDIRECTORY, false); - - /* Hide "found town" and "fund new house" if we are a spectator */ - if (_local_company != COMPANY_SPECTATOR) { - if (_game_mode == GM_EDITOR || _settings_game.economy.found_town != TF_FORBIDDEN) { - *list->Append() = new DropDownListStringItem(STR_TOWN_MENU_FOUND_TOWN, TME_SHOW_FOUNDTOWN, false); - } - if (_game_mode == GM_EDITOR || _settings_game.economy.allow_placing_houses) { - *list->Append() = new DropDownListStringItem(STR_TOWN_MENU_FUND_HOUSE, TME_SHOW_BUILDHOUSE, false); - } - } - - PopupMainToolbMenu(w, WID_TN_TOWNS, list, TME_SHOW_TOWNDIRECTORY); + PopupMainToolbMenu(w, WID_TN_TOWNS, STR_TOWN_MENU_TOWN_DIRECTORY, (_settings_game.economy.found_town == TF_FORBIDDEN) ? 1 : 2); return CBF_NONE; } @@ -531,9 +512,10 @@ static CallBackFunction ToolbarTownClick(Window *w) static CallBackFunction MenuClickTown(int index) { switch (index) { - case TME_SHOW_TOWNDIRECTORY: ShowTownDirectory(); break; - case TME_SHOW_FOUNDTOWN: ShowFoundTownWindow(); break; - case TME_SHOW_BUILDHOUSE: ShowBuildHousePicker(); break; + case 0: ShowTownDirectory(); break; + case 1: // setting could be changed when the dropdown was open + if (_settings_game.economy.found_town != TF_FORBIDDEN) ShowFoundTownWindow(); + break; } return CBF_NONE; } @@ -1237,10 +1219,9 @@ static CallBackFunction ToolbarScenGenLand(Window *w) static CallBackFunction ToolbarScenGenTown(Window *w) { - DropDownList *list = new DropDownList(); - *list->Append() = new DropDownListStringItem(STR_TOWN_MENU_FOUND_TOWN, TME_SHOW_FOUNDTOWN, false); - *list->Append() = new DropDownListStringItem(STR_TOWN_MENU_FUND_HOUSE, TME_SHOW_BUILDHOUSE, false); - PopupMainToolbMenu(w, WID_TE_TOWN_GENERATE, list, TME_SHOW_FOUNDTOWN); + w->HandleButtonClick(WID_TE_TOWN_GENERATE); + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + ShowFoundTownWindow(); return CBF_NONE; } @@ -2067,10 +2048,9 @@ struct ScenarioEditorToolbarWindow : Window { virtual void OnDropdownSelect(int widget, int index) { - /* The map and the town buttons are in a different location on - * the scenario editor toolbar, so we need to adjust for it. */ + /* The map button is in a different location on the scenario + * editor toolbar, so we need to adjust for it. */ if (widget == WID_TE_SMALL_MAP) widget = WID_TN_SMALL_MAP; - if (widget == WID_TE_TOWN_GENERATE) widget = WID_TN_TOWNS; CallBackFunction cbf = _menu_clicked_procs[widget](index); if (cbf != CBF_NONE) this->last_started_action = cbf; if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); @@ -2223,7 +2203,7 @@ static const NWidgetPart _nested_toolb_scen_inner_widgets[] = { NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT), NWidget(NWID_SPACER), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_LAND_GENERATE), SetDataTip(SPR_IMG_LANDSCAPING, STR_SCENEDIT_TOOLBAR_LANDSCAPE_GENERATION), - NWidget(WWT_IMGBTN, COLOUR_GREY, WID_TE_TOWN_GENERATE), SetDataTip(SPR_IMG_TOWN, STR_SCENEDIT_TOOLBAR_TOWN_GENERATION), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_TOWN_GENERATE), SetDataTip(SPR_IMG_TOWN, STR_SCENEDIT_TOOLBAR_TOWN_GENERATION), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_INDUSTRY), SetDataTip(SPR_IMG_INDUSTRY, STR_SCENEDIT_TOOLBAR_INDUSTRY_GENERATION), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_ROADS), SetDataTip(SPR_IMG_BUILDROAD, STR_SCENEDIT_TOOLBAR_ROAD_CONSTRUCTION), NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TE_WATER), SetDataTip(SPR_IMG_BUILDWATER, STR_TOOLBAR_TOOLTIP_BUILD_SHIP_DOCKS), diff --git a/src/town.h b/src/town.h index 0795767c53..06edbe80ad 100644 --- a/src/town.h +++ b/src/town.h @@ -170,19 +170,7 @@ enum TownFlags { CommandCost CheckforTownRating(DoCommandFlag flags, Town *t, TownRatingCheckType type); -TileIndexDiffC GetHouseNorthPartDiffC(HouseID &house); - -/** - * Determines if a given HouseID is part of a multitile house. - * The given ID is set to the ID of the north tile and the TileDiff to the north tile is returned. - * - * @param house Is changed to the HouseID of the north tile of the same house - * @return TileDiff from the tile of the given HouseID to the north tile - */ -static inline TileIndexDiff GetHouseNorthPart(HouseID &house) -{ - return ToTileIndexDiff(GetHouseNorthPartDiffC(house)); -} +TileIndexDiff GetHouseNorthPart(HouseID &house); Town *CalcClosestTownFromTile(TileIndex tile, uint threshold = UINT_MAX); @@ -200,7 +188,7 @@ void UpdateTownCargoBitmap(); CommandCost CheckIfAuthorityAllowsNewStation(TileIndex tile, DoCommandFlag flags); Town *ClosestTownFromTile(TileIndex tile, uint threshold); void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags); -HouseZonesBits TryGetTownRadiusGroup(const Town *t, TileIndex tile, uint *distance = NULL, uint *zone_inner = NULL, uint *zone_outer = NULL); +HouseZonesBits TryGetTownRadiusGroup(const Town *t, TileIndex tile); HouseZonesBits GetTownRadiusGroup(const Town *t, TileIndex tile); void SetTownRatingTestMode(bool mode); uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t); diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 68eddb8649..83e21d935f 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -52,8 +52,6 @@ #include "safeguards.h" -static const uint SQUARED_TOWN_RADIUS_FUNDED_CENTRE = 26; ///< Squared radius (exclusive) for the most inner #HZB_TOWN_CENTRE town zone while "fund buildings" is in progress. - TownID _new_town_id; uint32 _town_cargoes_accepted; ///< Bitmap of all cargoes accepted by houses. @@ -72,10 +70,6 @@ Town::~Town() * and remove from list of sorted towns */ DeleteWindowById(WC_TOWN_VIEW, this->index); - /* It is possible that "select town to join house to" window - * contain this town. Simply close it. */ - DeleteWindowById(WC_SELECT_TOWN, 0); - /* Check no industry is related to us. */ const Industry *i; FOR_ALL_INDUSTRIES(i) assert(i->town != this); @@ -168,64 +162,6 @@ void Town::InitializeLayout(TownLayout layout) return Town::Get(index); } -static inline uint16 DefaultHouseCostForCargoAccepted(uint16 callback_mask, byte cargo_acceptance, CargoID accepts_cargo) -{ - uint16 cost = HasBit(callback_mask, CBM_HOUSE_CARGO_ACCEPTANCE) ? 6 : cargo_acceptance; // assume 75% (6/8) if cb 1F is available - if (HasBit(callback_mask, CBM_HOUSE_ACCEPT_CARGO)) { - return cost * 4; // assume 50% if cb 2A is available (all three cargoes) - } else if (accepts_cargo == CT_PASSENGERS || accepts_cargo == CT_MAIL) { - return cost * 2; // 25% for passengers/mail - } else if (accepts_cargo != CT_INVALID) { - return cost * 8; // 100% for other cargo (goods etc.) - } else { - return 0; - } -} - -/** - * Generate default value of the house build cost multiplier for a - * house of given properties. - * - * @param callback_mask The bitmask of callbacks defined for the house. Used to determine - * if the house has custom cargo production/acceptance. - * @param population Population of the house. - * @param mail_generation Mail generation multiplier of the house. - * @param cargo_acceptance1 How much cargo is accepted by the house (first cargo). - * @param cargo_acceptance2 How much cargo is accepted by the house (second cargo). - * @param cargo_acceptance3 How much cargo is accepted by the house (third cargo). - * @param accepts_cargo1 Cargo accepted by the house (first). - * @param accepts_cargo2 Cargo accepted by the house (second). - * @param accepts_cargo3 Cargo accepted by the house (third). - * @return Default build cost multiplier. - * - * @see HouseSpec - */ -uint16 DefaultHouseCostBaseMultiplier(uint16 callback_mask, byte population, byte mail_generation, byte cargo_acceptance1, byte cargo_acceptance2, byte cargo_acceptance3, CargoID accepts_cargo1, CargoID accepts_cargo2, CargoID accepts_cargo3) -{ - uint16 cost = 64; - - if (HasBit(callback_mask, CBM_HOUSE_PRODUCE_CARGO)) { - cost += 128 * 3 / 4; // assume 75% if cb 2E is available - } else { - cost += 128 * (population * population + mail_generation * mail_generation) / (255 * 255 + 255 * 255); - } - - cost += DefaultHouseCostForCargoAccepted(callback_mask, cargo_acceptance1, accepts_cargo1); - cost += DefaultHouseCostForCargoAccepted(callback_mask, cargo_acceptance2, accepts_cargo2); - cost += DefaultHouseCostForCargoAccepted(callback_mask, cargo_acceptance3, accepts_cargo3); - - return cost; -} - -/** - * Get the cost for building this house - * @return the cost (inflation corrected etc) - */ -Money HouseSpec::GetConstructionCost() const -{ - return (_price[PR_BUILD_HOUSE] * this->construction_cost) >> 8; -} - /** * Get the cost for removing this house * @return the cost (inflation corrected etc) @@ -337,8 +273,7 @@ static void DrawOldHouseTileInGUI(int x, int y, HouseID house_id, bool ground) } else { /* Add a house on top of the ground? */ if (dcts->building.sprite != 0) { - Point sub = RemapCoords(dcts->subtile_x, dcts->subtile_y, 0); - DrawSprite(dcts->building.sprite, dcts->building.pal, x + UnScaleGUI(sub.x), y + UnScaleGUI(sub.y)); + DrawSprite(dcts->building.sprite, dcts->building.pal, x + dcts->subtile_x, y + dcts->subtile_y); } /* Draw the lift */ if (dcts->draw_proc == 1) DrawHouseLiftInGUI(x, y); @@ -346,17 +281,15 @@ static void DrawOldHouseTileInGUI(int x, int y, HouseID house_id, bool ground) } /** - * Draw image of a house. Image will be centered between the \c left and the \c right and vertically aligned to the \c bottom. + * Draw image of a house. Image will be centered between the \c left and the \c right and verticaly aligned to the \c bottom. * * @param house_id house type * @param left left bound of the drawing area * @param top top bound of the drawing area * @param right right bound of the drawing area * @param bottom bottom bound of the drawing area - * @param image_type Context in which the house is being drawn - * @param variant house variant */ -void DrawHouseImage(HouseID house_id, int left, int top, int right, int bottom, HouseImageType image_type, HouseVariant variant) +void DrawHouseImage(HouseID house_id, int left, int top, int right, int bottom) { DrawPixelInfo tmp_dpi; if (!FillDrawPixelInfo(&tmp_dpi, left, top, right - left + 1, bottom - top + 1)) return; @@ -365,14 +298,13 @@ void DrawHouseImage(HouseID house_id, int left, int top, int right, int bottom, const HouseSpec *hs = HouseSpec::Get(house_id); - /* sprites are relative to the top corner of the ground tile */ - const uint gui_tile_pixels = ScaleGUITrad(TILE_PIXELS); // TILE_PIXELS rescaled to current GUI zoom - uint x = (right - left) / 2; - uint y = bottom - top - gui_tile_pixels; - if (hs->building_flags & TILE_SIZE_1x2) x -= gui_tile_pixels / 2; - if (hs->building_flags & TILE_SIZE_2x1) x += gui_tile_pixels / 2; - if (hs->building_flags & BUILDING_HAS_2_TILES) y -= gui_tile_pixels / 2; - if (hs->building_flags & BUILDING_HAS_4_TILES) y -= gui_tile_pixels / 2; + /* sprites are relative to the topmost pixel of the ground tile */ + uint x = (right - left + 1) / 2; + uint y = bottom - top + 1 - TILE_PIXELS; + if (hs->building_flags & TILE_SIZE_1x2) x -= TILE_PIXELS / 2; + if (hs->building_flags & TILE_SIZE_2x1) x += TILE_PIXELS / 2; + if (hs->building_flags & BUILDING_HAS_2_TILES) y -= TILE_PIXELS / 2; + if (hs->building_flags & BUILDING_HAS_4_TILES) y -= TILE_PIXELS / 2; bool new_house = false; if (house_id >= NEW_HOUSE_OFFSET) { @@ -394,10 +326,10 @@ void DrawHouseImage(HouseID house_id, int left, int top, int right, int bottom, for (uint row = 0; row < num_row; row++) { for (uint col = 0; col < num_col; col++) { Point offset = RemapCoords(row * TILE_SIZE, col * TILE_SIZE, 0); // offset for current tile - offset.x = UnScaleGUI(offset.x); - offset.y = UnScaleGUI(offset.y); + offset.x = UnScaleByZoom(offset.x, ZOOM_LVL_GUI); + offset.y = UnScaleByZoom(offset.y, ZOOM_LVL_GUI); if (new_house) { - DrawNewHouseTileInGUI(x + offset.x, y + offset.y, hid, variant, image_type, ground); + DrawNewHouseTileInGUI(x + offset.x, y + offset.y, hid, ground); } else { DrawOldHouseTileInGUI(x + offset.x, y + offset.y, hid, ground); } @@ -726,15 +658,14 @@ static CommandCost ClearTile_Town(TileIndex tile, DoCommandFlag flags) return cost; } -void AddProducedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &produced, HouseVariant variant) +void AddProducedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &produced) { const HouseSpec *hs = HouseSpec::Get(house_id); if (HasBit(hs->callback_mask, CBM_HOUSE_PRODUCE_CARGO)) { Town *t = (tile == INVALID_TILE) ? NULL : Town::GetByTile(tile); - HouseImageType image_type = (tile == INVALID_TILE) ? HIT_GUI_HOUSE_PREVIEW : HIT_HOUSE_TILE; for (uint i = 0; i < 256; i++) { - uint16 callback = GetHouseCallback(CBID_HOUSE_PRODUCE_CARGO, i, 0, house_id, t, tile, image_type, variant); + uint16 callback = GetHouseCallback(CBID_HOUSE_PRODUCE_CARGO, i, 0, house_id, t, tile); if (callback == CALLBACK_FAILED || callback == CALLBACK_HOUSEPRODCARGO_END) break; @@ -765,11 +696,10 @@ static inline void AddAcceptedCargoSetMask(CargoID cargo, uint amount, CargoArra SetBit(*always_accepted, cargo); } -void AddAcceptedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &acceptance, uint32 *always_accepted, HouseVariant variant) +void AddAcceptedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &acceptance, uint32 *always_accepted) { const HouseSpec *hs = HouseSpec::Get(house_id); Town *t = (tile == INVALID_TILE) ? NULL : Town::GetByTile(tile); - HouseImageType image_type = (tile == INVALID_TILE) ? HIT_GUI_HOUSE_PREVIEW : HIT_HOUSE_TILE; CargoID accepts[3]; /* Set the initial accepted cargo types */ @@ -779,7 +709,7 @@ void AddAcceptedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &accepta /* Check for custom accepted cargo types */ if (HasBit(hs->callback_mask, CBM_HOUSE_ACCEPT_CARGO)) { - uint16 callback = GetHouseCallback(CBID_HOUSE_ACCEPT_CARGO, 0, 0, house_id, t, tile, image_type, variant); + uint16 callback = GetHouseCallback(CBID_HOUSE_ACCEPT_CARGO, 0, 0, house_id, t, tile); if (callback != CALLBACK_FAILED) { /* Replace accepted cargo types with translated values from callback */ accepts[0] = GetCargoTranslation(GB(callback, 0, 5), hs->grf_prop.grffile); @@ -790,7 +720,7 @@ void AddAcceptedHouseCargo(HouseID house_id, TileIndex tile, CargoArray &accepta /* Check for custom cargo acceptance */ if (HasBit(hs->callback_mask, CBM_HOUSE_CARGO_ACCEPTANCE)) { - uint16 callback = GetHouseCallback(CBID_HOUSE_CARGO_ACCEPTANCE, 0, 0, house_id, t, tile, image_type, variant); + uint16 callback = GetHouseCallback(CBID_HOUSE_CARGO_ACCEPTANCE, 0, 0, house_id, t, tile); if (callback != CALLBACK_FAILED) { AddAcceptedCargoSetMask(accepts[0], GB(callback, 0, 4), acceptance, always_accepted); AddAcceptedCargoSetMask(accepts[1], GB(callback, 4, 4), acceptance, always_accepted); @@ -2141,52 +2071,32 @@ bool GenerateTowns(TownLayout layout) /** - * Returns the bit corresponding to the town zone of the specified tile. - * Also returns bounds of this zone. - * - * Unlikely to #GetTownRadiusGroup, returns #HZB_END if the tile is outside of the town. - * - * When returning #HZB_END (outside of the town) \c zone_outer will be set to \c UINT_MAX. + * Returns the bit corresponding to the town zone of the specified tile + * or #HZB_END if the tile is ouside of the town. * * @param t Town on which town zone is to be found * @param tile TileIndex where town zone needs to be found - * @param [out] distance (may be NULL) squared euclidean distance (#DistanceSquare) between the tile and center of the town - * @param [out] zone_inner (may be NULL) squared radius of inner edge of the returned zone (inclusive) - * @param [out] zone_outer (may be NULL) squared radius of outer edge of the returned zone (exclusive) * @return the bit position of the given zone, as defined in HouseZones + * + * @see GetTownRadiusGroup */ -HouseZonesBits TryGetTownRadiusGroup(const Town *t, TileIndex tile, uint *distance, uint *zone_inner, uint *zone_outer) +HouseZonesBits TryGetTownRadiusGroup(const Town *t, TileIndex tile) { - uint dist = DistanceSquare(t->xy, tile); - uint funded_rad = (t->fund_buildings_months > 0) ? SQUARED_TOWN_RADIUS_FUNDED_CENTRE : 0; - const uint32 (&zones)[HZB_END] = t->cache.squared_town_zone_radius; + uint dist = DistanceSquare(tile, t->xy); - /* Check if we are within the town at all. */ - uint inner_rad = max(zones[HZB_TOWN_EDGE], funded_rad); - uint outer_rad = 0; - HouseZonesBits ret = HZB_END; - if (dist < inner_rad) { - /* Loop through zones and find the one that contains the tile. */ - inner_rad = 0; - outer_rad = max(zones[HZB_TOWN_CENTRE], funded_rad); - for (ret = HZB_TOWN_CENTRE; ret > HZB_TOWN_EDGE; ret--) { - if (dist < outer_rad) break; - inner_rad = max(inner_rad, outer_rad); - outer_rad = zones[ret - 1]; - } + if (t->fund_buildings_months && dist <= 25) return HZB_TOWN_CENTRE; + + HouseZonesBits smallest = HZB_END; + for (HouseZonesBits i = HZB_BEGIN; i < HZB_END; i++) { + if (dist < t->cache.squared_town_zone_radius[i]) smallest = i; } - if (distance != NULL) *distance = dist; - if (zone_inner != NULL) *zone_inner = inner_rad; - if (zone_outer != NULL) *zone_outer = outer_rad; - - return ret; + return smallest; } /** * Returns the bit corresponding to the town zone of the specified tile. - * - * Unlikely to #TryGetTownRadiusGroup, returns #HZB_TOWN_EDGE if the tile outside of the town. + * Returns #HZB_TOWN_EDGE if the tile is either in an edge zone or ouside of the town. * * @param t Town on which town zone is to be found * @param tile TileIndex where town zone needs to be found @@ -2251,7 +2161,7 @@ static void MakeTownHouse(TileIndex t, Town *town, byte counter, byte stage, Hou * @param tile tile to check * @param town town that is checking * @param noslope are slopes (foundations) allowed? - * @return cost of building here or error message + * @return success if house can be built here, error message otherwise */ static inline CommandCost CanBuildHouseHere(TileIndex tile, TownID town, bool noslope) { @@ -2272,330 +2182,206 @@ static inline CommandCost CanBuildHouseHere(TileIndex tile, TownID town, bool no /* do not try to build over house owned by another town */ if (IsTileType(tile, MP_HOUSE) && GetTownIndex(tile) != town) return CMD_ERROR; - return ret; + return CommandCost(); } /** - * Checks if a house can be built at this tile, must have the same max z as parameter. - * @param tile tile to check - * @param town town that is checking - * @param z max z of this tile so more parts of a house are at the same height (with foundation) - * @param noslope are slopes (foundations) allowed? - * @return cost of building here or error message - * @see CanBuildHouseHere() - */ -static CommandCost CheckBuildHouseSameZ(TileIndex tile, TownID town, int z, bool noslope) -{ - CommandCost ret = CanBuildHouseHere(tile, town, noslope); - - /* if building on slopes is allowed, there will be flattening foundation (to tile max z) */ - if (ret.Succeeded() && GetTileMaxZ(tile) != z) ret = CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); - - return ret; -} - - -/** - * Checks if a house of size 2x2 can be built at this tile - * @param tile tile, N corner - * @param town town that is checking - * @param z maximum tile z so all tile have the same max z - * @param noslope are slopes (foundations) allowed? - * @return true iff house can be built - * @see CheckBuildHouseSameZ() - */ -static bool CheckFree2x2Area(TileIndex tile, TownID town, int z, bool noslope) -{ - /* we need to check this tile too because we can be at different tile now */ - if (CheckBuildHouseSameZ(tile, town, z, noslope).Failed()) return false; - - for (DiagDirection d = DIAGDIR_SE; d < DIAGDIR_END; d++) { - tile += TileOffsByDiagDir(d); - if (CheckBuildHouseSameZ(tile, town, z, noslope).Failed()) return false; - } - - return true; -} - - -/** - * Check whether houses of given type are buildable in current game. + * Checks if a house can be built here. Important is slope, bridge above + * and ability to clear the land. * - * @param house house type - * @param availability house must be allowed in any of the given climates (#HZ_CLIMALL bits) - * and in any of the given town zones (#HZ_ZONALL bits), skip testing - * climate if no climate bits are given, skip testing town zones if no - * town zone bits are given - * @return success if house is available, error message otherwise + * @param ta tile area to check + * @param town town that is checking + * @param maxz z level of the house, check if all tiles have this max z level + * @param noslope are slopes (foundations) allowed? + * @return success if house can be built here, error message otherwise + * + * @see TownLayoutAllowsHouseHere */ -CommandCost IsHouseTypeAllowed(HouseID house, HouseZones availability, bool allow_historical) +static inline CommandCost CanBuildHouseHere(const TileArea &ta, TownID town, int maxz, bool noslope) { - const HouseSpec *hs = HouseSpec::Get(house); - - /* Disallow disabled and replaced houses. */ - if (!hs->enabled || hs->grf_prop.override != INVALID_HOUSE_ID) return CMD_ERROR; - - /* Disallow additional parts of a multi-tile building. */ - if (!(hs->building_flags & BUILDING_HAS_1_TILE)) return CMD_ERROR; - - /* Check if the house is allowed in all of the given house zones. */ - if ((availability & HZ_ZONALL) && !(availability & HZ_ZONALL & hs->building_availability)) { - return_cmd_error(STR_ERROR_BUILDING_NOT_ALLOWED_IN_THIS_TOWN_ZONE); + TILE_AREA_LOOP(tile, ta) { + CommandCost ret = CanBuildHouseHere(tile, town, noslope); + /* if building on slopes is allowed, there will be flattening foundation (to tile max z) */ + if (ret.Succeeded() && GetTileMaxZ(tile) != maxz) ret = CommandCost(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + if (ret.Failed()) return ret; } - /* Check if we can build this house in any of the given climates. */ - if ((availability & HZ_CLIMALL) && !(availability & HZ_CLIMALL & hs->building_availability)) { - if ((availability & HZ_SUBARTC_ABOVE) && (hs->building_availability & HZ_SUBARTC_BELOW)) { - return_cmd_error(STR_ERROR_CAN_ONLY_BE_BUILT_BELOW_SNOW_LINE); - } - if ((availability & HZ_SUBARTC_BELOW) && (hs->building_availability & HZ_SUBARTC_ABOVE)) { - return_cmd_error(STR_ERROR_CAN_ONLY_BE_BUILT_ABOVE_SNOW_LINE); - } - return CMD_ERROR; - } - - /* Check for historical buildings. */ - if (!allow_historical && hs->extra_flags & BUILDING_IS_HISTORICAL) return CMD_ERROR; - - /* Check if the house can be placed manually. */ - if (_current_company != OWNER_TOWN && (hs->extra_flags & DISALLOW_BUILDING_MANUALLY)) return CMD_ERROR; - if (_current_company < MAX_COMPANIES && (hs->extra_flags & DISALLOW_BUILDING_BY_COMPANIES)) return CMD_ERROR; - return CommandCost(); } + /** - * Check if house is currently buildable. + * Test whether houses of given type are avaliable in current game. + * + * The function will check whether the house is available at all e.g. is not overriden. + * Also availability for current climate and given house zone will be tested. * * @param house house type - * @param t the town the house is about to be built - * @param ignore_year allow obsolete and feature houses - * @return success if house is buildable, error message otherwise + * @param above_snowline true to test availability above the snow line, false for below (arctic climate only) + * @param zone return error if houses are forbidden in this house zone + * @return success if house is avaliable, error message otherwise */ -static CommandCost CheckCanBuildHouse(HouseID house, const Town *t, bool ignore_year) +static inline CommandCost IsHouseTypeAllowed(HouseID house, bool above_snowline, HouseZonesBits zone) + { + const HouseSpec *hs = HouseSpec::Get(house); + /* Disallow disabled and replaced houses. */ + if (!hs->enabled || hs->grf_prop.override != INVALID_HOUSE_ID) return CMD_ERROR; + + /* Check if we can build this house in current climate. */ + if (_settings_game.game_creation.landscape != LT_ARCTIC) { + if (!(hs->building_availability & (HZ_TEMP << _settings_game.game_creation.landscape))) return CMD_ERROR; + } else if (above_snowline) { + if (!(hs->building_availability & HZ_SUBARTC_ABOVE)) return_cmd_error(STR_ERROR_BUILDING_NOT_ALLOWED_ABOVE_SNOW_LINE); + } else { + if (!(hs->building_availability & HZ_SUBARTC_BELOW)) return_cmd_error(STR_ERROR_BUILDING_NOT_ALLOWED_BELOW_SNOW_LINE); + } + + /* Check if the house zone is allowed for this type of houses. */ + if (!HasBit(hs->building_availability & HZ_ZONALL, zone)) { + return_cmd_error(STR_ERROR_BUILDING_NOT_ALLOWED_IN_THIS_TOWN_ZONE); + } + + return CommandCost(); +} + + +/** + * Check whether a town can hold more house types. + * @param t the town we wan't to check + * @param house type of the house we wan't to add + * @return success if houses of this type are allowed, error message otherwise + */ +static inline CommandCost IsAnotherHouseTypeAllowedInTown(Town *t, HouseID house) { const HouseSpec *hs = HouseSpec::Get(house); - if (!ignore_year && (_cur_year > hs->max_year || _cur_year < hs->min_year)) return CMD_ERROR; - /* Don't let these counters overflow. Global counters are 32bit, there will never be that many houses. */ if (hs->class_id != HOUSE_NO_CLASS) { /* id_count is always <= class_count, so it doesn't need to be checked */ - if (t->cache.building_counts.class_count[hs->class_id] == UINT16_MAX) return CMD_ERROR; + if (t->cache.building_counts.class_count[hs->class_id] == UINT16_MAX) return_cmd_error(STR_ERROR_TOO_MANY_HOUSE_SETS); } else { /* If the house has no class, check id_count instead */ - if (t->cache.building_counts.id_count[house] == UINT16_MAX) return CMD_ERROR; - } - - /* Special houses that there can be only one of. */ - if (hs->building_flags & BUILDING_IS_CHURCH) { - if (HasBit(t->flags, TOWN_HAS_CHURCH)) return_cmd_error(STR_ERROR_ONLY_ONE_CHURCH_PER_TOWN); - } else if (hs->building_flags & BUILDING_IS_STADIUM) { - if (HasBit(t->flags, TOWN_HAS_STADIUM)) return_cmd_error(STR_ERROR_ONLY_ONE_STADIUM_PER_TOWN); + if (t->cache.building_counts.id_count[house] == UINT16_MAX) return_cmd_error(STR_ERROR_TOO_MANY_HOUSE_TYPES); } return CommandCost(); } -/** - * Check if a house can be built at a given area. - * @param house house type - * @param tile northern tile of the area to check - * @return cost of building here or error message - * @see CanBuildHouseHere() - */ -CommandCost CheckFlatLandHouse(HouseID house, TileIndex tile) -{ - const HouseSpec *hs = HouseSpec::Get(house); - uint w = hs->building_flags & BUILDING_2_TILES_X ? 2 : 1; - uint h = hs->building_flags & BUILDING_2_TILES_Y ? 2 : 1; - bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0; - int maxz = GetTileMaxZ(tile); - CommandCost cost(EXPENSES_CONSTRUCTION); - TILE_AREA_LOOP(ti, TileArea(tile, w, h)) { - CommandCost ret = (ti == tile) ? - CanBuildHouseHere(ti, INVALID_TOWN, noslope) : - CheckBuildHouseSameZ(ti, INVALID_TOWN, maxz, noslope); - if (ret.Failed()) return ret; - cost.AddCost(ret); - } - - return cost; -} - - /** * Checks if current town layout allows building here * @param t town - * @param tile tile to check + * @param ta tile area to check * @return true iff town layout allows building here * @note see layouts */ -static inline bool TownLayoutAllowsHouseHere(Town *t, TileIndex tile) +static inline bool TownLayoutAllowsHouseHere(Town *t, const TileArea &ta) { /* Allow towns everywhere when we don't build roads */ if (!_settings_game.economy.allow_town_roads && !_generating_world) return true; - TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile); + TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, ta.tile); + const uint overflow = 3 * 4 * UINT16_MAX; // perform "floor division" switch (t->layout) { - case TL_2X2_GRID: - if ((grid_pos.x % 3) == 0 || (grid_pos.y % 3) == 0) return false; - break; - - case TL_3X3_GRID: - if ((grid_pos.x % 4) == 0 || (grid_pos.y % 4) == 0) return false; - break; - - default: - break; + case TL_2X2_GRID: return (uint)(grid_pos.x + overflow) % 3 >= ta.w && (uint)(grid_pos.y + overflow) % 3 >= ta.h; + case TL_3X3_GRID: return (uint)(grid_pos.x + overflow) % 4 >= ta.w && (uint)(grid_pos.y + overflow) % 4 >= ta.h; + default: return true; } - - return true; } /** - * Checks if current town layout allows 2x2 building here - * @param t town - * @param tile tile to check - * @return true iff town layout allows 2x2 building here - * @note see layouts - */ -static inline bool TownLayoutAllows2x2HouseHere(Town *t, TileIndex tile) -{ - /* Allow towns everywhere when we don't build roads */ - if (!_settings_game.economy.allow_town_roads && !_generating_world) return true; - - /* Compute relative position of tile. (Positive offsets are towards north) */ - TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile); - - switch (t->layout) { - case TL_2X2_GRID: - grid_pos.x %= 3; - grid_pos.y %= 3; - if ((grid_pos.x != 2 && grid_pos.x != -1) || - (grid_pos.y != 2 && grid_pos.y != -1)) return false; - break; - - case TL_3X3_GRID: - if ((grid_pos.x & 3) < 2 || (grid_pos.y & 3) < 2) return false; - break; - - default: - break; - } - - return true; -} - - -/** - * Checks if 1x2 or 2x1 building is allowed here, also takes into account current town layout - * Also, tests both building positions that occupy this tile - * @param tile tile where the building should be built - * @param t town - * @param maxz all tiles should have the same height - * @param noslope are slopes forbidden? - * @param second diagdir from first tile to second tile - */ -static bool CheckTownBuild2House(TileIndex *tile, Town *t, int maxz, bool noslope, DiagDirection second) -{ - /* 'tile' is already checked in BuildTownHouse() - CanBuildHouseHere() and slope test */ - - TileIndex tile2 = *tile + TileOffsByDiagDir(second); - if (TownLayoutAllowsHouseHere(t, tile2) && CheckBuildHouseSameZ(tile2, t->index, maxz, noslope).Succeeded()) return true; - - tile2 = *tile + TileOffsByDiagDir(ReverseDiagDir(second)); - if (TownLayoutAllowsHouseHere(t, tile2) && CheckBuildHouseSameZ(tile2, t->index, maxz, noslope).Succeeded()) { - *tile = tile2; - return true; - } - - return false; -} - - -/** - * Checks if 2x2 building is allowed here, also takes into account current town layout - * Also, tests all four building positions that occupy this tile - * @param tile tile where the building should be built - * @param t town - * @param maxz all tiles should have the same height - * @param noslope are slopes forbidden? - */ -static bool CheckTownBuild2x2House(TileIndex *tile, Town *t, int maxz, bool noslope) -{ - TileIndex tile2 = *tile; - - for (DiagDirection d = DIAGDIR_SE;; d++) { // 'd' goes through DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_END - if (TownLayoutAllows2x2HouseHere(t, tile2) && CheckFree2x2Area(tile2, t->index, maxz, noslope)) { - *tile = tile2; - return true; - } - if (d == DIAGDIR_END) break; - tile2 += TileOffsByDiagDir(ReverseDiagDir(d)); // go clockwise - } - - return false; -} - - -/** - * Check if a given tile is not too far from town for placing a house. + * Find a suitable place (free of any obstacles) for a new town house. Search around a given location + * taking into account the layout of the town. * + * @param tile tile that must be included by the building + * @param t the town we are building in + * @param house house type + * @return where the building can be placed, INVALID_TILE if no lacation was found + * + * @pre CanBuildHouseHere(tile, t->index, false) + * + * @see CanBuildHouseHere + */ +static TileIndex FindPlaceForTownHouseAroundTile(TileIndex tile, Town *t, HouseID house) +{ + const HouseSpec *hs = HouseSpec::Get(house); + bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0; + + TileArea ta(tile, 1, 1); + DiagDirection dir; + uint count; + if (hs->building_flags & TILE_SIZE_2x2) { + ta.w = ta.h = 2; + dir = DIAGDIR_NW; // 'd' goes through DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_SE + count = 4; + } else if (hs->building_flags & TILE_SIZE_2x1) { + ta.w = 2; + dir = DIAGDIR_NE; + count = 2; + } else if (hs->building_flags & TILE_SIZE_1x2) { + ta.h = 2; + dir = DIAGDIR_NW; + count = 2; + } else { // TILE_SIZE_1x1 + /* CanBuildHouseHere(tile, t->index, false) already checked */ + if (noslope && !IsTileFlat(tile)) return INVALID_TILE; + return tile; + } + + int maxz = GetTileMaxZ(tile); + /* Drift around the tile and find a place for the house. For 1x2 and 2x1 houses just two + * positions will be checked (at the exact tile and the other). In case of 2x2 houses + * 4 positions have to be checked (clockwise). */ + while (count-- > 0) { + if (!TownLayoutAllowsHouseHere(t, ta)) continue; + if (CanBuildHouseHere(ta, t->index, maxz, noslope).Succeeded()) return ta.tile; + ta.tile += TileOffsByDiagDir(dir); + dir = ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT); + } + + return INVALID_TILE; +} + + +/** + * Check if a given house can be built in a given town. + * @param house house type * @param t the town - * @param tile the tile - * @param allow_outside whether to allow some area outside but near to the town. - * @return success or an error + * @return success if house can be built, error message otherwise */ -CommandCost CheckHouseDistanceFromTown(const Town *t, TileIndex tile, bool allow_outside) +static CommandCost CheckCanBuildHouse(HouseID house, const Town *t) { - uint square_dist = DistanceSquare(t->xy, tile); - uint square_rad = t->cache.squared_town_zone_radius[HZB_BEGIN]; - if (t->fund_buildings_months > 0) square_rad = max(square_rad, SQUARED_TOWN_RADIUS_FUNDED_CENTRE); + const HouseSpec *hs = HouseSpec::Get(house); - if (square_dist < square_rad) return CommandCost(); - - if (allow_outside) { - /* How far from edge of a town a house can be placed (in tiles). */ - const uint overdist = 8; - if (SqrtCmp(square_dist, square_rad, overdist * overdist)) return CommandCost(); + if (_loaded_newgrf_features.has_newhouses && !_generating_world && + _game_mode != GM_EDITOR && (hs->extra_flags & BUILDING_IS_HISTORICAL) != 0) { + return CMD_ERROR; } - return_cmd_error(STR_ERROR_TOO_FAR_FROM_TOWN); + if (_cur_year > hs->max_year) return_cmd_error(STR_ERROR_BUILDING_IS_TOO_OLD); + if (_cur_year < hs->min_year) return_cmd_error(STR_ERROR_BUILDING_IS_TOO_MODERN); + + /* Special houses that there can be only one of. */ + if (hs->building_flags & BUILDING_IS_CHURCH) { + if (HasBit(t->flags, TOWN_HAS_CHURCH)) return_cmd_error(STR_ERROR_ONLY_ONE_BUILDING_ALLOWED_PER_TOWN); + } else if (hs->building_flags & BUILDING_IS_STADIUM) { + if (HasBit(t->flags, TOWN_HAS_STADIUM)) return_cmd_error(STR_ERROR_ONLY_ONE_BUILDING_ALLOWED_PER_TOWN); + } + + return CommandCost(); } -/** - * Set house variant after placing a house. - * - * @param tile tile where the house was placed - * @param variant house variant - */ -static void SetupHouseVariant(TileIndex tile, byte variant) -{ - if (!_loaded_newgrf_features.has_newhouses) return; - - HouseID house = GetHouseType(tile); - if (house < NEW_HOUSE_OFFSET) return; - - BuildingFlags flags = HouseSpec::Get(house)->building_flags; - AnimateNewHouseSetupVariant(tile, variant); - if (flags & BUILDING_2_TILES_Y) AnimateNewHouseSetupVariant(tile + TileDiffXY(0, 1), variant); - if (flags & BUILDING_2_TILES_X) AnimateNewHouseSetupVariant(tile + TileDiffXY(1, 0), variant); - if (flags & BUILDING_HAS_4_TILES) AnimateNewHouseSetupVariant(tile + TileDiffXY(1, 1), variant); -} /** * Really build a house. * @param t town to build house in * @param tile house location * @param house house type - * @param variant house variant * @param random_bits random bits for the house */ -static void DoBuildHouse(Town *t, TileIndex tile, HouseID house, byte variant, byte random_bits) +static void DoBuildHouse(Town *t, TileIndex tile, HouseID house, byte random_bits) { t->cache.num_houses++; @@ -2625,80 +2411,56 @@ static void DoBuildHouse(Town *t, TileIndex tile, HouseID house, byte variant, b } MakeTownHouse(tile, t, construction_counter, construction_stage, house, random_bits); - SetupHouseVariant(tile, variant); UpdateTownRadius(t); UpdateTownCargoes(t, tile); } -/** - * Convert current climate to corresponding #HouseZones. - * - * @param altitude Test against given tile height (above/below snowline). - * -1 to return all bits possible in current climate e.g. - * both HZ_SUBARTC_ABOVE and HZ_SUBARTC_BELOW in arctic. - * @return Bitmask related to current climate (subset of #HZ_CLIMALL). - */ -HouseZones CurrentClimateHouseZones(int altitude) -{ - if (_settings_game.game_creation.landscape == LT_ARCTIC) { - if (altitude < 0) return HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW; - if (altitude > HighestSnowLine()) return HZ_SUBARTC_ABOVE; - } - return (HouseZones)(HZ_TEMP << _settings_game.game_creation.landscape); -} - -static Money GetHouseConstructionCost(HouseID house) -{ - const HouseSpec *hs = HouseSpec::Get(house); - uint flags = hs->building_flags; - Money cost = hs->GetConstructionCost(); - if (flags & BUILDING_2_TILES_Y) cost += (++hs)->GetConstructionCost(); - if (flags & BUILDING_2_TILES_X) cost += (++hs)->GetConstructionCost(); - if (flags & BUILDING_HAS_4_TILES) cost += (++hs)->GetConstructionCost(); - return cost; -} - /** * Place a custom house * @param tile tile where the house will be located * @param flags flags for the command - * @param p1 - * - p1 = (bit 0-15) - the HouseID of the house - * - p1 = (bit 16-31) - the TownID of the town - * @param p2 - * - p2 = (bit 0-7 ) - random bits - * - p2 = (bit 8-15) - house variant (1..num_variants) or 0 for no variant + * @param p1 \n + * bits 0..15 - the HouseID of the house \n + * bits 16..31 - the TownID of the town \n + * @param p2 \n + * bits 0..7 - random bits \n * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildHouse(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { + if (_game_mode != GM_EDITOR && // in scenario editor anyone can build a house + _current_company != OWNER_TOWN && // towns naturally can build houses + _current_company != OWNER_DEITY) { // GameScript can place a house too + return CMD_ERROR; + } + HouseID house = GB(p1, 0, 16); - Town *t = Town::GetIfValid(GB(p1, 16, 16)); + Town *t = Town::Get(GB(p1, 16, 16)); if (t == NULL) return CMD_ERROR; byte random_bits = GB(p2, 0, 8); - byte variant = GB(p2, 8, 8); - bool deity = (_current_company == OWNER_DEITY) || (_game_mode == GM_EDITOR && _current_company == OWNER_NONE); - if (!deity && !_settings_game.economy.allow_placing_houses) return CMD_ERROR; - HouseZones zones = CurrentClimateHouseZones(deity ? -1 : GetTileMaxZ(tile)); - if (!deity) zones |= (HouseZones)(HZ_ZON1 << GetTownRadiusGroup(t, tile)); + int max_z = GetTileMaxZ(tile); + bool above_snowline = (_settings_game.game_creation.landscape == LT_ARCTIC) && (max_z > HighestSnowLine()); - CommandCost cost(EXPENSES_CONSTRUCTION); - - CommandCost ret = CheckHouseDistanceFromTown(t, tile, deity); - if (ret.Succeeded()) ret = IsHouseTypeAllowed(house, zones, deity); - if (ret.Succeeded()) ret = CheckCanBuildHouse(house, t, deity); - if (ret.Succeeded()) ret = CheckFlatLandHouse(house, tile); - if (ret.Succeeded()) cost.AddCost(ret); - if (ret.Succeeded()) ret = HouseAllowsConstruction(house, variant, tile, t, random_bits); + CommandCost ret = IsHouseTypeAllowed(house, above_snowline, TryGetTownRadiusGroup(t, tile)); + if (ret.Succeeded()) ret = IsAnotherHouseTypeAllowedInTown(t, house); + if (ret.Succeeded()) ret = CheckCanBuildHouse(house, t); + if (ret.Succeeded()) { + /* While placing a house manually, try only at exact position and ignore the layout */ + const HouseSpec *hs = HouseSpec::Get(house); + uint w = hs->building_flags & BUILDING_2_TILES_X ? 2 : 1; + uint h = hs->building_flags & BUILDING_2_TILES_Y ? 2 : 1; + bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0; + ret = CanBuildHouseHere(TileArea(tile, w, h), t->index, max_z, noslope); + } if (ret.Failed()) return ret; - /* Place the house. */ - if (flags & DC_EXEC) DoBuildHouse(t, tile, house, variant, random_bits); + /* Check if GRF allows this house */ + if (!HouseAllowsConstruction(house, tile, t, random_bits)) return_cmd_error(STR_ERROR_BUILDING_NOT_ALLOWED); - cost.AddCost(GetHouseConstructionCost(house)); - return cost; + if (flags & DC_EXEC) DoBuildHouse(t, tile, house, random_bits); + return CommandCost(); } /** @@ -2710,18 +2472,17 @@ CommandCost CmdBuildHouse(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 static bool BuildTownHouse(Town *t, TileIndex tile) { /* forbidden building here by town layout */ - if (!TownLayoutAllowsHouseHere(t, tile)) return false; + if (!TownLayoutAllowsHouseHere(t, TileArea(tile, 1, 1))) return false; /* no house allowed at all, bail out */ if (CanBuildHouseHere(tile, t->index, false).Failed()) return false; - Slope slope = GetTileSlope(tile); - int maxz = GetTileMaxZ(tile); - - /* Get the town zone type of the current tile, as well as the climate. - * This will allow to easily compare with the specs of the new house to build */ - HouseZones bitmask = (HouseZones)(HZ_ZON1 << GetTownRadiusGroup(t, tile)) | CurrentClimateHouseZones(maxz); + bool above_snowline = _settings_game.game_creation.landscape == LT_ARCTIC && GetTileMaxZ(tile) > HighestSnowLine(); + HouseZonesBits zone = GetTownRadiusGroup(t, tile); + /* bits 0-4 are used + * bits 11-15 are used + * bits 5-10 are not used. */ HouseID houses[NUM_HOUSES]; uint num = 0; uint probs[NUM_HOUSES]; @@ -2729,7 +2490,8 @@ static bool BuildTownHouse(Town *t, TileIndex tile) /* Generate a list of all possible houses that can be built. */ for (uint i = 0; i < NUM_HOUSES; i++) { - if (IsHouseTypeAllowed(i, bitmask, _generating_world || _game_mode == GM_EDITOR).Failed()) continue; + if (IsHouseTypeAllowed((HouseID)i, above_snowline, zone).Failed()) continue; + if (IsAnotherHouseTypeAllowedInTown(t, (HouseID)i).Failed()) continue; /* Without NewHouses, all houses have probability '1' */ uint cur_prob = (_loaded_newgrf_features.has_newhouses ? HouseSpec::Get(i)->probability : 1); @@ -2763,30 +2525,18 @@ static bool BuildTownHouse(Town *t, TileIndex tile) houses[i] = houses[num]; probs[i] = probs[num]; - if (CheckCanBuildHouse(house, t, false).Failed()) continue; + CommandCost ret = CheckCanBuildHouse(house, t); + if (ret.Failed()) continue; - const HouseSpec *hs = HouseSpec::Get(house); - - /* Make sure there is no slope? */ - bool noslope = (hs->building_flags & TILE_NOT_SLOPED) != 0; - if (noslope && slope != SLOPE_FLAT) continue; - - if (hs->building_flags & TILE_SIZE_2x2) { - if (!CheckTownBuild2x2House(&tile, t, maxz, noslope)) continue; - } else if (hs->building_flags & TILE_SIZE_2x1) { - if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SW)) continue; - } else if (hs->building_flags & TILE_SIZE_1x2) { - if (!CheckTownBuild2House(&tile, t, maxz, noslope, DIAGDIR_SE)) continue; - } else { - /* 1x1 house checks are already done */ - } + tile = FindPlaceForTownHouseAroundTile(tile, t, house); + if (tile == INVALID_TILE) continue; byte random_bits = Random(); /* Check if GRF allows this house */ - if (HouseAllowsConstruction(house, HOUSE_NO_VARIANT, tile, t, random_bits).Failed()) continue; + if (!HouseAllowsConstruction(house, tile, t, random_bits)) continue; - DoBuildHouse(t, tile, house, HOUSE_NO_VARIANT, random_bits); + DoBuildHouse(t, tile, house, random_bits); return true; } @@ -2810,27 +2560,30 @@ static void DoClearTownHouseHelper(TileIndex tile, Town *t, HouseID house) } /** - * Determines if a given HouseID is part of a multi-tile house. + * Determines if a given HouseID is part of a multitile house. * The given ID is set to the ID of the north tile and the TileDiff to the north tile is returned. * * @param house Is changed to the HouseID of the north tile of the same house - * @return TileIndexDiffC from the tile of the given HouseID to the north tile + * @return TileDiff from the tile of the given HouseID to the north tile */ -TileIndexDiffC GetHouseNorthPartDiffC(HouseID &house) +TileIndexDiff GetHouseNorthPart(HouseID &house) { - TileIndexDiffC ret = { 0, 0 }; if (house >= 3) { // house id 0,1,2 MUST be single tile houses, or this code breaks. if (HouseSpec::Get(house - 1)->building_flags & TILE_SIZE_2x1) { - house--, ret.x = -1, ret.y = 0; + house--; + return TileDiffXY(-1, 0); } else if (HouseSpec::Get(house - 1)->building_flags & BUILDING_2_TILES_Y) { - house--, ret.x = 0, ret.y = -1; + house--; + return TileDiffXY(0, -1); } else if (HouseSpec::Get(house - 2)->building_flags & BUILDING_HAS_4_TILES) { - house -= 2, ret.x = -1, ret.y = 0; + house -= 2; + return TileDiffXY(-1, 0); } else if (HouseSpec::Get(house - 3)->building_flags & BUILDING_HAS_4_TILES) { - house -= 3, ret.x = -1, ret.y = -1; + house -= 3; + return TileDiffXY(-1, -1); } } - return ret; + return 0; } void ClearTownHouse(Town *t, TileIndex tile) @@ -2898,7 +2651,6 @@ CommandCost CmdRenameTown(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 t->UpdateVirtCoord(); InvalidateWindowData(WC_TOWN_DIRECTORY, 0, 1); - SetWindowDirty(WC_SELECT_TOWN, 0); // only repaint this window even thought it may be too narrow for the new name UpdateAllStationVirtCoords(); } return CommandCost(); diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 5fa57b296a..ed19d033a4 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -18,7 +18,6 @@ #include "company_func.h" #include "company_base.h" #include "company_gui.h" -#include "core/random_func.hpp" #include "network/network.h" #include "string_func.h" #include "strings_func.h" @@ -36,20 +35,17 @@ #include "newgrf_config.h" #include "newgrf_house.h" #include "date_func.h" -#include "zoom_func.h" +#include "core/random_func.hpp" #include "widgets/town_widget.h" #include "table/strings.h" -#include - #include "safeguards.h" typedef GUIList GUITownList; -static CommandCost ListTownsToJoinHouseTo(HouseID house, TileIndex tile, TownList *towns); -static void ShowSelectTownWindow(const TownList &towns, const CommandContainer &cmd); +static void PlaceProc_House(TileIndex tile); static const NWidgetPart _nested_town_authority_widgets[] = { NWidget(NWID_HORIZONTAL), @@ -1204,15 +1200,11 @@ static WindowDesc _found_town_desc( void ShowFoundTownWindow() { - if (_game_mode != GM_EDITOR) { - if (_settings_game.economy.found_town == TF_FORBIDDEN) return; - if (!Company::IsValidID(_local_company)) return; - } + if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return; AllocateWindowDescFront(&_found_town_desc, 0); } -/** List of buildable houses and house sets. */ -class GUIHouseList : protected SmallVector { +class GUIHouseList : public SmallVector { protected: SmallVector house_sets; ///< list of house sets, each item points the first house of the set in the houses array @@ -1242,112 +1234,82 @@ public: *this->house_sets.Append() = 0; // terminator } - /** - * Get house at given offset. - * @param house_set House set (or a negative number). - * @param house_offset Offset of the house within the set (or a negative number). - * @return House at given offset or #INVALID_HOUSE_ID if \c house_set or \c house_offset is negative. - */ - inline HouseID GetHouseAtOffset(int house_set, int house_offset) const + inline HouseID GetHouseAtOffset(uint house_set, uint house_offset) const { - if (house_set < 0 || house_offset < 0) return INVALID_HOUSE_ID; - assert(house_set < (int)this->NumHouseSets()); - assert(this->house_sets[house_set] + house_offset < (int)this->Length()); return *this->Get(this->house_sets[house_set] + house_offset); } - /** - * Get the number of house sets. - * @return Number of house sets. - */ uint NumHouseSets() const { return this->house_sets.Length() - 1; // last item is a terminator } - /** - * Get number of houses in a given set. - * @param house_set House set (or a negative number). - * @return Number of houses in the given set or 0 if \c house_set is negative. - */ - uint NumHousesInHouseSet(int house_set) const + uint NumHousesInHouseSet(uint house_set) const { - assert(house_set < (int)this->NumHouseSets()); + assert(house_set < this->NumHouseSets()); /* There is a terminator on the list of house sets. It's equal to the number * of all houses. We can safely use "house_set + 1" even for the last * house set. */ - return house_set < 0 ? 0 : this->house_sets[house_set + 1] - this->house_sets[house_set]; + return this->house_sets[house_set + 1] - this->house_sets[house_set]; } - /** - * Find the house set of a given house. - * - * This operation is O(number of house sets). - * - * @param house The house (or #INVALID_HOUSE_ID). - * @return House set of the house or -1 if not found. - */ int FindHouseSet(HouseID house) const { - if (house != INVALID_HOUSE_ID) { - const GRFFile *house_set = HouseSpec::Get(house)->grf_prop.grffile; - for (uint i = 0; i < this->NumHouseSets(); i++) { - if (HouseSpec::Get(this->GetHouseAtOffset(i, 0))->grf_prop.grffile == house_set) return i; - } + const GRFFile *house_set = HouseSpec::Get(house)->grf_prop.grffile; + for (uint i = 0; i < this->NumHouseSets(); i++) { + if (HouseSpec::Get(this->GetHouseAtOffset(i, 0))->grf_prop.grffile == house_set) return i; } return -1; } - /** - * Find offset of a given house within a given house set. - * - * This operation is O(number of houses in the set). - * - * @param house_set House set to search in (or a negative number). - * @param house The house (or #INVALID_HOUSE_ID). - * @return Offset of the house within the set or -1 if not found or \c house_set is negative. - */ - int FindHouseOffset(int house_set, HouseID house) const - { - assert(house_set < (int)this->NumHouseSets()); - if (house_set >= 0 && house != INVALID_HOUSE_ID) { - const HouseID *begin = this->Begin() + this->house_sets[house_set]; - const HouseID *end = this->Begin() + this->house_sets[house_set + 1]; - const HouseID *pos = std::lower_bound(begin, end, house); - if (pos != end && *pos == house) return pos - begin; - } - return -1; - } - - /** - * Get the name of a given house set. - * - * @param house_set The set. - * @return The name (appropriate string parameters will be set). - */ - StringID GetNameOfHouseSet(uint house_set) const + int FindHouseOffset(uint house_set, HouseID house) const { assert(house_set < this->NumHouseSets()); - const GRFFile *grf = HouseSpec::Get(this->GetHouseAtOffset(house_set, 0))->grf_prop.grffile; - if (grf != NULL) { - SetDParamStr(0, GetGRFConfig(grf->grfid)->GetName()); - return STR_JUST_RAW_STRING; - } else { - SetDParam(0, STR_BASIC_HOUSE_SET_NAME); - return STR_JUST_STRING; + uint count = this->NumHousesInHouseSet(house_set); + for (uint i = 0; i < count; i++) { + if (this->GetHouseAtOffset(house_set, i) == house) return i; } + return -1; } - /** (Re)build the list. */ + const char *GetNameOfHouseSet(uint house_set) const + { + assert(house_set < this->NumHouseSets()); + const GRFFile *gf = HouseSpec::Get(this->GetHouseAtOffset(house_set, 0))->grf_prop.grffile; + if (gf != NULL) return GetGRFConfig(gf->grfid)->GetName(); + + static char name[DRAW_STRING_BUFFER]; + GetString(name, STR_BASIC_HOUSE_SET_NAME, lastof(name)); + return name; + } + + /** + * Notify the sortlist that the rebuild is done + * + * @note This forces a resort + */ void Build() { /* collect items */ this->Clear(); - HouseZones zones = CurrentClimateHouseZones(); for (HouseID house = 0; house < NUM_HOUSES; house++) { - if (IsHouseTypeAllowed(house, zones, _game_mode == GM_EDITOR).Succeeded()) *this->Append() = house; + const HouseSpec *hs = HouseSpec::Get(house); + /* is the house enabled? */ + if (!hs->enabled) continue; + /* is the house overriden? */ + if (hs->grf_prop.override != INVALID_HOUSE_ID) continue; + /* is the house allownd in current landscape? */ + HouseZones landscapes = (HouseZones)(HZ_TEMP << _settings_game.game_creation.landscape); + if (_settings_game.game_creation.landscape == LT_ARCTIC) landscapes |= HZ_SUBARTC_ABOVE; + if (!(hs->building_availability & landscapes)) continue; + /* is the house allowed at any of house zones at all? */ + if (!(hs->building_availability & HZ_ZONALL)) continue; + /* is there any year in which the house is allowed? */ + if (hs->min_year > hs->max_year) continue; + + /* add the house */ + *this->Append() = house; } - this->Compact(); /* arrange items */ QSortT(this->Begin(), this->Length(), HouseSorter); @@ -1368,121 +1330,85 @@ public: } }; -static struct { - HouseID id; - HouseVariant variant; -} -_cur_house = { INVALID_HOUSE_ID, HOUSE_NO_VARIANT }; ///< house selected in the house picker window +static HouseID _cur_house = INVALID_HOUSE_ID; ///< house selected in the house picker window /** The window used for building houses. */ class HousePickerWindow : public Window { - friend void ShowBuildHousePicker(); - protected: - GUIHouseList house_list; ///< list of houses and house sets - HouseZonesBits tileselect_zone; ///< house zone (closest town) of currently highlighted tile - bool tileselect_bad_land; ///< whether currently highlighted tile has wrong landscape for the house (e.g. wrong side of the snowline) + GUIHouseList house_list; ///< list of houses and house sets + int house_offset; ///< index of selected house + uint house_set; ///< index of selected house set + uint line_height; ///< height of a single line in the list of house sets + HouseID display_house; ///< house ID of currently displayed house - /** - * Get the height of a single line in the list of house sets. - * @return the height - */ - inline uint GetLineHeight() const + void RestoreSelectedHouseIndex() { - return FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; + this->house_set = 0; + this->house_offset = 0; + + if (this->house_list.Length() == 0) { // no houses at all? + _cur_house = INVALID_HOUSE_ID; + this->display_house = _cur_house; + return; + } + + if (_cur_house != INVALID_HOUSE_ID) { + int house_set = this->house_list.FindHouseSet(_cur_house); + if (house_set >= 0) { + this->house_set = house_set; + int house_offset = this->house_list.FindHouseOffset(house_set, _cur_house); + if (house_offset >= 0) { + this->house_offset = house_offset; + return; + } + } + } + _cur_house = this->house_list.GetHouseAtOffset(this->house_set, this->house_offset); + this->display_house = _cur_house; } - /** - * Test whether this window is currently being a callback window for ongoing tile selection. - * @return result of the test - * @see SetObjectToPlace - */ - inline bool IsObjectToPlaceSet() const + void SelectHouseIntl(uint new_house_set, int new_house_offset) { - return _thd.window_class == WC_BUILD_HOUSE; - } - - /** - * Test whether a given house is currently disabled (greyed out houses). - * @return result of the test - */ - inline bool IsHouseDisabled(HouseID house) const - { - if (_game_mode == GM_EDITOR) return false; - const HouseSpec *hs = HouseSpec::Get(house); - return _cur_year < hs->min_year || _cur_year > hs->max_year; - } - - /** - * Get currently selected house set. - * @return index of the house set - */ - int GetCurrentHouseSet() const - { - return this->house_list.FindHouseSet(_cur_house.id); + SetObjectToPlaceWnd(SPR_CURSOR_TOWN, PAL_NONE, HT_RECT, this); + this->house_set = new_house_set; + this->house_offset = new_house_offset; + _cur_house = this->house_list.GetHouseAtOffset(new_house_set, new_house_offset); + this->display_house = _cur_house; } /** * Select another house. - * @param new_house_set index of the house set, -1 to auto-select - * @param new_house_offset offset of the house, -1 to auto-select - * @param new_house_variant variant of the house, -1 to auto-select - * @param clicked whether to make the house button "clicked" and activate house placement (>0 yes, 0 no, -1 auto) + * @param new_house_set index of the house set + * @param new_house_offset offset of the house */ - void SelectOtherHouse(int new_house_set, int new_house_offset, int new_house_variant, int clicked) + void SelectOtherHouse(uint new_house_set, int new_house_offset) { - if (this->house_list.NumHouseSets() == 0) { // special handling needed - _cur_house.id = INVALID_HOUSE_ID; - _cur_house.variant = HOUSE_NO_VARIANT; - if (this->IsObjectToPlaceSet()) ResetObjectToPlace(); - return; - } + assert(new_house_set < this->house_list.NumHouseSets()); + assert(new_house_offset < (int) this->house_list.NumHousesInHouseSet(new_house_set)); + assert(new_house_offset >= 0); - /* auto-select */ - if (new_house_set < 0) new_house_set = max(0, this->GetCurrentHouseSet()); - if (new_house_offset < 0) new_house_offset = max(0, this->house_list.FindHouseOffset(new_house_set, _cur_house.id)); - if (clicked < 0) clicked = this->IsObjectToPlaceSet() ? 1 : 0; - - HouseID new_house_id = this->house_list.GetHouseAtOffset(new_house_set, new_house_offset); - - const HouseSpec *hs = HouseSpec::Get(new_house_id); - if (hs->num_variants == 0) { - new_house_variant = HOUSE_NO_VARIANT; - } else { - if (new_house_variant < 0 && new_house_id == _cur_house.id) new_house_variant = _cur_house.variant; - if (!IsInsideBS(new_house_variant, HOUSE_FIRST_VARIANT, hs->num_variants)) new_house_variant = HOUSE_FIRST_VARIANT; - } - - _cur_house.id = new_house_id; - _cur_house.variant = new_house_variant; - - bool disabled = this->IsHouseDisabled(_cur_house.id); - - if (clicked > 0 ? disabled : this->IsObjectToPlaceSet()) { - ResetObjectToPlace(); // warning, this may cause recursion through OnPlaceObjectAbort - } + SelectHouseIntl(new_house_set, new_house_offset); NWidgetMatrix *matrix = this->GetWidget(WID_HP_HOUSE_SELECT_MATRIX); - matrix->SetCount(this->house_list.NumHousesInHouseSet(new_house_set)); - matrix->SetClicked(clicked > 0 ? new_house_offset : -1); - - this->GetWidget(WID_HP_PREV_VARIANT_SEL)->SetDisplayedPlane(hs->num_variants > 1 ? 0 : SZSP_NONE); - this->GetWidget(WID_HP_NEXT_VARIANT_SEL)->SetDisplayedPlane(hs->num_variants > 1 ? 0 : SZSP_NONE); - this->SetWidgetDisabledState(WID_HP_PREV_VARIANT, _cur_house.variant <= HOUSE_FIRST_VARIANT); - this->SetWidgetDisabledState(WID_HP_NEXT_VARIANT, _cur_house.variant >= HOUSE_FIRST_VARIANT + hs->num_variants - 1); - + matrix->SetCount(this->house_list.NumHousesInHouseSet(this->house_set)); + matrix->SetClicked(this->house_offset); + this->UpdateSelectSize(); this->SetDirty(); + } - if (clicked > 0 && !disabled) { - if (!this->IsObjectToPlaceSet()) SetObjectToPlaceWnd(SPR_CURSOR_TOWN, PAL_NONE, HT_RECT, this); - SetTileSelectSize( - hs->building_flags & BUILDING_2_TILES_X ? 2 : 1, - hs->building_flags & BUILDING_2_TILES_Y ? 2 : 1); + void UpdateSelectSize() + { + uint w = 1, h = 1; + if (_cur_house != INVALID_HOUSE_ID) { + const HouseSpec *hs = HouseSpec::Get(_cur_house); + if (hs->building_flags & BUILDING_2_TILES_X) w++; + if (hs->building_flags & BUILDING_2_TILES_Y) h++; } + SetTileSelectSize(w, h); } public: - HousePickerWindow(WindowDesc *desc, WindowNumber number) : Window(desc), tileselect_zone(HZB_END), tileselect_bad_land(false) + HousePickerWindow(WindowDesc *desc, WindowNumber number) : Window(desc) { this->CreateNestedTree(); /* there is no shade box but we will shade the window if there is no house to show */ @@ -1490,117 +1416,151 @@ public: NWidgetMatrix *matrix = this->GetWidget(WID_HP_HOUSE_SELECT_MATRIX); matrix->SetScrollbar(this->GetScrollbar(WID_HP_HOUSE_SELECT_SCROLL)); this->FinishInitNested(number); + + if (_cur_house != INVALID_HOUSE_ID) matrix->SetClicked(this->house_offset); // set clicked item again to make it visible } - ~HousePickerWindow() - { - DeleteWindowById(WC_SELECT_TOWN, 0); - } - - virtual void OnInit() + virtual void OnInit() override { this->house_list.Build(); + this->RestoreSelectedHouseIndex(); + this->UpdateSelectSize(); - /* restore last house */ - this->SelectOtherHouse(-1, -1, -1, -1); + /* if we have exactly one set of houses and it's not the default one then display it's name in the title bar */ + this->GetWidget(WID_HP_CAPTION)->widget_data = + (this->house_list.NumHouseSets() == 1 && HouseSpec::Get(this->house_list[0])->grf_prop.grffile != NULL) ? + STR_HOUSE_BUILD_CUSTOM_CAPTION : STR_HOUSE_BUILD_CAPTION; /* hide widgets if we have no houses to show */ - this->SetShaded(this->house_list.NumHouseSets() == 0); + this->SetShaded(this->house_list.Length() == 0); - if (this->house_list.NumHouseSets() != 0) { + if (this->house_list.Length() != 0) { /* show the list of house sets if we have at least 2 items to show */ this->GetWidget(WID_HP_HOUSE_SETS_SEL)->SetDisplayedPlane(this->house_list.NumHouseSets() > 1 ? 0 : SZSP_NONE); /* set number of items in the list of house sets */ this->GetWidget(WID_HP_HOUSE_SETS)->widget_data = (this->house_list.NumHouseSets() << MAT_ROW_START) | (1 << MAT_COL_START); /* show the landscape info only in arctic climate (above/below snowline) */ this->GetWidget(WID_HP_HOUSE_LANDSCAPE_SEL)->SetDisplayedPlane(_settings_game.game_creation.landscape == LT_ARCTIC ? 0 : SZSP_NONE); + /* update the matrix of houses */ + NWidgetMatrix *matrix = this->GetWidget(WID_HP_HOUSE_SELECT_MATRIX); + matrix->SetCount(this->house_list.NumHousesInHouseSet(this->house_set)); + matrix->SetClicked(this->house_offset); + SelectHouseIntl(this->house_set, this->house_offset); + } else { + ResetObjectToPlace(); } } virtual void SetStringParameters(int widget) const { - switch (widget) { - case WID_HP_CAPTION: - if (this->house_list.NumHouseSets() == 1 && _loaded_newgrf_features.has_newhouses) { - StringID str = this->house_list.GetNameOfHouseSet(0); - InjectDParam(1); - SetDParam(0, str); - } else { - SetDParam(0, STR_JUST_STRING); - SetDParam(1, STR_HOUSE_BUILD_CAPTION_DEFAULT_TEXT); - } - break; + if (widget == WID_HP_CAPTION) { + if (this->house_list.NumHouseSets() == 1) SetDParamStr(0, this->house_list.GetNameOfHouseSet(0)); + } else if (this->display_house == INVALID_HOUSE_ID) { + switch (widget) { + case WID_HP_CAPTION: + break; - case WID_HP_HOUSE_NAME: - SetDParam(0, GetHouseName(_cur_house.id, INVALID_TILE, _cur_house.variant)); - break; + case WID_HP_HOUSE_ZONES: + for (int i = 0; i < HZB_END; i++) { + SetDParam(2 * i, STR_HOUSE_BUILD_HOUSE_ZONE_DISABLED); + SetDParam(2 * i + 1, i + 1); + } + break; - case WID_HP_HISTORICAL_BUILDING: - SetDParam(0, HouseSpec::Get(_cur_house.id)->extra_flags & BUILDING_IS_HISTORICAL ? STR_HOUSE_BUILD_HISTORICAL_BUILDING : STR_EMPTY); - break; + case WID_HP_HOUSE_YEARS: + SetDParam(0, STR_HOUSE_BUILD_YEARS_BAD_YEAR); + SetDParam(1, 0); + SetDParam(2, STR_HOUSE_BUILD_YEARS_BAD_YEAR); + SetDParam(3, 0); + break; - case WID_HP_HOUSE_POPULATION: - SetDParam(0, HouseSpec::Get(_cur_house.id)->population); - break; + case WID_HP_HOUSE_ACCEPTANCE: + SetDParamStr(0, ""); + break; - case WID_HP_HOUSE_ZONES: { - HouseZones zones = (HouseZones)(HouseSpec::Get(_cur_house.id)->building_availability & HZ_ZONALL); - for (HouseZonesBits i = HZB_BEGIN; i < HZB_END; i++) { - StringID str = STR_HOUSE_BUILD_HOUSE_ZONE_GOOD; - str += !HasBit(zones, i) ? 1 : 0; // bad : good - str += (this->tileselect_zone == i) ? 2 : 0; // highlighted : not highlighted - SetDParam(2 * i, str); - SetDParam(2 * i + 1, i + 1); - } - break; + case WID_HP_HOUSE_SUPPLY: + SetDParam(0, 0); + break; + + default: + SetDParam(0, STR_EMPTY); + break; } + } else { + switch (widget) { + case WID_HP_HOUSE_NAME: + SetDParam(0, GetHouseName(this->display_house)); + break; - case WID_HP_HOUSE_LANDSCAPE: - switch (HouseSpec::Get(_cur_house.id)->building_availability & (HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW)) { - case HZ_SUBARTC_ABOVE: SetDParam(0, this->tileselect_bad_land ? STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE_BAD : STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE_GOOD); break; - case HZ_SUBARTC_BELOW: SetDParam(0, this->tileselect_bad_land ? STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE_BAD : STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE_GOOD); break; - default: SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ABOVE_OR_BELOW_SNOWLINE); break; + case WID_HP_HISTORICAL_BUILDING: + SetDParam(0, HouseSpec::Get(this->display_house)->extra_flags & BUILDING_IS_HISTORICAL ? STR_HOUSE_BUILD_HISTORICAL_BUILDING : STR_EMPTY); + break; + + case WID_HP_HOUSE_POPULATION: + SetDParam(0, HouseSpec::Get(this->display_house)->population); + break; + + case WID_HP_HOUSE_ZONES: { + HouseZones zones = (HouseZones)(HouseSpec::Get(this->display_house)->building_availability & HZ_ZONALL); + for (int i = 0; i < HZB_END; i++) { + /* colour: gold(enabled)/grey(disabled) */ + SetDParam(2 * i, HasBit(zones, HZB_END - i - 1) ? STR_HOUSE_BUILD_HOUSE_ZONE_ENABLED : STR_HOUSE_BUILD_HOUSE_ZONE_DISABLED); + /* digit: 1(center)/2/3/4/5(edge) */ + SetDParam(2 * i + 1, i + 1); + } + break; } - break; - case WID_HP_HOUSE_YEARS: { - const HouseSpec *hs = HouseSpec::Get(_cur_house.id); - SetDParam(0, hs->min_year <= _cur_year ? STR_HOUSE_BUILD_YEAR_GOOD : STR_HOUSE_BUILD_YEAR_BAD); - SetDParam(1, hs->min_year); - SetDParam(2, hs->max_year >= _cur_year ? STR_HOUSE_BUILD_YEAR_GOOD : STR_HOUSE_BUILD_YEAR_BAD); - SetDParam(3, hs->max_year); - break; - } - - case WID_HP_HOUSE_ACCEPTANCE: { - static char buff[DRAW_STRING_BUFFER] = ""; - char *str = buff; - CargoArray cargo; - uint32 dummy = 0; - AddAcceptedHouseCargo(_cur_house.id, INVALID_TILE, cargo, &dummy, _cur_house.variant); - for (uint i = 0; i < NUM_CARGO; i++) { - if (cargo[i] == 0) continue; - /* If the accepted value is less than 8, show it in 1/8:ths */ - SetDParam(0, cargo[i] < 8 ? STR_HOUSE_BUILD_CARGO_VALUE_EIGHTS : STR_HOUSE_BUILD_CARGO_VALUE_JUST_NAME); - SetDParam(1, cargo[i]); - SetDParam(2, CargoSpec::Get(i)->name); - str = GetString(str, str == buff ? STR_HOUSE_BUILD_CARGO_FIRST : STR_HOUSE_BUILD_CARGO_SEPARATED, lastof(buff)); + case WID_HP_HOUSE_LANDSCAPE: { + StringID info = STR_HOUSE_BUILD_LANDSCAPE_ABOVE_OR_BELOW_SNOWLINE; + switch (HouseSpec::Get(this->display_house)->building_availability & (HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW)) { + case HZ_SUBARTC_ABOVE: info = STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE; break; + case HZ_SUBARTC_BELOW: info = STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE; break; + default: break; + } + SetDParam(0, info); + break; } - if (str == buff) GetString(buff, STR_JUST_NOTHING, lastof(buff)); - SetDParamStr(0, buff); - break; - } - case WID_HP_HOUSE_SUPPLY: { - CargoArray cargo; - AddProducedHouseCargo(_cur_house.id, INVALID_TILE, cargo, _cur_house.variant); - uint32 cargo_mask = 0; - for (uint i = 0; i < NUM_CARGO; i++) if (cargo[i] != 0) SetBit(cargo_mask, i); - SetDParam(0, cargo_mask); - break; - } + case WID_HP_HOUSE_YEARS: { + const HouseSpec *hs = HouseSpec::Get(this->display_house); + SetDParam(0, hs->min_year <= _cur_year ? STR_HOUSE_BUILD_YEARS_GOOD_YEAR : STR_HOUSE_BUILD_YEARS_BAD_YEAR); + SetDParam(1, hs->min_year); + SetDParam(2, hs->max_year >= _cur_year ? STR_HOUSE_BUILD_YEARS_GOOD_YEAR : STR_HOUSE_BUILD_YEARS_BAD_YEAR); + SetDParam(3, hs->max_year); + break; + } - default: break; + case WID_HP_HOUSE_ACCEPTANCE: { + static char buff[DRAW_STRING_BUFFER] = ""; + char *str = buff; + CargoArray cargo; + uint32 dummy = 0; + AddAcceptedHouseCargo(this->display_house, INVALID_TILE, cargo, &dummy); + for (uint i = 0; i < NUM_CARGO; i++) { + if (cargo[i] == 0) continue; + /* If the accepted value is less than 8, show it in 1/8:ths */ + SetDParam(0, cargo[i] < 8 ? STR_HOUSE_BUILD_CARGO_VALUE_EIGHTS : STR_HOUSE_BUILD_CARGO_VALUE_JUST_NAME); + SetDParam(1, cargo[i]); + SetDParam(2, CargoSpec::Get(i)->name); + str = GetString(str, str == buff ? STR_HOUSE_BUILD_CARGO_FIRST : STR_HOUSE_BUILD_CARGO_SEPARATED, lastof(buff)); + } + if (str == buff) GetString(buff, STR_JUST_NOTHING, lastof(buff)); + SetDParamStr(0, buff); + break; + } + + case WID_HP_HOUSE_SUPPLY: { + CargoArray cargo; + AddProducedHouseCargo(this->display_house, INVALID_TILE, cargo); + uint32 cargo_mask = 0; + for (uint i = 0; i < NUM_CARGO; i++) if (cargo[i] != 0) SetBit(cargo_mask, i); + SetDParam(0, cargo_mask); + break; + } + + default: break; + } } } @@ -1613,15 +1573,11 @@ public: max_w = max(max_w, GetStringBoundingBox(this->house_list.GetNameOfHouseSet(i)).width); } size->width = max(size->width, max_w + padding.width); - size->height = this->house_list.NumHouseSets() * this->GetLineHeight(); + this->line_height = FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM; + size->height = this->house_list.NumHouseSets() * this->line_height; break; } - case WID_HP_HOUSE_PREVIEW: - size->width = max(size->width, ScaleGUITrad(4 * TILE_PIXELS) + padding.width); - size->height = max(size->height, ScaleGUITrad(140) + padding.height); // this is slightly less then MAX_BUILDING_PIXELS, buildings will be clipped if necessary - break; - case WID_HP_HOUSE_NAME: size->width = 120; // we do not want this window to get too wide, better clip break; @@ -1632,20 +1588,25 @@ public: case WID_HP_HOUSE_POPULATION: SetDParam(0, 0); - SetDParamMaxValue(0, UINT8_MAX); - size->width = max(size->width, GetStringBoundingBox(STR_HOUSE_BUILD_HOUSE_POPULATION).width + padding.width); + /* max popultion is 255 - 3 digits */ + size->width = max(size->width, GetStringBoundingBox(STR_HOUSE_BUILD_HOUSE_POPULATION).width + 3 * GetDigitWidth() + padding.width); break; + case WID_HP_HOUSE_ZONES: { + for (int i = 0; i < HZB_END; i++) { + SetDParam(2 * i, STR_HOUSE_BUILD_HOUSE_ZONE_ENABLED); // colour + SetDParam(2 * i + 1, i + 1); // digit: 1(center)/2/3/4/5(edge) + } + size->width = max(size->width, GetStringBoundingBox(STR_HOUSE_BUILD_HOUSE_ZONES).width + padding.width); + break; + } + case WID_HP_HOUSE_LANDSCAPE: { SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ABOVE_OR_BELOW_SNOWLINE); Dimension dim = GetStringBoundingBox(STR_HOUSE_BUILD_LANDSCAPE); - SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE_GOOD); + SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE); dim = maxdim(dim, GetStringBoundingBox(STR_HOUSE_BUILD_LANDSCAPE)); - SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ONLY_ABOVE_SNOWLINE_BAD); - dim = maxdim(dim, GetStringBoundingBox(STR_HOUSE_BUILD_LANDSCAPE)); - SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE_GOOD); - dim = maxdim(dim, GetStringBoundingBox(STR_HOUSE_BUILD_LANDSCAPE)); - SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE_BAD); + SetDParam(0, STR_HOUSE_BUILD_LANDSCAPE_ONLY_BELOW_SNOWLINE); dim = maxdim(dim, GetStringBoundingBox(STR_HOUSE_BUILD_LANDSCAPE)); dim.width += padding.width; dim.height += padding.height; @@ -1654,17 +1615,12 @@ public: } case WID_HP_HOUSE_YEARS: { - SetDParamMaxValue(1, MAX_YEAR); - SetDParamMaxValue(3, MAX_YEAR); - Dimension dim = { 0, 0 }; - for (uint good_bad_from = 0; good_bad_from < 2; good_bad_from++) { - for (uint good_bad_to = 0; good_bad_to < 2; good_bad_to++) { - SetDParam(0, STR_HOUSE_BUILD_YEAR_GOOD + good_bad_from); - SetDParam(2, STR_HOUSE_BUILD_YEAR_GOOD + good_bad_to); - dim = maxdim(dim, GetStringBoundingBox(STR_HOUSE_BUILD_YEARS)); - } - } - dim.width += padding.width; + SetDParam(0, STR_HOUSE_BUILD_YEARS_GOOD_YEAR); + SetDParam(1, 0); + SetDParam(2, STR_HOUSE_BUILD_YEARS_GOOD_YEAR); + SetDParam(3, 0); + Dimension dim = GetStringBoundingBox(STR_HOUSE_BUILD_YEARS); + dim.width += 14 * GetDigitWidth() + padding.width; // space for about 16 digits (14 + two zeros) should be enough, don't make the window too wide dim.height += padding.height; *size = maxdim(*size, dim); break; @@ -1674,11 +1630,6 @@ public: resize->height = 1; // don't snap to rows of this matrix break; - case WID_HP_HOUSE_SELECT: - size->width = ScaleGUITrad(2 * TILE_PIXELS) + WD_IMGBTN_LEFT + WD_IMGBTN_RIGHT; - size->height = ScaleGUITrad(58) + WD_IMGBTN_TOP + WD_IMGBTN_BOTTOM; - break; - /* these texts can be long, better clip */ case WID_HP_HOUSE_ACCEPTANCE: case WID_HP_HOUSE_SUPPLY: @@ -1694,31 +1645,31 @@ public: switch (GB(widget, 0, 16)) { case WID_HP_HOUSE_SETS: { int y = r.top + WD_MATRIX_TOP; - int sel = this->GetCurrentHouseSet(); for (uint i = 0; i < this->house_list.NumHouseSets(); i++) { - DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y, this->house_list.GetNameOfHouseSet(i), (int)i == sel ? TC_WHITE : TC_BLACK); - y += this->GetLineHeight(); + SetDParamStr(0, this->house_list.GetNameOfHouseSet(i)); + DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y, STR_JUST_RAW_STRING, i == this->house_set ? TC_WHITE : TC_BLACK); + y += this->line_height; } break; } case WID_HP_HOUSE_PREVIEW: - DrawHouseImage(_cur_house.id, r.left, r.top, r.right, r.bottom, HIT_GUI_HOUSE_PREVIEW, _cur_house.variant); + if (this->display_house != INVALID_HOUSE_ID) { + DrawHouseImage(this->display_house, r.left, r.top, r.right, r.bottom); + } break; case WID_HP_HOUSE_SELECT: { - HouseID house = this->house_list.GetHouseAtOffset(this->GetCurrentHouseSet(), GB(widget, 16, 16)); - int lowered = (house == _cur_house.id) ? 1 : 0; + HouseID house = this->house_list.GetHouseAtOffset(this->house_set, GB(widget, 16, 16)); + int lowered = (house == _cur_house) ? 1 : 0; DrawHouseImage(house, - r.left + WD_IMGBTN_LEFT + lowered, r.top + WD_IMGBTN_TOP + lowered, - r.right - WD_IMGBTN_RIGHT + lowered, r.bottom - WD_IMGBTN_BOTTOM + lowered, - HIT_GUI_HOUSE_LIST); - /* grey out outdated houses */ - if (!this->IsHouseDisabled(house)) break; - GfxFillRect( - r.left + WD_BEVEL_LEFT, r.top + WD_BEVEL_TOP, - r.right - WD_BEVEL_LEFT, r.bottom - WD_BEVEL_BOTTOM, - PC_BLACK, FILLRECT_CHECKER); + r.left + WD_MATRIX_LEFT + lowered, r.top + WD_MATRIX_TOP + lowered, + r.right - WD_MATRIX_RIGHT + lowered, r.bottom - WD_MATRIX_BOTTOM + lowered); + const HouseSpec *hs = HouseSpec::Get(house); + /* disabled? */ + if (_cur_year < hs->min_year || _cur_year > hs->max_year) { + GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK, FILLRECT_CHECKER); + } break; } } @@ -1728,104 +1679,30 @@ public: { switch (GB(widget, 0, 16)) { case WID_HP_HOUSE_SETS: { - uint index = (uint)(pt.y - this->GetWidget(widget)->pos_y) / this->GetLineHeight(); - if (index < this->house_list.NumHouseSets()) { - this->SelectOtherHouse(index, -1, -1, -1); - } + uint index = (uint)(pt.y - this->GetWidget(widget)->pos_y) / this->line_height; + if (index < this->house_list.NumHouseSets() && index != this->house_set) this->SelectOtherHouse(index, 0); break; } - case WID_HP_PREV_VARIANT: - this->SelectOtherHouse(-1, -1, _cur_house.variant - 1, -1); - break; - - case WID_HP_NEXT_VARIANT: - this->SelectOtherHouse(-1, -1, _cur_house.variant + 1, -1); - break; - case WID_HP_HOUSE_SELECT: - this->SelectOtherHouse(-1, GB(widget, 16, 16), -1, 1); + this->SelectOtherHouse(this->house_set, GB(widget, 16, 16)); break; } } - virtual void OnPlaceObject(Point pt, TileIndex tile) + virtual void OnPlaceObject(Point pt, TileIndex tile) override { - DeleteWindowById(WC_SELECT_TOWN, 0); - - TownList towns; - CommandCost ret = ListTownsToJoinHouseTo(_cur_house.id, tile, &towns); - if (ret.Failed()) { - ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HOUSE_HERE, ret.GetErrorMessage(), WL_INFO); - return; - } - - CommandContainer cmd = { - tile, - _cur_house.id, // p1 - house type and town index (town not yet set) - InteractiveRandomRange(1 << 8), // p2 - 8 random bits for the house - CMD_BUILD_HOUSE | CMD_MSG(STR_ERROR_CAN_T_BUILD_HOUSE_HERE), - CcFoundTown, - "" - }; - - /* Place the house right away if CTRL is not pressed. */ - if (!_ctrl_pressed) { - SB(cmd.p1, 16, 16, towns[0]); - DoCommandP(&cmd); - return; - } - - /* Check if the place is buildable. */ - ret = CheckFlatLandHouse(_cur_house.id, tile); - if (ret.Failed()) { - ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HOUSE_HERE, ret.GetErrorMessage(), WL_INFO); - return; - } - - /* Show the joiner window. */ - if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); - ShowSelectTownWindow(towns, cmd); + PlaceProc_House(tile); } - virtual void OnPlaceObjectAbort() + virtual void OnPlaceObjectAbort() override { - this->SelectOtherHouse(-1, -1, -1, 0); - this->tileselect_zone = HZB_END; - this->tileselect_bad_land = false; - } - - virtual void OnTick() - { - if (this->IsObjectToPlaceSet() && (_thd.dirty & 4)) { - _thd.dirty &= ~4; - - HouseZonesBits prev_zone = this->tileselect_zone; - bool prev_land = this->tileselect_bad_land; - - this->tileselect_zone = HZB_END; - this->tileselect_bad_land = false; - if (_thd.drawstyle == HT_RECT) { - TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y); - if (tile < MapSize()) { - /* find best town zone */ - const Town *t; - FOR_ALL_TOWNS(t) { - if (CheckHouseDistanceFromTown(t, tile, false).Failed()) continue; - HouseZonesBits zone = GetTownRadiusGroup(t, tile); - if (!IsInsideMM(this->tileselect_zone, zone, HZB_END)) this->tileselect_zone = zone; - } - /* check the snowline */ - if (_settings_game.game_creation.landscape == LT_ARCTIC) { - HouseZones zone = HouseSpec::Get(_cur_house.id)->building_availability & (HZ_SUBARTC_ABOVE | HZ_SUBARTC_BELOW); - this->tileselect_bad_land = HasExactlyOneBit(zone) && ((GetTileMaxZ(tile) > HighestSnowLine()) != (zone == HZ_SUBARTC_ABOVE)); - } - } - } - - if (prev_zone != this->tileselect_zone) this->SetWidgetDirty(WID_HP_HOUSE_ZONES); - if (prev_land != this->tileselect_bad_land) this->SetWidgetDirty(WID_HP_HOUSE_LANDSCAPE); - } + this->house_offset = -1; + _cur_house = INVALID_HOUSE_ID; + NWidgetMatrix *matrix = this->GetWidget(WID_HP_HOUSE_SELECT_MATRIX); + matrix->SetClicked(-1); + this->UpdateSelectSize(); + this->SetDirty(); } }; @@ -1849,30 +1726,9 @@ static const NWidgetPart _nested_house_picker_widgets[] = { SetMatrixDataTip(1, 1, STR_HOUSE_BUILD_HOUSESET_LIST_TOOLTIP), EndContainer(), EndContainer(), - /* HOUSE PICTURE AND PREV/NEXT BUTTONS */ - NWidget(NWID_HORIZONTAL), - NWidget(NWID_VERTICAL), - NWidget(NWID_SPACER), SetFill(1, 1), SetResize(1, 1), - NWidget(NWID_HORIZONTAL), - NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), - NWidget(NWID_SELECTION, COLOUR_DARK_GREEN, WID_HP_PREV_VARIANT_SEL), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_HP_PREV_VARIANT), SetDataTip(SPR_ARROW_LEFT, STR_NULL), - EndContainer(), - EndContainer(), - EndContainer(), - NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_HP_HOUSE_PREVIEW), SetFill(0, 1), SetResize(0, 1), SetPadding(0, 8, 0, 8), - NWidget(NWID_VERTICAL), - NWidget(NWID_SPACER), SetFill(1, 1), SetResize(1, 1), - NWidget(NWID_HORIZONTAL), - NWidget(NWID_SELECTION, COLOUR_DARK_GREEN, WID_HP_NEXT_VARIANT_SEL), - NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_HP_NEXT_VARIANT), SetDataTip(SPR_ARROW_RIGHT, STR_NULL), - EndContainer(), - NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), - EndContainer(), - EndContainer(), - EndContainer(), - /* HOUSE LABEL */ - NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_HP_HOUSE_NAME), SetDataTip(STR_HOUSE_BUILD_HOUSE_NAME, STR_NULL), SetMinimalSize(120, 0), SetPadding(5, 0, 0, 0), + /* HOUSE PICTURE AND LABEL */ + NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_HP_HOUSE_PREVIEW), SetFill(1, 1), SetResize(0, 1), SetMinimalSize(2 * TILE_PIXELS, 142), + NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_HP_HOUSE_NAME), SetDataTip(STR_HOUSE_BUILD_HOUSE_NAME, STR_NULL), SetMinimalSize(120, 0), NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_HP_HISTORICAL_BUILDING), SetDataTip(STR_JUST_STRING, STR_NULL), /* HOUSE INFOS (SHORT TEXTS) */ NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_HP_HOUSE_POPULATION), SetDataTip(STR_HOUSE_BUILD_HOUSE_POPULATION, STR_NULL), SetPadding(5, 0, 0, 0), @@ -1883,8 +1739,8 @@ static const NWidgetPart _nested_house_picker_widgets[] = { NWidget(WWT_TEXT, COLOUR_DARK_GREEN, WID_HP_HOUSE_YEARS), SetDataTip(STR_HOUSE_BUILD_YEARS, STR_NULL), EndContainer(), /* RIGHT: MATRIX OF HOUSES */ - NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_HP_HOUSE_SELECT_MATRIX), SetPIP(0, 2, 0), SetPadding(5, 2, 2, 4), SetScrollbar(WID_HP_HOUSE_SELECT_SCROLL), - NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_HP_HOUSE_SELECT), + NWidget(NWID_MATRIX, COLOUR_DARK_GREEN, WID_HP_HOUSE_SELECT_MATRIX), SetPIP(0, 2, 0), SetPadding(2, 2, 2, 2), SetScrollbar(WID_HP_HOUSE_SELECT_SCROLL), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_HP_HOUSE_SELECT), SetMinimalSize(64, 64), SetFill(0, 0), SetResize(0, 0), SetDataTip(0x0, STR_HOUSE_BUILD_SELECT_HOUSE_TOOLTIP), SetScrollbar(WID_HP_HOUSE_SELECT_SCROLL), EndContainer(), EndContainer(), @@ -1916,12 +1772,11 @@ static WindowDesc _house_picker_desc( /** * Show our house picker. + * @param parent The toolbar window we're associated with. */ void ShowBuildHousePicker() { - if (_game_mode != GM_EDITOR && !Company::IsValidID(_local_company)) return; - HousePickerWindow *w = AllocateWindowDescFront(&_house_picker_desc, 0, true); - if (w != NULL) w->SelectOtherHouse(-1, -1, -1, 1); // push the button + AllocateWindowDescFront(&_house_picker_desc, 0); } @@ -1941,21 +1796,6 @@ struct SelectTownWindow : Window { this->FinishInitNested(); } - /** - * Get list i-th item string. Appropriate string parameters will be set. - * @param i Index of the item. - * @return The string. - */ - StringID GetTownString(uint i) const - { - SetDParam(0, this->towns[i]); - if (CheckHouseDistanceFromTown(Town::Get(this->towns[i]), this->cmd.tile, false).Failed()) { - return STR_SELECT_TOWN_LIST_TOWN_OUTSIDE; - } - SetDParam(1, GetTownRadiusGroup(Town::Get(this->towns[i]), this->cmd.tile) + 1); - return STR_SELECT_TOWN_LIST_TOWN_ZONE; - } - virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { if (widget != WID_ST_PANEL) return; @@ -1963,7 +1803,8 @@ struct SelectTownWindow : Window { /* Determine the widest string */ Dimension d = { 0, 0 }; for (uint i = 0; i < this->towns.Length(); i++) { - d = maxdim(d, GetStringBoundingBox(this->GetTownString(i))); + SetDParam(0, this->towns[i]); + d = maxdim(d, GetStringBoundingBox(STR_SELECT_TOWN_LIST_ITEM)); } resize->height = d.height; @@ -1980,7 +1821,8 @@ struct SelectTownWindow : Window { uint y = r.top + WD_FRAMERECT_TOP; uint end = min(this->vscroll->GetCount(), this->vscroll->GetPosition() + this->vscroll->GetCapacity()); for (uint i = this->vscroll->GetPosition(); i < end; i++) { - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, this->GetTownString(i)); + SetDParam(0, this->towns[i]); + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SELECT_TOWN_LIST_ITEM); y += this->resize.step_height; } } @@ -2034,53 +1876,59 @@ static void ShowSelectTownWindow(const TownList &towns, const CommandContainer & new SelectTownWindow(&_select_town_desc, towns, cmd); } -/** Helper class for sorting a list of towns to join house to. */ -struct TownsToJoinHouseToListSorter { - TileIndex tile; // Tile where the house is about to be placed. - - bool operator () (const TownID &a, const TownID &b) const - { - uint dist_a, dist_b, inner_a, inner_b, outer_a, outer_b; - HouseZonesBits zone_a = TryGetTownRadiusGroup(Town::Get(a), this->tile, &dist_a, &inner_a, &outer_a); - HouseZonesBits zone_b = TryGetTownRadiusGroup(Town::Get(b), this->tile, &dist_b, &inner_b, &outer_b); - - if (zone_a != zone_b) return zone_a != HZB_END && (zone_b == HZB_END || zone_a > zone_b); - - if (zone_a == HZB_END) return dist_a - inner_a < dist_b - inner_b; - - return (uint64)(dist_a - inner_a) * (uint64)(outer_b - inner_b) < - (uint64)(dist_b - inner_b) * (uint64)(outer_a - inner_a); - } -}; - -/** - * Make a list of towns for which a house can be joined to. - * - * @param house Type of the house to join. - * @param tile Tile where the house is about to be placed. - * @param [out] towns Container where sorted towns will be stored. - * @return Success or an error. - */ -static CommandCost ListTownsToJoinHouseTo(HouseID house, TileIndex tile, TownList *towns) +static void PlaceProc_House(TileIndex tile) { - const bool deity = (_game_mode == GM_EDITOR); - StringID error = STR_ERROR_MUST_FOUND_TOWN_FIRST; + if (_town_pool.items == 0) { + ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HOUSE_HERE, STR_ERROR_MUST_FOUND_TOWN_FIRST, WL_INFO); + return; + } + + DeleteWindowById(WC_SELECT_TOWN, 0); + + if (_cur_house == INVALID_HOUSE_ID) return; + + /* build a list of towns to join to */ + TownList towns; + HouseZones house_zones = HouseSpec::Get(_cur_house)->building_availability & HZ_ZONALL; + uint best_dist = UINT_MAX; + int best_zone = (int)HZB_BEGIN - 1; const Town *t; FOR_ALL_TOWNS(t) { - CommandCost ret = CheckHouseDistanceFromTown(t, tile, deity); - if (ret.Failed()) { - if (error == STR_ERROR_MUST_FOUND_TOWN_FIRST) error = ret.GetErrorMessage(); - continue; + HouseZonesBits town_zone = TryGetTownRadiusGroup(t, tile); + if (HasBit(house_zones, town_zone)) { + /* If CTRL is NOT pressed keep only single town on the list, the best one. + * Otherwise add all towns to the list so they can be shown to the player. */ + if (!_ctrl_pressed) { + if ((int)town_zone < best_zone) continue; + uint dist = DistanceSquare(tile, t->xy); + if (dist >= best_dist) continue; + best_dist = dist; + best_zone = town_zone; + towns.Clear(); + } + *towns.Append() = t->index; } - if (!deity && !HasBit(HouseSpec::Get(house)->building_availability, GetTownRadiusGroup(t, tile))) { - error = STR_ERROR_BUILDING_NOT_ALLOWED_IN_THIS_TOWN_ZONE; - continue; - } - *(towns->Append()) = t->index; } - if (towns->Length() == 0) return CommandCost(error); - TownsToJoinHouseToListSorter compare = { tile }; - std::sort(towns->Begin(), towns->End(), compare); - return CommandCost(); + if (towns.Length() == 0) { + ShowErrorMessage(STR_ERROR_CAN_T_BUILD_HOUSE_HERE, STR_ERROR_BUILDING_NOT_ALLOWED_IN_THIS_TOWN_ZONE, WL_INFO); + return; + } + + CommandContainer cmd = { + tile, + _cur_house, // p1 - house type and town index (town not yet set) + InteractiveRandom(), // p2 - random bits for the house + CMD_BUILD_HOUSE | CMD_MSG(STR_ERROR_CAN_T_BUILD_HOUSE_HERE), + CcPlaySound1E, + "" + }; + + if (!_ctrl_pressed) { + SB(cmd.p1, 16, 16, towns[0]); // set the town, it's alone on the list + DoCommandP(&cmd); + } else { + if (!_settings_client.gui.persistent_buildingtools) DeleteWindowById(WC_BUILD_HOUSE, 0); + ShowSelectTownWindow(towns, cmd); + } } diff --git a/src/town_gui.h b/src/town_gui.h new file mode 100644 index 0000000000..3b22d68daf --- /dev/null +++ b/src/town_gui.h @@ -0,0 +1,17 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * 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. + * 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. + * 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 . + */ + +/** @file town_gui.h Types and functions related to the town GUI. */ + +#ifndef TOWN_GUI_H +#define TOWN_GUI_H + +void ShowBuildHousePicker(); + +#endif /* TOWN_GUI_H */ diff --git a/src/widgets/terraform_widget.h b/src/widgets/terraform_widget.h index 7f8a4c4d1b..67c9f09318 100644 --- a/src/widgets/terraform_widget.h +++ b/src/widgets/terraform_widget.h @@ -39,6 +39,7 @@ enum EditorTerraformToolbarWidgets { WID_ETT_PLACE_ROCKS, ///< Place rocks button. WID_ETT_PLACE_DESERT, ///< Place desert button (in tropical climate). WID_ETT_PLACE_OBJECT, ///< Place transmitter button. + WID_ETT_PLACE_HOUSE, ///< Place house button. WID_ETT_BUTTONS_END, ///< End of pushable buttons. WID_ETT_INCREASE_SIZE = WID_ETT_BUTTONS_END, ///< Upwards arrow button to increase terraforming size. WID_ETT_DECREASE_SIZE, ///< Downwards arrow button to decrease terraforming size. diff --git a/src/widgets/town_widget.h b/src/widgets/town_widget.h index 54c38d8903..59412ee17d 100644 --- a/src/widgets/town_widget.h +++ b/src/widgets/town_widget.h @@ -62,13 +62,6 @@ enum TownFoundingWidgets { WID_TF_LAYOUT_RANDOM, ///< Selection for a randomly chosen town layout. }; -/** Widgets of the #SelectTownWindow class. */ -enum SelectTownWidgets { - WID_ST_CAPTION, ///< Caption of the window. - WID_ST_PANEL, ///< Main panel. - WID_ST_SCROLLBAR, ///< Scrollbar of the panel. -}; - /** Widgets of the #HousePickerWindow class. */ enum HousePickerWidgets { WID_HP_CAPTION, @@ -79,10 +72,6 @@ enum HousePickerWidgets { WID_HP_HOUSE_SELECT_SCROLL, ///< Scrollbar associated with the house matrix. WID_HP_HOUSE_SELECT, ///< Panels with house images in the house matrix. WID_HP_HOUSE_PREVIEW, ///< House preview panel. - WID_HP_PREV_VARIANT_SEL, ///< Selection widget to show/hide the prev variant buttons. - WID_HP_PREV_VARIANT, ///< Prev variant button. - WID_HP_NEXT_VARIANT_SEL, ///< Selection widget to show/hide the next variant buttons. - WID_HP_NEXT_VARIANT, ///< Next variant button. WID_HP_HOUSE_NAME, ///< House name display. WID_HP_HISTORICAL_BUILDING, ///< "Historical building" label. WID_HP_HOUSE_POPULATION, ///< House population display. @@ -94,4 +83,11 @@ enum HousePickerWidgets { WID_HP_HOUSE_SUPPLY, ///< Cargo supplied. }; +/** Widgets of the #SelectTownWindow class. */ +enum SelectTownWidgets { + WID_ST_CAPTION, ///< Caption of the window. + WID_ST_PANEL, ///< Main panel. + WID_ST_SCROLLBAR, ///< Scrollbar of the panel. +}; + #endif /* WIDGETS_TOWN_WIDGET_H */