game.cpp 237 KB
Newer Older
1
2
//-------------------------------------------------------------------------
/*
3
Copyright (C) 2016 EDuke32 developers and contributors
4

5
This file is part of EDuke32.
6
7
8
9
10
11
12
13
14
15
16
17
18

EDuke32 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21
22
*/
//-------------------------------------------------------------------------

23
24
#define game_c_

25
26
27
28
#include "anim.h"
#include "cheats.h"
#include "cmdline.h"
#include "colmatch.h"
29
#include "communityapi.h"
30
#include "compat.h"
31
#include "crc32.h"
32
#include "demo.h"
33
#include "duke3d.h"
34
#include "input.h"
35
36
37
38
39
40
41
42
#include "menus.h"
#include "microprofile.h"
#include "network.h"
#include "osdcmds.h"
#include "osdfuncs.h"
#include "palette.h"
#include "renderlayer.h"
#include "savegame.h"
43
#include "sbar.h"
44
#include "screens.h"
45

46
47
48
49
#ifdef __ANDROID__
#include "android.h"
#endif

50
51
#include "vfs.h"

52
// Uncomment to prevent anything except mirrors from drawing. It is sensible to
53
// also uncomment ENGINE_CLEAR_SCREEN in build/src/engine_priv.h.
54
55
//#define DEBUG_MIRRORS_ONLY

56
57
58
59
60
61
62
63
#if KRANDDEBUG
# define GAME_INLINE
# define GAME_STATIC
#else
# define GAME_INLINE inline
# define GAME_STATIC static
#endif

64
#ifdef _WIN32
65
66
67
# include <shellapi.h>
# define UPDATEINTERVAL 604800 // 1w
# include "winbits.h"
Richard Gobeille's avatar
   
Richard Gobeille committed
68
#else
69
70
71
# ifndef GEKKO
#  include <sys/ioctl.h>
# endif
Richard Gobeille's avatar
   
Richard Gobeille committed
72
#endif /* _WIN32 */
73

Evan Ramos's avatar
Evan Ramos committed
74
75
const char* AppProperName = APPNAME;
const char* AppTechnicalName = APPBASENAME;
Evan Ramos's avatar
Evan Ramos committed
76

77
int32_t g_quitDeadline = 0;
78

Richard Gobeille's avatar
Richard Gobeille committed
79
int32_t g_cameraDistance = 0, g_cameraClock = 0;
80
static int32_t g_quickExit;
81

82
char boardfilename[BMAX_PATH] = {0}, currentboardfilename[BMAX_PATH] = {0};
83
char previousboardfilename[BMAX_PATH] = {0};
84

Richard Gobeille's avatar
Richard Gobeille committed
85
86
int32_t voting = -1;
int32_t vote_map = -1, vote_episode = -1;
87

88
89
int32_t g_BenchmarkMode = BENCHMARKMODE_OFF;

90
int32_t g_Debug = 0;
Richard Gobeille's avatar
Richard Gobeille committed
91

92
93
94
#ifndef EDUKE32_STANDALONE
static const char *defaultrtsfilename[GAMECOUNT] = { "DUKE.RTS", "NAM.RTS", "NAPALM.RTS", "WW2GI.RTS" };
#endif
95

Richard Gobeille's avatar
Richard Gobeille committed
96
int32_t g_Shareware = 0;
Richard Gobeille's avatar
Richard Gobeille committed
97

98
99
100
// This was 32 for a while, but I think lowering it to 24 will help things like the Dingoo.
// Ideally, we would look at our memory usage on our most cramped platform and figure out
// how much of that is needed for the underlying OS and things like SDL instead of guessing
101
#ifndef GEKKO
102
int32_t MAXCACHE1DSIZE = (96*1024*1024);
103
#else
104
int32_t MAXCACHE1DSIZE = (8*1024*1024);
105
#endif
106

Richard Gobeille's avatar
Richard Gobeille committed
107
int32_t tempwallptr;
Richard Gobeille's avatar
Richard Gobeille committed
108

Richard Gobeille's avatar
Richard Gobeille committed
109
static int32_t nonsharedtimer;
Richard Gobeille's avatar
Richard Gobeille committed
110

111
112
int32_t ticrandomseed;

Richard Gobeille's avatar
Richard Gobeille committed
113
int32_t hud_showmapname = 1;
114

Richard Gobeille's avatar
Richard Gobeille committed
115
int32_t g_levelTextTime = 0;
Richard Gobeille's avatar
   
Richard Gobeille committed
116

Richard Gobeille's avatar
   
Richard Gobeille committed
117
#if defined(RENDERTYPEWIN) && defined(USE_OPENGL)
Richard Gobeille's avatar
   
Richard Gobeille committed
118
119
120
extern char forcegl;
#endif

121
void M32RunScript(const char *s) { UNREFERENCED_PARAMETER(s); };  // needed for linking since it's referenced from build/src/osd.c
122

123
const char *G_DefaultRtsFile(void)
124
{
125
#ifndef EDUKE32_STANDALONE
126
127
128
129
130
131
    if (DUKE)
        return defaultrtsfilename[GAME_DUKE];
    else if (WW2GI)
        return defaultrtsfilename[GAME_WW2GI];
    else if (NAPALM)
    {
132
        if (!testkopen(defaultrtsfilename[GAME_NAPALM],0) && testkopen(defaultrtsfilename[GAME_NAM],0))
133
            return defaultrtsfilename[GAME_NAM]; // NAM/NAPALM Sharing
134
135
136
137
138
        else
            return defaultrtsfilename[GAME_NAPALM];
    }
    else if (NAM)
    {
139
        if (!testkopen(defaultrtsfilename[GAME_NAM],0) && testkopen(defaultrtsfilename[GAME_NAPALM],0))
140
            return defaultrtsfilename[GAME_NAPALM]; // NAM/NAPALM Sharing
141
142
143
        else
            return defaultrtsfilename[GAME_NAM];
    }
144
#endif
145

146
    return "";
147
148
}

149
enum gametokens
150
{
Richard Gobeille's avatar
   
Richard Gobeille committed
151
    T_INCLUDE = 0,
Richard Gobeille's avatar
Richard Gobeille committed
152
    T_INTERFACE = 0,
Richard Gobeille's avatar
   
Richard Gobeille committed
153
    T_LOADGRP = 1,
Richard Gobeille's avatar
Richard Gobeille committed
154
    T_MODE = 1,
Richard Gobeille's avatar
   
Richard Gobeille committed
155
    T_CACHESIZE = 2,
156
    T_ALLOW = 2,
157
    T_DEFINE,
158
    T_NOAUTOLOAD,
159
    T_INCLUDEDEFAULT,
160
161
162
    T_MUSIC,
    T_SOUND,
    T_FILE,
163
    T_CUTSCENE,
164
    T_ANIMSOUNDS,
165
    T_NOFLOORPALRANGE,
166
    T_ID,
167
168
169
170
171
172
    T_MINPITCH,
    T_MAXPITCH,
    T_PRIORITY,
    T_TYPE,
    T_DISTANCE,
    T_VOLUME,
173
174
    T_DELAY,
    T_RENAMEFILE,
175
    T_GLOBALGAMEFLAGS,
176
    T_ASPECT,
177
178
179
    T_FORCEFILTER,
    T_FORCENOFILTER,
    T_TEXTUREFILTER,
Evan Ramos's avatar
Evan Ramos committed
180
181
182
183
184
    T_NEWGAMECHOICES,
    T_CHOICE,
    T_NAME,
    T_LOCKED,
    T_HIDDEN,
Evan Ramos's avatar
Evan Ramos committed
185
    T_USERCONTENT,
Evan Ramos's avatar
Locale    
Evan Ramos committed
186
    T_LOCALIZATION,
Richard Gobeille's avatar
Richard Gobeille committed
187
188
};

Richard Gobeille's avatar
Richard Gobeille committed
189
190
191
192
193
194
195
static void gameTimerHandler(void)
{
    MUSIC_Update();
    G_HandleSpecialKeys();
}


Richard Gobeille's avatar
Richard Gobeille committed
196
void G_HandleSpecialKeys(void)
197
{
198
199
    auto &myplayer = *g_player[myconnectindex].ps;

200
    // we need CONTROL_GetInput in order to pick up joystick button presses
201
    if (CONTROL_Started && (!(myplayer.gm & MODE_GAME) || (myplayer.gm & MODE_MENU)))
202
203
204
205
206
    {
        ControlInfo noshareinfo;
        CONTROL_GetInput(&noshareinfo);
    }

Richard Gobeille's avatar
Richard Gobeille committed
207
    if (g_networkMode != NET_DEDICATED_SERVER && ALT_IS_PRESSED && KB_KeyPressed(sc_Enter))
208
    {
209
        if (videoSetGameMode(!ud.setup.fullscreen, ud.setup.xdim, ud.setup.ydim, ud.setup.bpp, ud.detail))
210
        {
211
212
            OSD_Printf(OSD_ERROR "Failed setting video mode!\n");

213
            if (videoSetGameMode(ud.setup.fullscreen, ud.setup.xdim, ud.setup.ydim, ud.setup.bpp, ud.detail))
214
                G_GameExit("Fatal error: unable to recover from failure setting video mode!\n");
215
        }
216
217
218
        else
            ud.setup.fullscreen = !ud.setup.fullscreen;

219
        KB_ClearKeyDown(sc_Enter);
Richard Gobeille's avatar
Richard Gobeille committed
220
221
        g_restorePalette = 1;
        G_UpdateScreenArea();
222
223
    }

Richard Gobeille's avatar
   
Richard Gobeille committed
224
    if (KB_UnBoundKeyPressed(sc_F12))
225
226
    {
        KB_ClearKeyDown(sc_F12);
Richard Gobeille's avatar
Richard Gobeille committed
227
        videoCaptureScreen(
228
#ifndef EDUKE32_STANDALONE
229
        "duke0000.tga"
230
#else
231
        "capt0000.tga"
232
#endif
233
234
        ,
        0);
235
        P_DoQuote(QUOTE_SCREEN_SAVED, &myplayer);
236
237
    }

238
    // only dispatch commands here when not in a game
239
    if ((myplayer.gm & MODE_GAME) != MODE_GAME)
240
        OSD_DispatchQueued();
241

242
#ifdef DEBUGGINGAIDS
243
    if (g_quickExit == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && KB_KeyPressed(sc_End))
244
    {
245
        g_quickExit = 1;
Richard Gobeille's avatar
Richard Gobeille committed
246
        G_GameExit("Quick Exit.");
247
    }
248
#endif
Richard Gobeille's avatar
Richard Gobeille committed
249
250
}

251
void G_GameQuit(void)
252
{
253
254
255
256
    if (numplayers < 2)
        G_GameExit(" ");

    if (g_gameQuit == 0)
257
    {
258
        g_gameQuit = 1;
259
        g_quitDeadline = (int32_t) totalclock+120;
260
        g_netDisconnect = 1;
261
262
    }

263
    if ((totalclock > g_quitDeadline) && (g_gameQuit == 1))
264
265
266
        G_GameExit("Timed out.");
}

267

268
269
int32_t A_CheckInventorySprite(spritetype *s)
{
270
    switch (tileGetMapping(s->picnum))
271
    {
272
273
274
275
276
277
278
    case FIRSTAID__:
    case STEROIDS__:
    case HEATSENSOR__:
    case BOOTS__:
    case JETPACK__:
    case HOLODUKE__:
    case AIRTANK__:
279
280
281
        return 1;
    default:
        return 0;
282
    }
283
284
}

285

286

287
void G_GameExit(const char *msg)
288
{
289
290
    if (*msg != 0 && g_player[myconnectindex].ps != NULL)
        g_player[myconnectindex].ps->palette = BASEPAL;
291

292
293
    if (ud.recstat == 1)
        G_CloseDemoWrite();
294
    else if (ud.recstat == 2)
295
296
297
        MAYBE_FCLOSE_AND_NULL(g_demo_filePtr);
    // JBF: fixes crash on demo playback
    // PK: modified from original
298

299
    if (!g_quickExit)
300
    {
301
        if (VM_OnEventWithReturn(EVENT_EXITGAMESCREEN, g_player[myconnectindex].ps->i, myconnectindex, 0) == 0 &&
302
           g_mostConcurrentPlayers > 1 && g_player[myconnectindex].ps->gm & MODE_GAME && GTFLAGS(GAMETYPE_SCORESHEET) && *msg == ' ')
303
        {
304
            G_BonusScreen(1);
305
            videoSetGameMode(ud.setup.fullscreen, ud.setup.xdim, ud.setup.ydim, ud.setup.bpp, ud.detail);
306
307
        }

308
309
310
        // shareware and TEN screens
        if (VM_OnEventWithReturn(EVENT_EXITPROGRAMSCREEN, g_player[myconnectindex].ps->i, myconnectindex, 0) == 0 &&
           *msg != 0 && *(msg+1) != 'V' && *(msg+1) != 'Y')
311
            G_DisplayExtraScreens();
312
313
    }

314
    if (*msg != 0) initprintf("%s\n",msg);
315

316
    if (in3dmode())
317
        G_Shutdown();
318

319
    if (*msg != 0)
320
    {
321
        if (!(msg[0] == ' ' && msg[1] == 0))
322
323
        {
            char titlebuf[256];
324
            Bsprintf(titlebuf,HEAD2 " %s",s_buildRev);
325
            wm_msgbox(titlebuf, "%s", msg);
326
        }
327
328
    }

329
330
    Bfflush(NULL);

331
    exit(EXIT_SUCCESS);
332
}
333

Richard Gobeille's avatar
Richard Gobeille committed
334

335
336
337
338
#ifdef YAX_DEBUG
// ugh...
char m32_debugstr[64][128];
int32_t m32_numdebuglines=0;
Richard Gobeille's avatar
Richard Gobeille committed
339

340
341
static void M32_drawdebug(void)
{
Richard Gobeille's avatar
Richard Gobeille committed
342
    int i, col=paletteGetClosestColor(255,255,255);
343
    int x=4, y=8;
344

345
    if (m32_numdebuglines>0)
346
    {
347
        videoBeginDrawing();
348
349
        for (i=0; i<m32_numdebuglines && y<ydim-8; i++, y+=8)
            printext256(x,y,col,0,m32_debugstr[i],xdim>640?0:1);
350
        videoEndDrawing();
351
    }
352
    m32_numdebuglines=0;
353
}
354
#endif
Richard Gobeille's avatar
Richard Gobeille committed
355

356

357
static int32_t G_DoThirdPerson(const DukePlayer_t *pp, vec3_t *vect, int16_t *vsectnum, int16_t ang, int16_t horiz)
358
{
359
    auto const sp = &sprite[pp->i];
360
361
362
    int32_t i, hx, hy;
    int32_t bakcstat = sp->cstat;
    hitdata_t hit;
363

364
365
366
    vec3_t n = {
        sintable[(ang+1536)&2047]>>4,
        sintable[(ang+1024)&2047]>>4,
367
        (horiz-100) * 128
368
    };
Richard Gobeille's avatar
Richard Gobeille committed
369

370
    updatesectorz(vect->x,vect->y,vect->z,vsectnum);
371

372
373
374
    sp->cstat &= ~0x101;
    hitscan(vect, *vsectnum, n.x,n.y,n.z, &hit, CLIPMASK1);
    sp->cstat = bakcstat;
375

376
377
    if (*vsectnum < 0)
        return -1;
378

379
380
    hx = hit.pos.x-(vect->x);
    hy = hit.pos.y-(vect->y);
381

382
    if (klabs(n.x)+klabs(n.y) > klabs(hx)+klabs(hy))
383
    {
384
        *vsectnum = hit.sect;
385

386
        if (hit.wall >= 0)
387
        {
388
            int32_t daang = getangle(wall[wall[hit.wall].point2].x-wall[hit.wall].x,
389
                             wall[wall[hit.wall].point2].y-wall[hit.wall].y);
390

391
            i = n.x*sintable[daang] + n.y*sintable[(daang+1536)&2047];
392

393
394
395
            if (klabs(n.x) > klabs(n.y))
                hx -= mulscale28(n.x,i);
            else hy -= mulscale28(n.y,i);
396
        }
397
        else if (hit.sprite < 0)
398
        {
399
400
401
            if (klabs(n.x) > klabs(n.y))
                hx -= (n.x>>5);
            else hy -= (n.y>>5);
402
403
        }

404
405
406
        if (klabs(n.x) > klabs(n.y))
            i = divscale16(hx,n.x);
        else i = divscale16(hy,n.y);
407

408
409
410
        if (i < CAMERADIST)
            CAMERADIST = i;
    }
411

412
413
414
    vect->x += mulscale16(n.x,CAMERADIST);
    vect->y += mulscale16(n.y,CAMERADIST);
    vect->z += mulscale16(n.z,CAMERADIST);
415

416
417
    CAMERADIST = min(CAMERADIST+(((int32_t) totalclock-CAMERACLOCK)<<10),65536);
    CAMERACLOCK = (int32_t) totalclock;
418

419
    updatesectorz(vect->x,vect->y,vect->z,vsectnum);
420

421
422
    return 0;
}
Richard Gobeille's avatar
Richard Gobeille committed
423

424
425
426
427
#ifdef LEGACY_ROR
char ror_protectedsectors[MAXSECTORS];
static int32_t drawing_ror = 0;
static int32_t ror_sprite = -1;
428

429
static void G_OROR_DupeSprites(spritetype const *sp)
430
431
432
{
    // dupe the sprites touching the portal to the other sector
    int32_t k;
433
    spritetype const *refsp;
434

435
    if ((unsigned)sp->yvel >= (unsigned)g_mostConcurrentPlayers)
436
        return;
437

438
    refsp = &sprite[sp->yvel];
439

440
    for (SPRITES_OF_SECT(sp->sectnum, k))
Richard Gobeille's avatar
   
Richard Gobeille committed
441
    {
442
        if (spritesortcnt >= maxspritesonscreen)
443
            break;
Richard Gobeille's avatar
   
Richard Gobeille committed
444

445
        if (sprite[k].picnum != SECTOREFFECTOR && sprite[k].z >= sp->z)
446
        {
447
            tspriteptr_t tsp = renderAddTSpriteFromSprite(k);
448
            Duke_ApplySpritePropertiesToTSprite(tsp, (uspriteptr_t)&sprite[k]);
449

450
451
452
453
            tsp->x += (refsp->x - sp->x);
            tsp->y += (refsp->y - sp->y);
            tsp->z += -sp->z + actor[sp->yvel].ceilingz;
            tsp->sectnum = refsp->sectnum;
Richard Gobeille's avatar
   
Richard Gobeille committed
454

455
//            OSD_Printf("duped sprite of pic %d at %d %d %d\n",tsp->picnum,tsp->x,tsp->y,tsp->z);
Richard Gobeille's avatar
Richard Gobeille committed
456
        }
457
458
459
    }
}

460
461
462
static int16_t SE40backupStat[MAXSECTORS];
static int32_t SE40backupZ[MAXSECTORS];

463
static void G_SE40(int32_t smoothratio)
464
{
465
466
467
468
469
    if ((unsigned)ror_sprite < MAXSPRITES)
    {
        int32_t x, y, z;
        int16_t sect;
        int32_t level = 0;
470
        auto const sp = &sprite[ror_sprite];
471
        const int32_t sprite2 = sp->yvel;
Richard Gobeille's avatar
   
Richard Gobeille committed
472

473
474
        if ((unsigned)sprite2 >= MAXSPRITES)
            return;
Richard Gobeille's avatar
   
Richard Gobeille committed
475

476
477
        if (klabs(sector[sp->sectnum].floorz - sp->z) < klabs(sector[sprite[sprite2].sectnum].floorz - sprite[sprite2].z))
            level = 1;
Richard Gobeille's avatar
   
Richard Gobeille committed
478

479
480
481
        x = CAMERA(pos.x) - sp->x;
        y = CAMERA(pos.y) - sp->y;
        z = CAMERA(pos.z) - (level ? sector[sp->sectnum].floorz : sector[sp->sectnum].ceilingz);
482

483
484
        sect = sprite[sprite2].sectnum;
        updatesector(sprite[sprite2].x + x, sprite[sprite2].y + y, &sect);
485

486
        if (sect != -1)
487
        {
488
489
490
491
492
            int32_t renderz, picnum;
            // XXX: PK: too large stack allocation for my taste
            int32_t i;
            int32_t pix_diff, newz;
            //                initprintf("drawing ror\n");
493

494
495
496
497
498
499
500
            if (level)
            {
                // renderz = sector[sprite[sprite2].sectnum].ceilingz;
                renderz = sprite[sprite2].z - (sprite[sprite2].yrepeat * tilesiz[sprite[sprite2].picnum].y<<1);
                picnum = sector[sprite[sprite2].sectnum].ceilingpicnum;
                sector[sprite[sprite2].sectnum].ceilingpicnum = 562;
                tilesiz[562].x = tilesiz[562].y = 0;
501

502
503
                pix_diff = klabs(z) >> 8;
                newz = - ((pix_diff / 128) + 1) * (128<<8);
504

505
506
                for (i = 0; i < numsectors; i++)
                {
507
508
                    SE40backupStat[i] = sector[i].ceilingstat;
                    SE40backupZ[i] = sector[i].ceilingz;
509
                    if (!ror_protectedsectors[i] || sp->lotag == 41)
510
511
512
513
514
515
516
517
518
519
520
521
522
                    {
                        sector[i].ceilingstat = 1;
                        sector[i].ceilingz += newz;
                    }
                }
            }
            else
            {
                // renderz = sector[sprite[sprite2].sectnum].floorz;
                renderz = sprite[sprite2].z;
                picnum = sector[sprite[sprite2].sectnum].floorpicnum;
                sector[sprite[sprite2].sectnum].floorpicnum = 562;
                tilesiz[562].x = tilesiz[562].y = 0;
523

524
525
                pix_diff = klabs(z) >> 8;
                newz = ((pix_diff / 128) + 1) * (128<<8);
Richard Gobeille's avatar
   
Richard Gobeille committed
526

527
528
                for (i = 0; i < numsectors; i++)
                {
529
530
                    SE40backupStat[i] = sector[i].floorstat;
                    SE40backupZ[i] = sector[i].floorz;
531
                    if (!ror_protectedsectors[i] || sp->lotag == 41)
532
533
534
535
536
537
                    {
                        sector[i].floorstat = 1;
                        sector[i].floorz = +newz;
                    }
                }
            }
Richard Gobeille's avatar
   
Richard Gobeille committed
538

539
#ifdef POLYMER
540
            if (videoGetRenderMode() == REND_POLYMER)
541
                polymer_setanimatesprites(G_DoSpriteAnimations, CAMERA(pos.x), CAMERA(pos.y), CAMERA(pos.z), fix16_to_int(CAMERA(q16ang)), smoothratio);
542
#endif
Richard Gobeille's avatar
Richard Gobeille committed
543
            renderDrawRoomsQ16(sprite[sprite2].x + x, sprite[sprite2].y + y,
544
                      z + renderz, CAMERA(q16ang), CAMERA(q16horiz), sect);
545
            drawing_ror = 1 + level;
546

547
548
            if (drawing_ror == 2) // viewing from top
                G_OROR_DupeSprites(sp);
549

550
            G_DoSpriteAnimations(CAMERA(pos.x),CAMERA(pos.y),CAMERA(pos.z),fix16_to_int(CAMERA(q16ang)),smoothratio);
Richard Gobeille's avatar
Richard Gobeille committed
551
            renderDrawMasks();
552

553
554
555
556
557
            if (level)
            {
                sector[sprite[sprite2].sectnum].ceilingpicnum = picnum;
                for (i = 0; i < numsectors; i++)
                {
558
559
                    sector[i].ceilingstat = SE40backupStat[i];
                    sector[i].ceilingz = SE40backupZ[i];
560
561
562
563
564
                }
            }
            else
            {
                sector[sprite[sprite2].sectnum].floorpicnum = picnum;
565

566
567
                for (i = 0; i < numsectors; i++)
                {
568
569
                    sector[i].floorstat = SE40backupStat[i];
                    sector[i].floorz = SE40backupZ[i];
570
571
572
                }
            }
        }
573
    }
574
}
575
#endif
576

577
void G_HandleMirror(int32_t x, int32_t y, int32_t z, fix16_t a, fix16_t q16horiz, int32_t smoothratio)
578
{
579
580
    MICROPROFILE_SCOPEI("Game", EDUKE32_FUNCTION, MP_YELLOWGREEN);

581
    if ((gotpic[MIRROR>>3]&pow2char[MIRROR&7])
582
#ifdef POLYMER
583
        && (videoGetRenderMode() != REND_POLYMER)
584
585
#endif
        )
Richard Gobeille's avatar
   
Richard Gobeille committed
586
    {
587
588
589
590
        if (g_mirrorCount == 0)
        {
            // NOTE: We can have g_mirrorCount==0 but gotpic'd MIRROR,
            // for example in LNGA2.
591
            gotpic[MIRROR>>3] &= ~pow2char[MIRROR&7];
592
593
594
595

            //give scripts the chance to reset gotpics for effects that run in EVENT_DISPLAYROOMS
            //EVENT_RESETGOTPICS must be called after the last call to EVENT_DISPLAYROOMS in a frame, but before any engine-side renderDrawRoomsQ16
            VM_OnEvent(EVENT_RESETGOTPICS, -1, -1);
596
597
            return;
        }
Richard Gobeille's avatar
   
Richard Gobeille committed
598

599
        int32_t i = 0, dst = INT32_MAX;
600

601
        for (bssize_t k=g_mirrorCount-1; k>=0; k--)
602
        {
603
604
605
            if (!wallvisible(x, y, g_mirrorWall[k]))
                continue;

606
607
608
            const int32_t j =
                klabs(wall[g_mirrorWall[k]].x - x) +
                klabs(wall[g_mirrorWall[k]].y - y);
609

610
611
612
            if (j < dst)
                dst = j, i = k;
        }
613

614
615
616
        if (wall[g_mirrorWall[i]].overpicnum != MIRROR)
        {
            // Try to find a new mirror wall in case the original one was broken.
Richard Gobeille's avatar
   
Richard Gobeille committed
617

618
619
            int32_t startwall = sector[g_mirrorSector[i]].wallptr;
            int32_t endwall = startwall + sector[g_mirrorSector[i]].wallnum;
620

621
            for (bssize_t k=startwall; k<endwall; k++)
622
623
624
625
626
627
628
629
            {
                int32_t j = wall[k].nextwall;
                if (j >= 0 && (wall[j].cstat&32) && wall[j].overpicnum==MIRROR)  // cmp. premap.c
                {
                    g_mirrorWall[i] = j;
                    break;
                }
            }
630
        }
631

632
633
634
        if (wall[g_mirrorWall[i]].overpicnum == MIRROR)
        {
            int32_t tposx, tposy;
635
            fix16_t tang;
Philipp Kutin's avatar
Philipp Kutin committed
636

637
            //prepare to render any scripted EVENT_DISPLAYROOMS extras as mirrored
638
            renderPrepareMirror(x, y, z, a, q16horiz, g_mirrorWall[i], &tposx, &tposy, &tang);
Philipp Kutin's avatar
Philipp Kutin committed
639

640
641
            int32_t j = g_visibility;
            g_visibility = (j>>1) + (j>>2);
642

643
            //backup original camera position
644
645
            auto origCam = CAMERA(pos);
            fix16_t origCamq16ang   = CAMERA(q16ang);
646
            fix16_t origCamq16horiz = CAMERA(q16horiz);
647

648
            //set the camera inside the mirror facing out
649
650
            CAMERA(pos)      = { tposx, tposy, z };
            CAMERA(q16ang)   = tang;
651
            CAMERA(q16horiz) = q16horiz;
652

653
654
655
            display_mirror = 1;
            VM_OnEventWithReturn(EVENT_DISPLAYROOMS, g_player[0].ps->i, 0, 0);
            display_mirror = 0;
656

657
            //reset the camera position
658
659
            CAMERA(pos)      = origCam;
            CAMERA(q16ang)   = origCamq16ang;
660
661
            CAMERA(q16horiz) = origCamq16horiz;

662
663
664
665
            //give scripts the chance to reset gotpics for effects that run in EVENT_DISPLAYROOMS
            //EVENT_RESETGOTPICS must be called after the last call to EVENT_DISPLAYROOMS in a frame, but before any engine-side renderDrawRoomsQ16
            VM_OnEvent(EVENT_RESETGOTPICS, -1, -1);

666
            //prepare to render the mirror
667
            renderPrepareMirror(x, y, z, a, q16horiz, g_mirrorWall[i], &tposx, &tposy, &tang);
668

669
            if (videoGetRenderMode() != REND_POLYMER)
670
671
            {
                int32_t didmirror;
672

673
                yax_preparedrawrooms();
Richard Gobeille's avatar
Richard Gobeille committed
674
                didmirror = renderDrawRoomsQ16(tposx,tposy,z,tang,q16horiz,g_mirrorSector[i]+MAXSECTORS);
675
                //POGO: if didmirror == 0, we may simply wish to abort instead of rendering with yax_drawrooms (which may require cleaning yax state)
676
677
                if (videoGetRenderMode() != REND_CLASSIC || didmirror)
                    yax_drawrooms(G_DoSpriteAnimations, g_mirrorSector[i], didmirror, smoothratio);
678
            }
679
#ifdef USE_OPENGL
680
            else
Richard Gobeille's avatar
Richard Gobeille committed
681
                renderDrawRoomsQ16(tposx,tposy,z,tang,q16horiz,g_mirrorSector[i]+MAXSECTORS);
682
            // XXX: Sprites don't get drawn with TROR/Polymost
683
#endif
684
            display_mirror = 1;
685
            G_DoSpriteAnimations(tposx,tposy,z,fix16_to_int(tang),smoothratio);
686
            display_mirror = 0;
687

Richard Gobeille's avatar
Richard Gobeille committed
688
689
            renderDrawMasks();
            renderCompleteMirror();   //Reverse screen x-wise in this function
690
691
            g_visibility = j;
        }
692
693
    }
}
694

695
696
static void G_ClearGotMirror()
{
697
#ifdef SPLITSCREEN_MOD_HACKS
698
    if (!g_fakeMultiMode)
699
#endif
700
701
702
703
704
705
706
    {
        // HACK for splitscreen mod: this is so that mirrors will be drawn
        // from showview commands. Ugly, because we'll attempt do draw mirrors
        // each frame then. But it's better than not drawing them, I guess.
        // XXX: fix the sequence of setting/clearing this bit. Right now,
        // we always draw one frame without drawing the mirror, after which
        // the bit gets set and drawn subsequently.
707
        gotpic[MIRROR>>3] &= ~pow2char[MIRROR&7];
708
    }
709
}
710

711
712
713
#ifdef USE_OPENGL
static void G_ReadGLFrame(void)
{
714
715
    MICROPROFILE_SCOPEI("Game", EDUKE32_FUNCTION, MP_YELLOWGREEN);

716
717
    // Save OpenGL screenshot with Duke3D palette
    // NOTE: maybe need to move this to the engine...
718
719
720
    
    static char lock;
    static palette_t *frame;
721

Richard Gobeille's avatar
Richard Gobeille committed
722
    lock = CACHE1D_PERMANENT;
723

724
    if (frame == nullptr)
Richard Gobeille's avatar
Richard Gobeille committed
725
        g_cache.allocateBlock((intptr_t *)&frame, xdim * ydim * sizeof(palette_t), &lock);
726

727
728
729
730
731
732
    char *const pic = (char *) waloff[TILE_SAVESHOT];

    int const xf = divscale16(ydim*4/3, 320);
    int const yf = divscale16(ydim, 200);  // (ydim<<16)/200

    tilesiz[TILE_SAVESHOT] = { 200, 320 };
733

734
    videoBeginDrawing();
735
    glReadPixels(0, 0, xdim, ydim, GL_RGBA, GL_UNSIGNED_BYTE, frame);
736
    videoEndDrawing();
737

738
    for (int y = 0; y < 200; y++)
739
    {
740
        const int32_t base = mulscale16(200 - y - 1, yf)*xdim;
741

742
        for (int x = 0; x < 320; x++)
743
        {
744
            const palette_t *pix = &frame[base + mulscale16(x, xf) + (xdim-(ydim*4/3))/2];
Richard Gobeille's avatar
Richard Gobeille committed
745
            pic[320 * y + x] = paletteGetClosestColor(pix->r, pix->g, pix->b);
746
747
748
        }
    }

749
    lock = CACHE1D_FREE;
750
}
751
#endif
752

753
void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
754
{
755
756
    MICROPROFILE_SCOPEI("Game", EDUKE32_FUNCTION, MP_YELLOWGREEN);

757
758
    auto const &thisPlayer = g_player[playerNum];
    auto const  pPlayer    = thisPlayer.ps;
759

760
    int const viewingRange = viewingrange;
761

762
    if (g_networkMode == NET_DEDICATED_SERVER) return;
763

764
    totalclocklock = totalclock;
765
    rotatespritesmoothratio = smoothRatio;
766

767
    if (pub > 0 || videoGetRenderMode() >= REND_POLYMOST) // JBF 20040101: redraw background always
768
769
770
771
772
773
    {
#ifndef EDUKE32_TOUCH_DEVICES
        if (ud.screen_size >= 8)
#endif
            G_DrawBackground();
        pub = 0;
774
775
    }

776
    VM_OnEvent(EVENT_DISPLAYSTART, pPlayer->i, playerNum);
777

778
    if ((ud.overhead_on == 2 && !automapping) || ud.show_help || (pPlayer->cursectnum == -1 && videoGetRenderMode() != REND_CLASSIC))
779
        return;
780

781
    if (r_usenewaspect)
782
    {
783
        newaspect_enable = 1;
784
        videoSetCorrectedAspect();
785
    }
786

787
    if (pPlayer->on_crane > -1)
788
        smoothRatio = 65536;
789

790