1 /* $Id: map.hpp,v 1.74.4.1 2006/01/20 11:23:31 chfreund Exp $ */
2 
3 /******************************************************************************/
4 
5 #ifndef _MAP_HPP_
6 #define _MAP_HPP_
7 
8 /******************************************************************************/
9 
10 #include <SDL.h>
11 
12 #include "global.hpp"
13 #include "string.hpp"
14 #include "serializable.hpp"
15 #include "serialize.hpp"
16 #include "mapmemory.hpp"
17 #include "flag.hpp"
18 #include "theme.hpp"
19 #include "graphics.hpp"
20 
21 /******************************************************************************/
22 
23 // forward declaration needed for method ServerMap::createFromTheme
24 class World;
25 
26 //////////////////////////////////////////////////////////////////////
27 // Map contains the 'static' part of the battleground (e.g. a
28 // background, the foreground pixels, flags for each pixel etc)
29 //////////////////////////////////////////////////////////////////////
30 
31 /******************************************************************************/
32 
33 class Map {
34 	protected:
35 		//! sets entire flag field to zero
36 		void initializeFlags( void );
37 		//! m_sizeX is the width, m_sizeY the height of the map
38 		Uint32 m_sizeX, m_sizeY;
39 		//! A flag field e.g. for marking obstacles
40 		MapMemory<Flag> m_flags;
41 
42 /******************************************************************************/
43 
44 	public:
Map(Uint32 sizeX,Uint32 sizeY)45 		Map( Uint32 sizeX, Uint32 sizeY )
46 			: m_sizeX( sizeX ), m_sizeY( sizeY ),
47 			  m_flags( sizeX, sizeY ) {
48 			initializeFlags();
49 		}
50 
51 /******************************************************************************/
52 
Map(MapMemory<Flag> & flags)53 		Map( MapMemory<Flag>& flags )
54 			: m_sizeX( flags.getSizeX() ), m_sizeY( flags.getSizeY() ),
55 			  m_flags( flags ) {
56 		}
57 
58 /******************************************************************************/
59 
~Map()60 		virtual ~Map() { }
61 
62 /******************************************************************************/
63 
64 		// returns the width of the map
getSizeX() const65 		Uint32 getSizeX() const {
66 			return m_sizeX;
67 		}
68 
69 /******************************************************************************/
70 
71 		// returns the height of the map
getSizeY() const72 		Uint32 getSizeY() const {
73 			return m_sizeY;
74 		}
75 
76 /******************************************************************************/
77 
78 		// returns a pointer to the flag container
getFlags()79 		MapMemory<Flag>* getFlags() {
80 			return &m_flags;
81 		}
82 
83 /******************************************************************************/
84 
85 		//! \name methods concerning map creation from a theme definition
86 		//@{
87 		//! creates a map from a theme
88 		void createFromTheme( Theme& theme, World& world );
89 		//! fills the map with random circles
90 		void fillWithRandomCircles();
91 		//! places the passed sprite as map stuff of passed material
92 		void placeMapStuffItem( const Sprite* const item,
93 		                        const Sprite* const flags,
94 		                        const Uint32 x, const Uint32 y,
95 		                        const int material,
96 		                        const bool smoothable = true );
97 		//! places the passed sprite as map stuff of passed material
placeMapStuffItem(const Sprite * const item,const Uint32 x,const Uint32 y,const int material,const bool smoothable=true)98 		void placeMapStuffItem( const Sprite* const item,
99 		                        const Uint32 x, const Uint32 y,
100 		                        const int material,
101 		                        const bool smoothable = true ) {
102 			placeMapStuffItem( item, item, x, y, material, smoothable );
103 		}
104 		//@}
105 
106 		void displayFlags();
107 		bool saveFlags( const char* comment );
108 
109 		virtual void getColorAt( const Uint32 x, const Uint32 y,
110 		                         Uint8& red, Uint8& green, Uint8& blue ) = 0;
111 		virtual void setColorAt( const Uint32 x, const Uint32 y,
112 		                         const Uint8 r, const Uint8 g, const Uint8 b ) = 0;
113 		virtual void setColorAt( const Uint32 x, const Uint32 y, const Uint32 color ) = 0;
114 
115 /******************************************************************************/
116 
destroyEarthPixel(const Uint32 x,const Uint32 y,const Flag mask)117 		void destroyEarthPixel( const Uint32 x, const Uint32 y, const Flag mask ) {
118 			Flag& flag = m_flags.getReferenceTo( x, y );
119 
120 			if( (flag & Flags::OBSTACLE_EARTH) && ! (flag & mask) ) {
121 				LOG( 5 ) INFO( "Map::destroyEarthPixel: at (%i, %i)\n", x, y );
122 				flag = 0;
123 				setColorAt( x, y, 0, 0, 0 );
124 			}
125 		}
126 
127 /******************************************************************************/
128 
destroyEarthPixelClip(const Uint32 x,const Uint32 y,const Flag mask)129 		void destroyEarthPixelClip( const Uint32 x, const Uint32 y, const Flag mask ) {
130 			if( x < m_sizeX && y < m_sizeY ) {
131 				destroyEarthPixel( x, y, mask );
132 			}
133 		}
134 
135 		//! combines all flags in the specified rectangle with binary OR
136 
137 		/*! Combines all map flags in a rectangular area with the binary
138 		 *  OR operator and returns this value. The rectange area is
139 		 *  specified by passing its uper left corner (x1,y1) and its
140 		 *  bottom right corner (x2,y2). This means that the rectangle
141 		 *  includes the points (x1,y1) and (x2,y2).
142 		 */
getOrCombinedFlagsInRect(Sint32 x1,Sint32 y1,Sint32 x2,Sint32 y2)143 		Flag getOrCombinedFlagsInRect( Sint32 x1, Sint32 y1,
144 		                               Sint32 x2, Sint32 y2 ) {
145 			if(   x1 < 0               )  x1 = 0;
146 			if(   y1 < 0               )  y1 = 0;
147 			if( ++x2 > (Sint32)m_sizeX )  x2 = (Sint32)m_sizeX;
148 			if( ++y2 > (Sint32)m_sizeY )  y2 = (Sint32)m_sizeY;
149 
150 			Flag orCollected = 0;
151 			for( int y = y1; y < y2; y++ ) {
152 				for( int x = x1; x < x2; x++ ) {
153 					orCollected |= m_flags.getValueAt( x, y );
154 				}
155 			}
156 			return orCollected;
157 		}
158 
159 /******************************************************************************/
160 
testPassable(const Uint32 x,const Uint32 y,const Flag mask)161 		bool testPassable( const Uint32 x, const Uint32 y, const Flag mask ) {
162 			return ! (m_flags.getValueAt( x, y ) & mask);
163 		}
164 
165 /******************************************************************************/
166 
testPassableClip(const Uint32 x,const Uint32 y,const Flag mask)167 		bool testPassableClip( const Uint32 x, const Uint32 y, const Flag mask ) {
168 			if( x >= m_sizeX || y >= m_sizeY ) return false;
169 			return ! (m_flags.getValueAt( x, y ) & mask);
170 		}
171 
172 /******************************************************************************/
173 
testPassableSmoothing(const Uint32 x,const Uint32 y,const Flag mask)174 		bool testPassableSmoothing( const Uint32 x, const Uint32 y,
175 		                            const Flag mask ) {
176 			DBG( 5 ) {
177 				if( x >= m_sizeX || y >= m_sizeY ) {
178 					CHECK( false, "Map::testPassableSmoothing: coordinates out of bounds: "
179 					              "(%3i, %3i)\n", x, y );
180 					return false;
181 				}
182 			}
183 
184 			const Flag& flag = m_flags.getReferenceTo( x, y );
185 
186 			if( ! (flag & mask) ) {
187 				/*
188 				int passableNeighbors = 0;
189 				if( m_flags.getValueAt( x-1, y-1 ) & Flag::PASSABLE ) passableNeighbors++;
190 				if( m_flags.getValueAt( x  , y-1 ) & Flag::PASSABLE ) passableNeighbors++;
191 				if( m_flags.getValueAt( x+1, y-1 ) & Flag::PASSABLE ) passableNeighbors++;
192 				if( m_flags.getValueAt( x-1, y   ) & Flag::PASSABLE ) passableNeighbors++;
193 				if( m_flags.getValueAt( x+1, y   ) & Flag::PASSABLE ) passableNeighbors++;
194 				if( m_flags.getValueAt( x-1, y+1 ) & Flag::PASSABLE ) passableNeighbors++;
195 				if( m_flags.getValueAt( x  , y+1 ) & Flag::PASSABLE ) passableNeighbors++;
196 				if( m_flags.getValueAt( x+1, y+1 ) & Flag::PASSABLE ) passableNeighbors++;
197 
198 				if( passableNeighbors <= 3 ) {
199 					LOG( 4 ) INFO( "Map::testPassableSmoothing: isolated hole => fill pixel\n" );
200 					m_flags.getReferenceTo( x, y ) &= ~Flag::PASSABLE;
201 					m_pixels.setValueAt( x, y, 0x00ff00 );
202 					return false;
203 				}*/
204 				return true;
205 			}
206 			else {
207 				if( ! (flag & Flags::UNSMOOTHABLE) ) {
208 					int passableNeighbors = 0;
209 					if( ! (m_flags.getValueAt( x-1, y-1 ) & mask) ) passableNeighbors++;
210 					if( ! (m_flags.getValueAt( x  , y-1 ) & mask) ) passableNeighbors++;
211 					if( ! (m_flags.getValueAt( x+1, y-1 ) & mask) ) passableNeighbors++;
212 					if( ! (m_flags.getValueAt( x-1, y   ) & mask) ) passableNeighbors++;
213 					if( ! (m_flags.getValueAt( x+1, y   ) & mask) ) passableNeighbors++;
214 					if( ! (m_flags.getValueAt( x-1, y+1 ) & mask) ) passableNeighbors++;
215 					if( ! (m_flags.getValueAt( x  , y+1 ) & mask) ) passableNeighbors++;
216 					if( ! (m_flags.getValueAt( x+1, y+1 ) & mask) ) passableNeighbors++;
217 
218 					if( passableNeighbors >= 5 ) {
219 						LOG( 5 ) INFO( "Map::testPassableSmoothing: isolated pixel => destroy pixel\n" );
220 						destroyEarthPixel( x, y, 0 );
221 						DBG( 5 ) setColorAt( x, y, 0, 255, 0 );
222 						return true;
223 					}
224 				}
225 				return false;
226 			}
227 		}
228 
229 /******************************************************************************/
230 
makeRectHole(const Sint32 sx,const Sint32 sy,const Sint32 width,const Sint32 height,const Flag mask)231 		void makeRectHole( const Sint32 sx, const Sint32 sy, const Sint32 width,
232 		                   const Sint32 height, const Flag mask ) {
233 			for( int y = sy; y < sy + height; y++ )
234 				for( int x = sx; x < sx + width; x++ )
235 					destroyEarthPixelClip( x, y, mask );
236 		}
237 
238 /******************************************************************************/
239 
makeHole(const Sint32 xc,const Sint32 yc,const Sint32 size,const Flag mask)240 		void makeHole( const Sint32 xc, const Sint32 yc,
241 		               const Sint32 size, const Flag mask ) {
242 			Sint32 x = 0;
243 			Sint32 y = size;
244 			real d = 1.25 - static_cast<real>( size );
245 
246 			Sint32 i;
247 			for ( i = -x; i <= x; i++ ) {
248 				destroyEarthPixelClip( xc + i, yc + y, mask );
249 				destroyEarthPixelClip( xc + i, yc - y, mask );
250 			}
251 			for ( i = -y; i <= y; i++ ) {
252 				destroyEarthPixelClip( xc + i, yc + x, mask );
253 				destroyEarthPixelClip( xc + i, yc - x, mask );
254 			}
255 
256 			while ( y > x ) {
257 				x++;
258 				if ( d > 0 ) {
259 					y--;
260 					d += 2.0 * (x - y);
261 				}
262 				else {
263 					d += 2.0 * x + 1.0;
264 				}
265 
266 				for ( i = -x; i <= x; i++ ) {
267 					destroyEarthPixelClip( xc + i, yc + y, mask );
268 					destroyEarthPixelClip( xc + i, yc - y, mask );
269 				}
270 				for ( i = -y; i <= y; i++ ) {
271 					destroyEarthPixelClip( xc + i, yc + x, mask );
272 					destroyEarthPixelClip( xc + i, yc - x, mask );
273 				}
274 			}
275 		}
276 
277 /******************************************************************************/
278 
setSpecialEarth(const Uint32 x,const Uint32 y,const Flag flag)279 		void setSpecialEarth( const Uint32 x, const Uint32 y,
280 		                      const Flag flag ) {
281 			// check clipping
282 			if( x >=  m_sizeX  || y >=  m_sizeY ) return;
283 
284 			real brightness;
285 			int r, g, b;
286 
287 			if( flag & Flags::INDESTRUCTIBLE ) {
288 				brightness = 0.5 + 0.2*localRnd.getNormedReal();
289 				r = g = b = static_cast<int>( 0x9c * brightness );
290 			}
291 			else if( flag & Flags::UNDIGGABLE ) {
292 				brightness = 0.5 + 0.2*localRnd.getNormedReal();
293 				r = static_cast<int>( 0x9c * brightness ),
294 				g = static_cast<int>( 0x5a * brightness ),
295 				b = static_cast<int>( 0x31 * brightness );
296 			} else {
297 				brightness = 0.7 + 0.3*localRnd.getNormedReal();
298 				r = static_cast<int>( 0x9c * brightness );
299 				g = static_cast<int>( 0x5a * brightness );
300 				b = static_cast<int>( 0x31 * brightness );
301 			}
302 
303 			setColorAt( x, y, r, g, b );
304 			m_flags.getReferenceTo( x, y ) = flag;
305 		}
306 
307 /******************************************************************************/
308 
makeStone(const Sint32 xc,const Sint32 yc,const Sint32 size,const Flag flag,const Flag mask)309 		void makeStone( const Sint32 xc, const Sint32 yc, const Sint32 size,
310 		                const Flag flag, const Flag mask ) {
311 			Sint32 x = 0;
312 			Sint32 y = size;
313 			real d = 1.25 - static_cast<real>( size );
314 
315 			Sint32 i;
316 			for ( i = -x; i <= x; i++ ) {
317 				if( testPassableClip( xc + i, yc + y, mask )) setSpecialEarth( xc + i, yc + y, flag );
318 				if( testPassableClip( xc + i, yc - y, mask )) setSpecialEarth( xc + i, yc - y, flag );
319 			}
320 			for ( i = -y; i <= y; i++ ) {
321 				if( testPassableClip( xc + i, yc + x, mask )) setSpecialEarth( xc + i, yc + x, flag );
322 				if( testPassableClip( xc + i, yc - x, mask )) setSpecialEarth( xc + i, yc - x, flag );
323 			}
324 
325 			while ( y > x ) {
326 				x++;
327 				if ( d > 0 ) {
328 					y--;
329 					d += 2.0 * (x - y);
330 				}
331 				else {
332 					d += 2.0 * x + 1.0;
333 				}
334 
335 				for ( i = -x; i <= x; i++ ) {
336 					if( testPassableClip( xc + i, yc + y, mask )) setSpecialEarth( xc + i, yc + y, flag );
337 					if( testPassableClip( xc + i, yc - y, mask )) setSpecialEarth( xc + i, yc - y, flag );
338 				}
339 				for ( i = -y; i <= y; i++ ) {
340 					if( testPassableClip( xc + i, yc + x, mask )) setSpecialEarth( xc + i, yc + x, flag );
341 					if( testPassableClip( xc + i, yc - x, mask )) setSpecialEarth( xc + i, yc - x, flag );
342 				}
343 			}
344 		}
345 
346 /******************************************************************************/
347 
calculateBounceAngle(const real dx,const real dy,const int cx,const int cy,const Flag mask)348 		real calculateBounceAngle( const real dx, const real dy,
349 		                            const int cx, const int cy, const Flag mask ) {
350 			static const int  dxTest[8] = { 0,  1,  1,  1,  0, -1, -1, -1},
351 			                  dyTest[8] = {-1, -1,  0,  1,  1,  1,  0, -1};
352 			const real incomingAngle = ATAN2_REAL( dy, dx );
353 			int incomingDir = static_cast<int>( 8*incomingAngle/(2*M_PI) + 6.5 ) % 8;
354 
355 			if( ! testPassableSmoothing( cx + dxTest[incomingDir],
356 			                             cy + dyTest[incomingDir], mask ) ) {
357 				incomingDir = (incomingDir + 1) % 8;
358 				if( ! testPassableSmoothing( cx + dxTest[incomingDir],
359 				                             cy + dyTest[incomingDir], mask ) ) {
360 					incomingDir = (incomingDir + 6) % 8;
361 					if( ! testPassableSmoothing( cx + dxTest[incomingDir],
362 					                             cy + dyTest[incomingDir], mask ) ) {
363 						incomingDir = (incomingDir + 1) % 8;
364 					}
365 				}
366 			}
367 
368 			int cwTest = (incomingDir+1) % 8, ccwTest = (incomingDir+7) % 8;
369 
370 			LOG( 5 )
371 			INFO( "Map::calculateBounceAngle:\n%c%c%c\n%c%c%c\n%c%c%c\n",
372 			      (testPassableSmoothing( cx - 1, cy - 1, mask ) ? '.' : '#'),
373 			      (testPassableSmoothing( cx    , cy - 1, mask ) ? '.' : '#'),
374 			      (testPassableSmoothing( cx + 1, cy - 1, mask ) ? '.' : '#'),
375 			      (testPassableSmoothing( cx - 1, cy    , mask ) ? '.' : '#'),
376 			      (testPassableSmoothing( cx    , cy    , mask ) ? '.' : '#'),
377 			      (testPassableSmoothing( cx + 1, cy    , mask ) ? '.' : '#'),
378 			      (testPassableSmoothing( cx - 1, cy + 1, mask ) ? '.' : '#'),
379 			      (testPassableSmoothing( cx    , cy + 1, mask ) ? '.' : '#'),
380 			      (testPassableSmoothing( cx + 1, cy + 1, mask ) ? '.' : '#') );
381 
382 			// look for obstacles in clockwise direction
383 			while( testPassableSmoothing( cx + dxTest[cwTest], cy + dyTest[cwTest], mask )
384 			       && cwTest != incomingDir )
385 				cwTest = (cwTest+1) % 8;
386 
387 			// look for obstacles in counter-clockwise direction
388 			while( testPassableSmoothing( cx + dxTest[ccwTest], cy + dyTest[ccwTest], mask )
389 			       && ccwTest != incomingDir )
390 				ccwTest = (ccwTest+7) % 8;
391 
392 			LOG( 5 ) INFO( "incomingDir: %i; cwTest: %i; ccwTest: %i\n",
393 			      incomingDir, cwTest, ccwTest );
394 
395 			/*
396 			if( (ccwTest-cwTest+8) % 8 <= 2 ) {
397 				return -incomingAngle;
398 			}
399 			else {
400 			*/
401 				return -incomingAngle + (M_PI*45.0/180.0)*(cwTest+ccwTest);
402 			//}
403 		}
404 
405 /******************************************************************************/
406 
testCollRect(const int px,const int py,const int width,const int height,int & cx,int & cy)407 		bool testCollRect( const int px, const int py,
408 		                   const int width, const int height,
409 		                   int& cx, int& cy ) {
410 			LOG( 5 ) INFO( "Map::testCollRect: px: %3i; py: %3i; width: %3i; height: %3i\n",
411 			                px, py, width, height );
412 
413 			for( int x = px; x < px+width; x++ ) {
414 				//m_pixels.setValueAt( x, py, 0xffff00 );
415 				if( ! testPassableSmoothing( x, py, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ) ) {
416 					cx = x; cy = py;
417 					LOG( 5 ) INFO( "Acoll at %i, %i\n", cx, cy );
418 					DBG( 5 ) setColorAt( cx, cy, 0, 255, 0 );
419 					return false;
420 				}
421 				//m_pixels.setValueAt( x, py+height-1, 0xffff00 );
422 				if( ! testPassableSmoothing( x, py+height-1, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ) ) {
423 					cx = x; cy = py+height-1;
424 					LOG( 5 ) INFO( "Bcoll at %i, %i\n", cx, cy );
425 					DBG( 5 ) setColorAt( cx, cy, 0, 255, 0 );
426 					return false;
427 				}
428 			}
429 
430 			for( int y = py+1; y < py+height-1; y++ ) {
431 				//m_pixels.setValueAt( px      , y, 0xffff00 );
432 				if( ! testPassableSmoothing( px, y, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ) ) {
433 					cx = px; cy = y;
434 					LOG( 5 ) INFO( "Ccoll at %i, %i\n", cx, cy );
435 					DBG( 5 ) setColorAt( cx, cy, 0, 255, 0 );
436 					return false;
437 				}
438 				//m_pixels.setValueAt( px+width-1, y, 0xffff00 );
439 				if( ! testPassableSmoothing( px+width-1, y, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ) ) {
440 					cx = px+width-1; cy = y;
441 					LOG( 5 ) INFO( "Dcoll at %i, %i\n", cx, cy );
442 					DBG( 5 ) setColorAt( cx, cy, 0, 255, 0 );
443 					return false;
444 				}
445 			}
446 
447 			return true;
448 		}
449 
450 /******************************************************************************/
451 
testFilledCollRect(const int px,const int py,const int width,const int height)452 		bool testFilledCollRect( const int px, const int py,
453 		                         const int width, const int height ) {
454 			LOG( 5 ) INFO( "Map::testFilledCollRect: px: %3i; py: %3i; width: %3i; height: %3i\n",
455 			                px, py, width, height );
456 
457 			// check clipping
458 			if( px < 0 || py < 0 || static_cast<Uint32>( px+width  ) >=  m_sizeX  ||
459 			                        static_cast<Uint32>( py+height ) >=  m_sizeY ) {
460 				return false;
461 			}
462 
463 			for( int y = py; y < py+height; y++ ) {
464 				for( int x = px; x < px+width; x++ ) {
465 					//m_pixels.setValueAt( x, py, 0xffff00 );
466 					if( ! testPassableSmoothing( x, y, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ) ) {
467 						return false;
468 					}
469 				}
470 			}
471 
472 			return true;
473 		}
474 
475 /******************************************************************************/
476 
testFilledCollRectCollPos(const int px,const int py,const int width,const int height,int & cx,int & cy)477 		bool testFilledCollRectCollPos( const int px, const int py,
478 		                                const int width, const int height,
479 		                                int& cx, int& cy ) {
480 			LOG( 5 ) INFO( "Map::testFilledCollRect: px: %3i; py: %3i; width: %3i; height: %3i\n",
481 			                px, py, width, height );
482 
483 			// check clipping
484 			if( px < 0 || py < 0 || static_cast<Uint32>( px+width  ) >=  m_sizeX  ||
485 			                        static_cast<Uint32>( py+height ) >=  m_sizeY ) {
486 				cx = cy = -1;
487 				return false;
488 			}
489 
490 			for( cy = py; cy < py+height; cy++ ) {
491 				for( cx = px; cx < px+width; cx++ ) {
492 					//m_pixels.setValueAt( x, py, 0xffff00 );
493 					if( ! testPassableSmoothing( cx, cy, Flags::OBSTACLE_EARTH | Flags::OBSTACLE_OBJECT ) ) {
494 						return false;
495 					}
496 				}
497 			}
498 
499 			return true;
500 		}
501 
502 /******************************************************************************/
503 
setCollRect(const int px,const int py,const int width,const int height)504 		void setCollRect( const int px, const int py,
505 		                  const int width, const int height ) {
506 			LOG( 5 ) INFO( "Map::setCollRect: setting collision rectangle at (%i, %i, %i, %i)\n",
507 			               px, py, width, height );
508 			bool saveFlagsLater = false;
509 			String comment;
510 			DBG( 3 ) {
511 				if( ! CHECK( testFilledCollRect( px, py, width, height ),
512 				             "Map::setCollRect: collRect not completely passable\n" ) ) {
513 					comment.format( "Map::setCollRect: collRect( %i, %i, %i, %i ) not "
514 					                "completely passable", px, py,
515 					                width, height );
516 					saveFlagsLater = true;
517 				}
518 			}
519 
520 			int x, y;
521 			for( y = py; y < py+height; y++ ) {
522 				for( x = px; x < px+width; x++ ) {
523 					m_flags.getReferenceTo( x, y ) |= Flags::OBSTACLE_OBJECT | Flags::UNABLE_ANY;
524 				}
525 			}
526 
527 			if( saveFlagsLater ) saveFlags( comment );
528 		}
529 
530 /******************************************************************************/
531 
removeCollRect(const int px,const int py,const int width,const int height)532 		void removeCollRect( const int px, const int py,
533 		                     const int width, const int height ) {
534 			int x, y;
535 			for( y = py; y < py+height; y++ ) {
536 				for( x = px; x < px+width; x++ ) {
537 					m_flags.getReferenceTo( x, y ) &= ~(Flags::OBSTACLE_OBJECT | Flags::UNABLE_ANY);
538 				}
539 			}
540 		}
541 
542 /******************************************************************************/
543 
testLineCollRect(int x1,int y1,int x2,int y2,const int width,const int height,int & pcx,int & pcy,int & cx,int & cy)544 		bool testLineCollRect( int x1, int y1, int x2, int y2,
545 		                       const int width, const int height,
546 		                       int& pcx, int& pcy, int& cx, int& cy ) {
547 			int i;
548 			int sx, sy;  // step positive or negative (1 or -1)
549 			int dx, dy;  // delta (difference in X and Y between points)
550 			int e;
551 
552 			LOG( 5 ) INFO( "Map::testLineCollRect: x1: %3i; y1: %3i; x2: %3i; y2: %3i; width: %3i; height: %3i\n",
553 			               x1, y1, x2, y2, width, height );
554 			dx = abs( x2 - x1 );
555 			sx = (x2 - x1 > 0) ? 1 : -1;
556 			dy = abs( y2 - y1 );
557 			sy = (y2 - y1 > 0) ? 1 : -1;
558 
559 			cx = pcx = x1; cy = pcy = y1;
560 
561 			if( dy <= dx ) {
562 				e = (dy << 1) - dx;
563 				for( i = 0; i <= dx; i++ ) {
564 					if( ! testCollRect( x1, y1, width, height, cx, cy ) ) {
565 						DBG( 3 ) CHECK( ! (pcx == cx && pcy == cy && &pcx != &cx), "Map::testLineCollRect: "
566 						                "precollision coords == collision coords (A) "
567 						                "(%i, %i) -> (%i, %i) preColl: (%i, %i), coll: "
568 						                "(%i, %i)\n", x1, y1, x2, y2, pcx, pcy, cx, cy );
569 						return false;
570 					}
571 					else { pcx = x1; pcy = y1; }
572 
573 					if( e >= 0 ) {
574 						y1 += sy; e -= (dx << 1);
575 					}
576 					x1 += sx; e += (dy << 1);
577 				}
578 			}
579 			else {
580 		#define SWAP(a,b) {int tmpswap = a; a = b; b = tmpswap;}
581 				SWAP(x1, y1); SWAP(dx, dy); SWAP(sx, sy);
582 		#undef SWAP
583 				e = (dy << 1) - dx;
584 				for( i = 0; i <= dx; i++ ) {
585 					if( ! testCollRect( y1, x1, width, height, cx, cy ) ) {
586 						DBG( 3 ) CHECK( ! (pcx == cx && pcy == cy && &pcx != &cx), "Map::testLineCollRect: "
587 						                "precollision coords == collision coords (B) "
588 						                "(%i, %i) -> (%i, %i) preColl: (%i, %i), coll: "
589 						                "(%i, %i)\n", y1, x1, x2, y2, pcx, pcy, cx, cy );
590 						return false;
591 					}
592 					else { pcx = y1; pcy = x1; }
593 
594 					if( e >= 0 ) {
595 						y1 += sy; e -= (dx << 1);
596 					}
597 					x1 += sx; e += (dy << 1);
598 				}
599 			}
600 
601 			return true;
602 		}
603 
604 /******************************************************************************/
605 
testLinePassable(int x1,int y1,int x2,int y2,int & pcx,int & pcy,int & cx,int & cy,const Flag mask)606 		bool testLinePassable( int x1, int y1, int x2, int y2,
607 		                       int& pcx, int& pcy, int& cx, int& cy,
608 		                       const Flag mask ) {
609 			int i;
610 			int sx, sy;  // step positive or negative (1 or -1)
611 			int dx, dy;  // delta (difference in X and Y between points)
612 			int e;
613 
614 			dx = abs( x2 - x1 );
615 			sx = (x2 - x1 > 0) ? 1 : -1;
616 			dy = abs( y2 - y1 );
617 			sy = (y2 - y1 > 0) ? 1 : -1;
618 
619 			cx = pcx = x1; cy = pcy = y1;
620 
621 			if( dy <= dx ) {
622 				e = (dy << 1) - dx;
623 				for( i = 0; i <= dx; i++ ) {
624 					if( ! testPassable( x1, y1, mask ) ) {
625 						cx = x1; cy = y1;
626 						DBG( 5 ) CHECK( ! (pcx == cx && pcy == cy && &pcx != &cx), "Map::testLinePassable: "
627 						                "precollision coords == collision coords (A) "
628 						                "(%i, %i) -> (%i, %i) preColl: (%i, %i), coll: "
629 						                "(%i, %i)\n", x1, y1, x2, y2, pcx, pcy, cx, cy );
630 						return false;
631 					}
632 					else { pcx = x1; pcy = y1; }
633 
634 					if( e >= 0 ) {
635 						y1 += sy; e -= (dx << 1);
636 					}
637 					x1 += sx; e += (dy << 1);
638 				}
639 			}
640 			else {
641 		#define SWAP(a,b) {int tmpswap = a; a = b; b = tmpswap;}
642 				SWAP(x1, y1); SWAP(dx, dy); SWAP(sx, sy);
643 		#undef SWAP
644 				e = (dy << 1) - dx;
645 				for( i = 0; i <= dx; i++ ) {
646 					if( ! testPassable( y1, x1, mask ) ) {
647 						cx = y1; cy = x1;
648 						DBG( 5 ) CHECK( ! (pcx == cx && pcy == cy && &pcx != &cx), "Map::testLinePassable: "
649 						                "precollision coords == collision coords (B) "
650 						                "(%i, %i) -> (%i, %i) preColl: (%i, %i), coll: "
651 						                "(%i, %i)\n", y1, x1, x2, y2, pcx, pcy, cx, cy );
652 						return false;
653 					}
654 					else { pcx = y1; pcy = x1; }
655 
656 					if( e >= 0 ) {
657 						y1 += sy; e -= (dx << 1);
658 					}
659 					x1 += sx; e += (dy << 1);
660 				}
661 			}
662 
663 			return true;
664 		}
665 
666 /******************************************************************************/
667 
testLinePassableSmoothing(int x1,int y1,int x2,int y2,int & pcx,int & pcy,int & cx,int & cy,const Flag mask)668 		bool testLinePassableSmoothing( int x1, int y1, int x2, int y2,
669 		                                int& pcx, int& pcy, int& cx, int& cy,
670 		                                const Flag mask ) {
671 			int i;
672 			int sx, sy;  // step positive or negative (1 or -1)
673 			int dx, dy;  // delta (difference in X and Y between points)
674 			int e;
675 
676 			dx = abs( x2 - x1 );
677 			sx = (x2 - x1 > 0) ? 1 : -1;
678 			dy = abs( y2 - y1 );
679 			sy = (y2 - y1 > 0) ? 1 : -1;
680 
681 			cx = pcx = x1; cy = pcy = y1;
682 
683 			if( dy <= dx ) {
684 				e = (dy << 1) - dx;
685 				for( i = 0; i <= dx; i++ ) {
686 					if( ! testPassableSmoothing( x1, y1, mask ) ) {
687 						cx = x1; cy = y1;
688 						DBG( 5 ) CHECK( ! (pcx == cx && pcy == cy && &pcx != &cx), "Map::testLinePassableSmoothing: "
689 						                "precollision coords == collision coords (A) "
690 						                "(%i, %i) -> (%i, %i) preColl: (%i, %i), coll: "
691 						                "(%i, %i)\n", x1, y1, x2, y2, pcx, pcy, cx, cy );
692 						return false;
693 					}
694 					else { pcx = x1; pcy = y1; }
695 
696 					if( e >= 0 ) {
697 						y1 += sy; e -= (dx << 1);
698 					}
699 					x1 += sx; e += (dy << 1);
700 				}
701 			}
702 			else {
703 		#define SWAP(a,b) {int tmpswap = a; a = b; b = tmpswap;}
704 				SWAP(x1, y1); SWAP(dx, dy); SWAP(sx, sy);
705 		#undef SWAP
706 				e = (dy << 1) - dx;
707 				for( i = 0; i <= dx; i++ ) {
708 					if( ! testPassableSmoothing( y1, x1, mask ) ) {
709 						cx = y1; cy = x1;
710 						DBG( 5 ) CHECK( ! (pcx == cx && pcy == cy && &pcx != &cx), "Map::testLinePassableSmoothing: "
711 						                "precollision coords == collision coords (B) "
712 						                "(%i, %i) -> (%i, %i) preColl: (%i, %i), coll: "
713 						                "(%i, %i)\n", y1, x1, x2, y2, pcx, pcy, cx, cy );
714 						return false;
715 					}
716 					else { pcx = y1; pcy = x1; }
717 
718 					if( e >= 0 ) {
719 						y1 += sy; e -= (dx << 1);
720 					}
721 					x1 += sx; e += (dy << 1);
722 				}
723 			}
724 
725 			return true;
726 		}
727 
728 /******************************************************************************/
729 
drawLine(int x1,int y1,int x2,int y2,const Uint32 col)730 		void drawLine( int x1, int y1, int x2, int y2, const Uint32 col) {
731 			int i;
732 			int sx, sy;  // step positive or negative (1 or -1)
733 			int dx, dy;  // delta (difference in X and Y between points)
734 			int e;
735 
736 			dx = abs( x2 - x1 );
737 			sx = (x2 - x1 > 0) ? 1 : -1;
738 			dy = abs( y2 - y1 );
739 			sy = (y2 - y1 > 0) ? 1 : -1;
740 
741 			if( dy <= dx ) {
742 				e = (dy << 1) - dx;
743 				for( i = 0; i <= dx; i++ ) {
744 					setColorAt( x1, y1, col );
745 
746 					if( e >= 0 ) {
747 						y1 += sy; e -= (dx << 1);
748 					}
749 					x1 += sx; e += (dy << 1);
750 				}
751 			}
752 			else {
753 		#define SWAP(a,b) {int tmpswap = a; a = b; b = tmpswap;}
754 				SWAP(x1, y1); SWAP(dx, dy); SWAP(sx, sy);
755 		#undef SWAP
756 				e = (dy << 1) - dx;
757 				for( i = 0; i <= dx; i++ ) {
758 					setColorAt( y1, x1, col );
759 
760 					if( e >= 0 ) {
761 						y1 += sy; e -= (dx << 1);
762 					}
763 					x1 += sx; e += (dy << 1);
764 				}
765 			}
766 		}
767 
768 };
769 
770 /******************************************************************************/
771 /******************************************************************************/
772 /******************************************************************************/
773 
774 class ServerMap : public Map, public Serializable {
775 	private:
776 		//! Storage for the foreground pixels
777 		MapMemory<Uint32> m_pixels;
778 		//! Default format for the map on the server
779 		SDL_PixelFormat m_pixelFormat;
780 
781 	public:
782 		//! Constructor
ServerMap(int sizeX,int sizeY)783 		ServerMap( int sizeX, int sizeY )
784 			: Map( sizeX, sizeY ),
785 			  m_pixels( sizeX, sizeY ) {
786 			m_pixelFormat.palette = 0;
787 			m_pixelFormat.BitsPerPixel = 32;
788 			m_pixelFormat.BytesPerPixel = 4;
789 			m_pixelFormat.Rmask = 0x000000ff;
790 			m_pixelFormat.Gmask = 0x0000ff00;
791 			m_pixelFormat.Bmask = 0x00ff0000;
792 			m_pixelFormat.Amask = 0x00000000;
793 			m_pixelFormat.Rshift = 0;
794 			m_pixelFormat.Gshift = 8;
795 			m_pixelFormat.Bshift = 16;
796 			m_pixelFormat.Ashift = 24;
797 			m_pixelFormat.Rloss = 0;
798 			m_pixelFormat.Gloss = 0;
799 			m_pixelFormat.Bloss = 0;
800 			m_pixelFormat.Aloss = 0;
801 		}
802 
803 /******************************************************************************/
804 
805 		// Destructor
~ServerMap()806 		virtual ~ServerMap() {
807 			LOG( 4 ) INFO( "ServerMap::~ServerMap: destructor called\n" );
808 		}
809 
810 /******************************************************************************/
811 
812 		// returns a pointer to the pixel container
getPixels()813 		MapMemory<Uint32>* getPixels() {
814 			return &m_pixels;
815 		}
816 
817 /******************************************************************************/
818 
getPixelFormat() const819 		const SDL_PixelFormat* getPixelFormat() const {
820 			return &m_pixelFormat;
821 		}
822 
823 /******************************************************************************/
824 
getColorAt(const Uint32 x,const Uint32 y,Uint8 & red,Uint8 & green,Uint8 & blue)825 		virtual void getColorAt( const Uint32 x, const Uint32 y,
826 		                         Uint8& red, Uint8& green, Uint8& blue ) {
827 			Uint32 pixel = m_pixels.getValueAt( x, y );
828 			SDL_GetRGB( pixel, &m_pixelFormat, &red, &green, &blue );
829 		}
830 
831 /******************************************************************************/
832 
setColorAt(const Uint32 x,const Uint32 y,const Uint8 r,const Uint8 g,const Uint8 b)833 		virtual void setColorAt( const Uint32 x, const Uint32 y,
834 		                         const Uint8 r, const Uint8 g, const Uint8 b ) {
835 			const Uint32 pixel = SDL_MapRGB( &m_pixelFormat, r, g, b );
836 			m_pixels.setValueAt( x, y, pixel );
837 		}
838 
839 /******************************************************************************/
840 
setColorAt(const Uint32 x,const Uint32 y,const Uint32 color)841 		virtual void setColorAt( const Uint32 x, const Uint32 y, const Uint32 color ) {
842 			const Uint8 r = static_cast<Uint8>( color >> 16 ),
843 			            g = static_cast<Uint8>( color >>  8 ),
844 			            b = static_cast<Uint8>( color       );
845 			const Uint32 pixel = SDL_MapRGB( &m_pixelFormat, r, g, b );
846 			m_pixels.setValueAt( x, y, pixel );
847 		}
848 
849 /******************************************************************************/
850 
851 		//! creates a map from a theme
852 //		void createFromTheme( Theme& theme, World& world );
853 		//! fills the map with random circles
854 //		void fillWithRandomCircles();
855 		//! places the passed sprite as map stuff of passed material
856 		void placeMapStuffItem( const Sprite* const item,
857 		                        const Sprite* const flags,
858 		                        const Uint32 x, const Uint32 y,
859 		                        const int material,
860 		                        const bool smoothable = true );
861 		//! places the passed sprite as map stuff of passed material
placeMapStuffItem(const Sprite * const item,const Uint32 x,const Uint32 y,const int material,const bool smoothable=true)862 		void placeMapStuffItem( const Sprite* const item,
863 		                        const Uint32 x, const Uint32 y,
864 		                        const int material,
865 		                        const bool smoothable = true ) {
866 			placeMapStuffItem( item, item, x, y, material, smoothable );
867 		}
868 
869 		//! \name (de)serialization
870 		//@{
871 		//! return the size of the serialized map
getSerializeBufferSize() const872 		virtual Uint32 getSerializeBufferSize() const {
873 			return 2 * Serialize<Uint32>::sizeOf() +
874 				m_pixels.getSerializeBufferSize() +
875 				m_flags.getSerializeBufferSize();
876 		}
877 
878 		/*! \brief serialize the map into the buffer starting at
879 		 *  bufferPointer. After this method bufferPointer points to
880 		 *  next char after the last char of the serialization of the
881 		 *  map.
882 		 */
883 		virtual void serialize( Uint8*& bufferPointer ) const;
884 
885 		/*! \brief create a new map out of a serialization in the buffer
886 		 *  starting at bufferPointer. After this method bufferPointer
887 		 *  points to next char after the last char of the serialization
888 		 *  of the map.
889 		 */
890 		static ServerMap* createAndDeserialize( Uint8*& bufferPointer );
891 
892 		/*! \brief replace the existing map by deserializing the contents
893 		 *  of the buffer pointed to by bufferPointer. After this method
894 		 *  bufferPointer points to next char after the last char of the
895 		 *  serialization of the map.
896 		 */
897 		virtual void deserialize( Uint8*& bufferPointer );
898 		//@}
899 };
900 
901 /******************************************************************************/
902 /******************************************************************************/
903 /******************************************************************************/
904 
905 class ClientMap : public Map {
906 	private:
907 		//! Storage for the foreground pixels
908 		SDL_Surface* m_pixels;
909 
910 /******************************************************************************/
911 
912 	public:
913 		//! Constructor
ClientMap(ServerMap & serverMap,SDL_PixelFormat * format,Uint32 flags)914 		ClientMap( ServerMap& serverMap, SDL_PixelFormat* format, Uint32 flags )
915 			: Map( *serverMap.getFlags() ) {
916 			LOG( 3 ) INFO( "ClientMap::ClientMap: Constructor called\n" );
917 			LOG( 4 ) INFO( "ClientMap::ClientMap: Create surface from server map data\n" );
918 			SDL_Surface* serverSurface =
919 				SDL_CreateRGBSurfaceFrom( serverMap.getPixels()->getMemory(),
920 				                          serverMap.getSizeX(), serverMap.getSizeY(),
921 				                          serverMap.getPixelFormat()->BitsPerPixel,
922 				                          serverMap.getPixelFormat()->BytesPerPixel *
923 				                          serverMap.getSizeX(),
924 				                          serverMap.getPixelFormat()->Rmask,
925 				                          serverMap.getPixelFormat()->Gmask,
926 				                          serverMap.getPixelFormat()->Bmask,
927 				                          serverMap.getPixelFormat()->Amask );
928 			ASSERT( serverSurface, "ClientMap::ClientMap: could not create surface\n" );
929 			LOG( 4 ) INFO( "ClientMap::ClientMap: Converting server surface to client format\n" );
930 			m_pixels = SDL_ConvertSurface( serverSurface, format, flags );
931 			ASSERT( m_pixels, "ClientMap::ClientMap: could not convert surface\n" );
932 			LOG( 4 ) INFO( "ClientMap::ClientMap: Freeing server surface\n" );
933 			SDL_FreeSurface( serverSurface );
934 		}
935 
936 /******************************************************************************/
937 
938 		//! Copy constructor
ClientMap(const ClientMap & m)939 		ClientMap( const ClientMap& m ) : Map( m ) {
940 			m_pixels = SDL_CreateRGBSurface
941 				( m.m_pixels->flags, m.m_pixels->w, m.m_pixels->h,
942 				  m.m_pixels->format->BitsPerPixel,
943 				  m.m_pixels->format->Rmask,
944 				  m.m_pixels->format->Gmask,
945 				  m.m_pixels->format->Bmask,
946 				  m.m_pixels->format->Amask );
947 			ASSERT( m_pixels, "ClientMap::ClientMap: could not create surface\n" );
948 			SDL_BlitSurface( m.m_pixels, 0, m_pixels, 0 );
949 		}
950 
951 /******************************************************************************/
952 
953 		// Destructor
~ClientMap()954 		virtual ~ClientMap() {
955 			LOG( 3 ) INFO( "ClientMap::~ClientMap: destructor called\n" );
956 			SDL_FreeSurface( m_pixels );
957 		}
958 
getPixels()959 		SDL_Surface* getPixels() {
960 			return m_pixels;
961 		}
962 
963 /******************************************************************************/
964 
getColorAt(const Uint32 x,const Uint32 y,Uint8 & red,Uint8 & green,Uint8 & blue)965 		virtual void getColorAt( const Uint32 x, const Uint32 y,
966 		                         Uint8& red, Uint8& green, Uint8& blue ) {
967 			Uint32 pixel = Graphics::getPixel( m_pixels, x, y );
968 			SDL_GetRGB( pixel, m_pixels->format, &red, &green, &blue );
969 		}
970 
971 /******************************************************************************/
972 
setColorAt(const Uint32 x,const Uint32 y,const Uint8 r,const Uint8 g,const Uint8 b)973 		virtual void setColorAt( const Uint32 x, const Uint32 y,
974 		                         const Uint8 r, const Uint8 g, const Uint8 b ) {
975 			const Uint32 pixel = SDL_MapRGB( m_pixels->format, r, g, b );
976 			Graphics::setPixel( m_pixels, x, y, pixel );
977 		}
978 
979 /******************************************************************************/
980 
setColorAt(const Uint32 x,const Uint32 y,const Uint32 color)981 		virtual void setColorAt( const Uint32 x, const Uint32 y, const Uint32 color ) {
982 			const Uint8 r = static_cast<Uint8>( color >> 16 ),
983 			            g = static_cast<Uint8>( color >>  8 ),
984 			            b = static_cast<Uint8>( color       );
985 			const Uint32 pixel = SDL_MapRGB( m_pixels->format, r, g, b );
986 			Graphics::setPixel( m_pixels, x, y, pixel );
987 		}
988 
989 /******************************************************************************/
990 
991 		//! \name (de)serialization
992 		//@{
993 		//! return the size of the serialized map
getSerializeBufferSize() const994 		virtual Uint32 getSerializeBufferSize() const {
995 			return 2 * Serialize<Uint32>::sizeOf() +
996 				m_pixels->h * m_pixels->pitch +
997 				m_flags.getSerializeBufferSize();
998 		}
999 
1000 		/*! \brief serialize the map into the buffer starting at
1001 		 *  bufferPointer. After this method bufferPointer points to
1002 		 *  next char after the last char of the serialization of the
1003 		 *  map.
1004 		 */
1005 		virtual void serialize( Uint8*& bufferPointer ) const;
1006 
1007 		/*! \brief replace the existing map by deserializing the contents
1008 		 *  of the buffer pointed to by bufferPointer. After this method
1009 		 *  bufferPointer points to next char after the last char of the
1010 		 *  serialization of the map.
1011 		 */
1012 		virtual void deserialize( Uint8*& bufferPointer );
1013 		//@}
1014 };
1015 
1016 #endif // _MAP_HPP_
1017