Selaa lähdekoodia

handling of Rebirth ability for Phoenix.
However, I've got no idea how to restore its animation to alive state.

DjWarmonger 14 vuotta sitten
vanhempi
sitoutus
5269e845fd

+ 1 - 0
client/CBattleInterface.h

@@ -45,6 +45,7 @@ struct SStackAttackedInfo
 	const CStack * attacker; //attacking stack
 	bool byShooting; //if true, stack has been attacked by shooting
 	bool killed; //if true, stack has been killed
+	bool rebirth; //if true, play rebirth animation after all
 };
 
 /// Small struct which contains information about the position and the velocity of a projectile

+ 1 - 1
client/CPlayerInterface.cpp

@@ -796,7 +796,7 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
 			if (defender && !i->isSecondary())
 				battleInt->displayEffect(i->effect, defender->position);
 		}
-		SStackAttackedInfo to_put = {defender, i->damageAmount, i->killedAmount, attacker, LOCPLINT->curAction->actionType==7, i->killed()};
+		SStackAttackedInfo to_put = {defender, i->damageAmount, i->killedAmount, attacker, LOCPLINT->curAction->actionType==7, i->killed(), i->willRebirth()};
 		arg.push_back(to_put);
 
 	}

+ 14 - 12
config/cr_abils.txt

@@ -177,6 +177,8 @@
 + 125 NON_LIVING 0 0 0				//energy elementals shouldn't get morale		
 + 127 NON_LIVING 0 0 0						
 + 129 NON_LIVING 0 0 0						//Crystal Dragons do not fly
++ 131 REBIRTH 20 0 0	//20% of stack is resurrected					
++ 131 CASTS 1 0 0	//Phoenix rebirths once					
 + 132 DRAGON_NATURE 0 0 0			//azure dragon is a dragon			
 - 133 FLYING						
 + 133 DRAGON_NATURE 0 0 0			//crystal dragon is a dragon			
@@ -184,19 +186,19 @@
 + 135 DRAGON_NATURE 0 0 0			//rust dragon is a dragon			
 + 135 ACID_BREATH 25 0 20			//20% chance to do 25 damage			
 + 135 SPELL_AFTER_ATTACK 100 80 0			//always reduce defense			
-+ 136 NO_OBSTACLES_PENALTY 0 0 0			//Enchanter			
-+ 137 NO_DISTANCE_PENALTY 0 0 0			//Sharpshooter			
-+ 137 NO_OBSTACLES_PENALTY 0 0 0					//first aid tent can heal	
-+ 140 DOUBLE_WIDE 0 0 0 			//boar should be treated as double-wide	//Ammo Cart		
-+ 142 DOUBLE_WIDE 0 0 0 			//nomads should be treated as double-wide		//arrow turret	
++ 136 NO_OBSTACLES_PENALTY 0 0 0			//Enchanter		//first aid tent can heal	
++ 137 NO_DISTANCE_PENALTY 0 0 0			//Sharpshooter	//Ammo Cart		
++ 137 NO_OBSTACLES_PENALTY 0 0 0					//arrow turret	
++ 140 DOUBLE_WIDE 0 0 0 			//boar should be treated as double-wide			
++ 142 DOUBLE_WIDE 0 0 0 			//nomads should be treated as double-wide			
 + 144 FULL_HP_REGENERATION 0 0 0 			//troll			
-+ 147 HEALER 0 0 0						
-+ 148 NOT_ACTIVE 0 0 0						
-+ 149 SHOOTER 0 0 0					//Gorynyches fly	
-+ 151 DRAGON_NATURE 0 0 0			//diamond dragon is a dragon			//hell hound doesn't fly
-+ 154 DRAGON_NATURE 0 0 0			//blood dragon is a dragon			//cerberus doesn't fly
-+ 155 DRAGON_NATURE 0 0 0			//darkness dragon is a dragon		//psychic elemental	
-+ 168 FLYING 0 0 0					//magic elemental	
++ 147 HEALER 0 0 0					//Gorynyches fly	
++ 148 NOT_ACTIVE 0 0 0						//hell hound doesn't fly
++ 149 SHOOTER 0 0 0						//cerberus doesn't fly
++ 151 DRAGON_NATURE 0 0 0			//diamond dragon is a dragon		//psychic elemental	
++ 154 DRAGON_NATURE 0 0 0			//blood dragon is a dragon		//magic elemental	
++ 155 DRAGON_NATURE 0 0 0			//darkness dragon is a dragon			
++ 168 FLYING 0 0 0						
 -  46 FLYING		  				
 -  47 FLYING			  			
 - 120 DOUBLE_WIDE						

+ 35 - 12
lib/BattleState.cpp

@@ -7,6 +7,7 @@
 #include <sstream>
 #include <boost/foreach.hpp>
 #include <boost/assign/list_of.hpp>
+#include <boost/random/linear_congruential.hpp>
 
 #include "VCMI_Lib.h"
 #include "CObjectHandler.h"
@@ -25,7 +26,7 @@
  * Full text of license available in license.txt file, in main folder
  *
  */
-
+extern boost::rand48 ran;
 
 const CStack * BattleInfo::getNextStack() const
 {
@@ -845,10 +846,15 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CStack* attacker, THex
 	return attackedCres;
 }
 
-int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower )
+int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower)
 {
-	if(!caster) //TODO: something better
-		return std::max(5, usedSpellPower);
+	if(!caster)
+	{
+		if (!usedSpellPower)
+			return 3; //default duration of all creature spells
+		else
+			return usedSpellPower; //use creature spell power
+	}
 	switch(spell->id)
 	{
 	case 56: //frenzy
@@ -2107,9 +2113,9 @@ void CStack::postInit()
 	firstHPleft = valOfBonuses(Bonus::STACK_HEALTH);
 	shots = getCreature()->valOfBonuses(Bonus::SHOTS);
 	counterAttacks = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
+	casts = valOfBonuses(Bonus::CASTS); //TODO: set them in cr_abils.txt
 	state.insert(ALIVE);  //alive state indication
 
-
 	assert(firstHPleft > 0);
 }
 
@@ -2282,15 +2288,15 @@ void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
 		sf.push_back(makeFeatureVal(Bonus::GENERAL_ATTACK_REDUCTION, Bonus::UNTIL_ATTACK | Bonus::N_TURNS, 0, power, Bonus::SPELL_EFFECT, sse.turnsRemain));
 		sf.back().sid = sse.sid;
 	 	break;
-	case 70: //Stone Gaze //TODO: allow stacks use arbitrary spell power
+	case 70: //Stone Gaze
 	case 74: //Paralyze
 		sf.push_back(makeFeatureVal(Bonus::NOT_ACTIVE, Bonus::UNITL_BEING_ATTACKED | Bonus::N_TURNS, 0, 0, Bonus::SPELL_EFFECT, sse.turnsRemain));
 		sf.back().sid = sse.sid;
 		break;
 	case 71: //Poison
-		sf.push_back(featureGeneratorVT(Bonus::POISON, 0, 30, 3, Bonus::INDEPENDENT_MAX)); //max hp penalty from this source
+		sf.push_back(featureGeneratorVT(Bonus::POISON, 0, 30, sse.turnsRemain, Bonus::INDEPENDENT_MAX)); //max hp penalty from this source
 		sf.back().sid = sse.sid;
-		sf.push_back(featureGeneratorVT(Bonus::STACK_HEALTH, 0, -10, 3, Bonus::PERCENT_TO_ALL));
+		sf.push_back(featureGeneratorVT(Bonus::STACK_HEALTH, 0, -10, sse.turnsRemain, Bonus::PERCENT_TO_ALL));
 		sf.back().sid = sse.sid;
 		break;
 	case 72: //Bind
@@ -2301,17 +2307,17 @@ void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
 	 	sf.back().sid = sse.sid;
 		break;
 	case 73: //Disease
-		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -2 ,3));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, -2 , sse.turnsRemain));
 	 	sf.back().sid = sse.sid;
-		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -2 ,3));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -2 , sse.turnsRemain));
 	 	sf.back().sid = sse.sid;
 		break;
 	case 75: //Age
-		sf.push_back(featureGeneratorVT(Bonus::STACK_HEALTH, 0, -50, 3, Bonus::PERCENT_TO_ALL));
+		sf.push_back(featureGeneratorVT(Bonus::STACK_HEALTH, 0, -50, sse.turnsRemain, Bonus::PERCENT_TO_ALL));
 		sf.back().sid = sse.sid;
 		break;
 	case 80: //Acid Breath
-		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -3, 1));
+		sf.push_back(featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE, -sse.turnsRemain, 1));
 	 	sf.back().sid = sse.sid;
 		sf.back().duration = Bonus::PERMANENT;
 		sf.back().valType = Bonus::ADDITIVE_VALUE;
@@ -2489,6 +2495,23 @@ void CStack::prepareAttacked(BattleStackAttacked &bsa) const
 		bsa.newAmount = 0;
 		bsa.flags |= BattleStackAttacked::KILLED;
 		bsa.killedAmount = count; //we cannot kill more creatures than we have
+
+		int resurrectFactor = valOfBonuses(Bonus::REBIRTH);
+		if (resurrectFactor > 0 && casts) //there must be casts left
+		{
+			int resurrectedCount = base->count * resurrectFactor / 100;
+			if (resurrectedCount)
+				resurrectedCount += ((base->count % resurrectedCount) * resurrectFactor / 100.0f) > ran()%100 ? 1 : 0; //last stack has proportional chance to rebirth
+			else //only one unit
+				resurrectedCount += (base->count * resurrectFactor / 100.0f) > ran()%100 ? 1 : 0;
+			if (hasBonusOfType(Bonus::REBIRTH, 1));
+				amax (resurrectedCount, 1); //resurrect at least one Sacred Phoenix
+			if (resurrectedCount)
+			{
+				bsa.flags |= BattleStackAttacked::REBIRTH;
+				bsa.newAmount = resurrectedCount; //risky?
+			}
+		}
 	}
 	else
 	{

+ 2 - 1
lib/BattleState.h

@@ -144,6 +144,7 @@ public:
 	THex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
 	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
 
 	std::set<ECombatInfo> state;
 	//overrides
@@ -199,7 +200,7 @@ public:
 		h & static_cast<CBonusSystemNode&>(*this);
 		h & static_cast<CStackBasicDescriptor&>(*this);
 		h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks
-			& shots & count;
+			& shots & casts & count;
 
 		TSlot slot = (base ? base->armyObj->findStack(base) : -1);
 		const CArmedInstance *army = (base ? base->armyObj : NULL);

+ 1 - 1
lib/HeroBonus.h

@@ -167,7 +167,7 @@ namespace PrimarySkill
 	BONUS_NAME(SPECIFIC_SPELL_POWER) /* value used for Thunderbolt and Resurrection casted by units, subtype - spell id */\
 	BONUS_NAME(CREATURE_SPELL_POWER) /* value per unit, divided by 100 (so faerie Dragons have 800)*/ \
 	BONUS_NAME(CREATURE_ENCHANT_POWER) /* total duration of spells casted by creature */ \
-	BONUS_NAME(REBIRTH) /* val - percent of life restored */
+	BONUS_NAME(REBIRTH) /* val - percent of life restored, subtype = 0 - regular, 1 - at least one unit (sacred Phoenix) */
 
 /// Struct for handling bonuses of several types. Can be transfered to any hero
 struct DLL_EXPORT Bonus

+ 5 - 1
lib/NetPacks.h

@@ -1263,7 +1263,7 @@ struct BattleStackAttacked : public CPackForClient//3005
 
 	ui32 stackAttacked, attackerID;
 	ui32 newAmount, newHP, killedAmount, damageAmount;
-	enum EFlags {KILLED = 1, EFFECT = 2, SECONDARY = 4};
+	enum EFlags {KILLED = 1, EFFECT = 2, SECONDARY = 4, REBIRTH = 8};
 	ui8 flags; //uses EFlags (above)
 	ui32 effect; //set only if flag EFFECT is set
 	std::vector<StacksHealedOrResurrected> healedStacks; //used when life drain
@@ -1281,6 +1281,10 @@ struct BattleStackAttacked : public CPackForClient//3005
 	{
 		return flags & SECONDARY;
 	}
+	bool willRebirth() const//if stack was not a primary target (receives no spell effects)
+	{
+		return flags & REBIRTH;
+	}
 	bool lifeDrain() const //if this attack involves life drain effect
 	{
 		return healedStacks.size() > 0;

+ 8 - 1
lib/NetPacksLib.cpp

@@ -974,14 +974,21 @@ DLL_EXPORT void BattleStackAttacked::applyGs( CGameState *gs )
 	CStack * at = gs->curB->getStack(stackAttacked);
 	at->count = newAmount;
 	at->firstHPleft = newHP;
+
 	if(killed())
+	{
 		at->state -= ALIVE;
-
+	}
 	//life drain handling
 	for (int g=0; g<healedStacks.size(); ++g)
 	{
 		healedStacks[g].applyGs(gs);
 	}
+	if (willRebirth())
+	{
+		at->casts--;
+		at->state.insert(ALIVE); //hmm?
+	}
 }
 
 DLL_EXPORT void BattleAttack::applyGs( CGameState *gs )

+ 2 - 2
server/CGameHandler.cpp

@@ -4366,8 +4366,8 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 				continue;
 
 			//casting
-			if (castMe)
-				handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, attacker->count, SpellCasting::AFTER_ATTACK_CASTING, attacker);
+			if (castMe) //stacks use 0 spell power. If needed, default = 3 or custom value is used
+				handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, 0, SpellCasting::AFTER_ATTACK_CASTING, attacker);
 		}
 	}
 	if (attacker->hasBonusOfType(Bonus::DEATH_STARE)) // spell id 79