Browse Source

Fixed all issues with Resurrection: #123, #1358

DjWarmonger 12 years ago
parent
commit
5f310aa20f

+ 18 - 26
client/battle/CBattleInterface.cpp

@@ -1750,25 +1750,14 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack)
 				for (Bonus * spellBonus : spellBonuses)
 				{
 					spell = CGI->spellh->spells[spellBonus->subtype];
-					if (spell->isRisingSpell())
+					switch (spellBonus->subtype)
 					{
-						possibleActions.push_back (RISING_SPELL);
-					}
-					//possibleActions.push_back (NO_LOCATION);
-					//possibleActions.push_back (ANY_LOCATION);
-					//TODO: allow stacks cast aimed spells
-					//possibleActions.push_back (OTHER_SPELL);
-					else
-					{
-						switch (spellBonus->subtype)
-						{
-							case SpellID::REMOVE_OBSTACLE:
-								possibleActions.push_back (OBSTACLE);
-								break;
-							default:
-								possibleActions.push_back (selectionTypeByPositiveness (*spell));
-								break;
-						}
+						case SpellID::REMOVE_OBSTACLE:
+							possibleActions.push_back (OBSTACLE);
+							break;
+						default:
+							possibleActions.push_back (selectionTypeByPositiveness (*spell));
+							break;
 					}
 
 				}
@@ -2194,13 +2183,18 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 					legalAction = true;
 				break;
 			case FRIENDLY_CREATURE_SPELL:
-				if (shere && shere->alive() && ourStack && isCastingPossibleHere (sactive, shere, myNumber))
-					legalAction = true;
-				break;
-			case RISING_SPELL:
-				if (shere && shere->canBeHealed() && ourStack && isCastingPossibleHere (sactive, shere, myNumber)) //TODO: at least one stack has to be raised by resurrection / animate dead
-					legalAction = true;
+			{
+				if (isCastingPossibleHere (sactive, shere, myNumber)); //need to be called before sp is determined
+				{
+					bool rise = false; //TODO: can you imagine rising hostile creatures?
+					sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo];
+					if (sp && sp->isRisingSpell())
+							rise = true;
+					if (shere && (shere->alive() || rise) && ourStack)
+						legalAction = true;
+				}
 				break;
+			}
 			case RANDOM_GENIE_SPELL:
 			{
 				if (shere && ourStack && shere != sactive) //only positive spells for other allied creatures
@@ -2379,7 +2373,6 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				break;
 			case HOSTILE_CREATURE_SPELL:
 			case FRIENDLY_CREATURE_SPELL:
-			case RISING_SPELL:
 				sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
 				consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s
 				switch (sp->id)
@@ -2452,7 +2445,6 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 		{
 			case HOSTILE_CREATURE_SPELL:
 			case FRIENDLY_CREATURE_SPELL:
-			case RISING_SPELL:
 			case RANDOM_GENIE_SPELL:
 				cursorFrame = ECursor::COMBAT_BLOCKED;
 				consoleMsg = CGI->generaltexth->allTexts[23];

+ 8 - 7
lib/BattleState.cpp

@@ -112,7 +112,7 @@ void BattleInfo::calculateCasualties( std::map<ui32,si32> *casualties ) const
 	for(auto & elem : stacks)//setting casualties
 	{
 		const CStack * const st = elem;
-		si32 killed = (st->alive() ? st->baseAmount - st->count : st->baseAmount);
+		si32 killed = (st->alive() ? (st->baseAmount - st->count + st->resurrected) : st->baseAmount);
 		vstd::amax(killed, 0);
 		if(killed)
 			casualties[!st->attackerOwned][st->getCreature()->idNumber] += killed;
@@ -161,9 +161,9 @@ CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor &base, bool at
 }
 
 //All spells casted by hero 9resurrection, cure, sacrifice)
-ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack) const
+ui32 CBattleInfoCallback::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack) const
 {
-	bool resurrect = resurrects(spell->id);
+	bool resurrect = spell->isRisingSpell();
 	int healedHealth;
 	if (spell->id == SpellID::SACRIFICE && sacrificedStack)
 		healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count;
@@ -173,15 +173,15 @@ ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell *
 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
 }
 //Archangel
-ui32 BattleInfo::calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const
+ui32 CBattleInfoCallback::calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const
 {
-	bool resurrect = resurrects(spell->id);
+	bool resurrect = spell->isRisingSpell();
 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
 }
 //Casted by stack, no hero bonus applied
-ui32 BattleInfo::calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const
+ui32 CBattleInfoCallback::calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const
 {
-	bool resurrect = resurrects(spell->id);
+	bool resurrect = spell->isRisingSpell();
 	int healedHealth = usedSpellPower * spell->power + spell->powers[spellSchoolLevel];
 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
 }
@@ -893,6 +893,7 @@ void CStack::postInit()
 	shots = getCreature()->valOfBonuses(Bonus::SHOTS);
 	counterAttacks = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
 	casts = valOfBonuses(Bonus::CASTS);
+	resurrected = 0;
 }
 
 ui32 CStack::level() const

+ 2 - 5
lib/BattleState.h

@@ -125,14 +125,10 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
 	CStack * generateNewStack(const CStackBasicDescriptor &base, bool attackerOwned, SlotID slot, BattleHex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
 	int getIdForNewStack() const; //suggest a currently unused ID that'd suitable for generating a new stack
 	//std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
-	ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = nullptr) const; //Sacrifice
-	ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel
-	ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //healing spells casted by stacks
 	bool resurrects(SpellID spellid) const; //TODO: move it to spellHandler?
 
 	const CGHeroInstance * getHero(PlayerColor player) const; //returns fighting hero that belongs to given player
 
-
 	std::vector<ui32> calculateResistedStacks(const CSpell * sp, const CGHeroInstance * caster, const CGHeroInstance * hero2, const std::vector<const CStack*> affectedCreatures, PlayerColor casterSideOwner, ECastingMode::ECastingMode mode, int usedSpellPower, int spellLevel) const;
 
 	const CStack * battleGetStack(BattleHex pos, bool onlyAlive); //returns stack at given tile
@@ -165,6 +161,7 @@ public:
 	ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1)
 	si16 shots; //how many shots left
 	ui8 casts; //how many casts left
+	TQuantity resurrected; // these units will be taken back after battle is over
 
 	std::set<EBattleStackState::EBattleStackState> state;
 	//overrides
@@ -218,7 +215,7 @@ public:
 		h & static_cast<CBonusSystemNode&>(*this);
 		h & static_cast<CStackBasicDescriptor&>(*this);
 		h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks
-			& shots & casts & count;
+			& shots & casts & count & resurrected;
 
 		const CArmedInstance *army = (base ? base->armyObj : nullptr);
 		SlotID slot = (base ? base->armyObj->findStack(base) : SlotID());

+ 8 - 6
lib/CBattleCallback.cpp

@@ -503,8 +503,8 @@ const CStack* CBattleInfoCallback::battleGetStackByPos(BattleHex pos, bool onlyA
 {
 	RETURN_IF_NOT_BATTLE(nullptr);
 	for(auto s : battleGetAllStacks())
-		if(vstd::contains(s->getHexes(), pos)  &&  (!onlyAlive || s->alive()))
-			return s;
+		if(vstd::contains(s->getHexes(), pos) && (!onlyAlive || s->alive()))
+				return s;
 
 	return nullptr;
 }
@@ -1562,7 +1562,8 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
 
 		if (spell->isRisingSpell())
 		{
-			if (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised
+			auto maxHealth = calculateHealedHP (caster, spell, subject);
+			if (subject->count >= subject->baseAmount || maxHealth < subject->MaxHealth()) //must be able to rise at least one full creature
 				return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
 		}
 
@@ -1663,7 +1664,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
 		{
 			const CGHeroInstance * caster = battleGetFightingHero(side);
 			bool targetExists = false;
-			for(const CStack * stack : battleAliveStacks())
+			for(const CStack * stack : battleGetAllStacks()) //dead stacks will be immune anyway
 			{
 				switch (spell->positiveness)
 				{
@@ -1941,8 +1942,9 @@ std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell *
 
 	const ui8 attackerSide = playerToSide(attackerOwner) == 1;
 	const auto attackedHexes = spell->rangeInHexes(destinationTile, skillLevel, attackerSide);
-	const bool onlyAlive = spell->id != SpellID::RESURRECTION && spell->id != SpellID::ANIMATE_DEAD; //when casting resurrection or animate dead we should be allow to select dead stack
-	//fixme: what about other rising spells (Sacrifice) ?
+	const bool onlyAlive = !spell->isRisingSpell(); //when casting resurrection or animate dead we should be allow to select dead stack
+
+	//TODO: more generic solution for mass spells
 	if(spell->id == SpellID::DEATH_RIPPLE || spell->id == SpellID::DESTROY_UNDEAD || spell->id == SpellID::ARMAGEDDON)
 	{
 		for(const CStack *stack : battleGetAllStacks())

+ 3 - 0
lib/CBattleCallback.h

@@ -260,6 +260,9 @@ public:
 	std::vector<BattleHex> battleGetPossibleTargets(PlayerColor player, const CSpell *spell) const;
 	ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
 	ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
+	ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = nullptr) const; //Sacrifice
+	ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel
+	ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //healing spells casted by stacks
 	std::set<const CStack*> getAffectedCreatures(const CSpell * s, int skillLevel, PlayerColor attackerOwner, BattleHex destinationTile); //calculates stack affected by given spell
 
 	SpellID battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode) const;

+ 2 - 2
lib/NetPacksLib.cpp

@@ -1403,12 +1403,12 @@ DLL_LINKAGE void StacksHealedOrResurrected::applyGs( CGameState *gs )
 		if(resurrected)
 		{
 			changedStack->state.insert(EBattleStackState::ALIVE);
-			if(elem.lowLevelResurrection)
-				changedStack->state.insert(EBattleStackState::SUMMONED); //TODO: different counter for rised units
 		}
 		//int missingHPfirst = changedStack->MaxHealth() - changedStack->firstHPleft;
 		int 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())
 		{

+ 3 - 0
server/CGameHandler.cpp

@@ -6266,6 +6266,9 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CArmedInstance *army, BattleI
 		if(vstd::contains(st->state, EBattleStackState::SUMMONED)) //don't take into account summoned stacks
 			continue;
 
+		//FIXME: this info is also used in BattleInfo::calculateCasualties, refactor
+		st->count = std::max (0, st->count - st->resurrected);
+
 		if(st->owner==color && !army->slotEmpty(st->slot) && st->count < army->getStackCount(st->slot))
 		{
 			StackLocation sl(army, st->slot);