From ba017c443d6fb3847696a892203f4a68388cffd7 Mon Sep 17 00:00:00 2001 From: AlexVinS Date: Sat, 7 Nov 2015 11:42:06 +0300 Subject: [PATCH] Start from diff of pull request #124 from vcmi/issue/1372 --- client/battle/CBattleInterface.cpp | 15 +++- lib/GameConstants.cpp | 1 + lib/GameConstants.h | 1 + lib/NetPacksLib.cpp | 2 +- server/CGameHandler.cpp | 112 ++++++++++++++++++++++------- server/CGameHandler.h | 9 ++- 6 files changed, 107 insertions(+), 33 deletions(-) diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 83bf8fde7..d43467c3f 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -2163,7 +2163,15 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) break; case RISE_DEMONS: if (shere && ourStack && !shere->alive()) - legalAction = true; + { + if(!(shere->hasBonusOfType(Bonus::UNDEAD) + || shere->hasBonusOfType(Bonus::NON_LIVING) + || vstd::contains(shere->state, EBattleStackState::SUMMONED) + || vstd::contains(shere->state, EBattleStackState::CLONED) + || shere->hasBonusOfType(Bonus::SIEGE_WEAPON) + )) + legalAction = true; + } break; } if (legalAction) @@ -2320,7 +2328,10 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType) break; case RISE_DEMONS: cursorType = ECursor::SPELLBOOK; - realizeAction = [=]{ giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID); }; + realizeAction = [=] + { + giveCommand(Battle::DAEMON_SUMMONING, myNumber, activeStack->ID); + }; break; case CATAPULT: cursorFrame = ECursor::COMBAT_SHOOT_CATAPULT; diff --git a/lib/GameConstants.cpp b/lib/GameConstants.cpp index 864b1aba2..712cdd722 100644 --- a/lib/GameConstants.cpp +++ b/lib/GameConstants.cpp @@ -19,6 +19,7 @@ #include "spells/CSpellHandler.h" const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2); +const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(255); const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253); const PlayerColor PlayerColor::UNFLAGGABLE = PlayerColor(254); const PlayerColor PlayerColor::NEUTRAL = PlayerColor(255); diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 623eb29f5..1ece2170a 100644 --- a/lib/GameConstants.h +++ b/lib/GameConstants.h @@ -236,6 +236,7 @@ class SlotID : public BaseForID friend class CNonConstInfoCallback; DLL_LINKAGE static const SlotID COMMANDER_SLOT_PLACEHOLDER; + DLL_LINKAGE static const SlotID SUMMONED_SLOT_PLACEHOLDER; ///curB->generateNewStack(csbd, attacker, SlotID(255), pos); //TODO: netpacks? + CStack * addedStack = gs->curB->generateNewStack(csbd, attacker, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks? if (summoned) addedStack->state.insert(EBattleStackState::SUMMONED); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 368201a81..30cdeab52 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -662,8 +662,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer sendAndApply(&cs); } - cab1.takeFromArmy(this); - cab2.takeFromArmy(this); //take casualties after battle is deleted + cab1.updateArmy(this); + cab2.updateArmy(this); //take casualties after battle is deleted //if one hero has lost we will erase him if(battleResult.data->winner!=0 && hero1) @@ -3907,18 +3907,16 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) //TODO: From Strategija: //Summon Demon is a level 2 spell. { - StartAction start_action(ba); - sendAndApply(&start_action); - const CStack *summoner = gs->curB->battleGetStackByID(ba.stackNumber), *destStack = gs->curB->battleGetStackByPos(ba.destinationTile, false); + CreatureID summonedType(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype);//in case summoner can summon more than one type of monsters... scream! BattleStackAdded bsa; bsa.attacker = summoner->attackerOwned; - bsa.creID = CreatureID(summoner->getBonusLocalFirst(Selector::type(Bonus::DAEMON_SUMMONING))->subtype); //in case summoner can summon more than one type of monsters... scream! + bsa.creID = summonedType; ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID.toEnum()); - ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount; + ui64 targetHealth = destStack->getCreature()->MaxHealth() * destStack->baseAmount;//todo: ignore AGE effect ui64 canRiseHp = std::min(targetHealth, risedHp); ui32 canRiseAmount = canRiseHp / VLC->creh->creatures.at(bsa.creID)->MaxHealth(); @@ -3930,6 +3928,9 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) if (bsa.amount) //there's rare possibility single creature cannot rise desired type { + StartAction start_action(ba); + sendAndApply(&start_action); + BattleStacksRemoved bsr; //remove body bsr.stackIDs.insert(destStack->ID); sendAndApply(&bsr); @@ -3941,9 +3942,9 @@ bool CGameHandler::makeBattleAction( BattleAction &ba ) ssp.val = -1; ssp.absolute = false; sendAndApply(&ssp); - } - sendAndApply(&end_action); + sendAndApply(&end_action); + } break; } case Battle::MONSTER_SPELL: @@ -5878,13 +5879,52 @@ void CGameHandler::duelFinished() return; } -CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat) +CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat): + army(_army) { heroWithDeadCommander = ObjectInstanceID(); PlayerColor color = army->tempOwner; if(color == PlayerColor::UNFLAGGABLE) color = PlayerColor::NEUTRAL; + + auto killStack = [&, this](const SlotID slot, const CStackInstance * instance) + { + StackLocation sl(army, slot); + newStackCounts.push_back(TStackAndItsNewCount(sl, 0)); + if(nullptr == instance) + return; + auto c = dynamic_cast (instance); + if (c) //switch commander status to dead + { + auto h = dynamic_cast (army); + if (h && h->commander == c) + heroWithDeadCommander = army->id; //TODO: unify commander handling + } + }; + + //1. Find removed stacks. + for(const auto & slotInfo : army->stacks) + { + const SlotID slot = slotInfo.first; + const CStackInstance * instance = slotInfo.second; + + if(nullptr != instance)//just in case + { + bool found = false; + for(const CStack * sta : bat->stacks) + { + if(sta->base == instance) + { + found = true; + break; + } + } + //stack in this slot was removed == it is dead + if(!found) + killStack(slot, instance); + } + } for(CStack *st : bat->stacks) { @@ -5896,7 +5936,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI //FIXME: this info is also used in BattleInfo::calculateCasualties, refactor st->count = std::max (0, st->count - st->resurrected); - if (!st->count && !st->base) //we can imagine stacks of war mahcines that are not spawned by artifacts? + if (!st->count && !st->base) //we can imagine stacks of war machines that are not spawned by artifacts? { auto warMachine = VLC->arth->creatureToMachineID(st->type->idNumber); //catapult artifact remain even if "creature" killed in siege @@ -5907,29 +5947,32 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI removedWarMachines.push_back (ArtifactLocation(hero, hero->getArtPos(warMachine, true))); } } - - if(!army->slotEmpty(st->slot) && st->count < army->getStackCount(st->slot)) + + if(army->slotEmpty(st->slot)) { - StackLocation sl(army, st->slot); - if(st->alive()) - newStackCounts.push_back(std::pair(sl, st->count)); - else - newStackCounts.push_back(std::pair(sl, 0)); - } - if (st->base && !st->count) - { - auto c = dynamic_cast (st->base); - if (c) //switch commander status to dead + if(st->slot == SlotID::SUMMONED_SLOT_PLACEHOLDER && !vstd::contains(st->state, EBattleStackState::SUMMONED) && st->alive() && st->count > 0) { - auto h = dynamic_cast (army); - if (h && h->commander == c) - heroWithDeadCommander = army->id; //TODO: unify commander handling + //this stack was permanently summoned + const CreatureID summonedType = st->type->idNumber; + summoned[summonedType] += st->count; + } + } + else + { + if(st->count == 0 || !st->alive()) + { + killStack(st->slot, st->base); + } + else if(st->count < army->getStackCount(st->slot)) + { + StackLocation sl(army, st->slot); + newStackCounts.push_back(TStackAndItsNewCount(sl, st->count)); } } } } -void CasualtiesAfterBattle::takeFromArmy(CGameHandler *gh) +void CasualtiesAfterBattle::updateArmy(CGameHandler *gh) { for(TStackAndItsNewCount &ncount : newStackCounts) { @@ -5938,6 +5981,21 @@ void CasualtiesAfterBattle::takeFromArmy(CGameHandler *gh) else gh->eraseStack(ncount.first, true); } + for(auto summoned_iter : summoned) + { + SlotID slot = army->getSlotFor(summoned_iter.first); + if(slot.validSlot()) + { + StackLocation location(army, slot); + gh->addToSlot(location, summoned_iter.first.toCreature(), summoned_iter.second); + } + else + { + //even if it will be possible to summon anything permanently it should be checked for free slot + //necromancy is handled separately + gh->complain("No free slot to put summoned creature"); + } + } for (auto al : removedWarMachines) { gh->removeArtifact(al); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 104979140..e8ae1153b 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -69,13 +69,16 @@ public: struct CasualtiesAfterBattle { typedef std::pair TStackAndItsNewCount; + typedef std::map TSummoned; enum {ERASE = -1}; + const CArmedInstance * army; std::vector newStackCounts; std::vector removedWarMachines; - ObjectInstanceID heroWithDeadCommander; //TODO: unify stack loactions + TSummoned summoned; + ObjectInstanceID heroWithDeadCommander; //TODO: unify stack locations - CasualtiesAfterBattle(const CArmedInstance *army, BattleInfo *bat); - void takeFromArmy(CGameHandler *gh); + CasualtiesAfterBattle(const CArmedInstance * _army, BattleInfo *bat); + void updateArmy(CGameHandler *gh); }; class CGameHandler : public IGameCallback, CBattleInfoCallback