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 <stdlib.h>
24 #include <string.h>
25 
26 #include "compat.h"
27 #include "a.h"
28 #include "build.h"
29 #include "colmatch.h"
30 #include "pragmas.h"
31 #include "mmulti.h"
32 #include "osd.h"
33 #include "common_game.h"
34 
35 #include "aihand.h"
36 #include "blood.h"
37 #include "choke.h"
38 #include "config.h"
39 #include "db.h"
40 #include "endgame.h"
41 #include "gamemenu.h"
42 #include "gameutil.h"
43 #include "globals.h"
44 #include "gfx.h"
45 #include "levels.h"
46 #include "loadsave.h"
47 #include "map2d.h"
48 #include "messages.h"
49 #include "menu.h"
50 #include "mirrors.h"
51 #include "network.h"
52 #include "player.h"
53 #include "replace.h"
54 #include "screen.h"
55 #include "sectorfx.h"
56 #include "tile.h"
57 #include "trig.h"
58 #include "view.h"
59 #include "warp.h"
60 #include "weapon.h"
61 #ifdef NOONE_EXTENSIONS
62 #include "nnexts.h"
63 #endif
64 
65 struct VIEW {
66     int at0;
67     int at4;
68     int at8; // bob height
69     int atc; // bob width
70     int at10;
71     int at14;
72     int at18; // bob sway y
73     int at1c; // bob sway x
74     fix16_t at20;
75     fix16_t at24; // horiz
76     int at28; // horizoff
77     int at2c;
78     fix16_t at30; // angle
79     int at34; // weapon z
80     int at38; // view z
81     int at3c;
82     int at40;
83     int at44;
84     int at48; // posture
85     int at4c; // spin
86     int at50; // x
87     int at54; // y
88     int at58; // z
89     int at5c; //xvel
90     int at60; //yvel
91     int at64; //zvel
92     short at68; // sectnum
93     unsigned int at6a; // floordist
94     char at6e; // look center
95     char at6f;
96     char at70; // run
97     char at71; // jump
98     char at72; // underwater
99     short at73; // sprite flags
100     SPRITEHIT at75;
101 };
102 
103 VIEW gPrevView[kMaxPlayers];
104 VIEWPOS gViewPos;
105 int gViewIndex;
106 
107 struct INTERPOLATE {
108     void *pointer;
109     int value;
110     int value2;
111     INTERPOLATE_TYPE type;
112 };
113 
114 int pcBackground;
115 int gViewMode = 3;
116 int gViewSize = 2;
117 
118 bool gPrediction = true;
119 
120 VIEW predict, predictOld;
121 
122 VIEW predictFifo[256];
123 
124 int gInterpolate;
125 int nInterpolations;
126 char gInterpolateSprite[(kMaxSprites+7)>>3];
127 char gInterpolateWall[(kMaxWalls+7)>>3];
128 char gInterpolateSector[(kMaxSectors+7)>>3];
129 
130 #define kMaxInterpolations 16384
131 
132 INTERPOLATE gInterpolation[kMaxInterpolations];
133 
134 int gViewXCenter, gViewYCenter;
135 int gViewX0, gViewY0, gViewX1, gViewY1;
136 int gViewX0S, gViewY0S, gViewX1S, gViewY1S;
137 int xscale, xscalecorrect, yscale, xstep, ystep;
138 
139 int gScreenTilt;
140 
141 CGameMessageMgr gGameMessageMgr;
142 
143 bool bLoadScreenCrcMatch = false;
144 
RotateYZ(int * pX,int * pY,int * pZ,int ang)145 void RotateYZ(int *pX, int *pY, int *pZ, int ang)
146 {
147     UNREFERENCED_PARAMETER(pX);
148     int oY, oZ, angSin, angCos;
149     oY = *pY;
150     oZ = *pZ;
151     angSin = Sin(ang);
152     angCos = Cos(ang);
153     *pY = dmulscale30r(oY,angCos,oZ,-angSin);
154     *pZ = dmulscale30r(oY,angSin,oZ,angCos);
155 }
156 
RotateXZ(int * pX,int * pY,int * pZ,int ang)157 void RotateXZ(int *pX, int *pY, int *pZ, int ang)
158 {
159     UNREFERENCED_PARAMETER(pY);
160     int oX, oZ, angSin, angCos;
161     oX = *pX;
162     oZ = *pZ;
163     angSin = Sin(ang);
164     angCos = Cos(ang);
165     *pX = dmulscale30r(oX,angCos,oZ,-angSin);
166     *pZ = dmulscale30r(oX,angSin,oZ,angCos);
167 }
168 
RotateXY(int * pX,int * pY,int * pZ,int ang)169 void RotateXY(int *pX, int *pY, int *pZ, int ang)
170 {
171     UNREFERENCED_PARAMETER(pZ);
172     int oX, oY, angSin, angCos;
173     oX = *pX;
174     oY = *pY;
175     angSin = Sin(ang);
176     angCos = Cos(ang);
177     *pX = dmulscale30r(oX,angCos,oY,-angSin);
178     *pY = dmulscale30r(oX,angSin,oY,angCos);
179 }
180 
181 FONT gFont[kFontNum];
182 
FontSet(int id,int tile,int space)183 void FontSet(int id, int tile, int space)
184 {
185     if (id < 0 || id >= kFontNum || tile < 0 || tile >= kMaxTiles)
186         return;
187 
188     FONT* pFont = &gFont[id];
189     int yoff = 0;
190 
191     DICTNODE* hQFont = gSysRes.Lookup(id, "QFN");
192     if (hQFont)
193     {
194         QFONT *pQFont = (QFONT*)gSysRes.Load(hQFont);
195         for (int i = 32; i < 128; i++)
196         {
197             int const nTile = tile + i - 32;
198             QFONTCHAR* pChar = &pQFont->at20[i];
199             yoff = min(yoff, pQFont->atf + pChar->oy);
200         }
201         for (int i = 32; i < 128; i++)
202         {
203             int const nTile = tile + i - 32;
204             if (waloff[nTile])
205                 continue;
206             QFONTCHAR *pChar = &pQFont->at20[i];
207             int const width = max(pChar->w, pChar->ox);
208             int const height = max(pQFont->atf+pChar->oy+pChar->h-yoff, 1);
209             char *tilePtr = (char*)tileCreate(nTile, width, height);
210             if (!tilePtr)
211                 continue;
212             Bmemset(tilePtr, 255, width * height);
213             for (int x = 0; x < pChar->w; x++)
214             {
215                 for (int y = 0; y < pChar->h; y++)
216                 {
217                     int const dx = x;
218                     int const dy = y + pQFont->atf + pChar->oy-yoff;
219                     if (dx >= 0 && dx < width && dy >= 0 && dy < height)
220                         tilePtr[dx*height + dy] = pQFont->at820[pChar->offset + x * pChar->h + y];
221                 }
222             }
223         }
224 
225         pFont->tile = tile;
226         pFont->xSize = pQFont->at12;
227         pFont->ySize = pQFont->at13;
228         pFont->space = pQFont->at11;
229         pFont->yoff = yoff;
230 
231         return;
232     }
233     int xSize = 0;
234     int ySize = 0;
235     pFont->tile = tile;
236     for (int i = 0; i < 96; i++)
237     {
238         if (tilesiz[tile+i].x > xSize)
239             xSize = tilesiz[tile+i].x;
240         if (tilesiz[tile+i].y > ySize)
241             ySize = tilesiz[tile+i].y;
242     }
243     pFont->xSize = xSize;
244     pFont->ySize = ySize;
245     pFont->space = space;
246     pFont->yoff = yoff;
247 }
248 
viewGetFontInfo(int id,const char * unk1,int * pXSize,int * pYSize)249 void viewGetFontInfo(int id, const char *unk1, int *pXSize, int *pYSize)
250 {
251     if (id < 0 || id >= kFontNum)
252         return;
253     FONT *pFont = &gFont[id];
254     if (!unk1)
255     {
256         if (pXSize)
257             *pXSize = pFont->xSize;
258         if (pYSize)
259             *pYSize = pFont->ySize;
260     }
261     else
262     {
263         int width = -pFont->space;
264         for (const char *pBuf = unk1; *pBuf != 0; pBuf++)
265         {
266             int tile = ((*pBuf-32)&127)+pFont->tile;
267             if (tilesiz[tile].x != 0 && tilesiz[tile].y != 0)
268                 width += tilesiz[tile].x+pFont->space;
269         }
270         if (pXSize)
271             *pXSize = width;
272         if (pYSize)
273             *pYSize = pFont->ySize;
274     }
275 }
276 
viewUpdatePages(void)277 void viewUpdatePages(void)
278 {
279     pcBackground = numpages;
280 }
281 
viewToggle(int viewMode)282 void viewToggle(int viewMode)
283 {
284     if (viewMode == 3)
285         gViewMode = 4;
286     else
287     {
288         gViewMode = 3;
289         viewResizeView(gViewSize);
290     }
291 }
292 
viewInitializePrediction(void)293 void viewInitializePrediction(void)
294 {
295     predict.at30 = gMe->q16ang;
296     predict.at20 = gMe->q16look;
297     predict.at24 = gMe->q16horiz;
298     predict.at28 = gMe->q16slopehoriz;
299     predict.at2c = gMe->slope;
300     predict.at6f = gMe->cantJump;
301     predict.at70 = gMe->isRunning;
302     predict.at72 = gMe->isUnderwater;
303     predict.at71 = gMe->input.buttonFlags.jump;
304     predict.at50 = gMe->pSprite->x;
305     predict.at54 = gMe->pSprite->y;
306     predict.at58 = gMe->pSprite->z;
307     predict.at68 = gMe->pSprite->sectnum;
308     predict.at73 = gMe->pSprite->flags;
309     predict.at5c = xvel[gMe->pSprite->index];
310     predict.at60 = yvel[gMe->pSprite->index];
311     predict.at64 = zvel[gMe->pSprite->index];
312     predict.at6a = gMe->pXSprite->height;
313     predict.at48 = gMe->posture;
314     predict.at4c = gMe->spin;
315     predict.at6e = gMe->input.keyFlags.lookCenter;
316     memcpy(&predict.at75,&gSpriteHit[gMe->pSprite->extra],sizeof(SPRITEHIT));
317     predict.at0 = gMe->bobPhase;
318     predict.at4 = gMe->bobAmp;
319     predict.at8 = gMe->bobHeight;
320     predict.atc = gMe->bobWidth;
321     predict.at10 = gMe->swayPhase;
322     predict.at14 = gMe->swayAmp;
323     predict.at18 = gMe->swayHeight;
324     predict.at1c = gMe->swayWidth;
325     predict.at34 = gMe->zWeapon-gMe->zView-(12<<8);
326     predict.at38 = gMe->zView;
327     predict.at3c = gMe->zViewVel;
328     predict.at40 = gMe->zWeapon;
329     predict.at44 = gMe->zWeaponVel;
330     predictOld = predict;
331     if (numplayers != 1)
332     {
333         gViewAngle = predict.at30;
334         gViewLook = predict.at20;
335     }
336 }
337 
viewUpdatePrediction(GINPUT * pInput)338 void viewUpdatePrediction(GINPUT *pInput)
339 {
340     predictOld = predict;
341     short bakCstat = gMe->pSprite->cstat;
342     gMe->pSprite->cstat = 0;
343     fakePlayerProcess(gMe, pInput);
344     fakeActProcessSprites();
345     gMe->pSprite->cstat = bakCstat;
346     predictFifo[gPredictTail&255] = predict;
347     gPredictTail++;
348     if (numplayers != 1)
349     {
350         gViewAngle = predict.at30;
351         gViewLook = predict.at20;
352     }
353 }
354 
sub_158B4(PLAYER * pPlayer)355 void sub_158B4(PLAYER *pPlayer)
356 {
357     predict.at38 = predict.at58 - pPlayer->pPosture[pPlayer->lifeMode][predict.at48].eyeAboveZ;
358     predict.at40 = predict.at58 - pPlayer->pPosture[pPlayer->lifeMode][predict.at48].weaponAboveZ;
359 }
360 
fakeProcessInput(PLAYER * pPlayer,GINPUT * pInput)361 void fakeProcessInput(PLAYER *pPlayer, GINPUT *pInput)
362 {
363     POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][predict.at48];
364 
365     if (numplayers > 1 && gPrediction)
366     {
367         gViewAngleAdjust = 0.f;
368         gViewLookRecenter = false;
369         gViewLookAdjust = 0.f;
370     }
371 
372     predict.at70 = pInput->syncFlags.run;
373     predict.at70 = 0;
374     predict.at71 = pInput->buttonFlags.jump;
375     if (predict.at48 == 1)
376     {
377         int x = Cos(fix16_to_int(predict.at30));
378         int y = Sin(fix16_to_int(predict.at30));
379         if (pInput->forward)
380         {
381             int forward = pInput->forward;
382             if (forward > 0)
383                 forward = mulscale8(pPosture->frontAccel, forward);
384             else
385                 forward = mulscale8(pPosture->backAccel, forward);
386             predict.at5c += mulscale30(forward, x);
387             predict.at60 += mulscale30(forward, y);
388         }
389         if (pInput->strafe)
390         {
391             int strafe = pInput->strafe;
392             strafe = mulscale8(pPosture->sideAccel, strafe);
393             predict.at5c += mulscale30(strafe, y);
394             predict.at60 -= mulscale30(strafe, x);
395         }
396     }
397     else if (predict.at6a < 0x100)
398     {
399         int speed = 0x10000;
400         if (predict.at6a > 0)
401             speed -= divscale16(predict.at6a, 0x100);
402         int x = Cos(fix16_to_int(predict.at30));
403         int y = Sin(fix16_to_int(predict.at30));
404         if (pInput->forward)
405         {
406             int forward = pInput->forward;
407             if (forward > 0)
408                 forward = mulscale8(pPosture->frontAccel, forward);
409             else
410                 forward = mulscale8(pPosture->backAccel, forward);
411             if (predict.at6a)
412                 forward = mulscale16(forward, speed);
413             predict.at5c += mulscale30(forward, x);
414             predict.at60 += mulscale30(forward, y);
415         }
416         if (pInput->strafe)
417         {
418             int strafe = pInput->strafe;
419             strafe = mulscale8(pPosture->sideAccel, strafe);
420             if (predict.at6a)
421                 strafe = mulscale16(strafe, speed);
422             predict.at5c += mulscale30(strafe, y);
423             predict.at60 -= mulscale30(strafe, x);
424         }
425     }
426     if (pInput->q16turn)
427         predict.at30 = (predict.at30+pInput->q16turn)&0x7ffffff;
428     if (pInput->keyFlags.spin180)
429         if (!predict.at4c)
430             predict.at4c = -1024;
431     if (predict.at4c < 0)
432     {
433         int speed;
434         if (predict.at48 == 1)
435             speed = 64;
436         else
437             speed = 128;
438 
439         predict.at4c = min(predict.at4c+speed, 0);
440         predict.at30 += fix16_from_int(speed);
441         if (numplayers > 1 && gPrediction)
442             gViewAngleAdjust += float(speed);
443     }
444 
445     if (!predict.at71)
446         predict.at6f = 0;
447 
448     switch (predict.at48)
449     {
450     case 1:
451         if (predict.at71)
452             predict.at64 -= pPosture->normalJumpZ;//0x5b05;
453         if (pInput->buttonFlags.crouch)
454             predict.at64 += pPosture->normalJumpZ;//0x5b05;
455         break;
456     case 2:
457         if (!pInput->buttonFlags.crouch)
458             predict.at48 = 0;
459         break;
460     default:
461         if (!predict.at6f && predict.at71 && predict.at6a == 0) {
462             if (packItemActive(pPlayer, 4)) predict.at64 = pPosture->pwupJumpZ;//-0x175555;
463             else predict.at64 = pPosture->normalJumpZ;//-0xbaaaa;
464             predict.at6f = 1;
465         }
466         if (pInput->buttonFlags.crouch)
467             predict.at48 = 2;
468         break;
469     }
470 #if 0
471     if (predict.at6e && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown)
472     {
473         if (predict.at20 < 0)
474             predict.at20 = fix16_min(predict.at20+F16(4), F16(0));
475         if (predict.at20 > 0)
476             predict.at20 = fix16_max(predict.at20-F16(4), F16(0));
477         if (predict.at20 == 0)
478             predict.at6e = 0;
479     }
480     else
481     {
482         if (pInput->buttonFlags.lookUp)
483             predict.at20 = fix16_min(predict.at20+F16(4), F16(60));
484         if (pInput->buttonFlags.lookDown)
485             predict.at20 = fix16_max(predict.at20-F16(4), F16(-60));
486     }
487     predict.at20 = fix16_clamp(predict.at20+pInput->q16mlook, F16(-60), F16(60));
488 
489     if (predict.at20 > 0)
490         predict.at24 = mulscale30(F16(120), Sin(fix16_to_int(predict.at20<<3)));
491     else if (predict.at20 < 0)
492         predict.at24 = mulscale30(F16(180), Sin(fix16_to_int(predict.at20<<3)));
493     else
494         predict.at24 = 0;
495 #endif
496     CONSTEXPR int upAngle = 289;
497     CONSTEXPR int downAngle = -347;
498     CONSTEXPR double lookStepUp = 4.0*upAngle/60.0;
499     CONSTEXPR double lookStepDown = -4.0*downAngle/60.0;
500     if (predict.at6e && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown)
501     {
502         if (predict.at20 < 0)
503             predict.at20 = fix16_min(predict.at20+F16(lookStepDown), F16(0));
504         if (predict.at20 > 0)
505             predict.at20 = fix16_max(predict.at20-F16(lookStepUp), F16(0));
506         if (predict.at20 == 0)
507             predict.at6e = 0;
508     }
509     else
510     {
511         if (pInput->buttonFlags.lookUp)
512             predict.at20 = fix16_min(predict.at20+F16(lookStepUp), F16(upAngle));
513         if (pInput->buttonFlags.lookDown)
514             predict.at20 = fix16_max(predict.at20-F16(lookStepDown), F16(downAngle));
515     }
516     if (numplayers > 1 && gPrediction)
517     {
518         if (pInput->buttonFlags.lookUp)
519         {
520             gViewLookAdjust += float(lookStepUp);
521         }
522         if (pInput->buttonFlags.lookDown)
523         {
524             gViewLookAdjust -= float(lookStepDown);
525         }
526         gViewLookRecenter = predict.at6e && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown;
527     }
528     predict.at20 = fix16_clamp(predict.at20+(pInput->q16mlook<<3), F16(downAngle), F16(upAngle));
529     predict.at24 = fix16_from_float(100.f*tanf(fix16_to_float(predict.at20)*fPI/1024.f));
530 
531     int nSector = predict.at68;
532     int florhit = predict.at75.florhit & 0xc000;
533     char va;
534     if (predict.at6a < 16 && (florhit == 0x4000 || florhit == 0))
535         va = 1;
536     else
537         va = 0;
538     if (va && (sector[nSector].floorstat&2) != 0)
539     {
540         int z1 = getflorzofslope(nSector, predict.at50, predict.at54);
541         int x2 = predict.at50+mulscale30(64, Cos(fix16_to_int(predict.at30)));
542         int y2 = predict.at54+mulscale30(64, Sin(fix16_to_int(predict.at30)));
543         short nSector2 = nSector;
544         updatesector(x2, y2, &nSector2);
545         if (nSector2 == nSector)
546         {
547             int z2 = getflorzofslope(nSector2, x2, y2);
548             predict.at28 = interpolate(predict.at28, fix16_from_int(z1-z2)>>3, 0x4000);
549         }
550     }
551     else
552     {
553         predict.at28 = interpolate(predict.at28, 0, 0x4000);
554         if (klabs(predict.at28) < 4)
555             predict.at28 = 0;
556     }
557     predict.at2c = (-fix16_to_int(predict.at24))<<7;
558 }
559 
fakePlayerProcess(PLAYER * pPlayer,GINPUT * pInput)560 void fakePlayerProcess(PLAYER *pPlayer, GINPUT *pInput)
561 {
562     spritetype *pSprite = pPlayer->pSprite;
563     XSPRITE *pXSprite = pPlayer->pXSprite;
564     POSTURE* pPosture = &pPlayer->pPosture[pPlayer->lifeMode][predict.at48];
565 
566     int top, bottom;
567     GetSpriteExtents(pSprite, &top, &bottom);
568 
569     top += predict.at58-pSprite->z;
570     bottom += predict.at58-pSprite->z;
571 
572     int dzb = (bottom-predict.at58)/4;
573     int dzt = (predict.at58-top)/4;
574 
575     int dw = pSprite->clipdist<<2;
576     short nSector = predict.at68;
577     if (!gNoClip)
578     {
579         pushmove_old((int32_t*)&predict.at50, (int32_t*)&predict.at54, (int32_t*)&predict.at58, &predict.at68, dw, dzt, dzb, CLIPMASK0);
580         if (predict.at68 == -1)
581             predict.at68 = nSector;
582     }
583     fakeProcessInput(pPlayer, pInput);
584 
585     int nSpeed = approxDist(predict.at5c, predict.at60);
586 
587     predict.at3c = interpolate(predict.at3c, predict.at64, 0x7000);
588     int dz = predict.at58-pPosture->eyeAboveZ-predict.at38;
589     if (dz > 0)
590         predict.at3c += mulscale16(dz<<8, 0xa000);
591     else
592         predict.at3c += mulscale16(dz<<8, 0x1800);
593     predict.at38 += predict.at3c>>8;
594 
595     predict.at44 = interpolate(predict.at44, predict.at64, 0x5000);
596     dz = predict.at58-pPosture->weaponAboveZ-predict.at40;
597     if (dz > 0)
598         predict.at44 += mulscale16(dz<<8, 0x8000);
599     else
600         predict.at44 += mulscale16(dz<<8, 0xc00);
601     predict.at40 += predict.at44>>8;
602 
603     predict.at34 = predict.at40 - predict.at38 - (12<<8);
604 
605     predict.at0 = ClipLow(predict.at0-4, 0);
606 
607     nSpeed >>= 16;
608     if (predict.at48 == 1)
609     {
610         predict.at4 = (predict.at4+17)&2047;
611         predict.at14 = (predict.at14+17)&2047;
612         predict.at8 = mulscale30(10*pPosture->bobV,Sin(predict.at4*2));
613         predict.atc = mulscale30(predict.at0*pPosture->bobH,Sin(predict.at4-256));
614         predict.at18 = mulscale30(predict.at0*pPosture->swayV,Sin(predict.at14*2));
615         predict.at1c = mulscale30(predict.at0*pPosture->swayH,Sin(predict.at14-0x155));
616     }
617     else
618     {
619         if (pXSprite->height < 256)
620         {
621             predict.at4 = (predict.at4+(pPosture->pace[predict.at70]*4))&2047;
622             predict.at14 = (predict.at14+(pPosture->pace[predict.at70]*4)/2)&2047;
623             if (predict.at70)
624             {
625                 if (predict.at0 < 60)
626                     predict.at0 = ClipHigh(predict.at0 + nSpeed, 60);
627             }
628             else
629             {
630                 if (predict.at0 < 30)
631                     predict.at0 = ClipHigh(predict.at0 + nSpeed, 30);
632             }
633         }
634         predict.at8 = mulscale30(predict.at0*pPosture->bobV,Sin(predict.at4*2));
635         predict.atc = mulscale30(predict.at0*pPosture->bobH,Sin(predict.at4-256));
636         predict.at18 = mulscale30(predict.at0*pPosture->swayV,Sin(predict.at14*2));
637         predict.at1c = mulscale30(predict.at0*pPosture->swayH,Sin(predict.at14-0x155));
638     }
639     if (!pXSprite->health)
640         return;
641     predict.at72 = 0;
642     if (predict.at48 == 1)
643     {
644         predict.at72 = 1;
645         int nSector = predict.at68;
646         int nLink = gLowerLink[nSector];
647         if (nLink > 0 && (sprite[nLink].type == kMarkerLowGoo || sprite[nLink].type == kMarkerLowWater))
648         {
649             if (getceilzofslope(nSector, predict.at50, predict.at54) > predict.at38)
650                 predict.at72 = 0;
651         }
652     }
653 }
654 
fakeMoveDude(spritetype * pSprite)655 void fakeMoveDude(spritetype *pSprite)
656 {
657     PLAYER *pPlayer = NULL;
658     int bottom, top;
659     if (IsPlayerSprite(pSprite))
660         pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
661     dassert(pSprite->type >= kDudeBase && pSprite->type < kDudeMax);
662     GetSpriteExtents(pSprite, &top, &bottom);
663     top += predict.at58 - pSprite->z;
664     bottom += predict.at58 - pSprite->z;
665     int bz = (bottom-predict.at58)/4;
666     int tz = (predict.at58-top)/4;
667     int wd = pSprite->clipdist*4;
668     int nSector = predict.at68;
669     dassert(nSector >= 0 && nSector < kMaxSectors);
670     if (predict.at5c || predict.at60)
671     {
672         if (pPlayer && gNoClip)
673         {
674             predict.at50 += predict.at5c>>12;
675             predict.at54 += predict.at60>>12;
676             if (!FindSector(predict.at50, predict.at54, &nSector))
677                 nSector = predict.at68;
678         }
679         else
680         {
681             short bakCstat = pSprite->cstat;
682             pSprite->cstat &= ~257;
683             predict.at75.hit = ClipMove(&predict.at50, &predict.at54, &predict.at58, &nSector, predict.at5c >> 12, predict.at60 >> 12, wd, tz, bz, CLIPMASK0);
684             if (nSector == -1)
685                 nSector = predict.at68;
686 
687             if (sector[nSector].type >= kSectorPath && sector[nSector].type <= kSectorRotate)
688             {
689                 short nSector2 = nSector;
690                 pushmove_old((int32_t*)&predict.at50, (int32_t*)&predict.at54, (int32_t*)&predict.at58, &nSector2, wd, tz, bz, CLIPMASK0);
691                 if (nSector2 != -1)
692                     nSector = nSector2;
693             }
694 
695             dassert(nSector >= 0);
696 
697             pSprite->cstat = bakCstat;
698         }
699         switch (predict.at75.hit&0xc000)
700         {
701         case 0x8000:
702         {
703             int nHitWall = predict.at75.hit&0x3fff;
704             walltype *pHitWall = &wall[nHitWall];
705             if (pHitWall->nextsector != -1)
706             {
707                 sectortype *pHitSector = &sector[pHitWall->nextsector];
708                 if (top < pHitSector->ceilingz || bottom > pHitSector->floorz)
709                 {
710                     // ???
711                 }
712             }
713             actWallBounceVector(&predict.at5c, &predict.at60, nHitWall, 0);
714             break;
715         }
716         }
717     }
718     if (predict.at68 != nSector)
719     {
720         dassert(nSector >= 0 && nSector < kMaxSectors);
721         predict.at68 = nSector;
722     }
723     char bUnderwater = 0;
724     char bDepth = 0;
725     int nXSector = sector[nSector].extra;
726     if (nXSector > 0)
727     {
728         XSECTOR *pXSector = &xsector[nXSector];
729         if (pXSector->Underwater)
730             bUnderwater = 1;
731         if (pXSector->Depth)
732             bDepth = 1;
733     }
734     int nUpperLink = gUpperLink[nSector];
735     int nLowerLink = gLowerLink[nSector];
736     if (nUpperLink >= 0 && (sprite[nUpperLink].type == kMarkerUpWater || sprite[nUpperLink].type == kMarkerUpGoo))
737         bDepth = 1;
738     if (nLowerLink >= 0 && (sprite[nLowerLink].type == kMarkerLowWater || sprite[nLowerLink].type == kMarkerLowGoo))
739         bDepth = 1;
740     if (pPlayer)
741         wd += 16;
742 
743     if (predict.at64)
744         predict.at58 += predict.at64 >> 8;
745 
746     spritetype pSpriteBak = *pSprite;
747     spritetype *pTempSprite = pSprite;
748     pTempSprite->x = predict.at50;
749     pTempSprite->y = predict.at54;
750     pTempSprite->z = predict.at58;
751     pTempSprite->sectnum = predict.at68;
752     int ceilZ, ceilHit, floorZ, floorHit;
753     GetZRange(pTempSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0);
754     GetSpriteExtents(pTempSprite, &top, &bottom);
755     if (predict.at73 & 2)
756     {
757         int vc = 58254;
758         if (bDepth)
759         {
760             if (bUnderwater)
761             {
762                 int cz = getceilzofslope(nSector, predict.at50, predict.at54);
763                 if (cz > top)
764                     vc += ((bottom-cz)*-80099) / (bottom-top);
765                 else
766                     vc = 0;
767             }
768             else
769             {
770                 int fz = getflorzofslope(nSector, predict.at50, predict.at54);
771                 if (fz < bottom)
772                     vc += ((bottom-fz)*-80099) / (bottom-top);
773             }
774         }
775         else
776         {
777             if (bUnderwater)
778                 vc = 0;
779             else if (bottom >= floorZ)
780                 vc = 0;
781         }
782         if (vc)
783         {
784             predict.at58 += ((vc*4)/2)>>8;
785             predict.at64 += vc;
786         }
787     }
788     GetSpriteExtents(pTempSprite, &top, &bottom);
789     if (bottom >= floorZ)
790     {
791         int floorZ2 = floorZ;
792         int floorHit2 = floorHit;
793         GetZRange(pTempSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, pSprite->clipdist<<2, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR);
794         if (bottom <= floorZ && predict.at58-floorZ2 < bz)
795         {
796             floorZ = floorZ2;
797             floorHit = floorHit2;
798         }
799     }
800     if (floorZ <= bottom)
801     {
802         predict.at75.florhit = floorHit;
803         predict.at58 += floorZ-bottom;
804         int var44 = predict.at64-velFloor[predict.at68];
805         if (var44 > 0)
806         {
807             actFloorBounceVector(&predict.at5c, &predict.at60, &var44, predict.at68, 0);
808             predict.at64 = var44;
809             if (klabs(predict.at64) < 0x10000)
810             {
811                 predict.at64 = velFloor[predict.at68];
812                 predict.at73 &= ~4;
813             }
814             else
815                 predict.at73 |= 4;
816         }
817         else if (predict.at64 == 0)
818             predict.at73 &= ~4;
819     }
820     else
821     {
822         predict.at75.florhit = 0;
823         if (predict.at73 & 2)
824             predict.at73 |= 4;
825     }
826     if (top <= ceilZ)
827     {
828         predict.at75.ceilhit = ceilHit;
829         predict.at58 += ClipLow(ceilZ-top, 0);
830         if (predict.at64 <= 0 && (predict.at73&4))
831             predict.at64 = mulscale16(-predict.at64, 0x2000);
832     }
833     else
834         predict.at75.ceilhit = 0;
835 
836     GetSpriteExtents(pTempSprite, &top, &bottom);
837     *pSprite = pSpriteBak;
838     predict.at6a = ClipLow(floorZ-bottom, 0)>>8;
839     if (predict.at5c || predict.at60)
840     {
841         if ((floorHit & 0xc000) == 0xc000)
842         {
843             int nHitSprite = floorHit & 0x3fff;
844             if ((sprite[nHitSprite].cstat & 0x30) == 0)
845             {
846                 predict.at5c += mulscale(4, predict.at50 - sprite[nHitSprite].x, 2);
847                 predict.at60 += mulscale(4, predict.at54 - sprite[nHitSprite].y, 2);
848                 return;
849             }
850         }
851         int nXSector = sector[pSprite->sectnum].extra;
852         if (nXSector > 0 && xsector[nXSector].Underwater)
853             return;
854         if (predict.at6a >= 0x100)
855             return;
856         int nDrag = gDudeDrag;
857         if (predict.at6a > 0)
858             nDrag -= scale(gDudeDrag, predict.at6a, 0x100);
859         predict.at5c -= mulscale16r(predict.at5c, nDrag);
860         predict.at60 -= mulscale16r(predict.at60, nDrag);
861         if (approxDist(predict.at5c, predict.at60) < 0x1000)
862             predict.at5c = predict.at60 = 0;
863     }
864 }
865 
fakeActAirDrag(spritetype * pSprite,int num)866 void fakeActAirDrag(spritetype *pSprite, int num)
867 {
868     UNREFERENCED_PARAMETER(pSprite);
869     int xvec = 0;
870     int yvec = 0;
871     int nSector = predict.at68;
872     dassert(nSector >= 0 && nSector < kMaxSectors);
873     sectortype *pSector = &sector[nSector];
874     int nXSector = pSector->extra;
875     if (nXSector > 0)
876     {
877         dassert(nXSector < kMaxXSectors);
878         XSECTOR *pXSector = &xsector[nXSector];
879         if (pXSector->windVel && (pXSector->windAlways || pXSector->busy))
880         {
881             int vel = pXSector->windVel<<12;
882             if (!pXSector->windAlways && pXSector->busy)
883                 vel = mulscale16(vel, pXSector->busy);
884             xvec = mulscale30(vel, Cos(pXSector->windAng));
885             yvec = mulscale30(vel, Sin(pXSector->windAng));
886         }
887     }
888     predict.at5c += mulscale16(xvec-predict.at5c, num);
889     predict.at60 += mulscale16(yvec-predict.at60, num);
890     predict.at64 -= mulscale16(predict.at64, num);
891 }
892 
fakeActProcessSprites(void)893 void fakeActProcessSprites(void)
894 {
895     spritetype *pSprite = gMe->pSprite;
896     if (pSprite->statnum == kStatDude)
897     {
898         int nXSprite = pSprite->extra;
899         dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
900         int nSector = predict.at68;
901         int nXSector = sector[nSector].extra;
902         XSECTOR *pXSector = NULL;
903         if (nXSector > 0)
904         {
905             dassert(nXSector > 0 && nXSector < kMaxXSectors);
906             dassert(xsector[nXSector].reference == nSector);
907             pXSector = &xsector[nXSector];
908         }
909         if (pXSector)
910         {
911             int top, bottom;
912             GetSpriteExtents(pSprite, &top, &bottom);
913             top += predict.at58 - pSprite->z;
914             bottom += predict.at58 - pSprite->z;
915             if (getflorzofslope(nSector, predict.at50, predict.at54) < bottom)
916             {
917                 int angle = pXSector->panAngle;
918                 int speed = 0;
919                 if (pXSector->panAlways || pXSector->state || pXSector->busy)
920                 {
921                     speed = pXSector->panVel << 9;
922                     if (!pXSector->panAlways && pXSector->busy)
923                         speed = mulscale16(speed, pXSector->busy);
924                 }
925                 if (sector[nSector].floorstat&64)
926                     angle = (GetWallAngle(sector[nSector].wallptr)+512)&2047;
927                 predict.at5c += mulscale30(speed,Cos(angle));
928                 predict.at60 += mulscale30(speed,Sin(angle));
929             }
930         }
931         if (pXSector && pXSector->Underwater)
932             fakeActAirDrag(pSprite, 5376);
933         else
934             fakeActAirDrag(pSprite, 128);
935 
936         if ((predict.at73 & 4) != 0 || predict.at5c != 0 || predict.at60 != 0 || predict.at64 != 0 || velFloor[predict.at68] != 0 || velCeil[predict.at68] != 0)
937         {
938             fakeMoveDude(pSprite);
939         }
940     }
941 }
942 
viewCorrectPrediction(void)943 void viewCorrectPrediction(void)
944 {
945     if (numplayers == 1)
946     {
947         gViewLook = gMe->q16look;
948         gViewAngle = gMe->q16ang;
949         return;
950     }
951     spritetype *pSprite = gMe->pSprite;
952     VIEW *pView = &predictFifo[(gNetFifoTail-1)&255];
953     if (gMe->q16ang != pView->at30 || pView->at24 != gMe->q16horiz || pView->at50 != pSprite->x || pView->at54 != pSprite->y || pView->at58 != pSprite->z)
954     {
955         viewInitializePrediction();
956         predictOld = gPrevView[myconnectindex];
957         gPredictTail = gNetFifoTail;
958         while (gPredictTail < gNetFifoHead[myconnectindex])
959         {
960             viewUpdatePrediction(&gFifoInput[gPredictTail&255][myconnectindex]);
961         }
962     }
963 }
964 
viewBackupView(int nPlayer)965 void viewBackupView(int nPlayer)
966 {
967     PLAYER *pPlayer = &gPlayer[nPlayer];
968     VIEW *pView = &gPrevView[nPlayer];
969     pView->at30 = pPlayer->q16ang;
970     pView->at50 = pPlayer->pSprite->x;
971     pView->at54 = pPlayer->pSprite->y;
972     pView->at38 = pPlayer->zView;
973     pView->at34 = pPlayer->zWeapon-pPlayer->zView-0xc00;
974     pView->at24 = pPlayer->q16horiz;
975     pView->at28 = pPlayer->q16slopehoriz;
976     pView->at2c = pPlayer->slope;
977     pView->at8 = pPlayer->bobHeight;
978     pView->atc = pPlayer->bobWidth;
979     pView->at18 = pPlayer->swayHeight;
980     pView->at1c = pPlayer->swayWidth;
981 }
982 
viewCorrectViewOffsets(int nPlayer,vec3_t const * oldpos)983 void viewCorrectViewOffsets(int nPlayer, vec3_t const *oldpos)
984 {
985     PLAYER *pPlayer = &gPlayer[nPlayer];
986     VIEW *pView = &gPrevView[nPlayer];
987     pView->at50 += pPlayer->pSprite->x-oldpos->x;
988     pView->at54 += pPlayer->pSprite->y-oldpos->y;
989     pView->at38 += pPlayer->pSprite->z-oldpos->z;
990 }
991 
viewClearInterpolations(void)992 void viewClearInterpolations(void)
993 {
994     nInterpolations = 0;
995     memset(gInterpolateSprite, 0, sizeof(gInterpolateSprite));
996     memset(gInterpolateWall, 0, sizeof(gInterpolateWall));
997     memset(gInterpolateSector, 0, sizeof(gInterpolateSector));
998 }
999 
viewAddInterpolation(void * data,INTERPOLATE_TYPE type)1000 void viewAddInterpolation(void *data, INTERPOLATE_TYPE type)
1001 {
1002     if (nInterpolations == kMaxInterpolations)
1003         ThrowError("Too many interpolations");
1004     INTERPOLATE *pInterpolate = &gInterpolation[nInterpolations++];
1005     pInterpolate->pointer = data;
1006     pInterpolate->type = type;
1007     switch (type)
1008     {
1009     case INTERPOLATE_TYPE_INT:
1010         pInterpolate->value = *((int*)data);
1011         break;
1012     case INTERPOLATE_TYPE_SHORT:
1013         pInterpolate->value = *((short*)data);
1014         break;
1015     }
1016 }
1017 
CalcInterpolations(void)1018 void CalcInterpolations(void)
1019 {
1020     int i;
1021     INTERPOLATE *pInterpolate = gInterpolation;
1022     for (i = 0; i < nInterpolations; i++, pInterpolate++)
1023     {
1024         switch (pInterpolate->type)
1025         {
1026         case INTERPOLATE_TYPE_INT:
1027         {
1028             pInterpolate->value2 = *((int*)pInterpolate->pointer);
1029             int newValue = interpolate(pInterpolate->value, *((int*)pInterpolate->pointer), gInterpolate);
1030             *((int*)pInterpolate->pointer) = newValue;
1031             break;
1032         }
1033         case INTERPOLATE_TYPE_SHORT:
1034         {
1035             pInterpolate->value2 = *((short*)pInterpolate->pointer);
1036             int newValue = interpolate(pInterpolate->value, *((short*)pInterpolate->pointer), gInterpolate);
1037             *((short*)pInterpolate->pointer) = newValue;
1038             break;
1039         }
1040         }
1041     }
1042 }
1043 
RestoreInterpolations(void)1044 void RestoreInterpolations(void)
1045 {
1046     int i;
1047     INTERPOLATE *pInterpolate = gInterpolation;
1048     for (i = 0; i < nInterpolations; i++, pInterpolate++)
1049     {
1050         switch (pInterpolate->type)
1051         {
1052         case INTERPOLATE_TYPE_INT:
1053             *((int*)pInterpolate->pointer) = pInterpolate->value2;
1054             break;
1055         case INTERPOLATE_TYPE_SHORT:
1056             *((short*)pInterpolate->pointer) = pInterpolate->value2;
1057             break;
1058         }
1059     }
1060 }
1061 
viewDrawText(int nFont,const char * pString,int x,int y,int nShade,int nPalette,int position,char shadow,unsigned int nStat,uint8_t alpha)1062 void viewDrawText(int nFont, const char *pString, int x, int y, int nShade, int nPalette, int position, char shadow, unsigned int nStat, uint8_t alpha)
1063 {
1064     if (nFont < 0 || nFont >= kFontNum || !pString) return;
1065     FONT *pFont = &gFont[nFont];
1066 
1067     y += pFont->yoff;
1068 
1069     if (position)
1070     {
1071         const char *s = pString;
1072         int width = -pFont->space;
1073         while (*s)
1074         {
1075             int nTile = ((*s-' ')&127)+pFont->tile;
1076             if (tilesiz[nTile].x && tilesiz[nTile].y)
1077                 width += tilesiz[nTile].x+pFont->space;
1078             s++;
1079         }
1080         if (position == 1)
1081             width >>= 1;
1082         x -= width;
1083     }
1084     const char *s = pString;
1085     while (*s)
1086     {
1087         int nTile = ((*s-' ')&127) + pFont->tile;
1088         if (tilesiz[nTile].x && tilesiz[nTile].y)
1089         {
1090             if (shadow)
1091             {
1092                 rotatesprite_fs_alpha((x+1)<<16, (y+1)<<16, 65536, 0, nTile, 127, nPalette, 26|nStat, alpha);
1093             }
1094             rotatesprite_fs_alpha(x<<16, y<<16, 65536, 0, nTile, nShade, nPalette, 26|nStat, alpha);
1095             x += tilesiz[nTile].x+pFont->space;
1096         }
1097         s++;
1098     }
1099 }
1100 
viewTileSprite(int nTile,int nShade,int nPalette,int x1,int y1,int x2,int y2)1101 void viewTileSprite(int nTile, int nShade, int nPalette, int x1, int y1, int x2, int y2)
1102 {
1103     Rect rect1 = Rect(x1, y1, x2, y2);
1104     Rect rect2 = Rect(0, 0, xdim, ydim);
1105     rect1 &= rect2;
1106 
1107     if (!rect1)
1108         return;
1109 
1110     dassert(nTile >= 0 && nTile < kMaxTiles);
1111     int width = tilesiz[nTile].x;
1112     int height = tilesiz[nTile].y;
1113     int bx1 = DecBy(rect1.x0+1, width);
1114     int by1 = DecBy(rect1.y0+1, height);
1115     int bx2 = IncBy(rect1.x1-1, width);
1116     int by2 = IncBy(rect1.y1-1, height);
1117     for (int x = bx1; x < bx2; x += width)
1118         for (int y = by1; y < by2; y += height)
1119             rotatesprite(x<<16, y<<16, 65536, 0, nTile, nShade, nPalette, 64+16+8, x1, y1, x2-1, y2-1);
1120 }
1121 
InitStatusBar(void)1122 void InitStatusBar(void)
1123 {
1124     tileLoadTile(2200);
1125 }
DrawStatSprite(int nTile,int x,int y,int nShade,int nPalette,unsigned int nStat,int nScale)1126 void DrawStatSprite(int nTile, int x, int y, int nShade, int nPalette, unsigned int nStat, int nScale)
1127 {
1128     rotatesprite(x<<16, y<<16, nScale, 0, nTile, nShade, nPalette, nStat | 74, 0, 0, xdim-1, ydim-1);
1129 }
DrawStatMaskedSprite(int nTile,int x,int y,int nShade,int nPalette,unsigned int nStat,int nScale)1130 void DrawStatMaskedSprite(int nTile, int x, int y, int nShade, int nPalette, unsigned int nStat, int nScale)
1131 {
1132     rotatesprite(x<<16, y<<16, nScale, 0, nTile, nShade, nPalette, nStat | 10, 0, 0, xdim-1, ydim-1);
1133 }
1134 
DrawStatNumber(const char * pFormat,int nNumber,int nTile,int x,int y,int nShade,int nPalette,unsigned int nStat,int nScale)1135 void DrawStatNumber(const char *pFormat, int nNumber, int nTile, int x, int y, int nShade, int nPalette, unsigned int nStat, int nScale)
1136 {
1137     char tempbuf[80];
1138     int width = tilesiz[nTile].x+1;
1139     x <<= 16;
1140     sprintf(tempbuf, pFormat, nNumber);
1141     for (unsigned int i = 0; i < strlen(tempbuf); i++, x += width*nScale)
1142     {
1143         if (tempbuf[i] == ' ') continue;
1144         rotatesprite(x, y<<16, nScale, 0, nTile+tempbuf[i]-'0', nShade, nPalette, nStat | 10, 0, 0, xdim-1, ydim-1);
1145     }
1146 }
1147 
TileHGauge(int nTile,int x,int y,int nMult,int nDiv,int nStat,int nScale)1148 void TileHGauge(int nTile, int x, int y, int nMult, int nDiv, int nStat, int nScale)
1149 {
1150     int bx = scale(mulscale16(tilesiz[nTile].x,nScale),nMult,nDiv)+x;
1151     int sbx;
1152     switch (nStat&(512+256))
1153     {
1154     case 256:
1155         sbx = mulscale16(bx, xscalecorrect)-1;
1156         break;
1157     case 512:
1158         bx -= 320;
1159         sbx = xdim+mulscale16(bx, xscalecorrect)-1;
1160         break;
1161     default:
1162         bx -= 160;
1163         sbx = (xdim>>1)+mulscale16(bx, xscalecorrect)-1;
1164         break;
1165     }
1166     rotatesprite(x<<16, y<<16, nScale, 0, nTile, 0, 0, nStat|90, 0, 0, sbx, ydim-1);
1167 }
1168 
1169 int gPackIcons[5] = {
1170     2569, 2564, 2566, 2568, 2560
1171 };
1172 
1173 struct PACKICON2 {
1174     short nTile;
1175     int nScale;
1176     int nYOffs;
1177 };
1178 
1179 PACKICON2 gPackIcons2[] = {
1180     { 519, (int)(65536*0.5), 0 },
1181     { 830, (int)(65536*0.3), 0 },
1182     { 760, (int)(65536*0.6), 0 },
1183     { 839, (int)(65536*0.5), -4 },
1184     { 827, (int)(65536*0.4), 0 },
1185 };
1186 
1187 struct AMMOICON {
1188     short nTile;
1189     int nScale;
1190     int nYOffs;
1191 };
1192 
1193 AMMOICON gAmmoIcons[] = {
1194     { -1, 0, 0 },
1195     { 816, (int)(65536 * 0.5), 0 },
1196     { 619, (int)(65536 * 0.8), 0 },
1197     { 817, (int)(65536 * 0.7), 3 },
1198     { 801, (int)(65536 * 0.5), -6 },
1199     { 589, (int)(65536 * 0.7), 2 },
1200     { 618, (int)(65536 * 0.5), 4 },
1201     { 548, (int)(65536 * 0.3), -6 },
1202     { 820, (int)(65536 * 0.3), -6 },
1203     { 525, (int)(65536 * 0.6), -6 },
1204     { 811, (int)(65536 * 0.5), 2 },
1205     { 810, (int)(65536 * 0.45), 2 },
1206 };
1207 
1208 struct WEAPONICON {
1209     short nTile;
1210     char zOffset;
1211 };
1212 
1213 WEAPONICON gWeaponIcon[] = {
1214     { -1, 0 },
1215     { -1, 0 }, // 1: pitchfork
1216     { 524, 6 }, // 2: flare gun
1217     { 559, 6 }, // 3: shotgun
1218     { 558, 8 }, // 4: tommy gun
1219     { 526, 6 }, // 5: napalm launcher
1220     { 589, 11 }, // 6: dynamite
1221     { 618, 11 }, // 7: spray can
1222     { 539, 6 }, // 8: tesla gun
1223     { 800, 0 }, // 9: life leech
1224     { 525, 11 }, // 10: voodoo doll
1225     { 811, 11 }, // 11: proxy bomb
1226     { 810, 11 }, // 12: remote bomb
1227     { -1, 0 },
1228 };
1229 
1230 int dword_14C508;
1231 
viewDrawStats(PLAYER * pPlayer,int x,int y)1232 void viewDrawStats(PLAYER *pPlayer, int x, int y)
1233 {
1234     const int nFont = 3;
1235     char buffer[128];
1236     if (!gLevelStats)
1237         return;
1238 
1239     int nHeight;
1240     viewGetFontInfo(nFont, NULL, NULL, &nHeight);
1241     sprintf(buffer, "T:%d:%02d.%02d",
1242         (gLevelTime/(kTicsPerSec*60)),
1243         (gLevelTime/kTicsPerSec)%60,
1244         ((gLevelTime%kTicsPerSec)*33)/10
1245         );
1246     viewDrawText(3, buffer, x, y, 20, 0, 0, true, 256);
1247     y += nHeight+1;
1248     if (gGameOptions.nGameType != 3)
1249         sprintf(buffer, "K:%d/%d", gKillMgr.at4, gKillMgr.at0);
1250     else
1251         sprintf(buffer, "K:%d", pPlayer->fragCount);
1252     viewDrawText(3, buffer, x, y, 20, 0, 0, true, 256);
1253     y += nHeight+1;
1254     sprintf(buffer, "S:%d/%d", gSecretMgr.nNormalSecretsFound, gSecretMgr.nAllSecrets);
1255     viewDrawText(3, buffer, x, y, 20, 0, 0, true, 256);
1256 }
1257 
1258 #define kSBarNumberHealth 9220
1259 #define kSBarNumberAmmo 9230
1260 #define kSBarNumberInv 9240
1261 #define kSBarNumberArmor1 9250
1262 #define kSBarNumberArmor2 9260
1263 #define kSBarNumberArmor3 9270
1264 
1265 struct POWERUPDISPLAY
1266 {
1267     int nTile;
1268     float nScaleRatio;
1269     int yOffset;
1270     int remainingDuration;
1271 };
1272 
1273 #define nPowerUps 11
1274 
sortPowerUps(POWERUPDISPLAY * powerups)1275 void sortPowerUps(POWERUPDISPLAY* powerups) {
1276     for (int i = 1; i < nPowerUps; i++)
1277     {
1278         for (int j = 0; j < nPowerUps-i; j++)
1279         {
1280             if (powerups[j].remainingDuration > powerups[j+1].remainingDuration)
1281             {
1282                 POWERUPDISPLAY temp = powerups[j];
1283                 powerups[j] = powerups[j+1];
1284                 powerups[j+1] = temp;
1285             }
1286         }
1287     }
1288 }
1289 
viewDrawPowerUps(PLAYER * pPlayer)1290 void viewDrawPowerUps(PLAYER* pPlayer)
1291 {
1292     if (!gPowerupDuration)
1293         return;
1294 
1295     POWERUPDISPLAY powerups[nPowerUps];
1296     powerups[0] = { gPowerUpInfo[kPwUpShadowCloak].picnum,  0.4f, 0, pPlayer->pwUpTime[kPwUpShadowCloak] }; // Invisibility
1297     powerups[1] = { gPowerUpInfo[kPwUpReflectShots].picnum, 0.4f, 5, pPlayer->pwUpTime[kPwUpReflectShots] }; // Reflects enemy shots
1298     powerups[2] = { gPowerUpInfo[kPwUpDeathMask].picnum, 0.3f, 9, pPlayer->pwUpTime[kPwUpDeathMask] }; // Invulnerability
1299     powerups[3] = { gPowerUpInfo[kPwUpTwoGuns].picnum, 0.3f, 5, pPlayer->pwUpTime[kPwUpTwoGuns] }; // Guns Akimbo
1300     powerups[4] = { gPowerUpInfo[kPwUpShadowCloakUseless].picnum, 0.4f, 9, pPlayer->pwUpTime[kPwUpShadowCloakUseless] }; // Does nothing, only appears at near the end of Cryptic Passage's Lost Monastery (CP04)
1301 
1302     // Not in official maps, but custom maps can use them
1303     powerups[5] = { gPowerUpInfo[kPwUpFeatherFall].picnum, 0.3f, 7, pPlayer->pwUpTime[kPwUpFeatherFall] }; // Makes player immune to fall damage
1304     powerups[6] = { gPowerUpInfo[kPwUpGasMask].picnum, 0.4f, 4, pPlayer->pwUpTime[kPwUpGasMask] }; // Makes player immune to choke damage
1305     powerups[7] = { gPowerUpInfo[kPwUpDoppleganger].picnum, 0.5f, 5, pPlayer->pwUpTime[kPwUpDoppleganger] }; // Works in multiplayer, it swaps player's team colors, so enemy team player thinks it's a team mate
1306     powerups[8] = { gPowerUpInfo[kPwUpAsbestArmor].picnum, 0.3f, 9, pPlayer->pwUpTime[kPwUpAsbestArmor] }; // Makes player immune to fire damage and draws HUD
1307     powerups[9] = { gPowerUpInfo[kPwUpGrowShroom].picnum, 0.4f, 4, pPlayer->pwUpTime[kPwUpGrowShroom] }; // Grows player size, works only if gModernMap == true
1308     powerups[10] = { gPowerUpInfo[kPwUpShrinkShroom].picnum, 0.4f, 4, pPlayer->pwUpTime[kPwUpShrinkShroom] }; // Shrinks player size, works only if gModernMap == true
1309 
1310     sortPowerUps(powerups);
1311 
1312     const int warningTime = 5;
1313     const int x = 15;
1314     int y = 50;
1315     for (int i = 0; i < nPowerUps; i++)
1316     {
1317         if (powerups[i].remainingDuration)
1318         {
1319             int remainingSeconds = powerups[i].remainingDuration / 100;
1320             if (remainingSeconds > warningTime || ((int)totalclock & 32))
1321             {
1322                 DrawStatMaskedSprite(powerups[i].nTile, x, y + powerups[i].yOffset, 0, 0, 256, (int)(65536 * powerups[i].nScaleRatio));
1323             }
1324 
1325             DrawStatNumber("%d", remainingSeconds, kSBarNumberInv, x + 15, y, 0, remainingSeconds > warningTime ? 0 : 2, 256, 65536 * 0.5);
1326             y += 20;
1327         }
1328     }
1329 }
1330 
viewDrawMapTitle(void)1331 void viewDrawMapTitle(void)
1332 {
1333     if (!gShowMapTitle || gGameMenuMgr.m_bActive)
1334         return;
1335 
1336     int const fadeStartTic = int((videoGetRenderMode() == REND_CLASSIC ? 1.25f : 1.f)*kTicsPerSec);
1337     int const fadeEndTic = int(1.5f*kTicsPerSec);
1338     if (gLevelTime > fadeEndTic)
1339         return;
1340     uint8_t const alpha = clamp((gLevelTime-fadeStartTic)*255/(fadeEndTic-fadeStartTic), 0, 255);
1341 
1342     if (alpha != 255)
1343     {
1344         viewDrawText(1, levelGetTitle(), 160, 50, -128, 0, 1, 1, 0, alpha);
1345     }
1346 }
1347 
viewDrawAimedPlayerName(void)1348 void viewDrawAimedPlayerName(void)
1349 {
1350     if (!gShowPlayerNames || (gView->aim.dx == 0 && gView->aim.dy == 0))
1351         return;
1352 
1353     int hit = HitScan(gView->pSprite, gView->pSprite->z, gView->aim.dx, gView->aim.dy, gView->aim.dz, CLIPMASK0, 512);
1354     if (hit == 3)
1355     {
1356         spritetype* pSprite = &sprite[gHitInfo.hitsprite];
1357         if (IsPlayerSprite(pSprite))
1358         {
1359             char nPlayer = pSprite->type-kDudePlayer1;
1360             char* szName = gProfile[nPlayer].name;
1361             int nPalette = (gPlayer[nPlayer].teamId&3)+11;
1362             viewDrawText(4, szName, 160, 125, -128, nPalette, 1, 1);
1363         }
1364     }
1365 }
1366 
viewDrawPack(PLAYER * pPlayer,int x,int y)1367 void viewDrawPack(PLAYER *pPlayer, int x, int y)
1368 {
1369     int packs[5];
1370     if (pPlayer->packItemTime)
1371     {
1372         int nPacks = 0;
1373         int width = 0;
1374         for (int i = 0; i < 5; i++)
1375         {
1376             if (pPlayer->packSlots[i].curAmount)
1377             {
1378                 packs[nPacks++] = i;
1379                 width += tilesiz[gPackIcons[i]].x + 1;
1380             }
1381         }
1382         width /= 2;
1383         x -= width;
1384         for (int i = 0; i < nPacks; i++)
1385         {
1386             int nPack = packs[i];
1387             DrawStatSprite(2568, x+1, y-8);
1388             DrawStatSprite(2568, x+1, y-6);
1389             DrawStatSprite(gPackIcons[nPack], x+1, y+1);
1390             if (nPack == pPlayer->packItemId)
1391                 DrawStatMaskedSprite(2559, x+1, y+1);
1392             int nShade;
1393             if (pPlayer->packSlots[nPack].isActive)
1394                 nShade = 4;
1395             else
1396                 nShade = 24;
1397             DrawStatNumber("%3d", pPlayer->packSlots[nPack].curAmount, 2250, x-4, y-13, nShade, 0);
1398             x += tilesiz[gPackIcons[nPack]].x + 1;
1399         }
1400     }
1401     if (pPlayer->packItemTime != dword_14C508)
1402     {
1403         viewUpdatePages();
1404     }
1405     dword_14C508 = pPlayer->packItemTime;
1406 }
1407 
DrawPackItemInStatusBar(PLAYER * pPlayer,int x,int y,int x2,int y2,int nStat)1408 void DrawPackItemInStatusBar(PLAYER *pPlayer, int x, int y, int x2, int y2, int nStat)
1409 {
1410     if (pPlayer->packItemId < 0) return;
1411 
1412     DrawStatSprite(gPackIcons[pPlayer->packItemId], x, y, 0, 0, nStat);
1413     DrawStatNumber("%3d", pPlayer->packSlots[pPlayer->packItemId].curAmount, 2250, x2, y2, 0, 0, nStat);
1414 }
1415 
DrawPackItemInStatusBar2(PLAYER * pPlayer,int x,int y,int x2,int y2,int nStat,int nScale)1416 void DrawPackItemInStatusBar2(PLAYER *pPlayer, int x, int y, int x2, int y2, int nStat, int nScale)
1417 {
1418     if (pPlayer->packItemId < 0) return;
1419 
1420     DrawStatMaskedSprite(gPackIcons2[pPlayer->packItemId].nTile, x, y+gPackIcons2[pPlayer->packItemId].nYOffs, 0, 0, nStat, gPackIcons2[pPlayer->packItemId].nScale);
1421     DrawStatNumber("%3d", pPlayer->packSlots[pPlayer->packItemId].curAmount, kSBarNumberInv, x2, y2, 0, 0, nStat, nScale);
1422 }
1423 
viewDrawPlayerSlots(void)1424 void viewDrawPlayerSlots(void)
1425 {
1426     for (int nRows = (gNetPlayers - 1) / 4; nRows >= 0; nRows--)
1427     {
1428         for (int nCol = 0; nCol < 4; nCol++)
1429         {
1430             DrawStatSprite(2229, 40 + nCol * 80, 4 + nRows * 9, 16);
1431         }
1432     }
1433 }
1434 
1435 char gTempStr[128];
1436 
viewDrawPlayerFrags(void)1437 void viewDrawPlayerFrags(void)
1438 {
1439     viewDrawPlayerSlots();
1440     for (int i = 0, p = connecthead; p >= 0; i++, p = connectpoint2[p])
1441     {
1442         int x = 80 * (i & 3);
1443         int y = 9 * (i / 4);
1444         int col = gPlayer[p].teamId & 3;
1445         char* name = gProfile[p].name;
1446         if (gProfile[p].skill == 2)
1447             sprintf(gTempStr, "%s", name);
1448         else
1449             sprintf(gTempStr, "%s [%d]", name, gProfile[p].skill);
1450         Bstrupr(gTempStr);
1451         viewDrawText(4, gTempStr, x + 4, y + 1, -128, 11 + col, 0, 0);
1452         sprintf(gTempStr, "%2d", gPlayer[p].fragCount);
1453         viewDrawText(4, gTempStr, x + 76, y + 1, -128, 11 + col, 2, 0);
1454     }
1455 }
1456 
viewDrawPlayerFlags(void)1457 void viewDrawPlayerFlags(void)
1458 {
1459     viewDrawPlayerSlots();
1460     for (int i = 0, p = connecthead; p >= 0; i++, p = connectpoint2[p])
1461     {
1462         int x = 80 * (i & 3);
1463         int y = 9 * (i / 4);
1464         int col = gPlayer[p].teamId & 3;
1465         char* name = gProfile[p].name;
1466         if (gProfile[p].skill == 2)
1467             sprintf(gTempStr, "%s", name);
1468         else
1469             sprintf(gTempStr, "%s [%d]", name, gProfile[p].skill);
1470         Bstrupr(gTempStr);
1471         viewDrawText(4, gTempStr, x + 4, y + 1, -128, 11 + col, 0, 0);
1472 
1473         sprintf(gTempStr, "F");
1474         x += 76;
1475         if (gPlayer[p].hasFlag & 2)
1476         {
1477             viewDrawText(4, gTempStr, x, y + 1, -128, 12, 2, 0);
1478             x -= 6;
1479         }
1480 
1481         if (gPlayer[p].hasFlag & 1)
1482             viewDrawText(4, gTempStr, x, y + 1, -128, 11, 2, 0);
1483     }
1484 }
1485 
viewDrawCtfHudVanilla(ClockTicks arg)1486 void viewDrawCtfHudVanilla(ClockTicks arg)
1487 {
1488     int x = 1, y = 1;
1489     if (dword_21EFD0[0] == 0 || ((int)totalclock & 8))
1490     {
1491         viewDrawText(0, "BLUE", x, y, -128, 10, 0, 0, 256);
1492         dword_21EFD0[0] = dword_21EFD0[0] - arg;
1493         if (dword_21EFD0[0] < 0)
1494             dword_21EFD0[0] = 0;
1495         sprintf(gTempStr, "%-3d", dword_21EFB0[0]);
1496         viewDrawText(0, gTempStr, x, y + 10, -128, 10, 0, 0, 256);
1497     }
1498     x = 319;
1499     if (dword_21EFD0[1] == 0 || ((int)totalclock & 8))
1500     {
1501         viewDrawText(0, "RED", x, y, -128, 7, 2, 0, 512);
1502         dword_21EFD0[1] = dword_21EFD0[1] - arg;
1503         if (dword_21EFD0[1] < 0)
1504             dword_21EFD0[1] = 0;
1505         sprintf(gTempStr, "%3d", dword_21EFB0[1]);
1506         viewDrawText(0, gTempStr, x, y + 10, -128, 7, 2, 0, 512);
1507     }
1508 }
1509 
flashTeamScore(ClockTicks arg,int team,bool show)1510 void flashTeamScore(ClockTicks arg, int team, bool show)
1511 {
1512     dassert(0 == team || 1 == team); // 0: blue, 1: red
1513 
1514     if (dword_21EFD0[team] == 0 || ((int)totalclock & 8))
1515     {
1516         dword_21EFD0[team] = dword_21EFD0[team] - arg;
1517         if (dword_21EFD0[team] < 0)
1518             dword_21EFD0[team] = 0;
1519 
1520         if (show)
1521             DrawStatNumber("%d", dword_21EFB0[team], kSBarNumberInv, 290, team ? 125 : 90, 0, team ? 2 : 10, 512, 65536 * 0.75);
1522     }
1523 }
1524 
viewDrawCtfHud(ClockTicks arg)1525 void viewDrawCtfHud(ClockTicks arg)
1526 {
1527     if (0 == gViewSize)
1528     {
1529         flashTeamScore(arg, 0, false);
1530         flashTeamScore(arg, 1, false);
1531         return;
1532     }
1533 
1534     bool blueFlagTaken = false;
1535     bool redFlagTaken = false;
1536     int blueFlagCarrierColor = 0;
1537     int redFlagCarrierColor = 0;
1538     for (int i = 0, p = connecthead; p >= 0; i++, p = connectpoint2[p])
1539     {
1540         if ((gPlayer[p].hasFlag & 1) != 0)
1541         {
1542             blueFlagTaken = true;
1543             blueFlagCarrierColor = gPlayer[p].teamId & 3;
1544         }
1545         if ((gPlayer[p].hasFlag & 2) != 0)
1546         {
1547             redFlagTaken = true;
1548             redFlagCarrierColor = gPlayer[p].teamId & 3;
1549         }
1550     }
1551 
1552     bool meHaveBlueFlag = gMe->hasFlag & 1;
1553     DrawStatMaskedSprite(meHaveBlueFlag ? 3558 : 3559, 320, 75, 0, 10, 512, 65536 * 0.35);
1554     if (gBlueFlagDropped)
1555         DrawStatMaskedSprite(2332, 305, 83, 0, 10, 512, 65536);
1556     else if (blueFlagTaken)
1557         DrawStatMaskedSprite(4097, 307, 77, 0, blueFlagCarrierColor ? 2 : 10, 512, 65536);
1558     flashTeamScore(arg, 0, true);
1559 
1560     bool meHaveRedFlag = gMe->hasFlag & 2;
1561     DrawStatMaskedSprite(meHaveRedFlag ? 3558 : 3559, 320, 110, 0, 2, 512, 65536 * 0.35);
1562     if (gRedFlagDropped)
1563         DrawStatMaskedSprite(2332, 305, 117, 0, 2, 512, 65536);
1564     else if (redFlagTaken)
1565         DrawStatMaskedSprite(4097, 307, 111, 0, redFlagCarrierColor ? 2 : 10, 512, 65536);
1566     flashTeamScore(arg, 1, true);
1567 }
1568 
UpdateStatusBar(ClockTicks arg)1569 void UpdateStatusBar(ClockTicks arg)
1570 {
1571     PLAYER *pPlayer = gView;
1572     XSPRITE *pXSprite = pPlayer->pXSprite;
1573 
1574     int nPalette = 0;
1575 
1576     if (gGameOptions.nGameType == 3)
1577     {
1578         if (pPlayer->teamId & 1)
1579             nPalette = 7;
1580         else
1581             nPalette = 10;
1582     }
1583 
1584     if (gViewSize < 0) return;
1585 
1586     if (gViewSize == 1)
1587     {
1588         DrawStatMaskedSprite(2169, 12, 195, 0, 0, 256, (int)(65536*0.56));
1589         DrawStatNumber("%d", pXSprite->health>>4, kSBarNumberHealth, 28, 187, 0, 0, 256);
1590         if (pPlayer->armor[1])
1591         {
1592             DrawStatMaskedSprite(2578, 70, 186, 0, 0, 256, (int)(65536*0.5));
1593             DrawStatNumber("%3d", pPlayer->armor[1]>>4, kSBarNumberArmor2, 83, 187, 0, 0, 256, (int)(65536*0.65));
1594         }
1595         if (pPlayer->armor[0])
1596         {
1597             DrawStatMaskedSprite(2586, 112, 195, 0, 0, 256, (int)(65536*0.5));
1598             DrawStatNumber("%3d", pPlayer->armor[0]>>4, kSBarNumberArmor1, 125, 187, 0, 0, 256, (int)(65536*0.65));
1599         }
1600         if (pPlayer->armor[2])
1601         {
1602             DrawStatMaskedSprite(2602, 155, 196, 0, 0, 256, (int)(65536*0.5));
1603             DrawStatNumber("%3d", pPlayer->armor[2]>>4, kSBarNumberArmor3, 170, 187, 0, 0, 256, (int)(65536*0.65));
1604         }
1605 
1606         DrawPackItemInStatusBar2(pPlayer, 225, 194, 240, 187, 512, (int)(65536*0.7));
1607 
1608         if (pPlayer->curWeapon && pPlayer->weaponAmmo != -1)
1609         {
1610             int num = pPlayer->ammoCount[pPlayer->weaponAmmo];
1611             if (pPlayer->weaponAmmo == 6)
1612                 num /= 10;
1613             if ((unsigned int)gAmmoIcons[pPlayer->weaponAmmo].nTile < kMaxTiles)
1614                 DrawStatMaskedSprite(gAmmoIcons[pPlayer->weaponAmmo].nTile, 304, 192+gAmmoIcons[pPlayer->weaponAmmo].nYOffs,
1615                     0, 0, 512, gAmmoIcons[pPlayer->weaponAmmo].nScale);
1616             DrawStatNumber("%3d", num, kSBarNumberAmmo, 267, 187, 0, 0, 512);
1617         }
1618 
1619         for (int i = 0; i < 6; i++)
1620         {
1621             if (pPlayer->hasKey[i+1])
1622                 DrawStatMaskedSprite(2552+i, 260+10*i, 170, 0, 0, 512, (int)(65536*0.25));
1623         }
1624 
1625         if (pPlayer->throwPower)
1626             TileHGauge(2260, 124, 175-10, pPlayer->throwPower, 65536);
1627         else
1628             viewDrawPack(pPlayer, 166, 200-tilesiz[2201].y/2-30);
1629         viewDrawStats(pPlayer, 2, 140);
1630         viewDrawPowerUps(pPlayer);
1631     }
1632     else if (gViewSize <= 2)
1633     {
1634         if (pPlayer->throwPower)
1635             TileHGauge(2260, 124, 175, pPlayer->throwPower, 65536);
1636         else
1637             viewDrawPack(pPlayer, 166, 200-tilesiz[2201].y/2);
1638     }
1639     if (gViewSize == 2)
1640     {
1641         DrawStatSprite(2201, 34, 187, 16, nPalette, 256);
1642         if (pXSprite->health >= 16 || ((int)totalclock&16) || pXSprite->health == 0)
1643         {
1644             DrawStatNumber("%3d", pXSprite->health>>4, 2190, 8, 183, 0, 0, 256);
1645         }
1646         if (pPlayer->curWeapon && pPlayer->weaponAmmo != -1)
1647         {
1648             int num = pPlayer->ammoCount[pPlayer->weaponAmmo];
1649             if (pPlayer->weaponAmmo == 6)
1650                 num /= 10;
1651             DrawStatNumber("%3d", num, 2240, 42, 183, 0, 0, 256);
1652         }
1653         DrawStatSprite(2173, 284, 187, 16, nPalette, 512);
1654         if (pPlayer->armor[1])
1655         {
1656             TileHGauge(2207, 250, 175, pPlayer->armor[1], 3200, 512);
1657             DrawStatNumber("%3d", pPlayer->armor[1]>>4, 2230, 255, 178, 0, 0, 512);
1658         }
1659         if (pPlayer->armor[0])
1660         {
1661             TileHGauge(2209, 250, 183, pPlayer->armor[0], 3200, 512);
1662             DrawStatNumber("%3d", pPlayer->armor[0]>>4, 2230, 255, 186, 0, 0, 512);
1663         }
1664         if (pPlayer->armor[2])
1665         {
1666             TileHGauge(2208, 250, 191, pPlayer->armor[2], 3200, 512);
1667             DrawStatNumber("%3d", pPlayer->armor[2]>>4, 2230, 255, 194, 0, 0, 512);
1668         }
1669         DrawPackItemInStatusBar(pPlayer, 286, 186, 302, 183, 512);
1670 
1671         for (int i = 0; i < 6; i++)
1672         {
1673             int nTile = 2220+i;
1674             int x, nStat = 0;
1675             int y = 200-6;
1676             if (i&1)
1677             {
1678                 x = 320-(78+(i>>1)*10);
1679                 nStat |= 512;
1680             }
1681             else
1682             {
1683                 x = 73+(i>>1)*10;
1684                 nStat |= 256;
1685             }
1686             if (pPlayer->hasKey[i+1])
1687                 DrawStatSprite(nTile, x, y, 0, 0, nStat);
1688 #if 0
1689             else
1690                 DrawStatSprite(nTile, x, y, 40, 5, nStat);
1691 #endif
1692         }
1693         viewDrawStats(pPlayer, 2, 140);
1694         viewDrawPowerUps(pPlayer);
1695     }
1696     else if (gViewSize > 2)
1697     {
1698         viewDrawPack(pPlayer, 160, 200-tilesiz[2200].y);
1699         DrawStatMaskedSprite(2200, 160, 172, 16, nPalette);
1700         DrawPackItemInStatusBar(pPlayer, 265, 186, 260, 172);
1701         if (pXSprite->health >= 16 || ((int)totalclock&16) || pXSprite->health == 0)
1702         {
1703             DrawStatNumber("%3d", pXSprite->health>>4, 2190, 86, 183, 0, 0);
1704         }
1705         if (pPlayer->curWeapon && pPlayer->weaponAmmo != -1)
1706         {
1707             int num = pPlayer->ammoCount[pPlayer->weaponAmmo];
1708             if (pPlayer->weaponAmmo == 6)
1709                 num /= 10;
1710             DrawStatNumber("%3d", num, 2240, 216, 183, 0, 0);
1711         }
1712         for (int i = 9; i >= 1; i--)
1713         {
1714             int x = 135+((i-1)/3)*23;
1715             int y = 182+((i-1)%3)*6;
1716             int num = pPlayer->ammoCount[i];
1717             if (i == 6)
1718                 num /= 10;
1719             if (i == pPlayer->weaponAmmo)
1720             {
1721                 DrawStatNumber("%3d", num, 2230, x, y, -128, 10);
1722             }
1723             else
1724             {
1725                 DrawStatNumber("%3d", num, 2230, x, y, 32, 10);
1726             }
1727         }
1728 
1729         if (pPlayer->weaponAmmo == 10)
1730         {
1731             DrawStatNumber("%2d", pPlayer->ammoCount[10], 2230, 291, 194, -128, 10);
1732         }
1733         else
1734         {
1735             DrawStatNumber("%2d", pPlayer->ammoCount[10], 2230, 291, 194, 32, 10);
1736         }
1737 
1738         if (pPlayer->weaponAmmo == 11)
1739         {
1740             DrawStatNumber("%2d", pPlayer->ammoCount[11], 2230, 309, 194, -128, 10);
1741         }
1742         else
1743         {
1744             DrawStatNumber("%2d", pPlayer->ammoCount[11], 2230, 309, 194, 32, 10);
1745         }
1746 
1747         if (pPlayer->armor[1])
1748         {
1749             TileHGauge(2207, 44, 174, pPlayer->armor[1], 3200);
1750             DrawStatNumber("%3d", pPlayer->armor[1]>>4, 2230, 50, 177, 0, 0);
1751         }
1752         if (pPlayer->armor[0])
1753         {
1754             TileHGauge(2209, 44, 182, pPlayer->armor[0], 3200);
1755             DrawStatNumber("%3d", pPlayer->armor[0]>>4, 2230, 50, 185, 0, 0);
1756         }
1757         if (pPlayer->armor[2])
1758         {
1759             TileHGauge(2208, 44, 190, pPlayer->armor[2], 3200);
1760             DrawStatNumber("%3d", pPlayer->armor[2]>>4, 2230, 50, 193, 0, 0);
1761         }
1762         sprintf(gTempStr, "v%s", GetVersionString());
1763         viewDrawText(3, gTempStr, 20, 191, 32, 0, 1, 0);
1764 
1765         for (int i = 0; i < 6; i++)
1766         {
1767             int nTile = 2220+i;
1768             int x = 73+(i&1)*173;
1769             int y = 171+(i>>1)*11;
1770             if (pPlayer->hasKey[i+1])
1771                 DrawStatSprite(nTile, x, y);
1772             else
1773                 DrawStatSprite(nTile, x, y, 40, 5);
1774         }
1775         DrawStatMaskedSprite(2202, 118, 185, pPlayer->isRunning ? 16 : 40);
1776         DrawStatMaskedSprite(2202, 201, 185, pPlayer->isRunning ? 16 : 40);
1777         if (pPlayer->throwPower)
1778         {
1779             TileHGauge(2260, 124, 175, pPlayer->throwPower, 65536);
1780         }
1781         viewDrawStats(pPlayer, 2, 140);
1782         viewDrawPowerUps(pPlayer);
1783     }
1784 
1785     if (gGameOptions.nGameType < 1) return;
1786 
1787     if (gGameOptions.nGameType == 3)
1788     {
1789         if (VanillaMode())
1790         {
1791             viewDrawCtfHudVanilla(arg);
1792         }
1793         else
1794         {
1795             viewDrawCtfHud(arg);
1796             viewDrawPlayerFlags();
1797         }
1798     }
1799     else
1800     {
1801         viewDrawPlayerFrags();
1802     }
1803 }
1804 
viewPrecacheTiles(void)1805 void viewPrecacheTiles(void)
1806 {
1807     tilePrecacheTile(2173, 0);
1808     tilePrecacheTile(2200, 0);
1809     tilePrecacheTile(2201, 0);
1810     tilePrecacheTile(2202, 0);
1811     tilePrecacheTile(2207, 0);
1812     tilePrecacheTile(2208, 0);
1813     tilePrecacheTile(2209, 0);
1814     tilePrecacheTile(2229, 0);
1815     tilePrecacheTile(2260, 0);
1816     tilePrecacheTile(2559, 0);
1817     tilePrecacheTile(2169, 0);
1818     tilePrecacheTile(2578, 0);
1819     tilePrecacheTile(2586, 0);
1820     tilePrecacheTile(2602, 0);
1821     for (int i = 0; i < 10; i++)
1822     {
1823         tilePrecacheTile(2190 + i, 0);
1824         tilePrecacheTile(2230 + i, 0);
1825         tilePrecacheTile(2240 + i, 0);
1826         tilePrecacheTile(2250 + i, 0);
1827         tilePrecacheTile(kSBarNumberHealth + i, 0);
1828         tilePrecacheTile(kSBarNumberAmmo + i, 0);
1829         tilePrecacheTile(kSBarNumberInv + i, 0);
1830         tilePrecacheTile(kSBarNumberArmor1 + i, 0);
1831         tilePrecacheTile(kSBarNumberArmor2 + i, 0);
1832         tilePrecacheTile(kSBarNumberArmor3 + i, 0);
1833     }
1834     for (int i = 0; i < 5; i++)
1835     {
1836         tilePrecacheTile(gPackIcons[i], 0);
1837         tilePrecacheTile(gPackIcons2[i].nTile, 0);
1838     }
1839     for (int i = 0; i < 6; i++)
1840     {
1841         tilePrecacheTile(2220 + i, 0);
1842         tilePrecacheTile(2552 + i, 0);
1843     }
1844 }
1845 
1846 int *lensTable;
1847 
1848 int gZoom = 1024;
1849 
1850 int dword_172CE0[16][3];
1851 
viewInit(void)1852 void viewInit(void)
1853 {
1854     initprintf("Initializing status bar\n");
1855     InitStatusBar();
1856     FontSet(0, 4096, 0);
1857     FontSet(1, 4192, 1);
1858     FontSet(2, 4288, 1);
1859     FontSet(3, 4384, 1);
1860     FontSet(4, 4480, 0);
1861 
1862     DICTNODE *hLens = gSysRes.Lookup("LENS", "DAT");
1863     dassert(hLens != NULL);
1864     dassert(gSysRes.Size(hLens) == kLensSize * kLensSize * sizeof(int));
1865 
1866     lensTable = (int*)gSysRes.Lock(hLens);
1867 #if B_BIG_ENDIAN == 1
1868     for (int i = 0; i < kLensSize*kLensSize; i++)
1869     {
1870         lensTable[i] = B_LITTLE32(lensTable[i]);
1871     }
1872 #endif
1873     char *data = tileAllocTile(4077, kLensSize, kLensSize, 0, 0);
1874     memset(data, 255, kLensSize*kLensSize);
1875     gGameMessageMgr.SetState(gMessageState);
1876     gGameMessageMgr.SetCoordinates(1, 1);
1877     char nFont;
1878     if (gMessageFont == 0)
1879         nFont = 3;
1880     else
1881         nFont = 0;
1882 
1883     gGameMessageMgr.SetFont(nFont);
1884     gGameMessageMgr.SetMaxMessages(gMessageCount);
1885     gGameMessageMgr.SetMessageTime(gMessageTime);
1886 
1887     for (int i = 0; i < 16; i++)
1888     {
1889         dword_172CE0[i][0] = mulscale16(wrand(), 2048);
1890         dword_172CE0[i][1] = mulscale16(wrand(), 2048);
1891         dword_172CE0[i][2] = mulscale16(wrand(), 2048);
1892     }
1893     gViewMap.sub_25C38(0, 0, gZoom, 0, gFollowMap);
1894 
1895     g_frameDelay = calcFrameDelay(r_maxfps + r_maxfpsoffset);
1896 
1897     bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC;
1898 }
1899 
viewResizeView(int size)1900 void viewResizeView(int size)
1901 {
1902     int xdimcorrect = ClipHigh(scale(ydim, 4, 3), xdim);
1903     gViewXCenter = xdim-xdim/2;
1904     gViewYCenter = ydim-ydim/2;
1905     xscale = divscale16(xdim, 320);
1906     xscalecorrect = divscale16(xdimcorrect, 320);
1907     yscale = divscale16(ydim, 200);
1908     xstep = divscale16(320, xdim);
1909     ystep = divscale16(200, ydim);
1910     gViewSize = ClipRange(size, 0, 7);
1911     if (gViewSize <= 2)
1912     {
1913         gViewX0 = 0;
1914         gViewX1 = xdim-1;
1915         gViewY0 = 0;
1916         gViewY1 = ydim-1;
1917         if (gGameOptions.nGameType > 0 && gGameOptions.nGameType <= 3)
1918         {
1919             gViewY0 = (tilesiz[2229].y*ydim*((gNetPlayers+3)/4))/200;
1920         }
1921         gViewX0S = divscale16(gViewX0, xscalecorrect);
1922         gViewY0S = divscale16(gViewY0, yscale);
1923         gViewX1S = divscale16(gViewX1, xscalecorrect);
1924         gViewY1S = divscale16(gViewY1, yscale);
1925     }
1926     else
1927     {
1928         gViewX0 = 0;
1929         gViewY0 = 0;
1930         gViewX1 = xdim-1;
1931         gViewY1 = ydim-1-(25*ydim)/200;
1932         if (gGameOptions.nGameType > 0 && gGameOptions.nGameType <= 3)
1933         {
1934             gViewY0 = (tilesiz[2229].y*ydim*((gNetPlayers+3)/4))/200;
1935         }
1936 
1937         int height = gViewY1-gViewY0;
1938         gViewX0 += mulscale16(xdim*(gViewSize-3),4096);
1939         gViewX1 -= mulscale16(xdim*(gViewSize-3),4096);
1940         gViewY0 += mulscale16(height*(gViewSize-3),4096);
1941         gViewY1 -= mulscale16(height*(gViewSize-3),4096);
1942         gViewX0S = divscale16(gViewX0, xscalecorrect);
1943         gViewY0S = divscale16(gViewY0, yscale);
1944         gViewX1S = divscale16(gViewX1, xscalecorrect);
1945         gViewY1S = divscale16(gViewY1, yscale);
1946     }
1947     videoSetViewableArea(gViewX0, gViewY0, gViewX1, gViewY1);
1948     gGameMessageMgr.SetCoordinates(gViewX0S + 1, gViewY0S + 1);
1949     viewSetCrosshairColor(CrosshairColors.r, CrosshairColors.g, CrosshairColors.b);
1950     viewUpdatePages();
1951 }
1952 
1953 #define kBackTile 253
1954 
UpdateFrame(void)1955 void UpdateFrame(void)
1956 {
1957     viewTileSprite(kBackTile, 0, 0, 0, 0, xdim, gViewY0-3);
1958     viewTileSprite(kBackTile, 0, 0, 0, gViewY1+4, xdim, ydim);
1959     viewTileSprite(kBackTile, 0, 0, 0, gViewY0-3, gViewX0-3, gViewY1+4);
1960     viewTileSprite(kBackTile, 0, 0, gViewX1+4, gViewY0-3, xdim, gViewY1+4);
1961 
1962     viewTileSprite(kBackTile, 20, 0, gViewX0-3, gViewY0-3, gViewX0, gViewY1+1);
1963     viewTileSprite(kBackTile, 20, 0, gViewX0, gViewY0-3, gViewX1+4, gViewY0);
1964     viewTileSprite(kBackTile, 10, 1, gViewX1+1, gViewY0, gViewX1+4, gViewY1+4);
1965     viewTileSprite(kBackTile, 10, 1, gViewX0-3, gViewY1+1, gViewX1+1, gViewY1+4);
1966 }
1967 
viewDrawInterface(ClockTicks arg)1968 void viewDrawInterface(ClockTicks arg)
1969 {
1970     if (gViewMode == 3/* && gViewSize >= 3*/ && (pcBackground != 0 || videoGetRenderMode() >= REND_POLYMOST))
1971     {
1972         UpdateFrame();
1973         pcBackground--;
1974     }
1975     UpdateStatusBar(arg);
1976 }
1977 
1978 static fix16_t gCameraAng;
1979 
viewInsertTSprite(int nSector,int nStatnum,T const * const pSprite)1980 template<typename T> tspritetype* viewInsertTSprite(int nSector, int nStatnum, T const * const pSprite)
1981 {
1982     int nTSprite = spritesortcnt;
1983     tspritetype *pTSprite = &tsprite[nTSprite];
1984     memset(pTSprite, 0, sizeof(tspritetype));
1985     pTSprite->cstat = 128;
1986     pTSprite->xrepeat = 64;
1987     pTSprite->yrepeat = 64;
1988     pTSprite->owner = -1;
1989     pTSprite->extra = -1;
1990     pTSprite->type = -spritesortcnt;
1991     pTSprite->statnum = nStatnum;
1992     pTSprite->sectnum = nSector;
1993     spritesortcnt++;
1994     if (pSprite)
1995     {
1996         pTSprite->x = pSprite->x;
1997         pTSprite->y = pSprite->y;
1998         pTSprite->z = pSprite->z;
1999         pTSprite->owner = pSprite->owner;
2000         pTSprite->ang = pSprite->ang;
2001     }
2002     if (videoGetRenderMode() >= REND_POLYMOST)
2003     {
2004         pTSprite->x += Cos(gCameraAng)>>25;
2005         pTSprite->y += Sin(gCameraAng)>>25;
2006     }
2007     return pTSprite;
2008 }
2009 
2010 int effectDetail[] = {
2011     4, 4, 4, 4, 0, 0, 0, 0, 0, 1, 4, 4, 0, 0, 0, 1, 0, 0, 0
2012 };
2013 
viewAddEffect(int nTSprite,VIEW_EFFECT nViewEffect)2014 tspritetype *viewAddEffect(int nTSprite, VIEW_EFFECT nViewEffect)
2015 {
2016     dassert(nViewEffect >= 0 && nViewEffect < kViewEffectMax);
2017     auto pTSprite = &tsprite[nTSprite];
2018     if (gDetail < effectDetail[nViewEffect] || nTSprite >= kMaxViewSprites) return NULL;
2019     switch (nViewEffect)
2020     {
2021 #ifdef NOONE_EXTENSIONS
2022     case kViewEffectSpotProgress: {
2023         XSPRITE* pXSprite = &xsprite[pTSprite->extra];
2024         int perc = (100 * pXSprite->data3) / kMaxPatrolSpotValue;
2025         int width = (94 * pXSprite->data3) / kMaxPatrolSpotValue;
2026 
2027         int top, bottom;
2028         GetSpriteExtents(pTSprite, &top, &bottom);
2029 
2030         if (rendmode != REND_CLASSIC) {
2031 
2032             auto pNSprite2 = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2033             pNSprite2->picnum = 2203;
2034 
2035             pNSprite2->xrepeat = width;
2036             pNSprite2->yrepeat = 20;
2037             pNSprite2->pal = 10;
2038             if (perc >= 75) pNSprite2->pal = 0;
2039             else if (perc >= 50) pNSprite2->pal = 6;
2040 
2041             pNSprite2->z = top - 2048;
2042             pNSprite2->shade = -128;
2043 
2044 
2045         } else {
2046 
2047 
2048             auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2049             auto pNSprite2 = viewInsertTSprite(pTSprite->sectnum, 32766, pTSprite);
2050             pNSprite->cstat |= CSTAT_SPRITE_TRANSLUCENT_INVERT | CSTAT_SPRITE_TRANSLUCENT;
2051 
2052             pNSprite->picnum = 2229;
2053             pNSprite2->picnum = 2203;
2054 
2055             pNSprite->xoffset = -1;
2056             pNSprite->xrepeat = 40;
2057             pNSprite->yrepeat = 64;
2058             pNSprite->pal = 5;
2059 
2060             pNSprite2->xrepeat = width;
2061             pNSprite2->yrepeat = 34;
2062             pNSprite2->pal = 10;
2063             if (perc >= 75) pNSprite2->pal = 0;
2064             else if (perc >= 50) pNSprite2->pal = 6;
2065 
2066             pNSprite->z = pNSprite2->z = top - 2048;
2067             pNSprite->shade = pNSprite2->shade = -128;
2068 
2069         }
2070         break;
2071     }
2072 #endif
2073     case kViewEffectAtom:
2074         for (int i = 0; i < 16; i++)
2075         {
2076             auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2077             int ang = ((int)gFrameClock*2048)/120;
2078             int nRand1 = dword_172CE0[i][0];
2079             int nRand2 = dword_172CE0[i][1];
2080             int nRand3 = dword_172CE0[i][2];
2081             ang += nRand3;
2082             int x = mulscale30(512, Cos(ang));
2083             int y = mulscale30(512, Sin(ang));
2084             int z = 0;
2085             RotateYZ(&x, &y, &z, nRand1);
2086             RotateXZ(&x, &y, &z, nRand2);
2087             pNSprite->x = pTSprite->x + x;
2088             pNSprite->y = pTSprite->y + y;
2089             pNSprite->z = pTSprite->z + (z<<4);
2090             pNSprite->picnum = 1720;
2091             pNSprite->shade = -128;
2092         }
2093         break;
2094     case kViewEffectFlag:
2095     case kViewEffectBigFlag:
2096     {
2097         int top, bottom;
2098         GetSpriteExtents(pTSprite, &top, &bottom);
2099         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2100         pNSprite->shade = -128;
2101         pNSprite->pal = 0;
2102         pNSprite->z = top;
2103         if (nViewEffect == kViewEffectFlag)
2104             pNSprite->xrepeat = pNSprite->yrepeat = 24;
2105         else
2106             pNSprite->xrepeat = pNSprite->yrepeat = 64;
2107         pNSprite->picnum = 3558;
2108         return pNSprite;
2109     }
2110     case kViewEffectTesla:
2111     {
2112         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2113         pNSprite->z = pTSprite->z;
2114         pNSprite->cstat |= 2;
2115         pNSprite->shade = -128;
2116         pNSprite->xrepeat = pTSprite->xrepeat;
2117         pNSprite->yrepeat = pTSprite->yrepeat;
2118         pNSprite->picnum = 2135;
2119         break;
2120     }
2121     case kViewEffectShoot:
2122     {
2123         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2124         pNSprite->shade = -128;
2125         pNSprite->pal = 0;
2126         pNSprite->xrepeat = pNSprite->yrepeat = 64;
2127         pNSprite->picnum = 2605;
2128         return pNSprite;
2129     }
2130     case kViewEffectReflectiveBall:
2131     {
2132         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2133         pNSprite->shade = 26;
2134         pNSprite->pal = 0;
2135         pNSprite->cstat |= 2;
2136         pNSprite->xrepeat = pNSprite->yrepeat = 64;
2137         pNSprite->picnum = 2089;
2138         break;
2139     }
2140     case kViewEffectPhase:
2141     {
2142         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2143         int top, bottom;
2144         GetSpriteExtents(pTSprite, &top, &bottom);
2145         pNSprite->shade = 26;
2146         pNSprite->pal = 0;
2147         pNSprite->cstat |= 2;
2148         pNSprite->xrepeat = pNSprite->yrepeat = 24;
2149         pNSprite->picnum = 626;
2150         pNSprite->z = top;
2151         break;
2152     }
2153     case kViewEffectTrail:
2154     {
2155         int nAng = pTSprite->ang;
2156         if (pTSprite->cstat & 16)
2157         {
2158             nAng = (nAng+512)&2047;
2159         }
2160         else
2161         {
2162             nAng = (nAng+1024)&2047;
2163         }
2164         for (int i = 0; i < 5 && spritesortcnt < kMaxViewSprites; i++)
2165         {
2166             int nSector = pTSprite->sectnum;
2167             auto pNSprite = viewInsertTSprite<tspritetype>(nSector, 32767, NULL);
2168             int nLen = 128+(i<<7);
2169             int x = mulscale30(nLen, Cos(nAng));
2170             pNSprite->x = pTSprite->x + x;
2171             int y = mulscale30(nLen, Sin(nAng));
2172             pNSprite->y = pTSprite->y + y;
2173             pNSprite->z = pTSprite->z;
2174             dassert(nSector >= 0 && nSector < kMaxSectors);
2175             FindSector(pNSprite->x, pNSprite->y, pNSprite->z, &nSector);
2176             pNSprite->sectnum = nSector;
2177             pNSprite->owner = pTSprite->owner;
2178             pNSprite->picnum = pTSprite->picnum;
2179             pNSprite->cstat |= 2;
2180             if (i < 2)
2181                 pNSprite->cstat |= 514;
2182             pNSprite->shade = ClipLow(pTSprite->shade-16, -128);
2183             pNSprite->xrepeat = pTSprite->xrepeat;
2184             pNSprite->yrepeat = pTSprite->yrepeat;
2185             pNSprite->picnum = pTSprite->picnum;
2186         }
2187         break;
2188     }
2189     case kViewEffectFlame:
2190     {
2191         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2192         pNSprite->shade = -128;
2193         pNSprite->z = pTSprite->z;
2194         pNSprite->picnum = 908;
2195         pNSprite->statnum = kStatDecoration;
2196         pNSprite->xrepeat = pNSprite->yrepeat = (tilesiz[pTSprite->picnum].x*pTSprite->xrepeat)/64;
2197         break;
2198     }
2199     case kViewEffectSmokeHigh:
2200     {
2201         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2202         int top, bottom;
2203         GetSpriteExtents(pTSprite, &top, &bottom);
2204         pNSprite->z = top;
2205         if (IsDudeSprite(pTSprite))
2206             pNSprite->picnum = 672;
2207         else
2208             pNSprite->picnum = 754;
2209         pNSprite->cstat |= 2;
2210         pNSprite->shade = 8;
2211         pNSprite->xrepeat = pTSprite->xrepeat;
2212         pNSprite->yrepeat = pTSprite->yrepeat;
2213         break;
2214     }
2215     case kViewEffectSmokeLow:
2216     {
2217         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2218         int top, bottom;
2219         GetSpriteExtents(pTSprite, &top, &bottom);
2220         pNSprite->z = bottom;
2221         if (pTSprite->type >= kDudeBase && pTSprite->type < kDudeMax)
2222             pNSprite->picnum = 672;
2223         else
2224             pNSprite->picnum = 754;
2225         pNSprite->cstat |= 2;
2226         pNSprite->shade = 8;
2227         pNSprite->xrepeat = pTSprite->xrepeat;
2228         pNSprite->yrepeat = pTSprite->yrepeat;
2229         break;
2230     }
2231     case kViewEffectTorchHigh:
2232     {
2233         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2234         int top, bottom;
2235         GetSpriteExtents(pTSprite, &top, &bottom);
2236         pNSprite->z = top;
2237         pNSprite->picnum = 2101;
2238         pNSprite->shade = -128;
2239         pNSprite->xrepeat = pNSprite->yrepeat = (tilesiz[pTSprite->picnum].x*pTSprite->xrepeat)/32;
2240         break;
2241     }
2242     case kViewEffectTorchLow:
2243     {
2244         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2245         int top, bottom;
2246         GetSpriteExtents(pTSprite, &top, &bottom);
2247         pNSprite->z = bottom;
2248         pNSprite->picnum = 2101;
2249         pNSprite->shade = -128;
2250         pNSprite->xrepeat = pNSprite->yrepeat = (tilesiz[pTSprite->picnum].x*pTSprite->xrepeat)/32;
2251         break;
2252     }
2253     case kViewEffectShadow:
2254     {
2255         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2256         pNSprite->z = getflorzofslope(pTSprite->sectnum, pNSprite->x, pNSprite->y);
2257         pNSprite->shade = 127;
2258         pNSprite->cstat |= 2;
2259         pNSprite->xrepeat = pTSprite->xrepeat;
2260         pNSprite->yrepeat = pTSprite->yrepeat>>2;
2261         pNSprite->picnum = pTSprite->picnum;
2262         pNSprite->pal = 5;
2263         int height = tilesiz[pNSprite->picnum].y;
2264         int center = height/2+picanm[pNSprite->picnum].yofs;
2265         pNSprite->z -= (pNSprite->yrepeat<<2)*(height-center);
2266         break;
2267     }
2268     case kViewEffectFlareHalo:
2269     {
2270         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2271         pNSprite->shade = -128;
2272         pNSprite->pal = 2;
2273         pNSprite->cstat |= 2;
2274         pNSprite->z = pTSprite->z;
2275         pNSprite->xrepeat = pTSprite->xrepeat;
2276         pNSprite->yrepeat = pTSprite->yrepeat;
2277         pNSprite->picnum = 2427;
2278         break;
2279     }
2280     case kViewEffectCeilGlow:
2281     {
2282         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2283         sectortype *pSector = &sector[pTSprite->sectnum];
2284         pNSprite->x = pTSprite->x;
2285         pNSprite->y = pTSprite->y;
2286         pNSprite->z = pSector->ceilingz;
2287         pNSprite->picnum = 624;
2288         pNSprite->shade = ((pTSprite->z-pSector->ceilingz)>>8)-64;
2289         pNSprite->pal = 2;
2290         pNSprite->xrepeat = pNSprite->yrepeat = 64;
2291         pNSprite->cstat |= 106;
2292         pNSprite->ang = pTSprite->ang;
2293         pNSprite->owner = pTSprite->owner;
2294         break;
2295     }
2296     case kViewEffectFloorGlow:
2297     {
2298         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2299         sectortype *pSector = &sector[pTSprite->sectnum];
2300         pNSprite->x = pTSprite->x;
2301         pNSprite->y = pTSprite->y;
2302         pNSprite->z = pSector->floorz;
2303         pNSprite->picnum = 624;
2304         char nShade = (pSector->floorz-pTSprite->z)>>8;
2305         pNSprite->shade = nShade-32;
2306         pNSprite->pal = 2;
2307         pNSprite->xrepeat = pNSprite->yrepeat = nShade;
2308         pNSprite->cstat |= 98;
2309         pNSprite->ang = pTSprite->ang;
2310         pNSprite->owner = pTSprite->owner;
2311         break;
2312     }
2313     case kViewEffectSpear:
2314     {
2315         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2316         pNSprite->z = pTSprite->z;
2317         if (gDetail > 1)
2318             pNSprite->cstat |= 514;
2319         pNSprite->shade = ClipLow(pTSprite->shade-32, -128);
2320         pNSprite->xrepeat = pTSprite->xrepeat;
2321         pNSprite->yrepeat = 64;
2322         pNSprite->picnum = 775;
2323         break;
2324     }
2325     case kViewEffectShowWeapon:
2326     {
2327         dassert(pTSprite->type >= kDudePlayer1 && pTSprite->type <= kDudePlayer8);
2328         PLAYER *pPlayer = &gPlayer[pTSprite->type-kDudePlayer1];
2329         WEAPONICON weaponIcon = gWeaponIcon[pPlayer->curWeapon];
2330         const int nTile = weaponIcon.nTile;
2331         if (nTile < 0) break;
2332         auto pNSprite = viewInsertTSprite(pTSprite->sectnum, 32767, pTSprite);
2333         pNSprite->x = pTSprite->x;
2334         pNSprite->y = pTSprite->y;
2335         pNSprite->z = pTSprite->z-(32<<8);
2336         pNSprite->picnum = nTile;
2337         pNSprite->shade = pTSprite->shade;
2338         pNSprite->xrepeat = 32;
2339         pNSprite->yrepeat = 32;
2340         const int nVoxel = voxelIndex[nTile];
2341         if (gShowWeapon == 2 && usevoxels && gDetail >= 4 && videoGetRenderMode() != REND_POLYMER && nVoxel != -1)
2342         {
2343             pNSprite->cstat |= 48;
2344             pNSprite->cstat &= ~8;
2345             pNSprite->picnum = nVoxel;
2346             pNSprite->z -= weaponIcon.zOffset<<8;
2347             const int lifeLeech = 9;
2348             if (pPlayer->curWeapon == lifeLeech)
2349             {
2350                 pNSprite->x -=  mulscale30(128, Cos(pNSprite->ang));
2351                 pNSprite->y -= mulscale30(128, Sin(pNSprite->ang));
2352             }
2353         }
2354         break;
2355     }
2356     }
2357     return NULL;
2358 }
2359 
2360 LOCATION gPrevSpriteLoc[kMaxSprites];
2361 
viewApplyDefaultPal(tspritetype * pTSprite,sectortype const * pSector)2362 static void viewApplyDefaultPal(tspritetype *pTSprite, sectortype const *pSector)
2363 {
2364     int const nXSector = pSector->extra;
2365     XSECTOR const *pXSector = nXSector >= 0 ? &xsector[nXSector] : NULL;
2366     if (pXSector && pXSector->color && (VanillaMode() || pSector->floorpal != 0))
2367     {
2368         pTSprite->pal = pSector->floorpal;
2369     }
2370 }
2371 
viewProcessSprites(int32_t cX,int32_t cY,int32_t cZ,int32_t cA,int32_t smooth)2372 void viewProcessSprites(int32_t cX, int32_t cY, int32_t cZ, int32_t cA, int32_t smooth)
2373 {
2374     UNREFERENCED_PARAMETER(smooth);
2375     dassert(spritesortcnt <= kMaxViewSprites);
2376     gCameraAng = cA;
2377     int nViewSprites = spritesortcnt;
2378     for (int nTSprite = spritesortcnt-1; nTSprite >= 0; nTSprite--)
2379     {
2380         tspritetype *pTSprite = &tsprite[nTSprite];
2381         //int nXSprite = pTSprite->extra;
2382         int nXSprite = sprite[pTSprite->owner].extra;
2383         XSPRITE *pTXSprite = NULL;
2384         if (qsprite_filler[pTSprite->owner] > gDetail)
2385         {
2386             pTSprite->xrepeat = 0;
2387             continue;
2388         }
2389         if (nXSprite > 0)
2390         {
2391             pTXSprite = &xsprite[nXSprite];
2392         }
2393         int nTile = pTSprite->picnum;
2394         if (nTile < 0 || nTile >= kMaxTiles)
2395         {
2396             continue;
2397         }
2398 
2399         int nSprite = pTSprite->owner;
2400         if (gViewInterpolate && TestBitString(gInterpolateSprite, nSprite) && !(pTSprite->flags&512))
2401         {
2402             LOCATION *pPrevLoc = &gPrevSpriteLoc[nSprite];
2403             pTSprite->x = interpolate(pPrevLoc->x, pTSprite->x, gInterpolate);
2404             pTSprite->y = interpolate(pPrevLoc->y, pTSprite->y, gInterpolate);
2405             pTSprite->z = interpolate(pPrevLoc->z, pTSprite->z, gInterpolate);
2406             pTSprite->ang = pPrevLoc->ang+mulscale16(((pTSprite->ang-pPrevLoc->ang+1024)&2047)-1024, gInterpolate);
2407         }
2408         int nAnim = 0;
2409         switch (picanm[nTile].extra & 7) {
2410             case 0:
2411                 //dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
2412                 if (nXSprite <= 0 || nXSprite >= kMaxXSprites) break;
2413                 switch (pTSprite->type) {
2414                     case kSwitchToggle:
2415                     case kSwitchOneWay:
2416                         if (xsprite[nXSprite].state) nAnim = 1;
2417                         break;
2418                     case kSwitchCombo:
2419                         nAnim = xsprite[nXSprite].data1;
2420                         break;
2421                 }
2422                 break;
2423             case 1:
2424             {
2425                 if (tilehasmodelorvoxel(pTSprite->picnum, pTSprite->pal) && !(spriteext[nSprite].flags&SPREXT_NOTMD))
2426                 {
2427                     pTSprite->cstat &= ~4;
2428                     break;
2429                 }
2430                 int dX = cX - pTSprite->x;
2431                 int dY = cY - pTSprite->y;
2432                 RotateVector(&dX, &dY, 128-pTSprite->ang);
2433                 nAnim = GetOctant(dX, dY);
2434                 if (nAnim <= 4)
2435                 {
2436                     pTSprite->cstat &= ~4;
2437                 }
2438                 else
2439                 {
2440                     nAnim = 8 - nAnim;
2441                     pTSprite->cstat |= 4;
2442                 }
2443                 break;
2444             }
2445             case 2:
2446             {
2447                 if (tilehasmodelorvoxel(pTSprite->picnum, pTSprite->pal) && !(spriteext[nSprite].flags&SPREXT_NOTMD))
2448                 {
2449                     pTSprite->cstat &= ~4;
2450                     break;
2451                 }
2452                 int dX = cX - pTSprite->x;
2453                 int dY = cY - pTSprite->y;
2454                 RotateVector(&dX, &dY, 128-pTSprite->ang);
2455                 nAnim = GetOctant(dX, dY);
2456                 break;
2457             }
2458             case 3:
2459             {
2460                 if (nXSprite > 0)
2461                 {
2462                     if (gSpriteHit[nXSprite].florhit == 0)
2463                         nAnim = 1;
2464                 }
2465                 else
2466                 {
2467                     int top, bottom;
2468                     GetSpriteExtents(pTSprite, &top, &bottom);
2469                     if (getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) > bottom)
2470                         nAnim = 1;
2471                 }
2472                 break;
2473             }
2474             case 6:
2475             case 7:
2476             {
2477 #ifdef USE_OPENGL
2478                 if (videoGetRenderMode() >= REND_POLYMOST && usemodels && md_tilehasmodel(pTSprite->picnum, pTSprite->pal) >= 0 && !(spriteext[nSprite].flags&SPREXT_NOTMD))
2479                     break;
2480 #endif
2481                 // Can be overridden by def script
2482                 if (usevoxels && gDetail >= 4 && videoGetRenderMode() != REND_POLYMER && tiletovox[pTSprite->picnum] == -1 && voxelIndex[pTSprite->picnum] != -1 && !(spriteext[nSprite].flags&SPREXT_NOTMD))
2483                 {
2484                     if ((pTSprite->flags&kHitagRespawn) == 0)
2485                     {
2486                         pTSprite->cstat |= 48;
2487                         pTSprite->cstat &= ~(4|8);
2488                         pTSprite->yoffset += picanm[pTSprite->picnum].yofs;
2489                         pTSprite->picnum = voxelIndex[pTSprite->picnum];
2490                         if (!voxoff[pTSprite->picnum])
2491                             qloadvoxel(pTSprite->picnum);
2492                         if ((picanm[nTile].extra&7) == 7)
2493                         {
2494                             pTSprite->ang = ((int)totalclock<<3)&2047;
2495                         }
2496                     }
2497                 }
2498                 break;
2499             }
2500         }
2501         while (nAnim > 0)
2502         {
2503             pTSprite->picnum += picanm[pTSprite->picnum].num+1;
2504             nAnim--;
2505         }
2506 
2507         if ((pTSprite->cstat&48) != 48 && usevoxels && videoGetRenderMode() != REND_POLYMER && !(spriteext[nSprite].flags&SPREXT_NOTMD))
2508         {
2509             int const nRootTile = pTSprite->picnum;
2510             int nAnimTile = pTSprite->picnum + animateoffs_replace(pTSprite->picnum, 32768+pTSprite->owner);
2511 
2512 #if 0
2513             if (tiletovox[nAnimTile] != -1)
2514             {
2515                 pTSprite->yoffset += picanm[nAnimTile].yofs;
2516                 pTSprite->xoffset += picanm[nAnimTile].xofs;
2517             }
2518 #endif
2519 
2520             int const nVoxel = tiletovox[pTSprite->picnum];
2521 
2522             if (nVoxel != -1 && ((voxrotate[nVoxel>>3]&pow2char[nVoxel&7]) != 0 || (picanm[nRootTile].extra&7) == 7))
2523                 pTSprite->ang = (pTSprite->ang+((int)totalclock<<3))&2047;
2524         }
2525 
2526 #ifdef USE_OPENGL
2527         if ((pTSprite->cstat&48) != 48 && usemodels && !(spriteext[nSprite].flags&SPREXT_NOTMD))
2528         {
2529             int const nRootTile = pTSprite->picnum;
2530             int nAnimTile = pTSprite->picnum + animateoffs_replace(pTSprite->picnum, 32768+pTSprite->owner);
2531 
2532             if (usemodels && tile2model[Ptile2tile(nAnimTile, pTSprite->pal)].modelid >= 0 &&
2533                 tile2model[Ptile2tile(nAnimTile, pTSprite->pal)].framenum >= 0)
2534             {
2535                 pTSprite->yoffset += picanm[nAnimTile].yofs;
2536                 pTSprite->xoffset += picanm[nAnimTile].xofs;
2537 
2538                 if ((picanm[nRootTile].extra&7) == 7)
2539                     pTSprite->ang = (pTSprite->ang+((int)totalclock<<3))&2047;
2540             }
2541         }
2542 #endif
2543 
2544         sectortype *pSector = &sector[pTSprite->sectnum];
2545         XSECTOR *pXSector;
2546         int nShade = pTSprite->shade;
2547         if (pSector->extra > 0)
2548         {
2549             pXSector = &xsector[pSector->extra];
2550         }
2551         else
2552         {
2553             pXSector = NULL;
2554         }
2555         if ((pSector->ceilingstat&1) && (pSector->floorstat&32768) == 0)
2556         {
2557             nShade += tileShade[pSector->ceilingpicnum]+pSector->ceilingshade;
2558         }
2559         else
2560         {
2561             nShade += tileShade[pSector->floorpicnum]+pSector->floorshade;
2562         }
2563         nShade += tileShade[pTSprite->picnum];
2564         pTSprite->shade = ClipRange(nShade, -128, 127);
2565         if ((pTSprite->flags&kHitagRespawn) && sprite[pTSprite->owner].owner == 3)
2566         {
2567             dassert(pTXSprite != NULL);
2568             pTSprite->xrepeat = 48;
2569             pTSprite->yrepeat = 48;
2570             pTSprite->shade = -128;
2571             pTSprite->picnum = 2272 + 2*pTXSprite->respawnPending;
2572             pTSprite->cstat &= ~514;
2573             if (((IsItemSprite(pTSprite) || IsAmmoSprite(pTSprite)) && gGameOptions.nItemSettings == 2)
2574                 || (IsWeaponSprite(pTSprite) && gGameOptions.nWeaponSettings == 3))
2575             {
2576                 pTSprite->xrepeat = pTSprite->yrepeat = 48;
2577             }
2578             else
2579             {
2580                 pTSprite->xrepeat = pTSprite->yrepeat = 0;
2581             }
2582         }
2583         if (spritesortcnt >= kMaxViewSprites) continue;
2584         if (pTXSprite && pTXSprite->burnTime > 0)
2585         {
2586             pTSprite->shade = ClipRange(pTSprite->shade-16-QRandom(8), -128, 127);
2587         }
2588         if (pTSprite->flags&256)
2589         {
2590             viewAddEffect(nTSprite, kViewEffectSmokeHigh);
2591         }
2592         if (pTSprite->flags&1024)
2593         {
2594             pTSprite->cstat |= 4;
2595         }
2596         if (pTSprite->flags&2048)
2597         {
2598             pTSprite->cstat |= 8;
2599         }
2600         switch (pTSprite->statnum) {
2601         case kStatDecoration: {
2602             switch (pTSprite->type) {
2603                 case kDecorationCandle:
2604                     if (!pTXSprite || pTXSprite->state == 1) {
2605                         pTSprite->shade = -128;
2606                         viewAddEffect(nTSprite, kViewEffectPhase);
2607                     } else {
2608                         pTSprite->shade = -8;
2609                     }
2610                     break;
2611                 case kDecorationTorch:
2612                     if (!pTXSprite || pTXSprite->state == 1) {
2613                         pTSprite->picnum++;
2614                         viewAddEffect(nTSprite, kViewEffectTorchHigh);
2615                     } else {
2616                         viewAddEffect(nTSprite, kViewEffectSmokeHigh);
2617                     }
2618                     break;
2619                 default:
2620                     viewApplyDefaultPal(pTSprite, pSector);
2621                     break;
2622             }
2623         }
2624         break;
2625         case kStatItem: {
2626             switch (pTSprite->type) {
2627                 case kItemFlagABase:
2628                     if (pTXSprite && pTXSprite->state > 0 && gGameOptions.nGameType == 3) {
2629                         auto pNTSprite = viewAddEffect(nTSprite, kViewEffectBigFlag);
2630                         if (pNTSprite) pNTSprite->pal = 10;
2631                     }
2632                     break;
2633                 case kItemFlagBBase:
2634                     if (pTXSprite && pTXSprite->state > 0 && gGameOptions.nGameType == 3) {
2635                         auto pNTSprite = viewAddEffect(nTSprite, kViewEffectBigFlag);
2636                         if (pNTSprite) pNTSprite->pal = 7;
2637                     }
2638                     break;
2639                 case kItemFlagA:
2640                     pTSprite->pal = 10;
2641                     pTSprite->cstat |= 1024;
2642                     break;
2643                 case kItemFlagB:
2644                     pTSprite->pal = 7;
2645                     pTSprite->cstat |= 1024;
2646                     break;
2647                 default:
2648                     if (pTSprite->type >= kItemKeySkull && pTSprite->type < kItemKeyMax)
2649                         pTSprite->shade = -128;
2650 
2651                     viewApplyDefaultPal(pTSprite, pSector);
2652                     break;
2653             }
2654         }
2655         break;
2656         case kStatProjectile: {
2657             switch (pTSprite->type) {
2658                 case kMissileTeslaAlt:
2659                     pTSprite->yrepeat = 128;
2660                     pTSprite->cstat |= 32;
2661                     break;
2662                 case kMissileTeslaRegular:
2663                     viewAddEffect(nTSprite, kViewEffectTesla);
2664                     break;
2665                 case kMissileButcherKnife:
2666                     viewAddEffect(nTSprite, kViewEffectTrail);
2667                     break;
2668                 case kMissileFlareRegular:
2669                 case kMissileFlareAlt:
2670                     if (pTSprite->statnum == kStatFlare) {
2671                         dassert(pTXSprite != NULL);
2672                         if (pTXSprite->target == gView->nSprite) {
2673                             pTSprite->xrepeat = 0;
2674                             break;
2675                         }
2676                     }
2677 
2678                     viewAddEffect(nTSprite, kViewEffectFlareHalo);
2679                     if (pTSprite->type != kMissileFlareRegular) break;
2680                     sectortype *pSector = &sector[pTSprite->sectnum];
2681 
2682                     int zDiff = (pTSprite->z - pSector->ceilingz) >> 8;
2683                     if ((pSector->ceilingstat&1) == 0 && zDiff < 64) {
2684                         viewAddEffect(nTSprite, kViewEffectCeilGlow);
2685                     }
2686 
2687                     zDiff = (pSector->floorz - pTSprite->z) >> 8;
2688                     if ((pSector->floorstat&1) == 0 && zDiff < 64) {
2689                         viewAddEffect(nTSprite, kViewEffectFloorGlow);
2690                     }
2691                     break;
2692                 }
2693             break;
2694         }
2695         case kStatDude:
2696         {
2697             if (pTSprite->type == kDudeHand && pTXSprite->aiState == &hand13A3B4)
2698             {
2699                 spritetype *pTTarget = &sprite[pTXSprite->target];
2700                 dassert(pTXSprite != NULL && pTTarget != NULL);
2701                 if (IsPlayerSprite(pTTarget))
2702                 {
2703                     pTSprite->xrepeat = 0;
2704                     break;
2705                 }
2706             }
2707 
2708 
2709 
2710 
2711             if (pXSector && pXSector->color) pTSprite->pal = pSector->floorpal;
2712             if (powerupCheck(gView, kPwUpBeastVision) > 0) pTSprite->shade = -128;
2713 
2714             if (IsPlayerSprite(pTSprite)) {
2715                 viewApplyDefaultPal(pTSprite, pSector);
2716 
2717                 PLAYER *pPlayer = &gPlayer[pTSprite->type-kDudePlayer1];
2718                 if (powerupCheck(pPlayer, kPwUpShadowCloak) && !powerupCheck(gView, kPwUpBeastVision)) {
2719                     pTSprite->cstat |= 2;
2720                     pTSprite->pal = 5;
2721                 }  else if (powerupCheck(pPlayer, kPwUpDeathMask)) {
2722                     pTSprite->shade = -128;
2723                     pTSprite->pal = 5;
2724                 } else if (powerupCheck(pPlayer, kPwUpDoppleganger)) {
2725                     pTSprite->pal = 11+(gView->teamId&3);
2726                 }
2727 
2728                 if (powerupCheck(pPlayer, kPwUpReflectShots)) {
2729                     viewAddEffect(nTSprite, kViewEffectReflectiveBall);
2730                 }
2731 
2732                 if (gShowWeapon && gGameOptions.nGameType > 0 && gView) {
2733                     viewAddEffect(nTSprite, kViewEffectShowWeapon);
2734                 }
2735 
2736                 if (pPlayer->flashEffect && (gView != pPlayer || gViewPos != VIEWPOS_0)) {
2737                     auto pNTSprite = viewAddEffect(nTSprite, kViewEffectShoot);
2738                     if (pNTSprite) {
2739                         POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
2740                         pNTSprite->x += mulscale28(pPosture->zOffset, Cos(pTSprite->ang));
2741                         pNTSprite->y += mulscale28(pPosture->zOffset, Sin(pTSprite->ang));
2742                         pNTSprite->z = pPlayer->pSprite->z-pPosture->xOffset;
2743                     }
2744                 }
2745 
2746                 if (pPlayer->hasFlag > 0 && gGameOptions.nGameType == 3) {
2747                     if (pPlayer->hasFlag&1)  {
2748                         auto pNTSprite = viewAddEffect(nTSprite, kViewEffectFlag);
2749                         if (pNTSprite)
2750                         {
2751                             pNTSprite->pal = 10;
2752                             pNTSprite->cstat |= 4;
2753                         }
2754                     }
2755                     if (pPlayer->hasFlag&2) {
2756                         auto pNTSprite = viewAddEffect(nTSprite, kViewEffectFlag);
2757                         if (pNTSprite)
2758                         {
2759                             pNTSprite->pal = 7;
2760                             pNTSprite->cstat |= 4;
2761                         }
2762                     }
2763                 }
2764             }
2765 
2766             if (pTSprite->owner != gView->pSprite->index || gViewPos != VIEWPOS_0) {
2767                 if (getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) >= cZ)
2768                 {
2769                     viewAddEffect(nTSprite, kViewEffectShadow);
2770                 }
2771             }
2772 
2773             #ifdef NOONE_EXTENSIONS
2774             if (gModernMap) { // add target spot indicator for patrol dudes
2775                 XSPRITE* pTXSprite = &xsprite[pTSprite->extra];
2776                 if (pTXSprite->dudeFlag4 && aiInPatrolState(pTXSprite->aiState) && pTXSprite->data3 > 0 && pTXSprite->data3 <= kMaxPatrolSpotValue)
2777                     viewAddEffect(nTSprite, kViewEffectSpotProgress);
2778             }
2779             #endif
2780             break;
2781         }
2782         case kStatTraps: {
2783             if (pTSprite->type == kTrapSawCircular) {
2784                 if (pTXSprite->state) {
2785                     if (pTXSprite->data1) {
2786                         pTSprite->picnum = 772;
2787                         if (pTXSprite->data2)
2788                             viewAddEffect(nTSprite, kViewEffectSpear);
2789                     }
2790                 }
2791                 else if (pTXSprite->data1) pTSprite->picnum = 773;
2792                 else pTSprite->picnum = 656;
2793 
2794             }
2795             break;
2796         }
2797         case kStatThing: {
2798             viewApplyDefaultPal(pTSprite, pSector);
2799 
2800             if (pTSprite->type < kThingBase || pTSprite->type >= kThingMax || !gSpriteHit[nXSprite].florhit) {
2801                 if ((pTSprite->flags & kPhysMove) && getflorzofslope(pTSprite->sectnum, pTSprite->x, pTSprite->y) >= cZ)
2802                     viewAddEffect(nTSprite, kViewEffectShadow);
2803             }
2804         }
2805         break;
2806         }
2807     }
2808 
2809     for (int nTSprite = spritesortcnt-1; nTSprite >= nViewSprites; nTSprite--)
2810     {
2811         tspritetype *pTSprite = &tsprite[nTSprite];
2812         int nAnim = 0;
2813         switch (picanm[pTSprite->picnum].extra&7)
2814         {
2815             case 1:
2816             {
2817                 int dX = cX - pTSprite->x;
2818                 int dY = cY - pTSprite->y;
2819                 RotateVector(&dX, &dY, 128-pTSprite->ang);
2820                 nAnim = GetOctant(dX, dY);
2821                 if (nAnim <= 4)
2822                 {
2823                     pTSprite->cstat &= ~4;
2824                 }
2825                 else
2826                 {
2827                     nAnim = 8 - nAnim;
2828                     pTSprite->cstat |= 4;
2829                 }
2830                 break;
2831             }
2832             case 2:
2833             {
2834                 int dX = cX - pTSprite->x;
2835                 int dY = cY - pTSprite->y;
2836                 RotateVector(&dX, &dY, 128-pTSprite->ang);
2837                 nAnim = GetOctant(dX, dY);
2838                 break;
2839             }
2840         }
2841         while (nAnim > 0)
2842         {
2843             pTSprite->picnum += picanm[pTSprite->picnum].num+1;
2844             nAnim--;
2845         }
2846     }
2847 }
2848 
2849 int othercameradist = 1280;
2850 int cameradist = -1;
2851 int othercameraclock, cameraclock;
2852 
CalcOtherPosition(spritetype * pSprite,int * pX,int * pY,int * pZ,int * vsectnum,int nAng,fix16_t zm)2853 void CalcOtherPosition(spritetype *pSprite, int *pX, int *pY, int *pZ, int *vsectnum, int nAng, fix16_t zm)
2854 {
2855     int vX = mulscale30(-Cos(nAng), 1280);
2856     int vY = mulscale30(-Sin(nAng), 1280);
2857     int vZ = fix16_to_int(mulscale(zm, 1280, 3))-(16<<8);
2858     int bakCstat = pSprite->cstat;
2859     pSprite->cstat &= ~256;
2860     dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
2861     FindSector(*pX, *pY, *pZ, vsectnum);
2862     short nHSector;
2863     int hX, hY;
2864     vec3_t pos = {*pX, *pY, *pZ};
2865     hitdata_t hitdata;
2866     hitscan(&pos, *vsectnum, vX, vY, vZ, &hitdata, CLIPMASK1);
2867     nHSector = hitdata.sect;
2868     hX = hitdata.pos.x;
2869     hY = hitdata.pos.y;
2870     int dX = hX-*pX;
2871     int dY = hY-*pY;
2872     if (klabs(vX)+klabs(vY) > klabs(dX)+klabs(dY))
2873     {
2874         *vsectnum = nHSector;
2875         dX -= ksgn(vX)<<6;
2876         dY -= ksgn(vY)<<6;
2877         int nDist;
2878         if (klabs(vX) > klabs(vY))
2879         {
2880             nDist = ClipHigh(divscale16(dX,vX), othercameradist);
2881         }
2882         else
2883         {
2884             nDist = ClipHigh(divscale16(dY,vY), othercameradist);
2885         }
2886         othercameradist = nDist;
2887     }
2888     *pX += mulscale16(vX, othercameradist);
2889     *pY += mulscale16(vY, othercameradist);
2890     *pZ += mulscale16(vZ, othercameradist);
2891     othercameradist = ClipHigh(othercameradist+(((int)(totalclock-othercameraclock))<<10), 65536);
2892     othercameraclock = (int)totalclock;
2893     dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
2894     FindSector(*pX, *pY, *pZ, vsectnum);
2895     pSprite->cstat = bakCstat;
2896 }
2897 
CalcPosition(spritetype * pSprite,int * pX,int * pY,int * pZ,int * vsectnum,int nAng,fix16_t zm)2898 void CalcPosition(spritetype *pSprite, int *pX, int *pY, int *pZ, int *vsectnum, int nAng, fix16_t zm)
2899 {
2900     int vX = mulscale30(-Cos(nAng), 1280);
2901     int vY = mulscale30(-Sin(nAng), 1280);
2902     int vZ = fix16_to_int(mulscale(zm, 1280, 3))-(16<<8);
2903     int bakCstat = pSprite->cstat;
2904     pSprite->cstat &= ~256;
2905     dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
2906     FindSector(*pX, *pY, *pZ, vsectnum);
2907     short nHSector;
2908     int hX, hY;
2909     hitscangoal.x = hitscangoal.y = 0x1fffffff;
2910     vec3_t pos = { *pX, *pY, *pZ };
2911     hitdata_t hitdata;
2912     hitscan(&pos, *vsectnum, vX, vY, vZ, &hitdata, CLIPMASK1);
2913     nHSector = hitdata.sect;
2914     hX = hitdata.pos.x;
2915     hY = hitdata.pos.y;
2916     int dX = hX-*pX;
2917     int dY = hY-*pY;
2918     if (klabs(vX)+klabs(vY) > klabs(dX)+klabs(dY))
2919     {
2920         *vsectnum = nHSector;
2921         dX -= ksgn(vX)<<6;
2922         dY -= ksgn(vY)<<6;
2923         int nDist;
2924         if (klabs(vX) > klabs(vY))
2925         {
2926             nDist = ClipHigh(divscale16(dX,vX), cameradist);
2927         }
2928         else
2929         {
2930             nDist = ClipHigh(divscale16(dY,vY), cameradist);
2931         }
2932         cameradist = nDist;
2933     }
2934     *pX += mulscale16(vX, cameradist);
2935     *pY += mulscale16(vY, cameradist);
2936     *pZ += mulscale16(vZ, cameradist);
2937     cameradist = ClipHigh(cameradist+(((int)(totalclock-cameraclock))<<10), 65536);
2938     cameraclock = (int)totalclock;
2939     dassert(*vsectnum >= 0 && *vsectnum < kMaxSectors);
2940     FindSector(*pX, *pY, *pZ, vsectnum);
2941     pSprite->cstat = bakCstat;
2942 }
2943 
2944 struct {
2945     short nTile;
2946     unsigned char nStat;
2947     unsigned char nPal;
2948     int nScale;
2949     short nX, nY;
2950 } burnTable[9] = {
2951      { 2101, 2, 0, 118784, 10, 220 },
2952      { 2101, 2, 0, 110592, 40, 220 },
2953      { 2101, 2, 0, 81920, 85, 220 },
2954      { 2101, 2, 0, 69632, 120, 220 },
2955      { 2101, 2, 0, 61440, 160, 220 },
2956      { 2101, 2, 0, 73728, 200, 220 },
2957      { 2101, 2, 0, 77824, 235, 220 },
2958      { 2101, 2, 0, 110592, 275, 220 },
2959      { 2101, 2, 0, 122880, 310, 220 }
2960 };
2961 
viewBurnTime(int gScale)2962 void viewBurnTime(int gScale)
2963 {
2964     if (!gScale) return;
2965 
2966     for (int i = 0; i < 9; i++)
2967     {
2968         int nTile = burnTable[i].nTile+qanimateoffs(burnTable[i].nTile,32768+i);
2969         int nScale = burnTable[i].nScale;
2970         if (gScale < 600)
2971         {
2972             nScale = scale(nScale, gScale, 600);
2973         }
2974         rotatesprite(burnTable[i].nX<<16, burnTable[i].nY<<16, nScale, 0, nTile,
2975             0, burnTable[i].nPal, burnTable[i].nStat, windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y);
2976     }
2977 }
2978 
2979 // by NoOne: show warning msgs in game instead of throwing errors (in some cases)
viewSetSystemMessage(const char * pMessage,...)2980 void viewSetSystemMessage(const char* pMessage, ...) {
2981     char buffer[1024]; va_list args; va_start(args, pMessage);
2982     vsprintf(buffer, pMessage, args);
2983 
2984     OSD_Printf("%s\n", buffer); // print it also in console
2985     gGameMessageMgr.Add(buffer, 15, 7, MESSAGE_PRIORITY_NORMAL);
2986 }
2987 
viewSetMessage(const char * pMessage,const int pal,const MESSAGE_PRIORITY priority)2988 void viewSetMessage(const char *pMessage, const int pal, const MESSAGE_PRIORITY priority)
2989 {
2990     OSD_Printf("%s\n", pMessage);
2991     gGameMessageMgr.Add(pMessage, 15, pal, priority);
2992 }
2993 
viewDisplayMessage(void)2994 void viewDisplayMessage(void)
2995 {
2996     gGameMessageMgr.Display();
2997 }
2998 
2999 char errMsg[256];
3000 
viewSetErrorMessage(const char * pMessage)3001 void viewSetErrorMessage(const char *pMessage)
3002 {
3003     if (!pMessage)
3004     {
3005         strcpy(errMsg, "");
3006     }
3007     else
3008     {
3009         strcpy(errMsg, pMessage);
3010     }
3011 }
3012 
DoLensEffect(void)3013 void DoLensEffect(void)
3014 {
3015     char *d = (char*)waloff[4077];
3016     dassert(d != NULL);
3017     char *s = (char*)waloff[4079];
3018     dassert(s != NULL);
3019     for (int i = 0; i < kLensSize*kLensSize; i++, d++)
3020         if (lensTable[i] >= 0)
3021             *d = s[lensTable[i]];
3022     tileInvalidate(4077, -1, -1);
3023 }
3024 
UpdateDacs(int nPalette,bool bNoTint)3025 void UpdateDacs(int nPalette, bool bNoTint)
3026 {
3027     static RGB newDAC[256];
3028     static int oldPalette;
3029     if (oldPalette != nPalette)
3030     {
3031         scrSetPalette(nPalette);
3032         oldPalette = nPalette;
3033     }
3034 
3035 #ifdef USE_OPENGL
3036     if (videoGetRenderMode() >= REND_POLYMOST)
3037     {
3038         gLastPal = 0;
3039         polytint_t *tint = &hictinting[MAXPALOOKUPS-1];
3040         int nRed = 0;
3041         int nGreen = 0;
3042         int nBlue = 0;
3043         tint->f = 0;
3044         switch (nPalette)
3045         {
3046         case 0:
3047         default:
3048             tint->r = 255;
3049             tint->g = 255;
3050             tint->b = 255;
3051             break;
3052         case 1:
3053             tint->r = 132;
3054             tint->g = 164;
3055             tint->b = 255;
3056             break;
3057         case 2:
3058             tint->r = 255;
3059             tint->g = 126;
3060             tint->b = 105;
3061             break;
3062         case 3:
3063             tint->r = 162;
3064             tint->g = 186;
3065             tint->b = 15;
3066             break;
3067         case 4:
3068             tint->r = 255;
3069             tint->g = 255;
3070             tint->b = 255;
3071             break;
3072         }
3073         if (!bNoTint && gView != nullptr)
3074         {
3075             nRed += gView->pickupEffect;
3076             nGreen += gView->pickupEffect;
3077             nBlue -= gView->pickupEffect;
3078 
3079             nRed += ClipHigh(gView->painEffect, 85)*2;
3080             nGreen -= ClipHigh(gView->painEffect, 85)*3;
3081             nBlue -= ClipHigh(gView->painEffect, 85)*3;
3082 
3083             nRed -= gView->blindEffect;
3084             nGreen -= gView->blindEffect;
3085             nBlue -= gView->blindEffect;
3086 
3087             nRed -= gView->chokeEffect>>6;
3088             nGreen -= gView->chokeEffect>>5;
3089             nBlue -= gView->chokeEffect>>6;
3090         }
3091         nRed = ClipRange(nRed, -255, 255);
3092         nGreen = ClipRange(nGreen, -255, 255);
3093         nBlue = ClipRange(nBlue, -255, 255);
3094 
3095         videoSetPalette(0, nPalette, 2);
3096         videoTintBlood(nRed, nGreen, nBlue);
3097     }
3098     else
3099 #endif
3100     {
3101         gLastPal = nPalette;
3102         if (bNoTint || gView == nullptr)
3103         {
3104             memcpy(newDAC, baseDAC, sizeof(newDAC));
3105         }
3106         else
3107         {
3108             for (int i = 0; i < 256; i++)
3109             {
3110                 int nRed = baseDAC[i].red;
3111                 int nGreen = baseDAC[i].green;
3112                 int nBlue = baseDAC[i].blue;
3113                 nRed += gView->pickupEffect;
3114                 nGreen += gView->pickupEffect;
3115                 nBlue -= gView->pickupEffect;
3116 
3117                 nRed += ClipHigh(gView->painEffect, 85)*2;
3118                 nGreen -= ClipHigh(gView->painEffect, 85)*3;
3119                 nBlue -= ClipHigh(gView->painEffect, 85)*3;
3120 
3121                 nRed -= gView->blindEffect;
3122                 nGreen -= gView->blindEffect;
3123                 nBlue -= gView->blindEffect;
3124 
3125                 nRed -= gView->chokeEffect>>6;
3126                 nGreen -= gView->chokeEffect>>5;
3127                 nBlue -= gView->chokeEffect>>6;
3128 
3129                 newDAC[i].red = ClipRange(nRed, 0, 255);
3130                 newDAC[i].green = ClipRange(nGreen, 0, 255);
3131                 newDAC[i].blue = ClipRange(nBlue, 0, 255);
3132             }
3133         }
3134         if (memcmp(newDAC, curDAC, 768) != 0)
3135         {
3136             memcpy(curDAC, newDAC, 768);
3137             gSetDacRange(0, 256, curDAC);
3138         }
3139     }
3140 }
3141 
3142 char otherMirrorGotpic[2];
3143 char bakMirrorGotpic[2];
3144 // int gVisibility;
3145 
3146 int deliriumTilt, deliriumTurn, deliriumPitch;
3147 int gScreenTiltO, deliriumTurnO, deliriumPitchO;
3148 
3149 int gShowFrameRate = 1;
3150 
viewUpdateDelirium(void)3151 void viewUpdateDelirium(void)
3152 {
3153     gScreenTiltO = gScreenTilt;
3154     deliriumTurnO = deliriumTurn;
3155     deliriumPitchO = deliriumPitch;
3156     int powerCount;
3157     if ((powerCount = powerupCheck(gView, kPwUpDeliriumShroom)) != 0)
3158     {
3159         int tilt1 = 170, tilt2 = 170, pitch = 20;
3160         int timer = (int)gFrameClock*4;
3161         if (powerCount < 512)
3162         {
3163             int powerScale = (powerCount<<16) / 512;
3164             tilt1 = mulscale16(tilt1, powerScale);
3165             tilt2 = mulscale16(tilt2, powerScale);
3166             pitch = mulscale16(pitch, powerScale);
3167         }
3168         int sin2 = costable[(2*timer-512)&2047] / 2;
3169         int sin3 = costable[(3*timer-512)&2047] / 2;
3170         gScreenTilt = mulscale30(sin2+sin3,tilt1);
3171         int sin4 = costable[(4*timer-512)&2047] / 2;
3172         deliriumTurn = mulscale30(sin3+sin4,tilt2);
3173         int sin5 = costable[(5*timer-512)&2047] / 2;
3174         deliriumPitch = mulscale30(sin4+sin5,pitch);
3175         return;
3176     }
3177     gScreenTilt = ((gScreenTilt+1024)&2047)-1024;
3178     if (gScreenTilt > 0)
3179     {
3180         gScreenTilt -= 8;
3181         if (gScreenTilt < 0)
3182             gScreenTilt = 0;
3183     }
3184     else if (gScreenTilt < 0)
3185     {
3186         gScreenTilt += 8;
3187         if (gScreenTilt >= 0)
3188             gScreenTilt = 0;
3189     }
3190 }
3191 
3192 int shakeHoriz, shakeAngle, shakeX, shakeY, shakeZ, shakeBobX, shakeBobY;
3193 
viewUpdateShake(void)3194 void viewUpdateShake(void)
3195 {
3196     shakeHoriz = 0;
3197     shakeAngle = 0;
3198     shakeX = 0;
3199     shakeY = 0;
3200     shakeZ = 0;
3201     shakeBobX = 0;
3202     shakeBobY = 0;
3203     if (gView->flickerEffect)
3204     {
3205         int nValue = ClipHigh(gView->flickerEffect * 8, 2000);
3206         shakeHoriz += QRandom2(nValue >> 8);
3207         shakeAngle += QRandom2(nValue >> 8);
3208         shakeX += QRandom2(nValue >> 4);
3209         shakeY += QRandom2(nValue >> 4);
3210         shakeZ += QRandom2(nValue);
3211         shakeBobX += QRandom2(nValue);
3212         shakeBobY += QRandom2(nValue);
3213     }
3214     if (gView->quakeEffect)
3215     {
3216         int nValue = ClipHigh(gView->quakeEffect * 8, 2000);
3217         shakeHoriz += QRandom2(nValue >> 8);
3218         shakeAngle += QRandom2(nValue >> 8);
3219         shakeX += QRandom2(nValue >> 4);
3220         shakeY += QRandom2(nValue >> 4);
3221         shakeZ += QRandom2(nValue);
3222         shakeBobX += QRandom2(nValue);
3223         shakeBobY += QRandom2(nValue);
3224     }
3225 }
3226 
3227 float r_ambientlight = 1.0, r_ambientlightrecip = 1.0;
3228 
3229 int gLastPal = 0;
3230 
3231 int32_t g_frameRate;
3232 
viewDrawScreen(void)3233 void viewDrawScreen(void)
3234 {
3235     int nPalette = 0;
3236     static ClockTicks lastUpdate;
3237     int defaultHoriz = gCenterHoriz ? 100 : 90;
3238 
3239 #ifdef USE_OPENGL
3240     polymostcenterhoriz = defaultHoriz;
3241 #endif
3242     ClockTicks delta = totalclock - lastUpdate;
3243     if (delta < 0)
3244         delta = 0;
3245     lastUpdate = totalclock;
3246     if (!gPaused && (!CGameMenuMgr::m_bActive || gGameOptions.nGameType != 0))
3247     {
3248         gInterpolate = ((totalclock-gNetFifoClock)+4).toScale16()/4;
3249     }
3250     if (gInterpolate < 0 || gInterpolate > 65536)
3251     {
3252         gInterpolate = ClipRange(gInterpolate, 0, 65536);
3253     }
3254     if (gViewInterpolate)
3255     {
3256         CalcInterpolations();
3257     }
3258 
3259     if (gViewMode == 3 || gViewMode == 4 || gOverlayMap)
3260     {
3261         DoSectorLighting();
3262     }
3263     if (gViewMode == 3 || gOverlayMap)
3264     {
3265         int yxAspect = yxaspect;
3266         int viewingRange = viewingrange;
3267         if (r_usenewaspect)
3268         {
3269             newaspect_enable = 1;
3270             videoSetCorrectedAspect();
3271         }
3272         renderSetAspect(Blrintf(float(viewingrange) * tanf(gFov * (PI/360.f))), yxaspect);
3273         int cX = gView->pSprite->x;
3274         int cY = gView->pSprite->y;
3275         int cZ = gView->zView;
3276         int zDelta = gView->zWeapon-gView->zView-(12<<8);
3277         fix16_t cA = gView->q16ang;
3278         fix16_t q16horiz = gView->q16horiz;
3279         fix16_t q16slopehoriz = gView->q16slopehoriz;
3280         int v74 = gView->bobWidth;
3281         int v8c = gView->bobHeight;
3282         int v4c = gView->swayWidth;
3283         int v48 = gView->swayHeight;
3284         int nSectnum = gView->pSprite->sectnum;
3285         if (gViewInterpolate)
3286         {
3287             if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0)
3288             {
3289                 nSectnum = predict.at68;
3290                 cX = interpolate(predictOld.at50, predict.at50, gInterpolate);
3291                 cY = interpolate(predictOld.at54, predict.at54, gInterpolate);
3292                 cZ = interpolate(predictOld.at38, predict.at38, gInterpolate);
3293                 zDelta = interpolate(predictOld.at34, predict.at34, gInterpolate);
3294                 cA = interpolateangfix16(predictOld.at30, predict.at30, gInterpolate);
3295                 q16horiz = interpolate(predictOld.at24, predict.at24, gInterpolate);
3296                 q16slopehoriz = interpolate(predictOld.at28, predict.at28, gInterpolate);
3297                 v74 = interpolate(predictOld.atc, predict.atc, gInterpolate);
3298                 v8c = interpolate(predictOld.at8, predict.at8, gInterpolate);
3299                 v4c = interpolate(predictOld.at1c, predict.at1c, gInterpolate);
3300                 v48 = interpolate(predictOld.at18, predict.at18, gInterpolate);
3301             }
3302             else
3303             {
3304                 VIEW *pView = &gPrevView[gViewIndex];
3305                 cX = interpolate(pView->at50, cX, gInterpolate);
3306                 cY = interpolate(pView->at54, cY, gInterpolate);
3307                 cZ = interpolate(pView->at38, cZ, gInterpolate);
3308                 zDelta = interpolate(pView->at34, zDelta, gInterpolate);
3309                 cA = interpolateangfix16(pView->at30, cA, gInterpolate);
3310                 q16horiz = interpolate(pView->at24, q16horiz, gInterpolate);
3311                 q16slopehoriz = interpolate(pView->at28, q16slopehoriz, gInterpolate);
3312                 v74 = interpolate(pView->atc, v74, gInterpolate);
3313                 v8c = interpolate(pView->at8, v8c, gInterpolate);
3314                 v4c = interpolate(pView->at1c, v4c, gInterpolate);
3315                 v48 = interpolate(pView->at18, v48, gInterpolate);
3316             }
3317         }
3318         if (gView == gMe && (numplayers <= 1 || gPrediction) && gView->pXSprite->health != 0 && !VanillaMode())
3319         {
3320             CONSTEXPR int upAngle = 289;
3321             CONSTEXPR int downAngle = -347;
3322             fix16_t q16look;
3323             cA = gViewAngle;
3324             q16look = gViewLook;
3325             q16horiz = fix16_from_float(100.f * tanf(fix16_to_float(q16look) * fPI / 1024.f));
3326         }
3327         viewUpdateShake();
3328         q16horiz += fix16_from_int(shakeHoriz);
3329         cA += fix16_from_int(shakeAngle);
3330         cX += shakeX;
3331         cY += shakeY;
3332         cZ += shakeZ;
3333         v4c += shakeBobX;
3334         v48 += shakeBobY;
3335         q16horiz += fix16_from_int(mulscale30(0x40000000-Cos(gView->tiltEffect<<2), 30));
3336         if (gViewPos == 0)
3337         {
3338             if (gViewHBobbing)
3339             {
3340                 cX -= mulscale30(v74, Sin(fix16_to_int(cA)))>>4;
3341                 cY += mulscale30(v74, Cos(fix16_to_int(cA)))>>4;
3342             }
3343             if (gViewVBobbing)
3344             {
3345                 cZ += v8c;
3346             }
3347             if (gSlopeTilting)
3348             {
3349                 q16horiz += q16slopehoriz;
3350             }
3351             cZ += fix16_to_int(q16horiz*10);
3352             cameradist = -1;
3353             cameraclock = (int)totalclock;
3354         }
3355         else
3356         {
3357             CalcPosition(gView->pSprite, (int*)&cX, (int*)&cY, (int*)&cZ, &nSectnum, fix16_to_int(cA), q16horiz);
3358         }
3359         CheckLink((int*)&cX, (int*)&cY, (int*)&cZ, &nSectnum);
3360         int v78 = interpolateang(gScreenTiltO, gScreenTilt, gInterpolate);
3361         char v14 = 0;
3362         char v10 = 0;
3363         bool bDelirium = powerupCheck(gView, kPwUpDeliriumShroom) > 0;
3364         static bool bDeliriumOld = false;
3365         int tiltcs, tiltdim;
3366         char v4 = powerupCheck(gView, kPwUpCrystalBall) > 0;
3367 #ifdef USE_OPENGL
3368         renderSetRollAngle(0);
3369 #endif
3370         if (v78 || bDelirium)
3371         {
3372             if (videoGetRenderMode() == REND_CLASSIC)
3373             {
3374                 int vr = viewingrange;
3375                 walock[TILTBUFFER] = 255;
3376                 if (!waloff[TILTBUFFER])
3377                 {
3378                     tileAllocTile(TILTBUFFER, 640, 640, 0, 0);
3379                 }
3380                 if (xdim >= 640 && ydim >= 640)
3381                 {
3382                     tiltcs = 1;
3383                     tiltdim = 640;
3384                 }
3385                 else
3386                 {
3387                     tiltcs = 0;
3388                     tiltdim = 320;
3389                 }
3390                 renderSetTarget(TILTBUFFER, tiltdim, tiltdim);
3391                 int nAng = v78&511;
3392                 if (nAng > 256)
3393                 {
3394                     nAng = 512-nAng;
3395                 }
3396                 renderSetAspect(mulscale16(vr, dmulscale32(Cos(nAng), 262144, Sin(nAng), 163840)), yxaspect);
3397             }
3398 #ifdef USE_OPENGL
3399             else
3400                 renderSetRollAngle(v78);
3401 #endif
3402         }
3403         else if (v4 && gNetPlayers > 1)
3404         {
3405             int tmp = ((int)totalclock/240)%(gNetPlayers-1);
3406             int i = connecthead;
3407             while (1)
3408             {
3409                 if (i == gViewIndex)
3410                     i = connectpoint2[i];
3411                 if (tmp == 0)
3412                     break;
3413                 i = connectpoint2[i];
3414                 tmp--;
3415             }
3416             PLAYER *pOther = &gPlayer[i];
3417             //othercameraclock = gGameClock;
3418             if (!waloff[4079])
3419             {
3420                 tileAllocTile(4079, 128, 128, 0, 0);
3421             }
3422             renderSetTarget(4079, 128, 128);
3423             renderSetAspect(65536, 78643);
3424             int vd8 = pOther->pSprite->x;
3425             int vd4 = pOther->pSprite->y;
3426             int vd0 = pOther->zView;
3427             int vcc = pOther->pSprite->sectnum;
3428             int v50 = pOther->pSprite->ang;
3429             int v54 = 0;
3430             if (pOther->flickerEffect)
3431             {
3432                 int nValue = ClipHigh(pOther->flickerEffect*8, 2000);
3433                 v54 += QRandom2(nValue>>8);
3434                 v50 += QRandom2(nValue>>8);
3435                 vd8 += QRandom2(nValue>>4);
3436                 vd4 += QRandom2(nValue>>4);
3437                 vd0 += QRandom2(nValue);
3438             }
3439             if (pOther->quakeEffect)
3440             {
3441                 int nValue = ClipHigh(pOther->quakeEffect*8, 2000);
3442                 v54 += QRandom2(nValue >> 8);
3443                 v50 += QRandom2(nValue >> 8);
3444                 vd8 += QRandom2(nValue >> 4);
3445                 vd4 += QRandom2(nValue >> 4);
3446                 vd0 += QRandom2(nValue);
3447             }
3448             CalcOtherPosition(pOther->pSprite, &vd8, &vd4, &vd0, &vcc, v50, 0);
3449             CheckLink(&vd8, &vd4, &vd0, &vcc);
3450             if (IsUnderwaterSector(vcc))
3451             {
3452                 v14 = 10;
3453             }
3454             memcpy(bakMirrorGotpic, gotpic+510, 2);
3455             memcpy(gotpic+510, otherMirrorGotpic, 2);
3456             g_visibility = (int32_t)(ClipLow(gVisibility-32*pOther->visibility, 0) * (numplayers > 1 ? 1.f : r_ambientlightrecip));
3457             int vc4, vc8;
3458             getzsofslope(vcc, vd8, vd4, &vc8, &vc4);
3459             if (vd0 >= vc4)
3460             {
3461                 vd0 = vc4-(gUpperLink[vcc] >= 0 ? 0 : (8<<8));
3462             }
3463             if (vd0 <= vc8)
3464             {
3465                 vd0 = vc8+(gLowerLink[vcc] >= 0 ? 0 : (8<<8));
3466             }
3467             v54 = ClipRange(v54, -200, 200);
3468 RORHACKOTHER:
3469             int ror_status[16];
3470             for (int i = 0; i < 16; i++)
3471                 ror_status[i] = TestBitString(gotpic, 4080 + i);
3472             yax_preparedrawrooms();
3473             DrawMirrors(vd8, vd4, vd0, fix16_from_int(v50), fix16_from_int(v54 + defaultHoriz), gInterpolate, -1);
3474             drawrooms(vd8, vd4, vd0, v50, v54 + defaultHoriz, vcc);
3475             yax_drawrooms(viewProcessSprites, vcc, 0, gInterpolate);
3476             bool do_ror_hack = false;
3477             for (int i = 0; i < 16; i++)
3478                 if (ror_status[i] != TestBitString(gotpic, 4080 + i))
3479                     do_ror_hack = true;
3480             if (do_ror_hack)
3481             {
3482                 spritesortcnt = 0;
3483                 goto RORHACKOTHER;
3484             }
3485             memcpy(otherMirrorGotpic, gotpic+510, 2);
3486             memcpy(gotpic+510, bakMirrorGotpic, 2);
3487             viewProcessSprites(vd8, vd4, vd0, v50, gInterpolate);
3488             renderDrawMasks();
3489             renderRestoreTarget();
3490         }
3491         else
3492         {
3493             othercameraclock = (int)totalclock;
3494         }
3495 
3496         if (!bDelirium)
3497         {
3498             deliriumTilt = 0;
3499             deliriumTurn = 0;
3500             deliriumPitch = 0;
3501         }
3502         int nSprite = headspritestat[kStatExplosion];
3503         int unk = 0;
3504         while (nSprite >= 0)
3505         {
3506             spritetype *pSprite = &sprite[nSprite];
3507             int nXSprite = pSprite->extra;
3508             dassert(nXSprite > 0 && nXSprite < kMaxXSprites);
3509             XSPRITE *pXSprite = &xsprite[nXSprite];
3510             if (TestBitString(gotsector, pSprite->sectnum))
3511             {
3512                 unk += pXSprite->data3*32;
3513             }
3514             nSprite = nextspritestat[nSprite];
3515         }
3516         nSprite = headspritestat[kStatProjectile];
3517         while (nSprite >= 0) {
3518             spritetype *pSprite = &sprite[nSprite];
3519             switch (pSprite->type) {
3520                 case kMissileFlareRegular:
3521                 case kMissileTeslaAlt:
3522                 case kMissileFlareAlt:
3523                 case kMissileTeslaRegular:
3524                     if (TestBitString(gotsector, pSprite->sectnum)) unk += 256;
3525                     break;
3526             }
3527             nSprite = nextspritestat[nSprite];
3528         }
3529         g_visibility = (int32_t)(ClipLow(gVisibility - 32 * gView->visibility - unk, 0) * (numplayers > 1 ? 1.f : r_ambientlightrecip));
3530         cA = (cA + interpolateangfix16(fix16_from_int(deliriumTurnO), fix16_from_int(deliriumTurn), gInterpolate)) & 0x7ffffff;
3531         int vfc, vf8;
3532         getzsofslope(nSectnum, cX, cY, &vfc, &vf8);
3533         if (cZ >= vf8)
3534         {
3535             cZ = vf8-(gUpperLink[nSectnum] >= 0 ? 0 : (8<<8));
3536         }
3537         if (cZ <= vfc)
3538         {
3539             cZ = vfc+(gLowerLink[nSectnum] >= 0 ? 0 : (8<<8));
3540         }
3541         q16horiz = ClipRange(q16horiz, F16(-200), F16(200));
3542 RORHACK:
3543         int ror_status[16];
3544         for (int i = 0; i < 16; i++)
3545             ror_status[i] = TestBitString(gotpic, 4080+i);
3546         fix16_t deliriumPitchI = interpolate(fix16_from_int(deliriumPitchO), fix16_from_int(deliriumPitch), gInterpolate);
3547         DrawMirrors(cX, cY, cZ, cA, q16horiz + fix16_from_int(defaultHoriz) + deliriumPitchI, gInterpolate, gViewIndex);
3548         int bakCstat = gView->pSprite->cstat;
3549         if (gViewPos == 0)
3550         {
3551             gView->pSprite->cstat |= 32768;
3552         }
3553         else
3554         {
3555             gView->pSprite->cstat |= 514;
3556         }
3557 #ifdef POLYMER
3558         if (videoGetRenderMode() == REND_POLYMER)
3559             polymer_setanimatesprites(viewProcessSprites, cX, cY, cZ, fix16_to_int(cA), gInterpolate);
3560 #endif
3561         yax_preparedrawrooms();
3562         renderDrawRoomsQ16(cX, cY, cZ, cA, q16horiz + fix16_from_int(defaultHoriz) + deliriumPitchI, nSectnum);
3563         yax_drawrooms(viewProcessSprites, nSectnum, 0, gInterpolate);
3564         viewProcessSprites(cX, cY, cZ, fix16_to_int(cA), gInterpolate);
3565         bool do_ror_hack = false;
3566         for (int i = 0; i < 16; i++)
3567             if (ror_status[i] != TestBitString(gotpic, 4080+i))
3568                 do_ror_hack = true;
3569         if (do_ror_hack)
3570         {
3571             gView->pSprite->cstat = bakCstat;
3572             spritesortcnt = 0;
3573             goto RORHACK;
3574         }
3575         sub_5571C(1);
3576         int nSpriteSortCnt = spritesortcnt;
3577         renderDrawMasks();
3578         spritesortcnt = nSpriteSortCnt;
3579         sub_5571C(0);
3580         sub_557C4(cX, cY, gInterpolate);
3581         renderDrawMasks();
3582         gView->pSprite->cstat = bakCstat;
3583 
3584         if (v78 || bDelirium)
3585         {
3586             if (videoGetRenderMode() == REND_CLASSIC)
3587             {
3588                 dassert(waloff[ TILTBUFFER ] != 0);
3589                 renderRestoreTarget();
3590                 int vrc = 64+4+2+1024;
3591                 if (bDelirium)
3592                 {
3593                     vrc = 64+32+4+2+1+1024;
3594                 }
3595                 int nAng = v78 & 511;
3596                 if (nAng > 256)
3597                 {
3598                     nAng = 512 - nAng;
3599                 }
3600                 int nScale = dmulscale32(Cos(nAng), 262144, Sin(nAng), 163840)>>tiltcs;
3601                 rotatesprite(160<<16, 100<<16, nScale, v78+512, TILTBUFFER, 0, 0, vrc, gViewX0, gViewY0, gViewX1, gViewY1);
3602             }
3603 #ifdef USE_OPENGL
3604             else
3605             {
3606                 if (videoGetRenderMode() == REND_POLYMOST && gDeliriumBlur)
3607                 {
3608                     if (!bDeliriumOld)
3609                     {
3610                         glAccum(GL_LOAD, 1.f);
3611                     }
3612                     else
3613                     {
3614                         const float fBlur = pow(1.f/3.f, 30.f/g_frameRate);
3615                         glAccum(GL_MULT, fBlur);
3616                         glAccum(GL_ACCUM, 1.f-fBlur);
3617                         glAccum(GL_RETURN, 1.f);
3618                     }
3619                 }
3620             }
3621 #endif
3622         }
3623 
3624         bDeliriumOld = bDelirium && gDeliriumBlur;
3625 
3626         if (r_usenewaspect)
3627             newaspect_enable = 0;
3628         renderSetAspect(viewingRange, yxAspect);
3629         int nClipDist = gView->pSprite->clipdist<<2;
3630         int ve8, vec, vf0, vf4;
3631         GetZRange(gView->pSprite, &vf4, &vf0, &vec, &ve8, nClipDist, 0);
3632 #if 0
3633         int tmpSect = nSectnum;
3634         if ((vf0 & 0xc000) == 0x4000)
3635         {
3636             tmpSect = vf0 & (kMaxWalls-1);
3637         }
3638         int v8 = byte_1CE5C2 > 0 && (sector[tmpSect].ceilingstat&1);
3639         if (gWeather.at12d8 > 0 || v8)
3640         {
3641             gWeather.Draw(cX, cY, cZ, cA, q16horiz + defaultHoriz + deliriumPitch, gWeather.at12d8);
3642             if (v8)
3643             {
3644                 gWeather.at12d8 = ClipRange(delta*8+gWeather.at12d8, 0, 4095);
3645             }
3646             else
3647             {
3648                 gWeather.at12d8 = ClipRange(gWeather.at12d8-delta*64, 0, 4095);
3649             }
3650         }
3651 #endif
3652         if (gViewPos == 0)
3653         {
3654             if (gAimReticle)
3655             {
3656                 rotatesprite(160<<16, defaultHoriz<<16, 65536, 0, kCrosshairTile, 0, g_isAlterDefaultCrosshair ? CROSSHAIR_PAL : 0, 2, gViewX0, gViewY0, gViewX1, gViewY1);
3657             }
3658             cX = (v4c>>8)+160;
3659             cY = (v48>>8)+220+(zDelta>>7);
3660             int nShade = sector[nSectnum].floorshade; int nPalette = 0;
3661             if (sector[gView->pSprite->sectnum].extra > 0) {
3662                 sectortype *pSector = &sector[gView->pSprite->sectnum];
3663                 XSECTOR *pXSector = &xsector[pSector->extra];
3664                 if (pXSector->color)
3665                     nPalette = pSector->floorpal;
3666             }
3667 
3668             #ifdef NOONE_EXTENSIONS
3669                 if (gView->sceneQav < 0) WeaponDraw(gView, nShade, cX, cY, nPalette);
3670                 else if (gView->pXSprite->health > 0) playerQavSceneDraw(gView, nShade, cX, cY, nPalette);
3671                 else {
3672                     gView->sceneQav = gView->weaponQav = -1;
3673                     gView->weaponTimer = gView->curWeapon = 0;
3674                 }
3675             #else
3676                 WeaponDraw(gView, nShade, cX, cY, nPalette);
3677             #endif
3678 
3679 
3680         }
3681         if (gViewPos == 0 && gView->pXSprite->burnTime > 60)
3682         {
3683             viewBurnTime(gView->pXSprite->burnTime);
3684         }
3685         if (packItemActive(gView, 1))
3686         {
3687             rotatesprite(0, 0, 65536, 0, 2344, 0, 0, 256+18, gViewX0, gViewY0, gViewX1, gViewY1);
3688             rotatesprite(320<<16, 0, 65536, 1024, 2344, 0, 0, 512+22, gViewX0, gViewY0, gViewX1, gViewY1);
3689             rotatesprite(0, 200<<16, 65536, 0, 2344, 0, 0, 256+22, gViewX0, gViewY0, gViewX1, gViewY1);
3690             rotatesprite(320<<16, 200<<16, 65536, 1024, 2344, 0, 0, 512+18, gViewX0, gViewY0, gViewX1, gViewY1);
3691             if (gDetail >= 4)
3692             {
3693                 rotatesprite(15<<16, 3<<16, 65536, 0, 2346, 32, 0, 256+19, gViewX0, gViewY0, gViewX1, gViewY1);
3694                 rotatesprite(212<<16, 77<<16, 65536, 0, 2347, 32, 0, 512+19, gViewX0, gViewY0, gViewX1, gViewY1);
3695             }
3696         }
3697         if (powerupCheck(gView, kPwUpAsbestArmor) > 0)
3698         {
3699             rotatesprite(0, 200<<16, 65536, 0, 2358, 0, 0, 256+22, gViewX0, gViewY0, gViewX1, gViewY1);
3700             rotatesprite(320<<16, 200<<16, 65536, 1024, 2358, 0, 0, 512+18, gViewX0, gViewY0, gViewX1, gViewY1);
3701         }
3702         if (v4 && gNetPlayers > 1)
3703         {
3704             DoLensEffect();
3705             viewingRange = viewingrange;
3706             yxAspect = yxaspect;
3707             renderSetAspect(65536, 54613);
3708             rotatesprite(280<<16, 35<<16, 53248, 512, 4077, v10, v14, 512+6, gViewX0, gViewY0, gViewX1, gViewY1);
3709             rotatesprite(280<<16, 35<<16, 53248, 0, 1683, v10, 0, 512+35, gViewX0, gViewY0, gViewX1, gViewY1);
3710             renderSetAspect(viewingRange, yxAspect);
3711         }
3712 
3713         if (powerupCheck(gView, kPwUpDeathMask) > 0) nPalette = 4;
3714         else if(powerupCheck(gView, kPwUpReflectShots) > 0) nPalette = 1;
3715         else if (gView->isUnderwater) {
3716             if (gView->nWaterPal) nPalette = gView->nWaterPal;
3717             else {
3718                 if (gView->pXSprite->medium == kMediumWater) nPalette = 1;
3719                 else if (gView->pXSprite->medium == kMediumGoo) nPalette = 3;
3720                 else nPalette = 2;
3721             }
3722         }
3723     }
3724     if (gViewMode == 4)
3725     {
3726         gViewMap.sub_25DB0(gView->pSprite);
3727     }
3728     viewDrawInterface(delta);
3729     int zn = ((gView->zWeapon-gView->zView-(12<<8))>>7)+220;
3730     PLAYER *pPSprite = &gPlayer[gMe->pSprite->type-kDudePlayer1];
3731     if (IsPlayerSprite(gMe->pSprite) && pPSprite->hand == 1)
3732     {
3733         //static int lastClock;
3734         gChoke.sub_84110(160, zn);
3735         //if ((gGameClock % 5) == 0 && gGameClock != lastClock)
3736         //{
3737         //    gChoke.swayV(pPSprite);
3738         //}
3739         //lastClock = gGameClock;
3740     }
3741     if (byte_1A76C6)
3742     {
3743         DrawStatSprite(2048, xdim-15, 20);
3744     }
3745     viewDisplayMessage();
3746     CalcFrameRate();
3747 #if 0
3748     if (gShowFrameRate)
3749     {
3750         int fX, fY;
3751         if (gViewMode == 3)
3752         {
3753             fX = gViewX1;
3754         }
3755         else
3756         {
3757             fX = xdim;
3758         }
3759         if (gViewMode == 3)
3760         {
3761             fY = gViewY0;
3762         }
3763         else
3764         {
3765             fY = 0;
3766         }
3767         if (gViewMode == 4)
3768         {
3769             fY += mulscale16(20, yscale);
3770         }
3771         sprintf(gTempStr, "%3i", gFrameRate);
3772         printext256(fX-12, fY, 31, -1, gTempStr, 1);
3773         fY += 8;
3774         sprintf(gTempStr, "pos=%d,%d,%d", gView->pSprite->x, gView->pSprite->y, gView->pSprite->z);
3775         printext256(fX-strlen(gTempStr)*4, fY, 31, -1, gTempStr, 1);
3776     }
3777 #endif
3778     viewDrawMapTitle();
3779     viewDrawAimedPlayerName();
3780     viewPrintFPS();
3781     if (gPaused)
3782     {
3783         viewDrawText(1, "PAUSED", 160, 10, 0, 0, 1, 0);
3784     }
3785     else if (gView != gMe)
3786     {
3787         sprintf(gTempStr, "] %s [", gProfile[gView->nPlayer].name);
3788         viewDrawText(0, gTempStr, 160, 10, 0, 0, 1, 0);
3789     }
3790     if (errMsg[0])
3791     {
3792         viewDrawText(0, errMsg, 160, 20, 0, 0, 1, 0);
3793     }
3794     if (gViewInterpolate)
3795     {
3796         RestoreInterpolations();
3797     }
3798     UpdateDacs(nPalette);
3799 }
3800 
3801 int nLoadingScreenTile;
3802 char pzLoadingScreenText1[256], pzLoadingScreenText2[256], pzLoadingScreenText3[256];
3803 
viewLoadingScreenWide(void)3804 void viewLoadingScreenWide(void)
3805 {
3806     videoClearScreen(0);
3807 #ifdef USE_OPENGL
3808     if ((blood_globalflags&BLOOD_FORCE_WIDELOADSCREEN) || (bLoadScreenCrcMatch && !(usehightile && h_xsize[kLoadScreen])))
3809 #else
3810     if ((blood_globalflags&BLOOD_FORCE_WIDELOADSCREEN) || bLoadScreenCrcMatch)
3811 #endif
3812     {
3813         if (yxaspect >= 65536)
3814         {
3815             rotatesprite(160<<16, 100<<16, 65536, 0, kLoadScreen, 0, 0, 1024+64+8+2, 0, 0, xdim-1, ydim-1);
3816         }
3817         else
3818         {
3819             int width = roundscale(xdim, 240, ydim);
3820             int nCount = (width+kLoadScreenWideBackWidth-1)/kLoadScreenWideBackWidth;
3821             for (int i = 0; i < nCount; i++)
3822             {
3823                 rotatesprite_fs((i*kLoadScreenWideBackWidth)<<16, 0, 65536, 0, kLoadScreenWideBack, 0, 0, 256+64+16+8+2);
3824             }
3825             rotatesprite_fs((kLoadScreenWideSideWidth>>1)<<16, 200<<15, 65536, 0, kLoadScreenWideLeft, 0, 0, 256+8+2);
3826             rotatesprite_fs((320-(kLoadScreenWideSideWidth>>1))<<16, 200<<15, 65536, 0, kLoadScreenWideRight, 0, 0, 512+8+2);
3827             rotatesprite_fs(320<<15, 200<<15, 65536, 0, kLoadScreenWideMiddle, 0, 0, 8+2);
3828         }
3829     }
3830     else
3831         rotatesprite(160<<16, 100<<16, 65536, 0, kLoadScreen, 0, 0, 64+8+2, 0, 0, xdim-1, ydim-1);
3832 }
3833 
viewLoadingScreenUpdate(const char * pzText4,int nPercent)3834 void viewLoadingScreenUpdate(const char *pzText4, int nPercent)
3835 {
3836     int vc;
3837     gMenuTextMgr.GetFontInfo(1, NULL, NULL, &vc);
3838     if (nLoadingScreenTile == kLoadScreen)
3839         viewLoadingScreenWide();
3840     else if (nLoadingScreenTile)
3841     {
3842         videoClearScreen(0);
3843         rotatesprite(160<<16, 100<<16, 65536, 0, nLoadingScreenTile, 0, 0, 74, 0, 0, xdim-1, ydim-1);
3844     }
3845     if (pzLoadingScreenText1[0])
3846     {
3847         rotatesprite(160<<16, 20<<16, 65536, 0, 2038, -128, 0, 78, 0, 0, xdim-1, ydim-1);
3848         viewDrawText(1, pzLoadingScreenText1, 160, 20-vc/2, -128, 0, 1, 1);
3849     }
3850     if (pzLoadingScreenText2[0])
3851     {
3852         viewDrawText(1, pzLoadingScreenText2, 160, 50, -128, 0, 1, 1);
3853     }
3854     if (pzLoadingScreenText3[0])
3855     {
3856         viewDrawText(1, pzLoadingScreenText3, 160, 70, -128, 0, 1, 1);
3857     }
3858     if (pzText4)
3859     {
3860         viewDrawText(3, pzText4, 160, 124, -128, 0, 1, 1);
3861     }
3862 
3863     if (nPercent != -1)
3864         TileHGauge(2260, 86, 110, nPercent, 100, 0, 131072);
3865 
3866     viewDrawText(3, "Please Wait", 160, 134, -128, 0, 1, 1);
3867 }
3868 
viewLoadingScreen(int nTile,const char * pText,const char * pText2,const char * pText3)3869 void viewLoadingScreen(int nTile, const char *pText, const char *pText2, const char *pText3)
3870 {
3871     UpdateDacs(0, true);
3872     nLoadingScreenTile = nTile;
3873     if (pText)
3874         strncpy(pzLoadingScreenText1, pText, 256);
3875     else
3876         pzLoadingScreenText1[0] = 0;
3877     if (pText2)
3878         strncpy(pzLoadingScreenText2, pText2, 256);
3879     else
3880         pzLoadingScreenText2[0] = 0;
3881     if (pText3)
3882         strncpy(pzLoadingScreenText3, pText3, 256);
3883     else
3884         pzLoadingScreenText3[0] = 0;
3885     viewLoadingScreenUpdate(NULL, -1);
3886 }
3887 
3888 palette_t CrosshairColors = { 255, 255, 255, 0 };
3889 bool g_isAlterDefaultCrosshair = false;
3890 
viewSetCrosshairColor(int32_t r,int32_t g,int32_t b)3891 void viewSetCrosshairColor(int32_t r, int32_t g, int32_t b)
3892 {
3893     if (!g_isAlterDefaultCrosshair)
3894         return;
3895 
3896     CrosshairColors.r = r;
3897     CrosshairColors.g = g;
3898     CrosshairColors.b = b;
3899 
3900     tileLoad(kCrosshairTile);
3901 
3902     if (!waloff[kCrosshairTile])
3903         return;
3904 
3905     char *ptr = (char *)waloff[kCrosshairTile];
3906 
3907     int32_t ii = tilesiz[kCrosshairTile].x * tilesiz[kCrosshairTile].y;
3908 
3909     dassert(ii > 0);
3910 
3911     int32_t i = (videoGetRenderMode() == REND_CLASSIC)
3912         ? paletteGetClosestColor(CrosshairColors.r, CrosshairColors.g, CrosshairColors.b)
3913         : paletteGetClosestColor(255, 255, 255);  // use white in GL so we can tint it to the right color
3914 
3915     do
3916     {
3917         if (*ptr != 255)
3918             *ptr = i;
3919         ptr++;
3920     } while (--ii);
3921 
3922     paletteMakeLookupTable(CROSSHAIR_PAL, NULL, CrosshairColors.r, CrosshairColors.g, CrosshairColors.b, 1);
3923 
3924 #ifdef USE_OPENGL
3925     // XXX: this makes us also load all hightile textures tinted with the crosshair color!
3926     polytint_t & crosshairtint = hictinting[CROSSHAIR_PAL];
3927     crosshairtint.r = CrosshairColors.r;
3928     crosshairtint.g = CrosshairColors.g;
3929     crosshairtint.b = CrosshairColors.b;
3930     crosshairtint.f = HICTINT_USEONART | HICTINT_GRAYSCALE;
3931 #endif
3932     tileInvalidate(kCrosshairTile, -1, -1);
3933 }
3934 
viewResetCrosshairToDefault(void)3935 void viewResetCrosshairToDefault(void)
3936 {
3937     paletteFreeLookupTable(CROSSHAIR_PAL);
3938     tileLoad(kCrosshairTile);
3939 }
3940 
3941 #define COLOR_RED redcol
3942 #define COLOR_WHITE whitecol
3943 
3944 #define LOW_FPS 60
3945 #define SLOW_FRAME_TIME 20
3946 
3947 #if defined GEKKO
3948 # define FPS_YOFFSET 16
3949 #else
3950 # define FPS_YOFFSET 0
3951 #endif
3952 
3953 #define FPS_COLOR(x) ((x) ? COLOR_RED : COLOR_WHITE)
3954 
3955 int32_t gShowFps, gFramePeriod;
3956 
viewPrintFPS(void)3957 void viewPrintFPS(void)
3958 {
3959     char tempbuf[128];
3960     static int32_t frameCount;
3961     static double cumulativeFrameDelay;
3962     static double lastFrameTime;
3963     static float lastFPS, minFPS = FLT_MAX, maxFPS;
3964     static double minGameUpdate = DBL_MAX, maxGameUpdate;
3965 
3966     double frameTime = timerGetHiTicks();
3967     double frameDelay = frameTime - lastFrameTime;
3968     cumulativeFrameDelay += frameDelay;
3969 
3970     if (frameDelay >= 0)
3971     {
3972         int32_t x = (xdim <= 640);
3973 
3974         if (gShowFps)
3975         {
3976             int32_t chars = Bsprintf(tempbuf, "%.1f ms, %5.1f fps", frameDelay, lastFPS);
3977 
3978             printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+2+FPS_YOFFSET, 0, -1, tempbuf, x);
3979             printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+1+FPS_YOFFSET,
3980                 FPS_COLOR(lastFPS < LOW_FPS), -1, tempbuf, x);
3981 
3982             if (gShowFps > 1)
3983             {
3984                 chars = Bsprintf(tempbuf, "max: %5.1f fps", maxFPS);
3985 
3986                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+10+2+FPS_YOFFSET, 0, -1, tempbuf, x);
3987                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+10+FPS_YOFFSET,
3988                     FPS_COLOR(maxFPS < LOW_FPS), -1, tempbuf, x);
3989 
3990                 chars = Bsprintf(tempbuf, "min: %5.1f fps", minFPS);
3991 
3992                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+20+2+FPS_YOFFSET, 0, -1, tempbuf, x);
3993                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+20+FPS_YOFFSET,
3994                     FPS_COLOR(minFPS < LOW_FPS), -1, tempbuf, x);
3995             }
3996             if (gShowFps > 2)
3997             {
3998                 if (g_gameUpdateTime > maxGameUpdate) maxGameUpdate = g_gameUpdateTime;
3999                 if (g_gameUpdateTime < minGameUpdate) minGameUpdate = g_gameUpdateTime;
4000 
4001                 chars = Bsprintf(tempbuf, "Game Update: %2.2f ms + draw: %2.2f ms", g_gameUpdateTime, g_gameUpdateAndDrawTime);
4002 
4003                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+30+2+FPS_YOFFSET, 0, -1, tempbuf, x);
4004                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+30+FPS_YOFFSET,
4005                     FPS_COLOR(g_gameUpdateAndDrawTime >= SLOW_FRAME_TIME), -1, tempbuf, x);
4006 
4007                 chars = Bsprintf(tempbuf, "GU min/max/avg: %5.2f/%5.2f/%5.2f ms", minGameUpdate, maxGameUpdate, g_gameUpdateAvgTime);
4008 
4009                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+40+2+FPS_YOFFSET, 0, -1, tempbuf, x);
4010                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+40+FPS_YOFFSET,
4011                     FPS_COLOR(maxGameUpdate >= SLOW_FRAME_TIME), -1, tempbuf, x);
4012 
4013                 chars = Bsprintf(tempbuf, "bufferjitter: %i", gBufferJitter);
4014 
4015                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+50+2+FPS_YOFFSET, 0, -1, tempbuf, x);
4016                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+50+FPS_YOFFSET,
4017                     COLOR_WHITE, -1, tempbuf, x);
4018 #if 0
4019                 chars = Bsprintf(tempbuf, "G_MoveActors(): %.3e ms", g_moveActorsTime);
4020 
4021                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+50+2+FPS_YOFFSET, 0, -1, tempbuf, x);
4022                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+50+FPS_YOFFSET,
4023                     COLOR_WHITE, -1, tempbuf, x);
4024 
4025                 chars = Bsprintf(tempbuf, "G_MoveWorld(): %.3e ms", g_moveWorldTime);
4026 
4027                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+60+2+FPS_YOFFSET, 0, -1, tempbuf, x);
4028                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+60+FPS_YOFFSET,
4029                     COLOR_WHITE, -1, tempbuf, x);
4030 #endif
4031             }
4032 #if 0
4033             // lag meter
4034             if (g_netClientPeer)
4035             {
4036                 chars = Bsprintf(tempbuf, "%d +- %d ms", (g_netClientPeer->lastRoundTripTime + g_netClientPeer->roundTripTime)/2,
4037                     (g_netClientPeer->lastRoundTripTimeVariance + g_netClientPeer->roundTripTimeVariance)/2);
4038 
4039                 printext256(windowxy2.x-(chars<<(3-x))+1, windowxy1.y+30+2+FPS_YOFFSET, 0, -1, tempbuf, x);
4040                 printext256(windowxy2.x-(chars<<(3-x)), windowxy1.y+30+1+FPS_YOFFSET, FPS_COLOR(g_netClientPeer->lastRoundTripTime > 200), -1, tempbuf, x);
4041             }
4042 #endif
4043         }
4044 
4045         if (cumulativeFrameDelay >= 1000.0)
4046         {
4047             lastFPS = 1000.f * frameCount / cumulativeFrameDelay;
4048             g_frameRate = Blrintf(lastFPS);
4049             frameCount = 0;
4050             cumulativeFrameDelay = 0.0;
4051 
4052             if (gShowFps > 1)
4053             {
4054                 if (lastFPS > maxFPS) maxFPS = lastFPS;
4055                 if (lastFPS < minFPS) minFPS = lastFPS;
4056 
4057                 static int secondCounter;
4058 
4059                 if (++secondCounter >= gFramePeriod)
4060                 {
4061                     maxFPS = (lastFPS + maxFPS) * .5f;
4062                     minFPS = (lastFPS + minFPS) * .5f;
4063                     maxGameUpdate = (g_gameUpdateTime + maxGameUpdate) * 0.5;
4064                     minGameUpdate = (g_gameUpdateTime + minGameUpdate) * 0.5;
4065                     secondCounter = 0;
4066                 }
4067             }
4068         }
4069         frameCount++;
4070     }
4071     lastFrameTime = frameTime;
4072 }
4073 
4074 #undef FPS_COLOR
4075 
4076 class ViewLoadSave : public LoadSave {
4077 public:
4078     void Load(void);
4079     void Save(void);
4080 };
4081 
4082 static ViewLoadSave *myLoadSave;
4083 
4084 static int messageTime;
4085 static char message[256];
4086 
Load(void)4087 void ViewLoadSave::Load(void)
4088 {
4089     Read(&messageTime, sizeof(messageTime));
4090     Read(message, sizeof(message));
4091     Read(otherMirrorGotpic, sizeof(otherMirrorGotpic));
4092     Read(bakMirrorGotpic, sizeof(bakMirrorGotpic));
4093     Read(&gScreenTilt, sizeof(gScreenTilt));
4094     Read(&deliriumTilt, sizeof(deliriumTilt));
4095     Read(&deliriumTurn, sizeof(deliriumTurn));
4096     Read(&deliriumPitch, sizeof(deliriumPitch));
4097 }
4098 
Save(void)4099 void ViewLoadSave::Save(void)
4100 {
4101     Write(&messageTime, sizeof(messageTime));
4102     Write(message, sizeof(message));
4103     Write(otherMirrorGotpic, sizeof(otherMirrorGotpic));
4104     Write(bakMirrorGotpic, sizeof(bakMirrorGotpic));
4105     Write(&gScreenTilt, sizeof(gScreenTilt));
4106     Write(&deliriumTilt, sizeof(deliriumTilt));
4107     Write(&deliriumTurn, sizeof(deliriumTurn));
4108     Write(&deliriumPitch, sizeof(deliriumPitch));
4109 }
4110 
ViewLoadSaveConstruct(void)4111 void ViewLoadSaveConstruct(void)
4112 {
4113     myLoadSave = new ViewLoadSave();
4114 }
4115