diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 6e082b93c..05ed10689 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -326,6 +326,12 @@ void AIGateway::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID her }); } +void AIGateway::heroExperienceChanged(const CGHeroInstance * hero, si64 val) +{ + LOG_TRACE_PARAMS(logAi, "val '%i'", val); + NET_EVENT_HANDLER; +} + void AIGateway::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) { LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", which.getNum() % val); diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index cde5d4ddf..dd2537434 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -123,6 +123,7 @@ public: void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override; void tileRevealed(const std::unordered_set & pos) override; void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override; + void heroExperienceChanged(const CGHeroInstance * hero, si64 val) override; void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override; void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level, QueryID queryID) override; void heroMovePointsChanged(const CGHeroInstance * hero) override; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index cddd802f0..cf88e7b04 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -364,6 +364,12 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q }); } +void VCAI::heroExperienceChanged(const CGHeroInstance * hero, si64 val) +{ + LOG_TRACE_PARAMS(logAi, "val '%i'", val); + NET_EVENT_HANDLER; +} + void VCAI::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) { LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", which.getNum() % val); diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 9260589eb..6a3c97a69 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -164,6 +164,7 @@ public: void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override; void tileRevealed(const std::unordered_set & pos) override; void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override; + void heroExperienceChanged(const CGHeroInstance * hero, si64 val) override; void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override; void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level, QueryID queryID) override; void heroMovePointsChanged(const CGHeroInstance * hero) override; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 72e622cc4..a99cfcb94 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -462,18 +462,17 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town) ENGINE->windows().pushWindow(newCastleInt); } +void CPlayerInterface::heroExperienceChanged(const CGHeroInstance * hero, si64 val) +{ + EVENT_HANDLER_CALLED_BY_CLIENT; + for(auto ctw : ENGINE->windows().findWindows()) + ctw->updateExperience(); +} + void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) { EVENT_HANDLER_CALLED_BY_CLIENT; - if (which == PrimarySkill::EXPERIENCE) - { - for(auto ctw : ENGINE->windows().findWindows()) - ctw->updateExperience(); - } - else - { - adventureInt->onHeroChanged(hero); - } + adventureInt->onHeroChanged(hero); } void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index fb0b7fc61..d1a5d7356 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -109,6 +109,7 @@ protected: // Call-ins from server, should not be called directly, but only via void commanderGotLevel (const CCommanderInstance * commander, std::vector skills, QueryID queryID) override; void heroInGarrisonChange(const CGTownInstance *town) override; void heroMoved(const TryMoveHero & details, bool verbose = true) override; + void heroExperienceChanged(const CGHeroInstance * hero, si64 val) override; void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override; void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override; void heroManaPointsChanged(const CGHeroInstance * hero) override; diff --git a/client/ClientNetPackVisitors.h b/client/ClientNetPackVisitors.h index f68467559..02d0bf69d 100644 --- a/client/ClientNetPackVisitors.h +++ b/client/ClientNetPackVisitors.h @@ -32,7 +32,8 @@ public: } void visitSetResources(SetResources & pack) override; - void visitSetPrimSkill(SetPrimSkill & pack) override; + void visitSetPrimarySkill(SetPrimarySkill & pack) override; + void visitSetHeroExperience(SetHeroExperience & pack) override; void visitSetSecSkill(SetSecSkill & pack) override; void visitHeroVisitCastle(HeroVisitCastle & pack) override; void visitSetMana(SetMana & pack) override; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index c2cb0220a..1795d5a76 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -122,7 +122,18 @@ void ApplyClientNetPackVisitor::visitSetResources(SetResources & pack) callInterfaceIfPresent(cl, pack.player, &IGameEventsReceiver::receivedResource); } -void ApplyClientNetPackVisitor::visitSetPrimSkill(SetPrimSkill & pack) +void ApplyClientNetPackVisitor::visitSetHeroExperience(SetHeroExperience & pack) +{ + const CGHeroInstance * h = cl.gameInfo().getHero(pack.id); + if(!h) + { + logNetwork->error("Cannot find hero with pack.id %d", pack.id.getNum()); + return; + } + callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroExperienceChanged, h, pack.val); +} + +void ApplyClientNetPackVisitor::visitSetPrimarySkill(SetPrimarySkill & pack) { const CGHeroInstance * h = cl.gameInfo().getHero(pack.id); if(!h) diff --git a/docs/modders/Bonus/Bonus_Types.md b/docs/modders/Bonus/Bonus_Types.md index d82160184..d58b46ba3 100644 --- a/docs/modders/Bonus/Bonus_Types.md +++ b/docs/modders/Bonus/Bonus_Types.md @@ -190,6 +190,12 @@ Increases experience gain from all sources by affected heroes - val: additional experience bonus, percentage +### STACK_EXPERIENCE_GAIN_PERCENT + +Increases experience gain from combat by affected units. No effect if stack experience is off. Has no effect on commanders + +- val: additional experience bonus, percentage + ### UNDEAD_RAISE_PERCENTAGE Defines percentage of enemy troops that will be raised after battle into own army (Necromancy). Raised unit is determined by IMPROVED_NECROMANCY bonus diff --git a/lib/bonuses/BonusEnum.h b/lib/bonuses/BonusEnum.h index be8113504..7ee9e3e03 100644 --- a/lib/bonuses/BonusEnum.h +++ b/lib/bonuses/BonusEnum.h @@ -188,6 +188,7 @@ class JsonNode; BONUS_NAME(MULTIHEX_UNIT_ATTACK) /*eg. dragons*/ \ BONUS_NAME(MULTIHEX_ENEMY_ATTACK) /*eg. dragons*/ \ BONUS_NAME(MULTIHEX_ANIMATION) /*eg. dragons*/ \ + BONUS_NAME(STACK_EXPERIENCE_GAIN_PERCENT) /*modifies all stack experience gains*/\ /* end of list */ diff --git a/lib/callback/IGameEventsReceiver.h b/lib/callback/IGameEventsReceiver.h index 5c7bd38b5..edd0294db 100644 --- a/lib/callback/IGameEventsReceiver.h +++ b/lib/callback/IGameEventsReceiver.h @@ -56,6 +56,7 @@ public: virtual void heroCreated(const CGHeroInstance*){}; virtual void heroInGarrisonChange(const CGTownInstance *town){}; virtual void heroMoved(const TryMoveHero & details, bool verbose = true){}; + virtual void heroExperienceChanged(const CGHeroInstance * hero, si64 val){}; virtual void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val){}; virtual void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val){}; virtual void heroManaPointsChanged(const CGHeroInstance * hero){} //not called at the beginning of turn and after spell casts diff --git a/lib/constants/EntityIdentifiers.cpp b/lib/constants/EntityIdentifiers.cpp index a626c7b8d..9ed3e4132 100644 --- a/lib/constants/EntityIdentifiers.cpp +++ b/lib/constants/EntityIdentifiers.cpp @@ -100,7 +100,6 @@ const PrimarySkill PrimarySkill::ATTACK(0); const PrimarySkill PrimarySkill::DEFENSE(1); const PrimarySkill PrimarySkill::SPELL_POWER(2); const PrimarySkill PrimarySkill::KNOWLEDGE(3); -const PrimarySkill PrimarySkill::EXPERIENCE(4); const BoatId BoatId::NONE(-1); const BoatId BoatId::NECROPOLIS(0); diff --git a/lib/constants/EntityIdentifiers.h b/lib/constants/EntityIdentifiers.h index 51cfac6b6..713496fa1 100644 --- a/lib/constants/EntityIdentifiers.h +++ b/lib/constants/EntityIdentifiers.h @@ -250,8 +250,6 @@ public: static const std::array & ALL_SKILLS(); - static const PrimarySkill EXPERIENCE; - static si32 decode(const std::string& identifier); static std::string encode(const si32 index); static std::string entityType(); diff --git a/lib/gameState/GameStatePackVisitor.cpp b/lib/gameState/GameStatePackVisitor.cpp index a0d666fd7..09bc761b6 100644 --- a/lib/gameState/GameStatePackVisitor.cpp +++ b/lib/gameState/GameStatePackVisitor.cpp @@ -48,13 +48,30 @@ void GameStatePackVisitor::visitSetResources(SetResources & pack) gs.getPlayerState(pack.player)->resources.positive(); } -void GameStatePackVisitor::visitSetPrimSkill(SetPrimSkill & pack) +void GameStatePackVisitor::visitSetPrimarySkill(SetPrimarySkill & pack) { CGHeroInstance * hero = gs.getHero(pack.id); assert(hero); hero->setPrimarySkill(pack.which, pack.val, pack.mode); } +void GameStatePackVisitor::visitSetHeroExperience(SetHeroExperience & pack) +{ + CGHeroInstance * hero = gs.getHero(pack.id); + assert(hero); + hero->setExperience(pack.val, pack.mode); +} + +void GameStatePackVisitor::visitGiveStackExperience(GiveStackExperience & pack) +{ + auto * army = gs.getArmyInstance(pack.id); + + for (const auto & slot : pack.val) + army->getStackPtr(slot.first)->giveAverageStackExperience(slot.second); + + army->nodeHasChanged(); +} + void GameStatePackVisitor::visitSetSecSkill(SetSecSkill & pack) { CGHeroInstance *hero = gs.getHero(pack.id); @@ -1233,15 +1250,6 @@ void GameStatePackVisitor::visitBattleResultAccepted(BattleResultAccepted & pack attackerHero->removeBonusesRecursive(Bonus::OneBattle); if(const auto defenderHero = gs.getHero(pack.heroResult[BattleSide::DEFENDER].heroID)) defenderHero->removeBonusesRecursive(Bonus::OneBattle); - - if(gs.getSettings().getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) - { - if(const auto attackerArmy = gs.getArmyInstance(pack.heroResult[BattleSide::ATTACKER].armyID)) - attackerArmy->giveAverageStackExperience(pack.heroResult[BattleSide::ATTACKER].exp); - - if(const auto defenderArmy = gs.getArmyInstance(pack.heroResult[BattleSide::DEFENDER].armyID)) - defenderArmy->giveAverageStackExperience(pack.heroResult[BattleSide::DEFENDER].exp); - } } void GameStatePackVisitor::visitBattleStackMoved(BattleStackMoved & pack) diff --git a/lib/gameState/GameStatePackVisitor.h b/lib/gameState/GameStatePackVisitor.h index c9bc35fc5..8a5890453 100644 --- a/lib/gameState/GameStatePackVisitor.h +++ b/lib/gameState/GameStatePackVisitor.h @@ -27,7 +27,9 @@ public: } void visitSetResources(SetResources & pack) override; - void visitSetPrimSkill(SetPrimSkill & pack) override; + void visitSetPrimarySkill(SetPrimarySkill & pack) override; + void visitSetHeroExperience(SetHeroExperience & pack) override; + void visitGiveStackExperience(GiveStackExperience & pack) override; void visitSetSecSkill(SetSecSkill & pack) override; void visitHeroVisitCastle(HeroVisitCastle & pack) override; void visitSetMana(SetMana & pack) override; diff --git a/lib/mapObjects/CGHeroInstance.cpp b/lib/mapObjects/CGHeroInstance.cpp index dd5575ec0..29925bc80 100644 --- a/lib/mapObjects/CGHeroInstance.cpp +++ b/lib/mapObjects/CGHeroInstance.cpp @@ -1451,35 +1451,34 @@ std::vector CGHeroInstance::getLevelupSkillCandidates(IGameRando return skills; } + void CGHeroInstance::setPrimarySkill(PrimarySkill primarySkill, si64 value, ChangeValueMode mode) { - if(primarySkill < PrimarySkill::EXPERIENCE) - { - auto skill = getLocalBonus(Selector::type()(BonusType::PRIMARY_SKILL) - .And(Selector::subtype()(BonusSubtypeID(primarySkill))) - .And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL))); - assert(skill); + auto skill = getLocalBonus(Selector::type()(BonusType::PRIMARY_SKILL) + .And(Selector::subtype()(BonusSubtypeID(primarySkill))) + .And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL))); + assert(skill); - if(mode == ChangeValueMode::ABSOLUTE) - { - skill->val = static_cast(value); - } - else - { - skill->val += static_cast(value); - } - nodeHasChanged(); - } - else if(primarySkill == PrimarySkill::EXPERIENCE) + if(mode == ChangeValueMode::ABSOLUTE) { - if(mode == ChangeValueMode::ABSOLUTE) - { - exp = value; - } - else - { - exp += value; - } + skill->val = static_cast(value); + } + else + { + skill->val += static_cast(value); + } + nodeHasChanged(); +} + +void CGHeroInstance::setExperience(si64 value, ChangeValueMode mode) +{ + if(mode == ChangeValueMode::ABSOLUTE) + { + exp = value; + } + else + { + exp += value; } } diff --git a/lib/mapObjects/CGHeroInstance.h b/lib/mapObjects/CGHeroInstance.h index 6b7fbc09c..3f37397ed 100644 --- a/lib/mapObjects/CGHeroInstance.h +++ b/lib/mapObjects/CGHeroInstance.h @@ -193,6 +193,7 @@ public: bool canLearnSkill() const; bool canLearnSkill(const SecondarySkill & which) const; + void setExperience(si64 value, ChangeValueMode mode); void setPrimarySkill(PrimarySkill primarySkill, si64 value, ChangeValueMode mode); void setSecSkillLevel(const SecondarySkill & which, int val, ChangeValueMode mode); // abs == 0 - changes by value; 1 - sets to value void levelUp(); diff --git a/lib/networkPacks/NetPackVisitor.h b/lib/networkPacks/NetPackVisitor.h index bd93b5246..885065f50 100644 --- a/lib/networkPacks/NetPackVisitor.h +++ b/lib/networkPacks/NetPackVisitor.h @@ -38,7 +38,9 @@ public: virtual void visitEntitiesChanged(EntitiesChanged & pack) {} virtual void visitSetRewardableConfiguration(SetRewardableConfiguration & pack) {} virtual void visitSetResources(SetResources & pack) {} - virtual void visitSetPrimSkill(SetPrimSkill & pack) {} + virtual void visitSetPrimarySkill(SetPrimarySkill & pack) {} + virtual void visitSetHeroExperience(SetHeroExperience & pack) {} + virtual void visitGiveStackExperience(GiveStackExperience & pack) {} virtual void visitSetSecSkill(SetSecSkill & pack) {} virtual void visitHeroVisitCastle(HeroVisitCastle & pack) {} virtual void visitChangeSpells(ChangeSpells & pack) {} diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 88067bd3e..ed4d02221 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -114,9 +114,19 @@ void SetResources::visitTyped(ICPackVisitor & visitor) visitor.visitSetResources(*this); } -void SetPrimSkill::visitTyped(ICPackVisitor & visitor) +void SetPrimarySkill::visitTyped(ICPackVisitor & visitor) { - visitor.visitSetPrimSkill(*this); + visitor.visitSetPrimarySkill(*this); +} + +void SetHeroExperience::visitTyped(ICPackVisitor & visitor) +{ + visitor.visitSetHeroExperience(*this); +} + +void GiveStackExperience::visitTyped(ICPackVisitor & visitor) +{ + visitor.visitGiveStackExperience(*this); } void SetSecSkill::visitTyped(ICPackVisitor & visitor) diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index d1903a8cf..68af639e4 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -199,7 +199,7 @@ struct DLL_LINKAGE SetResources : public CPackForClient } }; -struct DLL_LINKAGE SetPrimSkill : public CPackForClient +struct DLL_LINKAGE SetPrimarySkill : public CPackForClient { void visitTyped(ICPackVisitor & visitor) override; @@ -217,6 +217,36 @@ struct DLL_LINKAGE SetPrimSkill : public CPackForClient } }; +struct DLL_LINKAGE SetHeroExperience : public CPackForClient +{ + void visitTyped(ICPackVisitor & visitor) override; + + ChangeValueMode mode = ChangeValueMode::RELATIVE; + ObjectInstanceID id; + si64 val = 0; + + template void serialize(Handler & h) + { + h & mode; + h & id; + h & val; + } +}; + +struct DLL_LINKAGE GiveStackExperience : public CPackForClient +{ + void visitTyped(ICPackVisitor & visitor) override; + + ObjectInstanceID id; + std::map val; + + template void serialize(Handler & h) + { + h & id; + h & val; + } +}; + struct DLL_LINKAGE SetSecSkill : public CPackForClient { void visitTyped(ICPackVisitor & visitor) override; diff --git a/lib/serializer/RegisterTypes.h b/lib/serializer/RegisterTypes.h index d08118f86..28cc95d52 100644 --- a/lib/serializer/RegisterTypes.h +++ b/lib/serializer/RegisterTypes.h @@ -145,7 +145,7 @@ void registerTypes(Serializer &s) s.template registerType(89); s.template registerType(90); s.template registerType(91); - s.template registerType(92); + s.template registerType(92); s.template registerType(93); s.template registerType(94); s.template registerType(95); @@ -296,6 +296,8 @@ void registerTypes(Serializer &s) s.template registerType(244); s.template registerType(245); s.template registerType(246); + s.template registerType(247); + s.template registerType(248); } VCMI_LIB_NAMESPACE_END diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index e93d0d449..c8f600859 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -154,7 +154,7 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero) logGlobal->trace("%s got level %d", hero->getNameTranslated(), hero->level); auto primarySkill = randomizer->rollPrimarySkillForLevelup(hero); - SetPrimSkill sps; + SetPrimarySkill sps; sps.id = hero->id; sps.which = primarySkill; sps.mode = ChangeValueMode::RELATIVE; @@ -338,6 +338,19 @@ void CGameHandler::expGiven(const CGHeroInstance *hero) levelUpCommander(hero->getCommander()); } +void CGameHandler::giveStackExperience(const CArmedInstance * army, TExpType val) +{ + GiveStackExperience gse; + gse.id = army->id; + + for (const auto & stack : army->Slots()) + { + int experienceBonusMultiplier = stack.second->valOfBonuses(BonusType::STACK_EXPERIENCE_GAIN_PERCENT); + gse.val[stack.first] = val + val * experienceBonusMultiplier / 100; + } + sendAndApply(gse); +} + void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountToGain) { TExpType maxExp = LIBRARY->heroh->reqExp(LIBRARY->heroh->maxSupportedLevel()); @@ -362,12 +375,11 @@ void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountTo sendAndApply(iw); } - SetPrimSkill sps; - sps.id = hero->id; - sps.which = PrimarySkill::EXPERIENCE; - sps.mode = ChangeValueMode::RELATIVE; - sps.val = amountToGain; - sendAndApply(sps); + SetHeroExperience she; + she.id = hero->id; + she.mode = ChangeValueMode::RELATIVE; + she.val = amountToGain; + sendAndApply(she); //hero may level up if (hero->getCommander() && hero->getCommander()->alive) @@ -385,7 +397,7 @@ void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountTo void CGameHandler::changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, ChangeValueMode mode) { - SetPrimSkill sps; + SetPrimarySkill sps; sps.id = hero->id; sps.which = which; sps.mode = mode; diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 2080336e1..3b01d0b71 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -121,6 +121,7 @@ public: bool removeObject(const CGObjectInstance * obj, const PlayerColor & initiator) override; void setOwner(const CGObjectInstance * obj, PlayerColor owner) override; void giveExperience(const CGHeroInstance * hero, TExpType val) override; + void giveStackExperience(const CArmedInstance * army, TExpType val); void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, ChangeValueMode mode) override; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, ChangeValueMode mode) override; diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 42dce3e64..fdfc5bb33 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -331,8 +331,12 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle) gameHandler->swapGarrisonOnSiege(winnerHero->getVisitedTown()->id); //return defending visitor from garrison to its rightful place } //give exp - if(!finishingBattle->isDraw() && battleResult->exp[finishingBattle->winnerSide] && winnerHero) - gameHandler->giveExperience(winnerHero, battleResult->exp[finishingBattle->winnerSide]); + if(!finishingBattle->isDraw() && battleResult->exp[finishingBattle->winnerSide]) + { + gameHandler->giveStackExperience(battle.battleGetArmyObject(finishingBattle->winnerSide), battleResult->exp[finishingBattle->winnerSide]); + if (winnerHero) + gameHandler->giveExperience(winnerHero, battleResult->exp[finishingBattle->winnerSide]); + } // Add statistics if(loserHero && !finishingBattle->isDraw())