/* * CTownHandler.h, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #pragma once #include #include #include "ConstTransitivePtr.h" #include "ResourceSet.h" #include "int3.h" #include "GameConstants.h" #include "IHandlerBase.h" #include "LogicalExpression.h" #include "battle/BattleHex.h" #include "bonuses/Bonus.h" #include "bonuses/BonusList.h" #include "Point.h" #include "rewardable/Info.h" #include "filesystem/ResourcePath.h" VCMI_LIB_NAMESPACE_BEGIN class CLegacyConfigParser; class JsonNode; class CTown; class CFaction; struct BattleHex; class JsonSerializeFormat; /// a typical building encountered in every castle ;] /// this is structure available to both client and server /// contains all mechanics-related data about town structures class DLL_LINKAGE CBuilding { std::string modScope; std::string identifier; public: using TRequired = LogicalExpression; CTown * town; // town this building belongs to TResources resources; TResources produce; TRequired requirements; BuildingID bid; //structure ID BuildingID upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty BuildingSubID::EBuildingSubID subId; /// subtype for special buildings, -1 = the building is not special std::set overrideBids; /// the building which bonuses should be overridden with bonuses of the current building BonusList buildingBonuses; BonusList onVisitBonuses; Rewardable::Info rewardableObjectInfo; ///configurable rewards for special buildings enum EBuildMode { BUILD_NORMAL, // 0 - normal, default BUILD_AUTO, // 1 - auto - building appears when all requirements are built BUILD_SPECIAL, // 2 - special - building can not be built normally BUILD_GRAIL // 3 - grail - building reqires grail to be built } mode; enum ETowerHeight // for lookup towers and some grails { HEIGHT_NO_TOWER = 5, // building has not 'lookout tower' ability HEIGHT_LOW = 10, // low lookout tower, but castle without lookout tower gives radius 5 HEIGHT_AVERAGE = 15, HEIGHT_HIGH = 20, // such tower is in the Tower town HEIGHT_SKYSHIP = std::numeric_limits::max() // grail, open entire map } height; static const std::map MODES; static const std::map TOWER_TYPES; CBuilding() : town(nullptr), mode(BUILD_NORMAL) {}; const BuildingTypeUniqueID getUniqueTypeID() const; std::string getJsonKey() const; std::string getNameTranslated() const; std::string getDescriptionTranslated() const; std::string getBaseTextID() const; std::string getNameTextID() const; std::string getDescriptionTextID() const; //return base of upgrade(s) or this BuildingID getBase() const; // returns how many times build has to be upgraded to become build si32 getDistance(const BuildingID & build) const; STRONG_INLINE bool IsTradeBuilding() const { return bid == BuildingID::MARKETPLACE || subId == BuildingSubID::ARTIFACT_MERCHANT || subId == BuildingSubID::FREELANCERS_GUILD; } STRONG_INLINE bool IsWeekBonus() const { return subId == BuildingSubID::STABLES || subId == BuildingSubID::MANA_VORTEX; } STRONG_INLINE bool IsVisitingBonus() const { return subId == BuildingSubID::ATTACK_VISITING_BONUS || subId == BuildingSubID::DEFENSE_VISITING_BONUS || subId == BuildingSubID::SPELL_POWER_VISITING_BONUS || subId == BuildingSubID::KNOWLEDGE_VISITING_BONUS || subId == BuildingSubID::EXPERIENCE_VISITING_BONUS || subId == BuildingSubID::CUSTOM_VISITING_BONUS; } void addNewBonus(const std::shared_ptr & b, BonusList & bonusList) const; friend class CTownHandler; }; /// This is structure used only by client /// Consists of all gui-related data about town structures /// Should be moved from lib to client struct DLL_LINKAGE CStructure { CBuilding * building; // base building. If null - this structure will be always present on screen CBuilding * buildable; // building that will be used to determine built building and visible cost. Usually same as "building" int3 pos; AnimationPath defName; ImagePath borderName; ImagePath areaName; std::string identifier; bool hiddenUpgrade; // used only if "building" is upgrade, if true - structure on town screen will behave exactly like parent (mouse clicks, hover texts, etc) }; struct DLL_LINKAGE SPuzzleInfo { ui16 number; //type of puzzle si16 x, y; //position ui16 whenUncovered; //determines the sequnce of discovering (the lesser it is the sooner puzzle will be discovered) ImagePath filename; //file with graphic of this puzzle }; class DLL_LINKAGE CFaction : public Faction { friend class CTownHandler; friend class CBuilding; friend class CTown; std::string modScope; std::string identifier; FactionID index = FactionID::NEUTRAL; FactionID getFaction() const override; //This function should not be used public: TerrainId nativeTerrain; EAlignment alignment = EAlignment::NEUTRAL; bool preferUndergroundPlacement = false; bool special = false; /// Boat that will be used by town shipyard (if any) /// and for placing heroes directly on boat (in map editor, water prisons & taverns) BoatId boatType = BoatId::CASTLE; CTown * town = nullptr; //NOTE: can be null ImagePath creatureBg120; ImagePath creatureBg130; std::vector puzzleMap; CFaction() = default; ~CFaction(); int32_t getIndex() const override; int32_t getIconIndex() const override; std::string getJsonKey() const override; void registerIcons(const IconRegistar & cb) const override; FactionID getId() const override; std::string getNameTranslated() const override; std::string getNameTextID() const override; bool hasTown() const override; TerrainId getNativeTerrain() const override; EAlignment getAlignment() const override; BoatId getBoatType() const override; void updateFrom(const JsonNode & data); void serializeJson(JsonSerializeFormat & handler); }; class DLL_LINKAGE CTown { friend class CTownHandler; size_t namesCount = 0; public: CTown(); ~CTown(); std::string getBuildingScope() const; std::set getAllBuildings() const; const CBuilding * getSpecialBuilding(BuildingSubID::EBuildingSubID subID) const; std::string getGreeting(BuildingSubID::EBuildingSubID subID) const; void setGreeting(BuildingSubID::EBuildingSubID subID, const std::string & message) const; //may affect only mutable field BuildingID getBuildingType(BuildingSubID::EBuildingSubID subID) const; std::string getRandomNameTextID(size_t index) const; size_t getRandomNamesCount() const; CFaction * faction; /// level -> list of creatures on this tier // TODO: replace with pointers to CCreature std::vector > creatures; std::map > buildings; std::vector dwellings; //defs for adventure map dwellings for new towns, [0] means tier 1 creatures etc. std::vector dwellingNames; // should be removed at least from configs in favor of auto-detection std::map hordeLvl; //[0] - first horde building creature level; [1] - second horde building (-1 if not present) ui32 mageLevel; //max available mage guild level GameResID primaryRes; ArtifactID warMachine; SpellID moatAbility; // default chance for hero of specific class to appear in tavern, if field "tavern" was not set // resulting chance = sqrt(town.chance * heroClass.chance) ui32 defaultTavernChance; // Client-only data. Should be moved away from lib struct ClientInfo { //icons [fort is present?][build limit reached?] -> index of icon in def files int icons[2][2]; std::string iconSmall[2][2]; /// icon names used during loading std::string iconLarge[2][2]; VideoPath tavernVideo; AudioPath musicTheme; ImagePath townBackground; ImagePath guildBackground; ImagePath guildWindow; AnimationPath buildingsIcons; ImagePath hallBackground; /// vector[row][column] = list of buildings in this slot std::vector< std::vector< std::vector > > hallSlots; /// list of town screen structures. /// NOTE: index in vector is meaningless. Vector used instead of list for a bit faster access std::vector > structures; std::string siegePrefix; std::vector siegePositions; CreatureID siegeShooter; // shooter creature ID std::string towerIconSmall; std::string towerIconLarge; } clientInfo; private: ///generated bonusing buildings messages for all towns of this type. mutable std::map specialMessages; //may be changed by CGTownBuilding::getVisitingBonusGreeting() const }; class DLL_LINKAGE CTownHandler : public CHandlerBase { struct BuildingRequirementsHelper { JsonNode json; CBuilding * building; CTown * town; }; std::map warMachinesToLoad; std::vector requirementsToLoad; std::vector overriddenBidsToLoad; //list of buildings, which bonuses should be overridden. static const TPropagatorPtr & emptyPropagator(); void initializeRequirements(); void initializeOverridden(); void initializeWarMachines(); /// loads CBuilding's into town void loadBuildingRequirements(CBuilding * building, const JsonNode & source, std::vector & bidsToLoad) const; void loadBuilding(CTown * town, const std::string & stringID, const JsonNode & source); void loadBuildings(CTown * town, const JsonNode & source); std::shared_ptr createBonus(CBuilding * build, BonusType type, int val) const; std::shared_ptr createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype) const; std::shared_ptr createBonus(CBuilding * build, BonusType type, int val, BonusSubtypeID subtype, const TPropagatorPtr & prop) const; /// loads CStructure's into town void loadStructure(CTown & town, const std::string & stringID, const JsonNode & source) const; void loadStructures(CTown & town, const JsonNode & source) const; /// loads town hall vector (hallSlots) void loadTownHall(CTown & town, const JsonNode & source) const; void loadSiegeScreen(CTown & town, const JsonNode & source) const; void loadClientData(CTown & town, const JsonNode & source) const; void loadTown(CTown * town, const JsonNode & source); void loadPuzzle(CFaction & faction, const JsonNode & source) const; void loadRandomFaction(); public: template static R getMappedValue(const K key, const R defval, const std::map & map, bool required = true); template static R getMappedValue(const JsonNode & node, const R defval, const std::map & map, bool required = true); CTown * randomTown; CFaction * randomFaction; CTownHandler(); ~CTownHandler(); std::vector loadLegacyData() override; void loadObject(std::string scope, std::string name, const JsonNode & data) override; void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override; void addBonusesForVanilaBuilding(CBuilding * building) const; void loadCustom() override; void afterLoadFinalization() override; std::set getDefaultAllowed() const; std::set getAllowedFactions(bool withTown = true) const; static void loadSpecialBuildingBonuses(const JsonNode & source, BonusList & bonusList, CBuilding * building); protected: const std::vector & getTypeNames() const override; CFaction * loadFromJson(const std::string & scope, const JsonNode & data, const std::string & identifier, size_t index) override; }; VCMI_LIB_NAMESPACE_END