From f306d7bb70db6c187b641e7c63f1324742243921 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 2 Mar 2013 16:55:51 +0000 Subject: [PATCH] some changes towards editing H3 objects via mods. Should be stable, report if not. - removed duplicated json loading code in handlers - simpler and mod-friendly handling of combined artifacts - reorganized CCreature to avoid huge number of fields in one structure --- client/BattleInterface/CBattleAnimations.cpp | 25 +- client/BattleInterface/CBattleInterface.cpp | 2 +- config/heroes.json | 186 ++++---- lib/CArtHandler.cpp | 421 +++++-------------- lib/CArtHandler.h | 32 +- lib/CCreatureHandler.cpp | 257 ++++++----- lib/CCreatureHandler.h | 96 +++-- lib/CGameState.cpp | 2 +- lib/CHeroHandler.cpp | 91 ++-- lib/CHeroHandler.h | 10 +- lib/CModHandler.cpp | 14 +- lib/CObjectHandler.cpp | 2 +- lib/CTownHandler.cpp | 78 ++-- lib/CTownHandler.h | 2 +- lib/GameConstants.h | 58 +-- lib/JsonNode.h | 16 +- lib/Mapping/MapFormatH3M.cpp | 2 +- 17 files changed, 565 insertions(+), 729 deletions(-) diff --git a/client/BattleInterface/CBattleAnimations.cpp b/client/BattleInterface/CBattleAnimations.cpp index 735617676..435a1626b 100644 --- a/client/BattleInterface/CBattleAnimations.cpp +++ b/client/BattleInterface/CBattleAnimations.cpp @@ -137,7 +137,10 @@ bool CDefenceAnimation::init() if(attacker != NULL) { int attackerAnimType = owner->creAnims[attacker->ID]->getType(); - if( attackerAnimType == 11 && attackerAnimType == 12 && attackerAnimType == 13 && owner->creAnims[attacker->ID]->getFrame() < attacker->getCreature()->attackClimaxFrame ) + if( ( attackerAnimType == CCreatureAnim::ATTACK_UP || + attackerAnimType == CCreatureAnim::ATTACK_FRONT || + attackerAnimType == CCreatureAnim::ATTACK_DOWN ) && + owner->creAnims[attacker->ID]->getFrame() < attacker->getCreature()->animation.attackClimaxFrame ) return false; } @@ -738,7 +741,7 @@ bool CShootingAnimation::init() spi.step = 0; spi.frameNum = 0; - spi.spin = shooterInfo->projectileSpin; + spi.spin = shooterInfo->animation.projectileSpin; Point xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner); Point destcoord; @@ -756,20 +759,20 @@ bool CShootingAnimation::init() if (projectileAngle > straightAngle) { //upper shot - spi.x = xycoord.x + projectileOrigin.x + shooterInfo->upperRightMissleOffsetX; - spi.y = xycoord.y + projectileOrigin.y + shooterInfo->upperRightMissleOffsetY; + spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.upperRightMissleOffsetX; + spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.upperRightMissleOffsetY; } else if (projectileAngle < -straightAngle) { //lower shot - spi.x = xycoord.x + projectileOrigin.x + shooterInfo->lowerRightMissleOffsetX; - spi.y = xycoord.y + projectileOrigin.y + shooterInfo->lowerRightMissleOffsetY; + spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.lowerRightMissleOffsetX; + spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.lowerRightMissleOffsetY; } else { //straight shot - spi.x = xycoord.x + projectileOrigin.x + shooterInfo->rightMissleOffsetX; - spi.y = xycoord.y + projectileOrigin.y + shooterInfo->rightMissleOffsetY; + spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.rightMissleOffsetX; + spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.rightMissleOffsetY; } double animSpeed = 23.0 * owner->getAnimSpeed(); // flight speed of projectile @@ -808,8 +811,8 @@ bool CShootingAnimation::init() spi.lastStep = static_cast((spi.catapultInfo->toX - spi.catapultInfo->fromX) / animSpeed); spi.dx = animSpeed; spi.dy = 0; - spi.x = xycoord.x + projectileOrigin.x + shooterInfo->rightMissleOffsetX + 17.; - spi.y = xycoord.y + projectileOrigin.y + shooterInfo->rightMissleOffsetY + 10.; + spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.rightMissleOffsetX + 17.; + spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.rightMissleOffsetY + 10.; // Add explosion anim int xEnd = static_cast(spi.x + spi.lastStep * spi.dx); @@ -830,7 +833,7 @@ bool CShootingAnimation::init() } // Set projectile animation start delay which is specified in frames - spi.animStartDelay = shooterInfo->attackClimaxFrame; + spi.animStartDelay = shooterInfo->animation.attackClimaxFrame; owner->projectiles.push_back(spi); //attack animation diff --git a/client/BattleInterface/CBattleInterface.cpp b/client/BattleInterface/CBattleInterface.cpp index c0ef893d5..db049bc4e 100644 --- a/client/BattleInterface/CBattleInterface.cpp +++ b/client/BattleInterface/CBattleInterface.cpp @@ -300,7 +300,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe else creature = s->getCreature(); - projectile = CDefHandler::giveDef(creature->projectile); + projectile = CDefHandler::giveDef(creature->animation.projectileImageName); if(projectile->ourImages.size() > 2) //add symmetric images { diff --git a/config/heroes.json b/config/heroes.json index 71285cddb..082a3154c 100644 --- a/config/heroes.json +++ b/config/heroes.json @@ -123,7 +123,7 @@ "id": 8, "class" : "cleric", "female": false, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -138,7 +138,7 @@ "id": 9, "class" : "cleric", "female": true, - "spellbook": [ 41 ], + "spellbook": [ "bless" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -153,7 +153,7 @@ "id": 10, "class" : "cleric", "female": false, - "spellbook": [ 45 ], + "spellbook": [ "weakness" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -168,7 +168,7 @@ "id": 11, "class" : "cleric", "female": true, - "spellbook": [ 20 ], + "spellbook": [ "frostRing" ], "skills": [ { "skill" : "wisdom", "level": "advanced" } @@ -182,7 +182,7 @@ "id": 12, "class" : "cleric", "female": false, - "spellbook": [ 42 ], + "spellbook": [ "curse" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -197,7 +197,7 @@ "id": 13, "class" : "cleric", "female": true, - "spellbook": [ 35 ], + "spellbook": [ "dispel" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -211,7 +211,7 @@ { "id": 14, "class" : "cleric", - "spellbook": [ 48 ], + "spellbook": [ "prayer" ], "female": false, "skills": [ @@ -227,7 +227,7 @@ "id": 15, "class" : "cleric", "female": true, - "spellbook": [ 37 ], + "spellbook": [ "cure" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -351,7 +351,7 @@ { "id": 24, "class" : "druid", - "spellbook": [ 55 ], + "spellbook": [ "slayer" ], "female": false, "skills": [ @@ -367,7 +367,7 @@ "id": 25, "class" : "druid", "female": false, - "spellbook": [ 37 ], + "spellbook": [ "cure" ], "skills": [ { "skill" : "wisdom", "level": "advanced" }, @@ -382,7 +382,7 @@ "id": 26, "class" : "druid", "female": false, - "spellbook": [ 42 ], + "spellbook": [ "curse" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -397,7 +397,7 @@ "id": 27, "class" : "druid", "female": true, - "spellbook": [ 0 ], + "spellbook": [ "summonBoat" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -412,7 +412,7 @@ "id": 28, "class" : "druid", "female": false, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -426,7 +426,7 @@ { "id": 29, "class" : "druid", - "spellbook": [ 51 ], + "spellbook": [ "fortune" ], "female": true, "skills": [ @@ -442,7 +442,7 @@ "id": 30, "class" : "druid", "female": false, - "spellbook": [ 16 ], + "spellbook": [ "iceBolt" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -457,7 +457,7 @@ "id": 31, "class" : "druid", "female": false, - "spellbook": [ 30 ], + "spellbook": [ "protectAir" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -472,7 +472,7 @@ "id": 32, "class" : "alchemist", "female": false, - "spellbook": [ 27 ], + "spellbook": [ "shield" ], "skills": [ { "skill" : "scouting", "level": "basic" }, @@ -487,7 +487,7 @@ "id": 33, "class" : "alchemist", "female": false, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "scholar", "level": "advanced" } @@ -500,7 +500,7 @@ { "id": 34, "class" : "alchemist", - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "female": true, "skills": [ @@ -516,7 +516,7 @@ "id": 35, "class" : "alchemist", "female": true, - "spellbook": [ 27 ], + "spellbook": [ "shield" ], "skills": [ { "skill" : "scholar", "level": "basic" }, @@ -531,7 +531,7 @@ "id": 36, "class" : "alchemist", "female": false, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "mysticism", "level": "basic" }, @@ -546,7 +546,7 @@ "id": 37, "class" : "alchemist", "female": false, - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "skills": [ { "skill" : "scholar", "level": "basic" }, @@ -561,7 +561,7 @@ "id": 38, "class" : "alchemist", "female": true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "mysticism", "level": "basic" }, @@ -575,7 +575,7 @@ { "id": 39, "class" : "alchemist", - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "female": true, "skills": [ @@ -591,7 +591,7 @@ "id": 40, "class" : "wizard", "female": false, - "spellbook": [ 60 ], + "spellbook": [ "hypnotize" ], "skills": [ { "skill" : "wisdom", "level": "advanced" } @@ -605,7 +605,7 @@ "id": 41, "class" : "wizard", "female": false, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -620,7 +620,7 @@ "id": 42, "class" : "wizard", "female": true, - "spellbook": [ 35 ], + "spellbook": [ "dispel" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -635,7 +635,7 @@ "id": 43, "class" : "wizard", "female": true, - "spellbook": [ 51 ], + "spellbook": [ "fortune" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -649,7 +649,7 @@ { "id": 44, "class" : "wizard", - "spellbook": [ 27 ], + "spellbook": [ "shield" ], "female": false, "skills": [ @@ -665,7 +665,7 @@ "id": 45, "class" : "wizard", "female": false, - "spellbook": [ 19 ], + "spellbook": [ "chainLightning" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -680,7 +680,7 @@ "id": 46, "class" : "wizard", "female": true, - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -695,7 +695,7 @@ "id": 47, "class" : "wizard", "female": true, - "spellbook": [ 42 ], + "spellbook": [ "curse" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -818,7 +818,7 @@ { "id": 56, "class" : "heretic", - "spellbook": [ 3 ], + "spellbook": [ "viewEarth" ], "female": false, "skills": [ @@ -834,7 +834,7 @@ "id": 57, "class" : "heretic", "female": false, - "spellbook": [ 22 ], + "spellbook": [ "inferno" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -849,7 +849,7 @@ "id": 58, "class" : "heretic", "female": false, - "spellbook": [ 30 ], + "spellbook": [ "protectAir" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -864,7 +864,7 @@ "id": 59, "class" : "heretic", "female": true, - "spellbook": [ 45 ], + "spellbook": [ "weakness" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -879,7 +879,7 @@ "id": 60, "class" : "heretic", "female": false, - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -894,7 +894,7 @@ "id": 61, "class" : "heretic", "female": true, - "spellbook": [ 43 ], + "spellbook": [ "bloodlust" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -909,7 +909,7 @@ "id": 62, "class" : "heretic", "female": false, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -924,7 +924,7 @@ "id": 63, "class" : "heretic", "female": false, - "spellbook": [ 21 ], + "spellbook": [ "fireball" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -939,7 +939,7 @@ "id": 64, "class" : "deathknight", "female": false, - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -954,7 +954,7 @@ "id": 65, "class" : "deathknight", "female": false, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -969,7 +969,7 @@ "id": 66, "class" : "deathknight", "female": false, - "spellbook": [ 54 ], + "spellbook": [ "slow" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -984,7 +984,7 @@ "id": 67, "class" : "deathknight", "female": true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -999,7 +999,7 @@ "id": 68, "class" : "deathknight", "female": true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1014,7 +1014,7 @@ "id": 69, "class" : "deathknight", "female": true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "necromancy", "level": "advanced" } @@ -1028,7 +1028,7 @@ "id": 70, "class" : "deathknight", "female": false, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1043,7 +1043,7 @@ "id": 71, "class" : "deathknight", "female": false, - "spellbook": [ 27 ], + "spellbook": [ "shield" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1058,7 +1058,7 @@ "id": 72, "class" : "necromancer", "female": true, - "spellbook": [ 24 ], + "spellbook": [ "deathRipple" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1073,7 +1073,7 @@ "id": 73, "class" : "necromancer", "female": true, - "spellbook": [ 23 ], + "spellbook": [ "meteorShower" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1088,7 +1088,7 @@ "id": 74, "class" : "necromancer", "female": false, - "spellbook": [ 54 ], + "spellbook": [ "slow" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1103,7 +1103,7 @@ "id": 75, "class" : "necromancer", "female": false, - "spellbook": [ 27 ], + "spellbook": [ "shield" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1118,7 +1118,7 @@ "id": 76, "class" : "necromancer", "female": false, - "spellbook": [ 39 ], + "spellbook": [ "animateDead" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1133,7 +1133,7 @@ "id": 77, "class" : "necromancer", "female": true, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1148,7 +1148,7 @@ "id": 78, "class" : "necromancer", "female": true, - "spellbook": [ 42 ], + "spellbook": [ "curse" ], "skills": [ { "skill" : "necromancy", "level": "advanced" } @@ -1162,7 +1162,7 @@ "id": 79, "class" : "necromancer", "female": false, - "spellbook": [ 30 ], + "spellbook": [ "protectAir" ], "skills": [ { "skill" : "necromancy", "level": "basic" }, @@ -1287,7 +1287,7 @@ { "id": 88, "class" : "overlord", - "spellbook": [ 38 ], + "spellbook": [ "resurrection" ], "female": false, "skills": [ @@ -1303,7 +1303,7 @@ "id": 89, "class" : "overlord", "female": false, - "spellbook": [ 27 ], + "spellbook": [ "shield" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1318,7 +1318,7 @@ "id": 90, "class" : "overlord", "female": false, - "spellbook": [ 43 ], + "spellbook": [ "bloodlust" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1333,7 +1333,7 @@ "id": 91, "class" : "overlord", "female": true, - "spellbook": [ 38 ], + "spellbook": [ "resurrection" ], "skills": [ { "skill" : "wisdom", "level": "advanced" } @@ -1347,7 +1347,7 @@ "id": 92, "class" : "overlord", "female": false, - "spellbook": [ 54 ], + "spellbook": [ "slow" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1362,7 +1362,7 @@ "id": 93, "class" : "overlord", "female": false, - "spellbook": [ 23 ], + "spellbook": [ "meteorShower" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1377,7 +1377,7 @@ "id": 94, "class" : "overlord", "female": true, - "spellbook": [ 30 ], + "spellbook": [ "protectAir" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1392,7 +1392,7 @@ "id": 95, "class" : "overlord", "female": false, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1513,7 +1513,7 @@ { "id": 104, "class" : "battlemage", - "spellbook": [ 43 ], + "spellbook": [ "bloodlust" ], "female": true, "skills": [ @@ -1529,7 +1529,7 @@ "id": 105, "class" : "battlemage", "female": false, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1544,7 +1544,7 @@ "id": 106, "class" : "battlemage", "female": true, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1559,7 +1559,7 @@ "id": 107, "class" : "battlemage", "female": false, - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1574,7 +1574,7 @@ "id": 108, "class" : "battlemage", "female": false, - "spellbook": [ 44 ], + "spellbook": [ "precision" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1588,7 +1588,7 @@ { "id": 109, "class" : "battlemage", - "spellbook": [ 54 ], + "spellbook": [ "slow" ], "female": true, "skills": [ @@ -1604,7 +1604,7 @@ "id": 110, "class" : "battlemage", "female": true, - "spellbook": [ 30 ], + "spellbook": [ "protectAir" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1619,7 +1619,7 @@ "id": 111, "class" : "battlemage", "female": false, - "spellbook": [ 43 ], + "spellbook": [ "bloodlust" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1744,7 +1744,7 @@ { "id": 120, "class" : "witch", - "spellbook": [ 45 ], + "spellbook": [ "weakness" ], "female": true, "skills": [ @@ -1759,7 +1759,7 @@ "id": 121, "class" : "witch", "female": true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1774,7 +1774,7 @@ "id": 122, "class" : "witch", "female": true, - "spellbook": [ 54 ], + "spellbook": [ "slow" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1789,7 +1789,7 @@ "id": 123, "class" : "witch", "female": true, - "spellbook": [ 31 ], + "spellbook": [ "protectFire" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1804,7 +1804,7 @@ "id": 124, "class" : "witch", "female": true, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1819,7 +1819,7 @@ "id": 125, "class" : "witch", "female": true, - "spellbook": [ 27 ], + "spellbook": [ "shield" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1834,7 +1834,7 @@ "id": 126, "class" : "witch", "female": true, - "spellbook": [ 35 ], + "spellbook": [ "dispel" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1849,7 +1849,7 @@ "id": 127, "class" : "witch", "female": true, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -1983,7 +1983,7 @@ { "id": 136, "class" : "elementalist", - "spellbook": [ 13 ], + "spellbook": [ "fireWall" ], "female": true, "skills": [ @@ -1999,7 +1999,7 @@ "id": 137, "class" : "elementalist", "female": true, - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2014,7 +2014,7 @@ "id": 138, "class" : "elementalist", "female": true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2029,7 +2029,7 @@ "id": 139, "class" : "elementalist", "female": true, - "spellbook": [ 46 ], + "spellbook": [ "stoneSkin" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2044,7 +2044,7 @@ "id": 140, "class" : "elementalist", "female": false, - "spellbook": [ 43 ], + "spellbook": [ "bloodlust" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2059,7 +2059,7 @@ "id": 141, "class" : "elementalist", "female": false, - "spellbook": [ 47 ], + "spellbook": [ "disruptingRay" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2074,7 +2074,7 @@ "id": 142, "class" : "elementalist", "female": false, - "spellbook": [ 35 ], + "spellbook": [ "dispel" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2089,7 +2089,7 @@ "id": 143, "class" : "elementalist", "female": false, - "spellbook": [ 54 ], + "spellbook": [ "slow" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2119,7 +2119,7 @@ "class" : "witch", "female": true, "special" : true, - "spellbook": [ 22 ], + "spellbook": [ "inferno" ], "skills": [ { "skill" : "wisdom", "level": "basic" }, @@ -2149,7 +2149,7 @@ "class" : "wizard", "female": false, "special" : true, - "spellbook": [ 53 ], + "spellbook": [ "haste" ], "skills": [ { "skill" : "wisdom", "level": "advanced" } @@ -2197,7 +2197,7 @@ "class" : "deathknight", "female": false, "special" : true, - "spellbook": [ 54 ], + "spellbook": [ "slow" ], "skills": [ { "skill" : "necromancy", "level": "advanced" } @@ -2214,7 +2214,7 @@ "class" : "warlock", "female": true, "special" : true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "estates", "level": "basic" }, @@ -2246,7 +2246,7 @@ "class" : "warlock", "female": true, "special" : true, - "spellbook": [ 15 ], + "spellbook": [ "magicArrow" ], "skills": [ { "skill" : "estates", "level": "basic" }, diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 60a4fc99d..31878eafc 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -78,102 +78,12 @@ bool CArtifact::isBig () const return VLC->arth->isBigArtifact(id); } -// /** -// * Checks whether the artifact fits at a given slot. -// * @param artifWorn A hero's set of worn artifacts. -// */ -// bool CArtifact::fitsAt (const std::map &artifWorn, ui16 slotID) const -// { -// if (!vstd::contains(possibleSlots, slotID)) -// return false; -// -// // Can't put an artifact in a locked slot. -// std::map::const_iterator it = artifWorn.find(slotID); -// if (it != artifWorn.end() && it->second->id == 145) -// return false; -// -// // Check if a combination artifact fits. -// // TODO: Might want a more general algorithm? -// // Assumes that misc & rings fits only in their slots, and others in only one slot and no duplicates. -// if (constituents != NULL) -// { -// std::map tempArtifWorn = artifWorn; -// const ui16 ringSlots[] = {6, 7}; -// const ui16 miscSlots[] = {9, 10, 11, 12, 18}; -// int rings = 0; -// int misc = 0; -// -// VLC->arth->unequipArtifact(tempArtifWorn, slotID); -// -// BOOST_FOREACH(ui32 constituentID, *constituents) -// { -// const CArtifact& constituent = *VLC->arth->artifacts[constituentID]; -// const int slot = constituent.possibleSlots[0]; -// -// if (slot == 6 || slot == 7) -// rings++; -// else if ((slot >= 9 && slot <= 12) || slot == 18) -// misc++; -// else if (tempArtifWorn.find(slot) != tempArtifWorn.end()) -// return false; -// } -// -// // Ensure enough ring slots are free -// for (int i = 0; i < sizeof(ringSlots)/sizeof(*ringSlots); i++) -// { -// if (tempArtifWorn.find(ringSlots[i]) == tempArtifWorn.end() || ringSlots[i] == slotID) -// rings--; -// } -// if (rings > 0) -// return false; -// -// // Ensure enough misc slots are free. -// for (int i = 0; i < sizeof(miscSlots)/sizeof(*miscSlots); i++) -// { -// if (tempArtifWorn.find(miscSlots[i]) == tempArtifWorn.end() || miscSlots[i] == slotID) -// misc--; -// } -// if (misc > 0) -// return false; -// } -// -// return true; -// } - -// bool CArtifact::canBeAssembledTo (const std::map &artifWorn, ui32 artifactID) const -// { -// if (constituentOf == NULL || !vstd::contains(*constituentOf, artifactID)) -// return false; -// -// const CArtifact &artifact = *VLC->arth->artifacts[artifactID]; -// assert(artifact.constituents); -// -// BOOST_FOREACH(ui32 constituentID, *artifact.constituents) -// { -// bool found = false; -// for (std::map::const_iterator it = artifWorn.begin(); it != artifWorn.end(); ++it) -// { -// if (it->second->id == constituentID) -// { -// found = true; -// break; -// } -// } -// if (!found) -// return false; -// } -// -// return true; -// } - CArtifact::CArtifact() { setNodeType(ARTIFACT); possibleSlots[ArtBearer::HERO]; //we want to generate map entry even if it will be empty possibleSlots[ArtBearer::CREATURE]; //we want to generate map entry even if it will be empty possibleSlots[ArtBearer::COMMANDER]; - constituents = NULL; //default pointer to zero - constituentOf = NULL; } CArtifact::~CArtifact() @@ -205,21 +115,6 @@ std::string CArtifact::nodeName() const { return "Artifact: " + Name(); } -// void CArtifact::getParents(TCNodes &out, const CBonusSystemNode *root /*= NULL*/) const -// { -// //combined artifact carries bonuses from its parts -// if(constituents) -// { -// BOOST_FOREACH(ui32 id, *constituents) -// out.insert(VLC->arth->artifacts[id]); -// } -// } - -// void CScroll::Init() -// { -// // addNewBonus (Bonus (Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT, 1, id, spellid, Bonus::INDEPENDENT_MAX)); -// // //boost::algorithm::replace_first(description, "[spell name]", VLC->spellh->spells[spellid].name); -// } void CArtifact::addNewBonus(Bonus *b) { @@ -229,19 +124,6 @@ void CArtifact::addNewBonus(Bonus *b) CBonusSystemNode::addNewBonus(b); } -void CArtifact::setName (std::string desc) -{ - name = desc; -} -void CArtifact::setDescription (std::string desc) -{ - description = desc; -} -void CArtifact::setEventText (std::string desc) -{ - eventText = desc; -} - void CGrowingArtifact::levelUpArtifact (CArtifactInstance * art) { Bonus b; @@ -277,20 +159,20 @@ CArtHandler::CArtHandler() CArtHandler::~CArtHandler() { - for (std::vector< ConstTransitivePtr >::iterator it = artifacts.begin(); it != artifacts.end(); ++it) - { - delete (*it)->constituents; - delete (*it)->constituentOf; - } } void CArtHandler::loadArtifacts(bool onlyTxt) { - std::vector slots; - slots += 17, 16, 15, 14, 13, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0; + if (onlyTxt) + return; // looks to be broken anyway... + + std::vector artSlots; + artSlots += 17, 16, 15, 14, 13, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0; + growingArtifacts += ArtifactID::AXE_OF_SMASHING, ArtifactID::MITHRIL_MAIL, ArtifactID::SWORD_OF_SHARPNESS, ArtifactID::PENDANT_OF_SORCERY, ArtifactID::BOOTS_OF_HASTE, ArtifactID::BOW_OF_SEEKING, ArtifactID::DRAGON_EYE_RING; + static std::map classes = map_list_of('S',CArtifact::ART_SPECIAL)('T',CArtifact::ART_TREASURE)('N',CArtifact::ART_MINOR)('J',CArtifact::ART_MAJOR)('R',CArtifact::ART_RELIC); @@ -313,32 +195,32 @@ void CArtHandler::loadArtifacts(bool onlyTxt) { art = new CArtifact(); } - CArtifact &nart = *art; - nart.id=i; - nart.iconIndex=i; - nart.setName (parser.readString()); - nart.setEventText (events.readString()); + art->id=i; + art->iconIndex=i; + art->name = parser.readString(); + art->eventText = events.readString(); events.endLine(); - nart.price= parser.readNumber(); + art->price= parser.readNumber(); - for(int j=0;jpossibleSlots[ArtBearer::HERO].push_back(ArtifactPosition(artSlots[j])); } - nart.aClass = classes[parser.readString()[0]]; + art->aClass = classes[parser.readString()[0]]; //load description and remove quotation marks - nart.setDescription (parser.readString()); + art->description = parser.readString(); parser.endLine(); - if(onlyTxt) + if(onlyTxt) // FIXME: pointer to art will be lost. Bug? continue; - artifacts.push_back(&nart); + artifacts.push_back(art); } + if (VLC->modh->modules.COMMANDERS) { //TODO: move all artifacts config to separate json file const JsonNode config(ResourceID("config/commanders.json")); @@ -356,65 +238,51 @@ void CArtHandler::loadArtifacts(bool onlyTxt) } } - sortArts(); if(onlyTxt) return; - addBonuses(); -} + JsonNode config(ResourceID("config/artifacts.json")); -void CArtHandler::reverseMapArtifactConstituents() // Populate reverse mappings of combinational artifacts. -{ - BOOST_FOREACH(CArtifact *artifact, artifacts) + BOOST_FOREACH(auto & node, config["artifacts"].Struct()) { - if (artifact->constituents != NULL) - { - BOOST_FOREACH(ui32 constituentID, *artifact->constituents) - { - if (artifacts[constituentID]->constituentOf == NULL) - artifacts[constituentID]->constituentOf = new std::vector(); - artifacts[constituentID]->constituentOf->push_back(artifact->id); - } - } + int numeric = node.second["id"].Float(); + CArtifact * art = artifacts[numeric]; + + loadArtifactJson(art, node.second); + + VLC->modh->identifiers.registerObject ("artifact." + node.first, numeric); } } -void CArtHandler::load(const JsonNode & node) +void CArtHandler::load(std::string objectID, const JsonNode & node) { - BOOST_FOREACH(auto & entry, node.Struct()) - { - if (!entry.second.isNull()) // may happens if mod removed creature by setting json entry to null - { - CArtifact * art = loadArtifact(entry.second); - art->id = ArtifactID(artifacts.size()); + CArtifact * art = loadArtifact(node); + art->id = ArtifactID(artifacts.size()); - artifacts.push_back(art); - tlog5 << "Added artifact: " << entry.first << "\n"; - VLC->modh->identifiers.registerObject (std::string("artifact.") + entry.first, art->id); - } - } + artifacts.push_back(art); + tlog5 << "Added artifact: " << objectID << "\n"; + VLC->modh->identifiers.registerObject ("artifact." + objectID, art->id); } CArtifact * CArtHandler::loadArtifact(const JsonNode & node) { CArtifact * art = new CArtifact; - const JsonNode *value; const JsonNode & text = node["text"]; - art->setName (text["name"].String()); - art->setDescription (text["description"].String()); - art->setEventText (text["event"].String()); + art->name = text["name"].String(); + art->description = text["description"].String(); + art->eventText = text["event"].String(); const JsonNode & graphics = node["graphics"]; art->iconIndex = graphics["iconIndex"].Float(); art->image = graphics["image"].String(); - value = &graphics["large"]; - if (!value->isNull()) - art->large = value->String(); + + if (!graphics["large"].loadTo(art->large)) + art->large = art->image; + art->advMapDef = graphics["map"].String(); art->price = node["value"].Float(); - { auto it = artifactClassMap.find (node["class"].String()); if (it != artifactClassMap.end()) @@ -423,44 +291,14 @@ CArtifact * CArtHandler::loadArtifact(const JsonNode & node) } else { - tlog2 << "Warning! Artifact rarity " << value->String() << " not recognized!"; + tlog2 << "Warning! Artifact rarity " << node["class"].String() << " not recognized!"; art->aClass = CArtifact::ART_SPECIAL; } } - int bearerType = -1; - bool heroArt = false; - + if (!node["slot"].isNull()) //we assume non-hero slots are irrelevant? { - const JsonNode & bearer = node["type"]; - BOOST_FOREACH (const JsonNode & b, bearer.Vector()) - { - auto it = artifactBearerMap.find (b.String()); - if (it != artifactBearerMap.end()) - { - bearerType = it->second; - switch (bearerType) - { - case ArtBearer::HERO: //TODO: allow arts having several possible bearers - heroArt = true; - break; - case ArtBearer::COMMANDER: - makeItCommanderArt (art, false); //do not erase already existing slots - break; - case ArtBearer::CREATURE: - makeItCreatureArt (art, false); - break; - } - } - else - tlog2 << "Warning! Artifact type " << b.String() << " not recognized!"; - } - } - - value = &node["slot"]; - if (!value->isNull() && heroArt) //we assume non-hero slots are irrelevant? - { - std::string slotName = value->String(); + std::string slotName = node["slot"].String(); if (slotName == "MISC") { //unfortunatelly slot ids aare not continuous @@ -480,25 +318,64 @@ CArtifact * CArtHandler::loadArtifact(const JsonNode & node) art->possibleSlots[ArtBearer::HERO].push_back (slot); } else - tlog2 << "Warning! Artifact slot " << value->String() << " not recognized!"; + tlog2 << "Warning! Artifact slot " << node["slot"].String() << " not recognized!"; } } - readComponents (node, art); - - BOOST_FOREACH (const JsonNode &bonus, node["bonuses"].Vector()) - { - auto b = JsonUtils::parseBonus(bonus); - //TODO: bonus->sid = art->id; - art->addNewBonus(b); - } + loadArtifactJson(art, node); return art; } +void CArtHandler::loadArtifactJson(CArtifact * art, const JsonNode & artifact) +{ + BOOST_FOREACH (auto b, artifact["bonuses"].Vector()) + { + auto bonus = JsonUtils::parseBonus (b); + bonus->sid = art->id; + art->addNewBonus (bonus); + } + BOOST_FOREACH (const JsonNode & b, artifact["type"].Vector()) + { + auto it = artifactBearerMap.find (b.String()); + if (it != artifactBearerMap.end()) + { + int bearerType = it->second; + switch (bearerType) + { + case ArtBearer::HERO://TODO: allow arts having several possible bearers + break; + case ArtBearer::COMMANDER: + makeItCommanderArt (art); //original artifacts should have only one bearer type + break; + case ArtBearer::CREATURE: + makeItCreatureArt (art); + break; + } + } + else + tlog2 << "Warning! Artifact type " << b.String() << " not recognized!"; + } + + if (!artifact["components"].isNull()) + { + art->constituents.reset(new std::vector()); + BOOST_FOREACH (auto component, artifact["components"].Vector()) + { + VLC->modh->identifiers.requestIdentifier("artifact." + component.String(), [art](si32 id) + { + // when this code is called both combinational art as well as component are loaded + // so it is safe to access any of them + art->addConstituent(ArtifactID(id)); + VLC->arth->artifacts[id]->constituentOf.push_back(art->id); + }); + } + } +} + void CArtifact::addConstituent (ArtifactID component) { - assert (constituents); + assert (constituents); // not a combinational art constituents->push_back (component); } @@ -534,28 +411,6 @@ CreatureID CArtHandler::machineIDToCreature(ArtifactID id) return CreatureID(id + dif); } -void CArtHandler::sortArts() -{ - //for (int i=0; iaClass) - // { - // case CArtifact::ART_TREASURE: - // treasures.push_back(allowedArtifacts[i]); - // break; - // case CArtifact::ART_MINOR: - // minors.push_back(allowedArtifacts[i]); - // break; - // case CArtifact::ART_MAJOR: - // majors.push_back(allowedArtifacts[i]); - // break; - // case CArtifact::ART_RELIC: - // relics.push_back(allowedArtifacts[i]); - // break; - // } - //} -} - ArtifactID CArtHandler::getRandomArt(int flags) { return getArtSync(ran(), flags, true); @@ -679,84 +534,6 @@ void CArtHandler::makeItCommanderArt( ArtifactID aid, bool onlyCommander /*= tru makeItCommanderArt (a, onlyCommander); } -void CArtHandler::addBonuses() -{ - const JsonNode config(ResourceID("config/artifacts.json")); - BOOST_FOREACH (auto & artifact, config["artifacts"].Struct()) //pair (id, properties) - { - auto ga = artifacts[artifact.second["id"].Float()].get(); - - BOOST_FOREACH (auto b, artifact.second["bonuses"].Vector()) - { - auto bonus = JsonUtils::parseBonus (b); - bonus->sid = ga->id; - ga->addNewBonus (bonus); - } - BOOST_FOREACH (const JsonNode & b, artifact.second["type"].Vector()) //TODO: remove duplicate code - { - auto it = artifactBearerMap.find (b.String()); - if (it != artifactBearerMap.end()) - { - int bearerType = it->second; - switch (bearerType) - { - case ArtBearer::HERO: - break; - case ArtBearer::COMMANDER: - makeItCommanderArt (ga); //original artifacts should have only one bearer type - break; - case ArtBearer::CREATURE: - makeItCreatureArt (ga); - break; - } - } - else - tlog2 << "Warning! Artifact type " << b.String() << " not recognized!"; - } - readComponents (artifact.second, ga); - - VLC->modh->identifiers.registerObject ("artifact." + artifact.first, ga->id); - } -} - -void CArtHandler::readComponents (const JsonNode & node, CArtifact * art) -{ - const JsonNode *value; - value = &node["components"]; - if (!value->isNull()) - { - art->constituents = new std::vector(); - BOOST_FOREACH (auto component, value->Vector()) - { - VLC->modh->identifiers.requestIdentifier(std::string("artifact.") + component.String(), - [art](si32 id) - { - art->addConstituent(ArtifactID(id)); - } - ); - } - } - -} - -void CArtHandler::clear() -{ - BOOST_FOREACH(CArtifact *art, artifacts) - delete art; - artifacts.clear(); - - clearHlpLists(); - -} - -void CArtHandler::clearHlpLists() -{ - treasures.clear(); - minors.clear(); - majors.clear(); - relics.clear(); -} - bool CArtHandler::legalArtifact(ArtifactID id) { auto art = artifacts[id]; @@ -770,7 +547,6 @@ bool CArtHandler::legalArtifact(ArtifactID id) void CArtHandler::initAllowedArtifactsList(const std::vector &allowed) { allowedArtifacts.clear(); - clearHlpLists(); for (ArtifactID i=ArtifactID::SPELLBOOK; iarth->artifacts[aid]); -// } - void CArtifactInstance::setType( CArtifact *Art ) { artType = Art; @@ -966,17 +736,16 @@ void CArtifactInstance::removeFrom(ArtifactLocation al) bool CArtifactInstance::canBeDisassembled() const { - return artType->constituents && artType->constituentOf->size(); + return artType->constituents != nullptr; } std::vector CArtifactInstance::assemblyPossibilities(const CArtifactSet *h) const { std::vector ret; - if(!artType->constituentOf //not a part of combined artifact - || artType->constituents) //combined artifact already: no combining of combined artifacts... for now. + if(artType->constituents) //combined artifact already: no combining of combined artifacts... for now. return ret; - BOOST_FOREACH(ui32 possibleCombinedArt, *artType->constituentOf) + BOOST_FOREACH(ui32 possibleCombinedArt, artType->constituentOf) { const CArtifact * const artifact = VLC->arth->artifacts[possibleCombinedArt]; assert(artifact->constituents); diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index 1bb5ec03e..4b04d4fec 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -15,6 +15,8 @@ * Full text of license available in license.txt file, in main folder * */ + +class CArtHandler; class CDefHandler; class CArtifact; class CGHeroInstance; @@ -53,22 +55,20 @@ public: const std::string &Name() const; //getter const std::string &Description() const; //getter const std::string &EventText() const; + bool isBig () const; - void setName (std::string desc); - void setDescription (std::string desc); - void setEventText (std::string desc); void addConstituent (ArtifactID component); int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other - std::string nodeName() const OVERRIDE; - void addNewBonus(Bonus *b) OVERRIDE; + std::string nodeName() const override; + void addNewBonus(Bonus *b) override; virtual void levelUpArtifact (CArtifactInstance * art){}; ui32 price; bmap > possibleSlots; //Bearer Type => ids of slots where artifact can be placed - std::vector * constituents; // Artifacts IDs a combined artifact consists of, or NULL. - std::vector * constituentOf; // Reverse map of constituents. + std::unique_ptr > constituents; // Artifacts IDs a combined artifact consists of, or NULL. + std::vector constituentOf; // Reverse map of constituents - combined arts that include this art EartClass aClass; ArtifactID id; @@ -82,8 +82,7 @@ public: CArtifact(); ~CArtifact(); - //override - //void getParents(TCNodes &out, const CBonusSystemNode *root = NULL) const; + friend class CArtHandler; }; class DLL_LINKAGE CGrowingArtifact : public CArtifact //for example commander artifacts getting bonuses after battle @@ -201,20 +200,15 @@ public: std::set growingArtifacts; void loadArtifacts(bool onlyTxt); - /// load all artifacts from json structure - void load(const JsonNode & node); + /// load artifact from json structure + void load(std::string objectID, const JsonNode & node); /// load one artifact from json config CArtifact * loadArtifact(const JsonNode & node); - ///read (optional) components of combined artifact - void readComponents (const JsonNode & node, CArtifact * art); - void reverseMapArtifactConstituents (); - void sortArts(); - void addBonuses(); - void clear(); - void clearHlpLists(); + void loadArtifactJson(CArtifact * art, const JsonNode & node); + + void addBonuses(CArtifact *art, const JsonNode &bonusList); - //void restockArtifactList()'' void fillList(std::vector &listToBeFilled, CArtifact::EartClass artifactClass); //fills given empty list with allowed artifacts of gibven class. No side effects boost::optional&> listFromClass(CArtifact::EartClass artifactClass); diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index f00ba2c8a..7004610c9 100644 --- a/lib/CCreatureHandler.cpp +++ b/lib/CCreatureHandler.cpp @@ -208,6 +208,50 @@ static void RemoveAbility(CCreature *cre, const JsonNode &ability) cre->removeBonus(b); } +void CCreatureHandler::loadBonuses(CCreature & ncre, std::string bonuses) +{ + static const std::map abilityMap = + boost::assign::map_list_of + ("FLYING_ARMY", Bonus::FLYING) + ("SHOOTING_ARMY", Bonus::SHOOTER) + ("SIEGE_WEAPON", Bonus::SIEGE_WEAPON) + ("const_free_attack", Bonus::BLOCKS_RETALIATION) + ("IS_UNDEAD", Bonus::UNDEAD) + ("const_no_melee_penalty",Bonus::NO_MELEE_PENALTY) + ("const_jousting",Bonus::JOUSTING) + ("KING_1",Bonus::KING1) + ("KING_2",Bonus::KING2) + ("KING_3",Bonus::KING3) + ("const_no_wall_penalty",Bonus::NO_WALL_PENALTY) + ("CATAPULT",Bonus::CATAPULT) + ("MULTI_HEADED",Bonus::ATTACKS_ALL_ADJACENT) + ("IMMUNE_TO_MIND_SPELLS",Bonus::MIND_IMMUNITY) + ("IMMUNE_TO_FIRE_SPELLS",Bonus::FIRE_IMMUNITY) + ("HAS_EXTENDED_ATTACK",Bonus::TWO_HEX_ATTACK_BREATH); + + auto hasAbility = [&](const std::string name) -> bool + { + return boost::algorithm::find_first(bonuses, name); + }; + BOOST_FOREACH(auto a, abilityMap) + { + if(hasAbility(a.first)) + ncre.addBonus(0, a.second); + } + if(hasAbility("DOUBLE_WIDE")) + ncre.doubleWide = true; + if(hasAbility("const_raises_morale")) + { + ncre.addBonus(+1, Bonus::MORALE);; + ncre.getBonusList().back()->addPropagator(make_shared(CBonusSystemNode::HERO)); + } + if(hasAbility("const_lowers_morale")) + { + ncre.addBonus(-1, Bonus::MORALE);; + ncre.getBonusList().back()->effectRange = Bonus::ONLY_ENEMY_ARMY; + } +} + void CCreatureHandler::loadCreatures() { tlog5 << "\t\tReading ZCRTRAIT.TXT" << std::endl; @@ -255,8 +299,8 @@ void CCreatureHandler::loadCreatures() ncre.ammMin = parser.readNumber(); ncre.ammMax = parser.readNumber(); - ncre.abilityText = parser.readString(); - ncre.abilityRefs = parser.readString(); + std::string abilities = parser.readString(); + loadBonuses(ncre, parser.readString()); { //adding abilities from ZCRTRAIT.TXT static const std::map < std::string,Bonus::BonusType> abilityMap = boost::assign::map_list_of @@ -277,11 +321,11 @@ void CCreatureHandler::loadCreatures() ("IMMUNE_TO_FIRE_SPELLS",Bonus::FIRE_IMMUNITY) ("IMMUNE_TO_FIRE_SPELLS",Bonus::FIRE_IMMUNITY) ("HAS_EXTENDED_ATTACK",Bonus::TWO_HEX_ATTACK_BREATH); - - auto hasAbility = [&ncre](const std::string name) -> bool + + auto hasAbility = [&](const std::string name) -> bool { - return boost::algorithm::find_first(ncre.abilityRefs,name); - }; + return boost::algorithm::find_first(abilities, name); + }; BOOST_FOREACH(auto a, abilityMap) { if(hasAbility(a.first)) @@ -315,18 +359,6 @@ void CCreatureHandler::loadCreatures() int creatureID = node.second["id"].Float(); CCreature *c = creatures[creatureID]; - BOOST_FOREACH(const JsonNode &ability, node.second["ability_remove"].Vector()) - { - RemoveAbility(c, ability); - } - BOOST_FOREACH(const JsonNode &ability, node.second["abilities"].Vector()) - { - if (ability.getType() == JsonNode::DATA_VECTOR) - AddAbility(c, ability.Vector()); - else - c->addNewBonus(JsonUtils::parseBonus(ability)); - } - loadCreatureJson(c, node.second); // Main reference name, e.g. royalGriffin @@ -508,47 +540,41 @@ void CCreatureHandler::loadAnimationInfo() void CCreatureHandler::loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser & parser) { - unit.timeBetweenFidgets = parser.readNumber(); - unit.walkAnimationTime = parser.readNumber(); - unit.attackAnimationTime = parser.readNumber(); - unit.flightAnimationDistance = parser.readNumber(); + unit.animation.timeBetweenFidgets = parser.readNumber(); + unit.animation.walkAnimationTime = parser.readNumber(); + unit.animation.attackAnimationTime = parser.readNumber(); + unit.animation.flightAnimationDistance = parser.readNumber(); /////////////////////// - unit.upperRightMissleOffsetX = parser.readNumber(); - unit.upperRightMissleOffsetY = parser.readNumber(); - unit.rightMissleOffsetX = parser.readNumber(); - unit.rightMissleOffsetY = parser.readNumber(); - unit.lowerRightMissleOffsetX = parser.readNumber(); - unit.lowerRightMissleOffsetY = parser.readNumber(); + unit.animation.upperRightMissleOffsetX = parser.readNumber(); + unit.animation.upperRightMissleOffsetY = parser.readNumber(); + unit.animation.rightMissleOffsetX = parser.readNumber(); + unit.animation.rightMissleOffsetY = parser.readNumber(); + unit.animation.lowerRightMissleOffsetX = parser.readNumber(); + unit.animation.lowerRightMissleOffsetY = parser.readNumber(); /////////////////////// for(int jjj=0; jjj<12; ++jjj) { - unit.missleFrameAngles[jjj] = parser.readNumber(); + unit.animation.missleFrameAngles[jjj] = parser.readNumber(); } - unit.troopCountLocationOffset= parser.readNumber(); - unit.attackClimaxFrame = parser.readNumber(); + unit.animation.troopCountLocationOffset= parser.readNumber(); + unit.animation.attackClimaxFrame = parser.readNumber(); parser.endLine(); } -void CCreatureHandler::load(const JsonNode & node) +void CCreatureHandler::load(std::string creatureID, const JsonNode & node) { - BOOST_FOREACH(auto & entry, node.Struct()) - { - if (!entry.second.isNull()) // may happens if mod removed creature by setting json entry to null - { - CCreature * creature = loadCreature(entry.second); - creature->nameRef = entry.first; - creature->idNumber = CreatureID(creatures.size()); + CCreature * creature = loadCreature(node); + creature->nameRef = creatureID; + creature->idNumber = CreatureID(creatures.size()); - creatures.push_back(creature); - tlog5 << "Added creature: " << entry.first << "\n"; - registerCreature(creature->nameRef,creature->idNumber); - } - } + creatures.push_back(creature); + tlog5 << "Added creature: " << creatureID << "\n"; + registerCreature(creature->nameRef, creature->idNumber); } CCreature * CCreatureHandler::loadCreature(const JsonNode & node) @@ -586,74 +612,31 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node) cre->doubleWide = node["doubleWide"].Bool(); - BOOST_FOREACH (const JsonNode &bonus, node["abilities"].Vector()) - { - auto b = JsonUtils::parseBonus(bonus); - b->source = Bonus::CREATURE_ABILITY; - b->duration = Bonus::PERMANENT; - cre->addNewBonus(b); - } - - BOOST_FOREACH (const JsonNode &exp, node["stackExperience"].Vector()) - { - auto bonus = JsonUtils::parseBonus (exp["bonus"]); - bonus->source = Bonus::STACK_EXPERIENCE; - bonus->duration = Bonus::PERMANENT; - const JsonVector &values = exp["values"].Vector(); - int lowerLimit = 1;//, upperLimit = 255; - if (values[0].getType() == JsonNode::JsonType::DATA_BOOL) - { - BOOST_FOREACH (const JsonNode &val, values) - { - if (val.Bool() == true) - { - bonus->limiter = make_shared(RankRangeLimiter(lowerLimit)); - cre->addNewBonus (new Bonus(*bonus)); //bonuses must be unique objects - break; //TODO: allow bonuses to turn off? - } - ++lowerLimit; - } - } - else - { - int lastVal = 0; - BOOST_FOREACH (const JsonNode &val, values) - { - if (val.Float() != lastVal) - { - bonus->val = val.Float() - lastVal; - bonus->limiter.reset (new RankRangeLimiter(lowerLimit)); - cre->addNewBonus (new Bonus(*bonus)); - } - lastVal = val.Float(); - ++lowerLimit; - } - } - } //graphics + loadStackExperience(cre, node["stackExperience"]); const JsonNode & graphics = node["graphics"]; - cre->timeBetweenFidgets = graphics["timeBetweenFidgets"].Float(); - cre->troopCountLocationOffset = graphics["troopCountLocationOffset"].Float(); - cre->attackClimaxFrame = graphics["attackClimaxFrame"].Float(); + cre->animation.timeBetweenFidgets = graphics["timeBetweenFidgets"].Float(); + cre->animation.troopCountLocationOffset = graphics["troopCountLocationOffset"].Float(); + cre->animation.attackClimaxFrame = graphics["attackClimaxFrame"].Float(); const JsonNode & animationTime = graphics["animationTime"]; - cre->walkAnimationTime = animationTime["walk"].Float(); - cre->attackAnimationTime = animationTime["attack"].Float(); - cre->flightAnimationDistance = animationTime["flight"].Float(); //? + cre->animation.walkAnimationTime = animationTime["walk"].Float(); + cre->animation.attackAnimationTime = animationTime["attack"].Float(); + cre->animation.flightAnimationDistance = animationTime["flight"].Float(); //? const JsonNode & missile = graphics["missile"]; const JsonNode & offsets = missile["offset"]; - cre->upperRightMissleOffsetX = offsets["upperX"].Float(); - cre->upperRightMissleOffsetY = offsets["upperY"].Float(); - cre->rightMissleOffsetX = offsets["middleX"].Float(); - cre->rightMissleOffsetY = offsets["middleY"].Float(); - cre->lowerRightMissleOffsetX = offsets["lowerX"].Float(); - cre->lowerRightMissleOffsetY = offsets["lowerY"].Float(); + cre->animation.upperRightMissleOffsetX = offsets["upperX"].Float(); + cre->animation.upperRightMissleOffsetY = offsets["upperY"].Float(); + cre->animation.rightMissleOffsetX = offsets["middleX"].Float(); + cre->animation.rightMissleOffsetY = offsets["middleY"].Float(); + cre->animation.lowerRightMissleOffsetX = offsets["lowerX"].Float(); + cre->animation.lowerRightMissleOffsetY = offsets["lowerY"].Float(); int i = 0; BOOST_FOREACH (auto & angle, missile["frameAngles"].Vector()) { - cre->missleFrameAngles[i++] = angle.Float(); + cre->animation.missleFrameAngles[i++] = angle.Float(); } cre->advMapDef = graphics["map"].String(); cre->iconIndex = graphics["iconIndex"].Float(); @@ -667,6 +650,24 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c creature->level = config["level"].Float(); creature->animDefName = config["graphics"]["animation"].String(); + BOOST_FOREACH(const JsonNode &ability, config["ability_remove"].Vector()) + { + RemoveAbility(creature, ability); + } + + BOOST_FOREACH(const JsonNode &ability, config["abilities"].Vector()) + { + if (ability.getType() == JsonNode::DATA_VECTOR) + AddAbility(creature, ability.Vector()); // used only for H3 creatures + else + { + auto b = JsonUtils::parseBonus(ability); + b->source = Bonus::CREATURE_ABILITY; + b->duration = Bonus::PERMANENT; + creature->addNewBonus(b); + } + } + VLC->modh->identifiers.requestIdentifier(std::string("faction.") + config["faction"].String(), [=](si32 faction) { creature->faction = faction; @@ -683,12 +684,10 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c if(config["hasDoubleWeek"].Bool()) doubledCreatures.insert(creature->idNumber); - creature->projectile = config["graphics"]["missile"]["projectile"].String(); - creature->projectileSpin = config["graphics"]["missile"]["spinning"].Bool(); + creature->animation.projectileImageName = config["graphics"]["missile"]["projectile"].String(); + creature->animation.projectileSpin = config["graphics"]["missile"]["spinning"].Bool(); creature->special = config["special"].Bool(); - if ( creature->special ) - notUsedMonsters.insert(creature->idNumber); const JsonNode & sounds = config["sound"]; @@ -706,6 +705,46 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c #undef GET_SOUND_VALUE } +void CCreatureHandler::loadStackExperience(CCreature * creature, const JsonNode & input) +{ + BOOST_FOREACH (const JsonNode &exp, input.Vector()) + { + auto bonus = JsonUtils::parseBonus (exp["bonus"]); + bonus->source = Bonus::STACK_EXPERIENCE; + bonus->duration = Bonus::PERMANENT; + const JsonVector &values = exp["values"].Vector(); + int lowerLimit = 1;//, upperLimit = 255; + if (values[0].getType() == JsonNode::JsonType::DATA_BOOL) + { + BOOST_FOREACH (const JsonNode &val, values) + { + if (val.Bool() == true) + { + bonus->limiter = make_shared(RankRangeLimiter(lowerLimit)); + creature->addNewBonus (new Bonus(*bonus)); //bonuses must be unique objects + break; //TODO: allow bonuses to turn off? + } + ++lowerLimit; + } + } + else + { + int lastVal = 0; + BOOST_FOREACH (const JsonNode &val, values) + { + if (val.Float() != lastVal) + { + bonus->val = val.Float() - lastVal; + bonus->limiter.reset (new RankRangeLimiter(lowerLimit)); + creature->addNewBonus (new Bonus(*bonus)); + } + lastVal = val.Float(); + ++lowerLimit; + } + } + } +} + void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigParser & parser) //help function for parsing CREXPBON.txt { bool enable = false; //some bonuses are activated with values 2 or 1 @@ -1022,7 +1061,7 @@ CreatureID CCreatureHandler::pickRandomMonster(const boost::function &ran do { r = vstd::pickRandomElementOf(creatures, randGen)->idNumber; - } while (vstd::contains(VLC->creh->notUsedMonsters,r)); + } while (VLC->creh->creatures[r] && VLC->creh->creatures[r]->special); // find first "not special" creature } else { @@ -1031,9 +1070,9 @@ CreatureID CCreatureHandler::pickRandomMonster(const boost::function &ran BOOST_FOREACH(const CBonusSystemNode *b, creaturesOfLevel[tier].getChildrenNodes()) { assert(b->getNodeType() == CBonusSystemNode::CREATURE); - CreatureID creid = static_cast(b)->idNumber; - if(!vstd::contains(notUsedMonsters, creid)) - allowed.push_back(creid); + const CCreature * crea = dynamic_cast(b); + if(crea && !crea->special) + allowed.push_back(crea->idNumber); } if(!allowed.size()) diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index c82aaa9c4..789cb51ea 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -20,40 +20,58 @@ class CLegacyConfigParser; class CCreatureHandler; class CCreature; -struct CreaturesBattleSounds; class DLL_LINKAGE CCreature : public CBonusSystemNode { public: - std::string namePl, nameSing, nameRef; //name in singular and plural form; and reference name - TResources cost; //cost[res_id] - amount of that resource - std::set upgradeNames; //for reference, they are later transformed info ui32 upgrades - std::set upgrades; // IDs of creatures to which this creature can be upgraded - //damage, hp. etc are handled by Bonuses - ui32 fightValue, AIValue, growth, hordeGrowth; - ui32 ammMin, ammMax; - std::string abilityText; //description of abilities - std::string abilityRefs; //references to abilities, in text format - std::string animDefName; - std::string advMapDef; //for new creatures only - CreatureID idNumber; - si32 iconIndex; // index of icon in files like twcrport - TFaction faction; //-1 = neutral - ui8 level; // 0 - unknown - bool doubleWide; - bool special; // Creature is not available normally (war machines, commanders, etc + std::string nameRef; // reference name, stringID + std::string nameSing;// singular name, e.g. Centaur + std::string namePl; // plural name, e.g. Centaurs - ///animation info - double timeBetweenFidgets, walkAnimationTime, attackAnimationTime, flightAnimationDistance; - int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX, upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY; - double missleFrameAngles[12]; - int troopCountLocationOffset, attackClimaxFrame; - std::string projectile; - bool projectileSpin; //if true, appropriate projectile is spinning during flight - ///end of anim info + std::string abilityText; //description of abilities + + CreatureID idNumber; + TFaction faction; + ui8 level; // 0 - unknown + + //stats that are not handled by bonus system + ui32 fightValue, AIValue, growth, hordeGrowth; + ui32 ammMin, ammMax; // initial size of stack of these creatures on adventure map (if not set in editor) + + bool doubleWide; + bool special; // Creature is not available normally (war machines, commanders, several unused creatures, etc + + TResources cost; //cost[res_id] - amount of that resource required to buy creature from dwelling + std::set upgrades; // IDs of creatures to which this creature can be upgraded + + std::string animDefName; // creature animation used during battles + std::string advMapDef; //for new creatures only, image for adventure map + si32 iconIndex; // index of icon in files like twcrport + + struct CreatureAnimation + { + double timeBetweenFidgets, walkAnimationTime, attackAnimationTime, flightAnimationDistance; + int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX, + upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY; + + double missleFrameAngles[12]; + int troopCountLocationOffset, attackClimaxFrame; + + std::string projectileImageName; + bool projectileSpin; //if true, appropriate projectile is spinning during flight + + template void serialize(Handler &h, const int version) + { + h & timeBetweenFidgets & walkAnimationTime & attackAnimationTime & flightAnimationDistance; + h & upperRightMissleOffsetX & rightMissleOffsetX & lowerRightMissleOffsetX; + h & upperRightMissleOffsetY & rightMissleOffsetY & lowerRightMissleOffsetY; + h & missleFrameAngles & troopCountLocationOffset & attackClimaxFrame; + h & projectileImageName & projectileSpin; + } + } animation; //sound info - struct CreaturesBattleSounds + struct CreatureBattleSounds { std::string attack; std::string defend; @@ -106,21 +124,15 @@ public: & cost & upgrades & fightValue & AIValue & growth & hordeGrowth & ammMin & ammMax & level - & abilityText & abilityRefs & animDefName & advMapDef; + & abilityText & animDefName & advMapDef; h & iconIndex; - h & idNumber & faction - & timeBetweenFidgets & walkAnimationTime & attackAnimationTime & flightAnimationDistance - & upperRightMissleOffsetX & rightMissleOffsetX & lowerRightMissleOffsetX & upperRightMissleOffsetY & rightMissleOffsetY & lowerRightMissleOffsetY - & missleFrameAngles & troopCountLocationOffset & attackClimaxFrame; - h & sounds & projectile & projectileSpin; + h & idNumber & faction & sounds & animation; - h & doubleWide; + h & doubleWide & special; } - CCreature(); - friend class CCreatureHandler; }; class DLL_LINKAGE CCreatureHandler @@ -129,11 +141,11 @@ private: CBonusSystemNode allCreatures; CBonusSystemNode creaturesOfLevel[GameConstants::CREATURES_PER_TOWN + 1];//index 0 is used for creatures of unknown tier or outside <1-7> range + void loadStackExperience(CCreature * creature, const JsonNode &input); void loadCreatureJson(CCreature * creature, const JsonNode & config); public: - std::set notUsedMonsters; std::set doubledCreatures; //they get double week - std::vector > creatures; //creature ID -> creature info + std::vector > creatures; //creature ID -> creature info. //stack exp std::map > stackBonuses; // bonus => name, description @@ -148,10 +160,12 @@ public: /// loading functions + /// adding abilities from ZCRTRAIT.TXT + void loadBonuses(CCreature & creature, std::string bonuses); /// load all creatures from H3 files void loadCreatures(); - /// load all creatures from json structure - void load(const JsonNode & node); + /// load creature from json structure + void load(std::string creatureID, const JsonNode & node); /// load one creature from json config CCreature * loadCreature(const JsonNode & node); /// generates tier-specific bonus tree entries @@ -176,7 +190,7 @@ public: template void serialize(Handler &h, const int version) { //TODO: should be optimized, not all these informations needs to be serialized (same for ccreature) - h & notUsedMonsters & doubledCreatures & creatures; + h & doubledCreatures & creatures; h & stackBonuses & expRanks & maxExpPerBattle & expAfterUpgrade; h & skillLevels & skillRequirements & commanderLevelPremy; h & allCreatures; diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index 1f0e1eea2..7b7abf5b6 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -2478,7 +2478,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level) } if(level >= 4) //obelisks found { - //TODO: obtainPlayersStats - obelisks found + FILL_FIELD(obelisks, CGObelisk::visited[gs->getPlayerTeam(g->second.color)->id]) } if(level >= 5) //artifacts { diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index 9d5a3384e..796953a33 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -131,21 +131,15 @@ void CHeroClassHandler::load() } } -void CHeroClassHandler::load(const JsonNode & classes) +void CHeroClassHandler::load(std::string objectID, const JsonNode & input) { - BOOST_FOREACH(auto & entry, classes.Struct()) - { - if (!entry.second.isNull()) // may happens if mod removed creature by setting json entry to null - { - CHeroClass * heroClass = loadClass(entry.second); - heroClass->identifier = entry.first; - heroClass->id = heroClasses.size(); + CHeroClass * heroClass = loadClass(input); + heroClass->identifier = objectID; + heroClass->id = heroClasses.size(); - heroClasses.push_back(heroClass); - tlog5 << "Added hero class: " << entry.first << "\n"; - VLC->modh->identifiers.registerObject("heroClass." + heroClass->identifier, heroClass->id); - } - } + heroClasses.push_back(heroClass); + tlog5 << "Added hero class: " << objectID << "\n"; + VLC->modh->identifiers.registerObject("heroClass." + heroClass->identifier, heroClass->id); } CHeroClass *CHeroClassHandler::loadClass(const JsonNode & node) @@ -209,20 +203,14 @@ CHeroHandler::CHeroHandler() { } -void CHeroHandler::load(const JsonNode & input) +void CHeroHandler::load(std::string objectID, const JsonNode & input) { - BOOST_FOREACH(auto & entry, input.Struct()) - { - if (!entry.second.isNull()) // may happens if mod removed creature by setting json entry to null - { - CHero * hero = loadHero(entry.second); - hero->ID = heroes.size(); + CHero * hero = loadHero(input); + hero->ID = heroes.size(); - heroes.push_back(hero); - tlog5 << "Added hero: " << entry.first << "\n"; - VLC->modh->identifiers.registerObject("hero." + entry.first, hero->ID); - } - } + heroes.push_back(hero); + tlog5 << "Added hero: " << objectID << "\n"; + VLC->modh->identifiers.registerObject("hero." + objectID, hero->ID); } CHero * CHeroHandler::loadHero(const JsonNode & node) @@ -278,9 +266,23 @@ void CHeroHandler::loadHeroJson(CHero * hero, const JsonNode & node) hero->secSkillsInit.push_back(std::make_pair(skillID, skillLevel)); } + // spellbook is considered present if hero have "spellbook" entry even when this is an empty set (0 spells) + hero->haveSpellBook = node["spellbook"].isNull(); + BOOST_FOREACH(const JsonNode & spell, node["spellbook"].Vector()) { - hero->spells.insert(SpellID(spell.Float())); + if (spell.getType() == JsonNode::DATA_FLOAT) // for compatibility + { + hero->spells.insert(SpellID(spell.Float())); + } + else + { + VLC->modh->identifiers.requestIdentifier("spell." + spell.String(), + [=](si32 spellID) + { + hero->spells.insert(SpellID(spellID)); + }); + } } //deprecated, used only for original spciealties @@ -317,13 +319,14 @@ void CHeroHandler::loadHeroJson(CHero * hero, const JsonNode & node) void CHeroHandler::load() { + VLC->heroh = this; + for (int i = 0; i < GameConstants::SKILL_QUANTITY; ++i) { VLC->modh->identifiers.registerObject("skill." + NSecondarySkill::names[i], i); } classes.load(); loadHeroes(); - loadHeroTexts(); loadObstacles(); loadTerrains(); loadBallistics(); @@ -382,17 +385,26 @@ void CHeroHandler::loadObstacles() void CHeroHandler::loadHeroes() { - VLC->heroh = this; + CLegacyConfigParser specParser("DATA/HEROSPEC.TXT"); + CLegacyConfigParser bioParser("DATA/HEROBIOS.TXT"); CLegacyConfigParser parser("DATA/HOTRAITS.TXT"); parser.endLine(); //ignore header parser.endLine(); + specParser.endLine(); //ignore header + specParser.endLine(); + for (int i=0; iname = parser.readString(); + hero->specName = specParser.readString(); + hero->specTooltip = specParser.readString(); + hero->specDescr = specParser.readString(); + hero->biography = bioParser.readString(); + hero->initialArmy.resize(3); for(int x=0;x<3;x++) { @@ -408,6 +420,8 @@ void CHeroHandler::loadHeroes() }); } parser.endLine(); + specParser.endLine(); + bioParser.endLine(); hero->ID = heroes.size(); hero->imageIndex = hero->ID; @@ -422,27 +436,6 @@ void CHeroHandler::loadHeroes() } } -void CHeroHandler::loadHeroTexts() -{ - CLegacyConfigParser parser("DATA/HEROSPEC.TXT"); - CLegacyConfigParser bioParser("DATA/HEROBIOS.TXT"); - - //skip header - parser.endLine(); - parser.endLine(); - - int i=0; - do - { - CHero * hero = heroes[i++]; - hero->specName = parser.readString(); - hero->specTooltip = parser.readString(); - hero->specDescr = parser.readString(); - hero->biography = bioParser.readString(); - } - while (parser.endLine() && bioParser.endLine() && heroes.size() > i); -} - void CHeroHandler::loadBallistics() { CLegacyConfigParser ballParser("DATA/BALLIST.TXT"); diff --git a/lib/CHeroHandler.h b/lib/CHeroHandler.h index fb0acb0f5..ad981b73a 100644 --- a/lib/CHeroHandler.h +++ b/lib/CHeroHandler.h @@ -68,8 +68,9 @@ public: std::vector spec; std::vector specialty; std::set spells; + bool haveSpellBook; + bool special; // hero is special and won't be placed in game (unless preset on map), e.g. campaign heroes ui8 sex; // default sex: 0=male, 1=female - ui8 special; // hero is special and won't be placed in game (unless preset on map), e.g. campaign heroes /// Localized texts std::string name; //name of hero @@ -86,7 +87,7 @@ public: template void serialize(Handler &h, const int version) { - h & ID & imageIndex & initialArmy & heroClass & secSkillsInit & spec & specialty & spells & sex; + h & ID & imageIndex & initialArmy & heroClass & secSkillsInit & spec & specialty & spells & haveSpellBook & sex & special; h & name & biography & specName & specDescr & specTooltip; h & iconSpecSmall & iconSpecLarge & portraitSmall & portraitLarge; } @@ -157,7 +158,7 @@ public: void load(); /// load any number of classes from json - void load(const JsonNode & classes); + void load(std::string objectID, const JsonNode & classes); /// load one class from json CHeroClass * loadClass(const JsonNode & node); @@ -206,7 +207,7 @@ public: ui64 reqExp(ui32 level) const; //calculates experience required for given level /// Load multiple heroes from json - void load(const JsonNode & heroes); + void load(std::string objectID, const JsonNode & heroes); /// Load single hero from json CHero * loadHero(const JsonNode & node); @@ -215,7 +216,6 @@ public: void load(); void loadHeroes(); - void loadHeroTexts(); void loadExperience(); void loadBallistics(); void loadTerrains(); diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index 5d7153567..6e61da44a 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -281,9 +281,17 @@ std::vector CModHandler::getActiveMods() } template -void handleData(Handler handler, const JsonNode & config) +void handleData(Handler handler, const JsonNode & sourceList) { - handler->load(JsonUtils::assembleFromFiles(config.convertTo >())); + JsonNode config = JsonUtils::assembleFromFiles(sourceList.convertTo >()); + + BOOST_FOREACH(auto & entry, config.Struct()) + { + if (!entry.second.isNull()) // may happens if mod removed object by setting json entry to null + { + handler->load(entry.first, entry.second); + } + } } void CModHandler::loadActiveMods() @@ -346,8 +354,6 @@ void CModHandler::reload() VLC->dobjinfo->gobjs[Obj::ARTIFACT][art->id] = info; } } - - VLC->arth->reverseMapArtifactConstituents(); } { diff --git a/lib/CObjectHandler.cpp b/lib/CObjectHandler.cpp index 204a994e4..3241ed42e 100644 --- a/lib/CObjectHandler.cpp +++ b/lib/CObjectHandler.cpp @@ -740,7 +740,7 @@ void CGHeroInstance::initHero() else //remove placeholder spells -= SpellID::PRESET; - if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && !type->spells.empty()) //no catapult means we haven't read pre-existent set -> use default rules for spellbook + if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook) //no catapult means we haven't read pre-existent set -> use default rules for spellbook putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0)); if(!getArt(ArtifactPosition::MACH4)) diff --git a/lib/CTownHandler.cpp b/lib/CTownHandler.cpp index df9dccfa2..105b54a70 100644 --- a/lib/CTownHandler.cpp +++ b/lib/CTownHandler.cpp @@ -21,6 +21,8 @@ * */ +const int NAMES_PER_TOWN=16; // number of town names per faction in H3 files. Json can define any number + const std::string & CBuilding::Name() const { return name; @@ -218,7 +220,7 @@ void CTownHandler::loadLegacyData(JsonNode & dest) town["name"].String() = typeParser.readString(); - for (int i=0; ifirst + 1; - else - id = node.second["index"].Float(); + if (source["index"].isNull()) + id = factions.rbegin()->first + 1; + else + id = source["index"].Float(); - CFaction & faction = factions[id]; + CFaction & faction = factions[id]; - faction.factionID = id; - faction.name = node.second["name"].String(); + faction.factionID = id; + faction.name = source["name"].String(); - VLC->modh->identifiers.requestIdentifier ("creature." + node.second["commander"].String(), - [=](si32 commanderID) - { - factions[id].commander = CreatureID(commanderID); - }); - - faction.creatureBg120 = node.second["creatureBackground"]["120px"].String(); - faction.creatureBg130 = node.second["creatureBackground"]["130px"].String(); - - faction.nativeTerrain = ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, - node.second["nativeTerrain"].String())); - int alignment = vstd::find_pos(EAlignment::names, node.second["alignment"].String()); - if (alignment == -1) - faction.alignment = EAlignment::NEUTRAL; - else - faction.alignment = static_cast(alignment); - - if (!node.second["town"].isNull()) + VLC->modh->identifiers.requestIdentifier ("creature." + source["commander"].String(), + [=](si32 commanderID) { - towns[id].typeID = id; - loadTown(towns[id], node.second["town"]); - } - if (!node.second["puzzleMap"].isNull()) - loadPuzzle(faction, node.second["puzzleMap"]); + factions[id].commander = CreatureID(commanderID); + }); - tlog5 << "Added faction: " << node.first << "\n"; - VLC->modh->identifiers.registerObject(std::string("faction.") + node.first, faction.factionID); + faction.creatureBg120 = source["creatureBackground"]["120px"].String(); + faction.creatureBg130 = source["creatureBackground"]["130px"].String(); + + faction.nativeTerrain = ETerrainType(vstd::find_pos(GameConstants::TERRAIN_NAMES, + source["nativeTerrain"].String())); + int alignment = vstd::find_pos(EAlignment::names, source["alignment"].String()); + if (alignment == -1) + faction.alignment = EAlignment::NEUTRAL; + else + faction.alignment = static_cast(alignment); + + if (!source["town"].isNull()) + { + towns[id].typeID = id; + loadTown(towns[id], source["town"]); } + if (!source["puzzleMap"].isNull()) + loadPuzzle(faction, source["puzzleMap"]); + + tlog5 << "Added faction: " << townID << "\n"; + VLC->modh->identifiers.registerObject(std::string("faction.") + townID, faction.factionID); } void CTownHandler::load() @@ -587,7 +586,10 @@ void CTownHandler::load() } } } - load(buildingsConf); + BOOST_FOREACH(auto & entry, buildingsConf.Struct()) + { + load(entry.first, entry.second); + } } std::set CTownHandler::getDefaultAllowedFactions() const diff --git a/lib/CTownHandler.h b/lib/CTownHandler.h index fca1057ef..81f953e48 100644 --- a/lib/CTownHandler.h +++ b/lib/CTownHandler.h @@ -221,7 +221,7 @@ public: /// main loading function for mods, accepts merged JSON source and add all entries from it into game /// all entries in JSON should be checked for validness before using this function - void load(const JsonNode & source); + void load(std::string townID, const JsonNode & source); /// "entry point" for loading of OH3 town. /// reads legacy txt's from H3 + vcmi json, merges them diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 6b4556801..a98b19fcb 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -60,36 +60,38 @@ namespace GameConstants const int BFIELD_HEIGHT = 11; const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT; - const int ARMY_SIZE = 7; + const int PUZZLE_MAP_PIECES = 48; - const int CREATURES_COUNT = 197; - const int CRE_LEVELS = 10; - const int F_NUMBER = 9; //factions (town types) quantity const int PLAYER_LIMIT = 8; //player limit per map const int MAX_HEROES_PER_PLAYER = 8; + const int AVAILABLE_HEROES_PER_PLAYER = 2; + + const int UNFLAGGABLE_PLAYER = 254; //254 - neutral objects (pandora, banks) + const int NEUTRAL_PLAYER=255; const int ALL_PLAYERS = 255; //bitfield - const int HEROES_PER_TYPE=8; //amount of heroes of each type - const int SKILL_QUANTITY=28; + + const ui16 BACKPACK_START = 19; + const int CREATURES_PER_TOWN = 7; //without upgrades + const int SPELL_LEVELS = 5; + + const int SPELLBOOK_GOLD_COST = 500; + const int BATTLE_PENALTY_DISTANCE = 10; //if the distance is > than this, then shooting stack has distance penalty + const int ARMY_SIZE = 7; const int SKILL_PER_HERO=8; + + const int CRE_LEVELS = 10; + const int SKILL_QUANTITY=28; + const int PRIMARY_SKILLS=4; + const int TERRAIN_TYPES=10; + const int RESOURCE_QUANTITY=8; + const int HEROES_PER_TYPE=8; //amount of heroes of each type + + // amounts of OH3 objects. Can be changed by mods, should be used only during H3 loading phase + const int F_NUMBER = 9; const int ARTIFACTS_QUANTITY=171; const int HEROES_QUANTITY=156; const int SPELLS_QUANTITY=70; - const int PRIMARY_SKILLS=4; - const int UNFLAGGABLE_PLAYER = 254; //254 - neutral objects (pandora, banks) - const int NEUTRAL_PLAYER=255; - const int NAMES_PER_TOWN=16; - const int CREATURES_PER_TOWN = 7; //without upgrades - const int SPELL_LEVELS = 5; - const int AVAILABLE_HEROES_PER_PLAYER = 2; - const int SPELLBOOK_GOLD_COST = 500; - const int PUZZLE_MAP_PIECES = 48; - - const int BATTLE_PENALTY_DISTANCE = 10; //if the distance is > than this, then shooting stack has distance penalty - - const ui16 BACKPACK_START = 19; - - const int TERRAIN_TYPES=10; - const int RESOURCE_QUANTITY=8; + const int CREATURES_COUNT = 197; } @@ -740,9 +742,9 @@ public: BALLISTA = 4, AMMO_CART = 5, FIRST_AID_TENT = 6, - CENTAUR_AXE = 7, - BLACKSHARD_OF_THE_DEAD_KNIGHT = 8, - CORNUCOPIA = 140, + //CENTAUR_AXE = 7, + //BLACKSHARD_OF_THE_DEAD_KNIGHT = 8, + //CORNUCOPIA = 140, ART_SELECTION = 144, ART_LOCK = 145, AXE_OF_SMASHING = 146, @@ -753,8 +755,8 @@ public: BOOTS_OF_HASTE = 151, BOW_OF_SEEKING = 152, DRAGON_EYE_RING = 153, - HARDENED_SHIELD = 154, - SLAVAS_RING_OF_POWER = 155 + //HARDENED_SHIELD = 154, + //SLAVAS_RING_OF_POWER = 155 }; ArtifactID(EArtifactID _num = NONE) : num(_num) @@ -853,7 +855,7 @@ public: ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID) // Typedef declarations -typedef si8 TFaction; +typedef ui8 TFaction; typedef si64 TExpType; typedef std::pair TDmgRange; typedef si32 TBonusSubtype; diff --git a/lib/JsonNode.h b/lib/JsonNode.h index 5bf5491e1..0027f3437 100644 --- a/lib/JsonNode.h +++ b/lib/JsonNode.h @@ -89,6 +89,12 @@ public: template Type convertTo() const; + /// Similar to convertTo but will assign data only if node is not null + /// Othervice original data will be preserved + /// Returns true if data was assigned + template + bool loadTo(Type & data) const; + //operator [], for structs only - get child node by name JsonNode & operator[](std::string child); const JsonNode & operator[](std::string child) const; @@ -367,4 +373,12 @@ template Type JsonNode::convertTo() const { return JsonDetail::JsonConverter::convert(*this); -} \ No newline at end of file +} + +template +bool JsonNode::loadTo(Type & data) const +{ + if (!isNull()) + data = convertTo(); + return !isNull(); +} diff --git a/lib/Mapping/MapFormatH3M.cpp b/lib/Mapping/MapFormatH3M.cpp index 34f4fd2af..251f50a34 100644 --- a/lib/Mapping/MapFormatH3M.cpp +++ b/lib/Mapping/MapFormatH3M.cpp @@ -683,7 +683,7 @@ CArtifactInstance * CMapLoaderH3M::createArtifact(int aid, int spellID /*= -1*/) map->addNewArtifactInstance(a); //TODO make it nicer - if(a->artType && a->artType->constituents) + if(a->artType && a->artType->constituents != nullptr) { CCombinedArtifactInstance * comb = dynamic_cast(a); BOOST_FOREACH(CCombinedArtifactInstance::ConstituentInfo & ci, comb->constituentsInfo)