Subversion Repositories eduke32

Rev

Rev 5047 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 5047 Rev 5048
1
//-------------------------------------------------------------------------
1
//-------------------------------------------------------------------------
2
/*
2
/*
3
Copyright (C) 2010 EDuke32 developers and contributors
3
Copyright (C) 2010 EDuke32 developers and contributors
4

4

5
This file is part of EDuke32.
5
This file is part of EDuke32.
6

6

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

10

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

14

15
See the GNU General Public License for more details.
15
See the GNU General Public License for more details.
16

16

17
You should have received a copy of the GNU General Public License
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
*/
20
*/
21
//-------------------------------------------------------------------------
21
//-------------------------------------------------------------------------
22
22
23
#include "duke3d.h"
23
#include "duke3d.h"
24
24
25
#include "baselayer.h"
25
#include "baselayer.h"
26
#include "renderlayer.h"
26
#include "renderlayer.h"
27
27
28
#include "scriplib.h"
28
#include "scriplib.h"
29
#include "file_lib.h"
29
#include "file_lib.h"
30
#include "mathutil.h"
30
#include "mathutil.h"
31
#include "gamedefs.h"
31
#include "gamedefs.h"
32
#include "keyboard.h"
32
#include "keyboard.h"
33
#include "mouse.h"  // JBF 20030809
33
#include "mouse.h"  // JBF 20030809
34
#include "joystick.h"
34
#include "joystick.h"
35
#include "function.h"
35
#include "function.h"
36
#include "control.h"
36
#include "control.h"
37
#include "fx_man.h"
37
#include "fx_man.h"
38
#include "sounds.h"
38
#include "sounds.h"
39
#include "config.h"
39
#include "config.h"
40
#include "osd.h"
40
#include "osd.h"
41
#include "osdfuncs.h"
41
#include "osdfuncs.h"
42
#include "osdcmds.h"
42
#include "osdcmds.h"
43
#include "scriptfile.h"
43
#include "scriptfile.h"
44
#include "grpscan.h"
44
#include "grpscan.h"
45
#include "gamedef.h"
45
#include "gamedef.h"
46
#include "kplib.h"
46
#include "kplib.h"
47
#include "crc32.h"
47
#include "crc32.h"
48
#include "hightile.h"
48
#include "hightile.h"
49
#include "control.h"
49
#include "control.h"
50
#include "lz4.h"
50
#include "lz4.h"
51
#include "net.h"
51
#include "net.h"
52
#include "premap.h"
52
#include "premap.h"
53
#include "gameexec.h"
53
#include "gameexec.h"
54
#include "menus.h"
54
#include "menus.h"
55
#include "savegame.h"
55
#include "savegame.h"
56
#include "anim.h"
56
#include "anim.h"
57
#include "demo.h"
57
#include "demo.h"
58
#include "common.h"
58
#include "common.h"
59
#include "common_game.h"
59
#include "common_game.h"
60
#include "input.h"
60
#include "input.h"
61
#include "compat.h"
61
#include "compat.h"
62
62
63
#ifdef __ANDROID__
63
#ifdef __ANDROID__
64
#include "android.h"
64
#include "android.h"
65
#endif
65
#endif
66
66
67
#ifdef LUNATIC
67
#ifdef LUNATIC
68
# include "lunatic_game.h"
68
# include "lunatic_game.h"
69
#endif
69
#endif
70
70
71
// Uncomment to prevent anything except mirrors from drawing. It is sensible to
71
// Uncomment to prevent anything except mirrors from drawing. It is sensible to
72
// also uncomment ENGINE_CLEAR_SCREEN in build/src/engine_priv.h.
72
// also uncomment ENGINE_CLEAR_SCREEN in build/src/engine_priv.h.
73
//#define DEBUG_MIRRORS_ONLY
73
//#define DEBUG_MIRRORS_ONLY
74
74
75
#if KRANDDEBUG
75
#if KRANDDEBUG
76
# define GAME_INLINE
76
# define GAME_INLINE
77
# define GAME_STATIC
77
# define GAME_STATIC
78
#else
78
#else
79
# define GAME_INLINE inline
79
# define GAME_INLINE inline
80
# define GAME_STATIC static
80
# define GAME_STATIC static
81
#endif
81
#endif
82
82
83
#ifdef _WIN32
83
#ifdef _WIN32
84
# include "winlayer.h"
84
# include "winlayer.h"
85
# define WIN32_LEAN_AND_MEAN
85
# define WIN32_LEAN_AND_MEAN
86
# include <windows.h>
86
# include <windows.h>
87
# include <shellapi.h>
87
# include <shellapi.h>
88
# define UPDATEINTERVAL 604800 // 1w
88
# define UPDATEINTERVAL 604800 // 1w
89
# include "winbits.h"
89
# include "winbits.h"
90
#else
90
#else
91
# ifndef GEKKO
91
# ifndef GEKKO
92
#  include <sys/ioctl.h>
92
#  include <sys/ioctl.h>
93
# endif
93
# endif
94
#endif /* _WIN32 */
94
#endif /* _WIN32 */
95
95
96
const char* AppProperName = "EDuke32";
96
const char* AppProperName = "EDuke32";
97
const char* AppTechnicalName = "eduke32";
97
const char* AppTechnicalName = "eduke32";
98
98
99
int32_t g_quitDeadline = 0;
99
int32_t g_quitDeadline = 0;
100
100
101
#ifdef LUNATIC
101
#ifdef LUNATIC
102
camera_t g_camera;
102
camera_t g_camera;
103
#else
103
#else
104
int32_t g_cameraDistance = 0, g_cameraClock = 0;
104
int32_t g_cameraDistance = 0, g_cameraClock = 0;
105
#endif
105
#endif
106
static int32_t g_quickExit;
106
static int32_t g_quickExit;
107
static int32_t g_commandSetup = 0;
107
static int32_t g_commandSetup = 0;
108
int32_t g_noSetup = 0;
108
int32_t g_noSetup = 0;
109
static int32_t g_noAutoLoad = 0;
109
static int32_t g_noAutoLoad = 0;
110
static int32_t g_noSound = 0;
110
static int32_t g_noSound = 0;
111
static int32_t g_noMusic = 0;
111
static int32_t g_noMusic = 0;
112
static const char *CommandMap = NULL;
112
static const char *CommandMap = NULL;
113
static const char *CommandName = NULL;
113
static const char *CommandName = NULL;
114
int32_t g_forceWeaponChoice = 0;
114
int32_t g_forceWeaponChoice = 0;
115
int32_t g_fakeMultiMode = 0;
115
int32_t g_fakeMultiMode = 0;
116
116
117
double g_moveActorsTime = 0;  // in ms, smoothed
117
double g_moveActorsTime = 0;  // in ms, smoothed
118
118
119
char boardfilename[BMAX_PATH] = {0}, currentboardfilename[BMAX_PATH] = {0};
119
char boardfilename[BMAX_PATH] = {0}, currentboardfilename[BMAX_PATH] = {0};
120
120
121
int32_t voting = -1;
121
int32_t voting = -1;
122
int32_t vote_map = -1, vote_episode = -1;
122
int32_t vote_map = -1, vote_episode = -1;
123
123
124
static int32_t g_Debug = 0;
124
static int32_t g_Debug = 0;
125
static int32_t g_noLogoAnim = 0;
125
static int32_t g_noLogoAnim = 0;
126
static int32_t g_noLogo = 0;
126
static int32_t g_noLogo = 0;
127
127
128
const char *defaultrtsfilename[GAMECOUNT] = { "DUKE.RTS", "NAM.RTS", "NAPALM.RTS", "WW2GI.RTS" };
128
const char *defaultrtsfilename[GAMECOUNT] = { "DUKE.RTS", "NAM.RTS", "NAPALM.RTS", "WW2GI.RTS" };
129
129
130
// g_rtsNamePtr can point to an argv[] element
130
// g_rtsNamePtr can point to an argv[] element
131
const char *g_rtsNamePtr = NULL;
131
const char *g_rtsNamePtr = NULL;
132
132
133
int32_t g_Shareware = 0;
133
int32_t g_Shareware = 0;
134
134
135
#define MAXUSERQUOTES 6
135
#define MAXUSERQUOTES 6
136
int32_t quotebot, quotebotgoal;
136
int32_t quotebot, quotebotgoal;
137
static int32_t user_quote_time[MAXUSERQUOTES];
137
static int32_t user_quote_time[MAXUSERQUOTES];
138
static char user_quote[MAXUSERQUOTES][178];
138
static char user_quote[MAXUSERQUOTES][178];
139
139
140
// This was 32 for a while, but I think lowering it to 24 will help things like the Dingoo.
140
// This was 32 for a while, but I think lowering it to 24 will help things like the Dingoo.
141
// Ideally, we would look at our memory usage on our most cramped platform and figure out
141
// Ideally, we would look at our memory usage on our most cramped platform and figure out
142
// how much of that is needed for the underlying OS and things like SDL instead of guessing
142
// how much of that is needed for the underlying OS and things like SDL instead of guessing
143
#ifndef GEKKO
143
#ifndef GEKKO
144
static int32_t MAXCACHE1DSIZE = (24*1048576);
144
static int32_t MAXCACHE1DSIZE = (24*1048576);
145
#else
145
#else
146
static int32_t MAXCACHE1DSIZE = (8*1048576);
146
static int32_t MAXCACHE1DSIZE = (8*1048576);
147
#endif
147
#endif
148
148
149
int32_t tempwallptr;
149
int32_t tempwallptr;
150
150
151
static int32_t nonsharedtimer;
151
static int32_t nonsharedtimer;
152
152
153
int32_t ticrandomseed;
153
int32_t ticrandomseed;
154
154
155
static void G_DrawCameraText(int16_t i);
155
static void G_DrawCameraText(int16_t i);
156
GAME_STATIC GAME_INLINE int32_t G_MoveLoop(void);
156
GAME_STATIC GAME_INLINE int32_t G_MoveLoop(void);
157
static void G_DoOrderScreen(void);
157
static void G_DoOrderScreen(void);
158
158
159
#define FTAOPAQUETIME 30
159
#define FTAOPAQUETIME 30
160
160
161
#define ftapulseshade                                                                                                  \
161
#define ftapulseshade                                                                                                  \
162
    ((hud_glowingquotes && (getrendermode() == REND_CLASSIC || ps->fta >= FTAOPAQUETIME)) ?                            \
162
    ((hud_glowingquotes && (getrendermode() == REND_CLASSIC || ps->fta >= FTAOPAQUETIME)) ?                            \
163
     (sintable[((uint32_t)ps->fta << 7) & 2047] >> 11) :                                                               \
163
     (sintable[((uint32_t)ps->fta << 7) & 2047] >> 11) :                                                               \
164
     (sintable[((uint32_t)FTAOPAQUETIME << 7) & 2047] >> 11))
164
     (sintable[((uint32_t)FTAOPAQUETIME << 7) & 2047] >> 11))
165
165
166
#define quotepulseshade (sintable[((uint32_t)totalclock<<5)&2047]>>11)
166
#define quotepulseshade (sintable[((uint32_t)totalclock<<5)&2047]>>11)
167
167
168
int32_t althud_numbertile = 2930;
168
int32_t althud_numbertile = 2930;
169
int32_t althud_numberpal = 0;
169
int32_t althud_numberpal = 0;
170
170
171
#ifdef EDUKE32_TOUCH_DEVICES
171
#ifdef EDUKE32_TOUCH_DEVICES
172
int32_t althud_shadows = 0;
172
int32_t althud_shadows = 0;
173
#else
173
#else
174
int32_t althud_shadows = 1;
174
int32_t althud_shadows = 1;
175
#endif
175
#endif
176
176
177
int32_t althud_flashing = 1;
177
int32_t althud_flashing = 1;
178
int32_t hud_glowingquotes = 1;
178
int32_t hud_glowingquotes = 1;
179
int32_t hud_showmapname = 1;
179
int32_t hud_showmapname = 1;
180
180
181
int32_t g_levelTextTime = 0;
181
int32_t g_levelTextTime = 0;
182
182
183
int32_t r_maxfps = 0;
183
int32_t r_maxfps = 0;
184
uint32_t g_frameDelay = 0;
184
uint32_t g_frameDelay = 0;
185
185
186
#if defined(RENDERTYPEWIN) && defined(USE_OPENGL)
186
#if defined(RENDERTYPEWIN) && defined(USE_OPENGL)
187
extern char forcegl;
187
extern char forcegl;
188
#endif
188
#endif
189
189
190
void M32RunScript(const char *s) { UNREFERENCED_PARAMETER(s); };  // needed for linking since it's referenced from build/src/osd.c
190
void M32RunScript(const char *s) { UNREFERENCED_PARAMETER(s); };  // needed for linking since it's referenced from build/src/osd.c
191
191
192
const char *G_DefaultRtsFile(void)
192
const char *G_DefaultRtsFile(void)
193
{
193
{
194
    if (DUKE)
194
    if (DUKE)
195
        return defaultrtsfilename[GAME_DUKE];
195
        return defaultrtsfilename[GAME_DUKE];
196
    else if (WW2GI)
196
    else if (WW2GI)
197
        return defaultrtsfilename[GAME_WW2GI];
197
        return defaultrtsfilename[GAME_WW2GI];
198
    else if (NAPALM)
198
    else if (NAPALM)
199
    {
199
    {
200
        if (!testkopen(defaultrtsfilename[GAME_NAPALM],0) && testkopen(defaultrtsfilename[GAME_NAM],0))
200
        if (!testkopen(defaultrtsfilename[GAME_NAPALM],0) && testkopen(defaultrtsfilename[GAME_NAM],0))
201
            return defaultrtsfilename[GAME_NAM]; // NAM/NAPALM Sharing
201
            return defaultrtsfilename[GAME_NAM]; // NAM/NAPALM Sharing
202
        else
202
        else
203
            return defaultrtsfilename[GAME_NAPALM];
203
            return defaultrtsfilename[GAME_NAPALM];
204
    }
204
    }
205
    else if (NAM)
205
    else if (NAM)
206
    {
206
    {
207
        if (!testkopen(defaultrtsfilename[GAME_NAM],0) && testkopen(defaultrtsfilename[GAME_NAPALM],0))
207
        if (!testkopen(defaultrtsfilename[GAME_NAM],0) && testkopen(defaultrtsfilename[GAME_NAPALM],0))
208
            return defaultrtsfilename[GAME_NAPALM]; // NAM/NAPALM Sharing
208
            return defaultrtsfilename[GAME_NAPALM]; // NAM/NAPALM Sharing
209
        else
209
        else
210
            return defaultrtsfilename[GAME_NAM];
210
            return defaultrtsfilename[GAME_NAM];
211
    }
211
    }
212
212
213
    return defaultrtsfilename[0];
213
    return defaultrtsfilename[0];
214
}
214
}
215
215
216
enum gametokens
216
enum gametokens
217
{
217
{
218
    T_INCLUDE = 0,
218
    T_INCLUDE = 0,
219
    T_INTERFACE = 0,
219
    T_INTERFACE = 0,
220
    T_LOADGRP = 1,
220
    T_LOADGRP = 1,
221
    T_MODE = 1,
221
    T_MODE = 1,
222
    T_CACHESIZE = 2,
222
    T_CACHESIZE = 2,
223
    T_ALLOW = 2,
223
    T_ALLOW = 2,
224
    T_NOAUTOLOAD,
224
    T_NOAUTOLOAD,
225
    T_INCLUDEDEFAULT,
225
    T_INCLUDEDEFAULT,
226
    T_MUSIC,
226
    T_MUSIC,
227
    T_SOUND,
227
    T_SOUND,
228
    T_FILE,
228
    T_FILE,
229
    T_CUTSCENE,
229
    T_CUTSCENE,
230
    T_ANIMSOUNDS,
230
    T_ANIMSOUNDS,
231
    T_NOFLOORPALRANGE,
231
    T_NOFLOORPALRANGE,
232
    T_ID,
232
    T_ID,
233
    T_DELAY
233
    T_DELAY
234
};
234
};
235
235
236
236
237
static int32_t sbarsc(int32_t sc)
237
static int32_t sbarsc(int32_t sc)
238
{
238
{
239
    return scale(sc,ud.statusbarscale,100);
239
    return scale(sc,ud.statusbarscale,100);
240
}
240
}
241
241
242
static int32_t sbarx(int32_t x)
242
static int32_t sbarx(int32_t x)
243
{
243
{
244
    if (ud.screen_size == 4) return sbarsc(x<<16);
244
    if (ud.screen_size == 4) return sbarsc(x<<16);
245
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x<<16);
245
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x<<16);
246
}
246
}
247
247
248
static int32_t sbarxr(int32_t x)
248
static int32_t sbarxr(int32_t x)
249
{
249
{
250
    if (ud.screen_size == 4) return (320<<16) - sbarsc(x<<16);
250
    if (ud.screen_size == 4) return (320<<16) - sbarsc(x<<16);
251
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x<<16);
251
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x<<16);
252
}
252
}
253
253
254
static int32_t sbary(int32_t y)
254
static int32_t sbary(int32_t y)
255
{
255
{
256
    if (ud.althud == 2 && ud.screen_size == 4) return sbarsc(y << 16);
256
    if (ud.althud == 2 && ud.screen_size == 4) return sbarsc(y << 16);
257
    else return (200<<16) - sbarsc(200<<16) + sbarsc(y<<16);
257
    else return (200<<16) - sbarsc(200<<16) + sbarsc(y<<16);
258
}
258
}
259
259
260
static int32_t sbarx16(int32_t x)
260
static int32_t sbarx16(int32_t x)
261
{
261
{
262
    if (ud.screen_size == 4) return sbarsc(x);
262
    if (ud.screen_size == 4) return sbarsc(x);
263
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x);
263
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x);
264
}
264
}
265
265
266
#if 0 // enable if ever needed
266
#if 0 // enable if ever needed
267
static int32_t sbarxr16(int32_t x)
267
static int32_t sbarxr16(int32_t x)
268
{
268
{
269
    if (ud.screen_size == 4) return (320<<16) - sbarsc(x);
269
    if (ud.screen_size == 4) return (320<<16) - sbarsc(x);
270
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x);
270
    return (((320<<16) - sbarsc(320<<16)) >> 1) + sbarsc(x);
271
}
271
}
272
#endif
272
#endif
273
273
274
static int32_t sbary16(int32_t y)
274
static int32_t sbary16(int32_t y)
275
{
275
{
276
    return (200<<16) - sbarsc(200<<16) + sbarsc(y);
276
    return (200<<16) - sbarsc(200<<16) + sbarsc(y);
277
}
277
}
278
278
279
int32_t textsc(int32_t sc)
279
int32_t textsc(int32_t sc)
280
{
280
{
281
    // prevent ridiculousness to a degree
281
    // prevent ridiculousness to a degree
282
    if (xdim <= 320) return sc;
282
    if (xdim <= 320) return sc;
283
    else if (xdim <= 640) return scale(sc,min(200,ud.textscale),100);
283
    else if (xdim <= 640) return scale(sc,min(200,ud.textscale),100);
284
    else if (xdim <= 800) return scale(sc,min(300,ud.textscale),100);
284
    else if (xdim <= 800) return scale(sc,min(300,ud.textscale),100);
285
    else if (xdim <= 1024) return scale(sc,min(350,ud.textscale),100);
285
    else if (xdim <= 1024) return scale(sc,min(350,ud.textscale),100);
286
    return scale(sc,ud.textscale,100);
286
    return scale(sc,ud.textscale,100);
287
}
287
}
288
288
289
static void G_PatchStatusBar(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
289
static void G_PatchStatusBar(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
290
{
290
{
291
    int32_t scl = sbarsc(65536);
291
    int32_t scl = sbarsc(65536);
292
    int32_t tx = sbarx16((160<<16) - (tilesiz[BOTTOMSTATUSBAR].x<<15)); // centered
292
    int32_t tx = sbarx16((160<<16) - (tilesiz[BOTTOMSTATUSBAR].x<<15)); // centered
293
    int32_t ty = sbary(200-tilesiz[BOTTOMSTATUSBAR].y);
293
    int32_t ty = sbary(200-tilesiz[BOTTOMSTATUSBAR].y);
294
294
295
    int32_t clx1 = sbarsc(scale(x1,xdim,320)), cly1 = sbarsc(scale(y1,ydim,200));
295
    int32_t clx1 = sbarsc(scale(x1,xdim,320)), cly1 = sbarsc(scale(y1,ydim,200));
296
    int32_t clx2 = sbarsc(scale(x2,xdim,320)), cly2 = sbarsc(scale(y2,ydim,200));
296
    int32_t clx2 = sbarsc(scale(x2,xdim,320)), cly2 = sbarsc(scale(y2,ydim,200));
297
    int32_t clofx = (xdim - sbarsc(xdim)) >> 1, clofy = (ydim - sbarsc(ydim));
297
    int32_t clofx = (xdim - sbarsc(xdim)) >> 1, clofy = (ydim - sbarsc(ydim));
298
298
299
    rotatesprite(tx,ty,scl,0,BOTTOMSTATUSBAR,4,0,10+16+64,clx1+clofx,cly1+clofy,clx2+clofx-1,cly2+clofy-1);
299
    rotatesprite(tx,ty,scl,0,BOTTOMSTATUSBAR,4,0,10+16+64,clx1+clofx,cly1+clofy,clx2+clofx-1,cly2+clofy-1);
300
}
300
}
301
301
302
void P_SetGamePalette(DukePlayer_t *player, uint8_t palid, int32_t set)
302
void P_SetGamePalette(DukePlayer_t *player, uint8_t palid, int32_t set)
303
{
303
{
304
    if (palid >= BASEPALCOUNT)
304
    if (palid >= BASEPALCOUNT)
305
        palid = BASEPAL;
305
        palid = BASEPAL;
306
306
307
    player->palette = palid;
307
    player->palette = palid;
308
308
309
    if (player != g_player[screenpeek].ps)
309
    if (player != g_player[screenpeek].ps)
310
        return;
310
        return;
311
311
312
    setbrightness(ud.brightness>>2, palid, set);
312
    setbrightness(ud.brightness>>2, palid, set);
313
}
313
}
314
314
315
// get the string length until the next '\n'
315
// get the string length until the next '\n'
316
int32_t G_GetStringLineLength(const char *text, const char *end, const int32_t iter)
316
int32_t G_GetStringLineLength(const char *text, const char *end, const int32_t iter)
317
{
317
{
318
    int32_t length = 0;
318
    int32_t length = 0;
319
319
320
    while (*text != '\n' && text != end)
320
    while (*text != '\n' && text != end)
321
    {
321
    {
322
        ++length;
322
        ++length;
323
323
324
        text += iter;
324
        text += iter;
325
    }
325
    }
326
326
327
    return length;
327
    return length;
328
}
328
}
329
329
330
int32_t G_GetStringNumLines(const char *text, const char *end, const int32_t iter)
330
int32_t G_GetStringNumLines(const char *text, const char *end, const int32_t iter)
331
{
331
{
332
    int32_t count = 1;
332
    int32_t count = 1;
333
333
334
    while (text != end)
334
    while (text != end)
335
    {
335
    {
336
        if (*text == '\n')
336
        if (*text == '\n')
337
            ++count;
337
            ++count;
338
        text += iter;
338
        text += iter;
339
    }
339
    }
340
340
341
    return count;
341
    return count;
342
}
342
}
343
// Note: Neither of these care about TEXT_LINEWRAP. This is intended.
343
// Note: Neither of these care about TEXT_LINEWRAP. This is intended.
344
344
345
// This function requires you to Bfree() the returned char*.
345
// This function requires you to Bfree() the returned char*.
346
char* G_GetSubString(const char *text, const char *end, const int32_t iter, const int32_t length)
346
char* G_GetSubString(const char *text, const char *end, const int32_t iter, const int32_t length)
347
{
347
{
348
    char *line = (char*)Xmalloc((length+1) * sizeof(char));
348
    char *line = (char*)Xmalloc((length+1) * sizeof(char));
349
    int32_t counter = 0;
349
    int32_t counter = 0;
350
350
351
    while (counter < length && text != end)
351
    while (counter < length && text != end)
352
    {
352
    {
353
        line[counter] = *text;
353
        line[counter] = *text;
354
354
355
        text += iter;
355
        text += iter;
356
        ++counter;
356
        ++counter;
357
    }
357
    }
358
358
359
    line[counter] = '\0';
359
    line[counter] = '\0';
360
360
361
    return line;
361
    return line;
362
}
362
}
363
363
364
// assign the character's tilenum
364
// assign the character's tilenum
365
int32_t G_GetStringTile(int32_t font, char *t, int32_t f)
365
int32_t G_GetStringTile(int32_t font, char *t, int32_t f)
366
{
366
{
367
    if (f & TEXT_DIGITALNUMBER)
367
    if (f & TEXT_DIGITALNUMBER)
368
        return *t - '0' + font; // copied from digitalnumber
368
        return *t - '0' + font; // copied from digitalnumber
369
    else if (f & (TEXT_BIGALPHANUM|TEXT_GRAYFONT))
369
    else if (f & (TEXT_BIGALPHANUM|TEXT_GRAYFONT))
370
    {
370
    {
371
        int32_t offset = (f & TEXT_GRAYFONT) ? 26 : 0;
371
        int32_t offset = (f & TEXT_GRAYFONT) ? 26 : 0;
372
372
373
        if (*t >= '0' && *t <= '9')
373
        if (*t >= '0' && *t <= '9')
374
            return *t - '0' + font + ((f & TEXT_GRAYFONT) ? 26 : -10);
374
            return *t - '0' + font + ((f & TEXT_GRAYFONT) ? 26 : -10);
375
        else if (*t >= 'a' && *t <= 'z')
375
        else if (*t >= 'a' && *t <= 'z')
376
            return *t - 'a' + font + ((f & TEXT_GRAYFONT) ? -26 : 26);
376
            return *t - 'a' + font + ((f & TEXT_GRAYFONT) ? -26 : 26);
377
        else if (*t >= 'A' && *t <= 'Z')
377
        else if (*t >= 'A' && *t <= 'Z')
378
            return *t - 'A' + font;
378
            return *t - 'A' + font;
379
        else switch (*t)
379
        else switch (*t)
380
        {
380
        {
381
            case '_':
381
            case '_':
382
            case '-':
382
            case '-':
383
                return font - (11 + offset);
383
                return font - (11 + offset);
384
                break;
384
                break;
385
            case '.':
385
            case '.':
386
                return font + (BIGPERIOD - (BIGALPHANUM + offset));
386
                return font + (BIGPERIOD - (BIGALPHANUM + offset));
387
                break;
387
                break;
388
            case ',':
388
            case ',':
389
                return font + (BIGCOMMA - (BIGALPHANUM + offset));
389
                return font + (BIGCOMMA - (BIGALPHANUM + offset));
390
                break;
390
                break;
391
            case '!':
391
            case '!':
392
                return font + (BIGX_ - (BIGALPHANUM + offset));
392
                return font + (BIGX_ - (BIGALPHANUM + offset));
393
                break;
393
                break;
394
            case '?':
394
            case '?':
395
                return font + (BIGQ - (BIGALPHANUM + offset));
395
                return font + (BIGQ - (BIGALPHANUM + offset));
396
                break;
396
                break;
397
            case ';':
397
            case ';':
398
                return font + (BIGSEMI - (BIGALPHANUM + offset));
398
                return font + (BIGSEMI - (BIGALPHANUM + offset));
399
                break;
399
                break;
400
            case ':':
400
            case ':':
401
                return font + (BIGCOLIN - (BIGALPHANUM + offset));
401
                return font + (BIGCOLIN - (BIGALPHANUM + offset));
402
                break;
402
                break;
403
            case '\\':
403
            case '\\':
404
            case '/':
404
            case '/':
405
                return font + (68 - offset); // 3008-2940
405
                return font + (68 - offset); // 3008-2940
406
                break;
406
                break;
407
            case '%':
407
            case '%':
408
                return font + (69 - offset); // 3009-2940
408
                return font + (69 - offset); // 3009-2940
409
                break;
409
                break;
410
            case '`':
410
            case '`':
411
            case '\"': // could be better hacked in
411
            case '\"': // could be better hacked in
412
            case '\'':
412
            case '\'':
413
                return font + (BIGAPPOS - (BIGALPHANUM + offset));
413
                return font + (BIGAPPOS - (BIGALPHANUM + offset));
414
                break;
414
                break;
415
            default: // unknown character
415
            default: // unknown character
416
                *t = ' '; // whitespace-ize
416
                *t = ' '; // whitespace-ize
417
            case '\n':
417
            case '\n':
418
                return font;
418
                return font;
419
                break;
419
                break;
420
        }
420
        }
421
    }
421
    }
422
    else
422
    else
423
        return *t - '!' + font; // uses ASCII order
423
        return *t - '!' + font; // uses ASCII order
424
}
424
}
425
425
426
#define NUMHACKACTIVE ((f & TEXT_GAMETEXTNUMHACK) && t >= '0' && t <= '9')
426
#define NUMHACKACTIVE ((f & TEXT_GAMETEXTNUMHACK) && t >= '0' && t <= '9')
427
427
428
// qstrdim
428
// qstrdim
429
vec2_t G_ScreenTextSize(const int32_t font,
429
vec2_t G_ScreenTextSize(const int32_t font,
430
                        int32_t x, int32_t y, const int32_t z, const int32_t blockangle,
430
                        int32_t x, int32_t y, const int32_t z, const int32_t blockangle,
431
                        const char *str, const int32_t o,
431
                        const char *str, const int32_t o,
432
                        int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween,
432
                        int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween,
433
                        const int32_t f,
433
                        const int32_t f,
434
                        int32_t x1, int32_t y1, int32_t x2, int32_t y2)
434
                        int32_t x1, int32_t y1, int32_t x2, int32_t y2)
435
{
435
{
436
    vec2_t size = { 0, 0, }; // eventually the return value
436
    vec2_t size = { 0, 0, }; // eventually the return value
437
    vec2_t pos = { 0, 0, }; // holds the coordinate position as we draw each character tile of the string
437
    vec2_t pos = { 0, 0, }; // holds the coordinate position as we draw each character tile of the string
438
    vec2_t extent = { 0, 0, }; // holds the x-width of each character and the greatest y-height of each line
438
    vec2_t extent = { 0, 0, }; // holds the x-width of each character and the greatest y-height of each line
439
    vec2_t offset = { 0, 0, }; // temporary; holds the last movement made in both directions
439
    vec2_t offset = { 0, 0, }; // temporary; holds the last movement made in both directions
440
440
441
    int32_t tile;
441
    int32_t tile;
442
    char t;
442
    char t;
443
443
444
    // set the start and end points depending on direction
444
    // set the start and end points depending on direction
445
    int32_t iter = (f & TEXT_BACKWARDS) ? -1 : 1; // iteration direction
445
    int32_t iter = (f & TEXT_BACKWARDS) ? -1 : 1; // iteration direction
446
446
447
    const char *end;
447
    const char *end;
448
    const char *text;
448
    const char *text;
449
449
450
    if (str == NULL)
450
    if (str == NULL)
451
        return size;
451
        return size;
452
452
453
    end = (f & TEXT_BACKWARDS) ? str-1 : Bstrchr(str,'\0');
453
    end = (f & TEXT_BACKWARDS) ? str-1 : Bstrchr(str,'\0');
454
    text = (f & TEXT_BACKWARDS) ? Bstrchr(str,'\0')-1 : str;
454
    text = (f & TEXT_BACKWARDS) ? Bstrchr(str,'\0')-1 : str;
455
455
456
    // optimization: justification in both directions
456
    // optimization: justification in both directions
457
    if ((f & TEXT_XJUSTIFY) && (f & TEXT_YJUSTIFY))
457
    if ((f & TEXT_XJUSTIFY) && (f & TEXT_YJUSTIFY))
458
    {
458
    {
459
        size.x = xbetween;
459
        size.x = xbetween;
460
        size.y = ybetween;
460
        size.y = ybetween;
461
        return size;
461
        return size;
462
    }
462
    }
463
463
464
    // for best results, we promote 320x200 coordinates to full precision before any math
464
    // for best results, we promote 320x200 coordinates to full precision before any math
465
    if (!(o & ROTATESPRITE_FULL16))
465
    if (!(o & ROTATESPRITE_FULL16))
466
    {
466
    {
467
        x <<= 16;
467
        x <<= 16;
468
        y <<= 16;
468
        y <<= 16;
469
        xspace <<= 16;
469
        xspace <<= 16;
470
        yline <<= 16;
470
        yline <<= 16;
471
        xbetween <<= 16;
471
        xbetween <<= 16;
472
        ybetween <<= 16;
472
        ybetween <<= 16;
473
    }
473
    }
474
    // coordinate values should be shifted left by 16
474
    // coordinate values should be shifted left by 16
475
475
476
    // handle zooming where applicable
476
    // handle zooming where applicable
477
    xspace = scale(xspace, z, 65536);
477
    xspace = scale(xspace, z, 65536);
478
    yline = scale(yline, z, 65536);
478
    yline = scale(yline, z, 65536);
479
    xbetween = scale(xbetween, z, 65536);
479
    xbetween = scale(xbetween, z, 65536);
480
    ybetween = scale(ybetween, z, 65536);
480
    ybetween = scale(ybetween, z, 65536);
481
    // size/width/height/spacing/offset values should be multiplied or scaled by $z, zoom (since 100% is 65536, the same as 1<<16)
481
    // size/width/height/spacing/offset values should be multiplied or scaled by $z, zoom (since 100% is 65536, the same as 1<<16)
482
482
483
    // loop through the string
483
    // loop through the string
484
    while ((t = *text) && text != end)
484
    while ((t = *text) && text != end)
485
    {
485
    {
486
        // handle escape sequences
486
        // handle escape sequences
487
        if (t == '^' && Bisdigit(*(text+iter)) && !(f & TEXT_LITERALESCAPE))
487
        if (t == '^' && Bisdigit(*(text+iter)) && !(f & TEXT_LITERALESCAPE))
488
        {
488
        {
489
            text += iter + iter;
489
            text += iter + iter;
490
            if (Bisdigit(*text))
490
            if (Bisdigit(*text))
491
                text += iter;
491
                text += iter;
492
            continue;
492
            continue;
493
        }
493
        }
494
494
495
        // handle case bits
495
        // handle case bits
496
        if (f & TEXT_UPPERCASE)
496
        if (f & TEXT_UPPERCASE)
497
        {
497
        {
498
            if (f & TEXT_INVERTCASE) // optimization...?
498
            if (f & TEXT_INVERTCASE) // optimization...?
499
            { // v^ important that these two ifs remain separate due to the else below
499
            { // v^ important that these two ifs remain separate due to the else below
500
                if (Bisupper(t))
500
                if (Bisupper(t))
501
                    t = Btolower(t);
501
                    t = Btolower(t);
502
            }
502
            }
503
            else if (Bislower(t))
503
            else if (Bislower(t))
504
                t = Btoupper(t);
504
                t = Btoupper(t);
505
        }
505
        }
506
        else if (f & TEXT_INVERTCASE)
506
        else if (f & TEXT_INVERTCASE)
507
        {
507
        {
508
            if (Bisupper(t))
508
            if (Bisupper(t))
509
                t = Btolower(t);
509
                t = Btolower(t);
510
            else if (Bislower(t))
510
            else if (Bislower(t))
511
                t = Btoupper(t);
511
                t = Btoupper(t);
512
        }
512
        }
513
513
514
        // translate the character to a tilenum
514
        // translate the character to a tilenum
515
        tile = G_GetStringTile(font, &t, f);
515
        tile = G_GetStringTile(font, &t, f);
516
516
517
        // reset this here because we haven't printed anything yet this loop
517
        // reset this here because we haven't printed anything yet this loop
518
        extent.x = 0;
518
        extent.x = 0;
519
519
520
        // reset this here because the act of printing something on this line means that we include the margin above in the total size
520
        // reset this here because the act of printing something on this line means that we include the margin above in the total size
521
        offset.y = 0;
521
        offset.y = 0;
522
522
523
        // handle each character itself in the context of screen drawing
523
        // handle each character itself in the context of screen drawing
524
        switch (t)
524
        switch (t)
525
        {
525
        {
526
            case '\t':
526
            case '\t':
527
            case ' ':
527
            case ' ':
528
                // width
528
                // width
529
                extent.x = xspace;
529
                extent.x = xspace;
530
530
531
                if (f & (TEXT_INTERNALSPACE|TEXT_TILESPACE))
531
                if (f & (TEXT_INTERNALSPACE|TEXT_TILESPACE))
532
                {
532
                {
533
                    char space = '.'; // this is subject to change as an implementation detail
533
                    char space = '.'; // this is subject to change as an implementation detail
534
                    if (f & TEXT_TILESPACE)
534
                    if (f & TEXT_TILESPACE)
535
                        space = '\x7F'; // tile after '~'
535
                        space = '\x7F'; // tile after '~'
536
                    tile = G_GetStringTile(font, &space, f);
536
                    tile = G_GetStringTile(font, &space, f);
537
537
538
                    extent.x += (tilesiz[tile].x * z);
538
                    extent.x += (tilesiz[tile].x * z);
539
                }
539
                }
540
540
541
                // prepare the height // near-CODEDUP the other two near-CODEDUPs for this section
541
                // prepare the height // near-CODEDUP the other two near-CODEDUPs for this section
542
                {
542
                {
543
                    int32_t tempyextent = yline;
543
                    int32_t tempyextent = yline;
544
544
545
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
545
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
546
                    {
546
                    {
547
                        char line = 'A'; // this is subject to change as an implementation detail
547
                        char line = 'A'; // this is subject to change as an implementation detail
548
                        if (f & TEXT_TILELINE)
548
                        if (f & TEXT_TILELINE)
549
                            line = '\x7F'; // tile after '~'
549
                            line = '\x7F'; // tile after '~'
550
                        tile = G_GetStringTile(font, &line, f);
550
                        tile = G_GetStringTile(font, &line, f);
551
551
552
                        tempyextent += tilesiz[tile].y * z;
552
                        tempyextent += tilesiz[tile].y * z;
553
                    }
553
                    }
554
554
555
                    SetIfGreater(&extent.y, tempyextent);
555
                    SetIfGreater(&extent.y, tempyextent);
556
                }
556
                }
557
557
558
                if (t == '\t')
558
                if (t == '\t')
559
                    extent.x <<= 2; // *= 4
559
                    extent.x <<= 2; // *= 4
560
560
561
                break;
561
                break;
562
562
563
            case '\n': // near-CODEDUP "if (wrap)"
563
            case '\n': // near-CODEDUP "if (wrap)"
564
                extent.x = 0;
564
                extent.x = 0;
565
565
566
                // save the position
566
                // save the position
567
                if (!(f & TEXT_XOFFSETZERO)) // we want the entire offset to count as the character width
567
                if (!(f & TEXT_XOFFSETZERO)) // we want the entire offset to count as the character width
568
                    pos.x -= offset.x;
568
                    pos.x -= offset.x;
569
                SetIfGreater(&size.x, pos.x);
569
                SetIfGreater(&size.x, pos.x);
570
570
571
                // reset the position
571
                // reset the position
572
                pos.x = 0;
572
                pos.x = 0;
573
573
574
                // prepare the height
574
                // prepare the height
575
                {
575
                {
576
                    int32_t tempyextent = yline;
576
                    int32_t tempyextent = yline;
577
577
578
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
578
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
579
                    {
579
                    {
580
                        char line = 'A'; // this is subject to change as an implementation detail
580
                        char line = 'A'; // this is subject to change as an implementation detail
581
                        if (f & TEXT_TILELINE)
581
                        if (f & TEXT_TILELINE)
582
                            line = '\x7F'; // tile after '~'
582
                            line = '\x7F'; // tile after '~'
583
                        tile = G_GetStringTile(font, &line, f);
583
                        tile = G_GetStringTile(font, &line, f);
584
584
585
                        tempyextent += tilesiz[tile].y * z;
585
                        tempyextent += tilesiz[tile].y * z;
586
                    }
586
                    }
587
587
588
                    SetIfGreater(&extent.y, tempyextent);
588
                    SetIfGreater(&extent.y, tempyextent);
589
                }
589
                }
590
590
591
                // move down the line height
591
                // move down the line height
592
                if (!(f & TEXT_YOFFSETZERO))
592
                if (!(f & TEXT_YOFFSETZERO))
593
                    pos.y += extent.y;
593
                    pos.y += extent.y;
594
594
595
                // reset the current height
595
                // reset the current height
596
                extent.y = 0;
596
                extent.y = 0;
597
597
598
                // line spacing
598
                // line spacing
599
                offset.y = (f & TEXT_YJUSTIFY) ? 0 : ybetween; // ternary to prevent overflow
599
                offset.y = (f & TEXT_YJUSTIFY) ? 0 : ybetween; // ternary to prevent overflow
600
                pos.y += offset.y;
600
                pos.y += offset.y;
601
601
602
                break;
602
                break;
603
603
604
            default:
604
            default:
605
                // width
605
                // width
606
                extent.x = tilesiz[tile].x * z;
606
                extent.x = tilesiz[tile].x * z;
607
607
608
                // obnoxious hardcoded functionality from gametext
608
                // obnoxious hardcoded functionality from gametext
609
                if (NUMHACKACTIVE)
609
                if (NUMHACKACTIVE)
610
                {
610
                {
611
                    char numeral = '0'; // this is subject to change as an implementation detail
611
                    char numeral = '0'; // this is subject to change as an implementation detail
612
                    extent.x = (tilesiz[G_GetStringTile(font, &numeral, f)].x-1) * z;
612
                    extent.x = (tilesiz[G_GetStringTile(font, &numeral, f)].x-1) * z;
613
                }
613
                }
614
614
615
                // height
615
                // height
616
                SetIfGreater(&extent.y, (tilesiz[tile].y * z));
616
                SetIfGreater(&extent.y, (tilesiz[tile].y * z));
617
617
618
                break;
618
                break;
619
        }
619
        }
620
620
621
        // incrementing the coordinate counters
621
        // incrementing the coordinate counters
622
        offset.x = 0;
622
        offset.x = 0;
623
623
624
        // advance the x coordinate
624
        // advance the x coordinate
625
        if (!(f & TEXT_XOFFSETZERO) || NUMHACKACTIVE)
625
        if (!(f & TEXT_XOFFSETZERO) || NUMHACKACTIVE)
626
            offset.x += extent.x;
626
            offset.x += extent.x;
627
627
628
        // account for text spacing
628
        // account for text spacing
629
        if (!NUMHACKACTIVE // this "if" line ONLY == replicating hardcoded stuff
629
        if (!NUMHACKACTIVE // this "if" line ONLY == replicating hardcoded stuff
630
            && t != '\n'
630
            && t != '\n'
631
            && !(f & TEXT_XJUSTIFY)) // to prevent overflow
631
            && !(f & TEXT_XJUSTIFY)) // to prevent overflow
632
            offset.x += xbetween;
632
            offset.x += xbetween;
633
633
634
        // line wrapping
634
        // line wrapping
635
        if ((f & TEXT_LINEWRAP) && !(f & TEXT_XRIGHT) && !(f & TEXT_XCENTER) && blockangle % 512 == 0)
635
        if ((f & TEXT_LINEWRAP) && !(f & TEXT_XRIGHT) && !(f & TEXT_XCENTER) && blockangle % 512 == 0)
636
        {
636
        {
637
            int32_t wrap = 0;
637
            int32_t wrap = 0;
638
            const int32_t ang = blockangle % 2048;
638
            const int32_t ang = blockangle % 2048;
639
639
640
            // this is the only place in qstrdim where angle actually affects direction, but only in the wrapping measurement
640
            // this is the only place in qstrdim where angle actually affects direction, but only in the wrapping measurement
641
            switch (ang)
641
            switch (ang)
642
            {
642
            {
643
                case 0:
643
                case 0:
644
                    wrap = (x + (pos.x + offset.x) > ((o & 2) ? (320<<16) : ((x2 - USERQUOTE_RIGHTOFFSET)<<16)));
644
                    wrap = (x + (pos.x + offset.x) > ((o & 2) ? (320<<16) : ((x2 - USERQUOTE_RIGHTOFFSET)<<16)));
645
                    break;
645
                    break;
646
                case 512:
646
                case 512:
647
                    wrap = (y + (pos.x + offset.x) > ((o & 2) ? (200<<16) : ((y2 - USERQUOTE_RIGHTOFFSET)<<16)));
647
                    wrap = (y + (pos.x + offset.x) > ((o & 2) ? (200<<16) : ((y2 - USERQUOTE_RIGHTOFFSET)<<16)));
648
                    break;
648
                    break;
649
                case 1024:
649
                case 1024:
650
                    wrap = (x - (pos.x + offset.x) < ((o & 2) ? 0 : ((x1 + USERQUOTE_RIGHTOFFSET)<<16)));
650
                    wrap = (x - (pos.x + offset.x) < ((o & 2) ? 0 : ((x1 + USERQUOTE_RIGHTOFFSET)<<16)));
651
                    break;
651
                    break;
652
                case 1536:
652
                case 1536:
653
                    wrap = (y - (pos.x + offset.x) < ((o & 2) ? 0 : ((y1 + USERQUOTE_RIGHTOFFSET)<<16)));
653
                    wrap = (y - (pos.x + offset.x) < ((o & 2) ? 0 : ((y1 + USERQUOTE_RIGHTOFFSET)<<16)));
654
                    break;
654
                    break;
655
            }
655
            }
656
            if (wrap) // near-CODEDUP "case '\n':"
656
            if (wrap) // near-CODEDUP "case '\n':"
657
            {
657
            {
658
                // save the position
658
                // save the position
659
                SetIfGreater(&size.x, pos.x);
659
                SetIfGreater(&size.x, pos.x);
660
660
661
                // reset the position
661
                // reset the position
662
                pos.x = 0;
662
                pos.x = 0;
663
663
664
                // prepare the height
664
                // prepare the height
665
                {
665
                {
666
                    int32_t tempyextent = yline;
666
                    int32_t tempyextent = yline;
667
667
668
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
668
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
669
                    {
669
                    {
670
                        char line = 'A'; // this is subject to change as an implementation detail
670
                        char line = 'A'; // this is subject to change as an implementation detail
671
                        if (f & TEXT_TILELINE)
671
                        if (f & TEXT_TILELINE)
672
                            line = '\x7F'; // tile after '~'
672
                            line = '\x7F'; // tile after '~'
673
                        tile = G_GetStringTile(font, &line, f);
673
                        tile = G_GetStringTile(font, &line, f);
674
674
675
                        tempyextent += tilesiz[tile].y * z;
675
                        tempyextent += tilesiz[tile].y * z;
676
                    }
676
                    }
677
677
678
                    SetIfGreater(&extent.y, tempyextent);
678
                    SetIfGreater(&extent.y, tempyextent);
679
                }
679
                }
680
680
681
                // move down the line height
681
                // move down the line height
682
                if (!(f & TEXT_YOFFSETZERO))
682
                if (!(f & TEXT_YOFFSETZERO))
683
                    pos.y += extent.y;
683
                    pos.y += extent.y;
684
684
685
                // reset the current height
685
                // reset the current height
686
                extent.y = 0;
686
                extent.y = 0;
687
687
688
                // line spacing
688
                // line spacing
689
                offset.y = (f & TEXT_YJUSTIFY) ? 0 : ybetween; // ternary to prevent overflow
689
                offset.y = (f & TEXT_YJUSTIFY) ? 0 : ybetween; // ternary to prevent overflow
690
                pos.y += offset.y;
690
                pos.y += offset.y;
691
            }
691
            }
692
            else
692
            else
693
                pos.x += offset.x;
693
                pos.x += offset.x;
694
        }
694
        }
695
        else
695
        else
696
            pos.x += offset.x;
696
            pos.x += offset.x;
697
697
698
        // save some trouble with calculation in case the line breaks
698
        // save some trouble with calculation in case the line breaks
699
        if (!(f & TEXT_XOFFSETZERO) || NUMHACKACTIVE)
699
        if (!(f & TEXT_XOFFSETZERO) || NUMHACKACTIVE)
700
            offset.x -= extent.x;
700
            offset.x -= extent.x;
701
701
702
        // iterate to the next character in the string
702
        // iterate to the next character in the string
703
        text += iter;
703
        text += iter;
704
    }
704
    }
705
705
706
    // calculate final size
706
    // calculate final size
707
    if (!(f & TEXT_XOFFSETZERO))
707
    if (!(f & TEXT_XOFFSETZERO))
708
        pos.x -= offset.x;
708
        pos.x -= offset.x;
709
709
710
    if (!(f & TEXT_YOFFSETZERO))
710
    if (!(f & TEXT_YOFFSETZERO))
711
    {
711
    {
712
        pos.y -= offset.y;
712
        pos.y -= offset.y;
713
        pos.y += extent.y;
713
        pos.y += extent.y;
714
    }
714
    }
715
    else
715
    else
716
        pos.y += ybetween;
716
        pos.y += ybetween;
717
717
718
    SetIfGreater(&size.x, pos.x);
718
    SetIfGreater(&size.x, pos.x);
719
    SetIfGreater(&size.y, pos.y);
719
    SetIfGreater(&size.y, pos.y);
720
720
721
    // justification where only one of the two directions is set, so we have to iterate
721
    // justification where only one of the two directions is set, so we have to iterate
722
    if (f & TEXT_XJUSTIFY)
722
    if (f & TEXT_XJUSTIFY)
723
        size.x = xbetween;
723
        size.x = xbetween;
724
    if (f & TEXT_YJUSTIFY)
724
    if (f & TEXT_YJUSTIFY)
725
        size.y = ybetween;
725
        size.y = ybetween;
726
726
727
    // return values in the same manner we receive them
727
    // return values in the same manner we receive them
728
    if (!(o & ROTATESPRITE_FULL16))
728
    if (!(o & ROTATESPRITE_FULL16))
729
    {
729
    {
730
        size.x >>= 16;
730
        size.x >>= 16;
731
        size.y >>= 16;
731
        size.y >>= 16;
732
    }
732
    }
733
733
734
    return size;
734
    return size;
735
}
735
}
736
736
737
void G_AddCoordsFromRotation(vec2_t *coords, const vec2_t *unitDirection, const int32_t magnitude)
737
void G_AddCoordsFromRotation(vec2_t *coords, const vec2_t *unitDirection, const int32_t magnitude)
738
{
738
{
739
    coords->x += scale(magnitude, unitDirection->x, 16384);
739
    coords->x += scale(magnitude, unitDirection->x, 16384);
740
    coords->y += scale(magnitude, unitDirection->y, 16384);
740
    coords->y += scale(magnitude, unitDirection->y, 16384);
741
}
741
}
742
742
743
// screentext
743
// screentext
744
vec2_t G_ScreenText(const int32_t font,
744
vec2_t G_ScreenText(const int32_t font,
745
                    int32_t x, int32_t y, const int32_t z, const int32_t blockangle, const int32_t charangle,
745
                    int32_t x, int32_t y, const int32_t z, const int32_t blockangle, const int32_t charangle,
746
                    const char *str, const int32_t shade, int32_t pal, int32_t o, int32_t alpha,
746
                    const char *str, const int32_t shade, int32_t pal, int32_t o, int32_t alpha,
747
                    int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween, const int32_t f,
747
                    int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween, const int32_t f,
748
                    const int32_t x1, const int32_t y1, const int32_t x2, const int32_t y2)
748
                    const int32_t x1, const int32_t y1, const int32_t x2, const int32_t y2)
749
{
749
{
750
    vec2_t size = { 0, 0, }; // eventually the return value
750
    vec2_t size = { 0, 0, }; // eventually the return value
751
    vec2_t origin = { 0, 0, }; // where to start, depending on the alignment
751
    vec2_t origin = { 0, 0, }; // where to start, depending on the alignment
752
    vec2_t pos = { 0, 0, }; // holds the coordinate position as we draw each character tile of the string
752
    vec2_t pos = { 0, 0, }; // holds the coordinate position as we draw each character tile of the string
753
    vec2_t extent = { 0, 0, }; // holds the x-width of each character and the greatest y-height of each line
753
    vec2_t extent = { 0, 0, }; // holds the x-width of each character and the greatest y-height of each line
754
    const vec2_t Xdirection = { sintable[(blockangle+512)&2047], sintable[blockangle&2047], };
754
    const vec2_t Xdirection = { sintable[(blockangle+512)&2047], sintable[blockangle&2047], };
755
    const vec2_t Ydirection = { sintable[(blockangle+1024)&2047], sintable[(blockangle+512)&2047], };
755
    const vec2_t Ydirection = { sintable[(blockangle+1024)&2047], sintable[(blockangle+512)&2047], };
756
756
757
    int32_t blendidx=0, tile;
757
    int32_t blendidx=0, tile;
758
    char t;
758
    char t;
759
759
760
    // set the start and end points depending on direction
760
    // set the start and end points depending on direction
761
    int32_t iter = (f & TEXT_BACKWARDS) ? -1 : 1; // iteration direction
761
    int32_t iter = (f & TEXT_BACKWARDS) ? -1 : 1; // iteration direction
762
762
763
    const char *end;
763
    const char *end;
764
    const char *text;
764
    const char *text;
765
765
766
    if (str == NULL)
766
    if (str == NULL)
767
        return size;
767
        return size;
768
768
769
    NEG_ALPHA_TO_BLEND(alpha, blendidx, o);
769
    NEG_ALPHA_TO_BLEND(alpha, blendidx, o);
770
770
771
    end = (f & TEXT_BACKWARDS) ? str-1 : Bstrchr(str,'\0');
771
    end = (f & TEXT_BACKWARDS) ? str-1 : Bstrchr(str,'\0');
772
    text = (f & TEXT_BACKWARDS) ? Bstrchr(str,'\0')-1 : str;
772
    text = (f & TEXT_BACKWARDS) ? Bstrchr(str,'\0')-1 : str;
773
773
774
    // for best results, we promote 320x200 coordinates to full precision before any math
774
    // for best results, we promote 320x200 coordinates to full precision before any math
775
    if (!(o & ROTATESPRITE_FULL16))
775
    if (!(o & ROTATESPRITE_FULL16))
776
    {
776
    {
777
        x <<= 16;
777
        x <<= 16;
778
        y <<= 16;
778
        y <<= 16;
779
        xspace <<= 16;
779
        xspace <<= 16;
780
        yline <<= 16;
780
        yline <<= 16;
781
        xbetween <<= 16;
781
        xbetween <<= 16;
782
        ybetween <<= 16;
782
        ybetween <<= 16;
783
    }
783
    }
784
    // coordinate values should be shifted left by 16
784
    // coordinate values should be shifted left by 16
785
785
786
    // eliminate conflicts, necessary here to get the correct size value
786
    // eliminate conflicts, necessary here to get the correct size value
787
    // especially given justification's special handling in G_ScreenTextSize()
787
    // especially given justification's special handling in G_ScreenTextSize()
788
    if ((f & TEXT_XRIGHT) || (f & TEXT_XCENTER) || (f & TEXT_XJUSTIFY) || (f & TEXT_YJUSTIFY) || blockangle % 512 != 0)
788
    if ((f & TEXT_XRIGHT) || (f & TEXT_XCENTER) || (f & TEXT_XJUSTIFY) || (f & TEXT_YJUSTIFY) || blockangle % 512 != 0)
789
        o &= ~TEXT_LINEWRAP;
789
        o &= ~TEXT_LINEWRAP;
790
790
791
    // size is the return value, and we need it for alignment
791
    // size is the return value, and we need it for alignment
792
    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);
792
    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);
793
793
794
    // handle zooming where applicable
794
    // handle zooming where applicable
795
    xspace = scale(xspace, z, 65536);
795
    xspace = scale(xspace, z, 65536);
796
    yline = scale(yline, z, 65536);
796
    yline = scale(yline, z, 65536);
797
    xbetween = scale(xbetween, z, 65536);
797
    xbetween = scale(xbetween, z, 65536);
798
    ybetween = scale(ybetween, z, 65536);
798
    ybetween = scale(ybetween, z, 65536);
799
    // size/width/height/spacing/offset values should be multiplied or scaled by $z, zoom (since 100% is 65536, the same as 1<<16)
799
    // size/width/height/spacing/offset values should be multiplied or scaled by $z, zoom (since 100% is 65536, the same as 1<<16)
800
800
801
    // alignment
801
    // alignment
802
    // near-CODEDUP "case '\n':"
802
    // near-CODEDUP "case '\n':"
803
    {
803
    {
804
        int32_t lines = G_GetStringNumLines(text, end, iter);
804
        int32_t lines = G_GetStringNumLines(text, end, iter);
805
805
806
        if ((f & TEXT_XJUSTIFY) || (f & TEXT_XRIGHT) || (f & TEXT_XCENTER))
806
        if ((f & TEXT_XJUSTIFY) || (f & TEXT_XRIGHT) || (f & TEXT_XCENTER))
807
        {
807
        {
808
            const int32_t length = G_GetStringLineLength(text, end, iter);
808
            const int32_t length = G_GetStringLineLength(text, end, iter);
809
809
810
            int32_t linewidth = size.x;
810
            int32_t linewidth = size.x;
811
811
812
            if (lines != 1)
812
            if (lines != 1)
813
            {
813
            {
814
                char *line = G_GetSubString(text, end, iter, length);
814
                char *line = G_GetSubString(text, end, iter, length);
815
815
816
                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;
816
                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;
817
817
818
                Bfree(line);
818
                Bfree(line);
819
            }
819
            }
820
820
821
            if (f & TEXT_XJUSTIFY)
821
            if (f & TEXT_XJUSTIFY)
822
            {
822
            {
823
                size.x = xbetween;
823
                size.x = xbetween;
824
824
825
                xbetween = (length == 1) ? 0 : tabledivide32_noinline((xbetween - linewidth), (length - 1));
825
                xbetween = (length == 1) ? 0 : tabledivide32_noinline((xbetween - linewidth), (length - 1));
826
826
827
                linewidth = size.x;
827
                linewidth = size.x;
828
            }
828
            }
829
829
830
            if (f & TEXT_XRIGHT)
830
            if (f & TEXT_XRIGHT)
831
                origin.x = -linewidth;
831
                origin.x = -linewidth;
832
            else if (f & TEXT_XCENTER)
832
            else if (f & TEXT_XCENTER)
833
                origin.x = -(linewidth / 2);
833
                origin.x = -(linewidth / 2);
834
        }
834
        }
835
835
836
        if (f & TEXT_YJUSTIFY)
836
        if (f & TEXT_YJUSTIFY)
837
        {
837
        {
838
            const int32_t tempswap = ybetween;
838
            const int32_t tempswap = ybetween;
839
            ybetween = (lines == 1) ? 0 : tabledivide32_noinline(ybetween - size.y, lines - 1);
839
            ybetween = (lines == 1) ? 0 : tabledivide32_noinline(ybetween - size.y, lines - 1);
840
            size.y = tempswap;
840
            size.y = tempswap;
841
        }
841
        }
842
842
843
        if (f & TEXT_YBOTTOM)
843
        if (f & TEXT_YBOTTOM)
844
            origin.y = -size.y;
844
            origin.y = -size.y;
845
        else if (f & TEXT_YCENTER)
845
        else if (f & TEXT_YCENTER)
846
            origin.y = -(size.y / 2);
846
            origin.y = -(size.y / 2);
847
    }
847
    }
848
848
849
    // loop through the string
849
    // loop through the string
850
    while ((t = *text) && text != end)
850
    while ((t = *text) && text != end)
851
    {
851
    {
852
        int32_t orientation = o;
852
        int32_t orientation = o;
853
        int32_t angle = blockangle + charangle;
853
        int32_t angle = blockangle + charangle;
854
854
855
        // handle escape sequences
855
        // handle escape sequences
856
        if (t == '^' && Bisdigit(*(text+iter)) && !(f & TEXT_LITERALESCAPE))
856
        if (t == '^' && Bisdigit(*(text+iter)) && !(f & TEXT_LITERALESCAPE))
857
        {
857
        {
858
            char smallbuf[4];
858
            char smallbuf[4];
859
859
860
            text += iter;
860
            text += iter;
861
            smallbuf[0] = *text;
861
            smallbuf[0] = *text;
862
862
863
            text += iter;
863
            text += iter;
864
            if (Bisdigit(*text))
864
            if (Bisdigit(*text))
865
            {
865
            {
866
                smallbuf[1] = *text;
866
                smallbuf[1] = *text;
867
                smallbuf[2] = '\0';
867
                smallbuf[2] = '\0';
868
                text += iter;
868
                text += iter;
869
            }
869
            }
870
            else
870
            else
871
                smallbuf[1] = '\0';
871
                smallbuf[1] = '\0';
872
872
873
            if (!(f & TEXT_IGNOREESCAPE))
873
            if (!(f & TEXT_IGNOREESCAPE))
874
                pal = Batoi(smallbuf);
874
                pal = Batoi(smallbuf);
875
875
876
            continue;
876
            continue;
877
        }
877
        }
878
878
879
        // handle case bits
879
        // handle case bits
880
        if (f & TEXT_UPPERCASE)
880
        if (f & TEXT_UPPERCASE)
881
        {
881
        {
882
            if (f & TEXT_INVERTCASE) // optimization...?
882
            if (f & TEXT_INVERTCASE) // optimization...?
883
            { // v^ important that these two ifs remain separate due to the else below
883
            { // v^ important that these two ifs remain separate due to the else below
884
                if (Bisupper(t))
884
                if (Bisupper(t))
885
                    t = Btolower(t);
885
                    t = Btolower(t);
886
            }
886
            }
887
            else if (Bislower(t))
887
            else if (Bislower(t))
888
                t = Btoupper(t);
888
                t = Btoupper(t);
889
        }
889
        }
890
        else if (f & TEXT_INVERTCASE)
890
        else if (f & TEXT_INVERTCASE)
891
        {
891
        {
892
            if (Bisupper(t))
892
            if (Bisupper(t))
893
                t = Btolower(t);
893
                t = Btolower(t);
894
            else if (Bislower(t))
894
            else if (Bislower(t))
895
                t = Btoupper(t);
895
                t = Btoupper(t);
896
        }
896
        }
897
897
898
        // translate the character to a tilenum
898
        // translate the character to a tilenum
899
        tile = G_GetStringTile(font, &t, f);
899
        tile = G_GetStringTile(font, &t, f);
900
900
901
        switch (t)
901
        switch (t)
902
        {
902
        {
903
            case '\t':
903
            case '\t':
904
            case ' ':
904
            case ' ':
905
            case '\n':
905
            case '\n':
906
            case '\x7F':
906
            case '\x7F':
907
                break;
907
                break;
908
908
909
            default:
909
            default:
910
            {
910
            {
911
                vec2_t location = { x, y, };
911
                vec2_t location = { x, y, };
912
912
913
                G_AddCoordsFromRotation(&location, &Xdirection, origin.x);
913
                G_AddCoordsFromRotation(&location, &Xdirection, origin.x);
914
                G_AddCoordsFromRotation(&location, &Ydirection, origin.y);
914
                G_AddCoordsFromRotation(&location, &Ydirection, origin.y);
915
915
916
                G_AddCoordsFromRotation(&location, &Xdirection, pos.x);
916
                G_AddCoordsFromRotation(&location, &Xdirection, pos.x);
917
                G_AddCoordsFromRotation(&location, &Ydirection, pos.y);
917
                G_AddCoordsFromRotation(&location, &Ydirection, pos.y);
918
918
919
                rotatesprite_(location.x, location.y, z, angle, tile, shade, pal, orientation, alpha, blendidx, x1, y1, x2, y2);
919
                rotatesprite_(location.x, location.y, z, angle, tile, shade, pal, orientation, alpha, blendidx, x1, y1, x2, y2);
920
920
921
                break;
921
                break;
922
            }
922
            }
923
        }
923
        }
924
924
925
        // reset this here because we haven't printed anything yet this loop
925
        // reset this here because we haven't printed anything yet this loop
926
        extent.x = 0;
926
        extent.x = 0;
927
927
928
        // handle each character itself in the context of screen drawing
928
        // handle each character itself in the context of screen drawing
929
        switch (t)
929
        switch (t)
930
        {
930
        {
931
            case '\t':
931
            case '\t':
932
            case ' ':
932
            case ' ':
933
                // width
933
                // width
934
                extent.x = xspace;
934
                extent.x = xspace;
935
935
936
                if (f & (TEXT_INTERNALSPACE|TEXT_TILESPACE))
936
                if (f & (TEXT_INTERNALSPACE|TEXT_TILESPACE))
937
                {
937
                {
938
                    char space = '.'; // this is subject to change as an implementation detail
938
                    char space = '.'; // this is subject to change as an implementation detail
939
                    if (f & TEXT_TILESPACE)
939
                    if (f & TEXT_TILESPACE)
940
                        space = '\x7F'; // tile after '~'
940
                        space = '\x7F'; // tile after '~'
941
                    tile = G_GetStringTile(font, &space, f);
941
                    tile = G_GetStringTile(font, &space, f);
942
942
943
                    extent.x += (tilesiz[tile].x * z);
943
                    extent.x += (tilesiz[tile].x * z);
944
                }
944
                }
945
945
946
                // prepare the height // near-CODEDUP the other two near-CODEDUPs for this section
946
                // prepare the height // near-CODEDUP the other two near-CODEDUPs for this section
947
                {
947
                {
948
                    int32_t tempyextent = yline;
948
                    int32_t tempyextent = yline;
949
949
950
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
950
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
951
                    {
951
                    {
952
                        char line = 'A'; // this is subject to change as an implementation detail
952
                        char line = 'A'; // this is subject to change as an implementation detail
953
                        if (f & TEXT_TILELINE)
953
                        if (f & TEXT_TILELINE)
954
                            line = '\x7F'; // tile after '~'
954
                            line = '\x7F'; // tile after '~'
955
                        tile = G_GetStringTile(font, &line, f);
955
                        tile = G_GetStringTile(font, &line, f);
956
956
957
                        tempyextent += tilesiz[tile].y * z;
957
                        tempyextent += tilesiz[tile].y * z;
958
                    }
958
                    }
959
959
960
                    SetIfGreater(&extent.y, tempyextent);
960
                    SetIfGreater(&extent.y, tempyextent);
961
                }
961
                }
962
962
963
                if (t == '\t')
963
                if (t == '\t')
964
                    extent.x <<= 2; // *= 4
964
                    extent.x <<= 2; // *= 4
965
965
966
                break;
966
                break;
967
967
968
            case '\n': // near-CODEDUP "if (wrap)"
968
            case '\n': // near-CODEDUP "if (wrap)"
969
                extent.x = 0;
969
                extent.x = 0;
970
970
971
                // reset the position
971
                // reset the position
972
                pos.x = 0;
972
                pos.x = 0;
973
973
974
                // prepare the height
974
                // prepare the height
975
                {
975
                {
976
                    int32_t tempyextent = yline;
976
                    int32_t tempyextent = yline;
977
977
978
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
978
                    if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
979
                    {
979
                    {
980
                        char line = 'A'; // this is subject to change as an implementation detail
980
                        char line = 'A'; // this is subject to change as an implementation detail
981
                        if (f & TEXT_TILELINE)
981
                        if (f & TEXT_TILELINE)
982
                            line = '\x7F'; // tile after '~'
982
                            line = '\x7F'; // tile after '~'
983
                        tile = G_GetStringTile(font, &line, f);
983
                        tile = G_GetStringTile(font, &line, f);
984
984
985
                        tempyextent += tilesiz[tile].y * z;
985
                        tempyextent += tilesiz[tile].y * z;
986
                    }
986
                    }
987
987
988
                    SetIfGreater(&extent.y, tempyextent);
988
                    SetIfGreater(&extent.y, tempyextent);
989
                }
989
                }
990
990
991
                // move down the line height
991
                // move down the line height
992
                if (!(f & TEXT_YOFFSETZERO))
992
                if (!(f & TEXT_YOFFSETZERO))
993
                    pos.y += extent.y;
993
                    pos.y += extent.y;
994
994
995
                // reset the current height
995
                // reset the current height
996
                extent.y = 0;
996
                extent.y = 0;
997
997
998
                // line spacing
998
                // line spacing
999
                pos.y += ybetween;
999
                pos.y += ybetween;
1000
1000
1001
                // near-CODEDUP "alignments"
1001
                // near-CODEDUP "alignments"
1002
                if ((f & TEXT_XJUSTIFY) || (f & TEXT_XRIGHT) || (f & TEXT_XCENTER))
1002
                if ((f & TEXT_XJUSTIFY) || (f & TEXT_XRIGHT) || (f & TEXT_XCENTER))
1003
                {
1003
                {
1004
                    const int32_t length = G_GetStringLineLength(text+1, end, iter);
1004
                    const int32_t length = G_GetStringLineLength(text+1, end, iter);
1005
1005
1006
                    char *line = G_GetSubString(text+1, end, iter, length);
1006
                    char *line = G_GetSubString(text+1, end, iter, length);
1007
1007
1008
                    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;
1008
                    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;
1009
1009
1010
                    Bfree(line);
1010
                    Bfree(line);
1011
1011
1012
                    if (f & TEXT_XJUSTIFY)
1012
                    if (f & TEXT_XJUSTIFY)
1013
                    {
1013
                    {
1014
                        xbetween = (length == 1) ? 0 : tabledivide32_noinline(xbetween - linewidth, length - 1);
1014
                        xbetween = (length == 1) ? 0 : tabledivide32_noinline(xbetween - linewidth, length - 1);
1015
1015
1016
                        linewidth = size.x;
1016
                        linewidth = size.x;
1017
                    }
1017
                    }
1018
1018
1019
                    if (f & TEXT_XRIGHT)
1019
                    if (f & TEXT_XRIGHT)
1020
                        origin.x = -linewidth;
1020
                        origin.x = -linewidth;
1021
                    else if (f & TEXT_XCENTER)
1021
                    else if (f & TEXT_XCENTER)
1022
                        origin.x = -(linewidth / 2);
1022
                        origin.x = -(linewidth / 2);
1023
                }
1023
                }
1024
1024
1025
                break;
1025
                break;
1026
1026
1027
            default:
1027
            default:
1028
                // width
1028
                // width
1029
                extent.x = tilesiz[tile].x * z;
1029
                extent.x = tilesiz[tile].x * z;
1030
1030
1031
                // obnoxious hardcoded functionality from gametext
1031
                // obnoxious hardcoded functionality from gametext
1032
                if (NUMHACKACTIVE)
1032
                if (NUMHACKACTIVE)
1033
                {
1033
                {
1034
                    char numeral = '0'; // this is subject to change as an implementation detail
1034
                    char numeral = '0'; // this is subject to change as an implementation detail
1035
                    extent.x = (tilesiz[G_GetStringTile(font, &numeral, f)].x-1) * z;
1035
                    extent.x = (tilesiz[G_GetStringTile(font, &numeral, f)].x-1) * z;
1036
                }
1036
                }
1037
1037
1038
                // height
1038
                // height
1039
                SetIfGreater(&extent.y, (tilesiz[tile].y * z));
1039
                SetIfGreater(&extent.y, (tilesiz[tile].y * z));
1040
1040
1041
                break;
1041
                break;
1042
        }
1042
        }
1043
1043
1044
        // incrementing the coordinate counters
1044
        // incrementing the coordinate counters
1045
        {
1045
        {
1046
            int32_t xoffset = 0;
1046
            int32_t xoffset = 0;
1047
1047
1048
            // advance the x coordinate
1048
            // advance the x coordinate
1049
            if (!(f & TEXT_XOFFSETZERO) || NUMHACKACTIVE)
1049
            if (!(f & TEXT_XOFFSETZERO) || NUMHACKACTIVE)
1050
                xoffset += extent.x;
1050
                xoffset += extent.x;
1051
1051
1052
            // account for text spacing
1052
            // account for text spacing
1053
            if (!NUMHACKACTIVE // this "if" line ONLY == replicating hardcoded stuff
1053
            if (!NUMHACKACTIVE // this "if" line ONLY == replicating hardcoded stuff
1054
                && t != '\n')
1054
                && t != '\n')
1055
                xoffset += xbetween;
1055
                xoffset += xbetween;
1056
1056
1057
            // line wrapping
1057
            // line wrapping
1058
            if (f & TEXT_LINEWRAP)
1058
            if (f & TEXT_LINEWRAP)
1059
            {
1059
            {
1060
                int32_t wrap = 0;
1060
                int32_t wrap = 0;
1061
                const int32_t ang = blockangle % 2048;
1061
                const int32_t ang = blockangle % 2048;
1062
1062
1063
                // it's safe to make some assumptions and not go through G_AddCoordsFromRotation() since we limit to four directions
1063
                // it's safe to make some assumptions and not go through G_AddCoordsFromRotation() since we limit to four directions
1064
                switch (ang)
1064
                switch (ang)
1065
                {
1065
                {
1066
                    case 0:
1066
                    case 0:
1067
                        wrap = (x + (pos.x + xoffset) > ((orientation & 2) ? (320<<16) : ((x2 - USERQUOTE_RIGHTOFFSET)<<16)));
1067
                        wrap = (x + (pos.x + xoffset) > ((orientation & 2) ? (320<<16) : ((x2 - USERQUOTE_RIGHTOFFSET)<<16)));
1068
                        break;
1068
                        break;
1069
                    case 512:
1069
                    case 512:
1070
                        wrap = (y + (pos.x + xoffset) > ((orientation & 2) ? (200<<16) : ((y2 - USERQUOTE_RIGHTOFFSET)<<16)));
1070
                        wrap = (y + (pos.x + xoffset) > ((orientation & 2) ? (200<<16) : ((y2 - USERQUOTE_RIGHTOFFSET)<<16)));
1071
                        break;
1071
                        break;
1072
                    case 1024:
1072
                    case 1024:
1073
                        wrap = (x - (pos.x + xoffset) < ((orientation & 2) ? 0 : ((x1 + USERQUOTE_RIGHTOFFSET)<<16)));
1073
                        wrap = (x - (pos.x + xoffset) < ((orientation & 2) ? 0 : ((x1 + USERQUOTE_RIGHTOFFSET)<<16)));
1074
                        break;
1074
                        break;
1075
                    case 1536:
1075
                    case 1536:
1076
                        wrap = (y - (pos.x + xoffset) < ((orientation & 2) ? 0 : ((y1 + USERQUOTE_RIGHTOFFSET)<<16)));
1076
                        wrap = (y - (pos.x + xoffset) < ((orientation & 2) ? 0 : ((y1 + USERQUOTE_RIGHTOFFSET)<<16)));
1077
                        break;
1077
                        break;
1078
                }
1078
                }
1079
                if (wrap) // near-CODEDUP "case '\n':"
1079
                if (wrap) // near-CODEDUP "case '\n':"
1080
                {
1080
                {
1081
                    // reset the position
1081
                    // reset the position
1082
                    pos.x = 0;
1082
                    pos.x = 0;
1083
1083
1084
                    // prepare the height
1084
                    // prepare the height
1085
                    {
1085
                    {
1086
                        int32_t tempyextent = yline;
1086
                        int32_t tempyextent = yline;
1087
1087
1088
                        if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
1088
                        if (f & (TEXT_INTERNALLINE|TEXT_TILELINE))
1089
                        {
1089
                        {
1090
                            char line = 'A'; // this is subject to change as an implementation detail
1090
                            char line = 'A'; // this is subject to change as an implementation detail
1091
                            if (f & TEXT_TILELINE)
1091
                            if (f & TEXT_TILELINE)
1092
                                line = '\x7F'; // tile after '~'
1092
                                line = '\x7F'; // tile after '~'
1093
                            tile = G_GetStringTile(font, &line, f);
1093
                            tile = G_GetStringTile(font, &line, f);
1094
1094
1095
                            tempyextent += tilesiz[tile].y * z;
1095
                            tempyextent += tilesiz[tile].y * z;
1096
                        }
1096
                        }
1097
1097
1098
                        SetIfGreater(&extent.y, tempyextent);
1098
                        SetIfGreater(&extent.y, tempyextent);
1099
                    }
1099
                    }
1100
1100
1101
                    // move down the line height
1101
                    // move down the line height
1102
                    if (!(f & TEXT_YOFFSETZERO))
1102
                    if (!(f & TEXT_YOFFSETZERO))
1103
                        pos.y += extent.y;
1103
                        pos.y += extent.y;
1104
1104
1105
                    // reset the current height
1105
                    // reset the current height
1106
                    extent.y = 0;
1106
                    extent.y = 0;
1107
1107
1108
                    // line spacing
1108
                    // line spacing
1109
                    pos.y += ybetween;
1109
                    pos.y += ybetween;
1110
                }
1110
                }
1111
                else
1111
                else
1112
                    pos.x += xoffset;
1112
                    pos.x += xoffset;
1113
            }
1113
            }
1114
            else
1114
            else
1115
                pos.x += xoffset;
1115
                pos.x += xoffset;
1116
        }
1116
        }
1117
1117
1118
        // iterate to the next character in the string
1118
        // iterate to the next character in the string
1119
        text += iter;
1119
        text += iter;
1120
    }
1120
    }
1121
1121
1122
    // return values in the same manner we receive them
1122
    // return values in the same manner we receive them
1123
    if (!(o & ROTATESPRITE_FULL16))
1123
    if (!(o & ROTATESPRITE_FULL16))
1124
    {
1124
    {
1125
        size.x >>= 16;
1125
        size.x >>= 16;
1126
        size.y >>= 16;
1126
        size.y >>= 16;
1127
    }
1127
    }
1128
1128
1129
    return size;
1129
    return size;
1130
}
1130
}
1131
1131
1132
vec2_t G_ScreenTextShadow(int32_t sx, int32_t sy,
1132
vec2_t G_ScreenTextShadow(int32_t sx, int32_t sy,
1133
                          const int32_t font,
1133
                          const int32_t font,
1134
                          int32_t x, int32_t y, const int32_t z, const int32_t blockangle, const int32_t charangle,
1134
                          int32_t x, int32_t y, const int32_t z, const int32_t blockangle, const int32_t charangle,
1135
                          const char *str, const int32_t shade, int32_t pal, int32_t o, const int32_t alpha,
1135
                          const char *str, const int32_t shade, int32_t pal, int32_t o, const int32_t alpha,
1136
                          int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween, const int32_t f,
1136
                          int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween, const int32_t f,
1137
                          const int32_t x1, const int32_t y1, const int32_t x2, const int32_t y2)
1137
                          const int32_t x1, const int32_t y1, const int32_t x2, const int32_t y2)
1138
{
1138
{
1139
    vec2_t size = { 0, 0, }; // eventually the return value
1139
    vec2_t size = { 0, 0, }; // eventually the return value
1140
1140
1141
    if (!(o & ROTATESPRITE_FULL16))
1141
    if (!(o & ROTATESPRITE_FULL16))
1142
    {
1142
    {
1143
        sx <<= 16;
1143
        sx <<= 16;
1144
        sy <<= 16;
1144
        sy <<= 16;
1145
        x <<= 16;
1145
        x <<= 16;
1146
        y <<= 16;
1146
        y <<= 16;
1147
        xspace <<= 16;
1147
        xspace <<= 16;
1148
        yline <<= 16;
1148
        yline <<= 16;
1149
        xbetween <<= 16;
1149
        xbetween <<= 16;
1150
        ybetween <<= 16;
1150
        ybetween <<= 16;
1151
    }
1151
    }
1152
1152
1153
    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);
1153
    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);
1154
1154
1155
    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);
1155
    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);
1156
1156
1157
    // return values in the same manner we receive them
1157
    // return values in the same manner we receive them
1158
    if (!(o & ROTATESPRITE_FULL16))
1158
    if (!(o & ROTATESPRITE_FULL16))
1159
    {
1159
    {
1160
        size.x >>= 16;
1160
        size.x >>= 16;
1161
        size.y >>= 16;
1161
        size.y >>= 16;
1162
    }
1162
    }
1163
1163
1164
    return size;
1164
    return size;
1165
}
1165
}
1166
1166
1167
// flags
1167
// flags
1168
//  4: small font, wrap strings?
1168
//  4: small font, wrap strings?
1169
int32_t G_PrintGameText(int32_t hack, int32_t tile, int32_t x,  int32_t y,  const char *t,
1169
int32_t G_PrintGameText(int32_t hack, int32_t tile, int32_t x,  int32_t y,  const char *t,
1170
                        int32_t s,    int32_t p,    int32_t o,
1170
                        int32_t s,    int32_t p,    int32_t o,
1171
                        int32_t x1,   int32_t y1,   int32_t x2, int32_t y2, int32_t z, int32_t a)
1171
                        int32_t x1,   int32_t y1,   int32_t x2, int32_t y2, int32_t z, int32_t a)
1172
{
1172
{
1173
    vec2_t dim;
1173
    vec2_t dim;
1174
    int32_t f = TEXT_GAMETEXTNUMHACK;
1174
    int32_t f = TEXT_GAMETEXTNUMHACK;
1175
    int32_t xbetween = 0;
1175
    int32_t xbetween = 0;
1176
    const int32_t orient = (hack & 4) || (hack & 1) ? (8|16|(o&1)|(o&32)) : (2|o);
1176
    const int32_t orient = (hack & 4) || (hack & 1) ? (8|16|(o&1)|(o&32)) : (2|o);
1177
1177
1178
    if (t == NULL)
1178
    if (t == NULL)
1179
        return -1;
1179
        return -1;
1180
1180
1181
    if (!(o & ROTATESPRITE_FULL16))
1181
    if (!(o & ROTATESPRITE_FULL16))
1182
    {
1182
    {
1183
        x <<= 16;
1183
        x <<= 16;
1184
        y <<= 16;
1184
        y <<= 16;
1185
    }
1185
    }
1186
1186
1187
    if (hack & 4)
1187
    if (hack & 4)
1188
    {
1188
    {
1189
        x = textsc(x);
1189
        x = textsc(x);
1190
        z = textsc(z);
1190
        z = textsc(z);
1191
        f |= TEXT_LINEWRAP;
1191
        f |= TEXT_LINEWRAP;
1192
    }
1192
    }
1193
1193
1194
    if (hack & 8)
1194
    if (hack & 8)
1195
    {
1195
    {
1196
        f |= TEXT_XOFFSETZERO;
1196
        f |= TEXT_XOFFSETZERO;
1197
        xbetween = 8;
1197
        xbetween = 8;
1198
    }
1198
    }
1199
1199
1200
    // order is important, this bit comes after the rest
1200
    // order is important, this bit comes after the rest
1201
    if ((hack & 2) && !NAM) // squishtext
1201
    if ((hack & 2) && !NAM) // squishtext
1202
        --xbetween;
1202
        --xbetween;
1203
1203
1204
    if (x == (160<<16))
1204
    if (x == (160<<16))
1205
        f |= TEXT_XCENTER;
1205
        f |= TEXT_XCENTER;
1206
1206
1207
    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);
1207
    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);
1208
1208
1209
    x += dim.x;
1209
    x += dim.x;
1210
1210
1211
    if (!(o & ROTATESPRITE_FULL16))
1211
    if (!(o & ROTATESPRITE_FULL16))
1212
        x >>= 16;
1212
        x >>= 16;
1213
1213
1214
    return x;
1214
    return x;
1215
}
1215
}
1216
1216
1217
int32_t G_GameTextLen(int32_t x,const char *t)
1217
int32_t G_GameTextLen(int32_t x,const char *t)
1218
{
1218
{
1219
    vec2_t dim;
1219
    vec2_t dim;
1220
1220
1221
    if (t == NULL)
1221
    if (t == NULL)
1222
        return -1;
1222
        return -1;
1223
1223
1224
    dim = G_ScreenTextSize(STARTALPHANUM, x, 0, textsc(65536L), 0, t, 2, 5, 8, 0, 0, TEXT_GAMETEXTNUMHACK, 0, 0, xdim-1, ydim-1);
1224
    dim = G_ScreenTextSize(STARTALPHANUM, x, 0, textsc(65536L), 0, t, 2, 5, 8, 0, 0, TEXT_GAMETEXTNUMHACK, 0, 0, xdim-1, ydim-1);
1225
1225
1226
    x += dim.x;
1226
    x += dim.x;
1227
1227
1228
    return x;
1228
    return x;
1229
}
1229
}
1230
1230
1231
// minitext_yofs: in hud_scale-independent, (<<16)-scaled, 0-200-normalized y coords,
1231
// minitext_yofs: in hud_scale-independent, (<<16)-scaled, 0-200-normalized y coords,
1232
// (sb&ROTATESPRITE_MAX) only.
1232
// (sb&ROTATESPRITE_MAX) only.
1233
static int32_t minitext_yofs = 0;
1233
static int32_t minitext_yofs = 0;
1234
int32_t minitext_lowercase = 0;
1234
int32_t minitext_lowercase = 0;
1235
int32_t minitext_(int32_t x,int32_t y,const char *t,int32_t s,int32_t p,int32_t sb)
1235
int32_t minitext_(int32_t x,int32_t y,const char *t,int32_t s,int32_t p,int32_t sb)
1236
{
1236
{
1237
    vec2_t dim;
1237
    vec2_t dim;
1238
    int32_t z = 65536L;
1238
    int32_t z = 65536L;
1239
    int32_t f = 0;
1239
    int32_t f = 0;
1240
1240
1241
    if (t == NULL)
1241
    if (t == NULL)
1242
    {
1242
    {
1243
        OSD_Printf("minitext: NULL text!\n");
1243
        OSD_Printf("minitext: NULL text!\n");
1244
        return 0;
1244
        return 0;
1245
    }
1245
    }
1246
1246
1247
    if (!(sb & ROTATESPRITE_FULL16))
1247
    if (!(sb & ROTATESPRITE_FULL16))
1248
    {
1248
    {
1249
        x<<=16;
1249
        x<<=16;
1250
        y<<=16;
1250
        y<<=16;
1251
    }
1251
    }
1252
1252
1253
    if (!minitext_lowercase)
1253
    if (!minitext_lowercase)
1254
        f |= TEXT_UPPERCASE;
1254
        f |= TEXT_UPPERCASE;
1255
1255
1256
    if (sb & ROTATESPRITE_MAX)
1256
    if (sb & ROTATESPRITE_MAX)
1257
    {
1257
    {
1258
        x = sbarx16(x);
1258
        x = sbarx16(x);
1259
        y = minitext_yofs+sbary16(y);
1259
        y = minitext_yofs+sbary16(y);
1260
        z = sbarsc(z);
1260
        z = sbarsc(z);
1261
    }
1261
    }
1262
1262
1263
    sb &= (ROTATESPRITE_MAX-1)|RS_CENTERORIGIN;
1263
    sb &= (ROTATESPRITE_MAX-1)|RS_CENTERORIGIN;
1264
1264
1265
    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);
1265
    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);
1266
1266
1267
    x += dim.x;
1267
    x += dim.x;
1268
1268
1269
    if (!(sb & ROTATESPRITE_FULL16))
1269
    if (!(sb & ROTATESPRITE_FULL16))
1270
        x >>= 16;
1270
        x >>= 16;
1271
1271
1272
    return x;
1272
    return x;
1273
}
1273
}
1274
1274
1275
void G_AddUserQuote(const char *daquote)
1275
void G_AddUserQuote(const char *daquote)
1276
{
1276
{
1277
    int32_t i;
1277
    int32_t i;
1278
1278
1279
    for (i=MAXUSERQUOTES-1; i>0; i--)
1279
    for (i=MAXUSERQUOTES-1; i>0; i--)
1280
    {
1280
    {
1281
        Bstrcpy(user_quote[i],user_quote[i-1]);
1281
        Bstrcpy(user_quote[i],user_quote[i-1]);
1282
        user_quote_time[i] = user_quote_time[i-1];
1282
        user_quote_time[i] = user_quote_time[i-1];
1283
    }
1283
    }
1284
    Bstrcpy(user_quote[0],daquote);
1284
    Bstrcpy(user_quote[0],daquote);
1285
    OSD_Printf("%s\n",daquote);
1285
    OSD_Printf("%s\n",daquote);
1286
1286
1287
    user_quote_time[0] = ud.msgdisptime;
1287
    user_quote_time[0] = ud.msgdisptime;
1288
    pub = NUMPAGES;
1288
    pub = NUMPAGES;
1289
}
1289
}
1290
1290
1291
void G_HandleSpecialKeys(void)
1291
void G_HandleSpecialKeys(void)
1292
{
1292
{
1293
    // we need CONTROL_GetInput in order to pick up joystick button presses
1293
    // we need CONTROL_GetInput in order to pick up joystick button presses
1294
    if (CONTROL_Started && !(g_player[myconnectindex].ps->gm & MODE_GAME))
1294
    if (CONTROL_Started && !(g_player[myconnectindex].ps->gm & MODE_GAME))
1295
    {
1295
    {
1296
        ControlInfo noshareinfo;
1296
        ControlInfo noshareinfo;
1297
        CONTROL_GetInput(&noshareinfo);
1297
        CONTROL_GetInput(&noshareinfo);
1298
    }
1298
    }
1299
1299
1300
//    CONTROL_ProcessBinds();
1300
//    CONTROL_ProcessBinds();
1301
1301
1302
    if (g_networkMode != NET_DEDICATED_SERVER && ALT_IS_PRESSED && KB_KeyPressed(sc_Enter))
1302
    if (g_networkMode != NET_DEDICATED_SERVER && ALT_IS_PRESSED && KB_KeyPressed(sc_Enter))
1303
    {
1303
    {
1304
        if (setgamemode(!ud.config.ScreenMode,ud.config.ScreenWidth,ud.config.ScreenHeight,ud.config.ScreenBPP))
1304
        if (setgamemode(!ud.config.ScreenMode,ud.config.ScreenWidth,ud.config.ScreenHeight,ud.config.ScreenBPP))
1305
        {
1305
        {
1306
            OSD_Printf(OSD_ERROR "Failed setting fullscreen video mode.\n");
1306
            OSD_Printf(OSD_ERROR "Failed setting fullscreen video mode.\n");
1307
            if (setgamemode(ud.config.ScreenMode, ud.config.ScreenWidth, ud.config.ScreenHeight, ud.config.ScreenBPP))
1307
            if (setgamemode(ud.config.ScreenMode, ud.config.ScreenWidth, ud.config.ScreenHeight, ud.config.ScreenBPP))
1308
                G_GameExit("Failed to recover from failure to set fullscreen video mode.\n");
1308
                G_GameExit("Failed to recover from failure to set fullscreen video mode.\n");
1309
        }
1309
        }
1310
        else ud.config.ScreenMode = !ud.config.ScreenMode;
1310
        else ud.config.ScreenMode = !ud.config.ScreenMode;
1311
        KB_ClearKeyDown(sc_Enter);
1311
        KB_ClearKeyDown(sc_Enter);
1312
        g_restorePalette = 1;
1312
        g_restorePalette = 1;
1313
        G_UpdateScreenArea();
1313
        G_UpdateScreenArea();
1314
    }
1314
    }
1315
1315
1316
    if (KB_UnBoundKeyPressed(sc_F12))
1316
    if (KB_UnBoundKeyPressed(sc_F12))
1317
    {
1317
    {
1318
        char titlebuf[256];
1318
        char titlebuf[256];
1319
        Bsprintf(titlebuf,HEAD2 " %s",s_buildRev);
1319
        Bsprintf(titlebuf,HEAD2 " %s",s_buildRev);
1320
1320
1321
        KB_ClearKeyDown(sc_F12);
1321
        KB_ClearKeyDown(sc_F12);
1322
        screencapture("duke0000.tga",0,titlebuf);
1322
        screencapture("duke0000.tga",0,titlebuf);
1323
        P_DoQuote(QUOTE_SCREEN_SAVED,g_player[myconnectindex].ps);
1323
        P_DoQuote(QUOTE_SCREEN_SAVED,g_player[myconnectindex].ps);
1324
    }
1324
    }
1325
1325
1326
    // only dispatch commands here when not in a game
1326
    // only dispatch commands here when not in a game
1327
    if (!(g_player[myconnectindex].ps->gm & MODE_GAME))
1327
    if (!(g_player[myconnectindex].ps->gm & MODE_GAME))
1328
        OSD_DispatchQueued();
1328
        OSD_DispatchQueued();
1329
1329
1330
    if (g_quickExit == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && (KB_KeyPressed(sc_Delete)||KB_KeyPressed(sc_End)))
1330
    if (g_quickExit == 0 && KB_KeyPressed(sc_LeftControl) && KB_KeyPressed(sc_LeftAlt) && (KB_KeyPressed(sc_Delete)||KB_KeyPressed(sc_End)))
1331
    {
1331
    {
1332
        g_quickExit = 1;
1332
        g_quickExit = 1;
1333
        G_GameExit("Quick Exit.");
1333
        G_GameExit("Quick Exit.");
1334
    }
1334
    }
1335
}
1335
}
1336
1336
1337
void G_GameQuit(void)
1337
void G_GameQuit(void)
1338
{
1338
{
1339
    if (numplayers < 2)
1339
    if (numplayers < 2)
1340
        G_GameExit(" ");
1340
        G_GameExit(" ");
1341
1341
1342
    if (g_gameQuit == 0)
1342
    if (g_gameQuit == 0)
1343
    {
1343
    {
1344
        g_gameQuit = 1;
1344
        g_gameQuit = 1;
1345
        g_quitDeadline = totalclock+120;
1345
        g_quitDeadline = totalclock+120;
1346
        g_netDisconnect = 1;
1346
        g_netDisconnect = 1;
1347
    }
1347
    }
1348
1348
1349
    if ((totalclock > g_quitDeadline) && (g_gameQuit == 1))
1349
    if ((totalclock > g_quitDeadline) && (g_gameQuit == 1))
1350
        G_GameExit("Timed out.");
1350
        G_GameExit("Timed out.");
1351
}
1351
}
1352
1352
1353
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
1353
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
1354
extern int32_t cacnum;
1354
extern int32_t cacnum;
1355
extern cactype cac[];
1355
extern cactype cac[];
1356
#endif
1356
#endif
1357
1357
1358
static void G_ShowCacheLocks(void)
1358
static void G_ShowCacheLocks(void)
1359
{
1359
{
1360
    int16_t i,k;
1360
    int16_t i,k;
1361
1361
1362
    if (offscreenrendering)
1362
    if (offscreenrendering)
1363
        return;
1363
        return;
1364
1364
1365
    k = 0;
1365
    k = 0;
1366
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
1366
#if !defined DEBUG_ALLOCACHE_AS_MALLOC
1367
    for (i=cacnum-1; i>=0; i--)
1367
    for (i=cacnum-1; i>=0; i--)
1368
        if ((*cac[i].lock) >= 200)
1368
        if ((*cac[i].lock) >= 200)
1369
        {
1369
        {
1370
            if (k >= ydim-12)
1370
            if (k >= ydim-12)
1371
                break;
1371
                break;
1372
1372
1373
            Bsprintf(tempbuf,"Locked- %d: Leng:%d, Lock:%d",i,cac[i].leng,*cac[i].lock);
1373
            Bsprintf(tempbuf,"Locked- %d: Leng:%d, Lock:%d",i,cac[i].leng,*cac[i].lock);
1374
            printext256(0L,k,31,-1,tempbuf,1);
1374
            printext256(0L,k,31,-1,tempbuf,1);
1375
            k += 6;
1375
            k += 6;
1376
        }
1376
        }
1377
#endif
1377
#endif
1378
    if (k < ydim-12)
1378
    if (k < ydim-12)
1379
        k += 6;
1379
        k += 6;
1380
1380
1381
    for (i=10; i>=0; i--)
1381
    for (i=10; i>=0; i--)
1382
        if (rts_lumplockbyte[i] >= 200)
1382
        if (rts_lumplockbyte[i] >= 200)
1383
        {
1383
        {
1384
            if (k >= ydim-12)
1384
            if (k >= ydim-12)
1385
                break;
1385
                break;
1386
1386
1387
            Bsprintf(tempbuf,"RTS Locked %d:",i);
1387
            Bsprintf(tempbuf,"RTS Locked %d:",i);
1388
            printext256(0,k,31,-1,tempbuf,1);
1388
            printext256(0,k,31,-1,tempbuf,1);
1389
            k += 6;
1389
            k += 6;
1390
        }
1390
        }
1391
1391
1392
    if (k >= ydim-12 && k<ydim-6)
1392
    if (k >= ydim-12 && k<ydim-6)
1393
        printext256(0,k,31,-1,"(MORE . . .)",1);
1393
        printext256(0,k,31,-1,"(MORE . . .)",1);
1394
1394
1395
    // sounds
1395
    // sounds
1396
    if (xdim < 640)
1396
    if (xdim < 640)
1397
        return;
1397
        return;
1398
1398
1399
    k = 18;
1399
    k = 18;
1400
    for (i=0; i<=g_maxSoundPos; i++)
1400
    for (i=0; i<=g_maxSoundPos; i++)
1401
        if (g_sounds[i].num > 0)
1401
        if (g_sounds[i].num > 0)
1402
        {
1402
        {
1403
            int32_t j, n=g_sounds[i].num;
1403
            int32_t j, n=g_sounds[i].num;
1404
1404
1405
            for (j=0; j<n; j++)
1405
            for (j=0; j<n; j++)
1406
            {
1406
            {
1407
                if (k >= ydim-12)
1407
                if (k >= ydim-12)
1408
                    break;
1408
                    break;
1409
1409
1410
                Bsprintf(tempbuf, "snd #%d inst %d: voice %d, ow %d", i, j,
1410
                Bsprintf(tempbuf, "snd #%d inst %d: voice %d, ow %d", i, j,
1411
                         g_sounds[i].SoundOwner[j].voice, g_sounds[i].SoundOwner[j].ow);
1411
                         g_sounds[i].SoundOwner[j].voice, g_sounds[i].SoundOwner[j].ow);
1412
                printext256(240,k,31,-1,tempbuf,0);
1412
                printext256(240,k,31,-1,tempbuf,0);
1413
1413
1414
                k += 9;
1414
                k += 9;
1415
            }
1415
            }
1416
        }
1416
        }
1417
}
1417
}
1418
1418
1419
int32_t A_CheckInventorySprite(spritetype *s)
1419
int32_t A_CheckInventorySprite(spritetype *s)
1420
{
1420
{
1421
    switch (DYNAMICTILEMAP(s->picnum))
1421
    switch (DYNAMICTILEMAP(s->picnum))
1422
    {
1422
    {
1423
    case FIRSTAID__STATIC:
1423
    case FIRSTAID__STATIC:
1424
    case STEROIDS__STATIC:
1424
    case STEROIDS__STATIC:
1425
    case HEATSENSOR__STATIC:
1425
    case HEATSENSOR__STATIC:
1426
    case BOOTS__STATIC:
1426
    case BOOTS__STATIC:
1427
    case JETPACK__STATIC:
1427
    case JETPACK__STATIC:
1428
    case HOLODUKE__STATIC:
1428
    case HOLODUKE__STATIC:
1429
    case AIRTANK__STATIC:
1429
    case AIRTANK__STATIC:
1430
        return 1;
1430
        return 1;
1431
    default:
1431
    default:
1432
        return 0;
1432
        return 0;
1433
    }
1433
    }
1434
}
1434
}
1435
1435
1436
// MYOS* CON commands.
1436
// MYOS* CON commands.
1437
LUNATIC_EXTERN void G_DrawTileGeneric(int32_t x, int32_t y, int32_t zoom, int32_t tilenum,
1437
LUNATIC_EXTERN void G_DrawTileGeneric(int32_t x, int32_t y, int32_t zoom, int32_t tilenum,
1438
                                      int32_t shade, int32_t orientation, int32_t p)
1438
                                      int32_t shade, int32_t orientation, int32_t p)
1439
{
1439
{
1440
    int32_t a = 0;
1440
    int32_t a = 0;
1441
1441
1442
    orientation &= (ROTATESPRITE_MAX-1);
1442
    orientation &= (ROTATESPRITE_MAX-1);
1443
1443
1444
    if (orientation&4)
1444
    if (orientation&4)
1445
        a = 1024;
1445
        a = 1024;
1446
1446
1447
    if (!(orientation&ROTATESPRITE_FULL16))
1447
    if (!(orientation&ROTATESPRITE_FULL16))
1448
    {
1448
    {
1449
        x<<=16;
1449
        x<<=16;
1450
        y<<=16;
1450
        y<<=16;
1451
    }
1451
    }
1452
1452
1453
    rotatesprite_win(x,y,zoom,a,tilenum,shade,p,2|orientation);
1453
    rotatesprite_win(x,y,zoom,a,tilenum,shade,p,2|orientation);
1454
}
1454
}
1455
1455
1456
#if !defined LUNATIC
1456
#if !defined LUNATIC
1457
void G_DrawTile(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation)
1457
void G_DrawTile(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation)
1458
{
1458
{
1459
    DukePlayer_t *ps = g_player[screenpeek].ps;
1459
    DukePlayer_t *ps = g_player[screenpeek].ps;
1460
    int32_t p = ps->cursectnum >= 0 ? sector[ps->cursectnum].floorpal : 0;
1460
    int32_t p = ps->cursectnum >= 0 ? sector[ps->cursectnum].floorpal : 0;
1461
1461
1462
    G_DrawTileGeneric(x,y,65536, tilenum,shade,orientation, p);
1462
    G_DrawTileGeneric(x,y,65536, tilenum,shade,orientation, p);
1463
}
1463
}
1464
1464
1465
void G_DrawTilePal(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation, int32_t p)
1465
void G_DrawTilePal(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation, int32_t p)
1466
{
1466
{
1467
    G_DrawTileGeneric(x,y,65536, tilenum,shade,orientation, p);
1467
    G_DrawTileGeneric(x,y,65536, tilenum,shade,orientation, p);
1468
}
1468
}
1469
1469
1470
void G_DrawTileSmall(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation)
1470
void G_DrawTileSmall(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation)
1471
{
1471
{
1472
    DukePlayer_t *ps = g_player[screenpeek].ps;
1472
    DukePlayer_t *ps = g_player[screenpeek].ps;
1473
    int32_t p = ps->cursectnum >= 0 ? sector[ps->cursectnum].floorpal : 0;
1473
    int32_t p = ps->cursectnum >= 0 ? sector[ps->cursectnum].floorpal : 0;
1474
1474
1475
    G_DrawTileGeneric(x,y,32768, tilenum,shade,orientation, p);
1475
    G_DrawTileGeneric(x,y,32768, tilenum,shade,orientation, p);
1476
}
1476
}
1477
1477
1478
void G_DrawTilePalSmall(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation, int32_t p)
1478
void G_DrawTilePalSmall(int32_t x, int32_t y, int32_t tilenum, int32_t shade, int32_t orientation, int32_t p)
1479
{
1479
{
1480
    G_DrawTileGeneric(x,y,32768, tilenum,shade,orientation, p);
1480
    G_DrawTileGeneric(x,y,32768, tilenum,shade,orientation, p);
1481
}
1481
}
1482
#endif
1482
#endif
1483
1483
1484
#define POLYMOSTTRANS (1)
1484
#define POLYMOSTTRANS (1)
1485
#define POLYMOSTTRANS2 (1|32)
1485
#define POLYMOSTTRANS2 (1|32)
1486
1486
1487
// Draws inventory numbers in the HUD for both the full and mini status bars.
1487
// Draws inventory numbers in the HUD for both the full and mini status bars.
1488
// yofs: in hud_scale-independent, (<<16)-scaled, 0-200-normalized y coords.
1488
// yofs: in hud_scale-independent, (<<16)-scaled, 0-200-normalized y coords.
1489
static void G_DrawInvNum(int32_t x, int32_t yofs, int32_t y, char num1, char ha, int32_t sbits)
1489
static void G_DrawInvNum(int32_t x, int32_t yofs, int32_t y, char num1, char ha, int32_t sbits)
1490
{
1490
{
1491
    char dabuf[16];
1491
    char dabuf[16];
1492
    int32_t i, shd = (x < 0);
1492
    int32_t i, shd = (x < 0);
1493
1493
1494
    const int32_t sbscale = sbarsc(65536);
1494
    const int32_t sbscale = sbarsc(65536);
1495
    const int32_t sby = yofs+sbary(y), sbyp1 = yofs+sbary(y+1);
1495
    const int32_t sby = yofs+sbary(y), sbyp1 = yofs+sbary(y+1);
1496
1496
1497
    if (shd) x = -x;
1497
    if (shd) x = -x;
1498
1498
1499
    Bsprintf(dabuf, "%d", num1);
1499
    Bsprintf(dabuf, "%d", num1);
1500
1500
1501
    if (num1 > 99)
1501
    if (num1 > 99)
1502
    {
1502
    {
1503
        if (shd && ud.screen_size == 4 && getrendermode() >= REND_POLYMOST && althud_shadows)
1503
        if (shd && ud.screen_size == 4 && getrendermode() >= REND_POLYMOST && althud_shadows)
1504
        {
1504
        {
1505
            for (i=0; i<=2; i++)
1505
            for (i=0; i<=2; i++)
1506
                rotatesprite_fs(sbarx(x+(-4+4*i)+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[i]-'0',
1506
                rotatesprite_fs(sbarx(x+(-4+4*i)+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[i]-'0',
1507
                                127, 4, POLYMOSTTRANS|sbits);
1507
                                127, 4, POLYMOSTTRANS|sbits);
1508
        }
1508
        }
1509
1509
1510
        for (i=0; i<=2; i++)
1510
        for (i=0; i<=2; i++)
1511
            rotatesprite_fs(sbarx(x+(-4+4*i)),sby,sbscale,0,THREEBYFIVE+dabuf[i]-'0',ha, 0, sbits);
1511
            rotatesprite_fs(sbarx(x+(-4+4*i)),sby,sbscale,0,THREEBYFIVE+dabuf[i]-'0',ha, 0, sbits);
1512
        return;
1512
        return;
1513
    }
1513
    }
1514
1514
1515
    if (num1 > 9)
1515
    if (num1 > 9)
1516
    {
1516
    {
1517
        if (shd && ud.screen_size == 4 && getrendermode() >= REND_POLYMOST && althud_shadows)
1517
        if (shd && ud.screen_size == 4 && getrendermode() >= REND_POLYMOST && althud_shadows)
1518
        {
1518
        {
1519
            rotatesprite_fs(sbarx(x+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[0]-'0',127,4,POLYMOSTTRANS|sbits);
1519
            rotatesprite_fs(sbarx(x+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[0]-'0',127,4,POLYMOSTTRANS|sbits);
1520
            rotatesprite_fs(sbarx(x+4+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[1]-'0',127,4,POLYMOSTTRANS|sbits);
1520
            rotatesprite_fs(sbarx(x+4+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[1]-'0',127,4,POLYMOSTTRANS|sbits);
1521
        }
1521
        }
1522
1522
1523
        rotatesprite_fs(sbarx(x),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits);
1523
        rotatesprite_fs(sbarx(x),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits);
1524
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,sbits);
1524
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,sbits);
1525
        return;
1525
        return;
1526
    }
1526
    }
1527
1527
1528
    rotatesprite_fs(sbarx(x+4+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,4,sbits);
1528
    rotatesprite_fs(sbarx(x+4+1),sbyp1,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,4,sbits);
1529
    rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits);
1529
    rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,sbits);
1530
}
1530
}
1531
1531
1532
static void G_DrawWeapNum(int16_t ind,int32_t x,int32_t y,int32_t num1, int32_t num2,int32_t ha)
1532
static void G_DrawWeapNum(int16_t ind,int32_t x,int32_t y,int32_t num1, int32_t num2,int32_t ha)
1533
{
1533
{
1534
    char dabuf[16];
1534
    char dabuf[16];
1535
1535
1536
    const int32_t sbscale = sbarsc(65536);
1536
    const int32_t sbscale = sbarsc(65536);
1537
    const int32_t sby = sbary(y);
1537
    const int32_t sby = sbary(y);
1538
1538
1539
    rotatesprite_fs(sbarx(x-7),sby,sbscale,0,THREEBYFIVE+ind+1,ha-10,7,10);
1539
    rotatesprite_fs(sbarx(x-7),sby,sbscale,0,THREEBYFIVE+ind+1,ha-10,7,10);
1540
    rotatesprite_fs(sbarx(x-3),sby,sbscale,0,THREEBYFIVE+10,ha,0,10);
1540
    rotatesprite_fs(sbarx(x-3),sby,sbscale,0,THREEBYFIVE+10,ha,0,10);
1541
1541
1542
    if (VOLUMEONE && (ind > HANDBOMB_WEAPON || ind < 0))
1542
    if (VOLUMEONE && (ind > HANDBOMB_WEAPON || ind < 0))
1543
    {
1543
    {
1544
        minitextshade(x+1,y-4,"ORDER",20,11,2+8+16+ROTATESPRITE_MAX);
1544
        minitextshade(x+1,y-4,"ORDER",20,11,2+8+16+ROTATESPRITE_MAX);
1545
        return;
1545
        return;
1546
    }
1546
    }
1547
1547
1548
    rotatesprite_fs(sbarx(x+9),sby,sbscale,0,THREEBYFIVE+11,ha,0,10);
1548
    rotatesprite_fs(sbarx(x+9),sby,sbscale,0,THREEBYFIVE+11,ha,0,10);
1549
1549
1550
    if (num1 > 99) num1 = 99;
1550
    if (num1 > 99) num1 = 99;
1551
    if (num2 > 99) num2 = 99;
1551
    if (num2 > 99) num2 = 99;
1552
1552
1553
    Bsprintf(dabuf,"%d",num1);
1553
    Bsprintf(dabuf,"%d",num1);
1554
    if (num1 > 9)
1554
    if (num1 > 9)
1555
    {
1555
    {
1556
        rotatesprite_fs(sbarx(x),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1556
        rotatesprite_fs(sbarx(x),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1557
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1557
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1558
    }
1558
    }
1559
    else rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1559
    else rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1560
1560
1561
    Bsprintf(dabuf,"%d",num2);
1561
    Bsprintf(dabuf,"%d",num2);
1562
    if (num2 > 9)
1562
    if (num2 > 9)
1563
    {
1563
    {
1564
        rotatesprite_fs(sbarx(x+13),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1564
        rotatesprite_fs(sbarx(x+13),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1565
        rotatesprite_fs(sbarx(x+17),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1565
        rotatesprite_fs(sbarx(x+17),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1566
        return;
1566
        return;
1567
    }
1567
    }
1568
    rotatesprite_fs(sbarx(x+13),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1568
    rotatesprite_fs(sbarx(x+13),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1569
}
1569
}
1570
1570
1571
static void G_DrawWeapNum2(char ind,int32_t x,int32_t y,int32_t num1, int32_t num2,char ha)
1571
static void G_DrawWeapNum2(char ind,int32_t x,int32_t y,int32_t num1, int32_t num2,char ha)
1572
{
1572
{
1573
    char dabuf[16];
1573
    char dabuf[16];
1574
1574
1575
    const int32_t sbscale = sbarsc(65536);
1575
    const int32_t sbscale = sbarsc(65536);
1576
    const int32_t sby = sbary(y);
1576
    const int32_t sby = sbary(y);
1577
1577
1578
    rotatesprite_fs(sbarx(x-7),sby,sbscale,0,THREEBYFIVE+ind+1,ha-10,7,10);
1578
    rotatesprite_fs(sbarx(x-7),sby,sbscale,0,THREEBYFIVE+ind+1,ha-10,7,10);
1579
    rotatesprite_fs(sbarx(x-4),sby,sbscale,0,THREEBYFIVE+10,ha,0,10);
1579
    rotatesprite_fs(sbarx(x-4),sby,sbscale,0,THREEBYFIVE+10,ha,0,10);
1580
    rotatesprite_fs(sbarx(x+13),sby,sbscale,0,THREEBYFIVE+11,ha,0,10);
1580
    rotatesprite_fs(sbarx(x+13),sby,sbscale,0,THREEBYFIVE+11,ha,0,10);
1581
1581
1582
    Bsprintf(dabuf,"%d",num1);
1582
    Bsprintf(dabuf,"%d",num1);
1583
    if (num1 > 99)
1583
    if (num1 > 99)
1584
    {
1584
    {
1585
        rotatesprite_fs(sbarx(x),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1585
        rotatesprite_fs(sbarx(x),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1586
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1586
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1587
        rotatesprite_fs(sbarx(x+8),sby,sbscale,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10);
1587
        rotatesprite_fs(sbarx(x+8),sby,sbscale,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10);
1588
    }
1588
    }
1589
    else if (num1 > 9)
1589
    else if (num1 > 9)
1590
    {
1590
    {
1591
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1591
        rotatesprite_fs(sbarx(x+4),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1592
        rotatesprite_fs(sbarx(x+8),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1592
        rotatesprite_fs(sbarx(x+8),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1593
    }
1593
    }
1594
    else rotatesprite_fs(sbarx(x+8),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1594
    else rotatesprite_fs(sbarx(x+8),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1595
1595
1596
    Bsprintf(dabuf,"%d",num2);
1596
    Bsprintf(dabuf,"%d",num2);
1597
    if (num2 > 99)
1597
    if (num2 > 99)
1598
    {
1598
    {
1599
        rotatesprite_fs(sbarx(x+17),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1599
        rotatesprite_fs(sbarx(x+17),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1600
        rotatesprite_fs(sbarx(x+21),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1600
        rotatesprite_fs(sbarx(x+21),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1601
        rotatesprite_fs(sbarx(x+25),sby,sbscale,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10);
1601
        rotatesprite_fs(sbarx(x+25),sby,sbscale,0,THREEBYFIVE+dabuf[2]-'0',ha,0,10);
1602
    }
1602
    }
1603
    else if (num2 > 9)
1603
    else if (num2 > 9)
1604
    {
1604
    {
1605
        rotatesprite_fs(sbarx(x+17),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1605
        rotatesprite_fs(sbarx(x+17),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1606
        rotatesprite_fs(sbarx(x+21),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1606
        rotatesprite_fs(sbarx(x+21),sby,sbscale,0,THREEBYFIVE+dabuf[1]-'0',ha,0,10);
1607
        return;
1607
        return;
1608
    }
1608
    }
1609
    else
1609
    else
1610
        rotatesprite_fs(sbarx(x+25),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1610
        rotatesprite_fs(sbarx(x+25),sby,sbscale,0,THREEBYFIVE+dabuf[0]-'0',ha,0,10);
1611
}
1611
}
1612
1612
1613
static void G_DrawWeapAmounts(const DukePlayer_t *p,int32_t x,int32_t y,int32_t u)
1613
static void G_DrawWeapAmounts(const DukePlayer_t *p,int32_t x,int32_t y,int32_t u)
1614
{
1614
{
1615
    int32_t cw = p->curr_weapon;
1615
    int32_t cw = p->curr_weapon;
1616
1616
1617
    if (u&4)
1617
    if (u&4)
1618
    {
1618
    {
1619
        if (u != -1) G_PatchStatusBar(88,178,88+37,178+6); //original code: (96,178,96+12,178+6);
1619
        if (u != -1) G_PatchStatusBar(88,178,88+37,178+6); //original code: (96,178,96+12,178+6);
1620
        G_DrawWeapNum2(PISTOL_WEAPON,x,y,
1620
        G_DrawWeapNum2(PISTOL_WEAPON,x,y,
1621
                       p->ammo_amount[PISTOL_WEAPON],p->max_ammo_amount[PISTOL_WEAPON],
1621
                       p->ammo_amount[PISTOL_WEAPON],p->max_ammo_amount[PISTOL_WEAPON],
1622
                       12-20*(cw == PISTOL_WEAPON));
1622
                       12-20*(cw == PISTOL_WEAPON));
1623
    }
1623
    }
1624
    if (u&8)
1624
    if (u&8)
1625
    {
1625
    {
1626
        if (u != -1) G_PatchStatusBar(88,184,88+37,184+6); //original code: (96,184,96+12,184+6);
1626
        if (u != -1) G_PatchStatusBar(88,184,88+37,184+6); //original code: (96,184,96+12,184+6);
1627
        G_DrawWeapNum2(SHOTGUN_WEAPON,x,y+6,
1627
        G_DrawWeapNum2(SHOTGUN_WEAPON,x,y+6,
1628
                       p->ammo_amount[SHOTGUN_WEAPON],p->max_ammo_amount[SHOTGUN_WEAPON],
1628
                       p->ammo_amount[SHOTGUN_WEAPON],p->max_ammo_amount[SHOTGUN_WEAPON],
1629
                       (((p->gotweapon & (1<<SHOTGUN_WEAPON)) == 0)*9)+12-18*
1629
                       (((p->gotweapon & (1<<SHOTGUN_WEAPON)) == 0)*9)+12-18*
1630
                       (cw == SHOTGUN_WEAPON));
1630
                       (cw == SHOTGUN_WEAPON));
1631
    }
1631
    }
1632
    if (u&16)
1632
    if (u&16)
1633
    {
1633
    {
1634
        if (u != -1) G_PatchStatusBar(88,190,88+37,190+6); //original code: (96,190,96+12,190+6);
1634
        if (u != -1) G_PatchStatusBar(88,190,88+37,190+6); //original code: (96,190,96+12,190+6);
1635
        G_DrawWeapNum2(CHAINGUN_WEAPON,x,y+12,
1635
        G_DrawWeapNum2(CHAINGUN_WEAPON,x,y+12,
1636
                       p->ammo_amount[CHAINGUN_WEAPON],p->max_ammo_amount[CHAINGUN_WEAPON],
1636
                       p->ammo_amount[CHAINGUN_WEAPON],p->max_ammo_amount[CHAINGUN_WEAPON],
1637
                       (((p->gotweapon & (1<<CHAINGUN_WEAPON)) == 0)*9)+12-18*
1637
                       (((p->gotweapon & (1<<CHAINGUN_WEAPON)) == 0)*9)+12-18*
1638
                       (cw == CHAINGUN_WEAPON));
1638
                       (cw == CHAINGUN_WEAPON));
1639
    }
1639
    }
1640
    if (u&32)
1640
    if (u&32)
1641
    {
1641
    {
1642
        if (u != -1) G_PatchStatusBar(127,178,127+29,178+6); //original code: (135,178,135+8,178+6);
1642
        if (u != -1) G_PatchStatusBar(127,178,127+29,178+6); //original code: (135,178,135+8,178+6);
1643
        G_DrawWeapNum(RPG_WEAPON,x+39,y,
1643
        G_DrawWeapNum(RPG_WEAPON,x+39,y,
1644
                      p->ammo_amount[RPG_WEAPON],p->max_ammo_amount[RPG_WEAPON],
1644
                      p->ammo_amount[RPG_WEAPON],p->max_ammo_amount[RPG_WEAPON],
1645
                      (((p->gotweapon & (1<<RPG_WEAPON)) == 0)*9)+12-19*
1645
                      (((p->gotweapon & (1<<RPG_WEAPON)) == 0)*9)+12-19*
1646
                      (cw == RPG_WEAPON));
1646
                      (cw == RPG_WEAPON));
1647
    }
1647
    }
1648
    if (u&64)
1648
    if (u&64)
1649
    {
1649
    {
1650
        if (u != -1) G_PatchStatusBar(127,184,127+29,184+6); //original code: (135,184,135+8,184+6);
1650
        if (u != -1) G_PatchStatusBar(127,184,127+29,184+6); //original code: (135,184,135+8,184+6);
1651
        G_DrawWeapNum(HANDBOMB_WEAPON,x+39,y+6,
1651
        G_DrawWeapNum(HANDBOMB_WEAPON,x+39,y+6,
1652
                      p->ammo_amount[HANDBOMB_WEAPON],p->max_ammo_amount[HANDBOMB_WEAPON],
1652
                      p->ammo_amount[HANDBOMB_WEAPON],p->max_ammo_amount[HANDBOMB_WEAPON],
1653
                      (((!p->ammo_amount[HANDBOMB_WEAPON])|((p->gotweapon & (1<<HANDBOMB_WEAPON)) == 0))*9)+12-19*
1653
                      (((!p->ammo_amount[HANDBOMB_WEAPON])|((p->gotweapon & (1<<HANDBOMB_WEAPON)) == 0))*9)+12-19*
1654
                      ((cw == HANDBOMB_WEAPON) || (cw == HANDREMOTE_WEAPON)));
1654
                      ((cw == HANDBOMB_WEAPON) || (cw == HANDREMOTE_WEAPON)));
1655
    }
1655
    }
1656
    if (u&128)
1656
    if (u&128)
1657
    {
1657
    {
1658
        if (u != -1) G_PatchStatusBar(127,190,127+29,190+6); //original code: (135,190,135+8,190+6);
1658
        if (u != -1) G_PatchStatusBar(127,190,127+29,190+6); //original code: (135,190,135+8,190+6);
1659
1659
1660
        if (p->subweapon&(1<<GROW_WEAPON))
1660
        if (p->subweapon&(1<<GROW_WEAPON))
1661
            G_DrawWeapNum(SHRINKER_WEAPON,x+39,y+12,
1661
            G_DrawWeapNum(SHRINKER_WEAPON,x+39,y+12,
1662
                          p->ammo_amount[GROW_WEAPON],p->max_ammo_amount[GROW_WEAPON],
1662
                          p->ammo_amount[GROW_WEAPON],p->max_ammo_amount[GROW_WEAPON],
1663
                          (((p->gotweapon & (1<<GROW_WEAPON)) == 0)*9)+12-18*
1663
                          (((p->gotweapon & (1<<GROW_WEAPON)) == 0)*9)+12-18*
1664
                          (cw == GROW_WEAPON));
1664
                          (cw == GROW_WEAPON));
1665
        else
1665
        else
1666
            G_DrawWeapNum(SHRINKER_WEAPON,x+39,y+12,
1666
            G_DrawWeapNum(SHRINKER_WEAPON,x+39,y+12,
1667
                          p->ammo_amount[SHRINKER_WEAPON]