From ac997b8214b0bab0f926cc29f108f5f78b95b72f Mon Sep 17 00:00:00 2001
From: DjWarmonger <warmonger@vp.pl>
Date: Tue, 3 Jul 2012 08:07:34 +0000
Subject: [PATCH] Partial interactive mode for Commander level-up dialog. It is
 possible to select secondary skill.

---
 client/CCreatureWindow.cpp  | 149 ++++++++++++++++++++++++++++--------
 client/CCreatureWindow.h    |  21 +++++
 client/CPlayerInterface.cpp |  10 +--
 client/NetPacksClient.cpp   |   4 +-
 lib/NetPacks.h              |   2 +-
 5 files changed, 143 insertions(+), 43 deletions(-)

diff --git a/client/CCreatureWindow.cpp b/client/CCreatureWindow.cpp
index b4c842e3f..2cd6db8c4 100644
--- a/client/CCreatureWindow.cpp
+++ b/client/CCreatureWindow.cpp
@@ -29,6 +29,7 @@ using namespace CSDL_Ext;
 
 class CBonusItem;
 class CCreatureArtifactInstance;
+class CSelectableSkill;
 
 /*
  * CCreatureWindow.cpp, part of VCMI engine
@@ -146,6 +147,28 @@ CCreatureWindow::CCreatureWindow (const CCommanderInstance * Commander):
 	dismiss = new CAdventureMapButton("",CGI->generaltexth->zelp[445].second, cfl, 333, 148,"IVIEWCR2.DEF", SDLK_d);
 }
 
+CCreatureWindow::CCreatureWindow (std::vector<ui32> &skills, const CCommanderInstance * Commander, boost::function<void(ui32)> &callback):
+    CWindowObject(PLAYER_COLORED),
+    type(COMMANDER_LEVEL_UP),
+	upgradeOptions(skills), //copy skills to choose from
+	commander (Commander),
+	levelUp (callback),
+	selectedOption (0) //choose something before drawing
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	init(commander, commander, dynamic_cast<const CGHeroInstance*>(commander->armyObj));
+
+	boost::function<void()> Dsm;
+	CFunctionList<void()> fs[2];
+	//on dismiss confirmed
+	fs[0] += Dsm; //dismiss
+	fs[0] += boost::bind(&CCreatureWindow::close,this);//close this window
+	CFunctionList<void()> cfl;
+	cfl = boost::bind(&CPlayerInterface::showYesNoDialog,LOCPLINT,CGI->generaltexth->allTexts[12],fs[0],fs[1],false,std::vector<CComponent*>());
+	dismiss = new CAdventureMapButton("",CGI->generaltexth->zelp[445].second, cfl, 333, 148,"IVIEWCR2.DEF", SDLK_d);
+}
+
 void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *StackNode, const CGHeroInstance *HeroOwner)
 {
 	creatureArtifact = NULL; //may be set later
@@ -311,41 +334,11 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 		creArt = true;
 		for (int i = ECommander::ATTACK; i <= ECommander::SPELL_POWER; ++i)
 		{
-			if (commander->secondarySkills[i])
+			if (commander->secondarySkills[i] || vstd::contains(upgradeOptions, i))
 			{
-				std::string file = "zvs/Lib1.res/_";
-				switch (i)
-				{
-					case ECommander::ATTACK:
-						file += "AT";
-						break;
-					case ECommander::DEFENSE:
-						file += "DF";
-						break;
-					case ECommander::HEALTH:
-						file += "HP";
-						break;
-					case ECommander::DAMAGE:
-						file += "DM";
-						break;
-					case ECommander::SPEED:
-						file += "SP";
-						break;
-					case ECommander::SPELL_POWER:
-						file += "MP";
-						break;
-				}
-				std::string sufix = boost::lexical_cast<std::string>((int)(commander->secondarySkills[i] - 1)); //casting ui8 causes ascii char conversion
-				if (type == COMMANDER_LEVEL_UP)
-				{
-					if (commander->secondarySkills[i] < ECommander::MAX_SKILL_LEVEL)
-						sufix += "="; //level-up highlight
-					else
-						sufix = "no"; //not avaliable - no number
-				}
-				file += sufix += ".bmp";
+				std::string file = skillToFile(i);
 
-				new CPicture(file, 37 + i * 84, 224);
+				skillPictures.push_back(new CPicture(file, 37 + i * 84, 224));
 			}
 		}
 		//print commander level
@@ -354,6 +347,30 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
 
 		new CLabel(488, 82, FONT_SMALL, CENTER, Colors::Cornsilk,
 		           boost::lexical_cast<std::string>(stack->experience));
+
+		if (type == COMMANDER_LEVEL_UP)
+		{
+			BOOST_FOREACH (auto option, upgradeOptions)
+			{
+				ui32 index = selectableSkills.size();
+
+				CSelectableSkill * selectableSkill = new CSelectableSkill();
+				if (option < 100)
+				{
+					selectableSkill->pos = skillPictures[option]->pos; //should match picture
+				}
+				else
+				{
+					selectableSkill->pos = Rect (95, 256, 55, 55); //TODO: scroll
+					Bonus b = CGI->creh->skillRequirements[option-100].first; 
+					bonusItems.push_back (new CBonusItem (genRect(0, 0, 251, 57), stack->bonusToString(&b, false), stack->bonusToString(&b, true), stack->bonusToGraphics(&b)));
+				}
+
+				selectableSkill->callback = boost::bind(&CCreatureWindow::selectSkill, this, index);
+				selectableSkills.push_back (selectableSkill);
+				//TODO: add clickable abilities to bonusItems
+			}
+		}
 	}
 	if (creArt) //stack or commander artifacts
 	{
@@ -492,6 +509,15 @@ void CCreatureWindow::showAll(SDL_Surface * to)
 
 	BOOST_FOREACH(CBonusItem* b, bonusItems)
 		b->showAll (to);
+
+	BOOST_FOREACH(auto s, selectableSkills)
+		s->showAll (to);
+
+	for (int i = 0; i < skillPictures.size(); i++)
+	{
+		skillPictures[i]->bg = BitmapHandler::loadBitmap (skillToFile(i));
+		skillPictures[i]->showAll (to);
+	}
 }
 
 void CCreatureWindow::show(SDL_Surface * to)
@@ -501,12 +527,57 @@ void CCreatureWindow::show(SDL_Surface * to)
 }
 
 
+void CCreatureWindow::close()
+{
+	if (upgradeOptions.size()) //a skill for commander was chosen
+		levelUp (upgradeOptions[selectedOption]); //callback
+
+	GH.popIntTotally(this);
+}
+
 void CCreatureWindow::sliderMoved(int newpos)
 {
 	recreateSkillList(newpos); //move components
 	redraw();
 }
 
+std::string CCreatureWindow::skillToFile (int skill)
+{
+		std::string file = "zvs/Lib1.res/_";
+		switch (skill)
+		{
+			case ECommander::ATTACK:
+				file += "AT";
+				break;
+			case ECommander::DEFENSE:
+				file += "DF";
+				break;
+			case ECommander::HEALTH:
+				file += "HP";
+				break;
+			case ECommander::DAMAGE:
+				file += "DM";
+				break;
+			case ECommander::SPEED:
+				file += "SP";
+				break;
+			case ECommander::SPELL_POWER:
+				file += "MP";
+				break;
+		}
+		std::string sufix = boost::lexical_cast<std::string>((int)(commander->secondarySkills[skill])); //casting ui8 causes ascii char conversion
+		if (type == COMMANDER_LEVEL_UP)
+		{
+			if (upgradeOptions.size() && upgradeOptions[selectedOption] == skill)//that one specific skill is selected
+				sufix += "="; //level-up highlight
+			else if (!vstd::contains(upgradeOptions, skill))
+				sufix = "no"; //not avaliable - no number
+		}
+		file += sufix += ".bmp";
+
+		return file;
+}
+
 void CCreatureWindow::setArt(const CArtifactInstance *art)
 {
 	creatureArtifact = art;
@@ -562,6 +633,12 @@ void CCreatureWindow::artifactMoved (const ArtifactLocation &artLoc, const Artif
 	artifactRemoved (artLoc); //same code
 }
 
+void CCreatureWindow::selectSkill (ui32 which)
+{
+	selectedOption = which;
+	redraw();
+}
+
 CCreatureWindow::~CCreatureWindow()
 {
  	for (int i=0; i<upgResCost.size(); ++i)
@@ -605,6 +682,12 @@ CBonusItem::~CBonusItem()
 	//delete bonusGraphics; //automatic destruction
 }
 
+void CSelectableSkill::clickLeft(tribool down, bool previousState)
+{
+	if (down)
+		callback();
+}
+
 void CCreInfoWindow::show(SDL_Surface * to)
 {
 	CIntObject::show(to);
diff --git a/client/CCreatureWindow.h b/client/CCreatureWindow.h
index ff3b68a41..d825f03f0 100644
--- a/client/CCreatureWindow.h
+++ b/client/CCreatureWindow.h
@@ -35,6 +35,7 @@ class LRClickableAreaWTextComp;
 class CSlider;
 class CLabel;
 class CAnimImage;
+class CSelectableSkill;
 
 // New creature window
 class CCreatureWindow : public CWindowObject, public CArtifactHolder
@@ -66,6 +67,14 @@ public:
 	CAdventureMapButton * passArtToHero;
 	CAnimImage *artifactImage;
 
+	//commander level-up
+	int selectedOption;
+	std::vector<ui32> upgradeOptions;
+	std::vector<CSelectableSkill *> selectableSkills;
+	std::vector<CPicture *> skillPictures; //secondary skills
+
+	std::string skillToFile(int skill); //return bitmap for secondary skill depending on selection / avaliability
+	void selectSkill (ui32 which);
 	void setArt(const CArtifactInstance *creatureArtifact);
 
 	void artifactRemoved (const ArtifactLocation &artLoc);
@@ -75,11 +84,13 @@ public:
 
 	boost::function<void()> dsm; //dismiss button callback
 	boost::function<void()> Upg; //upgrade button callback
+	boost::function<void(ui32)> levelUp; //choose commander skill to level up
 
 	CCreatureWindow(const CStack & stack, int type); //battle c-tor
 	CCreatureWindow (const CStackInstance &stack, int Type); //pop-up c-tor
 	CCreatureWindow(const CStackInstance &st, int Type, boost::function<void()> Upg, boost::function<void()> Dsm, UpgradeInfo *ui); //full garrison window
 	CCreatureWindow(const CCommanderInstance * commander); //commander window
+	CCreatureWindow(std::vector<ui32> &skills, const CCommanderInstance * commander, boost::function<void(ui32)> &callback); 
 	CCreatureWindow(int Cid, int Type, int creatureCount); //c-tor
 
 	void init(const CStackInstance *stack, const CBonusSystemNode *stackNode, const CGHeroInstance *heroOwner);
@@ -87,6 +98,7 @@ public:
 	void show(SDL_Surface * to);
 	void printLine(int nr, const std::string &text, int baseVal, int val=-1, bool range=false);
 	void sliderMoved(int newpos);
+	void close();
 	~CCreatureWindow(); //d-tor
 
 	void recreateSkillList(int pos);
@@ -109,6 +121,15 @@ public:
 	void showAll (SDL_Surface * to);
 };
 
+class CSelectableSkill : public LRClickableAreaWText
+{
+public:
+	boost::function<void()> callback; //TODO: create more generic clickable class than AdvMapButton?
+
+	virtual void clickLeft(tribool down, bool previousState);
+	virtual void clickRight(tribool down, bool previousState){};
+};
+
 /// original creature info window
 class CCreInfoWindow : public CWindowObject
 {
diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp
index 07f7e8663..95edfc485 100644
--- a/client/CPlayerInterface.cpp
+++ b/client/CPlayerInterface.cpp
@@ -482,14 +482,12 @@ void CPlayerInterface::commanderGotLevel (const CCommanderInstance * commander,
 	waitWhileDialog();
 	CCS->soundh->playSound(soundBase::heroNewLevel);
 
-	//boost::function<void(ui32)>(boost::bind(&CCallback::selectionMade,cl->callbacks[h->tempOwner].get(),_1,id))
-	auto callback2 = boost::bind (&CCallback::selectionMade, cb, 0, playerID);
-	showYesNoDialog ("Commander got level", callback2, callback2, true, std::vector<CComponent*>());
-	//showYesNoDialog ("Commander got level", callback, callback, true, std::vector<CComponent*>());
+	//auto callback2 = boost::bind (&CCallback::selectionMade, cb, 0, playerID);
+	//showYesNoDialog ("Commander got level", callback2, callback2, true, std::vector<CComponent*>());
 
 	//TODO: display full window
-	//CCreatureWindow * cw = new CCreatureWindow(commander);
-	//GH.pushInt(cw);
+	CCreatureWindow * cw = new CCreatureWindow(skills, commander, callback);
+	GH.pushInt(cw);
 }
 void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 {
diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp
index fa7095d59..f4d9cfef6 100644
--- a/client/NetPacksClient.cpp
+++ b/client/NetPacksClient.cpp
@@ -543,9 +543,7 @@ void CommanderLevelUp::applyCl( CClient *cl )
 	if (commander->armyObj && vstd::contains(cl->playerint, player)) //is it possible for Commander to exist beyond armed instance?
 	{
 		auto callback = boost::function<void(ui32)>(boost::bind(&CCallback::selectionMade,cl->callbacks[player].get(),_1,id));
-		
-		cl->playerint[player]->showBlockingDialog("Commander got level", std::vector<Component>(), id, -1, false, true);
-		//cl->playerint[player]->commanderGotLevel(commander, skills, callback);
+		cl->playerint[player]->commanderGotLevel(commander, skills, callback);
 	}
 }
 
diff --git a/lib/NetPacks.h b/lib/NetPacks.h
index eb59a3b26..7a02d2507 100644
--- a/lib/NetPacks.h
+++ b/lib/NetPacks.h
@@ -1167,7 +1167,7 @@ struct CommanderLevelUp : public Query
 	si32 heroid; //for commander attached to hero
 	StackLocation sl; //for commander not on the hero?
 
-	std::vector<ui32> skills; //1-6 - secondary skills, val - 100 - special skill
+	std::vector<ui32> skills; //0-5 - secondary skills, val-100 - special skill
 
 	CommanderLevelUp(){type = 2005;};