Browse Source

Merge pull request #240 from vcmi/spellWindowRewrite

Spell window rewrite
Alexander Shishkin 9 years ago
parent
commit
4876cc178d

+ 1 - 1
client/CPreGame.cpp

@@ -3667,7 +3667,7 @@ void CBonusSelection::updateBonusSelection()
 		if (picNumber != -1)
 			picName += ":" + boost::lexical_cast<std::string>(picNumber);
 
-		auto   anim = new CAnimation();
+		auto anim = std::make_shared<CAnimation>();
 		anim->setCustom(picName, 0);
 		bonusButton->setImage(anim);
 		const SDL_Color brightYellow = { 242, 226, 110, 0 };

+ 1 - 2
client/battle/CBattleInterface.cpp

@@ -898,8 +898,7 @@ void CBattleInterface::bSpellf()
 	ESpellCastProblem::ESpellCastProblem spellCastProblem;
 	if (curInt->cb->battleCanCastSpell(&spellCastProblem))
 	{
-		auto  spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), myHero, curInt.get());
-		GH.pushInt(spellWindow);
+		GH.pushInt(new CSpellWindow(myHero, curInt.get()));
 	}
 	else if(spellCastProblem == ESpellCastProblem::MAGIC_IS_BLOCKED)
 	{

+ 5 - 6
client/battle/CBattleInterfaceClasses.cpp

@@ -200,8 +200,7 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
 		}
 		CCS->curh->changeGraphic(ECursor::ADVENTURE, 0);
 
-		auto  spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), myHero, myOwner->getCurrentPlayerInterface());
-		GH.pushInt(spellWindow);
+		GH.pushInt(new CSpellWindow(myHero, myOwner->getCurrentPlayerInterface()));
 	}
 }
 
@@ -380,7 +379,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 		auto heroInfo = owner.cb->battleGetHeroInfo(i);
 		const int xs[] = {21, 392};
 
-		if(heroInfo.portrait >= 0) //attacking hero 
+		if(heroInfo.portrait >= 0) //attacking hero
 		{
 			new CAnimImage("PortraitsLarge", heroInfo.portrait, 0, xs[i], 38);
 			sideNames[i] = heroInfo.name;
@@ -448,7 +447,7 @@ CBattleResultWindow::CBattleResultWindow(const BattleResult &br, const SDL_Rect
 			boost::algorithm::replace_first(str,"%s",ourHero->name);
 			boost::algorithm::replace_first(str,"%d",boost::lexical_cast<std::string>(br.exp[weAreAttacker?0:1]));
 		}
-		
+
 		new CTextBox(str, Rect(69, 203, 330, 68), 0, FONT_SMALL, CENTER, Colors::WHITE);
 	}
 	else // we lose
@@ -509,7 +508,7 @@ void CBattleResultWindow::bExitf()
 	if(dynamic_cast<CBattleInterface*>(GH.topInt()))
 		GH.popInts(1); //pop battle interface if present
 
-	//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle, 
+	//Result window and battle interface are gone. We requested all dialogs to be closed before opening the battle,
 	//so we can be sure that there is no dialogs left on GUI stack.
 	intTmp.showingDialog->setn(false);
 	CCS->videoh->close();
@@ -532,7 +531,7 @@ Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBatt
 			break;
 		case -4: //upper turret
 			ret = cbi->siegeH->town->town->clientInfo.siegePositions[20];
-			break;	
+			break;
 		}
 	}
 	else

+ 0 - 17
client/gui/CAnimation.cpp

@@ -1065,7 +1065,6 @@ CAnimation::CAnimation(std::string Name, bool Compressed):
 	CDefFile * file = getFile();
 	init(file);
 	delete file;
-	loadedAnims.insert(this);
 }
 
 CAnimation::CAnimation():
@@ -1073,7 +1072,6 @@ CAnimation::CAnimation():
 	compressed(false)
 {
 	init(nullptr);
-	loadedAnims.insert(this);
 }
 
 CAnimation::~CAnimation()
@@ -1085,7 +1083,6 @@ CAnimation::~CAnimation()
 			for (auto & _image : elem.second)
 				delete _image.second;
 	}
-	loadedAnims.erase(this);
 }
 
 void CAnimation::setCustom(std::string filename, size_t frame, size_t group)
@@ -1167,20 +1164,6 @@ size_t CAnimation::size(size_t group) const
 	return 0;
 }
 
-std::set<CAnimation*> CAnimation::loadedAnims;
-
-void CAnimation::getAnimInfo()
-{
-	logGlobal->errorStream() << "Animation stats: Loaded " << loadedAnims.size() << " total";
-	for(auto anim : loadedAnims)
-	{
-		logGlobal->errorStream() << "Name: " << anim->name << " Groups: " << anim->images.size();
-		if(!anim->images.empty())
-			logGlobal->errorStream() << ", " << anim->images.begin()->second.size() << " image loaded in group " << anim->images.begin()->first;
-	}
-}
-
-
 float CFadeAnimation::initialCounter() const
 {
 	if (fadingMode == EMode::OUT)

+ 0 - 4
client/gui/CAnimation.h

@@ -195,10 +195,6 @@ public:
 	CAnimation();
 	~CAnimation();
 
-    //static method for debugging - print info about loaded animations
-	static void getAnimInfo();
-	static std::set<CAnimation*> loadedAnims;
-
 	//add custom surface to the selected position.
 	void setCustom(std::string filename, size_t frame, size_t group=0);
 

+ 5 - 4
client/widgets/Buttons.cpp

@@ -272,10 +272,11 @@ void CButton::setIndex(size_t index, bool playerColoredButton)
 	if (index == currentImage || index>=imageNames.size())
 		return;
 	currentImage = index;
-	setImage(new CAnimation(imageNames[index]), playerColoredButton);
+	auto anim = std::make_shared<CAnimation>(imageNames[index]);
+	setImage(anim, playerColoredButton);
 }
 
-void CButton::setImage(CAnimation* anim, bool playerColoredButton, int animFlags)
+void CButton::setImage(std::shared_ptr<CAnimation> anim, bool playerColoredButton, int animFlags)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 
@@ -294,7 +295,7 @@ void CButton::setPlayerColor(PlayerColor player)
 void CButton::showAll(SDL_Surface * to)
 {
 	CIntObject::showAll(to);
-	
+
 	if (borderColor && borderColor->a == 0)
 		CSDL_Ext::drawBorder(to, pos.x-1, pos.y-1, pos.w+2, pos.h+2, int3(borderColor->r, borderColor->g, borderColor->b));
 }
@@ -453,7 +454,7 @@ CVolumeSlider::CVolumeSlider(const Point &position, const std::string &defName,
 	helpHandlers(help)
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	animImage = new CAnimImage(new CAnimation(defName), 0, 0, position.x, position.y),
+	animImage = new CAnimImage(std::make_shared<CAnimation>(defName), 0, 0, position.x, position.y),
 	assert(!defName.empty());
 	addUsedEvents(LCLICK | RCLICK | WHEEL);
 	pos.x += position.x;

+ 1 - 1
client/widgets/Buttons.h

@@ -110,7 +110,7 @@ public:
 
 	/// Appearance modifiers
 	void setIndex(size_t index, bool playerColoredButton=false);
-	void setImage(CAnimation* anim, bool playerColoredButton=false, int animFlags=0);
+	void setImage(std::shared_ptr<CAnimation> anim, bool playerColoredButton=false, int animFlags=0);
 	void setPlayerColor(PlayerColor player);
 
 	/// CIntObject overrides

+ 1 - 4
client/widgets/CArtifactHolder.cpp

@@ -123,10 +123,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
 	if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
 	{
 		if(ourArt->artType->id == ArtifactID::SPELLBOOK)
-		{
-			auto   spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt);
-			GH.pushInt(spellWindow);
-		}
+			GH.pushInt(new CSpellWindow(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt));
 	}
 
 	if (!down && previousState)

+ 7 - 6
client/widgets/Images.cpp

@@ -78,6 +78,7 @@ CPicture::CPicture(const Rect &r, ui32 color, bool screenFormat /*= false*/)
 
 CPicture::CPicture(SDL_Surface *BG, const Rect &SrcRect, int x /*= 0*/, int y /*= 0*/, bool free /*= false*/)
 {
+	visible = true;
 	needRefresh = false;
 	srcRect = new Rect(SrcRect);
 	pos.x += x;
@@ -112,19 +113,20 @@ CPicture::~CPicture()
 
 void CPicture::init()
 {
+	visible = true;
 	needRefresh = false;
 	srcRect = nullptr;
 }
 
 void CPicture::show(SDL_Surface * to)
 {
-	if (needRefresh)
+	if (visible && needRefresh)
 		showAll(to);
 }
 
 void CPicture::showAll(SDL_Surface * to)
 {
-	if(bg)
+	if(bg && visible)
 	{
 		if(srcRect)
 		{
@@ -210,7 +212,7 @@ void CFilledTexture::showAll(SDL_Surface *to)
 	CSDL_Ext::fillTexture(to, texture);
 }
 
-CAnimImage::CAnimImage(std::string name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
+CAnimImage::CAnimImage(const std::string & name, size_t Frame, size_t Group, int x, int y, ui8 Flags):
 	frame(Frame),
 	group(Group),
 	player(-1),
@@ -218,11 +220,11 @@ CAnimImage::CAnimImage(std::string name, size_t Frame, size_t Group, int x, int
 {
 	pos.x += x;
 	pos.y += y;
-	anim = new CAnimation(name);
+	anim = std::make_shared<CAnimation>(name);
 	init();
 }
 
-CAnimImage::CAnimImage(CAnimation *Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags):
+CAnimImage::CAnimImage(std::shared_ptr<CAnimation> Anim, size_t Frame, size_t Group, int x, int y, ui8 Flags):
 	anim(Anim),
 	frame(Frame),
 	group(Group),
@@ -259,7 +261,6 @@ CAnimImage::~CAnimImage()
 	anim->unload(frame, group);
 	if (flags & CShowableAnim::BASE)
 		anim->unload(0,group);
-	delete anim;
 }
 
 void CAnimImage::showAll(SDL_Surface * to)

+ 4 - 4
client/widgets/Images.h

@@ -28,7 +28,7 @@ public:
 	Rect * srcRect; //if nullptr then whole surface will be used
 	bool freeSurf; //whether surface will be freed upon CPicture destruction
 	bool needRefresh;//Surface needs to be displayed each frame
-
+	bool visible;
 	operator SDL_Surface*()
 	{
 		return bg;
@@ -70,7 +70,7 @@ public:
 class CAnimImage: public CIntObject
 {
 private:
-	CAnimation* anim;
+	std::shared_ptr<CAnimation> anim;
 	//displayed frame/group
 	size_t frame;
 	size_t group;
@@ -82,8 +82,8 @@ private:
 public:
 	bool visible;
 
-	CAnimImage(std::string name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
-	CAnimImage(CAnimation* anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
+	CAnimImage(const std::string & name, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
+	CAnimImage(std::shared_ptr<CAnimation> Anim, size_t Frame, size_t Group=0, int x=0, int y=0, ui8 Flags=0);
 	~CAnimImage();//d-tor
 
 	//size of animation

+ 1 - 2
client/windows/CAdvmapInterface.cpp

@@ -714,8 +714,7 @@ void CAdvMapInt::fshowSpellbok()
 
 	centerOn(selection);
 
-	auto   spellWindow = new CSpellWindow(genRect(595, 620, (screen->w - 620)/2, (screen->h - 595)/2), curHero(), LOCPLINT, false);
-	GH.pushInt(spellWindow);
+	GH.pushInt(new CSpellWindow(curHero(), LOCPLINT, false));
 }
 
 void CAdvMapInt::fadventureOPtions()

+ 0 - 1
client/windows/CHeroWindow.cpp

@@ -4,7 +4,6 @@
 #include "CAdvmapInterface.h"
 #include "CCreatureWindow.h"
 #include "CKingdomInterface.h"
-#include "CSpellWindow.h"
 #include "GUIClasses.h"
 
 #include "../CBitmapHandler.h"

+ 216 - 306
client/windows/CSpellWindow.cpp

@@ -8,8 +8,6 @@
 #include "InfoWindows.h"
 #include "CCastleInterface.h"
 
-#include "../CBitmapHandler.h"
-#include "../CDefHandler.h"
 #include "../CGameInfo.h"
 #include "../CMessage.h"
 #include "../CMT.h"
@@ -23,6 +21,7 @@
 #include "../gui/SDL_Extensions.h"
 #include "../widgets/MiscWidgets.h"
 #include "../widgets/CComponent.h"
+#include "../widgets/TextControls.h"
 
 #include "../../CCallback.h"
 
@@ -45,45 +44,59 @@
  *
  */
 
-SpellbookInteractiveArea::SpellbookInteractiveArea(const SDL_Rect & myRect, std::function<void()> funcL,
-	const std::string & textR, std::function<void()> funcHon, std::function<void()> funcHoff, CPlayerInterface * _myInt)
+CSpellWindow::InteractiveArea::InteractiveArea(const SDL_Rect & myRect, std::function<void()> funcL, int helpTextId, CSpellWindow * _owner)
 {
 	addUsedEvents(LCLICK | RCLICK | HOVER);
 	pos = myRect;
 	onLeft = funcL;
-	textOnRclick = textR;
-	onHoverOn = funcHon;
-	onHoverOff = funcHoff;
-	myInt = _myInt;
+	hoverText = CGI->generaltexth->zelp[helpTextId].first;
+	helpText = CGI->generaltexth->zelp[helpTextId].second;
+	owner = _owner;
 }
 
-void SpellbookInteractiveArea::clickLeft(tribool down, bool previousState)
+void CSpellWindow::InteractiveArea::clickLeft(tribool down, bool previousState)
 {
 	if(!down)
-	{
 		onLeft();
-	}
 }
 
-void SpellbookInteractiveArea::clickRight(tribool down, bool previousState)
+void CSpellWindow::InteractiveArea::clickRight(tribool down, bool previousState)
 {
-	adventureInt->handleRightClick(textOnRclick, down);
+	adventureInt->handleRightClick(helpText, down);
 }
 
-void SpellbookInteractiveArea::hover(bool on)
+void CSpellWindow::InteractiveArea::hover(bool on)
 {
-	//Hoverable::hover(on);
 	if(on)
-	{
-		onHoverOn();
-	}
+		owner->statusBar->setText(hoverText);
 	else
+		owner->statusBar->clear();
+}
+
+class SpellbookSpellSorter
+{
+public:
+	bool operator()(const CSpell * A, const CSpell * B)
 	{
-		onHoverOff();
+		if(A->level < B->level)
+			return true;
+		if(A->level > B->level)
+			return false;
+
+
+		for(ui8 schoolId = 0; schoolId < 4; schoolId++)
+		{
+			if(A->school.at((ESpellSchool)schoolId) && !B->school.at((ESpellSchool)schoolId))
+				return true;
+			if(!A->school.at((ESpellSchool)schoolId) && B->school.at((ESpellSchool)schoolId))
+				return false;
+		}
+
+		return A->name < B->name;
 	}
-}
+} spellsorter;
 
-CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells):
+CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells):
     CWindowObject(PLAYER_COLORED, "SpelBack"),
 	battleSpellsOnly(openOnBattleSpells),
 	selectedTab(4),
@@ -91,12 +104,15 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 	myHero(_myHero),
 	myInt(_myInt)
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	//initializing castable spells
-	for(ui32 v=0; v<CGI->spellh->objects.size(); ++v)
+	mySpells.reserve(CGI->spellh->objects.size());
+	for(const CSpell * spell : CGI->spellh->objects)
 	{
-		if( !CGI->spellh->objects[v]->creatureAbility && myHero->canCastThisSpell(CGI->spellh->objects[v]) )
-			mySpells.insert(SpellID(v));
+		if(!spell->isCreatureAbility() && myHero->canCastThisSpell(spell))
+			mySpells.push_back(spell);
 	}
+	std::sort(mySpells.begin(), mySpells.end(), spellsorter);
 
 	//initializing sizes of spellbook's parts
 	for(auto & elem : sitesPerTabAdv)
@@ -104,14 +120,13 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 	for(auto & elem : sitesPerTabBattle)
 		elem = 0;
 
-	for(auto g : mySpells)
+	for(const auto spell : mySpells)
 	{
-		const CSpell &s = *CGI->spellh->objects[g];
-		Uint8 *sitesPerOurTab = s.combatSpell ? sitesPerTabBattle : sitesPerTabAdv;
+		int * sitesPerOurTab = spell->isCombatSpell() ? sitesPerTabBattle : sitesPerTabAdv;
 
 		++sitesPerOurTab[4];
 
-		s.forEachSchool([&sitesPerOurTab](const SpellSchoolInfo & school, bool & stop)
+		spell->forEachSchool([&sitesPerOurTab](const SpellSchoolInfo & school, bool & stop)
 		{
 			++sitesPerOurTab[(ui8)school.id];
 		});
@@ -155,45 +170,48 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 
 	//numbers of spell pages computed
 
-	leftCorner = BitmapHandler::loadBitmap("SpelTrnL.bmp", true);
-	rightCorner = BitmapHandler::loadBitmap("SpelTrnR.bmp", true);
+	leftCorner = new CPicture("SpelTrnL.bmp", 97, 77);
+	rightCorner = new CPicture("SpelTrnR.bmp", 487, 72);
 
-	spells = new CAnimation("Spells.def");
+	spells = std::make_shared<CAnimation>("Spells");
 
-	spellTab = CDefHandler::giveDef("SpelTab.def");
-	schools = CDefHandler::giveDef("Schools.def");
-	schoolBorders[0] = CDefHandler::giveDef("SplevA.def");
-	schoolBorders[1] = CDefHandler::giveDef("SplevF.def");
-	schoolBorders[2] = CDefHandler::giveDef("SplevW.def");
-	schoolBorders[3] = CDefHandler::giveDef("SplevE.def");
+	spellTab = new CAnimImage("SpelTab", selectedTab, 0, 524, 88);
+	schools = new CAnimImage("Schools",0,0,117,74);
 
+	schoolBorders[0] = std::make_shared<CAnimation>("SplevA.def");
+	schoolBorders[1] = std::make_shared<CAnimation>("SplevF.def");
+	schoolBorders[2] = std::make_shared<CAnimation>("SplevW.def");
+	schoolBorders[3] = std::make_shared<CAnimation>("SplevE.def");
 
+	for(auto item : schoolBorders)
+		item->load();
+	mana = new CLabel(435, 426, FONT_SMALL, CENTER, Colors::YELLOW, boost::lexical_cast<std::string>(myHero->mana));
+	statusBar = new CGStatusBar(7, 569, "Spelroll.bmp");
 
-	statusBar = new CGStatusBar(7 + pos.x, 569 + pos.y, "Spelroll.bmp");
 	SDL_Rect temp_rect = genRect(45, 35, 479 + pos.x, 405 + pos.y);
-	exitBtn = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::fexitb, this), CGI->generaltexth->zelp[460].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[460].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::fexitb, this), 460, this);
 	temp_rect = genRect(45, 35, 221 + pos.x, 405 + pos.y);
-	battleSpells = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::fbattleSpellsb, this), CGI->generaltexth->zelp[453].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[453].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::fbattleSpellsb, this), 453, this);
 	temp_rect = genRect(45, 35, 355 + pos.x, 405 + pos.y);
-	adventureSpells = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::fadvSpellsb, this), CGI->generaltexth->zelp[452].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[452].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::fadvSpellsb, this), 452, this);
 	temp_rect = genRect(45, 35, 418 + pos.x, 405 + pos.y);
-	manaPoints = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::fmanaPtsb, this), CGI->generaltexth->zelp[459].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[459].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::fmanaPtsb, this), 459, this);
 
 	temp_rect = genRect(36, 56, 549 + pos.x, 94 + pos.y);
-	selectSpellsA = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 0), CGI->generaltexth->zelp[454].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[454].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 0), 454, this);
 	temp_rect = genRect(36, 56, 549 + pos.x, 151 + pos.y);
-	selectSpellsE = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 3), CGI->generaltexth->zelp[457].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[457].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 3), 457, this);
 	temp_rect = genRect(36, 56, 549 + pos.x, 210 + pos.y);
-	selectSpellsF = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 1), CGI->generaltexth->zelp[455].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[455].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 1), 455, this);
 	temp_rect = genRect(36, 56, 549 + pos.x, 270 + pos.y);
-	selectSpellsW = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 2), CGI->generaltexth->zelp[456].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[456].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 2), 456, this);
 	temp_rect = genRect(36, 56, 549 + pos.x, 330 + pos.y);
-	selectSpellsAll = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 4), CGI->generaltexth->zelp[458].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[458].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::selectSchool, this, 4), 458, this);
 
-	temp_rect = genRect(leftCorner->h, leftCorner->w, 97 + pos.x, 77 + pos.y);
-	lCorner = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::fLcornerb, this), CGI->generaltexth->zelp[450].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[450].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
-	temp_rect = genRect(rightCorner->h, rightCorner->w, 487 + pos.x, 72 + pos.y);
-	rCorner = new SpellbookInteractiveArea(temp_rect, std::bind(&CSpellWindow::fRcornerb, this), CGI->generaltexth->zelp[451].second, std::bind(&CGStatusBar::setText, statusBar, (CGI->generaltexth->zelp[451].first)), std::bind(&CGStatusBar::clear, statusBar), myInt);
+	temp_rect = genRect(leftCorner->bg->h, leftCorner->bg->w, 97 + pos.x, 77 + pos.y);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::fLcornerb, this), 450, this);
+	temp_rect = genRect(rightCorner->bg->h, rightCorner->bg->w, 487 + pos.x, 72 + pos.y);
+	new InteractiveArea(temp_rect, std::bind(&CSpellWindow::fRcornerb, this), 451, this);
 
 	//areas for spells
 	int xpos = 117 + pos.x, ypos = 90 + pos.y;
@@ -221,51 +239,26 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
 	}
 
 	selectedTab = battleSpellsOnly ? myInt->spellbookSettings.spellbookLastTabBattle : myInt->spellbookSettings.spellbookLastTabAdvmap;
-	currentPage = battleSpellsOnly ? myInt->spellbookSettings.spellbookLastPageBattle : myInt->spellbookSettings.spellbokLastPageAdvmap;
-
+	spellTab->setFrame(selectedTab, 0);
+	int cp = battleSpellsOnly ? myInt->spellbookSettings.spellbookLastPageBattle : myInt->spellbookSettings.spellbokLastPageAdvmap;
 	// spellbook last page battle index is not reset after battle, so this needs to stay here
-	vstd::abetween(currentPage, 0, std::max(0, pagesWithinCurrentTab() - 1));
-
+	vstd::abetween(cp, 0, std::max(0, pagesWithinCurrentTab() - 1));
+	setCurrentPage(cp);
 	computeSpellsPerArea();
 	addUsedEvents(KEYBOARD);
 }
 
 CSpellWindow::~CSpellWindow()
 {
-	SDL_FreeSurface(leftCorner);
-	SDL_FreeSurface(rightCorner);
+	for(auto item : schoolBorders)
+		item->unload();
 	spells->unload();
-	delete spells;
-	delete spellTab;
-	delete schools;
-	for(auto & elem : schoolBorders)
-		delete elem;
-
-	delete exitBtn;
-	delete battleSpells;
-	delete adventureSpells;
-	delete manaPoints;
-	delete statusBar;
-
-	delete selectSpellsA;
-	delete selectSpellsE;
-	delete selectSpellsF;
-	delete selectSpellsW;
-	delete selectSpellsAll;
-
-	delete lCorner;
-	delete rCorner;
-
-	for(auto & elem : spellAreas)
-	{
-		delete elem;
-	}
 }
 
 void CSpellWindow::fexitb()
 {
-	(LOCPLINT->battleInt ? myInt->spellbookSettings.spellbookLastTabBattle : myInt->spellbookSettings.spellbookLastTabAdvmap) = selectedTab;
-	(LOCPLINT->battleInt ? myInt->spellbookSettings.spellbookLastPageBattle : myInt->spellbookSettings.spellbokLastPageAdvmap) = currentPage;
+	(myInt->battleInt ? myInt->spellbookSettings.spellbookLastTabBattle : myInt->spellbookSettings.spellbookLastTabAdvmap) = selectedTab;
+	(myInt->battleInt ? myInt->spellbookSettings.spellbookLastPageBattle : myInt->spellbookSettings.spellbokLastPageAdvmap) = currentPage;
 
 	GH.popIntTotally(this);
 }
@@ -276,7 +269,7 @@ void CSpellWindow::fadvSpellsb()
 	{
 		turnPageRight();
 		battleSpellsOnly = false;
-		currentPage = 0;
+		setCurrentPage(0);
 	}
 	computeSpellsPerArea();
 }
@@ -287,7 +280,7 @@ void CSpellWindow::fbattleSpellsb()
 	{
 		turnPageLeft();
 		battleSpellsOnly = true;
-		currentPage = 0;
+		setCurrentPage(0);
 	}
 	computeSpellsPerArea();
 }
@@ -305,7 +298,8 @@ void CSpellWindow::selectSchool(int school)
 		else
 			turnPageRight();
 		selectedTab = school;
-		currentPage = 0;
+		spellTab->setFrame(selectedTab, 0);
+		setCurrentPage(0);
 	}
 	computeSpellsPerArea();
 }
@@ -315,7 +309,7 @@ void CSpellWindow::fLcornerb()
 	if(currentPage>0)
 	{
 		turnPageLeft();
-		--currentPage;
+		setCurrentPage(currentPage - 1);
 	}
 	computeSpellsPerArea();
 	GH.breakEventHandling();
@@ -326,96 +320,36 @@ void CSpellWindow::fRcornerb()
 	if((currentPage + 1) < (pagesWithinCurrentTab()))
 	{
 		turnPageRight();
-		++currentPage;
+		setCurrentPage(currentPage + 1);
 	}
 	computeSpellsPerArea();
 	GH.breakEventHandling();
 }
 
-void CSpellWindow::showAll(SDL_Surface * to)
-{
-	CWindowObject::showAll(to);
-	blitAt(spellTab->ourImages[selectedTab].bitmap, 524 + pos.x, 88 + pos.y, to);
-
-	std::ostringstream mana;
-	mana<<myHero->mana;
-	printAtMiddleLoc(mana.str(), 435, 426, FONT_SMALL, Colors::YELLOW, to);
-
-	statusBar->showAll(to);
-
-	//printing school images
-	if(selectedTab!=4 && currentPage == 0)
-	{
-		blitAt(schools->ourImages[selectedTab].bitmap, 117 + pos.x, 74 + pos.y, to);
-	}
-
-	//printing corners
-	if(currentPage!=0)
-	{
-		blitAt(leftCorner, lCorner->pos.x, lCorner->pos.y, to);
-	}
-	if((currentPage+1) < (pagesWithinCurrentTab()) )
-	{
-		blitAt(rightCorner, rCorner->pos.x, rCorner->pos.y, to);
-	}
-
-	//printing spell info
-	for(auto & elem : spellAreas)
-	{
-		elem->showAll(to);
-	}
-}
-
 void CSpellWindow::show(SDL_Surface * to)
 {
 	statusBar->show(to);
 }
 
-class SpellbookSpellSorter
-{
-public:
-	bool operator()(const int & a, const int & b)
-	{
-		const CSpell & A = *CGI->spellh->objects[a];
-		const CSpell & B = *CGI->spellh->objects[b];
-		if(A.level<B.level)
-			return true;
-		if(A.level>B.level)
-			return false;
-
-
-		for(ui8 schoolId = 0; schoolId < 4; schoolId++)
-		{
-			if(A.school.at((ESpellSchool)schoolId) && !B.school.at((ESpellSchool)schoolId))
-				return true;
-			if(!A.school.at((ESpellSchool)schoolId) && B.school.at((ESpellSchool)schoolId))
-				return false;
-		}
-
-		return A.name < B.name;
-	}
-} spellsorter;
-
 void CSpellWindow::computeSpellsPerArea()
 {
-	std::vector<SpellID> spellsCurSite;
-	for(const SpellID & spellID : mySpells)
+	std::vector<const CSpell *> spellsCurSite;
+	spellsCurSite.reserve(mySpells.size());
+	for(const CSpell * spell : mySpells)
 	{
-		const CSpell * s = spellID.toSpell();
-
-		if(s->combatSpell ^ !battleSpellsOnly
-			&& ((selectedTab == 4) || s->school.at((ESpellSchool)selectedTab))
+		if(spell->combatSpell ^ !battleSpellsOnly
+			&& ((selectedTab == 4) || spell->school.at((ESpellSchool)selectedTab))
 			)
 		{
-			spellsCurSite.push_back(spellID);
+			spellsCurSite.push_back(spell);
 		}
 	}
-	std::sort(spellsCurSite.begin(), spellsCurSite.end(), spellsorter);
+
 	if(selectedTab == 4)
 	{
 		if(spellsCurSite.size() > 12)
 		{
-			spellsCurSite = std::vector<SpellID>(spellsCurSite.begin() + currentPage*12, spellsCurSite.end());
+			spellsCurSite = std::vector<const CSpell *>(spellsCurSite.begin() + currentPage*12, spellsCurSite.end());
 			if(spellsCurSite.size() > 12)
 			{
 				spellsCurSite.erase(spellsCurSite.begin()+12, spellsCurSite.end());
@@ -432,7 +366,7 @@ void CSpellWindow::computeSpellsPerArea()
 			}
 			else
 			{
-				spellsCurSite = std::vector<SpellID>(spellsCurSite.begin() + (currentPage-1)*12 + 10, spellsCurSite.end());
+				spellsCurSite = std::vector<const CSpell *>(spellsCurSite.begin() + (currentPage-1)*12 + 10, spellsCurSite.end());
 				if(spellsCurSite.size() > 12)
 				{
 					spellsCurSite.erase(spellsCurSite.begin()+12, spellsCurSite.end());
@@ -445,75 +379,41 @@ void CSpellWindow::computeSpellsPerArea()
 	{
 		for(size_t c=0; c<12; ++c)
 		{
-			if(c<spellsCurSite.size())
+			if(c < spellsCurSite.size())
 			{
 				spellAreas[c]->setSpell(spellsCurSite[c]);
 			}
 			else
 			{
-				spellAreas[c]->setSpell(SpellID::NONE);
+				spellAreas[c]->setSpell(nullptr);
 			}
 		}
 	}
 	else
 	{
-		spellAreas[0]->setSpell(SpellID::NONE);
-		spellAreas[1]->setSpell(SpellID::NONE);
+		spellAreas[0]->setSpell(nullptr);
+		spellAreas[1]->setSpell(nullptr);
 		for(size_t c=0; c<10; ++c)
 		{
-			if(c<spellsCurSite.size())
+			if(c < spellsCurSite.size())
 				spellAreas[c+2]->setSpell(spellsCurSite[c]);
 			else
-				spellAreas[c+2]->setSpell(SpellID::NONE);
+				spellAreas[c+2]->setSpell(nullptr);
 		}
 	}
 	redraw();
 }
 
-void CSpellWindow::activate()
-{
-	CIntObject::activate();
-	exitBtn->activate();
-	battleSpells->activate();
-	adventureSpells->activate();
-	manaPoints->activate();
-
-	selectSpellsA->activate();
-	selectSpellsE->activate();
-	selectSpellsF->activate();
-	selectSpellsW->activate();
-	selectSpellsAll->activate();
-
-	for(auto & elem : spellAreas)
-	{
-		elem->activate();
-	}
-
-	lCorner->activate();
-	rCorner->activate();
-}
-
-void CSpellWindow::deactivate()
+void CSpellWindow::setCurrentPage(int value)
 {
-	CIntObject::deactivate();
-	exitBtn->deactivate();
-	battleSpells->deactivate();
-	adventureSpells->deactivate();
-	manaPoints->deactivate();
-
-	selectSpellsA->deactivate();
-	selectSpellsE->deactivate();
-	selectSpellsF->deactivate();
-	selectSpellsW->deactivate();
-	selectSpellsAll->deactivate();
-
-	for(auto & elem : spellAreas)
-	{
-		elem->deactivate();
-	}
-
-	lCorner->deactivate();
-	rCorner->deactivate();
+	currentPage = value;
+	schools->visible = selectedTab!=4 && currentPage == 0;
+	if(selectedTab != 4)
+		schools->setFrame(selectedTab, 0);
+	leftCorner->visible = currentPage != 0;
+	rightCorner->visible = (currentPage+1) < pagesWithinCurrentTab();
+
+	mana->setText(boost::lexical_cast<std::string>(myHero->mana));//just in case, it will be possible to cast spell without closing book
 }
 
 void CSpellWindow::turnPageLeft()
@@ -570,7 +470,7 @@ void CSpellWindow::keyPressed(const SDL_KeyboardEvent & key)
 		}
 
 		//alt + 1234567890-= casts spell from 1 - 12 slot
-		if(LOCPLINT->altPressed())
+		if(myInt->altPressed())
 		{
 			SDL_Keycode hlpKey = key.keysym.sym;
 			if(CGuiHandler::isNumKey(hlpKey, false))
@@ -594,7 +494,7 @@ void CSpellWindow::keyPressed(const SDL_KeyboardEvent & key)
 	}
 }
 
-Uint8 CSpellWindow::pagesWithinCurrentTab()
+int CSpellWindow::pagesWithinCurrentTab()
 {
 	return battleSpellsOnly ? sitesPerTabBattle[selectedTab] : sitesPerTabAdv[selectedTab];
 }
@@ -606,39 +506,53 @@ CSpellWindow::SpellArea::SpellArea(SDL_Rect pos, CSpellWindow * owner)
 	addUsedEvents(LCLICK | RCLICK | HOVER);
 
 	spellCost = whichSchool = schoolLevel = -1;
-	mySpell = SpellID::NONE;
+	mySpell = nullptr;
+	schoolBorder = nullptr;
+
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	image = new CAnimImage(owner->spells, 0, 0);
+	image->visible = false;
+
+	name  = new CLabel(39, 70, FONT_TINY, CENTER);
+	level = new CLabel(39, 82, FONT_TINY, CENTER);
+	cost  = new CLabel(39, 94, FONT_TINY, CENTER);
+}
+
+CSpellWindow::SpellArea::~SpellArea()
+{
+	if(schoolBorder)
+		schoolBorder->decreaseRef();
 }
 
 void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 {
-	if(!down && mySpell != SpellID::NONE)
+	if(!down && mySpell)
 	{
-		const CSpell * sp = mySpell.toSpell();
-
-		int spellCost = owner->myInt->cb->getSpellCost(sp, owner->myHero);
+		int spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero);
 		if(spellCost > owner->myHero->mana) //insufficient mana
 		{
 			owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[206]) % spellCost % owner->myHero->mana));
 			return;
 		}
 		//battle spell on adv map or adventure map spell during combat => display infowindow, not cast
-		if((sp->isCombatSpell() && !owner->myInt->battleInt)
-		   || (sp->isAdventureSpell() && (owner->myInt->battleInt || owner->myInt->castleInt)))
+		if((mySpell->isCombatSpell() && !owner->myInt->battleInt)
+		   || (mySpell->isAdventureSpell() && (owner->myInt->battleInt || owner->myInt->castleInt)))
 		{
-			std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell, 0));
-			LOCPLINT->showInfoDialog(sp->getLevelInfo(schoolLevel).description, hlp);
+			std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell->id, 0));
+			owner->myInt->showInfoDialog(mySpell->getLevelInfo(schoolLevel).description, hlp);
 			return;
 		}
 
 		//we will cast a spell
-		if(sp->combatSpell && owner->myInt->battleInt && owner->myInt->cb->battleCanCastSpell()) //if battle window is open
+		if(mySpell->combatSpell && owner->myInt->battleInt && owner->myInt->cb->battleCanCastSpell()) //if battle window is open
 		{
-			ESpellCastProblem::ESpellCastProblem problem = owner->myInt->cb->battleCanCastThisSpell(sp);
+			ESpellCastProblem::ESpellCastProblem problem = owner->myInt->cb->battleCanCastThisSpell(mySpell);
 			switch (problem)
 			{
 			case ESpellCastProblem::OK:
 				{
-					owner->myInt->battleInt->castThisSpell(mySpell);
+					owner->myInt->battleInt->castThisSpell(mySpell->id);
 					owner->fexitb();
 					return;
 				}
@@ -713,25 +627,25 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 				}
 			}
 		}
-		else if(sp->isAdventureSpell() && !owner->myInt->battleInt) //adventure spell and not in battle
+		else if(mySpell->isAdventureSpell() && !owner->myInt->battleInt) //adventure spell and not in battle
 		{
 			const CGHeroInstance *h = owner->myHero;
 			GH.popInt(owner);
 
 			auto guard = vstd::makeScopeGuard([this]
 			{
-				(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastTabBattle : owner->myInt->spellbookSettings.spellbookLastTabAdvmap) = owner->selectedTab;
-				(LOCPLINT->battleInt ? owner->myInt->spellbookSettings.spellbookLastPageBattle : owner->myInt->spellbookSettings.spellbokLastPageAdvmap) = owner->currentPage;
+				(owner->myInt->battleInt ? owner->myInt->spellbookSettings.spellbookLastTabBattle : owner->myInt->spellbookSettings.spellbookLastTabAdvmap) = owner->selectedTab;
+				(owner->myInt->battleInt ? owner->myInt->spellbookSettings.spellbookLastPageBattle : owner->myInt->spellbookSettings.spellbokLastPageAdvmap) = owner->currentPage;
 				delete owner;
 			});
 
-			if(mySpell == SpellID::TOWN_PORTAL)
+			if(mySpell->id == SpellID::TOWN_PORTAL)
 			{
 				//special case
 				//todo: move to mechanics
 
 				std::vector <int> availableTowns;
-				std::vector <const CGTownInstance*> Towns = LOCPLINT->cb->getTownsInfo(false);
+				std::vector <const CGTownInstance*> Towns = owner->myInt->cb->getTownsInfo(false);
 
 				vstd::erase_if(Towns, [this](const CGTownInstance * t)
 				{
@@ -745,7 +659,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 					return;
 				}
 
-				const int movementCost = (h->getSpellSchoolLevel(sp) >= 3) ? 200 : 300;
+				const int movementCost = (h->getSpellSchoolLevel(mySpell) >= 3) ? 200 : 300;
 
 				if(h->movement < movementCost)
 				{
@@ -753,14 +667,14 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 					return;
 				}
 
-				if (h->getSpellSchoolLevel(sp) < 2) //not advanced or expert - teleport to nearest available city
+				if (h->getSpellSchoolLevel(mySpell) < 2) //not advanced or expert - teleport to nearest available city
 				{
 					auto nearest = Towns.cbegin(); //nearest town's iterator
-					si32 dist = LOCPLINT->cb->getTown((*nearest)->id)->pos.dist2dSQ(h->pos);
+					si32 dist = owner->myInt->cb->getTown((*nearest)->id)->pos.dist2dSQ(h->pos);
 
 					for (auto i = nearest + 1; i != Towns.cend(); ++i)
 					{
-						const CGTownInstance * dest = LOCPLINT->cb->getTown((*i)->id);
+						const CGTownInstance * dest = owner->myInt->cb->getTown((*i)->id);
 						si32 curDist = dest->pos.dist2dSQ(h->pos);
 
 						if (curDist < dist)
@@ -771,11 +685,11 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 					}
 
 					if ((*nearest)->visitingHero)
-						LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[123]);
+						owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[123]);
 					else
 					{
-						const CGTownInstance * town = LOCPLINT->cb->getTown((*nearest)->id);
-						LOCPLINT->cb->castSpell(h, mySpell, town->visitablePos());// - town->getVisitableOffset());
+						const CGTownInstance * town = owner->myInt->cb->getTown((*nearest)->id);
+						owner->myInt->cb->castSpell(h, mySpell->id, town->visitablePos());// - town->getVisitableOffset());
 					}
 				}
 				else
@@ -789,42 +703,42 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 						}
 					}
 
-					auto castTownPortal = [h](int townId)
+					auto castTownPortal = [this, h](int townId)
 					{
-						const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(townId));
-						LOCPLINT->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
+						const CGTownInstance * dest = owner->myInt->cb->getTown(ObjectInstanceID(townId));
+						owner->myInt->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
 					};
 
 					if (availableTowns.empty())
-						LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
+						owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[124]);
 					else
 						GH.pushInt (new CObjectListWindow(availableTowns,
-							new CAnimImage("SPELLSCR",mySpell),
+							new CAnimImage("SPELLSCR",mySpell->id),
 							CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41],
 							castTownPortal));
 				}
 				return;
 			}
 
-			if(mySpell == SpellID::SUMMON_BOAT)
+			if(mySpell->id == SpellID::SUMMON_BOAT)
 			{
 				//special case
 				//todo: move to mechanics
 				int3 pos = h->bestLocation();
 				if(pos.x < 0)
 				{
-					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[334]); //There is no place to put the boat.
+					owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[334]); //There is no place to put the boat.
 					return;
 				}
 			}
 
-			if(sp->getTargetType() == CSpell::LOCATION)
+			if(mySpell->getTargetType() == CSpell::LOCATION)
 			{
-				adventureInt->enterCastingMode(sp);
+				adventureInt->enterCastingMode(mySpell);
 			}
-			else if(sp->getTargetType() == CSpell::NO_TARGET)
+			else if(mySpell->getTargetType() == CSpell::NO_TARGET)
 			{
-				LOCPLINT->cb->castSpell(h, mySpell);
+				owner->myInt->cb->castSpell(h, mySpell->id);
 			}
 			else
 			{
@@ -836,12 +750,11 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
 
 void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 {
-	if(down && mySpell != -1)
+	if(down && mySpell)
 	{
 		std::string dmgInfo;
-		const CGHeroInstance * hero = owner->myHero;
-		int causedDmg = owner->myInt->cb->estimateSpellDamage( CGI->spellh->objects[mySpell], (hero ? hero : nullptr));
-		if(causedDmg == 0 || mySpell == SpellID::TITANS_LIGHTNING_BOLT) //Titan's Lightning Bolt already has damage info included
+		int causedDmg = owner->myInt->cb->estimateSpellDamage(mySpell, owner->myHero);
+		if(causedDmg == 0 || mySpell->id == SpellID::TITANS_LIGHTNING_BOLT) //Titan's Lightning Bolt already has damage info included
 			dmgInfo = "";
 		else
 		{
@@ -849,83 +762,80 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
 			boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(causedDmg));
 		}
 
-		CRClickPopup::createAndPush(CGI->spellh->objects[mySpell]->getLevelInfo(schoolLevel).description + dmgInfo,
-		                            new CComponent(CComponent::spell, mySpell));
+		CRClickPopup::createAndPush(mySpell->getLevelInfo(schoolLevel).description + dmgInfo,
+		                            new CComponent(CComponent::spell, mySpell->id));
 	}
 }
 
 void CSpellWindow::SpellArea::hover(bool on)
 {
-	//Hoverable::hover(on);
-	if(mySpell != -1)
+	if(mySpell)
 	{
 		if(on)
-		{
-			std::ostringstream ss;
-			ss<<CGI->spellh->objects[mySpell]->name<<" ("<<CGI->generaltexth->allTexts[171+CGI->spellh->objects[mySpell]->level]<<")";
-			owner->statusBar->setText(ss.str());
-		}
+			owner->statusBar->setText(boost::to_string(boost::format("%s (%s)") % mySpell->name % CGI->generaltexth->allTexts[171+mySpell->level]));
 		else
-		{
 			owner->statusBar->clear();
-		}
 	}
 }
 
 void CSpellWindow::SpellArea::showAll(SDL_Surface * to)
 {
-	if(mySpell < 0)
+	if(!mySpell)
 		return;
 
-	const CSpell * spell = mySpell.toSpell();
-	owner->spells->load(mySpell);
+	//printing border (indicates level of magic school)
+	schoolBorder->draw(to, pos.x, pos.y);
+	CIntObject::showAll(to);
+}
 
-	IImage * icon = owner->spells->getImage(mySpell,0,false);
+void CSpellWindow::SpellArea::setSpell(const CSpell * spell)
+{
+	if(schoolBorder)
+		schoolBorder->decreaseRef();
+	image->visible = false;
+	name->setText("");
+	level->setText("");
+	cost->setText("");
+	mySpell = spell;
+	if(mySpell)
+	{
+		schoolLevel = owner->myHero->getSpellSchoolLevel(mySpell, &whichSchool);
+		spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero);
+
+		image->setFrame(mySpell->id);
+		image->visible = true;
+		schoolBorder = owner->schoolBorders[owner->selectedTab >= 4 ? whichSchool : owner->selectedTab]->getImage(schoolLevel,0);
+
+		SDL_Color firstLineColor, secondLineColor;
+		if(spellCost > owner->myHero->mana) //hero cannot cast this spell
+		{
+			static const SDL_Color unavailableSpell = {239, 189, 33, 0};
+			firstLineColor = Colors::WHITE;
+			secondLineColor = unavailableSpell;
+		}
+		else
+		{
+			firstLineColor = Colors::YELLOW;
+			secondLineColor = Colors::WHITE;
+		}
 
-	if(icon != nullptr)
-		icon->draw(to, pos.x, pos.y);
-	else
-		logGlobal->errorStream() << __FUNCTION__ << ": failed to load spell icon for spell with id " << mySpell;
+		name->color = firstLineColor;
+		name->setText(mySpell->name);
 
-	blitAt(owner->schoolBorders[owner->selectedTab >= 4 ? whichSchool : owner->selectedTab]->ourImages[schoolLevel].bitmap, pos.x, pos.y, to); //printing border (indicates level of magic school)
+		level->color = secondLineColor;
+		if(schoolLevel > 0)
+		{
+			boost::format fmt("%s/%s");
+			fmt % CGI->generaltexth->allTexts[171 + mySpell->level];
+			fmt % CGI->generaltexth->levels.at(3+(schoolLevel-1));//lines 4-6
+			level->setText(fmt.str());
+		}
+		else
+			level->setText(CGI->generaltexth->allTexts[171 + mySpell->level]);
 
-	SDL_Color firstLineColor, secondLineColor;
-	if(spellCost > owner->myHero->mana) //hero cannot cast this spell
-	{
-		static const SDL_Color unavailableSpell = {239, 189, 33, 0};
-		firstLineColor = Colors::WHITE;
-		secondLineColor = unavailableSpell;
-	}
-	else
-	{
-		firstLineColor = Colors::YELLOW;
-		secondLineColor = Colors::WHITE;
-	}
-	//printing spell's name
-	printAtMiddleLoc(spell->name, 39, 70, FONT_TINY, firstLineColor, to);
-	//printing lvl
-	if(schoolLevel > 0)
-	{
-		boost::format fmt("%s/%s");
-		fmt % CGI->generaltexth->allTexts[171 + spell->level];
-		fmt % CGI->generaltexth->levels.at(3+(schoolLevel-1));//lines 4-6
-		printAtMiddleLoc(fmt.str(), 39, 82, FONT_TINY, secondLineColor, to);
+		cost->color = secondLineColor;
+		boost::format costfmt("%s: %d");
+		costfmt % CGI->generaltexth->allTexts[387] % spellCost;
+		cost->setText(costfmt.str());
 	}
-	else
-		printAtMiddleLoc(CGI->generaltexth->allTexts[171 + spell->level], 39, 82, FONT_TINY, secondLineColor, to);
-	//printing  cost
-	std::ostringstream ss;
-	ss << CGI->generaltexth->allTexts[387] << ": " << spellCost;
-	printAtMiddleLoc(ss.str(), 39, 94, FONT_TINY, secondLineColor, to);
-}
-
-void CSpellWindow::SpellArea::setSpell(SpellID spellID)
-{
-	mySpell = spellID;
-	if(mySpell < 0)
-		return;
-
-	const CSpell * spell = CGI->spellh->objects[mySpell];
-	schoolLevel = owner->myHero->getSpellSchoolLevel(spell, &whichSchool);
-	spellCost = owner->myInt->cb->getSpellCost(spell, owner->myHero);
 }

+ 50 - 53
client/windows/CSpellWindow.h

@@ -13,88 +13,87 @@
  */
 
 struct SDL_Surface;
-class CDefHandler;
 struct SDL_Rect;
+class IImage;
+class CAnimImage;
+class CPicture;
+class CLabel;
 class CGHeroInstance;
 class CGStatusBar;
 class CPlayerInterface;
-
-/// Spellbook button is used by the spell window class
-class SpellbookInteractiveArea : public CIntObject
-{
-private:
-	std::function<void()> onLeft;
-	std::string textOnRclick;
-	std::function<void()> onHoverOn;
-	std::function<void()> onHoverOff;
-	CPlayerInterface * myInt;
-public:
-	void clickLeft(tribool down, bool previousState);
-	void clickRight(tribool down, bool previousState);
-	void hover(bool on);
-
-	SpellbookInteractiveArea(const SDL_Rect & myRect, std::function<void()> funcL, const std::string & textR,
-		std::function<void()> funcHon, std::function<void()> funcHoff, CPlayerInterface * _myInt);//c-tor
-};
+class CSpellWindow;
+class CSpell;
 
 /// The spell window
 class CSpellWindow : public CWindowObject
 {
-private:
 	class SpellArea : public CIntObject
 	{
-	public:
-		SpellID mySpell;
+		const CSpell * mySpell;
 		int schoolLevel; //range: 0 none, 3 - expert
 		int whichSchool; //0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
 		int spellCost;
 		CSpellWindow * owner;
+		CAnimImage * image;
+		IImage * schoolBorder;
+		CLabel * name, * level, * cost;
+	public:
+		SpellArea(SDL_Rect pos, CSpellWindow * owner);
+		~SpellArea();
+		void setSpell(const CSpell * spell);
 
+		void clickLeft(tribool down, bool previousState) override;
+		void clickRight(tribool down, bool previousState) override;
+		void hover(bool on) override;
+		void showAll(SDL_Surface * to) override;
+	};
 
-		SpellArea(SDL_Rect pos, CSpellWindow * owner);
+	class InteractiveArea : public CIntObject
+	{
+		std::function<void()> onLeft;
+		CSpellWindow * owner;
 
-		void setSpell(SpellID spellID);
+		std::string hoverText;
+		std::string helpText;
+	public:
+		void clickLeft(tribool down, bool previousState) override;
+		void clickRight(tribool down, bool previousState) override;
+		void hover(bool on) override;
 
-		void clickLeft(tribool down, bool previousState);
-		void clickRight(tribool down, bool previousState);
-		void hover(bool on);
-		void showAll(SDL_Surface * to);
+		InteractiveArea(const SDL_Rect & myRect, std::function<void()> funcL, int helpTextId, CSpellWindow * _owner);//c-tor
 	};
 
-	SDL_Surface * leftCorner, * rightCorner;
-	
-	CAnimation * spells; //pictures of spells
-	
-	CDefHandler	* spellTab, //school select
-		* schools, //schools' pictures
-		* schoolBorders [4]; //schools' 'borders': [0]: air, [1]: fire, [2]: water, [3]: earth
-
-	SpellbookInteractiveArea * exitBtn, * battleSpells, * adventureSpells, * manaPoints;
-	SpellbookInteractiveArea * selectSpellsA, * selectSpellsE, * selectSpellsF, * selectSpellsW, * selectSpellsAll;
-	SpellbookInteractiveArea * lCorner, * rCorner;
+	CPicture * leftCorner, * rightCorner;
+
+	std::shared_ptr<CAnimation> spells; //pictures of spells
+
+	CAnimImage * spellTab; //school select
+	CAnimImage * schools; //schools' pictures
+	std::array< std::shared_ptr<CAnimation>, 4> schoolBorders; //schools' 'borders': [0]: air, [1]: fire, [2]: water, [3]: earth
+
 	SpellArea * spellAreas[12];
+	CLabel * mana;
 	CGStatusBar * statusBar;
 
-	Uint8 sitesPerTabAdv[5];
-	Uint8 sitesPerTabBattle[5];
+	int sitesPerTabAdv[5];
+	int sitesPerTabBattle[5];
 
 	bool battleSpellsOnly; //if true, only battle spells are displayed; if false, only adventure map spells are displayed
 	Uint8 selectedTab; // 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic, 4 - all schools
-	Uint8 currentPage; //changes when corners are clicked
-	std::set<SpellID> mySpells; //all spels in this spellbook
+	int currentPage; //changes when corners are clicked
+	std::vector<const CSpell *> mySpells; //all spels in this spellbook
 
 	const CGHeroInstance * myHero; //hero whose spells are presented
+	CPlayerInterface * myInt;
 
 	void computeSpellsPerArea(); //recalculates spellAreas::mySpell
 
+	void setCurrentPage(int value);
 	void turnPageLeft();
 	void turnPageRight();
 
-	CPlayerInterface * myInt;
-
 public:
-
-	CSpellWindow(const SDL_Rect & myRect, const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells = true); //c-tor
+	CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _myInt, bool openOnBattleSpells = true); //c-tor
 	~CSpellWindow(); //d-tor
 
 	void fexitb();
@@ -106,10 +105,8 @@ public:
 	void fRcornerb();
 
 	void selectSchool(int school); //schools: 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic, 4 - all schools
-	Uint8 pagesWithinCurrentTab();
-	void keyPressed(const SDL_KeyboardEvent & key);
-	void activate();
-	void deactivate();
-	void showAll(SDL_Surface * to);
-	void show(SDL_Surface * to);
+	int pagesWithinCurrentTab();
+
+	void keyPressed(const SDL_KeyboardEvent & key) override;
+	void show(SDL_Surface * to) override;
 };

+ 0 - 1
client/windows/GUIClasses.cpp

@@ -5,7 +5,6 @@
 #include "CCastleInterface.h"
 #include "CCreatureWindow.h"
 #include "CHeroWindow.h"
-#include "CSpellWindow.h"
 
 #include "../CBitmapHandler.h"
 #include "../CGameInfo.h"

+ 11 - 1
lib/spells/CSpellHandler.cpp

@@ -69,6 +69,15 @@ namespace SpellConfig
 			Bonus::EARTH_SPELLS
 		}
 	};
+
+	//order as described in http://bugs.vcmi.eu/view.php?id=91
+	static const ESpellSchool SCHOOL_ORDER[4] =
+	{
+		ESpellSchool::AIR,  //=0
+		ESpellSchool::FIRE, //=1
+		ESpellSchool::EARTH,//=3(!)
+		ESpellSchool::WATER //=2(!)
+	};
 }
 
 ///CSpell::LevelInfo
@@ -226,8 +235,9 @@ CSpell::ETargetType CSpell::getTargetType() const
 void CSpell::forEachSchool(const std::function<void(const SpellSchoolInfo &, bool &)>& cb) const
 {
 	bool stop = false;
-	for(const SpellSchoolInfo & cnf : SpellConfig::SCHOOL)
+	for(ESpellSchool iter : SpellConfig::SCHOOL_ORDER)
 	{
+		const SpellSchoolInfo & cnf = SpellConfig::SCHOOL[(ui8)iter];
 		if(school.at(cnf.id))
 		{
 			cb(cnf, stop);