Browse Source

Vlc obstacles [part 1] (#888)

* obstacles content handler, entity service and VLC integration
Nordsoft91 3 years ago
parent
commit
ad01c7ffce

+ 6 - 0
client/CGameInfo.cpp

@@ -38,6 +38,7 @@ void CGameInfo::setFromLib()
 	skillh = VLC->skillh;
 	objtypeh = VLC->objtypeh;
 	battleFieldHandler = VLC->battlefieldsHandler;
+	obstacleHandler = VLC->obstacleHandler;
 }
 
 const ArtifactService * CGameInfo::artifacts() const
@@ -85,6 +86,11 @@ const SkillService * CGameInfo::skills() const
 	return globalServices->skills();
 }
 
+const ObstacleService * CGameInfo::obstacles() const
+{
+	return globalServices->obstacles();
+}
+
 void CGameInfo::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
 {
 	logGlobal->error("CGameInfo::updateEntity call is not expected.");

+ 3 - 0
client/CGameInfo.h

@@ -32,6 +32,7 @@ class CGameState;
 class IMainVideoPlayer;
 class CServerHandler;
 class BattleFieldHandler;
+class ObstacleHandler;
 
 class CMap;
 
@@ -62,6 +63,7 @@ public:
 	const spells::Service * spells() const override;
 	const SkillService * skills() const override;
 	const BattleFieldService * battlefields() const override;
+	const ObstacleService * obstacles() const override;
 
 	void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
 
@@ -77,6 +79,7 @@ public:
 	ConstTransitivePtr<CSkillHandler> skillh;
 	ConstTransitivePtr<CObjectHandler> objh;
 	ConstTransitivePtr<CObjectClassesHandler> objtypeh;
+	ConstTransitivePtr<ObstacleHandler> obstacleHandler;
 	CGeneralTextHandler * generaltexth;
 	CMapHandler * mh;
 	CTownHandler * townh;

+ 3 - 2
client/battle/CBattleInterface.cpp

@@ -42,6 +42,7 @@
 #include "../../lib/spells/Problem.h"
 #include "../../lib/CTownHandler.h"
 #include "../../lib/BattleFieldHandler.h"
+#include "../../lib/ObstacleHandler.h"
 #include "../../lib/CGameState.h"
 #include "../../lib/mapping/CMap.h"
 #include "../../lib/NetPacks.h"
@@ -361,7 +362,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
 	{
 		if(elem->obstacleType == CObstacleInstance::USUAL)
 		{
-			std::string animationName = elem->getInfo().defName;
+			std::string animationName = elem->getInfo().animation;
 
 			auto cached = animationsCache.find(animationName);
 
@@ -379,7 +380,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
 		}
 		else if (elem->obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
 		{
-			std::string animationName = elem->getInfo().defName;
+			std::string animationName = elem->getInfo().animation;
 
 			auto cached = animationsCache.find(animationName);
 

+ 4 - 0
config/gameConfig.json

@@ -88,5 +88,9 @@
 	"battlefields":
 	[
 		"config/battlefields.json"
+	],
+	"obstacles":
+	[
+		"config/obstacles.json"
 	]
 }

+ 469 - 348
config/obstacles.json

@@ -8,1228 +8,1349 @@
 // * "blockedTiles": for absolute obstacles contains absolute coordinates. For usual obstacles contains offsets relative to the obstacle position (that is bottom left corner). If obstacle is placed in an odd row (counting from 0) and the blocked tile is in an even row, position will be shifted one tile to the left. Thanks to that ie. -16 is always top-right hex, no matter where the obstale will get placed.
 // * "width" for usual obstacles it's count of tiles that must be free to the right for obstacle to be placed. For absolute obstacles, it's x offset for the graphics.
 // * "height" for usual obstacles it's count of tiles that must be free to the top for obstacle to be placed. For absolute obstacles, it's y offset for the graphics.
-// * "defname" is name of the graphics. It's def file for usual obstacles and bitmap for the absolute ones.
+// * "animation" is name of the graphics. It's def file for usual obstacles and bitmap for the absolute ones.
 
 {
-"obstacles" : [
+	"0":
 	{
-		"id" : 0,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObDino1.def",
-		"unknown" : 1
+		"animation" : "ObDino1.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"1":
 	{
-		"id" : 1,
 		"allowedTerrain" : ["dirt", "sand", "rough", "subterra"],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObDino2.def",
-		"unknown" : 0
+		"animation" : "ObDino2.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"2":
 	{
-		"id" : 2,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, -14, -15, -16],
-		"defname" : "ObDino3.def",
-		"unknown" : 1
+		"animation" : "ObDino3.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"3":
 	{
-		"id" : 3,
 		"allowedTerrain" : ["dirt", "rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObSkel1.def",
-		"unknown" : 1
+		"animation" : "ObSkel1.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"4":
 	{
-		"id" : 4,
 		"allowedTerrain" : ["dirt", "rough", "subterra"],
 		"specialBattlefields" : ["sand_shore", "cursed_ground"],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObSkel2.def",
-		"unknown" : 1
+		"animation" : "ObSkel2.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"5":
 	{
-		"id" : 5,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3],
-		"defname" : "ObBDT01.def",
-		"unknown" : 1
+		"animation" : "ObBDT01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"6":
 	{
-		"id" : 6,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [-15, -16],
-		"defname" : "ObDRk01.def",
-		"unknown" : 1
+		"animation" : "ObDRk01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"7":
 	{
-		"id" : 7,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObDRk02.def",
-		"unknown" : 0
+		"animation" : "ObDRk02.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"8":
 	{
-		"id" : 8,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [-16],
-		"defname" : "ObDRk03.def",
-		"unknown" : 1
+		"animation" : "ObDRk03.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"9":
 	{
-		"id" : 9,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObDRk04.def",
-		"unknown" : 0
+		"animation" : "ObDRk04.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"10":
 	{
-		"id" : 10,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObDSh01.def",
-		"unknown" : 0
+		"animation" : "ObDSh01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"11":
 	{
-		"id" : 11,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObDTF03.def",
-		"unknown" : 0
+		"animation" : "ObDTF03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"12":
 	{
-		"id" : 12,
 		"allowedTerrain" : ["dirt", "rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [0, 1, 2, 3],
-		"defname" : "ObDtS03.def",
-		"unknown" : 0
+		"animation" : "ObDtS03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"13":
 	{
-		"id" : 13,
 		"allowedTerrain" : ["dirt", "rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, -15],
-		"defname" : "ObDtS04.def",
-		"unknown" : 1
+		"animation" : "ObDtS04.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"14":
 	{
-		"id" : 14,
 		"allowedTerrain" : ["dirt", "rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [2, -15, -16],
-		"defname" : "ObDtS14.def",
-		"unknown" : 1
+		"animation" : "ObDtS14.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"15":
 	{
-		"id" : 15,
 		"allowedTerrain" : ["dirt", "rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [1, -16, -33],
-		"defname" : "ObDtS15.def",
-		"unknown" : 1
+		"animation" : "ObDtS15.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"16":
 	{
-		"id" : 16,
 		"allowedTerrain" : ["sand"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 4,
 		"blockedTiles" :  [-15, -16, -32, -33, -48, -49],
-		"defname" : "ObDsM01.def",
-		"unknown" : 1
+		"animation" : "ObDsM01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"17":
 	{
-		"id" : 17,
 		"allowedTerrain" : ["sand"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [1, -15, -16],
-		"defname" : "ObDsS02.def",
-		"unknown" : 1
+		"animation" : "ObDsS02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"18":
 	{
-		"id" : 18,
 		"allowedTerrain" : ["sand"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3, -15, -16],
-		"defname" : "ObDsS17.def",
-		"unknown" : 1
+		"animation" : "ObDsS17.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"19":
 	{
-		"id" : 19,
 		"allowedTerrain" : ["grass", "swamp"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObGLg01.def",
-		"unknown" : 1
+		"animation" : "ObGLg01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"20":
 	{
-		"id" : 20,
 		"allowedTerrain" : ["grass", "swamp"],
 		"specialBattlefields" : ["magic_plains"],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObGRk01.def",
-		"unknown" : 0
+		"animation" : "ObGRk01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"21":
 	{
-		"id" : 21,
 		"allowedTerrain" : ["grass", "swamp"],
 		"specialBattlefields" : [],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObGSt01.def",
-		"unknown" : 1
+		"animation" : "ObGSt01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"22":
 	{
-		"id" : 22,
 		"allowedTerrain" : ["grass"],
 		"specialBattlefields" : ["magic_plains"],
 		"width" : 6,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3, 4, -13, -14, -15, -16],
-		"defname" : "ObGrS01.def",
-		"unknown" : 1
+		"animation" : "ObGrS01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"23":
 	{
-		"id" : 23,
 		"allowedTerrain" : ["grass"],
 		"specialBattlefields" : [],
 		"width" : 7,
 		"height" : 1,
 		"blockedTiles" :  [1, 2],
-		"defname" : "OBGrS02.def",
-		"unknown" : 1
+		"animation" : "OBGrS02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"24":
 	{
-		"id" : 24,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 1,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObSnS01.def",
-		"unknown" : 1
+		"animation" : "ObSnS01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"25":
 	{
-		"id" : 25,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 5,
 		"height" : 1,
 		"blockedTiles" :  [1, 2, 3, 4],
-		"defname" : "ObSnS02.def",
-		"unknown" : 1
+		"animation" : "ObSnS02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"26":
 	{
-		"id" : 26,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [0, -16, -33],
-		"defname" : "ObSnS03.def",
-		"unknown" : 1
+		"animation" : "ObSnS03.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"27":
 	{
-		"id" : 27,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 1,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObSnS04.def",
-		"unknown" : 1
+		"animation" : "ObSnS04.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"28":
 	{
-		"id" : 28,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 1,
 		"blockedTiles" :  [1],
-		"defname" : "ObSnS05.def",
-		"unknown" : 1
+		"animation" : "ObSnS05.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"29":
 	{
-		"id" : 29,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [1, 2],
-		"defname" : "ObSnS06.def",
-		"unknown" : 0
+		"animation" : "ObSnS06.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"30":
 	{
-		"id" : 30,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObSnS07.def",
-		"unknown" : 1
+		"animation" : "ObSnS07.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"31":
 	{
-		"id" : 31,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObSnS08.def",
-		"unknown" : 0
+		"animation" : "ObSnS08.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"32":
 	{
-		"id" : 32,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 7,
 		"height" : 2,
 		"blockedTiles" :  [2, 3, 4, 5, -13, -14, -15, -16],
-		"defname" : "ObSnS09.def",
-		"unknown" : 1
+		"animation" : "ObSnS09.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"33":
 	{
-		"id" : 33,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 5,
 		"height" : 5,
 		"blockedTiles" :  [3, -13, -14, -15, -33, -49, -66],
-		"defname" : "ObSnS10.def",
-		"unknown" : 1
+		"animation" : "ObSnS10.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"34":
 	{
-		"id" : 34,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [0],
-		"defname" : "ObSwS01.def",
-		"unknown" : 0
+		"animation" : "ObSwS01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"35":
 	{
-		"id" : 35,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 8,
 		"height" : 3,
 		"blockedTiles" :  [-10, -11, -12, -13, -14, -15, -16],
-		"defname" : "ObSwS02.def",
-		"unknown" : 1
+		"animation" : "ObSwS02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"36":
 	{
-		"id" : 36,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObSwS03.def",
-		"unknown" : 0
+		"animation" : "ObSwS03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"37":
 	{
-		"id" : 37,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 1,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObSwS04.def",
-		"unknown" : 0
+		"animation" : "ObSwS04.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"38":
 	{
-		"id" : 38,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 5,
 		"height" : 4,
 		"blockedTiles" :  [-13, -14, -15, -16, -30, -31, -32, -33],
-		"defname" : "ObSwS11b.def",
-		"unknown" : 1
+		"animation" : "ObSwS11b.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"39":
 	{
-		"id" : 39,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 3,
 		"blockedTiles" :  [-16, -17, -31, -32, -33, -34],
-		"defname" : "ObSwS13a.def",
-		"unknown" : 1
+		"animation" : "ObSwS13a.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"40":
 	{
-		"id" : 40,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, -16],
-		"defname" : "ObRgS01.def",
-		"unknown" : 1
+		"animation" : "ObRgS01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"41":
 	{
-		"id" : 41,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 4,
 		"height" : 3,
 		"blockedTiles" :  [-14, -15, -16, -32, -33],
-		"defname" : "ObRgS02.def",
-		"unknown" : 1
+		"animation" : "ObRgS02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"42":
 	{
-		"id" : 42,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, -15, -16],
-		"defname" : "ObRgS03.def",
-		"unknown" : 1
+		"animation" : "ObRgS03.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"43":
 	{
-		"id" : 43,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [-16, -32, -33],
-		"defname" : "ObRgS04.def",
-		"unknown" : 1
+		"animation" : "ObRgS04.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"44":
 	{
-		"id" : 44,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [-15, -16, -32],
-		"defname" : "ObRgS05.def",
-		"unknown" : 1
+		"animation" : "ObRgS05.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"45":
 	{
-		"id" : 45,
 		"allowedTerrain" : ["subterra"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [0, 1, 2, -15, -16],
-		"defname" : "ObSuS01.def",
-		"unknown" : 0
+		"animation" : "ObSuS01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"46":
 	{
-		"id" : 46,
 		"allowedTerrain" : ["subterra"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObSuS02.def",
-		"unknown" : 0
+		"animation" : "ObSuS02.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"47":
 	{
-		"id" : 47,
 		"allowedTerrain" : ["subterra"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 3,
 		"blockedTiles" :  [0, 1, 2, 3, -14, -15, -16],
-		"defname" : "ObSuS11b.def",
-		"unknown" : 0
+		"animation" : "ObSuS11b.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"48":
 	{
-		"id" : 48,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 3,
 		"blockedTiles" :  [-14, -32, -33],
-		"defname" : "ObLvS01.def",
-		"unknown" : 1
+		"animation" : "ObLvS01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"49":
 	{
-		"id" : 49,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2, -14, -15, -16],
-		"defname" : "ObLvS02.def",
-		"unknown" : 1
+		"animation" : "ObLvS02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"50":
 	{
-		"id" : 50,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 5,
 		"height" : 3,
 		"blockedTiles" :  [-13, -14, -15, -30, -31, -32, -33],
-		"defname" : "ObLvS03.def",
-		"unknown" : 1
+		"animation" : "ObLvS03.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"51":
 	{
-		"id" : 51,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObLvS04.def",
-		"unknown" : 0
+		"animation" : "ObLvS04.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"52":
 	{
-		"id" : 52,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 4,
 		"height" : 4,
 		"blockedTiles" :  [-14, -15, -32, -33, -49, -50],
-		"defname" : "ObLvS09.def",
-		"unknown" : 1
+		"animation" : "ObLvS09.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"53":
 	{
-		"id" : 53,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 5,
 		"height" : 3,
 		"blockedTiles" :  [-13, -14, -15, -16, -30, -31],
-		"defname" : "ObLvS17.def",
-		"unknown" : 1
+		"animation" : "ObLvS17.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"54":
 	{
-		"id" : 54,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 5,
 		"height" : 3,
 		"blockedTiles" :  [-13, -14, -15, -16, -31, -32, -33],
-		"defname" : "ObLvS22.def",
-		"unknown" : 1
+		"animation" : "ObLvS22.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"55":
 	{
-		"id" : 55,
 		"allowedTerrain" : ["water"],
 		"specialBattlefields" : [],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [-15, -16, -33],
-		"defname" : "ObBtS04.def",
-		"unknown" : 1
+		"animation" : "ObBtS04.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"56":
 	{
-		"id" : 56,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [1, -15, -16],
-		"defname" : "ObBhS02.def",
-		"unknown" : 1
+		"animation" : "ObBhS02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"57":
 	{
-		"id" : 57,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObBhS03.def",
-		"unknown" : 0
+		"animation" : "ObBhS03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"58":
 	{
-		"id" : 58,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 5,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3, -14, -15, -16],
-		"defname" : "ObBhS11a.def",
-		"unknown" : 1
+		"animation" : "ObBhS11a.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"59":
 	{
-		"id" : 59,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, -14, -15],
-		"defname" : "ObBhS12b.def",
-		"unknown" : 1
+		"animation" : "ObBhS12b.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"60":
 	{
-		"id" : 60,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, -16],
-		"defname" : "ObBhS14b.def",
-		"unknown" : 1
+		"animation" : "ObBhS14b.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"61":
 	{
-		"id" : 61,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["holy_ground"],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObHGs00.def",
-		"unknown" : 0
+		"animation" : "ObHGs00.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"62":
 	{
-		"id" : 62,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["holy_ground"],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObHGs01.def",
-		"unknown" : 0
+		"animation" : "ObHGs01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"63":
 	{
-		"id" : 63,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["holy_ground"],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [1],
-		"defname" : "ObHGs02.def",
-		"unknown" : 0
+		"animation" : "ObHGs02.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"64":
 	{
-		"id" : 64,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["holy_ground"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObHGs03.def",
-		"unknown" : 0
+		"animation" : "ObHGs03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"65":
 	{
-		"id" : 65,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["holy_ground"],
 		"width" : 4,
 		"height" : 3,
 		"blockedTiles" :  [0, 1, 2, 3],
-		"defname" : "ObHGs04.def",
-		"unknown" : 0
+		"animation" : "ObHGs04.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"66":
 	{
-		"id" : 66,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["evil_fog"],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObEFs00.def",
-		"unknown" : 0
+		"animation" : "ObEFs00.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"67":
 	{
-		"id" : 67,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["evil_fog"],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObEFs01.def",
-		"unknown" : 0
+		"animation" : "ObEFs01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"68":
 	{
-		"id" : 68,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["evil_fog"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObEFs02.def",
-		"unknown" : 0
+		"animation" : "ObEFs02.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"69":
 	{
-		"id" : 69,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["evil_fog"],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [1, 2],
-		"defname" : "ObEFs03.def",
-		"unknown" : 0
+		"animation" : "ObEFs03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"70":
 	{
-		"id" : 70,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["evil_fog"],
 		"width" : 6,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3, -12, -13],
-		"defname" : "ObEFs04.def",
-		"unknown" : 0
+		"animation" : "ObEFs04.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"71":
 	{
-		"id" : 71,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["clover_field"],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObCFs00.def",
-		"unknown" : 1
+		"animation" : "ObCFs00.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"72":
 	{
-		"id" : 72,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["clover_field"],
 		"width" : 3,
 		"height" : 1,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObCFs01.def",
-		"unknown" : 1
+		"animation" : "ObCFs01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"73":
 	{
-		"id" : 73,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["clover_field"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, -15, -16],
-		"defname" : "ObCFs02.def",
-		"unknown" : 1
+		"animation" : "ObCFs02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"74":
 	{
-		"id" : 74,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["clover_field"],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2, -14, -15, -16],
-		"defname" : "ObCFs03.def",
-		"unknown" : 1
+		"animation" : "ObCFs03.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"75":
 	{
-		"id" : 75,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["lucid_pools"],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObLPs00.def",
-		"unknown" : 1
+		"animation" : "ObLPs00.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"76":
 	{
-		"id" : 76,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["lucid_pools"],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObLPs01.def",
-		"unknown" : 1
+		"animation" : "ObLPs01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"77":
 	{
-		"id" : 77,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["lucid_pools"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, -15, -16],
-		"defname" : "ObLPs02.def",
-		"unknown" : 1
+		"animation" : "ObLPs02.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"78":
 	{
-		"id" : 78,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["lucid_pools"],
 		"width" : 5,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3, -13, -14, -15, -16],
-		"defname" : "ObLPs03.def",
-		"unknown" : 1
+		"animation" : "ObLPs03.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"79":
 	{
-		"id" : 79,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["fiery_fields"],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObFFs00.def",
-		"unknown" : 0
+		"animation" : "ObFFs00.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"80":
 	{
-		"id" : 80,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["fiery_fields"],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObFFs01.def",
-		"unknown" : 0
+		"animation" : "ObFFs01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"81":
 	{
-		"id" : 81,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["fiery_fields"],
 		"width" : 3,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, 2, -15],
-		"defname" : "ObFFs02.def",
-		"unknown" : 0
+		"animation" : "ObFFs02.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"82":
 	{
-		"id" : 82,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["fiery_fields"],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3, -15, -16],
-		"defname" : "ObFFs03.def",
-		"unknown" : 0
+		"animation" : "ObFFs03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"83":
 	{
-		"id" : 83,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["fiery_fields"],
 		"width" : 3,
 		"height" : 3,
 		"blockedTiles" :  [0, 1, 2, 3, -14, -15, -16],
-		"defname" : "ObFFs04.def",
-		"unknown" : 0
+		"animation" : "ObFFs04.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"84":
 	{
-		"id" : 84,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["rocklands"],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObRLs00.def",
-		"unknown" : 0
+		"animation" : "ObRLs00.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"85":
 	{
-		"id" : 85,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["rocklands"],
 		"width" : 2,
 		"height" : 1,
 		"blockedTiles" :  [0, 1],
-		"defname" : "ObRLs01.def",
-		"unknown" : 0
+		"animation" : "ObRLs01.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"86":
 	{
-		"id" : 86,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["rocklands"],
 		"width" : 3,
 		"height" : 1,
 		"blockedTiles" :  [0, 1, 2],
-		"defname" : "ObRLs02.def",
-		"unknown" : 0
+		"animation" : "ObRLs02.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"87":
 	{
-		"id" : 87,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["rocklands"],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [1, 2, 3, -15, -16],
-		"defname" : "ObRLs03.def",
-		"unknown" : 0
+		"animation" : "ObRLs03.def",
+		"unknown" : 0,
+		"absolute" : false
 	},
+	"88":
 	{
-		"id" : 88,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["magic_clouds"],
 		"width" : 1,
 		"height" : 1,
 		"blockedTiles" :  [0],
-		"defname" : "ObMCs00.def",
-		"unknown" : 1
+		"animation" : "ObMCs00.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"89":
 	{
-		"id" : 89,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["magic_clouds"],
 		"width" : 2,
 		"height" : 2,
 		"blockedTiles" :  [1, -16],
-		"defname" : "ObMCs01.def",
-		"unknown" : 1
+		"animation" : "ObMCs01.def",
+		"unknown" : 1,
+		"absolute" : false
 	},
+	"90":
 	{
-		"id" : 90,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["magic_clouds"],
 		"width" : 4,
 		"height" : 2,
 		"blockedTiles" :  [0, 1, -14, -15],
-		"defname" : "ObMCs02.def",
-		"unknown" : 1
-	}
-],
-"absoluteObstacles" : [
+		"animation" : "ObMCs02.def",
+		"unknown" : 1,
+		"absolute" : false
+	},
+
+	"100":
 	{
-		"id" : 0,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 124,
 		"height" : 254,
 		"blockedTiles" :  [80, 94, 95, 96, 97, 105, 106, 107, 108, 109, 110],
-		"defname" : "ObDtL04.pcx"
+		"animation" : "ObDtL04.pcx",
+		"absolute" : true
 	},
+	"101":
 	{
-		"id" : 1,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 256,
 		"height" : 254,
 		"blockedTiles" :  [73, 91, 108, 109, 110, 111, 112, 113],
-		"defname" : "ObDtL06.pcx"
+		"animation" : "ObDtL06.pcx",
+		"absolute" : true
 	},
+	"102":
 	{
-		"id" : 2,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 168,
 		"height" : 212,
 		"blockedTiles" :  [60, 61, 62, 63, 64, 72, 73, 74, 75, 76, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149],
-		"defname" : "ObDtL10.pcx"
+		"animation" : "ObDtL10.pcx",
+		"absolute" : true
 	},
+	"103":
 	{
-		"id" : 3,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 124,
 		"height" : 254,
 		"blockedTiles" :  [88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98],
-		"defname" : "ObDtL02.pcx"
+		"animation" : "ObDtL02.pcx",
+		"absolute" : true
 	},
+	"104":
 	{
-		"id" : 4,
 		"allowedTerrain" : ["dirt"],
 		"specialBattlefields" : [],
 		"width" : 146,
 		"height" : 254,
 		"blockedTiles" :  [76, 77, 78, 79, 80, 89, 90, 91, 92, 93],
-		"defname" : "ObDtL03.pcx"
+		"animation" : "ObDtL03.pcx",
+		"absolute" : true
 	},
+	"105":
 	{
-		"id" : 5,
 		"allowedTerrain" : ["grass"],
 		"specialBattlefields" : [],
 		"width" : 173,
 		"height" : 221,
 		"blockedTiles" :  [55, 56, 57, 58, 75, 76, 77, 95, 112, 113, 131],
-		"defname" : "ObGrL01.pcx"
+		"animation" : "ObGrL01.pcx",
+		"absolute" : true
 	},
+	"106":
 	{
-		"id" : 6,
 		"allowedTerrain" : ["grass"],
 		"specialBattlefields" : [],
 		"width" : 180,
 		"height" : 264,
 		"blockedTiles" :  [81, 91, 92, 93, 94, 95, 96, 97, 98, 106, 107, 123],
-		"defname" : "ObGrL02.pcx"
+		"animation" : "ObGrL02.pcx",
+		"absolute" : true
 	},
+	"107":
 	{
-		"id" : 7,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 166,
 		"height" : 255,
 		"blockedTiles" :  [76, 77, 78, 79, 91, 92, 93, 97, 98, 106, 107, 108],
-		"defname" : "ObSnL01.pcx"
+		"animation" : "ObSnL01.pcx",
+		"absolute" : true
 	},
+	"108":
 	{
-		"id" : 8,
 		"allowedTerrain" : ["snow"],
 		"specialBattlefields" : [],
 		"width" : 302,
 		"height" : 172,
 		"blockedTiles" :  [41, 42, 43, 58, 75, 92, 108, 126, 143],
-		"defname" : "ObSnL14.pcx"
+		"animation" : "ObSnL14.pcx",
+		"absolute" : true
 	},
+	"109":
 	{
-		"id" : 9,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 300,
 		"height" : 170,
 		"blockedTiles" :  [40, 41, 58, 59, 74, 75, 92, 93, 109, 110, 111, 127, 128, 129, 130],
-		"defname" : "ObSwL15.pcx"
+		"animation" : "ObSwL15.pcx",
+		"absolute" : true
 	},
+	"110":
 	{
-		"id" : 10,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 278,
 		"height" : 171,
 		"blockedTiles" :  [43, 60, 61, 77, 93, 94, 95, 109, 110, 126, 127],
-		"defname" : "ObSwL14.pcx"
+		"animation" : "ObSwL14.pcx",
+		"absolute" : true
 	},
+	"111":
 	{
-		"id" : 11,
 		"allowedTerrain" : ["swamp"],
 		"specialBattlefields" : [],
 		"width" : 256,
 		"height" : 254,
 		"blockedTiles" :  [74, 75, 76, 77, 91, 92, 93, 94, 95, 109, 110, 111, 112],
-		"defname" : "ObSwL22.pcx"
+		"animation" : "ObSwL22.pcx",
+		"absolute" : true
 	},
+	"112":
 	{
-		"id" : 12,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 124,
 		"height" : 254,
 		"blockedTiles" :  [77, 78, 79, 80, 81, 91, 92, 93, 94, 105, 106, 107],
-		"defname" : "ObLvL01.pcx"
+		"animation" : "ObLvL01.pcx",
+		"absolute" : true
 	},
+	"113":
 	{
-		"id" : 13,
 		"allowedTerrain" : ["lava"],
 		"specialBattlefields" : [],
 		"width" : 256,
 		"height" : 128,
 		"blockedTiles" :  [43, 60, 61, 76, 77, 93, 109, 126, 127, 142, 143],
-		"defname" : "OBLvL02.pcx"
+		"animation" : "OBLvL02.pcx",
+		"absolute" : true
 	},
+	"114":
 	{
-		"id" : 14,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 186,
 		"height" : 212,
 		"blockedTiles" :  [55, 72, 90, 107, 125, 126, 127, 128, 129, 130, 131, 132],
-		"defname" : "ObRgL01.pcx"
+		"animation" : "ObRgL01.pcx",
+		"absolute" : true
 	},
+	"115":
 	{
-		"id" : 15,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 347,
 		"height" : 174,
 		"blockedTiles" :  [41, 59, 76, 94, 111, 129, 143, 144, 145],
-		"defname" : "ObRgL02.pcx"
+		"animation" : "ObRgL02.pcx",
+		"absolute" : true
 	},
+	"116":
 	{
-		"id" : 16,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 294,
 		"height" : 169,
 		"blockedTiles" :  [40, 41, 42, 43, 58, 75, 93, 110, 128, 145],
-		"defname" : "ObRgL03.pcx"
+		"animation" : "ObRgL03.pcx",
+		"absolute" : true
 	},
+	"117":
 	{
-		"id" : 17,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 165,
 		"height" : 257,
 		"blockedTiles" :  [72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 89, 105],
-		"defname" : "ObRgL04.pcx"
+		"animation" : "ObRgL04.pcx",
+		"absolute" : true
 	},
+	"118":
 	{
-		"id" : 18,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 208,
 		"height" : 268,
 		"blockedTiles" :  [72, 73, 74, 75, 76, 77, 78, 79, 80, 90, 91, 92, 93, 94, 95, 96, 97],
-		"defname" : "ObRgL05.pcx"
+		"animation" : "ObRgL05.pcx",
+		"absolute" : true
 	},
+	"119":
 	{
-		"id" : 19,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 252,
 		"height" : 254,
 		"blockedTiles" :  [73, 74, 75, 76, 77, 78, 91, 92, 93, 94],
-		"defname" : "ObRgL06.pcx"
+		"animation" : "ObRgL06.pcx",
+		"absolute" : true
 	},
+	"120":
 	{
-		"id" : 20,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 278,
 		"height" : 128,
 		"blockedTiles" :  [23, 40, 58, 75, 93, 110, 128, 145, 163],
-		"defname" : "ObRgL15.pcx"
+		"animation" : "ObRgL15.pcx",
+		"absolute" : true
 	},
+	"121":
 	{
-		"id" : 21,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 208,
 		"height" : 268,
 		"blockedTiles" :  [72, 73, 74, 75, 76, 77, 78, 79, 80, 90, 91, 92, 93, 94, 95, 96, 97],
-		"defname" : "ObRgL05.pcx"
+		"animation" : "ObRgL05.pcx",
+		"absolute" : true
 	},
+	"122":
 	{
-		"id" : 22,
 		"allowedTerrain" : ["rough"],
 		"specialBattlefields" : ["cursed_ground"],
 		"width" : 168,
 		"height" : 212,
 		"blockedTiles" :  [73, 74, 75, 76, 77, 78, 79, 90, 91, 92, 93, 94, 95, 96, 97, 106, 107, 108, 109, 110, 111, 112],
-		"defname" : "ObRgL22.pcx"
+		"animation" : "ObRgL22.pcx",
+		"absolute" : true
 	},
+	"123":
 	{
-		"id" : 23,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 147,
 		"height" : 264,
 		"blockedTiles" :  [72, 73, 74, 75, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98],
-		"defname" : "ObBhL02.pcx"
+		"animation" : "ObBhL02.pcx",
+		"absolute" : true
 	},
+	"124":
 	{
-		"id" : 24,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 178,
 		"height" : 262,
 		"blockedTiles" :  [71, 72, 73, 74, 75, 76, 77, 78, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98],
-		"defname" : "ObBhL03.pcx"
+		"animation" : "ObBhL03.pcx",
+		"absolute" : true
 	},
+	"125":
 	{
-		"id" : 25,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 173,
 		"height" : 257,
 		"blockedTiles" :  [72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 89, 90, 105, 106],
-		"defname" : "ObBhL05.pcx"
+		"animation" : "ObBhL05.pcx",
+		"absolute" : true
 	},
+	"126":
 	{
-		"id" : 26,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 241,
 		"height" : 272,
 		"blockedTiles" :  [73, 91, 108, 109, 110, 111, 112, 113],
-		"defname" : "ObBhL06.pcx"
+		"animation" : "ObBhL06.pcx",
+		"absolute" : true
 	},
+	"127":
 	{
-		"id" : 27,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 261,
 		"height" : 129,
 		"blockedTiles" :  [27, 28, 43, 44, 60, 61, 76, 77, 93, 94, 109, 110, 126, 127, 142, 143, 159],
-		"defname" : "ObBhL14.pcx"
+		"animation" : "ObBhL14.pcx",
+		"absolute" : true
 	},
+	"128":
 	{
-		"id" : 28,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["sand_shore"],
 		"width" : 180,
 		"height" : 154,
 		"blockedTiles" :  [22, 38, 39, 40, 44, 45, 46, 55, 56, 57, 62, 63, 123, 124, 125, 130, 131, 140, 141, 146, 147, 148],
-		"defname" : "ObBhL16.pcx"
+		"animation" : "ObBhL16.pcx",
+		"absolute" : true
 	},
+	"129":
 	{
-		"id" : 29,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["clover_field"],
 		"width" : 304,
 		"height" : 264,
 		"blockedTiles" :  [76, 77, 92, 93, 94, 95, 109, 110, 111],
-		"defname" : "ObCFL00.pcx"
+		"animation" : "ObCFL00.pcx",
+		"absolute" : true
 	},
+	"130":
 	{
-		"id" : 30,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["lucid_pools"],
 		"width" : 256,
 		"height" : 257,
 		"blockedTiles" :  [76, 77, 78, 92, 93, 94, 107, 108, 109],
-		"defname" : "ObLPL00.pcx"
+		"animation" : "ObLPL00.pcx",
+		"absolute" : true
 	},
+	"131":
 	{
-		"id" : 31,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["fiery_fields"],
 		"width" : 257,
 		"height" : 255,
 		"blockedTiles" :  [76, 77, 91, 92, 93, 94, 95, 108, 109, 110, 111],
-		"defname" : "ObFFL00.pcx"
+		"animation" : "ObFFL00.pcx",
+		"absolute" : true
 	},
+	"132":
 	{
-		"id" : 32,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["rocklands"],
 		"width" : 277,
 		"height" : 218,
 		"blockedTiles" :  [60, 61, 75, 76, 77, 91, 92, 93, 94, 95],
-		"defname" : "ObRLL00.pcx"
+		"animation" : "ObRLL00.pcx",
+		"absolute" : true
 	},
+	"133":
 	{
-		"id" : 33,
 		"allowedTerrain" : [],
 		"specialBattlefields" : ["magic_clouds"],
 		"width" : 300,
 		"height" : 214,
 		"blockedTiles" :  [59, 60, 74, 75, 76, 93, 94, 95, 111, 112],
-		"defname" : "ObMCL00.pcx"
+		"animation" : "ObMCL00.pcx",
+		"absolute" : true
 	}
-]
-
 }

+ 1 - 1
config/schemas/battlefield.json

@@ -2,7 +2,7 @@
 	"type":"object",
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI battlefield format",
-	"description" : "Format used to define new artifacts in VCMI",
+	"description" : "Format used to define new battlefields in VCMI",
 	"required" : [ "graphics" ],
 
 	"additionalProperties" : false,

+ 46 - 0
config/schemas/obstacle.json

@@ -0,0 +1,46 @@
+{
+	"type":"object",
+	"$schema": "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI obstacle format",
+	"description" : "Format used to define new obstacles in VCMI",
+	"required" : [ "animation" ],
+
+	"additionalProperties" : false,
+	"properties":{
+		"allowedTerrains": {
+			"type": "array",
+			"description": "Obstacles can be place on specified terrains only",
+			"items": { "type" : "string" }
+		},
+		"specialBattlefields": {
+			"type": "array",
+			"description": "Obstacles can be placed on specified specified battlefields",
+			"items": { "$ref" : "battlefield.json" }
+		},
+		"width": {
+			"type": "number",
+			"description": "Width ob obstacle"
+		},
+		"height": {
+			"type": "number",
+			"description": "height if obstacle"
+		},
+		"blockedTiles": {
+			"type": "array",
+			"description": "Blocked hexes - absolute or relative hex id",
+			"items": { "type" : "number" }
+		},
+		"animation": {
+			"type": "string",
+			"description": "Image resource"
+		},
+		"unknown": {
+			"type": "number",
+			"description": "Unknown field"
+		},
+		"absolute": {
+			"type": "boolean",
+			"description": "Should be used absolute or relative coordinates for obstacle. There is possible only one absolute obstacle"
+		}
+	}
+}

+ 2 - 0
include/vcmi/Services.h

@@ -20,6 +20,7 @@ class HeroTypeService;
 class SkillService;
 class JsonNode;
 class BattleFieldService;
+class ObstacleService;
 
 namespace spells
 {
@@ -50,6 +51,7 @@ public:
 	virtual const spells::Service * spells() const = 0;
 	virtual const SkillService * skills() const = 0;
 	virtual const BattleFieldService * battlefields() const = 0;
+	virtual const ObstacleService * obstacles() const = 0;
 
 	virtual void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) = 0;
 

+ 1 - 1
lib/BattleFieldHandler.cpp

@@ -1,5 +1,5 @@
 /*
- * BattleFieldHandler.h, part of VCMI engine
+ * BattleFieldHandler.cpp, part of VCMI engine
  *
  * Authors: listed in file AUTHORS in main folder
  *

+ 1 - 0
lib/BattleFieldHandler.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include <vcmi/EntityService.h>
+#include <vcmi/Entity.h>
 #include "HeroBonus.h"
 #include "GameConstants.h"
 #include "IHandlerBase.h"

+ 0 - 81
lib/CHeroHandler.cpp

@@ -153,41 +153,6 @@ CHeroClass::CHeroClass()
 {
 }
 
-std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const
-{
-	std::vector<BattleHex> ret;
-	if(isAbsoluteObstacle)
-	{
-		assert(!hex.isValid());
-		range::copy(blockedTiles, std::back_inserter(ret));
-		return ret;
-	}
-
-	for(int offset : blockedTiles)
-	{
-		BattleHex toBlock = hex + offset;
-		if((hex.getY() & 1) && !(toBlock.getY() & 1))
-			toBlock += BattleHex::LEFT;
-
-		if(!toBlock.isValid())
-			logGlobal->error("Misplaced obstacle!");
-		else
-			ret.push_back(toBlock);
-	}
-
-	return ret;
-}
-
-bool CObstacleInfo::isAppropriate(const Terrain & terrainType, const BattleField & battlefield) const
-{
-	auto bgInfo = battlefield.getInfo();
-
-	if(bgInfo->isSpecial)
-		return vstd::contains(allowedSpecialBfields, bgInfo->identifier);
-
-	return vstd::contains(allowedTerrains, terrainType);
-}
-
 void CHeroClassHandler::fillPrimarySkillData(const JsonNode & node, CHeroClass * heroClass, PrimarySkill::PrimarySkill pSkill)
 {
 	const auto & skillName = PrimarySkill::names[pSkill];
@@ -378,7 +343,6 @@ CHeroHandler::~CHeroHandler() = default;
 
 CHeroHandler::CHeroHandler()
 {
-	loadObstacles();
 	loadTerrains();
 	for(int i = 0; i < Terrain::Manager::terrains().size(); ++i)
 	{
@@ -818,51 +782,6 @@ void CHeroHandler::loadExperience()
 	expPerLevel.pop_back();//last value is broken
 }
 
-void CHeroHandler::loadObstacles()
-{
-	auto loadObstacles = [](const JsonNode & node, bool absolute, std::vector<CObstacleInfo> & out)
-	{
-		for(const JsonNode &obs : node.Vector())
-		{
-			out.emplace_back();
-			CObstacleInfo & obi = out.back();
-			obi.defName = obs["defname"].String();
-			obi.width =  static_cast<si32>(obs["width"].Float());
-			obi.height = static_cast<si32>(obs["height"].Float());
-			for(auto & t : obs["allowedTerrain"].Vector())
-				obi.allowedTerrains.emplace_back(t.String());
-			for(auto & t : obs["specialBattlefields"].Vector())
-				obi.allowedSpecialBfields.emplace_back(t.String());
-			obi.blockedTiles = obs["blockedTiles"].convertTo<std::vector<si16> >();
-			obi.isAbsoluteObstacle = absolute;
-		}
-	};
-	
-	auto allConfigs = VLC->modh->getActiveMods();
-	allConfigs.insert(allConfigs.begin(), "core");
-	for(auto & mod : allConfigs)
-	{
-		ISimpleResourceLoader * modResourceLoader;
-		try
-		{
-			modResourceLoader = CResourceHandler::get(mod);
-		}
-		catch(const std::out_of_range &)
-		{
-			logMod->warn("Mod '%1%' doesn't exist! Its obstacles won't be loaded!", mod);
-			continue;
-		}
-
-		const ResourceID obstaclesResource{"config/obstacles.json"};
-		if(!modResourceLoader->existsResource(obstaclesResource))
-			continue;
-		
-		const JsonNode config(mod, obstaclesResource);
-		loadObstacles(config["obstacles"], false, obstacles);
-		loadObstacles(config["absoluteObstacles"], true, absoluteObstacles);
-	}
-}
-
 /// convert h3-style ID (e.g. Gobin Wolf Rider) to vcmi (e.g. goblinWolfRider)
 static std::string genRefName(std::string input)
 {

+ 0 - 32
lib/CHeroHandler.h

@@ -223,32 +223,6 @@ public:
 	EAlignment::EAlignment getAlignment() const;
 };
 
-struct DLL_LINKAGE CObstacleInfo
-{
-	std::string defName;
-	std::vector<Terrain> allowedTerrains;
-	std::vector<std::string> allowedSpecialBfields;
-
-	ui8 isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same
-	si32 width, height; //how much space to the right and up is needed to place obstacle (affects only placement algorithm)
-	std::vector<si16> blockedTiles; //offsets relative to obstacle position (that is its left bottom corner)
-
-	std::vector<BattleHex> getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex'
-
-	bool isAppropriate(const Terrain & terrainType, const BattleField & specialBattlefield) const;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & defName;
-		h & allowedTerrains;
-		h & allowedSpecialBfields;
-		h & isAbsoluteObstacle;
-		h & width;
-		h & height;
-		h & blockedTiles;
-	}
-};
-
 class DLL_LINKAGE CHeroClassHandler : public CHandlerBase<HeroClassID, HeroClass, CHeroClass, HeroClassService>
 {
 	void fillPrimarySkillData(const JsonNode & node, CHeroClass * heroClass, PrimarySkill::PrimarySkill pSkill);
@@ -286,7 +260,6 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero
 	void loadExperience();
 	void loadBallistics();
 	void loadTerrains();
-	void loadObstacles();
 
 public:
 	CHeroClassHandler classes;
@@ -315,9 +288,6 @@ public:
 	};
 	std::vector<SBallisticsLevelInfo> ballistics; //info about ballistics ability per level; [0] - none; [1] - basic; [2] - adv; [3] - expert
 
-	std::vector<CObstacleInfo> obstacles; //info about obstacles that may be placed on battlefield
-	std::vector<CObstacleInfo> absoluteObstacles; //info about obstacles that may be placed on battlefield
-
 	ui32 level(ui64 experience) const; //calculates level corresponding to given experience amount
 	ui64 reqExp(ui32 level) const; //calculates experience required for given level
 
@@ -340,8 +310,6 @@ public:
 		h & expPerLevel;
 		h & ballistics;
 		h & terrCosts;
-		h & obstacles;
-		h & absoluteObstacles;
 	}
 
 protected:

+ 2 - 0
lib/CMakeLists.txt

@@ -179,6 +179,7 @@ set(lib_SRCS
 		JsonNode.cpp
 		LogicalExpression.cpp
 		NetPacksLib.cpp
+		ObstacleHandler.cpp
 		StartInfo.cpp
 		ResourceSet.cpp
 		ScriptHandler.cpp
@@ -418,6 +419,7 @@ set(lib_HEADERS
 		NetPacksBase.h
 		NetPacks.h
 		NetPacksLobby.h
+		ObstacleHandler.h
 		PathfinderUtil.h
 		ResourceSet.h
 		ScriptHandler.h

+ 2 - 0
lib/CModHandler.cpp

@@ -26,6 +26,7 @@
 #include "CSkillHandler.h"
 #include "ScriptHandler.h"
 #include "BattleFieldHandler.h"
+#include "ObstacleHandler.h"
 
 #include <vstd/StringUtils.h>
 
@@ -435,6 +436,7 @@ void CContentHandler::init()
 	handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template")));
 	handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script")));
 	handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield")));
+	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle")));
 	//TODO: any other types of moddables?
 }
 

+ 22 - 1
lib/GameConstants.cpp

@@ -35,6 +35,7 @@
 #include "CGeneralTextHandler.h"
 #include "CModHandler.h"//todo: remove
 #include "BattleFieldHandler.h"
+#include "ObstacleHandler.h"
 
 const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
 const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3);
@@ -292,4 +293,24 @@ BattleField BattleField::fromString(std::string identifier)
 		return BattleField(rawId.get());
 	else
 		return BattleField::NONE;
-}
+}
+		
+const ObstacleInfo * Obstacle::getInfo() const
+{
+	return VLC->obstacles()->getById(*this);
+}
+
+Obstacle::operator std::string() const
+{
+	return getInfo()->identifier;
+}
+
+Obstacle Obstacle::fromString(std::string identifier)
+{
+	auto rawId = VLC->modh->identifiers.getIdentifier("core", "obstacle", identifier);
+
+	if(rawId)
+		return Obstacle(rawId.get());
+	else
+		return Obstacle(-1);
+}

+ 10 - 0
lib/GameConstants.h

@@ -1125,6 +1125,16 @@ class BattleField : public BaseForID<BattleField, si32>
 
 	DLL_LINKAGE static BattleField fromString(std::string identifier);
 };
+	
+class ObstacleInfo;
+class Obstacle : public BaseForID<Obstacle, si32>
+{
+	INSTID_LIKE_CLASS_COMMON(Obstacle, si32)
+	
+	DLL_LINKAGE const ObstacleInfo * getInfo() const;
+	DLL_LINKAGE operator std::string() const;
+	DLL_LINKAGE static Obstacle fromString(std::string identifier);
+};
 
 enum class ESpellSchool: ui8
 {

+ 1 - 0
lib/IGameCallback.cpp

@@ -17,6 +17,7 @@
 #include "CBonusTypeHandler.h"
 #include "CModHandler.h"
 #include "BattleFieldHandler.h"
+#include "ObstacleHandler.h"
 
 #include "serializer/CSerializer.h" // for SAVEGAME_MAGIC
 #include "serializer/BinaryDeserializer.h"

+ 112 - 0
lib/ObstacleHandler.cpp

@@ -0,0 +1,112 @@
+/*
+ * ObstacleHandler.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "ObstacleHandler.h"
+#include "BattleFieldHandler.h"
+
+int32_t ObstacleInfo::getIndex() const
+{
+	return obstacle.getNum();
+}
+
+int32_t ObstacleInfo::getIconIndex() const
+{
+	return iconIndex;
+}
+
+const std::string & ObstacleInfo::getName() const
+{
+	return identifier;
+}
+
+const std::string & ObstacleInfo::getJsonKey() const
+{
+	return identifier;
+}
+
+void ObstacleInfo::registerIcons(const IconRegistar & cb) const
+{
+}
+
+Obstacle ObstacleInfo::getId() const
+{
+	return obstacle;
+}
+
+
+std::vector<BattleHex> ObstacleInfo::getBlocked(BattleHex hex) const
+{
+	std::vector<BattleHex> ret;
+	if(isAbsoluteObstacle)
+	{
+		assert(!hex.isValid());
+		range::copy(blockedTiles, std::back_inserter(ret));
+		return ret;
+	}
+	
+	for(int offset : blockedTiles)
+	{
+		BattleHex toBlock = hex + offset;
+		if((hex.getY() & 1) && !(toBlock.getY() & 1))
+			toBlock += BattleHex::LEFT;
+		
+		if(!toBlock.isValid())
+			logGlobal->error("Misplaced obstacle!");
+		else
+			ret.push_back(toBlock);
+	}
+	
+	return ret;
+}
+
+bool ObstacleInfo::isAppropriate(const Terrain & terrainType, const BattleField & battlefield) const
+{
+	auto bgInfo = battlefield.getInfo();
+	
+	if(bgInfo->isSpecial)
+		return vstd::contains(allowedSpecialBfields, bgInfo->identifier);
+	
+	return vstd::contains(allowedTerrains, terrainType);
+}
+
+ObstacleInfo * ObstacleHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index)
+{
+	auto * info = new ObstacleInfo(Obstacle(index), identifier);
+	
+	info->animation = json["animation"].String();
+	info->width = json["width"].Integer();
+	info->height = json["height"].Integer();
+	for(auto & t : json["allowedTerrain"].Vector())
+		info->allowedTerrains.emplace_back(t.String());
+	for(auto & t : json["specialBattlefields"].Vector())
+		info->allowedSpecialBfields.emplace_back(t.String());
+	info->blockedTiles = json["blockedTiles"].convertTo<std::vector<si16>>();
+	info->isAbsoluteObstacle = json["absolute"].Bool();
+	
+	objects.push_back(info);
+	
+	return info;
+}
+
+std::vector<JsonNode> ObstacleHandler::loadLegacyData(size_t dataSize)
+{
+	return {};
+}
+
+std::vector<bool> ObstacleHandler::getDefaultAllowed() const
+{
+	return {};
+}
+
+const std::vector<std::string> & ObstacleHandler::getTypeNames() const
+{
+	static const std::vector<std::string> types = { "obstacle" };
+	return types;
+}

+ 94 - 0
lib/ObstacleHandler.h

@@ -0,0 +1,94 @@
+/*
+ * ObstacleHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include <vcmi/EntityService.h>
+#include <vcmi/Entity.h>
+#include "GameConstants.h"
+#include "IHandlerBase.h"
+#include "Terrain.h"
+#include "battle/BattleHex.h"
+
+class DLL_LINKAGE ObstacleInfo : public EntityT<Obstacle>
+{
+public:
+	ObstacleInfo(): obstacle(-1), width(0), height(0), isAbsoluteObstacle(false), iconIndex(0)
+	{}
+	
+	ObstacleInfo(Obstacle obstacle, std::string identifier)
+	: obstacle(obstacle), identifier(identifier), iconIndex(obstacle.getNum()), width(0), height(0), isAbsoluteObstacle(false)
+	{
+	}
+	
+	Obstacle obstacle;
+	si32 iconIndex;
+	std::string identifier;
+	std::string appearAnimation, animation, dissapearAnimation;
+	std::vector<Terrain> allowedTerrains;
+	std::vector<std::string> allowedSpecialBfields;
+	
+	//TODO: here is extra field to implement it's logic in the future but save backward compatibility
+	int obstacleType = -1;
+	
+	ui8 isAbsoluteObstacle; //there may only one such obstacle in battle and its position is always the same
+	si32 width, height; //how much space to the right and up is needed to place obstacle (affects only placement algorithm)
+	std::vector<si16> blockedTiles; //offsets relative to obstacle position (that is its left bottom corner)
+	
+	int32_t getIndex() const override;
+	int32_t getIconIndex() const override;
+	const std::string & getJsonKey() const override;
+	const std::string & getName() const override;
+	void registerIcons(const IconRegistar & cb) const override;
+	Obstacle getId() const override;
+	
+	std::vector<BattleHex> getBlocked(BattleHex hex) const; //returns vector of hexes blocked by obstacle when it's placed on hex 'hex'
+	
+	bool isAppropriate(const Terrain & terrainType, const BattleField & specialBattlefield) const;
+	
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & obstacle;
+		h & obstacleType;
+		h & iconIndex;
+		h & identifier;
+		h & animation;
+		h & appearAnimation;
+		h & dissapearAnimation;
+		h & allowedTerrains;
+		h & allowedSpecialBfields;
+		h & isAbsoluteObstacle;
+		h & width;
+		h & height;
+		h & blockedTiles;
+	}
+};
+
+class DLL_LINKAGE ObstacleService : public EntityServiceT<Obstacle, ObstacleInfo>
+{
+public:
+};
+
+class ObstacleHandler: public CHandlerBase<Obstacle, ObstacleInfo, ObstacleInfo, ObstacleService>
+{
+public:	
+	ObstacleInfo * loadFromJson(const std::string & scope,
+										const JsonNode & json,
+										const std::string & identifier,
+										size_t index) override;
+	
+	const std::vector<std::string> & getTypeNames() const override;
+	std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
+	std::vector<bool> getDefaultAllowed() const override;
+	
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & objects;
+	}
+};

+ 8 - 0
lib/VCMI_Lib.cpp

@@ -33,6 +33,7 @@
 #include "mapping/CMapEditManager.h"
 #include "ScriptHandler.h"
 #include "BattleFieldHandler.h"
+#include "ObstacleHandler.h"
 
 LibClasses * VLC = nullptr;
 
@@ -116,6 +117,11 @@ const BattleFieldService * LibClasses::battlefields() const
 	return battlefieldsHandler;
 }
 
+const ObstacleService * LibClasses::obstacles() const
+{
+	return obstacleHandler;
+}
+
 void LibClasses::updateEntity(Metatype metatype, int32_t index, const JsonNode & data)
 {
 	switch(metatype)
@@ -212,6 +218,8 @@ void LibClasses::init(bool onlyEssential)
 	createHandler(scriptHandler, "Script", pomtime);
 
 	createHandler(battlefieldsHandler, "Battlefields", pomtime);
+	
+	createHandler(obstacleHandler, "Obstacles", pomtime);
 
 	logGlobal->info("\tInitializing handlers: %d ms", totalTime.getDiff());
 

+ 4 - 0
lib/VCMI_Lib.h

@@ -27,6 +27,7 @@ class CContentHandler;
 class BattleFieldHandler;
 class IBonusTypeHandler;
 class CBonusTypeHandler;
+class ObstacleHandler;
 class CTerrainViewPatternConfig;
 class CRmgTemplateStorage;
 class IHandlerBase;
@@ -58,6 +59,7 @@ public:
 	const spells::Service * spells() const override;
 	const SkillService * skills() const override;
 	const BattleFieldService * battlefields() const override;
+	const ObstacleService * obstacles() const override;
 
 	void updateEntity(Metatype metatype, int32_t index, const JsonNode & data) override;
 
@@ -79,6 +81,7 @@ public:
 	CTerrainViewPatternConfig * terviewh;
 	CRmgTemplateStorage * tplh;
 	BattleFieldHandler * battlefieldsHandler;
+	ObstacleHandler * obstacleHandler;
 	scripting::ScriptHandler * scriptHandler;
 
 	LibClasses(); //c-tor, loads .lods and NULLs handlers
@@ -108,6 +111,7 @@ public:
 		h & spellh;
 		h & skillh;
 		h & battlefieldsHandler;
+		h & obstacleHandler;
 
 		if(!h.saving)
 		{

+ 10 - 11
lib/battle/BattleInfo.cpp

@@ -17,6 +17,7 @@
 #include "../CGeneralTextHandler.h"
 #include "../Terrain.h"
 #include "../BattleFieldHandler.h"
+#include "../ObstacleHandler.h"
 
 //TODO: remove
 #include "../IGameCallback.h"
@@ -240,9 +241,6 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, const Terrain & terrain,
 	//randomize obstacles
  	if (town == nullptr && !creatureBank) //do it only when it's not siege and not creature bank
  	{
-		const int ABSOLUTE_OBSTACLES_COUNT = VLC->heroh->absoluteObstacles.size();
-		const int USUAL_OBSTACLES_COUNT = VLC->heroh->obstacles.size(); //shouldn't be changes if we want H3-like obstacle placement
-
 		RandGen r;
 		auto ourRand = [&](){ return r.rand(); };
 		r.srand(tile);
@@ -253,17 +251,19 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, const Terrain & terrain,
 
 		auto appropriateAbsoluteObstacle = [&](int id)
 		{
-			return VLC->heroh->absoluteObstacles[id].isAppropriate(curB->terrainType, battlefieldType);
+			auto * info = Obstacle(id).getInfo();
+			return info && info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
 		};
-		auto appropriateUsualObstacle = [&](int id) -> bool
+		auto appropriateUsualObstacle = [&](int id)
 		{
-			return VLC->heroh->obstacles[id].isAppropriate(curB->terrainType, battlefieldType);
+			auto * info = Obstacle(id).getInfo();
+			return info && !info->isAbsoluteObstacle && info->isAppropriate(curB->terrainType, battlefieldType);
 		};
 
+		RangeGenerator obidgen(0, VLC->obstacleHandler->objects.size() - 1, ourRand);
+		
 		if(r.rand(1,100) <= 40) //put cliff-like obstacle
 		{
-			RangeGenerator obidgen(0, ABSOLUTE_OBSTACLES_COUNT-1, ourRand);
-
 			try
 			{
 				auto obstPtr = std::make_shared<CObstacleInstance>();
@@ -274,7 +274,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, const Terrain & terrain,
 
 				for(BattleHex blocked : obstPtr->getBlockedTiles())
 					blockedTiles.push_back(blocked);
-				tilesToBlock -= (int)VLC->heroh->absoluteObstacles[obstPtr->ID].blockedTiles.size() / 2;
+				tilesToBlock -= Obstacle(obstPtr->ID).getInfo()->blockedTiles.size() / 2;
 			}
 			catch(RangeGenerator::ExhaustedPossibilities &)
 			{
@@ -283,14 +283,13 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, const Terrain & terrain,
 			}
 		}
 
-		RangeGenerator obidgen(0, USUAL_OBSTACLES_COUNT-1, ourRand);
 		try
 		{
 			while(tilesToBlock > 0)
 			{
 				auto tileAccessibility = curB->getAccesibility();
 				const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
-				const CObstacleInfo &obi = VLC->heroh->obstacles[obid];
+				const ObstacleInfo &obi = *Obstacle(obid).getInfo();
 
 				auto validPosition = [&](BattleHex pos) -> bool
 				{

+ 3 - 10
lib/battle/CObstacleInstance.cpp

@@ -11,6 +11,7 @@
 #include "CObstacleInstance.h"
 #include "../CHeroHandler.h"
 #include "../CTownHandler.h"
+#include "../ObstacleHandler.h"
 #include "../VCMI_Lib.h"
 #include "../NetPacksBase.h"
 
@@ -29,17 +30,9 @@ CObstacleInstance::~CObstacleInstance()
 
 }
 
-const CObstacleInfo & CObstacleInstance::getInfo() const
+const ObstacleInfo & CObstacleInstance::getInfo() const
 {
-	switch(obstacleType)
-	{
-	case ABSOLUTE_OBSTACLE:
-		return VLC->heroh->absoluteObstacles[ID];
-	case USUAL:
-		return VLC->heroh->obstacles[ID];
-	default:
-		throw std::runtime_error("Unknown obstacle type in CObstacleInstance::getInfo()");
-	}
+	return *Obstacle(ID).getInfo();
 }
 
 std::vector<BattleHex> CObstacleInstance::getBlockedTiles() const

+ 2 - 2
lib/battle/CObstacleInstance.h

@@ -10,7 +10,7 @@
 #pragma once
 #include "BattleHex.h"
 
-struct CObstacleInfo;
+class ObstacleInfo;
 class ObstacleChanges;
 class JsonSerializeFormat;
 
@@ -30,7 +30,7 @@ struct DLL_LINKAGE CObstacleInstance
 	CObstacleInstance();
 	virtual ~CObstacleInstance();
 
-	const CObstacleInfo &getInfo() const; //allowed only when not generated by spell (usual or absolute)
+	const ObstacleInfo &getInfo() const; //allowed only when not generated by spell (usual or absolute)
 
 	std::vector<BattleHex> getBlockedTiles() const;
 	std::vector<BattleHex> getStoppingTile() const; //hexes that will stop stack move

+ 1 - 0
test/mock/mock_Services.h

@@ -25,6 +25,7 @@ public:
 	MOCK_CONST_METHOD0(spells, const spells::Service *());
 	MOCK_CONST_METHOD0(skills, const SkillService * ());
 	MOCK_CONST_METHOD0(battlefields, const BattleFieldService *());
+	MOCK_CONST_METHOD0(obstacles, const ObstacleService *());
 
 	MOCK_METHOD3(updateEntity, void(Metatype, int32_t, const JsonNode &));