diff --git a/config/creatures.json b/config/creatures.json index 20bd13d92..5e030b6ae 100644 --- a/config/creatures.json +++ b/config/creatures.json @@ -1753,6 +1753,10 @@ "level": 0, "name": [ "Paladin1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 37, 0 ] ], //expert cure "defname": "ZM174NPC.DEF" }, @@ -1761,6 +1765,10 @@ "level": 0, "name": [ "Hierophant1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 27, 0 ] ], //expert shield "defname": "ZM175NPC.DEF" }, @@ -1769,6 +1777,10 @@ "level": 0, "name": [ "TempleGuardian1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 44, 0 ] ], //expert precision "defname": "ZM176NPC.DEF" }, @@ -1777,6 +1789,10 @@ "level": 0, "name": [ "Succubus1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 29, 0 ] ], //expert fire shield "defname": "ZM177NPC.DEF" }, @@ -1785,6 +1801,10 @@ "level": 0, "name": [ "SoulEater1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 39, 0 ] ], //expert animate dead "defname": "ZM178NPC.DEF" }, @@ -1793,6 +1813,10 @@ "level": 0, "name": [ "Brute1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 46, 0 ] ], //expert stone skin "defname": "ZM179NPC.DEF" }, @@ -1801,6 +1825,10 @@ "level": 0, "name": [ "OgreLeader1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 37, 0 ] ], //expert cure "defname": "ZM180NPC.DEF" }, @@ -1809,6 +1837,10 @@ "level": 0, "name": [ "Shaman1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 53, 0 ] ], //expert haste "defname": "ZM181NPC.DEF" }, @@ -1817,6 +1849,10 @@ "level": 0, "name": [ "AstralSpirit1" ], "faction": -1, + "ability_add": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ], + [ "CASTS", 1, 0, 0 ] , + [ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] , + [ "SPELLCASTER", 3, 58, 0 ] ], //expert counterstrike "defname": "ZM182NPC.DEF" }, diff --git a/lib/BattleState.cpp b/lib/BattleState.cpp index 72a33c576..027e76fd7 100644 --- a/lib/BattleState.cpp +++ b/lib/BattleState.cpp @@ -376,6 +376,40 @@ std::vector BattleInfo::getAccessibility( const CStack * stack, bool return ret; } +BattleHex BattleInfo::getClosestTile (bool attackerOwned, int initialPos, std::set & possibilities) const +{ + std::vector sortedTiles (possibilities.begin(), possibilities.end()); //set can't be sorted properly :( + + BattleHex initialHex = BattleHex(initialPos); + auto compareDistance = [initialPos, initialHex](const BattleHex left, const BattleHex right) -> bool + { + return initialHex.getDistance (initialHex, left) < initialHex.getDistance (initialHex, right); + }; + + boost::sort (sortedTiles, compareDistance); //closest tiles at front + + int closestDistance = initialHex.getDistance(initialPos, sortedTiles.front()); //sometimes closest tiles can be many hexes away + + auto notClosest = [closestDistance, initialPos](const BattleHex here) -> bool + { + return closestDistance < here.getDistance (initialPos, here); + }; + + boost::remove_if (sortedTiles, notClosest); //only closest tiles are interesting + + auto compareHorizontal = [attackerOwned](const BattleHex left, const BattleHex right) -> bool + { + if (attackerOwned) + return left.getX() > right.getX(); //find furthest right + else + return left.getX() < right.getX(); //find furthest left + }; + + boost::sort (sortedTiles, compareHorizontal); + + return sortedTiles.front(); +} + int BattleInfo::getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos) const { int pos; @@ -391,23 +425,15 @@ int BattleInfo::getAvaliableHex(TCreature creID, bool attackerOwned, int initial bool ac[GameConstants::BFIELD_SIZE]; std::set occupyable; + bool twoHex = VLC->creh->creatures[creID]->isDoubleWide(); bool flying = VLC->creh->creatures[creID]->isFlying();// vstd::contains(VLC->creh->creatures[creID]->bonuses, Bonus::FLYING); - getAccessibilityMap(ac, twoHex, attackerOwned, true, occupyable, flying); - for (int g = pos; (-1 < g) && (g < GameConstants::BFIELD_SIZE); ) - { - if ((g % GameConstants::BFIELD_WIDTH != 0) && (g % GameConstants::BFIELD_WIDTH != GameConstants::BFIELD_WIDTH-1) && BattleInfo::isAccessible (g, ac, twoHex, attackerOwned, flying, true)) - { - pos = g; - break; - } - if (attackerOwned) - ++g; //probably some more sophisticated range-based iteration is needed - else - --g; - } + getAccessibilityMap (ac, twoHex, attackerOwned, true, occupyable, flying); - return pos; + if (!occupyable.size()) + return -1; //all tiles are covered + + return getClosestTile (attackerOwned, initialPos, occupyable); } bool BattleInfo::isStackBlocked(const CStack * stack) const diff --git a/lib/BattleState.h b/lib/BattleState.h index 85b82cfae..70be0bf3a 100644 --- a/lib/BattleState.h +++ b/lib/BattleState.h @@ -92,6 +92,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode const CStack * getStackT(BattleHex tileID, bool onlyAlive = true) const; void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set & occupyable, bool flying, const CStack* stackToOmmit = NULL) const; //send pointer to at least 187 allocated bytes static bool isAccessible(BattleHex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS + BattleHex getClosestTile (bool attackerOwned, int initialPos, std::set & possibilities) const; //TODO: vector or set? copying one to another is bad int getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects void makeBFS(BattleHex start, bool*accessibility, BattleHex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result std::pair< std::vector, int > getPath(BattleHex start, BattleHex dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned); //returned value: pair; length may be different than number of elements in path since flying vreatures jump between distant hexes diff --git a/lib/CCreatureSet.cpp b/lib/CCreatureSet.cpp index bfe4a2d60..938754810 100644 --- a/lib/CCreatureSet.cpp +++ b/lib/CCreatureSet.cpp @@ -946,6 +946,34 @@ ui8 CStackInstance::bearerType() const return ArtBearer::CREATURE; } +CCommanderInstance::CCommanderInstance() +{ + init(); +} + +CCommanderInstance::CCommanderInstance (TCreature id) +{ + init(); + CStackInstance (id, 1); //init with single unit + name = "Commando"; //TODO - parse them +} + +void CCommanderInstance::init() +{ + alive = true; + experience = 0; + count = 0; + type = NULL; + idRand = -1; + _armyObj = NULL; + setNodeType (Bonus::COMMANDER); +} + +CCommanderInstance::~CCommanderInstance() +{ + +} + CStackBasicDescriptor::CStackBasicDescriptor() { type = NULL; diff --git a/lib/CCreatureSet.h b/lib/CCreatureSet.h index 16dd92c8b..3ccf91282 100644 --- a/lib/CCreatureSet.h +++ b/lib/CCreatureSet.h @@ -28,6 +28,7 @@ public: class DLL_LINKAGE CStackInstance : public CBonusSystemNode, public CStackBasicDescriptor, public CArtifactSet { +protected: const CArmedInstance *_armyObj; //stack must be part of some army, army must be part of some object public: int idRand; //hlp variable used during loading game -> "id" placeholder for randomization @@ -56,7 +57,7 @@ public: si32 magicResistance() const; int getCreatureID() const; //-1 if not available std::string getName() const; //plural or singular - void init(); + virtual void init(); CStackInstance(); CStackInstance(TCreature id, TQuantity count); CStackInstance(const CCreature *cre, TQuantity count); @@ -65,7 +66,7 @@ public: void setType(int creID); void setType(const CCreature *c); void setArmyObj(const CArmedInstance *ArmyObj); - void giveStackExp(expType exp); + virtual void giveStackExp(expType exp); bool valid(bool allowUnrandomized) const; ui8 bearerType() const OVERRIDE; //from CArtifactSet virtual std::string nodeName() const OVERRIDE; //from CBonusSystemnode @@ -74,6 +75,7 @@ public: class DLL_LINKAGE CCommanderInstance : public CStackInstance { +public: //TODO: what if Commander is not a part of creature set? //commander class is determined by its base creature @@ -81,6 +83,10 @@ class DLL_LINKAGE CCommanderInstance : public CStackInstance std::string name; // each Commander has different name std::vector > secondarySkills; //ID, level //std::vector arts; + void init(); + CCommanderInstance(); + CCommanderInstance (TCreature id); + ~CCommanderInstance(); ui64 getPower() const {return 0;}; int getExpRank() const {return 0;}; diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 319a5309b..50b375e49 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -711,6 +711,7 @@ CGHeroInstance::CGHeroInstance() visitedTown = NULL; type = NULL; boat = NULL; + commander = NULL; sex = 0xff; secSkills.push_back(std::make_pair(-1, -1)); speciality.setNodeType(CBonusSystemNode::SPECIALITY); @@ -773,6 +774,11 @@ void CGHeroInstance::initHero() } assert(validTypes()); + if (GameConstants::COMMANDERS) + { + commander = new CCommanderInstance (VLC->creh->factionCommanders[type->heroType / 2]); //hopefully it returns town type + } + hoverName = VLC->generaltexth->allTexts[15]; boost::algorithm::replace_first(hoverName,"%s",name); boost::algorithm::replace_first(hoverName,"%s", type->heroClass->name); diff --git a/lib/CObjectHandler.h b/lib/CObjectHandler.h index 2668a015d..c5edc00e6 100644 --- a/lib/CObjectHandler.h +++ b/lib/CObjectHandler.h @@ -327,7 +327,7 @@ public: h & exp & level & name & biography & portrait & mana & secSkills & movement & sex & inTownGarrison & /*artifacts & artifWorn & */spells & patrol & moveDir; - h & type & speciality; + h & type & speciality & commander; BONUS_TREE_DESERIALIZATION_FIX //visitied town pointer will be restored by map serialization method } diff --git a/lib/HeroBonus.h b/lib/HeroBonus.h index 23f1d06e0..e3fc83d53 100644 --- a/lib/HeroBonus.h +++ b/lib/HeroBonus.h @@ -213,6 +213,7 @@ struct DLL_LINKAGE Bonus CAMPAIGN_BONUS, SPECIAL_WEEK, STACK_EXPERIENCE, + COMMANDER, //TODO: consider using simply STACK_INSTANCE OTHER /*used for defensive stance and default value of spell level limit*/ }; diff --git a/lib/RegisterTypes.h b/lib/RegisterTypes.h index ef4f0a784..2417da146 100644 --- a/lib/RegisterTypes.h +++ b/lib/RegisterTypes.h @@ -85,6 +85,7 @@ void registerTypes1(Serializer &s) s.template registerType(); s.template registerType(); s.template registerType(); + s.template registerType(); s.template registerType(); s.template registerType(); s.template registerType(); diff --git a/lib/map.h b/lib/map.h index 3e0bf4ffe..f9e0fc72c 100644 --- a/lib/map.h +++ b/lib/map.h @@ -25,6 +25,7 @@ class CArtifactInstance; class CGDefInfo; class CGObjectInstance; class CGHeroInstance; +class CCommanderInstance; class CGCreature; class CQuest; class CGTownInstance; @@ -305,6 +306,7 @@ struct DLL_LINKAGE Mapa : public CMapHeader std::vector< ConstTransitivePtr > heroes; std::vector< ConstTransitivePtr > towns; std::vector< ConstTransitivePtr > artInstances; //stores all artifacts + //std::vector< ConstTransitivePtr > commanders; //bmap > monsters; //bmap > heroesToBeat;