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