/* * CGameState.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 "bonuses/CBonusSystemNode.h" #include "IGameCallback.h" #include "LoadProgress.h" #include "ConstTransitivePtr.h" namespace boost { class shared_mutex; } VCMI_LIB_NAMESPACE_BEGIN class EVictoryLossCheckResult; class Services; class IMapService; class CMap; struct CPack; class CHeroClass; struct EventCondition; struct CampaignTravel; class CStackInstance; class CGameStateCampaign; class TavernHeroesPool; struct SThievesGuildInfo; template class CApplier; class CBaseForGSApply; struct DLL_LINKAGE RumorState { enum ERumorType : ui8 { TYPE_NONE = 0, TYPE_RAND, TYPE_SPECIAL, TYPE_MAP }; enum ERumorTypeSpecial : ui8 { RUMOR_OBELISKS = 208, RUMOR_ARTIFACTS = 209, RUMOR_ARMY = 210, RUMOR_INCOME = 211, RUMOR_GRAIL = 212 }; ERumorType type; std::map> last; RumorState(){type = TYPE_NONE;}; bool update(int id, int extra); template void serialize(Handler &h, const int version) { h & type; h & last; } }; struct UpgradeInfo { CreatureID oldID; //creature to be upgraded std::vector newID; //possible upgrades std::vector cost; // cost[upgrade_serial] -> set of pairs; cost is for single unit (not entire stack) UpgradeInfo(){oldID = CreatureID::NONE;}; }; class BattleInfo; DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult); class DLL_LINKAGE CGameState : public CNonConstInfoCallback { friend class CGameStateCampaign; public: /// Stores number of times each artifact was placed on map via randomization std::map allocatedArtifacts; /// List of currently ongoing battles std::vector> currentBattles; /// ID that can be allocated to next battle BattleID nextBattleID = BattleID(0); //we have here all heroes available on this map that are not hired std::unique_ptr heroesPool; /// list of players currently making turn. Usually - just one, except for simturns std::set actingPlayers; CGameState(); virtual ~CGameState(); void preInit(Services * services); void init(const IMapService * mapService, StartInfo * si, Load::ProgressAccumulator &, bool allowSavingRandomMap = true); void updateOnLoad(StartInfo * si); ConstTransitivePtr scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized) ui32 day; //total number of days in game ConstTransitivePtr map; std::map players; std::map teams; CBonusSystemNode globalEffects; RumorState rumor; static boost::shared_mutex mutex; void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override; bool giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid); /// picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly HeroTypeID pickNextHeroType(const PlayerColor & owner); void apply(CPack *pack); BattleField battleGetBattlefieldType(int3 tile, CRandomGenerator & rand); void fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const override; PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const override; bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out) override; //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists void calculatePaths(const std::shared_ptr & config) override; int3 guardingCreaturePosition (int3 pos) const override; std::vector guardingCreatures (int3 pos) const; void updateRumor(); /// Gets a artifact ID randomly and removes the selected artifact from this handler. ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags); ArtifactID pickRandomArtifact(CRandomGenerator & rand, std::function accepts); ArtifactID pickRandomArtifact(CRandomGenerator & rand, int flags, std::function accepts); ArtifactID pickRandomArtifact(CRandomGenerator & rand, std::set filtered); /// Returns battle in which selected player is engaged, or nullptr if none. /// Can NOT be used with neutral player, use battle by ID instead const BattleInfo * getBattle(const PlayerColor & player) const; /// Returns battle by its unique identifier, or nullptr if not found const BattleInfo * getBattle(const BattleID & battle) const; BattleInfo * getBattle(const BattleID & battle); // ----- victory, loss condition checks ----- EVictoryLossCheckResult checkForVictoryAndLoss(const PlayerColor & player) const; bool checkForVictory(const PlayerColor & player, const EventCondition & condition) const; //checks if given player is winner PlayerColor checkForStandardWin() const; //returns color of player that accomplished standard victory conditions or 255 (NEUTRAL) if no winner bool checkForStandardLoss(const PlayerColor & player) const; //checks if given player lost the game void obtainPlayersStats(SThievesGuildInfo & tgi, int level); //fills tgi with info about other players that is available at given level of thieves' guild bool isVisible(int3 pos, const std::optional & player) const override; bool isVisible(const CGObjectInstance * obj, const std::optional & player) const override; int getDate(Date mode=Date::DAY) const override; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month // ----- getters, setters ----- /// This RNG should only be used inside GS or CPackForClient-derived applyGs /// If this doesn't work for your code that mean you need a new netpack /// /// Client-side must use CRandomGenerator::getDefault which is not serialized /// /// CGameHandler have it's own getter for CRandomGenerator::getDefault /// Any server-side code outside of GH must use CRandomGenerator::getDefault CRandomGenerator & getRandomGenerator(); template void serialize(Handler &h, const int version) { h & scenarioOps; h & initialOpts; h & actingPlayers; h & day; h & map; h & players; h & teams; h & heroesPool; h & globalEffects; h & rand; h & rumor; h & campaign; h & allocatedArtifacts; BONUS_TREE_DESERIALIZATION_FIX } private: // ----- initialization ----- void preInitAuto(); void initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking); void checkMapChecksum(); void initGlobalBonuses(); void initGrailPosition(); void initRandomFactionsForPlayers(); void randomizeMapObjects(); void initPlayerStates(); void placeStartingHeroes(); void placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos); void removeHeroPlaceholders(); void initDifficulty(); void initHeroes(); void placeHeroesInTowns(); void initFogOfWar(); void initStartingBonus(); void initTowns(); void initMapObjects(); void initVisitingAndGarrisonedHeroes(); void initCampaign(); // ----- bonus system handling ----- void buildBonusSystemTree(); void attachArmedObjects(); void buildGlobalTeamPlayerTree(); void deserializationFix(); // ---- misc helpers ----- CGHeroInstance * getUsedHero(const HeroTypeID & hid) const; bool isUsedHero(const HeroTypeID & hid) const; //looks in heroes and prisons std::set getUnusedAllowedHeroes(bool alsoIncludeNotAllowed = false) const; HeroTypeID pickUnusedHeroTypeRandomly(const PlayerColor & owner); // picks a unused hero type randomly UpgradeInfo fillUpgradeInfo(const CStackInstance &stack) const; // ---- data ----- std::shared_ptr> applier; CRandomGenerator rand; Services * services; /// Ponter to campaign state manager. Nullptr for single scenarios std::unique_ptr campaign; friend class IGameCallback; friend class CMapHandler; friend class CGameHandler; }; VCMI_LIB_NAMESPACE_END