1 /***************************************************************************
2                 map.cpp  -  Manages and renders the level map
3                              -------------------
4     begin                : Sat May 3 2003
5     copyright            : (C) 2003 by Gabor Torok
6     email                : cctorok@yahoo.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "../common/constants.h"
19 #include "map.h"
20 #include "effect.h"
21 #include "frustum.h"
22 #include "location.h"
23 #include "shape.h"
24 #include "shapes.h"
25 #include "glshape.h"
26 #include "virtualshape.h"
27 #include "md2shape.h"
28 #include "renderedprojectile.h"
29 #include "renderedcreature.h"
30 #include "rendereditem.h"
31 #include "mapadapter.h"
32 #include "../io/zipfile.h"
33 #include "../rpg/spell.h"
34 #include "maprenderhelper.h"
35 #include "../debug.h"
36 #include "projectilerenderer.h"
37 #include "../quickhull.h"
38 
39 using namespace std;
40 
41 // ###### MS Visual C++ specific ######
42 #if defined(_MSC_VER) && defined(_DEBUG)
43 # define new DEBUG_NEW
44 # undef THIS_FILE
45   static char THIS_FILE[] = __FILE__;
46 #endif
47 
48 
49 // set to 1 to enable bounding box and ground grid drawing
50 #define DEBUG_MOUSE_POS 0
51 
52 #define USE_LIGHTING 1
53 
54 #define MOUSE_ROT_DELTA 2
55 
56 #define ZOOM_DELTA 1.2f
57 
58 #define KEEP_MAP_SIZE 0
59 
60 #define MVW 100
61 #define MVD 100
62 
63 #define WATER_AMP 0.25f
64 #define WATER_ANIM_SPEED 10.0f
65 #define WATER_HEIGHT 1.2f
66 #define WATER_STEP 0.07f
67 
68 //#define DEBUG_RENDER 1
69 
70 // this is the clockwise order of movements
71 int Map::dir_index[] = { Constants::MOVE_UP, Constants::MOVE_LEFT, Constants::MOVE_DOWN, Constants::MOVE_RIGHT };
72 
73 bool Map::debugMd2Shapes = false;
74 
75 bool mouseMove = false;
76 int mouseMoveX = 0;
77 int mouseMoveY = 0;
78 float moveAngle = 0;
79 float moveDelta = 0;
80 
81 
82 // how long to wait between quakes
83 #define QUAKE_DELAY 45000
84 
85 // how long each quake lasts
86 #define QUAKE_DURATION 4000
87 
88 // how fast is a quake?
89 #define QUAKE_TICK_FREQ 50
90 
91 float quakeOffsX = 0;
92 float quakeOffsY = 0;
93 Uint32 quakeStartTime = 0;
94 Uint32 nextQuakeStartTime = 0;
95 Uint32 lastQuakeTick = 0;
96 bool quakeOnce = false;
97 
98 const float Map::shadowTransformMatrix[16] = {
99 	1, 0, 0, 0,
100 	0, 1, 0, 0,
101 // 0.5f, -0.5f, 0, 0,
102 	0.75f, -0.25f, 0, 0,
103 	0, 0, 0, 1
104 };
105 
106 MapMemoryManager Map::mapMemoryManager;
107 
Map(MapAdapter * adapter,Preferences * preferences,Shapes * shapes)108 Map::Map( MapAdapter *adapter, Preferences *preferences, Shapes *shapes ) {
109 	roofAlphaUpdate = 0;
110 	roofAlpha = 1;
111 
112 	hasWater = false;
113 
114 	startx = starty = 128;
115 	cursorMapX = cursorMapY = cursorMapZ = MAP_WIDTH + 1;
116 	cursorFlatMapX = cursorFlatMapY = MAP_WIDTH + 1;
117 	cursorChunkX = cursorChunkY = ( MAP_CHUNKS_X ) + 1;
118 	cursorWidth = 1;
119 	cursorDepth = 1;
120 	cursorHeight = MAP_WALL_HEIGHT;
121 	cursorZ = 0;
122 	lastOutlinedX = lastOutlinedY = lastOutlinedZ = MAP_WIDTH;
123 
124 	mouseMoveScreen = true;
125 	mouseZoom = mouseRot = false;
126 	mouseRotDir = 1;
127 	move = 0;
128 
129 	mapViewWidth = MVW;
130 	mapViewDepth = MVD;
131 
132 	chunkCount = 0;
133 	frustum = new CFrustum();
134 	useFrustum = true;
135 
136 	this->adapter = adapter;
137 	this->preferences = preferences;
138 	this->shapes = shapes;
139 
140 	// only use 1 (disabled) or 0 (enabled)
141 	LIGHTMAP_ENABLED = 0;
142 	zoom = 1.0f;
143 	zoomIn = zoomOut = false;
144 	x = y = 0;
145 	mapx = mapy = 0.0f;
146 	floorOnly = false;
147 	useShadow = false;
148 	//alwaysCenter = true;
149 
150 	debugX = debugY = debugZ = -1;
151 
152 	mapChanged = true;
153 	resortShapes = true;
154 	groundVisible = true;
155 
156 	floorTexWidth = floorTexHeight = 0;
157 
158 	mapCenterCreature = NULL;
159 
160 	this->xrot = 0.0f;
161 	this->yrot = 30.0f;
162 	this->zrot = 45.0f;
163 	this->xRotating = this->yRotating = this->zRotating = 0.0f;
164 
165 	setViewArea( 0, 0,
166 	             adapter->getScreenWidth(),
167 	             adapter->getScreenHeight() );
168 
169 	float adjust = static_cast<float>( viewWidth ) / 800.0f;
170 	this->xpos = static_cast<float>( viewWidth ) / 2.0f / adjust;
171 	this->ypos = static_cast<float>( viewHeight ) / 2.0f / adjust;
172 	this->zpos = 0.0f;
173 
174 	this->debugGridFlag = false;
175 	this->drawGridFlag = false;
176 
177 
178 	// initialize shape graph of "in view shapes"
179 	for ( int x = 0; x < MAP_WIDTH; x++ ) {
180 		for ( int y = 0; y < MAP_DEPTH; y++ ) {
181 			floorPositions[x][y] = NULL;
182 			itemPos[x][y] = NULL;
183 			for ( int z = 0; z < MAP_VIEW_HEIGHT; z++ ) {
184 				pos[x][y][z] = NULL;
185 				effect[x][y][z] = NULL;
186 			}
187 		}
188 	}
189 	// Init the pos cache
190 	for ( int x = 0; x < MAX_POS_CACHE; x++ ) {
191 		posCache[x] = NULL;
192 	}
193 	nbPosCache = -1;
194 
195 	for ( int x = 0; x < MAP_WIDTH; x++ ) {
196 		for ( int y = 0; y < MAP_DEPTH; y++ ) {
197 			ground[x][y] = 0;
198 		}
199 	}
200 	heightMapEnabled = false;
201 	for ( int i = 0; i < 4; i++ )
202 		debugHeightPosXX[i] = debugHeightPosYY[i] = 0;
203 
204 
205 	// initialize the lightmap
206 	for ( int x = 0; x < MAP_CHUNKS_X; x++ ) {
207 		for ( int y = 0; y < MAP_CHUNKS_Y; y++ ) {
208 			lightMap[x][y] = ( LIGHTMAP_ENABLED ? 0 : 1 );
209 		}
210 	}
211 
212 	lightMapChanged = true;
213 	colorAlreadySet = false;
214 	selectedDropTarget = NULL;
215 
216 	helper = NULL;
217 
218 	laterCount = stencilCount = otherCount = damageCount = roofCount = 0;
219 
220 	quakesEnabled = false;
221 
222 	refreshGroundPos = true;
223 
224 	outdoorShadow = adapter->getNamedTexture( "outdoors_shadow" );
225 	outdoorShadowTree = adapter->getNamedTexture( "outdoors_shadow_tree" );
226 	//waterTexture = adapter->getNamedTexture( "water" );
227 
228 	hackBlockingPos = new Location();
229 	hackBlockingPos->creature = NULL;
230 	hackBlockingPos->heightPos = 15;
231 	hackBlockingPos->item = NULL;
232 	hackBlockingPos->outlineColor = NULL;
233 	hackBlockingPos->shape = NULL;
234 	hackBlockingPos->x = hackBlockingPos->y = hackBlockingPos->z = 0;
235 
236 	selectedTrapIndex = -1;
237 
238 	isCurrentlyUnderRoof = isRoofShowing = true;
239 	weather = WEATHER_CLEAR;
240 
241 	gridEnabled = true;
242 
243 	adapter->writeLogMessage( Constants::getMessage( Constants::WELCOME ), Constants::MSGTYPE_SYSTEM );
244 	adapter->writeLogMessage( "----------------------------------", Constants::MSGTYPE_SYSTEM );
245 }
246 
~Map()247 Map::~Map() {
248 	removeAllEffects();
249 	// can't just call reset() since it consults with external objects
250 	set<Location*> deleted;
251 	for ( int xp = 0; xp < MAP_WIDTH; xp++ ) {
252 		for ( int yp = 0; yp < MAP_DEPTH; yp++ ) {
253 			if ( floorPositions[xp][yp] ) {
254 				Uint32 key = createPairKey( xp, yp );
255 				if ( water.find( key ) != water.end() ) {
256 					delete water[ key ];
257 					water.erase( key );
258 				}
259 			}
260 			if ( itemPos[xp][yp] ) {
261 				Location *p = itemPos[xp][yp];
262 				if ( deleted.find( p ) == deleted.end() ) deleted.insert( p );
263 				itemPos[xp][yp] = NULL;
264 			}
265 			for ( int zp = 0; zp < MAP_VIEW_HEIGHT; zp++ ) {
266 				if ( pos[xp][yp][zp] ) {
267 					Location *p = pos[xp][yp][zp];
268 					if ( deleted.find( p ) == deleted.end() ) deleted.insert( p );
269 					pos[xp][yp][zp] = NULL;
270 				}
271 			}
272 		}
273 	}
274 	for ( set<Location*>::iterator i = deleted.begin(); i != deleted.end(); ++i ) {
275 		Location *p = *i;
276 		mapMemoryManager.deleteLocation( p );
277 	}
278 	water.clear();
279 
280 	delete hackBlockingPos;
281 	delete frustum;
282 	// did not create it: delete helper;
283 
284 	for ( size_t i = 0; i < trapList.size(); ++i ) {
285 		for ( size_t j = 0; j < trapList[i].hull.size(); ++j ) {
286 			delete trapList[i].hull[j];
287 		}
288 	}
289 }
290 
291 /// After work is done, reset everything.
292 
reset()293 void Map::reset() {
294 
295 	creatureMap.clear();
296 	creatureEffectMap.clear();
297 
298 	roofAlphaUpdate = 0;
299 	roofAlpha = 1;
300 
301 	//cerr << "reset 1" << endl;
302 	edited = false;
303 	strcpy( this->name, "" );
304 	hasWater = false;
305 
306 	//cerr << "reset 2" << endl;
307 	// remove locking info
308 	clearLocked();
309 
310 	//cerr << "reset 3" << endl;
311 	// remove area effects
312 	removeAllEffects();
313 
314 	//cerr << "reset 4" << endl;
315 	// clear map
316 	set<Location*> deleted;
317 	for ( int xp = 0; xp < MAP_WIDTH; xp++ ) {
318 		for ( int yp = 0; yp < MAP_DEPTH; yp++ ) {
319 			if ( floorPositions[xp][yp] ) {
320 				Uint32 key = createPairKey( xp, yp );
321 				if ( water.find( key ) != water.end() ) {
322 					delete water[ key ];
323 					water.erase( key );
324 				}
325 			}
326 			if ( itemPos[xp][yp] ) {
327 				Location *p = itemPos[xp][yp];
328 				if ( deleted.find( p ) == deleted.end() ) deleted.insert( p );
329 				itemPos[xp][yp] = NULL;
330 			}
331 			for ( int zp = 0; zp < MAP_VIEW_HEIGHT; zp++ ) {
332 				if ( pos[xp][yp][zp] ) {
333 					Location *p = pos[xp][yp][zp];
334 					if ( deleted.find( p ) == deleted.end() ) deleted.insert( p );
335 					pos[xp][yp][zp] = NULL;
336 				}
337 			}
338 		}
339 	}
340 	for ( set<Location*>::iterator i = deleted.begin(); i != deleted.end(); ++i ) {
341 		Location *p = *i;
342 		mapMemoryManager.deleteLocation( p );
343 	}
344 	water.clear();
345 
346 	//cerr << "reset 5" << endl;
347 	zoom = 1.0f;
348 	zoomIn = zoomOut = false;
349 	x = y = 0;
350 	mapx = mapy = 0.0f;
351 	floorOnly = false;
352 	useShadow = false;
353 	//alwaysCenter = true;
354 	debugX = debugY = debugZ = -1;
355 	mapChanged = true;
356 	groundVisible = true;
357 	resortShapes = true;
358 	lastOutlinedX = lastOutlinedY = lastOutlinedZ = MAP_WIDTH;
359 	floorTexWidth = floorTexHeight = 0;
360 	floorTex.clear();
361 	mapCenterCreature = NULL;
362 	secretDoors.clear();
363 
364 	this->xrot = 0.0f;
365 	this->yrot = 30.0f;
366 	this->zrot = 45.0f;
367 	this->xRotating = this->yRotating = this->zRotating = 0.0f;
368 
369 	setViewArea( 0, 0,
370 	             adapter->getScreenWidth(),
371 	             adapter->getScreenHeight() );
372 
373 	float adjust = static_cast<float>( viewWidth ) / 800.0f;
374 	this->xpos = static_cast<float>( viewWidth ) / 2.0f / adjust;
375 	this->ypos = static_cast<float>( viewHeight ) / 2.0f / adjust;
376 	this->zpos = 0.0f;
377 
378 	this->debugGridFlag = false;
379 	this->drawGridFlag = false;
380 
381 	//cerr << "reset 6" << endl;
382 
383 	for ( int x = 0; x < MAP_CHUNKS_X; x++ ) {
384 		for ( int y = 0; y < MAP_CHUNKS_Y; y++ ) {
385 			rugPos[x][y].texture.clear();
386 		}
387 	}
388 
389 	// initialize shape graph of "in view shapes"
390 	for ( int x = 0; x < MAP_WIDTH; x++ ) {
391 		for ( int y = 0; y < MAP_DEPTH; y++ ) {
392 			floorPositions[x][y] = NULL;
393 			itemPos[x][y] = NULL;
394 			for ( int z = 0; z < MAP_VIEW_HEIGHT; z++ ) {
395 				pos[x][y][z] = NULL;
396 				effect[x][y][z] = NULL;
397 			}
398 		}
399 	}
400 	// Init the pos cache
401 	//for(int x = 0; x < MAX_POS_CACHE; x++) {
402 	//  posCache[x] = NULL;
403 	//}
404 	//nbPosCache = -1;
405 
406 	//cerr << "reset 7" << endl;
407 	// initialize the lightmap
408 	for ( int x = 0; x < MAP_CHUNKS_X; x++ ) {
409 		for ( int y = 0; y < MAP_CHUNKS_Y; y++ ) {
410 			lightMap[x][y] = ( LIGHTMAP_ENABLED ? 0 : 1 );
411 		}
412 	}
413 	lightMapChanged = true;
414 	colorAlreadySet = false;
415 	selectedDropTarget = NULL;
416 
417 	if ( helper ) helper->reset();
418 
419 	laterCount = stencilCount = otherCount = damageCount = roofCount = 0;
420 
421 	quakesEnabled = false;
422 	for ( int x = 0; x < MAP_WIDTH; x++ ) {
423 		for ( int y = 0; y < MAP_DEPTH; y++ ) {
424 			ground[x][y] = 0;
425 			groundTex[x][y].clear();
426 			for ( int z = 0; z < MAX_OUTDOOR_LAYER; z++ ) {
427 				int tx = x / OUTDOORS_STEP;
428 				int ty = y / OUTDOORS_STEP;
429 				outdoorTex[tx][ty][z].texture.clear();
430 			}
431 		}
432 	}
433 	heightMapEnabled = false;
434 	for ( int i = 0; i < 4; i++ )
435 		debugHeightPosXX[i] = debugHeightPosYY[i] = 0;
436 
437 	refreshGroundPos = true;
438 
439 	isCurrentlyUnderRoof = isRoofShowing = true;
440 
441 	clearTraps();
442 
443 	gates.clear();
444 	teleporters.clear();
445 
446 	gridEnabled = true;
447 }
448 
449 /// Sets the size and position of the viewport.
450 
setViewArea(int x,int y,int w,int h)451 void Map::setViewArea( int x, int y, int w, int h ) {
452 	//viewX = x;
453 //  viewY = y;
454 	viewX = 0;
455 	viewY = 0;
456 	viewWidth = w;
457 	viewHeight = h;
458 
459 	float adjust = static_cast<float>( viewWidth ) / 800.0f;
460 	if ( preferences->getKeepMapSize() ) {
461 		zoom = static_cast<float>( adapter->getScreenWidth() ) / static_cast<float>( w );
462 	}
463 	xpos = static_cast<int>( static_cast<float>( viewWidth ) / zoom / 2.0f / adjust );
464 	ypos = static_cast<int>( static_cast<float>( viewHeight ) / zoom / 2.0f / adjust );
465 
466 	refresh();
467 }
468 
469 /// Centers view on map position x,y.
470 
center(Sint16 x,Sint16 y,bool force)471 void Map::center( Sint16 x, Sint16 y, bool force ) {
472 	Sint16 nx = x - mapViewWidth / 2;
473 	Sint16 ny = y - mapViewDepth / 2;
474 	if ( preferences->getAlwaysCenterMap() || force ) {
475 		// relocate
476 		this->x = nx;
477 		this->y = ny;
478 		this->mapx = nx;
479 		this->mapy = ny;
480 	}
481 }
482 
483 /// Removes effects that are done playing.
484 
removeCurrentEffects()485 void Map::removeCurrentEffects() {
486 	for ( map<Uint32, EffectLocation*>::iterator i = currentEffectsMap.begin();
487 	        i != currentEffectsMap.end(); ) {
488 		Uint32 key = i->first;
489 		EffectLocation *pos = i->second;
490 		if ( !pos->isEffectOn() ) {
491 			int x, y, z;
492 			decodeTripletKey( key, &x, &y, &z );
493 			currentEffectsMap.erase( i++ );
494 			removeEffect( x, y, z );
495 			resortShapes = mapChanged = true;
496 		} else {
497 			++i;
498 		}
499 	}
500 
501 
502 
503 	/*
504 	int chunkOffsetX = 0;
505 	int chunkStartX = (getX() - MAP_OFFSET) / MAP_UNIT;
506 	int mod = (getX() - MAP_OFFSET) % MAP_UNIT;
507 	if(mod) {
508 	  chunkOffsetX = -mod;
509 	}
510 	int chunkEndX = mapViewWidth / MAP_UNIT + chunkStartX;
511 
512 	int chunkOffsetY = 0;
513 	int chunkStartY = (getY() - MAP_OFFSET) / MAP_UNIT;
514 	mod = (getY() - MAP_OFFSET) % MAP_UNIT;
515 	if(mod) {
516 	  chunkOffsetY = -mod;
517 	}
518 	int chunkEndY = mapViewDepth / MAP_UNIT + chunkStartY;
519 
520 	int posX, posY;
521 	for(int chunkX = chunkStartX; chunkX < chunkEndX; chunkX++) {
522 	  if(chunkX < 0 || chunkX > MAP_WIDTH / MAP_UNIT) continue;
523 	  for(int chunkY = chunkStartY; chunkY < chunkEndY; chunkY++) {
524 	    if(chunkY < 0 || chunkY > MAP_DEPTH / MAP_UNIT) continue;
525 	    for(int yp = 0; yp < MAP_UNIT; yp++) {
526 	      for(int xp = 0; xp < MAP_UNIT; xp++) {
527 
528 	//           In scourge, shape coordinates are given by their
529 	//           left-bottom corner. So the +1 for posY moves the
530 	//           position 1 unit down the Y axis, which is the
531 	//           unit square's bottom left corner.
532 	        posX = chunkX * MAP_UNIT + xp + MAP_OFFSET;
533 	        posY = chunkY * MAP_UNIT + yp + MAP_OFFSET + 1;
534 
535 	        for(int zp = 0; zp < MAP_VIEW_HEIGHT; zp++) {
536 	          if( effect[posX][posY][zp] && !effect[posX][posY][zp]->isEffectOn() ) {
537 	            removeEffect( posX, posY, zp );
538 	            mapChanged = true;
539 	          }
540 	        }
541 	      }
542 	    }
543 	  }
544 	}
545 	*/
546 }
547 
checkLightMap(int chunkX,int chunkY)548 bool Map::checkLightMap( int chunkX, int chunkY ) {
549 	return !helper->isLightMapEnabled() || lightMap[chunkX][chunkY];
550 }
551 
552 /// Sets up the shapes array for a specified layer.
553 
554 /// The position and dimensions of the map chunk to draw are to be provided.
555 /// If forGround is true, it sets up the ground layer.
556 /// If forWater is true, it sets up the indoors water layer.
557 /// Otherwise it populates the shapes array.
558 
setupShapes(bool forGround,bool forWater,int * csx,int * cex,int * csy,int * cey)559 void Map::setupShapes( bool forGround, bool forWater, int *csx, int *cex, int *csy, int *cey ) {
560 	if ( !forGround && !forWater ) {
561 		laterCount = stencilCount = otherCount = damageCount = roofCount = 0;
562 		creatureMap.clear();
563 		creatureEffectMap.clear();
564 		trapSet.clear();
565 		mapChanged = false;
566 	}
567 
568 	chunkCount = 0;
569 	int chunkOffsetX, chunkOffsetY;
570 	int chunkStartX, chunkStartY;
571 	int chunkEndX, chunkEndY;
572 	calculateChunkInfo( &chunkOffsetX, &chunkOffsetY, &chunkStartX, &chunkStartY, &chunkEndX, &chunkEndY );
573 	if ( csx ) {
574 		*csx = chunkStartX;
575 		*cex = chunkEndX;
576 		*csy = chunkStartY;
577 		*cey = chunkEndY;
578 	}
579 
580 	set<Location*> seenPos;
581 	Shape *shape;
582 	int posX, posY;
583 	float xpos2, ypos2, zpos2;
584 	for ( int chunkX = chunkStartX; chunkX < chunkEndX; chunkX++ ) {
585 		for ( int chunkY = chunkStartY; chunkY < chunkEndY; chunkY++ ) {
586 
587 			// remember the chunk's starting pos.
588 			float chunkPosX = static_cast<float>( ( chunkX - chunkStartX ) * MAP_UNIT + chunkOffsetX ) * MUL;
589 			float chunkPosY = static_cast<float>( ( chunkY - chunkStartY ) * MAP_UNIT + chunkOffsetY ) * MUL;
590 
591 			// frustum testing (including extra for roof pieces)
592 			if ( useFrustum && !frustum->CubeInFrustum( chunkPosX, chunkPosY, 0.0f, static_cast<float>( MAP_UNIT ) * MUL ) ) {
593 				continue;
594 			}
595 
596 
597 			// FIXME: works but slow. Use 1 polygon instead (like floor)
598 			// special cave edge code
599 			if ( !( forGround || forWater ) && floorTexWidth > 0 && !isHeightMapEnabled() && ( chunkX < 0 || chunkY < 0 ) ) {
600 				for ( int yp = CAVE_CHUNK_SIZE; yp < MAP_UNIT + CAVE_CHUNK_SIZE; yp += CAVE_CHUNK_SIZE ) {
601 					for ( int xp = 0; xp < MAP_UNIT; xp += CAVE_CHUNK_SIZE ) {
602 						xpos2 = static_cast<float>( ( chunkX - chunkStartX ) * MAP_UNIT + xp + chunkOffsetX ) * MUL;
603 						ypos2 = static_cast<float>( ( chunkY - chunkStartY ) * MAP_UNIT - CAVE_CHUNK_SIZE + yp + chunkOffsetY ) * MUL;
604 						setupPosition( 0, CAVE_CHUNK_SIZE, 0, xpos2, ypos2, 0, pos[0][CAVE_CHUNK_SIZE][0]->shape, NULL, NULL, NULL );
605 					}
606 				}
607 			}
608 
609 			if ( chunkX < 0 || chunkX > MAP_CHUNKS_X || chunkY < 0 || chunkY > MAP_CHUNKS_Y )
610 				continue;
611 
612 			// store this chunk
613 			chunks[chunkCount].x = chunkPosX;
614 			chunks[chunkCount].y = chunkPosY;
615 			chunks[chunkCount].cx = chunkX;
616 			chunks[chunkCount].cy = chunkY;
617 			chunkCount++;
618 
619 			// FIXME: this works except it draws other doors on the same
620 			// chunk that should be hidden. To really fix it, we need to
621 			// keep track of which side of the chunk to draw.
622 			Uint16 drawSide = 0;
623 			if ( !checkLightMap( chunkX, chunkY ) ) {
624 				if ( forGround || forWater ) continue;
625 				else {
626 					// look to the left
627 					if ( chunkX >= 1 && lightMap[chunkX - 1][chunkY] ) drawSide |= Constants::MOVE_LEFT;
628 					// look to the right
629 					if ( chunkX + 1 < MAP_CHUNKS_X && lightMap[chunkX + 1][chunkY] ) drawSide |= Constants::MOVE_RIGHT;
630 					// look above
631 					if ( chunkY - 1 >= 0 && lightMap[chunkX][chunkY - 1] ) drawSide |= Constants::MOVE_UP;
632 					// look below
633 					if ( chunkY + 1 < MAP_CHUNKS_Y && lightMap[chunkX][chunkY + 1] ) drawSide |= Constants::MOVE_DOWN;
634 					// if not, skip this chunk
635 					if ( !drawSide ) continue;
636 				}
637 			}
638 
639 			// draw rugs
640 			if ( ( forGround || forWater ) && hasRugAtPosition( chunkX, chunkY ) ) {
641 				xpos2 = static_cast<float>( ( chunkX - chunkStartX ) * MAP_UNIT + chunkOffsetX ) * MUL;
642 				ypos2 = static_cast<float>( ( chunkY - chunkStartY ) * MAP_UNIT + chunkOffsetY ) * MUL;
643 				drawRug( &rugPos[ chunkX ][ chunkY ], xpos2, ypos2, chunkX, chunkY );
644 			}
645 
646 			for ( int yp = 0; yp < MAP_UNIT; yp++ ) {
647 				for ( int xp = 0; xp < MAP_UNIT; xp++ ) {
648 					/**
649 					 In scourge, shape coordinates are given by their
650 					 left-bottom corner. So the +1 for posY moves the
651 					 position 1 unit down the Y axis, which is the
652 					 unit square's bottom left corner.
653 					 */
654 					posX = chunkX * MAP_UNIT + xp + MAP_OFFSET;
655 					posY = chunkY * MAP_UNIT + yp + MAP_OFFSET + 1;
656 
657 					// show traps
658 					int trapIndex = getTrapAtLoc( posX, posY );
659 					if ( trapIndex != -1 && checkLightMap( chunkX, chunkY ) )
660 						trapSet.insert( ( Uint8 )trapIndex );
661 
662 					if ( forGround || forWater ) {
663 						shape = floorPositions[posX][posY];
664 						if ( shape ) {
665 							//cerr << "pos=" << posX << "," << posY << endl;
666 							//cerr << "\tfloor shape=" << shape->getName() << endl;
667 							xpos2 = static_cast<float>( ( chunkX - chunkStartX ) * MAP_UNIT + xp + chunkOffsetX ) * MUL;
668 							ypos2 = static_cast<float>( ( chunkY - chunkStartY ) * MAP_UNIT - shape->getDepth() + yp + chunkOffsetY ) * MUL;
669 
670 							if ( forWater ) {
671 								drawWaterPosition( posX, posY, xpos2, ypos2, shape );
672 							} else {
673 								drawGroundPosition( posX, posY, xpos2, ypos2, shape );
674 							}
675 						}
676 					} else {
677 
678 						if ( checkLightMap( chunkX, chunkY ) && itemPos[posX][posY] &&
679 						        itemPos[posX][posY]->x == posX && itemPos[posX][posY]->y == posY ) {
680 
681 							shape = itemPos[posX][posY]->shape;
682 
683 							xpos2 = static_cast<float>( ( chunkX - chunkStartX ) * MAP_UNIT + xp + chunkOffsetX ) * MUL;
684 							ypos2 = static_cast<float>( ( chunkY - chunkStartY ) * MAP_UNIT - shape->getDepth() + yp + chunkOffsetY ) * MUL;
685 
686 							setupPosition( posX, posY, 0, xpos2, ypos2, 0, shape, itemPos[posX][posY]->item, NULL, NULL, true );
687 						}
688 
689 						checkUnderRoof();
690 
691 						for ( int zp = 0; zp < MAP_VIEW_HEIGHT; zp++ ) {
692 							if ( checkLightMap( chunkX, chunkY ) && effect[posX][posY][zp] && !effect[posX][posY][zp]->isInDelay() ) {
693 								xpos2 = static_cast<float>( ( chunkX - chunkStartX ) * MAP_UNIT + xp + chunkOffsetX ) * MUL;
694 								ypos2 = static_cast<float>( ( chunkY - chunkStartY ) * MAP_UNIT - 1 + yp + chunkOffsetY ) * MUL;
695 								zpos2 = static_cast<float>( zp ) * MUL;
696 
697 								setupPosition( posX, posY, zp - effect[posX][posY][zp]->z, xpos2, ypos2, zpos2,
698 								               effect[posX][posY][zp]->effect->getShape(), NULL, NULL, effect[posX][posY][zp] );
699 							}
700 
701 							Location *location = pos[posX][posY][zp];
702 							if ( location && seenPos.find( location ) == seenPos.end() ) {
703 								seenPos.insert( location );
704 								//if( location && location->x == posX && location->y == posY && location->z == zp ) {
705 								setupLocation( location, drawSide, chunkStartX, chunkStartY, chunkOffsetX, chunkOffsetY );
706 							}
707 						}
708 					}
709 				}
710 			}
711 		}
712 	}
713 }
714 
715 /// Update the "under the roof" status. Return true if the "under the roof" status has changed.
716 
checkUnderRoof()717 bool Map::checkUnderRoof() {
718 	// skip roofs if inside
719 	bool oldRoof = isCurrentlyUnderRoof;
720 	isCurrentlyUnderRoof = true;
721 	if ( settings->isGridShowing() ) {
722 		isCurrentlyUnderRoof = !isRoofShowing;
723 	} else if ( adapter->getPlayer() ) {
724 		int px = toint( adapter->getPlayer()->getX() + adapter->getPlayer()->getShape()->getWidth() / 2 );
725 		int py = toint( adapter->getPlayer()->getY() - 1 - adapter->getPlayer()->getShape()->getDepth() / 2 );
726 		Location *roof = getLocation( px, py, MAP_WALL_HEIGHT );
727 		if ( !( roof && roof->shape && roof->shape->isRoof() ) ) {
728 			isCurrentlyUnderRoof = false;
729 		}
730 	}
731 	return ( isCurrentlyUnderRoof != oldRoof );
732 }
733 
734 /// Sets up a map location and checks whether it is visible/accessible.
735 
setupLocation(Location * location,Uint16 drawSide,int chunkStartX,int chunkStartY,int chunkOffsetX,int chunkOffsetY)736 void Map::setupLocation( Location *location, Uint16 drawSide, int chunkStartX, int chunkStartY, int chunkOffsetX, int chunkOffsetY ) {
737 
738 	int posX, posY, posZ;
739 	bool lightEdge;
740 	float xpos2, ypos2, zpos2;
741 	int chunkX, chunkY;
742 	calculateLocationInfo( location, chunkStartX, chunkStartY, chunkOffsetX, chunkOffsetY, drawSide,
743 	                       &posX, &posY, &posZ, &xpos2, &ypos2, &zpos2, &chunkX, &chunkY, &lightEdge );
744 
745 	// visible or on the edge or a roof piece
746 	if ( checkLightMap( chunkX, chunkY ) || lightEdge ) {
747 		if ( location->creature ) {
748 			location->heightPos = findMaxHeightPos( location->creature->getX(),
749 			                                        location->creature->getY(),
750 			                                        location->creature->getZ() );
751 		}
752 
753 		//if( !useFrustum || frustum->ShapeInFrustum( xpos2, ypos2, zpos2, shape ) ) {
754 		setupPosition( posX, posY, posZ, xpos2, ypos2, zpos2, location->shape, location->item, location->creature, NULL );
755 		//}
756 	}
757 
758 }
759 
760 /// Draws the rugs in Scourge HQ.
761 
drawRug(Rug * rug,float xpos2,float ypos2,int xchunk,int ychunk)762 void Map::drawRug( Rug *rug, float xpos2, float ypos2, int xchunk, int ychunk ) {
763 	glPushMatrix();
764 	glTranslatef( xpos2, ypos2, 0.255f * MUL );
765 	glRotatef( rug->angle, 0.0f, 0.0f, 1.0f );
766 	float f = MAP_UNIT * MUL;
767 	float offset = 2.5f * MUL;
768 
769 	float sx, sy, ex, ey;
770 	// starting section
771 	if ( rug->isHorizontal ) {
772 		sx = offset;
773 		sy = offset * 2;
774 		ex = f - offset;
775 		ey = f - offset * 2;
776 	} else {
777 		sy = offset;
778 		sx = offset * 2;
779 		ey = f - offset;
780 		ex = f - offset * 2;
781 	}
782 
783 	glDisable( GL_CULL_FACE );
784 	glEnable( GL_TEXTURE_2D );
785 	glColor4f( 1.0f, 1.0f, 1.0f, 0.9f );
786 	rug->texture.glBind();
787 	glBegin( GL_TRIANGLE_STRIP );
788 	if ( rug->isHorizontal ) {
789 		glTexCoord2f( 1.0f, 0.0f );
790 		glVertex2f( sx, sy );
791 		glTexCoord2f( 1.0f, 1.0f );
792 		glVertex2f( ex, sy );
793 		glTexCoord2f( 0.0f, 0.0f );
794 		glVertex2f( sx, ey );
795 		glTexCoord2f( 0.0f, 1.0f );
796 		glVertex2f( ex, ey );
797 	} else {
798 		glTexCoord2f( 0.0f, 0.0f );
799 		glVertex2f( sx, sy );
800 		glTexCoord2f( 1.0f, 0.0f );
801 		glVertex2f( ex, sy );
802 		glTexCoord2f( 0.0f, 1.0f );
803 		glVertex2f( sx, ey );
804 		glTexCoord2f( 1.0f, 1.0f );
805 		glVertex2f( ex, ey );
806 	}
807 	glEnd();
808 	glDisable( GL_TEXTURE_2D );
809 	glPopMatrix();
810 }
811 
812 /// Draws a shape sitting on the ground at the specified map coordinates.
813 
drawGroundPosition(int posX,int posY,float xpos2,float ypos2,Shape * shape)814 void Map::drawGroundPosition( int posX, int posY,
815                               float xpos2, float ypos2,
816                               Shape *shape ) {
817 	GLuint name;
818 	// encode this shape's map location in its name
819 	name = posX + ( MAP_WIDTH * posY );
820 	glTranslatef( xpos2, ypos2, 0.0f );
821 
822 	glPushName( name );
823 	glColor4f( 1.0f, 1.0f, 1.0f, 0.9f );
824 	if ( isHeightMapEnabled() ) {
825 		shape->drawHeightMap( ground, posX, posY );
826 	} else {
827 		shape->draw();
828 	}
829 	glPopName();
830 
831 	glTranslatef( -xpos2, -ypos2, 0.0f );
832 }
833 
834 /// Draws a shape placed on an indoor water tile.
835 
drawWaterPosition(int posX,int posY,float xpos2,float ypos2,Shape * shape)836 void Map::drawWaterPosition( int posX, int posY,
837                              float xpos2, float ypos2,
838                              Shape *shape ) {
839 	GLuint name;
840 	// encode this shape's map location in its name
841 	name = posX + ( MAP_WIDTH * posY );
842 	glTranslatef( xpos2, ypos2, 0.0f );
843 
844 	// draw water
845 	Uint32 key = createPairKey( posX, posY );
846 	if ( water.find( key ) != water.end() ) {
847 		glDisable( GL_CULL_FACE );
848 
849 		float sx = ( static_cast<float>( MAP_UNIT ) / static_cast<float>( WATER_TILE_X ) ) * MUL;
850 		float sy = ( static_cast<float>( MAP_UNIT ) / static_cast<float>( WATER_TILE_Y ) ) * MUL;
851 
852 		int xp = 0;
853 		int yp = 0;
854 		while ( true ) {
855 			int stx = xp;
856 			int sty = yp;
857 			glBegin( GL_TRIANGLE_STRIP );
858 			for ( int i = 0; i < 4; i++ ) {
859 				int wx, wy;
860 				if ( xp == WATER_TILE_X && yp == WATER_TILE_Y ) {
861 					wx = ( posX + MAP_UNIT ) * WATER_TILE_X;
862 					wy = ( posY + MAP_UNIT ) * WATER_TILE_Y;
863 				} else if ( xp == WATER_TILE_X ) {
864 					wx = ( posX + MAP_UNIT ) * WATER_TILE_X;
865 					wy = posY * WATER_TILE_Y + yp;
866 				} else if ( yp == WATER_TILE_Y ) {
867 					wx = posX * WATER_TILE_X + xp;
868 					wy = ( posY + MAP_UNIT ) * WATER_TILE_Y;
869 				} else {
870 					wx = posX * WATER_TILE_X + xp;
871 					wy = posY * WATER_TILE_Y + yp;
872 				}
873 
874 				int xx = wx % WATER_TILE_X;
875 				int yy = wy % WATER_TILE_Y;
876 				WaterTile *w = NULL;
877 				Uint32 key = createPairKey( wx / WATER_TILE_X, wy / WATER_TILE_Y );
878 				if ( water.find( key ) != water.end() ) {
879 					w = water[key];
880 
881 					Uint32 time = SDL_GetTicks();
882 					Uint32 elapsedTime = time - w->lastTime[xx][yy];
883 					if ( elapsedTime >= ( Uint32 )( 1000.0f / WATER_ANIM_SPEED ) ) {
884 
885 						w->z[xx][yy] += w->step[xx][yy];
886 						if ( w->z[xx][yy] > WATER_AMP ||
887 						        w->z[xx][yy] < -WATER_AMP ) w->step[xx][yy] *= -1.0f;
888 
889 						w->lastTime[xx][yy] = time;
890 					}
891 				}
892 
893 				float zz = ( w ? w->z[xx][yy] : 0.0f );
894 				float sz = ( WATER_HEIGHT + zz ) * MUL;
895 				glColor4f( 0.3f + ( zz / 30.0f ),
896 				           0.25f + ( zz / 10.0f ),
897 				           0.17f + ( zz / 15.0f ),
898 				           0.5f );
899 
900 
901 				glVertex3f( static_cast<float>( xp ) * sx, static_cast<float>( yp ) * sy, sz );
902 
903 				switch ( i ) {
904 				case 0: xp++; break;
905 				case 1: yp++; xp--; break;
906 				case 2: xp++; break;
907 				case 3: yp--; xp--; break;
908 				}
909 				if ( xp > WATER_TILE_X || yp > WATER_TILE_Y ) {
910 					break;
911 				}
912 			}
913 			glEnd();
914 			xp = stx + 1;
915 			yp = sty;
916 			if ( xp >= WATER_TILE_X ) {
917 				xp = 0;
918 				yp++;
919 				if ( yp >= WATER_TILE_Y ) break;
920 			}
921 		}
922 
923 
924 		//glDepthMask( GL_TRUE );
925 		//glDisable( GL_BLEND );
926 	}
927 
928 	glTranslatef( -xpos2, -ypos2, 0.0f );
929 }
930 
931 
setupPosition(int posX,int posY,int posZ,float xpos2,float ypos2,float zpos2,Shape * shape,RenderedItem * item,RenderedCreature * creature,EffectLocation * effect,bool itemPos)932 void Map::setupPosition( int posX, int posY, int posZ,
933                          float xpos2, float ypos2, float zpos2,
934                          Shape *shape, RenderedItem *item, RenderedCreature *creature,
935                          EffectLocation *effect,
936                          bool itemPos ) {
937 
938 	// This really doesn't make a difference unfortunately.
939 	//if(!isOnScreen(posX, posY, posZ)) return;
940 
941 	GLuint name;
942 	name = posX + ( MAP_WIDTH * ( posY ) ) + ( MAP_WIDTH * MAP_DEPTH * posZ );
943 
944 	// special effects
945 	if ( ( effect || ( creature && creature->isEffectOn() ) ) && !shape->isRoof() ) {
946 		damage[damageCount].xpos = xpos2;
947 		damage[damageCount].ypos = ypos2;
948 		damage[damageCount].zpos = zpos2;
949 		damage[damageCount].shape = shape;
950 		damage[damageCount].item = item;
951 		damage[damageCount].creature = creature;
952 		damage[damageCount].effect = effect;
953 		damage[damageCount].name = name;
954 		damage[damageCount].pos = ( itemPos ? getItemLocation( posX, posY ) : getLocation( posX, posY, posZ ) );
955 		damage[damageCount].inFront = false;
956 		damage[damageCount].x = posX;
957 		damage[damageCount].y = posY;
958 		if ( creature ) {
959 			creatureEffectMap[creature] = &( damage[damageCount] );
960 		}
961 		damageCount++;
962 
963 		// don't draw shape if it's an area effect
964 		if ( !creature ) return;
965 	}
966 
967 	if ( shape->isRoof() ) {
968 		roof[roofCount].xpos = xpos2;
969 		roof[roofCount].ypos = ypos2;
970 		roof[roofCount].zpos = zpos2;
971 		roof[roofCount].shape = shape;
972 		roof[roofCount].item = item;
973 		roof[roofCount].creature = creature;
974 		roof[roofCount].effect = NULL;
975 		roof[roofCount].name = name;
976 		roof[roofCount].pos = ( itemPos ? getItemLocation( posX, posY ) : getLocation( posX, posY, posZ ) );
977 		roof[roofCount].inFront = false;
978 		roof[roofCount].x = posX;
979 		roof[roofCount].y = posY;
980 		roofCount++;
981 	} else if ( shape->isStencil() ) {
982 		stencil[stencilCount].xpos = xpos2;
983 		stencil[stencilCount].ypos = ypos2;
984 		stencil[stencilCount].zpos = zpos2;
985 		stencil[stencilCount].shape = shape;
986 		stencil[stencilCount].item = item;
987 		stencil[stencilCount].creature = creature;
988 		stencil[stencilCount].effect = NULL;
989 		stencil[stencilCount].name = name;
990 		stencil[stencilCount].pos = ( itemPos ? getItemLocation( posX, posY ) : getLocation( posX, posY, posZ ) );
991 		stencil[stencilCount].inFront = false;
992 		stencil[stencilCount].x = posX;
993 		stencil[stencilCount].y = posY;
994 		stencilCount++;
995 	} else if ( !shape->isStencil() ) {
996 		if ( shape->drawFirst() ) {
997 			other[otherCount].xpos = xpos2;
998 			other[otherCount].ypos = ypos2;
999 			other[otherCount].zpos = zpos2;
1000 			other[otherCount].shape = shape;
1001 			other[otherCount].item = item;
1002 			other[otherCount].creature = creature;
1003 			other[otherCount].effect = NULL;
1004 			other[otherCount].name = name;
1005 			other[otherCount].pos = ( itemPos ? getItemLocation( posX, posY ) : getLocation( posX, posY, posZ ) );
1006 			other[otherCount].inFront = false;
1007 			other[otherCount].x = posX;
1008 			other[otherCount].y = posY;
1009 			if ( creature ) {
1010 				creatureMap[creature] = &( other[otherCount] );
1011 			}
1012 			otherCount++;
1013 		}
1014 		if ( shape->drawLater() ) {
1015 			later[laterCount].xpos = xpos2;
1016 			later[laterCount].ypos = ypos2;
1017 			later[laterCount].zpos = zpos2;
1018 			later[laterCount].shape = shape;
1019 			later[laterCount].item = item;
1020 			later[laterCount].creature = creature;
1021 			later[laterCount].effect = NULL;
1022 			later[laterCount].name = name;
1023 			later[laterCount].pos = ( itemPos ? getItemLocation( posX, posY ) : getLocation( posX, posY, posZ ) );
1024 			later[laterCount].inFront = false;
1025 			later[laterCount].x = posX;
1026 			later[laterCount].y = posY;
1027 			laterCount++;
1028 		}
1029 	}
1030 }
1031 
1032 /// Current camera zoom as a percentage value.
1033 
getZoomPercent()1034 float Map::getZoomPercent() {
1035 	return ( zoom - settings->getMinZoomIn() ) / ( settings->getMaxZoomOut() - settings->getMinZoomIn() );
1036 }
1037 
1038 /// Initialization before doing the actual drawing.
1039 
preDraw()1040 void Map::preDraw() {
1041 	if ( refreshGroundPos ) {
1042 		createGroundMap();
1043 		refreshGroundPos = false;
1044 	}
1045 
1046 	// must move map before calling getMapXY(Z)AtScreenXY!
1047 	if ( move ) moveMap( move );
1048 
1049 	if ( zoomIn ) {
1050 		if ( zoom <= settings->getMinZoomIn() ) {
1051 			zoomOut = false;
1052 		} else {
1053 			zoom /= ZOOM_DELTA;
1054 			mapChanged = true;
1055 		}
1056 	} else if ( zoomOut ) {
1057 		if ( zoom >= settings->getMaxZoomOut() ) {
1058 			zoomOut = false;
1059 		} else {
1060 			zoom *= ZOOM_DELTA;
1061 		}
1062 	}
1063 
1064 	float adjust = static_cast<float>( viewWidth ) / 800.0f;
1065 	xpos = static_cast<int>( static_cast<float>( viewWidth ) / zoom / 2.0f / adjust );
1066 	ypos = static_cast<int>( static_cast<float>( viewHeight ) / zoom / 2.0f / adjust );
1067 
1068 	float oldrot;
1069 
1070 	oldrot = yrot;
1071 	if ( yRotating != 0 ) yrot += yRotating;
1072 	if ( yrot >= settings->getMaxYRot() || yrot < 0 ) yrot = oldrot;
1073 
1074 	oldrot = zrot;
1075 	if ( zRotating != 0 ) {
1076 		resortShapes = true;
1077 		zrot += zRotating;
1078 	}
1079 	if ( zrot >= 360 ) zrot -= 360;
1080 	if ( zrot < 0 ) zrot = 360 + zrot;
1081 
1082 	if ( settings->isPlayerEnabled() && ( preferences->getAlwaysCenterMap() || mapCenterCreature ) ) {
1083 		mapChanged = resortShapes = true;
1084 	}
1085 	initMapView();
1086 
1087 	frustum->CalculateFrustum();
1088 	if ( lightMapChanged && helper->isLightMapEnabled() ) configureLightMap();
1089 	if ( !currentEffectsMap.empty() ) removeCurrentEffects();
1090 	// populate the shape arrays
1091 	if ( mapChanged ) {
1092 		//if( settings->isPlayerEnabled() && adapter->getPlayer() ) adapter->getPlayer()->setMapChanged();
1093 		int csx, cex, csy, cey;
1094 		setupShapes( false, false, &csx, &cex, &csy, &cey );
1095 #ifdef SHOW_FPS
1096 //     int shapeCount = laterCount + otherCount + damageCount + stencilCount;
1097 //     if( settings->isPlayerEnabled() ) {
1098 //       snprintf(mapDebugStr, DEBUG_SIZE, "c=%d,%d p=%d,%d chunks=(%s %d out of %d) x:%d-%d y:%d-%d shapes=%d trap=%d zoom=%.2f xrot=%.2f yrot=%.2f zrot=%.2f",
1099 //       cursorFlatMapX, cursorFlatMapY + 1,
1100 //       ( adapter->getPlayer() ? toint(adapter->getPlayer()->getX()) : -1 ),
1101 //       ( adapter->getPlayer() ? toint(adapter->getPlayer()->getY()) : -1 ),
1102 //       (useFrustum ? "*" : ""),
1103 //       chunkCount, ((cex - csx)*(cey - csy)),
1104 //       csx, cex, csy, cey, shapeCount, selectedTrapIndex,
1105 //       zoom, xrot, yrot, zrot );
1106 //       //            shapeCount, laterCount, otherCount, damageCount, stencilCount);
1107 //     } else {
1108 //       snprintf(mapDebugStr, DEBUG_SIZE, "E=%d chunks=(%s %d out of %d) x:%d-%d y:%d-%d shapes=%d",
1109 //               static_cast<int>(currentEffectsMap.size()), (useFrustum ? "*" : ""), chunkCount, ((cex - csx)*(cey - csy)),
1110 //               csx, cex, csy, cey, shapeCount);
1111 //     }
1112 //     adapter->setDebugStr(mapDebugStr);
1113 #endif
1114 	}
1115 }
1116 
1117 /// Draws traps, and cleans up after drawing the main 3D view.
1118 
postDraw()1119 void Map::postDraw() {
1120 	drawTraps();
1121 
1122 	glDisable( GL_SCISSOR_TEST );
1123 
1124 	// cancel mouse-based map movement (middle button)
1125 	if ( mouseRot ) {
1126 		setXRot( 0 );
1127 		setYRot( 0 );
1128 		setZRot( 0 );
1129 	}
1130 	if ( mouseZoom ) {
1131 		mouseZoom = false;
1132 		setZoomIn( false );
1133 		setZoomOut( false );
1134 	}
1135 }
1136 
1137 /// Draws the complete 3D view.
1138 
draw()1139 void Map::draw() {
1140 	if ( helper->isIndoors() ) {
1141 		drawIndoors();
1142 	} else {
1143 		drawOutdoors();
1144 	}
1145 
1146 	drawProjectiles();
1147 
1148 	if ( adapter->isMouseIsMovingOverMap() ) {
1149 		// find the map coordinates (must be done after drawing is complete)
1150 		Location *pos = NULL;
1151 		getMapXYZAtScreenXY( &cursorMapX, &cursorMapY, &cursorMapZ, &pos );
1152 		cursorFlatMapX = cursorMapX;
1153 		cursorFlatMapY = cursorMapY;
1154 		cursorChunkX = ( cursorFlatMapX - MAP_OFFSET ) / MAP_UNIT;
1155 		cursorChunkY = ( cursorFlatMapY - MAP_OFFSET ) / MAP_UNIT;
1156 		if ( pos ) {
1157 			cursorMapX = pos->x;
1158 			cursorMapY = pos->y;
1159 			cursorMapZ = pos->z;
1160 		}
1161 	}
1162 
1163 	if ( DEBUG_MOUSE_POS || ( settings->isGridShowing() && gridEnabled ) ) {
1164 		willDrawGrid();
1165 	}
1166 }
1167 
1168 /// Renders the 3D view for indoor levels.
1169 
drawIndoors()1170 void Map::drawIndoors() {
1171 	if ( preferences->getStencilbuf() && preferences->getStencilBufInitialized() ) {
1172 		// stencil and draw the floor
1173 		//glDisable(GL_DEPTH_TEST);
1174 		//glColorMask(0,0,0,0);
1175 		glEnable( GL_STENCIL_TEST );
1176 		glStencilOp( GL_REPLACE, GL_REPLACE, GL_REPLACE );
1177 		glStencilFunc( GL_ALWAYS, 1, 0xffffffff );
1178 
1179 		// cave floor and map editor bottom (so cursor shows)
1180 		if ( settings->isGridShowing() || floorTexWidth > 0 || isHeightMapEnabled() ) {
1181 			renderFloor();
1182 		} else {
1183 			setupShapes( true, false );
1184 		}
1185 
1186 		// shadows
1187 		if ( preferences->getShadows() >= Constants::OBJECT_SHADOWS &&
1188 		        helper->drawShadow() ) {
1189 			glStencilFunc( GL_EQUAL, 1, 0xffffffff );
1190 			glStencilOp( GL_KEEP, GL_KEEP, GL_INCR );
1191 			glDisable( GL_TEXTURE_2D );
1192 			glDepthMask( GL_FALSE );
1193 			glEnable( GL_BLEND );
1194 			useShadow = true;
1195 			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1196 			for ( int i = 0; i < otherCount; i++ ) {
1197 				doDrawShape( &other[i] );
1198 			}
1199 			if ( preferences->getShadows() == Constants::ALL_SHADOWS ) {
1200 				for ( int i = 0; i < stencilCount; i++ ) {
1201 					doDrawShape( &stencil[i] );
1202 				}
1203 			}
1204 			useShadow = false;
1205 			glDisable( GL_BLEND );
1206 			glEnable( GL_TEXTURE_2D );
1207 			glDepthMask( GL_TRUE );
1208 		}
1209 
1210 		//glEnable(GL_DEPTH_TEST);
1211 		glDisable( GL_STENCIL_TEST );
1212 	} else {
1213 		// draw the ground
1214 		setupShapes( true, false );
1215 	}
1216 
1217 	// draw lava flows
1218 	for ( int i = 0; i < otherCount; i++ ) {
1219 		if ( other[i].shape->isFlatCaveshape() ) {
1220 			doDrawShape( &other[i] );
1221 		}
1222 	}
1223 
1224 	// draw the creatures/objects/doors/etc.
1225 	DrawLater *playerDrawLater = NULL;
1226 	for ( int i = 0; i < otherCount; i++ ) {
1227 		if ( other[i].shape->isFlatCaveshape() ) continue;
1228 		if ( settings->isPlayerEnabled() ) {
1229 			if ( other[i].creature && other[i].creature == adapter->getPlayer() )
1230 				playerDrawLater = &( other[i] );
1231 		}
1232 		if ( selectedDropTarget && ( ( selectedDropTarget->creature && selectedDropTarget->creature == other[i].creature ) ||
1233 		                             ( selectedDropTarget->item && selectedDropTarget->item == other[i].item ) ) ) {
1234 			colorAlreadySet = true;
1235 			glColor4f( 0.0f, 1.0f, 1.0f, 1.0f );
1236 		}
1237 		doDrawShape( &other[i] );
1238 
1239 		// FIXME: if feeling masochistic, try using stencil buffer to remove shadow-on-shadow effect.
1240 		// draw simple shadow in outdoors
1241 		if ( !helper->drawShadow() ) {
1242 			if ( other[i].creature ) {
1243 				glColor4f( 0.04f, 0.0f, 0.07f, 0.4f );
1244 				drawGroundTex( outdoorShadow, other[i].creature->getX() + 0.25f, other[i].creature->getY() + 0.25f, ( other[i].creature->getShape()->getWidth() + 2 ) * 0.7f, other[i].creature->getShape()->getDepth() * 0.7f );
1245 			} else if ( other[i].pos && other[i].shape && other[i].shape->isOutdoorShadow() ) {
1246 				glColor4f( 0.04f, 0.0f, 0.07f, 0.4f );
1247 				drawGroundTex( outdoorShadowTree,  static_cast<float>( other[i].pos->x ) - ( other[i].shape->getWidth() / 2.0f ) + ( other[i].shape->getWindValue() / 2.0f ), static_cast<float>( other[i].pos->y ) + ( other[i].shape->getDepth() / 2.0f ), other[i].shape->getWidth() * 1.7f, other[i].shape->getDepth() * 1.7f );
1248 			}
1249 		}
1250 	}
1251 
1252 	// draw the walls: walls in front of the player will be transparent
1253 	if ( playerDrawLater ) {
1254 
1255 		if ( floorTexWidth == 0 && resortShapes ) {
1256 			if ( helper->isShapeSortingEnabled() ) {
1257 				sortShapes( playerDrawLater, stencil, stencilCount );
1258 			}
1259 			resortShapes = false;
1260 		}
1261 
1262 		// draw walls behind the player
1263 		for ( int i = 0; i < stencilCount; i++ ) if ( !( stencil[i].inFront ) ) doDrawShape( &( stencil[i] ) );
1264 
1265 		// draw walls in front of the player and water effects
1266 		glEnable( GL_BLEND );
1267 		glDepthMask( GL_FALSE );
1268 		if ( hasWater && preferences->getStencilbuf() && preferences->getStencilBufInitialized() ) {
1269 
1270 			// stencil out the transparent walls (and draw them)
1271 			//glDisable(GL_DEPTH_TEST);
1272 			//glColorMask(0,0,0,0);
1273 			glClear( GL_STENCIL_BUFFER_BIT );
1274 			glEnable( GL_STENCIL_TEST );
1275 			glStencilOp( GL_REPLACE, GL_REPLACE, GL_REPLACE );
1276 			glStencilFunc( GL_ALWAYS, 1, 0xffffffff );
1277 			// draw walls blended in front of the player
1278 			// 6,2 6,4 work well
1279 			// FIXME: blending walls have some artifacts that depth-sorting
1280 			// is supposed to get rid of but that didn't work for me.
1281 			//glBlendFunc( GL_SRC_ALPHA, GL_SRC_COLOR );
1282 			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1283 			for ( int i = 0; i < stencilCount; i++ ) {
1284 				if ( stencil[i].inFront ) {
1285 					glColor4f( 1.0f, 1.0f, 1.0f, 0.45f );
1286 					colorAlreadySet = true;
1287 					doDrawShape( &( stencil[i] ) );
1288 				}
1289 			}
1290 
1291 			// draw the water (except where the transp. walls are)
1292 			glStencilFunc( GL_NOTEQUAL, 1, 0xffffffff );
1293 			glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
1294 			glDisable( GL_TEXTURE_2D );
1295 			glBlendFunc( GL_ONE, GL_SRC_COLOR );
1296 			setupShapes( false, true );
1297 			glEnable( GL_TEXTURE_2D );
1298 
1299 			glDisable( GL_STENCIL_TEST );
1300 		} else {
1301 			// draw transp. walls and water w/o stencil buffer
1302 			//        glBlendFunc( GL_SRC_ALPHA, GL_SRC_COLOR );
1303 			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1304 			for ( int i = 0; i < stencilCount; i++ ) {
1305 				if ( stencil[i].inFront ) {
1306 					glColor4f( 1.0f, 1.0f, 1.0f, 0.45f );
1307 					colorAlreadySet = true;
1308 					doDrawShape( &( stencil[i] ) );
1309 				}
1310 			}
1311 			if ( hasWater ) {
1312 				glDisable( GL_TEXTURE_2D );
1313 				glBlendFunc( GL_ONE, GL_SRC_COLOR );
1314 				setupShapes( false, true );
1315 				glEnable( GL_TEXTURE_2D );
1316 			}
1317 		}
1318 		glDepthMask( GL_TRUE );
1319 
1320 	} else {
1321 		// no player; just draw the damn walls
1322 		for ( int i = 0; i < stencilCount; i++ )
1323 			doDrawShape( &( stencil[i] ) );
1324 
1325 		// draw water (has to come after walls to look good)
1326 		if ( hasWater ) {
1327 			glEnable( GL_BLEND );
1328 			glDepthMask( GL_FALSE );
1329 			glDisable( GL_TEXTURE_2D );
1330 			glBlendFunc( GL_ONE, GL_SRC_COLOR );
1331 			setupShapes( false, true );
1332 			glDepthMask( GL_TRUE );
1333 		}
1334 	}
1335 
1336 	// draw the effects
1337 	glEnable( GL_TEXTURE_2D );
1338 	glEnable( GL_BLEND );
1339 	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1340 
1341 	// draw the roofs
1342 	Uint32 now = SDL_GetTicks();
1343 	if ( now - roofAlphaUpdate > 25 ) {
1344 		roofAlphaUpdate = now;
1345 		if ( isCurrentlyUnderRoof ) {
1346 			if ( roofAlpha > 0 ) {
1347 				roofAlpha -= 0.05f;
1348 			} else {
1349 				roofAlpha = 0;
1350 			}
1351 		} else {
1352 			if ( roofAlpha < 1 ) {
1353 				roofAlpha += 0.05f;
1354 			} else {
1355 				roofAlpha = 1;
1356 			}
1357 		}
1358 	}
1359 	if ( roofAlpha > 0 ) {
1360 		for ( int i = 0; i < roofCount; i++ ) {
1361 			( ( GLShape* )roof[i].shape )->setAlpha( roofAlpha );
1362 			doDrawShape( &roof[i] );
1363 		}
1364 	}
1365 
1366 	glDepthMask( GL_FALSE );
1367 
1368 	for ( int i = 0; i < laterCount; i++ ) {
1369 		later[i].shape->setupBlending();
1370 		doDrawShape( &later[i] );
1371 		later[i].shape->endBlending();
1372 	}
1373 	glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1374 	for ( int i = 0; i < damageCount; i++ ) {
1375 		doDrawShape( &damage[i], 1 );
1376 	}
1377 
1378 	// draw the fog of war or shading
1379 #ifdef USE_LIGHTING
1380 #if DEBUG_MOUSE_POS == 0
1381 	if ( helper && !adapter->isInMovieMode() && !( isCurrentlyUnderRoof && !groundVisible ) ) helper->draw( getX(), getY(), MVW, MVD );
1382 #endif
1383 #endif
1384 
1385 	glDisable( GL_BLEND );
1386 	glDepthMask( GL_TRUE );
1387 }
1388 
1389 /// Renders the 3D view for outdoor levels.
1390 
drawOutdoors()1391 void Map::drawOutdoors() {
1392 	// draw the ground
1393 	renderFloor();
1394 
1395 	// draw the creatures/objects/doors/etc.
1396 	for ( int i = 0; i < otherCount; i++ ) {
1397 		if ( selectedDropTarget && ( ( selectedDropTarget->creature && selectedDropTarget->creature == other[i].creature ) ||
1398 		                             ( selectedDropTarget->item && selectedDropTarget->item == other[i].item ) ) ) {
1399 			colorAlreadySet = true;
1400 			glColor4f( 0.0f, 1.0f, 1.0f, 1.0f );
1401 		}
1402 		doDrawShape( &other[i] );
1403 
1404 		// FIXME: if feeling masochistic, try using stencil buffer to remove shadow-on-shadow effect.
1405 		// draw simple shadow in outdoors
1406 		if ( other[i].creature ) {
1407 			glColor4f( 0.04f, 0.0f, 0.07f, 0.4f );
1408 			drawGroundTex( outdoorShadow, other[i].creature->getX() + 0.25f, other[i].creature->getY() + 0.25f, ( other[i].creature->getShape()->getWidth() + 2 ) * 0.7f, other[i].creature->getShape()->getDepth() * 0.7f );
1409 		} else if ( other[i].pos && other[i].shape && other[i].shape->isOutdoorShadow() ) {
1410 			glColor4f( 0.04f, 0.0f, 0.07f, 0.4f );
1411 			drawGroundTex( outdoorShadowTree, static_cast<float>( other[i].pos->x ) - ( other[i].shape->getWidth() / 2.0f ) + ( other[i].shape->getWindValue() / 2.0f ), static_cast<float>( other[i].pos->y ) + ( other[i].shape->getDepth() / 2.0f ), other[i].shape->getWidth() * 1.7f, other[i].shape->getDepth() * 1.7f );
1412 		}
1413 	}
1414 
1415 	for ( int i = 0; i < stencilCount; i++ ) doDrawShape( &( stencil[i] ) );
1416 
1417 	// draw the effects
1418 	glEnable( GL_TEXTURE_2D );
1419 	glEnable( GL_BLEND );
1420 	drawRoofs();
1421 
1422 	glDepthMask( GL_FALSE );
1423 	drawEffects();
1424 
1425 	// draw the fog of war or shading
1426 #ifdef USE_LIGHTING
1427 #if DEBUG_MOUSE_POS == 0
1428 	if ( helper && !adapter->isInMovieMode() && !( isCurrentlyUnderRoof && !groundVisible ) ) {
1429 		helper->draw( getX(), getY(), MVW, MVD );
1430 	}
1431 #endif
1432 #endif
1433 
1434 	glDisable( GL_BLEND );
1435 	glDepthMask( GL_TRUE );
1436 }
1437 
1438 /// Draws creature effects and damage counters.
1439 
drawEffects()1440 void Map::drawEffects() {
1441 	for ( int i = 0; i < laterCount; i++ ) {
1442 		later[i].shape->setupBlending();
1443 		doDrawShape( &later[i] );
1444 		later[i].shape->endBlending();
1445 	}
1446 	glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1447 	for ( int i = 0; i < damageCount; i++ ) {
1448 		doDrawShape( &damage[i], 1 );
1449 	}
1450 }
1451 
1452 /// Draws the roofs on outdoor levels, including the fading.
1453 
drawRoofs()1454 void Map::drawRoofs() {
1455 	// draw the roofs
1456 	Uint32 now = SDL_GetTicks();
1457 	if ( now - roofAlphaUpdate > 25 ) {
1458 		roofAlphaUpdate = now;
1459 		if ( isCurrentlyUnderRoof ) {
1460 			if ( roofAlpha > 0 ) {
1461 				roofAlpha -= 0.05f;
1462 			} else {
1463 				roofAlpha = 0;
1464 			}
1465 		} else {
1466 			if ( roofAlpha < 1 ) {
1467 				roofAlpha += 0.05f;
1468 			} else {
1469 				roofAlpha = 1;
1470 			}
1471 		}
1472 	}
1473 	if ( roofAlpha > 0 ) {
1474 //  glEnable( GL_BLEND );
1475 //  glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1476 		for ( int i = 0; i < roofCount; i++ ) {
1477 			( ( GLShape* )roof[i].shape )->setAlpha( roofAlpha );
1478 			doDrawShape( &roof[i] );
1479 		}
1480 //    glDisable( GL_BLEND );
1481 	}
1482 }
1483 
willDrawGrid()1484 void Map::willDrawGrid() {
1485 
1486 	glDisable( GL_CULL_FACE );
1487 	glDisable( GL_TEXTURE_2D );
1488 
1489 	glEnable( GL_BLEND );
1490 	glDepthMask( GL_FALSE );
1491 	glBlendFunc( GL_SRC_ALPHA, GL_ONE );
1492 
1493 	// draw the starting position
1494 	float xpos2 = static_cast<float>( this->startx - getX() ) * MUL;
1495 	float ypos2 = static_cast<float>( this->starty - getY() - 1 ) * MUL;
1496 	float zpos2 = 0.0f * MUL;
1497 	float w = 2.0f * MUL;
1498 	float h = 4.0f * MUL;
1499 	if ( useFrustum && frustum->CubeInFrustum( xpos2, ypos2, 0.0f, w * MUL ) ) {
1500 		for ( int i = 0; i < 2; i++ ) {
1501 			glPushMatrix();
1502 			glTranslatef( xpos2, ypos2, zpos2 );
1503 			if ( i == 0 ) {
1504 				glColor4f( 1.0f, 0.0f, 0.0f, 0.5f );
1505 				glBegin( GL_TRIANGLES );
1506 			} else {
1507 				glColor4f( 1.0f, 0.7f, 0.0f, 0.5f );
1508 				glBegin( GL_LINE_LOOP );
1509 			}
1510 
1511 			glVertex3f( 0.0f, 0.0f, 0.0f );
1512 			glVertex3f( -w, -w, h );
1513 			glVertex3f( w, -w, h );
1514 
1515 			glVertex3f( 0.0f, 0.0f, 0.0f );
1516 			glVertex3f( -w, w, h );
1517 			glVertex3f( w, w, h );
1518 
1519 			glVertex3f( 0.0f, 0.0f, 0.0f );
1520 			glVertex3f( -w, -w, h );
1521 			glVertex3f( -w, w, h );
1522 
1523 			glVertex3f( 0.0f, 0.0f, 0.0f );
1524 			glVertex3f( w, -w, h );
1525 			glVertex3f( w, w, h );
1526 
1527 
1528 			glVertex3f( 0.0f, 0.0f, h * 2 );
1529 			glVertex3f( -w, -w, h );
1530 			glVertex3f( w, -w, h );
1531 
1532 			glVertex3f( 0.0f, 0.0f, h * 2 );
1533 			glVertex3f( -w, w, h );
1534 			glVertex3f( w, w, h );
1535 
1536 			glVertex3f( 0.0f, 0.0f, h * 2 );
1537 			glVertex3f( -w, -w, h );
1538 			glVertex3f( -w, w, h );
1539 
1540 			glVertex3f( 0.0f, 0.0f, h * 2 );
1541 			glVertex3f( w, -w, h );
1542 			glVertex3f( w, w, h );
1543 
1544 			glEnd();
1545 			glPopMatrix();
1546 		}
1547 	}
1548 
1549 	glDisable( GL_DEPTH_TEST );
1550 	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1551 	int chunkX = ( cursorFlatMapX - MAP_OFFSET ) / MAP_UNIT;
1552 	int chunkY = ( cursorFlatMapY - MAP_OFFSET - 1 ) / MAP_UNIT;
1553 	float m = 0.5f * MUL;
1554 	int const TMPLEN = 100;
1555 	char tmp[TMPLEN];
1556 	for ( int i = 0; i < chunkCount; i++ ) {
1557 
1558 		float n = static_cast<float>( MAP_UNIT ) * MUL;
1559 
1560 		glPushMatrix();
1561 		glTranslatef( chunks[i].x, chunks[i].y - ( 1.0f * MUL ), 0 );
1562 
1563 		if ( chunks[i].cx == chunkX && chunks[i].cy == chunkY ) {
1564 			glColor4f( 0.0f, 1.0f, 0.0f, 0.25f );
1565 			glLineWidth( 1 );
1566 			snprintf( tmp, TMPLEN, "%d,%d", ( chunkX * MAP_UNIT + MAP_OFFSET ), ( chunkY * MAP_UNIT + MAP_OFFSET + 1 ) );
1567 			adapter->texPrint( 0, 0, tmp );
1568 			for ( int xx = 1; xx < MAP_UNIT; xx++ ) {
1569 				glBegin( GL_LINES );
1570 				glVertex3f( 0, xx * MUL, m );
1571 				glVertex3f( n, xx * MUL, m );
1572 				glEnd();
1573 				glBegin( GL_LINES );
1574 				glVertex3f( xx * MUL, 0, m );
1575 				glVertex3f( xx * MUL, n, m );
1576 				glEnd();
1577 			}
1578 		} else {
1579 			glColor4f( 1.0f, 1.0f, 1.0f, 0.25f );
1580 			glLineWidth( 1 );
1581 		}
1582 		glBegin( GL_LINE_LOOP );
1583 		glVertex3f( 0.0f, 0.0f, m );
1584 		glVertex3f( n, 0.0f, m );
1585 		glVertex3f( n, n, m );
1586 		glVertex3f( 0.0f, n, m );
1587 		glEnd();
1588 		glPopMatrix();
1589 	}
1590 
1591 	glPushMatrix();
1592 
1593 	float xp = static_cast<float>( cursorFlatMapX - getX() ) * MUL;
1594 	float yp = ( static_cast<float>( cursorFlatMapY - getY() ) - 1.0f ) * MUL;
1595 	float cw = static_cast<float>( cursorWidth ) * MUL;
1596 	float cd = -static_cast<float>( cursorDepth ) * MUL;
1597 	m = ( cursorZ ? cursorZ : 0.5f ) * MUL;
1598 	float ch = static_cast<float>( cursorHeight + cursorZ ) * MUL;
1599 
1600 	float red = 1.0f;
1601 	float green = 0.9f;
1602 	float blue = 0.15f;
1603 	bool found = false;
1604 	if ( cursorFlatMapX < MAP_WIDTH && cursorFlatMapY < MAP_DEPTH ) {
1605 		for ( int xx = cursorFlatMapX; xx < cursorFlatMapX + cursorWidth; xx++ ) {
1606 			for ( int yy = cursorFlatMapY - 1; yy >= cursorFlatMapY - cursorDepth; yy-- ) {
1607 				for ( int zz = 0; zz < cursorHeight; zz++ ) {
1608 					if ( pos[xx][yy + 1][zz] ) {
1609 						found = true;
1610 						break;
1611 					}
1612 				}
1613 			}
1614 		}
1615 	}
1616 	if ( found ) {
1617 		green = 0.15f;
1618 	}
1619 
1620 	// draw the cursor
1621 	glColor4f( red, green, blue, 0.25f );
1622 	glTranslatef( xp, yp, 0.0f );
1623 	glBegin( GL_QUADS );
1624 
1625 	glVertex3f( 0.0f, 0.0f, m );
1626 	glVertex3f( cw, 0.0f, m );
1627 	glVertex3f( cw, cd, m );
1628 	glVertex3f( 0.0f, cd, m );
1629 
1630 	glVertex3f( 0.0f, 0.0f, ch );
1631 	glVertex3f( cw, 0.0f, ch );
1632 	glVertex3f( cw, cd, ch );
1633 	glVertex3f( 0.0f, cd, ch );
1634 
1635 	glVertex3f( 0.0f, 0.0f, m );
1636 	glVertex3f( cw, 0.0f, m );
1637 	glVertex3f( cw, 0.0f, ch );
1638 	glVertex3f( 0.0f, 0.0f, ch );
1639 
1640 	glVertex3f( 0.0f, cd, m );
1641 	glVertex3f( cw, cd, m );
1642 	glVertex3f( cw, cd, ch );
1643 	glVertex3f( 0.0f, cd, ch );
1644 
1645 	glVertex3f( 0.0f, 0.0f, m );
1646 	glVertex3f( 0.0f, cd, m );
1647 	glVertex3f( 0.0f, cd, ch );
1648 	glVertex3f( 0.0f, 0.0f, ch );
1649 
1650 	glVertex3f( cw, 0.0f, m );
1651 	glVertex3f( cw, cd, m );
1652 	glVertex3f( cw, cd, ch );
1653 	glVertex3f( cw, 0.0f, ch );
1654 
1655 	glEnd();
1656 	glPopMatrix();
1657 
1658 	glEnable( GL_CULL_FACE );
1659 	glEnable( GL_TEXTURE_2D );
1660 
1661 	glDisable( GL_BLEND );
1662 	glDepthMask( GL_TRUE );
1663 	glEnable( GL_DEPTH_TEST );
1664 }
1665 
1666 /// Sorts the shapes in respect to the player's position.
1667 
1668 /// Since rooms are rectangular, we can do this hack... a wall horizontal
1669 /// wall piece will use the player's x coord. and its y coord.
1670 /// A vertical one will use its X and the player's Y.
1671 /// This is so that even if the wall extends below the player the entire
1672 /// length has the same characteristics.
1673 
sortShapes(DrawLater * playerDrawLater,DrawLater * shapes,int shapeCount)1674 void Map::sortShapes( DrawLater *playerDrawLater, DrawLater *shapes, int shapeCount ) {
1675 	GLdouble mm[16];
1676 	glGetDoublev( GL_MODELVIEW_MATRIX, mm );
1677 	GLdouble pm[16];
1678 	glGetDoublev( GL_PROJECTION_MATRIX, pm );
1679 	GLint vp[4];
1680 	glGetIntegerv( GL_VIEWPORT, vp );
1681 
1682 	GLdouble playerWinX, playerWinY, playerWinZ;
1683 	gluProject( playerDrawLater->xpos, playerDrawLater->ypos, 0, mm, pm, vp, &playerWinX, &playerWinY, &playerWinZ );
1684 
1685 	set< int > xset, yset;
1686 	map< string, bool > cache;
1687 	GLdouble objX, objY;
1688 	for ( int i = 0; i < shapeCount; i++ ) {
1689 		// skip square shapes the first time around
1690 		if ( shapes[i].shape->getWidth() == shapes[i].shape->getDepth() ) {
1691 			shapes[i].inFront = false;
1692 			continue;
1693 		} else if ( shapes[i].shape->getWidth() > shapes[i].shape->getDepth() ) {
1694 			objX = playerDrawLater->xpos;
1695 			objY = shapes[i].ypos;
1696 			yset.insert( toint( shapes[i].ypos ) );
1697 		} else {
1698 			objX = shapes[i].xpos;
1699 			objY = playerDrawLater->ypos;
1700 			xset.insert( toint( shapes[i].xpos ) );
1701 		}
1702 		shapes[i].inFront = isShapeInFront( playerWinY, objX, objY, &cache, mm, pm, vp );
1703 	}
1704 	// now process square shapes: if their x or y lies on a wall-line, they're transparent
1705 	for ( int i = 0; i < shapeCount; i++ ) {
1706 		if ( shapes[i].shape->getWidth() == shapes[i].shape->getDepth() ) {
1707 			if ( xset.find( toint( shapes[i].xpos ) ) != xset.end() ) {
1708 				objX = shapes[i].xpos;
1709 				objY = playerDrawLater->ypos;
1710 			} else if ( yset.find( toint( shapes[i].ypos ) ) != yset.end() ) {
1711 				objX = playerDrawLater->xpos;
1712 				objY = shapes[i].ypos;
1713 			} else {
1714 				continue;
1715 			}
1716 			shapes[i].inFront = isShapeInFront( playerWinY, objX, objY, &cache, mm, pm, vp );
1717 		}
1718 	}
1719 }
1720 
1721 /// Is there a shape in front of the player, obscuring him/her?
1722 
isShapeInFront(GLdouble playerWinY,GLdouble objX,GLdouble objY,map<string,bool> * cache,GLdouble * mm,GLdouble * pm,GLint * vp)1723 bool Map::isShapeInFront( GLdouble playerWinY, GLdouble objX, GLdouble objY, map< string, bool > *cache, GLdouble *mm, GLdouble *pm, GLint *vp ) {
1724 
1725 	GLdouble wallWinX, wallWinY, wallWinZ;
1726 	bool b;
1727 	char tmp[80];
1728 
1729 	snprintf( tmp, 80, "%f,%f", objX, objY );
1730 	string key = tmp;
1731 	if ( cache->find( key ) == cache->end() ) {
1732 		gluProject( objX, objY, 0, mm, pm, vp, &wallWinX, &wallWinY, &wallWinZ );
1733 		b = ( wallWinY < playerWinY );
1734 		( *cache )[ key ] = b;
1735 	} else {
1736 		b = ( *cache )[ key ];
1737 	}
1738 	return b;
1739 }
1740 
1741 /// Draws the projectiles.
1742 
drawProjectiles()1743 void Map::drawProjectiles() {
1744 	for ( map<RenderedCreature*, vector<RenderedProjectile*>*>::iterator i = RenderedProjectile::getProjectileMap()->begin();
1745 	        i != RenderedProjectile::getProjectileMap()->end(); ++i ) {
1746 		//RenderedCreature *creature = i->first;
1747 		vector<RenderedProjectile*> *p = i->second;
1748 		for ( vector<RenderedProjectile*>::iterator e = p->begin(); e != p->end(); ++e ) {
1749 			RenderedProjectile *proj = *e;
1750 
1751 			// calculate the path
1752 			vector<CVector3> path;
1753 			for ( int i = 0; i < proj->getStepCount(); i++ ) {
1754 				CVector3 v;
1755 				v.x = ( ( proj->getX( i ) + proj->getRenderer()->getOffsetX() - static_cast<float>( getX() ) ) * MUL );
1756 				v.y = ( ( proj->getY( i ) - proj->getRenderer()->getOffsetY() - static_cast<float>( getY() ) - 1.0f ) * MUL );
1757 				v.z = ( proj->getZ( i ) + proj->getRenderer()->getOffsetZ() ) * MUL;
1758 				path.push_back( v );
1759 			}
1760 			proj->getRenderer()->drawPath( this, proj, &path );
1761 		}
1762 	}
1763 }
1764 
1765 /// Draws a shape stored in a DrawLater object.
1766 
doDrawShape(DrawLater * later,int effect)1767 void Map::doDrawShape( DrawLater *later, int effect ) {
1768 	doDrawShape( later->xpos, later->ypos, later->zpos, later->shape, effect, later );
1769 }
1770 
doDrawShape(float xpos2,float ypos2,float zpos2,Shape * shape,int effect,DrawLater * later)1771 void Map::doDrawShape( float xpos2, float ypos2, float zpos2, Shape *shape, int effect, DrawLater *later ) {
1772 
1773 	// fog test for creatures
1774 	if ( helper && later && later->creature && later->pos && !adapter->isInMovieMode() && !helper->isVisible( later->pos->x, later->pos->y, later->creature->getShape() ) ) {
1775 		return;
1776 	}
1777 
1778 	if ( shape ) ( ( GLShape* )shape )->useShadow = useShadow;
1779 
1780 	// slow on mac os X:
1781 	// glPushAttrib(GL_ENABLE_BIT);
1782 
1783 	glPushMatrix();
1784 
1785 	float heightPos = 0.0f;
1786 	if ( later && later->pos ) {
1787 		GLShape *s = ( GLShape* )later->pos->shape;
1788 		if ( s->isVirtual() ) {
1789 			s = ( ( VirtualShape* )s )->getRef();
1790 		}
1791 
1792 		if ( !s->getIgnoreHeightMap() ) {
1793 			heightPos = later->pos->heightPos * MUL;
1794 		}
1795 	} else if ( later && later->effect ) {
1796 		heightPos = later->effect->heightPos;
1797 	}
1798 
1799 	float xdiff = 0;
1800 	float ydiff = 0;
1801 	if ( later && later->creature ) {
1802 		xdiff = ( later->creature->getX() - static_cast<float>( toint( later->creature->getX() ) ) );
1803 		ydiff = ( later->creature->getY() - static_cast<float>( toint( later->creature->getY() ) ) );
1804 	}
1805 
1806 	if ( useShadow ) {
1807 		// put shadow above the floor a little
1808 		glTranslatef( xpos2 + xdiff * MUL, ypos2 + ydiff * MUL, ( 0.26f * MUL + heightPos ) );
1809 		glMultMatrixf( shadowTransformMatrix );
1810 		// purple shadows
1811 		glColor4f( 0.04f, 0.0f, 0.07f, 0.6f );
1812 	} else {
1813 		glTranslatef( xpos2 + xdiff * MUL, ypos2 + ydiff * MUL, zpos2 + heightPos );
1814 
1815 		if ( later && later->pos ) {
1816 			glTranslatef( later->pos->moveX, later->pos->moveY, later->pos->moveZ );
1817 			glRotatef( later->pos->angleX, 1.0f, 0.0f, 0.0f );
1818 			glRotatef( later->pos->angleY, 0.0f, 1.0f, 0.0f );
1819 			glRotatef( later->pos->angleZ, 0.0f, 0.0f, 1.0f );
1820 		}
1821 
1822 		if ( later && later->creature ) {
1823 			glTranslatef( later->creature->getOffsetX(), later->creature->getOffsetY(), later->creature->getOffsetZ() );
1824 		}
1825 
1826 #ifdef DEBUG_SECRET_DOORS
1827 		if ( later && later->pos ) {
1828 			int xp = later->pos->x;
1829 			int yp = later->pos->y;
1830 			int index = xp + MAP_WIDTH * yp;
1831 			if ( secretDoors.find( index ) != secretDoors.end() ) {
1832 				glColor4f( 1.0f, 0.3f, 0.3f, 1.0f );
1833 				colorAlreadySet = true;
1834 			}
1835 		}
1836 #endif
1837 
1838 		// show detected secret doors
1839 		if ( later && later->pos ) {
1840 			if ( isSecretDoor( later->pos ) && ( isSecretDoorDetected( later->pos ) || settings->isGridShowing() ) ) {
1841 				glColor4f( 0.3f, 0.7f, 0.3f, 1.0f );
1842 				colorAlreadySet = true;
1843 			}
1844 		}
1845 
1846 		if ( colorAlreadySet   ) {
1847 			colorAlreadySet = false;
1848 		} else {
1849 			if ( later && later->pos && isLocked( later->pos->x, later->pos->y, later->pos->z ) ) {
1850 				glColor4f( 1.0f, 0.3f, 0.3f, 1.0f );
1851 			} else {
1852 				//glColor4f(0.72f, 0.65f, 0.55f, 0.5f);
1853 				glColor4f( 1.0f, 1.0f, 1.0f, 0.9f );
1854 			}
1855 		}
1856 	}
1857 
1858 	glDisable( GL_CULL_FACE );
1859 
1860 	if ( shape ) {
1861 		( ( GLShape* )shape )->setCameraRot( xrot, yrot, zrot );
1862 		( ( GLShape* )shape )->setCameraPos( xpos, ypos, zpos, xpos2, ypos2, zpos2 );
1863 		if ( later && later->pos ) ( ( GLShape* )shape )->setLocked( isLocked( later->pos->x, later->pos->y, 0 ) );
1864 		else ( ( GLShape* )shape )->setLocked( false );
1865 	}
1866 	if ( effect && later ) {
1867 		if ( later->creature ) {
1868 			// translate hack for md2 models... see: md2shape::draw()
1869 			//glTranslatef( 0, -1 * MUL, 0 );
1870 			later->creature->getEffect()->draw( later->creature->getEffectType(),
1871 			                                    later->creature->getDamageEffect() );
1872 			//glTranslatef( 0, 1 * MUL, 0 );
1873 		} else if ( later->effect ) {
1874 			later->effect->getEffect()->draw( later->effect->getEffectType(),
1875 			                                  later->effect->getDamageEffect() );
1876 		}
1877 	} else if ( later && later->creature && !useShadow ) {
1878 		// outline mission creatures
1879 		if ( adapter->isMissionCreature( later->creature ) ) {
1880 			//if( session->getCurrentMission() &&
1881 			//session->getCurrentMission()->isMissionCreature( later->creature ) ) {
1882 			shape->outline( 0.15f, 0.15f, 0.4f );
1883 		} else if ( later->creature->isBoss() ) {
1884 			shape->outline( 0.4f, 0.15f, 0.4f );
1885 		} else if ( later && later->pos &&
1886 		            later->pos->outlineColor ) {
1887 			shape->outline( later->pos->outlineColor );
1888 		}
1889 
1890 		if ( later->creature->getStateMod( StateMod::invisible ) ) {
1891 			glColor4f( 0.3f, 0.8f, 1.0f, 0.5f );
1892 			glEnable( GL_BLEND );
1893 			//glDepthMask( GL_FALSE );
1894 			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1895 		} else if ( later->creature->getStateMod( StateMod::possessed ) ) {
1896 			glColor4f( 1.0, 0.3f, 0.8f, 1.0f );
1897 		}
1898 		shape->draw();
1899 
1900 		if ( later->creature->getStateMod( StateMod::invisible ) ) {
1901 			glDisable( GL_BLEND );
1902 			//glDepthMask( GL_TRUE );
1903 		}
1904 
1905 	} else if ( later && later->item && !useShadow ) {
1906 
1907 		if ( later->item->isSpecial() ) {
1908 			shape->outline( Constants::SPECIAL_ITEM_COLOR );
1909 		} else if ( later->item->isMagicItem() ) {
1910 			shape->outline( Constants::MAGIC_ITEM_COLOR[ later->item->getMagicLevel() ] );
1911 		} else if ( later->item->getContainsMagicItem() ) {
1912 			shape->outline( 0.8f, 0.8f, 0.3f );
1913 		}
1914 
1915 		if ( later && later->pos && later->pos->outlineColor && !useShadow )
1916 			shape->outline( later->pos->outlineColor );
1917 		shape->draw();
1918 
1919 
1920 
1921 	} else {
1922 		if ( later ) {
1923 			bool sides[6];
1924 			findOccludedSides( later, sides );
1925 			shape->setOccludedSides( sides );
1926 		}
1927 		if ( later && later->pos ) {
1928 			if ( later->pos->outlineColor && !useShadow ) {
1929 				shape->outline( later->pos->outlineColor );
1930 			}
1931 			if ( shape->getTextureCount() > 3 ) {
1932 				// select which alternate texture to use
1933 				shape->setTextureIndex( later->pos->texIndex );
1934 			}
1935 		}
1936 		shape->draw();
1937 	}
1938 
1939 #if DEBUG_MOUSE_POS == 1
1940 	if ( shape && !useShadow && ( ( later && later->item ) || ( later && later->creature ) || shape->isInteractive() ) ) {
1941 		glDisable( GL_DEPTH_TEST );
1942 		glDepthMask( GL_FALSE );
1943 		glDisable( GL_CULL_FACE );
1944 		glDisable( GL_TEXTURE_2D );
1945 		glColor4f( 1, 1, 1, 1 );
1946 		glBegin( GL_LINE_LOOP );
1947 		glVertex3f( 0, 0, 0 );
1948 		glVertex3f( 0, shape->getDepth() * MUL, 0 );
1949 		glVertex3f( shape->getWidth() * MUL, shape->getDepth() * MUL, 0 );
1950 		glVertex3f( shape->getWidth() * MUL, 0, 0 );
1951 		glEnd();
1952 		glBegin( GL_LINE_LOOP );
1953 		glVertex3f( 0, 0, shape->getHeight() * MUL );
1954 		glVertex3f( 0, shape->getDepth() * MUL, shape->getHeight() * MUL );
1955 		glVertex3f( shape->getWidth() * MUL, shape->getDepth() * MUL, shape->getHeight() * MUL );
1956 		glVertex3f( shape->getWidth() * MUL, 0, shape->getHeight() * MUL );
1957 		glEnd();
1958 		glBegin( GL_LINES );
1959 		glVertex3f( 0, 0, 0 );
1960 		glVertex3f( 0, 0, shape->getHeight() * MUL );
1961 		glEnd();
1962 		glBegin( GL_LINES );
1963 		glVertex3f( 0, shape->getDepth() * MUL, 0 );
1964 		glVertex3f( 0, shape->getDepth() * MUL, shape->getHeight() * MUL );
1965 		glEnd();
1966 		glBegin( GL_LINES );
1967 		glVertex3f( shape->getWidth() * MUL, shape->getDepth() * MUL, 0 );
1968 		glVertex3f( shape->getWidth() * MUL, shape->getDepth() * MUL, shape->getHeight() * MUL );
1969 		glEnd();
1970 		glBegin( GL_LINES );
1971 		glVertex3f( shape->getWidth() * MUL, 0, 0 );
1972 		glVertex3f( shape->getWidth() * MUL, 0, shape->getHeight() * MUL );
1973 		glEnd();
1974 		glDepthMask( GL_TRUE );
1975 		glEnable( GL_DEPTH_TEST );
1976 	}
1977 #endif
1978 
1979 	// in the map editor outline virtual shapes
1980 	if ( shape->isVirtual() && settings->isGridShowing() && gridEnabled ) {
1981 
1982 		if ( heightPos > 1 ) {
1983 			cerr << "heightPos=" << heightPos << " for virtual shape " << shape->getName() << endl;
1984 		}
1985 		if ( later && later->pos && later->pos->z > 0 ) {
1986 			cerr << "z=" << later->pos->z << " for virtual shape " << shape->getName() << endl;
1987 		}
1988 
1989 		glColor4f( 0.75f, 0.75f, 1.0f, 1.0f );
1990 
1991 		float z = ( shape->getHeight() + 0.25f ) * MUL;
1992 		float lowZ = 0.25f * MUL;
1993 
1994 		float wm = shape->getWidth() * MUL;
1995 		float dm = shape->getDepth() * MUL;
1996 
1997 		glPushMatrix();
1998 		glTranslatef( 0.0f, 20.0f, z );
1999 		adapter->texPrint( 0, 0, "virtual" );
2000 		glPopMatrix();
2001 
2002 		glDisable( GL_TEXTURE_2D );
2003 		glBegin( GL_LINE_LOOP );
2004 		glVertex3f( 0.0f, 0.0f, z );
2005 		glVertex3f( 0.0f, dm, z );
2006 		glVertex3f( wm, dm, z );
2007 		glVertex3f( wm, 0.0f, z );
2008 
2009 		glVertex3f( 0.0f, 0.0f, z );
2010 		glVertex3f( 0.0f, 0.0f, lowZ );
2011 		glVertex3f( wm, 0.0f, lowZ );
2012 		glVertex3f( wm, 0.0f, z );
2013 
2014 		glVertex3f( 0.0f, dm, z );
2015 		glVertex3f( 0.0f, dm, lowZ );
2016 		glVertex3f( wm, dm, lowZ );
2017 		glVertex3f( wm, dm, z );
2018 
2019 		glVertex3f( 0.0f, 0.0f, z );
2020 		glVertex3f( 0.0f, 0.0f, lowZ );
2021 		glVertex3f( 0.0f, dm, lowZ );
2022 		glVertex3f( 0.0f, dm, z );
2023 
2024 		glVertex3f( wm, 0.0f, z );
2025 		glVertex3f( wm, 0.0f, lowZ );
2026 		glVertex3f( wm, dm, lowZ );
2027 		glVertex3f( wm, dm, z );
2028 
2029 		glEnd();
2030 		glEnable( GL_TEXTURE_2D );
2031 	}
2032 
2033 	glPopMatrix();
2034 
2035 	// slow on mac os X
2036 	// glPopAttrib();
2037 
2038 	if ( shape )
2039 		( ( GLShape* )shape )->useShadow = false;
2040 }
2041 
2042 /// Determines which sides of a shape are not visible for various reasons.
2043 
findOccludedSides(DrawLater * later,bool * sides)2044 void Map::findOccludedSides( DrawLater *later, bool *sides ) {
2045 	if ( colorAlreadySet || !later || !later->pos || !later->shape || !later->shape->isStencil() || ( later->shape && isDoor( later->shape ) ) ) {
2046 		sides[Shape::BOTTOM_SIDE] = sides[Shape::N_SIDE] = sides[Shape::S_SIDE] =
2047 		                                                     sides[Shape::E_SIDE] = sides[Shape::W_SIDE] = sides[Shape::TOP_SIDE] = true;
2048 		return;
2049 	}
2050 
2051 	sides[Shape::BOTTOM_SIDE] = sides[Shape::N_SIDE] =
2052 	                              sides[Shape::S_SIDE] = sides[Shape::E_SIDE] =
2053 	                                                       sides[Shape::W_SIDE] = sides[Shape::TOP_SIDE] = false;
2054 
2055 	int x, y;
2056 	Location *pos;
2057 	for ( x = later->pos->x; x < later->pos->x + later->pos->shape->getWidth(); x++ ) {
2058 		y = later->y - later->pos->shape->getDepth();
2059 		pos = getLocation( x, y, later->pos->z );
2060 		if ( !pos || !pos->shape->isStencil() || ( !isLocationInLight( x, y, pos->shape ) && !isDoorType( pos->shape ) ) ) {
2061 			sides[Shape::N_SIDE] = true;
2062 		}
2063 
2064 		y = later->y + 1;
2065 		pos = getLocation( x, y, later->pos->z );
2066 		if ( !pos || !pos->shape->isStencil() || ( !isLocationInLight( x, y, pos->shape ) && !isDoorType( pos->shape ) ) ) {
2067 			sides[Shape::S_SIDE] = true;
2068 		}
2069 
2070 		if ( sides[Shape::N_SIDE] && sides[Shape::S_SIDE] ) {
2071 			break;
2072 		}
2073 	}
2074 
2075 
2076 	for ( y = later->pos->y - later->pos->shape->getDepth() + 1; y <= later->pos->y; y++ ) {
2077 		x = later->x - 1;
2078 		pos = getLocation( x, y, later->pos->z );
2079 		if ( !pos || !pos->shape->isStencil() || ( !isLocationInLight( x, y, pos->shape ) && !isDoorType( pos->shape ) ) ) {
2080 			sides[Shape::W_SIDE] = true;
2081 		}
2082 
2083 		x = later->x + later->pos->shape->getWidth();
2084 		pos = getLocation( x, y, later->pos->z );
2085 		if ( !pos || !pos->shape->isStencil() || ( !isLocationInLight( x, y, pos->shape ) && !isDoorType( pos->shape ) ) ) {
2086 			sides[Shape::E_SIDE] = true;
2087 		}
2088 
2089 		if ( sides[Shape::W_SIDE] && sides[Shape::E_SIDE] ) {
2090 			break;
2091 		}
2092 	}
2093 
2094 
2095 	for ( x = later->pos->x; x < later->pos->x + later->pos->shape->getWidth(); x++ ) {
2096 		for ( y = later->pos->y - later->pos->shape->getDepth() + 1; !sides[Shape::TOP_SIDE] && y <= later->pos->y; y++ ) {
2097 			pos = getLocation( x, y, later->pos->z + later->pos->shape->getHeight() );
2098 			if ( !pos || !pos->shape->isStencil() || ( !isLocationInLight( x, y, pos->shape ) && !isDoorType( pos->shape ) ) ) {
2099 				sides[Shape::TOP_SIDE] = true;
2100 				break;
2101 			}
2102 		}
2103 	}
2104 }
2105 
2106 /// Is the x,y,z location currently on the screen?
2107 
isOnScreen(Uint16 mapx,Uint16 mapy,Uint16 mapz)2108 bool Map::isOnScreen( Uint16 mapx, Uint16 mapy, Uint16 mapz ) {
2109 	glPushMatrix();
2110 
2111 	// Initialize the scene w/o y rotation.
2112 	initMapView( true );
2113 
2114 	double obj_x = ( mapx - getX() + 1 ) * MUL;
2115 	double obj_y = ( mapy - getY() - 2 ) * MUL;
2116 	double obj_z = 0.0f;
2117 	//double obj_z = mapz * MUL;
2118 	double win_x, win_y, win_z;
2119 
2120 	double projection[16];
2121 	double modelview[16];
2122 	GLint viewport[4];
2123 
2124 	glGetDoublev( GL_PROJECTION_MATRIX, projection );
2125 	glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
2126 	glGetIntegerv( GL_VIEWPORT, viewport );
2127 
2128 	int res = gluProject( obj_x, obj_y, obj_z, modelview, projection, viewport, &win_x, &win_y, &win_z );
2129 
2130 	glDisable( GL_SCISSOR_TEST );
2131 	glPopMatrix();
2132 
2133 	if ( res ) {
2134 		win_y = adapter->getScreenHeight() - win_y;
2135 		return ( win_x >= 0 && win_x < adapter->getScreenWidth() && win_y >= 0 && win_y < adapter->getScreenHeight() );
2136 	}
2137 	return false;
2138 }
2139 
2140 /// Converts a map position to a screen coordinate (where on the screen is tile x,y?)
2141 
getScreenXYAtMapXY(Uint16 mapx,Uint16 mapy,Uint16 * screenx,Uint16 * screeny)2142 void Map::getScreenXYAtMapXY( Uint16 mapx, Uint16 mapy, Uint16 *screenx, Uint16 *screeny ) {
2143 	glPushMatrix();
2144 
2145 	// Initialize the scene with y rotation.
2146 	initMapView( false );
2147 
2148 	double obj_x = ( mapx - getX() + 1 ) * MUL;
2149 	double obj_y = ( mapy - getY() - 2 ) * MUL;
2150 	double obj_z = 0.0f;
2151 	//double obj_z = mapz * MUL;
2152 	double win_x, win_y, win_z;
2153 
2154 	double projection[16];
2155 	double modelview[16];
2156 	GLint viewport[4];
2157 
2158 	glGetDoublev( GL_PROJECTION_MATRIX, projection );
2159 	glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
2160 	glGetIntegerv( GL_VIEWPORT, viewport );
2161 
2162 	int res = gluProject( obj_x, obj_y, obj_z, modelview, projection, viewport, &win_x, &win_y, &win_z );
2163 
2164 	glDisable( GL_SCISSOR_TEST );
2165 	glPopMatrix();
2166 
2167 	if ( res ) {
2168 		win_y = adapter->getScreenHeight() - win_y;
2169 		if ( win_x < 0 ) win_x = 0;
2170 		if ( win_x > adapter->getScreenWidth() - 1 ) win_x = adapter->getScreenWidth() - 1;
2171 		if ( win_y < 0 ) win_y = 0;
2172 		if ( win_y > adapter->getScreenHeight() - 1 ) win_y = adapter->getScreenHeight() - 1;
2173 		*screenx = static_cast<Uint16>( win_x );
2174 		*screeny = static_cast<Uint16>( win_y );
2175 	}
2176 }
2177 
getPanningFromMapXY(Uint16 mapx,Uint16 mapy)2178 int Map::getPanningFromMapXY( Uint16 mapx, Uint16 mapy ) {
2179 	Uint16 screenx, screeny;
2180 	getScreenXYAtMapXY( mapx, mapy, &screenx, &screeny );
2181 	float panning = ( static_cast<float>( screenx ) / adapter->getScreenWidth() ) * 255;
2182 	return toint( panning );
2183 }
2184 
2185 /*
2186 void Map::showInfoAtMapPos(Uint16 mapx, Uint16 mapy, Uint16 mapz, char *message) {
2187   float xpos2 = (static_cast<float>(mapx - getX()) * MUL);
2188   float ypos2 = (static_cast<float>(mapy - getY()) * MUL);
2189   float zpos2 = static_cast<float>(mapz) * MUL;
2190   glTranslatef( xpos2, ypos2, zpos2 + 100);
2191 
2192   //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
2193   //glRasterPos2f( 0, 0 );
2194   scourge->getSDLHandler()->texPrint(0, 0, "%s", message);
2195 
2196   glTranslatef( -xpos2, -ypos2, -(zpos2 + 100));
2197 }
2198 */
2199 
2200 /// Positions the "camera".
2201 
setPos(float x,float y,float z)2202 void Map::setPos( float x, float y, float z ) {
2203 	this->x = ( int )x;
2204 	this->y = ( int )y;
2205 	this->mapx = x;
2206 	this->mapy = y;
2207 	mapChanged = true;
2208 }
2209 
2210 /// Initializes the map view (translate, rotate)
2211 
initMapView(bool ignoreRot)2212 void Map::initMapView( bool ignoreRot ) {
2213 	glLoadIdentity();
2214 
2215 	glTranslatef( viewX, viewY, 0 );
2216 	glScissor( viewX, adapter->getScreenHeight() - ( viewY + viewHeight ), viewWidth, viewHeight );
2217 	glEnable( GL_SCISSOR_TEST );
2218 	// adjust for screen size
2219 	float adjust = static_cast<float>( viewWidth ) / 800.0f;
2220 	glScalef( adjust, adjust, adjust );
2221 
2222 	glScalef( zoom, zoom, zoom );
2223 
2224 	// translate the camera and rotate
2225 	// the offsets ensure that the center of rotation is under the player
2226 	glTranslatef( this->xpos, this->ypos, 0 );
2227 	if ( !ignoreRot ) {
2228 		glRotatef( xrot, 0.0f, 1.0f, 0.0f );
2229 		glRotatef( yrot, 1.0f, 0.0f, 0.0f );
2230 		glRotatef( zrot, 0.0f, 0.0f, 1.0f );
2231 	}
2232 	glTranslatef( 0.0f, 0.0f, this->zpos );
2233 
2234 	// adjust for centered-map movement
2235 	float xdiff = 0;
2236 	float ydiff = 0;
2237 	if ( settings->isPlayerEnabled() && ( preferences->getAlwaysCenterMap() || mapCenterCreature ) ) {
2238 		RenderedCreature *c = ( mapCenterCreature ? mapCenterCreature : adapter->getPlayer() );
2239 		if ( c ) {
2240 			xdiff = ( c->getX() - static_cast<float>( toint( c->getX() ) ) );
2241 			ydiff = ( c->getY() - static_cast<float>( toint( c->getY() ) ) );
2242 		}
2243 	}
2244 	float startx = -( static_cast<float>( mapViewWidth ) / 2.0 + ( mapx - static_cast<float>( x ) + xdiff ) - 4 ) * MUL;
2245 	float starty = -( static_cast<float>( mapViewDepth ) / 2.0 + ( mapy - static_cast<float>( y ) + ydiff ) - 8 ) * MUL;
2246 	float startz = 0.0;
2247 
2248 	glTranslatef( startx, starty, startz );
2249 
2250 	if ( quakesEnabled ) {
2251 		Uint32 now = SDL_GetTicks();
2252 
2253 		// is it time to quake again?
2254 		if ( now >= nextQuakeStartTime ) {
2255 			nextQuakeStartTime =
2256 			  now +
2257 			  QUAKE_DELAY +
2258 			  Util::dice( QUAKE_DELAY / 2 );
2259 			// start a quake unless this is the very first time
2260 			quakeStartTime = ( quakeStartTime == 0 ? nextQuakeStartTime : now );
2261 			if ( quakeStartTime == now ) adapter->writeLogMessage( _( "A tremor shakes the earth..." ) );
2262 		}
2263 
2264 		// is it quaking now?
2265 		if ( now - quakeStartTime < QUAKE_DURATION ) {
2266 			if ( now - lastQuakeTick >= QUAKE_TICK_FREQ ) {
2267 				quakeOffsX = Util::roll( 0.0f, 3.0f * MUL );
2268 				quakeOffsY = Util::roll( 0.0f, 3.0f * MUL );
2269 				lastQuakeTick = now;
2270 			}
2271 		} else {
2272 			if ( quakeOnce ) {
2273 				quakeOnce = false;
2274 				quakesEnabled = false;
2275 			}
2276 			quakeOffsX = quakeOffsY = 0;
2277 		}
2278 
2279 		glTranslatef( quakeOffsX, quakeOffsY, 0 );
2280 	}
2281 }
2282 
2283 /// Trigger a quake (the screen shivers for a while).
2284 
quake()2285 void Map::quake() {
2286 	quakesEnabled = true;
2287 	nextQuakeStartTime = SDL_GetTicks() + 2 * QUAKE_DELAY;
2288 	quakeStartTime = SDL_GetTicks();
2289 	quakeOnce = true;
2290 }
2291 
2292 /// Moves a creature (instantly) by 1 tile into the specified direction.
2293 
2294 /// if you can't move to this spot (blocked) returns the blocking shape,
2295 /// otherwise returns NULL and moves the shape.
2296 
moveCreature(Sint16 x,Sint16 y,Sint16 z,Uint16 dir,RenderedCreature * newCreature)2297 Location *Map::moveCreature( Sint16 x, Sint16 y, Sint16 z, Uint16 dir, RenderedCreature *newCreature ) {
2298 	Sint16 nx = x;
2299 	Sint16 ny = y;
2300 	Sint16 nz = z;
2301 	switch ( dir ) {
2302 	case Constants::MOVE_UP: ny--; break;
2303 	case Constants::MOVE_DOWN: ny++; break;
2304 	case Constants::MOVE_LEFT: nx--; break;
2305 	case Constants::MOVE_RIGHT: nx++; break;
2306 	}
2307 	return moveCreature( x, y, z, nx, ny, nz, newCreature );
2308 }
2309 
2310 /// Moves a creature (instantly).
2311 
moveCreature(Sint16 x,Sint16 y,Sint16 z,Sint16 nx,Sint16 ny,Sint16 nz,RenderedCreature * newCreature)2312 Location *Map::moveCreature( Sint16 x, Sint16 y, Sint16 z, Sint16 nx, Sint16 ny, Sint16 nz, RenderedCreature *newCreature ) {
2313 
2314 	// no need to actually move data
2315 	if ( x == nx && y == ny && z == nz ) {
2316 		return NULL;
2317 	}
2318 
2319 	//float interX, interY;
2320 	Location *position = isBlocked( nx, ny, nz, x, y, z, newCreature->getShape() );
2321 	if ( position ) return position;
2322 
2323 	// move position
2324 	moveCreaturePos( nx, ny, nz, x, y, z, newCreature );
2325 
2326 	return NULL;
2327 }
2328 
setRugPosition(Sint16 xchunk,Sint16 ychunk,Rug * rug)2329 void Map::setRugPosition( Sint16 xchunk, Sint16 ychunk, Rug *rug ) {
2330 	rugPos[ xchunk ][ ychunk ] = *rug;
2331 }
2332 
2333 /// Creates a shape on the floor (indoors).
2334 
setFloorPosition(Sint16 x,Sint16 y,Shape * shape)2335 void Map::setFloorPosition( Sint16 x, Sint16 y, Shape *shape ) {
2336 	if ( x < 0 || y < 0 || x >= MAP_WIDTH || y >= MAP_DEPTH ) {
2337 		cerr << "*** floor position out of bounds: " << x << "," << y << endl;
2338 		//((RenderedCreature*)NULL)->getName();
2339 		return;
2340 	}
2341 
2342 	floorPositions[x][y] = shape;
2343 	WaterTile *w = new WaterTile;
2344 	for ( int xp = 0; xp < WATER_TILE_X; xp++ ) {
2345 		for ( int yp = 0; yp < WATER_TILE_Y; yp++ ) {
2346 			w->z[xp][yp] = Util::roll( -WATER_AMP, WATER_AMP );
2347 			w->step[xp][yp] = WATER_STEP * ( Util::dice( 2 ) ? 1 : -1 );
2348 			w->lastTime[xp][yp] = 0;
2349 		}
2350 	}
2351 	water[createPairKey( x, y )] = w;
2352 }
2353 
removeRugPosition(Sint16 xchunk,Sint16 ychunk)2354 void Map::removeRugPosition( Sint16 xchunk, Sint16 ychunk ) {
2355 	rugPos[ xchunk ][ ychunk ].texture.clear();
2356 }
2357 
2358 /// Removes items lying on the floor and water at the specified location.
2359 
removeFloorPosition(Sint16 x,Sint16 y)2360 Shape *Map::removeFloorPosition( Sint16 x, Sint16 y ) {
2361 	Shape *shape = NULL;
2362 	if ( floorPositions[x][y] ) {
2363 		shape = floorPositions[x][y];
2364 		floorPositions[x][y] = 0;
2365 	}
2366 	Uint32 key = createPairKey( x, y );
2367 	if ( water.find( key ) != water.end() ) {
2368 		delete water[key];
2369 		water.erase( key );
2370 	}
2371 	return shape;
2372 }
2373 
2374 /// Can the specified shape be moved from one x,y,z position to another x,y,z position?
2375 
2376 /// Can shape at shapeX, shapeY, shapeZ move to location x, y, z?
2377 /// returns NULL if ok, or the blocking Shape* otherwise.
2378 /// if newz is not null, it will ignore blocking "item"-s and instead stack the new
2379 /// shape on top, returning the new z position in newz.
2380 
isBlocked(Sint16 x,Sint16 y,Sint16 z,Sint16 shapeX,Sint16 shapeY,Sint16 shapeZ,Shape * s,int * newz,bool useItemPos)2381 Location *Map::isBlocked( Sint16 x, Sint16 y, Sint16 z, Sint16 shapeX, Sint16 shapeY, Sint16 shapeZ, Shape *s, int *newz, bool useItemPos ) {
2382 	int zz = z;
2383 	for ( int sx = 0; sx < s->getWidth(); sx++ ) {
2384 		for ( int sy = 0; sy < s->getDepth(); sy++ ) {
2385 			if ( !s->isInside( sx, sy ) ) continue;
2386 			if ( fabs( getGroundHeight( ( x + sx ) / OUTDOORS_STEP, ( y - sy ) / OUTDOORS_STEP ) ) > 10.0f ) {
2387 				return hackBlockingPos;
2388 			}
2389 
2390 			// find the lowest location where this item fits
2391 			int sz = z;
2392 			while ( sz < zz + s->getHeight() ) {
2393 				Location *loc = pos[x + sx][y - sy][z + sz];
2394 				if ( loc && loc->shape && !loc->shape->isRoof() &&
2395 				        !( loc->x == shapeX && loc->y == shapeY && loc->z == shapeZ ) ) {
2396 					if ( newz && ( loc->item || loc->creature ) ) {
2397 						int tz = loc->z + loc->shape->getHeight();
2398 						if ( tz > zz ) zz = tz;
2399 						if ( zz + s->getHeight() >= MAP_VIEW_HEIGHT ) {
2400 							return pos[x + sx][y - sy][z + sz];
2401 						}
2402 						if ( zz > sz ) sz = zz;
2403 						else break;
2404 					} else if ( newz && loc ) {
2405 						return pos[x + sx][y - sy][z + sz];
2406 					} else if ( !newz && !( loc && loc->item && !loc->item->isBlocking() ) ) {
2407 						return pos[x + sx][y - sy][z + sz];
2408 					} else {
2409 						sz++;
2410 					}
2411 				} else {
2412 					sz++;
2413 				}
2414 			}
2415 		}
2416 	}
2417 
2418 	// check itemPos space: cancel if item cannot be stored here... maybe better to
2419 	// create a list of items at each location? (in itemPos[] only, inter-locking shapes
2420 	// are not supported by pos[]) For now, only 1 item per pos in itemPos[].
2421 	if ( useItemPos && !zz ) {
2422 		for ( int sx = 0; sx < s->getWidth(); sx++ ) {
2423 			for ( int sy = 0; sy < s->getDepth(); sy++ ) {
2424 				if ( !s->isInside( sx, sy ) ) continue;
2425 				Location *loc = itemPos[x + sx][y - sy];
2426 				if ( loc && !( loc->x == shapeX && loc->y == shapeY ) ) {
2427 					return loc;
2428 				}
2429 			}
2430 		}
2431 	}
2432 
2433 	if ( newz )
2434 		*newz = zz;
2435 	return NULL;
2436 }
2437 
2438 /** This one only returns if the shape originates at xyz. */
2439 
getPosition(Sint16 x,Sint16 y,Sint16 z)2440 Location *Map::getPosition( Sint16 x, Sint16 y, Sint16 z ) {
2441 	if ( pos[x][y][z] && ( ( pos[x][y][z]->shape && pos[x][y][z]->x == x && pos[x][y][z]->y == y && pos[x][y][z]->z == z ) ) )
2442 		return pos[x][y][z];
2443 	return NULL;
2444 }
2445 
2446 /// Stops a visual effect.
2447 
stopEffect(Sint16 x,Sint16 y,Sint16 z)2448 void Map::stopEffect( Sint16 x, Sint16 y, Sint16 z ) {
2449 	removeEffect( x, y, z );
2450 }
2451 
2452 /// Starts a visual effect.
2453 
startEffect(Sint16 x,Sint16 y,Sint16 z,int effect_type,GLuint duration,int width,int height,GLuint delay,bool forever,DisplayInfo * di)2454 void Map::startEffect( Sint16 x, Sint16 y, Sint16 z, int effect_type, GLuint duration, int width, int height, GLuint delay, bool forever, DisplayInfo *di ) {
2455 
2456 	if ( x >= MAP_WIDTH || y >= MAP_DEPTH || z >= MAP_VIEW_HEIGHT ) {
2457 		cerr << "*** STARTEFFECT out of bounds: pos=" << x << "," << y << "," << z << endl;
2458 		( ( RenderedCreature* )NULL )->getName();
2459 	}
2460 
2461 	// show an effect
2462 	if ( effect[x][y][z] ) return;
2463 
2464 	effect[x][y][z] = mapMemoryManager.newEffectLocation( this, preferences, shapes, width, height );
2465 	/*
2466 	effect[x][y][z]->effect = new Effect( preferences,
2467 	                                      shapes,
2468 	                                      width, height );
2469 	effect[x][y][z]->effect->deleteParticles();
2470 	*/
2471 	effect[x][y][z]->resetDamageEffect();
2472 	effect[x][y][z]->effectType = effect_type;
2473 	effect[x][y][z]->effectDuration = duration;
2474 	effect[x][y][z]->effectDelay = delay;
2475 	effect[x][y][z]->forever = forever;
2476 	effect[x][y][z]->x = x;
2477 	effect[x][y][z]->y = y;
2478 	effect[x][y][z]->z = z;
2479 	effect[x][y][z]->effect->setSize( width, height );
2480 	effect[x][y][z]->heightPos = findMaxHeightPos( x, y, z );
2481 	currentEffectsMap[ createTripletKey( x, y, z ) ] = effect[x][y][z];
2482 
2483 	if ( di )
2484 		effect[x][y][z]->effect->setDisplayInfo( di );
2485 
2486 	// need to do this to make sure effect shows up
2487 	resortShapes = mapChanged = true;
2488 }
2489 
2490 /// Removes the visual effect at x,y,z.
2491 
removeEffect(Sint16 x,Sint16 y,Sint16 z)2492 void Map::removeEffect( Sint16 x, Sint16 y, Sint16 z ) {
2493 
2494 	if ( x >= MAP_WIDTH || y >= MAP_DEPTH || z >= MAP_VIEW_HEIGHT ) {
2495 		cerr << "*** REMOVEEFFECT out of bounds: pos=" << x << "," << y << "," << z << endl;
2496 		( ( RenderedCreature* )NULL )->getName();
2497 	}
2498 
2499 	if ( effect[x][y][z] ) {
2500 		mapMemoryManager.deleteEffectLocation( effect[x][y][z] );
2501 		effect[x][y][z] = NULL;
2502 	}
2503 }
2504 
2505 /// Removes all special visual effects (spells, etc.)
2506 
removeAllEffects()2507 void Map::removeAllEffects() {
2508 	for ( int x = 0; x < MAP_WIDTH; x++ ) {
2509 		for ( int y = 0; y < MAP_DEPTH; y++ ) {
2510 			for ( int z = 0; z < MAP_VIEW_HEIGHT; z++ ) {
2511 				if ( effect[x][y][z] ) {
2512 					mapMemoryManager.deleteEffectLocation( effect[x][y][z] );
2513 					effect[x][y][z] = NULL;
2514 				}
2515 			}
2516 		}
2517 	}
2518 	currentEffectsMap.clear();
2519 }
2520 
findMaxHeightPos(float x,float y,float z,bool findMax)2521 float Map::findMaxHeightPos( float x, float y, float z, bool findMax ) {
2522 	float pos = 0;
2523 	float xp = ( x ) / OUTDOORS_STEP;
2524 	float yp = ( y ) / OUTDOORS_STEP;
2525 
2526 	int xx = toint( xp );
2527 	int yy = toint( yp );
2528 
2529 	debugHeightPosXX[0] = xx;
2530 	debugHeightPosYY[0] = yy;
2531 
2532 	debugHeightPosXX[1] = xx;
2533 	debugHeightPosYY[1] = yy - 1;
2534 
2535 	debugHeightPosXX[2] = xx + 1;
2536 	debugHeightPosYY[2] = yy - 1;
2537 
2538 	debugHeightPosXX[3] = xx + 1;
2539 	debugHeightPosYY[3] = yy;
2540 
2541 	float zz = 0;
2542 	if ( findMax ) {
2543 		// find the max
2544 		for ( int i = 0; i < 4; i++ ) {
2545 			if ( zz < ground[ debugHeightPosXX[ i ] ][ debugHeightPosYY[ i ] ] ) {
2546 				zz = ground[ debugHeightPosXX[ i ] ][ debugHeightPosYY[ i ] ];
2547 			}
2548 		}
2549 	} else {
2550 		// find the average
2551 		int count = 0;
2552 		for ( int i = 0; i < 4; i++ ) {
2553 			// skip 'lake' heights
2554 			if ( ground[ debugHeightPosXX[ i ] ][ debugHeightPosYY[ i ] ] > 0 ) {
2555 				zz += ground[ debugHeightPosXX[ i ] ][ debugHeightPosYY[ i ] ];
2556 				count++;
2557 			}
2558 		}
2559 		if ( zz > 0 && count > 0 ) zz /= static_cast<float>( count );
2560 	}
2561 	if ( z < zz ) pos = zz;
2562 	return pos;
2563 }
2564 
2565 /// Returns true if any of the tiles in the specified area is textured.
2566 
hasOutdoorTexture(int x,int y,int width,int height)2567 bool Map::hasOutdoorTexture( int x, int y, int width, int height ) {
2568 	for ( int xx = x; xx < x + width; xx++ ) {
2569 		for ( int yy = y - height - 1; yy <= y; yy++ ) {
2570 			int tx = x / OUTDOORS_STEP;
2571 			int ty = y / OUTDOORS_STEP;
2572 			for ( int z = 0; z < MAX_OUTDOOR_LAYER; z++ ) {
2573 				if ( outdoorTex[tx][ty][z].outdoorThemeRef != -1 ) return true;
2574 			}
2575 		}
2576 	}
2577 	return false;
2578 }
2579 
2580 /// Sets the outdoor ground texture for a given map position (detailed version).
2581 
setOutdoorTexture(int x,int y,float offsetX,float offsetY,int ref,float angle,bool horizFlip,bool vertFlip,int z)2582 void Map::setOutdoorTexture( int x, int y, float offsetX, float offsetY, int ref,
2583                              float angle, bool horizFlip, bool vertFlip, int z ) {
2584 	int faceCount = getShapes()->getCurrentTheme()->getOutdoorFaceCount( ref );
2585 	if ( faceCount == 0 ) {
2586 		//cerr << "Map Error: no textures for outdoor theme! ref=" << WallTheme::outdoorThemeRefName[ref] << endl;
2587 		return;
2588 	}
2589 	Texture* textureGroup = getShapes()->getCurrentTheme()->getOutdoorTextureGroup( ref );
2590 	int width = getShapes()->getCurrentTheme()->getOutdoorTextureWidth( ref );
2591 	int height = getShapes()->getCurrentTheme()->getOutdoorTextureHeight( ref );
2592 
2593 	int tx = x / OUTDOORS_STEP;
2594 	int ty = ( y - height - 1 ) / OUTDOORS_STEP;
2595 	int tw = static_cast<int>( width / OUTDOORS_STEP );
2596 	int th = static_cast<int>( height / OUTDOORS_STEP );
2597 	outdoorTex[tx][ty][z].offsetX = offsetX;
2598 	outdoorTex[tx][ty][z].offsetY = offsetY;
2599 	outdoorTex[tx][ty][z].angle = angle;
2600 	outdoorTex[tx][ty][z].horizFlip = horizFlip;
2601 	outdoorTex[tx][ty][z].vertFlip = vertFlip;
2602 	outdoorTex[tx][ty][z].outdoorThemeRef = ref;
2603 
2604 	// computed values
2605 	outdoorTex[tx][ty][z].width = tw;
2606 	outdoorTex[tx][ty][z].height = th;
2607 	outdoorTex[tx][ty][z].texture = textureGroup[ Util::dice( faceCount ) ];
2608 	mapChanged = true;
2609 }
2610 
2611 /// Deletes the outdoor ground texture at the specified coordinates.
2612 
removeOutdoorTexture(int x,int y,float width,float height,int z)2613 void Map::removeOutdoorTexture( int x, int y, float width, float height, int z ) {
2614 	int tx = x / OUTDOORS_STEP;
2615 	int ty = ( y - height - 1 ) / OUTDOORS_STEP;
2616 	outdoorTex[tx][ty][z].outdoorThemeRef = -1;
2617 	outdoorTex[tx][ty][z].texture.clear();
2618 	mapChanged = true;
2619 }
2620 
2621 
setPositionInner(Sint16 x,Sint16 y,Sint16 z,Shape * shape,RenderedItem * item,RenderedCreature * creature)2622 void Map::setPositionInner( Sint16 x, Sint16 y, Sint16 z,
2623                             Shape *shape,
2624                             RenderedItem *item,
2625                             RenderedCreature *creature ) {
2626 	if ( x < 0 || y < 0 || z < 0 ||
2627 	        x >= MAP_WIDTH || y >= MAP_DEPTH || z >= MAP_VIEW_HEIGHT ) {
2628 		cerr << "*** Error can't set position outside bounds:" << x << "," << y << "," << z << endl;
2629 		return;
2630 	}
2631 
2632 	resortShapes = mapChanged = true;
2633 
2634 	bool isNonBlockingItem = ( item && !item->isBlocking() && !z && settings->isItemPosEnabled() );
2635 	Location *p = ( isNonBlockingItem ? itemPos[ x ][ y ] : pos[ x ][ y ][ z ] );
2636 	if ( !p ) {
2637 		p = mapMemoryManager.newLocation();
2638 	}
2639 
2640 	p->shape = shape;
2641 	p->item = item;
2642 	p->creature = creature;
2643 	p->heightPos = findMaxHeightPos( x, y, z );
2644 	p->x = x;
2645 	p->y = y;
2646 	p->z = z;
2647 	p->outlineColor = NULL;
2648 	p->texIndex = -1;
2649 	if ( p->shape->getTextureCount() > 3 ) {
2650 		// pick one of the texture groups (3 textures + variants)
2651 		int n = Util::dice( p->shape->getTextureCount() - 2 );
2652 		// -1 means, use the correct default texture (correct for the side of the glShape)
2653 		p->texIndex = ( n == 0 ? -1 : n + 2 );
2654 	}
2655 
2656 	for ( int xp = 0; xp < shape->getWidth(); xp++ ) {
2657 		for ( int yp = 0; yp < shape->getDepth(); yp++ ) {
2658 			if ( !shape->isInside( xp, yp ) ) continue;
2659 			for ( int zp = 0; zp < shape->getHeight(); zp++ ) {
2660 
2661 				// I _hate_ c++... moving secret doors up causes array roll-over problems.
2662 				if ( x + xp < 0 || y - yp < 0 || z + zp < 0 || x + xp >= MAP_WIDTH || y - yp >= MAP_DEPTH || z + zp >= MAP_VIEW_HEIGHT )
2663 					break;
2664 
2665 				// Either the same old pos reused or nothing there.
2666 				// If these are not true, we're leaking memory.
2667 				//assert( pos[x + xp][y - yp][z + zp] == p ||
2668 				//!( pos[x + xp][y - yp][z + zp] ) );
2669 
2670 				if ( zp && pos[x + xp][y - yp][z + zp] && pos[x + xp][y - yp][z + zp] != p ) {
2671 					cerr << "error setting position:" << " x=" << ( x + xp ) << " y=" << ( y - yp ) << " z=" << ( z + zp ) <<
2672 					" shape=" << p->shape->getName() << endl;
2673 				} else {
2674 					if ( isNonBlockingItem )
2675 						itemPos[x + xp][y - yp] = p;
2676 					else
2677 						pos[x + xp][y - yp][z + zp] = p;
2678 				}
2679 			}
2680 		}
2681 	}
2682 
2683 	// remember gates and teleporters
2684 	if ( shape == shapes->findShapeByName( "GATE_UP" ) ||
2685 	        shape == shapes->findShapeByName( "GATE_DOWN" ) ||
2686 	        shape == shapes->findShapeByName( "GATE_DOWN_OUTDOORS" ) ) {
2687 		gates.insert( p );
2688 	} else if ( shape == shapes->findShapeByName( "TELEPORTER" ) ) {
2689 		teleporters.insert( p );
2690 	}
2691 }
2692 
2693 /// Places a shape at a x,y,z position (extended version).
2694 
setPosition(Sint16 x,Sint16 y,Sint16 z,Shape * shape,DisplayInfo * di)2695 void Map::setPosition( Sint16 x, Sint16 y, Sint16 z, Shape *shape, DisplayInfo *di ) {
2696 	GLShape* gls = dynamic_cast<GLShape*>( shape );
2697 	if ( gls ) {
2698 
2699 		if ( gls->hasVirtualShapes() ) {
2700 //   cerr << "Adding virtual shapes for: " << shape->getName() << endl;
2701 			for ( unsigned int n = 0; n < gls->getVirtualShapes()->size(); n++ ) {
2702 				VirtualShape *vs = ( VirtualShape* )( gls->getVirtualShapes()->at( n ) );
2703 //    cerr << "virtual shape: offset=" << vs->getOffsetX() << "," << vs->getOffsetY() << "," << vs->getOffsetZ() << " dim=" <<
2704 //     vs->getWidth() << "," << vs->getDepth() << "," << vs->getHeight() << endl;
2705 				setPositionInner( x + vs->getOffsetX(), y + vs->getOffsetY(), z + vs->getOffsetZ(), vs, NULL, NULL );
2706 			}
2707 		} else {
2708 			setPositionInner( x, y, z, shape, NULL, NULL );
2709 
2710 			if ( gls->getEffectType() > -1 ) {
2711 
2712 				int ex = x + gls->getEffectX();
2713 				int ey = y - shape->getDepth() - gls->getEffectY();
2714 				int ez = z + gls->getEffectZ();
2715 
2716 				if ( !effect[ex][ey][ez] ) {
2717 					startEffect( ex, ey, ez, gls->getEffectType(), 0, gls->getEffectWidth(), gls->getEffectDepth(), 0, true, di );
2718 				}
2719 			}
2720 		}
2721 
2722 		// squirrel trimmings
2723 		adapter->shapeAdded( shape->getName(), x, y, z );
2724 	}
2725 }
2726 
2727 /// Deletes the shape at the specified map position.
2728 
removePosition(Sint16 x,Sint16 y,Sint16 z)2729 Shape *Map::removePosition( Sint16 x, Sint16 y, Sint16 z ) {
2730 	Shape *shape = NULL;
2731 	if ( pos[x][y][z] && pos[x][y][z]->shape && pos[x][y][z]->x == x && pos[x][y][z]->y == y && pos[x][y][z]->z == z ) {
2732 		resortShapes = mapChanged = true;
2733 		shape = pos[x][y][z]->shape;
2734 		if ( ( ( GLShape* )shape )->getEffectType() > -1 ) {
2735 			int ex = x + ( ( GLShape* )shape )->getEffectX();
2736 			int ey = y - shape->getDepth() - ( ( GLShape* )shape )->getEffectY();
2737 			int ez = z + ( ( GLShape* )shape )->getEffectZ();
2738 			removeEffect( ex, ey, ez );
2739 		}
2740 
2741 		Location *p = pos[ x ][ y ][ z ];
2742 
2743 		for ( int xp = 0; xp < shape->getWidth(); xp++ ) {
2744 			for ( int yp = 0; yp < shape->getDepth(); yp++ ) {
2745 				if ( !shape->isInside( xp, yp ) ) continue;
2746 				for ( int zp = 0; zp < shape->getHeight(); zp++ ) {
2747 
2748 					// I _hate_ c++... moving secret doors up causes array roll-over problems.
2749 					if ( x + xp < 0 || y - yp < 0 || z + zp < 0 || x + xp >= MAP_WIDTH || y - yp >= MAP_DEPTH || z + zp >= MAP_VIEW_HEIGHT )
2750 						break;
2751 
2752 					// Assert we're dealing with the right shape
2753 					//assert( pos[ x + xp ][ y - yp ][ z + zp ] == p );
2754 					if ( pos[ x + xp ][ y - yp ][ z + zp ] != p ) {
2755 						cerr << "Error removing position:" << " x=" << ( x + xp ) << " y=" << ( y - yp ) << " z=" << ( z + zp ) << endl;
2756 					} else {
2757 						pos[ x + xp ][ y - yp ][ z + zp ] = NULL;
2758 					}
2759 				}
2760 			}
2761 		}
2762 
2763 		// Actually free the shape
2764 		mapMemoryManager.deleteLocation( p );
2765 
2766 		// forget gates and teleporters
2767 		if ( shape == shapes->findShapeByName( "GATE_UP" ) ||
2768 		        shape == shapes->findShapeByName( "GATE_DOWN" ) ||
2769 		        shape == shapes->findShapeByName( "GATE_DOWN_OUTDOORS" ) ) {
2770 			gates.erase( p );
2771 		} else if ( shape == shapes->findShapeByName( "TELEPORTER" ) ) {
2772 			teleporters.erase( p );
2773 		}
2774 	}
2775 	return shape;
2776 }
2777 
2778 /// Deletes an item at x,y.
2779 
removeItemPosition(Sint16 x,Sint16 y)2780 Shape *Map::removeItemPosition( Sint16 x, Sint16 y ) {
2781 	Shape *shape = NULL;
2782 	if ( itemPos[x][y] && itemPos[x][y]->shape && itemPos[x][y]->x == x && itemPos[x][y]->y == y ) {
2783 		resortShapes = mapChanged = true;
2784 		shape = itemPos[x][y]->shape;
2785 		if ( ( ( GLShape* )shape )->getEffectType() > -1 ) {
2786 			int ex = x + ( ( GLShape* )shape )->getEffectX();
2787 			int ey = y - shape->getDepth() - ( ( GLShape* )shape )->getEffectY();
2788 			int ez = ( ( GLShape* )shape )->getEffectZ();
2789 			removeEffect( ex, ey, ez );
2790 		}
2791 
2792 		Location *p = itemPos[ x ][ y ];
2793 
2794 		for ( int xp = 0; xp < shape->getWidth(); xp++ ) {
2795 			for ( int yp = 0; yp < shape->getDepth(); yp++ ) {
2796 				if ( !shape->isInside( xp, yp ) ) continue;
2797 				// I _hate_ c++... moving secret doors up causes array roll-over problems.
2798 				if ( x + xp < 0 || y - yp < 0 || x + xp >= MAP_WIDTH || y - yp >= MAP_DEPTH )
2799 					break;
2800 
2801 				// Assert we're dealing with the right shape
2802 				//assert( pos[ x + xp ][ y - yp ][ z + zp ] == p );
2803 				if ( itemPos[ x + xp ][ y - yp ] != p ) {
2804 					cerr << "Error removing item position:" << " x=" << ( x + xp ) << " y=" << ( y - yp ) << endl;
2805 				} else {
2806 					itemPos[ x + xp ][ y - yp ] = NULL;
2807 				}
2808 			}
2809 		}
2810 
2811 		// Actually free the shape
2812 		mapMemoryManager.deleteLocation( p );
2813 	}
2814 	return shape;
2815 }
2816 
2817 /// Deletes the shape at x,y,z.
2818 
removeLocation(Sint16 x,Sint16 y,Sint16 z)2819 Shape *Map::removeLocation( Sint16 x, Sint16 y, Sint16 z ) {
2820 	if ( pos[x][y][z] && pos[x][y][z]->shape )
2821 		return removePosition( pos[x][y][z]->x, pos[x][y][z]->y, pos[x][y][z]->z );
2822 	else return NULL;
2823 }
2824 
2825 /// Places an item at x,y,z.
2826 
setItem(Sint16 x,Sint16 y,Sint16 z,RenderedItem * item)2827 void Map::setItem( Sint16 x, Sint16 y, Sint16 z, RenderedItem *item ) {
2828 	if ( item && item->getShape() ) {
2829 		setPositionInner( x, y, z, item->getShape(), item, NULL );
2830 	}
2831 }
2832 
2833 /// Deletes the item at x,y,z.
2834 
removeItem(Sint16 x,Sint16 y,Sint16 z)2835 RenderedItem *Map::removeItem( Sint16 x, Sint16 y, Sint16 z ) {
2836 	RenderedItem *item = NULL;
2837 	if ( !z && itemPos[x][y] && itemPos[x][y]->item ) {
2838 		item = itemPos[x][y]->item;
2839 		removeItemPosition( x, y );
2840 	} else {
2841 		item = ( pos[x][y][z] ? pos[x][y][z]->item : NULL );
2842 		removePosition( x, y, z );
2843 	}
2844 	return item;
2845 }
2846 
2847 /// Drops all items above the specified item.
2848 
dropItemsAbove(int x,int y,int z,RenderedItem * item)2849 void Map::dropItemsAbove( int x, int y, int z, RenderedItem *item ) {
2850 	int count = 0;
2851 	Location drop[100];
2852 	for ( int tx = 0; tx < item->getShape()->getWidth(); tx++ ) {
2853 		for ( int ty = 0; ty < item->getShape()->getDepth(); ty++ ) {
2854 			if ( !item->getShape()->isInside( tx, ty ) ) continue;
2855 			for ( int tz = z + item->getShape()->getHeight(); tz < MAP_VIEW_HEIGHT; tz++ ) {
2856 				Location *loc2 = pos[x + tx][y - ty][tz];
2857 				if ( loc2 && loc2->item ) {
2858 					drop[count].x = loc2->x;
2859 					drop[count].y = loc2->y;
2860 					drop[count].z = loc2->z - item->getShape()->getHeight();
2861 					drop[count].item = loc2->item;
2862 					count++;
2863 					removeItem( loc2->x, loc2->y, loc2->z );
2864 					tz += drop[count - 1].item->getShape()->getHeight() - 1;
2865 				}
2866 			}
2867 		}
2868 	}
2869 	for ( int i = 0; i < count; i++ ) {
2870 		//cerr << "item " << drop[i].item->getItemName() << " new z=" << drop[i].z << endl;
2871 		setItem( drop[i].x, drop[i].y, drop[i].z, drop[i].item );
2872 	}
2873 }
2874 
2875 /// Makes the specified creature appear at x,y,z.
2876 
setCreature(Sint16 x,Sint16 y,Sint16 z,RenderedCreature * creature)2877 void Map::setCreature( Sint16 x, Sint16 y, Sint16 z, RenderedCreature *creature ) {
2878 	if ( creature && creature->getShape() ) {
2879 		if ( helper && creature->isPartyMember() ) helper->visit( creature );
2880 
2881 		// pick up any objects in the way
2882 		for ( int xp = 0; xp < creature->getShape()->getWidth(); xp++ ) {
2883 			for ( int yp = 0; yp < creature->getShape()->getDepth(); yp++ ) {
2884 				if ( !creature->getShape()->isInside( xp, yp ) ) continue;
2885 				for ( int zp = 0; zp < creature->getShape()->getHeight(); zp++ ) {
2886 					if ( pos[x + xp][y - yp][z + zp] && pos[x + xp][y - yp][z + zp]->item ) {
2887 						// creature picks up non-blocking item (this is the only way to handle
2888 						// non-blocking items. It's also very 'roguelike'.
2889 						RenderedItem *item = pos[x + xp][y - yp][z + zp]->item;
2890 						removeItem( pos[x + xp][y - yp][z + zp]->x, pos[x + xp][y - yp][z + zp]->y, pos[x + xp][y - yp][z + zp]->z );
2891 						creature->pickUpOnMap( item );
2892 						char message[120];
2893 						snprintf( message, 120, _( "%s picks up %s." ), creature->getName(), item->getItemName() );
2894 						adapter->writeLogMessage( message );
2895 					}
2896 				}
2897 			}
2898 		}
2899 
2900 		setPositionInner( x, y, z, creature->getShape(), NULL, creature );
2901 	}
2902 }
2903 
2904 /// Moves a creature instantly to a new position. Picks up any items at the new position.
2905 
moveCreaturePos(Sint16 nx,Sint16 ny,Sint16 nz,Sint16 ox,Sint16 oy,Sint16 oz,RenderedCreature * creature)2906 void Map::moveCreaturePos( Sint16 nx, Sint16 ny, Sint16 nz, Sint16 ox, Sint16 oy, Sint16 oz, RenderedCreature *creature ) {
2907 	Location *p = pos[ox][oy][oz];
2908 	if ( creature && creature->getShape() && p && p->creature && p->x == ox && p->y == oy && p->z == oz ) {
2909 
2910 		// remove the old pos
2911 		Location *tmp[MAP_UNIT][MAP_UNIT][MAP_UNIT];
2912 		for ( int xp = 0; xp < creature->getShape()->getWidth(); xp++ ) {
2913 			for ( int yp = 0; yp < creature->getShape()->getDepth(); yp++ ) {
2914 				if ( !creature->getShape()->isInside( xp, yp ) ) continue;
2915 				for ( int zp = 0; zp < creature->getShape()->getHeight(); zp++ ) {
2916 					int oldX = ox + xp;
2917 					int oldY = oy - yp;
2918 					int oldZ = oz + zp;
2919 					tmp[xp][yp][zp] = pos[oldX][oldY][oldZ];
2920 					pos[oldX][oldY][oldZ] = NULL;
2921 					if ( !( tmp[xp][yp][zp] ) )
2922 						cerr << "*** tmp is null!" << endl;
2923 					else
2924 						tmp[xp][yp][zp]->outlineColor = NULL;
2925 				}
2926 			}
2927 		}
2928 
2929 		if ( helper && creature->isPartyMember() ) helper->visit( creature );
2930 
2931 		// pick up any items in the way
2932 		for ( int xp = 0; xp < creature->getShape()->getWidth(); xp++ ) {
2933 			for ( int yp = 0; yp < creature->getShape()->getDepth(); yp++ ) {
2934 				if ( !creature->getShape()->isInside( xp, yp ) ) continue;
2935 				for ( int zp = 0; zp < creature->getShape()->getHeight(); zp++ ) {
2936 					int newX = nx + xp;
2937 					int newY = ny - yp;
2938 					int newZ = nz + zp;
2939 
2940 					if ( pos[newX][newY][newZ] ) {
2941 						if ( pos[newX][newY][newZ]->item ) {
2942 							// creature picks up non-blocking item (this is the only way to handle
2943 							// non-blocking items. It's also very 'roguelike'.)
2944 							RenderedItem *item = pos[newX][newY][newZ]->item;
2945 							removeItem( pos[newX][newY][newZ]->x, pos[newX][newY][newZ]->y, pos[newX][newY][newZ]->z );
2946 							creature->pickUpOnMap( item );
2947 							char message[120];
2948 							snprintf( message, 120, _( "%s picks up %s." ), creature->getName(), item->getItemName() );
2949 							adapter->writeLogMessage( message );
2950 						} else {
2951 							cerr << "*** Error: when moving " << creature->getName() << " path contained a non-item position." << endl;
2952 						}
2953 					}
2954 				}
2955 			}
2956 		}
2957 
2958 		// insert the new pos
2959 		for ( int xp = 0; xp < creature->getShape()->getWidth(); xp++ ) {
2960 			for ( int yp = 0; yp < creature->getShape()->getDepth(); yp++ ) {
2961 				if ( !creature->getShape()->isInside( xp, yp ) ) continue;
2962 				for ( int zp = 0; zp < creature->getShape()->getHeight(); zp++ ) {
2963 					int newX = nx + xp;
2964 					int newY = ny - yp;
2965 					int newZ = nz + zp;
2966 
2967 					// copy
2968 					pos[newX][newY][newZ] = tmp[xp][yp][zp];
2969 					pos[newX][newY][newZ]->item = NULL;
2970 					pos[newX][newY][newZ]->shape = creature->getShape();
2971 					pos[newX][newY][newZ]->creature = creature;
2972 					pos[newX][newY][newZ]->x = nx;
2973 					pos[newX][newY][newZ]->y = ny;
2974 					pos[newX][newY][newZ]->z = nz;
2975 					pos[newX][newY][newZ]->outlineColor = NULL;
2976 				}
2977 			}
2978 		}
2979 
2980 		// instead of repainting the entire map, just update this creature's rendering info
2981 		// resortShapes = mapChanged = true;
2982 		DrawLater *later = creatureMap[creature];
2983 		if ( later ) {
2984 			if ( later->creature != creature ) {
2985 				cerr << "*** Error: creatureMap is damaged!!! creature=" << creature->getName() << endl;
2986 				cerr << "\tlocation: shape=" << ( later->shape ? later->shape->getName() : "null" ) <<
2987 				" item=" << ( later->item ? later->item->getItemName() : "null" ) <<
2988 				" creature=" << ( later->creature ? later->creature->getName() : "null" ) <<
2989 				endl;
2990 				//creatureMap.clear();
2991 				resortShapes = mapChanged = true;
2992 			} else {
2993 				//cerr << "*** adjusting rendering info for " << creature->getName() << endl;
2994 				// resort shapes if the player moved
2995 				if ( adapter->getPlayer() == creature ) {
2996 					checkUnderRoof();
2997 					//cerr << "*** player moved" << endl;
2998 					resortShapes = true;
2999 				}
3000 
3001 				int chunkOffsetX, chunkOffsetY;
3002 				int chunkStartX, chunkStartY;
3003 				int chunkEndX, chunkEndY;
3004 				calculateChunkInfo( &chunkOffsetX, &chunkOffsetY, &chunkStartX, &chunkStartY, &chunkEndX, &chunkEndY );
3005 
3006 				Location *location = pos[nx][ny][nz];
3007 				int posX, posY, posZ;
3008 				bool lightEdge;
3009 				float xpos2, ypos2, zpos2;
3010 				int chunkX, chunkY;
3011 				calculateLocationInfo( location, chunkStartX, chunkStartY, chunkOffsetX, chunkOffsetY, 0,
3012 				                       &posX, &posY, &posZ, &xpos2, &ypos2, &zpos2,
3013 				                       &chunkX, &chunkY, &lightEdge );
3014 
3015 				location->heightPos = findMaxHeightPos( location->creature->getX(), location->creature->getY(), location->creature->getZ() );
3016 				later->xpos = xpos2;
3017 				later->ypos = ypos2;
3018 				later->zpos = zpos2;
3019 				//later->pos = location;
3020 				//later->inFront = false;
3021 				later->x = posX;
3022 				later->y = posY;
3023 
3024 				// also move the creature's effect
3025 				DrawLater *effect = creatureEffectMap[creature];
3026 				if ( effect ) {
3027 					effect->xpos = xpos2;
3028 					effect->ypos = ypos2;
3029 					effect->zpos = zpos2;
3030 					effect->x = posX;
3031 					effect->y = posY;
3032 				}
3033 			}
3034 		} else if ( helper && helper->isVisible( toint( creature->getX() ), toint( creature->getY() ), creature->getShape() ) ) {
3035 //			cerr << "!!! 1" << creature->getName() << " " << SDL_GetTicks() << " pos=" <<
3036 //				toint( creature->getX() ) << "," << toint( creature->getY() ) << "," << toint( creature->getZ() ) << endl;
3037 			Location *pos = getLocation( (int)( creature->getX() ), (int)( creature->getY() ), (int)( creature->getZ() ) );
3038 			if ( pos ) {
3039 //				cerr << "!!! 2" << creature->getName() << " " << SDL_GetTicks() << endl;
3040 				int chunkX, chunkY;
3041 				getChunk( pos->x, pos->y, &chunkX, &chunkY );
3042 				if ( checkLightMap( chunkX, chunkY ) ) {
3043 					// If this creature has no creatureMap entry but should now be visible,
3044 					// it means the creature wondered into the visible area: repaint everything.
3045 					// An optimization here would be to restrict setupShapes to the creatures that changed.
3046 					resortShapes = mapChanged = true;
3047 //					cerr << "!!! 3" << creature->getName() << " " << SDL_GetTicks() << endl;
3048 				}
3049 			}
3050 		}
3051 	}
3052 }
3053 
getChunk(int mapX,int mapY,int * chunkX,int * chunkY)3054 void Map::getChunk( int mapX, int mapY, int *chunkX, int *chunkY ) {
3055 	*chunkX = ( mapX - MAP_OFFSET ) / MAP_UNIT;
3056 	*chunkY = ( mapY - 1 - MAP_OFFSET ) / MAP_UNIT;
3057 }
3058 
3059 /// Sets up map location info.
3060 
calculateLocationInfo(Location * location,int chunkStartX,int chunkStartY,int chunkOffsetX,int chunkOffsetY,Uint16 drawSide,int * posX,int * posY,int * posZ,float * xpos,float * ypos,float * zpos,int * chunkX,int * chunkY,bool * lightEdge)3061 void Map::calculateLocationInfo( Location *location,
3062                                  int chunkStartX, int chunkStartY,
3063                                  int chunkOffsetX, int chunkOffsetY,
3064                                  Uint16 drawSide,
3065                                  int *posX, int *posY, int *posZ,
3066                                  float *xpos, float *ypos, float *zpos,
3067                                  int *chunkX, int *chunkY,
3068                                  bool *lightEdge ) {
3069 	*posX = location->x;
3070 	*posY = location->y;
3071 	*posZ = location->z;
3072 	getChunk( location->x, location->y, &( *chunkX ), &( *chunkY ) );
3073 	//*chunkX = ( *posX - MAP_OFFSET ) / MAP_UNIT;
3074 	//*chunkY = ( *posY - 1 - MAP_OFFSET ) / MAP_UNIT;
3075 	int xp = ( *posX - MAP_OFFSET ) % MAP_UNIT;
3076 	int yp = ( *posY - 1 - MAP_OFFSET ) % MAP_UNIT;
3077 	int zp = *posZ;
3078 
3079 	// is this shape visible on the edge an chunk in darkness?
3080 	*lightEdge =
3081 	  ( !checkLightMap( *chunkX, *chunkY ) && location->shape && !location->creature &&
3082 	    ( ( drawSide & Constants::MOVE_DOWN && yp >= MAP_UNIT - MAP_UNIT_OFFSET && location->shape->getDepth() <= MAP_UNIT_OFFSET ) ||
3083 	      ( drawSide & Constants::MOVE_UP && yp <= MAP_UNIT_OFFSET && location->shape->getDepth() <= MAP_UNIT_OFFSET ) ||
3084 	      ( drawSide & Constants::MOVE_LEFT && xp < MAP_UNIT_OFFSET && location->shape->getWidth() <= MAP_UNIT_OFFSET ) ||
3085 	      ( drawSide & Constants::MOVE_RIGHT && xp >= MAP_UNIT - MAP_UNIT_OFFSET && location->shape->getWidth() <= MAP_UNIT_OFFSET ) )
3086 	  );
3087 
3088 	*xpos = static_cast<float>( ( *chunkX - chunkStartX ) * MAP_UNIT + xp + chunkOffsetX ) * MUL;
3089 	*ypos = static_cast<float>( ( *chunkY - chunkStartY ) * MAP_UNIT + yp - location->shape->getDepth() + chunkOffsetY ) * MUL;
3090 	*zpos = static_cast<float>( zp ) * MUL;
3091 }
3092 
3093 /// Sets up chunk info.
3094 
calculateChunkInfo(int * chunkOffsetX,int * chunkOffsetY,int * chunkStartX,int * chunkStartY,int * chunkEndX,int * chunkEndY)3095 void Map::calculateChunkInfo( int *chunkOffsetX, int *chunkOffsetY,
3096                               int *chunkStartX, int *chunkStartY,
3097                               int *chunkEndX, int *chunkEndY ) {
3098 	*chunkOffsetX = 0;
3099 	*chunkStartX = ( getX() - MAP_OFFSET ) / MAP_UNIT;
3100 	int mod = ( getX() - MAP_OFFSET ) % MAP_UNIT;
3101 	if ( mod ) {
3102 		*chunkOffsetX = -mod;
3103 	}
3104 	*chunkEndX = mapViewWidth / MAP_UNIT + *chunkStartX;
3105 
3106 	*chunkOffsetY = 0;
3107 	*chunkStartY = ( getY() - MAP_OFFSET ) / MAP_UNIT;
3108 	mod = ( getY() - MAP_OFFSET ) % MAP_UNIT;
3109 	if ( mod ) {
3110 		*chunkOffsetY = -mod;
3111 	}
3112 	*chunkEndY = mapViewDepth / MAP_UNIT + *chunkStartY;
3113 }
3114 
3115 /// Removes a creature from the map.
3116 
removeCreature(Sint16 x,Sint16 y,Sint16 z)3117 RenderedCreature *Map::removeCreature( Sint16 x, Sint16 y, Sint16 z ) {
3118 	RenderedCreature *creature = ( pos[x][y][z] ? pos[x][y][z]->creature : NULL );
3119 	removePosition( x, y, z );
3120 	return creature;
3121 }
3122 
3123 /// Is there a wall between the two given shapes?
3124 
3125 // FIXME: only uses x,y for now
3126 // return false if there is any hole in the walls
isWallBetweenShapes(int x1,int y1,int z1,Shape * shape1,int x2,int y2,int z2,Shape * shape2)3127 bool Map::isWallBetweenShapes( int x1, int y1, int z1, Shape *shape1, int x2, int y2, int z2, Shape *shape2 ) {
3128 	for ( int x = x1; x < x1 + shape1->getWidth(); x++ ) {
3129 		for ( int y = y1; y < y1 + shape1->getDepth(); y++ ) {
3130 			for ( int xx = x2; xx < x2 + shape2->getWidth(); xx++ ) {
3131 				for ( int yy = y2; yy < y2 + shape2->getDepth(); yy++ ) {
3132 					Shape *shape = isWallBetween( x, y, z1, xx, yy, z2 );
3133 					if ( !shape || shape == shape2 )
3134 						return false;
3135 				}
3136 			}
3137 		}
3138 	}
3139 	return true;
3140 }
3141 
3142 /// Is there a wall between the two given positions?
3143 
3144 // FIXME: only uses x,y for now
isWallBetween(int x1,int y1,int z1,int x2,int y2,int z2)3145 Shape *Map::isWallBetween( int x1, int y1, int z1, int x2, int y2, int z2 ) {
3146 
3147 	if ( x1 == x2 && y1 == y2 )
3148 		return isWall( x1, y1, z1 );
3149 	if ( x1 == x2 ) {
3150 		if ( y1 > y2 )
3151 			SWAP( y1, y2 );
3152 		for ( int y = y1; y <= y2; y++ ) {
3153 			Shape *shape = isWall( x1, y, z1 );
3154 			if ( shape )
3155 				return shape;
3156 		}
3157 		return NULL;
3158 	}
3159 	if ( y1 == y2 ) {
3160 		if ( x1 > x2 ) SWAP( x1, x2 );
3161 		for ( int x = x1; x <= x2; x++ ) {
3162 			Shape *shape = isWall( x, y1, z1 );
3163 			if ( shape )
3164 				return shape;
3165 		}
3166 		return NULL;
3167 	}
3168 
3169 
3170 	//  fprintf(stderr, "Checking for wall: from: %d,%d to %d,%d\n", x1, y1, x2, y2);
3171 	Shape *shape = NULL;
3172 	bool yDiffBigger = ( abs( y2 - y1 ) > abs( x2 - x1 ) );
3173 	float m = static_cast<float>( y2 - y1 ) / static_cast<float>( x2 - x1 );
3174 	int steps = ( yDiffBigger ? abs( y2 - y1 ) : abs( x2 - x1 ) );
3175 	float x = x1;
3176 	float y = y1;
3177 	for ( int i = 0; i < steps; i++ ) {
3178 		Shape *ss = isWall( static_cast<int>( x ), static_cast<int>( y ), z1 );
3179 		if ( ss ) {
3180 			shape = ss;
3181 			break;
3182 		}
3183 		if ( yDiffBigger ) {
3184 			if ( y1 < y2 )
3185 				y += 1.0f;
3186 			else
3187 				y -= 1.0f;
3188 
3189 			if ( x1 < x2 )
3190 				x += 1.0f / abs( static_cast<int>( m ) );
3191 			else
3192 				x += -1.0f / abs( static_cast<int>( m ) );
3193 		} else {
3194 			if ( x1 < x2 )
3195 				x += 1.0f;
3196 			else
3197 				x -= 1.0f;
3198 
3199 			if ( y1 < y2 )
3200 				y += abs( static_cast<int>( m ) );
3201 			else
3202 				y += -1.0 * abs( static_cast<int>( m ) );
3203 		}
3204 	}
3205 	//  fprintf(stderr, "wall in between? %s\n", (ret ? "TRUE": "FALSE"));
3206 	return shape;
3207 }
3208 
3209 /// Is there a wall at the given position?
3210 
isWall(int x,int y,int z)3211 Shape *Map::isWall( int x, int y, int z ) {
3212 	Location *loc = getLocation( static_cast<int>( x ), static_cast<int>( y ), z );
3213 	return( loc && ( !loc->item || loc->item->getShape() != loc->shape ) &&
3214 	        ( !loc->creature || loc->creature->getShape() != loc->shape ) ? loc->shape : NULL );
3215 }
3216 
3217 /// Is there enough room for the shape at the specified position?
3218 
shapeFits(Shape * shape,int x,int y,int z)3219 bool Map::shapeFits( Shape *shape, int x, int y, int z ) {
3220 	for ( int tx = 0; tx < shape->getWidth(); tx++ ) {
3221 		for ( int ty = 0; ty < shape->getDepth(); ty++ ) {
3222 			if ( !shape->isInside( tx, ty ) ) continue;
3223 			for ( int tz = 0; tz < shape->getHeight(); tz++ ) {
3224 				if ( getLocation( x + tx, y - ty, z + tz ) ) {
3225 					return false;
3226 				}
3227 			}
3228 		}
3229 	}
3230 	return true;
3231 }
3232 
3233 /// Checks whether a shape can be placed at x,y,z (outdoors version).
3234 
shapeFitsOutdoors(GLShape * shape,int x,int y,int z)3235 bool Map::shapeFitsOutdoors( GLShape *shape, int x, int y, int z ) {
3236 	bool b = shapeFits( shape, x, y, z ) && !coversDoor( shape, x, y );
3237 	if ( b ) {
3238 		int h = getGroundHeight( x / OUTDOORS_STEP, y / OUTDOORS_STEP );
3239 		b = h >= -3 && h < 3;
3240 		if ( b ) {
3241 			int fx = ( ( x - MAP_OFFSET )  / MAP_UNIT ) * MAP_UNIT + MAP_OFFSET;
3242 			int fy = ( ( y - MAP_OFFSET )  / MAP_UNIT ) * MAP_UNIT + MAP_OFFSET + MAP_UNIT;
3243 			b = getFloorPosition( fx, fy ) == NULL;
3244 		}
3245 	}
3246 	return b;
3247 }
3248 
3249 /// Does this shape placed at x,y cover a door so it can't open?
3250 
coversDoor(Shape * shape,int x,int y)3251 bool Map::coversDoor( Shape *shape, int x, int y ) {
3252 	for ( int ty = y - shape->getDepth() - 6; ty < y + 6; ty++ ) {
3253 		for ( int tx = x - 6; tx < x + shape->getWidth() + 6; tx++ ) {
3254 			if ( isDoor( tx, ty ) ) return true;
3255 		}
3256 	}
3257 	return false;
3258 }
3259 
3260 /// Returns whether the shape would be blocked by something else when placed here.
3261 
3262 // FIXME: only uses x, y for now
getBlockingLocation(Shape * shape,int x,int y,int z)3263 Location *Map::getBlockingLocation( Shape *shape, int x, int y, int z ) {
3264 	for ( int tx = 0; tx < shape->getWidth(); tx++ ) {
3265 		for ( int ty = 0; ty < shape->getDepth(); ty++ ) {
3266 			if ( !shape->isInside( tx, ty ) ) continue;
3267 			Location *loc = getLocation( x + tx, y - ty, 0 );
3268 			if ( loc )
3269 				return loc;
3270 		}
3271 	}
3272 	return NULL;
3273 }
3274 
3275 /// If the shape can be dropped at x,y,z, return the location.
3276 
getDropLocation(Shape * shape,int x,int y,int z)3277 Location *Map::getDropLocation( Shape *shape, int x, int y, int z ) {
3278 	for ( int tx = 0; tx < shape->getWidth(); tx++ ) {
3279 		for ( int ty = 0; ty < shape->getDepth(); ty++ ) {
3280 			if ( !shape->isInside( tx, ty ) ) continue;
3281 			Location *loc = getLocation( x + tx, y - ty, z );
3282 			if ( loc ) {
3283 				return loc;
3284 			}
3285 		}
3286 	}
3287 	return NULL;
3288 }
3289 
3290 /// Sets up the light map.
3291 
3292 /// The light map is used indoors and determines which tiles on the level
3293 /// are currently visible or not (because sight is blocked by a door etc.)
3294 
configureLightMap()3295 void Map::configureLightMap() {
3296 	lightMapChanged = false;
3297 	groundVisible = false;
3298 
3299 	// draw nothing at first
3300 	for ( int x = 0; x < MAP_CHUNKS_X; x++ ) {
3301 		for ( int y = 0; y < MAP_CHUNKS_Y; y++ ) {
3302 			lightMap[x][y] = ( LIGHTMAP_ENABLED && settings->isLightMapEnabled() ? 0 : 1 );
3303 		}
3304 	}
3305 	if ( !( LIGHTMAP_ENABLED && settings->isLightMapEnabled() ) )
3306 		return;
3307 
3308 	int chunkX = ( toint( adapter->getPlayer()->getX() ) + ( adapter->getPlayer()->getShape()->getWidth() / 2 ) - MAP_OFFSET ) / MAP_UNIT;
3309 	int chunkY = ( toint( adapter->getPlayer()->getY() ) - ( adapter->getPlayer()->getShape()->getDepth() / 2 ) - MAP_OFFSET ) / MAP_UNIT;
3310 
3311 	traceLight( chunkX, chunkY, lightMap, false );
3312 }
3313 
3314 /// Can the given position currently be reached?
3315 
isPositionAccessible(int atX,int atY)3316 bool Map::isPositionAccessible( int atX, int atY ) {
3317 	// interpret the results: see if the target is "in light"
3318 	int chunkX = ( atX - MAP_OFFSET ) / MAP_UNIT;
3319 	int chunkY = ( atY - MAP_OFFSET ) / MAP_UNIT;
3320 	return ( accessMap[chunkX][chunkY] != 0 );
3321 }
3322 
3323 /// Configures the "access map" (it stores which map tiles are accessible).
3324 
configureAccessMap(int fromX,int fromY)3325 void Map::configureAccessMap( int fromX, int fromY ) {
3326 	// create the access map
3327 	for ( int x = 0; x < MAP_CHUNKS_X; x++ ) {
3328 		for ( int y = 0; y < MAP_CHUNKS_Y; y++ ) {
3329 			accessMap[x][y] = 0;
3330 		}
3331 	}
3332 	int chunkX = ( fromX - MAP_OFFSET ) / MAP_UNIT;
3333 	int chunkY = ( fromY - MAP_OFFSET ) / MAP_UNIT;
3334 	traceLight( chunkX, chunkY, accessMap, true );
3335 }
3336 
3337 /// Determines (in indoor areas) which parts of the map are currently visible.
3338 
traceLight(int chunkX,int chunkY,int lm[MAP_CHUNKS_X][MAP_CHUNKS_Y],bool onlyLockedDoors)3339 void Map::traceLight( int chunkX, int chunkY, int lm[MAP_CHUNKS_X][MAP_CHUNKS_Y], bool onlyLockedDoors ) {
3340 	if ( chunkX < 0 || chunkX >= MAP_CHUNKS_X || chunkY < 0 || chunkY >= MAP_CHUNKS_Y )
3341 		return;
3342 
3343 	// already visited?
3344 	if ( lm[chunkX][chunkY] )
3345 		return;
3346 
3347 	// let there be light
3348 	lm[chunkX][chunkY] = 1;
3349 
3350 	// if there is no roof here, enable the ground
3351 	if ( !getLocation( MAP_OFFSET + chunkX * MAP_UNIT + ( MAP_UNIT / 2 ),
3352 	                   MAP_OFFSET + chunkY * MAP_UNIT + ( MAP_UNIT / 2 ),
3353 	                   MAP_WALL_HEIGHT )  ) {
3354 		groundVisible = true;
3355 	}
3356 
3357 	// can we go N?
3358 	int x, y;
3359 	bool blocked = false;
3360 	x = chunkX * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 );
3361 	for ( y = chunkY * MAP_UNIT + MAP_OFFSET - ( MAP_UNIT / 2 ); y < chunkY * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 ); y++ ) {
3362 		if ( isLocationBlocked( x, y, 0, onlyLockedDoors ) ) {
3363 			blocked = true;
3364 			break;
3365 		}
3366 	}
3367 	if ( !blocked )
3368 		traceLight( chunkX, chunkY - 1, lm, onlyLockedDoors );
3369 
3370 	// can we go E?
3371 	blocked = false;
3372 	y = chunkY * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 );
3373 	for ( x = chunkX * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 ); x < chunkX * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 ) + MAP_UNIT; x++ ) {
3374 		if ( isLocationBlocked( x, y, 0, onlyLockedDoors ) ) {
3375 			blocked = true;
3376 			break;
3377 		}
3378 	}
3379 	if ( !blocked )
3380 		traceLight( chunkX + 1, chunkY, lm, onlyLockedDoors );
3381 
3382 	// can we go S?
3383 	blocked = false;
3384 	x = chunkX * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 );
3385 	for ( y = chunkY * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 ); y < chunkY * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 ) + MAP_UNIT; y++ ) {
3386 		if ( isLocationBlocked( x, y, 0, onlyLockedDoors ) ) {
3387 			blocked = true;
3388 			break;
3389 		}
3390 	}
3391 	if ( !blocked )
3392 		traceLight( chunkX, chunkY + 1, lm, onlyLockedDoors );
3393 
3394 	// can we go W?
3395 	blocked = false;
3396 	y = chunkY * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 );
3397 	for ( x = chunkX * MAP_UNIT + MAP_OFFSET - ( MAP_UNIT / 2 ); x < chunkX * MAP_UNIT + MAP_OFFSET + ( MAP_UNIT / 2 ); x++ ) {
3398 		if ( isLocationBlocked( x, y, 0, onlyLockedDoors ) ) {
3399 			blocked = true;
3400 			break;
3401 		}
3402 	}
3403 	if ( !blocked )
3404 		traceLight( chunkX - 1, chunkY, lm, onlyLockedDoors );
3405 }
3406 
3407 /// Is the location blocked by something you can't walk through (except creatures)?
3408 
isLocationBlocked(int x,int y,int z,bool onlyLockedDoors)3409 bool Map::isLocationBlocked( int x, int y, int z, bool onlyLockedDoors ) {
3410 	if ( x >= 0 && x < MAP_WIDTH && y >= 0 && y < MAP_DEPTH && z >= 0 && z < MAP_VIEW_HEIGHT ) {
3411 		Location *pos = getLocation( x, y, z );
3412 
3413 		bool ret = true;
3414 		if ( pos == NULL || pos->item || pos->creature )
3415 			ret = false;
3416 		else if ( onlyLockedDoors && isDoor( x, y ) )
3417 			ret = isLocked( pos->x, pos->y, pos->z );
3418 		else if ( pos && isSecretDoor( pos ) && pos->z > 0 )
3419 			ret = false;
3420 		else if ( !( ( GLShape* )( pos->shape ) )->isLightBlocking() )
3421 			ret = false;
3422 		return ret;
3423 	}
3424 	return true;
3425 }
3426 
3427 /// It draws... a cube. Unused.
3428 
drawCube(float x,float y,float z,float r)3429 void Map::drawCube( float x, float y, float z, float r ) {
3430 	glBegin( GL_QUADS );
3431 	// front
3432 //  glNormal3f(0.0f, 0.0f, 1.0f);
3433 	glVertex3f( -r + x, -r + y, r + z );
3434 	glVertex3f( r + x, -r + y, r + z );
3435 	glVertex3f( r + x, r + y, r + z );
3436 	glVertex3f( -r + x, r + y, r + z );
3437 
3438 	// back
3439 //  glNormal3f(0.0f, 0.0f, -1.0f);
3440 	glVertex3f( r + x, -r + y, -r + z );
3441 	glVertex3f( -r + x, -r + y, -r + z );
3442 	glVertex3f( -r + x, r + y, -r + z );
3443 	glVertex3f( r + x, r + y, -r + z );
3444 
3445 	// top
3446 //  glNormal3f(0.0f, 1.0f, 0.0f);
3447 	glVertex3f( -r + x, r + y, r + z );
3448 	glVertex3f( r + x, r + y, r + z );
3449 	glVertex3f( r + x, r + y, -r + z );
3450 	glVertex3f( -r + x, r + y, -r + z );
3451 
3452 	// bottom
3453 //  glNormal3f(0.0f, -1.0f, 0.0f);
3454 	glVertex3f( -r + x, -r + y, -r + z );
3455 	glVertex3f( r + x, -r + y, -r + z );
3456 	glVertex3f( r + x, -r + y, r + z );
3457 	glVertex3f( -r + x, -r + y, r + z );
3458 
3459 	// left
3460 //  glNormal3f(-1.0f, 0.0f, 0.0f);
3461 	glVertex3f( -r + x, -r + y, -r + z );
3462 	glVertex3f( -r + x, -r + y, r + z );
3463 	glVertex3f( -r + x, r + y, r + z );
3464 	glVertex3f( -r + x, r + y, -r + z );
3465 
3466 	// right
3467 //  glNormal3f(1.0f, 0.0f, 0.0f);
3468 	glVertex3f( r + x, -r + y, r + z );
3469 	glVertex3f( r + x, -r + y, -r + z );
3470 	glVertex3f( r + x, r + y, -r + z );
3471 	glVertex3f( r + x, r + y, r + z );
3472 
3473 	glEnd();
3474 
3475 }
3476 
3477 /// Finds creatures in a specified area and adds them to a targets array.
3478 
3479 /// Find the creatures in this area and add them to the targets array.
3480 /// Returns the number of creatures found. (0 if none.)
3481 /// It's the caller responsibility to create the targets array.
3482 
getCreaturesInArea(int x,int y,int radius,RenderedCreature * targets[])3483 int Map::getCreaturesInArea( int x, int y, int radius, RenderedCreature *targets[] ) {
3484 	int count = 0;
3485 	for ( int xx = x - radius; xx < x + radius && xx < MAP_WIDTH; xx++ ) {
3486 		for ( int yy = y - radius; yy < y + radius && yy < MAP_DEPTH; yy++ ) {
3487 			Location *loc = pos[xx][yy][0];
3488 			if ( loc && loc->creature ) {
3489 				bool alreadyFound = false;
3490 				for ( int i = 0; i < count; i++ ) {
3491 					if ( targets[i] == loc->creature ) {
3492 						alreadyFound = true;
3493 						break;
3494 					}
3495 				}
3496 				if ( !alreadyFound ) {
3497 					targets[count++] = loc->creature;
3498 				}
3499 			}
3500 		}
3501 	}
3502 	return count;
3503 }
3504 
3505 /// Is there a door at tx,ty?
3506 
isDoor(int tx,int ty)3507 bool Map::isDoor( int tx, int ty ) {
3508 	if ( tx >= 0 && tx < MAP_WIDTH && ty >= 0 && ty < MAP_DEPTH ) {
3509 		Location *loc = getLocation( tx, ty, 0 );
3510 		return( loc && isDoor( loc->shape ) );
3511 	}
3512 	return false;
3513 }
3514 
3515 /// Is this shape a door?
3516 
isDoor(Shape * shape)3517 bool Map::isDoor( Shape *shape ) {
3518 	return( shape == shapes->findShapeByName( "EW_DOOR" ) || shape == shapes->findShapeByName( "NS_DOOR" ) );
3519 }
3520 
3521 /// Is this shape a door or door frame?
3522 
isDoorType(Shape * shape,bool includeCorner)3523 bool Map::isDoorType( Shape *shape, bool includeCorner ) {
3524 	return( shape == shapes->findShapeByName( "EW_DOOR" ) || shape == shapes->findShapeByName( "NS_DOOR" ) ||
3525 	        shape == shapes->findShapeByName( "EW_DOOR_TOP" ) || shape == shapes->findShapeByName( "NS_DOOR_TOP" ) ||
3526 	        shape == shapes->findShapeByName( "DOOR_SIDE" ) || ( includeCorner && shape == shapes->findShapeByName( "CORNER" ) ) );
3527 }
3528 
3529 /// Sets the locked state of the door at x,y,z.
3530 
setLocked(int doorX,int doorY,int doorZ,bool value)3531 void Map::setLocked( int doorX, int doorY, int doorZ, bool value ) {
3532 	locked[createTripletKey( doorX, doorY, doorZ )] = value;
3533 	//Location *p = pos[doorX][doorY][doorZ];
3534 }
3535 
toggleLightMap()3536 int Map::toggleLightMap() {
3537 	LIGHTMAP_ENABLED = ( LIGHTMAP_ENABLED ? 0 : 1 );
3538 	lightMapChanged = true;
3539 	return LIGHTMAP_ENABLED;
3540 }
3541 
3542 /// Is the location currently within the screen borders?
3543 
isLocationVisible(int x,int y)3544 bool Map::isLocationVisible( int x, int y ) {
3545 	return ( x >= getX() && x < getX() + mapViewWidth && y >= getY() && y < getY() + mapViewDepth );
3546 }
3547 
3548 /// Is the location/shape currently visible on an indoor map?
3549 
isLocationInLight(int x,int y,Shape * shape)3550 bool Map::isLocationInLight( int x, int y, Shape *shape ) {
3551 	int chunkX = ( x - MAP_OFFSET ) / MAP_UNIT;
3552 	int chunkY = ( y - ( MAP_OFFSET + 1 ) ) / MAP_UNIT;
3553 	if ( !checkLightMap( chunkX, chunkY ) ) return false;
3554 	return ( helper && helper->isVisible( x, y, shape ) );
3555 }
3556 
3557 /// Map view move handler.
3558 
3559 /// Move and rotate map.
3560 /// Modifiers:
3561 /// -CTRL + arrow keys / mouse at edge of screen: rotate map
3562 /// -arrow keys / mouse at edge of screen: move map fast
3563 /// -SHIFT + arrow keys / mouse at edge of screen: slow move map
3564 /// -SHIFT + CTRL + arrow keys / mouse at edge of screen: slow rotate map
3565 
moveMap(int dir)3566 void Map::moveMap( int dir ) {
3567 	if ( SDL_GetModState() & KMOD_CTRL ) {
3568 		float rot;
3569 		if ( SDL_GetModState() & KMOD_SHIFT ) {
3570 			rot = 1.5f;
3571 		} else {
3572 			rot = 5.0f;
3573 		}
3574 		if ( dir & Constants::MOVE_DOWN ) setYRot( -1.0f * rot );
3575 		if ( dir & Constants::MOVE_UP ) setYRot( rot );
3576 		if ( dir & Constants::MOVE_RIGHT ) setZRot( -1.0f * rot );
3577 		if ( dir & Constants::MOVE_LEFT ) setZRot( rot );
3578 	} else if ( !preferences->getAlwaysCenterMap() ) {
3579 
3580 		// stop rotating (angle of rotation is kept)
3581 		setYRot( 0 );
3582 		setZRot( 0 );
3583 
3584 		mapChanged = resortShapes = true;
3585 		float delta = ( SDL_GetModState() & KMOD_SHIFT ? 0.5f : 1.0f );
3586 		if ( mouseMove ) {
3587 
3588 			// normalize z rot to 0-359
3589 			float z = moveAngle - ( getZRot() - 90 );
3590 			if ( z < 0 ) z += 360;
3591 			if ( z >= 360 ) z -= 360;
3592 			//float zrad = Constants::toRadians(z);
3593 
3594 			mapx += -moveDelta / 5.0f * Constants::sinFromAngle( z );
3595 			mapy += moveDelta / 5.0f * Constants::cosFromAngle( z );
3596 			moveDelta = 0; // reset to only move when the mouse is moved
3597 		} else {
3598 
3599 			// normalize z rot to 0-359
3600 			float z = getZRot();
3601 			if ( z < 0 ) z += 360;
3602 			if ( z >= 360 ) z -= 360;
3603 			//float zrad = Constants::toRadians(z);
3604 
3605 			// cerr << "-------------------" << endl;
3606 			// cerr << "x=" << x << " y=" << y << " zrot=" << z << endl;
3607 
3608 			if ( dir & Constants::MOVE_DOWN ) {
3609 				mapx += delta * Constants::sinFromAngle( z );
3610 				mapy += delta * Constants::cosFromAngle( z );
3611 			}
3612 			if ( dir & Constants::MOVE_UP ) {
3613 				mapx += delta * -Constants::sinFromAngle( z );
3614 				mapy += delta * -Constants::cosFromAngle( z );
3615 			}
3616 			if ( dir & Constants::MOVE_LEFT ) {
3617 				mapx += delta * -Constants::cosFromAngle( z );
3618 				mapy += delta * Constants::sinFromAngle( z );
3619 			}
3620 			if ( dir & Constants::MOVE_RIGHT ) {
3621 				mapx += delta * Constants::cosFromAngle( z );
3622 				mapy += delta * -Constants::sinFromAngle( z );
3623 			}
3624 		}
3625 
3626 		// cerr << "xdelta=" << xdelta << " ydelta=" << ydelta << endl;
3627 
3628 		if ( mapy > MAP_DEPTH - mapViewDepth ) mapy = MAP_DEPTH - mapViewDepth;
3629 		if ( mapy < 0 ) mapy = 0;
3630 		if ( mapx > MAP_WIDTH - mapViewWidth ) mapx = MAP_WIDTH - mapViewWidth;
3631 		if ( mapx < 0 ) mapx = 0;
3632 		// cerr << "mapx=" << mapx << " mapy=" << mapy << endl;
3633 
3634 		x = static_cast<int>( rint( mapx ) );
3635 		y = static_cast<int>( rint( mapy ) );
3636 		// cerr << "FINAL: x=" << x << " y=" << y << endl;
3637 
3638 	}
3639 }
3640 
handleEvent(SDL_Event * event)3641 void Map::handleEvent( SDL_Event *event ) {
3642 
3643 	// turn off outlining
3644 	if ( lastOutlinedX < MAP_WIDTH ) {
3645 		Location *pos = getLocation( lastOutlinedX, lastOutlinedY, lastOutlinedZ );
3646 		if ( !lastOutlinedZ && !pos ) pos = getItemLocation( lastOutlinedX, lastOutlinedY );
3647 		if ( pos ) {
3648 			pos->outlineColor = NULL;
3649 			lastOutlinedX = lastOutlinedY = lastOutlinedZ = MAP_WIDTH;
3650 		}
3651 	}
3652 
3653 	int ea;
3654 	int mx, my;
3655 	switch ( event->type ) {
3656 	case SDL_MOUSEMOTION:
3657 		if ( mouseRot ) {
3658 			adapter->setCursorVisible( false );
3659 			setZRot( mouseRotDir * event->motion.xrel * MOUSE_ROT_DELTA );
3660 			setYRot( -event->motion.yrel * MOUSE_ROT_DELTA );
3661 		} else if ( mouseMove ) {
3662 			adapter->setCursorVisible( false );
3663 			int q;
3664 			moveDelta = sqrt( static_cast<float>( event->motion.xrel * event->motion.xrel ) +
3665 			                  static_cast<float>( event->motion.yrel * event->motion.yrel ) ) / zoom;
3666 			Constants::getQuadrantAndAngle( event->motion.xrel, event->motion.yrel, &q, &moveAngle );
3667 			mouseMoveScreen = true;
3668 			setMove( Constants::MOVE_UP ); // so move != 0
3669 		} else {
3670 			//sdlHandler->applyMouseOffset(event->motion.x, event->motion.y, &mx, &my);
3671 			mx = event->motion.x;
3672 			my = event->motion.y;
3673 			if ( mx < 10 ) {
3674 				mouseMoveScreen = true;
3675 				setMove( Constants::MOVE_LEFT );
3676 			} else if ( mx >= adapter->getScreenWidth() - 10 ) {
3677 				mouseMoveScreen = true;
3678 				setMove( Constants::MOVE_RIGHT );
3679 			} else if ( my < 10 ) {
3680 				mouseMoveScreen = true;
3681 				setMove( Constants::MOVE_UP );
3682 			} else if ( my >= adapter->getScreenHeight() - 10 ) {
3683 				mouseMoveScreen = true;
3684 				setMove( Constants::MOVE_DOWN );
3685 			} else {
3686 				if ( mouseMoveScreen ) {
3687 					mouseMoveScreen = false;
3688 					removeMove( Constants::MOVE_LEFT | Constants::MOVE_RIGHT );
3689 					removeMove( Constants::MOVE_UP | Constants::MOVE_DOWN );
3690 					setYRot( 0.0f );
3691 					setZRot( 0.0f );
3692 				}
3693 			}
3694 
3695 			// highlight the item under the mouse if it's useable
3696 			//cerr << "pos=" << getCursorMapX() << "," << getCursorMapY() << endl;
3697 			if ( getCursorMapX() < MAP_WIDTH ) {
3698 				Location *pos = getLocation( getCursorMapX(), getCursorMapY(), getCursorMapZ() );
3699 				if ( !pos ) pos = getItemLocation( getCursorMapX(), getCursorMapY() );
3700 				if ( pos && preferences->isOutlineInteractiveItems() ) {
3701 					Color *color = adapter->getOutlineColor( pos );
3702 					if ( color ) {
3703 						pos->outlineColor = color;
3704 						lastOutlinedX = pos->x;
3705 						lastOutlinedY = pos->y;
3706 						lastOutlinedZ = pos->z;
3707 					}
3708 				}
3709 			}
3710 
3711 			if ( getCursorFlatMapX() < MAP_WIDTH ) {
3712 				// highlight traps too
3713 				selectedTrapIndex = getTrapAtLoc( getCursorFlatMapX(), getCursorFlatMapY() + 2 );
3714 			}
3715 		}
3716 		break;
3717 	case SDL_MOUSEBUTTONDOWN:
3718 		if ( event->button.button ) {
3719 			if ( event->button.button == SDL_BUTTON_MIDDLE ) {
3720 				mouseRot = true;
3721 				mouseRotDir = ( event->button.y - viewY < viewHeight / 2 ? 1 : -1 );
3722 			} else if ( event->button.button == SDL_BUTTON_RIGHT ) {
3723 				mouseMove = true;
3724 				mouseMoveX = event->button.x;
3725 				mouseMoveY = event->button.y;
3726 			}
3727 			if ( event->button.button == SDL_BUTTON_WHEELUP ) {
3728 				mouseZoom = true;
3729 				setZoomIn( false );
3730 				setZoomOut( true );
3731 			}
3732 			if ( event->button.button == SDL_BUTTON_WHEELDOWN ) {
3733 				mouseZoom = true;
3734 				setZoomIn( true );
3735 				setZoomOut( false );
3736 			}
3737 		}
3738 		break;
3739 	case SDL_MOUSEBUTTONUP:
3740 		adapter->setCursorVisible( true );
3741 		if ( event->button.button ) {
3742 			if ( event->button.button == SDL_BUTTON_MIDDLE || event->button.button == SDL_BUTTON_RIGHT ) {
3743 				mouseRot = false;
3744 				mouseMove = false;
3745 				moveDelta = 0;
3746 				setXRot( 0 );
3747 				setYRot( 0 );
3748 				setZRot( 0 );
3749 				move = 0;
3750 			}
3751 		}
3752 		break;
3753 	case SDL_KEYDOWN:
3754 	case SDL_KEYUP:
3755 		// xxx_yyy_stop means : "do xxx_yyy action when the corresponding key is up"
3756 		ea = preferences->getEngineAction( event );
3757 		if ( ea == SET_MOVE_DOWN ) {
3758 			setMove( Constants::MOVE_DOWN );
3759 		} else if ( ea == SET_MOVE_UP ) {
3760 			setMove( Constants::MOVE_UP );
3761 		} else if ( ea == SET_MOVE_RIGHT ) {
3762 			setMove( Constants::MOVE_RIGHT );
3763 		} else if ( ea == SET_MOVE_LEFT ) {
3764 			setMove( Constants::MOVE_LEFT );
3765 		} else if ( ea == SET_MOVE_DOWN_STOP ) {
3766 			setYRot( 0.0f );
3767 			setYRot( 0 );
3768 			removeMove( Constants::MOVE_DOWN );
3769 		} else if ( ea == SET_MOVE_UP_STOP ) {
3770 			setYRot( 0.0f );
3771 			setYRot( 0 );
3772 			removeMove( Constants::MOVE_UP );
3773 		} else if ( ea == SET_MOVE_RIGHT_STOP ) {
3774 			setYRot( 0.0f );
3775 			setZRot( 0 );
3776 			removeMove( Constants::MOVE_RIGHT );
3777 		} else if ( ea == SET_MOVE_LEFT_STOP ) {
3778 			setYRot( 0.0f );
3779 			setZRot( 0 );
3780 			removeMove( Constants::MOVE_LEFT );
3781 		} else if ( ea == SET_ZOOM_IN ) {
3782 			setZoomIn( true );
3783 		} else if ( ea == SET_ZOOM_OUT ) {
3784 			setZoomOut( true );
3785 		} else if ( ea == SET_ZOOM_IN_STOP ) {
3786 			setZoomIn( false );
3787 		} else if ( ea == SET_ZOOM_OUT_STOP ) {
3788 			setZoomOut( false );
3789 		}
3790 		break;
3791 	default: break;
3792 	}
3793 }
3794 
3795 
3796 
GameMapSettings()3797 GameMapSettings::GameMapSettings() {
3798 }
3799 
~GameMapSettings()3800 GameMapSettings::~GameMapSettings() {
3801 }
3802 
3803 /// Does it have a "light map" (stores which parts of the level are visible)?
3804 
isLightMapEnabled()3805 bool GameMapSettings::isLightMapEnabled() {
3806 	return true;
3807 }
3808 
isGridShowing()3809 bool GameMapSettings::isGridShowing() {
3810 	return false;
3811 }
3812 
isPlayerEnabled()3813 bool GameMapSettings::isPlayerEnabled() {
3814 	return true;
3815 }
3816 
isItemPosEnabled()3817 bool GameMapSettings::isItemPosEnabled() {
3818 	return true;
3819 }
3820 
3821 /// How far can the camera zoom in?
3822 
getMinZoomIn()3823 float GameMapSettings::getMinZoomIn() {
3824 	return 0.85f;
3825 }
3826 
3827 /// How far can the camera zoom out?
3828 
getMaxZoomOut()3829 float GameMapSettings::getMaxZoomOut() {
3830 	return 2.8f;
3831 }
3832 
3833 /// How "high" can the camera be (degrees)?
3834 
getMaxYRot()3835 float GameMapSettings::getMaxYRot() {
3836 	return 55.0f;
3837 }
3838 
3839 
3840 
3841 
3842 
EditorMapSettings()3843 EditorMapSettings::EditorMapSettings() {
3844 }
3845 
~EditorMapSettings()3846 EditorMapSettings::~EditorMapSettings() {
3847 }
3848 
isLightMapEnabled()3849 bool EditorMapSettings::isLightMapEnabled() {
3850 	return false;
3851 }
3852 
isGridShowing()3853 bool EditorMapSettings::isGridShowing() {
3854 	return true;
3855 }
3856 
isPlayerEnabled()3857 bool EditorMapSettings::isPlayerEnabled() {
3858 	return false;
3859 }
3860 
isItemPosEnabled()3861 bool EditorMapSettings::isItemPosEnabled() {
3862 	return false;
3863 }
3864 
getMinZoomIn()3865 float EditorMapSettings::getMinZoomIn() {
3866 	return 0.05f;
3867 }
3868 
getMaxZoomOut()3869 float EditorMapSettings::getMaxZoomOut() {
3870 	return 2.8f;
3871 }
3872 
getMaxYRot()3873 float EditorMapSettings::getMaxYRot() {
3874 	return 90.0f;
3875 }
3876 
3877 #define NEG_GROUND_HEIGHT 0x00100000
saveMap(const string & name,string & result,bool absolutePath,int referenceType)3878 void Map::saveMap( const string& name, string& result, bool absolutePath, int referenceType ) {
3879 
3880 	if ( name.length() == 0 ) {
3881 		result = _( "You need to name the map first." );
3882 		return;
3883 	}
3884 
3885 	MapInfo *info = new MapInfo;
3886 	info->version = PERSIST_VERSION;
3887 
3888 	info->reference_type = referenceType;
3889 
3890 	info->start_x = startx;
3891 	info->start_y = starty;
3892 
3893 	info->grid_x = mapGridX;
3894 	info->grid_y = mapGridY;
3895 
3896 	info->hasWater = ( hasWater ? 1 : 0 );
3897 
3898 	info->map_type = ( helper == MapRenderHelper::helpers[ MapRenderHelper::CAVE_HELPER ] ?
3899 	                   MapRenderHelper::CAVE_HELPER :
3900 	                   ( helper == MapRenderHelper::helpers[ MapRenderHelper::ROOM_HELPER ] ?
3901 	                     MapRenderHelper::ROOM_HELPER :
3902 	                     MapRenderHelper::OUTDOOR_HELPER ) );
3903 
3904 	strncpy( ( char* )info->theme_name, shapes->getCurrentThemeName(), 254 );
3905 	info->theme_name[254] = 0;
3906 
3907 	info->pos_count = 0;
3908 	for ( int x = 0; x < MAP_WIDTH; x++ ) {
3909 		for ( int y = 0; y < MAP_DEPTH; y++ ) {
3910 
3911 			if ( floorPositions[x][y] ) {
3912 				info->pos[ info->pos_count ] = Persist::createLocationInfo( x, y, 0 );
3913 				strncpy( ( char* )info->pos[ info->pos_count ]->floor_shape_name, floorPositions[x][y]->getName(), 254 );
3914 				info->pos[ info->pos_count ]->floor_shape_name[254] = 0;
3915 				info->pos_count++;
3916 			}
3917 
3918 			if ( itemPos[x][y] && itemPos[x][y]->x == x && itemPos[x][y]->y == y && itemPos[x][y]->item ) {
3919 				info->pos[ info->pos_count ] = Persist::createLocationInfo( x, y, 0 );
3920 				if ( referenceType == REF_TYPE_NAME ) {
3921 					strncpy( ( char* )info->pos[ info->pos_count ]->item_pos_name, itemPos[x][y]->item->getType(), 254 );
3922 					info->pos[ info->pos_count ]->item_pos_name[254] = 0;
3923 				} else {
3924 					info->pos[ info->pos_count ]->item_pos = itemPos[x][y]->item->save();
3925 				}
3926 				info->pos_count++;
3927 			}
3928 
3929 			// for virtual shapes, only save the reference shape pointed to by the virutal shape that is actually drawn
3930 			for ( int z = 0; z < MAP_VIEW_HEIGHT; z++ ) {
3931 				if ( pos[x][y][z] &&
3932 						pos[x][y][z]->x == x && pos[x][y][z]->y == y && pos[x][y][z]->z == z &&
3933 						!( pos[x][y][z]->shape && pos[x][y][z]->shape->isVirtual() && !( ( VirtualShape* )pos[x][y][z]->shape )->isDrawn() ) &&
3934 						!( pos[x][y][z]->creature && pos[x][y][z]->creature->isPartyMember() ) ) {
3935 
3936 					info->pos[ info->pos_count ] = Persist::createLocationInfo( x, y, z );
3937 
3938 					if ( pos[x][y][z]->item ) {
3939 						if ( referenceType == REF_TYPE_NAME ) {
3940 							strncpy( ( char* )info->pos[ info->pos_count ]->item_name, pos[x][y][z]->item->getType(), 254 );
3941 						} else {
3942 							info->pos[ info->pos_count ]->item = pos[x][y][z]->item->save();
3943 						}
3944 					} else if ( pos[x][y][z]->creature ) {
3945 						if ( referenceType == REF_TYPE_NAME ) {
3946 							strncpy( ( char* )info->pos[ info->pos_count ]->monster_name, pos[x][y][z]->creature->getType(), 254 );
3947 							info->pos[ info->pos_count ]->monster_name[254] = 0;
3948 						} else {
3949 							info->pos[ info->pos_count ]->creature = pos[x][y][z]->creature->save();
3950 						}
3951 					} else {
3952 						if ( pos[x][y][z]->shape->isVirtual() ) {
3953 							VirtualShape *vs = ( VirtualShape* )pos[x][y][z]->shape;
3954 							strncpy( ( char* )( info->pos[ info->pos_count ]->shape_name ), vs->getRef()->getName(), 254 );
3955 							info->pos[ info->pos_count ]->x -= vs->getOffsetX();
3956 							info->pos[ info->pos_count ]->y -= vs->getOffsetY();
3957 							info->pos[ info->pos_count ]->z -= vs->getOffsetZ();
3958 						} else {
3959 							strncpy( ( char* )( info->pos[ info->pos_count ]->shape_name ), pos[x][y][z]->shape->getName(), 254 );
3960 						}
3961 						info->pos[ info->pos_count ]->shape_name[254] = 0;
3962 					}
3963 
3964 					// save the deity locations
3965 					char const* p = adapter->getMagicSchoolIndexForLocation( pos[x][y][z] );
3966 					if ( p ) {
3967 						strncpy( ( char* )info->pos[ info->pos_count ]->magic_school_name, p, 254 );
3968 						info->pos[ info->pos_count ]->magic_school_name[254] = 0;
3969 					}
3970 
3971 					info->pos_count++;
3972 				}
3973 			}
3974 		}
3975 	}
3976 
3977 	// save rugs
3978 	info->rug_count = 0;
3979 	for ( int x = 0; x < MAP_CHUNKS_X; x++ ) {
3980 		for ( int y = 0; y < MAP_CHUNKS_Y; y++ ) {
3981 			if ( rugPos[x][y].texture.isSpecified() ) {
3982 				info->rugPos[ info->rug_count ] = Persist::createRugInfo( x, y );
3983 				info->rugPos[ info->rug_count ]->angle = toint( rugPos[x][y].angle * 100.0f );
3984 				info->rugPos[ info->rug_count ]->isHorizontal = ( rugPos[x][y].isHorizontal ? 1 : 0 );
3985 				info->rugPos[ info->rug_count ]->texture = 666; // -=K=-: no point to store any GPU texture names? rugPos[x][y].texture;
3986 				info->rug_count++;
3987 			}
3988 		}
3989 	}
3990 
3991 	// save doors
3992 	info->locked_count = 0;
3993 	for ( map<Uint32, bool>::iterator i = locked.begin(); i != locked.end(); ++i ) {
3994 		Uint32 key = i->first;
3995 		Uint8 value = ( Uint8 )( i->second ? 1 : 0 );
3996 		info->locked[ info->locked_count++ ] = Persist::createLockedInfo( key, value );
3997 	}
3998 	info->door_count = 0;
3999 	for ( map<Uint32, Uint32>::iterator i = doorToKey.begin(); i != doorToKey.end(); ++i ) {
4000 		Uint32 key = i->first;
4001 		Uint32 value = i->second;
4002 		info->door[ info->door_count++ ] = Persist::createDoorInfo( key, value );
4003 	}
4004 
4005 	// secret doors
4006 	info->secret_count = 0;
4007 	for ( map<int, bool>::iterator i = secretDoors.begin(); i != secretDoors.end(); ++i ) {
4008 		Uint32 key = ( Uint32 )( i->first );
4009 		Uint8 value = ( Uint8 )( i->second ? 1 : 0 );
4010 		info->secret[ info->secret_count++ ] = Persist::createLockedInfo( key, value );
4011 	}
4012 
4013 	// save the fog
4014 	if ( helper ) helper->saveHelper( &( info->fog_info ) );
4015 
4016 	info->edited = edited;
4017 
4018 	// save ground height for outdoors maps
4019 	// save the outdoor textures
4020 	info->heightMapEnabled = ( Uint8 )( heightMapEnabled ? 1 : 0 );
4021 	info->outdoorTextureInfoCount = 0;
4022 	for ( int gx = 0; gx < MAP_TILES_X; gx++ ) {
4023 		for ( int gy = 0; gy < MAP_TILES_Y; gy++ ) {
4024 			Uint32 base = ( ground[ gx ][ gy ] < 0 ? NEG_GROUND_HEIGHT : 0x00000000 );
4025 			info->ground[ gx ][ gy ] = ( Uint32 )( fabs( ground[ gx ][ gy ] ) * 100 ) + base;
4026 			for ( int z = 0; z < MAX_OUTDOOR_LAYER; z++ ) {
4027 				if ( outdoorTex[ gx ][ gy ][ z ].texture.isSpecified() ) {
4028 					OutdoorTextureInfo *oti = new OutdoorTextureInfo;
4029 					int height = getShapes()->getCurrentTheme()->getOutdoorTextureHeight( outdoorTex[ gx ][ gy ][z].outdoorThemeRef );
4030 					int mx = gx * OUTDOORS_STEP;
4031 					int my = gy * OUTDOORS_STEP + height + 1;
4032 					oti->x = mx;
4033 					oti->y = my;
4034 					oti->angle = outdoorTex[ gx ][ gy ][z].angle * 1000;
4035 					oti->horizFlip = outdoorTex[ gx ][ gy ][z].horizFlip;
4036 					oti->vertFlip = outdoorTex[ gx ][ gy ][z].vertFlip;
4037 					oti->offsetX = outdoorTex[ gx ][ gy ][z].offsetX * 1000;
4038 					oti->offsetY = outdoorTex[ gx ][ gy ][z].offsetY * 1000;
4039 					oti->outdoorThemeRef = outdoorTex[ gx ][ gy ][z].outdoorThemeRef;
4040 					oti->z = z;
4041 					info->outdoorTexture[ info->outdoorTextureInfoCount++ ] = oti;
4042 				}
4043 			}
4044 		}
4045 	}
4046 
4047 	// save traps
4048 	info->trapCount = ( Uint8 )trapList.size();
4049 	for ( unsigned int i = 0; i < trapList.size(); i++ ) {
4050 		Trap trap = trapList[ i ];
4051 		info->trap[i] = Persist::createTrapInfo( trap.r.x, trap.r.y, trap.r.w, trap.r.h, trap.type, trap.discovered, trap.enabled );
4052 	}
4053 
4054 	string fileName;
4055 	if ( absolutePath ) {
4056 		fileName = name;
4057 	} else {
4058 		fileName = rootDir + ( "/maps/" + name + ".map" );
4059 	}
4060 
4061 	cerr << "saving map: " << fileName << endl;
4062 
4063 	FILE *fp = fopen( fileName.c_str(), "wb" );
4064 	File *file = new ZipFile( fp, ZipFile::ZIP_WRITE );
4065 	Persist::saveMap( file, info );
4066 
4067 	delete file;
4068 
4069 	Persist::deleteMapInfo( info );
4070 
4071 	// save npc info when saving from map editor (ie. map templates)
4072 	if ( referenceType == REF_TYPE_NAME ) {
4073 		adapter->saveMapData( fileName );
4074 	}
4075 
4076 	result += "Map saved: ";
4077 	result += name;
4078 }
4079 
4080 /// Initializes the textures for cave levels.
4081 
initForCave(char * themeName)4082 void Map::initForCave( char *themeName ) {
4083 	if ( !themeName ) {
4084 		shapes->loadRandomCaveTheme();
4085 	} else {
4086 		shapes->loadCaveTheme( themeName );
4087 	}
4088 
4089 	string ref = WallTheme::themeRefName[ WallTheme::THEME_REF_PASSAGE_FLOOR ];
4090 	Texture* floorTextureGroup = shapes->getCurrentTheme()->getTextureGroup( ref );
4091 	setFloor( CAVE_CHUNK_SIZE, CAVE_CHUNK_SIZE, floorTextureGroup[ GLShape::TOP_SIDE ] );
4092 }
4093 
loadMap(const string & name,std::string & result,StatusReport * report,int level,int depth,bool changingStory,bool fromRandom,bool goingUp,bool goingDown,vector<RenderedItem * > * items,vector<RenderedCreature * > * creatures,bool absolutePath,char * templateMapName)4094 bool Map::loadMap( const string& name, std::string& result, StatusReport *report,
4095                    int level, int depth,
4096                    bool changingStory, bool fromRandom,
4097                    bool goingUp, bool goingDown,
4098                    vector< RenderedItem* > *items,
4099                    vector< RenderedCreature* > *creatures,
4100                    bool absolutePath,
4101                    char *templateMapName ) {
4102 	if ( !name.length() ) {
4103 		result = _( "Enter a name of a map to load." );
4104 		return false;
4105 	}
4106 
4107 	stringstream tmpFileName;
4108 	if ( absolutePath ) {
4109 		tmpFileName << name;
4110 	} else {
4111 		if ( depth > 0 ) {
4112 			tmpFileName << rootDir << "/maps/" << name << depth << ".map";
4113 		} else {
4114 			tmpFileName << rootDir << "/maps/" << name << ".map";
4115 		}
4116 	}
4117 	string fileName = tmpFileName.str();
4118 	cerr << "Looking for map: " << fileName << endl;
4119 
4120 	FILE *fp = fopen( fileName.c_str(), "rb" );
4121 	if ( !fp ) {
4122 		result = "Can't find map: " + fileName;
4123 		return false;
4124 	}
4125 	if ( report ) report->updateStatus( 0, 7, _( "Loading map" ) );
4126 	File *file = new ZipFile( fp, ZipFile::ZIP_READ );
4127 	MapInfo *info = Persist::loadMap( file );
4128 	delete file;
4129 
4130 	if ( report ) report->updateStatus( 1, 7, _( "Loading theme" ) );
4131 
4132 	// reset the map
4133 	reset();
4134 
4135 	if ( info->map_type == MapRenderHelper::ROOM_HELPER ) {
4136 		// it's a room-type map
4137 		setMapRenderHelper( MapRenderHelper::helpers[ MapRenderHelper::ROOM_HELPER ] );
4138 		// load the theme
4139 		if ( getPreferences()->isDebugTheme() ) shapes->loadDebugTheme();
4140 		else shapes->loadTheme( ( const char* )info->theme_name );
4141 	} else if ( info->map_type == MapRenderHelper::CAVE_HELPER ) {
4142 		// it's a room-type map
4143 		setMapRenderHelper( MapRenderHelper::helpers[ MapRenderHelper::CAVE_HELPER ] );
4144 
4145 		// prepare map for cave (load theme, etc.)
4146 		initForCave( ( char* )info->theme_name );
4147 
4148 		// load the fog
4149 		getMapRenderHelper()->loadHelper( &( info->fog_info ) );
4150 	} else if ( info->map_type == MapRenderHelper::OUTDOOR_HELPER ) {
4151 		// it's an outdoors type map
4152 		setMapRenderHelper( MapRenderHelper::helpers[ MapRenderHelper::OUTDOOR_HELPER ] );
4153 		if ( getPreferences()->isDebugTheme() ) shapes->loadDebugTheme();
4154 		else shapes->loadTheme( ( const char* )info->theme_name );
4155 	} else {
4156 		cerr << "*** error unknown map type: " << info->map_type << endl;
4157 		return false;
4158 	}
4159 
4160 	edited = info->edited != 0;
4161 
4162 	// load ground heights for outdoors maps
4163 	heightMapEnabled = ( info->heightMapEnabled == 1 );
4164 	for ( int gx = 0; gx < MAP_TILES_X; gx++ ) {
4165 		for ( int gy = 0; gy < MAP_TILES_Y; gy++ ) {
4166 			if ( info->ground[ gx ][ gy ] > NEG_GROUND_HEIGHT ) {
4167 				ground[ gx ][ gy ] = ( info->ground[ gx ][ gy ] - NEG_GROUND_HEIGHT ) / -100.0f;
4168 			} else {
4169 				ground[ gx ][ gy ] = info->ground[ gx ][ gy ] / 100.0f;
4170 			}
4171 		}
4172 	}
4173 
4174 	for ( int i = 0; i < ( int )info->outdoorTextureInfoCount; i++ ) {
4175 		OutdoorTextureInfo *oti = info->outdoorTexture[i];
4176 		setOutdoorTexture( oti->x, oti->y,
4177 		                   oti->offsetX / 1000.0f, oti->offsetY / 1000.0f,
4178 		                   oti->outdoorThemeRef, oti->angle / 1000.0f,
4179 		                   oti->horizFlip != 0, oti->vertFlip != 0, oti->z );
4180 	}
4181 
4182 	if ( heightMapEnabled ) {
4183 		initOutdoorsGroundTexture();
4184 	}
4185 
4186 	setHasWater( info->hasWater == 1 ? true : false );
4187 
4188 	if ( report ) report->updateStatus( 2, 7, _( "Starting map" ) );
4189 
4190 	/*
4191 	// Start at the saved start pos. or where the party
4192 	// was on the last level if changing stories.
4193 	// When using an absolutePath (ie. saved generated maps) also start at the last
4194 	// saved position.
4195 	if( absolutePath || !changingStory || !( settings->isPlayerEnabled() ) || fromRandom ) {
4196 	  startx = info->start_x;
4197 	  starty = info->start_y;
4198 	} else {
4199 	  startx = toint( adapter->getPlayer()->getX() );
4200 	  starty = toint( adapter->getPlayer()->getY() );
4201 	}
4202 	*/
4203 	cerr << "LOADMAP: using saved starting position. goingUp=" << goingUp << " goingDown=" << goingDown << endl;
4204 	startx = info->start_x;
4205 	starty = info->start_y;
4206 	float start_dist = -1;
4207 
4208 	mapGridX = info->grid_x;
4209 	mapGridY = info->grid_y;
4210 
4211 	if ( report ) report->updateStatus( 3, 7, _( "Initializing map" ) );
4212 
4213 	GLShape *shape;
4214 	DisplayInfo di;
4215 	for ( int i = 0; i < static_cast<int>( info->pos_count ); i++ ) {
4216 
4217 		if ( info->pos[i]->x >= MAP_WIDTH || info->pos[i]->y >= MAP_DEPTH || info->pos[i]->z >= MAP_VIEW_HEIGHT ) {
4218 			cerr << "*** Skipping invalid map location: " << info->pos[i]->x << "," << info->pos[i]->y << "," << info->pos[i]->z << endl;
4219 			continue;
4220 		}
4221 
4222 		if ( strlen( ( char* )( info->pos[i]->floor_shape_name ) ) ) {
4223 			shape = shapes->findShapeByName( ( char* )( info->pos[i]->floor_shape_name ) );
4224 			if ( shape )
4225 				setFloorPosition( info->pos[i]->x, info->pos[i]->y, shape );
4226 			else
4227 				cerr << "Map::load failed to find floor shape: " << info->pos[i]->floor_shape_name <<
4228 				" at pos: " << info->pos[i]->x << "," << info->pos[i]->y << endl;
4229 		}
4230 
4231 		if ( strlen( ( char* )( info->pos[i]->item_pos_name ) ) || info->pos[i]->item_pos ) {
4232 			RenderedItem *item;
4233 			if ( info->reference_type == REF_TYPE_NAME ) {
4234 				item = adapter->createItem( ( char* )( info->pos[i]->item_pos_name ), level, depth );
4235 			} else {
4236 				item = adapter->createItem( info->pos[i]->item_pos );
4237 			}
4238 			if ( item ) {
4239 				setItem( info->pos[i]->x, info->pos[i]->y, 0, item );
4240 				if ( items )
4241 					items->push_back( item );
4242 			} else
4243 				cerr << "Map::load failed to item at pos: " << info->pos[i]->x << "," << info->pos[i]->y << ",0" << endl;
4244 		}
4245 
4246 		// load the deity locations
4247 		MagicSchool *ms = NULL;
4248 		if ( strlen( ( char* )info->pos[i]->magic_school_name ) ) {
4249 			ms = MagicSchool::getMagicSchoolByName( ( char* )info->pos[i]->magic_school_name );
4250 			di.red = ms->getDeityRed();
4251 			di.green = ms->getDeityGreen();
4252 			di.blue = ms->getDeityBlue();
4253 		}
4254 
4255 		// load the items
4256 		if ( strlen( ( char* )( info->pos[i]->item_name ) ) || info->pos[i]->item ) {
4257 			RenderedItem *item;
4258 			if ( info->reference_type == REF_TYPE_NAME ) {
4259 				item = adapter->createItem( ( char* )( info->pos[i]->item_name ), level, depth );
4260 			} else {
4261 				item = adapter->createItem( info->pos[i]->item );
4262 			}
4263 			if ( item ) {
4264 				setItem( info->pos[i]->x, info->pos[i]->y, info->pos[i]->z, item );
4265 				if ( items ) items->push_back(  item );
4266 			} else cerr << "Map::load failed to item at pos: " << info->pos[i]->x << "," << info->pos[i]->y << "," << info->pos[i]->z << endl;
4267 		} else if ( strlen( ( char* )( info->pos[i]->monster_name ) ) || info->pos[i]->creature ) {
4268 			RenderedCreature *creature;
4269 			if ( info->reference_type == REF_TYPE_NAME ) {
4270 				creature = adapter->createMonster( ( char* )( info->pos[i]->monster_name ) );
4271 			} else {
4272 				creature = adapter->createMonster( info->pos[i]->creature );
4273 			}
4274 			if ( creature ) {
4275 				setCreature( info->pos[i]->x, info->pos[i]->y, info->pos[i]->z, creature );
4276 				creature->moveTo( info->pos[i]->x, info->pos[i]->y, info->pos[i]->z );
4277 				if ( creatures ) creatures->push_back(  creature );
4278 			} else cerr << "Map::load failed to creature at pos: " << info->pos[i]->x << "," << info->pos[i]->y << "," << info->pos[i]->z << endl;
4279 		} else if ( strlen( ( char* )( info->pos[i]->shape_name ) ) ) {
4280 			shape = shapes->findShapeByName( ( char* )( info->pos[i]->shape_name ) );
4281 			if ( shape ) {
4282 				setPosition( info->pos[i]->x, info->pos[i]->y, info->pos[i]->z, shape, ( ms ? &di : NULL ) );
4283 				if ( settings->isPlayerEnabled() ) {
4284 					if ( ( goingUp && !strcmp( ( char* )info->pos[i]->shape_name, "GATE_DOWN" ) ) ||
4285 					        ( goingDown && !strcmp( ( char* )info->pos[i]->shape_name, "GATE_UP" ) ) ||
4286 					        ( goingUp && !strcmp( ( char* )info->pos[i]->shape_name, "GATE_DOWN_OUTDOORS" ) ) ) {
4287 						float d = ( info->pos[i]->x - adapter->getPlayer()->getX() ) * ( info->pos[i]->x - adapter->getPlayer()->getX() ) +
4288 						          ( info->pos[i]->y - adapter->getPlayer()->getY() ) * ( info->pos[i]->y - adapter->getPlayer()->getY() );
4289 						if ( start_dist == -1 || d < start_dist ) {
4290 							startx = info->pos[i]->x;
4291 							starty = info->pos[i]->y;
4292 							start_dist = d;
4293 							cerr << "LOADMAP: using stairs as starting position. Dist=" << start_dist << endl;
4294 						}
4295 					}
4296 				}
4297 			} else
4298 				cerr << "Map::load failed to find shape: " << info->pos[i]->shape_name <<
4299 				" at pos: " << info->pos[i]->x << "," << info->pos[i]->y << "," << info->pos[i]->z << endl;
4300 		}
4301 
4302 		// load the deity locations
4303 		if ( ms ) {
4304 			Location *pos = getPosition( info->pos[i]->x, info->pos[i]->y, info->pos[i]->z );
4305 			if ( !pos ) {
4306 				cerr << "*** error: Can't find position to place deity!" << endl;
4307 			} else {
4308 				adapter->setMagicSchoolIndexForLocation( pos, ms->getName() );
4309 			}
4310 		}
4311 	}
4312 
4313 	// load rugs
4314 	Rug rug;
4315 	for ( int i = 0; i < static_cast<int>( info->rug_count ); i++ ) {
4316 		//rug.angle = info->rugPos[i]->angle / 100.0f;
4317 		rug.angle = Util::roll( -15.0f, 15.0f ); // fixme?
4318 		rug.isHorizontal = ( info->rugPos[i]->isHorizontal == 1 );
4319 		rug.texture = shapes->getRandomRug(); // fixme?
4320 		setRugPosition( info->rugPos[i]->cx, info->rugPos[i]->cy, &rug );
4321 	}
4322 
4323 	// load traps
4324 	for ( int i = 0; i < info->trapCount; i++ ) {
4325 		TrapInfo *trapInfo = info->trap[ i ];
4326 		int index = addTrap( trapInfo->x, trapInfo->y, trapInfo->w, trapInfo->h );
4327 		Trap *trap = getTrapLoc( index );
4328 		trap->discovered = ( trapInfo->discovered != 0 );
4329 		trap->enabled = ( trapInfo->enabled != 0 );
4330 		trap->type = static_cast<int>( trapInfo->type );
4331 	}
4332 
4333 	// load doors
4334 	for ( int i = 0; i < static_cast<int>( info->locked_count ); i++ ) {
4335 		locked[ info->locked[ i ]->key ] = ( info->locked[ i ]->value == 1 ? true : false );
4336 	}
4337 	for ( int i = 0; i < static_cast<int>( info->door_count ); i++ ) {
4338 		doorToKey[ info->door[ i ]->key ] = info->door[ i ]->value;
4339 		keyToDoor[ info->door[ i ]->value ] = info->door[ i ]->key;
4340 	}
4341 
4342 	// secret doors
4343 	for ( int i = 0; i < static_cast<int>( info->secret_count ); i++ ) {
4344 		secretDoors[ static_cast<int>( info->secret[ i ]->key ) ] = ( info->secret[ i ]->value == 1 ? true : false );
4345 	}
4346 
4347 	if ( report ) report->updateStatus( 4, 7, _( "Finishing map" ) );
4348 
4349 	this->center( info->start_x, info->start_y, true );
4350 
4351 	Persist::deleteMapInfo( info );
4352 
4353 	if ( report ) report->updateStatus( 5, 7, _( "Loading Creatures" ) );
4354 
4355 	// load map-related data from text file
4356 	stringstream txtfileName;
4357 	if ( templateMapName ) {
4358 		if ( depth > 0 ) {
4359 			txtfileName << rootDir << "/maps/" << templateMapName << depth << ".map";
4360 		} else {
4361 			txtfileName << rootDir << "/maps/" << templateMapName << ".map";
4362 		}
4363 	} else {
4364 		txtfileName << fileName;
4365 	}
4366 	cerr << "Looking for txt file: " << txtfileName.str() << endl;
4367 	adapter->loadMapData( txtfileName.str() );
4368 
4369 	strcpy( this->name, ( templateMapName ? templateMapName : name.c_str() ) );
4370 
4371 	if ( report ) report->updateStatus( 6, 7, _( "Starting party" ) );
4372 
4373 	/*
4374 	  FIXME: Place the party at the start. This code attempts to find a place
4375 	  for the party near the gate (which was set in Scourge::useGate.) Need to
4376 	  find a better AI solution as this code can place the party outside the walls.
4377 	  For now always leave "whitespace" around gates in edited levels.
4378 	*/
4379 	if ( settings->isPlayerEnabled() ) {
4380 		int xx = startx;
4381 		int yy = starty;
4382 		int nx, ny;
4383 		for ( int t = 0; t < adapter->getPartySize(); t++ ) {
4384 			if ( !adapter->getParty( t )->getStateMod( StateMod::dead ) ) {
4385 				adapter->getParty( t )->findPlace( xx, yy, &nx, &ny );
4386 				xx = nx;
4387 				yy = ny;
4388 			}
4389 		}
4390 	}
4391 	result = _( "Map loaded: " ) + name;
4392 
4393 	return true;
4394 }
4395 
loadMapLocation(const std::string & name,std::string & result,int * gridX,int * gridY,int depth)4396 void Map::loadMapLocation( const std::string& name, std::string& result, int *gridX, int *gridY, int depth ) {
4397 	Uint16 tmpX, tmpY;
4398 	if ( name.empty() ) {
4399 		result = _( "Enter a name of a map to load." );
4400 		return;
4401 	}
4402 
4403 	stringstream fileName;
4404 	if ( depth > 0 ) {
4405 		fileName << rootDir << "/maps/" << name << depth << ".map";
4406 	} else {
4407 		fileName << rootDir << "/maps/" << name << ".map";
4408 	}
4409 	//cerr << "loading map header: " << fileName.str() << endl;
4410 
4411 	FILE *fp = fopen( fileName.str().c_str(), "rb" );
4412 	if ( !fp ) {
4413 		result = _( "Can't find map: " ) + name;
4414 		return;
4415 	}
4416 	File *file = new ZipFile( fp, ZipFile::ZIP_READ );
4417 	Persist::loadMapHeader( file, &tmpX, &tmpY );
4418 	delete file;
4419 
4420 	*gridX = static_cast<int>( tmpX );
4421 	*gridY = static_cast<int>( tmpY );
4422 
4423 	result = _( "Map header loaded: " ) + name;
4424 }
4425 
4426 
getMapXYAtScreenXY(Uint16 * mapx,Uint16 * mapy)4427 void Map::getMapXYAtScreenXY( Uint16 *mapx, Uint16 *mapy ) {
4428 	getMapXYAtScreenXY( adapter->getMouseX(), adapter->getMouseY(), mapx, mapy );
4429 }
4430 
getMapXYAtScreenXY(Uint16 x,Uint16 y,Uint16 * mapx,Uint16 * mapy)4431 void Map::getMapXYAtScreenXY( Uint16 x, Uint16 y, Uint16 *mapx, Uint16 *mapy ) {
4432 	double obj_x, obj_y, obj_z;
4433 	double win_x = static_cast<double>( x );
4434 	double win_y = static_cast<double>( adapter->getScreenHeight() - y );
4435 
4436 	// get the depth buffer value
4437 	float win_z;
4438 	glReadPixels( static_cast<int>( win_x ), static_cast<int>( win_y ), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &win_z );
4439 
4440 	double projection[16];
4441 	double modelview[16];
4442 	GLint viewport[4];
4443 
4444 	glGetDoublev( GL_PROJECTION_MATRIX, projection );
4445 	glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
4446 	glGetIntegerv( GL_VIEWPORT, viewport );
4447 
4448 	int res = gluUnProject( win_x, win_y, win_z, modelview, projection, viewport, &obj_x, &obj_y, &obj_z );
4449 
4450 	if ( res ) {
4451 		*mapx = getX() + ( Uint16 )( obj_x / MUL );
4452 		*mapy = getY() + ( Uint16 )( obj_y / MUL );
4453 
4454 		debugX = getX() + static_cast<int>( obj_x / MUL );
4455 		debugY = getY() + static_cast<int>( obj_y / MUL );
4456 		debugZ = static_cast<int>( obj_z / MUL );
4457 	} else {
4458 		*mapx = *mapy = MAP_WIDTH + 1;
4459 	}
4460 }
4461 
4462 // these are arrived at by trial-and-error
4463 #define RAY_PICK_MIN_T 0.6f
4464 #define RAY_PICK_MAX_T 0.8f
4465 #define RAY_PICK_STEP_COUNT 30
getMapXYZAtScreenXY(Uint16 * mapx,Uint16 * mapy,Uint16 * mapz,Location ** pos)4466 void Map::getMapXYZAtScreenXY( Uint16 *mapx, Uint16 *mapy, Uint16 *mapz, Location **pos ) {
4467 	double win_x = static_cast<double>( adapter->getMouseX() );
4468 	double win_y = static_cast<double>( adapter->getScreenHeight() - adapter->getMouseY() );
4469 
4470 	double projection[16];
4471 	double modelview[16];
4472 	GLint viewport[4];
4473 
4474 	glGetDoublev( GL_PROJECTION_MATRIX, projection );
4475 	glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
4476 	glGetIntegerv( GL_VIEWPORT, viewport );
4477 
4478 	// the near plane's mouse coordinates
4479 	double px, py, pz;
4480 	int res = gluUnProject( win_x, win_y, 0.0f, modelview, projection, viewport, &px, &py, &pz );
4481 
4482 	// the far plane's mouse coordinates
4483 	double qx, qy, qz;
4484 	res = gluUnProject( win_x, win_y, 1.0f, modelview, projection, viewport, &qx, &qy, &qz );
4485 
4486 	// Take steps along the pick-ray. A t of 0 to 1 is the line segment from the near to the far plane.
4487 #if DEBUG_MOUSE_POS == 1
4488 	cerr << "------------------------------" << endl;
4489 #endif
4490 	for ( int i = 0; i < RAY_PICK_STEP_COUNT; i++ ) {
4491 		float t = RAY_PICK_MIN_T + ( ( ( float )i / ( float )RAY_PICK_STEP_COUNT ) * ( RAY_PICK_MAX_T - RAY_PICK_MIN_T ) );
4492 		double ox = px + t * ( qx - px );
4493 		double oy = py + t * ( qy - py );
4494 		double oz = pz + t * ( qz - pz );
4495 
4496 		double mx = getX() + ( ox / MUL );
4497 		double my = getY() + ( oy / MUL ) + 2.0f;
4498 		float gh = ( mx >= 0 && mx < MAP_WIDTH && my >= 0 && my < MAP_DEPTH ? findMaxHeightPos( mx, my, 0 ) : 0 );
4499 		double mz = ( ( oz ) / MUL ) - gh;
4500 #if DEBUG_MOUSE_POS == 1
4501 		cerr << "map: " << mx << "," << my << "," << mz << " ground=" << gh << endl;
4502 #endif
4503 		*mapx = ( Uint16 )mx;
4504 		*mapy = ( Uint16 )my;
4505 
4506 		// hit something?
4507 		for ( int xx = -1; xx < 1; xx++ ) {
4508 			for ( int yy = -1; yy < 1; yy++ ) {
4509 				for ( int zz = -1; zz < 1; zz++ ) {
4510 
4511 					float fx = mx + xx;
4512 					float fy = my + yy;
4513 					float fz = mz + zz;
4514 
4515 					if ( fx >= 0 && fx < MAP_WIDTH &&
4516 					        fy >= 0 && fy < MAP_DEPTH &&
4517 					        fz >= 0 && fz < MAP_VIEW_HEIGHT ) {
4518 						Location *location = getLocation( ( int )fx, ( int )fy, ( int )fz );
4519 						if ( location && ( location )->shape && ( ( location )->creature || ( location )->item || ( location )->shape->isInteractive() || isSecretDoor( location ) ) ) {
4520 							Shape *shape = ( location )->shape;
4521 							if ( mx >= ( location )->x + shape->getOffsX() / MUL &&
4522 							        mx < ( location )->x + shape->getOffsX() / MUL + shape->getWidth() &&
4523 							        my >= ( location )->y + shape->getOffsY() / MUL - shape->getDepth() &&
4524 							        my < ( location )->y + shape->getOffsY() / MUL + 1 &&
4525 							        mz >= ( location )->z + shape->getOffsZ() / MUL &&
4526 							        mz < ( location )->z + shape->getOffsZ() / MUL + shape->getHeight() ) {
4527 								*pos = location;
4528 								*mapx = fx;
4529 								*mapy = fy;
4530 								*mapz = fz;
4531 								snprintf( mapDebugStr, DEBUG_SIZE, "map: %.2f,%.2f,%.2f pos:%d,%d,%d - %d,%d,%d",
4532 								          mx, my, mz,
4533 								          location->x, location->y - shape->getDepth() + 1, location->z,
4534 								          location->x + shape->getWidth(), location->y + 1, location->z + shape->getHeight() );
4535 								adapter->setDebugStr( mapDebugStr );
4536 								return;
4537 							}
4538 						}
4539 					}
4540 				}
4541 			}
4542 		}
4543 		if ( toint( mz ) <= 0.25f ) {
4544 			*pos = NULL;
4545 			*mapz = 0;
4546 			snprintf( mapDebugStr, DEBUG_SIZE, "map: %d,%d,%d", *mapx, *mapy, *mapz );
4547 			adapter->setDebugStr( mapDebugStr );
4548 			return;
4549 		}
4550 	}
4551 	*pos = NULL;
4552 	*mapz = 0;
4553 	adapter->setDebugStr( "map: " );
4554 }
4555 
4556 /**
4557   Maxsize of 0 means unlimited size cache.
4558 */
MapMemoryManager(int maxSize)4559 MapMemoryManager::MapMemoryManager( int maxSize ) {
4560 	this->maxSize = maxSize;
4561 	this->accessCount = 0;
4562 	this->usedCount = 0;
4563 	// make room for at least this many pointers
4564 	unused.reserve( 200000 );
4565 
4566 	this->usedEffectCount = 0;
4567 	unusedEffect.reserve( 50000 );
4568 }
4569 
~MapMemoryManager()4570 MapMemoryManager::~MapMemoryManager() {
4571 	for ( int i = 0; i < static_cast<int>( unused.size() ); i++ ) {
4572 		delete unused[i];
4573 	}
4574 	unused.clear();
4575 	for ( int i = 0; i < static_cast<int>( unusedEffect.size() ); i++ ) {
4576 		delete unusedEffect[i];
4577 	}
4578 	unusedEffect.clear();
4579 }
4580 
4581 /// Creates a new, empty map location. Reuses an unused location if possible.
4582 
newLocation()4583 Location *MapMemoryManager::newLocation() {
4584 	Location *pos;
4585 	if ( !unused.empty() ) {
4586 		pos = unused[ unused.size() - 1 ];
4587 		unused.pop_back();
4588 	} else {
4589 		pos = new Location();
4590 	}
4591 	usedCount++;
4592 
4593 	printStatus();
4594 
4595 	// reset it
4596 	pos->x = pos->y = pos->z = 0;
4597 	pos->heightPos = 0;
4598 	pos->shape = NULL;
4599 	pos->item = NULL;
4600 	pos->creature = NULL;
4601 	pos->outlineColor = NULL;
4602 	pos->texIndex = 0;
4603 
4604 	return pos;
4605 }
4606 
4607 /// Creates a new, empty effect location. Reuses an unused location if possible.
4608 
newEffectLocation(Map * theMap,Preferences * preferences,Shapes * shapes,int width,int height)4609 EffectLocation *MapMemoryManager::newEffectLocation( Map *theMap, Preferences *preferences, Shapes *shapes, int width, int height ) {
4610 	EffectLocation *pos;
4611 	if ( !unusedEffect.empty() ) {
4612 		pos = unusedEffect[ unusedEffect.size() - 1 ];
4613 		unusedEffect.pop_back();
4614 		pos->effect->reset();
4615 	} else {
4616 		pos = new EffectLocation;
4617 		//pos->effect = new Effect( theMap, preferences, shapes, 4, 4 );
4618 		pos->effect = new Effect( theMap, preferences, shapes, width, height );
4619 		pos->effect->deleteParticles();
4620 	}
4621 	usedEffectCount++;
4622 
4623 	printStatus();
4624 
4625 	// reset it
4626 	pos->x = pos->y = pos->z = 0;
4627 	pos->effectDuration = 0;
4628 	pos->damageEffectCounter = 0;
4629 	pos->effectType = 0;
4630 	pos->effectDelay = 0;
4631 	pos->heightPos = 0;
4632 
4633 	return pos;
4634 }
4635 
4636 /// Deletes a map location and adds it to the "unused" array.
4637 
deleteLocation(Location * pos)4638 void MapMemoryManager::deleteLocation( Location *pos ) {
4639 	if ( !maxSize || static_cast<int>( unused.size() ) < maxSize ) {
4640 		unused.push_back( pos );
4641 	} else {
4642 		delete pos;
4643 	}
4644 	usedCount--;
4645 	printStatus();
4646 }
4647 
4648 /// Deletes the special effect at a location and adds it to the "unused" array.
4649 
deleteEffectLocation(EffectLocation * pos)4650 void MapMemoryManager::deleteEffectLocation( EffectLocation *pos ) {
4651 	if ( !maxSize || static_cast<int>( unusedEffect.size() ) < maxSize ) {
4652 		unusedEffect.push_back( pos );
4653 	} else {
4654 		delete pos;
4655 	}
4656 	usedEffectCount--;
4657 	printStatus();
4658 }
4659 
4660 /// Outputs memory status to stderr.
4661 
printStatus()4662 void MapMemoryManager::printStatus() {
4663 	if ( ++accessCount > 5000 ) {
4664 		cerr << "Map size: " << usedCount << " Kb:" << ( static_cast<float>( sizeof( Location )*usedCount ) / 1024.0f ) <<
4665 		" Cache: " << unused.size() << " Kb:" << ( static_cast<float>( sizeof( Location )*unused.size() ) / 1024.0f ) << endl;
4666 		cerr << "Effect size: " << usedEffectCount << " Kb:" << ( static_cast<float>( sizeof( EffectLocation )*usedEffectCount ) / 1024.0f ) <<
4667 		" Cache: " << unusedEffect.size() << " Kb:" << ( static_cast<float>( sizeof( EffectLocation )*unusedEffect.size() ) / 1024.0f ) << endl;
4668 		accessCount = 0;
4669 	}
4670 }
4671 
4672 /// Defines the render helper that should be used (dungeon, cave, outdoor...).
4673 
setMapRenderHelper(MapRenderHelper * helper)4674 void Map::setMapRenderHelper( MapRenderHelper *helper ) {
4675 	this->helper = helper;
4676 	this->helper->setMap( this );
4677 	LIGHTMAP_ENABLED = this->helper->isLightMapEnabled();
4678 	// diff. default angle in outdoors
4679 	if ( !helper->drawShadow() ) this->yrot = settings->getMaxYRot() - 1;
4680 	//lightMapChanged = true;
4681 }
4682 
4683 /// Adds a secret door at x,y.
4684 
addSecretDoor(int x,int y)4685 void Map::addSecretDoor( int x, int y ) {
4686 	int index = y * MAP_WIDTH + x;
4687 	secretDoors[ index ] = false;
4688 	resortShapes = mapChanged = true;
4689 }
4690 
4691 /// Deletes the secret door at x,y.
4692 
removeSecretDoor(int x,int y)4693 void Map::removeSecretDoor( int x, int y ) {
4694 	int index = y * MAP_WIDTH + x;
4695 	if ( secretDoors.find( index ) != secretDoors.end() ) {
4696 		secretDoors.erase( index );
4697 		resortShapes = mapChanged = true;
4698 	}
4699 }
4700 
4701 /// Is here a secret door?
4702 
isSecretDoor(Location * pos)4703 bool Map::isSecretDoor( Location *pos ) {
4704 	return isSecretDoor( pos->x, pos->y );
4705 }
4706 
4707 /// Is there a secret door at x,y?
4708 
isSecretDoor(int x,int y)4709 bool Map::isSecretDoor( int x, int y ) {
4710 	return( secretDoors.find( y * MAP_WIDTH + x ) != secretDoors.end() ? true : false );
4711 }
4712 
4713 /// Did we detect this secret door?
4714 
isSecretDoorDetected(Location * pos)4715 bool Map::isSecretDoorDetected( Location *pos ) {
4716 	return isSecretDoorDetected( pos->x, pos->y );
4717 }
4718 
4719 /// Did we detect the secret door at x,y?
4720 
isSecretDoorDetected(int x,int y)4721 bool Map::isSecretDoorDetected( int x, int y ) {
4722 	int index = y * MAP_WIDTH + x;
4723 	if ( secretDoors.find( index ) != secretDoors.end() ) {
4724 		return secretDoors[ index ];
4725 	} else {
4726 		return false;
4727 	}
4728 }
4729 
4730 /// Mark a secret door as detected.
4731 
setSecretDoorDetected(Location * pos)4732 void Map::setSecretDoorDetected( Location *pos ) {
4733 	setSecretDoorDetected( pos->x, pos->y );
4734 }
4735 
4736 /// Mark a secret door as detected.
4737 
setSecretDoorDetected(int x,int y)4738 void Map::setSecretDoorDetected( int x, int y ) {
4739 	int index = y * MAP_WIDTH + x;
4740 	if ( secretDoors.find( index ) != secretDoors.end() ) {
4741 		secretDoors[ index ] = true;
4742 	}
4743 }
4744 
4745 /// Draws the floor/ground of the map.
4746 
renderFloor()4747 void Map::renderFloor() {
4748 	glEnable( GL_TEXTURE_2D );
4749 	glColor4f( 1.0f, 1.0f, 1.0f, 0.9f );
4750 	if ( floorTex.isSpecified() ) floorTex.glBind();
4751 	glPushMatrix();
4752 	if ( isHeightMapEnabled() ) {
4753 		if ( groundVisible || settings->isGridShowing() ) {
4754 			drawHeightMapFloor();
4755 			drawWaterLevel();
4756 		}
4757 		setupShapes( true, false );
4758 	} else {
4759 		if ( settings->isGridShowing() ) {
4760 			glDisable( GL_TEXTURE_2D );
4761 			glColor4f( 0.0f, 0.0f, 0.0f, 0.0f );
4762 		}
4763 		drawFlatFloor();
4764 	}
4765 	glPopMatrix();
4766 
4767 	// show floor in map editor
4768 	if ( settings->isGridShowing() ) {
4769 		setupShapes( true, false );
4770 	}
4771 }
4772 
4773 /// Draws the ground on outdoor maps.
4774 
drawHeightMapFloor()4775 bool Map::drawHeightMapFloor() {
4776 	CVectorTex *p[4];
4777 	float gx, gy;
4778 
4779 	bool ret = true;
4780 
4781 	glDisable( GL_CULL_FACE );
4782 	glEnable( GL_TEXTURE_2D );
4783 
4784 	int startX = ( getX() / OUTDOORS_STEP );
4785 	int startY = ( getY() / OUTDOORS_STEP );
4786 	int endX = ( ( getX() + mapViewWidth ) / OUTDOORS_STEP ) - 1;
4787 	int endY = ( ( getY() + mapViewDepth ) / OUTDOORS_STEP ) - 1;
4788 
4789 	for ( int yy = startY; yy < endY; yy++ ) {
4790 		for ( int xx = startX; xx < endX; xx++ ) {
4791 
4792 			//int chunkX = ( ( xx * OUTDOORS_STEP ) - MAP_OFFSET ) / MAP_UNIT;
4793 			//int chunkY = ( ( ( yy + 1 ) * OUTDOORS_STEP ) - ( MAP_OFFSET + 1 ) ) / MAP_UNIT;
4794 			//if( lightMap[chunkX][chunkY] ) {
4795 			groundPos[ xx ][ yy ].tex.glBind();
4796 			//} else {
4797 			//glDisable( GL_TEXTURE_2D );
4798 			//}
4799 
4800 			p[0] = &( groundPos[ xx ][ yy ] );
4801 			p[1] = &( groundPos[ xx + 1 ][ yy ] );
4802 			p[2] = &( groundPos[ xx ][ yy + 1 ] );
4803 			p[3] = &( groundPos[ xx + 1 ][ yy + 1 ] );
4804 			glBegin( GL_TRIANGLE_STRIP );
4805 			for ( int i = 0; i < 4; i++ ) {
4806 				//if( lightMap[chunkX][chunkY] ) {
4807 				glTexCoord2f( p[i]->u, p[i]->v );
4808 				glColor4f( p[i]->r, p[i]->g, p[i]->b, p[i]->a );
4809 				//} else {
4810 				//glColor4f( 0, 0, 0, 0 );
4811 				//}
4812 				gx = p[i]->x - getX() * MUL;
4813 				gy = p[i]->y - getY() * MUL;
4814 				glVertex3f( gx, gy, p[i]->z );
4815 			}
4816 			glEnd();
4817 		}
4818 	}
4819 
4820 	//glEnable( GL_DEPTH_TEST );
4821 	glDepthMask( GL_FALSE );
4822 	glEnable( GL_BLEND );
4823 	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
4824 
4825 	// draw outdoor textures
4826 	//cerr << "from: " << getX() << "," << getY() << " to: " << ( getX() + mapViewWidth ) << "," << ( getY() + mapViewDepth ) << endl;
4827 	for ( int z = 0; z < MAX_OUTDOOR_LAYER; z++ ) {
4828 		for ( int yy = startY; yy < endY; yy++ ) {
4829 			for ( int xx = startX; xx < endX; xx++ ) {
4830 				if ( outdoorTex[xx][yy][z].texture.isSpecified() ) {
4831 					drawOutdoorTex( outdoorTex[xx][yy][z].texture, xx + outdoorTex[xx][yy][z].offsetX, yy + outdoorTex[xx][yy][z].offsetY, outdoorTex[xx][yy][z].width, outdoorTex[xx][yy][z].height, outdoorTex[xx][yy][z].angle );
4832 				}
4833 			}
4834 		}
4835 	}
4836 
4837 	//glDisable( GL_DEPTH_TEST );
4838 	glDepthMask( GL_TRUE );
4839 	glDisable( GL_BLEND );
4840 
4841 	if ( DEBUG_MOUSE_POS || ( settings->isGridShowing() && gridEnabled ) ) {
4842 		//glDisable( GL_DEPTH_TEST );
4843 		glEnable( GL_BLEND );
4844 		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
4845 		glDisable( GL_TEXTURE_2D );
4846 		glColor4f( 0.4f, 0.4f, 0.4f, 0.3f );
4847 		for ( int yy = startY; yy < endY; yy++ ) {
4848 			for ( int xx = startX; xx < endX; xx++ ) {
4849 
4850 				p[0] = &( groundPos[ xx ][ yy + 1 ] );
4851 				p[1] = &( groundPos[ xx ][ yy ] );
4852 				p[2] = &( groundPos[ xx + 1 ][ yy ] );
4853 				p[3] = &( groundPos[ xx + 1 ][ yy + 1 ] );
4854 				glBegin( GL_LINE_LOOP );
4855 				for ( int i = 0; i < 4; i++ ) {
4856 					gx = p[i]->x - getX() * MUL;
4857 					gy = p[i]->y - getY() * MUL;
4858 					glVertex3f( gx, gy, p[i]->z + 0.05f * MUL );
4859 				}
4860 				glEnd();
4861 			}
4862 		}
4863 		glEnable( GL_TEXTURE_2D );
4864 		//glEnable( GL_DEPTH_TEST );
4865 		glDisable( GL_BLEND );
4866 	}
4867 
4868 	return ret;
4869 }
4870 
4871 /// Draws a ground texture on outdoor maps. Uses OUTDOORS_STEP coordinates.
4872 
drawOutdoorTex(Texture tex,float tx,float ty,float tw,float th,float angle)4873 void Map::drawOutdoorTex( Texture tex, float tx, float ty, float tw, float th, float angle ) {
4874 	tex.glBind();
4875 
4876 	glMatrixMode( GL_TEXTURE );
4877 	glPushMatrix();
4878 	glLoadIdentity();
4879 
4880 	glTranslatef( 0.5f, 0.5f, 0.0f );
4881 	glRotatef( angle, 0.0f, 0.0f, 1.0f );
4882 	glTranslatef( -0.5f, -0.5f, 0.0f );
4883 
4884 	//glTranslatef( offSX, offSY, 0 );
4885 	glMatrixMode( GL_MODELVIEW );
4886 
4887 	int sx = tx;
4888 	int sy = ty;
4889 	int ex = tx + tw;
4890 	if ( ex == sx ) ex++;
4891 	int ey = ty + th;
4892 	if ( ey == sy ) ey++;
4893 
4894 	int DIFF_Z = 0.01f * MUL;
4895 	CVectorTex *p;
4896 	for ( int xx = sx; xx < ex; xx++ ) {
4897 		for ( int yy = sy; yy < ey; yy++ ) {
4898 
4899 			float texSx = ( ( xx - sx ) ) / ( float )( ex - sx );
4900 			float texEx = ( ( xx + 1 - sx ) ) / ( float )( ex - sx );
4901 			float texSy = ( ( yy - sy ) ) / ( float )( ey - sy );
4902 			float texEy = ( ( yy + 1 - sy ) ) / ( float )( ey - sy );
4903 
4904 			//glBegin( GL_LINE_LOOP );
4905 			glBegin( GL_TRIANGLE_STRIP );
4906 
4907 			p = &groundPos[ xx ][ yy ];
4908 			glColor4f( p->r, p->g, p->b, p->a );
4909 			glTexCoord2f( texSx, texSy );
4910 			glVertex3f( p->x - getX() * MUL, p->y - getY() * MUL, p->z + DIFF_Z );
4911 
4912 			p = &groundPos[ xx + 1 ][ yy ];
4913 			glColor4f( p->r, p->g, p->b, p->a );
4914 			glTexCoord2f( texEx, texSy );
4915 			glVertex3f( p->x - getX() * MUL, p->y - getY() * MUL, p->z + DIFF_Z );
4916 
4917 			p = &groundPos[ xx ][ yy + 1 ];
4918 			glColor4f( p->r, p->g, p->b, p->a );
4919 			glTexCoord2f( texSx, texEy );
4920 			glVertex3f( p->x - getX() * MUL, p->y - getY() * MUL, p->z + DIFF_Z );
4921 
4922 			p = &groundPos[ xx + 1 ][ yy + 1 ];
4923 			glColor4f( p->r, p->g, p->b, p->a );
4924 			glTexCoord2f( texEx, texEy );
4925 			glVertex3f( p->x - getX() * MUL, p->y - getY() * MUL, p->z + DIFF_Z );
4926 
4927 			glEnd();
4928 		}
4929 	}
4930 
4931 	glMatrixMode( GL_TEXTURE );
4932 	glPopMatrix();
4933 	glMatrixMode( GL_MODELVIEW );
4934 
4935 #ifdef DEBUG_HEIGHT_MAP
4936 	debugGround( sx, sy, ex, ey );
4937 #endif
4938 }
4939 
4940 /// Draws a ground texture on outdoor maps. Uses map coordinates.
4941 
4942 /// Draw a texture on top of the ground map. This is useful for drawing shadows or
4943 /// selection circles on top of un-even terrain.
4944 
4945 #define GROUND_TEX_Z_OFFSET 0.26f
4946 
drawGroundTex(Texture tex,float tx,float ty,float tw,float th,float angle)4947 void Map::drawGroundTex( Texture tex, float tx, float ty, float tw, float th, float angle ) {
4948 
4949 	//glEnable( GL_DEPTH_TEST );
4950 	glDepthMask( GL_FALSE );
4951 	glEnable( GL_BLEND );
4952 	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
4953 	glDisable( GL_CULL_FACE );
4954 	glEnable( GL_TEXTURE_2D );
4955 	tex.glBind();
4956 
4957 	//glColor4f( 1, 0, 0, 1 );
4958 	//glDepthMask( GL_FALSE );
4959 	//glDisable( GL_DEPTH_TEST );
4960 
4961 	// which ground pos?
4962 	float sx = ( tx / static_cast<float>( OUTDOORS_STEP ) );
4963 	float sy = ( ( ty - th - 1 ) / static_cast<float>( OUTDOORS_STEP ) );
4964 	float ex = ( ( tx + tw ) / static_cast<float>( OUTDOORS_STEP ) );
4965 	float ey = ( ( ty - 1 ) / static_cast<float>( OUTDOORS_STEP ) );
4966 #ifdef DEBUG_RENDER
4967 	cerr << "s=" << sx << "," << sy << " e=" << ex << "," << ey << endl;
4968 #endif
4969 
4970 	// offset to our texture inside the ground pos
4971 	float offSX = tx - ( sx * OUTDOORS_STEP );
4972 	float offSY = ( ty - th - 1 ) - ( sy * OUTDOORS_STEP );
4973 	float offEX = offSX + tw;
4974 	float offEY = offSY + th;
4975 
4976 #ifdef DEBUG_RENDER
4977 	cerr << "tex size=" << ( ( ex - sx ) * OUTDOORS_STEP ) << "," << ( ( ey - sy ) * OUTDOORS_STEP ) << " player size=" << tw << endl;
4978 	cerr << "tex=" << ( sx * OUTDOORS_STEP ) << "," << ( sy * OUTDOORS_STEP ) << " player=" << adapter->getPlayer()->getX() << "," << adapter->getPlayer()->getY() << endl;
4979 	cerr << "offs: " << offSX << "," << offSY << " " << offEX << "," << offEY << endl;
4980 #endif
4981 
4982 	// converted to texture coordinates ( 0-1 )
4983 	offSX = -offSX / ( ( ex - sx ) * OUTDOORS_STEP );
4984 	offSY = -offSY / ( ( ey - sy ) * OUTDOORS_STEP );
4985 	offEX = 1 - ( offEX / ( ( ex - sx ) * OUTDOORS_STEP ) ) + 1;
4986 	offEY = 1 - ( offEY / ( ( ey - sy ) * OUTDOORS_STEP ) ) + 1;
4987 #ifdef DEBUG_RENDER
4988 	cerr << "\toffs: " << offSX << "," << offSY << " " << offEX << "," << offEY << endl;
4989 #endif
4990 
4991 	// don't repeat the texture
4992 	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
4993 	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
4994 
4995 	glMatrixMode( GL_TEXTURE );
4996 	glPushMatrix();
4997 	glLoadIdentity();
4998 
4999 	glTranslatef( 0.5f, 0.5f, 0.0f );
5000 	glRotatef( angle, 0.0f, 0.0f, 1.0f );
5001 	glTranslatef( -0.5f, -0.5f, 0.0f );
5002 
5003 	glTranslatef( offSX, offSY, 0.0f );
5004 	glMatrixMode( GL_MODELVIEW );
5005 
5006 
5007 	float gx, gy;
5008 	for ( int xx = static_cast<int>( sx ); xx <= static_cast<int>( ex ); xx++ ) {
5009 		for ( int yy = static_cast<int>( sy ); yy <= static_cast<int>( ey ); yy++ ) {
5010 
5011 			float texSx = ( ( xx - sx ) * ( offEX - offSX ) ) / ( ex - sx );
5012 			float texEx = ( ( xx + 1 - sx ) * ( offEX - offSX ) ) / ( ex - sx );
5013 			float texSy = ( ( yy - sy ) * ( offEY - offSY ) ) / ( ey - sy );
5014 			float texEy = ( ( yy + 1 - sy ) * ( offEY - offSY ) ) / ( ey - sy );
5015 
5016 			//glBegin( GL_LINE_LOOP );
5017 			glBegin( GL_TRIANGLE_STRIP );
5018 
5019 			glTexCoord2f( texSx, texSy );
5020 			//glColor4f( 1, 0, 0, 1 );
5021 			gx = groundPos[ xx ][ yy ].x - getX() * MUL;
5022 			gy = groundPos[ xx ][ yy ].y - getY() * MUL;
5023 			glVertex3f( gx, gy, groundPos[ xx ][ yy ].z + GROUND_TEX_Z_OFFSET * MUL );
5024 
5025 			glTexCoord2f( texEx, texSy );
5026 			//glColor4f( 1, 1, 1, 1 );
5027 			gx = groundPos[ xx + 1 ][ yy ].x - getX() * MUL;
5028 			gy = groundPos[ xx + 1 ][ yy ].y - getY() * MUL;
5029 			glVertex3f( gx, gy, groundPos[ xx + 1 ][ yy ].z + GROUND_TEX_Z_OFFSET * MUL );
5030 
5031 			glTexCoord2f( texSx, texEy );
5032 			//glColor4f( 1, 1, 1, 1 );
5033 			gx = groundPos[ xx ][ yy + 1 ].x - getX() * MUL;
5034 			gy = groundPos[ xx ][ yy + 1 ].y - getY() * MUL;
5035 			glVertex3f( gx, gy, groundPos[ xx ][ yy + 1 ].z + GROUND_TEX_Z_OFFSET * MUL );
5036 
5037 			glTexCoord2f( texEx, texEy );
5038 			//glColor4f( 1, 1, 1, 1 );
5039 			gx = groundPos[ xx + 1 ][ yy + 1 ].x - getX() * MUL;
5040 			gy = groundPos[ xx + 1 ][ yy + 1 ].y - getY() * MUL;
5041 			glVertex3f( gx, gy, groundPos[ xx + 1 ][ yy + 1 ].z + GROUND_TEX_Z_OFFSET * MUL );
5042 
5043 			glEnd();
5044 		}
5045 	}
5046 
5047 	glMatrixMode( GL_TEXTURE );
5048 	glPopMatrix();
5049 	glMatrixMode( GL_MODELVIEW );
5050 
5051 
5052 	// switch back to repeating the texture
5053 	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
5054 	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
5055 
5056 	//glDisable( GL_DEPTH_TEST );
5057 	glDepthMask( GL_TRUE );
5058 	glDisable( GL_BLEND );
5059 
5060 #ifdef DEBUG_HEIGHT_MAP
5061 	debugGround( sx, sy, ex, ey );
5062 #endif
5063 }
5064 
debugGround(int sx,int sy,int ex,int ey)5065 void Map::debugGround( int sx, int sy, int ex, int ey ) {
5066 	glDisable( GL_TEXTURE_2D );
5067 	glColor4f( 0.0f, 1.0f, 0.0f, 1.0f );
5068 	float gx, gy;
5069 	for ( int xx = sx; xx <= ex; xx++ ) {
5070 		for ( int yy = sy; yy <= ey; yy++ ) {
5071 			glBegin( GL_LINE_LOOP );
5072 			gx = groundPos[ xx ][ yy + 1 ].x - getX() * MUL;
5073 			gy = groundPos[ xx ][ yy + 1 ].y - getY() * MUL;
5074 			glVertex3f( gx, gy, groundPos[ xx ][ yy + 1 ].z + GROUND_TEX_Z_OFFSET * MUL );
5075 
5076 			gx = groundPos[ xx ][ yy ].x - getX() * MUL;
5077 			gy = groundPos[ xx ][ yy ].y - getY() * MUL;
5078 			glVertex3f( gx, gy, groundPos[ xx ][ yy ].z + GROUND_TEX_Z_OFFSET * MUL );
5079 
5080 			gx = groundPos[ xx + 1 ][ yy ].x - getX() * MUL;
5081 			gy = groundPos[ xx + 1 ][ yy ].y - getY() * MUL;
5082 			glVertex3f( gx, gy, groundPos[ xx + 1 ][ yy ].z + GROUND_TEX_Z_OFFSET * MUL );
5083 
5084 			gx = groundPos[ xx + 1 ][ yy + 1 ].x - getX() * MUL;
5085 			gy = groundPos[ xx + 1 ][ yy + 1 ].y - getY() * MUL;
5086 			glVertex3f( gx, gy, groundPos[ xx + 1 ][ yy + 1 ].z + GROUND_TEX_Z_OFFSET * MUL );
5087 
5088 			glEnd();
5089 		}
5090 	}
5091 	glEnable( GL_TEXTURE_2D );
5092 }
5093 
5094 /// Sets up the outdoor ground heightfield including texturing and lighting.
5095 
createGroundMap()5096 void Map::createGroundMap() {
5097 	float w, d, h;
5098 	for ( int xx = 0; xx < MAP_TILES_X; xx++ ) {
5099 		for ( int yy = 0; yy < MAP_TILES_Y; yy++ ) {
5100 			w = static_cast<float>( xx * OUTDOORS_STEP ) * MUL;
5101 			d = static_cast<float>( yy * OUTDOORS_STEP - 1 ) * MUL;
5102 			h = ( ground[ xx ][ yy ] ) * MUL;
5103 
5104 			groundPos[ xx ][ yy ].x = w;
5105 			groundPos[ xx ][ yy ].y = d;
5106 			groundPos[ xx ][ yy ].z = h;
5107 			//groundPos[ xx ][ yy ].u = ( xx * OUTDOORS_STEP * 32 ) / static_cast<float>(MAP_WIDTH);
5108 			//groundPos[ xx ][ yy ].v = ( yy * OUTDOORS_STEP * 32 ) / static_cast<float>(MAP_DEPTH);
5109 
5110 			groundPos[ xx ][ yy ].u = ( ( xx % OUTDOOR_FLOOR_TEX_SIZE ) / static_cast<float>( OUTDOOR_FLOOR_TEX_SIZE ) ) + ( xx / OUTDOOR_FLOOR_TEX_SIZE );
5111 			groundPos[ xx ][ yy ].v = ( ( yy % OUTDOOR_FLOOR_TEX_SIZE ) / static_cast<float>( OUTDOOR_FLOOR_TEX_SIZE ) ) + ( yy / OUTDOOR_FLOOR_TEX_SIZE );
5112 
5113 			groundPos[ xx ][ yy ].tex = groundTex[ xx ][ yy ];
5114 
5115 			// height-based light
5116 			if ( ground[ xx ][ yy ] >= 10 ) {
5117 				// ground (rock)
5118 				float n = ( h / ( 13.0f * MUL ) );
5119 				groundPos[ xx ][ yy ].r = n * 0.5f;
5120 				groundPos[ xx ][ yy ].g = n * 0.6f;
5121 				groundPos[ xx ][ yy ].b = n * 1.0f;
5122 				groundPos[ xx ][ yy ].a = 1;
5123 			} else if ( ground[ xx ][ yy ] <= -10 ) {
5124 				// water
5125 				float n = ( -h / ( 13.0f * MUL ) );
5126 				groundPos[ xx ][ yy ].r = n * 0.05f;
5127 				groundPos[ xx ][ yy ].g = n * 0.4f;
5128 				groundPos[ xx ][ yy ].b = n * 1;
5129 				groundPos[ xx ][ yy ].a = 1;
5130 			} else {
5131 				float n = ( h / ( 6.0f * MUL ) ) * 0.65f + 0.35f;
5132 				if ( Util::dice( 6 ) ) {
5133 					//groundPos[ xx ][ yy ].r = n * 0.55f;
5134 					groundPos[ xx ][ yy ].r = n;
5135 					groundPos[ xx ][ yy ].g = n;
5136 					//groundPos[ xx ][ yy ].b = n * 0.45f;
5137 					groundPos[ xx ][ yy ].b = n;
5138 					groundPos[ xx ][ yy ].a = 1;
5139 				} else {
5140 					groundPos[ xx ][ yy ].r = n;
5141 					groundPos[ xx ][ yy ].g = n;
5142 					//groundPos[ xx ][ yy ].b = n * 0.25f;
5143 					groundPos[ xx ][ yy ].b = n;
5144 					groundPos[ xx ][ yy ].a = 1;
5145 				}
5146 			}
5147 			//n++;
5148 		}
5149 	}
5150 
5151 
5152 	// add light
5153 	CVectorTex *p[3];
5154 	for ( int xx = 0; xx < MAP_TILES_X; xx++ ) {
5155 		for ( int yy = 0; yy < MAP_TILES_Y; yy++ ) {
5156 			p[0] = &( groundPos[ xx ][ yy ] );
5157 			p[1] = &( groundPos[ xx + 1 ][ yy ] );
5158 			p[2] = &( groundPos[ xx ][ yy + 1 ] );
5159 			addLight( p[0], p[1], p[2] );
5160 			addLight( p[1], p[0], p[2] );
5161 			addLight( p[2], p[0], p[1] );
5162 		}
5163 	}
5164 }
5165 
5166 /// Adds a light source.
5167 
addLight(CVectorTex * pt,CVectorTex * a,CVectorTex * b)5168 void Map::addLight( CVectorTex *pt, CVectorTex *a, CVectorTex *b ) {
5169 	float v[3], u[3], normal[3];
5170 
5171 	v[0] = pt->x - a->x;
5172 	v[1] = pt->y - a->y;
5173 	v[2] = pt->z - a->z;
5174 	Util::normalize( v );
5175 
5176 	u[0] = pt->x - b->x;
5177 	u[1] = pt->y - b->y;
5178 	u[2] = pt->z - b->z;
5179 	Util::normalize( u );
5180 
5181 	Util::cross_product( u, v, normal );
5182 	float light = Util::getLight( normal );
5183 	pt->r *= light;
5184 	pt->g *= light;
5185 	pt->b *= light;
5186 }
5187 
5188 #define WATER_MOVE_SPEED 80
5189 Uint32 waterMoveTick = 0;
5190 #define WATER_MOVE_DELTA 0.005f
5191 GLfloat waterTexX = 0;
5192 GLfloat waterTexY = 0;
5193 
5194 /// Draws the water level on outdoor maps.
5195 
drawWaterLevel()5196 void Map::drawWaterLevel() {
5197 	Uint32 t = SDL_GetTicks();
5198 	if ( t - waterMoveTick > WATER_MOVE_SPEED ) {
5199 		waterMoveTick = t;
5200 		waterTexX += WATER_MOVE_DELTA;
5201 		if ( waterTexX >= 1.0f ) waterTexX -= 1.0f;
5202 		waterTexY += WATER_MOVE_DELTA;
5203 		if ( waterTexY >= 1.0f ) waterTexY -= 1.0f;
5204 	}
5205 
5206 	glEnable( GL_TEXTURE_2D );
5207 	getShapes()->getCurrentTheme()->getOutdoorTextureGroup( WallTheme::OUTDOOR_THEME_REF_WATER )[0].glBind();
5208 	glEnable( GL_BLEND );
5209 	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
5210 	GLfloat ratio = MAP_UNIT / CAVE_CHUNK_SIZE;
5211 	float w = static_cast<float>( mapViewWidth ) * MUL;
5212 	float d = static_cast<float>( mapViewDepth ) * MUL;
5213 	//float z = -4 * MUL;
5214 	//glTranslatef( xpos2, ypos2, 0.0f);
5215 	glColor4f( 1.0f, 1.0f, 1.0f, 0.35f );
5216 // glNormal3f( 0.0f, 0.0f, 1.0f );
5217 	glBegin( GL_TRIANGLE_STRIP );
5218 	glTexCoord2f( getX() / MUL * ratio + waterTexX, getY() / MUL * ratio + waterTexY );
5219 	glVertex3f( 0.0f, 0.0f, -0.3f );
5220 	glTexCoord2f( ( getX() + mapViewWidth ) / MUL * ratio + waterTexX, getY() / MUL * ratio + waterTexY );
5221 	glVertex3f( w, 0.0f, -0.3f );
5222 	glTexCoord2f( getX() / MUL * ratio + waterTexX, ( getY() + mapViewDepth ) / MUL * ratio + waterTexY );
5223 	glVertex3f( 0.0f, d, -0.3f );
5224 	glTexCoord2f( ( getX() + mapViewWidth ) / MUL * ratio + waterTexX, ( getY() + mapViewDepth ) / MUL * ratio + waterTexY );
5225 	glVertex3f( w, d, -0.3f );
5226 	glEnd();
5227 	//glDisable( GL_BLEND );
5228 }
5229 
5230 /// Draws the indoors floor as a single quad.
5231 
drawFlatFloor()5232 void Map::drawFlatFloor() {
5233 	glDisable( GL_CULL_FACE );
5234 	GLfloat ratio = MAP_UNIT / CAVE_CHUNK_SIZE;
5235 	float w = static_cast<float>( mapViewWidth ) * MUL;
5236 	float d = static_cast<float>( mapViewDepth ) * MUL;
5237 	//glTranslatef( xpos2, ypos2, 0.0f);
5238 // glNormal3f( 0.0f, 0.0f, 1.0f );
5239 	glBegin( GL_TRIANGLE_STRIP );
5240 	glTexCoord2f( getX() / MUL * ratio, getY() / MUL * ratio );
5241 	glVertex3f( 0.0f, 0.0f, 0.0f );
5242 	glTexCoord2f( ( getX() + mapViewWidth ) / MUL * ratio, getY() / MUL * ratio );
5243 	glVertex3f( w, 0.0f, 0.0f );
5244 	glTexCoord2f( getX() / MUL * ratio, ( getY() + mapViewDepth ) / MUL * ratio );
5245 	glVertex3f( 0.0f, d, 0.0f );
5246 	glTexCoord2f( ( getX() + mapViewWidth ) / MUL * ratio, ( getY() + mapViewDepth ) / MUL * ratio );
5247 	glVertex3f( w, d, 0.0f );
5248 	glEnd();
5249 }
5250 
5251 /// Initializes the outdoor ground textures. Takes height into account.
5252 
initOutdoorsGroundTexture()5253 void Map::initOutdoorsGroundTexture() {
5254 	// set ground texture
5255 
5256 	map<int, int> texturesUsed;
5257 
5258 	int ex = MAP_TILES_X;
5259 	int ey = MAP_TILES_Y;
5260 	// ideally the below would be refs[ex][ey] but that won't work in C++... :-(
5261 	int refs[MAP_WIDTH][MAP_DEPTH];
5262 	for ( int x = 0; x < ex; x += OUTDOOR_FLOOR_TEX_SIZE ) {
5263 		for ( int y = 0; y < ey; y += OUTDOOR_FLOOR_TEX_SIZE ) {
5264 			bool high = isRockTexture( x, y );
5265 			bool low = isLakebedTexture( x, y );
5266 			// if it's both high and low, make rock texture. Otherwise mountain sides will be drawn with lakebed texture.
5267 			int r = high ? WallTheme::OUTDOOR_THEME_REF_ROCK :
5268 			        ( low ? WallTheme::OUTDOOR_THEME_REF_LAKEBED :
5269 			          WallTheme::OUTDOOR_THEME_REF_GRASS );
5270 			Texture tex = getThemeTex( r );
5271 			for ( int xx = 0; xx < OUTDOOR_FLOOR_TEX_SIZE; xx++ ) {
5272 				for ( int yy = 0; yy < OUTDOOR_FLOOR_TEX_SIZE; yy++ ) {
5273 					refs[x + xx][y + yy] = r;
5274 					setGroundTex( x + xx, y + yy, tex );
5275 				}
5276 			}
5277 		}
5278 	}
5279 
5280 	for ( int x = OUTDOOR_FLOOR_TEX_SIZE; x < ex - OUTDOOR_FLOOR_TEX_SIZE; x += OUTDOOR_FLOOR_TEX_SIZE ) {
5281 		for ( int y = OUTDOOR_FLOOR_TEX_SIZE; y < ey - OUTDOOR_FLOOR_TEX_SIZE; y += OUTDOOR_FLOOR_TEX_SIZE ) {
5282 			if ( refs[x][y] != WallTheme::OUTDOOR_THEME_REF_GRASS ) {
5283 				bool w = refs[x - OUTDOOR_FLOOR_TEX_SIZE][y] == refs[x][y] ? true : false;
5284 				bool e = refs[x + OUTDOOR_FLOOR_TEX_SIZE][y] == refs[x][y] ? true : false;
5285 				bool s = refs[x][y + OUTDOOR_FLOOR_TEX_SIZE] == refs[x][y] ? true : false;
5286 				bool n = refs[x][y - OUTDOOR_FLOOR_TEX_SIZE] == refs[x][y] ? true : false;
5287 				if ( !( w && e && s && n ) ) {
5288 					applyGrassEdges( x, y, w, e, s, n );
5289 				}
5290 			}
5291 		}
5292 	}
5293 
5294 	addHighVariation( WallTheme::OUTDOOR_THEME_REF_SNOW, GROUND_LAYER );
5295 	addHighVariation( WallTheme::OUTDOOR_THEME_REF_SNOW_BIG, GROUND_LAYER );
5296 
5297 }
5298 
5299 /// Sets up a smoothly blended grass edge.
5300 
5301 /// It takes a couple of parameters: The x,y map position and four parameters
5302 /// that specify in which direction(s) to apply the blending.
5303 
applyGrassEdges(int x,int y,bool w,bool e,bool s,bool n)5304 void Map::applyGrassEdges( int x, int y, bool w, bool e, bool s, bool n ) {
5305 	int angle = 0;
5306 	int sx = x;
5307 	int sy = y + 1 + OUTDOOR_FLOOR_TEX_SIZE;
5308 	int ref = -1;
5309 	if ( !w && !s && !e ) {
5310 		angle = 180;
5311 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_TIP;
5312 	} else if ( !e && !s && !n ) {
5313 		angle = 270;
5314 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_TIP;
5315 	} else if ( !e && !n && !w ) {
5316 		angle = 0;
5317 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_TIP;
5318 	} else if ( !w && !n && !s ) {
5319 		angle = 90;
5320 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_TIP;
5321 
5322 	} else if ( !w && !e ) {
5323 		angle = 0;
5324 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_NARROW;
5325 	} else if ( !n && !s ) {
5326 		angle = 90;
5327 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_NARROW;
5328 
5329 	} else if ( !w && !s ) {
5330 		angle = 90;
5331 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_CORNER;
5332 	} else if ( !e && !s ) {
5333 		angle = 180;
5334 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_CORNER;
5335 	} else if ( !e && !n ) {
5336 		angle = 270;
5337 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_CORNER;
5338 	} else if ( !w && !n ) {
5339 		angle = 0;
5340 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_CORNER;
5341 
5342 	} else if ( !e ) {
5343 		angle = 180;
5344 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_EDGE;
5345 	} else if ( !w ) {
5346 		angle = 0;
5347 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_EDGE;
5348 	} else if ( !n ) {
5349 		angle = 270;
5350 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_EDGE;
5351 	} else if ( !s ) {
5352 		angle = 90;
5353 		ref = WallTheme::OUTDOOR_THEME_REF_GRASS_EDGE;
5354 	}
5355 
5356 	if ( ref > -1 ) {
5357 		setOutdoorTexture( sx * OUTDOORS_STEP, sy * OUTDOORS_STEP, 0, 0, ref, angle, false, false, GROUND_LAYER );
5358 	}
5359 }
5360 
5361 /// Returns a random variation of the outdoor texture specified by ref.
5362 
getThemeTex(int ref)5363 Texture Map::getThemeTex( int ref ) {
5364 	int faceCount = getShapes()->getCurrentTheme()->getOutdoorFaceCount( ref );
5365 	Texture* textureGroup = getShapes()->getCurrentTheme()->getOutdoorTextureGroup( ref );
5366 	return textureGroup[ Util::dice( faceCount ) ];
5367 }
5368 
5369 /// Adds semi-random height variation to an outdoor map.
5370 
5371 /// Higher parts of the map are randomly selected, their height value
5372 /// set to z and textured with the referenced theme specific texture.
5373 
addHighVariation(int ref,int z)5374 void Map::addHighVariation( int ref, int z ) {
5375 	int width = getShapes()->getCurrentTheme()->getOutdoorTextureWidth( ref );
5376 	int height = getShapes()->getCurrentTheme()->getOutdoorTextureHeight( ref );
5377 	int outdoor_w = width / OUTDOORS_STEP;
5378 	int outdoor_h = height / OUTDOORS_STEP;
5379 	int ex = MAP_TILES_X;
5380 	int ey = MAP_TILES_Y;
5381 	for ( int x = 0; x < ex; x += outdoor_w ) {
5382 		for ( int y = 0; y < ey; y += outdoor_h ) {
5383 			if ( isAllHigh( x, y, outdoor_w, outdoor_h ) && !Util::dice( 10 ) &&
5384 			        !hasOutdoorTexture( x, y, width, height ) ) {
5385 				setOutdoorTexture( x * OUTDOORS_STEP, ( y + outdoor_h + 1 ) * OUTDOORS_STEP,
5386 				                   0, 0, ref, Util::dice( 4 ) * 90.0f, false, false, z );
5387 			}
5388 		}
5389 	}
5390 }
5391 
5392 /// Should a rock texture be applied to this map position due to its height?
5393 
isRockTexture(int x,int y)5394 bool Map::isRockTexture( int x, int y ) {
5395 	bool high = false;
5396 	for ( int xx = 0; xx < OUTDOOR_FLOOR_TEX_SIZE + 1; xx++ ) {
5397 		for ( int yy = 0; yy < OUTDOOR_FLOOR_TEX_SIZE + 1; yy++ ) {
5398 			if ( ground[ x + xx ][ y + yy ] > 10 ) {
5399 				high = true;
5400 				break;
5401 			}
5402 		}
5403 	}
5404 	return high;
5405 }
5406 
5407 
isLakebedTexture(int x,int y)5408 bool Map::isLakebedTexture( int x, int y ) {
5409 	bool low = false;
5410 	for ( int xx = 0; xx < OUTDOOR_FLOOR_TEX_SIZE + 1; xx++ ) {
5411 		for ( int yy = 0; yy < OUTDOOR_FLOOR_TEX_SIZE + 1; yy++ ) {
5412 			if ( ground[ x + xx ][ y + yy ] < -10 ) {
5413 				low = true;
5414 				break;
5415 			}
5416 		}
5417 	}
5418 	return low;
5419 }
5420 
5421 /// Are all map tiles in the specified area high above "sea level"?
5422 
isAllHigh(int x,int y,int w,int h)5423 bool Map::isAllHigh( int x, int y, int w, int h ) {
5424 	bool high = true;
5425 	for ( int xx = 0; xx < w + 1; xx++ ) {
5426 		for ( int yy = 0; yy < h + 1; yy++ ) {
5427 			if ( ground[ x + xx ][ y + yy ] < 10 ) {
5428 				high = false;
5429 				break;
5430 			}
5431 		}
5432 	}
5433 	return high;
5434 }
5435 
5436 /// Adds a trap of size w,h at x,y.
5437 
addTrap(int x,int y,int w,int h)5438 int Map::addTrap( int x, int y, int w, int h ) {
5439 	Trap trap;
5440 	trap.r.x = x;
5441 	trap.r.y = y;
5442 	trap.r.w = w;
5443 	trap.r.h = h;
5444 	trap.type = 0;
5445 	trap.discovered = false;
5446 	trap.enabled = true;
5447 	Uint8 trapIndex = ( Uint8 )trapList.size();
5448 	vector<CVector2*> points;
5449 	for ( int xx = x; xx < x + w; xx++ ) {
5450 		for ( int yy = y; yy < y + h; yy++ ) {
5451 			trapPos[ ( xx * ( Uint32 )MAP_WIDTH ) + yy ] = trapIndex;
5452 		}
5453 	}
5454 
5455 	// find the convex hull
5456 	for ( int xx = x; xx <= x + w; xx++ ) {
5457 		for ( int yy = y - 2; yy <= y + h - 2; yy++ ) {
5458 			if ( !isWall( static_cast<int>( xx ), static_cast<int>( yy ), 0 ) ) {
5459 				CVector2 *p = new CVector2;
5460 				p->x = xx;
5461 				p->y = yy;
5462 				points.push_back( p );
5463 			}
5464 		}
5465 	}
5466 	QuickHull::findConvexHull( &points, &( trap.hull ) );
5467 
5468 	trapList.push_back( trap );
5469 	mapChanged = true;
5470 	return trapIndex;
5471 }
5472 
5473 /// Clears the traps.
5474 
clearTraps()5475 void Map::clearTraps() {
5476 	for ( unsigned int i = 0; i < trapList.size(); i++ ) {
5477 		Trap& trap = trapList[ i ];
5478 		while ( !trap.hull.empty() ) {
5479 			delete trap.hull.back();
5480 			trap.hull.pop_back();
5481 		}
5482 	}
5483 	trapPos.clear();
5484 	trapList.clear();
5485 	trapSet.clear();
5486 	selectedTrapIndex = -1;
5487 }
5488 
5489 /// Removes the trap specified by index.
5490 
removeTrap(int trapIndex)5491 void Map::removeTrap( int trapIndex ) {
5492 	if ( static_cast<int>( trapList.size() ) > trapIndex ) {
5493 		Trap trap = trapList[ trapIndex ];
5494 		for ( int xx = trap.r.x; xx < trap.r.x + trap.r.w; xx++ ) {
5495 			for ( int yy = trap.r.y; yy < trap.r.y + trap.r.h; yy++ ) {
5496 				trapPos.erase( ( xx * ( Uint32 )MAP_WIDTH ) + yy );
5497 			}
5498 		}
5499 		trapList.erase( trapList.begin() + trapIndex );
5500 		mapChanged = true;
5501 	}
5502 }
5503 
5504 /// Returns the trap at map pos x,y, or -1 if no trap found.
5505 
getTrapAtLoc(int x,int y)5506 int Map::getTrapAtLoc( int x, int y ) {
5507 	Uint32 key = ( x * ( Uint32 )MAP_WIDTH ) + y;
5508 	if ( trapPos.find( key ) == trapPos.end() ) return -1;
5509 	else return trapPos[ key ];
5510 }
5511 
5512 /// Returns the map location of the trap specified by index.
5513 
getTrapLoc(int trapIndex)5514 Trap *Map::getTrapLoc( int trapIndex ) {
5515 	if ( static_cast<int>( trapList.size() ) <= trapIndex || trapIndex < 0 ) return NULL;
5516 	else return &( trapList[ trapIndex ] );
5517 }
5518 
5519 /// Draws the traps.
5520 
drawTraps()5521 void Map::drawTraps() {
5522 	for ( set<Uint8>::iterator i = trapSet.begin(); i != trapSet.end(); i++ ) {
5523 		Trap *trap = getTrapLoc( static_cast<int>( *i ) );
5524 
5525 		if ( trap->discovered || settings->isGridShowing() || DEBUG_TRAPS ) {
5526 
5527 			glEnable( GL_BLEND );
5528 			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
5529 			glDisable( GL_CULL_FACE );
5530 			glDisable( GL_TEXTURE_2D );
5531 
5532 			// get the color
5533 			// FIXME: colors should be ref-d from scourgeview.cpp colors
5534 			if ( !trap->enabled ) {
5535 				//ret = disabledTrapColor;
5536 				glColor4f( 0.5, 0.5, 0.5, 0.5f );
5537 			} else if ( trap->discovered ) {
5538 				if ( trap == getTrapLoc( getSelectedTrapIndex() ) ) {
5539 					//ret = outlineColor;
5540 					glColor4f( 0.3f, 0.3f, 0.5f, 0.5f );
5541 				} else {
5542 					// ret = enabledTrapColor;
5543 					glColor4f( 1.0f, 1.0f, 0.0f, 0.5f );
5544 				}
5545 			} else {
5546 				// ret = debugTrapColor;
5547 				glColor4f( 0.0f, 1.0f, 1.0f, 0.5f );
5548 			}
5549 
5550 			glLineWidth( 3 );
5551 			//glBegin( GL_POLYGON );
5552 			glBegin( GL_LINE_LOOP );
5553 			for ( unsigned int i = 0; i < trap->hull.size(); i++ ) {
5554 				CVector2 *p = trap->hull[ i ];
5555 				glVertex3f( ( p->x - getX() ) * MUL, ( p->y - getY() ) * MUL, 0.5f * MUL );
5556 			}
5557 			glEnd();
5558 			glLineWidth( 1 );
5559 			glEnable( GL_TEXTURE_2D );
5560 			//glDisable( GL_BLEND );
5561 		}
5562 	}
5563 }
5564 
5565 /// Is it safe to put the shape on this map tile?
5566 
canFit(int x,int y,Shape * shape)5567 bool Map::canFit( int x, int y, Shape *shape ) {
5568 	if ( x < MAP_OFFSET || x >= MAP_WIDTH - MAP_OFFSET ||
5569 	        y < MAP_OFFSET || y >= MAP_DEPTH - MAP_OFFSET ) {
5570 		return false;
5571 	}
5572 	int fx = ( ( x - MAP_OFFSET ) / MAP_UNIT ) * MAP_UNIT + MAP_OFFSET;
5573 	int fy = ( ( y - MAP_OFFSET ) / MAP_UNIT ) * MAP_UNIT + MAP_OFFSET + MAP_UNIT;
5574 	if ( isHeightMapEnabled() ) {
5575 		int gx = fx / OUTDOORS_STEP;
5576 		int gy = fy / OUTDOORS_STEP;
5577 		if ( ground[ gx ][ gy ] < 10 && ground[ gx ][ gy ] > -10 ) {
5578 			return( !isBlocked( x, y, 0, 0, 0, 0, shape, NULL ) ? true : false );
5579 		}
5580 	} else {
5581 		Shape *floor = floorPositions[fx][fy];
5582 		if ( floor ) {
5583 			return( !isBlocked( x, y, 0, 0, 0, 0, shape, NULL ) ? true : false );
5584 		}
5585 	}
5586 	return false;
5587 }
5588 
5589 /// Nothing on this map tile?
5590 
isEmpty(int x,int y)5591 bool Map::isEmpty( int x, int y ) {
5592 	if ( x < MAP_OFFSET || x >= MAP_WIDTH - MAP_OFFSET ||
5593 	        y < MAP_OFFSET || y >= MAP_DEPTH - MAP_OFFSET ) {
5594 		return false;
5595 	}
5596 	return( getLocation( x, y, 0 ) == NULL ? true : false );
5597 }
5598 
inMapEditor()5599 bool Map::inMapEditor() {
5600 	return settings->isGridShowing();
5601 }
5602 
5603 /// Determines which type of weather the map will have.
5604 
generateWeather()5605 int Map::generateWeather() {
5606 	if ( Util::dice( 3 ) == 0 && heightMapEnabled ) {
5607 		weather = Util::pickOne( 1, MAX_WEATHER );
5608 	} else {
5609 		weather = WEATHER_CLEAR;
5610 	}
5611 	return weather;
5612 }
5613 
5614 /// Sets whether house roofs should be shown.
5615 
setRoofShowing(bool b)5616 void Map::setRoofShowing( bool b ) {
5617 	isRoofShowing = b;
5618 	mapChanged = true;
5619 }
5620 
5621 /// Forces a refresh of the map state.
5622 
refresh()5623 void Map::refresh() {
5624 	mapChanged = lightMapChanged = resortShapes = true;
5625 }
5626