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