Missiles _mimfnum is being used for a lot more then just directions and can also be a 16 degree direction so can't apply enum verbs and restricitons to it in most cases. This in turns also affects SetMissDir and some other functions that have to stay int.
1081 lines
23 KiB
C++
1081 lines
23 KiB
C++
/**
|
|
* @file themes.cpp
|
|
*
|
|
* Implementation of the theme room placing algorithms.
|
|
*/
|
|
#include "themes.h"
|
|
|
|
#include "items.h"
|
|
#include "monster.h"
|
|
#include "objects.h"
|
|
#include "quests.h"
|
|
#include "trigs.h"
|
|
|
|
namespace devilution {
|
|
|
|
int numthemes;
|
|
bool armorFlag;
|
|
bool ThemeGoodIn[4];
|
|
bool weaponFlag;
|
|
bool treasureFlag;
|
|
bool mFountainFlag;
|
|
bool cauldronFlag;
|
|
bool tFountainFlag;
|
|
int zharlib;
|
|
int themex;
|
|
int themey;
|
|
int themeVar1;
|
|
ThemeStruct themes[MAXTHEMES];
|
|
bool pFountainFlag;
|
|
bool bFountainFlag;
|
|
bool bCrossFlag;
|
|
|
|
/** Specifies the set of special theme IDs from which one will be selected at random. */
|
|
theme_id ThemeGood[4] = { THEME_GOATSHRINE, THEME_SHRINE, THEME_SKELROOM, THEME_LIBRARY };
|
|
/** Specifies a 5x5 area to fit theme objects. */
|
|
int trm5x[] = {
|
|
-2, -1, 0, 1, 2,
|
|
-2, -1, 0, 1, 2,
|
|
-2, -1, 0, 1, 2,
|
|
-2, -1, 0, 1, 2,
|
|
-2, -1, 0, 1, 2
|
|
};
|
|
/** Specifies a 5x5 area to fit theme objects. */
|
|
int trm5y[] = {
|
|
-2, -2, -2, -2, -2,
|
|
-1, -1, -1, -1, -1,
|
|
0, 0, 0, 0, 0,
|
|
1, 1, 1, 1, 1,
|
|
2, 2, 2, 2, 2
|
|
};
|
|
/** Specifies a 3x3 area to fit theme objects. */
|
|
int trm3x[] = {
|
|
-1, 0, 1,
|
|
-1, 0, 1,
|
|
-1, 0, 1
|
|
};
|
|
/** Specifies a 3x3 area to fit theme objects. */
|
|
int trm3y[] = {
|
|
-1, -1, -1,
|
|
0, 0, 0,
|
|
1, 1, 1
|
|
};
|
|
|
|
bool TFit_Shrine(int i)
|
|
{
|
|
int xp, yp, found;
|
|
|
|
xp = 0;
|
|
yp = 0;
|
|
found = 0;
|
|
while (found == 0) {
|
|
if (dTransVal[xp][yp] == themes[i].ttval) {
|
|
if (nTrapTable[dPiece[xp][yp - 1]]
|
|
&& !nSolidTable[dPiece[xp - 1][yp]]
|
|
&& !nSolidTable[dPiece[xp + 1][yp]]
|
|
&& dTransVal[xp - 1][yp] == themes[i].ttval
|
|
&& dTransVal[xp + 1][yp] == themes[i].ttval
|
|
&& dObject[xp - 1][yp - 1] == 0
|
|
&& dObject[xp + 1][yp - 1] == 0) {
|
|
found = 1;
|
|
}
|
|
if (found == 0
|
|
&& nTrapTable[dPiece[xp - 1][yp]]
|
|
&& !nSolidTable[dPiece[xp][yp - 1]]
|
|
&& !nSolidTable[dPiece[xp][yp + 1]]
|
|
&& dTransVal[xp][yp - 1] == themes[i].ttval
|
|
&& dTransVal[xp][yp + 1] == themes[i].ttval
|
|
&& dObject[xp - 1][yp - 1] == 0
|
|
&& dObject[xp - 1][yp + 1] == 0) {
|
|
found = 2;
|
|
}
|
|
}
|
|
if (found == 0) {
|
|
xp++;
|
|
if (xp == MAXDUNX) {
|
|
xp = 0;
|
|
yp++;
|
|
if (yp == MAXDUNY)
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
themex = xp;
|
|
themey = yp;
|
|
themeVar1 = found;
|
|
return true;
|
|
}
|
|
|
|
bool TFit_Obj5(int t)
|
|
{
|
|
int xp, yp;
|
|
int i, r, rs;
|
|
bool found;
|
|
|
|
xp = 0;
|
|
yp = 0;
|
|
r = random_(0, 5) + 1;
|
|
rs = r;
|
|
while (r > 0) {
|
|
found = false;
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
found = true;
|
|
for (i = 0; found && i < 25; i++) {
|
|
if (nSolidTable[dPiece[xp + trm5x[i]][yp + trm5y[i]]]) {
|
|
found = false;
|
|
}
|
|
if (dTransVal[xp + trm5x[i]][yp + trm5y[i]] != themes[t].ttval) {
|
|
found = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
xp++;
|
|
if (xp == MAXDUNX) {
|
|
xp = 0;
|
|
yp++;
|
|
if (yp == MAXDUNY) {
|
|
if (r == rs) {
|
|
return false;
|
|
}
|
|
yp = 0;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
r--;
|
|
}
|
|
|
|
themex = xp;
|
|
themey = yp;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TFit_SkelRoom(int t)
|
|
{
|
|
int i;
|
|
|
|
if (leveltype != DTYPE_CATHEDRAL && leveltype != DTYPE_CATACOMBS) {
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < nummtypes; i++) {
|
|
if (IsSkel(Monsters[i].mtype)) {
|
|
themeVar1 = i;
|
|
return TFit_Obj5(t);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TFit_GoatShrine(int t)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nummtypes; i++) {
|
|
if (IsGoat(Monsters[i].mtype)) {
|
|
themeVar1 = i;
|
|
return TFit_Obj5(t);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CheckThemeObj3(int xp, int yp, int t, int f)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 9; i++) {
|
|
if (xp + trm3x[i] < 0 || yp + trm3y[i] < 0)
|
|
return false;
|
|
if (nSolidTable[dPiece[xp + trm3x[i]][yp + trm3y[i]]])
|
|
return false;
|
|
if (dTransVal[xp + trm3x[i]][yp + trm3y[i]] != themes[t].ttval)
|
|
return false;
|
|
if (dObject[xp + trm3x[i]][yp + trm3y[i]])
|
|
return false;
|
|
if (f != -1 && random_(0, f) == 0)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TFit_Obj3(int t)
|
|
{
|
|
int xp, yp;
|
|
char objrnd[4] = { 4, 4, 3, 5 };
|
|
|
|
for (yp = 1; yp < MAXDUNY - 1; yp++) {
|
|
for (xp = 1; xp < MAXDUNX - 1; xp++) {
|
|
if (CheckThemeObj3(xp, yp, t, objrnd[leveltype - 1])) {
|
|
themex = xp;
|
|
themey = yp;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CheckThemeReqs(theme_id t)
|
|
{
|
|
bool rv;
|
|
|
|
rv = true;
|
|
switch (t) {
|
|
case THEME_SHRINE:
|
|
case THEME_SKELROOM:
|
|
case THEME_LIBRARY:
|
|
if (leveltype == DTYPE_CAVES || leveltype == DTYPE_HELL) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
case THEME_BLOODFOUNTAIN:
|
|
if (!bFountainFlag) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
case THEME_PURIFYINGFOUNTAIN:
|
|
if (!pFountainFlag) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
case THEME_ARMORSTAND:
|
|
if (leveltype == DTYPE_CATHEDRAL) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
case THEME_CAULDRON:
|
|
if (leveltype != DTYPE_HELL || !cauldronFlag) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
case THEME_MURKYFOUNTAIN:
|
|
if (!mFountainFlag) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
case THEME_TEARFOUNTAIN:
|
|
if (!tFountainFlag) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
case THEME_WEAPONRACK:
|
|
if (leveltype == DTYPE_CATHEDRAL) {
|
|
rv = false;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static bool SpecialThemeFit(int i, theme_id t)
|
|
{
|
|
bool rv;
|
|
|
|
rv = CheckThemeReqs(t);
|
|
switch (t) {
|
|
case THEME_SHRINE:
|
|
case THEME_LIBRARY:
|
|
if (rv) {
|
|
rv = TFit_Shrine(i);
|
|
}
|
|
break;
|
|
case THEME_SKELROOM:
|
|
if (rv) {
|
|
rv = TFit_SkelRoom(i);
|
|
}
|
|
break;
|
|
case THEME_BLOODFOUNTAIN:
|
|
if (rv) {
|
|
rv = TFit_Obj5(i);
|
|
}
|
|
if (rv) {
|
|
bFountainFlag = false;
|
|
}
|
|
break;
|
|
case THEME_PURIFYINGFOUNTAIN:
|
|
if (rv) {
|
|
rv = TFit_Obj5(i);
|
|
}
|
|
if (rv) {
|
|
pFountainFlag = false;
|
|
}
|
|
break;
|
|
case THEME_MURKYFOUNTAIN:
|
|
if (rv) {
|
|
rv = TFit_Obj5(i);
|
|
}
|
|
if (rv) {
|
|
mFountainFlag = false;
|
|
}
|
|
break;
|
|
case THEME_TEARFOUNTAIN:
|
|
if (rv) {
|
|
rv = TFit_Obj5(i);
|
|
}
|
|
if (rv) {
|
|
tFountainFlag = false;
|
|
}
|
|
break;
|
|
case THEME_CAULDRON:
|
|
if (rv) {
|
|
rv = TFit_Obj5(i);
|
|
}
|
|
if (rv) {
|
|
cauldronFlag = false;
|
|
}
|
|
break;
|
|
case THEME_GOATSHRINE:
|
|
if (rv) {
|
|
rv = TFit_GoatShrine(i);
|
|
}
|
|
break;
|
|
case THEME_TORTURE:
|
|
case THEME_DECAPITATED:
|
|
case THEME_ARMORSTAND:
|
|
case THEME_BRNCROSS:
|
|
case THEME_WEAPONRACK:
|
|
if (rv) {
|
|
rv = TFit_Obj3(i);
|
|
}
|
|
break;
|
|
case THEME_TREASURE:
|
|
rv = treasureFlag;
|
|
if (rv) {
|
|
treasureFlag = false;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
bool CheckThemeRoom(int tv)
|
|
{
|
|
int i, j, tarea;
|
|
|
|
for (i = 0; i < numtrigs; i++) {
|
|
if (dTransVal[trigs[i]._tx][trigs[i]._ty] == tv)
|
|
return false;
|
|
}
|
|
|
|
tarea = 0;
|
|
for (j = 0; j < MAXDUNY; j++) {
|
|
for (i = 0; i < MAXDUNX; i++) {
|
|
if (dTransVal[i][j] != tv)
|
|
continue;
|
|
if (dFlags[i][j] & BFLAG_POPULATED)
|
|
return false;
|
|
|
|
tarea++;
|
|
}
|
|
}
|
|
|
|
if (leveltype == DTYPE_CATHEDRAL && (tarea < 9 || tarea > 100))
|
|
return false;
|
|
|
|
for (j = 0; j < MAXDUNY; j++) {
|
|
for (i = 0; i < MAXDUNX; i++) {
|
|
if (dTransVal[i][j] != tv || nSolidTable[dPiece[i][j]])
|
|
continue;
|
|
if (dTransVal[i - 1][j] != tv && !nSolidTable[dPiece[i - 1][j]])
|
|
return false;
|
|
if (dTransVal[i + 1][j] != tv && !nSolidTable[dPiece[i + 1][j]])
|
|
return false;
|
|
if (dTransVal[i][j - 1] != tv && !nSolidTable[dPiece[i][j - 1]])
|
|
return false;
|
|
if (dTransVal[i][j + 1] != tv && !nSolidTable[dPiece[i][j + 1]])
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void InitThemes()
|
|
{
|
|
int i;
|
|
|
|
zharlib = -1;
|
|
numthemes = 0;
|
|
armorFlag = true;
|
|
bFountainFlag = true;
|
|
cauldronFlag = true;
|
|
mFountainFlag = true;
|
|
pFountainFlag = true;
|
|
tFountainFlag = true;
|
|
treasureFlag = true;
|
|
bCrossFlag = false;
|
|
weaponFlag = true;
|
|
|
|
if (currlevel == 16)
|
|
return;
|
|
|
|
if (leveltype == DTYPE_CATHEDRAL) {
|
|
for (i = 0; i < sizeof(ThemeGoodIn) / sizeof(ThemeGoodIn[0]); i++)
|
|
ThemeGoodIn[i] = false;
|
|
|
|
for (i = 0; i < 256 && numthemes < MAXTHEMES; i++) {
|
|
if (CheckThemeRoom(i)) {
|
|
themes[numthemes].ttval = i;
|
|
theme_id j = ThemeGood[random_(0, 4)];
|
|
while (!SpecialThemeFit(numthemes, j)) {
|
|
j = (theme_id)random_(0, 17);
|
|
}
|
|
themes[numthemes].ttype = j;
|
|
numthemes++;
|
|
}
|
|
}
|
|
}
|
|
if (leveltype == DTYPE_CATACOMBS || leveltype == DTYPE_CAVES || leveltype == DTYPE_HELL) {
|
|
for (i = 0; i < themeCount; i++)
|
|
themes[i].ttype = THEME_NONE;
|
|
if (QuestStatus(Q_ZHAR)) {
|
|
for (int j = 0; j < themeCount; j++) {
|
|
themes[j].ttval = themeLoc[j].ttval;
|
|
if (SpecialThemeFit(j, THEME_LIBRARY)) {
|
|
themes[j].ttype = THEME_LIBRARY;
|
|
zharlib = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < themeCount; i++) {
|
|
if (themes[i].ttype == THEME_NONE) {
|
|
themes[i].ttval = themeLoc[i].ttval;
|
|
theme_id j = ThemeGood[random_(0, 4)];
|
|
while (!SpecialThemeFit(i, j)) {
|
|
j = (theme_id)random_(0, 17);
|
|
}
|
|
themes[i].ttype = j;
|
|
}
|
|
}
|
|
numthemes += themeCount;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief HoldThemeRooms marks theme rooms as populated.
|
|
*/
|
|
void HoldThemeRooms()
|
|
{
|
|
int i, x, y;
|
|
char v;
|
|
|
|
if (currlevel != 16) {
|
|
if (leveltype == DTYPE_CATHEDRAL) {
|
|
for (i = 0; i < numthemes; i++) {
|
|
v = themes[i].ttval;
|
|
for (y = 0; y < MAXDUNY; y++) {
|
|
for (x = 0; x < MAXDUNX; x++) {
|
|
if (dTransVal[x][y] == v) {
|
|
dFlags[x][y] |= BFLAG_POPULATED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
DRLG_HoldThemeRooms();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* PlaceThemeMonsts places theme monsters with the specified frequency.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
* @param f frequency (1/f likelihood of adding monster).
|
|
*/
|
|
void PlaceThemeMonsts(int t, int f)
|
|
{
|
|
int xp, yp;
|
|
int scattertypes[138];
|
|
int numscattypes, mtype, i;
|
|
|
|
numscattypes = 0;
|
|
for (i = 0; i < nummtypes; i++) {
|
|
if (Monsters[i].mPlaceFlags & PLACE_SCATTER) {
|
|
scattertypes[numscattypes] = i;
|
|
numscattypes++;
|
|
}
|
|
}
|
|
mtype = scattertypes[random_(0, numscattypes)];
|
|
for (yp = 0; yp < MAXDUNY; yp++) {
|
|
for (xp = 0; xp < MAXDUNX; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]] && dItem[xp][yp] == 0 && dObject[xp][yp] == 0) {
|
|
if (random_(0, f) == 0) {
|
|
AddMonster(xp, yp, (direction)random_(0, 8), mtype, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Theme_Barrel initializes the barrel theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_Barrel(int t)
|
|
{
|
|
int xp, yp;
|
|
char barrnd[4] = { 2, 6, 4, 8 };
|
|
char monstrnd[4] = { 5, 7, 3, 9 };
|
|
|
|
for (yp = 0; yp < MAXDUNY; yp++) {
|
|
for (xp = 0; xp < MAXDUNX; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
if (random_(0, barrnd[leveltype - 1]) == 0) {
|
|
_object_id r = OBJ_BARREL;
|
|
if (random_(0, barrnd[leveltype - 1]) != 0) {
|
|
r = OBJ_BARRELEX;
|
|
}
|
|
AddObject(r, xp, yp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_Shrine initializes the shrine theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_Shrine(int t)
|
|
{
|
|
char monstrnd[4] = { 6, 6, 3, 9 };
|
|
|
|
TFit_Shrine(t);
|
|
if (themeVar1 == 1) {
|
|
AddObject(OBJ_CANDLE2, themex - 1, themey);
|
|
AddObject(OBJ_SHRINER, themex, themey);
|
|
AddObject(OBJ_CANDLE2, themex + 1, themey);
|
|
} else {
|
|
AddObject(OBJ_CANDLE2, themex, themey - 1);
|
|
AddObject(OBJ_SHRINEL, themex, themey);
|
|
AddObject(OBJ_CANDLE2, themex, themey + 1);
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_MonstPit initializes the monster pit theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_MonstPit(int t)
|
|
{
|
|
int r;
|
|
int ixp, iyp;
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
r = random_(0, 100) + 1;
|
|
ixp = 0;
|
|
iyp = 0;
|
|
while (r > 0) {
|
|
if (dTransVal[ixp][iyp] == themes[t].ttval && !nSolidTable[dPiece[ixp][iyp]]) {
|
|
--r;
|
|
}
|
|
if (r <= 0)
|
|
continue;
|
|
ixp++;
|
|
if (ixp == MAXDUNX) {
|
|
ixp = 0;
|
|
iyp++;
|
|
if (iyp == MAXDUNY) {
|
|
iyp = 0;
|
|
}
|
|
}
|
|
}
|
|
CreateRndItem(ixp, iyp, true, false, true);
|
|
ItemNoFlippy();
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_SkelRoom initializes the skeleton room theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_SkelRoom(int t)
|
|
{
|
|
int xp, yp, i;
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
TFit_SkelRoom(t);
|
|
|
|
xp = themex;
|
|
yp = themey;
|
|
|
|
AddObject(OBJ_SKFIRE, xp, yp);
|
|
|
|
if (random_(0, monstrnd[leveltype - 1]) != 0) {
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp - 1, yp - 1);
|
|
} else {
|
|
AddObject(OBJ_BANNERL, xp - 1, yp - 1);
|
|
}
|
|
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp, yp - 1);
|
|
|
|
if (random_(0, monstrnd[leveltype - 1]) != 0) {
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp + 1, yp - 1);
|
|
} else {
|
|
AddObject(OBJ_BANNERR, xp + 1, yp - 1);
|
|
}
|
|
if (random_(0, monstrnd[leveltype - 1]) != 0) {
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp - 1, yp);
|
|
} else {
|
|
AddObject(OBJ_BANNERM, xp - 1, yp);
|
|
}
|
|
if (random_(0, monstrnd[leveltype - 1]) != 0) {
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp + 1, yp);
|
|
} else {
|
|
AddObject(OBJ_BANNERM, xp + 1, yp);
|
|
}
|
|
if (random_(0, monstrnd[leveltype - 1]) != 0) {
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp - 1, yp + 1);
|
|
} else {
|
|
AddObject(OBJ_BANNERR, xp - 1, yp + 1);
|
|
}
|
|
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp, yp + 1);
|
|
|
|
if (random_(0, monstrnd[leveltype - 1]) != 0) {
|
|
i = PreSpawnSkeleton();
|
|
SpawnSkeleton(i, xp + 1, yp + 1);
|
|
} else {
|
|
AddObject(OBJ_BANNERL, xp + 1, yp + 1);
|
|
}
|
|
|
|
if (dObject[xp][yp - 3] == 0) {
|
|
AddObject(OBJ_SKELBOOK, xp, yp - 2);
|
|
}
|
|
if (dObject[xp][yp + 3] == 0) {
|
|
AddObject(OBJ_SKELBOOK, xp, yp + 2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Theme_Treasure initializes the treasure theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_Treasure(int t)
|
|
{
|
|
int xp, yp;
|
|
int i;
|
|
char treasrnd[4] = { 4, 9, 7, 10 };
|
|
char monstrnd[4] = { 6, 8, 3, 7 };
|
|
|
|
AdvanceRndSeed();
|
|
for (yp = 0; yp < MAXDUNY; yp++) {
|
|
for (xp = 0; xp < MAXDUNX; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
int rv = random_(0, treasrnd[leveltype - 1]);
|
|
// BUGFIX: the `2*` in `2*random_(0, treasrnd...) == 0` has no effect, should probably be `random_(0, 2*treasrnd...) == 0`
|
|
if ((2 * random_(0, treasrnd[leveltype - 1])) == 0) {
|
|
CreateTypeItem(xp, yp, false, ITYPE_GOLD, IMISC_NONE, false, true);
|
|
ItemNoFlippy();
|
|
}
|
|
if (rv == 0) {
|
|
CreateRndItem(xp, yp, false, false, true);
|
|
ItemNoFlippy();
|
|
}
|
|
if (rv == 0 || rv >= treasrnd[leveltype - 1] - 2) {
|
|
i = ItemNoFlippy();
|
|
if (rv >= treasrnd[leveltype - 1] - 2 && leveltype != DTYPE_CATHEDRAL) {
|
|
items[i]._ivalue >>= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_Library initializes the library theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_Library(int t)
|
|
{
|
|
int xp, yp, oi;
|
|
char librnd[4] = { 1, 2, 2, 5 };
|
|
char monstrnd[4] = { 5, 7, 3, 9 };
|
|
|
|
TFit_Shrine(t);
|
|
|
|
if (themeVar1 == 1) {
|
|
AddObject(OBJ_BOOKCANDLE, themex - 1, themey);
|
|
AddObject(OBJ_BOOKCASER, themex, themey);
|
|
AddObject(OBJ_BOOKCANDLE, themex + 1, themey);
|
|
} else {
|
|
AddObject(OBJ_BOOKCANDLE, themex, themey - 1);
|
|
AddObject(OBJ_BOOKCASEL, themex, themey);
|
|
AddObject(OBJ_BOOKCANDLE, themex, themey + 1);
|
|
}
|
|
|
|
for (yp = 1; yp < MAXDUNY - 1; yp++) {
|
|
for (xp = 1; xp < MAXDUNX - 1; xp++) {
|
|
if (CheckThemeObj3(xp, yp, t, -1) && dMonster[xp][yp] == 0 && random_(0, librnd[leveltype - 1]) == 0) {
|
|
AddObject(OBJ_BOOKSTAND, xp, yp);
|
|
if (random_(0, 2 * librnd[leveltype - 1]) != 0 && dObject[xp][yp]) { /// BUGFIX: check dObject[xp][yp] was populated by AddObject (fixed)
|
|
oi = dObject[xp][yp] - 1;
|
|
object[oi]._oSelFlag = 0;
|
|
object[oi]._oAnimFrame += 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (QuestStatus(Q_ZHAR)) {
|
|
if (t == zharlib) {
|
|
return;
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype]); /// BUGFIX: `leveltype - 1`
|
|
} else {
|
|
PlaceThemeMonsts(t, monstrnd[leveltype]); /// BUGFIX: `leveltype - 1`
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Theme_Torture initializes the torture theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_Torture(int t)
|
|
{
|
|
int xp, yp;
|
|
char tortrnd[4] = { 6, 8, 3, 8 };
|
|
char monstrnd[4] = { 6, 8, 3, 9 };
|
|
|
|
for (yp = 1; yp < MAXDUNY - 1; yp++) {
|
|
for (xp = 1; xp < MAXDUNX - 1; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
if (CheckThemeObj3(xp, yp, t, -1)) {
|
|
if (random_(0, tortrnd[leveltype - 1]) == 0) {
|
|
AddObject(OBJ_TNUDEM2, xp, yp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_BloodFountain initializes the blood fountain theme.
|
|
* @param t Theme number (index into themes array).
|
|
*/
|
|
void Theme_BloodFountain(int t)
|
|
{
|
|
char monstrnd[4] = { 6, 8, 3, 9 };
|
|
|
|
TFit_Obj5(t);
|
|
AddObject(OBJ_BLOODFTN, themex, themey);
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_Decap initializes the decapitated theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_Decap(int t)
|
|
{
|
|
int xp, yp;
|
|
char decaprnd[4] = { 6, 8, 3, 8 };
|
|
char monstrnd[4] = { 6, 8, 3, 9 };
|
|
|
|
for (yp = 1; yp < MAXDUNY - 1; yp++) {
|
|
for (xp = 1; xp < MAXDUNX - 1; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
if (CheckThemeObj3(xp, yp, t, -1)) {
|
|
if (random_(0, decaprnd[leveltype - 1]) == 0) {
|
|
AddObject(OBJ_DECAP, xp, yp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_PurifyingFountain initializes the purifying fountain theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_PurifyingFountain(int t)
|
|
{
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
TFit_Obj5(t);
|
|
AddObject(OBJ_PURIFYINGFTN, themex, themey);
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_ArmorStand initializes the armor stand theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_ArmorStand(int t)
|
|
{
|
|
int xp, yp;
|
|
char armorrnd[4] = { 6, 8, 3, 8 };
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
if (armorFlag) {
|
|
TFit_Obj3(t);
|
|
AddObject(OBJ_ARMORSTAND, themex, themey);
|
|
}
|
|
for (yp = 0; yp < MAXDUNY; yp++) {
|
|
for (xp = 0; xp < MAXDUNX; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
if (CheckThemeObj3(xp, yp, t, -1)) {
|
|
if (random_(0, armorrnd[leveltype - 1]) == 0) {
|
|
AddObject(OBJ_ARMORSTANDN, xp, yp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
armorFlag = false;
|
|
}
|
|
|
|
/**
|
|
* Theme_GoatShrine initializes the goat shrine theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_GoatShrine(int t)
|
|
{
|
|
int xx, yy;
|
|
|
|
TFit_GoatShrine(t);
|
|
AddObject(OBJ_GOATSHRINE, themex, themey);
|
|
for (yy = themey - 1; yy <= themey + 1; yy++) {
|
|
for (xx = themex - 1; xx <= themex + 1; xx++) {
|
|
if (dTransVal[xx][yy] == themes[t].ttval && !nSolidTable[dPiece[xx][yy]] && (xx != themex || yy != themey)) {
|
|
AddMonster(xx, yy, DIR_SW, themeVar1, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Theme_Cauldron initializes the cauldron theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_Cauldron(int t)
|
|
{
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
TFit_Obj5(t);
|
|
AddObject(OBJ_CAULDRON, themex, themey);
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_MurkyFountain initializes the murky fountain theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_MurkyFountain(int t)
|
|
{
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
TFit_Obj5(t);
|
|
AddObject(OBJ_MURKYFTN, themex, themey);
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_TearFountain initializes the tear fountain theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_TearFountain(int t)
|
|
{
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
TFit_Obj5(t);
|
|
AddObject(OBJ_TEARFTN, themex, themey);
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
}
|
|
|
|
/**
|
|
* Theme_BrnCross initializes the burning cross theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_BrnCross(int t)
|
|
{
|
|
int xp, yp;
|
|
char monstrnd[4] = { 6, 8, 3, 9 };
|
|
char bcrossrnd[4] = { 5, 7, 3, 8 };
|
|
|
|
for (yp = 0; yp < MAXDUNY; yp++) {
|
|
for (xp = 0; xp < MAXDUNX; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
if (CheckThemeObj3(xp, yp, t, -1)) {
|
|
if (random_(0, bcrossrnd[leveltype - 1]) == 0) {
|
|
AddObject(OBJ_TBCROSS, xp, yp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
bCrossFlag = true;
|
|
}
|
|
|
|
/**
|
|
* Theme_WeaponRack initializes the weapon rack theme.
|
|
*
|
|
* @param t theme number (index into themes array).
|
|
*/
|
|
void Theme_WeaponRack(int t)
|
|
{
|
|
int xp, yp;
|
|
char weaponrnd[4] = { 6, 8, 5, 8 };
|
|
char monstrnd[4] = { 6, 7, 3, 9 };
|
|
|
|
if (weaponFlag) {
|
|
TFit_Obj3(t);
|
|
AddObject(OBJ_WEAPONRACK, themex, themey);
|
|
}
|
|
for (yp = 0; yp < MAXDUNY; yp++) {
|
|
for (xp = 0; xp < MAXDUNX; xp++) {
|
|
if (dTransVal[xp][yp] == themes[t].ttval && !nSolidTable[dPiece[xp][yp]]) {
|
|
if (CheckThemeObj3(xp, yp, t, -1)) {
|
|
if (random_(0, weaponrnd[leveltype - 1]) == 0) {
|
|
AddObject(OBJ_WEAPONRACKN, xp, yp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
PlaceThemeMonsts(t, monstrnd[leveltype - 1]);
|
|
weaponFlag = false;
|
|
}
|
|
|
|
/**
|
|
* UpdateL4Trans sets each value of the transparency map to 1.
|
|
*/
|
|
void UpdateL4Trans()
|
|
{
|
|
int i, j;
|
|
|
|
for (j = 0; j < MAXDUNY; j++) {
|
|
for (i = 0; i < MAXDUNX; i++) {
|
|
if (dTransVal[i][j] != 0) {
|
|
dTransVal[i][j] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CreateThemeRooms adds thematic elements to rooms.
|
|
*/
|
|
void CreateThemeRooms()
|
|
{
|
|
int i;
|
|
|
|
if (currlevel == 16) {
|
|
return;
|
|
}
|
|
InitObjFlag = true;
|
|
for (i = 0; i < numthemes; i++) {
|
|
themex = 0;
|
|
themey = 0;
|
|
switch (themes[i].ttype) {
|
|
case THEME_BARREL:
|
|
Theme_Barrel(i);
|
|
break;
|
|
case THEME_SHRINE:
|
|
Theme_Shrine(i);
|
|
break;
|
|
case THEME_MONSTPIT:
|
|
Theme_MonstPit(i);
|
|
break;
|
|
case THEME_SKELROOM:
|
|
Theme_SkelRoom(i);
|
|
break;
|
|
case THEME_TREASURE:
|
|
Theme_Treasure(i);
|
|
break;
|
|
case THEME_LIBRARY:
|
|
Theme_Library(i);
|
|
break;
|
|
case THEME_TORTURE:
|
|
Theme_Torture(i);
|
|
break;
|
|
case THEME_BLOODFOUNTAIN:
|
|
Theme_BloodFountain(i);
|
|
break;
|
|
case THEME_DECAPITATED:
|
|
Theme_Decap(i);
|
|
break;
|
|
case THEME_PURIFYINGFOUNTAIN:
|
|
Theme_PurifyingFountain(i);
|
|
break;
|
|
case THEME_ARMORSTAND:
|
|
Theme_ArmorStand(i);
|
|
break;
|
|
case THEME_GOATSHRINE:
|
|
Theme_GoatShrine(i);
|
|
break;
|
|
case THEME_CAULDRON:
|
|
Theme_Cauldron(i);
|
|
break;
|
|
case THEME_MURKYFOUNTAIN:
|
|
Theme_MurkyFountain(i);
|
|
break;
|
|
case THEME_TEARFOUNTAIN:
|
|
Theme_TearFountain(i);
|
|
break;
|
|
case THEME_BRNCROSS:
|
|
Theme_BrnCross(i);
|
|
break;
|
|
case THEME_WEAPONRACK:
|
|
Theme_WeaponRack(i);
|
|
break;
|
|
case THEME_NONE:
|
|
app_fatal("Unknown theme type: %d", themes[i].ttype);
|
|
}
|
|
}
|
|
InitObjFlag = false;
|
|
if (leveltype == DTYPE_HELL && themeCount > 0) {
|
|
UpdateL4Trans();
|
|
}
|
|
}
|
|
|
|
} // namespace devilution
|