1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 Nuke.YKT
5 
6 This file is part of NBlood.
7 
8 NBlood is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23 #include <string.h>
24 #include "build.h"
25 #include "common_game.h"
26 
27 #include "blood.h"
28 #include "db.h"
29 #include "eventq.h"
30 #include "globals.h"
31 #include "levels.h"
32 #include "loadsave.h"
33 #include "sfx.h"
34 #include "sound.h"
35 #include "seq.h"
36 #include "gameutil.h"
37 #include "actor.h"
38 #include "tile.h"
39 #include "view.h"
40 
41 #define kMaxClients 256
42 #define kMaxSequences 1024
43 
44 static ACTIVE activeList[kMaxSequences];
45 static int activeCount = 0;
46 static int nClients = 0;
47 static void(*clientCallback[kMaxClients])(int, int);
48 
seqRegisterClient(void (* pClient)(int,int))49 int seqRegisterClient(void(*pClient)(int, int))
50 {
51     dassert(nClients < kMaxClients);
52     clientCallback[nClients] = pClient;
53     return nClients++;
54 }
55 
Preload(void)56 void Seq::Preload(void)
57 {
58     if (memcmp(signature, "SEQ\x1a", 4) != 0)
59         ThrowError("Invalid sequence");
60     if ((version & 0xff00) != 0x300)
61         ThrowError("Obsolete sequence version");
62     for (int i = 0; i < nFrames; i++)
63         tilePreloadTile(seqGetTile(&frames[i]));
64 }
65 
Precache(void)66 void Seq::Precache(void)
67 {
68     if (memcmp(signature, "SEQ\x1a", 4) != 0)
69         ThrowError("Invalid sequence");
70     if ((version & 0xff00) != 0x300)
71         ThrowError("Obsolete sequence version");
72     for (int i = 0; i < nFrames; i++)
73         tilePrecacheTile(seqGetTile(&frames[i]));
74 }
75 
seqPrecacheId(int id)76 void seqPrecacheId(int id)
77 {
78     DICTNODE *hSeq = gSysRes.Lookup(id, "SEQ");
79     if (!hSeq)
80         return;
81     Seq *pSeq = (Seq*)gSysRes.Lock(hSeq);
82     pSeq->Precache();
83     gSysRes.Unlock(hSeq);
84 }
85 
86 SEQINST siWall[kMaxXWalls];
87 SEQINST siCeiling[kMaxXSectors];
88 SEQINST siFloor[kMaxXSectors];
89 SEQINST siSprite[kMaxXSprites];
90 SEQINST siMasked[kMaxXWalls];
91 
UpdateSprite(int nXSprite,SEQFRAME * pFrame)92 void UpdateSprite(int nXSprite, SEQFRAME *pFrame)
93 {
94     dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
95     int nSprite = xsprite[nXSprite].reference;
96     dassert(nSprite >= 0 && nSprite < kMaxSprites);
97     spritetype *pSprite = &sprite[nSprite];
98     dassert(pSprite->extra == nXSprite);
99     if (pSprite->flags & 2)
100     {
101         if (tilesiz[pSprite->picnum].y != tilesiz[seqGetTile(pFrame)].y || picanm[pSprite->picnum].yofs != picanm[seqGetTile(pFrame)].yofs
102             || (pFrame->yrepeat && pFrame->yrepeat != pSprite->yrepeat))
103             pSprite->flags |= 4;
104     }
105     pSprite->picnum = seqGetTile(pFrame);
106     if (pFrame->pal)
107         pSprite->pal = pFrame->pal;
108     pSprite->shade = pFrame->shade;
109 
110     int scale = xsprite[nXSprite].scale; // SEQ size scaling
111     if (pFrame->xrepeat) {
112         if (scale) pSprite->xrepeat = ClipRange(mulscale8(pFrame->xrepeat, scale), 0, 255);
113         else pSprite->xrepeat = pFrame->xrepeat;
114     }
115 
116     if (pFrame->yrepeat) {
117         if (scale) pSprite->yrepeat = ClipRange(mulscale8(pFrame->yrepeat, scale), 0, 255);
118         else pSprite->yrepeat = pFrame->yrepeat;
119     }
120 
121     if (pFrame->transparent)
122         pSprite->cstat |= 2;
123     else
124         pSprite->cstat &= ~2;
125     if (pFrame->transparent2)
126         pSprite->cstat |= 512;
127     else
128         pSprite->cstat &= ~512;
129     if (pFrame->blockable)
130         pSprite->cstat |= 1;
131     else
132         pSprite->cstat &= ~1;
133     if (pFrame->hittable)
134         pSprite->cstat |= 256;
135     else
136         pSprite->cstat &= ~256;
137     if (pFrame->invisible)
138         pSprite->cstat |= 32768;
139     else
140         pSprite->cstat &= (unsigned short)~32768;
141     if (pFrame->at6_0)
142         pSprite->cstat |= 4096;
143     else
144         pSprite->cstat &= ~4096;
145     if (pFrame->at5_6)
146         pSprite->flags |= 256;
147     else
148         pSprite->flags &= ~256;
149     if (pFrame->at5_7)
150         pSprite->flags |= 8;
151     else
152         pSprite->flags &= ~8;
153     if (pFrame->at6_3)
154         pSprite->flags |= 1024;
155     else
156         pSprite->flags &= ~1024;
157     if (pFrame->at6_4)
158         pSprite->flags |= 2048;
159     else
160         pSprite->flags &= ~2048;
161 }
162 
UpdateWall(int nXWall,SEQFRAME * pFrame)163 void UpdateWall(int nXWall, SEQFRAME *pFrame)
164 {
165     dassert(nXWall > 0 && nXWall < kMaxXWalls);
166     int nWall = xwall[nXWall].reference;
167     dassert(nWall >= 0 && nWall < kMaxWalls);
168     walltype *pWall = &wall[nWall];
169     dassert(pWall->extra == nXWall);
170     pWall->picnum = seqGetTile(pFrame);
171     if (pFrame->pal)
172         pWall->pal = pFrame->pal;
173     if (pFrame->transparent)
174         pWall->cstat |= 128;
175     else
176         pWall->cstat &= ~128;
177     if (pFrame->transparent2)
178         pWall->cstat |= 512;
179     else
180         pWall->cstat &= ~512;
181     if (pFrame->blockable)
182         pWall->cstat |= 1;
183     else
184         pWall->cstat &= ~1;
185     if (pFrame->hittable)
186         pWall->cstat |= 64;
187     else
188         pWall->cstat &= ~64;
189 }
190 
UpdateMasked(int nXWall,SEQFRAME * pFrame)191 void UpdateMasked(int nXWall, SEQFRAME *pFrame)
192 {
193     dassert(nXWall > 0 && nXWall < kMaxXWalls);
194     int nWall = xwall[nXWall].reference;
195     dassert(nWall >= 0 && nWall < kMaxWalls);
196     walltype *pWall = &wall[nWall];
197     dassert(pWall->extra == nXWall);
198     dassert(pWall->nextwall >= 0);
199     walltype *pWallNext = &wall[pWall->nextwall];
200     pWall->overpicnum = pWallNext->overpicnum = seqGetTile(pFrame);
201     if (pFrame->pal)
202         pWall->pal = pWallNext->pal = pFrame->pal;
203     if (pFrame->transparent)
204     {
205         pWall->cstat |= 128;
206         pWallNext->cstat |= 128;
207     }
208     else
209     {
210         pWall->cstat &= ~128;
211         pWallNext->cstat &= ~128;
212     }
213     if (pFrame->transparent2)
214     {
215         pWall->cstat |= 512;
216         pWallNext->cstat |= 512;
217     }
218     else
219     {
220         pWall->cstat &= ~512;
221         pWallNext->cstat &= ~512;
222     }
223     if (pFrame->blockable)
224     {
225         pWall->cstat |= 1;
226         pWallNext->cstat |= 1;
227     }
228     else
229     {
230         pWall->cstat &= ~1;
231         pWallNext->cstat &= ~1;
232     }
233     if (pFrame->hittable)
234     {
235         pWall->cstat |= 64;
236         pWallNext->cstat |= 64;
237     }
238     else
239     {
240         pWall->cstat &= ~64;
241         pWallNext->cstat &= ~64;
242     }
243 }
244 
UpdateFloor(int nXSector,SEQFRAME * pFrame)245 void UpdateFloor(int nXSector, SEQFRAME *pFrame)
246 {
247     dassert(nXSector > 0 && nXSector < kMaxXSectors);
248     int nSector = xsector[nXSector].reference;
249     dassert(nSector >= 0 && nSector < kMaxSectors);
250     sectortype *pSector = &sector[nSector];
251     dassert(pSector->extra == nXSector);
252     pSector->floorpicnum = seqGetTile(pFrame);
253     pSector->floorshade = pFrame->shade;
254     if (pFrame->pal)
255         pSector->floorpal = pFrame->pal;
256 }
257 
UpdateCeiling(int nXSector,SEQFRAME * pFrame)258 void UpdateCeiling(int nXSector, SEQFRAME *pFrame)
259 {
260     dassert(nXSector > 0 && nXSector < kMaxXSectors);
261     int nSector = xsector[nXSector].reference;
262     dassert(nSector >= 0 && nSector < kMaxSectors);
263     sectortype *pSector = &sector[nSector];
264     dassert(pSector->extra == nXSector);
265     pSector->ceilingpicnum = seqGetTile(pFrame);
266     pSector->ceilingshade = pFrame->shade;
267     if (pFrame->pal)
268         pSector->ceilingpal = pFrame->pal;
269 }
270 
Update(ACTIVE * pActive)271 void SEQINST::Update(ACTIVE *pActive)
272 {
273     dassert(frameIndex < pSequence->nFrames);
274     switch (pActive->type)
275     {
276     case 0:
277         UpdateWall(pActive->xindex, &pSequence->frames[frameIndex]);
278         break;
279     case 1:
280         UpdateCeiling(pActive->xindex , &pSequence->frames[frameIndex]);
281         break;
282     case 2:
283         UpdateFloor(pActive->xindex, &pSequence->frames[frameIndex]);
284         break;
285     case 3:
286     {
287         UpdateSprite(pActive->xindex, &pSequence->frames[frameIndex]);
288         if (pSequence->frames[frameIndex].at6_1) {
289 
290             int sound = pSequence->nSoundID;
291 
292             // by NoOne: add random sound range feature
293             if (!VanillaMode() && pSequence->frames[frameIndex].soundRange > 0)
294                 sound += Random(((pSequence->frames[frameIndex].soundRange == 1) ? 2 : pSequence->frames[frameIndex].soundRange));
295 
296             sfxPlay3DSound(&sprite[xsprite[pActive->xindex].reference], sound, -1, 0);
297         }
298 
299         // by NoOne: add surfaceSound trigger feature
300         spritetype* pSprite = &sprite[xsprite[pActive->xindex].reference];
301         if (!VanillaMode() && pSequence->frames[frameIndex].surfaceSound && zvel[pSprite->index] == 0 && xvel[pSprite->index] != 0) {
302 
303             if (gUpperLink[pSprite->sectnum] >= 0) break; // don't play surface sound for stacked sectors
304             int surf = tileGetSurfType(pSprite->sectnum + 0x4000); if (!surf) break;
305             static int surfSfxMove[15][4] = {
306                 /* {snd1, snd2, gameVolume, myVolume} */
307                 {800,801,80,25},
308                 {802,803,80,25},
309                 {804,805,80,25},
310                 {806,807,80,25},
311                 {808,809,80,25},
312                 {810,811,80,25},
313                 {812,813,80,25},
314                 {814,815,80,25},
315                 {816,817,80,25},
316                 {818,819,80,25},
317                 {820,821,80,25},
318                 {822,823,80,25},
319                 {824,825,80,25},
320                 {826,827,80,25},
321                 {828,829,80,25},
322             };
323 
324             int sndId = surfSfxMove[surf][Random(2)];
325             DICTNODE * hRes = gSoundRes.Lookup(sndId, "SFX"); SFX * pEffect = (SFX*)gSoundRes.Load(hRes);
326             sfxPlay3DSoundCP(pSprite, sndId, -1, 0, 0, (surfSfxMove[surf][2] != pEffect->relVol) ? pEffect->relVol : surfSfxMove[surf][3]);
327         }
328         break;
329     }
330     case 4:
331         UpdateMasked(pActive->xindex, &pSequence->frames[frameIndex]);
332         break;
333     }
334     if (pSequence->frames[frameIndex].at5_5 && nCallbackID != -1)
335         clientCallback[nCallbackID](pActive->type, pActive->xindex);
336 }
337 
GetInstance(int nType,int nXIndex)338 SEQINST * GetInstance(int nType, int nXIndex)
339 {
340     switch (nType)
341     {
342     case 0:
343         if (nXIndex > 0 && nXIndex < kMaxXWalls) return &siWall[nXIndex];
344         break;
345     case 1:
346         if (nXIndex > 0 && nXIndex < kMaxXSectors) return &siCeiling[nXIndex];
347         break;
348     case 2:
349         if (nXIndex > 0 && nXIndex < kMaxXSectors) return &siFloor[nXIndex];
350         break;
351     case 3:
352         if (nXIndex > 0 && nXIndex < kMaxXSprites) return &siSprite[nXIndex];
353         break;
354     case 4:
355         if (nXIndex > 0 && nXIndex < kMaxWalls) return &siMasked[nXIndex];
356         break;
357     }
358     return NULL;
359 }
360 
UnlockInstance(SEQINST * pInst)361 void UnlockInstance(SEQINST *pInst)
362 {
363     dassert(pInst != NULL);
364     dassert(pInst->hSeq != NULL);
365     dassert(pInst->pSequence != NULL);
366     gSysRes.Unlock(pInst->hSeq);
367     pInst->hSeq = NULL;
368     pInst->pSequence = NULL;
369     pInst->isPlaying = 0;
370 }
371 
seqSpawn(int nSeq,int nType,int nXIndex,int nCallbackID)372 void seqSpawn(int nSeq, int nType, int nXIndex, int nCallbackID)
373 {
374     SEQINST *pInst = GetInstance(nType, nXIndex);
375     if (!pInst) return;
376 
377     DICTNODE *hSeq = gSysRes.Lookup(nSeq, "SEQ");
378     if (!hSeq)
379         ThrowError("Missing sequence #%d", nSeq);
380 
381     int i = activeCount;
382     if (pInst->isPlaying)
383     {
384         if (hSeq == pInst->hSeq)
385             return;
386         UnlockInstance(pInst);
387         for (i = 0; i < activeCount; i++)
388         {
389             if (activeList[i].type == nType && activeList[i].xindex == nXIndex)
390                 break;
391         }
392         dassert(i < activeCount);
393     }
394     Seq *pSeq = (Seq*)gSysRes.Lock(hSeq);
395     if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0)
396         ThrowError("Invalid sequence %d", nSeq);
397     if ((pSeq->version & 0xff00) != 0x300)
398         ThrowError("Sequence %d is obsolete version", nSeq);
399     if ((pSeq->version & 0xff) == 0x00)
400     {
401         for (int i = 0; i < pSeq->nFrames; i++)
402             pSeq->frames[i].tile2 = 0;
403     }
404     pInst->isPlaying = 1;
405     pInst->hSeq = hSeq;
406     pInst->pSequence = pSeq;
407     pInst->nSeq = nSeq;
408     pInst->nCallbackID = nCallbackID;
409     pInst->timeCount = pSeq->ticksPerFrame;
410     pInst->frameIndex = 0;
411     if (i == activeCount)
412     {
413         dassert(activeCount < kMaxSequences);
414         activeList[activeCount].type = nType;
415         activeList[activeCount].xindex = nXIndex;
416         activeCount++;
417     }
418     pInst->Update(&activeList[i]);
419 }
420 
seqKill(int nType,int nXIndex)421 void seqKill(int nType, int nXIndex)
422 {
423     SEQINST *pInst = GetInstance(nType, nXIndex);
424     if (!pInst || !pInst->isPlaying)
425         return;
426     int i;
427     for (i = 0; i < activeCount; i++)
428     {
429         if (activeList[i].type == nType && activeList[i].xindex == nXIndex)
430             break;
431     }
432     dassert(i < activeCount);
433     activeCount--;
434     activeList[i] = activeList[activeCount];
435     pInst->isPlaying = 0;
436     UnlockInstance(pInst);
437 }
438 
seqKillAll(void)439 void seqKillAll(void)
440 {
441     for (int i = 0; i < kMaxXWalls; i++)
442     {
443         if (siWall[i].isPlaying)
444             UnlockInstance(&siWall[i]);
445         if (siMasked[i].isPlaying)
446             UnlockInstance(&siMasked[i]);
447     }
448     for (int i = 0; i < kMaxXSectors; i++)
449     {
450         if (siCeiling[i].isPlaying)
451             UnlockInstance(&siCeiling[i]);
452         if (siFloor[i].isPlaying)
453             UnlockInstance(&siFloor[i]);
454     }
455     for (int i = 0; i < kMaxXSprites; i++)
456     {
457         if (siSprite[i].isPlaying)
458             UnlockInstance(&siSprite[i]);
459     }
460     activeCount = 0;
461 }
462 
seqGetStatus(int nType,int nXIndex)463 int seqGetStatus(int nType, int nXIndex)
464 {
465     SEQINST *pInst = GetInstance(nType, nXIndex);
466     if (pInst && pInst->isPlaying)
467         return pInst->frameIndex;
468     return -1;
469 }
470 
seqGetID(int nType,int nXIndex)471 int seqGetID(int nType, int nXIndex)
472 {
473     SEQINST *pInst = GetInstance(nType, nXIndex);
474     if (pInst)
475         return pInst->nSeq;
476     return -1;
477 }
478 
seqProcess(int nTicks)479 void seqProcess(int nTicks)
480 {
481     for (int i = 0; i < activeCount; i++)
482     {
483         SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex);
484         Seq *pSeq = pInst->pSequence;
485         dassert(pInst->frameIndex < pSeq->nFrames);
486         pInst->timeCount -= nTicks;
487         while (pInst->timeCount < 0)
488         {
489             pInst->timeCount += pSeq->ticksPerFrame;
490             pInst->frameIndex++;
491             if (pInst->frameIndex == pSeq->nFrames)
492             {
493                 if (pSeq->flags & 1)
494                     pInst->frameIndex = 0;
495                 else
496                 {
497                     UnlockInstance(pInst);
498                     if (pSeq->flags & 2)
499                     {
500                         switch (activeList[i].type)
501                         {
502                         case 3:
503                         {
504                             int nXSprite = activeList[i].xindex;
505                             int nSprite = xsprite[nXSprite].reference;
506                             dassert(nSprite >= 0 && nSprite < kMaxSprites);
507                             evKill(nSprite, 3);
508                             if ((sprite[nSprite].flags & kHitagRespawn) && sprite[nSprite].inittype >= kDudeBase && sprite[nSprite].inittype < kDudeMax)
509                                 evPost(nSprite, 3, gGameOptions.nMonsterRespawnTime, kCallbackRespawn);
510                             else
511                                 DeleteSprite(nSprite);
512                             break;
513                         }
514                         case 4:
515                         {
516                             int nXWall = activeList[i].xindex;
517                             int nWall = xwall[nXWall].reference;
518                             dassert(nWall >= 0 && nWall < kMaxWalls);
519                             wall[nWall].cstat &= ~(8 + 16 + 32);
520                             if (wall[nWall].nextwall != -1)
521                                 wall[wall[nWall].nextwall].cstat &= ~(8 + 16 + 32);
522                             break;
523                         }
524                         }
525                     }
526                     activeList[i--] = activeList[--activeCount];
527                     break;
528                 }
529             }
530             pInst->Update(&activeList[i]);
531         }
532     }
533 }
534 
535 class SeqLoadSave : public LoadSave {
536     virtual void Load(void);
537     virtual void Save(void);
538 };
539 
Load(void)540 void SeqLoadSave::Load(void)
541 {
542     Read(&siWall, sizeof(siWall));
543     Read(&siMasked, sizeof(siMasked));
544     Read(&siCeiling, sizeof(siCeiling));
545     Read(&siFloor, sizeof(siFloor));
546     Read(&siSprite, sizeof(siSprite));
547     Read(&activeList, sizeof(activeList));
548     Read(&activeCount, sizeof(activeCount));
549     for (int i = 0; i < kMaxXWalls; i++)
550     {
551         siWall[i].hSeq = NULL;
552         siMasked[i].hSeq = NULL;
553         siWall[i].pSequence = NULL;
554         siMasked[i].pSequence = NULL;
555     }
556     for (int i = 0; i < kMaxXSectors; i++)
557     {
558         siCeiling[i].hSeq = NULL;
559         siFloor[i].hSeq = NULL;
560         siCeiling[i].pSequence = NULL;
561         siFloor[i].pSequence = NULL;
562     }
563     for (int i = 0; i < kMaxXSprites; i++)
564     {
565         siSprite[i].hSeq = NULL;
566         siSprite[i].pSequence = NULL;
567     }
568     for (int i = 0; i < activeCount; i++)
569     {
570         SEQINST *pInst = GetInstance(activeList[i].type, activeList[i].xindex);
571         if (pInst->isPlaying)
572         {
573             int nSeq = pInst->nSeq;
574             DICTNODE *hSeq = gSysRes.Lookup(nSeq, "SEQ");
575             if (!hSeq) {
576                 ThrowError("Missing sequence #%d", nSeq);
577                 continue;
578             }
579             Seq *pSeq = (Seq*)gSysRes.Lock(hSeq);
580             if (memcmp(pSeq->signature, "SEQ\x1a", 4) != 0)
581                 ThrowError("Invalid sequence %d", nSeq);
582             if ((pSeq->version & 0xff00) != 0x300)
583                 ThrowError("Sequence %d is obsolete version", nSeq);
584             pInst->hSeq = hSeq;
585             pInst->pSequence = pSeq;
586         }
587     }
588 }
589 
Save(void)590 void SeqLoadSave::Save(void)
591 {
592     Write(&siWall, sizeof(siWall));
593     Write(&siMasked, sizeof(siMasked));
594     Write(&siCeiling, sizeof(siCeiling));
595     Write(&siFloor, sizeof(siFloor));
596     Write(&siSprite, sizeof(siSprite));
597     Write(&activeList, sizeof(activeList));
598     Write(&activeCount, sizeof(activeCount));
599 }
600 
601 static SeqLoadSave *myLoadSave;
602 
SeqLoadSaveConstruct(void)603 void SeqLoadSaveConstruct(void)
604 {
605     myLoadSave = new SeqLoadSave();
606 }
607