Selaa lähdekoodia

- console logger by default uses same format as previously (no extra data)
- a lot of changes in configs;
- - update to creature format - abilities are now json structure
- - multiple bugfixes revealed by validation
- made schemas a bit more strict
- creatures data can be replaced via mods
- it is possible to validate vcmi configs using schemas (disabled)

Ivan Savenko 12 vuotta sitten
vanhempi
sitoutus
0cf969d508

+ 1 - 1
CMakeLists.txt

@@ -133,7 +133,7 @@ endif()
 # For apple this files will be already inside vcmiclient bundle
 if (NOT APPLE)
 	# copy whole directory but .svn control files and user-specific settings.json
-	install(DIRECTORY config DESTINATION ${DATA_DIR} PATTERN ".svn" EXCLUDE PATTERN "settings.json" EXCLUDE)
+	install(DIRECTORY config DESTINATION ${DATA_DIR} PATTERN ".svn" EXCLUDE)
 	# copy vcmi mod along with all its content
 	install(DIRECTORY Mods/vcmi DESTINATION ${DATA_DIR}/Mods PATTERN ".svn" EXCLUDE)
 	# copy only mod.json for WoG

+ 1 - 2
client/battle/CBattleAnimations.cpp

@@ -139,8 +139,7 @@ bool CDefenceAnimation::init()
 			int attackerAnimType = owner->creAnims[attacker->ID]->getType();
 			if( ( attackerAnimType == CCreatureAnim::ATTACK_UP ||
 			    attackerAnimType == CCreatureAnim::ATTACK_FRONT ||
-			    attackerAnimType == CCreatureAnim::ATTACK_DOWN ) &&
-			    owner->creAnims[attacker->ID]->getFrame() < attacker->getCreature()->animation.attackClimaxFrame )
+			    attackerAnimType == CCreatureAnim::ATTACK_DOWN ) )
 				return false;
 		}
 

+ 49 - 16
config/artifacts.json

@@ -2194,7 +2194,7 @@
 					"valueType" : "BASE_NUMBER"
 				},
 				{
-					"additionalInfo" : 2,
+					"addInfo" : 2,
 					"subtype" : "spell.lightningBolt",
 					"type" : "ENCHANTER",
 					"val" : 0,
@@ -2256,28 +2256,28 @@
 					"valueType" : "PERCENT_TO_BASE"
 				},
 				{
-					"additionalInfo" : 1,
+					"addInfo" : 1,
 					"subtype" : "spell.age",
 					"type" : "SPELL_BEFORE_ATTACK",
 					"val" : 50,
 					"valueType" : "BASE_NUMBER"
 				},
 				{
-					"additionalInfo" : 1,
+					"addInfo" : 1,
 					"subtype" : "spell.berserk",
 					"type" : "SPELL_AFTER_ATTACK",
 					"val" : 50,
 					"valueType" : "BASE_NUMBER"
 				},
 				{
-					"additionalInfo" : 1,
+					"addInfo" : 1,
 					"subtype" : "spell.poison",
 					"type" : "SPELL_AFTER_ATTACK",
 					"val" : 50,
 					"valueType" : "BASE_NUMBER"
 				},
 				{
-					"additionalInfo" : 1,
+					"addInfo" : 1,
 					"subtype" : "spell.disruptingRay",
 					"type" : "SPELL_AFTER_ATTACK",
 					"val" : 50,
@@ -2363,7 +2363,12 @@
 				[
 					{
 						"level": 6,
-						"bonus": ["PRIMARY_SKILL", 1, "primSkill.attack", 0]
+						"bonus": 
+						{
+							"type" : "PRIMARY_SKILL",
+							"subtype" : "primSkill.attack",
+							"val" : 1
+						}
 					}
 				]
 			}
@@ -2385,7 +2390,11 @@
 				[
 					{
 						"level": 1,
-						"bonus": ["STACK_HEALTH", 1, 0, 0]
+						"bonus": 
+						{
+							"type" : "STACK_HEALTH",
+							"val" : 1
+						}
 					}
 				]
 			}
@@ -2408,7 +2417,11 @@
 				[
 					{
 						"level": 1,
-						"bonus": ["CREATURE_DAMAGE", 1, 0, 0]
+						"bonus": 
+						{
+							"type" : "CREATURE_DAMAGE",
+							"val" : 1
+						}
 					}
 				]
 			}
@@ -2435,7 +2448,11 @@
 				[
 					{
 						"level": 10,
-						"bonus": ["CREATURE_ENCHANT_POWER", 1, 0, 0]
+						"bonus": 
+						{
+							"type" : "CREATURE_ENCHANT_POWER",
+							"val" : 1
+						}
 					}
 				]
 			}
@@ -2457,7 +2474,11 @@
 				[
 					{
 						"level": 10,
-						"bonus": ["STACKS_SPEED", 1, 0, 0]
+						"bonus": 
+						{
+							"type" : "STACKS_SPEED",
+							"val" : 1
+						}
 					}
 				]
 			}
@@ -2468,20 +2489,28 @@
 			"type" : ["COMMANDER"],
 			"growing":
 			{
-				"id": 152, //bow of seeking
 				"thresholdBonuses":
 				[
 					{
 						"level": 5,
-						"bonus": ["SHOOTER", 0, 0, 0]
+						"bonus":
+						{
+							"type" : "SHOOTER"
+						}
 					},
 					{
 						"level": 25,
-						"bonus": ["NO_WALL_PENALTY", 0, 0, 0]
+						"bonus": 
+						{
+							"type" : "NO_WALL_PENALTY"
+						}
 					},
 					{
 						"level": 50,
-						"bonus": ["NO_DISTANCE_PENALTY", 0, 0, 0]
+						"bonus": 
+						{
+							"type" : "NO_DISTANCE_PENALTY"
+						}
 					}
 				]
 			}
@@ -2505,12 +2534,16 @@
 			"type" : ["COMMANDER"],
 			"growing":
 			{
-				"id": 153, //hardened shield
 				"bonusesPerLevel":
 				[
 					{
 						"level": 6,
-						"bonus": ["PRIMARY_SKILL", 1, "primSkill.defence", 0]
+						"bonus": 
+						{
+							"type" : "PRIMARY_SKILL",
+							"subtype" : "primSkill.defence",
+							"val" : 1
+						}
 					}
 				]
 			}

+ 95 - 33
config/creatures/castle.json

@@ -5,7 +5,13 @@
 		"level": 1,
 		"faction": "castle",
 		"upgrades": ["halberdier"],
-		"abilities": [ [ "CHARGE_IMMUNITY", 0, 0, 0 ] ], 		//pikeman immunity to Champion charge bonus
+		"abilities":
+		{
+			"cavalryChargeImmunity" :
+			{
+				"type" : "CHARGE_IMMUNITY"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CPKMAN.DEF"
@@ -24,7 +30,13 @@
 		"id": 1,
 		"level": 1,
 		"faction": "castle",
-		"abilities": [ [ "CHARGE_IMMUNITY", 0, 0, 0 ] ], 		//halberdier immunity to Champion charge bonus
+		"abilities":
+		{
+			"cavalryChargeImmunity" :
+			{
+				"type" : "CHARGE_IMMUNITY"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CHALBD.DEF"
@@ -50,8 +62,7 @@
 			"animation": "CLCBOW.DEF",
 			"missile" :
 			{
-				"projectile": "PLCBOWX.DEF",
-				"spinning": false
+				"projectile": "PLCBOWX.DEF"
 			}
 		},
 		"sound" :
@@ -69,20 +80,20 @@
 		"id": 3,
 		"level": 2,
 		"faction": "castle",
-		"abilities": [
-						{
-							"type": "ADDITIONAL_ATTACK",
-							"val" : 1,
-							"effectRange": "ONLY_DISTANCE_FIGHT"
-						}
-					],	
+		"abilities": {
+			"extraAttack" :
+			{
+				"type": "ADDITIONAL_ATTACK",
+				"val" : 1,
+				"effectRange": "ONLY_DISTANCE_FIGHT"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CHCBOW.DEF",
 			"missile" :
 			{
-				"projectile": "PLCBOWX.DEF",
-				"spinning": false
+				"projectile": "PLCBOWX.DEF"
 			}
 		},
 		"sound" :
@@ -100,7 +111,14 @@
 		"id": 4,
 		"level": 3,
 		"faction": "castle",
-		"abilities": [ [ "ADDITIONAL_RETALIATION", 1, 0, 0 ] ], 	//griffins retaliate twice
+		"abilities":
+		{
+			"extraRetaliation" :
+			{
+				"type" : "ADDITIONAL_RETALIATION",
+				"val" : 1
+			}
+		},
 		"upgrades": ["royalGriffin"],
 		"hasDoubleWeek": true,
 		"graphics" :
@@ -121,7 +139,13 @@
 		"id": 5,
 		"level": 3,
 		"faction": "castle",
-		"abilities": [ [ "UNLIMITED_RETALIATIONS", 0, 0, 0 ] ],	//royal griffins retaliate always
+		"abilities":
+		{
+			"unlimitedRetaliation" :
+			{
+				"type" : "UNLIMITED_RETALIATIONS"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CRGRIF.DEF"
@@ -159,7 +183,14 @@
 		"id": 7,
 		"level": 4,
 		"faction": "castle",
-		"abilities": [ [ "ADDITIONAL_ATTACK", 1, 0, 0 ] ],
+		"abilities":
+		{
+			"extraAttack" :
+			{
+				"type" : "ADDITIONAL_ATTACK",
+				"val" : 1
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CCRUSD.DEF"
@@ -184,8 +215,7 @@
 			"animation": "CMONKK.DEF",
 			"missile" :
 			{
-				"projectile": "CPRZEAX.DEF",
-				"spinning": false
+				"projectile": "CPRZEAX.DEF"
 			}
 		},
 		"sound" :
@@ -208,8 +238,7 @@
 			"animation": "CZEALT.DEF",
 			"missile" :
 			{
-				"projectile": "CPRZEAX.DEF",
-				"spinning": false
+				"projectile": "CPRZEAX.DEF"
 			}
 		},
 		"sound" :
@@ -264,11 +293,21 @@
 		"id": 12,
 		"level": 7,
 		"faction": "castle",
-		"abilities": 
-		[ 
-			["HATE", 50, "creature.archDevil", 0],	//angels hate archdevils
-			["HATE", 50, "creature.devil", 0]		//angels hate devil
-		],			   	 	
+		"abilities":
+		{
+			"hateDevils" :
+			{
+				"type" : "HATE",
+				"subtype" : "creature.devil",
+				"val" : 50
+			},
+			"hateArchDevils" :
+			{
+				"type" : "HATE",
+				"subtype" : "creature.archDevil",
+				"val" : 50
+			}
+		},
 		"upgrades": ["archangel"],
 		"graphics" :
 		{
@@ -288,14 +327,37 @@
 		"id": 13,
 		"level": 7,
 		"faction": "castle",
-		"abilities": 
-		[ 
-			["SPECIFIC_SPELL_POWER", 100, "spell.resurrection", 0],	// 100 hp per Archangel
-			["SPELLCASTER", 0, "spell.resurrection", 0 ],			//archangels cast resurrection
-			["HATE", 50, "creature.archDevil", 0],	//archangels hate archdevils
-			["HATE", 50, "creature.devil", 0] ,		//archangels hate devils
-			["CASTS", 1, 0, 0]
-		],
+		"abilities":
+		{
+			"resurrection100hp" : 
+			{
+				"type" : "SPECIFIC_SPELL_POWER",
+				"subtype" : "spell.resurrection",
+				"val" : 100
+			},
+			"resurrects" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.resurrection"
+			},
+			"spellpoints" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"hateDevils" :
+			{
+				"type" : "HATE",
+				"subtype" : "creature.devil",
+				"val" : 50
+			},
+			"hateArchDevils" :
+			{
+				"type" : "HATE",
+				"subtype" : "creature.archDevil",
+				"val" : 50
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CRANGL.DEF"

+ 268 - 64
config/creatures/conflux.json

@@ -5,14 +5,34 @@
 		"level": 2,
 		"extraNames": [ "airElementals" ],
 		"faction": "conflux",
-		"abilities": 
-		[ 
-			["MIND_IMMUNITY", 0, 0, 0],                       //air elementals are immune to mind spells 
-			["SPELL_IMMUNITY", 0, "spell.meteorShower", 0], //air elementals are immune to meteor shower
-			["NON_LIVING", 0, 0, 0 ],                        //air elementals are non-living
-			["MORE_DAMAGE_FROM_SPELL", 100, "spell.chainLightning", 0],//air elementals are vulnerable to chain lightning
-			["MORE_DAMAGE_FROM_SPELL", 100, "spell.lightningBolt", 0]  //air elementals are vulnerable to lightning bolt
-		],	  			
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"immuneToMind" : 
+			{
+				"type" : "MIND_IMMUNITY"
+			},
+			"meteorShowerImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.meteorShower"
+			},
+			"lightingVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.lightningBolt",
+				"val" : 100
+			},
+			"chainLightingVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.chainLightning",
+				"val" : 100
+			}
+		},
 		"upgrades": ["stormElemental"],
 		"graphics" :
 		{
@@ -32,14 +52,33 @@
 		"id": 113,
 		"level": 5,
 		"faction": "conflux",
-		"abilities": 
-		[ 
-			["MIND_IMMUNITY", 0, 0, 0], //earth elementals are immune to mind spells 
-			["SPELL_IMMUNITY", 0, "spell.chainLightning", 0],//earth elementals are immune to chain lightning
-			["SPELL_IMMUNITY", 0, "spell.lightningBolt", 0], //earth elementals are immune to lightning bolt
-			["NON_LIVING", 0, 0, 0],
-			[ "MORE_DAMAGE_FROM_SPELL", 100, "spell.meteorShower", 0 ]   //earth elementals are vulnerable to meteor shower
-		],		
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"immuneToMind" : 
+			{
+				"type" : "MIND_IMMUNITY"
+			},
+			"meteorShowerVulnerability" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.meteorShower",
+				"val" : 100
+			},
+			"lightingImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.lightningBolt"
+			},
+			"chainLightingImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.chainLightning"
+			}
+		},
 		"upgrades": ["magmaElemental"],
 		"graphics" :
 		{
@@ -59,13 +98,33 @@
 		"id": 114,
 		"level": 4,
 		"faction": "conflux",
-		"abilities": 
-		[ 
-			["MIND_IMMUNITY", 0, 0, 0],//fire elementals are immune to mind spells 
-			["NON_LIVING", 0, 0, 0 ],				//fire elementals are non-living
-			[ "MORE_DAMAGE_FROM_SPELL", 100, "spell.frostRing", 0 ],		//fire elementals are vulnerable to frost ring
-			[ "MORE_DAMAGE_FROM_SPELL", 100, "spell.iceBolt", 0 ],		//fire elementals are vulnerable to ice bolt
-			[ "FIRE_IMMUNITY", 0, 0, 0 ] ],			//fire elementals are immune to fire spells
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"immuneToMind" : 
+			{
+				"type" : "MIND_IMMUNITY"
+			},
+			"immuneToFire" : 
+			{
+				"type" : "FIRE_IMMUNITY"
+			},
+			"frostRingVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.frostRing",
+				"val" : 100
+			},
+			"iceBoltVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.iceBolt",
+				"val" : 100
+			}
+		},
 		"upgrades": ["energyElemental"],
 		"graphics" :
 		{
@@ -86,17 +145,52 @@
 		"level": 3,
 		"extraNames": [ "waterElementals" ],
 		"faction": "conflux",
-		"abilities": [ 
-			["MIND_IMMUNITY", 0, 0, 0],//water elementals are immune to mind spells
-			["SPELL_IMMUNITY", 0, "spell.frostRing", 0 ],//water elementals are immune to frost ring
-			["SPELL_IMMUNITY", 0, "spell.iceBolt", 0 ],//water elementals are immune to ice bolt
-			["NON_LIVING", 0, 0, 0 ],				//water elementals are non-living
-			["MORE_DAMAGE_FROM_SPELL", 100, "spell.fireShield", 0 ],		//water elementals are vulnerable to fire shield
-			["MORE_DAMAGE_FROM_SPELL", 100, "spell.inferno", 0 ],		//water elementals are vulnerable to inferno
-			["MORE_DAMAGE_FROM_SPELL", 100, "spell.fireball", 0 ],		//water elementals are vulnerable to fireball
-			["MORE_DAMAGE_FROM_SPELL", 100, "spell.fireWall", 0 ],		//water elementals are vulnerable to fire wall
-			["DOUBLE_WIDE", 0, 0, 0 ] 
-		],
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"immuneToMind" : 
+			{
+				"type" : "MIND_IMMUNITY"
+			},
+			"fireShieldVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.fireShield",
+				"val" : 100
+			},
+			"infernoVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.inferno",
+				"val" : 100
+			},
+			"fireballVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.fireball",
+				"val" : 100
+			},
+			"fireWallVulnerablity" :
+			{
+				"type" : "MORE_DAMAGE_FROM_SPELL",
+				"subtype" : "spell.fireWall",
+				"val" : 100
+			},
+			"iceBoltImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.iceBolt"
+			},
+			"frostRingImmunity" :
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.frostRing"
+			}
+		},
+		"doubleWide" : true,
 		"upgrades": ["iceElemental"],
 		"graphics" :
 		{
@@ -154,8 +248,14 @@
 		"id": 120,
 		"level": 6,
 		"faction": "conflux",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ] ],				//magic elementals shouldn't get morale
-		"ability_remove": [ "DOUBLE_WIDE" ],
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			}
+		},
+		"doubleWide" : false,
 		"upgrades": ["magicElemental"],
 		"graphics" :
 		{
@@ -175,9 +275,19 @@
 		"id": 121,
 		"level": 6,
 		"faction": "conflux",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ],				//ice elementals shouldn't get morale
-						 [ "LEVEL_SPELL_IMMUNITY", 5, 0, 0 ] ],	//magic elementals are immune to all spells
-		"ability_remove": [ "DOUBLE_WIDE" ],
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"magicImmunity" :
+			{
+				"type" : "LEVEL_SPELL_IMMUNITY",
+				"val" : 5
+			}
+		},
+		"doubleWide" : false,
 		"graphics" :
 		{
 			"animation": "CMAGEL.DEF"
@@ -196,18 +306,36 @@
 		"id": 123,
 		"level": 3,
 		"faction": "conflux",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ],
-						 [ "DOUBLE_WIDE", 0, 0, 0 ],					//ice elemental should be treated as double-wide
-						 [ "CREATURE_ENCHANT_POWER", 6, 0, 0 ],
-						 [ "CASTS", 3, 0, 0 ],
-						 [ "SPELLCASTER", 2, "spell.protectWater", 0 ]],
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"spellPower" : 
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 6
+			},
+			"spellPoints" : 
+			{
+				"type" : "CASTS",
+				"val" : 3
+			},
+			"spellcaster":
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.protectWater",
+				"val" : 2
+			}
+		},
+		"doubleWide" : true,
 		"graphics" :
 		{
 			"animation": "CICEE.DEF",
 			"missile" :
 			{
-				"projectile": "PICEE.DEF",
-				"spinning": false
+				"projectile": "PICEE.DEF"
 			}
 		},
 		"sound" :
@@ -225,10 +353,29 @@
 		"id": 125,
 		"level": 5,
 		"faction": "conflux",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ],						//magma elementals shouldn't get morale
-						[ "CREATURE_ENCHANT_POWER", 6, 0, 0 ],
-						[ "CASTS", 3, 0, 0 ],
-						[ "SPELLCASTER", 2, "spell.protectEarth", 0 ]],
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"spellPower" : 
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 6
+			},
+			"spellPoints" : 
+			{
+				"type" : "CASTS",
+				"val" : 3
+			},
+			"spellcaster":
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.protectEarth",
+				"val" : 2
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CSTONE.DEF"
@@ -247,17 +394,35 @@
 		"id": 127,
 		"level": 2,
 		"faction": "conflux",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ],						//storm elementals shouldn't get morale
-						[ "CREATURE_ENCHANT_POWER", 6, 0, 0 ],
-						[ "CASTS", 3, 0, 0 ],
-						[ "SPELLCASTER", 2, "spell.protectAir", 0 ]],
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"spellPower" : 
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 6
+			},
+			"spellPoints" : 
+			{
+				"type" : "CASTS",
+				"val" : 3
+			},
+			"spellcaster":
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.protectAir",
+				"val" : 2
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CSTORM.DEF",
 			"missile" :
 			{
-				"projectile": "CPRGTIX.DEF",
-				"spinning": false
+				"projectile": "CPRGTIX.DEF"
 			}
 		},
 		"sound" :
@@ -275,10 +440,29 @@
 		"id": 129,
 		"level": 4,
 		"faction": "conflux",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ] ,					//energy elementals shouldn't get morale
-						[ "CREATURE_ENCHANT_POWER", 6, 0, 0 ],
-						[ "CASTS", 3, 0, 0 ],
-						[ "SPELLCASTER", 2, "spell.protectFire", 0 ]],
+		"abilities":
+		{
+			"nonLiving" : 
+			{
+				"type" : "NON_LIVING"
+			},
+			"spellPower" : 
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 6
+			},
+			"spellPoints" : 
+			{
+				"type" : "CASTS",
+				"val" : 3
+			},
+			"spellcaster":
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.protectFire",
+				"val" : 2
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CNRG.DEF"
@@ -298,7 +482,13 @@
 		"level": 7,
 		"faction": "conflux",
 		"upgrades": ["phoenix"],
-		"abilities": [ ["FIRE_IMMUNITY", 0, 0, 0] ],
+		"abilities":
+		{
+			"immuneToFire" :
+			{
+				"type" : "FIRE_IMMUNITY"
+			},
+		},
 		"graphics" :
 		{
 			"animation": "CFBIRD.DEF"
@@ -317,9 +507,23 @@
 		"id": 131,
 		"level": 7,
 		"faction": "conflux",
-		"abilities": [ [ "CASTS", 1, 0, 0 ],	//Phoenix rebirths once
-						["FIRE_IMMUNITY", 0, 0, 0],
-						[ "REBIRTH", 20, 0, 0 ] ],	//20% of stack is resurrected
+		"abilities":
+		{
+			"rebirthOnce" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"immuneToFire" :
+			{
+				"type" : "FIRE_IMMUNITY"
+			},
+			"rebirth" : 
+			{
+				"type" : "REBIRTH",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CPHX.DEF"

+ 127 - 30
config/creatures/dungeon.json

@@ -4,7 +4,14 @@
 		"id": 70,
 		"level": 1,
 		"faction": "dungeon",
-		"abilities": [ [ "SPELL_IMMUNITY", 0, "spell.blind", 0 ] ],	  	   	//troglodytes are immune to blind
+		"abilities":
+		{
+			"blindImmunity" : 
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.blind"
+			}
+		},
 		"upgrades": ["infernalTroglodyte"],
 		"hasDoubleWeek": true,
 		"graphics" :
@@ -25,7 +32,14 @@
 		"id": 71,
 		"level": 1,
 		"faction": "dungeon",
-		"abilities": [ [ "SPELL_IMMUNITY", 0, "spell.blind", 0 ] ],		   	//infernal troglodytes are immune to blind
+		"abilities":
+		{
+			"blindImmunity" : 
+			{
+				"type" : "SPELL_IMMUNITY",
+				"subtype" : "spell.blind"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CITROG.DEF"
@@ -44,7 +58,13 @@
 		"id": 72,
 		"level": 2,
 		"faction": "dungeon",
-		"abilities": [ [ "RETURN_AFTER_STRIKE", 0, 0, 0 ] ],	   	//Harpies return after attack
+		"abilities":
+		{
+			"strikeAndReturn" : 
+			{
+				"type" : "RETURN_AFTER_STRIKE"
+			}
+		},
 		"upgrades": ["harpyHag"],
 		"hasDoubleWeek": true,
 		"graphics" :
@@ -66,8 +86,17 @@
 		"id": 73,
 		"level": 2,
 		"faction": "dungeon",
-		"abilities": [ [ "RETURN_AFTER_STRIKE", 0, 0, 0 ],	   	//Harpy Hags return after attack
-						 [ "BLOCKS_RETALIATION", 0, 0, 0 ] ],	   	//Harpy Hags
+		"abilities":
+		{
+			"strikeAndReturn" : 
+			{
+				"type" : "RETURN_AFTER_STRIKE"
+			},
+			"noRetaliation" : 
+			{
+				"type" : "BLOCKS_RETALIATION"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CHARPH.DEF"
@@ -93,8 +122,7 @@
 			"animation": "CBEHOL.DEF",
 			"missile" :
 			{
-				"projectile": "SMBALX.DEF",
-				"spinning": false
+				"projectile": "SMBALX.DEF"
 			}
 		},
 		"sound" :
@@ -104,8 +132,7 @@
 			"killed": "BHDRKILL.wav",
 			"move": "BHDRMOVE.wav",
 			"shoot": "BHDRSHOT.wav",
-			"wince": "BHDRWNCE.wav",
-			"ext1": "BHDRDETH.wav"
+			"wince": "BHDRWNCE.wav"
 		}
 	},
 	"evilEye" :
@@ -118,8 +145,7 @@
 			"animation": "CEVEYE.DEF",
 			"missile" :
 			{
-				"projectile": "SMBALX.DEF",
-				"spinning": false
+				"projectile": "SMBALX.DEF"
 			}
 		},
 		"sound" :
@@ -129,8 +155,7 @@
 			"killed": "EVLIKILL.wav",
 			"move": "EVLIMOVE.wav",
 			"shoot": "EVLISHOT.wav",
-			"wince": "EVLIWNCE.wav",
-			"ext1": "EVLIDETH.wav"
+			"wince": "EVLIWNCE.wav"
 		}
 	},
 	"medusa" :
@@ -138,15 +163,23 @@
 		"id": 76,
 		"level": 4,
 		"faction": "dungeon",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 20, "spell.stoneGaze", 2000 ] ],
+		"abilities":
+		{
+			"petrification" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.stoneGaze",
+				"val" : 20,
+				"addInfo" : 2000 // FIXME: replace with range field?
+			}
+		},
 		"upgrades": ["medusaQueen"],
 		"graphics" :
 		{
 			"animation": "CMEDUS.DEF",
 			"missile" :
 			{
-				"projectile": "PMEDUSX.DEF",
-				"spinning": false
+				"projectile": "PMEDUSX.DEF"
 			}
 		},
 		"sound" :
@@ -164,14 +197,22 @@
 		"id": 77,
 		"level": 4,
 		"faction": "dungeon",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 20, "spell.stoneGaze", 2000 ] ],
+		"abilities":
+		{
+			"petrification" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.stoneGaze",
+				"val" : 20,
+				"addInfo" : 2000 // FIXME: replace with range?
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CMEDUQ.DEF",
 			"missile" :
 			{
-				"projectile": "PMEDUSX.DEF",
-				"spinning": false
+				"projectile": "PMEDUSX.DEF"
 			}
 		},
 		"sound" :
@@ -189,7 +230,13 @@
 		"id": 78,
 		"level": 5,
 		"faction": "dungeon",
-		"abilities": [ [ "SELF_MORALE", 0, 0, 0 ] ],
+		"abilities": 
+		{
+			"fearless" :
+			{
+				"type" : "SELF_MORALE"
+			}
+		 },
 		"upgrades": ["minotaurKing"],
 		"graphics" :
 		{
@@ -209,7 +256,13 @@
 		"id": 79,
 		"level": 5,
 		"faction": "dungeon",
-		"abilities": [ [ "SELF_MORALE", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"fearless" :
+			{
+				"type" : "SELF_MORALE"
+			}
+		 },
 		"graphics" :
 		{
 			"animation": "CMINOK.DEF"
@@ -249,7 +302,15 @@
 		"id": 81,
 		"level": 6,
 		"faction": "dungeon",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 20, "spell.paralyze", 0 ] ],
+		"abilities":
+		{
+			"paralize" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.paralyze",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CCMCOR.DEF"
@@ -269,9 +330,22 @@
 		"id": 82,
 		"level": 7,
 		"faction": "dungeon",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],			//red dragon is a dragon
-						 [ "TWO_HEX_ATTACK_BREATH", 0, 0, 0 ],  	//Red Dragon has breath attack
-						 [ "LEVEL_SPELL_IMMUNITY", 3, 0, 0 ] ],   	//red dragon's spell immunity
+		"abilities":
+		{
+			"dragon" : 
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"fireBreath" :
+			{
+				"type" : "TWO_HEX_ATTACK_BREATH"
+			},
+			"spellImmunity" :
+			{
+				"type" : "LEVEL_SPELL_IMMUNITY",
+				"val" : 3
+			}
+		},
 		"upgrades": ["blackDragon"],
 		"graphics" :
 		{
@@ -291,11 +365,34 @@
 		"id": 83,
 		"level": 7,
 		"faction": "dungeon",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],			//black dragon is a dragon
-						 [ "TWO_HEX_ATTACK_BREATH", 0, 0, 0 ],  	//Black Dragon has breath attack
-						 [ "HATE", 50, "creature.titan", 0 ],	//Hate Titans
-						 [ "HATE", 50, "creature.giant", 0 ],	//Hate Giants
-						 [ "LEVEL_SPELL_IMMUNITY", 5, 0, 0 ] ],   	//black dragon's spell immunity
+		"abilities":
+		{
+			"dragon" : 
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"fireBreath" :
+			{
+				"type" : "TWO_HEX_ATTACK_BREATH"
+			},
+			"spellImmunity" :
+			{
+				"type" : "LEVEL_SPELL_IMMUNITY",
+				"val" : 5
+			},
+			"hateGiants" :
+			{
+				"type" : "HATE",
+				"subtype" : "creature.giant",
+				"val" : 50
+			},
+			"hateTitans" :
+			{
+				"type" : "HATE",
+				"subtype" : "creature.titan",
+				"val" : 50
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CBDRGN.DEF"

+ 83 - 15
config/creatures/fortress.json

@@ -49,8 +49,7 @@
 			"animation": "CPLIZA.DEF",
 			"missile" :
 			{
-				"projectile": "PPLIZAX.DEF",
-				"spinning": false
+				"projectile": "PPLIZAX.DEF"
 			}
 		},
 		"sound" :
@@ -73,8 +72,7 @@
 			"animation": "CALIZA.DEF",
 			"missile" :
 			{
-				"projectile": "PPLIZAX.DEF",
-				"spinning": false
+				"projectile": "PPLIZAX.DEF"
 			}
 		},
 		"sound" :
@@ -111,7 +109,14 @@
 		"id": 103,
 		"level": 5,
 		"faction": "fortress",
-		"abilities": [ [ "DEATH_STARE", 10, 0, 0 ] ],   	//mighty gorgons
+		"abilities":
+		{
+			"deathStare" : 
+			{
+				"type" : "DEATH_STARE",
+				"val" : 10
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CBGOG.DEF"
@@ -131,7 +136,15 @@
 		"level": 3,
 		"extraNames": [ "dragonFly" ],
 		"faction": "fortress",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 100, "spell.dispelHelpful", 0 ] ], 
+		"abilities":
+		{
+			"dispellHelpful" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.dispelHelpful",
+				"val" : 100
+			}
+		},
 		"upgrades": ["fireDragonFly"],
 		"hasDoubleWeek": true,
 		"graphics" :
@@ -152,8 +165,21 @@
 		"id": 105,
 		"level": 3,
 		"faction": "fortress",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 100, "spell.dispelHelpful", 0 ],	
-					   [ "SPELL_AFTER_ATTACK", 100, "spell.weakness", 0 ] ],  
+		"abilities":
+		{
+			"dispellHelpful" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.dispelHelpful",
+				"val" : 100
+			},
+			"castWeakness" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.weakness",
+				"val" : 100
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CDRFIR.DEF"
@@ -173,7 +199,15 @@
 		"id": 106,
 		"level": 4,
 		"faction": "fortress",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 20, "spell.stoneGaze", 0 ] ],
+		"abilities":
+		{
+			"petrify" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.stoneGaze",
+				"val" : 20
+			}
+		},
 		"upgrades": ["greaterBasilisk"],
 		"graphics" :
 		{
@@ -193,7 +227,15 @@
 		"id": 107,
 		"level": 4,
 		"faction": "fortress",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 20, "spell.stoneGaze", 0 ] ],
+		"abilities":
+		{
+			"petrify" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.stoneGaze",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CGBASI.DEF"
@@ -231,7 +273,15 @@
 		"id": 109,
 		"level": 6,
 		"faction": "fortress",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 50, "spell.poison", 0 ] ],   	//50% probability (from FizMiG)
+		"abilities":
+		{
+			"petrify" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.stoneGaze",
+				"val" : 50
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CWYVMN.DEF"
@@ -250,8 +300,17 @@
 		"id": 110,
 		"level": 7,
 		"faction": "fortress",
-		"abilities": [ [ "BLOCKS_RETALIATION", 0, 0, 0 ], 	   	
-					   [ "ATTACKS_ALL_ADJACENT", 0, 0, 0 ] ], 
+		"abilities":
+		{
+			"noRetaliate" :
+			{
+				"type" : "BLOCKS_RETALIATION"
+			},
+			"attackAll" :
+			{
+				"type" : "ATTACKS_ALL_ADJACENT"
+			}
+		},
 		"upgrades": ["chaosHydra"],
 		"graphics" :
 		{
@@ -271,8 +330,17 @@
 		"id": 111,
 		"level": 7,
 		"faction": "fortress",
-		"abilities": [ [ "BLOCKS_RETALIATION", 0, 0, 0 ], 	   	
-					   [ "ATTACKS_ALL_ADJACENT", 0, 0, 0 ] ],   
+		"abilities":
+		{
+			"noRetaliate" :
+			{
+				"type" : "BLOCKS_RETALIATION"
+			},
+			"attackAll" :
+			{
+				"type" : "ATTACKS_ALL_ADJACENT"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CCHYDR.DEF"

+ 166 - 41
config/creatures/inferno.json

@@ -23,7 +23,14 @@
 		"id": 43,
 		"level": 1,
 		"faction": "inferno",
-		"abilities": [ [ "MANA_CHANNELING", 20, 0, 0 ] ], 		//familiars
+		"abilities":
+		{
+			"manaChannel" :
+			{
+				"type" : "MANA_CHANNELING",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CFAMIL.DEF"
@@ -49,8 +56,7 @@
 			"animation": "CGOG.DEF",
 			"missile" :
 			{
-				"projectile": "CPRGOGX.DEF",
-				"spinning": false
+				"projectile": "CPRGOGX.DEF"
 			}
 		},
 		"sound" :
@@ -60,8 +66,7 @@
 			"killed": "GOGGKILL.wav",
 			"move": "GOGGMOVE.wav",
 			"shoot": "GOGGSHOT.wav",
-			"wince": "GOGGWNCE.wav",
-			"ext1": "GOGFLAME.wav"
+			"wince": "GOGGWNCE.wav"
 		}
 	},
 	"magog" :
@@ -69,14 +74,20 @@
 		"id": 45,
 		"level": 2,
 		"faction": "inferno",
-		"abilities": [ [ "SPELL_LIKE_ATTACK", 0, 21, 0 ] ], 		//magogs fire with fireballs
+		"abilities":
+		{
+			"fireball" :
+			{
+				"type" : "SPELL_LIKE_ATTACK",
+				"subtype" : "spell.fireball"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CMAGOG.DEF",
 			"missile" :
 			{
-				"projectile": "CPRGOGX.DEF",
-				"spinning": false
+				"projectile": "CPRGOGX.DEF"
 			}
 		},
 		"sound" :
@@ -86,8 +97,7 @@
 			"killed": "MGOGKILL.wav",
 			"move": "MGOGMOVE.wav",
 			"shoot": "MGOGSHOT.wav",
-			"wince": "MGOGWNCE.wav",
-			"ext1": "GOGFLAME.wav"
+			"wince": "MGOGWNCE.wav"
 		}
 	},
 	"hellHound" :
@@ -96,7 +106,10 @@
 		"level": 3,
 		"faction": "inferno",
 		"upgrades": ["cerberus"],
-		"ability_remove": [ "FLYING" ], //hell hound doesn't fly
+		"abilities":
+		{
+			 "FLYING_ARMY" : null //hell hound doesn't fly
+		},
 		"graphics" :
 		{
 			"animation": "CHHOUN.DEF"
@@ -115,9 +128,18 @@
 		"id": 47,
 		"level": 3,
 		"faction": "inferno",
-		"abilities": [ [ "THREE_HEADED_ATTACK", 0, 0, 0 ],
-						 [ "BLOCKS_RETALIATION", 0, 0, 0 ] ],
-		"ability_remove": [ "FLYING" ],  //cerberus doesn't fly
+		"abilities":
+		{
+			"threeHeads" : 
+			{
+				"type" : "THREE_HEADED_ATTACK"
+			},
+			"noRetaliation" :
+			{
+				"type" : "BLOCKS_RETALIATION"
+			},
+			"FLYING_ARMY" : null //cerberus doesn't fly
+		},
 		"graphics" :
 		{
 			"animation": "CCERBU.DEF"
@@ -192,8 +214,20 @@
 		"id": 51,
 		"level": 5,
 		"faction": "inferno",
-		"abilities": [ [ "DAEMON_SUMMONING", 50, 48, 0 ],
-						[ "CASTS", 1, 0, 0] ],		//pit lord
+		"abilities":
+		{
+			"demonSummon" :
+			{
+				"type" : "DAEMON_SUMMONING",
+				"subtype" : "creature.demon",
+				"val" : 50
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CPFOE.DEF"
@@ -211,11 +245,30 @@
 	{
 		"id": 52,
 		"level": 6,
-		"faction": "inferno",
-		"abilities": [ [ "HATE", 50, 37, 0 ],
-						 [ "HATE", 50, 36, 0 ],
-						 [ "FLYING", 0, 0, 0 ],  					//efreeti hate master genies
-						 [ "FIRE_IMMUNITY", 0, 0, 0 ] ], 			//efreeti hate genies
+		"faction": "inferno", 
+		"abilities":
+		{
+			"hateGenies" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.genie",
+				"val" : 50
+			},
+			"hateMasterGenies" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.masterGenie",
+				"val" : 50
+			},
+			"canFly" :
+			{
+				"type" : "FLYING"
+			},
+			"immuneToFire" :
+			{
+				"type" : "FIRE_IMMUNITY"
+			}
+		},
 		"upgrades": ["efreetSultan"],
 		"graphics" :
 		{
@@ -235,11 +288,34 @@
 		"id": 53,
 		"level": 6,
 		"faction": "inferno",
-		"abilities": [ [ "HATE", 50, 37, 0 ],
-						 [ "HATE", 50, 36, 0 ],
-						 [ "FLYING", 0, 0, 0 ],		  	 		//efreet sultans hate master genies
-						 [ "FIRE_SHIELD", 0, 36, 0 ],				//efreet sultans hate genies
-						 [ "FIRE_IMMUNITY", 0, 0, 0 ] ],			//efreet sultan		//Efreet Sultan
+		"abilities":
+		{
+			"hateGenies" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.genie",
+				"val" : 50
+			},
+			"hateMasterGenies" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.masterGenie",
+				"val" : 50
+			},
+			"canFly" :
+			{
+				"type" : "FLYING"
+			},
+			"immuneToFire" :
+			{
+				"type" : "FIRE_IMMUNITY"
+			},
+			"fireShield" :
+			{
+				"type" : "FIRE_SHIELD",
+				"subtype" : 36 //FIXME: what is this magic number for?
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CEFRES.DEF"
@@ -259,12 +335,36 @@
 		"id": 54,
 		"level": 7,
 		"faction": "inferno",
-		"ability_remove": [ "FLYING" ],  //use teleport instead
-		"abilities": [ [ "HATE", 50, 13, 0 ],
-						[ "FLYING", 0, 1, 0], //teleport
-						 [ "HATE", 50, 12, 0 ],
-						 [ "ENEMY_LUCK_DECREASING", 1, 0, 0 ],	//devils				//devils hate archangles
-						 [ "BLOCKS_RETALIATION", 0, 0, 0 ] ], 	 	//devils			//devils hate angels
+		"abilities":
+		{
+			"hateAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.angel",
+				"val" : 50
+			},
+			"hateArchAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.angel",
+				"val" : 50
+			},
+			"FLYING_ARMY" :
+			{
+				// type loaded from crtraits
+				"subtype" : 1 // teleports
+			},
+			"descreaseLuck" :
+			{
+				"type" : "LUCK",
+				"effectRange" : "ONLY_ENEMY_ARMY",
+				"val" : -1
+			},
+			"blockRetaliation" :
+			{
+				"type" : "BLOCKS_RETALIATION"
+			}
+		},
 		"upgrades": ["archDevil"],
 		"graphics" :
 		{
@@ -277,8 +377,8 @@
 			"killed": "DEVLKILL.wav",
 			"move": "DEVLMOVE.wav",
 			"wince": "DEVLWNCE.wav",
-			"ext1": "DEVLEXT1.wav",
-			"ext2": "DEVLEXT2.wav"
+			"startMoving": "DEVLEXT1.wav",
+			"endMoving": "DEVLEXT2.wav"
 		}
 	},
 	"archDevil" :
@@ -286,14 +386,39 @@
 		"id": 55,
 		"level": 7,
 		"faction": "inferno",
-		"ability_remove": [ "FLYING" ],  //use teleport instead
-		"abilities": [ [ "HATE", 50, 13, 0 ],
-						 [ "HATE", 50, 12, 0 ],
-						 [ "FLYING", 0, 1, 0],
-						 [ "ENEMY_LUCK_DECREASING", 1, 0, 0 ], 	//archdevils				//archdevils hate archangles
-						 [ "BLOCKS_RETALIATION", 0, 0, 0 ] ], 	 	//archdevils			//archdevils hate angels
+		"abilities" :
+		{
+			"hateAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.angel",
+				"val" : 50
+			},
+			"hateArchAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.angel",
+				"val" : 50
+			},
+			"FLYING_ARMY" :
+			{
+				// type loaded from crtraits
+				"subtype" : 1 // teleports
+			},
+			"descreaseLuck" :
+			{
+				"type" : "LUCK",
+				"effectRange" : "ONLY_ENEMY_ARMY",
+				"val" : -1
+			},
+			"blockRetaliation" :
+			{
+				"type" : "BLOCKS_RETALIATION"
+			}
+		},
 		"graphics" :
 		{
+			"missile" : null,
 			"animation": "CADEVL.DEF"
 		},
 		"sound" :
@@ -304,7 +429,7 @@
 			"move": "ADVLMOVE.wav",
 			"wince": "ADVLWNCE.wav",
 			"startMoving": "ADVLEXT1.wav",
-			"stopMoving": "ADVLEXT2.wav"
+			"endMoving": "ADVLEXT2.wav"
 		}
 	}
 }

+ 107 - 23
config/creatures/necropolis.json

@@ -79,7 +79,14 @@
 		"id": 60,
 		"level": 3,
 		"faction": "necropolis",
-		"abilities": [ [ "FULL_HP_REGENERATION", 0, 1, 0 ] ],
+		"abilities":
+		{
+			"regenerate" : 
+			{
+				"type" : "FULL_HP_REGENERATION",
+				"subtype" : 1
+			}
+		},
 		"upgrades": ["wraith"],
 		"hasDoubleWeek": true,
 		"graphics" :
@@ -100,8 +107,19 @@
 		"id": 61,
 		"level": 3,
 		"faction": "necropolis",
-		"abilities": [ [ "FULL_HP_REGENERATION", 0, 1, 0 ],
-						 [ "MANA_DRAIN", 2, 0, 0 ] ],
+		"abilities":
+		{
+			"regenerate" : 
+			{
+				"type" : "FULL_HP_REGENERATION",
+				"subtype" : 1
+			},
+			"drainsMana" :
+			{
+				"type" : "MANA_DRAIN",
+				"val" : 2
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CWRAIT.DEF"
@@ -120,7 +138,14 @@
 		"id": 62,
 		"level": 4,
 		"faction": "necropolis",
-		"abilities": [ [ "BLOCKS_RETALIATION", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"noRetalitation" : 
+			{
+				"type" : "BLOCKS_RETALIATION",
+				"subtype" : 1
+			}
+		},
 		"upgrades": ["vampireLord"],
 		"graphics" :
 		{
@@ -134,7 +159,7 @@
 			"move": "VAMPMOVE.wav",
 			"wince": "VAMPWNCE.wav",
 			"startMoving": "VAMPEXT1.wav",
-			"stopMoving": "VAMPEXT2.wav"
+			"endMoving": "VAMPEXT2.wav"
 		}
 	},
 	"vampireLord" :
@@ -142,8 +167,19 @@
 		"id": 63,
 		"level": 4,
 		"faction": "necropolis",
-		"abilities": [ [ "LIFE_DRAIN", 100, 0, 0 ], //drain 100% of damage dealt
-						 [ "BLOCKS_RETALIATION", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"noRetalitation" : 
+			{
+				"type" : "BLOCKS_RETALIATION",
+				"subtype" : 1
+			},
+			"drainsLife" :
+			{
+				"type" : "LIFE_DRAIN",
+				"val" : 100
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CNOSFE.DEF"
@@ -156,8 +192,8 @@
 			"move": "NOSFMOVE.wav",
 			"shoot": "NOSFSHOT.wav",
 			"wince": "NOSFWNCE.wav",
-			"ext1": "NOSFEXT1.wav",
-			"ext2": "NOSFEXT2.wav"
+			"startMoving": "NOSFEXT1.wav",
+			"endMoving": "NOSFEXT2.wav"
 		}
 	},
 	"lich" :
@@ -165,15 +201,21 @@
 		"id": 64,
 		"level": 5,
 		"faction": "necropolis",
-		"abilities": [ [ "SPELL_LIKE_ATTACK", 0, "spell.deathCloud", 0 ] ],
+		"abilities":
+		{
+			"deathCloud" :
+			{
+				"type" : "SPELL_LIKE_ATTACK",
+				"subtype" : "spell.deathCloud"
+			}
+		},
 		"upgrades": ["powerLich"],
 		"graphics" :
 		{
 			"animation": "CLICH.DEF",
 			"missile" :
 			{
-				"projectile": "PLICH.DEF",
-				"spinning": false
+				"projectile": "PLICH.DEF"
 			}
 		},
 		"sound" :
@@ -183,8 +225,7 @@
 			"killed": "LICHKILL.wav",
 			"move": "LICHMOVE.wav",
 			"shoot": "LICHSHOT.wav",
-			"wince": "LICHWNCE.wav",
-			"ext1": "LICHATK2.wav"
+			"wince": "LICHWNCE.wav"
 		}
 	},
 	"powerLich" :
@@ -192,14 +233,20 @@
 		"id": 65,
 		"level": 5,
 		"faction": "necropolis",
-		"abilities": [ [ "SPELL_LIKE_ATTACK", 0, "spell.deathCloud", 0 ] ], 		//power liches
+		"abilities":
+		{
+			"deathCloud" :
+			{
+				"type" : "SPELL_LIKE_ATTACK",
+				"subtype" : "spell.deathCloud"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CPLICH.DEF",
 			"missile" :
 			{
-				"projectile": "PLICH.DEF",
-				"spinning": false
+				"projectile": "PLICH.DEF"
 			}
 		},
 		"sound" :
@@ -217,7 +264,15 @@
 		"id": 66,
 		"level": 6,
 		"faction": "necropolis",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 20, "spell.curse", 0 ] ], 	
+		"abilities":
+		{
+			"curses" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.curse",
+				"val" : 20
+			}
+		},
 		"upgrades": ["dreadKnight"],
 		"graphics" :
 		{
@@ -237,8 +292,20 @@
 		"id": 67,
 		"level": 6,
 		"faction": "necropolis",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 20, "spell.curse", 0 ], 	
-						 [ "DOUBLE_DAMAGE_CHANCE", 20, 0, 0 ] ],
+		"abilities":
+		{
+			"curses" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.curse",
+				"val" : 20
+			},
+			"deathStrike" :
+			{
+				"type" : "DOUBLE_DAMAGE_CHANCE",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CBLORD.DEF"
@@ -257,7 +324,13 @@
 		"id": 68,
 		"level": 7,
 		"faction": "necropolis",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ] ],			//bone dragon is a dragon
+		"abilities" :
+		{
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE"
+			}
+		},
 		"upgrades": ["ghostDragon"],
 		"graphics" :
 		{
@@ -277,8 +350,19 @@
 		"id": 69,
 		"level": 7,
 		"faction": "necropolis",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],			//ghost dragon is a dragon
-						 [ "SPELL_AFTER_ATTACK", 20, "spell.age", 0 ] ],  	
+		"abilities":
+		{
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"age" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.age",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CHDRGN.DEF"

+ 241 - 47
config/creatures/neutral.json

@@ -5,8 +5,19 @@
 		"id": 116,
 		"level": 4,
 		"faction": "neutral",
-		"abilities": [ [ "SPELL_DAMAGE_REDUCTION", 85, -1, 0 ],		//gold golems reduce dmg from spells
-						 [ "NON_LIVING", 0, 0, 0 ] ],		 	   	//diamond golems are non-living
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "SPELL_DAMAGE_REDUCTION",
+				"subtype" : -1,
+				"val" : 85
+			},
+			"nonliving" :
+			{
+				"type" : "NON_LIVING"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CGGOLE.DEF"
@@ -25,8 +36,19 @@
 		"id": 117,
 		"level": 5,
 		"faction": "neutral",
-		"abilities": [ [ "SPELL_DAMAGE_REDUCTION", 95, -1, 0 ],		//diamond golems reduce dmg from spells
-						 [ "NON_LIVING", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "SPELL_DAMAGE_REDUCTION",
+				"subtype" : -1,
+				"val" : 95
+			},
+			"nonliving" :
+			{
+				"type" : "NON_LIVING"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CDGOLE.DEF"
@@ -46,11 +68,30 @@
 		"id": 132,
 		"level": 10,
 		"faction": "neutral",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],
-						 [ "TWO_HEX_ATTACK_BREATH", 0, 0, 0 ],  //azure dragon's breath
-						 [ "FEARLESS", 0, 0, 0 ],  //azure dragon is immune to fear
-						  [ "LEVEL_SPELL_IMMUNITY", 3, 0, 0 ],	//immunity spell levels 1-3
-						[ "FEAR", 0, 0, 0]],			//azure dragon is a dragon
+		"abilities":
+		{
+			"dragon" : 
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"fireBreath" :
+			{
+				"type" : "TWO_HEX_ATTACK_BREATH"
+			},
+			"spellImmunity" :
+			{
+				"type" : "LEVEL_SPELL_IMMUNITY",
+				"val" : 3
+			},
+			"fearless" :
+			{
+				"type" : "FEARLESS"
+			},
+			"fear" :
+			{
+				"type" : "FEAR"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CADRGN.DEF"
@@ -70,8 +111,14 @@
 		"id": 133,
 		"level": 10,
 		"faction": "neutral",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ] ],			//crystal dragon is a dragon
-		"ability_remove": [ "FLYING" ],							//Crystal Dragons do not fly
+		"abilities":
+		{
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"FLYING" : null
+		},
 		"graphics" :
 		{
 			"animation": "CCDRGN.DEF"
@@ -91,18 +138,84 @@
 		"id": 134,
 		"level": 8,
 		"faction": "neutral",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],			//faerie dragon is a dragon
-						[ "MAGIC_MIRROR", 30, 0, 0 ],
-						[ "CASTS", 5, 0, 0 ],
-						[ "CREATURE_SPELL_POWER", 500, 0, 0], 	//5 spell power per dragon
-						[ "SPELLCASTER", 2, "spell.magicArrow", 10 ],			
-						[ "SPELLCASTER", 2, "spell.iceBolt", 22 ],			
-						[ "SPELLCASTER", 2, "spell.lightningBolt", 22 ],			
-						[ "SPELLCASTER", 2, "spell.chainLightning", 5 ],			
-						[ "SPELLCASTER", 2, "spell.frostRing", 10 ],			
-						[ "SPELLCASTER", 2, "spell.fireball", 21 ],			
-						[ "SPELLCASTER", 2, "spell.inferno", 5 ],			
-						[ "SPELLCASTER", 2, "spell.meteorShower", 5 ]],			
+		"abilities":
+		{
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE",
+			},
+			"mirror" :
+			{
+				"type" : "MAGIC_MIRROR",
+				"val" : 30
+			},
+			"casts" :
+			{
+				"type" : "CASTS",
+				"val" : 5
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 500
+			},
+			"castsMagicArrow" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.magicArrow",
+				"addInfo" : 22,
+				"val" : 2
+			},
+			"castsIceBolt" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.iceBolt",
+				"addInfo" : 22,
+				"val" : 2
+			},
+			"castsLightningBolt" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.lightningBolt",
+				"addInfo" : 22,
+				"val" : 2
+			},
+			"castsChainLightning" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.chainLightning",
+				"addInfo" : 5,
+				"val" : 2
+			},
+			"castsFrostRing" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.frostRing",
+				"addInfo" : 10,
+				"val" : 2
+			},
+			"castsFireball" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.fireball",
+				"addInfo" : 21,
+				"val" : 2
+			},
+			"castsInferno" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.inferno",
+				"addInfo" : 5,
+				"val" : 2
+			},
+			"castsMeteorShower" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.meteorShower",
+				"addInfo" : 5,
+				"val" : 2
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CFDRGN.DEF"
@@ -123,9 +236,25 @@
 		"id": 135,
 		"level": 10,
 		"faction": "neutral",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 100, 80, 0 ],			//always reduce defense
-						 [ "ACID_BREATH", 25, 0, 20 ],			//20% chance to do 25 damage
-						 [ "DRAGON_NATURE", 0, 0, 0 ] ],			//rust dragon is a dragon
+		"abilities":
+		{
+			"dragon" : 
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"acidBreath" :
+			{
+				"type" : "ACID_BREATH",
+				"val" : 25,
+				"addInfo" : 20
+			},
+			"reduceDefence" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.acidBreath",
+				"val" : 100
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CRSDGN.DEF"
@@ -146,21 +275,67 @@
 		"level": 6,
 		"extraNames": [ "enchanters" ],
 		"faction": "neutral",
-		"abilities": [ [ "NO_WALL_PENALTY", 0, 0, 0 ],
-						 [ "ENCHANTER", 3, "spell.airShield", 3],		
-						 [ "ENCHANTER", 3, "spell.bless", 3],		
-						 [ "ENCHANTER", 3, "spell.weakness", 3],		
-						 [ "ENCHANTER", 3, "spell.stoneSkin", 3],		
-						 [ "ENCHANTER", 3, "spell.slow", 3],		
-						 [ "ENCHANTER", 3, "spell.haste", 3],		
-						 [ "CASTS", 5, 0, 0]],			
+		"abilities":
+		{
+			"noPenalty" : 
+			{
+				"type" : "NO_WALL_PENALTY"
+			},
+			"casts" :
+			{
+				"type" : "CASTS",
+				"val" : 5
+			},
+			"castsAirShield" :
+			{
+				"type" : "ENCHANTER",
+				"subtype" : "spell.airShield",
+				"val" : 3,
+				"addInfo" : 3
+			},
+			"castsBless" :
+			{
+				"type" : "ENCHANTER",
+				"subtype" : "spell.bless",
+				"val" : 3,
+				"addInfo" : 3
+			},
+			"castsWeakness" :
+			{
+				"type" : "ENCHANTER",
+				"subtype" : "spell.weakness",
+				"val" : 3,
+				"addInfo" : 3
+			},
+			"castsStoneSkin" :
+			{
+				"type" : "ENCHANTER",
+				"subtype" : "spell.stoneSkin",
+				"val" : 3,
+				"addInfo" : 3
+			},
+			"castsSlow" :
+			{
+				"type" : "ENCHANTER",
+				"subtype" : "spell.slow",
+				"val" : 3,
+				"addInfo" : 3
+			},
+			"castsHaste" :
+			{
+				"type" : "ENCHANTER",
+				"subtype" : "spell.haste",
+				"val" : 3,
+				"addInfo" : 3
+			}
+			
+		},
 		"graphics" :
 		{
-			"animation": "CENCH.DEF",
+			"animation": "CGTITA.DEF",
 			"missile" :
 			{
-				"projectile": "CPRZEAX.DEF",
-				"spinning": false
+				"projectile": "CPRZEAX.DEF"
 			}
 		},
 		"sound" :
@@ -180,15 +355,23 @@
 		"level": 4,
 		"extraNames": [ "sharpshooters" ],
 		"faction": "neutral",
-		"abilities": [ [ "NO_WALL_PENALTY", 0, 0, 0 ],
-						 [ "NO_DISTANCE_PENALTY", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"noPenalty" : 
+			{
+				"type" : "NO_WALL_PENALTY"
+			},
+			"noDistancePenalty" :
+			{
+				"type" : "NO_DISTANCE_PENALTY"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CSHARP.DEF",
 			"missile" :
 			{
-				"projectile": "PELFX.DEF",
-				"spinning": false
+				"projectile": "PELFX.DEF"
 			}
 		},
 		"sound" :
@@ -211,8 +394,7 @@
 			"animation": "CHALF.DEF",
 			"missile" :
 			{
-				"projectile": "PHALF.DEF",
-				"spinning": true
+				"projectile": "PHALF.DEF"
 			}
 		},
 		"sound" :
@@ -248,7 +430,7 @@
 		"id": 140,
 		"level": 2,
 		"faction": "neutral",
-		"abilities": [ [ "DOUBLE_WIDE", 0, 0, 0 ] ], 			//boar should be treated as double-wide
+		"doubleWide" : true,
 		"graphics" :
 		{
 			"animation": "CBOAR.DEF"
@@ -267,7 +449,13 @@
 		"id": 141,
 		"level": 3,
 		"faction": "neutral",
-		"abilities": [ [ "UNDEAD", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"undead" :
+			{
+				"type" : "UNDEAD"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CMUMMY.DEF"
@@ -286,7 +474,7 @@
 		"id": 142,
 		"level": 3,
 		"faction": "neutral",
-		"abilities": [ [ "DOUBLE_WIDE", 0, 0, 0 ] ], 			//nomads should be treated as double-wide
+		"doubleWide" : true,
 		"graphics" :
 		{
 			"animation": "CNOMAD.DEF"
@@ -323,7 +511,13 @@
 		"id": 144,
 		"level": 5,
 		"faction": "neutral",
-		"abilities": [ [ "FULL_HP_REGENERATION", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"regenerates" :
+			{
+				"type" : "FULL_HP_REGENERATION"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CTROLL.DEF"

+ 123 - 27
config/creatures/rampart.json

@@ -8,6 +8,7 @@
 		"hasDoubleWeek": true,
 		"graphics" :
 		{
+			"missile" : null,
 			"animation": "CCENTR.DEF"
 		},
 		"sound" :
@@ -27,6 +28,7 @@
 		"faction": "rampart",
 		"graphics" :
 		{
+			"missile" : null,
 			"animation": "CECENT.DEF"
 		},
 		"sound" :
@@ -43,7 +45,14 @@
 		"id": 16,
 		"level": 2,
 		"faction": "rampart",
-		"abilities": [ [ "MAGIC_RESISTANCE", 20, 0, 0 ] ], 		//dwarf's magic resistance 20%
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 20
+			}
+		},
 		"upgrades": ["battleDwarf"],
 		"graphics" :
 		{
@@ -63,7 +72,14 @@
 		"id": 17,
 		"level": 2,
 		"faction": "rampart",
-		"abilities": [ [ "MAGIC_RESISTANCE", 40, 0, 0 ] ], 	 	//battle dwarf's magic resistance 40%
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 40
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CBDWAR.DEF"
@@ -88,8 +104,7 @@
 			"animation": "CELF.DEF",
 			"missile" :
 			{
-				"projectile": "PELFX.DEF",
-				"spinning": false
+				"projectile": "PELFX.DEF"
 			}
 		},
 		"sound" :
@@ -107,20 +122,21 @@
 		"id": 19,
 		"level": 3,
 		"faction": "rampart",
-		"abilities": [
-						{
-							"type": "ADDITIONAL_ATTACK",
-							"val" : 1,
-							"effectRange": "ONLY_DISTANCE_FIGHT"
-						}
-					],	
+		"abilities": 
+		{
+			"doubleShot" :
+			{
+				"type": "ADDITIONAL_ATTACK",
+				"val" : 1,
+				"effectRange": "ONLY_DISTANCE_FIGHT"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CGRELF.DEF",
 			"missile" :
 			{
-				"projectile": "PELFX.DEF",
-				"spinning": false
+				"projectile": "PELFX.DEF"
 			}
 		},
 		"sound" :
@@ -138,7 +154,14 @@
 		"id": 20,
 		"level": 4,
 		"faction": "rampart",
-		"abilities": [ [ "CHANGES_SPELL_COST_FOR_ENEMY", 2, 0, 0 ] ],	//pegasus makes spell cost higher for enemy mage
+		"abilities":
+		{
+			"increaseManaCost" :
+			{
+				"type" : "CHANGES_SPELL_COST_FOR_ENEMY",
+				"val" : 2
+			}
+		},
 		"upgrades": ["silverPegasus"],
 		"hasDoubleWeek": true,
 		"graphics" :
@@ -159,7 +182,14 @@
 		"id": 21,
 		"level": 4,
 		"faction": "rampart",
-		"abilities": [ [ "CHANGES_SPELL_COST_FOR_ENEMY", 2, 0, 0 ] ], 	//silver pegasus makes spell cost higher for enemy mage
+		"abilities":
+		{
+			"increaseManaCost" :
+			{
+				"type" : "CHANGES_SPELL_COST_FOR_ENEMY",
+				"val" : 2
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CAPEGS.DEF"
@@ -178,7 +208,15 @@
 		"id": 22,
 		"level": 5,
 		"faction": "rampart",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 100, "spell.bind", 0 ] ],   //dendroids cast bind
+		"abilities":
+		{
+			"binds" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.bind",
+				"val" : 100
+			}
+		},
 		"upgrades": ["dendroidSoldier"],
 		"graphics" :
 		{
@@ -198,7 +236,15 @@
 		"id": 23,
 		"level": 5,
 		"faction": "rampart",
-		"abilities": [ [ "SPELL_AFTER_ATTACK", 100, "spell.bind", 0 ] ],	//dendroid guards cast bind
+		"abilities":
+		{
+			"binds" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.bind",
+				"val" : 100
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CBTREE.DEF"
@@ -217,8 +263,20 @@
 		"id": 24,
 		"level": 6,
 		"faction": "rampart",
-		"abilities": [ [ "SPELL_RESISTANCE_AURA", 0, 55, 0 ],	//unicorn
-						 [ "SPELL_AFTER_ATTACK", 20, "spell.blind", 0 ] ],	//unicorns cast blind with 20% probability
+		"abilities":
+		{
+			"spellResistAura" :
+			{
+				"type" : "SPELL_RESISTANCE_AURA",
+				"val" : 20
+			},
+			"blinds" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.blind",
+				"val" : 20
+			}
+		},
 		"upgrades": ["warUnicorn"],
 		"graphics" :
 		{
@@ -238,8 +296,20 @@
 		"id": 25,
 		"level": 6,
 		"faction": "rampart",
-		"abilities": [ [ "SPELL_RESISTANCE_AURA", 20, 55, 0 ], 	//war unicorn
-						 [ "SPELL_AFTER_ATTACK", 20, "spell.blind", 0 ] ],	//war unicorns cast blind with 20% probability
+		"abilities":
+		{
+			"spellResistAura" :
+			{
+				"type" : "SPELL_RESISTANCE_AURA",
+				"val" : 20
+			},
+			"blinds" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.blind",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CWUNIC.DEF"
@@ -259,9 +329,22 @@
 		"id": 26,
 		"level": 7,
 		"faction": "rampart",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],			//green dragon is a dragon
-						 [ "TWO_HEX_ATTACK_BREATH", 0, 0, 0 ],  	//green dragon's breath
-						 [ "LEVEL_SPELL_IMMUNITY", 3, 0, 0 ] ], 	//green dragon's spell immunity
+		"abilities":
+		{
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"fireBreath" :
+			{
+				"type" : "TWO_HEX_ATTACK_BREATH"
+			},
+			"spellImmunity" :
+			{
+				"type" : "LEVEL_SPELL_IMMUNITY",
+				"val" : 3
+			}
+		},
 		"upgrades": ["goldDragon"],
 		"graphics" :
 		{
@@ -281,9 +364,22 @@
 		"id": 27,
 		"level": 7,
 		"faction": "rampart",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],			//gold dragon is a dragon
-					   [ "TWO_HEX_ATTACK_BREATH", 0, 0, 0 ],  	//gold dragon's breath
-					   [ "LEVEL_SPELL_IMMUNITY", 4, 0, 0 ] ], 	//gold dragon's spell immunity
+		"abilities":
+		{
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE"
+			},
+			"fireBreath" :
+			{
+				"type" : "TWO_HEX_ATTACK_BREATH"
+			},
+			"spellImmunity" :
+			{
+				"type" : "LEVEL_SPELL_IMMUNITY",
+				"val" : 4
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CDDRAG.DEF"

+ 16 - 16
config/creatures/special.json

@@ -4,25 +4,29 @@
 	"unused122" :
 	{
 		"faction": "neutral",
-		"special" : true,
+		"disabled" : true,
+		"graphics" : null,
 		"id" : 122
 	},
 	"unused124" :
 	{
 		"faction": "neutral",
-		"special" : true,
+		"disabled" : true,
+		"graphics" : null,
 		"id" : 124
 	},
 	"unused126" :
 	{
 		"faction": "neutral",
-		"special" : true,
+		"disabled" : true,
+		"graphics" : null,
 		"id" : 126
 	},
 	"unused128" :
 	{
 		"faction": "neutral",
-		"special" : true,
+		"disabled" : true,
+		"graphics" : null,
 		"id" : 128
 	},
 
@@ -37,8 +41,7 @@
 			"animation": "SMCATA.DEF",
 			"missile" :
 			{
-				"projectile": "SMCATX.DEF",
-				"spinning": true
+				"projectile": "SMCATX.DEF"
 			}
 		},
 		"sound" :
@@ -59,8 +62,7 @@
 			"animation": "SMBAL.DEF",
 			"missile" :
 			{
-				"projectile": "SMBALX.DEF",
-				"spinning": false
+				"projectile": "SMBALX.DEF"
 			}
 		},
 		"sound" :
@@ -76,7 +78,7 @@
 		"id": 147,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "HEALER", 0, 0, 0 ] ],
+		"abilities": { "heals" : { "type" : "HEALER" } },
 		"graphics" :
 		{
 			"animation": "SMTENT.DEF"
@@ -93,7 +95,7 @@
 		"id": 148,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "NOT_ACTIVE", 0, 0, 0 ] ],
+		"abilities": { "inactive" : { "type" : "NOT_ACTIVE" } },
 		"graphics" :
 		{
 			"animation": "SMCART.DEF"
@@ -110,13 +112,11 @@
 		"id": 149,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "SHOOTER", 0, 0, 0 ] ],
+		"abilities": { "shooter" : { "type" : "SHOOTER" } },
 		"graphics" :
 		{
-			"missile" :
-			{
-				"projectile": "SMBALX.DEF", //workaround for crash
-			}
-		}
+			"animation" : "This should never be used"
+		},
+		"sound": {}
 	}
 }

+ 62 - 16
config/creatures/stronghold.json

@@ -63,7 +63,14 @@
 		"id": 87,
 		"level": 2,
 		"faction": "stronghold",
-		"abilities": [ [ "ADDITIONAL_ATTACK", 1, 0, 0 ] ],
+		"abilities":
+		{
+			"extraAttack" :
+			{
+				"type" : "ADDITIONAL_ATTACK",
+				"val" : 1
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CUWLFR.DEF"
@@ -88,8 +95,7 @@
 			"animation": "CORC.DEF",
 			"missile" :
 			{
-				"projectile": "PORCHX.DEF",
-				"spinning": true
+				"projectile": "PORCHX.DEF"
 			}
 		},
 		"sound" :
@@ -112,8 +118,7 @@
 			"animation": "CORCCH.DEF",
 			"missile" :
 			{
-				"projectile": "PORCHX.DEF",
-				"spinning": true
+				"projectile": "PORCHX.DEF"
 			}
 		},
 		"sound" :
@@ -150,9 +155,25 @@
 		"id": 91,
 		"level": 4,
 		"faction": "stronghold",
-		"abilities": [ [ "SPELLCASTER", 2, "spell.bloodlust", 0 ],
-						 [ "CASTS", 3, 0, 0],
-						 [ "CREATURE_ENCHANT_POWER", 3, 0, 0]],   	   	   	
+		"abilities":
+		{
+			"castsBloodlust" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.bloodlust",
+				"val" : 2
+			},
+			"castsCount" :
+			{
+				"type" : "CASTS",
+				"val" : 3
+			},
+			"castLength" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "COGMAG.DEF"
@@ -191,8 +212,21 @@
 		"id": 93,
 		"level": 5,
 		"faction": "stronghold",
-		"abilities": [ [ "SPECIFIC_SPELL_POWER", 10, "spell.thunderbolt", 0 ],	//10 damage per unit
-						 [ "SPELL_AFTER_ATTACK", 20, "spell.thunderbolt", 0 ] ],   	
+		"abilities":
+		{
+			"thunderStrength" :
+			{
+				"type" : "SPECIFIC_SPELL_POWER",
+				"subtype" : "spell.thunderbolt",
+				"val" : 10
+			},
+			"thunderOnAttack" :
+			{
+				"type" : "SPELL_AFTER_ATTACK",
+				"subtype" : "spell.thunderbolt",
+				"val" : 20
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CTBIRD.DEF"
@@ -217,8 +251,7 @@
 			"animation": "CCYCLR.DEF",
 			"missile" :
 			{
-				"projectile": "PCYCLBX.DEF",
-				"spinning": true
+				"projectile": "PCYCLBX.DEF"
 			}
 		},
 		"sound" :
@@ -241,8 +274,7 @@
 			"animation": "CCYCLLOR.DEF",
 			"missile" :
 			{
-				"projectile": "PCYCLBX.DEF",
-				"spinning": true
+				"projectile": "PCYCLBX.DEF"
 			}
 		},
 		"sound" :
@@ -260,7 +292,14 @@
 		"id": 96,
 		"level": 7,
 		"faction": "stronghold",
-		"abilities": [ [ "ENEMY_DEFENCE_REDUCTION", 40, 0, 0 ] ],		
+		"abilities":
+		{
+			"reduceDefence" :
+			{
+				"type" : "ENEMY_DEFENCE_REDUCTION",
+				"val" : 40
+			}
+		},
 		"upgrades": ["ancientBehemoth"],
 		"graphics" :
 		{
@@ -280,7 +319,14 @@
 		"id": 97,
 		"level": 7,
 		"faction": "stronghold",
-		"abilities": [ [ "ENEMY_DEFENCE_REDUCTION", 80, 0, 0 ] ],
+		"abilities":
+		{
+			"reduceDefence" :
+			{
+				"type" : "ENEMY_DEFENCE_REDUCTION",
+				"val" : 80
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CABEHE.DEF"

+ 139 - 28
config/creatures/tower.json

@@ -31,8 +31,7 @@
 			"animation": "CGREMM.DEF",
 			"missile" :
 			{
-				"projectile": "CPRGRE.DEF",
-				"spinning": false
+				"projectile": "CPRGRE.DEF"
 			}
 		},
 		"sound" :
@@ -50,7 +49,13 @@
 		"id": 30,
 		"level": 2,
 		"faction": "tower",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ] ],  	  		//stone gargoyles are non-living
+		"abilities":
+		{
+			"nonliving" :
+			{
+				"type" : "NON_LIVING"
+			}
+		},
 		"upgrades": ["obsidianGargoyle"],
 		"graphics" :
 		{
@@ -70,7 +75,13 @@
 		"id": 31,
 		"level": 2,
 		"faction": "tower",
-		"abilities": [ [ "NON_LIVING", 0, 0, 0 ] ],  			//obsidian gargoyles are non-living
+		"abilities":
+		{
+			"nonliving" :
+			{
+				"type" : "NON_LIVING"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "COGARG.DEF"
@@ -89,8 +100,19 @@
 		"id": 32,
 		"level": 3,
 		"faction": "tower",
-		"abilities": [ [ "SPELL_DAMAGE_REDUCTION", 50, -1, 0 ],		//stone golems reduce dmg from spells
-					   [ "NON_LIVING", 0, 0, 0 ] ],  			//stone golems are non-living
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "SPELL_DAMAGE_REDUCTION",
+				"subtype" : -1,
+				"val" : 50
+			},
+			"nonliving" :
+			{
+				"type" : "NON_LIVING"
+			}
+		},
 		"upgrades": ["stoneGolem"],
 		"graphics" :
 		{
@@ -110,8 +132,19 @@
 		"id": 33,
 		"level": 3,
 		"faction": "tower",
-		"abilities": [ [ "SPELL_DAMAGE_REDUCTION", 75, -1, 0 ],	   	//iron golems reduce dmg from spells
-					   [ "NON_LIVING", 0, 0, 0 ] ],  			//iron golems are non-living
+		"abilities" :
+		{
+			"magicResistance" :
+			{
+				"type" : "SPELL_DAMAGE_REDUCTION",
+				"subtype" : -1,
+				"val" : 75
+			},
+			"nonliving" :
+			{
+				"type" : "NON_LIVING"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CIGOLE.DEF"
@@ -130,15 +163,21 @@
 		"id": 34,
 		"level": 4,
 		"faction": "tower",
-		"abilities": [ [ "CHANGES_SPELL_COST_FOR_ALLY", 2, 0, 0 ] ],   	//mages reduce spell cost
+		"abilities": 
+		{
+			"reduceSpellCost" : 
+			{
+				"type" : "CHANGES_SPELL_COST_FOR_ALLY",
+				"val" :  2
+			}
+		},
 		"upgrades": ["archMage"],
 		"graphics" :
 		{
 			"animation": "CMAGE.DEF",
 			"missile" :
 			{
-				"projectile": "PMAGEX.DEF",
-				"spinning": false
+				"projectile": "PMAGEX.DEF"
 			}
 		},
 		"sound" :
@@ -156,14 +195,20 @@
 		"id": 35,
 		"level": 4,
 		"faction": "tower",
-		"abilities": [ [ "CHANGES_SPELL_COST_FOR_ALLY", 2, 0, 0 ]],		//archmages reduce spell cost
+		"abilities": 
+		{
+			"reduceSpellCost" : 
+			{
+				"type" : "CHANGES_SPELL_COST_FOR_ALLY",
+				"val" :  2
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CAMAGE.DEF",
 			"missile" :
 			{
-				"projectile": "PMAGEX.DEF",
-				"spinning": false
+				"projectile": "PMAGEX.DEF"
 			}
 		},
 		"sound" :
@@ -181,8 +226,21 @@
 		"id": 36,
 		"level": 5,
 		"faction": "tower",
-		"abilities": [ [ "HATE", 50, 53, 0 ],				  	//master genies hate efreets
-					   [ "HATE", 50, 52, 0 ] ],				  	//genies hate efreet sultans
+		"abilities":
+		{
+			"hateAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.efreet",
+				"val" : 50
+			},
+			"hateArchAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.efreetSultan",
+				"val" : 50
+			}
+		},
 		"upgrades": ["masterGenie"],
 		"graphics" :
 		{
@@ -202,11 +260,36 @@
 		"id": 37,
 		"level": 5,
 		"faction": "tower",
-		"abilities": [ [ "CREATURE_ENCHANT_POWER", 5, 0, 0 ],		//spells last 5 turns
-						 [ "RANDOM_SPELLCASTER", 2, 0, 0 ],  		//master genies cast spells on advanced level
-						 [ "CASTS", 3, 0, 0],
-						 [ "HATE", 50, 53, 0 ],
-						 [ "HATE", 50, 52, 0 ] ],				  	//master genies hate efreet sultans
+		"abilities":
+		{
+			"hateAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.efreet",
+				"val" : 50
+			},
+			"hateArchAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.efreetSultan",
+				"val" : 50
+			},
+			"spellsLength" : 
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 5
+			},
+			"randomSpellcaster" :
+			{
+				"type" : "RANDOM_SPELLCASTER",
+				"val" : 2
+			},
+			"casts" :
+			{
+				"type" : "CASTS",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CSULTA.DEF"
@@ -226,7 +309,13 @@
 		"id": 38,
 		"level": 6,
 		"faction": "tower",
-		"abilities": [ [ "BLOCKS_RETALIATION", 0, 0, 0 ] ],		//nagas block retaliation
+		"abilities" :
+		{
+			"noRetaliation" : 
+			{
+				"type" : "BLOCKS_RETALIATION"
+			}
+		},
 		"upgrades": ["nagaQueen"],
 		"graphics" :
 		{
@@ -246,7 +335,13 @@
 		"id": 39,
 		"level": 6,
 		"faction": "tower",
-		"abilities": [ [ "BLOCKS_RETALIATION", 0, 0, 0 ] ],  	//naga queens block retaliation
+		"abilities" :
+		{
+			"noRetaliation" : 
+			{
+				"type" : "BLOCKS_RETALIATION"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CNAGAG.DEF"
@@ -265,7 +360,13 @@
 		"id": 40,
 		"level": 7,
 		"faction": "tower",
-		"abilities": [ ["MIND_IMMUNITY", 0, 0, 0] ],			//giants are immune to mind spells
+		"abilities" :
+		{
+			"immuneToMind" : 
+			{
+				"type" : "MIND_IMMUNITY"
+			}
+		},
 		"upgrades": ["titan"],
 		"graphics" :
 		{
@@ -285,15 +386,25 @@
 		"id": 41,
 		"level": 7,
 		"faction": "tower",
-		"abilities": [ ["MIND_IMMUNITY", 0, 0, 0],			//Titans are immune to mind spells
-						 [ "HATE", 50, "creature.blackDragon", 0 ] ],					//titans hate black dragons
+		"abilities" :
+		{
+			"immuneToMind" : 
+			{
+				"type" : "MIND_IMMUNITY"
+			},
+			"hateArchAngels" : 
+			{
+				"type" : "HATE",
+				"subtype" : "creature.blackDragon",
+				"val" : 50
+			}
+		},
 		"graphics" :
 		{
 			"animation": "CGTITA.DEF",
 			"missile" :
 			{
-				"projectile": "CPRGTIX.DEF",
-				"spinning": false
+				"projectile": "CPRGTIX.DEF"
 			}
 		},
 		"sound" :

+ 386 - 99
config/creatures/wog.json

@@ -22,7 +22,13 @@
 		"id": 151,
 		"level": 8,
 		"faction": "rampart",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ] ],			//diamond dragon is a dragon
+		"abilities":
+		{
+			"dragon" : 
+			{
+				"type" : "DRAGON_NATURE"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM151Z.DEF"
@@ -46,8 +52,7 @@
 			"animation": "ZM152Z.DEF",
 			"missile" :
 			{
-				"projectile": "CPRGTIX.DEF",
-				"spinning": false
+				"projectile": "CPRGTIX.DEF"
 			}
 		},
 		"sound" :
@@ -65,9 +70,17 @@
 		"id": 153,
 		"level": 8,
 		"faction": "inferno",
-		"abilities": [ [ "FLYING", 0, 1, 0 ] ], 
+		"abilities":
+		{
+			"teleport" :
+			{
+				"type" : "FLYING",
+				"subtype" : 1
+			}
+		},
 		"graphics" :
 		{
+			"missile" : null,
 			"animation": "ZM153Z.DEF"
 		},
 		"sound" :
@@ -78,7 +91,7 @@
 			"move": "ADVLMOVE.wav",
 			"wince": "ADVLWNCE.wav",
 			"startMoving": "ADVLEXT1.wav",
-			"stopMoving": "ADVLEXT2.wav"
+			"endMoving": "ADVLEXT2.wav"
 		}
 	},
 	"bloodDragon" :
@@ -86,8 +99,18 @@
 		"id": 154,
 		"level": 8,
 		"faction": "necropolis",
-		"abilities": [ [ "LIFE_DRAIN", 40, 0, 0 ], //40%
-						[ "DRAGON_NATURE", 0, 0, 0 ] ],			//blood dragon is a dragon
+		"abilities":
+		{
+			"drainsLife" : 
+			{
+				"type" : "LIFE_DRAIN",
+				"val" : 40
+			},
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM154Z.DEF"
@@ -106,7 +129,13 @@
 		"id": 155,
 		"level": 8,
 		"faction": "dungeon",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ] ],			//darkness dragon is a dragon
+		"abilities":
+		{
+			"dragon" :
+			{
+				"type" : "DRAGON_NATURE"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM155Z.DEF"
@@ -143,7 +172,10 @@
 		"id": 157,
 		"level": 8,
 		"faction": "fortress",
-		"ability_remove": [ "SHOOTER" ],	//Hell Hydra certainly does not shoot
+		"abilities" : 
+		{
+			"SHOOTER" : null
+		},
 		"graphics" :
 		{
 			"animation": "ZM157Z.DEF"
@@ -195,7 +227,7 @@
 	},
 	"godWar" :
 	{
-		"special" : true,
+		"disabled" : true,
 		"id": 160,
 		"level": 0,
 		"faction": "neutral",
@@ -206,7 +238,7 @@
 	},
 	"godPeace" :
 	{
-		"special" : true,
+		"disabled" : true,
 		"id": 161,
 		"level": 0,
 		"faction": "neutral",
@@ -217,7 +249,7 @@
 	},
 	"godMana" :
 	{
-		"special" : true,
+		"disabled" : true,
 		"id": 162,
 		"level": 0,
 		"faction": "neutral",
@@ -228,7 +260,7 @@
 	},
 	"godLore" :
 	{
-		"special" : true,
+		"disabled" : true,
 		"id": 163,
 		"level": 0,
 		"faction": "neutral",
@@ -314,7 +346,13 @@
 		"id": 168,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "FLYING", 0, 0, 0 ] ],      //Gorynyches fly
+		"abilities":
+		{
+			"canFly" :
+			{
+				"type" : "FLYING"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM168DG.DEF"
@@ -338,8 +376,7 @@
 			"animation": "ZM169ZL.DEF",
 			"missile" :
 			{
-				"projectile": "CPRZEAX.DEF",
-				"spinning": false
+				"projectile": "CPRZEAX.DEF"
 			}
 		},
 		"sound" :
@@ -362,8 +399,7 @@
 			"animation": "ZM170SW.DEF",
 			"missile" :
 			{
-				"projectile": "PLCBOWX.DEF",
-				"spinning": false
+				"projectile": "PLCBOWX.DEF"
 			}
 		},
 		"sound" :
@@ -386,8 +422,7 @@
 			"animation": "ZM171SR.DEF",
 			"missile" :
 			{
-				"projectile": "PLCBOWX.DEF",
-				"spinning": false
+				"projectile": "PLCBOWX.DEF"
 			}
 		},
 		"sound" :
@@ -425,12 +460,11 @@
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM173M.DEF",
-			"missile" :
-			{
-				"projectile": "CPRGRE.DEF",
-				"spinning": true
-			}
+			"animation": "ZM173M.DEF"
+			//"missile" :
+			//{
+			//	"projectile": "CPRGRE.DEF"
+			//}
 		},
 		"sound" :
 		{
@@ -460,7 +494,7 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "CRUSATTK.wav",
 			"defend": "CRUSDFND.wav",
@@ -475,11 +509,35 @@
 		"id": 175,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.shield", 0 ] ], //expert shield
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.shield",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM175NPC.DEF",
@@ -488,7 +546,7 @@
 				"projectile": "CPRZEAX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "MONKATTK.wav",
 			"defend": "MONKDFND.wav",
@@ -504,11 +562,35 @@
 		"id": 176,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.precision", 0 ] ], //expert precision
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.precision",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM176NPC.DEF",
@@ -517,15 +599,14 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "LICHATTK.wav",
 			"defend": "LICHDFND.wav",
 			"killed": "LICHKILL.wav",
 			"move": "LICHMOVE.wav",
 			"shoot": "LICHSHOT.wav",
-			"wince": "LICHWNCE.wav",
-			"ext1": "LICHATK2.wav"
+			"wince": "LICHWNCE.wav"
 		}
 	},
 	"succubus1" :
@@ -534,11 +615,35 @@
 		"id": 177,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.fireShield", 0 ] ], //expert fire shield
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.fireShield",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM177NPC.DEF",
@@ -547,7 +652,7 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "SGRGATTK.wav",
 			"defend": "SGRGDFND.wav",
@@ -562,11 +667,35 @@
 		"id": 178,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.animateDead", 0 ] ], //expert animate dead
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.animateDead",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM178NPC.DEF",
@@ -575,7 +704,7 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "GNOLATTK.wav",
 			"defend": "GNOLDFND.wav",
@@ -590,11 +719,35 @@
 		"id": 179,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.stoneSkin", 0 ] ], //expert stone skin
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.stoneSkin",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM179NPC.DEF",
@@ -603,7 +756,7 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "PFOEATTK.wav",
 			"defend": "PFOEDFND.wav",
@@ -618,11 +771,35 @@
 		"id": 180,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.cure", 0 ] ], //expert cure
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.cure",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM180NPC.DEF",
@@ -631,7 +808,7 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "TRLLATTK.wav",
 			"defend": "TRLLDFND.wav",
@@ -646,11 +823,35 @@
 		"id": 181,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.haste", 0 ] ], //expert haste
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.haste",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM181NPC.DEF",
@@ -659,7 +860,7 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "AMAGATTK.wav",
 			"defend": "AMAGDFND.wav",
@@ -675,11 +876,35 @@
 		"id": 182,
 		"level": 0,
 		"faction": "neutral",
-		"abilities": [ [ "MAGIC_RESISTANCE", 5, 0, 0 ],
-						[ "CASTS", 1, 0, 0 ] ,
-						[ "CREATURE_ENCHANT_POWER", 1, 0, 0 ] ,
-						[ "CREATURE_SPELL_POWER", 100, 0, 0 ] ,
-						[ "SPELLCASTER", 3, "spell.counterstrike", 0 ] ], //expert counterstrike
+		"abilities":
+		{
+			"magicResistance" :
+			{
+				"type" : "MAGIC_RESISTANCE",
+				"val" : 5
+			},
+			"castsAmount" :
+			{
+				"type" : "CASTS",
+				"val" : 1
+			},
+			"enchant" :
+			{
+				"type" : "CREATURE_ENCHANT_POWER",
+				"val" : 1
+			},
+			"spellpower" :
+			{
+				"type" : "CREATURE_SPELL_POWER",
+				"val" : 100
+			},
+			"canCast" :
+			{
+				"type" : "SPELLCASTER",
+				"subtype" : "spell.counterstrike",
+				"val" : 3
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM182NPC.DEF",
@@ -688,7 +913,7 @@
 				"projectile": "PLCBOWX.DEF"
 			}
 		},
-		"sounds" :
+		"sound" :
 		{
 			"attack": "GENIATTK.wav",
 			"defend": "GENIDFND.wav",
@@ -705,95 +930,139 @@
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM174NPC.DEF"
+			"animation": "ZM174NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"hierophant2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 184,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM175NPC.DEF"
+			"animation": "ZM175NPC.DEF",
+			"missile" :
+			{
+				"projectile": "CPRZEAX.DEF"
+			}
 		}
 	},
 	"templeGuardian2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 185,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM176NPC.DEF"
+			"animation": "ZM176NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"succubus2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 186,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM177NPC.DEF"
+			"animation": "ZM177NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"soulEater2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 187,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM178NPC.DEF"
+			"animation": "ZM178NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"brute2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 188,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM179NPC.DEF"
+			"animation": "ZM179NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"ogreLeader2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 189,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM180NPC.DEF"
+			"animation": "ZM180NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"shaman2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 190,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM181NPC.DEF"
+			"animation": "ZM181NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"astralSpirit2" :
 	{
 		"special" : true,
+		"disabled" : true,
 		"id": 191,
 		"level": 0,
 		"faction": "neutral",
 		"graphics" :
 		{
-			"animation": "ZM182NPC.DEF"
+			"animation": "ZM182NPC.DEF",
+			"missile" :
+			{
+				"projectile": "PLCBOWX.DEF"
+			}
 		}
 	},
 	"sylvanCentaur" :
@@ -806,8 +1075,7 @@
 			"animation": "ZM192Z.DEF",
 			"missile" :
 			{
-				"projectile": "PELFX.DEF",
-				"spinning": true
+				"projectile": "PELFX.DEF"
 			}
 		},
 		"sound" :
@@ -830,8 +1098,7 @@
 			"animation": "ZM193Z.DEF",
 			"missile" :
 			{
-				"projectile": "CPRZEAX.DEF",
-				"spinning": false
+				"projectile": "CPRZEAX.DEF"
 			}
 		},
 		"sound" :
@@ -878,20 +1145,40 @@
 		"id": 196,
 		"level": 10,
 		"faction": "neutral",
-		"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ],
-					 [ "TWO_HEX_ATTACK_BREATH", 0, 0, 0 ],
-					 [ "DRAGON_NATURE", 0, 0, 0 ],
-					 [ "UNDEAD", 0, 0, 0 ],
-					 [ "FLYING", 0, 0, 0 ], 
-					 [ "SPELL_LIKE_ATTACK", 0, "spell.deathCloud", 0 ],
-					 [ "SHOOTER", 0, 0, 0 ] ],
+		"abilities":
+		{
+			"dragon" : 
+			{
+				"type" : "DRAGON_NATURE",
+			},
+			"dragonBreath" :
+			{
+				"type" : "TWO_HEX_ATTACK_BREATH"
+			},
+			"undead" :
+			{
+				"type" : "UNDEAD"
+			},
+			"canFly" : 
+			{
+				"type" : "FLYING"
+			},
+			"deathCloud" :
+			{
+				"type" : "SPELL_LIKE_ATTACK",
+				"subtype" : "spell.deathCloud"
+			},
+			"canShoot" :
+			{
+				"type" : "SHOOTER"
+			}
+		},
 		"graphics" :
 		{
 			"animation": "ZM196Z.DEF",
 			"missile" :
 			{
-				"projectile": "ZSHOT195.DEF",
-				"spinning": false
+				"projectile": "ZSHOT195.DEF"
 			}
 		},
 		"sound" :

+ 1 - 0
config/factions/neutral.json

@@ -1,6 +1,7 @@
 {
 	"neutral" :
 	{
+		"name" : "Neutral",
 		"index" : 9,
 		"alignment" : "neutral",
 		"creatureBackground" :

+ 45 - 3
config/schemas/artifact.json

@@ -4,6 +4,24 @@
 	"title" : "VCMI artifact format",
 	"description" : "Format used to define new artifacts in VCMI",
 	"required" : [ "class", "graphics", "text", "type", "value" ],
+	
+	"definitions" : {
+		"growingBonusList" : {
+			"type" : "array",
+			"items" : {
+				"type" : "object",
+				"additionalProperties" : false,
+				"properties" : {
+					"level" : {
+						"type" : "number"
+					},
+					"bonus" : { "$ref" : "vcmi:bonus" }
+				}
+			}
+		}
+	},
+
+	"additionalProperties" : false,
 	"properties":{
 		"bonuses": {
 			"type":"array",
@@ -15,6 +33,10 @@
 			"enum" : [ "SPECIAL", "TREASURE", "MINOR", "MAJOR", "RELIC" ],
 			"description": "Artifact class, treasure, minor, major or relic"
 		},
+		"id" : {
+			"type" : "number",
+			"description" : "Private field to break things, do not use."
+		},
 		"components": {
 			"type":"array",
 			"description": "Optional, list of components for combinational artifacts",
@@ -22,8 +44,10 @@
 		},
 		"graphics": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Graphical files associated with the artifact",
-			"required" : [ "iconIndex", "image", "map" ],
+			//"required" : [ "iconIndex", "image", "map" ],
+			"required" : [ "iconIndex" ],
 			"properties":{
 				"iconIndex": {
 					"type":"number",
@@ -43,12 +67,30 @@
 				}
 			}
 		},
+		"growing" : {
+			"type" : "object",
+			"additionalProperties" : false,
+			"properties" : {
+				"bonusesPerLevel" : { "$ref" : "#/definitions/growingBonusList"},
+				"thresholdBonuses" : { "$ref" : "#/definitions/growingBonusList"}
+			}
+		},
 		"slot": {
-			"type":"string",
-			"description": "Slot to which this artifact can be put, if applicable"
+			"description": "Slot to which this artifact can be put, if applicable",
+			"oneOf" : [
+				{
+					"type":"string"
+				},
+				{
+					"type" : "array",
+					"minItems" : 1,
+					"additionalItems" : { "type" : "string" }
+				}
+			]
 		},
 		"text": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Texts associated with artifact",
 			"required" : [ "description", "event", "name" ],
 			"properties":{

+ 20 - 10
config/schemas/bonus.json

@@ -4,6 +4,8 @@
 	"title" : "VCMI bonus system format",
 	"description" : "Subsection of several formats, used to add generic bonuses to objects",
 	"required": ["type"],
+
+	"additionalProperties" : false,
 	"properties":{
 		"addInfo": {
 			"anyOf" : [
@@ -28,18 +30,26 @@
 			"type":"array",
 			"description": "limiters",
 			"items": {
-				"type":"object",
-				"properties" : {
-					"parameters": {
-						"type":"array",
-						"description" : "parameters",
-						"additionalItems": true
+				"oneOf" : [
+					{
+						"type":"object",
+						"additionalProperties" : false,
+						"properties" : {
+							"parameters": {
+								"type":"array",
+								"description" : "parameters",
+								"additionalItems": true
+							},
+							"type": {
+								"type":"string",
+								"description": "type",
+							}
+						}
 					},
-					"type": {
-						"type":"string",
-						"description": "type",
+					{
+						"type" : "string"
 					}
-				}
+				]
 			}
 		},
 		"propagator": {

+ 60 - 24
config/schemas/creature.json

@@ -3,14 +3,25 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI creature format",
 	"description": "Json format for defining new creatures in VCMI",
-	"required" : [
-		"name", "faction", "cost", "level", "fightValue", "aiValue",
-		"attack", "defense", "hitPoints", "speed", "damage", "advMapAmount",
-		"graphics", "sound"
+	"required" : [ "faction" ],
+	"anyOf" : [
+		{
+			"disabled" : { "enum" : [ true ] }
+		},
+		{
+			"required" : [
+				"name", "cost", "level", "fightValue", "aiValue",
+				"attack", "defense", "hitPoints", "speed", "damage", "advMapAmount",
+				"graphics", "sound"
+			]
+		}
 	],
+	
+	"additionalProperties" : false,
 	"properties":{
 		"name": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Translatable names for this creature",
 			"required" : [ "singular", "plural" ],
 			"properties":{
@@ -24,12 +35,26 @@
 				}
 			}
 		},
+		"abilityText" : {
+			"type" : "string",
+			"description" : "Text version of creature abilities. Used only with original creature window"
+		},
+		"id" : {
+			"type" : "number",
+			"description" : "Private field to break things, do not use."
+		},
+		"extraNames" : {
+			"type" : "array",
+			"items" : { "type" : "string" },
+			"description" : "Private field to break things, do not use."
+		},
 		"faction": {
 			"type":"string",
 			"description": "Faction this creature belongs to. Examples: castle, rampart"
 		},
 		"cost": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Cost to recruit this creature",
 			"properties":{
 				"wood":    { "type":"number"},
@@ -45,6 +70,10 @@
 			"type":"boolean",
 			"description": "Marks this object as special and not available by default"
 		},
+		"disabled": {
+			"type":"boolean",
+			"description": "Object is competely disabled and may not be even loaded in-game"
+		},
 		"level": { "type":"number"},
 		"fightValue": {
 			"type":"number",
@@ -70,6 +99,7 @@
 
 		"damage": {
 			"type":"object",
+			"additionalProperties" : false,
 			"properties":{
 				"max": { "type":"number" },
 				"min": { "type":"number" }
@@ -81,6 +111,7 @@
 		},
 		"advMapAmount": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description" : "Initial size of random stacks on adventure map",
 			"properties":{
 				"min": { "type":"number" },
@@ -101,15 +132,18 @@
 			"description": "creature may receive \"week of\" events"
 		},
 		"abilities": {
-			"type":"array",
 			"description": "Creature abilities described using Bonus system",
-			"items": { "$ref" : "vcmi:bonus" }
+			"type":"object",
+			"additionalProperties": {
+				"$ref" : "vcmi:bonus"
+			}
 		},
 		"stackExperience": {
 			"type":"array",
 			"description": "Stack experience, using bonus system",
 			"items":{
 				"type":"object",
+				"additionalProperties" : false,
 				"required" : [ "bonus", "values" ],
 				"description": "0",
 				"properties":{
@@ -119,7 +153,6 @@
 						"minItems" : 10,
 						"maxItems" : 10,
 						"description": "Strength of the bonus",
-						"additionalItems" : true,
 						"anyof" : [
 							{ "items": { "type" : "number"  } },
 							{ "items": { "type" : "boolean" } }
@@ -130,14 +163,16 @@
 		},
 		"graphics": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Describes how this creature looks like during battles",
 			"required" : [
-				"animationTime", "iconLarge", "iconSmall", "iconIndex",
-				"map", "animation", "attackClimaxFrame", "timeBetweenFidgets"
+				"animationTime", "iconIndex",
+				"map", "animation", "timeBetweenFidgets"
 			],
 			"properties":{
 				"animationTime": {
 					"type":"object",
+					"additionalProperties" : false,
 					"required" : [ "attack", "flight", "walk" ],
 					"description": "Length of several animations",
 					"properties":{
@@ -176,13 +211,10 @@
 					"type":"string",
 					"description": ".def file with animation of this creature in battles"
 				},
-				"attackClimaxFrame": {
-					"type":"number",
-					"description": "Frame from attack animation during which creature deals damage"
-				},
 				"missile": {
 					"type":"object",
-					"required" : [ "projectile", "frameAngles", "offset" ],
+					"additionalProperties" : false,
+					"required" : [ "projectile", "frameAngles", "offset", "attackClimaxFrame" ],
 					"description": "Missile description for archers",
 					"properties":{
 						"projectile": {
@@ -201,6 +233,7 @@
 						},
 						"offset": {
 							"type":"object",
+							"additionalProperties" : false,
 							"required" : [ "lowerX", "lowerY", "middleX", "middleY", "upperX", "upperY" ],
 							"description": "Position where projectile image appears during shooting in specific direction",
 							"properties":{
@@ -211,6 +244,10 @@
 								"upperX":  { "type":"number" },
 								"upperY":  { "type":"number" }
 							}
+						},
+						"attackClimaxFrame": {
+							"type":"number",
+							"description": "Frame from attack animation during which creature deals damage"
 						}
 					}
 				},
@@ -226,18 +263,17 @@
 		},
 		"sound": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Various sound files associated with this creature",
 			"properties":{
-				"attack":    { "type":"string" },
-				"defend":    { "type":"string" },
-				"killed":    { "type":"string" },
-				"moveEnd":   { "type":"string" },
-				"moveStart": { "type":"string" },
-				"move":      { "type":"string" },
-				"shoot":     { "type":"string" },
-				"wince":     { "type":"string" },
-				"ext1":      { "type":"string" },
-				"ext2":      { "type":"string" }
+				"attack":      { "type":"string" },
+				"defend":      { "type":"string" },
+				"killed":      { "type":"string" },
+				"startMoving": { "type":"string" },
+				"endMoving":   { "type":"string" },
+				"move":        { "type":"string" },
+				"shoot":       { "type":"string" },
+				"wince":       { "type":"string" }
 			}
 		}
 	}

+ 16 - 1
config/schemas/faction.json

@@ -7,11 +7,17 @@
 	"dependencies" : {
 		"town" : [ "puzzleMap", "commander" ]
 	},
+	
+	"additionalProperties" : false,
 	"properties":{
 		"name" : {
 			"type" : "string",
 			"description" : "Translatable name of town"
 		},
+		"index" : {
+			"type" : "number",
+			"description" : "Private field to break things, do not use."
+		},
 		"alignment": {
 			"type":"string",
 			"enum" : [ "good", "neutral", "evil" ],
@@ -23,6 +29,7 @@
 		},
 		"creatureBackground": {
 			"type":"object",
+			"additionalProperties" : false,
 			"required" : [ "120px", "130px" ],
 			"description": "Backgrounds for creature info card",
 			"properties":{
@@ -42,6 +49,7 @@
 		},
 		"puzzleMap": {
 			"type":"object",
+			"additionalProperties" : false,
 			"required" : [ "prefix", "pieces" ],
 			"description": "Puzzle map from obelisks for this town. Must contain 48 pieces",
 			"properties":{
@@ -52,6 +60,7 @@
 					"maxItems" : 48,
 					"items": {
 						"type":"object",
+						"additionalProperties" : false,
 						"properties":{
 							"index": { "type":"number", "description" : "Order in which images will be opened" },
 							"x":     { "type":"number", "description" : "X coordinate on screen" },
@@ -67,15 +76,17 @@
 		},
 		"town": {
 			"type":"object",
+			"additionalProperties" : false,
 			"required" : [
 				"adventureMap", "buildingsIcons", "buildings", "creatures", "guildWindow", "names",
 				"hallBackground", "hallSlots", "horde", "icons", "mageGuild", "moatDamage",
-				"musicTheme", "siege", "structures", "townBackground", "warMachine", "primaryResource"
+				"musicTheme", "siege", "structures", "townBackground", "warMachine"
 			],
 			"description": "town",
 			"properties":{
 				"adventureMap": {
 					"type":"object",
+					"additionalProperties" : false,
 					"description": "Paths to images of object on adventure map",
 					"required" : [ "capitol", "castle", "village" ],
 					"properties":{
@@ -96,6 +107,7 @@
 							"description" : "Dwellings on adventure map",
 							"items" : {
 								"type" : "object",
+								"additionalProperties" : false,
 								"properties" : {
 									"name":     { "type":"string" },
 									"graphics": { "type":"string" }
@@ -176,11 +188,13 @@
 				},
 				"icons": {
 					"type":"object",
+					"additionalProperties" : false,
 					"description": "Town icons",
 					"required" : [ "fort", "village" ],
 					"properties":{
 						"fort": {
 							"type":"object",
+							"additionalProperties" : false,
 							"required" : [ "normal", "built" ],
 							"description": "Icons for town with built fort",
 							"properties":{
@@ -196,6 +210,7 @@
 						},
 						"village": {
 							"type":"object",
+							"additionalProperties" : false,
 							"required" : [ "normal", "built" ],
 							"description": "Icons for town without fort",
 							"properties":{

+ 20 - 2
config/schemas/hero.json

@@ -4,6 +4,8 @@
 	"title" : "VCMI hero format",
 	"description" : "Format used to define new heroes in VCMI",
 	"required": [ "army", "class", "images", "skills", "texts" ],
+
+	"additionalProperties" : false,
 	"properties":{
 		"army": {
 			"type":"array",
@@ -12,6 +14,7 @@
 			"maxItems" : 3,
 			"items": {
 				"type":"object",
+				"additionalProperties" : false,
 				"required" : [ "creature", "min", "max" ],
 				"properties":{
 					"creature": {
@@ -29,10 +32,14 @@
 				}
 			}
 		},
+		"id" : {
+			"type" : "number",
+			"description" : "Private field to break things, do not use."
+		},
 		"special": {
 				"type":"boolean",
 				"description": "Marks this object as special and not available by default"
-			},
+		},
 		"class": {
 			"type":"string",
 			"description": "Hero class, e.g. knight or battleMage"
@@ -43,8 +50,9 @@
 		},
 		"images": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "images",
-			"required": [ "index", "large", "small", "specialtyLarge", "specialtySmall" ],
+			"required": [ "index" ],
 			"properties":{
 				"index": {
 					"type":"number",
@@ -74,6 +82,7 @@
 			"maxItems" : 8,
 			"items": {
 				"type":"object",
+				"additionalProperties" : false,
 				"required" : [ "level", "skill" ],
 				"properties":{
 					"level": {
@@ -88,11 +97,18 @@
 				}
 			}
 		},
+		"specialties" : 
+		{
+			"type" : "array",
+			"description" : "Specialty format used for OH3 heroes. Use \"specialty\" instead",
+			"additionalItems" : true
+		},
 		"specialty": {
 			"type":"array",
 			"description": "Description of hero specialty using bonus system",	
 			"items": {
 				"type":"object",
+				"additionalProperties" : false,
 				"required" : [ "bonuses" ],
 				"properties":{
 					"growsWithLevel" : {
@@ -114,6 +130,7 @@
 		},
 		"texts": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "All translatable texts related to hero",
 			"required" : [ "biography", "name", "specialty" ],
 			"properties":{
@@ -127,6 +144,7 @@
 				},
 				"specialty": {
 					"type":"object",
+					"additionalProperties" : false,
 					"description": "Hero specialty information",
 					"required" : [ "description", "name", "tooltip" ],
 					"properties":{

+ 9 - 0
config/schemas/heroClass.json

@@ -7,14 +7,18 @@
 		"animation", "faction", "highLevelChance", "lowLevelChance",
 		"name", "primarySkills", "secondarySkills", "tavern"
 	],
+
+	"additionalProperties" : false,
 	"properties":{
 		"animation": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Files related to hero animation",
 			"required": [ "battle", "map" ],
 			"properties":{
 				"battle": {
 					"type":"object",
+					"additionalProperties" : false,
 					"description": "Hero animations for battle",
 					"required": [ "female", "male" ],
 					"properties":{
@@ -30,6 +34,7 @@
 				},
 				"map": {
 					"type":"object",
+					"additionalProperties" : false,
 					"description": "Hero animations for adventure map",
 					"required": [ "female", "male" ],
 					"properties":{
@@ -45,6 +50,10 @@
 				}
 			}
 		},
+		"id" : {
+			"type" : "number",
+			"description" : "Private field to break things, do not use."
+		},
 		"faction": {
 			"type":"string",
 			"description": "Faction this hero class belongs to"

+ 3 - 1
config/schemas/mod.json

@@ -4,8 +4,9 @@
 	"title" : "VCMI mod file format",
 	"description" : "Format used to define main mod file (mod.json) in VCMI",
 	"required" : [ "name", "description" ],
-	"properties":{
 
+	"additionalProperties" : false,
+	"properties":{
 		"name": {
 			"type":"string",
 			"description": "Short name of your mod. No more than 2-3 words"
@@ -60,6 +61,7 @@
 				"description" : "list of data sources attached to this mount point",
 				"items": {
 					"type":"object",
+					"additionalProperties" : false,
 					"properties":{
 						"path": {
 							"type":"string",

+ 13 - 2
config/schemas/settings.json

@@ -9,12 +9,14 @@
 			"type" : "string", 
 			"enum" : [ "trace", "debug", "info", "warn", "error" ]
 		}
-	}, 
+	},
+	"additionalProperties" : false,
 	"properties":
 	{
 		"general" : {
 			"type" : "object",
 			"default": {},
+			"additionalProperties" : false,
 			"required" : [ "classicCreatureWindow", "playerName", "showfps", "music", "sound" ],
 			"properties" : {
 				"classicCreatureWindow" : {
@@ -41,11 +43,13 @@
 		},
 		"video" : {
 			"type" : "object",
+			"additionalProperties" : false,
 			"default": {},
 			"required" : [ "screenRes", "bitsPerPixel", "fullscreen" ],
 			"properties" : {
 				"screenRes" : {
 					"type" : "object",
+					"additionalProperties" : false,
 					"required" : [ "width", "height" ],
 					"properties" : {
 						"width"  : { "type" : "number" },
@@ -65,6 +69,7 @@
 		},
 		"adventure" : {
 			"type" : "object",
+			"additionalProperties" : false,
 			"default": {},
 			"required" : [ "heroSpeed", "enemySpeed", "scrollSpeed", "heroReminder" ],
 			"properties" : {
@@ -88,6 +93,7 @@
 		},
 		"battle" : {
 			"type" : "object",
+			"additionalProperties" : false,
 			"default": {},
 			"required" : [ "animationSpeed", "mouseShadow", "cellBorders", "stackRange", "showQueue" ],
 			"properties" : {
@@ -115,6 +121,7 @@
 		},
 		"server" : {
 			"type" : "object",
+			"additionalProperties" : false,
 			"default": {},
 			"required" : [ "server", "port", "localInformation", "playerAI", "neutralAI" ],
 			"properties" : {
@@ -142,6 +149,7 @@
 		},
 		"logging" : {
 			"type" : "object",
+			"additionalProperties" : false,
 			"default" : {},
 			"required" : [ "console", "file", "loggers" ],
 			"properties" : {
@@ -152,7 +160,7 @@
 					"properties" : {
 						"format" : {
 							"type" : "string",
-							"default" : "%l %n [%t] - %m"
+							"default" : "%m"
 						},
 						"threshold" : {
 							"$ref" : "#/definitions/logLevelEnum",
@@ -173,6 +181,7 @@
 							],
 							"items" : {
 								"type" : "object",
+								"additionalProperties" : false,
 								"default" : {},
 								"required" : [ "domain", "level", "color" ],
 								"properties" : {
@@ -186,6 +195,7 @@
 				},
 				"file" : {
 					"type" : "object",
+					"additionalProperties" : false,
 					"default" : {},
 					"required" : [ "format" ],
 					"properties" : {
@@ -200,6 +210,7 @@
 					"default" : [ { "domain" : "global", "level" : "info" } ],
 					"items" : {
 						"type" : "object",
+						"additionalProperties" : false,
 						"required" : [ "level", "domain" ],
 						"properties" : {
 							"domain" : { "type" : "string" },

+ 2 - 0
config/schemas/townBuilding.json

@@ -1,5 +1,6 @@
 {
 	"type":"object",
+	"additionalProperties" : false,
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI town building format",
 	"description" : "Format used to define town buildings in VCMI",
@@ -35,6 +36,7 @@
 		},
 		"cost": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description": "Cost to build this building",
 			"properties":{
 				"wood":    { "type":"number"},

+ 8 - 0
config/schemas/townSiege.json

@@ -1,5 +1,6 @@
 {
 	"type":"object",
+	"additionalProperties" : false,
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI siege screen format",
 	"description" : "Format used to define town siege screen in VCMI",
@@ -12,6 +13,7 @@
 	{
 		"point" : {
 			"type" : "object",
+			"additionalProperties" : false,
 			"required" : [ "x", "y" ],
 			"properties":{
 				"x": { "type":"number" },
@@ -20,6 +22,7 @@
 		},
 		"tower" : {
 			"type" : "object",
+			"additionalProperties" : false,
 			"required" : [ "battlement", "creature", "tower" ],
 			"properties":{
 				"battlement": {
@@ -41,6 +44,7 @@
 	"properties":{
 		"gate": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description" : "Town gates",
 			"properties":{
 				"arch": {
@@ -59,6 +63,7 @@
 		},
 		"moat": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description" : "Castle moat description",
 			"properties":{
 				"bank": {
@@ -81,6 +86,7 @@
 		},
 		"static": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description" : "Static sections of walls",
 			"properties":{
 				"background": {
@@ -99,6 +105,7 @@
 		},
 		"towers": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description" : "Decription of towers",
 			"properties":{
 				"bottom": { "$ref" : "#/definitions/tower", "description" : "Bottom tower" },
@@ -108,6 +115,7 @@
 		},
 		"walls": {
 			"type":"object",
+			"additionalProperties" : false,
 			"description" : "Destructible sections of the walls",
 			"properties":{
 				"bottomMid": {

+ 1 - 0
config/schemas/townStructure.json

@@ -4,6 +4,7 @@
 	"title" : "VCMI town structures format",
 	"description" : "Format used to define structures visible on town screen in VCMI",
 	"required": [ "animation", "x", "y"],
+	"additionalProperties" : false,
 	"properties":{
 		"animation": {
 			"type":"string",

+ 3 - 2
lib/CArtHandler.cpp

@@ -205,6 +205,7 @@ void CArtHandler::load(bool onlyTxt)
 		JsonNode & artData = h3Data[numeric];
 		JsonUtils::merge(artData, node.second);
 
+		//JsonUtils::validate(artData, "vcmi:artifact", node.first);
 		artifacts[numeric] = loadArtifact(artData);
 		artifacts[numeric]->id = ArtifactID(numeric);
 
@@ -386,11 +387,11 @@ 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())));
+		art->bonusesPerLevel.push_back (std::pair <ui16, Bonus> (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"])));
 	}
 	BOOST_FOREACH (auto b, node["growing"]["thresholdBonuses"].Vector())
 	{
-		art->thresholdBonuses.push_back (std::pair <ui16, Bonus> (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"].Vector())));
+		art->thresholdBonuses.push_back (std::pair <ui16, Bonus> (b["level"].Float(), *JsonUtils::parseBonus (b["bonus"])));
 	}
 }
 

+ 198 - 182
lib/CCreatureHandler.cpp

@@ -7,6 +7,7 @@
 #include "CGameState.h"
 #include "CTownHandler.h"
 #include "CModHandler.h"
+#include "StringConstants.h"
 
 using namespace boost::assign;
 
@@ -19,24 +20,6 @@ using namespace boost::assign;
  * Full text of license available in license.txt file, in main folder
  *
  */
- 
-static inline void registerCreature(const std::string &name, const si32 id)
-{
-	const std::string fullname = "creature." + name;
-	VLC->modh->identifiers.registerObject(fullname,id);
-}
-
-///CCreatureHandler
-
-CCreatureHandler::CCreatureHandler()
-{
-	VLC->creh = this;
-
-	allCreatures.setDescription("All creatures");
-	creaturesOfLevel[0].setDescription("Creatures of unnormalized tier");
-	for(int i = 1; i < ARRAY_COUNT(creaturesOfLevel); i++)
-		creaturesOfLevel[i].setDescription("Creatures of tier " + boost::lexical_cast<std::string>(i));
-}
 
 int CCreature::getQuantityID(const int & quantity)
 {
@@ -182,78 +165,108 @@ static void AddAbility(CCreature *cre, const JsonVector &ability_vec)
 	cre->addNewBonus(nsf);
 }
 
-static void RemoveAbility(CCreature *cre, const JsonNode &ability)
+CCreatureHandler::CCreatureHandler()
 {
-	const std::string type = ability.String();
+	VLC->creh = this;
 
-	auto it = bonusNameMap.find(type);
+	allCreatures.setDescription("All creatures");
+	creaturesOfLevel[0].setDescription("Creatures of unnormalized tier");
+	for(int i = 1; i < ARRAY_COUNT(creaturesOfLevel); i++)
+		creaturesOfLevel[i].setDescription("Creatures of tier " + boost::lexical_cast<std::string>(i));
 
-	if (it == bonusNameMap.end()) {
-		if (type == "DOUBLE_WIDE")
-			cre->doubleWide = false;
-		else
-            logGlobal->errorStream() << "Error: invalid ability type " << type << " in creatures config";
+	loadCommanders();
+}
 
-		return;
-	}
+void CCreatureHandler::loadCommanders()
+{
+	const JsonNode config(ResourceID("config/commanders.json"));
 
-	const int typeNo = it->second;
+	BOOST_FOREACH (auto bonus, config["bonusPerLevel"].Vector())
+	{
+		commanderLevelPremy.push_back(JsonUtils::parseBonus (bonus.Vector()));
+	}
 
-	Bonus::BonusType ecf = static_cast<Bonus::BonusType>(typeNo);
+	int i = 0;
+	BOOST_FOREACH (auto skill, config["skillLevels"].Vector())
+	{
+		skillLevels.push_back (std::vector<ui8>());
+		BOOST_FOREACH (auto skillLevel, skill["levels"].Vector())
+		{
+			skillLevels[i].push_back (skillLevel.Float());
+		}
+		++i;
+	}
 
-	Bonus *b = cre->getBonusLocalFirst(Selector::type(ecf));
-	cre->removeBonus(b);
+	BOOST_FOREACH (auto ability, config["abilityRequirements"].Vector())
+	{
+		std::pair <Bonus, std::pair <ui8, ui8> > a;
+		a.first = *JsonUtils::parseBonus (ability["ability"].Vector());
+		a.second.first = ability["skills"].Vector()[0].Float();
+		a.second.second = ability["skills"].Vector()[1].Float();
+		skillRequirements.push_back (a);
+	}
 }
 
-void CCreatureHandler::loadBonuses(CCreature & ncre, std::string bonuses)
+void CCreatureHandler::loadBonuses(JsonNode & creature, std::string bonuses)
 {
-	static const std::map<std::string,Bonus::BonusType> abilityMap =
+	auto makeBonusNode = [&](std::string type) -> JsonNode
+	{
+		JsonNode ret;
+		ret["type"].String() = type;
+		return ret;
+	};
+
+	static const std::map<std::string, JsonNode> abilityMap =
 	  boost::assign::map_list_of
-	    ("FLYING_ARMY", Bonus::FLYING)
-	    ("SHOOTING_ARMY", Bonus::SHOOTER)
-	    ("SIEGE_WEAPON", Bonus::SIEGE_WEAPON)
-	    ("const_free_attack", Bonus::BLOCKS_RETALIATION)
-	    ("IS_UNDEAD", Bonus::UNDEAD)
-	    ("const_no_melee_penalty",Bonus::NO_MELEE_PENALTY)
-	    ("const_jousting",Bonus::JOUSTING)
-	    ("KING_1",Bonus::KING1)
-	    ("KING_2",Bonus::KING2)
-		("KING_3",Bonus::KING3)
-		("const_no_wall_penalty",Bonus::NO_WALL_PENALTY)
-		("CATAPULT",Bonus::CATAPULT)
-		("MULTI_HEADED",Bonus::ATTACKS_ALL_ADJACENT)
-		("IMMUNE_TO_MIND_SPELLS",Bonus::MIND_IMMUNITY)
-		("HAS_EXTENDED_ATTACK",Bonus::TWO_HEX_ATTACK_BREATH);
+	    ("FLYING_ARMY",            makeBonusNode("FLYING"))
+	    ("SHOOTING_ARMY",          makeBonusNode("SHOOTER"))
+	    ("SIEGE_WEAPON",           makeBonusNode("SIEGE_WEAPON"))
+	    ("const_free_attack",      makeBonusNode("BLOCKS_RETALIATION"))
+	    ("IS_UNDEAD",              makeBonusNode("UNDEAD"))
+	    ("const_no_melee_penalty", makeBonusNode("NO_MELEE_PENALTY"))
+	    ("const_jousting",         makeBonusNode("JOUSTING"))
+	    ("KING_1",                 makeBonusNode("KING1"))
+	    ("KING_2",                 makeBonusNode("KING2"))
+		("KING_3",                 makeBonusNode("KING3"))
+		("const_no_wall_penalty",  makeBonusNode("NO_WALL_PENALTY"))
+		("CATAPULT",               makeBonusNode("CATAPULT"))
+		("MULTI_HEADED",           makeBonusNode("ATTACKS_ALL_ADJACENT"))
+		("IMMUNE_TO_MIND_SPELLS",  makeBonusNode("MIND_IMMUNITY"))
+		("HAS_EXTENDED_ATTACK",    makeBonusNode("TWO_HEX_ATTACK_BREATH"));
 
 	auto hasAbility = [&](const std::string name) -> bool
 	{
 		return boost::algorithm::find_first(bonuses, name);
 	};
+
 	BOOST_FOREACH(auto a, abilityMap)
 	{
 		if(hasAbility(a.first))
-			ncre.addBonus(0, a.second);
+			creature["abilities"][a.first] = a.second;
 	}
 	if(hasAbility("DOUBLE_WIDE"))
-		ncre.doubleWide = true;
+		creature["doubleWide"].Bool() = true;
+
 	if(hasAbility("const_raises_morale"))
 	{
-		ncre.addBonus(+1, Bonus::MORALE);;
-		ncre.getBonusList().back()->addPropagator(make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO));
+		JsonNode node = makeBonusNode("MORALE");
+		node["val"].Float() = 1;
+		node["propagator"].String() = "HERO";
+		creature["abilities"]["const_raises_morale"] = node;
 	}
 	if(hasAbility("const_lowers_morale"))
 	{
-		ncre.addBonus(-1, Bonus::MORALE);;
-		ncre.getBonusList().back()->effectRange = Bonus::ONLY_ENEMY_ARMY;
+		JsonNode node = makeBonusNode("MORALE");
+		node["val"].Float() = 1;
+		node["effectRange"].String() = "ONLY_ENEMY_ARMY";
+		creature["abilities"]["const_lowers_morale"] = node;
 	}
 }
 
-
 void CCreatureHandler::load()
 {
-    logGlobal->traceStream() << "\t\tReading ZCRTRAIT.TXT";
+	std::vector<JsonNode> h3Data;
 
-	////////////reading ZCRTRAIT.TXT ///////////////////
 	CLegacyConfigParser parser("DATA/ZCRTRAIT.TXT");
 
 	parser.endLine(); // header
@@ -265,70 +278,80 @@ void CCreatureHandler::load()
 		while (parser.isNextEntryEmpty())
 			parser.endLine();
 
-		CCreature &ncre = *new CCreature;
-		ncre.idNumber = CreatureID(creatures.size());
-		ncre.cost.resize(GameConstants::RESOURCE_QUANTITY);
-		ncre.level=0;
-		ncre.iconIndex = ncre.idNumber + 2; // +2 for empty\selection images
+		JsonNode data;
 
-		ncre.nameSing = parser.readString();
-		ncre.namePl   = parser.readString();
+		data["graphics"]["iconIndex"].Float() = h3Data.size() + 2; // +2 for empty\selection images
+
+		data["name"]["singular"].String() =  parser.readString();
+		data["name"]["plural"].String() =  parser.readString();
 
 		for(int v=0; v<7; ++v)
-		{
-			ncre.cost[v] = parser.readNumber();
-		}
-		ncre.fightValue = parser.readNumber();
-		ncre.AIValue = parser.readNumber();
-		ncre.growth = parser.readNumber();
-		ncre.hordeGrowth = parser.readNumber();
-
-		ncre.addBonus(parser.readNumber(), Bonus::STACK_HEALTH);
-		ncre.addBonus(parser.readNumber(), Bonus::STACKS_SPEED);
-		ncre.addBonus(parser.readNumber(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
-		ncre.addBonus(parser.readNumber(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
-		ncre.addBonus(parser.readNumber(), Bonus::CREATURE_DAMAGE, 1);
-		ncre.addBonus(parser.readNumber(), Bonus::CREATURE_DAMAGE, 2);
-		ncre.addBonus(parser.readNumber(), Bonus::SHOTS);
-
-		//spells - not used?
-		parser.readNumber();
-		ncre.ammMin = parser.readNumber();
-		ncre.ammMax = parser.readNumber();
-		
-		ncre.abilityText = parser.readString();
-		loadBonuses(ncre, parser.readString()); //Attributes
-
-		creatures.push_back(&ncre);
+			data["cost"][GameConstants::RESOURCE_NAMES[v]].Float() = parser.readNumber();
+
+		data["fightValue"].Float() = parser.readNumber();
+		data["aiValue"].Float() = parser.readNumber();
+		data["growth"].Float() = parser.readNumber();
+		data["horde"].Float() = parser.readNumber();
+
+		data["hitPoints"].Float() = parser.readNumber();
+		data["speed"].Float() = parser.readNumber();
+		data["attack"].Float() = parser.readNumber();
+		data["defense"].Float() = parser.readNumber();
+		data["damage"]["min"].Float() = parser.readNumber();
+		data["damage"]["max"].Float() = parser.readNumber();
+
+		if (float shots = parser.readNumber())
+			data["shots"].Float() = shots;
+
+		if (float spells = parser.readNumber())
+			data["spellPoints"].Float() = spells;
+
+		data["advMapAmount"]["min"].Float() = parser.readNumber();
+		data["advMapAmount"]["max"].Float() = parser.readNumber();
+
+		data["abilityText"].String() = parser.readString();
+		loadBonuses(data, parser.readString()); //Attributes
+
+		h3Data.push_back(data);
 	}
 	while (parser.endLine());
 
-	// loading creatures properties
-    logGlobal->traceStream() << "\t\tReading creatures json configs";
+	loadAnimationInfo(h3Data);
 
 	const JsonNode gameConf(ResourceID("config/gameConfig.json"));
-	const JsonNode config(JsonUtils::assembleFromFiles(gameConf["creatures"].convertTo<std::vector<std::string> >()));
+	JsonNode config(JsonUtils::assembleFromFiles(gameConf["creatures"].convertTo<std::vector<std::string> >()));
+
+	creatures.resize(GameConstants::CREATURES_COUNT);
 
 	BOOST_FOREACH(auto & node, config.Struct())
 	{
-		int creatureID = node.second["id"].Float();
-		CCreature *c = creatures[creatureID];
+		int numeric = node.second["id"].Float();
 
-		loadCreatureJson(c, node.second);
+		JsonUtils::merge(h3Data[numeric], node.second);
 
-		// Main reference name, e.g. royalGriffin
-		c->nameRef = node.first;
-		registerCreature(node.first, c->idNumber);
+		//JsonUtils::validate(h3Data[numeric], "vcmi:creature", node.first);
+
+		creatures[numeric] = loadCreature(h3Data[numeric]);
+		creatures[numeric]->idNumber = CreatureID(numeric);
+
+		VLC->modh->identifiers.registerObject ("creature." + node.first, numeric);
 
 		// Alternative names, if any
-		BOOST_FOREACH(const JsonNode &name, node.second["extraNames"].Vector())
-		{
-			registerCreature(name.String(), c->idNumber);
-		}
+		BOOST_FOREACH(const JsonNode &name, h3Data[numeric]["extraNames"].Vector())
+			VLC->modh->identifiers.registerObject ("creature." + name.String(), numeric);
 	}
 
-	loadAnimationInfo();
+	for (size_t i=0; i < creatures.size(); i++)
+	{
+		if (creatures[i] == nullptr)
+			logGlobal->warnStream() << "Warning: creature with id " << i << " is missing!";
+	}
+
+	loadCrExpBon();
+}
 
+void CCreatureHandler::loadCrExpBon()
+{
 	if (VLC->modh->modules.STACK_EXP) 	//reading default stack experience bonuses
 	{
 		CLegacyConfigParser parser("DATA/CREXPBON.TXT");
@@ -429,76 +452,60 @@ void CCreatureHandler::load()
 			maxExpPerBattle[0] = maxExpPerBattle[7];
 
 	}//end of Stack Experience
-
-    logGlobal->traceStream() << "\t\tReading config/commanders.json";
-	const JsonNode config3(ResourceID("config/commanders.json"));
-
-	BOOST_FOREACH (auto bonus, config3["bonusPerLevel"].Vector())
-	{
-		commanderLevelPremy.push_back(JsonUtils::parseBonus (bonus.Vector()));
-	}
-
-	int i = 0;
-	BOOST_FOREACH (auto skill, config3["skillLevels"].Vector())
-	{
-		skillLevels.push_back (std::vector<ui8>());
-		BOOST_FOREACH (auto skillLevel, skill["levels"].Vector())
-		{
-			skillLevels[i].push_back (skillLevel.Float());
-		}
-		++i;
-	}
-
-	BOOST_FOREACH (auto ability, config3["abilityRequirements"].Vector())
-	{
-		std::pair <Bonus, std::pair <ui8, ui8> > a;
-		a.first = *JsonUtils::parseBonus (ability["ability"].Vector());
-		a.second.first = ability["skills"].Vector()[0].Float();
-		a.second.second = ability["skills"].Vector()[1].Float();
-		skillRequirements.push_back (a);
-	}
 }
 
-void CCreatureHandler::loadAnimationInfo()
+void CCreatureHandler::loadAnimationInfo(std::vector<JsonNode> &h3Data)
 {
 	CLegacyConfigParser parser("DATA/CRANIM.TXT");
 
 	parser.endLine(); // header
 	parser.endLine();
 
-	for(int dd=0; dd<creatures.size(); ++dd)
+	for(int dd=0; dd<GameConstants::CREATURES_COUNT; ++dd)
 	{
 		while (parser.isNextEntryEmpty() && parser.endLine()) // skip empty lines
 			;
 
-		loadUnitAnimInfo(*creatures[dd], parser);
+		loadUnitAnimInfo(h3Data[dd]["graphics"], parser);
+		parser.endLine();
 	}
 }
 
-void CCreatureHandler::loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser & parser)
+void CCreatureHandler::loadUnitAnimInfo(JsonNode & graphics, CLegacyConfigParser & parser)
 {
-	unit.animation.timeBetweenFidgets = parser.readNumber();
-	unit.animation.walkAnimationTime = parser.readNumber();
-	unit.animation.attackAnimationTime = parser.readNumber();
-	unit.animation.flightAnimationDistance = parser.readNumber();
-	///////////////////////
+	graphics["map"].String(); //create empty string. Real value will be loaded from H3 txt's
+	graphics["timeBetweenFidgets"].Float() = parser.readNumber();
 
-	unit.animation.upperRightMissleOffsetX = parser.readNumber();
-	unit.animation.upperRightMissleOffsetY = parser.readNumber();
-	unit.animation.rightMissleOffsetX = parser.readNumber();
-	unit.animation.rightMissleOffsetY = parser.readNumber();
-	unit.animation.lowerRightMissleOffsetX = parser.readNumber();
-	unit.animation.lowerRightMissleOffsetY = parser.readNumber();
+	JsonNode & animationTime = graphics["animationTime"];
+	animationTime["walk"].Float() = parser.readNumber();
+	animationTime["attack"].Float() = parser.readNumber();
+	animationTime["flight"].Float() = parser.readNumber();
 
-	///////////////////////
+	JsonNode & missile = graphics["missile"];
+	JsonNode & offsets = missile["offset"];
 
-	for(int jjj=0; jjj<12; ++jjj)
-		unit.animation.missleFrameAngles.push_back(parser.readNumber());
+	offsets["upperX"].Float() = parser.readNumber();
+	offsets["upperY"].Float() = parser.readNumber();
+	offsets["middleX"].Float() = parser.readNumber();
+	offsets["middleY"].Float() = parser.readNumber();
+	offsets["lowerX"].Float() = parser.readNumber();
+	offsets["lowerY"].Float() = parser.readNumber();
 
-	unit.animation.troopCountLocationOffset= parser.readNumber();
-	unit.animation.attackClimaxFrame = parser.readNumber();
+	for(int i=0; i<12; i++)
+	{
+		JsonNode entry;
+		entry.Float() = parser.readNumber();
+		missile["frameAngles"].Vector().push_back(entry);
+	}
 
-	parser.endLine();
+	graphics["troopCountLocationOffset"].Float() = parser.readNumber();
+
+	missile["attackClimaxFrame"].Float() = parser.readNumber();
+
+	// assume that creature is not a shooter and should not have whole missile field
+	if (missile["frameAngles"].Vector()[0].Float() == 0 &&
+	    missile["attackClimaxFrame"].Float() == 0)
+		graphics.Struct().erase("missile");
 }
 
 void CCreatureHandler::load(std::string creatureID, const JsonNode & node)
@@ -509,7 +516,8 @@ void CCreatureHandler::load(std::string creatureID, const JsonNode & node)
 
 	creatures.push_back(creature);
     logGlobal->traceStream() << "Added creature: " << creatureID;
-	registerCreature(creature->nameRef, creature->idNumber);
+
+	VLC->modh->identifiers.registerObject ("creature." + creature->nameRef, creature->idNumber);
 }
 
 CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
@@ -531,13 +539,12 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
 	cre->addBonus(node["speed"].Float(), Bonus::STACKS_SPEED);
 	cre->addBonus(node["attack"].Float(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
 	cre->addBonus(node["defense"].Float(), Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
-	const JsonNode &  vec = node["damage"];
-	cre->addBonus(vec["min"].Float(), Bonus::CREATURE_DAMAGE, 1);
-	cre->addBonus(vec["max"].Float(), Bonus::CREATURE_DAMAGE, 2);
+	cre->addBonus(node["damage"]["min"].Float(), Bonus::CREATURE_DAMAGE, 1);
+	cre->addBonus(node["damage"]["max"].Float(), Bonus::CREATURE_DAMAGE, 2);
 
-	auto & amounts = node ["advMapAmount"];
-	cre->ammMin = amounts["min"].Float();
-	cre->ammMax = amounts["max"].Float();
+	cre->ammMin = node["advMapAmount"]["min"].Float();
+	cre->ammMax = node["advMapAmount"]["max"].Float();
+	assert(cre->ammMin <= cre->ammMax);
 
 	if (!node["shots"].isNull())
 		cre->addBonus(node["shots"].Float(), Bonus::SHOTS);
@@ -547,13 +554,16 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
 
 	cre->doubleWide = node["doubleWide"].Bool();
 
-	//graphics
 	loadStackExperience(cre, node["stackExperience"]);
+	loadJsonAnimation(cre, node["graphics"]);
+	loadCreatureJson(cre, node);
+	return cre;
+}
 
-	const JsonNode & graphics = node["graphics"];
+void CCreatureHandler::loadJsonAnimation(CCreature * cre, const JsonNode & graphics)
+{
 	cre->animation.timeBetweenFidgets = graphics["timeBetweenFidgets"].Float();
 	cre->animation.troopCountLocationOffset = graphics["troopCountLocationOffset"].Float();
-	cre->animation.attackClimaxFrame = graphics["attackClimaxFrame"].Float();
 
 	const JsonNode & animationTime = graphics["animationTime"];
 	cre->animation.walkAnimationTime = animationTime["walk"].Float();
@@ -569,13 +579,11 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
 	cre->animation.lowerRightMissleOffsetX = offsets["lowerX"].Float();
 	cre->animation.lowerRightMissleOffsetY = offsets["lowerY"].Float();
 
-	cre->animation.missleFrameAngles = missile["frameAngles"].convertTo<std::vector<double>>();
+	cre->animation.attackClimaxFrame = missile["attackClimaxFrame"].Float();
+	cre->animation.missleFrameAngles = missile["frameAngles"].convertTo<std::vector<double> >();
 
 	cre->advMapDef = graphics["map"].String();
 	cre->iconIndex = graphics["iconIndex"].Float();
-
-	loadCreatureJson(cre, node);
-	return cre;
 }
 
 void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & config)
@@ -583,21 +591,32 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
 	creature->level = config["level"].Float();
 	creature->animDefName = config["graphics"]["animation"].String();
 
-	BOOST_FOREACH(const JsonNode &ability, config["ability_remove"].Vector())
+	if (config["abilities"].getType() == JsonNode::DATA_STRUCT)
 	{
-		RemoveAbility(creature, ability);
+		BOOST_FOREACH(auto &ability, config["abilities"].Struct())
+		{
+			if (!ability.second.isNull())
+			{
+				auto b = JsonUtils::parseBonus(ability.second);
+				b->source = Bonus::CREATURE_ABILITY;
+				b->duration = Bonus::PERMANENT;
+				creature->addNewBonus(b);
+			}
+		}
 	}
-
-	BOOST_FOREACH(const JsonNode &ability, config["abilities"].Vector())
+	else
 	{
-		if (ability.getType() == JsonNode::DATA_VECTOR)
-			AddAbility(creature, ability.Vector()); // used only for H3 creatures
-		else
+		BOOST_FOREACH(const JsonNode &ability, config["abilities"].Vector())
 		{
-			auto b = JsonUtils::parseBonus(ability);
-			b->source = Bonus::CREATURE_ABILITY;
-			b->duration = Bonus::PERMANENT;
-			creature->addNewBonus(b);
+			if (ability.getType() == JsonNode::DATA_VECTOR)
+				AddAbility(creature, ability.Vector()); // used only for H3 creatures
+			else
+			{
+				auto b = JsonUtils::parseBonus(ability);
+				b->source = Bonus::CREATURE_ABILITY;
+				b->duration = Bonus::PERMANENT;
+				creature->addNewBonus(b);
+			}
 		}
 	}
 
@@ -618,9 +637,8 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
 		doubledCreatures.insert(creature->idNumber);
 
 	creature->animation.projectileImageName = config["graphics"]["missile"]["projectile"].String();
-	//creature->animation.projectileSpin = config["graphics"]["missile"]["spinning"].Bool();
 
-	creature->special = config["special"].Bool();
+	creature->special = config["special"].Bool() || config["disabled"].Bool();
 
 	const JsonNode & sounds = config["sound"];
 
@@ -631,8 +649,6 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
 	GET_SOUND_VALUE(move);
 	GET_SOUND_VALUE(shoot);
 	GET_SOUND_VALUE(wince);
-	GET_SOUND_VALUE(ext1);
-	GET_SOUND_VALUE(ext2);
 	GET_SOUND_VALUE(startMoving);
 	GET_SOUND_VALUE(endMoving);
 #undef GET_SOUND_VALUE

+ 10 - 8
lib/CCreatureHandler.h

@@ -79,14 +79,12 @@ public:
 		std::string move;
 		std::string shoot; // range attack
 		std::string wince; // attacked but did not die
-		std::string ext1;  // creature specific extension
-		std::string ext2;  // creature specific extension
-		std::string startMoving; // usually same as ext1
-		std::string endMoving;	// usually same as ext2
+		std::string startMoving;
+		std::string endMoving;
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
-			h & attack & defend & killed & move & shoot & wince & ext1 & ext2 & startMoving & endMoving;
+			h & attack & defend & killed & move & shoot & wince & startMoving & endMoving;
 		}
 	} sounds;
 
@@ -140,6 +138,7 @@ private:
 	CBonusSystemNode allCreatures;
 	CBonusSystemNode creaturesOfLevel[GameConstants::CREATURES_PER_TOWN + 1];//index 0 is used for creatures of unknown tier or outside <1-7> range
 
+	void loadJsonAnimation(CCreature * creature, const JsonNode & graphics);
 	void loadStackExperience(CCreature * creature, const JsonNode &input);
 	void loadCreatureJson(CCreature * creature, const JsonNode & config);
 public:
@@ -159,9 +158,10 @@ public:
 	/// loading functions
 
 	/// adding abilities from ZCRTRAIT.TXT
-	void loadBonuses(CCreature & creature, std::string bonuses);
+	void loadBonuses(JsonNode & creature, std::string bonuses);
 	/// load all creatures from H3 files
 	void load();
+	void loadCommanders();
 	/// load creature from json structure
 	void load(std::string creatureID, const JsonNode & node);
 	/// load one creature from json config
@@ -169,9 +169,11 @@ public:
 	/// generates tier-specific bonus tree entries
 	void buildBonusTreeForTiers();
 	/// read cranim.txt file from H3
-	void loadAnimationInfo();
+	void loadAnimationInfo(std::vector<JsonNode> & h3Data);
 	/// read one line from cranim.txt
-	void loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser &parser);
+	void loadUnitAnimInfo(JsonNode & unit, CLegacyConfigParser &parser);
+	/// load all creatures from H3 files
+	void loadCrExpBon();
 	/// parse crexpbon.txt file from H3
 	void loadStackExp(Bonus & b, BonusList & bl, CLegacyConfigParser &parser);
 	/// help function for parsing CREXPBON.txt

+ 3 - 0
lib/CHeroHandler.cpp

@@ -123,6 +123,7 @@ void CHeroClassHandler::load()
 		JsonNode & classData = h3Data[numeric];
 		JsonUtils::merge(classData, node.second);
 
+		//JsonUtils::validate(classData, "vcmi:heroClass", node.first);
 		heroClasses[numeric] = loadClass(classData);
 		heroClasses[numeric]->id = numeric;
 
@@ -457,6 +458,8 @@ void CHeroHandler::loadHeroes()
 	{
 		ui32 identifier = entry.second["id"].Float();
 		JsonUtils::merge(h3Data[identifier], entry.second);
+
+		//JsonUtils::validate(h3Data[identifier], "vcmi:hero", entry.first);
 		CHero * hero = loadHero(h3Data[identifier]);
 		hero->ID = identifier;
 		heroes[identifier] = hero;

+ 3 - 0
lib/CTownHandler.cpp

@@ -70,6 +70,8 @@ JsonNode readBuilding(CLegacyConfigParser & parser)
 	BOOST_FOREACH(const std::string & resID, GameConstants::RESOURCE_NAMES)
 		cost[resID].Float() = parser.readNumber();
 
+	cost.Struct().erase("mithril"); // erase mithril to avoid confusing validator
+
 	parser.endLine();
 	return ret;
 }
@@ -588,6 +590,7 @@ void CTownHandler::load()
 	}
 	BOOST_FOREACH(auto & entry, buildingsConf.Struct())
 	{
+		//JsonUtils::validate(entry.second, "vcmi:faction", entry.first);
 		load(entry.first, entry.second);
 	}
 }

+ 2 - 1
lib/HeroBonus.cpp

@@ -66,7 +66,8 @@ const bmap<std::string, TLimiterPtr> bonusLimiterMap = boost::assign::map_list_o
 const bmap<std::string, TPropagatorPtr> bonusPropagatorMap = boost::assign::map_list_of
 	("BATTLE_WIDE", make_shared<CPropagatorNodeType>(CBonusSystemNode::BATTLE))
 	("VISITED_TOWN_AND_VISITOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::TOWN_AND_VISITOR))
-	("PLAYER_PROPAGATOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER));
+	("PLAYER_PROPAGATOR", make_shared<CPropagatorNodeType>(CBonusSystemNode::PLAYER))
+	("HERO", make_shared<CPropagatorNodeType>(CBonusSystemNode::HERO));
 
 
 #define BONUS_LOG_LINE(x) logBonus->traceStream() << x

+ 25 - 14
lib/JsonNode.cpp

@@ -755,18 +755,23 @@ std::string JsonValidator::validateEnum(const JsonNode &node, const JsonVector &
 
 std::string JsonValidator::validatesSchemaList(const JsonNode &node, const JsonNode &schemas, std::string errorMsg, std::function<bool(size_t)> isValid)
 {
-	std::string errors;
 	if (!schemas.isNull())
 	{
+		std::string errors = "<tested schemas>\n";
 		size_t result = 0;
 
 		BOOST_FOREACH(auto & schema, schemas.Vector())
 		{
 			std::string error = validateNode(node, schema);
 			if (error.empty())
+			{
 				result++;
+			}
 			else
-				errors += fail(error);
+			{
+				errors += error;
+				errors += "<end of schema>\n";
+			}
 		}
 		if (isValid(result))
 		{
@@ -794,7 +799,7 @@ std::string JsonValidator::validateNodeType(const JsonNode &node, const JsonNode
 	});
 
 	// data must be valid against one and only one schema
-	errors += validatesSchemaList(node, schema["oneOf"], "Failed to pass only one and only one schema", [&](size_t count)
+	errors += validatesSchemaList(node, schema["oneOf"], "Failed to pass one and only one schema", [&](size_t count)
 	{
 		return count == 1;
 	});
@@ -805,13 +810,6 @@ std::string JsonValidator::validateNodeType(const JsonNode &node, const JsonNode
 		if (validateNode(node, schema["not"]).empty())
 			errors += fail("Successful validation against negative check");
 	}
-	// basic schema check
-	if (!schema["type"].isNull())
-	{
-		JsonNode::JsonType type = stringToType.find(schema["type"].String())->second;
-		if(type != node.getType())
-			errors += fail("Type mismatch!");
-	}
 	return errors;
 }
 
@@ -822,6 +820,9 @@ std::string JsonValidator::validateNode(const JsonNode &node, const JsonNode &sc
 
 	assert(!schema.isNull()); // can this error be triggered?
 
+	if (node.isNull())
+		return ""; // node not present. consider to be "valid"
+
 	if (!schema["$ref"].isNull())
 	{
 		std::string URI = schema["$ref"].String();
@@ -833,6 +834,14 @@ std::string JsonValidator::validateNode(const JsonNode &node, const JsonNode &sc
 		return validateRoot(node, URI);
 	}
 
+	// basic schema check
+	if (!schema["type"].isNull())
+	{
+		JsonNode::JsonType type = stringToType.find(schema["type"].String())->second;
+		if(type != node.getType())
+			return errors + fail("Type mismatch!"); // different type. Any other checks are useless
+	}
+
 	errors += validateNodeType(node, schema);
 
 	// enumeration - data must be equeal to one of items in list
@@ -876,10 +885,10 @@ std::string JsonValidator::validateVectorItem(const JsonVector items, const Json
 		return validateNode(items[index], additional);
 
 	// or, additionalItems field can be bool which indicates if such items are allowed
-	// default = false, so case if additionalItems is not present will be handled as well
-	if (!additional.Bool())
+	if (!additional.isNull() && additional.Bool() == false) // present and set to false - error
 		return fail("Unknown entry found");
 
+	// by default - additional items are allowed
 	return "";
 }
 
@@ -930,9 +939,11 @@ std::string JsonValidator::validateStructItem(const JsonNode &node, const JsonNo
 	if (additional.getType() == JsonNode::DATA_STRUCT)
 		return validateNode(node, additional);
 
-	if (!additional.Bool())
+	// or, additionalItems field can be bool which indicates if such items are allowed
+	if (!additional.isNull() && additional.Bool() == false) // present and set to false - error
 		return fail("Unknown entry found: " + nodeName);
 
+	// by default - additional items are allowed
 	return "";
 }
 
@@ -947,7 +958,7 @@ std::string JsonValidator::validateStruct(const JsonNode &node, const JsonNode &
 
 	BOOST_FOREACH(auto & required, schema["required"].Vector())
 	{
-		if (!vstd::contains(map, required.String()))
+		if (node[required.String()].isNull())
 			errors += fail("Required entry " + required.String() + " is missing");
 	}
 

+ 0 - 3
lib/filesystem/CResourceLoader.cpp

@@ -174,9 +174,6 @@ void CResourceLoader::addLoader(std::string mountPoint, shared_ptr<ISimpleResour
 		ResourceID ident(mountPoint, entry.first.getName(), entry.first.getType());
 		ResourceLocator locator(loader.get(), entry.second);
 
-		if (ident.getType() == EResType::OTHER)
-            logGlobal->warnStream() << "Warning: unknown file type: " << entry.second;
-
 		resources[ident].push_back(locator);
 	}
 }

+ 6 - 3
lib/logging/CLogger.cpp

@@ -266,9 +266,9 @@ CLogFormatter::CLogFormatter() : pattern("%m")
 
 }
 
-CLogFormatter::CLogFormatter(const std::string & pattern) : pattern(pattern)
+CLogFormatter::CLogFormatter(const std::string & pattern)
 {
-
+	setPattern(pattern);
 }
 
 std::string CLogFormatter::format(const LogRecord & record) const
@@ -370,7 +370,10 @@ EConsoleTextColor::EConsoleTextColor CColorMapping::getColorFor(const CLoggerDom
 
 CLogConsoleTarget::CLogConsoleTarget(CConsoleHandler * console) : console(console), threshold(ELogLevel::INFO), coloredOutputEnabled(true)
 {
-    formatter.setPattern("%l %n [%t] - %m");
+    // more verbose version:
+	//formatter.setPattern("%l %n [%t] - %m");
+
+	formatter.setPattern("%m");
 }
 
 void CLogConsoleTarget::write(const LogRecord & record)