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.
5198 lines
130 KiB
5198 lines
130 KiB
/** |
|
* @file items.cpp |
|
* |
|
* Implementation of item functionality. |
|
*/ |
|
#include <algorithm> |
|
#include <climits> |
|
#include <cstdint> |
|
|
|
#include "cursor.h" |
|
#include "doom.h" |
|
#include "dx.h" |
|
#include "init.h" |
|
#include "lighting.h" |
|
#include "missiles.h" |
|
#include "options.h" |
|
#include "stores.h" |
|
#include "utils/language.h" |
|
#include "utils/math.h" |
|
|
|
#define ITEMTYPES 43 |
|
|
|
namespace devilution { |
|
namespace { |
|
std::optional<CelSprite> itemanims[ITEMTYPES]; |
|
|
|
} // namespace |
|
|
|
enum anim_armor_id : uint8_t { |
|
// clang-format off |
|
ANIM_ID_LIGHT_ARMOR = 0, |
|
ANIM_ID_MEDIUM_ARMOR = 1 << 4, |
|
ANIM_ID_HEAVY_ARMOR = 1 << 5, |
|
// clang-format on |
|
}; |
|
|
|
int itemactive[MAXITEMS]; |
|
bool uitemflag; |
|
int itemavail[MAXITEMS]; |
|
ItemStruct curruitem; |
|
ItemGetRecordStruct itemrecord[MAXITEMS]; |
|
/** Contains the items on ground in the current game. */ |
|
ItemStruct items[MAXITEMS + 1]; |
|
bool itemhold[3][3]; |
|
CornerStoneStruct CornerStone; |
|
bool UniqueItemFlags[128]; |
|
int numitems; |
|
int gnNumGetRecords; |
|
|
|
/* data */ |
|
|
|
int OilLevels[] = { 1, 10, 1, 10, 4, 1, 5, 17, 1, 10 }; |
|
int OilValues[] = { 500, 2500, 500, 2500, 1500, 100, 2500, 15000, 500, 2500 }; |
|
item_misc_id OilMagic[] = { |
|
IMISC_OILACC, |
|
IMISC_OILMAST, |
|
IMISC_OILSHARP, |
|
IMISC_OILDEATH, |
|
IMISC_OILSKILL, |
|
IMISC_OILBSMTH, |
|
IMISC_OILFORT, |
|
IMISC_OILPERM, |
|
IMISC_OILHARD, |
|
IMISC_OILIMP, |
|
}; |
|
char OilNames[10][25] = { |
|
N_("Oil of Accuracy"), |
|
N_("Oil of Mastery"), |
|
N_("Oil of Sharpness"), |
|
N_("Oil of Death"), |
|
N_("Oil of Skill"), |
|
N_("Blacksmith Oil"), |
|
N_("Oil of Fortitude"), |
|
N_("Oil of Permanence"), |
|
N_("Oil of Hardening"), |
|
N_("Oil of Imperviousness") |
|
}; |
|
int MaxGold = GOLD_MAX_LIMIT; |
|
|
|
/** Maps from item_cursor_graphic to in-memory item type. */ |
|
BYTE ItemCAnimTbl[] = { |
|
20, 16, 16, 16, 4, 4, 4, 12, 12, 12, |
|
12, 12, 12, 12, 12, 21, 21, 25, 12, 28, |
|
28, 28, 38, 38, 38, 32, 38, 38, 38, 24, |
|
24, 26, 2, 25, 22, 23, 24, 25, 27, 27, |
|
29, 0, 0, 0, 12, 12, 12, 12, 12, 0, |
|
8, 8, 0, 8, 8, 8, 8, 8, 8, 6, |
|
8, 8, 8, 6, 8, 8, 6, 8, 8, 6, |
|
6, 6, 8, 8, 8, 5, 9, 13, 13, 13, |
|
5, 5, 5, 15, 5, 5, 18, 18, 18, 30, |
|
5, 5, 14, 5, 14, 13, 16, 18, 5, 5, |
|
7, 1, 3, 17, 1, 15, 10, 14, 3, 11, |
|
8, 0, 1, 7, 0, 7, 15, 7, 3, 3, |
|
3, 6, 6, 11, 11, 11, 31, 14, 14, 14, |
|
6, 6, 7, 3, 8, 14, 0, 14, 14, 0, |
|
33, 1, 1, 1, 1, 1, 7, 7, 7, 14, |
|
14, 17, 17, 17, 0, 34, 1, 0, 3, 17, |
|
8, 8, 6, 1, 3, 3, 11, 3, 12, 12, |
|
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, |
|
12, 12, 12, 12, 12, 12, 12, 35, 39, 36, |
|
36, 36, 37, 38, 38, 38, 38, 38, 41, 42, |
|
8, 8, 8, 17, 0, 6, 8, 11, 11, 3, |
|
3, 1, 6, 6, 6, 1, 8, 6, 11, 3, |
|
6, 8, 1, 6, 6, 17, 40, 0, 0 |
|
}; |
|
/** Map of item type .cel file names. */ |
|
const char *const ItemDropNames[] = { |
|
"Armor2", |
|
"Axe", |
|
"FBttle", |
|
"Bow", |
|
"GoldFlip", |
|
"Helmut", |
|
"Mace", |
|
"Shield", |
|
"SwrdFlip", |
|
"Rock", |
|
"Cleaver", |
|
"Staff", |
|
"Ring", |
|
"CrownF", |
|
"LArmor", |
|
"WShield", |
|
"Scroll", |
|
"FPlateAr", |
|
"FBook", |
|
"Food", |
|
"FBttleBB", |
|
"FBttleDY", |
|
"FBttleOR", |
|
"FBttleBR", |
|
"FBttleBL", |
|
"FBttleBY", |
|
"FBttleWH", |
|
"FBttleDB", |
|
"FEar", |
|
"FBrain", |
|
"FMush", |
|
"Innsign", |
|
"Bldstn", |
|
"Fanvil", |
|
"FLazStaf", |
|
"bombs1", |
|
"halfps1", |
|
"wholeps1", |
|
"runes1", |
|
"teddys1", |
|
"cows1", |
|
"donkys1", |
|
"mooses1", |
|
}; |
|
/** Maps of item drop animation length. */ |
|
BYTE ItemAnimLs[] = { |
|
15, |
|
13, |
|
16, |
|
13, |
|
10, |
|
13, |
|
13, |
|
13, |
|
13, |
|
10, |
|
13, |
|
13, |
|
13, |
|
13, |
|
13, |
|
13, |
|
13, |
|
13, |
|
13, |
|
1, |
|
16, |
|
16, |
|
16, |
|
16, |
|
16, |
|
16, |
|
16, |
|
16, |
|
13, |
|
12, |
|
12, |
|
13, |
|
13, |
|
13, |
|
8, |
|
10, |
|
16, |
|
16, |
|
10, |
|
10, |
|
15, |
|
15, |
|
15, |
|
}; |
|
/** Maps of drop sounds effect of dropping the item on ground. */ |
|
_sfx_id ItemDropSnds[] = { |
|
IS_FHARM, |
|
IS_FAXE, |
|
IS_FPOT, |
|
IS_FBOW, |
|
IS_GOLD, |
|
IS_FCAP, |
|
IS_FSWOR, |
|
IS_FSHLD, |
|
IS_FSWOR, |
|
IS_FROCK, |
|
IS_FAXE, |
|
IS_FSTAF, |
|
IS_FRING, |
|
IS_FCAP, |
|
IS_FLARM, |
|
IS_FSHLD, |
|
IS_FSCRL, |
|
IS_FHARM, |
|
IS_FBOOK, |
|
IS_FLARM, |
|
IS_FPOT, |
|
IS_FPOT, |
|
IS_FPOT, |
|
IS_FPOT, |
|
IS_FPOT, |
|
IS_FPOT, |
|
IS_FPOT, |
|
IS_FPOT, |
|
IS_FBODY, |
|
IS_FBODY, |
|
IS_FMUSH, |
|
IS_ISIGN, |
|
IS_FBLST, |
|
IS_FANVL, |
|
IS_FSTAF, |
|
IS_FROCK, |
|
IS_FSCRL, |
|
IS_FSCRL, |
|
IS_FROCK, |
|
IS_FMUSH, |
|
IS_FHARM, |
|
IS_FLARM, |
|
IS_FLARM, |
|
}; |
|
/** Maps of drop sounds effect of placing the item in the inventory. */ |
|
_sfx_id ItemInvSnds[] = { |
|
IS_IHARM, |
|
IS_IAXE, |
|
IS_IPOT, |
|
IS_IBOW, |
|
IS_GOLD, |
|
IS_ICAP, |
|
IS_ISWORD, |
|
IS_ISHIEL, |
|
IS_ISWORD, |
|
IS_IROCK, |
|
IS_IAXE, |
|
IS_ISTAF, |
|
IS_IRING, |
|
IS_ICAP, |
|
IS_ILARM, |
|
IS_ISHIEL, |
|
IS_ISCROL, |
|
IS_IHARM, |
|
IS_IBOOK, |
|
IS_IHARM, |
|
IS_IPOT, |
|
IS_IPOT, |
|
IS_IPOT, |
|
IS_IPOT, |
|
IS_IPOT, |
|
IS_IPOT, |
|
IS_IPOT, |
|
IS_IPOT, |
|
IS_IBODY, |
|
IS_IBODY, |
|
IS_IMUSH, |
|
IS_ISIGN, |
|
IS_IBLST, |
|
IS_IANVL, |
|
IS_ISTAF, |
|
IS_IROCK, |
|
IS_ISCROL, |
|
IS_ISCROL, |
|
IS_IROCK, |
|
IS_IMUSH, |
|
IS_IHARM, |
|
IS_ILARM, |
|
IS_ILARM, |
|
}; |
|
/** Maps from Griswold premium item number to a quality level delta as added to the base quality level. */ |
|
int premiumlvladd[] = { |
|
// clang-format off |
|
-1, |
|
-1, |
|
0, |
|
0, |
|
1, |
|
2, |
|
// clang-format on |
|
}; |
|
/** Maps from Griswold premium item number to a quality level delta as added to the base quality level. */ |
|
int premiumLvlAddHellfire[] = { |
|
// clang-format off |
|
-1, |
|
-1, |
|
-1, |
|
0, |
|
0, |
|
0, |
|
0, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
3, |
|
3, |
|
// clang-format on |
|
}; |
|
|
|
bool IsItemAvailable(int i) |
|
{ |
|
if (gbIsHellfire) |
|
return true; |
|
return ( |
|
i != IDI_MAPOFDOOM // Cathedral Map |
|
&& i != IDI_LGTFORGE // Bovine Plate |
|
&& (i < IDI_OIL || i > IDI_GREYSUIT) // Hellfire exclusive items |
|
&& (i < 83 || i > 86) // Oils |
|
&& i != 92 // Scroll of Search |
|
&& (i < 161 || i > 165) // Runes |
|
&& i != IDI_SORCERER // Short Staff of Mana |
|
) |
|
|| ( |
|
// Bard items are technically Hellfire-exclusive |
|
// but are just normal items with adjusted stats. |
|
sgOptions.Gameplay.bTestBard && (i == IDI_BARDSWORD || i == IDI_BARDDAGGER)); |
|
} |
|
|
|
BYTE GetOutlineColor(const ItemStruct &item, bool checkReq) |
|
{ |
|
if (checkReq && !item._iStatFlag) |
|
return ICOL_RED; |
|
if (item._itype == ITYPE_GOLD) |
|
return ICOL_YELLOW; |
|
if (item._iMagical == ITEM_QUALITY_MAGIC) |
|
return ICOL_BLUE; |
|
if (item._iMagical == ITEM_QUALITY_UNIQUE) |
|
return ICOL_YELLOW; |
|
|
|
return ICOL_WHITE; |
|
} |
|
|
|
bool IsUniqueAvailable(int i) |
|
{ |
|
return gbIsHellfire || i <= 89; |
|
} |
|
|
|
static bool IsPrefixValidForItemType(int i, int flgs) |
|
{ |
|
int PLIType = PL_Prefix[i].PLIType; |
|
|
|
if (!gbIsHellfire) { |
|
if (i > 82) |
|
return false; |
|
|
|
if (i >= 12 && i <= 20) |
|
PLIType &= ~PLT_STAFF; |
|
} |
|
|
|
return (flgs & PLIType) != 0; |
|
} |
|
|
|
static bool IsSuffixValidForItemType(int i, int flgs) |
|
{ |
|
int PLIType = PL_Suffix[i].PLIType; |
|
|
|
if (!gbIsHellfire) { |
|
if (i > 94) |
|
return false; |
|
|
|
if ((i >= 0 && i <= 1) |
|
|| (i >= 14 && i <= 15) |
|
|| (i >= 21 && i <= 22) |
|
|| (i >= 34 && i <= 36) |
|
|| (i >= 41 && i <= 44) |
|
|| (i >= 60 && i <= 63)) |
|
PLIType &= ~PLT_STAFF; |
|
} |
|
|
|
return (flgs & PLIType) != 0; |
|
} |
|
|
|
int items_get_currlevel() |
|
{ |
|
int lvl; |
|
|
|
lvl = currlevel; |
|
if (currlevel >= 17 && currlevel <= 20) |
|
lvl = currlevel - 8; |
|
if (currlevel >= 21 && currlevel <= 24) |
|
lvl = currlevel - 7; |
|
|
|
return lvl; |
|
} |
|
|
|
void InitItemGFX() |
|
{ |
|
char arglist[64]; |
|
|
|
int itemTypes = gbIsHellfire ? ITEMTYPES : 35; |
|
for (int i = 0; i < itemTypes; i++) { |
|
sprintf(arglist, "Items\\%s.CEL", ItemDropNames[i]); |
|
itemanims[i] = LoadCel(arglist, ItemAnimWidth); |
|
} |
|
memset(UniqueItemFlags, 0, sizeof(UniqueItemFlags)); |
|
} |
|
|
|
bool ItemPlace(int xp, int yp) |
|
{ |
|
if (dMonster[xp][yp] != 0) |
|
return false; |
|
if (dPlayer[xp][yp] != 0) |
|
return false; |
|
if (dItem[xp][yp] != 0) |
|
return false; |
|
if (dObject[xp][yp] != 0) |
|
return false; |
|
if ((dFlags[xp][yp] & BFLAG_POPULATED) != 0) |
|
return false; |
|
if (nSolidTable[dPiece[xp][yp]]) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
void AddInitItems() |
|
{ |
|
int x, y, j, rnd; |
|
|
|
int curlv = items_get_currlevel(); |
|
rnd = GenerateRnd(3) + 3; |
|
for (j = 0; j < rnd; j++) { |
|
int ii = AllocateItem(); |
|
|
|
x = GenerateRnd(80) + 16; |
|
y = GenerateRnd(80) + 16; |
|
while (!ItemPlace(x, y)) { |
|
x = GenerateRnd(80) + 16; |
|
y = GenerateRnd(80) + 16; |
|
} |
|
items[ii].position = { x, y }; |
|
|
|
dItem[x][y] = ii + 1; |
|
|
|
items[ii]._iSeed = AdvanceRndSeed(); |
|
SetRndSeed(items[ii]._iSeed); |
|
|
|
if (GenerateRnd(2) != 0) |
|
GetItemAttrs(ii, IDI_HEAL, curlv); |
|
else |
|
GetItemAttrs(ii, IDI_MANA, curlv); |
|
|
|
items[ii]._iCreateInfo = curlv | CF_PREGEN; |
|
SetupItem(ii); |
|
items[ii]._iAnimFrame = items[ii]._iAnimLen; |
|
items[ii]._iAnimFlag = false; |
|
items[ii]._iSelFlag = 1; |
|
DeltaAddItem(ii); |
|
} |
|
} |
|
|
|
static void items_42390F() |
|
{ |
|
int x, y, id; |
|
|
|
x = GenerateRnd(80) + 16; |
|
y = GenerateRnd(80) + 16; |
|
while (!ItemPlace(x, y)) { |
|
x = GenerateRnd(80) + 16; |
|
y = GenerateRnd(80) + 16; |
|
} |
|
switch (currlevel) { |
|
case 22: |
|
id = IDI_NOTE2; |
|
break; |
|
case 23: |
|
id = IDI_NOTE3; |
|
break; |
|
default: |
|
id = IDI_NOTE1; |
|
break; |
|
} |
|
SpawnQuestItem(id, x, y, 0, 1); |
|
} |
|
|
|
void InitItems() |
|
{ |
|
int i; |
|
|
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_GOLD, 1); |
|
golditem = items[0]; |
|
golditem._iStatFlag = true; |
|
numitems = 0; |
|
|
|
for (i = 0; i < MAXITEMS; i++) { |
|
items[i]._itype = ITYPE_NONE; |
|
items[i].position = { 0, 0 }; |
|
items[i]._iAnimFlag = false; |
|
items[i]._iSelFlag = 0; |
|
items[i]._iIdentified = false; |
|
items[i]._iPostDraw = false; |
|
} |
|
|
|
for (i = 0; i < MAXITEMS; i++) { |
|
itemavail[i] = i; |
|
itemactive[i] = 0; |
|
} |
|
|
|
if (!setlevel) { |
|
AdvanceRndSeed(); /* unused */ |
|
if (QuestStatus(Q_ROCK)) |
|
SpawnRock(); |
|
if (QuestStatus(Q_ANVIL)) |
|
SpawnQuestItem(IDI_ANVIL, 2 * setpc_x + 27, 2 * setpc_y + 27, 0, 1); |
|
if (sgGameInitInfo.bCowQuest && currlevel == 20) |
|
SpawnQuestItem(IDI_BROWNSUIT, 25, 25, 3, 1); |
|
if (sgGameInitInfo.bCowQuest && currlevel == 19) |
|
SpawnQuestItem(IDI_GREYSUIT, 25, 25, 3, 1); |
|
if (currlevel > 0 && currlevel < 16) |
|
AddInitItems(); |
|
if (currlevel >= 21 && currlevel <= 23) |
|
items_42390F(); |
|
} |
|
|
|
uitemflag = false; |
|
} |
|
|
|
void CalcPlrItemVals(int p, bool Loadgfx) |
|
{ |
|
int d; |
|
|
|
int mind = 0; // min damage |
|
int maxd = 0; // max damage |
|
int tac = 0; // accuracy |
|
|
|
int g; |
|
int i; |
|
|
|
int bdam = 0; // bonus damage |
|
int btohit = 0; // bonus chance to hit |
|
int bac = 0; // bonus accuracy |
|
|
|
int iflgs = ISPL_NONE; // item_special_effect flags |
|
|
|
int pDamAcFlags = 0; |
|
|
|
int sadd = 0; // added strength |
|
int madd = 0; // added magic |
|
int dadd = 0; // added dexterity |
|
int vadd = 0; // added vitality |
|
|
|
uint64_t spl = 0; // bitarray for all enabled/active spells |
|
|
|
int fr = 0; // fire resistance |
|
int lr = 0; // lightning resistance |
|
int mr = 0; // magic resistance |
|
|
|
int dmod = 0; // bonus damage mod? |
|
int ghit = 0; // increased damage from enemies |
|
|
|
int lrad = 10; // light radius |
|
|
|
int ihp = 0; // increased HP |
|
int imana = 0; // increased mana |
|
|
|
int spllvladd = 0; // increased spell level |
|
int enac = 0; // enhanced accuracy |
|
|
|
int fmin = 0; // minimum fire damage |
|
int fmax = 0; // maximum fire damage |
|
int lmin = 0; // minimum lightning damage |
|
int lmax = 0; // maximum lightning damage |
|
|
|
for (i = 0; i < NUM_INVLOC; i++) { |
|
ItemStruct *itm = &plr[p].InvBody[i]; |
|
if (!itm->isEmpty() && itm->_iStatFlag) { |
|
|
|
mind += itm->_iMinDam; |
|
maxd += itm->_iMaxDam; |
|
tac += itm->_iAC; |
|
|
|
if (itm->_iSpell != SPL_NULL) { |
|
spl |= GetSpellBitmask(itm->_iSpell); |
|
} |
|
|
|
if (itm->_iMagical == ITEM_QUALITY_NORMAL || itm->_iIdentified) { |
|
bdam += itm->_iPLDam; |
|
btohit += itm->_iPLToHit; |
|
if (itm->_iPLAC) { |
|
int tmpac = itm->_iAC; |
|
tmpac *= itm->_iPLAC; |
|
tmpac /= 100; |
|
if (tmpac == 0) |
|
tmpac = math::Sign(itm->_iPLAC); |
|
bac += tmpac; |
|
} |
|
iflgs |= itm->_iFlags; |
|
pDamAcFlags |= itm->_iDamAcFlags; |
|
sadd += itm->_iPLStr; |
|
madd += itm->_iPLMag; |
|
dadd += itm->_iPLDex; |
|
vadd += itm->_iPLVit; |
|
fr += itm->_iPLFR; |
|
lr += itm->_iPLLR; |
|
mr += itm->_iPLMR; |
|
dmod += itm->_iPLDamMod; |
|
ghit += itm->_iPLGetHit; |
|
lrad += itm->_iPLLight; |
|
ihp += itm->_iPLHP; |
|
imana += itm->_iPLMana; |
|
spllvladd += itm->_iSplLvlAdd; |
|
enac += itm->_iPLEnAc; |
|
fmin += itm->_iFMinDam; |
|
fmax += itm->_iFMaxDam; |
|
lmin += itm->_iLMinDam; |
|
lmax += itm->_iLMaxDam; |
|
} |
|
} |
|
} |
|
|
|
if (mind == 0 && maxd == 0) { |
|
mind = 1; |
|
maxd = 1; |
|
|
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) { |
|
maxd = 3; |
|
} |
|
|
|
if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) { |
|
maxd = 3; |
|
} |
|
|
|
if (plr[p]._pClass == HeroClass::Monk) { |
|
mind = std::max(mind, plr[p]._pLevel / 2); |
|
maxd = std::max(maxd, (int)plr[p]._pLevel); |
|
} |
|
} |
|
|
|
if ((plr[p]._pSpellFlags & 2) == 2) { |
|
sadd += 2 * plr[p]._pLevel; |
|
dadd += plr[p]._pLevel + plr[p]._pLevel / 2; |
|
vadd += 2 * plr[p]._pLevel; |
|
} |
|
if ((plr[p]._pSpellFlags & 4) == 4) { |
|
sadd -= 2 * plr[p]._pLevel; |
|
dadd -= plr[p]._pLevel + plr[p]._pLevel / 2; |
|
vadd -= 2 * plr[p]._pLevel; |
|
} |
|
|
|
plr[p]._pIMinDam = mind; |
|
plr[p]._pIMaxDam = maxd; |
|
plr[p]._pIAC = tac; |
|
plr[p]._pIBonusDam = bdam; |
|
plr[p]._pIBonusToHit = btohit; |
|
plr[p]._pIBonusAC = bac; |
|
plr[p]._pIFlags = iflgs; |
|
plr[p].pDamAcFlags = pDamAcFlags; |
|
plr[p]._pIBonusDamMod = dmod; |
|
plr[p]._pIGetHit = ghit; |
|
|
|
lrad = clamp(lrad, 2, 15); |
|
|
|
if (plr[p]._pLightRad != lrad && p == myplr) { |
|
ChangeLightRadius(plr[p]._plid, lrad); |
|
ChangeVisionRadius(plr[p]._pvid, lrad); |
|
plr[p]._pLightRad = lrad; |
|
} |
|
|
|
plr[p]._pStrength = std::max(0, sadd + plr[p]._pBaseStr); |
|
plr[p]._pMagic = std::max(0, madd + plr[p]._pBaseMag); |
|
plr[p]._pDexterity = std::max(0, dadd + plr[p]._pBaseDex); |
|
plr[p]._pVitality = std::max(0, vadd + plr[p]._pBaseVit); |
|
|
|
if (plr[p]._pClass == HeroClass::Rogue) { |
|
plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 200; |
|
} else if (plr[p]._pClass == HeroClass::Monk) { |
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_STAFF) { |
|
if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype != ITYPE_STAFF && (!plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty() || !plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty())) { |
|
plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 300; |
|
} else { |
|
plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 150; |
|
} |
|
} else { |
|
plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 150; |
|
} |
|
} else if (plr[p]._pClass == HeroClass::Bard) { |
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SWORD || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SWORD) |
|
plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 150; |
|
else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_BOW || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_BOW) { |
|
plr[p]._pDamageMod = plr[p]._pLevel * (plr[p]._pStrength + plr[p]._pDexterity) / 250; |
|
} else { |
|
plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 100; |
|
} |
|
} else if (plr[p]._pClass == HeroClass::Barbarian) { |
|
|
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_AXE || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_AXE) { |
|
plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 75; |
|
} else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_MACE || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_MACE) { |
|
plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 75; |
|
} else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_BOW || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_BOW) { |
|
plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 300; |
|
} else { |
|
plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 100; |
|
} |
|
|
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD || plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD) { |
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD) |
|
plr[p]._pIAC -= plr[p].InvBody[INVLOC_HAND_LEFT]._iAC / 2; |
|
else if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD) |
|
plr[p]._pIAC -= plr[p].InvBody[INVLOC_HAND_RIGHT]._iAC / 2; |
|
} else if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_RIGHT]._itype != ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_LEFT]._itype != ITYPE_BOW && plr[p].InvBody[INVLOC_HAND_RIGHT]._itype != ITYPE_BOW) { |
|
plr[p]._pDamageMod += plr[p]._pLevel * plr[p]._pVitality / 100; |
|
} |
|
plr[p]._pIAC += plr[p]._pLevel / 4; |
|
} else { |
|
plr[p]._pDamageMod = plr[p]._pLevel * plr[p]._pStrength / 100; |
|
} |
|
|
|
plr[p]._pISpells = spl; |
|
|
|
EnsureValidReadiedSpell(plr[p]); |
|
|
|
plr[p]._pISplLvlAdd = spllvladd; |
|
plr[p]._pIEnAc = enac; |
|
|
|
if (plr[p]._pClass == HeroClass::Barbarian) { |
|
mr += plr[p]._pLevel; |
|
fr += plr[p]._pLevel; |
|
lr += plr[p]._pLevel; |
|
} |
|
|
|
if ((plr[p]._pSpellFlags & 4) == 4) { |
|
mr -= plr[p]._pLevel; |
|
fr -= plr[p]._pLevel; |
|
lr -= plr[p]._pLevel; |
|
} |
|
|
|
if ((iflgs & ISPL_ALLRESZERO) != 0) { |
|
// reset resistances to zero if the respective special effect is active |
|
mr = 0; |
|
fr = 0; |
|
lr = 0; |
|
} |
|
|
|
plr[p]._pMagResist = clamp(mr, 0, MAXRESIST); |
|
plr[p]._pFireResist = clamp(fr, 0, MAXRESIST); |
|
plr[p]._pLghtResist = clamp(lr, 0, MAXRESIST); |
|
|
|
if (plr[p]._pClass == HeroClass::Warrior) { |
|
vadd *= 2; |
|
} else if (plr[p]._pClass == HeroClass::Barbarian) { |
|
vadd += vadd; |
|
vadd += (vadd / 4); |
|
} else if (plr[p]._pClass == HeroClass::Rogue || plr[p]._pClass == HeroClass::Monk || plr[p]._pClass == HeroClass::Bard) { |
|
vadd += vadd / 2; |
|
} |
|
ihp += (vadd << 6); // BUGFIX: blood boil can cause negative shifts here (see line 757) |
|
|
|
if (plr[p]._pClass == HeroClass::Sorcerer) { |
|
madd *= 2; |
|
} |
|
if (plr[p]._pClass == HeroClass::Rogue || plr[p]._pClass == HeroClass::Monk) { |
|
madd += madd / 2; |
|
} else if (plr[p]._pClass == HeroClass::Bard) { |
|
madd += (madd / 4) + (madd / 2); |
|
} |
|
imana += (madd << 6); |
|
|
|
plr[p]._pMaxHP = ihp + plr[p]._pMaxHPBase; |
|
plr[p]._pHitPoints = std::min(ihp + plr[p]._pHPBase, plr[p]._pMaxHP); |
|
|
|
if (p == myplr && (plr[p]._pHitPoints >> 6) <= 0) { |
|
SetPlayerHitPoints(p, 0); |
|
} |
|
|
|
plr[p]._pMaxMana = imana + plr[p]._pMaxManaBase; |
|
plr[p]._pMana = std::min(imana + plr[p]._pManaBase, plr[p]._pMaxMana); |
|
|
|
plr[p]._pIFMinDam = fmin; |
|
plr[p]._pIFMaxDam = fmax; |
|
plr[p]._pILMinDam = lmin; |
|
plr[p]._pILMaxDam = lmax; |
|
|
|
if ((iflgs & ISPL_INFRAVISION) != 0) { |
|
plr[p]._pInfraFlag = true; |
|
} else { |
|
plr[p]._pInfraFlag = false; |
|
} |
|
|
|
plr[p]._pBlockFlag = false; |
|
if (plr[p]._pClass == HeroClass::Monk) { |
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) { |
|
plr[p]._pBlockFlag = true; |
|
plr[p]._pIFlags |= ISPL_FASTBLOCK; |
|
} |
|
if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_STAFF && plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) { |
|
plr[p]._pBlockFlag = true; |
|
plr[p]._pIFlags |= ISPL_FASTBLOCK; |
|
} |
|
if (plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty() && plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty()) |
|
plr[p]._pBlockFlag = true; |
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON && plr[p].InvBody[INVLOC_HAND_LEFT]._iLoc != ILOC_TWOHAND && plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty()) |
|
plr[p]._pBlockFlag = true; |
|
if (plr[p].InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON && plr[p].InvBody[INVLOC_HAND_RIGHT]._iLoc != ILOC_TWOHAND && plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty()) |
|
plr[p]._pBlockFlag = true; |
|
} |
|
plr[p]._pwtype = WT_MELEE; |
|
|
|
g = 0; |
|
|
|
if (!plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty() |
|
&& plr[p].InvBody[INVLOC_HAND_LEFT]._iClass == ICLASS_WEAPON |
|
&& plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) { |
|
g = plr[p].InvBody[INVLOC_HAND_LEFT]._itype; |
|
} |
|
|
|
if (!plr[p].InvBody[INVLOC_HAND_RIGHT].isEmpty() |
|
&& plr[p].InvBody[INVLOC_HAND_RIGHT]._iClass == ICLASS_WEAPON |
|
&& plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) { |
|
g = plr[p].InvBody[INVLOC_HAND_RIGHT]._itype; |
|
} |
|
|
|
switch (g) { |
|
case ITYPE_SWORD: |
|
g = ANIM_ID_SWORD; |
|
break; |
|
case ITYPE_AXE: |
|
g = ANIM_ID_AXE; |
|
break; |
|
case ITYPE_BOW: |
|
plr[p]._pwtype = WT_RANGED; |
|
g = ANIM_ID_BOW; |
|
break; |
|
case ITYPE_MACE: |
|
g = ANIM_ID_MACE; |
|
break; |
|
case ITYPE_STAFF: |
|
g = ANIM_ID_STAFF; |
|
break; |
|
} |
|
|
|
if (plr[p].InvBody[INVLOC_HAND_LEFT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag) { |
|
plr[p]._pBlockFlag = true; |
|
g++; |
|
} |
|
if (plr[p].InvBody[INVLOC_HAND_RIGHT]._itype == ITYPE_SHIELD && plr[p].InvBody[INVLOC_HAND_RIGHT]._iStatFlag) { |
|
plr[p]._pBlockFlag = true; |
|
g++; |
|
} |
|
|
|
if (plr[p].InvBody[INVLOC_CHEST]._itype == ITYPE_HARMOR && plr[p].InvBody[INVLOC_CHEST]._iStatFlag) { |
|
if (plr[p]._pClass == HeroClass::Monk && plr[p].InvBody[INVLOC_CHEST]._iMagical == ITEM_QUALITY_UNIQUE) |
|
plr[p]._pIAC += plr[p]._pLevel / 2; |
|
g += ANIM_ID_HEAVY_ARMOR; |
|
} else if (plr[p].InvBody[INVLOC_CHEST]._itype == ITYPE_MARMOR && plr[p].InvBody[INVLOC_CHEST]._iStatFlag) { |
|
if (plr[p]._pClass == HeroClass::Monk) { |
|
if (plr[p].InvBody[INVLOC_CHEST]._iMagical == ITEM_QUALITY_UNIQUE) |
|
plr[p]._pIAC += plr[p]._pLevel * 2; |
|
else |
|
plr[p]._pIAC += plr[p]._pLevel / 2; |
|
} |
|
g += ANIM_ID_MEDIUM_ARMOR; |
|
} else if (plr[p]._pClass == HeroClass::Monk) { |
|
plr[p]._pIAC += plr[p]._pLevel * 2; |
|
} |
|
|
|
if (plr[p]._pgfxnum != g && Loadgfx) { |
|
plr[p]._pgfxnum = g; |
|
plr[p]._pGFXLoad = 0; |
|
LoadPlrGFX(p, PFILE_STAND); |
|
SetPlrAnims(p); |
|
|
|
d = plr[p]._pdir; |
|
|
|
assert(plr[p]._pNAnim[d]); |
|
NewPlrAnim(p, plr[p]._pNAnim[d], plr[p]._pNFrames, 3, plr[p]._pNWidth); |
|
} else { |
|
plr[p]._pgfxnum = g; |
|
} |
|
|
|
if (plr[p].InvBody[INVLOC_AMULET].isEmpty() || plr[p].InvBody[INVLOC_AMULET].IDidx != IDI_AURIC) { |
|
int half = MaxGold; |
|
MaxGold = GOLD_MAX_LIMIT; |
|
|
|
if (half != MaxGold) |
|
StripTopGold(p); |
|
} else { |
|
MaxGold = GOLD_MAX_LIMIT * 2; |
|
} |
|
|
|
drawmanaflag = true; |
|
drawhpflag = true; |
|
} |
|
|
|
void CalcPlrScrolls(int p) |
|
{ |
|
int i, j; |
|
|
|
plr[p]._pScrlSpells = 0; |
|
for (i = 0; i < plr[p]._pNumInv; i++) { |
|
if (!plr[p].InvList[i].isEmpty() && (plr[p].InvList[i]._iMiscId == IMISC_SCROLL || plr[p].InvList[i]._iMiscId == IMISC_SCROLLT)) { |
|
if (plr[p].InvList[i]._iStatFlag) |
|
plr[p]._pScrlSpells |= GetSpellBitmask(plr[p].InvList[i]._iSpell); |
|
} |
|
} |
|
|
|
for (j = 0; j < MAXBELTITEMS; j++) { |
|
if (!plr[p].SpdList[j].isEmpty() && (plr[p].SpdList[j]._iMiscId == IMISC_SCROLL || plr[p].SpdList[j]._iMiscId == IMISC_SCROLLT)) { |
|
if (plr[p].SpdList[j]._iStatFlag) |
|
plr[p]._pScrlSpells |= GetSpellBitmask(plr[p].SpdList[j]._iSpell); |
|
} |
|
} |
|
EnsureValidReadiedSpell(plr[p]); |
|
} |
|
|
|
void CalcPlrStaff(int p) |
|
{ |
|
plr[p]._pISpells = 0; |
|
if (!plr[p].InvBody[INVLOC_HAND_LEFT].isEmpty() |
|
&& plr[p].InvBody[INVLOC_HAND_LEFT]._iStatFlag |
|
&& plr[p].InvBody[INVLOC_HAND_LEFT]._iCharges > 0) { |
|
plr[p]._pISpells |= GetSpellBitmask(plr[p].InvBody[INVLOC_HAND_LEFT]._iSpell); |
|
} |
|
} |
|
|
|
void CalcSelfItems(int pnum) |
|
{ |
|
int i; |
|
PlayerStruct *p; |
|
ItemStruct *pi; |
|
bool sf, changeflag; |
|
int sa, ma, da; |
|
|
|
p = &plr[pnum]; |
|
|
|
sa = 0; |
|
ma = 0; |
|
da = 0; |
|
pi = p->InvBody; |
|
for (i = 0; i < NUM_INVLOC; i++, pi++) { |
|
if (!pi->isEmpty()) { |
|
pi->_iStatFlag = true; |
|
if (pi->_iIdentified) { |
|
sa += pi->_iPLStr; |
|
ma += pi->_iPLMag; |
|
da += pi->_iPLDex; |
|
} |
|
} |
|
} |
|
do { |
|
changeflag = false; |
|
pi = p->InvBody; |
|
for (i = 0; i < NUM_INVLOC; i++, pi++) { |
|
if (!pi->isEmpty() && pi->_iStatFlag) { |
|
sf = true; |
|
if (sa + p->_pBaseStr < pi->_iMinStr) |
|
sf = false; |
|
if (ma + p->_pBaseMag < pi->_iMinMag) |
|
sf = false; |
|
if (da + p->_pBaseDex < pi->_iMinDex) |
|
sf = false; |
|
if (!sf) { |
|
changeflag = true; |
|
pi->_iStatFlag = false; |
|
if (pi->_iIdentified) { |
|
sa -= pi->_iPLStr; |
|
ma -= pi->_iPLMag; |
|
da -= pi->_iPLDex; |
|
} |
|
} |
|
} |
|
} |
|
} while (changeflag); |
|
} |
|
|
|
static bool ItemMinStats(PlayerStruct *p, ItemStruct *x) |
|
{ |
|
if (p->_pMagic < x->_iMinMag) |
|
return false; |
|
|
|
if (p->_pStrength < x->_iMinStr) |
|
return false; |
|
|
|
if (p->_pDexterity < x->_iMinDex) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
void CalcPlrItemMin(int pnum) |
|
{ |
|
PlayerStruct *p; |
|
ItemStruct *pi; |
|
int i; |
|
|
|
p = &plr[pnum]; |
|
pi = p->InvList; |
|
i = p->_pNumInv; |
|
|
|
while (i--) { |
|
pi->_iStatFlag = ItemMinStats(p, pi); |
|
pi++; |
|
} |
|
|
|
pi = p->SpdList; |
|
for (i = MAXBELTITEMS; i != 0; i--) { |
|
if (!pi->isEmpty()) { |
|
pi->_iStatFlag = ItemMinStats(p, pi); |
|
} |
|
pi++; |
|
} |
|
} |
|
|
|
void CalcPlrBookVals(int p) |
|
{ |
|
int i, slvl; |
|
|
|
if (currlevel == 0) { |
|
for (i = 1; !witchitem[i].isEmpty(); i++) { |
|
WitchBookLevel(i); |
|
witchitem[i]._iStatFlag = StoreStatOk(&witchitem[i]); |
|
} |
|
} |
|
|
|
for (i = 0; i < plr[p]._pNumInv; i++) { |
|
if (plr[p].InvList[i]._itype == ITYPE_MISC && plr[p].InvList[i]._iMiscId == IMISC_BOOK) { |
|
plr[p].InvList[i]._iMinMag = spelldata[plr[p].InvList[i]._iSpell].sMinInt; |
|
slvl = plr[p]._pSplLvl[plr[p].InvList[i]._iSpell]; |
|
|
|
while (slvl != 0) { |
|
plr[p].InvList[i]._iMinMag += 20 * plr[p].InvList[i]._iMinMag / 100; |
|
slvl--; |
|
if (plr[p].InvList[i]._iMinMag + 20 * plr[p].InvList[i]._iMinMag / 100 > 255) { |
|
plr[p].InvList[i]._iMinMag = 255; |
|
slvl = 0; |
|
} |
|
} |
|
plr[p].InvList[i]._iStatFlag = ItemMinStats(&plr[p], &plr[p].InvList[i]); |
|
} |
|
} |
|
} |
|
|
|
void CalcPlrInv(int p, bool Loadgfx) |
|
{ |
|
CalcPlrItemMin(p); |
|
CalcSelfItems(p); |
|
CalcPlrItemVals(p, Loadgfx); |
|
CalcPlrItemMin(p); |
|
if (p == myplr) { |
|
CalcPlrBookVals(p); |
|
CalcPlrScrolls(p); |
|
CalcPlrStaff(p); |
|
if (p == myplr && currlevel == 0) |
|
RecalcStoreStats(); |
|
} |
|
} |
|
|
|
void SetPlrHandItem(ItemStruct *h, int idata) |
|
{ |
|
ItemDataStruct *pAllItem; |
|
|
|
pAllItem = &AllItemsList[idata]; |
|
|
|
// zero-initialize struct |
|
memset(h, 0, sizeof(*h)); |
|
|
|
h->_itype = pAllItem->itype; |
|
h->_iCurs = pAllItem->iCurs; |
|
strcpy(h->_iName, _(pAllItem->iName)); |
|
strcpy(h->_iIName, _(pAllItem->iName)); |
|
h->_iLoc = pAllItem->iLoc; |
|
h->_iClass = pAllItem->iClass; |
|
h->_iMinDam = pAllItem->iMinDam; |
|
h->_iMaxDam = pAllItem->iMaxDam; |
|
h->_iAC = pAllItem->iMinAC; |
|
h->_iMiscId = pAllItem->iMiscId; |
|
h->_iSpell = pAllItem->iSpell; |
|
|
|
if (pAllItem->iMiscId == IMISC_STAFF) { |
|
h->_iCharges = gbIsHellfire ? 18 : 40; |
|
} |
|
|
|
h->_iMaxCharges = h->_iCharges; |
|
h->_iDurability = pAllItem->iDurability; |
|
h->_iMaxDur = pAllItem->iDurability; |
|
h->_iMinStr = pAllItem->iMinStr; |
|
h->_iMinMag = pAllItem->iMinMag; |
|
h->_iMinDex = pAllItem->iMinDex; |
|
h->_ivalue = pAllItem->iValue; |
|
h->_iIvalue = pAllItem->iValue; |
|
h->_iPrePower = IPL_INVALID; |
|
h->_iSufPower = IPL_INVALID; |
|
h->_iMagical = ITEM_QUALITY_NORMAL; |
|
h->IDidx = idata; |
|
if (gbIsHellfire) |
|
h->dwBuff |= CF_HELLFIRE; |
|
} |
|
|
|
void GetPlrHandSeed(ItemStruct *h) |
|
{ |
|
h->_iSeed = AdvanceRndSeed(); |
|
} |
|
|
|
/** |
|
* @brief Set a new unique seed value on the given item |
|
* @param pnum Player id |
|
* @param h Item to update |
|
*/ |
|
void GetGoldSeed(int pnum, ItemStruct *h) |
|
{ |
|
int i, ii, s; |
|
bool doneflag; |
|
|
|
do { |
|
doneflag = true; |
|
s = AdvanceRndSeed(); |
|
for (i = 0; i < numitems; i++) { |
|
ii = itemactive[i]; |
|
if (items[ii]._iSeed == s) |
|
doneflag = false; |
|
} |
|
if (pnum == myplr) { |
|
for (i = 0; i < plr[pnum]._pNumInv; i++) { |
|
if (plr[pnum].InvList[i]._iSeed == s) |
|
doneflag = false; |
|
} |
|
} |
|
} while (!doneflag); |
|
|
|
h->_iSeed = s; |
|
} |
|
|
|
void SetPlrHandSeed(ItemStruct *h, int iseed) |
|
{ |
|
h->_iSeed = iseed; |
|
} |
|
|
|
int GetGoldCursor(int value) |
|
{ |
|
if (value >= GOLD_MEDIUM_LIMIT) |
|
return ICURS_GOLD_LARGE; |
|
|
|
if (value <= GOLD_SMALL_LIMIT) |
|
return ICURS_GOLD_SMALL; |
|
|
|
return ICURS_GOLD_MEDIUM; |
|
} |
|
|
|
/** |
|
* @brief Update the gold cursor on the given gold item |
|
* @param h The item to update |
|
*/ |
|
void SetPlrHandGoldCurs(ItemStruct *h) |
|
{ |
|
h->_iCurs = GetGoldCursor(h->_ivalue); |
|
} |
|
|
|
void CreatePlrItems(int p) |
|
{ |
|
int i; |
|
ItemStruct *pi = plr[p].InvBody; |
|
|
|
for (i = NUM_INVLOC; i != 0; i--) { |
|
pi->_itype = ITYPE_NONE; |
|
pi++; |
|
} |
|
|
|
// converting this to a for loop creates a `rep stosd` instruction, |
|
// so this probably actually was a memset |
|
memset(&plr[p].InvGrid, 0, sizeof(plr[p].InvGrid)); |
|
|
|
pi = plr[p].InvList; |
|
for (i = NUM_INV_GRID_ELEM; i != 0; i--) { |
|
pi->_itype = ITYPE_NONE; |
|
pi++; |
|
} |
|
|
|
plr[p]._pNumInv = 0; |
|
|
|
pi = &plr[p].SpdList[0]; |
|
for (i = MAXBELTITEMS; i != 0; i--) { |
|
pi->_itype = ITYPE_NONE; |
|
pi++; |
|
} |
|
|
|
switch (plr[p]._pClass) { |
|
case HeroClass::Warrior: |
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_WARRIOR); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]); |
|
|
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_RIGHT], IDI_WARRSHLD); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_RIGHT]); |
|
|
|
#ifdef _DEBUG |
|
if (!debug_mode_key_w) |
|
#endif |
|
{ |
|
SetPlrHandItem(&plr[p].HoldItem, IDI_WARRCLUB); |
|
GetPlrHandSeed(&plr[p].HoldItem); |
|
AutoPlaceItemInInventory(p, plr[p].HoldItem, true); |
|
} |
|
|
|
SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[0]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[1]); |
|
break; |
|
case HeroClass::Rogue: |
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_ROGUE); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[0]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[1]); |
|
break; |
|
case HeroClass::Sorcerer: |
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], gbIsHellfire ? IDI_SORCERER : 166); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[0], gbIsHellfire ? IDI_HEAL : IDI_MANA); |
|
GetPlrHandSeed(&plr[p].SpdList[0]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[1], gbIsHellfire ? IDI_HEAL : IDI_MANA); |
|
GetPlrHandSeed(&plr[p].SpdList[1]); |
|
break; |
|
|
|
case HeroClass::Monk: |
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_SHORTSTAFF); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]); |
|
SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[0]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[1]); |
|
break; |
|
case HeroClass::Bard: |
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], IDI_BARDSWORD); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]); |
|
|
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_RIGHT], IDI_BARDDAGGER); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_RIGHT]); |
|
SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[0]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[1]); |
|
break; |
|
case HeroClass::Barbarian: |
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_LEFT], 139); // TODO: add more enums to items |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_LEFT]); |
|
|
|
SetPlrHandItem(&plr[p].InvBody[INVLOC_HAND_RIGHT], IDI_WARRSHLD); |
|
GetPlrHandSeed(&plr[p].InvBody[INVLOC_HAND_RIGHT]); |
|
SetPlrHandItem(&plr[p].SpdList[0], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[0]); |
|
|
|
SetPlrHandItem(&plr[p].SpdList[1], IDI_HEAL); |
|
GetPlrHandSeed(&plr[p].SpdList[1]); |
|
break; |
|
} |
|
|
|
SetPlrHandItem(&plr[p].HoldItem, IDI_GOLD); |
|
GetPlrHandSeed(&plr[p].HoldItem); |
|
|
|
#ifdef _DEBUG |
|
if (!debug_mode_key_w) { |
|
#endif |
|
plr[p].HoldItem._ivalue = 100; |
|
plr[p].HoldItem._iCurs = ICURS_GOLD_SMALL; |
|
plr[p]._pGold = plr[p].HoldItem._ivalue; |
|
plr[p].InvList[plr[p]._pNumInv++] = plr[p].HoldItem; |
|
plr[p].InvGrid[30] = plr[p]._pNumInv; |
|
#ifdef _DEBUG |
|
} else { |
|
plr[p].HoldItem._ivalue = GOLD_MAX_LIMIT; |
|
plr[p].HoldItem._iCurs = ICURS_GOLD_LARGE; |
|
plr[p]._pGold = plr[p].HoldItem._ivalue * 40; |
|
for (i = 0; i < NUM_INV_GRID_ELEM; i++) { |
|
GetPlrHandSeed(&plr[p].HoldItem); |
|
plr[p].InvList[plr[p]._pNumInv++] = plr[p].HoldItem; |
|
plr[p].InvGrid[i] = plr[p]._pNumInv; |
|
} |
|
} |
|
#endif |
|
|
|
CalcPlrItemVals(p, false); |
|
} |
|
|
|
bool ItemSpaceOk(int i, int j) |
|
{ |
|
int oi; |
|
|
|
// BUGFIX: Check `i + 1 >= MAXDUNX` and `j + 1 >= MAXDUNY` (applied) |
|
if (i < 0 || i + 1 >= MAXDUNX || j < 0 || j + 1 >= MAXDUNY) |
|
return false; |
|
|
|
if (dMonster[i][j] != 0) |
|
return false; |
|
|
|
if (dPlayer[i][j] != 0) |
|
return false; |
|
|
|
if (dItem[i][j] != 0) |
|
return false; |
|
|
|
if (dObject[i][j] != 0) { |
|
oi = dObject[i][j] > 0 ? dObject[i][j] - 1 : -(dObject[i][j] + 1); |
|
if (object[oi]._oSolidFlag) |
|
return false; |
|
} |
|
|
|
if (dObject[i + 1][j + 1] > 0 && object[dObject[i + 1][j + 1] - 1]._oSelFlag != 0) |
|
return false; |
|
|
|
if (dObject[i + 1][j + 1] < 0 && object[-(dObject[i + 1][j + 1] + 1)]._oSelFlag != 0) |
|
return false; |
|
|
|
if (dObject[i + 1][j] > 0 |
|
&& dObject[i][j + 1] > 0 |
|
&& object[dObject[i + 1][j] - 1]._oSelFlag != 0 |
|
&& object[dObject[i][j + 1] - 1]._oSelFlag != 0) { |
|
return false; |
|
} |
|
|
|
return !nSolidTable[dPiece[i][j]]; |
|
} |
|
|
|
static bool GetItemSpace(int x, int y, int8_t inum) |
|
{ |
|
int i, j, rs; |
|
int xx, yy; |
|
bool savail; |
|
|
|
yy = 0; |
|
for (j = y - 1; j <= y + 1; j++) { |
|
xx = 0; |
|
for (i = x - 1; i <= x + 1; i++) { |
|
itemhold[xx][yy] = ItemSpaceOk(i, j); |
|
xx++; |
|
} |
|
yy++; |
|
} |
|
|
|
savail = false; |
|
for (j = 0; j < 3; j++) { |
|
for (i = 0; i < 3; i++) { |
|
if (itemhold[i][j]) |
|
savail = true; |
|
} |
|
} |
|
|
|
rs = GenerateRnd(15) + 1; |
|
|
|
if (!savail) |
|
return false; |
|
|
|
xx = 0; |
|
yy = 0; |
|
while (rs > 0) { |
|
if (itemhold[xx][yy]) |
|
rs--; |
|
if (rs > 0) { |
|
xx++; |
|
if (xx == 3) { |
|
xx = 0; |
|
yy++; |
|
if (yy == 3) |
|
yy = 0; |
|
} |
|
} |
|
} |
|
|
|
xx += x - 1; |
|
yy += y - 1; |
|
items[inum].position = { xx, yy }; |
|
dItem[xx][yy] = inum + 1; |
|
|
|
return true; |
|
} |
|
|
|
int AllocateItem() |
|
{ |
|
int inum = itemavail[0]; |
|
itemavail[0] = itemavail[MAXITEMS - numitems - 1]; |
|
itemactive[numitems] = inum; |
|
numitems++; |
|
|
|
memset(&items[inum], 0, sizeof(*items)); |
|
|
|
return inum; |
|
} |
|
|
|
static void GetSuperItemSpace(int x, int y, int8_t inum) |
|
{ |
|
if (!GetItemSpace(x, y, inum)) { |
|
for (int k = 2; k < 50; k++) { |
|
for (int j = -k; j <= k; j++) { |
|
int yy = y + j; |
|
for (int i = -k; i <= k; i++) { |
|
int xx = i + x; |
|
if (ItemSpaceOk(xx, yy)) { |
|
items[inum].position = { xx, yy }; |
|
dItem[xx][yy] = inum + 1; |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
void GetSuperItemLoc(int x, int y, int *xx, int *yy) |
|
{ |
|
int i, j, k; |
|
|
|
for (k = 1; k < 50; k++) { |
|
for (j = -k; j <= k; j++) { |
|
*yy = y + j; |
|
for (i = -k; i <= k; i++) { |
|
*xx = i + x; |
|
if (ItemSpaceOk(*xx, *yy)) { |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
void CalcItemValue(int i) |
|
{ |
|
int v = items[i]._iVMult1 + items[i]._iVMult2; |
|
if (v > 0) { |
|
v *= items[i]._ivalue; |
|
} |
|
if (v < 0) { |
|
v = items[i]._ivalue / v; |
|
} |
|
v = items[i]._iVAdd1 + items[i]._iVAdd2 + v; |
|
items[i]._iIvalue = std::max(v, 1); |
|
} |
|
|
|
void GetBookSpell(int i, int lvl) |
|
{ |
|
int rv; |
|
|
|
if (lvl == 0) |
|
lvl = 1; |
|
|
|
int maxSpells = gbIsHellfire ? MAX_SPELLS : 37; |
|
|
|
rv = GenerateRnd(maxSpells) + 1; |
|
|
|
if (gbIsSpawn && lvl > 5) |
|
lvl = 5; |
|
|
|
int s = SPL_FIREBOLT; |
|
enum spell_id bs = SPL_FIREBOLT; |
|
while (rv > 0) { |
|
int sLevel = GetSpellBookLevel(static_cast<spell_id>(s)); |
|
if (sLevel != -1 && lvl >= sLevel) { |
|
rv--; |
|
bs = static_cast<spell_id>(s); |
|
} |
|
s++; |
|
if (!gbIsMultiplayer) { |
|
if (s == SPL_RESURRECT) |
|
s = SPL_TELEKINESIS; |
|
} |
|
if (!gbIsMultiplayer) { |
|
if (s == SPL_HEALOTHER) |
|
s = SPL_FLARE; |
|
} |
|
if (s == maxSpells) |
|
s = 1; |
|
} |
|
strcat(items[i]._iName, _(spelldata[bs].sNameText)); |
|
strcat(items[i]._iIName, _(spelldata[bs].sNameText)); |
|
items[i]._iSpell = bs; |
|
items[i]._iMinMag = spelldata[bs].sMinInt; |
|
items[i]._ivalue += spelldata[bs].sBookCost; |
|
items[i]._iIvalue += spelldata[bs].sBookCost; |
|
if (spelldata[bs].sType == STYPE_FIRE) |
|
items[i]._iCurs = ICURS_BOOK_RED; |
|
else if (spelldata[bs].sType == STYPE_LIGHTNING) |
|
items[i]._iCurs = ICURS_BOOK_BLUE; |
|
else if (spelldata[bs].sType == STYPE_MAGIC) |
|
items[i]._iCurs = ICURS_BOOK_GREY; |
|
} |
|
|
|
void GetStaffPower(int i, int lvl, int bs, bool onlygood) |
|
{ |
|
int l[256]; |
|
char istr[128]; |
|
int nl, j, preidx; |
|
bool addok; |
|
int tmp; |
|
|
|
tmp = GenerateRnd(10); |
|
preidx = -1; |
|
if (tmp == 0 || onlygood) { |
|
nl = 0; |
|
for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) { |
|
if (IsPrefixValidForItemType(j, PLT_STAFF) && PL_Prefix[j].PLMinLvl <= lvl) { |
|
addok = true; |
|
if (onlygood && !PL_Prefix[j].PLOk) |
|
addok = false; |
|
if (addok) { |
|
l[nl] = j; |
|
nl++; |
|
if (PL_Prefix[j].PLDouble) { |
|
l[nl] = j; |
|
nl++; |
|
} |
|
} |
|
} |
|
} |
|
if (nl != 0) { |
|
preidx = l[GenerateRnd(nl)]; |
|
sprintf(istr, "%s %s", _(PL_Prefix[preidx].PLName), items[i]._iIName); |
|
strcpy(items[i]._iIName, istr); |
|
items[i]._iMagical = ITEM_QUALITY_MAGIC; |
|
SaveItemPower( |
|
i, |
|
PL_Prefix[preidx].PLPower, |
|
PL_Prefix[preidx].PLParam1, |
|
PL_Prefix[preidx].PLParam2, |
|
PL_Prefix[preidx].PLMinVal, |
|
PL_Prefix[preidx].PLMaxVal, |
|
PL_Prefix[preidx].PLMultVal); |
|
items[i]._iPrePower = PL_Prefix[preidx].PLPower; |
|
} |
|
} |
|
if (!control_WriteStringToBuffer((BYTE *)items[i]._iIName)) { |
|
strcpy(items[i]._iIName, _(AllItemsList[items[i].IDidx].iSName)); |
|
if (preidx != -1) { |
|
sprintf(istr, "%s %s", _(PL_Prefix[preidx].PLName), items[i]._iIName); |
|
strcpy(items[i]._iIName, istr); |
|
} |
|
sprintf(istr, _("%s of %s"), items[i]._iIName, _(spelldata[bs].sNameText)); |
|
strcpy(items[i]._iIName, istr); |
|
if (items[i]._iMagical == ITEM_QUALITY_NORMAL) |
|
strcpy(items[i]._iName, items[i]._iIName); |
|
} |
|
CalcItemValue(i); |
|
} |
|
|
|
void GetStaffSpell(int i, int lvl, bool onlygood) |
|
{ |
|
int l, rv, minc, maxc, v; |
|
char istr[68]; |
|
|
|
if (!gbIsHellfire && GenerateRnd(4) == 0) { |
|
GetItemPower(i, lvl / 2, lvl, PLT_STAFF, onlygood); |
|
} else { |
|
int maxSpells = gbIsHellfire ? MAX_SPELLS : 37; |
|
l = lvl / 2; |
|
if (l == 0) |
|
l = 1; |
|
rv = GenerateRnd(maxSpells) + 1; |
|
|
|
if (gbIsSpawn && lvl > 10) |
|
lvl = 10; |
|
|
|
int s = SPL_FIREBOLT; |
|
enum spell_id bs = SPL_NULL; |
|
while (rv > 0) { |
|
int sLevel = GetSpellStaffLevel(static_cast<spell_id>(s)); |
|
if (sLevel != -1 && l >= sLevel) { |
|
rv--; |
|
bs = static_cast<spell_id>(s); |
|
} |
|
s++; |
|
if (!gbIsMultiplayer && s == SPL_RESURRECT) |
|
s = SPL_TELEKINESIS; |
|
if (!gbIsMultiplayer && s == SPL_HEALOTHER) |
|
s = SPL_FLARE; |
|
if (s == maxSpells) |
|
s = SPL_FIREBOLT; |
|
} |
|
sprintf(istr, _("%s of %s"), items[i]._iName, _(spelldata[bs].sNameText)); |
|
if (!control_WriteStringToBuffer((BYTE *)istr)) |
|
sprintf(istr, _("Staff of %s"), _(spelldata[bs].sNameText)); |
|
strcpy(items[i]._iName, istr); |
|
strcpy(items[i]._iIName, istr); |
|
|
|
minc = spelldata[bs].sStaffMin; |
|
maxc = spelldata[bs].sStaffMax - minc + 1; |
|
items[i]._iSpell = bs; |
|
items[i]._iCharges = minc + GenerateRnd(maxc); |
|
items[i]._iMaxCharges = items[i]._iCharges; |
|
|
|
items[i]._iMinMag = spelldata[bs].sMinInt; |
|
v = items[i]._iCharges * spelldata[bs].sStaffCost / 5; |
|
items[i]._ivalue += v; |
|
items[i]._iIvalue += v; |
|
GetStaffPower(i, lvl, bs, onlygood); |
|
} |
|
} |
|
|
|
void GetOilType(int i, int max_lvl) |
|
{ |
|
int cnt, t, j, r; |
|
char rnd[32]; |
|
|
|
if (!gbIsMultiplayer) { |
|
if (max_lvl == 0) |
|
max_lvl = 1; |
|
cnt = 0; |
|
|
|
for (j = 0; j < (int)(sizeof(OilLevels) / sizeof(OilLevels[0])); j++) { |
|
if (OilLevels[j] <= max_lvl) { |
|
rnd[cnt] = j; |
|
cnt++; |
|
} |
|
} |
|
r = GenerateRnd(cnt); |
|
t = rnd[r]; |
|
} else { |
|
r = GenerateRnd(2); |
|
t = (r != 0 ? 6 : 5); |
|
} |
|
strcpy(items[i]._iName, _(OilNames[t])); |
|
strcpy(items[i]._iIName, _(OilNames[t])); |
|
items[i]._iMiscId = OilMagic[t]; |
|
items[i]._ivalue = OilValues[t]; |
|
items[i]._iIvalue = OilValues[t]; |
|
} |
|
|
|
void GetItemAttrs(int i, int idata, int lvl) |
|
{ |
|
items[i]._itype = AllItemsList[idata].itype; |
|
items[i]._iCurs = AllItemsList[idata].iCurs; |
|
strcpy(items[i]._iName, _(AllItemsList[idata].iName)); |
|
strcpy(items[i]._iIName, _(AllItemsList[idata].iName)); |
|
items[i]._iLoc = AllItemsList[idata].iLoc; |
|
items[i]._iClass = AllItemsList[idata].iClass; |
|
items[i]._iMinDam = AllItemsList[idata].iMinDam; |
|
items[i]._iMaxDam = AllItemsList[idata].iMaxDam; |
|
items[i]._iAC = AllItemsList[idata].iMinAC + GenerateRnd(AllItemsList[idata].iMaxAC - AllItemsList[idata].iMinAC + 1); |
|
items[i]._iFlags = AllItemsList[idata].iFlags; |
|
items[i]._iMiscId = AllItemsList[idata].iMiscId; |
|
items[i]._iSpell = AllItemsList[idata].iSpell; |
|
items[i]._iMagical = ITEM_QUALITY_NORMAL; |
|
items[i]._ivalue = AllItemsList[idata].iValue; |
|
items[i]._iIvalue = AllItemsList[idata].iValue; |
|
items[i]._iDurability = AllItemsList[idata].iDurability; |
|
items[i]._iMaxDur = AllItemsList[idata].iDurability; |
|
items[i]._iMinStr = AllItemsList[idata].iMinStr; |
|
items[i]._iMinMag = AllItemsList[idata].iMinMag; |
|
items[i]._iMinDex = AllItemsList[idata].iMinDex; |
|
items[i].IDidx = idata; |
|
if (gbIsHellfire) |
|
items[i].dwBuff |= CF_HELLFIRE; |
|
items[i]._iPrePower = IPL_INVALID; |
|
items[i]._iSufPower = IPL_INVALID; |
|
|
|
if (items[i]._iMiscId == IMISC_BOOK) |
|
GetBookSpell(i, lvl); |
|
|
|
if (gbIsHellfire && items[i]._iMiscId == IMISC_OILOF) |
|
GetOilType(i, lvl); |
|
|
|
if (items[i]._itype != ITYPE_GOLD) |
|
return; |
|
|
|
int rndv; |
|
int itemlevel = items_get_currlevel(); |
|
switch (sgGameInitInfo.nDifficulty) { |
|
case DIFF_NORMAL: |
|
rndv = 5 * itemlevel + GenerateRnd(10 * itemlevel); |
|
break; |
|
case DIFF_NIGHTMARE: |
|
rndv = 5 * (itemlevel + 16) + GenerateRnd(10 * (itemlevel + 16)); |
|
break; |
|
case DIFF_HELL: |
|
rndv = 5 * (itemlevel + 32) + GenerateRnd(10 * (itemlevel + 32)); |
|
break; |
|
} |
|
if (leveltype == DTYPE_HELL) |
|
rndv += rndv / 8; |
|
|
|
items[i]._ivalue = std::min(rndv, GOLD_MAX_LIMIT); |
|
SetPlrHandGoldCurs(&items[i]); |
|
} |
|
|
|
int RndPL(int param1, int param2) |
|
{ |
|
return param1 + GenerateRnd(param2 - param1 + 1); |
|
} |
|
|
|
int PLVal(int pv, int p1, int p2, int minv, int maxv) |
|
{ |
|
if (p1 == p2) |
|
return minv; |
|
if (minv == maxv) |
|
return minv; |
|
return minv + (maxv - minv) * (100 * (pv - p1) / (p2 - p1)) / 100; |
|
} |
|
|
|
void SaveItemPower(int i, item_effect_type power, int param1, int param2, int minval, int maxval, int multval) |
|
{ |
|
int r, r2; |
|
|
|
r = RndPL(param1, param2); |
|
switch (power) { |
|
case IPL_TOHIT: |
|
items[i]._iPLToHit += r; |
|
break; |
|
case IPL_TOHIT_CURSE: |
|
items[i]._iPLToHit -= r; |
|
break; |
|
case IPL_DAMP: |
|
items[i]._iPLDam += r; |
|
break; |
|
case IPL_DAMP_CURSE: |
|
items[i]._iPLDam -= r; |
|
break; |
|
case IPL_DOPPELGANGER: |
|
items[i]._iDamAcFlags |= 16; |
|
[[fallthrough]]; |
|
case IPL_TOHIT_DAMP: |
|
r = RndPL(param1, param2); |
|
items[i]._iPLDam += r; |
|
if (param1 == 20) |
|
r2 = RndPL(1, 5); |
|
if (param1 == 36) |
|
r2 = RndPL(6, 10); |
|
if (param1 == 51) |
|
r2 = RndPL(11, 15); |
|
if (param1 == 66) |
|
r2 = RndPL(16, 20); |
|
if (param1 == 81) |
|
r2 = RndPL(21, 30); |
|
if (param1 == 96) |
|
r2 = RndPL(31, 40); |
|
if (param1 == 111) |
|
r2 = RndPL(41, 50); |
|
if (param1 == 126) |
|
r2 = RndPL(51, 75); |
|
if (param1 == 151) |
|
r2 = RndPL(76, 100); |
|
items[i]._iPLToHit += r2; |
|
break; |
|
case IPL_TOHIT_DAMP_CURSE: |
|
items[i]._iPLDam -= r; |
|
if (param1 == 25) |
|
r2 = RndPL(1, 5); |
|
if (param1 == 50) |
|
r2 = RndPL(6, 10); |
|
items[i]._iPLToHit -= r2; |
|
break; |
|
case IPL_ACP: |
|
items[i]._iPLAC += r; |
|
break; |
|
case IPL_ACP_CURSE: |
|
items[i]._iPLAC -= r; |
|
break; |
|
case IPL_SETAC: |
|
items[i]._iAC = r; |
|
break; |
|
case IPL_AC_CURSE: |
|
items[i]._iAC -= r; |
|
break; |
|
case IPL_FIRERES: |
|
items[i]._iPLFR += r; |
|
break; |
|
case IPL_LIGHTRES: |
|
items[i]._iPLLR += r; |
|
break; |
|
case IPL_MAGICRES: |
|
items[i]._iPLMR += r; |
|
break; |
|
case IPL_ALLRES: |
|
items[i]._iPLFR = std::max(items[i]._iPLFR + r, 0); |
|
items[i]._iPLLR = std::max(items[i]._iPLLR + r, 0); |
|
items[i]._iPLMR = std::max(items[i]._iPLMR + r, 0); |
|
break; |
|
case IPL_SPLLVLADD: |
|
items[i]._iSplLvlAdd = r; |
|
break; |
|
case IPL_CHARGES: |
|
items[i]._iCharges *= param1; |
|
items[i]._iMaxCharges = items[i]._iCharges; |
|
break; |
|
case IPL_SPELL: |
|
items[i]._iSpell = static_cast<spell_id>(param1); |
|
items[i]._iCharges = param2; |
|
items[i]._iMaxCharges = param2; |
|
break; |
|
case IPL_FIREDAM: |
|
items[i]._iFlags |= ISPL_FIREDAM; |
|
items[i]._iFlags &= ~ISPL_LIGHTDAM; |
|
items[i]._iFMinDam = param1; |
|
items[i]._iFMaxDam = param2; |
|
items[i]._iLMinDam = 0; |
|
items[i]._iLMaxDam = 0; |
|
break; |
|
case IPL_LIGHTDAM: |
|
items[i]._iFlags |= ISPL_LIGHTDAM; |
|
items[i]._iFlags &= ~ISPL_FIREDAM; |
|
items[i]._iLMinDam = param1; |
|
items[i]._iLMaxDam = param2; |
|
items[i]._iFMinDam = 0; |
|
items[i]._iFMaxDam = 0; |
|
break; |
|
case IPL_STR: |
|
items[i]._iPLStr += r; |
|
break; |
|
case IPL_STR_CURSE: |
|
items[i]._iPLStr -= r; |
|
break; |
|
case IPL_MAG: |
|
items[i]._iPLMag += r; |
|
break; |
|
case IPL_MAG_CURSE: |
|
items[i]._iPLMag -= r; |
|
break; |
|
case IPL_DEX: |
|
items[i]._iPLDex += r; |
|
break; |
|
case IPL_DEX_CURSE: |
|
items[i]._iPLDex -= r; |
|
break; |
|
case IPL_VIT: |
|
items[i]._iPLVit += r; |
|
break; |
|
case IPL_VIT_CURSE: |
|
items[i]._iPLVit -= r; |
|
break; |
|
case IPL_ATTRIBS: |
|
items[i]._iPLStr += r; |
|
items[i]._iPLMag += r; |
|
items[i]._iPLDex += r; |
|
items[i]._iPLVit += r; |
|
break; |
|
case IPL_ATTRIBS_CURSE: |
|
items[i]._iPLStr -= r; |
|
items[i]._iPLMag -= r; |
|
items[i]._iPLDex -= r; |
|
items[i]._iPLVit -= r; |
|
break; |
|
case IPL_GETHIT_CURSE: |
|
items[i]._iPLGetHit += r; |
|
break; |
|
case IPL_GETHIT: |
|
items[i]._iPLGetHit -= r; |
|
break; |
|
case IPL_LIFE: |
|
items[i]._iPLHP += r << 6; |
|
break; |
|
case IPL_LIFE_CURSE: |
|
items[i]._iPLHP -= r << 6; |
|
break; |
|
case IPL_MANA: |
|
items[i]._iPLMana += r << 6; |
|
drawmanaflag = true; |
|
break; |
|
case IPL_MANA_CURSE: |
|
items[i]._iPLMana -= r << 6; |
|
drawmanaflag = true; |
|
break; |
|
case IPL_DUR: |
|
r2 = r * items[i]._iMaxDur / 100; |
|
items[i]._iMaxDur += r2; |
|
items[i]._iDurability += r2; |
|
break; |
|
case IPL_CRYSTALLINE: |
|
items[i]._iPLDam += 140 + r * 2; |
|
[[fallthrough]]; |
|
case IPL_DUR_CURSE: |
|
items[i]._iMaxDur -= r * items[i]._iMaxDur / 100; |
|
items[i]._iMaxDur = std::max<uint8_t>(items[i]._iMaxDur, 1); |
|
|
|
items[i]._iDurability = items[i]._iMaxDur; |
|
break; |
|
case IPL_INDESTRUCTIBLE: |
|
items[i]._iDurability = DUR_INDESTRUCTIBLE; |
|
items[i]._iMaxDur = DUR_INDESTRUCTIBLE; |
|
break; |
|
case IPL_LIGHT: |
|
items[i]._iPLLight += param1; |
|
break; |
|
case IPL_LIGHT_CURSE: |
|
items[i]._iPLLight -= param1; |
|
break; |
|
case IPL_MULT_ARROWS: |
|
items[i]._iFlags |= ISPL_MULT_ARROWS; |
|
break; |
|
case IPL_FIRE_ARROWS: |
|
items[i]._iFlags |= ISPL_FIRE_ARROWS; |
|
items[i]._iFlags &= ~ISPL_LIGHT_ARROWS; |
|
items[i]._iFMinDam = param1; |
|
items[i]._iFMaxDam = param2; |
|
items[i]._iLMinDam = 0; |
|
items[i]._iLMaxDam = 0; |
|
break; |
|
case IPL_LIGHT_ARROWS: |
|
items[i]._iFlags |= ISPL_LIGHT_ARROWS; |
|
items[i]._iFlags &= ~ISPL_FIRE_ARROWS; |
|
items[i]._iLMinDam = param1; |
|
items[i]._iLMaxDam = param2; |
|
items[i]._iFMinDam = 0; |
|
items[i]._iFMaxDam = 0; |
|
break; |
|
case IPL_FIREBALL: |
|
items[i]._iFlags |= (ISPL_LIGHT_ARROWS | ISPL_FIRE_ARROWS); |
|
items[i]._iFMinDam = param1; |
|
items[i]._iFMaxDam = param2; |
|
items[i]._iLMinDam = 0; |
|
items[i]._iLMaxDam = 0; |
|
break; |
|
case IPL_THORNS: |
|
items[i]._iFlags |= ISPL_THORNS; |
|
break; |
|
case IPL_NOMANA: |
|
items[i]._iFlags |= ISPL_NOMANA; |
|
drawmanaflag = true; |
|
break; |
|
case IPL_NOHEALPLR: |
|
items[i]._iFlags |= ISPL_NOHEALPLR; |
|
break; |
|
case IPL_ABSHALFTRAP: |
|
items[i]._iFlags |= ISPL_ABSHALFTRAP; |
|
break; |
|
case IPL_KNOCKBACK: |
|
items[i]._iFlags |= ISPL_KNOCKBACK; |
|
break; |
|
case IPL_3XDAMVDEM: |
|
items[i]._iFlags |= ISPL_3XDAMVDEM; |
|
break; |
|
case IPL_ALLRESZERO: |
|
items[i]._iFlags |= ISPL_ALLRESZERO; |
|
break; |
|
case IPL_NOHEALMON: |
|
items[i]._iFlags |= ISPL_NOHEALMON; |
|
break; |
|
case IPL_STEALMANA: |
|
if (param1 == 3) |
|
items[i]._iFlags |= ISPL_STEALMANA_3; |
|
if (param1 == 5) |
|
items[i]._iFlags |= ISPL_STEALMANA_5; |
|
drawmanaflag = true; |
|
break; |
|
case IPL_STEALLIFE: |
|
if (param1 == 3) |
|
items[i]._iFlags |= ISPL_STEALLIFE_3; |
|
if (param1 == 5) |
|
items[i]._iFlags |= ISPL_STEALLIFE_5; |
|
drawhpflag = true; |
|
break; |
|
case IPL_TARGAC: |
|
if (gbIsHellfire) |
|
items[i]._iPLEnAc = param1; |
|
else |
|
items[i]._iPLEnAc += r; |
|
break; |
|
case IPL_FASTATTACK: |
|
if (param1 == 1) |
|
items[i]._iFlags |= ISPL_QUICKATTACK; |
|
if (param1 == 2) |
|
items[i]._iFlags |= ISPL_FASTATTACK; |
|
if (param1 == 3) |
|
items[i]._iFlags |= ISPL_FASTERATTACK; |
|
if (param1 == 4) |
|
items[i]._iFlags |= ISPL_FASTESTATTACK; |
|
break; |
|
case IPL_FASTRECOVER: |
|
if (param1 == 1) |
|
items[i]._iFlags |= ISPL_FASTRECOVER; |
|
if (param1 == 2) |
|
items[i]._iFlags |= ISPL_FASTERRECOVER; |
|
if (param1 == 3) |
|
items[i]._iFlags |= ISPL_FASTESTRECOVER; |
|
break; |
|
case IPL_FASTBLOCK: |
|
items[i]._iFlags |= ISPL_FASTBLOCK; |
|
break; |
|
case IPL_DAMMOD: |
|
items[i]._iPLDamMod += r; |
|
break; |
|
case IPL_RNDARROWVEL: |
|
items[i]._iFlags |= ISPL_RNDARROWVEL; |
|
break; |
|
case IPL_SETDAM: |
|
items[i]._iMinDam = param1; |
|
items[i]._iMaxDam = param2; |
|
break; |
|
case IPL_SETDUR: |
|
items[i]._iDurability = param1; |
|
items[i]._iMaxDur = param1; |
|
break; |
|
case IPL_FASTSWING: |
|
items[i]._iFlags |= ISPL_FASTERATTACK; |
|
break; |
|
case IPL_ONEHAND: |
|
items[i]._iLoc = ILOC_ONEHAND; |
|
break; |
|
case IPL_DRAINLIFE: |
|
items[i]._iFlags |= ISPL_DRAINLIFE; |
|
break; |
|
case IPL_RNDSTEALLIFE: |
|
items[i]._iFlags |= ISPL_RNDSTEALLIFE; |
|
break; |
|
case IPL_INFRAVISION: |
|
items[i]._iFlags |= ISPL_INFRAVISION; |
|
break; |
|
case IPL_NOMINSTR: |
|
items[i]._iMinStr = 0; |
|
break; |
|
case IPL_INVCURS: |
|
items[i]._iCurs = param1; |
|
break; |
|
case IPL_ADDACLIFE: |
|
items[i]._iFlags |= (ISPL_LIGHT_ARROWS | ISPL_FIRE_ARROWS); |
|
items[i]._iFMinDam = param1; |
|
items[i]._iFMaxDam = param2; |
|
items[i]._iLMinDam = 1; |
|
items[i]._iLMaxDam = 0; |
|
break; |
|
case IPL_ADDMANAAC: |
|
items[i]._iFlags |= (ISPL_LIGHTDAM | ISPL_FIREDAM); |
|
items[i]._iFMinDam = param1; |
|
items[i]._iFMaxDam = param2; |
|
items[i]._iLMinDam = 2; |
|
items[i]._iLMaxDam = 0; |
|
break; |
|
case IPL_FIRERESCLVL: |
|
items[i]._iPLFR = 30 - plr[myplr]._pLevel; |
|
items[i]._iPLFR = std::max<int16_t>(items[i]._iPLFR, 0); |
|
|
|
break; |
|
case IPL_FIRERES_CURSE: |
|
items[i]._iPLFR -= r; |
|
break; |
|
case IPL_LIGHTRES_CURSE: |
|
items[i]._iPLLR -= r; |
|
break; |
|
case IPL_MAGICRES_CURSE: |
|
items[i]._iPLMR -= r; |
|
break; |
|
case IPL_ALLRES_CURSE: |
|
items[i]._iPLFR -= r; |
|
items[i]._iPLLR -= r; |
|
items[i]._iPLMR -= r; |
|
break; |
|
case IPL_DEVASTATION: |
|
items[i]._iDamAcFlags |= 0x01; |
|
break; |
|
case IPL_DECAY: |
|
items[i]._iDamAcFlags |= 0x02; |
|
items[i]._iPLDam += r; |
|
break; |
|
case IPL_PERIL: |
|
items[i]._iDamAcFlags |= 0x04; |
|
break; |
|
case IPL_JESTERS: |
|
items[i]._iDamAcFlags |= 0x08; |
|
break; |
|
case IPL_ACDEMON: |
|
items[i]._iDamAcFlags |= 0x20; |
|
break; |
|
case IPL_ACUNDEAD: |
|
items[i]._iDamAcFlags |= 0x40; |
|
break; |
|
case IPL_MANATOLIFE: |
|
r2 = ((plr[myplr]._pMaxManaBase >> 6) * 50 / 100); |
|
items[i]._iPLMana -= (r2 << 6); |
|
items[i]._iPLHP += (r2 << 6); |
|
break; |
|
case IPL_LIFETOMANA: |
|
r2 = ((plr[myplr]._pMaxHPBase >> 6) * 40 / 100); |
|
items[i]._iPLHP -= (r2 << 6); |
|
items[i]._iPLMana += (r2 << 6); |
|
break; |
|
default: |
|
break; |
|
} |
|
if (items[i]._iVAdd1 || items[i]._iVMult1) { |
|
items[i]._iVAdd2 = PLVal(r, param1, param2, minval, maxval); |
|
items[i]._iVMult2 = multval; |
|
} else { |
|
items[i]._iVAdd1 = PLVal(r, param1, param2, minval, maxval); |
|
items[i]._iVMult1 = multval; |
|
} |
|
} |
|
|
|
static void SaveItemSuffix(int i, int sufidx) |
|
{ |
|
int param1 = PL_Suffix[sufidx].PLParam1; |
|
int param2 = PL_Suffix[sufidx].PLParam2; |
|
|
|
if (!gbIsHellfire) { |
|
if (sufidx >= 84 && sufidx <= 86) { |
|
param1 = 2 << param1; |
|
param2 = 6 << param2; |
|
} |
|
} |
|
|
|
SaveItemPower( |
|
i, |
|
PL_Suffix[sufidx].PLPower, |
|
param1, |
|
param2, |
|
PL_Suffix[sufidx].PLMinVal, |
|
PL_Suffix[sufidx].PLMaxVal, |
|
PL_Suffix[sufidx].PLMultVal); |
|
} |
|
|
|
void GetItemPower(int i, int minlvl, int maxlvl, affix_item_type flgs, bool onlygood) |
|
{ |
|
int pre, post, nt, nl, j, preidx, sufidx; |
|
int l[256]; |
|
char istr[128]; |
|
goodorevil goe; |
|
|
|
pre = GenerateRnd(4); |
|
post = GenerateRnd(3); |
|
if (pre != 0 && post == 0) { |
|
if (GenerateRnd(2) != 0) |
|
post = 1; |
|
else |
|
pre = 0; |
|
} |
|
preidx = -1; |
|
sufidx = -1; |
|
goe = GOE_ANY; |
|
if (!onlygood && GenerateRnd(3) != 0) |
|
onlygood = true; |
|
if (pre == 0) { |
|
nt = 0; |
|
for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) { |
|
if (IsPrefixValidForItemType(j, flgs)) { |
|
if (PL_Prefix[j].PLMinLvl >= minlvl && PL_Prefix[j].PLMinLvl <= maxlvl && (!onlygood || PL_Prefix[j].PLOk) && (flgs != PLT_STAFF || PL_Prefix[j].PLPower != IPL_CHARGES)) { |
|
l[nt] = j; |
|
nt++; |
|
if (PL_Prefix[j].PLDouble) { |
|
l[nt] = j; |
|
nt++; |
|
} |
|
} |
|
} |
|
} |
|
if (nt != 0) { |
|
preidx = l[GenerateRnd(nt)]; |
|
sprintf(istr, "%s %s", _(PL_Prefix[preidx].PLName), items[i]._iIName); |
|
strcpy(items[i]._iIName, istr); |
|
items[i]._iMagical = ITEM_QUALITY_MAGIC; |
|
SaveItemPower( |
|
i, |
|
PL_Prefix[preidx].PLPower, |
|
PL_Prefix[preidx].PLParam1, |
|
PL_Prefix[preidx].PLParam2, |
|
PL_Prefix[preidx].PLMinVal, |
|
PL_Prefix[preidx].PLMaxVal, |
|
PL_Prefix[preidx].PLMultVal); |
|
items[i]._iPrePower = PL_Prefix[preidx].PLPower; |
|
goe = PL_Prefix[preidx].PLGOE; |
|
} |
|
} |
|
if (post != 0) { |
|
nl = 0; |
|
for (j = 0; PL_Suffix[j].PLPower != IPL_INVALID; j++) { |
|
if (IsSuffixValidForItemType(j, flgs) |
|
&& PL_Suffix[j].PLMinLvl >= minlvl && PL_Suffix[j].PLMinLvl <= maxlvl |
|
&& !((goe == GOE_GOOD && PL_Suffix[j].PLGOE == GOE_EVIL) || (goe == GOE_EVIL && PL_Suffix[j].PLGOE == GOE_GOOD)) |
|
&& (!onlygood || PL_Suffix[j].PLOk)) { |
|
l[nl] = j; |
|
nl++; |
|
} |
|
} |
|
if (nl != 0) { |
|
sufidx = l[GenerateRnd(nl)]; |
|
sprintf(istr, _("%s of %s"), items[i]._iIName, _(PL_Suffix[sufidx].PLName)); |
|
strcpy(items[i]._iIName, istr); |
|
items[i]._iMagical = ITEM_QUALITY_MAGIC; |
|
SaveItemSuffix(i, sufidx); |
|
items[i]._iSufPower = PL_Suffix[sufidx].PLPower; |
|
} |
|
} |
|
if (!control_WriteStringToBuffer((BYTE *)items[i]._iIName)) { |
|
int aii = items[i].IDidx; |
|
if (AllItemsList[aii].iSName != nullptr) |
|
strcpy(items[i]._iIName, _(AllItemsList[aii].iSName)); |
|
else |
|
items[i]._iName[0] = 0; |
|
|
|
if (preidx != -1) { |
|
sprintf(istr, "%s %s", _(PL_Prefix[preidx].PLName), items[i]._iIName); |
|
strcpy(items[i]._iIName, istr); |
|
} |
|
if (sufidx != -1) { |
|
sprintf(istr, _("%s of %s"), items[i]._iIName, _(PL_Suffix[sufidx].PLName)); |
|
strcpy(items[i]._iIName, istr); |
|
} |
|
} |
|
if (preidx != -1 || sufidx != -1) |
|
CalcItemValue(i); |
|
} |
|
|
|
void GetItemBonus(int i, int minlvl, int maxlvl, bool onlygood, bool allowspells) |
|
{ |
|
if (minlvl > 25) |
|
minlvl = 25; |
|
|
|
switch (items[i]._itype) { |
|
case ITYPE_SWORD: |
|
case ITYPE_AXE: |
|
case ITYPE_MACE: |
|
GetItemPower(i, minlvl, maxlvl, PLT_WEAP, onlygood); |
|
break; |
|
case ITYPE_BOW: |
|
GetItemPower(i, minlvl, maxlvl, PLT_BOW, onlygood); |
|
break; |
|
case ITYPE_SHIELD: |
|
GetItemPower(i, minlvl, maxlvl, PLT_SHLD, onlygood); |
|
break; |
|
case ITYPE_LARMOR: |
|
case ITYPE_HELM: |
|
case ITYPE_MARMOR: |
|
case ITYPE_HARMOR: |
|
GetItemPower(i, minlvl, maxlvl, PLT_ARMO, onlygood); |
|
break; |
|
case ITYPE_STAFF: |
|
if (allowspells) |
|
GetStaffSpell(i, maxlvl, onlygood); |
|
else |
|
GetItemPower(i, minlvl, maxlvl, PLT_STAFF, onlygood); |
|
break; |
|
case ITYPE_RING: |
|
case ITYPE_AMULET: |
|
GetItemPower(i, minlvl, maxlvl, PLT_MISC, onlygood); |
|
break; |
|
case ITYPE_NONE: |
|
case ITYPE_MISC: |
|
case ITYPE_GOLD: |
|
break; |
|
} |
|
} |
|
|
|
void SetupItem(int i) |
|
{ |
|
int it; |
|
|
|
it = ItemCAnimTbl[items[i]._iCurs]; |
|
items[i]._iAnimData = &*itemanims[it]; |
|
items[i]._iAnimLen = ItemAnimLs[it]; |
|
items[i]._iIdentified = false; |
|
items[i]._iPostDraw = false; |
|
|
|
if (!plr[myplr].pLvlLoad) { |
|
items[i]._iAnimFrame = 1; |
|
items[i]._iAnimFlag = true; |
|
items[i]._iSelFlag = 0; |
|
} else { |
|
items[i]._iAnimFrame = items[i]._iAnimLen; |
|
items[i]._iAnimFlag = false; |
|
items[i]._iSelFlag = 1; |
|
} |
|
} |
|
|
|
int RndItem(int m) |
|
{ |
|
int i, ri, r; |
|
int ril[512]; |
|
|
|
if ((monster[m].MData->mTreasure & 0x8000) != 0) |
|
return -((monster[m].MData->mTreasure & 0xFFF) + 1); |
|
|
|
if ((monster[m].MData->mTreasure & 0x4000) != 0) |
|
return 0; |
|
|
|
if (GenerateRnd(100) > 40) |
|
return 0; |
|
|
|
if (GenerateRnd(100) > 25) |
|
return IDI_GOLD + 1; |
|
|
|
ri = 0; |
|
for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
if (AllItemsList[i].iRnd == IDROP_DOUBLE && monster[m].mLevel >= AllItemsList[i].iMinMLvl |
|
&& ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
if (AllItemsList[i].iRnd != IDROP_NEVER && monster[m].mLevel >= AllItemsList[i].iMinMLvl |
|
&& ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer) |
|
ri--; |
|
if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer) |
|
ri--; |
|
} |
|
|
|
r = GenerateRnd(ri); |
|
return ril[r] + 1; |
|
} |
|
|
|
int RndUItem(int m) |
|
{ |
|
int i, ri; |
|
int ril[512]; |
|
bool okflag; |
|
|
|
if (m != -1 && (monster[m].MData->mTreasure & 0x8000) != 0 && !gbIsMultiplayer) |
|
return -((monster[m].MData->mTreasure & 0xFFF) + 1); |
|
|
|
int curlv = items_get_currlevel(); |
|
ri = 0; |
|
for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
okflag = true; |
|
if (AllItemsList[i].iRnd == IDROP_NEVER) |
|
okflag = false; |
|
if (m != -1) { |
|
if (monster[m].mLevel < AllItemsList[i].iMinMLvl) |
|
okflag = false; |
|
} else { |
|
if (2 * curlv < AllItemsList[i].iMinMLvl) |
|
okflag = false; |
|
} |
|
if (AllItemsList[i].itype == ITYPE_MISC) |
|
okflag = false; |
|
if (AllItemsList[i].itype == ITYPE_GOLD) |
|
okflag = false; |
|
if (AllItemsList[i].iMiscId == IMISC_BOOK) |
|
okflag = true; |
|
if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer) |
|
okflag = false; |
|
if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer) |
|
okflag = false; |
|
if (okflag && ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
} |
|
|
|
return ril[GenerateRnd(ri)]; |
|
} |
|
|
|
int RndAllItems() |
|
{ |
|
int i, ri; |
|
int ril[512]; |
|
|
|
if (GenerateRnd(100) > 25) |
|
return 0; |
|
|
|
int curlv = items_get_currlevel(); |
|
ri = 0; |
|
for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
if (AllItemsList[i].iRnd != IDROP_NEVER && 2 * curlv >= AllItemsList[i].iMinMLvl && ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer) |
|
ri--; |
|
if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer) |
|
ri--; |
|
} |
|
|
|
return ril[GenerateRnd(ri)]; |
|
} |
|
|
|
int RndTypeItems(int itype, int imid, int lvl) |
|
{ |
|
int i, ri; |
|
bool okflag; |
|
int ril[512]; |
|
|
|
ri = 0; |
|
for (i = 0; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
okflag = true; |
|
if (AllItemsList[i].iRnd == IDROP_NEVER) |
|
okflag = false; |
|
if (lvl * 2 < AllItemsList[i].iMinMLvl) |
|
okflag = false; |
|
if (AllItemsList[i].itype != itype) |
|
okflag = false; |
|
if (imid != -1 && AllItemsList[i].iMiscId != imid) |
|
okflag = false; |
|
if (okflag && ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
} |
|
|
|
return ril[GenerateRnd(ri)]; |
|
} |
|
|
|
_unique_items CheckUnique(int i, int lvl, int uper, bool recreate) |
|
{ |
|
int j, numu; |
|
bool uok[128]; |
|
|
|
if (GenerateRnd(100) > uper) |
|
return UITEM_INVALID; |
|
|
|
numu = 0; |
|
memset(uok, 0, sizeof(uok)); |
|
for (j = 0; UniqueItemList[j].UIItemId != UITYPE_INVALID; j++) { |
|
if (!IsUniqueAvailable(j)) |
|
break; |
|
if (UniqueItemList[j].UIItemId == AllItemsList[items[i].IDidx].iItemId |
|
&& lvl >= UniqueItemList[j].UIMinLvl |
|
&& (recreate || !UniqueItemFlags[j] || gbIsMultiplayer)) { |
|
uok[j] = true; |
|
numu++; |
|
} |
|
} |
|
|
|
if (numu == 0) |
|
return UITEM_INVALID; |
|
|
|
GenerateRnd(10); /// BUGFIX: unused, last unique in array always gets chosen |
|
uint8_t idata = 0; |
|
while (numu > 0) { |
|
if (uok[idata]) |
|
numu--; |
|
if (numu > 0) { |
|
idata++; |
|
if (idata == 128) |
|
idata = 0; |
|
} |
|
} |
|
|
|
return (_unique_items)idata; |
|
} |
|
|
|
void GetUniqueItem(int i, _unique_items uid) |
|
{ |
|
UniqueItemFlags[uid] = true; |
|
SaveItemPower(i, UniqueItemList[uid].UIPower1, UniqueItemList[uid].UIParam1, UniqueItemList[uid].UIParam2, 0, 0, 1); |
|
|
|
if (UniqueItemList[uid].UINumPL > 1) |
|
SaveItemPower(i, UniqueItemList[uid].UIPower2, UniqueItemList[uid].UIParam3, UniqueItemList[uid].UIParam4, 0, 0, 1); |
|
if (UniqueItemList[uid].UINumPL > 2) |
|
SaveItemPower(i, UniqueItemList[uid].UIPower3, UniqueItemList[uid].UIParam5, UniqueItemList[uid].UIParam6, 0, 0, 1); |
|
if (UniqueItemList[uid].UINumPL > 3) |
|
SaveItemPower(i, UniqueItemList[uid].UIPower4, UniqueItemList[uid].UIParam7, UniqueItemList[uid].UIParam8, 0, 0, 1); |
|
if (UniqueItemList[uid].UINumPL > 4) |
|
SaveItemPower(i, UniqueItemList[uid].UIPower5, UniqueItemList[uid].UIParam9, UniqueItemList[uid].UIParam10, 0, 0, 1); |
|
if (UniqueItemList[uid].UINumPL > 5) |
|
SaveItemPower(i, UniqueItemList[uid].UIPower6, UniqueItemList[uid].UIParam11, UniqueItemList[uid].UIParam12, 0, 0, 1); |
|
|
|
strcpy(items[i]._iIName, _(UniqueItemList[uid].UIName)); |
|
items[i]._iIvalue = UniqueItemList[uid].UIValue; |
|
|
|
if (items[i]._iMiscId == IMISC_UNIQUE) |
|
items[i]._iSeed = uid; |
|
|
|
items[i]._iUid = uid; |
|
items[i]._iMagical = ITEM_QUALITY_UNIQUE; |
|
items[i]._iCreateInfo |= CF_UNIQUE; |
|
} |
|
|
|
void SpawnUnique(_unique_items uid, int x, int y) |
|
{ |
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
GetSuperItemSpace(x, y, ii); |
|
int curlv = items_get_currlevel(); |
|
|
|
int idx = 0; |
|
while (AllItemsList[idx].iItemId != UniqueItemList[uid].UIItemId) |
|
idx++; |
|
|
|
GetItemAttrs(ii, idx, curlv); |
|
GetUniqueItem(ii, uid); |
|
SetupItem(ii); |
|
|
|
return; |
|
} |
|
|
|
void ItemRndDur(int ii) |
|
{ |
|
if (items[ii]._iDurability && items[ii]._iDurability != DUR_INDESTRUCTIBLE) |
|
items[ii]._iDurability = GenerateRnd(items[ii]._iMaxDur / 2) + (items[ii]._iMaxDur / 4) + 1; |
|
} |
|
|
|
void SetupAllItems(int ii, int idx, int iseed, int lvl, int uper, bool onlygood, bool recreate, bool pregen) |
|
{ |
|
int iblvl; |
|
|
|
items[ii]._iSeed = iseed; |
|
SetRndSeed(iseed); |
|
GetItemAttrs(ii, idx, lvl / 2); |
|
items[ii]._iCreateInfo = lvl; |
|
|
|
if (pregen) |
|
items[ii]._iCreateInfo |= CF_PREGEN; |
|
if (onlygood) |
|
items[ii]._iCreateInfo |= CF_ONLYGOOD; |
|
|
|
if (uper == 15) |
|
items[ii]._iCreateInfo |= CF_UPER15; |
|
else if (uper == 1) |
|
items[ii]._iCreateInfo |= CF_UPER1; |
|
|
|
if (items[ii]._iMiscId != IMISC_UNIQUE) { |
|
iblvl = -1; |
|
if (GenerateRnd(100) <= 10 || GenerateRnd(100) <= lvl) { |
|
iblvl = lvl; |
|
} |
|
if (iblvl == -1 && items[ii]._iMiscId == IMISC_STAFF) { |
|
iblvl = lvl; |
|
} |
|
if (iblvl == -1 && items[ii]._iMiscId == IMISC_RING) { |
|
iblvl = lvl; |
|
} |
|
if (iblvl == -1 && items[ii]._iMiscId == IMISC_AMULET) { |
|
iblvl = lvl; |
|
} |
|
if (onlygood) |
|
iblvl = lvl; |
|
if (uper == 15) |
|
iblvl = lvl + 4; |
|
if (iblvl != -1) { |
|
_unique_items uid = CheckUnique(ii, iblvl, uper, recreate); |
|
if (uid == UITEM_INVALID) { |
|
GetItemBonus(ii, iblvl / 2, iblvl, onlygood, true); |
|
} else { |
|
GetUniqueItem(ii, uid); |
|
} |
|
} |
|
if (items[ii]._iMagical != ITEM_QUALITY_UNIQUE) |
|
ItemRndDur(ii); |
|
} else { |
|
if (items[ii]._iLoc != ILOC_UNEQUIPABLE) { |
|
GetUniqueItem(ii, (_unique_items)iseed); // uid is stored in iseed for uniques |
|
} |
|
} |
|
SetupItem(ii); |
|
} |
|
|
|
void SpawnItem(int m, int x, int y, bool sendmsg) |
|
{ |
|
int idx; |
|
bool onlygood = true; |
|
|
|
if (monster[m]._uniqtype || ((monster[m].MData->mTreasure & 0x8000) && gbIsMultiplayer)) { |
|
idx = RndUItem(m); |
|
if (idx < 0) { |
|
SpawnUnique((_unique_items) - (idx + 1), x, y); |
|
return; |
|
} |
|
onlygood = true; |
|
} else if (quests[Q_MUSHROOM]._qactive != QUEST_ACTIVE || quests[Q_MUSHROOM]._qvar1 != QS_MUSHGIVEN) { |
|
idx = RndItem(m); |
|
if (!idx) |
|
return; |
|
if (idx > 0) { |
|
idx--; |
|
onlygood = false; |
|
} else { |
|
SpawnUnique((_unique_items) - (idx + 1), x, y); |
|
return; |
|
} |
|
} else { |
|
idx = IDI_BRAIN; |
|
quests[Q_MUSHROOM]._qvar1 = QS_BRAINSPAWNED; |
|
} |
|
|
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
GetSuperItemSpace(x, y, ii); |
|
int uper = monster[m]._uniqtype ? 15 : 1; |
|
|
|
int mLevel = monster[m].MData->mLevel; |
|
if (!gbIsHellfire && monster[m].MType->mtype == MT_DIABLO) |
|
mLevel -= 15; |
|
|
|
SetupAllItems(ii, idx, AdvanceRndSeed(), mLevel, uper, onlygood, false, false); |
|
|
|
if (sendmsg) |
|
NetSendCmdDItem(false, ii); |
|
} |
|
|
|
static void SetupBaseItem(int x, int y, int idx, bool onlygood, bool sendmsg, bool delta) |
|
{ |
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
GetSuperItemSpace(x, y, ii); |
|
int curlv = items_get_currlevel(); |
|
|
|
SetupAllItems(ii, idx, AdvanceRndSeed(), 2 * curlv, 1, onlygood, false, delta); |
|
|
|
if (sendmsg) |
|
NetSendCmdDItem(false, ii); |
|
if (delta) |
|
DeltaAddItem(ii); |
|
} |
|
|
|
void CreateRndItem(int x, int y, bool onlygood, bool sendmsg, bool delta) |
|
{ |
|
int idx = onlygood ? RndUItem(-1) : RndAllItems(); |
|
|
|
SetupBaseItem(x, y, idx, onlygood, sendmsg, delta); |
|
} |
|
|
|
void SetupAllUseful(int ii, int iseed, int lvl) |
|
{ |
|
int idx; |
|
|
|
items[ii]._iSeed = iseed; |
|
SetRndSeed(iseed); |
|
|
|
if (gbIsHellfire) { |
|
idx = GenerateRnd(7); |
|
switch (idx) { |
|
case 0: |
|
idx = IDI_PORTAL; |
|
if ((lvl <= 1)) |
|
idx = IDI_HEAL; |
|
break; |
|
case 1: |
|
case 2: |
|
idx = IDI_HEAL; |
|
break; |
|
case 3: |
|
idx = IDI_PORTAL; |
|
if ((lvl <= 1)) |
|
idx = IDI_MANA; |
|
break; |
|
case 4: |
|
case 5: |
|
idx = IDI_MANA; |
|
break; |
|
default: |
|
idx = IDI_OIL; |
|
break; |
|
} |
|
} else { |
|
if (GenerateRnd(2) != 0) |
|
idx = IDI_HEAL; |
|
else |
|
idx = IDI_MANA; |
|
|
|
if (lvl > 1 && GenerateRnd(3) == 0) |
|
idx = IDI_PORTAL; |
|
} |
|
|
|
GetItemAttrs(ii, idx, lvl); |
|
items[ii]._iCreateInfo = lvl | CF_USEFUL; |
|
SetupItem(ii); |
|
} |
|
|
|
void CreateRndUseful(int x, int y, bool sendmsg) |
|
{ |
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
GetSuperItemSpace(x, y, ii); |
|
int curlv = items_get_currlevel(); |
|
|
|
SetupAllUseful(ii, AdvanceRndSeed(), curlv); |
|
if (sendmsg) |
|
NetSendCmdDItem(false, ii); |
|
} |
|
|
|
void CreateTypeItem(int x, int y, bool onlygood, int itype, int imisc, bool sendmsg, bool delta) |
|
{ |
|
int idx; |
|
|
|
int curlv = items_get_currlevel(); |
|
if (itype != ITYPE_GOLD) |
|
idx = RndTypeItems(itype, imisc, curlv); |
|
else |
|
idx = IDI_GOLD; |
|
|
|
SetupBaseItem(x, y, idx, onlygood, sendmsg, delta); |
|
} |
|
|
|
void RecreateItem(int ii, int idx, uint16_t icreateinfo, int iseed, int ivalue, bool isHellfire) |
|
{ |
|
bool _gbIsHellfire = gbIsHellfire; |
|
gbIsHellfire = isHellfire; |
|
|
|
if (idx == IDI_GOLD) { |
|
SetPlrHandItem(&items[ii], IDI_GOLD); |
|
items[ii]._iSeed = iseed; |
|
items[ii]._iCreateInfo = icreateinfo; |
|
items[ii]._ivalue = ivalue; |
|
SetPlrHandGoldCurs(&items[ii]); |
|
gbIsHellfire = _gbIsHellfire; |
|
return; |
|
} |
|
|
|
if (icreateinfo == 0) { |
|
SetPlrHandItem(&items[ii], idx); |
|
SetPlrHandSeed(&items[ii], iseed); |
|
gbIsHellfire = _gbIsHellfire; |
|
return; |
|
} |
|
|
|
if ((icreateinfo & CF_UNIQUE) == 0) { |
|
if ((icreateinfo & CF_TOWN) != 0) { |
|
RecreateTownItem(ii, idx, icreateinfo, iseed); |
|
gbIsHellfire = _gbIsHellfire; |
|
return; |
|
} |
|
|
|
if ((icreateinfo & CF_USEFUL) == CF_USEFUL) { |
|
SetupAllUseful(ii, iseed, icreateinfo & CF_LEVEL); |
|
gbIsHellfire = _gbIsHellfire; |
|
return; |
|
} |
|
} |
|
|
|
int level = icreateinfo & CF_LEVEL; |
|
|
|
int uper = 0; |
|
if ((icreateinfo & CF_UPER1) != 0) |
|
uper = 1; |
|
if ((icreateinfo & CF_UPER15) != 0) |
|
uper = 15; |
|
|
|
bool onlygood = (icreateinfo & CF_ONLYGOOD) != 0; |
|
bool recreate = (icreateinfo & CF_UNIQUE) != 0; |
|
bool pregen = (icreateinfo & CF_PREGEN) != 0; |
|
|
|
SetupAllItems(ii, idx, iseed, level, uper, onlygood, recreate, pregen); |
|
gbIsHellfire = _gbIsHellfire; |
|
} |
|
|
|
void RecreateEar(int ii, uint16_t ic, int iseed, int Id, int dur, int mdur, int ch, int mch, int ivalue, int ibuff) |
|
{ |
|
SetPlrHandItem(&items[ii], IDI_EAR); |
|
tempstr[0] = (ic >> 8) & 0x7F; |
|
tempstr[1] = ic & 0x7F; |
|
tempstr[2] = (iseed >> 24) & 0x7F; |
|
tempstr[3] = (iseed >> 16) & 0x7F; |
|
tempstr[4] = (iseed >> 8) & 0x7F; |
|
tempstr[5] = iseed & 0x7F; |
|
tempstr[6] = Id & 0x7F; |
|
tempstr[7] = dur & 0x7F; |
|
tempstr[8] = mdur & 0x7F; |
|
tempstr[9] = ch & 0x7F; |
|
tempstr[10] = mch & 0x7F; |
|
tempstr[11] = (ivalue >> 8) & 0x7F; |
|
tempstr[12] = (ibuff >> 24) & 0x7F; |
|
tempstr[13] = (ibuff >> 16) & 0x7F; |
|
tempstr[14] = (ibuff >> 8) & 0x7F; |
|
tempstr[15] = ibuff & 0x7F; |
|
tempstr[16] = '\0'; |
|
sprintf(items[ii]._iName, _("Ear of %s"), tempstr); |
|
items[ii]._iCurs = ((ivalue >> 6) & 3) + ICURS_EAR_SORCERER; |
|
items[ii]._ivalue = ivalue & 0x3F; |
|
items[ii]._iCreateInfo = ic; |
|
items[ii]._iSeed = iseed; |
|
} |
|
|
|
void items_427A72() |
|
{ |
|
PkItemStruct id; |
|
BYTE *buffer; |
|
|
|
if (CornerStone.activated) { |
|
if (!CornerStone.item.isEmpty()) { |
|
PackItem(&id, &CornerStone.item); |
|
buffer = (BYTE *)&id; |
|
for (size_t i = 0; i < sizeof(PkItemStruct); i++) { |
|
sprintf(&sgOptions.Hellfire.szItem[i * 2], "%02X", buffer[i]); |
|
} |
|
} else { |
|
sgOptions.Hellfire.szItem[0] = '\0'; |
|
} |
|
} |
|
} |
|
|
|
int char2int(char input) |
|
{ |
|
if (input >= '0' && input <= '9') |
|
return input - '0'; |
|
if (input >= 'A' && input <= 'F') |
|
return input - 'A' + 10; |
|
return 0; |
|
} |
|
|
|
void hex2bin(const char *src, int bytes, char *target) |
|
{ |
|
for (int i = 0; i < bytes; i++, src += 2) { |
|
target[i] = (char2int(*src) << 4) | char2int(src[1]); |
|
} |
|
} |
|
|
|
void items_427ABA(Point position) |
|
{ |
|
PkItemStruct PkSItem; |
|
|
|
if (CornerStone.activated || position.x == 0 || position.y == 0) { |
|
return; |
|
} |
|
|
|
CornerStone.item._itype = ITYPE_NONE; |
|
CornerStone.activated = true; |
|
if (dItem[position.x][position.y]) { |
|
int ii = dItem[position.x][position.y] - 1; |
|
for (int i = 0; i < numitems; i++) { |
|
if (itemactive[i] == ii) { |
|
DeleteItem(ii, i); |
|
break; |
|
} |
|
} |
|
dItem[position.x][position.y] = 0; |
|
} |
|
|
|
if (strlen(sgOptions.Hellfire.szItem) < sizeof(PkItemStruct) * 2) |
|
return; |
|
|
|
hex2bin(sgOptions.Hellfire.szItem, sizeof(PkItemStruct), (char *)&PkSItem); |
|
|
|
int ii = AllocateItem(); |
|
|
|
dItem[position.x][position.y] = ii + 1; |
|
|
|
UnPackItem(&PkSItem, &items[ii], (PkSItem.dwBuff & CF_HELLFIRE) != 0); |
|
items[ii].position = position; |
|
RespawnItem(&items[ii], false); |
|
CornerStone.item = items[ii]; |
|
} |
|
|
|
void SpawnQuestItem(int itemid, int x, int y, int randarea, int selflag) |
|
{ |
|
if (randarea) { |
|
int tries = 0; |
|
while (true) { |
|
tries++; |
|
if (tries > 1000 && randarea > 1) |
|
randarea--; |
|
|
|
x = GenerateRnd(MAXDUNX); |
|
y = GenerateRnd(MAXDUNY); |
|
|
|
bool failed = false; |
|
for (int i = 0; i < randarea && !failed; i++) { |
|
for (int j = 0; j < randarea && !failed; j++) { |
|
failed = !ItemSpaceOk(i + x, j + y); |
|
} |
|
} |
|
if (!failed) |
|
break; |
|
} |
|
} |
|
|
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
|
|
items[ii].position = { x, y }; |
|
|
|
dItem[x][y] = ii + 1; |
|
|
|
int curlv = items_get_currlevel(); |
|
GetItemAttrs(ii, itemid, curlv); |
|
|
|
SetupItem(ii); |
|
items[ii]._iPostDraw = true; |
|
if (selflag) { |
|
items[ii]._iSelFlag = selflag; |
|
items[ii]._iAnimFrame = items[ii]._iAnimLen; |
|
items[ii]._iAnimFlag = false; |
|
} |
|
} |
|
|
|
void SpawnRock() |
|
{ |
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int oi; |
|
bool ostand = false; |
|
for (int i = 0; i < nobjects && !ostand; i++) { |
|
oi = objectactive[i]; |
|
ostand = object[oi]._otype == OBJ_STAND; |
|
} |
|
|
|
if (!ostand) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
|
|
int xx = object[oi].position.x; |
|
int yy = object[oi].position.y; |
|
items[ii].position = { xx, yy }; |
|
dItem[xx][items[ii].position.y] = ii + 1; |
|
int curlv = items_get_currlevel(); |
|
GetItemAttrs(ii, IDI_ROCK, curlv); |
|
SetupItem(ii); |
|
items[ii]._iSelFlag = 2; |
|
items[ii]._iPostDraw = true; |
|
items[ii]._iAnimFrame = 11; |
|
} |
|
|
|
void SpawnRewardItem(int itemid, int xx, int yy) |
|
{ |
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
|
|
items[ii].position = { xx, yy }; |
|
dItem[xx][yy] = ii + 1; |
|
int curlv = items_get_currlevel(); |
|
GetItemAttrs(ii, itemid, curlv); |
|
SetupItem(ii); |
|
items[ii]._iSelFlag = 2; |
|
items[ii]._iPostDraw = true; |
|
items[ii]._iAnimFrame = 1; |
|
items[ii]._iAnimFlag = true; |
|
items[ii]._iIdentified = true; |
|
} |
|
|
|
void SpawnMapOfDoom(int xx, int yy) |
|
{ |
|
SpawnRewardItem(IDI_MAPOFDOOM, xx, yy); |
|
} |
|
|
|
void SpawnRuneBomb(int xx, int yy) |
|
{ |
|
SpawnRewardItem(IDI_RUNEBOMB, xx, yy); |
|
} |
|
|
|
void SpawnTheodore(int xx, int yy) |
|
{ |
|
SpawnRewardItem(IDI_THEODORE, xx, yy); |
|
} |
|
|
|
void RespawnItem(ItemStruct *item, bool FlipFlag) |
|
{ |
|
int it; |
|
|
|
it = ItemCAnimTbl[item->_iCurs]; |
|
item->_iAnimData = &*itemanims[it]; |
|
item->_iAnimLen = ItemAnimLs[it]; |
|
item->_iPostDraw = false; |
|
item->_iRequest = false; |
|
if (FlipFlag) { |
|
item->_iAnimFrame = 1; |
|
item->_iAnimFlag = true; |
|
item->_iSelFlag = 0; |
|
} else { |
|
item->_iAnimFrame = item->_iAnimLen; |
|
item->_iAnimFlag = false; |
|
item->_iSelFlag = 1; |
|
} |
|
|
|
if (item->_iCurs == ICURS_MAGIC_ROCK) { |
|
item->_iSelFlag = 1; |
|
PlaySfxLoc(ItemDropSnds[it], item->position.x, item->position.y); |
|
} |
|
if (item->_iCurs == ICURS_TAVERN_SIGN) |
|
item->_iSelFlag = 1; |
|
if (item->_iCurs == ICURS_ANVIL_OF_FURY) |
|
item->_iSelFlag = 1; |
|
} |
|
|
|
void DeleteItem(int ii, int i) |
|
{ |
|
itemavail[MAXITEMS - numitems] = ii; |
|
numitems--; |
|
if (numitems > 0 && i != numitems) |
|
itemactive[i] = itemactive[numitems]; |
|
} |
|
|
|
void ItemDoppel() |
|
{ |
|
if (!gbIsMultiplayer) |
|
return; |
|
|
|
static int idoppely = 16; |
|
|
|
for (int idoppelx = 16; idoppelx < 96; idoppelx++) { |
|
if (dItem[idoppelx][idoppely]) { |
|
ItemStruct *i = &items[dItem[idoppelx][idoppely] - 1]; |
|
if (i->position.x != idoppelx || i->position.y != idoppely) |
|
dItem[idoppelx][idoppely] = 0; |
|
} |
|
} |
|
|
|
idoppely++; |
|
if (idoppely == 96) |
|
idoppely = 16; |
|
} |
|
|
|
void ProcessItems() |
|
{ |
|
int i, ii; |
|
|
|
for (i = 0; i < numitems; i++) { |
|
ii = itemactive[i]; |
|
if (items[ii]._iAnimFlag) { |
|
items[ii]._iAnimFrame++; |
|
if (items[ii]._iCurs == ICURS_MAGIC_ROCK) { |
|
if (items[ii]._iSelFlag == 1 && items[ii]._iAnimFrame == 11) |
|
items[ii]._iAnimFrame = 1; |
|
if (items[ii]._iSelFlag == 2 && items[ii]._iAnimFrame == 21) |
|
items[ii]._iAnimFrame = 11; |
|
} else { |
|
if (items[ii]._iAnimFrame == items[ii]._iAnimLen / 2) |
|
PlaySfxLoc(ItemDropSnds[ItemCAnimTbl[items[ii]._iCurs]], items[ii].position.x, items[ii].position.y); |
|
|
|
if (items[ii]._iAnimFrame >= items[ii]._iAnimLen) { |
|
items[ii]._iAnimFrame = items[ii]._iAnimLen; |
|
items[ii]._iAnimFlag = false; |
|
items[ii]._iSelFlag = 1; |
|
} |
|
} |
|
} |
|
} |
|
ItemDoppel(); |
|
} |
|
|
|
void FreeItemGFX() |
|
{ |
|
for (auto &itemanim : itemanims) { |
|
itemanim = std::nullopt; |
|
} |
|
} |
|
|
|
void GetItemFrm(int i) |
|
{ |
|
items[i]._iAnimData = &*itemanims[ItemCAnimTbl[items[i]._iCurs]]; |
|
} |
|
|
|
void GetItemStr(int i) |
|
{ |
|
int nGold; |
|
|
|
if (items[i]._itype != ITYPE_GOLD) { |
|
if (items[i]._iIdentified) |
|
strcpy(infostr, items[i]._iIName); |
|
else |
|
strcpy(infostr, items[i]._iName); |
|
|
|
if (items[i]._iMagical == ITEM_QUALITY_MAGIC) |
|
infoclr = COL_BLUE; |
|
if (items[i]._iMagical == ITEM_QUALITY_UNIQUE) |
|
infoclr = COL_GOLD; |
|
} else { |
|
nGold = items[i]._ivalue; |
|
sprintf(infostr, ngettext("%i gold piece", "%i gold pieces", nGold), nGold); |
|
} |
|
} |
|
|
|
void CheckIdentify(int pnum, int cii) |
|
{ |
|
ItemStruct *pi; |
|
|
|
if (cii >= NUM_INVLOC) |
|
pi = &plr[pnum].InvList[cii - NUM_INVLOC]; |
|
else |
|
pi = &plr[pnum].InvBody[cii]; |
|
|
|
pi->_iIdentified = true; |
|
CalcPlrInv(pnum, true); |
|
|
|
if (pnum == myplr) |
|
NewCursor(CURSOR_HAND); |
|
} |
|
|
|
static void RepairItem(ItemStruct *i, int lvl) |
|
{ |
|
if (i->_iDurability == i->_iMaxDur) { |
|
return; |
|
} |
|
|
|
if (i->_iMaxDur <= 0) { |
|
i->_itype = ITYPE_NONE; |
|
return; |
|
} |
|
|
|
int rep = 0; |
|
do { |
|
rep += lvl + GenerateRnd(lvl); |
|
i->_iMaxDur -= std::max(i->_iMaxDur / (lvl + 9), 1); |
|
if (!i->_iMaxDur) { |
|
i->_itype = ITYPE_NONE; |
|
return; |
|
} |
|
} while (rep + i->_iDurability < i->_iMaxDur); |
|
|
|
i->_iDurability = std::min<int>(i->_iDurability + rep, i->_iMaxDur); |
|
} |
|
|
|
void DoRepair(int pnum, int cii) |
|
{ |
|
PlayerStruct *p; |
|
ItemStruct *pi; |
|
|
|
p = &plr[pnum]; |
|
PlaySfxLoc(IS_REPAIR, p->position.tile.x, p->position.tile.y); |
|
|
|
if (cii >= NUM_INVLOC) { |
|
pi = &p->InvList[cii - NUM_INVLOC]; |
|
} else { |
|
pi = &p->InvBody[cii]; |
|
} |
|
|
|
RepairItem(pi, p->_pLevel); |
|
CalcPlrInv(pnum, true); |
|
|
|
if (pnum == myplr) |
|
NewCursor(CURSOR_HAND); |
|
} |
|
|
|
static void RechargeItem(ItemStruct *i, int r) |
|
{ |
|
if (i->_iCharges == i->_iMaxCharges) |
|
return; |
|
|
|
do { |
|
i->_iMaxCharges--; |
|
if (i->_iMaxCharges == 0) { |
|
return; |
|
} |
|
i->_iCharges += r; |
|
} while (i->_iCharges < i->_iMaxCharges); |
|
|
|
i->_iCharges = std::min(i->_iCharges, i->_iMaxCharges); |
|
} |
|
|
|
void DoRecharge(int pnum, int cii) |
|
{ |
|
PlayerStruct *p; |
|
ItemStruct *pi; |
|
int r; |
|
|
|
p = &plr[pnum]; |
|
if (cii >= NUM_INVLOC) { |
|
pi = &p->InvList[cii - NUM_INVLOC]; |
|
} else { |
|
pi = &p->InvBody[cii]; |
|
} |
|
if (pi->_itype == ITYPE_STAFF && pi->_iSpell != SPL_NULL) { |
|
r = GetSpellBookLevel(pi->_iSpell); |
|
r = GenerateRnd(p->_pLevel / r) + 1; |
|
RechargeItem(pi, r); |
|
CalcPlrInv(pnum, true); |
|
} |
|
|
|
if (pnum == myplr) |
|
NewCursor(CURSOR_HAND); |
|
} |
|
|
|
static bool OilItem(ItemStruct *x, PlayerStruct *p) |
|
{ |
|
int dur, r; |
|
|
|
if (x->_iClass == ICLASS_MISC) { |
|
return false; |
|
} |
|
if (x->_iClass == ICLASS_GOLD) { |
|
return false; |
|
} |
|
if (x->_iClass == ICLASS_QUEST) { |
|
return false; |
|
} |
|
|
|
switch (p->_pOilType) { |
|
case IMISC_OILACC: |
|
case IMISC_OILMAST: |
|
case IMISC_OILSHARP: |
|
if (x->_iClass == ICLASS_ARMOR) { |
|
return false; |
|
} |
|
break; |
|
case IMISC_OILDEATH: |
|
if (x->_iClass == ICLASS_ARMOR) { |
|
return false; |
|
} |
|
if (x->_itype == ITYPE_BOW) { |
|
return false; |
|
} |
|
break; |
|
case IMISC_OILHARD: |
|
case IMISC_OILIMP: |
|
if (x->_iClass == ICLASS_WEAPON) { |
|
return false; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
switch (p->_pOilType) { |
|
case IMISC_OILACC: |
|
if (x->_iPLToHit < 50) { |
|
x->_iPLToHit += GenerateRnd(2) + 1; |
|
} |
|
break; |
|
case IMISC_OILMAST: |
|
if (x->_iPLToHit < 100) { |
|
x->_iPLToHit += GenerateRnd(3) + 3; |
|
} |
|
break; |
|
case IMISC_OILSHARP: |
|
if (x->_iMaxDam - x->_iMinDam < 30) { |
|
x->_iMaxDam = x->_iMaxDam + 1; |
|
} |
|
break; |
|
case IMISC_OILDEATH: |
|
if (x->_iMaxDam - x->_iMinDam < 30) { |
|
x->_iMinDam = x->_iMinDam + 1; |
|
x->_iMaxDam = x->_iMaxDam + 2; |
|
} |
|
break; |
|
case IMISC_OILSKILL: |
|
r = GenerateRnd(6) + 5; |
|
x->_iMinStr = std::max(0, x->_iMinStr - r); |
|
x->_iMinMag = std::max(0, x->_iMinMag - r); |
|
x->_iMinDex = std::max(0, x->_iMinDex - r); |
|
break; |
|
case IMISC_OILBSMTH: |
|
if (x->_iMaxDur != 255) { |
|
if (x->_iDurability < x->_iMaxDur) { |
|
dur = (x->_iMaxDur + 4) / 5 + x->_iDurability; |
|
dur = std::min<int>(dur, x->_iMaxDur); |
|
} else { |
|
if (x->_iMaxDur >= 100) { |
|
return true; |
|
} |
|
dur = x->_iMaxDur + 1; |
|
x->_iMaxDur = dur; |
|
} |
|
x->_iDurability = dur; |
|
} |
|
break; |
|
case IMISC_OILFORT: |
|
if (x->_iMaxDur != 255 && x->_iMaxDur < 200) { |
|
r = GenerateRnd(41) + 10; |
|
x->_iMaxDur += r; |
|
x->_iDurability += r; |
|
} |
|
break; |
|
case IMISC_OILPERM: |
|
x->_iDurability = 255; |
|
x->_iMaxDur = 255; |
|
break; |
|
case IMISC_OILHARD: |
|
if (x->_iAC < 60) { |
|
x->_iAC += GenerateRnd(2) + 1; |
|
} |
|
break; |
|
case IMISC_OILIMP: |
|
if (x->_iAC < 120) { |
|
x->_iAC += GenerateRnd(3) + 3; |
|
} |
|
break; |
|
default: |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
void DoOil(int pnum, int cii) |
|
{ |
|
PlayerStruct *p = &plr[pnum]; |
|
|
|
if (cii >= NUM_INVLOC || cii == INVLOC_HEAD || (cii > INVLOC_AMULET && cii <= INVLOC_CHEST)) { |
|
if (OilItem(&p->InvBody[cii], p)) { |
|
CalcPlrInv(pnum, true); |
|
if (pnum == myplr) { |
|
NewCursor(CURSOR_HAND); |
|
} |
|
} |
|
} |
|
} |
|
|
|
void PrintItemOil(char IDidx) |
|
{ |
|
switch (IDidx) { |
|
case IMISC_OILACC: |
|
strcpy(tempstr, _("increases a weapon's")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("chance to hit")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILMAST: |
|
strcpy(tempstr, _("greatly increases a")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("weapon's chance to hit")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILSHARP: |
|
strcpy(tempstr, _("increases a weapon's")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("damage potential")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILDEATH: |
|
strcpy(tempstr, _("greatly increases a weapon's")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("damage potential - not bows")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILSKILL: |
|
strcpy(tempstr, _("reduces attributes needed")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("to use armor or weapons")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILBSMTH: |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("restores 20% of an")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("item's durability")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILFORT: |
|
strcpy(tempstr, _("increases an item's")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("current and max durability")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILPERM: |
|
strcpy(tempstr, _("makes an item indestructible")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILHARD: |
|
strcpy(tempstr, _("increases the armor class")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("of armor and shields")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OILIMP: |
|
strcpy(tempstr, _("greatly increases the armor")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("class of armor and shields")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_RUNEF: |
|
strcpy(tempstr, _("sets fire trap")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_RUNEL: |
|
strcpy(tempstr, _("sets lightning trap")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_GR_RUNEL: |
|
strcpy(tempstr, _("sets lightning trap")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_GR_RUNEF: |
|
strcpy(tempstr, _("sets fire trap")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_RUNES: |
|
strcpy(tempstr, _("sets petrification trap")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_FULLHEAL: |
|
strcpy(tempstr, _("fully recover life")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_HEAL: |
|
strcpy(tempstr, _("recover partial life")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_OLDHEAL: |
|
strcpy(tempstr, _("recover life")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_DEADHEAL: |
|
strcpy(tempstr, _("deadly heal")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_MANA: |
|
strcpy(tempstr, _("recover mana")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_FULLMANA: |
|
strcpy(tempstr, _("fully recover mana")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXSTR: |
|
strcpy(tempstr, _("increase strength")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXMAG: |
|
strcpy(tempstr, _("increase magic")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXDEX: |
|
strcpy(tempstr, _("increase dexterity")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXVIT: |
|
strcpy(tempstr, _("increase vitality")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXWEAK: |
|
strcpy(tempstr, _("decrease strength")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXDIS: |
|
strcpy(tempstr, _("decrease strength")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXCLUM: |
|
strcpy(tempstr, _("decrease dexterity")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_ELIXSICK: |
|
strcpy(tempstr, _("decrease vitality")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_REJUV: |
|
strcpy(tempstr, _("recover life and mana")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
case IMISC_FULLREJUV: |
|
strcpy(tempstr, _("fully recover life and mana")); |
|
AddPanelString(tempstr, true); |
|
break; |
|
} |
|
} |
|
|
|
void PrintItemPower(char plidx, ItemStruct *x) |
|
{ |
|
switch (plidx) { |
|
case IPL_TOHIT: |
|
case IPL_TOHIT_CURSE: |
|
sprintf(tempstr, _("chance to hit: %+i%%"), x->_iPLToHit); |
|
break; |
|
case IPL_DAMP: |
|
case IPL_DAMP_CURSE: |
|
sprintf(tempstr, _("%+i%% damage"), x->_iPLDam); |
|
break; |
|
case IPL_TOHIT_DAMP: |
|
case IPL_TOHIT_DAMP_CURSE: |
|
sprintf(tempstr, _("to hit: %+i%%, %+i%% damage"), x->_iPLToHit, x->_iPLDam); |
|
break; |
|
case IPL_ACP: |
|
case IPL_ACP_CURSE: |
|
sprintf(tempstr, _("%+i%% armor"), x->_iPLAC); |
|
break; |
|
case IPL_SETAC: |
|
sprintf(tempstr, _("armor class: %i"), x->_iAC); |
|
break; |
|
case IPL_AC_CURSE: |
|
sprintf(tempstr, _("armor class: %i"), x->_iAC); |
|
break; |
|
case IPL_FIRERES: |
|
case IPL_FIRERES_CURSE: |
|
if (x->_iPLFR < 75) |
|
sprintf(tempstr, _("Resist Fire: %+i%%"), x->_iPLFR); |
|
else |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("Resist Fire: 75% MAX")); |
|
break; |
|
case IPL_LIGHTRES: |
|
case IPL_LIGHTRES_CURSE: |
|
if (x->_iPLLR < 75) |
|
sprintf(tempstr, _("Resist Lightning: %+i%%"), x->_iPLLR); |
|
else |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("Resist Lightning: 75% MAX")); |
|
break; |
|
case IPL_MAGICRES: |
|
case IPL_MAGICRES_CURSE: |
|
if (x->_iPLMR < 75) |
|
sprintf(tempstr, _("Resist Magic: %+i%%"), x->_iPLMR); |
|
else |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("Resist Magic: 75% MAX")); |
|
break; |
|
case IPL_ALLRES: |
|
case IPL_ALLRES_CURSE: |
|
if (x->_iPLFR < 75) |
|
sprintf(tempstr, _("Resist All: %+i%%"), x->_iPLFR); |
|
if (x->_iPLFR >= 75) |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("Resist All: 75% MAX")); |
|
break; |
|
case IPL_SPLLVLADD: |
|
if (x->_iSplLvlAdd > 0) |
|
sprintf(tempstr, ngettext("spells are increased %i level", "spells are increased %i levels", x->_iSplLvlAdd), x->_iSplLvlAdd); |
|
else if (x->_iSplLvlAdd < 0) |
|
sprintf(tempstr, ngettext("spells are decreased %i level", "spells are decreased %i levels", -x->_iSplLvlAdd), -x->_iSplLvlAdd); |
|
else if (x->_iSplLvlAdd == 0) |
|
strcpy(tempstr, _("spell levels unchanged (?)")); |
|
break; |
|
case IPL_CHARGES: |
|
strcpy(tempstr, _("Extra charges")); |
|
break; |
|
case IPL_SPELL: |
|
sprintf(tempstr, ngettext("%i %s charge", "%i %s charges", x->_iMaxCharges), x->_iMaxCharges, _(spelldata[x->_iSpell].sNameText)); |
|
break; |
|
case IPL_FIREDAM: |
|
if (x->_iFMinDam == x->_iFMaxDam) |
|
sprintf(tempstr, _("Fire hit damage: %i"), x->_iFMinDam); |
|
else |
|
sprintf(tempstr, _("Fire hit damage: %i-%i"), x->_iFMinDam, x->_iFMaxDam); |
|
break; |
|
case IPL_LIGHTDAM: |
|
if (x->_iLMinDam == x->_iLMaxDam) |
|
sprintf(tempstr, _("Lightning hit damage: %i"), x->_iLMinDam); |
|
else |
|
sprintf(tempstr, _("Lightning hit damage: %i-%i"), x->_iLMinDam, x->_iLMaxDam); |
|
break; |
|
case IPL_STR: |
|
case IPL_STR_CURSE: |
|
sprintf(tempstr, _("%+i to strength"), x->_iPLStr); |
|
break; |
|
case IPL_MAG: |
|
case IPL_MAG_CURSE: |
|
sprintf(tempstr, _("%+i to magic"), x->_iPLMag); |
|
break; |
|
case IPL_DEX: |
|
case IPL_DEX_CURSE: |
|
sprintf(tempstr, _("%+i to dexterity"), x->_iPLDex); |
|
break; |
|
case IPL_VIT: |
|
case IPL_VIT_CURSE: |
|
sprintf(tempstr, _("%+i to vitality"), x->_iPLVit); |
|
break; |
|
case IPL_ATTRIBS: |
|
case IPL_ATTRIBS_CURSE: |
|
sprintf(tempstr, _("%+i to all attributes"), x->_iPLStr); |
|
break; |
|
case IPL_GETHIT_CURSE: |
|
case IPL_GETHIT: |
|
sprintf(tempstr, _("%+i damage from enemies"), x->_iPLGetHit); |
|
break; |
|
case IPL_LIFE: |
|
case IPL_LIFE_CURSE: |
|
sprintf(tempstr, _("Hit Points: %+i"), x->_iPLHP >> 6); |
|
break; |
|
case IPL_MANA: |
|
case IPL_MANA_CURSE: |
|
sprintf(tempstr, _("Mana: %+i"), x->_iPLMana >> 6); |
|
break; |
|
case IPL_DUR: |
|
strcpy(tempstr, _("high durability")); |
|
break; |
|
case IPL_DUR_CURSE: |
|
strcpy(tempstr, _("decreased durability")); |
|
break; |
|
case IPL_INDESTRUCTIBLE: |
|
strcpy(tempstr, _("indestructible")); |
|
break; |
|
case IPL_LIGHT: |
|
sprintf(tempstr, _("+%i%% light radius"), 10 * x->_iPLLight); |
|
break; |
|
case IPL_LIGHT_CURSE: |
|
sprintf(tempstr, _("-%i%% light radius"), -10 * x->_iPLLight); |
|
break; |
|
case IPL_MULT_ARROWS: |
|
strcpy(tempstr, _("multiple arrows per shot")); |
|
break; |
|
case IPL_FIRE_ARROWS: |
|
if (x->_iFMinDam == x->_iFMaxDam) |
|
sprintf(tempstr, _("fire arrows damage: %i"), x->_iFMinDam); |
|
else |
|
sprintf(tempstr, _("fire arrows damage: %i-%i"), x->_iFMinDam, x->_iFMaxDam); |
|
break; |
|
case IPL_LIGHT_ARROWS: |
|
if (x->_iLMinDam == x->_iLMaxDam) |
|
sprintf(tempstr, _("lightning arrows damage %i"), x->_iLMinDam); |
|
else |
|
sprintf(tempstr, _("lightning arrows damage %i-%i"), x->_iLMinDam, x->_iLMaxDam); |
|
break; |
|
case IPL_FIREBALL: |
|
if (x->_iFMinDam == x->_iFMaxDam) |
|
sprintf(tempstr, _("fireball damage: %i"), x->_iFMinDam); |
|
else |
|
sprintf(tempstr, _("fireball damage: %i-%i"), x->_iFMinDam, x->_iFMaxDam); |
|
break; |
|
case IPL_THORNS: |
|
strcpy(tempstr, _("attacker takes 1-3 damage")); |
|
break; |
|
case IPL_NOMANA: |
|
strcpy(tempstr, _("user loses all mana")); |
|
break; |
|
case IPL_NOHEALPLR: |
|
strcpy(tempstr, _("you can't heal")); |
|
break; |
|
case IPL_ABSHALFTRAP: |
|
strcpy(tempstr, _("absorbs half of trap damage")); |
|
break; |
|
case IPL_KNOCKBACK: |
|
strcpy(tempstr, _("knocks target back")); |
|
break; |
|
case IPL_3XDAMVDEM: |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("+200% damage vs. demons")); |
|
break; |
|
case IPL_ALLRESZERO: |
|
strcpy(tempstr, _("All Resistance equals 0")); |
|
break; |
|
case IPL_NOHEALMON: |
|
strcpy(tempstr, _("hit monster doesn't heal")); |
|
break; |
|
case IPL_STEALMANA: |
|
if ((x->_iFlags & ISPL_STEALMANA_3) != 0) |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("hit steals 3% mana")); |
|
if ((x->_iFlags & ISPL_STEALMANA_5) != 0) |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("hit steals 5% mana")); |
|
break; |
|
case IPL_STEALLIFE: |
|
if ((x->_iFlags & ISPL_STEALLIFE_3) != 0) |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("hit steals 3% life")); |
|
if ((x->_iFlags & ISPL_STEALLIFE_5) != 0) |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("hit steals 5% life")); |
|
break; |
|
case IPL_TARGAC: |
|
strcpy(tempstr, _("penetrates target's armor")); |
|
break; |
|
case IPL_FASTATTACK: |
|
if ((x->_iFlags & ISPL_QUICKATTACK) != 0) |
|
strcpy(tempstr, _("quick attack")); |
|
if ((x->_iFlags & ISPL_FASTATTACK) != 0) |
|
strcpy(tempstr, _("fast attack")); |
|
if ((x->_iFlags & ISPL_FASTERATTACK) != 0) |
|
strcpy(tempstr, _("faster attack")); |
|
if ((x->_iFlags & ISPL_FASTESTATTACK) != 0) |
|
strcpy(tempstr, _("fastest attack")); |
|
break; |
|
case IPL_FASTRECOVER: |
|
if ((x->_iFlags & ISPL_FASTRECOVER) != 0) |
|
strcpy(tempstr, _("fast hit recovery")); |
|
if ((x->_iFlags & ISPL_FASTERRECOVER) != 0) |
|
strcpy(tempstr, _("faster hit recovery")); |
|
if ((x->_iFlags & ISPL_FASTESTRECOVER) != 0) |
|
strcpy(tempstr, _("fastest hit recovery")); |
|
break; |
|
case IPL_FASTBLOCK: |
|
strcpy(tempstr, _("fast block")); |
|
break; |
|
case IPL_DAMMOD: |
|
sprintf(tempstr, ngettext("adds %i point to damage", "adds %i points to damage", x->_iPLDamMod), x->_iPLDamMod); |
|
break; |
|
case IPL_RNDARROWVEL: |
|
strcpy(tempstr, _("fires random speed arrows")); |
|
break; |
|
case IPL_SETDAM: |
|
strcpy(tempstr, _("unusual item damage")); |
|
break; |
|
case IPL_SETDUR: |
|
strcpy(tempstr, _("altered durability")); |
|
break; |
|
case IPL_FASTSWING: |
|
strcpy(tempstr, _("Faster attack swing")); |
|
break; |
|
case IPL_ONEHAND: |
|
strcpy(tempstr, _("one handed sword")); |
|
break; |
|
case IPL_DRAINLIFE: |
|
strcpy(tempstr, _("constantly lose hit points")); |
|
break; |
|
case IPL_RNDSTEALLIFE: |
|
strcpy(tempstr, _("life stealing")); |
|
break; |
|
case IPL_NOMINSTR: |
|
strcpy(tempstr, _("no strength requirement")); |
|
break; |
|
case IPL_INFRAVISION: |
|
strcpy(tempstr, _("see with infravision")); |
|
break; |
|
case IPL_INVCURS: |
|
strcpy(tempstr, " "); |
|
break; |
|
case IPL_ADDACLIFE: |
|
if (x->_iFMinDam == x->_iFMaxDam) |
|
sprintf(tempstr, _("lightning damage: %i"), x->_iFMinDam); |
|
else |
|
sprintf(tempstr, _("lightning damage: %i-%i"), x->_iFMinDam, x->_iFMaxDam); |
|
break; |
|
case IPL_ADDMANAAC: |
|
strcpy(tempstr, _("charged bolts on hits")); |
|
break; |
|
case IPL_FIRERESCLVL: |
|
if (x->_iPLFR <= 0) |
|
strcpy(tempstr, " "); |
|
else if (x->_iPLFR >= 1) |
|
sprintf(tempstr, _("Resist Fire: %+i%%"), x->_iPLFR); |
|
break; |
|
case IPL_DEVASTATION: |
|
strcpy(tempstr, _("occasional triple damage")); |
|
break; |
|
case IPL_DECAY: |
|
sprintf(tempstr, _("decaying %+i%% damage"), x->_iPLDam); |
|
break; |
|
case IPL_PERIL: |
|
strcpy(tempstr, _("2x dmg to monst, 1x to you")); |
|
break; |
|
case IPL_JESTERS: |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("Random 0 - 500% damage")); |
|
break; |
|
case IPL_CRYSTALLINE: |
|
sprintf(tempstr, _("low dur, %+i%% damage"), x->_iPLDam); |
|
break; |
|
case IPL_DOPPELGANGER: |
|
sprintf(tempstr, _("to hit: %+i%%, %+i%% damage"), x->_iPLToHit, x->_iPLDam); |
|
break; |
|
case IPL_ACDEMON: |
|
strcpy(tempstr, _("extra AC vs demons")); |
|
break; |
|
case IPL_ACUNDEAD: |
|
strcpy(tempstr, _("extra AC vs undead")); |
|
break; |
|
case IPL_MANATOLIFE: |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("50% Mana moved to Health")); |
|
break; |
|
case IPL_LIFETOMANA: |
|
/*xgettext:no-c-format*/ strcpy(tempstr, _("40% Health moved to Mana")); |
|
break; |
|
default: |
|
strcpy(tempstr, _("Another ability (NW)")); |
|
break; |
|
} |
|
} |
|
|
|
static void DrawUTextBack(const CelOutputBuffer &out) |
|
{ |
|
CelDrawTo(out, RIGHT_PANEL_X - SPANEL_WIDTH + 24, 327, *pSTextBoxCels, 1); |
|
DrawHalfTransparentRectTo(out, RIGHT_PANEL_X - SPANEL_WIDTH + 27, 28, 265, 297); |
|
} |
|
|
|
void PrintUString(const CelOutputBuffer &out, int x, int y, bool cjustflag, const char *str, text_color col) |
|
{ |
|
int len, width, sx, sy, i, k; |
|
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; |
|
} |
|
|
|
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; |
|
} |
|
} |
|
|
|
static void DrawULine(const CelOutputBuffer &out, int y) |
|
{ |
|
BYTE *src = out.at(26 + RIGHT_PANEL - SPANEL_WIDTH, 25); |
|
BYTE *dst = out.at(26 + RIGHT_PANEL_X - SPANEL_WIDTH, y * 12 + 38); |
|
|
|
for (int i = 0; i < 3; i++, src += out.pitch(), dst += out.pitch()) |
|
memcpy(dst, src, 267); // BUGFIX: should be 267 (fixed) |
|
} |
|
|
|
void DrawUniqueInfo(const CelOutputBuffer &out) |
|
{ |
|
int uid, y; |
|
|
|
if ((!chrflag && !questlog) || gnScreenWidth >= SPANEL_WIDTH * 3) { |
|
uid = curruitem._iUid; |
|
DrawUTextBack(GlobalBackBuffer()); |
|
PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, 2, true, _(UniqueItemList[uid].UIName), COL_GOLD); |
|
DrawULine(out, 5); |
|
PrintItemPower(UniqueItemList[uid].UIPower1, &curruitem); |
|
y = 6 - UniqueItemList[uid].UINumPL + 8; |
|
PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y, true, tempstr, COL_WHITE); |
|
if (UniqueItemList[uid].UINumPL > 1) { |
|
PrintItemPower(UniqueItemList[uid].UIPower2, &curruitem); |
|
PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 2, true, tempstr, COL_WHITE); |
|
} |
|
if (UniqueItemList[uid].UINumPL > 2) { |
|
PrintItemPower(UniqueItemList[uid].UIPower3, &curruitem); |
|
PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 4, true, tempstr, COL_WHITE); |
|
} |
|
if (UniqueItemList[uid].UINumPL > 3) { |
|
PrintItemPower(UniqueItemList[uid].UIPower4, &curruitem); |
|
PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 6, true, tempstr, COL_WHITE); |
|
} |
|
if (UniqueItemList[uid].UINumPL > 4) { |
|
PrintItemPower(UniqueItemList[uid].UIPower5, &curruitem); |
|
PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 8, true, tempstr, COL_WHITE); |
|
} |
|
if (UniqueItemList[uid].UINumPL > 5) { |
|
PrintItemPower(UniqueItemList[uid].UIPower6, &curruitem); |
|
PrintUString(out, 0 + RIGHT_PANEL - SPANEL_WIDTH, y + 10, true, tempstr, COL_WHITE); |
|
} |
|
} |
|
} |
|
|
|
void PrintItemMisc(ItemStruct *x) |
|
{ |
|
if (x->_iMiscId == IMISC_SCROLL) { |
|
strcpy(tempstr, _("Right-click to read")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId == IMISC_SCROLLT) { |
|
strcpy(tempstr, _("Right-click to read, then")); |
|
AddPanelString(tempstr, true); |
|
strcpy(tempstr, _("left-click to target")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId >= IMISC_USEFIRST && x->_iMiscId <= IMISC_USELAST) { |
|
PrintItemOil(x->_iMiscId); |
|
strcpy(tempstr, _("Right-click to use")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId > IMISC_OILFIRST && x->_iMiscId < IMISC_OILLAST) { |
|
PrintItemOil(x->_iMiscId); |
|
strcpy(tempstr, _("Right click to use")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId > IMISC_RUNEFIRST && x->_iMiscId < IMISC_RUNELAST) { |
|
PrintItemOil(x->_iMiscId); |
|
strcpy(tempstr, _("Right click to use")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId == IMISC_BOOK) { |
|
strcpy(tempstr, _("Right-click to read")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId == IMISC_NOTE) { |
|
strcpy(tempstr, _("Right click to read")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId == IMISC_MAPOFDOOM) { |
|
strcpy(tempstr, _("Right-click to view")); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId == IMISC_EAR) { |
|
sprintf(tempstr, _("Level: %i"), x->_ivalue); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId == IMISC_AURIC) { |
|
strcpy(tempstr, _("Doubles gold capacity")); |
|
AddPanelString(tempstr, true); |
|
} |
|
} |
|
|
|
static void PrintItemInfo(ItemStruct *x) |
|
{ |
|
PrintItemMisc(x); |
|
uint8_t str = x->_iMinStr; |
|
uint8_t dex = x->_iMinDex; |
|
uint8_t mag = x->_iMinMag; |
|
if (str != 0 || mag != 0 || dex != 0) { |
|
strcpy(tempstr, _("Required:")); |
|
if (str) |
|
sprintf(tempstr + strlen(tempstr), _(" %i Str"), str); |
|
if (mag) |
|
sprintf(tempstr + strlen(tempstr), _(" %i Mag"), mag); |
|
if (dex) |
|
sprintf(tempstr + strlen(tempstr), _(" %i Dex"), dex); |
|
AddPanelString(tempstr, true); |
|
} |
|
pinfoflag = true; |
|
} |
|
|
|
void PrintItemDetails(ItemStruct *x) |
|
{ |
|
if (x->_iClass == ICLASS_WEAPON) { |
|
if (x->_iMinDam == x->_iMaxDam) { |
|
if (x->_iMaxDur == DUR_INDESTRUCTIBLE) |
|
sprintf(tempstr, _("damage: %i Indestructible"), x->_iMinDam); |
|
else |
|
sprintf(tempstr, _("damage: %i Dur: %i/%i"), x->_iMinDam, x->_iDurability, x->_iMaxDur); |
|
} else { |
|
if (x->_iMaxDur == DUR_INDESTRUCTIBLE) |
|
sprintf(tempstr, _("damage: %i-%i Indestructible"), x->_iMinDam, x->_iMaxDam); |
|
else |
|
sprintf(tempstr, _("damage: %i-%i Dur: %i/%i"), x->_iMinDam, x->_iMaxDam, x->_iDurability, x->_iMaxDur); |
|
} |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iClass == ICLASS_ARMOR) { |
|
if (x->_iMaxDur == DUR_INDESTRUCTIBLE) |
|
sprintf(tempstr, _("armor: %i Indestructible"), x->_iAC); |
|
else |
|
sprintf(tempstr, _("armor: %i Dur: %i/%i"), x->_iAC, x->_iDurability, x->_iMaxDur); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMiscId == IMISC_STAFF && x->_iMaxCharges != 0) { |
|
if (x->_iMinDam == x->_iMaxDam) |
|
sprintf(tempstr, _("dam: %i Dur: %i/%i"), x->_iMinDam, x->_iDurability, x->_iMaxDur); |
|
else |
|
sprintf(tempstr, _("dam: %i-%i Dur: %i/%i"), x->_iMinDam, x->_iMaxDam, x->_iDurability, x->_iMaxDur); |
|
sprintf(tempstr, _("Charges: %i/%i"), x->_iCharges, x->_iMaxCharges); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iPrePower != -1) { |
|
PrintItemPower(x->_iPrePower, x); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iSufPower != -1) { |
|
PrintItemPower(x->_iSufPower, x); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMagical == ITEM_QUALITY_UNIQUE) { |
|
AddPanelString(_("unique item"), true); |
|
uitemflag = true; |
|
curruitem = *x; |
|
} |
|
PrintItemInfo(x); |
|
} |
|
|
|
void PrintItemDur(ItemStruct *x) |
|
{ |
|
if (x->_iClass == ICLASS_WEAPON) { |
|
if (x->_iMinDam == x->_iMaxDam) { |
|
if (x->_iMaxDur == DUR_INDESTRUCTIBLE) |
|
sprintf(tempstr, _("damage: %i Indestructible"), x->_iMinDam); |
|
else |
|
sprintf(tempstr, _("damage: %i Dur: %i/%i"), x->_iMinDam, x->_iDurability, x->_iMaxDur); |
|
} else { |
|
if (x->_iMaxDur == DUR_INDESTRUCTIBLE) |
|
sprintf(tempstr, _("damage: %i-%i Indestructible"), x->_iMinDam, x->_iMaxDam); |
|
else |
|
sprintf(tempstr, _("damage: %i-%i Dur: %i/%i"), x->_iMinDam, x->_iMaxDam, x->_iDurability, x->_iMaxDur); |
|
} |
|
AddPanelString(tempstr, true); |
|
if (x->_iMiscId == IMISC_STAFF && x->_iMaxCharges) { |
|
sprintf(tempstr, _("Charges: %i/%i"), x->_iCharges, x->_iMaxCharges); |
|
AddPanelString(tempstr, true); |
|
} |
|
if (x->_iMagical != ITEM_QUALITY_NORMAL) |
|
AddPanelString(_("Not Identified"), true); |
|
} |
|
if (x->_iClass == ICLASS_ARMOR) { |
|
if (x->_iMaxDur == DUR_INDESTRUCTIBLE) |
|
sprintf(tempstr, _("armor: %i Indestructible"), x->_iAC); |
|
else |
|
sprintf(tempstr, _("armor: %i Dur: %i/%i"), x->_iAC, x->_iDurability, x->_iMaxDur); |
|
AddPanelString(tempstr, true); |
|
if (x->_iMagical != ITEM_QUALITY_NORMAL) |
|
AddPanelString(_("Not Identified"), true); |
|
if (x->_iMiscId == IMISC_STAFF && x->_iMaxCharges) { |
|
sprintf(tempstr, _("Charges: %i/%i"), x->_iCharges, x->_iMaxCharges); |
|
AddPanelString(tempstr, true); |
|
} |
|
} |
|
if (x->_itype == ITYPE_RING || x->_itype == ITYPE_AMULET) |
|
AddPanelString(_("Not Identified"), true); |
|
PrintItemInfo(x); |
|
} |
|
|
|
void UseItem(int p, item_misc_id Mid, spell_id spl) |
|
{ |
|
int l, j; |
|
|
|
switch (Mid) { |
|
case IMISC_HEAL: |
|
case IMISC_FOOD: |
|
j = plr[p]._pMaxHP >> 8; |
|
l = ((j >> 1) + GenerateRnd(j)) << 6; |
|
if (plr[p]._pClass == HeroClass::Warrior || plr[p]._pClass == HeroClass::Barbarian) |
|
l *= 2; |
|
if (plr[p]._pClass == HeroClass::Rogue || plr[p]._pClass == HeroClass::Monk || plr[p]._pClass == HeroClass::Bard) |
|
l += l / 2; |
|
plr[p]._pHitPoints = std::min(l + plr[p]._pHitPoints, plr[p]._pMaxHP); |
|
plr[p]._pHPBase = std::min(l + plr[p]._pHPBase, plr[p]._pMaxHPBase); |
|
drawhpflag = true; |
|
break; |
|
case IMISC_FULLHEAL: |
|
plr[p]._pHitPoints = plr[p]._pMaxHP; |
|
plr[p]._pHPBase = plr[p]._pMaxHPBase; |
|
drawhpflag = true; |
|
break; |
|
case IMISC_MANA: |
|
j = plr[p]._pMaxMana >> 8; |
|
l = ((j >> 1) + GenerateRnd(j)) << 6; |
|
if (plr[p]._pClass == HeroClass::Sorcerer) |
|
l *= 2; |
|
if (plr[p]._pClass == HeroClass::Rogue || plr[p]._pClass == HeroClass::Monk || plr[p]._pClass == HeroClass::Bard) |
|
l += l / 2; |
|
if ((plr[p]._pIFlags & ISPL_NOMANA) == 0) { |
|
plr[p]._pMana = std::min(l + plr[p]._pMana, plr[p]._pMaxMana); |
|
plr[p]._pManaBase = std::min(l + plr[p]._pManaBase, plr[p]._pMaxManaBase); |
|
drawmanaflag = true; |
|
} |
|
break; |
|
case IMISC_FULLMANA: |
|
if ((plr[p]._pIFlags & ISPL_NOMANA) == 0) { |
|
plr[p]._pMana = plr[p]._pMaxMana; |
|
plr[p]._pManaBase = plr[p]._pMaxManaBase; |
|
drawmanaflag = true; |
|
} |
|
break; |
|
case IMISC_ELIXSTR: |
|
ModifyPlrStr(p, 1); |
|
break; |
|
case IMISC_ELIXMAG: |
|
ModifyPlrMag(p, 1); |
|
if (gbIsHellfire) { |
|
plr[p]._pMana = plr[p]._pMaxMana; |
|
plr[p]._pManaBase = plr[p]._pMaxManaBase; |
|
drawmanaflag = true; |
|
} |
|
break; |
|
case IMISC_ELIXDEX: |
|
ModifyPlrDex(p, 1); |
|
break; |
|
case IMISC_ELIXVIT: |
|
ModifyPlrVit(p, 1); |
|
if (gbIsHellfire) { |
|
plr[p]._pHitPoints = plr[p]._pMaxHP; |
|
plr[p]._pHPBase = plr[p]._pMaxHPBase; |
|
drawhpflag = true; |
|
} |
|
break; |
|
case IMISC_REJUV: |
|
j = plr[p]._pMaxHP >> 8; |
|
l = ((j / 2) + GenerateRnd(j)) << 6; |
|
if (plr[p]._pClass == HeroClass::Warrior || plr[p]._pClass == HeroClass::Barbarian) |
|
l *= 2; |
|
if (plr[p]._pClass == HeroClass::Rogue) |
|
l += l / 2; |
|
plr[p]._pHitPoints = std::min(plr[p]._pHitPoints + l, plr[p]._pMaxHP); |
|
plr[p]._pHPBase = std::min(plr[p]._pHPBase + l, plr[p]._pMaxHPBase); |
|
drawhpflag = true; |
|
j = plr[p]._pMaxMana >> 8; |
|
l = ((j / 2) + GenerateRnd(j)) << 6; |
|
if (plr[p]._pClass == HeroClass::Sorcerer) |
|
l *= 2; |
|
if (plr[p]._pClass == HeroClass::Rogue) |
|
l += l / 2; |
|
if ((plr[p]._pIFlags & ISPL_NOMANA) == 0) { |
|
plr[p]._pMana = std::min(plr[p]._pMana + l, plr[p]._pMaxMana); |
|
plr[p]._pManaBase = std::min(plr[p]._pManaBase + l, plr[p]._pMaxManaBase); |
|
drawmanaflag = true; |
|
} |
|
break; |
|
case IMISC_FULLREJUV: |
|
plr[p]._pHitPoints = plr[p]._pMaxHP; |
|
plr[p]._pHPBase = plr[p]._pMaxHPBase; |
|
drawhpflag = true; |
|
if ((plr[p]._pIFlags & ISPL_NOMANA) == 0) { |
|
plr[p]._pMana = plr[p]._pMaxMana; |
|
plr[p]._pManaBase = plr[p]._pMaxManaBase; |
|
drawmanaflag = true; |
|
} |
|
break; |
|
case IMISC_SCROLL: |
|
if (spelldata[spl].sTargeted) { |
|
plr[p]._pTSpell = spl; |
|
plr[p]._pTSplType = RSPLTYPE_INVALID; |
|
if (p == myplr) |
|
NewCursor(CURSOR_TELEPORT); |
|
} else { |
|
ClrPlrPath(&plr[p]); |
|
plr[p]._pSpell = spl; |
|
plr[p]._pSplType = RSPLTYPE_INVALID; |
|
plr[p]._pSplFrom = 3; |
|
plr[p].destAction = ACTION_SPELL; |
|
plr[p].destParam1 = cursmx; |
|
plr[p].destParam2 = cursmy; |
|
if (p == myplr && spl == SPL_NOVA) |
|
NetSendCmdLoc(myplr, true, CMD_NOVA, { cursmx, cursmy }); |
|
} |
|
break; |
|
case IMISC_SCROLLT: |
|
if (spelldata[spl].sTargeted) { |
|
plr[p]._pTSpell = spl; |
|
plr[p]._pTSplType = RSPLTYPE_INVALID; |
|
if (p == myplr) |
|
NewCursor(CURSOR_TELEPORT); |
|
} else { |
|
ClrPlrPath(&plr[p]); |
|
plr[p]._pSpell = spl; |
|
plr[p]._pSplType = RSPLTYPE_INVALID; |
|
plr[p]._pSplFrom = 3; |
|
plr[p].destAction = ACTION_SPELL; |
|
plr[p].destParam1 = cursmx; |
|
plr[p].destParam2 = cursmy; |
|
} |
|
break; |
|
case IMISC_BOOK: |
|
plr[p]._pMemSpells |= GetSpellBitmask(spl); |
|
if (plr[p]._pSplLvl[spl] < MAX_SPELL_LEVEL) |
|
plr[p]._pSplLvl[spl]++; |
|
if ((plr[p]._pIFlags & ISPL_NOMANA) == 0) { |
|
plr[p]._pMana += spelldata[spl].sManaCost << 6; |
|
plr[p]._pMana = std::min(plr[p]._pMana, plr[p]._pMaxMana); |
|
plr[p]._pManaBase += spelldata[spl].sManaCost << 6; |
|
plr[p]._pManaBase = std::min(plr[p]._pManaBase, plr[p]._pMaxManaBase); |
|
} |
|
if (p == myplr) |
|
CalcPlrBookVals(p); |
|
drawmanaflag = true; |
|
break; |
|
case IMISC_MAPOFDOOM: |
|
doom_init(); |
|
break; |
|
case IMISC_OILACC: |
|
case IMISC_OILMAST: |
|
case IMISC_OILSHARP: |
|
case IMISC_OILDEATH: |
|
case IMISC_OILSKILL: |
|
case IMISC_OILBSMTH: |
|
case IMISC_OILFORT: |
|
case IMISC_OILPERM: |
|
case IMISC_OILHARD: |
|
case IMISC_OILIMP: |
|
plr[p]._pOilType = Mid; |
|
if (p != myplr) { |
|
return; |
|
} |
|
if (sbookflag) { |
|
sbookflag = false; |
|
} |
|
if (!invflag) { |
|
invflag = true; |
|
} |
|
NewCursor(CURSOR_OIL); |
|
break; |
|
case IMISC_SPECELIX: |
|
ModifyPlrStr(p, 3); |
|
ModifyPlrMag(p, 3); |
|
ModifyPlrDex(p, 3); |
|
ModifyPlrVit(p, 3); |
|
break; |
|
case IMISC_RUNEF: |
|
plr[p]._pTSpell = SPL_RUNEFIRE; |
|
plr[p]._pTSplType = RSPLTYPE_INVALID; |
|
if (p == myplr) |
|
NewCursor(CURSOR_TELEPORT); |
|
break; |
|
case IMISC_RUNEL: |
|
plr[p]._pTSpell = SPL_RUNELIGHT; |
|
plr[p]._pTSplType = RSPLTYPE_INVALID; |
|
if (p == myplr) |
|
NewCursor(CURSOR_TELEPORT); |
|
break; |
|
case IMISC_GR_RUNEL: |
|
plr[p]._pTSpell = SPL_RUNENOVA; |
|
plr[p]._pTSplType = RSPLTYPE_INVALID; |
|
if (p == myplr) |
|
NewCursor(CURSOR_TELEPORT); |
|
break; |
|
case IMISC_GR_RUNEF: |
|
plr[p]._pTSpell = SPL_RUNEIMMOLAT; |
|
plr[p]._pTSplType = RSPLTYPE_INVALID; |
|
if (p == myplr) |
|
NewCursor(CURSOR_TELEPORT); |
|
break; |
|
case IMISC_RUNES: |
|
plr[p]._pTSpell = SPL_RUNESTONE; |
|
plr[p]._pTSplType = RSPLTYPE_INVALID; |
|
if (p == myplr) |
|
NewCursor(CURSOR_TELEPORT); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
bool StoreStatOk(ItemStruct *h) |
|
{ |
|
bool sf; |
|
|
|
sf = true; |
|
if (plr[myplr]._pStrength < h->_iMinStr) |
|
sf = false; |
|
if (plr[myplr]._pMagic < h->_iMinMag) |
|
sf = false; |
|
if (plr[myplr]._pDexterity < h->_iMinDex) |
|
sf = false; |
|
|
|
return sf; |
|
} |
|
|
|
bool SmithItemOk(int i) |
|
{ |
|
bool rv; |
|
|
|
rv = true; |
|
if (AllItemsList[i].itype == ITYPE_MISC) |
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_GOLD) |
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_STAFF && (!gbIsHellfire || AllItemsList[i].iSpell)) |
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_RING) |
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_AMULET) |
|
rv = false; |
|
|
|
return rv; |
|
} |
|
|
|
int RndSmithItem(int lvl) |
|
{ |
|
int i, ri; |
|
int ril[512]; |
|
|
|
ri = 0; |
|
for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
if (AllItemsList[i].iRnd != IDROP_NEVER && SmithItemOk(i) && lvl >= AllItemsList[i].iMinMLvl |
|
&& ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
if (AllItemsList[i].iRnd == IDROP_DOUBLE |
|
&& ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
} |
|
} |
|
|
|
return ril[GenerateRnd(ri)] + 1; |
|
} |
|
|
|
void BubbleSwapItem(ItemStruct *a, ItemStruct *b) |
|
{ |
|
ItemStruct h; |
|
|
|
h = *a; |
|
*a = *b; |
|
*b = h; |
|
} |
|
|
|
void SortSmith() |
|
{ |
|
int j, k; |
|
bool sorted; |
|
|
|
j = 0; |
|
while (!smithitem[j + 1].isEmpty()) { |
|
j++; |
|
} |
|
|
|
sorted = false; |
|
while (j > 0 && !sorted) { |
|
sorted = true; |
|
for (k = 0; k < j; k++) { |
|
if (smithitem[k].IDidx > smithitem[k + 1].IDidx) { |
|
BubbleSwapItem(&smithitem[k], &smithitem[k + 1]); |
|
sorted = false; |
|
} |
|
} |
|
j--; |
|
} |
|
} |
|
|
|
void SpawnSmith(int lvl) |
|
{ |
|
int i, iCnt, idata; |
|
int maxValue, maxItems; |
|
|
|
ItemStruct holditem; |
|
holditem = items[0]; |
|
|
|
if (gbIsHellfire) { |
|
maxValue = 200000; |
|
maxItems = 25; |
|
} else { |
|
maxValue = 140000; |
|
maxItems = 20; |
|
} |
|
|
|
iCnt = GenerateRnd(maxItems - 10) + 10; |
|
for (i = 0; i < iCnt; i++) { |
|
do { |
|
memset(&items[0], 0, sizeof(*items)); |
|
items[0]._iSeed = AdvanceRndSeed(); |
|
SetRndSeed(items[0]._iSeed); |
|
idata = RndSmithItem(lvl) - 1; |
|
GetItemAttrs(0, idata, lvl); |
|
} while (items[0]._iIvalue > maxValue); |
|
smithitem[i] = items[0]; |
|
smithitem[i]._iCreateInfo = lvl | CF_SMITH; |
|
smithitem[i]._iIdentified = true; |
|
smithitem[i]._iStatFlag = StoreStatOk(&smithitem[i]); |
|
} |
|
for (i = iCnt; i < SMITH_ITEMS; i++) |
|
smithitem[i]._itype = ITYPE_NONE; |
|
|
|
SortSmith(); |
|
items[0] = holditem; |
|
} |
|
|
|
bool PremiumItemOk(int i) |
|
{ |
|
bool rv; |
|
|
|
rv = true; |
|
if (AllItemsList[i].itype == ITYPE_MISC) |
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_GOLD) |
|
rv = false; |
|
if (!gbIsHellfire && AllItemsList[i].itype == ITYPE_STAFF) |
|
rv = false; |
|
|
|
if (gbIsMultiplayer) { |
|
if (AllItemsList[i].iMiscId == IMISC_OILOF) |
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_RING) |
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_AMULET) |
|
rv = false; |
|
} |
|
|
|
return rv; |
|
} |
|
|
|
int RndPremiumItem(int minlvl, int maxlvl) |
|
{ |
|
int i, ri; |
|
int ril[512]; |
|
|
|
ri = 0; |
|
for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
if (AllItemsList[i].iRnd != IDROP_NEVER) { |
|
if (PremiumItemOk(i)) { |
|
if (AllItemsList[i].iMinMLvl >= minlvl && AllItemsList[i].iMinMLvl <= maxlvl && ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return ril[GenerateRnd(ri)] + 1; |
|
} |
|
|
|
static void SpawnOnePremium(int i, int plvl, int myplr) |
|
{ |
|
int ivalue = 0; |
|
bool keepgoing = false; |
|
ItemStruct holditem = items[0]; |
|
|
|
int strength = std::max(plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Strength), plr[myplr]._pStrength); |
|
int dexterity = std::max(plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Dexterity), plr[myplr]._pDexterity); |
|
int magic = std::max(plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Magic), plr[myplr]._pMagic); |
|
strength *= 1.2; |
|
dexterity *= 1.2; |
|
magic *= 1.2; |
|
|
|
plvl = clamp(plvl, 1, 30); |
|
|
|
int count = 0; |
|
|
|
do { |
|
keepgoing = false; |
|
memset(&items[0], 0, sizeof(*items)); |
|
items[0]._iSeed = AdvanceRndSeed(); |
|
SetRndSeed(items[0]._iSeed); |
|
int itype = RndPremiumItem(plvl / 4, plvl) - 1; |
|
GetItemAttrs(0, itype, plvl); |
|
GetItemBonus(0, plvl / 2, plvl, true, !gbIsHellfire); |
|
|
|
if (!gbIsHellfire) { |
|
if (items[0]._iIvalue > 140000) { |
|
keepgoing = true; // prevent breaking the do/while loop too early by failing hellfire's condition in while |
|
continue; |
|
} |
|
break; |
|
} |
|
|
|
switch (items[0]._itype) { |
|
case ITYPE_LARMOR: |
|
case ITYPE_MARMOR: |
|
case ITYPE_HARMOR: { |
|
const auto mostValuablePlayerArmor = plr[myplr].GetMostValuableItem( |
|
[](const ItemStruct &item) { |
|
return item._itype == ITYPE_LARMOR |
|
|| item._itype == ITYPE_MARMOR |
|
|| item._itype == ITYPE_HARMOR; |
|
}); |
|
|
|
ivalue = mostValuablePlayerArmor == nullptr ? 0 : mostValuablePlayerArmor->_iIvalue; |
|
break; |
|
} |
|
case ITYPE_SHIELD: |
|
case ITYPE_AXE: |
|
case ITYPE_BOW: |
|
case ITYPE_MACE: |
|
case ITYPE_SWORD: |
|
case ITYPE_HELM: |
|
case ITYPE_STAFF: |
|
case ITYPE_RING: |
|
case ITYPE_AMULET: { |
|
const auto mostValuablePlayerItem = plr[myplr].GetMostValuableItem( |
|
[](const ItemStruct &item) { return item._itype == items[0]._itype; }); |
|
|
|
ivalue = mostValuablePlayerItem == nullptr ? 0 : mostValuablePlayerItem->_iIvalue; |
|
break; |
|
} |
|
default: |
|
ivalue = 0; |
|
break; |
|
} |
|
ivalue *= 0.8; |
|
|
|
count++; |
|
} while (keepgoing |
|
|| (( |
|
items[0]._iIvalue > 200000 |
|
|| items[0]._iMinStr > strength |
|
|| items[0]._iMinMag > magic |
|
|| items[0]._iMinDex > dexterity |
|
|| items[0]._iIvalue < ivalue) |
|
&& count < 150)); |
|
premiumitems[i] = items[0]; |
|
premiumitems[i]._iCreateInfo = plvl | CF_SMITHPREMIUM; |
|
premiumitems[i]._iIdentified = true; |
|
premiumitems[i]._iStatFlag = StoreStatOk(&premiumitems[i]); |
|
items[0] = holditem; |
|
} |
|
|
|
void SpawnPremium(int pnum) |
|
{ |
|
int i; |
|
|
|
int lvl = plr[pnum]._pLevel; |
|
int maxItems = gbIsHellfire ? SMITH_PREMIUM_ITEMS : 6; |
|
if (numpremium < maxItems) { |
|
for (i = 0; i < maxItems; i++) { |
|
if (premiumitems[i].isEmpty()) { |
|
int plvl = premiumlevel + (gbIsHellfire ? premiumLvlAddHellfire[i] : premiumlvladd[i]); |
|
SpawnOnePremium(i, plvl, pnum); |
|
} |
|
} |
|
numpremium = maxItems; |
|
} |
|
while (premiumlevel < lvl) { |
|
premiumlevel++; |
|
if (gbIsHellfire) { |
|
premiumitems[0] = premiumitems[3]; |
|
premiumitems[1] = premiumitems[4]; |
|
premiumitems[2] = premiumitems[5]; |
|
premiumitems[3] = premiumitems[6]; |
|
premiumitems[4] = premiumitems[7]; |
|
premiumitems[5] = premiumitems[8]; |
|
premiumitems[6] = premiumitems[9]; |
|
premiumitems[7] = premiumitems[10]; |
|
premiumitems[8] = premiumitems[11]; |
|
premiumitems[9] = premiumitems[12]; |
|
SpawnOnePremium(10, premiumlevel + premiumLvlAddHellfire[10], pnum); |
|
premiumitems[11] = premiumitems[13]; |
|
SpawnOnePremium(12, premiumlevel + premiumLvlAddHellfire[12], pnum); |
|
premiumitems[13] = premiumitems[14]; |
|
SpawnOnePremium(14, premiumlevel + premiumLvlAddHellfire[14], pnum); |
|
} else { |
|
premiumitems[0] = premiumitems[2]; |
|
premiumitems[1] = premiumitems[3]; |
|
premiumitems[2] = premiumitems[4]; |
|
SpawnOnePremium(3, premiumlevel + premiumlvladd[3], pnum); |
|
premiumitems[4] = premiumitems[5]; |
|
SpawnOnePremium(5, premiumlevel + premiumlvladd[5], pnum); |
|
} |
|
} |
|
} |
|
|
|
bool WitchItemOk(int i) |
|
{ |
|
bool rv; |
|
|
|
rv = false; |
|
if (AllItemsList[i].itype == ITYPE_MISC) |
|
rv = true; |
|
if (AllItemsList[i].itype == ITYPE_STAFF) |
|
rv = true; |
|
if (AllItemsList[i].iMiscId == IMISC_MANA) |
|
rv = false; |
|
if (AllItemsList[i].iMiscId == IMISC_FULLMANA) |
|
rv = false; |
|
if (AllItemsList[i].iSpell == SPL_TOWN) |
|
rv = false; |
|
if (AllItemsList[i].iMiscId == IMISC_FULLHEAL) |
|
rv = false; |
|
if (AllItemsList[i].iMiscId == IMISC_HEAL) |
|
rv = false; |
|
if (AllItemsList[i].iMiscId > IMISC_OILFIRST && AllItemsList[i].iMiscId < IMISC_OILLAST) |
|
rv = false; |
|
if (AllItemsList[i].iSpell == SPL_RESURRECT && !gbIsMultiplayer) |
|
rv = false; |
|
if (AllItemsList[i].iSpell == SPL_HEALOTHER && !gbIsMultiplayer) |
|
rv = false; |
|
|
|
return rv; |
|
} |
|
|
|
int RndWitchItem(int lvl) |
|
{ |
|
int i, ri; |
|
int ril[512]; |
|
|
|
ri = 0; |
|
for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
if (AllItemsList[i].iRnd != IDROP_NEVER && WitchItemOk(i) && lvl >= AllItemsList[i].iMinMLvl |
|
&& ri < 512) { |
|
|
|
ril[ri] = i; |
|
ri++; |
|
} |
|
} |
|
|
|
return ril[GenerateRnd(ri)] + 1; |
|
} |
|
|
|
void SortWitch() |
|
{ |
|
int j, k; |
|
bool sorted; |
|
|
|
j = 3; |
|
while (!witchitem[j + 1].isEmpty()) { |
|
j++; |
|
} |
|
|
|
sorted = false; |
|
while (j > 3 && !sorted) { |
|
sorted = true; |
|
for (k = 3; k < j; k++) { |
|
if (witchitem[k].IDidx > witchitem[k + 1].IDidx) { |
|
BubbleSwapItem(&witchitem[k], &witchitem[k + 1]); |
|
sorted = false; |
|
} |
|
} |
|
j--; |
|
} |
|
} |
|
|
|
void WitchBookLevel(int ii) |
|
{ |
|
int slvl; |
|
|
|
if (witchitem[ii]._iMiscId == IMISC_BOOK) { |
|
witchitem[ii]._iMinMag = spelldata[witchitem[ii]._iSpell].sMinInt; |
|
slvl = plr[myplr]._pSplLvl[witchitem[ii]._iSpell]; |
|
while (slvl) { |
|
witchitem[ii]._iMinMag += 20 * witchitem[ii]._iMinMag / 100; |
|
slvl--; |
|
if (witchitem[ii]._iMinMag + 20 * witchitem[ii]._iMinMag / 100 > 255) { |
|
witchitem[ii]._iMinMag = 255; |
|
slvl = 0; |
|
} |
|
} |
|
} |
|
} |
|
|
|
void SpawnWitch(int lvl) |
|
{ |
|
int i, j, iCnt; |
|
int idata, maxlvl, maxValue; |
|
|
|
j = 3; |
|
|
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_MANA, 1); |
|
witchitem[0] = items[0]; |
|
witchitem[0]._iCreateInfo = lvl; |
|
witchitem[0]._iStatFlag = true; |
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_FULLMANA, 1); |
|
witchitem[1] = items[0]; |
|
witchitem[1]._iCreateInfo = lvl; |
|
witchitem[1]._iStatFlag = true; |
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_PORTAL, 1); |
|
witchitem[2] = items[0]; |
|
witchitem[2]._iCreateInfo = lvl; |
|
witchitem[2]._iStatFlag = true; |
|
|
|
if (gbIsHellfire) { |
|
iCnt = GenerateRnd(WITCH_ITEMS - 10) + 10; |
|
maxValue = 200000; |
|
|
|
int bCnt; |
|
int books = GenerateRnd(4); |
|
for (i = 114, bCnt = 0; i <= 117 && bCnt < books; ++i) { |
|
if (WitchItemOk(i) |
|
&& lvl >= AllItemsList[i].iMinMLvl) { |
|
memset(&items[0], 0, sizeof(*items)); |
|
items[0]._iSeed = AdvanceRndSeed(); |
|
SetRndSeed(items[0]._iSeed); |
|
GenerateRnd(1); |
|
|
|
GetItemAttrs(0, i, lvl); |
|
witchitem[j] = items[0]; |
|
witchitem[j]._iCreateInfo = lvl | CF_WITCH; |
|
witchitem[j]._iIdentified = true; |
|
WitchBookLevel(j); |
|
witchitem[j]._iStatFlag = StoreStatOk(&witchitem[j]); |
|
j++; |
|
bCnt++; |
|
} |
|
} |
|
} else { |
|
iCnt = GenerateRnd(WITCH_ITEMS - 12) + 10; |
|
maxValue = 140000; |
|
} |
|
|
|
for (i = j; i < iCnt; i++) { |
|
do { |
|
memset(&items[0], 0, sizeof(*items)); |
|
items[0]._iSeed = AdvanceRndSeed(); |
|
SetRndSeed(items[0]._iSeed); |
|
idata = RndWitchItem(lvl) - 1; |
|
GetItemAttrs(0, idata, lvl); |
|
maxlvl = -1; |
|
if (GenerateRnd(100) <= 5) |
|
maxlvl = 2 * lvl; |
|
if (maxlvl == -1 && items[0]._iMiscId == IMISC_STAFF) |
|
maxlvl = 2 * lvl; |
|
if (maxlvl != -1) |
|
GetItemBonus(0, maxlvl / 2, maxlvl, true, true); |
|
} while (items[0]._iIvalue > maxValue); |
|
witchitem[i] = items[0]; |
|
witchitem[i]._iCreateInfo = lvl | CF_WITCH; |
|
witchitem[i]._iIdentified = true; |
|
WitchBookLevel(i); |
|
witchitem[i]._iStatFlag = StoreStatOk(&witchitem[i]); |
|
} |
|
|
|
for (i = iCnt; i < WITCH_ITEMS; i++) |
|
witchitem[i]._itype = ITYPE_NONE; |
|
|
|
SortWitch(); |
|
} |
|
|
|
int RndBoyItem(int lvl) |
|
{ |
|
int i, ri; |
|
int ril[512]; |
|
|
|
ri = 0; |
|
for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
if (AllItemsList[i].iRnd != IDROP_NEVER && PremiumItemOk(i) && lvl >= AllItemsList[i].iMinMLvl |
|
&& ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
} |
|
|
|
return ril[GenerateRnd(ri)] + 1; |
|
} |
|
|
|
void SpawnBoy(int lvl) |
|
{ |
|
int itype; |
|
|
|
int ivalue = 0; |
|
bool keepgoing = false; |
|
int count = 0; |
|
|
|
HeroClass pc = plr[myplr]._pClass; |
|
int strength = std::max(plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Strength), plr[myplr]._pStrength); |
|
int dexterity = std::max(plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Dexterity), plr[myplr]._pDexterity); |
|
int magic = std::max(plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Magic), plr[myplr]._pMagic); |
|
strength *= 1.2; |
|
dexterity *= 1.2; |
|
magic *= 1.2; |
|
|
|
if (boylevel < (lvl / 2) || boyitem.isEmpty()) { |
|
do { |
|
keepgoing = false; |
|
memset(&items[0], 0, sizeof(*items)); |
|
items[0]._iSeed = AdvanceRndSeed(); |
|
SetRndSeed(items[0]._iSeed); |
|
itype = RndBoyItem(lvl) - 1; |
|
GetItemAttrs(0, itype, lvl); |
|
GetItemBonus(0, lvl, 2 * lvl, true, true); |
|
|
|
if (!gbIsHellfire) { |
|
if (items[0]._iIvalue > 90000) { |
|
keepgoing = true; // prevent breaking the do/while loop too early by failing hellfire's condition in while |
|
continue; |
|
} |
|
break; |
|
} |
|
|
|
ivalue = 0; |
|
|
|
int itemType = items[0]._itype; |
|
|
|
switch (itemType) { |
|
case ITYPE_LARMOR: |
|
case ITYPE_MARMOR: |
|
case ITYPE_HARMOR: { |
|
const auto mostValuablePlayerArmor = plr[myplr].GetMostValuableItem( |
|
[](const ItemStruct &item) { |
|
return item._itype == ITYPE_LARMOR |
|
|| item._itype == ITYPE_MARMOR |
|
|| item._itype == ITYPE_HARMOR; |
|
}); |
|
|
|
ivalue = mostValuablePlayerArmor == nullptr ? 0 : mostValuablePlayerArmor->_iIvalue; |
|
break; |
|
} |
|
case ITYPE_SHIELD: |
|
case ITYPE_AXE: |
|
case ITYPE_BOW: |
|
case ITYPE_MACE: |
|
case ITYPE_SWORD: |
|
case ITYPE_HELM: |
|
case ITYPE_STAFF: |
|
case ITYPE_RING: |
|
case ITYPE_AMULET: { |
|
const auto mostValuablePlayerItem = plr[myplr].GetMostValuableItem( |
|
[itemType](const ItemStruct &item) { return item._itype == itemType; }); |
|
|
|
ivalue = mostValuablePlayerItem == nullptr ? 0 : mostValuablePlayerItem->_iIvalue; |
|
break; |
|
} |
|
} |
|
ivalue *= 0.8; |
|
|
|
count++; |
|
|
|
if (count < 200) { |
|
switch (pc) { |
|
case HeroClass::Warrior: |
|
if (itemType == ITYPE_BOW || itemType == ITYPE_STAFF) |
|
ivalue = INT_MAX; |
|
break; |
|
case HeroClass::Rogue: |
|
if (itemType == ITYPE_SWORD || itemType == ITYPE_STAFF || itemType == ITYPE_AXE || itemType == ITYPE_MACE || itemType == ITYPE_SHIELD) |
|
ivalue = INT_MAX; |
|
break; |
|
case HeroClass::Sorcerer: |
|
if (itemType == ITYPE_STAFF || itemType == ITYPE_AXE || itemType == ITYPE_BOW || itemType == ITYPE_MACE) |
|
ivalue = INT_MAX; |
|
break; |
|
case HeroClass::Monk: |
|
if (itemType == ITYPE_BOW || itemType == ITYPE_MARMOR || itemType == ITYPE_SHIELD || itemType == ITYPE_MACE) |
|
ivalue = INT_MAX; |
|
break; |
|
case HeroClass::Bard: |
|
if (itemType == ITYPE_AXE || itemType == ITYPE_MACE || itemType == ITYPE_STAFF) |
|
ivalue = INT_MAX; |
|
break; |
|
case HeroClass::Barbarian: |
|
if (itemType == ITYPE_BOW || itemType == ITYPE_STAFF) |
|
ivalue = INT_MAX; |
|
break; |
|
} |
|
} |
|
} while (keepgoing |
|
|| (( |
|
items[0]._iIvalue > 200000 |
|
|| items[0]._iMinStr > strength |
|
|| items[0]._iMinMag > magic |
|
|| items[0]._iMinDex > dexterity |
|
|| items[0]._iIvalue < ivalue) |
|
&& count < 250)); |
|
boyitem = items[0]; |
|
boyitem._iCreateInfo = lvl | CF_BOY; |
|
boyitem._iIdentified = true; |
|
boyitem._iStatFlag = StoreStatOk(&boyitem); |
|
boylevel = lvl / 2; |
|
} |
|
} |
|
|
|
bool HealerItemOk(int i) |
|
{ |
|
if (AllItemsList[i].itype != ITYPE_MISC) |
|
return false; |
|
|
|
if (AllItemsList[i].iMiscId == IMISC_SCROLL) |
|
return AllItemsList[i].iSpell == SPL_HEAL; |
|
if (AllItemsList[i].iMiscId == IMISC_SCROLLT) |
|
return AllItemsList[i].iSpell == SPL_HEALOTHER && gbIsMultiplayer; |
|
|
|
if (!gbIsMultiplayer) { |
|
if (AllItemsList[i].iMiscId == IMISC_ELIXSTR) |
|
return !gbIsHellfire || plr[myplr]._pBaseStr < plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Strength); |
|
if (AllItemsList[i].iMiscId == IMISC_ELIXMAG) |
|
return !gbIsHellfire || plr[myplr]._pBaseMag < plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Magic); |
|
if (AllItemsList[i].iMiscId == IMISC_ELIXDEX) |
|
return !gbIsHellfire || plr[myplr]._pBaseDex < plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Dexterity); |
|
if (AllItemsList[i].iMiscId == IMISC_ELIXVIT) |
|
return !gbIsHellfire || plr[myplr]._pBaseVit < plr[myplr].GetMaximumAttributeValue(CharacterAttribute::Vitality); |
|
} |
|
|
|
if (AllItemsList[i].iMiscId == IMISC_REJUV) |
|
return true; |
|
if (AllItemsList[i].iMiscId == IMISC_FULLREJUV) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
int RndHealerItem(int lvl) |
|
{ |
|
int i, ri; |
|
int ril[512]; |
|
|
|
ri = 0; |
|
for (i = 1; AllItemsList[i].iLoc != ILOC_INVALID; i++) { |
|
if (!IsItemAvailable(i)) |
|
continue; |
|
|
|
if (AllItemsList[i].iRnd != IDROP_NEVER && HealerItemOk(i) && lvl >= AllItemsList[i].iMinMLvl |
|
&& ri < 512) { |
|
ril[ri] = i; |
|
ri++; |
|
} |
|
} |
|
|
|
return ril[GenerateRnd(ri)] + 1; |
|
} |
|
|
|
void SortHealer() |
|
{ |
|
int j, k; |
|
bool sorted; |
|
|
|
j = 2; |
|
while (!healitem[j + 1].isEmpty()) { |
|
j++; |
|
} |
|
|
|
sorted = false; |
|
while (j > 2 && !sorted) { |
|
sorted = true; |
|
for (k = 2; k < j; k++) { |
|
if (healitem[k].IDidx > healitem[k + 1].IDidx) { |
|
BubbleSwapItem(&healitem[k], &healitem[k + 1]); |
|
sorted = false; |
|
} |
|
} |
|
j--; |
|
} |
|
} |
|
|
|
void SpawnHealer(int lvl) |
|
{ |
|
int i, nsi, srnd, itype; |
|
|
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_HEAL, 1); |
|
healitem[0] = items[0]; |
|
healitem[0]._iCreateInfo = lvl; |
|
healitem[0]._iStatFlag = true; |
|
|
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_FULLHEAL, 1); |
|
healitem[1] = items[0]; |
|
healitem[1]._iCreateInfo = lvl; |
|
healitem[1]._iStatFlag = true; |
|
|
|
if (gbIsMultiplayer) { |
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_RESURRECT, 1); |
|
healitem[2] = items[0]; |
|
healitem[2]._iCreateInfo = lvl; |
|
healitem[2]._iStatFlag = true; |
|
|
|
srnd = 3; |
|
} else { |
|
srnd = 2; |
|
} |
|
nsi = GenerateRnd(gbIsHellfire ? 10 : 8) + 10; |
|
for (i = srnd; i < nsi; i++) { |
|
memset(&items[0], 0, sizeof(*items)); |
|
items[0]._iSeed = AdvanceRndSeed(); |
|
SetRndSeed(items[0]._iSeed); |
|
itype = RndHealerItem(lvl) - 1; |
|
GetItemAttrs(0, itype, lvl); |
|
healitem[i] = items[0]; |
|
healitem[i]._iCreateInfo = lvl | CF_HEALER; |
|
healitem[i]._iIdentified = true; |
|
healitem[i]._iStatFlag = StoreStatOk(&healitem[i]); |
|
} |
|
for (i = nsi; i < 20; i++) { |
|
healitem[i]._itype = ITYPE_NONE; |
|
} |
|
SortHealer(); |
|
} |
|
|
|
void SpawnStoreGold() |
|
{ |
|
memset(&items[0], 0, sizeof(*items)); |
|
GetItemAttrs(0, IDI_GOLD, 1); |
|
golditem = items[0]; |
|
golditem._iStatFlag = true; |
|
} |
|
|
|
void RecreateSmithItem(int ii, int lvl, int iseed) |
|
{ |
|
SetRndSeed(iseed); |
|
int itype = RndSmithItem(lvl) - 1; |
|
GetItemAttrs(ii, itype, lvl); |
|
|
|
items[ii]._iSeed = iseed; |
|
items[ii]._iCreateInfo = lvl | CF_SMITH; |
|
items[ii]._iIdentified = true; |
|
} |
|
|
|
void RecreatePremiumItem(int ii, int plvl, int iseed) |
|
{ |
|
SetRndSeed(iseed); |
|
int itype = RndPremiumItem(plvl / 4, plvl) - 1; |
|
GetItemAttrs(ii, itype, plvl); |
|
GetItemBonus(ii, plvl / 2, plvl, true, !gbIsHellfire); |
|
|
|
items[ii]._iSeed = iseed; |
|
items[ii]._iCreateInfo = plvl | CF_SMITHPREMIUM; |
|
items[ii]._iIdentified = true; |
|
} |
|
|
|
void RecreateBoyItem(int ii, int lvl, int iseed) |
|
{ |
|
SetRndSeed(iseed); |
|
int itype = RndBoyItem(lvl) - 1; |
|
GetItemAttrs(ii, itype, lvl); |
|
GetItemBonus(ii, lvl, 2 * lvl, true, true); |
|
|
|
items[ii]._iSeed = iseed; |
|
items[ii]._iCreateInfo = lvl | CF_BOY; |
|
items[ii]._iIdentified = true; |
|
} |
|
|
|
void RecreateWitchItem(int ii, int idx, int lvl, int iseed) |
|
{ |
|
if (idx == IDI_MANA || idx == IDI_FULLMANA || idx == IDI_PORTAL) { |
|
GetItemAttrs(ii, idx, lvl); |
|
} else if (gbIsHellfire && idx >= 114 && idx <= 117) { |
|
SetRndSeed(iseed); |
|
GenerateRnd(1); |
|
GetItemAttrs(ii, idx, lvl); |
|
} else { |
|
SetRndSeed(iseed); |
|
int itype = RndWitchItem(lvl) - 1; |
|
GetItemAttrs(ii, itype, lvl); |
|
int iblvl = -1; |
|
if (GenerateRnd(100) <= 5) |
|
iblvl = 2 * lvl; |
|
if (iblvl == -1 && items[ii]._iMiscId == IMISC_STAFF) |
|
iblvl = 2 * lvl; |
|
if (iblvl != -1) |
|
GetItemBonus(ii, iblvl / 2, iblvl, true, true); |
|
} |
|
|
|
items[ii]._iSeed = iseed; |
|
items[ii]._iCreateInfo = lvl | CF_WITCH; |
|
items[ii]._iIdentified = true; |
|
} |
|
|
|
void RecreateHealerItem(int ii, int idx, int lvl, int iseed) |
|
{ |
|
int itype; |
|
|
|
if (idx == IDI_HEAL || idx == IDI_FULLHEAL || idx == IDI_RESURRECT) { |
|
GetItemAttrs(ii, idx, lvl); |
|
} else { |
|
SetRndSeed(iseed); |
|
itype = RndHealerItem(lvl) - 1; |
|
GetItemAttrs(ii, itype, lvl); |
|
} |
|
|
|
items[ii]._iSeed = iseed; |
|
items[ii]._iCreateInfo = lvl | CF_HEALER; |
|
items[ii]._iIdentified = true; |
|
} |
|
|
|
void RecreateTownItem(int ii, int idx, uint16_t icreateinfo, int iseed) |
|
{ |
|
if ((icreateinfo & CF_SMITH) != 0) |
|
RecreateSmithItem(ii, icreateinfo & CF_LEVEL, iseed); |
|
else if ((icreateinfo & CF_SMITHPREMIUM) != 0) |
|
RecreatePremiumItem(ii, icreateinfo & CF_LEVEL, iseed); |
|
else if ((icreateinfo & CF_BOY) != 0) |
|
RecreateBoyItem(ii, icreateinfo & CF_LEVEL, iseed); |
|
else if ((icreateinfo & CF_WITCH) != 0) |
|
RecreateWitchItem(ii, idx, icreateinfo & CF_LEVEL, iseed); |
|
else if ((icreateinfo & CF_HEALER) != 0) |
|
RecreateHealerItem(ii, idx, icreateinfo & CF_LEVEL, iseed); |
|
} |
|
|
|
void RecalcStoreStats() |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < SMITH_ITEMS; i++) { |
|
if (!smithitem[i].isEmpty()) { |
|
smithitem[i]._iStatFlag = StoreStatOk(&smithitem[i]); |
|
} |
|
} |
|
for (i = 0; i < SMITH_PREMIUM_ITEMS; i++) { |
|
if (!premiumitems[i].isEmpty()) { |
|
premiumitems[i]._iStatFlag = StoreStatOk(&premiumitems[i]); |
|
} |
|
} |
|
for (i = 0; i < 20; i++) { |
|
if (!witchitem[i].isEmpty()) { |
|
witchitem[i]._iStatFlag = StoreStatOk(&witchitem[i]); |
|
} |
|
} |
|
for (i = 0; i < 20; i++) { |
|
if (!healitem[i].isEmpty()) { |
|
healitem[i]._iStatFlag = StoreStatOk(&healitem[i]); |
|
} |
|
} |
|
boyitem._iStatFlag = StoreStatOk(&boyitem); |
|
} |
|
|
|
int ItemNoFlippy() |
|
{ |
|
int r = itemactive[numitems - 1]; |
|
items[r]._iAnimFrame = items[r]._iAnimLen; |
|
items[r]._iAnimFlag = false; |
|
items[r]._iSelFlag = 1; |
|
|
|
return r; |
|
} |
|
|
|
void CreateSpellBook(int x, int y, spell_id ispell, bool sendmsg, bool delta) |
|
{ |
|
int lvl = currlevel; |
|
|
|
if (gbIsHellfire) { |
|
lvl = GetSpellBookLevel(ispell) + 1; |
|
if (lvl < 1) { |
|
return; |
|
} |
|
} |
|
|
|
int idx = RndTypeItems(ITYPE_MISC, IMISC_BOOK, lvl); |
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
|
|
while (true) { |
|
memset(&items[ii], 0, sizeof(*items)); |
|
SetupAllItems(ii, idx, AdvanceRndSeed(), 2 * lvl, 1, true, false, delta); |
|
if (items[ii]._iMiscId == IMISC_BOOK && items[ii]._iSpell == ispell) |
|
break; |
|
} |
|
GetSuperItemSpace(x, y, ii); |
|
|
|
if (sendmsg) |
|
NetSendCmdDItem(false, ii); |
|
if (delta) |
|
DeltaAddItem(ii); |
|
} |
|
|
|
static void CreateMagicItem(int x, int y, int lvl, int imisc, int imid, int icurs, bool sendmsg, bool delta) |
|
{ |
|
if (numitems >= MAXITEMS) |
|
return; |
|
|
|
int ii = AllocateItem(); |
|
int idx = RndTypeItems(imisc, imid, lvl); |
|
|
|
while (true) { |
|
memset(&items[ii], 0, sizeof(*items)); |
|
SetupAllItems(ii, idx, AdvanceRndSeed(), 2 * lvl, 1, true, false, delta); |
|
if (items[ii]._iCurs == icurs) |
|
break; |
|
|
|
idx = RndTypeItems(imisc, imid, lvl); |
|
} |
|
GetSuperItemSpace(x, y, ii); |
|
|
|
if (sendmsg) |
|
NetSendCmdDItem(false, ii); |
|
if (delta) |
|
DeltaAddItem(ii); |
|
} |
|
|
|
void CreateMagicArmor(int x, int y, int imisc, int icurs, bool sendmsg, bool delta) |
|
{ |
|
int lvl = items_get_currlevel(); |
|
CreateMagicItem(x, y, lvl, imisc, IMISC_NONE, icurs, sendmsg, delta); |
|
} |
|
|
|
void CreateAmulet(int x, int y, int lvl, bool sendmsg, bool delta) |
|
{ |
|
CreateMagicItem(x, y, lvl, ITYPE_AMULET, IMISC_AMULET, ICURS_AMULET, sendmsg, delta); |
|
} |
|
|
|
void CreateMagicWeapon(int x, int y, int imisc, int icurs, bool sendmsg, bool delta) |
|
{ |
|
int imid = IMISC_NONE; |
|
if (imisc == ITYPE_STAFF) |
|
imid = IMISC_STAFF; |
|
|
|
int curlv = items_get_currlevel(); |
|
|
|
CreateMagicItem(x, y, curlv, imisc, imid, icurs, sendmsg, delta); |
|
} |
|
|
|
static void NextItemRecord(int i) |
|
{ |
|
gnNumGetRecords--; |
|
|
|
if (gnNumGetRecords == 0) { |
|
return; |
|
} |
|
|
|
itemrecord[i].dwTimestamp = itemrecord[gnNumGetRecords].dwTimestamp; |
|
itemrecord[i].nSeed = itemrecord[gnNumGetRecords].nSeed; |
|
itemrecord[i].wCI = itemrecord[gnNumGetRecords].wCI; |
|
itemrecord[i].nIndex = itemrecord[gnNumGetRecords].nIndex; |
|
} |
|
|
|
bool GetItemRecord(int nSeed, uint16_t wCI, int nIndex) |
|
{ |
|
int i; |
|
DWORD dwTicks; |
|
|
|
dwTicks = SDL_GetTicks(); |
|
|
|
for (i = 0; i < gnNumGetRecords; i++) { |
|
if (dwTicks - itemrecord[i].dwTimestamp > 6000) { |
|
NextItemRecord(i); |
|
i--; |
|
} else if (nSeed == itemrecord[i].nSeed && wCI == itemrecord[i].wCI && nIndex == itemrecord[i].nIndex) { |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void SetItemRecord(int nSeed, uint16_t wCI, int nIndex) |
|
{ |
|
DWORD dwTicks; |
|
|
|
dwTicks = SDL_GetTicks(); |
|
|
|
if (gnNumGetRecords == MAXITEMS) { |
|
return; |
|
} |
|
|
|
itemrecord[gnNumGetRecords].dwTimestamp = dwTicks; |
|
itemrecord[gnNumGetRecords].nSeed = nSeed; |
|
itemrecord[gnNumGetRecords].wCI = wCI; |
|
itemrecord[gnNumGetRecords].nIndex = nIndex; |
|
gnNumGetRecords++; |
|
} |
|
|
|
void PutItemRecord(int nSeed, uint16_t wCI, int nIndex) |
|
{ |
|
int i; |
|
DWORD dwTicks; |
|
|
|
dwTicks = SDL_GetTicks(); |
|
|
|
for (i = 0; i < gnNumGetRecords; i++) { |
|
if (dwTicks - itemrecord[i].dwTimestamp > 6000) { |
|
NextItemRecord(i); |
|
i--; |
|
} else if (nSeed == itemrecord[i].nSeed && wCI == itemrecord[i].wCI && nIndex == itemrecord[i].nIndex) { |
|
NextItemRecord(i); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
} // namespace devilution
|
|
|