1 /////////////////////////////////////////
2 //
3 //                  OpenLieroX
4 //
5 // code under LGPL, based on JasonBs work,
6 // enhanced by Dark Charlie and Albert Zeyer
7 //
8 //
9 /////////////////////////////////////////
10 
11 
12 // Map class
13 // Created 22/1/02
14 // Jason Boettcher
15 
16 
17 #include <cassert>
18 #include <zlib.h>
19 #include <list>
20 
21 
22 #include "LieroX.h"
23 #include "CViewport.h"
24 #include "CMap.h"
25 #include "EndianSwap.h"
26 #include "MathLib.h"
27 #include "Error.h"
28 #include "ConfigHandler.h"
29 #include "GfxPrimitives.h"
30 #include "PixelFunctors.h"
31 #include "FindFile.h"
32 #include "StringUtils.h"
33 #include "InputEvents.h"
34 #include "CWorm.h"
35 #include "Entity.h"
36 #include "CServer.h"
37 #include "ProfileSystem.h"
38 #include "Cache.h"
39 #include "Debug.h"
40 #include "FlagInfo.h"
41 #include "FileUtils.h"
42 #include "EndianSwap.h"
43 #include "MapLoader.h"
44 
45 
46 ////////////////////
47 // Copies all info from the given map to this map
NewFrom(CMap * map)48 bool CMap::NewFrom(CMap* map)
49 {
50 	if (map == NULL || !map->Created)
51 		return false;
52 
53 	Name = map->Name;
54 	FileName = map->FileName;
55 	Type = map->Type;
56 	Width = map->Width;
57 	Height = map->Height;
58 	MinimapWidth = map->MinimapWidth;
59 	MinimapHeight = map->MinimapHeight;
60 	Theme = map->Theme;
61 	nTotalDirtCount = map->nTotalDirtCount;
62 	nGridWidth = map->nGridWidth;
63 	nGridHeight = map->nGridHeight;
64 	nGridCols = map->nGridCols;
65 	nGridRows = map->nGridRows;
66 	bMiniMapDirty = map->bMiniMapDirty;
67 	NumObjects = map->NumObjects;
68 
69 	bmpGreenMask = GetCopiedImage(map->bmpGreenMask);
70 
71 	// Create the map (and bmpImage and friends)
72 	if (!Create(Width, Height, Theme.name, MinimapWidth, MinimapHeight))
73 		return false;
74 
75 	// Copy the data
76 	// TODO: why DrawImage and not CopySurface?
77 	DrawImage(bmpImage.get(), map->bmpImage, 0, 0);
78 	DrawImage(bmpDrawImage.get(), map->bmpDrawImage, 0, 0);
79 	DrawImage(bmpBackImage.get(), map->bmpBackImage, 0, 0);
80 	DrawImage(bmpMiniMap.get(), map->bmpMiniMap, 0, 0);
81 	memcpy(PixelFlags, map->PixelFlags, Width * Height);
82 	memcpy(CollisionGrid, map->CollisionGrid, Width * Height);
83 	DrawImage(bmpShadowMap.get(), map->bmpShadowMap, 0, 0);
84 #ifdef _AI_DEBUG
85 	DrawImage(bmpDebugImage.get(), map->bmpDebugImage, 0, 0);
86 #endif
87 	memcpy(GridFlags, map->GridFlags, nGridCols * nGridRows);
88 	memcpy(AbsoluteGridFlags, map->AbsoluteGridFlags, nGridCols * nGridRows);
89 	memcpy(Objects, map->Objects, MAX_OBJECTS * sizeof(object_t));
90 	bmpBackImageHiRes = NULL;
91 	if( map->bmpBackImageHiRes.get() )
92 	{
93 		bmpBackImageHiRes = gfxCreateSurface(Width*2, Height*2);
94 		if( ! bmpBackImageHiRes.get() )
95 		{
96 			errors("CMap::NewFrom(): ERROR: cannot create bmpBackImageHiRes\n");
97 			return false;
98 		}
99 		DrawImage(bmpBackImageHiRes.get(), map->bmpBackImageHiRes, 0, 0);
100 	}
101 
102 	AdditionalData = map->AdditionalData;
103 
104 	Created = true;
105 
106 	return true;
107 }
108 
109 
110 ////////////////
111 // Save this map to cache
SaveToCache()112 void CMap::SaveToCache()
113 {
114 	cCache.SaveMap(FileName, this);
115 }
116 
117 ///////////////
118 // Try to load the map from cache
LoadFromCache()119 bool CMap::LoadFromCache()
120 {
121 	SmartPointer<CMap> cached = cCache.GetMap(FileName);
122 	if (!cached.get())
123 		return false;
124 
125 	return NewFrom(cached.get());
126 }
127 
128 /////////////////////
129 // Returns number of bytes that the map takes in memory
GetMemorySize()130 size_t CMap::GetMemorySize()
131 {
132 	size_t res = sizeof(CMap) +
133 		GetSurfaceMemorySize(bmpBackImage.get()) + GetSurfaceMemorySize(bmpImage.get()) +
134 		GetSurfaceMemorySize(bmpDrawImage.get()) + GetSurfaceMemorySize(bmpGreenMask.get()) +
135 		GetSurfaceMemorySize(bmpShadowMap.get()) + GetSurfaceMemorySize(bmpMiniMap.get()) +
136 		2 * Width * Height + // Pixel flags + Collision grid
137 		2 * nGridCols * nGridRows + // Grids
138 		Name.size() + FileName.size() +
139 		Theme.name.size();
140 #ifdef _AI_DEBUG
141 	res += GetSurfaceMemorySize(bmpDebugImage.get());
142 #endif
143 	if( bmpBackImageHiRes.get() )
144 		res += GetSurfaceMemorySize(bmpBackImageHiRes.get());
145 	return res;
146 }
147 
148 ///////////////////
149 // Allocate a new map
Create(uint _width,uint _height,const std::string & _theme,uint _minimap_w,uint _minimap_h)150 bool CMap::Create(uint _width, uint _height, const std::string& _theme, uint _minimap_w, uint _minimap_h)
151 {
152 	if(Created)
153 		Shutdown();
154 
155 	Width = _width;
156 	Height = _height;
157 	MinimapWidth = _minimap_w;
158 	MinimapHeight = _minimap_h;
159 
160 	fBlinkTime = 0;
161 
162 	Objects = new object_t[MAX_OBJECTS];
163 	if(Objects == NULL)
164 	{
165 		errors << "CMap::New:: cannot create object array" << endl;
166 		return false;
167 	}
168 
169 	// Load the tiles
170 	if(!LoadTheme(_theme))
171 	{
172 		errors("CMap::New:: ERROR: cannot create titles/theme\n");
173 		return false;
174 	}
175 
176 	// Create the surface
177 	if(!CreateSurface())
178 	{
179 		errors("CMap::New:: ERROR: cannot create surface\n");
180 		return false;
181 	}
182 
183 	// Create the pixel flags
184 	if(!CreatePixelFlags())
185 	{
186 		errors("CMap::New:: ERROR: cannot create pixel flags\n");
187 		return false;
188 	}
189 
190     // Create the AI Grid
191     if(!createGrid())
192 	{
193 		errors("CMap::New: ERROR: cannot create AI grid\n");
194 		return false;
195 	}
196 
197 	// Create collision grid
198 	if (!createCollisionGrid())  {
199 		errors("CMap::New: ERROR: cannot create collision grid\n");
200 		return false;
201 	}
202 
203 	Created = true;
204 
205 	return true;
206 }
207 
208 ///////////////////
209 // Create a new map
New(uint _width,uint _height,const std::string & _theme,uint _minimap_w,uint _minimap_h)210 bool CMap::New(uint _width, uint _height, const std::string& _theme, uint _minimap_w, uint _minimap_h)
211 {
212 	NumObjects = 0;
213     nTotalDirtCount = 0;
214     //sRandomLayout.bUsed = false;
215 
216 	// Create the map
217 	if (!Create(_width, _height, _theme, _minimap_w, _minimap_h))
218 		return false;
219 
220 	// Place default tiles
221 	TileMap();
222 
223 	// Update the mini map
224 	UpdateMiniMap();
225 
226     // Calculate the total dirt count
227     CalculateDirtCount();
228 
229     // Calculate the grid
230     calculateGrid();
231 
232 	Created = true;
233 
234 	return true;
235 }
236 
237 
238 ///////////////////
239 // Clear the map
Clear()240 void CMap::Clear()
241 {
242 	TileMap();
243 }
244 
245 ///////////////////
246 // Load the theme
LoadTheme(const std::string & _theme)247 bool CMap::LoadTheme(const std::string& _theme)
248 {
249 	// Already loaded
250 	if (Theme.name == _theme /* && sRandomLayout.szTheme == _theme */) {
251 		notes << "LoadTheme: Theme " << _theme << " already loaded" << endl;
252 	} else {
253 		const std::string thmdir = "data/themes/" + _theme;
254 		const std::string thmfile = thmdir + "/theme.txt";
255 
256 		Theme.name = _theme;
257 		//sRandomLayout.szTheme = _theme;
258 
259 		LOAD_IMAGE(Theme.bmpBacktile, thmdir + "/Backtile.png");
260 		LOAD_IMAGE(Theme.bmpFronttile, thmdir + "/Fronttile.png");
261 
262 
263 		// Stones
264 		ReadInteger(thmfile, "General", "NumStones", &Theme.NumStones, 0);
265 
266 		for(int n=0;n<Theme.NumStones;n++) {
267 			LOAD_IMAGE(Theme.bmpStones[n], thmdir + "/Stone" + itoa(n+1) + ".png");
268 			SetColorKey(Theme.bmpStones[n].get());
269 		}
270 
271 
272 		// Holes
273 		for(int n=0;n<5;n++) {
274 			LOAD_IMAGE(Theme.bmpHoles[n], thmdir + "/Hole" + itoa(n+1) + ".png");
275 			SetColorKey(Theme.bmpHoles[n].get(), 0, 0, 0); // use black as colorkey
276 		}
277 
278 		// Calculate the default colour from a non-pink, non-black colour in the hole image
279 		LOCK_OR_FAIL(Theme.bmpFronttile);
280 		Theme.iDefaultColour = Color(Theme.bmpFronttile->format, GetPixel(Theme.bmpFronttile.get(),0,0));
281 		UnlockSurface(Theme.bmpFronttile);
282 		SmartPointer<SDL_Surface> hole = Theme.bmpHoles[0];
283 		LOCK_OR_FAIL(hole);
284 		if(hole.get()) {
285 			for(int y=0; y<hole.get()->h; y++) {
286 				for(int x=0; x<hole.get()->w; x++) {
287 					Color pixel = Color(hole.get()->format, GetPixel(hole.get(),x,y));
288 					if(pixel != tLX->clBlack && pixel != tLX->clPink)  {
289 						Theme.iDefaultColour = pixel;
290 						break;
291 					}
292 				}
293 			}
294 		}
295 		UnlockSurface(hole);
296 
297 
298 		// Misc
299 		ReadInteger(thmfile, "General", "NumMisc", &Theme.NumMisc, 0);
300 		for(int n = 0; n < Theme.NumMisc; n++) {
301 			LOAD_IMAGE(Theme.bmpMisc[n], thmdir + "/misc" + itoa(n+1) + ".png");
302 			SetColorKey(Theme.bmpMisc[n].get());
303 		}
304 	}
305 
306     // Load the green dirt mask
307 	if(bmpGreenMask.get() == NULL)
308 		LOAD_IMAGE(bmpGreenMask, std::string("data/gfx/greenball.png"));
309 
310 	return true;
311 }
312 
313 
314 	typedef std::vector<std::string> themelist;
315 	class ThemesCounter { public:
316 		themelist* themes;
ThemesCounter(themelist * t)317 		ThemesCounter(themelist* t) : themes(t) {}
operator ()(const std::string & dir)318 		inline bool operator() (const std::string& dir) {
319 			size_t pos = findLastPathSep(dir);
320 			std::string theme = dir.substr(pos+1);
321 			if(CMap::validateTheme(theme))
322 				themes->push_back(theme);
323 			return true;
324 		}
325 	};
326 
327 ///////////////////
328 // Finds a theme at random and returns the name
findRandomTheme()329 std::string CMap::findRandomTheme() {
330     // Find directories in the theme dir
331 	themelist themes;
332 
333     // Count the number of themes
334 	ThemesCounter counter(&themes);
335 	FindFiles(counter, "data/themes", false, FM_DIR);
336 
337 	if(themes.size() == 0) {
338 		// If we get here, then default to dirt
339 		notes("CMap::findRandomTheme(): no themes found\n");
340 		notes("                         Defaulting to \"dirt\"\n");
341 		return "dirt";
342 	}
343 
344     // Get a random number
345     int t = GetRandomInt((int)themes.size()-1);
346     return themes[t];
347 }
348 
349 
350 ///////////////////
351 // Checks if a theme is a valid theme
validateTheme(const std::string & name)352 bool CMap::validateTheme(const std::string& name) {
353     // Does simple checks to see if the main files exists
354     // Ie 'backtile.png' 'fronttile.png' & 'theme.txt'
355 
356     std::string thm,buf;
357 
358 	thm = std::string("data/themes/") + name;
359 
360     // Backtile.png
361     buf = thm + "/backtile.png";
362     if (!IsFileAvailable(buf,false))
363         return false;
364 
365     // Fronttile.png
366     buf = thm + "/fronttile.png";
367     if (!IsFileAvailable(buf,false))
368         return false;
369 
370     // Theme.txt
371     buf = thm + "/theme.txt";
372     if (!IsFileAvailable(buf,false))
373         return false;
374 
375     // All 3 files are good
376 
377     return true;
378 }
379 
380 
381 ///////////////////
382 // Creates the level surface
CreateSurface()383 bool CMap::CreateSurface()
384 {
385 	SDL_PixelFormat *fmt = getMainPixelFormat();
386 	if(fmt == NULL)
387 		errors("CMap::CreateSurface: ERROR: fmt is nothing\n");
388 
389 	bmpImage = gfxCreateSurface(Width, Height);
390 	if(bmpImage.get() == NULL) {
391 		SetError("CMap::CreateSurface(): bmpImage creation failed, perhaps out of memory");
392 		return false;
393 	}
394 
395 #ifdef _AI_DEBUG
396 	bmpDebugImage = gfxCreateSurface(Width*2, Height*2);
397 	if (bmpDebugImage.get() == NULL)  {
398 		SetError("CMap::CreateSurface(): bmpDebugImage creation failed perhaps out of memory");
399 		return false;
400 	}
401 
402 	SetColorKey(bmpDebugImage.get());
403 	FillSurfaceTransparent(bmpDebugImage.get());
404 #endif
405 
406 	bmpDrawImage = gfxCreateSurface(Width*2, Height*2);
407 	if(bmpDrawImage.get() == NULL) {
408 		SetError("CMap::CreateSurface(): bmpDrawImage creation failed, perhaps out of memory");
409 		return false;
410 	}
411 
412 	bmpBackImage = gfxCreateSurface(Width, Height);
413 	if(bmpBackImage.get() == NULL) {
414 		SetError("CMap::CreateSurface(): bmpBackImage creation failed, perhaps out of memory");
415 		return false;
416 	}
417 
418 	bmpMiniMap = gfxCreateSurface(MinimapWidth, MinimapHeight);
419 	if(bmpMiniMap.get() == NULL) {
420 		SetError("CMap::CreateSurface(): bmpMiniMap creation failed, perhaps out of memory");
421 		return false;
422 	}
423 
424     bmpShadowMap = gfxCreateSurface(Width, Height);
425 	if(bmpShadowMap.get() == NULL) {
426 		SetError("CMap::CreateSurface(): bmpShadowMap creation failed, perhaps out of memory");
427 		return false;
428 	}
429 
430 	return true;
431 }
432 
433 ////////////////////
434 // Updates an area according to pixel flags, recalculates minimap, draw image, pixel flags and shadow
UpdateArea(int x,int y,int w,int h,bool update_image)435 void CMap::UpdateArea(int x, int y, int w, int h, bool update_image)
436 {
437 	if(bDedicated) return;
438 
439 	int i, j;
440 
441 	// When drawing shadows, we have to update a bigger area
442 	int shadow_update = tLXOptions->bShadows ? SHADOW_DROP : 0;
443 
444 	x -= shadow_update;
445 	y -= shadow_update;
446 	w += 2 * shadow_update;
447 	h += 2 * shadow_update;
448 
449 
450 	// Clipping
451 	if (!ClipRefRectWith(x, y, w, h, (SDLRect&)bmpImage.get()->clip_rect))
452 		return;
453 
454 	// Grid
455 	lockFlags();
456 
457 	// Update the bmpImage according to pixel flags
458 	if (update_image)  {
459 
460 		// Update
461 		if( bmpBackImageHiRes.get() )
462 		{
463 			LOCK_OR_QUIT(bmpDrawImage);
464 			LOCK_OR_QUIT(bmpBackImageHiRes);
465 			Uint8 *img_pixel, *back_pixel;
466 			Uint16 ImgRowStep, FlagsRowStep, ImgRowSize;
467 			uchar *pf;
468 			byte bpp = bmpDrawImage.get()->format->BytesPerPixel;
469 			byte bppX2 = bpp * 2;
470 
471 			img_pixel = (Uint8 *)bmpDrawImage.get()->pixels + y * 2 * bmpDrawImage.get()->pitch + x * 2 * bpp;
472 			back_pixel = (Uint8 *)bmpBackImageHiRes.get()->pixels + y * 2 * bmpBackImageHiRes.get()->pitch + x * 2 * bpp;
473 			pf = PixelFlags + y * Width + x;
474 
475 			ImgRowSize = bmpDrawImage.get()->pitch;
476 			ImgRowStep = ImgRowSize * 2 - (w * bpp * 2);
477 			FlagsRowStep = Width - w;
478 
479 			for (i = h; i; --i)  {
480 				for (j = w; j; --j)  {
481 					if (*pf & PX_EMPTY) // Empty pixel - copy from the background image
482 					{
483 						memcpy(img_pixel, back_pixel, bppX2);
484 						memcpy(img_pixel + ImgRowSize, back_pixel + ImgRowSize, bppX2);
485 					}
486 
487 					img_pixel += bppX2;
488 					back_pixel += bppX2;
489 					pf++;
490 				}
491 
492 				img_pixel += ImgRowStep;
493 				back_pixel += ImgRowStep;
494 				pf += FlagsRowStep;
495 			}
496 			UnlockSurface(bmpDrawImage);
497 			UnlockSurface(bmpBackImageHiRes);
498 		}
499 		else
500 		{
501 			LOCK_OR_QUIT(bmpImage);
502 			LOCK_OR_QUIT(bmpBackImage);
503 
504 			// Init the variables
505 			Uint8 *img_pixel, *back_pixel;
506 			Uint16 ImgRowStep, BackRowStep, FlagsRowStep;
507 			uchar *pf;
508 			byte bpp = bmpImage.get()->format->BytesPerPixel;
509 
510 			img_pixel = (Uint8 *)bmpImage.get()->pixels + y * bmpImage.get()->pitch + x * bpp;
511 			back_pixel = (Uint8 *)bmpBackImage.get()->pixels + y * bmpBackImage.get()->pitch + x * bpp;
512 			pf = PixelFlags + y * Width + x;
513 
514 			ImgRowStep = bmpImage.get()->pitch - (w * bpp);
515 			BackRowStep = bmpBackImage.get()->pitch - (w * bpp);
516 			FlagsRowStep = Width - w;
517 			for (i = h; i; --i)  {
518 				for (j = w; j; --j)  {
519 					if (*pf & PX_EMPTY) // Empty pixel - copy from the background image
520 						memcpy(img_pixel, back_pixel, bpp);
521 
522 					img_pixel += bpp;
523 					back_pixel += bpp;
524 					pf++;
525 				}
526 
527 				img_pixel += ImgRowStep;
528 				back_pixel += BackRowStep;
529 				pf += FlagsRowStep;
530 			}
531 			UnlockSurface(bmpImage);
532 			UnlockSurface(bmpBackImage);
533 		}
534 	}
535 
536 	unlockFlags();
537 
538 	// Update collision grid
539 	calculateCollisionGridArea(x, y, w, h);
540 
541 	// Apply shadow
542 	ApplyShadow(x - shadow_update, y - shadow_update, w + 2 * shadow_update, h + 2 * shadow_update);
543 
544 	// Update draw image
545 	UpdateDrawImage(x, y, w, h);
546 
547 	// Update minimap
548 	UpdateMiniMapRect(x - shadow_update - 10, y - shadow_update - 10, w + 2 * shadow_update + 20, h + 2 * shadow_update + 20);
549 }
550 
551 
552 
Resample2_getColor(SDL_Surface * bmpSrc,int sx,int sy)553 inline Color Resample2_getColor(SDL_Surface* bmpSrc, int sx, int sy) {
554 	if(sx < 0 || sx >= bmpSrc->w || sy < 0 || sy >= bmpSrc->h) return Color(0,0,0);
555 	return Color( bmpSrc->format, GetPixel(bmpSrc, sx, sy) );
556 }
557 
Resample2_isDominantColor(SDL_Surface * bmpSrc,int dx,int dy)558 inline bool Resample2_isDominantColor(SDL_Surface* bmpSrc, int dx, int dy) {
559 	Color baseC = Resample2_getColor(bmpSrc, dx, dy);
560 	Color otherC;
561 	int count = 0, otherColCount = 0;
562 
563 	Color curC;
564 
565 	for(short x = -1; x <= 1; x += 1) {
566 		for(short y = (x == 0) ? -1 : 0; y <= 1; y += 2) {
567 			curC = Resample2_getColor(bmpSrc, dx + x, dy + y);
568 
569 			if(baseC == curC) count++;
570 			else {
571 				if(otherC != curC) {
572 					otherColCount++;
573 					otherC = curC;
574 				}
575 			}
576 
577 		}
578 	}
579 
580 	return count >= 3 || (count == 2 && otherColCount >= 2);
581 }
582 
DrawImageResampled2(SDL_Surface * bmpDest,SDL_Surface * bmpSrc,int sx,int sy,int w,int h)583 void DrawImageResampled2(SDL_Surface* bmpDest, SDL_Surface* bmpSrc, int sx, int sy, int w, int h) {
584 	if (!ClipRefRectWith(sx, sy, w, h, (SDLRect&)bmpSrc->clip_rect))
585 		return;
586 
587 	int dx2 = sx*2 + w*2;
588 	int dy2 = sy*2 + h*2;
589 	for(int dy = sy*2; dy < dy2; dy++) {
590 		for(int dx = sx*2; dx < dx2; dx++) {
591 
592 			Color col;
593 
594 			if(dx % 2 == 0 && dy % 2 == 0)
595 				{ col = Resample2_getColor(bmpSrc, dx/2, dy/2); }
596 			else if(dx % 2 == 1 && dy % 2 == 0)
597 				{ col = Resample2_getColor(bmpSrc, dx/2, dy/2) * 0.5 + Resample2_getColor(bmpSrc, (dx + 1)/2, dy/2) * 0.5; }
598 			else if(dx % 2 == 0 && dy % 2 == 1)
599 				{ col = Resample2_getColor(bmpSrc, dx/2, dy/2) * 0.5 + Resample2_getColor(bmpSrc, dx/2, (dy - 1)/2) * 0.5; }
600 			else
601 				{ col =
602 					Resample2_getColor(bmpSrc, dx/2, dy/2) * 0.25 + Resample2_getColor(bmpSrc, dx/2, (dy - 1)/2) * 0.25 +
603 					Resample2_getColor(bmpSrc, (dx + 1)/2, dy/2) * 0.25 + Resample2_getColor(bmpSrc, (dx + 1)/2, (dy - 1)/2) * 0.25;
604 				}
605 
606 			Uint8* dst_px = (Uint8 *)bmpDest->pixels + dy * bmpDest->pitch + dx * bmpDest->format->BytesPerPixel;
607 			PutPixelToAddr(dst_px, col.get(bmpDest->format), bmpDest->format->BytesPerPixel);
608 		}
609 	}
610 }
611 
612 
613 ////////////////////
614 // Updates the bmpDrawImage with data from bmpImage
615 // X, Y, W, H apply to bmpImage, not bmpDrawImage
UpdateDrawImage(int x,int y,int w,int h)616 void CMap::UpdateDrawImage(int x, int y, int w, int h)
617 {
618 	if(bDedicated) return;
619 	if( bmpBackImageHiRes.get() )
620 		return;
621 	if(tLXOptions->bAntiAliasing)
622 		DrawImageScale2x(bmpDrawImage.get(), bmpImage, x, y, x*2, y*2, w, h);
623 	else
624 		DrawImageStretch2(bmpDrawImage.get(), bmpImage, x, y, x*2, y*2, w, h);
625 }
626 
627 ////////////////
628 // Set dimensions of the minimap
SetMinimapDimensions(uint _w,uint _h)629 void CMap::SetMinimapDimensions(uint _w, uint _h)
630 {
631 	if(bDedicated) return;
632 
633 	// check if values make sense
634 	if(_w >= 320 || _h >= 240) {
635 		errors << "CMap::SetMinimapDimensions: dimension " << _w << "x" << _h << " too big" << endl;
636 		// default values from Menu_HostShowMinimap
637 		_w = 128; _h = 96;
638 	}
639 
640 	// If already created, reallocate
641 	if (bmpMiniMap.get())  {
642 		bmpMiniMap = gfxCreateSurface(_w, _h);
643 		MinimapWidth = _w;
644 		MinimapHeight = _h;
645 		UpdateMiniMap(true);
646 	// Just set it and CreateSurface will do the rest of the job
647 	} else {
648 		// TODO: why can we assure here that the bmpMiniMap has the right dimension?
649 		MinimapWidth = _w;
650 		MinimapHeight = _h;
651 	}
652 }
653 
654 
655 ///////////////////
656 // Creates the level pixel flags
CreatePixelFlags()657 bool CMap::CreatePixelFlags()
658 {
659 	lockFlags();
660 	PixelFlags = new uchar[Width*Height];
661 	unlockFlags();
662 	if(PixelFlags == NULL) {
663 		SetError("CMap::CreatePixelFlags(): Out of memory");
664 		return false;
665 	}
666 
667 	return true;
668 }
669 
670 
671 ///////////////////
672 // Create the AI Grid
createGrid()673 bool CMap::createGrid() {
674     nGridWidth = 15;
675     nGridHeight = 15;
676 
677     nGridCols = Width/nGridWidth + 1;
678     nGridRows = Height/nGridHeight + 1;
679 
680     lockFlags();
681     GridFlags = new uchar[nGridCols * nGridRows];
682     AbsoluteGridFlags = new uchar[nGridCols * nGridRows];
683     if(GridFlags == NULL || AbsoluteGridFlags == NULL) {
684 		if(GridFlags) delete[] GridFlags;
685 		GridFlags = NULL;
686 		if(AbsoluteGridFlags) delete[] AbsoluteGridFlags;
687 		AbsoluteGridFlags = NULL;
688 		unlockFlags();
689         SetError("CMap::CreateGrid(): Out of memory");
690         return false;
691     }
692 	memset(GridFlags,PX_EMPTY,nGridCols*nGridRows*sizeof(uchar));
693 	memset(AbsoluteGridFlags,PX_EMPTY,nGridCols*nGridRows*sizeof(uchar));
694 	unlockFlags();
695 
696     return true;
697 }
698 
699 
700 ///////////////////
701 // Calculate the grid
calculateGrid()702 void CMap::calculateGrid()
703 {
704 	lockFlags();
705     for(uint y=0; y<Height; y+=nGridHeight)  {
706         for(uint x=0; x<Width; x+=nGridWidth) {
707             calculateGridCell(x,y, false);
708         }
709     }
710     unlockFlags();
711 }
712 
713 
714 ///////////////////
715 // Calculate a single grid cell
716 // x & y are pixel locations, not grid cell locations
717 // WARNING: not thread-safe (the caller has to ensure the threadsafty!)
calculateGridCell(int x,int y,bool bSkipEmpty)718 void CMap::calculateGridCell(int x, int y, bool bSkipEmpty)
719 {
720     int i = x / nGridWidth;
721     int j = y / nGridHeight;
722 
723     if(i < 0 || j < 0 || i >= nGridCols || j >= nGridRows) return;
724 
725     x = i * nGridWidth;
726     y = j * nGridHeight;
727 
728     uchar *cell = GridFlags + j*nGridCols + i;
729     uchar *abs_cell = AbsoluteGridFlags + j*nGridCols + i;
730 
731     // Skip empty cells?
732     if(bSkipEmpty && *cell == PX_EMPTY)
733         return;
734 
735     int dirtCount = 0;
736     int rockCount = 0;
737 
738 	int clip_h = MIN(y + nGridHeight, Height);
739 	int clip_w = MIN(x + nGridWidth, Width);
740 	uchar *pf;
741 
742     // Go through every pixel in the cell and get a solid flag count
743     for(int b = y; b < clip_h; b++) {
744 
745         pf = PixelFlags + b*Width + x;
746         for(int a = x; a < clip_w; a++, pf++) {
747 
748             if(*pf & PX_DIRT)
749                 dirtCount++;
750             if(*pf & PX_ROCK)
751                 rockCount++;
752         }
753     }
754 
755     *abs_cell = PX_EMPTY;
756     if(dirtCount > 0)
757     	*abs_cell |= PX_DIRT;
758     if(rockCount > 0)
759     	*abs_cell |= PX_ROCK;
760 
761     int size = nGridWidth*nGridHeight / 10;
762 
763     // If the dirt or rock count is greater than a 10th, the cell is flagged
764     *cell = PX_EMPTY;
765     if(dirtCount > size)
766         *cell = PX_DIRT;
767     if(rockCount > size)    // Rock overrides dirt
768         *cell = PX_ROCK;
769 }
770 
771 /////////////////////
772 // Create the grid used for fast collision checks
createCollisionGrid()773 bool CMap::createCollisionGrid()
774 {
775 	CollisionGrid = new uchar[Width * Height];
776 	return true;
777 }
778 
getCollGridCellH() const779 int CMap::getCollGridCellH() const {
780 	return 10;
781 }
782 
getCollGridCellW() const783 int CMap::getCollGridCellW() const {
784 	return 10;
785 }
786 
calculateCollisionGridArea(int x,int y,int w,int h)787 void CMap::calculateCollisionGridArea(int x, int y, int w, int h)
788 {
789 	const int cw = getCollGridCellW();
790 	const int ch = getCollGridCellH();
791 
792 #define GRID_CELL(X, Y)  (CollisionGrid + (Y) * Width + (X))
793 #define PIXEL_FLAG(X, Y)  (PixelFlags + (Y) * Width + (X))
794 #define CHECK(X, Y)  if ((X) >= (int)Width || (X) < 0 || (Y) >= (int)Height || (Y) < 0)  { errors << "CMap::calculateCollisionGridArea(): Calculating over map borders" << endl; RaiseDebugger(); }
795 
796 	// Top part
797 	for (int j = y; j < ch; j++)
798 		for (int i = x; i < x + w; i++)  {
799 			CHECK(i, j);
800 			*GRID_CELL(i, j) = 1;
801 		}
802 
803 	// Bottom part
804 	for (int j = Height - ch - 1; j < y + h; j++)
805 		for (int i = x; i < x + w; i++)  {
806 			CHECK(i ,j);
807 			*GRID_CELL(i, j) = 1;
808 		}
809 
810 	// Left part
811 	const int tb = MIN(y + h, Height - ch - 1);
812 	for (int j = MAX(ch, y); j < tb; j++)
813 		for (int i = x; i < cw; i++)  {
814 			CHECK(i, j);
815 			*GRID_CELL(i, j) = 1;
816 		}
817 
818 	// Right part
819 	for (int j = MAX(ch, y); j < tb; j++)
820 		for (int i = Width - cw - 1; i < x + w; i++)  {
821 			CHECK(i, j);
822 			*GRID_CELL(i, j) = 1;
823 		}
824 
825 	// The rest
826 	SDL_Rect clip = { (SDLRect::Type) cw, (SDLRect::Type) ch, (SDLRect::TypeS) (Width - 2 * cw - 1), (SDLRect::TypeS) (Height - 2 * ch - 1) };
827 	if (!ClipRefRectWith(x, y, w, h, (SDLRect&)clip))
828 		return;
829 
830 #define PX_SOLID (PX_DIRT|PX_ROCK)
831 
832 	const int cwh = cw/2;
833 	const int chh = ch/2;
834 	for (int j = y; j < y + h; ++j)
835 		for (int i = x; i < x + w; ++i)  {
836 
837 			// If this pixel is solid, great, we can just move on
838 			if ((*PIXEL_FLAG(i, j) & PX_SOLID))  {
839 				// Also set & skip few following pixels, because they would become solid because of this one anyway
840 				CHECK(i + cwh - 1, j);
841 				memset(GRID_CELL(i, j), 1, cwh);  // HINT: this is always safe because we never get to the real bounds here
842 				i += cwh - 1;
843 				continue;
844 			}
845 
846 			// First try some special pixels to speed this up a bit in case the cell is solid
847 			if ((*PIXEL_FLAG(i - cwh, j - chh) & PX_SOLID) ||
848 				(*PIXEL_FLAG(i + cwh, j - chh) & PX_SOLID) ||
849 				(*PIXEL_FLAG(i - cwh, j + chh) & PX_SOLID) ||
850 				(*PIXEL_FLAG(i + cwh, j + chh) & PX_SOLID))  {
851 				CHECK(i, j);
852 				*GRID_CELL(i, j) = 1;
853 				continue;
854 			}
855 
856 			// Check the whole cell
857 			*GRID_CELL(i, j) = 0;
858 			for (int cy = j - chh; cy < j + chh; cy++)
859 				for (int cx = i - cwh; cx < i + cwh; cx++)  {
860 					CHECK(cx, cy);
861 					if (*PIXEL_FLAG(cx, cy) & PX_SOLID)  {
862 						CHECK(i, j);
863 						*GRID_CELL(i, j) = 1;
864 						break;
865 					}
866 				}
867 		}
868 
869 	//notes << "Calculating collision grid took " << df.seconds() << " seconds." << endl;
870 
871 #undef PIXEL_FLAG
872 #undef CHECK
873 #undef PX_SOLID
874 #undef GRID_CELL
875 }
876 
877 
878 ///////////////////
879 // Tile the map
TileMap()880 void CMap::TileMap()
881 {
882 	if(bDedicated) return;
883 
884 	if(!bmpImage.get()) {
885 		errors << "CMap::TileMap: map-image not loaded" << endl;
886 		return;
887 	}
888 
889 	uint x,y;
890 
891 	// Place the tiles
892 
893 	// Place the first row
894 	for(y=0;y<Height;y+=Theme.bmpFronttile.get()->h) {
895 		for(x=0;x<Width;x+=Theme.bmpFronttile.get()->w) {
896 			DrawImage(bmpImage.get(), Theme.bmpFronttile,x,y);
897 		}
898 	}
899 
900 	for(y=0;y<Height;y+=Theme.bmpBacktile.get()->h) {
901 		for(x=0;x<Width;x+=Theme.bmpBacktile.get()->w) {
902 			DrawImage(bmpBackImage.get(), Theme.bmpBacktile,x,y);
903 		}
904 	}
905 
906 	// Update the draw image
907 	UpdateDrawImage(0, 0, Width, Height);
908 
909 	// Set the pixel flags
910 	lockFlags();
911 	memset(PixelFlags,PX_DIRT,Width*Height*sizeof(uchar));
912 	unlockFlags();
913 
914 	// Update collision grid
915 	memset(CollisionGrid, 1, Width * Height * sizeof(uchar));
916 
917     // Calculate the shadowmap
918     CalculateShadowMap();
919 
920     // Calculate the total dirt count
921     nTotalDirtCount = Width * Height;
922 
923 	bMiniMapDirty = true;
924 }
925 
926 
927 ///////////////////
928 // Calculate the dirt count in the level
CalculateDirtCount()929 void CMap::CalculateDirtCount()
930 {
931     nTotalDirtCount = 0;
932 	uint n;
933 	const uint size = Width * Height;
934 
935 	for (n = 0; n < size; n++)
936 		if(PixelFlags[n] & PX_DIRT)
937 			nTotalDirtCount++;
938 }
939 
940 
getGroundPos(CMap * cMap,const CVec & pos,CVec * ret,uchar badPX)941 static bool getGroundPos(CMap* cMap, const CVec& pos, CVec* ret, uchar badPX) {
942 	// TODO: optimise
943 
944 	long x = long(pos.x), y = long(pos.y);
945 
946 	uchar px = cMap->GetPixelFlag(x,y);
947 	if(px & badPX) { // we are already bad, go up
948 		while(y >= 0) {
949 			px = cMap->GetPixelFlag(x,y);
950 			if(!(px & badPX)) { // found place
951 				*ret = CVec((float)x, (float)y);
952 				return true;
953 			}
954 			y--;
955 		}
956 		// nothing found
957 		return false;
958 	}
959 
960 	// go down
961 	while((unsigned long)y < cMap->GetHeight()) {
962 		px = cMap->GetPixelFlag(x,y);
963 		if(px & badPX) { // found place
964 			*ret = CVec((float)x, (float)(y - 1));
965 			return true;
966 		}
967 		y++;
968 	}
969 
970 	// everything free
971 	*ret = CVec((float)x, (float)(cMap->GetHeight() - 1));
972 	return true;
973 }
974 
groundPos(const CVec & pos)975 CVec CMap::groundPos(const CVec& pos) {
976 	CVec ret = pos;
977 	lockFlags(false);
978 
979 	if(getGroundPos(this, pos, &ret, PX_DIRT|PX_ROCK)) {
980 		unlockFlags(false);
981 		return ret;
982 	}
983 
984 	getGroundPos(this, pos, &ret, PX_ROCK);
985 
986 	unlockFlags(false);
987 	return ret;
988 }
989 
990 
991 ///////////////////
992 // Draw the map
Draw(SDL_Surface * bmpDest,const SDL_Rect & rect,int worldX,int worldY)993 void CMap::Draw(SDL_Surface *bmpDest, const SDL_Rect& rect, int worldX, int worldY)
994 {
995 	if(!bmpDrawImage.get() || !bmpDest) return; // safty
996 
997 	if(!cClient->getGameLobby()->features[FT_InfiniteMap]) {
998 
999 		// Draw black borders around the level
1000 		/*
1001 		if (x < 0) {
1002 			rectfill(where, 0, 0, -x * 2, where->h, 0);
1003 		}
1004 		if (bmpDrawImage.get() && x * 2 > bmpDrawImage.get()->w - where->w) {
1005 			rectfill(where, bmpDrawImage.get()->w - x * 2, 0, where->w, where->h, 0);
1006 		}
1007 		if (y < 0) {
1008 			rectfill(where, 0, 0, where->w, -y * 2, 0);
1009 		}
1010 		if (bmpDrawImage.get() && y * 2 > bmpDrawImage.get()->h - where->h) {
1011 			rectfill(where, 0, bmpDrawImage.get()->h - y * 2, where->w, where->h, 0);
1012 		}
1013 		*/
1014 
1015 		DrawImageAdv(bmpDest, bmpDrawImage, worldX*2, worldY*2,rect.x,rect.y,rect.w,rect.h);
1016 #ifdef _AI_DEBUG
1017 		DrawImageAdv(bmpDest, bmpDebugImage, worldX*2, worldY*2,rect.x,rect.y,rect.w,rect.h);
1018 #endif
1019 	}
1020 	else {
1021 		DrawImageTiled(bmpDest, bmpDrawImage, worldX*2, worldY*2, rect.w, rect.h, rect.x, rect.y, rect.w, rect.h);
1022 #ifdef _AI_DEBUG
1023 		DrawImageTiled(bmpDest, bmpDebugImage, worldX*2, worldY*2, rect.w, rect.h,rect.x,rect.y,rect.w,rect.h);
1024 #endif
1025 	}
1026 }
1027 
1028 ///////////////////
1029 // Draw the map
Draw(SDL_Surface * bmpDest,CViewport * view)1030 void CMap::Draw(SDL_Surface * bmpDest, CViewport *view)
1031 {
1032 	SDL_Rect destRect = {(SDLRect::Type) view->GetLeft(), (SDLRect::Type) view->GetTop(),
1033 						(SDLRect::TypeS) (view->GetWidth()*2), (SDLRect::TypeS) (view->GetHeight()*2) };
1034 	Draw(bmpDest, destRect, view->GetWorldX(), view->GetWorldY());
1035 }
1036 
1037 struct ShadowClipInfo  {
1038 	int map_x;
1039 	int map_y;
1040 	int dest_x;
1041 	int dest_y;
1042 	int obj_x;
1043 	int obj_y;
1044 	int w;
1045 	int h;
1046 };
1047 
1048 
1049 /////////////////////
1050 // Helper function for DrawObjectShadow
ClipShadow(SDL_Surface * bmpDest,SDL_Surface * bmpObj,SDL_Surface * bmpShadowMap,int sx,int sy,int w,int h,CViewport * view,int wx,int wy)1051 ShadowClipInfo ClipShadow(SDL_Surface * bmpDest, SDL_Surface * bmpObj, SDL_Surface *bmpShadowMap,
1052 						  int sx, int sy, int w, int h, CViewport *view, int wx, int wy)
1053 {
1054 	ShadowClipInfo res;
1055 
1056 	res.dest_x = ((wx + SHADOW_DROP - view->GetWorldX()) * 2) + view->GetLeft();
1057 	res.dest_y = ((wy + SHADOW_DROP - view->GetWorldY()) * 2) + view->GetTop();
1058 
1059 	// Calculate positions and clipping
1060 	int clip_shift_x = - MIN(0, wx - view->GetWorldX() + SHADOW_DROP) * 2;  // When we clip left/top on dest, we have to
1061 	int clip_shift_y = - MIN(0, wy - view->GetWorldY() + SHADOW_DROP) * 2;  // "shift" the coordinates on other surfaces, too
1062 
1063 	res.obj_x = sx + clip_shift_x;
1064 	res.obj_y = sy + clip_shift_y;
1065 
1066 	res.map_x = wx + SHADOW_DROP + clip_shift_x;
1067 	res.map_y = wy + SHADOW_DROP + clip_shift_y;
1068 	int shadowmap_real_w = w / 2;
1069 	int shadowmap_real_h = h / 2;
1070 
1071 	// Clipping
1072 	ClipRefRectWith(res.dest_x, res.dest_y, w, h, (SDLRect&)bmpDest->clip_rect);
1073 	ClipRefRectWith(res.obj_x, res.obj_y, w, h, (SDLRect&)bmpObj->clip_rect);
1074 	ClipRefRectWith(res.map_x, res.map_y, shadowmap_real_w, shadowmap_real_h, (SDLRect&)bmpShadowMap->clip_rect);
1075 
1076 	res.w = MIN(w, shadowmap_real_w * 2);
1077 	res.h = MIN(h, shadowmap_real_h * 2);
1078 
1079 	return res;
1080 }
1081 
1082 ///////////////////
1083 // Checks if the given area is free (no rock/dirt inside)
CheckAreaFree(int x,int y,int w,int h)1084 bool CMap::CheckAreaFree(int x, int y, int w, int h)
1085 {
1086 	// If there is some kind of clipping, the area is not free
1087 	if (x < 0 || x + w >= (int)Width || y < 0 || y + h >= (int)Height)
1088 		return false;
1089 
1090 	// Go through the area collision grid cells and check if they are free
1091 	int xr = x + getCollGridCellW() / 2;
1092 	int yr = y + getCollGridCellH() / 2;
1093 	for (int j = yr; j < y + h; j += getCollGridCellH())
1094 		for (int i = xr; i < x + w; i += getCollGridCellW())
1095 			if (CollisionGrid[j * Width + i])
1096 				return false;
1097 
1098 	return true;
1099 }
1100 
1101 ///////////////////
1102 // Draw an object's shadow
DrawObjectShadow(SDL_Surface * bmpDest,SDL_Surface * bmpObj,SDL_Surface * bmpObjShadow,int sx,int sy,int w,int h,CViewport * view,int wx,int wy)1103 void CMap::DrawObjectShadow(SDL_Surface * bmpDest, SDL_Surface * bmpObj, SDL_Surface * bmpObjShadow, int sx, int sy, int w, int h, CViewport *view, int wx, int wy)
1104 {
1105 	// TODO: simplify, possibly think up a better algo...
1106 	// TODO: reduce local variables to 5
1107 	if(!bmpDest) {
1108 		errors << "CMap::DrawObjectShadow: bmpDest not set" << endl;
1109 		return;
1110 	}
1111 
1112 	if(!bmpObj) {
1113 		errors << "CMap::DrawObjectShadow: bmpObj not set" << endl;
1114 		return;
1115 	}
1116 
1117 	if(!view) {
1118 		errors << "CMap::DrawObjectShadow: view not set" << endl;
1119 		return;
1120 	}
1121 
1122 	if(!bmpShadowMap.get()) {
1123 		errors << "CMap::DrawObjectShadow: shadow map not initialised" << endl;
1124 		return;
1125 	}
1126 
1127 	const ShadowClipInfo i = ClipShadow(bmpDest, bmpObj, bmpShadowMap.get(), sx, sy, w, h, view, wx, wy);
1128 	if (!i.h || !i.w)
1129 		return;
1130 
1131 	// If we are small and the area around is empty, just use the shadow image instead
1132 	// HINT: this is not really exact, it can happen that two shadows overlap
1133 	// This case is however *very* rare and it would be too difficult/slow to check for it here
1134 	if (w <= getCollGridCellW() && h <= getCollGridCellH() &&
1135 		GetCollisionFlag(i.map_x, i.map_y, false) == 0 && bmpObjShadow)  {
1136 
1137 		DrawImageAdv(bmpDest, bmpObjShadow, i.obj_x, i.obj_y, i.dest_x, i.dest_y, i.w, i.h);
1138 		return;
1139 	}
1140 
1141 	// If we are bigger, we have to check the whole area of the shadow
1142 	if (CheckAreaFree(i.map_x, i.map_y, i.w, i.h) && bmpObjShadow)  {
1143 		DrawImageAdv(bmpDest, bmpObjShadow, i.obj_x, i.obj_y, i.dest_x, i.dest_y, i.w, i.h);
1144 		return;
1145 	}
1146 
1147 	// Lock the surfaces
1148 	LOCK_OR_QUIT(bmpDest);
1149 	LOCK_OR_QUIT(bmpObj);
1150 	LOCK_OR_QUIT(bmpShadowMap);
1151 
1152 	// Pixels
1153 	Uint8 *dest_row = (Uint8 *)bmpDest->pixels + (i.dest_y * bmpDest->pitch) + (i.dest_x * bmpDest->format->BytesPerPixel);
1154 	Uint8 *obj_row  = (Uint8 *)bmpObj->pixels + (i.obj_y * bmpObj->pitch) + (i.obj_x * bmpObj->format->BytesPerPixel);
1155 	Uint8 *shadow_row = (Uint8 *)bmpShadowMap->pixels + (i.map_y * bmpShadowMap->pitch) + (i.map_x * bmpShadowMap->format->BytesPerPixel);
1156 	uchar *pf_row = PixelFlags + i.map_y * Width + i.map_x;
1157 
1158 	PixelGet& getter = getPixelGetFunc(bmpObj);
1159 	PixelCopy& copier = getPixelCopyFunc(bmpShadowMap.get(), bmpDest);
1160 
1161 	// Draw the shadow
1162 	for (int loop_y = i.h; loop_y; --loop_y)  {
1163 		uchar *pf = pf_row;
1164 		Uint8 *shadowmap_px = shadow_row;
1165 		Uint8 *obj_px = obj_row;
1166 		Uint8 *dest_px = dest_row;
1167 
1168 		for (int loop_x = i.w; loop_x; --loop_x)  {
1169 
1170 			if (*pf & PX_EMPTY)  { // Don't draw shadow on solid objects
1171 
1172 				// Put pixel if not tranparent
1173 				if (!IsTransparent(bmpObj, getter.get(obj_px)))
1174 					copier.copy(dest_px, shadowmap_px);
1175 			}
1176 
1177 			// Update the pixels & flag
1178 			dest_px		 += bmpDest->format->BytesPerPixel;
1179 			obj_px		 += bmpObj->format->BytesPerPixel;
1180 			shadowmap_px += bmpShadowMap->format->BytesPerPixel * (loop_x & 1); // We draw the shadow doubly stretched -> only 1/2 pixel on shadowmap
1181 			pf			 += loop_x & 1;
1182 		}
1183 
1184 		// Skip to next row
1185 		dest_row	   += bmpDest->pitch;
1186 		obj_row		   += bmpObj->pitch;
1187 		shadow_row	   += bmpShadowMap->pitch * (loop_y & 1); // We draw the shadow doubly stretched -> only 1/2 row on shadowmap
1188 		pf_row		   += Width * (loop_y & 1);
1189 	}
1190 
1191 	UnlockSurface(bmpShadowMap);
1192 	UnlockSurface(bmpDest);
1193 	UnlockSurface(bmpObj);
1194 }
1195 
1196 
1197 ///////////////////
1198 // Draw a pixel sized shadow
DrawPixelShadow(SDL_Surface * bmpDest,CViewport * view,int wx,int wy)1199 void CMap::DrawPixelShadow(SDL_Surface * bmpDest, CViewport *view, int wx, int wy)
1200 {
1201 	if(bDedicated) return;
1202 
1203     wx += SHADOW_DROP;
1204     wy += SHADOW_DROP;
1205 
1206 	// HINT: Clipping is done by DrawImageAdv/GetPixelFlag
1207 
1208 	// Get real coordinates
1209 	// TODO: DrawObjectShadow also doesn't do correct shadow for infinite maps, so let's ignore it here too atm
1210 	//VectorD2<int> p = view->physicToReal(VectorD2<int>(wx,wy), cClient->getGameLobby()->features[FT_InfiniteMap], GetWidth(), GetHeight());
1211 	VectorD2<int> p = view->physicToReal(VectorD2<int>(wx,wy));
1212 
1213 	// NOTE: if we fix shadow for infinite maps here, we cannot use GetPixel this way!
1214 	if( GetPixelFlag(wx, wy /*, cClient->getGameLobby()->features[FT_InfiniteMap]*/) & PX_EMPTY )  {  // We should check all the 4 pixels, but no one will ever notice it
1215 		LOCK_OR_QUIT(bmpShadowMap);
1216 		Color color = Color(bmpShadowMap->format, GetPixel(bmpShadowMap.get(), wx, wy));
1217 		UnlockSurface(bmpShadowMap);
1218 
1219 		DrawRectFill2x2(bmpDest, p.x, p.y, color);
1220 	}
1221 }
1222 
1223 
1224 ///////////////////
1225 // Carve a hole in the map
1226 //
1227 // Returns the number of dirt pixels carved
1228 // IMPORTANT: hole and map must have same gfx format
CarveHole(int size,CVec pos,bool wrapAround)1229 int CMap::CarveHole(int size, CVec pos, bool wrapAround)
1230 {
1231 	// Just clamp it and continue
1232 	size = MAX(size, 0);
1233 	size = MIN(size, 4);
1234 
1235 	// Calculate half
1236 	SmartPointer<SDL_Surface> hole = Theme.bmpHoles[size];
1237 	if (!hole.get())
1238 		return 0;
1239 
1240 	int nNumDirt = 0;
1241 	int w = hole.get()->w;
1242 	int h = hole.get()->h;
1243 	int map_x = (int)pos.x - w / 2;
1244 	int map_y = (int)pos.y - h / 2;
1245 	if (wrapAround)  {
1246 		int map_x2 = map_x;
1247 		int map_y2 = map_y;
1248 		if (map_x <= hole->w / 2)
1249 			map_x2 = map_x + (int)Width;
1250 		if (map_x >= (int)Width - hole->w / 2)
1251 			map_x2 = map_x - (int)Width;
1252 		if (map_y <= hole->h / 2)
1253 			map_y2 = map_y + (int)Height;
1254 		if (map_y >= (int)Height - hole->h / 2)
1255 			map_y2 = map_y - (int)Height;
1256 		if (map_x2 != map_x || map_y2 != map_y)
1257 			this->CarveHole(size, CVec((float)map_x2 + w / 2, (float)map_y2 + h / 2), false);
1258 	}
1259 
1260 	// Clipping
1261 	if (!ClipRefRectWith(map_x, map_y, w, h, (SDLRect&)bmpImage.get()->clip_rect))
1262 		return 0;
1263 
1264 	// Variables
1265 	byte bpp = hole.get()->format->BytesPerPixel;
1266 
1267 	if (!LockSurface(hole))
1268 		return 0;
1269 
1270 	Uint8* hole_px = (Uint8 *)hole.get()->pixels;
1271 	uchar* PixelFlag = PixelFlags + map_y * Width + map_x;
1272 
1273 	int HoleRowStep = hole.get()->pitch - (w * bpp);
1274 	int PixelFlagRowStep = Width - w;
1275 
1276 	// Lock
1277 	lockFlags();
1278 
1279 	if( bmpBackImageHiRes.get() ) // Hi-res image
1280 	{
1281 		if (!LockSurface(bmpDrawImage))
1282 			return 0;
1283 		Uint8* mapimage_px = (Uint8 *)bmpDrawImage.get()->pixels + map_y * 2 * bmpDrawImage.get()->pitch + map_x * bpp * 2;
1284 		int MapImageRowSize = bmpDrawImage.get()->pitch;
1285 		int MapImageRowStep = bmpDrawImage.get()->pitch * 2 - (w * bpp * 2);
1286 		for(int hy = h; hy; --hy)
1287 		{
1288 			for(int hx = w; hx; --hx)
1289 			{
1290 				if (*PixelFlag & PX_DIRT)  // Carve only dirt
1291 				{
1292 					Uint32 CurrentPixel = GetPixelFromAddr(hole_px, bpp);
1293 					// Set the flag to empty
1294 					if(CurrentPixel == tLX->clPink.get(hole.get()->format))
1295 					{
1296 						// Increase the dirt count
1297 						nNumDirt++;
1298 						*PixelFlag = PX_EMPTY;
1299 					}
1300 					else if(CurrentPixel != tLX->clBlack.get(hole.get()->format)) // Put pixels that are not black/pink (eg, brown)
1301 					{
1302 						PutPixelToAddr(mapimage_px, CurrentPixel, bpp);
1303 						PutPixelToAddr(mapimage_px+bpp, CurrentPixel, bpp);
1304 						PutPixelToAddr(mapimage_px+MapImageRowSize, CurrentPixel, bpp);
1305 						PutPixelToAddr(mapimage_px+MapImageRowSize+bpp, CurrentPixel, bpp);
1306 					}
1307 				}
1308 				hole_px += bpp;
1309 				mapimage_px += bpp * 2;
1310 				PixelFlag++;
1311 			}
1312 			hole_px += HoleRowStep;
1313 			mapimage_px += MapImageRowStep;
1314 			PixelFlag += PixelFlagRowStep;
1315 		}
1316 		UnlockSurface(bmpDrawImage);
1317 	}
1318 	else // Low-res image
1319 	{
1320 		if (!LockSurface(bmpImage))
1321 			return 0;
1322 		Uint8* mapimage_px = (Uint8 *)bmpImage.get()->pixels + map_y * bmpImage.get()->pitch + map_x * bpp;
1323 		int MapImageRowStep = bmpImage.get()->pitch - (w * bpp);
1324 		for(int hy = h; hy; --hy)  {
1325 			for(int hx = w; hx; --hx) {
1326 
1327 				// Carve only dirt
1328 				if (*PixelFlag & PX_DIRT)  {
1329 
1330 					Uint32 CurrentPixel = GetPixelFromAddr(hole_px, bpp);
1331 
1332 					// Set the flag to empty
1333 					if(CurrentPixel == tLX->clPink.get(hole.get()->format)) {
1334 
1335 						// Increase the dirt count
1336 						nNumDirt++;
1337 
1338 						*PixelFlag = PX_EMPTY;
1339 
1340 					// Put pixels that are not black/pink (eg, brown)
1341 					} else if(CurrentPixel != tLX->clBlack.get(hole.get()->format))
1342 						PutPixelToAddr(mapimage_px, CurrentPixel, bpp);
1343 				}
1344 
1345 				hole_px += bpp;
1346 				mapimage_px += bpp;
1347 				PixelFlag++;
1348 
1349 			}
1350 
1351 			hole_px += HoleRowStep;
1352 			mapimage_px += MapImageRowStep;
1353 			PixelFlag += PixelFlagRowStep;
1354 		}
1355 		UnlockSurface(bmpImage);
1356 	}
1357 
1358 	unlockFlags();
1359 
1360 	UnlockSurface(hole);
1361 
1362 	if(nNumDirt)  { // Update only when something has been carved
1363 		UpdateArea(map_x, map_y, w, h, true);
1364 
1365 		// Grid
1366 		int i, j;
1367 		for(j = map_y; j < map_y + h; j += nGridHeight)
1368 			for(i = map_x; i < map_x + w; i += nGridWidth)
1369 				calculateGridCell(i, j, true);
1370 	}
1371 
1372     return nNumDirt;
1373 }
1374 
1375 
1376 /*inline void CarveHole_handlePixel(CMap* map, int& nNumDirt, int map_x, int map_y, Uint32 hole_pixel) {
1377 	uchar* px = map->GetPixelFlags() + map_y * map->GetWidth() + map_x;
1378 
1379 	if(*px & PX_DIRT) {
1380 		// Set the flag to empty
1381 		if(hole_pixel == tLX->clPink) {
1382 
1383 			// Increase the dirt count
1384 			nNumDirt++;
1385 
1386 			*px = PX_EMPTY;
1387 
1388 		// Put pixels that are not black/pink (eg, brown)
1389 		} else if(hole_pixel != tLX->clBlack)
1390 			PutPixel(map->GetImage(), map_x, map_y, hole_pixel);
1391 	}
1392 
1393 	if(*px & PX_EMPTY) {
1394 		// redraw background-pixel because perhaps we don't have shadow here any more
1395 		// we will update the shadowed pixel later
1396 		CopyPixel_SameFormat(map->GetImage(), map->GetBackImage(), map_x, map_y);
1397 	}
1398 
1399 }
1400 
1401 class CarveHole_PixelWalker {
1402 public:
1403 	CMap* map; SmartPointer<SDL_Surface> hole; int& nNumDirt;
1404 	int map_left, map_top;
1405 
1406 	CarveHole_PixelWalker(CMap* map_, const SmartPointer<SDL_Surface> & hole_, int& nNumDirt_, int map_left_, int map_top_) :
1407 		map(map_), hole(hole_), nNumDirt(nNumDirt_), map_left(map_left_), map_top(map_top_) {}
1408 
1409 	inline bool operator()(int map_x, int map_y) {
1410 		CarveHole_handlePixel(map, nNumDirt, map_x, map_y, GetPixel(hole, map_x - map_left, map_y - map_top));
1411 		return true;
1412 	}
1413 
1414 };
1415 
1416 
1417 ///////////////////
1418 // Carve a hole in the map
1419 //
1420 // Returns the number of dirt pixels carved
1421 int CMap::CarveHole(int size, CVec pos)
1422 {
1423 	if(size < 0 || size > 4) {
1424 		// Just clamp it and continue
1425 		size = MAX(size, 0);
1426 		size = MIN(size, 4);
1427 	}
1428 
1429 	// Calculate half
1430 	SmartPointer<SDL_Surface> hole = Theme.bmpHoles[size];
1431 	if (!hole)
1432 		return 0;
1433 
1434 	int nNumDirt = 0;
1435 	int map_left = (int)pos.x - (hole->w / 2);
1436 	int map_top = (int)pos.y - (hole->h / 2);
1437 
1438 	lockFlags();
1439 
1440 	if (!LockSurface(hole))
1441 		return 0;
1442 	if (!LockSurface(bmpImage))
1443 		return 0;
1444 	if (!LockSurface(bmpBackImage))
1445 		return 0;
1446 
1447 	walkPixels(ClipRect<int>(&map_left, &map_top, &hole->w, &hole->h), CarveHole_PixelWalker(this, hole, nNumDirt, map_left, map_top));
1448 
1449 	unlockFlags();
1450 
1451 	UnlockSurface(hole);
1452 	UnlockSurface(bmpImage);
1453 	UnlockSurface(bmpBackImage);
1454 
1455 	if(!nNumDirt)
1456 		return 0;
1457 
1458     // Recalculate the grid
1459     lockFlags();
1460 	for(int hx = 0; hx < hole->w; hx += nGridWidth)
1461 		for(int hy = 0; hy < hole->h; hy += nGridHeight) {
1462 			const int x = map_left + hx;
1463 			const int y = map_top + hy;
1464 			calculateGridCell(x, y, true);
1465 		}
1466 	unlockFlags();
1467 
1468 	// Apply a shadow
1469 	ApplyShadow(map_left - 5, map_top - 5, hole->w + 25, hole->h + 25);
1470 
1471 	// Update the draw image
1472 	UpdateDrawImage(map_left - 5, map_top - 5, hole->w + 25, hole->h + 25);
1473 
1474 	UpdateMiniMapRect(map_left, map_top, hole->w, hole->h);
1475 
1476     return nNumDirt;
1477 }*/
1478 
1479 
1480 ///////////////////
1481 // Place a bit of dirt
1482 //
1483 // Returns the number of dirt pixels placed
PlaceDirt(int size,CVec pos)1484 int CMap::PlaceDirt(int size, CVec pos)
1485 {
1486 	SmartPointer<SDL_Surface> hole;
1487 	int dx,dy, sx,sy;
1488 	int x,y;
1489 	int w,h;
1490 	int ix,iy;
1491 	Uint32 pixel, pixel2;
1492 	uchar flag;
1493 
1494     int nDirtCount = 0;
1495 
1496 	// Just clamp it and continue
1497 	size = MAX(size,0);
1498 	size = MIN(size,4);
1499 
1500 
1501 	// Calculate half
1502 	hole = Theme.bmpHoles[size];
1503 	Uint32 pink = tLX->clPink.get(hole.get()->format);
1504 	w = hole.get()->w;
1505 	h = hole.get()->h;
1506 
1507 	sx = (int)pos.x-(hole.get()->w>>1);
1508 	sy = (int)pos.y-(hole.get()->h>>1);
1509 
1510 
1511 	if (!LockSurface(hole))
1512 		return 0;
1513 	if (!LockSurface(Theme.bmpFronttile))
1514 		return 0;
1515 
1516 	Uint8 *p;
1517 	uchar *px;
1518 	Uint8 *p2;
1519 
1520 	short screenbpp = getMainPixelFormat()->BytesPerPixel;
1521 
1522 	// Calculate clipping
1523 	int clip_y = MAX(sy, 0);
1524 	int clip_x = MAX(sx, 0);
1525 	int clip_h = MIN(sy+h, bmpImage.get()->h);
1526 	int clip_w = MIN(sx+w, bmpImage.get()->w);
1527 	int hole_clip_y = -MIN(sy,(int)0);
1528 	int hole_clip_x = -MIN(sx,(int)0);
1529 
1530 	lockFlags();
1531 
1532 	if( bmpBackImageHiRes.get() ) // Hi-res image
1533 	{
1534 		if (!LockSurface(bmpDrawImage))
1535 			return 0;
1536 		// Go through the pixels in the hole, setting the flags to dirt
1537 		int DrawImagePitch = bmpDrawImage.get()->pitch;
1538 		for(y = hole_clip_y, dy = clip_y; dy < clip_h; y++, dy++) {
1539 
1540 			p = (Uint8 *)hole.get()->pixels + y * hole.get()->pitch + hole_clip_x * hole.get()->format->BytesPerPixel;
1541 			px = PixelFlags + dy * Width + clip_x;
1542 			p2 = (Uint8 *)bmpDrawImage.get()->pixels + dy * 2 * bmpDrawImage.get()->pitch + clip_x * 2 * bmpDrawImage.get()->format->BytesPerPixel;
1543 
1544 			for(x=hole_clip_x,dx=clip_x;dx<clip_w;x++,dx++) {
1545 
1546 				pixel = GetPixelFromAddr(p, screenbpp);
1547 				flag = *(uchar *)px;
1548 
1549 				ix = dx % Theme.bmpFronttile.get()->w;
1550 				iy = dy % Theme.bmpFronttile.get()->h;
1551 
1552 				// Set the flag to empty
1553 				if(!IsTransparent(hole.get(), pixel) && !(flag & PX_ROCK)) {
1554                     if( flag & PX_EMPTY )
1555                         nDirtCount++;
1556 
1557 					*(uchar*)px = PX_DIRT;
1558 					pixel2 = GetPixel(Theme.bmpFronttile.get(), ix, iy);
1559 					// Place the dirt image
1560 					PutPixelToAddr(p2, pixel2, screenbpp);
1561 					PutPixelToAddr(p2+screenbpp, pixel2, screenbpp);
1562 					PutPixelToAddr(p2+DrawImagePitch, pixel2, screenbpp);
1563 					PutPixelToAddr(p2+DrawImagePitch+screenbpp, pixel2, screenbpp);
1564 				}
1565 
1566 				// Put pixels that are not black/pink (eg, brown)
1567                 if(!IsTransparent(hole.get(), pixel) && pixel != pink && (flag & PX_EMPTY)) {
1568 					PutPixelToAddr(p2, pixel, screenbpp);
1569 					PutPixelToAddr(p2+screenbpp, pixel, screenbpp);
1570 					PutPixelToAddr(p2+DrawImagePitch, pixel, screenbpp);
1571 					PutPixelToAddr(p2+DrawImagePitch+screenbpp, pixel, screenbpp);
1572                     *(uchar*)px = PX_DIRT;
1573                     nDirtCount++;
1574                 }
1575 
1576 				p += screenbpp;
1577 				p2 += screenbpp * 2;
1578 				px++;
1579 			}
1580 		}
1581 		UnlockSurface(bmpDrawImage);
1582 	}
1583 	else // Low-res image
1584 	{
1585 		if (!LockSurface(bmpImage))
1586 			return 0;
1587 		// Go through the pixels in the hole, setting the flags to dirt
1588 		for(y = hole_clip_y, dy = clip_y; dy < clip_h; y++, dy++) {
1589 
1590 			p = (Uint8 *)hole.get()->pixels + y * hole.get()->pitch + hole_clip_x * hole.get()->format->BytesPerPixel;
1591 			px = PixelFlags + dy * Width + clip_x;
1592 			p2 = (Uint8 *)bmpImage.get()->pixels + dy * bmpImage.get()->pitch + clip_x * bmpImage.get()->format->BytesPerPixel;
1593 
1594 			for(x=hole_clip_x,dx=clip_x;dx<clip_w;x++,dx++) {
1595 
1596 				pixel = GetPixelFromAddr(p, screenbpp);
1597 				flag = *(uchar *)px;
1598 
1599 				ix = dx % Theme.bmpFronttile.get()->w;
1600 				iy = dy % Theme.bmpFronttile.get()->h;
1601 
1602 				// Set the flag to empty
1603 				if(!IsTransparent(hole.get(), pixel) && !(flag & PX_ROCK)) {
1604                     if( flag & PX_EMPTY )
1605                         nDirtCount++;
1606 
1607 					*(uchar*)px = PX_DIRT;
1608 
1609 					// Place the dirt image
1610 					PutPixelToAddr(p2, GetPixel(Theme.bmpFronttile.get(), ix, iy), screenbpp);
1611 				}
1612 
1613 				// Put pixels that are not black/pink (eg, brown)
1614                 if(!IsTransparent(hole.get(), pixel) && pixel != pink && (flag & PX_EMPTY)) {
1615 					PutPixelToAddr(p2, pixel, screenbpp);
1616                     *(uchar*)px = PX_DIRT;
1617                     nDirtCount++;
1618                 }
1619 
1620 				p += screenbpp;
1621 				p2 += screenbpp;
1622 				px++;
1623 			}
1624 		}
1625 		UnlockSurface(bmpImage);
1626 	}
1627 
1628 	unlockFlags();
1629 
1630 	UnlockSurface(hole);
1631 	UnlockSurface(Theme.bmpFronttile);
1632 
1633 	UpdateArea(sx, sy, w, h);
1634 
1635 	// Grid
1636 	int i, j;
1637 	for(j = sy; j < sy + h + nGridHeight; j += nGridHeight)
1638 		for(i = sx; i < sx + w + nGridWidth; i += nGridWidth)
1639 			calculateGridCell(i, j, false);
1640 
1641     return nDirtCount;
1642 }
1643 
1644 
1645 ///////////////////
1646 // Place a blob of green dirt (greenball)
1647 //
1648 // Returns the number of dirt pixels placed
PlaceGreenDirt(CVec pos)1649 int CMap::PlaceGreenDirt(CVec pos)
1650 {
1651 	if (!bmpGreenMask.get()) {
1652 		warnings << "CMap::PlaceGreenDirt: bmpGreenMask not loaded" << endl;
1653 		return 0;
1654 	}
1655 
1656  	int dx,dy, sx,sy;
1657 	int x,y;
1658 	int w,h;
1659 	Uint32 pixel;
1660 	uchar flag;
1661     const Uint32 green = MakeColour(0,255,0);
1662 	const Uint32 pink = MakeColour(255,0,255);
1663     const Uint32 greens[4] = {MakeColour(148,136,0),
1664 				              MakeColour(136,124,0),
1665 						      MakeColour(124,112,0),
1666 							  MakeColour(116,100,0)};
1667 
1668     int nGreenCount = 0;
1669 
1670 	// Calculate half
1671 	w = bmpGreenMask.get()->w;
1672 	h = bmpGreenMask.get()->h;
1673 
1674 	sx = (int)pos.x-(w>>1);
1675 	sy = (int)pos.y-(h>>1);
1676 
1677 
1678 	if (!LockSurface(bmpGreenMask))
1679 		return 0;
1680 
1681 	Uint8 *p;
1682 	uchar *px;
1683 	Uint8 *p2;
1684 	Uint32 gr;
1685 
1686 	// Calculate clipping
1687 	int clip_y = MAX(sy, 0);
1688 	int clip_x = MAX(sx, 0);
1689 	int clip_h = MIN(sy+h, bmpImage.get()->h);
1690 	int clip_w = MIN(sx+w, bmpImage.get()->w);
1691 	int green_clip_y = -MIN(sy,(int)0);
1692 	int green_clip_x = -MIN(sx,(int)0);
1693 
1694 	short screenbpp = getMainPixelFormat()->BytesPerPixel;
1695 
1696 	lockFlags();
1697 
1698 	if( bmpBackImageHiRes.get() ) // Hi-res image
1699 	{
1700 		if (!LockSurface(bmpDrawImage))
1701 			return 0;
1702 		// Go through the pixels in the hole, setting the flags to dirt
1703 		int DrawImagePitch = bmpDrawImage.get()->pitch;
1704 		for(y = green_clip_y, dy=clip_y; dy < clip_h; y++, dy++) {
1705 
1706 			p = (Uint8*)bmpGreenMask.get()->pixels
1707 				+ y * bmpGreenMask.get()->pitch
1708 				+ green_clip_x * bmpGreenMask.get()->format->BytesPerPixel;
1709 			px = PixelFlags + dy * Width + clip_x;
1710 			p2 = (Uint8 *)bmpDrawImage.get()->pixels + dy * 2 * bmpDrawImage.get()->pitch + clip_x * 2 * bmpDrawImage.get()->format->BytesPerPixel;
1711 
1712 			for(x = green_clip_x, dx=clip_x; dx < clip_w; x++, dx++) {
1713 
1714 				pixel = GetPixelFromAddr(p,screenbpp);
1715 				flag = *(uchar*)px;
1716 
1717 				// Set the flag to dirt
1718 				if(pixel == green && (flag & PX_EMPTY)) {
1719 					*(uchar*)px = PX_DIRT;
1720 					nGreenCount++;
1721 
1722 					// Place a random green pixel
1723 					gr = greens[ GetRandomInt(3) ];
1724 
1725 					// Place the green pixel
1726 					PutPixelToAddr(p2, gr, screenbpp);
1727 					PutPixelToAddr(p2+screenbpp, gr, screenbpp);
1728 					PutPixelToAddr(p2+DrawImagePitch, gr, screenbpp);
1729 					PutPixelToAddr(p2+DrawImagePitch+screenbpp, gr, screenbpp);
1730 				}
1731 
1732 				// Put pixels that are not green/pink (eg, dark green)
1733                 if(pixel != green && pixel != pink && (flag & PX_EMPTY)) {
1734 					PutPixelToAddr(p2, pixel, screenbpp);
1735 					PutPixelToAddr(p2+screenbpp, pixel, screenbpp);
1736 					PutPixelToAddr(p2+DrawImagePitch, pixel, screenbpp);
1737 					PutPixelToAddr(p2+DrawImagePitch+screenbpp, pixel, screenbpp);
1738 					*(uchar*)px = PX_DIRT;
1739 					nGreenCount++;
1740                 }
1741 
1742 				p += screenbpp;
1743 				p2 += screenbpp * 2;
1744 				px++;
1745 			}
1746 		}
1747 		UnlockSurface(bmpDrawImage);
1748 	}
1749 	else // Low-res image
1750 	{
1751 		if (!LockSurface(bmpImage))
1752 			return 0;
1753 		// Go through the pixels in the hole, setting the flags to dirt
1754 		for(y = green_clip_y, dy=clip_y; dy < clip_h; y++, dy++) {
1755 
1756 			p = (Uint8*)bmpGreenMask.get()->pixels
1757 				+ y * bmpGreenMask.get()->pitch
1758 				+ green_clip_x * bmpGreenMask.get()->format->BytesPerPixel;
1759 			px = PixelFlags + dy * Width + clip_x;
1760 			p2 = (Uint8 *)bmpImage.get()->pixels + dy * bmpImage.get()->pitch + clip_x * bmpImage.get()->format->BytesPerPixel;
1761 
1762 			for(x = green_clip_x, dx=clip_x; dx < clip_w; x++, dx++) {
1763 
1764 				pixel = GetPixelFromAddr(p,screenbpp);
1765 				flag = *(uchar*)px;
1766 
1767 				// Set the flag to dirt
1768 				if(pixel == green && (flag & PX_EMPTY)) {
1769 					*(uchar*)px = PX_DIRT;
1770                     nGreenCount++;
1771 
1772                     // Place a random green pixel
1773                     gr = greens[ GetRandomInt(3) ];
1774 
1775 					// Place the green pixel
1776 					PutPixelToAddr(p2, gr, screenbpp);
1777 				}
1778 
1779 				// Put pixels that are not green/pink (eg, dark green)
1780                 if(pixel != green && pixel != pink && (flag & PX_EMPTY)) {
1781 					PutPixelToAddr(p2, pixel, screenbpp);
1782                     *(uchar*)px = PX_DIRT;
1783                     nGreenCount++;
1784                 }
1785 
1786 				p += screenbpp;
1787 				p2 += screenbpp;
1788 				px++;
1789 			}
1790 		}
1791 		UnlockSurface(bmpImage);
1792 	}
1793 
1794 	unlockFlags();
1795 
1796 	UnlockSurface(bmpGreenMask);
1797 
1798 	// Nothing placed, no need to update
1799 	if (nGreenCount)  {
1800 		UpdateArea(sx, sy, w, h);
1801 
1802 		// Grid
1803 		int i, j;
1804 		for(j = sy; j < sy + h + nGridHeight; j += nGridHeight)
1805 			for(i = sx; i < sx + w + nGridWidth; i += nGridWidth)
1806 				calculateGridCell(i, j, false);
1807 	}
1808 
1809     return nGreenCount;
1810 }
1811 
1812 ////////////////////
1813 // Checks for an object collision with the map
StaticCollisionCheck(const CVec & objpos,int objw,int objh,bool infiniteMap) const1814 CMap::CollisionInfo CMap::StaticCollisionCheck(const CVec &objpos, int objw, int objh, bool infiniteMap) const
1815 {
1816 	CollisionInfo result;
1817 	result.moveToX = (int)objpos.x;
1818 	result.moveToY = (int)objpos.y;
1819 
1820 	if (infiniteMap)
1821 		StaticCollisionCheckInfinite(objpos, objw, objh, result);
1822 	else
1823 		StaticCollisionCheckFinite(objpos, objw, objh, result);
1824 
1825 	return result;
1826 }
1827 
1828 //////////////////////
1829 // Collision check for finite map (with bounds hits)
StaticCollisionCheckFinite(const CVec & objpos,int objw,int objh,CMap::CollisionInfo & result) const1830 void CMap::StaticCollisionCheckFinite(const CVec &objpos, int objw, int objh, CMap::CollisionInfo &result) const
1831 {
1832 	// Bounds
1833 	if (objpos.x - objw / 2 <= 0)  {
1834 		result.moveToX = objw / 2 + 1;
1835 		result.occured = result.left = result.hitBounds = true;
1836 	}
1837 
1838 	if (objpos.x + objw / 2 >= Width)  {
1839 		result.moveToX = Width - objw / 2 - 1;
1840 		result.occured = result.right = result.hitBounds = true;
1841 	}
1842 
1843 	if (objpos.y - objh / 2 <= 0)  {
1844 		result.moveToY = objh / 2 + 1;
1845 		result.occured = result.top = result.hitBounds = true;
1846 	}
1847 
1848 	if (objpos.y + objh / 2 >= Height)  {
1849 		result.moveToY = Height - objh / 2 - 1;
1850 		result.occured = result.bottom = result.hitBounds = true;
1851 	}
1852 
1853 	// Use collision grid to check if the check is necessary
1854 	if (objw <= getCollGridCellW() && objh <= getCollGridCellH())  {
1855 		int x = CLAMP<int>((int)objpos.x, 0, Width - 1);
1856 		int y = CLAMP<int>((int)objpos.y, 0, Height - 1);
1857 		if (CollisionGrid[y * Width + x] == 0)  // If there's no dirt/rock around, no need to check any further
1858 			return;
1859 	}
1860 
1861 	// Cross check, taken from worm collision
1862 	SDL_Rect coll_r = { (SDLRect::Type) ((int)objpos.x - objw / 2), (SDLRect::Type) ((int)objpos.y - objh / 2), (SDLRect::TypeS) objw, (SDLRect::TypeS) objh };
1863 	if (!ClipRefRectWith(coll_r.x, coll_r.y, coll_r.w, coll_r.h, (SDLRect&)bmpImage->clip_rect))
1864 		return;
1865 	result.x = coll_r.x + coll_r.w / 2;
1866 	result.y = coll_r.y + coll_r.h / 2;
1867 
1868 	// Top to bottom
1869 	uchar *pf = PixelFlags + coll_r.y * Width + (coll_r.x + coll_r.w / 2);
1870 	for (int y = coll_r.y; y < coll_r.y + coll_r.h; y++)  {
1871 		if (*pf & (PX_ROCK|PX_DIRT))  {
1872 			result.occured = true;
1873 			result.hitRockDirt = true;
1874 			result.y = y;
1875 
1876 			if (y < coll_r.y + coll_r.h / 2)  {
1877 				result.top = true;
1878 				result.moveToY = y + objh / 2 + 1;
1879 			} else {
1880 				result.bottom = true;
1881 				result.moveToY = y - objh / 2 - 1;
1882 			}
1883 		}
1884 
1885 		pf += Width;
1886 	}
1887 
1888 	// Left to right
1889 	pf = PixelFlags + (coll_r.y + coll_r.h / 2) * Width + coll_r.x;
1890 	for (int x = coll_r.x; x < coll_r.x + coll_r.w; x++)  {
1891 		if (*pf & (PX_ROCK|PX_DIRT))  {
1892 			result.occured = true;
1893 			result.hitRockDirt = true;
1894 			result.x = x;
1895 
1896 			if (x < coll_r.x + coll_r.w / 2)  {
1897 				result.left = true;
1898 				result.moveToX = x + objw / 2 + 1;
1899 			} else {
1900 				result.right = true;
1901 				result.moveToX = x - objw / 2 - 1;
1902 			}
1903 		}
1904 
1905 		++pf;
1906 	}
1907 }
1908 
1909 //////////////////////////
1910 // Collision check for infinite map
StaticCollisionCheckInfinite(const CVec & objpos,int objw,int objh,CMap::CollisionInfo & result) const1911 void CMap::StaticCollisionCheckInfinite(const CVec &objpos, int objw, int objh, CMap::CollisionInfo &result) const
1912 {
1913 	// Use collision grid to check if the check is necessary
1914 	if (objw <= getCollGridCellW() && objh <= getCollGridCellH())  {
1915 		// HINT: if we are at the bounds, this check will work, because the flags are set to 1 around all borders
1916 		int x = WrapAroundX((int)objpos.x - objw / 2);
1917 		int y = WrapAroundY((int)objpos.y + objh / 2);
1918 		if (CollisionGrid[y * Width + x] == 0)  // If there's no dirt/rock around, no need to check any further
1919 			return;
1920 	}
1921 
1922 	// Cross check, taken from worm collision
1923 	SDL_Rect coll_r = { (SDLRect::Type) ((int)objpos.x - objw / 2), (SDLRect::Type) ((int)objpos.y - objh / 2), (SDLRect::TypeS) objw, (SDLRect::TypeS) objh };
1924 	result.x = coll_r.x + coll_r.w / 2;
1925 	result.y = coll_r.y + coll_r.h / 2;
1926 
1927 	// Top to bottom
1928 	const int wx = coll_r.x + coll_r.w / 2;
1929 	for (int y = coll_r.y; y < coll_r.y + coll_r.h; y++)  {
1930 		if (GetPixelFlag(wx, y, true) & (PX_ROCK|PX_DIRT))  {
1931 			result.occured = true;
1932 			result.hitRockDirt = true;
1933 			result.y = WrapAroundY(y);
1934 
1935 			if (y < coll_r.y + coll_r.h / 2)  {
1936 				result.top = true;
1937 				result.moveToY = WrapAroundY(y + objh / 2 + 1);
1938 			} else {
1939 				result.bottom = true;
1940 				result.moveToY = WrapAroundY(y - objh / 2 - 1);
1941 			}
1942 		}
1943 	}
1944 
1945 	// Left to right
1946 	const int wy = coll_r.y + coll_r.h / 2;
1947 	for (int x = coll_r.x; x < coll_r.x + coll_r.w; x++)  {
1948 		if (GetPixelFlag(x, wy, true) & (PX_ROCK|PX_DIRT))  {
1949 			result.occured = true;
1950 			result.hitRockDirt = true;
1951 			result.x = WrapAroundX(x);
1952 
1953 			if (x < coll_r.x + coll_r.w / 2)  {
1954 				result.left = true;
1955 				result.moveToX = WrapAroundX(x + objw / 2 + 1);
1956 			} else {
1957 				result.right = true;
1958 				result.moveToX = WrapAroundX(x - objw / 2 - 1);
1959 			}
1960 		}
1961 	}
1962 }
1963 
1964 
1965 ///////////////////
1966 // Apply a shadow to an area
ApplyShadow(int sx,int sy,int w,int h)1967 void CMap::ApplyShadow(int sx, int sy, int w, int h)
1968 {
1969 	if(bDedicated) return;
1970 	// Draw shadows?
1971 	if(!tLXOptions->bShadows) return;
1972 
1973 	int x, y, n;
1974 	uchar *px;
1975 	uchar *p;
1976 	uint ox,oy;
1977 	uchar flag;
1978 	Uint32 offset;
1979 
1980 	Uint8 *pixel,*src;
1981 
1982 	int screenbpp = getMainPixelFormat()->BytesPerPixel;
1983 
1984 	LOCK_OR_QUIT(bmpShadowMap);
1985 
1986 	lockFlags();
1987 
1988 	int clip_y = MAX(sy, (int)0);
1989 	int clip_x = MAX(sx, (int)0);
1990 	int clip_h = MIN(sy + h, Height);
1991 	int clip_w = MIN(sx + w, Width);
1992 
1993 	if( bmpBackImageHiRes.get() ) // Hi-res image
1994 	{
1995 		LOCK_OR_QUIT(bmpDrawImage);
1996 		int DrawImagePitch = bmpDrawImage.get()->pitch;
1997 		int ShadowMapPitch = bmpShadowMap.get()->pitch;
1998 		for(y = clip_y; y < clip_h; y++)
1999 		{
2000 			px = PixelFlags + y * Width + clip_x;
2001 			for(x = clip_x; x < clip_w; x++)
2002 			{
2003 				flag = *(uchar *)px;
2004 				// Edge hack
2005 				//if(x==0 || y==0 || x==Width-1 || y==Height-1)
2006 					//flag = PX_EMPTY;
2007 				if(!(flag & PX_EMPTY))
2008 				{
2009 					ox = x+1; oy = y+1;
2010 					// Draw the shadow
2011 					for(n = 0; n < SHADOW_DROP; n++)
2012 					{
2013 						// Clipping
2014 						if(ox >= Width) break;
2015 						if(oy >= Height) break;
2016 
2017 						p = PixelFlags + oy * Width + ox;
2018 						if(!( (*(uchar *)p) & PX_EMPTY))
2019 							break;
2020 
2021 						pixel = (Uint8*)bmpDrawImage.get()->pixels + oy*2*DrawImagePitch + ox*2*screenbpp;
2022 						src = (Uint8*)bmpShadowMap.get()->pixels + oy*ShadowMapPitch + ox*screenbpp;
2023 						memcpy(pixel, src, screenbpp);
2024 						memcpy(pixel+screenbpp, src, screenbpp);
2025 						memcpy(pixel+DrawImagePitch, src, screenbpp);
2026 						memcpy(pixel+DrawImagePitch+screenbpp, src, screenbpp);
2027 
2028 						*(uchar *)p |= PX_EMPTY | PX_SHADOW;
2029 						ox++; oy++;
2030 					}
2031 				}
2032 
2033 				px++;
2034 			}
2035 		}
2036 		UnlockSurface(bmpDrawImage);
2037 	}
2038 	else // Low-res image
2039 	{
2040 		LOCK_OR_QUIT(bmpImage);
2041 		for(y = clip_y; y < clip_h; y++) {
2042 
2043 			px = PixelFlags + y * Width + clip_x;
2044 
2045 			for(x = clip_x; x < clip_w; x++) {
2046 
2047 				flag = *(uchar *)px;
2048 
2049 				// Edge hack
2050 				//if(x==0 || y==0 || x==Width-1 || y==Height-1)
2051 					//flag = PX_EMPTY;
2052 
2053 				if(!(flag & PX_EMPTY)) {
2054 					ox = x+1; oy = y+1;
2055 
2056 					// Draw the shadow
2057 					for(n = 0; n < SHADOW_DROP; n++) {
2058 
2059 						// Clipping
2060 						if(ox >= Width) break;
2061 						if(oy >= Height) break;
2062 
2063 						p = PixelFlags + oy * Width + ox;
2064 						if(!( (*(uchar *)p) & PX_EMPTY))
2065 							break;
2066 
2067                         offset = oy*bmpImage.get()->pitch + ox*screenbpp;
2068                         pixel = (Uint8*)bmpImage.get()->pixels + offset;
2069                         src = (Uint8*)bmpShadowMap.get()->pixels + offset;
2070 						memcpy(pixel, src, screenbpp);
2071 
2072 						*(uchar *)p |= PX_EMPTY | PX_SHADOW;
2073 						ox++; oy++;
2074 					}
2075 				}
2076 
2077 				px++;
2078 			}
2079 		}
2080 		UnlockSurface(bmpImage);
2081 	}
2082 
2083 	unlockFlags();
2084 
2085 	UnlockSurface(bmpShadowMap);
2086 
2087 	bMiniMapDirty = true;
2088 }
2089 
2090 
2091 ///////////////////
2092 // Calculate the shadow map
CalculateShadowMap()2093 void CMap::CalculateShadowMap()
2094 {
2095 	// This should be faster
2096 	SDL_BlitSurface(bmpBackImage.get(), NULL, bmpShadowMap.get(), NULL);
2097 	DrawRectFillA(bmpShadowMap.get(),0,0,bmpShadowMap.get()->w,bmpShadowMap.get()->h,tLX->clBlack,96);
2098 }
2099 
2100 
2101 ///////////////////
2102 // Place a stone
PlaceStone(int size,CVec pos)2103 void CMap::PlaceStone(int size, CVec pos)
2104 {
2105 	SmartPointer<SDL_Surface> stone;
2106 	short dy, sx,sy;
2107 	short x,y;
2108 	short w,h;
2109 
2110 	if(size < 0 || size >= Theme.NumStones) {
2111 		warnings("WARNING: Bad stone size\n");
2112 		size = CLAMP(size, 0, Theme.NumStones - 1);
2113 	}
2114 
2115 
2116 	// Add the stone to the object list
2117 	if(NumObjects+1 < MAX_OBJECTS) {
2118 		object_t *o = &Objects[NumObjects++];
2119 		o->Type = OBJ_STONE;
2120 		o->Size = size;
2121 		o->X = (int) pos.x;
2122         o->Y = (int) pos.y;
2123 	}
2124 
2125 
2126 
2127 	// Calculate half
2128 	stone = Theme.bmpStones[size];
2129 	w = stone.get()->w;
2130 	h = stone.get()->h;
2131 
2132 	sx = (int)pos.x - (stone.get()->w >> 1);
2133 	sy = (int)pos.y - (stone.get()->h >> 1);
2134 
2135 	// Blit the stone to the surface
2136 	DrawImage(bmpImage.get(), stone, sx, sy);
2137 
2138 	LOCK_OR_QUIT(stone);
2139 
2140 	lockFlags();
2141 
2142 	// Calculate the clipping bounds, so we don't have to check each loop then
2143 	short clip_h = MIN(sy+stone.get()->h, bmpImage.get()->h) - sy;
2144 	short clip_w = MIN(sx+stone.get()->w, bmpImage.get()->w) - sx;
2145 	short clip_y = 0;
2146 	short clip_x = 0;
2147 	if (sy<0)
2148 		clip_y = abs(sy);
2149 	if (sx<0)
2150 		clip_x = abs(sx);
2151 
2152 	// Pixels
2153 	Uint8 *p = NULL;
2154 	Uint8 *PixelRow = (Uint8*)stone.get()->pixels + clip_y*stone.get()->pitch;
2155 	uchar *px = PixelFlags;
2156 	short pf_tmp = MAX((short)0, sx);
2157 	short p_tmp = clip_x * stone.get()->format->BytesPerPixel;
2158 
2159 	// Go through the pixels in the stone and update pixel flags
2160 	for(y = clip_y, dy = MAX((short)0, sy); y < clip_h; y++, dy++, PixelRow += stone.get()->pitch) {
2161 
2162 		p = PixelRow+p_tmp;
2163 		px = PixelFlags + dy * Width + pf_tmp;
2164 
2165 		for(x = clip_x; x < clip_w; x++) {
2166 
2167 			// Rock?
2168 			if(!IsTransparent(stone.get(), GetPixelFromAddr(p, stone.get()->format->BytesPerPixel))) {
2169 				*(uchar*)px = PX_ROCK;
2170 			}
2171 
2172 			p += stone.get()->format->BytesPerPixel;
2173 			px++;
2174 		}
2175 	}
2176 
2177 	// Recalculate the grid
2178 	for (y = sy; y < sy + stone->h + nGridHeight; y += nGridHeight)
2179 		for (x = sx; x < sx + stone->w + nGridWidth; x += nGridWidth)
2180 			calculateGridCell(x, y, false);
2181 
2182 	unlockFlags();
2183 
2184 	UnlockSurface(stone);
2185 
2186 	UpdateArea(sx, sy, w, h);
2187 
2188     // Calculate the total dirt count
2189     CalculateDirtCount();
2190 }
2191 
2192 
2193 ///////////////////
2194 // Place a miscellaneous item
PlaceMisc(int id,CVec pos)2195 void CMap::PlaceMisc(int id, CVec pos)
2196 {
2197 
2198 	SmartPointer<SDL_Surface> misc;
2199 	short dy,dx, sx,sy;
2200 	short x,y;
2201 
2202 	if(id < 0 || id >= Theme.NumMisc) {
2203 		warnings("Bad misc size\n");
2204 		if(id < 0) id = 0;
2205 		else id = Theme.NumMisc-1;
2206 	}
2207 
2208 	// Add the misc to the object list
2209 	if(NumObjects+1 < MAX_OBJECTS) {
2210 		object_t *o = &Objects[NumObjects++];
2211 		o->Type = OBJ_MISC;
2212 		o->Size = id;
2213 		o->X = (int) pos.x;
2214         o->Y = (int) pos.y;
2215 	}
2216 
2217 
2218 	// Calculate half
2219 	misc = Theme.bmpMisc[id];
2220 
2221 	sx = (int)pos.x-(misc.get()->w>>1);
2222 	sy = (int)pos.y-(misc.get()->h>>1);
2223 
2224 
2225 	LOCK_OR_QUIT(misc);
2226 	LOCK_OR_QUIT(bmpImage);
2227 
2228 	lockFlags();
2229 
2230 	// Calculate the clipping bounds, so we don't have to check each loop then
2231 	short clip_h = MIN(sy+misc.get()->h,bmpImage.get()->h)-sy;
2232 	short clip_w = MIN(sx+misc.get()->w,bmpImage.get()->w)-sx;
2233 	short clip_y = 0;
2234 	short clip_x = 0;
2235 	if (sy<0)
2236 		clip_y = -sy;
2237 	if (sx<0)
2238 		clip_x = -sx;
2239 
2240 	Uint8 *p = NULL;
2241 	Uint8 *PixelRow = (Uint8 *)misc.get()->pixels+clip_y*misc.get()->pitch;
2242 	uchar *px = PixelFlags;
2243 
2244 	// Temps for better performance
2245 	short pf_tmp = MAX((short)0,sx);
2246 	short p_tmp = clip_x*misc.get()->format->BytesPerPixel;
2247 	short dx_tmp = MAX((short)0,sx);
2248 
2249 	// Go through the pixels in the misc item
2250 	for(y = clip_y, dy = MAX((short)0, sy); y < clip_h; y++, dy++, PixelRow += misc.get()->pitch) {
2251 
2252 		p = PixelRow + p_tmp;
2253 		px = PixelFlags + dy * Width + pf_tmp;
2254 
2255 		for(x = clip_x, dx = dx_tmp; x < clip_w; dx++, x++) {
2256 
2257 			// Put the pixel down
2258 			if(!IsTransparent(misc.get(), GetPixelFromAddr(p, misc.get()->format->BytesPerPixel)) && (*px & PX_DIRT)) {
2259 				PutPixel(bmpImage.get(), dx, dy, GetPixelFromAddr(p, misc.get()->format->BytesPerPixel));
2260 				*(uchar*)px = PX_DIRT;
2261 			}
2262 
2263 			p += misc.get()->format->BytesPerPixel;
2264 			px++;
2265 		}
2266 	}
2267 
2268 	unlockFlags();
2269 
2270 	UnlockSurface(bmpImage);
2271 	UnlockSurface(misc);
2272 
2273 	// Update the draw image
2274 	UpdateDrawImage(sx, sy, clip_w, clip_h);
2275 
2276 	bMiniMapDirty = true;
2277 }
2278 
2279 
2280 ///////////////////
2281 // Put a pixel onto the front image buffer
2282 // TODO: atm, this isnt used at all; some outcommented usage is in debug parts of AI; so shouldn't we put the pixel on the debug image then?
PutImagePixel(uint x,uint y,Color colour)2283 void CMap::PutImagePixel(uint x, uint y, Color colour)
2284 {
2285     // Checking edges
2286 	if(x >= Width || y >= Height)
2287 		return;
2288 
2289 	LOCK_OR_QUIT(bmpImage)
2290     PutPixel(bmpImage.get(), x, y, colour.get(bmpImage.get()->format));
2291 	UnlockSurface(bmpImage);
2292 
2293 	x *= 2;
2294 	y *= 2;
2295 	DrawRectFill2x2_NoClip(bmpDrawImage.get(), x, y, colour);
2296 }
2297 
2298 
2299 ///////////////////
2300 // Update the minimap
UpdateMiniMap(bool force)2301 void CMap::UpdateMiniMap(bool force)
2302 {
2303 	if(bDedicated) return;
2304 	if(!bMiniMapDirty && !force) return;
2305 
2306 	if(!bmpMiniMap.get()) {
2307 		errors << "CMap::UpdateMiniMap: minimap surface not initialised" << endl;
2308 		return;
2309 	}
2310 
2311 	if(!bmpDrawImage.get()) {
2312 		errors << "CMap::UpdateMiniMap: drawimage surface not initialised" << endl;
2313 		return;
2314 	}
2315 
2316 	if( bmpBackImageHiRes.get() )
2317 	{
2318 		if (tLXOptions->bAntiAliasing)
2319 			DrawImageResampledAdv(bmpMiniMap.get(), bmpDrawImage, 0, 0, 0, 0, bmpDrawImage.get()->w, bmpDrawImage.get()->h, bmpMiniMap->w, bmpMiniMap->h);
2320 		else
2321 			DrawImageResizedAdv(bmpMiniMap.get(), bmpDrawImage, 0, 0, 0, 0, bmpDrawImage.get()->w, bmpDrawImage.get()->h, bmpMiniMap->w, bmpMiniMap->h);
2322 	}
2323 	else
2324 	{
2325 		if (tLXOptions->bAntiAliasing)
2326 			DrawImageResampledAdv(bmpMiniMap.get(), bmpImage, 0, 0, 0, 0, bmpImage.get()->w, bmpImage.get()->h, bmpMiniMap->w, bmpMiniMap->h);
2327 		else
2328 			DrawImageResizedAdv(bmpMiniMap.get(), bmpImage, 0, 0, 0, 0, bmpImage.get()->w, bmpImage.get()->h, bmpMiniMap->w, bmpMiniMap->h);
2329 	}
2330 
2331 	// Not dirty anymore
2332 	bMiniMapDirty = false;
2333 }
2334 
2335 ///////////////////
2336 // Update an area of the minimap
2337 // X, Y, W and H apply to the bmpImage, not bmpMinimap
UpdateMiniMapRect(int x,int y,int w,int h)2338 void CMap::UpdateMiniMapRect(int x, int y, int w, int h)
2339 {
2340 	if(bDedicated) return;
2341 
2342 	// If the minimap is going to be fully repainted, just move on
2343 	if (bMiniMapDirty)
2344 		return;
2345 
2346 	if( bmpBackImageHiRes.get() )
2347 	{
2348 		// Calculate ratios
2349 		const float xratio = (float)bmpMiniMap.get()->w / (float)bmpDrawImage.get()->w;
2350 		const float yratio = (float)bmpMiniMap.get()->h / (float)bmpDrawImage.get()->h;
2351 
2352 		const int dx = (int)((float)x * xratio);
2353 		const int dy = (int)((float)y * yratio);
2354 
2355 		if (tLXOptions->bAntiAliasing)
2356 			DrawImageResampledAdv(bmpMiniMap.get(), bmpDrawImage, x - 1, y - 1, dx, dy, w + 1, h + 1, xratio, yratio);
2357 		else
2358 			DrawImageResizedAdv(bmpMiniMap.get(), bmpDrawImage, x - 1, y - 1, dx, dy, w + 1, h + 1, xratio, yratio);
2359 	}
2360 	else
2361 	{
2362 		// Calculate ratios
2363 		const float xratio = (float)bmpMiniMap.get()->w / (float)bmpImage.get()->w;
2364 		const float yratio = (float)bmpMiniMap.get()->h / (float)bmpImage.get()->h;
2365 
2366 		const int dx = (int)((float)x * xratio);
2367 		const int dy = (int)((float)y * yratio);
2368 
2369 		if (tLXOptions->bAntiAliasing)
2370 			DrawImageResampledAdv(bmpMiniMap.get(), bmpImage, x - 1, y - 1, dx, dy, w + 1, h + 1, xratio, yratio);
2371 		else
2372 			DrawImageResizedAdv(bmpMiniMap.get(), bmpImage, x - 1, y - 1, dx, dy, w + 1, h + 1, xratio, yratio);
2373 	}
2374 }
2375 
2376 
drawOnMiniMap(SDL_Surface * bmpDest,uint miniX,uint miniY,const CVec & pos,Uint8 r,Uint8 g,Uint8 b,bool big,bool special)2377 void CMap::drawOnMiniMap(SDL_Surface* bmpDest, uint miniX, uint miniY, const CVec& pos, Uint8 r, Uint8 g, Uint8 b, bool big, bool special) {
2378 	if(bDedicated) return;
2379 
2380 	float xstep,ystep;
2381 	float mx,my;
2382 	int mw = bmpMiniMap.get()->w;
2383 	int mh = bmpMiniMap.get()->h;
2384 
2385 	// Calculate steps
2386 	xstep = (float)Width/ (float)mw;
2387 	ystep = (float)Height / (float)mh;
2388 
2389 	byte dr,dg,db;
2390 	dr = ~r;
2391 	dg = ~g;
2392 	db = ~b;
2393 
2394 	r += (int)( (float)dr*(fBlinkTime.seconds()*2.0f));
2395 	g += (int)( (float)dg*(fBlinkTime.seconds()*2.0f));
2396 	b += (int)( (float)db*(fBlinkTime.seconds()*2.0f));
2397 
2398 
2399 
2400 	mx = pos.x/xstep;
2401 	my = pos.y/ystep;
2402 	mx = (float)floor(mx);
2403 	my = (float)floor(my);
2404 
2405 	mx = MIN(mw-(float)1,mx); mx = MAX((float)0,mx);
2406 	my = MIN(mh-(float)1,my); my = MAX((float)0,my);
2407 	int i=(int)mx + miniX;
2408 	int j=(int)my + miniY;
2409 	// Snap it to the nearest 2nd pixel (prevent 'jumping')
2410 	//x -= x % 2;
2411 	//y -= y % 2;
2412 
2413 	Color col = Color(r,g,b);
2414 
2415 	{
2416 		int x = MAX((int)miniX, i-1);
2417 		int y = MAX((int)miniY, j-1);
2418 
2419 		if(!big && !special)
2420 			DrawRectFill2x2(bmpDest, x, y, col);
2421 		else if(!big && special) {
2422 			if(fBlinkTime > 0.25f) {
2423 				DrawLine(bmpDest, x-1, y-1, x+1, y+1, col);
2424 				DrawLine(bmpDest, x-1, y+1, x+1, y-1, col);
2425 			} else
2426 				DrawRectFill2x2(bmpDest, x, y, col);
2427 		}
2428 		else if(big && !special)
2429 			DrawRectFill(bmpDest, x, y, i+2, j+2, col);
2430 		else if(big && special) {
2431 			if(fBlinkTime > 0.25f) {
2432 				DrawLine(bmpDest, x-1, y-1, i+2, j+2, col);
2433 				DrawLine(bmpDest, x-1, j+2, i+2, y-1, col);
2434 			} else {
2435 				DrawRectFill(bmpDest, x, y, i+2, j+2, col);
2436 			}
2437 		}
2438 	}
2439 }
2440 
2441 ///////////////////
2442 // Draw & Simulate the minimap
DrawMiniMap(SDL_Surface * bmpDest,uint x,uint y,TimeDiff dt,CWorm * worms)2443 void CMap::DrawMiniMap(SDL_Surface * bmpDest, uint x, uint y, TimeDiff dt, CWorm *worms)
2444 {
2445 	if(worms == NULL || bmpMiniMap.get() == NULL)
2446 		return;
2447 
2448 
2449 	// Update the minimap (only if dirty)
2450 	if(bMiniMapDirty)
2451 		UpdateMiniMap();
2452 
2453 
2454 	// Draw the minimap
2455 	DrawImage(bmpDest, bmpMiniMap, x, y);
2456 
2457 	fBlinkTime+=dt;
2458 	if(fBlinkTime>0.5f)
2459 		fBlinkTime=TimeDiff();
2460 
2461 
2462 	// Show worms
2463 	CWorm *w = worms;
2464 	for(int n=0;n<MAX_WORMS;n++,w++) {
2465 		if(!w->getAlive() || !w->isUsed() || !cClient->isWormVisibleOnAnyViewport(n))
2466 			continue;
2467 
2468 		Color gameCol = w->getGameColour();
2469 
2470 		// Our worms are bigger
2471 		bool big = false;
2472 
2473 		// Tagged worms or local players are bigger, depending on the game type
2474 		if(cClient->getGeneralGameType() != GMT_TIME) {
2475 			big = (w->getType() == PRF_HUMAN && w->getLocal());
2476 		} else {
2477 			big = w->getTagIT()!=0;
2478 		}
2479 
2480 		drawOnMiniMap(bmpDest, x, y, w->getPos(), gameCol.r, gameCol.g, gameCol.b, big, false);
2481 	}
2482 
2483 	if(cClient && cClient->getGameReady()) {
2484 		cClient->flagInfo()->drawOnMiniMap(this, bmpDest, x, y);
2485 	}
2486 }
2487 
2488 ///////////////////
2489 // Load the map
Load(const std::string & filename)2490 bool CMap::Load(const std::string& filename)
2491 {
2492 	// TODO: remove that as soon as we do the map loading in a seperate thread
2493 	ScopedBackgroundLoadingAni backgroundLoadingAni(320, 280, 50, 50, Color(128,128,128), Color(64,64,64));
2494 
2495 	// Weird
2496 	if (filename == "") {
2497 		warnings("WARNING: loading unnamed map, ignoring ...\n");
2498 		return true;
2499 	}
2500 
2501 	// Already loaded?
2502 	if (FileName == filename && Created)  {
2503 		notes("HINT: map " + filename + " is already loaded\n");
2504 		return true;
2505 	}
2506 
2507 
2508 	FileName = filename;
2509 
2510 	// try loading a previously cached map
2511 	if(LoadFromCache()) {
2512 		// everything is ok
2513 		notes << "reusing cached map for " << filename << endl;
2514 		LoadPostProcess();
2515 		PostProcessMirroredMap();
2516 		return true;
2517 	}
2518 
2519 	MapLoader* loader = MapLoader::open(filename);
2520 	if(!loader) {
2521 		warnings << "level " << filename << " couldn't be loaded" << endl;
2522 		return false;
2523 	}
2524 
2525 	{
2526 		bool res = loader->parseData(this);
2527 		delete loader;
2528 		if(!res) {
2529 			warnings << "level " << filename << " is corrupted" << endl;
2530 			return false;
2531 		}
2532 
2533 		if(!PixelFlags) {
2534 			errors << "level loader for " << filename << " is behaving wrong" << endl;
2535 			return false;
2536 		}
2537 	}
2538 
2539 	bMiniMapDirty = true;
2540 	Created = true;
2541     //sRandomLayout.bUsed = false;
2542 
2543 	LoadPostProcess();
2544 
2545 	// Save the map to cache
2546 	SaveToCache();
2547 
2548 	PostProcessMirroredMap();
2549 
2550 	return true;
2551 }
2552 
LoadPostProcess()2553 void CMap::LoadPostProcess()
2554 {
2555 	// Calculate the shadowmap
2556 	CalculateShadowMap();
2557 
2558 	// Apply the shadow
2559 	ApplyShadow(0, 0, Width, Height);
2560 
2561 	// Update the draw image
2562 	UpdateDrawImage(0, 0, bmpImage->w, bmpImage->h);
2563 
2564 	// Update the minimap
2565 	UpdateMiniMap(true);
2566 
2567 	// Calculate the total dirt count
2568 	CalculateDirtCount();
2569 
2570 	// Calculate the grid
2571 	calculateGrid();
2572 
2573 	// Calculate collision grid
2574 	calculateCollisionGridArea(0, 0, Width, Height);
2575 }
2576 
2577 
2578 ///////////////////
2579 // Save the map
Save(const std::string & name,const std::string & filename)2580 bool CMap::Save(const std::string& name, const std::string& filename)
2581 {
2582 	FILE *fp = OpenGameFile(filename,"wb");
2583 	if(fp == NULL)
2584 		return false;
2585 
2586 	Type = MPT_IMAGE;
2587 
2588 	// Header
2589 	int		version = MAP_VERSION;
2590 
2591 	fwrite("LieroX Level",	32,	fp);
2592 	fwrite_endian_compat((version),	sizeof(int),	1,	fp);
2593 	fwrite(name,	64,	fp);
2594 	fwrite_endian_compat((Width),		sizeof(int),	1,	fp);
2595 	fwrite_endian_compat((Height),		sizeof(int),	1,	fp);
2596 	fwrite_endian_compat((Type),		sizeof(int),	1,	fp);
2597 	fwrite(Theme.name,	32,	fp);
2598 	fwrite_endian_compat((NumObjects),	sizeof(int),	1,	fp);
2599 
2600 
2601 	// Save the images if in an image format
2602 	if(Type == MPT_IMAGE)
2603 		return SaveImageFormat(fp);
2604 
2605 	lockFlags();
2606 
2607 	// Dirt map
2608 	uint n;
2609 	for(n=0;n<Width*Height;) {
2610 		uchar t = 0;
2611 
2612 		// 1 bit == 1 pixel with a yes/no dirt flag
2613 		for(ushort i=0;i<8;i++) {
2614 			uchar value = (PixelFlags[n++] & PX_EMPTY);
2615 			t |= (value << i);
2616 		}
2617 
2618 		fwrite(&t,	sizeof(uchar),	1,	fp);
2619 	}
2620 
2621 	unlockFlags();
2622 
2623 	// Objects
2624 	object_t *o = Objects;
2625 	for(int i=0;i<NumObjects;i++,o++) {
2626 		fwrite_endian_compat((o->Type),sizeof(int),	1,	fp);
2627 		fwrite_endian_compat((o->Size),sizeof(int),	1,	fp);
2628 		fwrite_endian_compat((o->X),	sizeof(int),	1,	fp);
2629         fwrite_endian_compat((o->Y),	sizeof(int),	1,	fp);
2630 	}
2631 
2632 	fclose(fp);
2633 
2634 	return true;
2635 }
2636 
2637 
2638 ///////////////////
2639 // Save the images
SaveImageFormat(FILE * fp)2640 bool CMap::SaveImageFormat(FILE *fp)
2641 {
2642 	uint x,y,n,p;
2643 	Uint8 r,g,b,a;
2644 
2645 	// The images are saved in a raw 24bit format.
2646 	// 8 bits per r,g,b channel
2647 
2648 	if( !bmpBackImage.get() || !bmpImage.get() || !PixelFlags )
2649 		return false;
2650 
2651 	// Write out the images & pixeflags to memory, compress the data & save the compressed data
2652 	Uint32 size = (Width*Height * 3) * 2 + (Width*Height) + 1;
2653 	Uint32 destsize = size + (size / 8) + 12;
2654 
2655 	uchar *pSource = new uchar[size];
2656 	uchar *pDest = new uchar[destsize];
2657 
2658 	if(!pSource || !pDest) {
2659 		errors("CMap::SaveImageFormat: ERROR: not enough memory for pSource and pDest\n");
2660 		fclose(fp);
2661 		return false;
2662 	}
2663 
2664 
2665 	// Save the back image
2666 	p=0;
2667 	LOCK_OR_FAIL(bmpBackImage);
2668 	for(y=0; y<Height; y++) {
2669 		for(x=0; x<Width; x++) {
2670 			GetColour4( GetPixel(bmpBackImage.get(),x,y), bmpBackImage.get()->format, &r, &g, &b ,&a );
2671 
2672 			pSource[p++] = r;
2673 			pSource[p++] = g;
2674 			pSource[p++] = b;
2675 		}
2676 	}
2677 	UnlockSurface(bmpBackImage);
2678 
2679 
2680 	// Save the front image
2681 	LOCK_OR_FAIL(bmpImage);
2682 	for(y=0; y<Height; y++) {
2683 		for(x=0; x<Width; x++) {
2684 			GetColour4( GetPixel(bmpImage.get(),x,y), bmpImage.get()->format, &r, &g, &b ,&a );
2685 			pSource[p++] = r;
2686 			pSource[p++] = g;
2687 			pSource[p++] = b;
2688 		}
2689 	}
2690 	UnlockSurface(bmpImage);
2691 
2692 	// Save the pixel flags
2693 	lockFlags();
2694 	for(n=0;n<Width*Height;n++) {
2695 		uchar t = PX_EMPTY;
2696 
2697 		if(PixelFlags[n] & PX_DIRT)
2698 			t = PX_DIRT;
2699 		if(PixelFlags[n] & PX_ROCK)
2700 			t = PX_ROCK;
2701 
2702 		pSource[p++] = t;
2703 	}
2704 	unlockFlags();
2705 
2706 	// Compress it
2707 	ulong lng_dsize = destsize;
2708 	if( compress( pDest, &lng_dsize, pSource, size) != Z_OK ) {
2709 		errors("Failed compressing\n");
2710 		fclose(fp);
2711 		delete[] pSource;
2712 		delete[] pDest;
2713 		return false;
2714 	}
2715 	destsize = lng_dsize; // WARNING: possible overflow ; TODO: do a check for it?
2716 
2717 	// Write out the details & the data
2718 	fwrite_endian_compat((destsize), sizeof(Uint32), 1, fp);
2719 	fwrite_endian_compat((size), sizeof(Uint32), 1, fp);
2720 	fwrite(pDest, sizeof(uchar), destsize, fp);
2721 
2722 	delete[] pSource;
2723 	delete[] pDest;
2724 
2725 	// TODO: save hi-res image here
2726 
2727 	fclose(fp);
2728 	return true;
2729 }
2730 
2731 
2732 ///////////////////
2733 // DEBUG: Draw the pixel flags
DEBUG_DrawPixelFlags(int x,int y,int w,int h)2734 void CMap::DEBUG_DrawPixelFlags(int x, int y, int w, int h)
2735 {
2736 
2737     /*for(y=0;y<Height;y+=30) {
2738 		for(x=0;x<Width;x+=30) {
2739             CarveHole(5,CVec(x,y));
2740 
2741         }
2742     }*/
2743 
2744 
2745 /*	n=0;
2746 	for(y=0;y<Height;y++) {
2747 		for(x=0;x<Width;x++) {
2748 
2749 			switch(PixelFlags[n]) { //NOOO
2750 
2751 				case PX_EMPTY:	PutPixel(bmpImage,x,y,tLX->clBlack);	break;
2752 				case PX_DIRT:	PutPixel(bmpImage,x,y,MakeColour(255,0,0));	break;
2753 				case PX_ROCK:	PutPixel(bmpImage,x,y,MakeColour(128,128,128));	break;
2754 			}
2755 			n++;
2756 		}
2757 	}*/
2758 	/*SDL_Rect oldrect = bmpDrawImage->clip_rect;
2759 	SDL_Rect newrect = { x * 2, y * 2, w * 2, h * 2 };
2760 	SDL_SetClipRect(bmpDrawImage.get(), &newrect);*/
2761 #ifdef _AI_DEBUG
2762 
2763 	int xstart = MAX(0, x / nGridWidth);
2764 	int ystart = MAX(0, y / nGridHeight);
2765 	int xend = MIN((x + w) / nGridWidth, nGridCols - 1);
2766 	int yend = MIN((y + h) / nGridHeight, nGridRows - 1);
2767 
2768 
2769     for(int py = ystart; py <= yend; py++) {
2770         for(int px = xstart; px <= xend; px++) {
2771 			uchar pf = GridFlags[py * nGridCols + px];
2772 
2773             if(pf & PX_ROCK)
2774                 DrawRectFill(bmpDebugImage.get(),px*nGridWidth*2,py*nGridHeight*2,(px*nGridWidth+nGridWidth)*2,(py*nGridHeight+nGridHeight)*2, Color(128,128,128));
2775             else if(pf & PX_DIRT)
2776                 DrawRectFill(bmpDebugImage.get(),px*nGridWidth*2,py*nGridHeight*2,(px*nGridWidth+nGridWidth)*2,(py*nGridHeight+nGridHeight)*2, Color(255,0,0));
2777             else if(pf & PX_EMPTY)
2778                 DrawRectFill(bmpDebugImage.get(),px*nGridWidth*2,py*nGridHeight*2,(px*nGridWidth+nGridWidth)*2,(py*nGridHeight+nGridHeight)*2, Color(0,0,128));
2779             //DrawRect(bmpDrawImage,x*nGridWidth*2,y*nGridHeight*2,(x*nGridWidth+nGridWidth)*2,(y*nGridHeight+nGridHeight)*2, 0);
2780 			//DrawImageStretch2(bmpDrawImage,bmpImage,0,0,0,0,bmpImage->w,bmpImage->h);
2781         }
2782     }
2783 
2784 	SDL_SetAlpha(bmpDebugImage.get(), SDL_SRCALPHA, 128);
2785 
2786 #endif
2787 
2788 	//SDL_SetClipRect(bmpDrawImage.get(), &oldrect);
2789 }
2790 
2791 
2792 ///////////////////
2793 // Shutdown the map
Shutdown()2794 void CMap::Shutdown()
2795 {
2796 	lockFlags();
2797 
2798 	if(Created) {
2799 
2800 		//notes << "Some created map is shutting down..." << endl;
2801 
2802 		bmpImage = NULL;
2803 		bmpDrawImage = NULL;
2804 #ifdef _AI_DEBUG
2805 		bmpDebugImage = NULL;
2806 #endif
2807 		bmpBackImage = NULL;
2808 		bmpShadowMap = NULL;
2809 		bmpMiniMap = NULL;
2810 		bmpBackImageHiRes = NULL;
2811 
2812 		if(PixelFlags)
2813 			delete[] PixelFlags;
2814 		PixelFlags = NULL;
2815 
2816         if(GridFlags)
2817             delete[] GridFlags;
2818         GridFlags = NULL;
2819 
2820         if(AbsoluteGridFlags)
2821             delete[] AbsoluteGridFlags;
2822         AbsoluteGridFlags = NULL;
2823 
2824 		if(CollisionGrid)
2825 			delete[] CollisionGrid;
2826 		CollisionGrid = NULL;
2827 
2828 		if(Objects)
2829 			delete[] Objects;
2830 		Objects = NULL;
2831 		NumObjects = 0;
2832 		AdditionalData.clear();
2833 	}
2834 	// Safety
2835 	else  {
2836 		bmpImage = NULL;
2837 		bmpDrawImage = NULL;
2838 #ifdef _AI_DEBUG
2839 		bmpDebugImage = NULL;
2840 #endif
2841 		bmpBackImage = NULL;
2842 		bmpBackImageHiRes = NULL;
2843 		bmpShadowMap = NULL;
2844 		bmpMiniMap = NULL;
2845 		PixelFlags = NULL;
2846 		GridFlags = NULL;
2847 		AbsoluteGridFlags = NULL;
2848 		Objects = NULL;
2849 		AdditionalData.clear();
2850 	}
2851 
2852 	Created = false;
2853 	FileName = "";
2854 
2855 	unlockFlags();
2856 }
2857 
2858 
2859 #ifdef _AI_DEBUG
ClearDebugImage()2860 void CMap::ClearDebugImage() {
2861 	if (bmpDebugImage.get()) {
2862 		FillSurfaceTransparent(bmpDebugImage.get());
2863 	}
2864 }
2865 #endif
2866 
2867 ///////////////////
2868 // Carve a hole
2869 // Returns the number of dirt pixels carved
2870 // TODO: why do we have this function? why not CMap::CarveHole?
CarveHole(CVec pos)2871 int CarveHole(CVec pos)
2872 {
2873 	if(!cClient->getMap()) {
2874 		errors << "CarveHole: client map not loaded" << endl;
2875 		return 0;
2876 	}
2877 
2878 	if(!cClient->getMap()->GetImage().get()) {
2879 		errors << "CarveHole: client map invalid" << endl;
2880 		return 0;
2881 	}
2882 
2883 	int x,y;
2884 	Color Colour = cClient->getMap()->GetTheme()->iDefaultColour;
2885 
2886 	// Go through until we find dirt to throw around
2887 	y = MAX(MIN((int)pos.y, (int)cClient->getMap()->GetHeight() - 1), 0);
2888 
2889 	if (!LockSurface(cClient->getMap()->GetImage()))
2890 		return 0;
2891 	for(x=(int)pos.x-2; x<=(int)pos.x+2; x++) {
2892 		// Clipping
2893 		if(x < 0) continue;
2894 		if((uint)x >= cClient->getMap()->GetWidth())	break;
2895 
2896 		if(cClient->getMap()->GetPixelFlag(x,y) & PX_DIRT) {
2897 			Colour = Color(cClient->getMap()->GetImage()->format, GetPixel(cClient->getMap()->GetImage().get(), x, y));
2898 			for(short n=0; n<3; n++)
2899 				SpawnEntity(ENT_PARTICLE,0,pos,CVec(GetRandomNum()*30,GetRandomNum()*10),Colour,NULL);
2900 			break;
2901 		}
2902 	}
2903 	UnlockSurface(cClient->getMap()->GetImage());
2904 
2905 	// Just carve a hole for the moment
2906 	return cClient->getMap()->CarveHole(3,pos,cClient->getGameLobby()->features[FT_InfiniteMap]);
2907 }
2908 
2909 
2910 
2911 
2912 
2913 /*
2914 ===========================
2915 
2916     Collision Detection
2917 
2918 ===========================
2919 */
2920 
2921 class set_col_and_break {
2922 public:
2923 	CVec collision;
2924 	bool hit;
2925 
set_col_and_break()2926 	set_col_and_break() : hit(false) {}
operator ()(int x,int y)2927 	bool operator()(int x, int y) {
2928 		hit = true;
2929 		collision.x = (float)x;
2930 		collision.y = (float)y;
2931 		return false;
2932 	}
2933 };
2934 
2935 
2936 
2937 ///////////////////
2938 // Check for a collision
2939 // HINT: this function is not used at the moment; and it is incomplete...
CheckCollision(float dt,CVec pos,CVec vel,uchar checkflags)2940 int CheckCollision(float dt, CVec pos, CVec vel, uchar checkflags)
2941 {
2942 /*	set_col_and_break col_action;
2943 	fastTraceLine(trg, pos, map, checkflags, col_action);
2944 	if(col_action.hit) {
2945 
2946 	}*/
2947 	assert(false);
2948 	return 0;
2949 
2950 /*	int		CollisionSide = 0;
2951 	int		mw = map->GetWidth();
2952 	int		mh = map->GetHeight();
2953 	int		px,py, x,y, w,h;
2954 	int		top,bottom,left,right;
2955 
2956 	px=(int)pos.x;
2957 	py=(int)pos.y;
2958 
2959 	top=bottom=left=right=0;
2960 
2961 	w = width;
2962 	h = height;
2963 
2964 	// Hit edges
2965 	// Check the collision side
2966 	if(px-w<0)
2967 		CollisionSide |= COL_LEFT;
2968 	if(py-h<0)
2969 		CollisionSide |= COL_TOP;
2970 	if(px+w>=mw)
2971 		CollisionSide |= COL_RIGHT;
2972 	if(py+h>=mh)
2973 		CollisionSide |= COL_BOTTOM;
2974 	if(CollisionSide) return CollisionSide;
2975 
2976 
2977 
2978 
2979 	for(y=py-h;y<=py+h;y++) {
2980 
2981 		// Clipping means that it has collided
2982 		if(y<0)	{
2983 			CollisionSide |= COL_TOP;
2984 			return CollisionSide;
2985 		}
2986 		if(y>=mh) {
2987 			CollisionSide |= COL_BOTTOM;
2988 			return CollisionSide;
2989 		}
2990 
2991 
2992 		const uchar *pf = map->GetPixelFlags() + y*mw + px-w;
2993 
2994 		for(x=px-w;x<=px+w;x++) {
2995 
2996 			// Clipping
2997 			if(x<0) {
2998 				CollisionSide |= COL_LEFT;
2999 				return CollisionSide;
3000 			}
3001 			if(x>=mw) {
3002 				CollisionSide |= COL_RIGHT;
3003 				return CollisionSide;
3004 			}
3005 
3006 			if(*pf & PX_DIRT || *pf & PX_ROCK) {
3007 				if(y<py)
3008 					top++;
3009 				if(y>py)
3010 					bottom++;
3011 				if(x<px)
3012 					left++;
3013 				if(x>px)
3014 					right++;
3015 
3016 				//return Collision(*pf);
3017 			}
3018 
3019 			pf++;
3020 		}
3021 	}
3022 
3023 	// Check for a collision
3024 	if(top || bottom || left || right) {
3025 		CollisionSide = 0;
3026 
3027 
3028 		// Find the collision side
3029 		if( (left>right || left>2) && left>1 && vel.x < 0)
3030 			CollisionSide = COL_LEFT;
3031 
3032 		if( (right>left || right>2) && right>1 && vel.x > 0)
3033 			CollisionSide = COL_RIGHT;
3034 
3035 		if(top>1 && vel.y < 0)
3036 			CollisionSide = COL_TOP;
3037 
3038 		if(bottom>1 && vel.y > 0)
3039 			CollisionSide = COL_BOTTOM;
3040 	}
3041 
3042 
3043 	return CollisionSide; */
3044 }
3045 
SmartPointer_ObjectDeinit(CMap * obj)3046 template <> void SmartPointer_ObjectDeinit<CMap> ( CMap * obj )
3047 {
3048 	delete obj;
3049 }
3050 
3051 
3052 
3053 ///////////////////
3054 // Get the level name from specified file
GetLevelName(const std::string & filename,bool abs_filename)3055 std::string CMap::GetLevelName(const std::string& filename, bool abs_filename)
3056 {
3057 	MapLoader* loader = MapLoader::open(abs_filename ? filename : ("levels/" + filename), abs_filename, false);
3058 	if(!loader) return "";
3059 
3060 	std::string name = loader->header().name;
3061 	delete loader;
3062 	return name;
3063 }
3064 
PostProcessMirroredMap()3065 void CMap::PostProcessMirroredMap()
3066 {
3067 	if (!cClient->getGameLobby()->features[FT_MirroredMap] && !cClient->getGameLobby()->features[FT_MirroredMapTop])
3068 		return;
3069 
3070 	if (cClient->getGameLobby()->features[FT_MirroredMap]) {
3071 		CMap m;
3072 		m.Create(Width * 2, Height, Theme.name, MinimapWidth, MinimapHeight); // This erases filename and other data
3073 
3074 		if (bmpBackImageHiRes.get())
3075 			m.bmpBackImageHiRes = gfxCreateSurface(bmpBackImageHiRes->w * 2, bmpBackImageHiRes->h);
3076 
3077 		lockFlags();
3078 		m.lockFlags();
3079 
3080 		if ((int)cClient->getGameLobby()->features[FT_MirroredMapSide] == 1) {
3081 			// Add to the right side
3082 			DrawImage(m.bmpImage.get(), bmpImage, 0, 0);
3083 			DrawImageAdv_Mirror(m.bmpImage.get(), bmpImage, 0, 0, bmpImage->w, 0, bmpImage->w, bmpImage->h);
3084 
3085 			DrawImage(m.bmpDrawImage.get(), bmpDrawImage, 0, 0);
3086 			DrawImageAdv_Mirror(m.bmpDrawImage.get(), bmpDrawImage, 0, 0, bmpDrawImage->w, 0, bmpDrawImage->w, bmpDrawImage->h);
3087 
3088 			DrawImage(m.bmpBackImage.get(), bmpBackImage, 0, 0);
3089 			DrawImageAdv_Mirror(m.bmpBackImage.get(), bmpBackImage, 0, 0, bmpBackImage->w, 0, bmpBackImage->w, bmpBackImage->h);
3090 
3091 			if (bmpBackImageHiRes.get()) {
3092 				DrawImage(m.bmpBackImageHiRes.get(), bmpBackImageHiRes, 0, 0);
3093 				DrawImageAdv_Mirror(m.bmpBackImageHiRes.get(), bmpBackImageHiRes, 0, 0, bmpBackImageHiRes->w, 0, bmpBackImageHiRes->w, bmpBackImageHiRes->h);
3094 			}
3095 
3096 			for (unsigned y = 0; y < Height; y++) {
3097 				for (unsigned x = 0; x < Width; x++) {
3098 					m.PixelFlags[y * Width * 2 + x] = PixelFlags[y * Width + x];
3099 					m.PixelFlags[y * Width * 2 + (Width * 2 - x - 1)] = PixelFlags[y * Width + x];
3100 				}
3101 			}
3102 		} else {
3103 			// Add to the left side
3104 			DrawImage(m.bmpImage.get(), bmpImage, bmpImage->w, 0);
3105 			DrawImageAdv_Mirror(m.bmpImage.get(), bmpImage, 0, 0, 0, 0, bmpImage->w, bmpImage->h);
3106 
3107 			DrawImage(m.bmpDrawImage.get(), bmpDrawImage, bmpDrawImage->w, 0);
3108 			DrawImageAdv_Mirror(m.bmpDrawImage.get(), bmpDrawImage, 0, 0, 0, 0, bmpDrawImage->w, bmpDrawImage->h);
3109 
3110 			DrawImage(m.bmpBackImage.get(), bmpBackImage, bmpBackImage->w, 0);
3111 			DrawImageAdv_Mirror(m.bmpBackImage.get(), bmpBackImage, 0, 0, 0, 0, bmpBackImage->w, bmpBackImage->h);
3112 
3113 			if (bmpBackImageHiRes.get()) {
3114 				DrawImage(m.bmpBackImageHiRes.get(), bmpBackImageHiRes, bmpBackImageHiRes->w, 0);
3115 				DrawImageAdv_Mirror(m.bmpBackImageHiRes.get(), bmpBackImageHiRes, 0, 0, 0, 0, bmpBackImageHiRes->w, bmpBackImageHiRes->h);
3116 			}
3117 
3118 			for (unsigned y = 0; y < Height; y++) {
3119 				for (unsigned x = 0; x < Width; x++) {
3120 					m.PixelFlags[y * Width * 2 + x + Width] = PixelFlags[y * Width + x];
3121 					m.PixelFlags[y * Width * 2 + (Width - x - 1)] = PixelFlags[y * Width + x];
3122 				}
3123 			}
3124 		}
3125 
3126 		Width *= 2;
3127 		nTotalDirtCount *= 2;
3128 
3129 		delete [] PixelFlags;
3130 		PixelFlags = NULL;
3131 		unlockFlags();
3132 		CreatePixelFlags();
3133 		lockFlags();
3134 		memcpy(PixelFlags, m.PixelFlags, Width * Height);
3135 		unlockFlags();
3136 		m.unlockFlags();
3137 
3138 		bmpImage = m.bmpImage;
3139 		bmpDrawImage = m.bmpDrawImage;
3140 		bmpBackImage = m.bmpBackImage;
3141 		bmpShadowMap = m.bmpShadowMap;
3142 		if (bmpBackImageHiRes.get()) {
3143 			bmpBackImageHiRes = m.bmpBackImageHiRes;
3144 		}
3145 #ifdef _AI_DEBUG
3146 		bmpDebugImage = m.bmpDebugImage;
3147 #endif
3148 	}
3149 
3150 	if (cClient->getGameLobby()->features[FT_MirroredMapTop]) {
3151 		CMap m;
3152 		m.Create(Width, Height * 2, Theme.name, MinimapWidth, MinimapHeight); // This erases filename and other data
3153 
3154 		if (bmpBackImageHiRes.get())
3155 			m.bmpBackImageHiRes = gfxCreateSurface(bmpBackImageHiRes->w, bmpBackImageHiRes->h * 2);
3156 
3157 		lockFlags();
3158 		m.lockFlags();
3159 
3160 		// Add to the right side
3161 		DrawImage(m.bmpImage.get(), bmpImage, 0, bmpImage->h);
3162 		DrawImageAdv_MirrorVertical(m.bmpImage.get(), bmpImage, 0, 0, 0, 0, bmpImage->w, bmpImage->h);
3163 
3164 		DrawImage(m.bmpDrawImage.get(), bmpDrawImage, 0, bmpDrawImage->h);
3165 		DrawImageAdv_MirrorVertical(m.bmpDrawImage.get(), bmpDrawImage, 0, 0, 0, 0, bmpDrawImage->w, bmpDrawImage->h);
3166 
3167 		DrawImage(m.bmpBackImage.get(), bmpBackImage, 0, bmpBackImage->h);
3168 		DrawImageAdv_MirrorVertical(m.bmpBackImage.get(), bmpBackImage, 0, 0, 0, 0, bmpBackImage->w, bmpBackImage->h);
3169 
3170 		if (bmpBackImageHiRes.get()) {
3171 			DrawImage(m.bmpBackImageHiRes.get(), bmpBackImageHiRes, 0, bmpBackImageHiRes->h);
3172 			DrawImageAdv_MirrorVertical(m.bmpBackImageHiRes.get(), bmpBackImageHiRes, 0, 0, 0, 0, bmpBackImageHiRes->w, bmpBackImageHiRes->h);
3173 		}
3174 
3175 		for (unsigned y = 0; y < Height; y++) {
3176 			for (unsigned x = 0; x < Width; x++) {
3177 				m.PixelFlags[(Height + y) * Width + x] = PixelFlags[y * Width + x];
3178 				m.PixelFlags[(Height - y - 1) * Width + x] = PixelFlags[y * Width + x];
3179 			}
3180 		}
3181 
3182 		Height *= 2;
3183 		nTotalDirtCount *= 2;
3184 
3185 		delete [] PixelFlags;
3186 		PixelFlags = NULL;
3187 		unlockFlags();
3188 		CreatePixelFlags();
3189 		lockFlags();
3190 		memcpy(PixelFlags, m.PixelFlags, Width * Height);
3191 		unlockFlags();
3192 		m.unlockFlags();
3193 
3194 		bmpImage = m.bmpImage;
3195 		bmpDrawImage = m.bmpDrawImage;
3196 		bmpBackImage = m.bmpBackImage;
3197 		bmpShadowMap = m.bmpShadowMap;
3198 		if (bmpBackImageHiRes.get()) {
3199 			bmpBackImageHiRes = m.bmpBackImageHiRes;
3200 		}
3201 #ifdef _AI_DEBUG
3202 		bmpDebugImage = m.bmpDebugImage;
3203 #endif
3204 	}
3205 
3206 	if (GridFlags)
3207 		delete[] GridFlags;
3208 	GridFlags = NULL;
3209 	if (AbsoluteGridFlags)
3210 		delete[] AbsoluteGridFlags;
3211 	AbsoluteGridFlags = NULL;
3212 	createGrid();
3213 
3214 	if (CollisionGrid)
3215 		delete [] CollisionGrid;
3216 	CollisionGrid = NULL;
3217 	createCollisionGrid();
3218 
3219 	bMiniMapDirty = true;
3220 
3221 	LoadPostProcess();
3222 }
3223