You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2736 lines
72 KiB

/**
* @file msg.cpp
*
* Implementation of function for sending and reciving network messages.
*/
#include <climits>
#include <memory>
5 years ago
#include <fmt/format.h>
#include <list>
5 years ago
#include "DiabloUI/diabloui.h"
#include "automap.h"
#include "control.h"
#include "dead.h"
#include "drlg_l1.h"
#include "dthread.h"
#include "encrypt.h"
#include "engine/random.hpp"
#include "gamemenu.h"
#include "lighting.h"
#include "missiles.h"
#include "nthread.h"
#include "objects.h"
#include "options.h"
#include "pfile.h"
#include "plrmsg.h"
#include "spells.h"
#include "storm/storm.h"
#include "sync.h"
#include "town.h"
#include "towners.h"
#include "tmsg.h"
#include "trigs.h"
#include "utils/language.h"
namespace devilution {
bool deltaload;
BYTE gbBufferMsgs;
int dwRecCount;
namespace {
struct TMegaPkt {
uint32_t spaceLeft;
byte data[32000];
TMegaPkt()
: spaceLeft(sizeof(data))
{
}
};
#define MAX_CHUNKS (NUMLEVELS + 4)
uint32_t sgdwOwnerWait;
uint32_t sgdwRecvOffset;
int sgnCurrMegaPlayer;
DLevel sgLevels[NUMLEVELS];
BYTE sbLastCmd;
byte sgRecvBuf[sizeof(DLevel) + 1];
BYTE sgbRecvCmd;
LocalLevel sgLocals[NUMLEVELS];
DJunk sgJunk;
bool sgbDeltaChanged;
BYTE sgbDeltaChunks;
std::list<TMegaPkt> MegaPktList;
void GetNextPacket()
{
MegaPktList.emplace_back();
5 years ago
}
void FreePackets()
5 years ago
{
MegaPktList.clear();
5 years ago
}
void PrePacket()
5 years ago
{
uint8_t playerId = std::numeric_limits<uint8_t>::max();
for (TMegaPkt &pkt : MegaPktList) {
byte *data = pkt.data;
size_t spaceLeft = sizeof(pkt.data);
while (spaceLeft != pkt.spaceLeft) {
auto cmdId = static_cast<_cmd_id>(*data);
if (cmdId == FAKE_CMD_SETID) {
auto *cmd = (TFakeCmdPlr *)data;
5 years ago
data += sizeof(*cmd);
5 years ago
spaceLeft -= sizeof(*cmd);
playerId = cmd->bPlr;
continue;
}
if (cmdId == FAKE_CMD_DROPID) {
auto *cmd = (TFakeDropPlr *)data;
5 years ago
data += sizeof(*cmd);
spaceLeft -= sizeof(*cmd);
multi_player_left(cmd->bPlr, cmd->dwReason);
continue;
5 years ago
}
if (playerId >= MAX_PLRS) {
Log("Missing source of network message");
return;
}
uint32_t size = ParseCmd(playerId, (TCmd *)data);
if (size == 0) {
Log("Discarding bad network message");
return;
}
uint32_t pktSize = size;
data += pktSize;
spaceLeft -= pktSize;
5 years ago
}
}
}
void SendPacket(int pnum, const void *packet, DWORD dwSize)
{
TFakeCmdPlr cmd;
if (pnum != sgnCurrMegaPlayer) {
sgnCurrMegaPlayer = pnum;
cmd.bCmd = FAKE_CMD_SETID;
cmd.bPlr = pnum;
SendPacket(pnum, &cmd, sizeof(cmd));
}
if (MegaPktList.back().spaceLeft < dwSize)
GetNextPacket();
5 years ago
TMegaPkt &currMegaPkt = MegaPktList.back();
memcpy(currMegaPkt.data + sizeof(currMegaPkt.data) - currMegaPkt.spaceLeft, packet, dwSize);
currMegaPkt.spaceLeft -= dwSize;
}
int WaitForTurns()
5 years ago
{
DWORD turns;
if (sgbDeltaChunks == 0) {
nthread_send_and_recv_turn(0, 0);
if (!SNetGetOwnerTurnsWaiting(&turns) && SErrGetLastError() == STORM_ERROR_NOT_IN_GAME)
5 years ago
return 100;
if (SDL_GetTicks() - sgdwOwnerWait <= 2000 && turns < gdwTurnsInTransit)
5 years ago
return 0;
sgbDeltaChunks++;
}
5 years ago
multi_process_network_packets();
nthread_send_and_recv_turn(0, 0);
if (nthread_has_500ms_passed()) {
nthread_recv_turns();
}
5 years ago
if (gbGameDestroyed)
return 100;
if (gbDeltaSender >= MAX_PLRS) {
sgbDeltaChunks = 0;
sgbRecvCmd = CMD_DLEVEL_END;
gbDeltaSender = MyPlayerId;
5 years ago
nthread_set_turn_upper_bit();
}
if (sgbDeltaChunks == MAX_CHUNKS - 1) {
sgbDeltaChunks = MAX_CHUNKS;
return 99;
}
return 100 * sgbDeltaChunks / MAX_CHUNKS;
}
byte *DeltaExportItem(byte *dst, const TCmdPItem *src)
{
5 years ago
for (int i = 0; i < MAXITEMS; i++, src++) {
if (src->bCmd == CMD_INVALID) {
*dst++ = byte { 0xFF };
} else {
5 years ago
memcpy(dst, src, sizeof(TCmdPItem));
dst += sizeof(TCmdPItem);
}
}
5 years ago
return dst;
}
size_t DeltaImportItem(const byte *src, TCmdPItem *dst)
{
size_t size = 0;
5 years ago
for (int i = 0; i < MAXITEMS; i++, dst++) {
if (src[size] == byte { 0xFF }) {
5 years ago
memset(dst, 0xFF, sizeof(TCmdPItem));
size++;
} else {
memcpy(dst, &src[size], sizeof(TCmdPItem));
size += sizeof(TCmdPItem);
}
}
return size;
}
byte *DeltaExportObject(byte *dst, const DObjectStr *src)
{
memcpy(dst, src, sizeof(DObjectStr) * MAXOBJECTS);
return dst + sizeof(DObjectStr) * MAXOBJECTS;
}
size_t DeltaImportObject(const byte *src, DObjectStr *dst)
5 years ago
{
memcpy(dst, src, sizeof(DObjectStr) * MAXOBJECTS);
return sizeof(DObjectStr) * MAXOBJECTS;
5 years ago
}
byte *DeltaExportMonster(byte *dst, const DMonsterStr *src)
{
5 years ago
for (int i = 0; i < MAXMONSTERS; i++, src++) {
if (src->_mx == 0xFF) {
*dst++ = byte { 0xFF };
} else {
memcpy(dst, src, sizeof(DMonsterStr));
dst += sizeof(DMonsterStr);
}
}
return dst;
}
void DeltaImportMonster(const byte *src, DMonsterStr *dst)
{
size_t size = 0;
5 years ago
for (int i = 0; i < MAXMONSTERS; i++, dst++) {
if (src[size] == byte { 0xFF }) {
5 years ago
memset(dst, 0xFF, sizeof(DMonsterStr));
size++;
5 years ago
} else {
memcpy(dst, &src[size], sizeof(DMonsterStr));
size += sizeof(DMonsterStr);
5 years ago
}
}
}
byte *DeltaExportJunk(byte *dst)
{
for (auto &portal : sgJunk.portal) {
if (portal.x == 0xFF) {
*dst++ = byte { 0xFF };
} else {
memcpy(dst, &portal, sizeof(DPortal));
dst += sizeof(DPortal);
}
}
int q = 0;
for (auto &quest : Quests) {
if (!QuestsData[quest._qidx].isSinglePlayerOnly) {
sgJunk.quests[q].qlog = quest._qlog ? 1 : 0;
sgJunk.quests[q].qstate = quest._qactive;
sgJunk.quests[q].qvar1 = quest._qvar1;
memcpy(dst, &sgJunk.quests[q], sizeof(MultiQuests));
dst += sizeof(MultiQuests);
q++;
}
}
return dst;
}
void DeltaImportJunk(const byte *src)
{
for (int i = 0; i < MAXPORTAL; i++) {
if (*src == byte { 0xFF }) {
5 years ago
memset(&sgJunk.portal[i], 0xFF, sizeof(DPortal));
src++;
SetPortalStats(i, false, 0, 0, 0, DTYPE_TOWN);
5 years ago
} else {
memcpy(&sgJunk.portal[i], src, sizeof(DPortal));
src += sizeof(DPortal);
SetPortalStats(
5 years ago
i,
true,
5 years ago
sgJunk.portal[i].x,
sgJunk.portal[i].y,
sgJunk.portal[i].level,
(dungeon_type)sgJunk.portal[i].ltype);
5 years ago
}
}
int q = 0;
for (auto &quest : Quests) {
if (!QuestsData[quest._qidx].isSinglePlayerOnly) {
memcpy(&sgJunk.quests[q], src, sizeof(MultiQuests));
5 years ago
src += sizeof(MultiQuests);
quest._qlog = sgJunk.quests[q].qlog != 0;
quest._qactive = sgJunk.quests[q].qstate;
quest._qvar1 = sgJunk.quests[q].qvar1;
q++;
5 years ago
}
}
}
DWORD CompressData(byte *buffer, byte *end)
5 years ago
{
DWORD size = end - buffer - 1;
DWORD pkSize = PkwareCompress(buffer + 1, size);
*buffer = size != pkSize ? byte { 1 } : byte { 0 };
5 years ago
return pkSize + 1;
}
void DeltaImportData(BYTE cmd, DWORD recvOffset)
5 years ago
{
if (sgRecvBuf[0] != byte { 0 })
PkwareDecompress(&sgRecvBuf[1], recvOffset, sizeof(sgRecvBuf) - 1);
5 years ago
byte *src = &sgRecvBuf[1];
5 years ago
if (cmd == CMD_DLEVEL_JUNK) {
DeltaImportJunk(src);
} else if (cmd >= CMD_DLEVEL_0 && cmd <= CMD_DLEVEL_24) {
5 years ago
BYTE i = cmd - CMD_DLEVEL_0;
src += DeltaImportItem(src, sgLevels[i].item);
src += DeltaImportObject(src, sgLevels[i].object);
5 years ago
DeltaImportMonster(src, sgLevels[i].monster);
} else {
app_fatal("Unkown network message type: %i", cmd);
5 years ago
}
sgbDeltaChunks++;
sgbDeltaChanged = true;
5 years ago
}
DWORD OnLevelData(int pnum, const TCmd *pCmd)
5 years ago
{
auto *p = (TCmdPlrInfoHdr *)pCmd;
5 years ago
if (gbDeltaSender != pnum) {
if (p->bCmd == CMD_DLEVEL_END || (p->bCmd == CMD_DLEVEL_0 && p->wOffset == 0)) {
5 years ago
gbDeltaSender = pnum;
sgbRecvCmd = CMD_DLEVEL_END;
} else {
return p->wBytes + sizeof(*p);
}
}
if (sgbRecvCmd == CMD_DLEVEL_END) {
if (p->bCmd == CMD_DLEVEL_END) {
sgbDeltaChunks = MAX_CHUNKS - 1;
return p->wBytes + sizeof(*p);
}
if (p->bCmd == CMD_DLEVEL_0 && p->wOffset == 0) {
5 years ago
sgdwRecvOffset = 0;
sgbRecvCmd = p->bCmd;
} else {
return p->wBytes + sizeof(*p);
}
} else if (sgbRecvCmd != p->bCmd) {
DeltaImportData(sgbRecvCmd, sgdwRecvOffset);
if (p->bCmd == CMD_DLEVEL_END) {
sgbDeltaChunks = MAX_CHUNKS - 1;
sgbRecvCmd = CMD_DLEVEL_END;
return p->wBytes + sizeof(*p);
}
sgdwRecvOffset = 0;
sgbRecvCmd = p->bCmd;
5 years ago
}
assert(p->wOffset == sgdwRecvOffset);
5 years ago
memcpy(&sgRecvBuf[p->wOffset], &p[1], p->wBytes);
sgdwRecvOffset += p->wBytes;
return p->wBytes + sizeof(*p);
}
void DeltaSyncGolem(const TCmdGolem *pG, int pnum, BYTE bLevel)
{
if (!gbIsMultiplayer)
5 years ago
return;
sgbDeltaChanged = true;
5 years ago
DMonsterStr *pD = &sgLevels[bLevel].monster[pnum];
pD->_mx = pG->_mx;
pD->_my = pG->_my;
pD->_mactive = UINT8_MAX;
5 years ago
pD->_menemy = pG->_menemy;
pD->_mdir = pG->_mdir;
pD->_mhitpoints = pG->_mhitpoints;
}
void DeltaLeaveSync(BYTE bLevel)
{
if (!gbIsMultiplayer)
5 years ago
return;
if (currlevel == 0)
glSeedTbl[0] = AdvanceRndSeed();
5 years ago
if (currlevel <= 0)
return;
for (int i = 0; i < ActiveMonsterCount; i++) {
int ma = ActiveMonsters[i];
auto &monster = Monsters[ma];
if (monster._mhitpoints == 0)
5 years ago
continue;
sgbDeltaChanged = true;
5 years ago
DMonsterStr *pD = &sgLevels[bLevel].monster[ma];
pD->_mx = monster.position.tile.x;
pD->_my = monster.position.tile.y;
pD->_mdir = monster._mdir;
pD->_menemy = encode_enemy(monster);
pD->_mhitpoints = monster._mhitpoints;
pD->_mactive = monster._msquelch;
}
memcpy(&sgLocals[bLevel].automapsv, AutomapView, sizeof(AutomapView));
}
void DeltaSyncObject(int oi, _cmd_id bCmd, BYTE bLevel)
5 years ago
{
if (!gbIsMultiplayer)
5 years ago
return;
sgbDeltaChanged = true;
5 years ago
sgLevels[bLevel].object[oi].bCmd = bCmd;
5 years ago
}
bool DeltaGetItem(const TCmdGItem *pI, BYTE bLevel)
5 years ago
{
if (!gbIsMultiplayer)
return true;
5 years ago
5 years ago
TCmdPItem *pD = sgLevels[bLevel].item;
for (int i = 0; i < MAXITEMS; i++, pD++) {
if (pD->bCmd == CMD_INVALID || pD->wIndx != pI->wIndx || pD->wCI != pI->wCI || pD->dwSeed != pI->dwSeed)
5 years ago
continue;
if (pD->bCmd == CMD_WALKXY) {
return true;
5 years ago
}
if (pD->bCmd == CMD_STAND) {
sgbDeltaChanged = true;
5 years ago
pD->bCmd = CMD_WALKXY;
return true;
5 years ago
}
if (pD->bCmd == CMD_ACK_PLRINFO) {
sgbDeltaChanged = true;
pD->bCmd = CMD_INVALID;
return true;
5 years ago
}
app_fatal("delta:1");
}
if ((pI->wCI & CF_PREGEN) == 0)
return false;
5 years ago
pD = sgLevels[bLevel].item;
for (int i = 0; i < MAXITEMS; i++, pD++) {
if (pD->bCmd == CMD_INVALID) {
sgbDeltaChanged = true;
5 years ago
pD->bCmd = CMD_WALKXY;
pD->x = pI->x;
pD->y = pI->y;
pD->wIndx = pI->wIndx;
pD->wCI = pI->wCI;
pD->dwSeed = pI->dwSeed;
pD->bId = pI->bId;
pD->bDur = pI->bDur;
pD->bMDur = pI->bMDur;
pD->bCh = pI->bCh;
pD->bMCh = pI->bMCh;
pD->wValue = pI->wValue;
pD->dwBuff = pI->dwBuff;
pD->wToHit = pI->wToHit;
pD->wMaxDam = pI->wMaxDam;
pD->bMinStr = pI->bMinStr;
pD->bMinMag = pI->bMinMag;
pD->bMinDex = pI->bMinDex;
pD->bAC = pI->bAC;
break;
}
}
return true;
5 years ago
}
void DeltaPutItem(const TCmdPItem *pI, int x, int y, BYTE bLevel)
5 years ago
{
if (!gbIsMultiplayer)
5 years ago
return;
5 years ago
TCmdPItem *pD = sgLevels[bLevel].item;
for (int i = 0; i < MAXITEMS; i++, pD++) {
5 years ago
if (pD->bCmd != CMD_WALKXY
&& pD->bCmd != CMD_INVALID
5 years ago
&& pD->wIndx == pI->wIndx
&& pD->wCI == pI->wCI
&& pD->dwSeed == pI->dwSeed) {
5 years ago
if (pD->bCmd == CMD_ACK_PLRINFO)
return;
app_fatal("%s", _("Trying to drop a floor item?"));
5 years ago
}
}
pD = sgLevels[bLevel].item;
for (int i = 0; i < MAXITEMS; i++, pD++) {
if (pD->bCmd == CMD_INVALID) {
sgbDeltaChanged = true;
5 years ago
memcpy(pD, pI, sizeof(TCmdPItem));
pD->bCmd = CMD_ACK_PLRINFO;
pD->x = x;
pD->y = y;
return;
}
}
}
bool IOwnLevel(int nReqLevel)
{
int i;
for (i = 0; i < MAX_PLRS; i++) {
if (!Players[i].plractive)
continue;
if (Players[i]._pLvlChanging)
continue;
if (Players[i].plrlevel != nReqLevel)
continue;
if (i == MyPlayerId && gbBufferMsgs != 0)
continue;
break;
}
return i == MyPlayerId;
}
void DeltaOpenPortal(int pnum, uint8_t x, uint8_t y, uint8_t bLevel, dungeon_type bLType, bool bSetLvl)
{
sgbDeltaChanged = true;
sgJunk.portal[pnum].x = x;
sgJunk.portal[pnum].y = y;
sgJunk.portal[pnum].level = bLevel;
sgJunk.portal[pnum].ltype = bLType;
sgJunk.portal[pnum].setlvl = bSetLvl ? 1 : 0;
}
void CheckUpdatePlayer(int pnum)
{
if (gbIsMultiplayer && pnum == MyPlayerId)
pfile_update(true);
}
5 years ago
void PlayerMessageFormat(const char *pszFmt, ...)
{
static DWORD msgErrTimer;
DWORD ticks;
char msg[256];
va_list va;
va_start(va, pszFmt);
ticks = SDL_GetTicks();
if (ticks - msgErrTimer >= 5000) {
msgErrTimer = ticks;
vsprintf(msg, pszFmt, va);
ErrorPlrMsg(msg);
}
va_end(va);
}
void NetSendCmdGItem2(bool usonly, _cmd_id bCmd, uint8_t mast, uint8_t pnum, const TCmdGItem &item)
{
TCmdGItem cmd;
memcpy(&cmd, &item, sizeof(cmd));
cmd.bPnum = pnum;
cmd.bCmd = bCmd;
cmd.bMaster = mast;
if (!usonly) {
cmd.dwTime = 0;
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
5 years ago
return;
}
int ticks = SDL_GetTicks();
if (cmd.dwTime == 0) {
cmd.dwTime = ticks;
} else if (ticks - cmd.dwTime > 5000) {
return;
}
tmsg_add((byte *)&cmd, sizeof(cmd));
}
bool NetSendCmdReq2(_cmd_id bCmd, uint8_t mast, uint8_t pnum, const TCmdGItem &item)
{
TCmdGItem cmd;
memcpy(&cmd, &item, sizeof(cmd));
cmd.bCmd = bCmd;
cmd.bPnum = pnum;
cmd.bMaster = mast;
int ticks = SDL_GetTicks();
if (cmd.dwTime == 0)
cmd.dwTime = ticks;
else if (ticks - cmd.dwTime > 5000)
return false;
tmsg_add((byte *)&cmd, sizeof(cmd));
return true;
}
void NetSendCmdExtra(const TCmdGItem &item)
{
TCmdGItem cmd;
memcpy(&cmd, &item, sizeof(cmd));
cmd.dwTime = 0;
cmd.bCmd = CMD_ITEMEXTRA;
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
DWORD OnWalk(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdLoc *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position)) {
ClrPlrPath(player);
MakePlrPath(player, position, true);
player.destAction = ACTION_NONE;
}
return sizeof(message);
}
DWORD OnAddStrength(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 256)
ModifyPlrStr(pnum, message.wParam1);
return sizeof(message);
}
DWORD OnAddMagic(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 256)
ModifyPlrMag(pnum, message.wParam1);
return sizeof(message);
}
DWORD OnAddDexterity(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 256)
ModifyPlrDex(pnum, message.wParam1);
return sizeof(message);
}
DWORD OnAddVitality(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 256)
ModifyPlrVit(pnum, message.wParam1);
return sizeof(message);
}
DWORD OnGotoGetItem(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position) && message.wParam1 < MAXITEMS + 1) {
MakePlrPath(player, position, false);
player.destAction = ACTION_PICKUPITEM;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnRequestGetItem(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdGItem *)pCmd;
if (gbBufferMsgs != 1 && IOwnLevel(player.plrlevel)) {
if (GetItemRecord(p->dwSeed, p->wCI, p->wIndx)) {
int ii = FindGetItem(p->wIndx, p->wCI, p->dwSeed);
if (ii != -1) {
NetSendCmdGItem2(false, CMD_GETITEM, MyPlayerId, p->bPnum, *p);
if (p->bPnum != MyPlayerId)
SyncGetItem({ p->x, p->y }, p->wIndx, p->wCI, p->dwSeed);
else
InvGetItem(MyPlayerId, &Items[ii], ii);
SetItemRecord(p->dwSeed, p->wCI, p->wIndx);
} else if (!NetSendCmdReq2(CMD_REQUESTGITEM, MyPlayerId, p->bPnum, *p)) {
NetSendCmdExtra(*p);
}
}
}
return sizeof(*p);
}
DWORD OnGetItem(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdGItem *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
int ii = FindGetItem(p->wIndx, p->wCI, p->dwSeed);
if (DeltaGetItem(p, p->bLevel)) {
if ((currlevel == p->bLevel || p->bPnum == MyPlayerId) && p->bMaster != MyPlayerId) {
if (p->bPnum == MyPlayerId) {
if (currlevel != p->bLevel) {
auto &player = Players[MyPlayerId];
ii = SyncPutItem(player, player.position.tile, p->wIndx, p->wCI, p->dwSeed, p->bId, p->bDur, p->bMDur, p->bCh, p->bMCh, p->wValue, p->dwBuff, p->wToHit, p->wMaxDam, p->bMinStr, p->bMinMag, p->bMinDex, p->bAC);
if (ii != -1)
InvGetItem(MyPlayerId, &Items[ii], ii);
} else {
InvGetItem(MyPlayerId, &Items[ii], ii);
}
} else {
SyncGetItem({ p->x, p->y }, p->wIndx, p->wCI, p->dwSeed);
}
}
} else {
NetSendCmdGItem2(true, CMD_GETITEM, p->bMaster, p->bPnum, *p);
}
}
return sizeof(*p);
}
DWORD OnGotoAutoGetItem(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position) && message.wParam1 < MAXITEMS + 1) {
MakePlrPath(player, position, false);
player.destAction = ACTION_PICKUPAITEM;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnRequestAutoGetItem(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdGItem *)pCmd;
if (gbBufferMsgs != 1 && IOwnLevel(player.plrlevel)) {
if (GetItemRecord(p->dwSeed, p->wCI, p->wIndx)) {
int ii = FindGetItem(p->wIndx, p->wCI, p->dwSeed);
if (ii != -1) {
NetSendCmdGItem2(false, CMD_AGETITEM, MyPlayerId, p->bPnum, *p);
if (p->bPnum != MyPlayerId)
SyncGetItem({ p->x, p->y }, p->wIndx, p->wCI, p->dwSeed);
else
AutoGetItem(MyPlayerId, &Items[p->bCursitem], p->bCursitem);
SetItemRecord(p->dwSeed, p->wCI, p->wIndx);
} else if (!NetSendCmdReq2(CMD_REQUESTAGITEM, MyPlayerId, p->bPnum, *p)) {
NetSendCmdExtra(*p);
}
}
}
return sizeof(*p);
}
DWORD OnAutoGetItem(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdGItem *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
FindGetItem(p->wIndx, p->wCI, p->dwSeed);
if (DeltaGetItem(p, p->bLevel)) {
if ((currlevel == p->bLevel || p->bPnum == MyPlayerId) && p->bMaster != MyPlayerId) {
if (p->bPnum == MyPlayerId) {
if (currlevel != p->bLevel) {
auto &player = Players[MyPlayerId];
int ii = SyncPutItem(player, player.position.tile, p->wIndx, p->wCI, p->dwSeed, p->bId, p->bDur, p->bMDur, p->bCh, p->bMCh, p->wValue, p->dwBuff, p->wToHit, p->wMaxDam, p->bMinStr, p->bMinMag, p->bMinDex, p->bAC);
if (ii != -1)
AutoGetItem(MyPlayerId, &Items[ii], ii);
} else {
AutoGetItem(MyPlayerId, &Items[p->bCursitem], p->bCursitem);
}
} else {
SyncGetItem({ p->x, p->y }, p->wIndx, p->wCI, p->dwSeed);
}
}
} else {
NetSendCmdGItem2(true, CMD_AGETITEM, p->bMaster, p->bPnum, *p);
}
}
return sizeof(*p);
}
DWORD OnItemExtra(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdGItem *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
DeltaGetItem(p, p->bLevel);
if (currlevel == Players[pnum].plrlevel)
SyncGetItem({ p->x, p->y }, p->wIndx, p->wCI, p->dwSeed);
}
return sizeof(*p);
}
DWORD OnPutItem(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdPItem *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p));
else if (currlevel == Players[pnum].plrlevel) {
int ii;
if (pnum == MyPlayerId)
ii = InvPutItem(Players[pnum], { p->x, p->y });
else
ii = SyncPutItem(Players[pnum], { p->x, p->y }, p->wIndx, p->wCI, p->dwSeed, p->bId, p->bDur, p->bMDur, p->bCh, p->bMCh, p->wValue, p->dwBuff, p->wToHit, p->wMaxDam, p->bMinStr, p->bMinMag, p->bMinDex, p->bAC);
if (ii != -1) {
PutItemRecord(p->dwSeed, p->wCI, p->wIndx);
DeltaPutItem(p, Items[ii].position.x, Items[ii].position.y, Players[pnum].plrlevel);
CheckUpdatePlayer(pnum);
}
return sizeof(*p);
} else {
PutItemRecord(p->dwSeed, p->wCI, p->wIndx);
DeltaPutItem(p, p->x, p->y, Players[pnum].plrlevel);
CheckUpdatePlayer(pnum);
}
return sizeof(*p);
}
DWORD OnSyncPutItem(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdPItem *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p));
else if (currlevel == Players[pnum].plrlevel) {
int ii = SyncPutItem(Players[pnum], { p->x, p->y }, p->wIndx, p->wCI, p->dwSeed, p->bId, p->bDur, p->bMDur, p->bCh, p->bMCh, p->wValue, p->dwBuff, p->wToHit, p->wMaxDam, p->bMinStr, p->bMinMag, p->bMinDex, p->bAC);
if (ii != -1) {
PutItemRecord(p->dwSeed, p->wCI, p->wIndx);
DeltaPutItem(p, Items[ii].position.x, Items[ii].position.y, Players[pnum].plrlevel);
CheckUpdatePlayer(pnum);
}
return sizeof(*p);
} else {
PutItemRecord(p->dwSeed, p->wCI, p->wIndx);
DeltaPutItem(p, p->x, p->y, Players[pnum].plrlevel);
CheckUpdatePlayer(pnum);
}
return sizeof(*p);
}
DWORD OnRespawnItem(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdPItem *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
auto &player = Players[pnum];
int playerLevel = player.plrlevel;
if (currlevel == playerLevel && pnum != MyPlayerId) {
SyncPutItem(player, { p->x, p->y }, p->wIndx, p->wCI, p->dwSeed, p->bId, p->bDur, p->bMDur, p->bCh, p->bMCh, p->wValue, p->dwBuff, p->wToHit, p->wMaxDam, p->bMinStr, p->bMinMag, p->bMinDex, p->bAC);
}
PutItemRecord(p->dwSeed, p->wCI, p->wIndx);
DeltaPutItem(p, p->x, p->y, playerLevel);
}
return sizeof(*p);
}
DWORD OnAttackTile(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdLoc *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position)) {
MakePlrPath(player, position, false);
player.destAction = ACTION_ATTACK;
player.destParam1 = position.x;
player.destParam2 = position.y;
}
return sizeof(message);
}
DWORD OnStandingAttackTile(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdLoc *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position)) {
ClrPlrPath(player);
player.destAction = ACTION_ATTACK;
player.destParam1 = position.x;
player.destParam2 = position.y;
}
return sizeof(message);
5 years ago
}
DWORD OnRangedAttackTile(const TCmd *pCmd, Player &player)
5 years ago
{
const auto &message = *reinterpret_cast<const TCmdLoc *>(pCmd);
const Point position { message.x, message.y };
5 years ago
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position)) {
ClrPlrPath(player);
player.destAction = ACTION_RATTACK;
player.destParam1 = position.x;
player.destParam2 = position.y;
}
return sizeof(message);
}
DWORD OnSpellWall(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdLocParam4 *)pCmd;
5 years ago
if (gbBufferMsgs != 1 && currlevel == player.plrlevel) {
auto spell = static_cast<spell_id>(p->wParam1);
auto spellType = static_cast<spell_type>(p->wParam2);
if (currlevel != 0 || spelldata[spell].sTownSpell) {
ClrPlrPath(player);
player.destAction = ACTION_SPELLWALL;
player.destParam1 = p->x;
player.destParam2 = p->y;
player.destParam3 = static_cast<Direction>(p->wParam3);
player.destParam4 = p->wParam4;
player._pSpell = spell;
player._pSplType = spellType;
player._pSplFrom = 0;
} else {
PlayerMessageFormat(fmt::format(_("{:s} has cast an illegal spell."), player._pName).c_str());
}
}
return sizeof(*p);
}
DWORD OnSpellTile(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdLocParam3 *)pCmd;
if (gbBufferMsgs != 1 && currlevel == player.plrlevel) {
auto spell = static_cast<spell_id>(p->wParam1);
auto spellType = static_cast<spell_type>(p->wParam2);
if (currlevel != 0 || spelldata[spell].sTownSpell) {
ClrPlrPath(player);
player.destAction = ACTION_SPELL;
player.destParam1 = p->x;
player.destParam2 = p->y;
player.destParam3 = static_cast<Direction>(p->wParam3);
player._pSpell = spell;
player._pSplType = spellType;
player._pSplFrom = 0;
} else {
PlayerMessageFormat(fmt::format(_("{:s} has cast an illegal spell."), player._pName).c_str());
}
}
return sizeof(*p);
}
DWORD OnTargetSpellTile(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdLocParam2 *)pCmd;
if (gbBufferMsgs != 1 && currlevel == player.plrlevel) {
auto spell = static_cast<spell_id>(p->wParam1);
if (currlevel != 0 || spelldata[spell].sTownSpell) {
ClrPlrPath(player);
player.destAction = ACTION_SPELL;
player.destParam1 = p->x;
player.destParam2 = p->y;
player.destParam3 = static_cast<Direction>(p->wParam2);
player._pSpell = spell;
player._pSplType = RSPLTYPE_INVALID;
player._pSplFrom = 2;
} else {
PlayerMessageFormat(fmt::format(_("{:s} has cast an illegal spell."), player._pName).c_str());
}
}
return sizeof(*p);
5 years ago
}
DWORD OnOperateObjectTile(const TCmd *pCmd, Player &player)
5 years ago
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position) && message.wParam1 < MAXOBJECTS) {
MakePlrPath(player, position, !Objects[message.wParam1]._oSolidFlag && !Objects[message.wParam1]._oDoorFlag);
player.destAction = ACTION_OPERATE;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnDisarm(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position) && message.wParam1 < MAXOBJECTS) {
MakePlrPath(player, position, !Objects[message.wParam1]._oSolidFlag && !Objects[message.wParam1]._oDoorFlag);
player.destAction = ACTION_DISARM;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnOperateObjectTelekinesis(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && message.wParam1 < MAXOBJECTS) {
player.destAction = ACTION_OPERATETK;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnAttackMonster(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && message.wParam1 < MAXMONSTERS) {
Point position = Monsters[message.wParam1].position.future;
if (player.position.tile.WalkingDistance(position) > 1)
MakePlrPath(player, position, false);
player.destAction = ACTION_ATTACKMON;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnAttackPlayer(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && message.wParam1 < MAX_PLRS) {
MakePlrPath(player, Players[message.wParam1].position.future, false);
player.destAction = ACTION_ATTACKPLR;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnRangedAttackMonster(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && message.wParam1 < MAXMONSTERS) {
ClrPlrPath(player);
player.destAction = ACTION_RATTACKMON;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnRangedAttackPlayer(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && message.wParam1 < MAX_PLRS) {
ClrPlrPath(player);
player.destAction = ACTION_RATTACKPLR;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnSpellMonster(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdParam4 *)pCmd;
if (gbBufferMsgs != 1 && currlevel == player.plrlevel) {
auto spell = static_cast<spell_id>(p->wParam2);
auto spellType = static_cast<spell_type>(p->wParam3);
if (currlevel != 0 || spelldata[spell].sTownSpell) {
ClrPlrPath(player);
player.destAction = ACTION_SPELLMON;
player.destParam1 = p->wParam1;
player.destParam2 = p->wParam4;
player._pSpell = spell;
player._pSplType = spellType;
player._pSplFrom = 0;
} else {
PlayerMessageFormat(fmt::format(_("{:s} has cast an illegal spell."), player._pName).c_str());
}
}
return sizeof(*p);
}
DWORD OnSpellPlayer(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdParam4 *)pCmd;
if (gbBufferMsgs != 1 && currlevel == player.plrlevel) {
auto spell = static_cast<spell_id>(p->wParam2);
auto spellType = static_cast<spell_type>(p->wParam3);
if (currlevel != 0 || spelldata[spell].sTownSpell) {
ClrPlrPath(player);
player.destAction = ACTION_SPELLPLR;
player.destParam1 = p->wParam1;
player.destParam2 = p->wParam4;
player._pSpell = spell;
player._pSplType = spellType;
player._pSplFrom = 0;
} else {
PlayerMessageFormat(fmt::format(_("{:s} has cast an illegal spell."), player._pName).c_str());
}
}
return sizeof(*p);
}
DWORD OnTargetSpellMonster(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdParam3 *)pCmd;
if (gbBufferMsgs != 1 && currlevel == player.plrlevel) {
auto spell = static_cast<spell_id>(p->wParam2);
if (currlevel != 0 || spelldata[spell].sTownSpell) {
ClrPlrPath(player);
player.destAction = ACTION_SPELLMON;
player.destParam1 = p->wParam1;
player.destParam2 = p->wParam3;
player._pSpell = spell;
player._pSplType = RSPLTYPE_INVALID;
player._pSplFrom = 2;
} else {
PlayerMessageFormat(fmt::format(_("{:s} has cast an illegal spell."), player._pName).c_str());
}
}
return sizeof(*p);
}
DWORD OnTargetSpellPlayer(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdParam3 *)pCmd;
if (gbBufferMsgs != 1 && currlevel == player.plrlevel) {
auto spell = static_cast<spell_id>(p->wParam2);
if (currlevel != 0 || spelldata[spell].sTownSpell) {
ClrPlrPath(player);
player.destAction = ACTION_SPELLPLR;
player.destParam1 = p->wParam1;
player.destParam2 = p->wParam3;
player._pSpell = spell;
player._pSplType = RSPLTYPE_INVALID;
player._pSplFrom = 2;
} else {
PlayerMessageFormat(fmt::format(_("{:s} has cast an illegal spell."), player._pName).c_str());
}
}
5 years ago
return sizeof(*p);
}
DWORD OnKnockback(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1 && currlevel == Players[pnum].plrlevel && message.wParam1 < MAXMONSTERS) {
M_GetKnockback(message.wParam1);
M_StartHit(message.wParam1, pnum, 0);
}
return sizeof(message);
}
DWORD OnResurrect(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (message.wParam1 < MAX_PLRS) {
DoResurrect(pnum, message.wParam1);
CheckUpdatePlayer(pnum);
}
return sizeof(message);
}
DWORD OnHealOther(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1 && currlevel == Players[pnum].plrlevel && message.wParam1 < MAX_PLRS)
DoHealOther(pnum, message.wParam1);
return sizeof(message);
}
DWORD OnTalkXY(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1 && currlevel == player.plrlevel && InDungeonBounds(position) && message.wParam1 < NUM_TOWNERS) {
MakePlrPath(player, position, false);
player.destAction = ACTION_TALK;
player.destParam1 = message.wParam1;
}
return sizeof(message);
}
DWORD OnNewLevel(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdParam2 *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p));
else if (pnum != MyPlayerId)
StartNewLvl(pnum, (interface_mode)p->wParam1, p->wParam2);
return sizeof(*p);
}
DWORD OnWarp(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (message.wParam1 < MAXPORTAL) {
StartWarpLvl(pnum, message.wParam1);
}
return sizeof(message);
}
DWORD OnMonstDeath(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (pnum != MyPlayerId && InDungeonBounds(position) && message.wParam1 < MAXMONSTERS) {
int playerLevel = Players[pnum].plrlevel;
if (currlevel == playerLevel)
M_SyncStartKill(message.wParam1, position, pnum);
delta_kill_monster(message.wParam1, position, playerLevel);
}
return sizeof(message);
}
DWORD OnKillGolem(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (pnum != MyPlayerId && InDungeonBounds(position) && message.wParam1 < NUMLEVELS) {
if (currlevel == message.wParam1)
M_SyncStartKill(pnum, position, pnum);
delta_kill_monster(pnum, position, message.wParam1);
}
return sizeof(message);
}
DWORD OnAwakeGolem(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdGolem *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p));
else if (currlevel != Players[pnum].plrlevel)
DeltaSyncGolem(p, pnum, p->_currlevel);
else if (pnum != MyPlayerId) {
// check if this player already has an active golem
bool addGolem = true;
for (int i = 0; i < ActiveMissileCount; i++) {
int mi = ActiveMissiles[i];
auto &missile = Missiles[mi];
if (missile._mitype == MIS_GOLEM && missile._misource == pnum) {
addGolem = false;
// CODEFIX: break, don't need to check the rest
}
}
if (addGolem)
AddMissile(Players[pnum].position.tile, { p->_mx, p->_my }, p->_mdir, MIS_GOLEM, TARGET_MONSTERS, pnum, 0, 1);
}
return sizeof(*p);
}
DWORD OnMonstDamage(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdMonDamage *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p)); // BUGFIX: change to sizeof(*p) or it still uses TCmdParam2 size for hellfire (fixed)
else if (pnum != MyPlayerId) {
int playerLevel = Players[pnum].plrlevel;
if (currlevel == playerLevel) {
auto &monster = Monsters[p->wMon];
monster.mWhoHit |= 1 << pnum;
if (monster._mhitpoints > 0) {
monster._mhitpoints -= p->dwDam;
if ((monster._mhitpoints >> 6) < 1)
monster._mhitpoints = 1 << 6;
delta_monster_hp(p->wMon, monster._mhitpoints, playerLevel);
}
}
}
return sizeof(*p);
}
DWORD OnPlayerDeath(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (pnum != MyPlayerId)
StartPlayerKill(pnum, message.wParam1);
else
CheckUpdatePlayer(pnum);
return sizeof(message);
}
DWORD OnPlayerDamage(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdDamage *)pCmd;
if (p->bPlr == MyPlayerId && currlevel != 0 && gbBufferMsgs != 1) {
if (currlevel == player.plrlevel && p->dwDam <= 192000 && Players[MyPlayerId]._pHitPoints >> 6 > 0) {
ApplyPlrDamage(MyPlayerId, 0, 0, p->dwDam, 1);
}
}
return sizeof(*p);
}
DWORD OnOpenDoor(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (message.wParam1 < MAXOBJECTS) {
int playerLevel = Players[pnum].plrlevel;
if (currlevel == playerLevel)
SyncOpObject(pnum, CMD_OPENDOOR, message.wParam1);
DeltaSyncObject(message.wParam1, CMD_OPENDOOR, playerLevel);
}
return sizeof(message);
}
DWORD OnCloseDoor(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (message.wParam1 < MAXOBJECTS) {
int playerLevel = Players[pnum].plrlevel;
if (currlevel == playerLevel)
SyncOpObject(pnum, CMD_CLOSEDOOR, message.wParam1);
DeltaSyncObject(message.wParam1, CMD_CLOSEDOOR, playerLevel);
}
return sizeof(message);
}
DWORD OnOperateObject(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
} else if (message.wParam1 < MAXOBJECTS) {
int playerLevel = Players[pnum].plrlevel;
if (currlevel == playerLevel)
SyncOpObject(pnum, CMD_OPERATEOBJ, message.wParam1);
DeltaSyncObject(message.wParam1, CMD_OPERATEOBJ, playerLevel);
}
return sizeof(message);
}
DWORD OnPlayerOperateObject(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdParam2 *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
int playerLevel = Players[pnum].plrlevel;
if (currlevel == playerLevel)
SyncOpObject(p->wParam1, CMD_PLROPOBJ, p->wParam2);
DeltaSyncObject(p->wParam2, CMD_PLROPOBJ, playerLevel);
}
return sizeof(*p);
}
DWORD OnBreakObject(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdParam2 *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
int playerLevel = Players[pnum].plrlevel;
if (currlevel == playerLevel)
SyncBreakObj(p->wParam1, p->wParam2);
DeltaSyncObject(p->wParam2, CMD_BREAKOBJ, playerLevel);
}
return sizeof(*p);
}
DWORD OnChangePlayerItems(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdChItem *)pCmd;
auto bodyLocation = static_cast<inv_body_loc>(p->bLoc);
auto &player = Players[pnum];
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p));
else if (pnum != MyPlayerId)
CheckInvSwap(player, p->bLoc, p->wIndx, p->wCI, p->dwSeed, p->bId != 0, p->dwBuff);
player.ReadySpellFromEquipment(bodyLocation);
return sizeof(*p);
}
DWORD OnDeletePlayerItems(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdDelItem *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p));
else if (pnum != MyPlayerId)
inv_update_rem_item(Players[pnum], p->bLoc);
return sizeof(*p);
}
DWORD OnPlayerLevel(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 < MAXCHARLEVEL && pnum != MyPlayerId)
Players[pnum]._pLevel = message.wParam1;
return sizeof(message);
}
DWORD OnDropItem(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdPItem *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, sizeof(*p));
else
DeltaPutItem(p, p->x, p->y, Players[pnum].plrlevel);
return sizeof(*p);
}
DWORD OnSendPlayerInfo(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdPlrInfoHdr *)pCmd;
if (gbBufferMsgs == 1)
SendPacket(pnum, p, p->wBytes + sizeof(*p));
else
recv_plrinfo(pnum, p, p->bCmd == CMD_ACK_PLRINFO);
return p->wBytes + sizeof(*p);
}
DWORD OnPlayerJoinLevel(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdLocParam1 *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs == 1) {
SendPacket(pnum, &message, sizeof(message));
return sizeof(message);
}
int playerLevel = message.wParam1;
if (playerLevel > (gbIsHellfire ? 24 : 16) || !InDungeonBounds(position)) {
return sizeof(message);
}
auto &player = Players[pnum];
player._pLvlChanging = false;
if (player._pName[0] != '\0' && !player.plractive) {
ResetPlayerGFX(player);
player.plractive = true;
gbActivePlayers++;
EventPlrMsg(fmt::format(_("Player '{:s}' (level {:d}) just joined the game"), player._pName, player._pLevel).c_str());
}
if (player.plractive && MyPlayerId != pnum) {
player.position.tile = position;
player.plrlevel = playerLevel;
ResetPlayerGFX(player);
if (currlevel == player.plrlevel) {
SyncInitPlr(pnum);
if ((player._pHitPoints >> 6) > 0) {
StartStand(pnum, Direction::South);
} else {
player._pgfxnum = 0;
player._pmode = PM_DEATH;
NewPlrAnim(player, player_graphic::Death, Direction::South, player._pDFrames, 1);
player.AnimInfo.CurrentFrame = player.AnimInfo.NumberOfFrames - 1;
dFlags[player.position.tile.x][player.position.tile.y] |= BFLAG_DEAD_PLAYER;
}
player._pvid = AddVision(player.position.tile, player._pLightRad, pnum == MyPlayerId);
}
}
return sizeof(message);
}
DWORD OnActivatePortal(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdLocParam3 *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
ActivatePortal(pnum, p->x, p->y, p->wParam1, static_cast<dungeon_type>(p->wParam2), p->wParam3 != 0);
if (pnum != MyPlayerId) {
if (currlevel == 0)
AddInTownPortal(pnum);
else if (currlevel == Players[pnum].plrlevel) {
bool addPortal = true;
for (int i = 0; i < ActiveMissileCount; i++) {
int mi = ActiveMissiles[i];
auto &missile = Missiles[mi];
if (missile._mitype == MIS_TOWN && missile._misource == pnum) {
addPortal = false;
// CODEFIX: break
}
}
if (addPortal)
AddWarpMissile(pnum, p->x, p->y);
} else {
RemovePortalMissile(pnum);
}
}
DeltaOpenPortal(pnum, p->x, p->y, p->wParam1, static_cast<dungeon_type>(p->wParam2), p->wParam3 != 0);
}
return sizeof(*p);
}
DWORD OnDeactivatePortal(const TCmd *pCmd, int pnum)
{
if (gbBufferMsgs == 1) {
SendPacket(pnum, pCmd, sizeof(*pCmd));
} else {
if (PortalOnLevel(pnum))
RemovePortalMissile(pnum);
DeactivatePortal(pnum);
delta_close_portal(pnum);
}
return sizeof(*pCmd);
}
DWORD OnRestartTown(const TCmd *pCmd, int pnum)
{
if (gbBufferMsgs == 1) {
SendPacket(pnum, pCmd, sizeof(*pCmd));
} else {
if (pnum == MyPlayerId) {
MyPlayerIsDead = false;
gamemenu_off();
}
RestartTownLvl(pnum);
}
return sizeof(*pCmd);
}
DWORD OnSetStrength(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 750 && pnum != MyPlayerId)
SetPlrStr(Players[pnum], message.wParam1);
return sizeof(message);
}
DWORD OnSetDexterity(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 750 && pnum != MyPlayerId)
SetPlrDex(Players[pnum], message.wParam1);
return sizeof(message);
}
DWORD OnSetMagic(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 750 && pnum != MyPlayerId)
SetPlrMag(Players[pnum], message.wParam1);
return sizeof(message);
}
DWORD OnSetVitality(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs == 1)
SendPacket(pnum, &message, sizeof(message));
else if (message.wParam1 <= 750 && pnum != MyPlayerId)
SetPlrVit(Players[pnum], message.wParam1);
return sizeof(message);
}
DWORD OnString(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdString *)pCmd;
int len = strlen(p->str);
if (gbBufferMsgs == 0)
SendPlrMsg(pnum, p->str);
return len + 2; // length of string + nul terminator + sizeof(p->bCmd)
}
DWORD OnSyncQuest(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdQuest *)pCmd;
if (gbBufferMsgs == 1) {
SendPacket(pnum, p, sizeof(*p));
} else {
if (pnum != MyPlayerId)
SetMultiQuest(p->q, p->qstate, p->qlog != 0, p->qvar1);
sgbDeltaChanged = true;
}
return sizeof(*p);
}
DWORD OnCheatExperience(const TCmd *pCmd, int pnum) // NOLINT(misc-unused-parameters)
{
#ifdef _DEBUG
if (gbBufferMsgs == 1)
SendPacket(pnum, pCmd, sizeof(*pCmd));
else if (Players[pnum]._pLevel < MAXCHARLEVEL - 1) {
Players[pnum]._pExperience = Players[pnum]._pNextExper;
if (sgOptions.Gameplay.bExperienceBar) {
force_redraw = 255;
}
NextPlrLevel(pnum);
}
#endif
return sizeof(*pCmd);
}
DWORD OnCheatSpellLevel(const TCmd *pCmd, int pnum) // NOLINT(misc-unused-parameters)
{
#ifdef _DEBUG
if (gbBufferMsgs == 1) {
SendPacket(pnum, pCmd, sizeof(*pCmd));
} else {
auto &player = Players[pnum];
player._pSplLvl[player._pRSpell]++;
}
#endif
return sizeof(*pCmd);
}
DWORD OnDebug(const TCmd *pCmd)
{
return sizeof(*pCmd);
}
DWORD OnNova(const TCmd *pCmd, int pnum)
{
const auto &message = *reinterpret_cast<const TCmdLoc *>(pCmd);
const Point position { message.x, message.y };
if (gbBufferMsgs != 1) {
auto &player = Players[pnum];
if (currlevel == player.plrlevel && pnum != MyPlayerId && InDungeonBounds(position)) {
ClrPlrPath(player);
player._pSpell = SPL_NOVA;
player._pSplType = RSPLTYPE_INVALID;
player._pSplFrom = 3;
player.destAction = ACTION_SPELL;
player.destParam1 = position.x;
player.destParam2 = position.y;
}
}
return sizeof(message);
}
DWORD OnSetShield(const TCmd *pCmd, Player &player)
{
if (gbBufferMsgs != 1)
player.pManaShield = true;
return sizeof(*pCmd);
}
DWORD OnRemoveShield(const TCmd *pCmd, Player &player)
{
if (gbBufferMsgs != 1)
player.pManaShield = false;
return sizeof(*pCmd);
}
DWORD OnSetReflect(const TCmd *pCmd, Player &player)
{
const auto &message = *reinterpret_cast<const TCmdParam1 *>(pCmd);
if (gbBufferMsgs != 1)
player.wReflections = message.wParam1;
return sizeof(message);
}
DWORD OnNakrul(const TCmd *pCmd)
{
if (gbBufferMsgs != 1) {
OperateNakrulLever();
IsUberRoomOpened = true;
Quests[Q_NAKRUL]._qactive = QUEST_DONE;
monster_some_crypt();
}
return sizeof(*pCmd);
}
DWORD OnOpenHive(const TCmd *pCmd, int pnum)
{
auto *p = (TCmdLocParam2 *)pCmd;
if (gbBufferMsgs != 1) {
AddMissile({ p->x, p->y }, { p->wParam1, p->wParam2 }, Direction::South, MIS_HIVEEXP2, TARGET_MONSTERS, pnum, 0, 0);
TownOpenHive();
InitTownTriggers();
}
return sizeof(*p);
}
DWORD OnOpenCrypt(const TCmd *pCmd)
{
if (gbBufferMsgs != 1) {
TownOpenGrave();
InitTownTriggers();
if (currlevel == 0)
PlaySFX(IS_SARC);
}
return sizeof(*pCmd);
}
} // namespace
void msg_send_drop_pkt(int pnum, int reason)
{
TFakeDropPlr cmd;
cmd.dwReason = reason;
cmd.bCmd = FAKE_CMD_DROPID;
cmd.bPlr = pnum;
SendPacket(pnum, &cmd, sizeof(cmd));
}
bool msg_wait_resync()
{
bool success;
GetNextPacket();
sgbDeltaChunks = 0;
sgnCurrMegaPlayer = -1;
sgbRecvCmd = CMD_DLEVEL_END;
gbBufferMsgs = 1;
sgdwOwnerWait = SDL_GetTicks();
success = UiProgressDialog(_("Waiting for game data..."), WaitForTurns);
gbBufferMsgs = 0;
if (!success) {
FreePackets();
return false;
}
if (gbGameDestroyed) {
DrawDlg("%s", _("The game ended"));
FreePackets();
return false;
}
if (sgbDeltaChunks != MAX_CHUNKS) {
DrawDlg("%s", _("Unable to get level data"));
FreePackets();
return false;
}
return true;
}
void run_delta_info()
{
if (!gbIsMultiplayer)
return;
gbBufferMsgs = 2;
PrePacket();
gbBufferMsgs = 0;
FreePackets();
}
void DeltaExportData(int pnum)
{
if (sgbDeltaChanged) {
for (int i = 0; i < NUMLEVELS; i++) {
std::unique_ptr<byte[]> dst { new byte[sizeof(DLevel) + 1] };
byte *dstEnd = &dst.get()[1];
dstEnd = DeltaExportItem(dstEnd, sgLevels[i].item);
dstEnd = DeltaExportObject(dstEnd, sgLevels[i].object);
dstEnd = DeltaExportMonster(dstEnd, sgLevels[i].monster);
int size = CompressData(dst.get(), dstEnd);
dthread_send_delta(pnum, static_cast<_cmd_id>(i + CMD_DLEVEL_0), std::move(dst), size);
}
std::unique_ptr<byte[]> dst { new byte[sizeof(DJunk) + 1] };
byte *dstEnd = &dst.get()[1];
dstEnd = DeltaExportJunk(dstEnd);
int size = CompressData(dst.get(), dstEnd);
dthread_send_delta(pnum, CMD_DLEVEL_JUNK, std::move(dst), size);
}
std::unique_ptr<byte[]> src { new byte[1] { static_cast<byte>(0) } };
dthread_send_delta(pnum, CMD_DLEVEL_END, std::move(src), 1);
}
void delta_init()
{
sgbDeltaChanged = false;
memset(&sgJunk, 0xFF, sizeof(sgJunk));
memset(sgLevels, 0xFF, sizeof(sgLevels));
memset(sgLocals, 0, sizeof(sgLocals));
deltaload = false;
}
void delta_kill_monster(int mi, Point position, BYTE bLevel)
{
if (!gbIsMultiplayer)
return;
sgbDeltaChanged = true;
DMonsterStr *pD = &sgLevels[bLevel].monster[mi];
pD->_mx = position.x;
pD->_my = position.y;
pD->_mdir = Monsters[mi]._mdir;
pD->_mhitpoints = 0;
}
void delta_monster_hp(int mi, int hp, BYTE bLevel)
{
if (!gbIsMultiplayer)
return;
sgbDeltaChanged = true;
DMonsterStr *pD = &sgLevels[bLevel].monster[mi];
if (pD->_mhitpoints > hp)
pD->_mhitpoints = hp;
}
void delta_sync_monster(const TSyncMonster &monsterSync, uint8_t level)
{
if (!gbIsMultiplayer)
return;
assert(level < NUMLEVELS);
sgbDeltaChanged = true;
DMonsterStr &monster = sgLevels[level].monster[monsterSync._mndx];
if (monster._mhitpoints == 0)
return;
monster._mx = monsterSync._mx;
monster._my = monsterSync._my;
monster._mactive = UINT8_MAX;
monster._menemy = monsterSync._menemy;
}
bool delta_portal_inited(int i)
{
return sgJunk.portal[i].x == 0xFF;
}
bool delta_quest_inited(int i)
{
return sgJunk.quests[i].qstate != QUEST_INVALID;
}
void DeltaAddItem(int ii)
{
if (!gbIsMultiplayer)
return;
TCmdPItem *pD = sgLevels[currlevel].item;
for (int i = 0; i < MAXITEMS; i++, pD++) {
if (pD->bCmd != CMD_INVALID
&& pD->wIndx == Items[ii].IDidx
&& pD->wCI == Items[ii]._iCreateInfo
&& pD->dwSeed == Items[ii]._iSeed
&& (pD->bCmd == CMD_WALKXY || pD->bCmd == CMD_STAND)) {
return;
}
}
pD = sgLevels[currlevel].item;
for (int i = 0; i < MAXITEMS; i++, pD++) {
if (pD->bCmd != CMD_INVALID)
continue;
sgbDeltaChanged = true;
pD->bCmd = CMD_STAND;
pD->x = Items[ii].position.x;
pD->y = Items[ii].position.y;
pD->wIndx = Items[ii].IDidx;
pD->wCI = Items[ii]._iCreateInfo;
pD->dwSeed = Items[ii]._iSeed;
pD->bId = Items[ii]._iIdentified ? 1 : 0;
pD->bDur = Items[ii]._iDurability;
pD->bMDur = Items[ii]._iMaxDur;
pD->bCh = Items[ii]._iCharges;
pD->bMCh = Items[ii]._iMaxCharges;
pD->wValue = Items[ii]._ivalue;
pD->wToHit = Items[ii]._iPLToHit;
pD->wMaxDam = Items[ii]._iMaxDam;
pD->bMinStr = Items[ii]._iMinStr;
pD->bMinMag = Items[ii]._iMinMag;
pD->bMinDex = Items[ii]._iMinDex;
pD->bAC = Items[ii]._iAC;
pD->dwBuff = Items[ii].dwBuff;
return;
}
}
void DeltaSaveLevel()
{
if (!gbIsMultiplayer)
return;
for (int i = 0; i < MAX_PLRS; i++) {
if (i != MyPlayerId)
ResetPlayerGFX(Players[i]);
}
Players[MyPlayerId]._pLvlVisited[currlevel] = true;
DeltaLeaveSync(currlevel);
}
namespace {
Point GetItemPosition(Point position)
{
if (CanPut(position))
return position;
for (int k = 1; k < 50; k++) {
for (int j = -k; j <= k; j++) {
int yy = position.y + j;
for (int l = -k; l <= k; l++) {
int xx = position.x + l;
if (CanPut({ xx, yy }))
return { xx, yy };
}
}
}
return position;
}
} //namespace
void DeltaLoadLevel()
{
if (!gbIsMultiplayer)
return;
deltaload = true;
if (currlevel != 0) {
for (int i = 0; i < ActiveMonsterCount; i++) {
if (sgLevels[currlevel].monster[i]._mx == 0xFF)
continue;
M_ClearSquares(i);
int x = sgLevels[currlevel].monster[i]._mx;
int y = sgLevels[currlevel].monster[i]._my;
auto &monster = Monsters[i];
monster.position.tile = { x, y };
monster.position.old = { x, y };
monster.position.future = { x, y };
if (sgLevels[currlevel].monster[i]._mhitpoints != -1)
monster._mhitpoints = sgLevels[currlevel].monster[i]._mhitpoints;
if (sgLevels[currlevel].monster[i]._mhitpoints == 0) {
M_ClearSquares(i);
if (monster._mAi != AI_DIABLO) {
if (monster._uniqtype == 0) {
assert(monster.MType != nullptr);
AddCorpse(monster.position.tile, monster.MType->mdeadval, monster._mdir);
} else {
AddCorpse(monster.position.tile, monster._udeadval, monster._mdir);
}
}
monster._mDelFlag = true;
M_UpdateLeader(i);
} else {
decode_enemy(monster, sgLevels[currlevel].monster[i]._menemy);
if (monster.position.tile != Point { 0, 0 } && monster.position.tile != GolemHoldingCell)
dMonster[monster.position.tile.x][monster.position.tile.y] = i + 1;
if (i < MAX_PLRS) {
GolumAi(i);
monster._mFlags |= (MFLAG_TARGETS_MONSTER | MFLAG_GOLEM);
} else {
M_StartStand(monster, monster._mdir);
}
monster._msquelch = sgLevels[currlevel].monster[i]._mactive;
}
}
memcpy(AutomapView, &sgLocals[currlevel], sizeof(AutomapView));
}
for (int i = 0; i < MAXITEMS; i++) {
if (sgLevels[currlevel].item[i].bCmd == CMD_INVALID)
continue;
if (sgLevels[currlevel].item[i].bCmd == CMD_WALKXY) {
int ii = FindGetItem(
sgLevels[currlevel].item[i].wIndx,
sgLevels[currlevel].item[i].wCI,
sgLevels[currlevel].item[i].dwSeed);
if (ii != -1) {
if (dItem[Items[ii].position.x][Items[ii].position.y] == ii + 1)
dItem[Items[ii].position.x][Items[ii].position.y] = 0;
DeleteItem(ii, i);
}
}
if (sgLevels[currlevel].item[i].bCmd == CMD_ACK_PLRINFO) {
int ii = AllocateItem();
auto &item = Items[ii];
if (sgLevels[currlevel].item[i].wIndx == IDI_EAR) {
RecreateEar(
item,
sgLevels[currlevel].item[i].wCI,
sgLevels[currlevel].item[i].dwSeed,
sgLevels[currlevel].item[i].bId,
sgLevels[currlevel].item[i].bDur,
sgLevels[currlevel].item[i].bMDur,
sgLevels[currlevel].item[i].bCh,
sgLevels[currlevel].item[i].bMCh,
sgLevels[currlevel].item[i].wValue,
sgLevels[currlevel].item[i].dwBuff);
} else {
RecreateItem(
item,
sgLevels[currlevel].item[i].wIndx,
sgLevels[currlevel].item[i].wCI,
sgLevels[currlevel].item[i].dwSeed,
sgLevels[currlevel].item[i].wValue,
(sgLevels[currlevel].item[i].dwBuff & CF_HELLFIRE) != 0);
if (sgLevels[currlevel].item[i].bId != 0)
item._iIdentified = true;
item._iDurability = sgLevels[currlevel].item[i].bDur;
item._iMaxDur = sgLevels[currlevel].item[i].bMDur;
item._iCharges = sgLevels[currlevel].item[i].bCh;
item._iMaxCharges = sgLevels[currlevel].item[i].bMCh;
item._iPLToHit = sgLevels[currlevel].item[i].wToHit;
item._iMaxDam = sgLevels[currlevel].item[i].wMaxDam;
item._iMinStr = sgLevels[currlevel].item[i].bMinStr;
item._iMinMag = sgLevels[currlevel].item[i].bMinMag;
item._iMinDex = sgLevels[currlevel].item[i].bMinDex;
item._iAC = sgLevels[currlevel].item[i].bAC;
item.dwBuff = sgLevels[currlevel].item[i].dwBuff;
}
int x = sgLevels[currlevel].item[i].x;
int y = sgLevels[currlevel].item[i].y;
item.position = GetItemPosition({ x, y });
dItem[item.position.x][item.position.y] = ii + 1;
RespawnItem(&Items[ii], false);
}
}
if (currlevel != 0) {
for (int i = 0; i < MAXOBJECTS; i++) {
switch (sgLevels[currlevel].object[i].bCmd) {
case CMD_OPENDOOR:
case CMD_CLOSEDOOR:
case CMD_OPERATEOBJ:
case CMD_PLROPOBJ:
SyncOpObject(-1, sgLevels[currlevel].object[i].bCmd, i);
break;
case CMD_BREAKOBJ:
SyncBreakObj(-1, i);
break;
default:
break;
}
}
for (int i = 0; i < ActiveObjectCount; i++) {
int ot = Objects[ActiveObjects[i]]._otype;
if (ot == OBJ_TRAPL || ot == OBJ_TRAPR)
Obj_Trap(ActiveObjects[i]);
}
}
deltaload = false;
}
void NetSendCmd(bool bHiPri, _cmd_id bCmd)
{
TCmd cmd;
cmd.bCmd = bCmd;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdGolem(BYTE mx, BYTE my, Direction dir, BYTE menemy, int hp, BYTE cl)
{
TCmdGolem cmd;
cmd.bCmd = CMD_AWAKEGOLEM;
cmd._mx = mx;
cmd._my = my;
cmd._mdir = dir;
cmd._menemy = menemy;
cmd._mhitpoints = hp;
cmd._currlevel = cl;
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdLoc(int playerId, bool bHiPri, _cmd_id bCmd, Point position)
{
TCmdLoc cmd;
cmd.bCmd = bCmd;
cmd.x = position.x;
cmd.y = position.y;
if (bHiPri)
NetSendHiPri(playerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(playerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdLocParam1(bool bHiPri, _cmd_id bCmd, Point position, uint16_t wParam1)
{
TCmdLocParam1 cmd;
cmd.bCmd = bCmd;
cmd.x = position.x;
cmd.y = position.y;
cmd.wParam1 = wParam1;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdLocParam2(bool bHiPri, _cmd_id bCmd, Point position, uint16_t wParam1, uint16_t wParam2)
{
TCmdLocParam2 cmd;
cmd.bCmd = bCmd;
cmd.x = position.x;
cmd.y = position.y;
cmd.wParam1 = wParam1;
cmd.wParam2 = wParam2;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdLocParam3(bool bHiPri, _cmd_id bCmd, Point position, uint16_t wParam1, uint16_t wParam2, uint16_t wParam3)
{
TCmdLocParam3 cmd;
cmd.bCmd = bCmd;
cmd.x = position.x;
cmd.y = position.y;
cmd.wParam1 = wParam1;
cmd.wParam2 = wParam2;
cmd.wParam3 = wParam3;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdLocParam4(bool bHiPri, _cmd_id bCmd, Point position, uint16_t wParam1, uint16_t wParam2, uint16_t wParam3, uint16_t wParam4)
{
TCmdLocParam4 cmd;
cmd.bCmd = bCmd;
cmd.x = position.x;
cmd.y = position.y;
cmd.wParam1 = wParam1;
cmd.wParam2 = wParam2;
cmd.wParam3 = wParam3;
cmd.wParam4 = wParam4;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdParam1(bool bHiPri, _cmd_id bCmd, uint16_t wParam1)
{
TCmdParam1 cmd;
cmd.bCmd = bCmd;
cmd.wParam1 = wParam1;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdParam2(bool bHiPri, _cmd_id bCmd, uint16_t wParam1, uint16_t wParam2)
{
TCmdParam2 cmd;
cmd.bCmd = bCmd;
cmd.wParam1 = wParam1;
cmd.wParam2 = wParam2;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdParam3(bool bHiPri, _cmd_id bCmd, uint16_t wParam1, uint16_t wParam2, uint16_t wParam3)
{
TCmdParam3 cmd;
cmd.bCmd = bCmd;
cmd.wParam1 = wParam1;
cmd.wParam2 = wParam2;
cmd.wParam3 = wParam3;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdParam4(bool bHiPri, _cmd_id bCmd, uint16_t wParam1, uint16_t wParam2, uint16_t wParam3, uint16_t wParam4)
{
TCmdParam4 cmd;
cmd.bCmd = bCmd;
cmd.wParam1 = wParam1;
cmd.wParam2 = wParam2;
cmd.wParam3 = wParam3;
cmd.wParam4 = wParam4;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdQuest(bool bHiPri, const Quest &quest)
{
TCmdQuest cmd;
cmd.bCmd = CMD_SYNCQUEST;
cmd.q = quest._qidx,
cmd.qstate = quest._qactive;
cmd.qlog = quest._qlog ? 1 : 0;
cmd.qvar1 = quest._qvar1;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdGItem(bool bHiPri, _cmd_id bCmd, BYTE mast, BYTE pnum, BYTE ii)
{
TCmdGItem cmd;
cmd.bCmd = bCmd;
cmd.bPnum = pnum;
cmd.bMaster = mast;
cmd.bLevel = currlevel;
cmd.bCursitem = ii;
cmd.dwTime = 0;
cmd.x = Items[ii].position.x;
cmd.y = Items[ii].position.y;
cmd.wIndx = Items[ii].IDidx;
if (Items[ii].IDidx == IDI_EAR) {
cmd.wCI = Items[ii]._iName[8] | (Items[ii]._iName[7] << 8);
cmd.dwSeed = Items[ii]._iName[12] | ((Items[ii]._iName[11] | ((Items[ii]._iName[10] | (Items[ii]._iName[9] << 8)) << 8)) << 8);
cmd.bId = Items[ii]._iName[13];
cmd.bDur = Items[ii]._iName[14];
cmd.bMDur = Items[ii]._iName[15];
cmd.bCh = Items[ii]._iName[16];
cmd.bMCh = Items[ii]._iName[17];
cmd.wValue = Items[ii]._ivalue | (Items[ii]._iName[18] << 8) | ((Items[ii]._iCurs - ICURS_EAR_SORCERER) << 6);
cmd.dwBuff = Items[ii]._iName[22] | ((Items[ii]._iName[21] | ((Items[ii]._iName[20] | (Items[ii]._iName[19] << 8)) << 8)) << 8);
} else {
cmd.wCI = Items[ii]._iCreateInfo;
cmd.dwSeed = Items[ii]._iSeed;
cmd.bId = Items[ii]._iIdentified ? 1 : 0;
cmd.bDur = Items[ii]._iDurability;
cmd.bMDur = Items[ii]._iMaxDur;
cmd.bCh = Items[ii]._iCharges;
cmd.bMCh = Items[ii]._iMaxCharges;
cmd.wValue = Items[ii]._ivalue;
cmd.wToHit = Items[ii]._iPLToHit;
cmd.wMaxDam = Items[ii]._iMaxDam;
cmd.bMinStr = Items[ii]._iMinStr;
cmd.bMinMag = Items[ii]._iMinMag;
cmd.bMinDex = Items[ii]._iMinDex;
cmd.bAC = Items[ii]._iAC;
cmd.dwBuff = Items[ii].dwBuff;
}
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdPItem(bool bHiPri, _cmd_id bCmd, Point position)
{
TCmdPItem cmd;
cmd.bCmd = bCmd;
cmd.x = position.x;
cmd.y = position.y;
auto &myPlayer = Players[MyPlayerId];
cmd.wIndx = myPlayer.HoldItem.IDidx;
if (myPlayer.HoldItem.IDidx == IDI_EAR) {
cmd.wCI = myPlayer.HoldItem._iName[8] | (myPlayer.HoldItem._iName[7] << 8);
cmd.dwSeed = myPlayer.HoldItem._iName[12] | ((myPlayer.HoldItem._iName[11] | ((myPlayer.HoldItem._iName[10] | (myPlayer.HoldItem._iName[9] << 8)) << 8)) << 8);
cmd.bId = myPlayer.HoldItem._iName[13];
cmd.bDur = myPlayer.HoldItem._iName[14];
cmd.bMDur = myPlayer.HoldItem._iName[15];
cmd.bCh = myPlayer.HoldItem._iName[16];
cmd.bMCh = myPlayer.HoldItem._iName[17];
cmd.wValue = myPlayer.HoldItem._ivalue | (myPlayer.HoldItem._iName[18] << 8) | ((myPlayer.HoldItem._iCurs - ICURS_EAR_SORCERER) << 6);
cmd.dwBuff = myPlayer.HoldItem._iName[22] | ((myPlayer.HoldItem._iName[21] | ((myPlayer.HoldItem._iName[20] | (myPlayer.HoldItem._iName[19] << 8)) << 8)) << 8);
} else {
cmd.wCI = myPlayer.HoldItem._iCreateInfo;
cmd.dwSeed = myPlayer.HoldItem._iSeed;
cmd.bId = myPlayer.HoldItem._iIdentified ? 1 : 0;
cmd.bDur = myPlayer.HoldItem._iDurability;
cmd.bMDur = myPlayer.HoldItem._iMaxDur;
cmd.bCh = myPlayer.HoldItem._iCharges;
cmd.bMCh = myPlayer.HoldItem._iMaxCharges;
cmd.wValue = myPlayer.HoldItem._ivalue;
cmd.wToHit = myPlayer.HoldItem._iPLToHit;
cmd.wMaxDam = myPlayer.HoldItem._iMaxDam;
cmd.bMinStr = myPlayer.HoldItem._iMinStr;
cmd.bMinMag = myPlayer.HoldItem._iMinMag;
cmd.bMinDex = myPlayer.HoldItem._iMinDex;
cmd.bAC = myPlayer.HoldItem._iAC;
cmd.dwBuff = myPlayer.HoldItem.dwBuff;
}
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdChItem(bool bHiPri, BYTE bLoc)
{
TCmdChItem cmd;
auto &myPlayer = Players[MyPlayerId];
cmd.bCmd = CMD_CHANGEPLRITEMS;
cmd.bLoc = bLoc;
cmd.wIndx = myPlayer.HoldItem.IDidx;
cmd.wCI = myPlayer.HoldItem._iCreateInfo;
cmd.dwSeed = myPlayer.HoldItem._iSeed;
cmd.bId = myPlayer.HoldItem._iIdentified ? 1 : 0;
cmd.dwBuff = myPlayer.HoldItem.dwBuff;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdDelItem(bool bHiPri, BYTE bLoc)
{
TCmdDelItem cmd;
cmd.bLoc = bLoc;
cmd.bCmd = CMD_DELPLRITEMS;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdDItem(bool bHiPri, int ii)
{
TCmdPItem cmd;
cmd.bCmd = CMD_DROPITEM;
cmd.x = Items[ii].position.x;
cmd.y = Items[ii].position.y;
cmd.wIndx = Items[ii].IDidx;
if (Items[ii].IDidx == IDI_EAR) {
cmd.wCI = Items[ii]._iName[8] | (Items[ii]._iName[7] << 8);
cmd.dwSeed = Items[ii]._iName[12] | ((Items[ii]._iName[11] | ((Items[ii]._iName[10] | (Items[ii]._iName[9] << 8)) << 8)) << 8);
cmd.bId = Items[ii]._iName[13];
cmd.bDur = Items[ii]._iName[14];
cmd.bMDur = Items[ii]._iName[15];
cmd.bCh = Items[ii]._iName[16];
cmd.bMCh = Items[ii]._iName[17];
cmd.wValue = Items[ii]._ivalue | (Items[ii]._iName[18] << 8) | ((Items[ii]._iCurs - ICURS_EAR_SORCERER) << 6);
cmd.dwBuff = Items[ii]._iName[22] | ((Items[ii]._iName[21] | ((Items[ii]._iName[20] | (Items[ii]._iName[19] << 8)) << 8)) << 8);
} else {
cmd.wCI = Items[ii]._iCreateInfo;
cmd.dwSeed = Items[ii]._iSeed;
cmd.bId = Items[ii]._iIdentified ? 1 : 0;
cmd.bDur = Items[ii]._iDurability;
cmd.bMDur = Items[ii]._iMaxDur;
cmd.bCh = Items[ii]._iCharges;
cmd.bMCh = Items[ii]._iMaxCharges;
cmd.wValue = Items[ii]._ivalue;
cmd.wToHit = Items[ii]._iPLToHit;
cmd.wMaxDam = Items[ii]._iMaxDam;
cmd.bMinStr = Items[ii]._iMinStr;
cmd.bMinMag = Items[ii]._iMinMag;
cmd.bMinDex = Items[ii]._iMinDex;
cmd.bAC = Items[ii]._iAC;
cmd.dwBuff = Items[ii].dwBuff;
}
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdDamage(bool bHiPri, uint8_t bPlr, uint32_t dwDam)
{
TCmdDamage cmd;
cmd.bCmd = CMD_PLRDAMAGE;
cmd.bPlr = bPlr;
cmd.dwDam = dwDam;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdMonDmg(bool bHiPri, uint16_t wMon, uint32_t dwDam)
{
TCmdMonDamage cmd;
cmd.bCmd = CMD_MONSTDAMAGE;
cmd.wMon = wMon;
cmd.dwDam = dwDam;
if (bHiPri)
NetSendHiPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
else
NetSendLoPri(MyPlayerId, (byte *)&cmd, sizeof(cmd));
}
void NetSendCmdString(uint32_t pmask, const char *pszStr)
{
TCmdString cmd;
cmd.bCmd = CMD_STRING;
strcpy(cmd.str, pszStr);
multi_send_msg_packet(pmask, (byte *)&cmd, strlen(pszStr) + 2);
}
void delta_close_portal(int pnum)
{
memset(&sgJunk.portal[pnum], 0xFF, sizeof(sgJunk.portal[pnum]));
sgbDeltaChanged = true;
}
uint32_t ParseCmd(int pnum, const TCmd *pCmd)
5 years ago
{
sbLastCmd = pCmd->bCmd;
if (sgwPackPlrOffsetTbl[pnum] != 0 && sbLastCmd != CMD_ACK_PLRINFO && sbLastCmd != CMD_SEND_PLRINFO)
return 0;
auto &player = Players[pnum];
5 years ago
switch (pCmd->bCmd) {
case CMD_SYNCDATA:
return OnSyncData(pCmd, pnum);
5 years ago
case CMD_WALKXY:
return OnWalk(pCmd, player);
5 years ago
case CMD_ADDSTR:
return OnAddStrength(pCmd, pnum);
5 years ago
case CMD_ADDDEX:
return OnAddDexterity(pCmd, pnum);
5 years ago
case CMD_ADDMAG:
return OnAddMagic(pCmd, pnum);
5 years ago
case CMD_ADDVIT:
return OnAddVitality(pCmd, pnum);
5 years ago
case CMD_GOTOGETITEM:
return OnGotoGetItem(pCmd, player);
5 years ago
case CMD_REQUESTGITEM:
return OnRequestGetItem(pCmd, player);
5 years ago
case CMD_GETITEM:
return OnGetItem(pCmd, pnum);
5 years ago
case CMD_GOTOAGETITEM:
return OnGotoAutoGetItem(pCmd, player);
5 years ago
case CMD_REQUESTAGITEM:
return OnRequestAutoGetItem(pCmd, player);
5 years ago
case CMD_AGETITEM:
return OnAutoGetItem(pCmd, pnum);
5 years ago
case CMD_ITEMEXTRA:
return OnItemExtra(pCmd, pnum);
5 years ago
case CMD_PUTITEM:
return OnPutItem(pCmd, pnum);
5 years ago
case CMD_SYNCPUTITEM:
return OnSyncPutItem(pCmd, pnum);
5 years ago
case CMD_RESPAWNITEM:
return OnRespawnItem(pCmd, pnum);
5 years ago
case CMD_ATTACKXY:
return OnAttackTile(pCmd, player);
5 years ago
case CMD_SATTACKXY:
return OnStandingAttackTile(pCmd, player);
5 years ago
case CMD_RATTACKXY:
return OnRangedAttackTile(pCmd, player);
5 years ago
case CMD_SPELLXYD:
return OnSpellWall(pCmd, player);
5 years ago
case CMD_SPELLXY:
return OnSpellTile(pCmd, player);
5 years ago
case CMD_TSPELLXY:
return OnTargetSpellTile(pCmd, player);
5 years ago
case CMD_OPOBJXY:
return OnOperateObjectTile(pCmd, player);
5 years ago
case CMD_DISARMXY:
return OnDisarm(pCmd, player);
5 years ago
case CMD_OPOBJT:
return OnOperateObjectTelekinesis(pCmd, player);
5 years ago
case CMD_ATTACKID:
return OnAttackMonster(pCmd, player);
5 years ago
case CMD_ATTACKPID:
return OnAttackPlayer(pCmd, player);
5 years ago
case CMD_RATTACKID:
return OnRangedAttackMonster(pCmd, player);
5 years ago
case CMD_RATTACKPID:
return OnRangedAttackPlayer(pCmd, player);
5 years ago
case CMD_SPELLID:
return OnSpellMonster(pCmd, player);
5 years ago
case CMD_SPELLPID:
return OnSpellPlayer(pCmd, player);
5 years ago
case CMD_TSPELLID:
return OnTargetSpellMonster(pCmd, player);
5 years ago
case CMD_TSPELLPID:
return OnTargetSpellPlayer(pCmd, player);
5 years ago
case CMD_KNOCKBACK:
return OnKnockback(pCmd, pnum);
5 years ago
case CMD_RESURRECT:
return OnResurrect(pCmd, pnum);
5 years ago
case CMD_HEALOTHER:
return OnHealOther(pCmd, pnum);
5 years ago
case CMD_TALKXY:
return OnTalkXY(pCmd, player);
5 years ago
case CMD_DEBUG:
return OnDebug(pCmd);
5 years ago
case CMD_NEWLVL:
return OnNewLevel(pCmd, pnum);
5 years ago
case CMD_WARP:
return OnWarp(pCmd, pnum);
5 years ago
case CMD_MONSTDEATH:
return OnMonstDeath(pCmd, pnum);
5 years ago
case CMD_KILLGOLEM:
return OnKillGolem(pCmd, pnum);
5 years ago
case CMD_AWAKEGOLEM:
return OnAwakeGolem(pCmd, pnum);
5 years ago
case CMD_MONSTDAMAGE:
return OnMonstDamage(pCmd, pnum);
5 years ago
case CMD_PLRDEAD:
return OnPlayerDeath(pCmd, pnum);
5 years ago
case CMD_PLRDAMAGE:
return OnPlayerDamage(pCmd, player);
5 years ago
case CMD_OPENDOOR:
return OnOpenDoor(pCmd, pnum);
5 years ago
case CMD_CLOSEDOOR:
return OnCloseDoor(pCmd, pnum);
5 years ago
case CMD_OPERATEOBJ:
return OnOperateObject(pCmd, pnum);
5 years ago
case CMD_PLROPOBJ:
return OnPlayerOperateObject(pCmd, pnum);
5 years ago
case CMD_BREAKOBJ:
return OnBreakObject(pCmd, pnum);
5 years ago
case CMD_CHANGEPLRITEMS:
return OnChangePlayerItems(pCmd, pnum);
5 years ago
case CMD_DELPLRITEMS:
return OnDeletePlayerItems(pCmd, pnum);
5 years ago
case CMD_PLRLEVEL:
return OnPlayerLevel(pCmd, pnum);
5 years ago
case CMD_DROPITEM:
return OnDropItem(pCmd, pnum);
5 years ago
case CMD_ACK_PLRINFO:
case CMD_SEND_PLRINFO:
return OnSendPlayerInfo(pCmd, pnum);
5 years ago
case CMD_PLAYER_JOINLEVEL:
return OnPlayerJoinLevel(pCmd, pnum);
5 years ago
case CMD_ACTIVATEPORTAL:
return OnActivatePortal(pCmd, pnum);
5 years ago
case CMD_DEACTIVATEPORTAL:
return OnDeactivatePortal(pCmd, pnum);
5 years ago
case CMD_RETOWN:
return OnRestartTown(pCmd, pnum);
5 years ago
case CMD_SETSTR:
return OnSetStrength(pCmd, pnum);
5 years ago
case CMD_SETMAG:
return OnSetMagic(pCmd, pnum);
5 years ago
case CMD_SETDEX:
return OnSetDexterity(pCmd, pnum);
5 years ago
case CMD_SETVIT:
return OnSetVitality(pCmd, pnum);
5 years ago
case CMD_STRING:
return OnString(pCmd, pnum);
5 years ago
case CMD_SYNCQUEST:
return OnSyncQuest(pCmd, pnum);
5 years ago
case CMD_CHEAT_EXPERIENCE:
return OnCheatExperience(pCmd, pnum);
5 years ago
case CMD_CHEAT_SPELL_LEVEL:
return OnCheatSpellLevel(pCmd, pnum);
5 years ago
case CMD_NOVA:
return OnNova(pCmd, pnum);
5 years ago
case CMD_SETSHIELD:
return OnSetShield(pCmd, player);
5 years ago
case CMD_REMSHIELD:
return OnRemoveShield(pCmd, player);
case CMD_SETREFLECT:
return OnSetReflect(pCmd, player);
5 years ago
case CMD_NAKRUL:
return OnNakrul(pCmd);
5 years ago
case CMD_OPENHIVE:
return OnOpenHive(pCmd, pnum);
5 years ago
case CMD_OPENCRYPT:
return OnOpenCrypt(pCmd);
default:
break;
5 years ago
}
if (pCmd->bCmd < CMD_DLEVEL_0 || pCmd->bCmd > CMD_DLEVEL_END) {
SNetDropPlayer(pnum, LEAVE_DROP);
return 0;
}
return OnLevelData(pnum, pCmd);
5 years ago
}
} // namespace devilution