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