瀏覽代碼

- pregame will use same resolution as main game
- disabled most of now unused code for changing resolution
- added missing spell effect sounds
- adventure map spells sounds
- remaining fixes for big endian systems

Ivan Savenko 13 年之前
父節點
當前提交
45c2809a40

+ 3 - 9
client/CAdvmapInterface.cpp

@@ -216,12 +216,6 @@ void CMinimapSurfacesRef::initMap(int level)
 
 void CMinimapSurfacesRef::initFoW(int level)
 {
-	/*for(int g=0; g<FoW.size(); ++g)
-	{
-		SDL_FreeSurface(FoW[g]);
-	}
-	FoW.clear();*/
-
 	const Rect &minimap_pos = adventureInt->minimap.pos;
 	int3 mapSizes = LOCPLINT->cb->getMapSize();
 	int mw = map_[0]->w, mh = map_[0]->h;//,
@@ -230,7 +224,7 @@ void CMinimapSurfacesRef::initFoW(int level)
 	{
 		if(level>=0 && d!=level)
 			continue;
-		SDL_Surface * pt = CSDL_Ext::newSurface(minimap_pos.w, minimap_pos.h, CSDL_Ext::std32bppSurface);
+		SDL_Surface * pt = CSDL_Ext::createSurfaceWithBpp<4>(minimap_pos.w, minimap_pos.h);
 		for (int i=0; i<mw; i++)
 		{
 			for (int j=0; j<mh; j++)
@@ -261,7 +255,7 @@ void CMinimapSurfacesRef::initFlaggableObjs(int level)
 	{
 		if(level>=0 && d!=level)
 			continue;
-		SDL_Surface * pt = CSDL_Ext::newSurface(minimap_pos.w, minimap_pos.h, CSDL_Ext::std32bppSurface);
+		SDL_Surface * pt = CSDL_Ext::createSurfaceWithBpp<4>(minimap_pos.w, minimap_pos.h);
 		for (int i=0; i<mw; i++)
 		{
 			for (int j=0; j<mh; j++)
@@ -1648,7 +1642,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView /*= true*/)
 	LOCPLINT->cb->setSelection(sel);
 	selection = sel;
 	if (LOCPLINT->battleInt == NULL)
-		CCS->musich->playMusic(CCS->musich->terrainMusics[LOCPLINT->cb->getTile(sel->visitablePos())->tertype]);
+		CCS->musich->playMusic(CCS->musich->terrainMusics[LOCPLINT->cb->getTile(sel->visitablePos())->tertype], -1);
 	if(centerView)
 		centerOn(sel);
 

+ 11 - 8
client/CAdvmapInterface.h

@@ -40,13 +40,16 @@ public:
 	static void showScenarioInfo();
 };
 
-class CMinimapSurfacesRef {
+//Player-specific minimaps
+class CMinimapSurfacesRef
+{
 public:
-    CMinimapSurfacesRef();
-    std::vector< SDL_Surface* > &map();
-    std::vector< SDL_Surface* > &FoW();
-    std::vector< SDL_Surface* > &flObjs();
-    void free();
+	CMinimapSurfacesRef();
+	// see private members descriptions
+	std::vector< SDL_Surface* > &map();
+	std::vector< SDL_Surface* > &FoW();
+	std::vector< SDL_Surface* > &flObjs();
+	void free();
 private:
 	void redraw(int level=-1);// (level==-1) => redraw all levels
 	void initMap(int level=-1);// (level==-1) => redraw all levels
@@ -54,8 +57,8 @@ private:
 	void initFlaggableObjs(int level=-1);// (level==-1) => redraw all levels
 	void showVisibleTiles(int level=-1);// (level==-1) => redraw all levels
 private:    
-    std::vector< SDL_Surface* > map_, FoW_, flObjs_; //one bitmap for each level (terrain, Fog of War, flaggable objects) (one for underworld, one for surface)
-    bool ready;
+	std::vector< SDL_Surface* > map_, FoW_, flObjs_; //one bitmap for each level (terrain, Fog of War, flaggable objects) (one for underworld, one for surface)
+	bool ready;
 };
 
 /// Minimap which is displayed at the right upper corner of adventure map

+ 1 - 10
client/CConfigHandler.cpp

@@ -270,15 +270,6 @@ void config::CConfigHandler::init()
 	}
 
 	const JsonNode& screenRes = settings["video"]["screenRes"];
-	const JsonNode& gameRes = settings["video"]["gameRes"];
 
-	//fixing screenx / screeny
-	if (screenRes["width"].Float()  != gameRes["width"].Float()
-	 || screenRes["height"].Float() != gameRes["height"].Float())
-	{
-		Settings screen = settings.write["video"]["screenRes"];
-		screen["width"].Float()  = gameRes["width"].Float();
-		screen["height"].Float() = gameRes["height"].Float();
-	}
-	SetResolution(gameRes["width"].Float(), gameRes["height"].Float());
+	SetResolution(screenRes["width"].Float(), screenRes["height"].Float());
 }

+ 33 - 30
client/CMT.cpp

@@ -75,7 +75,7 @@ std::queue<SDL_Event*> events;
 boost::mutex eventsM;
 
 static bool gOnlyAI = false;
-static bool setResolution = false; //set by event handling thread after resolution is adjusted
+//static bool setResolution = false; //set by event handling thread after resolution is adjusted
 
 static bool ermInteractiveMode = false; //structurize when time is right
 void processCommand(const std::string &message);
@@ -83,7 +83,7 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
 void dispose();
 void playIntro();
 static void listenForEvents();
-void requestChangingResolution();
+//void requestChangingResolution();
 void startGame(StartInfo * options, CConnection *serv = NULL);
 
 #ifndef _WIN32
@@ -102,7 +102,7 @@ void startGameFromFile(const std::string &fname)
 		if(!out.sfile || !*out.sfile)
 		{
 			tlog1 << "Failed to open startfile, falling back to the main menu!\n";
-			GH.curInt = new CGPreGame;
+			GH.curInt = CGPreGame::create();
 			return;
 		}
 		out >> si;
@@ -116,12 +116,6 @@ void startGameFromFile(const std::string &fname)
 void init()
 {
 	CStopWatch tmh, pomtime;
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
-	int rmask = 0xff000000;int gmask = 0x00ff0000;int bmask = 0x0000ff00;int amask = 0x000000ff;
-#else
-	int rmask = 0x000000ff;	int gmask = 0x0000ff00;	int bmask = 0x00ff0000;	int amask = 0xff000000;
-#endif
-	CSDL_Ext::std32bppSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, 1, 32, rmask, gmask, bmask, amask);
 	tlog0 << "\tInitializing minors: " << pomtime.getDiff() << std::endl;
 
 	//initializing audio
@@ -256,7 +250,7 @@ int main(int argc, char** argv)
 	atexit(SDL_Quit);
 
 	const JsonNode& video = settings["video"];
-	const JsonNode& res = video["menuRes"];
+	const JsonNode& res = video["screenRes"];
 
 	setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), video["fullscreen"].Bool());
 
@@ -294,7 +288,7 @@ int main(int argc, char** argv)
 
 
 		if(!vm.count("start"))
-			GH.curInt = new CGPreGame; //will set CGP pointer to itself
+			GH.curInt = CGPreGame::create(); //will set CGP pointer to itself
 		else
 			startGameFromFile(vm["start"].as<std::string>());
 	}
@@ -332,7 +326,6 @@ void processCommand(const std::string &message)
 	std::string cn; //command name
 	readed >> cn;
 
-
 	if(LOCPLINT && LOCPLINT->cingconsole)
 		LOCPLINT->cingconsole->print(message);
 
@@ -408,7 +401,7 @@ void processCommand(const std::string &message)
 		std::string fname;
 		readed >> fname;
 		client->loadGame(fname);
-	}
+	}/*
 	else if(cn=="resolution" || cn == "r")
 	{
 		if(LOCPLINT)
@@ -437,15 +430,14 @@ void processCommand(const std::string &message)
 			auto j = conf.guiOptions.begin();
 			std::advance(j, i - 1); //move j to the i-th resolution info
 			const int w = j->first.first, h = j->first.second;
-			Settings res = settings.write["video"]["gameRes"];
 			Settings screen = settings.write["video"]["screenRes"];
-			res["width"].Float()  = screen["width"].Float()  = w;
-			res["height"].Float() = screen["height"].Float() = h;
+			screen["width"].Float()  = w;
+			screen["height"].Float() = h;
 			conf.SetResolution(screen["width"].Float(), screen["height"].Float());
 			tlog0 << "Screen resolution set to " << (int)screen["width"].Float() << " x "
-			                                     << (int)screen["height"].Float() <<". It will be applied when the game starts.\n";
+												 << (int)screen["height"].Float() <<". It will be applied afters game restart.\n";
 		}
-	}
+	}*/
 	else if(message=="get txt")
 	{
 		boost::filesystem::create_directory("Extracted_txts");
@@ -578,6 +570,7 @@ void dispose()
 	delete logfile;
 }
 
+//used only once during initialization
 static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo)
 {
 	// VCMI will only work with 2, 3 or 4 bytes per pixel
@@ -596,7 +589,7 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
 	
 	if(suggestedBpp != bpp)
 	{
-		tlog2 << "Warning: SDL says that "  << bpp << "bpp is wrong and suggests " << suggestedBpp << std::endl;
+		tlog2 << "Note: SDL suggests to use " << suggestedBpp << " bpp instead of"  << bpp << " bpp "  << std::endl;
 	}
 
 	//For some reason changing fullscreen via config window checkbox result in SDL_Quit event
@@ -647,15 +640,24 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
 	//TODO: centering game window on other platforms (or does the environment do their job correctly there?)
 
 	screenBuf = bufOnScreen ? screen : screen2;
-	setResolution = true;
+	//setResolution = true;
 }
 
 static void fullScreenChanged(const JsonNode &newState)
 {
 	boost::unique_lock<boost::recursive_mutex> lock(*LOCPLINT->pim);
-	const JsonNode& video = settings["video"];
-	const JsonNode& res = video["screenRes"];
-	setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), newState.Bool(), false);
+
+	bool fullscreen = newState.Bool();
+	int bitsPerPixel = screen->format->BitsPerPixel;
+
+	bitsPerPixel = SDL_VideoModeOK(screen->w, screen->h, bitsPerPixel, SDL_SWSURFACE|(fullscreen?SDL_FULLSCREEN:0));
+	if(bitsPerPixel == 0)
+	{
+		tlog1 << "Error: SDL says that " << screen->w << "x" << screen->h << " resolution is not available!\n";
+		return;
+	}
+
+	screen = SDL_SetVideoMode(screen->w, screen->h, bitsPerPixel, SDL_SWSURFACE|(fullscreen?SDL_FULLSCREEN:0));
 	GH.totalRedraw();
 }
 
@@ -711,17 +713,17 @@ static void listenForEvents()
 
 			switch(ev->user.code)
 			{
-			case CHANGE_SCREEN_RESOLUTION:
+		/*	case CHANGE_SCREEN_RESOLUTION:
 			{
 				tlog0 << "Changing resolution has been requested\n";
 				const JsonNode& video = settings["video"];
 				const JsonNode& res = video["gameRes"];
 				setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), video["fullscreen"].Bool());
 				break;
-			}
+			}*/
 			case RETURN_TO_MAIN_MENU:
 				endGame();
-				CGPreGame::createIfNotPresent();
+				CGPreGame::create();
 				GH.curInt = CGP;
 				GH.defActionsDef = 63;
 				break;
@@ -737,7 +739,7 @@ static void listenForEvents()
 				break;
 			case RETURN_TO_MENU_LOAD:
 				endGame();
-				CGPreGame::createIfNotPresent();
+				CGPreGame::create();
 				GH.defActionsDef = 63;
 				CGP->update();
 				CGP->menu->switchToTab(vstd::find_pos(CGP->menu->menuNameToEntry, "load"));
@@ -773,7 +775,7 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
 			it->second.human = false;
 		}
 	}
-	const JsonNode& res = settings["video"]["screenRes"];
+/*	const JsonNode& res = settings["video"]["screenRes"];
 
 	if(screen->w != res["width"].Float()   ||   screen->h != res["height"].Float())
 	{
@@ -784,7 +786,7 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
 		while(!setResolution) boost::this_thread::sleep(boost::posix_time::milliseconds(50));
 	}
 	else
-		setResolution = true;
+		setResolution = true;*/
 
 		client = new CClient;
 
@@ -805,7 +807,7 @@ void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
 
 		client->connectionHandler = new boost::thread(&CClient::run, client);
 }
-
+/*
 void requestChangingResolution()
 {
 	//mark that we are going to change resolution
@@ -817,3 +819,4 @@ void requestChangingResolution()
 	ev.user.code = CHANGE_SCREEN_RESOLUTION;
 	SDL_PushEvent(&ev);
 }
+*/

+ 4 - 1
client/CMusicHandler.cpp

@@ -242,7 +242,10 @@ void CSoundHandler::initSpellsSounds(const std::vector< ConstTransitivePtr<CSpel
 			if (vstd::contains(spellSounds, s))
 				tlog1 << "Spell << " << spellid << " already has a sound" << std::endl;
 
-			spellSounds[s] = getSoundID(node["soundfile"].String());
+			soundBase::soundID sound = getSoundID(node["soundfile"].String());
+			if (sound == soundBase::invalid)
+				tlog0 << "Error: invalid sound for id "<< spellid << "\n";
+			spellSounds[s] = sound;
 		}
 	}
 }

+ 4 - 1
client/CPlayerInterface.cpp

@@ -263,7 +263,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	if(makingTurn  &&  ho->tempOwner == playerID) //we are moving our hero - we may need to update assigned path
 	{
 		//We may need to change music - select new track, music handler will change it if needed
-		CCS->musich->playMusic(CCS->musich->terrainMusics[LOCPLINT->cb->getTile(ho->visitablePos())->tertype]);
+		CCS->musich->playMusic(CCS->musich->terrainMusics[LOCPLINT->cb->getTile(ho->visitablePos())->tertype], -1);
 
 		if(details.result == TryMoveHero::TELEPORTATION)
 		{
@@ -2073,6 +2073,9 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
 	{
 		eraseCurrentPathOf(caster, false);
 	}
+	const CSpell * spell = CGI->spellh->spells[spellID];
+	if (vstd::contains(CCS->soundh->spellSounds, spell))
+		CCS->soundh->playSound(CCS->soundh->spellSounds[spell]);
 }
 
 void CPlayerInterface::eraseCurrentPathOf( const CGHeroInstance * ho, bool checkForExistanceOfPath /*= true */ )

+ 5 - 1
client/CPlayerInterface.h

@@ -71,7 +71,11 @@ namespace boost
 	class recursive_mutex;
 };
 
-enum {CHANGE_SCREEN_RESOLUTION = 1, RETURN_TO_MAIN_MENU = 2, STOP_CLIENT = 3, RESTART_GAME,
+enum {
+	/*CHANGE_SCREEN_RESOLUTION = 1,*/
+	RETURN_TO_MAIN_MENU = 2,
+	STOP_CLIENT = 3,
+	RESTART_GAME,
 	RETURN_TO_MENU_LOAD};
 
 /// Central class for managing user interface logic

+ 115 - 54
client/CPreGame.cpp

@@ -61,7 +61,7 @@ using boost::ref;
 
 void startGame(StartInfo * options, CConnection *serv = NULL);
 
-CGPreGame * CGP;
+CGPreGame * CGP = nullptr;
 ISelectionScreenInfo *SEL;
 
 static int playerColor; //if more than one player - applies to the first
@@ -215,6 +215,9 @@ CMenuScreen::CMenuScreen(const JsonNode& configNode):
 	BOOST_FOREACH(const JsonNode& node, config["images"].Vector())
 		images.push_back(createPicture(node));
 
+	if (!images.empty())
+		pos = images[0]->center();
+
 	//Hardcoded entry
 	menuNameToEntry.push_back("credits");
 
@@ -230,10 +233,19 @@ CIntObject * CMenuScreen::createTab(size_t index)
 	return new CMenuEntry(this, config["items"].Vector()[index]);
 }
 
+void CMenuScreen::showAll(SDL_Surface * to)
+{
+	CIntObject::showAll(to);
+
+	if (pos.h != to->h || pos.w != to->w)
+		CMessage::drawBorder(1, to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);
+
+}
+
 void CMenuScreen::show(SDL_Surface * to)
 {
 	if (!config["video"].isNull())
-		CCS->videoh->update(config["video"]["x"].Float(), config["video"]["y"].Float(), to, true, false);
+		CCS->videoh->update(config["video"]["x"].Float() + pos.x, config["video"]["y"].Float() + pos.y, to, true, false);
 	CIntObject::show(to);
 }
 
@@ -355,15 +367,20 @@ CreditsScreen::CreditsScreen()
 	used |= LCLICK | RCLICK;
 	type |= REDRAW_PARENT;
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
-	pos.w = 800;
-	pos.h = 600;
+	pos.w = CGP->menu->pos.w;
+	pos.h = CGP->menu->pos.h;
 	std::string text = bitmaph->getTextFile("CREDITS");
 	size_t firstQuote = text.find('\"')+1;
 	text = text.substr(firstQuote, text.find('\"', firstQuote) - firstQuote );
-	credits = new CTextBox(text, Rect(450, 600, 350, 32000), 0, FONT_CREDITS, CENTER, Colors::Cornsilk);
+	credits = new CTextBox(text, Rect(pos.w - 350, 600, 350, 32000), 0, FONT_CREDITS, CENTER, Colors::Cornsilk);
 	credits->pos.h = credits->maxH;
 }
 
+void CreditsScreen::showAll(SDL_Surface * to)
+{
+	//Do not draw anything
+}
+
 void CreditsScreen::show(SDL_Surface * to)
 {
 	static int count = 0;
@@ -373,9 +390,13 @@ void CreditsScreen::show(SDL_Surface * to)
 		credits->pos.y--;
 		count = 0;
 	}
-	SDL_SetClipRect(screen, &credits->pos);
+	Rect creditsArea = credits->pos & pos;
+	SDL_SetClipRect(screenBuf, &creditsArea);
+	SDL_SetClipRect(screen, &creditsArea);
 	redraw();
+	CIntObject::showAll(to);
 	SDL_SetClipRect(screen, NULL);
+	SDL_SetClipRect(screenBuf, NULL);
 
 	//end of credits, close this screen
 	if (credits->pos.y + credits->pos.h < 0)
@@ -416,6 +437,7 @@ void CGPreGame::openSel(CMenuScreen::EState screenType, CMenuScreen::EMultiMode
 
 void CGPreGame::loadGraphics()
 {
+	background = BitmapHandler::loadBitmap("DIBOXBCK");
 	victory = CDefHandler::giveDef("SCNRVICT.DEF");
 	loss = CDefHandler::giveDef("SCNRLOSS.DEF");
 	bonuses = CDefHandler::giveDef("SCNRSTAR.DEF");
@@ -429,6 +451,7 @@ void CGPreGame::disposeGraphics()
 {
 	delete victory;
 	delete loss;
+	SDL_FreeSurface(background);
 	SDL_FreeSurface(rHero);
 	SDL_FreeSurface(nHero);
 	SDL_FreeSurface(rTown);
@@ -439,6 +462,7 @@ void CGPreGame::update()
 {
 	if (GH.listInt.empty())
 	{
+		GH.pushInt(this);
 		GH.pushInt(menu);
 		menu->switchToTab(0);
 	}
@@ -464,6 +488,18 @@ void CGPreGame::update()
 	CCS->curh->draw2();
 }
 
+void CGPreGame::showAll(SDL_Surface *to)
+{
+	//fill screen with background texture
+	for (int y=0; y<to->h; y+=background->h)
+	{
+		for (int x=0; x<to->w; x+=background->w)
+		{
+			blitAt(background, x, y, to);
+		}
+	}
+}
+
 void CGPreGame::openCampaignScreen(std::string name)
 {
 	BOOST_FOREACH(const JsonNode& node, (*pregameConfig)["campaignsset"].Vector())
@@ -477,17 +513,18 @@ void CGPreGame::openCampaignScreen(std::string name)
 	tlog1<<"Unknown campaign set: "<<name<<"\n";
 }
 
-void CGPreGame::createIfNotPresent()
+CGPreGame *CGPreGame::create()
 {
 	if(!CGP)
 		CGP = new CGPreGame();
+	return CGP;
 }
 
 CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer /*= CMenuScreen::SINGLE_PLAYER*/, const std::map<ui32, std::string> *Names /*= NULL*/)
 	: ISelectionScreenInfo(Names), serverHandlingThread(NULL), mx(new boost::recursive_mutex),
 	  serv(NULL), ongoingClosing(false), myNameID(255)
 {
-	CGPreGame::createIfNotPresent(); //we depend on its graphics
+	CGPreGame::create(); //we depend on its graphics
 	screenType = Type;
 	multiPlayer = MultiPlayer;
 
@@ -507,20 +544,20 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 	pos.h = 584;
 	if(Type == CMenuScreen::saveGame)
 	{
+		bordered = false;
 		center(pos);
 	}
 	else if(Type == CMenuScreen::campaignList)
 	{
+		bordered = false;
 		bg = new CPicture(BitmapHandler::loadBitmap("CamCust.bmp"), 0, 0, true);
-
-		pos.x = 3;
-		pos.y = 6;
+		pos = bg->center();
 	}
 	else
 	{
-		pos.x = 3;
-		pos.y = 6;
-		bg = new CPicture(BitmapHandler::loadBitmap(rand()%2 ? "ZPIC1000.bmp" : "ZPIC1001.bmp"), -3, -6, true);
+		bordered = true;
+		bg = new CPicture(BitmapHandler::loadBitmap(rand()%2 ? "ZPIC1000.bmp" : "ZPIC1001.bmp"), 0, 0, true);
+		pos = bg->center();
 	}
 
 	sInfo.difficulty = 1;
@@ -549,20 +586,20 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		{
 			card->difficulty->onChange = bind(&CSelectionScreen::difficultyChange, this, _1);
 			card->difficulty->select(1, 0);
-			CAdventureMapButton *select = new CAdventureMapButton(CGI->generaltexth->zelp[45], bind(&CSelectionScreen::toggleTab, this, sel), 411, 75, "GSPBUTT.DEF", SDLK_s);
+			CAdventureMapButton *select = new CAdventureMapButton(CGI->generaltexth->zelp[45], bind(&CSelectionScreen::toggleTab, this, sel), 411, 80, "GSPBUTT.DEF", SDLK_s);
 			select->addTextOverlay(CGI->generaltexth->allTexts[500], FONT_SMALL);
 
-			CAdventureMapButton *opts = new CAdventureMapButton(CGI->generaltexth->zelp[46], bind(&CSelectionScreen::toggleTab, this, opt), 411, 503, "GSPBUTT.DEF", SDLK_a);
+			CAdventureMapButton *opts = new CAdventureMapButton(CGI->generaltexth->zelp[46], bind(&CSelectionScreen::toggleTab, this, opt), 411, 510, "GSPBUTT.DEF", SDLK_a);
 			opts->addTextOverlay(CGI->generaltexth->allTexts[501], FONT_SMALL);
 
-			CAdventureMapButton *random = new CAdventureMapButton(CGI->generaltexth->zelp[47], bind(&CSelectionScreen::toggleTab, this, sel), 411, 99, "GSPBUTT.DEF", SDLK_r);
+			CAdventureMapButton *random = new CAdventureMapButton(CGI->generaltexth->zelp[47], bind(&CSelectionScreen::toggleTab, this, sel), 411, 105, "GSPBUTT.DEF", SDLK_r);
 			random->addTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL);
 
-			start  = new CAdventureMapButton(CGI->generaltexth->zelp[103], bind(&CSelectionScreen::startGame, this), 411, 529, "SCNRBEG.DEF", SDLK_b);
+			start  = new CAdventureMapButton(CGI->generaltexth->zelp[103], bind(&CSelectionScreen::startGame, this), 411, 535, "SCNRBEG.DEF", SDLK_b);
 
 			if(network)
 			{
-				CAdventureMapButton *hideChat = new CAdventureMapButton(CGI->generaltexth->zelp[48], bind(&InfoCard::toggleChat, card), 619, 75, "GSPBUT2.DEF", SDLK_h);
+				CAdventureMapButton *hideChat = new CAdventureMapButton(CGI->generaltexth->zelp[48], bind(&InfoCard::toggleChat, card), 619, 83, "GSPBUT2.DEF", SDLK_h);
 				hideChat->addTextOverlay(CGI->generaltexth->allTexts[531], FONT_SMALL);
 
 				if(multiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)
@@ -579,19 +616,18 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		break;
 	case CMenuScreen::loadGame:
 		sel->recActions = 255;
-		start  = new CAdventureMapButton(CGI->generaltexth->zelp[103], bind(&CSelectionScreen::startGame, this), 411, 529, "SCNRLOD.DEF", SDLK_l);
+		start  = new CAdventureMapButton(CGI->generaltexth->zelp[103], bind(&CSelectionScreen::startGame, this), 411, 535, "SCNRLOD.DEF", SDLK_l);
 		break;
 	case CMenuScreen::saveGame:
 		sel->recActions = 255;
-		start  = new CAdventureMapButton("", CGI->generaltexth->zelp[103].second, bind(&CSelectionScreen::startGame, this), 411, 529, "SCNRSAV.DEF");
+		start  = new CAdventureMapButton("", CGI->generaltexth->zelp[103].second, bind(&CSelectionScreen::startGame, this), 411, 535, "SCNRSAV.DEF");
 		break;
 	case CMenuScreen::campaignList:
 		sel->recActions = 255;
-		start  = new CAdventureMapButton(std::pair<std::string, std::string>(), bind(&CSelectionScreen::startCampaign, this), 411, 529, "SCNRLOD.DEF", SDLK_b);
+		start  = new CAdventureMapButton(std::pair<std::string, std::string>(), bind(&CSelectionScreen::startCampaign, this), 411, 535, "SCNRLOD.DEF", SDLK_b);
 		break;
 	}
 
-
 	start->assignedKeys.insert(SDLK_RETURN);
 
 	std::string backName;
@@ -604,7 +640,7 @@ CSelectionScreen::CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMulti
 		backName = "SCNRBACK.DEF";
 	}
 
-	back = new CAdventureMapButton("", CGI->generaltexth->zelp[105].second, bind(&CGuiHandler::popIntTotally, &GH, this), 581, 529, backName, SDLK_ESCAPE);
+	back = new CAdventureMapButton("", CGI->generaltexth->zelp[105].second, bind(&CGuiHandler::popIntTotally, &GH, this), 581, 535, backName, SDLK_ESCAPE);
 
 	if(network)
 	{
@@ -780,6 +816,8 @@ void CSelectionScreen::startGame()
 		StartInfo *si = new StartInfo(sInfo);
 		GH.popIntTotally(this); //delete me
 		GH.popInt(GH.topInt()); //only deactivate main menu screen
+		GH.totalRedraw();
+		GH.popInt(GH.topInt()); //and pregame background
 		//SEL->current = NULL;
 		//curOpts = NULL;
 		::startGame(si);
@@ -927,6 +965,13 @@ void CSelectionScreen::propagateNames()
 	*serv << &pn;
 }
 
+void CSelectionScreen::showAll(SDL_Surface *to)
+{
+	CIntObject::showAll(to);
+	if (bordered && (pos.h != to->h || pos.w != to->w))
+		CMessage::drawBorder(1, to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);
+}
+
 // A new size filter (Small, Medium, ...) has been selected. Populate
 // selMaps with the relevant data.
 void SelectionTab::filter( int size, bool selectFirst )
@@ -964,7 +1009,6 @@ void SelectionTab::filter( int size, bool selectFirst )
 	}
 }
 
-
 void SelectionTab::getFiles(std::vector<FileInfo> &out, const std::string &dirname, const std::string &ext)
 {
 	CFileUtility::getFilesWithExt(out, dirname, ext);
@@ -1046,20 +1090,15 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 
 	if (Type != CMenuScreen::campaignList)
 	{
-		bg = new CPicture(BitmapHandler::loadBitmap("SCSELBCK.bmp"), 0, 0, true);
-		pos.w = bg->pos.w;
-		pos.h = bg->pos.h;
+		bg = new CPicture("SCSELBCK.bmp", 0, 6);
+		pos = bg->pos;
 	}
 	else
 	{
-		SDL_Surface * tmp1 = BitmapHandler::loadBitmap("CAMCUST.bmp");
-		SDL_Surface * tmp = CSDL_Ext::newSurface(400, tmp1->h);
-		blitAt(tmp1, 0, 0, tmp);
-		SDL_FreeSurface(tmp1);
-		bg = new CPicture(tmp, 0, 0, true);
-		pos.w = bg->pos.w;
-		pos.h = bg->pos.h;
-		bg->pos.x = bg->pos.y = 0;
+		bg = nullptr; //use background from parent
+		pos.w = parent->pos.w;
+		pos.h = parent->pos.h;
+		pos.x += 3; pos.y += 6;
 	}
 
 	if(MultiPlayer == CMenuScreen::MULTI_NETWORK_GUEST)
@@ -1111,9 +1150,9 @@ SelectionTab::SelectionTab(CMenuScreen::EState Type, const boost::function<void(
 				FileInfo fi;
 				fi.inLod = cpm[g].loadFromLod;
 				fi.name = cpm[g].filename;
-				toParse.push_back(fi);
 				if (cpm[g].loadFromLod)
 				{
+					toParse.push_back(fi);
 					allItems.push_back(CMapInfo(false));
 				}
 			}
@@ -1522,6 +1561,7 @@ InfoCard::InfoCard( bool Network )
 {
 	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	pos.x += 393;
+	pos.y += 6;
 	used = RCLICK;
 	mapDescription = NULL;
 
@@ -1535,7 +1575,7 @@ InfoCard::InfoCard( bool Network )
 	}
 	else
 	{
-		bg = new CPicture(BitmapHandler::loadBitmap("GSELPOP1.bmp"), 0, 0, true);
+		bg = new CPicture("GSELPOP1.bmp", 0, 0);
 		CGuiHandler::moveChild(bg, this, parent);
 		auto it = vstd::find(parent->children, this); //our position among parent children
 		parent->children.insert(it, bg); //put BG before us
@@ -1825,7 +1865,7 @@ OptionsTab::OptionsTab():
 	turnDuration(NULL)
 {
 	OBJ_CONSTRUCTION;
-	bg = new CPicture(BitmapHandler::loadBitmap("ADVOPTBK.bmp"), 0, 0, true);
+	bg = new CPicture("ADVOPTBK", 0, 6);
 	pos = bg->pos;
 
 	if(SEL->screenType == CMenuScreen::newGame)
@@ -2715,6 +2755,9 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 	loadPositionsOfGraphics();
 
 	background = BitmapHandler::loadBitmap(bgNames[ourCampaign->camp->header.mapVersion]);
+	pos.h = background->h;
+	pos.w = background->w;
+	center();
 
 	SDL_Surface * panel = BitmapHandler::loadBitmap("CAMPBRF.BMP");
 
@@ -2726,25 +2769,25 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 
 	//campaign name
 	if (ourCampaign->camp->header.name.length())
-		printAtLoc(ourCampaign->camp->header.name, 481, 28, FONT_BIG, Colors::Jasmine, background);
+		CSDL_Ext::printAt(ourCampaign->camp->header.name, 481, 28, FONT_BIG, Colors::Jasmine, background);
 	else
-		printAtLoc("Unnamed", 481, 28, FONT_BIG, Colors::Jasmine, background);
+		CSDL_Ext::printAt("Unnamed", 481, 28, FONT_BIG, Colors::Jasmine, background);
 
 	//map size icon
 	sizes = CDefHandler::giveDef("SCNRMPSZ.DEF");
 
 
 	//campaign description
-	printAtLoc(CGI->generaltexth->allTexts[38], 481, 63, FONT_SMALL, Colors::Jasmine, background);
+	CSDL_Ext::printAt(CGI->generaltexth->allTexts[38], 481, 63, FONT_SMALL, Colors::Jasmine, background);
 
 	cmpgDesc = new CTextBox(ourCampaign->camp->header.description, Rect(480, 86, 286, 117), 1);
-	cmpgDesc->showAll(background);
+	//cmpgDesc->showAll(background);
 
 	//map description
 	mapDesc = new CTextBox("", Rect(480, 280, 286, 117), 1);
 
 	//bonus choosing
-	printAtLoc(CGI->generaltexth->allTexts[71], 511, 432, FONT_MEDIUM, Colors::Cornsilk, background); //Choose a bonus:
+	CSDL_Ext::printAt(CGI->generaltexth->allTexts[71], 511, 432, FONT_MEDIUM, Colors::Cornsilk, background); //Choose a bonus:
 	bonuses = new CHighlightableButtonsGroup(bind(&CBonusSelection::selectBonus, this, _1));
 
 	//set left part of window
@@ -2774,15 +2817,15 @@ CBonusSelection::CBonusSelection( CCampaignState * _ourCampaign )
 	}
 
 	//allies / enemies
-	printAtLoc(CGI->generaltexth->allTexts[390] + ":", 486, 407, FONT_SMALL, Colors::Cornsilk, background); //Allies
-	printAtLoc(CGI->generaltexth->allTexts[391] + ":", 619, 407, FONT_SMALL, Colors::Cornsilk, background); //Enemies
+	CSDL_Ext::printAt(CGI->generaltexth->allTexts[390] + ":", 486, 407, FONT_SMALL, Colors::Cornsilk, background); //Allies
+	CSDL_Ext::printAt(CGI->generaltexth->allTexts[391] + ":", 619, 407, FONT_SMALL, Colors::Cornsilk, background); //Enemies
 
 	SDL_FreeSurface(panel);
 
 	//difficulty
 	std::vector<std::string> difficulty;
 	boost::split(difficulty, CGI->generaltexth->allTexts[492], boost::is_any_of(" "));
-	printAtLoc(difficulty.back(), 689, 432, FONT_MEDIUM, Colors::Cornsilk, background); //Difficulty
+	CSDL_Ext::printAt(difficulty.back(), 689, 432, FONT_MEDIUM, Colors::Cornsilk, background); //Difficulty
 
 	//difficulty pics
 	for (int b=0; b<ARRAY_COUNT(diffPics); ++b)
@@ -2830,6 +2873,8 @@ void CBonusSelection::showAll(SDL_Surface * to)
 	CIntObject::showAll(to);
 
 	show(to);
+	if (pos.h != to->h || pos.w != to->w)
+		CMessage::drawBorder(1, to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);
 }
 
 void CBonusSelection::loadPositionsOfGraphics()
@@ -2935,18 +2980,19 @@ void CBonusSelection::show(SDL_Surface * to)
 	for (std::map<int, PlayerSettings>::const_iterator i = sInfo.playerInfos.begin(); i != sInfo.playerInfos.end(); i++)
 	{
 		int *myx = ((i->first == playerColor  ||  ourHeader->players[i->first].team == myT) ? &fx : &ex);
-		blitAtLoc(sFlags->ourImages[i->first].bitmap, *myx, 405, to);
+		blitAtLoc(sFlags->ourImages[i->first].bitmap, pos.x + *myx, pos.y + 405, to);
 		*myx += sFlags->ourImages[i->first].bitmap->w;
 	}
 
 	//difficulty
-	blitAt(diffPics[sInfo.difficulty], 709, 455, to);
+	blitAtLoc(diffPics[sInfo.difficulty], 709, 455, to);
 
 	CIntObject::show(to);
 }
 
 void CBonusSelection::updateBonusSelection()
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
 	//graphics:
 	//spell - SPELLBON.DEF
 	//monster - TWCRPORT.DEF
@@ -3198,8 +3244,8 @@ CBonusSelection::CRegion::CRegion( CBonusSelection * _owner, bool _accessible, b
 
 	const SCampPositions & campDsc = owner->campDescriptions[owner->ourCampaign->camp->header.mapVersion];
 	const SCampPositions::SRegionDesc & desc = campDsc.regions[myNumber];
-	pos.x = desc.xpos;
-	pos.y = desc.ypos;
+	pos.x += desc.xpos;
+	pos.y += desc.ypos;
 
 	//loading of graphics
 
@@ -3255,17 +3301,17 @@ void CBonusSelection::CRegion::show(SDL_Surface * to)
 	if (!accessible)
 	{
 		//show as striped
-		blitAt(graphics[2], pos.x, pos.y, to);
+		blitAtLoc(graphics[2], 0, 0, to);
 	}
 	else if (this == owner->highlightedRegion)
 	{
 		//show as selected
-		blitAt(graphics[1], pos.x, pos.y, to);
+		blitAtLoc(graphics[1], 0, 0, to);
 	}
 	else
 	{
 		//show as not selected selected
-		blitAt(graphics[0], pos.x, pos.y, to);
+		blitAtLoc(graphics[0], 0, 0, to);
 	}
 }
 
@@ -3573,6 +3619,14 @@ CCampaignScreen::CCampaignScreen(const JsonNode &config)
 	BOOST_FOREACH(const JsonNode& node, config["images"].Vector())
 		images.push_back(createPicture(node));
 
+	if (!images.empty())
+	{
+		images[0]->center();              // move background to center
+		moveTo(images[0]->pos.topLeft()); // move everything else to center
+		images[0]->moveTo(pos.topLeft()); // restore moved twice background
+		pos = images[0]->pos;             // fix height\width of this window
+	}
+
 	if (!config["exitbutton"].isNull())
 	{
 		back = createExitButton(config["exitbutton"]);
@@ -3582,3 +3636,10 @@ CCampaignScreen::CCampaignScreen(const JsonNode &config)
 	BOOST_FOREACH(const JsonNode& node, config["items"].Vector())
 		campButtons.push_back(new CCampaignButton(node));
 }
+
+void CCampaignScreen::showAll(SDL_Surface *to)
+{
+	CIntObject::showAll(to);
+	if (pos.h != to->h || pos.w != to->w)
+		CMessage::drawBorder(1, to, pos.w+28, pos.h+30, pos.x-14, pos.y-15);
+}

+ 14 - 5
client/CPreGame.h

@@ -66,6 +66,7 @@ public:
 	};
 	CMenuScreen(const JsonNode& configNode);
 
+	void showAll(SDL_Surface * to);
 	void show(SDL_Surface * to);
 	void activate();
 	void deactivate();
@@ -90,6 +91,7 @@ public:
 	CreditsScreen();
 
 	void show(SDL_Surface * to);
+	void showAll(SDL_Surface * to);
 
 	void clickLeft(tribool down, bool previousState);
 	void clickRight(tribool down, bool previousState);
@@ -274,6 +276,7 @@ public:
 /// The actual map selection screen which consists of the options and selection tab
 class CSelectionScreen : public CIntObject, public ISelectionScreenInfo
 {
+	bool bordered;
 public:
 	CPicture *bg; //general bg image
 	InfoCard *card;
@@ -308,6 +311,7 @@ public:
 	void postRequest(ui8 what, ui8 dir) OVERRIDE;
 	void postChatMessage(const std::string &txt) OVERRIDE;
 	void propagateNames();
+	void showAll(SDL_Surface *to);
 };
 
 /// Save game screen
@@ -479,30 +483,35 @@ public:
 	enum CampaignSet {ROE, AB, SOD, WOG};
 
 	CCampaignScreen(const JsonNode &config);
+	void showAll(SDL_Surface *to);
 };
 
 /// Handles background screen, loads graphics for victory/loss condition and random town or hero selection
 class CGPreGame : public CIntObject, public IUpdateable
 {
 	const JsonNode * const pregameConfig;
+
+	void loadGraphics();
+	void disposeGraphics();
+
+	CGPreGame(); //Use createIfNotPresent
+
 public:
 	CMenuScreen* menu;
 
+	SDL_Surface *background;
 	SDL_Surface *nHero, *rHero, *nTown, *rTown; // none/random hero/town imgs
 	CDefHandler *bonuses;
 	CDefHandler *victory, *loss;
 
-	CGPreGame();
 	~CGPreGame();
 	void update();
 	void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER);
 
-	void loadGraphics();
-	void disposeGraphics();
-
+	void showAll(SDL_Surface *to);
 	void openCampaignScreen(std::string name);
 
-	static void createIfNotPresent();
+	static CGPreGame * create();
 };
 
 extern ISelectionScreenInfo *SEL;

+ 9 - 9
client/CSoundBase.h

@@ -476,15 +476,15 @@ VCMI_SOUND_NAME(horseLava) VCMI_SOUND_FILE(HORSE07.wav) \
 VCMI_SOUND_NAME(horseWater) VCMI_SOUND_FILE(HORSE08.wav) \
 VCMI_SOUND_NAME(horseRock) VCMI_SOUND_FILE(HORSE09.wav) \
 VCMI_SOUND_NAME(horseFly) VCMI_SOUND_FILE(HORSE10.wav) \
-VCMI_SOUND_NAME(horse20) VCMI_SOUND_FILE(HORSE20.wav) \
-VCMI_SOUND_NAME(horse21) VCMI_SOUND_FILE(HORSE21.wav) \
-VCMI_SOUND_NAME(horse22) VCMI_SOUND_FILE(HORSE22.wav) \
-VCMI_SOUND_NAME(horse23) VCMI_SOUND_FILE(HORSE23.wav) \
-VCMI_SOUND_NAME(horse24) VCMI_SOUND_FILE(HORSE24.wav) \
-VCMI_SOUND_NAME(horse25) VCMI_SOUND_FILE(HORSE25.wav) \
-VCMI_SOUND_NAME(horse26) VCMI_SOUND_FILE(HORSE26.wav) \
-VCMI_SOUND_NAME(horse27) VCMI_SOUND_FILE(HORSE27.wav) \
-VCMI_SOUND_NAME(horse29) VCMI_SOUND_FILE(HORSE29.wav) \
+VCMI_SOUND_NAME(horsePenaltyDirt) VCMI_SOUND_FILE(HORSE20.wav) \
+VCMI_SOUND_NAME(horsePenaltySand) VCMI_SOUND_FILE(HORSE21.wav) \
+VCMI_SOUND_NAME(horsePenaltyGrass) VCMI_SOUND_FILE(HORSE22.wav) \
+VCMI_SOUND_NAME(horsePenaltySnow) VCMI_SOUND_FILE(HORSE23.wav) \
+VCMI_SOUND_NAME(horsePenaltySwamp) VCMI_SOUND_FILE(HORSE24.wav) \
+VCMI_SOUND_NAME(horsePenaltyRough) VCMI_SOUND_FILE(HORSE25.wav) \
+VCMI_SOUND_NAME(horsePenaltySubterranean) VCMI_SOUND_FILE(HORSE26.wav) \
+VCMI_SOUND_NAME(horsePenaltyLava) VCMI_SOUND_FILE(HORSE27.wav) \
+VCMI_SOUND_NAME(horsePenaltyRock) VCMI_SOUND_FILE(HORSE29.wav) \
 VCMI_SOUND_NAME(hydraAttack) VCMI_SOUND_FILE(HYDRATTK.wav) \
 VCMI_SOUND_NAME(hydraDefend) VCMI_SOUND_FILE(HYDRDFND.wav) \
 VCMI_SOUND_NAME(hydraKill) VCMI_SOUND_FILE(HYDRKILL.wav) \

+ 1 - 3
client/FontBase.h

@@ -15,12 +15,11 @@ enum EFonts
 	FONT_BIG, FONT_CALLI, FONT_CREDITS, FONT_HIGH_SCORE, FONT_MEDIUM, FONT_SMALL, FONT_TIMES, FONT_TINY, FONT_VERD
 };
 
-
 struct Font
 {
 	struct Char
 	{
-		si32 unknown1, width, unknown2, offset;
+		si32 leftOffset, width, rightOffset;
 		ui8 *pixels;
 	};
 
@@ -29,7 +28,6 @@ struct Font
 
 	ui8 *data;
 
-
 	Font(ui8 *Data);
 	~Font();
 	int getWidth(const char *text) const;

+ 5 - 7
client/GUIClasses.cpp

@@ -3720,13 +3720,11 @@ CSystemOptionsWindow::CSystemOptionsWindow(const SDL_Rect &Pos, CPlayerInterface
 	newCreatureWin->select(settings["general"]["classicCreatureWindow"].Bool());
 	fullscreen->select(settings["video"]["fullscreen"].Bool());
 
-	gameResButton = new CAdventureMapButton("", rsHelp, boost::bind(&CSystemOptionsWindow::selectGameRes, this, false), 28, 275,"SYSOB12", SDLK_g);
+	gameResButton = new CAdventureMapButton("", rsHelp, boost::bind(&CSystemOptionsWindow::selectGameRes, this), 28, 275,"SYSOB12", SDLK_g);
 }
 
-void CSystemOptionsWindow::selectGameRes(bool pregame)
+void CSystemOptionsWindow::selectGameRes()
 {
-	assert(pregame == false);//TODO
-
 	//TODO: translation and\or config file
 	static const std::string rsLabel = "Select resolution";
 	static const std::string rsHelp = "Change in-game screen resolution.";
@@ -3741,16 +3739,16 @@ void CSystemOptionsWindow::selectGameRes(bool pregame)
 	}
 
 	GH.pushInt(new CObjectListWindow(items, NULL, rsLabel, rsHelp,
-	           boost::bind(&CSystemOptionsWindow::setGameRes, this, pregame, _1)));
+			   boost::bind(&CSystemOptionsWindow::setGameRes, this, _1)));
 }
 
-void CSystemOptionsWindow::setGameRes(bool pregame, int index)
+void CSystemOptionsWindow::setGameRes(int index)
 {
 	config::CConfigHandler::GuiOptionsMap::const_iterator iter = conf.guiOptions.begin();
 	while (index--)
 		iter++;
 
-	Settings gameRes = settings.write["video"]["gameRes"];
+	Settings gameRes = settings.write["video"]["screenRes"];
 	gameRes["width"].Float() = iter->first.first;
 	gameRes["height"].Float() = iter->first.second;
 }

+ 2 - 2
client/GUIClasses.h

@@ -689,8 +689,8 @@ private:
 	void toggleCreatureWin(bool on);
 	void toggleFullscreen(bool on);
 
-	void selectGameRes(bool pregame);
-	void setGameRes(bool pregame, int index);
+	void selectGameRes();
+	void setGameRes(int index);
 
 	void pushSDLEvent(int type, int usercode);
 

+ 6 - 6
client/Graphics.cpp

@@ -730,17 +730,17 @@ Font::Font(ui8 *Data)
 	i = 32;
 	for(int ci = 0; ci < 256; ci++)
 	{
-		chars[ci].unknown1 = read_le_u32(data + i); i+=4;
+		chars[ci].leftOffset = read_le_u32(data + i); i+=4;
 		chars[ci].width = read_le_u32(data + i); i+=4;
-		chars[ci].unknown2 = read_le_u32(data + i); i+=4;
+		chars[ci].rightOffset = read_le_u32(data + i); i+=4;
 
 		//if(ci>=30)
 		//	tlog0 << ci << ". (" << (char)ci << "). Width: " << chars[ci].width << " U1/U2:" << chars[ci].unknown1 << "/" << chars[ci].unknown2 << std::endl;
 	}
 	for(int ci = 0; ci < 256; ci++)
 	{
-		chars[ci].offset =  read_le_u32(data + i); i+=4;
-		chars[ci].pixels = data + 4128 + chars[ci].offset;
+		int offset =  read_le_u32(data + i); i+=4;
+		chars[ci].pixels = data + 4128 + offset;
 	}
 }
 
@@ -757,7 +757,7 @@ int Font::getWidth(const char *text ) const
 	for(int i = 0; i < length; i++)
 	{
 		ui8 c = text[i];
-		ret += chars[c].width + chars[c].unknown1 + chars[c].unknown2;
+		ret += chars[c].width + chars[c].leftOffset + chars[c].rightOffset;
 	}
 
 	return ret;
@@ -766,7 +766,7 @@ int Font::getWidth(const char *text ) const
 int Font::getCharWidth( char c ) const
 {
 	const Char &C = chars[(ui8)c];
-	return C.width + C.unknown1 + C.unknown2;;
+	return C.width + C.leftOffset + C.rightOffset;;
 }
 
 /*

+ 52 - 30
client/UIFramework/SDL_Extensions.cpp

@@ -35,6 +35,19 @@ SDL_Surface * CSDL_Ext::copySurface(SDL_Surface * mod) //returns copy of given s
 	return SDL_ConvertSurface(mod, mod->format, mod->flags);
 }
 
+template<int bpp>
+SDL_Surface * CSDL_Ext::createSurfaceWithBpp(int width, int height)
+{
+	int rMask = 0, gMask = 0, bMask = 0, aMask = 0;
+
+	Channels::px<bpp>::r.set((Uint8*)&rMask, 255);
+	Channels::px<bpp>::g.set((Uint8*)&gMask, 255);
+	Channels::px<bpp>::b.set((Uint8*)&bMask, 255);
+	Channels::px<bpp>::a.set((Uint8*)&aMask, 255);
+
+	return SDL_CreateRGBSurface( SDL_SWSURFACE | SDL_SRCALPHA, width, height, bpp * 8, rMask, gMask, bMask, aMask);
+}
+
 bool isItIn(const SDL_Rect * rect, int x, int y)
 {
 	return (x>rect->x && x<rect->x+rect->w) && (y>rect->y && y<rect->y+rect->h);
@@ -230,68 +243,74 @@ void printAt(const std::string & text, int x, int y, TTF_Font * font, SDL_Color
 	SDL_FreeSurface(temp);
 }
 
-
-
-void CSDL_Ext::printAt( const std::string & text, int x, int y, EFonts font, SDL_Color kolor/*=Colors::Cornsilk*/, SDL_Surface * dst/*=screen*/ )
+void CSDL_Ext::printAt( const std::string & text, int dstX, int dstY, EFonts font, SDL_Color color, SDL_Surface * dst)
 {
 	if(!text.size())
 		return;
 
 	if (graphics->fontsTrueType[font])
 	{
-		printAt(text,x, y, graphics->fontsTrueType[font], kolor, dst);
+		printAt(text,dstX, dstY, graphics->fontsTrueType[font], color, dst);
 		return;
 	}
 
 	assert(dst);
 	assert(font < Graphics::FONTS_NUMBER);
 
-	//assume BGR dst surface, TODO - make it general in a tidy way
-	assert(dst->format->Rshift > dst->format->Gshift);
-	assert(dst->format->Gshift > dst->format->Bshift);
+	Rect clipRect;
+	SDL_GetClipRect(dst, &clipRect);
 
 	const Font *f = graphics->fonts[font];
 	const Uint8 bpp = dst->format->BytesPerPixel;
-	Uint8 *px = NULL;
-	Uint8 *src = NULL;
-	TColorPutter colorPutter = getPutterFor(dst, false);
 
+	TColorPutter colorPutter = getPutterFor(dst, 0);
 
 	//if text is in {} braces, we'll ommit them
-	const int first = (text[0] == '{' ? 1 : 0);
-	const int beyondEnd = (text[text.size()-1] == '}' ? text.size()-1 : text.size());
+	const int textBegin = (text[0] == '{' ? 1 : 0);
+	const int textEnd = (text[text.size()-1] == '}' ? text.size()-1 : text.size());
 
-	for(int txti = first; txti < beyondEnd; txti++)
+	SDL_LockSurface(dst);
+	// for each symbol
+	for(int index = textBegin; index < textEnd; index++)
 	{
-		const ui8 c = text[txti];
-		x += f->chars[c].unknown1;
+		const ui8 symbol = text[index];
+		dstX += f->chars[symbol].leftOffset;
+
+		int lineBegin = std::max<int>(0, clipRect.y - dstY);
+		int lineEnd   = std::min<int>(f->height, clipRect.y + clipRect.h - dstY - 1);
 
-		for(int i = std::max(0, -y); i < f->height  &&  (y + i) < (dst->h - 1); i++)
+		//for each line in symbol
+		for(int dy = lineBegin; dy <lineEnd; dy++)
 		{
-			px = (Uint8*)dst->pixels;
-			px +=  (y+i) * dst->pitch  +  x * bpp;
-	 		src = f->chars[c].pixels;
-			src += i * f->chars[c].width;//if we have reached end of surface in previous line
+			Uint8 *dstLine = (Uint8*)dst->pixels;
+			Uint8 *srcLine = f->chars[symbol].pixels;
+
+			dstLine += (dstY+dy) * dst->pitch + dstX * bpp;
+			srcLine += dy * f->chars[symbol].width;
+
+			int rowBegin = std::max(0, clipRect.x - dstX);
+			int rowEnd   = std::min(f->chars[symbol].width, clipRect.x + clipRect.w - dstX - 1);
 
-			for(int j = std::max(0, -x); j < f->chars[c].width  &&  (j + x) < (dst->w - 1); j++)
+			//for each column in line
+			for(int dx = rowBegin; dx < rowEnd; dx++)
 			{
-				switch(*src)
+				Uint8* dstPixel = dstLine + dx*bpp;
+				switch(*(srcLine + dx))
 				{
 				case 1: //black "shadow"
-					memset(px, 0, bpp);
+					memset(dstPixel, 0, bpp);
 					break;
 				case 255: //text colour
-					colorPutter(px, kolor.r, kolor.g, kolor.b);
+					colorPutter(dstPixel, color.r, color.g, color.b);
 					break;
 				}
-				src++;
-				px += bpp;
 			}
 		}
 
-		x += f->chars[c].width;
-		x += f->chars[c].unknown2;
+		dstX += f->chars[symbol].width;
+		dstX += f->chars[symbol].rightOffset;
 	}
+	SDL_UnlockSurface(dst);
 }
 
 void printTo(const std::string & text, int x, int y, TTF_Font * font, SDL_Color kolor, SDL_Surface * dst, ui8 quality=2)
@@ -483,9 +502,10 @@ Uint32 CSDL_Ext::SDL_GetPixel(SDL_Surface *surface, const int & x, const int & y
 
 void CSDL_Ext::alphaTransform(SDL_Surface *src)
 {
+	//NOTE: colors #7 & #8 used in some of WoG objects. Don't know how they're handled by H3
 	assert(src->format->BitsPerPixel == 8);
 	SDL_Color colors[] = {{0,0,0,255}, {0,0,0,214}, {0,0,0,164}, {0,0,0,82}, {0,0,0,128},
-						{255,0,0,0}, {255,0,0,0}, {255,0,0,0}, {0,0,0,192}, {0,0,0,192}};
+						{255,255,255,0}, {255,255,255,0}, {255,255,255,0}, {0,0,0,192}, {0,0,0,192}};
 
 	SDL_SetColors(src, colors, 0, ARRAY_COUNT(colors));
 	SDL_SetColorKey(src, SDL_SRCCOLORKEY, SDL_MapRGBA(src->format, 0, 0, 0, 255));
@@ -1135,4 +1155,6 @@ std::string CSDL_Ext::trimToFit(std::string text, int widthLimit, EFonts font)
 	return text;
 }
 
-SDL_Surface * CSDL_Ext::std32bppSurface = NULL;
+template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<2>(int, int);
+template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<3>(int, int);
+template SDL_Surface * CSDL_Ext::createSurfaceWithBpp<4>(int, int);

+ 2 - 1
client/UIFramework/SDL_Extensions.h

@@ -117,7 +117,6 @@ namespace CSDL_Ext
 {
 	void blitSurface(SDL_Surface * src, SDL_Rect * srcRect, SDL_Surface * dst, SDL_Rect * dstRect);
 	void fillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color);
-	extern SDL_Surface * std32bppSurface;
 
 	void SDL_PutPixelWithoutRefresh(SDL_Surface *ekran, const int & x, const int & y, const Uint8 & R, const Uint8 & G, const Uint8 & B, Uint8 A = 255);
 	void SDL_PutPixelWithoutRefreshIfInSurf(SDL_Surface *ekran, const int & x, const int & y, const Uint8 & R, const Uint8 & G, const Uint8 & B, Uint8 A = 255);
@@ -171,6 +170,8 @@ namespace CSDL_Ext
 	std::string processStr(std::string str, std::vector<std::string> & tor); //replaces %s in string
 	SDL_Surface * newSurface(int w, int h, SDL_Surface * mod=screen); //creates new surface, with flags/format same as in surface given
 	SDL_Surface * copySurface(SDL_Surface * mod); //returns copy of given surface
+	template<int bpp>
+	SDL_Surface * createSurfaceWithBpp(int width, int height); //create surface with give bits per pixels value
 	void VflipSurf(SDL_Surface * surf); //fluipis given surface by vertical axis
 
 	template<int bpp>

+ 9 - 11
client/UIFramework/SDL_Pixels.h

@@ -195,18 +195,16 @@ STRONG_INLINE void ColorPutter<bpp, incrementPtr>::PutColor(Uint8 *&ptr, const U
 template<int bpp, int incrementPtr>
 STRONG_INLINE void ColorPutter<bpp, incrementPtr>::PutColorRow(Uint8 *&ptr, const SDL_Color & Color, size_t count)
 {
-	static_assert(incrementPtr >= -1 && incrementPtr <= +1, "Invalid incrementPtr value!");
-
-	Uint8 pixel[bpp];
-	Channels::px<bpp>::r.set(pixel, Color.r);
-	Channels::px<bpp>::g.set(pixel, Color.g);
-	Channels::px<bpp>::b.set(pixel, Color.b);
-	Channels::px<bpp>::a.set(pixel, 0);
-
-	for (size_t i=0; i<count; i++)
+	if (count)
 	{
-		memcpy(ptr, pixel, bpp);
-		ptr += bpp * incrementPtr;
+		Uint8 *pixel = ptr;
+		PutColor(ptr, Color.r, Color.g, Color.b);
+
+		for (size_t i=0; i<count-1; i++)
+		{
+			memcpy(ptr, pixel, bpp);
+			ptr += bpp * incrementPtr;
+		}
 	}
 }
 

+ 1 - 17
config/defaultSettings.json

@@ -30,29 +30,13 @@
 	"video" : {
 		"type" : "object",
 		"properties" : {
-			"gameRes" : {
-				"type" : "object",
-				"properties" : {
-					"width"  : { "type" : "number" },
-					"height" : { "type" : "number" }
-				},
-				"default": {"width" : 800, "height": 600 }
-			},
-			"menuRes" : {
-				"type" : "object",
-				"properties" : {
-					"width"  : { "type" : "number" },
-					"height" : { "type" : "number" }
-				},
-				"default": {"width" : 800, "height": 600 }
-			},
 			"screenRes" : {
 				"type" : "object",
 				"properties" : {
 					"width"  : { "type" : "number" },
 					"height" : { "type" : "number" }
 				},
-				"default": {"width" : 0, "height": 0 }
+				"default": {"width" : 800, "height": 600 }
 			},
 			"bitsPerPixel" : {
 				"type" : "number",

+ 186 - 20
config/sp_sounds.json

@@ -4,6 +4,76 @@
 {
 	"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",
@@ -19,6 +89,16 @@
 			"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",
@@ -29,6 +109,11 @@
 			"soundfile": "FIREBALL.wav",
 			"name": "fireball"
 		},
+		{
+			"id": 22 ,
+			"name":"Inferno",
+			"soundfile":"FIREBLST.wav"
+		},
 		{
 			"id": 23,
 			"soundfile": "METEOR.wav",
@@ -39,6 +124,11 @@
 			"soundfile": "DEATHRIP.wav",
 			"name": "death ripple"
 		},
+		{
+			"id": 25 ,
+			"name":"Destroy undead",
+			"soundfile":"COLDRING.wav"
+		},
 		{
 			"id": 26,
 			"soundfile": "ARMGEDN.wav",
@@ -54,6 +144,17 @@
 			"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",
@@ -83,10 +184,30 @@
 			"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",
@@ -167,6 +288,21 @@
 			"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",
@@ -181,10 +317,45 @@
 			"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 - not sure"
+			"name": "stone gaze and paralyze"
 		},
 		{
 			"id": 71,
@@ -209,11 +380,11 @@
 		{
 			"id": 75,
 			"soundfile": "AGE.wav",
-			"name": "aging - already used (?)"
+			"name": "aging"
 		},
 
 		{
-			"id": 6,
+			"id": 76,
 			"soundfile": "DEATHCLD.wav",
 			"name": "death cloud"
 		},
@@ -240,20 +411,15 @@
 	]
 }
 
-// Missing:
-// 18 	# implosion
-// 22 	# inferno
-// 25	# destroy undead
-// BLIND.wav
-// CURE.wav
-// HYPNOTIZ.wav
-// DEATHBLO.wav
-// DRAINLIF.wav
-// DRGNSLAY.wav
-// DISGUISE.wav
-// QUIKSAND.wav
-// FIRESHIE.wav fireshield when cast
-// FIRESHLD.wav fireshield effect
-// ANIMDEAD.wav
-// ANTIMAGK.wav
-
+// Several probably missing sounds of creature abilities
+// ACID.WAV        Acid breath    Rust Dragons
+// DEATHBLO.WAV    Deathblow      Death knigts
+// DRAINLIF.WAV    Drain life     Vampires
+// FEAR.WAV        Fear           Azure dragons
+// MAGCHDRN.WAV    Steal mana     Imps
+// MAGCHFIL.WAV    Steal mana     Either for upgrades or for receiving mana by hero
+// MAGICRES.WAV    Magic resist   Dwarves
+// MANADRAI.WAV    Mana drain     Ghosts
+// REGENER.WAV     Regeneration   Ghosts, Trolls
+// RESURECT.WAV    Resurrection   Both archangels and Demons
+// SPONTCOMB.WAV   Fireball       Magogs

+ 1 - 1
lib/CObjectHandler.cpp

@@ -4844,7 +4844,7 @@ void CGBonusingObject::onHeroVisit( const CGHeroInstance * h ) const
 		bonusMove = 400;
 		break;
 	case 94: //Stables
-		sound = soundBase::horse20;
+		sound = soundBase::STORE;
 		bool someUpgradeDone = false;
 
 		for (TSlots::const_iterator i = h->Slots().begin(); i != h->Slots().end(); ++i)

+ 2 - 1
lib/CSpellHandler.h

@@ -57,7 +57,8 @@ namespace Spells
 {
 	enum
 	{
-		SUMMON_BOAT=0, SCUTTLE_BOAT=1, VISIONS=2, VIEW_EARTH=3, DISGUISE=4, VIEW_AIR=5, FLY=6, WATER_WALK=7, DIMENSION_DOOR=8, TOWN_PORTAL=9,
+		SUMMON_BOAT=0, SCUTTLE_BOAT=1, VISIONS=2, VIEW_EARTH=3, DISGUISE=4, VIEW_AIR=5,
+		FLY=6, WATER_WALK=7, DIMENSION_DOOR=8, TOWN_PORTAL=9,
 
 		QUICKSAND=10, LAND_MINE=11, FORCE_FIELD=12, FIRE_WALL=13, EARTHQUAKE=14,
 		MAGIC_ARROW=15, ICE_BOLT=16, LIGHTNING_BOLT=17, IMPLOSION=18,