mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Implemented object schema. Commiting current progress before sync with
upstream.
This commit is contained in:
		| @@ -8,7 +8,8 @@ | ||||
| 		"creature"   : 150, | ||||
| 		"faction"    : 9, | ||||
| 		"hero"       : 156, | ||||
|                 "spell"      : 81, | ||||
| 		"spell"      : 81, | ||||
| 		"object"     : 256, | ||||
| 		"mapVersion" : 28 // max supported version, SoD | ||||
| 	}, | ||||
|  | ||||
|   | ||||
| @@ -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" } | ||||
| } | ||||
|   | ||||
| @@ -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" } | ||||
| } | ||||
|   | ||||
| @@ -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" },	 | ||||
| } | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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); | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -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)); | ||||
|   | ||||
| @@ -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; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user