Browse Source

Rewritten battle obstacles. New file for lib: CObstacleInstance.cpp.
Now obstacles should be placed exactly like they were in OH3.
All problems with displaying obstacles in battlefield should be gone. They should be now matched to the single pixel.
If there are still some discrepancies, please report them.

Michał W. Urbańczyk 13 years ago
parent
commit
7dc0d6878e

+ 45 - 13
client/BattleInterface/CBattleInterface.cpp

@@ -270,10 +270,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	for(int h = 0; h < bfield.size(); ++h)
 	{
 		bfield[h].myNumber = h;
-
-		int x = 14 + ((h/GameConstants::BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(h%GameConstants::BFIELD_WIDTH);
-		int y = 86 + 42 * (h/GameConstants::BFIELD_WIDTH);
-		bfield[h].pos = genRect(cellShade->h, cellShade->w, x + pos.x, y + pos.y);
+		bfield[h].pos = hexPosition(h);
 		bfield[h].accessible = true;
 		bfield[h].myInterface = this;
 	}
@@ -341,10 +338,20 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 	std::vector<CObstacleInstance> obst = curInt->cb->battleGetAllObstacles();
 	for(size_t t = 0; t < obst.size(); ++t)
 	{
-		idToObstacle[obst[t].ID] = CDefHandler::giveDef(CGI->heroh->obstacles.find(obst[t].ID)->second.defName);
-		for(size_t n = 0; n < idToObstacle[obst[t].ID]->ourImages.size(); ++n)
+		int ID = obst[t].ID;
+		std::string gfxName = obst[t].getInfo().defName;
+
+		if(!obst[t].isAbsoluteObstacle)
 		{
-			SDL_SetColorKey(idToObstacle[obst[t].ID]->ourImages[n].bitmap, SDL_SRCCOLORKEY, SDL_MapRGB(idToObstacle[obst[t].ID]->ourImages[n].bitmap->format,0,255,255));
+			idToObstacle[ID] = CDefHandler::giveDef(gfxName);
+			for(size_t n = 0; n < idToObstacle[ID]->ourImages.size(); ++n)
+			{
+				SDL_SetColorKey(idToObstacle[ID]->ourImages[n].bitmap, SDL_SRCCOLORKEY, SDL_MapRGB(idToObstacle[ID]->ourImages[n].bitmap->format,0,255,255));
+			}
+		}
+		else
+		{
+			idToAbsoluteObstacle[ID] = BitmapHandler::loadBitmap(gfxName);
 		}
 	}
 
@@ -625,10 +632,15 @@ void CBattleInterface::show(SDL_Surface * to)
 	//preparing obstacles to be shown
 	std::vector<CObstacleInstance> obstacles = curInt->cb->battleGetAllObstacles();
 	std::multimap<BattleHex, int> hexToObstacle;
+
 	for(size_t b = 0; b < obstacles.size(); ++b)
 	{
-		BattleHex position = CGI->heroh->obstacles.find(obstacles[b].ID)->second.getMaxBlocked(obstacles[b].pos);
-		hexToObstacle.insert(std::make_pair(position, b));
+		const CObstacleInstance &oi = obstacles[b];
+		if(!oi.isAbsoluteObstacle)
+		{
+			//BattleHex position = CGI->heroh->obstacles.find(obstacles[b].ID)->second.getMaxBlocked(obstacles[b].pos);
+			hexToObstacle.insert(std::make_pair(oi.pos, b));
+		}
 	}
 
 	////showing units //a lot of work...
@@ -892,11 +904,15 @@ void CBattleInterface::showObstacles(std::multimap<BattleHex, int> *hexToObstacl
 	for(std::multimap<BattleHex, int>::const_iterator it = obstRange.first; it != obstRange.second; ++it)
 	{
 		CObstacleInstance & curOb = obstacles[it->second];
-		std::pair<si16, si16> shift = CGI->heroh->obstacles.find(curOb.ID)->second.posShift;
-		int x = ((curOb.pos/GameConstants::BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(curOb.pos%GameConstants::BFIELD_WIDTH) + pos.x + shift.first;
-		int y = 86 + 42 * (curOb.pos/GameConstants::BFIELD_WIDTH) + pos.y + shift.second;
 		std::vector<Cimage> &images = idToObstacle[curOb.ID]->ourImages; //reference to animation of obstacle
-		blitAt(images[((animCount+1)/(4/getAnimSpeed()))%images.size()].bitmap, x, y, to);
+		Rect r = hexPosition(hex);
+		int offset = images.front().bitmap->h % 42;
+		if(offset > 15) //experimental value, may need tweaking if some obstacles are shown too low/high
+			offset -= 42;
+
+		r.y += 42 - images.front().bitmap->h + offset;
+		//r.y -= cellShade->h*CGI->heroh->obstacles.find(curOb.ID)->second.height - images.front().bitmap->h;
+		blitAt(images[((animCount+1)/(4/getAnimSpeed()))%images.size()].bitmap, r.x, r.y, to);
 	}
 }
 
@@ -2251,6 +2267,12 @@ void CBattleInterface::redrawBackgroundWithHexes(const CStack * activeStack)
 	curInt->cb->battleGetStackCountOutsideHexes(stackCountOutsideHexes);
 	//preparating background graphic with hexes and shaded hexes
 	blitAt(background, 0, 0, backgroundWithHexes);
+
+	//draw absolute obstacles (cliffs and so on)
+	BOOST_FOREACH(const CObstacleInstance &oi, curInt->cb->battleGetAllObstacles())
+		if(oi.isAbsoluteObstacle)
+			blitAt(idToAbsoluteObstacle[oi.ID], oi.getInfo().width, oi.getInfo().height, backgroundWithHexes);
+
 	if(settings["battle"]["cellBorders"].Bool())
 		CSDL_Ext::blit8bppAlphaTo24bpp(cellBorders, NULL, backgroundWithHexes, NULL);
 
@@ -3182,6 +3204,16 @@ BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)
 	}
 	return -1;
 }
+
+Rect CBattleInterface::hexPosition(BattleHex hex) const
+{
+	int x = 14 + ((hex.getY())%2==0 ? 22 : 0) + 44*hex.getX() + pos.x;
+	int y = 86 + 42 * hex.getY() + pos.y;
+	int w = cellShade->w;
+	int h = cellShade->h;
+	return Rect(x, y, w, h);
+}
+
 std::string CBattleInterface::SiegeHelper::townTypeInfixes[GameConstants::F_NUMBER] = {"CS", "RM", "TW", "IN", "NC", "DN", "ST", "FR", "EL"};
 
 CBattleInterface::SiegeHelper::SiegeHelper(const CGTownInstance *siegeTown, const CBattleInterface * _owner)

+ 2 - 0
client/BattleInterface/CBattleInterface.h

@@ -111,6 +111,7 @@ private:
 	std::map< int, CCreatureAnimation * > creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
 	std::map< int, CDefHandler * > idToProjectile; //projectiles of creatures (creatureID, defhandler)
 	std::map< int, CDefHandler * > idToObstacle; //obstacles located on the battlefield
+	std::map< int, SDL_Surface * > idToAbsoluteObstacle; //obstacles located on the battlefield
 	std::map< int, bool > creDir; // <creatureID, if false reverse creature's animation>
 	ui8 animCount;
 	const CStack * activeStack; //number of active stack; NULL - no one
@@ -261,6 +262,7 @@ public:
 	void hideQueue();
 	void showQueue();
 	PossibleActions selectionTypeByPositiveness(const CSpell & spell);
+	Rect hexPosition(BattleHex hex) const;
 
 	void handleHex(BattleHex myNumber, int eventType);
 	bool isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber);

+ 1235 - 113
config/obstacles.json

@@ -1,113 +1,1235 @@
-{
-	// Battle obstacles
-	// id:
-	// defname: graphics
-	// blockmap: X - blocked, N - not blocked, L - description goes to the next line, starting with the left bottom hex
-	// terrains: bitmap of allowed terrains - 
-	// 			 		1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   
-	//					7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean
-	//					13. swamp/trees  14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools
-	//					18. holy ground   19. clover field  20. evil fog   21. "favourable winds" text on magic plains background
-	// 					22. cursed ground   23. rough  24. ship to ship   25. ship
-	// shift_x, shift_y: shift of obstacle's position in the battlefield <x shift, y shift>,
-	// 					eg. if it's <-1, 2> obstacle will be printed one pixel to the left and two to the bottom]
-
-	"obstacles": [
-		{ "id": 5, "defname": "OBBDT01.DEF", "blockmap": "LNXXX", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 56, "defname": "OBBHS02.DEF", "blockmap": "XXLNX", "terrains": "1100000000000000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 57, "defname": "OBBHS03.DEF", "blockmap": "LXXX", "terrains": "1111100000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 91, "defname": "OBBHS04.DEF", "blockmap": "XXLNXX", "terrains": "1100000000000000000000000", "shift_x": -40, "shift_y": -20 },
-		{ "id": 58, "defname": "OBBHS11A.DEF", "blockmap": "XXXLNXXX", "terrains": "1100000000000000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 59, "defname": "OBBHS12B.DEF", "blockmap": "NXXLNXX", "terrains": "1100000000000000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 60, "defname": "OBBHS14B.DEF", "blockmap": "NXLNXX", "terrains": "1111100000000000000000100", "shift_x": 36, "shift_y": 0 },
-		{ "id": 92, "defname": "OBBHS16A.DEF", "blockmap": "XLNXX", "terrains": "1100000000000000000000000", "shift_x": -8, "shift_y": -28 },
-		{ "id": 55, "defname": "OBBTS04.DEF", "blockmap": "XLXX", "terrains": "0000000000000000000000011", "shift_x": -30, "shift_y": -1 },
-		{ "id": 71, "defname": "OBCFS00.DEF", "blockmap": "X", "terrains": "0000000000000000001000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 72, "defname": "OBCFS01.DEF", "blockmap": "XX", "terrains": "0000000000000000001000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 73, "defname": "OBCFS02.DEF", "blockmap": "XXLNXX", "terrains": "0000000000000000001000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 74, "defname": "OBCFS03.DEF", "blockmap": "NXXLNXX", "terrains": "0000000000000000001000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 0, "defname": "OBDINO1.DEF", "blockmap": "XX", "terrains": "1111110000000000000000100", "shift_x": 14, "shift_y": 0 },
-		{ "id": 2, "defname": "OBDINO2.DEF", "blockmap": "LXXX", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 1, "defname": "OBDINO3.DEF", "blockmap": "XXXLXX", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 6, "defname": "OBDRK01.DEF", "blockmap": "XX", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 7, "defname": "OBDRK02.DEF", "blockmap": "LXX", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 8, "defname": "OBDRK03.DEF", "blockmap": "X", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": -8 },
-		{ "id": 9, "defname": "OBDRK04.DEF", "blockmap": "LXX", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 10, "defname": "OBDSH01.DEF", "blockmap": "LXX", "terrains": "1111110000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 93, "defname": "OBDSM01.DEF", "blockmap": "NNXXXLNNXXXLNNXX", "terrains": "1100000000000000000000000", "shift_x": 36, "shift_y": 0 },
-		{ "id": 17, "defname": "OBDSM02.DEF", "blockmap": "XXLNX", "terrains": "1111100000000000000000100", "shift_x": -9, "shift_y": -2 },
-		{ "id": 18, "defname": "OBDSS17.DEF", "blockmap": "XXLNXXX", "terrains": "1111100000000000000000100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 11, "defname": "OBDTF03.DEF", "blockmap": "XX", "terrains": "1111100000000000000001100", "shift_x": 14, "shift_y": 0 },
-		{ "id": 12, "defname": "OBDTS03.DEF", "blockmap": "LLNXXXX", "terrains": "1111100000000000000001100", "shift_x": 14, "shift_y": 0 },
-		{ "id": 13, "defname": "OBDTS04.DEF", "blockmap": "LNXX", "terrains": "1111100000000000000001100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 14, "defname": "OBDTS14.DEF", "blockmap": "XXLNNX", "terrains": "1111100000000000000001100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 15, "defname": "OBDTS15.DEF", "blockmap": "XLXLNX", "terrains": "1111100000000000000001100", "shift_x": -30, "shift_y": 0 },
-		{ "id": 66, "defname": "OBEFS00.DEF", "blockmap": "X", "terrains": "0000000000000000000100000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 67, "defname": "OBEFS01.DEF", "blockmap": "XX", "terrains": "0000000000000000000100000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 68, "defname": "OBEFS02.DEF", "blockmap": "NXLXXX", "terrains": "0000000000000000000100000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 69, "defname": "OBEFS03.DEF", "blockmap": "LNXX", "terrains": "0000000000000000000100000", "shift_x": 0, "shift_y": 0 },
-		{ "id": 70, "defname": "OBEFS04.DEF", "blockmap": "NNXXXLNXXX", "terrains": "0000000000000000000100000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 79, "defname": "OBFFS00.DEF", "blockmap": "X", "terrains": "0000000000000100000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 80, "defname": "OBFFS01.DEF", "blockmap": "XX", "terrains": "0000000000000100000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 81, "defname": "OBFFS02.DEF", "blockmap": "NXLXXX", "terrains": "0000000000000100000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 82, "defname": "OBFFS03.DEF", "blockmap": "XXLNXXX", "terrains": "0000000000000100000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 83, "defname": "OBFFS04.DEF", "blockmap": "XXXLXXXX", "terrains": "0000000000000100000000000", "shift_x": -8, "shift_y": -42 },
-		{ "id": 19, "defname": "OBGLG01.DEF", "blockmap": "XX", "terrains": "0000000000001000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 94, "defname": "OBGRK01.DEF", "blockmap": "XX", "terrains": "0000000000001000000000000", "shift_x": 14, "shift_y": -42 },
-		{ "id": 23, "defname": "OBGRK02.DEF", "blockmap": "XX", "terrains": "0000000000001000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 20, "defname": "OBGRS02.DEF", "blockmap": "NXX", "terrains": "0000000000001000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 22, "defname": "OBGRS01.DEF", "blockmap": "XXXXLNXXXX", "terrains": "0000011010001000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 35, "defname": "OBGRS03.DEF", "blockmap": "XXXXXXX", "terrains": "1111111010000000000000100", "shift_x": 0, "shift_y": 0 },
-		{ "id": 21, "defname": "OBGST01.DEF", "blockmap": "X", "terrains": "1111111010000000000000100", "shift_x": 14, "shift_y": 0 },
-		{ "id": 61, "defname": "OBHGS00.DEF", "blockmap": "X", "terrains": "0000000000000000010000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 62, "defname": "OBHGS01.DEF", "blockmap": "XX", "terrains": "0000000000000000010000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 63, "defname": "OBHGS02.DEF", "blockmap": "X", "terrains": "0000000000000000010000000", "shift_x": -30, "shift_y": -84 },
-		{ "id": 64, "defname": "OBHGS03.DEF", "blockmap": "XXX", "terrains": "0000000000000000010000000", "shift_x": 14, "shift_y": -42 },
-		{ "id": 65, "defname": "OBHGS04.DEF", "blockmap": "XXXX", "terrains": "0000000000000000010000000", "shift_x": 14, "shift_y": -84 },
-		{ "id": 75, "defname": "OBLPS00.DEF", "blockmap": "X", "terrains": "0000000000000000100000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 76, "defname": "OBLPS01.DEF", "blockmap": "XX", "terrains": "0000000000000000100000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 77, "defname": "OBLPS02.DEF", "blockmap": "NXXLNX", "terrains": "0000000000000000100000000", "shift_x": 36, "shift_y": 0 },
-		{ "id": 78, "defname": "OBLPS03.DEF", "blockmap": "XXXXLNXXXX", "terrains": "0000000000000000100000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 48, "defname": "OBLVS01.DEF", "blockmap": "XXLNNX", "terrains": "0000000100000000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 49, "defname": "OBLVS02.DEF", "blockmap": "XXXLXXX", "terrains": "0000000100000000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 50, "defname": "OBLVS03.DEF", "blockmap": "XXXXLNXXX", "terrains": "0000000100000000000000000", "shift_x": -30, "shift_y": 0 },
-		{ "id": 51, "defname": "OBLVS04.DEF", "blockmap": "XXX", "terrains": "0000000100000000000000000", "shift_x": 14, "shift_y": -42 },
-		{ "id": 52, "defname": "OBLVS09.DEF", "blockmap": "XXLNXXLNNXX", "terrains": "0000000100000000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 53, "defname": "OBLVS17.DEF", "blockmap": "NNXLXXXX", "terrains": "0000000100000000000000000", "shift_x": -30, "shift_y": 0 },
-		{ "id": 54, "defname": "OBLVS22.DEF", "blockmap": "XXXLXXXX", "terrains": "0000000100000000000000000", "shift_x": -30, "shift_y": 0 },
-		{ "id": 88, "defname": "OBMCS00.DEF", "blockmap": "X", "terrains": "0000000000000001000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 89, "defname": "OBMCS01.DEF", "blockmap": "XLNX", "terrains": "0000000000000001000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 90, "defname": "OBMCS02.DEF", "blockmap": "NXXLXX", "terrains": "0000000000000001000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 41, "defname": "OBRGS01.DEF", "blockmap": "XLXX", "terrains": "1111100000000000000001100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 40, "defname": "OBRGS02.DEF", "blockmap": "NXXLNXXX", "terrains": "1111100000000000000001100", "shift_x": 14, "shift_y": 0 },
-		{ "id": 42, "defname": "OBRGS03.DEF", "blockmap": "XXLNX", "terrains": "1111100000000000000001100", "shift_x": -8, "shift_y": 0 },
-		{ "id": 43, "defname": "OBRGS04.DEF", "blockmap": "XXLX", "terrains": "1111100000000000000001100", "shift_x": -30, "shift_y": 0 },
-		{ "id": 44, "defname": "OBRGS05.DEF", "blockmap": "NXLXX", "terrains": "1111100000000000000001100", "shift_x": -30, "shift_y": 0 },
-		{ "id": 16, "defname": "OBRGS06.DEF", "blockmap": "NXXXXXXLNNNNNNNXLNNNNNNNNX", "terrains": "1111100000000000000001100", "shift_x": -30, "shift_y": -30 },
-		{ "id": 84, "defname": "OBRLS00.DEF", "blockmap": "X", "terrains": "0000000000000010000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 85, "defname": "OBRLS01.DEF", "blockmap": "XX", "terrains": "0000000000000010000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 86, "defname": "OBRLS02.DEF", "blockmap": "XXX", "terrains": "0000000000000010000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 87, "defname": "OBRLS03.DEF", "blockmap": "XLNXX", "terrains": "0000000000000010000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 4, "defname": "OBSKEL1.DEF", "blockmap": "XX", "terrains": "1111110000000000000001100", "shift_x": 14, "shift_y": 0 },
-		{ "id": 3, "defname": "OBSKEL2.DEF", "blockmap": "XX", "terrains": "1111110000000000000001100", "shift_x": 14, "shift_y": 0 },
-		{ "id": 24, "defname": "OBSNS01.DEF", "blockmap": "XXX", "terrains": "0000000001100000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 25, "defname": "OBSNS02.DEF", "blockmap": "NXXXX", "terrains": "0000000001100000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 26, "defname": "OBSNS03.DEF", "blockmap": "XLXLX", "terrains": "0000000001100000000000000", "shift_x": -30, "shift_y": 0 },
-		{ "id": 27, "defname": "OBSNS04.DEF", "blockmap": "XXX", "terrains": "0000000001100000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 28, "defname": "OBSNS05.DEF", "blockmap": "X", "terrains": "0000000001100000000000000", "shift_x": -30, "shift_y": 0 },
-		{ "id": 29, "defname": "OBSNS06.DEF", "blockmap": "LNXX", "terrains": "0000000001100000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 30, "defname": "OBSNS07.DEF", "blockmap": "XX", "terrains": "0000000001100000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 31, "defname": "OBSNS08.DEF", "blockmap": "LNXXX", "terrains": "0000000001100000000000000", "shift_x": 36, "shift_y": 0 },
-		{ "id": 32, "defname": "OBSNS09.DEF", "blockmap": "XXXXLNNXXXX", "terrains": "0000000001100000000000000", "shift_x": -8, "shift_y": 0 },
-		{ "id": 33, "defname": "OBSNS10.DEF", "blockmap": "XXLNXLNXXLNNXXX", "terrains": "0000000001100000000000000", "shift_x": -30, "shift_y": -30 },
-		{ "id": 45, "defname": "OBSUS01.DEF", "blockmap": "XXLXXX", "terrains": "0000000000010000000000000", "shift_x": -8, "shift_y": -42 },
-		{ "id": 46, "defname": "OBSUS02.DEF", "blockmap": "XXX", "terrains": "0000000000010000000000000", "shift_x": 14, "shift_y": -42 },
-		{ "id": 47, "defname": "OBSUS11B.DEF", "blockmap": "XXXLXXXX", "terrains": "0000000000010000000000000", "shift_x": -9, "shift_y": -43 },
-		{ "id": 34, "defname": "OBSWS01.DEF", "blockmap": "LNX", "terrains": "0000000000001000000000000", "shift_x": 36, "shift_y": 0 },
-		{ "id": 95, "defname": "OBSWS02.DEF", "blockmap": "XXXXXXX", "terrains": "0000000000001000000000000", "shift_x": -8, "shift_y": -42 },
-		{ "id": 36, "defname": "OBSWS03.DEF", "blockmap": "XX", "terrains": "0000000000001000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 37, "defname": "OBSWS04.DEF", "blockmap": "XXX", "terrains": "0000000000001000000000000", "shift_x": 14, "shift_y": 0 },
-		{ "id": 38, "defname": "OBSWS11B.DEF", "blockmap": "XXXXLXXXX", "terrains": "0000000000001000000000000", "shift_x": -30, "shift_y": -40 },
-		{ "id": 39, "defname": "OBSWS13A.DEF", "blockmap": "XXXXLXX", "terrains": "0000000000001000000000000", "shift_x": 14, "shift_y": 0 }
-	]
-}
+// Defines battle obstacles. We have two vectors of them:
+// * "obstacles" are usual obtacles, that are randomly placed in the battlefield.
+// * "absoluteObstacles" are a little special: there can be only one such obstacle in the battlefield and its position is always the same.
+//
+// Their properties:
+// * "allowedTerrain" vector of terrain types (TT format) where obstacle is appropriate
+// * "specialBattlefields" vector of battlefield images (BI format) where obstacle is appropriate. If there is a special battlefield image, then only this list is checked. Otherwise it's ignored. 
+// * "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.
+
+{
+"obstacles" : [
+	{
+		"id" : 0,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObDino1.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 1,
+		"allowedTerrain" : [0, 1, 5, 6],
+		"specialBattlefields" : [0],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObDino2.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 2,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, -14, -15, -16],
+		"defname" : "ObDino3.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 3,
+		"allowedTerrain" : [0, 5],
+		"specialBattlefields" : [2],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObSkel1.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 4,
+		"allowedTerrain" : [0, 5, 6],
+		"specialBattlefields" : [0, 2],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObSkel2.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 5,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3],
+		"defname" : "ObBDT01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 6,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [-15, -16],
+		"defname" : "ObDRk01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 7,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObDRk02.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 8,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [-16],
+		"defname" : "ObDRk03.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 9,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObDRk04.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 10,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObDSh01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 11,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObDTF03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 12,
+		"allowedTerrain" : [0, 5],
+		"specialBattlefields" : [2],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [0, 1, 2, 3],
+		"defname" : "ObDtS03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 13,
+		"allowedTerrain" : [0, 5],
+		"specialBattlefields" : [2],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, -15],
+		"defname" : "ObDtS04.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 14,
+		"allowedTerrain" : [0, 5],
+		"specialBattlefields" : [2],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [2, -15, -16],
+		"defname" : "ObDtS14.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 15,
+		"allowedTerrain" : [0, 5],
+		"specialBattlefields" : [2],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [1, -16, -33],
+		"defname" : "ObDtS15.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 16,
+		"allowedTerrain" : [1],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 4,
+		"blockedTiles" :  [-15, -16, -32, -33, -48, -49],
+		"defname" : "ObDsM01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 17,
+		"allowedTerrain" : [1],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [1, -15, -16],
+		"defname" : "ObDsS02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 18,
+		"allowedTerrain" : [1],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3, -15, -16],
+		"defname" : "ObDsS17.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 19,
+		"allowedTerrain" : [2, 4],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObGLg01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 20,
+		"allowedTerrain" : [2, 4],
+		"specialBattlefields" : [1],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObGRk01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 21,
+		"allowedTerrain" : [2, 4],
+		"specialBattlefields" : [],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObGSt01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 22,
+		"allowedTerrain" : [2],
+		"specialBattlefields" : [1],
+		"width" : 6,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3, 4, -13, -14, -15, -16],
+		"defname" : "ObGrS01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 23,
+		"allowedTerrain" : [2],
+		"specialBattlefields" : [],
+		"width" : 7,
+		"height" : 1,
+		"blockedTiles" :  [1, 2],
+		"defname" : "OBGrS02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 24,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 1,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObSnS01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 25,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 5,
+		"height" : 1,
+		"blockedTiles" :  [1, 2, 3, 4],
+		"defname" : "ObSnS02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 26,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [0, -16, -33],
+		"defname" : "ObSnS03.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 27,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 1,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObSnS04.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 28,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 1,
+		"blockedTiles" :  [1],
+		"defname" : "ObSnS05.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 29,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [1, 2],
+		"defname" : "ObSnS06.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 30,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObSnS07.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 31,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObSnS08.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 32,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 7,
+		"height" : 2,
+		"blockedTiles" :  [2, 3, 4, 5, -13, -14, -15, -16],
+		"defname" : "ObSnS09.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 33,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 5,
+		"height" : 5,
+		"blockedTiles" :  [3, -13, -14, -15, -33, -49, -66],
+		"defname" : "ObSnS10.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 34,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [0],
+		"defname" : "ObSwS01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 35,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 8,
+		"height" : 3,
+		"blockedTiles" :  [-10, -11, -12, -13, -14, -15, -16],
+		"defname" : "ObSwS02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 36,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObSwS03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 37,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 1,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObSwS04.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 38,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 5,
+		"height" : 4,
+		"blockedTiles" :  [-13, -14, -15, -16, -30, -31, -32, -33],
+		"defname" : "ObSwS11b.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 39,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 3,
+		"blockedTiles" :  [-16, -17, -31, -32, -33, -34],
+		"defname" : "ObSwS13a.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 40,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, -16],
+		"defname" : "ObRgS01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 41,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 4,
+		"height" : 3,
+		"blockedTiles" :  [-14, -15, -16, -32, -33],
+		"defname" : "ObRgS02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 42,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, -15, -16],
+		"defname" : "ObRgS03.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 43,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [-16, -32, -33],
+		"defname" : "ObRgS04.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 44,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [-15, -16, -32],
+		"defname" : "ObRgS05.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 45,
+		"allowedTerrain" : [6],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [0, 1, 2, -15, -16],
+		"defname" : "ObSuS01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 46,
+		"allowedTerrain" : [6],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObSuS02.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 47,
+		"allowedTerrain" : [6],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 3,
+		"blockedTiles" :  [0, 1, 2, 3, -14, -15, -16],
+		"defname" : "ObSuS11b.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 48,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 3,
+		"blockedTiles" :  [-14, -32, -33],
+		"defname" : "ObLvS01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 49,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2, -14, -15, -16],
+		"defname" : "ObLvS02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 50,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 5,
+		"height" : 3,
+		"blockedTiles" :  [-13, -14, -15, -30, -31, -32, -33],
+		"defname" : "ObLvS03.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 51,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObLvS04.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 52,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 4,
+		"height" : 4,
+		"blockedTiles" :  [-14, -15, -32, -33, -49, -50],
+		"defname" : "ObLvS09.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 53,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 5,
+		"height" : 3,
+		"blockedTiles" :  [-13, -14, -15, -16, -30, -31],
+		"defname" : "ObLvS17.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 54,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 5,
+		"height" : 3,
+		"blockedTiles" :  [-13, -14, -15, -16, -31, -32, -33],
+		"defname" : "ObLvS22.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 55,
+		"allowedTerrain" : [8],
+		"specialBattlefields" : [],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [-15, -16, -33],
+		"defname" : "ObBtS04.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 56,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [1, -15, -16],
+		"defname" : "ObBhS02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 57,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObBhS03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 58,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 5,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3, -14, -15, -16],
+		"defname" : "ObBhS11a.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 59,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, -14, -15],
+		"defname" : "ObBhS12b.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 60,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, -16],
+		"defname" : "ObBhS14b.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 61,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [3],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObHGs00.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 62,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [3],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObHGs01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 63,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [3],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [1],
+		"defname" : "ObHGs02.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 64,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [3],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObHGs03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 65,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [3],
+		"width" : 4,
+		"height" : 3,
+		"blockedTiles" :  [0, 1, 2, 3],
+		"defname" : "ObHGs04.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 66,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [4],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObEFs00.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 67,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [4],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObEFs01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 68,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [4],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObEFs02.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 69,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [4],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [1, 2],
+		"defname" : "ObEFs03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 70,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [4],
+		"width" : 6,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3, -12, -13],
+		"defname" : "ObEFs04.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 71,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [5],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObCFs00.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 72,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [5],
+		"width" : 3,
+		"height" : 1,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObCFs01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 73,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [5],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, -15, -16],
+		"defname" : "ObCFs02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 74,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [5],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2, -14, -15, -16],
+		"defname" : "ObCFs03.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 75,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [6],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObLPs00.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 76,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [6],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObLPs01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 77,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [6],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, -15, -16],
+		"defname" : "ObLPs02.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 78,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [6],
+		"width" : 5,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3, -13, -14, -15, -16],
+		"defname" : "ObLPs03.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 79,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [7],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObFFs00.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 80,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [7],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObFFs01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 81,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [7],
+		"width" : 3,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, 2, -15],
+		"defname" : "ObFFs02.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 82,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [7],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3, -15, -16],
+		"defname" : "ObFFs03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 83,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [7],
+		"width" : 3,
+		"height" : 3,
+		"blockedTiles" :  [0, 1, 2, 3, -14, -15, -16],
+		"defname" : "ObFFs04.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 84,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [8],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObRLs00.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 85,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [8],
+		"width" : 2,
+		"height" : 1,
+		"blockedTiles" :  [0, 1],
+		"defname" : "ObRLs01.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 86,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [8],
+		"width" : 3,
+		"height" : 1,
+		"blockedTiles" :  [0, 1, 2],
+		"defname" : "ObRLs02.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 87,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [8],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [1, 2, 3, -15, -16],
+		"defname" : "ObRLs03.def",
+		"unknown" : 0
+	},
+	{
+		"id" : 88,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [9],
+		"width" : 1,
+		"height" : 1,
+		"blockedTiles" :  [0],
+		"defname" : "ObMCs00.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 89,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [9],
+		"width" : 2,
+		"height" : 2,
+		"blockedTiles" :  [1, -16],
+		"defname" : "ObMCs01.def",
+		"unknown" : 1
+	},
+	{
+		"id" : 90,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [9],
+		"width" : 4,
+		"height" : 2,
+		"blockedTiles" :  [0, 1, -14, -15],
+		"defname" : "ObMCs02.def",
+		"unknown" : 1
+	},
+],
+"absoluteObstacles" : [
+	{
+		"id" : 0,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 124,
+		"height" : 254,
+		"blockedTiles" :  [80, 94, 95, 96, 97, 105, 106, 107, 108, 109, 110],
+		"defname" : "ObDtL04.pcx",
+	},
+	{
+		"id" : 1,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 256,
+		"height" : 254,
+		"blockedTiles" :  [73, 91, 108, 109, 110, 111, 112, 113],
+		"defname" : "ObDtL06.pcx",
+	},
+	{
+		"id" : 2,
+		"allowedTerrain" : [0],
+		"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",
+	},
+	{
+		"id" : 3,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 124,
+		"height" : 254,
+		"blockedTiles" :  [88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98],
+		"defname" : "ObDtL02.pcx",
+	},
+	{
+		"id" : 4,
+		"allowedTerrain" : [0],
+		"specialBattlefields" : [],
+		"width" : 146,
+		"height" : 254,
+		"blockedTiles" :  [76, 77, 78, 79, 80, 89, 90, 91, 92, 93],
+		"defname" : "ObDtL03.pcx",
+	},
+	{
+		"id" : 5,
+		"allowedTerrain" : [2],
+		"specialBattlefields" : [],
+		"width" : 173,
+		"height" : 221,
+		"blockedTiles" :  [55, 56, 57, 58, 75, 76, 77, 95, 112, 113, 131],
+		"defname" : "ObGrL01.pcx",
+	},
+	{
+		"id" : 6,
+		"allowedTerrain" : [2],
+		"specialBattlefields" : [],
+		"width" : 180,
+		"height" : 264,
+		"blockedTiles" :  [81, 91, 92, 93, 94, 95, 96, 97, 98, 106, 107, 123],
+		"defname" : "ObGrL02.pcx",
+	},
+	{
+		"id" : 7,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 166,
+		"height" : 255,
+		"blockedTiles" :  [76, 77, 78, 79, 91, 92, 93, 97, 98, 106, 107, 108],
+		"defname" : "ObSnL01.pcx",
+	},
+	{
+		"id" : 8,
+		"allowedTerrain" : [3],
+		"specialBattlefields" : [],
+		"width" : 302,
+		"height" : 172,
+		"blockedTiles" :  [41, 42, 43, 58, 75, 92, 108, 126, 143],
+		"defname" : "ObSnL14.pcx",
+	},
+	{
+		"id" : 9,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 300,
+		"height" : 170,
+		"blockedTiles" :  [40, 41, 58, 59, 74, 75, 92, 93, 109, 110, 111, 127, 128, 129, 130],
+		"defname" : "ObSwL15.pcx",
+	},
+	{
+		"id" : 10,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 278,
+		"height" : 171,
+		"blockedTiles" :  [43, 60, 61, 77, 93, 94, 95, 109, 110, 126, 127],
+		"defname" : "ObSwL14.pcx",
+	},
+	{
+		"id" : 11,
+		"allowedTerrain" : [4],
+		"specialBattlefields" : [],
+		"width" : 256,
+		"height" : 254,
+		"blockedTiles" :  [74, 75, 76, 77, 91, 92, 93, 94, 95, 109, 110, 111, 112],
+		"defname" : "ObSwL22.pcx",
+	},
+	{
+		"id" : 12,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 124,
+		"height" : 254,
+		"blockedTiles" :  [77, 78, 79, 80, 81, 91, 92, 93, 94, 105, 106, 107],
+		"defname" : "ObLvL01.pcx",
+	},
+	{
+		"id" : 13,
+		"allowedTerrain" : [7],
+		"specialBattlefields" : [],
+		"width" : 256,
+		"height" : 128,
+		"blockedTiles" :  [43, 60, 61, 76, 77, 93, 109, 126, 127, 142, 143],
+		"defname" : "OBLvL02.pcx",
+	},
+	{
+		"id" : 14,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 186,
+		"height" : 212,
+		"blockedTiles" :  [55, 72, 90, 107, 125, 126, 127, 128, 129, 130, 131, 132],
+		"defname" : "ObRgL01.pcx",
+	},
+	{
+		"id" : 15,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 347,
+		"height" : 174,
+		"blockedTiles" :  [41, 59, 76, 94, 111, 129, 143, 144, 145],
+		"defname" : "ObRgL02.pcx",
+	},
+	{
+		"id" : 16,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 294,
+		"height" : 169,
+		"blockedTiles" :  [40, 41, 42, 43, 58, 75, 93, 110, 128, 145],
+		"defname" : "ObRgL03.pcx",
+	},
+	{
+		"id" : 17,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 165,
+		"height" : 257,
+		"blockedTiles" :  [72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 89, 105],
+		"defname" : "ObRgL04.pcx",
+	},
+	{
+		"id" : 18,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"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",
+	},
+	{
+		"id" : 19,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 252,
+		"height" : 254,
+		"blockedTiles" :  [73, 74, 75, 76, 77, 78, 91, 92, 93, 94],
+		"defname" : "ObRgL06.pcx",
+	},
+	{
+		"id" : 20,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"width" : 278,
+		"height" : 128,
+		"blockedTiles" :  [23, 40, 58, 75, 93, 110, 128, 145, 163],
+		"defname" : "ObRgL15.pcx",
+	},
+	{
+		"id" : 21,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"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",
+	},
+	{
+		"id" : 22,
+		"allowedTerrain" : [5],
+		"specialBattlefields" : [2],
+		"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",
+	},
+	{
+		"id" : 23,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 147,
+		"height" : 264,
+		"blockedTiles" :  [72, 73, 74, 75, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98],
+		"defname" : "ObBhL02.pcx",
+	},
+	{
+		"id" : 24,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"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",
+	},
+	{
+		"id" : 25,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 173,
+		"height" : 257,
+		"blockedTiles" :  [72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 89, 90, 105, 106],
+		"defname" : "ObBhL05.pcx",
+	},
+	{
+		"id" : 26,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"width" : 241,
+		"height" : 272,
+		"blockedTiles" :  [73, 91, 108, 109, 110, 111, 112, 113],
+		"defname" : "ObBhL06.pcx",
+	},
+	{
+		"id" : 27,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"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",
+	},
+	{
+		"id" : 28,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [0],
+		"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",
+	},
+	{
+		"id" : 29,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [5],
+		"width" : 304,
+		"height" : 264,
+		"blockedTiles" :  [76, 77, 92, 93, 94, 95, 109, 110, 111],
+		"defname" : "ObCFL00.pcx",
+	},
+	{
+		"id" : 30,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [6],
+		"width" : 256,
+		"height" : 257,
+		"blockedTiles" :  [76, 77, 78, 92, 93, 94, 107, 108, 109],
+		"defname" : "ObLPL00.pcx",
+	},
+	{
+		"id" : 31,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [7],
+		"width" : 257,
+		"height" : 255,
+		"blockedTiles" :  [76, 77, 91, 92, 93, 94, 95, 108, 109, 110, 111],
+		"defname" : "ObFFL00.pcx",
+	},
+	{
+		"id" : 32,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [8],
+		"width" : 277,
+		"height" : 218,
+		"blockedTiles" :  [60, 61, 75, 76, 77, 91, 92, 93, 94, 95],
+		"defname" : "ObRLL00.pcx",
+	},
+	{
+		"id" : 33,
+		"allowedTerrain" : [],
+		"specialBattlefields" : [9],
+		"width" : 300,
+		"height" : 214,
+		"blockedTiles" :  [59, 60, 74, 75, 76, 93, 94, 95, 111, 112],
+		"defname" : "ObMCL00.pcx",
+	}
+]
+
+}

+ 169 - 43
lib/BattleState.cpp

@@ -153,7 +153,7 @@ void BattleInfo::getAccessibilityMap(bool *accessibility, bool twoHex, bool atta
 	//obstacles
 	for(ui32 b=0; b<obstacles.size(); ++b)
 	{
-		std::vector<BattleHex> blocked = VLC->heroh->obstacles[obstacles[b].ID].getBlocked(obstacles[b].pos);
+		std::vector<BattleHex> blocked = obstacles[b].getBlocked();
 		for(ui32 c=0; c<blocked.size(); ++c)
 		{
 			if(blocked[c] >=0 && blocked[c] < GameConstants::BFIELD_SIZE)
@@ -1501,6 +1501,81 @@ namespace CGH
 	}
 }
 
+//RNG that works like H3 one
+struct RandGen
+{
+	int seed;
+
+	void srand(int s)
+	{
+		seed = s;
+	}
+	void srand(int3 pos)
+	{
+		srand(110291 * pos.x + 167801 * pos.y + 81569);
+	}
+	int rand()
+	{
+		seed = 214013 * seed + 2531011;
+		return (seed >> 16) & 0x7FFF;
+	}
+	int rand(int min, int max)
+	{
+		if(min == max)
+			return min;
+		if(min > max)
+			return min;
+		return min + rand() % (max - min + 1);
+	}
+};
+
+struct RangeGenerator
+{
+	class ExhaustedPossibilities : public std::exception
+	{
+	};
+
+	RangeGenerator(int _min, int _max, boost::function<int()> _myRand)
+	{
+		myRand = _myRand;
+		min = _min;
+		remainingCount = _max - _min + 1;
+		remaining.resize(remainingCount, true);
+	}
+
+	//get number fulfilling predicate. Never gives the same number twice.
+	int getSuchNumber(boost::function<bool(int)> goodNumberPred = 0)
+	{
+		int ret = -1;
+		do
+		{
+			if(!remainingCount)
+				throw ExhaustedPossibilities();
+
+			int n = myRand() % remainingCount;
+			int i = 0;
+			for(;;i++)
+			{
+				assert(i < (int)remaining.size());
+				if(!remaining[i])
+					continue;
+				if(!n)
+					break;
+				n--;
+			}
+
+			remainingCount--;
+			remaining[i] = false;
+			ret = i + min;
+		} while(goodNumberPred && !goodNumberPred(ret));
+		return ret;
+	}
+
+	int min, remainingCount;
+	std::vector<bool> remaining;
+	boost::function<int()> myRand;
+};
+
 BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const CArmedInstance *armies[2], const CGHeroInstance * heroes[2], bool creatureBank, const CGTownInstance *town )
 {
 	CMP_stack cmpst;
@@ -1577,7 +1652,8 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 		stacks.push_back(stack);
 	}
 
-	for(unsigned g=0; g<stacks.size(); ++g) //shifting positions of two-hex creatures
+	//shifting positions of two-hex creatures
+	for(unsigned g=0; g<stacks.size(); ++g) 
 	{
 		//we should do that for creature bank too
 		if(stacks[g]->doubleWide() && stacks[g]->attackerOwned)
@@ -1667,62 +1743,97 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
 	}
 
 	//randomize obstacles
-	if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank
-	{
-		bool obAv[GameConstants::BFIELD_SIZE]; //availability of hexes for obstacles;
-		std::vector<int> possibleObstacles;
+ 	if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank
+ 	{
+		const int ABSOLUTE_OBSTACLES_COUNT = 34, USUAL_OBSTACLES_COUNT = 91; //shouldn't be changes if we want H3-like obstacle placement
+
+		RandGen r;
+		auto ourRand = [&]{ return r.rand(); };
+		r.srand(tile);
+		const int sound = r.rand(1,8); //battle sound ID to play... can't do anything with it here
+		int tilesToBlock = r.rand(5,12);
+		const int specialBattlefield = battlefieldTypeToBI(terType);
+
+		std::vector<BattleHex> blockedTiles;
 
-		for(int i=0; i<GameConstants::BFIELD_SIZE; ++i)
+		auto appropriateAbsoluteObstacle = [&](int id)
 		{
-			if(i%17 < 4 || i%17 > 12)
+			return VLC->heroh->absoluteObstacles[id].isAppropriate(terrain, specialBattlefield);
+		};
+		auto appropriateUsualObstacle = [&](int id) -> bool
+		{
+			return VLC->heroh->obstacles[id].isAppropriate(terrain, specialBattlefield);
+		};
+
+		if(r.rand(1,100) <= 40) //put cliff-like obstacle
+		{
+			RangeGenerator obidgen(0, ABSOLUTE_OBSTACLES_COUNT-1, ourRand);
+			
+			try
 			{
-				obAv[i] = false;
+				CObstacleInstance coi;
+				coi.isAbsoluteObstacle = true;
+				coi.ID = obidgen.getSuchNumber(appropriateAbsoluteObstacle);
+				coi.uniqueID = curB->obstacles.size();
+				curB->obstacles.push_back(coi);
+
+				BOOST_FOREACH(BattleHex blocked, coi.getBlocked())
+					blockedTiles.push_back(blocked);
+				tilesToBlock -= VLC->heroh->absoluteObstacles[coi.ID].blockedTiles.size() / 2;
 			}
-			else
+			catch(RangeGenerator::ExhaustedPossibilities &)
 			{
-				obAv[i] = true;
+				//silently ignore, if we can't place absolute obstacle, we'll go wityh the usual ones
 			}
 		}
 
-		for(std::map<int, CObstacleInfo>::const_iterator g=VLC->heroh->obstacles.begin(); g!=VLC->heroh->obstacles.end(); ++g)
+		RangeGenerator obidgen(0, USUAL_OBSTACLES_COUNT-1, ourRand);
+		try
 		{
-			if(g->second.allowedTerrains[terType-1] == '1') //we need to take terType with -1 because terrain ids start from 1 and allowedTerrains array is indexed from 0
+			while(tilesToBlock > 0)
 			{
-				possibleObstacles.push_back(g->first);
-			}
-		}
+				const int obid = obidgen.getSuchNumber(appropriateUsualObstacle);
+				const CObstacleInfo &obi = VLC->heroh->obstacles[obid];
 
-		srand(time(NULL));
-		if(possibleObstacles.size() > 0) //we cannot place any obstacles when we don't have them
-		{
-			int toBlock = rand()%6 + 6; //how many hexes should be blocked by obstacles
-			while(toBlock>0)
-			{
-				CObstacleInstance coi;
-				coi.uniqueID = curB->obstacles.size();
-				coi.ID = possibleObstacles[rand()%possibleObstacles.size()];
-				coi.pos = rand()%GameConstants::BFIELD_SIZE;
-				std::vector<BattleHex> block = VLC->heroh->obstacles[coi.ID].getBlocked(coi.pos);
-				bool badObstacle = false;
-				for(int b=0; b<block.size(); ++b)
+				auto validPosition = [&](BattleHex pos) -> bool
 				{
-					if(block[b] < 0 || block[b] >= GameConstants::BFIELD_SIZE || !obAv[block[b]])
+					if(obi.height >= pos.getY())
+						return false;
+					if(pos.getX() == 0)
+						return false;
+					if(pos.getX() + obi.width > 15)
+						return false;
+					if(vstd::contains(blockedTiles, pos))
+						return false;
+
+					BOOST_FOREACH(BattleHex blocked, obi.getBlocked(pos))
 					{
-						badObstacle = true;
-						break;
+						if(vstd::contains(blockedTiles, blocked))
+							return false;
+						int x = blocked.getX();
+						if(x <= 2 || x >= 14)
+							return false;
 					}
-				}
-				if(badObstacle) continue;
-				//obstacle can be placed
-				curB->obstacles.push_back(coi);
-				for(int b=0; b<block.size(); ++b)
-				{
-					if(block[b] >= 0 && block[b] < GameConstants::BFIELD_SIZE)
-						obAv[block[b]] = false;
-				}
-				toBlock -= block.size();
+
+					return true;
+				};
+
+				RangeGenerator posgenerator(18, 168, ourRand);
+
+				CObstacleInstance oi;
+				oi.ID = obid;
+				oi.pos = posgenerator.getSuchNumber(validPosition);
+				oi.uniqueID = curB->obstacles.size();
+				curB->obstacles.push_back(oi);
+
+				BOOST_FOREACH(BattleHex blocked, oi.getBlocked())
+					blockedTiles.push_back(blocked);
+				tilesToBlock -= obi.blockedTiles.size();
 			}
 		}
+		catch(RangeGenerator::ExhaustedPossibilities &)
+		{
+		}
 	}
 
 	//spell level limiting bonus
@@ -2400,7 +2511,7 @@ bool BattleInfo::isObstacleOnTile(BattleHex tile) const
 	std::set<BattleHex> coveredHexes;
 	BOOST_FOREACH(const CObstacleInstance &obs, obstacles)
 	{
-		std::vector<BattleHex> blocked = VLC->heroh->obstacles.find(obs.ID)->second.getBlocked(obs.pos);
+		std::vector<BattleHex> blocked = obs.getBlocked();
 		for(size_t w = 0; w < blocked.size(); ++w)
 			coveredHexes.insert(blocked[w]);
 	}
@@ -2415,6 +2526,21 @@ const CStack * BattleInfo::getStackIf(boost::function<bool(const CStack*)> pred)
 		: *stackItr;
 }
 
+int BattleInfo::battlefieldTypeToBI(int bfieldType)
+{
+	static const std::map<int, int> theMap = boost::assign::map_list_of(19, BattlefieldBI::CLOVER_FIELD)
+		(22, BattlefieldBI::CURSED_GROUND)(20, BattlefieldBI::EVIL_FOG)(21, BattlefieldBI::NONE)
+		(14, BattlefieldBI::FIERY_FIELDS)(18, BattlefieldBI::HOLY_GROUND)(17, BattlefieldBI::LUCID_POOLS)
+		(16, BattlefieldBI::MAGIC_CLOUDS)(9, BattlefieldBI::MAGIC_PLAINS)(15, BattlefieldBI::ROCKLANDS)
+		(1, BattlefieldBI::COASTAL);
+
+	auto itr = theMap.find(bfieldType);
+	if(itr != theMap.end())
+		return itr->second;
+
+	return BattlefieldBI::NONE;
+}
+
 CStack::CStack(const CStackInstance *Base, int O, int I, bool AO, int S)
 	: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),   
 	counterAttacks(1)

+ 3 - 0
lib/BattleState.h

@@ -155,6 +155,9 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
 
 	int theOtherPlayer(int player) const;
 	ui8 whatSide(int player) const;
+
+	static int battlefieldTypeToBI(int bfieldType); //converts above to ERM BI format
+	static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format
 };
 
 class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor

+ 1 - 1
lib/CCreatureSet.cpp

@@ -915,7 +915,7 @@ void CStackInstance::deserializationFix()
 {
 	const CCreature *backup = type;
 	type = NULL;
-	setType(backup);
+		setType(backup);
 	const CArmedInstance *armyBackup = _armyObj;
 	_armyObj = NULL;
 	setArmyObj(armyBackup);

+ 13 - 11
lib/CGameState.cpp

@@ -1589,7 +1589,7 @@ void CGameState::init(StartInfo * si)
 	}
 }
 
-int CGameState::battleGetBattlefieldType(int3 tile)
+int CGameState::battleGetBattlefieldType(int3 tile) const
 {
 	if(tile==int3() && curB)
 		tile = curB->tile;
@@ -1609,27 +1609,28 @@ int CGameState::battleGetBattlefieldType(int3 tile)
 			|| !objs[g]->coveringAt(objs[g]->pos.x - tile.x, tile.y - objs[g]->pos.y + 5)
 			) //look only for objects covering given tile
 			continue;
+
 		switch(objs[g]->ID)
 		{
-		case 222: //clover field
+		case Obj::CLOVER_FIELD:
 			return 19;
-		case 21: case 223: //cursed ground
+		case Obj::CURSED_GROUND1: case Obj::CURSED_GROUND2:
 			return 22;
-		case 224: //evil fog
+		case Obj::EVIL_FOG:
 			return 20;
-		case 225: //favourable winds
+		case Obj::FAVORABLE_WINDS:
 			return 21;
-		case 226: //fiery fields
+		case Obj::FIERY_FIELDS:
 			return 14;
-		case 227: //holy ground
+		case Obj::HOLY_GROUNDS:
 			return 18;
-		case 228: //lucid pools
+		case Obj::LUCID_POOLS:
 			return 17;
-		case 229: //magic clouds
+		case Obj::MAGIC_CLOUDS:
 			return 16;
-		case 46: case 230: //magic plains
+		case Obj::MAGIC_PLAINS1: case Obj::MAGIC_PLAINS2:
 			return 9;
-		case 231: //rocklands
+		case Obj::ROCKLANDS:
 			return 15;
 		}
 	}
@@ -1664,6 +1665,7 @@ int CGameState::battleGetBattlefieldType(int3 tile)
 	}
 }
 
+
 std::set<std::pair<int, int> > costDiff(const std::vector<ui32> &a, const std::vector<ui32> &b, const int modifier = 100) //modifer %
 {
 	std::set<std::pair<int, int> > ret;

+ 1 - 1
lib/CGameState.h

@@ -381,7 +381,7 @@ public:
 	void giveHeroArtifact(CGHeroInstance *h, int aid);
 
 	void apply(CPack *pack);
-	int battleGetBattlefieldType(int3 tile = int3());//   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
+	int battleGetBattlefieldType(int3 tile) const;//   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
 	UpgradeInfo getUpgradeInfo(const CStackInstance &stack);
 	int getPlayerRelations(ui8 color1, ui8 color2);// 0 = enemy, 1 = ally, 2 = same player
 	bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile

+ 44 - 81
lib/CHeroHandler.cpp

@@ -6,13 +6,7 @@
 #include "../lib/JsonNode.h"
 #include "GameConstants.h"
 #include <boost/version.hpp>
-#if BOOST_VERSION >= 103800
-#include <boost/spirit/include/classic.hpp>
-#else
-#include <boost/spirit.hpp>
-#endif
-
-using namespace boost::spirit;
+#include "BattleHex.h"
 
 extern CLodHandler * bitmaph;
 void loadToIt(std::string &dest, const std::string &src, int &iter, int mode);
@@ -57,74 +51,37 @@ EAlignment::EAlignment CHeroClass::getAlignment()
 	return (EAlignment::EAlignment)alignment;
 }
 
-int CObstacleInfo::getWidth() const
+std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const
 {
-	int ret = 1;
-	int line = 1;
-	for(int h=0; h<blockmap.size(); ++h)
+	std::vector<BattleHex> ret;
+	if(isAbsoluteObstacle)
 	{
-		int cur = - line/2;
-		switch(blockmap[h])
-		{
-		case 'X' : case 'N':
-			++cur;
-			break;
-		case 'L':
-			if(cur > ret)
-				ret = cur;
-			++line;
-			break;
-		}
+		assert(!hex.isValid());
+		range::copy(blockedTiles, std::back_inserter(ret));
+		return ret;
 	}
-	return ret;
-}
 
-int CObstacleInfo::getHeight() const
-{
-	int ret = 1;
-	for(int h=0; h<blockmap.size(); ++h)
+	BOOST_FOREACH(int offset, blockedTiles)
 	{
-		if(blockmap[h] == 'L')
-		{
-			++ret;
-		}
-	}
-	return ret;
-}
+		BattleHex toBlock = hex + offset;
+		if((hex.getY() & 1) && !(toBlock.getY() & 1))
+			toBlock += BattleHex::LEFT;
 
-std::vector<BattleHex> CObstacleInfo::getBlocked(BattleHex hex) const
-{
-	std::vector<BattleHex> ret;
-	int cur = hex; //currently browsed hex
-	int curBeg = hex; //beginning of current line
-	for(int h=0; h<blockmap.size(); ++h)
-	{
-		switch(blockmap[h])
-		{
-		case 'X':
-			ret.push_back(cur);
-			++cur;
-			break;
-		case 'L':
-			cur = curBeg + GameConstants::BFIELD_WIDTH;
-			if((cur/GameConstants::BFIELD_WIDTH)%2 != 1)
-			{
-				cur--;
-			}
-			curBeg = cur;
-			break;
-		case 'N':
-			++cur;
-			break;
-		}
+		if(!toBlock.isValid())
+			tlog1 << "Misplaced obstacle!\n";
+		else
+			ret.push_back(toBlock);
 	}
+
 	return ret;
 }
 
-BattleHex CObstacleInfo::getMaxBlocked(BattleHex hex) const
+bool CObstacleInfo::isAppropriate(int terrainType, int specialBattlefield /*= -1*/) const
 {
-	std::vector<BattleHex> blocked = getBlocked(hex);
-	return *std::max_element(blocked.begin(), blocked.end());
+	if(specialBattlefield != -1)
+		return vstd::contains(allowedSpecialBfields, specialBattlefield);
+
+	return vstd::contains(allowedTerrains, terrainType);
 }
 
 CHeroHandler::~CHeroHandler()
@@ -141,21 +98,27 @@ CHeroHandler::CHeroHandler()
 
 void CHeroHandler::loadObstacles()
 {
-	const JsonNode config(GameConstants::DATA_DIR + "/config/obstacles.json");
-
-	BOOST_FOREACH(const JsonNode &obs, config["obstacles"].Vector()) {
-		CObstacleInfo obi;
+	auto loadObstacles = [](const JsonNode &node, bool absolute, std::map<int, CObstacleInfo> &out)
+	{
+		BOOST_FOREACH(const JsonNode &obs, node.Vector()) 
+		{
+			int ID = obs["id"].Float();
+			CObstacleInfo & obi = out[ID];
+			obi.ID = ID;
+			obi.defName = obs["defname"].String();
+			obi.width = obs["width"].Float();
+			obi.height = obs["height"].Float();
+			obi.allowedTerrains = obs["allowedTerrain"].StdVector<ui8>();
+			obi.allowedSpecialBfields = obs["specialBattlefields"].StdVector<ui8>();
+			obi.blockedTiles = obs["blockedTiles"].StdVector<si16>();
+			obi.isAbsoluteObstacle = absolute;
+		}
+	};
 
-		obi.ID = obs["id"].Float();
-		obi.defName = obs["defname"].String();
-		obi.blockmap = obs["blockmap"].String();
-		obi.allowedTerrains = obs["terrains"].String();
-		assert(obi.allowedTerrains.size() >= 25);
-		obi.posShift.first = obs["shift_x"].Float();
-		obi.posShift.second = obs["shift_y"].Float();
 
-		obstacles[obi.ID] = obi;
-	}
+	const JsonNode config(GameConstants::DATA_DIR + "/config/obstacles.json");
+	loadObstacles(config["obstacles"], false, obstacles);
+	loadObstacles(config["absoluteObstacles"], true, absoluteObstacles);
 }
 
 void CHeroHandler::loadPuzzleInfo()
@@ -164,12 +127,12 @@ void CHeroHandler::loadPuzzleInfo()
 
 	int faction = 0;
 
-	BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector()) {
-
+	BOOST_FOREACH(const JsonNode &puzzle, config["puzzles"].Vector()) 
+	{
 		int idx = 0;
 
-		BOOST_FOREACH(const JsonNode &piece, puzzle.Vector()) {
-
+		BOOST_FOREACH(const JsonNode &piece, puzzle.Vector()) 
+		{
 			SPuzzleInfo spi;
 
 			spi.x = piece["x"].Float();

+ 16 - 15
lib/CHeroHandler.h

@@ -1,7 +1,6 @@
 #pragma once
 
 
-#include "BattleHex.h"
 #include "../lib/ConstTransitivePtr.h"
 #include "GameConstants.h"
 
@@ -18,6 +17,7 @@ class CHeroClass;
 class CDefHandler;
 class CGameInfo;
 class CGHeroInstance;
+struct BattleHex;
 
 struct SSpecialtyInfo
 {	si32 type;
@@ -85,22 +85,22 @@ public:
 
 struct DLL_LINKAGE CObstacleInfo
 {
-	int ID;
-	std::string defName, 
-		blockmap, //blockmap: X - blocked, N - not blocked, L - description goes to the next line, staring with the left bottom hex
-		allowedTerrains; /*terrains[i]: 1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   
-			7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees  
-			14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field  
-			20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough 
-			24. ship to ship   25. ship*/
-	std::pair<si16, si16> posShift; //shift of obstacle's position in the battlefield <x shift, y shift>, eg. if it's <-1, 2> obstacle will be printed one pixel to the left and two to the bottom
-	int getWidth() const; //returns width of obstacle in hexes
-	int getHeight() const; //returns height of obstacle in hexes
+	si32 ID;
+	std::string defName;
+	std::vector<ui8> allowedTerrains;
+	std::vector<ui8> 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'
-	BattleHex getMaxBlocked(BattleHex hex) const; //returns maximal hex (max number) covered by this obstacle
+
+	bool isAppropriate(int terrainType, int specialBattlefield = -1) const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & ID & defName & blockmap & allowedTerrains & posShift;
+		h & ID & defName & allowedTerrains & allowedSpecialBfields & isAbsoluteObstacle & width & height & blockedTiles;
 	}
 };
 
@@ -140,6 +140,7 @@ public:
 	std::vector<SBallisticsLevelInfo> ballistics; //info about ballistics ability per level; [0] - none; [1] - basic; [2] - adv; [3] - expert
 
 	std::map<int, CObstacleInfo> obstacles; //info about obstacles that may be placed on battlefield
+	std::map<int, CObstacleInfo> absoluteObstacles; //info about obstacles that may be placed on battlefield
 	std::vector<int> nativeTerrains; //info about native terrains of different factions
 
 	void loadObstacles(); //loads info about obstacles
@@ -159,7 +160,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & heroClasses & heroes & expPerLevel & ballistics & obstacles & nativeTerrains & puzzleInfo;
+		h & heroClasses & heroes & expPerLevel & ballistics & obstacles & absoluteObstacles & nativeTerrains & puzzleInfo;
 		if(!h.saving)
 		{
 			//restore class pointers

+ 2 - 2
lib/CMapInfo.cpp

@@ -45,9 +45,9 @@ void CMapInfo::mapInit(const std::string &fname, const ui8 *map )
 		mapHeader->initFromMemory(map, i);
 		countPlayers();
 	}
-	catch (const std::string &e)
+	catch (const std::exception &e)
 	{
-		tlog1 << "\t\tWarning: evil map file: " << fname << ": " << e << std::endl; 
+		tlog1 << "\t\tWarning: evil map file: " << fname << ": " << e.what() << std::endl; 
 		delete mapHeader;
 		mapHeader = NULL;
 	}

+ 15 - 4
lib/CObstacleInstance.h

@@ -1,12 +1,23 @@
 #pragma once
+#include "BattleHex.h"
+
+struct CObstacleInfo;
 
 struct DLL_LINKAGE CObstacleInstance
 {
-	int uniqueID;
-	int ID; //ID of obstacle (defines type of it)
-	int pos; //position on battlefield
+	si32 uniqueID;
+	si32 ID; //ID of obstacle (defines type of it)
+	BattleHex pos; //position on battlefield
+
+	ui8 isAbsoluteObstacle; //if true, then position is meaningless
+
+	CObstacleInstance();
+
+	const CObstacleInfo &getInfo() const;
+	std::vector<BattleHex> getBlocked() const;
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & ID & pos & uniqueID;
+		h & ID & pos & isAbsoluteObstacle & uniqueID;
 	}
 };

+ 31 - 0
lib/GameConstants.h

@@ -187,6 +187,7 @@ namespace Obj
 		BOAT = 8,
 		CREATURE_BANK = 16,
 		CREATURE_GENERATOR1 = 17,
+		CURSED_GROUND1 = 21,
 		DERELICT_SHIP = 24,
 		DRAGON_UTOPIA = 25,
 		GARRISON = 33,
@@ -194,6 +195,7 @@ namespace Obj
 		MONOLITH1 = 43,
 		MONOLITH2 = 44,
 		MONOLITH3 = 45,
+		MAGIC_PLAINS1 = 46,
 		SCHOOL_OF_MAGIC = 47,
 		MINE = 53,
 		MONSTER = 54,
@@ -209,6 +211,35 @@ namespace Obj
 		WHIRLPOOL = 111,
 		BORDER_GATE = 212,
 		GARRISON2 = 219,
+		CLOVER_FIELD = 222,
+		CURSED_GROUND2 = 223,
+		EVIL_FOG = 224,
+		FAVORABLE_WINDS = 225,
+		FIERY_FIELDS = 226,
+		HOLY_GROUNDS = 227,
+		LUCID_POOLS = 228,
+		MAGIC_CLOUDS = 229,
+		MAGIC_PLAINS2 = 230,
+		ROCKLANDS = 231,
+	};
+}
+
+//follows ERM BI (battle image) format
+namespace BattlefieldBI
+{
+	enum
+	{
+		NONE = -1,
+		COASTAL,
+		CURSED_GROUND,
+		MAGIC_PLAINS,
+		HOLY_GROUND,
+		EVIL_FOG,
+		CLOVER_FIELD,
+		LUCID_POOLS,
+		FIERY_FIELDS,
+		ROCKLANDS,
+		MAGIC_CLOUDS,
 	};
 }
 

+ 13 - 0
lib/JsonNode.h

@@ -75,6 +75,19 @@ public:
 	const JsonVector & Vector() const;
 	const JsonMap & Struct() const;
 
+	template<typename T>
+	std::vector<T> StdVector() const
+	{
+		
+		static_assert(typename std::is_arithmetic<T>::value, "This works with numbers only.");
+		std::vector<T> ret;
+		BOOST_FOREACH(const JsonNode &node, Vector())
+		{
+			ret.push_back(node.Float());
+		}
+		return ret;
+	}
+
 	//operator [], for structs only - get child node by name
 	JsonNode & operator[](std::string child);
 	const JsonNode & operator[](std::string child) const;

+ 1 - 0
lib/VCMI_lib.vcxproj

@@ -239,6 +239,7 @@
     <ClCompile Include="CLogger.cpp" />
     <ClCompile Include="CMapInfo.cpp" />
     <ClCompile Include="CObjectHandler.cpp" />
+    <ClCompile Include="CObstacleInstance.cpp" />
     <ClCompile Include="Connection.cpp" />
     <ClCompile Include="CSpellHandler.cpp" />
     <ClCompile Include="CThreadHelper.cpp" />

+ 6 - 7
server/CGameHandler.cpp

@@ -3884,17 +3884,16 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 	case Spells::REMOVE_OBSTACLE:
 		{
 			ObstaclesRemoved obr;
-			for(int g=0; g<gs->curB->obstacles.size(); ++g)
+			BOOST_FOREACH(const CObstacleInstance &obstacle, battleGetAllObstacles())
 			{
-				std::vector<BattleHex> blockedHexes = VLC->heroh->obstacles[gs->curB->obstacles[g].ID].getBlocked(gs->curB->obstacles[g].pos);
-
-				if(vstd::contains(blockedHexes, destination)) //this obstacle covers given hex
-				{
-					obr.obstacles.insert(gs->curB->obstacles[g].uniqueID);
-				}
+				if(vstd::contains(obstacle.getBlocked(), destination))
+					obr.obstacles.insert(obstacle.uniqueID);
 			}
+
 			if(!obr.obstacles.empty())
 				sendAndApply(&obr);
+			else
+				complain("There's no obstacle to remove!");
 			break;
 		}
 		break;