浏览代码

Support for Death Stare.
Some tweaks are still needed. Level 0 spell-like abilities won't work correctly.

DjWarmonger 14 年之前
父节点
当前提交
7591d06b05

+ 24 - 4
client/CBattleInterface.cpp

@@ -2843,19 +2843,39 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 	}
 	}
 
 
 	//displaying message in console
 	//displaying message in console
+	bool customSpell = false;
 	if(sc->affectedCres.size() == 1)
 	if(sc->affectedCres.size() == 1)
 	{
 	{
 		std::string text = CGI->generaltexth->allTexts[195];
 		std::string text = CGI->generaltexth->allTexts[195];
 		if(sc->castedByHero)
 		if(sc->castedByHero)
 		{
 		{
 			boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetFightingHero(sc->side)->name);
 			boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetFightingHero(sc->side)->name);
+			boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getCreature()->namePl ); //target
 		}
 		}
 		else
 		else
 		{
 		{
-			boost::algorithm::replace_first(text, "%s", "Creature"); //TODO: better fix
+			if (sc->id == 79) // Death Stare
+			{
+				customSpell = true;
+				if (sc->dmgToDisplay > 1)
+				{
+					text = CGI->generaltexth->allTexts[119]; //%d %s die under the terrible gaze of the %s.
+					boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
+					boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getCreature()->namePl );
+				}
+				else
+				{
+					text = CGI->generaltexth->allTexts[118]; //One %s dies under the terrible gaze of the %s.
+					boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->type->nameSing);
+				}
+				boost::algorithm::replace_first(text, "%s", "Creatures"); //casting stack
+			}
+			else
+				boost::algorithm::replace_first(text, "%s", "Creature"); //TODO: better fix
 		}
 		}
-		boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name);
-		boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getCreature()->namePl );
+		if (!customSpell)
+			boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name);
+
 		console->addText(text);
 		console->addText(text);
 	}
 	}
 	else
 	else
@@ -2872,7 +2892,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 		boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name);
 		boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name);
 		console->addText(text);
 		console->addText(text);
 	}
 	}
-	if(sc->dmgToDisplay != 0)
+	if(sc->dmgToDisplay && !customSpell)
 	{
 	{
 		std::string dmgInfo = CGI->generaltexth->allTexts[343].substr(1, CGI->generaltexth->allTexts[343].size() - 1);
 		std::string dmgInfo = CGI->generaltexth->allTexts[343].substr(1, CGI->generaltexth->allTexts[343].size() - 1);
 		boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
 		boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));

+ 1 - 1
config/cr_abils.txt

@@ -115,7 +115,7 @@
 +  93 SPELL_AFTER_ATTACK 0 77 20   	//thunderbirds					
 +  93 SPELL_AFTER_ATTACK 0 77 20   	//thunderbirds					
 +  96 ENEMY_DEFENCE_REDUCTION 40 0 0		//behemots				
 +  96 ENEMY_DEFENCE_REDUCTION 40 0 0		//behemots				
 +  97 ENEMY_DEFENCE_REDUCTION 80 0 0		//ancient behemots				
 +  97 ENEMY_DEFENCE_REDUCTION 80 0 0		//ancient behemots				
-+ 103 SPELL_AFTER_ATTACK 0 79 10   	//mighty gorgons					
++ 103 DEATH_STARE 10 0 0   	//mighty gorgons					
 + 104 SPELL_AFTER_ATTACK 0 78 100  	//serpent fly					
 + 104 SPELL_AFTER_ATTACK 0 78 100  	//serpent fly					
 + 105 SPELL_AFTER_ATTACK 0 45 100  	//mighty gorgons					
 + 105 SPELL_AFTER_ATTACK 0 45 100  	//mighty gorgons					
 + 105 SPELL_AFTER_ATTACK 0 78 100	//dragon fly					
 + 105 SPELL_AFTER_ATTACK 0 78 100	//dragon fly					

+ 2 - 2
config/sp_sounds.txt

@@ -18,6 +18,7 @@
 31 PROTECTF.wav	# protection from fire
 31 PROTECTF.wav	# protection from fire
 32 PROTECTW.wav	# protection from water
 32 PROTECTW.wav	# protection from water
 33 PROTECTE.wav	# protection from earth
 33 PROTECTE.wav	# protection from earth
+35 DISPELL.wav	# dispell
 41 BLESS.wav	# bless
 41 BLESS.wav	# bless
 42 CURSE.wav	# curse
 42 CURSE.wav	# curse
 43 BLOODLUS.wav	# bloodlust
 43 BLOODLUS.wav	# bloodlust
@@ -35,7 +36,7 @@
 55 SLAYER.wav	# slayer
 55 SLAYER.wav	# slayer
 56 FRENZY.wav	# frenzy
 56 FRENZY.wav	# frenzy
 61 FORGET.wav	# forgetfulness
 61 FORGET.wav	# forgetfulness
-
+79 DEATHSTR.wav # Death Stare
 
 
 
 
 
 
@@ -47,7 +48,6 @@
 #DEATHBLO.wav
 #DEATHBLO.wav
 #DRAINLIF.wav
 #DRAINLIF.wav
 #DRGNSLAY.wav
 #DRGNSLAY.wav
-#DISPELL.wav
 #DISGUISE.wav
 #DISGUISE.wav
 #DISEASE.wav
 #DISEASE.wav
 #QUIKSAND.wav
 #QUIKSAND.wav

+ 1 - 1
config/spell_info.txt

@@ -79,6 +79,6 @@
 76 -1 -1 0 0 0 0
 76 -1 -1 0 0 0 0
 77 -1 38 0 0 0 0
 77 -1 38 0 0 0 0
 78 -1 41 0 0 0 0
 78 -1 41 0 0 0 0
-79 -1 -1 0 0 0 0
+79 0 80 0 0 0 0
 80 -1 -1 0 0 0 0
 80 -1 -1 0 0 0 0
 -1
 -1

+ 4 - 3
lib/BattleState.cpp

@@ -1791,9 +1791,10 @@ SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInstance
 			std::remove_if(immunities.begin(), immunities.end(), NegateRemover);
 			std::remove_if(immunities.begin(), immunities.end(), NegateRemover);
 		}
 		}
 
 
-		if(subject->hasBonusOfType(Bonus::SPELL_IMMUNITY, spell->id)
-			|| ( immunities.size() > 0 && immunities.totalValue() >= spell->level))
-		{
+		if(subject->hasBonusOfType(Bonus::SPELL_IMMUNITY, spell->id) ||
+			( immunities.size() > 0 && immunities.totalValue() >= spell->level)
+			&& spell->level) //many creature abilities have level equal to 0, may cause bugs
+		{ 
 			return SpellCasting::STACK_IMMUNE_TO_SPELL;
 			return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 		}
 		//dispel helpful spells
 		//dispel helpful spells

+ 4 - 0
lib/CCreatureHandler.cpp

@@ -841,6 +841,10 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src
 		b.type = Bonus::CHANGES_SPELL_COST_FOR_ALLY; break;
 		b.type = Bonus::CHANGES_SPELL_COST_FOR_ALLY; break;
 	case 'e':
 	case 'e':
 		b.type = Bonus::DOUBLE_DAMAGE_CHANCE; break;
 		b.type = Bonus::DOUBLE_DAMAGE_CHANCE; break;
+	case 'E':
+		b.type = Bonus::DEATH_STARE;
+		b.subtype = 0; //Gorgon
+		break;
 	case 'g':
 	case 'g':
 		b.type = Bonus::SPELL_DAMAGE_REDUCTION;
 		b.type = Bonus::SPELL_DAMAGE_REDUCTION;
 		b.subtype = -1; //all magic schools
 		b.subtype = -1; //all magic schools

+ 3 - 1
lib/CCreatureSet.cpp

@@ -600,6 +600,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
 				case Bonus::CHANGES_SPELL_COST_FOR_ALLY:
 				case Bonus::CHANGES_SPELL_COST_FOR_ALLY:
 				case Bonus::CHANGES_SPELL_COST_FOR_ENEMY:
 				case Bonus::CHANGES_SPELL_COST_FOR_ENEMY:
 				case Bonus::ENEMY_DEFENCE_REDUCTION:
 				case Bonus::ENEMY_DEFENCE_REDUCTION:
+				case Bonus::DEATH_STARE:
 					boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSybtype(bonus->type, bonus->subtype))));
 					boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSybtype(bonus->type, bonus->subtype))));
 					break;
 					break;
 				case Bonus::HATE:
 				case Bonus::HATE:
@@ -644,7 +645,8 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
 			fileName = "E_CHAMP.bmp"; break;
 			fileName = "E_CHAMP.bmp"; break;
 		case Bonus::DOUBLE_DAMAGE_CHANCE:
 		case Bonus::DOUBLE_DAMAGE_CHANCE:
 			fileName = "E_DBLOW.bmp"; break;
 			fileName = "E_DBLOW.bmp"; break;
-			//"E_DEATH.bmp"
+		case Bonus::DEATH_STARE:
+			fileName = "E_DEATH.bmp"; break;
 			//"E_DEFBON.bmp"
 			//"E_DEFBON.bmp"
 		case Bonus::NO_DISTANCE_PENALTY:
 		case Bonus::NO_DISTANCE_PENALTY:
 			fileName = "E_DIST.bmp"; break;
 			fileName = "E_DIST.bmp"; break;

+ 2 - 1
lib/HeroBonus.h

@@ -156,7 +156,8 @@ namespace PrimarySkill
 	BONUS_NAME(DRAGON_NATURE) \
 	BONUS_NAME(DRAGON_NATURE) \
 	BONUS_NAME(CREATURE_DAMAGE)/*subtype 0 = both, 1 = min, 2 = max*/\
 	BONUS_NAME(CREATURE_DAMAGE)/*subtype 0 = both, 1 = min, 2 = max*/\
 	BONUS_NAME(EXP_MULTIPLIER)/* val - percent of additional exp gained by stack/commander (base value 100)*/\
 	BONUS_NAME(EXP_MULTIPLIER)/* val - percent of additional exp gained by stack/commander (base value 100)*/\
-	BONUS_NAME(SHOTS)
+	BONUS_NAME(SHOTS)\
+	BONUS_NAME(DEATH_STARE) /*subtype 0 - gorgon, 1 - commander*/
 
 
 /// Struct for handling bonuses of several types. Can be transfered to any hero
 /// Struct for handling bonuses of several types. Can be transfered to any hero
 struct DLL_EXPORT Bonus
 struct DLL_EXPORT Bonus

+ 51 - 0
server/CGameHandler.cpp

@@ -20,6 +20,9 @@
 #include "../lib/VCMIDirs.h"
 #include "../lib/VCMIDirs.h"
 #include "../client/CSoundBase.h"
 #include "../client/CSoundBase.h"
 #include "CGameHandler.h"
 #include "CGameHandler.h"
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/variate_generator.hpp>
+#include <boost/random/poisson_distribution.hpp>
 
 
 /*
 /*
  * CGameHandler.cpp, part of VCMI engine
  * CGameHandler.cpp, part of VCMI engine
@@ -3401,6 +3404,11 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
 			continue;
 			continue;
 		sc.dmgToDisplay += gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl, usedSpellPower);
 		sc.dmgToDisplay += gs->curB->calculateSpellDmg(spell, caster, *it, spellLvl, usedSpellPower);
 	}
 	}
+	if (spellID = 79) // Death stare
+	{
+		sc.dmgToDisplay = usedSpellPower;
+		amin(sc.dmgToDisplay, (*attackedCres.begin())->count); // hopefully stack is already reduced after attack
+	}
 
 
 	sendAndApply(&sc);
 	sendAndApply(&sc);
 
 
@@ -3585,6 +3593,27 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, int destinatio
 
 
 			break;
 			break;
 		}
 		}
+	case 79: //Death stare - handled in a bit different way
+		{
+			StacksInjured si;
+			for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
+			{
+				if((*it)->hasBonusOfType (Bonus::UNDEAD) || (*it)->hasBonusOfType (Bonus::NON_LIVING)) //this creature is immune
+					continue;
+
+				BattleStackAttacked bsa;
+				bsa.flags |= BattleStackAttacked::EFFECT;
+				bsa.effect = spell->mainEffectAnim; //TODO: find which it is
+				bsa.damageAmount = usedSpellPower * (*it)->valOfBonuses(Bonus::STACK_HEALTH);
+				bsa.stackAttacked = (*it)->ID;
+				bsa.attackerID = -1;
+				(*it)->prepareAttacked(bsa);
+				si.stacks.push_back(bsa);
+			}
+			if(!si.stacks.empty())
+				sendAndApply(&si);
+		}
+		break;
 	}
 	}
 
 
 }
 }
@@ -4210,6 +4239,28 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
 			}
 			}
 		}
 		}
 	}
 	}
+	if (attacker->hasBonusOfType(Bonus::DEATH_STARE)) // spell id 79
+	{
+		int staredCreatures = 0;
+		double mean = attacker->count * attacker->valOfBonuses(Bonus::DEATH_STARE, 0) / 100;
+		if (mean >= 1)
+		{
+			boost::poisson_distribution<int, double> p((int)mean);
+			boost::mt19937 rng;
+			boost::variate_generator<boost::mt19937&, boost::poisson_distribution<int, double>> dice (rng, p);
+			staredCreatures += dice();
+		}
+		if (((int)(mean * 100)) < rand() % 100) //fractional chance for one last kill
+			++staredCreatures;
+		 
+		staredCreatures += attacker->type->level * attacker->valOfBonuses(Bonus::DEATH_STARE, 1);
+		if (staredCreatures)
+		{
+			if (bat.bsa.size() && bat.bsa[0].newAmount > 0) //TODO: death stare was not originally avaliable for multiple-hex attacks, but...
+			handleSpellCasting(79, 0, gs->curB->getStack(bat.bsa[0].stackAttacked)->position,
+				!attacker->attackerOwned, attacker->owner, NULL, NULL, staredCreatures, SpellCasting::AFTER_ATTACK_CASTING);
+		}
+	}
 }
 }
 
 
 bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &pos)
 bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &pos)