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 = §or[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 = §or[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