|
@@ -1048,17 +1048,19 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
|
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
|
|
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ int64_t drainedLife = 0;
|
|
|
|
+
|
|
// only primary target
|
|
// only primary target
|
|
if(defender->alive())
|
|
if(defender->alive())
|
|
- applyBattleEffects(bat, blm, attackerState, fireShield, defender, distance, false);
|
|
|
|
|
|
+ drainedLife += applyBattleEffects(bat, attackerState, fireShield, defender, distance, false);
|
|
|
|
|
|
//multiple-hex normal attack
|
|
//multiple-hex normal attack
|
|
std::set<const CStack*> attackedCreatures = gs->curB->getAttackedCreatures(attacker, targetHex, bat.shot()); //creatures other than primary target
|
|
std::set<const CStack*> attackedCreatures = gs->curB->getAttackedCreatures(attacker, targetHex, bat.shot()); //creatures other than primary target
|
|
-
|
|
|
|
for(const CStack * stack : attackedCreatures)
|
|
for(const CStack * stack : attackedCreatures)
|
|
{
|
|
{
|
|
if(stack != defender && stack->alive()) //do not hit same stack twice
|
|
if(stack != defender && stack->alive()) //do not hit same stack twice
|
|
- applyBattleEffects(bat, blm, attackerState, fireShield, stack, distance, true);
|
|
|
|
|
|
+ drainedLife += applyBattleEffects(bat, attackerState, fireShield, stack, distance, true);
|
|
}
|
|
}
|
|
|
|
|
|
std::shared_ptr<const Bonus> bonus = attacker->getBonusLocalFirst(Selector::type()(Bonus::SPELL_LIKE_ATTACK));
|
|
std::shared_ptr<const Bonus> bonus = attacker->getBonusLocalFirst(Selector::type()(Bonus::SPELL_LIKE_ATTACK));
|
|
@@ -1086,7 +1088,7 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
|
{
|
|
{
|
|
if(stack != defender && stack->alive()) //do not hit same stack twice
|
|
if(stack != defender && stack->alive()) //do not hit same stack twice
|
|
{
|
|
{
|
|
- applyBattleEffects(bat, blm, attackerState, fireShield, stack, distance, true);
|
|
|
|
|
|
+ drainedLife += applyBattleEffects(bat, attackerState, fireShield, stack, distance, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1134,7 +1136,28 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
|
|
|
|
|
addGenericKilledLog(blm, defender, totalKills, multipleTargets);
|
|
addGenericKilledLog(blm, defender, totalKills, multipleTargets);
|
|
}
|
|
}
|
|
- sendAndApply(&blm);
|
|
|
|
|
|
+
|
|
|
|
+ // drain life effect (as well as log entry) must be applied after the attack
|
|
|
|
+ if(drainedLife > 0)
|
|
|
|
+ {
|
|
|
|
+ BattleAttack bat;
|
|
|
|
+ bat.stackAttacking = attacker->unitId();
|
|
|
|
+ {
|
|
|
|
+ CustomEffectInfo customEffect;
|
|
|
|
+ customEffect.sound = soundBase::DRAINLIF;
|
|
|
|
+ customEffect.effect = 52;
|
|
|
|
+ customEffect.stack = attackerState->unitId();
|
|
|
|
+ bat.customEffects.push_back(std::move(customEffect));
|
|
|
|
+ }
|
|
|
|
+ sendAndApply(&bat);
|
|
|
|
+
|
|
|
|
+ MetaString text;
|
|
|
|
+ attackerState->addText(text, MetaString::GENERAL_TXT, 361);
|
|
|
|
+ attackerState->addNameReplacement(text, false);
|
|
|
|
+ text.addReplacement(drainedLife);
|
|
|
|
+ defender->addNameReplacement(text, true);
|
|
|
|
+ blm.lines.push_back(std::move(text));
|
|
|
|
+ }
|
|
|
|
|
|
if(!fireShield.empty())
|
|
if(!fireShield.empty())
|
|
{
|
|
{
|
|
@@ -1174,12 +1197,24 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
|
StacksInjured pack;
|
|
StacksInjured pack;
|
|
pack.stacks.push_back(bsa);
|
|
pack.stacks.push_back(bsa);
|
|
sendAndApply(&pack);
|
|
sendAndApply(&pack);
|
|
- sendGenericKilledLog(attacker, bsa.killedAmount, false);
|
|
|
|
|
|
+
|
|
|
|
+ // TODO: this is already implemented in Damage::describeEffect()
|
|
|
|
+ {
|
|
|
|
+ MetaString text;
|
|
|
|
+ text.addTxt(MetaString::GENERAL_TXT, 376);
|
|
|
|
+ text.addReplacement(MetaString::SPELL_NAME, SpellID::FIRE_SHIELD);
|
|
|
|
+ text.addReplacement(totalDamage);
|
|
|
|
+ blm.lines.push_back(std::move(text));
|
|
|
|
+ }
|
|
|
|
+ addGenericKilledLog(blm, attacker, bsa.killedAmount, false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ sendAndApply(&blm);
|
|
|
|
+
|
|
handleAfterAttackCasting(ranged, attacker, defender);
|
|
handleAfterAttackCasting(ranged, attacker, defender);
|
|
}
|
|
}
|
|
-void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
|
|
|
|
|
|
+
|
|
|
|
+int64_t CGameHandler::applyBattleEffects(BattleAttack & bat, std::shared_ptr<battle::CUnitState> attackerState, FireShieldInfo & fireShield, const CStack * def, int distance, bool secondary)
|
|
{
|
|
{
|
|
BattleStackAttacked bsa;
|
|
BattleStackAttacked bsa;
|
|
if(secondary)
|
|
if(secondary)
|
|
@@ -1208,34 +1243,14 @@ void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm
|
|
CStack::prepareAttacked(bsa, getRandomGenerator(), bai.defender->acquireState()); //calculate casualties
|
|
CStack::prepareAttacked(bsa, getRandomGenerator(), bai.defender->acquireState()); //calculate casualties
|
|
}
|
|
}
|
|
|
|
|
|
- auto addLifeDrain = [&](int64_t & toHeal, EHealLevel level, EHealPower power)
|
|
|
|
- {
|
|
|
|
- attackerState->heal(toHeal, level, power);
|
|
|
|
-
|
|
|
|
- if(toHeal > 0)
|
|
|
|
- {
|
|
|
|
- CustomEffectInfo customEffect;
|
|
|
|
- customEffect.sound = soundBase::DRAINLIF;
|
|
|
|
- customEffect.effect = 52;
|
|
|
|
- customEffect.stack = attackerState->unitId();
|
|
|
|
- bat.customEffects.push_back(customEffect);
|
|
|
|
-
|
|
|
|
- MetaString text;
|
|
|
|
- attackerState->addText(text, MetaString::GENERAL_TXT, 361);
|
|
|
|
- attackerState->addNameReplacement(text, false);
|
|
|
|
- text.addReplacement((int)toHeal);
|
|
|
|
- def->addNameReplacement(text, true);
|
|
|
|
- blm.lines.push_back(text);
|
|
|
|
- }
|
|
|
|
- };
|
|
|
|
|
|
+ int64_t drainedLife = 0;
|
|
|
|
|
|
//life drain handling
|
|
//life drain handling
|
|
if(attackerState->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving())
|
|
if(attackerState->hasBonusOfType(Bonus::LIFE_DRAIN) && def->isLiving())
|
|
{
|
|
{
|
|
int64_t toHeal = bsa.damageAmount * attackerState->valOfBonuses(Bonus::LIFE_DRAIN) / 100;
|
|
int64_t toHeal = bsa.damageAmount * attackerState->valOfBonuses(Bonus::LIFE_DRAIN) / 100;
|
|
-
|
|
|
|
- if(toHeal > 0)
|
|
|
|
- addLifeDrain(toHeal, EHealLevel::RESURRECT, EHealPower::PERMANENT);
|
|
|
|
|
|
+ attackerState->heal(toHeal, EHealLevel::RESURRECT, EHealPower::PERMANENT);
|
|
|
|
+ drainedLife += toHeal;
|
|
}
|
|
}
|
|
|
|
|
|
//soul steal handling
|
|
//soul steal handling
|
|
@@ -1248,7 +1263,8 @@ void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm
|
|
if(attackerState->hasBonusOfType(Bonus::SOUL_STEAL, subtype))
|
|
if(attackerState->hasBonusOfType(Bonus::SOUL_STEAL, subtype))
|
|
{
|
|
{
|
|
int64_t toHeal = bsa.killedAmount * attackerState->valOfBonuses(Bonus::SOUL_STEAL, subtype) * attackerState->MaxHealth();
|
|
int64_t toHeal = bsa.killedAmount * attackerState->valOfBonuses(Bonus::SOUL_STEAL, subtype) * attackerState->MaxHealth();
|
|
- addLifeDrain(toHeal, EHealLevel::OVERHEAL, ((subtype == 0) ? EHealPower::ONE_BATTLE : EHealPower::PERMANENT));
|
|
|
|
|
|
+ attackerState->heal(toHeal, EHealLevel::OVERHEAL, ((subtype == 0) ? EHealPower::ONE_BATTLE : EHealPower::PERMANENT));
|
|
|
|
+ drainedLife += toHeal;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1263,6 +1279,8 @@ void CGameHandler::applyBattleEffects(BattleAttack & bat, BattleLogMessage & blm
|
|
auto fireShieldDamage = (std::min<int64_t>(def->getAvailableHealth(), bsa.damageAmount) * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
|
|
auto fireShieldDamage = (std::min<int64_t>(def->getAvailableHealth(), bsa.damageAmount) * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
|
|
fireShield.push_back(std::make_pair(def, fireShieldDamage));
|
|
fireShield.push_back(std::make_pair(def, fireShieldDamage));
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ return drainedLife;
|
|
}
|
|
}
|
|
|
|
|
|
void CGameHandler::sendGenericKilledLog(const CStack * defender, int32_t killed, bool multiple)
|
|
void CGameHandler::sendGenericKilledLog(const CStack * defender, int32_t killed, bool multiple)
|
|
@@ -1297,8 +1315,8 @@ void CGameHandler::addGenericKilledLog(BattleLogMessage & blm, const CStack * de
|
|
txt % (multiple ? VLC->generaltexth->allTexts[42] : defender->getCreature()->nameSing); // creature perishes
|
|
txt % (multiple ? VLC->generaltexth->allTexts[42] : defender->getCreature()->nameSing); // creature perishes
|
|
}
|
|
}
|
|
MetaString line;
|
|
MetaString line;
|
|
- line.addReplacement(txt.str());
|
|
|
|
- blm.lines.push_back(line);
|
|
|
|
|
|
+ line << txt.str();
|
|
|
|
+ blm.lines.push_back(std::move(line));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1652,7 +1670,9 @@ CGameHandler::~CGameHandler()
|
|
void CGameHandler::reinitScripting()
|
|
void CGameHandler::reinitScripting()
|
|
{
|
|
{
|
|
serverEventBus = make_unique<events::EventBus>();
|
|
serverEventBus = make_unique<events::EventBus>();
|
|
|
|
+#if SCRIPTING_ENABLED
|
|
serverScripts.reset(new scripting::PoolImpl(this, spellEnv));
|
|
serverScripts.reset(new scripting::PoolImpl(this, spellEnv));
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
|
|
void CGameHandler::init(StartInfo *si)
|
|
void CGameHandler::init(StartInfo *si)
|
|
@@ -2112,7 +2132,9 @@ void CGameHandler::run(bool resume)
|
|
logGlobal->info(sbuffer.str());
|
|
logGlobal->info(sbuffer.str());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#if SCRIPTING_ENABLED
|
|
services()->scripts()->run(serverScripts);
|
|
services()->scripts()->run(serverScripts);
|
|
|
|
+#endif
|
|
|
|
|
|
if(resume)
|
|
if(resume)
|
|
events::GameResumed::defaultExecute(serverEventBus.get());
|
|
events::GameResumed::defaultExecute(serverEventBus.get());
|
|
@@ -5585,7 +5607,7 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
|
|
auto topArmy = dialog->exchangingArmies.at(0);
|
|
auto topArmy = dialog->exchangingArmies.at(0);
|
|
auto bottomArmy = dialog->exchangingArmies.at(1);
|
|
auto bottomArmy = dialog->exchangingArmies.at(1);
|
|
|
|
|
|
- if (topArmy == o1 && bottomArmy == o2 || bottomArmy == o1 && topArmy == o2)
|
|
|
|
|
|
+ if ((topArmy == o1 && bottomArmy == o2) || (bottomArmy == o1 && topArmy == o2))
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -7319,15 +7341,17 @@ CRandomGenerator & CGameHandler::getRandomGenerator()
|
|
return CRandomGenerator::getDefault();
|
|
return CRandomGenerator::getDefault();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#if SCRIPTING_ENABLED
|
|
scripting::Pool * CGameHandler::getGlobalContextPool() const
|
|
scripting::Pool * CGameHandler::getGlobalContextPool() const
|
|
{
|
|
{
|
|
return serverScripts.get();
|
|
return serverScripts.get();
|
|
}
|
|
}
|
|
|
|
|
|
-scripting::Pool * CGameHandler::getContextPool() const
|
|
|
|
|
|
+scripting::Pool * CGameHandler::getContextPool() const
|
|
{
|
|
{
|
|
return serverScripts.get();
|
|
return serverScripts.get();
|
|
}
|
|
}
|
|
|
|
+#endif
|
|
|
|
|
|
const ObjectInstanceID CGameHandler::putNewObject(Obj ID, int subID, int3 pos)
|
|
const ObjectInstanceID CGameHandler::putNewObject(Obj ID, int subID, int3 pos)
|
|
{
|
|
{
|