Просмотр исходного кода

Merge pull request #106 from vcmi/rmgTemplatesAsMods

Okay, looks stable for now.
DjWarmonger 10 лет назад
Родитель
Сommit
5d067416a1
26 измененных файлов с 1268 добавлено и 826 удалено
  1. 63 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Analogy/Content/config/defaultTemplates/analogy.json
  2. 12 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Analogy/mod.json
  3. 227 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Clash of Dragons/Content/config/defaultTemplates/clashOfDragons.json
  4. 12 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Clash of Dragons/mod.json
  5. 239 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Coldshadows Fantasy/Content/config/defaultTemplates/coldshadowsFantasy.json
  6. 12 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Coldshadows Fantasy/mod.json
  7. 76 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Golden Ring/Content/config/defaultTemplates/goldenRing.json
  8. 12 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Golden Ring/mod.json
  9. 75 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Jebus Cross/Content/config/defaultTemplates/jebusCross.json
  10. 12 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Jebus Cross/mod.json
  11. 65 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Upgrade/Content/config/defaultTemplates/upgrade.json
  12. 12 0
      Mods/vcmi/Mods/defaultTemplates/Mods/Upgrade/mod.json
  13. 9 0
      Mods/vcmi/Mods/defaultTemplates/mod.json
  14. 3 4
      client/CPreGame.cpp
  15. 0 542
      config/rmg.json
  16. 79 0
      config/schemas/template.json
  17. 1 0
      lib/CModHandler.cpp
  18. 2 0
      lib/GameConstants.h
  19. 2 2
      lib/VCMI_Lib.cpp
  20. 82 26
      lib/rmg/CMapGenOptions.cpp
  21. 4 3
      lib/rmg/CMapGenOptions.h
  22. 29 10
      lib/rmg/CMapGenerator.cpp
  23. 178 173
      lib/rmg/CRmgTemplateStorage.cpp
  24. 14 24
      lib/rmg/CRmgTemplateStorage.h
  25. 47 42
      lib/rmg/CRmgTemplateZone.cpp
  26. 1 0
      lib/rmg/CRmgTemplateZone.h

+ 63 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Analogy/Content/config/defaultTemplates/analogy.json

@@ -0,0 +1,63 @@
+{
+	"Analogy" : 
+	{
+		"minSize" : "m", "maxSize" : "m+u",
+		"players" : "4",
+		"zones" :
+		{
+			"1" :
+			{
+				"type" : "playerStart", "size" : 2, "owner" : 1,
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"mines" : {"wood" : 1, "ore" : 1, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1},
+				"treasure" : [
+								{"min" : 2100, "max": 3000, "density" : 5},
+								{"min" : 300, "max": 1500, "density" : 10}
+							] 
+			},
+			"2" :
+			{
+				"type" : "playerStart", "size" : 2, "owner" : 2,
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"3" :
+			{
+				"type" : "playerStart", "size" : 2, "owner" : 3,
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"4" :
+			{
+				"type" : "playerStart", "size" : 2, "owner" : 4,
+				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"5" :
+			{
+				"type" : "treasure", "size" : 3, "terrainTypes" : [ "sand" ], "matchTerrainToTown" : false,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "strong",
+				"mines" : {"gold" : 2},
+				"treasure" : [
+								{"min" : 9000, "max": 10000, "density" : 3},
+								{"min" : 6000, "max": 10000, "density" : 15}
+							]
+			}
+		},
+		"connections" :
+		[
+			{ "a" : "1", "b" : "5", "guard" : 5000 },
+			{ "a" : "2", "b" : "5", "guard" : 5000 },
+			{ "a" : "3", "b" : "5", "guard" : 5000 },
+			{ "a" : "4", "b" : "5", "guard" : 5000 }
+		]
+	}
+}

+ 12 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Analogy/mod.json

@@ -0,0 +1,12 @@
+{
+	"name" : "Analogy",
+	"description" : "Random map template, 4p M/M+U",
+
+	"version" : "0.99",
+	"author" : "VCMI Team",
+	"contact" : "http://forum.vcmi.eu/index.php",
+	"modType" : "Templates",
+	"templates" : [
+		"config/defaultTemplates/analogy"
+	],
+}

+ 227 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Clash of Dragons/Content/config/defaultTemplates/clashOfDragons.json

@@ -0,0 +1,227 @@
+{
+	"Clash of Dragons 1.2":
+	{
+		"minSize" : "l+u", "maxSize" : "xl+u",
+		"players" : "2",
+		"zones":
+		{
+			"1":
+			{
+				"type" : "playerStart", "size" : 100, "owner" : 1,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"monsters" : "weak",
+				"mines" : {"wood" : 1, "ore" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 2000, "max": 5000, "density": 4},
+								{"min" : 5000, "max": 9000, "density": 6},
+								{"min" : 300, "max": 1900, "density": 15}
+							]
+			},
+			"2":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "towns" : 1 },
+				"terrainTypes" : ["grass"], "matchTerrainToTown" : false,
+				"monsters" : "normal",
+				"mines" : {"gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 5000, "max": 12000, "density": 2},
+								{"min" : 10000, "max": 20000, "density": 4},
+								{"min" : 300, "max": 1900, "density": 15}
+							]
+			},
+			"3":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "towns" : 1 },
+				"terrainTypes" : ["lava"], "matchTerrainToTown" : false,
+				"monsters" : "strong",
+				"mines" : {"wood" : 1, "ore" : 1, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 20000, "max": 25000, "density": 10},
+								{"min" : 10000, "max": 15000, "density": 8},
+								{"min" : 300, "max": 1900, "density": 10}
+							]
+			},
+			"4":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "towns" : 1 },
+				"terrainTypes" : ["dirt"], "matchTerrainToTown" : false,
+				"monsters" : "strong",
+				"mines" : {"gold" : 2},
+				"treasure" : [
+								{"min" : 20000, "max": 25000, "density": 15},
+								{"min" : 10000, "max": 15000, "density": 10},
+								{"min" : 300, "max": 1900, "density": 6}
+							]
+			},
+			"5":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "castles" : 1 },
+				"terrainTypes" : ["snow"], "matchTerrainToTown" : false,
+				"allowedTowns" : ["tower"],
+				"monsters" : "strong",
+				"mines" : {"gold" : 2},
+				"treasure" : [
+								{"min" : 20000, "max": 30000, "density": 15}
+							]
+			},
+			"6":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypes" : ["grass"], "matchTerrainToTown" : false,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"mines" : {"mercury" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 80000, "max": 120000, "density": 10}
+							]
+			},
+			"7":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypeLikeZone" : 6,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"mines" : {"sulfur" : 1, "gold" : 1},
+				"treasureLikeZone" : 6
+			},
+			"8":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypeLikeZone" : 6,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"mines" : {"crystal" : 1, "gold" : 1},
+				"treasureLikeZone" : 6
+			},
+			"9":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypeLikeZone" : 6,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"mines" : {"gems" : 1, "gold" : 1},
+				"treasureLikeZone" : 6
+			},
+			"10":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypes" : ["snow"], "matchTerrainToTown" : false,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"mines" : {"mercury" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 80000, "max": 80000, "density": 10}
+							]
+			},
+			"11":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypeLikeZone" : 10,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"minesLikeZone" : 10,
+				"treasureLikeZone" : 10
+			},
+			"12":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypeLikeZone" : 10,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"minesLikeZone" : 10,
+				"treasureLikeZone" : 10
+			},
+			"13":
+			{
+				"type" : "treasure", "size" : 40,
+				"terrainTypeLikeZone" : 10,
+				"allowedTowns" : ["tower", "castle", "neutral"],
+				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
+				"monsters" : "strong",
+				"minesLikeZone" : 10,
+				"treasureLikeZone" : 10
+			},
+			"14":
+			{
+				"type" : "playerStart", "size" : 100, "owner" : 2,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"15":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "towns" : 1 },
+				"terrainTypeLikeZone" : 2,
+				"monsters" : "normal",
+				"minesLikeZone" : 2,
+				"treasureLikeZone" : 2
+			},
+			"16":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "towns" : 1 },
+				"terrainTypeLikeZone" : 3,
+				"monsters" : "strong",
+				"minesLikeZone" : 3,
+				"treasureLikeZone" : 3
+			},
+			"17":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "towns" : 1 },
+				"terrainTypeLikeZone" : 4,
+				"monsters" : "strong",
+				"minesLikeZone" : 4,
+				"treasureLikeZone" : 4
+			},
+			"18":
+			{
+				"type" : "treasure", "size" : 100,
+				"neutralTowns" : { "castles" : 1 },
+				"terrainTypeLikeZone" : 5,
+				"townTypeLikeZone" : 5,
+				"monsters" : "strong",
+				"minesLikeZone" : 5,
+				"treasureLikeZone" : 5
+			}
+		},
+		"connections" :
+		[
+			{ "a" : "1", "b" : "2", "guard" : 4000 },
+			{ "a" : "2", "b" : "3", "guard" : 12000 },
+			{ "a" : "2", "b" : "4", "guard" : 20000 },
+			{ "a" : "4", "b" : "5", "guard" : 30000 },
+			{ "a" : "5", "b" : "6", "guard" : 50000 },
+			{ "a" : "6", "b" : "7", "guard" : 50000 },
+			{ "a" : "7", "b" : "8", "guard" : 50000 },
+			{ "a" : "7", "b" : "9", "guard" : 50000 },
+			{ "a" : "18", "b" : "9", "guard" : 50000 },
+			{ "a" : "5", "b" : "10", "guard" : 50000 },
+			{ "a" : "10", "b" : "11", "guard" : 50000 },
+			{ "a" : "11", "b" : "12", "guard" : 50000 },
+			{ "a" : "11", "b" : "13", "guard" : 50000 },
+			{ "a" : "14", "b" : "15", "guard" : 4000 },
+			{ "a" : "15", "b" : "16", "guard" : 12000 },
+			{ "a" : "15", "b" : "17", "guard" : 20000 },
+			{ "a" : "17", "b" : "18", "guard" : 30000 },
+			{ "a" : "18", "b" : "13", "guard" : 50000 },
+			{ "a" : "18", "b" : "5", "guard" : 150000 },
+			{ "a" : "3", "b" : "16", "guard" : 180000 }
+		]
+	}
+}

+ 12 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Clash of Dragons/mod.json

@@ -0,0 +1,12 @@
+{
+	"name" : "Clash of Dragons",
+	"description" : "Random map template, 1v1 L+U/XL+U",
+
+	"version" : "0.99",
+	"author" : "Malekith, ported by VCMI Team",
+	"contact" : "http://forum.vcmi.eu/index.php",
+	"modType" : "Templates",
+	"templates" : [
+		"config/defaultTemplates/clashOfDragons"
+	],
+}

+ 239 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Coldshadows Fantasy/Content/config/defaultTemplates/coldshadowsFantasy.json

@@ -0,0 +1,239 @@
+{
+	"Coldshadow's Fantasy":
+	{
+		"minSize" : "xl+u", "maxSize" : "xl+u",
+		"players" : "4-8", "cpu" : "3-6",
+		"zones":
+		{
+			"1":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 1,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "normal",
+				"mines" : {"wood" : 2, "ore" : 2, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 7500, "max": 25000, "density": 4},
+								{"min" : 3000, "max": 9000, "density": 6},
+								{"min" : 300, "max": 3000, "density": 8}
+							]
+			},
+			"2":
+			{
+				"type" : "cpuStart", "size" : 30, "owner" : 2,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"3":
+			{
+				"type" : "cpuStart", "size" : 30, "owner" : 3,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"4":
+			{
+				"type" : "cpuStart", "size" : 30, "owner" : 4,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"5":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 5,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"6":
+			{
+				"type" : "cpuStart", "size" : 30, "owner" : 6,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"7":
+			{
+				"type" : "cpuStart", "size" : 30, "owner" : 7,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"8":
+			{
+				"type" : "cpuStart", "size" : 30, "owner" : 8,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 1 },
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"9":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypes" : ["subterra"], "matchTerrainToTown" : false,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "strong",
+				"mines" : {"gems" : 1, "sulfur" : 1, "mercury" : 1, "crystal" : 1},
+				"treasure" : [
+								{"min" : 45000, "max": 75000, "density": 3},
+								{"min" : 15000, "max": 50000, "density": 3},
+								{"min" : 3080, "max": 12500, "density": 4}
+							]
+			},
+			"10":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 9,
+				"treasureLikeZone" : 9
+			},
+			"11":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 9,
+				"treasureLikeZone" : 9
+			},
+			"12":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 9,
+				"treasureLikeZone" : 9
+			},
+			"13":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "strong",
+				"minesLikeZone" : 9,
+				"treasureLikeZone" : 9
+			},
+			"14":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 9,
+				"treasureLikeZone" : 9
+			},
+			"15":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 9,
+				"treasureLikeZone" : 9
+			},
+			"16":
+			{
+				"type" : "treasure", "size" : 15,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 9,
+				"treasureLikeZone" : 9
+			},
+			"17":
+			{
+				"type" : "junction", "size" : 30,
+				"terrainTypeLikeZone" : 9,
+				"allowedTowns" : ["neutral"],
+				"monsters" : "strong",
+				"mines" : {"gold" : 1},
+				"treasure" : [
+								{"min" : 65000, "max": 100000, "density": 3},
+								{"min" : 50000, "max": 100000, "density": 3},
+								{"min" : 10000, "max": 15000, "density": 3}
+							]
+			},
+			"18":
+			{
+				"type" : "junction", "size" : 30,
+				"terrainTypeLikeZone" : 9,
+				"allowedTowns" : ["neutral"],
+				"monsters" : "strong",
+				"minesLikeZone" : 17,
+				"treasureLikeZone" : 17
+			},
+			"19":
+			{
+				"type" : "junction", "size" : 30,
+				"terrainTypeLikeZone" : 9,
+				"allowedTowns" : ["neutral"],
+				"monsters" : "strong",
+				"minesLikeZone" : 17,
+				"treasureLikeZone" : 17
+			},
+			"20":
+			{
+				"type" : "junction", "size" : 30,
+				"terrainTypeLikeZone" : 9,
+				"allowedTowns" : ["neutral"],
+				"monsters" : "strong",
+				"minesLikeZone" : 17,
+				"treasureLikeZone" : 17
+			},
+			"21":
+			{
+				"type" : "treasure", "size" : 20,
+				"terrainTypeLikeZone" : 9,
+				"neutralTowns" : { "castles" : 1 },
+				"monsters" : "strong",
+				"treasure" : [
+								{"min" : 100000, "max": 130000, "density": 3},
+								{"min" : 100000, "max": 150000, "density": 3},
+								{"min" : 20000, "max": 60000, "density": 3}
+							]
+			}
+		},
+		"connections" :
+		[
+			{ "a" : "1", "b" : "9", "guard" : 36000 },
+			{ "a" : "2", "b" : "10", "guard" : 12000 },
+			{ "a" : "3", "b" : "11", "guard" : 12000 },
+			{ "a" : "4", "b" : "12", "guard" : 12000 },
+			{ "a" : "5", "b" : "13", "guard" : 36000 },
+			{ "a" : "6", "b" : "14", "guard" : 12000 },
+			{ "a" : "7", "b" : "15", "guard" : 12000 },
+			{ "a" : "8", "b" : "16", "guard" : 12000 },
+			{ "a" : "9", "b" : "17", "guard" : 75000 },
+			{ "a" : "10", "b" : "17", "guard" : 25000 },
+			{ "a" : "11", "b" : "18", "guard" : 25000 },
+			{ "a" : "12", "b" : "18", "guard" : 25000 },
+			{ "a" : "13", "b" : "19", "guard" : 75000 },
+			{ "a" : "14", "b" : "19", "guard" : 25000 },
+			{ "a" : "15", "b" : "20", "guard" : 25000 },
+			{ "a" : "16", "b" : "20", "guard" : 25000 },
+			{ "a" : "17", "b" : "18", "guard" : 50000 },
+			{ "a" : "19", "b" : "20", "guard" : 50000 },
+			{ "a" : "17", "b" : "21", "guard" : 60000 },
+			{ "a" : "18", "b" : "21", "guard" : 60000 },
+			{ "a" : "19", "b" : "21", "guard" : 60000 },
+			{ "a" : "20", "b" : "21", "guard" : 60000 }
+		]
+	}
+}

+ 12 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Coldshadows Fantasy/mod.json

@@ -0,0 +1,12 @@
+{
+	"name" : "Coldshadow's Fantasy",
+	"description" : "Random map template, 4-8p XL+U",
+
+	"version" : "0.99",
+	"author" : "Coldshadow, ported by VCMI Team",
+	"contact" : "http://forum.vcmi.eu/index.php",
+	"modType" : "Templates",
+	"templates" : [
+		"config/defaultTemplates/coldshadowsFantasy"
+	],
+}

+ 76 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Golden Ring/Content/config/defaultTemplates/goldenRing.json

@@ -0,0 +1,76 @@
+{
+	"Golden Ring" :
+	{
+		"minSize" : "m+u", "maxSize" : "l",
+		"players" : "3",
+		"zones" :
+		{
+			"1" :
+			{
+				"type" : "playerStart", "size" : 2, "owner" : 1,
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"mines" : {"wood" : 1, "ore" : 1},
+				"treasure" : [
+								{"min" : 300, "max": 2000, "density": 15},
+								{"min" : 2100, "max": 2500, "density": 5}
+							]
+			},
+			"2" :
+			{
+				"type" : "playerStart", "size" : 2, "owner" : 2,
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"3" :
+			{
+				"type" : "playerStart", "size" : 2, "owner" : 3,
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"4" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 1,
+					"monsters" : "normal",
+					"mines" : {"gems" : 1, "crystal" : 1},
+					"treasure" : [
+									{"min" : 3000, "max": 10000, "density" : 12},
+									{"min" : 6000, "max": 10000, "density" : 6}
+								]},
+			"5" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 1,
+					"monsters" : "normal",
+					"mines" : {"sulfur" : 1, "mercury" : 1},
+					"treasureLikeZone" : 4},
+			"6" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 2, "monsters" : "normal", "minesLikeZone" : 5, "treasureLikeZone" : 4 },
+			"7" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 2, "monsters" : "normal", "minesLikeZone" : 4, "treasureLikeZone" : 4 },
+			"8" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 3, "monsters" : "normal", "minesLikeZone" : 4, "treasureLikeZone" : 4 },
+			"9" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 3, "monsters" : "normal", "minesLikeZone" : 5, "treasureLikeZone" : 4 },
+			"10" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 },
+					"monsters" : "strong",
+					"mines" : {"gold" : 1},
+					"treasure" : [
+									{"min" : 21000, "max": 25000, "density" : 3},
+									{"min" : 10000, "max": 21000, "density" : 10}
+								]},
+			"11" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 }, "monsters" : "strong", "minesLikeZone" : 10, "treasureLikeZone" : 10 },
+			"12" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 }, "monsters" : "strong", "minesLikeZone" : 10, "treasureLikeZone" : 10 }
+		},
+		"connections" :
+		[
+			{ "a" : "1", "b" : "4", "guard" : 2500 },
+			{ "a" : "1", "b" : "5", "guard" : 2500 },
+			{ "a" : "2", "b" : "6", "guard" : 2500 },
+			{ "a" : "2", "b" : "7", "guard" : 2500 },
+			{ "a" : "3", "b" : "8", "guard" : 2500 },
+			{ "a" : "3", "b" : "9", "guard" : 2500 },
+			{ "a" : "4", "b" : "10", "guard" : 20000 },
+			{ "a" : "5", "b" : "12", "guard" : 20000 },
+			{ "a" : "6", "b" : "10", "guard" : 20000 },
+			{ "a" : "7", "b" : "11", "guard" : 20000 },
+			{ "a" : "8", "b" : "12", "guard" : 20000 },
+			{ "a" : "9", "b" : "11", "guard" : 20000 }
+		]
+	}
+}

+ 12 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Golden Ring/mod.json

@@ -0,0 +1,12 @@
+{
+	"name" : "Golden Ring",
+	"description" : "Random map template, 3p M+U/L",
+
+	"version" : "0.99",
+	"author" : "VCMI Team",
+	"contact" : "http://forum.vcmi.eu/index.php",
+	"modType" : "Templates",
+	"templates" : [
+		"config/defaultTemplates/goldenRing"
+	],
+}

+ 75 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Jebus Cross/Content/config/defaultTemplates/jebusCross.json

@@ -0,0 +1,75 @@
+{
+	"Jebus Cross":
+	{
+		"minSize" : "l+u", "maxSize" : "xl+u",
+		"players" : "2-4",
+		"zones":
+		{
+			"1":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 1,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"bannedTowns" : ["necropolis", "conflux"],
+				"monsters" : "weak",
+				"mines" : {"wood" : 4, "ore" : 4, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 2},
+				"treasure" : [
+								{"min" : 12000, "max": 22000, "density": 1},
+								{"min" : 5000, "max": 16000, "density": 6},
+								{"min" : 300, "max": 3000, "density": 14}
+							]
+			},
+			"2":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 2,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"bannedTowns" : ["necropolis", "conflux"],
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"3":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 3,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"bannedTowns" : ["necropolis", "conflux"],
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"4":
+			{
+				"type" : "playerStart", "size" : 30, "owner" : 4,
+				"playerTowns" : { "castles" : 1 },
+				"neutralTowns" : { "towns" : 2 },
+				"bannedTowns" : ["necropolis", "conflux"],
+				"monsters" : "weak",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"5" :
+			{
+				"type" : "treasure", "size" : 40,
+				"neutralTowns" : { "castles" : 2 },
+				"terrainTypes" : [ "sand" ], "matchTerrainToTown" : false,
+				"bannedTowns" : ["necropolis", "conflux"],
+				"monsters" : "strong",
+				"mines" : {"gold" : 4},
+				"treasure" : [
+								{"min" : 35000, "max": 55000, "density" : 3},
+								{"min" : 25000, "max": 35000, "density" : 10},
+								{"min" : 10000, "max": 25000, "density" : 10}
+							]
+			}
+		},
+		"connections" :
+		[
+			{ "a" : "1", "b" : "5", "guard" : 45000 },
+			{ "a" : "2", "b" : "5", "guard" : 45000 },
+			{ "a" : "3", "b" : "5", "guard" : 45000 },
+			{ "a" : "4", "b" : "5", "guard" : 45000 }
+		]
+	}
+}

+ 12 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Jebus Cross/mod.json

@@ -0,0 +1,12 @@
+{
+	"name" : "Jebus Cross",
+	"description" : "Random map template, 4p L+U/XL+U",
+
+	"version" : "0.99",
+	"author" : "Bjorn190, ported by VCMI Team",
+	"contact" : "http://forum.vcmi.eu/index.php",
+	"modType" : "Templates",
+	"templates" : [
+		"config/defaultTemplates/jebusCross"
+	],
+}

+ 65 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Upgrade/Content/config/defaultTemplates/upgrade.json

@@ -0,0 +1,65 @@
+{
+	"Upgrade" :
+	{
+		"minSize" : "s+u", "maxSize" : "m",
+		"players" : "2",
+		"zones" :
+		{
+			"1" :
+			{
+				"type" : "playerStart", "size" : 3, "owner" : 1,
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"mines" : {"wood" : 1, "ore" : 1},
+				"treasure" : [
+								{"min" : 400, "max": 1500, "density" : 16},
+								{"min" : 1500, "max": 2500, "density" : 4}
+								]
+			},
+			"2" :
+			{
+				"type" : "playerStart", "size" : 3, "owner" : 2,
+				"playerTowns" : { "castles" : 1 },
+				"monsters" : "normal",
+				"minesLikeZone" : 1,
+				"treasureLikeZone" : 1
+			},
+			"3" :
+			{
+				"type" : "treasure", "size" : 4, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 1,
+				"monsters" : "weak",
+				"mines" : {"gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
+				"treasure" : [
+								{"min" : 2000, "max": 4000, "density" : 15},
+								{"min" : 4000, "max": 5000, "density" : 5}
+							]
+			},
+			"4" :
+			{
+				"type" : "treasure", "size" : 4, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 2,
+				"monsters" : "weak",
+				"minesLikeZone" : 3,
+				"treasureLikeZone" : 3
+			},
+			"5" :
+			{
+				"type" : "treasure", "size" : 5, "neutralTowns" : { "castles" : 1 }, "terrainTypes" : [ "sand" ],
+				"monsters" : "strong",
+				"mines" : {"gold" : 2},
+				"treasure" : [
+								{"min" : 11000, "max": 12000, "density" : 5},
+								{"min" : 6000, "max": 11000, "density" : 10}
+							]
+			}
+		},
+		"connections" :
+		[
+			{ "a" : "1", "b" : "3", "guard" : 3000 },
+			{ "a" : "1", "b" : "5", "guard" : 9000 },
+			{ "a" : "2", "b" : "4", "guard" : 3000 },
+			{ "a" : "2", "b" : "5", "guard" : 9000 },
+			{ "a" : "3", "b" : "5", "guard" : 6000 },
+			{ "a" : "4", "b" : "5", "guard" : 6000 }
+		]
+	}
+}

+ 12 - 0
Mods/vcmi/Mods/defaultTemplates/Mods/Upgrade/mod.json

@@ -0,0 +1,12 @@
+{
+	"name" : "Upgrade",
+	"description" : "Random map template, 1v1 S+U/M",
+
+	"version" : "0.99",
+	"author" : "BVCMI Team",
+	"contact" : "http://forum.vcmi.eu/index.php",
+	"modType" : "Templates",
+	"templates" : [
+		"config/defaultTemplates/upgrade"
+	],
+}

+ 9 - 0
Mods/vcmi/Mods/defaultTemplates/mod.json

@@ -0,0 +1,9 @@
+{
+	"name" : "Default templates",
+	"description" : "A set of random map templates for VCMI",
+
+	"version" : "0.99",
+	"author" : "VCMI Team",
+	"contact" : "http://forum.vcmi.eu/index.php",
+	"modType" : "Templates"
+}

+ 3 - 4
client/CPreGame.cpp

@@ -1671,7 +1671,7 @@ CRandomMapTab::CRandomMapTab()
 	{
 		mapGenOptions.setPlayerCount(btnId);
 		deactivateButtonsFrom(teamsCntGroup, btnId);
-		deactivateButtonsFrom(compOnlyPlayersCntGroup, 8 - btnId + 1);
+		deactivateButtonsFrom(compOnlyPlayersCntGroup, btnId);
 		validatePlayersCnt(btnId);
 		if(!SEL->isGuest())
 			updateMapInfo();
@@ -1694,7 +1694,6 @@ CRandomMapTab::CRandomMapTab()
 	compOnlyPlayersCntGroup->pos.y += 285;
 	compOnlyPlayersCntGroup->pos.x += BTNS_GROUP_LEFT_MARGIN;
 	addButtonsWithRandToGroup(compOnlyPlayersCntGroup, numberDefs, 0, 7, NUMBERS_WIDTH, 224, 232);
-	compOnlyPlayersCntGroup->setSelected(0);
 	compOnlyPlayersCntGroup->addCallback([&](int btnId)
 	{
 		mapGenOptions.setCompOnlyPlayerCount(btnId);
@@ -1808,9 +1807,9 @@ void CRandomMapTab::validatePlayersCnt(int playersCnt)
 		mapGenOptions.setTeamCount(playersCnt - 1);
 		teamsCntGroup->setSelected(mapGenOptions.getTeamCount());
 	}
-	if(mapGenOptions.getCompOnlyPlayerCount() > 8 - playersCnt)
+	if(mapGenOptions.getCompOnlyPlayerCount() >= playersCnt)
 	{
-		mapGenOptions.setCompOnlyPlayerCount(8 - playersCnt);
+		mapGenOptions.setCompOnlyPlayerCount(playersCnt - 1);
 		compOnlyPlayersCntGroup->setSelected(mapGenOptions.getCompOnlyPlayerCount());
 	}
 

+ 0 - 542
config/rmg.json

@@ -1,542 +0,0 @@
-// Defines random map templates.
-{
-	"Analogy" : 
-	{
-		"minSize" : "m", "maxSize" : "m+u",
-		"players" : "4",
-		"zones" :
-		{
-			"1" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 1,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
-				"monsters" : "normal",
-				"mines" : {"wood" : 1, "ore" : 1, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1},
-				"treasure" : [
-								{"min" : 2100, "max": 3000, "density" : 5},
-								{"min" : 300, "max": 1500, "density" : 10}
-							] 
-			},
-			"2" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 2,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
-				"monsters" : "normal",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"3" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 3,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
-				"monsters" : "normal",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"4" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 4,
-				"playerTowns" : { "castles" : 1 }, "neutralTowns" : { "towns" : 1 }, "townsAreSameType" : true,
-				"monsters" : "normal",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"5" :
-			{
-				"type" : "treasure", "size" : 3, "terrainTypes" : [ "sand" ], "matchTerrainToTown" : false,
-				"neutralTowns" : { "castles" : 1 },
-				"monsters" : "strong",
-				"mines" : {"gold" : 2},
-				"treasure" : [
-								{"min" : 9000, "max": 10000, "density" : 3},
-								{"min" : 6000, "max": 10000, "density" : 15}
-							]
-			}
-		},
-		"connections" :
-		[
-			{ "a" : "1", "b" : "5", "guard" : 5000 },
-			{ "a" : "2", "b" : "5", "guard" : 5000 },
-			{ "a" : "3", "b" : "5", "guard" : 5000 },
-			{ "a" : "4", "b" : "5", "guard" : 5000 }
-		]
-	},
-	"Upgrade" :
-	{
-		"minSize" : "s+u", "maxSize" : "m",
-		"players" : "2",
-		"zones" :
-		{
-			"1" :
-			{
-				"type" : "playerStart", "size" : 3, "owner" : 1,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "normal",
-				"mines" : {"wood" : 1, "ore" : 1},
-				"treasure" : [
-								{"min" : 400, "max": 1500, "density" : 16},
-								{"min" : 1500, "max": 2500, "density" : 4}
-								]
-			},
-			"2" :
-			{
-				"type" : "playerStart", "size" : 3, "owner" : 2,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "normal",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"3" :
-			{
-				"type" : "treasure", "size" : 4, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 1,
-				"monsters" : "weak",
-				"mines" : {"gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
-				"treasure" : [
-								{"min" : 2000, "max": 4000, "density" : 15},
-								{"min" : 4000, "max": 5000, "density" : 5}
-							]
-			},
-			"4" :
-			{
-				"type" : "treasure", "size" : 4, "neutralTowns" : { "towns" : 1 }, "townTypeLikeZone" : 2,
-				"monsters" : "weak",
-				"minesLikeZone" : 3,
-				"treasureLikeZone" : 3
-			},
-			"5" :
-			{
-				"type" : "treasure", "size" : 5, "neutralTowns" : { "castles" : 1 }, "terrainTypes" : [ "sand" ],
-				"monsters" : "strong",
-				"mines" : {"gold" : 2},
-				"treasure" : [
-								{"min" : 11000, "max": 12000, "density" : 5},
-								{"min" : 6000, "max": 11000, "density" : 10}
-							]
-			}
-		},
-		"connections" :
-		[
-			{ "a" : "1", "b" : "3", "guard" : 3000 },
-			{ "a" : "1", "b" : "5", "guard" : 9000 },
-			{ "a" : "2", "b" : "4", "guard" : 3000 },
-			{ "a" : "2", "b" : "5", "guard" : 9000 },
-			{ "a" : "3", "b" : "5", "guard" : 6000 },
-			{ "a" : "4", "b" : "5", "guard" : 6000 }
-		]
-	},
-	"Golden Ring" :
-	{
-		"minSize" : "m+u", "maxSize" : "l",
-		"players" : "3",
-		"zones" :
-		{
-			"1" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 1,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "normal",
-				"mines" : {"wood" : 1, "ore" : 1},
-				"treasure" : [
-								{"min" : 300, "max": 2000, "density": 15},
-								{"min" : 2100, "max": 2500, "density": 5}
-							]
-			},
-			"2" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 2,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "normal",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"3" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 3,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "normal",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"4" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 1,
-					"monsters" : "normal",
-					"mines" : {"gems" : 1, "crystal" : 1},
-					"treasure" : [
-									{"min" : 3000, "max": 10000, "density" : 12},
-									{"min" : 6000, "max": 10000, "density" : 6}
-								]},
-			"5" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 1,
-					"monsters" : "normal",
-					"mines" : {"sulfur" : 1, "mercury" : 1},
-					"treasureLikeZone" : 4},
-			"6" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 2, "monsters" : "normal", "minesLikeZone" : 5, "treasureLikeZone" : 4 },
-			"7" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 2, "monsters" : "normal", "minesLikeZone" : 4, "treasureLikeZone" : 4 },
-			"8" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 3, "monsters" : "normal", "minesLikeZone" : 4, "treasureLikeZone" : 4 },
-			"9" : { "type" : "treasure", "size" : 1, "terrainTypeLikeZone" : 3, "monsters" : "normal", "minesLikeZone" : 5, "treasureLikeZone" : 4 },
-			"10" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 },
-					"monsters" : "strong",
-					"mines" : {"gold" : 1},
-					"treasure" : [
-									{"min" : 21000, "max": 25000, "density" : 3},
-									{"min" : 10000, "max": 21000, "density" : 10}
-								]},
-			"11" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 }, "monsters" : "strong", "minesLikeZone" : 10, "treasureLikeZone" : 10 },
-			"12" : { "type" : "treasure", "size" : 1, "neutralTowns" : { "towns" : 1 }, "monsters" : "strong", "minesLikeZone" : 10, "treasureLikeZone" : 10 }
-		},
-		"connections" :
-		[
-			{ "a" : "1", "b" : "4", "guard" : 2500 },
-			{ "a" : "1", "b" : "5", "guard" : 2500 },
-			{ "a" : "2", "b" : "6", "guard" : 2500 },
-			{ "a" : "2", "b" : "7", "guard" : 2500 },
-			{ "a" : "3", "b" : "8", "guard" : 2500 },
-			{ "a" : "3", "b" : "9", "guard" : 2500 },
-			{ "a" : "4", "b" : "10", "guard" : 20000 },
-			{ "a" : "5", "b" : "12", "guard" : 20000 },
-			{ "a" : "6", "b" : "10", "guard" : 20000 },
-			{ "a" : "7", "b" : "11", "guard" : 20000 },
-			{ "a" : "8", "b" : "12", "guard" : 20000 },
-			{ "a" : "9", "b" : "11", "guard" : 20000 }
-		]
-	},
-	"Unfair Game" :
-	{
-		"minSize" : "m", "maxSize" : "m",
-		"players" : "2", "cpu" : "2",
-		"zones" :
-		{
-			"1" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 1,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "normal",
-			},
-			"2" :
-			{
-				"type" : "playerStart", "size" : 2, "owner" : 2,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "normal",
-			},
-			"3" :
-			{
-				"type" : "cpuStart", "size" : 3, "owner" : 3,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "weak",
-			},
-			"4" :
-			{
-				"type" : "cpuStart", "size" : 3, "owner" : 4,
-				"playerTowns" : { "castles" : 1 },
-				"monsters" : "weak",
-			},
-			"5" : { "type" : "treasure", "size" : 1, "monsters" : "strong", "terrainTypeLikeZone" : 3 },
-			"6" : { "type" : "treasure", "size" : 1, "monsters" : "strong", "terrainTypeLikeZone" : 4 }
-		},
-		"connections" :
-		[
-			{ "a" : "1", "b" : "3", "guard" : 5000 },
-			{ "a" : "1", "b" : "4", "guard" : 5000 },
-			{ "a" : "2", "b" : "3", "guard" : 5000 },
-			{ "a" : "2", "b" : "4", "guard" : 5000 },
-			{ "a" : "3", "b" : "5", "guard" : 2000 },
-			{ "a" : "4", "b" : "6", "guard" : 2000 }
-		]
-	},
-	"Jebus Cross":
-	{
-		"minSize" : "l+u", "maxSize" : "xl+u",
-		"players" : "4",
-		"zones":
-		{
-			"1":
-			{
-				"type" : "playerStart", "size" : 30, "owner" : 1,
-				"playerTowns" : { "castles" : 1 },
-				"neutralTowns" : { "towns" : 2 },
-				"bannedTowns" : ["necropolis", "conflux"],
-				"monsters" : "weak",
-				"mines" : {"wood" : 4, "ore" : 4, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 2},
-				"treasure" : [
-								{"min" : 12000, "max": 22000, "density": 1},
-								{"min" : 5000, "max": 16000, "density": 6},
-								{"min" : 300, "max": 3000, "density": 14}
-							]
-			},
-			"2":
-			{
-				"type" : "playerStart", "size" : 30, "owner" : 2,
-				"playerTowns" : { "castles" : 1 },
-				"neutralTowns" : { "towns" : 2 },
-				"bannedTowns" : ["necropolis", "conflux"],
-				"monsters" : "weak",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"3":
-			{
-				"type" : "playerStart", "size" : 30, "owner" : 3,
-				"playerTowns" : { "castles" : 1 },
-				"neutralTowns" : { "towns" : 2 },
-				"bannedTowns" : ["necropolis", "conflux"],
-				"monsters" : "weak",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"4":
-			{
-				"type" : "playerStart", "size" : 30, "owner" : 4,
-				"playerTowns" : { "castles" : 1 },
-				"neutralTowns" : { "towns" : 2 },
-				"bannedTowns" : ["necropolis", "conflux"],
-				"monsters" : "weak",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"5" :
-			{
-				"type" : "treasure", "size" : 40,
-				"neutralTowns" : { "castles" : 2 },
-				"terrainTypes" : [ "sand" ], "matchTerrainToTown" : false,
-				"bannedTowns" : ["necropolis", "conflux"],
-				"monsters" : "strong",
-				"mines" : {"gold" : 4},
-				"treasure" : [
-								{"min" : 35000, "max": 55000, "density" : 3},
-								{"min" : 25000, "max": 35000, "density" : 10},
-								{"min" : 10000, "max": 25000, "density" : 10}
-							]
-			}
-		},
-		"connections" :
-		[
-			{ "a" : "1", "b" : "5", "guard" : 45000 },
-			{ "a" : "2", "b" : "5", "guard" : 45000 },
-			{ "a" : "3", "b" : "5", "guard" : 45000 },
-			{ "a" : "4", "b" : "5", "guard" : 45000 }
-		]
-	},
-	"Clash of Dragons 1.2":
-	{
-		"minSize" : "l+u", "maxSize" : "xl+u",
-		"players" : "2",
-		"zones":
-		{
-			"1":
-			{
-				"type" : "playerStart", "size" : 100, "owner" : 1,
-				"playerTowns" : { "castles" : 1 },
-				"neutralTowns" : { "towns" : 2 },
-				"monsters" : "weak",
-				"mines" : {"wood" : 1, "ore" : 1, "gold" : 1},
-				"treasure" : [
-								{"min" : 2000, "max": 5000, "density": 4},
-								{"min" : 5000, "max": 9000, "density": 6},
-								{"min" : 300, "max": 1900, "density": 15}
-							]
-			},
-			"2":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "towns" : 1 },
-				"terrainTypes" : ["grass"], "matchTerrainToTown" : false,
-				"monsters" : "normal",
-				"mines" : {"gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
-				"treasure" : [
-								{"min" : 5000, "max": 12000, "density": 2},
-								{"min" : 10000, "max": 20000, "density": 4},
-								{"min" : 300, "max": 1900, "density": 15}
-							]
-			},
-			"3":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "towns" : 1 },
-				"terrainTypes" : ["lava"], "matchTerrainToTown" : false,
-				"monsters" : "strong",
-				"mines" : {"wood" : 1, "ore" : 1, "gems" : 1, "crystal" : 1, "sulfur" : 1, "mercury" : 1, "gold" : 1},
-				"treasure" : [
-								{"min" : 20000, "max": 25000, "density": 10},
-								{"min" : 10000, "max": 15000, "density": 8},
-								{"min" : 300, "max": 1900, "density": 10}
-							]
-			},
-			"4":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "towns" : 1 },
-				"terrainTypes" : ["dirt"], "matchTerrainToTown" : false,
-				"monsters" : "strong",
-				"mines" : {"gold" : 2},
-				"treasure" : [
-								{"min" : 20000, "max": 25000, "density": 15},
-								{"min" : 10000, "max": 15000, "density": 10},
-								{"min" : 300, "max": 1900, "density": 6}
-							]
-			},
-			"5":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "castles" : 1 },
-				"terrainTypes" : ["snow"], "matchTerrainToTown" : false,
-				"allowedTowns" : ["tower"],
-				"monsters" : "strong",
-				"mines" : {"gold" : 2},
-				"treasure" : [
-								{"min" : 20000, "max": 30000, "density": 15}
-							]
-			},
-			"6":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypes" : ["grass"], "matchTerrainToTown" : false,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"mines" : {"mercury" : 1, "gold" : 1},
-				"treasure" : [
-								{"min" : 80000, "max": 120000, "density": 10}
-							]
-			},
-			"7":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypeLikeZone" : 6,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"mines" : {"sulfur" : 1, "gold" : 1},
-				"treasureLikeZone" : 6
-			},
-			"8":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypeLikeZone" : 6,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"mines" : {"crystal" : 1, "gold" : 1},
-				"treasureLikeZone" : 6
-			},
-			"9":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypeLikeZone" : 6,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"mines" : {"gems" : 1, "gold" : 1},
-				"treasureLikeZone" : 6
-			},
-			"10":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypes" : ["snow"], "matchTerrainToTown" : false,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"mines" : {"mercury" : 1, "gold" : 1},
-				"treasure" : [
-								{"min" : 80000, "max": 80000, "density": 10}
-							]
-			},
-			"11":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypeLikeZone" : 10,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"minesLikeZone" : 10,
-				"treasureLikeZone" : 10
-			},
-			"12":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypeLikeZone" : 10,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"minesLikeZone" : 10,
-				"treasureLikeZone" : 10
-			},
-			"13":
-			{
-				"type" : "treasure", "size" : 40,
-				"terrainTypeLikeZone" : 10,
-				"allowedTowns" : ["tower", "castle", "neutral"],
-				"allowedMonsters" : ["neutral", "rampart", "dungeon"],
-				"monsters" : "strong",
-				"minesLikeZone" : 10,
-				"treasureLikeZone" : 10
-			},
-			"14":
-			{
-				"type" : "playerStart", "size" : 100, "owner" : 2,
-				"playerTowns" : { "castles" : 1 },
-				"neutralTowns" : { "towns" : 2 },
-				"monsters" : "weak",
-				"minesLikeZone" : 1,
-				"treasureLikeZone" : 1
-			},
-			"15":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "towns" : 1 },
-				"terrainTypeLikeZone" : 2,
-				"monsters" : "normal",
-				"minesLikeZone" : 2,
-				"treasureLikeZone" : 2
-			},
-			"16":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "towns" : 1 },
-				"terrainTypeLikeZone" : 3,
-				"monsters" : "strong",
-				"minesLikeZone" : 3,
-				"treasureLikeZone" : 3
-			},
-			"17":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "towns" : 1 },
-				"terrainTypeLikeZone" : 4,
-				"monsters" : "strong",
-				"minesLikeZone" : 4,
-				"treasureLikeZone" : 4
-			},
-			"18":
-			{
-				"type" : "treasure", "size" : 100,
-				"neutralTowns" : { "castles" : 1 },
-				"terrainTypeLikeZone" : 5,
-				"townTypeLikeZone" : 5,
-				"monsters" : "strong",
-				"minesLikeZone" : 5,
-				"treasureLikeZone" : 5
-			}
-		},
-		"connections" :
-		[
-			{ "a" : "1", "b" : "2", "guard" : 4000 },
-			{ "a" : "2", "b" : "3", "guard" : 12000 },
-			{ "a" : "2", "b" : "4", "guard" : 20000 },
-			{ "a" : "4", "b" : "5", "guard" : 30000 },
-			{ "a" : "5", "b" : "6", "guard" : 50000 },
-			{ "a" : "6", "b" : "7", "guard" : 50000 },
-			{ "a" : "7", "b" : "8", "guard" : 50000 },
-			{ "a" : "7", "b" : "9", "guard" : 50000 },
-			{ "a" : "18", "b" : "9", "guard" : 50000 },
-			{ "a" : "5", "b" : "10", "guard" : 50000 },
-			{ "a" : "10", "b" : "11", "guard" : 50000 },
-			{ "a" : "11", "b" : "12", "guard" : 50000 },
-			{ "a" : "11", "b" : "13", "guard" : 50000 },
-			{ "a" : "14", "b" : "15", "guard" : 4000 },
-			{ "a" : "15", "b" : "16", "guard" : 12000 },
-			{ "a" : "15", "b" : "17", "guard" : 20000 },
-			{ "a" : "17", "b" : "18", "guard" : 30000 },
-			{ "a" : "18", "b" : "13", "guard" : 50000 },
-			{ "a" : "18", "b" : "5", "guard" : 150000 },
-			{ "a" : "3", "b" : "16", "guard" : 180000 }
-		]
-	}
-}

+ 79 - 0
config/schemas/template.json

@@ -0,0 +1,79 @@
+{
+
+	"type":"object",
+	"$schema": "",
+
+	"title" : "VCMI random map template format",
+	"description" : "Format used to define random map templates in VCMI",
+
+
+	"definitions" :
+	{
+		"zone":{
+			"type": "object",
+			"required" : ["type", "monsters", "size"],
+			"properties":{
+				"type":{"$ref" : "#/definitions/type"},
+				"size":{"$ref" : "#/definitions/size"},			
+				"playerTowns":{"$ref" : "#/definitions/playerTowns"},				
+				"neuralTowns":{"$ref" : "#/definitions/neuralTowns"},
+				"townsAreSameType":{"$ref" : "#/definitions/townsAreSameType"},
+				"monsters":{"$ref" : "#/definitions/monsters"},
+				"mines":{"$ref" : "#/definitions/mines"},
+				"treasure":{
+					"type":"array",
+					"items":{
+						"type": "object",
+						"properties":{
+							"min": {"type":"number", "minimum" : 0},
+							"max": {"type":"number", "minimum" : 0},
+							"density": {"type":"number", "minimum" : 1}
+						},					
+						"additionalProperties" : false						
+					}
+				}				
+			}		
+		},
+		"type" :{
+			"enum": ["playerStart", "cpuStart", "treasure", "junction"],
+			"additionalProperties" : false,
+			"type":"string"
+		},
+		"size":{
+			"type": "number",
+			"minimum": 1,
+			"additionalProperties" : false,
+		}
+		"connection":
+		{
+			required: ["a", "b", "guard"]
+			properties:{
+				"a":{
+					"type" : "string"
+				},
+				"b":{
+					"type" : "string"
+				},
+				"guard":
+				{
+					"type": "number",
+					"minimum" : 0
+				}
+			}
+		}
+	},
+
+	"properties":
+	{
+		"zones":{
+			"type": "object",
+			"additionalProperties":{"$ref" : "#/definitions/zone"	}						
+		},
+		"connections":{
+			"type": "array",
+			"items":{"$ref" : "#/definitions/connection"}	
+		},
+		"required" : ["zones", "connections"],
+		"additionalProperties" : false
+	}
+}

+ 1 - 0
lib/CModHandler.cpp

@@ -385,6 +385,7 @@ CContentHandler::CContentHandler()
 	handlers.insert(std::make_pair("objects", ContentTypeHandler(VLC->objtypeh, "object")));
 	handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
 	handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
+	handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template")));
 
 	//TODO: any other types of moddables?
 }

+ 2 - 0
lib/GameConstants.h

@@ -942,6 +942,8 @@ enum class ESpellSchool: ui8
 	EARTH 	= 3
 };
 
+ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID)
+
 // Typedef declarations
 typedef ui8 TFaction;
 typedef si64 TExpType;

+ 2 - 2
lib/VCMI_Lib.cpp

@@ -115,12 +115,12 @@ void LibClasses::init()
 
 	createHandler(terviewh, "Terrain view pattern", pomtime);
 
+	createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?)
+
 	logGlobal->infoStream()<<"\tInitializing handlers: "<< totalTime.getDiff();
 
 	modh->load();
 
-	createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?)
-
 	modh->afterLoad();
 
 	//FIXME: make sure that everything is ok after game restart

+ 82 - 26
lib/rmg/CMapGenOptions.cpp

@@ -20,7 +20,7 @@
 #include "../CTownHandler.h"
 
 CMapGenOptions::CMapGenOptions() : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(false),
-	playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(0), compOnlyTeamCount(RANDOM_SIZE),
+	playerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(RANDOM_SIZE), compOnlyTeamCount(RANDOM_SIZE), humanPlayersCount(0),
 	waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr)
 {
 	resetPlayersMap();
@@ -67,9 +67,14 @@ void CMapGenOptions::setPlayerCount(si8 value)
 {
 	assert((value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I) || value == RANDOM_SIZE);
 	playerCount = value;
-	auto possibleCompPlayersCount = PlayerColor::PLAYER_LIMIT_I-value;
-	if(compOnlyPlayerCount > possibleCompPlayersCount)
+
+	auto possibleCompPlayersCount = value;
+	if (compOnlyPlayerCount > possibleCompPlayersCount)
 		setCompOnlyPlayerCount(possibleCompPlayersCount);
+
+	if (getPlayerCount() != RANDOM_SIZE && getCompOnlyPlayerCount() != RANDOM_SIZE)
+		humanPlayersCount = getPlayerCount() - getCompOnlyPlayerCount();
+
 	resetPlayersMap();
 }
 
@@ -80,7 +85,7 @@ si8 CMapGenOptions::getTeamCount() const
 
 void CMapGenOptions::setTeamCount(si8 value)
 {
-	assert(playerCount == RANDOM_SIZE || (value >= 0 && value < playerCount) || value == RANDOM_SIZE);
+	assert(getPlayerCount() == RANDOM_SIZE || (value >= 0 && value < getPlayerCount()) || value == RANDOM_SIZE);
 	teamCount = value;
 }
 
@@ -91,8 +96,12 @@ si8 CMapGenOptions::getCompOnlyPlayerCount() const
 
 void CMapGenOptions::setCompOnlyPlayerCount(si8 value)
 {
-	assert(value == RANDOM_SIZE || (value >= 0 && value <= PlayerColor::PLAYER_LIMIT_I - playerCount));
+	assert(value == RANDOM_SIZE || (value >= 0 && value <= getPlayerCount()));
 	compOnlyPlayerCount = value;
+
+	if (getPlayerCount() != RANDOM_SIZE && getCompOnlyPlayerCount() != RANDOM_SIZE)
+		humanPlayersCount = getPlayerCount() - getCompOnlyPlayerCount();
+
 	resetPlayersMap();
 }
 
@@ -130,18 +139,19 @@ void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value
 void CMapGenOptions::resetPlayersMap()
 {
 	players.clear();
-	int realPlayersCnt = playerCount == RANDOM_SIZE ? static_cast<int>(PlayerColor::PLAYER_LIMIT_I) : playerCount;
-	int realCompOnlyPlayersCnt = compOnlyPlayerCount == RANDOM_SIZE ? (PlayerColor::PLAYER_LIMIT_I - realPlayersCnt) : compOnlyPlayerCount;
+	int realPlayersCnt = humanPlayersCount;
+	int realCompOnlyPlayersCnt = (compOnlyPlayerCount == RANDOM_SIZE) ? (PlayerColor::PLAYER_LIMIT_I - realPlayersCnt) : compOnlyPlayerCount;
 	int totalPlayersLimit = realPlayersCnt + realCompOnlyPlayersCnt;
-	if(playerCount == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE)
+	if (getPlayerCount() == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE)
 		totalPlayersLimit = static_cast<int>(PlayerColor::PLAYER_LIMIT_I);
 
+	//FIXME: what happens with human players here?
 	for(int color = 0; color < totalPlayersLimit; ++color)
 	{
 		CPlayerSettings player;
 		player.setColor(PlayerColor(color));
 		auto playerType = EPlayerType::AI;
-		if((playerCount != RANDOM_SIZE && color >= realPlayersCnt)
+		if ((getPlayerCount() != RANDOM_SIZE && color >= realPlayersCnt)
 		   || (compOnlyPlayerCount != RANDOM_SIZE && color >= (PlayerColor::PLAYER_LIMIT_I-compOnlyPlayerCount)))
 		{
 			playerType = EPlayerType::COMP_ONLY;
@@ -191,7 +201,7 @@ const std::map<std::string, CRmgTemplate *> & CMapGenOptions::getAvailableTempla
 void CMapGenOptions::finalize(CRandomGenerator & rand)
 {
 	logGlobal->infoStream() << boost::format ("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d")
-											% playerCount % teamCount % compOnlyPlayerCount % compOnlyTeamCount % waterContent % monsterStrength;
+											% (int)getPlayerCount() % (int)getTeamCount() % (int)getCompOnlyPlayerCount() % (int)getCompOnlyTeamCount() % (int)getWaterContent() % (int)getMonsterStrength();
 
 	if(!mapTemplate)
 	{
@@ -199,17 +209,18 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
 	}
 	assert(mapTemplate);
 
-	if(playerCount == RANDOM_SIZE)
+	if (getPlayerCount() == RANDOM_SIZE)
 	{
 		auto possiblePlayers = mapTemplate->getPlayers().getNumbers();
-		possiblePlayers.erase(possiblePlayers.begin(), possiblePlayers.lower_bound(countHumanPlayers()));
+		//ignore all non-randomized players, make sure these players will not be missing after roll
+		possiblePlayers.erase(possiblePlayers.begin(), possiblePlayers.lower_bound(countHumanPlayers() + countCompOnlyPlayers()));
 		assert(!possiblePlayers.empty());
-		playerCount = *RandomGeneratorUtil::nextItem(possiblePlayers, rand);
+		setPlayerCount (*RandomGeneratorUtil::nextItem(possiblePlayers, rand));
 		updatePlayers();
 	}
 	if(teamCount == RANDOM_SIZE)
 	{
-		teamCount = rand.nextInt(playerCount - 1);
+		teamCount = rand.nextInt(getPlayerCount() - 1);
 		if (teamCount == 1)
 			teamCount = 0;
 	}
@@ -236,9 +247,37 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
 	assert (vstd::iswithin(waterContent, EWaterContent::NONE, EWaterContent::ISLANDS));
 	assert (vstd::iswithin(monsterStrength, EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG));
 
+
 	//rectangular maps are the future of gaming
 	//setHeight(20);
 	//setWidth(50);
+
+	logGlobal->traceStream() << "Player config:";
+	int humanPlayers = 0, cpuOnlyPlayers = 0, AIplayers = 0;
+	for (auto player : players)
+	{
+		std::string playerType;
+		switch (player.second.getPlayerType())
+		{
+		case EPlayerType::AI:
+			playerType = "AI";
+			AIplayers++;
+			break;
+		case EPlayerType::COMP_ONLY:
+			playerType = "computer only";
+			cpuOnlyPlayers++;
+			break;
+		case EPlayerType::HUMAN:
+			playerType = "human only";
+			humanPlayers++;
+			break;
+			default:
+				assert(false);
+		}
+		logGlobal->traceStream() << boost::format("Player %d: %s") % player.second.getColor() % playerType;
+	}
+	setCompOnlyPlayerCount(cpuOnlyPlayers); //human players are set automaticlaly (?)
+	logGlobal->infoStream() << boost::format("Final player config: %d total, %d cpu-only") % players.size() % (int)getCompOnlyPlayerCount();
 }
 
 void CMapGenOptions::updatePlayers()
@@ -248,7 +287,7 @@ void CMapGenOptions::updatePlayers()
 	{
 		auto it = itrev;
 		--it;
-		if(players.size() == playerCount) break;
+		if (players.size() == getPlayerCount()) break;
 		if(it->second.getPlayerType() == EPlayerType::AI)
 		{
 			players.erase(it);
@@ -262,14 +301,12 @@ void CMapGenOptions::updatePlayers()
 
 void CMapGenOptions::updateCompOnlyPlayers()
 {
-	auto totalPlayersCnt = playerCount + compOnlyPlayerCount;
-
 	// Remove comp only players only from the end of the players map if necessary
 	for(auto itrev = players.end(); itrev != players.begin();)
 	{
 		auto it = itrev;
 		--it;
-		if(players.size() <= totalPlayersCnt) break;
+		if (players.size() <= getPlayerCount()) break;
 		if(it->second.getPlayerType() == EPlayerType::COMP_ONLY)
 		{
 			players.erase(it);
@@ -281,7 +318,13 @@ void CMapGenOptions::updateCompOnlyPlayers()
 	}
 
 	// Add some comp only players if necessary
-	auto compOnlyPlayersToAdd = totalPlayersCnt - players.size();
+	int compOnlyPlayersToAdd = getPlayerCount() - players.size();
+	
+	if (compOnlyPlayersToAdd < 0)
+	{
+		logGlobal->errorStream() << boost::format("Incorrect number of players to add. Requested players %d, current players %d") % playerCount % players.size();
+		assert (compOnlyPlayersToAdd < 0);
+	}
 	for(int i = 0; i < compOnlyPlayersToAdd; ++i)
 	{
 		CPlayerSettings pSettings;
@@ -299,6 +342,15 @@ int CMapGenOptions::countHumanPlayers() const
 	}));
 }
 
+int CMapGenOptions::countCompOnlyPlayers() const
+{
+	return static_cast<int>(boost::count_if(players, [](const std::pair<PlayerColor, CPlayerSettings> & pair)
+	{
+		return pair.second.getPlayerType() == EPlayerType::COMP_ONLY;
+	}));
+}
+
+
 PlayerColor CMapGenOptions::getNextPlayerColor() const
 {
 	for(PlayerColor i = PlayerColor(0); i < PlayerColor::PLAYER_LIMIT; i.advance(1))
@@ -308,7 +360,8 @@ PlayerColor CMapGenOptions::getNextPlayerColor() const
 			return i;
 		}
 	}
-	assert(0);
+	logGlobal->errorStream() << "Failed to get next player color";
+	assert(false);
 	return PlayerColor(0);
 }
 
@@ -338,33 +391,36 @@ const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand
 		if(tplSize >= tpl->getMinSize() && tplSize <= tpl->getMaxSize())
 		{
 			bool isPlayerCountValid = false;
-			if(playerCount != RANDOM_SIZE)
+			if (getPlayerCount() != RANDOM_SIZE)
 			{
-				if(tpl->getPlayers().isInRange(playerCount)) isPlayerCountValid = true;
+				if (tpl->getPlayers().isInRange(getPlayerCount()))
+					isPlayerCountValid = true;
 			}
 			else
 			{
 				// Human players shouldn't be banned when playing with random player count
 				auto playerNumbers = tpl->getPlayers().getNumbers();
-				if(playerNumbers.lower_bound(countHumanPlayers()) != playerNumbers.end())
+				if(countHumanPlayers() <= *boost::min_element(playerNumbers))
 				{
 					isPlayerCountValid = true;
 				}
 			}
 
-			if(isPlayerCountValid)
+			if (isPlayerCountValid)
 			{
 				bool isCpuPlayerCountValid = false;
 				if(compOnlyPlayerCount != RANDOM_SIZE)
 				{
-					if(tpl->getCpuPlayers().isInRange(compOnlyPlayerCount)) isCpuPlayerCountValid = true;
+					if (tpl->getCpuPlayers().isInRange(compOnlyPlayerCount))
+						isCpuPlayerCountValid = true;
 				}
 				else
 				{
 					isCpuPlayerCountValid = true;
 				}
 
-				if(isCpuPlayerCountValid) potentialTpls.push_back(tpl);
+				if(isCpuPlayerCountValid)
+					potentialTpls.push_back(tpl);
 			}
 		}
 	}

+ 4 - 3
lib/rmg/CMapGenOptions.h

@@ -103,7 +103,7 @@ public:
 	bool getHasTwoLevels() const;
 	void setHasTwoLevels(bool value);
 
-	/// The count of the players ranging from 1 to PlayerColor::PLAYER_LIMIT or RANDOM_SIZE for random. If you call
+	/// The count of all (human or computer) players ranging from 1 to PlayerColor::PLAYER_LIMIT or RANDOM_SIZE for random. If you call
 	/// this method, all player settings are reset to default settings.
 	si8 getPlayerCount() const;
 	void setPlayerCount(si8 value);
@@ -155,6 +155,7 @@ public:
 private:
 	void resetPlayersMap();
 	int countHumanPlayers() const;
+	int countCompOnlyPlayers() const;
 	PlayerColor getNextPlayerColor() const;
 	void updateCompOnlyPlayers();
 	void updatePlayers();
@@ -162,7 +163,7 @@ private:
 
 	si32 width, height;
 	bool hasTwoLevels;
-	si8 playerCount, teamCount, compOnlyPlayerCount, compOnlyTeamCount;
+	si8 playerCount, teamCount, humanPlayersCount, compOnlyPlayerCount, compOnlyTeamCount;
 	EWaterContent::EWaterContent waterContent;
 	EMonsterStrength::EMonsterStrength monsterStrength;
 	std::map<PlayerColor, CPlayerSettings> players;
@@ -173,7 +174,7 @@ public:
 	void serialize(Handler & h, const int version)
 	{
 		h & width & height & hasTwoLevels & playerCount & teamCount & compOnlyPlayerCount;
-		h & compOnlyTeamCount & waterContent & monsterStrength & players;
+		h & compOnlyTeamCount & waterContent & monsterStrength & players & humanPlayersCount;
 		//TODO add name of template to class, enables selection of a template by a user
 	}
 };

+ 29 - 10
lib/rmg/CMapGenerator.cpp

@@ -144,7 +144,7 @@ std::string CMapGenerator::getMapDescription() const
 
     std::stringstream ss;
     ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, Random seed was %d, size %dx%d") +
-        ", levels %s, humans %d, computers %d, water %s, monster %s, VCMI map") % mapGenOptions->getMapTemplate()->getName() %
+        ", levels %s, players %d, computers %d, water %s, monster %s, VCMI map") % mapGenOptions->getMapTemplate()->getName() %
 		randomSeed % map->width % map->height % (map->twoLevel ? "2" : "1") % static_cast<int>(mapGenOptions->getPlayerCount()) %
 		static_cast<int>(mapGenOptions->getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions->getWaterContent()] %
 		monsterStrengthStr[monsterStrengthIndex]);
@@ -169,19 +169,32 @@ std::string CMapGenerator::getMapDescription() const
 void CMapGenerator::addPlayerInfo()
 {
 	// Calculate which team numbers exist
-	std::array<std::list<int>, 2> teamNumbers; // 0= cpu/human, 1= cpu only
+
+	enum ETeams {CPHUMAN = 0, CPUONLY = 1, AFTER_LAST = 2};
+	std::array<std::list<int>, 2> teamNumbers;
+
 	int teamOffset = 0;
-	for(int i = 0; i < 2; ++i)
-	{
-		int playerCount = i == 0 ? mapGenOptions->getPlayerCount() : mapGenOptions->getCompOnlyPlayerCount();
-		int teamCount = i == 0 ? mapGenOptions->getTeamCount() : mapGenOptions->getCompOnlyTeamCount();
+	int playerCount = 0;
+	int teamCount = 0;
 
+	for (int i = CPHUMAN; i < AFTER_LAST; ++i)
+	{
+		if (i == CPHUMAN)
+		{
+			playerCount = mapGenOptions->getPlayerCount();
+			teamCount = mapGenOptions->getTeamCount();
+		}
+		else
+		{
+			playerCount = mapGenOptions->getCompOnlyPlayerCount();
+			teamCount = mapGenOptions->getCompOnlyTeamCount();
+		}
+		
 		if(playerCount == 0)
 		{
 			continue;
 		}
-		int playersPerTeam = playerCount /
-				(teamCount == 0 ? playerCount : teamCount);
+		int playersPerTeam = playerCount / (teamCount == 0 ? playerCount : teamCount);
 		int teamCountNorm = teamCount;
 		if(teamCountNorm == 0)
 		{
@@ -202,17 +215,23 @@ void CMapGenerator::addPlayerInfo()
 	}
 
 	// Team numbers are assigned randomly to every player
+	//TODO: allow customize teams in rmg template
 	for(const auto & pair : mapGenOptions->getPlayersSettings())
 	{
 		const auto & pSettings = pair.second;
 		PlayerInfo player;
 		player.canComputerPlay = true;
-		int j = pSettings.getPlayerType() == EPlayerType::COMP_ONLY ? 1 : 0;
-		if(j == 0)
+		int j = (pSettings.getPlayerType() == EPlayerType::COMP_ONLY) ? CPUONLY : CPHUMAN;
+		if (j == CPHUMAN)
 		{
 			player.canHumanPlay = true;
 		}
 
+		if (teamNumbers[j].empty())
+		{
+			logGlobal->errorStream() << boost::format("Not enough places in team for %s player") % ((j == CPUONLY) ? "CPU" : "CPU or human");
+			assert (teamNumbers[j].size());
+		}
         auto itTeam = RandomGeneratorUtil::nextItem(teamNumbers[j], rand);
 		player.team = TeamID(*itTeam);
 		teamNumbers[j].erase(itTeam);

+ 178 - 173
lib/rmg/CRmgTemplateStorage.cpp

@@ -23,214 +23,216 @@
 #include "../GameConstants.h"
 #include "../StringConstants.h"
 
-const std::map<std::string, CRmgTemplate *> & CRmgTemplateLoader::getTemplates() const
+const std::map<std::string, CRmgTemplate *> & CRmgTemplateStorage::getTemplates() const
 {
 	return templates;
 }
 
-void CJsonRmgTemplateLoader::loadTemplates()
+void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
+{
+	//unused
+	loadObject(scope, name, data);
+}
+
+void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const JsonNode & data)
 {
-	const JsonNode rootNode(ResourceID("config/rmg.json"));
-	for(const auto & templatePair : rootNode.Struct())
+	auto tpl = new CRmgTemplate();
+	try
 	{
-		auto tpl = new CRmgTemplate();
-		try
+		tpl->setName(name); //TODO?
+		const auto & templateNode = data;
+
+		// Parse main template data
+		tpl->setMinSize(parseMapTemplateSize(templateNode["minSize"].String()));
+		tpl->setMaxSize(parseMapTemplateSize(templateNode["maxSize"].String()));
+		tpl->setPlayers(parsePlayers(templateNode["players"].String()));
+		tpl->setCpuPlayers(parsePlayers(templateNode["cpu"].String()));
+
+		// Parse zones
+		std::map<TRmgTemplateZoneId, CRmgTemplateZone *> zones;
+		for (const auto & zonePair : templateNode["zones"].Struct())
 		{
-			tpl->setName(templatePair.first);
-			const auto & templateNode = templatePair.second;
-
-			// Parse main template data
-			tpl->setMinSize(parseMapTemplateSize(templateNode["minSize"].String()));
-			tpl->setMaxSize(parseMapTemplateSize(templateNode["maxSize"].String()));
-			tpl->setPlayers(parsePlayers(templateNode["players"].String()));
-			tpl->setCpuPlayers(parsePlayers(templateNode["cpu"].String()));
-
-			// Parse zones
-			std::map<TRmgTemplateZoneId, CRmgTemplateZone *> zones;
-			for (const auto & zonePair : templateNode["zones"].Struct())
-			{
-				auto zone = new CRmgTemplateZone();
-				auto zoneId = boost::lexical_cast<TRmgTemplateZoneId>(zonePair.first);
-				zone->setId(zoneId);
+			auto zone = new CRmgTemplateZone();
+			auto zoneId = boost::lexical_cast<TRmgTemplateZoneId>(zonePair.first);
+			zone->setId(zoneId);
 
-				const auto & zoneNode = zonePair.second;
-				zone->setType(parseZoneType(zoneNode["type"].String()));
-				zone->setSize(zoneNode["size"].Float());
-				if (!zoneNode["owner"].isNull()) zone->setOwner(zoneNode["owner"].Float());
+			const auto & zoneNode = zonePair.second;
+			zone->setType(parseZoneType(zoneNode["type"].String()));
+			zone->setSize(zoneNode["size"].Float());
+			if (!zoneNode["owner"].isNull()) zone->setOwner(zoneNode["owner"].Float());
 
-				zone->setPlayerTowns(parseTemplateZoneTowns(zoneNode["playerTowns"]));
-				zone->setNeutralTowns(parseTemplateZoneTowns(zoneNode["neutralTowns"]));
-				if (!zoneNode["matchTerrainToTown"].isNull()) //default : true
-					zone->setMatchTerrainToTown(zoneNode["matchTerrainToTown"].Bool());
-				zone->setTerrainTypes(parseTerrainTypes(zoneNode["terrainTypes"].Vector(), zone->getDefaultTerrainTypes()));
+			zone->setPlayerTowns(parseTemplateZoneTowns(zoneNode["playerTowns"]));
+			zone->setNeutralTowns(parseTemplateZoneTowns(zoneNode["neutralTowns"]));
+			if (!zoneNode["matchTerrainToTown"].isNull()) //default : true
+				zone->setMatchTerrainToTown(zoneNode["matchTerrainToTown"].Bool());
+			zone->setTerrainTypes(parseTerrainTypes(zoneNode["terrainTypes"].Vector(), zone->getDefaultTerrainTypes()));
 
-				if (!zoneNode["townsAreSameType"].isNull()) //default : false
-					zone->setTownsAreSameType((zoneNode["townsAreSameType"].Bool()));
+			if (!zoneNode["townsAreSameType"].isNull()) //default : false
+				zone->setTownsAreSameType((zoneNode["townsAreSameType"].Bool()));
 
-				for (int i = 0; i < 2; ++i)
+			for (int i = 0; i < 2; ++i)
+			{
+				std::set<TFaction> allowedTownTypes;
+				if (i)
 				{
-					std::set<TFaction> allowedTownTypes;
-					if (i)
-					{
-						if (zoneNode["allowedTowns"].isNull())
-							allowedTownTypes = zone->getDefaultTownTypes();
-					}
-					else
-					{
-						if (zoneNode["allowedMonsters"].isNull())
-							allowedTownTypes = VLC->townh->getAllowedFactions(false);
-					}
-
-					if (allowedTownTypes.empty())
-					{
-						for (const JsonNode & allowedTown : zoneNode[i ? "allowedTowns" : "allowedMonsters"].Vector())
-						{
-							//complain if the town type is not present in our game
-							if (auto id = VLC->modh->identifiers.getIdentifier("faction", allowedTown, false))
-								allowedTownTypes.insert(id.get());
-						}
-					}
-
-					if (!zoneNode[i ? "bannedTowns" : "bannedMonsters"].isNull())
-					{
-						for (const JsonNode & bannedTown : zoneNode[i ? "bannedTowns" : "bannedMonsters"].Vector())
-						{
-							//erase unindentified towns silently
-							if (auto id = VLC->modh->identifiers.getIdentifier("faction", bannedTown, true))
-								vstd::erase_if_present(allowedTownTypes, id.get());
-						}
-					}
-					if (i)
-						zone->setTownTypes(allowedTownTypes);
-					else
-						zone->setMonsterTypes(allowedTownTypes);
+					if (zoneNode["allowedTowns"].isNull())
+						allowedTownTypes = zone->getDefaultTownTypes();
 				}
-
-				const std::string monsterStrength = zoneNode["monsters"].String();
-				if (monsterStrength == "weak")
-					zone->setMonsterStrength(EMonsterStrength::ZONE_WEAK);
-				else if (monsterStrength == "normal")
-					zone->setMonsterStrength(EMonsterStrength::ZONE_NORMAL);
-				else if (monsterStrength == "strong")
-					zone->setMonsterStrength(EMonsterStrength::ZONE_STRONG);
 				else
-					throw (rmgException("incorrect monster power"));
-
-				if (!zoneNode["mines"].isNull())
 				{
-					auto mines = zoneNode["mines"].Struct();
-					//FIXME: maybe there is a smarter way to parse it already?
-					zone->setMinesAmount(Res::WOOD, mines["wood"].Float());
-					zone->setMinesAmount(Res::ORE, mines["ore"].Float());
-					zone->setMinesAmount(Res::GEMS, mines["gems"].Float());
-					zone->setMinesAmount(Res::CRYSTAL, mines["crystal"].Float());
-					zone->setMinesAmount(Res::SULFUR, mines["sulfur"].Float());
-					zone->setMinesAmount(Res::MERCURY, mines["mercury"].Float());
-					zone->setMinesAmount(Res::GOLD, mines["gold"].Float());
-					//TODO: Mithril
+					if (zoneNode["allowedMonsters"].isNull())
+						allowedTownTypes = VLC->townh->getAllowedFactions(false);
 				}
 
-				//treasures
-				if (!zoneNode["treasure"].isNull())
+				if (allowedTownTypes.empty())
 				{
-					//TODO: parse vector of different treasure settings
-					if (zoneNode["treasure"].getType() == JsonNode::DATA_STRUCT)
+					for (const JsonNode & allowedTown : zoneNode[i ? "allowedTowns" : "allowedMonsters"].Vector())
 					{
-						auto treasureInfo = zoneNode["treasure"].Struct();
-						{
-							CTreasureInfo ti;
-							ti.min = treasureInfo["min"].Float();
-							ti.max = treasureInfo["max"].Float();
-							ti.density = treasureInfo["density"].Float(); //TODO: use me
-							zone->addTreasureInfo(ti);
-						}
+						//complain if the town type is not present in our game
+						if (auto id = VLC->modh->identifiers.getIdentifier("faction", allowedTown, false))
+							allowedTownTypes.insert(id.get());
 					}
-					else if (zoneNode["treasure"].getType() == JsonNode::DATA_VECTOR)
+				}
+
+				if (!zoneNode[i ? "bannedTowns" : "bannedMonsters"].isNull())
+				{
+					for (const JsonNode & bannedTown : zoneNode[i ? "bannedTowns" : "bannedMonsters"].Vector())
 					{
-						for (auto treasureInfo : zoneNode["treasure"].Vector())
-						{
-							CTreasureInfo ti;
-							ti.min = treasureInfo["min"].Float();
-							ti.max = treasureInfo["max"].Float();
-							ti.density = treasureInfo["density"].Float();
-							zone->addTreasureInfo(ti);
-						}
+						//erase unindentified towns silently
+						if (auto id = VLC->modh->identifiers.getIdentifier("faction", bannedTown, true))
+							vstd::erase_if_present(allowedTownTypes, id.get());
 					}
 				}
-
-				zones[zone->getId()] = zone;
+				if (i)
+					zone->setTownTypes(allowedTownTypes);
+				else
+					zone->setMonsterTypes(allowedTownTypes);
 			}
 
-			//copy settings from already parsed zones
-			for (const auto & zonePair : templateNode["zones"].Struct())
+			const std::string monsterStrength = zoneNode["monsters"].String();
+			if (monsterStrength == "weak")
+				zone->setMonsterStrength(EMonsterStrength::ZONE_WEAK);
+			else if (monsterStrength == "normal")
+				zone->setMonsterStrength(EMonsterStrength::ZONE_NORMAL);
+			else if (monsterStrength == "strong")
+				zone->setMonsterStrength(EMonsterStrength::ZONE_STRONG);
+			else
+				throw (rmgException("incorrect monster power"));
+
+			if (!zoneNode["mines"].isNull())
 			{
-				auto zoneId = boost::lexical_cast<TRmgTemplateZoneId>(zonePair.first);
-				auto zone = zones[zoneId];
-
-				const auto & zoneNode = zonePair.second;
-
-				if (!zoneNode["terrainTypeLikeZone"].isNull())
-				{
-					int id = zoneNode["terrainTypeLikeZone"].Float();
-					zone->setTerrainTypes(zones[id]->getTerrainTypes());
-					zone->setMatchTerrainToTown(zones[id]->getMatchTerrainToTown());
-				}
-
-				if (!zoneNode["townTypeLikeZone"].isNull())
-					zone->setTownTypes (zones[zoneNode["townTypeLikeZone"].Float()]->getTownTypes());
+				auto mines = zoneNode["mines"].Struct();
+				//FIXME: maybe there is a smarter way to parse it already?
+				zone->setMinesAmount(Res::WOOD, mines["wood"].Float());
+				zone->setMinesAmount(Res::ORE, mines["ore"].Float());
+				zone->setMinesAmount(Res::GEMS, mines["gems"].Float());
+				zone->setMinesAmount(Res::CRYSTAL, mines["crystal"].Float());
+				zone->setMinesAmount(Res::SULFUR, mines["sulfur"].Float());
+				zone->setMinesAmount(Res::MERCURY, mines["mercury"].Float());
+				zone->setMinesAmount(Res::GOLD, mines["gold"].Float());
+				//TODO: Mithril
+			}
 
-				if (!zoneNode["treasureLikeZone"].isNull())
+			//treasures
+			if (!zoneNode["treasure"].isNull())
+			{
+				//TODO: parse vector of different treasure settings
+				if (zoneNode["treasure"].getType() == JsonNode::DATA_STRUCT)
 				{
-					for (auto treasureInfo : zones[zoneNode["treasureLikeZone"].Float()]->getTreasureInfo())
+					auto treasureInfo = zoneNode["treasure"].Struct();
 					{
-						zone->addTreasureInfo(treasureInfo);
+						CTreasureInfo ti;
+						ti.min = treasureInfo["min"].Float();
+						ti.max = treasureInfo["max"].Float();
+						ti.density = treasureInfo["density"].Float(); //TODO: use me
+						zone->addTreasureInfo(ti);
 					}
 				}
-
-				if (!zoneNode["minesLikeZone"].isNull())
+				else if (zoneNode["treasure"].getType() == JsonNode::DATA_VECTOR)
 				{
-					for (auto mineInfo : zones[zoneNode["minesLikeZone"].Float()]->getMinesInfo())
+					for (auto treasureInfo : zoneNode["treasure"].Vector())
 					{
-						zone->setMinesAmount (mineInfo.first, mineInfo.second);
+						CTreasureInfo ti;
+						ti.min = treasureInfo["min"].Float();
+						ti.max = treasureInfo["max"].Float();
+						ti.density = treasureInfo["density"].Float();
+						zone->addTreasureInfo(ti);
 					}
-					
 				}
 			}
 
-			tpl->setZones(zones);
+			zones[zone->getId()] = zone;
+		}
+
+		//copy settings from already parsed zones
+		for (const auto & zonePair : templateNode["zones"].Struct())
+		{
+			auto zoneId = boost::lexical_cast<TRmgTemplateZoneId>(zonePair.first);
+			auto zone = zones[zoneId];
+
+			const auto & zoneNode = zonePair.second;
+
+			if (!zoneNode["terrainTypeLikeZone"].isNull())
+			{
+				int id = zoneNode["terrainTypeLikeZone"].Float();
+				zone->setTerrainTypes(zones[id]->getTerrainTypes());
+				zone->setMatchTerrainToTown(zones[id]->getMatchTerrainToTown());
+			}
+
+			if (!zoneNode["townTypeLikeZone"].isNull())
+				zone->setTownTypes (zones[zoneNode["townTypeLikeZone"].Float()]->getTownTypes());
 
-			// Parse connections
-			std::list<CRmgTemplateZoneConnection> connections;
-			for(const auto & connPair : templateNode["connections"].Vector())
+			if (!zoneNode["treasureLikeZone"].isNull())
 			{
-				CRmgTemplateZoneConnection conn;
-				conn.setZoneA(zones.find(boost::lexical_cast<TRmgTemplateZoneId>(connPair["a"].String()))->second);
-				conn.setZoneB(zones.find(boost::lexical_cast<TRmgTemplateZoneId>(connPair["b"].String()))->second);
-				conn.setGuardStrength(connPair["guard"].Float());
-				connections.push_back(conn);
+				for (auto treasureInfo : zones[zoneNode["treasureLikeZone"].Float()]->getTreasureInfo())
+				{
+					zone->addTreasureInfo(treasureInfo);
+				}
 			}
-			tpl->setConnections(connections);
+
+			if (!zoneNode["minesLikeZone"].isNull())
 			{
-				auto zones = tpl->getZones();
-				for (auto con : tpl->getConnections())
+				for (auto mineInfo : zones[zoneNode["minesLikeZone"].Float()]->getMinesInfo())
 				{
-					auto idA = con.getZoneA()->getId();
-					auto idB = con.getZoneB()->getId();
-					zones[idA]->addConnection(idB);
-					zones[idB]->addConnection(idA);
+					zone->setMinesAmount (mineInfo.first, mineInfo.second);
 				}
+					
 			}
-			tpl->validate();
-			templates[tpl->getName()] = tpl;
 		}
-		catch(const std::exception & e)
+
+		tpl->setZones(zones);
+
+		// Parse connections
+		std::list<CRmgTemplateZoneConnection> connections;
+		for(const auto & connPair : templateNode["connections"].Vector())
 		{
-			logGlobal->errorStream() << boost::format("Template %s has errors. Message: %s.") % tpl->getName() % std::string(e.what());
+			CRmgTemplateZoneConnection conn;
+			conn.setZoneA(zones.find(boost::lexical_cast<TRmgTemplateZoneId>(connPair["a"].String()))->second);
+			conn.setZoneB(zones.find(boost::lexical_cast<TRmgTemplateZoneId>(connPair["b"].String()))->second);
+			conn.setGuardStrength(connPair["guard"].Float());
+			connections.push_back(conn);
 		}
+		tpl->setConnections(connections);
+		{
+			auto zones = tpl->getZones();
+			for (auto con : tpl->getConnections())
+			{
+				auto idA = con.getZoneA()->getId();
+				auto idB = con.getZoneB()->getId();
+				zones[idA]->addConnection(idB);
+				zones[idB]->addConnection(idA);
+			}
+		}
+		tpl->validate();
+		templates[tpl->getName()] = tpl;
+	}
+	catch(const std::exception & e)
+	{
+		logGlobal->errorStream() << boost::format("Template %s has errors. Message: %s.") % tpl->getName() % std::string(e.what());
 	}
 }
 
-CRmgTemplate::CSize CJsonRmgTemplateLoader::parseMapTemplateSize(const std::string & text) const
+CRmgTemplate::CSize CRmgTemplateStorage::parseMapTemplateSize(const std::string & text) const
 {
 	CRmgTemplate::CSize size;
 	if(text.empty()) return size;
@@ -265,7 +267,7 @@ CRmgTemplate::CSize CJsonRmgTemplateLoader::parseMapTemplateSize(const std::stri
 	return size;
 }
 
-ETemplateZoneType::ETemplateZoneType CJsonRmgTemplateLoader::parseZoneType(const std::string & type) const
+ETemplateZoneType::ETemplateZoneType CRmgTemplateStorage::parseZoneType(const std::string & type) const
 {
 	static const std::map<std::string, ETemplateZoneType::ETemplateZoneType> zoneTypeMapping = 
 	{
@@ -279,7 +281,7 @@ ETemplateZoneType::ETemplateZoneType CJsonRmgTemplateLoader::parseZoneType(const
 	return it->second;
 }
 
-CRmgTemplateZone::CTownInfo CJsonRmgTemplateLoader::parseTemplateZoneTowns(const JsonNode & node) const
+CRmgTemplateZone::CTownInfo CRmgTemplateStorage::parseTemplateZoneTowns(const JsonNode & node) const
 {
 	CRmgTemplateZone::CTownInfo towns;
 	towns.setTownCount(node["towns"].Float());
@@ -289,7 +291,7 @@ CRmgTemplateZone::CTownInfo CJsonRmgTemplateLoader::parseTemplateZoneTowns(const
 	return towns;
 }
 
-std::set<TFaction> CJsonRmgTemplateLoader::parseTownTypes(const JsonVector & townTypesVector, const std::set<TFaction> & defaultTownTypes) const
+std::set<TFaction> CRmgTemplateStorage::parseTownTypes(const JsonVector & townTypesVector, const std::set<TFaction> & defaultTownTypes) const
 {
 	std::set<TFaction> townTypes;
 	for(const auto & townTypeNode : townTypesVector)
@@ -311,7 +313,7 @@ std::set<TFaction> CJsonRmgTemplateLoader::parseTownTypes(const JsonVector & tow
 	return townTypes;
 }
 
-std::set<ETerrainType> CJsonRmgTemplateLoader::parseTerrainTypes(const JsonVector & terTypeStrings, const std::set<ETerrainType> & defaultTerrainTypes) const
+std::set<ETerrainType> CRmgTemplateStorage::parseTerrainTypes(const JsonVector & terTypeStrings, const std::set<ETerrainType> & defaultTerrainTypes) const
 {
 	std::set<ETerrainType> terTypes;
 	if (terTypeStrings.empty()) //nothing was specified
@@ -334,7 +336,7 @@ std::set<ETerrainType> CJsonRmgTemplateLoader::parseTerrainTypes(const JsonVecto
 	return terTypes;
 }
 
-CRmgTemplate::CPlayerCountRange CJsonRmgTemplateLoader::parsePlayers(const std::string & players) const
+CRmgTemplate::CPlayerCountRange CRmgTemplateStorage::parsePlayers(const std::string & players) const
 {
 	CRmgTemplate::CPlayerCountRange playerRange;
 	if(players.empty())
@@ -363,21 +365,24 @@ CRmgTemplate::CPlayerCountRange CJsonRmgTemplateLoader::parsePlayers(const std::
 	return playerRange;
 }
 
-const std::map<std::string, CRmgTemplate *> & CRmgTemplateStorage::getTemplates() const
-{
-	return templates;
-}
-
 CRmgTemplateStorage::CRmgTemplateStorage()
 {
-	auto jsonLoader = make_unique<CJsonRmgTemplateLoader>();
-	jsonLoader->loadTemplates();
-
-	const auto & tpls = jsonLoader->getTemplates();
-	templates.insert(tpls.begin(), tpls.end());
+	//TODO: load all
 }
 
 CRmgTemplateStorage::~CRmgTemplateStorage()
 {
 	for (auto & pair : templates) delete pair.second;
 }
+
+std::vector<bool> CRmgTemplateStorage::getDefaultAllowed() const
+{
+	//all templates are allowed
+	return std::vector<bool>();
+}
+
+std::vector<JsonNode> CRmgTemplateStorage::loadLegacyData(size_t dataSize)
+{
+	return std::vector<JsonNode>();
+	//it would be cool to load old rmg.txt files
+};

+ 14 - 24
lib/rmg/CRmgTemplateStorage.h

@@ -13,28 +13,27 @@
 
 #include "CRmgTemplate.h"
 #include "CRmgTemplateZone.h"
+#include "../IHandlerBase.h"
 
 class JsonNode;
 
 typedef std::vector<JsonNode> JsonVector;
 
-/// The CRmgTemplateLoader is a abstract base class for loading templates.
-class DLL_LINKAGE CRmgTemplateLoader
+/// The CJsonRmgTemplateLoader loads templates from a JSON file.
+class DLL_LINKAGE CRmgTemplateStorage : public IHandlerBase
 {
 public:
-	virtual ~CRmgTemplateLoader() { };
-	virtual void loadTemplates() = 0;
+	CRmgTemplateStorage();
+	~CRmgTemplateStorage();
+
 	const std::map<std::string, CRmgTemplate *> & getTemplates() const;
 
-protected:
-	std::map<std::string, CRmgTemplate *> templates;
-};
+	std::vector<bool> getDefaultAllowed() const;
+	std::vector<JsonNode> loadLegacyData(size_t dataSize);
 
-/// The CJsonRmgTemplateLoader loads templates from a JSON file.
-class DLL_LINKAGE CJsonRmgTemplateLoader : public CRmgTemplateLoader
-{
-public:
-	void loadTemplates() override;
+	/// loads single object into game. Scope is namespace of this object, same as name of source mod
+	virtual void loadObject(std::string scope, std::string name, const JsonNode & data);
+	virtual void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index);
 
 private:
 	CRmgTemplate::CSize parseMapTemplateSize(const std::string & text) const;
@@ -43,17 +42,8 @@ private:
 	std::set<TFaction> parseTownTypes(const JsonVector & townTypesVector, const std::set<TFaction> & defaultTownTypes) const;
 	std::set<ETerrainType> parseTerrainTypes(const JsonVector & terTypeStrings, const std::set<ETerrainType> & defaultTerrainTypes) const;
 	CRmgTemplate::CPlayerCountRange parsePlayers(const std::string & players) const;
-};
 
-/// The class CRmgTemplateStorage stores random map templates.
-class DLL_LINKAGE CRmgTemplateStorage
-{
-public:
-	CRmgTemplateStorage();
-	~CRmgTemplateStorage();
-
-	const std::map<std::string, CRmgTemplate *> & getTemplates() const;
-
-private:
-	std::map<std::string, CRmgTemplate *> templates; /// Key: Template name
+protected:
+	std::map<std::string, CRmgTemplate *> templates;
 };
+

+ 47 - 42
lib/rmg/CRmgTemplateZone.cpp

@@ -1191,70 +1191,66 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
 		logGlobal->infoStream() << "Preparing playing zone";
 		int player_id = *owner - 1;
 		auto & playerInfo = gen->map->players[player_id];
+		PlayerColor player(player_id);
 		if (playerInfo.canAnyonePlay())
 		{
-			PlayerColor player(player_id);
+			player = PlayerColor(player_id);
 			townType = gen->mapGenOptions->getPlayersSettings().find(player)->second.getStartingTown();
 
 			if (townType == CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
-			{
-				if (townTypes.size())
-					townType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
-				else
-					townType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
-			}
-			
-			auto  town = new CGTownInstance();
-			town->ID = Obj::TOWN;
+				randomizeTownType(gen);
+		}
+		else //no player - randomize town
+		{
+			player = PlayerColor::NEUTRAL;
+			randomizeTownType(gen);
+		}
 
-			town->subID = townType;
-			town->tempOwner = player;
-			town->builtBuildings.insert(BuildingID::FORT);
-			town->builtBuildings.insert(BuildingID::DEFAULT);
+		auto  town = new CGTownInstance();
+		town->ID = Obj::TOWN;
 
-			for (auto spell : VLC->spellh->objects) //add all regular spells to town
-			{
-				if (!spell->isSpecialSpell() && !spell->isCreatureAbility())
-					town->possibleSpells.push_back(spell->id);
-			}
-			//towns are big objects and should be centered around visitable position
-			placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0); //generate no guards, but free path to entrance
-			cutPathAroundTown(town);
+		town->subID = townType;
+		town->tempOwner = player;
+		town->builtBuildings.insert(BuildingID::FORT);
+		town->builtBuildings.insert(BuildingID::DEFAULT);
 
-			totalTowns++;
-			//register MAIN town of zone only
-			gen->registerZone (town->subID);
+		for (auto spell : VLC->spellh->objects) //add all regular spells to town
+		{
+			if (!spell->isSpecialSpell() && !spell->isCreatureAbility())
+				town->possibleSpells.push_back(spell->id);
+		}
+		//towns are big objects and should be centered around visitable position
+		placeAndGuardObject(gen, town, getPos() + town->getVisitableOffset(), 0); //generate no guards, but free path to entrance
+		cutPathAroundTown(town);
+
+		totalTowns++;
+		//register MAIN town of zone only
+		gen->registerZone (town->subID);
 
+		if (playerInfo.canAnyonePlay()) //configure info for owning player
+		{
 			logGlobal->traceStream() << "Fill player info " << player_id;
 
 			// Update player info
 			playerInfo.allowedFactions.clear();
-			playerInfo.allowedFactions.insert (townType);
+			playerInfo.allowedFactions.insert(townType);
 			playerInfo.hasMainTown = true;
 			playerInfo.posOfMainTown = town->pos - town->getVisitableOffset();
 			playerInfo.generateHeroAtMainTown = true;
 
 			//now create actual towns
-			addNewTowns (playerTowns.getCastleCount() - 1, true, player);
-			addNewTowns (playerTowns.getTownCount(), false, player);
-
-			//requiredObjects.push_back(town);
+			addNewTowns(playerTowns.getCastleCount() - 1, true, player);
+			addNewTowns(playerTowns.getTownCount(), false, player);
 		}
 		else
-		{			
-			type = ETemplateZoneType::TREASURE;
-			if (townTypes.size())
-				townType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
-			else
-				townType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
+		{
+			addNewTowns(playerTowns.getCastleCount() - 1, true, PlayerColor::NEUTRAL);
+			addNewTowns(playerTowns.getTownCount(), false, PlayerColor::NEUTRAL);
 		}
 	}
-	else //no player
+	else //randomize town types for any other zones as well
 	{
-		if (townTypes.size())
-			townType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
-		else
-			townType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed
+		randomizeTownType(gen);
 	}
 
 	addNewTowns (neutralTowns.getCastleCount(), true, PlayerColor::NEUTRAL);
@@ -1273,11 +1269,20 @@ void CRmgTemplateZone::initTownType (CMapGenerator* gen)
 				townType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
 			else if (monsterTypes.size())
 				townType = *RandomGeneratorUtil::nextItem(monsterTypes, gen->rand); //this happens in Clash of Dragons in treasure zones, where all towns are banned
+			else //just in any case
+				randomizeTownType(gen);
 		}
-
 	}
 }
 
+void CRmgTemplateZone::randomizeTownType (CMapGenerator* gen)
+{
+	if (townTypes.size())
+		townType = *RandomGeneratorUtil::nextItem(townTypes, gen->rand);
+	else
+		townType = *RandomGeneratorUtil::nextItem(getDefaultTownTypes(), gen->rand); //it is possible to have zone with no towns allowed, we still need some
+}
+
 void CRmgTemplateZone::initTerrainType (CMapGenerator* gen)
 {
 

+ 1 - 0
lib/rmg/CRmgTemplateZone.h

@@ -162,6 +162,7 @@ public:
 	bool placeMines (CMapGenerator* gen);
 	void initTownType (CMapGenerator* gen);
 	void paintZoneTerrain (CMapGenerator* gen, ETerrainType terrainType);
+	void randomizeTownType(CMapGenerator* gen); //helper function
 	void initTerrainType (CMapGenerator* gen);
 	void createBorder(CMapGenerator* gen);
 	void fractalize(CMapGenerator* gen);