mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-23 22:37:55 +02:00
Merge pull request #1229 from IvanSavenko/battle_improvements
Battle: Fixing bugs & Implementation of missing features
This commit is contained in:
@@ -1012,6 +1012,7 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
||||
BattleAttack bat;
|
||||
BattleLogMessage blm;
|
||||
bat.stackAttacking = attacker->unitId();
|
||||
bat.tile = targetHex;
|
||||
|
||||
std::shared_ptr<battle::CUnitState> attackerState = attacker->acquireState();
|
||||
|
||||
@@ -1117,6 +1118,9 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
||||
bat.attackerChanges.changedStacks.push_back(info);
|
||||
}
|
||||
|
||||
if (drainedLife > 0)
|
||||
bat.flags |= BattleAttack::LIFE_DRAIN;
|
||||
|
||||
sendAndApply(&bat);
|
||||
|
||||
{
|
||||
@@ -1145,17 +1149,6 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
||||
// 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);
|
||||
@@ -1190,28 +1183,30 @@ void CGameHandler::makeAttack(const CStack * attacker, const CStack * defender,
|
||||
//FIXME: add custom effect on actor
|
||||
}
|
||||
|
||||
BattleStackAttacked bsa;
|
||||
|
||||
bsa.stackAttacked = attacker->ID; //invert
|
||||
bsa.attackerID = uint32_t(-1);
|
||||
bsa.flags |= BattleStackAttacked::EFFECT;
|
||||
bsa.effect = 11;
|
||||
bsa.damageAmount = totalDamage;
|
||||
attacker->prepareAttacked(bsa, getRandomGenerator());
|
||||
|
||||
StacksInjured pack;
|
||||
pack.stacks.push_back(bsa);
|
||||
sendAndApply(&pack);
|
||||
|
||||
// TODO: this is already implemented in Damage::describeEffect()
|
||||
if (totalDamage > 0)
|
||||
{
|
||||
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));
|
||||
BattleStackAttacked bsa;
|
||||
|
||||
bsa.flags |= BattleStackAttacked::FIRE_SHIELD;
|
||||
bsa.stackAttacked = attacker->ID; //invert
|
||||
bsa.attackerID = defender->ID;
|
||||
bsa.damageAmount = totalDamage;
|
||||
attacker->prepareAttacked(bsa, getRandomGenerator());
|
||||
|
||||
StacksInjured pack;
|
||||
pack.stacks.push_back(bsa);
|
||||
sendAndApply(&pack);
|
||||
|
||||
// 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);
|
||||
}
|
||||
addGenericKilledLog(blm, attacker, bsa.killedAmount, false);
|
||||
}
|
||||
|
||||
sendAndApply(&blm);
|
||||
@@ -1224,6 +1219,7 @@ int64_t CGameHandler::applyBattleEffects(BattleAttack & bat, std::shared_ptr<bat
|
||||
BattleStackAttacked bsa;
|
||||
if(secondary)
|
||||
bsa.flags |= BattleStackAttacked::SECONDARY; //all other targets do not suffer from spells & spell-like abilities
|
||||
|
||||
bsa.attackerID = attackerState->unitId();
|
||||
bsa.stackAttacked = def->unitId();
|
||||
{
|
||||
@@ -1277,8 +1273,12 @@ int64_t CGameHandler::applyBattleEffects(BattleAttack & bat, std::shared_ptr<bat
|
||||
bat.bsa.push_back(bsa); //add this stack to the list of victims after drain life has been calculated
|
||||
|
||||
//fire shield handling
|
||||
if(!bat.shot() && !def->isClone() &&
|
||||
def->hasBonusOfType(Bonus::FIRE_SHIELD) && !attackerState->hasBonusOfType(Bonus::FIRE_IMMUNITY))
|
||||
if(!bat.shot() &&
|
||||
!def->isClone() &&
|
||||
def->hasBonusOfType(Bonus::FIRE_SHIELD) &&
|
||||
!attackerState->hasBonusOfType(Bonus::FIRE_IMMUNITY) &&
|
||||
CStack::isMeleeAttackPossible(attackerState.get(), def) // attacked needs to be adjacent to defender for fire shield to trigger (e.g. Dragon Breath attack)
|
||||
)
|
||||
{
|
||||
//TODO: use damage with bonus but without penalties
|
||||
auto fireShieldDamage = (std::min<int64_t>(def->getAvailableHealth(), bsa.damageAmount) * def->valOfBonuses(Bonus::FIRE_SHIELD)) / 100;
|
||||
@@ -4478,7 +4478,6 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
case EActionType::SHOOT: //shoot
|
||||
case EActionType::CATAPULT: //catapult
|
||||
case EActionType::STACK_HEAL: //healing with First Aid Tent
|
||||
case EActionType::DAEMON_SUMMONING:
|
||||
case EActionType::MONSTER_SPELL:
|
||||
|
||||
if (!stack)
|
||||
@@ -4648,12 +4647,12 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
|
||||
logGlobal->trace("%s will attack %s", stack->nodeName(), destinationStack->nodeName());
|
||||
|
||||
if(stack->getPosition() != attackPos //we wasn't able to reach destination tile
|
||||
&& !(stack->doubleWide() && (stack->getPosition() == attackPos.cloneInDirection(stack->destShiftDir(), false))) //nor occupy specified hex
|
||||
if(stack->getPosition() != attackPos
|
||||
&& !(stack->doubleWide() && (stack->getPosition() == attackPos.cloneInDirection(stack->destShiftDir(), false)))
|
||||
)
|
||||
{
|
||||
complain("We cannot move this stack to its destination " + stack->getCreature()->namePl);
|
||||
ok = false;
|
||||
// we were not able to reach destination tile, nor occupy specified hex
|
||||
// abort attack attempt, but treat this case as legal - we may have stepped onto a quicksands/mine
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4689,8 +4688,8 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
makeAttack(destinationStack, stack, 0, stack->getPosition(), true, false, true);
|
||||
}
|
||||
|
||||
//move can cause death, eg. by walking into the moat, first strike can cause death as well
|
||||
if(stack->alive() && destinationStack->alive())
|
||||
//move can cause death, eg. by walking into the moat, first strike can cause death or paralysis/petrification
|
||||
if(stack->alive() && !stack->hasBonusOfType(Bonus::NOT_ACTIVE) && destinationStack->alive())
|
||||
{
|
||||
makeAttack(stack, destinationStack, (i ? 0 : distance), destinationTile, i==0, false, false);//no distance travelled on second attack
|
||||
}
|
||||
@@ -4923,13 +4922,13 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
switch(attackedPart)
|
||||
{
|
||||
case EWallPart::KEEP:
|
||||
posRemove = -2;
|
||||
posRemove = BattleHex::CASTLE_CENTRAL_TOWER;
|
||||
break;
|
||||
case EWallPart::BOTTOM_TOWER:
|
||||
posRemove = -3;
|
||||
posRemove = BattleHex::CASTLE_BOTTOM_TOWER;
|
||||
break;
|
||||
case EWallPart::UPPER_TOWER:
|
||||
posRemove = -4;
|
||||
posRemove = BattleHex::CASTLE_UPPER_TOWER;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5012,58 +5011,6 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EActionType::DAEMON_SUMMONING:
|
||||
//TODO: From Strategija:
|
||||
//Summon Demon is a level 2 spell.
|
||||
{
|
||||
if(target.size() < 1)
|
||||
{
|
||||
complain("Destination required for summon action.");
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const CStack * summoner = gs->curB->battleGetStackByID(ba.stackNumber);
|
||||
const CStack * destStack = gs->curB->battleGetStackByPos(target.at(0).hexValue, false);
|
||||
|
||||
CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type()(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream!
|
||||
|
||||
ui64 risedHp = summoner->getCount() * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, summonedType.toEnum());
|
||||
ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;
|
||||
|
||||
ui64 canRiseHp = std::min(targetHealth, risedHp);
|
||||
ui32 canRiseAmount = static_cast<ui32>(canRiseHp / summonedType.toCreature()->MaxHealth());
|
||||
|
||||
battle::UnitInfo info;
|
||||
info.id = gs->curB->battleNextUnitId();
|
||||
info.count = std::min(canRiseAmount, destStack->baseAmount);
|
||||
info.type = summonedType;
|
||||
info.side = summoner->side;
|
||||
info.position = gs->curB->getAvaliableHex(summonedType, summoner->side, destStack->getPosition());
|
||||
info.summoned = false;
|
||||
|
||||
BattleUnitsChanged addUnits;
|
||||
addUnits.changedStacks.emplace_back(info.id, UnitChanges::EOperation::ADD);
|
||||
info.save(addUnits.changedStacks.back().data);
|
||||
|
||||
if(info.count > 0) //there's rare possibility single creature cannot rise desired type
|
||||
{
|
||||
auto wrapper = wrapAction(ba);
|
||||
|
||||
BattleUnitsChanged removeUnits;
|
||||
removeUnits.changedStacks.emplace_back(destStack->unitId(), UnitChanges::EOperation::REMOVE);
|
||||
sendAndApply(&removeUnits);
|
||||
sendAndApply(&addUnits);
|
||||
|
||||
BattleSetStackProperty ssp;
|
||||
ssp.stackID = ba.stackNumber;
|
||||
ssp.which = BattleSetStackProperty::CASTS; //reduce number of casts
|
||||
ssp.val = -1;
|
||||
ssp.absolute = false;
|
||||
sendAndApply(&ssp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EActionType::MONSTER_SPELL:
|
||||
{
|
||||
auto wrapper = wrapAction(ba);
|
||||
@@ -5095,7 +5042,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ba.actionType == EActionType::DAEMON_SUMMONING || ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
|
||||
if(ba.actionType == EActionType::WAIT || ba.actionType == EActionType::DEFEND
|
||||
|| ba.actionType == EActionType::SHOOT || ba.actionType == EActionType::MONSTER_SPELL)
|
||||
handleDamageFromObstacle(stack);
|
||||
if(ba.stackNumber == gs->curB->activeStack || battleResult.get()) //active stack has moved or battle has finished
|
||||
@@ -5469,33 +5416,29 @@ bool CGameHandler::handleDamageFromObstacle(const CStack * curStack, bool stackI
|
||||
if(!sp)
|
||||
COMPLAIN_RET("Invalid obstacle instance");
|
||||
|
||||
// For the hidden spell created obstacles, e.g. QuickSand, it should be revealed after taking damage
|
||||
ObstacleChanges changeInfo;
|
||||
changeInfo.id = spellObstacle->uniqueID;
|
||||
if (oneTimeObstacle)
|
||||
changeInfo.operation = ObstacleChanges::EOperation::ACTIVATE_AND_REMOVE;
|
||||
else
|
||||
changeInfo.operation = ObstacleChanges::EOperation::ACTIVATE_AND_UPDATE;
|
||||
|
||||
SpellCreatedObstacle changedObstacle;
|
||||
changedObstacle.uniqueID = spellObstacle->uniqueID;
|
||||
changedObstacle.revealed = true;
|
||||
|
||||
changeInfo.data.clear();
|
||||
JsonSerializer ser(nullptr, changeInfo.data);
|
||||
ser.serializeStruct("obstacle", changedObstacle);
|
||||
|
||||
BattleObstaclesChanged bocp;
|
||||
bocp.changes.emplace_back(changeInfo);
|
||||
sendAndApply(&bocp);
|
||||
|
||||
spells::BattleCast battleCast(gs->curB, &caster, spells::Mode::HERO, sp);
|
||||
battleCast.applyEffects(spellEnv, spells::Target(1, spells::Destination(curStack)), true);
|
||||
|
||||
if(oneTimeObstacle)
|
||||
{
|
||||
removeObstacle(*obstacle);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For the hidden spell created obstacles, e.g. QuickSand, it should be revealed after taking damage
|
||||
ObstacleChanges changeInfo;
|
||||
changeInfo.id = spellObstacle->uniqueID;
|
||||
changeInfo.operation = ObstacleChanges::EOperation::UPDATE;
|
||||
|
||||
SpellCreatedObstacle changedObstacle;
|
||||
changedObstacle.uniqueID = spellObstacle->uniqueID;
|
||||
changedObstacle.revealed = true;
|
||||
|
||||
changeInfo.data.clear();
|
||||
JsonSerializer ser(nullptr, changeInfo.data);
|
||||
ser.serializeStruct("obstacle", changedObstacle);
|
||||
|
||||
BattleObstaclesChanged bocp;
|
||||
bocp.changes.emplace_back(changeInfo);
|
||||
sendAndApply(&bocp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(obstacle->obstacleType == CObstacleInstance::MOAT)
|
||||
@@ -7237,7 +7180,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
||||
void CGameHandler::removeObstacle(const CObstacleInstance & obstacle)
|
||||
{
|
||||
BattleObstaclesChanged obsRem;
|
||||
obsRem.changes.emplace_back(obstacle.uniqueID, BattleChanges::EOperation::REMOVE);
|
||||
obsRem.changes.emplace_back(obstacle.uniqueID, ObstacleChanges::EOperation::REMOVE);
|
||||
sendAndApply(&obsRem);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user