diff --git a/Mods/vcmi/Content/config/english.json b/Mods/vcmi/Content/config/english.json index 82fb3b677..7b62549f2 100644 --- a/Mods/vcmi/Content/config/english.json +++ b/Mods/vcmi/Content/config/english.json @@ -1016,6 +1016,7 @@ "core.bonus.MANA_DRAIN.description" : "{Drains enemy mana}\nDrains ${val} mana every turn from enemy hero", "core.bonus.MECHANICAL.description" : "{Mechanical}\nThis unit is immune to effects that only affect living and can be repaired", "core.bonus.MIND_IMMUNITY.description" : "{Mind Spell Immunity}\nThis unit cannot be targeted by spells that affect its mind", + "core.bonus.MORE_DAMAGE_FROM_SPELL.description" : "{Vulnerable to ${subtype.spell}}\nThe damage taken by this unit when hit by a ${subtype.spell} is increased by ${val}%", "core.bonus.NO_DISTANCE_PENALTY.description" : "{No distance penalty}\nRanged attacks deal full damage at any distance", "core.bonus.NO_MELEE_PENALTY.description" : "{No melee penalty}\nThis ranged unit deals full damage with melee attacks", "core.bonus.NO_MORALE.description" : "{Neutral Morale}\nCreature is immune to morale effects", diff --git a/client/battle/BattleProjectileController.cpp b/client/battle/BattleProjectileController.cpp index df8d0211e..a37dc1b03 100644 --- a/client/battle/BattleProjectileController.cpp +++ b/client/battle/BattleProjectileController.cpp @@ -161,7 +161,7 @@ const CCreature & BattleProjectileController::getShooter(const CStack * stack) c if(creature->getId() == CreatureID::ARROW_TOWERS) creature = owner.siegeController->getTurretCreature(stack->initialPosition); - if(creature->animation.missileFrameAngles.empty()) + if(creature->animation.missileFrameAngles.empty() && creature->animation.projectileRay.empty()) { logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->getNameSingularTranslated()); creature = CreatureID(CreatureID::ARCHER).toCreature(); diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 60eae5819..3b77a23ee 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -2184,8 +2184,16 @@ void CMageGuildScreen::updateSpells(ObjectInstanceID tID) uint32_t spellCount = town->spellsAtLevel(i+1,false); //spell at level with -1 hmmm? for(uint32_t j=0; jmageGuildLevel() && town->spells[i].size()>j) + if (town->hasBuilt(BuildingSubID::AURORA_BOREALIS)) + { + std::string auroraBorealisName = town->getTown()->getSpecialBuilding(BuildingSubID::AURORA_BOREALIS)->getNameTranslated(); + + auroraBorealisScrolls.push_back(std::make_shared(positions[i][j], auroraBorealisName)); + } + else if(imageGuildLevel() && town->spells[i].size()>j) + { spells.push_back(std::make_shared(positions[i][j], town->spells[i][j].toSpell(), townId)); + } else emptyScrolls.push_back(std::make_shared(AnimationPath::builtin("TPMAGES.DEF"), 1, 0, positions[i][j].x, positions[i][j].y)); } @@ -2194,6 +2202,22 @@ void CMageGuildScreen::updateSpells(ObjectInstanceID tID) redraw(); } +CMageGuildScreen::ScrollAllSpells::ScrollAllSpells(Point position, const std::string & buildingName) +{ + constexpr int auroraBorealisImageIndex = 70; + + OBJECT_CONSTRUCTION; + pos += position; + image = std::make_shared(AnimationPath::builtin("SPELLSCR"), auroraBorealisImageIndex); + pos = image->pos; + + MetaString description; + description.appendTextID("core.genrltxt.714"); + description.replaceRawString(buildingName); + + text = std::make_shared(Rect(Point(), pos.dimensions()), description.toString(), description.toString() ); +} + CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell, ObjectInstanceID townId) : spell(Spell), townId(townId) { diff --git a/client/windows/CCastleInterface.h b/client/windows/CCastleInterface.h index 3ff010091..ba1e3a7f9 100644 --- a/client/windows/CCastleInterface.h +++ b/client/windows/CCastleInterface.h @@ -37,6 +37,7 @@ class CGarrisonInt; class CComponent; class CComponentBox; class LRClickableArea; +class LRClickableAreaWText; class CTextInputWithConfirm; /// Building "button" @@ -394,10 +395,21 @@ class CMageGuildScreen : public CStatusbarWindow void showPopupWindow(const Point & cursorPosition) override; void hover(bool on) override; }; + + class ScrollAllSpells : public CIntObject + { + std::shared_ptr image; + std::shared_ptr text; + + public: + ScrollAllSpells(Point position, const std::string & buildingName); + }; + std::shared_ptr window; std::shared_ptr exit; std::vector> spells; std::vector> emptyScrolls; + std::vector> auroraBorealisScrolls; std::shared_ptr resdatabar; diff --git a/config/factions/conflux.json b/config/factions/conflux.json index b9ef8f020..27140d243 100644 --- a/config/factions/conflux.json +++ b/config/factions/conflux.json @@ -196,7 +196,7 @@ "earthMagic" ] }, - "grail": { }, + "grail": { "type" : "auroraBorealis" }, "extraTownHall": { }, "extraCityHall": { }, "extraCapitol": { }, diff --git a/config/factions/inferno.json b/config/factions/inferno.json index e6fd10f10..07ee66bbf 100644 --- a/config/factions/inferno.json +++ b/config/factions/inferno.json @@ -205,7 +205,7 @@ }, "horde2": { "upgrades" : "dwellingLvl3" }, "horde2Upgr": { "upgrades" : "dwellingUpLvl3", "requires" : [ "horde2" ] }, - "grail": { }, + "grail": { "type" : "deityOfFire" }, "dwellingLvl1": { "requires" : [ "fort" ] }, "dwellingLvl2": { "requires" : [ "dwellingLvl1" ] }, diff --git a/config/schemas/townBuilding.json b/config/schemas/townBuilding.json index 3b4417549..581d37708 100644 --- a/config/schemas/townBuilding.json +++ b/config/schemas/townBuilding.json @@ -36,7 +36,7 @@ }, "type" : { "type" : "string", - "enum" : [ "mysticPond", "castleGate", "portalOfSummoning", "library", "escapeTunnel", "treasury", "bank" ], + "enum" : [ "mysticPond", "castleGate", "portalOfSummoning", "library", "escapeTunnel", "treasury", "bank", "auroraBorealis", "deityOfFire" ], "description" : "Subtype for some special buildings" }, "mode" : { diff --git a/docs/modders/Bonus/Bonus_Types.md b/docs/modders/Bonus/Bonus_Types.md index 66cad30e0..c7b63f8e1 100644 --- a/docs/modders/Bonus/Bonus_Types.md +++ b/docs/modders/Bonus/Bonus_Types.md @@ -543,9 +543,26 @@ When affected unit is attacked from behind, it will receive more damage when att Affected unit will deal more damage when attacking specific creature -- subtype - identifier of hated creature, ie. "creature.genie" +- subtype - identifier of hated creature, ie. `genie` - val - additional damage, percentage +### HATES_TRAIT + +Affected unit will deal more damage when attacking unit that has specific bonus. Note that this bonus has no assigned description. To make it visible in creature window UI, make sure to provide custom description for such bonus. + +- subtype - identifier of hated bonus, ie. `UNDEAD` +- val - additional damage, percentage + +Example: Unit deals 50% more damage to any target that has UNDEAD bonus + +```json + "hatesUndead" : { + "type" : "HATES_TRAIT", + "subtype" : "UNDEAD", + "val" : 50 + } +``` + ### SPELL_LIKE_ATTACK Affected unit ranged attack will use animation and range of specified spell (Magog, Lich) diff --git a/docs/modders/Entities_Format/Town_Building_Format.md b/docs/modders/Entities_Format/Town_Building_Format.md index ecc4f7678..6b0c947a7 100644 --- a/docs/modders/Entities_Format/Town_Building_Format.md +++ b/docs/modders/Entities_Format/Town_Building_Format.md @@ -238,15 +238,13 @@ Building requirements can be described using logical expressions: Following Heroes III buildings can be used as unique buildings for a town. Their functionality should be identical to a corresponding H3 building. H3 buildings that are not present in this list contain no hardcoded functionality. See vcmi json configuration to see how such buildings can be implemented in a mod. - `mysticPond` -- `artifactMerchant` -- `freelancersGuild` -- `magicUniversity` - `castleGate` -- `creatureTransformer` - `portalOfSummoning` - `library` - `escapeTunnel` - `treasury` +- `auroraBorealis` +- `deityOfFire` #### Buildings from other Heroes III mods diff --git a/lib/battle/DamageCalculator.cpp b/lib/battle/DamageCalculator.cpp index 3a53c0424..144d58b9b 100644 --- a/lib/battle/DamageCalculator.cpp +++ b/lib/battle/DamageCalculator.cpp @@ -285,13 +285,26 @@ double DamageCalculator::getAttackFromBackFactor() const return 0; } -double DamageCalculator::getAttackHateFactor() const +double DamageCalculator::getAttackHateCreatureFactor() const { //assume that unit have only few HATE features and cache them all auto allHateEffects = info.attacker->getBonusesOfType(BonusType::HATE); return allHateEffects->valOfBonuses(Selector::subtype()(BonusSubtypeID(info.defender->creatureId()))) / 100.0; } +double DamageCalculator::getAttackHateTraitFactor() const +{ + //assume that unit have only few HATE features and cache them all + auto allHateEffects = info.attacker->getBonusesOfType(BonusType::HATES_TRAIT); + + auto selector = [this](const Bonus* hateBonus) -> bool + { + return info.defender->hasBonusOfType(hateBonus->subtype.as().toEnum()); + }; + + return allHateEffects->valOfBonuses(selector) / 100.0; +} + double DamageCalculator::getAttackRevengeFactor() const { if(info.attacker->hasBonusOfType(BonusType::REVENGE)) //HotA Haspid ability @@ -475,7 +488,8 @@ std::vector DamageCalculator::getAttackFactors() const getAttackFromBackFactor(), getAttackDeathBlowFactor(), getAttackDoubleDamageFactor(), - getAttackHateFactor(), + getAttackHateCreatureFactor(), + getAttackHateTraitFactor(), getAttackRevengeFactor() }; } diff --git a/lib/battle/DamageCalculator.h b/lib/battle/DamageCalculator.h index d0d649f71..c729c0cb6 100644 --- a/lib/battle/DamageCalculator.h +++ b/lib/battle/DamageCalculator.h @@ -50,7 +50,8 @@ class DLL_LINKAGE DamageCalculator double getAttackJoustingFactor() const; double getAttackDeathBlowFactor() const; double getAttackDoubleDamageFactor() const; - double getAttackHateFactor() const; + double getAttackHateCreatureFactor() const; + double getAttackHateTraitFactor() const; double getAttackRevengeFactor() const; double getAttackFromBackFactor() const; diff --git a/lib/bonuses/BonusCustomTypes.cpp b/lib/bonuses/BonusCustomTypes.cpp index 6f6ac83b0..579a97102 100644 --- a/lib/bonuses/BonusCustomTypes.cpp +++ b/lib/bonuses/BonusCustomTypes.cpp @@ -10,6 +10,8 @@ #include "StdInc.h" #include "BonusCustomTypes.h" +#include "CBonusTypeHandler.h" +#include "GameLibrary.h" VCMI_LIB_NAMESPACE_BEGIN @@ -75,4 +77,19 @@ std::string BonusCustomSource::encode(const si32 index) return std::to_string(index); } +std::string BonusTypeID::encode(int32_t index) +{ + if (index == static_cast(BonusType::NONE)) + return ""; + return LIBRARY->bth->bonusToString(static_cast(index)); +} + +si32 BonusTypeID::decode(const std::string & identifier) +{ + if (identifier.empty()) + return RiverId::NO_RIVER.getNum(); + + return resolveIdentifier("bonus", identifier); +} + VCMI_LIB_NAMESPACE_END diff --git a/lib/bonuses/BonusCustomTypes.h b/lib/bonuses/BonusCustomTypes.h index 2520549ad..7d8458091 100644 --- a/lib/bonuses/BonusCustomTypes.h +++ b/lib/bonuses/BonusCustomTypes.h @@ -11,6 +11,7 @@ #include "../constants/EntityIdentifiers.h" #include "../constants/VariantIdentifier.h" +#include "BonusEnum.h" VCMI_LIB_NAMESPACE_BEGIN @@ -77,7 +78,27 @@ public: static BonusCustomSubtype creatureLevel(int level); }; -using BonusSubtypeID = VariantIdentifier; -using BonusSourceID = VariantIdentifier; +class DLL_LINKAGE BonusTypeID : public EntityIdentifier +{ +public: + using EntityIdentifier::EntityIdentifier; + using EnumType = BonusType; + + static std::string encode(int32_t index); + static si32 decode(const std::string & identifier); + + constexpr EnumType toEnum() const + { + return static_cast(EntityIdentifier::num); + } + + constexpr BonusTypeID(const EnumType & enumValue) + { + EntityIdentifier::num = static_cast(enumValue); + } +}; + +using BonusSubtypeID = VariantIdentifier; +using BonusSourceID = VariantIdentifier; VCMI_LIB_NAMESPACE_END diff --git a/lib/bonuses/BonusEnum.h b/lib/bonuses/BonusEnum.h index a3b3e839f..665072907 100644 --- a/lib/bonuses/BonusEnum.h +++ b/lib/bonuses/BonusEnum.h @@ -194,6 +194,7 @@ class JsonNode; BONUS_NAME(TRANSMUTATION_IMMUNITY) /*blocks TRANSMUTATION bonus*/\ BONUS_NAME(COMBAT_MANA_BONUS) /* Additional mana per combat */ \ BONUS_NAME(SPECIFIC_SPELL_RANGE) /* value used for allowed spell range, subtype - spell id */\ + BONUS_NAME(HATES_TRAIT) /* affected unit deals additional damage to units with specific bonus. subtype - bonus, val - damage bonus percent */ \ /* end of list */ diff --git a/lib/constants/Enumerations.h b/lib/constants/Enumerations.h index 952f7b64f..303b55f38 100644 --- a/lib/constants/Enumerations.h +++ b/lib/constants/Enumerations.h @@ -31,7 +31,9 @@ namespace BuildingSubID PORTAL_OF_SUMMONING, ESCAPE_TUNNEL, TREASURY, - BANK + BANK, + AURORA_BOREALIS, + DEITY_OF_FIRE }; } diff --git a/lib/constants/StringConstants.h b/lib/constants/StringConstants.h index 97016e852..b3f39f68e 100644 --- a/lib/constants/StringConstants.h +++ b/lib/constants/StringConstants.h @@ -135,7 +135,9 @@ namespace MappedKeys { "library", BuildingSubID::LIBRARY }, { "escapeTunnel", BuildingSubID::ESCAPE_TUNNEL }, { "treasury", BuildingSubID::TREASURY }, - { "bank", BuildingSubID::BANK } + { "bank", BuildingSubID::BANK }, + { "auroraBorealis", BuildingSubID::AURORA_BOREALIS }, + { "deityOfFire", BuildingSubID::DEITY_OF_FIRE } }; static const std::map MARKET_NAMES_TO_TYPES = diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 57b198686..ddfa5a89b 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -1242,17 +1242,20 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio case EventCondition::HAVE_CREATURES: { //check if in players armies there is enough creatures - int total = 0; //creature counter - for(auto ai : map->getObjects()) - { - if(ai->getOwner() == player) - { - for(const auto & elem : ai->Slots()) //iterate through army - if(elem.second->getId() == condition.objectType.as()) //it's searched creature - total += elem.second->getCount(); - } - } - return total >= condition.value; + // NOTE: only heroes & towns are checked, in line with H3. + // Garrisons, mines, and guards of owned dwellings(!) are excluded + int totalCreatures = 0; + for (const auto & hero : p->getHeroes()) + for(const auto & elem : hero->Slots()) //iterate through army + if(elem.second->getId() == condition.objectType.as()) //it's searched creature + totalCreatures += elem.second->getCount(); + + for (const auto & town : p->getTowns()) + for(const auto & elem : town->Slots()) //iterate through army + if(elem.second->getId() == condition.objectType.as()) //it's searched creature + totalCreatures += elem.second->getCount(); + + return totalCreatures >= condition.value; } case EventCondition::HAVE_RESOURCES: { @@ -1626,18 +1629,19 @@ void CGameState::loadGame(CLoadFile & file) logGlobal->info("Loading game state..."); CMapHeader dummyHeader; - StartInfo dummyStartInfo; ActiveModsInSaveList dummyActiveMods; file.load(dummyHeader); if (file.hasFeature(ESerializationVersion::NO_RAW_POINTERS_IN_SERIALIZER)) { + StartInfo dummyStartInfo; file.load(dummyStartInfo); file.load(dummyActiveMods); file.load(*this); } else { + auto dummyStartInfo = std::make_shared(); bool dummyA = false; uint32_t dummyB = 0; uint16_t dummyC = 0; diff --git a/lib/gameState/CGameStateCampaign.h b/lib/gameState/CGameStateCampaign.h index b57769aa4..5f430f0ea 100644 --- a/lib/gameState/CGameStateCampaign.h +++ b/lib/gameState/CGameStateCampaign.h @@ -81,11 +81,9 @@ public: { bool dummyA = false; uint32_t dummyB = 0; - uint16_t dummyC = 0; h & dummyA; h & dummyB; - h & dummyC; } } }; diff --git a/lib/json/JsonBonus.cpp b/lib/json/JsonBonus.cpp index 872ed3242..3405668a8 100644 --- a/lib/json/JsonBonus.cpp +++ b/lib/json/JsonBonus.cpp @@ -91,6 +91,14 @@ static void loadBonusSubtype(BonusSubtypeID & subtype, BonusType type, const Jso }); break; } + case BonusType::HATES_TRAIT: + { + LIBRARY->identifiers()->requestIdentifier( "bonus", node, [&subtype](int32_t identifier) + { + subtype = BonusType(identifier); + }); + break; + } case BonusType::NO_TERRAIN_PENALTY: { LIBRARY->identifiers()->requestIdentifier( "terrain", node, [&subtype](int32_t identifier) diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 085674e74..c40242755 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -905,13 +905,6 @@ bool CGTownInstance::hasBuilt(const BuildingID & buildingID) const return vstd::contains(builtBuildings, buildingID); } -bool CGTownInstance::hasBuilt(const BuildingID & buildingID, FactionID townID) const -{ - if (townID == getTown()->faction->getId() || townID == FactionID::ANY) - return hasBuilt(buildingID); - return false; -} - void CGTownInstance::addBuilding(const BuildingID & buildingID) { if(buildingID == BuildingID::NONE) diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index 4b1c9811b..b286a762b 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -165,7 +165,6 @@ public: bool hasBuilt(BuildingSubID::EBuildingSubID buildingID) const; //checks if building is constructed and town has same subID bool hasBuilt(const BuildingID & buildingID) const; - bool hasBuilt(const BuildingID & buildingID, FactionID townID) const; void addBuilding(const BuildingID & buildingID); void removeBuilding(const BuildingID & buildingID); void removeAllBuildings(); diff --git a/lib/rewardable/Reward.h b/lib/rewardable/Reward.h index 2f9d896ca..1edbd20d5 100644 --- a/lib/rewardable/Reward.h +++ b/lib/rewardable/Reward.h @@ -143,12 +143,20 @@ struct DLL_LINKAGE Reward final h & movePoints; h & primary; h & secondary; - h & heroBonuses; if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS) { + h & heroBonuses; h & playerBonuses; h & commanderBonuses; } + else + { + std::vector bonuses; + h & bonuses; + for (const auto & bonus : bonuses) + heroBonuses.push_back(std::make_shared(bonus)); + } + h & grantedArtifacts; if (h.version >= Handler::Version::REWARDABLE_EXTENSIONS) { diff --git a/lib/spells/CSpellHandler.cpp b/lib/spells/CSpellHandler.cpp index 00fb835a5..122caf768 100644 --- a/lib/spells/CSpellHandler.cpp +++ b/lib/spells/CSpellHandler.cpp @@ -953,7 +953,7 @@ std::shared_ptr CSpellHandler::loadFromJson(const std::string & scope, c const si32 levelPower = levelObject.power = static_cast(levelNode["power"].Integer()); - if (!spell->isCreatureAbility()) + if (!levelNode["description"].String().empty()) LIBRARY->generaltexth->registerString(scope, spell->getDescriptionTextID(levelIndex), levelNode["description"]); levelObject.cost = static_cast(levelNode["cost"].Integer()); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7b42be2cc..34a827dd9 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -773,7 +773,7 @@ void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h) ChangeSpells cs; cs.hid = h->id; cs.learn = true; - if (t->hasBuilt(BuildingID::GRAIL, ETownType::CONFLUX) && t->hasBuilt(BuildingID::MAGES_GUILD_1)) + if (t->hasBuilt(BuildingSubID::AURORA_BOREALIS) && t->hasBuilt(BuildingID::MAGES_GUILD_1)) { // Aurora Borealis give spells of all levels even if only level 1 mages guild built for (int i = 0; i < h->maxSpellLevel(); i++) @@ -2115,22 +2115,6 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, } }; - //Performs stuff that has to be done after new building is built - auto processAfterBuiltStructure = [t, this](const BuildingID buildingID) - { - auto isMageGuild = (buildingID <= BuildingID::MAGES_GUILD_5 && buildingID >= BuildingID::MAGES_GUILD_1); - auto isLibrary = isMageGuild ? false - : t->getTown()->buildings.at(buildingID)->subId == BuildingSubID::EBuildingSubID::LIBRARY; - - if(isMageGuild || isLibrary || (t->getFactionID() == ETownType::CONFLUX && buildingID == BuildingID::GRAIL)) - { - if(t->getVisitingHero()) - giveSpells(t,t->getVisitingHero()); - if(t->getGarrisonHero()) - giveSpells(t,t->getGarrisonHero()); - } - }; - //Checks if all requirements will be met with expected building list "buildingsThatWillBe" auto areRequirementsFulfilled = [&buildingsThatWillBe](const BuildingID & buildID) { @@ -2192,8 +2176,20 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, sendAndApply(ns); //Other post-built events. To some logic like giving spells to work gamestate changes for new building must be already in place! - for(auto builtID : ns.bid) - processAfterBuiltStructure(builtID); + for(auto buildingID : ns.bid) + { + bool isMageGuild = buildingID <= BuildingID::MAGES_GUILD_5 && buildingID >= BuildingID::MAGES_GUILD_1; + bool isLibrary = t->getTown()->buildings.at(buildingID)->subId == BuildingSubID::LIBRARY; + bool isAurora = t->getTown()->buildings.at(buildingID)->subId == BuildingSubID::AURORA_BOREALIS; + + if(isMageGuild || isLibrary || isAurora) + { + if(t->getVisitingHero()) + giveSpells(t,t->getVisitingHero()); + if(t->getGarrisonHero()) + giveSpells(t,t->getGarrisonHero()); + } + }; // now when everything is built - reveal tiles for lookout tower changeFogOfWar(t->getSightCenter(), t->getSightRadius(), t->getOwner(), ETileVisibility::REVEALED); diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index cf9340efb..14b8e3e78 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -1448,7 +1448,7 @@ void BattleActionProcessor::applyBattleEffects(const CBattleInfoCallback & battl } //life drain handling - if(attackerState->hasBonusOfType(BonusType::LIFE_DRAIN) && def->isLiving()) + if(attackerState->hasBonusOfType(BonusType::LIFE_DRAIN) && def->isLiving() && attackerState->getTotalHealth() != attackerState->getAvailableHealth()) { int64_t toHeal = bsa.damageAmount * attackerState->valOfBonuses(BonusType::LIFE_DRAIN) / 100; healInfo += attackerState->heal(toHeal, EHealLevel::RESURRECT, EHealPower::PERMANENT); diff --git a/server/processors/NewTurnProcessor.cpp b/server/processors/NewTurnProcessor.cpp index 69376f1e3..af343e545 100644 --- a/server/processors/NewTurnProcessor.cpp +++ b/server/processors/NewTurnProcessor.cpp @@ -512,7 +512,7 @@ std::tuple NewTurnProcessor::pickWeekType(bool newMonth) for (const auto & townID : gameHandler->gameState().getMap().getAllTowns()) { const auto * t = gameHandler->gameState().getTown(townID); - if (t->hasBuilt(BuildingID::GRAIL, ETownType::INFERNO)) + if (t->hasBuilt(BuildingSubID::DEITY_OF_FIRE)) return { EWeekType::DEITYOFFIRE, CreatureID::IMP }; }