瀏覽代碼

Added configurable animation for SPELL_LIKE_ATTACK

AlexVinS 11 年之前
父節點
當前提交
d94f15bdf4

+ 15 - 1
client/CPlayerInterface.cpp

@@ -876,7 +876,7 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	BATTLE_EVENT_POSSIBLE_RETURN;
-
+	
 	std::vector<StackAttackedInfo> arg;
 	for(auto & elem : bsa)
 	{
@@ -887,6 +887,11 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
 			if (defender && !elem.isSecondary())
 				battleInt->displayEffect(elem.effect, defender->position);
 		}
+		if(elem.isSpell())
+		{
+			if (defender)
+				battleInt->displaySpellEffect(elem.spellID, defender->position);			
+		}
 		//FIXME: why action is deleted during enchanter cast?
 		bool remoteAttack = false;
 
@@ -968,6 +973,15 @@ void CPlayerInterface::battleAttack(const BattleAttack *ba)
 		const CStack * attacked = cb->battleGetStackByID(ba->bsa.begin()->stackAttacked);
 		battleInt->stackAttacking( attacker, ba->counter() ? curAction->destinationTile + shift : curAction->additionalInfo, attacked, false);
 	}
+	
+	battleInt->waitForAnims();
+	
+	if(ba->spellLike())
+	{
+		//display hit animation		
+		SpellID spellID = ba->spellID;			
+		battleInt->displaySpellHit(spellID,curAction->destinationTile);	
+	}
 }
 void CPlayerInterface::battleObstaclePlaced(const CObstacleInstance &obstacle)
 {

+ 31 - 14
client/battle/CBattleInterface.cpp

@@ -1226,7 +1226,8 @@ void CBattleInterface::displayBattleFinished()
 
 void CBattleInterface::spellCast( const BattleSpellCast * sc )
 {
-	const CSpell &spell = *CGI->spellh->objects[sc->id];
+	const SpellID spellID(sc->id);
+	const CSpell &spell = * spellID.toSpell();
 
 	const std::string& castSoundPath = spell.getCastSound();
 	
@@ -1289,11 +1290,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 	}	
 	waitForAnims();
 	
-	//queuing hit animation
-	for(const CSpell::TAnimation & animation : spell.animationInfo.hit)
-	{			
-		addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, sc->tile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
-	}
+	displaySpellHit(spellID, sc->tile);
 	
 	//queuing affect /resist animation	
 	for (auto & elem : sc->affectedCres) 
@@ -1301,16 +1298,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 		BattleHex position = curInt->cb->battleGetStackByID(elem, false)->position;
 		
 		if(vstd::contains(sc->resisted,elem))
-		{
 			displayEffect(78, position);
-		}
 		else
-		{		
-			for(const CSpell::TAnimation & animation : spell.animationInfo.affect)
-			{				
-				addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, position, false, animation.verticalPosition == VerticalPosition::BOTTOM));
-			}			
-		}
+			displaySpellEffect(spellID, position);	
 	}
 
 	switch(sc->id)
@@ -1570,6 +1560,33 @@ void CBattleInterface::displayEffect(ui32 effect, int destTile, bool areaEffect)
 	addNewAnim(new CSpellEffectAnimation(this, effect, destTile, 0, 0, false));
 }
 
+void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect)
+{
+	const CSpell * spell = spellID.toSpell();
+	
+	if(spell == nullptr)
+		return;
+
+	for(const CSpell::TAnimation & animation : spell->animationInfo.affect)
+	{				
+		addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
+	}
+}
+
+void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect)
+{
+	const CSpell * spell = spellID.toSpell();
+	
+	if(spell == nullptr)
+		return;	
+	
+	for(const CSpell::TAnimation & animation : spell->animationInfo.hit)
+	{			
+		addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
+	}
+}
+
+
 void CBattleInterface::battleTriggerEffect(const BattleTriggerEffect & bte)
 {
 	const CStack * stack = curInt->cb->battleGetStackByID(bte.stackID);

+ 4 - 1
client/battle/CBattleInterface.h

@@ -318,7 +318,10 @@ public:
 	void spellCast(const BattleSpellCast * sc); //called when a hero casts a spell
 	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
 	void castThisSpell(int spellID); //called when player has chosen a spell from spellbook
-	void displayEffect(ui32 effect, int destTile, bool areaEffect = true); //displays effect of a spell on the battlefield; affected: true - attacker. false - defender
+	void displayEffect(ui32 effect, int destTile, bool areaEffect = true); //displays custom effect on the battlefield
+	void displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
+	void displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
+	
 	void battleTriggerEffect(const BattleTriggerEffect & bte);
 	void setBattleCursor(const int myNumber); //really complex and messy, sets attackingHex
 	void endAction(const BattleAction* action);

+ 7 - 7
config/battles_graphics.json

@@ -90,11 +90,11 @@
 		{ "id": 59, "defnames": [ "C09SPF0.DEF" ] },
 		{ "id": 62, "defnames": [ "C07SPF60.DEF", "C07SPF61.DEF", "C07SPF62.DEF" ] },
 		{ "id": 64, "defnames": [ "C20SPX.DEF" ] }, //merged
-		{ "id": 67, "defnames": [ "SP11_.DEF" ] },
-		{ "id": 68, "defnames": [ "SP02_.DEF" ] },
-		{ "id": 69, "defnames": [ "SP05_.DEF" ] },
-		{ "id": 71, "defnames": [ "SP01_.DEF" ] },
-		{ "id": 72, "defnames": [ "SP04_.DEF" ] },
+		{ "id": 67, "defnames": [ "SP11_.DEF" ] }, //merged
+		{ "id": 68, "defnames": [ "SP02_.DEF" ] }, //merged
+		{ "id": 69, "defnames": [ "SP05_.DEF" ] }, //merged
+		{ "id": 71, "defnames": [ "SP01_.DEF" ] }, //merged
+		{ "id": 72, "defnames": [ "SP04_.DEF" ] }, //merged
 		{ "id": 73, "defnames": [ "SP03_.DEF" ] },
 		{ "id": 74, "defnames": [ "SP12_.DEF" ] },
 		{ "id": 75, "defnames": [ "SP07_A.DEF" ] },
@@ -102,8 +102,8 @@
 		{ "id": 77, "defnames": [ "SP08_.DEF" ] },
 		{ "id": 78, "defnames": [ "SP09_.DEF" ] },
 		{ "id": 79, "defnames": [ "C01SPE0.DEF" ] },//merged
-		{ "id": 80, "defnames": [ "C07SPE0.DEF" ] },
-		{ "id": 81, "defnames": [ "C17SPW0.DEF" ] },
+		{ "id": 80, "defnames": [ "C07SPE0.DEF" ] },//merged
+		{ "id": 81, "defnames": [ "C17SPW0.DEF" ] },//merged
 		{ "id": 82, "defnames": [ "C09SPF3.DEF" ] },
 		{ "id": 84, "defnames": [ "ZMGC02.DEF" ] }
 	]

+ 7 - 0
config/schemas/spell.json

@@ -107,6 +107,13 @@
 					}
 				}
 			}
+		},
+		
+		"texts":{
+			"type": "object",
+			
+			
+			"additionalProperties" : false
 		}
 	},
 

+ 36 - 0
config/spells/ability.json

@@ -3,6 +3,9 @@
 		"index" : 70,
 		"targetType": "NO_TARGET",
 		"anim" : 70,
+		"animation":{
+			//need special animation
+		},		
 		"sounds": {
 			"cast": "PARALYZE"
 		},
@@ -36,6 +39,9 @@
 		"index" : 71,
 		"targetType": "NO_TARGET",
 		"anim" : 67,
+		"animation":{
+			"affect":["SP11_"]
+		},		
 		"sounds": {
 			"cast": "POISON"
 		},
@@ -71,6 +77,9 @@
 		"index" : 72,
 		"targetType": "NO_TARGET",
 		"anim" : 68,
+		"animation":{
+			"affect":["SP02_"]
+		},		
 		"sounds": {
 			"cast": "BIND"
 		},
@@ -95,6 +104,9 @@
 		"index" : 73,
 		"targetType": "NO_TARGET",
 		"anim" : 69,
+		"animation":{
+			"affect":["SP05_"]
+		},		
 		"sounds": {
 			"cast": "DISEASE"
 		},
@@ -130,6 +142,9 @@
 		"index" : 74,
 		"targetType": "NO_TARGET",
 		"anim" : 70,
+		"animation":{
+			//missing
+		},		
 		"sounds": {
 			"cast": "PARALYZE"
 		},
@@ -163,6 +178,9 @@
 		"index" : 75,
 		"targetType": "NO_TARGET",
 		"anim" : 71,
+		"animation":{
+			"affect":["SP01_"]
+		},		
 		"sounds": {
 			"cast": "AGE"
 		},
@@ -192,6 +210,9 @@
 		"index" : 76,
 		"targetType": "NO_TARGET",
 		"anim" : 72,
+		"animation":{
+			"hit":["SP04_"]
+		},		
 		"sounds": {
 			"cast": "DEATHCLD"
 		},
@@ -212,6 +233,9 @@
 		"index" : 77,
 		"targetType": "NO_TARGET",
 		"anim" : 38,
+		"animation":{
+			"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
+		},		
 		"sounds": {
 			"cast": "LIGHTBLT"
 		},
@@ -230,6 +254,9 @@
 		"index" : 78,
 		"targetType": "NO_TARGET",
 		"anim" : 41,
+		"animation":{
+			"affect":["C05SPW"]
+		},		
 		"sounds": {
 			"cast": "DISPELL"
 		},
@@ -247,6 +274,9 @@
 		"index" : 79,
 		"targetType": "NO_TARGET",
 		"anim" : 80,
+		"animation":{
+			"affect":["C07SPE0"]
+		},		
 		"sounds": {
 			"cast": "DEATHSTR"
 		},
@@ -267,6 +297,9 @@
 		"index" : 80,
 		"targetType": "NO_TARGET",
 		"anim" : 81,
+		"animation":{
+			"affect":["C17SPW0"]
+		},		
 		"sounds": {
 			"cast": "ACID"
 		},
@@ -293,6 +326,9 @@
 		"index" : 81,
 		"targetType": "NO_TARGET",
 		"anim" : 81,
+		"animation":{
+			//???
+		},		
 		"sounds": {
 			"cast": "ACID"
 		},

+ 1 - 1
config/spells/offensive.json

@@ -68,7 +68,7 @@
 		"targetType": "CREATURE",
 		"anim" : 38,
 		"animation":{
-			"hit":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
+			"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
 		},		
 		"sounds": {
 			"cast": "LIGHTBLT"

+ 5 - 0
lib/GameConstants.cpp

@@ -87,6 +87,11 @@ CCreature * CreatureID::toCreature() const
 
 CSpell * SpellID::toSpell() const
 {
+	if(num < 0 || num >= VLC->spellh->objects.size())
+	{	
+		logGlobal->errorStream() << "Unable to get spell of invalid ID " << int(num);
+		return nullptr;
+	}		
 	return VLC->spellh->objects[*this];
 }
 

+ 21 - 7
lib/NetPacks.h

@@ -1320,16 +1320,18 @@ struct StacksHealedOrResurrected : public CPackForClient //3013
 
 struct BattleStackAttacked : public CPackForClient//3005
 {
-	BattleStackAttacked(){flags = 0; type = 3005;};
+	BattleStackAttacked():
+		flags(0), spellID(SpellID::NONE){type=3005;};
 	void applyFirstCl(CClient * cl);
 	//void applyCl(CClient *cl);
 	DLL_LINKAGE void applyGs(CGameState *gs);
 
 	ui32 stackAttacked, attackerID;
 	ui32 newAmount, newHP, killedAmount, damageAmount;
-	enum EFlags {KILLED = 1, EFFECT = 2, SECONDARY = 4, REBIRTH = 8, CLONE_KILLED = 16};
-	ui8 flags; //uses EFlags (above)
+	enum EFlags {KILLED = 1, EFFECT = 2/*deprecated */, SECONDARY = 4, REBIRTH = 8, CLONE_KILLED = 16, SPELL_EFFECT = 32 /*, BONUS_EFFECT = 64 */};
+	ui32 flags; //uses EFlags (above)
 	ui32 effect; //set only if flag EFFECT is set
+	SpellID spellID; //only if flag SPELL_EFFECT is set
 	std::vector<StacksHealedOrResurrected> healedStacks; //used when life drain
 
 
@@ -1349,6 +1351,11 @@ struct BattleStackAttacked : public CPackForClient//3005
 	{
 		return flags & SECONDARY;
 	}
+	///Attacked with spell (SPELL_LIKE_ATTACK)
+	bool isSpell() const
+	{
+		return flags & SPELL_EFFECT;
+	}
 	bool willRebirth() const//resurrection, e.g. Phoenix
 	{
 		return flags & REBIRTH;
@@ -1361,6 +1368,7 @@ struct BattleStackAttacked : public CPackForClient//3005
 	{
 		h & stackAttacked & attackerID & newAmount & newHP & flags & killedAmount & damageAmount & effect
 			& healedStacks;
+		h & spellID;
 	}
 	bool operator<(const BattleStackAttacked &b) const
 	{
@@ -1370,15 +1378,17 @@ struct BattleStackAttacked : public CPackForClient//3005
 
 struct BattleAttack : public CPackForClient//3006
 {
-	BattleAttack(){flags = 0; type = 3006;};
+	BattleAttack(): flags(0), spellID(SpellID::NONE){type = 3006;};
 	void applyFirstCl(CClient *cl);
 	DLL_LINKAGE void applyGs(CGameState *gs);
 	void applyCl(CClient *cl);
 
 	std::vector<BattleStackAttacked> bsa;
 	ui32 stackAttacking;
-	ui8 flags; //uses Eflags (below)
-	enum EFlags{SHOT = 1, COUNTER = 2, LUCKY = 4, UNLUCKY = 8, BALLISTA_DOUBLE_DMG = 16, DEATH_BLOW = 32};
+	ui32 flags; //uses Eflags (below)
+	enum EFlags{SHOT = 1, COUNTER = 2, LUCKY = 4, UNLUCKY = 8, BALLISTA_DOUBLE_DMG = 16, DEATH_BLOW = 32, SPELL_LIKE = 64};
+	
+	SpellID spellID; //for SPELL_LIKE 
 
 	bool shot() const//distance attack - decrease number of shots
 	{
@@ -1404,13 +1414,17 @@ struct BattleAttack : public CPackForClient//3006
 	{
 		return flags & DEATH_BLOW;
 	}
+	bool spellLike() const
+	{
+		return flags & SPELL_LIKE;
+	}
 	//bool killed() //if target stack was killed
 	//{
 	//	return bsa.killed();
 	//}
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & bsa & stackAttacking & flags;
+		h & bsa & stackAttacking & flags & spellID;
 	}
 };
 

+ 20 - 5
server/CGameHandler.cpp

@@ -794,11 +794,14 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 	}
 
 	const Bonus * bonus = att->getBonusLocalFirst(Selector::type(Bonus::SPELL_LIKE_ATTACK));
-	if (bonus && (bat.shot())) //TODO: make it work in meele?
-	{
-		bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
-		bat.bsa.front().effect = VLC->spellh->objects.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect?
-
+	if (bonus && (bat.shot())) //TODO: make it work in melee?
+	{	
+		//this is need for displaying hit animation
+		bat.flags |= BattleAttack::SPELL_LIKE;
+		bat.spellID = SpellID(bonus->subtype);
+		
+		//TODO: should spell override creature`s projectile?
+		
 		std::set<const CStack*> attackedCreatures = SpellID(bonus->subtype).toSpell()->getAffectedStacks(gs->curB, ECastingMode::SPELL_LIKE_ATTACK, att->owner, bonus->val, targetHex);
 	
 		//TODO: get exact attacked hex for defender
@@ -810,6 +813,18 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
 				applyBattleEffects(bat, att, stack, distance, true);
 			}
 		}
+		
+		//now add effect info for all attacked stacks
+		for(BattleStackAttacked & bsa : bat.bsa)
+		{
+			if(bsa.attackerID == att->ID) //this is our attack and not f.e. fire shield
+			{
+				//this is need for displaying affect animation
+				bsa.flags |= BattleStackAttacked::SPELL_EFFECT;
+				bsa.spellID = SpellID(bonus->subtype);
+			}
+		}
+		
 	}
 }
 void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, const CStack *def, int distance, bool secondary) //helper function for prepareAttack