/*-----------------------------------------------------------------------** ** Diablo ** ** Thematic rooms file ** ** (C)1995 Condor, Inc. All rights reserved. ** **-----------------------------------------------------------------------** ** $Header: /Diablo/THEMES.CPP 2 1/23/97 12:21p Jmorin $ **-----------------------------------------------------------------------** ** ** File Routines **-----------------------------------------------------------------------*/ #include "diablo.h" #pragma hdrstop #include "sound.h" #include "themes.h" #include "gendung.h" #include "engine.h" #include "objects.h" #include "objdat.h" #include "monster.h" #include "monstdat.h" #include "items.h" #include "quests.h" #include "trigs.h" /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ #define TOTAL_THEMES 17 #define THEME_NONE -1 #define THEME_BARREL 0 #define THEME_SHRINE 1 #define THEME_MONSTPIT 2 #define THEME_SKELRM 3 #define THEME_TREASURE 4 #define THEME_LIBRARY 5 #define THEME_TORTURE 6 #define THEME_BLOODFTN 7 #define THEME_DECAP 8 #define THEME_PURIFYINGFTN 9 #define THEME_ARMORSTAND 10 #define THEME_GOATSHRINE 11 #define THEME_CAULDRON 12 #define THEME_MURKYFTN 13 #define THEME_TEARFTN 14 #define THEME_BRNCROSS 15 #define THEME_WEAPONRACK 16 /*-----------------------------------------------------------------------** ** Global variables **-----------------------------------------------------------------------*/ ThemeStruct theme[MAXTHEMES]; int numthemes; int zharlib; int themex, themey; int themeVar1; //Theme flags BOOL armorFlag; BOOL bFountainFlag; BOOL cauldronFlag; BOOL mFountainFlag; BOOL pFountainFlag; BOOL tFountainFlag; BOOL treasureFlag; BOOL bCrossFlag; BOOL weaponFlag; /*-----------------------------------------------------------------------*/ // List of themes that it trys to fit first #define THEME_NUMGOOD 4 int ThemeGood[THEME_NUMGOOD] = { THEME_GOATSHRINE, THEME_SHRINE, THEME_SKELRM, THEME_LIBRARY, }; BOOL ThemeGoodIn[THEME_NUMGOOD]; /*-----------------------------------------------------------------------*/ // 5x5 area offsets 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 }; 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 }; // 3x3 area offsets int trm3x[] = { -1, 0, 1, -1, 0, 1, -1, 0, 1 }; int trm3y[] = { -1, -1, -1, 0, 0, 0, 1, 1, 1 }; /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ BOOL TFit_Shrine(int i) { int xp, yp, found; app_assert((DWORD)i < MAXTHEMES); xp = 0; yp = 0; found = 0; while (!found) { if (dTransVal[xp][yp] == theme[i].ttval) { if ((nTrapTable[dPiece[xp][yp-1]]) && (!nSolidTable[dPiece[xp-1][yp]]) && (!nSolidTable[dPiece[xp+1][yp]]) && (dTransVal[xp-1][yp] == theme[i].ttval) && (dTransVal[xp+1][yp] == theme[i].ttval) && (dObject[xp-1][yp-1] == 0) && (dObject[xp+1][yp-1] == 0)) found = 1; if (!found) { if ((nTrapTable[dPiece[xp-1][yp]]) && (!nSolidTable[dPiece[xp][yp-1]]) && (!nSolidTable[dPiece[xp][yp+1]]) && (dTransVal[xp][yp-1] == theme[i].ttval) && (dTransVal[xp][yp+1] == theme[i].ttval) && (dObject[xp-1][yp-1] == 0) && (dObject[xp-1][yp+1] == 0)) found = 2; } } if (!found) { xp++; if (xp == DMAXX) { xp = 0; yp++; if (yp == DMAXY) return(FALSE); } } } themex = xp; themey = yp; themeVar1 = found; return(TRUE); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ BOOL TFit_Obj5(int t) { int xp, yp; int i, r, rs; BOOL found; app_assert((DWORD)t < MAXTHEMES); xp = 0; yp = 0; r = random(0, 5) + 1; rs = r; while (r > 0) { found = FALSE; if ((dTransVal[xp][yp] == theme[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]] != theme[t].ttval) found = FALSE; } } if (!found) { xp++; if (xp == DMAXX) { xp = 0; yp++; if (yp == DMAXY) { if (r == rs) return(FALSE); else yp = 0; } } } else r--; } themex = xp; themey = yp; return(TRUE); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ BOOL TFit_SkelRoom(int t) { int i; if (leveltype == 1 || leveltype == 2) { for (i = 0; i < nummtypes; i++) { if (IsSkel(Monsters[i].mtype)) { //Initialize themeVar1 to monster type skeleton 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)) { //Initialize themeVar1 to monster type goat themeVar1 = i; return TFit_Obj5(t); } } return(FALSE); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ BOOL CheckThemeObj3(int xp, int yp, int t, int f) { app_assert((DWORD)t < MAXTHEMES); for (int 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]] != theme[t].ttval) return FALSE; if (dObject[xp+trm3x[i]][yp+trm3y[i]] != 0) 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 < DMAXY-1; yp++) { for (xp = 1; xp < DMAXX-1; xp++) { if (CheckThemeObj3(xp, yp, t, objrnd[leveltype-1])) { themex = xp; themey = yp; return TRUE; } } } return FALSE; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ BOOL CheckThemeReqs(int t) /*-----------------------------------------------------------------------** ** Description: Checks theme room requirements, such as themes for specific ** levels or themes which can only be used once per level. ** Input: None ** Return: TRUE = All requirements met **-----------------------------------------------------------------------*/ { BOOL rv = TRUE; switch(t) { case THEME_SHRINE: case THEME_LIBRARY: if (leveltype == 3 || leveltype == 4) rv = FALSE; break; case THEME_SKELRM: if (leveltype == 3 || leveltype == 4) rv = FALSE; break; case THEME_BLOODFTN: if (!bFountainFlag) rv = FALSE; break; case THEME_PURIFYINGFTN: if (!pFountainFlag) rv = FALSE; break; case THEME_MURKYFTN: if (!mFountainFlag) rv = FALSE; break; case THEME_TEARFTN: if (!tFountainFlag) rv = FALSE; break; case THEME_ARMORSTAND: case THEME_WEAPONRACK: if (leveltype == 1) rv = FALSE; break; case THEME_CAULDRON: if ((leveltype != 4) || (!cauldronFlag)) rv = FALSE; break; } return rv; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ BOOL SpecialThemeFit(int i, int t) { BOOL rv = TRUE; rv = CheckThemeReqs(t); switch(t) { case THEME_SHRINE: case THEME_LIBRARY: if (rv) rv = TFit_Shrine(i); break; case THEME_SKELRM: if (rv) rv = TFit_SkelRoom(i); break; case THEME_BLOODFTN: if (rv) rv = TFit_Obj5(i); //This is set so we only get one blood fountain per level if (rv) bFountainFlag = FALSE; break; case THEME_PURIFYINGFTN: if (rv) rv = TFit_Obj5(i); //This is set so we only get one purifying fountain per level if (rv) pFountainFlag = FALSE; break; case THEME_MURKYFTN: if (rv) rv = TFit_Obj5(i); //This is set so we only get one murky fountain per level if (rv) mFountainFlag = FALSE; break; case THEME_TEARFTN: if (rv) rv = TFit_Obj5(i); //This is set so we only get one tear fountain per level if (rv) tFountainFlag = FALSE; break; case THEME_CAULDRON: if (rv) rv = TFit_Obj5(i); //This is set so we only get one cauldron room per level if (rv) cauldronFlag = FALSE; break; case THEME_GOATSHRINE: if (rv) rv = TFit_GoatShrine(i); break; case THEME_DECAP: case THEME_TORTURE: case THEME_ARMORSTAND: case THEME_BRNCROSS: case THEME_WEAPONRACK: if (rv) rv = TFit_Obj3(i); break; case THEME_TREASURE: rv = treasureFlag; //This is set so we only get one treasure room per level if (rv) treasureFlag = FALSE; break; } return(rv); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ BOOL CheckThemeRoom(int tv) { int i, j; int tarea; for (i = 0; i < numtrigs; i++) { if (dTransVal[trigs[i]._tx][trigs[i]._ty] == tv) return(FALSE); } // Check area requirements tarea = 0; for (j = 0; j < DMAXY; j++) { for (i = 0; i < DMAXX; i++) { if (dTransVal[i][j] == tv) { if (dFlags[i][j] & BFLAG_SETPC) return(FALSE); tarea++; } } } if (leveltype == 1) { if ((tarea < 9) || (tarea > 100)) return(FALSE); } // Make sure it is a solid room for (j = 0; j < DMAXY; j++) { for (i = 0; i < DMAXX; i++) { if ((dTransVal[i][j] == tv) && (!nSolidTable[dPiece[i][j]])) { 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); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ static int ThemeRoomExitCount(int tv) { int i, j; int exits; exits = 0; for (j = 1; j < DMAXY - 1; j++) { for (i = 1; i < DMAXX - 1; i++) { if ((dTransVal[i][j] == tv) && (!nSolidTable[dPiece[i][j]])) { if ((dTransVal[i - 1][j] != tv) && (!nSolidTable[dPiece[i - 1][j]])) exits++; if ((dTransVal[i + 1][j] != tv) && (!nSolidTable[dPiece[i + 1][j]])) exits++; if ((dTransVal[i][j - 1] != tv) && (!nSolidTable[dPiece[i][j - 1]])) exits++; if ((dTransVal[i][j + 1] != tv) && (!nSolidTable[dPiece[i][j + 1]])) exits++; } } } return exits; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ static BOOL IsDeadEndThemeRoom(int tv) { return ThemeRoomExitCount(tv) <= 6; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ static int ThemeRoomArea(int tv) { int i, j; int area; area = 0; for (j = 0; j < DMAXY; j++) { for (i = 0; i < DMAXX; i++) { if ((dTransVal[i][j] == tv) && (!nSolidTable[dPiece[i][j]])) area++; } } return area; } #define CATROOM_DEADEND 0x01 #define CATROOM_SMALL 0x02 #define CATROOM_LARGE 0x04 typedef struct { int minLevel; int maxLevel; int roomFlags; const int *themeList; int themeListLen; } CathedralRoomPick; /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ static BOOL CathedralRoomShapeFits(int tv, int roomFlags) { int area; if ((roomFlags & CATROOM_DEADEND) && !IsDeadEndThemeRoom(tv)) return FALSE; area = ThemeRoomArea(tv); if ((roomFlags & CATROOM_SMALL) && area > 36) return FALSE; if ((roomFlags & CATROOM_LARGE) && area < 49) return FALSE; return TRUE; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ static BOOL PickThemeFromList(int i, const int *themeList, int themeListLen, int *themeType) { int start, n, t; if (themeListLen <= 0) return FALSE; start = random(0, themeListLen); for (n = 0; n < themeListLen; n++) { t = themeList[(start + n) % themeListLen]; if (SpecialThemeFit(i, t)) { *themeType = t; return TRUE; } } return FALSE; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ static BOOL PickCathedralArchetypeTheme(int i, int depth, int *themeType) { static const int candlelitEntryChapel[] = { THEME_SHRINE, THEME_LIBRARY, }; static const int collapsedPrayerNook[] = { THEME_BARREL, THEME_SHRINE, THEME_SKELRM, }; static const int skeletonAmbushCryptlet[] = { THEME_SKELRM, THEME_MONSTPIT, }; static const int desecratedShrineRoom[] = { THEME_GOATSHRINE, THEME_BLOODFTN, THEME_MURKYFTN, }; static const int abandonedStudy[] = { THEME_LIBRARY, THEME_SHRINE, }; static const int guardedSupplyRoom[] = { THEME_BARREL, THEME_TREASURE, THEME_MONSTPIT, }; static const int processionHall[] = { THEME_SHRINE, THEME_LIBRARY, THEME_BARREL, }; static const int sealedTreasureCloset[] = { THEME_TREASURE, }; static const int bloodTrailRoom[] = { THEME_BLOODFTN, THEME_DECAP, THEME_TORTURE, }; static const int falseSanctuary[] = { THEME_SHRINE, THEME_GOATSHRINE, THEME_MONSTPIT, }; static const CathedralRoomPick rooms[] = { { 1, 2, CATROOM_LARGE, candlelitEntryChapel, sizeof(candlelitEntryChapel) / sizeof(candlelitEntryChapel[0]) }, { 1, 3, CATROOM_DEADEND | CATROOM_SMALL, collapsedPrayerNook, sizeof(collapsedPrayerNook) / sizeof(collapsedPrayerNook[0]) }, { 1, 4, 0, skeletonAmbushCryptlet, sizeof(skeletonAmbushCryptlet) / sizeof(skeletonAmbushCryptlet[0]) }, { 2, 4, CATROOM_LARGE, desecratedShrineRoom, sizeof(desecratedShrineRoom) / sizeof(desecratedShrineRoom[0]) }, { 1, 3, 0, abandonedStudy, sizeof(abandonedStudy) / sizeof(abandonedStudy[0]) }, { 1, 2, CATROOM_DEADEND | CATROOM_SMALL, guardedSupplyRoom, sizeof(guardedSupplyRoom) / sizeof(guardedSupplyRoom[0]) }, { 1, 4, CATROOM_LARGE, processionHall, sizeof(processionHall) / sizeof(processionHall[0]) }, { 1, 4, CATROOM_DEADEND | CATROOM_SMALL, sealedTreasureCloset, sizeof(sealedTreasureCloset) / sizeof(sealedTreasureCloset[0]) }, { 3, 4, 0, bloodTrailRoom, sizeof(bloodTrailRoom) / sizeof(bloodTrailRoom[0]) }, { 2, 4, 0, falseSanctuary, sizeof(falseSanctuary) / sizeof(falseSanctuary[0]) }, }; int start, n, roomCount; const CathedralRoomPick *room; roomCount = sizeof(rooms) / sizeof(rooms[0]); start = random(0, roomCount); for (n = 0; n < roomCount; n++) { room = &rooms[(start + n) % roomCount]; if (depth < room->minLevel || depth > room->maxLevel) continue; if (!CathedralRoomShapeFits(theme[i].ttval, room->roomFlags)) continue; if (PickThemeFromList(i, room->themeList, room->themeListLen, themeType)) return TRUE; } return FALSE; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ static int PickL1Theme(int i) { int depth; int tries; int t; depth = currlevel; if (depth < 1) depth = 1; if (depth > 4) depth = 4; if (PickCathedralArchetypeTheme(i, depth, &t)) return t; for (tries = 0; tries < 64; tries++) { t = random(0, TOTAL_THEMES); if (SpecialThemeFit(i, t)) return t; } return THEME_BARREL; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void InitThemes() { int i, t; numthemes = 0; zharlib = -1; //Initialize theme flags armorFlag = TRUE; bFountainFlag = TRUE; cauldronFlag = TRUE; mFountainFlag = TRUE; pFountainFlag = TRUE; tFountainFlag = TRUE; treasureFlag = TRUE; bCrossFlag = FALSE; weaponFlag = TRUE; if (currlevel == 16) return; //Find L1 theme room types if (leveltype == 1) { for (i = 0; i < THEME_NUMGOOD; i++) ThemeGoodIn[i] = FALSE; for (i = 0; (i < 256) && (numthemes < MAXTHEMES); i++) { if (CheckThemeRoom(i)) { theme[numthemes].ttval = i; t = PickL1Theme(numthemes); theme[numthemes].ttype = t; numthemes++; } } } //Find L2, L3, L4 theme room types if (leveltype == 2 || leveltype == 3 || leveltype == 4) { //Initialize theme room types app_assert((DWORD)themeCount < MAXTHEMES); for (i = 0; i < themeCount; i++) { theme[i].ttype = THEME_NONE; } //Special Quest if (QuestStatus(Q_ZHAR)) { for (i = 0; i < themeCount; i++) { theme[i].ttval = themeLoc[i].ttval; if (SpecialThemeFit(i, THEME_LIBRARY)) { theme[i].ttype = THEME_LIBRARY; zharlib = i; break; } } } //Add themeLoc array to general theme array for (i = 0; i < themeCount; i++) { if (theme[i].ttype == THEME_NONE) { theme[i].ttval = themeLoc[i].ttval; t = ThemeGood[random(0, THEME_NUMGOOD)]; while (!SpecialThemeFit(i, t)) t = random(0, TOTAL_THEMES); theme[i].ttype = t; } } //Add L2 theme count to general theme count numthemes += themeCount; } } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void HoldThemeRooms() { int i, x, y; char tv; if (currlevel == 16) return; app_assert((DWORD)numthemes < MAXTHEMES); if (leveltype == 1) { for (i = 0; i < numthemes; i++) { tv = theme[i].ttval; for (y = 0; y < DMAXY; y++) { for (x = 0; x < DMAXX; x++) { if (dTransVal[x][y] == tv) dFlags[x][y] |= BFLAG_SETPC; } } } } else { //Hold L2,L3,L4 DRLG_HoldThemeRooms(); } } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void PlaceThemeMonsts(int t, int f) { int xp, yp, mtype; int scattertypes[LASTMT]; int numscattypes = 0; int i; app_assert((DWORD)t < MAXTHEMES); for (i = 0; i < nummtypes; i++) { if (Monsters[i].mPlaceFlags & MPFLAG_SCATTER) scattertypes[numscattypes++] = i; } mtype = scattertypes[random(0, numscattypes)]; for (yp = 0; yp < DMAXY; yp++) { for (xp = 0; xp < DMAXX; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]]) && (dItem[xp][yp] == 0) && (dObject[xp][yp] == 0)) { if (random(0, f) == 0) AddMonster(xp, yp, random(0, 8), mtype, TRUE); } } } } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void PlaceSpecificThemeMonsts(int t, int f, int mtype) { int xp, yp; app_assert((DWORD)t < MAXTHEMES); for (yp = 0; yp < DMAXY; yp++) { for (xp = 0; xp < DMAXX; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]]) && (dItem[xp][yp] == 0) && (dObject[xp][yp] == 0)) { if (random(0, f) == 0) AddMonster(xp, yp, random(0, 8), mtype, TRUE); } } } } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_Barrel(int t) { int xp, yp, r; char barrnd[4] = { 2, 6, 4, 8 }; char monstrnd[4] = { 5, 7, 3, 9 }; app_assert((DWORD)t < MAXTHEMES); for (yp = 0; yp < DMAXY; yp++) { for (xp = 0; xp < DMAXX; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]])) { if (!random(0, barrnd[leveltype-1])) { if (!random(0, barrnd[leveltype-1])) r = OBJ_BARREL; else r = OBJ_BARRELEX; AddObject(r, xp, yp); } } } } PlaceThemeMonsts(t, monstrnd[leveltype-1]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ 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]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_MonstPit(int t) { int r, ixp, iyp; char monstrnd[4] = { 6, 7, 3, 9 }; app_assert((DWORD)t < MAXTHEMES); r = random(0, 100)+1; ixp = 0; iyp = 0; while (r > 0) { if ((dTransVal[ixp][iyp] == theme[t].ttval) && (!nSolidTable[dPiece[ixp][iyp]])) r--; if (r > 0) { ixp++; if (ixp == DMAXX) { ixp = 0; iyp++; if (iyp == DMAXY) iyp = 0; } } } CreateRndItem(ixp, iyp, TRUE, FALSE, TRUE); // Create a good item ItemNoFlippy(); PlaceThemeMonsts(t, monstrnd[leveltype-1]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_SkelRoom(int t) { int mt; int xp, yp; int ii; char monstrnd[4] = { 6, 7, 3, 9 }; TFit_SkelRoom(t); mt = themeVar1; xp = themex; yp = themey; AddObject(OBJ_SKFIRE, xp, yp); if (random(0, monstrnd[leveltype-1])) { ii = PreSpawnSkeleton(); SpawnSkeleton(ii, xp-1, yp-1); } else AddObject(OBJ_BANNERL, xp-1, yp-1); ii = PreSpawnSkeleton(); SpawnSkeleton(ii, xp, yp-1); if (random(0, monstrnd[leveltype-1])) { ii = PreSpawnSkeleton(); SpawnSkeleton(ii, xp+1, yp-1); } else AddObject(OBJ_BANNERR, xp+1, yp-1); if (random(0, monstrnd[leveltype-1])) { ii = PreSpawnSkeleton(); SpawnSkeleton(ii, xp-1, yp); } else AddObject(OBJ_BANNERM, xp-1, yp); if (random(0, monstrnd[leveltype-1])) { ii = PreSpawnSkeleton(); SpawnSkeleton(ii, xp+1, yp); } else AddObject(OBJ_BANNERM, xp+1, yp); if (random(0, monstrnd[leveltype-1])) { ii = PreSpawnSkeleton(); SpawnSkeleton(ii, xp-1, yp+1); } else AddObject(OBJ_BANNERR, xp-1, yp+1); ii = PreSpawnSkeleton(); SpawnSkeleton(ii, xp, yp+1); if (random(0, monstrnd[leveltype-1])) { ii = PreSpawnSkeleton(); SpawnSkeleton(ii, 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); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_Treasure(int t) { int xp, yp, i; char treasrnd[4] = { 4, 9, 7, 10 }; char monstrnd[4] = { 6, 8, 3, 7 }; app_assert((DWORD)t < MAXTHEMES); int rs = GetRndSeed(); for (yp = 0; yp < DMAXY; yp++) { for (xp = 0; xp < DMAXX; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]])) { int rv = random(0, treasrnd[leveltype-1]); if (!(random(0, treasrnd[leveltype-1])*2)) { CreateTypeItem(xp, yp, FALSE, IT_GOLD, 0, 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 != 1) item[i]._ivalue = item[i]._ivalue >> 1; } } } } PlaceThemeMonsts(t, monstrnd[leveltype-1]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ 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 < DMAXY-1; yp++) { for (xp = 1; xp < DMAXX-1; xp++) { if (CheckThemeObj3(xp, yp, t, -1) && (dMonster[xp][yp] == 0) && // Zhar the mad fix (!random(0, librnd[leveltype-1]))) { AddObject(OBJ_BOOKSTAND, xp, yp); // Pre used? if (random(0, (librnd[leveltype-1] << 1))) { oi = dObject[xp][yp]-1; object[oi]._oSelFlag = OSEL_NONE; object[oi]._oAnimFrame += 2; } } } } //special quest if (QuestStatus(Q_ZHAR)) { if (t != zharlib) PlaceThemeMonsts(t, monstrnd[leveltype]); } else PlaceThemeMonsts(t, monstrnd[leveltype]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_Torture(int t) { int xp, yp; char tortrnd[4] = { 6, 8, 3, 8 }; char monstrnd[4] = { 6, 8, 3, 9 }; app_assert((DWORD)t < MAXTHEMES); //Add tortured body for (yp = 1; yp < DMAXY-1; yp++) { for (xp = 1; xp < DMAXX-1; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]])) { if (CheckThemeObj3(xp, yp, t, -1) && (!random(0, tortrnd[leveltype-1]))) AddObject(OBJ_TNUDEM2, xp, yp); } } } PlaceThemeMonsts(t, monstrnd[leveltype-1]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ 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]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_Decap(int t) { int xp, yp; char decaprnd[4] = { 6, 8, 3, 8 }; char monstrnd[4] = { 6, 8, 3, 9 }; app_assert((DWORD)t < MAXTHEMES); //Add decapitated body for (yp = 1; yp < DMAXY-1; yp++) { for (xp = 1; xp < DMAXX-1; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]])) { if (CheckThemeObj3(xp, yp, t, -1) && (!random(0, decaprnd[leveltype-1]))) AddObject(OBJ_DECAP, xp, yp); } } } PlaceThemeMonsts(t, monstrnd[leveltype-1]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ 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]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_ArmorStand(int t) { int xp, yp; char armorrnd[4] = { 6, 8, 3, 8 }; char monstrnd[4] = { 6, 7, 3, 9 }; app_assert((DWORD)t < MAXTHEMES); //Add functional stand w/ armor if(armorFlag) { TFit_Obj3(t); AddObject(OBJ_ARMORSTAND, themex, themey); } //Add stand only for (yp = 0; yp < DMAXY; yp++) { for (xp = 0; xp < DMAXX; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]])) { if (CheckThemeObj3(xp, yp, t, -1) && (!random(0, armorrnd[leveltype-1]))) AddObject(OBJ_ARMORSTANDN, xp, yp); } } } PlaceThemeMonsts(t, monstrnd[leveltype-1]); //This is set so we only get one functional armor stand per level armorFlag = FALSE; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_GoatShrine(int t) { int xx, yy; app_assert((DWORD)t < MAXTHEMES); TFit_GoatShrine(t); AddObject(OBJ_GOATSHRINE, themex, themey); //Add goat monsters around shrine for (yy = themey-1; yy <= themey+1; yy++) { for (xx = themex-1; xx <= themex+1; xx++) { if ((dTransVal[xx][yy] == theme[t].ttval) && (!nSolidTable[dPiece[xx][yy]])) { //Skip center monster since shrine is in the center if (xx == themex && yy == themey) continue; else AddMonster(xx, yy, 1, themeVar1, TRUE); } } } } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ 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]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ 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]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ 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]); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_BrnCross(int t) { int xp, yp; char monstrnd[4] = { 6, 8, 3, 9 }; char bcrossrnd[4] = { 5, 7, 3, 8 }; app_assert((DWORD)t < MAXTHEMES); for (yp = 0; yp < DMAXY; yp++) { for (xp = 0; xp < DMAXX; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]])) { if (CheckThemeObj3(xp, yp, t, -1) && (!random(0, bcrossrnd[leveltype-1]))) AddObject(OBJ_TBCROSS, xp, yp); } } } PlaceThemeMonsts(t, monstrnd[leveltype-1]); //Set so we know a burning cross theme room has been created on the level. bCrossFlag = TRUE; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void Theme_WeaponRack(int t) { int xp, yp; char weaponrnd[4] = { 6, 8, 5, 8 }; char monstrnd[4] = { 6, 7, 3, 9 }; app_assert((DWORD)t < MAXTHEMES); //Add functional rack w/ weapon if(weaponFlag) { TFit_Obj3(t); AddObject(OBJ_WEAPONRACK, themex, themey); } //Add rack only for (yp = 0; yp < DMAXY; yp++) { for (xp = 0; xp < DMAXX; xp++) { if ((dTransVal[xp][yp] == theme[t].ttval) && (!nSolidTable[dPiece[xp][yp]])) { if (CheckThemeObj3(xp, yp, t, -1) && (!random(0, weaponrnd[leveltype-1]))) AddObject(OBJ_WEAPONRACKN, xp, yp); } } } PlaceThemeMonsts(t, monstrnd[leveltype-1]); //This is set so we only get one functional weapon rack per level weaponFlag = FALSE; } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void UpdateL4Trans() { int i, j; for (j = 0; j < DMAXY; j++) { for (i = 0; i < DMAXX; i++) { if (dTransVal[i][j] != 0) dTransVal[i][j] = 1; } } } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/ void CreateThemeRooms() { int i; if (currlevel == 16) return; InitObjFlag = TRUE; app_assert((DWORD)numthemes < MAXTHEMES); for (i = 0; i < numthemes; i++) { themex = 0; themey = 0; switch(theme[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_SKELRM: 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_BLOODFTN: Theme_BloodFountain(i); break; case THEME_DECAP: Theme_Decap(i); break; case THEME_PURIFYINGFTN: 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_TEARFTN: Theme_TearFountain(i); break; case THEME_MURKYFTN: Theme_MurkyFountain(i); break; case THEME_BRNCROSS: Theme_BrnCross(i); break; case THEME_WEAPONRACK: Theme_WeaponRack(i); break; } } InitObjFlag = FALSE; //Update L4 trans values to get rid of blinking if (leveltype == 4 && themeCount > 0) UpdateL4Trans(); } /*-----------------------------------------------------------------------** **-----------------------------------------------------------------------*/