浏览代码

- finally, solution for imageIndex fields. All image lists will be generated in runtime
- iconIndex fields from json are no longer used
- simple check for missing icons on startup

Ivan Savenko 12 年之前
父节点
当前提交
3285f1910b

+ 2 - 2
Mods/vcmi/Sprites/PortraitsSmall.json

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

+ 55 - 35
client/CAnimation.cpp

@@ -329,7 +329,7 @@ const std::map<size_t, size_t > CDefFile::getEntries() const
 {
 	std::map<size_t, size_t > ret;
 
-	for (std::map<size_t, std::vector <size_t> >::const_iterator mapIt = offset.begin(); mapIt!=offset.end(); ++mapIt)
+	for (auto mapIt = offset.begin(); mapIt!=offset.end(); ++mapIt)
 		ret[mapIt->first] =  mapIt->second.size();
 	return ret;
 }
@@ -935,10 +935,21 @@ bool CAnimation::loadFrame(CDefFile * file, size_t frame, size_t group)
 	//try to get image from def
 	if (source[group][frame].getType() == JsonNode::DATA_NULL)
 	{
-		if (compressed)
-			images[group][frame] = new CompImage(file, frame, group);
-		else
-			images[group][frame] = new SDLImage(file, frame, group);
+		auto frameList = file->getEntries();
+
+		if (vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present
+		{
+			if (compressed)
+				images[group][frame] = new CompImage(file, frame, group);
+			else
+				images[group][frame] = new SDLImage(file, frame, group);
+		}
+		else // missing image
+		{
+			printError(frame, group, "LoadFrame");
+			images[group][frame] = new SDLImage("DEFAULT", compressed);
+		}
+
 	}
 	else //load from separate file
 	{
@@ -972,6 +983,38 @@ bool CAnimation::unloadFrame(size_t frame, size_t group)
 	return false;
 }
 
+void CAnimation::initFromJson(const JsonNode & config)
+{
+	std::string basepath;
+	basepath = config["basepath"].String();
+
+	BOOST_FOREACH(const JsonNode &group, config["sequences"].Vector())
+	{
+		size_t groupID = group["group"].Float();//TODO: string-to-value conversion("moving" -> MOVING)
+		source[groupID].clear();
+
+		BOOST_FOREACH(const JsonNode &frame, group["frames"].Vector())
+		{
+			source[groupID].push_back(JsonNode());
+			std::string filename =  frame.String();
+			source[groupID].back()["file"].String() = basepath + filename;
+		}
+	}
+
+	BOOST_FOREACH(const JsonNode &node, config["images"].Vector())
+	{
+		size_t group = node["group"].Float();
+		size_t frame = node["frame"].Float();
+
+		if (source[group].size() <= frame)
+			source[group].resize(frame+1);
+
+		source[group][frame] = node;
+		std::string filename =  node["file"].String();
+		source[group][frame]["file"].String() = basepath + filename;
+	}
+}
+
 void CAnimation::init(CDefFile * file)
 {
 	if (file)
@@ -982,8 +1025,12 @@ void CAnimation::init(CDefFile * file)
 			source[mapIt->first].resize(mapIt->second);
 	}
 
-	auto & configList = CResourceHandler::get()->getResourcesWithName(
-	                      ResourceID(std::string("SPRITES/") + name, EResType::TEXT));
+	ResourceID resID(std::string("SPRITES/") + name, EResType::TEXT);
+
+	if (vstd::contains(graphics->imageLists, resID.getName()))
+		initFromJson(graphics->imageLists[resID.getName()]);
+
+	auto & configList = CResourceHandler::get()->getResourcesWithName(resID);
 
 	BOOST_FOREACH(auto & entry, configList)
 	{
@@ -993,34 +1040,7 @@ void CAnimation::init(CDefFile * file)
 
 		const JsonNode config((char*)textData.get(), stream->getSize());
 
-		std::string basepath;
-		basepath = config["basepath"].String();
-
-		BOOST_FOREACH(const JsonNode &group, config["sequences"].Vector())
-		{
-			size_t groupID = group["group"].Float();//TODO: string-to-value conversion("moving" -> MOVING)
-			source[groupID].clear();
-
-			BOOST_FOREACH(const JsonNode &frame, group["frames"].Vector())
-			{
-				source[groupID].push_back(JsonNode());
-				std::string filename =  frame.String();
-				source[groupID].back()["file"].String() = basepath + filename;
-			}
-		}
-
-		BOOST_FOREACH(const JsonNode &node, config["images"].Vector())
-		{
-			size_t group = node["group"].Float();
-			size_t frame = node["frame"].Float();
-
-			if (source[group].size() <= frame)
-				source[group].resize(frame+1);
-
-			source[group][frame] = node;
-			std::string filename =  node["file"].String();
-			source[group][frame]["file"].String() = basepath + filename;
-		}
+		initFromJson(config);
 	}
 }
 

+ 1 - 0
client/CAnimation.h

@@ -175,6 +175,7 @@ private:
 	bool unloadFrame(size_t frame, size_t group);
 
 	//initialize animation from file
+	void initFromJson(const JsonNode & input);
 	void init(CDefFile * file);
 
 	//try to open def file

+ 3 - 1
client/CMT.cpp

@@ -139,11 +139,13 @@ void init()
 
 	pomtime.getDiff();
 	CCS->curh = new CCursorHandler;
+	graphics = new Graphics(); // should be before curh->init()
+
 	CCS->curh->initCursor();
 	CCS->curh->show();
     logGlobal->infoStream()<<"Screen handler: "<<pomtime.getDiff();
 	pomtime.getDiff();
-	graphics = new Graphics();
+
 	graphics->loadHeroAnims();
     logGlobal->infoStream()<<"\tMain graphics: "<<tmh.getDiff();
     logGlobal->infoStream()<<"Initializing game graphics: "<<tmh.getDiff();

+ 1 - 1
client/CPreGame.cpp

@@ -2633,7 +2633,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
 		WOOD = 0,       ORE     = 0,    MITHRIL = 10, // resources unavailable in bonuses file
 
 		TOWN_RANDOM = 38,  TOWN_NONE = 39, // Special frames in ITPA
-		HERO_RANDOM = 200, HERO_NONE = 201 // Special frames in PortraitsSmall
+		HERO_RANDOM = 163, HERO_NONE = 164 // Special frames in PortraitsSmall
 	};
 
 	switch(type)

+ 56 - 0
client/Graphics.cpp

@@ -126,6 +126,7 @@ Graphics::Graphics()
 	tasks += boost::bind(&Graphics::loadHeroFlags,this);
 	tasks += boost::bind(&Graphics::initializeBattleGraphics,this);
 	tasks += boost::bind(&Graphics::loadErmuToPicture,this);
+	tasks += boost::bind(&Graphics::initializeImageLists,this);
 	tasks += GET_DEF_ESS(resources32,"RESOURCE.DEF");
 	tasks += GET_DEF_ESS(spellscr,"SPELLSCR.DEF");
 	tasks += GET_DEF_ESS(heroMoveArrows,"ADAG.DEF");
@@ -417,3 +418,58 @@ void Graphics::loadErmuToPicture()
 	}
 	assert (etp_idx == 44);
 }
+
+void Graphics::addImageListEntry(size_t index, std::string listName, std::string imageName)
+{
+	if (!imageName.empty())
+	{
+		if (!CResourceHandler::get()->existsResource(ResourceID("SPRITES/" + imageName, EResType::IMAGE)))
+			logGlobal->errorStream() << "Required image " << "SPRITES/" << imageName << " is missing!";
+
+		JsonNode entry;
+		entry["frame"].Float() = index;
+		entry["file"].String() = imageName;
+
+		imageLists["SPRITES/" + listName]["images"].Vector().push_back(entry);
+	}
+}
+
+void Graphics::initializeImageLists()
+{
+	BOOST_FOREACH(const CCreature * creature, CGI->creh->creatures)
+	{
+		addImageListEntry(creature->iconIndex, "CPRSMALL", creature->smallIconName);
+		addImageListEntry(creature->iconIndex, "TWCRPORT", creature->largeIconName);
+	}
+
+	BOOST_FOREACH(const CHero * hero, CGI->heroh->heroes)
+	{
+		addImageListEntry(hero->imageIndex, "UN32", hero->iconSpecSmall);
+		addImageListEntry(hero->imageIndex, "UN44", hero->iconSpecLarge);
+		addImageListEntry(hero->imageIndex, "PORTRAITSLARGE", hero->portraitLarge);
+		addImageListEntry(hero->imageIndex, "PORTRAITSSMALL", hero->portraitSmall);
+	}
+
+	BOOST_FOREACH(const CArtifact * art, CGI->arth->artifacts)
+	{
+		addImageListEntry(art->iconIndex, "ARTIFACT", art->image);
+		addImageListEntry(art->iconIndex, "ARTIFACTLARGE", art->large);
+	}
+
+	BOOST_FOREACH(const CFaction * faction, CGI->townh->factions)
+	{
+		if (faction->town)
+		{
+			auto & info = faction->town->clientInfo;
+			addImageListEntry(info.icons[0][0], "ITPT", info.iconLarge[0][0]);
+			addImageListEntry(info.icons[0][1], "ITPT", info.iconLarge[0][1]);
+			addImageListEntry(info.icons[1][0], "ITPT", info.iconLarge[1][0]);
+			addImageListEntry(info.icons[1][1], "ITPT", info.iconLarge[1][1]);
+
+			addImageListEntry(info.icons[0][0] + 2, "ITPA", info.iconSmall[0][0]);
+			addImageListEntry(info.icons[0][1] + 2, "ITPA", info.iconSmall[0][1]);
+			addImageListEntry(info.icons[1][0] + 2, "ITPA", info.iconSmall[1][0]);
+			addImageListEntry(info.icons[1][1] + 2, "ITPA", info.iconSmall[1][1]);
+		}
+	}
+}

+ 6 - 0
client/Graphics.h

@@ -35,6 +35,8 @@ enum EFonts
 /// Handles fonts, hero images, town images, various graphics
 class Graphics
 {
+	void addImageListEntry(size_t index, std::string listName, std::string imageName);
+
 public:
 	//Fonts
 	static const int FONTS_NUMBER = 9;
@@ -54,6 +56,8 @@ public:
 	CDefHandler * FoWfullHide; //for Fog of War
 	CDefHandler * FoWpartialHide; //for For of War
 
+	std::map<std::string, JsonNode> imageLists;
+
 	std::map<std::string, CDefEssential *> advmapobjGraphics;
 	CDefEssential * getDef(const CGObjectInstance * obj);
 	CDefEssential * getDef(const CGDefInfo * info);
@@ -75,7 +79,9 @@ public:
 	CDefEssential *  loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations);
 	void loadErmuToPicture();
 	void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player
+
 	void loadFonts();
+	void initializeImageLists();
 };
 
 extern Graphics * graphics;

+ 0 - 5
config/factions/castle.json

@@ -114,11 +114,6 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBCSUP_5.def", "x" : 160, "y" : 190, "z" : -1, "border" : "TOCSCAV2.bmp", "area" : "TZCSCAV2.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBCSUP_6.def", "x" : 303, "y" : 0,   "z" : -1, "border" : "TOCSANG2.bmp", "area" : "TZCSANG2.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 18, "built" : 19 },
-				"fort"    : {"normal" : 0,  "built" : 1 }
-			},
 
 			"musicTheme" : "music/CstleTown",
 

+ 1 - 5
config/factions/conflux.json

@@ -118,11 +118,7 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBELUP_5.def", "x" : 394, "y" : 283, "z" : 2,  "border" : "TOELUP_5.bmp", "area" : "TZELUP_5.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBELUP_6.def", "x" : 43,  "y" : 0,   "z" : -2, "border" : "TOELUP_6.bmp", "area" : "TZELUP_6.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 34, "built" : 35 },
-				"fort"    : {"normal" : 16, "built" : 17 }
-			},
+
 			"musicTheme" : "music/ElemTown",
 
 			"townBackground": "TBELBACK.bmp",

+ 1 - 5
config/factions/dungeon.json

@@ -113,11 +113,7 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBDNUP_5.def", "x" : 270, "y" : 253, "z" : -1, "border" : "TODMAN2.bmp",  "area" : "TZDMAN2.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBDNUP_6.def", "x" : 550, "y" : 0,   "z" : -1, "border" : "TODDRA2A.bmp", "area" : "TZDDRA2A.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 28, "built" : 29 },
-				"fort"    : {"normal" : 10, "built" : 11 }
-			},
+
 			"musicTheme" : "music/Dungeon",
 
 			"townBackground": "TBDNBACK.bmp",

+ 1 - 5
config/factions/fortress.json

@@ -114,11 +114,7 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBFRUP_5.def", "x" : 0,   "y" : 4,   "border" : "TOFWYV2.bmp",  "area" : "TZFWYV2.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBFRUP_6.def", "x" : 587, "y" : 263, "z" : 5,  "border" : "TOFHYD2A.bmp", "area" : "TZFHYD2A.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 32, "built" : 33 },
-				"fort"    : {"normal" : 14, "built" : 15 }
-			},
+
 			"musicTheme" : "music/FortressTown",
 
 			"townBackground": "TBFRBACK.bmp",

+ 1 - 5
config/factions/inferno.json

@@ -114,11 +114,7 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBINUP_5.def", "x" : 220, "y" : 282, "z" : 5,  "border" : "TOIEFR2.bmp",  "area" : "TZIEFR2.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBINUP_6.def", "x" : 420, "y" : 105, "z" : -1, "border" : "TOIDVL2.bmp",  "area" : "TZIDVL2.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 24, "built" : 25 },
-				"fort"    : {"normal" : 6,  "built" : 7 }
-			},
+
 			"musicTheme" : "music/InfernoTown",
 
 			"townBackground": "TBINBACK.bmp",

+ 1 - 5
config/factions/necropolis.json

@@ -118,11 +118,7 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBNCUP_5.def", "x" : 0,   "y" : 30,  "border" : "TONBKN2.bmp",  "area" : "TZNBKN2.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBNCUP_6.def", "x" : 662, "y" : 23,  "border" : "TONBON2.bmp",  "area" : "TZNBON2.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 26, "built" : 27 },
-				"fort"    : {"normal" : 8,  "built" : 9 }
-			},
+
 			"musicTheme" : "music/NecroTown",
 
 			"townBackground": "TBNCBACK.bmp",

+ 0 - 5
config/factions/rampart.json

@@ -118,11 +118,6 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBRMUP_5.def", "x" : 362, "y" : 90,  "z" : -2, "border" : "TORUNI2.bmp",  "area" : "TZRUNI2.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBRMUP_6.def", "x" : 502, "y" : 5,   "z" : -5, "border" : "TORDR2AA.bmp", "area" : "TZRDR2AA.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 20, "built" : 21 },
-				"fort"    : {"normal" : 2,  "built" : 3 }
-			},
 
 			"musicTheme" : "music/Rampart",
 

+ 1 - 5
config/factions/stronghold.json

@@ -112,11 +112,7 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBSTUP_5.def", "x" : 616, "y" : 93,  "z" : -2, "border" : "TOSCYC2A.bmp", "area" : "TZSCYC2A.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBSTUP_6.def", "x" : 604, "y" : 0,   "border" : "TOSBEH2A.bmp", "area" : "TZSBEH2A.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 30, "built" : 31 },
-				"fort"    : {"normal" : 12, "built" : 13 }
-			},
+
 			"musicTheme" : "music/Stronghold",
 
 			"townBackground": "TBSTBACK.bmp",

+ 0 - 5
config/factions/tower.json

@@ -113,11 +113,6 @@
 				"dwellingUpLvl6": { "id" : 42, "animation" : "TBTWUP_5.def", "x" : 681, "y" : 157, "z" : 2,  "border" : "TOTNAG2.bmp",  "area" : "TZTNAG2.bmp" },
 				"dwellingUpLvl7": { "id" : 43, "animation" : "TBTWUP_6.def", "x" : 75,  "y" : 91,  "z" : -1, "border" : "TOTTIT2.bmp",  "area" : "TZTTIT2.bmp" }
 			},
-			"icons" :
-			{
-				"village" : {"normal" : 22, "built" : 23 },
-				"fort"    : {"normal" : 4,  "built" : 5 }
-			},
 
 			"musicTheme" : "music/TowerTown",
 

+ 2 - 7
config/schemas/artifact.json

@@ -3,7 +3,7 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI artifact format",
 	"description" : "Format used to define new artifacts in VCMI",
-	"required" : [ "class", "graphics", "text", "type", "value" ],
+	"required" : [ "class", "text", "type", "value" ],
 	
 	"definitions" : {
 		"growingBonusList" : {
@@ -46,13 +46,8 @@
 			"type":"object",
 			"additionalProperties" : false,
 			"description": "Graphical files associated with the artifact",
-			//"required" : [ "iconIndex", "image", "map" ],
-			"required" : [ "iconIndex" ],
+			"required" : [ "image", "map" ],
 			"properties":{
-				"iconIndex": {
-					"type":"number",
-					"description": "index of icon of this artifact in .def file"
-				},
 				"image": {
 					"type":"string",
 					"description": "Base image for this artifact, used for example in hero screen"

+ 1 - 6
config/schemas/creature.json

@@ -166,8 +166,7 @@
 			"additionalProperties" : false,
 			"description": "Describes how this creature looks like during battles",
 			"required" : [
-				"animationTime", "iconIndex",
-				"map", "animation", "timeBetweenFidgets"
+				"animationTime", "animation", "timeBetweenFidgets"
 			],
 			"properties":{
 				"animationTime": {
@@ -198,10 +197,6 @@
 					"type":"string",
 					"description": "Small icon for this creature, used for example in exchange screen"
 				},
-				"iconIndex": {
-					"type":"number",
-					"description": "Index of icon of this creature in .def file"
-				},
 
 				"map": {
 					"type":"string",

+ 33 - 29
config/schemas/faction.json

@@ -1,4 +1,32 @@
 {
+	"definitions" : {
+		"townIcon" : 
+		{
+			"type" : "object",
+			"additionalProperties" : false,
+			"required" : [ "small", "large" ],
+			"properties" : {
+				"small" : { "type" : "string" },
+				"large" : { "type" : "string" }
+			}
+		},
+		"townIconPair" : {
+			"type":"object",
+			"additionalProperties" : false,
+			"required" : [ "normal", "built" ],
+			"properties": {
+				"built": {
+					"$ref" : "#/definitions/townIcon",
+					"description": "Icon used after player build something in town"
+				},
+				"normal": {
+					"$ref" : "#/definitions/townIcon",
+					"description": "Icon used normally"
+				}
+			}
+		}
+	},
+
 	"type":"object",
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI faction format",
@@ -79,7 +107,7 @@
 			"additionalProperties" : false,
 			"required" : [
 				"adventureMap", "buildingsIcons", "buildings", "creatures", "guildWindow", "names",
-				"hallBackground", "hallSlots", "horde", "icons", "mageGuild", "moatDamage",
+				"hallBackground", "hallSlots", "horde", "mageGuild", "moatDamage",
 				"musicTheme", "siege", "structures", "townBackground", "warMachine"
 			],
 			"description": "town",
@@ -193,36 +221,12 @@
 					"required" : [ "fort", "village" ],
 					"properties":{
 						"fort": {
-							"type":"object",
-							"additionalProperties" : false,
-							"required" : [ "normal", "built" ],
-							"description": "Icons for town with built fort",
-							"properties":{
-								"built": {
-									"type":"number",
-									"description": "Icon used after player build something in town"
-								},
-								"normal": {
-									"type":"number",
-									"description": "Icon used normally"
-								}
-							}
+							"$ref" : "#/definitions/townIconPair",
+							"description": "Icons for town with built fort"
 						},
 						"village": {
-							"type":"object",
-							"additionalProperties" : false,
-							"required" : [ "normal", "built" ],
-							"description": "Icons for town without fort",
-							"properties":{
-								"built": {
-									"type":"number",
-									"description": "Icon used after player build something in town"
-								},
-								"normal": {
-									"type":"number",
-									"description": "Icon used normally"
-								}
-							}
+							"$ref" : "#/definitions/townIconPair",
+							"description": "Icons for town without fort"
 						}
 					}
 				},

+ 12 - 7
config/schemas/hero.json

@@ -3,7 +3,16 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI hero format",
 	"description" : "Format used to define new heroes in VCMI",
-	"required": [ "army", "class", "images", "skills", "texts" ],
+	"required": [ "army", "class", "skills", "texts" ],
+	
+	"oneOf" : [
+		{
+			"required" : [ "images" ]
+		},
+		{
+			"required" : [ "index" ]
+		}
+	],
 
 	"additionalProperties" : false,
 	"properties":{
@@ -46,18 +55,14 @@
 		},
 		"female": {
 			"type":"boolean",
-			"description": "This hero is female by default"
+			"description": "This hero is female (changeable via editor)"
 		},
 		"images": {
 			"type":"object",
 			"additionalProperties" : false,
 			"description": "images",
-			"required": [ "index" ],
+			"required": [ "large", "small", "specialtyLarge", "specialtySmall" ],
 			"properties":{
-				"index": {
-					"type":"number",
-					"description": "Index of image in portraitsLarge/portraitsSmall files"
-				},
 				"large": {
 					"type":"string",
 					"description": "Large version of portrait for use in hero screen"

+ 8 - 2
lib/CArtHandler.cpp

@@ -173,7 +173,6 @@ std::vector<JsonNode> CArtHandler::loadLegacyData(size_t dataSize)
 	{
 		JsonNode artData;
 
-		artData["graphics"]["iconIndex"].Float() = i;
 		artData["text"]["name"].String() = parser.readString();
 		artData["text"]["event"].String() = events.readString();
 		artData["value"].Float() = parser.readNumber();
@@ -200,6 +199,10 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
 {
 	auto object = loadFromJson(data);
 	object->id = ArtifactID(artifacts.size());
+	if (object->id < ArtifactID::ART_SELECTION)
+		object->iconIndex = object->id;
+	else
+		object->iconIndex = object->id + 2;
 
 	artifacts.push_back(object);
 
@@ -210,6 +213,10 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
 {
 	auto object = loadFromJson(data);
 	object->id = ArtifactID(index);
+	if (object->id < ArtifactID::ART_SELECTION)
+		object->iconIndex = object->id;
+	else
+		object->iconIndex = object->id + 2;
 
 	assert(artifacts[index] == nullptr); // ensure that this id was not loaded before
 	artifacts[index] = object;
@@ -236,7 +243,6 @@ CArtifact * CArtHandler::loadFromJson(const JsonNode & node)
 	art->eventText   = text["event"].String();
 
 	const JsonNode & graphics = node["graphics"];
-	art->iconIndex = graphics["iconIndex"].Float();
 	art->image = graphics["image"].String();
 
 	if (!graphics["large"].isNull())

+ 1 - 1
lib/CArtHandler.h

@@ -51,7 +51,7 @@ public:
 	std::string image;
 	std::string large; // big image for cutom artifacts, used in drag & drop
 	std::string advMapDef; //used for adventure map object
-	si32 iconIndex; //TODO: handle automatically
+	si32 iconIndex;
 
 	const std::string &Name() const; //getter
 	const std::string &Description() const; //getter

+ 4 - 3
lib/CCreatureHandler.cpp

@@ -282,8 +282,6 @@ std::vector<JsonNode> CCreatureHandler::loadLegacyData(size_t dataSize)
 
 		JsonNode data;
 
-		data["graphics"]["iconIndex"].Float() = h3Data.size() + 2; // +2 for empty\selection images
-
 		data["name"]["singular"].String() =  parser.readString();
 		data["name"]["plural"].String() =  parser.readString();
 
@@ -326,6 +324,7 @@ void CCreatureHandler::loadObject(std::string scope, std::string name, const Jso
 {
 	auto object = loadFromJson(data);
 	object->idNumber = CreatureID(creatures.size());
+	object->iconIndex = object->idNumber + 2;
 
 	creatures.push_back(object);
 
@@ -341,6 +340,7 @@ void CCreatureHandler::loadObject(std::string scope, std::string name, const Jso
 {
 	auto object = loadFromJson(data);
 	object->idNumber = CreatureID(index);
+	object->iconIndex = object->idNumber + 2;
 
 	assert(creatures[index] == nullptr); // ensure that this id was not loaded before
 	creatures[index] = object;
@@ -584,7 +584,8 @@ void CCreatureHandler::loadJsonAnimation(CCreature * cre, const JsonNode & graph
 	cre->animation.missleFrameAngles = missile["frameAngles"].convertTo<std::vector<double> >();
 
 	cre->advMapDef = graphics["map"].String();
-	cre->iconIndex = graphics["iconIndex"].Float();
+	cre->smallIconName = graphics["iconSmall"].String();
+	cre->largeIconName = graphics["iconLarge"].String();
 }
 
 void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & config)

+ 5 - 1
lib/CCreatureHandler.h

@@ -49,6 +49,10 @@ public:
 	std::string advMapDef; //for new creatures only, image for adventure map
 	si32 iconIndex; // index of icon in files like twcrport
 
+	/// names of files with appropriate icons. Used only during loading
+	std::string smallIconName;
+	std::string largeIconName;
+
 	struct CreatureAnimation
 	{
 		double timeBetweenFidgets, walkAnimationTime, attackAnimationTime, flightAnimationDistance;
@@ -123,7 +127,7 @@ public:
 			& fightValue & AIValue & growth & hordeGrowth
 			& ammMin & ammMax & level
 			& abilityText & animDefName & advMapDef;
-		h & iconIndex;
+		h & iconIndex & smallIconName & largeIconName;
 
 		h & idNumber & faction & sounds & animation;
 

+ 2 - 3
lib/CHeroHandler.cpp

@@ -228,7 +228,6 @@ CHero * CHeroHandler::loadFromJson(const JsonNode & node)
 	hero->specTooltip = node["texts"]["specialty"]["tooltip"].String();
 	hero->specDescr   = node["texts"]["specialty"]["description"].String();
 
-	hero->imageIndex = node["images"]["index"].Float();
 	hero->iconSpecSmall = node["images"]["specialtySmall"].String();
 	hero->iconSpecLarge = node["images"]["specialtyLarge"].String();
 	hero->portraitSmall = node["images"]["small"].String();
@@ -439,8 +438,6 @@ std::vector<JsonNode> CHeroHandler::loadLegacyData(size_t dataSize)
 		heroData["texts"]["specialty"]["tooltip"].String() = specParser.readString();
 		heroData["texts"]["specialty"]["description"].String() = specParser.readString();
 
-		heroData["images"]["index"].Float() = i;
-
 		for(int x=0;x<3;x++)
 		{
 			JsonNode armySlot;
@@ -463,6 +460,7 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod
 {
 	auto object = loadFromJson(data);
 	object->ID = heroes.size();
+	object->imageIndex = heroes.size() + 8; // 2 special frames + some extra portraits
 
 	heroes.push_back(object);
 
@@ -473,6 +471,7 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod
 {
 	auto object = loadFromJson(data);
 	object->ID = index;
+	object->imageIndex = index;
 
 	assert(heroes[index] == nullptr); // ensure that this id was not loaded before
 	heroes[index] = object;

+ 2 - 2
lib/CObjectHandler.cpp

@@ -70,11 +70,11 @@ static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const u
 	IObjectInterface::cb->sendAndApply(&iw);
 }
 
-static void showInfoDialog(const ObjectInstanceID heroID, const ui32 txtID, const ui16 soundID)
+/*static void showInfoDialog(const ObjectInstanceID heroID, const ui32 txtID, const ui16 soundID)
 {
 	const PlayerColor playerID = IObjectInterface::cb->getOwner(heroID);
 	showInfoDialog(playerID,txtID,soundID);
-}
+}*/
 
 static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 soundID)
 {

+ 46 - 23
lib/CTownHandler.cpp

@@ -428,32 +428,33 @@ void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source)
 	pos[1]  = JsonToPoint(source["static"]["background"]);
 }
 
-void CTownHandler::loadClientData(CTown &town, const JsonNode & source)
+static void readIcon(JsonNode source, std::string & small, std::string & large)
 {
-	town.clientInfo.icons[0][0] = source["icons"]["village"]["normal"].Float();
-	town.clientInfo.icons[0][1] = source["icons"]["village"]["built"].Float();
-	town.clientInfo.icons[1][0] = source["icons"]["fort"]["normal"].Float();
-	town.clientInfo.icons[1][1] = source["icons"]["fort"]["built"].Float();
-
-	town.clientInfo.hallBackground = source["hallBackground"].String();
-	town.clientInfo.musicTheme = source["musicTheme"].String();
-	town.clientInfo.townBackground = source["townBackground"].String();
-	town.clientInfo.guildWindow = source["guildWindow"].String();
-	town.clientInfo.buildingsIcons = source["buildingsIcons"].String();
-
-	town.clientInfo.advMapVillage = source["adventureMap"]["village"].String();
-	town.clientInfo.advMapCastle  = source["adventureMap"]["castle"].String();
-	town.clientInfo.advMapCapitol = source["adventureMap"]["capitol"].String();
-
-	const JsonNode *value = &source["adventureMap"]["dwellings"];
-	if (!value->isNull())
+	if (source.getType() == JsonNode::DATA_STRUCT) // don't crash on old format
 	{
-		BOOST_FOREACH (const JsonNode &d, value->Vector())
-		{
-			town.dwellings.push_back (d["graphics"].String());
-			town.dwellingNames.push_back (d["name"].String());
-		}
+		small = source["small"].String();
+		large = source["large"].String();
 	}
+}
+
+void CTownHandler::loadClientData(CTown &town, const JsonNode & source)
+{
+	CTown::ClientInfo & info = town.clientInfo;
+
+	readIcon(source["icons"]["village"]["normal"], info.iconSmall[0][0], info.iconLarge[0][0]);
+	readIcon(source["icons"]["village"]["built"], info.iconSmall[0][1], info.iconLarge[0][1]);
+	readIcon(source["icons"]["fort"]["normal"], info.iconSmall[1][0], info.iconLarge[1][0]);
+	readIcon(source["icons"]["fort"]["built"], info.iconSmall[1][1], info.iconLarge[1][1]);
+
+	info.hallBackground = source["hallBackground"].String();
+	info.musicTheme = source["musicTheme"].String();
+	info.townBackground = source["townBackground"].String();
+	info.guildWindow = source["guildWindow"].String();
+	info.buildingsIcons = source["buildingsIcons"].String();
+
+	info.advMapVillage = source["adventureMap"]["village"].String();
+	info.advMapCastle  = source["adventureMap"]["castle"].String();
+	info.advMapCapitol = source["adventureMap"]["capitol"].String();
 
 	loadTownHall(town,   source["hallSlots"]);
 	loadStructures(town, source["structures"]);
@@ -525,6 +526,12 @@ void CTownHandler::loadTown(CTown &town, const JsonNode & source)
 		});
 	}
 
+	BOOST_FOREACH (const JsonNode &d, source["adventureMap"]["dwellings"].Vector())
+	{
+		town.dwellings.push_back (d["graphics"].String());
+		town.dwellingNames.push_back (d["name"].String());
+	}
+
 	loadBuildings(town, source["buildings"]);
 	loadClientData(town,source);
 }
@@ -597,6 +604,14 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 {
 	auto object = loadFromJson(data);
 	object->index = factions.size();
+	if (object->town)
+	{
+		auto & info = object->town->clientInfo;
+		info.icons[0][0] = 8 + object->index * 4 + 0;
+		info.icons[0][1] = 8 + object->index * 4 + 1;
+		info.icons[1][0] = 8 + object->index * 4 + 2;
+		info.icons[1][1] = 8 + object->index * 4 + 3;
+	}
 
 	factions.push_back(object);
 
@@ -607,6 +622,14 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 {
 	auto object = loadFromJson(data);
 	object->index = index;
+	if (object->town)
+	{
+		auto & info = object->town->clientInfo;
+		info.icons[0][0] = (GameConstants::F_NUMBER + object->index) * 2 + 0;
+		info.icons[0][1] = (GameConstants::F_NUMBER + object->index) * 2 + 1;
+		info.icons[1][0] = object->index * 2 + 0;
+		info.icons[1][1] = object->index * 2 + 1;
+	}
 
 	assert(factions[index] == nullptr); // ensure that this id was not loaded before
 	factions[index] = object;

+ 4 - 2
lib/CTownHandler.h

@@ -163,6 +163,8 @@ public:
 
 		//icons [fort is present?][build limit reached?] -> index of icon in def files
 		int icons[2][2];
+		std::string iconSmall[2][2]; /// icon names used during loading
+		std::string iconLarge[2][2];
 
 		std::string musicTheme;
 		std::string townBackground;
@@ -187,8 +189,8 @@ public:
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
-			h & icons & musicTheme & townBackground & guildWindow & buildingsIcons & hallBackground & hallSlots & structures;
-			h & advMapVillage & advMapCastle & advMapCapitol;
+			h & icons & iconSmall & iconLarge & musicTheme & townBackground & guildWindow & buildingsIcons & hallBackground;
+			h & advMapVillage & advMapCastle & advMapCapitol & hallSlots & structures;
 			h & siegePrefix & siegePositions & siegeShooter & siegeShooterCropHeight;
 		}
 	} clientInfo;