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()
{
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;
}

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;
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;

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 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);

1
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,

42
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<spell_id>(s));
if (sLevel != -1 && lvl >= sLevel) {
rv--;
bs = static_cast<spell_id>(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<spell_id>(s));
if (sLevel != -1 && l >= sLevel) {
rv--;
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]._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;

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
*/
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);

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)
{
pItem->_iSeed = file->nextLE<Sint32>();
@ -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<Uint32>();
if (gbIsHellfireSaveGame)
pItem->_iDamAcFlags = file->nextLE<Sint32>();
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<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
* @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<Uint32>(pItem->_iStatFlag);
file->writeLE<Sint32>(idx);
file->skip(4); // Unused
file->writeLE<Uint32>(pItem->dwBuff);
if (gbIsHellfire)
file->writeLE<Uint32>(pItem->_iDamAcFlags);
}
@ -1667,6 +1735,18 @@ static void SavePortal(SaveHelper *file, int i)
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()
{
SaveHelper file("game", FILEBUFF);
@ -1990,6 +2070,10 @@ void LoadLevel()
}
}
if (gbIsHellfireSaveGame != gbIsHellfire) {
RemoveEmptyLevelItems();
}
if (!gbSkipSync) {
AutomapZoomReset();
ResyncQuests();

10
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();

11
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);
}

1
Source/msg.h

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

1
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) {

27
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++;
}

4
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

25
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);
}

4
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;

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) {
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;
}
}

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 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
}

22
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);

1
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,

Loading…
Cancel
Save