#pragma once //#ifndef _MSC_VER #include "CCreatureHandler.h" #include "VCMI_Lib.h" #include "mapping/CMap.h" //#endif #include "HeroBonus.h" #include "CCreatureSet.h" #include "ConstTransitivePtr.h" #include "IGameCallback.h" #include "ResourceSet.h" #include "int3.h" #include "CObjectHandler.h" #include "IGameCallback.h" /* * 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 * */ class CTown; class CCallback; class IGameCallback; class CCreatureSet; class CStack; class CQuest; class CGHeroInstance; class CGTownInstance; class CArmedInstance; class CGDwelling; class CObjectScript; class CGObjectInstance; class CCreature; class CMap; struct StartInfo; struct SDL_Surface; class CMapHandler; class CPathfinder; struct SetObjectProperty; struct MetaString; struct CPack; class CSpell; struct TerrainTile; class CHeroClass; class CCampaign; class CCampaignState; class IModableArt; class CGGarrison; class CGameInfo; struct QuestInfo; class CQuest; class CCampaignScenario; struct EventCondition; class CScenarioTravel; namespace boost { class shared_mutex; } //numbers of creatures are exact numbers if detailed else they are quantity ids (0 - a few, 1 - several and so on; additionally -1 - unknown) struct ArmyDescriptor : public std::map { bool isDetailed; DLL_LINKAGE ArmyDescriptor(const CArmedInstance *army, bool detailed); //not detailed -> quantity ids as count DLL_LINKAGE ArmyDescriptor(); DLL_LINKAGE int getStrength() const; }; struct DLL_LINKAGE InfoAboutArmy { PlayerColor owner; std::string name; ArmyDescriptor army; InfoAboutArmy(); InfoAboutArmy(const CArmedInstance *Army, bool detailed); void initFromArmy(const CArmedInstance *Army, bool detailed); }; struct DLL_LINKAGE InfoAboutHero : public InfoAboutArmy { private: void assign(const InfoAboutHero & iah); public: struct DLL_LINKAGE Details { std::vector primskills; si32 mana, luck, morale; } *details; const CHeroClass *hclass; int portrait; InfoAboutHero(); InfoAboutHero(const InfoAboutHero & iah); InfoAboutHero(const CGHeroInstance *h, bool detailed); ~InfoAboutHero(); InfoAboutHero & operator=(const InfoAboutHero & iah); void initFromHero(const CGHeroInstance *h, bool detailed); }; /// Struct which holds a int information about a town struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy { struct DLL_LINKAGE Details { si32 hallLevel, goldIncome; bool customRes; bool garrisonedHero; } *details; const CTown *tType; si32 built; si32 fortLevel; //0 - none InfoAboutTown(); InfoAboutTown(const CGTownInstance *t, bool detailed); ~InfoAboutTown(); void initFromTown(const CGTownInstance *t, bool detailed); }; // typedef si32 TResourceUnit; // typedef std::vector TResourceVector; // typedef std::set TResourceSet; struct DLL_LINKAGE SThievesGuildInfo { std::vector playerColors; //colors of players that are in-game std::vector< std::vector< PlayerColor > > numOfTowns, numOfHeroes, gold, woodOre, mercSulfCrystGems, obelisks, artifacts, army, income; // [place] -> [colours of players] std::map colorToBestHero; //maps player's color to his best heros' std::map personality; // color to personality // ai tactic std::map bestCreature; // color to ID // id or -1 if not known // template void serialize(Handler &h, const int version) // { // h & playerColors & numOfTowns & numOfHeroes & gold & woodOre & mercSulfCrystGems & obelisks & artifacts & army & income; // h & colorToBestHero & personality & bestCreature; // } }; struct DLL_LINKAGE PlayerState : public CBonusSystemNode { public: PlayerColor color; bool human; //true if human controlled player, false for AI ObjectInstanceID currentSelection; //id of hero/town, 0xffffffff if none TeamID team; TResources resources; std::vector > heroes; std::vector > towns; std::vector > availableHeroes; //heroes available in taverns std::vector > dwellings; //used for town growth std::vector quests; //store info about all received quests bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory EPlayerStatus::EStatus status; boost::optional daysWithoutCastle; PlayerState(); std::string nodeName() const override; template void serialize(Handler &h, const int version) { h & color & human & currentSelection & team & resources & status; h & heroes & towns & availableHeroes & dwellings; h & getBonusList(); //FIXME FIXME FIXME h & status & daysWithoutCastle; h & enteredLosingCheatCode & enteredWinningCheatCode; h & static_cast(*this); } }; struct DLL_LINKAGE TeamState : public CBonusSystemNode { public: TeamID id; //position in gameState::teams std::set players; // members of this team std::vector > > fogOfWarMap; //true - visible, false - hidden TeamState(); template void serialize(Handler &h, const int version) { h & id & players & fogOfWarMap; h & static_cast(*this); } }; 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;}; }; struct DLL_LINKAGE CGPathNode { enum EAccessibility { NOT_SET = 0, ACCESSIBLE = 1, //tile can be entered and passed VISITABLE, //tile can be entered as the last tile in path BLOCKVIS, //visitable from neighbouring tile but not passable BLOCKED //tile can't be entered nor visited }; EAccessibility accessible; ui8 land; ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn ui32 moveRemains; //remaining tiles after hero reaches the tile CGPathNode * theNodeBefore; int3 coord; //coordinates CGPathNode(); bool reachable() const; }; struct DLL_LINKAGE CGPath { std::vector nodes; //just get node by node int3 startPos() const; // start point int3 endPos() const; //destination point void convert(ui8 mode); //mode=0 -> from 'manifest' to 'object' }; struct DLL_LINKAGE CPathsInfo { bool isValid; const CGHeroInstance *hero; int3 hpos; int3 sizes; CGPathNode ***nodes; //[w][h][level] bool getPath(const int3 &dst, CGPath &out); CPathsInfo(const int3 &Sizes); ~CPathsInfo(); }; struct DLL_EXPORT DuelParameters { ETerrainType terType; BFieldType bfieldType; struct DLL_EXPORT SideSettings { struct DLL_EXPORT StackSettings { CreatureID type; si32 count; template void serialize(Handler &h, const int version) { h & type & count; } StackSettings(); StackSettings(CreatureID Type, si32 Count); } stacks[GameConstants::ARMY_SIZE]; si32 heroId; //-1 if none std::vector heroPrimSkills; //may be empty std::map artifacts; std::vector > heroSecSkills; //may be empty; pairs , level [0-3] std::set spells; SideSettings(); template void serialize(Handler &h, const int version) { h & stacks & heroId & heroPrimSkills & artifacts & heroSecSkills & spells; } } sides[2]; std::vector > obstacles; static DuelParameters fromJSON(const std::string &fname); struct CusomCreature { int id; int attack, defense, dmg, HP, speed, shoots; CusomCreature() { id = attack = defense = dmg = HP = speed = shoots = -1; } template void serialize(Handler &h, const int version) { h & id & attack & defense & dmg & HP & speed & shoots; } }; std::vector creatures; DuelParameters(); template void serialize(Handler &h, const int version) { h & terType & bfieldType & sides & obstacles & creatures; } }; class CPathfinder : private CGameInfoCallback { private: bool useSubterraneanGates; bool allowEmbarkAndDisembark; CPathsInfo &out; const CGHeroInstance *hero; const std::vector > > &FoW; std::list mq; //BFS queue -> nodes to be checked int3 curPos; CGPathNode *cp; //current (source) path node -> we took it from the queue CGPathNode *dp; //destination node -> it's a neighbour of cp that we consider const TerrainTile *ct, *dt; //tile info for both nodes ui8 useEmbarkCost; //0 - usual movement; 1 - embark; 2 - disembark int destTopVisObjID; CGPathNode *getNode(const int3 &coord); void initializeGraph(); bool goodForLandSeaTransition(); //checks if current move will be between sea<->land. If so, checks it legality (returns false if movement is not possible) and sets useEmbarkCost CGPathNode::EAccessibility evaluateAccessibility(const TerrainTile *tinfo) const; bool canMoveBetween(const int3 &a, const int3 &b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility) public: CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance *_hero); void calculatePaths(int3 src = int3(-1,-1,-1), int movement = -1); //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 }; struct BattleInfo; class DLL_LINKAGE EVictoryLossCheckResult { public: static EVictoryLossCheckResult victory(std::string toSelf, std::string toOthers); static EVictoryLossCheckResult defeat(std::string toSelf, std::string toOthers); EVictoryLossCheckResult(); bool operator==(EVictoryLossCheckResult const & other) const; bool operator!=(EVictoryLossCheckResult const & other) const; bool victory() const; bool loss() const; EVictoryLossCheckResult invert(); std::string messageToSelf; std::string messageToOthers; template void serialize(Handler &h, const int version) { h & intValue & messageToSelf & messageToOthers; } private: enum EResult { DEFEAT = -1, INGAME = 0, VICTORY= +1 }; EVictoryLossCheckResult(si32 intValue, std::string toSelf, std::string toOthers); si32 intValue; // uses EResult }; DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult); class DLL_LINKAGE CGameState : public CNonConstInfoCallback { public: struct DLL_LINKAGE HeroesPool { std::map > heroesPool; //[subID] - heroes available to buy; nullptr if not available std::map pavailable; // [subid] -> which players can recruit hero (binary flags) CGHeroInstance * pickHeroFor(bool native, PlayerColor player, const CTown *town, std::map > &available, const CHeroClass *bannedClass = nullptr) const; template void serialize(Handler &h, const int version) { h & heroesPool & pavailable; } } hpool; //we have here all heroes available on this map that are not hired CGameState(); virtual ~CGameState(); void init(StartInfo * si); ConstTransitivePtr scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized) PlayerColor currentPlayer; //ID of player currently having turn ConstTransitivePtr curB; //current battle ui32 day; //total number of days in game ConstTransitivePtr map; std::map players; std::map teams; CBonusSystemNode globalEffects; boost::shared_mutex *mx; void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid); void apply(CPack *pack); BFieldType battleGetBattlefieldType(int3 tile) const; UpgradeInfo getUpgradeInfo(const CStackInstance &stack); PlayerRelations::PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2); bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile bool checkForVisitableDir(const int3 & src, const TerrainTile *pom, const int3 & dst) const; //check if src tile is visitable from dst tile void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out, int3 src = int3(-1,-1,-1), int movement = -1); //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 int3 guardingCreaturePosition (int3 pos) const; std::vector guardingCreatures (int3 pos) const; // ----- victory, loss condition checks ----- EVictoryLossCheckResult checkForVictoryAndLoss(PlayerColor player) const; bool checkForVictory(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(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 std::map > unusedHeroesFromPool(); //heroes pool without heroes that are available in taverns BattleInfo * setupBattle(int3 tile, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town); bool isVisible(int3 pos, PlayerColor player); bool isVisible(const CGObjectInstance *obj, boost::optional player); void getNeighbours(const TerrainTile &srct, int3 tile, std::vector &vec, const boost::logic::tribool &onLand, bool limitCoastSailing); int getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints=-1, bool checkLast=true); int getDate(Date::EDateType mode=Date::DAY) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month template void serialize(Handler &h, const int version) { h & scenarioOps & initialOpts & currentPlayer & day & map & players & teams & hpool & globalEffects; BONUS_TREE_DESERIALIZATION_FIX } private: struct CrossoverHeroesList { std::vector heroesFromPreviousScenario, heroesFromAnyPreviousScenarios; void addHeroToBothLists(CGHeroInstance * hero); void removeHeroFromBothLists(CGHeroInstance * hero); }; struct CampaignHeroReplacement { CampaignHeroReplacement(CGHeroInstance * hero, ObjectInstanceID heroPlaceholderId); CGHeroInstance * hero; ObjectInstanceID heroPlaceholderId; }; // ----- initialization ----- void initNewGame(); void initCampaign(); void initDuel(); void checkMapChecksum(); void initGrailPosition(); void initRandomFactionsForPlayers(); void randomizeMapObjects(); void randomizeObject(CGObjectInstance *cur); void initPlayerStates(); void placeCampaignHeroes(); CrossoverHeroesList getCrossoverHeroesFromPreviousScenarios() const; /// returns heroes and placeholders in where heroes will be put std::vector generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes); /// gets prepared and copied hero instances with crossover heroes from prev. scenario and travel options from current scenario void prepareCrossoverHeroes(std::vector & campaignHeroReplacements, const CScenarioTravel & travelOptions) const; void replaceHeroesPlaceholders(const std::vector & campaignHeroReplacements); void placeStartingHeroes(); void placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeId, int3 townPos); void initStartingResources(); void initHeroes(); void giveCampaignBonusToHero(CGHeroInstance * hero); void initFogOfWar(); void initStartingBonus(); void initTowns(); void initMapObjects(); void initVisitingAndGarrisonedHeroes(); // ----- bonus system handling ----- void buildBonusSystemTree(); void attachArmedObjects(); void buildGlobalTeamPlayerTree(); void deserializationFix(); // ---- misc helpers ----- CGHeroInstance * getUsedHero(HeroTypeID hid) const; bool isUsedHero(HeroTypeID hid) const; //looks in heroes and prisons std::set getUnusedAllowedHeroes(bool alsoIncludeNotAllowed = false) const; std::pair pickObject(CGObjectInstance *obj); //chooses type of object to be randomized, returns int pickUnusedHeroTypeRandomly(PlayerColor owner) const; // picks a unused hero type randomly int pickNextHeroType(PlayerColor owner) const; // picks next free hero type of the H3 hero init sequence -> chosen starting hero, then unused hero type randomly friend class CCallback; friend class CClient; friend class IGameCallback; friend class CMapHandler; friend class CGameHandler; }; struct DLL_LINKAGE QuestInfo //universal interface for human and AI { const CQuest * quest; const CGObjectInstance * obj; //related object, most likely Seer Hut int3 tile; QuestInfo(){}; QuestInfo (const CQuest * Quest, const CGObjectInstance * Obj, int3 Tile) : quest (Quest), obj (Obj), tile (Tile){}; //FIXME: assignment operator should return QuestInfo & bool operator= (const QuestInfo &qi) { quest = qi.quest; obj = qi.obj; tile = qi.tile; return true; } bool operator== (const QuestInfo & qi) const { return (quest == qi.quest && obj == qi.obj); } //std::vector > texts //allow additional info for quest log? template void serialize(Handler &h, const int version) { h & quest & obj & tile; } };