mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-14 22:51:11 +00:00
b196e67f40
* - Change gBusyCount limit to a higher value since xsector limit raised. gModernMap: - The return of the event causer (kChannelEventCauser, TXID 50). - Fix flags for kModernPropertiesChanger type. - Add new modern type kModernVelocityChanger. - New features for kMarkerWarpDest type. - New features for kModernSectorFXChanger type. - Fix sector condition 58. - Fix sprite hitscan conditions (35 - 38). - Add new command (kCmdEventKillFull). - Misc changes. # Conflicts: # source/games/blood/src/actor.cpp # source/games/blood/src/nnexts.cpp
810 lines
19 KiB
C++
810 lines
19 KiB
C++
//-------------------------------------------------------------------------
|
||
/*
|
||
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
||
Copyright (C) 2019 Nuke.YKT
|
||
Copyright (C) 2020 Christoph Oelckers
|
||
|
||
This file is part of Raze.
|
||
|
||
Raze is free software; you can redistribute it and/or
|
||
modify it under the terms of the GNU General Public License version 2
|
||
as published by the Free Software Foundation.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
||
See the GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program; if not, write to the Free Software
|
||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
*/
|
||
//-------------------------------------------------------------------------
|
||
|
||
#include "ns.h"
|
||
#include <set>
|
||
#include "build.h"
|
||
#include "printf.h"
|
||
#include "blood.h"
|
||
#include "secrets.h"
|
||
#include "serializer.h"
|
||
#include "bloodactor.h"
|
||
|
||
BEGIN_BLD_NS
|
||
|
||
|
||
const int kMaxID = 1024;
|
||
EventObject rxBucket[kChannelMax];
|
||
unsigned short bucketHead[kMaxID + 1];
|
||
static int bucketCount;
|
||
static std::multiset<EVENT> queue;
|
||
|
||
|
||
FString EventObject::description() const
|
||
{
|
||
if (isActor()) return FStringf("actor %d", ActorP->GetIndex()); // do not add a read barrier here!
|
||
if (isSector()) return FStringf("sector %d", int(index >> 8));
|
||
if (isWall()) return FStringf("wall %d", int(index >> 8));
|
||
return FString("invalid object");
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
static int GetBucketChannel(EventObject* pBucket)
|
||
{
|
||
if (pBucket->isSector())
|
||
{
|
||
auto pSector = pBucket->sector();
|
||
assert(pSector->hasX());
|
||
return pSector->xs().rxID;
|
||
}
|
||
if (pBucket->isWall())
|
||
{
|
||
auto pWall = pBucket->wall();
|
||
assert(pWall->hasX());
|
||
return pWall->xw().rxID;
|
||
}
|
||
if (pBucket->isActor())
|
||
{
|
||
auto pActor = pBucket->actor();
|
||
return pActor ? pActor->xspr.rxID : 0;
|
||
}
|
||
|
||
Printf(PRINT_HIGH, "Unexpected rxBucket %s", pBucket->description().GetChars());
|
||
return 0;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
static int CompareChannels(EventObject* ref1, EventObject* ref2)
|
||
{
|
||
return GetBucketChannel(ref1) - GetBucketChannel(ref2);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
static EventObject* SortGetMiddle(EventObject* a1, EventObject* a2, EventObject* a3)
|
||
{
|
||
if (CompareChannels(a1, a2) > 0)
|
||
{
|
||
if (CompareChannels(a1, a3) > 0)
|
||
{
|
||
if (CompareChannels(a2, a3) > 0)
|
||
return a2;
|
||
return a3;
|
||
}
|
||
return a1;
|
||
}
|
||
else
|
||
{
|
||
if (CompareChannels(a1, a3) < 0)
|
||
{
|
||
if (CompareChannels(a2, a3) > 0)
|
||
return a3;
|
||
return a2;
|
||
}
|
||
return a1;
|
||
}
|
||
}
|
||
|
||
static void SortSwap(EventObject* a, EventObject* b)
|
||
{
|
||
EventObject t = *a;
|
||
*a = *b;
|
||
*b = t;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
static void SortRXBucket(int nCount)
|
||
{
|
||
EventObject* v144[32];
|
||
int vc4[32];
|
||
int v14 = 0;
|
||
EventObject* pArray = rxBucket;
|
||
while (true)
|
||
{
|
||
while (nCount > 1)
|
||
{
|
||
if (nCount < 16)
|
||
{
|
||
for (int nDist = 3; nDist > 0; nDist -= 2)
|
||
{
|
||
for (EventObject* pI = pArray + nDist; pI < pArray + nCount; pI += nDist)
|
||
{
|
||
for (EventObject* pJ = pI; pJ > pArray && CompareChannels(pJ - nDist, pJ) > 0; pJ -= nDist)
|
||
{
|
||
SortSwap(pJ, pJ - nDist);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
EventObject* middle = pArray + nCount / 2;
|
||
if (nCount > 29)
|
||
{
|
||
EventObject* first = pArray;
|
||
EventObject* last = pArray + nCount - 1;
|
||
if (nCount > 42)
|
||
{
|
||
int eighth = nCount / 8;
|
||
first = SortGetMiddle(first, first + eighth, first + eighth * 2);
|
||
middle = SortGetMiddle(middle - eighth, middle, middle + eighth);
|
||
last = SortGetMiddle(last - eighth * 2, last - eighth, last);
|
||
}
|
||
middle = SortGetMiddle(first, middle, last);
|
||
}
|
||
EventObject pivot = *middle;
|
||
EventObject* first = pArray;
|
||
EventObject* last = pArray + nCount - 1;
|
||
EventObject* vbx = first;
|
||
EventObject* v4 = last;
|
||
while (true)
|
||
{
|
||
while (vbx <= v4)
|
||
{
|
||
int nCmp = CompareChannels(vbx, &pivot);
|
||
if (nCmp > 0)
|
||
break;
|
||
if (nCmp == 0)
|
||
{
|
||
SortSwap(vbx, first);
|
||
first++;
|
||
}
|
||
vbx++;
|
||
}
|
||
while (vbx <= v4)
|
||
{
|
||
int nCmp = CompareChannels(v4, &pivot);
|
||
if (nCmp < 0)
|
||
break;
|
||
if (nCmp == 0)
|
||
{
|
||
SortSwap(v4, last);
|
||
last--;
|
||
}
|
||
v4--;
|
||
}
|
||
if (vbx > v4)
|
||
break;
|
||
SortSwap(vbx, v4);
|
||
v4--;
|
||
vbx++;
|
||
}
|
||
EventObject* v2c = pArray + nCount;
|
||
int vt = int(min(vbx - first, first - pArray));
|
||
for (int i = 0; i < vt; i++)
|
||
{
|
||
SortSwap(&vbx[i - vt], &pArray[i]);
|
||
}
|
||
vt = int(min(last - v4, v2c - last - 1));
|
||
for (int i = 0; i < vt; i++)
|
||
{
|
||
SortSwap(&v2c[i - vt], &vbx[i]);
|
||
}
|
||
int vvsi = int(last - v4);
|
||
int vvdi = int(vbx - first);
|
||
if (vvsi >= vvdi)
|
||
{
|
||
vc4[v14] = vvsi;
|
||
v144[v14] = v2c - vvsi;
|
||
nCount = vvdi;
|
||
v14++;
|
||
}
|
||
else
|
||
{
|
||
vc4[v14] = vvdi;
|
||
v144[v14] = pArray;
|
||
nCount = vvsi;
|
||
pArray = v2c - vvsi;
|
||
v14++;
|
||
}
|
||
}
|
||
if (v14 == 0)
|
||
return;
|
||
v14--;
|
||
pArray = v144[v14];
|
||
nCount = vc4[v14];
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
static void createBucketHeads()
|
||
{
|
||
int i, j;
|
||
// create the list of header indices
|
||
for (i = 0, j = 0; i < kMaxID; i++)
|
||
{
|
||
bucketHead[i] = (short)j;
|
||
while (j < bucketCount && GetBucketChannel(&rxBucket[j]) == i)
|
||
{
|
||
j++;
|
||
}
|
||
}
|
||
bucketHead[i] = (short)j;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
void evInit(TArray<DBloodActor*>& actors)
|
||
{
|
||
int nCount = 0;
|
||
|
||
queue.clear();
|
||
memset(rxBucket, 0, sizeof(rxBucket));
|
||
|
||
// add all the tags to the bucket array
|
||
for (auto& sect : sector)
|
||
{
|
||
if (sect.hasX() && sect.xs().rxID > 0)
|
||
{
|
||
assert(nCount < kChannelMax);
|
||
rxBucket[nCount] = EventObject(§);
|
||
nCount++;
|
||
}
|
||
}
|
||
|
||
for (auto& wal : wall)
|
||
{
|
||
if (wal.hasX() && wal.xw().rxID > 0)
|
||
{
|
||
assert(nCount < kChannelMax);
|
||
rxBucket[nCount] = EventObject(&wal);
|
||
nCount++;
|
||
}
|
||
}
|
||
|
||
for (auto actor : actors)
|
||
{
|
||
if (actor->exists() && actor->hasX() && actor->xspr.rxID > 0)
|
||
{
|
||
assert(nCount < kChannelMax);
|
||
rxBucket[nCount] = EventObject(actor);
|
||
nCount++;
|
||
}
|
||
}
|
||
SortRXBucket(nCount);
|
||
bucketCount = nCount;
|
||
createBucketHeads();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
static bool evGetSourceState(EventObject& eob)
|
||
{
|
||
if (eob.isSector())
|
||
{
|
||
auto pSect = eob.sector();
|
||
return pSect->hasX() && pSect->xs().state != 0;
|
||
}
|
||
else if (eob.isWall())
|
||
{
|
||
auto pWall = eob.wall();
|
||
return pWall->hasX() && pWall->xw().state != 0;
|
||
}
|
||
else if (eob.isActor())
|
||
{
|
||
auto actor = eob.actor();
|
||
if (actor && actor->hasX())
|
||
return actor->xspr.state != 0;
|
||
}
|
||
|
||
// shouldn't reach this point
|
||
return false;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
void evSend(EventObject& eob, int rxId, COMMAND_ID command, DBloodActor* initiator)
|
||
{
|
||
switch (command) {
|
||
case kCmdState:
|
||
command = evGetSourceState(eob) ? kCmdOn : kCmdOff;
|
||
break;
|
||
case kCmdNotState:
|
||
command = evGetSourceState(eob) ? kCmdOff : kCmdOn;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
EVENT event;
|
||
event.target = eob;
|
||
event.cmd = command;
|
||
event.initiator = gModernMap? initiator : nullptr;
|
||
|
||
switch (rxId) {
|
||
case kChannelTextOver:
|
||
if (command >= kCmdNumberic) trTextOver(command - kCmdNumberic);
|
||
else viewSetSystemMessage("Invalid TextOver command by %s", eob.description().GetChars());
|
||
return;
|
||
case kChannelLevelExitNormal:
|
||
levelEndLevel(0);
|
||
return;
|
||
case kChannelLevelExitSecret:
|
||
levelEndLevel(1);
|
||
return;
|
||
#ifdef NOONE_EXTENSIONS
|
||
// finished level and load custom level <20> via numbered command.
|
||
case kChannelModernEndLevelCustom:
|
||
if (command >= kCmdNumberic) levelEndLevelCustom(command - kCmdNumberic);
|
||
else viewSetSystemMessage("Invalid Level-Exit# command by %s", eob.description().GetChars());
|
||
return;
|
||
#endif
|
||
case kChannelSetTotalSecrets:
|
||
if (command >= kCmdNumberic) gSecretMgr.SetCount(command - kCmdNumberic);
|
||
else viewSetSystemMessage("Invalid Total-Secrets command by %s", eob.description().GetChars());
|
||
break;
|
||
case kChannelSecretFound:
|
||
{
|
||
int nIndex = -1;
|
||
int nType = -1;
|
||
if (eob.isActor() && eob.actor()) nIndex = eob.actor()->GetIndex(), nType = Secret_Sprite;
|
||
else if (eob.isSector()) nIndex = eob.rawindex(), nType = Secret_Sector;
|
||
else if (eob.isWall()) nIndex = eob.rawindex(), nType = Secret_Wall;
|
||
if (SECRET_Trigger(nIndex, nType)) // if the hint system knows this secret it's a retrigger - skip that.
|
||
{
|
||
if (command >= kCmdNumberic)
|
||
{
|
||
gSecretMgr.Found(command - kCmdNumberic);
|
||
if (gGameOptions.nGameType == 0)
|
||
{
|
||
viewSetMessage(GStrings(FStringf("TXTB_SECRET%d", Random(2))), nullptr, MESSAGE_PRIORITY_SECRET);
|
||
}
|
||
}
|
||
else viewSetSystemMessage("Invalid Trigger-Secret command by %s", eob.description().GetChars());
|
||
}
|
||
break;
|
||
}
|
||
case kChannelRemoteBomb0:
|
||
case kChannelRemoteBomb1:
|
||
case kChannelRemoteBomb2:
|
||
case kChannelRemoteBomb3:
|
||
case kChannelRemoteBomb4:
|
||
case kChannelRemoteBomb5:
|
||
case kChannelRemoteBomb6:
|
||
case kChannelRemoteBomb7:
|
||
{
|
||
BloodStatIterator it(kStatThing);
|
||
while (auto actor = it.Next())
|
||
{
|
||
if (actor->spr.flags & 32)
|
||
continue;
|
||
if (actor->hasX())
|
||
{
|
||
if (actor->xspr.rxID == rxId)
|
||
trMessageSprite(actor, event);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case kChannelTeamAFlagCaptured:
|
||
case kChannelTeamBFlagCaptured:
|
||
{
|
||
BloodStatIterator it(kStatItem);
|
||
while (auto actor = it.Next())
|
||
{
|
||
if (actor->spr.flags & 32)
|
||
continue;
|
||
if (actor->hasX())
|
||
{
|
||
if (actor->xspr.rxID == rxId)
|
||
trMessageSprite(actor, event);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
|
||
#ifdef NOONE_EXTENSIONS
|
||
if (gModernMap)
|
||
{
|
||
// allow to send commands on player sprites
|
||
PLAYER* pPlayer = NULL;
|
||
if (playerRXRngIsFine(rxId))
|
||
{
|
||
if ((pPlayer = getPlayerById((rxId - kChannelPlayer7) + kMaxPlayers)) != nullptr)
|
||
{
|
||
if (command == kCmdEventKillFull)
|
||
evKillActor(pPlayer->actor);
|
||
else
|
||
trMessageSprite(pPlayer->actor, event);
|
||
}
|
||
}
|
||
else if (rxId == kChannelAllPlayers)
|
||
{
|
||
for (int i = 0; i < kMaxPlayers; i++)
|
||
{
|
||
if ((pPlayer = getPlayerById(i)) != nullptr)
|
||
{
|
||
if (command == kCmdEventKillFull)
|
||
evKillActor(pPlayer->actor);
|
||
else
|
||
trMessageSprite(pPlayer->actor, event);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
// send command on sprite which created the event sequence
|
||
else if (rxId == kChannelEventCauser && event.initiator != nullptr)
|
||
{
|
||
DBloodActor* einitiator = event.initiator;
|
||
if (!(einitiator->spr.flags & kHitagFree) && !(einitiator->spr.flags & kHitagRespawn))
|
||
{
|
||
if (command == kCmdEventKillFull)
|
||
evKillActor(einitiator);
|
||
else
|
||
trMessageSprite(einitiator, event);
|
||
}
|
||
|
||
return;
|
||
}
|
||
else if (command == kCmdEventKillFull)
|
||
{
|
||
killEvents(rxId, command);
|
||
return;
|
||
}
|
||
|
||
}
|
||
#endif
|
||
for (int i = bucketHead[rxId]; i < bucketHead[rxId + 1]; i++)
|
||
{
|
||
auto eo = rxBucket[i];
|
||
if (!event.event_isObject(eo))
|
||
{
|
||
if (eo.isSector())
|
||
{
|
||
trMessageSector(eo.sector(), event);
|
||
}
|
||
else if (eo.isWall())
|
||
{
|
||
trMessageWall(eo.wall(), event);
|
||
}
|
||
else if (eo.isActor())
|
||
{
|
||
auto actor = eo.actor();
|
||
|
||
if (actor && actor->hasX() && !(actor->spr.flags & 32))
|
||
{
|
||
if (actor->xspr.rxID > 0)
|
||
trMessageSprite(actor, event);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
void evPost_(EventObject& eob, unsigned int nDelta, COMMAND_ID command, DBloodActor* initiator)
|
||
{
|
||
assert(command != kCmdCallback);
|
||
if (command == kCmdState) command = evGetSourceState(eob) ? kCmdOn : kCmdOff;
|
||
else if (command == kCmdNotState) command = evGetSourceState(eob) ? kCmdOff : kCmdOn;
|
||
EVENT evn = { eob, (int8_t)command, 0, PlayClock + (int)nDelta, MakeObjPtr(gModernMap ? initiator : nullptr) };
|
||
queue.insert(evn);
|
||
}
|
||
|
||
void evPost_(const EventObject& eob, unsigned int nDelta, CALLBACK_ID callback)
|
||
{
|
||
EVENT evn = { eob, kCmdCallback, (int16_t)callback, PlayClock + (int)nDelta };
|
||
queue.insert(evn);
|
||
}
|
||
|
||
|
||
void evPostActor(DBloodActor* actor, unsigned int nDelta, COMMAND_ID command, DBloodActor* initiator)
|
||
{
|
||
auto ev = EventObject(actor);
|
||
evPost_(ev, nDelta, command, initiator);
|
||
}
|
||
|
||
void evPostActor(DBloodActor* actor, unsigned int nDelta, CALLBACK_ID callback)
|
||
{
|
||
evPost_(EventObject(actor), nDelta, callback);
|
||
}
|
||
|
||
void evPostSector(sectortype* sect, unsigned int nDelta, COMMAND_ID command, DBloodActor* initiator)
|
||
{
|
||
auto ev = EventObject(sect);
|
||
evPost_(ev, nDelta, command, initiator);
|
||
}
|
||
|
||
void evPostSector(sectortype* sect, unsigned int nDelta, CALLBACK_ID callback)
|
||
{
|
||
evPost_(EventObject(sect), nDelta, callback);
|
||
}
|
||
|
||
void evPostWall(walltype* wal, unsigned int nDelta, COMMAND_ID command, DBloodActor* initiator)
|
||
{
|
||
auto ev = EventObject(wal);
|
||
evPost_(ev, nDelta, command, initiator);
|
||
}
|
||
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
void evKill_(const EventObject& eob)
|
||
{
|
||
for (auto ev = queue.begin(); ev != queue.end();)
|
||
{
|
||
if (ev->event_isObject(eob)) ev = queue.erase(ev);
|
||
else ev++;
|
||
}
|
||
}
|
||
|
||
void evKill_(const EventObject& eob, DBloodActor* initiator)
|
||
{
|
||
for (auto ev = queue.begin(); ev != queue.end();)
|
||
{
|
||
if (ev->event_isObject(eob) && ev->initiator.ForceGet() == initiator) ev = queue.erase(ev);
|
||
else ev++;
|
||
}
|
||
}
|
||
|
||
void evKill_(const EventObject& eob, CALLBACK_ID cb)
|
||
{
|
||
for (auto ev = queue.begin(); ev != queue.end();)
|
||
{
|
||
if (ev->event_isObject(eob) && ev->funcID == cb) ev = queue.erase(ev);
|
||
else ev++;
|
||
}
|
||
}
|
||
|
||
void evKillActor(DBloodActor* actor)
|
||
{
|
||
evKill_(EventObject(actor));
|
||
}
|
||
|
||
void evKillActor(DBloodActor* actor, DBloodActor* initiator)
|
||
{
|
||
if (!gModernMap)
|
||
evKill_(EventObject(actor));
|
||
else
|
||
evKill_(EventObject(actor), initiator);
|
||
}
|
||
|
||
void evKillActor(DBloodActor* actor, CALLBACK_ID cb)
|
||
{
|
||
evKill_(EventObject(actor));
|
||
}
|
||
|
||
void evKillWall(walltype* wal)
|
||
{
|
||
evKill_(EventObject(wal));
|
||
}
|
||
|
||
void evKillSector(sectortype* sec)
|
||
{
|
||
evKill_(EventObject(sec));
|
||
}
|
||
|
||
void evKillWall(walltype* wal, DBloodActor* initiator)
|
||
{
|
||
evKill_(EventObject(wal), initiator);
|
||
}
|
||
|
||
void evKillSector(sectortype* sec, DBloodActor* initiator)
|
||
{
|
||
evKill_(EventObject(sec), initiator);
|
||
}
|
||
|
||
// these have no target.
|
||
void evSendGame(int rxId, COMMAND_ID command, DBloodActor* initiator = nullptr)
|
||
{
|
||
auto ev = EventObject(nullptr);
|
||
evSend(ev, rxId, command, initiator);
|
||
}
|
||
|
||
void evSendActor(DBloodActor* actor, int rxId, COMMAND_ID command, DBloodActor* initiator = nullptr)
|
||
{
|
||
auto ev = EventObject(actor);
|
||
evSend(ev, rxId, command, initiator);
|
||
}
|
||
|
||
void evSendSector(sectortype* sect, int rxId, COMMAND_ID command, DBloodActor* initiator = nullptr)
|
||
{
|
||
auto ev = EventObject(sect);
|
||
evSend(ev, rxId, command, initiator);
|
||
}
|
||
|
||
void evSendWall(walltype* wal, int rxId, COMMAND_ID command, DBloodActor* initiator = nullptr)
|
||
{
|
||
auto ev = EventObject(wal);
|
||
evSend(ev, rxId, command, initiator);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
void evProcess(unsigned int time)
|
||
{
|
||
while (queue.size() > 0 && (int)time >= queue.begin()->priority)
|
||
{
|
||
EVENT event = *queue.begin();
|
||
queue.erase(queue.begin());
|
||
if (event.target.isActor())
|
||
{
|
||
// Don't call events on destroyed actors. Seems to happen occasionally.
|
||
if (!event.target.actor() || event.target.actor()->spr.statnum == kStatFree) continue;
|
||
}
|
||
|
||
if (event.cmd == kCmdCallback)
|
||
{
|
||
assert(event.funcID < kCallbackMax);
|
||
if (event.target.isActor()) gCallback[event.funcID](event.target.actor(), nullptr);
|
||
else if (event.target.isSector()) gCallback[event.funcID](nullptr, event.target.sector());
|
||
// no case for walls defined here.
|
||
}
|
||
else
|
||
{
|
||
if (event.target.isActor()) trMessageSprite(event.target.actor(), event);
|
||
else if (event.target.isSector()) trMessageSector(event.target.sector(), event);
|
||
else if (event.target.isWall()) trMessageWall(event.target.wall(), event);
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
FSerializer& Serialize(FSerializer& arc, const char* keyname, EventObject& w, EventObject* def)
|
||
{
|
||
if (arc.BeginObject(keyname))
|
||
{
|
||
int type = w.isActor() ? 0 : w.isSector() ? 1 : 2;
|
||
arc("type", type);
|
||
switch (type)
|
||
{
|
||
case 0:
|
||
{
|
||
DBloodActor* a = arc.isWriting() ? w.actor() : nullptr;
|
||
arc("actor", a);
|
||
if (arc.isReading()) w = EventObject(a);
|
||
break;
|
||
}
|
||
case 1:
|
||
{
|
||
auto s = arc.isWriting() ? w.sector() : nullptr;
|
||
arc("sector", s);
|
||
if (arc.isReading()) w = EventObject(s);
|
||
break;
|
||
}
|
||
case 2:
|
||
{
|
||
auto s = arc.isWriting() ? w.wall() : nullptr;
|
||
arc("wall", s);
|
||
if (arc.isReading()) w = EventObject(s);
|
||
break;
|
||
}
|
||
}
|
||
arc.EndObject();
|
||
}
|
||
return arc;
|
||
}
|
||
|
||
FSerializer& Serialize(FSerializer& arc, const char* keyname, EVENT& w, EVENT* def)
|
||
{
|
||
if (arc.BeginObject(keyname))
|
||
{
|
||
arc("target", w.target)
|
||
("command", w.cmd)
|
||
("func", w.funcID)
|
||
("prio", w.priority)
|
||
("initiator",w.initiator)
|
||
.EndObject();
|
||
}
|
||
return arc;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
//
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
|
||
void SerializeEvents(FSerializer& arc)
|
||
{
|
||
if (arc.BeginObject("events"))
|
||
{
|
||
arc("bucketcount", bucketCount)
|
||
.Array("buckets", rxBucket, bucketCount)
|
||
.Array("buckethead", bucketHead, countof(bucketHead));
|
||
|
||
int numEvents = (int)queue.size();
|
||
arc("eventcount", numEvents);
|
||
if (arc.BeginArray("events"))
|
||
{
|
||
if (arc.isReading())
|
||
{
|
||
queue.clear();
|
||
EVENT ev;
|
||
for (int i = 0; i < numEvents; i++)
|
||
{
|
||
arc(nullptr, ev);
|
||
queue.insert(ev);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (auto item : queue)
|
||
{
|
||
arc(nullptr, item);
|
||
}
|
||
}
|
||
arc.EndArray();
|
||
}
|
||
arc.EndObject();
|
||
}
|
||
}
|
||
|
||
END_BLD_NS
|