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 "engine.h"
25 #include "player.h"
26 #include "anims.h"
27 #include "status.h"
28 #include "exhumed.h"
29 #include "sequence.h"
30 #include "init.h"
31 #include "names.h"
32 #include "items.h"
33 #include "view.h"
34 #include "trigdat.h"
35 #include "light.h"
36 #include "save.h"
37 #include <string.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include "typedefs.h"
42 
43 //short nMaskY;
44 //short statusmask[MAXXDIM];
45 
46 static short nAnimsFree = 0;
47 
48 short message_timer = 0;
49 char message_text[80];
50 int magicperline;
51 int airperline;
52 int healthperline;
53 int nAirFrames;
54 int nCounter;
55 int nCounterDest;
56 
57 short nStatusSeqOffset;
58 short nItemFrames;
59 
60 int laststatusx;
61 int laststatusy;
62 
63 int16_t nItemSeq;
64 short nDigit[3];
65 
66 short nMagicFrames;
67 short nHealthLevel;
68 short nItemFrame;
69 short nMeterRange;
70 short nMagicLevel;
71 short nHealthFrame;
72 short nMagicFrame;
73 
74 short statusx;
75 short statusy;
76 short nHealthFrames;
77 short airframe;
78 
79 int16_t nFirstAnim;
80 int16_t nLastAnim;
81 short nItemAltSeq;
82 
83 short airpages = 0;
84 short ammodelay = 3;
85 short nCounterBullet = -1;
86 
87 struct statusAnim
88 {
89     int16_t s1;
90     int16_t s2;
91 //    int16_t nPage;
92     int8_t nPrevAnim;
93     int8_t nNextAnim;
94 };
95 
96 #define kMaxStatusAnims		50
97 
98 statusAnim StatusAnim[kMaxStatusAnims];
99 uint8_t StatusAnimsFree[kMaxStatusAnims];
100 uint8_t StatusAnimFlags[kMaxStatusAnims];
101 
102 short nItemSeqOffset[] = {91, 72, 76, 79, 68, 87, 83};
103 
104 short word_9AD54[kMaxPlayers] = {0, 0, 0, 0, 0, 0, 0, 0};
105 int dword_9AD64[kMaxPlayers] = {0, 0, 0, 0, 0, 0, 0, 0};
106 
107 void SetCounterDigits();
108 void SetItemSeq();
109 void SetItemSeq2(int nSeqOffset);
110 void DestroyStatusAnim(short nAnim);
111 
112 
BuildStatusAnim(int val,int nFlags)113 int BuildStatusAnim(int val, int nFlags)
114 {
115     // destroy this anim if it already exists
116     for (int i = nFirstAnim; i >= 0; i = StatusAnim[i].nPrevAnim)
117     {
118         if (StatusAnim[i].s1 == val) {
119             DestroyStatusAnim(i);
120             break;
121         }
122     }
123 
124     if (nAnimsFree <= 0) {
125         return -1;
126     }
127 
128     nAnimsFree--;
129 
130     uint8_t nStatusAnim = StatusAnimsFree[nAnimsFree];
131 
132     StatusAnim[nStatusAnim].nPrevAnim = -1;
133     StatusAnim[nStatusAnim].nNextAnim = nLastAnim;
134 
135     if (nLastAnim < 0) {
136         nFirstAnim = nStatusAnim;
137     }
138     else {
139         StatusAnim[nLastAnim].nPrevAnim = nStatusAnim;
140     }
141 
142     nLastAnim = nStatusAnim;
143 
144     StatusAnim[nStatusAnim].s1 = val;
145     StatusAnim[nStatusAnim].s2 = 0;
146     StatusAnimFlags[nStatusAnim] = nFlags;
147 //    StatusAnim[nStatusAnim].nPage = numpages;
148     return nStatusAnim;
149 }
150 
RefreshStatus()151 void RefreshStatus()
152 {
153     short nLives = nPlayerLives[nLocalPlayer];
154     if (nLives < 0 || nLives > kMaxPlayerLives) {
155         bail2dos("illegal value for nPlayerLives #%d\n", nLocalPlayer);
156     }
157 
158     // draws the red dots that indicate the lives amount
159     BuildStatusAnim(145 + (2 * nLives), 0);
160 
161     uint16_t nKeys = PlayerList[nLocalPlayer].keys;
162 
163     int val = 37;
164 
165     for (int i = 0; i < 4; i++)
166     {
167         if (nKeys & 0x1000) {
168             BuildStatusAnim(val, 0);
169         }
170 
171         nKeys >>= 1;
172         val += 2;
173     }
174 
175     SetPlayerItem(nLocalPlayer, nPlayerItem[nLocalPlayer]);
176     SetHealthFrame(0);
177     SetMagicFrame();
178     SetAirFrame();
179 }
180 
InitStatus()181 void InitStatus()
182 {
183     nStatusSeqOffset = SeqOffsets[kSeqStatus];
184     nHealthFrames = SeqSize[nStatusSeqOffset + 1];
185     int nPicNum   = seq_GetSeqPicnum(kSeqStatus, 1, 0);
186     nMagicFrames  = SeqSize[nStatusSeqOffset + 129];
187     nHealthFrame  = 0;
188     nMagicFrame   = 0;
189     nHealthLevel  = 0;
190     nMagicLevel   = 0;
191     nMeterRange   = tilesiz[nPicNum].y;
192     magicperline  = 1000 / nMeterRange;
193     healthperline = 800 / nMeterRange;
194     nAirFrames = SeqSize[nStatusSeqOffset + 133];
195     airperline = 100 / nAirFrames;
196     nCounter   = 0;
197     nCounterDest = 0;
198 
199     memset(nDigit, 0, sizeof(nDigit));
200 
201     SetCounter(0);
202     SetHealthFrame(0);
203     SetMagicFrame();
204 
205     for (int i = 0; i < kMaxStatusAnims; i++) {
206         StatusAnimsFree[i] = i;
207     }
208 
209     nLastAnim  = -1;
210     nFirstAnim = -1;
211     nItemSeq   = -1;
212     nAnimsFree = kMaxStatusAnims;
213     statusx    = xdim - 320;
214     textpages  = 0;
215     message_timer = 0;
216     statusy = ydim - 200;
217 }
218 
MoveStatusAnims()219 void MoveStatusAnims()
220 {
221     for (int i = nFirstAnim; i >= 0; i = StatusAnim[i].nPrevAnim)
222     {
223         seq_MoveSequence(-1, nStatusSeqOffset + StatusAnim[i].s1, StatusAnim[i].s2);
224 
225         StatusAnim[i].s2++;
226 
227         short nSize = SeqSize[nStatusSeqOffset + StatusAnim[i].s1];
228 
229         if (StatusAnim[i].s2 >= nSize)
230         {
231             if (StatusAnimFlags[i] & 0x10) {
232                 StatusAnim[i].s2 = 0;
233             }
234             else {
235                 StatusAnim[i].s2 = nSize - 1; // restart it
236             }
237         }
238     }
239 }
240 
DestroyStatusAnim(short nAnim)241 void DestroyStatusAnim(short nAnim)
242 {
243     int8_t nPrev = StatusAnim[nAnim].nPrevAnim;
244     int8_t nNext = StatusAnim[nAnim].nNextAnim;
245 
246     if (nNext >= 0) {
247         StatusAnim[nNext].nPrevAnim = nPrev;
248     }
249 
250     if (nPrev >= 0) {
251         StatusAnim[nPrev].nNextAnim = nNext;
252     }
253 
254     if (nAnim == nFirstAnim) {
255         nFirstAnim = nPrev;
256     }
257 
258     if (nAnim == nLastAnim) {
259         nLastAnim = nNext;
260     }
261 
262     StatusAnimsFree[nAnimsFree] = (uint8_t)nAnim;
263     nAnimsFree++;
264 }
265 
DrawStatusAnims()266 void DrawStatusAnims()
267 {
268     for (int i = nFirstAnim; i >= 0; i = StatusAnim[i].nPrevAnim)
269     {
270         int nSequence = nStatusSeqOffset + StatusAnim[i].s1;
271 
272         seq_DrawStatusSequence(nSequence, StatusAnim[i].s2, 0);
273 
274 /*
275         if (StatusAnim[nAnim].s2 >= (SeqSize[nSequence] - 1))
276         {
277             if (!(StatusAnimFlags[nAnim] & 0x10))
278             {
279                 StatusAnim[nAnim].nPage--;
280                 if (StatusAnim[nAnim].nPage <= 0) {
281                     DestroyStatusAnim(nAnim);
282                 }
283             }
284         }
285 */
286     }
287 }
288 
SetMagicFrame()289 void SetMagicFrame()
290 {
291     nMagicLevel = (1000 - PlayerList[nLocalPlayer].nMagic) / magicperline;
292 
293     if (nMagicLevel >= nMeterRange) {
294         nMagicLevel = nMeterRange - 1;
295     }
296 
297     if (nMagicLevel < 0) {
298         nMagicLevel = 0;
299     }
300 
301     SetItemSeq();
302 }
303 
SetHealthFrame(short nVal)304 void SetHealthFrame(short nVal)
305 {
306     nHealthLevel = (800 - PlayerList[nLocalPlayer].nHealth) / healthperline;
307 
308     if (nHealthLevel >= nMeterRange ) {
309         nHealthLevel = nMeterRange - 1;
310     }
311 
312     if (nHealthLevel < 0) {
313         nHealthLevel = 0;
314     }
315 
316     if (nVal < 0) {
317         BuildStatusAnim(4, 0);
318     }
319 }
320 
SetAirFrame()321 void SetAirFrame()
322 {
323     airframe = PlayerList[nLocalPlayer].nAir / airperline;
324 
325     if (airframe >= nAirFrames)
326     {
327         airframe = nAirFrames - 1;
328     }
329     else if (airframe < 0)
330     {
331         airframe = 0;
332     }
333 }
334 
SetCounter(short nVal)335 void SetCounter(short nVal)
336 {
337     if (nVal <= 999)
338     {
339         if (nVal < 0) {
340             nVal = 0;
341         }
342     }
343     else {
344         nVal = 999;
345     }
346 
347     nCounterDest = nVal;
348 }
349 
SetCounterImmediate(short nVal)350 void SetCounterImmediate(short nVal)
351 {
352     SetCounter(nVal);
353     nCounter = nCounterDest;
354 
355     SetCounterDigits();
356 }
357 
SetCounterDigits()358 void SetCounterDigits()
359 {
360     nDigit[2] = 3 * (nCounter / 100 % 10);
361     nDigit[1] = 3 * (nCounter / 10 % 10);
362     nDigit[0] = 3 * (nCounter % 10);
363 }
364 
SetItemSeq()365 void SetItemSeq()
366 {
367     short nItem = nPlayerItem[nLocalPlayer];
368     if (nItem < 0)
369     {
370         nItemSeq = -1;
371         return;
372     }
373 
374     short nOffset = nItemSeqOffset[nItem];
375 
376     SetItemSeq2(nOffset);
377 }
378 
SetItemSeq2(int nSeqOffset)379 void SetItemSeq2(int nSeqOffset)
380 {
381     short nItem = nPlayerItem[nLocalPlayer];
382 
383     if (nItemMagic[nItem] <= PlayerList[nLocalPlayer].nMagic) {
384         nItemAltSeq = 0;
385     }
386     else {
387         nItemAltSeq = 2;
388     }
389 
390     nItemFrame = 0;
391     nItemSeq = nSeqOffset + nItemAltSeq;
392     nItemFrames = SeqSize[nItemSeq + nStatusSeqOffset];
393 }
394 
SetPlayerItem(short nPlayer,short nItem)395 void SetPlayerItem(short nPlayer, short nItem)
396 {
397     nPlayerItem[nPlayer] = nItem;
398 
399     if (nPlayer == nLocalPlayer)
400     {
401         SetItemSeq();
402         if (nItem >= 0) {
403             BuildStatusAnim(156 + (2 * PlayerList[nLocalPlayer].items[nItem]), 0);
404         }
405     }
406 }
407 
SetNextItem(int nPlayer)408 void SetNextItem(int nPlayer)
409 {
410     short nItem = nPlayerItem[nPlayer];
411 
412     int i;
413 
414     for (i = 6; i > 0; i--)
415     {
416         nItem++;
417         if (nItem == 6)
418             nItem = 0;
419 
420         if (PlayerList[nPlayer].items[nItem] != 0)
421             break;
422     }
423 
424     if (i > 0) {
425         SetPlayerItem(nPlayer, nItem);
426     }
427 }
428 
SetPrevItem(int nPlayer)429 void SetPrevItem(int nPlayer)
430 {
431     if (nPlayerItem[nPlayer] == -1)
432         return;
433 
434     int nItem = nPlayerItem[nPlayer];
435 
436     int i;
437 
438     for (i = 6; i > 0; i--)
439     {
440         nItem--;
441         if (nItem < 0)
442             nItem = 5;
443 
444         if (PlayerList[nPlayer].items[nItem] != 0)
445             break;
446     }
447 
448     if (i > 0) {
449         SetPlayerItem(nPlayer, nItem);
450     }
451 }
452 
MoveStatus()453 void MoveStatus()
454 {
455     if (nItemSeq >= 0)
456     {
457         nItemFrame++;
458 
459         if (nItemFrame >= nItemFrames)
460         {
461             if (nItemSeq == 67) {
462                 SetItemSeq();
463             }
464             else
465             {
466                 nItemSeq -= nItemAltSeq;
467 
468                 if (nItemAltSeq || totalmoves & 0x1F)
469                 {
470                     if (nItemSeq < 2) {
471                         nItemAltSeq = 0;
472                     }
473                 }
474                 else
475                 {
476                     nItemAltSeq = 1;
477                 }
478 
479                 nItemFrame = 0;
480                 nItemSeq += nItemAltSeq;
481                 nItemFrames = SeqSize[nStatusSeqOffset + nItemSeq];
482             }
483         }
484     }
485 
486     if (message_timer)
487     {
488         message_timer -= 4;
489         if (message_timer <= 0)
490         {
491             if (screensize > 0) {
492                 textpages = numpages;
493             }
494 
495             message_timer = 0;
496         }
497     }
498 
499     nHealthFrame++;
500     if (nHealthFrame >= nHealthFrames) {
501         nHealthFrame = 0;
502     }
503 
504     nMagicFrame++;
505     if (nMagicFrame >= nMagicFrames) {
506         nMagicFrame = 0;
507     }
508 
509     MoveStatusAnims();
510 
511     if (nCounter == nCounterDest)
512     {
513         nCounter = nCounterDest;
514         ammodelay = 3;
515         return;
516     }
517     else
518     {
519         ammodelay--;
520         if (ammodelay > 0) {
521             return;
522         }
523     }
524 
525     int eax = nCounterDest - nCounter;
526 
527     if (eax <= 0)
528     {
529         if (eax >= -30)
530         {
531             for (int i = 0; i < 3; i++)
532             {
533                 nDigit[i]--;
534 
535                 if (nDigit[i] < 0)
536                 {
537                     nDigit[i] += 30;
538                 }
539 
540                 if (nDigit[i] < 27) {
541                     break;
542                 }
543             }
544         }
545         else
546         {
547             nCounter += (nCounterDest - nCounter) >> 1;
548             SetCounterDigits();
549             return;
550         }
551     }
552     else
553     {
554         if (eax <= 30)
555         {
556             for (int i = 0; i < 3; i++)
557             {
558                 nDigit[i]++;
559 
560                 if (nDigit[i] <= 27) {
561                     break;
562                 }
563 
564                 if (nDigit[i] >= 30) {
565                     nDigit[i] -= 30;
566                 }
567             }
568         }
569         else
570         {
571             nCounter += (nCounterDest - nCounter) >> 1;
572             SetCounterDigits();
573             return;
574         }
575     }
576 
577     if (!(nDigit[0] % 3)) {
578         nCounter = nDigit[0] / 3 + 100 * (nDigit[2] / 3) + 10 * (nDigit[1] / 3);
579     }
580 
581     eax = nCounterDest - nCounter;
582     if (eax < 0) {
583         eax = -eax;
584     }
585 
586     ammodelay = 4 - (eax >> 1);
587     if (ammodelay < 1) {
588         ammodelay = 1;
589     }
590 }
591 
UnMaskStatus()592 void UnMaskStatus()
593 {
594 #if 0
595     for (int i = 0; i < xdim; i++) {
596         startdmost[i] = ydim;
597     }
598 #endif
599 }
600 
MaskStatus()601 void MaskStatus()
602 {
603 #if 0
604     for (int i = 0; i < xdim; i++)
605     {
606         short bx = startdmost[i];
607         short cx = statusmask[i];
608 
609         if (bx > cx) {
610             startdmost[i] = cx;
611         }
612     }
613 #endif
614 }
615 
LoadStatus()616 void LoadStatus()
617 {
618 #if 0
619     int i;
620     short nSize;
621     short tmp;
622     short buffer[1024];
623 //	memset(buffer, 0, sizeof(buffer)); // bjd - added by me
624 
625     for (i = 0; i < xdim; i++) {
626         statusmask[i] = ydim;
627     }
628 
629     nMaskY = ydim;
630 
631     int hStatus = kopen4load("status.msk", 1);
632     if (!hStatus) {
633         return;
634     }
635 
636     kread(hStatus, &nSize, sizeof(nSize));
637 
638     int nCount = nSize >> 1;
639 
640     kread(hStatus, &tmp, sizeof(tmp));
641     kread(hStatus, buffer, nSize);
642 
643     kclose(hStatus);
644 
645     short *pStatusMask = statusmask;
646 
647     for (i = 0; i < nCount; i++)
648     {
649         int v8 = ydim - ((ydim * buffer[i]) / 200);
650         *pStatusMask++ = ydim - v8;
651 
652         if (bHiRes) {
653             *pStatusMask++ = ydim - v8;
654         }
655 
656         if (ydim - v8 < nMaskY) {
657             nMaskY = ydim - v8;
658         }
659     }
660 #endif
661 }
662 
ClearStatusMessage()663 void ClearStatusMessage()
664 {
665     message_timer = 0;
666     message_text[0] = '\0';
667 }
668 
StatusMessage(int messageTime,const char * fmt,...)669 void StatusMessage(int messageTime, const char *fmt, ...)
670 {
671     message_timer = messageTime;
672 
673     va_list args;
674     va_start(args, fmt);
675 
676     vsprintf(message_text, fmt, args);
677 
678     if (screensize > 0) {
679         textpages = numpages;
680     }
681 }
682 
DrawSnakeCamStatus()683 void DrawSnakeCamStatus()
684 {
685     printext(0, 0, "S E R P E N T   C A M", kTileFont);
686 }
687 
DrawStatus()688 void DrawStatus()
689 {
690     char numberBuf[10] = {0};
691     char stringBuf[20] = {0};
692     char coordBuf[50] = {0}; // not sure of the size for this?
693 
694     if (!bFullScreen && nNetTime)
695     {
696         // bjd - commenting out this check seems to fix the black status bar at 320x200 resolution
697 //		if (bHiRes) {
698             NoClip();
699 //		}
700 
701         // draw the main bar itself
702         seq_DrawStatusSequence(nStatusSeqOffset, 0, 0);
703 
704         seq_DrawStatusSequence(nStatusSeqOffset + 128, 0, 0);
705         seq_DrawStatusSequence(nStatusSeqOffset + 127, 0, 0);
706         seq_DrawStatusSequence(nStatusSeqOffset + 1, nHealthFrame, nHealthLevel);
707         seq_DrawStatusSequence(nStatusSeqOffset + 129, nMagicFrame, nMagicLevel);
708         seq_DrawStatusSequence(nStatusSeqOffset + 125, 0, 0); // draw ankh on health pool
709         seq_DrawStatusSequence(nStatusSeqOffset + 130, 0, 0); // draw health pool frame (top)
710         seq_DrawStatusSequence(nStatusSeqOffset + 131, 0, 0); // magic pool frame (bottom)
711 
712         if (nItemSeq >= 0) {
713             seq_DrawStatusSequence(nItemSeq + nStatusSeqOffset, nItemFrame, 0);
714         }
715 
716         // draws health level dots, animates breathing lungs and other things
717         DrawStatusAnims();
718 
719         // draw the blue air level meter when underwater (but not responsible for animating the breathing lungs otherwise)
720         if (airpages)
721         {
722             seq_DrawStatusSequence(nStatusSeqOffset + 133, airframe, 0);
723             // airpages--;
724         }
725 
726         // draw compass
727         seq_DrawStatusSequence(nStatusSeqOffset + 35, ((inita + 128) & kAngleMask) >> 8, 0);
728 
729         /*
730         if (bCoordinates)
731         {
732             sprintf(numberBuf, "%i", lastfps);
733             // char *cFPS = itoa(lastfps, numberBuf, 10);
734             printext(xdim - 20, nViewTop, numberBuf, kTile159, -1);
735         }
736         */
737 
738         // draw ammo count
739         seq_DrawStatusSequence(nStatusSeqOffset + 44, nDigit[2], 0);
740         seq_DrawStatusSequence(nStatusSeqOffset + 45, nDigit[1], 0);
741         seq_DrawStatusSequence(nStatusSeqOffset + 46, nDigit[0], 0);
742 
743         // bjd - commenting out this check seems to fix the black status bar at 320x200 resolution
744 //		if (bHiRes) {
745             Clip();
746 //		}
747     }
748 
749     if (nNetPlayerCount)
750     {
751         NoClip();
752 
753         int shade;
754 
755         if ((int)totalclock / kTimerTicks & 1) {
756             shade = -100;
757         }
758         else {
759             shade = 127;
760         }
761 
762         int nTile = kTile3593;
763 
764         int x = 320 / (nTotalPlayers + 1);
765 
766         for (int i = 0; i < nTotalPlayers; i++)
767         {
768             int nScore = nPlayerScore[i];
769             if (word_9AD54[i] == nScore)
770             {
771                 int v9 = dword_9AD64[i];
772                 if (v9 && v9 <= (int)totalclock) {
773                     dword_9AD64[i] = 0;
774                 }
775             }
776             else
777             {
778                 word_9AD54[i] = nScore;
779                 dword_9AD64[i] = (int)totalclock + 30;
780             }
781 
782             overwritesprite(x, 7, nTile, 0, 3, kPalNormal);
783 
784             if (i != nLocalPlayer) {
785                 shade = -100;
786             }
787 
788             sprintf(stringBuf, "%d", nPlayerScore[i]);
789             int nStringLen = MyGetStringWidth(stringBuf);
790 
791             myprintext(x - (nStringLen / 2), 4, stringBuf, shade);
792 
793             x *= 2;
794             nTile++;
795         }
796 
797         if (nNetTime >= 0)
798         {
799             int y = nViewTop;
800 
801             if (nNetTime)
802             {
803                 int v12 = (nNetTime + 29) / 30 % 60;
804                 int v13 = (nNetTime + 29) / 1800;
805                 nNetTime += 29;
806 
807                 sprintf(stringBuf, "%d.%02d", v13, v12);
808 
809                 if (bHiRes) {
810                     y = nViewTop / 2;
811                 }
812 
813                 if (nViewTop <= 0) {
814                     y += 20;
815                 }
816                 else {
817                     y += 15;
818                 }
819 
820                 nNetTime -= 29;
821             }
822             else
823             {
824                 y = 100;
825                 strcpy(stringBuf, "GAME OVER");
826             }
827 
828             int nLenString = MyGetStringWidth(stringBuf);
829             myprintext((320 - nLenString) / 2, y, stringBuf, 0);
830         }
831 
832         Clip();
833     }
834 
835     if (bCoordinates)
836     {
837         int nSprite = PlayerList[nLocalPlayer].nSprite;
838 
839         int x = (nViewLeft + nViewRight) / 2;
840 
841         sprintf(coordBuf, "X %d", (int)sprite[nSprite].x);
842         printext(x, nViewTop + 1, coordBuf, kTileFont);
843 
844         sprintf(coordBuf, "Y %d", (int)sprite[nSprite].y);
845         printext(x, nViewTop + 10, coordBuf, kTileFont);
846     }
847 
848     if (bHolly)
849     {
850         sprintf(message_text, "HOLLY: %s", sHollyStr);
851         printext(0, 0, message_text, kTileFont);
852     }
853     else if (nSnakeCam < 0)
854     {
855         if (message_timer) {
856             printext(0, 0, message_text, kTileFont);
857         }
858     }
859 }
860 
861 class StatusLoadSave : public LoadSave
862 {
863 public:
864     virtual void Load();
865     virtual void Save();
866 };
867 
Load()868 void StatusLoadSave::Load()
869 {
870     Read(&nAnimsFree, sizeof(nAnimsFree));
871     Read(&message_timer, sizeof(message_timer));
872     Read(&message_text, sizeof(message_text));
873     Read(&magicperline, sizeof(magicperline));
874     Read(&airperline, sizeof(airperline));
875     Read(&healthperline, sizeof(healthperline));
876     Read(&nAirFrames, sizeof(nAirFrames));
877     Read(&nCounter, sizeof(nCounter));
878     Read(&nCounterDest, sizeof(nCounterDest));
879     Read(&nStatusSeqOffset, sizeof(nStatusSeqOffset));
880     Read(&nItemFrames, sizeof(nItemFrames));
881     Read(&laststatusx, sizeof(laststatusx));
882     Read(&laststatusy, sizeof(laststatusy));
883     Read(&nItemSeq, sizeof(nItemSeq));
884     Read(nDigit, sizeof(nDigit));
885     Read(&nMagicFrames, sizeof(nMagicFrames));
886     Read(&nHealthLevel, sizeof(nHealthLevel));
887     Read(&nItemFrame, sizeof(nItemFrame));
888     Read(&nMeterRange, sizeof(nMeterRange));
889     Read(&nMagicLevel, sizeof(nMagicLevel));
890     Read(&nHealthFrame, sizeof(nHealthFrame));
891     Read(&nMagicFrame, sizeof(nMagicFrame));
892     Read(&statusx, sizeof(statusx));
893     Read(&statusy, sizeof(statusy));
894     Read(&nHealthFrames, sizeof(nHealthFrames));
895     Read(&airframe, sizeof(airframe));
896     Read(&nFirstAnim, sizeof(nFirstAnim));
897     Read(&nLastAnim, sizeof(nLastAnim));
898     Read(&nItemAltSeq, sizeof(nItemAltSeq));
899     Read(&airpages, sizeof(airpages));
900     Read(&ammodelay, sizeof(ammodelay));
901     Read(&nCounterBullet, sizeof(nCounterBullet));
902 
903     Read(StatusAnim, sizeof(StatusAnim));
904     Read(StatusAnimsFree, sizeof(StatusAnimsFree));
905     Read(StatusAnimFlags, sizeof(StatusAnimFlags));
906 }
907 
Save()908 void StatusLoadSave::Save()
909 {
910     Write(&nAnimsFree, sizeof(nAnimsFree));
911     Write(&message_timer, sizeof(message_timer));
912     Write(&message_text, sizeof(message_text));
913     Write(&magicperline, sizeof(magicperline));
914     Write(&airperline, sizeof(airperline));
915     Write(&healthperline, sizeof(healthperline));
916     Write(&nAirFrames, sizeof(nAirFrames));
917     Write(&nCounter, sizeof(nCounter));
918     Write(&nCounterDest, sizeof(nCounterDest));
919     Write(&nStatusSeqOffset, sizeof(nStatusSeqOffset));
920     Write(&nItemFrames, sizeof(nItemFrames));
921     Write(&laststatusx, sizeof(laststatusx));
922     Write(&laststatusy, sizeof(laststatusy));
923     Write(&nItemSeq, sizeof(nItemSeq));
924     Write(nDigit, sizeof(nDigit));
925     Write(&nMagicFrames, sizeof(nMagicFrames));
926     Write(&nHealthLevel, sizeof(nHealthLevel));
927     Write(&nItemFrame, sizeof(nItemFrame));
928     Write(&nMeterRange, sizeof(nMeterRange));
929     Write(&nMagicLevel, sizeof(nMagicLevel));
930     Write(&nHealthFrame, sizeof(nHealthFrame));
931     Write(&nMagicFrame, sizeof(nMagicFrame));
932     Write(&statusx, sizeof(statusx));
933     Write(&statusy, sizeof(statusy));
934     Write(&nHealthFrames, sizeof(nHealthFrames));
935     Write(&airframe, sizeof(airframe));
936     Write(&nFirstAnim, sizeof(nFirstAnim));
937     Write(&nLastAnim, sizeof(nLastAnim));
938     Write(&nItemAltSeq, sizeof(nItemAltSeq));
939     Write(&airpages, sizeof(airpages));
940     Write(&ammodelay, sizeof(ammodelay));
941     Write(&nCounterBullet, sizeof(nCounterBullet));
942 
943     Write(StatusAnim, sizeof(StatusAnim));
944     Write(StatusAnimsFree, sizeof(StatusAnimsFree));
945     Write(StatusAnimFlags, sizeof(StatusAnimFlags));
946 }
947 
948 static StatusLoadSave* myLoadSave;
949 
StatusLoadSaveConstruct()950 void StatusLoadSaveConstruct()
951 {
952     myLoadSave = new StatusLoadSave();
953 }
954