From 1535182577395a9c304a7df33f23b68e40e9cb69 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 21 Nov 2020 15:09:38 +0100 Subject: [PATCH] - simplified the event management further and added a JSON serializer for it. --- source/blood/src/blood.h | 1 + source/blood/src/db.h | 12 + source/blood/src/eventq.cpp | 1109 +++++++++++++++++---------------- source/blood/src/eventq.h | 114 ++-- source/blood/src/loadsave.cpp | 12 +- source/core/savegamehelp.cpp | 3 +- 6 files changed, 678 insertions(+), 573 deletions(-) diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h index e8d704ae0..3634d34f3 100644 --- a/source/blood/src/blood.h +++ b/source/blood/src/blood.h @@ -71,6 +71,7 @@ struct GameInterface : ::GameInterface { const char* Name() override { return "Blood"; } void app_init() override; + void SerializeGameState(FSerializer& arc) override; void loadPalette() override; void clearlocalinputstate() override; bool GenerateSavePic() override; diff --git a/source/blood/src/db.h b/source/blood/src/db.h index 6c8ec4c68..24d187d3e 100644 --- a/source/blood/src/db.h +++ b/source/blood/src/db.h @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS + enum { kMaxXSprites = 16384, @@ -31,6 +32,17 @@ enum kMaxXSectors = 512 }; +enum +{ + kAttrMove = 0x0001, // is affected by movement physics + kAttrGravity = 0x0002, // is affected by gravity + kAttrFalling = 0x0004, // in z motion + kAttrAiming = 0x0008, + kAttrRespawn = 0x0010, + kAttrFree = 0x0020, + kAttrSmoke = 0x0100, // receives tsprite smoke/steam + }; + // by NoOne: functions to quckly check range of specifical arrays inline bool xspriRangeIsFine(int nXindex) { diff --git a/source/blood/src/eventq.cpp b/source/blood/src/eventq.cpp index a125f73cc..9b104e6df 100644 --- a/source/blood/src/eventq.cpp +++ b/source/blood/src/eventq.cpp @@ -2,10 +2,11 @@ /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 Nuke.YKT +Copyright (C) 2020 Christoph Oelckers -This file is part of NBlood. +This file is part of Raze. -NBlood is free software; you can redistribute it and/or +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. @@ -21,578 +22,644 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- -#include "ns.h" // Must come before everything else! - -#include -#include -#include +#include "ns.h" #include #include "build.h" +#include "printf.h" #include "common_game.h" - -#include "callback.h" -#include "db.h" #include "eventq.h" -#include "globals.h" -#include "loadsave.h" +#include "db.h" +#include "levels.h" #include "triggers.h" #include "view.h" #include "nnexts.h" #include "secrets.h" +#include "serializer.h" BEGIN_BLD_NS -struct queueItem +const int kMaxID = 1024; +RXBUCKET rxBucket[kChannelMax]; +unsigned short bucketHead[kMaxID + 1]; +static int bucketCount; +static std::multiset queue; + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static int GetBucketChannel(const RXBUCKET* pBucket) { - uint32_t priority; - EVENT data; + int nXIndex; + switch (pBucket->type) + { + case SS_SECTOR: + nXIndex = sector[pBucket->index].extra; + assert(nXIndex > 0); + return xsector[nXIndex].rxID; - bool operator<(const queueItem& other) const - { - return priority < other.priority; - } -}; + case SS_WALL: + nXIndex = wall[pBucket->index].extra; + assert(nXIndex > 0); + return xwall[nXIndex].rxID; -class EventQueue -{ -public: - std::multiset set; + case SS_SPRITE: + nXIndex = sprite[pBucket->index].extra; + assert(nXIndex > 0); + return xsprite[nXIndex].rxID; + } - bool IsNotEmpty(unsigned int nTime) - { - return set.size() > 0 && nTime >= set.begin()->priority; - } - EVENT ERemove(void) - { - assert(set.size() > 0); - EVENT data = set.begin()->data; - set.erase(set.begin()); - return data; - } - - template - void Kill(func pMatch) - { - for (auto i = set.begin(); i != set.end();) - { - if (pMatch(i->data)) - i = set.erase(i); - else - i++; - } - } -}; - -EventQueue eventQ; - -RXBUCKET rxBucket[kChannelMax+1]; - -int GetBucketChannel(const RXBUCKET *pRX) -{ - switch (pRX->type) { - case 6: { - int nIndex = pRX->index; - int nXIndex = sector[nIndex].extra; - assert(nXIndex > 0); - return xsector[nXIndex].rxID; - } - case 0: { - int nIndex = pRX->index; - int nXIndex = wall[nIndex].extra; - assert(nXIndex > 0); - return xwall[nXIndex].rxID; - } - case 3: { - int nIndex = pRX->index; - int nXIndex = sprite[nIndex].extra; - assert(nXIndex > 0); - return xsprite[nXIndex].rxID; - } - } - return 0; + Printf(PRINT_HIGH, "Unexpected rxBucket type %d", pBucket->type); + return 0; } -#if 0 -int CompareChannels(const void *a, const void *b) -{ - return GetBucketChannel((const RXBUCKET*)a)-GetBucketChannel((const RXBUCKET*)b); -} -#else -static int CompareChannels(RXBUCKET *a, RXBUCKET *b) -{ - return GetBucketChannel(a) - GetBucketChannel(b); -} -#endif +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- -static RXBUCKET *SortGetMiddle(RXBUCKET *a1, RXBUCKET *a2, RXBUCKET *a3) +static int CompareChannels(const RXBUCKET* ref1, const RXBUCKET* ref2) { - 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; - } + return GetBucketChannel(ref1) - GetBucketChannel(ref2); } -static void SortSwap(RXBUCKET *a, RXBUCKET *b) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static RXBUCKET* SortGetMiddle(RXBUCKET* a1, RXBUCKET* a2, RXBUCKET* a3) { - RXBUCKET t = *a; - *a = *b; - *b = t; + 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(RXBUCKET* a, RXBUCKET* b) +{ + RXBUCKET t = *a; + *a = *b; + *b = t; +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + static void SortRXBucket(int nCount) { - RXBUCKET *v144[32]; - int vc4[32]; - int v14 = 0; - RXBUCKET *pArray = rxBucket; - while (true) - { - while (nCount > 1) - { - if (nCount < 16) - { - for (int nDist = 3; nDist > 0; nDist -= 2) - { - for (RXBUCKET *pI = pArray+nDist; pI < pArray+nCount; pI += nDist) - { - for (RXBUCKET *pJ = pI; pJ > pArray && CompareChannels(pJ-nDist, pJ) > 0; pJ -= nDist) - { - SortSwap(pJ, pJ-nDist); - } - } - } - break; - } - RXBUCKET *v30, *vdi, *vsi; - vdi = pArray + nCount / 2; - if (nCount > 29) - { - v30 = pArray; - vsi = pArray + nCount-1; - if (nCount > 42) - { - int v20 = nCount / 8; - v30 = SortGetMiddle(v30, v30+v20, v30+v20*2); - vdi = SortGetMiddle(vdi-v20, vdi, vdi+v20); - vsi = SortGetMiddle(vsi-v20*2, vsi-v20, vsi); - } - vdi = SortGetMiddle(v30, vdi, vsi); - } - RXBUCKET v44 = *vdi; - RXBUCKET *vc = pArray; - RXBUCKET *v8 = pArray+nCount-1; - RXBUCKET *vbx = vc; - RXBUCKET *v4 = v8; - while (true) - { - while (vbx <= v4) - { - int nCmp = CompareChannels(vbx, &v44); - if (nCmp > 0) - break; - if (nCmp == 0) - { - SortSwap(vbx, vc); - vc++; - } - vbx++; - } - while (vbx <= v4) - { - int nCmp = CompareChannels(v4, &v44); - if (nCmp < 0) - break; - if (nCmp == 0) - { - SortSwap(v4, v8); - v8--; - } - v4--; - } - if (vbx > v4) - break; - SortSwap(vbx, v4); - v4--; - vbx++; - } - RXBUCKET *v2c = pArray+nCount; - int vt = ClipHigh(vbx-vc, vc-pArray); - for (int i = 0; i < vt; i++) - { - SortSwap(&vbx[i-vt], &pArray[i]); - } - vt = ClipHigh(v8-v4, v2c-v8-1); - for (int i = 0; i < vt; i++) - { - SortSwap(&v2c[i-vt], &vbx[i]); - } - int vvsi = v8-v4; - int vvdi = vbx-vc; - 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]; - } + RXBUCKET* v144[32]; + int vc4[32]; + int v14 = 0; + RXBUCKET* pArray = rxBucket; + while (true) + { + while (nCount > 1) + { + if (nCount < 16) + { + for (int nDist = 3; nDist > 0; nDist -= 2) + { + for (RXBUCKET* pI = pArray + nDist; pI < pArray + nCount; pI += nDist) + { + for (RXBUCKET* pJ = pI; pJ > pArray && CompareChannels(pJ - nDist, pJ) > 0; pJ -= nDist) + { + SortSwap(pJ, pJ - nDist); + } + } + } + break; + } + RXBUCKET * middle = pArray + nCount / 2; + if (nCount > 29) + { + RXBUCKET* first = pArray; + RXBUCKET* 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); + } + RXBUCKET pivot = *middle; + RXBUCKET* first = pArray; + RXBUCKET* last = pArray + nCount - 1; + RXBUCKET* vbx = first; + RXBUCKET* 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++; + } + RXBUCKET* v2c = pArray + nCount; + int vt = ClipHigh(vbx - first, first - pArray); + for (int i = 0; i < vt; i++) + { + SortSwap(&vbx[i - vt], &pArray[i]); + } + vt = ClipHigh(last - v4, v2c - last - 1); + for (int i = 0; i < vt; i++) + { + SortSwap(&v2c[i - vt], &vbx[i]); + } + int vvsi = last - v4; + int vvdi = 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]; + } } -unsigned short bucketHead[1024+1]; +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- -void evInit(void) +static void createBucketHeads() { - eventQ.set.clear(); - int nCount = 0; - for (int i = 0; i < numsectors; i++) - { - int nXSector = sector[i].extra; - if (nXSector >= kMaxXSectors) - I_Error("Invalid xsector reference in sector %d", i); - if (nXSector > 0 && xsector[nXSector].rxID > 0) - { - assert(nCount < kChannelMax); - rxBucket[nCount].type = 6; - rxBucket[nCount].index = i; - nCount++; - } - } - for (int i = 0; i < numwalls; i++) - { - int nXWall = wall[i].extra; - if (nXWall >= kMaxXWalls) - I_Error("Invalid xwall reference in wall %d", i); - if (nXWall > 0 && xwall[nXWall].rxID > 0) - { - assert(nCount < kChannelMax); - rxBucket[nCount].type = 0; - rxBucket[nCount].index = i; - nCount++; - } - } - for (int i = 0; i < kMaxSprites; i++) - { - if (sprite[i].statnum < kMaxStatus) - { - int nXSprite = sprite[i].extra; - if (nXSprite >= kMaxXSprites) - I_Error("Invalid xsprite reference in sprite %d", i); - if (nXSprite > 0 && xsprite[nXSprite].rxID > 0) - { - assert(nCount < kChannelMax); - rxBucket[nCount].type = 3; - rxBucket[nCount].index = i; - nCount++; - } - } - } - SortRXBucket(nCount); - int i, j = 0; - for (i = 0; i < 1024; i++) - { - bucketHead[i] = j; - while(j < nCount && GetBucketChannel(&rxBucket[j]) == i) - j++; - } - bucketHead[i] = j; + 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; } -char evGetSourceState(int nType, int nIndex) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void evInit() { - switch (nType) - { - case 6: - { - int nXIndex = sector[nIndex].extra; - assert(nXIndex > 0 && nXIndex < kMaxXSectors); - return xsector[nXIndex].state; - } - case 0: - { - int nXIndex = wall[nIndex].extra; - assert(nXIndex > 0 && nXIndex < kMaxXWalls); - return xwall[nXIndex].state; - } - case 3: - { - int nXIndex = sprite[nIndex].extra; - assert(nXIndex > 0 && nXIndex < kMaxXSprites); - return xsprite[nXIndex].state; - } - } - return 0; + int nCount = 0; + + queue.clear(); + memset(rxBucket, 0, sizeof(rxBucket)); + + // add all the tags to the bucket array + for (int i = 0; i < numsectors; i++) + { + int nXSector = sector[i].extra; + if (nXSector > 0 && xsector[nXSector].rxID > 0) + { + assert(nCount < kChannelMax); + rxBucket[nCount].type = SS_SECTOR; + rxBucket[nCount].index = i; + nCount++; + } + } + + for (int i = 0; i < numwalls; i++) + { + int nXWall = wall[i].extra; + if (nXWall > 0 && xwall[nXWall].rxID > 0) + { + assert(nCount < kChannelMax); + rxBucket[nCount].type = SS_WALL; + rxBucket[nCount].index = i; + nCount++; + } + } + + for (int i = 0; i < kMaxSprites; i++) + { + if (sprite[i].statnum < kMaxStatus) + { + int nXSprite = sprite[i].extra; + if (nXSprite > 0 && xsprite[nXSprite].rxID > 0) + { + assert(nCount < kChannelMax); + rxBucket[nCount].type = SS_SPRITE; + rxBucket[nCount].index = i; + nCount++; + } + } + } + + SortRXBucket(nCount); + bucketCount = nCount; + createBucketHeads(); } +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +static bool evGetSourceState(int type, int nIndex) +{ + int nXIndex; + + switch (type) + { + case SS_SECTOR: + nXIndex = sector[nIndex].extra; + assert(nXIndex > 0 && nXIndex < kMaxXSectors); + return xsector[nXIndex].state != 0; + + case SS_WALL: + nXIndex = wall[nIndex].extra; + assert(nXIndex > 0 && nXIndex < kMaxXWalls); + return xwall[nXIndex].state != 0; + + case SS_SPRITE: + nXIndex = sprite[nIndex].extra; + assert(nXIndex > 0 && nXIndex < kMaxXSprites); + return xsprite[nXIndex].state != 0; + } + + // shouldn't reach this point + return false; +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + void evSend(int nIndex, int nType, int rxId, COMMAND_ID command) { - switch (command) { - case kCmdState: - command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; - break; - case kCmdNotState: - command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; - break; - default: - break; - } - - EVENT event; - event.index = nIndex; - event.type = nType; - event.cmd = command; + switch (command) { + case kCmdState: + command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; + break; + case kCmdNotState: + command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; + break; + default: + break; + } - switch (rxId) { - case kChannelTextOver: - if (command >= kCmdNumberic) trTextOver(command - kCmdNumberic); - else viewSetSystemMessage("Invalid TextOver command by xobject #%d (object type %d)", nIndex, nType); - return; - case kChannelLevelExitNormal: - levelEndLevel(0); - return; - case kChannelLevelExitSecret: - levelEndLevel(1); - return; - #ifdef NOONE_EXTENSIONS - // finished level and load custom level ¹ via numbered command. - case kChannelModernEndLevelCustom: - if (command >= kCmdNumberic) levelEndLevelCustom(command - kCmdNumberic); - else viewSetSystemMessage("Invalid Level-Exit# command by xobject #%d (object type %d)", nIndex, nType); - return; - #endif - case kChannelSetTotalSecrets: - if (command >= kCmdNumberic) levelSetupSecret(command - kCmdNumberic); - else viewSetSystemMessage("Invalid Total-Secrets command by xobject #%d (object type %d)", nIndex, nType); - break; - case kChannelSecretFound: + EVENT event; + event.index = nIndex; + event.type = nType; + event.cmd = command; + + switch (rxId) { + case kChannelTextOver: + if (command >= kCmdNumberic) trTextOver(command - kCmdNumberic); + else viewSetSystemMessage("Invalid TextOver command by xobject #%d (object type %d)", nIndex, nType); + return; + case kChannelLevelExitNormal: + levelEndLevel(0); + return; + case kChannelLevelExitSecret: + levelEndLevel(1); + return; +#ifdef NOONE_EXTENSIONS + // finished level and load custom level ¹ via numbered command. + case kChannelModernEndLevelCustom: + if (command >= kCmdNumberic) levelEndLevelCustom(command - kCmdNumberic); + else viewSetSystemMessage("Invalid Level-Exit# command by xobject #%d (object type %d)", nIndex, nType); + return; +#endif + case kChannelSetTotalSecrets: + if (command >= kCmdNumberic) levelSetupSecret(command - kCmdNumberic); + else viewSetSystemMessage("Invalid Total-Secrets command by xobject #%d (object type %d)", nIndex, nType); + break; + case kChannelSecretFound: SECRET_Trigger(nIndex + 65536 * nType); - if (command >= kCmdNumberic) levelTriggerSecret(command - kCmdNumberic); - else viewSetSystemMessage("Invalid Trigger-Secret command by xobject #%d (object type %d)", nIndex, nType); - break; - case kChannelRemoteBomb0: - case kChannelRemoteBomb1: - case kChannelRemoteBomb2: - case kChannelRemoteBomb3: - case kChannelRemoteBomb4: - case kChannelRemoteBomb5: - case kChannelRemoteBomb6: - case kChannelRemoteBomb7: - { - int nSprite; - StatIterator it(kStatThing); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype* pSprite = &sprite[nSprite]; - if (pSprite->flags & 32) - continue; - int nXSprite = pSprite->extra; - if (nXSprite > 0) - { - XSPRITE* pXSprite = &xsprite[nXSprite]; - if (pXSprite->rxID == rxId) - trMessageSprite(nSprite, event); - } - } - return; - } - case kChannelTeamAFlagCaptured: - case kChannelTeamBFlagCaptured: - { - int nSprite; - StatIterator it(kStatItem); - while ((nSprite = it.NextIndex()) >= 0) - { - spritetype* pSprite = &sprite[nSprite]; - if (pSprite->flags & 32) - continue; - int nXSprite = pSprite->extra; - if (nXSprite > 0) - { - XSPRITE* pXSprite = &xsprite[nXSprite]; - if (pXSprite->rxID == rxId) - trMessageSprite(nSprite, event); - } - } - return; - } - default: - break; - } + if (command >= kCmdNumberic) levelTriggerSecret(command - kCmdNumberic); + else viewSetSystemMessage("Invalid Trigger-Secret command by xobject #%d (object type %d)", nIndex, nType); + break; + case kChannelRemoteBomb0: + case kChannelRemoteBomb1: + case kChannelRemoteBomb2: + case kChannelRemoteBomb3: + case kChannelRemoteBomb4: + case kChannelRemoteBomb5: + case kChannelRemoteBomb6: + case kChannelRemoteBomb7: + { + int nSprite; + StatIterator it(kStatThing); + while ((nSprite = it.NextIndex()) >= 0) + { + spritetype* pSprite = &sprite[nSprite]; + if (pSprite->flags & 32) + continue; + int nXSprite = pSprite->extra; + if (nXSprite > 0) + { + XSPRITE* pXSprite = &xsprite[nXSprite]; + if (pXSprite->rxID == rxId) + trMessageSprite(nSprite, event); + } + } + return; + } + case kChannelTeamAFlagCaptured: + case kChannelTeamBFlagCaptured: + { + int nSprite; + StatIterator it(kStatItem); + while ((nSprite = it.NextIndex()) >= 0) + { + spritetype* pSprite = &sprite[nSprite]; + if (pSprite->flags & 32) + continue; + int nXSprite = pSprite->extra; + if (nXSprite > 0) + { + XSPRITE* pXSprite = &xsprite[nXSprite]; + if (pXSprite->rxID == rxId) + trMessageSprite(nSprite, 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((kChannelPlayer0 - kChannelPlayer7) + kMaxPlayers)) != NULL) - trMessageSprite(pPlayer->nSprite, event); - } else if (rxId == kChannelAllPlayers) { - for (int i = 0; i < kMaxPlayers; i++) { - if ((pPlayer = getPlayerById(i)) != NULL) - trMessageSprite(pPlayer->nSprite, event); - } - } +#ifdef NOONE_EXTENSIONS + if (gModernMap) + { + // allow to send commands on player sprites + PLAYER* pPlayer = NULL; + if (playerRXRngIsFine(rxId)) + { + if ((pPlayer = getPlayerById((kChannelPlayer0 - kChannelPlayer7) + kMaxPlayers)) != nullptr) + trMessageSprite(pPlayer->nSprite, event); + } + else if (rxId == kChannelAllPlayers) + { + for (int i = 0; i < kMaxPlayers; i++) + { + if ((pPlayer = getPlayerById(i)) != nullptr) + trMessageSprite(pPlayer->nSprite, event); + } + } - } - #endif - for (int i = bucketHead[rxId]; i < bucketHead[rxId+1]; i++) { - if (event.type != rxBucket[i].type || event.index != rxBucket[i].index) { - switch (rxBucket[i].type) { - case 6: - trMessageSector(rxBucket[i].index, event); - break; - case 0: - trMessageWall(rxBucket[i].index, event); - break; - case 3: - { - int nSprite = rxBucket[i].index; - spritetype *pSprite = &sprite[nSprite]; - if (pSprite->flags&32) - continue; - int nXSprite = pSprite->extra; - if (nXSprite > 0) - { - XSPRITE *pXSprite = &xsprite[nXSprite]; - if (pXSprite->rxID > 0) - trMessageSprite(nSprite, event); - } - break; - } - } - } - } + } +#endif + for (int i = bucketHead[rxId]; i < bucketHead[rxId + 1]; i++) + { + if (event.type != rxBucket[i].type || event.index != rxBucket[i].index) + { + switch (rxBucket[i].type) + { + case 6: + trMessageSector(rxBucket[i].index, event); + break; + case 0: + trMessageWall(rxBucket[i].index, event); + break; + case 3: + { + int nSprite = rxBucket[i].index; + spritetype* pSprite = &sprite[nSprite]; + if (pSprite->flags & 32) + continue; + int nXSprite = pSprite->extra; + if (nXSprite > 0) + { + XSPRITE* pXSprite = &xsprite[nXSprite]; + if (pXSprite->rxID > 0) + trMessageSprite(nSprite, event); + } + break; + } + } + } + } } -void evPost(int nIndex, int nType, unsigned int nDelta, COMMAND_ID command) { - assert(command != kCmdCallback); - if (command == kCmdState) command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; - else if (command == kCmdNotState) command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; - EVENT evn = {}; - evn.index = nIndex; - evn.type = nType; - evn.cmd = command; - eventQ.set.insert({ gFrameClock + nDelta, evn }); -} +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- -void evPost(int nIndex, int nType, unsigned int nDelta, CALLBACK_ID callback) { - EVENT evn = {}; - evn.index = nIndex; - evn.type = nType; - evn.cmd = kCmdCallback; - evn.funcID = callback; - eventQ.set.insert({ gFrameClock + nDelta, evn }); -} - -void evProcess(unsigned int nTime) +void evPost(int nIndex, int nType, unsigned int nDelta, COMMAND_ID command) { - while(eventQ.IsNotEmpty(nTime)) - { - EVENT event = eventQ.ERemove(); - if (event.cmd == kCmdCallback) - { - assert(event.funcID < kCallbackMax); - assert(gCallback[event.funcID] != NULL); - gCallback[event.funcID](event.index); - } - else - { - switch (event.type) - { - case 6: - trMessageSector(event.index, event); - break; - case 0: - trMessageWall(event.index, event); - break; - case 3: - trMessageSprite(event.index, event); - break; - } - } - } + assert(command != kCmdCallback); + if (command == kCmdState) command = evGetSourceState(nType, nIndex) ? kCmdOn : kCmdOff; + else if (command == kCmdNotState) command = evGetSourceState(nType, nIndex) ? kCmdOff : kCmdOn; + EVENT evn = { (int16_t)nIndex, (int8_t)nType, (int8_t)command, 0, gFrameClock + (int)nDelta }; + queue.insert(evn); } -void evKill(int a1, int a2) +void evPost(int nIndex, int nType, unsigned int nDelta, CALLBACK_ID callback) { - eventQ.Kill([=](EVENT nItem)->bool {return nItem.index == a1 && nItem.type == a2; }); -} - -void evKill(int a1, int a2, CALLBACK_ID a3) -{ - EVENT evn = { (unsigned int)a1, (unsigned int)a2, kCmdCallback, (unsigned int)a3 }; - eventQ.Kill([=](EVENT nItem)->bool {return !memcmp(&nItem, &evn, sizeof(EVENT)); }); -} - -class EventQLoadSave : public LoadSave -{ -public: - virtual void Load(); - virtual void Save(); -}; - -void EventQLoadSave::Load() -{ - eventQ.set.clear(); - int nEvents; - Read(&nEvents, sizeof(nEvents)); - for (int i = 0; i < nEvents; i++) - { - queueItem event = {}; - Read(&event, sizeof(event)); - eventQ.set.insert(event); - } - Read(rxBucket, sizeof(rxBucket)); - Read(bucketHead, sizeof(bucketHead)); -} - -void EventQLoadSave::Save() -{ - int nEvents = eventQ.set.size(); - Write(&nEvents, sizeof(nEvents)); - for (auto &item : eventQ.set) - { - Write(&item, sizeof(item)); - } - Write(rxBucket, sizeof(rxBucket)); - Write(bucketHead, sizeof(bucketHead)); + EVENT evn = { (int16_t)nIndex, (int8_t)nType, kCmdCallback, (int16_t)callback, gFrameClock + (int)nDelta }; + queue.insert(evn); } -void EventQLoadSaveConstruct(void) +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void evKill(int index, int type) { - new EventQLoadSave(); + for (auto ev = queue.begin(); ev != queue.end();) + { + if (ev->index == index && ev->type == type) ev = queue.erase(ev); + else ev++; + } +} + +void evKill(int index, int type, CALLBACK_ID cb) +{ + for (auto ev = queue.begin(); ev != queue.end();) + { + if (ev->index == index && ev->type == type && ev->funcID == cb) ev = queue.erase(ev); + else ev++; + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void evProcess(unsigned int time) +{ + while (queue.size() > 0 && time >= queue.begin()->priority) + { + EVENT event = *queue.begin(); + queue.erase(queue.begin()); + + if (event.cmd == kCmdCallback) + { + assert(event.funcID < kCallbackMax); + assert(gCallback[event.funcID] != nullptr); + gCallback[event.funcID](event.index); + } + else + { + switch (event.type) + { + case SS_SECTOR: + trMessageSector(event.index, event); + break; + case SS_WALL: + trMessageWall(event.index, event); + break; + case SS_SPRITE: + trMessageSprite(event.index, event); + break; + } + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +FSerializer& Serialize(FSerializer& arc, const char* keyname, EVENT& w, EVENT* def) +{ + if (arc.BeginObject(keyname)) + { + arc("index", w.index) + ("type", w.type) + ("command", w.cmd) + ("func", w.funcID) + ("prio", w.priority) + .EndObject(); + } + return arc; +} + +FSerializer& Serialize(FSerializer& arc, const char* keyname, RXBUCKET& w, RXBUCKET* def) +{ + if (arc.BeginObject(keyname)) + { + arc("index", w.index) + ("type", w.type) + .EndObject(); + } + return arc; +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +void SerializeEvents(FSerializer& arc) +{ + if (arc.BeginObject("events")) + { + arc("bucketcount", bucketCount) + .Array("buckets", rxBucket, bucketCount); + + if (arc.isReading()) createBucketHeads(); + 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 diff --git a/source/blood/src/eventq.h b/source/blood/src/eventq.h index 93c00852e..b5f281bd4 100644 --- a/source/blood/src/eventq.h +++ b/source/blood/src/eventq.h @@ -27,48 +27,48 @@ BEGIN_BLD_NS enum { -kChannelZero = 0, -kChannelSetTotalSecrets, -kChannelSecretFound, -kChannelTextOver, -kChannelLevelExitNormal, -kChannelLevelExitSecret, -kChannelModernEndLevelCustom, // custom level end -kChannelLevelStart, -kChannelLevelStartMatch, // DM and TEAMS -kChannelLevelStartCoop, -kChannelLevelStartTeamsOnly, -kChannelPlayerDeathTeamA = 15, -kChannelPlayerDeathTeamB, -///////////////////////////// -// channels of players to send commands on -kChannelPlayer0 = 30, -kChannelPlayer1, -kChannelPlayer2, -kChannelPlayer3, -kChannelPlayer4, -kChannelPlayer5, -kChannelPlayer6, -kChannelPlayer7, -kChannelAllPlayers = kChannelPlayer0 + kMaxPlayers, -// channel of event causer -kChannelEventCauser = 50, -// map requires modern features to work properly -kChannelMapModernize = 60, -///////////////////////////// -kChannelTeamAFlagCaptured = 80, -kChannelTeamBFlagCaptured, -kChannelRemoteBomb0 = 90, -kChannelRemoteBomb1, -kChannelRemoteBomb2, -kChannelRemoteBomb3, -kChannelRemoteBomb4, -kChannelRemoteBomb5, -kChannelRemoteBomb6, -kChannelRemoteBomb7, -kChannelUser = 100, -kChannelUserMax = 1024, -kChannelMax = 4096, + kChannelZero = 0, + kChannelSetTotalSecrets, + kChannelSecretFound, + kChannelTextOver, + kChannelLevelExitNormal, + kChannelLevelExitSecret, + kChannelModernEndLevelCustom, // custom level end + kChannelLevelStart, + kChannelLevelStartMatch, // DM and TEAMS + kChannelLevelStartCoop, + kChannelLevelStartTeamsOnly, + kChannelPlayerDeathTeamA = 15, + kChannelPlayerDeathTeamB, + ///////////////////////////// + // channels of players to send commands on + kChannelPlayer0 = 30, + kChannelPlayer1, + kChannelPlayer2, + kChannelPlayer3, + kChannelPlayer4, + kChannelPlayer5, + kChannelPlayer6, + kChannelPlayer7, + kChannelAllPlayers = kChannelPlayer0 + kMaxPlayers, + // channel of event causer + kChannelEventCauser = 50, + // map requires modern features to work properly + kChannelMapModernize = 60, + ///////////////////////////// + kChannelTeamAFlagCaptured = 80, + kChannelTeamBFlagCaptured, + kChannelRemoteBomb0 = 90, + kChannelRemoteBomb1, + kChannelRemoteBomb2, + kChannelRemoteBomb3, + kChannelRemoteBomb4, + kChannelRemoteBomb5, + kChannelRemoteBomb6, + kChannelRemoteBomb7, + kChannelUser = 100, + kChannelUserMax = 1024, + kChannelMax = 4096, }; struct RXBUCKET @@ -76,6 +76,7 @@ struct RXBUCKET uint16_t index; uint8_t type; }; +extern void (*gCallback[])(int); extern RXBUCKET rxBucket[]; extern unsigned short bucketHead[]; @@ -120,6 +121,17 @@ kCmdModernFeaturesDisable = 200, // must be in object with kChannelMapModerniz kCmdNumbericMax = 255, }; +enum SSType +{ + SS_WALL = 0, + SS_CEILING = 1, + SS_FLOOR = 2, + SS_SPRITE = 3, + SS_MASKED = 4, + + SS_SECTOR = 6, +}; + inline bool playerRXRngIsFine(int rx) { return (rx >= kChannelPlayer0 && rx < kChannelPlayer7); } @@ -128,15 +140,21 @@ inline bool channelRangeIsFine(int channel) { return (channel >= kChannelUser && channel < kChannelUserMax); } -struct EVENT { - unsigned int index: 14; // index - unsigned int type: 3; // type - unsigned int cmd: 8; // cmd - unsigned int funcID: 8; // callback +struct EVENT +{ + int16_t index; + int8_t type; + int8_t cmd; + int16_t funcID; + int priority; + + bool operator<(const EVENT& other) const + { + return priority < other.priority; + } }; void evInit(void); -char evGetSourceState(int nType, int nIndex); void evSend(int nIndex, int nType, int rxId, COMMAND_ID command); void evPost(int nIndex, int nType, unsigned int nDelta, COMMAND_ID command); void evPost(int nIndex, int nType, unsigned int nDelta, CALLBACK_ID callback); diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp index 9fcb6c89c..de3aef3cb 100644 --- a/source/blood/src/loadsave.cpp +++ b/source/blood/src/loadsave.cpp @@ -731,7 +731,6 @@ void MyLoadSave::Save(void) void ActorLoadSaveConstruct(void); void AILoadSaveConstruct(void); void EndGameLoadSaveConstruct(void); -void EventQLoadSaveConstruct(void); void LevelsLoadSaveConstruct(void); void MessagesLoadSaveConstruct(void); void MirrorLoadSaveConstruct(void); @@ -751,7 +750,6 @@ void LoadSaveSetup(void) ActorLoadSaveConstruct(); AILoadSaveConstruct(); EndGameLoadSaveConstruct(); - EventQLoadSaveConstruct(); LevelsLoadSaveConstruct(); MessagesLoadSaveConstruct(); MirrorLoadSaveConstruct(); @@ -765,4 +763,14 @@ void LoadSaveSetup(void) #endif } + +void SerializeEvents(FSerializer& arc); + +void GameInterface::SerializeGameState(FSerializer& arc) +{ + SerializeEvents(arc); +} + + + END_BLD_NS diff --git a/source/core/savegamehelp.cpp b/source/core/savegamehelp.cpp index 66694dc1d..31f3568cc 100644 --- a/source/core/savegamehelp.cpp +++ b/source/core/savegamehelp.cpp @@ -101,6 +101,7 @@ static void SerializeSession(FSerializer& arc) SerializeAutomap(arc); SerializeHud(arc); SerializeGlobals(arc); + gi->SerializeGameState(arc); } //============================================================================= @@ -156,7 +157,6 @@ bool OpenSaveGameForRead(const char *name) // Load system-side data from savegames. loadMapBackup(currentLevel->fileName); SerializeSession(arc); - gi->SerializeGameState(arc); } return savereader != nullptr; } @@ -250,7 +250,6 @@ bool OpenSaveGameForWrite(const char* filename, const char *name) // Handle system-side modules that need to persist data in savegames here, in a central place. savegamesession.OpenWriter(save_formatted); SerializeSession(savegamesession); - gi->SerializeGameState(savegamesession); buff = savegamesession.GetCompressedOutput(); AddCompressedSavegameChunk("session.json", buff);