You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
867 lines
25 KiB
867 lines
25 KiB
/** |
|
* @file quests.cpp |
|
* |
|
* Implementation of functionality for handling quests. |
|
*/ |
|
|
|
#include "control.h" |
|
#include "cursor.h" |
|
#include "gendung.h" |
|
#include "init.h" |
|
#include "minitext.h" |
|
#include "missiles.h" |
|
#include "options.h" |
|
#include "stores.h" |
|
#include "towners.h" |
|
#include "trigs.h" |
|
#include "utils/language.h" |
|
|
|
namespace devilution { |
|
|
|
int qtopline; |
|
bool questlog; |
|
BYTE *pQLogCel; |
|
/** Contains the quests of the current game. */ |
|
QuestStruct quests[MAXQUESTS]; |
|
int qline; |
|
int qlist[MAXQUESTS]; |
|
int numqlines; |
|
int WaterDone; |
|
int ReturnLvlX; |
|
int ReturnLvlY; |
|
dungeon_type ReturnLvlT; |
|
int ReturnLvl; |
|
|
|
/** Contains the data related to each quest_id. */ |
|
QuestData questlist[] = { |
|
// clang-format off |
|
// _qdlvl, _qdmultlvl, _qlvlt, _qdtype, _qdrnd, _qslvl, isSinglePlayerOnly, _qdmsg, _qlstr |
|
{ 5, -1, DTYPE_NONE, Q_ROCK, 100, SL_NONE, true, TEXT_INFRA5, N_("The Magic Rock") }, |
|
{ 9, -1, DTYPE_NONE, Q_MUSHROOM, 100, SL_NONE, true, TEXT_MUSH8, N_("Black Mushroom") }, |
|
{ 4, -1, DTYPE_NONE, Q_GARBUD, 100, SL_NONE, true, TEXT_GARBUD1, N_("Gharbad The Weak") }, |
|
{ 8, -1, DTYPE_NONE, Q_ZHAR, 100, SL_NONE, true, TEXT_ZHAR1, N_("Zhar the Mad") }, |
|
{ 14, -1, DTYPE_NONE, Q_VEIL, 100, SL_NONE, true, TEXT_VEIL9, N_("Lachdanan") }, |
|
{ 15, -1, DTYPE_NONE, Q_DIABLO, 100, SL_NONE, false, TEXT_VILE3, N_("Diablo") }, |
|
{ 2, 2, DTYPE_NONE, Q_BUTCHER, 100, SL_NONE, false, TEXT_BUTCH9, N_("The Butcher") }, |
|
{ 4, -1, DTYPE_NONE, Q_LTBANNER, 100, SL_NONE, true, TEXT_BANNER2, N_("Ogden's Sign") }, |
|
{ 7, -1, DTYPE_NONE, Q_BLIND, 100, SL_NONE, true, TEXT_BLINDING, N_("Halls of the Blind") }, |
|
{ 5, -1, DTYPE_NONE, Q_BLOOD, 100, SL_NONE, true, TEXT_BLOODY, N_("Valor") }, |
|
{ 10, -1, DTYPE_NONE, Q_ANVIL, 100, SL_NONE, true, TEXT_ANVIL5, N_("Anvil of Fury") }, |
|
{ 13, -1, DTYPE_NONE, Q_WARLORD, 100, SL_NONE, true, TEXT_BLOODWAR, N_("Warlord of Blood") }, |
|
{ 3, 3, DTYPE_CATHEDRAL, Q_SKELKING, 100, SL_SKELKING, false, TEXT_KING2, N_("The Curse of King Leoric") }, |
|
{ 2, -1, DTYPE_CAVES, Q_PWATER, 100, SL_POISONWATER, true, TEXT_POISON3, N_("Poisoned Water Supply") }, |
|
{ 6, -1, DTYPE_CATACOMBS, Q_SCHAMB, 100, SL_BONECHAMB, true, TEXT_BONER, N_("The Chamber of Bone") }, |
|
{ 15, 15, DTYPE_CATHEDRAL, Q_BETRAYER, 100, SL_VILEBETRAYER, false, TEXT_VILE1, N_("Archbishop Lazarus") }, |
|
{ 17, 17, DTYPE_NONE, Q_GRAVE, 100, SL_NONE, false, TEXT_GRAVE7, N_("Grave Matters") }, |
|
{ 9, 9, DTYPE_NONE, Q_FARMER, 100, SL_NONE, false, TEXT_FARMER1, N_("Farmer's Orchard") }, |
|
{ 17, -1, DTYPE_NONE, Q_GIRL, 100, SL_NONE, true, TEXT_GIRL2, N_("Little Girl") }, |
|
{ 19, -1, DTYPE_NONE, Q_TRADER, 100, SL_NONE, true, TEXT_TRADER, N_("Wandering Trader") }, |
|
{ 17, 17, DTYPE_NONE, Q_DEFILER, 100, SL_NONE, false, TEXT_DEFILER1, N_("The Defiler") }, |
|
{ 21, 21, DTYPE_NONE, Q_NAKRUL, 100, SL_NONE, false, TEXT_NAKRUL1, N_("Na-Krul") }, |
|
{ 21, -1, DTYPE_NONE, Q_CORNSTN, 100, SL_NONE, true, TEXT_CORNSTN, N_("Cornerstone of the World") }, |
|
{ 9, 9, DTYPE_NONE, Q_JERSEY, 100, SL_NONE, false, TEXT_JERSEY4, N_("The Jersey's Jersey") }, |
|
// clang-format on |
|
}; |
|
/** |
|
* Specifies a delta in X-coordinates from the quest entrance for |
|
* which the hover text of the cursor will be visible. |
|
*/ |
|
char questxoff[7] = { 0, -1, 0, -1, -2, -1, -2 }; |
|
/** |
|
* Specifies a delta in Y-coordinates from the quest entrance for |
|
* which the hover text of the cursor will be visible. |
|
*/ |
|
char questyoff[7] = { 0, 0, -1, -1, -1, -2, -2 }; |
|
const char *const questtrigstr[5] = { |
|
N_("King Leoric's Tomb"), |
|
N_("The Chamber of Bone"), |
|
N_("Maze"), |
|
N_("A Dark Passage"), |
|
N_("Unholy Altar") |
|
}; |
|
/** |
|
* A quest group containing the three quests the Butcher, |
|
* Ogden's Sign and Gharbad the Weak, which ensures that exactly |
|
* two of these three quests appear in any single player game. |
|
*/ |
|
int QuestGroup1[3] = { Q_BUTCHER, Q_LTBANNER, Q_GARBUD }; |
|
/** |
|
* A quest group containing the three quests Halls of the Blind, |
|
* the Magic Rock and Valor, which ensures that exactly two of |
|
* these three quests appear in any single player game. |
|
*/ |
|
int QuestGroup2[3] = { Q_BLIND, Q_ROCK, Q_BLOOD }; |
|
/** |
|
* A quest group containing the three quests Black Mushroom, |
|
* Zhar the Mad and Anvil of Fury, which ensures that exactly |
|
* two of these three quests appear in any single player game. |
|
*/ |
|
int QuestGroup3[3] = { Q_MUSHROOM, Q_ZHAR, Q_ANVIL }; |
|
/** |
|
* A quest group containing the two quests Lachdanan and Warlord |
|
* of Blood, which ensures that exactly one of these two quests |
|
* appears in any single player game. |
|
*/ |
|
int QuestGroup4[2] = { Q_VEIL, Q_WARLORD }; |
|
|
|
void InitQuests() |
|
{ |
|
int i, initiatedQuests; |
|
DWORD z; |
|
|
|
if (!gbIsMultiplayer) { |
|
for (i = 0; i < MAXQUESTS; i++) { |
|
quests[i]._qactive = QUEST_NOTAVAIL; |
|
} |
|
} else { |
|
for (i = 0; i < MAXQUESTS; i++) { |
|
if (questlist[i].isSinglePlayerOnly) { |
|
quests[i]._qactive = QUEST_NOTAVAIL; |
|
} |
|
} |
|
} |
|
|
|
Qtalklist[TOWN_HEALER][Q_MUSHROOM] = TEXT_NONE; |
|
Qtalklist[TOWN_WITCH][Q_MUSHROOM] = TEXT_MUSH9; |
|
|
|
questlog = false; |
|
WaterDone = 0; |
|
initiatedQuests = 0; |
|
|
|
for (z = 0; z < MAXQUESTS; z++) { |
|
if (gbIsMultiplayer && questlist[z].isSinglePlayerOnly) |
|
continue; |
|
quests[z]._qtype = questlist[z]._qdtype; |
|
if (gbIsMultiplayer) { |
|
quests[z]._qlevel = questlist[z]._qdmultlvl; |
|
if (!delta_quest_inited(initiatedQuests)) { |
|
quests[z]._qactive = QUEST_INIT; |
|
quests[z]._qvar1 = 0; |
|
quests[z]._qlog = false; |
|
} |
|
initiatedQuests++; |
|
} else { |
|
quests[z]._qactive = QUEST_INIT; |
|
quests[z]._qlevel = questlist[z]._qdlvl; |
|
quests[z]._qvar1 = 0; |
|
quests[z]._qlog = false; |
|
} |
|
|
|
quests[z]._qslvl = questlist[z]._qslvl; |
|
quests[z].position = { 0, 0 }; |
|
quests[z]._qidx = z; |
|
quests[z]._qlvltype = questlist[z]._qlvlt; |
|
quests[z]._qvar2 = 0; |
|
quests[z]._qmsg = questlist[z]._qdmsg; |
|
} |
|
|
|
if (!gbIsMultiplayer && sgOptions.Gameplay.bRandomizeQuests) { |
|
SetRndSeed(glSeedTbl[15]); |
|
if (GenerateRnd(2) != 0) |
|
quests[Q_PWATER]._qactive = QUEST_NOTAVAIL; |
|
else |
|
quests[Q_SKELKING]._qactive = QUEST_NOTAVAIL; |
|
|
|
quests[QuestGroup1[GenerateRnd(sizeof(QuestGroup1) / sizeof(int))]]._qactive = QUEST_NOTAVAIL; |
|
quests[QuestGroup2[GenerateRnd(sizeof(QuestGroup2) / sizeof(int))]]._qactive = QUEST_NOTAVAIL; |
|
quests[QuestGroup3[GenerateRnd(sizeof(QuestGroup3) / sizeof(int))]]._qactive = QUEST_NOTAVAIL; |
|
quests[QuestGroup4[GenerateRnd(sizeof(QuestGroup4) / sizeof(int))]]._qactive = QUEST_NOTAVAIL; |
|
} |
|
#ifdef _DEBUG |
|
if (questdebug != -1) |
|
quests[questdebug]._qactive = QUEST_ACTIVE; |
|
#endif |
|
|
|
if (gbIsSpawn) { |
|
for (z = 0; z < MAXQUESTS; z++) { |
|
quests[z]._qactive = QUEST_NOTAVAIL; |
|
} |
|
} |
|
|
|
if (quests[Q_SKELKING]._qactive == QUEST_NOTAVAIL) |
|
quests[Q_SKELKING]._qvar2 = 2; |
|
if (quests[Q_ROCK]._qactive == QUEST_NOTAVAIL) |
|
quests[Q_ROCK]._qvar2 = 2; |
|
quests[Q_LTBANNER]._qvar1 = 1; |
|
if (gbIsMultiplayer) |
|
quests[Q_BETRAYER]._qvar1 = 2; |
|
} |
|
|
|
void CheckQuests() |
|
{ |
|
if (gbIsSpawn) |
|
return; |
|
|
|
int i, rportx, rporty; |
|
|
|
if (QuestStatus(Q_BETRAYER) && gbIsMultiplayer && quests[Q_BETRAYER]._qvar1 == 2) { |
|
AddObject(OBJ_ALTBOY, 2 * setpc_x + 20, 2 * setpc_y + 22); |
|
quests[Q_BETRAYER]._qvar1 = 3; |
|
NetSendCmdQuest(true, Q_BETRAYER); |
|
} |
|
|
|
if (gbIsMultiplayer) { |
|
return; |
|
} |
|
|
|
if (currlevel == quests[Q_BETRAYER]._qlevel |
|
&& !setlevel |
|
&& quests[Q_BETRAYER]._qvar1 >= 2 |
|
&& (quests[Q_BETRAYER]._qactive == QUEST_ACTIVE || quests[Q_BETRAYER]._qactive == QUEST_DONE) |
|
&& (quests[Q_BETRAYER]._qvar2 == 0 || quests[Q_BETRAYER]._qvar2 == 2)) { |
|
quests[Q_BETRAYER].position.x = 2 * quests[Q_BETRAYER].position.x + 16; |
|
quests[Q_BETRAYER].position.y = 2 * quests[Q_BETRAYER].position.y + 16; |
|
rportx = quests[Q_BETRAYER].position.x; |
|
rporty = quests[Q_BETRAYER].position.y; |
|
AddMissile(rportx, rporty, rportx, rporty, 0, MIS_RPORTAL, TARGET_MONSTERS, myplr, 0, 0); |
|
quests[Q_BETRAYER]._qvar2 = 1; |
|
if (quests[Q_BETRAYER]._qactive == QUEST_ACTIVE) { |
|
quests[Q_BETRAYER]._qvar1 = 3; |
|
} |
|
} |
|
|
|
if (quests[Q_BETRAYER]._qactive == QUEST_DONE |
|
&& setlevel |
|
&& setlvlnum == SL_VILEBETRAYER |
|
&& quests[Q_BETRAYER]._qvar2 == 4) { |
|
rportx = 35; |
|
rporty = 32; |
|
AddMissile(rportx, rporty, rportx, rporty, 0, MIS_RPORTAL, TARGET_MONSTERS, myplr, 0, 0); |
|
quests[Q_BETRAYER]._qvar2 = 3; |
|
} |
|
|
|
if (setlevel) { |
|
if (setlvlnum == quests[Q_PWATER]._qslvl |
|
&& quests[Q_PWATER]._qactive != QUEST_INIT |
|
&& leveltype == quests[Q_PWATER]._qlvltype |
|
&& nummonsters == 4 |
|
&& quests[Q_PWATER]._qactive != QUEST_DONE) { |
|
quests[Q_PWATER]._qactive = QUEST_DONE; |
|
PlaySfxLoc(IS_QUESTDN, plr[myplr].position.tile.x, plr[myplr].position.tile.y); |
|
LoadPalette("Levels\\L3Data\\L3pwater.pal"); |
|
WaterDone = 32; |
|
} |
|
if (WaterDone > 0) { |
|
palette_update_quest_palette(WaterDone); |
|
WaterDone--; |
|
} |
|
} else if (plr[myplr]._pmode == PM_STAND) { |
|
for (i = 0; i < MAXQUESTS; i++) { |
|
if (currlevel == quests[i]._qlevel |
|
&& quests[i]._qslvl != 0 |
|
&& quests[i]._qactive != QUEST_NOTAVAIL |
|
&& plr[myplr].position.tile.x == quests[i].position.x |
|
&& plr[myplr].position.tile.y == quests[i].position.y) { |
|
if (quests[i]._qlvltype != DTYPE_NONE) { |
|
setlvltype = quests[i]._qlvltype; |
|
} |
|
StartNewLvl(myplr, WM_DIABSETLVL, quests[i]._qslvl); |
|
} |
|
} |
|
} |
|
} |
|
|
|
bool ForceQuests() |
|
{ |
|
int i, j, qx, qy, ql; |
|
|
|
if (gbIsSpawn) |
|
return false; |
|
|
|
if (gbIsMultiplayer) { |
|
return false; |
|
} |
|
|
|
for (i = 0; i < MAXQUESTS; i++) { |
|
|
|
if (i != Q_BETRAYER && currlevel == quests[i]._qlevel && quests[i]._qslvl != 0) { |
|
ql = quests[quests[i]._qidx]._qslvl - 1; |
|
qx = quests[i].position.x; |
|
qy = quests[i].position.y; |
|
|
|
for (j = 0; j < 7; j++) { |
|
if (qx + questxoff[j] == cursmx && qy + questyoff[j] == cursmy) { |
|
sprintf(infostr, _("To %s"), questtrigstr[ql]); |
|
cursmx = qx; |
|
cursmy = qy; |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool QuestStatus(int i) |
|
{ |
|
if (setlevel) |
|
return false; |
|
if (currlevel != quests[i]._qlevel) |
|
return false; |
|
if (quests[i]._qactive == QUEST_NOTAVAIL) |
|
return false; |
|
if (gbIsMultiplayer && questlist[i].isSinglePlayerOnly) |
|
return false; |
|
return true; |
|
} |
|
|
|
void CheckQuestKill(int m, bool sendmsg) |
|
{ |
|
int i, j; |
|
|
|
if (gbIsSpawn) |
|
return; |
|
|
|
if (monster[m].MType->mtype == MT_SKING) { |
|
quests[Q_SKELKING]._qactive = QUEST_DONE; |
|
plr[myplr].PlaySpeach(82, 30); |
|
if (sendmsg) |
|
NetSendCmdQuest(true, Q_SKELKING); |
|
|
|
} else if (monster[m].MType->mtype == MT_CLEAVER) { |
|
quests[Q_BUTCHER]._qactive = QUEST_DONE; |
|
plr[myplr].PlaySpeach(80, 30); |
|
if (sendmsg) |
|
NetSendCmdQuest(true, Q_BUTCHER); |
|
} else if (monster[m]._uniqtype - 1 == UMT_GARBUD) { //"Gharbad the Weak" |
|
quests[Q_GARBUD]._qactive = QUEST_DONE; |
|
plr[myplr].PlaySpeach(61, 30); |
|
} else if (monster[m]._uniqtype - 1 == UMT_ZHAR) { //"Zhar the Mad" |
|
quests[Q_ZHAR]._qactive = QUEST_DONE; |
|
plr[myplr].PlaySpeach(62, 30); |
|
} else if (monster[m]._uniqtype - 1 == UMT_LAZURUS && gbIsMultiplayer) { //"Arch-Bishop Lazarus" |
|
quests[Q_BETRAYER]._qactive = QUEST_DONE; |
|
quests[Q_BETRAYER]._qvar1 = 7; |
|
quests[Q_DIABLO]._qactive = QUEST_ACTIVE; |
|
|
|
for (j = 0; j < MAXDUNY; j++) { |
|
for (i = 0; i < MAXDUNX; i++) { |
|
if (dPiece[i][j] == 370) { |
|
trigs[numtrigs].position = { i, j }; |
|
trigs[numtrigs]._tmsg = WM_DIABNEXTLVL; |
|
numtrigs++; |
|
} |
|
} |
|
} |
|
plr[myplr].PlaySpeach(83, 30); |
|
if (sendmsg) { |
|
NetSendCmdQuest(true, Q_BETRAYER); |
|
NetSendCmdQuest(true, Q_DIABLO); |
|
} |
|
} else if (monster[m]._uniqtype - 1 == UMT_LAZURUS && !gbIsMultiplayer) { //"Arch-Bishop Lazarus" |
|
quests[Q_BETRAYER]._qactive = QUEST_DONE; |
|
InitVPTriggers(); |
|
quests[Q_BETRAYER]._qvar1 = 7; |
|
quests[Q_BETRAYER]._qvar2 = 4; |
|
quests[Q_DIABLO]._qactive = QUEST_ACTIVE; |
|
AddMissile(35, 32, 35, 32, 0, MIS_RPORTAL, TARGET_MONSTERS, myplr, 0, 0); |
|
plr[myplr].PlaySpeach(83, 30); |
|
} else if (monster[m]._uniqtype - 1 == UMT_WARLORD) { //"Warlord of Blood" |
|
quests[Q_WARLORD]._qactive = QUEST_DONE; |
|
plr[myplr].PlaySpeach(94, 30); |
|
} |
|
} |
|
|
|
void DrawButcher() |
|
{ |
|
int x, y; |
|
|
|
x = 2 * setpc_x + 16; |
|
y = 2 * setpc_y + 16; |
|
DRLG_RectTrans(x + 3, y + 3, x + 10, y + 10); |
|
} |
|
|
|
void DrawSkelKing(int q, int x, int y) |
|
{ |
|
quests[q].position = { 2 * x + 28, 2 * y + 23 }; |
|
} |
|
|
|
void DrawWarLord(int x, int y) |
|
{ |
|
int rw, rh; |
|
int i, j; |
|
BYTE *sp, *setp; |
|
int v; |
|
|
|
setp = LoadFileInMem("Levels\\L4Data\\Warlord2.DUN", nullptr); |
|
rw = *setp; |
|
sp = setp + 2; |
|
rh = *sp; |
|
sp += 2; |
|
setpc_w = rw; |
|
setpc_h = rh; |
|
setpc_x = x; |
|
setpc_y = y; |
|
for (j = y; j < y + rh; j++) { |
|
for (i = x; i < x + rw; i++) { |
|
if (*sp != 0) { |
|
v = *sp; |
|
} else { |
|
v = 6; |
|
} |
|
dungeon[i][j] = v; |
|
sp += 2; |
|
} |
|
} |
|
mem_free_dbg(setp); |
|
} |
|
|
|
void DrawSChamber(int q, int x, int y) |
|
{ |
|
int i, j; |
|
int rw, rh; |
|
int xx, yy; |
|
BYTE *sp, *setp; |
|
int v; |
|
|
|
setp = LoadFileInMem("Levels\\L2Data\\Bonestr1.DUN", nullptr); |
|
rw = *setp; |
|
sp = setp + 2; |
|
rh = *sp; |
|
sp += 2; |
|
setpc_w = rw; |
|
setpc_h = rh; |
|
setpc_x = x; |
|
setpc_y = y; |
|
for (j = y; j < y + rh; j++) { |
|
for (i = x; i < x + rw; i++) { |
|
if (*sp != 0) { |
|
v = *sp; |
|
} else { |
|
v = 3; |
|
} |
|
dungeon[i][j] = v; |
|
sp += 2; |
|
} |
|
} |
|
xx = 2 * x + 22; |
|
yy = 2 * y + 23; |
|
quests[q].position = { xx, yy }; |
|
mem_free_dbg(setp); |
|
} |
|
|
|
void DrawLTBanner(int x, int y) |
|
{ |
|
int rw, rh; |
|
int i, j; |
|
BYTE *sp, *setp; |
|
|
|
setp = LoadFileInMem("Levels\\L1Data\\Banner1.DUN", nullptr); |
|
rw = *setp; |
|
sp = setp + 2; |
|
rh = *sp; |
|
sp += 2; |
|
setpc_w = rw; |
|
setpc_h = rh; |
|
setpc_x = x; |
|
setpc_y = y; |
|
for (j = 0; j < rh; j++) { |
|
for (i = 0; i < rw; i++) { |
|
if (*sp != 0) { |
|
pdungeon[x + i][y + j] = *sp; |
|
} |
|
sp += 2; |
|
} |
|
} |
|
mem_free_dbg(setp); |
|
} |
|
|
|
void DrawBlind(int x, int y) |
|
{ |
|
int rw, rh; |
|
int i, j; |
|
BYTE *sp, *setp; |
|
|
|
setp = LoadFileInMem("Levels\\L2Data\\Blind1.DUN", nullptr); |
|
rw = *setp; |
|
sp = setp + 2; |
|
rh = *sp; |
|
sp += 2; |
|
setpc_x = x; |
|
setpc_y = y; |
|
setpc_w = rw; |
|
setpc_h = rh; |
|
for (j = 0; j < rh; j++) { |
|
for (i = 0; i < rw; i++) { |
|
if (*sp != 0) { |
|
pdungeon[x + i][y + j] = *sp; |
|
} |
|
sp += 2; |
|
} |
|
} |
|
mem_free_dbg(setp); |
|
} |
|
|
|
void DrawBlood(int x, int y) |
|
{ |
|
int rw, rh; |
|
int i, j; |
|
BYTE *sp, *setp; |
|
|
|
setp = LoadFileInMem("Levels\\L2Data\\Blood2.DUN", nullptr); |
|
rw = *setp; |
|
sp = setp + 2; |
|
rh = *sp; |
|
sp += 2; |
|
setpc_x = x; |
|
setpc_y = y; |
|
setpc_w = rw; |
|
setpc_h = rh; |
|
for (j = 0; j < rh; j++) { |
|
for (i = 0; i < rw; i++) { |
|
if (*sp != 0) { |
|
dungeon[x + i][y + j] = *sp; |
|
} |
|
sp += 2; |
|
} |
|
} |
|
mem_free_dbg(setp); |
|
} |
|
|
|
void DRLG_CheckQuests(int x, int y) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < MAXQUESTS; i++) { |
|
if (QuestStatus(i)) { |
|
switch (quests[i]._qtype) { |
|
case Q_BUTCHER: |
|
DrawButcher(); |
|
break; |
|
case Q_LTBANNER: |
|
DrawLTBanner(x, y); |
|
break; |
|
case Q_BLIND: |
|
DrawBlind(x, y); |
|
break; |
|
case Q_BLOOD: |
|
DrawBlood(x, y); |
|
break; |
|
case Q_WARLORD: |
|
DrawWarLord(x, y); |
|
break; |
|
case Q_SKELKING: |
|
DrawSkelKing(i, x, y); |
|
break; |
|
case Q_SCHAMB: |
|
DrawSChamber(i, x, y); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
void SetReturnLvlPos() |
|
{ |
|
switch (setlvlnum) { |
|
case SL_SKELKING: |
|
ReturnLvlX = quests[Q_SKELKING].position.x + 1; |
|
ReturnLvlY = quests[Q_SKELKING].position.y; |
|
ReturnLvl = quests[Q_SKELKING]._qlevel; |
|
ReturnLvlT = DTYPE_CATHEDRAL; |
|
break; |
|
case SL_BONECHAMB: |
|
ReturnLvlX = quests[Q_SCHAMB].position.x + 1; |
|
ReturnLvlY = quests[Q_SCHAMB].position.y; |
|
ReturnLvl = quests[Q_SCHAMB]._qlevel; |
|
ReturnLvlT = DTYPE_CATACOMBS; |
|
break; |
|
case SL_MAZE: |
|
break; |
|
case SL_POISONWATER: |
|
ReturnLvlX = quests[Q_PWATER].position.x; |
|
ReturnLvlY = quests[Q_PWATER].position.y + 1; |
|
ReturnLvl = quests[Q_PWATER]._qlevel; |
|
ReturnLvlT = DTYPE_CATHEDRAL; |
|
break; |
|
case SL_VILEBETRAYER: |
|
ReturnLvlX = quests[Q_BETRAYER].position.x + 1; |
|
ReturnLvlY = quests[Q_BETRAYER].position.y - 1; |
|
ReturnLvl = quests[Q_BETRAYER]._qlevel; |
|
ReturnLvlT = DTYPE_HELL; |
|
break; |
|
case SL_NONE: |
|
break; |
|
} |
|
} |
|
|
|
void GetReturnLvlPos() |
|
{ |
|
if (quests[Q_BETRAYER]._qactive == QUEST_DONE) |
|
quests[Q_BETRAYER]._qvar2 = 2; |
|
ViewX = ReturnLvlX; |
|
ViewY = ReturnLvlY; |
|
currlevel = ReturnLvl; |
|
leveltype = ReturnLvlT; |
|
} |
|
|
|
void LoadPWaterPalette() |
|
{ |
|
if (!setlevel || setlvlnum != quests[Q_PWATER]._qslvl || quests[Q_PWATER]._qactive == QUEST_INIT || leveltype != quests[Q_PWATER]._qlvltype) |
|
return; |
|
|
|
if (quests[Q_PWATER]._qactive == QUEST_DONE) |
|
LoadPalette("Levels\\L3Data\\L3pwater.pal"); |
|
else |
|
LoadPalette("Levels\\L3Data\\L3pfoul.pal"); |
|
} |
|
|
|
void ResyncMPQuests() |
|
{ |
|
if (gbIsSpawn) |
|
return; |
|
|
|
if (quests[Q_SKELKING]._qactive == QUEST_INIT |
|
&& currlevel >= quests[Q_SKELKING]._qlevel - 1 |
|
&& currlevel <= quests[Q_SKELKING]._qlevel + 1) { |
|
quests[Q_SKELKING]._qactive = QUEST_ACTIVE; |
|
NetSendCmdQuest(true, Q_SKELKING); |
|
} |
|
if (quests[Q_BUTCHER]._qactive == QUEST_INIT |
|
&& currlevel >= quests[Q_BUTCHER]._qlevel - 1 |
|
&& currlevel <= quests[Q_BUTCHER]._qlevel + 1) { |
|
quests[Q_BUTCHER]._qactive = QUEST_ACTIVE; |
|
NetSendCmdQuest(true, Q_BUTCHER); |
|
} |
|
if (quests[Q_BETRAYER]._qactive == QUEST_INIT && currlevel == quests[Q_BETRAYER]._qlevel - 1) { |
|
quests[Q_BETRAYER]._qactive = QUEST_ACTIVE; |
|
NetSendCmdQuest(true, Q_BETRAYER); |
|
} |
|
if (QuestStatus(Q_BETRAYER)) |
|
AddObject(OBJ_ALTBOY, 2 * setpc_x + 20, 2 * setpc_y + 22); |
|
if (quests[Q_GRAVE]._qactive == QUEST_INIT && currlevel == quests[Q_GRAVE]._qlevel - 1) { |
|
quests[Q_GRAVE]._qactive = QUEST_ACTIVE; |
|
NetSendCmdQuest(true, Q_GRAVE); |
|
} |
|
if (quests[Q_DEFILER]._qactive == QUEST_INIT && currlevel == quests[Q_DEFILER]._qlevel - 1) { |
|
quests[Q_DEFILER]._qactive = QUEST_ACTIVE; |
|
NetSendCmdQuest(true, Q_DEFILER); |
|
} |
|
if (quests[Q_NAKRUL]._qactive == QUEST_INIT && currlevel == quests[Q_NAKRUL]._qlevel - 1) { |
|
quests[Q_NAKRUL]._qactive = QUEST_ACTIVE; |
|
NetSendCmdQuest(true, Q_NAKRUL); |
|
} |
|
if (quests[Q_JERSEY]._qactive == QUEST_INIT && currlevel == quests[Q_JERSEY]._qlevel - 1) { |
|
quests[Q_JERSEY]._qactive = QUEST_ACTIVE; |
|
NetSendCmdQuest(true, Q_JERSEY); |
|
} |
|
} |
|
|
|
void ResyncQuests() |
|
{ |
|
int i, tren, x, y; |
|
|
|
if (gbIsSpawn) |
|
return; |
|
|
|
if (QuestStatus(Q_LTBANNER)) { |
|
if (quests[Q_LTBANNER]._qvar1 == 1) { |
|
ObjChangeMapResync( |
|
setpc_w + setpc_x - 2, |
|
setpc_h + setpc_y - 2, |
|
setpc_w + setpc_x + 1, |
|
setpc_h + setpc_y + 1); |
|
} |
|
if (quests[Q_LTBANNER]._qvar1 == 2) { |
|
ObjChangeMapResync( |
|
setpc_w + setpc_x - 2, |
|
setpc_h + setpc_y - 2, |
|
setpc_w + setpc_x + 1, |
|
setpc_h + setpc_y + 1); |
|
ObjChangeMapResync(setpc_x, setpc_y, (setpc_w / 2) + setpc_x + 2, (setpc_h / 2) + setpc_y - 2); |
|
for (i = 0; i < nobjects; i++) |
|
SyncObjectAnim(objectactive[i]); |
|
tren = TransVal; |
|
TransVal = 9; |
|
DRLG_MRectTrans(setpc_x, setpc_y, (setpc_w / 2) + setpc_x + 4, setpc_y + (setpc_h / 2)); |
|
TransVal = tren; |
|
} |
|
if (quests[Q_LTBANNER]._qvar1 == 3) { |
|
x = setpc_x; |
|
y = setpc_y; |
|
ObjChangeMapResync(x, y, x + setpc_w + 1, y + setpc_h + 1); |
|
for (i = 0; i < nobjects; i++) |
|
SyncObjectAnim(objectactive[i]); |
|
tren = TransVal; |
|
TransVal = 9; |
|
DRLG_MRectTrans(setpc_x, setpc_y, (setpc_w / 2) + setpc_x + 4, setpc_y + (setpc_h / 2)); |
|
TransVal = tren; |
|
} |
|
} |
|
if (currlevel == quests[Q_MUSHROOM]._qlevel) { |
|
if (quests[Q_MUSHROOM]._qactive == QUEST_INIT && quests[Q_MUSHROOM]._qvar1 == QS_INIT) { |
|
SpawnQuestItem(IDI_FUNGALTM, 0, 0, 5, true); |
|
quests[Q_MUSHROOM]._qvar1 = QS_TOMESPAWNED; |
|
} else { |
|
if (quests[Q_MUSHROOM]._qactive == QUEST_ACTIVE) { |
|
if (quests[Q_MUSHROOM]._qvar1 >= QS_MUSHGIVEN) { |
|
Qtalklist[TOWN_WITCH][Q_MUSHROOM] = TEXT_NONE; |
|
Qtalklist[TOWN_HEALER][Q_MUSHROOM] = TEXT_MUSH3; |
|
} else if (quests[Q_MUSHROOM]._qvar1 >= QS_BRAINGIVEN) { |
|
Qtalklist[TOWN_HEALER][Q_MUSHROOM] = TEXT_NONE; |
|
} |
|
} |
|
} |
|
} |
|
if (currlevel == quests[Q_VEIL]._qlevel + 1 && quests[Q_VEIL]._qactive == QUEST_ACTIVE && quests[Q_VEIL]._qvar1 == 0) { |
|
quests[Q_VEIL]._qvar1 = 1; |
|
SpawnQuestItem(IDI_GLDNELIX, 0, 0, 5, true); |
|
} |
|
if (setlevel && setlvlnum == SL_VILEBETRAYER) { |
|
if (quests[Q_BETRAYER]._qvar1 >= 4) |
|
ObjChangeMapResync(1, 11, 20, 18); |
|
if (quests[Q_BETRAYER]._qvar1 >= 6) |
|
ObjChangeMapResync(1, 18, 20, 24); |
|
if (quests[Q_BETRAYER]._qvar1 >= 7) |
|
InitVPTriggers(); |
|
for (i = 0; i < nobjects; i++) |
|
SyncObjectAnim(objectactive[i]); |
|
} |
|
if (currlevel == quests[Q_BETRAYER]._qlevel |
|
&& !setlevel |
|
&& (quests[Q_BETRAYER]._qvar2 == 1 || quests[Q_BETRAYER]._qvar2 >= 3) |
|
&& (quests[Q_BETRAYER]._qactive == QUEST_ACTIVE || quests[Q_BETRAYER]._qactive == QUEST_DONE)) { |
|
quests[Q_BETRAYER]._qvar2 = 2; |
|
} |
|
} |
|
|
|
static void PrintQLString(const CelOutputBuffer &out, int x, int y, bool cjustflag, const char *str, text_color col) |
|
{ |
|
int len, width, i, k, sx, sy; |
|
BYTE c; |
|
|
|
sx = x + 32; |
|
sy = y * 12 + 44; |
|
len = strlen(str); |
|
k = 0; |
|
if (cjustflag) { |
|
width = 0; |
|
for (i = 0; i < len; i++) |
|
width += fontkern[fontframe[gbFontTransTbl[(BYTE)str[i]]]] + 1; |
|
if (width < 257) |
|
k = (257 - width) / 2; |
|
sx += k; |
|
} |
|
if (qline == y) { |
|
CelDrawTo(out, cjustflag ? x + k + 12 : x + 12, sy + 1, pSPentSpn2Cels, PentSpn2Spin(), 12); |
|
} |
|
for (i = 0; i < len; i++) { |
|
c = fontframe[gbFontTransTbl[(BYTE)str[i]]]; |
|
k += fontkern[c] + 1; |
|
if (c && k <= 257) { |
|
PrintChar(out, sx, sy, c, col); |
|
} |
|
sx += fontkern[c] + 1; |
|
} |
|
if (qline == y) { |
|
CelDrawTo(out, cjustflag ? x + k + 36 : 276 - x, sy + 1, pSPentSpn2Cels, PentSpn2Spin(), 12); |
|
} |
|
} |
|
|
|
void DrawQuestLog(const CelOutputBuffer &out) |
|
{ |
|
int y, i; |
|
|
|
PrintQLString(out, 0, 2, true, _("Quest Log"), COL_GOLD); |
|
CelDrawTo(out, 0, 351, pQLogCel, 1, SPANEL_WIDTH); |
|
y = qtopline; |
|
for (i = 0; i < numqlines; i++) { |
|
PrintQLString(out, 0, y, true, questlist[qlist[i]]._qlstr, COL_WHITE); |
|
y += 2; |
|
} |
|
PrintQLString(out, 0, 22, true, _("Close Quest Log"), COL_WHITE); |
|
} |
|
|
|
void StartQuestlog() |
|
{ |
|
DWORD i; |
|
|
|
numqlines = 0; |
|
for (i = 0; i < MAXQUESTS; i++) { |
|
if (quests[i]._qactive == QUEST_ACTIVE && quests[i]._qlog) { |
|
qlist[numqlines] = i; |
|
numqlines++; |
|
} |
|
} |
|
if (numqlines > 5) { |
|
qtopline = 5 - (numqlines / 2); |
|
} else { |
|
qtopline = 8; |
|
} |
|
qline = 22; |
|
if (numqlines != 0) |
|
qline = qtopline; |
|
questlog = true; |
|
} |
|
|
|
void QuestlogUp() |
|
{ |
|
if (numqlines) { |
|
if (qline == qtopline) { |
|
qline = 22; |
|
} else if (qline == 22) { |
|
qline = qtopline + 2 * numqlines - 2; |
|
} else { |
|
qline -= 2; |
|
} |
|
PlaySFX(IS_TITLEMOV); |
|
} |
|
} |
|
|
|
void QuestlogDown() |
|
{ |
|
if (numqlines) { |
|
if (qline == 22) { |
|
qline = qtopline; |
|
} else if (qline == qtopline + 2 * numqlines - 2) { |
|
qline = 22; |
|
} else { |
|
qline += 2; |
|
} |
|
PlaySFX(IS_TITLEMOV); |
|
} |
|
} |
|
|
|
void QuestlogEnter() |
|
{ |
|
PlaySFX(IS_TITLSLCT); |
|
if (numqlines && qline != 22) |
|
InitQTextMsg(quests[qlist[(qline - qtopline) / 2]]._qmsg); |
|
questlog = false; |
|
} |
|
|
|
void QuestlogESC() |
|
{ |
|
int y, i; |
|
|
|
y = (MouseY - 32) / 12; |
|
if (numqlines) { |
|
for (i = 0; i < numqlines; i++) { |
|
if (y == qtopline + 2 * i) { |
|
qline = y; |
|
QuestlogEnter(); |
|
} |
|
} |
|
} |
|
if (y == 22) { |
|
qline = 22; |
|
QuestlogEnter(); |
|
} |
|
} |
|
|
|
void SetMultiQuest(int q, quest_state s, int l, int v1) |
|
{ |
|
if (gbIsSpawn) |
|
return; |
|
|
|
if (quests[q]._qactive != QUEST_DONE) { |
|
if (s > quests[q]._qactive) |
|
quests[q]._qactive = s; |
|
quests[q]._qlog |= l; |
|
if (v1 > quests[q]._qvar1) |
|
quests[q]._qvar1 = v1; |
|
} |
|
} |
|
|
|
} // namespace devilution
|
|
|