1 /**
2 * @file themes.cpp
3 *
4 * Implementation of the theme room placing algorithms.
5 */
6 #include "all.h"
7
8 DEVILUTION_BEGIN_NAMESPACE
9
10 int numthemes;
11 BOOL armorFlag;
12 BOOL ThemeGoodIn[4];
13 BOOL weaponFlag;
14 BOOL treasureFlag;
15 BOOL mFountainFlag;
16 BOOL cauldronFlag;
17 BOOL tFountainFlag;
18 int zharlib;
19 int themex;
20 int themey;
21 int themeVar1;
22 ThemeStruct themes[MAXTHEMES];
23 BOOL pFountainFlag;
24 BOOL bFountainFlag;
25 BOOL bCrossFlag;
26
27 /** Specifies the set of special theme IDs from which one will be selected at random. */
28 int ThemeGood[4] = { THEME_GOATSHRINE, THEME_SHRINE, THEME_SKELROOM, THEME_LIBRARY };
29 /** Specifies a 5x5 area to fit theme objects. */
30 int trm5x[] = {
31 -2, -1, 0, 1, 2,
32 -2, -1, 0, 1, 2,
33 -2, -1, 0, 1, 2,
34 -2, -1, 0, 1, 2,
35 -2, -1, 0, 1, 2
36 };
37 /** Specifies a 5x5 area to fit theme objects. */
38 int trm5y[] = {
39 -2, -2, -2, -2, -2,
40 -1, -1, -1, -1, -1,
41 0, 0, 0, 0, 0,
42 1, 1, 1, 1, 1,
43 2, 2, 2, 2, 2
44 };
45 /** Specifies a 3x3 area to fit theme objects. */
46 int trm3x[] = {
47 -1, 0, 1,
48 -1, 0, 1,
49 -1, 0, 1
50 };
51 /** Specifies a 3x3 area to fit theme objects. */
52 int trm3y[] = {
53 -1, -1, -1,
54 0, 0, 0,
55 1, 1, 1
56 };
57
TFit_Shrine(int i)58 BOOL TFit_Shrine(int i)
59 {
60 int xp, yp, found;
61
62 xp = 0;
63 yp = 0;
64 found = 0;
65 while (found == 0) {
66 if (dTransVal[xp][yp] == themes[i].ttval) {
67 if (nTrapTable[dPiece[xp][yp - 1]]
68 && !nSolidTable[dPiece[xp - 1][yp]]
69 && !nSolidTable[dPiece[xp + 1][yp]]
70 && dTransVal[xp - 1][yp] == themes[i].ttval
71 && dTransVal[xp + 1][yp] == themes[i].ttval
72 && dObject[xp - 1][yp - 1] == 0
73 && dObject[xp + 1][yp - 1] == 0) {
74 found = 1;
75 }
76 if (found == 0
77 && nTrapTable[dPiece[xp - 1][yp]]
78 && !nSolidTable[dPiece[xp][yp - 1]]
79 && !nSolidTable[dPiece[xp][yp + 1]]
80 && dTransVal[xp][yp - 1] == themes[i].ttval
81 && dTransVal[xp][yp + 1] == themes[i].ttval
82 && dObject[xp - 1][yp - 1] == 0
83 && dObject[xp - 1][yp + 1] == 0) {
84 found = 2;
85 }
86 }
87 if (found == 0) {
88 xp++;
89 if (xp == MAXDUNX) {
90 xp = 0;
91 yp++;
92 if (yp == MAXDUNY)
93 return FALSE;
94 }
95 }
96 }
97 themex = xp;
98 themey = yp;
99 themeVar1 = found;
100 return TRUE;
101 }
102
TFit_Obj5(int t)103 BOOL TFit_Obj5(int t)
104 {
105 int xp, yp;
106 int i, r, rs;
107 BOOL found;
108
109 xp = 0;
110 yp = 0;
111 r = random_(0, 5) + 1;
112 rs = r;
113 while (r > 0) {
114 found = FALSE;
115 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
116 found = TRUE;
117 for (i = 0; found && i < 25; i++) {
118 if (nSolidTable[dPiece[xp + trm5x[i]][yp + trm5y[i]]]) {
119 found = FALSE;
120 }
121 if (dTransVal[xp + trm5x[i]][yp + trm5y[i]] != themes[t].ttval) {
122 found = FALSE;
123 }
124 }
125 }
126
127 if (!found) {
128 xp++;
129 if (xp == MAXDUNX) {
130 xp = 0;
131 yp++;
132 if (yp == MAXDUNY) {
133 if (r == rs) {
134 return FALSE;
135 }
136 yp = 0;
137 }
138 }
139 continue;
140 }
141
142 r--;
143 }
144
145 themex = xp;
146 themey = yp;
147
148 return TRUE;
149 }
150
TFit_SkelRoom(int t)151 BOOL TFit_SkelRoom(int t)
152 {
153 int i;
154
155 if (leveltype != DTYPE_CATHEDRAL && leveltype != DTYPE_CATACOMBS) {
156 return FALSE;
157 }
158
159 for (i = 0; i < nummtypes; i++) {
160 if (IsSkel(Monsters[i].mtype)) {
161 themeVar1 = i;
162 return TFit_Obj5(t);
163 }
164 }
165
166 return FALSE;
167 }
168
TFit_GoatShrine(int t)169 BOOL TFit_GoatShrine(int t)
170 {
171 int i;
172
173 for (i = 0; i < nummtypes; i++) {
174 if (IsGoat(Monsters[i].mtype)) {
175 themeVar1 = i;
176 return TFit_Obj5(t);
177 }
178 }
179
180 return FALSE;
181 }
182
CheckThemeObj3(int xp,int yp,int t,int f)183 BOOL CheckThemeObj3(int xp, int yp, int t, int f)
184 {
185 int i;
186
187 for (i = 0; i < 9; i++) {
188 if (xp + trm3x[i] < 0 || yp + trm3y[i] < 0)
189 return FALSE;
190 if (nSolidTable[dPiece[xp + trm3x[i]][yp + trm3y[i]]])
191 return FALSE;
192 if (dTransVal[xp + trm3x[i]][yp + trm3y[i]] != themes[t].ttval)
193 return FALSE;
194 if (dObject[xp + trm3x[i]][yp + trm3y[i]])
195 return FALSE;
196 if (f != -1 && random_(0, f) == 0)
197 return FALSE;
198 }
199
200 return TRUE;
201 }
202
TFit_Obj3(int t)203 BOOL TFit_Obj3(int t)
204 {
205 int xp, yp;
206 char objrnd[4] = { 4, 4, 3, 5 };
207
208 for (yp = 1; yp < MAXDUNY - 1; yp++) {
209 for (xp = 1; xp < MAXDUNX - 1; xp++) {
210 if (CheckThemeObj3(xp, yp, t, objrnd[leveltype - 1])) {
211 themex = xp;
212 themey = yp;
213 return TRUE;
214 }
215 }
216 }
217
218 return FALSE;
219 }
220
CheckThemeReqs(int t)221 BOOL CheckThemeReqs(int t)
222 {
223 BOOL rv;
224
225 rv = TRUE;
226 switch (t) {
227 case THEME_SHRINE:
228 case THEME_SKELROOM:
229 case THEME_LIBRARY:
230 if (leveltype == DTYPE_CAVES || leveltype == DTYPE_HELL) {
231 rv = FALSE;
232 }
233 break;
234 case THEME_BLOODFOUNTAIN:
235 if (!bFountainFlag) {
236 rv = FALSE;
237 }
238 break;
239 case THEME_PURIFYINGFOUNTAIN:
240 if (!pFountainFlag) {
241 rv = FALSE;
242 }
243 break;
244 case THEME_ARMORSTAND:
245 if (leveltype == DTYPE_CATHEDRAL) {
246 rv = FALSE;
247 }
248 break;
249 case THEME_CAULDRON:
250 if (leveltype != DTYPE_HELL || !cauldronFlag) {
251 rv = FALSE;
252 }
253 break;
254 case THEME_MURKYFOUNTAIN:
255 if (!mFountainFlag) {
256 rv = FALSE;
257 }
258 break;
259 case THEME_TEARFOUNTAIN:
260 if (!tFountainFlag) {
261 rv = FALSE;
262 }
263 break;
264 case THEME_WEAPONRACK:
265 if (leveltype == DTYPE_CATHEDRAL) {
266 rv = FALSE;
267 }
268 break;
269 }
270
271 return rv;
272 }
273
SpecialThemeFit(int i,int t)274 BOOL SpecialThemeFit(int i, int t)
275 {
276 BOOL rv;
277
278 rv = CheckThemeReqs(t);
279 switch (t) {
280 case THEME_SHRINE:
281 case THEME_LIBRARY:
282 if (rv) {
283 rv = TFit_Shrine(i);
284 }
285 break;
286 case THEME_SKELROOM:
287 if (rv) {
288 rv = TFit_SkelRoom(i);
289 }
290 break;
291 case THEME_BLOODFOUNTAIN:
292 if (rv) {
293 rv = TFit_Obj5(i);
294 }
295 if (rv) {
296 bFountainFlag = FALSE;
297 }
298 break;
299 case THEME_PURIFYINGFOUNTAIN:
300 if (rv) {
301 rv = TFit_Obj5(i);
302 }
303 if (rv) {
304 pFountainFlag = FALSE;
305 }
306 break;
307 case THEME_MURKYFOUNTAIN:
308 if (rv) {
309 rv = TFit_Obj5(i);
310 }
311 if (rv) {
312 mFountainFlag = FALSE;
313 }
314 break;
315 case THEME_TEARFOUNTAIN:
316 if (rv) {
317 rv = TFit_Obj5(i);
318 }
319 if (rv) {
320 tFountainFlag = FALSE;
321 }
322 break;
323 case THEME_CAULDRON:
324 if (rv) {
325 rv = TFit_Obj5(i);
326 }
327 if (rv) {
328 cauldronFlag = FALSE;
329 }
330 break;
331 case THEME_GOATSHRINE:
332 if (rv) {
333 rv = TFit_GoatShrine(i);
334 }
335 break;
336 case THEME_TORTURE:
337 case THEME_DECAPITATED:
338 case THEME_ARMORSTAND:
339 case THEME_BRNCROSS:
340 case THEME_WEAPONRACK:
341 if (rv) {
342 rv = TFit_Obj3(i);
343 }
344 break;
345 case THEME_TREASURE:
346 rv = treasureFlag;
347 if (rv) {
348 treasureFlag = FALSE;
349 }
350 break;
351 }
352
353 return rv;
354 }
355
CheckThemeRoom(int tv)356 BOOL CheckThemeRoom(int tv)
357 {
358 int i, j, tarea;
359
360 for (i = 0; i < numtrigs; i++) {
361 if (dTransVal[trigs[i]._tx][trigs[i]._ty] == tv)
362 return FALSE;
363 }
364
365 tarea = 0;
366 for (j = 0; j < MAXDUNY; j++) {
367 for (i = 0; i < MAXDUNX; i++) {
368 if (dTransVal[i][j] != tv)
369 continue;
370 if (dFlags[i][j] & BFLAG_POPULATED)
371 return FALSE;
372
373 tarea++;
374 }
375 }
376
377 if (leveltype == DTYPE_CATHEDRAL && (tarea < 9 || tarea > 100))
378 return FALSE;
379
380 for (j = 0; j < MAXDUNY; j++) {
381 for (i = 0; i < MAXDUNX; i++) {
382 if (dTransVal[i][j] != tv || nSolidTable[dPiece[i][j]])
383 continue;
384 if (dTransVal[i - 1][j] != tv && !nSolidTable[dPiece[i - 1][j]])
385 return FALSE;
386 if (dTransVal[i + 1][j] != tv && !nSolidTable[dPiece[i + 1][j]])
387 return FALSE;
388 if (dTransVal[i][j - 1] != tv && !nSolidTable[dPiece[i][j - 1]])
389 return FALSE;
390 if (dTransVal[i][j + 1] != tv && !nSolidTable[dPiece[i][j + 1]])
391 return FALSE;
392 }
393 }
394
395 return TRUE;
396 }
397
InitThemes()398 void InitThemes()
399 {
400 int i, j;
401
402 zharlib = -1;
403 numthemes = 0;
404 armorFlag = TRUE;
405 bFountainFlag = TRUE;
406 cauldronFlag = TRUE;
407 mFountainFlag = TRUE;
408 pFountainFlag = TRUE;
409 tFountainFlag = TRUE;
410 treasureFlag = TRUE;
411 bCrossFlag = FALSE;
412 weaponFlag = TRUE;
413
414 if (currlevel == 16)
415 return;
416
417 if (leveltype == DTYPE_CATHEDRAL) {
418 for (i = 0; i < sizeof(ThemeGoodIn) / sizeof(ThemeGoodIn[0]); i++)
419 ThemeGoodIn[i] = FALSE;
420
421 for (i = 0; i < 256 && numthemes < MAXTHEMES; i++) {
422 if (CheckThemeRoom(i)) {
423 themes[numthemes].ttval = i;
424 for (j = ThemeGood[random_(0, 4)];; j = random_(0, 17)) {
425 if (SpecialThemeFit(numthemes, j)) {
426 break;
427 }
428 }
429 themes[numthemes].ttype = j;
430 numthemes++;
431 }
432 }
433 }
434 if (leveltype == DTYPE_CATACOMBS || leveltype == DTYPE_CAVES || leveltype == DTYPE_HELL) {
435 for (i = 0; i < themeCount; i++)
436 themes[i].ttype = THEME_NONE;
437 if (QuestStatus(Q_ZHAR)) {
438 for (j = 0; j < themeCount; j++) {
439 themes[j].ttval = themeLoc[j].ttval;
440 if (SpecialThemeFit(j, THEME_LIBRARY)) {
441 themes[j].ttype = THEME_LIBRARY;
442 zharlib = j;
443 break;
444 }
445 }
446 }
447 for (i = 0; i < themeCount; i++) {
448 if (themes[i].ttype == THEME_NONE) {
449 themes[i].ttval = themeLoc[i].ttval;
450 for (j = ThemeGood[random_(0, 4)];; j = random_(0, 17)) {
451 if (SpecialThemeFit(i, j)) {
452 break;
453 }
454 }
455 themes[i].ttype = j;
456 }
457 }
458 numthemes += themeCount;
459 }
460 }
461
462 /**
463 * @brief HoldThemeRooms marks theme rooms as populated.
464 */
HoldThemeRooms()465 void HoldThemeRooms()
466 {
467 int i, x, y;
468 char v;
469
470 if (currlevel != 16) {
471 if (leveltype == DTYPE_CATHEDRAL) {
472 for (i = 0; i < numthemes; i++) {
473 v = themes[i].ttval;
474 for (y = 0; y < MAXDUNY; y++) {
475 for (x = 0; x < MAXDUNX; x++) {
476 if (dTransVal[x][y] == v) {
477 dFlags[x][y] |= BFLAG_POPULATED;
478 }
479 }
480 }
481 }
482 } else {
483 DRLG_HoldThemeRooms();
484 }
485 }
486 }
487
488 /**
489 * PlaceThemeMonsts places theme monsters with the specified frequency.
490 *
491 * @param t theme number (index into themes array).
492 * @param f frequency (1/f likelihood of adding monster).
493 */
PlaceThemeMonsts(int t,int f)494 void PlaceThemeMonsts(int t, int f)
495 {
496 int xp, yp;
497 int scattertypes[138];
498 int numscattypes, mtype, i;
499
500 numscattypes = 0;
501 for (i = 0; i < nummtypes; i++) {
502 if (Monsters[i].mPlaceFlags & PLACE_SCATTER) {
503 scattertypes[numscattypes] = i;
504 numscattypes++;
505 }
506 }
507 mtype = scattertypes[random_(0, numscattypes)];
508 for (yp = 0; yp < MAXDUNY; yp++) {
509 for (xp = 0; xp < MAXDUNX; xp++) {
510 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]] && dItem[xp][yp] == 0 && dObject[xp][yp] == 0) {
511 if (random_(0, f) == 0) {
512 AddMonster(xp, yp, random_(0, 8), mtype, TRUE);
513 }
514 }
515 }
516 }
517 }
518
519 /**
520 * Theme_Barrel initializes the barrel theme.
521 *
522 * @param t theme number (index into themes array).
523 */
Theme_Barrel(int t)524 void Theme_Barrel(int t)
525 {
526 int xp, yp, r;
527 char barrnd[4] = { 2, 6, 4, 8 };
528 char monstrnd[4] = { 5, 7, 3, 9 };
529
530 for (yp = 0; yp < MAXDUNY; yp++) {
531 for (xp = 0; xp < MAXDUNX; xp++) {
532 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
533 if (random_(0, barrnd[leveltype - 1]) == 0) {
534 if (random_(0, barrnd[leveltype - 1]) == 0) {
535 r = OBJ_BARREL;
536 } else {
537 r = OBJ_BARRELEX;
538 }
539 AddObject(r, xp, yp);
540 }
541 }
542 }
543 }
544 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
545 }
546
547 /**
548 * Theme_Shrine initializes the shrine theme.
549 *
550 * @param t theme number (index into themes array).
551 */
Theme_Shrine(int t)552 void Theme_Shrine(int t)
553 {
554 char monstrnd[4] = { 6, 6, 3, 9 };
555
556 TFit_Shrine(t);
557 if (themeVar1 == 1) {
558 AddObject(OBJ_CANDLE2, themex - 1, themey);
559 AddObject(OBJ_SHRINER, themex, themey);
560 AddObject(OBJ_CANDLE2, themex + 1, themey);
561 } else {
562 AddObject(OBJ_CANDLE2, themex, themey - 1);
563 AddObject(OBJ_SHRINEL, themex, themey);
564 AddObject(OBJ_CANDLE2, themex, themey + 1);
565 }
566 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
567 }
568
569 /**
570 * Theme_MonstPit initializes the monster pit theme.
571 *
572 * @param t theme number (index into themes array).
573 */
Theme_MonstPit(int t)574 void Theme_MonstPit(int t)
575 {
576 int r;
577 int ixp, iyp;
578 char monstrnd[4] = { 6, 7, 3, 9 };
579
580 r = random_(0, 100) + 1;
581 ixp = 0;
582 iyp = 0;
583 while (r > 0) {
584 if (dTransVal[ixp][iyp] == themes[t].ttval && !nSolidTable[dPiece[ixp][iyp]]) {
585 --r;
586 }
587 if (r <= 0)
588 continue;
589 ixp++;
590 if (ixp == MAXDUNX) {
591 ixp = 0;
592 iyp++;
593 if (iyp == MAXDUNY) {
594 iyp = 0;
595 }
596 }
597 }
598 CreateRndItem(ixp, iyp, TRUE, FALSE, TRUE);
599 ItemNoFlippy();
600 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
601 }
602
603 /**
604 * Theme_SkelRoom initializes the skeleton room theme.
605 *
606 * @param t theme number (index into themes array).
607 */
Theme_SkelRoom(int t)608 void Theme_SkelRoom(int t)
609 {
610 int xp, yp, i;
611 char monstrnd[4] = { 6, 7, 3, 9 };
612
613 TFit_SkelRoom(t);
614
615 xp = themex;
616 yp = themey;
617
618 AddObject(OBJ_SKFIRE, xp, yp);
619
620 if (random_(0, monstrnd[leveltype - 1]) != 0) {
621 i = PreSpawnSkeleton();
622 SpawnSkeleton(i, xp - 1, yp - 1);
623 } else {
624 AddObject(OBJ_BANNERL, xp - 1, yp - 1);
625 }
626
627 i = PreSpawnSkeleton();
628 SpawnSkeleton(i, xp, yp - 1);
629
630 if (random_(0, monstrnd[leveltype - 1]) != 0) {
631 i = PreSpawnSkeleton();
632 SpawnSkeleton(i, xp + 1, yp - 1);
633 } else {
634 AddObject(OBJ_BANNERR, xp + 1, yp - 1);
635 }
636 if (random_(0, monstrnd[leveltype - 1]) != 0) {
637 i = PreSpawnSkeleton();
638 SpawnSkeleton(i, xp - 1, yp);
639 } else {
640 AddObject(OBJ_BANNERM, xp - 1, yp);
641 }
642 if (random_(0, monstrnd[leveltype - 1]) != 0) {
643 i = PreSpawnSkeleton();
644 SpawnSkeleton(i, xp + 1, yp);
645 } else {
646 AddObject(OBJ_BANNERM, xp + 1, yp);
647 }
648 if (random_(0, monstrnd[leveltype - 1]) != 0) {
649 i = PreSpawnSkeleton();
650 SpawnSkeleton(i, xp - 1, yp + 1);
651 } else {
652 AddObject(OBJ_BANNERR, xp - 1, yp + 1);
653 }
654
655 i = PreSpawnSkeleton();
656 SpawnSkeleton(i, xp, yp + 1);
657
658 if (random_(0, monstrnd[leveltype - 1]) != 0) {
659 i = PreSpawnSkeleton();
660 SpawnSkeleton(i, xp + 1, yp + 1);
661 } else {
662 AddObject(OBJ_BANNERL, xp + 1, yp + 1);
663 }
664
665 if (dObject[xp][yp - 3] == 0) {
666 AddObject(OBJ_SKELBOOK, xp, yp - 2);
667 }
668 if (dObject[xp][yp + 3] == 0) {
669 AddObject(OBJ_SKELBOOK, xp, yp + 2);
670 }
671 }
672
673 /**
674 * Theme_Treasure initializes the treasure theme.
675 *
676 * @param t theme number (index into themes array).
677 */
Theme_Treasure(int t)678 void Theme_Treasure(int t)
679 {
680 int xp, yp;
681 int i;
682 char treasrnd[4] = { 4, 9, 7, 10 };
683 char monstrnd[4] = { 6, 8, 3, 7 };
684
685 AdvanceRndSeed();
686 for (yp = 0; yp < MAXDUNY; yp++) {
687 for (xp = 0; xp < MAXDUNX; xp++) {
688 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
689 int rv = random_(0, treasrnd[leveltype - 1]);
690 // BUGFIX: the `2*` in `2*random_(0, treasrnd...) == 0` has no effect, should probably be `random_(0, 2*treasrnd...) == 0`
691 if ((2 * random_(0, treasrnd[leveltype - 1])) == 0) {
692 CreateTypeItem(xp, yp, FALSE, ITYPE_GOLD, IMISC_NONE, FALSE, TRUE);
693 ItemNoFlippy();
694 }
695 if (rv == 0) {
696 CreateRndItem(xp, yp, FALSE, FALSE, TRUE);
697 ItemNoFlippy();
698 }
699 if (rv == 0 || rv >= treasrnd[leveltype - 1] - 2) {
700 i = ItemNoFlippy();
701 if (rv >= treasrnd[leveltype - 1] - 2 && leveltype != DTYPE_CATHEDRAL) {
702 item[i]._ivalue >>= 1;
703 }
704 }
705 }
706 }
707 }
708 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
709 }
710
711 /**
712 * Theme_Library initializes the library theme.
713 *
714 * @param t theme number (index into themes array).
715 */
Theme_Library(int t)716 void Theme_Library(int t)
717 {
718 int xp, yp, oi;
719 char librnd[4] = { 1, 2, 2, 5 };
720 char monstrnd[4] = { 5, 7, 3, 9 };
721
722 TFit_Shrine(t);
723
724 if (themeVar1 == 1) {
725 AddObject(OBJ_BOOKCANDLE, themex - 1, themey);
726 AddObject(OBJ_BOOKCASER, themex, themey);
727 AddObject(OBJ_BOOKCANDLE, themex + 1, themey);
728 } else {
729 AddObject(OBJ_BOOKCANDLE, themex, themey - 1);
730 AddObject(OBJ_BOOKCASEL, themex, themey);
731 AddObject(OBJ_BOOKCANDLE, themex, themey + 1);
732 }
733
734 for (yp = 1; yp < MAXDUNY - 1; yp++) {
735 for (xp = 1; xp < MAXDUNX - 1; xp++) {
736 if (CheckThemeObj3(xp, yp, t, -1) && dMonster[xp][yp] == 0 && random_(0, librnd[leveltype - 1]) == 0) {
737 AddObject(OBJ_BOOKSTAND, xp, yp);
738 if (random_(0, 2 * librnd[leveltype - 1]) != 0 && dObject[xp][yp]) { /// BUGFIX: check dObject[xp][yp] was populated by AddObject (fixed)
739 oi = dObject[xp][yp] - 1;
740 object[oi]._oSelFlag = 0;
741 object[oi]._oAnimFrame += 2;
742 }
743 }
744 }
745 }
746
747 if (QuestStatus(Q_ZHAR)) {
748 if (t == zharlib) {
749 return;
750 }
751 PlaceThemeMonsts(t, monstrnd[leveltype]); /// BUGFIX: `leveltype - 1`
752 } else {
753 PlaceThemeMonsts(t, monstrnd[leveltype]); /// BUGFIX: `leveltype - 1`
754 }
755 }
756
757 /**
758 * Theme_Torture initializes the torture theme.
759 *
760 * @param t theme number (index into themes array).
761 */
Theme_Torture(int t)762 void Theme_Torture(int t)
763 {
764 int xp, yp;
765 char tortrnd[4] = { 6, 8, 3, 8 };
766 char monstrnd[4] = { 6, 8, 3, 9 };
767
768 for (yp = 1; yp < MAXDUNY - 1; yp++) {
769 for (xp = 1; xp < MAXDUNX - 1; xp++) {
770 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
771 if (CheckThemeObj3(xp, yp, t, -1)) {
772 if (random_(0, tortrnd[leveltype - 1]) == 0) {
773 AddObject(OBJ_TNUDEM2, xp, yp);
774 }
775 }
776 }
777 }
778 }
779 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
780 }
781
782 /**
783 * Theme_BloodFountain initializes the blood fountain theme.
784 * @param t Theme number (index into themes array).
785 */
Theme_BloodFountain(int t)786 void Theme_BloodFountain(int t)
787 {
788 char monstrnd[4] = { 6, 8, 3, 9 };
789
790 TFit_Obj5(t);
791 AddObject(OBJ_BLOODFTN, themex, themey);
792 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
793 }
794
795 /**
796 * Theme_Decap initializes the decapitated theme.
797 *
798 * @param t theme number (index into themes array).
799 */
Theme_Decap(int t)800 void Theme_Decap(int t)
801 {
802 int xp, yp;
803 char decaprnd[4] = { 6, 8, 3, 8 };
804 char monstrnd[4] = { 6, 8, 3, 9 };
805
806 for (yp = 1; yp < MAXDUNY - 1; yp++) {
807 for (xp = 1; xp < MAXDUNX - 1; xp++) {
808 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
809 if (CheckThemeObj3(xp, yp, t, -1)) {
810 if (random_(0, decaprnd[leveltype - 1]) == 0) {
811 AddObject(OBJ_DECAP, xp, yp);
812 }
813 }
814 }
815 }
816 }
817 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
818 }
819
820 /**
821 * Theme_PurifyingFountain initializes the purifying fountain theme.
822 *
823 * @param t theme number (index into themes array).
824 */
Theme_PurifyingFountain(int t)825 void Theme_PurifyingFountain(int t)
826 {
827 char monstrnd[4] = { 6, 7, 3, 9 };
828
829 TFit_Obj5(t);
830 AddObject(OBJ_PURIFYINGFTN, themex, themey);
831 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
832 }
833
834 /**
835 * Theme_ArmorStand initializes the armor stand theme.
836 *
837 * @param t theme number (index into themes array).
838 */
Theme_ArmorStand(int t)839 void Theme_ArmorStand(int t)
840 {
841 int xp, yp;
842 char armorrnd[4] = { 6, 8, 3, 8 };
843 char monstrnd[4] = { 6, 7, 3, 9 };
844
845 if (armorFlag) {
846 TFit_Obj3(t);
847 AddObject(OBJ_ARMORSTAND, themex, themey);
848 }
849 for (yp = 0; yp < MAXDUNY; yp++) {
850 for (xp = 0; xp < MAXDUNX; xp++) {
851 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
852 if (CheckThemeObj3(xp, yp, t, -1)) {
853 if (random_(0, armorrnd[leveltype - 1]) == 0) {
854 AddObject(OBJ_ARMORSTANDN, xp, yp);
855 }
856 }
857 }
858 }
859 }
860 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
861 armorFlag = FALSE;
862 }
863
864 /**
865 * Theme_GoatShrine initializes the goat shrine theme.
866 *
867 * @param t theme number (index into themes array).
868 */
Theme_GoatShrine(int t)869 void Theme_GoatShrine(int t)
870 {
871 int xx, yy;
872
873 TFit_GoatShrine(t);
874 AddObject(OBJ_GOATSHRINE, themex, themey);
875 for (yy = themey - 1; yy <= themey + 1; yy++) {
876 for (xx = themex - 1; xx <= themex + 1; xx++) {
877 if (dTransVal[xx][yy] == themes[t].ttval && !nSolidTable[dPiece[xx][yy]] && (xx != themex || yy != themey)) {
878 AddMonster(xx, yy, DIR_SW, themeVar1, TRUE);
879 }
880 }
881 }
882 }
883
884 /**
885 * Theme_Cauldron initializes the cauldron theme.
886 *
887 * @param t theme number (index into themes array).
888 */
Theme_Cauldron(int t)889 void Theme_Cauldron(int t)
890 {
891 char monstrnd[4] = { 6, 7, 3, 9 };
892
893 TFit_Obj5(t);
894 AddObject(OBJ_CAULDRON, themex, themey);
895 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
896 }
897
898 /**
899 * Theme_MurkyFountain initializes the murky fountain theme.
900 *
901 * @param t theme number (index into themes array).
902 */
Theme_MurkyFountain(int t)903 void Theme_MurkyFountain(int t)
904 {
905 char monstrnd[4] = { 6, 7, 3, 9 };
906
907 TFit_Obj5(t);
908 AddObject(OBJ_MURKYFTN, themex, themey);
909 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
910 }
911
912 /**
913 * Theme_TearFountain initializes the tear fountain theme.
914 *
915 * @param t theme number (index into themes array).
916 */
Theme_TearFountain(int t)917 void Theme_TearFountain(int t)
918 {
919 char monstrnd[4] = { 6, 7, 3, 9 };
920
921 TFit_Obj5(t);
922 AddObject(OBJ_TEARFTN, themex, themey);
923 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
924 }
925
926 /**
927 * Theme_BrnCross initializes the burning cross theme.
928 *
929 * @param t theme number (index into themes array).
930 */
Theme_BrnCross(int t)931 void Theme_BrnCross(int t)
932 {
933 int xp, yp;
934 char monstrnd[4] = { 6, 8, 3, 9 };
935 char bcrossrnd[4] = { 5, 7, 3, 8 };
936
937 for (yp = 0; yp < MAXDUNY; yp++) {
938 for (xp = 0; xp < MAXDUNX; xp++) {
939 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
940 if (CheckThemeObj3(xp, yp, t, -1)) {
941 if (random_(0, bcrossrnd[leveltype - 1]) == 0) {
942 AddObject(OBJ_TBCROSS, xp, yp);
943 }
944 }
945 }
946 }
947 }
948 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
949 bCrossFlag = TRUE;
950 }
951
952 /**
953 * Theme_WeaponRack initializes the weapon rack theme.
954 *
955 * @param t theme number (index into themes array).
956 */
Theme_WeaponRack(int t)957 void Theme_WeaponRack(int t)
958 {
959 int xp, yp;
960 char weaponrnd[4] = { 6, 8, 5, 8 };
961 char monstrnd[4] = { 6, 7, 3, 9 };
962
963 if (weaponFlag) {
964 TFit_Obj3(t);
965 AddObject(OBJ_WEAPONRACK, themex, themey);
966 }
967 for (yp = 0; yp < MAXDUNY; yp++) {
968 for (xp = 0; xp < MAXDUNX; xp++) {
969 if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
970 if (CheckThemeObj3(xp, yp, t, -1)) {
971 if (random_(0, weaponrnd[leveltype - 1]) == 0) {
972 AddObject(OBJ_WEAPONRACKN, xp, yp);
973 }
974 }
975 }
976 }
977 }
978 PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
979 weaponFlag = FALSE;
980 }
981
982 /**
983 * UpdateL4Trans sets each value of the transparency map to 1.
984 */
UpdateL4Trans()985 void UpdateL4Trans()
986 {
987 int i, j;
988
989 for (j = 0; j < MAXDUNY; j++) {
990 for (i = 0; i < MAXDUNX; i++) {
991 if (dTransVal[i][j] != 0) {
992 dTransVal[i][j] = 1;
993 }
994 }
995 }
996 }
997
998 /**
999 * CreateThemeRooms adds thematic elements to rooms.
1000 */
CreateThemeRooms()1001 void CreateThemeRooms()
1002 {
1003 int i;
1004
1005 if (currlevel == 16) {
1006 return;
1007 }
1008 InitObjFlag = TRUE;
1009 for (i = 0; i < numthemes; i++) {
1010 themex = 0;
1011 themey = 0;
1012 switch (themes[i].ttype) {
1013 case THEME_BARREL:
1014 Theme_Barrel(i);
1015 break;
1016 case THEME_SHRINE:
1017 Theme_Shrine(i);
1018 break;
1019 case THEME_MONSTPIT:
1020 Theme_MonstPit(i);
1021 break;
1022 case THEME_SKELROOM:
1023 Theme_SkelRoom(i);
1024 break;
1025 case THEME_TREASURE:
1026 Theme_Treasure(i);
1027 break;
1028 case THEME_LIBRARY:
1029 Theme_Library(i);
1030 break;
1031 case THEME_TORTURE:
1032 Theme_Torture(i);
1033 break;
1034 case THEME_BLOODFOUNTAIN:
1035 Theme_BloodFountain(i);
1036 break;
1037 case THEME_DECAPITATED:
1038 Theme_Decap(i);
1039 break;
1040 case THEME_PURIFYINGFOUNTAIN:
1041 Theme_PurifyingFountain(i);
1042 break;
1043 case THEME_ARMORSTAND:
1044 Theme_ArmorStand(i);
1045 break;
1046 case THEME_GOATSHRINE:
1047 Theme_GoatShrine(i);
1048 break;
1049 case THEME_CAULDRON:
1050 Theme_Cauldron(i);
1051 break;
1052 case THEME_MURKYFOUNTAIN:
1053 Theme_MurkyFountain(i);
1054 break;
1055 case THEME_TEARFOUNTAIN:
1056 Theme_TearFountain(i);
1057 break;
1058 case THEME_BRNCROSS:
1059 Theme_BrnCross(i);
1060 break;
1061 case THEME_WEAPONRACK:
1062 Theme_WeaponRack(i);
1063 break;
1064 }
1065 }
1066 InitObjFlag = FALSE;
1067 if (leveltype == DTYPE_HELL && themeCount > 0) {
1068 UpdateL4Trans();
1069 }
1070 }
1071
1072 DEVILUTION_END_NAMESPACE
1073