Explorar el Código

* partial support for Haste spell
* started making external settings file (will be used for support for non 800x600 screen resolutions)
* minor changes

Michał W. Urbańczyk hace 17 años
padre
commit
a1606b2fb3

+ 23 - 22
CAdvmapInterface.cpp

@@ -1,32 +1,33 @@
-#include "stdafx.h"
+#include "AdventureMapButton.h"
 #include "CAdvmapInterface.h"
-#include "client/CBitmapHandler.h"
-#include "CPlayerInterface.h"
+#include "CCallback.h"
 #include "CCastleInterface.h"
 #include "CCursorHandler.h"
-#include "hch/CPreGameTextHandler.h"
-#include "hch/CGeneralTextHandler.h"
-#include "hch/CDefHandler.h"
-#include "hch/CTownHandler.h"
-#include "CPathfinder.h"
 #include "CGameInfo.h"
+#include "CHeroWindow.h"
+#include "CMessage.h"
+#include "CPathfinder.h"
+#include "CPlayerInterface.h"
 #include "SDL_Extensions.h"
-#include "CCallback.h"
-#include <boost/assign/std/vector.hpp>
+#include "client/CBitmapHandler.h"
+#include "client/CConfigHandler.h"
+#include "client/CSpellWindow.h"
+#include "client/Graphics.h"
+#include "hch/CDefHandler.h"
+#include "hch/CGeneralTextHandler.h"
+#include "hch/CHeroHandler.h"
+#include "hch/CObjectHandler.h"
+#include "hch/CPreGameTextHandler.h"
+#include "hch/CTownHandler.h"
+#include "lib/CondSh.h"
+#include "map.h"
 #include "mapHandler.h"
-#include "CMessage.h"
+#include "stdafx.h"
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/replace.hpp>
-#include "hch/CHeroHandler.h"
-#include <sstream>
-#include "AdventureMapButton.h"
-#include "CHeroWindow.h"
-#include "client/Graphics.h"
-#include "hch/CObjectHandler.h"
+#include <boost/assign/std/vector.hpp>
 #include <boost/thread.hpp>
-#include "map.h"
-#include "client/CSpellWindow.h"
-#include "lib/CondSh.h"
+#include <sstream>
 #pragma warning (disable : 4355) 
 extern TTF_Font * TNRB16, *TNR, *GEOR13, *GEORXX; //fonts
 
@@ -1309,8 +1310,8 @@ void CAdvMapInt::handleRightClick(std::string text, tribool down, CIntObject * c
 	{
 		boost::algorithm::erase_all(text,"\"");
 		CSimpleWindow * temp = CMessage::genWindow(text,LOCPLINT->playerID);
-		temp->pos.x=300-(temp->pos.w/2);
-		temp->pos.y=300-(temp->pos.h/2);
+		temp->pos.x=screen->w/2-(temp->pos.w/2);
+		temp->pos.y=screen->h/2-(temp->pos.h/2);
 		temp->owner = client;
 		LOCPLINT->objsToBlit.push_back(temp);
 	}

+ 7 - 4
CBattleInterface.cpp

@@ -426,7 +426,7 @@ void CBattleInterface::show(SDL_Surface * to)
 	//showing queue of stacks
 	if(showStackQueue)
 	{
-		int xPos = 400 - ( stacks.size() * 37 )/2;
+		int xPos = screen->w/2 - ( stacks.size() * 37 )/2;
 		int yPos = 10;
 
 		std::vector<CStack> stacksSorted;
@@ -1238,7 +1238,7 @@ void CBattleInterface::spellCasted(SpellCasted * sc)
 			//animation angle
 			float angle = atan2(float(destcoord.first - srccoord.first), float(destcoord.second - srccoord.second));
 
-			//choosign animation by angle
+			//choosing animation by angle
 			if(angle > 1.50)
 				animToDisplay = anims[0];
 			else if(angle > 1.20)
@@ -1272,6 +1272,9 @@ void CBattleInterface::spellCasted(SpellCasted * sc)
 			int b=0;
 			break;
 		}
+	case 53://haste
+		displayEffect(31,sc->tile,LOCPLINT->cb->battleGetStackByPos(sc->tile)->owner);
+		break;
 	}
 }
 
@@ -1817,8 +1820,8 @@ void CBattleHex::clickRight(boost::logic::tribool down)
 			const CGHeroInstance *h = myst.owner == myInterface->attackingHeroInstance->tempOwner ? myInterface->attackingHeroInstance : myInterface->defendingHeroInstance;
 			if(h)
 			{
-				pom->attackBonus = h->primSkills[0];
-				pom->defenseBonus = h->primSkills[1];
+				pom->attackBonus = h->getPrimSkillLevel(0);
+				pom->defenseBonus = h->getPrimSkillLevel(1);
 				pom->luck = h->getCurrentLuck();
 				pom->morale = h->getCurrentMorale();
 				pom->shotsLeft = myst.shots;

+ 37 - 0
CGameState.cpp

@@ -248,6 +248,36 @@ CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S)
 	state.insert(ALIVE);
 }
 
+ui32 CStack::speed()
+{
+	int premy=0;
+	StackEffect *effect = 0;
+	//haste effect check
+	effect = getEffect(53);
+	if(effect)
+		premy += VLC->spellh->spells[effect->id].powers[effect->level];
+	//slow effect check
+	effect = getEffect(54);
+	if(effect)
+		premy -= VLC->spellh->spells[effect->id].powers[effect->level];
+	//prayer effect check
+	effect = getEffect(48);
+	if(effect)
+		premy -= VLC->spellh->spells[effect->id].powers[effect->level];
+	//bind effect check
+	effect = getEffect(72);
+	if(effect) 
+		premy = -creature->speed;
+	return creature->speed + premy;
+}
+
+CStack::StackEffect * CStack::getEffect(ui16 id)
+{
+	for (int i=0; i< effects.size(); i++)
+		if(effects[i].id == id)
+			return &effects[i];
+	return NULL;
+}
 CGHeroInstance* CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, int notThatOne)
 {
 	if(player<0 || player>=PLAYER_LIMIT)
@@ -705,6 +735,13 @@ void CGameState::applyNL(IPack * pack)
 			//TODO: counter
 			break;
 		}
+	case 3010:
+		{
+			SetStackEffect *sc = static_cast<SetStackEffect*>(pack);
+			CStack *stack = curB->getStack(sc->stack);
+			stack->effects.push_back(sc->effect);
+			break;
+		}
 	}
 }
 void CGameState::apply(IPack * pack)

+ 13 - 1
CGameState.h

@@ -96,10 +96,22 @@ public:
 
 	std::set<EAbilities> abilities;
 	std::set<ECombatInfo> state;
+	struct StackEffect
+	{
+		ui16 id; //spell id
+		ui8 level; //skill level
+		ui16 turnsRemain; 
+		template <typename Handler> void serialize(Handler &h, const int version)
+		{
+			h & id & level & turnsRemain;
+		}
+	};
+	std::vector<StackEffect> effects;
 
 	CStack(CCreature * C, int A, int O, int I, bool AO, int S);
 	CStack() : creature(NULL),amount(-1),owner(255), position(-1), ID(-1), attackerOwned(true), firstHPleft(-1), slot(255), baseAmount(-1), counterAttacks(1){};
-
+	StackEffect * getEffect(ui16 id); //effect id (SP)
+	ui32 speed();
 	template <typename Handler> void save(Handler &h, const int version)
 	{
 		h & creature->idNumber;

+ 20 - 38
CMT.cpp

@@ -41,6 +41,7 @@
 #include "hch/CGeneralTextHandler.h"
 #include "client/Graphics.h"
 #include "client/Client.h"
+#include "client/CConfigHandler.h"
 #include "lib/Connection.h"
 #include "lib/Interprocess.h"
 #include "lib/VCMI_Lib.h"
@@ -59,49 +60,30 @@ int _tmain(int argc, _TCHAR* argv[])
 int main(int argc, char** argv)
 #endif
 { 
+	tlog0 << "Starting... " << std::endl;
+	THC timeHandler tmh, total, pomtime;
 	CClient *client = NULL;
 	boost::thread *console = NULL;
-	if(argc>2)
-	{
-		std::cout << "Special mode without new support for console!" << std::endl;
-	}
-	else
-	{
-		logfile = new std::ofstream("VCMI_Client_log.txt");
-		::console = new CConsoleHandler;
-		*::console->cb = boost::bind(processCommand,_1,boost::ref(client));
-		console = new boost::thread(boost::bind(&CConsoleHandler::run,::console));
-	}
-	tlog0 << "\tConsole and logifle ready!" << std::endl;
-	int port;
-	if(argc > 1)
-	{
-#ifdef _MSC_VER
-		port = _tstoi(argv[1]);
-#else
-		port = _ttoi(argv[1]);
-#endif
-	}
-	else
-	{
-		port = 3030;
-		tlog0 << "Port " << port << " will be used." << std::endl;
-	}
+
 	std::cout.flags(ios::unitbuf);
+	logfile = new std::ofstream("VCMI_Client_log.txt");
+	::console = new CConsoleHandler;
+	*::console->cb = boost::bind(processCommand,_1,boost::ref(client));
+	console = new boost::thread(boost::bind(&CConsoleHandler::run,::console));
+	tlog0 <<"Creating console and logfile: "<<pomtime.getDif() << std::endl;
+
+	conf.init();
+	tlog0 <<"Loading settings: "<<pomtime.getDif() << std::endl;
 	tlog0 << NAME << std::endl;
+
 	srand ( time(NULL) );
 	CPG=NULL;
 	atexit(SDL_Quit);
 	CGameInfo * cgi = CGI = new CGameInfo; //contains all global informations about game (texts, lodHandlers, map handler itp.)
-	//CLuaHandler luatest;
-	//luatest.test(); 
-		//CBIKHandler cb;
-		//cb.open("CSECRET.BIK");
-	tlog0 << "Starting... " << std::endl;
-	THC timeHandler tmh, total, pomtime;
+
 	if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO)==0)
 	{
-		screen = SDL_SetVideoMode(800,600,24,SDL_SWSURFACE|SDL_DOUBLEBUF/*|SDL_FULLSCREEN*/);  //initializing important global surface
+		screen = SDL_SetVideoMode(conf.cc.resx,conf.cc.resy,conf.cc.bpp,SDL_SWSURFACE|SDL_DOUBLEBUF|(conf.cc.fullscreen?SDL_FULLSCREEN:0));  //initializing important global surface
 		tlog0 <<"\tInitializing screen: "<<pomtime.getDif();
 			tlog0 << std::endl;
 		SDL_WM_SetCaption(NAME.c_str(),""); //set window title
@@ -124,13 +106,13 @@ int main(int argc, char** argv)
 		mush->initMusics();
 		//audio initialized 
 		cgi->mush = mush;
-		THC tlog0<<"\tInitializing sound: "<<pomtime.getDif()<<std::endl;
-		THC tlog0<<"Initializing screen, fonts and sound handling: "<<tmh.getDif()<<std::endl;
+		tlog0<<"\tInitializing sound: "<<pomtime.getDif()<<std::endl;
+		tlog0<<"Initializing screen, fonts and sound handling: "<<tmh.getDif()<<std::endl;
 		CDefHandler::Spriteh = cgi->spriteh = new CLodHandler();
 		cgi->spriteh->init("Data" PATHSEPARATOR "H3sprite.lod","Sprites");
 		BitmapHandler::bitmaph = cgi->bitmaph = new CLodHandler;
 		cgi->bitmaph->init("Data" PATHSEPARATOR "H3bitmap.lod","Data");
-		THC tlog0<<"Loading .lod files: "<<tmh.getDif()<<std::endl;
+		tlog0<<"Loading .lod files: "<<tmh.getDif()<<std::endl;
 		initDLL(cgi->bitmaph,::console,logfile);
 		CGI->arth = VLC->arth;
 		CGI->creh = VLC->creh;
@@ -171,7 +153,7 @@ int main(int argc, char** argv)
 		StartInfo *options = new StartInfo(cpg->runLoop());
 		tmh.getDif();
 	////////////////////////SERVER STARTING/////////////////////////////////////////////////
-		char portc[10]; SDL_itoa(port,portc,10);
+		char portc[10]; SDL_itoa(conf.cc.port,portc,10);
 		intpr::shared_memory_object smo(intpr::open_or_create,"vcmi_memory",intpr::read_write);
 		smo.truncate(sizeof(ServerReady));
 		intpr::mapped_region mr(smo,intpr::read_write);
@@ -203,7 +185,7 @@ int main(int argc, char** argv)
 			try
 			{
 				tlog0 << "Establishing connection...\n";
-				c = new CConnection("127.0.0.1",portc,NAME,logs);
+				c = new CConnection(conf.cc.server,portc,NAME,logs);
 			}
 			catch(...)
 			{

+ 2 - 2
CMessage.cpp

@@ -426,8 +426,8 @@ void CMessage::drawIWindow(CInfoWindow * ret, std::string text, int player, int
 	ret->bitmap = drawBox1(txts.first+70,txts.second+70,0);
 	ret->pos.h=ret->bitmap->h;
 	ret->pos.w=ret->bitmap->w;
-	ret->pos.x=400-(ret->pos.w/2);
-	ret->pos.y=300-(ret->pos.h/2);
+	ret->pos.x=screen->w/2-(ret->pos.w/2);
+	ret->pos.y=screen->h/2-(ret->pos.h/2);
 	int curh = 30; //gorny margines
 	blitTextOnSur(txtg,curh,ret->bitmap);
 	if (ret->components.size())

+ 19 - 8
CPlayerInterface.cpp

@@ -287,7 +287,8 @@ void CGarrisonSlot::show()
 	}
 	else
 	{
-		SDL_Rect jakis1 = genRect(pos.h,pos.w,owner->offx+ID*(pos.w+owner->interx),owner->offy+upg*(pos.h+owner->intery)), jakis2 = pos;
+		SDL_Rect jakis1 = genRect(pos.h,pos.w,owner->offx+ID*(pos.w+owner->interx),owner->offy+upg*(pos.h+owner->intery)), 
+			jakis2 = pos;
 		SDL_BlitSurface(owner->sur,&jakis1,screen,&jakis2);
 		if(owner->splitting)
 			blitAt(graphics->bigImgs[-1],pos);
@@ -382,7 +383,8 @@ void CGarrisonInt::createSlots()
 			i!=set1->slots.end(); i++)
 		{
 			(*sup)[i->first] =
-				new CGarrisonSlot(this, pos.x + (i->first*(58+interx)), pos.y,i->first, 0, &CGI->creh->creatures[i->second.first],i->second.second);
+				new CGarrisonSlot(this, pos.x + (i->first*(58+interx)), pos.y,i->first, 0, 
+									&CGI->creh->creatures[i->second.first],i->second.second);
 		}
 		for(int i=0; i<sup->size(); i++)
 			if((*sup)[i] == NULL)
@@ -396,7 +398,8 @@ void CGarrisonInt::createSlots()
 			i!=set2->slots.end(); i++)
 		{
 			(*sdown)[i->first] =
-				new CGarrisonSlot(this, pos.x + (i->first*(58+interx)), pos.y + 64 + intery,i->first,1, &CGI->creh->creatures[i->second.first],i->second.second);
+				new CGarrisonSlot(this, pos.x + (i->first*(58+interx)), pos.y + 64 + intery,i->first,1, 
+									&CGI->creh->creatures[i->second.first],i->second.second);
 		}
 		for(int i=0; i<sup->size(); i++)
 			if((*sdown)[i] == NULL)
@@ -464,7 +467,8 @@ void CGarrisonInt::splitStacks(int am2)
 		am2);
 
 }
-CGarrisonInt::CGarrisonInt(int x, int y, int inx, int iny, SDL_Surface *pomsur, int OX, int OY, const CArmedInstance *s1, const CArmedInstance *s2)
+CGarrisonInt::CGarrisonInt(int x, int y, int inx, int iny, SDL_Surface *pomsur, int OX, int OY, const CArmedInstance *s1, 
+						   const CArmedInstance *s2)
 	:interx(inx),intery(iny),sur(pomsur),highlighted(NULL),sup(NULL),sdown(NULL),oup(s1),odown(s2),
 	offx(OX),offy(OY)
 {
@@ -2527,13 +2531,16 @@ void CHeroList::clickRight(tribool down)
 		}
 
 		//show popup
-		CInfoPopup * ip = new CInfoPopup(graphics->heroWins[items[from+ny].first->subID],LOCPLINT->current->motion.x-graphics->heroWins[items[from+ny].first->subID]->w,LOCPLINT->current->motion.y-graphics->heroWins[items[from+ny].first->subID]->h,false);
+		CInfoPopup * ip = new CInfoPopup(graphics->heroWins[items[from+ny].first->subID],
+										LOCPLINT->current->motion.x-graphics->heroWins[items[from+ny].first->subID]->w,
+										LOCPLINT->current->motion.y-graphics->heroWins[items[from+ny].first->subID]->h,false
+										);
 		ip->activate();
 	}
 	else
 	{
-			LOCPLINT->adventureInt->handleRightClick(CGI->preth->zelp[303].second,down,this);
-			LOCPLINT->adventureInt->handleRightClick(CGI->preth->zelp[304].second,down,this);
+		LOCPLINT->adventureInt->handleRightClick(CGI->preth->zelp[303].second,down,this);
+		LOCPLINT->adventureInt->handleRightClick(CGI->preth->zelp[304].second,down,this);
 	}
 }
 void CHeroList::hover (bool on)
@@ -2555,8 +2562,12 @@ void CHeroList::updateHList()
 }
 void CHeroList::updateMove(const CGHeroInstance* which) //draws move points bar
 {
-	int ser = LOCPLINT->cb->getHeroSerial(which);
+	int ser = -1;
+	for(int i=0; i<items.size() && ser<0; i++)
+		if(items[i].first->subID == which->subID)
+			ser = i;
 	ser -= from;
+	if(ser<0 || ser > SIZE) return;
 	int pom = std::min((which->movement)/100,(int)mobile->ourImages.size()-1);
 	blitAt(mobile->ourImages[pom].bitmap,posmobx,posmoby+ser*32); //move point
 }

+ 150 - 0
client/CConfigHandler.cpp

@@ -0,0 +1,150 @@
+//#define BOOST_SPIRIT_DEBUG
+#include "CConfigHandler.h"
+#include <boost/bind.hpp>
+#include <boost/spirit.hpp>
+#include <fstream>
+using namespace config;
+using namespace boost::spirit;
+
+CConfigHandler conf;
+
+
+struct lerror
+{
+	std::string txt;
+	lerror(const std::string & TXT):txt(TXT){};
+	void operator()() const
+	{
+		tlog1 << txt << std::endl;
+	}
+	template<typename IteratorT>
+	void operator()(IteratorT t1, IteratorT t2) const
+	{
+		tlog1 << txt << std::endl;
+	}
+};
+struct lerror2
+{
+	std::string txt;
+	lerror2(const std::string & TXT):txt(TXT){};
+	template<typename IteratorT>
+	void operator()(IteratorT t1, IteratorT t2) const
+	{
+		std::string txt2(t1,t2);
+		tlog1 << txt << txt2 << std::endl;
+	}
+};
+//template <typename T, typename U>
+//struct AssignInAll
+//{
+//	std::vector<T> &items;
+//	U T::*pointer;
+//	AssignInAll(std::vector<T> &Items, U T::*Pointer)
+//		:items(Items),pointer(Pointer)
+//	{}
+//	void operator()(const U &as)
+//	{
+//		for(int i=0; i<items.size(); i++)
+//			items[i].*pointer = U;
+//	}
+//};
+struct SettingsGrammar : public grammar<SettingsGrammar>
+{
+	template <typename ScannerT>
+	struct definition
+	{
+		rule<ScannerT>  r, clientOption, clientOptionsSequence;
+		rule<ScannerT>  GUIOption, GUIOptionsSequence, AdvMapOptionsSequence, AdvMapOption;
+		definition(SettingsGrammar const& self)  
+		{ 
+			clientOption 
+				= str_p("resolution=") >> (uint_p[assign_a(conf.cc.resx)] >> 'x' >> uint_p[assign_a(conf.cc.resy)] | eps_p[lerror("Wrong resolution!")])
+				| str_p("port=") >> (uint_p[assign_a(conf.cc.port)] | eps_p[lerror("Wrong port!")])
+				| str_p("bpp=") >> (uint_p[assign_a(conf.cc.bpp)] | eps_p[lerror("Wrong bpp!")])
+				| str_p("localInformation=") >> (uint_p[assign_a(conf.cc.localInformation)] | eps_p[lerror("Wrong localInformation!")])
+				| str_p("fullscreen=") >> (uint_p[assign_a(conf.cc.fullscreen)] | eps_p[lerror("Wrong fullscreen!")])
+				| str_p("server=") >> ( ( +digit_p >> *('.' >> +digit_p) )[assign_a(conf.cc.server)]   | eps_p[lerror("Wrong server!")])
+				| str_p("defaultAI=") >> ((+(anychar_p - ';'))[assign_a(conf.cc.defaultAI)] | eps_p[lerror("Wrong defaultAI!")])
+				| (+(anychar_p - '}'))[lerror2("Unrecognized client option: ")]
+				;
+			clientOptionsSequence = *(clientOption >> (';' | eps_p[lerror("Semicolon lacking!")]));
+
+			AdvMapOption 
+				=	str_p("Buttons") >> ((ch_p('{') >> '}') | eps_p[lerror("Wrong Buttons!")])
+				|	str_p("Minimap : ") >> 
+						*(	
+							(	"width=" >> uint_p[assign_a(conf.gc.ac.minimap.w)] 
+							  |	"height=" >> uint_p[assign_a(conf.gc.ac.minimap.h)]
+							  |	"x=" >> uint_p[assign_a(conf.gc.ac.minimap.x)]
+							  |	"y=" >> uint_p[assign_a(conf.gc.ac.minimap.y)]
+							) 
+							>> *ch_p(',')
+						 );
+			AdvMapOptionsSequence = *(AdvMapOption >> (';' | eps_p[lerror("Semicolon lacking!")]));
+			
+			GUIOption = str_p("AdventureMap") >> (('{' >> AdvMapOptionsSequence >> '}') | eps_p[lerror("Wrong AdventureMap!")]);
+			GUIOptionsSequence = *(GUIOption >> (';' | eps_p[lerror("Semicolon after GUIOption lacking!")]));
+			r	= str_p("clientSettings") >> (('{' >> clientOptionsSequence >> '}') | eps_p[lerror("Wrong clientSettings!")])
+				>> str_p("GUISettings") >> (('{' >> GUIOptionsSequence >> '}') | eps_p[lerror("Wrong GUISettings!")]);
+			#ifdef BOOST_SPIRIT_DEBUG
+				BOOST_SPIRIT_DEBUG_RULE(clientOption);
+				BOOST_SPIRIT_DEBUG_RULE(clientOptionsSequence);
+				BOOST_SPIRIT_DEBUG_RULE(AdvMapOption);
+				BOOST_SPIRIT_DEBUG_RULE(AdvMapOptionsSequence);
+				BOOST_SPIRIT_DEBUG_RULE(GUIOption);
+				BOOST_SPIRIT_DEBUG_RULE(GUIOptionsSequence);
+				BOOST_SPIRIT_DEBUG_RULE(r);
+			#endif
+		}    
+
+		rule<ScannerT> const& start() const { return r; }
+	};
+};
+
+struct CommentsGrammar : public grammar<CommentsGrammar>
+{
+	template <typename ScannerT>
+	struct definition
+	{
+		rule<ScannerT>  comment;
+		definition(CommentsGrammar const& self)  
+		{ 
+			comment = comment_p("//") | comment_p("/*","*/") | space_p;
+			BOOST_SPIRIT_DEBUG_RULE(comment);
+		}
+		rule<ScannerT> const& start() const { return comment; }
+	};
+};
+
+CConfigHandler::CConfigHandler(void)
+{
+}
+
+CConfigHandler::~CConfigHandler(void)
+{
+}
+
+void config::CConfigHandler::init()
+{
+	std::vector<char> settings;
+	std::ifstream ifs("config/settings.txt");
+	if(!ifs)
+	{
+		tlog1 << "Cannot open config/settings.txt !\n";
+		return;
+	}
+	ifs.unsetf(std::ios::skipws); //  Turn of white space skipping on the stream
+	std::copy(std::istream_iterator<char>(ifs),std::istream_iterator<char>(),std::back_inserter(settings));
+	std::vector<char>::const_iterator first = settings.begin(), last = settings.end();
+	SettingsGrammar sg;
+	BOOST_SPIRIT_DEBUG_NODE(sg);
+	CommentsGrammar cg;    
+	BOOST_SPIRIT_DEBUG_NODE(cg);
+
+
+	parse_info<std::vector<char>::const_iterator> info = parse(first,last,sg,cg);
+	if(!info.hit)
+		tlog1 << "Cannot parse config/settings.txt file!\n";
+	else if(!info.full)
+		tlog2 << "Not entire config/settings.txt parsed!\n";
+}

+ 45 - 0
client/CConfigHandler.h

@@ -0,0 +1,45 @@
+#pragma once
+#include "../global.h"
+class CAdvMapInt;
+namespace config
+{
+	struct ClientConfig
+	{
+		int resx, resy, bpp, fullscreen; //client resolution/colours
+		int port, localInformation;
+		std::string server, //server address (e.g. 127.0.0.1)
+			defaultAI; //dll name
+	};
+	struct AdventureMapConfig
+	{
+		struct ButtonInfo
+		{
+			std::string hoverName, //shows in statusbar when hovered
+				helpBox, //shows in pop-up when r-clicked
+				defName;
+			std::vector<std::string> additionalDefs;
+			void (CAdvMapInt::*func)(); //function in advmapint bound to that button
+			int x, y; //position on the screen
+			bool playerColoured; //if true button will be colored to main player's color (works properly only for appropriate 8bpp graphics)
+		};
+		struct Minimap
+		{
+			int x, y, w, h;
+		} minimap;
+		std::vector<ButtonInfo> buttons;
+	};
+	struct GUIOptions
+	{
+		AdventureMapConfig ac;
+	};
+	class CConfigHandler
+	{
+	public:
+		ClientConfig cc;
+		GUIOptions gc;
+		void init();
+		CConfigHandler(void);
+		~CConfigHandler(void);
+	};
+}
+extern config::CConfigHandler conf;

+ 33 - 21
client/Client.cpp

@@ -1,22 +1,23 @@
-#include "Client.h"
-#include "../lib/Connection.h"
-#include "../StartInfo.h"
-#include "../map.h"
-#include "../CGameState.h"
-#include "../CGameInfo.h"
-#include "../mapHandler.h"
 #include "../CCallback.h"
-#include "../CPlayerInterface.h"
 #include "../CConsoleHandler.h"
+#include "../CGameInfo.h"
+#include "../CGameState.h"
+#include "../CPlayerInterface.h"
+#include "../StartInfo.h"
+#include "../hch/CArtHandler.h"
+#include "../hch/CGeneralTextHandler.h"
+#include "../hch/CObjectHandler.h"
+#include "../lib/Connection.h"
 #include "../lib/NetPacks.h"
+#include "../lib/VCMI_Lib.h"
+#include "../map.h"
+#include "../mapHandler.h"
+#include "CConfigHandler.h"
+#include "Client.h"
 #include <boost/bind.hpp>
-#include <boost/thread.hpp>
 #include <boost/foreach.hpp>
-#include "../hch/CObjectHandler.h"
-#include "../hch/CGeneralTextHandler.h"
-#include "../hch/CArtHandler.h"
+#include <boost/thread.hpp>
 #include <boost/thread/shared_mutex.hpp>
-#include "../lib/VCMI_Lib.h"
 CSharedCond<std::set<IPack*> > mess(new std::set<IPack*>);
 
 std::string toString(MetaString &ms)
@@ -126,11 +127,7 @@ CClient::CClient(CConnection *con, StartInfo *si)
 	if(mapa->checksum != sum)
 	{
 		tlog1 << "Wrong map checksum!!!" << std::endl;
-#ifndef __GNUC__
-		throw std::exception("Wrong checksum");
-#else
-		throw std::exception();
-#endif
+		throw std::string("Wrong checksum");
 	}
 	tlog0 << "\tUsing random seed: "<<seed << std::endl;
 
@@ -151,7 +148,7 @@ CClient::CClient(CConnection *con, StartInfo *si)
 		CCallback *cb = new CCallback(gs,color,this);
 		if(!gs->scenarioOps->playerInfos[i].human)
 		{
-			playerint[color] = static_cast<CGameInterface*>(CAIHandler::getNewAI(cb,"EmptyAI.dll"));
+			playerint[color] = static_cast<CGameInterface*>(CAIHandler::getNewAI(cb,conf.cc.defaultAI));
 		}
 		else 
 		{
@@ -160,7 +157,6 @@ CClient::CClient(CConnection *con, StartInfo *si)
 			playerint[color]->init(cb);
 		}
 	}
-	//cb = CGI->consoleh->cb = new CCallback(gs,-1,this);
 }
 CClient::~CClient(void)
 {
@@ -646,7 +642,23 @@ void CClient::process(int what)
 			SpellCasted sc;
 			*serv >> sc;
 			gs->apply(&sc);
-			//todo - apply
+			if(playerint.find(gs->curB->side1) != playerint.end())
+				playerint[gs->curB->side1]->battleSpellCasted(&sc);
+			if(playerint.find(gs->curB->side2) != playerint.end())
+				playerint[gs->curB->side2]->battleSpellCasted(&sc);
+			break;
+		}
+	case 3010:
+		{
+			tlog5 << "Effect set!\n";
+			SetStackEffect sse;
+			*serv >> sse;
+			gs->apply(&sse);
+			SpellCasted sc;
+			sc.id = sse.stack;
+			sc.side = 3; //doesn't matter
+			sc.skill = sse.effect.level;
+			sc.tile = gs->curB->getStack(sse.stack)->position;
 			if(playerint.find(gs->curB->side1) != playerint.end())
 				playerint[gs->curB->side1]->battleSpellCasted(&sc);
 			if(playerint.find(gs->curB->side2) != playerint.end())

+ 8 - 0
client/VCMI_client.vcproj

@@ -294,6 +294,10 @@
 				RelativePath="..\CCastleInterface.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\CConfigHandler.cpp"
+				>
+			</File>
 			<File
 				RelativePath=".\CCreatureAnimation.cpp"
 				>
@@ -436,6 +440,10 @@
 				RelativePath="..\CCastleInterface.h"
 				>
 			</File>
+			<File
+				RelativePath=".\CConfigHandler.h"
+				>
+			</File>
 			<File
 				RelativePath=".\CCreatureAnimation.h"
 				>

+ 24 - 0
config/settings.txt

@@ -0,0 +1,24 @@
+//DO NOT EDIT!!!
+//DO NOT READ!
+clientSettings
+{
+	port=3030;
+	resolution=800x600; // format: WxH
+	bpp=24; // bpp!=24 => problems
+	fullscreen=0; //0 - windowed mode, 1 - fullscreen
+	server=127.0.0.1; //use 127.0.0.1 for localhost
+	localInformation=2; //0 - *all* information sent from server (safest and slowest); 1 - map information sent from server; 2 - all information local-storaged
+	defaultAI=EmptyAI.dll; 
+}
+GUISettings
+{
+	//800x600 //settings specific for 800x600 resolution
+	//{
+		AdventureMap
+		{
+			Buttons
+			{
+			};
+		};
+	//};
+}

+ 7 - 2
hch/CObjectHandler.cpp

@@ -274,7 +274,7 @@ int CGHeroInstance::manaLimit() const
 	case 2:		modifier+=0.5;		break;
 	case 3:		modifier+=1.0;		break;
 	}
-	return 10*primSkills[3]*modifier;
+	return 10*getPrimSkillLevel(3)*modifier;
 }
 //void CGHeroInstance::setPosition(int3 Pos, bool h3m) //as above, but sets position
 //{
@@ -312,6 +312,11 @@ int CGHeroInstance::getSecSkillLevel(const int & ID) const
 }
 int lowestSpeed(const CGHeroInstance * chi)
 {
+	if(!chi->army.slots.size())
+	{
+		tlog1 << "Error! Hero " << chi->id << " ("<<chi->name<<") has no army!\n";
+		return 20;
+	}
 	std::map<si32,std::pair<ui32,si32> >::const_iterator i = chi->army.slots.begin();
 	int ret = VLC->creh->creatures[(*i++).second.first].speed;
 	for (;i!=chi->army.slots.end();i++)
@@ -569,7 +574,7 @@ void CGHeroInstance::initHero()
 
 	if(portrait < 0)
 		portrait = subID;
-	if((!primSkills.size()) || (primSkills[0]<0))
+	if((!primSkills.size()) || (getPrimSkillLevel(0)<0))
 	{
 		primSkills.resize(4);
 		primSkills[0] = type->heroClass->initialAttack;

+ 11 - 0
lib/NetPacks.h

@@ -543,6 +543,17 @@ struct SpellCasted : public CPack<SpellCasted>//3009
 	}
 };
 
+struct SetStackEffect : public CPack<SetStackEffect> //3010
+{
+	ui32 stack;
+	CStack::StackEffect effect;
+	SetStackEffect(){type = 3010;};
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & stack & effect;
+	}
+};
+
 struct ShowInInfobox : public CPack<ShowInInfobox> //107
 {
 	ShowInInfobox(){type = 107;};

+ 20 - 1
server/CGameHandler.cpp

@@ -56,6 +56,19 @@ double distance(int3 a, int3 b)
 {
 	return std::sqrt( (double)(a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) );
 }
+int getSchoolLevel(const CGHeroInstance *h, const CSpell *s)
+{
+	int ret = 0;
+	if(s->fire)
+		ret = std::max(ret,h->getSecSkillLevel(14));
+	if(s->air)
+		ret = std::max(ret,h->getSecSkillLevel(15));
+	if(s->water)
+		ret = std::max(ret,h->getSecSkillLevel(16));
+	if(s->earth)
+		ret = std::max(ret,h->getSecSkillLevel(17));
+	return ret;
+}
 void giveExp(BattleResult &r)
 {
 	r.exp[0] = 0;
@@ -1224,7 +1237,7 @@ upgend:
 									BattleStackAttacked bsa;
 									bsa.flags |= 2;
 									bsa.effect = 64;
-									bsa.damageAmount = h->primSkills[2] * 10; //TODO: use skill level
+									bsa.damageAmount = h->getPrimSkillLevel(2) * 10  +  s->powers[getSchoolLevel(h,s)]; 
 									bsa.stackAttacked = attacked->ID;
 									prepareAttacked(bsa,attacked);
 									sendAndApply(&bsa);
@@ -1232,6 +1245,12 @@ upgend:
 								}
 							case 53: //haste
 								{
+									SetStackEffect sse;
+									sse.stack = gs->curB->getStackT(ba.destinationTile)->ID;
+									sse.effect.id = 53;
+									sse.effect.level = getSchoolLevel(h,s);
+									sse.effect.turnsRemain = h->getPrimSkillLevel(2);
+									sendAndApply(&sse);
 									break;
 								}
 							}