mirror of
https://github.com/jmarshall23/Hellfire.git
synced 2026-08-11 23:40:51 +02:00
471 lines
14 KiB
C++
471 lines
14 KiB
C++
/*-----------------------------------------------------------------------**
|
|
** Diablo
|
|
**
|
|
** sync.cpp
|
|
**
|
|
** (C)1995 Condor, Inc. All rights reserved.
|
|
**
|
|
**-----------------------------------------------------------------------**
|
|
** $Header: /Diablo/SYNC.CPP 3 2/10/97 6:23p Dbrevik2 $
|
|
**-----------------------------------------------------------------------**
|
|
**
|
|
** File Routines
|
|
**-----------------------------------------------------------------------*/
|
|
|
|
|
|
#include "diablo.h"
|
|
#pragma hdrstop
|
|
#include "sound.h"
|
|
#include "msg.h"
|
|
#include "multi.h"
|
|
#include "gendung.h"
|
|
#include "engine.h"
|
|
#include "items.h"
|
|
#include "player.h"
|
|
#include "error.h"
|
|
#include "debug.h"
|
|
#include "monster.h"
|
|
#include "monstint.h"
|
|
#include "inv.h"
|
|
|
|
//******************************************************************
|
|
// externs
|
|
//******************************************************************
|
|
void M_ClearSquares(int nMonster);
|
|
void delta_sync_monster(const TSyncMonster * p,BYTE bLevel);
|
|
|
|
|
|
//******************************************************************
|
|
// private
|
|
//******************************************************************
|
|
static int sgnMonsters;
|
|
static WORD sgwDelta[MAXMONSTERS];
|
|
static WORD sgwLRU[MAXMONSTERS];
|
|
static int sgnLRUScan;
|
|
|
|
#define LRU_NEVER 0xffff
|
|
#define LRU_REINIT 0xfffe
|
|
#define LRU_INIT 0xff
|
|
|
|
static int sgnSyncItem = 0;
|
|
static int sgnSyncObj = 0;
|
|
static int sgnSyncPInv = 0;
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
static void prep_monster_list(void) {
|
|
for (int i = 0; i < nummonsters; i++) {
|
|
int mi = monstactive[i];
|
|
sgwDelta[mi] = abs(plr[myplr]._px - monster[mi]._mx) +
|
|
abs(plr[myplr]._py - monster[mi]._my);
|
|
|
|
// make all the squelched monsters particularly unattractive
|
|
// make all the unsquelched monsters decrement their LRU
|
|
// counters so that they will get sent eventually
|
|
if (! monster[mi]._msquelch)
|
|
sgwDelta[mi] += 0x1000;
|
|
else if (sgwLRU[mi])
|
|
sgwLRU[mi]--;
|
|
}
|
|
}
|
|
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
static void get_monster(TSyncMonster * p,int mi) {
|
|
// get the true monster number
|
|
// and save monster's parameters
|
|
p->_mndx = mi;
|
|
p->_mx = monster[mi]._mx;
|
|
p->_my = monster[mi]._my;
|
|
// encode player/monster enemy
|
|
p->_menemy = encode_enemy(mi);
|
|
p->_mdelta = sgwDelta[mi] > 0xff ? 0xff : sgwDelta[mi];
|
|
// cant have this since menemy maybe a monster id
|
|
//app_assert((DWORD) p->_menemy < MAX_PLRS);
|
|
|
|
// reset delta table so we don't find this monster again
|
|
sgwDelta[mi] = 0xffff;
|
|
|
|
// reset LRU count so we don't resend this monster unnecessarily
|
|
sgwLRU[mi] = monster[mi]._msquelch ? LRU_REINIT : LRU_NEVER;
|
|
}
|
|
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
static BOOL get_best_monster(TSyncMonster * p) {
|
|
// search all the monsters for the one closest to the player
|
|
int nFound = -1;
|
|
DWORD dwMin = 0xffffffff;
|
|
for (int i = 0; i < nummonsters; i++) {
|
|
int mi = monstactive[i];
|
|
|
|
// is this monster close to the local player?
|
|
if (sgwDelta[mi] >= dwMin) continue;
|
|
|
|
// is this a monster which has already been sent LRU?
|
|
if (sgwLRU[mi] >= LRU_REINIT) continue;
|
|
|
|
// found better candidate
|
|
dwMin = sgwDelta[mi];
|
|
nFound = mi;
|
|
}
|
|
if (nFound == -1) return FALSE;
|
|
get_monster(p,nFound);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
static BOOL get_lru_monster(TSyncMonster * p) {
|
|
// search all the monsters for the lowest LRU number
|
|
int nFound = -1;
|
|
DWORD dwMin = LRU_REINIT;
|
|
for (int i = 0; i < nummonsters; i++,sgnLRUScan++) {
|
|
if (sgnLRUScan >= nummonsters) sgnLRUScan = 0;
|
|
int mi = monstactive[sgnLRUScan];
|
|
|
|
// is this a monster which hasn't been sent in a while?
|
|
if (sgwLRU[mi] >= dwMin) continue;
|
|
|
|
dwMin = sgwLRU[mi];
|
|
nFound = mi;
|
|
}
|
|
if (nFound == -1) return FALSE;
|
|
get_monster(p,nFound);
|
|
return TRUE;
|
|
}
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
static void FillHeaderSync(TSyncHeader * pHdr)
|
|
{
|
|
// Fill one item
|
|
int ii;
|
|
if (numitems > 0) {
|
|
if (sgnSyncItem >= numitems) sgnSyncItem = 0;
|
|
ii = itemactive[sgnSyncItem++];
|
|
pHdr->bItemI = ii;
|
|
pHdr->bItemX = item[ii]._ix;
|
|
pHdr->bItemY = item[ii]._iy;
|
|
pHdr->wItemIndx = item[ii].IDidx;
|
|
if (item[ii].IDidx == IDI_EAR) {
|
|
pHdr->wItemCI = (item[ii]._iName[7] << 8) | item[ii]._iName[8];
|
|
pHdr->dwItemSeed = (item[ii]._iName[9] << 24) |
|
|
(item[ii]._iName[10] << 16) |
|
|
(item[ii]._iName[11] << 8) |
|
|
item[ii]._iName[12];
|
|
pHdr->bItemId = item[ii]._iName[13];
|
|
pHdr->bItemDur = item[ii]._iName[14];
|
|
pHdr->bItemMDur = item[ii]._iName[15];
|
|
pHdr->bItemCh = item[ii]._iName[16];
|
|
pHdr->bItemMCh = item[ii]._iName[17];
|
|
pHdr->wItemVal = (item[ii]._iName[18] << 8) | ((item[ii]._iCurs - ITEM_EAR1) << 6) | item[ii]._ivalue;
|
|
pHdr->dwItemBuff = (item[ii]._iName[19] << 24) |
|
|
(item[ii]._iName[20] << 16) |
|
|
(item[ii]._iName[21] << 8) |
|
|
item[ii]._iName[22];
|
|
} else {
|
|
pHdr->wItemCI = item[ii]._iCreateInfo;
|
|
pHdr->dwItemSeed = item[ii]._iSeed;
|
|
pHdr->bItemId = item[ii]._iIdentified;
|
|
pHdr->bItemDur = item[ii]._iDurability;
|
|
pHdr->bItemMDur = item[ii]._iMaxDur;
|
|
pHdr->bItemCh = item[ii]._iCharges;
|
|
pHdr->bItemMCh = item[ii]._iMaxCharges;
|
|
if (item[ii].IDidx == IDI_GOLD) pHdr->wItemVal = item[ii]._ivalue;
|
|
}
|
|
}
|
|
else {
|
|
pHdr->bItemI = 0xff;
|
|
}
|
|
|
|
// Fill with player inv info
|
|
app_assert((DWORD) sgnSyncPInv < NUM_INVLOC);
|
|
ItemStruct *itm = &plr[myplr].InvBody[sgnSyncPInv];
|
|
if (itm->_itype != -1) {
|
|
pHdr->bPInvLoc = sgnSyncPInv;
|
|
pHdr->wPInvIndx = itm->IDidx;
|
|
pHdr->wPInvCI = itm->_iCreateInfo;
|
|
pHdr->dwPInvSeed = itm->_iSeed;
|
|
// drb.patch1.start.02/10/97
|
|
pHdr->bPInvId = itm->_iIdentified;
|
|
// drb.patch1.end.02/10/97
|
|
}
|
|
else {
|
|
pHdr->bPInvLoc = 0xff;
|
|
}
|
|
|
|
// next item
|
|
sgnSyncPInv++;
|
|
if (sgnSyncPInv >= NUM_INVLOC)
|
|
sgnSyncPInv = 0;
|
|
}
|
|
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
DWORD sync_get(BYTE * pbBuf,DWORD dwMaxLen) {
|
|
// if there are no monsters to sync, exit
|
|
if (nummonsters < 1) return dwMaxLen;
|
|
|
|
// is there enough space for at header + one monster sync
|
|
if (dwMaxLen < sizeof(TSyncMonster) + sizeof(TSyncHeader))
|
|
return dwMaxLen;
|
|
|
|
// setup header
|
|
TSyncHeader * pHdr = (TSyncHeader *) pbBuf;
|
|
pbBuf += sizeof(TSyncHeader);
|
|
dwMaxLen -= sizeof(TSyncHeader);
|
|
pHdr->bCmd = CMD_SYNCDATA;
|
|
pHdr->bLevel = currlevel;
|
|
pHdr->wLen = 0;
|
|
// Put one object, one item and one plr inv item into sync packets
|
|
FillHeaderSync(pHdr);
|
|
|
|
// make sure we don't overrun header maximum length
|
|
app_assert(dwMaxLen <= 0xffff);
|
|
|
|
prep_monster_list();
|
|
for (int nMonsters = 0; nMonsters < nummonsters; nMonsters++) {
|
|
if (dwMaxLen < sizeof(TSyncMonster)) break;
|
|
|
|
BOOL bGotOne = FALSE;
|
|
if (nMonsters < 2) bGotOne = get_lru_monster((TSyncMonster *) pbBuf);
|
|
if (! bGotOne) bGotOne = get_best_monster((TSyncMonster *) pbBuf);
|
|
if (! bGotOne) break;
|
|
|
|
pbBuf += sizeof(TSyncMonster);
|
|
pHdr->wLen += sizeof(TSyncMonster);
|
|
dwMaxLen -= sizeof(TSyncMonster);
|
|
}
|
|
|
|
// return bytes left in buffer
|
|
return dwMaxLen;
|
|
}
|
|
|
|
|
|
/*-----------------------------------------------------------------------*
|
|
**-----------------------------------------------------------------------*
|
|
|
|
byte monstupdate[MAXMONSTERS];
|
|
|
|
void DaveMonstMap(BOOL initupdate, int pnum)
|
|
{
|
|
int i,j,m;
|
|
char tempstr[512], tempstr2[256];
|
|
|
|
if (initupdate)
|
|
for (i = 0; i < MAXMONSTERS; i++) monstupdate[i] = 0;
|
|
|
|
for (j = 0; j < DMAXY; j++) {
|
|
for (i = 0; i < DMAXX; i++) {
|
|
if (dFlags[i][j] & BFLAG_MONSTLR) {
|
|
if (dMonster[i-1][j] != dMonster[i][j-1]) {
|
|
sprintf(tempstr, "Error in map @ %i,%i x-1,j = %i, x,j-1 = %i\n", i, j, dMonster[i-1][j], dMonster[i][j-1]);
|
|
if (dMonster[i-1][j] == 0) sprintf(tempstr2, "Null monster\n @ %i,%i", i-1, j);
|
|
else {
|
|
if (dMonster[i-1][j] > 0) m = dMonster[i-1][j] - 1;
|
|
else m = -(dMonster[i-1][j] + 1);
|
|
sprintf(tempstr2, "Monst %i xy = %i,%i oxy = %i,%i mode = %i Updated = %i\n",
|
|
m, monster[m]._mx, monster[m]._my, monster[m]._moldx, monster[m]._moldy, monster[m]._mmode, monstupdate[m]);
|
|
}
|
|
strcat(tempstr, tempstr2);
|
|
if (dMonster[i][j-1] == 0) sprintf(tempstr2, "Null monster @ %i,%i\n", i, j-1);
|
|
else {
|
|
if (dMonster[i][j-1] > 0) m = dMonster[i][j-1] - 1;
|
|
else m = -(dMonster[i][j-1] + 1);
|
|
sprintf(tempstr2, "Monst %i xy = %i,%i oxy = %i,%i mode = %i Updated = %i\n",
|
|
m, monster[m]._mx, monster[m]._my, monster[m]._moldx, monster[m]._moldy, monster[m]._mmode, monstupdate[m]);
|
|
}
|
|
strcat(tempstr, tempstr2);
|
|
sprintf(tempstr2, "Info from plr %i = %s", pnum, plr[pnum]._pName);
|
|
strcat(tempstr, tempstr2);
|
|
app_fatal(tempstr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
//******************************************************************
|
|
//******************************************************************
|
|
static void sync_monster(int pnum,const TSyncMonster * p) {
|
|
|
|
// get monster index
|
|
int ndx = p->_mndx;
|
|
|
|
// did I just kill this monster?
|
|
if (monster[ndx]._mhitpoints <= 0) return;
|
|
|
|
// check for valid index
|
|
for (int i = 0; i < nummonsters && monstactive[i] != ndx; i++);
|
|
//app_assert(i < nummonsters);
|
|
|
|
// calc distance I think he is from me
|
|
DWORD delta =
|
|
abs(plr[myplr]._px - monster[ndx]._mx) +
|
|
abs(plr[myplr]._py - monster[ndx]._my);
|
|
if (delta > 0xff) delta = 0xff;
|
|
|
|
// if my delta is less than the other player's delta, use mine
|
|
if (delta < p->_mdelta) return;
|
|
// if the deltas are the same use the lowest player num
|
|
else if ((delta == p->_mdelta) && (pnum > myplr)) return;
|
|
|
|
// if the monster is going to be there soon, don't do anything
|
|
if (monster[ndx]._mfutx == p->_mx && monster[ndx]._mfuty == p->_my) return;
|
|
|
|
// Snake crash fix. Don't mess with missile monsters while they are warping
|
|
if (monster[ndx]._mmode == MM_MISSILE) return;
|
|
// Don't mess with stone cursed monsters
|
|
if (monster[ndx]._mmode == MM_STONE) return;
|
|
|
|
// if the monster is far away, jump him into position
|
|
int mdx = abs(monster[ndx]._mx - p->_mx);
|
|
int mdy = abs(monster[ndx]._my - p->_my);
|
|
if (mdx > 2 || mdy > 2) {
|
|
if (! dMonster[p->_mx][p->_my]) {
|
|
// remove where monster is now
|
|
M_ClearSquares(ndx);
|
|
|
|
// put him in new location
|
|
dMonster[p->_mx][p->_my] = ndx + 1;
|
|
monster[ndx]._mx = p->_mx;
|
|
monster[ndx]._my = p->_my;
|
|
decode_enemy(ndx, p->_menemy);
|
|
M_StartStand(ndx, GetDirection(p->_mx,p->_my, monster[ndx]._menemyx, monster[ndx]._menemyy));
|
|
|
|
// make sure he's awake
|
|
monster[ndx]._msquelch = 255;
|
|
}
|
|
}
|
|
else {
|
|
// if the monster is not already on the way to a nearby
|
|
// square, start him immediately on his way
|
|
if ((monster[ndx]._mmode < MM_WALK) || (monster[ndx]._mmode > MM_WALK3)) {
|
|
int md = GetDirection(monster[ndx]._mx, monster[ndx]._my, p->_mx, p->_my);
|
|
|
|
if (DirOK(ndx, md)) {
|
|
// remove where monster is now
|
|
M_ClearSquares(ndx);
|
|
|
|
// make monster walk into square where he should be
|
|
dMonster[monster[ndx]._mx][monster[ndx]._my] = ndx + 1;
|
|
M_WalkDir(ndx, md);
|
|
|
|
// make sure he's awake
|
|
monster[ndx]._msquelch = 255;
|
|
}
|
|
}
|
|
}
|
|
|
|
// sync enemy
|
|
// cant have this since menemy maybe a monster id
|
|
//app_assert((DWORD) p->_menemy < MAX_PLRS);
|
|
decode_enemy(ndx, p->_menemy);
|
|
}
|
|
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
static void UpdateHeaderSync(int pnum, const TSyncHeader * pHdr)
|
|
{
|
|
// Fill one item
|
|
if (pHdr->bItemI != 0xff) {
|
|
int ii = pHdr->bItemI;
|
|
if ((pHdr->wItemIndx != item[ii].IDidx) ||
|
|
(pHdr->wItemCI != item[ii]._iCreateInfo) ||
|
|
(pHdr->dwItemSeed != item[ii]._iSeed)) {
|
|
ii = FindGetItem(pHdr->wItemIndx, pHdr->wItemCI, pHdr->dwItemSeed);
|
|
if (ii == -1) {
|
|
SyncPutItem(pnum,
|
|
pHdr->bItemX,
|
|
pHdr->bItemY,
|
|
pHdr->wItemIndx,
|
|
pHdr->wItemCI,
|
|
pHdr->dwItemSeed,
|
|
pHdr->bItemId,
|
|
pHdr->bItemDur,
|
|
pHdr->bItemMDur,
|
|
pHdr->bItemCh,
|
|
pHdr->bItemMCh,
|
|
pHdr->wItemVal,
|
|
pHdr->dwItemBuff,
|
|
pHdr->wPLToHit,
|
|
pHdr->wMaxDam,
|
|
pHdr->bMinStr,
|
|
pHdr->bMinMag,
|
|
pHdr->bMinDex,
|
|
pHdr->bAC
|
|
);
|
|
}
|
|
}
|
|
}
|
|
// Fill with player inv info
|
|
if (pHdr->bPInvLoc != 0xff) {
|
|
ItemStruct *itm = &plr[myplr].InvBody[pHdr->bPInvLoc];
|
|
if ((pHdr->wPInvIndx != itm->IDidx) ||
|
|
(pHdr->wPInvCI != itm->_iCreateInfo) ||
|
|
// drb.patch1.start.02/10/97
|
|
// (pHdr->dwPInvSeed != itm->_iSeed)) {
|
|
(pHdr->dwPInvSeed != itm->_iSeed) ||
|
|
(pHdr->bPInvId != itm->_iIdentified)) {
|
|
// SyncInvPaste(pnum, pHdr->bPInvLoc, pHdr->wPInvIndx, pHdr->wPInvCI, pHdr->dwPInvSeed,);
|
|
SyncInvPaste(pnum, pHdr->bPInvLoc, pHdr->wPInvIndx, pHdr->wPInvCI, pHdr->dwPInvSeed, pHdr->bPInvId);
|
|
// drb.patch1.end.02/10/97
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
DWORD sync_update(int pnum,const BYTE * pbBuf) {
|
|
const TSyncHeader * pHdr = (const TSyncHeader *) pbBuf;
|
|
pbBuf += sizeof(TSyncHeader);
|
|
|
|
// make sure we have a valid sync record
|
|
if (pHdr->bCmd != CMD_SYNCDATA)
|
|
app_fatal("bad sync command");
|
|
|
|
// don't resync while we're in buffer mode
|
|
app_assert(gbBufferMsgs != BUFFER_PROCESS);
|
|
if (gbBufferMsgs == BUFFER_ON)
|
|
return pHdr->wLen + sizeof(TSyncHeader);
|
|
|
|
// we don't resync using our own information
|
|
if (pnum == myplr) return pHdr->wLen + sizeof(TSyncHeader);
|
|
|
|
// Make sure we are in sync with others on the level
|
|
//if (currlevel == pHdr->bLevel) UpdateHeaderSync(pnum, pHdr);
|
|
|
|
WORD wLen = pHdr->wLen;
|
|
while (wLen >= sizeof(TSyncMonster)) {
|
|
if (currlevel == pHdr->bLevel)
|
|
sync_monster(pnum,(const TSyncMonster *) pbBuf);
|
|
delta_sync_monster((const TSyncMonster *) pbBuf,pHdr->bLevel);
|
|
pbBuf += sizeof(TSyncMonster);
|
|
wLen -= sizeof(TSyncMonster);
|
|
}
|
|
|
|
// make sure we used all the bytes
|
|
app_assert(wLen == 0);
|
|
|
|
// return true number of bytes in our sync section
|
|
return pHdr->wLen + sizeof(TSyncHeader);
|
|
}
|
|
|
|
|
|
//******************************************************************
|
|
//******************************************************************
|
|
void sync_init() {
|
|
// don't let all the players start scanning in the same location.
|
|
// if each player is scanning from a different location, it is
|
|
// more likely that multiple monsters can be synced every turn
|
|
sgnLRUScan = myplr * 16;
|
|
FillMemory(sgwLRU,sizeof sgwLRU,LRU_INIT);
|
|
}
|