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