1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 sirlemonhead, Nuke.YKT
5 
6 This file is part of PCExhumed.
7 
8 PCExhumed 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 
24 #include "mummy.h"
25 #include "sequence.h"
26 #include "move.h"
27 #include "map.h"
28 #include "sound.h"
29 #include "exhumed.h"
30 #include "random.h"
31 #include "trigdat.h"
32 #include "bullet.h"
33 #include "items.h"
34 #include "save.h"
35 #include <assert.h>
36 #include "engine.h"
37 
38 struct Mummy
39 {
40     short nHealth;
41     short B;
42     short nAction;
43     short nSprite;
44     short nTarget;
45     short F;
46     short G;
47     short H;
48 };
49 
50 static actionSeq ActionSeq[] = {
51     {8,  0},
52     {0,  0},
53     {16, 0},
54     {24, 0},
55     {32, 1},
56     {40, 1},
57     {48, 1},
58     {50, 0}
59 };
60 
61 short nMummies = -1;
62 Mummy MummyList[kMaxMummies];
63 
64 
InitMummy()65 void InitMummy()
66 {
67     nMummies = 0;
68 }
69 
BuildMummy(int nSprite,int x,int y,int z,int nSector,int nAngle)70 int BuildMummy(int nSprite, int x, int y, int z, int nSector, int nAngle)
71 {
72     if (nMummies >= kMaxMummies) {
73         return -1;
74     }
75 
76     short nMummy = nMummies++;
77 
78     if (nSprite == -1)
79     {
80         nSprite = insertsprite(nSector, 102);
81     }
82     else
83     {
84         x = sprite[nSprite].x;
85         y = sprite[nSprite].y;
86         z = sprite[nSprite].z;
87         nAngle = sprite[nSprite].ang;
88 
89         changespritestat(nSprite, 102);
90     }
91 
92     assert(nSprite >= 0 && nSprite < kMaxSprites);
93 
94     sprite[nSprite].x = x;
95     sprite[nSprite].y = y;
96     sprite[nSprite].z = z;
97     sprite[nSprite].cstat = 0x101;
98     sprite[nSprite].shade = -12;
99     sprite[nSprite].clipdist = 32;
100     sprite[nSprite].xvel = 0;
101     sprite[nSprite].yvel = 0;
102     sprite[nSprite].zvel = 0;
103     sprite[nSprite].xrepeat = 42;
104     sprite[nSprite].yrepeat = 42;
105     sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal;
106     sprite[nSprite].xoffset = 0;
107     sprite[nSprite].yoffset = 0;
108     sprite[nSprite].ang = nAngle;
109     sprite[nSprite].picnum = 1;
110     sprite[nSprite].hitag = 0;
111     sprite[nSprite].lotag = runlist_HeadRun() + 1;
112     sprite[nSprite].extra = -1;
113 
114 //  GrabTimeSlot(3);
115 
116     MummyList[nMummy].nAction = 0;
117     MummyList[nMummy].nHealth = 640;
118     MummyList[nMummy].B = 0;
119     MummyList[nMummy].nSprite = nSprite;
120     MummyList[nMummy].nTarget = -1;
121     MummyList[nMummy].F = nMummy;
122     MummyList[nMummy].G = 0;
123 
124     sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nMummy | 0xE0000);
125 
126     MummyList[nMummy].H = runlist_AddRunRec(NewRun, nMummy | 0xE0000);
127 
128     nCreaturesLeft++;
129 
130     return (nMummy | 0xE0000);
131 }
132 
CheckMummyRevive(short nMummy)133 void CheckMummyRevive(short nMummy)
134 {
135     short nSprite = MummyList[nMummy].nSprite;
136 
137     for (int i = 0; i < nMummies; i++)
138     {
139         if (i != nMummy)
140         {
141             short nSprite2 = MummyList[i].nSprite;
142             if (sprite[nSprite2].statnum != 102) {
143                 continue;
144             }
145 
146             if (MummyList[i].nAction != 5) {
147                 continue;
148             }
149 
150             int x = klabs(sprite[nSprite2].x - sprite[nSprite].x) >> 8;
151             int y = klabs(sprite[nSprite2].y - sprite[nSprite].y) >> 8;
152 
153             if (x <= 20 && y <= 20)
154             {
155                 if (cansee(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - 8192, sprite[nSprite].sectnum,
156                           sprite[nSprite2].x, sprite[nSprite2].y, sprite[nSprite2].z - 8192, sprite[nSprite2].sectnum))
157                 {
158                     sprite[nSprite2].cstat = 0;
159                     MummyList[i].nAction = 6;
160                     MummyList[i].B = 0;
161                 }
162             }
163         }
164     }
165 }
166 
FuncMummy(int a,int nDamage,int nRun)167 void FuncMummy(int a, int nDamage, int nRun)
168 {
169     short nMummy = RunData[nRun].nVal;
170     assert(nMummy >= 0 && nMummy < kMaxMummies);
171 
172     short nTarget = UpdateEnemy(&MummyList[nMummy].nTarget);
173 
174     short nSprite = MummyList[nMummy].nSprite;
175     short nAction = MummyList[nMummy].nAction;
176 
177     int nMessage = a & kMessageMask;
178 
179     switch (nMessage)
180     {
181         default:
182         {
183             DebugOut("unknown msg %d for Mummy\n", nMessage);
184             break;
185         }
186 
187         case 0x20000:
188         {
189             Gravity(nSprite);
190 
191             int nSeq = SeqOffsets[kSeqMummy] + ActionSeq[nAction].a;
192 
193             sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, MummyList[nMummy].B);
194 
195             short nFrame = SeqBase[nSeq] + MummyList[nMummy].B;
196             short nFrameFlag = FrameFlag[nFrame];
197 
198             seq_MoveSequence(nSprite, nSeq, MummyList[nMummy].B);
199 
200             bool bVal = false;
201 
202             MummyList[nMummy].B++;
203             if (MummyList[nMummy].B >= SeqSize[nSeq])
204             {
205                 MummyList[nMummy].B = 0;
206 
207                 bVal = true;
208             }
209 
210             if (nTarget != -1 && nAction < 4)
211             {
212                 if ((!sprite[nTarget].cstat) && nAction)
213                 {
214                     MummyList[nMummy].nAction = 0;
215                     MummyList[nMummy].B = 0;
216                     sprite[nSprite].xvel = 0;
217                     sprite[nSprite].yvel = 0;
218                 }
219             }
220 
221             int nMov = MoveCreatureWithCaution(nSprite);
222 
223             if (nAction > 7)
224                 return;
225 
226             switch (nAction)
227             {
228                 case 0:
229                 {
230                     if ((MummyList[nMummy].F & 0x1F) == (totalmoves & 0x1F))
231                     {
232                         sprite[nSprite].cstat = 0x101;
233 
234                         if (nTarget < 0)
235                         {
236                             int nTarget = FindPlayer(nSprite, 100);
237                             if (nTarget >= 0)
238                             {
239                                 D3PlayFX(StaticSound[kSoundMummyChant0], nSprite);
240                                 MummyList[nMummy].B = 0;
241                                 MummyList[nMummy].nTarget = nTarget;
242                                 MummyList[nMummy].nAction = 1;
243                                 MummyList[nMummy].G = 90;
244 
245                                 sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2;
246                                 sprite[nSprite].yvel = sintable[sprite[nSprite].ang] >> 2; // NOTE no angle masking in original code
247                             }
248                         }
249                     }
250                     return;
251                 }
252 
253                 case 1:
254                 {
255                     if (MummyList[nMummy].G > 0)
256                     {
257                         MummyList[nMummy].G--;
258                     }
259 
260                     if ((MummyList[nMummy].F & 0x1F) == (totalmoves & 0x1F))
261                     {
262                         sprite[nSprite].cstat = 0x101;
263 
264                         PlotCourseToSprite(nSprite, nTarget);
265 
266                         if (MummyList[nMummy].nAction == 1)
267                         {
268                             if (RandomBit())
269                             {
270                                 if (cansee(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - GetSpriteHeight(nSprite), sprite[nSprite].sectnum,
271                                     sprite[nTarget].x, sprite[nTarget].y, sprite[nTarget].z - GetSpriteHeight(nTarget), sprite[nTarget].sectnum))
272                                 {
273                                     MummyList[nMummy].nAction = 3;
274                                     MummyList[nMummy].B = 0;
275 
276                                     sprite[nSprite].xvel = 0;
277                                     sprite[nSprite].yvel = 0;
278                                     return;
279                                 }
280                             }
281                         }
282                     }
283 
284                     // loc_2B5A8
285                     if (!MummyList[nMummy].B)
286                     {
287                         sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 1;
288                         sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1;
289                     }
290 
291                     if (sprite[nSprite].xvel || sprite[nSprite].yvel)
292                     {
293                         if (sprite[nSprite].xvel > 0)
294                         {
295                             sprite[nSprite].xvel -= 1024;
296                             if (sprite[nSprite].xvel < 0) {
297                                 sprite[nSprite].xvel = 0;
298                             }
299                         }
300                         else if (sprite[nSprite].xvel < 0)
301                         {
302                             sprite[nSprite].xvel += 1024;
303                             if (sprite[nSprite].xvel > 0) {
304                                 sprite[nSprite].xvel = 0;
305                             }
306                         }
307 
308                         if (sprite[nSprite].yvel > 0)
309                         {
310                             sprite[nSprite].yvel -= 1024;
311                             if (sprite[nSprite].yvel < 0) {
312                                 sprite[nSprite].yvel = 0;
313                             }
314                         }
315                         else if (sprite[nSprite].yvel < 0)
316                         {
317                             sprite[nSprite].yvel += 1024;
318                             if (sprite[nSprite].yvel > 0) {
319                                 sprite[nSprite].yvel = 0;
320                             }
321                         }
322                     }
323 
324                     if (nMov)
325                     {
326                         switch (nMov & 0xC000)
327                         {
328                             case 0x8000:
329                             {
330                                 sprite[nSprite].ang = (sprite[nSprite].ang + ((RandomWord() & 0x3FF) + 1024)) & kAngleMask;
331                                 sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2;
332                                 sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2;
333                                 return;
334                             }
335 
336                             case 0xC000:
337                             {
338                                 if ((nMov & 0x3FFF) == nTarget)
339                                 {
340                                     int nAngle = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y);
341                                     if (AngleDiff(sprite[nSprite].ang, nAngle) < 64)
342                                     {
343                                         MummyList[nMummy].nAction = 2;
344                                         MummyList[nMummy].B = 0;
345 
346                                         sprite[nSprite].xvel = 0;
347                                         sprite[nSprite].yvel = 0;
348                                     }
349                                 }
350                                 return;
351                             }
352                         }
353                     }
354 
355                     break;
356                 }
357 
358                 case 2:
359                 {
360                     if (nTarget == -1)
361                     {
362                         MummyList[nMummy].nAction = 0;
363                         MummyList[nMummy].B = 0;
364                     }
365                     else
366                     {
367                         if (PlotCourseToSprite(nSprite, nTarget) >= 1024)
368                         {
369                             MummyList[nMummy].nAction = 1;
370                             MummyList[nMummy].B = 0;
371                         }
372                         else if (nFrameFlag & 0x80)
373                         {
374                             runlist_DamageEnemy(nTarget, nSprite, 5);
375                         }
376                     }
377                     return;
378                 }
379 
380                 case 3:
381                 {
382                     if (bVal)
383                     {
384                         MummyList[nMummy].B = 0;
385                         MummyList[nMummy].nAction = 0;
386                         MummyList[nMummy].G = 100;
387                         MummyList[nMummy].nTarget = -1;
388                         return;
389                     }
390                     else if (nFrameFlag & 0x80)
391                     {
392                         SetQuake(nSprite, 100);
393 
394                         // low 16 bits of returned var contains the sprite index, the high 16 the bullet number
395                         int nBullet = BuildBullet(nSprite, 9, 0, 0, -15360, sprite[nSprite].ang, nTarget + 10000, 1);
396                         CheckMummyRevive(nMummy);
397 
398                         if (nBullet > -1)
399                         {
400                             if (!RandomSize(3))
401                             {
402                                 // FIXME CHECKME - nBullet & 0xFFFF can be -1. Original code doesn't handle this??
403 
404                                 SetBulletEnemy(nBullet >> 16, nTarget); // isolate the bullet number (shift off the sprite index)
405                                 sprite[nBullet & 0xFFFF].pal = 5;
406                             }
407                         }
408                     }
409                     return;
410                 }
411 
412                 case 4:
413                 {
414                     if (bVal)
415                     {
416                         MummyList[nMummy].B = 0;
417                         MummyList[nMummy].nAction = 5;
418                     }
419                     return;
420                 }
421 
422                 case 5:
423                 {
424                     MummyList[nMummy].B = 0;
425                     return;
426                 }
427 
428                 case 6:
429                 {
430                     if (bVal)
431                     {
432                         sprite[nSprite].cstat = 0x101;
433 
434                         MummyList[nMummy].nAction = 0;
435                         MummyList[nMummy].nHealth = 300;
436                         MummyList[nMummy].nTarget = -1;
437 
438                         nCreaturesLeft++;
439                     }
440                     return;
441                 }
442 
443                 case 7:
444                 {
445                     if (nMov & 0x20000)
446                     {
447                         sprite[nSprite].xvel >>= 1;
448                         sprite[nSprite].yvel >>= 1;
449                     }
450 
451                     if (bVal)
452                     {
453                         sprite[nSprite].xvel = 0;
454                         sprite[nSprite].yvel = 0;
455                         sprite[nSprite].cstat = 0x101;
456 
457                         MummyList[nMummy].nAction = 0;
458                         MummyList[nMummy].B = 0;
459                         MummyList[nMummy].nTarget = -1;
460                     }
461 
462                     return;
463                 }
464             }
465 
466             return;
467         }
468 
469         case 0x90000:
470         {
471             seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqMummy] + ActionSeq[nAction].a, MummyList[nMummy].B, ActionSeq[nAction].b);
472             return;
473         }
474 
475         case 0xA0000:
476         {
477             if (MummyList[nMummy].nHealth <= 0)
478                 return;
479 
480             nDamage = runlist_CheckRadialDamage(nSprite);
481             // fall through to 0x80000
482             fallthrough__;
483         }
484         case 0x80000:
485         {
486             if (nDamage <= 0)
487                 return;
488 
489             if (MummyList[nMummy].nHealth <= 0) {
490                 return;
491             }
492 
493             MummyList[nMummy].nHealth -= nDamage;
494 
495             if (MummyList[nMummy].nHealth <= 0)
496             {
497                 MummyList[nMummy].nHealth = 0;
498                 sprite[nSprite].cstat &= 0xFEFE;
499                 nCreaturesLeft--;
500 
501                 DropMagic(nSprite);
502 
503                 MummyList[nMummy].B = 0;
504                 MummyList[nMummy].nAction = 4;
505 
506                 sprite[nSprite].xvel = 0;
507                 sprite[nSprite].yvel = 0;
508                 sprite[nSprite].zvel = 0;
509                 sprite[nSprite].z = sector[sprite[nSprite].sectnum].floorz;
510             }
511             else
512             {
513                 if (!RandomSize(2))
514                 {
515                     MummyList[nMummy].nAction = 7;
516                     MummyList[nMummy].B = 0;
517 
518                     sprite[nSprite].xvel = 0;
519                     sprite[nSprite].yvel = 0;
520                 }
521             }
522 
523             return;
524         }
525     }
526 }
527 
528 class MummyLoadSave : public LoadSave
529 {
530 public:
531     virtual void Load();
532     virtual void Save();
533 };
534 
Load()535 void MummyLoadSave::Load()
536 {
537     Read(&nMummies, sizeof(nMummies));
538     Read(&MummyList, sizeof(MummyList[0]) * nMummies);
539 }
540 
Save()541 void MummyLoadSave::Save()
542 {
543     Write(&nMummies, sizeof(nMummies));
544     Write(&MummyList, sizeof(MummyList[0]) * nMummies);
545 }
546 
547 static MummyLoadSave* myLoadSave;
548 
MummyLoadSaveConstruct()549 void MummyLoadSaveConstruct()
550 {
551     myLoadSave = new MummyLoadSave();
552 }