1 /*
2  * PROPRIETARY INFORMATION.  This software is proprietary to POWDER
3  * Development, and is not to be reproduced, transmitted, or disclosed
4  * in any way without written permission.
5  *
6  * Produced by:	Jeff Lait
7  *
8  *      	POWDER Development
9  *
10  * NAME:        build.cpp ( POWDER Library, C++ )
11  *
12  * COMMENTS:
13  *	build.cpp contains all the routines used to build dungeon levels.
14  *	These are as many and varied as are the types of levels that can
15  *	be built.
16  *
17  *	The major hook is MAP::build, which is where the level layout
18  *	is determined and used for determining which generator should
19  *	be applied.
20  */
21 
22 #include <stdio.h>
23 #include <math.h>
24 #include "mygba.h"
25 #include "map.h"
26 #include "msg.h"
27 #include "rand.h"
28 #include "assert.h"
29 #include "creature.h"
30 #include "item.h"
31 #include "victory.h"
32 
33 #include "rooms/allrooms.h"
34 
35 void
clear()36 MAP::clear()
37 {
38     memset(mySquares, 0, MAP_WIDTH * MAP_HEIGHT);
39     memset(myFlags, 0, MAP_WIDTH * MAP_HEIGHT);
40     //memset(myFlags, SQUAREFLAG_LIT, MAP_WIDTH * MAP_HEIGHT);
41     //memset(myFlags, SQUAREFLAG_MAPPED, MAP_WIDTH * MAP_HEIGHT);
42 
43     MOB		*mob, *mnext;
44     ITEM	*item, *inext;
45 
46     for (mob = myMobHead; mob; mob = mnext)
47     {
48 	mnext = mob->getNext();
49 	mob->setNext(0);
50 	delete mob;
51     }
52     myMobHead = 0;
53     myMobCount = 0;
54 
55     for (item = myItemHead; item; item = inext)
56     {
57 	inext = item->getNext();
58 	item->setNext(0);
59 	delete item;
60     }
61     myItemHead = 0;
62     myItemCount = 0;
63 }
64 
65 void
build(bool alreadysavedseed)66 MAP::build(bool alreadysavedseed)
67 {
68     // Save our random seed used...
69     if (!alreadysavedseed)
70 	mySeed = rand_getseed();
71 
72     // Empty the map...
73     clear();
74 
75     // Clear out our wall map.
76     memset(ourWallMap, 0, MAP_WIDTH * MAP_HEIGHT);
77 
78     // Decide what sort of map to build.  If the current level is 10
79     // we do the big room.
80     // Rough map:
81     // 0: Surface World.
82     // 1-5: Roguelikes.
83     // 6-7: Lit QIX.
84     // 8-9: Unlit QIX.
85     // 10: Big Room
86     // 11-14: Mazes
87     // 15: Cretan Minotaur
88     // 16-17: Lit cavern
89     // 18: Dark cavern
90     // 19-20: Dark water cavern
91     // 21: Dark cavern, Belweir
92     // 22: Quizar
93     // 23: Hruth
94     // 24: Klaskov
95     // 25: Circles of Hell.
96     if ((0))
97     {
98 	// Stress test all room generators.
99 	static int ct = 0;
100 	ct++;
101 	switch (ct % 9)
102 	{
103 	    case 0:
104 		buildRoguelike();
105 		break;
106 	    case 1:
107 		buildDrunkenCavern(true, false, true, true);
108 		break;
109 	    case 2:
110 	    {
111 		static int	ct2 = 0;
112 
113 		ct2++;
114 		switch (ct2 % 5)
115 		{
116 		    case 0:
117 			// We really don't want a crash here!
118 			buildSurfaceWorld();
119 			break;
120 		    case 1:
121 			buildQuest(GOD_ROGUE);
122 			break;
123 		    case 2:
124 			buildQuest(GOD_BARB);
125 			break;
126 		    case 3:
127 			buildQuest(GOD_FIGHTER);
128 			break;
129 		    case 4:
130 			buildTutorial();
131 			break;
132 		}
133 		break;
134 	    }
135 	    case 3:
136 		buildQix(true);
137 		break;
138 	    case 4:
139 		// This breaks, I eat my shoe.
140 		buildBigRoom();
141 		break;
142 	    case 5:
143 		buildMaze(rand_choice(4)+1);
144 		break;
145 	    case 6:
146 		buildRiverRoom();
147 		break;
148 	    case 7:
149 		buildHello();
150 		break;
151 	    case 8:
152 		buildSpaceShip();
153 		break;
154 	}
155 
156 	if (!(ct & 127))
157 	    printf("Current room count %d\n", ct);
158     }
159     else if (glbStressTest && !glbMapStats)
160     {
161 	// Pick a random depth.
162 	myDepth = rand_range(0, 25);
163 	// Pick a random branch... Currently only tridude supported.
164 	if (!rand_choice(15))
165 	    myBranch = BRANCH_TRIDUDE;
166 	else
167 	    myBranch = BRANCH_MAIN;
168     }
169 
170     if (glbTutorial)
171     {
172 	buildTutorial();
173 	return;
174     }
175 
176     if (branchName() == BRANCH_TRIDUDE)
177     {
178 	// Simple enough.
179 	buildSpaceShip();
180 	return;
181     }
182 
183     if (myDepth == 0)
184     {
185 	buildSurfaceWorld();
186     }
187     else if (myDepth <= 5)
188     {
189 	//buildQix(true);
190 	//buildQuest(GOD_ROGUE);
191 	buildRoguelike();
192     }
193     else if (myDepth >= 6 && myDepth <= 7)
194     {
195 	buildQix(true);
196     }
197     else if (myDepth >= 8 && myDepth <= 9)
198     {
199 	buildQix(false);
200     }
201     else if (myDepth == 10)
202     {
203 	buildBigRoom();
204     }
205     else if (myDepth >= 11 && myDepth <= 15)
206     {
207 	buildMaze(rand_choice(4)+1);
208     }
209     else if (myDepth >= 16 && myDepth <= 17)
210     {
211 	buildDrunkenCavern(true, false, true, true);
212     }
213     else if (myDepth >= 18 && myDepth <= 18)
214     {
215 	buildDrunkenCavern(false, false, true, true);
216     }
217     else if (myDepth >= 19 && myDepth <= 20)
218     {
219 	buildDrunkenCavern(false, true, true, true);
220 	// If depth is 20, we want hard floors to prevent cheating!
221 	myGlobalFlags &= ~MAPFLAG_DIG;
222     }
223     else if (myDepth == 21)
224     {
225 	// Belweir's library is here!
226 	buildDrunkenCavern(false, false, true, true);
227     }
228     else if (myDepth == 22)
229     {
230 	buildQuest(GOD_ROGUE);
231     }
232     else if (myDepth == 23)
233     {
234 	buildQuest(GOD_BARB);
235     }
236     else if (myDepth == 24)
237     {
238 	buildQuest(GOD_FIGHTER);
239     }
240     else if (myDepth == 25)
241     {
242 	buildHello();
243     }
244     else
245     {
246 	// This should not happen.
247 	buildRoguelike();
248     }
249 
250     // Build some random traps.
251     int 	numtraps, i;
252 
253     numtraps = rand_choice(myDepth);
254     for (i = 0; i < numtraps; i++)
255     {
256 	int		tries = 10;
257 	int		x, y;
258 
259 	while (tries--)
260 	{
261 	    if (!findRandomLoc(x, y, MOVE_WALK,
262 			    true, false, false, false, false, false))
263 	    {
264 		tries = 0;
265 		break;
266 	    }
267 
268 	    if (createTrap(x, y, 0))
269 		break;
270 	}
271 	// Failed to find after 10 tries? Give up on all traps!
272 	if (!tries)
273 	    break;
274     }
275 
276     // Verify we have appropriate up and down stairs
277 #ifdef USEASSERT
278     if (myDepth)
279     {
280 	int x, y;
281 	UT_ASSERT(findTile(SQUARE_LADDERUP, x, y));
282     }
283     if (myDepth < 25)
284     {
285 	int x, y;
286 	UT_ASSERT(findTile(SQUARE_LADDERDOWN, x, y));
287     }
288 #endif
289 }
290 
291 void
roomToMapCoords(int & x,int & y,const ROOM & room,int rx,int ry)292 MAP::roomToMapCoords(int &x, int &y, const ROOM &room, int rx, int ry)
293 {
294     int		tmp;
295 
296     if (room.rotation & ROOM_FLIP_X)
297 	x = room.definition->size.x - rx - 1;
298     else
299 	x = rx;
300     if (room.rotation & ROOM_FLIP_Y)
301 	y = room.definition->size.y - ry - 1;
302     else
303 	y = ry;
304     if (room.rotation & ROOM_FLOP)
305     {
306 	tmp = x;
307 	x = y;
308 	y = tmp;
309     }
310     x += room.l.x;
311     y += room.l.y;
312 }
313 
314 void
mapToRoomCoords(int & rx,int & ry,const ROOM & room,int x,int y)315 MAP::mapToRoomCoords(int &rx, int &ry, const ROOM &room, int x, int y)
316 {
317     int		tmp;
318 
319     x -= room.l.x;
320     y -= room.l.y;
321     if (room.rotation & ROOM_FLOP)
322     {
323 	tmp = x;
324 	x = y;
325 	y = tmp;
326     }
327     if (room.rotation & ROOM_FLIP_Y)
328 	ry = room.definition->size.y - y - 1;
329     else
330 	ry = y;
331     if (room.rotation & ROOM_FLIP_X)
332 	rx = room.definition->size.x - x - 1;
333     else
334 	rx = x;
335 }
336 
337 // Draws a walled room using the inclusive rectangle specified.
338 void
drawRoom(const ROOM & room,bool lit,bool mazetype,bool caverntype)339 MAP::drawRoom(const ROOM &room, bool lit, bool mazetype, bool caverntype)
340 {
341     int		x, y;
342 
343     if (room.definition)
344     {
345 	int		squarenum;
346 	int		rx, ry, i;
347 
348 	squarenum = 0;
349 	for (ry = 0; ry < room.definition->size.y; ry++)
350 	{
351 	    for (rx = 0; rx < room.definition->size.x; rx++)
352 	    {
353 		roomToMapCoords(x, y, room, rx, ry);
354 		ourWallMap[y * MAP_WIDTH + x] = 1;
355 		SQUARE_NAMES		square;
356 
357 		square = room.definition->squarelist[squarenum];
358 		if (caverntype && square == SQUARE_WALL)
359 		    square = SQUARE_EMPTY;
360 		if (caverntype && square == SQUARE_FLOOR)
361 		    square = SQUARE_CORRIDOR;
362 		if (mazetype && square == SQUARE_CORRIDOR)
363 		    square = SQUARE_FLOOR;
364 		if (mazetype && square == SQUARE_EMPTY)
365 		    square = SQUARE_WALL;
366 		setTile(x, y, square);
367 
368 		squarenum++;
369 	    }
370 	}
371 
372 	for (i = 0; room.definition->squareflaglist[i].data != SQUAREFLAG_NONE;
373 		i++)
374 	{
375 	    rx = room.definition->squareflaglist[i].x;
376 	    ry = room.definition->squareflaglist[i].y;
377 	    roomToMapCoords(x, y, room, rx, ry);
378 	    setFlag(x, y, room.definition->squareflaglist[i].data);
379 	}
380 
381 	return;
382     }
383 
384     for (y = room.l.y; y <= room.h.y; y++)
385     {
386         for (x = room.l.x; x <= room.h.x; x++)
387 	{
388 	    // Mark the entire room in the wall map.
389 	    ourWallMap[y * MAP_WIDTH + x] = 1;
390 
391 	    if (x == room.l.x || x == room.h.x ||
392 		y == room.l.y || y == room.h.y)
393 	    {
394 		// Draw a wall.
395 		setTile(x, y, caverntype ? SQUARE_EMPTY : SQUARE_WALL);
396 		setFlag(x, y, SQUAREFLAG_LIT, lit);
397 	    }
398 	    else
399 	    {
400 		// Draw the floor.
401 		setTile(x, y, caverntype ? SQUARE_CORRIDOR : SQUARE_FLOOR);
402 		setFlag(x, y, SQUAREFLAG_LIT, lit);
403 	    }
404 	}
405     }
406 }
407 
408 bool
isMapExit(int x,int y) const409 MAP::isMapExit(int x, int y) const
410 {
411     UT_ASSERT(x >= 0 && x <= MAP_WIDTH);
412     UT_ASSERT(y >= 0 && y <= MAP_HEIGHT);
413     switch (getTile(x, y))
414     {
415 	case SQUARE_DOOR:
416 	case SQUARE_BLOCKEDDOOR:
417 	case SQUARE_SECRETDOOR:
418 	case SQUARE_CORRIDOR:
419 	case SQUARE_FLOOR:
420 	case SQUARE_OPENDOOR:
421 	    return true;
422 	default:
423 	    break;
424     }
425     return false;
426 }
427 
428 bool
isRoomExit(const ROOM & room,int x,int y) const429 MAP::isRoomExit(const ROOM &room, int x, int y) const
430 {
431     UT_ASSERT(x >= 0 && x < room.definition->size.x);
432     UT_ASSERT(y >= 0 && y < room.definition->size.y);
433 
434     UT_ASSERT(room.definition != 0);
435 
436     switch (room.definition->squarelist[x + y * room.definition->size.x])
437     {
438 	case SQUARE_DOOR:
439 	case SQUARE_BLOCKEDDOOR:
440 	case SQUARE_SECRETDOOR:
441 	case SQUARE_CORRIDOR:
442 	case SQUARE_FLOOR:
443 	case SQUARE_OPENDOOR:
444 	    return true;
445 
446 	case SQUARE_WATER:
447 	case SQUARE_LAVA:
448 	case SQUARE_ACID:
449 	default:
450 	    return false;
451     }
452 }
453 
454 bool
isMapPathway(int x,int y) const455 MAP::isMapPathway(int x, int y) const
456 {
457     if (x < 0 || x >= MAP_WIDTH)
458 	return false;
459     if (y < 0 || y >= MAP_HEIGHT)
460 	return false;
461 
462     switch (getTile(x, y))
463     {
464 	case SQUARE_CORRIDOR:
465 	case SQUARE_FLOOR:
466 	case SQUARE_WATER:
467 	case SQUARE_LAVA:
468 	case SQUARE_ACID:
469 	    return true;
470 	default:
471 	    break;
472     }
473     return false;
474 }
475 
476 bool
isTileWall(int tile) const477 MAP::isTileWall(int tile) const
478 {
479     switch (tile)
480     {
481 	case SQUARE_WALL:
482 	case SQUARE_DOOR:
483 	case SQUARE_BLOCKEDDOOR:
484 	case SQUARE_SECRETDOOR:
485 	case SQUARE_OPENDOOR:
486 	    return true;
487 	default:
488 	    break;
489     }
490     return false;
491 }
492 
493 bool
isMapWall(int x,int y) const494 MAP::isMapWall(int x, int y) const
495 {
496     // The -1 for width & hieght here is to intentionally leave a 1 space
497     // border on the edges so we can scroll past the edges.
498     if (x < 0 || x >= MAP_WIDTH-1)
499 	return true;
500     if (y < 0 || y >= MAP_HEIGHT-1)
501 	return true;
502 
503     if (ourWallMap[y * MAP_WIDTH + x])
504 	return true;
505 
506     return false;
507 }
508 
509 //
510 // Logic for setting a tile on a path...
511 //
512 void
setPathTile(int x,int y,bool lit,bool usefloor)513 MAP::setPathTile(int x, int y, bool lit, bool usefloor)
514 {
515     SQUARE_NAMES	oldtile, newtile;
516 
517     oldtile = getTile(x, y);
518     newtile = oldtile;		// Do nothing by default.
519     switch (oldtile)
520     {
521 	case SQUARE_EMPTY:
522 	    if (usefloor)
523 		newtile = SQUARE_FLOOR;
524 	    else
525 		newtile = SQUARE_CORRIDOR;
526 	    break;
527 	case SQUARE_FLOOR:
528 	    newtile = SQUARE_FLOOR;
529 	    break;
530 	case SQUARE_CORRIDOR:
531 	    if (usefloor)
532 		newtile = SQUARE_FLOOR;
533 	    else
534 		newtile = SQUARE_CORRIDOR;
535 	    break;
536 	case SQUARE_BLOCKEDDOOR:
537 	    // We only have two choices here.  We don't want to
538 	    // turn into a corridor or normal door or we allow creatures
539 	    // to escape.  Secret door is acceptable as creatures don't
540 	    // yet search by default
541 	    switch (rand_choice(6))
542 	    {
543 		case 0:
544 		case 1:
545 		case 2:
546 		case 3:
547 		case 4:
548 		    newtile = SQUARE_BLOCKEDDOOR;
549 		    lit = true;
550 		    break;
551 		case 5:
552 		    newtile = SQUARE_SECRETDOOR;
553 		    // Inherit existing lit flag.
554 		    lit = getFlag(x, y, SQUAREFLAG_LIT);
555 		    break;
556 	    }
557 
558 	    break;
559 	case SQUARE_DOOR:
560 	case SQUARE_WALL:
561 	case SQUARE_SECRETDOOR:
562 	case SQUARE_OPENDOOR:
563 	    // We have several choices:
564 	    // 1) Secret door
565 	    // 2) Door
566 	    // 3) No door.
567 	    switch (rand_choice(6))
568 	    {
569 		case 0:
570 		case 1:
571 		case 2:
572 		    newtile = SQUARE_DOOR;
573 		    lit = true;
574 		    break;
575 		case 3:
576 		    newtile = SQUARE_OPENDOOR;
577 		    lit = true;
578 		    break;
579 		case 4:
580 		    if (usefloor)
581 			newtile = SQUARE_FLOOR;
582 		    else
583 			newtile = SQUARE_CORRIDOR;
584 		    break;
585 		case 5:
586 		    newtile = SQUARE_SECRETDOOR;
587 		    // Inherit existing lit flag.
588 		    lit = getFlag(x, y, SQUAREFLAG_LIT);
589 		    break;
590 	    }
591 	    break;
592 	// Error checking:
593 	default:
594 	    newtile = SQUARE_WATER;
595 	    lit = true;
596 	    break;
597     }
598 
599     // Set the tile.
600     setTile(x, y, newtile);
601     setFlag(x, y, SQUAREFLAG_LIT, lit);
602 }
603 
604 //
605 // This initializes ourDistanceMap with the distance to go, including
606 // tunnelling as allowed.  Thus, if isMapWall, we can't go through it.
607 // If we must tunnel, distance is 2.
608 // We return false if there is no path (should not happen!)
609 //
610 #define STACK_SIZE 2048
611 
612 bool
buildMoveDistance(int px,int py,MOVE_NAMES move)613 MAP::buildMoveDistance(int px, int py, MOVE_NAMES move)
614 {
615     s8		stackx[STACK_SIZE], stacky[STACK_SIZE];
616     int		stackdepth = 0, stackbot = 0, stacktop = 0;
617     int		dist, nextdist;
618     int		dx, dy, x, y;
619 
620     // Initialize to infinity.
621     memset(ourDistanceMap, 0xff, MAP_WIDTH * MAP_HEIGHT * sizeof(u16));
622 
623     // Zero distance to self.
624     ourDistanceMap[py * MAP_WIDTH + px] = 0;
625 
626     // Add to our distance stack...
627     stackx[stacktop] = px;
628     stacky[stacktop] = py;
629     stacktop++;
630     stackdepth++;
631     stacktop &= STACK_SIZE-1;
632 
633     // And recurse...
634     while (stackdepth)
635     {
636 	// Pull top guy off the stack...
637 	stackdepth--;
638 	px = stackx[stackbot];
639 	py = stacky[stackbot];
640 	stackbot++;
641 
642 	// Now, see if anyone will have a shorter distance if they came
643 	// from here...
644 	dist = ourDistanceMap[py * MAP_WIDTH + px];
645 
646 	FORALL_4DIR(dx, dy)
647 	{
648 	    y = py + dy;
649 	    if (y < 0)
650 		continue;
651 	    if (y > MAP_HEIGHT-1)
652 		continue;
653 
654 	    x = px + dx;
655 	    if (x < 0)
656 		continue;
657 	    if (x > MAP_WIDTH-1)
658 		continue;
659 
660 	    // See if we can go there...
661 	    if (// canMove(x, y, move, true, true, true) ||
662 		(glb_squaredefs[getTile(x, y)].movetype & move) ||
663 		getTile(x, y) == SQUARE_SECRETDOOR ||
664 		getTile(x, y) == SQUARE_DOOR ||
665 		getTile(x, y) == SQUARE_BLOCKEDDOOR)
666 	    {
667 		nextdist = 1;
668 		nextdist += dist;
669 		// Check if it is strictly faster...
670 		if (nextdist < ourDistanceMap[y * MAP_WIDTH + x])
671 		{
672 		    // Store it.
673 		    ourDistanceMap[y * MAP_WIDTH + x] = nextdist;
674 		    // Add to our stack.
675 		    stackx[stacktop] = x;
676 		    stacky[stacktop] = y;
677 		    stackdepth++;
678 		    stacktop++;
679 		    stacktop &= STACK_SIZE-1;
680 		    UT_ASSERT(stackdepth < STACK_SIZE-2);
681 		}
682 	    }
683 	}
684     }
685 
686     return true;
687 }
688 
689 bool
buildDistance(int px,int py)690 MAP::buildDistance(int px, int py)
691 {
692     s8		stackx[STACK_SIZE], stacky[STACK_SIZE];
693     int		stackdepth = 0;
694     u16		dist, nextdist;
695     int		dx, dy, x, y;
696 
697     // Initialize to infinity.
698     memset(ourDistanceMap, 0xff, MAP_WIDTH * MAP_HEIGHT * sizeof(u16));
699 
700     // Zero distance to self.
701     ourDistanceMap[py * MAP_WIDTH + py] = 0;
702 
703     // Add to our distance stack...
704     stackx[stackdepth] = px;
705     stacky[stackdepth] = py;
706     stackdepth++;
707 
708     // And recurse...
709     while (stackdepth)
710     {
711 	// Pull top guy off the stack...
712 	stackdepth--;
713 	px = stackx[stackdepth];
714 	py = stacky[stackdepth];
715 
716 	// Now, see if anyone will have a shorter distance if they came
717 	// from here...
718 	dist = ourDistanceMap[py * MAP_WIDTH + px];
719 
720 	for (dy = -1; dy <= 1; dy++)
721 	{
722 	    y = py + dy;
723 	    if (y < 0)
724 		continue;
725 	    if (y >= MAP_HEIGHT-1)
726 		break;
727 	    for (dx = -1; dx <= 1; dx++)
728 	    {
729 		if (!dx && !dy)
730 		    continue;
731 
732 		x = px + dx;
733 		if (x < 0)
734 		    continue;
735 		if (x >= MAP_WIDTH-1)
736 		    break;
737 
738 		// See if we can go there...
739 		if (!isMapWall(x, y))
740 		{
741 		    nextdist = (getTile(x, y) == SQUARE_EMPTY) ? 2 : 1;
742 		    //nextdist = 1;
743 		    nextdist += dist;
744 		    // Check if it is strictly faster...
745 		    if (nextdist < ourDistanceMap[y * MAP_WIDTH + x])
746 		    {
747 			// Store it.
748 			ourDistanceMap[y * MAP_WIDTH + x] = nextdist;
749 			// Add to our stack.
750 			stackx[stackdepth] = x;
751 			stacky[stackdepth] = y;
752 			stackdepth++;
753 			UT_ASSERT(stackdepth < STACK_SIZE);
754 		    }
755 		}
756 	    }
757 	}
758     }
759 
760     return true;
761 }
762 
763 //
764 // Draw path using a distance function.
765 //
766 bool
drawPathStraight(int sx,int sy,int ex,int ey,bool lit)767 MAP::drawPathStraight(int sx, int sy, int ex, int ey, bool lit)
768 {
769     u16		dist, dp, dn;
770     int		d[2];
771     int		dir;
772     int		s[2], e[2];
773     int		rev = 0;
774 
775     buildDistance(ex, ey);
776     UT_ASSERT(!"Got to draw...");
777 
778     // Check to see if a path is possible....
779     if (ourDistanceMap[sy * MAP_WIDTH + sx] == 0xffff)
780     {
781 	UT_ASSERT(!"Impossibly map!");
782 	return false;
783     }
784 
785     s[0] = sx;
786     s[1] = sy;
787     e[0] = ex;
788     e[1] = ey;
789 
790     // Initial random direction.
791     dir = rand_choice(2);
792 
793     setPathTile(s[0], s[1], lit, false);
794 
795     while (s[0] != e[0] || s[1] != e[1])
796     {
797 	// Determine which way is down in dir....
798 	dist = ourDistanceMap[s[1] * MAP_WIDTH + s[0]];
799 
800 	d[!dir] = 0;
801 	dp = 0xffff;
802 	dn = 0xffff;
803 	if ((s[dir] + 1) < MAP_WIDTH)
804 	{
805 	    d[dir] = 1;
806 	    dp = ourDistanceMap[(s[1] + d[1]) * MAP_WIDTH + s[0] + d[0]];
807 	}
808 	if ((s[dir] - 1) >= 0)
809 	{
810 	    d[dir] = -1;
811 	    dn = ourDistanceMap[(s[1] + d[1]) * MAP_WIDTH + s[0] + d[0]];
812 	}
813 	if (dp >= dist && dn >= dist)
814 	{
815 	    // No one has a faster distance!  Change direction.
816 	    UT_ASSERT(!rev);
817 	    rev++;
818 	    dir = !dir;
819 	    continue;
820 	}
821 	if (dp < dist && dn < dist)
822 	{
823 	    // Both are shorter, this is a cusp which by definition
824 	    // cannot last.  Pick a random direction.
825 	    d[dir] = rand_sign();
826 	}
827 	else if (dp < dist)
828 	{
829 	    d[dir] = 1;
830 	}
831 	else
832 	{
833 	    UT_ASSERT(dn < dist);
834 	    // Dn is smallest.
835 	    d[dir] = -1;
836 	}
837 	rev = 0;
838 
839 	// Go in this direction until it is unprofitable...
840 	while (s[0] != e[0] || s[1] != e[1])
841 	{
842 	    setPathTile(s[0], s[1], lit, false);
843 	    s[0] += d[0];
844 	    s[1] += d[1];
845 
846 	    // Check if next tile is good...
847 	    dist = ourDistanceMap[s[1] * MAP_WIDTH + s[0]];
848 
849 	    if (s[dir] + d[dir] < 0)
850 		break;
851 	    if (s[dir] + d[dir] >= MAP_WIDTH)
852 		break;
853 	    if (ourDistanceMap[(s[1]+d[1]) * MAP_WIDTH + s[0] + d[0]] >= dist)
854 	    {
855 		// No longer profitable, stop moving this way!
856 		break;
857 	    }
858 	}
859     }
860     setPathTile(s[0], s[1], lit, false);
861     return true;
862 }
863 
864 //
865 // Draw a path from the given location to the given location.
866 // This will avoid walls.
867 // It will return if it failed.  It shouldn't, but shit happens.
868 //
869 bool
drawPath(int sx,int sy,int ex,int ey,bool lit)870 MAP::drawPath(int sx, int sy, int ex, int ey, bool lit)
871 {
872     int		d[2];
873     int		i, dir, wallhit;
874     int		s[2], e[2], n[2];
875     int		len = 0;
876     int		ncnt = 0;
877 
878     s[0] = sx;
879     s[1] = sy;
880     e[0] = ex;
881     e[1] = ey;
882 
883     // Initial random direction.
884     dir = rand_choice(2);
885 
886     setPathTile(s[0], s[1], lit, false);
887 
888     while (s[0] != e[0] || s[1] != e[1])
889     {
890 	// Find our current direction to go...
891 	for (i = 0; i < 2; i++)
892 	    d[i] = SIGN(e[i] - s[i]);
893 
894 	// Switch our direction.
895 	dir = !dir;
896 
897 	// If we are aligned in that direction, try the opposite.
898 	if (!d[dir])
899 	    dir = !dir;
900 
901 	// Move in the direction dir until a random chance fails,
902 	// or we get aligned with a destination.
903 	wallhit = 0;
904 	while (1)
905 	{
906 	    if (rand_chance(40))
907 	    {
908 		// Stop digging!
909 		break;
910 	    }
911 
912 	    // Alignment...  This only counts for straight moves,
913 	    // if bounced off a wall we don't want to trigger this.
914 	    if (!wallhit && s[dir] == e[dir])
915 		break;
916 
917 	    // Calculate the next pos...
918 	    n[0] = s[0];
919 	    n[1] = s[1];
920 	    n[dir] += d[dir];
921 	    // Check if our current dig direction is valid...
922 	    ncnt = 0;
923 	    while (isMapWall(n[0], n[1]))
924 	    {
925 		// Not a valid dig direction!  Rotate 90 degrees
926 		// and go in a random direction...
927 		// As we never build walls adjacent and they are square,
928 		// this guarantees a non-wall.
929 		// Except it doesn't.  WTF?
930 		dir = !dir;
931 		if (!d[dir])
932 		    d[dir] = rand_sign();
933 		n[0] = s[0];
934 		n[1] = s[1];
935 		n[dir] += d[dir];
936 		wallhit = 1;
937 		ncnt++;
938 		if (ncnt > 100)
939 		{
940 		    // THis fails when we hit the bottom most wall,
941 		    // turn to the left,
942 		    // and then hit the left wall prior to deciding to reset
943 		    // NOte that alignment will never trigger as wallhit
944 		    // is true, and we could have been going the wrong
945 		    // way anyways.
946 		    // Our abort here is reasonable enough.
947 		    return false;
948 		}
949 	    }
950 	    // Move in the desired direction...
951 	    s[dir] += d[dir];
952 	    // Write out the path tile...
953 	    setPathTile(s[0], s[1], lit, false);
954 
955 	    len++;
956 	    if (len > 100)
957 		return false;
958 	}
959     }
960     return true;
961 }
962 
963 void
drawPath_old(int sx,int sy,int ex,int ey,bool lit)964 MAP::drawPath_old(int sx, int sy, int ex, int ey, bool lit)
965 {
966     int		dx = 0, dy = 0, dir, dirlen;
967 
968     dir = 0;		// 1 is y, -1 is x.
969     while (sx != ex || sy != ey)
970     {
971 	// Choose our direction...
972 	dx = rand_sign();
973 	if (sx < ex)
974 	    dx = 1;
975 	else if (sx > ex)
976 	    dx = -1;
977 	dy = rand_sign();
978 	if (sy < ey)
979 	    dy = 1;
980 	else if (sy > ey)
981 	    dy = -1;
982 
983 	// Every so often the builder is drunk...
984 	if ((0)) // rand_chance(30)))
985 	{
986 	    dx *= -1;
987 	    dy *= -1;
988 	}
989 
990 	if (dir != 0)
991 	    dir *= -1;
992 	else
993 	{
994 	    if (dx && dy)
995 	    {
996 		dir = rand_sign();
997 	    }
998 	    else if (dx)
999 		dir = -1;
1000 	    else
1001 		dir = 1;
1002 	}
1003 
1004 	if (dir < 0)
1005 	    dy = 0;
1006 	else
1007 	    dx = 0;
1008 
1009 	// Find max len...
1010 	if (dir < 0)
1011 	    dirlen = abs(ex - sx);
1012 	else
1013 	    dirlen = abs(ey - sy);
1014 
1015 	if (dirlen > 3)
1016 	{
1017 	    // Move at least 3, but choose a randome length otherwise...
1018 	    dirlen = rand_range(3, dirlen);
1019 	}
1020 
1021 	// Move dirlen or a wall, whatever is first...
1022 	while (dirlen)
1023 	{
1024 	    setPathTile(sx, sy, lit, false);
1025 	    if (isMapWall(sx + dx, sy + dy))
1026 	    {
1027 		// Don't want to tunnel, so quit this path.
1028 		if (!(dx && dy))
1029 		    return;
1030 		break;
1031 	    }
1032 	    // Move forward...
1033 	    sx += dx;
1034 	    sy += dy;
1035 	    dirlen--;
1036 	}
1037     }
1038     // Hit the end, draw the last square...
1039     setPathTile(ex, ey, lit, false);
1040 }
1041 
1042 bool
drawCorridor(int sx,int sy,int sdx,int sdy,int ex,int ey,int edx,int edy,bool lit)1043 MAP::drawCorridor(int sx, int sy, int sdx, int sdy,
1044 	     int ex, int ey, int edx, int edy,
1045 	     bool lit)
1046 {
1047     bool		success;
1048 
1049     success = drawPath(sx+sdx, sy+sdy, ex+edx, ey+edy, lit);
1050 
1051     // Draw our doors.  We only do this if our path creation
1052     // was successful.
1053     if (success)
1054     {
1055 	setPathTile(sx, sy, true, false);
1056 	setPathTile(ex, ey, true, false);
1057     }
1058     return success;
1059 }
1060 
1061 //
1062 // Checks to see if we can build the specified room.
1063 // The room itself must be filled with void, for one space
1064 // around the room we should have no walls (don't want touching rooms
1065 // as that will annoy the path builder)
1066 // The boundary of the map counts as a wall for this purpose.
1067 //
1068 bool
checkRoomFits(const ROOM & room)1069 MAP::checkRoomFits(const ROOM &room)
1070 {
1071     int		x, y;
1072 
1073     for (y = room.l.y; y <= room.h.y; y++)
1074         for (x = room.l.x; x <= room.h.x; x++)
1075 	    if (getTile(x, y) != SQUARE_EMPTY)
1076 		return false;
1077 
1078     // Test the border...
1079     for (x = room.l.x-1; x <= room.h.x+1; x++)
1080     {
1081 	if (isMapWall(x, room.l.y-1))
1082 	    return false;
1083 	if (isMapWall(x, room.h.y+1))
1084 	    return false;
1085     }
1086     // And the x sides...
1087     for (y = room.l.y-1; y <= room.h.y+1; y++)
1088     {
1089 	if (isMapWall(room.l.x-1, y))
1090 	    return false;
1091 	if (isMapWall(room.h.x+1, y))
1092 	    return false;
1093     }
1094 
1095     // Everything passes...
1096     return true;
1097 }
1098 
1099 const ROOM_DEF *
chooseRandomRoom(int depth)1100 MAP::chooseRandomRoom(int depth)
1101 {
1102     int		 prob[NUM_ALLROOMDEFS];
1103     const ROOM_DEF	*def;
1104     int		 i, totalprob = 0, offset;
1105 
1106     for (i = 0; i < NUM_ALLROOMDEFS; i++)
1107     {
1108 	def = glb_allroomdefs[i];
1109 
1110 	prob[i] = def->rarity;
1111 
1112 	if (def->minlevel >= 0 &&
1113 	    def->minlevel > depth)
1114 	    prob[i] = 0;
1115 	if (def->maxlevel >= 0 &&
1116 	    def->maxlevel < depth)
1117 	    prob[i] = 0;
1118 
1119 	// Mandatory rooms are always built on any level that
1120 	// they are legal.
1121 	if (prob[i] && def->mandatory)
1122 	    return def;
1123 
1124 	totalprob += prob[i];
1125     }
1126 
1127     offset = rand_choice(totalprob);
1128     for (i = 0; i < NUM_ALLROOMDEFS; i++)
1129     {
1130 	if (offset < prob[i])
1131 	    break;
1132 	offset -= prob[i];
1133     }
1134     if (i >= NUM_ALLROOMDEFS)
1135     {
1136 	UT_ASSERT(!"Could not find a room!");
1137 	i = NUM_ALLROOMDEFS-1;
1138     }
1139 
1140     return glb_allroomdefs[i];
1141 }
1142 
1143 void
buildRandomRoom(ROOM & room)1144 MAP::buildRandomRoom(ROOM &room)
1145 {
1146     // We don't let the room lie on a map boundary as then if it gets
1147     // an exit on that side, the corridor won't be able to propogate.
1148     room.l.x = rand_range(1, MAP_WIDTH-4);
1149     room.l.y = rand_range(1, MAP_HEIGHT-4);
1150     room.h.x = rand_range(room.l.x + 3, MIN(room.l.x + 8, MAP_WIDTH-2));
1151     room.h.y = rand_range(room.l.y + 3, MIN(room.l.y + 8, MAP_HEIGHT-2));
1152     room.definition = 0;
1153 }
1154 
1155 void
buildRandomRoomFromDefinition(ROOM & room,const ROOM_DEF * def)1156 MAP::buildRandomRoomFromDefinition(ROOM &room, const ROOM_DEF *def)
1157 {
1158     UT_ASSERT((int)def);
1159     if (!def)
1160     {
1161 	buildRandomRoom(room);
1162 	return;
1163     }
1164 
1165     int			width, height;
1166 
1167     room.rotation = rand_choice(8);
1168 
1169     if (room.rotation & ROOM_FLOP)
1170     {
1171 	width = def->size.y;
1172 	height = def->size.x;
1173     }
1174     else
1175     {
1176 	width = def->size.x;
1177 	height = def->size.y;
1178     }
1179 
1180     // We do a bit of a border on these rooms to ensure
1181     // that we can always force exits on the maze levels.
1182     room.l.x = rand_range(3, MAP_WIDTH - 3 - width);
1183     room.l.y = rand_range(3, MAP_HEIGHT - 3 - height);
1184 
1185     // If the room overrides x or y, override the value.
1186     if (def->x >= 0)
1187 	room.l.x = def->x;
1188     if (def->y >= 0)
1189 	room.l.y = def->y;
1190 
1191     // Note room's are INCLUSIVE coords, while size defines
1192     // exclusive.
1193     room.h.x = room.l.x + width - 1;
1194     room.h.y = room.l.y + height - 1;
1195 
1196     UT_ASSERT(room.l.x >= 0);
1197     UT_ASSERT(room.h.x < MAP_WIDTH);
1198     UT_ASSERT(room.l.y >= 0);
1199     UT_ASSERT(room.h.y < MAP_HEIGHT);
1200 
1201     room.definition = def;
1202 }
1203 
1204 void
closeUnusedExits(const ROOM & room,bool force)1205 MAP::closeUnusedExits(const ROOM &room, bool force)
1206 {
1207     if (room.definition)
1208     {
1209 	// Only close on special rooms.
1210 	int		x, y, rx, ry;
1211 	int		lastx = 0, lasty = 0;
1212 	int		lastdx = 0, lastdy = 0;
1213 	SQUARE_NAMES	lastsquare = SQUARE_FLOOR;
1214 
1215 	for (x = room.l.x; x <= room.h.x; x++)
1216 	{
1217 	    mapToRoomCoords(rx, ry, room, x, room.l.y);
1218 	    if (isRoomExit(room, rx, ry))
1219 	    {
1220 		lastx = x;	lasty = room.l.y;
1221 		lastdx = 0;	lastdy = -1;
1222 		lastsquare = getTile(x, room.l.y);
1223 		if (!isMapPathway(x, room.l.y-1))
1224 		    setTile(x, room.l.y,
1225 			    getTile(x-1, room.l.y));
1226 		else
1227 		    force = false;
1228 	    }
1229 	    mapToRoomCoords(rx, ry, room, x, room.h.y);
1230 	    if (isRoomExit(room, rx, ry))
1231 	    {
1232 		lastx = x;	lasty = room.h.y;
1233 		lastdx = 0;	lastdy = 1;
1234 		lastsquare = getTile(x, room.h.y);
1235 		if (!isMapPathway(x, room.h.y+1))
1236 		    setTile(x, room.h.y,
1237 			    getTile(x-1, room.h.y));
1238 		else
1239 		    force = false;
1240 	    }
1241 	}
1242 	for (y = room.l.y; y <= room.h.y; y++)
1243 	{
1244 	    mapToRoomCoords(rx, ry, room, room.l.x, y);
1245 	    if (isRoomExit(room, rx, ry))
1246 	    {
1247 		lastx = room.l.x;	lasty = y;
1248 		lastdx = -1;		lastdy = 0;
1249 		lastsquare = getTile(room.l.x, y);
1250 		if (!isMapPathway(room.l.x-1, y))
1251 		    setTile(room.l.x, y,
1252 			    getTile(room.l.x, y-1));
1253 		else
1254 		    force = false;
1255 	    }
1256 	    mapToRoomCoords(rx, ry, room, room.h.x, y);
1257 	    if (isRoomExit(room, rx, ry))
1258 	    {
1259 		lastx = room.h.x;	lasty = y;
1260 		lastdx = 1;		lastdy = 0;
1261 		lastsquare = getTile(room.h.x, y);
1262 		if (!isMapPathway(room.h.x+1, y))
1263 		    setTile(room.h.x, y,
1264 			    getTile(room.h.x, y-1));
1265 		else
1266 		    force = false;
1267 	    }
1268 	}
1269 
1270 	// TODO: Have option to wipe room if doesn't work out
1271 	// nicely.
1272 
1273 	if (force)
1274 	{
1275 	    // We must force at least one exit.
1276 	    // We dig continuously in the direction given by our last exit
1277 	    // until we find a way out.
1278 	    UT_ASSERT(lastdx || lastdy);
1279 	    if (lastdx || lastdy)
1280 	    {
1281 		bool			found = false;
1282 		SQUARE_NAMES		square = SQUARE_WALL;
1283 		int			pass;
1284 		int			curdx = 0, curdy = 0;
1285 
1286 		for (pass = 0; pass < 3; pass++)
1287 		{
1288 		    found = true;
1289 
1290 		    // Determine initial x & curdx & curdy.
1291 		    switch (pass)
1292 		    {
1293 			case 0:		// Straight
1294 			    curdx = lastdx;
1295 			    curdy = lastdy;
1296 			    break;
1297 			case 1:		// Turn
1298 			    curdx = !lastdx;
1299 			    curdy = !lastdy;
1300 			    break;
1301 			case 2:		// Other turn.
1302 			    curdx = -!lastdx;
1303 			    curdy = -!lastdy;
1304 			    break;
1305 		    }
1306 		    // Always move in last delta direction because
1307 		    // that is the one that will get us outside of the
1308 		    // container.
1309 		    x = lastx+lastdx;
1310 		    y = lasty+lastdy;
1311 
1312 		    // Only prefetch this if it has a chance of being valid.
1313 		    if (isMapPathway(x, y))
1314 			square = getTile(x, y);
1315 		    while (!isMapPathway(x, y))
1316 		    {
1317 			if (x < 0 || x >= MAP_WIDTH ||
1318 			    y < 0 || y >= MAP_HEIGHT)
1319 			{
1320 			    // complete failure.
1321 			    found = false;
1322 			    break;
1323 			}
1324 			// Check adjacent squares to see if this opened up.
1325 			if (isMapPathway(x + !curdx, y + !curdy))
1326 			{
1327 			    square = getTile(x + !curdx, y + !curdy);
1328 			    break;
1329 			}
1330 			if (isMapPathway(x - !curdx, y - !curdy))
1331 			{
1332 			    square = getTile(x - !curdx, y - !curdy);
1333 			    break;
1334 			}
1335 			x += curdx;
1336 			y += curdy;
1337 			if (isMapPathway(x, y))
1338 			    square = getTile(x, y);
1339 		    }
1340 
1341 		    if (found)
1342 		    {
1343 			lastx += lastdx;
1344 			lasty += lastdy;
1345 			break;
1346 		    }
1347 		}
1348 
1349 		if (found)
1350 		{
1351 		    // We found a direction!  Fill it in!
1352 		    while (x != lastx || y != lasty)
1353 		    {
1354 			setTile(x, y, square);
1355 			x -= curdx;
1356 			y -= curdy;
1357 		    }
1358 		    setTile(x, y, square);
1359 
1360 		    // Restore the last square.
1361 		    {
1362 			x -= lastdx;
1363 			y -= lastdy;
1364 			setTile(x, y, lastsquare);
1365 		    }
1366 		}
1367 	    }
1368 	}
1369     }
1370 }
1371 
1372 void
populateRoom(const ROOM & room)1373 MAP::populateRoom(const ROOM &room)
1374 {
1375     if (!room.definition)
1376 	return;
1377 
1378     // It is very important we apply the room flags before we test
1379     // if loading as we want reloaded rooms to have the same flags
1380     // Otherwise populate traps will behave differently as it will
1381     // think it can make holes (and players can zap wands all of a
1382     // sudden)
1383 
1384     // Apply the flags from the room definition.
1385     if (!room.definition->allowgeneration)
1386 	myGlobalFlags &= ~MAPFLAG_NEWMOBS;
1387     if (!room.definition->allowdig)
1388 	myGlobalFlags &= ~MAPFLAG_DIG;
1389     if (!room.definition->allowitems)
1390 	myGlobalFlags |= MAPFLAG_NOITEMS;
1391     if (!room.definition->allowtel)
1392 	myGlobalFlags |= MAPFLAG_NOTELE;
1393 
1394     // If we are loading from disk, we've already populated the room
1395     // from disk.
1396     if (ourIsLoading)
1397 	return;
1398 
1399     RAND_STATEPUSH	saverng;
1400 
1401     int		x, y;
1402     const PT2	*pt;
1403     MOB		*mob;
1404     ITEM	*item;
1405     MOBSTACK	 roommobs;
1406 
1407     // Create listed mobs.
1408     for (pt = room.definition->moblist; pt->data != MOB_NONE; pt++)
1409     {
1410 	mob = MOB::create((MOB_NAMES) pt->data);
1411 
1412 	roomToMapCoords(x, y, room, pt->x, pt->y);
1413 	mob->move(x, y, true);
1414 	registerMob(mob);
1415 	roommobs.append(mob);
1416     }
1417 
1418     int			i;
1419     MOB			*leader = 0;
1420 
1421     // Create mobs of the given level.
1422     for (pt = room.definition->moblevellist; pt->data != MOBLEVEL_NONE; pt++)
1423     {
1424 	int			threat;
1425 
1426 	// If there is already a mob here, uniquify it.
1427 	if (pt->data == MOBLEVEL_UNIQUE)
1428 	{
1429 	    roomToMapCoords(x, y, room, pt->x, pt->y);
1430 	    mob = getMob(x, y);
1431 	    if (mob)
1432 	    {
1433 		mob->makeUnique();
1434 		continue;
1435 	    }
1436 	}
1437 
1438 	threat = getThreatLevel();
1439 	threat += glb_mobleveldefs[pt->data].maxlevel;
1440 
1441 	mob = MOB::createNPC(threat);
1442 
1443 	if (pt->data == MOBLEVEL_UNIQUE)
1444 	    mob->makeUnique();
1445 
1446 	roomToMapCoords(x, y, room, pt->x, pt->y);
1447 	mob->move(x, y, true);
1448 	registerMob(mob);
1449 	roommobs.append(mob);
1450     }
1451 
1452     // Apply intrinsics to any mobs:
1453     for (pt = room.definition->intrinsiclist; pt->data != INTRINSIC_NONE; pt++)
1454     {
1455 	MOB		*mob;
1456 
1457 	roomToMapCoords(x, y, room, pt->x, pt->y);
1458 	mob = getMob(x, y);
1459 	if (mob)
1460 	{
1461 	    mob->setIntrinsic((INTRINSIC_NAMES) pt->data);
1462 	    if (pt->data == INTRINSIC_LEADER)
1463 	    {
1464 		leader = mob;
1465 	    }
1466 	}
1467     }
1468 
1469     // If there is a leader, tame all the room's mobs to it.
1470     // Ideally we'd do something tad smarter to allow multiple
1471     // groups to be made.
1472     if (leader)
1473     {
1474 	for (i = 0; i < roommobs.entries(); i++)
1475 	{
1476 	    // Don't self tame :>
1477 	    if (roommobs(i) == leader)
1478 		continue;
1479 
1480 	    roommobs(i)->makeSlaveOf(leader);
1481 	}
1482     }
1483 
1484     // Create the listed items.
1485     for (pt = room.definition->itemlist; pt->data != ITEM_NONE; pt++)
1486     {
1487 	item = ITEM::create((ITEM_NAMES) pt->data);
1488 
1489 	roomToMapCoords(x, y, room, pt->x, pt->y);
1490 	acquireItem(item, x, y, 0);
1491     }
1492 
1493     // Create the listed item types.
1494     for (pt = room.definition->itemtypelist; pt->data != ITEMTYPE_NONE; pt++)
1495     {
1496 	item = ITEM::createRandomType((ITEMTYPE_NAMES) pt->data);
1497 
1498 	roomToMapCoords(x, y, room, pt->x, pt->y);
1499 	acquireItem(item, x, y, 0);
1500     }
1501 
1502     // Create the signposts
1503     for (pt = room.definition->signpostlist; pt->data != SIGNPOST_NONE; pt++)
1504     {
1505 	roomToMapCoords(x, y, room, pt->x, pt->y);
1506 	addSignPost((SIGNPOST_NAMES) pt->data, x, y);
1507     }
1508 }
1509 
1510 void
addSignPost(SIGNPOST_NAMES post,int x,int y)1511 MAP::addSignPost(SIGNPOST_NAMES post, int x, int y)
1512 {
1513     if (!mySignList)
1514 	mySignList = new SIGNLIST;
1515 
1516     mySignList->append(post, x, y);
1517 }
1518 
1519 void
chooseRandomExit(int * entrance,int * delta,const ROOM & room)1520 MAP::chooseRandomExit(int *entrance, int *delta, const ROOM &room)
1521 {
1522     if (room.definition)
1523     {
1524 	// We need to find valid door squares on the boundary
1525 	// only these can be exits.
1526 	int		x, y;
1527 	int		*estack, *dstack;
1528 	int		numexit = 0, choice;
1529 
1530 	// Maximum size of the stacks is the perimeter of the room.
1531 	estack = new int[ 2 * (room.h.x - room.l.x + 1) +
1532 			  2 * (room.h.y - room.l.y + 1) ];
1533 	dstack = new int[ 2 * (room.h.x - room.l.x + 1) +
1534 			  2 * (room.h.y - room.l.y + 1) ];
1535 
1536 	for (x = room.l.x; x <= room.h.x; x++)
1537 	{
1538 	    if (isMapExit(x, room.l.y))
1539 	    {
1540 		estack[numexit*2] = x;
1541 		estack[numexit*2+1] = room.l.y;
1542 		dstack[numexit*2] = 0;
1543 		dstack[numexit*2+1] = -1;
1544 		numexit++;
1545 	    }
1546 	    if (isMapExit(x, room.h.y))
1547 	    {
1548 		estack[numexit*2] = x;
1549 		estack[numexit*2+1] = room.h.y;
1550 		dstack[numexit*2] = 0;
1551 		dstack[numexit*2+1] = 1;
1552 		numexit++;
1553 	    }
1554 	}
1555 	for (y = room.l.y; y <= room.h.y; y++)
1556 	{
1557 	    if (isMapExit(room.l.x, y))
1558 	    {
1559 		estack[numexit*2] = room.l.x;
1560 		estack[numexit*2+1] = y;
1561 		dstack[numexit*2] = -1;
1562 		dstack[numexit*2+1] = 0;
1563 		numexit++;
1564 	    }
1565 	    if (isMapExit(room.h.x, y))
1566 	    {
1567 		estack[numexit*2] = room.h.x;
1568 		estack[numexit*2+1] = y;
1569 		dstack[numexit*2] = 1;
1570 		dstack[numexit*2+1] = 0;
1571 		numexit++;
1572 	    }
1573 	}
1574 
1575 	UT_ASSERT(numexit);
1576 	choice = rand_choice(numexit);
1577 
1578 	entrance[0] = estack[choice*2];
1579 	entrance[1] = estack[choice*2+1];
1580 	delta[0] = dstack[choice*2];
1581 	delta[1] = dstack[choice*2+1];
1582 
1583 	delete [] estack;
1584 	delete [] dstack;
1585 
1586 	return;
1587     }
1588 
1589     switch (rand_choice(4))
1590     {
1591 	case 0:
1592 	    entrance[0] = room.l.x;
1593 	    entrance[1] = rand_range(room.l.y+1, room.h.y-1);
1594 	    delta[0] = -1;
1595 	    delta[1] = 0;
1596 	    break;
1597 	case 1:
1598 	    entrance[0] = room.h.x;
1599 	    entrance[1] = rand_range(room.l.y+1, room.h.y-1);
1600 	    delta[0] = 1;
1601 	    delta[1] = 0;
1602 	    break;
1603 	case 2:
1604 	    entrance[0] = rand_range(room.l.x+1, room.h.x-1);
1605 	    entrance[1] = room.l.y;
1606 	    delta[0] = 0;
1607 	    delta[1] = -1;
1608 	    break;
1609 	case 3:
1610 	    entrance[0] = rand_range(room.l.x+1, room.h.x-1);
1611 	    entrance[1] = room.h.y;
1612 	    delta[0] = 0;
1613 	    delta[1] = 1;
1614 	    break;
1615     }
1616 }
1617 
1618 void
buildBigRoom()1619 MAP::buildBigRoom()
1620 {
1621     ROOM		room;
1622     int			x, y;
1623 
1624     room.l.x = 1;
1625     room.l.y = 1;
1626     room.h.x = 30;
1627     room.h.y = 30;
1628     room.definition = 0;
1629     drawRoom(room, true, false, false);
1630 
1631     // Build random up & down stair cases.
1632     while (1)
1633     {
1634 	x = rand_range(2, 29);
1635 	y = rand_range(2, 29);
1636 
1637 	if (getTile(x, y) == SQUARE_FLOOR)
1638 	{
1639 	    setTile(x, y, SQUARE_LADDERUP);
1640 	    // setTile(x-1, y, SQUARE_LADDERDOWN);
1641 	    break;
1642 	}
1643     }
1644     while (1)
1645     {
1646 	x = rand_range(2, 29);
1647 	y = rand_range(2, 29);
1648 
1649 	if (getTile(x, y) == SQUARE_FLOOR)
1650 	{
1651 	    setTile(x, y, SQUARE_LADDERDOWN);
1652 	    break;
1653 	}
1654     }
1655 }
1656 
1657 void
buildRoguelike()1658 MAP::buildRoguelike()
1659 {
1660     // Draw some rooms...
1661     ROOM	rooms[20];
1662     int		numroom = 0;
1663     int		fails = 0;
1664     int		i;
1665     int		src;
1666     int		room, x, y;
1667     int		maxdark, numdark;
1668     bool	isdark;
1669 
1670     // Track number of dark rooms.
1671     numdark = 0;
1672     // Only have up to depth - 1 dark rooms.  This prevents
1673     // start levels from having lots of dark rooms.
1674     maxdark = myDepth-1;
1675 
1676     // Build as many rooms as fits, up to twenty.
1677     while (fails < 40 && numroom < 20)
1678     {
1679 	if (!numroom)
1680 	{
1681 	    buildRandomRoomFromDefinition(rooms[numroom],
1682 			      chooseRandomRoom(myDepth));
1683 	}
1684 	else
1685 	{
1686 	    buildRandomRoom(rooms[numroom]);
1687 	}
1688 	if (checkRoomFits(rooms[numroom]))
1689 	{
1690 	    isdark = false;
1691 	    // Decide if it should be dark.
1692 	    if (numdark < maxdark)
1693 	    {
1694 		isdark = rand_chance(10);
1695 	    }
1696 	    if (isdark)
1697 		numdark++;
1698 
1699 	    drawRoom(rooms[numroom], !isdark, false, false);
1700 	    numroom++;
1701 	}
1702 	else
1703 	    fails++;
1704     }
1705 
1706     // Now, build corridors...
1707     // We want every room to be connected.
1708     // Thus, we have a pool of i connected rooms.  We each
1709     // round take i+1 and connect it to one of our connect rooms
1710     // at random.
1711 
1712     // If we fail to connect 40 times, we declare this dungeon sucky
1713     // and go again...
1714     fails = 0;
1715     for (i = 0; i < numroom - 1; i++)
1716     {
1717 	int		cs[2], ce[2], ds[2], de[2];
1718 	src = rand_range(0, i);
1719 
1720 	chooseRandomExit(cs, ds, rooms[src]);
1721 	chooseRandomExit(ce, de, rooms[i+1]);
1722 
1723 	// Draw a corridor between these two rooms.
1724 	if (!drawCorridor(cs[0], cs[1], ds[0], ds[1],
1725 			  ce[0], ce[1], de[0], de[1]))
1726 	{
1727 	    // Failed to draw a corridor, try again.
1728 	    i--;
1729 	    fails++;
1730 	    if (fails > 40)
1731 		break;
1732 
1733 	    continue;
1734 	}
1735     }
1736     if (fails > 40)
1737     {
1738 	UT_ASSERT(!"Failed to build a dungeon, retrying!");
1739 	build(true);
1740 	return;
1741     }
1742 
1743     // Now we close any unused exits in our special rooms...
1744     for (room = 0; room < numroom; room++)
1745     {
1746 	closeUnusedExits(rooms[room], false);
1747 	populateRoom(rooms[room]);
1748     }
1749 
1750     // Now, we pick a random up and down stair case...
1751     while (1)
1752     {
1753 	room = rand_choice(numroom);
1754 
1755 	// If there was only one room, we trivially allow so we
1756 	// can't get an infinite loop if said rooms doesn't allow stairs.
1757 	if (numroom > 1 &&
1758 	    rooms[room].definition && !rooms[room].definition->allowstairs)
1759 	    continue;
1760 
1761 	x = rand_range(rooms[room].l.x+1, rooms[room].h.x-1);
1762 	y = rand_range(rooms[room].l.y+1, rooms[room].h.y-1);
1763 
1764 	if (getTile(x, y) == SQUARE_FLOOR ||
1765 	    getTile(x, y) == SQUARE_CORRIDOR)
1766 	{
1767 	    setTile(x, y, SQUARE_LADDERDOWN);
1768 	    break;
1769 	}
1770     }
1771     while (1)
1772     {
1773 	room = rand_choice(numroom);
1774 
1775 	if (numroom > 1 &&
1776 	    rooms[room].definition && !rooms[room].definition->allowstairs)
1777 	    continue;
1778 
1779 	x = rand_range(rooms[room].l.x+1, rooms[room].h.x-1);
1780 	y = rand_range(rooms[room].l.y+1, rooms[room].h.y-1);
1781 
1782 	if (getTile(x, y) == SQUARE_FLOOR ||
1783 	    getTile(x, y) == SQUARE_CORRIDOR)
1784 	{
1785 	    setTile(x, y, SQUARE_LADDERUP);
1786 	    break;
1787 	}
1788     }
1789 }
1790 
1791 static int
findCell(int * cells,int c)1792 findCell(int *cells, int c)
1793 {
1794     int		n;
1795 
1796     n = cells[c];
1797     if (n == c)
1798 	return c;
1799 
1800     n = findCell(cells, n);
1801     cells[c] = n;
1802     return n;
1803 }
1804 
1805 void
smooth(SQUARE_NAMES floor,SQUARE_NAMES wall)1806 MAP::smooth(SQUARE_NAMES floor, SQUARE_NAMES wall)
1807 {
1808     int			x, y;
1809     int			n;
1810 
1811     for (y = 1; y < MAP_HEIGHT-1; y++)
1812     {
1813 	for (x = 1; x < MAP_WIDTH-1; x++)
1814 	{
1815 	    if (getTile(x, y) == floor)
1816 	    {
1817 		// If we have 3 non floor neighbours, delete.
1818 		n = 0;
1819 		if (getTile(x-1, y) == wall)
1820 		    n++;
1821 		if (getTile(x+1, y) == wall)
1822 		    n++;
1823 		if (getTile(x, y-1) == wall)
1824 		    n++;
1825 		if (getTile(x, y+1) == wall)
1826 		    n++;
1827 
1828 		if (n >= 3)
1829 		{
1830 		    setTile(x, y, wall);
1831 		}
1832 	    }
1833 	}
1834     }
1835 }
1836 
1837 void
fillWithTile(int x,int y,SQUARE_NAMES newtile,SQUARE_NAMES oldtile)1838 MAP::fillWithTile(int x, int y, SQUARE_NAMES newtile, SQUARE_NAMES oldtile)
1839 {
1840     int			stackpos;
1841 #define STACKSIZE 500
1842     int			stack[STACKSIZE];
1843 
1844     stackpos = 0;
1845     stack[stackpos++] = y * MAP_WIDTH + x;
1846     while (stackpos)
1847     {
1848 	stackpos--;
1849 	x = stack[stackpos] & 31;
1850 	y = stack[stackpos] >> 5;
1851 
1852 	if (getTile(x, y) == oldtile)
1853 	{
1854 	    setTile(x, y, newtile);
1855 
1856 	    if ((x > 0) && getTile(x-1, y) == oldtile)
1857 		stack[stackpos++] = (x-1) + (y * MAP_WIDTH);
1858 	    if (stackpos >= STACKSIZE)
1859 		continue;
1860 	    if ((x < MAP_WIDTH-1) && getTile(x+1, y) == oldtile)
1861 		stack[stackpos++] = (x+1) + (y * MAP_WIDTH);
1862 	    if (stackpos >= STACKSIZE)
1863 		continue;
1864 	    if ((y > 0) && getTile(x, y-1) == oldtile)
1865 		stack[stackpos++] = x + (y - 1) * MAP_WIDTH;
1866 	    if (stackpos >= STACKSIZE)
1867 		continue;
1868 	    if ((y < MAP_HEIGHT-1) && getTile(x, y+1) == oldtile)
1869 		stack[stackpos++] = x + (y + 1) * MAP_WIDTH;
1870 	    if (stackpos >= STACKSIZE)
1871 		continue;
1872 	}
1873     }
1874 }
1875 
1876 void
drawQixWall(int sx,int sy,int dx,int dy,bool islit)1877 MAP::drawQixWall(int sx, int sy, int dx, int dy, bool islit)
1878 {
1879     int		x, y;
1880     int		range;
1881 
1882     // It is possible that we were given an out of range start
1883     // value.  This is not a problem, but we should not go
1884     // writing.
1885     if (!compareTile(sx, sy, SQUARE_FLOOR))
1886 	return;
1887 
1888     range = rand_range(5, 20);
1889 
1890     x = sx;
1891     y = sy;
1892     while (range)
1893     {
1894 	setTile(x, y, SQUARE_WALL);
1895 
1896 	// See if now adjacent.
1897 	// These are intentionally crossed over!
1898 	if (!compareTile(x+dy, y+dx, SQUARE_FLOOR))
1899 	    break;
1900 	if (!compareTile(x-dy, y-dx, SQUARE_FLOOR))
1901 	    break;
1902 	if (!compareTile(x+dx, y+dy, SQUARE_FLOOR))
1903 	    break;
1904 
1905 	// Keep building.
1906 	x += dx;
1907 	y += dy;
1908 	range--;
1909     }
1910 
1911     // If we didn't hit anything, rotate.
1912     if (!range)
1913     {
1914 	// Rotate...
1915 	x -= dx;
1916 	y -= dy;
1917 	if (dx)
1918 	{
1919 	    dy = dx;
1920 	    dx = 0;
1921 	}
1922 	else
1923 	{
1924 	    dx = -dy;
1925 	    dy = 0;
1926 	}
1927 	x += dx;
1928 	y += dy;
1929 	drawQixWall(x, y, dx, dy, islit);
1930     }
1931 }
1932 
1933 void
buildQix(bool islit)1934 MAP::buildQix(bool islit)
1935 {
1936     int			x, y, cx, cy, dx, dy;
1937     int			i;
1938     ROOM		room;
1939     int			numwalls, tries, dir;
1940 
1941     // Build a drunken cavern to use as our guide.
1942     buildDrunkenCavern(islit, false, false, false);
1943 
1944     smooth(SQUARE_CORRIDOR, SQUARE_EMPTY);
1945 
1946     // Set the entire map to normal ground:
1947     // Boundary becomes walls
1948     for (y = 0; y < MAP_HEIGHT; y++)
1949     {
1950 	for (x = 0; x < MAP_WIDTH; x++)
1951 	{
1952 	    int		oldtile;
1953 
1954 	    oldtile = getTile(x, y);
1955 	    if (oldtile == SQUARE_CORRIDOR)
1956 		setTile(x, y, SQUARE_FLOOR);
1957 	    else if (oldtile == SQUARE_EMPTY)
1958 		setTile(x, y, SQUARE_WALL);
1959 
1960 	    // For debugging:
1961 	    // setFlag(x, y, SQUAREFLAG_MAPPED);
1962 	}
1963     }
1964 
1965     // Create the special room:
1966     buildRandomRoomFromDefinition(room,
1967 			  chooseRandomRoom(myDepth));
1968     drawRoom(room, true, true, false);
1969     populateRoom(room);
1970 
1971     numwalls = rand_range(17, 28);
1972     while (numwalls--)
1973     {
1974 	// Find a valid wall position.
1975 	tries = 100;
1976 	while (tries--)
1977 	{
1978 	    cx = rand_range(1, MAP_WIDTH-1);
1979 	    cy = rand_range(1, MAP_HEIGHT-1);
1980 
1981 	    // Ensure we are not in the special room.
1982 	    if (cx >= room.l.x && cx <= room.h.x &&
1983 		cy >= room.l.y && cy <= room.h.y)
1984 	    {
1985 		continue;
1986 	    }
1987 
1988 	    if (getTile(cx, cy) != SQUARE_FLOOR)
1989 		continue;
1990 
1991 	    // Verify we are not adjacent to a wall.
1992 	    if (getTile(cx, cy+1) != SQUARE_FLOOR)
1993 		continue;
1994 	    if (getTile(cx, cy-1) != SQUARE_FLOOR)
1995 		continue;
1996 	    if (getTile(cx+1, cy) != SQUARE_FLOOR)
1997 		continue;
1998 	    if (getTile(cx-1, cy) != SQUARE_FLOOR)
1999 		continue;
2000 
2001 	    // Valid wall, done.
2002 	    break;
2003 	}
2004 
2005 	// Couldn't find a valid space.
2006 	if (!tries)
2007 	    break;
2008 
2009 	dir = rand_choice(4);
2010 	getDirection(dir, dx, dy);
2011 
2012 	// Now draw walls in two directions until they hit a wall
2013 	// or become adjacent to a wall
2014 	drawQixWall(cx, cy, dx, dy, islit);
2015 
2016 	dir += rand_range(1, 3);
2017 	dir &= 3;
2018 	getDirection(dir, dx, dy);
2019 
2020 	drawQixWall(cx+dx, cy+dy, dx, dy, islit);
2021     }
2022 
2023     int		*cutout = ourCutOuts;
2024     int		*cell = ourCells;
2025     int		numcuts = 0, c1, c2;
2026 
2027     // Find all possible cuts
2028     for (y = 1; y < MAP_HEIGHT-1; y++)
2029     {
2030 	for (x = 1; x < MAP_WIDTH-1; x++)
2031 	{
2032 	    // Ensure we are not in the special room.
2033 	    if (x >= room.l.x && x <= room.h.x &&
2034 		y >= room.l.y && y <= room.h.y)
2035 	    {
2036 		continue;
2037 	    }
2038 
2039 	    // To be a cut, we must be a wall.
2040 	    if (getTile(x, y) != SQUARE_WALL)
2041 		continue;
2042 
2043 	    // To be a valid cut, we need to have a floor
2044 	    // on either side and walls on the opposite sides.
2045 	    if ((getTile(x-1, y) == SQUARE_FLOOR) &&
2046 		(getTile(x+1, y) == SQUARE_FLOOR) &&
2047 		(getTile(x, y-1) == SQUARE_WALL) &&
2048 		(getTile(x, y+1) == SQUARE_WALL))
2049 	    {
2050 		cutout[numcuts++] = y * MAP_WIDTH + x;
2051 	    }
2052 	    if ((getTile(x-1, y) == SQUARE_WALL) &&
2053 		(getTile(x+1, y) == SQUARE_WALL) &&
2054 		(getTile(x, y-1) == SQUARE_FLOOR) &&
2055 		(getTile(x, y+1) == SQUARE_FLOOR))
2056 	    {
2057 		cutout[numcuts++] = y * MAP_WIDTH + x;
2058 	    }
2059 	}
2060     }
2061     UT_ASSERT(numcuts < 512);
2062 
2063     // Initialize cell array:
2064     for (i = 0; i < 1024; i++)
2065 	cell[i] = i;
2066 
2067     // Connect anything already connected
2068     for (y = 1; y < MAP_HEIGHT-1; y++)
2069     {
2070 	for (x = 1; x < MAP_WIDTH-1; x++)
2071 	{
2072 	    if (getTile(x, y) != SQUARE_FLOOR)
2073 		continue;
2074 
2075 	    c1 = findCell(cell, y * MAP_WIDTH + x);
2076 
2077 	    if (getTile(x+1, y) == SQUARE_FLOOR)
2078 	    {
2079 		c2 = findCell(cell, y * MAP_WIDTH + (x + 1));
2080 		if (c1 < c2)
2081 		    cell[c2] = c1;
2082 		else if (c1 > c2)
2083 		    cell[c1] = c2;
2084 	    }
2085 
2086 	    if (getTile(x, y+1) == SQUARE_FLOOR)
2087 	    {
2088 		c2 = findCell(cell, (y+1) * MAP_WIDTH + x);
2089 		if (c1 < c2)
2090 		    cell[c2] = c1;
2091 		else if (c1 > c2)
2092 		    cell[c1] = c2;
2093 	    }
2094 	}
2095     }
2096 
2097     // Now, randomize our cut list:
2098     rand_shuffle(cutout, numcuts);
2099 
2100     for (i = 0; i < numcuts; i++)
2101     {
2102 	// Determine direction of cut
2103 	x = cutout[i] % MAP_WIDTH;
2104 	y = cutout[i] / MAP_WIDTH;
2105 
2106 	// Find direction.
2107 	if ((getTile(x+1, y) == SQUARE_WALL) ||
2108 	    (getTile(x-1, y) == SQUARE_WALL))
2109 	{
2110 	    dx = 0;
2111 	    dy = 1;
2112 	}
2113 	if ((getTile(x, y+1) == SQUARE_WALL) ||
2114 	    (getTile(x, y-1) == SQUARE_WALL))
2115 	{
2116 	    dx = 1;
2117 	    dy = 0;
2118 	}
2119 
2120 	// Check to see if the cells are different.
2121 	c1 = findCell(cell, (y+dy)*MAP_WIDTH+(x+dx));
2122 	c2 = findCell(cell, (y-dy)*MAP_WIDTH+(x-dx));
2123 
2124 	if (c1 != c2)
2125 	{
2126 	    setTile(x, y, SQUARE_DOOR);
2127 	    if (c1 < c2)
2128 		cell[c2] = c1;
2129 	    else if (c1 > c2)
2130 		cell[c1] = c2;
2131 
2132 	    // Add the door itself to the include list.
2133 	    c1 = findCell(cell, y * MAP_WIDTH + x);
2134 	    if (c1 < c2)
2135 		cell[c2] = c1;
2136 	    else if (c1 > c2)
2137 		cell[c1] = c2;
2138 	}
2139 	else if (rand_chance(5))
2140 	{
2141 	    setTile(x, y, SQUARE_SECRETDOOR);
2142 	}
2143     }
2144 
2145     // Now destroy any walls that are unconnected.
2146     int		c1cnt, c2cnt;
2147     c1cnt = 0;
2148     c1 = -1;
2149     for (y = 0; y < MAP_WIDTH; y++)
2150     {
2151 	for (x = 0; x < MAP_WIDTH; x++)
2152 	{
2153 	    if (x >= room.l.x && x <= room.h.x &&
2154 		y >= room.l.y && y <= room.h.y)
2155 	    {
2156 		continue;
2157 	    }
2158 
2159 	    if (getTile(x, y) == SQUARE_FLOOR || getTile(x, y) == SQUARE_DOOR)
2160 	    {
2161 		if (c1 < 0)
2162 		{
2163 		    c1 = findCell(cell, y * MAP_WIDTH + x);
2164 		    c1cnt = 0;
2165 		    for (i = 0; i < 1024; i++)
2166 		    {
2167 			if (findCell(cell, i) == c1)
2168 			    c1cnt++;
2169 		    }
2170 		}
2171 
2172 		c2 = findCell(cell, y * MAP_WIDTH + x);
2173 		if (c1 != c2)
2174 		{
2175 		    if (c2 == y * MAP_WIDTH + x)
2176 		    {
2177 			c2cnt = 0;
2178 			for (i = 0; i < 1024; i++)
2179 			{
2180 			    if (findCell(cell, i) == c2)
2181 				c2cnt++;
2182 			}
2183 
2184 			if (c2cnt > c1cnt)
2185 			{
2186 			    c1 = c2;
2187 			    c1cnt = c2cnt;
2188 			    // Restart so we rescan earlyer
2189 			    // parts.
2190 			    y = 0;
2191 			    x = 0;
2192 			    continue;
2193 			}
2194 		    }
2195 
2196 		    // Isolated region!  Reject!
2197 		    setTile(x, y, SQUARE_WALL);
2198 		}
2199 	    }
2200 	}
2201     }
2202 
2203     closeUnusedExits(room, true);
2204 
2205     // Now, we pick a random up and down stair case...
2206     while (1)
2207     {
2208         findRandomLoc(x, y, MOVE_WALK,
2209 			true, false, false, false, false, false);
2210 
2211 	if (!room.definition->allowstairs &&
2212 	     x >= room.l.x && x <= room.h.x &&
2213 	     y >= room.l.y && y <= room.h.y)
2214 	    continue;
2215 
2216 	if (getTile(x, y) == SQUARE_FLOOR)
2217 	{
2218 	    setTile(x, y, SQUARE_LADDERUP);
2219 	    break;
2220 	}
2221     }
2222 
2223     while (1)
2224     {
2225         findRandomLoc(x, y, MOVE_WALK,
2226 			true, false, false, false, false, false);
2227 
2228 	if (!room.definition->allowstairs &&
2229 	     x >= room.l.x && x <= room.h.x &&
2230 	     y >= room.l.y && y <= room.h.y)
2231 	    continue;
2232 
2233 	if (getTile(x, y) == SQUARE_FLOOR)
2234 	{
2235 	    setTile(x, y, SQUARE_LADDERDOWN);
2236 	    break;
2237 	}
2238     }
2239 }
2240 
2241 void
buildMaze(int stride)2242 MAP::buildMaze(int stride)
2243 {
2244     // A 32x32 room as 16x16 horizontal cuts and 16x16 vertical cuts,
2245     // for a max cut list of 512.
2246     int			*cutouts;
2247     // The cell tracks connected sets.  Each cell points to the
2248     // cell it is connected to, or to itself if it is the root of
2249     // the tree.
2250     int			*cell;
2251     int			numcuts;
2252     int			x, y, cx, cy, dx, dy;
2253     int			i;
2254     int			cut;
2255     int			c1, c2;
2256     ROOM		room;
2257 
2258     cell = ourCells;
2259     cutouts = ourCutOuts;
2260 
2261     // Set the entire map to normal walls:
2262     for (y = 0; y < MAP_HEIGHT; y++)
2263     {
2264 	for (x = 0; x < MAP_WIDTH; x++)
2265 	{
2266 	    setTile(x, y, SQUARE_WALL);
2267 	    // It would be very cruel not to light the maze.
2268 	    setFlag(x, y, SQUAREFLAG_LIT);
2269 
2270 	    // For debugging:
2271 	    // setFlag(x, y, SQUAREFLAG_MAPPED);
2272 	}
2273     }
2274 
2275     // We want every other square to be a hole.
2276     for (y = 1; y < MAP_HEIGHT-stride; y += stride + 1)
2277     {
2278 	for (x = 1; x < MAP_WIDTH-stride; x += stride + 1)
2279 	{
2280 	    for (dy = 0; dy < stride; dy++)
2281 	    {
2282 		for (dx = 0; dx < stride; dx++)
2283 		{
2284 		    setTile(x+dx, y+dy, SQUARE_FLOOR);
2285 		}
2286 	    }
2287 	}
2288     }
2289 
2290     // Create the special room:
2291     buildRandomRoomFromDefinition(room,
2292 			  chooseRandomRoom(myDepth));
2293     drawRoom(room, true, true, false);
2294     populateRoom(room);
2295 
2296     // We also want to initialize the cutout array here (which
2297     // is a list of all possible wall cuts.)
2298     // We do not include any cuts that will bridge the room - ie, either
2299     // side of the cut is inside of the room.
2300     numcuts = 0;
2301     for (y = 1; y < MAP_HEIGHT-stride; y += stride+1)
2302     {
2303 	for (x = 1; x < MAP_WIDTH-stride; x += stride+1)
2304 	{
2305 	    for (dx = 0; dx < stride; dx++)
2306 	    {
2307 		if (x+stride < MAP_WIDTH-2)
2308 		    if (x+stride+1 < room.l.x || x > room.h.x ||
2309 			y+dx < room.l.y || y+dx > room.h.y)
2310 			cutouts[numcuts++] = ((y+dx) * MAP_WIDTH) + x + stride;
2311 		if (y+stride < MAP_HEIGHT-2)
2312 		    if (x+dx < room.l.x || x+dx > room.h.x ||
2313 			y+stride+1 < room.l.y || y > room.h.y)
2314 			cutouts[numcuts++] = ((y+stride) * MAP_WIDTH) + x + dx;
2315 	    }
2316 	}
2317     }
2318     UT_ASSERT(numcuts < 512);
2319 
2320     // Initialize cell array:
2321     for (i = 0; i < 256; i++)
2322 	cell[i] = i;
2323 
2324     // Now, randomize our cut list:
2325     rand_shuffle(cutouts, numcuts);
2326 
2327     // And cut all the cutouts that won't join an existing pair...
2328     for (i = 0; i < numcuts; i++)
2329     {
2330 	cut = cutouts[i];
2331 
2332 	y = cut / MAP_WIDTH;
2333 	x = cut - y * MAP_WIDTH;
2334 	cx = (x-1) / (stride+1);
2335 	cy = (y-1) / (stride+1);
2336 	c1 = findCell(cell, cy * (MAP_WIDTH / 2) + cx);
2337 	if (cut & 1)
2338 	{
2339 	    // Vertical cut...
2340 	    c2 = findCell(cell, (cy+1) * (MAP_WIDTH / 2) + cx);
2341 	}
2342 	else
2343 	{
2344 	    // Horizontal cut...
2345 	    c2 = findCell(cell, cy * (MAP_WIDTH / 2) + cx + 1);
2346 	}
2347 
2348 	// We only connect if they are different.  This ensures
2349 	// we have a fully branching corridor.   It would also
2350 	// prevent any loop backs and make for lots of backtracking.
2351 	// Thus, to be kind, we add some extras...
2352 	if (c1 != c2 || rand_chance(10))
2353 	{
2354 	    // We have two different cells that would be joined
2355 	    // by this cut, so make it.
2356 	    setTile(x, y, SQUARE_FLOOR);
2357 	    // And mark the cells the same:
2358 	    cell[c2] = c1;
2359 	}
2360     }
2361 
2362     closeUnusedExits(room, true);
2363 
2364     // Now, we pick a random up and down stair case...
2365     while (1)
2366     {
2367         findRandomLoc(x, y, MOVE_WALK,
2368 			true, false, false, false, false, false);
2369 
2370 	if (!room.definition->allowstairs &&
2371 	     x >= room.l.x && x <= room.h.x &&
2372 	     y >= room.l.y && y <= room.h.y)
2373 	    continue;
2374 
2375 	if (getTile(x, y) == SQUARE_FLOOR)
2376 	{
2377 	    setTile(x, y, SQUARE_LADDERUP);
2378 	    break;
2379 	}
2380     }
2381 
2382     while (1)
2383     {
2384         findRandomLoc(x, y, MOVE_WALK,
2385 			true, false, false, false, false, false);
2386 
2387 	if (!room.definition->allowstairs &&
2388 	     x >= room.l.x && x <= room.h.x &&
2389 	     y >= room.l.y && y <= room.h.y)
2390 	    continue;
2391 
2392 	if (getTile(x, y) == SQUARE_FLOOR)
2393 	{
2394 	    setTile(x, y, SQUARE_LADDERDOWN);
2395 	    break;
2396 	}
2397     }
2398 }
2399 
2400 void
buildDrunkenCavern(bool islit,bool allowlakes,bool allowroom,bool buildstaircase)2401 MAP::buildDrunkenCavern(bool islit, bool allowlakes, bool allowroom, bool buildstaircase)
2402 {
2403     // This time the drunk in the title refers to the algorithm, not
2404     // my state when writing it.
2405 
2406     // int			numdrunks, i;
2407     int			rum, j;
2408     int			x, y;
2409     int			dx, dy;
2410     int			numcuts = 0;
2411     SQUARE_NAMES	tunneltype;
2412 
2413     if (islit)
2414     {
2415 	for (y = 0; y < MAP_HEIGHT; y++)
2416 	{
2417 	    for (x = 0; x < MAP_WIDTH; x++)
2418 	    {
2419 		setFlag(x, y, SQUAREFLAG_LIT);
2420 		// For debugging:
2421 		// setFlag(x, y, SQUAREFLAG_MAPPED);
2422 	    }
2423 	}
2424     }
2425 
2426     ROOM			room;
2427     room.definition = 0;
2428 
2429     if (allowroom)
2430     {
2431 	buildRandomRoomFromDefinition(room,
2432 			  chooseRandomRoom(myDepth));
2433 	drawRoom(room, islit, false, false);
2434 	populateRoom(room);
2435     }
2436 
2437     // 10-30 drunken walks.
2438     //numdrunks = rand_range(20, 30);
2439     //rum = rand_range(20, 30);
2440     //numdrunks = 1;
2441     //rum = 900;
2442 
2443     // We want numdrunks + sqrt(rum) == 20
2444     // The sqrt is because each drunk will move a distance on average
2445     // of sqrt(rum).
2446 
2447     // We may want to:
2448     // 1) Bias the birth of drunkards near tunnel terminuses
2449     // 2) Turn on back tracking to allow creation of deadends
2450     // 3) Tunnel until a certain percentage is execavated.
2451     /*
2452     numdrunks = rand_range(1, 17);
2453     rum = 20 - numdrunks;
2454     rum *= rum;
2455     */
2456     rum = 300;
2457 
2458     //BUF		buf;
2459     //buf.sprintf("Drunk: %d, Len: %d.  ", numdrunks, rum);
2460     //msg_report(buf);
2461     //for (i = 0; i < numdrunks; i++)
2462     while (numcuts < (MAP_WIDTH * MAP_HEIGHT / 2))
2463     {
2464 	// Pick a random drunk starting location.
2465 	//if (i)
2466 	if (numcuts)
2467 	{
2468 	    findRandomLoc(x, y, MOVE_STD_SWIM,
2469 			    true, false, false, false, false, false);
2470 	    if (isMapWall(x, y))
2471 		continue;
2472 	    tunneltype = SQUARE_CORRIDOR;
2473 	}
2474 	else
2475 	{
2476 	    while (1)
2477 	    {
2478 		x = rand_range(3, MAP_WIDTH-3);
2479 		y = rand_range(3, MAP_HEIGHT-3);
2480 		if (isMapWall(x, y))
2481 		    continue;
2482 		if (getTile(x, y) == SQUARE_EMPTY)
2483 		    break;
2484 	    }
2485 
2486 	    // We get free cuts corresponding to the size of the room.
2487 	    if (allowroom)
2488 		numcuts += (room.h.x - room.l.x) * (room.h.y - room.l.y);
2489 
2490 	    if (allowlakes)
2491 	    {
2492 		tunneltype = SQUARE_WATER;
2493 	    }
2494 	    else
2495 		tunneltype = SQUARE_CORRIDOR;
2496 	}
2497 
2498 	// Now start carving out the dungeon...
2499 	// Initial dig direction:
2500 	rand_direction(dx, dy);
2501 	for (j = 0; j < rum; j++)
2502 	{
2503 	    // Dig out this square...
2504 	    if (getTile(x, y) == SQUARE_EMPTY)
2505 		numcuts++;
2506 
2507 	    // Don't cut out special rooms.
2508 	    if (getTile(x, y) != SQUARE_CORRIDOR &&
2509 		getTile(x, y) != SQUARE_EMPTY &&
2510 		getTile(x, y) != SQUARE_WATER)
2511 	    {
2512 		break;
2513 	    }
2514 
2515 	    if (getTile(x, y) != SQUARE_WATER)
2516 		setTile(x, y, tunneltype);
2517 
2518 	    // Move drunkenly...
2519 	    x += dx;
2520 	    y += dy;
2521 
2522 	    // Stumble against the walls.
2523 	    if (x < 1)
2524 		x = 1;
2525 	    if (x >= MAP_WIDTH-2)
2526 		x = MAP_WIDTH-2;
2527 	    if (y < 1)
2528 		y = 1;
2529 	    if (y >= MAP_HEIGHT-2)
2530 		y = MAP_HEIGHT-2;
2531 
2532 	    // Change our direction...
2533 	    switch (rand_choice(3))
2534 	    {
2535 		case 0:
2536 		    // Unchanged.
2537 		    break;
2538 		case 1:
2539 		    // Turn, +1
2540 		    dx = !dx;
2541 		    dy = !dy;
2542 		    break;
2543 		case 2:
2544 		    // Turn -1
2545 		    dx = -!dx;
2546 		    dy = -!dy;
2547 		    break;
2548 	    }
2549 	}
2550     }
2551 
2552     if (allowroom)
2553 	closeUnusedExits(room, true);
2554 
2555     // Build the staircases..
2556     while (buildstaircase)
2557     {
2558         findRandomLoc(x, y, MOVE_STD_SWIM,
2559 			true, false, false, false, false, false);
2560 
2561 	if (room.definition &&
2562 	    !room.definition->allowstairs &&
2563 	     x >= room.l.x && x <= room.h.x &&
2564 	     y >= room.l.y && y <= room.h.y)
2565 	    continue;
2566 
2567 	if (getTile(x, y) == SQUARE_CORRIDOR || getTile(x, y) == SQUARE_WATER)
2568 	{
2569 	    setTile(x, y, SQUARE_LADDERUP);
2570 	    break;
2571 	}
2572     }
2573 
2574     while (buildstaircase)
2575     {
2576         findRandomLoc(x, y, MOVE_STD_SWIM,
2577 			true, false, false, false, false, false);
2578 
2579 	if (room.definition &&
2580 	    !room.definition->allowstairs &&
2581 	     x >= room.l.x && x <= room.h.x &&
2582 	     y >= room.l.y && y <= room.h.y)
2583 	    continue;
2584 
2585 	if (getTile(x, y) == SQUARE_CORRIDOR || getTile(x, y) == SQUARE_WATER)
2586 	{
2587 	    setTile(x, y, SQUARE_LADDERDOWN);
2588 	    break;
2589 	}
2590     }
2591 }
2592 
2593 void
buildRiverRoom()2594 MAP::buildRiverRoom()
2595 {
2596     int		upx = 0, upy;
2597     int		downx = 0, downy;
2598     int		minx, maxx;
2599     int		rx, ry;
2600     int		i;
2601 
2602     do
2603     {
2604 	// Empty the map...
2605 	clear();
2606 
2607 	// Clear out our wall map.
2608 	memset(ourWallMap, 0, MAP_WIDTH * MAP_HEIGHT);
2609 
2610 	buildDrunkenCavern(false, false, false, true);
2611 
2612 	// Find the up and down ladders, try to cut through them with a river.
2613 	if (!findTile(SQUARE_LADDERUP, upx, upy))
2614 	    continue;
2615 	if (!findTile(SQUARE_LADDERDOWN, downx, downy))
2616 	    continue;
2617     } while (abs(upx - downx) <= 7);
2618 
2619     // We are now guaranteed that we have a 5 space room for our river.
2620     if (upx < downx)
2621     {
2622 	minx = upx;
2623 	maxx = downx;
2624     }
2625     else
2626     {
2627 	minx = downx;
2628 	maxx = upx;
2629     }
2630     minx += 3;
2631     maxx -= 3;
2632 
2633     rx = (upx + downx) / 2;
2634     for (ry = 0; ry < MAP_HEIGHT; ry++)
2635     {
2636 	// Move rx randomly to left or right.
2637 	rx += rand_choice(3) - 1;
2638 	if (rx < minx)
2639 	    rx = minx;
2640 	if (rx > maxx)
2641 	    rx = maxx;
2642 
2643 	// Draw the river here.
2644 	for (i = -2; i <= 2; i++)
2645 	{
2646 	    // We only want to draw over corridor and normal walls.
2647 	    // Otherwise we could write over a special room.
2648 	    switch (getTile(rx+i, ry))
2649 	    {
2650 		case SQUARE_CORRIDOR:
2651 		case SQUARE_EMPTY:
2652 		    setTile(rx+i, ry, SQUARE_WATER);
2653 		    break;
2654 		default:
2655 		    break;
2656 	    }
2657 	}
2658     }
2659 }
2660 
2661 void
buildSurfaceWorld()2662 MAP::buildSurfaceWorld()
2663 {
2664     int		elevation[MAP_HEIGHT+1][MAP_WIDTH+1];
2665     int		scale, step, average, e;
2666     int		x, y, pass;
2667     SQUARE_NAMES	tile;
2668 
2669     // Set our flags appropraitel
2670     myGlobalFlags &= ~MAPFLAG_DIG;
2671     myGlobalFlags &= ~MAPFLAG_NEWMOBS;
2672     myGlobalFlags |= MAPFLAG_NOITEMS;
2673 
2674     for (scale = 5; scale >= 0; scale--)
2675     {
2676 	step = 1 << scale;
2677 
2678 	for (pass = 0; pass < 2; pass++)
2679 	{
2680 	    for (y = 0; y <= MAP_HEIGHT; y += step)
2681 	    {
2682 		for (x = 0; x <= MAP_WIDTH; x += step)
2683 		{
2684 		    // Clamp boundaries:
2685 		    if (!x || !y || (x == MAP_WIDTH) || (y == MAP_HEIGHT))
2686 			elevation[y][x] = 256;
2687 		    else if (x == MAP_WIDTH/2 && y == MAP_HEIGHT/2)
2688 			elevation[y][x] = 0;
2689 		    else
2690 		    {
2691 			// First pass we want only edges
2692 			// Second pass the centers.
2693 			if (pass)
2694 			{
2695 			    if (!(x & step) || !(y & step))
2696 				continue;
2697 
2698 			    average = elevation[y - step][x] +
2699 				      elevation[y + step][x] +
2700 				      elevation[y][x - step] +
2701 				      elevation[y][x + step];
2702 			    average = (average + 2) / 4;
2703 			}
2704 			else
2705 			{
2706 			    if (!((x & step) ^ (y & step)))
2707 				continue;
2708 
2709 			    if (x & step)
2710 			    {
2711 				average = elevation[y][x - step] +
2712 					  elevation[y][x + step];
2713 				average = (average + 1) / 2;
2714 			    }
2715 			    else
2716 			    {
2717 				average = elevation[y - step][x] +
2718 					  elevation[y + step][x];
2719 				average = (average + 1) / 2;
2720 			    }
2721 			}
2722 
2723 			average += rand_range(-step*10, step*10);
2724 
2725 			elevation[y][x] = average;
2726 		    }
2727 		}
2728 	    }
2729 	}		// for pass
2730     }
2731 
2732     for (y = 0; y < MAP_HEIGHT; y++)
2733     {
2734 	for (x = 0; x < MAP_WIDTH; x++)
2735 	{
2736 	    e = elevation[y][x];
2737 
2738 	    if (e < 50)
2739 		tile = SQUARE_GRASS;
2740 	    else if (e < 100)
2741 		tile = SQUARE_WATER;
2742 	    else if (e < 150)
2743 		tile = SQUARE_GRASS;
2744 	    else if (e < 200)
2745 		tile = SQUARE_FOREST;
2746 	    else if (e < 240)
2747 		tile = SQUARE_HILLS;
2748 	    else
2749 		tile = SQUARE_MOUNTAIN;
2750 
2751 	    setTile(x, y, tile);
2752 	    setFlag(x, y, SQUAREFLAG_LIT);
2753 	}
2754     }
2755 
2756     // One upstairs in the center...
2757     setTile(MAP_WIDTH/2, MAP_HEIGHT/2, SQUARE_LADDERDOWN);
2758 
2759     // Warning sign post
2760     setTile(MAP_WIDTH/2+2, MAP_HEIGHT/2, SQUARE_SIGNPOST);
2761     addSignPost(SIGNPOST_WON, MAP_WIDTH/2+2, MAP_HEIGHT/2);
2762 
2763     // Elder
2764     y = MAP_HEIGHT/2;
2765 
2766     for ( ; y < MAP_HEIGHT; y++)
2767     {
2768 	x = MAP_WIDTH/2;
2769 	for ( ; x < MAP_WIDTH; x++)
2770 	{
2771 	    if (!canMove(x, y, MOVE_STD_SWIM))
2772 		break;
2773 	}
2774 	if (canMove(x-1,y, MOVE_WALK))
2775 	{
2776 	    x--;
2777 	    break;
2778 	}
2779     }
2780     if (y >= MAP_HEIGHT)
2781     {
2782 	// Failed to place far away
2783 	x = MAP_WIDTH/2-1;
2784 	y = MAP_HEIGHT/2;
2785     }
2786     // If we are loading from disk, we've already populated the room
2787     // from disk.
2788     if (!ourIsLoading)
2789     {
2790 	MOB *mob;
2791 
2792 	mob = MOB::create(MOB_ELDER);
2793 
2794 	// interlevell so no traps
2795 	// but what do we do about failure???
2796 	// currently move never fails with interlevel.
2797 	if (mob->move(x, y, true))
2798 	    registerMob(mob);
2799 	else
2800 	    delete mob;
2801     }
2802 }
2803 
2804 void
buildTutorial()2805 MAP::buildTutorial()
2806 {
2807     // Find the appropriate room...
2808     int			i, nummatch, match = -1;
2809 
2810     nummatch = 0;
2811     for (i = 0; i < NUM_ALLROOMDEFS; i++)
2812     {
2813 	if (!strcmp(glb_allroomdefs[i]->name, "tutorial"))
2814 	{
2815 	    nummatch++;
2816 	    if (!rand_choice(nummatch))
2817 		match = i;
2818 	}
2819     }
2820 
2821     if (match == -1)
2822     {
2823 	// No matching god!
2824 	buildRoguelike();
2825 	return;
2826     }
2827 
2828     // Only have to build the room.  It is assumed to have
2829     // the required crap in it.
2830     ROOM		room;
2831 
2832     buildRandomRoomFromDefinition(room, glb_allroomdefs[match]);
2833     drawRoom(room, true, false, false);
2834     populateRoom(room);
2835 }
2836 
2837 void
buildQuest(GOD_NAMES god)2838 MAP::buildQuest(GOD_NAMES god)
2839 {
2840     // Set our flags appropraitely
2841 #if 0
2842     myGlobalFlags &= ~MAPFLAG_DIG;
2843     myGlobalFlags &= ~MAPFLAG_NEWMOBS;
2844     myGlobalFlags |= MAPFLAG_NOITEMS;
2845 #endif
2846 
2847     // Find the appropriate room...
2848     int			i, nummatch, match = -1;
2849 
2850     nummatch = 0;
2851     for (i = 0; i < NUM_ALLROOMDEFS; i++)
2852     {
2853 	if (glb_allroomdefs[i]->quest == god)
2854 	{
2855 	    nummatch++;
2856 	    if (!rand_choice(nummatch))
2857 		match = i;
2858 	}
2859     }
2860 
2861     if (match == -1)
2862     {
2863 	// No matching god!
2864 	buildRoguelike();
2865 	return;
2866     }
2867 
2868     // Only have to build the room.  It is assumed to have
2869     // the required crap in it.
2870     ROOM		room;
2871 
2872     buildRandomRoomFromDefinition(room, glb_allroomdefs[match]);
2873     drawRoom(room, true, false, false);
2874     populateRoom(room);
2875 }
2876 
2877 class CIRCLE
2878 {
2879 public:
CIRCLE()2880     CIRCLE() {}
CIRCLE(int r)2881     CIRCLE(int r)
2882     {
2883 	CIRCLE::r = r;
2884     }
2885     int		x, y;
2886     int		r;
2887 
2888     bool	overlaps(const CIRCLE &circle) const;
2889 };
2890 
2891 bool
overlaps(const CIRCLE & circle) const2892 CIRCLE::overlaps(const CIRCLE &circle) const
2893 {
2894     int		dx, dy, r2, cr2;
2895 
2896     dx = circle.x - x;
2897     dy = circle.y - y;
2898 
2899     r2 = dx * dx + dy * dy;
2900     cr2 = circle.r + r + 2;	// Extra two is to get a wall between them.
2901     cr2 *= cr2;
2902 
2903     // See if combined radii of circle is within r2.
2904     if (cr2 >= r2)
2905 	return true;
2906 
2907     return false;
2908 }
2909 
2910 typedef PTRSTACK<CIRCLE> CIRCLELIST;
2911 
2912 bool
circleFits(int x,int y,int r,const CIRCLELIST & list)2913 circleFits(int x, int y, int r, const CIRCLELIST &list)
2914 {
2915     int		i, n;
2916     CIRCLE	test;
2917 
2918     test.x = x;
2919     test.y = y;
2920     test.r = r;
2921 
2922     n = list.entries();
2923     for (i = 0; i < n; i++)
2924     {
2925 	if (test.overlaps(list(i)))
2926 	{
2927 	    return false;
2928 	}
2929     }
2930     return true;
2931 }
2932 
2933 bool
createCircle(CIRCLE & circle,int r,const CIRCLELIST & list)2934 createCircle(CIRCLE &circle, int r, const CIRCLELIST &list)
2935 {
2936     int		x, y, numchoice = 0;
2937 
2938     for (y = r+1; y < MAP_HEIGHT - (r+1); y++)
2939     {
2940 	for (x = r+1; x < MAP_WIDTH - (r+1); x++)
2941 	{
2942 	    if (circleFits(x, y, r, list))
2943 	    {
2944 		numchoice++;
2945 		if (!rand_choice(numchoice))
2946 		{
2947 		    circle.x = x;
2948 		    circle.y = y;
2949 		    circle.r = r;
2950 		}
2951 	    }
2952 	}
2953     }
2954     if (numchoice)
2955 	return true;
2956     return false;
2957 }
2958 
2959 MOB *
createGoldenTridudeLair(int x,int y)2960 MAP::createGoldenTridudeLair(int x, int y)
2961 {
2962     MOB		*leader, *mob;
2963     ITEM	*artifact;
2964     int		 i;
2965     const int	 numslaves = 9;
2966     int		 placedx[numslaves] =
2967     { 0, 0, 0, 1, 2, 3, -1, -2, -3 };
2968     int		 placedy[numslaves] =
2969     { 2, 3, 4,-1,-2,-3, -1, -2, -3 };
2970     MOB_NAMES	 placetype[numslaves] =
2971     {
2972 	MOB_REDTRIDUDE, MOB_REDTRIDUDE, MOB_REDTRIDUDE,
2973 	MOB_BLUETRIDUDE, MOB_BLUETRIDUDE, MOB_BLUETRIDUDE,
2974 	MOB_PURPLETRIDUDE, MOB_PURPLETRIDUDE, MOB_PURPLETRIDUDE
2975     };
2976 
2977     leader = MOB::create(MOB_GOLDTRIDUDE);
2978     leader->setIntrinsic(INTRINSIC_LEADER);
2979     leader->move(x, y, true);
2980     registerMob(leader);
2981 
2982     // The leader gets a tasty artifact.
2983     artifact = ITEM::createRandomType(ITEMTYPE_ARTIFACT);
2984     acquireItem(artifact, x, y, 0);
2985 
2986     for (i = 0; i < numslaves; i++)
2987     {
2988 	mob = MOB::create(placetype[i]);
2989 	mob->makeSlaveOf(leader);
2990 
2991 	mob->move(x + placedx[i], y + placedy[i], true);
2992 	registerMob(mob);
2993     }
2994 
2995     return leader;
2996 }
2997 
2998 void
createColouredTridudeLair(int x,int y,MOB_NAMES tridude,MOB * trueleader)2999 MAP::createColouredTridudeLair(int x, int y, MOB_NAMES tridude, MOB *trueleader)
3000 {
3001     MOB		*leader, *mob;
3002     int		 i;
3003     const int	 numslaves = 6;
3004     int		 placedx[numslaves] =
3005     { 0, -1,  1, 0, -2,  2};
3006     int		 placedy[numslaves] =
3007     { 1, -1, -1, 2, -2, -2};
3008 
3009     leader = MOB::create(tridude);
3010     leader->makeUnique();
3011     leader->setIntrinsic(INTRINSIC_LEADER);
3012     leader->move(x, y, true);
3013     if (trueleader)
3014 	leader->makeSlaveOf(trueleader);
3015     registerMob(leader);
3016 
3017     for (i = 0; i < numslaves; i++)
3018     {
3019 	mob = MOB::create(tridude);
3020 	mob->makeSlaveOf(leader);
3021 
3022 	mob->move(x + placedx[i], y + placedy[i], true);
3023 	registerMob(mob);
3024     }
3025 }
3026 
3027 void
buildSpaceShip()3028 MAP::buildSpaceShip()
3029 {
3030     int			x, y, r, dx, dy, i;
3031     SQUARE_NAMES	square;
3032     CIRCLELIST		list;
3033     CIRCLE		circle;
3034 
3035     // Set our appropriate flags.
3036     myGlobalFlags &= ~MAPFLAG_DIG;
3037     // We'll do our own populating.
3038     myGlobalFlags &= ~MAPFLAG_NEWMOBS;
3039 
3040     // This is random placement so we try until we get the required mandatory
3041     // rooms
3042     while (1)
3043     {
3044 	bool		created = false;
3045 	clear();
3046 
3047 	list.clear();
3048 
3049 	while (1)
3050 	{
3051 	    // Dimension door.
3052 	    if (!createCircle(circle, 2, list))
3053 		break;
3054 	    drawCircle(circle.x, circle.y, circle.r, SQUARE_METALFLOOR, SQUAREFLAG_LIT);
3055 	    list.append(circle);
3056 	    setTile(circle.x, circle.y, SQUARE_DIMDOOR);
3057 
3058 	    // Giant tridude
3059 	    if (!createCircle(circle, 5, list))
3060 		break;
3061 	    list.append(circle);
3062 	    drawCircle(circle.x, circle.y, circle.r, SQUARE_METALFLOOR, SQUAREFLAG_LIT);
3063 
3064 	    // Tri coloured tridude lairs
3065 	    for (i = 0; i < 3; i++)
3066 	    {
3067 		if (!createCircle(circle, 4, list))
3068 		    break;
3069 		drawCircle(circle.x, circle.y, circle.r, SQUARE_METALFLOOR, SQUAREFLAG_LIT);
3070 		list.append(circle);
3071 	    }
3072 	    if (i != 3)
3073 		break;
3074 
3075 	    // Some bonus rooms.
3076 	    // We don't care if these fail.
3077 	    for (i = 0; i < 6; i++)
3078 	    {
3079 		if (!createCircle(circle, 2+rand_choice(2), list))
3080 		    break;
3081 		drawCircle(circle.x, circle.y, circle.r, SQUARE_METALFLOOR, SQUAREFLAG_LIT);
3082 		list.append(circle);
3083 	    }
3084 
3085 	    created = true;
3086 	    break;
3087 	}
3088 
3089 	if (created)
3090 	    break;
3091     }
3092 
3093     // Populate rooms.
3094     if (!ourIsLoading)
3095     {
3096 	RAND_STATEPUSH		saverng;
3097 	const int		numlair = 3;
3098 	MOB			*goldtridude;
3099 	MOB_NAMES		lairtype[numlair] =
3100 	{
3101 	    MOB_REDTRIDUDE,
3102 	    MOB_BLUETRIDUDE,
3103 	    MOB_PURPLETRIDUDE
3104 	};
3105 	// Create the golden lair.
3106 	goldtridude = createGoldenTridudeLair(list(1).x, list(1).y);
3107 	// Rooms 2, 3, 4 are all tridude lairs
3108 	for (i = 0; i < numlair; i++)
3109 	{
3110 	    // Each lair is a feudal system.  Knock out the local
3111 	    // leader and they will fight the other packs.
3112 	    createColouredTridudeLair(list(i+2).x, list(i+2).y, lairtype[i],
3113 				    goldtridude);
3114 	}
3115 
3116 	// Now a smattering of regular tridudes.
3117 	for (i = 0; i < 10; i++)
3118 	{
3119 	    MOB		*mob;
3120 
3121 	    // Abort if no more room for some reason.
3122 	    if (!findRandomLoc(x, y, MOVE_WALK,
3123 			    false, false, false, false, false, false))
3124 		break;
3125 
3126 	    mob = MOB::create(MOB_TRIDUDE);
3127 
3128 	    mob->move(x, y, true);
3129 	    // These all owe allegiance to the giant tridude.
3130 	    mob->makeSlaveOf(goldtridude);
3131 	    registerMob(mob);
3132 	}
3133     }
3134 
3135     // Connect all rooms.
3136     // We have two sets: connected, and unconnected.
3137     // We find the closest pair between the two sets and connect them
3138     // then add the new room to the connected set.
3139     for (i = 1; i < list.entries(); i++)
3140     {
3141 	int		j, k;
3142 	int		minr2 = MAP_WIDTH*MAP_WIDTH*4;
3143 	int		minid1 = i, minid2 = 0;
3144 
3145 	for (j = 0; j < i; j++)
3146 	{
3147 	    for (k = i; k < list.entries(); k++)
3148 	    {
3149 		dx = list(j).x - list(k).x;
3150 		dy = list(j).y - list(k).y;
3151 		r = dx * dx + dy * dy;
3152 		if (r < minr2)
3153 		{
3154 		    minid2 = j;
3155 		    minid1 = k;
3156 		    minr2 = r;
3157 		}
3158 	    }
3159 	}
3160 
3161 	// Draw a line between the centers.
3162 	drawLine(list(minid1).x, list(minid1).y,
3163 		 list(minid2).x, list(minid2).y,
3164 		 SQUARE_METALFLOOR);
3165 
3166 	// Min pair is minid2 and minid1.
3167 	// Swap i with minid1 to mark it connected.
3168 	circle = list(i);
3169 	list.set(i, list(minid1));
3170 	list.set(minid1, circle);
3171     }
3172 
3173     // Outline all rooms with walls.
3174     for (y = 0; y < MAP_HEIGHT; y++)
3175     {
3176 	for (x = 0; x < MAP_WIDTH; x++)
3177 	{
3178 	    if (getTile(x, y) == SQUARE_EMPTY)
3179 	    {
3180 		// If a eight way neighbour is not empty and not
3181 		// a metal wall, convert.
3182 		for (dy = -1; dy <= 1; dy++)
3183 		{
3184 		    for (dx = -1; dx <= 1; dx++)
3185 		    {
3186 			if (!dx && !dy)
3187 			    continue;
3188 			if (x+dx < 0 || y + dy < 0)
3189 			    continue;
3190 			if (x+dx >= MAP_WIDTH || y + dy >= MAP_HEIGHT)
3191 			    continue;
3192 			square = getTile(x+dx, y+dy);
3193 			if (square != SQUARE_EMPTY && square != SQUARE_METALWALL)
3194 			{
3195 			    setTile(x, y, SQUARE_METALWALL);
3196 			    dx = dy = 2;
3197 			}
3198 		    }
3199 		}
3200 	    }
3201 	}
3202     }
3203 
3204     // Create viewports anywhere which is bordered on one half by
3205     // stars and the other by non-stars and is a metalwall.
3206     for (y = 1; y < MAP_HEIGHT-1; y++)
3207     {
3208 	for (x = 1; x < MAP_WIDTH-1; x++)
3209 	{
3210 	    const int viewportchance = 15;
3211 
3212 	    if (getTile(x, y) != SQUARE_METALWALL)
3213 		continue;
3214 
3215 	    if (getTile(x-1, y) != SQUARE_METALWALL &&
3216 		getTile(x+1, y) != SQUARE_METALWALL)
3217 	    {
3218 		if (getTile(x-1, y) == SQUARE_EMPTY ||
3219 		    getTile(x+1, y) == SQUARE_EMPTY)
3220 		{
3221 		    if (rand_chance(viewportchance))
3222 			setTile(x, y, SQUARE_VIEWPORT);
3223 		}
3224 	    }
3225 	    if (getTile(x, y-1) != SQUARE_METALWALL &&
3226 		getTile(x, y+1) != SQUARE_METALWALL)
3227 	    {
3228 		if (getTile(x, y-1) == SQUARE_EMPTY ||
3229 		    getTile(x, y+1) == SQUARE_EMPTY)
3230 		{
3231 		    if (rand_chance(viewportchance))
3232 			setTile(x, y, SQUARE_VIEWPORT);
3233 		}
3234 	    }
3235 	}
3236     }
3237 
3238     const int		numstars = 5;
3239 
3240     // Mark everything lit & having a random star.
3241     // We also turn off the mob generation inside stars
3242     // to stop you from being teleported there.
3243     const SQUARE_NAMES starlist[numstars] =
3244     {
3245 	SQUARE_STARS1,
3246 	SQUARE_STARS2,
3247 	SQUARE_STARS3,
3248 	SQUARE_STARS4,
3249 	SQUARE_STARS5
3250     };
3251     for (y = 0; y < MAP_HEIGHT; y++)
3252     {
3253 	for (x = 0; x < MAP_WIDTH; x++)
3254 	{
3255 	    setFlag(x, y, SQUAREFLAG_LIT);
3256 	    // For debugging:
3257     	    // setFlag(x, y, SQUAREFLAG_MAPPED);
3258 
3259 	    if (getTile(x, y) == SQUARE_EMPTY)
3260 	    {
3261 		setTile(x, y, starlist[rand_choice(numstars)]);
3262 		setFlag(x, y, SQUAREFLAG_NOMOB);
3263 	    }
3264 	}
3265     }
3266 }
3267 
3268 void
buildHello()3269 MAP::buildHello()
3270 {
3271     int			x, y, r;
3272     int			numcirc;
3273     SQUARE_NAMES	square;
3274     SQUAREFLAG_NAMES	flag;
3275 
3276     // Set our appropriate flags.
3277     myGlobalFlags &= ~MAPFLAG_DIG;
3278 
3279     clear();
3280 
3281     ROOM			room;
3282 
3283     // I'm just typing this to note I am currently flying first class
3284     // Woohoo!
3285     buildRandomRoomFromDefinition(room,
3286 		      chooseRandomRoom(myDepth));
3287     drawRoom(room, true, false, false);
3288     populateRoom(room);
3289 
3290     for (numcirc = 0; numcirc < 20; numcirc++)
3291     {
3292 	x = rand_range(1, MAP_WIDTH);
3293 	y = rand_range(1, MAP_HEIGHT);
3294 	r = rand_range(1, 6);
3295 
3296 	switch (rand_choice(3))
3297 	{
3298 	    case 0:
3299 		square = SQUARE_CORRIDOR;
3300 		flag = SQUAREFLAG_NONE;
3301 		break;
3302 	    case 1:
3303 		square = SQUARE_LAVA;
3304 		flag = SQUAREFLAG_LIT;
3305 		break;
3306 	    case 2:
3307 		square = SQUARE_WATER;
3308 		flag = SQUAREFLAG_NONE;
3309 		break;
3310 	    default:
3311 		UT_ASSERT(0);
3312 		square = SQUARE_EMPTY;
3313 		flag = SQUAREFLAG_NONE;
3314 		break;
3315 	}
3316 
3317 	drawCircle(x, y, r, square, flag);
3318     }
3319 
3320     closeUnusedExits(room, true);
3321 
3322     // Build the staircases..
3323     while (1)
3324     {
3325         findRandomLoc(x, y, MOVE_STD_SWIM,
3326 			true, false, false, false, false, false);
3327 
3328 	if (!room.definition->allowstairs &&
3329 	     x >= room.l.x && x <= room.h.x &&
3330 	     y >= room.l.y && y <= room.h.y)
3331 	    continue;
3332 
3333 	if (getTile(x, y) == SQUARE_CORRIDOR || getTile(x, y) == SQUARE_WATER
3334 	    || getTile(x, y) == SQUARE_LAVA)
3335 	{
3336 	    setTile(x, y, SQUARE_LADDERUP);
3337 	    break;
3338 	}
3339     }
3340 
3341     // There is no down stairs from hello.
3342 #if 0
3343     while (1)
3344     {
3345         findRandomLoc(x, y, MOVE_STD_SWIM,
3346 			true, false, false, false, false, false);
3347 
3348 	if (!room.definition->allowstairs &&
3349 	     x >= room.l.x && x <= room.h.x &&
3350 	     y >= room.l.y && y <= room.h.y)
3351 	    continue;
3352 
3353 	if (getTile(x, y) == SQUARE_CORRIDOR || getTile(x, y) == SQUARE_WATER
3354 	    || getTile(x, y) == SQUARE_LAVA)
3355 	{
3356 	    setTile(x, y, SQUARE_LADDERDOWN);
3357 	    break;
3358 	}
3359     }
3360 #endif
3361 }
3362 
3363 void
drawLine(int x1,int y1,int x2,int y2,SQUARE_NAMES square,bool overwrite)3364 MAP::drawLine(int x1, int y1, int x2, int y2,
3365 		SQUARE_NAMES square,
3366 		bool overwrite)
3367 {
3368     int		cx, cy, x, y, acx, acy;
3369 
3370     cx = x2 - x1;
3371     cy = y2 - y1;
3372     acx = abs(cx);
3373     acy = abs(cy);
3374 
3375 
3376     // Start and beginning are done separately as this code
3377     // is stolen from the LOS code that ignore start and end.
3378     if (overwrite || getTile(x1, y1) == SQUARE_EMPTY)
3379 	setTile(x1, y1, square);
3380     if (overwrite || getTile(x2, y2) == SQUARE_EMPTY)
3381 	setTile(x2, y2, square);
3382 
3383     if (acx > acy)
3384     {
3385 	int		dx, dy, error;
3386 
3387 	dx = SIGN(cx);
3388 	dy = SIGN(cy);
3389 
3390 	error = 0;
3391 	error = acy;
3392 	y = y1;
3393 	for (x = x1 + dx; x != x2; x += dx)
3394 	{
3395 	    if (error >= acx)
3396 	    {
3397 		error -= acx;
3398 		if (overwrite || getTile(x, y) == SQUARE_EMPTY)
3399 		    setTile(x, y, square);
3400 		y += dy;
3401 	    }
3402 
3403 	    error += acy;
3404 	    if (overwrite || getTile(x, y) == SQUARE_EMPTY)
3405 		setTile(x, y, square);
3406 	}
3407     }
3408     else
3409     {
3410 	int		dx, dy, error;
3411 
3412 	dx = SIGN(cx);
3413 	dy = SIGN(cy);
3414 
3415 	error = 0;
3416 	error = acx;
3417 	x = x1;
3418 	for (y = y1 + dy; y != y2; y += dy)
3419 	{
3420 	    if (error >= acy)
3421 	    {
3422 		error -= acy;
3423 		if (overwrite || getTile(x, y) == SQUARE_EMPTY)
3424 		    setTile(x, y, square);
3425 		x += dx;
3426 	    }
3427 
3428 	    error += acx;
3429 	    if (overwrite || getTile(x, y) == SQUARE_EMPTY)
3430 		setTile(x, y, square);
3431 	}
3432     }
3433 
3434 }
3435 
3436 void
drawCircle(int cx,int cy,int rad,SQUARE_NAMES square,SQUAREFLAG_NAMES flag,bool overwrite)3437 MAP::drawCircle(int cx, int cy, int rad,
3438 		SQUARE_NAMES square, SQUAREFLAG_NAMES flag,
3439 		bool overwrite)
3440 {
3441     int		x, y, r2;
3442 
3443     // Expand radius by .5 so we don't get pimply circles.
3444     r2 = (int) ((rad + 0.5) * (rad + 0.5));
3445 
3446     for (y = cy - rad; y <= cy + rad; y++)
3447     {
3448 	if (y < 0)
3449 	    continue;
3450 	// Leave blank tile
3451 	if (y >= MAP_HEIGHT-1)
3452 	    break;
3453 
3454 	for (x = cx - rad; x <= cx + rad; x++)
3455 	{
3456 	    if (x < 0)
3457 		continue;
3458 	    // Leave blank tile
3459 	    if (x >= MAP_WIDTH-1)
3460 		break;
3461 
3462 	    // Only overwrite earth tiles.
3463 	    if (!overwrite && getTile(x, y) != SQUARE_EMPTY)
3464 		continue;
3465 
3466 	    // Determine if this is in range.
3467 	    // My world for a bressenham circle algorithm!
3468 	    // Okay, not my world.
3469 	    // I've written enough of them that this code is ar esult
3470 	    // of extreme laziness/drunkness, rather than a lack of awareness
3471 	    // of a perfectly fine and fast appraoch.
3472 	    if ((y - cy) * (y - cy) + (x - cx) * (x - cx) < r2)
3473 	    {
3474 		setTile(x, y, square);
3475 		setRawFlag(x, y, flag);
3476 	    }
3477 	}
3478     }
3479 }
3480