Browse Source

Handle mixed item origins in multiplayer

pull/1316/head
Anders Jenbo 5 years ago
parent
commit
bba73ea20f
  1. 8
      Source/debug.cpp
  2. 7
      Source/inv.cpp
  3. 2
      Source/inv.h
  4. 1
      Source/itemdat.h
  5. 42
      Source/items.cpp
  6. 14
      Source/items.h
  7. 102
      Source/loadsave.cpp
  8. 10
      Source/loadsave.h
  9. 11
      Source/msg.cpp
  10. 1
      Source/msg.h
  11. 1
      Source/multi.cpp
  12. 27
      Source/pack.cpp
  13. 4
      Source/pack.h
  14. 25
      Source/pfile.cpp
  15. 4
      Source/player.cpp
  16. 19
      Source/spells.cpp
  17. 4
      Source/spells.h
  18. 22
      SourceT/pack_test.cpp
  19. 1
      enums.h

8
Source/debug.cpp

@ -85,12 +85,8 @@ void TakeGoldCheat()
void MaxSpellsCheat() void MaxSpellsCheat()
{ {
int i; for (int i = SPL_FIREBOLT; i < MAX_SPELLS; i++) {
if (GetSpellBookLevel((spell_id)i) != -1) {
int maxSpells = gbIsHellfire ? MAX_SPELLS : 37;
for (i = 1; i < maxSpells; i++) {
if (GetSpellBookLevel(i) != -1) {
plr[myplr]._pMemSpells |= GetSpellBitmask(i); plr[myplr]._pMemSpells |= GetSpellBitmask(i);
plr[myplr]._pSplLvl[i] = 10; plr[myplr]._pSplLvl[i] = 10;
} }

7
Source/inv.cpp

@ -1516,12 +1516,12 @@ void CheckInvPaste(int pnum, int mx, int my)
} }
} }
void CheckInvSwap(int pnum, BYTE bLoc, int idx, WORD wCI, int seed, BOOL bId) void CheckInvSwap(int pnum, BYTE bLoc, int idx, WORD wCI, int seed, BOOL bId, uint32_t dwBuff)
{ {
PlayerStruct *p; PlayerStruct *p;
memset(&item[MAXITEMS], 0, sizeof(*item)); memset(&item[MAXITEMS], 0, sizeof(*item));
RecreateItem(MAXITEMS, idx, wCI, seed, 0); RecreateItem(MAXITEMS, idx, wCI, seed, 0, (dwBuff & CF_HELLFIRE) != 0);
p = &plr[pnum]; p = &plr[pnum];
p->HoldItem = item[MAXITEMS]; p->HoldItem = item[MAXITEMS];
@ -2478,7 +2478,7 @@ int SyncPutItem(int pnum, int x, int y, int idx, WORD icreateinfo, int iseed, in
if (idx == IDI_EAR) { if (idx == IDI_EAR) {
RecreateEar(ii, icreateinfo, iseed, Id, dur, mdur, ch, mch, ivalue, ibuff); RecreateEar(ii, icreateinfo, iseed, Id, dur, mdur, ch, mch, ivalue, ibuff);
} else { } else {
RecreateItem(ii, idx, icreateinfo, iseed, ivalue); RecreateItem(ii, idx, icreateinfo, iseed, ivalue, (ibuff & CF_HELLFIRE) != 0);
if (Id) if (Id)
item[ii]._iIdentified = TRUE; item[ii]._iIdentified = TRUE;
item[ii]._iDurability = dur; item[ii]._iDurability = dur;
@ -2491,6 +2491,7 @@ int SyncPutItem(int pnum, int x, int y, int idx, WORD icreateinfo, int iseed, in
item[ii]._iMinMag = min_mag; item[ii]._iMinMag = min_mag;
item[ii]._iMinDex = min_dex; item[ii]._iMinDex = min_dex;
item[ii]._iAC = ac; item[ii]._iAC = ac;
item[ii].dwBuff = ibuff;
} }
item[ii]._ix = x; item[ii]._ix = x;

2
Source/inv.h

@ -40,7 +40,7 @@ bool AutoEquip(int playerNumber, const ItemStruct &item, bool persistItem = true
BOOL AutoPlace(int pnum, int ii, int sx, int sy, BOOL saveflag); BOOL AutoPlace(int pnum, int ii, int sx, int sy, BOOL saveflag);
BOOL SpecialAutoPlace(int pnum, int ii, const ItemStruct &item); BOOL SpecialAutoPlace(int pnum, int ii, const ItemStruct &item);
BOOL GoldAutoPlace(int pnum); BOOL GoldAutoPlace(int pnum);
void CheckInvSwap(int pnum, BYTE bLoc, int idx, WORD wCI, int seed, BOOL bId); void CheckInvSwap(int pnum, BYTE bLoc, int idx, WORD wCI, int seed, BOOL bId, uint32_t dwBuff);
void inv_update_rem_item(int pnum, BYTE iv); void inv_update_rem_item(int pnum, BYTE iv);
void RemoveInvItem(int pnum, int iv); void RemoveInvItem(int pnum, int iv);
void RemoveSpdBarItem(int pnum, int iv); void RemoveSpdBarItem(int pnum, int iv);

1
Source/itemdat.h

@ -463,6 +463,7 @@ typedef enum item_effect_type {
IPL_ADDMANAAC, IPL_ADDMANAAC,
IPL_FIRERESCLVL, IPL_FIRERESCLVL,
IPL_AC_CURSE, IPL_AC_CURSE,
IDI_LASTDIABLO = IPL_AC_CURSE,
IPL_FIRERES_CURSE, IPL_FIRERES_CURSE,
IPL_LIGHTRES_CURSE, IPL_LIGHTRES_CURSE,
IPL_MAGICRES_CURSE, IPL_MAGICRES_CURSE,

42
Source/items.cpp

@ -312,11 +312,19 @@ bool IsItemAvailable(int i)
return true; return true;
} }
bool IsUniqueAvailable(int i)
{
return gbIsHellfire || i <= 89;
}
static bool IsPrefixValidForItemType(int i, int flgs) static bool IsPrefixValidForItemType(int i, int flgs)
{ {
int PLIType = PL_Prefix[i].PLIType; int PLIType = PL_Prefix[i].PLIType;
if (!gbIsHellfire) { if (!gbIsHellfire) {
if (i > 82)
return false;
if (i >= 12 && i <= 20) if (i >= 12 && i <= 20)
PLIType &= ~PLT_STAFF; PLIType &= ~PLT_STAFF;
} }
@ -329,6 +337,9 @@ static bool IsSuffixValidForItemType(int i, int flgs)
int PLIType = PL_Suffix[i].PLIType; int PLIType = PL_Suffix[i].PLIType;
if (!gbIsHellfire) { if (!gbIsHellfire) {
if (i > 94)
return false;
if ((i >= 0 && i <= 1) if ((i >= 0 && i <= 1)
|| (i >= 14 && i <= 15) || (i >= 14 && i <= 15)
|| (i >= 21 && i <= 22) || (i >= 21 && i <= 22)
@ -1284,6 +1295,8 @@ void SetPlrHandItem(ItemStruct *h, int idata)
h->_iSufPower = IPL_INVALID; h->_iSufPower = IPL_INVALID;
h->_iMagical = ITEM_QUALITY_NORMAL; h->_iMagical = ITEM_QUALITY_NORMAL;
h->IDidx = idata; h->IDidx = idata;
if (gbIsHellfire)
h->dwBuff |= CF_HELLFIRE;
} }
void GetPlrHandSeed(ItemStruct *h) void GetPlrHandSeed(ItemStruct *h)
@ -1658,7 +1671,7 @@ void GetBookSpell(int i, int lvl)
int s = SPL_FIREBOLT; int s = SPL_FIREBOLT;
enum spell_id bs = SPL_FIREBOLT; enum spell_id bs = SPL_FIREBOLT;
while (rv > 0) { while (rv > 0) {
int sLevel = GetSpellBookLevel(s); int sLevel = GetSpellBookLevel(static_cast<spell_id>(s));
if (sLevel != -1 && lvl >= sLevel) { if (sLevel != -1 && lvl >= sLevel) {
rv--; rv--;
bs = static_cast<spell_id>(s); bs = static_cast<spell_id>(s);
@ -1702,8 +1715,6 @@ void GetStaffPower(int i, int lvl, int bs, BOOL onlygood)
if (tmp == 0 || onlygood) { if (tmp == 0 || onlygood) {
nl = 0; nl = 0;
for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) { for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) {
if (!gbIsHellfire && j > 82)
break;
if (IsPrefixValidForItemType(j, PLT_STAFF) && PL_Prefix[j].PLMinLvl <= lvl) { if (IsPrefixValidForItemType(j, PLT_STAFF) && PL_Prefix[j].PLMinLvl <= lvl) {
addok = TRUE; addok = TRUE;
if (onlygood && !PL_Prefix[j].PLOk) if (onlygood && !PL_Prefix[j].PLOk)
@ -1768,7 +1779,7 @@ void GetStaffSpell(int i, int lvl, BOOL onlygood)
int s = SPL_FIREBOLT; int s = SPL_FIREBOLT;
enum spell_id bs = SPL_NULL; enum spell_id bs = SPL_NULL;
while (rv > 0) { while (rv > 0) {
int sLevel = GetSpellStaffLevel(s); int sLevel = GetSpellStaffLevel(static_cast<spell_id>(s));
if (sLevel != -1 && l >= sLevel) { if (sLevel != -1 && l >= sLevel) {
rv--; rv--;
bs = static_cast<spell_id>(s); bs = static_cast<spell_id>(s);
@ -1853,6 +1864,8 @@ void GetItemAttrs(int i, int idata, int lvl)
item[i]._iMinMag = AllItemsList[idata].iMinMag; item[i]._iMinMag = AllItemsList[idata].iMinMag;
item[i]._iMinDex = AllItemsList[idata].iMinDex; item[i]._iMinDex = AllItemsList[idata].iMinDex;
item[i].IDidx = idata; item[i].IDidx = idata;
if (gbIsHellfire)
item[i].dwBuff |= CF_HELLFIRE;
item[i]._iPrePower = IPL_INVALID; item[i]._iPrePower = IPL_INVALID;
item[i]._iSufPower = IPL_INVALID; item[i]._iSufPower = IPL_INVALID;
@ -2332,8 +2345,6 @@ void GetItemPower(int i, int minlvl, int maxlvl, int flgs, BOOL onlygood)
if (pre == 0) { if (pre == 0) {
nt = 0; nt = 0;
for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) { for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) {
if (!gbIsHellfire && j > 82)
break;
if (IsPrefixValidForItemType(j, flgs)) { 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)) { 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; l[nt] = j;
@ -2365,8 +2376,6 @@ void GetItemPower(int i, int minlvl, int maxlvl, int flgs, BOOL onlygood)
if (post != 0) { if (post != 0) {
nl = 0; nl = 0;
for (j = 0; PL_Suffix[j].PLPower != IPL_INVALID; j++) { for (j = 0; PL_Suffix[j].PLPower != IPL_INVALID; j++) {
if (!gbIsHellfire && j > 94)
break;
if (IsSuffixValidForItemType(j, flgs) if (IsSuffixValidForItemType(j, flgs)
&& PL_Suffix[j].PLMinLvl >= minlvl && PL_Suffix[j].PLMinLvl <= maxlvl && 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)) && !((goe == GOE_GOOD && PL_Suffix[j].PLGOE == GOE_EVIL) || (goe == GOE_EVIL && PL_Suffix[j].PLGOE == GOE_GOOD))
@ -2620,7 +2629,7 @@ int CheckUnique(int i, int lvl, int uper, BOOL recreate)
numu = 0; numu = 0;
memset(uok, 0, sizeof(uok)); memset(uok, 0, sizeof(uok));
for (j = 0; UniqueItemList[j].UIItemId != UITYPE_INVALID; j++) { for (j = 0; UniqueItemList[j].UIItemId != UITYPE_INVALID; j++) {
if (!gbIsHellfire && j > 89) if (!IsUniqueAvailable(j))
break; break;
if (UniqueItemList[j].UIItemId == AllItemsList[item[i].IDidx].iItemId if (UniqueItemList[j].UIItemId == AllItemsList[item[i].IDidx].iItemId
&& lvl >= UniqueItemList[j].UIMinLvl && lvl >= UniqueItemList[j].UIMinLvl
@ -2899,31 +2908,38 @@ void CreateTypeItem(int x, int y, BOOL onlygood, int itype, int imisc, BOOL send
SetupBaseItem(x, y, idx, onlygood, sendmsg, delta); SetupBaseItem(x, y, idx, onlygood, sendmsg, delta);
} }
void RecreateItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue) void RecreateItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue, bool isHellfire)
{ {
bool _gbIsHellfire = gbIsHellfire;
gbIsHellfire = isHellfire;
if (idx == IDI_GOLD) { if (idx == IDI_GOLD) {
SetPlrHandItem(&item[ii], IDI_GOLD); SetPlrHandItem(&item[ii], IDI_GOLD);
item[ii]._iSeed = iseed; item[ii]._iSeed = iseed;
item[ii]._iCreateInfo = icreateinfo; item[ii]._iCreateInfo = icreateinfo;
item[ii]._ivalue = ivalue; item[ii]._ivalue = ivalue;
SetPlrHandGoldCurs(&item[ii]); SetPlrHandGoldCurs(&item[ii]);
gbIsHellfire = _gbIsHellfire;
return; return;
} }
if (icreateinfo == 0) { if (icreateinfo == 0) {
SetPlrHandItem(&item[ii], idx); SetPlrHandItem(&item[ii], idx);
SetPlrHandSeed(&item[ii], iseed); SetPlrHandSeed(&item[ii], iseed);
gbIsHellfire = _gbIsHellfire;
return; return;
} }
if ((icreateinfo & CF_UNIQUE) == 0) { if ((icreateinfo & CF_UNIQUE) == 0) {
if (icreateinfo & CF_TOWN) { if (icreateinfo & CF_TOWN) {
RecreateTownItem(ii, idx, icreateinfo, iseed, ivalue); RecreateTownItem(ii, idx, icreateinfo, iseed, ivalue);
gbIsHellfire = _gbIsHellfire;
return; return;
} }
if ((icreateinfo & CF_USEFUL) == CF_USEFUL) { if ((icreateinfo & CF_USEFUL) == CF_USEFUL) {
SetupAllUseful(ii, iseed, icreateinfo & CF_LEVEL); SetupAllUseful(ii, iseed, icreateinfo & CF_LEVEL);
gbIsHellfire = _gbIsHellfire;
return; return;
} }
} }
@ -2941,6 +2957,7 @@ void RecreateItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue)
bool pregen = (icreateinfo & CF_PREGEN) != 0; bool pregen = (icreateinfo & CF_PREGEN) != 0;
SetupAllItems(ii, idx, iseed, level, uper, onlygood, recreate, pregen); SetupAllItems(ii, idx, iseed, level, uper, onlygood, recreate, pregen);
gbIsHellfire = _gbIsHellfire;
} }
void RecreateEar(int ii, WORD ic, int iseed, int Id, int dur, int mdur, int ch, int mch, int ivalue, int ibuff) void RecreateEar(int ii, WORD ic, int iseed, int Id, int dur, int mdur, int ch, int mch, int ivalue, int ibuff)
@ -3034,8 +3051,7 @@ void items_427ABA(int x, int y)
dItem[x][y] = ii + 1; dItem[x][y] = ii + 1;
gbIsHellfireSaveGame = gbIsHellfire; UnPackItem(&PkSItem, &item[ii], (PkSItem.dwBuff & CF_HELLFIRE) != 0);
UnPackItem(&PkSItem, &item[ii]);
item[ii]._ix = x; item[ii]._ix = x;
item[ii]._iy = y; item[ii]._iy = y;
RespawnItem(ii, FALSE); RespawnItem(ii, FALSE);
@ -5307,7 +5323,7 @@ int ItemNoFlippy()
return r; return r;
} }
void CreateSpellBook(int x, int y, int ispell, BOOL sendmsg, BOOL delta) void CreateSpellBook(int x, int y, spell_id ispell, BOOL sendmsg, BOOL delta)
{ {
int lvl = currlevel; int lvl = currlevel;

14
Source/items.h

@ -37,6 +37,7 @@ typedef enum item_quality {
CF_TOWN is combining all store flags and indicates if item has been bought from a NPC CF_TOWN is combining all store flags and indicates if item has been bought from a NPC
*/ */
typedef enum icreateinfo_flag { typedef enum icreateinfo_flag {
// clang-format off
CF_LEVEL = (1 << 6) - 1, CF_LEVEL = (1 << 6) - 1,
CF_ONLYGOOD = 1 << 6, CF_ONLYGOOD = 1 << 6,
CF_UPER15 = 1 << 7, CF_UPER15 = 1 << 7,
@ -51,8 +52,15 @@ typedef enum icreateinfo_flag {
CF_USEFUL = CF_UPER15 | CF_UPER1, CF_USEFUL = CF_UPER15 | CF_UPER1,
CF_TOWN = CF_SMITH | CF_SMITHPREMIUM | CF_BOY | CF_WITCH | CF_HEALER, CF_TOWN = CF_SMITH | CF_SMITHPREMIUM | CF_BOY | CF_WITCH | CF_HEALER,
// clang-format on
} icreateinfo_flag; } icreateinfo_flag;
typedef enum icreateinfo_flag2 {
// clang-format off
CF_HELLFIRE = 1,
// clang-format on
} icreateinfo_flag2;
typedef struct ItemStruct { typedef struct ItemStruct {
Sint32 _iSeed; Sint32 _iSeed;
Uint16 _iCreateInfo; Uint16 _iCreateInfo;
@ -121,6 +129,7 @@ typedef struct ItemStruct {
Sint8 _iMinDex; Sint8 _iMinDex;
bool _iStatFlag; bool _iStatFlag;
Sint32 IDidx; Sint32 IDidx;
Uint32 dwBuff;
Sint32 _iDamAcFlags; Sint32 _iDamAcFlags;
/** /**
@ -263,6 +272,7 @@ extern BOOL UniqueItemFlag[128];
extern int numitems; extern int numitems;
bool IsItemAvailable(int i); bool IsItemAvailable(int i);
bool IsUniqueAvailable(int i);
void InitItemGFX(); void InitItemGFX();
void InitItems(); void InitItems();
void CalcPlrItemVals(int p, BOOL Loadgfx); void CalcPlrItemVals(int p, BOOL Loadgfx);
@ -288,7 +298,7 @@ void SpawnItem(int m, int x, int y, BOOL sendmsg);
void CreateRndItem(int x, int y, BOOL onlygood, BOOL sendmsg, BOOL delta); void CreateRndItem(int x, int y, BOOL onlygood, BOOL sendmsg, BOOL delta);
void CreateRndUseful(int pnum, int x, int y, BOOL sendmsg); void CreateRndUseful(int pnum, int x, int y, BOOL sendmsg);
void CreateTypeItem(int x, int y, BOOL onlygood, int itype, int imisc, BOOL sendmsg, BOOL delta); void CreateTypeItem(int x, int y, BOOL onlygood, int itype, int imisc, BOOL sendmsg, BOOL delta);
void RecreateItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue); void RecreateItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue, bool isHellfire);
void RecreateEar(int ii, WORD ic, int iseed, int Id, int dur, int mdur, int ch, int mch, int ivalue, int ibuff); void RecreateEar(int ii, WORD ic, int iseed, int Id, int dur, int mdur, int ch, int mch, int ivalue, int ibuff);
void items_427A72(); void items_427A72();
void items_427ABA(int x, int y); void items_427ABA(int x, int y);
@ -324,7 +334,7 @@ void SpawnStoreGold();
void RecreateTownItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue); void RecreateTownItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue);
void RecalcStoreStats(); void RecalcStoreStats();
int ItemNoFlippy(); int ItemNoFlippy();
void CreateSpellBook(int x, int y, int ispell, BOOL sendmsg, BOOL delta); void CreateSpellBook(int x, int y, spell_id ispell, BOOL sendmsg, BOOL delta);
void CreateMagicArmor(int x, int y, int imisc, int icurs, BOOL sendmsg, BOOL delta); void CreateMagicArmor(int x, int y, int imisc, int icurs, BOOL sendmsg, BOOL delta);
void CreateAmulet(int x, int y, int curlv, BOOL sendmsg, BOOL delta); void CreateAmulet(int x, int y, int curlv, BOOL sendmsg, BOOL delta);
void CreateMagicWeapon(int x, int y, int imisc, int icurs, BOOL sendmsg, BOOL delta); void CreateMagicWeapon(int x, int y, int imisc, int icurs, BOOL sendmsg, BOOL delta);

102
Source/loadsave.cpp

@ -183,6 +183,23 @@ public:
} }
void RemoveInvalidItem(ItemStruct *pItem)
{
bool isInvalid = !IsItemAvailable(pItem->IDidx) || !IsUniqueAvailable(pItem->_iUid);
if (!gbIsHellfire) {
isInvalid = isInvalid || (pItem->_itype == ITYPE_STAFF && GetSpellStaffLevel(pItem->_iSpell) == -1);
isInvalid = isInvalid || (pItem->_iMiscId == IMISC_BOOK && GetSpellBookLevel(pItem->_iSpell) == -1);
isInvalid = isInvalid || pItem->_iDamAcFlags != 0;
isInvalid = isInvalid || pItem->_iPrePower > IDI_LASTDIABLO;
isInvalid = isInvalid || pItem->_iSufPower > IDI_LASTDIABLO;
}
if (isInvalid) {
pItem->_itype = ITYPE_NONE;
}
}
static void LoadItemData(LoadHelper *file, ItemStruct *pItem) static void LoadItemData(LoadHelper *file, ItemStruct *pItem)
{ {
pItem->_iSeed = file->nextLE<Sint32>(); pItem->_iSeed = file->nextLE<Sint32>();
@ -261,16 +278,13 @@ static void LoadItemData(LoadHelper *file, ItemStruct *pItem)
if (!gbIsHellfireSaveGame) { if (!gbIsHellfireSaveGame) {
pItem->IDidx = RemapItemIdxFromDiablo(pItem->IDidx); pItem->IDidx = RemapItemIdxFromDiablo(pItem->IDidx);
} }
file->skip(4); // Unused pItem->dwBuff = file->nextLE<Uint32>();
if (gbIsHellfireSaveGame) if (gbIsHellfireSaveGame)
pItem->_iDamAcFlags = file->nextLE<Sint32>(); pItem->_iDamAcFlags = file->nextLE<Sint32>();
else else
pItem->_iDamAcFlags = 0; pItem->_iDamAcFlags = 0;
if (!IsItemAvailable(pItem->IDidx)) { RemoveInvalidItem(pItem);
pItem->IDidx = 0;
pItem->_itype = ITYPE_NONE;
}
} }
static void LoadItems(LoadHelper *file, const int n, ItemStruct *pItem) static void LoadItems(LoadHelper *file, const int n, ItemStruct *pItem)
@ -824,7 +838,7 @@ bool IsHeaderValid(Uint32 magicNumber)
return false; return false;
} }
void ConvertLevels() static void ConvertLevels()
{ {
// Backup current level state // Backup current level state
bool _setlevel = setlevel; bool _setlevel = setlevel;
@ -910,6 +924,56 @@ void SaveHotkeys()
file.writeLE<Uint8>(plr[myplr]._pRSplType); file.writeLE<Uint8>(plr[myplr]._pRSplType);
} }
static void LoadMatchingItems(LoadHelper *file, const int n, ItemStruct *pItem)
{
ItemStruct tempItem;
for (int i = 0; i < n; i++) {
LoadItemData(file, &tempItem);
if (pItem[i].isEmpty() || tempItem.isEmpty())
continue;
if (pItem[i]._iSeed != tempItem._iSeed)
continue;
pItem[i] = tempItem;
}
}
void LoadHeroItems(PlayerStruct *pPlayer)
{
LoadHelper file("heroitems");
if (!file.isValid())
return;
gbIsHellfireSaveGame = file.nextBool8();
LoadMatchingItems(&file, NUM_INVLOC, pPlayer->InvBody);
LoadMatchingItems(&file, NUM_INV_GRID_ELEM, pPlayer->InvList);
LoadMatchingItems(&file, MAXBELTITEMS, pPlayer->SpdList);
gbIsHellfireSaveGame = gbIsHellfire;
}
void RemoveEmptyInventory(int pnum)
{
for (int i = NUM_INV_GRID_ELEM; i > 0; i--) {
int idx = plr[pnum].InvGrid[i - 1];
if (idx > 0 && plr[pnum].InvList[idx - 1].isEmpty()) {
RemoveInvItem(pnum, idx - 1);
}
};
}
void RemoveEmptyLevelItems()
{
for (int i = numitems; i >= 0; i--) {
int ii = itemactive[i];
if (item[ii].isEmpty()) {
dItem[item[ii]._ix][item[ii]._iy] = 0;
DeleteItem(ii, i);
}
}
}
/** /**
* @brief Load game state * @brief Load game state
* @param firstflag Can be set to false if we are simply reloading the current game * @param firstflag Can be set to false if we are simply reloading the current game
@ -971,8 +1035,10 @@ void LoadGame(BOOL firstflag)
for (int i = 0; i < MAXPORTAL; i++) for (int i = 0; i < MAXPORTAL; i++)
LoadPortal(&file, i); LoadPortal(&file, i);
if (gbIsHellfireSaveGame != gbIsHellfire) if (gbIsHellfireSaveGame != gbIsHellfire) {
ConvertLevels(); ConvertLevels();
RemoveEmptyInventory(myplr);
}
LoadGameLevel(firstflag, ENTRY_LOAD); LoadGameLevel(firstflag, ENTRY_LOAD);
SyncInitPlr(myplr); SyncInitPlr(myplr);
@ -1102,8 +1168,10 @@ void LoadGame(BOOL firstflag)
SetCursor_(CURSOR_HAND); SetCursor_(CURSOR_HAND);
gbProcessPlayers = TRUE; gbProcessPlayers = TRUE;
if (gbIsHellfireSaveGame != gbIsHellfire) if (gbIsHellfireSaveGame != gbIsHellfire) {
RemoveEmptyLevelItems();
SaveGame(); SaveGame();
}
gbIsHellfireSaveGame = gbIsHellfire; gbIsHellfireSaveGame = gbIsHellfire;
} }
@ -1192,7 +1260,7 @@ static void SaveItem(SaveHelper *file, ItemStruct *pItem)
file->skip(1); // Alignment file->skip(1); // Alignment
file->writeLE<Uint32>(pItem->_iStatFlag); file->writeLE<Uint32>(pItem->_iStatFlag);
file->writeLE<Sint32>(idx); file->writeLE<Sint32>(idx);
file->skip(4); // Unused file->writeLE<Uint32>(pItem->dwBuff);
if (gbIsHellfire) if (gbIsHellfire)
file->writeLE<Uint32>(pItem->_iDamAcFlags); file->writeLE<Uint32>(pItem->_iDamAcFlags);
} }
@ -1667,6 +1735,18 @@ static void SavePortal(SaveHelper *file, int i)
file->writeLE<Uint32>(pPortal->setlvl); file->writeLE<Uint32>(pPortal->setlvl);
} }
void SaveHeroItems(PlayerStruct *pPlayer)
{
size_t items = NUM_INVLOC + NUM_INV_GRID_ELEM + MAXBELTITEMS;
SaveHelper file("heroitems", items * sizeof(ItemStruct));
file.writeLE<Uint8>(gbIsHellfire);
SaveItems(&file, pPlayer->InvBody, NUM_INVLOC);
SaveItems(&file, pPlayer->InvList, NUM_INV_GRID_ELEM);
SaveItems(&file, pPlayer->SpdList, MAXBELTITEMS);
}
void SaveGame() void SaveGame()
{ {
SaveHelper file("game", FILEBUFF); SaveHelper file("game", FILEBUFF);
@ -1990,6 +2070,10 @@ void LoadLevel()
} }
} }
if (gbIsHellfireSaveGame != gbIsHellfire) {
RemoveEmptyLevelItems();
}
if (!gbSkipSync) { if (!gbSkipSync) {
AutomapZoomReset(); AutomapZoomReset();
ResyncQuests(); ResyncQuests();

10
Source/loadsave.h

@ -6,6 +6,8 @@
#ifndef __LOADSAVE_H__ #ifndef __LOADSAVE_H__
#define __LOADSAVE_H__ #define __LOADSAVE_H__
#include "player.h"
DEVILUTION_BEGIN_NAMESPACE DEVILUTION_BEGIN_NAMESPACE
#ifdef __cplusplus #ifdef __cplusplus
@ -15,12 +17,20 @@ extern "C" {
extern bool gbIsHellfireSaveGame; extern bool gbIsHellfireSaveGame;
extern int giNumberOfLevels; extern int giNumberOfLevels;
void RemoveInvalidItem(ItemStruct *pItem);
int RemapItemIdxFromDiablo(int i); int RemapItemIdxFromDiablo(int i);
int RemapItemIdxToDiablo(int i); int RemapItemIdxToDiablo(int i);
bool IsHeaderValid(Uint32 magicNumber); bool IsHeaderValid(Uint32 magicNumber);
void LoadHotkeys(); void LoadHotkeys();
void LoadHeroItems(PlayerStruct *pPlayer);
/**
* @brief Remove invalid inventory items from the inventory grid
* @param pnum The id of the player
*/
void RemoveEmptyInventory(int pnum);
void LoadGame(BOOL firstflag); void LoadGame(BOOL firstflag);
void SaveHotkeys(); void SaveHotkeys();
void SaveHeroItems(PlayerStruct *pPlayer);
void SaveGame(); void SaveGame();
void SaveLevel(); void SaveLevel();
void LoadLevel(); void LoadLevel();

11
Source/msg.cpp

@ -654,6 +654,7 @@ void DeltaAddItem(int ii)
pD->bMinMag = item[ii]._iMinMag; pD->bMinMag = item[ii]._iMinMag;
pD->bMinDex = item[ii]._iMinDex; pD->bMinDex = item[ii]._iMinDex;
pD->bAC = item[ii]._iAC; pD->bAC = item[ii]._iAC;
pD->dwBuff = item[ii].dwBuff;
return; return;
} }
} }
@ -761,7 +762,8 @@ void DeltaLoadLevel()
sgLevels[currlevel].item[i].wIndx, sgLevels[currlevel].item[i].wIndx,
sgLevels[currlevel].item[i].wCI, sgLevels[currlevel].item[i].wCI,
sgLevels[currlevel].item[i].dwSeed, sgLevels[currlevel].item[i].dwSeed,
sgLevels[currlevel].item[i].wValue); sgLevels[currlevel].item[i].wValue,
(sgLevels[currlevel].item[i].dwBuff & CF_HELLFIRE) != 0);
if (sgLevels[currlevel].item[i].bId) if (sgLevels[currlevel].item[i].bId)
item[ii]._iIdentified = TRUE; item[ii]._iIdentified = TRUE;
item[ii]._iDurability = sgLevels[currlevel].item[i].bDur; item[ii]._iDurability = sgLevels[currlevel].item[i].bDur;
@ -774,6 +776,7 @@ void DeltaLoadLevel()
item[ii]._iMinMag = sgLevels[currlevel].item[i].bMinMag; item[ii]._iMinMag = sgLevels[currlevel].item[i].bMinMag;
item[ii]._iMinDex = sgLevels[currlevel].item[i].bMinDex; item[ii]._iMinDex = sgLevels[currlevel].item[i].bMinDex;
item[ii]._iAC = sgLevels[currlevel].item[i].bAC; item[ii]._iAC = sgLevels[currlevel].item[i].bAC;
item[ii].dwBuff = sgLevels[currlevel].item[i].dwBuff;
} }
x = sgLevels[currlevel].item[i].x; x = sgLevels[currlevel].item[i].x;
y = sgLevels[currlevel].item[i].y; y = sgLevels[currlevel].item[i].y;
@ -1001,6 +1004,7 @@ void NetSendCmdGItem(BOOL bHiPri, BYTE bCmd, BYTE mast, BYTE pnum, BYTE ii)
cmd.bMinMag = item[ii]._iMinMag; cmd.bMinMag = item[ii]._iMinMag;
cmd.bMinDex = item[ii]._iMinDex; cmd.bMinDex = item[ii]._iMinDex;
cmd.bAC = item[ii]._iAC; cmd.bAC = item[ii]._iAC;
cmd.dwBuff = item[ii].dwBuff;
} }
if (bHiPri) if (bHiPri)
@ -1098,6 +1102,7 @@ void NetSendCmdPItem(BOOL bHiPri, BYTE bCmd, BYTE x, BYTE y)
cmd.bMinMag = plr[myplr].HoldItem._iMinMag; cmd.bMinMag = plr[myplr].HoldItem._iMinMag;
cmd.bMinDex = plr[myplr].HoldItem._iMinDex; cmd.bMinDex = plr[myplr].HoldItem._iMinDex;
cmd.bAC = plr[myplr].HoldItem._iAC; cmd.bAC = plr[myplr].HoldItem._iAC;
cmd.dwBuff = plr[myplr].HoldItem.dwBuff;
} }
if (bHiPri) if (bHiPri)
@ -1116,6 +1121,7 @@ void NetSendCmdChItem(BOOL bHiPri, BYTE bLoc)
cmd.wCI = plr[myplr].HoldItem._iCreateInfo; cmd.wCI = plr[myplr].HoldItem._iCreateInfo;
cmd.dwSeed = plr[myplr].HoldItem._iSeed; cmd.dwSeed = plr[myplr].HoldItem._iSeed;
cmd.bId = plr[myplr].HoldItem._iIdentified; cmd.bId = plr[myplr].HoldItem._iIdentified;
cmd.dwBuff = plr[myplr].HoldItem.dwBuff;
if (bHiPri) if (bHiPri)
NetSendHiPri((BYTE *)&cmd, sizeof(cmd)); NetSendHiPri((BYTE *)&cmd, sizeof(cmd));
@ -1169,6 +1175,7 @@ void NetSendCmdDItem(BOOL bHiPri, int ii)
cmd.bMinMag = item[ii]._iMinMag; cmd.bMinMag = item[ii]._iMinMag;
cmd.bMinDex = item[ii]._iMinDex; cmd.bMinDex = item[ii]._iMinDex;
cmd.bAC = item[ii]._iAC; cmd.bAC = item[ii]._iAC;
cmd.dwBuff = item[ii].dwBuff;
} }
if (bHiPri) if (bHiPri)
@ -2133,7 +2140,7 @@ static DWORD On_CHANGEPLRITEMS(TCmd *pCmd, int pnum)
if (gbBufferMsgs == 1) if (gbBufferMsgs == 1)
msg_send_packet(pnum, p, sizeof(*p)); msg_send_packet(pnum, p, sizeof(*p));
else if (pnum != myplr) else if (pnum != myplr)
CheckInvSwap(pnum, p->bLoc, p->wIndx, p->wCI, p->dwSeed, p->bId); CheckInvSwap(pnum, p->bLoc, p->wIndx, p->wCI, p->dwSeed, p->bId, p->dwBuff);
return sizeof(*p); return sizeof(*p);
} }

1
Source/msg.h

@ -139,6 +139,7 @@ typedef struct TCmdChItem {
Uint16 wCI; Uint16 wCI;
Sint32 dwSeed; Sint32 dwSeed;
Uint8 bId; Uint8 bId;
Uint32 dwBuff;
} TCmdChItem; } TCmdChItem;
typedef struct TCmdDelItem { typedef struct TCmdDelItem {

1
Source/multi.cpp

@ -902,7 +902,6 @@ void recv_plrinfo(int pnum, TCmdPlrInfoHdr *p, BOOL recv)
sgwPackPlrOffsetTbl[pnum] = 0; sgwPackPlrOffsetTbl[pnum] = 0;
multi_player_left_msg(pnum, 0); multi_player_left_msg(pnum, 0);
plr[pnum]._pGFXLoad = 0; plr[pnum]._pGFXLoad = 0;
gbIsHellfireSaveGame = gbIsHellfire;
UnPackPlayer(&netplr[pnum], pnum, TRUE); UnPackPlayer(&netplr[pnum], pnum, TRUE);
if (!recv) { if (!recv) {

27
Source/pack.cpp

@ -38,6 +38,7 @@ void PackItem(PkItemStruct *id, const ItemStruct *is)
id->bMCh = is->_iMaxCharges; id->bMCh = is->_iMaxCharges;
if (is->IDidx == IDI_GOLD) if (is->IDidx == IDI_GOLD)
id->wValue = SwapLE16(is->_ivalue); id->wValue = SwapLE16(is->_ivalue);
id->dwBuff = is->dwBuff;
} }
} }
} }
@ -131,7 +132,7 @@ void PackPlayer(PkPlayerStruct *pPack, int pnum, BOOL manashield)
* @param is The source packed item * @param is The source packed item
* @param id The distination item * @param id The distination item
*/ */
void UnPackItem(const PkItemStruct *is, ItemStruct *id) void UnPackItem(const PkItemStruct *is, ItemStruct *id, bool isHellfire)
{ {
WORD idx = SwapLE16(is->idx); WORD idx = SwapLE16(is->idx);
if (idx == 0xFFFF) { if (idx == 0xFFFF) {
@ -139,7 +140,7 @@ void UnPackItem(const PkItemStruct *is, ItemStruct *id)
return; return;
} }
if (!gbIsHellfireSaveGame) { if (!isHellfire) {
idx = RemapItemIdxFromDiablo(idx); idx = RemapItemIdxFromDiablo(idx);
} }
@ -162,13 +163,20 @@ void UnPackItem(const PkItemStruct *is, ItemStruct *id)
SwapLE32(is->dwBuff)); SwapLE32(is->dwBuff));
} else { } else {
memset(&item[MAXITEMS], 0, sizeof(*item)); memset(&item[MAXITEMS], 0, sizeof(*item));
RecreateItem(MAXITEMS, idx, SwapLE16(is->iCreateInfo), SwapLE32(is->iSeed), SwapLE16(is->wValue)); RecreateItem(MAXITEMS, idx, SwapLE16(is->iCreateInfo), SwapLE32(is->iSeed), SwapLE16(is->wValue), isHellfire);
item[MAXITEMS]._iMagical = is->bId >> 1; item[MAXITEMS]._iMagical = is->bId >> 1;
item[MAXITEMS]._iIdentified = is->bId & 1; item[MAXITEMS]._iIdentified = is->bId & 1;
item[MAXITEMS]._iDurability = is->bDur; item[MAXITEMS]._iDurability = is->bDur;
item[MAXITEMS]._iMaxDur = is->bMDur; item[MAXITEMS]._iMaxDur = is->bMDur;
item[MAXITEMS]._iCharges = is->bCh; item[MAXITEMS]._iCharges = is->bCh;
item[MAXITEMS]._iMaxCharges = is->bMCh; item[MAXITEMS]._iMaxCharges = is->bMCh;
RemoveInvalidItem(&item[MAXITEMS]);
if (isHellfire)
item[MAXITEMS].dwBuff |= CF_HELLFIRE;
else
item[MAXITEMS].dwBuff &= ~CF_HELLFIRE;
} }
*id = item[MAXITEMS]; *id = item[MAXITEMS];
} }
@ -191,7 +199,7 @@ void VerifyGoldSeeds(PlayerStruct *pPlayer)
} }
} }
void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok) void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL netSync)
{ {
PlayerStruct *pPlayer; PlayerStruct *pPlayer;
int i; int i;
@ -226,7 +234,7 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok)
pPlayer->_pMaxHPBase = SwapLE32(pPack->pMaxHPBase); pPlayer->_pMaxHPBase = SwapLE32(pPack->pMaxHPBase);
pPlayer->_pHPBase = SwapLE32(pPack->pHPBase); pPlayer->_pHPBase = SwapLE32(pPack->pHPBase);
pPlayer->_pBaseToBlk = ToBlkTbl[pPlayer->_pClass]; pPlayer->_pBaseToBlk = ToBlkTbl[pPlayer->_pClass];
if (!killok) if (!netSync)
if ((int)(pPlayer->_pHPBase & 0xFFFFFFC0) < 64) if ((int)(pPlayer->_pHPBase & 0xFFFFFFC0) < 64)
pPlayer->_pHPBase = 64; pPlayer->_pHPBase = 64;
@ -243,7 +251,8 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok)
pi = &pPlayer->InvBody[0]; pi = &pPlayer->InvBody[0];
for (i = 0; i < NUM_INVLOC; i++) { for (i = 0; i < NUM_INVLOC; i++) {
UnPackItem(pki, pi); bool isHellfire = netSync ? ((pki->dwBuff & CF_HELLFIRE) != 0) : pPack->bIsHellfire;
UnPackItem(pki, pi, isHellfire);
pki++; pki++;
pi++; pi++;
} }
@ -252,7 +261,8 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok)
pi = &pPlayer->InvList[0]; pi = &pPlayer->InvList[0];
for (i = 0; i < NUM_INV_GRID_ELEM; i++) { for (i = 0; i < NUM_INV_GRID_ELEM; i++) {
UnPackItem(pki, pi); bool isHellfire = netSync ? ((pki->dwBuff & CF_HELLFIRE) != 0) : pPack->bIsHellfire;
UnPackItem(pki, pi, isHellfire);
pki++; pki++;
pi++; pi++;
} }
@ -267,7 +277,8 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok)
pi = &pPlayer->SpdList[0]; pi = &pPlayer->SpdList[0];
for (i = 0; i < MAXBELTITEMS; i++) { for (i = 0; i < MAXBELTITEMS; i++) {
UnPackItem(pki, pi); bool isHellfire = netSync ? ((pki->dwBuff & CF_HELLFIRE) != 0) : pPack->bIsHellfire;
UnPackItem(pki, pi, isHellfire);
pki++; pki++;
pi++; pi++;
} }

4
Source/pack.h

@ -80,9 +80,9 @@ typedef struct PkPlayerStruct {
#pragma pack(pop) #pragma pack(pop)
void PackPlayer(PkPlayerStruct *pPack, int pnum, BOOL manashield); void PackPlayer(PkPlayerStruct *pPack, int pnum, BOOL manashield);
void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok); void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL netSync);
void PackItem(PkItemStruct *id, const ItemStruct *is); void PackItem(PkItemStruct *id, const ItemStruct *is);
void UnPackItem(const PkItemStruct *is, ItemStruct *id); void UnPackItem(const PkItemStruct *is, ItemStruct *id, bool isHellfire);
/* rdata */ /* rdata */
#ifdef __cplusplus #ifdef __cplusplus

25
Source/pfile.cpp

@ -184,8 +184,10 @@ void pfile_write_hero()
if (pfile_open_archive(save_num)) { if (pfile_open_archive(save_num)) {
PackPlayer(&pkplr, myplr, !gbIsMultiplayer); PackPlayer(&pkplr, myplr, !gbIsMultiplayer);
pfile_encode_hero(&pkplr); pfile_encode_hero(&pkplr);
if (!gbVanilla) if (!gbVanilla) {
SaveHotkeys(); SaveHotkeys();
SaveHeroItems(&plr[myplr]);
}
pfile_flush(!gbIsMultiplayer, save_num); pfile_flush(!gbIsMultiplayer, save_num);
} }
} }
@ -276,9 +278,15 @@ BOOL pfile_ui_set_hero_infos(BOOL (*ui_add_hero_info)(_uiheroinfo *))
_uiheroinfo uihero; _uiheroinfo uihero;
strcpy(hero_names[i], pkplr.pName); strcpy(hero_names[i], pkplr.pName);
bool hasSaveGame = pfile_archive_contains_game(archive, i); bool hasSaveGame = pfile_archive_contains_game(archive, i);
if (!hasSaveGame) if (hasSaveGame)
gbIsHellfireSaveGame = pkplr.bIsHellfire; pkplr.bIsHellfire = gbIsHellfireSaveGame;
UnPackPlayer(&pkplr, 0, FALSE); UnPackPlayer(&pkplr, 0, FALSE);
LoadHeroItems(&plr[0]);
RemoveEmptyInventory(0);
CalcPlrInv(0, FALSE);
game_2_ui_player(plr, &uihero, hasSaveGame); game_2_ui_player(plr, &uihero, hasSaveGame);
ui_add_hero_info(&uihero); ui_add_hero_info(&uihero);
} }
@ -340,6 +348,7 @@ BOOL pfile_ui_save_create(_uiheroinfo *heroinfo)
game_2_ui_player(&plr[0], heroinfo, FALSE); game_2_ui_player(&plr[0], heroinfo, FALSE);
if (!gbVanilla) { if (!gbVanilla) {
SaveHotkeys(); SaveHotkeys();
SaveHeroItems(&plr[0]);
} }
pfile_flush(TRUE, save_num); pfile_flush(TRUE, save_num);
return TRUE; return TRUE;
@ -396,9 +405,15 @@ void pfile_read_player_from_save()
app_fatal("Unable to load character"); app_fatal("Unable to load character");
gbValidSaveFile = pfile_archive_contains_game(archive, save_num); gbValidSaveFile = pfile_archive_contains_game(archive, save_num);
if (!gbValidSaveFile) if (gbValidSaveFile)
gbIsHellfireSaveGame = pkplr.bIsHellfire; pkplr.bIsHellfire = gbIsHellfireSaveGame;
UnPackPlayer(&pkplr, myplr, FALSE); UnPackPlayer(&pkplr, myplr, FALSE);
LoadHeroItems(&plr[myplr]);
RemoveEmptyInventory(myplr);
CalcPlrInv(myplr, FALSE);
pfile_SFileCloseArchive(archive); pfile_SFileCloseArchive(archive);
} }

4
Source/player.cpp

@ -3473,8 +3473,8 @@ void ValidatePlayer()
} }
Uint64 msk = 0; Uint64 msk = 0;
for (b = 1; b < MAX_SPELLS; b++) { for (b = SPL_FIREBOLT; b < MAX_SPELLS; b++) {
if (GetSpellBookLevel(b) != -1) { if (GetSpellBookLevel((spell_id)b) != -1) {
msk |= GetSpellBitmask(b); msk |= GetSpellBitmask(b);
if (plr[myplr]._pSplLvl[b] > MAX_SPELL_LEVEL) if (plr[myplr]._pSplLvl[b] > MAX_SPELL_LEVEL)
plr[myplr]._pSplLvl[b] = MAX_SPELL_LEVEL; plr[myplr]._pSplLvl[b] = MAX_SPELL_LEVEL;

19
Source/spells.cpp

@ -343,7 +343,7 @@ void DoHealOther(int pnum, int rid)
} }
} }
int GetSpellBookLevel(int s) int GetSpellBookLevel(spell_id s)
{ {
if (gbIsSpawn) { if (gbIsSpawn) {
switch (s) { switch (s) {
@ -353,6 +353,8 @@ int GetSpellBookLevel(int s)
case SPL_FLARE: case SPL_FLARE:
case SPL_BONESPIRIT: case SPL_BONESPIRIT:
return -1; return -1;
default:
break;
} }
} }
@ -361,6 +363,10 @@ int GetSpellBookLevel(int s)
case SPL_NOVA: case SPL_NOVA:
case SPL_APOCA: case SPL_APOCA:
return -1; return -1;
default:
if (s > SPL_LASTDIABLO)
return -1;
break;
} }
} }
@ -368,13 +374,15 @@ int GetSpellBookLevel(int s)
switch (s) { switch (s) {
case SPL_ELEMENT: case SPL_ELEMENT:
return -1; return -1;
default:
break;
} }
} }
return spelldata[s].sBookLvl; return spelldata[s].sBookLvl;
} }
int GetSpellStaffLevel(int s) int GetSpellStaffLevel(spell_id s)
{ {
if (gbIsSpawn) { if (gbIsSpawn) {
switch (s) { switch (s) {
@ -385,13 +393,20 @@ int GetSpellStaffLevel(int s)
case SPL_FLARE: case SPL_FLARE:
case SPL_BONESPIRIT: case SPL_BONESPIRIT:
return -1; return -1;
default:
break;
} }
} }
if (!gbIsHellfire && s > SPL_LASTDIABLO)
return -1;
if (gbIsHellfire) { if (gbIsHellfire) {
switch (s) { switch (s) {
case SPL_ELEMENT: case SPL_ELEMENT:
return -1; return -1;
default:
break;
} }
} }

4
Source/spells.h

@ -20,8 +20,8 @@ void EnsureValidReadiedSpell(PlayerStruct &player);
void CastSpell(int id, int spl, int sx, int sy, int dx, int dy, int spllvl); void CastSpell(int id, int spl, int sx, int sy, int dx, int dy, int spllvl);
void DoResurrect(int pnum, int rid); void DoResurrect(int pnum, int rid);
void DoHealOther(int pnum, int rid); void DoHealOther(int pnum, int rid);
int GetSpellBookLevel(int s); int GetSpellBookLevel(spell_id s);
int GetSpellStaffLevel(int s); int GetSpellStaffLevel(spell_id s);
#ifdef __cplusplus #ifdef __cplusplus
} }

22
SourceT/pack_test.cpp

@ -220,11 +220,10 @@ TEST(pack, UnPackItem_diablo)
dvl::PkItemStruct is; dvl::PkItemStruct is;
dvl::gbIsHellfire = false; dvl::gbIsHellfire = false;
dvl::gbIsHellfireSaveGame = false;
dvl::gbIsMultiplayer = false; dvl::gbIsMultiplayer = false;
for (size_t i = 0; i < sizeof(PackedDiabloItems) / sizeof(*PackedDiabloItems); i++) { for (size_t i = 0; i < sizeof(PackedDiabloItems) / sizeof(*PackedDiabloItems); i++) {
dvl::UnPackItem(&PackedDiabloItems[i], &id); dvl::UnPackItem(&PackedDiabloItems[i], &id, false);
CompareItems(&id, &DiabloItems[i]); CompareItems(&id, &DiabloItems[i]);
dvl::PackItem(&is, &id); dvl::PackItem(&is, &id);
@ -238,11 +237,10 @@ TEST(pack, UnPackItem_diablo_unique_bug)
dvl::PkItemStruct pkItem = { 6, 655, 14, 5, 60, 60, 0, 0, 0, 0 }; // Veil of Steel - fixed dvl::PkItemStruct pkItem = { 6, 655, 14, 5, 60, 60, 0, 0, 0, 0 }; // Veil of Steel - fixed
dvl::gbIsHellfire = false; dvl::gbIsHellfire = false;
dvl::gbIsHellfireSaveGame = false;
dvl::gbIsMultiplayer = false; dvl::gbIsMultiplayer = false;
dvl::ItemStruct id; dvl::ItemStruct id;
dvl::UnPackItem(&pkItemBug, &id); dvl::UnPackItem(&pkItemBug, &id, false);
ASSERT_STREQ(id._iIName, "Veil of Steel"); ASSERT_STREQ(id._iIName, "Veil of Steel");
ASSERT_EQ(id._itype, dvl::ITYPE_HELM); ASSERT_EQ(id._itype, dvl::ITYPE_HELM);
ASSERT_EQ(id._iClass, dvl::ICLASS_ARMOR); ASSERT_EQ(id._iClass, dvl::ICLASS_ARMOR);
@ -288,11 +286,10 @@ TEST(pack, UnPackItem_diablo_multiplayer)
dvl::PkItemStruct is; dvl::PkItemStruct is;
dvl::gbIsHellfire = false; dvl::gbIsHellfire = false;
dvl::gbIsHellfireSaveGame = false;
dvl::gbIsMultiplayer = true; dvl::gbIsMultiplayer = true;
for (size_t i = 0; i < sizeof(PackedDiabloMPItems) / sizeof(*PackedDiabloMPItems); i++) { for (size_t i = 0; i < sizeof(PackedDiabloMPItems) / sizeof(*PackedDiabloMPItems); i++) {
dvl::UnPackItem(&PackedDiabloMPItems[i], &id); dvl::UnPackItem(&PackedDiabloMPItems[i], &id, false);
CompareItems(&id, &DiabloMPItems[i]); CompareItems(&id, &DiabloMPItems[i]);
dvl::PackItem(&is, &id); dvl::PackItem(&is, &id);
@ -428,14 +425,14 @@ TEST(pack, UnPackItem_hellfire)
dvl::PkItemStruct is; dvl::PkItemStruct is;
dvl::gbIsHellfire = true; dvl::gbIsHellfire = true;
dvl::gbIsHellfireSaveGame = true;
dvl::gbIsMultiplayer = false; dvl::gbIsMultiplayer = false;
for (size_t i = 0; i < sizeof(PackedHellfireItems) / sizeof(*PackedHellfireItems); i++) { for (size_t i = 0; i < sizeof(PackedHellfireItems) / sizeof(*PackedHellfireItems); i++) {
dvl::UnPackItem(&PackedHellfireItems[i], &id); dvl::UnPackItem(&PackedHellfireItems[i], &id, true);
CompareItems(&id, &HellfireItems[i]); CompareItems(&id, &HellfireItems[i]);
dvl::PackItem(&is, &id); dvl::PackItem(&is, &id);
is.dwBuff &= ~dvl::CF_HELLFIRE;
ComparePackedItems(&is, &PackedHellfireItems[i]); ComparePackedItems(&is, &PackedHellfireItems[i]);
} }
} }
@ -446,10 +443,9 @@ TEST(pack, UnPackItem_diablo_strip_hellfire_items)
dvl::ItemStruct id; dvl::ItemStruct id;
dvl::gbIsHellfire = false; dvl::gbIsHellfire = false;
dvl::gbIsHellfireSaveGame = true;
dvl::gbIsMultiplayer = false; dvl::gbIsMultiplayer = false;
dvl::UnPackItem(&is, &id); dvl::UnPackItem(&is, &id, true);
ASSERT_EQ(id._itype, dvl::ITYPE_NONE); ASSERT_EQ(id._itype, dvl::ITYPE_NONE);
} }
@ -459,7 +455,7 @@ TEST(pack, UnPackItem_empty)
dvl::PkItemStruct is = { 0, 0, 0xFFFF, 0, 0, 0, 0, 0, 0, 0 }; dvl::PkItemStruct is = { 0, 0, 0xFFFF, 0, 0, 0, 0, 0, 0, 0 };
dvl::ItemStruct id; dvl::ItemStruct id;
dvl::UnPackItem(&is, &id); dvl::UnPackItem(&is, &id, false);
ASSERT_EQ(id._itype, dvl::ITYPE_NONE); ASSERT_EQ(id._itype, dvl::ITYPE_NONE);
} }
@ -479,7 +475,7 @@ TEST(pack, PackItem_empty)
static void compareGold(const dvl::PkItemStruct *is, int iCurs) static void compareGold(const dvl::PkItemStruct *is, int iCurs)
{ {
dvl::ItemStruct id; dvl::ItemStruct id;
dvl::UnPackItem(is, &id); dvl::UnPackItem(is, &id, false);
ASSERT_EQ(id._iCurs, iCurs); ASSERT_EQ(id._iCurs, iCurs);
ASSERT_EQ(id.IDidx, dvl::IDI_GOLD); ASSERT_EQ(id.IDidx, dvl::IDI_GOLD);
ASSERT_EQ(id._ivalue, is->wValue); ASSERT_EQ(id._ivalue, is->wValue);
@ -514,7 +510,7 @@ TEST(pack, UnPackItem_ear)
dvl::PkItemStruct is = { 1633955154, 17509, 23, 111, 103, 117, 101, 68, 19843, 0 }; dvl::PkItemStruct is = { 1633955154, 17509, 23, 111, 103, 117, 101, 68, 19843, 0 };
dvl::ItemStruct id; dvl::ItemStruct id;
dvl::UnPackItem(&is, &id); dvl::UnPackItem(&is, &id, false);
ASSERT_STREQ(id._iName, "Ear of Dead-RogueDM"); ASSERT_STREQ(id._iName, "Ear of Dead-RogueDM");
ASSERT_EQ(id._ivalue, 3); ASSERT_EQ(id._ivalue, 3);

1
enums.h

@ -2006,6 +2006,7 @@ typedef enum spell_id {
SPL_HEALOTHER = 0x22, SPL_HEALOTHER = 0x22,
SPL_FLARE = 0x23, SPL_FLARE = 0x23,
SPL_BONESPIRIT = 0x24, SPL_BONESPIRIT = 0x24,
SPL_LASTDIABLO = SPL_BONESPIRIT,
SPL_MANA = 0x25, SPL_MANA = 0x25,
SPL_MAGI = 0x26, SPL_MAGI = 0x26,
SPL_JESTER = 0x27, SPL_JESTER = 0x27,

Loading…
Cancel
Save