浏览代码

- h3 hero classes and artifacts can be modified via json
- commander artifacts are now merged into main artifacts.json, todo - split 2.5k file into something manageable

Ivan Savenko 12 年之前
父节点
当前提交
5082dafd62
共有 10 个文件被更改,包括 487 次插入300 次删除
  1. 20 0
      client/CMT.cpp
  2. 89 9
      config/artifacts.json
  3. 0 81
      config/commanders.json
  4. 2 2
      config/factions/inferno.json
  5. 180 36
      config/heroClasses.json
  6. 147 128
      lib/CArtHandler.cpp
  7. 15 10
      lib/CArtHandler.h
  8. 32 27
      lib/CHeroHandler.cpp
  9. 2 2
      lib/CHeroHandler.h
  10. 0 5
      lib/StringConstants.h

+ 20 - 0
client/CMT.cpp

@@ -262,6 +262,26 @@ int main(int argc, char** argv)
 
 	preinitDLL(::console, logfile);
 
+	// Some basic data validation to produce better error messages in cases of incorrect install
+	auto testFile = [](std::string filename, std::string message) -> bool
+	{
+		if (CResourceHandler::get()->existsResource(ResourceID(filename)))
+			return true;
+
+		tlog1 << "Error: " << message << " was not found!\n";
+		return false;
+	};
+
+	if (!testFile("DATA/HELP.TXT", "Heroes III data") &&
+	    !testFile("DATA/ZELP.TXT", "In the Wake of Gods data") &&
+	    !testFile("MODS/VCMI/MOD.JSON", "VCMI mod") &&
+	    !testFile("DATA/StackQueueBgBig.PCX", "VCMI data"))
+		exit(1); // These are unrecoverable errors
+
+	// these two are optional + some installs have them on CD and not in data directory
+	testFile("VIDEO/GOOD1A.SMK", "campaign movies");
+	testFile("SOUNDS/G1A.WAV", "campaign music"); //technically not a music but voiced intro sounds
+
 	settings.init();
 	conf.init();
 	tlog0 <<"Loading settings: "<<pomtime.getDiff() << std::endl;

+ 89 - 9
config/artifacts.json

@@ -2356,7 +2356,17 @@
 				}
 			],
 			"id" : 146,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
+			"growing":
+			{
+				"bonusesPerLevel":
+				[
+					{
+						"level": 6,
+						"bonus": ["PRIMARY_SKILL", 1, "primSkill.attack", 0]
+					}
+				]
+			}
 		},
 		"mithrilMail":
 		{
@@ -2368,7 +2378,17 @@
 				}
 			],
 			"id" : 147,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
+			"growing":
+			{
+				"bonusesPerLevel":
+				[
+					{
+						"level": 1,
+						"bonus": ["STACK_HEALTH", 1, 0, 0]
+					}
+				]
+			}
 		},
 		"swordOfSharpness":
 		{
@@ -2381,7 +2401,17 @@
 				}
 			],
 			"id" : 148,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
+			"growing":
+			{
+				"bonusesPerLevel":
+				[
+					{
+						"level": 1,
+						"bonus": ["CREATURE_DAMAGE", 1, 0, 0]
+					}
+				]
+			}
 		},
 		"helmOfImmortality": //TODO: implement
 		{
@@ -2398,7 +2428,17 @@
 				}
 			],
 			"id" : 150,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
+			"growing":
+			{
+				"bonusesPerLevel":
+				[
+					{
+						"level": 10,
+						"bonus": ["CREATURE_ENCHANT_POWER", 1, 0, 0]
+					}
+				]
+			}
 		},
 		"bootsOfHaste":
 		{
@@ -2410,17 +2450,46 @@
 				}
 			],
 			"id" : 151,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
+			"growing":
+			{
+				"bonusesPerLevel":
+				[
+					{
+						"level": 10,
+						"bonus": ["STACKS_SPEED", 1, 0, 0]
+					}
+				]
+			}
 		},
 		"bowOfSeeking":
 		{
 			"id" : 152,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
+			"growing":
+			{
+				"id": 152, //bow of seeking
+				"thresholdBonuses":
+				[
+					{
+						"level": 5,
+						"bonus": ["SHOOTER", 0, 0, 0]
+					},
+					{
+						"level": 25,
+						"bonus": ["NO_WALL_PENALTY", 0, 0, 0]
+					},
+					{
+						"level": 50,
+						"bonus": ["NO_DISTANCE_PENALTY", 0, 0, 0]
+					}
+				]
+			}
 		},
 		"dragonEyeRing": //TODO: implement
 		{
 			"id" : 153,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
 		},
 		"hardenedShield":
 		{
@@ -2433,7 +2502,18 @@
 				}
 			],
 			"id" : 154,
-			"type" : ["COMMANDER"]
+			"type" : ["COMMANDER"],
+			"growing":
+			{
+				"id": 153, //hardened shield
+				"bonusesPerLevel":
+				[
+					{
+						"level": 6,
+						"bonus": ["PRIMARY_SKILL", 1, "primSkill.defence", 0]
+					}
+				]
+			}
 		},
 		"slavasRingOfPower": //TODO: implement if possible
 		{
@@ -2530,4 +2610,4 @@
 			"type" : ["HERO"]
 		}
 	}
-}
+}

+ 0 - 81
config/commanders.json

@@ -37,86 +37,5 @@
 		{"ability": ["JOUSTING", 0, 0, 0 ], "skills": [3, 4]},
 		{"ability": ["DEATH_STARE", 1, 1, 0 ], "skills": [3,5]},
 		{"ability": ["FLYING", 0, 0, 0 ], "skills": [4,5]}
-	],
-	"artifacts":
-	[
-		{
-			"id": 146, //axe of smashing
-			"bonusesPerLevel":
-			[
-				{
-					"level": 6,
-					"bonus": ["PRIMARY_SKILL", 1, "primSkill.attack", 0]
-				}
-			]
-		},
-		{
-			"id": 147, //mithril mail
-			"bonusesPerLevel":
-			[
-				{
-					"level": 1,
-					"bonus": ["STACK_HEALTH", 1, 0, 0]
-				}
-			]
-		},
-		{
-			"id": 148, //sword of sharpness
-			"bonusesPerLevel":
-			[
-				{
-					"level": 1,
-					"bonus": ["CREATURE_DAMAGE", 1, 0, 0]
-				}
-			]
-		},
-		{
-			"id": 150, //pendant of sorcery
-			"bonusesPerLevel":
-			[
-				{
-					"level": 10,
-					"bonus": ["CREATURE_ENCHANT_POWER", 1, 0, 0]
-				}
-			]
-		},
-		{
-			"id": 151, //boots of haste
-			"bonusesPerLevel":
-			[
-				{
-					"level": 10,
-					"bonus": ["STACKS_SPEED", 1, 0, 0]
-				}
-			]
-		},
-		{
-			"id": 152, //bow of seeking
-			"thresholdBonuses":
-			[
-				{
-					"level": 5,
-					"bonus": ["SHOOTER", 0, 0, 0]
-				},
-				{
-					"level": 25,
-					"bonus": ["NO_WALL_PENALTY", 0, 0, 0]
-				},
-				{
-					"level": 50,
-					"bonus": ["NO_DISTANCE_PENALTY", 0, 0, 0]
-				}
-			]
-		},
-		{
-			"id": 153, //hardened shield
-			"bonusesPerLevel":
-			[
-				{
-					"level": 6,
-					"bonus": ["PRIMARY_SKILL", 1, "primSkill.defence", 0]
-				}
-			]
-		}
 	]
 }

+ 2 - 2
config/factions/inferno.json

@@ -168,12 +168,12 @@
 				{ "id" : 15, "requires" : [ 14 ] },
 				{ "id" : 16 },
 				{ "id" : 18, "upgrades" : 30 },
-				{ "id" : 19, "upgrades" : 37, "requires" : [ 18, 19 ], "mode" : "auto" },
+				{ "id" : 19, "upgrades" : 37, "requires" : [ 18 ], "mode" : "auto" },
 				{ "id" : 21, "requires" : [ 7 ] },
 				{ "id" : 22, "requires" : [ 8 ] },
 				{ "id" : 23, "requires" : [ 0 ] },
 				{ "id" : 24, "upgrades" : 32 },
-				{ "id" : 25, "upgrades" : 39, "requires" : [ 24, 25 ], "mode" : "auto" },
+				{ "id" : 25, "upgrades" : 39, "requires" : [ 24 ], "mode" : "auto" },
 				{ "id" : 26, "mode" : "grail"},
 				{ "id" : 27, "requires" : [ 11 ], "mode" : "auto" },
 				{ "id" : 28, "requires" : [ 12 ], "mode" : "auto" },

+ 180 - 36
config/heroClasses.json

@@ -1,38 +1,182 @@
 {
-	// battle animations for heroes, ordered by faction
-	"heroBattleAnim" :
-	[
-		{ "male" : "CH00.DEF",  "female" : "CH01.DEF" },
-		{ "male" : "CH02.DEF",  "female" : "CH03.DEF" },
-		{ "male" : "CH05.DEF",  "female" : "CH04.DEF" },
-		{ "male" : "CH06.DEF",  "female" : "CH07.DEF" },
-		{ "male" : "CH08.DEF",  "female" : "CH09.DEF" },
-		{ "male" : "CH010.DEF", "female" : "CH11.DEF" },
-		{ "male" : "CH013.DEF", "female" : "CH012.DEF" },
-		{ "male" : "CH014.DEF", "female" : "CH015.DEF" },
-		{ "male" : "CH16.DEF",  "female" : "CH17.DEF" }
-	],
-
-	// map animations for heroes, ordered by hero class
-	"heroMapAnim" :
-	[
-		"AH00_.def",
-		"AH01_.def",
-		"AH02_.def",
-		"AH03_.def",
-		"AH04_.def",
-		"AH05_.def",
-		"AH06_.def",
-		"AH07_.def",
-		"AH08_.def",
-		"AH09_.def",
-		"AH10_.def",
-		"AH11_.def",
-		"AH12_.def",
-		"AH13_.def",
-		"AH14_.def",
-		"AH15_.def",
-		"AH16_.def",
-		"AH17_.def"
-	]
+	"knight":
+	{
+		"id": 0,
+		"faction" : "castle",
+		"animation":
+		{
+			"battle" : { "male" : "CH00.DEF",  "female" : "CH01.DEF" },
+			"map":     { "male" : "AH00_.def", "female" : "AH00_.def" }
+		}
+	},
+	"cleric" :
+	{
+		"id": 1,
+		"faction" : "castle",
+		"animation":
+		{
+			"battle" : { "male" : "CH00.DEF",  "female" : "CH01.DEF" },
+			"map":     { "male" : "AH01_.def", "female" : "AH01_.def" }
+		}
+	},
+	"ranger" :
+	{
+		"id": 2,
+		"faction" : "rampart",
+		"animation":
+		{
+			"battle" : { "male" : "CH02.DEF",  "female" : "CH03.DEF" },
+			"map":     { "male" : "AH02_.def", "female" : "AH02_.def" }
+		}
+	},
+	"druid" :
+	{
+		"id": 3,
+		"faction" : "rampart",
+		"animation":
+		{
+			"battle" : { "male" : "CH02.DEF",  "female" : "CH03.DEF" },
+			"map":     { "male" : "AH03_.def", "female" : "AH03_.def" }
+		}
+	},
+	"alchemist" :
+	{
+		"id": 4,
+		"faction" : "tower",
+		"animation":
+		{
+			"battle" : { "male" : "CH05.DEF",  "female" : "CH04.DEF" },
+			"map":     { "male" : "AH04_.def", "female" : "AH04_.def" }
+		}
+	},
+	"wizard" :
+	{
+		"id": 5,
+		"faction" : "tower",
+		"animation":
+		{
+			"battle" : { "male" : "CH05.DEF",  "female" : "CH04.DEF" },
+			"map":     { "male" : "AH05_.def", "female" : "AH05_.def" }
+		}
+	},
+	"demoniac" :
+	{
+		"id": 6,
+		"faction" : "inferno",
+		"animation":
+		{
+			"battle" : { "male" : "CH06.DEF",  "female" : "CH07.DEF" },
+			"map":     { "male" : "AH06_.def", "female" : "AH06_.def" }
+		}
+	},
+	"heretic" :
+	{
+		"id": 7,
+		"faction" : "inferno",
+		"animation":
+		{
+			"battle" : { "male" : "CH06.DEF",  "female" : "CH07.DEF" },
+			"map":     { "male" : "AH07_.def", "female" : "AH07_.def" }
+		}
+	},
+	"deathknight" :
+	{
+		"id": 8,
+		"faction" : "necropolis",
+		"animation":
+		{
+			"battle" : { "male" : "CH08.DEF",  "female" : "CH09.DEF" },
+			"map":     { "male" : "AH08_.def", "female" : "AH08_.def" }
+		}
+	},
+	"necromancer" :
+	{
+		"id": 9,
+		"faction" : "necropolis",
+		"animation":
+		{
+			"battle" : { "male" : "CH08.DEF",  "female" : "CH09.DEF" },
+			"map":     { "male" : "AH09_.def", "female" : "AH09_.def" }
+		}
+	},
+	"warlock" :
+	{
+		"id": 10,
+		"faction" : "dungeon",
+		"animation":
+		{
+			"battle" : { "male" : "CH010.DEF", "female" : "CH11.DEF" },
+			"map":     { "male" : "AH10_.def", "female" : "AH10_.def" }
+		}
+	},
+	"overlord" :
+	{
+		"id": 11,
+		"faction" : "dungeon",
+		"animation":
+		{
+			"battle" : { "male" : "CH010.DEF", "female" : "CH11.DEF" },
+			"map":     { "male" : "AH11_.def", "female" : "AH11_.def" }
+		}
+	},
+	"barbarian" :
+	{
+		"id": 12,
+		"faction" : "stronghold",
+		"animation":
+		{
+			"battle" : { "male" : "CH013.DEF", "female" : "CH012.DEF" },
+			"map":     { "male" : "AH12_.def", "female" : "AH12_.def" }
+		}
+	},
+	"battlemage" :
+	{
+		"id": 13,
+		"faction" : "stronghold",
+		"animation":
+		{
+			"battle" : { "male" : "CH013.DEF", "female" : "CH012.DEF" },
+			"map":     { "male" : "AH13_.def", "female" : "AH13_.def" }
+		}
+	},
+	"beastmaster" :
+	{
+		"id": 14,
+		"faction" : "fortress",
+		"animation":
+		{
+			"battle" : { "male" : "CH014.DEF", "female" : "CH015.DEF" },
+			"map":     { "male" : "AH14_.def", "female" : "AH14_.def" }
+		}
+	},
+	"witch" :
+	{
+		"id": 15,
+		"faction" : "fortress",
+		"animation":
+		{
+			"battle" : { "male" : "CH014.DEF", "female" : "CH015.DEF" },
+			"map":     { "male" : "AH15_.def", "female" : "AH15_.def" }
+		}
+	},
+	"planeswalker" :
+	{
+		"id": 16,
+		"faction" : "conflux",
+		"animation":
+		{
+			"battle" : { "male" : "CH16.DEF",  "female" : "CH17.DEF" },
+			"map":     { "male" : "AH16_.def", "female" : "AH16_.def" }
+		}
+	},
+	"elementalist" :
+	{
+		"id": 17,
+		"faction" : "conflux",
+		"animation":
+		{
+			"battle" : { "male" : "CH16.DEF",  "female" : "CH17.DEF" },
+			"map":     { "male" : "AH17_.def", "female" : "AH17_.def" }
+		}
+	}
 }

+ 147 - 128
lib/CArtHandler.cpp

@@ -24,40 +24,27 @@ using namespace boost::assign;
  */
 
 extern boost::rand48 ran;
-
-const std::map<std::string, CArtifact::EartClass> artifactClassMap = boost::assign::map_list_of
-	("TREASURE", CArtifact::ART_TREASURE)
-	("MINOR", CArtifact::ART_MINOR)
-	("MAJOR", CArtifact::ART_MAJOR)
-	("RELIC", CArtifact::ART_RELIC)
-	("SPECIAL", CArtifact::ART_SPECIAL);
-
-#define ART_BEARER(x) ( #x, ArtBearer::x )
-	const std::map<std::string, int> artifactBearerMap = boost::assign::map_list_of ART_BEARER_LIST;
-#undef ART_BEARER
-
-#define ART_POS(x) ( #x, ArtifactPosition::x )
-
-const std::map<std::string, ArtifactPosition> artifactPositionMap = boost::assign::map_list_of
-	ART_POS(HEAD)
-	ART_POS(SHOULDERS)
-	ART_POS(NECK)
-	ART_POS(RIGHT_HAND)
-	ART_POS(LEFT_HAND)
-	ART_POS(TORSO)
-	ART_POS(RIGHT_RING)
-	ART_POS(LEFT_RING)
-	ART_POS(FEET)
-	ART_POS(MISC1)
-	ART_POS(MISC2)
-	ART_POS(MISC3)
-	ART_POS(MISC4)
-	ART_POS(MISC5)
-	ART_POS(MACH1)
-	ART_POS(MACH2)
-	ART_POS(MACH3)
-	ART_POS(MACH4)
-	ART_POS(SPELLBOOK); //no need to specify commander / stack position?
+// Note: list must match entries in ArtTraits.txt
+#define ART_POS_LIST    \
+	ART_POS(SPELLBOOK)  \
+	ART_POS(MACH4)      \
+	ART_POS(MACH3)      \
+	ART_POS(MACH2)      \
+	ART_POS(MACH1)      \
+	ART_POS(MISC5)      \
+	ART_POS(MISC4)      \
+	ART_POS(MISC3)      \
+	ART_POS(MISC2)      \
+	ART_POS(MISC1)      \
+	ART_POS(FEET)       \
+	ART_POS(LEFT_RING)  \
+	ART_POS(RIGHT_RING) \
+	ART_POS(TORSO)      \
+	ART_POS(LEFT_HAND)  \
+	ART_POS(RIGHT_HAND) \
+	ART_POS(NECK)       \
+	ART_POS(SHOULDERS)  \
+	ART_POS(HEAD);
 
 const std::string & CArtifact::Name() const
 {
@@ -166,15 +153,12 @@ void CArtHandler::load(bool onlyTxt)
 	if (onlyTxt)
 		return; // looks to be broken anyway...
 
-	std::vector<ui16> artSlots;
-	artSlots += 17, 16, 15, 14, 13, 18, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0;
-
-	growingArtifacts += ArtifactID::AXE_OF_SMASHING, ArtifactID::MITHRIL_MAIL,
-		ArtifactID::SWORD_OF_SHARPNESS, ArtifactID::PENDANT_OF_SORCERY, ArtifactID::BOOTS_OF_HASTE,
-		ArtifactID::BOW_OF_SEEKING, ArtifactID::DRAGON_EYE_RING;
+	#define ART_POS(x) (  #x)
+	const std::vector<std::string> artSlots = boost::assign::list_of ART_POS_LIST;
+	#undef ART_POS
 
-	static std::map<char, CArtifact::EartClass> classes =
-	  map_list_of('S',CArtifact::ART_SPECIAL)('T',CArtifact::ART_TREASURE)('N',CArtifact::ART_MINOR)('J',CArtifact::ART_MAJOR)('R',CArtifact::ART_RELIC);
+	static std::map<char, std::string> classes =
+	  map_list_of('S',"SPECIAL")('T',"TREASURE")('N',"MINOR")('J',"MAJOR")('R',"RELIC");
 
 	CLegacyConfigParser parser("DATA/ARTRAITS.TXT");
 	CLegacyConfigParser events("DATA/ARTEVENT.TXT");
@@ -184,74 +168,54 @@ void CArtHandler::load(bool onlyTxt)
 
 	std::map<ui32,ui8>::iterator itr;
 
-	for (ArtifactID i=ArtifactID(0); i<GameConstants::ARTIFACTS_QUANTITY; i.advance(1))
+	std::vector<JsonNode> h3Data;
+
+	for (size_t i = 0; i < GameConstants::ARTIFACTS_QUANTITY; i++)
 	{
-		CArtifact *art;
-		if (vstd::contains (growingArtifacts, i))
-		{
-			art = new CGrowingArtifact();
-		}
-		else
-		{
-			art = new CArtifact();
-		}
-		art->id=i;
-		art->iconIndex=i;
-		art->name = parser.readString();
-		art->eventText = events.readString();
-		events.endLine();
+		JsonNode artData;
 
-		art->price= parser.readNumber();
+		artData["graphics"]["iconIndex"].Float() = i;
+		artData["text"]["name"].String() = parser.readString();
+		artData["text"]["event"].String() = events.readString();
+		artData["value"].Float() = parser.readNumber();
 
 		for(int j=0; j<artSlots.size(); j++)
 		{
 			if(parser.readString() == "x")
-				art->possibleSlots[ArtBearer::HERO].push_back(ArtifactPosition(artSlots[j]));
-		}
-		art->aClass = classes[parser.readString()[0]];
-
-		//load description and remove quotation marks
-		art->description = parser.readString();
-
-		parser.endLine();
-
-		if(onlyTxt) // FIXME: pointer to art will be lost. Bug?
-			continue;
-
-		artifacts.push_back(art);
-	}
-
-	if (VLC->modh->modules.COMMANDERS)
-	{ //TODO: move all artifacts config to separate json file
-		const JsonNode config(ResourceID("config/commanders.json"));
-		BOOST_FOREACH(const JsonNode &artifact, config["artifacts"].Vector())
-		{
-			auto ga = dynamic_cast <CGrowingArtifact *>(artifacts[artifact["id"].Float()].get());
-			BOOST_FOREACH (auto b, artifact["bonusesPerLevel"].Vector())
-			{
-				ga->bonusesPerLevel.push_back (std::pair <ui16, Bonus> (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector())));
-			}
-			BOOST_FOREACH (auto b, artifact["thresholdBonuses"].Vector())
 			{
-				ga->thresholdBonuses.push_back (std::pair <ui16, Bonus> (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector())));
+				artData["slot"].Vector().push_back(JsonNode());
+				artData["slot"].Vector().back().String() = artSlots.at(j);
 			}
 		}
+		artData["class"].String() = classes[parser.readString()[0]];
+		artData["text"]["description"].String() = parser.readString();
+
+		parser.endLine();
+		events.endLine();
+		h3Data.push_back(artData);
 	}
 
-	if(onlyTxt)
-		return;
+	artifacts.resize(GameConstants::ARTIFACTS_QUANTITY);
 
 	JsonNode config(ResourceID("config/artifacts.json"));
 
 	BOOST_FOREACH(auto & node, config["artifacts"].Struct())
 	{
 		int numeric = node.second["id"].Float();
-		CArtifact * art = artifacts[numeric];
+		JsonNode & artData = h3Data[numeric];
+		JsonUtils::merge(artData, node.second);
 
-		loadArtifactJson(art, node.second);
+		artifacts[numeric] = loadArtifact(artData);
+		artifacts[numeric]->id = ArtifactID(numeric);
 
 		VLC->modh->identifiers.registerObject ("artifact." + node.first, numeric);
 	}
+
+	for (size_t i=0; i < artifacts.size(); i++)
+	{
+		if (artifacts[i] == nullptr)
+			tlog0 << "Warning: artifact with id " << i << " is missing!\n";
+	}
 }
 
 void CArtHandler::load(std::string objectID, const JsonNode & node)
@@ -266,7 +230,16 @@ void CArtHandler::load(std::string objectID, const JsonNode & node)
 
 CArtifact * CArtHandler::loadArtifact(const JsonNode & node)
 {
-	CArtifact * art = new CArtifact;
+	CArtifact * art;
+
+	if (!VLC->modh->modules.COMMANDERS || node["growing"].isNull())
+		art = new CArtifact();
+	else
+	{
+		CGrowingArtifact * growing = new CGrowingArtifact();
+		loadGrowingArt(growing, node);
+		art = growing;
+	}
 
 	const JsonNode & text = node["text"];
 	art->name        = text["name"].String();
@@ -283,59 +256,90 @@ CArtifact * CArtHandler::loadArtifact(const JsonNode & node)
 	art->advMapDef = graphics["map"].String();
 
 	art->price = node["value"].Float();
+
+	loadSlots(art, node);
+	loadClass(art, node);
+	loadType(art, node);
+	loadComponents(art, node);
+
+	BOOST_FOREACH (auto b, node["bonuses"].Vector())
 	{
-		auto it = artifactClassMap.find (node["class"].String());
-		if (it != artifactClassMap.end())
+		auto bonus = JsonUtils::parseBonus (b);
+		bonus->sid = art->id;
+		art->addNewBonus (bonus);
+	}
+	return art;
+}
+
+void CArtHandler::addSlot(CArtifact * art, const std::string & slotID)
+{
+#define ART_POS(x) ( #x, ArtifactPosition::x )
+	static const std::map<std::string, ArtifactPosition> artifactPositionMap = boost::assign::map_list_of ART_POS_LIST;
+#undef ART_POS
+
+	if (slotID == "MISC")
+	{
+		art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5;
+	}
+	else if (slotID == "RING")
+	{
+		art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING;
+	}
+	else
+	{
+		auto it = artifactPositionMap.find (slotID);
+		if (it != artifactPositionMap.end())
 		{
-			art->aClass = it->second;
+			auto slot = it->second;
+			art->possibleSlots[ArtBearer::HERO].push_back (slot);
 		}
 		else
-		{
-			tlog2 << "Warning! Artifact rarity " << node["class"].String() << " not recognized!";
-			art->aClass = CArtifact::ART_SPECIAL;
-		}
+			tlog2 << "Warning! Artifact slot " << slotID << " not recognized!";
 	}
+}
 
+void CArtHandler::loadSlots(CArtifact * art, const JsonNode & node)
+{
 	if (!node["slot"].isNull()) //we assume non-hero slots are irrelevant?
 	{
-		std::string slotName = node["slot"].String();
-		if (slotName == "MISC")
-		{
-			//unfortunatelly slot ids aare not continuous
-			art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::MISC1, ArtifactPosition::MISC2, ArtifactPosition::MISC3, ArtifactPosition::MISC4, ArtifactPosition::MISC5;
-		}
-		else if (slotName == "RING")
-		{
-			art->possibleSlots[ArtBearer::HERO] += ArtifactPosition::LEFT_RING, ArtifactPosition::RIGHT_RING;
-		}
+		if (node["slot"].getType() == JsonNode::DATA_STRING)
+			addSlot(art, node["slot"].String());
 		else
 		{
-
-			auto it = artifactPositionMap.find (slotName);
-			if (it != artifactPositionMap.end())
-			{
-				auto slot = it->second;
-				art->possibleSlots[ArtBearer::HERO].push_back (slot);
-			}
-			else
-				tlog2 << "Warning! Artifact slot " << node["slot"].String() << " not recognized!";
+			BOOST_FOREACH (const JsonNode & slot, node["slot"].Vector())
+				addSlot(art, slot.String());
 		}
 	}
-
-	loadArtifactJson(art, node);
-
-	return art;
 }
 
-void CArtHandler::loadArtifactJson(CArtifact * art, const JsonNode & artifact)
+void CArtHandler::loadClass(CArtifact * art, const JsonNode & node)
 {
-	BOOST_FOREACH (auto b, artifact["bonuses"].Vector())
+	static const std::map<std::string, CArtifact::EartClass> artifactClassMap = boost::assign::map_list_of
+		("TREASURE", CArtifact::ART_TREASURE)
+		("MINOR", CArtifact::ART_MINOR)
+		("MAJOR", CArtifact::ART_MAJOR)
+		("RELIC", CArtifact::ART_RELIC)
+		("SPECIAL", CArtifact::ART_SPECIAL);
+
+	auto it = artifactClassMap.find (node["class"].String());
+	if (it != artifactClassMap.end())
 	{
-		auto bonus = JsonUtils::parseBonus (b);
-		bonus->sid = art->id;
-		art->addNewBonus (bonus);
+		art->aClass = it->second;
 	}
-	BOOST_FOREACH (const JsonNode & b, artifact["type"].Vector())
+	else
+	{
+		tlog2 << "Warning! Artifact rarity " << node["class"].String() << " not recognized!";
+		art->aClass = CArtifact::ART_SPECIAL;
+	}
+}
+
+void CArtHandler::loadType(CArtifact * art, const JsonNode & node)
+{
+#define ART_BEARER(x) ( #x, ArtBearer::x )
+	static const std::map<std::string, int> artifactBearerMap = boost::assign::map_list_of ART_BEARER_LIST;
+#undef ART_BEARER
+
+	BOOST_FOREACH (const JsonNode & b, node["type"].Vector())
 	{
 		auto it = artifactBearerMap.find (b.String());
 		if (it != artifactBearerMap.end())
@@ -356,11 +360,14 @@ void CArtHandler::loadArtifactJson(CArtifact * art, const JsonNode & artifact)
 		else
 			tlog2 << "Warning! Artifact type " << b.String() << " not recognized!";
 	}
+}
 
-	if (!artifact["components"].isNull())
+void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
+{
+	if (!node["components"].isNull())
 	{
 		art->constituents.reset(new std::vector<CArtifact *>());
-		BOOST_FOREACH (auto component, artifact["components"].Vector())
+		BOOST_FOREACH (auto component, node["components"].Vector())
 		{
 			VLC->modh->identifiers.requestIdentifier("artifact." + component.String(), [=](si32 id)
 			{
@@ -373,6 +380,18 @@ void CArtHandler::loadArtifactJson(CArtifact * art, const JsonNode & artifact)
 	}
 }
 
+void CArtHandler::loadGrowingArt(CGrowingArtifact * art, const JsonNode & node)
+{
+	BOOST_FOREACH (auto b, node["growing"]["bonusesPerLevel"].Vector())
+	{
+		art->bonusesPerLevel.push_back (std::pair <ui16, Bonus> (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector())));
+	}
+	BOOST_FOREACH (auto b, node["growing"]["thresholdBonuses"].Vector())
+	{
+		art->thresholdBonuses.push_back (std::pair <ui16, Bonus> (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector())));
+	}
+}
+
 ArtifactID CArtHandler::creatureToMachineID(CreatureID id)
 {
 	int dif = 142;

+ 15 - 10
lib/CArtHandler.h

@@ -59,8 +59,8 @@ public:
 	bool isBig () const;
 
 	int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other
-	std::string nodeName() const OVERRIDE;
-	void addNewBonus(Bonus *b) OVERRIDE;
+	std::string nodeName() const override;
+	void addNewBonus(Bonus *b) override;
 
 	virtual void levelUpArtifact (CArtifactInstance * art){};
 
@@ -112,7 +112,7 @@ public:
 
 	//CArtifactInstance(int aid);
 
-	std::string nodeName() const OVERRIDE;
+	std::string nodeName() const override;
 	void deserializationFix();
 	void setType(CArtifact *Art);
 
@@ -161,11 +161,11 @@ public:
 
 	std::vector<ConstituentInfo> constituentsInfo;
 
-	bool canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved = false) const OVERRIDE;
-	bool canBeDisassembled() const OVERRIDE;
-	void putAt(ArtifactLocation al) OVERRIDE;
-	void removeFrom(ArtifactLocation al) OVERRIDE;
-	bool isPart(const CArtifactInstance *supposedPart) const OVERRIDE;
+	bool canBePutAt(const CArtifactSet *artSet, ArtifactPosition slot, bool assumeDestRemoved = false) const override;
+	bool canBeDisassembled() const override;
+	void putAt(ArtifactLocation al) override;
+	void removeFrom(ArtifactLocation al) override;
+	bool isPart(const CArtifactInstance *supposedPart) const override;
 
 	void createConstituents();
 	void addAsConstituent(CArtifactInstance *art, ArtifactPosition slot);
@@ -187,6 +187,13 @@ public:
 
 class DLL_LINKAGE CArtHandler //handles artifacts
 {
+	void addSlot(CArtifact * art, const std::string & slotID);
+	void loadSlots(CArtifact * art, const JsonNode & node);
+	void loadClass(CArtifact * art, const JsonNode & node);
+	void loadType(CArtifact * art, const JsonNode & node);
+	void loadComponents(CArtifact * art, const JsonNode & node);
+	void loadGrowingArt(CGrowingArtifact * art, const JsonNode & node);
+
 	void giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype = -1, Bonus::ValueType valType = Bonus::BASE_NUMBER, shared_ptr<ILimiter> limiter = shared_ptr<ILimiter>(), int additionalinfo = 0);
 	void giveArtBonus(ArtifactID aid, Bonus::BonusType type, int val, int subtype, shared_ptr<IPropagator> propagator, int additionalinfo = 0);
 	void giveArtBonus(ArtifactID aid, Bonus *bonus);
@@ -204,8 +211,6 @@ public:
 	/// load one artifact from json config
 	CArtifact * loadArtifact(const JsonNode & node);
 
-	void loadArtifactJson(CArtifact * art, const JsonNode & node);
-
 	void addBonuses(CArtifact *art, const JsonNode &bonusList);
 
 	void fillList(std::vector<CArtifact*> &listToBeFilled, CArtifact::EartClass artifactClass); //fills given empty list with allowed artifacts of gibven class. No side effects

+ 32 - 27
lib/CHeroHandler.cpp

@@ -85,49 +85,54 @@ void CHeroClassHandler::load()
 	parser.endLine(); // header
 	parser.endLine();
 
+	std::vector<JsonNode> h3Data;
+
 	do
 	{
-		CHeroClass * hc = new CHeroClass;
+		JsonNode entry;
 
-		hc->name             = parser.readString();
-		hc->aggression       = parser.readNumber();
-		hc->id = heroClasses.size();
+		entry["name"].String() = parser.readString();
 
-		hc->primarySkillInitial   = parser.readNumArray<int>(GameConstants::PRIMARY_SKILLS);
-		hc->primarySkillLowLevel  = parser.readNumArray<int>(GameConstants::PRIMARY_SKILLS);
-		hc->primarySkillHighLevel = parser.readNumArray<int>(GameConstants::PRIMARY_SKILLS);
+		parser.readNumber(); // unused aggression
 
-		hc->secSkillProbability   = parser.readNumArray<int>(GameConstants::SKILL_QUANTITY);
+		for (size_t i=0; i < GameConstants::PRIMARY_SKILLS; i++)
+			entry["primarySkills"][PrimarySkill::names[i]].Float() = parser.readNumber();
 
-		for(int dd=0; dd<GameConstants::F_NUMBER; ++dd)
-		{
-			hc->selectionProbability[dd] = parser.readNumber();
-		}
+		for (size_t i=0; i < GameConstants::PRIMARY_SKILLS; i++)
+			entry["lowLevelChance"][PrimarySkill::names[i]].Float() = parser.readNumber();
 
-		VLC->modh->identifiers.requestIdentifier("faction." + ETownType::names[heroClasses.size()/2],
-		[=](si32 faction)
-		{
-			hc->faction = faction;
-		});
+		for (size_t i=0; i < GameConstants::PRIMARY_SKILLS; i++)
+			entry["highLevelChance"][PrimarySkill::names[i]].Float() = parser.readNumber();
+
+		for (size_t i=0; i < GameConstants::SKILL_QUANTITY; i++)
+			entry["secondarySkills"][NSecondarySkill::names[i]].Float() = parser.readNumber();
+
+		for(size_t i = 0; i < GameConstants::F_NUMBER; i++)
+			entry["tavern"][ETownType::names[i]].Float() = parser.readNumber();
 
-		heroClasses.push_back(hc);
-		VLC->modh->identifiers.registerObject("heroClass." + GameConstants::HERO_CLASSES_NAMES[hc->id], hc->id);
+		h3Data.push_back(entry);
 	}
 	while (parser.endLine() && !parser.isNextEntryEmpty());
 
-	const JsonNode & heroGraphics = JsonNode(ResourceID("config/heroClasses.json"));
+	JsonNode classConf = JsonNode(ResourceID("config/heroClasses.json"));
+	heroClasses.resize(GameConstants::F_NUMBER * 2);
 
-	for (size_t i=0; i<heroClasses.size(); i++)
+	BOOST_FOREACH(auto & node, classConf.Struct())
 	{
-		const JsonNode & battle = heroGraphics["heroBattleAnim"].Vector()[i/2];
+		int numeric = node.second["id"].Float();
+		JsonNode & classData = h3Data[numeric];
+		JsonUtils::merge(classData, node.second);
 
-		heroClasses[i]->imageBattleFemale = battle["female"].String();
-		heroClasses[i]->imageBattleMale = battle["male"].String();
+		heroClasses[numeric] = loadClass(classData);
+		heroClasses[numeric]->id = numeric;
 
-		const JsonNode & map = heroGraphics["heroMapAnim"].Vector()[i];
+		VLC->modh->identifiers.registerObject ("heroClass." + node.first, numeric);
+	}
 
-		heroClasses[i]->imageMapMale = map.String();
-		heroClasses[i]->imageMapFemale = map.String();
+	for (size_t i=0; i < heroClasses.size(); i++)
+	{
+		if (heroClasses[i] == nullptr)
+			tlog0 << "Warning: class with id " << i << " is missing!\n";
 	}
 }
 

+ 2 - 2
lib/CHeroHandler.h

@@ -98,7 +98,7 @@ class DLL_LINKAGE CHeroClass
 public:
 	std::string identifier;
 	std::string name; // translatable
-	double aggression;
+	//double aggression; // not used in vcmi.
 	TFaction faction;
 	ui8 id;
 
@@ -119,7 +119,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & identifier & name & faction & aggression;
+		h & identifier & name & faction;// & aggression;
 		h & primarySkillInitial   & primarySkillLowLevel;
 		h & primarySkillHighLevel & secSkillProbability;
 		h & selectionProbability;

+ 0 - 5
lib/StringConstants.h

@@ -26,11 +26,6 @@ namespace GameConstants
 	    "wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold", "mithril"
 	};
 
-	const std::string HERO_CLASSES_NAMES [F_NUMBER * 2] = {
-	    "knight",    "cleric",     "ranger",      "druid",       "alchemist",    "wizard",
-	    "demoniac",  "heretic",    "deathknight", "necromancer", "warlock",      "overlord",
-	    "barbarian", "battlemage", "beastmaster", "witch",       "planeswalker", "elementalist"
-	};
 	const std::string PLAYER_COLOR_NAMES [PlayerColor::PLAYER_LIMIT_I] = {
 		"red", "blue", "tan", "green", "orange", "purple", "teal", "pink"
 	};