//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors
This file is part of EDuke32.
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
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
#include "duke3d.h"
#include "baselayer.h"
#include "renderlayer.h"
#include "scriplib.h"
#include "file_lib.h"
#include "mathutil.h"
#include "gamedefs.h"
#include "keyboard.h"
#include "mouse.h" // JBF 20030809
#include "joystick.h"
#include "function.h"
#include "control.h"
#include "fx_man.h"
#include "sounds.h"
#include "config.h"
#include "osd.h"
#include "osdfuncs.h"
#include "osdcmds.h"
#include "scriptfile.h"
#include "grpscan.h"
#include "gamedef.h"
#include "kplib.h"
#include "crc32.h"
#include "hightile.h"
#include "control.h"
#include "lz4.h"
#include "net.h"
#include "premap.h"
#include "gameexec.h"
#include "menus.h"
#include "savegame.h"
#include "anim.h"
#include "demo.h"
#include "common.h"
#include "common_game.h"
#include "input.h"
#include "compat.h"
#ifdef __ANDROID__
#include "android.h"
#endif
#ifdef LUNATIC
# include "lunatic_game.h"
#endif
// Uncomment to prevent anything except mirrors from drawing. It is sensible to
// also uncomment ENGINE_CLEAR_SCREEN in build/src/engine_priv.h.
//#define DEBUG_MIRRORS_ONLY
#if KRANDDEBUG
# define GAME_INLINE
# define GAME_STATIC
#else
# define GAME_INLINE inline
# define GAME_STATIC static
#endif
#ifdef _WIN32
# include "winlayer.h"
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <shellapi.h>
# define UPDATEINTERVAL 604800 // 1w
# include "winbits.h"
#else
# ifndef GEKKO
# include <sys/ioctl.h>
# endif
#endif /* _WIN32 */
const char* AppProperName
= "EDuke32";
const char* AppTechnicalName
= "eduke32";
int32_t g_quitDeadline
= 0;
#ifdef LUNATIC
camera_t g_camera
;
#else
int32_t g_cameraDistance
= 0, g_cameraClock
= 0;
#endif
static int32_t g_quickExit
;
static int32_t g_commandSetup
= 0;
int32_t g_noSetup
= 0;
static int32_t g_noAutoLoad
= 0;
static int32_t g_noSound
= 0;
static int32_t g_noMusic
= 0;
static const char *CommandMap
= NULL
;
static const char *CommandName
= NULL
;
int32_t g_forceWeaponChoice
= 0;
int32_t g_fakeMultiMode
= 0;
double g_moveActorsTime
= 0; // in ms, smoothed
char boardfilename
[BMAX_PATH
] = {0}, currentboardfilename
[BMAX_PATH
] = {0};
int32_t voting
= -1;
int32_t vote_map
= -1, vote_episode
= -1;
static int32_t g_Debug
= 0;
static int32_t g_noLogoAnim
= 0;
static int32_t g_noLogo
= 0;
const char *defaultrtsfilename
[GAMECOUNT
] = { "DUKE.RTS", "NAM.RTS", "NAPALM.RTS", "WW2GI.RTS" };
// g_rtsNamePtr can point to an argv[] element
const char *g_rtsNamePtr
= NULL
;
int32_t g_Shareware
= 0;
#define MAXUSERQUOTES 6
int32_t quotebot
, quotebotgoal
;
static int32_t user_quote_time
[MAXUSERQUOTES
];
static char user_quote
[MAXUSERQUOTES
][178];
// 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
#ifndef GEKKO
static int32_t MAXCACHE1DSIZE
= (24*1048576);
#else
static int32_t MAXCACHE1DSIZE
= (8*1048576);
#endif
int32_t tempwallptr
;
static int32_t nonsharedtimer
;
int32_t ticrandomseed
;
static void G_DrawCameraText
(int16_t i
);
GAME_STATIC GAME_INLINE
int32_t G_MoveLoop
(void);
static void G_DoOrderScreen
(void);
#define FTAOPAQUETIME 30
#define ftapulseshade \
((hud_glowingquotes && (getrendermode() == REND_CLASSIC || ps->fta >= FTAOPAQUETIME)) ? \
(sintable[((uint32_t)ps->fta << 7) & 2047] >> 11) : \
(sintable[((uint32_t)FTAOPAQUETIME << 7) & 2047] >> 11))
#define quotepulseshade (sintable[((uint32_t)totalclock<<5)&2047]>>11)
int32_t althud_numbertile
= 2930;
int32_t althud_numberpal
= 0;
#ifdef EDUKE32_TOUCH_DEVICES
int32_t althud_shadows
= 0;
#else
int32_t althud_shadows
= 1;
#endif
int32_t althud_flashing
= 1;
int32_t hud_glowingquotes
= 1;
int32_t hud_showmapname
= 1;
int32_t g_levelTextTime
= 0;
int32_t r_maxfps
= 0;
uint32_t g_frameDelay
= 0;
#if defined(RENDERTYPEWIN) && defined(USE_OPENGL)
extern char forcegl
;
#endif
void M32RunScript
(const char *s
) { UNREFERENCED_PARAMETER
(s
); }; // needed for linking since it's referenced from build/src/osd.c
const char *G_DefaultRtsFile
(void)
{
if (DUKE
)
return defaultrtsfilename
[GAME_DUKE
];
else if (WW2GI
)
return defaultrtsfilename
[GAME_WW2GI
];
else if (NAPALM
)
{
if (!testkopen
(defaultrtsfilename
[GAME_NAPALM
],0) && testkopen
(defaultrtsfilename
[GAME_NAM
],0))
return defaultrtsfilename
[GAME_NAM
]; // NAM/NAPALM Sharing
else
return defaultrtsfilename
[GAME_NAPALM
];
}
else if (NAM
)
{
if (!testkopen
(defaultrtsfilename
[GAME_NAM
],0) && testkopen
(defaultrtsfilename
[GAME_NAPALM
],0))
return defaultrtsfilename
[GAME_NAPALM
]; // NAM/NAPALM Sharing
else
return defaultrtsfilename
[GAME_NAM
];
}
return defaultrtsfilename
[0];
}
enum gametokens
{
T_INCLUDE
= 0,
T_INTERFACE
= 0,
T_LOADGRP
= 1,
T_MODE
= 1,
T_CACHESIZE
= 2,
T_ALLOW
= 2,
T_NOAUTOLOAD
,
T_INCLUDEDEFAULT
,
T_MUSIC
,
T_SOUND
,
T_FILE
,
T_CUTSCENE
,
T_ANIMSOUNDS
,
T_NOFLOORPALRANGE
,
T_ID
,
T_DELAY
};
static int32_t sbarsc
(int32_t sc
)
{
return scale
(sc
,ud.
statusbarscale,100);
}
static int32_t sbarx
(int32_t x
)
{
if (ud.
screen_size == 4) return sbarsc
(x
<<16);
return (((320<<16) - sbarsc
(320<<16)) >> 1) + sbarsc
(x
<<16);
}
static int32_t sbarxr
(int32_t x
)
{
if (ud.
screen_size == 4) return (320<<16) - sbarsc
(x
<<16);
return (((320<<16) - sbarsc
(320<<16)) >> 1) + sbarsc
(x
<<16);
}
static int32_t sbary
(int32_t y
)
{
if (ud.
althud == 2 && ud.
screen_size == 4) return sbarsc
(y
<< 16);
else return (200<<16) - sbarsc
(200<<16) + sbarsc
(y
<<16);
}
static int32_t sbarx16
(int32_t x
)
{
if (ud.
screen_size == 4) return sbarsc
(x
);
return (((320<<16) - sbarsc
(320<<16)) >> 1) + sbarsc
(x
);
}
#if 0 // enable if ever needed
static int32_t sbarxr16
(int32_t x
)
{
if (ud.
screen_size == 4) return (320<<16) - sbarsc
(x
);
return (((320<<16) - sbarsc
(320<<16)) >> 1) + sbarsc
(x
);
}
#endif
static int32_t sbary16
(int32_t y
)
{
return (200<<16) - sbarsc
(200<<16) + sbarsc
(y
);
}
int32_t textsc
(int32_t sc
)
{
// prevent ridiculousness to a degree
if (xdim
<= 320) return sc
;
else if (xdim
<= 640) return scale
(sc
,min
(200,ud.
textscale),100);
else if (xdim
<= 800) return scale
(sc
,min
(300,ud.
textscale),100);
else if (xdim
<= 1024) return scale
(sc
,min
(350,ud.
textscale),100);
return scale
(sc
,ud.
textscale,100);
}
static int32_t gtextsc
(int32_t sc
)
{
return scale
(sc
,ud.
textscale,400);
}
static void G_PatchStatusBar
(int32_t x1
, int32_t y1
, int32_t x2
, int32_t y2
)
{
int32_t scl
= sbarsc
(65536);
int32_t tx
= sbarx16
((160<<16) - (tilesiz
[BOTTOMSTATUSBAR
].
x<<15)); // centered
int32_t ty
= sbary
(200-tilesiz
[BOTTOMSTATUSBAR
].
y);
int32_t clx1
= sbarsc
(scale
(x1
,xdim
,320)), cly1
= sbarsc
(scale
(y1
,ydim
,200));
int32_t clx2
= sbarsc
(scale
(x2
,xdim
,320)), cly2
= sbarsc
(scale
(y2
,ydim
,200));
int32_t clofx
= (xdim
- sbarsc
(xdim
)) >> 1, clofy
= (ydim
- sbarsc
(ydim
));
rotatesprite
(tx
,ty
,scl
,0,BOTTOMSTATUSBAR
,4,0,10+16+64,clx1
+clofx
,cly1
+clofy
,clx2
+clofx
-1,cly2
+clofy
-1);
}
void P_SetGamePalette
(DukePlayer_t
*player
, uint8_t palid
, int32_t set
)
{
if (palid
>= BASEPALCOUNT
)
palid
= BASEPAL
;
player
->palette
= palid
;
if (player
!= g_player
[screenpeek
].
ps)
return;
setbrightness
(ud.
brightness>>2, palid
, set
);
}
// get the string length until the next '\n'
int32_t G_GetStringLineLength
(const char *text
, const char *end
, const int32_t iter
)
{
int32_t length
= 0;
while (*text
!= '\n' && text
!= end
)
{
++length
;
text
+= iter
;
}
return length
;
}
int32_t G_GetStringNumLines
(const char *text
, const char *end
, const int32_t iter
)
{
int32_t count
= 1;
while (text
!= end
)
{
if (*text
== '\n')
++count
;
text
+= iter
;
}
return count
;
}
// Note: Neither of these care about TEXT_LINEWRAP. This is intended.
// This function requires you to Bfree() the returned char*.
char* G_GetSubString
(const char *text
, const char *end
, const int32_t iter
, const int32_t length
)
{
char *line
= (char*)Xmalloc
((length
+1) * sizeof(char));
int32_t counter
= 0;
while (counter
< length
&& text
!= end
)
{
line
[counter
] = *text
;
text
+= iter
;
++counter
;
}
line
[counter
] = '\0';
return line
;
}
// assign the character's tilenum
int32_t G_GetStringTile
(int32_t font
, char *t
, int32_t f
)
{
if (f
& TEXT_DIGITALNUMBER
)
return *t
- '0' + font
; // copied from digitalnumber
else if (f
& (TEXT_BIGALPHANUM
|TEXT_GRAYFONT
))
{
int32_t offset
= (f
& TEXT_GRAYFONT
) ? 26 : 0;
if (*t
>= '0' && *t
<= '9')
return *t
- '0' + font
+ ((f
& TEXT_GRAYFONT
) ? 26 : -10);
else if (*t
>= 'a' && *t
<= 'z')
return *t
- 'a' + font
+ ((f
& TEXT_GRAYFONT
) ? -26 : 26);
else if (*t
>= 'A' && *t
<= 'Z')
return *t
- 'A' + font
;
else switch (*t
)
{
case '_':
case '-':
return font
- (11 + offset
);
break;
case '.':
return font
+ (BIGPERIOD
- (BIGALPHANUM
+ offset
));
break;
case ',':
return font
+ (BIGCOMMA
- (BIGALPHANUM
+ offset
));
break;
case '!':
return font
+ (BIGX_
- (BIGALPHANUM
+ offset
));
break;
case '?':
return font
+ (BIGQ
- (BIGALPHANUM
+ offset
));
break;
case ';':
return font
+ (BIGSEMI
- (BIGALPHANUM
+ offset
));
break;
case ':':
return font
+ (BIGCOLIN
- (BIGALPHANUM
+ offset
));
break;
case '\\':
case '/':
return font
+ (68 - offset
); // 3008-2940
break;
case '%':
return font
+ (69 - offset
); // 3009-2940
break;
case '`':
case '\"': // could be better hacked in
case '\'':
return font
+ (BIGAPPOS
- (BIGALPHANUM
+ offset
));
break;
default: // unknown character
*t
= ' '; // whitespace-ize
case '\n':
return font
;
break;
}
}
else
return *t
- '!' + font
; // uses ASCII order
}
#define NUMHACKACTIVE ((f & TEXT_GAMETEXTNUMHACK) && t >= '0' && t <= '9')
// qstrdim
vec2_t G_ScreenTextSize
(const int32_t font
,
int32_t x
, int32_t y
, const int32_t z
, const int32_t blockangle
,
const char *str
, const int32_t o
,
int32_t xspace
, int32_t yline
, int32_t xbetween
, int32_t ybetween
,
const int32_t f
,
int32_t x1
, int32_t y1
, int32_t x2
, int32_t y2
)
{
vec2_t size
= { 0, 0, }; // eventually the return value
vec2_t pos
= { 0, 0, }; // holds the coordinate position as we draw each character tile of the string
vec2_t extent
= { 0, 0, }; // holds the x-width of each character and the greatest y-height of each line
vec2_t offset
= { 0, 0, }; // temporary; holds the last movement made in both directions
int32_t tile
;
char t
;
// set the start and end points depending on direction
int32_t iter
= (f
& TEXT_BACKWARDS
) ? -1 : 1; // iteration direction
const char *end
;
const char *text
;
if (str
== NULL
)
return size
;
end
= (f
& TEXT_BACKWARDS
) ? str
-1 : Bstrchr
(str
,'\0');
text
= (f
& TEXT_BACKWARDS
) ? Bstrchr
(str
,'\0')-1 : str
;
// optimization: justification in both directions
if ((f
& TEXT_XJUSTIFY
) && (f
& TEXT_YJUSTIFY
))
{
size.
x = xbetween
;
size.
y = ybetween
;
return size
;
}
// for best results, we promote 320x200 coordinates to full precision before any math
if (!(o
& ROTATESPRITE_FULL16
))
{
x
<<= 16;
y
<<= 16;
xspace
<<= 16;
yline
<<= 16;
xbetween
<<= 16;
ybetween
<<= 16;
}
// coordinate values should be shifted left by 16
// handle zooming where applicable
xspace
= scale
(xspace
, z
, 65536);
yline
= scale
(yline
, z
, 65536);
xbetween
= scale
(xbetween
, z
, 65536);
ybetween
= scale
(ybetween
, z
, 65536);
// size/width/height/spacing/offset values should be multiplied or scaled by $z, zoom (since 100% is 65536, the same as 1<<16)
// loop through the string
while ((t
= *text
) && text
!= end
)
{
// handle escape sequences
if (t
== '^' && Bisdigit
(*(text
+iter
)) && !(f
& TEXT_LITERALESCAPE
))
{
text
+= iter
+ iter
;
if (Bisdigit
(*text
))
text
+= iter
;
continue;
}
// handle case bits
if (f
& TEXT_UPPERCASE
)
{
if (f
& TEXT_INVERTCASE
) // optimization...?
{ // v^ important that these two ifs remain separate due to the else below
if (Bisupper
(t
))
t
= Btolower
(t
);
}
else if (Bislower
(t
))
t
= Btoupper
(t
);
}
else if (f
& TEXT_INVERTCASE
)
{
if (Bisupper
(t
))
t
= Btolower
(t
);
else if (Bislower
(t
))
t
= Btoupper
(t
);
}
// translate the character to a tilenum
tile
= G_GetStringTile
(font
, &t
, f
);
// reset this here because we haven't printed anything yet this loop
extent.
x = 0;
// reset this here because the act of printing something on this line means that we include the margin above in the total size
offset.
y = 0;
// handle each character itself in the context of screen drawing
switch (t
)
{
case '\t':
case ' ':
// width
extent.
x = xspace
;
if (f
& (TEXT_INTERNALSPACE
|TEXT_TILESPACE
))
{
char space
= '.'; // this is subject to change as an implementation detail
if (f
& TEXT_TILESPACE
)
space
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &space
, f
);
extent.
x += (tilesiz
[tile
].
x * z
);
}
// prepare the height // near-CODEDUP the other two near-CODEDUPs for this section
{
int32_t tempyextent
= yline
;
if (f
& (TEXT_INTERNALLINE
|TEXT_TILELINE
))
{
char line
= 'A'; // this is subject to change as an implementation detail
if (f
& TEXT_TILELINE
)
line
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &line
, f
);
tempyextent
+= tilesiz
[tile
].
y * z
;
}
SetIfGreater
(&extent.
y, tempyextent
);
}
if (t
== '\t')
extent.
x <<= 2; // *= 4
break;
case '\n': // near-CODEDUP "if (wrap)"
extent.
x = 0;
// save the position
if (!(f
& TEXT_XOFFSETZERO
)) // we want the entire offset to count as the character width
pos.
x -= offset.
x;
SetIfGreater
(&size.
x, pos.
x);
// reset the position
pos.
x = 0;
// prepare the height
{
int32_t tempyextent
= yline
;
if (f
& (TEXT_INTERNALLINE
|TEXT_TILELINE
))
{
char line
= 'A'; // this is subject to change as an implementation detail
if (f
& TEXT_TILELINE
)
line
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &line
, f
);
tempyextent
+= tilesiz
[tile
].
y * z
;
}
SetIfGreater
(&extent.
y, tempyextent
);
}
// move down the line height
if (!(f
& TEXT_YOFFSETZERO
))
pos.
y += extent.
y;
// reset the current height
extent.
y = 0;
// line spacing
offset.
y = (f
& TEXT_YJUSTIFY
) ? 0 : ybetween
; // ternary to prevent overflow
pos.
y += offset.
y;
break;
default:
// width
extent.
x = tilesiz
[tile
].
x * z
;
// obnoxious hardcoded functionality from gametext
if (NUMHACKACTIVE
)
{
char numeral
= '0'; // this is subject to change as an implementation detail
extent.
x = (tilesiz
[G_GetStringTile
(font
, &numeral
, f
)].
x-1) * z
;
}
// height
SetIfGreater
(&extent.
y, (tilesiz
[tile
].
y * z
));
break;
}
// incrementing the coordinate counters
offset.
x = 0;
// advance the x coordinate
if (!(f
& TEXT_XOFFSETZERO
) || NUMHACKACTIVE
)
offset.
x += extent.
x;
// account for text spacing
if (!NUMHACKACTIVE
// this "if" line ONLY == replicating hardcoded stuff
&& t
!= '\n'
&& !(f
& TEXT_XJUSTIFY
)) // to prevent overflow
offset.
x += xbetween
;
// line wrapping
if ((f
& TEXT_LINEWRAP
) && !(f
& TEXT_XRIGHT
) && !(f
& TEXT_XCENTER
) && blockangle
% 512 == 0)
{
int32_t wrap
= 0;
const int32_t ang
= blockangle
% 2048;
// this is the only place in qstrdim where angle actually affects direction, but only in the wrapping measurement
switch (ang
)
{
case 0:
wrap
= (x
+ (pos.
x + offset.
x) > ((o
& 2) ? (320<<16) : ((x2
- USERQUOTE_RIGHTOFFSET
)<<16)));
break;
case 512:
wrap
= (y
+ (pos.
x + offset.
x) > ((o
& 2) ? (200<<16) : ((y2
- USERQUOTE_RIGHTOFFSET
)<<16)));
break;
case 1024:
wrap
= (x
- (pos.
x + offset.
x) < ((o
& 2) ? 0 : ((x1
+ USERQUOTE_RIGHTOFFSET
)<<16)));
break;
case 1536:
wrap
= (y
- (pos.
x + offset.
x) < ((o
& 2) ? 0 : ((y1
+ USERQUOTE_RIGHTOFFSET
)<<16)));
break;
}
if (wrap
) // near-CODEDUP "case '\n':"
{
// save the position
SetIfGreater
(&size.
x, pos.
x);
// reset the position
pos.
x = 0;
// prepare the height
{
int32_t tempyextent
= yline
;
if (f
& (TEXT_INTERNALLINE
|TEXT_TILELINE
))
{
char line
= 'A'; // this is subject to change as an implementation detail
if (f
& TEXT_TILELINE
)
line
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &line
, f
);
tempyextent
+= tilesiz
[tile
].
y * z
;
}
SetIfGreater
(&extent.
y, tempyextent
);
}
// move down the line height
if (!(f
& TEXT_YOFFSETZERO
))
pos.
y += extent.
y;
// reset the current height
extent.
y = 0;
// line spacing
offset.
y = (f
& TEXT_YJUSTIFY
) ? 0 : ybetween
; // ternary to prevent overflow
pos.
y += offset.
y;
}
else
pos.
x += offset.
x;
}
else
pos.
x += offset.
x;
// save some trouble with calculation in case the line breaks
if (!(f
& TEXT_XOFFSETZERO
) || NUMHACKACTIVE
)
offset.
x -= extent.
x;
// iterate to the next character in the string
text
+= iter
;
}
// calculate final size
if (!(f
& TEXT_XOFFSETZERO
))
pos.
x -= offset.
x;
if (!(f
& TEXT_YOFFSETZERO
))
{
pos.
y -= offset.
y;
pos.
y += extent.
y;
}
else
pos.
y += ybetween
;
SetIfGreater
(&size.
x, pos.
x);
SetIfGreater
(&size.
y, pos.
y);
// justification where only one of the two directions is set, so we have to iterate
if (f
& TEXT_XJUSTIFY
)
size.
x = xbetween
;
if (f
& TEXT_YJUSTIFY
)
size.
y = ybetween
;
// return values in the same manner we receive them
if (!(o
& ROTATESPRITE_FULL16
))
{
size.
x >>= 16;
size.
y >>= 16;
}
return size
;
}
void G_AddCoordsFromRotation
(vec2_t
*coords
, const vec2_t
*unitDirection
, const int32_t magnitude
)
{
coords
->x
+= scale
(magnitude
, unitDirection
->x
, 16384);
coords
->y
+= scale
(magnitude
, unitDirection
->y
, 16384);
}
// screentext
vec2_t G_ScreenText
(const int32_t font
,
int32_t x
, int32_t y
, const int32_t z
, const int32_t blockangle
, const int32_t charangle
,
const char *str
, const int32_t shade
, int32_t pal
, int32_t o
, int32_t alpha
,
int32_t xspace
, int32_t yline
, int32_t xbetween
, int32_t ybetween
, const int32_t f
,
const int32_t x1
, const int32_t y1
, const int32_t x2
, const int32_t y2
)
{
vec2_t size
= { 0, 0, }; // eventually the return value
vec2_t origin
= { 0, 0, }; // where to start, depending on the alignment
vec2_t pos
= { 0, 0, }; // holds the coordinate position as we draw each character tile of the string
vec2_t extent
= { 0, 0, }; // holds the x-width of each character and the greatest y-height of each line
const vec2_t Xdirection
= { sintable
[(blockangle
+512)&2047], sintable
[blockangle
&2047], };
const vec2_t Ydirection
= { sintable
[(blockangle
+1024)&2047], sintable
[(blockangle
+512)&2047], };
int32_t blendidx
=0, tile
;
char t
;
// set the start and end points depending on direction
int32_t iter
= (f
& TEXT_BACKWARDS
) ? -1 : 1; // iteration direction
const char *end
;
const char *text
;
if (str
== NULL
)
return size
;
NEG_ALPHA_TO_BLEND
(alpha
, blendidx
, o
);
end
= (f
& TEXT_BACKWARDS
) ? str
-1 : Bstrchr
(str
,'\0');
text
= (f
& TEXT_BACKWARDS
) ? Bstrchr
(str
,'\0')-1 : str
;
// for best results, we promote 320x200 coordinates to full precision before any math
if (!(o
& ROTATESPRITE_FULL16
))
{
x
<<= 16;
y
<<= 16;
xspace
<<= 16;
yline
<<= 16;
xbetween
<<= 16;
ybetween
<<= 16;
}
// coordinate values should be shifted left by 16
// eliminate conflicts, necessary here to get the correct size value
// especially given justification's special handling in G_ScreenTextSize()
if ((f
& TEXT_XRIGHT
) || (f
& TEXT_XCENTER
) || (f
& TEXT_XJUSTIFY
) || (f
& TEXT_YJUSTIFY
) || blockangle
% 512 != 0)
o
&= ~TEXT_LINEWRAP
;
// size is the return value, and we need it for alignment
size
= G_ScreenTextSize
(font
, x
, y
, z
, blockangle
, str
, o
| ROTATESPRITE_FULL16
, xspace
, yline
, (f
& TEXT_XJUSTIFY
) ? 0 : xbetween
, (f
& TEXT_YJUSTIFY
) ? 0 : ybetween
, f
& ~
(TEXT_XJUSTIFY
|TEXT_YJUSTIFY
), x1
, y1
, x2
, y2
);
// handle zooming where applicable
xspace
= scale
(xspace
, z
, 65536);
yline
= scale
(yline
, z
, 65536);
xbetween
= scale
(xbetween
, z
, 65536);
ybetween
= scale
(ybetween
, z
, 65536);
// size/width/height/spacing/offset values should be multiplied or scaled by $z, zoom (since 100% is 65536, the same as 1<<16)
// alignment
// near-CODEDUP "case '\n':"
{
int32_t lines
= G_GetStringNumLines
(text
, end
, iter
);
if ((f
& TEXT_XJUSTIFY
) || (f
& TEXT_XRIGHT
) || (f
& TEXT_XCENTER
))
{
const int32_t length
= G_GetStringLineLength
(text
, end
, iter
);
int32_t linewidth
= size.
x;
if (lines
!= 1)
{
char *line
= G_GetSubString
(text
, end
, iter
, length
);
linewidth
= G_ScreenTextSize
(font
, x
, y
, z
, blockangle
, line
, o
| ROTATESPRITE_FULL16
, xspace
, yline
, (f
& TEXT_XJUSTIFY
) ? 0 : xbetween
, (f
& TEXT_YJUSTIFY
) ? 0 : ybetween
, f
& ~
(TEXT_XJUSTIFY
|TEXT_YJUSTIFY
|TEXT_BACKWARDS
), x1
, y1
, x2
, y2
).
x;
Bfree
(line
);
}
if (f
& TEXT_XJUSTIFY
)
{
size.
x = xbetween
;
xbetween
= (length
== 1) ? 0 : tabledivide32_noinline
((xbetween
- linewidth
), (length
- 1));
linewidth
= size.
x;
}
if (f
& TEXT_XRIGHT
)
origin.
x = -linewidth
;
else if (f
& TEXT_XCENTER
)
origin.
x = -(linewidth
/ 2);
}
if (f
& TEXT_YJUSTIFY
)
{
const int32_t tempswap
= ybetween
;
ybetween
= (lines
== 1) ? 0 : tabledivide32_noinline
(ybetween
- size.
y, lines
- 1);
size.
y = tempswap
;
}
if (f
& TEXT_YBOTTOM
)
origin.
y = -size.
y;
else if (f
& TEXT_YCENTER
)
origin.
y = -(size.
y / 2);
}
// loop through the string
while ((t
= *text
) && text
!= end
)
{
int32_t orientation
= o
;
int32_t angle
= blockangle
+ charangle
;
// handle escape sequences
if (t
== '^' && Bisdigit
(*(text
+iter
)) && !(f
& TEXT_LITERALESCAPE
))
{
char smallbuf
[4];
text
+= iter
;
smallbuf
[0] = *text
;
text
+= iter
;
if (Bisdigit
(*text
))
{
smallbuf
[1] = *text
;
smallbuf
[2] = '\0';
text
+= iter
;
}
else
smallbuf
[1] = '\0';
if (!(f
& TEXT_IGNOREESCAPE
))
pal
= Batoi
(smallbuf
);
continue;
}
// handle case bits
if (f
& TEXT_UPPERCASE
)
{
if (f
& TEXT_INVERTCASE
) // optimization...?
{ // v^ important that these two ifs remain separate due to the else below
if (Bisupper
(t
))
t
= Btolower
(t
);
}
else if (Bislower
(t
))
t
= Btoupper
(t
);
}
else if (f
& TEXT_INVERTCASE
)
{
if (Bisupper
(t
))
t
= Btolower
(t
);
else if (Bislower
(t
))
t
= Btoupper
(t
);
}
// translate the character to a tilenum
tile
= G_GetStringTile
(font
, &t
, f
);
switch (t
)
{
case '\t':
case ' ':
case '\n':
case '\x7F':
break;
default:
{
vec2_t location
= { x
, y
, };
G_AddCoordsFromRotation
(&location
, &Xdirection
, origin.
x);
G_AddCoordsFromRotation
(&location
, &Ydirection
, origin.
y);
G_AddCoordsFromRotation
(&location
, &Xdirection
, pos.
x);
G_AddCoordsFromRotation
(&location
, &Ydirection
, pos.
y);
rotatesprite_
(location.
x, location.
y, z
, angle
, tile
, shade
, pal
, orientation
, alpha
, blendidx
, x1
, y1
, x2
, y2
);
break;
}
}
// reset this here because we haven't printed anything yet this loop
extent.
x = 0;
// handle each character itself in the context of screen drawing
switch (t
)
{
case '\t':
case ' ':
// width
extent.
x = xspace
;
if (f
& (TEXT_INTERNALSPACE
|TEXT_TILESPACE
))
{
char space
= '.'; // this is subject to change as an implementation detail
if (f
& TEXT_TILESPACE
)
space
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &space
, f
);
extent.
x += (tilesiz
[tile
].
x * z
);
}
// prepare the height // near-CODEDUP the other two near-CODEDUPs for this section
{
int32_t tempyextent
= yline
;
if (f
& (TEXT_INTERNALLINE
|TEXT_TILELINE
))
{
char line
= 'A'; // this is subject to change as an implementation detail
if (f
& TEXT_TILELINE
)
line
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &line
, f
);
tempyextent
+= tilesiz
[tile
].
y * z
;
}
SetIfGreater
(&extent.
y, tempyextent
);
}
if (t
== '\t')
extent.
x <<= 2; // *= 4
break;
case '\n': // near-CODEDUP "if (wrap)"
extent.
x = 0;
// reset the position
pos.
x = 0;
// prepare the height
{
int32_t tempyextent
= yline
;
if (f
& (TEXT_INTERNALLINE
|TEXT_TILELINE
))
{
char line
= 'A'; // this is subject to change as an implementation detail
if (f
& TEXT_TILELINE
)
line
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &line
, f
);
tempyextent
+= tilesiz
[tile
].
y * z
;
}
SetIfGreater
(&extent.
y, tempyextent
);
}
// move down the line height
if (!(f
& TEXT_YOFFSETZERO
))
pos.
y += extent.
y;
// reset the current height
extent.
y = 0;
// line spacing
pos.
y += ybetween
;
// near-CODEDUP "alignments"
if ((f
& TEXT_XJUSTIFY
) || (f
& TEXT_XRIGHT
) || (f
& TEXT_XCENTER
))
{
const int32_t length
= G_GetStringLineLength
(text
+1, end
, iter
);
char *line
= G_GetSubString
(text
+1, end
, iter
, length
);
int32_t linewidth
= G_ScreenTextSize
(font
, x
, y
, z
, blockangle
, line
, o
| ROTATESPRITE_FULL16
, xspace
, yline
, (f
& TEXT_XJUSTIFY
) ? 0 : xbetween
, (f
& TEXT_YJUSTIFY
) ? 0 : ybetween
, f
& ~
(TEXT_XJUSTIFY
|TEXT_YJUSTIFY
|TEXT_BACKWARDS
), x1
, y1
, x2
, y2
).
x;
Bfree
(line
);
if (f
& TEXT_XJUSTIFY
)
{
xbetween
= (length
== 1) ? 0 : tabledivide32_noinline
(xbetween
- linewidth
, length
- 1);
linewidth
= size.
x;
}
if (f
& TEXT_XRIGHT
)
origin.
x = -linewidth
;
else if (f
& TEXT_XCENTER
)
origin.
x = -(linewidth
/ 2);
}
break;
default:
// width
extent.
x = tilesiz
[tile
].
x * z
;
// obnoxious hardcoded functionality from gametext
if (NUMHACKACTIVE
)
{
char numeral
= '0'; // this is subject to change as an implementation detail
extent.
x = (tilesiz
[G_GetStringTile
(font
, &numeral
, f
)].
x-1) * z
;
}
// height
SetIfGreater
(&extent.
y, (tilesiz
[tile
].
y * z
));
break;
}
// incrementing the coordinate counters
{
int32_t xoffset
= 0;
// advance the x coordinate
if (!(f
& TEXT_XOFFSETZERO
) || NUMHACKACTIVE
)
xoffset
+= extent.
x;
// account for text spacing
if (!NUMHACKACTIVE
// this "if" line ONLY == replicating hardcoded stuff
&& t
!= '\n')
xoffset
+= xbetween
;
// line wrapping
if (f
& TEXT_LINEWRAP
)
{
int32_t wrap
= 0;
const int32_t ang
= blockangle
% 2048;
// it's safe to make some assumptions and not go through G_AddCoordsFromRotation() since we limit to four directions
switch (ang
)
{
case 0:
wrap
= (x
+ (pos.
x + xoffset
) > ((orientation
& 2) ? (320<<16) : ((x2
- USERQUOTE_RIGHTOFFSET
)<<16)));
break;
case 512:
wrap
= (y
+ (pos.
x + xoffset
) > ((orientation
& 2) ? (200<<16) : ((y2
- USERQUOTE_RIGHTOFFSET
)<<16)));
break;
case 1024:
wrap
= (x
- (pos.
x + xoffset
) < ((orientation
& 2) ? 0 : ((x1
+ USERQUOTE_RIGHTOFFSET
)<<16)));
break;
case 1536:
wrap
= (y
- (pos.
x + xoffset
) < ((orientation
& 2) ? 0 : ((y1
+ USERQUOTE_RIGHTOFFSET
)<<16)));
break;
}
if (wrap
) // near-CODEDUP "case '\n':"
{
// reset the position
pos.
x = 0;
// prepare the height
{
int32_t tempyextent
= yline
;
if (f
& (TEXT_INTERNALLINE
|TEXT_TILELINE
))
{
char line
= 'A'; // this is subject to change as an implementation detail
if (f
& TEXT_TILELINE
)
line
= '\x7F'; // tile after '~'
tile
= G_GetStringTile
(font
, &line
, f
);
tempyextent
+= tilesiz
[tile
].
y * z
;
}
SetIfGreater
(&extent.
y, tempyextent
);
}
// move down the line height
if (!(f
& TEXT_YOFFSETZERO
))
pos.
y += extent.
y;
// reset the current height
extent.
y = 0;
// line spacing
pos.
y += ybetween
;
}
else
pos.
x += xoffset
;
}
else
pos.
x += xoffset
;
}
// iterate to the next character in the string
text
+= iter
;
}
// return values in the same manner we receive them
if (!(o
& ROTATESPRITE_FULL16
))
{
size.
x >>= 16;
size.
y >>= 16;
}
return size
;
}
vec2_t G_ScreenTextShadow
(int32_t sx
, int32_t sy
,
const int32_t font
,
int32_t x
, int32_t y
, const int32_t z
, const int32_t blockangle
, const int32_t charangle
,
const char *str
, const int32_t shade
, int32_t pal
, int32_t o
, const int32_t alpha
,
int32_t xspace
, int32_t yline
, int32_t xbetween
, int32_t ybetween
, const int32_t f
,
const int32_t x1
, const int32_t y1
, const int32_t x2
, const int32_t y2
)
{
vec2_t size
= { 0, 0, }; // eventually the return value
if (!(o
& ROTATESPRITE_FULL16
))
{
sx
<<= 16;
sy
<<= 16;
x
<<= 16;
y
<<= 16;
xspace
<<= 16;
yline
<<= 16;
xbetween
<<= 16;
ybetween
<<= 16;
}
G_ScreenText
(font
, x
+ scale
(sx
,z
,65536), y
+ scale
(sy
,z
,65536), z
, blockangle
, charangle
, str
, 127, 4, o
|ROTATESPRITE_FULL16
, alpha
, xspace
, yline
, xbetween
, ybetween
, f
, x1
, y1
, x2
, y2
);
size
= G_ScreenText
(font
, x
, y
, z
, blockangle
, charangle
, str
, shade
, pal
, o
|ROTATESPRITE_FULL16
, alpha
, xspace
, yline
, xbetween
, ybetween
, f
, x1
, y1
, x2
, y2
);
// return values in the same manner we receive them
if (!(o
& ROTATESPRITE_FULL16
))
{
size.
x >>= 16;
size.
y >>= 16;
}
return size
;
}
// flags
// 4: small font, wrap strings?
int32_t G_PrintGameText
(int32_t hack
, int32_t tile
, int32_t x
, int32_t y
, const char *t
,
int32_t s
, int32_t p
, int32_t o
,
int32_t x1
, int32_t y1
, int32_t x2
, int32_t y2
, int32_t z
, int32_t a
)
{
vec2_t dim
;
int32_t f
= TEXT_GAMETEXTNUMHACK
;
int32_t xbetween
= 0;
const int32_t orient
= (hack
& 4) || (hack
& 1) ? (8|16|(o
&1)|(o
&32)) : (2|o
);
if (t
== NULL
)
return -1;
if (!(o
& ROTATESPRITE_FULL16
))
{
x
<<= 16;
y
<<= 16;
}
if (hack
& 4)
{
x
= textsc
(x
);
z
= textsc
(z
);
f
|= TEXT_LINEWRAP
;
}
if (hack
& 8)
{
f
|= TEXT_XOFFSETZERO
;
xbetween
= 8;
}
// order is important, this bit comes after the rest
if ((hack
& 2) && !NAM
) // squishtext
--xbetween
;
if (x
== (160<<16))
f
|= TEXT_XCENTER
;
dim
= G_ScreenText
(tile
, x
, y
, z
, 0, 0, t
, s
, p
, orient
|ROTATESPRITE_FULL16
, a
, (5<<16), (8<<16), (xbetween
<<16), 0, f
, x1
, y1
, x2
, y2
);
x
+= dim.
x;
if (!(o
& ROTATESPRITE_FULL16
))
x
>>= 16;
return x
;
}
int32_t G_GameTextLen
(int32_t x
,const char *t
)
{
vec2_t dim
;
if (t
== NULL
)
return -1;
dim
= G_ScreenTextSize
(STARTALPHANUM
, x
, 0, textsc
(65536L), 0, t
, 2, 5, 8, 0, 0, TEXT_GAMETEXTNUMHACK
, 0, 0, xdim
-1, ydim
-1);
x
+= dim.
x;
return x
;
}
// minitext_yofs: in hud_scale-independent, (<<16)-scaled, 0-200-normalized y coords,
// (sb&ROTATESPRITE_MAX) only.
static int32_t minitext_yofs
= 0;
int32_t minitext_lowercase
= 0;
int32_t minitext_
(int32_t x
,int32_t y
,const char *t
,int32_t s
,int32_t p
,int32_t sb
)
{
vec2_t dim
;
int32_t z
= 65536L;
int32_t f
= 0;
if (t
== NULL
)
{
OSD_Printf
("minitext: NULL text!\n");
return 0;
}
if (!(sb
& ROTATESPRITE_FULL16
))
{
x
<<=16;
y
<<=16;
}
if (!minitext_lowercase
)
f
|= TEXT_UPPERCASE
;
if (sb
& ROTATESPRITE_MAX
)
{
x
= sbarx16
(x
);
y
= minitext_yofs
+sbary16
(y
);
z
= sbarsc
(z
);
}
sb
&= (ROTATESPRITE_MAX
-1)|RS_CENTERORIGIN
;
dim
= G_ScreenText
(MINIFONT
, x
, y
, z
, 0, 0, t
, s
, p
, sb
|ROTATESPRITE_FULL16
, 0, (4<<16), (8<<16), (1<<16), 0, f
, 0, 0, xdim
-1, ydim
-1);
x
+= dim.
x;
if (!(sb
& ROTATESPRITE_FULL16
))
x
>>= 16;
return x
;
}
void G_AddUserQuote
(const char *daquote
)
{
int32_t i
;
for (i
=MAXUSERQUOTES
-1; i
>0; i
--)
{
Bstrcpy
(user_quote
[i
],user_quote
[i
-1]);
user_quote_time
[i
] = user_quote_time
[i
-1];
}
Bstrcpy
(user_quote
[0],daquote
);
OSD_Printf
("%s\n",daquote
);
user_quote_time
[0] = ud.
msgdisptime;
pub
= NUMPAGES
;
}
void G_HandleSpecialKeys
(void)
{
// we need CONTROL_GetInput in order to pick up joystick button presses
if (CONTROL_Started
&& !(g_player
[myconnectindex
].
ps->gm
& MODE_GAME
))
{
ControlInfo noshareinfo
;
CONTROL_GetInput
(&noshareinfo
);
}
// CONTROL_ProcessBinds();
if (g_networkMode
!= NET_DEDICATED_SERVER
&& ALT_IS_PRESSED
&& KB_KeyPressed
(sc_Enter
))
{
if (setgamemode
(!ud.
config.
ScreenMode,ud.
config.
ScreenWidth,ud.
config.
ScreenHeight,ud.
config.
ScreenBPP))
{
OSD_Printf
(OSD_ERROR
"Failed setting fullscreen video mode.\n");
if (setgamemode
(ud.
config.
ScreenMode, ud.
config.
ScreenWidth, ud.
config.
ScreenHeight, ud.
config.
ScreenBPP))
G_GameExit
("Failed to recover from failure to set fullscreen video mode.\n");
}
else ud.
config.
ScreenMode = !ud.
config.
ScreenMode;
KB_ClearKeyDown
(sc_Enter
);
g_restorePalette
= 1;
G_UpdateScreenArea
();
}
if (KB_UnBoundKeyPressed
(sc_F12
))
{
char titlebuf
[256];
Bsprintf
(titlebuf
,HEAD2
" %s",s_buildRev
);
KB_ClearKeyDown
(sc_F12
);
screencapture
("duke0000.tga",0,titlebuf
);
P_DoQuote
(QUOTE_SCREEN_SAVED
,g_player
[myconnectindex
].
ps);
}
// only dispatch commands here when not in a game
if (!(g_player
[myconnectindex
].
ps->gm
& MODE_GAME
))
OSD_DispatchQueued
();
if (g_quickExit
== 0 && KB_KeyPressed
(sc_LeftControl
) && KB_KeyPressed
(sc_LeftAlt
) && (KB_KeyPressed
(sc_Delete
)||KB_KeyPressed
(sc_End
)))
{
g_quickExit
= 1;
G_GameExit
("Quick Exit.");
}
}
void G_GameQuit
(void)
{
if (numplayers
< 2)
G_GameExit
(" ");
if (g_gameQuit
== 0)
{
g_gameQuit
= 1;
g_quitDeadline
= totalclock
+120;
g_netDisconnect
= 1;
}
if ((totalclock
> g_quitDeadline
) && (g_gameQuit
== 1))
G_GameExit
("Timed out.");
}
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
extern int32_t cacnum
;
extern cactype cac
[];
#endif
static void G_ShowCacheLocks
(void)
{
int16_t i
,k
;
if (offscreenrendering
)
return;
k
= 0;
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
for (i
=cacnum
-1; i
>=0; i
--)
if ((*cac
[i
].
lock) >= 200)
{
if (k
>= ydim
-12)
break;
Bsprintf
(tempbuf
,"Locked- %d: Leng:%d, Lock:%d",i
,cac
[i
].
leng,*cac
[i
].
lock);
printext256
(0L,k
,31,-1,tempbuf
,1);
k
+= 6;
}
#endif
if (k
< ydim
-12)
k
+= 6;
for (i
=10; i
>=0; i
--)
if (rts_lumplockbyte
[i
] >= 200)
{
if (k
>= ydim
-12)
break;
Bsprintf
(tempbuf
,"RTS Locked %d:",i
);
printext256
(0,k
,31,-1,tempbuf
,1);
k
+= 6;
}
if (k
>= ydim
-12 && k
<ydim
-6)
printext256
(0,k
,31,-1,"(MORE . . .)",1);
// sounds
if (xdim
< 640)
return;
k
= 18;
for (i
=0; i
<=g_maxSoundPos
; i
++)
if (g_sounds
[i
].
num > 0)
{
int32_t j
, n
=g_sounds
[i
].
num;
for (j
=0; j
<n
; j
++)
{
if (k
>= ydim
-12)
break;
Bsprintf
(tempbuf
, "snd #%d inst %d: voice %d, ow %d", i
, j
,
g_sounds
[i
].
SoundOwner[j
].
voice, g_sounds
[i
].
SoundOwner[j
].
ow);
printext256
(240,k
,31,-1,tempbuf
,0);
k
+= 9;
}
}
}
int32_t A_CheckInventorySprite
(spritetype
*s
)
{
switch (DYNAMICTILEMAP
(s
->picnum
))
{
case FIRSTAID__STATIC
:
case STEROIDS__STATIC
:
case HEATSENSOR__STATIC
:
case BOOTS__STATIC
:
case JETPACK__STATIC
:
case HOLODUKE__STATIC
:
case AIRTANK__STATIC
:
return 1;
default:
return 0;
}
}
// MYOS* CON commands.
LUNATIC_EXTERN
void G_DrawTileGeneric
(int32_t x
, int32_t y
, int32_t zoom
, int32_t tilenum
,
int32_t shade
, int32_t orientation
, int32_t p
)
{
int32_t a
= 0;
orientation
&= (ROTATESPRITE_MAX
-1);
if (orientation
&4)
a
= 1024;
if (!(orientation
&ROTATESPRITE_FULL16
))
{
x
<<=16;
y
<<=16;
}
rotatesprite_win
(x
,y
,zoom
,a
,tilenum
,shade
,p
,2|orientation
);
}
#if !defined LUNATIC
void G_DrawTile
(int32_t x
, int32_t y
, int32_t tilenum
, int32_t shade
, int32_t orientation
)
{
DukePlayer_t
*ps
= g_player
[screenpeek
].
ps;
int32_t p
= ps
->cursectnum
>= 0 ? sector
[ps
->cursectnum
].
floorpal : 0;
G_DrawTileGeneric
(x
,y
,65536, tilenum
,shade
,orientation
, p
);
}
void G_DrawTilePal
(int32_t x
, int32_t y
, int32_t tilenum
, int32_t shade
, int32_t orientation
, int32_t p
)
{
G_DrawTileGeneric
(x
,y
,65536, tilenum
,shade
,orientation
, p
);
}
void G_DrawTileSmall
(int32_t x
, int32_t y
, int32_t tilenum
, int32_t shade
, int32_t orientation
)
{
DukePlayer_t
*ps
= g_player
[screenpeek
].
ps;
int32_t p
= ps
->cursectnum
>= 0 ? sector
[ps
->cursectnum
].
floorpal : 0;
G_DrawTileGeneric
(x
,y
,32768, tilenum
,shade
,orientation
, p
);
}
void G_DrawTilePalSmall
(int32_t x
, int32_t y
, int32_t tilenum
, int32_t shade
, int32_t orientation
, int32_t p
)
{
G_DrawTileGeneric
(x
,y
,32768, tilenum
,shade
,orientation
, p
);
}
#endif
#define POLYMOSTTRANS (1)
#define POLYMOSTTRANS2 (1|32)
// Draws inventory numbers in the HUD for both the full and mini status bars.
// yofs: in hud_scale-independent, (<<16)-scaled, 0-200-normalized y coords.
static void G_DrawInvNum
(int32_t x
, int32_t yofs
, int32_t y
, char num1
, char ha
, int32_t sbits
)
{
char dabuf
[16];
int32_t i
, shd
= (x
< 0);
const int32_t sbscale
= sbarsc
(65536);
const int32_t sby
= yofs
+sbary
(y
), sbyp1
= yofs
+sbary
(y
+1);
if (shd
) x
= -x
;
Bsprintf
(dabuf
, "%d", num1
);
if (num1
> 99)
{
if (shd
&& ud.
screen_size == 4 && getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
{
for (i
=0; i
<=2; i
++)
rotatesprite_fs
(sbarx
(x
+(-4+4*i
)+1),sbyp1
,sbscale
,0,THREEBYFIVE
+dabuf
[i
]-'0',
127, 4, POLYMOSTTRANS
|sbits
);
}
for (i
=0; i
<=2; i
++)
rotatesprite_fs
(sbarx
(x
+(-4+4*i
)),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[i
]-'0',ha
, 0, sbits
);
return;
}
if (num1
> 9)
{
if (shd
&& ud.
screen_size == 4 && getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
{
rotatesprite_fs
(sbarx
(x
+1),sbyp1
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',127,4,POLYMOSTTRANS
|sbits
);
rotatesprite_fs
(sbarx
(x
+4+1),sbyp1
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',127,4,POLYMOSTTRANS
|sbits
);
}
rotatesprite_fs
(sbarx
(x
),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,sbits
);
rotatesprite_fs
(sbarx
(x
+4),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',ha
,0,sbits
);
return;
}
rotatesprite_fs
(sbarx
(x
+4+1),sbyp1
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,4,sbits
);
rotatesprite_fs
(sbarx
(x
+4),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,sbits
);
}
static void G_DrawWeapNum
(int16_t ind
,int32_t x
,int32_t y
,int32_t num1
, int32_t num2
,int32_t ha
)
{
char dabuf
[16];
const int32_t sbscale
= sbarsc
(65536);
const int32_t sby
= sbary
(y
);
rotatesprite_fs
(sbarx
(x
-7),sby
,sbscale
,0,THREEBYFIVE
+ind
+1,ha
-10,7,10);
rotatesprite_fs
(sbarx
(x
-3),sby
,sbscale
,0,THREEBYFIVE
+10,ha
,0,10);
if (VOLUMEONE
&& (ind
> HANDBOMB_WEAPON
|| ind
< 0))
{
minitextshade
(x
+1,y
-4,"ORDER",20,11,2+8+16+ROTATESPRITE_MAX
);
return;
}
rotatesprite_fs
(sbarx
(x
+9),sby
,sbscale
,0,THREEBYFIVE
+11,ha
,0,10);
if (num1
> 99) num1
= 99;
if (num2
> 99) num2
= 99;
Bsprintf
(dabuf
,"%d",num1
);
if (num1
> 9)
{
rotatesprite_fs
(sbarx
(x
),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+4),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',ha
,0,10);
}
else rotatesprite_fs
(sbarx
(x
+4),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
Bsprintf
(dabuf
,"%d",num2
);
if (num2
> 9)
{
rotatesprite_fs
(sbarx
(x
+13),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+17),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',ha
,0,10);
return;
}
rotatesprite_fs
(sbarx
(x
+13),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
}
static void G_DrawWeapNum2
(char ind
,int32_t x
,int32_t y
,int32_t num1
, int32_t num2
,char ha
)
{
char dabuf
[16];
const int32_t sbscale
= sbarsc
(65536);
const int32_t sby
= sbary
(y
);
rotatesprite_fs
(sbarx
(x
-7),sby
,sbscale
,0,THREEBYFIVE
+ind
+1,ha
-10,7,10);
rotatesprite_fs
(sbarx
(x
-4),sby
,sbscale
,0,THREEBYFIVE
+10,ha
,0,10);
rotatesprite_fs
(sbarx
(x
+13),sby
,sbscale
,0,THREEBYFIVE
+11,ha
,0,10);
Bsprintf
(dabuf
,"%d",num1
);
if (num1
> 99)
{
rotatesprite_fs
(sbarx
(x
),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+4),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+8),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[2]-'0',ha
,0,10);
}
else if (num1
> 9)
{
rotatesprite_fs
(sbarx
(x
+4),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+8),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',ha
,0,10);
}
else rotatesprite_fs
(sbarx
(x
+8),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
Bsprintf
(dabuf
,"%d",num2
);
if (num2
> 99)
{
rotatesprite_fs
(sbarx
(x
+17),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+21),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+25),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[2]-'0',ha
,0,10);
}
else if (num2
> 9)
{
rotatesprite_fs
(sbarx
(x
+17),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
rotatesprite_fs
(sbarx
(x
+21),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[1]-'0',ha
,0,10);
return;
}
else
rotatesprite_fs
(sbarx
(x
+25),sby
,sbscale
,0,THREEBYFIVE
+dabuf
[0]-'0',ha
,0,10);
}
static void G_DrawWeapAmounts
(const DukePlayer_t
*p
,int32_t x
,int32_t y
,int32_t u
)
{
int32_t cw
= p
->curr_weapon
;
if (u
&4)
{
if (u
!= -1) G_PatchStatusBar
(88,178,88+37,178+6); //original code: (96,178,96+12,178+6);
G_DrawWeapNum2
(PISTOL_WEAPON
,x
,y
,
p
->ammo_amount
[PISTOL_WEAPON
],p
->max_ammo_amount
[PISTOL_WEAPON
],
12-20*(cw
== PISTOL_WEAPON
));
}
if (u
&8)
{
if (u
!= -1) G_PatchStatusBar
(88,184,88+37,184+6); //original code: (96,184,96+12,184+6);
G_DrawWeapNum2
(SHOTGUN_WEAPON
,x
,y
+6,
p
->ammo_amount
[SHOTGUN_WEAPON
],p
->max_ammo_amount
[SHOTGUN_WEAPON
],
(((p
->gotweapon
& (1<<SHOTGUN_WEAPON
)) == 0)*9)+12-18*
(cw
== SHOTGUN_WEAPON
));
}
if (u
&16)
{
if (u
!= -1) G_PatchStatusBar
(88,190,88+37,190+6); //original code: (96,190,96+12,190+6);
G_DrawWeapNum2
(CHAINGUN_WEAPON
,x
,y
+12,
p
->ammo_amount
[CHAINGUN_WEAPON
],p
->max_ammo_amount
[CHAINGUN_WEAPON
],
(((p
->gotweapon
& (1<<CHAINGUN_WEAPON
)) == 0)*9)+12-18*
(cw
== CHAINGUN_WEAPON
));
}
if (u
&32)
{
if (u
!= -1) G_PatchStatusBar
(127,178,127+29,178+6); //original code: (135,178,135+8,178+6);
G_DrawWeapNum
(RPG_WEAPON
,x
+39,y
,
p
->ammo_amount
[RPG_WEAPON
],p
->max_ammo_amount
[RPG_WEAPON
],
(((p
->gotweapon
& (1<<RPG_WEAPON
)) == 0)*9)+12-19*
(cw
== RPG_WEAPON
));
}
if (u
&64)
{
if (u
!= -1) G_PatchStatusBar
(127,184,127+29,184+6); //original code: (135,184,135+8,184+6);
G_DrawWeapNum
(HANDBOMB_WEAPON
,x
+39,y
+6,
p
->ammo_amount
[HANDBOMB_WEAPON
],p
->max_ammo_amount
[HANDBOMB_WEAPON
],
(((!p
->ammo_amount
[HANDBOMB_WEAPON
])|((p
->gotweapon
& (1<<HANDBOMB_WEAPON
)) == 0))*9)+12-19*
((cw
== HANDBOMB_WEAPON
) || (cw
== HANDREMOTE_WEAPON
)));
}
if (u
&128)
{
if (u
!= -1) G_PatchStatusBar
(127,190,127+29,190+6); //original code: (135,190,135+8,190+6);
if (p
->subweapon
&(1<<GROW_WEAPON
))
G_DrawWeapNum
(SHRINKER_WEAPON
,x
+39,y
+12,
p
->ammo_amount
[GROW_WEAPON
],p
->max_ammo_amount
[GROW_WEAPON
],
(((p
->gotweapon
& (1<<GROW_WEAPON
)) == 0)*9)+12-18*
(cw
== GROW_WEAPON
));
else
G_DrawWeapNum
(SHRINKER_WEAPON
,x
+39,y
+12,
p
->ammo_amount
[SHRINKER_WEAPON
],p
->max_ammo_amount
[SHRINKER_WEAPON
],
(((p
->gotweapon
& (1<<SHRINKER_WEAPON
)) == 0)*9)+12-18*
(cw
== SHRINKER_WEAPON
));
}
if (u
&256)
{
if (u
!= -1) G_PatchStatusBar
(158,178,162+29,178+6); //original code: (166,178,166+8,178+6);
G_DrawWeapNum
(DEVISTATOR_WEAPON
,x
+70,y
,
p
->ammo_amount
[DEVISTATOR_WEAPON
],p
->max_ammo_amount
[DEVISTATOR_WEAPON
],
(((p
->gotweapon
& (1<<DEVISTATOR_WEAPON
)) == 0)*9)+12-18*
(cw
== DEVISTATOR_WEAPON
));
}
if (u
&512)
{
if (u
!= -1) G_PatchStatusBar
(158,184,162+29,184+6); //original code: (166,184,166+8,184+6);
G_DrawWeapNum
(TRIPBOMB_WEAPON
,x
+70,y
+6,
p
->ammo_amount
[TRIPBOMB_WEAPON
],p
->max_ammo_amount
[TRIPBOMB_WEAPON
],
(((p
->gotweapon
& (1<<TRIPBOMB_WEAPON
)) == 0)*9)+12-18*
(cw
== TRIPBOMB_WEAPON
));
}
if (u
&65536L)
{
if (u
!= -1) G_PatchStatusBar
(158,190,162+29,190+6); //original code: (166,190,166+8,190+6);
G_DrawWeapNum
(-1,x
+70,y
+12,
p
->ammo_amount
[FREEZE_WEAPON
],p
->max_ammo_amount
[FREEZE_WEAPON
],
(((p
->gotweapon
& (1<<FREEZE_WEAPON
)) == 0)*9)+12-18*
(cw
== FREEZE_WEAPON
));
}
}
// yofs: in hud_scale-independent, (<<16)-scaled, 0-200-normalized y coords.
static void G_DrawDigiNum_
(int32_t x
, int32_t yofs
, int32_t y
, int32_t n
, char s
, int32_t cs
)
{
if (!(cs
& ROTATESPRITE_FULL16
))
{
x
<<= 16;
y
<<= 16;
}
G_DrawTXDigiNumZ
(DIGITALNUM
, sbarx16
(x
), yofs
+ sbary16
(y
), n
, s
, 0, cs
|ROTATESPRITE_FULL16
, 0, 0, xdim
-1, ydim
-1, sbarsc
(65536L));
}
static inline void G_DrawDigiNum
(int32_t x
, int32_t y
, int32_t n
, char s
, int32_t cs
)
{
G_DrawDigiNum_
(x
, 0, y
, n
, s
, cs
);
}
void G_DrawTXDigiNumZ
(int32_t starttile
, int32_t x
,int32_t y
,int32_t n
,int32_t s
,int32_t pal
,
int32_t cs
,int32_t x1
, int32_t y1
, int32_t x2
, int32_t y2
, int32_t z
)
{
char b
[12];
Bsprintf
(b
,"%d",n
);
if (!(cs
& ROTATESPRITE_FULL16
))
{
x
<<= 16;
y
<<= 16;
}
G_ScreenText
(starttile
, x
, y
, z
, 0, 0, b
, s
, pal
, cs
|2|ROTATESPRITE_FULL16
, 0, (4<<16), (8<<16), (1<<16), 0, TEXT_XCENTER
|TEXT_DIGITALNUMBER
, x1
, y1
, x2
, y2
);
}
static void G_DrawAltDigiNum
(int32_t x
, int32_t y
, int32_t n
, char s
, int32_t cs
)
{
int32_t i
, j
= 0, k
, p
, c
;
char b
[12];
int32_t rev
= (x
< 0);
int32_t shd
= (y
< 0);
const int32_t sbscale
= sbarsc
(65536);
if (rev
) x
= -x
;
if (shd
) y
= -y
;
i
= Bsprintf
(b
,"%d",n
);
for (k
=i
-1; k
>=0; k
--)
{
p
= althud_numbertile
+ b
[k
]-'0';
j
+= tilesiz
[p
].
x+1;
}
c
= x
-(j
>>1);
if (rev
)
{
for (k
=0; k
<i
; k
++)
{
p
= althud_numbertile
+ b
[k
]-'0';
if (shd
&& getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
rotatesprite_fs
(sbarxr
(c
+j
-1),sbary
(y
+1),sbscale
,0,p
,127,4,cs
|POLYMOSTTRANS2
);
rotatesprite_fs
(sbarxr
(c
+j
),sbary
(y
),sbscale
,0,p
,s
,althud_numberpal
,cs
);
j
-= tilesiz
[p
].
x+1;
}
return;
}
j
= 0;
for (k
=0; k
<i
; k
++)
{
p
= althud_numbertile
+ b
[k
]-'0';
if (shd
&& getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
rotatesprite_fs
(sbarx
(c
+j
+1),sbary
(y
+1),sbscale
,0,p
,127,4,cs
|POLYMOSTTRANS2
);
rotatesprite_fs
(sbarx
(c
+j
),sbary
(y
),sbscale
,0,p
,s
,althud_numberpal
,cs
);
j
+= tilesiz
[p
].
x+1;
}
}
static int32_t invensc
(int32_t maximum
) // used to reposition the inventory icon selector as the HUD scales
{
return scale
(maximum
<< 16, ud.
statusbarscale - 36, 100 - 36);
}
static void G_DrawInventory
(const DukePlayer_t
*p
)
{
int32_t n
, j
= 0, x
= 0, y
;
n
= (p
->inv_amount
[GET_JETPACK
] > 0)<<3;
if (n
&8) j
++;
n
|= (p
->inv_amount
[GET_SCUBA
] > 0)<<5;
if (n
&32) j
++;
n
|= (p
->inv_amount
[GET_STEROIDS
] > 0)<<1;
if (n
&2) j
++;
n
|= (p
->inv_amount
[GET_HOLODUKE
] > 0)<<2;
if (n
&4) j
++;
n
|= (p
->inv_amount
[GET_FIRSTAID
] > 0);
if (n
&1) j
++;
n
|= (p
->inv_amount
[GET_HEATS
] > 0)<<4;
if (n
&16) j
++;
n
|= (p
->inv_amount
[GET_BOOTS
] > 0)<<6;
if (n
&64) j
++;
x
= (160-(j
*11))<<16; // nearly center
j
= 0;
if (ud.
screen_size < 8) // mini-HUDs or no HUD
{
y
= 172<<16;
if (ud.
screen_size == 4 && ud.
althud == 1) // modern mini-HUD
y
-= invensc
(tilesiz
[BIGALPHANUM
].
y+10); // slide on the y-axis
}
else // full HUD
{
y
= (200<<16) - (sbarsc
(tilesiz
[BOTTOMSTATUSBAR
].
y<<16) + (12<<16) + (tilesiz
[BOTTOMSTATUSBAR
].
y<<(16-1)));
if (!ud.
statusbarmode) // original non-overlay mode
y
+= sbarsc
(tilesiz
[BOTTOMSTATUSBAR
].
y<<16)>>1; // account for the viewport y-size as the HUD scales
}
if (ud.
screen_size == 4 && !ud.
althud) // classic mini-HUD
x
+= invensc
(ud.
multimode > 1 ? 56 : 65); // slide on the x-axis
while (j
<= 9)
{
if (n
&(1<<j
))
{
switch (n
&(1<<j
))
{
case 1:
rotatesprite_win
(x
,y
,65536L,0,FIRSTAID_ICON
,0,0,2+16);
break;
case 2:
rotatesprite_win
(x
+(1<<16),y
,65536L,0,STEROIDS_ICON
,0,0,2+16);
break;
case 4:
rotatesprite_win
(x
+(2<<16),y
,65536L,0,HOLODUKE_ICON
,0,0,2+16);
break;
case 8:
rotatesprite_win
(x
,y
,65536L,0,JETPACK_ICON
,0,0,2+16);
break;
case 16:
rotatesprite_win
(x
,y
,65536L,0,HEAT_ICON
,0,0,2+16);
break;
case 32:
rotatesprite_win
(x
,y
,65536L,0,AIRTANK_ICON
,0,0,2+16);
break;
case 64:
rotatesprite_win
(x
,y
-(1<<16),65536L,0,BOOT_ICON
,0,0,2+16);
break;
}
x
+= 22<<16;
if (p
->inven_icon
== j
+1)
rotatesprite_win
(x
-(2<<16),y
+(19<<16),65536L,1024,ARROW
,-32,0,2+16);
}
j
++;
}
}
void G_DrawFrags
(void)
{
int32_t i
, j
= 0;
const int32_t orient
= 2+8+16+64;
for (TRAVERSE_CONNECT
(i
))
if (i
> j
)
j
= i
;
for (i
=0; i
<=(j
>>2); i
++)
rotatesprite_fs
(0,(8*i
)<<16,65600, 0, FRAGBAR
, 0,0,orient
);
for (TRAVERSE_CONNECT
(i
))
{
const DukePlayer_t
*ps
= g_player
[i
].
ps;
minitext
(21+(73*(i
&3)), 2+((i
&28)<<1), g_player
[i
].
user_name, ps
->palookup
, 2+8+16);
Bsprintf
(tempbuf
, "%d", ps
->frag
-ps
->fraggedself
);
minitext
(17+50+(73*(i
&3)), 2+((i
&28)<<1), tempbuf
, ps
->palookup
, 2+8+16);
}
}
static int32_t G_GetInvAmount
(const DukePlayer_t
*p
)
{
switch (p
->inven_icon
)
{
case ICON_FIRSTAID
:
return p
->inv_amount
[GET_FIRSTAID
];
case ICON_STEROIDS
:
return ((p
->inv_amount
[GET_STEROIDS
]+3)>>2);
case ICON_HOLODUKE
:
return ((p
->inv_amount
[GET_HOLODUKE
]+15)/24);
case ICON_JETPACK
:
return ((p
->inv_amount
[GET_JETPACK
]+15)>>4);
case ICON_HEATS
:
return p
->inv_amount
[GET_HEATS
]/12;
case ICON_SCUBA
:
return ((p
->inv_amount
[GET_SCUBA
]+63)>>6);
case ICON_BOOTS
:
return (p
->inv_amount
[GET_BOOTS
]>>1);
}
return -1;
}
static int32_t G_GetInvOn
(const DukePlayer_t
*p
)
{
switch (p
->inven_icon
)
{
case ICON_HOLODUKE
:
return p
->holoduke_on
;
case ICON_JETPACK
:
return p
->jetpack_on
;
case ICON_HEATS
:
return p
->heat_on
;
}
return 0x80000000;
}
static int32_t G_GetMorale
(int32_t p_i
, int32_t snum
)
{
#if !defined LUNATIC
return Gv_GetVarByLabel
("PLR_MORALE",-1, p_i
, snum
);
#else
UNREFERENCED_PARAMETER
(p_i
);
UNREFERENCED_PARAMETER
(snum
);
return -1;
#endif
}
static inline void rotatesprite_althud
(int32_t sx
, int32_t sy
, int32_t z
, int16_t a
, int16_t picnum
,int8_t dashade
, char dapalnum
, int32_t dastat
)
{
if (getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
rotatesprite_
(sbarx
(sx
+1), sbary
(sy
+1), z
, a
, picnum
, 127, 4, dastat
+ POLYMOSTTRANS2
, 0, 0, 0, 0, xdim
- 1, ydim
- 1);
rotatesprite_
(sbarx
(sx
), sbary
(sy
), z
, a
, picnum
, dashade
, dapalnum
, dastat
, 0, 0, 0, 0, xdim
- 1, ydim
- 1);
}
static inline void rotatesprite_althudr
(int32_t sx
, int32_t sy
, int32_t z
, int16_t a
, int16_t picnum
, int8_t dashade
, char dapalnum
, int32_t dastat
)
{
if (getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
rotatesprite_
(sbarxr
(sx
+ 1), sbary
(sy
+ 1), z
, a
, picnum
, 127, 4, dastat
+ POLYMOSTTRANS2
, 0, 0, 0, 0, xdim
- 1, ydim
- 1);
rotatesprite_
(sbarxr
(sx
), sbary
(sy
), z
, a
, picnum
, dashade
, dapalnum
, dastat
, 0, 0, 0, 0, xdim
- 1, ydim
- 1);
}
static void G_DrawStatusBar
(int32_t snum
)
{
const DukePlayer_t
*const p
= g_player
[snum
].
ps;
int32_t i
, j
, o
, u
;
int32_t permbit
= 0;
#ifdef SPLITSCREEN_MOD_HACKS
const int32_t ss
= g_fakeMultiMode
? 4 : ud.
screen_size;
const int32_t althud
= g_fakeMultiMode
? 0 : ud.
althud;
#else
const int32_t ss
= ud.
screen_size;
const int32_t althud
= ud.
althud;
#endif
const int32_t SBY
= (200-tilesiz
[BOTTOMSTATUSBAR
].
y);
const int32_t sb15
= sbarsc
(32768), sb15h
= sbarsc
(49152);
const int32_t sb16
= sbarsc
(65536);
static int32_t item_icons
[8];
if (ss
< 4)
return;
if (item_icons
[0] == 0)
{
int32_t iicons
[8] = { -1, FIRSTAID_ICON
, STEROIDS_ICON
, HOLODUKE_ICON
,
JETPACK_ICON
, HEAT_ICON
, AIRTANK_ICON
, BOOT_ICON
};
Bmemcpy
(item_icons
, iicons
, sizeof(item_icons
));
}
if (getrendermode
() >= REND_POLYMOST
) pus
= NUMPAGES
; // JBF 20040101: always redraw in GL
if ((g_netServer
|| ud.
multimode > 1) && ((GametypeFlags
[ud.
coop] & GAMETYPE_FRAGBAR
) || g_fakeMultiMode
))
{
if (pus
)
G_DrawFrags
();
else
{
for (TRAVERSE_CONNECT
(i
))
if (g_player
[i
].
ps->frag
!= sbar.
frag[i
])
{
G_DrawFrags
();
break;
}
}
for (TRAVERSE_CONNECT
(i
))
if (i
!= myconnectindex
)
sbar.
frag[i
] = g_player
[i
].
ps->frag
;
}
if (ss
== 4) //DRAW MINI STATUS BAR:
{
if (althud
)
{
// ALTERNATIVE STATUS BAR
int32_t hudoffset
= althud
== 2 ? 32 : 200;
static int32_t ammo_sprites
[MAX_WEAPONS
];
if (EDUKE32_PREDICT_FALSE
(ammo_sprites
[0] == 0))
{
/* this looks stupid but it lets us initialize static memory to dynamic values
these values can be changed from the CONs with dynamic tile remapping
but we don't want to have to recreate the values in memory every time
the HUD is drawn */
int32_t asprites
[MAX_WEAPONS
] = { -1, AMMO
, SHOTGUNAMMO
, BATTERYAMMO
,
RPGAMMO
, HBOMBAMMO
, CRYSTALAMMO
, DEVISTATORAMMO
,
TRIPBOMBSPRITE
, FREEZEAMMO
+1, HBOMBAMMO
, GROWAMMO
};
Bmemcpy
(ammo_sprites
, asprites
, sizeof(ammo_sprites
));
}
// rotatesprite_fs(sbarx(5+1),sbary(200-25+1),sb15h,0,SIXPAK,0,4,10+16+1+32);
// rotatesprite_fs(sbarx(5),sbary(200-25),sb15h,0,SIXPAK,0,0,10+16);
rotatesprite_althud
(2,hudoffset
-21,sb15h
,0,COLA
,0,0,10+16+256);
if (sprite
[p
->i
].
pal == 1 && p
->last_extra
< 2)
G_DrawAltDigiNum
(40,-(hudoffset
-22),1,-16,10+16+256);
else if (!althud_flashing
|| p
->last_extra
> (p
->max_player_health
>>2) || totalclock
&32)
{
int32_t s
= -8;
if (althud_flashing
&& p
->last_extra
> p
->max_player_health
)
s
+= (sintable
[(totalclock
<<5)&2047]>>10);
G_DrawAltDigiNum
(40,-(hudoffset
-22),p
->last_extra
,s
,10+16+256);
}
rotatesprite_althud
(62,hudoffset
-25,sb15h
,0,SHIELD
,0,0,10+16+256);
{
int32_t lAmount
= G_GetMorale
(p
->i
, snum
);
if (lAmount
== -1)
lAmount
= p
->inv_amount
[GET_SHIELD
];
G_DrawAltDigiNum
(105,-(hudoffset
-22),lAmount
,-16,10+16+256);
}
if (ammo_sprites
[p
->curr_weapon
] >= 0)
{
i
= (tilesiz
[ammo_sprites
[p
->curr_weapon
]].
y >= 50) ? 16384 : 32768;
rotatesprite_althudr
(57,hudoffset
-15,sbarsc
(i
),0,ammo_sprites
[p
->curr_weapon
],0,0,10+512);
}
if (PWEAPON
(snum
, p
->curr_weapon
, WorksLike
) == HANDREMOTE_WEAPON
) i
= HANDBOMB_WEAPON
;
else i
= p
->curr_weapon
;
if (PWEAPON
(snum
, p
->curr_weapon
, WorksLike
) != KNEE_WEAPON
&&
(!althud_flashing
|| totalclock
&32 || p
->ammo_amount
[i
] > (p
->max_ammo_amount
[i
]/10)))
G_DrawAltDigiNum
(-20,-(hudoffset
-22),p
->ammo_amount
[i
],-16,10+16+512);
o
= 102;
permbit
= 0;
if (p
->inven_icon
)
{
const int32_t orient
= 10+16+permbit
+256;
i
= ((unsigned)p
->inven_icon
< ICON_MAX
) ? item_icons
[p
->inven_icon
] : -1;
if (i
>= 0)
rotatesprite_althud
(231-o
,hudoffset
-21-2,sb16
,0,i
,0,0,orient
);
if (getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
minitextshade
(292-30-o
+1,hudoffset
-10-3+1,"%",127,4, POLYMOSTTRANS
+orient
+ROTATESPRITE_MAX
);
minitext
(292-30-o
,hudoffset
-10-3,"%",6, orient
+ROTATESPRITE_MAX
);
i
= G_GetInvAmount
(p
);
j
= G_GetInvOn
(p
);
G_DrawInvNum
(-(284-30-o
),0,hudoffset
-6-3,(uint8_t)i
,0,10+permbit
+256);
if (j
> 0)
{
if (getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
minitextshade
(288-30-o
+1,hudoffset
-20-3+1,"On",127,4, POLYMOSTTRANS
+orient
+ROTATESPRITE_MAX
);
minitext
(288-30-o
,hudoffset
-20-3,"On",0, orient
+ROTATESPRITE_MAX
);
}
else if ((uint32_t)j
!= 0x80000000)
{
if (getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
minitextshade
(284-30-o
+1,hudoffset
-20-3+1,"Off",127,4, POLYMOSTTRANS
+orient
+ROTATESPRITE_MAX
);
minitext
(284-30-o
,hudoffset
-20-3,"Off",2, orient
+ROTATESPRITE_MAX
);
}
if (p
->inven_icon
>= ICON_SCUBA
)
{
if (getrendermode
() >= REND_POLYMOST
&& althud_shadows
)
minitextshade
(284-35-o
+1,hudoffset
-20-3+1,"Auto",127,4, POLYMOSTTRANS
+orient
+ROTATESPRITE_MAX
);
minitext
(284-35-o
,hudoffset
-20-3,"Auto",2, orient
+ROTATESPRITE_MAX
);
}
}
if (ud.
althud == 2)
hudoffset
+= 40;
if (p
->got_access
&1) rotatesprite_althudr
(39, hudoffset
-43, sb15
, 0, ACCESSCARD
, 0, 0, 10+16+512);
if (p
->got_access
&4) rotatesprite_althudr
(34, hudoffset
-41, sb15
, 0, ACCESSCARD
, 0, 23, 10+16+512);
if (p
->got_access
&2) rotatesprite_althudr
(29, hudoffset
-39, sb15
, 0, ACCESSCARD
, 0, 21, 10+16+512);
}
else
{
// ORIGINAL MINI STATUS BAR
int32_t orient
= 2+8+16+256, yofssh
=0;
#ifdef SPLITSCREEN_MOD_HACKS
int32_t yofs
=0;
if (g_fakeMultiMode
)
{
const int32_t sidebyside
= (ud.
screen_size!=0);
if (sidebyside
&& snum
==1)
{
orient
|= RS_CENTERORIGIN
;
}
else if (!sidebyside
&& snum
==0)
{
yofs
= -100;
yofssh
= yofs
<<16;
}
}
#endif
rotatesprite_fs
(sbarx
(5), yofssh
+sbary
(200-28), sb16
, 0, HEALTHBOX
, 0, 21, orient
);
if (p
->inven_icon
)
rotatesprite_fs
(sbarx
(69), yofssh
+sbary
(200-30), sb16
, 0, INVENTORYBOX
, 0, 21, orient
);
// health
{
int32_t health
= (sprite
[p
->i
].
pal == 1 && p
->last_extra
< 2) ? 1 : p
->last_extra
;
G_DrawDigiNum_
(20, yofssh
, 200-17, health
, -16, orient
);
}
rotatesprite_fs
(sbarx
(37), yofssh
+sbary
(200-28), sb16
, 0, AMMOBOX
, 0, 21, orient
);
if (PWEAPON
(snum
, p
->curr_weapon
, WorksLike
) == HANDREMOTE_WEAPON
)
i
= HANDBOMB_WEAPON
;
else
i
= p
->curr_weapon
;
G_DrawDigiNum_
(53, yofssh
, 200-17, p
->ammo_amount
[i
], -16, orient
);
o
= 158;
permbit
= 0;
if (p
->inven_icon
)
{
// orient |= permbit;
i
= ((unsigned)p
->inven_icon
< ICON_MAX
) ? item_icons
[p
->inven_icon
] : -1;
if (i
>= 0)
rotatesprite_fs
(sbarx
(231-o
), yofssh
+sbary
(200-21), sb16
, 0, i
, 0, 0, orient
);
// scale by status bar size
orient
|= ROTATESPRITE_MAX
;
minitext_yofs
= yofssh
;
minitext
(292-30-o
, 190, "%", 6, orient
);
i
= G_GetInvAmount
(p
);
j
= G_GetInvOn
(p
);
G_DrawInvNum
(284-30-o
, yofssh
, 200-6, (uint8_t)i
, 0, orient
&~
16);
if (j
> 0)
minitext
(288-30-o
, 180, "On", 0, orient
);
else if ((uint32_t)j
!= 0x80000000)
minitext
(284-30-o
, 180, "Off", 2, orient
);
if (p
->inven_icon
>= ICON_SCUBA
)
minitext
(284-35-o
, 180, "Auto", 2, orient
);
minitext_yofs
= 0;
}
}
return;
}
//DRAW/UPDATE FULL STATUS BAR:
if (pus
)
{
pus
= 0;
u
= -1;
}
else u
= 0;
if (sbar.
frag[myconnectindex
] != p
->frag
)
{
sbar.
frag[myconnectindex
] = p
->frag
;
u
|= 32768;
}
if (sbar.
got_access != p
->got_access
)
{
sbar.
got_access = p
->got_access
;
u
|= 16384;
}
if (sbar.
last_extra != p
->last_extra
)
{
sbar.
last_extra = p
->last_extra
;
u
|= 1;
}
{
int32_t lAmount
= G_GetMorale
(p
->i
, snum
);
if (lAmount
== -1)
lAmount
= p
->inv_amount
[GET_SHIELD
];
if (sbar.
inv_amount[GET_SHIELD
] != lAmount
)
{
sbar.
inv_amount[GET_SHIELD
] = lAmount
;
u
|= 2;
}
}
if (sbar.
curr_weapon != p
->curr_weapon
)
{
sbar.
curr_weapon = p
->curr_weapon
;
u
|= (4+8+16+32+64+128+256+512+1024+65536L);
}
for (i
=1; i
<MAX_WEAPONS
; i
++)
{
if (sbar.
ammo_amount[i
] != p
->ammo_amount
[i
])
{
sbar.
ammo_amount[i
] = p
->ammo_amount
[i
];
if (i
< 9)
u
|= ((2<<i
)+1024);
else u
|= 65536L+1024;
}
if ((sbar.
gotweapon & (1<<i
)) != (p
->gotweapon
& (1<<i
)))
{
if (p
->gotweapon
& (1<<i
))
sbar.
gotweapon |= 1<<i
;
else sbar.
gotweapon &= ~
(1<<i
);
if (i
< 9)
u
|= ((2<<i
)+1024);
else u
|= 65536L+1024;
}
}
if (sbar.
inven_icon != p
->inven_icon
)
{
sbar.
inven_icon = p
->inven_icon
;
u
|= (2048+4096+8192);
}
if (sbar.
holoduke_on != p
->holoduke_on
)
{
sbar.
holoduke_on = p
->holoduke_on
;
u
|= (4096+8192);
}
if (sbar.
jetpack_on != p
->jetpack_on
)
{
sbar.
jetpack_on = p
->jetpack_on
;
u
|= (4096+8192);
}
if (sbar.
heat_on != p
->heat_on
)
{
sbar.
heat_on = p
->heat_on
;
u
|= (4096+8192);
}
{
static const int32_t check_items
[] = {
GET_FIRSTAID
, GET_STEROIDS
, GET_HOLODUKE
, GET_JETPACK
,
GET_HEATS
, GET_SCUBA
, GET_BOOTS
};
for (i
=0; i
<(int32_t)sizeof(check_items
)/(int32_t)sizeof(check_items
[0]); i
++)
{
int32_t item
= check_items
[i
];
if (sbar.
inv_amount[item
] != p
->inv_amount
[item
])
{
sbar.
inv_amount[item
] = p
->inv_amount
[item
];
u
|= 8192;
}
}
}
#if 0
if (u
== 0)
return;
#else
// FIXME: full status bar draws rectangles in the wrong places when it's
// updated partially.
u
= -1;
#endif
//0 - update health
//1 - update armor
//2 - update PISTOL_WEAPON ammo
//3 - update SHOTGUN_WEAPON ammo
//4 - update CHAINGUN_WEAPON ammo
//5 - update RPG_WEAPON ammo
//6 - update HANDBOMB_WEAPON ammo
//7 - update SHRINKER_WEAPON ammo
//8 - update DEVISTATOR_WEAPON ammo
//9 - update TRIPBOMB_WEAPON ammo
//10 - update ammo display
//11 - update inventory icon
//12 - update inventory on/off
//13 - update inventory %
//14 - update keys
//15 - update kills
//16 - update FREEZE_WEAPON ammo
if (u
== -1)
{
G_PatchStatusBar
(0,0,320,200);
if ((g_netServer
|| ud.
multimode > 1) && (GametypeFlags
[ud.
coop] & GAMETYPE_FRAGBAR
))
rotatesprite_fs
(sbarx
(277+1),sbary
(SBY
+7-1),sb16
,0,KILLSICON
,0,0,10+16);
}
if ((g_netServer
|| ud.
multimode > 1) && (GametypeFlags
[ud.
coop] & GAMETYPE_FRAGBAR
))
{
if (u
&32768)
{
if (u
!= -1) G_PatchStatusBar
(276,SBY
+17,299,SBY
+17+10);
G_DrawDigiNum
(287,SBY
+17,max
(p
->frag
-p
->fraggedself
,0),-16,10+16);
}
}
else
{
if (u
&16384)
{
if (u
!= -1) G_PatchStatusBar
(275,SBY
+18,299,SBY
+18+12);
if (p
->got_access
&4) rotatesprite_fs
(sbarx
(275),sbary
(SBY
+16),sb16
,0,ACCESS_ICON
,0,23,10+16);
if (p
->got_access
&2) rotatesprite_fs
(sbarx
(288),sbary
(SBY
+16),sb16
,0,ACCESS_ICON
,0,21,10+16);
if (p
->got_access
&1) rotatesprite_fs
(sbarx
(281),sbary
(SBY
+23),sb16
,0,ACCESS_ICON
,0,0,10+16);
}
}
if (u
&(4+8+16+32+64+128+256+512+65536L))
G_DrawWeapAmounts
(p
,96,SBY
+16,u
);
if (u
&1)
{
if (u
!= -1) G_PatchStatusBar
(20,SBY
+17,43,SBY
+17+11);
if (sprite
[p
->i
].
pal == 1 && p
->last_extra
< 2)
G_DrawDigiNum
(32,SBY
+17,1,-16,10+16);
else G_DrawDigiNum
(32,SBY
+17,p
->last_extra
,-16,10+16);
}
if (u
&2)
{
int32_t lAmount
= G_GetMorale
(p
->i
, snum
);
if (u
!= -1)
G_PatchStatusBar
(52,SBY
+17,75,SBY
+17+11);
if (lAmount
== -1)
G_DrawDigiNum
(64,SBY
+17,p
->inv_amount
[GET_SHIELD
],-16,10+16);
else
G_DrawDigiNum
(64,SBY
+17,lAmount
,-16,10+16);
}
if (u
&1024)
{
if (u
!= -1) G_PatchStatusBar
(196,SBY
+17,219,SBY
+17+11);
if (PWEAPON
(snum
, p
->curr_weapon
, WorksLike
) != KNEE_WEAPON
)
{
if (PWEAPON
(snum
, p
->curr_weapon
, WorksLike
) == HANDREMOTE_WEAPON
) i
= HANDBOMB_WEAPON
;
else i
= p
->curr_weapon
;
G_DrawDigiNum
(230-22,SBY
+17,p
->ammo_amount
[i
],-16,10+16);
}
}
if (u
&(2048+4096+8192))
{
if (u
!= -1)
{
if (u
&(2048+4096))
G_PatchStatusBar
(231,SBY
+13,265,SBY
+13+18);
else
G_PatchStatusBar
(250,SBY
+24,261,SBY
+24+6);
}
if (p
->inven_icon
)
{
o
= 0;
// permbit = 128;
if (u
&(2048+4096))
{
i
= ((unsigned)p
->inven_icon
< ICON_MAX
) ? item_icons
[p
->inven_icon
] : -1;
// XXX: i < 0?
rotatesprite_fs
(sbarx
(231-o
),sbary
(SBY
+13),sb16
,0,i
,0,0,10+16+permbit
);
minitext
(292-30-o
,SBY
+24,"%",6,10+16+permbit
+ ROTATESPRITE_MAX
);
if (p
->inven_icon
>= ICON_SCUBA
) minitext
(284-35-o
,SBY
+14,"Auto",2,10+16+permbit
+ ROTATESPRITE_MAX
);
}
if (u
&(2048+4096))
{
j
= G_GetInvOn
(p
);
if (j
> 0) minitext
(288-30-o
,SBY
+14,"On",0,10+16+permbit
+ ROTATESPRITE_MAX
);
else if ((uint32_t)j
!= 0x80000000) minitext
(284-30-o
,SBY
+14,"Off",2,10+16+permbit
+ ROTATESPRITE_MAX
);
}
if (u
&8192)
{
i
= G_GetInvAmount
(p
);
G_DrawInvNum
(284-30-o
,0,SBY
+28,(uint8_t)i
,0,10+permbit
);
}
}
}
}
#define COLOR_RED 248
#define COLOR_WHITE 31
#define LOW_FPS 30
#if defined GEKKO
# define FPS_YOFFSET 16
#else
# define FPS_YOFFSET 0
#endif
static void G_PrintFPS
(void)
{
// adapted from ZDoom because I like it better than what we had
// applicable ZDoom code available under GPL from csDoom
static int32_t FrameCount
= 0, LastCount
= 0, LastSec
= 0, LastMS
= 0;
static int32_t MinFrames
= INT32_MAX
, MaxFrames
= 0;
int32_t ms
= getticks
();
int32_t howlong
= ms
- LastMS
;
if (g_player
[0].
ps->player_par
< 2)
{
MinFrames
= INT32_MAX
;
MaxFrames
= 0;
}
if (howlong
>= 0)
{
int32_t thisSec
= ms
/1000;
int32_t x
= (xdim
<= 640);
if (ud.
tickrate)
{
int32_t chars
= Bsprintf
(tempbuf
, "%d ms (%3u fps)", howlong
, LastCount
);
printext256
(windowx2
-(chars
<<(3-x
))+1,windowy1
+2+FPS_YOFFSET
,0,-1,tempbuf
,x
);
printext256
(windowx2
-(chars
<<(3-x
)),windowy1
+1+FPS_YOFFSET
,
(LastCount
< LOW_FPS
) ? COLOR_RED
: COLOR_WHITE
,-1,tempbuf
,x
);
if (ud.
tickrate > 1)
{
chars
= Bsprintf
(tempbuf
, "max fps: %3u", MaxFrames
);
printext256
(windowx2
-(chars
<<(3-x
))+1, windowy1
+10+2+FPS_YOFFSET
, 0, -1, tempbuf
, x
);
printext256
(windowx2
-(chars
<<(3-x
)), windowy1
+10+FPS_YOFFSET
,
(MaxFrames
< LOW_FPS
) ? COLOR_RED
: COLOR_WHITE
, -1, tempbuf
, x
);
chars
= Bsprintf
(tempbuf
, "min fps: %3u", MinFrames
);
printext256
(windowx2
-(chars
<<(3-x
))+1, windowy1
+20+2+FPS_YOFFSET
, 0, -1, tempbuf
, x
);
printext256
(windowx2
-(chars
<<(3-x
)), windowy1
+20+FPS_YOFFSET
,
(MinFrames
< LOW_FPS
) ? COLOR_RED
: COLOR_WHITE
, -1, tempbuf
, x
);
}
// lag meter
if (g_netClientPeer
)
{
chars
= Bsprintf
(tempbuf
, "%d +- %d ms", (g_netClientPeer
->lastRoundTripTime
+ g_netClientPeer
->roundTripTime
)/2,
(g_netClientPeer
->lastRoundTripTimeVariance
+ g_netClientPeer
->roundTripTimeVariance
)/2);
printext256
(windowx2
-(chars
<<(3-x
))+1,windowy1
+30+2+FPS_YOFFSET
,0,-1,tempbuf
,x
);
printext256
(windowx2
-(chars
<<(3-x
)),windowy1
+30+1+FPS_YOFFSET
,g_netClientPeer
->lastRoundTripTime
> 200 ? COLOR_RED
: COLOR_WHITE
,-1,tempbuf
,x
);
}
}
if (thisSec
- LastSec
)
{
g_currentFrameRate
= LastCount
= tabledivide32_noinline
(FrameCount
, thisSec
- LastSec
);
LastSec
= thisSec
;
FrameCount
= 0;
if (!osdshown
)
{
if (LastCount
> MaxFrames
) MaxFrames
= LastCount
;
if (LastCount
< MinFrames
) MinFrames
= LastCount
;
}
}
FrameCount
++;
}
LastMS
= ms
;
}
// yxaspect and viewingrange just before the 'main' drawrooms call
static int32_t dr_yxaspect
, dr_viewingrange
;
#ifdef DEBUGGINGAIDS
static struct {
uint32_t lastgtic
;
uint32_t lastnumins
, numins
;
int32_t numonscreen
;
} g_spriteStat
;
#endif
#define printcoordsline(fmt, ...) do { \
Bsprintf(tempbuf, fmt, ## __VA_ARGS__); \
printext256(20, y+=9, 31, -1, tempbuf, 0); \
} while (0)
static void G_PrintCoords
(int32_t snum
)
{
const int32_t x
= 250;
int32_t y
= 16;
const DukePlayer_t
*ps
= g_player
[snum
].
ps;
const int32_t sectnum
= ps
->cursectnum
;
if ((GametypeFlags
[ud.
coop] & GAMETYPE_FRAGBAR
))
{
if (ud.
multimode > 4)
y
= 32;
else if (g_netServer
|| ud.
multimode > 1)
y
= 24;
}
Bsprintf
(tempbuf
,"XYZ= (%d,%d,%d)",ps
->pos.
x,ps
->pos.
y,ps
->pos.
z);
printext256
(x
,y
,31,-1,tempbuf
,0);
Bsprintf
(tempbuf
,"A/H/HO= %d,%d,%d",ps
->ang
,ps
->horiz
,ps
->horizoff
);
printext256
(x
,y
+9,31,-1,tempbuf
,0);
Bsprintf
(tempbuf
,"ZV= %d",ps
->vel.
z);
printext256
(x
,y
+18,31,-1,tempbuf
,0);
Bsprintf
(tempbuf
,"OG= %d SBRIDGE=%d SBS=%d",ps
->on_ground
, ps
->spritebridge
, ps
->sbs
);
printext256
(x
,y
+27,31,-1,tempbuf
,0);
if (sectnum
>= 0)
Bsprintf
(tempbuf
,"SECT= %d (LO=%d EX=%d)",sectnum
,TrackerCast
(sector
[sectnum
].
lotag),TrackerCast
(sector
[sectnum
].
extra));
else
Bsprintf
(tempbuf
,"SECT= %d", sectnum
);
printext256
(x
,y
+36,31,-1,tempbuf
,0);
// Bsprintf(tempbuf,"SEED= %d",randomseed);
// printext256(x,y+45,31,-1,tempbuf,0);
y
-= 9;
y
+= 7;
Bsprintf
(tempbuf
,"THOLD= %d", ps
->transporter_hold
);
printext256
(x
,y
+54,31,-1,tempbuf
,0);
Bsprintf
(tempbuf
,"GAMETIC= %d, TOTALCLOCK=%d", g_moveThingsCount
, totalclock
);
printext256
(x
,y
+63,31,-1,tempbuf
,0);
#ifdef DEBUGGINGAIDS
Bsprintf
(tempbuf
,"NUMSPRITES= %d", Numsprites
);
printext256
(x
,y
+72,31,-1,tempbuf
,0);
if (g_moveThingsCount
> g_spriteStat.
lastgtic + REALGAMETICSPERSEC
)
{
g_spriteStat.
lastgtic = g_moveThingsCount
;
g_spriteStat.
lastnumins = g_spriteStat.
numins;
g_spriteStat.
numins = 0;
}
Bsprintf
(tempbuf
,"INSERTIONS/s= %u", g_spriteStat.
lastnumins);
printext256
(x
,y
+81,31,-1,tempbuf
,0);
Bsprintf
(tempbuf
,"ONSCREEN= %d", g_spriteStat.
numonscreen);
printext256
(x
,y
+90,31,-1,tempbuf
,0);
y
+= 3*9;
#endif
y
+= 7;
Bsprintf
(tempbuf
,"VR=%.03f YX=%.03f",(double)dr_viewingrange
/65536.0,(double)dr_yxaspect
/65536.0);
printext256
(x
,y
+72,31,-1,tempbuf
,0);
Bsprintf
(tempbuf
, "MOVEACTORS [ms]= %.3e", g_moveActorsTime
);
printext256
(x
,y
+81,31,-1,tempbuf
,0);
#ifdef USE_OPENGL
if (ud.
coords == 2)
{
y
=16;
printcoordsline
("rendmode = %d", getrendermode
());
printcoordsline
("r_ambientlight = %.03f", r_ambientlight
);
if (rendmode
>= 3)
{
if (rendmode
==3)
printcoordsline
("r_usenewshading = %d", r_usenewshading
);
# ifdef POLYMER
else
printcoordsline
("r_pr_artmapping = %d", pr_artmapping
);
#endif
printcoordsline
("r_usetileshades = %d", r_usetileshades
);
}
}
#endif
}
// orientation flags depending on time that a quote has still to be displayed
static inline int32_t texto
(int32_t t
)
{
if (t
> 4) return 2+8+16;
if (t
> 2) return 2+8+16+1;
return 2+8+16+1+32;
}
static inline int32_t texta
(int32_t t
)
{
if (getrendermode
() == REND_CLASSIC
)
{
if (t
> 4) return 0;
if (t
> 2) return 85;
return 169;
}
t
= clamp
(t
<<3, 0, 255);
return 255 - t
;
}
static int32_t calc_ybase
(int32_t begy
)
{
int32_t k
= begy
;
if (GTFLAGS
(GAMETYPE_FRAGBAR
) && ud.
screen_size > 0
#ifdef SPLITSCREEN_MOD_HACKS
&& !g_fakeMultiMode
#endif
&& (g_netServer
|| ud.
multimode > 1))
{
int32_t i
, j
= 0;
k
+= 8;
for (TRAVERSE_CONNECT
(i
))
if (i
> j
)
j
= i
;
if (j
>= 4 && j
<= 8) k
+= 8;
else if (j
> 8 && j
<= 12) k
+= 16;
else if (j
> 12) k
+= 24;
}
return k
;
}
// this handles both multiplayer and item pickup message type text
// both are passed on to gametext
void G_PrintGameQuotes
(int32_t snum
)
{
int32_t i
, j
, k
;
const DukePlayer_t
*const ps
= g_player
[snum
].
ps;
const int32_t reserved_quote
= (ps
->ftq
>= QUOTE_RESERVED
&& ps
->ftq
<= QUOTE_RESERVED3
);
// NOTE: QUOTE_RESERVED4 is not included.
k
= calc_ybase
(1);
if (ps
->fta
> 1 && !reserved_quote
)
{
k
+= min
(7, ps
->fta
);
}
j
= scale
(k
, ydim
, 200);
for (i
=MAXUSERQUOTES
-1; i
>=0; i
--)
{
int32_t sh
, l
;
if (user_quote_time
[i
] <= 0)
continue;
k
= user_quote_time
[i
];
sh
= hud_glowingquotes
? (sintable
[((totalclock
+(i
<<2))<<5)&2047]>>11) : 0;
mpgametext
(j
, user_quote
[i
], sh
, texto
(k
));
j
+= textsc
(k
> 4 ? 8 : (k
<<1));
l
= G_GameTextLen
(USERQUOTE_LEFTOFFSET
, OSD_StripColors
(tempbuf
,user_quote
[i
]));
while (l
> (ud.
config.
ScreenWidth - USERQUOTE_RIGHTOFFSET
))
{
l
-= (ud.
config.
ScreenWidth-USERQUOTE_RIGHTOFFSET
);
j
+= textsc
(k
> 4 ? 8 : (k
<<1));
}
}
if (klabs
(quotebotgoal
-quotebot
) <= 16 && ud.
screen_size <= 8)
quotebot
+= ksgn
(quotebotgoal
-quotebot
);
else
quotebot
= quotebotgoal
;
if (ps
->fta
<= 1)
return;
if (EDUKE32_PREDICT_FALSE
(ScriptQuotes
[ps
->ftq
] == NULL
))
{
OSD_Printf
(OSD_ERROR
"%s %d null quote %d\n",__FILE__
,__LINE__
,ps
->ftq
);
return;
}
k
= calc_ybase
(0);
if (k
== 0)
{
if (reserved_quote
)
{
#ifdef SPLITSCREEN_MOD_HACKS
if (!g_fakeMultiMode
)
k
= 140; // quotebot-8-4;
else
k
= 50;
#else
k
= 140;
#endif
}
else
{
if (ud.
althud == 2)
k
= 32;
else
#ifdef GEKKO
k
= 16;
#elif defined EDUKE32_TOUCH_DEVICES
k
= 24;
#else
k
= 1;
#endif
}
}
int32_t pal
= 0;
#ifdef SPLITSCREEN_MOD_HACKS
if (g_fakeMultiMode
)
{
pal
= g_player
[snum
].
pcolor;
if (snum
== 1)
{
const int32_t sidebyside
= (ud.
screen_size != 0);
// NOTE: setting gametext's x -= 80 doesn't do the expected thing.
// Needs looking into.
if (sidebyside
)
k
+= 9;
else
k
+= 101;
}
}
#endif
gametextpalbits
(160, k
, ScriptQuotes
[ps
->ftq
], ftapulseshade
, pal
, 2 + 8 + 16, texta
(ps
->fta
));
}
void P_DoQuote
(int32_t q
, DukePlayer_t
*p
)
{
int32_t cq
= 0;
if (ud.
fta_on == 0 || q
< 0)
return;
if (q
& MAXQUOTES
)
{
cq
= 1;
q
&= ~MAXQUOTES
;
}
if (EDUKE32_PREDICT_FALSE
(ScriptQuotes
[q
] == NULL
))
{
OSD_Printf
(OSD_ERROR
"%s %d null quote %d\n",__FILE__
,__LINE__
,q
);
return;
}
if (p
->fta
> 0 && q
!= QUOTE_RESERVED
&& q
!= QUOTE_RESERVED2
)
if (p
->ftq
== QUOTE_RESERVED
|| p
->ftq
== QUOTE_RESERVED2
) return;
p
->fta
= 100;
if (p
->ftq
!= q
)
{
if (p
== g_player
[screenpeek
].
ps
&& Bstrcmp
(ScriptQuotes
[q
],"")) // avoid printing blank quotes
{
if (cq
) OSD_Printf
(OSDTEXT_BLUE
"%s\n",ScriptQuotes
[q
]);
else OSD_Printf
("%s\n",ScriptQuotes
[q
]);
}
p
->ftq
= q
;
}
pub
= NUMPAGES
;
pus
= NUMPAGES
;
}
////////// OFTEN-USED FEW-LINERS //////////
static void G_HandleEventsWhileNoInput
(void)
{
I_ClearAllInput
();
while (!I_CheckAllInput
())
G_HandleAsync
();
I_ClearAllInput
();
}
static int32_t G_PlaySoundWhileNoInput
(int32_t soundnum
)
{
S_PlaySound
(soundnum
);
I_ClearAllInput
();
while (S_CheckSoundPlaying
(-1, soundnum
))
{
G_HandleAsync
();
if (I_CheckAllInput
())
{
I_ClearAllInput
();
return 1;
}
}
return 0;
}
//////////
void G_FadePalette
(int32_t r
,int32_t g
,int32_t b
,int32_t e
)
{
setpalettefade
(r
,g
,b
,e
&63);
if ((e
&128) == 0)
{
int32_t tc
;
nextpage
();
tc
= totalclock
;
while (totalclock
< tc
+ 4)
G_HandleAsync
();
}
}
// START and END limits are always inclusive!
// STEP must evenly divide END-START, i.e. abs(end-start)%step == 0
void fadepal
(int32_t r
, int32_t g
, int32_t b
, int32_t start
, int32_t end
, int32_t step
)
{
if (getrendermode
() >= REND_POLYMOST
)
{
G_FadePalette
(r
, g
, b
, end
);
return;
}
// (end-start)/step + 1 iterations
do
{
if (KB_KeyPressed
(sc_Space
))
{
KB_ClearKeyDown
(sc_Space
);
setpalettefade
(r
,g
,b
,end
); // have to set to end fade value if we break!
return;
}
G_FadePalette
(r
,g
,b
,start
);
start
+= step
;
}
while (start
!= end
+step
);
}
// START and END limits are always inclusive!
static void fadepaltile
(int32_t r
, int32_t g
, int32_t b
, int32_t start
, int32_t end
, int32_t step
, int32_t tile
)
{
// STEP must evenly divide END-START
Bassert
(klabs
(end
-start
)%step
== 0);
clearallviews
(0);
// (end-start)/step + 1 iterations
do
{
#ifdef __ANDROID__ //Needed for N7 2013 to stop corruption while fading video
clearview
(0);
#endif
if (KB_KeyPressed
(sc_Space
))
{
KB_ClearKeyDown
(sc_Space
);
setpalettefade
(r
,g
,b
,end
); // have to set to end fade value if we break!
return;
}
rotatesprite_fs
(160<<16,100<<16,65536L,0,tile
,0,0,2+8+BGSTRETCH
);
G_FadePalette
(r
,g
,b
,start
);
start
+= step
;
}
while (start
!= end
+step
);
}
#ifdef LUNATIC
int32_t g_logoFlags
= 255;
#endif
#ifdef __ANDROID__
int inExtraScreens
= 0;
#endif
static void G_DisplayExtraScreens
(void)
{
int32_t flags
= G_GetLogoFlags
();
S_StopMusic
();
FX_StopAllSounds
();
if (!DUKEBETA
&& (!VOLUMEALL
|| flags
& LOGO_SHAREWARESCREENS
))
{
#ifdef __ANDROID__
inExtraScreens
= 1;
#endif
setview
(0,0,xdim
-1,ydim
-1);
flushperms
();
//g_player[myconnectindex].ps->palette = palette;
P_SetGamePalette
(g_player
[myconnectindex
].
ps, BASEPAL
, 1); // JBF 20040308
fadepal
(0,0,0, 0,63,7);
I_ClearAllInput
();
rotatesprite_fs
(160<<16,100<<16,65536L,0,3291,0,0,2+8+64+BGSTRETCH
);
fadepaltile
(0,0,0, 63,0,-7, 3291);
while (!I_CheckAllInput
())
G_HandleAsync
();
fadepaltile
(0,0,0, 0,63,7, 3291);
I_ClearAllInput
();
rotatesprite_fs
(160<<16,100<<16,65536L,0,3290,0,0,2+8+64+BGSTRETCH
);
fadepaltile
(0,0,0, 63,0,-7,3290);
while (!I_CheckAllInput
())
G_HandleAsync
();
#ifdef __ANDROID__
inExtraScreens
= 0;
#endif
}
if (flags
& LOGO_TENSCREEN
)
{
#ifdef __ANDROID__
inExtraScreens
= 1;
#endif
setview
(0,0,xdim
-1,ydim
-1);
flushperms
();
//g_player[myconnectindex].ps->palette = palette;
P_SetGamePalette
(g_player
[myconnectindex
].
ps, BASEPAL
, 1); // JBF 20040308
fadepal
(0,0,0, 0,63,7);
I_ClearAllInput
();
totalclock
= 0;
rotatesprite_fs
(160<<16,100<<16,65536L,0,TENSCREEN
,0,0,2+8+64+BGSTRETCH
);
fadepaltile
(0,0,0, 63,0,-7,TENSCREEN
);
while (!I_CheckAllInput
() && totalclock
< 2400)
G_HandleAsync
();
fadepaltile
(0,0,0, 0,63,7, TENSCREEN
);
I_ClearAllInput
();
#ifdef __ANDROID__
inExtraScreens
= 0;
#endif
}
}
extern int32_t g_doQuickSave
;
void G_GameExit
(const char *msg
)
{
#ifdef LUNATIC
El_PrintTimes
();
El_DestroyState
(&g_ElState
);
#endif
if (*msg
!= 0) g_player
[myconnectindex
].
ps->palette
= BASEPAL
;
if (ud.
recstat == 1)
G_CloseDemoWrite
();
else if (ud.
recstat == 2)
MAYBE_FCLOSE_AND_NULL
(g_demo_filePtr
);
// JBF: fixes crash on demo playback
// PK: modified from original
if (!g_quickExit
)
{
if (playerswhenstarted
> 1 && g_player
[myconnectindex
].
ps->gm
&MODE_GAME
&& GTFLAGS
(GAMETYPE_SCORESHEET
) && *msg
== ' ')
{
G_BonusScreen
(1);
setgamemode
(ud.
config.
ScreenMode,ud.
config.
ScreenWidth,ud.
config.
ScreenHeight,ud.
config.
ScreenBPP);
}
if (*msg
!= 0 && *(msg
+1) != 'V' && *(msg
+1) != 'Y')
G_DisplayExtraScreens
();
}
if (*msg
!= 0) initprintf
("%s\n",msg
);
if (in3dmode
())
G_Shutdown
();
if (*msg
!= 0)
{
if (!(msg
[0] == ' ' && msg
[1] == 0))
{
char titlebuf
[256];
Bsprintf
(titlebuf
,HEAD2
" %s",s_buildRev
);
wm_msgbox
(titlebuf
, "%s", msg
);
}
}
Bfflush
(NULL
);
exit(0);
}
static inline void G_MoveClouds
(void)
{
int32_t i
;
if (totalclock
<= cloudtotalclock
&& totalclock
>= (cloudtotalclock
-7))
return;
cloudtotalclock
= totalclock
+6;
for (i
=g_numClouds
-1; i
>=0; i
--)
{
cloudx
[i
] += (sintable
[(g_player
[screenpeek
].
ps->ang
+512)&2047]>>9);
cloudy
[i
] += (sintable
[g_player
[screenpeek
].
ps->ang
&2047]>>9);
sector
[clouds
[i
]].
ceilingxpanning = cloudx
[i
]>>6;
sector
[clouds
[i
]].
ceilingypanning = cloudy
[i
]>>6;
}
}
static void G_DrawOverheadMap
(int32_t cposx
, int32_t cposy
, int32_t czoom
, int16_t cang
)
{
int32_t i
, j
, k
, l
, x1
, y1
, x2
=0, y2
=0, x3
, y3
, x4
, y4
, ox
, oy
, xoff
, yoff
;
int32_t dax
, day
, cosang
, sinang
, xspan
, yspan
, sprx
, spry
;
int32_t xrepeat
, yrepeat
, z1
, z2
, startwall
, endwall
, tilenum
, daang
;
int32_t xvect
, yvect
, xvect2
, yvect2
;
int16_t p
;
char col
;
walltype
*wal
, *wal2
;
spritetype
*spr
;
int32_t tmpydim
= (xdim
*5)/8;
setaspect
(65536, divscale16
(tmpydim
*320, xdim
*200));
xvect
= sintable
[(-cang
)&2047] * czoom
;
yvect
= sintable
[(1536-cang
)&2047] * czoom
;
xvect2
= mulscale16
(xvect
,yxaspect
);
yvect2
= mulscale16
(yvect
,yxaspect
);
push_nofog
();
//Draw red lines
for (i
=numsectors
-1; i
>=0; i
--)
{
if (!(show2dsector
[i
>>3]&(1<<(i
&7)))) continue;
startwall
= sector
[i
].
wallptr;
endwall
= sector
[i
].
wallptr + sector
[i
].
wallnum;
z1
= sector
[i
].
ceilingz;
z2
= sector
[i
].
floorz;
for (j
=startwall
,wal
=&wall
[startwall
]; j
<endwall
; j
++,wal
++)
{
k
= wal
->nextwall
;
if (k
< 0) continue;
if (sector
[wal
->nextsector
].
ceilingz == z1
)
if (sector
[wal
->nextsector
].
floorz == z2
)
if (((wal
->cstat
|wall
[wal
->nextwall
].
cstat)&(16+32)) == 0) continue;
col
= 139; //red
if ((wal
->cstat
|wall
[wal
->nextwall
].
cstat)&1) col
= 234; //magenta
if (!(show2dsector
[wal
->nextsector
>>3]&(1<<(wal
->nextsector
&7))))
col
= 24;
else continue;
ox
= wal
->x
-cposx
;
oy
= wal
->y
-cposy
;
x1
= dmulscale16
(ox
,xvect
,-oy
,yvect
)+(xdim
<<11);
y1
= dmulscale16
(oy
,xvect2
,ox
,yvect2
)+(ydim
<<11);
wal2
= &wall
[wal
->point2
];
ox
= wal2
->x
-cposx
;
oy
= wal2
->y
-cposy
;
x2
= dmulscale16
(ox
,xvect
,-oy
,yvect
)+(xdim
<<11);
y2
= dmulscale16
(oy
,xvect2
,ox
,yvect2
)+(ydim
<<11);
drawline256
(x1
,y1
,x2
,y2
,col
);
}
}
pop_nofog
();
//Draw sprites
k
= g_player
[screenpeek
].
ps->i
;
for (i
=numsectors
-1; i
>=0; i
--)
{
if (!(show2dsector
[i
>>3]&(1<<(i
&7)))) continue;
for (j
=headspritesect
[i
]; j
>=0; j
=nextspritesect
[j
])
{
spr
= &sprite
[j
];
if (j
== k
|| (spr
->cstat
&0x8000) || spr
->cstat
== 257 || spr
->xrepeat
== 0) continue;
col
= 71; //cyan
if (spr
->cstat
&1) col
= 234; //magenta
sprx
= spr
->x
;
spry
= spr
->y
;
if ((spr
->cstat
&257) != 0) switch (spr
->cstat
&48)
{
case 0:
// break;
ox
= sprx
-cposx
;
oy
= spry
-cposy
;
x1
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y1
= dmulscale16
(oy
,xvect2
,ox
,yvect2
);
ox
= (sintable
[(spr
->ang
+512)&2047]>>7);
oy
= (sintable
[(spr
->ang
)&2047]>>7);
x2
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y2
= dmulscale16
(oy
,xvect
,ox
,yvect
);
x3
= mulscale16
(x2
,yxaspect
);
y3
= mulscale16
(y2
,yxaspect
);
drawline256
(x1
-x2
+(xdim
<<11),y1
-y3
+(ydim
<<11),
x1
+x2
+(xdim
<<11),y1
+y3
+(ydim
<<11),col
);
drawline256
(x1
-y2
+(xdim
<<11),y1
+x3
+(ydim
<<11),
x1
+x2
+(xdim
<<11),y1
+y3
+(ydim
<<11),col
);
drawline256
(x1
+y2
+(xdim
<<11),y1
-x3
+(ydim
<<11),
x1
+x2
+(xdim
<<11),y1
+y3
+(ydim
<<11),col
);
break;
case 16:
if (spr
->picnum
== LASERLINE
)
{
x1
= sprx
;
y1
= spry
;
tilenum
= spr
->picnum
;
xoff
= picanm
[tilenum
].
xofs + spr
->xoffset
;
if ((spr
->cstat
&4) > 0) xoff
= -xoff
;
k
= spr
->ang
;
l
= spr
->xrepeat
;
dax
= sintable
[k
&2047]*l
;
day
= sintable
[(k
+1536)&2047]*l
;
l
= tilesiz
[tilenum
].
x;
k
= (l
>>1)+xoff
;
x1
-= mulscale16
(dax
,k
);
x2
= x1
+mulscale16
(dax
,l
);
y1
-= mulscale16
(day
,k
);
y2
= y1
+mulscale16
(day
,l
);
ox
= x1
-cposx
;
oy
= y1
-cposy
;
x1
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y1
= dmulscale16
(oy
,xvect2
,ox
,yvect2
);
ox
= x2
-cposx
;
oy
= y2
-cposy
;
x2
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y2
= dmulscale16
(oy
,xvect2
,ox
,yvect2
);
drawline256
(x1
+(xdim
<<11),y1
+(ydim
<<11),
x2
+(xdim
<<11),y2
+(ydim
<<11),col
);
}
break;
case 32:
tilenum
= spr
->picnum
;
xoff
= picanm
[tilenum
].
xofs + spr
->xoffset
;
yoff
= picanm
[tilenum
].
yofs + spr
->yoffset
;
if ((spr
->cstat
&4) > 0) xoff
= -xoff
;
if ((spr
->cstat
&8) > 0) yoff
= -yoff
;
k
= spr
->ang
;
cosang
= sintable
[(k
+512)&2047];
sinang
= sintable
[k
&2047];
xspan
= tilesiz
[tilenum
].
x;
xrepeat
= spr
->xrepeat
;
yspan
= tilesiz
[tilenum
].
y;
yrepeat
= spr
->yrepeat
;
dax
= ((xspan
>>1)+xoff
)*xrepeat
;
day
= ((yspan
>>1)+yoff
)*yrepeat
;
x1
= sprx
+ dmulscale16
(sinang
,dax
,cosang
,day
);
y1
= spry
+ dmulscale16
(sinang
,day
,-cosang
,dax
);
l
= xspan
*xrepeat
;
x2
= x1
- mulscale16
(sinang
,l
);
y2
= y1
+ mulscale16
(cosang
,l
);
l
= yspan
*yrepeat
;
k
= -mulscale16
(cosang
,l
);
x3
= x2
+k
;
x4
= x1
+k
;
k
= -mulscale16
(sinang
,l
);
y3
= y2
+k
;
y4
= y1
+k
;
ox
= x1
-cposx
;
oy
= y1
-cposy
;
x1
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y1
= dmulscale16
(oy
,xvect2
,ox
,yvect2
);
ox
= x2
-cposx
;
oy
= y2
-cposy
;
x2
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y2
= dmulscale16
(oy
,xvect2
,ox
,yvect2
);
ox
= x3
-cposx
;
oy
= y3
-cposy
;
x3
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y3
= dmulscale16
(oy
,xvect2
,ox
,yvect2
);
ox
= x4
-cposx
;
oy
= y4
-cposy
;
x4
= dmulscale16
(ox
,xvect
,-oy
,yvect
);
y4
= dmulscale16
(oy
,xvect2
,ox
,yvect2
);
drawline256
(x1
+(xdim
<<11),y1
+(ydim
<<11),
x2
+(xdim
<<11),y2
+(ydim
<<11),col
);
drawline256
(x2
+(xdim
<<11),y2
+(ydim
<<11),
x3
+(xdim
<<11),y3
+(ydim
<<11),col
);
drawline256
(x3
+(xdim
<<11),y3
+(ydim
<<11),
x4
+(xdim
<<11),y4
+(ydim
<<11),col
);
drawline256
(x4
+(xdim
<<11),y4
+(ydim
<<11),
x1
+(xdim
<<11),y1
+(ydim
<<11),col
);
break;
}
}
}
push_nofog
();
//Draw white lines
for (i
=numsectors
-1; i
>=0; i
--)
{
if (!(show2dsector
[i
>>3]&(1<<(i
&7)))) continue;
startwall
= sector
[i
].
wallptr;
endwall
= sector
[i
].
wallptr + sector
[i
].
wallnum;
k
= -1;
for (j
=startwall
,wal
=&wall
[startwall
]; j
<endwall
; j
++,wal
++)
{
if (wal
->nextwall
>= 0) continue;
if (tilesiz
[wal
->picnum
].
x == 0) continue;
if (tilesiz
[wal
->picnum
].
y == 0) continue;
if (j
== k
)
{
x1
= x2
;
y1
= y2
;
}
else
{
ox
= wal
->x
-cposx
;
oy
= wal
->y
-cposy
;
x1
= dmulscale16
(ox
,xvect
,-oy
,yvect
)+(xdim
<<11);
y1
= dmulscale16
(oy
,xvect2
,ox
,yvect2
)+(ydim
<<11);
}
k
= wal
->point2
;
wal2
= &wall
[k
];
ox
= wal2
->x
-cposx
;
oy
= wal2
->y
-cposy
;
x2
= dmulscale16
(ox
,xvect
,-oy
,yvect
)+(xdim
<<11);
y2
= dmulscale16
(oy
,xvect2
,ox
,yvect2
)+(ydim
<<11);
drawline256
(x1
,y1
,x2
,y2
,24);
}
}
pop_nofog
();
setaspect_new
();
for (TRAVERSE_CONNECT
(p
))
{
if (ud.
scrollmode && p
== screenpeek
) continue;
ox
= sprite
[g_player
[p
].
ps->i
].
x-cposx
;
oy
= sprite
[g_player
[p
].
ps->i
].
y-cposy
;
daang
= (sprite
[g_player
[p
].
ps->i
].
ang-cang
)&2047;
if (p
== screenpeek
)
{
ox
= 0;
oy
= 0;
daang
= 0;
}
x1
= mulscale
(ox
,xvect
,16) - mulscale
(oy
,yvect
,16);
y1
= mulscale
(oy
,xvect2
,16) + mulscale
(ox
,yvect2
,16);
if (p
== screenpeek
|| GTFLAGS
(GAMETYPE_OTHERPLAYERSINMAP
))
{
if (sprite
[g_player
[p
].
ps->i
].
xvel > 16 && g_player
[p
].
ps->on_ground
)
i
= APLAYERTOP
+((totalclock
>>4)&3);
else
i
= APLAYERTOP
;
j
= klabs
(g_player
[p
].
ps->truefz
-g_player
[p
].
ps->pos.
z)>>8;
j
= mulscale
(czoom
*(sprite
[g_player
[p
].
ps->i
].
yrepeat+j
),yxaspect
,16);
if (j
< 22000) j
= 22000;
else if (j
> (65536<<1)) j
= (65536<<1);
rotatesprite_win
((x1
<<4)+(xdim
<<15),(y1
<<4)+(ydim
<<15),j
,daang
,i
,sprite
[g_player
[p
].
ps->i
].
shade,
(g_player
[p
].
ps->cursectnum
> -1)?sector
[g_player
[p
].
ps->cursectnum
].
floorpal:0, 0);
}
}
}
palette_t CrosshairColors
= { 255, 255, 255, 0 };
palette_t DefaultCrosshairColors
= { 0, 0, 0, 0 };
int32_t g_crosshairSum
= 0;
void G_GetCrosshairColor
(void)
{
// use the brightest color in the original 8-bit tile
int32_t bri
= 0, j
= 0, i
;
int32_t ii
;
char *ptr
= (char *)waloff
[CROSSHAIR
];
if (DefaultCrosshairColors.
f)
return;
if (waloff
[CROSSHAIR
] == 0)
{
loadtile
(CROSSHAIR
);
ptr
= (char *)waloff
[CROSSHAIR
];
}
ii
= tilesiz
[CROSSHAIR
].
x * tilesiz
[CROSSHAIR
].
y;
if (ii
<= 0) return;
do
{
if (*ptr
!= 255)
{
i
= curpalette
[(int32_t)*ptr
].
r+curpalette
[(int32_t)*ptr
].
g+curpalette
[(int32_t)*ptr
].
b;
if (i
> j
) { j
= i
; bri
= *ptr
; }
}
ptr
++;
}
while (--ii
);
Bmemcpy
(&CrosshairColors
, &curpalette
[bri
], sizeof(palette_t
));
Bmemcpy
(&DefaultCrosshairColors
, &curpalette
[bri
], sizeof(palette_t
));
DefaultCrosshairColors.
f = 1; // this flag signifies that the color has been detected
}
void G_SetCrosshairColor
(int32_t r
, int32_t g
, int32_t b
)
{
char *ptr
= (char *)waloff
[CROSSHAIR
];
int32_t i
, ii
;
if (DefaultCrosshairColors.
f == 0 || g_crosshairSum
== r
+(g
<<1)+(b
<<2)) return;
g_crosshairSum
= r
+(g
<<1)+(b
<<2);
CrosshairColors.
r = r
;
CrosshairColors.
g = g
;
CrosshairColors.
b = b
;
if (waloff
[CROSSHAIR
] == 0)
{
loadtile
(CROSSHAIR
);
ptr
= (char *)waloff
[CROSSHAIR
];
}
ii
= tilesiz
[CROSSHAIR
].
x * tilesiz
[CROSSHAIR
].
y;
if (ii
<= 0) return;
if (getrendermode
() == REND_CLASSIC
)
i
= getclosestcol
(CrosshairColors.
r>>2, CrosshairColors.
g>>2, CrosshairColors.
b>>2);
else i
= getclosestcol
(63, 63, 63); // use white in GL so we can tint it to the right color
do
{
if (*ptr
!= 255)
*ptr
= i
;
ptr
++;
}
while (--ii
);
makepalookup
(CROSSHAIR_PAL
, NULL
, CrosshairColors.
r>>2, CrosshairColors.
g>>2, CrosshairColors.
b>>2,1);
#ifdef USE_OPENGL
// XXX: this makes us also load all hightile textures tinted with the crosshair color!
Bmemcpy
(&hictinting
[CROSSHAIR_PAL
], &CrosshairColors
, sizeof(palette_t
));
hictinting
[CROSSHAIR_PAL
].
f = HICTINT_USEONART
| HICTINT_GRAYSCALE
;
#endif
invalidatetile
(CROSSHAIR
, -1, -1);
}
static inline int G_LastMapInfoIndex
(void)
{
Bassert
(ud.
last_level >= 1); // NOTE: last_level is 1-based
return ud.
volume_number*MAXLEVELS
+ ud.
last_level-1;
}
#define SCORESHEETOFFSET -20
static void G_ShowScores
(void)
{
int32_t t
, i
;
if (playerswhenstarted
> 1 && (GametypeFlags
[ud.
coop]&GAMETYPE_SCORESHEET
))
{
gametext
(160,SCORESHEETOFFSET
+58+2,"Multiplayer Totals",0,2+8+16);
gametext
(160,SCORESHEETOFFSET
+58+10,MapInfo
[G_LastMapInfoIndex
()].
name,0,2+8+16);
t
= 0;
minitext
(70,SCORESHEETOFFSET
+80,"Name",8,2+8+16+ROTATESPRITE_MAX
);
minitext
(170,SCORESHEETOFFSET
+80,"Frags",8,2+8+16+ROTATESPRITE_MAX
);
minitext
(200,SCORESHEETOFFSET
+80,"Deaths",8,2+8+16+ROTATESPRITE_MAX
);
minitext
(235,SCORESHEETOFFSET
+80,"Ping",8,2+8+16+ROTATESPRITE_MAX
);
for (i
=playerswhenstarted
-1; i
>=0; i
--)
{
if (!g_player
[i
].
playerquitflag)
continue;
minitext
(70,SCORESHEETOFFSET
+90+t
,g_player
[i
].
user_name,g_player
[i
].
ps->palookup
,2+8+16+ROTATESPRITE_MAX
);
Bsprintf
(tempbuf
,"%-4d",g_player
[i
].
ps->frag
);
minitext
(170,SCORESHEETOFFSET
+90+t
,tempbuf
,2,2+8+16+ROTATESPRITE_MAX
);
Bsprintf
(tempbuf
,"%-4d", g_player
[i
].
frags[i
] + g_player
[i
].
ps->fraggedself
);
minitext
(200,SCORESHEETOFFSET
+90+t
,tempbuf
,2,2+8+16+ROTATESPRITE_MAX
);
Bsprintf
(tempbuf
,"%-4d",g_player
[i
].
ping);
minitext
(235,SCORESHEETOFFSET
+90+t
,tempbuf
,2,2+8+16+ROTATESPRITE_MAX
);
t
+= 7;
}
}
}
#undef SCORESHEETOFFSET
#ifdef YAX_DEBUG
// ugh...
char m32_debugstr
[64][128];
int32_t m32_numdebuglines
=0;
static void M32_drawdebug
(void)
{
int i
, col
=getclosestcol
(63,63,63);
int x
=4, y
=8;
if (m32_numdebuglines
>0)
{
begindrawing
();
for (i
=0; i
<m32_numdebuglines
&& y
<ydim
-8; i
++, y
+=8)
printext256
(x
,y
,col
,0,m32_debugstr
[i
],xdim
>640?0:1);
enddrawing
();
}
m32_numdebuglines
=0;
}
#endif
////////// TINT ACCUMULATOR //////////
typedef struct {
int32_t r
,g
,b
;
// f: 0-63 scale
int32_t maxf
, sumf
;
} palaccum_t
;
#define PALACCUM_INITIALIZER { 0, 0, 0, 0, 0 }
/* For a picture frame F and n tints C_1, C_2, ... C_n weighted a_1, a_2,
* ... a_n (on a 0-1 scale), the faded frame is calculated as
*
* F_new := (1-max_i(a_i))*F + d*sum_i(a_i), where
*
* d := max_i(a_i)/sum_i(a_i).
*
* This means that
* 1) tint application is independent of their order.
* 2) going from n+1 to n tints is continuous when the leaving one has faded.
*
* But note that for more than one tint, the composite tint will in general
* change its hue as the ratio of the weights of the individual ones changes.
*/
static void palaccum_add
(palaccum_t
*pa
, const palette_t
*pal
, int32_t f
)
{
f
= clamp
(f
, 0, 63);
if (f
== 0)
return;
pa
->maxf
= max
(pa
->maxf
, f
);
pa
->sumf
+= f
;
pa
->r
+= f
*clamp
(pal
->r
, 0, 63);
pa
->g
+= f
*clamp
(pal
->g
, 0, 63);
pa
->b
+= f
*clamp
(pal
->b
, 0, 63);
}
static void G_FadePalaccum
(const palaccum_t
*pa
)
{
setpalettefade
(tabledivide32_noinline
(pa
->r
, pa
->sumf
),
tabledivide32_noinline
(pa
->g
, pa
->sumf
),
tabledivide32_noinline
(pa
->b
, pa
->sumf
), pa
->maxf
);
}
////////// DISPLAYREST //////////
void G_DisplayRest
(int32_t smoothratio
)
{
int32_t i
, j
;
palaccum_t tint
= PALACCUM_INITIALIZER
;
DukePlayer_t
*const pp
= g_player
[screenpeek
].
ps;
#ifdef SPLITSCREEN_MOD_HACKS
DukePlayer_t
*const pp2
= g_fakeMultiMode
==2 ? g_player
[1].
ps : NULL
;
#endif
int32_t cposx
, cposy
, cang
;
#ifdef USE_OPENGL
// this takes care of fullscreen tint for OpenGL
if (getrendermode
() >= REND_POLYMOST
)
{
if (pp
->palette
== WATERPAL
)
{
static const palette_t wp
= { 224, 192, 255, 0 };
Bmemcpy
(&hictinting
[MAXPALOOKUPS
-1], &wp
, sizeof(palette_t
));
}
else if (pp
->palette
== SLIMEPAL
)
{
static const palette_t sp
= { 208, 255, 192, 0 };
Bmemcpy
(&hictinting
[MAXPALOOKUPS
-1], &sp
, sizeof(palette_t
));
}
else
{
hictinting
[MAXPALOOKUPS
-1].
r = 255;
hictinting
[MAXPALOOKUPS
-1].
g = 255;
hictinting
[MAXPALOOKUPS
-1].
b = 255;
}
}
#endif // USE_OPENGL
palaccum_add
(&tint
, &pp
->pals
, pp
->pals.
f);
#ifdef SPLITSCREEN_MOD_HACKS
if (pp2
)
palaccum_add
(&tint
, &pp2
->pals
, pp2
->pals.
f);
#endif
{
static const palette_t loogiepal
= { 0, 63, 0, 0 };
palaccum_add
(&tint
, &loogiepal
, pp
->loogcnt
>>1);
#ifdef SPLITSCREEN_MOD_HACKS
if (pp2
)
palaccum_add
(&tint
, &loogiepal
, pp2
->loogcnt
>>1);
#endif
}
if (g_restorePalette
)
{
// reset a normal palette
static uint32_t omovethingscnt
;
if (g_restorePalette
< 2 || omovethingscnt
+1 == g_moveThingsCount
)
{
int32_t pal
= pp
->palette
;
#ifdef SPLITSCREEN_MOD_HACKS
const int32_t opal
= pal
;
if (pp2
) // splitscreen HACK: BASEPAL trumps all, then it's arbitrary.
pal
= min
(pal
, pp2
->palette
);
#endif
// g_restorePalette < 0: reset tinting, too (e.g. when loading new game)
P_SetGamePalette
(pp
, pal
, 2 + (g_restorePalette
>0)*16);
#ifdef SPLITSCREEN_MOD_HACKS
if (pp2
) // keep first player's pal as its member!
pp
->palette
= opal
;
#endif
g_restorePalette
= 0;
}
else
{
// delay setting the palette by one game tic
omovethingscnt
= g_moveThingsCount
;
}
}
if (ud.
show_help)
{
switch (ud.
show_help)
{
case 1:
rotatesprite_fs
(160<<16,100<<16,65536L,0,TEXTSTORY
,0,0,10+64);
break;
case 2:
rotatesprite_fs
(160<<16,100<<16,65536L,0,F1HELP
,0,0,10+64);
break;
}
if (I_ReturnTrigger
())
{
I_ReturnTriggerClear
();
ud.
show_help = 0;
if ((!g_netServer
&& ud.
multimode < 2) && ud.
recstat != 2)
{
ready2send
= 1;
totalclock
= ototalclock
;
}
G_UpdateScreenArea
();
}
return;
}
i
= pp
->cursectnum
;
if (i
> -1)
{
const walltype
*wal
= &wall
[sector
[i
].
wallptr];
show2dsector
[i
>>3] |= (1<<(i
&7));
for (j
=sector
[i
].
wallnum; j
>0; j
--,wal
++)
{
i
= wal
->nextsector
;
if (i
< 0) continue;
if (wal
->cstat
&0x0071) continue;
if (wall
[wal
->nextwall
].
cstat&0x0071) continue;
if (sector
[i
].
lotag == 32767) continue;
if (sector
[i
].
ceilingz >= sector
[i
].
floorz) continue;
show2dsector
[i
>>3] |= (1<<(i
&7));
}
}
if (ud.
camerasprite == -1)
{
if (ud.
overhead_on != 2)
{
if (pp
->newowner
>= 0)
G_DrawCameraText
(pp
->newowner
);
else
{
P_DisplayWeapon
(screenpeek
);
#ifdef SPLITSCREEN_MOD_HACKS
if (pp2
) // HACK
P_DisplayWeapon
(1);
#endif
if (pp
->over_shoulder_on
== 0)
P_DisplayScuba
(screenpeek
);
#ifdef SPLITSCREEN_MOD_HACKS
if (pp2
&& pp2
->over_shoulder_on
== 0) // HACK
P_DisplayScuba
(1);
#endif
}
G_MoveClouds
();
}
if (ud.
overhead_on > 0)
{
// smoothratio = min(max(smoothratio,0),65536);
smoothratio
= calc_smoothratio
(totalclock
, ototalclock
);
G_DoInterpolations
(smoothratio
);
if (ud.
scrollmode == 0)
{
if (pp
->newowner
== -1 && !ud.
pause_on)
{
cposx
= pp
->opos.
x + mulscale16
(pp
->pos.
x-pp
->opos.
x, smoothratio
);
cposy
= pp
->opos.
y + mulscale16
(pp
->pos.
y-pp
->opos.
y, smoothratio
);
cang
= pp
->oang
+ mulscale16
(((pp
->ang
+1024-pp
->oang
)&2047)-1024, smoothratio
);
}
else
{
cposx
= pp
->opos.
x;
cposy
= pp
->opos.
y;
cang
= pp
->oang
;
}
}
else
{
#ifdef __ANDROID__
CONTROL_Android_ScrollMap
(&ud.
fola,& ud.
folx,&ud.
foly,&pp
->zoom
);
#else
if (!ud.
pause_on)
{
ud.
fola += ud.
folavel>>3;
ud.
folx += (ud.
folfvel*sintable
[(512+2048-ud.
fola)&2047])>>14;
ud.
foly += (ud.
folfvel*sintable
[(512+1024-512-ud.
fola)&2047])>>14;
}
#endif
cposx
= ud.
folx;
cposy
= ud.
foly;
cang
= ud.
fola;
}
if (ud.
overhead_on == 2)
{
clearview
(0L);
drawmapview
(cposx
,cposy
,pp
->zoom
,cang
);
}
G_DrawOverheadMap
(cposx
,cposy
,pp
->zoom
,cang
);
G_RestoreInterpolations
();
if (ud.
overhead_on == 2)
{
const int32_t a
= (ud.
screen_size > 0) ? 147 : 179;
minitext
(5,a
+6,EpisodeNames
[ud.
volume_number],0,2+8+16+256);
minitext
(5,a
+6+6,MapInfo
[ud.
volume_number*MAXLEVELS
+ ud.
level_number].
name,0,2+8+16+256);
}
}
}
if (pp
->invdisptime
> 0) G_DrawInventory
(pp
);
if (VM_OnEvent
(EVENT_DISPLAYSBAR
, g_player
[screenpeek
].
ps->i
, screenpeek
) == 0)
G_DrawStatusBar
(screenpeek
);
#ifdef SPLITSCREEN_MOD_HACKS
// HACK
if (g_fakeMultiMode
==2)
{
G_DrawStatusBar
(1);
G_PrintGameQuotes
(1);
}
#endif
G_PrintGameQuotes
(screenpeek
);
if (ud.
show_level_text && hud_showmapname
&& g_levelTextTime
> 1)
{
int32_t bits
= 10+16;
if (g_levelTextTime
< 3)
bits
|= 1+32;
else if (g_levelTextTime
< 5)
bits
|= 1;
if (MapInfo
[(ud.
volume_number*MAXLEVELS
) + ud.
level_number].
name != NULL
)
{
if (currentboardfilename
[0] != 0 && ud.
volume_number == 0 && ud.
level_number == 7)
menutext_
(160,90+16+8,-g_levelTextTime
+22/*quotepulseshade*/,0,currentboardfilename
,bits
);
else menutext_
(160,90+16+8,-g_levelTextTime
+22/*quotepulseshade*/,0,MapInfo
[(ud.
volume_number*MAXLEVELS
) + ud.
level_number].
name,bits
);
}
}
if (I_EscapeTrigger
() && ud.
overhead_on == 0
&& ud.
show_help == 0
&& g_player
[myconnectindex
].
ps->newowner
== -1)
{
if ((g_player
[myconnectindex
].
ps->gm
&MODE_MENU
) == MODE_MENU
&& g_currentMenu
<= MENU_MAIN_INGAME
)
{
I_EscapeTriggerClear
();
S_PlaySound
(EXITMENUSOUND
);
M_ChangeMenu
(MENU_CLOSE
);
}
else if ((g_player
[myconnectindex
].
ps->gm
&MODE_MENU
) != MODE_MENU
&&
g_player
[myconnectindex
].
ps->newowner
== -1 &&
(g_player
[myconnectindex
].
ps->gm
&MODE_TYPE
) != MODE_TYPE
)
{
I_EscapeTriggerClear
();
FX_StopAllSounds
();
S_ClearSoundLocks
();
S_MenuSound
();
M_OpenMenu
(myconnectindex
);
if ((!g_netServer
&& ud.
multimode < 2) && ud.
recstat != 2) ready2send
= 0;
if (g_player
[myconnectindex
].
ps->gm
&MODE_GAME
) M_ChangeMenu
(MENU_MAIN_INGAME
);
else M_ChangeMenu
(MENU_MAIN
);
screenpeek
= myconnectindex
;
}
}
if (VM_HaveEvent
(EVENT_DISPLAYREST
))
{
int32_t vr
=viewingrange
, asp
=yxaspect
;
VM_OnEvent_
(EVENT_DISPLAYREST
, g_player
[screenpeek
].
ps->i
, screenpeek
);
setaspect
(vr
, asp
);
}
if (g_player
[myconnectindex
].
ps->newowner
== -1 && ud.
overhead_on == 0 && ud.
crosshair && ud.
camerasprite == -1)
{
int32_t a
= VM_OnEvent
(EVENT_DISPLAYCROSSHAIR
, g_player
[screenpeek
].
ps->i
, screenpeek
);
if ((unsigned)a
< MAXTILES
)
{
if (a
== 0)
a
= CROSSHAIR
;
vec2_t crosshairpos
= { 160<<16, 100<<16 };
rotatesprite_win
(crosshairpos.
x-(g_player
[myconnectindex
].
ps->look_ang
<<15),crosshairpos.
y,scale
(65536,ud.
crosshairscale,100),
0,a
,0,CROSSHAIR_PAL
,2+1);
#ifdef GEKKO
if ((g_player
[myconnectindex
].
ps->gm
&MODE_MENU
) == 0 && readmouseabsxy
(&crosshairpos
, &mouseabs
))
rotatesprite_win
(crosshairpos.
x,crosshairpos.
y,scale
(65536,ud.
crosshairscale,100),0,a
,0,CROSSHAIR_PAL
,2+1);
#endif
}
}
#if 0
if (GametypeFlags
[ud.
coop] & GAMETYPE_TDM
)
{
for (i
=0; i
<ud.
multimode; i
++)
{
if (g_player
[i
].
ps->team
== g_player
[myconnectindex
].
ps->team
)
{
j
= min
(max
((G_GetAngleDelta
(getangle
(g_player
[i
].
ps->pos.
x-g_player
[myconnectindex
].
ps->pos.
x,
g_player
[i
].
ps->pos.
y-g_player
[myconnectindex
].
ps->pos.
y),g_player
[myconnectindex
].
ps->ang
))>>1,-160),160);
rotatesprite_win
((160-j
)<<16,100L<<16,65536L,0,DUKEICON
,0,0,2+1);
}
}
}
#endif
if (ud.
pause_on==1 && (g_player
[myconnectindex
].
ps->gm
&MODE_MENU
) == 0)
menutext
(160,100,0,0,"Game Paused");
if (ud.
coords)
G_PrintCoords
(screenpeek
);
#ifdef YAX_DEBUG
M32_drawdebug
();
#endif
#ifdef USE_OPENGL
extern int32_t mdpause
;
mdpause
= (ud.
pause_on || (ud.
recstat==2 && (g_demo_paused
&& g_demo_goalCnt
==0)) || (g_player
[myconnectindex
].
ps->gm
&MODE_MENU
&& numplayers
< 2));
#endif
G_PrintFPS
();
// JBF 20040124: display level stats in screen corner
if (ud.
overhead_on != 2 && ud.
levelstats && VM_OnEvent
(EVENT_DISPLAYLEVELSTATS
, g_player
[myconnectindex
].
ps->i
, myconnectindex
) == 0)
{
DukePlayer_t
const * const myps
= g_player
[myconnectindex
].
ps;
i
= 198<<16;
if (ud.
screen_size == 4)
{
if (ud.
althud != 2)
i
-= sbarsc
(ud.
althud ? (tilesiz
[BIGALPHANUM
].
y+8)<<16 : tilesiz
[INVENTORYBOX
].
y<<16);
}
else if (ud.
screen_size > 2)
i
-= sbarsc
(tilesiz
[BOTTOMSTATUSBAR
].
y<<16);
Bsprintf
(tempbuf
,"T:^15%d:%02d.%02d",
(myps
->player_par
/(REALGAMETICSPERSEC
*60)),
(myps
->player_par
/REALGAMETICSPERSEC
)%60,
((myps
->player_par
%REALGAMETICSPERSEC
)*33)/10
);
G_ScreenText
(STARTALPHANUM
, 2<<16, i
-gtextsc
(21<<16), gtextsc
(65536), 0, 0, tempbuf
, 0, 10, 2|8|16|256|ROTATESPRITE_FULL16
, 0, 5<<16, 8<<16, 8<<16, 0, TEXT_XOFFSETZERO
|TEXT_GAMETEXTNUMHACK
, 0, 0, xdim
-1, ydim
-1);
if (ud.
player_skill > 3 || ((g_netServer
|| ud.
multimode > 1) && !GTFLAGS
(GAMETYPE_PLAYERSFRIENDLY
)))
Bsprintf
(tempbuf
,"K:^15%d",(ud.
multimode>1 &&!GTFLAGS
(GAMETYPE_PLAYERSFRIENDLY
))?
myps
->frag
-myps
->fraggedself
:myps
->actors_killed
);
else
{
if (myps
->actors_killed
>= myps
->max_actors_killed
)
Bsprintf
(tempbuf
,"K:%d/%d",myps
->actors_killed
,
myps
->max_actors_killed
>myps
->actors_killed
?
myps
->max_actors_killed
:myps
->actors_killed
);
else
Bsprintf
(tempbuf
,"K:^15%d/%d",myps
->actors_killed
,
myps
->max_actors_killed
>myps
->actors_killed
?
myps
->max_actors_killed
:myps
->actors_killed
);
}
G_ScreenText
(STARTALPHANUM
, 2<<16, i
-gtextsc
(14<<16), gtextsc
(65536), 0, 0, tempbuf
, 0, 10, 2|8|16|256|ROTATESPRITE_FULL16
, 0, 5<<16, 8<<16, 8<<16, 0, TEXT_XOFFSETZERO
|TEXT_GAMETEXTNUMHACK
, 0, 0, xdim
-1, ydim
-1);
if (myps
->secret_rooms
== myps
->max_secret_rooms
)
Bsprintf
(tempbuf
,"S:%d/%d", myps
->secret_rooms
, myps
->max_secret_rooms
);
else Bsprintf
(tempbuf
,"S:^15%d/%d", myps
->secret_rooms
, myps
->max_secret_rooms
);
G_ScreenText
(STARTALPHANUM
, 2<<16, i
-gtextsc
(7<<16), gtextsc
(65536), 0, 0, tempbuf
, 0, 10, 2|8|16|256|ROTATESPRITE_FULL16
, 0, 5<<16, 8<<16, 8<<16, 0, TEXT_XOFFSETZERO
|TEXT_GAMETEXTNUMHACK
, 0, 0, xdim
-1, ydim
-1);
}
if (g_player
[myconnectindex
].
gotvote == 0 && voting
!= -1 && voting
!= myconnectindex
)
{
Bsprintf
(tempbuf
,"%s^00 has called a vote for map",g_player
[voting
].
user_name);
gametext
(160,40,tempbuf
,0,2+8+16);
Bsprintf
(tempbuf
,"%s (E%dL%d)",MapInfo
[vote_episode
*MAXLEVELS
+ vote_map
].
name,vote_episode
+1,vote_map
+1);
gametext
(160,48,tempbuf
,0,2+8+16);
gametext
(160,70,"Press F1 to Accept, F2 to Decline",0,2+8+16);
}
if (BUTTON
(gamefunc_Show_DukeMatch_Scores
))
G_ShowScores
();
if (g_Debug
)
G_ShowCacheLocks
();
#ifdef LUNATIC
El_DisplayErrors
();
#endif
#ifndef EDUKE32_TOUCH_DEVICES
if (VOLUMEONE
)
{
if (ud.
show_help == 0 && g_showShareware
> 0 && (g_player
[myconnectindex
].
ps->gm
&MODE_MENU
) == 0)
rotatesprite_fs
((320-50)<<16,9<<16,65536L,0,BETAVERSION
,0,0,2+8+16+128);
}
#endif
if (!Demo_IsProfiling
())
{
if (g_player
[myconnectindex
].
ps->gm
&MODE_TYPE
)
Net_SendMessage
();
else
M_DisplayMenus
();
}
{
static int32_t applied
= 0;
if (tint.
maxf)
{
G_FadePalaccum
(&tint
);
applied
= 1;
}
else if (applied
)
{
// be sure to always un-apply a tint.
setpalettefade
(0,0,0, 0);
applied
= 0;
}
}
}
static int32_t G_DoThirdPerson
(const DukePlayer_t
*pp
, vec3_t
*vect
, int16_t *vsectnum
, int32_t ang
, int32_t horiz
)
{
spritetype
*sp
= &sprite
[pp
->i
];
int32_t i
, hx
, hy
;
int32_t daang
;
int32_t bakcstat
= sp
->cstat
;
hitdata_t hit
;
vec3_t n
= {
sintable
[(ang
+1536)&2047]>>4,
sintable
[(ang
+1024)&2047]>>4,
(horiz
-100)*128
};
updatesectorz
(vect
->x
,vect
->y
,vect
->z
,vsectnum
);
sp
->cstat
&= ~
0x101;
hitscan
(vect
, *vsectnum
, n.
x,n.
y,n.
z, &hit
, CLIPMASK1
);
sp
->cstat
= bakcstat
;
if (*vsectnum
< 0)
return -1;
hx
= hit.
pos.
x-(vect
->x
);
hy
= hit.
pos.
y-(vect
->y
);
if (klabs
(n.
x)+klabs
(n.
y) > klabs
(hx
)+klabs
(hy
))
{
*vsectnum
= hit.
sect;
if (hit.
wall >= 0)
{
daang
= getangle
(wall
[wall
[hit.
wall].
point2].
x-wall
[hit.
wall].
x,
wall
[wall
[hit.
wall].
point2].
y-wall
[hit.
wall].
y);
i
= n.
x*sintable
[daang
] + n.
y*sintable
[(daang
+1536)&2047];
if (klabs
(n.
x) > klabs
(n.
y))
hx
-= mulscale28
(n.
x,i
);
else hy
-= mulscale28
(n.
y,i
);
}
else if (hit.
sprite < 0)
{
if (klabs
(n.
x) > klabs
(n.
y))
hx
-= (n.
x>>5);
else hy
-= (n.
y>>5);
}
if (klabs
(n.
x) > klabs
(n.
y))
i
= divscale16
(hx
,n.
x);
else i
= divscale16
(hy
,n.
y);
if (i
< CAMERADIST
)
CAMERADIST
= i
;
}
vect
->x
+= mulscale16
(n.
x,CAMERADIST
);
vect
->y
+= mulscale16
(n.
y,CAMERADIST
);
vect
->z
+= mulscale16
(n.
z,CAMERADIST
);
CAMERADIST
= min
(CAMERADIST
+((totalclock
-CAMERACLOCK
)<<10),65536);
CAMERACLOCK
= totalclock
;
updatesectorz
(vect
->x
,vect
->y
,vect
->z
,vsectnum
);
return 0;
}
//REPLACE FULLY
void G_DrawBackground
(void)
{
const int32_t dapicnum
= BIGHOLE
;
int32_t x
,y
,x1
,x2
;
flushperms
();
// XXX: if dapicnum is not available, this might leave the menu background
// not drawn, leading to "HOM".
if (tilesiz
[dapicnum
].
x == 0 || tilesiz
[dapicnum
].
y == 0)
{
pus
= pub
= NUMPAGES
;
return;
}
int32_t y1
=0, y2
=ydim
;
if ((g_player
[myconnectindex
].
ps->gm
&MODE_GAME
) || ud.
recstat == 2)
{
if (GametypeFlags
[ud.
coop] & GAMETYPE_FRAGBAR
)
{
if ((g_netServer
|| ud.
multimode > 1)) y1
+= scale
(ydim
,8,200);
if (ud.
multimode > 4) y1
+= scale
(ydim
,8,200);
}
}
else
{
const int32_t MENUTILE
= MENUSCREEN
;//(getrendermode() == REND_CLASSIC ? MENUSCREEN : LOADSCREEN);
const int32_t fstilep
= tilesiz
[MENUTILE
].
x>=320 && tilesiz
[MENUTILE
].
y==200;
int32_t bgtile
= (fstilep
? MENUTILE
: BIGHOLE
);
clearallviews
(0);
// when not rendering a game, fullscreen wipe
// Gv_SetVar(g_iReturnVarID,tilesizx[MENUTILE]==320&&tilesizy[MENUTILE]==200?MENUTILE:BIGHOLE, -1, -1);
bgtile
= VM_OnEventWithReturn
(EVENT_GETMENUTILE
, -1, myconnectindex
, bgtile
);
// MENU_TILE: is the menu tile tileable?
#if !defined LUNATIC
if (Gv_GetVarByLabel
("MENU_TILE", !fstilep
, -1, -1))
#else
if (!fstilep
)
#endif
{
if ((unsigned)bgtile
< MAXTILES
)
for (y
=y1
; y
<y2
; y
+=tilesiz
[bgtile
].
y)
for (x
=0; x
<xdim
; x
+=tilesiz
[bgtile
].
x)
rotatesprite_fs
(x
<<16,y
<<16,65536L,0,bgtile
,8,0,8+16+64);
}
else rotatesprite_fs
(160<<16,100<<16,65536L,0,bgtile
,16,0,2+8+64+BGSTRETCH
);
return;
}
y2
= scale
(ydim
,200-sbarsc
(tilesiz
[BOTTOMSTATUSBAR
].
y),200);
if (ud.
screen_size > 8)
{
// across top
for (y
=0; y
<windowy1
; y
+=tilesiz
[dapicnum
].
y)
for (x
=0; x
<xdim
; x
+=tilesiz
[dapicnum
].
x)
rotatesprite
(x
<<16,y
<<16,65536L,0,dapicnum
,8,0,8+16+64,0,y1
,xdim
-1,windowy1
-1);
// sides
const int32_t rx
= windowx2
-windowx2
%tilesiz
[dapicnum
].
x;
for (y
=windowy1
-windowy1
%tilesiz
[dapicnum
].
y; y
<windowy2
; y
+=tilesiz
[dapicnum
].
y)
for (x
=0; x
<windowx1
|| x
+rx
<xdim
; x
+=tilesiz
[dapicnum
].
x)
{
rotatesprite
(x
<<16,y
<<16,65536L,0,dapicnum
,8,0,8+16+64,0,windowy1
,windowx1
-1,windowy2
-1);
rotatesprite
((x
+rx
)<<16,y
<<16,65536L,0,dapicnum
,8,0,8+16+64,windowx2
,windowy1
,xdim
-1,windowy2
-1);
}
// along bottom
for (y
=windowy2
-(windowy2
%tilesiz
[dapicnum
].
y); y
<y2
; y
+=tilesiz
[dapicnum
].
y)
for (x
=0; x
<xdim
; x
+=tilesiz
[dapicnum
].
x)
rotatesprite
(x
<<16,y
<<16,65536L,0,dapicnum
,8,0,8+16+64,0,windowy2
,xdim
-1,y2
-1);
}
// draw in the bits to the left and right of the non-fullsize status bar
if (ud.
screen_size >= 8 && ud.
statusbarmode == 0)
{
// when not rendering a game, fullscreen wipe
x2
= (xdim
- sbarsc
((int32_t)(ydim
*1.333333333333333333f))) >> 1;
for (y
=y2
-y2
%tilesiz
[dapicnum
].
y; y
<ydim
; y
+=tilesiz
[dapicnum
].
y)
for (x
=0; x
<xdim
>>1; x
+=tilesiz
[dapicnum
].
x)
{
rotatesprite
(x
<<16,y
<<16,65536L,0,dapicnum
,8,0,8+16+64,0,y2
,x2
,ydim
-1);
rotatesprite
((xdim
-x
)<<16,y
<<16,65536L,0,dapicnum
,8,0,8+16+64,xdim
-x2
-1,y2
,xdim
-1,ydim
-1);
}
}
if (ud.
screen_size > 8)
{
y
= 0;
if (GametypeFlags
[ud.
coop] & GAMETYPE_FRAGBAR
)
{
if ((g_netServer
|| ud.
multimode > 1)) y
+= 8;
if (ud.
multimode > 4) y
+= 8;
}
x1
= max
(windowx1
-4,0);
y1
= max
(windowy1
-4,y
);
x2
= min
(windowx2
+4,xdim
-1);
y2
= min
(windowy2
+4,scale
(ydim
,200-sbarsc
(tilesiz
[BOTTOMSTATUSBAR
].
y),200)-1);
for (y
=y1
+4; y
<y2
-4; y
+=64)
{
rotatesprite
(x1
<<16,y
<<16,65536L,0,VIEWBORDER
,0,0,8+16+64,x1
,y1
,x2
,y2
);
rotatesprite
((x2
+1)<<16,(y
+64)<<16,65536L,1024,VIEWBORDER
,0,0,8+16+64,x1
,y1
,x2
,y2
);
}
for (x
=x1
+4; x
<x2
-4; x
+=64)
{
rotatesprite
((x
+64)<<16,y1
<<16,65536L,512,VIEWBORDER
,0,0,8+16+64,x1
,y1
,x2
,y2
);
rotatesprite
(x
<<16,(y2
+1)<<16,65536L,1536,VIEWBORDER
,0,0,8+16+64,x1
,y1
,x2
,y2
);
}
rotatesprite
(x1
<<16,y1
<<16,65536L,0,VIEWBORDER
+1,0,0,8+16+64,x1
,y1
,x2
,y2
);
rotatesprite
((x2
+1)<<16,y1
<<16,65536L,512,VIEWBORDER
+1,0,0,8+16+64,x1
,y1
,x2
,y2
);
rotatesprite
((x2
+1)<<16,(y2
+1)<<16,65536L,1024,VIEWBORDER
+1,0,0,8+16+64,x1
,y1
,x2
,y2
);
rotatesprite
(x1
<<16,(y2
+1)<<16,65536L,1536,VIEWBORDER
+1,0,0,8+16+64,x1
,y1
,x2
,y2
);
}
pus
= pub
= NUMPAGES
;
}
#ifdef LEGACY_ROR
char ror_protectedsectors
[MAXSECTORS
];
static int32_t drawing_ror
= 0;
static int32_t ror_sprite
= -1;
static void G_OROR_DupeSprites
(const spritetype
*sp
)
{
// dupe the sprites touching the portal to the other sector
int32_t k
;
const spritetype
*refsp
;
if ((unsigned)sp
->yvel
>= (unsigned)playerswhenstarted
)
return;
refsp
= &sprite
[sp
->yvel
];
for (SPRITES_OF_SECT
(sp
->sectnum
, k
))
{
if (spritesortcnt
>= MAXSPRITESONSCREEN
)
break;
if (sprite
[k
].
picnum != SECTOREFFECTOR
&& sprite
[k
].
z >= sp
->z
)
{
Bmemcpy
(&tsprite
[spritesortcnt
], &sprite
[k
], sizeof(spritetype
));
tsprite
[spritesortcnt
].
x += (refsp
->x
- sp
->x
);
tsprite
[spritesortcnt
].
y += (refsp
->y
- sp
->y
);
tsprite
[spritesortcnt
].
z = tsprite
[spritesortcnt
].
z - sp
->z
+ actor
[sp
->yvel
].
ceilingz;
tsprite
[spritesortcnt
].
sectnum = refsp
->sectnum
;
tsprite
[spritesortcnt
].
owner = k
;
// OSD_Printf("duped sprite of pic %d at %d %d %d\n",tsprite[spritesortcnt].picnum,tsprite[spritesortcnt].x,tsprite[spritesortcnt].y,tsprite[spritesortcnt].z);
spritesortcnt
++;
}
}
}
static void G_SE40
(int32_t smoothratio
)
{
if ((unsigned)ror_sprite
< MAXSPRITES
)
{
int32_t x
, y
, z
;
int16_t sect
;
int32_t level
= 0;
const spritetype
*const sp
= &sprite
[ror_sprite
];
const int32_t sprite2
= sp
->yvel
;
if ((unsigned)sprite2
>= MAXSPRITES
)
return;
if (klabs
(sector
[sp
->sectnum
].
floorz - sp
->z
) < klabs
(sector
[sprite
[sprite2
].
sectnum].
floorz - sprite
[sprite2
].
z))
level
= 1;
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);
sect
= sprite
[sprite2
].
sectnum;
updatesector
(sprite
[sprite2
].
x + x
, sprite
[sprite2
].
y + y
, §
);
if (sect
!= -1)
{
int32_t renderz
, picnum
;
// XXX: PK: too large stack allocation for my taste
int16_t backupstat
[MAXSECTORS
];
int32_t backupz
[MAXSECTORS
];
int32_t i
;
int32_t pix_diff
, newz
;
// initprintf("drawing ror\n");
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;
pix_diff
= klabs
(z
) >> 8;
newz
= - ((pix_diff
/ 128) + 1) * (128<<8);
for (i
= 0; i
< numsectors
; i
++)
{
backupstat
[i
] = sector
[i
].
ceilingstat;
backupz
[i
] = sector
[i
].
ceilingz;
if (!ror_protectedsectors
[i
] || (ror_protectedsectors
[i
] && sp
->lotag
== 41))
{
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;
pix_diff
= klabs
(z
) >> 8;
newz
= ((pix_diff
/ 128) + 1) * (128<<8);
for (i
= 0; i
< numsectors
; i
++)
{
backupstat
[i
] = sector
[i
].
floorstat;
backupz
[i
] = sector
[i
].
floorz;
if (!ror_protectedsectors
[i
] || (ror_protectedsectors
[i
] && sp
->lotag
== 41))
{
sector
[i
].
floorstat = 1;
sector
[i
].
floorz = +newz
;
}
}
}
#ifdef POLYMER
if (getrendermode
() == REND_POLYMER
)
polymer_setanimatesprites
(G_DoSpriteAnimations
, CAMERA
(pos.
x), CAMERA
(pos.
y), CAMERA
(ang
), smoothratio
);
#endif
drawrooms
(sprite
[sprite2
].
x + x
, sprite
[sprite2
].
y + y
,
z
+ renderz
, CAMERA
(ang
), CAMERA
(horiz
), sect
);
drawing_ror
= 1 + level
;
if (drawing_ror
== 2) // viewing from top
G_OROR_DupeSprites
(sp
);
G_DoSpriteAnimations
(CAMERA
(pos.
x),CAMERA
(pos.
y),CAMERA
(ang
),smoothratio
);
drawmasks
();
if (level
)
{
sector
[sprite
[sprite2
].
sectnum].
ceilingpicnum = picnum
;
for (i
= 0; i
< numsectors
; i
++)
{
sector
[i
].
ceilingstat = backupstat
[i
];
sector
[i
].
ceilingz = backupz
[i
];
}
}
else
{
sector
[sprite
[sprite2
].
sectnum].
floorpicnum = picnum
;
for (i
= 0; i
< numsectors
; i
++)
{
sector
[i
].
floorstat = backupstat
[i
];
sector
[i
].
floorz = backupz
[i
];
}
}
}
}
}
#endif
void G_HandleMirror
(int32_t x
, int32_t y
, int32_t z
, int32_t a
, int32_t horiz
, int32_t smoothratio
)
{
if ((gotpic
[MIRROR
>>3]&(1<<(MIRROR
&7)))
#ifdef POLYMER
&& (getrendermode
() != REND_POLYMER
)
#endif
)
{
if (g_mirrorCount
== 0)
{
// NOTE: We can have g_mirrorCount==0 but gotpic'd MIRROR,
// for example in LNGA2.
gotpic
[MIRROR
>>3] &= ~
(1<<(MIRROR
&7));
return;
}
int32_t i
= 0, dst
= INT32_MAX
;
for (int32_t k
=g_mirrorCount
-1; k
>=0; k
--)
{
const int32_t j
=
klabs
(wall
[g_mirrorWall
[k
]].
x - x
) +
klabs
(wall
[g_mirrorWall
[k
]].
y - y
);
if (j
< dst
)
dst
= j
, i
= k
;
}
if (wall
[g_mirrorWall
[i
]].
overpicnum != MIRROR
)
{
// Try to find a new mirror wall in case the original one was broken.
int32_t startwall
= sector
[g_mirrorSector
[i
]].
wallptr;
int32_t endwall
= startwall
+ sector
[g_mirrorSector
[i
]].
wallnum;
for (int32_t k
=startwall
; k
<endwall
; k
++)
{
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;
}
}
}
if (wall
[g_mirrorWall
[i
]].
overpicnum == MIRROR
)
{
int32_t tposx
, tposy
;
int16_t tang
;
preparemirror
(x
, y
, a
, g_mirrorWall
[i
], &tposx
, &tposy
, &tang
);
int32_t j
= g_visibility
;
g_visibility
= (j
>>1) + (j
>>2);
if (getrendermode
() == REND_CLASSIC
)
{
int32_t didmirror
;
yax_preparedrawrooms
();
didmirror
= drawrooms
(tposx
,tposy
,z
,tang
,horiz
,g_mirrorSector
[i
]+MAXSECTORS
);
yax_drawrooms
(G_DoSpriteAnimations
, g_mirrorSector
[i
], didmirror
, smoothratio
);
}
#ifdef USE_OPENGL
else
drawrooms
(tposx
,tposy
,z
,tang
,horiz
,g_mirrorSector
[i
]+MAXSECTORS
);
// XXX: Sprites don't get drawn with TROR/Polymost
#endif
display_mirror
= 1;
G_DoSpriteAnimations
(tposx
,tposy
,tang
,smoothratio
);
display_mirror
= 0;
drawmasks
();
completemirror
(); //Reverse screen x-wise in this function
g_visibility
= j
;