1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-17 00:07:41 +02:00

Unified CStack ammo, casts and counterattacks

* it is possible now to add casts and shoots OTF (f.e. with spell bonus)

Centralized stack 'ammo' loading from bonus system.
* introduced small proxy class for local bonus cache
(no need to use global cache if particular selector used on node only in one place)
* handle killing resurrected creatures
* use IBonusBearer::MaxHealth() where possible
* Fixed https://bugs.vcmi.eu/view.php?id=2486
* Possible fix for 0 HP after resurrection.
* Hack-fixed https://bugs.vcmi.eu/view.php?id=2584
* Unified CStack health API
* Use CHealth for CStack count and health points
* increased SERIALIZATION_VERSION
This commit is contained in:
AlexVinS
2017-07-04 14:24:46 +03:00
parent 3634af10ba
commit 4f14f22d3a
33 changed files with 1128 additions and 636 deletions

View File

@ -1248,12 +1248,11 @@ DLL_LINKAGE void BattleNextRound::applyGs(CGameState *gs)
s->state -= EBattleStackState::HAD_MORALE;
s->state -= EBattleStackState::FEAR;
s->state -= EBattleStackState::DRAINED_MANA;
s->counterAttacksPerformed = 0;
s->counterAttacksTotalCache = 0;
s->counterAttacks.reset();
// new turn effects
s->updateBonuses(Bonus::NTurns);
if(s->alive() && vstd::contains(s->state, EBattleStackState::CLONED))
if(s->alive() && s->isClone())
{
//cloned stack has special lifetime marker
//check it after bonuses updated in battleTurnPassed()
@ -1281,36 +1280,40 @@ DLL_LINKAGE void BattleSetActiveStack::applyGs(CGameState *gs)
DLL_LINKAGE void BattleTriggerEffect::applyGs(CGameState *gs)
{
CStack *st = gs->curB->getStack(stackID);
switch (effect)
CStack * st = gs->curB->getStack(stackID);
assert(st);
switch(effect)
{
case Bonus::HP_REGENERATION:
st->firstHPleft += val;
vstd::amin (st->firstHPleft, (ui32)st->MaxHealth());
break;
case Bonus::MANA_DRAIN:
{
CGHeroInstance * h = gs->getHero(ObjectInstanceID(additionalInfo));
st->state.insert (EBattleStackState::DRAINED_MANA);
h->mana -= val;
vstd::amax(h->mana, 0);
break;
}
case Bonus::POISON:
{
auto b = st->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT, SpellID::POISON)
.And(Selector::type(Bonus::STACK_HEALTH)));
if (b)
b->val = val;
break;
}
case Bonus::ENCHANTER:
break;
case Bonus::FEAR:
st->state.insert(EBattleStackState::FEAR);
break;
default:
logNetwork->warnStream() << "Unrecognized trigger effect type "<< effect;
case Bonus::HP_REGENERATION:
{
int32_t toHeal = val;
CHealth health = st->healthAfterHealed(toHeal, EHealLevel::HEAL, EHealPower::PERMANENT);
st->setHealth(health);
break;
}
case Bonus::MANA_DRAIN:
{
CGHeroInstance * h = gs->getHero(ObjectInstanceID(additionalInfo));
st->state.insert (EBattleStackState::DRAINED_MANA);
h->mana -= val;
vstd::amax(h->mana, 0);
break;
}
case Bonus::POISON:
{
auto b = st->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT, SpellID::POISON)
.And(Selector::type(Bonus::STACK_HEALTH)));
if (b)
b->val = val;
break;
}
case Bonus::ENCHANTER:
break;
case Bonus::FEAR:
st->state.insert(EBattleStackState::FEAR);
break;
default:
logNetwork->warnStream() << "Unrecognized trigger effect type "<< effect;
}
}
@ -1393,11 +1396,14 @@ void BattleStackMoved::applyGs(CGameState *gs)
DLL_LINKAGE void BattleStackAttacked::applyGs(CGameState *gs)
{
CStack *at = gs->curB->getStack(stackAttacked);
CStack * at = gs->curB->getStack(stackAttacked);
assert(at);
at->popBonuses(Bonus::UntilBeingAttacked);
at->count = newAmount;
at->firstHPleft = newHP;
if(willRebirth())
at->health.reset();//kill stack first
else
at->setHealth(newHealth);
if(killed())
{
@ -1414,16 +1420,29 @@ DLL_LINKAGE void BattleStackAttacked::applyGs(CGameState *gs)
}
}
//life drain handling
for (auto & elem : healedStacks)
{
for(auto & elem : healedStacks)
elem.applyGs(gs);
}
if (willRebirth())
if(willRebirth())
{
at->casts--;
at->state.insert(EBattleStackState::ALIVE); //hmm?
//TODO: handle rebirth with StacksHealedOrResurrected
at->casts.use();
at->state.insert(EBattleStackState::ALIVE);
at->setHealth(newHealth);
//removing all spells effects
auto selector = [](const Bonus * b)
{
//Special case: DISRUPTING_RAY is "immune" to dispell
//Other even PERMANENT effects can be removed
if(b->source == Bonus::SPELL_EFFECT)
return b->sid != SpellID::DISRUPTING_RAY;
else
return false;
};
at->popBonuses(selector);
}
if (cloneKilled())
if(cloneKilled())
{
//"hide" killed creatures instead so we keep info about it
at->makeGhost();
@ -1437,35 +1456,20 @@ DLL_LINKAGE void BattleStackAttacked::applyGs(CGameState *gs)
//killed summoned creature should be removed like clone
if(killed() && vstd::contains(at->state, EBattleStackState::SUMMONED))
{
at->makeGhost();
}
}
DLL_LINKAGE void BattleAttack::applyGs(CGameState *gs)
DLL_LINKAGE void BattleAttack::applyGs(CGameState * gs)
{
CStack *attacker = gs->curB->getStack(stackAttacking);
CStack * attacker = gs->curB->getStack(stackAttacking);
assert(attacker);
if(counter())
attacker->counterAttacksPerformed++;
attacker->counterAttacks.use();
if(shot())
{
//don't remove ammo if we have a working ammo cart
bool hasAmmoCart = false;
for(const CStack * st : gs->curB->stacks)
{
if(st->owner == attacker->owner && st->getCreature()->idNumber == CreatureID::AMMO_CART && st->alive())
{
hasAmmoCart = true;
break;
}
}
attacker->shots.use();
if (!hasAmmoCart)
{
attacker->shots--;
}
}
for(BattleStackAttacked & stackAttacked : bsa)
stackAttacked.applyGs(gs);
@ -1620,7 +1624,8 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs(CGameState *gs)
{
for(auto & elem : healedStacks)
{
CStack * changedStack = gs->curB->getStack(elem.stackID, false);
CStack * changedStack = gs->curB->getStack(elem.stackId, false);
assert(changedStack);
//checking if we resurrect a stack that is under a living stack
auto accessibility = gs->curB->getAccesibility();
@ -1635,29 +1640,14 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs(CGameState *gs)
bool resurrected = !changedStack->alive(); //indicates if stack is resurrected or just healed
if(resurrected)
{
if(changedStack->count > 0 || changedStack->firstHPleft > 0)
if(changedStack->getCount() > 0 || changedStack->getFirstHPleft() > 0)
logGlobal->warn("Dead stack %s with positive total HP %d", changedStack->nodeName(), changedStack->totalHealth());
changedStack->state.insert(EBattleStackState::ALIVE);
}
int res;
if(canOverheal) //for example WoG ghost soul steal ability allows getting more units than before battle
res = elem.healedHP / changedStack->MaxHealth();
else
res = std::min(elem.healedHP / changedStack->MaxHealth() , changedStack->baseAmount - changedStack->count);
changedStack->count += res;
if(elem.lowLevelResurrection)
changedStack->resurrected += res;
changedStack->firstHPleft += elem.healedHP - res * changedStack->MaxHealth();
if(changedStack->firstHPleft > changedStack->MaxHealth())
{
changedStack->firstHPleft -= changedStack->MaxHealth();
if(changedStack->baseAmount > changedStack->count)
{
changedStack->count += 1;
}
}
vstd::amin(changedStack->firstHPleft, changedStack->MaxHealth());
changedStack->setHealth(elem);
if(resurrected)
{
//removing all spells effects
@ -1675,7 +1665,7 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs(CGameState *gs)
else if(cure)
{
//removing all effects from negative spells
auto selector = [](const Bonus* b)
auto selector = [](const Bonus * b)
{
//Special case: DISRUPTING_RAY is "immune" to dispell
//Other even PERMANENT effects can be removed
@ -1796,7 +1786,7 @@ DLL_LINKAGE void BattleStacksRemoved::applyGs(CGameState *gs)
DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs)
{
newStackID = 0;
if (!BattleHex(pos).isValid())
if(!BattleHex(pos).isValid())
{
logNetwork->warnStream() << "No place found for new stack!";
return;
@ -1804,33 +1794,32 @@ DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs)
CStackBasicDescriptor csbd(creID, amount);
CStack * addedStack = gs->curB->generateNewStack(csbd, side, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks?
if (summoned)
if(summoned)
addedStack->state.insert(EBattleStackState::SUMMONED);
gs->curB->localInitStack(addedStack);
gs->curB->stacks.push_back(addedStack); //the stack is not "SUMMONED", it is permanent
addedStack->localInit(gs->curB.get());
gs->curB->stacks.push_back(addedStack);
newStackID = addedStack->ID;
}
DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState *gs)
DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState * gs)
{
CStack * stack = gs->curB->getStack(stackID);
switch (which)
switch(which)
{
case CASTS:
{
if (absolute)
stack->casts = val;
if(absolute)
logNetwork->error("Can not change casts in absolute mode");
else
stack->casts += val;
vstd::amax(stack->casts, 0);
stack->casts.use(val);
break;
}
case ENCHANTER_COUNTER:
{
auto & counter = gs->curB->sides[gs->curB->whatSide(stack->owner)].enchanterCounter;
if (absolute)
if(absolute)
counter = val;
else
counter += val;