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