Forráskód Böngészése

Implemented object schema. Commiting current progress before sync with
upstream.

Ivan Savenko 11 éve
szülő
commit
dc7f820161

+ 2 - 1
config/defaultMods.json

@@ -8,7 +8,8 @@
 		"creature"   : 150,
 		"faction"    : 9,
 		"hero"       : 156,
-                "spell"      : 81,
+		"spell"      : 81,
+		"object"     : 256,
 		"mapVersion" : 28 // max supported version, SoD
 	},
 

+ 108 - 108
config/objects/generic.json

@@ -2,132 +2,132 @@
 	/// These are objects that can not be configured, either due to
 	/// their hardcoded status or because they don't have any configurable functionality
 	
-	"altarOfSacrifice"				: { "id" :2,  "handler": "market" },
-	"tradingPost"					: { "id" :221, "handler": "market" },
-	"tradingPost"					: { "id" :99, "handler": "market" },
-	"freelancer'SGuild"				: { "id" :213, "handler": "market" },
+	"altarOfSacrifice"				: { "index" :2,  "handler": "market" },
+	"tradingPost"					: { "index" :221, "handler": "market" },
+	"tradingPostDUPLICATE"					: { "index" :99, "handler": "market" },
+	"freelancersGuild"				: { "index" :213, "handler": "market" },
 
-	"blackMarket"					: { "id" :7,  "handler": "blackMarket" },
+	"blackMarket"					: { "index" :7,  "handler": "blackMarket" },
 
-	"crypt"							: { "id" :84, "handler": "bank" },
-	"shipwreck"						: { "id" :85, "handler": "bank" },
-	"derelictShip"					: { "id" :24, "handler": "bank" },
-	"dragonUtopia"					: { "id" :25, "handler": "bank" },
+	"crypt"							: { "index" :84, "handler": "bank" },
+	"shipwreck"						: { "index" :85, "handler": "bank" },
+	"derelictShip"					: { "index" :24, "handler": "bank" },
+	"dragonUtopia"					: { "index" :25, "handler": "bank" },
 
-	"pandoraBox"					: { "id" :6,  "handler": "pandora" },
-	"event"							: { "id" :26, "handler": "event" },
+	"pandoraBox"					: { "index" :6,  "handler": "pandora" },
+	"event"							: { "index" :26, "handler": "event" },
 
-	"redwoodObservatory"			: { "id" :58, "handler": "observatory" },
-	"pillarOfFire"					: { "id" :60, "handler": "observatory" },
-	"coverOfDarkness"				: { "id" :15, "handler": "observatory" },
+	"redwoodObservatory"			: { "index" :58, "handler": "observatory" },
+	"pillarOfFire"					: { "index" :60, "handler": "observatory" },
+	"coverOfDarkness"				: { "index" :15, "handler": "observatory" },
 	
-	"subterraneanGate"				: { "id" :103, "handler": "teleport" },
-	"whirlpool"						: { "id" :111, "handler": "teleport" },
+	"subterraneanGate"				: { "index" :103, "handler": "teleport" },
+	"whirlpool"						: { "index" :111, "handler": "teleport" },
 
-	"refugeeCamp"					: { "id" :78, "handler": "dwelling" },
-	"warMachineFactory"				: { "id" :106, "handler": "dwelling" },
+	"refugeeCamp"					: { "index" :78, "handler": "dwelling" },
+	"warMachineFactory"				: { "index" :106, "handler": "dwelling" },
 
-	"shrineOfMagicLevel1"			: { "id" :88, "handler": "shrine" },
-	"shrineOfMagicLevel2"			: { "id" :89, "handler": "shrine" },
-	"shrineOfMagicLevel3"			: { "id" :90, "handler": "shrine" },
+	"shrineOfMagicLevel1"			: { "index" :88, "handler": "shrine" },
+	"shrineOfMagicLevel2"			: { "index" :89, "handler": "shrine" },
+	"shrineOfMagicLevel3"			: { "index" :90, "handler": "shrine" },
 
-	"eyeOfTheMagi"					: { "id" :27, "handler": "magi" },
-	"hutOfTheMagi"					: { "id" :37, "handler": "magi" },
+	"eyeOfTheMagi"					: { "index" :27, "handler": "magi" },
+	"hutOfTheMagi"					: { "index" :37, "handler": "magi" },
 
-	"lighthouse"					: { "id" :42, "handler": "lighthouse" },
-	"magicWell"						: { "id" :49, "handler": "magicWell" },
-	"obelisk"						: { "id" :57, "handler": "obelisk" },
-	"oceanBottle"					: { "id" :59, "handler": "sign" },
-	"prison"						: { "id" :62, "handler": "hero" },
-	"pyramid"						: { "id" :63, "handler": "pyramid" },
-	"scholar"						: { "id" :81, "handler": "scholar" },
-	"shipyard"						: { "id" :87, "handler": "shipyard" },
-	"sign"							: { "id" :91, "handler": "sign" },
-	"sirens"						: { "id" :92, "handler": "siren" },
-	"denOfThieves"					: { "id" :97, "handler": "denOfThieves" },
-	"university"					: { "id" :104, "handler": "university" },
-	"witchHut"						: { "id" :113, "handler": "witch" },
-	"questGuard"					: { "id" :215, "handler": "questGuard" },
+	"lighthouse"					: { "index" :42, "handler": "lighthouse" },
+	"magicWell"						: { "index" :49, "handler": "magicWell" },
+	"obelisk"						: { "index" :57, "handler": "obelisk" },
+	"oceanBottle"					: { "index" :59, "handler": "sign" },
+	"prison"						: { "index" :62, "handler": "hero" },
+	"pyramid"						: { "index" :63, "handler": "pyramid" },
+	"scholar"						: { "index" :81, "handler": "scholar" },
+	"shipyard"						: { "index" :87, "handler": "shipyard" },
+	"sign"							: { "index" :91, "handler": "sign" },
+	"sirens"						: { "index" :92, "handler": "siren" },
+	"denOfThieves"					: { "index" :97, "handler": "denOfThieves" },
+	"university"					: { "index" :104, "handler": "university" },
+	"witchHut"						: { "index" :113, "handler": "witch" },
+	"questGuard"					: { "index" :215, "handler": "questGuard" },
 
 	/// Random objects
-	"randomResource"				: { "id" :76, "handler": "resource" },
-	"randomTown"					: { "id" :77, "handler": "town" },
-	"randomHero"					: { "id" :70, "handler": "hero" },
+	"randomResource"				: { "index" :76, "handler": "resource" },
+	"randomTown"					: { "index" :77, "handler": "town" },
+	"randomHero"					: { "index" :70, "handler": "hero" },
 	
-	"randomDwelling"				: { "id" :216, "handler": "dwelling" },
+	"randomDwelling"				: { "index" :216, "handler": "dwelling" },
 
-	"randomArtifact"				: { "id" :65, "handler": "artifact" },
-	"randomArtifactTreasure"		: { "id" :66, "handler": "artifact" },
-	"randomArtifactMinor"			: { "id" :67, "handler": "artifact" },
-	"randomArtifactMajor"			: { "id" :68, "handler": "artifact" },
-	"randomArtifactRelic"			: { "id" :69, "handler": "artifact" },
+	"randomArtifact"				: { "index" :65, "handler": "artifact" },
+	"randomArtifactTreasure"		: { "index" :66, "handler": "artifact" },
+	"randomArtifactMinor"			: { "index" :67, "handler": "artifact" },
+	"randomArtifactMajor"			: { "index" :68, "handler": "artifact" },
+	"randomArtifactRelic"			: { "index" :69, "handler": "artifact" },
 
-	"randomMonster"					: { "id" :71, "handler": "monster" },
-	"randomMonsterLevel1"			: { "id" :72, "handler": "monster" },
-	"randomMonsterLevel2"			: { "id" :73, "handler": "monster" },
-	"randomMonsterLevel3"			: { "id" :74, "handler": "monster" },
-	"randomMonsterLevel4"			: { "id" :75, "handler": "monster" },
-	"randomMonsterLevel5"			: { "id" :162, "handler": "monster" },
-	"randomMonsterLevel6"			: { "id" :163, "handler": "monster" },
-	"randomMonsterLevel7"			: { "id" :164, "handler": "monster" },
+	"randomMonster"					: { "index" :71, "handler": "monster" },
+	"randomMonsterLevel1"			: { "index" :72, "handler": "monster" },
+	"randomMonsterLevel2"			: { "index" :73, "handler": "monster" },
+	"randomMonsterLevel3"			: { "index" :74, "handler": "monster" },
+	"randomMonsterLevel4"			: { "index" :75, "handler": "monster" },
+	"randomMonsterLevel5"			: { "index" :162, "handler": "monster" },
+	"randomMonsterLevel6"			: { "index" :163, "handler": "monster" },
+	"randomMonsterLevel7"			: { "index" :164, "handler": "monster" },
 
 	/// Classes without dedicated object
-	"hillFort"						: { "id" :35, "handler": "generic" },
-	"grail"							: { "id" :36, "handler": "generic" },
-	"tavern"						: { "id" :95, "handler": "generic" },
-	"sanctuary"						: { "id" :80, "handler": "generic" },
+	"hillFort"						: { "index" :35, "handler": "generic" },
+	"grail"							: { "index" :36, "handler": "generic" },
+	"tavern"						: { "index" :95, "handler": "generic" },
+	"sanctuary"						: { "index" :80, "handler": "generic" },
 
 	/// Passive objects, terrain overlays
-	"cursedGround"					: { "id" :21, "handler": "generic" },
-	"magicPlains"					: { "id" :46, "handler": "generic" },
-	"swampFoliage"					: { "id" :211, "handler": "generic" },
-	"cloverField"					: { "id" :222, "handler": "generic" },
-	"cursedGround"					: { "id" :223, "handler": "generic" },
-	"evilFog"						: { "id" :224, "handler": "generic" },
-	"favorableWinds"				: { "id" :225, "handler": "generic" },
-	"fieryFields"					: { "id" :226, "handler": "generic" },
-	"holyGround"					: { "id" :227, "handler": "generic" },
-	"lucidPools"					: { "id" :228, "handler": "generic" },
-	"magicClouds"					: { "id" :229, "handler": "generic" },
-	"magicPlains"					: { "id" :230, "handler": "generic" },
-	"rocklands"						: { "id" :231, "handler": "generic" }
+	"cursedGround"					: { "index" :21, "handler": "generic" },
+	"magicPlains"					: { "index" :46, "handler": "generic" },
+	"swampFoliage"					: { "index" :211, "handler": "generic" },
+	"cloverField"					: { "index" :222, "handler": "generic" },
+	"cursedGroundDUPLICATE"			: { "index" :223, "handler": "generic" },
+	"evilFog"						: { "index" :224, "handler": "generic" },
+	"favorableWinds"				: { "index" :225, "handler": "generic" },
+	"fieryFields"					: { "index" :226, "handler": "generic" },
+	"holyGround"					: { "index" :227, "handler": "generic" },
+	"lucidPools"					: { "index" :228, "handler": "generic" },
+	"magicClouds"					: { "index" :229, "handler": "generic" },
+	"magicPlainsDUPLICATE"			: { "index" :230, "handler": "generic" },
+	"rocklands"						: { "index" :231, "handler": "generic" },
 
 	/// Decorations
-	"cactus"						: { "id" :116, "handler": "generic" },
-	"canyon"						: { "id" :117, "handler": "generic" },
-	"crater"						: { "id" :118, "handler": "generic" },
-	"deadVegetation"				: { "id" :119, "handler": "generic" },
-	"flowers"						: { "id" :120, "handler": "generic" },
-	"frozenLake"					: { "id" :121, "handler": "generic" },
-	"hole"							: { "id" :124, "handler": "generic" },
-	"kelp"							: { "id" :125, "handler": "generic" },
-	"lake"							: { "id" :126, "handler": "generic" },
-	"lavaFlow"						: { "id" :127, "handler": "generic" },
-	"lavaLake"						: { "id" :128, "handler": "generic" },
-	"mushrooms"						: { "id" :129, "handler": "generic" },
-	"log"							: { "id" :130, "handler": "generic" },
-	"mandrake"						: { "id" :131, "handler": "generic" },
-	"moss"							: { "id" :132, "handler": "generic" },
-	"mound"							: { "id" :133, "handler": "generic" },
-	"mountain"						: { "id" :134, "handler": "generic" },
-	"oakTrees"						: { "id" :135, "handler": "generic" },
-	"outcropping"					: { "id" :136, "handler": "generic" },
-	"pineTrees"						: { "id" :137, "handler": "generic" },
-	"riverDelta"					: { "id" :143, "handler": "generic" },
-	"rock"							: { "id" :147, "handler": "generic" },
-	"sandDune"						: { "id" :148, "handler": "generic" },
-	"sandPit"						: { "id" :149, "handler": "generic" },
-	"shrub"							: { "id" :150, "handler": "generic" },
-	"skull"							: { "id" :151, "handler": "generic" },
-	"stump"							: { "id" :153, "handler": "generic" },
-	"trees"							: { "id" :155, "handler": "generic" },
-	"volcano"						: { "id" :158, "handler": "generic" },
-	"reef"							: { "id" :161, "handler": "generic" },
-	"lake"							: { "id" :177, "handler": "generic" },
-	"trees"							: { "id" :199, "handler": "generic" },
-	"desertHills"					: { "id" :206, "handler": "generic" },
-	"dirtHills"						: { "id" :207, "handler": "generic" },
-	"grassHills"					: { "id" :208, "handler": "generic" },
-	"roughHills"					: { "id" :209, "handler": "generic" },
-	"subterraneanRocks"				: { "id" :210, "handler": "generic" },
+	"cactus"						: { "index" :116, "handler": "generic" },
+	"canyon"						: { "index" :117, "handler": "generic" },
+	"crater"						: { "index" :118, "handler": "generic" },
+	"deadVegetation"				: { "index" :119, "handler": "generic" },
+	"flowers"						: { "index" :120, "handler": "generic" },
+	"frozenLake"					: { "index" :121, "handler": "generic" },
+	"hole"							: { "index" :124, "handler": "generic" },
+	"kelp"							: { "index" :125, "handler": "generic" },
+	"lake"							: { "index" :126, "handler": "generic" },
+	"lavaFlow"						: { "index" :127, "handler": "generic" },
+	"lavaLake"						: { "index" :128, "handler": "generic" },
+	"mushrooms"						: { "index" :129, "handler": "generic" },
+	"log"							: { "index" :130, "handler": "generic" },
+	"mandrake"						: { "index" :131, "handler": "generic" },
+	"moss"							: { "index" :132, "handler": "generic" },
+	"mound"							: { "index" :133, "handler": "generic" },
+	"mountain"						: { "index" :134, "handler": "generic" },
+	"oakTrees"						: { "index" :135, "handler": "generic" },
+	"outcropping"					: { "index" :136, "handler": "generic" },
+	"pineTrees"						: { "index" :137, "handler": "generic" },
+	"riverDelta"					: { "index" :143, "handler": "generic" },
+	"rock"							: { "index" :147, "handler": "generic" },
+	"sandDune"						: { "index" :148, "handler": "generic" },
+	"sandPit"						: { "index" :149, "handler": "generic" },
+	"shrub"							: { "index" :150, "handler": "generic" },
+	"skull"							: { "index" :151, "handler": "generic" },
+	"stump"							: { "index" :153, "handler": "generic" },
+	"trees"							: { "index" :155, "handler": "generic" },
+	"volcano"						: { "index" :158, "handler": "generic" },
+	"reef"							: { "index" :161, "handler": "generic" },
+	"lakeDUPLICATE"					: { "index" :177, "handler": "generic" },
+	"treesDUPLICATE"				: { "index" :199, "handler": "generic" },
+	"desertHills"					: { "index" :206, "handler": "generic" },
+	"dirtHills"						: { "index" :207, "handler": "generic" },
+	"grassHills"					: { "index" :208, "handler": "generic" },
+	"roughHills"					: { "index" :209, "handler": "generic" },
+	"subterraneanRocks"				: { "index" :210, "handler": "generic" }
 }

+ 26 - 26
config/objects/moddables.json

@@ -5,7 +5,7 @@
 	
 	// subtype: artifact ID
 	"artifact" : {
-		"id" :5, 
+		"index" :5, 
 		"handler": "artifact",
 		"base" : {
 			"base" : {
@@ -17,7 +17,7 @@
 	
 	// subtype: hero CLASS (not hero).
 	"hero" : {
-		"id" :34,
+		"index" :34,
 		"handler": "hero",
 		"base" : {
 			"base" : {
@@ -29,8 +29,8 @@
 	
 	// subtype: creatures
 	"monster" : {
-		"id" :54,
-		"handler": "monster"
+		"index" :54,
+		"handler": "monster",
 		"base" : {
 			"base" : {
 				"visitableFrom" : [ "+++", "+-+", "+++" ],
@@ -41,7 +41,7 @@
 
 	// subtype: resource ID
 	"resource" : {
-		"id" :79,
+		"index" :79,
 		"handler": "resource",
 		"base" : {
 			"base" : {
@@ -53,7 +53,7 @@
 	
 	// subtype: faction
 	"town" : {
-		"id" :98,
+		"index" :98,
 		"handler": "town",
 		"base" : {
 			"base" : {
@@ -72,7 +72,7 @@
 
 	// subtype: one of 3 possible boats
 	"boat" : {
-		"id" :8,
+		"index" :8,
 		"handler": "boat",
 		"base" : {
 			"base" : {
@@ -83,41 +83,41 @@
 	},
 
 	// subtype: color of guard
-	"borderGuard"					: { "id" :9,  "handler": "borderGuard" },
-	"borderGate"					: { "id" :212, "handler": "borderGate" },
-	"keymasterTent"					: { "id" :10, "handler": "keymaster" },
+	"borderGuard"					: { "index" :9,  "handler": "borderGuard" },
+	"borderGate"					: { "index" :212, "handler": "borderGate" },
+	"keymasterTent"					: { "index" :10, "handler": "keymaster" },
 
 	// subtype: different content
-	"creatureBank"					: { "id" :16, "handler": "bank" },
+	"creatureBank"					: { "index" :16, "handler": "bank" },
 
 	// subtype: different revealed areas
-	"cartographer"					: { "id" :13, "handler": "cartographer" },
+	"cartographer"					: { "index" :13, "handler": "cartographer" },
 
 	// subtype: 0 = normal, 1 = anti-magic
-	"garrisonHorizontal"			: { "id" :33, "handler": "garrison" },
-	"garrisonVertical"				: { "id" :219, "handler": "garrison" },
+	"garrisonHorizontal"			: { "index" :33, "handler": "garrison" },
+	"garrisonVertical"				: { "index" :219, "handler": "garrison" },
 
 	// Subtype: paired monoliths
-	"monolithOneWayEntrance"		: { "id" :43, "handler": "teleport" },
-	"monolithOneWayExit"			: { "id" :44, "handler": "teleport" },
-	"monolithTwoWay"				: { "id" :45, "handler": "teleport" },
+	"monolithOneWayEntrance"		: { "index" :43, "handler": "teleport" },
+	"monolithOneWayExit"			: { "index" :44, "handler": "teleport" },
+	"monolithTwoWay"				: { "index" :45, "handler": "teleport" },
 	
 	// subtype: resource ID
-	"mine"							: { "id" :53, "handler": "mine" },
-	"abandonedMine"					: { "id" :220, "handler": "mine" },
+	"mine"							: { "index" :53, "handler": "mine" },
+	"abandonedMine"					: { "index" :220, "handler": "mine" },
 
 	// subtype: different appearance. That's all?
-	"seerHut"						: { "id" :83, "handler": "seerHut" },
+	"seerHut"						: { "index" :83, "handler": "seerHut" },
 
 	// subtype: level
-	"randomDwellingLvl"				: { "id" :217, "handler": "dwelling" },
+	"randomDwellingLvl"				: { "index" :217, "handler": "dwelling" },
 	
 	// subtype: faction ID
-	"randomDwellingFaction"			: { "id" :218, "handler": "dwelling" },
+	"randomDwellingFaction"			: { "index" :218, "handler": "dwelling" },
 
 	// subtype: not well defined, describes various dwellings that can be placed as random
 	"creatureGeneratorCommon" : {
-		"id" :17,
+		"index" :17,
 		"handler": "dwelling",
 		"base" : {
 			"base" : {
@@ -128,9 +128,9 @@
 	},
 	
 	// subtype: unique special dwellings - golem factory, elemental conflux
-	"creatureGeneratorSpecial"		: { "id" :20, "handler": "dwelling" },
+	"creatureGeneratorSpecial"		: { "index" :20, "handler": "dwelling" },
 	
 	// don't have subtypes (at least now), but closely connected to this objects
-	"spellScroll"					: { "id" :93, "handler": "artifact" },
-	"heroPlaceholder"				: { "id" :214, "handler": "heroPlaceholder" }
+	"spellScroll"					: { "index" :93, "handler": "artifact" },
+	"heroPlaceholder"				: { "index" :214, "handler": "heroPlaceholder" }
 }

+ 35 - 35
config/objects/rewardable.json

@@ -1,44 +1,44 @@
 {
 	/// These are objects that covered by concept of "configurable object"
 	/// Most or even all of their configuration located in this file
-	"magicSpring"					: { "id" :48, "handler": "magicSpring" },
+	"magicSpring"					: { "index" :48, "handler": "magicSpring" },
 
-	"mysticalGarden"				: { "id" :55, "handler": "oncePerWeek" },
-	"windmill"						: { "id" :112, "handler": "oncePerWeek" },
-	"waterWheel"					: { "id" :109, "handler": "oncePerWeek" },
+	"mysticalGarden"				: { "index" :55, "handler": "oncePerWeek" },
+	"windmill"						: { "index" :112, "handler": "oncePerWeek" },
+	"waterWheel"					: { "index" :109, "handler": "oncePerWeek" },
 	
-	"leanTo"						: { "id" :39, "handler": "onceVisitable" },
-	"corpse"						: { "id" :22, "handler": "onceVisitable" },
-	"wagon"							: { "id" :105, "handler": "onceVisitable" },
-	"warriorTomb"					: { "id" :108, "handler": "onceVisitable" },
+	"leanTo"						: { "index" :39, "handler": "onceVisitable" },
+	"corpse"						: { "index" :22, "handler": "onceVisitable" },
+	"wagon"							: { "index" :105, "handler": "onceVisitable" },
+	"warriorTomb"					: { "index" :108, "handler": "onceVisitable" },
 
-	"campfire"						: { "id" :12, "handler": "pickable" },
-	"flotsam"						: { "id" :29, "handler": "pickable" },
-	"seaChest"						: { "id" :82, "handler": "pickable" },
-	"shipwreckSurvivor"				: { "id" :86, "handler": "pickable" },
-	"treasureChest"					: { "id" :101, "handler": "pickable" },
+	"campfire"						: { "index" :12, "handler": "pickable" },
+	"flotsam"						: { "index" :29, "handler": "pickable" },
+	"seaChest"						: { "index" :82, "handler": "pickable" },
+	"shipwreckSurvivor"				: { "index" :86, "handler": "pickable" },
+	"treasureChest"					: { "index" :101, "handler": "pickable" },
 
-	"arena"							: { "id" :4,  "handler": "oncePerHero" },
-	"marlettoTower"					: { "id" :23, "handler": "oncePerHero" },
-	"gardenOfRevelation"			: { "id" :32, "handler": "oncePerHero" },
-	"libraryOfEnlightenment"		: { "id" :41, "handler": "oncePerHero" },
-	"mercenaryCamp"					: { "id" :51, "handler": "oncePerHero" },
-	"starAxis"						: { "id" :61, "handler": "oncePerHero" },
-	"learningStone"					: { "id" :100, "handler": "oncePerHero" },
-	"treeOfKnowledge"				: { "id" :102, "handler": "oncePerHero" },
-	"schoolOfMagic"					: { "id" :47, "handler": "oncePerHero" },
-	"schoolOfWar"					: { "id" :107, "handler": "oncePerHero" },
+	"arena"							: { "index" :4,  "handler": "oncePerHero" },
+	"marlettoTower"					: { "index" :23, "handler": "oncePerHero" },
+	"gardenOfRevelation"			: { "index" :32, "handler": "oncePerHero" },
+	"libraryOfEnlightenment"		: { "index" :41, "handler": "oncePerHero" },
+	"mercenaryCamp"					: { "index" :51, "handler": "oncePerHero" },
+	"starAxis"						: { "index" :61, "handler": "oncePerHero" },
+	"learningStone"					: { "index" :100, "handler": "oncePerHero" },
+	"treeOfKnowledge"				: { "index" :102, "handler": "oncePerHero" },
+	"schoolOfMagic"					: { "index" :47, "handler": "oncePerHero" },
+	"schoolOfWar"					: { "index" :107, "handler": "oncePerHero" },
 	
-	"buoy"							: { "id" :11, "handler": "bonusingObject" },
-	"swanPond"						: { "id" :14, "handler": "bonusingObject" },
-	"faerieRing"					: { "id" :28, "handler": "bonusingObject" },
-	"fountainOfFortune"				: { "id" :30, "handler": "bonusingObject" },
-	"fountainOfYouth"				: { "id" :31, "handler": "bonusingObject" },
-	"idolOfFortune"					: { "id" :38, "handler": "bonusingObject" },
-	"mermaids"						: { "id" :52, "handler": "bonusingObject" },
-	"oasis"							: { "id" :56, "handler": "bonusingObject" },
-	"stables"						: { "id" :94, "handler": "bonusingObject" },
-	"temple"						: { "id" :96, "handler": "bonusingObject" },
-	"rallyFlag"						: { "id" :64, "handler": "bonusingObject" },
-	"wateringHole"					: { "id" :110, "handler": "bonusingObject" },	
+	"buoy"							: { "index" :11, "handler": "bonusingObject" },
+	"swanPond"						: { "index" :14, "handler": "bonusingObject" },
+	"faerieRing"					: { "index" :28, "handler": "bonusingObject" },
+	"fountainOfFortune"				: { "index" :30, "handler": "bonusingObject" },
+	"fountainOfYouth"				: { "index" :31, "handler": "bonusingObject" },
+	"idolOfFortune"					: { "index" :38, "handler": "bonusingObject" },
+	"mermaids"						: { "index" :52, "handler": "bonusingObject" },
+	"oasis"							: { "index" :56, "handler": "bonusingObject" },
+	"stables"						: { "index" :94, "handler": "bonusingObject" },
+	"temple"						: { "index" :96, "handler": "bonusingObject" },
+	"rallyFlag"						: { "index" :64, "handler": "bonusingObject" },
+	"wateringHole"					: { "index" :110, "handler": "bonusingObject" },	
 }

+ 1 - 9
config/schemas/objectTemplate.json

@@ -3,18 +3,10 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI map object template format",
 	"description" : "Description of map object tempate that describes appearence of object instance",
-	"required": ["basebase", "base", "animation", "mask" ],
+	"required": [ "animation", "mask" ],
 
 	"additionalProperties" : false,
 	"properties":{
-		"basebase": {
-			"type" : "number",
-			"description": "Base object type, e.g. town or hero"
-		},
-		"base": {
-			"type" : "number",
-			"description": "Object subtype, e.g. Castle, Rampart, Cleric, Demon"
-		},
 		"animation": {
 			"type" : "string",
 			"description": "Path to def file with animation of this object",

+ 1 - 0
lib/CArtHandler.cpp

@@ -658,6 +658,7 @@ void CArtHandler::afterLoadFinalization()
 
 			// add new template.
 			// Necessary for objects added via mods that don't have any templates in H3
+			VLC->objtypeh->createObject(art->Name(), JsonNode(), Obj::ARTIFACT, art->id.num);
 			VLC->objtypeh->getHandlerFor(Obj::ARTIFACT, art->id)->addTemplate(base);
 		}
 	}

+ 77 - 33
lib/CObjectClassesHandler.cpp

@@ -333,11 +333,12 @@ bool ObjectTemplate::canBePlacedAt(ETerrainType terrain) const
 
 CObjectClassesHandler::CObjectClassesHandler()
 {
-	// list of all known handlers, hardcoded for now since the only way to add new objects is via C++ code
-	handlerConstructors["configurable"] = std::make_shared<CObjectWithRewardConstructor>;
-
+#define SET_HANDLER_CLASS(STRING, CLASSNAME) handlerConstructors[STRING] = std::make_shared<CLASSNAME>;
 #define SET_HANDLER(STRING, TYPENAME) handlerConstructors[STRING] = std::make_shared<CDefaultObjectTypeHandler<TYPENAME> >
 
+	// list of all known handlers, hardcoded for now since the only way to add new objects is via C++ code
+	SET_HANDLER_CLASS("configurable", CObjectWithRewardConstructor);
+
 	SET_HANDLER("", CGObjectInstance);
 	SET_HANDLER("generic", CGObjectInstance);
 
@@ -385,39 +386,34 @@ CObjectClassesHandler::CObjectClassesHandler()
 	SET_HANDLER("oncePerWeek", CGVisitableOPW);
 	SET_HANDLER("witch", CGWitchHut);
 
+#undef SET_HANDLER_CLASS
 #undef SET_HANDLER
 }
 
-static std::vector<JsonNode> readTextFile(std::string path)
+template<typename Container>
+void readTextFile(Container objects, std::string path)
 {
-	//TODO
+	CLegacyConfigParser parser(path);
+	size_t totalNumber = parser.readNumber(); // first line contains number of objects to read and nothing else
+	parser.endLine();
+
+	for (size_t i=0; i<totalNumber; i++)
+	{
+		ObjectTemplate templ;
+		templ.readTxt(parser);
+		parser.endLine();
+		typename Container::key_type key(templ.id.num, templ.subid);
+		objects.insert(std::make_pair(key, templ));
+	}
 }
 
 std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
 {
-	objects.resize(dataSize);
+	readTextFile(legacyTemplates, "Data/Objects.txt");
+	readTextFile(legacyTemplates, "Data/Heroes.txt");
 
 	std::vector<JsonNode> ret(dataSize);// create storage for 256 objects
-
-	auto parseFile = [&](std::string filename)
-	{
-		auto entries = readTextFile(filename);
-		for (JsonNode & entry : entries)
-		{
-			si32 id = entry["basebase"].Float();
-			si32 subid = entry["base"].Float();
-
-			entry.Struct().erase("basebase");
-			entry.Struct().erase("base");
-
-			if (ret[id].Vector().size() <= subid)
-				ret[id].Vector().resize(subid+1);
-			ret[id]["legacyTypes"].Vector()[subid][entry["animation"].String()].swap(entry);
-		}
-	};
-
-	//parseFile("Data/Objects.txt");
-	//parseFile("Data/Heroes.txt");
+	assert(dataSize == 256);
 
 	CLegacyConfigParser parser("Data/ObjNames.txt");
 	for (size_t i=0; i<256; i++)
@@ -428,16 +424,48 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
 	return ret;
 }
 
+/// selects preferred ID (or subID) for new object
+template<typename Map>
+si32 selectNextID(const JsonNode & fixedID, const Map & map, si32 defaultID)
+{
+	if (!fixedID.isNull() && fixedID.Float() < defaultID)
+		return fixedID.Float(); // H3M object with fixed ID
+
+	if (map.empty())
+		return defaultID; // no objects loaded, keep gap for H3M objects
+	if (map.rbegin()->first > defaultID)
+		return map.rbegin()->first + 1; // some modded objects loaded, return next available
+
+	return defaultID; // some H3M objects loaded, first modded found
+}
+
+void CObjectClassesHandler::loadObjectEntry(const JsonNode & entry, ObjectContainter * obj)
+{
+	auto handler = handlerConstructors.at(obj->handlerName)();
+	handler->init(entry);
+
+	si32 id = selectNextID(entry["index"], obj->objects, 256);
+	handler->setType(obj->id, id);
+
+	if (handler->getTemplates().empty())
+	{
+		auto range = legacyTemplates.equal_range(std::make_pair(obj->id, si32(entry["index"].Float())));
+		for (auto & templ : boost::make_iterator_range(range.first, range.second))
+			handler->addTemplate(templ.second);
+	}
+	obj->objects[id] = handler;
+}
+
 CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(const JsonNode & json)
 {
 	auto obj = new ObjectContainter();
 	obj->name = json["name"].String();
 	obj->handlerName = json["handler"].String();
-	obj->base = json["base"];
+	obj->base = json["base"]; // FIXME: when this data will be actually merged?
+	obj->id = selectNextID(json["index"], objects, 256);
 	for (auto entry : json["types"].Struct())
 	{
-		auto handler = handlerConstructors.at(obj->handlerName)();
-		handler->init(entry.second);
+		loadObjectEntry(entry.second, obj);
 	}
 	return obj;
 }
@@ -445,8 +473,7 @@ CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(co
 void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
 {
 	auto object = loadFromJson(data);
-	object->id = objects.size();
-	objects.push_back(object);
+	objects[object->id] = object;
 
 	VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
 }
@@ -454,7 +481,6 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
 void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
 {
 	auto object = loadFromJson(data);
-	object->id = index;
 
 	assert(objects[index] == nullptr); // ensure that this id was not loaded before
 	objects[index] = object;
@@ -462,6 +488,18 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
 	VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
 }
 
+void CObjectClassesHandler::createObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID)
+{
+	assert(objects.count(ID));
+	if (subID)
+	{
+		assert(objects.at(ID)->objects.count(subID.get()) == 0);
+		assert(config["index"].isNull());
+		config["index"].Float() = subID.get();
+	}
+	loadObjectEntry(config, objects[ID]);
+}
+
 std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
 {
 	return std::vector<bool>(); //TODO?
@@ -469,15 +507,21 @@ std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
 
 TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype) const
 {
-	if (objects.size() > type)
+	if (objects.count(type))
 	{
 		if (objects.at(type)->objects.count(subtype))
 			return objects.at(type)->objects.at(subtype);
 	}
+	logGlobal->errorStream() << "Failed to find object of type " << type << ":" << subtype;
 	assert(0); // FIXME: throw error?
 	return nullptr;
 }
 
+void CObjectClassesHandler::afterLoadFinalization()
+{
+	legacyTemplates.clear(); // whatever left there is no longer needed
+}
+
 std::string CObjectClassesHandler::getObjectName(si32 type) const
 {
 	assert(objects.count(type));

+ 11 - 5
lib/CObjectClassesHandler.h

@@ -110,12 +110,10 @@ class AObjectTypeHandler
 
 	std::vector<ObjectTemplate> templates;
 protected:
-	void setType(si32 type, si32 subtype);
 
 	virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const;
 public:
-	/// returns true if type is not configurable and new objects can be created without valid config
-	virtual bool confFree();
+	void setType(si32 type, si32 subtype);
 
 	/// loads templates from Json structure using fields "base" and "templates"
 	virtual void init(const JsonNode & input);
@@ -193,12 +191,18 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
 		}
 	};
 
+	typedef std::multimap<std::pair<si32, si32>, ObjectTemplate> TTemplatesContainer;
+
 	/// list of object handlers, each of them handles only one type
-	std::vector<ObjectContainter * > objects;
+	std::map<si32, ObjectContainter * > objects;
 
 	/// map that is filled during contruction with all known handlers. Not serializeable
 	std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
 
+	/// container with H3 templates, used only during loading
+	TTemplatesContainer legacyTemplates;
+
+	void loadObjectEntry(const JsonNode & entry, ObjectContainter * obj);
 	ObjectContainter * loadFromJson(const JsonNode & json);
 public:
 	CObjectClassesHandler();
@@ -208,7 +212,9 @@ public:
 	virtual void loadObject(std::string scope, std::string name, const JsonNode & data);
 	virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index);
 
-	virtual void afterLoadFinalization(){};
+	void createObject(std::string name, JsonNode config, si32 ID, boost::optional<si32> subID = boost::optional<si32>());
+
+	virtual void afterLoadFinalization();
 
 	virtual std::vector<bool> getDefaultAllowed() const;