//------------------------------------------------------------------------- /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 sirlemonhead, Nuke.YKT This file is part of PCExhumed. PCExhumed 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 "glbackend/glbackend.h" #include "sequence.h" #include "engine.h" #include "exhumed.h" #include "sound.h" #include "player.h" #include "aistuff.h" #include "view.h" #include #include // TEMP #include BEGIN_PS_NS #define kMaxSequences 4096 #define kMaxSEQFiles 78 #define kMaxSEQFrames 18000 #define kMaxSEQChunks 21000 short sequences = 0; short frames = 0; short chunks = 0; short nPilotLightFrame; short nPilotLightCount; short nPilotLightBase; short nShadowWidth = 1; short nFlameHeight = 1; short SeqBase[kMaxSequences]; short SeqSize[kMaxSequences]; short SeqFlag[kMaxSequences]; short FrameSound[kMaxSEQFrames]; short FrameSize[kMaxSEQFrames]; short FrameBase[kMaxSEQFrames]; short FrameFlag[kMaxSEQFrames]; short ChunkYpos[kMaxSEQChunks]; short ChunkXpos[kMaxSEQChunks]; short ChunkPict[kMaxSEQChunks]; short ChunkFlag[kMaxSEQChunks]; const char *SeqNames[kMaxSEQFiles] = { "rothands", "sword", "pistol", "m_60", "flamer", // 4 "grenade", "cobra", "bonesaw", "scramble", "glove", "mummy", // 10 "skull", "poof", "kapow", "fireball", "bubble", "spider", // 16 "anubis", "anuball", "fish", "snakehed", // 20? "snakbody", "wasp", "cobrapow", "scorp", "joe", // 25 "status", "dead", "deadex", "anupoof", "skulpoof", // 30 "bullet", "shadow", "grenroll", "grenboom", "splash", "grenpow", "skulstrt", "firepoof", "bloodhit", "lion", // 40 "items", "lavag", // 42 "lsplash", "lavashot", "smokebal", "firepot", "rex", "set", // 48 "queen", "roach", // 50 "hawk", "setghost", "setgblow", "bizztail", "bizzpoof", "queenegg", "roacshot", "backgrnd", "screens", // 59 "arrow", "fonts", "drips", "firetrap", "magic2", "creepy", "slider", // 66 "ravolt", "eyehit", "font2", // 69 "seebubbl", "blood", "drum", "poof2", "deadbrn", "grenbubb", "rochfire", "rat" }; short SeqOffsets[kMaxSEQFiles]; int seq_ReadSequence(const char *seqName) { int i; char buffer[200]; buffer[0] = '\0'; strcat(buffer, seqName); strcat(buffer, ".seq"); auto hFile = fileSystem.ReopenFileReader(fileSystem.FindFile(buffer), true); if (!hFile.isOpen()) { Printf("Unable to open '%s'!\n", buffer); return 0; } short tag; hFile.Read(&tag, sizeof(tag)); if (tag < MAKE_ID('I', 'H', 0, 0) || (tag > MAKE_ID('I', 'H', 0, 0) && tag != MAKE_ID('D', 'S', 0, 0))) { Printf("Unsupported sequence version!\n"); return 0; } short centerx, centery; // TODO - are global vars? short nSeqs; hFile.Read(¢erx, sizeof(centerx)); hFile.Read(¢ery, sizeof(centery)); hFile.Read(&nSeqs, sizeof(nSeqs)); if (nSeqs <= 0 || sequences + nSeqs >= kMaxSequences) { if (nSeqs < 0) { Printf("Invalid sequence count!\n"); return 0; } else { I_Error("Not enough sequences available! Increase array!\n"); } } hFile.Read(&SeqBase[sequences], nSeqs * sizeof(SeqBase[0])); hFile.Read(&SeqSize[sequences], nSeqs * sizeof(SeqSize[0])); hFile.Read(&SeqFlag[sequences], nSeqs * sizeof(SeqFlag[0])); for (i = 0; i < nSeqs; i++) { SeqBase[sequences + i] += frames; } short vdi = frames; int16_t nFrames; hFile.Read(&nFrames, sizeof(nFrames)); if (nFrames <= 0 || frames + nFrames >= kMaxSEQFrames) { if (nFrames < 0 ) { Printf("Invalid frame count!\n"); return 0; } else { I_Error("Not enough frames available! Increase FRAMEMAX!\n"); } } hFile.Read(&FrameBase[frames], nFrames * sizeof(FrameBase[0])); hFile.Read(&FrameSize[frames], nFrames * sizeof(FrameSize[0])); hFile.Read(&FrameFlag[frames], nFrames * sizeof(FrameFlag[0])); memset(&FrameSound[frames], -1, nFrames * sizeof(FrameSound[0])); for (i = 0; i < nFrames; i++) { FrameBase[frames + i] += chunks; } int16_t nChunks; hFile.Read(&nChunks, sizeof(nChunks)); if (nChunks < 0 || chunks + nChunks >= kMaxSEQChunks) { if (nChunks < 0 ) { Printf("Invalid chunk count!\n"); return 0; } else { I_Error("Not enough chunks available! Increase CHUNKMAX!\n"); } } hFile.Read(&ChunkXpos[chunks], nChunks * sizeof(ChunkXpos[0])); hFile.Read(&ChunkYpos[chunks], nChunks * sizeof(ChunkYpos[0])); hFile.Read(&ChunkPict[chunks], nChunks * sizeof(ChunkPict[0])); hFile.Read(&ChunkFlag[chunks], nChunks * sizeof(ChunkFlag[0])); for (i = 0; i < nChunks; i++) { ChunkXpos[chunks + i] -= centerx; ChunkYpos[chunks + i] -= centery; } sequences += nSeqs; FrameBase[frames + nFrames] = chunks + nChunks; frames += nFrames; SeqBase[sequences] = frames; chunks += nChunks; if (tag == MAKE_ID('D', 'S', 0, 0)) { short var_20; hFile.Read(&var_20, sizeof(var_20)); for (i = 0; i < var_20; i++) { hFile.Read(&buffer[i * 10], 8); } short var_24; hFile.Read(&var_24, sizeof(var_24)); for (i = 0; i < var_24; i++) { short var_28, var_2C; hFile.Read(&var_28, sizeof(var_28)); hFile.Read(&var_2C, sizeof(var_2C)); int hSound = LoadSound(&buffer[(var_2C&0x1FF)*10]); assert(vdi + var_28 < 18000); FrameSound[vdi + var_28] = hSound | (var_2C & 0xFE00); } } return nSeqs; } int seq_GetFirstSeqPicnum(int nSeq) { int i = SeqOffsets[nSeq]; i = SeqBase[i]; i = FrameBase[i]; i = ChunkPict[i]; return i; } void seq_LoadSequences() { int i; for (i = 0; i < kMaxSEQFiles; i++) { SeqOffsets[i] = sequences; if (seq_ReadSequence(SeqNames[i]) == 0) { Printf("Error loading '%s'\n", SeqNames[i]); } } #if 0 FILE* f = fopen("seq.dump", "wb"); fwrite(SeqBase, 1, sizeof(SeqBase), f); fwrite(SeqSize, 1, sizeof(SeqSize), f); fwrite(SeqFlag, 1, sizeof(SeqFlag), f); fwrite("++++++++++++++++", 1, 16, f); fwrite(FrameSound, 1, sizeof(FrameSound), f); fwrite("++++++++++++++++", 1, 16, f); fwrite(FrameSize, 1, sizeof(FrameSize), f); fwrite(FrameBase, 1, sizeof(FrameBase), f); fwrite(FrameFlag, 1, sizeof(FrameFlag), f); fwrite(ChunkYpos, 1, sizeof(ChunkYpos), f); fwrite(ChunkXpos, 1, sizeof(ChunkXpos), f); fwrite(ChunkPict, 1, sizeof(ChunkPict), f); fwrite(ChunkFlag, 1, sizeof(ChunkFlag), f); fclose(f); #endif nShadowPic = seq_GetFirstSeqPicnum(kSeqShadow); nShadowWidth = tilesiz[nShadowPic].x; nFlameHeight = tilesiz[seq_GetFirstSeqPicnum(kSeqFirePoof)].y; nBackgroundPic = seq_GetFirstSeqPicnum(kSeqBackgrnd); nPilotLightBase = SeqBase[SeqOffsets[kSeqFlamer] + 3]; nPilotLightCount = SeqSize[SeqOffsets[kSeqFlamer] + 3]; nPilotLightFrame = 0; nFontFirstChar = seq_GetFirstSeqPicnum(kSeqFont2); short nSize = SeqSize[SeqOffsets[kSeqFont2]]; for (i = 0; i < nSize; i++) { auto tex = tileGetTexture(nFontFirstChar + i); tex->SetOffsets(0, 0); } } short seq_GetFrameFlag(short val, short nFrame) { return FrameFlag[SeqBase[val] + nFrame]; } void seq_DrawPilotLightSeq(double xOffset, double yOffset) { short nSect = nPlayerViewSect[nLocalPlayer]; if (!(SectFlag[nSect] & kSectUnderwater)) { short nFrame = nPilotLightBase + nPilotLightFrame; short nFrameBase = FrameBase[nFrame]; short nFrameSize = FrameSize[nFrame]; while (1) { nFrameSize--; if (nFrameSize < 0) return; short nTile = ChunkPict[nFrameBase]; double x = ChunkXpos[nFrameBase] + (160 + xOffset); double y = ChunkYpos[nFrameBase] + (100 + yOffset); hud_drawsprite(x, y, 65536, fmod(-2 * FixedToFloat(PlayerList[nLocalPlayer].q16angle), kAngleMask + 1), nTile, 0, 0, 1); nFrameBase++; } } } /* 6 parameters arg0 - shade? */ int seq_DrawGunSequence(int nSeqOffset, short dx, double xOffs, double yOffs, int nShade, int nPal) { int nFrame = SeqBase[nSeqOffset] + dx; int nFrameBase = FrameBase[nFrame]; int nFrameSize = FrameSize[nFrame]; int frameFlag = FrameFlag[nFrame]; while (1) { nFrameSize--; if (nFrameSize < 0) break; int x = ChunkXpos[nFrameBase] + 160; int y = ChunkYpos[nFrameBase] + 100; int stat = 0; if (ChunkFlag[nFrameBase] & 1) stat |= RS_XFLIPHUD; if (ChunkFlag[nFrameBase] & 2) stat |= RS_YFLIPHUD; short nTile = ChunkPict[nFrameBase]; if (frameFlag & 4) nShade = -100; double alpha = 1; if (nPlayerInvisible[nLocalPlayer]) { alpha = 0.3; } hud_drawsprite(x + xOffs, y + yOffs, 65536, 0, nTile, nShade, nPal, stat, alpha); nFrameBase++; } return frameFlag; } int seq_GetFrameSound(int val, int edx) { return FrameSound[SeqBase[val] + edx]; } void seq_MoveSequence(short nSprite, short nSeq, short bx) { assert(nSeq >= 0); // TEMP int nSound = FrameSound[SeqBase[nSeq] + bx]; if (nSound == -1) { return; } if (nSprite > -1) { D3PlayFX(nSound, nSprite); } else { PlayLocalSound(nSound, 0); } } int seq_GetSeqPicnum2(short nSeq, short nFrame) { short nBase = FrameBase[SeqBase[nSeq] + nFrame]; return ChunkPict[nBase]; } int seq_GetSeqPicnum(short nSeq, short edx, short ebx) { edx += SeqOffsets[nSeq]; ebx += SeqBase[edx]; short c = FrameBase[ebx]; return ChunkPict[c]; } int seq_PlotArrowSequence(short nSprite, short nSeq, int nVal) { tspriteptr_t pTSprite = &tsprite[nSprite]; int nAngle = GetMyAngle(nCamerax - pTSprite->x, nCameray - pTSprite->y); int nSeqOffset = ((((pTSprite->ang + 512) - nAngle) + 128) & kAngleMask) >> 8; short nFrame = SeqBase[nSeqOffset + nSeq] + nVal; short nFrameBase = FrameBase[nFrame]; short nFrameSize = FrameSize[nFrame]; uint8_t nShade = pTSprite->shade; short nStat = pTSprite->cstat; nStat |= 0x80; if (nSeqOffset & 3) { nStat |= 0x18; } else { nStat &= 0x0E7; } if (FrameFlag[nFrame] & 4) { nShade -= 100; } pTSprite->cstat = nStat; pTSprite->shade = nShade; pTSprite->statnum = nFrameSize; if (ChunkFlag[nFrameBase] & 1) { pTSprite->xoffset = ChunkXpos[nFrameBase]; pTSprite->cstat |= 4; } else { pTSprite->xoffset = -ChunkXpos[nFrameBase]; } pTSprite->yoffset = -ChunkYpos[nFrameBase]; pTSprite->picnum = ChunkPict[nFrameBase]; return ChunkPict[nFrameBase]; } int seq_PlotSequence(short nSprite, short edx, short nFrame, short ecx) { tspriteptr_t pTSprite = &tsprite[nSprite]; int nAngle = GetMyAngle(nCamerax - pTSprite->x, nCameray - pTSprite->y); int val; if (ecx & 1) { val = 0; } else { val = (((pTSprite->ang - nAngle) + 128) & kAngleMask) >> 8; } int eax = SeqBase[edx] + nFrame; int edi = SeqBase[edx + val] + nFrame; short nBase = FrameBase[edi]; short nSize = FrameSize[edi]; int8_t shade = pTSprite->shade; if (FrameFlag[eax] & 4) { shade -= 100; } short nPict = ChunkPict[nBase]; if (ecx & 0x100) { edx = -3; } else { edx = 100; } int esi = nSize + 1; esi += edx; int var_14 = edx + 1; short nOwner = pTSprite->owner; while (1) { esi--; nSize--; if (esi < var_14) { break; } tspriteptr_t tsp = &tsprite[spritesortcnt++]; tsp->x = pTSprite->x; tsp->y = pTSprite->y; tsp->z = pTSprite->z; tsp->shade = shade; tsp->pal = pTSprite->pal; tsp->xrepeat = pTSprite->xrepeat; tsp->yrepeat = pTSprite->yrepeat; tsp->ang = pTSprite->ang; tsp->owner = pTSprite->owner; tsp->sectnum = pTSprite->sectnum; tsp->cstat = pTSprite->cstat |= 0x80; tsp->statnum = esi; if (ChunkFlag[nBase] & 1) { tsp->xoffset = ChunkXpos[nBase]; tsp->cstat |= 4; // x-flipped } else { tsp->xoffset = -ChunkXpos[nBase]; } tsp->yoffset = -ChunkYpos[nBase]; tsp->picnum = ChunkPict[nBase]; nBase++; } if (!(pTSprite->cstat & 0x101) || (sprite[nOwner].statnum == 100 && nNetPlayerCount)) { pTSprite->owner = -1; } else { short nSector = pTSprite->sectnum; int nFloorZ = sector[nSector].floorz; if (nFloorZ <= eyelevel[nLocalPlayer] + initz) { pTSprite->owner = -1; } else { pTSprite->picnum = nShadowPic; int edx = ((tilesiz[nPict].x << 5) / nShadowWidth) - ((nFloorZ - pTSprite->z) >> 10); if (edx < 1) { edx = 1; } pTSprite->cstat = 0x22; // transluscence, floor sprite pTSprite->z = videoGetRenderMode() >= REND_POLYMOST ? nFloorZ : nFloorZ + 1; pTSprite->yrepeat = (uint8_t)edx; pTSprite->xrepeat = (uint8_t)edx; pTSprite->statnum = -3; pTSprite->pal = 0; } } return nPict; } static SavegameHelper sgh("sequence", SV(nPilotLightFrame), SV(nPilotLightCount), SV(nPilotLightBase), SV(nShadowWidth), SV(nFlameHeight), nullptr); END_PS_NS