diff --git a/Source/debug.cpp b/Source/debug.cpp index 4fb45b550..824b1f946 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -85,12 +85,8 @@ void TakeGoldCheat() void MaxSpellsCheat() { - int i; - - int maxSpells = gbIsHellfire ? MAX_SPELLS : 37; - - for (i = 1; i < maxSpells; i++) { - if (GetSpellBookLevel(i) != -1) { + for (int i = SPL_FIREBOLT; i < MAX_SPELLS; i++) { + if (GetSpellBookLevel((spell_id)i) != -1) { plr[myplr]._pMemSpells |= GetSpellBitmask(i); plr[myplr]._pSplLvl[i] = 10; } diff --git a/Source/inv.cpp b/Source/inv.cpp index 0eb07744a..5ea55a4da 100644 --- a/Source/inv.cpp +++ b/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; 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->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) { RecreateEar(ii, icreateinfo, iseed, Id, dur, mdur, ch, mch, ivalue, ibuff); } else { - RecreateItem(ii, idx, icreateinfo, iseed, ivalue); + RecreateItem(ii, idx, icreateinfo, iseed, ivalue, (ibuff & CF_HELLFIRE) != 0); if (Id) item[ii]._iIdentified = TRUE; 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]._iMinDex = min_dex; item[ii]._iAC = ac; + item[ii].dwBuff = ibuff; } item[ii]._ix = x; diff --git a/Source/inv.h b/Source/inv.h index b3d765c9e..4f15cb5dd 100644 --- a/Source/inv.h +++ b/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 SpecialAutoPlace(int pnum, int ii, const ItemStruct &item); 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 RemoveInvItem(int pnum, int iv); void RemoveSpdBarItem(int pnum, int iv); diff --git a/Source/itemdat.h b/Source/itemdat.h index df3a731a4..6e67b361c 100644 --- a/Source/itemdat.h +++ b/Source/itemdat.h @@ -463,6 +463,7 @@ typedef enum item_effect_type { IPL_ADDMANAAC, IPL_FIRERESCLVL, IPL_AC_CURSE, + IDI_LASTDIABLO = IPL_AC_CURSE, IPL_FIRERES_CURSE, IPL_LIGHTRES_CURSE, IPL_MAGICRES_CURSE, diff --git a/Source/items.cpp b/Source/items.cpp index 1ae0f4265..f8bcf39b6 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -312,11 +312,19 @@ bool IsItemAvailable(int i) return true; } +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; } @@ -329,6 +337,9 @@ 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) @@ -1284,6 +1295,8 @@ void SetPlrHandItem(ItemStruct *h, int idata) h->_iSufPower = IPL_INVALID; h->_iMagical = ITEM_QUALITY_NORMAL; h->IDidx = idata; + if (gbIsHellfire) + h->dwBuff |= CF_HELLFIRE; } void GetPlrHandSeed(ItemStruct *h) @@ -1658,7 +1671,7 @@ void GetBookSpell(int i, int lvl) int s = SPL_FIREBOLT; enum spell_id bs = SPL_FIREBOLT; while (rv > 0) { - int sLevel = GetSpellBookLevel(s); + int sLevel = GetSpellBookLevel(static_cast(s)); if (sLevel != -1 && lvl >= sLevel) { rv--; bs = static_cast(s); @@ -1702,8 +1715,6 @@ void GetStaffPower(int i, int lvl, int bs, BOOL onlygood) if (tmp == 0 || onlygood) { nl = 0; 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) { addok = TRUE; if (onlygood && !PL_Prefix[j].PLOk) @@ -1768,7 +1779,7 @@ void GetStaffSpell(int i, int lvl, BOOL onlygood) int s = SPL_FIREBOLT; enum spell_id bs = SPL_NULL; while (rv > 0) { - int sLevel = GetSpellStaffLevel(s); + int sLevel = GetSpellStaffLevel(static_cast(s)); if (sLevel != -1 && l >= sLevel) { rv--; bs = static_cast(s); @@ -1853,6 +1864,8 @@ void GetItemAttrs(int i, int idata, int lvl) item[i]._iMinMag = AllItemsList[idata].iMinMag; item[i]._iMinDex = AllItemsList[idata].iMinDex; item[i].IDidx = idata; + if (gbIsHellfire) + item[i].dwBuff |= CF_HELLFIRE; item[i]._iPrePower = 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) { nt = 0; for (j = 0; PL_Prefix[j].PLPower != IPL_INVALID; j++) { - if (!gbIsHellfire && j > 82) - break; 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; @@ -2365,8 +2376,6 @@ void GetItemPower(int i, int minlvl, int maxlvl, int flgs, BOOL onlygood) if (post != 0) { nl = 0; for (j = 0; PL_Suffix[j].PLPower != IPL_INVALID; j++) { - if (!gbIsHellfire && j > 94) - break; 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)) @@ -2620,7 +2629,7 @@ int CheckUnique(int i, int lvl, int uper, BOOL recreate) numu = 0; memset(uok, 0, sizeof(uok)); for (j = 0; UniqueItemList[j].UIItemId != UITYPE_INVALID; j++) { - if (!gbIsHellfire && j > 89) + if (!IsUniqueAvailable(j)) break; if (UniqueItemList[j].UIItemId == AllItemsList[item[i].IDidx].iItemId && 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); } -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) { SetPlrHandItem(&item[ii], IDI_GOLD); item[ii]._iSeed = iseed; item[ii]._iCreateInfo = icreateinfo; item[ii]._ivalue = ivalue; SetPlrHandGoldCurs(&item[ii]); + gbIsHellfire = _gbIsHellfire; return; } if (icreateinfo == 0) { SetPlrHandItem(&item[ii], idx); SetPlrHandSeed(&item[ii], iseed); + gbIsHellfire = _gbIsHellfire; return; } if ((icreateinfo & CF_UNIQUE) == 0) { if (icreateinfo & CF_TOWN) { RecreateTownItem(ii, idx, icreateinfo, iseed, ivalue); + gbIsHellfire = _gbIsHellfire; return; } if ((icreateinfo & CF_USEFUL) == CF_USEFUL) { SetupAllUseful(ii, iseed, icreateinfo & CF_LEVEL); + gbIsHellfire = _gbIsHellfire; return; } } @@ -2941,6 +2957,7 @@ void RecreateItem(int ii, int idx, WORD icreateinfo, int iseed, int ivalue) bool pregen = (icreateinfo & CF_PREGEN) != 0; 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) @@ -3034,8 +3051,7 @@ void items_427ABA(int x, int y) dItem[x][y] = ii + 1; - gbIsHellfireSaveGame = gbIsHellfire; - UnPackItem(&PkSItem, &item[ii]); + UnPackItem(&PkSItem, &item[ii], (PkSItem.dwBuff & CF_HELLFIRE) != 0); item[ii]._ix = x; item[ii]._iy = y; RespawnItem(ii, FALSE); @@ -5307,7 +5323,7 @@ int ItemNoFlippy() 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; diff --git a/Source/items.h b/Source/items.h index 686dd7767..84a3e29ac 100644 --- a/Source/items.h +++ b/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 */ typedef enum icreateinfo_flag { + // clang-format off CF_LEVEL = (1 << 6) - 1, CF_ONLYGOOD = 1 << 6, CF_UPER15 = 1 << 7, @@ -51,8 +52,15 @@ typedef enum icreateinfo_flag { CF_USEFUL = CF_UPER15 | CF_UPER1, CF_TOWN = CF_SMITH | CF_SMITHPREMIUM | CF_BOY | CF_WITCH | CF_HEALER, + // clang-format on } icreateinfo_flag; +typedef enum icreateinfo_flag2 { + // clang-format off + CF_HELLFIRE = 1, + // clang-format on +} icreateinfo_flag2; + typedef struct ItemStruct { Sint32 _iSeed; Uint16 _iCreateInfo; @@ -121,6 +129,7 @@ typedef struct ItemStruct { Sint8 _iMinDex; bool _iStatFlag; Sint32 IDidx; + Uint32 dwBuff; Sint32 _iDamAcFlags; /** @@ -263,6 +272,7 @@ extern BOOL UniqueItemFlag[128]; extern int numitems; bool IsItemAvailable(int i); +bool IsUniqueAvailable(int i); void InitItemGFX(); void InitItems(); 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 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 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 items_427A72(); 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 RecalcStoreStats(); 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 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); diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index e2df267a4..568978ad3 100644 --- a/Source/loadsave.cpp +++ b/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) { pItem->_iSeed = file->nextLE(); @@ -261,16 +278,13 @@ static void LoadItemData(LoadHelper *file, ItemStruct *pItem) if (!gbIsHellfireSaveGame) { pItem->IDidx = RemapItemIdxFromDiablo(pItem->IDidx); } - file->skip(4); // Unused + pItem->dwBuff = file->nextLE(); if (gbIsHellfireSaveGame) pItem->_iDamAcFlags = file->nextLE(); else pItem->_iDamAcFlags = 0; - if (!IsItemAvailable(pItem->IDidx)) { - pItem->IDidx = 0; - pItem->_itype = ITYPE_NONE; - } + RemoveInvalidItem(pItem); } static void LoadItems(LoadHelper *file, const int n, ItemStruct *pItem) @@ -824,7 +838,7 @@ bool IsHeaderValid(Uint32 magicNumber) return false; } -void ConvertLevels() +static void ConvertLevels() { // Backup current level state bool _setlevel = setlevel; @@ -910,6 +924,56 @@ void SaveHotkeys() file.writeLE(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 * @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++) LoadPortal(&file, i); - if (gbIsHellfireSaveGame != gbIsHellfire) + if (gbIsHellfireSaveGame != gbIsHellfire) { ConvertLevels(); + RemoveEmptyInventory(myplr); + } LoadGameLevel(firstflag, ENTRY_LOAD); SyncInitPlr(myplr); @@ -1102,8 +1168,10 @@ void LoadGame(BOOL firstflag) SetCursor_(CURSOR_HAND); gbProcessPlayers = TRUE; - if (gbIsHellfireSaveGame != gbIsHellfire) + if (gbIsHellfireSaveGame != gbIsHellfire) { + RemoveEmptyLevelItems(); SaveGame(); + } gbIsHellfireSaveGame = gbIsHellfire; } @@ -1192,7 +1260,7 @@ static void SaveItem(SaveHelper *file, ItemStruct *pItem) file->skip(1); // Alignment file->writeLE(pItem->_iStatFlag); file->writeLE(idx); - file->skip(4); // Unused + file->writeLE(pItem->dwBuff); if (gbIsHellfire) file->writeLE(pItem->_iDamAcFlags); } @@ -1667,6 +1735,18 @@ static void SavePortal(SaveHelper *file, int i) file->writeLE(pPortal->setlvl); } +void SaveHeroItems(PlayerStruct *pPlayer) +{ + size_t items = NUM_INVLOC + NUM_INV_GRID_ELEM + MAXBELTITEMS; + SaveHelper file("heroitems", items * sizeof(ItemStruct)); + + file.writeLE(gbIsHellfire); + + SaveItems(&file, pPlayer->InvBody, NUM_INVLOC); + SaveItems(&file, pPlayer->InvList, NUM_INV_GRID_ELEM); + SaveItems(&file, pPlayer->SpdList, MAXBELTITEMS); +} + void SaveGame() { SaveHelper file("game", FILEBUFF); @@ -1990,6 +2070,10 @@ void LoadLevel() } } + if (gbIsHellfireSaveGame != gbIsHellfire) { + RemoveEmptyLevelItems(); + } + if (!gbSkipSync) { AutomapZoomReset(); ResyncQuests(); diff --git a/Source/loadsave.h b/Source/loadsave.h index 68573967b..8be603525 100644 --- a/Source/loadsave.h +++ b/Source/loadsave.h @@ -6,6 +6,8 @@ #ifndef __LOADSAVE_H__ #define __LOADSAVE_H__ +#include "player.h" + DEVILUTION_BEGIN_NAMESPACE #ifdef __cplusplus @@ -15,12 +17,20 @@ extern "C" { extern bool gbIsHellfireSaveGame; extern int giNumberOfLevels; +void RemoveInvalidItem(ItemStruct *pItem); int RemapItemIdxFromDiablo(int i); int RemapItemIdxToDiablo(int i); bool IsHeaderValid(Uint32 magicNumber); 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 SaveHotkeys(); +void SaveHeroItems(PlayerStruct *pPlayer); void SaveGame(); void SaveLevel(); void LoadLevel(); diff --git a/Source/msg.cpp b/Source/msg.cpp index 9f595ab42..eb47cd390 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -654,6 +654,7 @@ void DeltaAddItem(int ii) pD->bMinMag = item[ii]._iMinMag; pD->bMinDex = item[ii]._iMinDex; pD->bAC = item[ii]._iAC; + pD->dwBuff = item[ii].dwBuff; return; } } @@ -761,7 +762,8 @@ void DeltaLoadLevel() sgLevels[currlevel].item[i].wIndx, sgLevels[currlevel].item[i].wCI, 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) item[ii]._iIdentified = TRUE; item[ii]._iDurability = sgLevels[currlevel].item[i].bDur; @@ -774,6 +776,7 @@ void DeltaLoadLevel() item[ii]._iMinMag = sgLevels[currlevel].item[i].bMinMag; item[ii]._iMinDex = sgLevels[currlevel].item[i].bMinDex; item[ii]._iAC = sgLevels[currlevel].item[i].bAC; + item[ii].dwBuff = sgLevels[currlevel].item[i].dwBuff; } x = sgLevels[currlevel].item[i].x; 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.bMinDex = item[ii]._iMinDex; cmd.bAC = item[ii]._iAC; + cmd.dwBuff = item[ii].dwBuff; } if (bHiPri) @@ -1098,6 +1102,7 @@ void NetSendCmdPItem(BOOL bHiPri, BYTE bCmd, BYTE x, BYTE y) cmd.bMinMag = plr[myplr].HoldItem._iMinMag; cmd.bMinDex = plr[myplr].HoldItem._iMinDex; cmd.bAC = plr[myplr].HoldItem._iAC; + cmd.dwBuff = plr[myplr].HoldItem.dwBuff; } if (bHiPri) @@ -1116,6 +1121,7 @@ void NetSendCmdChItem(BOOL bHiPri, BYTE bLoc) cmd.wCI = plr[myplr].HoldItem._iCreateInfo; cmd.dwSeed = plr[myplr].HoldItem._iSeed; cmd.bId = plr[myplr].HoldItem._iIdentified; + cmd.dwBuff = plr[myplr].HoldItem.dwBuff; if (bHiPri) NetSendHiPri((BYTE *)&cmd, sizeof(cmd)); @@ -1169,6 +1175,7 @@ void NetSendCmdDItem(BOOL bHiPri, int ii) cmd.bMinMag = item[ii]._iMinMag; cmd.bMinDex = item[ii]._iMinDex; cmd.bAC = item[ii]._iAC; + cmd.dwBuff = item[ii].dwBuff; } if (bHiPri) @@ -2133,7 +2140,7 @@ static DWORD On_CHANGEPLRITEMS(TCmd *pCmd, int pnum) if (gbBufferMsgs == 1) msg_send_packet(pnum, p, sizeof(*p)); 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); } diff --git a/Source/msg.h b/Source/msg.h index 02bd0fd86..d9669d102 100644 --- a/Source/msg.h +++ b/Source/msg.h @@ -139,6 +139,7 @@ typedef struct TCmdChItem { Uint16 wCI; Sint32 dwSeed; Uint8 bId; + Uint32 dwBuff; } TCmdChItem; typedef struct TCmdDelItem { diff --git a/Source/multi.cpp b/Source/multi.cpp index 5790b2acd..d50aa58a8 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -902,7 +902,6 @@ void recv_plrinfo(int pnum, TCmdPlrInfoHdr *p, BOOL recv) sgwPackPlrOffsetTbl[pnum] = 0; multi_player_left_msg(pnum, 0); plr[pnum]._pGFXLoad = 0; - gbIsHellfireSaveGame = gbIsHellfire; UnPackPlayer(&netplr[pnum], pnum, TRUE); if (!recv) { diff --git a/Source/pack.cpp b/Source/pack.cpp index 700aa0e58..8f3abf67f 100644 --- a/Source/pack.cpp +++ b/Source/pack.cpp @@ -38,6 +38,7 @@ void PackItem(PkItemStruct *id, const ItemStruct *is) id->bMCh = is->_iMaxCharges; if (is->IDidx == IDI_GOLD) 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 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); if (idx == 0xFFFF) { @@ -139,7 +140,7 @@ void UnPackItem(const PkItemStruct *is, ItemStruct *id) return; } - if (!gbIsHellfireSaveGame) { + if (!isHellfire) { idx = RemapItemIdxFromDiablo(idx); } @@ -162,13 +163,20 @@ void UnPackItem(const PkItemStruct *is, ItemStruct *id) SwapLE32(is->dwBuff)); } else { 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]._iIdentified = is->bId & 1; item[MAXITEMS]._iDurability = is->bDur; item[MAXITEMS]._iMaxDur = is->bMDur; item[MAXITEMS]._iCharges = is->bCh; item[MAXITEMS]._iMaxCharges = is->bMCh; + + RemoveInvalidItem(&item[MAXITEMS]); + + if (isHellfire) + item[MAXITEMS].dwBuff |= CF_HELLFIRE; + else + item[MAXITEMS].dwBuff &= ~CF_HELLFIRE; } *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; int i; @@ -226,7 +234,7 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok) pPlayer->_pMaxHPBase = SwapLE32(pPack->pMaxHPBase); pPlayer->_pHPBase = SwapLE32(pPack->pHPBase); pPlayer->_pBaseToBlk = ToBlkTbl[pPlayer->_pClass]; - if (!killok) + if (!netSync) if ((int)(pPlayer->_pHPBase & 0xFFFFFFC0) < 64) pPlayer->_pHPBase = 64; @@ -243,7 +251,8 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok) pi = &pPlayer->InvBody[0]; 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++; pi++; } @@ -252,7 +261,8 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok) pi = &pPlayer->InvList[0]; 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++; pi++; } @@ -267,7 +277,8 @@ void UnPackPlayer(PkPlayerStruct *pPack, int pnum, BOOL killok) pi = &pPlayer->SpdList[0]; for (i = 0; i < MAXBELTITEMS; i++) { - UnPackItem(pki, pi); + bool isHellfire = netSync ? ((pki->dwBuff & CF_HELLFIRE) != 0) : pPack->bIsHellfire; + UnPackItem(pki, pi, isHellfire); pki++; pi++; } diff --git a/Source/pack.h b/Source/pack.h index 8ea0f8daf..fa0254bc0 100644 --- a/Source/pack.h +++ b/Source/pack.h @@ -80,9 +80,9 @@ typedef struct PkPlayerStruct { #pragma pack(pop) 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 UnPackItem(const PkItemStruct *is, ItemStruct *id); +void UnPackItem(const PkItemStruct *is, ItemStruct *id, bool isHellfire); /* rdata */ #ifdef __cplusplus diff --git a/Source/pfile.cpp b/Source/pfile.cpp index 25fe75721..b7d8c7a61 100644 --- a/Source/pfile.cpp +++ b/Source/pfile.cpp @@ -184,8 +184,10 @@ void pfile_write_hero() if (pfile_open_archive(save_num)) { PackPlayer(&pkplr, myplr, !gbIsMultiplayer); pfile_encode_hero(&pkplr); - if (!gbVanilla) + if (!gbVanilla) { SaveHotkeys(); + SaveHeroItems(&plr[myplr]); + } pfile_flush(!gbIsMultiplayer, save_num); } } @@ -276,9 +278,15 @@ BOOL pfile_ui_set_hero_infos(BOOL (*ui_add_hero_info)(_uiheroinfo *)) _uiheroinfo uihero; strcpy(hero_names[i], pkplr.pName); bool hasSaveGame = pfile_archive_contains_game(archive, i); - if (!hasSaveGame) - gbIsHellfireSaveGame = pkplr.bIsHellfire; + if (hasSaveGame) + pkplr.bIsHellfire = gbIsHellfireSaveGame; + UnPackPlayer(&pkplr, 0, FALSE); + + LoadHeroItems(&plr[0]); + RemoveEmptyInventory(0); + CalcPlrInv(0, FALSE); + game_2_ui_player(plr, &uihero, hasSaveGame); ui_add_hero_info(&uihero); } @@ -340,6 +348,7 @@ BOOL pfile_ui_save_create(_uiheroinfo *heroinfo) game_2_ui_player(&plr[0], heroinfo, FALSE); if (!gbVanilla) { SaveHotkeys(); + SaveHeroItems(&plr[0]); } pfile_flush(TRUE, save_num); return TRUE; @@ -396,9 +405,15 @@ void pfile_read_player_from_save() app_fatal("Unable to load character"); gbValidSaveFile = pfile_archive_contains_game(archive, save_num); - if (!gbValidSaveFile) - gbIsHellfireSaveGame = pkplr.bIsHellfire; + if (gbValidSaveFile) + pkplr.bIsHellfire = gbIsHellfireSaveGame; + UnPackPlayer(&pkplr, myplr, FALSE); + + LoadHeroItems(&plr[myplr]); + RemoveEmptyInventory(myplr); + CalcPlrInv(myplr, FALSE); + pfile_SFileCloseArchive(archive); } diff --git a/Source/player.cpp b/Source/player.cpp index ec1baf5e5..ef83e8a34 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -3473,8 +3473,8 @@ void ValidatePlayer() } Uint64 msk = 0; - for (b = 1; b < MAX_SPELLS; b++) { - if (GetSpellBookLevel(b) != -1) { + for (b = SPL_FIREBOLT; b < MAX_SPELLS; b++) { + if (GetSpellBookLevel((spell_id)b) != -1) { msk |= GetSpellBitmask(b); if (plr[myplr]._pSplLvl[b] > MAX_SPELL_LEVEL) plr[myplr]._pSplLvl[b] = MAX_SPELL_LEVEL; diff --git a/Source/spells.cpp b/Source/spells.cpp index ef56ea478..a3f3cd633 100644 --- a/Source/spells.cpp +++ b/Source/spells.cpp @@ -343,7 +343,7 @@ void DoHealOther(int pnum, int rid) } } -int GetSpellBookLevel(int s) +int GetSpellBookLevel(spell_id s) { if (gbIsSpawn) { switch (s) { @@ -353,6 +353,8 @@ int GetSpellBookLevel(int s) case SPL_FLARE: case SPL_BONESPIRIT: return -1; + default: + break; } } @@ -361,6 +363,10 @@ int GetSpellBookLevel(int s) case SPL_NOVA: case SPL_APOCA: return -1; + default: + if (s > SPL_LASTDIABLO) + return -1; + break; } } @@ -368,13 +374,15 @@ int GetSpellBookLevel(int s) switch (s) { case SPL_ELEMENT: return -1; + default: + break; } } return spelldata[s].sBookLvl; } -int GetSpellStaffLevel(int s) +int GetSpellStaffLevel(spell_id s) { if (gbIsSpawn) { switch (s) { @@ -385,13 +393,20 @@ int GetSpellStaffLevel(int s) case SPL_FLARE: case SPL_BONESPIRIT: return -1; + default: + break; } } + if (!gbIsHellfire && s > SPL_LASTDIABLO) + return -1; + if (gbIsHellfire) { switch (s) { case SPL_ELEMENT: return -1; + default: + break; } } diff --git a/Source/spells.h b/Source/spells.h index d50ab9d61..0c5ca3345 100644 --- a/Source/spells.h +++ b/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 DoResurrect(int pnum, int rid); void DoHealOther(int pnum, int rid); -int GetSpellBookLevel(int s); -int GetSpellStaffLevel(int s); +int GetSpellBookLevel(spell_id s); +int GetSpellStaffLevel(spell_id s); #ifdef __cplusplus } diff --git a/SourceT/pack_test.cpp b/SourceT/pack_test.cpp index a81802469..cb9633cf4 100644 --- a/SourceT/pack_test.cpp +++ b/SourceT/pack_test.cpp @@ -220,11 +220,10 @@ TEST(pack, UnPackItem_diablo) dvl::PkItemStruct is; dvl::gbIsHellfire = false; - dvl::gbIsHellfireSaveGame = false; dvl::gbIsMultiplayer = false; 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]); 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::gbIsHellfire = false; - dvl::gbIsHellfireSaveGame = false; dvl::gbIsMultiplayer = false; dvl::ItemStruct id; - dvl::UnPackItem(&pkItemBug, &id); + dvl::UnPackItem(&pkItemBug, &id, false); ASSERT_STREQ(id._iIName, "Veil of Steel"); ASSERT_EQ(id._itype, dvl::ITYPE_HELM); ASSERT_EQ(id._iClass, dvl::ICLASS_ARMOR); @@ -288,11 +286,10 @@ TEST(pack, UnPackItem_diablo_multiplayer) dvl::PkItemStruct is; dvl::gbIsHellfire = false; - dvl::gbIsHellfireSaveGame = false; dvl::gbIsMultiplayer = true; 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]); dvl::PackItem(&is, &id); @@ -428,14 +425,14 @@ TEST(pack, UnPackItem_hellfire) dvl::PkItemStruct is; dvl::gbIsHellfire = true; - dvl::gbIsHellfireSaveGame = true; dvl::gbIsMultiplayer = false; 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]); dvl::PackItem(&is, &id); + is.dwBuff &= ~dvl::CF_HELLFIRE; ComparePackedItems(&is, &PackedHellfireItems[i]); } } @@ -446,10 +443,9 @@ TEST(pack, UnPackItem_diablo_strip_hellfire_items) dvl::ItemStruct id; dvl::gbIsHellfire = false; - dvl::gbIsHellfireSaveGame = true; dvl::gbIsMultiplayer = false; - dvl::UnPackItem(&is, &id); + dvl::UnPackItem(&is, &id, true); 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::ItemStruct id; - dvl::UnPackItem(&is, &id); + dvl::UnPackItem(&is, &id, false); ASSERT_EQ(id._itype, dvl::ITYPE_NONE); } @@ -479,7 +475,7 @@ TEST(pack, PackItem_empty) static void compareGold(const dvl::PkItemStruct *is, int iCurs) { dvl::ItemStruct id; - dvl::UnPackItem(is, &id); + dvl::UnPackItem(is, &id, false); ASSERT_EQ(id._iCurs, iCurs); ASSERT_EQ(id.IDidx, dvl::IDI_GOLD); 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::ItemStruct id; - dvl::UnPackItem(&is, &id); + dvl::UnPackItem(&is, &id, false); ASSERT_STREQ(id._iName, "Ear of Dead-RogueDM"); ASSERT_EQ(id._ivalue, 3); diff --git a/enums.h b/enums.h index 6aedb41b4..610ce5176 100644 --- a/enums.h +++ b/enums.h @@ -2006,6 +2006,7 @@ typedef enum spell_id { SPL_HEALOTHER = 0x22, SPL_FLARE = 0x23, SPL_BONESPIRIT = 0x24, + SPL_LASTDIABLO = SPL_BONESPIRIT, SPL_MANA = 0x25, SPL_MAGI = 0x26, SPL_JESTER = 0x27,