Kaynağa Gözat

[Spells] Added basic support for icons and sounds

* few changes in spell format
* save format changed
alexvins 11 yıl önce
ebeveyn
işleme
065b8366fb

+ 2 - 0
ChangeLog

@@ -9,8 +9,10 @@ ADVENTURE MAP:
 BATTLES:
 
 SPELLS:
+* New configuration format: http://wiki.vcmi.eu/index.php?title=Spell_Format
 
 MODS:
+* Mods cas now add new (offensive, buffs, debuffs) spells and change existing
 
 0.94 -> 0.95 (Mar 01 2014)
 GENERAL:

+ 14 - 3
client/CCreatureWindow.cpp

@@ -185,6 +185,7 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 {
 	creatureArtifact = nullptr; //may be set later
 	artifactImage = nullptr;
+	spellEffectsPics = nullptr;
 	stack = Stack;
 	c = stack->type;
 	if(!StackNode)
@@ -411,28 +412,35 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 				passArtToHero = new CAdventureMapButton(std::string(), std::string(), boost::bind (&CCreatureWindow::passArtifactToHero, this), 437, 148, "OVBUTN1.DEF", SDLK_HOME);
 		}
 	}
-
+	
 	if (battleStack) //only during battle
 	{
+		spellEffectsPics = new CAnimation("SpellInt.def");
+
 		//spell effects
 		int printed=0; //how many effect pics have been printed
 		std::vector<si32> spells = battleStack->activeSpells();
 		for(si32 effect : spells)
 		{
+			const si32 imageIndex = effect+1; //there is "null" frame with index 0 in SpellInt.def
 			std::string spellText;
-			if (effect < graphics->spellEffectsPics->ourImages.size()) //not all effects have graphics (for eg. Acid Breath)
+			spellEffectsPics->load(imageIndex);
+			IImage * effectIcon = spellEffectsPics->getImage(imageIndex,0,false); //todo: better way to determine presence of icon
+			spellEffectsPics->unload(imageIndex);
+			if (effectIcon != nullptr) //not all effects have graphics (for eg. Acid Breath)
 			{
 				spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds."
 				boost::replace_first (spellText, "%s", CGI->spellh->objects[effect]->name);
 				int duration = battleStack->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT,effect))->turnsRemain;
 				boost::replace_first (spellText, "%d", boost::lexical_cast<std::string>(duration));
 
-				new CAnimImage("SpellInt", effect + 1, 0, 20 + 52 * printed, 184);
+				new CAnimImage("SpellInt.def", imageIndex, 0, 20 + 52 * printed, 184);
 				spellEffects.push_back(new LRClickableAreaWText(Rect(20 + 52 * printed, 184, 50, 38), spellText, spellText));
 				if (++printed >= 10) //we can fit only 10 effects
 					break;
 			}
 		}
+		
 		//print current health
 		printLine (5, CGI->generaltexth->allTexts[200], battleStack->firstHPleft);
 	}
@@ -689,6 +697,9 @@ CCreatureWindow::~CCreatureWindow()
  	for (auto & elem : upgResCost)
  		delete elem;
 	bonusItems.clear();
+	
+	if(spellEffectsPics!=nullptr)
+		delete spellEffectsPics;
 }
 
 CBonusItem::CBonusItem()

+ 2 - 1
client/CCreatureWindow.h

@@ -65,7 +65,8 @@ public:
 	CAdventureMapButton *dismiss, *upgrade, *ok;
 	CAdventureMapButton * leftArtRoll, * rightArtRoll; //artifact selection
 	CAdventureMapButton * passArtToHero;
-	CAnimImage *artifactImage;
+	CAnimImage * artifactImage;
+	CAnimation * spellEffectsPics; //bitmaps representing spells affecting a stack in battle
 
 	//commander level-up
 	int selectedOption; //index for upgradeOptions

+ 1 - 1
client/CMT.cpp

@@ -145,7 +145,7 @@ void init()
 
 	loadDLLClasses();
 	const_cast<CGameInfo*>(CGI)->setFromLib();
-	CCS->soundh->initSpellsSounds(CGI->spellh->objects);
+
     logGlobal->infoStream()<<"Initializing VCMI_Lib: "<<tmh.getDiff();
 
 	pomtime.getDiff();

+ 0 - 22
client/CMusicHandler.cpp

@@ -177,28 +177,6 @@ Mix_Chunk *CSoundHandler::GetSoundChunk(std::string &sound)
 	}
 }
 
-void CSoundHandler::initSpellsSounds(const std::vector< ConstTransitivePtr<CSpell> > &spells)
-{
-	const JsonNode config(ResourceID("config/sp_sounds.json"));
-
-	if (!config["spell_sounds"].isNull())
-	{
-		for(const JsonNode &node : config["spell_sounds"].Vector())
-		{
-			int spellid = node["id"].Float();
-			const CSpell *s = CGI->spellh->objects[spellid];
-
-			if (vstd::contains(spellSounds, s))
-                logGlobal->errorStream() << "Spell << " << spellid << " already has a sound";
-
-			std::string sound = node["soundfile"].String();
-			if (sound.empty())
-                logGlobal->errorStream() << "Error: invalid sound for id "<< spellid;
-			spellSounds[s] = sound;
-		}
-	}
-}
-
 // Plays a sound, and return its channel so we can fade it out later
 int CSoundHandler::playSound(soundBase::soundID soundID, int repeats)
 {

+ 0 - 3
client/CMusicHandler.h

@@ -57,7 +57,6 @@ public:
 	void init();
 	void release();
 
-	void initSpellsSounds(const std::vector< ConstTransitivePtr<CSpell> > &spells);
 	void setVolume(ui32 percent);
 
 	// Sounds
@@ -69,8 +68,6 @@ public:
 	void setCallback(int channel, std::function<void()> function);
 	void soundFinishedCallback(int channel);
 
-	std::map<const CSpell*, std::string> spellSounds;
-
 	// Sets
 	std::vector<soundBase::soundID> pickupSounds;
 	std::vector<soundBase::soundID> horseSounds;

+ 4 - 2
client/CPlayerInterface.cpp

@@ -2203,8 +2203,10 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
 		eraseCurrentPathOf(caster, false);
 	}
 	const CSpell * spell = CGI->spellh->objects[spellID];
-	if (vstd::contains(CCS->soundh->spellSounds, spell))
-		CCS->soundh->playSound(CCS->soundh->spellSounds[spell]);
+	
+	auto castSoundPath = spell->getCastSound();
+	if (!castSoundPath.empty())
+		CCS->soundh->playSound(castSoundPath);
 }
 
 void CPlayerInterface::eraseCurrentPathOf( const CGHeroInstance * ho, bool checkForExistanceOfPath /*= true */ )

+ 14 - 4
client/CSpellWindow.cpp

@@ -147,7 +147,9 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 
 	leftCorner = BitmapHandler::loadBitmap("SpelTrnL.bmp", true);
 	rightCorner = BitmapHandler::loadBitmap("SpelTrnR.bmp", true);
-	spells = CDefHandler::giveDef("Spells.def");
+	
+	spells = new CAnimation("Spells.def");
+		
 	spellTab = CDefHandler::giveDef("SpelTab.def");
 	schools = CDefHandler::giveDef("Schools.def");
 	schoolBorders[0] = CDefHandler::giveDef("SplevA.def");
@@ -222,6 +224,7 @@ CSpellWindow::~CSpellWindow()
 {
 	SDL_FreeSurface(leftCorner);
 	SDL_FreeSurface(rightCorner);
+	spells->unload();
 	delete spells;
 	delete spellTab;
 	delete schools;
@@ -775,7 +778,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 							LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
 						else
 							GH.pushInt (new CObjectListWindow(availableTowns,
-								new CPicture(graphics->spellscr->ourImages[spell].bitmap, 0, 0, false),
+								new CAnimImage("SPELLSCR",spell),
 								CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41],
 								boost::bind (&CSpellWindow::teleportTo, owner, _1, h)));
 					}
@@ -835,9 +838,16 @@ void CSpellWindow::SpellArea::showAll(SDL_Surface * to)
 	if(mySpell < 0)
 		return;
 
-	const CSpell * spell = mySpell.toSpell();
+	const CSpell * spell = mySpell.toSpell();	
+	owner->spells->load(mySpell);
+	
+	IImage * icon = owner->spells->getImage(mySpell,0,false);
+	
+	if(icon != nullptr)
+		icon->draw(to, pos.x, pos.y);
+	else
+		logGlobal->errorStream() << __FUNCTION__ << ": failed to load spell icon for spell with id " << mySpell;
 
-	blitAt(owner->spells->ourImages[mySpell].bitmap, pos.x, pos.y, to);
 	blitAt(owner->schoolBorders[owner->selectedTab >= 4 ? whichSchool : owner->selectedTab]->ourImages[schoolLevel].bitmap, pos.x, pos.y, to); //printing border (indicates level of magic school)
 
 	SDL_Color firstLineColor, secondLineColor;

+ 5 - 2
client/CSpellWindow.h

@@ -51,6 +51,7 @@ private:
 		int spellCost;
 		CSpellWindow * owner;
 
+
 		SpellArea(SDL_Rect pos, CSpellWindow * owner);
 
 		void setSpell(SpellID spellID);
@@ -62,8 +63,10 @@ private:
 	};
 
 	SDL_Surface * leftCorner, * rightCorner;
-	CDefHandler * spells, //pictures of spells
-		* spellTab, //school select
+	
+	CAnimation * spells; //pictures of spells
+	
+	CDefHandler	* spellTab, //school select
 		* schools, //schools' pictures
 		* schoolBorders [4]; //schools' 'borders': [0]: air, [1]: fire, [2]: water, [3]: earth
 

+ 3 - 3
client/GUIClasses.cpp

@@ -1841,7 +1841,7 @@ void CObjectListWindow::CItem::clickLeft(tribool down, bool previousState)
 		parent->changeSelection(index);
 }
 
-CObjectListWindow::CObjectListWindow(const std::vector<int> &_items, CPicture * titlePic, std::string _title, std::string _descr,
+CObjectListWindow::CObjectListWindow(const std::vector<int> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
 				std::function<void(int)> Callback):
     CWindowObject(PLAYER_COLORED, "TPGATE"),
 	onSelect(Callback)
@@ -1855,7 +1855,7 @@ CObjectListWindow::CObjectListWindow(const std::vector<int> &_items, CPicture *
 	init(titlePic, _title, _descr);
 }
 
-CObjectListWindow::CObjectListWindow(const std::vector<std::string> &_items, CPicture * titlePic, std::string _title, std::string _descr,
+CObjectListWindow::CObjectListWindow(const std::vector<std::string> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
 				std::function<void(int)> Callback):
     CWindowObject(PLAYER_COLORED, "TPGATE"),
 	onSelect(Callback)
@@ -1868,7 +1868,7 @@ CObjectListWindow::CObjectListWindow(const std::vector<std::string> &_items, CPi
 	init(titlePic, _title, _descr);
 }
 
-void CObjectListWindow::init(CPicture * titlePic, std::string _title, std::string _descr)
+void CObjectListWindow::init(CIntObject * titlePic, std::string _title, std::string _descr)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 

+ 5 - 5
client/GUIClasses.h

@@ -537,21 +537,21 @@ class CObjectListWindow : public CWindowObject
 	CLabel * title;
 	CLabel * descr;
 
-	CListBox *list;
-	CPicture *titleImage;//title image (castle gate\town portal picture)
+	CListBox * list;
+	CIntObject * titleImage;//title image (castle gate\town portal picture)
 	CAdventureMapButton *ok, *exit;
 
 	std::vector< std::pair<int, std::string> > items;//all items present in list
 
-	void init(CPicture * titlePic, std::string _title, std::string _descr);
+	void init(CIntObject * titlePic, std::string _title, std::string _descr);
 public:
 	size_t selected;//index of currently selected item
 	/// Callback will be called when OK button is pressed, returns id of selected item. initState = initially selected item
 	/// Image can be nullptr
 	///item names will be taken from map objects
-	CObjectListWindow(const std::vector<int> &_items, CPicture * titlePic, std::string _title, std::string _descr,
+	CObjectListWindow(const std::vector<int> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
                       std::function<void(int)> Callback);
-	CObjectListWindow(const std::vector<std::string> &_items, CPicture * titlePic, std::string _title, std::string _descr,
+	CObjectListWindow(const std::vector<std::string> &_items, CIntObject * titlePic, std::string _title, std::string _descr,
                       std::function<void(int)> Callback);
 
 	CIntObject *genItem(size_t index);

+ 12 - 5
client/Graphics.cpp

@@ -17,12 +17,14 @@
 #include "../lib/CCreatureHandler.h"
 #include "CBitmapHandler.h"
 #include "../lib/CObjectHandler.h"
+#include "../lib/CSpellHandler.h"
 #include "../lib/CDefObjInfoHandler.h"
 #include "../lib/CGameState.h"
 #include "../lib/JsonNode.h"
 #include "../lib/vcmi_endian.h"
 #include "../lib/GameConstants.h"
 #include "../lib/CStopWatch.h"
+#include "CAnimation.h"
 
 using namespace boost::assign;
 using namespace CSDL_Ext;
@@ -115,9 +117,7 @@ void Graphics::initializeBattleGraphics()
 		}
 
 		battleACToDef[ACid] = toAdd;
-	}
-
-	spellEffectsPics = CDefHandler::giveDefEss("SpellInt.def");
+	}	
 }
 Graphics::Graphics()
 {
@@ -128,8 +128,7 @@ Graphics::Graphics()
 	tasks += boost::bind(&Graphics::initializeBattleGraphics,this);
 	tasks += boost::bind(&Graphics::loadErmuToPicture,this);
 	tasks += boost::bind(&Graphics::initializeImageLists,this);
-	tasks += GET_DEF_ESS(resources32,"RESOURCE.DEF");
-	tasks += GET_DEF_ESS(spellscr,"SPELLSCR.DEF");
+	tasks += GET_DEF_ESS(resources32,"RESOURCE.DEF");	
 	tasks += GET_DEF_ESS(heroMoveArrows,"ADAG.DEF");
 
 	CThreadHelper th(&tasks,std::max((ui32)1,boost::thread::hardware_concurrency()));
@@ -411,4 +410,12 @@ void Graphics::initializeImageLists()
 			addImageListEntry(info.icons[1][1] + 2, "ITPA", info.iconSmall[1][1]);
 		}
 	}
+	
+	for(const CSpell * spell : CGI->spellh->objects)
+	{
+		addImageListEntry(spell->id, "SPELLS", spell->iconBook);
+		addImageListEntry(spell->id+1, "SPELLINT", spell->iconEffect);
+		addImageListEntry(spell->id, "SPELLBON", spell->iconScenarioBonus);
+		addImageListEntry(spell->id, "SPELLSCR", spell->iconScroll);		
+	}
 }

+ 2 - 4
client/Graphics.h

@@ -26,6 +26,7 @@ struct InfoAboutHero;
 struct InfoAboutTown;
 class CGObjectInstance;
 class ObjectTemplate;
+class CAnimation;
 
 enum EFonts
 {
@@ -41,7 +42,7 @@ public:
 	//Fonts
 	static const int FONTS_NUMBER = 9;
 	IFont * fonts[FONTS_NUMBER];
-	\
+	
 	//various graphics
 	SDL_Color * playerColors; //array [8]
 	SDL_Color * neutralColor;
@@ -66,9 +67,6 @@ public:
 	//for battles
 	std::vector< std::vector< std::string > > battleBacks; //battleBacks[terType] - vector of possible names for certain terrain type
 	std::map< int, std::vector < std::string > > battleACToDef; //maps AC format to vector of appropriate def names
-	CDefEssential * spellEffectsPics; //bitmaps representing spells affecting a stack in battle
-	//spells
-	CDefEssential *spellscr; //spell on the scroll 83x61
 	//functions
 	Graphics();	
 	void initializeBattleGraphics();

+ 4 - 2
client/battle/CBattleInterface.cpp

@@ -1230,8 +1230,10 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 
 	std::vector< std::string > anims; //for magic arrow and ice bolt
 
-	if (vstd::contains(CCS->soundh->spellSounds, &spell))
-		CCS->soundh->playSound(CCS->soundh->spellSounds[&spell]);
+	const std::string& castSoundPath = spell.getCastSound();
+	
+	if(!castSoundPath.empty())
+		CCS->soundh->playSound(castSoundPath);
 
 	switch(sc->id)
 	{

+ 36 - 5
config/schemas/spell.json

@@ -1,4 +1,4 @@
-{
+        {
 
 	"type":"object",
 	"$schema": "http://json-schema.org/draft-04/schema",
@@ -96,7 +96,7 @@
                 },
 
                 "defaultGainChance":{
-                        "type": "nomber",
+                        "type": "number",
                         "description": "Gain chance by default for all factions"
 
                 },
@@ -115,7 +115,7 @@
                 },
                 "anim":{
                         "type": "number",
-                        "description": "Main effect animation (AC format), -1 - none",
+                        "description": "Main effect animation (AC format), -1 - none, deprecated",
                         "minimum": -1
                 },
                 "counters":{
@@ -175,14 +175,45 @@
                          "additionalProperties" : false,
                          "properties":{
                                  "iconImmune":{
-                                      "type":"string",
-                                      "description":"Resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)",
+                                      "type": "string",
+                                      "description": "Resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)",
+                                      "format" : "imageFile"
+                                 },
+                                 "iconScenarioBonus":{
+                                      "type": "string",
+                                      "description": "Resourse path of icon for scenario bonus" ,
+                                      "format" : "imageFile"
+                                 },
+                                 "iconEffect":{
+                                      "type": "string",
+                                      "description": "Resourse path of icon for spell effects during battle" ,
+                                      "format" : "imageFile"
+                                 },
+                                 "iconBook":{
+                                      "type": "string",
+                                      "description":"Resourse path of icon for spellbook" ,
                                       "format" : "imageFile"
+                                 },
+                                 "iconScroll":{
+                                      "type": "string",
+                                      "description": "Resourse path of icon for spell scrolls",
+                                      "format": "imageFile"
                                  }
                          }
 
                 },
 
+                "sounds":{
+                         "type": "object",
+                         "additionalProperties" : false,
+                         "properties":{
+                                 "cast":{
+                                      "type": "string",
+                                      "description": "Resourse path of cast sound"
+                                 }
+                         }
+                },
+
                 "levels":{
                          "type": "object",
                          "additionalProperties" : false,

+ 0 - 412
config/sp_sounds.json

@@ -1,415 +1,3 @@
-// Sounds associated with spells
-// "name" is not currently used in game
-
-{
-	"spell_sounds": 
-	[
-		{
-			"id": 0 ,
-			"name":"Summon boat",
-			"soundfile":"SUMMBOAT.wav"
-		},
-		{
-			"id": 1 ,
-			"name":"Scuttle boat",
-			"soundfile":"SCUTBOAT.wav"
-		},
-		{
-			"id": 2 ,
-			"name":"Visions",
-			"soundfile":"VISIONS.wav"
-		},
-		{
-			"id": 3 ,
-			"name":"View Earth",
-			"soundfile":"VIEW.wav"
-		},
-		{
-			"id": 4 ,
-			"name":"Disguise",
-			"soundfile":"DISGUISE.wav"
-		},
-		{
-			"id": 5 ,
-			"name":"View Air",
-			"soundfile":"VIEW.wav"
-		},
-		{
-			"id": 6 ,
-			"name":"Fly",
-			"soundfile":"FLYSPELL.wav"
-		},
-		{
-			"id": 7 ,
-			"name":"Water walking",
-			"soundfile":"WATRWALK.wav"
-		},
-		{
-			"id": 8 ,
-			"name":"Dimension doors",
-			"soundfile":"TELPTOUT.wav"
-		},
-		{
-			"id": 9 ,
-			"name":"Town Portal",
-			"soundfile":"TELPTOUT.wav"
-		},
-		{
-			"id": 10 ,
-			"name":"Quick sands",
-			"soundfile":"QUIKSAND.wav"
-		},
-		{
-			"id": 12 ,
-			"name":"Force field",
-			"soundfile":"FORCEFLD.wav"
-		},
-		{
-			"id": 13 ,
-			"name":"Fire wall",
-			"soundfile":"FIREWALL.wav"
-		},
-		{
-			"id": 14 ,
-			"name":"Earthquake",
-			"soundfile":"ERTHQUAK.wav"
-		},
-		{
-			"id": 15,
-			"soundfile": "MAGICBLT.wav",
-			"name": "magic arrow"
-		},
-		{
-			"id": 16,
-			"soundfile": "ICERAY.wav",
-			"name": "ice bolt"
-		},
-		{
-			"id": 17,
-			"soundfile": "LIGHTBLT.wav",
-			"name": "lightning bolt"
-		},
-		{
-			"id": 18 ,
-			"name":"Implosion",
-			"soundfile":"DECAY.wav"
-		},
-		{
-			"id": 19 ,
-			"name":"Chaon lighting",
-			"soundfile":"CHAINLTE.wav"
-		},
-		{
-			"id": 20,
-			"soundfile": "FROSTING.wav",
-			"name": "frost ring"
-		},
-		{
-			"id": 21,
-			"soundfile": "FIREBALL.wav",
-			"name": "fireball"
-		},
-		{
-			"id": 22 ,
-			"name":"Inferno",
-			"soundfile":"FIREBLST.wav"
-		},
-		{
-			"id": 23,
-			"soundfile": "METEOR.wav",
-			"name": "meteor shower"
-		},
-		{
-			"id": 24,
-			"soundfile": "DEATHRIP.wav",
-			"name": "death ripple"
-		},
-		{
-			"id": 25 ,
-			"name":"Destroy undead",
-			"soundfile":"COLDRING.wav"
-		},
-		{
-			"id": 26,
-			"soundfile": "ARMGEDN.wav",
-			"name": "armageddon"
-		},
-		{
-			"id": 27,
-			"soundfile": "SHIELD.wav",
-			"name": "shield "
-		},
-		{
-			"id": 28,
-			"soundfile": "AIRSHELD.wav",
-			"name": "air shield"
-		},
-		{
-			"id": 29 ,
-			"name":"fireshield cast",
-			"soundfile":"FIRESHIE.wav"
-		},
-// It looks that fireshield has two separate souds. Disabling one of them for now
-//		{
-//			"id": 29 ,
-//			"name":"fireshield effect",
-//			"soundfile":"FIRESHLD.wav"
-//		},
-		{
-			"id": 30,
-			"soundfile": "PROTECTA.wav",
-			"name": "protection from air"
-		},
-		{
-			"id": 31,
-			"soundfile": "PROTECTF.wav",
-			"name": "protection from fire"
-		},
-		{
-			"id": 32,
-			"soundfile": "PROTECTW.wav",
-			"name": "protection from water"
-		},
-		{
-			"id": 33,
-			"soundfile": "PROTECTE.wav",
-			"name": "protection from earth"
-		},
-		{
-			"id": 34,
-			"soundfile": "ANIMDEAD.wav"
-		},
-		{
-			"id": 35,
-			"soundfile": "DISPELL.wav",
-			"name": "dispell"
-		},
-		{
-			"id": 36 ,
-			"name":"Magic mirror",
-			"soundfile":"BACKLASH.wav"
-		},
-		{
-			"id": 37 ,
-			"name":"Cure",
-			"soundfile":"CURE.wav"
-		},
-		{
-			"id": 38 ,
-			"name":"Resurrect",
-			"soundfile":"RESURECT.wav"
-		},
-		{
-			"id": 39,
-			"soundfile": "ANIMDEAD.wav"
-		},
-		{
-			"id": 40 ,
-			"name":"Sacrifice",
-			"soundfile":"SACRIF1.wav"
-		},
-		{
-			"id": 41,
-			"soundfile": "BLESS.wav",
-			"name": "bless"
-		},
-		{
-			"id": 42,
-			"soundfile": "CURSE.wav",
-			"name": "curse"
-		},
-		{
-			"id": 43,
-			"soundfile": "BLOODLUS.wav",
-			"name": "bloodlust"
-		},
-		{
-			"id": 44,
-			"soundfile": "PRECISON.wav",
-			"name": "precision"
-		},
-		{
-			"id": 45,
-			"soundfile": "WEAKNESS.wav",
-			"name": "weakness"
-		},
-		{
-			"id": 46,
-			"soundfile": "TUFFSKIN.wav",
-			"name": "stone skin"
-		},
-		{
-			"id": 47,
-			"soundfile": "DISRUPTR.wav",
-			"name": "disrupting ray"
-		},
-		{
-			"id": 48,
-			"soundfile": "PRAYER.wav",
-			"name": "prayer"
-		},
-		{
-			"id": 49,
-			"soundfile": "MIRTH.wav",
-			"name": "mirth"
-		},
-		{
-			"id": 50,
-			"soundfile": "SORROW.wav",
-			"name": "sorrow"
-		},
-		{
-			"id": 51,
-			"soundfile": "FORTUNE.wav",
-			"name": "fortune"
-		},
-		{
-			"id": 52,
-			"soundfile": "MISFORT.wav",
-			"name": "misfortune"
-		},
-		{
-			"id": 53,
-			"soundfile": "HASTE.wav",
-			"name": "haste"
-		},
-		{
-			"id": 54,
-			"soundfile": "MUCKMIRE.wav",
-			"name": "slow"
-		},
-		{
-			"id": 55,
-			"soundfile": "SLAYER.wav",
-			"name": "slayer"
-		},
-		{
-			"id": 56,
-			"soundfile": "FRENZY.wav",
-			"name": "frenzy"
-		},
-		{
-			"id": 57 ,
-			"name":"Titan Bolt",
-			"soundfile":"LIGHTBLT.wav"
-		},
-		{
-			"id": 58 ,
-			"name":"Counterstrike",
-			"soundfile":"CNTRSTRK.wav"
-		},
-		{
-			"id": 59 ,
-			"name":"Berserk",
-			"soundfile":"BERSERK.wav"
-		},
-		{
-			"id": 60,
-			"soundfile": "HYPNOTIZ.wav",
-			"name": "forgetfulness"
-		},
-		{
-			"id": 61,
-			"soundfile": "FORGET.wav",
-			"name": "forgetfulness"
-		},
-		{
-			"id": 62,
-			"soundfile": "BLIND.wav"
-		},
-		{
-			"id": 63 ,
-			"name":"Teleport",
-			"soundfile":"TELPTOUT.wav"
-		},
-		{
-			"id": 64 ,
-			"name":"Remove obstacle",
-			"soundfile":"REMOVEOB.wav"
-		},
-		{
-			"id": 65 ,
-			"name":"Clone",
-			"soundfile":"CLONE.wav"
-		},
-		{
-			"id": 66 ,
-			"name":"Summon elementals",
-			"soundfile":"SUMNELM.wav"
-		},
-		{
-			"id": 67 ,
-			"name":"Summon elementals",
-			"soundfile":"SUMNELM.wav"
-		},
-		{
-			"id": 68 ,
-			"name":"Summon elementals",
-			"soundfile":"SUMNELM.wav"
-		},
-		{
-			"id": 69 ,
-			"name":"Summon elementals",
-			"soundfile":"SUMNELM.wav"
-		},
-		{
-			"id": 70,
-			"soundfile": "PARALYZE.wav",
-			"name": "stone gaze and paralyze"
-		},
-		{
-			"id": 71,
-			"soundfile": "POISON.wav",
-			"name": "poison"
-		},
-		{
-			"id": 72,
-			"soundfile": "BIND.wav",
-			"name": " bind"
-		},
-		{
-			"id": 73,
-			"soundfile": "DISEASE.wav",
-			"name": ""
-		},
-		{
-			"id": 74,
-			"soundfile": "PARALYZE.wav",
-			"name": ""
-		},
-		{
-			"id": 75,
-			"soundfile": "AGE.wav",
-			"name": "aging"
-		},
-
-		{
-			"id": 76,
-			"soundfile": "DEATHCLD.wav",
-			"name": "death cloud"
-		},
-		{
-			"id": 77,
-			"soundfile": "LIGHTBLT.wav",
-			"name": "thunder"
-		},
-		{
-			"id": 78,
-			"soundfile": "DISPELL.wav",
-			"name": "dispell helpful spells"
-		},
-		{
-			"id": 79,
-			"soundfile": "DEATHSTR.wav",
-			"name": "Death Stare"
-		},
-		{
-			"id": 80,
-			"soundfile": "ACID.wav",
-			"name": " Acid breath / defence piercing"
-		}
-	]
-}
 
 // Several probably missing sounds of creature abilities
 // ACID.WAV        Acid breath    Rust Dragons

+ 261 - 13
config/spell_info.json

@@ -2,17 +2,20 @@
 	"summonBoat" : {
 		"index" : 0,
 		"anim" : -1,
+		"sounds": {
+			"cast": "SUMMBOAT"
+		},
 		"levels" :{
-                        "none":{
+			"none":{
 				"range" : "X"
 			},
-                        "basic":{
+			"basic":{
 				"range" : "X"
 			},
-                        "advanced":{
+			"advanced":{
 				"range" : "X"
 			},
-                        "expert":{
+			"expert":{
 				"range" : "X"
 			}
 		},
@@ -23,18 +26,20 @@
 	"scuttleBoat" : {
 		"index" : 1,
 		"anim" : -1,
-		"levels" :
-                {
-                        "none":	{
+		"sounds": {
+			"cast": "SCUTBOAT"
+		},
+		"levels" : {
+			"none":	{
 				"range" : "X"
 			},
-                        "basic":{
+			"basic":{
 				"range" : "X"
 			},
-                        "advanced":{
+			"advanced":{
 				"range" : "X"
 			},
-                        "expert":{
+			"expert":{
 				"range" : "X"
 			}
 		},
@@ -45,6 +50,9 @@
 	"visions" : {
 		"index" : 2,
 		"anim" : -1,
+		"sounds": {
+			"cast": "VISIONS"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -66,6 +74,9 @@
 	"viewEarth" : {
 		"index" : 3,
 		"anim" : -1,
+		"sounds": {
+			"cast": "VIEW"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -87,6 +98,9 @@
 	"disguise" : {
 		"index" : 4,
 		"anim" : -1,
+		"sounds": {
+			"cast": "DISGUISE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -108,6 +122,9 @@
 	"viewAir" : {
 		"index" : 5,
 		"anim" : -1,
+		"sounds": {
+			"cast": "VIEW"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -129,6 +146,9 @@
 	"fly" : {
 		"index" : 6,
 		"anim" : -1,
+		"sounds": {
+			"cast": "FLYSPELL"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -150,6 +170,9 @@
 	"waterWalk" : {
 		"index" : 7,
 		"anim" : -1,
+		"sounds": {
+			"cast": "WATRWALK"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -171,6 +194,9 @@
 	"dimensionDoor" : {
 		"index" : 8,
 		"anim" : -1,
+		"sounds": {
+			"cast": "TELPTOUT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -192,6 +218,9 @@
 	"townPortal" : {
 		"index" : 9,
 		"anim" : -1,
+		"sounds": {
+			"cast": "TELPTOUT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -213,6 +242,9 @@
 	"quicksand" : {
 		"index" : 10,
 		"anim" : -1,
+		"sounds": {
+			"cast": "QUIKSAND"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -234,6 +266,9 @@
 	"landMine" : {
 		"index" : 11,
 		"anim" : -1,
+		"sounds": {
+			"cast": ""
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -259,6 +294,9 @@
 	"forceField" : {
 		"index" : 12,
 		"anim" : -1,
+		"sounds": {
+			"cast": "FORCEFLD"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -280,6 +318,9 @@
 	"fireWall" : {
 		"index" : 13,
 		"anim" : -1,
+		"sounds": {
+			"cast": "FIREWALL"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -305,6 +346,9 @@
 	"earthquake" : {
 		"index" : 14,
 		"anim" : -1,
+		"sounds": {
+			"cast": "ERTHQUAK"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -326,6 +370,9 @@
 	"magicArrow" : {
 		"index" : 15,
 		"anim" : 64,
+		"sounds": {
+			"cast": "MAGICBLT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -352,6 +399,9 @@
 	"iceBolt" : {
 		"index" : 16,
 		"anim" : 46,
+		"sounds": {
+			"cast": "ICERAY"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -378,6 +428,9 @@
 	"lightningBolt" : {
 		"index" : 17,
 		"anim" : 38,
+		"sounds": {
+			"cast": "LIGHTBLT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -404,6 +457,9 @@
 	"implosion" : {
 		"index" : 18,
 		"anim" : 10,
+		"sounds": {
+			"cast": "DECAY"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -431,6 +487,9 @@
 	"chainLightning" : {
 		"index" : 19,
 		"anim" : 38,
+		"sounds": {
+			"cast": "CHAINLTE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -454,6 +513,9 @@
 	"frostRing" : {
 		"index" : 20,
 		"anim" : 45,
+		"sounds": {
+			"cast": "FROSTING"
+		},
 		"levels" : {
 			"none":{
 				"range" : "1"
@@ -480,6 +542,9 @@
 	"fireball" : {
 		"index" : 21,
 		"anim" : 53,
+		"sounds": {
+			"cast": "FIREBALL"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0,1"
@@ -506,6 +571,9 @@
 	"inferno" : {
 		"index" : 22,
 		"anim" : 9,
+		"sounds": {
+			"cast": "FIREBLST"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0-2"
@@ -532,6 +600,9 @@
 	"meteorShower" : {
 		"index" : 23,
 		"anim" : 16,
+		"sounds": {
+			"cast": "METEOR"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0,1"
@@ -558,6 +629,9 @@
 	"deathRipple" : {
 		"index" : 24,
 		"anim" : 8,
+		"sounds": {
+			"cast": "DEATHRIP"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -586,6 +660,9 @@
 	"destroyUndead" : {
 		"index" : 25,
 		"anim" : 29,
+		"sounds": {
+			"cast": "COLDRING"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -615,6 +692,9 @@
 	"armageddon" : {
 		"index" : 26,
 		"anim" : 12,
+		"sounds": {
+			"cast": "ARMGEDN"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -641,6 +721,9 @@
 	"shield" : {
 		"index" : 27,
 		"anim" : 27,
+		"sounds": {
+			"cast": "SHIELD"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -690,6 +773,9 @@
 	"airShield" : {
 		"index" : 28,
 		"anim" : 2,
+		"sounds": {
+			"cast": "AIRSHELD"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -739,6 +825,12 @@
 	"fireShield" : {
 		"index" : 29,
 		"anim" : 11,
+		"sounds": {
+			"cast": "FIRESHIE"
+		},
+		// It looks that fireshield has two separate sounds
+		//			"soundfile":"FIRESHLD.wav"
+		//		
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -784,6 +876,9 @@
 	"protectAir" : {
 		"index" : 30,
 		"anim" : 22,
+		"sounds": {
+			"cast": "PROTECTA"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -833,6 +928,9 @@
 	"protectFire" : {
 		"index" : 31,
 		"anim" : 24,
+		"sounds": {
+			"cast": "PROTECTF"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -882,6 +980,9 @@
 	"protectWater" : {
 		"index" : 32,
 		"anim" : 23,
+		"sounds": {
+			"cast": "PROTECTW"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -931,6 +1032,9 @@
 	"protectEarth" : {
 		"index" : 33,
 		"anim" : 26,
+		"sounds": {
+			"cast": "PROTECTE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -980,6 +1084,9 @@
 	"antiMagic" : {
 		"index" : 34,
 		"anim" : 5,
+		"sounds": {
+			"cast": "ANTIMAGK"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1033,6 +1140,9 @@
 	"dispel" : {
 		"index" : 35,
 		"anim" : 41,
+		"sounds": {
+			"cast": "DISPELL"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -1054,6 +1164,9 @@
 	"magicMirror" : {
 		"index" : 36,
 		"anim" : 3,
+		"sounds": {
+			"cast": "BACKLASH"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1103,6 +1216,9 @@
 	"cure" : {
 		"index" : 37,
 		"anim" : 39,
+		"sounds": {
+			"cast": "CURE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -1124,6 +1240,9 @@
 	"resurrection" : {
 		"index" : 38,
 		"anim" : 79,
+		"sounds": {
+			"cast": "RESURECT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -1150,6 +1269,9 @@
 	"animateDead" : {
 		"index" : 39,
 		"anim" : 79,
+		"sounds": {
+			"cast": "ANIMDEAD"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -1175,6 +1297,9 @@
 	"sacrifice" : {
 		"index" : 40,
 		"anim" : 79,
+		"sounds": {
+			"cast": "SACRIF1"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -1201,6 +1326,9 @@
 	"bless" : {
 		"index" : 41,
 		"anim" : 36,
+		"sounds": {
+			"cast": "BLESS"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1264,6 +1392,9 @@
 	"curse" : {
 		"index" : 42,
 		"anim" : 40,
+		"sounds": {
+			"cast": "CURSE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1331,6 +1462,9 @@
 	"bloodlust" : {
 		"index" : 43,
 		"anim" : 4,
+		"sounds": {
+			"cast": "BLOODLUS"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1391,6 +1525,9 @@
 	"precision" : {
 		"index" : 44,
 		"anim" : 25,
+		"sounds": {
+			"cast": "PRECISON"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1451,6 +1588,9 @@
 	"weakness" : {
 		"index" : 45,
 		"anim" : 56,
+		"sounds": {
+			"cast": "WEAKNESS"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1507,6 +1647,9 @@
 	"stoneSkin" : {
 		"index" : 46,
 		"anim" : 54,
+		"sounds": {
+			"cast": "TUFFSKIN"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1559,8 +1702,11 @@
 	},
 	"disruptingRay" : {
 		"index" : 47,
-                "targetType" : "CREATURE", //fix, dont remove
+				"targetType" : "CREATURE", //fix, dont remove
 		"anim" : 14,
+		"sounds": {
+			"cast": "DISRUPTR"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1618,6 +1764,9 @@
 	"prayer" : {
 		"index" : 48,
 		"anim" : 0,
+		"sounds": {
+			"cast": "PRAYER"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1719,6 +1868,9 @@
 	"mirth" : {
 		"index" : 49,
 		"anim" : 20,
+		"sounds": {
+			"cast": "MIRTH"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1771,6 +1923,9 @@
 	"sorrow" : {
 		"index" : 50,
 		"anim" : 30,
+		"sounds": {
+			"cast": "SORROW"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1828,6 +1983,9 @@
 	"fortune" : {
 		"index" : 51,
 		"anim" : 18,
+		"sounds": {
+			"cast": "FORTUNE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1880,6 +2038,9 @@
 	"misfortune" : {
 		"index" : 52,
 		"anim" : 48,
+		"sounds": {
+			"cast": "MISFORT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1932,6 +2093,9 @@
 	"haste" : {
 		"index" : 53,
 		"anim" : 31,
+		"sounds": {
+			"cast": "HASTE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -1991,6 +2155,9 @@
 	"slow" : {
 		"index" : 54,
 		"anim" : 19,
+		"sounds": {
+			"cast": "MUCKMIRE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2055,6 +2222,9 @@
 	"slayer" : {
 		"index" : 55,
 		"anim" : 28,
+		"sounds": {
+			"cast": "SLAYER"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2104,6 +2274,9 @@
 	"frenzy" : {
 		"index" : 56,
 		"anim" : 17,
+		"sounds": {
+			"cast": "FRENZY"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2153,6 +2326,9 @@
 	"titanBolt" : {
 		"index" : 57,
 		"anim" : 38,
+		"sounds": {
+			"cast": "LIGHTBLT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -2171,12 +2347,15 @@
 			"damage": true,
 			"offensive": true,
 			"negative": true,
-                        "special": true
+						"special": true
 		}
 	},
 	"counterstrike" : {
 		"index" : 58,
 		"anim" : 7,
+		"sounds": {
+			"cast": "CNTRSTRK"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2226,6 +2405,9 @@
 	"berserk" : {
 		"index" : 59,
 		"anim" : 35,
+		"sounds": {
+			"cast": "BERSERK"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2281,6 +2463,9 @@
 	"hypnotize" : {
 		"index" : 60,
 		"anim" : 21,
+		"sounds": {
+			"cast": "HYPNOTIZ"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2335,8 +2520,11 @@
 	},
 	"forgetfulness" : {
 		"index" : 61,
-                "targetType" : "CREATURE_EXPERT_MASSIVE", //fix dont remove
+		"targetType" : "CREATURE_EXPERT_MASSIVE", //fix dont remove
 		"anim" : 42,
+		"sounds": {
+			"cast": "FORGET"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2394,6 +2582,9 @@
 	"blind" : {
 		"index" : 62,
 		"anim" : 6,
+		"sounds": {
+			"cast": "BLIND"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2513,6 +2704,9 @@
 	"teleport" : {
 		"index" : 63,
 		"anim" : -1,
+		"sounds": {
+			"cast": "TELPTOUT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -2537,6 +2731,9 @@
 	"removeObstacle" : {
 		"index" : 64,
 		"anim" : -1,
+		"sounds": {
+			"cast": "REMOVEOB"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -2558,6 +2755,9 @@
 	"clone" : {
 		"index" : 65,
 		"anim" : -1,
+		"sounds": {
+			"cast": "CLONE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -2582,6 +2782,9 @@
 	"fireElemental" : {
 		"index" : 66,
 		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -2603,6 +2806,9 @@
 	"earthElemental" : {
 		"index" : 67,
 		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -2624,6 +2830,9 @@
 	"waterElemental" : {
 		"index" : 68,
 		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -2645,6 +2854,9 @@
 	"airElemental" : {
 		"index" : 69,
 		"anim" : -1,
+		"sounds": {
+			"cast": "SUMNELM"
+		},
 		"levels" : {
 			"none":{
 				"range" : "X"
@@ -2666,6 +2878,9 @@
 	"stoneGaze" : {
 		"index" : 70,
 		"anim" : 70,
+		"sounds": {
+			"cast": "PARALYZE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2751,6 +2966,9 @@
 	"poison" : {
 		"index" : 71,
 		"anim" : 67,
+		"sounds": {
+			"cast": "POISON"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2832,6 +3050,9 @@
 	"bind" : {
 		"index" : 72,
 		"anim" : 68,
+		"sounds": {
+			"cast": "BIND"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2885,6 +3106,9 @@
 	"disease" : {
 		"index" : 73,
 		"anim" : 69,
+		"sounds": {
+			"cast": "DISEASE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -2966,6 +3190,9 @@
 	"paralyze" : {
 		"index" : 74,
 		"anim" : 70,
+		"sounds": {
+			"cast": "PARALYZE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -3051,6 +3278,9 @@
 	"age" : {
 		"index" : 75,
 		"anim" : 71,
+		"sounds": {
+			"cast": "AGE"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -3108,6 +3338,9 @@
 	"deathCloud" : {
 		"index" : 76,
 		"anim" : 72,
+		"sounds": {
+			"cast": "DEATHCLD"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0-1"
@@ -3133,6 +3366,9 @@
 	"thunderbolt" : {
 		"index" : 77,
 		"anim" : 38,
+		"sounds": {
+			"cast": "LIGHTBLT"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -3156,6 +3392,9 @@
 	"dispelHelpful" : {
 		"index" : 78,
 		"anim" : 41,
+		"sounds": {
+			"cast": "DISPELL"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -3178,6 +3417,9 @@
 	"deathStare" : {
 		"index" : 79,
 		"anim" : 80,
+		"sounds": {
+			"cast": "DEATHSTR"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"
@@ -3203,6 +3445,9 @@
 	"acidBreath" : {
 		"index" : 80,
 		"anim" : 81,
+		"sounds": {
+			"cast": "ACID"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0",
@@ -3260,6 +3505,9 @@
 	"acidBreathDamage" : {
 		"index" : 81,
 		"anim" : 81,
+		"sounds": {
+			"cast": "ACID"
+		},
 		"levels" : {
 			"none":{
 				"range" : "0"

+ 1 - 1
lib/CBattleCallback.cpp

@@ -2063,7 +2063,7 @@ std::set<const CStack*> CBattleInfoCallback::getAffectedCreatures(const CSpell *
 		{
 			if(const CStack * st = battleGetStackByPos(hex, onlyAlive))
 			{
-				if (spell->id == 76) //Death Cloud //TODO: fireball and fire immunity
+				if (spell->id == SpellID::DEATH_CLOUD) //Death Cloud //TODO: fireball and fire immunity
 				{
 					if (st->isLiving() || st->coversPos(destinationTile)) //directly hit or alive
 					{

+ 17 - 5
lib/CSpellHandler.cpp

@@ -313,6 +313,13 @@ const std::string& CSpell::getIconImmune() const
 	return iconImmune;
 }
 
+const std::string& CSpell::getCastSound() const
+{
+	return castSound;
+}
+
+
+
 si32 CSpell::getCost(const int skillLevel) const
 {
     return costs[skillLevel];
@@ -693,11 +700,6 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
 
 	//by default all flags are set to false in constructor
 
-	if(flags["summoning"].Bool())
-	{
-		logGlobal->warnStream() << spell->name << ": summoning flag in unimplemented";
-	}
-
 	spell->isDamage = flags["damage"].Bool(); //do this before "offensive"
 
 	if(flags["offensive"].Bool())
@@ -767,6 +769,16 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
     const JsonNode & graphicsNode = json["graphics"];
  
 	spell->iconImmune = graphicsNode["iconImmune"].String();
+	spell->iconBook = graphicsNode["iconBook"].String();
+	spell->iconEffect = graphicsNode["iconEffect"].String();
+	spell->iconScenarioBonus = graphicsNode["iconScenarioBonus"].String();
+	spell->iconScroll = graphicsNode["iconScroll"].String();
+	
+	
+	
+	const JsonNode & soundsNode = json["sounds"];
+	
+	spell->castSound = soundsNode["cast"].String();
 
 
     //load level attributes

+ 14 - 3
lib/CSpellHandler.h

@@ -102,6 +102,8 @@ public:
 	* Returns resource name of icon for SPELL_IMMUNITY bonus
 	*/
 	const std::string& getIconImmune() const;
+	
+	const std::string& getCastSound() const;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -114,8 +116,12 @@ public:
         h & absoluteImmunities & defaultProbability;
 
         h & isSpecial;
+        
+        h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll ;
+		
 	}
 	friend class CSpellHandler;
+	friend class Graphics;
 
 private:
     void setIsOffensive(const bool val);
@@ -147,9 +153,14 @@ private:
 	///graphics related stuff
 
 	std::string iconImmune;
-
-
-
+	
+	std::string iconBook;
+	std::string iconEffect;
+	std::string iconScenarioBonus;	
+	std::string iconScroll;
+
+	///sound related stuff
+	std::string castSound;
 };
 
 

+ 1 - 1
lib/Connection.h

@@ -28,7 +28,7 @@
 #include "mapping/CCampaignHandler.h" //for CCampaignState
 #include "rmg/CMapGenerator.h" // for CMapGenOptions
 
-const ui32 version = 746;
+const ui32 version = 747;
 const ui32 minSupportedVersion = version;
 
 class CConnection;

+ 7 - 5
server/CGameHandler.cpp

@@ -4127,7 +4127,8 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
 					++chainLightningModifier;
 			}
 	}
-	else if (spell->hasEffects())
+	
+	if (spell->hasEffects())
 	{
 			int stackSpellPower = 0;
 			if (stack && mode != ECastingMode::MAGIC_MIRROR)
@@ -4142,7 +4143,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
 			CStack::stackEffectToFeature(sse.effect, pseudoBonus);
 			if (spellID == SpellID::SHIELD || spellID == SpellID::AIR_SHIELD)
 			{
-				sse.effect.back().val = (100 - sse.effect.back().val); //fix to original config: shiled should display damage reduction
+				sse.effect.back().val = (100 - sse.effect.back().val); //fix to original config: shield should display damage reduction
 			}
 			if (spellID == SpellID::BIND && stack)//bind
 			{
@@ -4209,7 +4210,8 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
 				sendAndApply(&sse);
 
 	}
-	else if (spell->isRisingSpell() || spell->id == SpellID::CURE)
+	
+	if (spell->isRisingSpell() || spell->id == SpellID::CURE)
 	{
 			int hpGained = 0;
 			if (stack)
@@ -4275,7 +4277,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
 				sendAndApply(&bsr);
 			}
 	}
-	else
+
 	switch (spellID)
 	{
 	case SpellID::QUICKSAND:
@@ -4370,7 +4372,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
 			if (!clonedStack)
 			{
 				complain ("No target stack to clone!");
-				return;
+				break;
 			}
 
 			BattleStackAdded bsa;