Răsfoiți Sursa

- merged minimap.json into terrains.json
- removed no longer used fields from Graphics + portraits.json
- work on pregame:
- - new code for map options window icons + popups
- - fixed bugs related to new towns
- - less hardcoded magic numbers

Ivan Savenko 13 ani în urmă
părinte
comite
1c5a4c669c

+ 3 - 1
Mods/vcmi/Sprites/PortraitsSmall.json

@@ -163,6 +163,8 @@
 		{ "frame" : 159, "file" : "HPS133Nc.bmp"},
 		{ "frame" : 160, "file" : "HPS134Nc.bmp"},
 		{ "frame" : 161, "file" : "HPS135Wi.bmp"},
-		{ "frame" : 162, "file" : "HPS136Wi.bmp"}
+		{ "frame" : 162, "file" : "HPS136Wi.bmp"},
+		{ "frame" : 200, "file" : "HPSRAND1.bmp"}, //random hero
+		{ "frame" : 201, "file" : "HPSRAND6.bmp"} //no hero
 	]
 }

+ 7 - 0
Mods/vcmi/Sprites/itpa.json

@@ -0,0 +1,7 @@
+{
+	"images" :
+	[
+		{ "frame" : 38, "file" : "HPSRAND0.bmp"},
+		{ "frame" : 39, "file" : "HPSRAND5.bmp"}
+	]
+}

+ 12 - 6
client/AdventureMapClasses.cpp

@@ -479,11 +479,17 @@ std::map<int, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors(std::string
 
 	const JsonNode config(ResourceID(from, EResType::TEXT));
 
-	BOOST_FOREACH(const JsonNode &m, config["MinimapColors"].Vector())
+	BOOST_FOREACH(auto &m, config.Struct())
 	{
-		int id = m["terrain_id"].Float();
+		auto index = boost::find(GameConstants::TERRAIN_NAMES, m.first);
+		if (index == boost::end(GameConstants::TERRAIN_NAMES))
+		{
+			tlog1 << "Error: unknown terrain in terrains.json: " << m.first << "\n";
+			continue;
+		}
+		int terrainID = index - boost::begin(GameConstants::TERRAIN_NAMES);
 
-		const JsonVector &unblockedVec = m["unblocked"].Vector();
+		const JsonVector &unblockedVec = m.second["minimapUnblocked"].Vector();
 		SDL_Color normal =
 		{
 			ui8(unblockedVec[0].Float()),
@@ -492,7 +498,7 @@ std::map<int, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors(std::string
 			ui8(255)
 		};
 
-		const JsonVector &blockedVec = m["blocked"].Vector();
+		const JsonVector &blockedVec = m.second["minimapBlocked"].Vector();
 		SDL_Color blocked =
 		{
 			ui8(blockedVec[0].Float()),
@@ -501,7 +507,7 @@ std::map<int, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors(std::string
 			ui8(255)
 		};
 
-		ret.insert(std::make_pair(id, std::make_pair(normal, blocked)));
+		ret.insert(std::make_pair(terrainID, std::make_pair(normal, blocked)));
 	}
 	return ret;
 }
@@ -511,7 +517,7 @@ CMinimap::CMinimap(const Rect &position):
     aiShield(nullptr),
     minimap(nullptr),
     level(0),
-    colors(loadColors("config/minimap.json"))
+    colors(loadColors("config/terrains.json"))
 {
 	pos.w = position.w;
 	pos.h = position.h;

+ 0 - 1
client/CGameInfo.h

@@ -67,7 +67,6 @@ public:
 	void setFromLib();
 
 	friend class CClient;
-	friend void initVillagesCapitols(CMap * map);
 
 	CGameInfo();
 };

+ 274 - 261
client/CPreGame.cpp

@@ -173,11 +173,19 @@ void updateStartInfo(std::string filename, StartInfo & sInfo, const CMapHeader *
 		pset.hero = pinfo.defaultHero();
 
 
-		if(pinfo.mainHeroName.length())
+		if(pinfo.customHeroID >= 0)
 		{
-			pset.heroName = pinfo.mainHeroName;
-			if((pset.heroPortrait = pinfo.mainHeroPortrait) == 255)
-				pset.heroPortrait = pinfo.p9;
+			pset.hero = pinfo.customHeroID;
+
+			if (!pinfo.mainHeroName.empty())
+				pset.heroName = pinfo.mainHeroName;
+			else
+				pset.heroName = CGI->heroh->heroes[pinfo.customHeroID]->name;
+
+			if (pinfo.mainHeroPortrait >= 0)
+				pset.heroPortrait = pinfo.mainHeroPortrait;
+			else
+				pset.heroPortrait = pinfo.customHeroID;
 		}
 		pset.handicap = 0;
 	}
@@ -470,21 +478,12 @@ void CGPreGame::loadGraphics()
 
 	victory = CDefHandler::giveDef("SCNRVICT.DEF");
 	loss = CDefHandler::giveDef("SCNRLOSS.DEF");
-	bonuses = CDefHandler::giveDef("SCNRSTAR.DEF");
-	rHero = BitmapHandler::loadBitmap("HPSRAND1.bmp");
-	rTown = BitmapHandler::loadBitmap("HPSRAND0.bmp");
-	nHero = BitmapHandler::loadBitmap("HPSRAND6.bmp");
-	nTown = BitmapHandler::loadBitmap("HPSRAND5.bmp");
 }
 
 void CGPreGame::disposeGraphics()
 {
 	delete victory;
 	delete loss;
-	SDL_FreeSurface(rHero);
-	SDL_FreeSurface(nHero);
-	SDL_FreeSurface(rTown);
-	SDL_FreeSurface(nTown);
 }
 
 void CGPreGame::update()
@@ -2247,10 +2246,10 @@ void OptionsTab::nextCastle( int player, int dir )
 	si16 &cur = s.castle;
 	auto & allowed = SEL->current->mapHeader->players[s.color].allowedFactions;
 
-	if (cur == -2) //no castle - no change
+	if (cur == PlayerSettings::NONE) //no change
 		return;
 
-	if (cur == -1) //random => first/last available
+	if (cur == PlayerSettings::RANDOM) //first/last available
 	{
 		if (dir > 0)
 			cur = *allowed.begin(); //id of first town
@@ -2272,14 +2271,15 @@ void OptionsTab::nextCastle( int player, int dir )
 		}
 	}
 
-	if(s.hero >= 0)
-		s.hero = -1;
+	if(s.hero >= 0 && SEL->current->mapHeader->players[s.color].customHeroID < 0) // remove hero unless it set to fixed one in map editor
+		s.hero =  PlayerSettings::RANDOM;
 	if(cur < 0  &&  s.bonus == PlayerSettings::RESOURCE)
 		s.bonus = PlayerSettings::RANDOM;
 
 	entries[player]->selectButtons();
 
 	SEL->propagateOptions();
+	entries[player]->update();
 	redraw();
 }
 
@@ -2296,7 +2296,7 @@ void OptionsTab::nextHero( int player, int dir )
 	if (s.castle < 0  ||  s.playerID == PlayerSettings::PLAYER_AI  ||  s.hero == PlayerSettings::NONE)
 		return;
 
-	if (s.hero == -1) //random => first/last available
+	if (s.hero == PlayerSettings::RANDOM) // first/last available
 	{
 		int max = (s.castle*GameConstants::HEROES_PER_TYPE*2+15),
 			min = (s.castle*GameConstants::HEROES_PER_TYPE*2);
@@ -2314,9 +2314,9 @@ void OptionsTab::nextHero( int player, int dir )
 	{
 		usedHeroes.erase(old);
 		usedHeroes.insert(s.hero);
+		entries[player]->update();
 		redraw();
 	}
-
 	SEL->propagateOptions();
 }
 
@@ -2339,10 +2339,6 @@ int OptionsTab::nextAllowedHero( int min, int max, int incl, int dir )
 
 bool OptionsTab::canUseThisHero( int ID )
 {
-	//for(int i=0;i<CPG->ret.playerInfos.size();i++)
-	//	if(CPG->ret.playerInfos[i].hero==ID) //hero is already taken
-	//		return false;
-
 	return CGI->heroh->heroes.size() > ID
 		&& !vstd::contains(usedHeroes, ID)
 		&& SEL->current->mapHeader->allowedHeroes[ID];
@@ -2359,7 +2355,9 @@ void OptionsTab::nextBonus( int player, int dir )
 	PlayerSettings &s = SEL->sInfo.playerInfos[player];
 	si8 &ret = s.bonus += dir;
 
-	if (s.hero==-2 && !SEL->current->mapHeader->players[s.color].heroesNames.size() && ret==PlayerSettings::ARTIFACT) //no hero - can't be artifact
+	if (s.hero==PlayerSettings::NONE &&
+		!SEL->current->mapHeader->players[s.color].heroesNames.size() &&
+		ret==PlayerSettings::ARTIFACT) //no hero - can't be artifact
 	{
 		if (dir<0)
 			ret=PlayerSettings::RANDOM;
@@ -2371,7 +2369,7 @@ void OptionsTab::nextBonus( int player, int dir )
 	if(ret < PlayerSettings::RANDOM)
 		ret = PlayerSettings::RESOURCE;
 
-	if (s.castle==-1 && ret==PlayerSettings::RESOURCE) //random castle - can't be resource
+	if (s.castle==PlayerSettings::RANDOM && ret==PlayerSettings::RESOURCE) //random castle - can't be resource
 	{
 		if (dir<0)
 			ret=PlayerSettings::GOLD;
@@ -2379,6 +2377,7 @@ void OptionsTab::nextBonus( int player, int dir )
 	}
 
 	SEL->propagateOptions();
+	entries[player]->update();
 	redraw();
 }
 
@@ -2549,15 +2548,9 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry( OptionsTab *owner, PlayerSet
 	else
 		flag = NULL;
 
-	town = new SelectedBox(TOWN, s.color);
-	town->pos.x += 119;
-	town->pos.y += 2;
-	hero = new SelectedBox(HERO, s.color);
-	hero->pos.x += 195;
-	hero->pos.y += 2;
-	bonus = new SelectedBox(BONUS, s.color);
-	bonus->pos.x += 271;
-	bonus->pos.y += 2;
+	town = new SelectedBox(Point(119, 2), s, TOWN);
+	hero = new SelectedBox(Point(195, 2), s, HERO);
+	bonus = new SelectedBox(Point(271, 2), s, BONUS);
 }
 
 void OptionsTab::PlayerOptionsEntry::showAll(SDL_Surface * to)
@@ -2567,6 +2560,13 @@ void OptionsTab::PlayerOptionsEntry::showAll(SDL_Surface * to)
 	printAtMiddleWBLoc(CGI->generaltexth->arraytxt[206+whoCanPlay], 28, 34, FONT_TINY, 8, Colors::WHITE, to);
 }
 
+void OptionsTab::PlayerOptionsEntry::update()
+{
+	town->update();
+	hero->update();
+	bonus->update();
+}
+
 void OptionsTab::PlayerOptionsEntry::selectButtons()
 {
 	if(!btns[0])
@@ -2608,299 +2608,312 @@ void OptionsTab::PlayerOptionsEntry::selectButtons()
 	}
 }
 
-void OptionsTab::SelectedBox::showAll(SDL_Surface * to)
-{
-	//PlayerSettings &s = SEL->sInfo.playerInfos[player];
-	SDL_Surface *toBlit = getImg();
-	const std::string *toPrint = getText();
-	blitAt(toBlit, pos, to);
-	printAtMiddleLoc(*toPrint, 23, 39, FONT_TINY, Colors::WHITE, to);
-}
-
-OptionsTab::SelectedBox::SelectedBox( SelType Which, ui8 Player )
-:which(Which), player(Player)
-{
-	SDL_Surface *img = getImg();
-	pos.w = img->w;
-	pos.h = img->h;
-	addUsedEvents(RCLICK);
-}
-
-size_t OptionsTab::SelectedBox::getBonusImageIndex() const
+size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
 {
 	enum EBonusSelection //frames of bonuses file
 	{
 		WOOD_ORE = 0,   CRYSTAL = 1,    GEM  = 2,
 		MERCURY  = 3,   SULFUR  = 5,    GOLD = 8,
 		ARTIFACT = 9,   RANDOM  = 10,
-		WOOD = 0,       ORE     = 0,    MITHRIL = 10 // resources unavailable in bonuses file
-	};
+		WOOD = 0,       ORE     = 0,    MITHRIL = 10, // resources unavailable in bonuses file
 
-	const PlayerSettings &s = SEL->sInfo.playerInfos[player];
-	switch(s.bonus)
-	{
-	case -1: return RANDOM;
-	case 0:  return ARTIFACT;
-	case 1:  return GOLD;
-	case 2:
-		switch (CGI->townh->towns[s.castle].primaryRes)
-		{
-		case 127          : return WOOD_ORE;
-		case Res::WOOD    : return WOOD;
-		case Res::MERCURY : return MERCURY;
-		case Res::ORE     : return ORE;
-		case Res::SULFUR  : return SULFUR;
-		case Res::CRYSTAL : return CRYSTAL;
-		case Res::GEMS    : return GEM;
-		case Res::GOLD    : return GOLD;
-		case Res::MITHRIL : return MITHRIL;
-		}
-	default:
-		assert(0);
-		return 0;
-	}
-}
+		TOWN_RANDOM = 38,  TOWN_NONE = 39, // Special frames in ITPA
+		HERO_RANDOM = 200, HERO_NONE = 201 // Special frames in PortraitsSmall
+	};
 
-SDL_Surface * OptionsTab::SelectedBox::getImg() const
-{
-	const PlayerSettings &s = SEL->sInfo.playerInfos[player];
-	switch(which)
+	switch(type)
 	{
 	case TOWN:
-		if (s.castle == -1)
-			return CGP->rTown;
-		if (s.castle == -2)
-			return CGP->nTown;
-		else
-			return graphics->getPic(s.castle, true, false);
+		switch (settings.castle)
+		{
+		case PlayerSettings::NONE:   return TOWN_NONE;
+		case PlayerSettings::RANDOM: return TOWN_RANDOM;
+		default: return CGI->townh->towns[settings.castle].clientInfo.icons[true][false] + 2;
+		}
+
 	case HERO:
-		if (s.hero == -1)
+		switch (settings.hero)
 		{
-			return CGP->rHero;
+		case PlayerSettings::NONE:   return HERO_NONE;
+		case PlayerSettings::RANDOM: return HERO_RANDOM;
+		default:
+			{
+				if(settings.heroPortrait >= 0)
+					return settings.heroPortrait;
+				return settings.hero;
+			}
 		}
-		else if (s.hero == -2)
+
+	case BONUS:
 		{
-			if(s.heroPortrait >= 0)
-				return graphics->portraitSmall[s.heroPortrait];
-			else
-				return CGP->nHero;
+			switch(settings.bonus)
+			{
+			case PlayerSettings::RANDOM:   return RANDOM;
+			case PlayerSettings::ARTIFACT: return ARTIFACT;
+			case PlayerSettings::GOLD:     return GOLD;
+			case PlayerSettings::RESOURCE:
+				{
+					switch(CGI->townh->towns[settings.castle].primaryRes)
+					{
+					case 127          : return WOOD_ORE;
+					case Res::WOOD    : return WOOD;
+					case Res::MERCURY : return MERCURY;
+					case Res::ORE     : return ORE;
+					case Res::SULFUR  : return SULFUR;
+					case Res::CRYSTAL : return CRYSTAL;
+					case Res::GEMS    : return GEM;
+					case Res::GOLD    : return GOLD;
+					case Res::MITHRIL : return MITHRIL;
+					}
+				}
+			}
 		}
-		else
+	}
+	return 0;
+}
+
+std::string OptionsTab::CPlayerSettingsHelper::getImageName()
+{
+	switch(type)
+	{
+	case OptionsTab::TOWN:  return "ITPA";
+	case OptionsTab::HERO:  return "PortraitsSmall";
+	case OptionsTab::BONUS: return "SCNRSTAR";
+	}
+	return "";
+}
+
+std::string OptionsTab::CPlayerSettingsHelper::getTitle()
+{
+	switch(type)
+	{
+	case OptionsTab::TOWN: return (settings.castle < 0) ? CGI->generaltexth->allTexts[103] : CGI->generaltexth->allTexts[80];
+	case OptionsTab::HERO: return (settings.hero   < 0) ? CGI->generaltexth->allTexts[101] : CGI->generaltexth->allTexts[77];
+	case OptionsTab::BONUS:
 		{
-			return graphics->portraitSmall[s.hero];
+			switch(settings.bonus)
+			{
+			case PlayerSettings::RANDOM:   return CGI->generaltexth->allTexts[86]; //{Random Bonus}
+			case PlayerSettings::ARTIFACT: return CGI->generaltexth->allTexts[83]; //{Artifact Bonus}
+			case PlayerSettings::GOLD:     return CGI->generaltexth->allTexts[84]; //{Gold Bonus}
+			case PlayerSettings::RESOURCE: return CGI->generaltexth->allTexts[85]; //{Resource Bonus}
+			}
 		}
-		break;
-	case BONUS:
-			return CGP->bonuses->ourImages[getBonusImageIndex()].bitmap;
-	default:
-		return nullptr;
 	}
+	return "";
 }
 
-const std::string * OptionsTab::SelectedBox::getText() const
+std::string OptionsTab::CPlayerSettingsHelper::getName()
 {
-	const PlayerSettings &s = SEL->sInfo.playerInfos[player];
-	switch(which)
+	switch(type)
 	{
 	case TOWN:
-		if (s.castle == -1)
-			return &CGI->generaltexth->allTexts[522];
-		else if (s.castle == -2)
-			return &CGI->generaltexth->allTexts[523];
-		else
-			return &CGI->townh->factions[s.castle].name;
-	case HERO:
-		if (s.hero == -1)
-			return &CGI->generaltexth->allTexts[522];
-		else if (s.hero == -2)
 		{
-			if(s.heroPortrait >= 0)
+			switch (settings.castle)
 			{
-				if(s.heroName.length())
-					return &s.heroName;
-				else
-					return &CGI->heroh->heroes[s.heroPortrait]->name;
+			case PlayerSettings::NONE   : return CGI->generaltexth->allTexts[523];
+			case PlayerSettings::RANDOM : return CGI->generaltexth->allTexts[522];
+			default : return CGI->townh->factions[settings.castle].name;
 			}
-			else
-				return &CGI->generaltexth->allTexts[523];
 		}
-		else
+	case HERO:
 		{
-			//if(s.heroName.length())
-			//	return &s.heroName;
-			//else
-				return &CGI->heroh->heroes[s.hero]->name;
+			switch (settings.hero)
+			{
+			case PlayerSettings::NONE   : return CGI->generaltexth->allTexts[523];
+			case PlayerSettings::RANDOM : return CGI->generaltexth->allTexts[522];
+			default :
+				{
+					if (!settings.heroName.empty())
+						return settings.heroName;
+					return CGI->heroh->heroes[settings.hero]->name;
+				}
+			}
 		}
 	case BONUS:
-		switch (s.bonus)
 		{
-		case -1:
-			return &CGI->generaltexth->allTexts[522];
-		default:
-			return &CGI->generaltexth->arraytxt[214 + s.bonus];
+			switch (settings.bonus)
+			{
+				case PlayerSettings::RANDOM : return CGI->generaltexth->allTexts[522];
+				default: return CGI->generaltexth->arraytxt[214 + settings.bonus];
+			}
 		}
-	default:
-		return NULL;
 	}
+	return "";
 }
 
-void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState )
+std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
 {
-	if(indeterminate(down) || !down) return;
-	const PlayerSettings &s = SEL->sInfo.playerInfos[player];
-	SDL_Surface *bmp = NULL;
-	const std::string *title = NULL, *subTitle = NULL;
-
-	subTitle = getText();
-
-	int val=-1;
-	switch(which)
+	switch(type)
 	{
-	case TOWN:
-		val = s.castle;
-		break;
+	case TOWN: return getName();
 	case HERO:
-		val = s.hero;
-		if(val == -2) //none => we may have some preset info
 		{
-			int p9 = SEL->current->mapHeader->players[s.color].p9;
-			if(p9 != 255  &&  SEL->sInfo.playerInfos[player].heroPortrait >= 0)
-				val = p9;
+			if (settings.hero >= 0)
+				return getName() + " - " + CGI->heroh->heroes[settings.hero]->heroClass->name;
+			return getName();
 		}
-		break;
+
 	case BONUS:
-		val = s.bonus;
-		break;
+		{
+			switch(settings.bonus)
+			{
+			case PlayerSettings::GOLD:     return CGI->generaltexth->allTexts[87]; //500-1000
+			case PlayerSettings::RESOURCE:
+				{
+					switch(CGI->townh->towns[settings.castle].primaryRes)
+					{
+					case Res::MERCURY: return CGI->generaltexth->allTexts[694];
+					case Res::SULFUR:  return CGI->generaltexth->allTexts[695];
+					case Res::CRYSTAL: return CGI->generaltexth->allTexts[692];
+					case Res::GEMS:    return CGI->generaltexth->allTexts[693];
+					case 127:          return CGI->generaltexth->allTexts[89]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool
+					}
+				}
+			}
+		}
 	}
+	return "";
+}
 
-	if(val == -1  ||  which == BONUS) //random or bonus box
+std::string OptionsTab::CPlayerSettingsHelper::getDescription()
+{
+	switch(type)
 	{
-		bmp = CMessage::drawDialogBox(256, 190);
-		std::string *description = NULL;
-
-		switch(which)
+	case TOWN: return CGI->generaltexth->allTexts[104];
+	case HERO: return CGI->generaltexth->allTexts[102];
+	case BONUS:
 		{
-		case TOWN:
-			title = &CGI->generaltexth->allTexts[103];
-			description = &CGI->generaltexth->allTexts[104];
-			break;
-		case HERO:
-			title = &CGI->generaltexth->allTexts[101];
-			description = &CGI->generaltexth->allTexts[102];
-			break;
-		case BONUS:
+			switch(settings.bonus)
 			{
-				switch(val)
+			case PlayerSettings::RANDOM:   return CGI->generaltexth->allTexts[94]; //Gold, wood and ore, or an artifact is randomly chosen as your starting bonus
+			case PlayerSettings::ARTIFACT: return CGI->generaltexth->allTexts[90]; //An artifact is randomly chosen and equipped to your starting hero
+			case PlayerSettings::GOLD:     return CGI->generaltexth->allTexts[92]; //At the start of the game, 500-1000 gold is added to your Kingdom's resource pool
+			case PlayerSettings::RESOURCE:
 				{
-				case PlayerSettings::RANDOM:
-					title = &CGI->generaltexth->allTexts[86]; //{Random Bonus}
-					description = &CGI->generaltexth->allTexts[94]; //Gold, wood and ore, or an artifact is randomly chosen as your starting bonus
-					break;
-				case PlayerSettings::ARTIFACT:
-					title = &CGI->generaltexth->allTexts[83]; //{Artifact Bonus}
-					description = &CGI->generaltexth->allTexts[90]; //An artifact is randomly chosen and equipped to your starting hero
-					break;
-				case PlayerSettings::GOLD:
-					title = &CGI->generaltexth->allTexts[84]; //{Gold Bonus}
-					subTitle = &CGI->generaltexth->allTexts[87]; //500-1000
-					description = &CGI->generaltexth->allTexts[92]; //At the start of the game, 500-1000 gold is added to your Kingdom's resource pool
-					break;
-				case PlayerSettings::RESOURCE:
+					switch(CGI->townh->towns[settings.castle].primaryRes)
 					{
-						title = &CGI->generaltexth->allTexts[85]; //{Resource Bonus}
-						switch(CGI->townh->towns[s.castle].primaryRes)
-						{
-						case 1:
-							subTitle = &CGI->generaltexth->allTexts[694];
-							description = &CGI->generaltexth->allTexts[690];
-							break;
-						case 3:
-							subTitle = &CGI->generaltexth->allTexts[695];
-							description = &CGI->generaltexth->allTexts[691];
-							break;
-						case 4:
-							subTitle = &CGI->generaltexth->allTexts[692];
-							description = &CGI->generaltexth->allTexts[688];
-							break;
-						case 5:
-							subTitle = &CGI->generaltexth->allTexts[693];
-							description = &CGI->generaltexth->allTexts[689];
-							break;
-						case 127:
-							subTitle = &CGI->generaltexth->allTexts[89]; //5-10 wood / 5-10 ore
-							description = &CGI->generaltexth->allTexts[93]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool
-							break;
-						}
+					case Res::MERCURY: return CGI->generaltexth->allTexts[690];
+					case Res::SULFUR:  return CGI->generaltexth->allTexts[691];
+					case Res::CRYSTAL: return CGI->generaltexth->allTexts[688];
+					case Res::GEMS:    return CGI->generaltexth->allTexts[689];
+					case 127:          return CGI->generaltexth->allTexts[93]; //At the start of the game, 5-10 wood and 5-10 ore are added to your Kingdom's resource pool
 					}
-					break;
 				}
 			}
-			break;
 		}
-
-		if(description)
-			CSDL_Ext::printAtMiddleWB(*description, 125, 145, FONT_SMALL, 37, Colors::WHITE, bmp);
 	}
-	else if(val == -2)
+	return "";
+}
+
+OptionsTab::CPregameTooltipBox::CPregameTooltipBox(CPlayerSettingsHelper & helper):
+	CWindowObject(BORDERED | RCLICK_POPUP),
+	CPlayerSettingsHelper(helper)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	int value;
+
+	switch(CPlayerSettingsHelper::type)
 	{
-		return;
+	break; case TOWN:
+			value = settings.castle;
+	break; case HERO:
+			value = settings.hero;
+	break; case BONUS:
+			value = settings.bonus;
 	}
-	else if(which == TOWN)
-	{
-		bmp = CMessage::drawDialogBox(256, 319);
-		title = &CGI->generaltexth->allTexts[80];
 
-		CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[79], 135, 137, FONT_MEDIUM, Colors::YELLOW, bmp);
+	if (value == PlayerSettings::RANDOM)
+		genBonusWindow();
+	else if (CPlayerSettingsHelper::type == BONUS)
+		genBonusWindow();
+	else if (CPlayerSettingsHelper::type == HERO)
+		genHeroWindow();
+	else if (CPlayerSettingsHelper::type == TOWN)
+		genTownWindow();
 
-		const CTown &t = CGI->townh->towns[val];
-		//print creatures
-		int x = 60, y = 159;
-		for(int i = 0; i < 7; i++)
-		{
-			int c = t.creatures[i][0];
-			blitAt(graphics->smallImgs[c], x, y, bmp);
-			CSDL_Ext::printAtMiddleWB(CGI->creh->creatures[c]->nameSing, x + 16, y + 45, FONT_TINY, 10, Colors::WHITE, bmp);
+	center();
+}
 
-			if(i == 2)
-			{
-				x = 40;
-				y += 76;
-			}
-			else
-			{
-				x += 52;
-			}
-		}
+void OptionsTab::CPregameTooltipBox::genHeader()
+{
+	new CFilledTexture("DIBOXBCK", pos);
+	updateShadow();
 
-	}
-	else if(val >= 0)
-	{
-		const CHero *h = CGI->heroh->heroes[val];
-		bmp = CMessage::drawDialogBox(320, 255);
-		title = &CGI->generaltexth->allTexts[77];
+	new CLabel(pos.w / 2 + 8, 21, FONT_MEDIUM, CENTER, Colors::YELLOW, getTitle());
 
-		CSDL_Ext::printAtMiddle(*title, 167, 36, FONT_MEDIUM, Colors::YELLOW, bmp);
-		CSDL_Ext::printAtMiddle(*subTitle + " - " + h->heroClass->name, 160, 99, FONT_SMALL, Colors::WHITE, bmp);
+	new CLabel(pos.w / 2, 88, FONT_SMALL, CENTER, Colors::WHITE, getSubtitle());
 
-		blitAt(getImg(), 136, 56, bmp);
+	new CAnimImage(getImageName(), getImageIndex(), 0, pos.w / 2 - 24, 45);
+}
 
-		//print specialty
-		CSDL_Ext::printAtMiddle(CGI->generaltexth->allTexts[78], 166, 132, FONT_MEDIUM, Colors::YELLOW, bmp);
-		blitAt(graphics->un44->ourImages[val].bitmap, 140, 150, bmp);
-		CSDL_Ext::printAtMiddle(CGI->generaltexth->hTxts[val].bonusName, 166, 203, FONT_SMALL, Colors::WHITE, bmp);
+void OptionsTab::CPregameTooltipBox::genTownWindow()
+{
+	pos = Rect(0, 0, 228, 290);
+	genHeader();
 
-		GH.pushInt(new CInfoPopup(bmp, true));
-		return;
-	}
+	new CLabel(pos.w / 2 + 8, 122, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[79]);
 
-	if(title)
-		CSDL_Ext::printAtMiddle(*title, 135, 36, FONT_MEDIUM, Colors::YELLOW, bmp);
-	if(subTitle)
-		CSDL_Ext::printAtMiddle(*subTitle, 127, 103, FONT_SMALL, Colors::WHITE, bmp);
+	std::vector<CComponent *> components;
+	const CTown & town = CGI->townh->towns[settings.castle];
 
-	blitAt(getImg(), 104, 60, bmp);
+	for (size_t i=0; i< town.creatures.size(); i++)
+		components.push_back(new CComponent(CComponent::creature, town.creatures[i].front(), 0, CComponent::tiny));
 
-	GH.pushInt(new CInfoPopup(bmp, true));
+	new CComponentBox(components, Rect(0, 140, pos.w, 140));
+}
+
+void OptionsTab::CPregameTooltipBox::genHeroWindow()
+{
+	pos = Rect(0, 0, 292, 226);
+	genHeader();
+
+	// speciality
+	new CAnimImage("UN44", settings.hero, 0, pos.w / 2 - 22, 134);
+
+	new CLabel(pos.w / 2 + 4, 117, FONT_MEDIUM, CENTER,  Colors::YELLOW, CGI->generaltexth->allTexts[78]);
+	new CLabel(pos.w / 2,     188, FONT_SMALL,  CENTER, Colors::WHITE, CGI->generaltexth->hTxts[settings.hero].bonusName);
+}
+
+void OptionsTab::CPregameTooltipBox::genBonusWindow()
+{
+	pos = Rect(0, 0, 228, 162);
+	genHeader();
+
+	new CTextBox(getDescription(), Rect(10, 88, pos.w - 20, 70), 0, FONT_SMALL, CENTER, Colors::WHITE );
+}
+
+OptionsTab::SelectedBox::SelectedBox(Point position, PlayerSettings & settings, SelType type)
+	:CIntObject(RCLICK, position),
+	CPlayerSettingsHelper(settings, type)
+{
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
+	image = new CAnimImage(getImageName(), getImageIndex());
+	subtitle = new CLabel(23, 39, FONT_TINY, CENTER, Colors::WHITE, getName());
+
+	pos = image->pos;
+}
+
+void OptionsTab::SelectedBox::update()
+{
+	image->setFrame(getImageIndex());
+	subtitle->setTxt(getName());
+}
+
+void OptionsTab::SelectedBox::clickRight( tribool down, bool previousState )
+{
+	if (down)
+	{
+		// cases when we do not need to display a message
+		if (settings.castle == -2 && CPlayerSettingsHelper::type == TOWN )
+			return;
+		if (settings.hero == -2 && SEL->current->mapHeader->players[settings.color].customHeroID == -1 && CPlayerSettingsHelper::type == HERO)
+			return;
+
+		GH.pushInt(new CPregameTooltipBox(*this));
+	}
 }
 
 CScenarioInfo::CScenarioInfo(const CMapHeader *mapHeader, const StartInfo *startInfo)

+ 36 - 10
client/CPreGame.h

@@ -192,18 +192,45 @@ class OptionsTab : public CIntObject
 public:
 	enum SelType {TOWN, HERO, BONUS};
 
-	struct SelectedBox : public CIntObject //img with current town/hero/bonus
+	struct CPlayerSettingsHelper
 	{
-		SelType which;
-		ui8 player; //serial nr
+		const PlayerSettings & settings;
+		const SelType type;
+
+		CPlayerSettingsHelper(const PlayerSettings & settings, SelType type):
+		    settings(settings),
+		    type(type)
+		{}
+
+		/// visible image settings
+		size_t getImageIndex();
+		std::string getImageName();
+
+		std::string getName();       /// name visible in options dialog
+		std::string getTitle();      /// title in popup box
+		std::string getSubtitle();   /// popup box subtitle
+		std::string getDescription();/// popup box description, not always present
+	};
+
+	class CPregameTooltipBox : public CWindowObject, public CPlayerSettingsHelper
+	{
+		void genHeader();
+		void genTownWindow();
+		void genHeroWindow();
+		void genBonusWindow();
+	public:
+		CPregameTooltipBox(CPlayerSettingsHelper & helper);
+	};
 
-		size_t getBonusImageIndex() const;
-		SDL_Surface *getImg() const;
-		const std::string *getText() const;
+	struct SelectedBox : public CIntObject, public CPlayerSettingsHelper //img with current town/hero/bonus
+	{
+		CAnimImage * image;
+		CLabel *subtitle;
 
-		SelectedBox(SelType Which, ui8 Player);
-		void showAll(SDL_Surface * to);
+		SelectedBox(Point position, PlayerSettings & settings, SelType type);
 		void clickRight(tribool down, bool previousState);
+
+		void update();
 	};
 
 	struct PlayerOptionsEntry : public CIntObject
@@ -221,6 +248,7 @@ public:
 		PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S);
 		void selectButtons(); //hides unavailable buttons
 		void showAll(SDL_Surface * to);
+		void update();
 	};
 
 	CSlider *turnDuration;
@@ -645,8 +673,6 @@ public:
 
 	CMenuScreen* menu;
 
-	SDL_Surface *nHero, *rHero, *nTown, *rTown; // none/random hero/town imgs
-	CDefHandler *bonuses;
 	CDefHandler *victory, *loss;
 
 	~CGPreGame();

+ 13 - 10
client/GUIClasses.cpp

@@ -811,13 +811,17 @@ void CComponent::init(Etype Type, int Subtype, int Val, ESize imageSize)
 	pos.w = image->pos.w;
 	pos.h = image->pos.h;
 
+	EFonts font = FONT_SMALL;
+	if (imageSize < small)
+		font = FONT_TINY; //other sizes?
+
 	pos.h += 4; //distance between text and image
 
-	std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(80, pos.w), FONT_SMALL);
+	std::vector<std::string> textLines = CMessage::breakText(getSubtitle(), std::max<int>(80, pos.w), font);
 	BOOST_FOREACH(auto & line, textLines)
 	{
-		int height = graphics->fonts[FONT_SMALL]->height;
-		CLabel * label = new CLabel(pos.w/2, pos.h + height/2, FONT_SMALL, CENTER, Colors::WHITE, line);
+		int height = graphics->fonts[font]->height;
+		CLabel * label = new CLabel(pos.w/2, pos.h + height/2, font, CENTER, Colors::WHITE, line);
 
 		pos.h += height;
 		if (label->pos.w > pos.w)
@@ -1035,7 +1039,7 @@ Point CComponentBox::getOrTextPos(CComponent *left, CComponent *right)
 
 int CComponentBox::getDistance(CComponent *left, CComponent *right)
 {
-	static const int betweenImagesMin = 50;
+	static const int betweenImagesMin = 20;
 	static const int betweenSubtitlesMin = 10;
 
 	int leftSubtitle  = ( left->pos.w -  left->image->pos.w) / 2;
@@ -4253,9 +4257,14 @@ void LRClickableAreaWTextComp::clickRight(tribool down, bool previousState)
 
 CHeroArea::CHeroArea(int x, int y, const CGHeroInstance * _hero):hero(_hero)
 {
+	OBJ_CONSTRUCTION_CAPTURING_ALL;
+
 	addUsedEvents(LCLICK | RCLICK | HOVER);
 	pos.x += x;	pos.w = 58;
 	pos.y += y;	pos.h = 64;
+
+	if (hero)
+		new CAnimImage("PortraitsLarge", hero->portrait);
 }
 
 void CHeroArea::clickLeft(tribool down, bool previousState)
@@ -4278,12 +4287,6 @@ void CHeroArea::hover(bool on)
 		GH.statusbar->clear();
 }
 
-void CHeroArea::showAll(SDL_Surface * to)
-{
-	if (hero)
-		blitAtLoc(graphics->portraitLarge[hero->portrait],0,0,to);
-}
-
 void LRClickableAreaOpenTown::clickLeft(tribool down, bool previousState)
 {
 	if((!down) && previousState && town)

+ 1 - 2
client/GUIClasses.h

@@ -846,15 +846,14 @@ public:
 /// Opens hero window by left-clicking on it
 class CHeroArea: public CIntObject
 {
-public:
 	const CGHeroInstance * hero;
+public:
 
 	CHeroArea(int x, int y, const CGHeroInstance * _hero);
 
 	void clickLeft(tribool down, bool previousState);
 	void clickRight(tribool down, bool previousState);
 	void hover(bool on);
-	void showAll(SDL_Surface * to);
 };
 
 /// Opens town screen by left-clicking on it

+ 0 - 45
client/Graphics.cpp

@@ -136,12 +136,9 @@ Graphics::Graphics()
 	tasks += boost::bind(&Graphics::loadTrueType,this);
 	tasks += boost::bind(&Graphics::loadPaletteAndColors,this);
 	tasks += boost::bind(&Graphics::loadHeroFlags,this);
-	tasks += boost::bind(&Graphics::loadHeroPortraits,this);
 	tasks += boost::bind(&Graphics::initializeBattleGraphics,this);
 	tasks += boost::bind(&Graphics::loadErmuToPicture,this);
 	tasks += GET_DEF_ESS(artDefs,"ARTIFACT.DEF");
-	tasks += GET_DEF_ESS(un44,"UN44.DEF");
-	tasks += GET_DEF_ESS(smallIcons,"ITPA.DEF");
 	tasks += GET_DEF_ESS(resources32,"RESOURCE.DEF");
 	tasks += GET_DEF(smi,"CPRSMALL.DEF");
 	tasks += GET_DEF(smi2,"TWCRPORT.DEF");
@@ -175,31 +172,6 @@ Graphics::Graphics()
 	bigImgs[71]->format->palette->colors[7] = green;
 	delete smi2;
 }
-void Graphics::loadHeroPortraits()
-{	
-	const JsonNode config(ResourceID("config/portraits.json"));
-
-	BOOST_FOREACH(const JsonNode &portrait_node, config["hero_portrait"].Vector()) {
-		std::string filename = portrait_node["filename"].String();
-
-		/* Small portrait. */
-		portraitSmall.push_back(BitmapHandler::loadBitmap(filename));
-
-		/* Large portrait. Alter the filename. Size letter is usually
-		 * third one, but there are exceptions and it should fix the
-		 * problem. */
-		for (int ff=0; ff<filename.size(); ++ff)
-		{
-			if (filename[ff]=='S') {
-				filename[ff]='L';
-				break;
-			}
-		}
-		portraitLarge.push_back(BitmapHandler::loadBitmap(filename));
-
-		SDL_SetColorKey(portraitLarge[portraitLarge.size()-1],SDL_SRCCOLORKEY,SDL_MapRGB(portraitLarge[portraitLarge.size()-1]->format,0,255,255));
-	}
-}
 
 void Graphics::loadHeroAnims()
 {
@@ -334,23 +306,6 @@ void Graphics::loadHeroFlags()
 	grupa.join_all();
 	tlog0 << "Loading and transforming heroes' flags: "<<th.getDiff()<<std::endl;
 }
-SDL_Surface * Graphics::getPic(int ID, bool fort, bool builded)
-{
-	if (ID==-1)
-		return smallIcons->ourImages[0].bitmap;
-	else if (ID==-2)
-		return smallIcons->ourImages[1].bitmap;
-	else if (ID==-3)
-		return smallIcons->ourImages[2+GameConstants::F_NUMBER*4].bitmap;
-	else
-	{
-		assert(vstd::contains(CGI->townh->towns, ID));
-		int pom = CGI->townh->towns[ID].clientInfo.icons[fort][builded];
-		if (smallIcons->ourImages.size() > pom + 2)
-			return smallIcons->ourImages[pom + 2].bitmap;
-		return nullptr;
-	}
-}
 
 void Graphics::blueToPlayersAdv(SDL_Surface * sur, int player)
 {

+ 1 - 6
client/Graphics.h

@@ -45,11 +45,8 @@ public:
 	SDL_Color * neutralColorPalette; 
 
 	CDefEssential * artDefs; //artifacts //TODO: move to CArtifact class
-	std::vector<SDL_Surface *> portraitSmall; //48x32 px portraits of heroes
-	std::vector<SDL_Surface *> portraitLarge; //58x64 px portraits of heroes
 	std::vector<CDefEssential *> flags1, flags2, flags3, flags4; //flags blitted on heroes when ,
-	CDefEssential * un44; //many things
-	CDefEssential * smallIcons, *resources32; //resources 32x32
+	CDefEssential * resources32; //resources 32x32
 	CDefEssential * flags;
 	CDefEssential * heroMoveArrows;
 	std::vector<CDefEssential *> heroAnims; // [class id: 0 - 17]  //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
@@ -82,9 +79,7 @@ public:
 	void loadHeroFlags(std::pair<std::vector<CDefEssential *> Graphics::*, std::vector<const char *> > &pr, bool mode);
 	void loadHeroAnims();
 	void loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations, std::vector<CDefEssential *> Graphics::*dst);
-	void loadHeroPortraits();
 	void loadErmuToPicture();
-	SDL_Surface * getPic(int ID, bool fort=true, bool builded=false); //returns small picture of town: ID=-1 - blank; -2 - border; -3 - random
 	void blueToPlayersAdv(SDL_Surface * sur, int player); //replaces blue interface colour with a color of player
 	void loadTrueType();
 	void loadFonts();

+ 0 - 16
config/minimap.json

@@ -1,16 +0,0 @@
-{
-	"MinimapColors":
-		// Colors of terrain in minimap, in RGB format
-		[
-			{ "name": "Dirt", "terrain_id": 0, "unblocked": [ 82, 56, 8 ], "blocked": [ 57, 40, 8 ] },
-			{ "name": "Sand", "terrain_id": 1, "unblocked": [ 222, 207, 140 ], "blocked": [ 165, 158, 107 ] },
-			{ "name": "Grass", "terrain_id": 2, "unblocked": [ 0, 65, 0 ], "blocked": [ 0, 48, 0 ] },
-			{ "name": "Snow", "terrain_id": 3, "unblocked": [ 181, 199, 198 ], "blocked": [ 140, 158, 156 ] },
-			{ "name": "Swamp", "terrain_id": 4, "unblocked": [ 74, 134, 107 ], "blocked": [ 33, 89, 66 ] },
-			{ "name": "Rough", "terrain_id": 5, "unblocked": [ 132, 113, 49 ], "blocked": [ 99, 81, 33 ] },
-			{ "name": "Subterranean", "terrain_id": 6, "unblocked": [ 132, 48, 0 ], "blocked": [ 90, 8, 0 ] },
-			{ "name": "Lava", "terrain_id": 7, "unblocked": [ 74, 73, 74 ], "blocked": [ 41, 40, 41 ] },
-			{ "name": "Water", "terrain_id": 8, "unblocked": [ 8, 81, 148 ], "blocked": [ 8, 81, 148 ] },
-			{ "name": "Rock", "terrain_id": 9, "unblocked": [ 0, 0, 0 ], "blocked": [ 0, 0, 0 ] }
-		]
-}

+ 0 - 169
config/portraits.json

@@ -1,169 +0,0 @@
-// Heroes portraits
-{
-		"hero_portrait" :
-		[
-			{ "id": 0, "filename": "HPS000KN.bmp" },
-			{ "id": 1, "filename": "HPS001KN.bmp" },
-			{ "id": 2, "filename": "HPS002KN.bmp" },
-			{ "id": 3, "filename": "HPS003KN.bmp" },
-			{ "id": 4, "filename": "HPS004KN.bmp" },
-			{ "id": 5, "filename": "HPS005KN.bmp" },
-			{ "id": 6, "filename": "HPS006KN.bmp" },
-			{ "id": 7, "filename": "HPS007KN.bmp" },
-			{ "id": 8, "filename": "HPS008CL.bmp" },
-			{ "id": 9, "filename": "HPS009CL.bmp" },
-			{ "id": 10, "filename": "HPS010CL.bmp" },
-			{ "id": 11, "filename": "HPS011CL.bmp" },
-			{ "id": 12, "filename": "HPS012CL.bmp" },
-			{ "id": 13, "filename": "HPS013CL.bmp" },
-			{ "id": 14, "filename": "HPS014CL.bmp" },
-			{ "id": 15, "filename": "HPS015CL.bmp" },
-			{ "id": 16, "filename": "HPS016RN.bmp" },
-			{ "id": 17, "filename": "HPS017RN.bmp" },
-			{ "id": 18, "filename": "HPS018RN.bmp" },
-			{ "id": 19, "filename": "HPS019RN.bmp" },
-			{ "id": 20, "filename": "HPS020RN.bmp" },
-			{ "id": 21, "filename": "HPS021RN.bmp" },
-			{ "id": 22, "filename": "HPS022RN.bmp" },
-			{ "id": 23, "filename": "HPS023RN.bmp" },
-			{ "id": 24, "filename": "HPS024DR.bmp" },
-			{ "id": 25, "filename": "HPS025DR.bmp" },
-			{ "id": 26, "filename": "HPS026DR.bmp" },
-			{ "id": 27, "filename": "HPS027DR.bmp" },
-			{ "id": 28, "filename": "HPS028DR.bmp" },
-			{ "id": 29, "filename": "HPS029DR.bmp" },
-			{ "id": 30, "filename": "HPS030DR.bmp" },
-			{ "id": 31, "filename": "HPS031DR.bmp" },
-			{ "id": 32, "filename": "HPS032AL.bmp" },
-			{ "id": 33, "filename": "HPS033AL.bmp" },
-			{ "id": 34, "filename": "HPS034AL.bmp" },
-			{ "id": 35, "filename": "HPS035AL.bmp" },
-			{ "id": 36, "filename": "HPS036AL.bmp" },
-			{ "id": 37, "filename": "HPS037AL.bmp" },
-			{ "id": 38, "filename": "HPS038AL.bmp" },
-			{ "id": 39, "filename": "HPS039AL.bmp" },
-			{ "id": 40, "filename": "HPS040WZ.bmp" },
-			{ "id": 41, "filename": "HPS041WZ.bmp" },
-			{ "id": 42, "filename": "HPS042WZ.bmp" },
-			{ "id": 43, "filename": "HPS043WZ.bmp" },
-			{ "id": 44, "filename": "HPS044WZ.bmp" },
-			{ "id": 45, "filename": "HPS045WZ.bmp" },
-			{ "id": 46, "filename": "HPS046WZ.bmp" },
-			{ "id": 47, "filename": "HPS047WZ.bmp" },
-			{ "id": 48, "filename": "HPS048HR.bmp" },
-			{ "id": 49, "filename": "HPS049HR.bmp" },
-			{ "id": 50, "filename": "HPS050HR.bmp" },
-			{ "id": 51, "filename": "HPS051HR.bmp" },
-			{ "id": 52, "filename": "HPS052HR.bmp" },
-			{ "id": 53, "filename": "HPS053HR.bmp" },
-			{ "id": 54, "filename": "HPS054HR.bmp" },
-			{ "id": 55, "filename": "HPS055HR.bmp" },
-			{ "id": 56, "filename": "HPS056DM.bmp" },
-			{ "id": 57, "filename": "HPS057DM.bmp" },
-			{ "id": 58, "filename": "HPS058DM.bmp" },
-			{ "id": 59, "filename": "HPS059DM.bmp" },
-			{ "id": 60, "filename": "HPS060DM.bmp" },
-			{ "id": 61, "filename": "HPS061DM.bmp" },
-			{ "id": 62, "filename": "HPS062DM.bmp" },
-			{ "id": 63, "filename": "HPS063DM.bmp" },
-			{ "id": 64, "filename": "HPS064DK.bmp" },
-			{ "id": 65, "filename": "HPS065DK.bmp" },
-			{ "id": 66, "filename": "HPS066DK.bmp" },
-			{ "id": 67, "filename": "HPS067DK.bmp" },
-			{ "id": 68, "filename": "HPS068DK.bmp" },
-			{ "id": 69, "filename": "HPS069DK.bmp" },
-			{ "id": 70, "filename": "HPS070DK.bmp" },
-			{ "id": 71, "filename": "HPS071DK.bmp" },
-			{ "id": 72, "filename": "HPS072NC.bmp" },
-			{ "id": 73, "filename": "HPS073NC.bmp" },
-			{ "id": 74, "filename": "HPS074NC.bmp" },
-			{ "id": 75, "filename": "HPS075NC.bmp" },
-			{ "id": 76, "filename": "HPS076NC.bmp" },
-			{ "id": 77, "filename": "HPS077NC.bmp" },
-			{ "id": 78, "filename": "HPS078NC.bmp" },
-			{ "id": 79, "filename": "HPS079NC.bmp" },
-			{ "id": 80, "filename": "HPS080OV.bmp" },
-			{ "id": 81, "filename": "HPS081OV.bmp" },
-			{ "id": 82, "filename": "HPS082OV.bmp" },
-			{ "id": 83, "filename": "HPS083OV.bmp" },
-			{ "id": 84, "filename": "HPS084OV.bmp" },
-			{ "id": 85, "filename": "HPS085OV.bmp" },
-			{ "id": 86, "filename": "HPS086OV.bmp" },
-			{ "id": 87, "filename": "HPS087OV.bmp" },
-			{ "id": 88, "filename": "HPS088WL.bmp" },
-			{ "id": 89, "filename": "HPS089WL.bmp" },
-			{ "id": 90, "filename": "HPS090WL.bmp" },
-			{ "id": 91, "filename": "HPS091WL.bmp" },
-			{ "id": 92, "filename": "HPS092WL.bmp" },
-			{ "id": 93, "filename": "HPS093WL.bmp" },
-			{ "id": 94, "filename": "HPS094WL.bmp" },
-			{ "id": 95, "filename": "HPS095WL.bmp" },
-			{ "id": 96, "filename": "HPS096BR.bmp" },
-			{ "id": 97, "filename": "HPS097BR.bmp" },
-			{ "id": 98, "filename": "HPS098BR.bmp" },
-			{ "id": 99, "filename": "HPS099BR.bmp" },
-			{ "id": 100, "filename": "HPS100BR.bmp" },
-			{ "id": 101, "filename": "HPS101BR.bmp" },
-			{ "id": 102, "filename": "HPS102BR.bmp" },
-			{ "id": 103, "filename": "HPS103BR.bmp" },
-			{ "id": 104, "filename": "HPS104BM.bmp" },
-			{ "id": 105, "filename": "HPS105BM.bmp" },
-			{ "id": 106, "filename": "HPS106BM.bmp" },
-			{ "id": 107, "filename": "HPS107BM.bmp" },
-			{ "id": 108, "filename": "HPS108BM.bmp" },
-			{ "id": 109, "filename": "HPS109BM.bmp" },
-			{ "id": 110, "filename": "HPS110BM.bmp" },
-			{ "id": 111, "filename": "HPS111BM.bmp" },
-			{ "id": 112, "filename": "HPS112BS.bmp" },
-			{ "id": 113, "filename": "HPS113BS.bmp" },
-			{ "id": 114, "filename": "HPS114BS.bmp" },
-			{ "id": 115, "filename": "HPS115BS.bmp" },
-			{ "id": 116, "filename": "HPS116BS.bmp" },
-			{ "id": 117, "filename": "HPS117BS.bmp" },
-			{ "id": 118, "filename": "HPS118BS.bmp" },
-			{ "id": 119, "filename": "HPS119BS.bmp" },
-			{ "id": 120, "filename": "HPS120WH.bmp" },
-			{ "id": 121, "filename": "HPS121WH.bmp" },
-			{ "id": 122, "filename": "HPS122WH.bmp" },
-			{ "id": 123, "filename": "HPS123WH.bmp" },
-			{ "id": 124, "filename": "HPS124WH.bmp" },
-			{ "id": 125, "filename": "HPS125WH.bmp" },
-			{ "id": 126, "filename": "HPS126WH.bmp" },
-			{ "id": 127, "filename": "HPS127WH.bmp" },
-			{ "id": 128, "filename": "HPS000PL.bmp" },
-			{ "id": 129, "filename": "HPS001PL.bmp" },
-			{ "id": 130, "filename": "HPS002PL.bmp" },
-			{ "id": 131, "filename": "HPS003PL.bmp" },
-			{ "id": 132, "filename": "HPS004PL.bmp" },
-			{ "id": 133, "filename": "HPS005PL.bmp" },
-			{ "id": 134, "filename": "HPS006PL.bmp" },
-			{ "id": 135, "filename": "HPS007PL.bmp" },
-			{ "id": 136, "filename": "HPS000EL.bmp" },
-			{ "id": 137, "filename": "HPS001EL.bmp" },
-			{ "id": 138, "filename": "HPS002EL.bmp" },
-			{ "id": 139, "filename": "HPS003EL.bmp" },
-			{ "id": 140, "filename": "HPS004EL.bmp" },
-			{ "id": 141, "filename": "HPS005EL.bmp" },
-			{ "id": 142, "filename": "HPS006EL.bmp" },
-			{ "id": 143, "filename": "HPS007EL.bmp" },
-			{ "id": 144, "filename": "HPS130KN.bmp" },
-			{ "id": 145, "filename": "HPS000SH.bmp" },
-			{ "id": 146, "filename": "HPS128QC.bmp" },
-			{ "id": 147, "filename": "HPS003SH.bmp" },
-			{ "id": 148, "filename": "HPS004SH.bmp" },
-			{ "id": 149, "filename": "HPS005SH.bmp" },
-			{ "id": 150, "filename": "HPS006SH.bmp" },
-			{ "id": 151, "filename": "HPS007SH.bmp" },
-			{ "id": 152, "filename": "HPS009SH.bmp" },
-			{ "id": 153, "filename": "HPS008SH.bmp" },
-			{ "id": 154, "filename": "HPS001SH.bmp" },
-			{ "id": 155, "filename": "HPS131DM.bmp" },
-			{ "id": 156, "filename": "HPS129MK.bmp" },
-			{ "id": 157, "filename": "HPS002SH.bmp" },
-			{ "id": 158, "filename": "HPS132Wl.bmp" },
-			{ "id": 159, "filename": "HPS133Nc.bmp" },
-			{ "id": 160, "filename": "HPS134Nc.bmp" },
-			{ "id": 161, "filename": "HPS135Wi.bmp" },
-			{ "id": 162, "filename": "HPS136Wi.bmp" }
-		]
-}

+ 60 - 11
config/terrains.json

@@ -1,13 +1,62 @@
 {
-	// Movement costs on different terrains
-	"dirt"     : 100,
-	"sand"     : 150,
-	"grass"    : 100,
-	"snow"     : 150,
-	"swamp"    : 175,
-	"rough"    : 125,
-	"subterra" : 100,
-	"lava"     : 100,
-	"water"    : 100,
-	"rock"     : -1,
+	"dirt" :
+	{
+		"moveCost" : 100,
+		"minimapUnblocked" : [ 82, 56, 8 ],
+		"minimapBlocked"   : [ 57, 40, 8 ]
+	},
+	"sand" :
+	{
+		"moveCost" : 150,
+		"minimapUnblocked" : [ 222, 207, 140 ],
+		"minimapBlocked"   : [ 165, 158, 107 ]
+	},
+	"grass" :
+	{
+		"moveCost" : 100,
+		"minimapUnblocked" : [ 0, 65, 0 ],
+		"minimapBlocked"   : [ 0, 48, 0 ]
+	},
+	"snow" :
+	{
+		"moveCost" : 150,
+		"minimapUnblocked" : [ 181, 199, 198 ],
+		"minimapBlocked"   : [ 140, 158, 156 ]
+	},
+	"swamp" :
+	{
+		"moveCost" : 175,
+		"minimapUnblocked" : [ 74, 134, 107 ],
+		"minimapBlocked"   : [ 33,  89,  66 ]
+	},
+	"rough" :
+	{
+		"moveCost" : 125,
+		"minimapUnblocked" : [ 132, 113, 49 ],
+		"minimapBlocked"   : [  99,  81, 33 ]
+	},
+	"subterra" :
+	{
+		"moveCost" : 100,
+		"minimapUnblocked" : [ 132, 48, 0 ],
+		"minimapBlocked"   : [  90,  8, 0 ]
+	},
+	"lava" :
+	{
+		"moveCost" : 100,
+		"minimapUnblocked" : [ 74, 73, 74 ],
+		"minimapBlocked"   : [ 41, 40, 41 ]
+	},
+	"water" :
+	{
+		"moveCost" : 100,
+		"minimapUnblocked" : [ 8, 81, 148 ],
+		"minimapBlocked"   : [ 8, 81, 148 ]
+	},
+	"rock" :
+	{
+		"moveCost" : -1,
+		"minimapUnblocked" : [ 0, 0, 0 ],
+		"minimapBlocked"   : [ 0, 0, 0 ]
+	}
 }

+ 4 - 3
lib/CArtHandler.cpp

@@ -468,6 +468,7 @@ CArtifact * CArtHandler::loadArtifact(const JsonNode & node)
 	
 	int bearerType = -1;
 
+	// FIXME FIXME FIXME: value is unitialized = crash!
 	auto it = artifactBearerMap.find (value->String());
 	if (it != artifactPositionMap.end())
 	{
@@ -487,9 +488,9 @@ CArtifact * CArtHandler::loadArtifact(const JsonNode & node)
 	else
 		tlog2 << "Warning! Artifact type " << value->String() << " not recognized!";
 
-	value = &node["slot"];
-	if (!value->isNull() && bearerType == ArtBearer::HERO) //we assume non-hero slots are irrelevant?
-	{
+	value = &node["slot"];
+	if (!value->isNull() && bearerType == ArtBearer::HERO) //we assume non-hero slots are irrelevant?
+	{
 		auto it = artifactPositionMap.find (value->String());
 		if (it != artifactPositionMap.end())
 		{

+ 1 - 1
lib/CHeroHandler.cpp

@@ -326,7 +326,7 @@ void CHeroHandler::loadTerrains()
 
 	terrCosts.reserve(GameConstants::TERRAIN_TYPES);
 	BOOST_FOREACH(const std::string & name, GameConstants::TERRAIN_NAMES)
-		terrCosts.push_back(config[name].Float());
+		terrCosts.push_back(config[name]["moveCost"].Float());
 }
 
 std::vector<ui8> CHeroHandler::getDefaultAllowedHeroes() const

+ 3 - 3
lib/Map/CMap.cpp

@@ -13,8 +13,8 @@ SHeroName::SHeroName() : heroId(-1)
 }
 
 PlayerInfo::PlayerInfo(): canHumanPlay(false), canComputerPlay(false),
-	aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(255), hasMainTown(true),
-	generateHeroAtMainTown(true), team(255), generateHero(false), p7(0), p8(0), p9(0), powerPlaceholders(-1)
+	aiTactic(EAiTactic::RANDOM), isFactionRandom(false), mainHeroPortrait(-1), hasMainTown(true),
+	generateHeroAtMainTown(true), team(255), generateHero(false), p7(0), hasHero(false), customHeroID(-1), powerPlaceholders(-1)
 {
 	allowedFactions = VLC->townh->getDefaultAllowedFactions();
 }
@@ -34,7 +34,7 @@ si8 PlayerInfo::defaultCastle() const
 si8 PlayerInfo::defaultHero() const
 {
 	// we will generate hero in front of main town
-	if((generateHeroAtMainTown && hasMainTown) || p8)
+	if((generateHeroAtMainTown && hasMainTown) || hasHero)
 	{
 		//random hero
 		return -1;

+ 7 - 7
lib/Map/CMap.h

@@ -107,8 +107,8 @@ struct DLL_LINKAGE PlayerInfo
 	/** Unused. True if the faction should be chosen randomly. */
 	bool isFactionRandom;
 
-	/** Specifies the ID of the main hero with chosen portrait. The default value is 255. */
-	ui32 mainHeroPortrait;
+	/** Specifies the ID of the main hero with chosen portrait. The default value is -1. */
+	si32 mainHeroPortrait;
 
 	/** The name of the main hero. */
 	std::string mainHeroName;
@@ -134,11 +134,11 @@ struct DLL_LINKAGE PlayerInfo
 	/** Unknown and unused. */
 	si32 p7;
 
-	/** TODO ? */
-	si32 p8;
+	/** Player has a (custom?) hero */
+	bool hasHero;
 
-	/** TODO ? */
-	si32 p9;
+	/** ID of custom hero, -1 if none */
+	si32 customHeroID;
 
 	/**
 	 * Unused. Count of hero placeholders containing hero type.
@@ -153,7 +153,7 @@ struct DLL_LINKAGE PlayerInfo
 	template <typename Handler>
 	void serialize(Handler & h, const int version)
 	{
-		h & p7 & p8 & p9 & canHumanPlay & canComputerPlay & aiTactic & allowedFactions & isFactionRandom &
+		h & p7 & hasHero & customHeroID & canHumanPlay & canComputerPlay & aiTactic & allowedFactions & isFactionRandom &
 			mainHeroPortrait & mainHeroName & heroesNames & hasMainTown & generateHeroAtMainTown &
 			posOfMainTown & team & generateHero;
 	}

+ 9 - 3
lib/Map/CMapService.cpp

@@ -302,13 +302,19 @@ void CMapLoaderH3M::readPlayerInfo()
 			mapHeader->players[i].posOfMainTown.z = buffer[pos++];
 		}
 
-		mapHeader->players[i].p8 = buffer[pos++];
-		mapHeader->players[i].p9 = buffer[pos++];
-		if(mapHeader->players[i].p9 != 0xff)
+		mapHeader->players[i].hasHero = buffer[pos++];
+		mapHeader->players[i].customHeroID = buffer[pos++];
+
+		if(mapHeader->players[i].customHeroID != 0xff)
 		{
 			mapHeader->players[i].mainHeroPortrait = buffer[pos++];
+			if (mapHeader->players[i].mainHeroPortrait == 0xff)
+				mapHeader->players[i].mainHeroPortrait = -1; //correct 1-byte -1 (0xff) into 4-byte -1
+
 			mapHeader->players[i].mainHeroName = readString(buffer, pos);
 		}
+		else
+			mapHeader->players[i].customHeroID = -1; //correct 1-byte -1 (0xff) into 4-byte -1
 
 		if(mapHeader->version != EMapFormat::ROE)
 		{

+ 2 - 2
lib/StartInfo.h

@@ -60,8 +60,8 @@ struct PlayerSettings
 	PlayerSettings()
 	{
 		bonus = RANDOM;
-		castle = -2;
-		heroPortrait = -1;
+		castle = NONE;
+		heroPortrait = RANDOM;
 	}
 };