disabled some quake-only teamplay stuff in non-quake builds.

GL: r_dynamic -1 is now r_temporalscenecache 1, which makes menu options etc a little friendlier. fixed a serious memory leak.
GL: Lightmaps are now uploaded using pbos to reduce cpu stalls (especially with temporalscenecache) and the resulting periodic framerate drops. Requires gl4.4.
PM: moved manifest-downloads to the package manager. still needs some proper testing.
PM: Fixed bug with downloading updates from every known mirror for that update.
PM: Fixed bug with duplicate mirrors...
PM: menuqc is now able to query available updates.
engine's Draw_TextBox centers the text box more appropriately and easily.
SV: added sv_autooffload cvar, when set the map command will automatically create a server in a separate process to reduce the effects of stutter in inefficient ssqc mods.
Menu: menu_mods now shares data with getgamedirinfo builtin.
MenuQC: Added some extra properties to the getgamedirinfo builtin.
MenuQC: Added Menu_RendererRestarted entrypoint.
MenuQC: _vid_renderer_opts cvar now has a value that actually reflects the windowing systems in the build, rather than just renderers.
CQSC: Added getlocationname builtin.
ALSA: device names are now more consistent with other audio drivers.
SV: added unsavegame console command, to delete unwanted saved games.
SV: hashtable entries are now saved into saved games.
SV: reworked player remapping strategy when loading games. Player slots are now directly swapped serverside, not reconnected.
SV: resend all csqc entity state when a client signals that it started recording a demo.
SV: Added SOLID_BSPTRIGGER as a shapely alternative to the aabb SOLID_TRIGGER. modelindex must still be set for this to work.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5668 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-04-19 01:23:32 +00:00
parent dba2e93f51
commit e8aa715763
78 changed files with 4226 additions and 2458 deletions

View file

@ -1134,7 +1134,7 @@ void CL_RecordMap_f (void)
Q_strncpyz(mapname, Cmd_Argv(2), sizeof(mapname));
CL_Disconnect_f();
SV_SpawnServer (mapname, NULL, false, false);
SV_SpawnServer (mapname, NULL, false, false, 0);
#ifdef MVD_RECORDING
COM_DefaultExtension(demoname, ".mvd", sizeof(demoname));
@ -1785,46 +1785,7 @@ void CL_Record_f (void)
}
else
{ //automagically generate a name
if (cl.playerview[0].spectator)
{ // FIXME: if tracking a player, use his name
fname = va ("spec_%s_%s",
TP_PlayerName(),
TP_MapName());
}
else
{ // guess game type and write demo name
i = TP_CountPlayers();
if (cl.teamplay && i >= 3)
{ // Teamplay
fname = va ("%s_%s_vs_%s_%s",
TP_PlayerName(),
TP_PlayerTeam(),
TP_EnemyTeam(),
TP_MapName());
}
else
{
if (i == 2)
{ // Duel
fname = va ("%s_vs_%s_%s",
TP_PlayerName(),
TP_EnemyName(),
TP_MapName());
}
else if (i > 2)
{ // FFA
fname = va ("%s_ffa_%s",
TP_PlayerName(),
TP_MapName());
}
else
{ // one player
fname = va ("%s_%s",
TP_PlayerName(),
TP_MapName());
}
}
}
fname = TP_GenerateDemoName();
}
while((p = strstr(fname, "..")))

View file

@ -50,6 +50,7 @@ float r_blobshadows;
extern cvar_t cl_gibfilter, cl_deadbodyfilter;
extern int cl_playerindex;
static qboolean cl_expandvisents;
extern world_t csqc_world;
@ -3299,7 +3300,10 @@ void CL_LinkStaticEntities(void *pvs, int *areas)
for (i = 0; i < cl.num_statics; i++)
{
if (cl_numvisedicts == cl_maxvisedicts)
{
cl_expandvisents=true;
break;
}
stat = &cl_static_entities[i];
clmodel = stat->ent.model;
@ -4078,7 +4082,7 @@ void CL_LinkPacketEntities (void)
}
// if set to invisible, skip
if (state->modelindex<1)
if (state->modelindex<1 || (state->effects & NQEF_NODRAW))
{
if (state->tagindex == 0xffff)
{
@ -4730,10 +4734,9 @@ void CLQW_ParsePlayerinfo (void)
state->pm_type = PM_NORMAL;
#ifdef QUAKESTATS
TP_ParsePlayerInfo(oldstate, state, info);
#ifdef QUAKESTATS
//can't CL_SetStatInt as we don't know if its actually us or not
cl.players[num].stats[STAT_WEAPONFRAME] = state->weaponframe;
cl.players[num].statsf[STAT_WEAPONFRAME] = state->weaponframe;
@ -4997,9 +5000,9 @@ guess_pm_type:
state->pm_type = PM_NORMAL;
}
#ifdef QUAKESTATS
TP_ParsePlayerInfo(oldstate, state, info);
#ifdef QUAKESTATS
//can't CL_SetStatInt as we don't know if its actually us or not
for (i = 0; i < cl.splitclients; i++)
{
@ -5895,7 +5898,7 @@ Made up of: clients, packet_entities, nails, and tents
void CL_ClearEntityLists(void)
{
cl_framecount++;
if (cl_numvisedicts+128 >= cl_maxvisedicts)
if (cl_expandvisents || cl_numvisedicts+128 >= cl_maxvisedicts)
{
int newnum = cl_maxvisedicts + 256;
entity_t *n = BZ_Realloc(cl_visedicts, newnum * sizeof(*n));
@ -5904,6 +5907,7 @@ void CL_ClearEntityLists(void)
cl_visedicts = n;
cl_maxvisedicts = newnum;
}
cl_expandvisents = false;
}
cl_numvisedicts = 0;
cl_numstrisidx = 0;

View file

@ -1609,7 +1609,7 @@ void CL_ResetFog(int ftype)
static void CL_ReconfigureCommands(int newgame)
{
static int oldgame;
static int oldgame = ~0;
extern void SCR_SizeUp_f (void); //cl_screen
extern void SCR_SizeDown_f (void); //cl_screen
#ifdef QUAKESTATS
@ -2956,6 +2956,25 @@ void CL_Reconnect_f (void)
return;
}
#if defined(HAVE_SERVER) && defined(SUBSERVERS)
if (sv.state == ss_clustermode)
{ //reconnecting while we're a cluster... o.O
char oldguid[sizeof(connectinfo.guid)];
Q_strncpyz(oldguid, connectinfo.guid, sizeof(oldguid));
memset(&connectinfo, 0, sizeof(connectinfo));
connectinfo.istransfer = false;
Q_strncpyz(connectinfo.guid, oldguid, sizeof(oldguid)); //retain the same guid on transfers
Cvar_Set(&cl_disconnectreason, "Transferring....");
connectinfo.trying = true;
connectinfo.defaultport = cl_defaultport.value;
connectinfo.protocol = CP_UNKNOWN;
SCR_SetLoadingStage(LS_CONNECTION);
CL_CheckForResend();
return;
}
#endif
CL_Disconnect(NULL);
connectinfo.tries = 0; //re-ensure routes.
CL_BeginServerReconnect();
@ -3557,7 +3576,7 @@ client_connect: //fixme: make function
Con_TPrintf ("connection\n");
#ifndef CLIENTONLY
if (sv.state)
if (sv.state && sv.state != ss_clustermode)
SV_UnspawnServer();
#endif
}
@ -4817,9 +4836,9 @@ void CL_Init (void)
Cmd_AddCommandAD ("timedemo", CL_TimeDemo_f, CL_DemoList_c, NULL);
#ifdef _DEBUG
Cmd_AddCommand ("freespace", CL_FreeSpace_f);
#endif
Cmd_AddCommand ("crashme_endgame", CL_CrashMeEndgame_f);
Cmd_AddCommand ("crashme_error", CL_CrashMeError_f);
#endif
Cmd_AddCommandD ("showpic", SCR_ShowPic_Script_f, "showpic <imagename> <placename> <x> <y> <zone> [width] [height] [touchcommand]\nDisplays an image onscreen, that potentially has a key binding attached to it when clicked/touched.\nzone should be one of: TL, TR, BL, BR, MM, TM, BM, ML, MR. This serves as an extra offset to move the image around the screen without any foreknowledge of the screen resolution.");
Cmd_AddCommandD ("showpic_removeall", SCR_ShowPic_Remove_f, "removes any pictures inserted with the showpic command.");
@ -5345,15 +5364,15 @@ qboolean Host_BeginFileDownload(struct dl_download *dl, char *mimetype)
}
return result;
}
void Host_RunFilePrompted(void *ctx, int button)
void Host_RunFilePrompted(void *ctx, promptbutton_t button)
{
hrf_t *f = ctx;
switch(button)
{
case 0:
case PROMPT_YES:
f->flags |= HRF_OVERWRITE;
break;
case 1:
case PROMPT_NO:
f->flags |= HRF_NOOVERWRITE;
break;
default:
@ -5519,7 +5538,7 @@ done:
else
{
host_parms.manifest = Z_StrDup(fdata);
man = FS_Manifest_Parse(NULL, fdata);
man = FS_Manifest_ReadMem(NULL, NULL, fdata);
if (man)
{
if (!man->updateurl)
@ -5527,9 +5546,6 @@ done:
// if (f->flags & HRF_DOWNLOADED)
man->blockupdate = true;
BZ_Free(fdata);
#ifdef PACKAGEMANAGER
PM_Shutdown();
#endif
FS_ChangeGame(man, true, true);
}
else
@ -5946,6 +5962,8 @@ double Host_Frame (double time)
SV_Frame();
RSpeedEnd(RSPEED_SERVER);
}
else
MSV_PollSlaves();
#endif
while(COM_DoWork(0, false))
;
@ -6070,6 +6088,8 @@ double Host_Frame (double time)
RSpeedEnd(RSPEED_SERVER);
host_frametime = ohft;
}
else
MSV_PollSlaves();
return 0;
}
#endif
@ -6141,6 +6161,8 @@ double Host_Frame (double time)
// if (cls.protocol != CP_QUAKE3 && cls.protocol != CP_QUAKE2)
// CL_ReadPackets (); //q3's cgame cannot cope with input commands with the same time as the most recent snapshot value
}
else
MSV_PollSlaves();
#endif
CL_CalcClientTime();
@ -6224,7 +6246,9 @@ double Host_Frame (double time)
CL_QTVPoll();
#ifdef QUAKESTATS
TP_UpdateAutoStatus();
#endif
host_framecount++;
cl.lasttime = cl.time;
@ -6661,10 +6685,6 @@ void Host_Init (quakeparms_t *parms)
V_Init ();
NET_Init ();
#ifdef PLUGINS
Plug_Initialise(false);
#endif
#if defined(Q2BSPS) || defined(Q3BSPS)
CM_Init();
#endif
@ -6694,6 +6714,10 @@ void Host_Init (quakeparms_t *parms)
Sbar_Init ();
CL_Init ();
#ifdef PLUGINS
Plug_Initialise(false);
#endif
#ifdef TEXTEDITOR
Editor_Init();
#endif
@ -6802,7 +6826,7 @@ void Host_Shutdown(void)
Cmd_Shutdown();
#ifdef PACKAGEMANAGER
PM_Shutdown();
PM_Shutdown(false);
#endif
Key_Unbindall_f();

View file

@ -968,11 +968,17 @@ qboolean CL_CheckOrEnqueDownloadFile (const char *filename, const char *localnam
if (flags & DLLF_ALLOWWEB)
{
const char *sv_dlURL = InfoBuf_ValueForKey(&cl.serverinfo, "sv_dlURL");
flags &= ~DLLF_ALLOWWEB;
if (*cl_download_mapsrc.string)
if (!strcmp(filename, localname))
if (!strncmp(filename, "maps/", 5))
if (!strcmp(filename + strlen(filename)-4, ".bsp"))
if (*sv_dlURL && (flags & DLLF_NONGAME) && !strncmp(filename, "package/", 8))
{
filename = va("%s/%s", cl_download_mapsrc.string, filename+8);
flags |= DLLF_ALLOWWEB;
}
else if (*cl_download_mapsrc.string &&
!strcmp(filename, localname) &&
!strncmp(filename, "maps/", 5) &&
!strcmp(filename + strlen(filename)-4, ".bsp"))
{
char base[MAX_QPATH];
COM_FileBase(filename, base, sizeof(base));
@ -1523,7 +1529,7 @@ static int CL_LoadSounds(int stage, qboolean dontactuallyload)
void Sound_CheckDownload(const char *s)
{
#if !defined(QUAKETC) && defined(AVAIL_OGGVORBIS)
#if defined(HAVE_LEGACY) && defined(AVAIL_OGGVORBIS)
char mangled[512];
#endif
if (*s == '*') //q2 sexed sound
@ -1536,7 +1542,7 @@ void Sound_CheckDownload(const char *s)
if (CL_CheckFile(s))
return; //we have it already
#if !defined(QUAKETC) && defined(AVAIL_OGGVORBIS)
#if defined(HAVE_LEGACY) && defined(AVAIL_OGGVORBIS)
//the things I do for nexuiz... *sigh*
COM_StripExtension(s, mangled, sizeof(mangled));
COM_DefaultExtension(mangled, ".ogg", sizeof(mangled));
@ -1550,7 +1556,7 @@ void Sound_CheckDownload(const char *s)
if (CL_CheckFile(s))
return; //we have it already
#if !defined(QUAKETC) && defined(AVAIL_OGGVORBIS)
#if defined(HAVE_LEGACY) && defined(AVAIL_OGGVORBIS)
//the things I do for nexuiz... *sigh*
COM_StripExtension(s, mangled, sizeof(mangled));
COM_DefaultExtension(mangled, ".ogg", sizeof(mangled));
@ -4857,6 +4863,7 @@ static void CLQW_ParseStartSoundPacket(void)
S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, NULL, volume/255.0, attenuation, 0, 0, 0);
}
#ifdef QUAKESTATS
for (i = 0; i < cl.splitclients; i++)
{
if (ent == cl.playerview[i].playernum+1)
@ -4866,6 +4873,7 @@ static void CLQW_ParseStartSoundPacket(void)
}
}
TP_CheckPickupSound(cl.sound_name[sound_num], pos, -1);
#endif
}
#ifdef Q2CLIENT
@ -5053,6 +5061,7 @@ static void CLNQ_ParseStartSoundPacket(void)
S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, vel, volume/255.0, attenuation, timeofs, pitchadj, flags);
}
#ifdef QUAKESTATS
for (i = 0; i < cl.splitclients; i++)
{
if (ent == cl.playerview[i].playernum+1)
@ -5062,6 +5071,7 @@ static void CLNQ_ParseStartSoundPacket(void)
}
}
TP_CheckPickupSound(cl.sound_name[sound_num], pos, -1);
#endif
}
#endif
@ -5506,8 +5516,10 @@ static void CL_SetStat_Internal (int pnum, int stat, int ivalue, float fvalue)
cl.playerview[pnum].stats[stat] = ivalue;
cl.playerview[pnum].statsf[stat] = fvalue;
#ifdef QUAKESTATS
if (pnum == 0)
TP_StatChanged(stat, ivalue);
#endif
}
#ifdef NQPROT

View file

@ -1181,7 +1181,7 @@ void CL_PredictMovePNum (int seat)
}
if (i == pe->num_entities && pv->nolocalplayer)
{
return; //no player, nothing makes sense any more.
//return; //no player, nothing makes sense any more.
from.state = &nullstate;
nopred = true;
}

View file

@ -755,16 +755,12 @@ int SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font)
if (p->flags & CPRINT_BACKGROUND)
{ //hexen2 style plaque.
int px, py, pw;
px = rect->x;
py = ( y * vid.height) / (float)vid.pixelheight;
pw = rect->width+8;
Font_EndString(font);
if (*p->titleimage)
R2D_ScalePic (rect->x + ((int)rect->width - pic->width)/2, rect->y + ((int)rect->height - pic->height)/2, pic->width, pic->height, pic);
else
Draw_TextBox(px-16, py-8-8, pw/8, linecount+2);
Draw_ApproxTextBox(rect->x, (y * vid.height) / (float)vid.pixelheight, rect->width, linecount*Font_CharVHeight(font));
Font_BeginString(font, rect->x, y, &left, &top);
}
@ -913,7 +909,9 @@ int R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int d
cprint_t p;
vrect_t r;
conchar_t buffer[16384]; //FIXME: make dynamic.
int lines;
p.cursorchar = NULL;
p.string = buffer;
p.stringbytes = sizeof(buffer);
r.x = x;
@ -927,7 +925,11 @@ int R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int d
p.time_start = cl.time;
*p.titleimage = 0;
return SCR_DrawCenterString(&r, &p, font);
lines = SCR_DrawCenterString(&r, &p, font);
// SCR_CopyCenterPrint(&p);
return lines;
}
qboolean SCR_HardwareCursorIsActive(void)

View file

@ -1549,27 +1549,27 @@ void Cam_AutoTrack_Update(const char *mode); //reset autotrack setting (because
void CL_Say (qboolean team, char *extra);
int TP_CategorizeMessage (char *s, int *offset, player_info_t **plr);
void TP_CheckPickupSound(char *s, vec3_t org, int seat);
qboolean TP_CheckSoundTrigger (char *str);
int TP_CountPlayers (void);
char* TP_EnemyName (void);
char* TP_EnemyTeam (void);
void TP_ExecTrigger (char *s, qboolean indemos);
void TP_ExecTrigger (char *s, qboolean indemos); //executes one of the user's f_foo aliases from some engine-defined event.
qboolean TP_FilterMessage (char *s);
void TP_Init(void);
char* TP_LocationName (const vec3_t location);
char* TP_MapName (void);
void TP_NewMap (void);
qboolean TP_CheckSoundTrigger (char *str); //plays sound files when some substring exists in chat.
void TP_SearchForMsgTriggers (char *s, int level); //msg_trigger: executes aliases when a chat message contains some user-defined string.
qboolean TP_SuppressMessage(char *buf); //true when the message contains macro results that the local player isn't meant to see (teamplay messages that contain enemy player counts for instance)
char *TP_GenerateDemoName(void); //makes something up.
#ifdef QUAKESTATS
//hack zone: this stuff makes assumptions about quake-only stats+items+rules and stuff.
void TP_CheckPickupSound(char *s, vec3_t org, int seat);
void TP_ParsePlayerInfo(player_state_t *oldstate, player_state_t *state, player_info_t *info);
qboolean TP_IsPlayerVisible(vec3_t origin);
char* TP_PlayerName (void);
char* TP_PlayerTeam (void);
void TP_SearchForMsgTriggers (char *s, int level);
qboolean TP_SoundTrigger(char *message);
void TP_StatChanged (int stat, int value);
qboolean TP_SuppressMessage(char *buf);
colourised_t *TP_FindColours(char *name);
void TP_UpdateAutoStatus(void);
#endif
#ifdef QWSKINS
colourised_t *TP_FindColours(char *name);
#endif
//
// skin.c

View file

@ -232,6 +232,11 @@ console_t *Con_FindConsole(const char *name)
console_t *Con_Create(const char *name, unsigned int flags)
{
console_t *con, *p;
if (!name)
{
static unsigned long seq;
name = va("c%lu", seq++);
}
if (!strcmp(name, "current"))
return NULL;
if (!strcmp(name, "head"))
@ -532,7 +537,7 @@ void QT_Create(char *command)
void Con_QTerm_f(void)
{
if(Cmd_IsInsecure())
Con_Printf("Server tried stuffcmding a restricted commandqterm %s\n", Cmd_Args());
Con_Printf("Server tried stuffcmding a restricted command: qterm %s\n", Cmd_Args());
else
QT_Create(Cmd_Args());
}

View file

@ -5498,6 +5498,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
memcpy(&fmtheader, filedata+4, sizeof(fmtheader));
if (fmtheader.dwSize != sizeof(fmtheader))
return NULL; //corrupt/different version
fmtheader.dwSize += 4;
memset(&fmt10header, 0, sizeof(fmt10header));
fmt10header.arraysize = (fmtheader.ddsCaps[1] & 0x200)?6:1; //cubemaps need 6 faces...
@ -5505,6 +5506,8 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
nummips = fmtheader.dwMipMapCount;
if (nummips < 1)
nummips = 1;
if (nummips > countof(mips->mip))
return NULL;
if (!(fmtheader.ddpfPixelFormat.dwFlags & 4))
{
@ -5591,7 +5594,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
else if (*(int*)&fmtheader.ddpfPixelFormat.dwFourCC == (('D'<<0)|('X'<<8)|('1'<<16)|('0'<<24)))
{
//this has some weird extra header with dxgi format types.
memcpy(&fmt10header, filedata+4+fmtheader.dwSize, sizeof(fmt10header));
memcpy(&fmt10header, filedata+fmtheader.dwSize, sizeof(fmt10header));
fmtheader.dwSize += sizeof(fmt10header);
switch(fmt10header.dxgiformat)
{
@ -5802,11 +5805,11 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
mips->extrafree = filedata;
mips->encoding = encoding;
filedata += 4+fmtheader.dwSize;
filedata += fmtheader.dwSize;
w = fmtheader.dwWidth;
h = fmtheader.dwHeight;
d = fmtheader.dwDepth;
w = max(1, fmtheader.dwWidth);
h = max(1, fmtheader.dwHeight);
d = max(1, fmtheader.dwDepth);
if (layers == 1)
{ //can just use the data without copying.
@ -5819,13 +5822,13 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
mips->mip[mipnum].width = w;
mips->mip[mipnum].height = h;
mips->mip[mipnum].depth = d;
mips->mipcount++;
filedata += datasize;
w = max(1, w>>1);
h = max(1, h>>1);
d = max(1, d>>1);
}
mips->mipcount = mipnum;
}
else
{ //we need to copy stuff in order to pack it properly. :(
@ -11057,10 +11060,10 @@ static qboolean Image_DecompressFormat(struct pendingtextureinfo *mips, const ch
rcoding = PTI_RGBX8;
break;
#else
case PTI_BC4_R8_SNORM:
case PTI_BC4_R8:
case PTI_BC5_RG8_SNORM:
case PTI_BC5_RG8:
case PTI_BC4_R_SNORM:
case PTI_BC4_R:
case PTI_BC5_RG_SNORM:
case PTI_BC5_RG:
Con_ThrottlePrintf(&throttle, 0, "Fallback BC4/BC5 decompression is not supported in this build\n");
break;
#endif

View file

@ -889,6 +889,8 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
}
return;
}
if (!con)
return; //can't do footers
Con_Footerf(con, false, "^m#^m ^[%s\\player\\%i^]: %if %ims", cl.players[player].name, player, cl.players[player].frags, cl.players[player].ping);
@ -1058,6 +1060,14 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
Cbuf_AddText(va("\nedit \"%s\"\n", c), RESTRICT_LOCAL);
return;
}
#endif
#ifdef SUBSERVERS
c = Info_ValueForKey(info, "ssv");
if (*c && !strchr(c, ';') && !strchr(c, '\n'))
{
Cbuf_AddText(va("\nssv \"%s\"\n", c), RESTRICT_LOCAL);
return;
}
#endif
c = Info_ValueForKey(info, "impulse");
if (*c && !strchr(c, ';') && !strchr(c, '\n'))
@ -1082,7 +1092,8 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info)
c = Info_ValueForKey(info, "desc");
if (*c)
{
Con_Footerf(con, false, "%s", c);
if (con)
Con_Footerf(con, false, "%s", c);
return;
}
@ -1133,8 +1144,8 @@ void Key_HandleConsoleLink(console_t *con, char *buffer)
{
//okay, its a valid link that they clicked
*end = 0;
#ifdef PLUGINS
if (!Plug_ConsoleLink(buffer+2, info, con->name))
#ifdef PLUGINS //plugins can use these window things like popup menus.
if (con && !Plug_ConsoleLink(buffer+2, info, con->name))
#endif
#ifdef CSQC_DAT
if (!CSQC_ConsoleLink(buffer+2, info))

File diff suppressed because it is too large Load diff

View file

@ -3,25 +3,37 @@
#include "quakedef.h"
#include "shader.h"
void Draw_TextBox (int x, int y, int width, int lines)
//draws the size specified, plus a little extra border (about 8 pixels in each direction, could be more though).
//size is in vpixels.
void Draw_ApproxTextBox (float x, float y, float width, float height)
{
mpic_t *p;
int cx, cy;
int n;
float cx, cy;
int n, lines, columns;
x -= 8;
y -= 8;
// draw left side
cx = x;
cy = y;
p = R2D_SafeCachePic ("gfx/box_tl.lmp");
if (R_GetShaderSizes(p, NULL, NULL, false) != true) //assume none exist
{
R2D_ImageColours(0.0, 0.0, 0.0, 0.5);
R2D_FillBlock(x, y, width*8 + 16, 8 * (2 + lines));
{ //simple fill.
R2D_ImageColours(0.1, 0.1, 0.1, 0.9);
R2D_FillBlock(x, y, width + 16, height + 16);
R2D_ImageColours(1.0, 1.0, 1.0, 1.0);
return;
}
//okay, we're drawing it with pics.
//expand the border to keep things centred.
lines = ceil(height/8);
y -= (lines*8-height)/2;
columns = ceil(width/16)*2; //columns must be a multiple of 2.
x -= (columns*8-width)/2;
// draw left side
cx = x;
cy = y;
if (p)
R2D_ScalePic (cx, cy, 8, 8, p);
p = R2D_SafeCachePic ("gfx/box_ml.lmp");
@ -37,7 +49,7 @@ void Draw_TextBox (int x, int y, int width, int lines)
// draw middle
cx += 8;
while (width > 0)
while (columns > 0)
{
cy = y;
p = R2D_SafeCachePic ("gfx/box_tm.lmp");
@ -55,7 +67,7 @@ void Draw_TextBox (int x, int y, int width, int lines)
p = R2D_SafeCachePic ("gfx/box_bm.lmp");
if (p)
R2D_ScalePic (cx, cy+8, 16, 8, p);
width -= 2;
columns -= 2;
cx += 16;
}
@ -469,8 +481,8 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
int framescroll = 0;
if (option && option->common.type == mt_box && !option->common.ishidden)
{
Draw_TextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height);
{ //FIXME: why is this here? why is this special?
Draw_ApproxTextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height);
option = option->common.next;
}
@ -600,7 +612,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
}
break;
case mt_box:
Draw_TextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height);
Draw_ApproxTextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height);
break;
case mt_slider:
if (option->slider.var)
@ -687,7 +699,7 @@ static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *men
if (option->edit.slim)
x += 8; // more space for cursor
else
Draw_TextBox(x-8, y-8, 16, 1);
Draw_ApproxTextBox(x, y, 16*8, 8);
Draw_FunString(x, y, option->edit.text);
if (menu->selecteditem == option && (int)(realtime*4) & 1)
@ -779,7 +791,7 @@ static void MenuDraw(emenu_t *menu)
// if (omousey > menu->ypos+option->common.posy && omousey < menu->ypos+option->common.posy+option->common.height)
{
int x = omousex+8;
int y = omousey;
int y = omousey+8;
int w;
int h;
int l, lines;
@ -792,11 +804,11 @@ static void MenuDraw(emenu_t *menu)
Font_EndString(font_default);
//figure out how wide that makes the tip
w = 16;
h = (lines+2)*8;
w = 0;
h = lines*8;
for (l = 0; l < lines; l++)
{
int lw = 16+Font_LineWidth(line_start[l], line_end[l])*vid.width/vid.pixelwidth;
int lw = Font_LineWidth(line_start[l], line_end[l])*vid.width/vid.pixelwidth;
if (w < lw)
w = lw;
}
@ -808,9 +820,7 @@ static void MenuDraw(emenu_t *menu)
y -= h;
// draw the background
Draw_TextBox(x, y, (w-16)/8, lines);
x += 8;
y += 8;
Draw_ApproxTextBox(x, y, w, lines*8);
//draw the text
Font_BeginString(font_default, x, y, &x, &y);
@ -2133,8 +2143,12 @@ static int M_Main_AddExtraOptions(emenu_t *mainm, int y)
#ifdef WEBCLIENT
MC_AddConsoleCommandQBigFont(mainm, 72, y, "Updates ", "menu_download\n"); y += 20;
#else
MC_AddConsoleCommandQBigFont(mainm, 72, y, "Packages ", "menu_download\n");
MC_AddConsoleCommandQBigFont(mainm, 72, y, "Packages ", "menu_download\n"); y += 20;
#endif
}
if (Cmd_Exists("menu_mods"))
{
MC_AddConsoleCommandQBigFont(mainm, 72, y, "Mods ", "menu_mods\n"); y += 20;
y += 20;
}
@ -2253,6 +2267,7 @@ void M_Menu_Main_f (void)
p = R2D_SafeCachePic("gfx/menu/title0.lmp");
if (R_GetShaderSizes(p, NULL, NULL, true) > 0)
{
int y = 64;
mainm = M_CreateMenu(0);
mainm->key = MC_Main_Key;
@ -2260,29 +2275,40 @@ void M_Menu_Main_f (void)
MC_AddCenterPicture(mainm, 0, 60, "gfx/menu/title0.lmp");
#ifndef CLIENTONLY
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64, "Single Player", "menu_single\n");
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Single Player", "menu_single\n");
mainm->selecteditem = (menuoption_t *)b;
b->common.width = 12*20;
b->common.height = 20;
y += 20;
#endif
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+20, "MultiPlayer", "menu_multi\n");
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "MultiPlayer", "menu_multi\n");
#ifdef CLIENTONLY
mainm->selecteditem = (menuoption_t *)b;
#endif
b->common.width = 12*20;
b->common.height = 20;
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+40, "Options", "menu_options\n");
y += 20;
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Options", "menu_options\n");
b->common.width = 12*20;
b->common.height = 20;
y += 20;
if (m_helpismedia.value)
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+60, "Media", "menu_media\n");
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Media", "menu_media\n");
else
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+60, "Help", "help\n");
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Help", "help\n");
b->common.width = 12*20;
b->common.height = 20;
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+80, "Quit", "menu_quit\n");
y += 20;
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Mods", "menu_modshelp\n");
b->common.width = 12*20;
b->common.height = 20;
y += 20;
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Quit", "menu_quit\n");
b->common.width = 12*20;
b->common.height = 20;
y += 20;
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 56, mainm->selecteditem->common.posy);
}

View file

@ -521,7 +521,7 @@ static void SL_PostDraw (emenu_t *menu)
h += 4;
h *= 8;
Draw_TextBox(vid.width/2 - w/2-12, vid.height/2 - h/2 - 8-8, w/8+1, h/8+1);
Draw_ApproxTextBox(vid.width/2.0f - w/2-4, vid.height/2.0f - h/2 - 8, w+8, h+8);
lx = vid.width/2 - w/2;
y = vid.height/2 - h/2 - 4;
@ -647,7 +647,7 @@ static void SL_PostDraw (emenu_t *menu)
}
else
{
Draw_TextBox(vid.width/2 - 100-12, vid.height/2 - 32, 200/8, 64/8);
Draw_ApproxTextBox(vid.width/2 - 100, vid.height/2 - 16, 200, 16*3);
Draw_FunStringWidth(vid.width/2 - 100, vid.height/2 - 8, "Querying server", 200, 2, false);
Draw_FunStringWidth(vid.width/2 - 100, vid.height/2 + 0, "Please wait", 200, 2, false);
}
@ -670,9 +670,8 @@ static void SL_PostDraw (emenu_t *menu)
specbutton.width = bw + 16;
specbutton.height = bh + 16;
R2D_ImageColours(1,1,1,1);
Draw_TextBox(lx, y, bw/8, bh/8);
y += 8;
lx += 8;
Draw_ApproxTextBox(lx, y, bw, bh);
if (mousecursor_x >= specbutton.x && mousecursor_x < specbutton.x+specbutton.width)
if (mousecursor_y >= specbutton.y && mousecursor_y < specbutton.y+specbutton.height)
@ -698,9 +697,9 @@ static void SL_PostDraw (emenu_t *menu)
joinbutton.width = bw + 16;
joinbutton.height = bh + 16;
R2D_ImageColours(1,1,1,1);
Draw_TextBox(lx, y, bw/8, bh/8);
y += 8;
lx += 8;
Draw_ApproxTextBox(lx, y, bw, bh);
if (mousecursor_x >= joinbutton.x && mousecursor_x < joinbutton.x+joinbutton.width)
if (mousecursor_y >= joinbutton.y && mousecursor_y < joinbutton.y+joinbutton.height)
@ -712,7 +711,7 @@ static void SL_PostDraw (emenu_t *menu)
else if (isrefreshing)
{
R2D_ImageColours(1,1,1,1);
Draw_TextBox(vid.width/2 - 100-12, vid.height/2 - 32, 200/8, 64/8);
Draw_ApproxTextBox(vid.width/2 - 100-4, vid.height/2 - 24, 200, 64);
Draw_FunStringWidth(vid.width/2 - 100, vid.height/2 - 8, "Refreshing, please wait", 200, 2, false);
Draw_FunStringWidth(vid.width/2 - 100, vid.height/2 + 0, va("%i of %i", Master_NumPolled(), Master_TotalCount()), 200, 2, false);
}

View file

@ -895,6 +895,7 @@ const char *presetexec[] =
"seta r_drawflame 0;"
"seta r_waterstyle 1;"
"seta r_lavastyle 1;" //defer to water
"seta r_fog_cullentities 2;"
// "seta r_slimestyle \"\";" //defer to water
"seta r_coronas 0;"
"seta r_shadow_realtime_dlight 0;"
@ -907,6 +908,7 @@ const char *presetexec[] =
"seta r_replacemodels \"\";"
"seta r_waterwarp 0;"
"seta r_lightstylesmooth 0;"
"seta r_lightstylespeed 0;"
"seta r_part_density 0.25;"
"seta cl_nolerp 1;"
"seta r_lerpmuzzlehack 0;"
@ -957,6 +959,8 @@ const char *presetexec[] =
"v_gunkick 1;"
"cl_rollangle 2.0;"
"cl_bob 0.02;"
"r_fog_cullentities 1;"
"r_lightstylespeed 10;"
"vid_hardwaregamma 1;" //auto hardware gamma, for fast fullscreen and usable windowed.
"r_part_classic_expgrav 1;" //vanillaery
"r_part_classic_opaque 1;"
@ -1144,7 +1148,22 @@ void FPS_Preset_f (void)
, RESTRICT_LOCAL, false);
return;
}
if (!stricmp("shib", arg))
{
Cbuf_InsertText(
"if r_dynamic >= 1\n"
"{\n" //fake it anyway.
"set r_shadow_realtime_dlight 1\n"
"set r_shadow_realtime_dlight_shadows 0\n"
"set r_dynamic 0\n"
"}\n"
"set r_temporalscenecache 1\n" //the main speedup.
"set r_lightstylespeed 0\n" //FIXME: we shouldn't need this, but its too stuttery without.
"set sv_autooffload 1\n" //Needs polish still.
"set gl_pbolightmaps 1\n" //FIXME: this needs to be the default eventually.
, RESTRICT_LOCAL, false);
return;
}
if (!stricmp("qw", arg))
{ //enable qwisms
Cbuf_InsertText(
@ -1380,26 +1399,27 @@ void M_Menu_Render_f (void)
{
MB_REDTEXT("Rendering Options", true),
MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", true),
MB_CHECKBOXCVAR("Graphics", r_graphics, 0), //graphics on / off. Its a general dig at modern games not having any real options.
MB_CHECKBOXCVAR("Disable VIS", r_novis, 0),
MB_CHECKBOXCVAR("Fast Sky", r_fastsky, 0),
MB_CHECKBOXCVARTIP("Graphics", r_graphics, 0, "The only option console games have. Yes, this is a joke cvar. Try toggling it!"), //graphics on / off. Its a general dig at modern games not having any real options.
MB_CHECKBOXCVARTIP("Disable VIS", r_novis, 0, "You shouldn't normally set this. Its better to use vispatches for your maps in order to support transparent water properly."),
MB_CHECKBOXCVARTIP("Fast Sky", r_fastsky, 0, "Use black skies. On modern machines this is more of a stylistic choice that a perfomance helper."),
MB_CHECKBOXCVAR("Lerp Images", gl_lerpimages, 0),
MB_CHECKBOXCVAR("Disable Model Lerp", r_nolerp, 0),
MB_CHECKBOXCVAR("Disable Framegroup Lerp", r_noframegrouplerp, 0),
MB_CHECKBOXCVAR("Lerp Images", gl_lerpimages, 0),
MB_CHECKBOXCVAR("Model Bobbing", cl_item_bobbing, 0),
MB_COMBOCVAR("Water Warp", r_waterwarp, warpopts, warpvalues, NULL),
MB_SLIDER("Water Alpha", r_wateralpha, 0, 1, 0.1, NULL),
MB_SLIDER("Viewmodel Alpha", r_drawviewmodel, 0, 1, 0.1, NULL),
MB_COMBOCVAR("Screen Tints", gl_cshiftenabled, cshiftopts, cshiftvalues, "Changes how screen flashes should be displayed (otherwise known as polyblends)."),
#ifdef QWSKINS
MB_CHECKBOXCVAR("Disable Colormap", gl_nocolors, 0),
MB_CHECKBOXCVAR("Ignore Player Colors", gl_nocolors, 0),
#endif
MB_COMBOCVAR("Log Centerprints", scr_logcenterprint, logcenteropts, logcentervalues, "Display centerprints in the console also."),
MB_CHECKBOXCVAR("FXAA", r_fxaa, 0),
#ifdef GLQUAKE
MB_CHECKBOXCVAR("Bloom", r_bloom, 0),
#endif
MB_CHECKBOXCVAR("HDR", r_hdr_irisadaptation, 0),
MB_CHECKBOXCVAR("Model Bobbing", cl_item_bobbing, 0),
MB_CHECKBOXCVARTIP("HDR", r_hdr_irisadaptation, 0, "Adjust scene brightness to compensate for lighting levels."),
MB_CHECKBOXCVARTIP("Temporal Scene Cache", r_temporalscenecache, 0, "Cache scene data to optimise complex scenes or unvised maps."),
MB_END()
};
menu = M_Options_Title(&y, 0);
@ -4057,23 +4077,14 @@ void M_Menu_ModelViewer_f(void)
}
#endif
typedef struct
{
struct
{
ftemanifest_t *manifest;
char *gamedir;
} *mod;
size_t nummods;
} modmenu_t;
#include "fs.h"
static void Mods_Draw(int x, int y, struct menucustom_s *c, struct emenu_s *m)
{
modmenu_t *mods = c->dptr;
int i = c->dint;
struct modlist_s *mod = Mods_GetMod(i);
c->common.width = vid.width - x - 16;
if (!mods->nummods && !i)
if (!mod && !i)
{
float scale[] = {8,8};
R_DrawTextField(0, y, vid.width, vid.height - y,
@ -4091,139 +4102,55 @@ static void Mods_Draw(int x, int y, struct menucustom_s *c, struct emenu_s *m)
return;
}
if (i < 0 || i > mods->nummods)
if (!mod)
return;
if (mods->mod[i].manifest)
if (mod->manifest)
{
if (mousecursor_y >= y && mousecursor_y < y+8)
Draw_AltFunString(x, y, mods->mod[i].manifest->formalname);
Draw_AltFunString(x, y, mod->manifest->formalname);
else
Draw_FunString(x, y, mods->mod[i].manifest->formalname);
Draw_FunString(x, y, mod->manifest->formalname);
}
else
{
if (mousecursor_y >= y && mousecursor_y < y+8)
Draw_AltFunString(x, y, mods->mod[i].gamedir);
Draw_AltFunString(x, y, mod->gamedir);
else
Draw_FunString(x, y, mods->mod[i].gamedir);
Draw_FunString(x, y, mod->gamedir);
}
}
static qboolean Mods_Key(struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)
{
modmenu_t *mods = c->dptr;
int i;
ftemanifest_t *man;
int gameidx = c->dint;
if (key == K_MOUSE1 || key == K_ENTER || key == K_GP_A)
{
qboolean wasgameless = !*FS_GetGamedir(false);
i = c->dint;
if (i < 0 || i > mods->nummods)
if (!Mods_GetMod(c->dint))
return false;
man = mods->mod[i].manifest;
mods->mod[i].manifest = NULL; //make sure the manifest survives the menu being closed.
M_RemoveMenu(m);
FS_ChangeGame(man, true, true);
Cbuf_AddText(va("\nfs_changegame %u\n", gameidx+1), RESTRICT_LOCAL);
if (wasgameless && !!*FS_GetGamedir(false))
{
//starting to a blank state generally means that the current(engine-default) config settings are utterly useless and windowed by default.
//so generally when switching to a *real* game, we want to restart video just so things like fullscreen etc are saved+used properly.
Cbuf_AddText("\nvid_restart\n", RESTRICT_LOCAL);
}
else
{
//if we're already running a game, this should probably just be a vid_reload instead to ensure that the conback etc is reloaded.
//(a full restart is annoying)
Cbuf_AddText("\nvid_reload\n", RESTRICT_LOCAL);
}
return true;
}
return false;
}
static void Mods_Remove (struct emenu_s *m)
{
modmenu_t *mods = m->data;
int i;
for (i = 0; i < mods->nummods; i++)
{
if (mods->mod[i].manifest)
FS_Manifest_Free(mods->mod[i].manifest);
Z_Free(mods->mod[i].gamedir);
}
Z_Free(mods->mod);
mods->mod = NULL;
}
static qboolean Mods_AddManifest(void *usr, ftemanifest_t *man)
{
modmenu_t *mods = usr;
int i = mods->nummods;
mods->mod = BZ_Realloc(mods->mod, (i+1) * sizeof(*mods->mod));
mods->mod[i].manifest = man;
mods->mod[i].gamedir = NULL;
mods->nummods = i+1;
return true;
}
static int QDECL Mods_AddGamedir(const char *fname, qofs_t fsize, time_t mtime, void *usr, searchpathfuncs_t *spath)
{
modmenu_t *mods = usr;
size_t l = strlen(fname);
int i, p;
char gamedir[MAX_QPATH];
if (l && fname[l-1] == '/' && l < countof(gamedir))
{
l--;
memcpy(gamedir, fname, l);
gamedir[l] = 0;
for (i = 0; i < mods->nummods; i++)
{
//don't add dupes (can happen from gamedir+homedir)
//if the gamedir was already included in one of the manifests, don't bother including it again.
//this generally removes id1.
if (mods->mod[i].manifest)
{
for (p = 0; p < countof(fs_manifest->gamepath); p++)
if (mods->mod[i].manifest->gamepath[p].path)
if (!Q_strcasecmp(mods->mod[i].manifest->gamepath[p].path, gamedir))
return true;
}
else if (mods->mod[i].gamedir)
{
if (!Q_strcasecmp(mods->mod[i].gamedir, gamedir))
return true;
}
}
mods->mod = BZ_Realloc(mods->mod, (i+1) * sizeof(*mods->mod));
mods->mod[i].manifest = NULL;
mods->mod[i].gamedir = Z_StrDup(gamedir);
mods->nummods = i+1;
}
return true;
}
#include "fs.h"
void M_Menu_Mods_f (void)
{
modmenu_t mods;
menucustom_t *c;
emenu_t *menu;
size_t i;
extern qboolean com_homepathenabled;
memset(&mods, 0, sizeof(mods));
FS_EnumerateKnownGames(Mods_AddManifest, &mods);
if (com_homepathenabled)
Sys_EnumerateFiles(com_homepath, "*", Mods_AddGamedir, &mods, NULL);
Sys_EnumerateFiles(com_gamepath, "*", Mods_AddGamedir, &mods, NULL);
//FIXME: sort by mtime?
menu = M_CreateMenu(sizeof(modmenu_t));
*(modmenu_t*)menu->data = mods;
menu = M_CreateMenu(0);
if (COM_FCheckExists("gfx/p_option.lmp"))
{
MC_AddPicture(menu, 16, 4, 32, 144, "gfx/qplaque.lmp");
@ -4231,7 +4158,7 @@ void M_Menu_Mods_f (void)
}
MC_AddFrameStart(menu, 32);
for (i = 0; i < mods.nummods || i<1; i++)
for (i = 0; i<1 || Mods_GetMod(i); i++)
{
c = MC_AddCustom(menu, 64, 32+i*8, menu->data, i, NULL);
if (!menu->cursoritem)
@ -4239,9 +4166,8 @@ void M_Menu_Mods_f (void)
c->common.height = 8;
c->draw = Mods_Draw;
c->key = Mods_Key;
menu->remove = Mods_Remove;
}
MC_AddFrameEnd(menu, 32+i*8);
MC_AddFrameEnd(menu, 32);
}
#if 0

View file

@ -136,7 +136,7 @@ void M_MenuS_Box_f (void)
return;
}
MC_AddBox(menu_script, x, y, width/8, height/8);
MC_AddBox(menu_script, x, y, width, height);
}
void M_MenuS_CheckBox_f (void)

View file

@ -351,7 +351,7 @@ void M_Menu_SinglePlayer_f (void)
MC_AddWhiteText(menu, 84, 0, 12*8, "This build is unable", false);
MC_AddWhiteText(menu, 84, 0, 13*8, "to start a local game", false);
MC_AddBox (menu, 60, 10*8, 25, 4);
MC_AddBox (menu, 60, 11*8, 25*8, 4*8);
#else
switch(M_GameType())
@ -540,7 +540,7 @@ void M_Menu_SinglePlayer_f (void)
p = R2D_SafeCachePic("gfx/sp_menu.lmp");
if (!p)
{
MC_AddBox (menu, 60, 10*8, 23, 4);
MC_AddBox (menu, 60, 10*8, 23*8, 4*8);
MC_AddWhiteText(menu, 92, 0, 12*8, "Couldn't find file", false);
MC_AddWhiteText(menu, 92, 0, 13*8, "gfx/sp_menu.lmp", false);
@ -619,6 +619,10 @@ static void M_DemoDraw(int x, int y, menucustom_t *control, emenu_t *menu)
demoitem_t *item, *lostit;
int ty;
char syspath[MAX_OSPATH];
if (FS_NativePath(info->fs->path, (info->fs->fsroot==FS_GAME)?FS_GAMEONLY:info->fs->fsroot, syspath, sizeof(syspath)))
Draw_FunString(x, y-16, syspath);
ty = vid.height-24;
item = info->selected;
while(item)
@ -954,16 +958,25 @@ static void ShowDemoMenu (emenu_t *menu, const char *path)
if (path != info->fs->path)
{
if (*path == '/')
if (*path == '/' && info->fs->fsroot != FS_SYSTEM)
path++;
Q_strncpyz(info->fs->path, path, sizeof(info->fs->path));
}
if (info->fs->fsroot == FS_GAME)
{
if (!strcmp(path, "../"))
{
Q_strncpyz(info->fs->path, "", sizeof(info->fs->path));
info->fs->fsroot = FS_ROOT;
}
}
else if (info->fs->fsroot == FS_ROOT)
{
if (!strcmp(path, "../"))
{
FS_NativePath("", FS_ROOT, info->fs->path, sizeof(info->fs->path));
Q_strncatz(info->fs->path, "../", sizeof(info->fs->path));
info->fs->fsroot = FS_SYSTEM;
while((s = strchr(info->fs->path, '\\')))
*s = '/';
@ -1003,7 +1016,7 @@ static void ShowDemoMenu (emenu_t *menu, const char *path)
Q_snprintfz(match, sizeof(match), "%s../", info->fs->path);
DemoAddItem(match, 0, 0, info, NULL);
}
else if (info->fs->fsroot == FS_GAME)
else if (info->fs->fsroot == FS_GAME || info->fs->fsroot == FS_ROOT)
{
Q_snprintfz(match, sizeof(match), "../");
DemoAddItem(match, 0, 0, info, NULL);
@ -1016,6 +1029,13 @@ static void ShowDemoMenu (emenu_t *menu, const char *path)
Q_snprintfz(match, sizeof(match), "/*");
Sys_EnumerateFiles("", match, DemoAddItem, info, NULL);
}
else if (info->fs->fsroot == FS_ROOT)
{
Q_snprintfz(match, sizeof(match), "%s*", info->fs->path);
if (*com_homepath)
Sys_EnumerateFiles(com_homepath, match, DemoAddItem, info, NULL);
Sys_EnumerateFiles(com_gamepath, match, DemoAddItem, info, NULL);
}
else
{
Q_snprintfz(match, sizeof(match), "%s*", info->fs->path);

View file

@ -415,29 +415,43 @@ void M_Restart_f(void)
typedef struct
{
menu_t m;
void (*callback)(void *, int);
void (*callback)(void *, promptbutton_t);
void *ctx;
int lines;
const char *messages;
const char *buttons[3];
int kbutton, mbutton;
qboolean mousedown;
} promptmenu_t;
static qboolean Prompt_MenuKeyEvent(struct menu_s *gm, qboolean isdown, unsigned int devid, int key, int unicode)
{
promptmenu_t *m = (promptmenu_t*)gm;
int action;
void (*callback)(void *, int) = m->callback;
promptbutton_t action;
void (*callback)(void *, promptbutton_t) = m->callback;
void *ctx = m->ctx;
extern qboolean keydown[];
if ((!isdown) != (key==K_MOUSE1))
return false; //don't care about releases, unless mouse1.
if (key == K_MOUSE1)
{ //mouse events fire their action on release.
if (isdown)
{
m->mousedown = true; //so we don't respond to stray release events.
return true;
}
else if (!m->mousedown)
return false; //looks like a stray release event. ignore it.
}
else
{ //keyboard events fire on press.
if (!isdown)
return false;
}
if (key == 'n' || key == 'N')
action = 1;
action = PROMPT_NO;
else if (key == 'y' || key == 'Y')
action = 0;
action = PROMPT_YES;
else if (key==K_RIGHTARROW || key==K_GP_DPAD_RIGHT || key==K_DOWNARROW || key==K_GP_DPAD_DOWN || (key == K_TAB && !keydown[K_LSHIFT] && !keydown[K_RSHIFT]))
{
for(;;)
@ -463,18 +477,29 @@ static qboolean Prompt_MenuKeyEvent(struct menu_s *gm, qboolean isdown, unsigned
return true;
}
else if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2)
action = -1;
action = PROMPT_CANCEL;
else if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1 || key == K_GP_A)
{
int button;
if (key == K_MOUSE1)
action = m->mbutton;
button = m->mbutton;
else
action = m->kbutton;
button = m->kbutton;
if (action == -1) //nothing focused
return false;
if (action == 2) //convert buttons to actions...
action = -1;
switch(button)
{
case 0:
action = PROMPT_YES;
break;
case 1:
action = PROMPT_NO;
break;
case 2:
action = PROMPT_CANCEL;
break;
default:
return false; //nothing focused.
}
}
else
return false; // no idea what that is
@ -491,28 +516,29 @@ static void Prompt_Draw(struct menu_s *g)
promptmenu_t *m = (promptmenu_t*)g;
int x = 64;
int y = 76;
int w = 224;
int h = m->lines*8+16;
float scale = Font_CharVHeight(font_console);
int w = 224*scale/8;
int h = (m->lines+3)*scale;
int i;
const char *msg = m->messages;
int bx[4];
x = ((vid.width-w)>>1);
Draw_TextBox(x-8, y, w/8, h/8);
y+=8;
Draw_ApproxTextBox(x, y, w, h);
y+=scale;
for (i = 0; i < m->lines; i++, msg = msg+strlen(msg)+1)
{
Draw_FunStringWidth(x, y, msg, w, 2, false);
y+=8;
Draw_FunStringWidthFont(font_console, x, y, msg, w, 2, false);
y+=scale;
}
y+=8;
y+=scale;
m->mbutton = -1;
bx[0] = x;
bx[1] = x+w/3;
bx[2] = x+w-w/3;
bx[3] = x+w;
if (mousecursor_y >= y && mousecursor_y <= y+8)
if (mousecursor_y >= y && mousecursor_y <= y+scale)
{
for (i = 0; i < 3; i++)
{
@ -528,25 +554,25 @@ static void Prompt_Draw(struct menu_s *g)
{
float alphamax = 0.5, alphamin = 0.2;
R2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);
R2D_FillBlock(bx[i], y, bx[i+1]-bx[i], 8);
R2D_FillBlock(bx[i], y, bx[i+1]-bx[i], scale);
R2D_ImageColours(1,1,1,1);
}
Draw_FunStringWidth(bx[i], y, m->buttons[i], bx[i+1]-bx[i], 2, m->kbutton==i);
Draw_FunStringWidthFont(font_console, bx[i], y, m->buttons[i], bx[i+1]-bx[i], 2, m->kbutton==i);
}
}
}
static void Prompt_Release(struct menu_s *gm)
{
promptmenu_t *m = (promptmenu_t*)gm;
void (*callback)(void *, int) = m->callback;
void (*callback)(void *, promptbutton_t) = m->callback;
void *ctx = m->ctx;
m->callback = NULL;
if (callback)
callback(ctx, -1);
callback(ctx, PROMPT_CANCEL);
Z_Free(m);
}
void Menu_Prompt (void (*callback)(void *, int), void *ctx, const char *messages, char *optionyes, char *optionno, char *optioncancel)
void Menu_Prompt (void (*callback)(void *, promptbutton_t), void *ctx, const char *messages, char *optionyes, char *optionno, char *optioncancel)
{
promptmenu_t *m;
char *t;
@ -1150,20 +1176,20 @@ static char *quitMessage [] =
NULL };*/
void Cmd_WriteConfig_f(void);
static void M_Menu_DoQuit(void *ctx, int option)
static void M_Menu_DoQuit(void *ctx, promptbutton_t option)
{
if (option == 0) //'yes - quit'
if (option == PROMPT_YES) //'yes - quit'
Cmd_ExecuteString("menu_quit force\n", RESTRICT_LOCAL);
// else if (option == 1) //'no - don't quit'
// else if (option == -1) //'cancel - don't quit'
// else if (option == PROMPT_NO) //'no - don't quit'
// else if (option == PROMPT_CANCEL) //'cancel - don't quit'
}
static void M_Menu_DoQuitSave(void *ctx, int option)
static void M_Menu_DoQuitSave(void *ctx, promptbutton_t option)
{
if (option == 0) //'yes - save-and-quit'
if (option == PROMPT_YES) //'yes - save-and-quit'
Cmd_ExecuteString("menu_quit forcesave\n", RESTRICT_LOCAL);
else if (option == 1) //'no - nosave-and-quit'
else if (option == PROMPT_NO) //'no - nosave-and-quit'
Cmd_ExecuteString("menu_quit force\n", RESTRICT_LOCAL);
// else if (option == -1) //'cancel - don't quit'
// else if (option == PROMPT_CANCEL) //'cancel - don't quit'
}
//quit menu

View file

@ -121,7 +121,13 @@ void Menu_Unlink(menu_t *menu);
void Menu_Push(menu_t *menu, qboolean prompt);
menu_t *Menu_FindContext(void *ctx);
void Menu_Prompt (void (*callback)(void *, int), void *ctx, const char *messages, char *optionyes, char *optionno, char *optioncancel);
typedef enum
{
PROMPT_YES = 0,
PROMPT_NO = 1,
PROMPT_CANCEL = -1,
} promptbutton_t;
void Menu_Prompt (void (*callback)(void *, promptbutton_t), void *ctx, const char *messages, char *optionyes, char *optionno, char *optioncancel);
#ifndef NOBUILTINMENUS
@ -491,6 +497,7 @@ void MP_CvarChanged(cvar_t *var);
qboolean MP_Init (void);
void MP_Shutdown (void);
qboolean MP_Toggle(int mode);
void MP_RendererRestarted(void);
void MP_Draw(void);
qboolean MP_UsingGamecodeLoadingScreen(void);
void MP_RegisterCvarsAndCmds(void);

View file

@ -208,7 +208,8 @@ const char *Mod_GetBoneName(struct model_s *model, int bonenum);
void Draw_FunString(float x, float y, const void *str);
void Draw_AltFunString(float x, float y, const void *str);
void Draw_FunStringWidth(float x, float y, const void *str, int width, int rightalign, qboolean highlight);
void Draw_FunStringWidthFont(struct font_s *font, float x, float y, const void *str, int width, int rightalign, qboolean highlight);
#define Draw_FunStringWidth(x,y,str,width,rightalign,highlight) Draw_FunStringWidthFont(font_default,x,y,str,width,rightalign,highlight)
extern int r_regsequence;

View file

@ -1084,104 +1084,129 @@ void QCBUILTIN PF_cl_setlocaluserinfo (pubprogfuncs_t *prinst, struct globalvars
}
#include "fs.h"
static struct modlist_s
{
ftemanifest_t *manifest;
char *gamedir;
char *description;
} *modlist;
static size_t nummods;
static qboolean modsinited;
static qboolean Mods_AddManifest(void *usr, ftemanifest_t *man)
{
int i = nummods;
modlist = BZ_Realloc(modlist, (i+1) * sizeof(*modlist));
modlist[i].manifest = man;
modlist[i].gamedir = man->updatefile;
modlist[i].description = man->formalname;
nummods = i+1;
return true;
}
static int QDECL Mods_AddGamedir(const char *fname, qofs_t fsize, time_t mtime, void *usr, searchpathfuncs_t *spath)
{
char *f;
size_t l = strlen(fname);
int i, p;
char gamedir[MAX_QPATH];
if (l && fname[l-1] == '/' && l < countof(gamedir))
{
l--;
memcpy(gamedir, fname, l);
gamedir[l] = 0;
for (i = 0; i < nummods; i++)
{
//don't add dupes (can happen from basedir+homedir)
//if the gamedir was already included in one of the manifests, don't bother including it again.
//this generally removes id1.
if (modlist[i].manifest)
{
for (p = 0; p < countof(fs_manifest->gamepath); p++)
if (modlist[i].manifest->gamepath[p].path)
if (!Q_strcasecmp(modlist[i].manifest->gamepath[p].path, gamedir))
return true;
}
else if (modlist[i].gamedir)
{
if (!Q_strcasecmp(modlist[i].gamedir, gamedir))
return true;
}
}
f = FS_MallocFile(va("%s%s/modinfo.txt", (const char*)usr, gamedir), FS_SYSTEM, NULL);
if (f)
{
modlist = BZ_Realloc(modlist, (i+1) * sizeof(*modlist));
modlist[i].manifest = NULL;
modlist[i].gamedir = Z_StrDup(gamedir);
modlist[i].description = f;
nummods = i+1;
}
}
return true;
}
static void Mods_InitModList (void)
{
extern qboolean com_homepathenabled;
FS_EnumerateKnownGames(Mods_AddManifest, NULL);
if (com_homepathenabled)
Sys_EnumerateFiles(com_homepath, "*", Mods_AddGamedir, com_homepath, NULL);
Sys_EnumerateFiles(com_gamepath, "*", Mods_AddGamedir, com_gamepath, NULL);
}
void QCBUILTIN PF_cl_getgamedirinfo(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
size_t diridx = G_FLOAT(OFS_PARM0);
int propidx = G_FLOAT(OFS_PARM1);
enum getgamedirinfo_e propidx = G_FLOAT(OFS_PARM1);
if (!modsinited)
struct modlist_s *mod, current;
if (G_FLOAT(OFS_PARM0) == -1)
{
modsinited = true;
Mods_InitModList();
current.description = fs_manifest->formalname;
current.gamedir = FS_GetGamedir(true);
current.manifest = fs_manifest;
mod = &current;
}
else
mod = Mods_GetMod(diridx);
G_INT(OFS_RETURN) = 0;
if (diridx < nummods)
if (mod)
{
switch(propidx)
{
case 1: //description (contents of modinfo.txt)
if (modlist[diridx].description)
RETURN_TSTRING(modlist[diridx].description);
case GGDI_GAMEDIR: //name
RETURN_TSTRING(mod->gamedir);
break;
case 2: //cvars
if (modlist[diridx].manifest)
if (modlist[diridx].manifest->defaultexec)
RETURN_TSTRING(modlist[diridx].manifest->defaultexec);
case GGDI_ALLGAMEDIRS:
{
char *dirs = NULL;
size_t d;
ftemanifest_t *man = mod->manifest?mod->manifest:fs_manifest;
//the basedirs
for (d = 0; d < countof(man->gamepath); d++)
{
if (man->gamepath[d].path && man->gamepath[d].flags&GAMEDIR_BASEGAME)
{
if (dirs)
Z_StrCat(&dirs, ";");
Z_StrCat(&dirs, man->gamepath[d].path);
}
}
if (mod->manifest)
{ //the manifest's mod dirs
for (d = 0; d < countof(man->gamepath); d++)
{
if (man->gamepath[d].path && !(man->gamepath[d].flags&GAMEDIR_BASEGAME))
{
if (dirs)
Z_StrCat(&dirs, ";");
Z_StrCat(&dirs, man->gamepath[d].path);
}
}
}
else //the specified gamedir
{
if (dirs)
Z_StrCat(&dirs, ";");
Z_StrCat(&dirs, mod->gamedir);
}
RETURN_TSTRING(dirs?dirs:"");
Z_Free(dirs);
}
break;
case 0: //name
RETURN_TSTRING(modlist[diridx].gamedir);
case GGDI_DESCRIPTION: //description (contents of modinfo.txt)
if (mod->description)
RETURN_TSTRING(mod->description);
break;
case GGDI_OVERRIDES: //cvars
if (mod->manifest)
if (mod->manifest->defaultexec)
RETURN_TSTRING(mod->manifest->defaultexec);
break;
case GGDI_LOADCOMMAND: //load command
RETURN_TSTRING(va("fs_changegame %u", (unsigned int)diridx+1u));
break;
case GGDI_ICON: //icon
{
char iname[MAX_QPATH];
shader_t *shader;
Q_snprintfz(iname, sizeof(iname), "gamedir/%u", (unsigned) diridx);
shader = R_RegisterShader(iname, SUF_2D,
"{\n"
"affine\n"
"nomipmaps\n"
"program default2d#PREMUL\n"
"{\n"
"clampmap $diffuse\n"
"blendfunc gl_one gl_one_minus_src_alpha\n"
"}\n"
"sort additive\n"
"}\n"
);
if (shader && !shader->defaulttextures->base)
{ //no textures yet? do something about it!
void *data = NULL;
size_t i;
qofs_t sz;
const char *extensions[] = {
#ifdef IMAGEFMT_PNG
".png",
#endif
".tga",
#ifdef IMAGEFMT_BMP
".ico",
#endif
};
if (mod->manifest && mod->manifest->iconname)
{
for (i = 0; i < countof(extensions) && !data; i++)
{
COM_StripExtension(mod->manifest->filename, iname, sizeof(iname));
COM_RequireExtension(iname, extensions[i], sizeof(iname));
data = FS_MallocFile(iname, FS_SYSTEM, &sz);
}
}
for (i = 0; i < countof(extensions) && !data; i++)
data = FS_MallocFile(va("%s/icon%s", mod->gamedir, extensions[i]), FS_ROOT, &sz);
Q_snprintfz(iname, sizeof(iname), "gamedir/%u", (unsigned) diridx);
shader->defaulttextures->base = Image_CreateTexture(iname, NULL, IF_PREMULTIPLYALPHA);
if (data)
Image_LoadTextureFromMemory(shader->defaulttextures->base, shader->defaulttextures->base->flags, iname, iname, data, sz);
}
if (shader && TEXLOADED(shader->defaulttextures->base))
RETURN_TSTRING(shader->name);
}
break;
}
}

View file

@ -984,7 +984,9 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *fte_restrict in, entity_t *ft
out->flags |= RF_DEPTHHACK|RF_WEAPONMODEL;
out->pvscache = p->pvsinfo; //for the areas.
out->pvscache.num_leafs = -1; //make visible globally
#if defined(Q2BSPS) || defined(Q3BSPS) || defined(TERRAIN)
out->pvscache.headnode = 0;
#endif
#endif
}
@ -2176,6 +2178,66 @@ void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
r[1] = 0;
r[2] = 0;
#ifdef HAVE_LEGACY
if (csqc_isdarkplaces && prinst == csqc_world.progs)
{
switch(parametertype)
{
case VF_VIEWPORT:
r[0] = r_refdef.grect.width * (float)vid.pixelwidth / vid.width;;
r[1] = r_refdef.grect.height * (float)vid.pixelheight / vid.height;
return;
case VF_SIZE_X:
*r = r_refdef.grect.width * (float)vid.pixelwidth / vid.width;
return;
case VF_SIZE_Y:
*r = r_refdef.grect.height * (float)vid.pixelheight / vid.height;
return;
case VF_SIZE:
r[0] = r_refdef.grect.width * (float)vid.pixelwidth / vid.width;;
r[1] = r_refdef.grect.height * (float)vid.pixelheight / vid.height;
r[2] = 0;
return;
case VF_DP_MAINVIEW:
r[0] = 1;
return;
case VF_DP_MINFPS_QUALITY:
r[0] = 1;
return;
case VF_DP_CLEARSCREEN:
r[0] = 0;
return;
case VF_DP_FOG_DENSITY:
r[0] = r_refdef.globalfog.density;
return;
case VF_DP_FOG_COLOR:
r[0] = r_refdef.globalfog.colour[0];
r[1] = r_refdef.globalfog.colour[1];
r[2] = r_refdef.globalfog.colour[2];
return;
case VF_DP_FOG_COLOR_R:
r[0] = r_refdef.globalfog.colour[0];
return;
case VF_DP_FOG_COLOR_G:
r[0] = r_refdef.globalfog.colour[1];
return;
case VF_DP_FOG_COLOR_B:
r[0] = r_refdef.globalfog.colour[2];
return;
case VF_DP_FOG_ALPHA:
r[0] = r_refdef.globalfog.alpha;
return;
case VF_DP_FOG_START:
case VF_DP_FOG_END:
case VF_DP_FOG_HEIGHT:
case VF_DP_FOG_FADEDEPTH:
return;
default:
break;
}
}
#endif
switch(parametertype)
{
nogameaccess:
@ -2264,24 +2326,14 @@ nogameaccess:
case VF_SIZE_X:
*r = r_refdef.grect.width;
if (csqc_isdarkplaces && prinst == csqc_world.progs)
*r *= (float)vid.pixelwidth / vid.width;
break;
case VF_SIZE_Y:
*r = r_refdef.grect.height;
if (csqc_isdarkplaces && prinst == csqc_world.progs)
*r *= (float)vid.pixelheight / vid.height;
break;
case VF_SIZE:
r[0] = r_refdef.grect.width;
r[1] = r_refdef.grect.height;
r[2] = 0;
if (csqc_isdarkplaces && prinst == csqc_world.progs)
{
r[0] *= (float)vid.pixelwidth / vid.width;
r[1] *= (float)vid.pixelheight / vid.height;
}
break;
case VF_MIN_X:
@ -2376,8 +2428,54 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
R2D_Flush();
csqc_rebuildmatricies = true;
G_FLOAT(OFS_RETURN) = 1;
#ifdef HAVE_LEGACY
if (csqc_isdarkplaces && prinst == csqc_world.progs)
{
switch(parametertype)
{
case VF_VIEWPORT:
r_refdef.grect.x = p[0] * (float)vid.width / vid.pixelwidth;
r_refdef.grect.y = p[1] * (float)vid.height / vid.pixelheight;
p = G_VECTOR(OFS_PARM2);
r_refdef.grect.width = p[0] * (float)vid.width / vid.pixelwidth;
r_refdef.grect.height = p[1] * (float)vid.height / vid.pixelheight;
r_refdef.dirty |= RDFD_FOV;
return;
case VF_SIZE_X:
r_refdef.grect.width = *p * (float)vid.width / vid.pixelwidth;
r_refdef.dirty |= RDFD_FOV;
return;
case VF_SIZE_Y:
r_refdef.grect.height = *p * (float)vid.height / vid.pixelheight;
r_refdef.dirty |= RDFD_FOV;
return;
case VF_SIZE:
r_refdef.grect.width = p[0] * (float)vid.width / vid.pixelwidth;
r_refdef.grect.height = p[1] * (float)vid.height / vid.pixelheight;
r_refdef.dirty |= RDFD_FOV;
return;
case VF_DP_MAINVIEW:
case VF_DP_MINFPS_QUALITY:
case VF_DP_CLEARSCREEN:
case VF_DP_FOG_DENSITY:
case VF_DP_FOG_COLOR:
case VF_DP_FOG_COLOR_R:
case VF_DP_FOG_COLOR_G:
case VF_DP_FOG_COLOR_B:
case VF_DP_FOG_ALPHA:
case VF_DP_FOG_START:
case VF_DP_FOG_END:
case VF_DP_FOG_HEIGHT:
case VF_DP_FOG_FADEDEPTH:
return;
default:
break;
}
}
#endif
switch(parametertype)
{
case VF_ACTIVESEAT:
@ -2497,12 +2595,6 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
r_refdef.grect.width = p[0];
r_refdef.grect.height = p[1];
r_refdef.dirty |= RDFD_FOV;
if (csqc_isdarkplaces)
{
r_refdef.grect.width *= (float)vid.width / vid.pixelwidth;
r_refdef.grect.height *= (float)vid.height / vid.pixelheight;
}
break;
case VF_MIN_X:
@ -3740,6 +3832,38 @@ static void QCBUILTIN PF_cl_setpause (pubprogfuncs_t *prinst, struct globalvars_
cl.implicitpause = !!G_FLOAT(OFS_PARM0);
}
static void QCBUILTIN PF_cl_RotateMoves (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ //changes the past...
float *angles = G_VECTOR(OFS_PARM0);
int frame;
vec3_t of,ou, nf,nu;
vec4_t mat[3];
vec3_t a;
int seat = ((prinst->callargc>1)?G_FLOAT(OFS_PARM1):csqc_playerseat);
if (seat < 0 || seat >= MAX_SPLITS)
{
G_FLOAT(OFS_RETURN) = false;
return;
}
AngleVectorsFLU(angles, mat[0], mat[1], mat[2]);
mat[0][3] = mat[1][3] = mat[2][3] = 0;
for (frame = 0; frame < countof(cl.outframes); frame++)
{
if (cl.outframes[frame].cmd_sequence > cl.ackedmovesequence)
{
a[0] = SHORT2ANGLE(cl.outframes[frame].cmd[seat].angles[0]);
a[1] = SHORT2ANGLE(cl.outframes[frame].cmd[seat].angles[1]);
a[2] = SHORT2ANGLE(cl.outframes[frame].cmd[seat].angles[2]);
AngleVectors(a, of, NULL, ou);
VectorTransform(of, mat, nf);
VectorTransform(ou, mat, nu);
VectorAngles(nf, nu, a, false);
cl.outframes[frame].cmd[seat].angles[0] = ANGLE2SHORT(a[0]);
cl.outframes[frame].cmd[seat].angles[1] = ANGLE2SHORT(a[1]);
cl.outframes[frame].cmd[seat].angles[2] = ANGLE2SHORT(a[2]);
}
}
}
//get the input commands, and stuff them into some globals.
static void QCBUILTIN PF_cs_getinputstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -6094,6 +6218,12 @@ static void QCBUILTIN PF_V_CalcRefdef(pubprogfuncs_t *prinst, struct globalvars_
VectorCopy(savedvel, csqc_playerview->simvel);
}
static void QCBUILTIN PF_getlocationname(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
float *loc = G_VECTOR(OFS_PARM0);
RETURN_TSTRING(TP_LocationName(loc));
}
#if 1
//static void QCBUILTIN PF_ReadServerEntityState(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
//{
@ -6641,6 +6771,7 @@ static struct {
{"processmodelevents", PF_processmodelevents, 0},
{"getnextmodelevent", PF_getnextmodelevent, 0},
{"getmodeleventidx", PF_getmodeleventidx, 0},
{"getlocationname", PF_getlocationname, 0},
{"crossproduct", PF_crossproduct, 0},
{"pushmove", PF_pushmove, 0},
@ -7085,6 +7216,9 @@ static struct {
#endif
{"netaddress_resolve", PF_netaddress_resolve, 625},
{"getgamedirinfo", PF_cl_getgamedirinfo, 626},
#ifdef PACKAGEMANAGER
{"getpackagemanagerinfo", PF_cl_getpackagemanagerinfo,0},
#endif
{"sprintf", PF_sprintf, 627},
{"getsurfacenumtriangles", PF_getsurfacenumtriangles, 628},
{"getsurfacetriangle", PF_getsurfacetriangle, 629},
@ -7097,7 +7231,7 @@ static struct {
// {NULL, PF_Fixme, 645},
// {NULL, PF_Fixme, 646},
// {NULL, PF_Fixme, 647},
// {NULL, PF_Fixme, 648},
{"CL_RotateMoves", PF_cl_RotateMoves, 648},
{"digest_hex", PF_digest_hex, 639},
{"digest_ptr", PF_digest_ptr, 0},
{"V_CalcRefdef", PF_V_CalcRefdef, 640},

View file

@ -1372,6 +1372,7 @@ static struct
func_t toggle;
func_t consolecommand;
func_t gethostcachecategory;
func_t rendererrestarted;
} mpfuncs;
jmp_buf mp_abort;
@ -1974,6 +1975,12 @@ void QCBUILTIN PF_crypto_getidfp(pubprogfuncs_t *prinst, struct globalvars_s *pr
//not supported.
G_INT(OFS_RETURN) = 0;
}
//float(string serveraddress) crypto_getidstatus
void QCBUILTIN PF_crypto_getidstatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
//not supported.
G_INT(OFS_RETURN) = 0;
}
//string(string serveraddress) crypto_getencryptlevel
void QCBUILTIN PF_crypto_getencryptlevel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -1992,6 +1999,12 @@ void QCBUILTIN PF_crypto_getmyidfp(pubprogfuncs_t *prinst, struct globalvars_s *
//not supported.
G_INT(OFS_RETURN) = 0;
}
//float(float i) PF_crypto_getmyidstatus
void QCBUILTIN PF_crypto_getmyidstatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
//not supported.
G_INT(OFS_RETURN) = 0;
}
static void QCBUILTIN PF_m_precache_model(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -2328,7 +2341,7 @@ static struct {
{"altstr_ins", PF_altstr_ins, 86},
{"findflags", PF_FindFlags, 87},
{"findchainflags", PF_menu_findchainflags, 88},
{"mcvar_defstring", PF_cvar_defstring, 89},
{"cvar_defstring", PF_cvar_defstring, 89},
{"setmodel", PF_m_setmodel, 90},
{"precache_model", PF_m_precache_model, 91},
{"setorigin", PF_m_setorigin, 92},
@ -2489,7 +2502,7 @@ static struct {
{"tokenizebyseparator", PF_tokenizebyseparator, 479},
{"strtolower", PF_strtolower, 480},
{"strtoupper", PF_strtoupper, 481},
{"cvar_defstring", PF_cvar_defstring, 482},
{"csqc_cvar_defstring", PF_cvar_defstring, 482},
// {NULL, PF_Fixme, 483},
{"strreplace", PF_strreplace, 484},
{"strireplace", PF_strireplace, 485},
@ -2567,6 +2580,9 @@ static struct {
#endif
{"netaddress_resolve", PF_netaddress_resolve, 625},
{"getgamedirinfo", PF_cl_getgamedirinfo, 626},
#ifdef PACKAGEMANAGER
{"getpackagemanagerinfo", PF_cl_getpackagemanagerinfo,0},
#endif
{"sprintf", PF_sprintf, 627},
// {NULL, PF_Fixme, 628},
// {NULL, PF_Fixme, 629},
@ -2582,9 +2598,9 @@ static struct {
{"digest_hex", PF_digest_hex, 639},
{"digest_ptr", PF_digest_ptr, 0},
// {NULL, PF_Fixme, 640},
{"crypto_getmyidstatus", PF_crypto_getmyidfp, 641},
// {NULL, PF_Fixme, 642},
// {NULL, PF_Fixme, 643},
{"crypto_getmyidstatus", PF_crypto_getmyidstatus, 641},
// {"coverage", PF_Fixme, 642},
{"crypto_getidstatus", PF_crypto_getidstatus, 643},
// {NULL, PF_Fixme, 644},
// {NULL, PF_Fixme, 645},
// {NULL, PF_Fixme, 646},
@ -3059,6 +3075,7 @@ qboolean MP_Init (void)
mpfuncs.toggle = PR_FindFunction(menu_world.progs, "m_toggle", PR_ANY);
mpfuncs.consolecommand = PR_FindFunction(menu_world.progs, "m_consolecommand", PR_ANY);
mpfuncs.gethostcachecategory = PR_FindFunction(menu_world.progs, "m_gethostcachecategory", PR_ANY);
mpfuncs.rendererrestarted = PR_FindFunction(menu_world.progs, "Menu_RendererRestarted", PR_ANY);
if (mpfuncs.init)
PR_ExecuteProgram(menu_world.progs, mpfuncs.init);
inmenuprogs--;
@ -3203,6 +3220,38 @@ int MP_GetServerCategory(int index)
return category;
}
void MP_RendererRestarted(void)
{
int i;
if (!menu_world.progs)
return;
menu_world.worldmodel = cl.worldmodel;
for (i = 0; i < MAX_CSMODELS; i++)
{
cl.model_csqcprecache[i] = NULL;
}
//FIXME: registered shaders
//let the csqc know that its rendertargets got purged
if (mpfuncs.rendererrestarted)
{
void *pr_globals = PR_globals(menu_world.progs, PR_CURRENT);
(((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(menu_world.progs, rf->description));
PR_ExecuteProgram(menu_world.progs, mpfuncs.rendererrestarted);
}
//in case it drew to any render targets.
if (R2D_Flush)
R2D_Flush();
if (*r_refdef.rt_destcolour[0].texname)
{
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname));
BE_RenderToTextureUpdate2d(true);
}
}
void MP_Draw(void)
{
extern qboolean scr_drawloading;

View file

@ -314,8 +314,10 @@ extern qboolean noclip_anglehack;
extern quakeparms_t host_parms;
extern cvar_t fs_gamename;
#ifdef PACKAGEMANAGER
extern cvar_t pkg_downloads_url;
extern cvar_t pkg_autoupdate;
#endif
extern cvar_t com_protocolname;
extern cvar_t com_protocolversion;
extern cvar_t com_nogamedirnativecode;

View file

@ -57,6 +57,8 @@ extern cvar_t r_stainfadeammount;
extern cvar_t r_lightmap_nearest;
extern cvar_t r_lightmap_format;
static void Surf_FreeLightmap(lightmapinfo_t *lm);
static int lightmap_shift;
int Surf_LightmapShift (model_t *model)
{
@ -2808,9 +2810,10 @@ void Surf_SetupFrame(void)
r_refdef.playerview->audio.entnum = r_refdef.playerview->viewentity;
VectorCopy(r_refdef.vieworg, r_refdef.playerview->audio.origin);
AngleVectors(r_refdef.viewangles, r_refdef.playerview->audio.forward,r_refdef.playerview->audio.right, r_refdef.playerview->audio.up);
if (r_viewcontents & FTECONTENTS_FLUID)
r_refdef.playerview->audio.reverbtype = 1;
else
// I'm fed up of openal users getting audio bugs when underwater.
// if (r_viewcontents & FTECONTENTS_FLUID)
// r_refdef.playerview->audio.reverbtype = 1;
// else
r_refdef.playerview->audio.reverbtype = 0;
VectorCopy(r_refdef.playerview->simvel, r_refdef.playerview->audio.velocity);
}
@ -2852,7 +2855,7 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent)
// calculate dynamic lighting for bmodel if it's not an
// instanced model
if (model->fromgame != fg_quake3 && model->fromgame != fg_doom3 && lightmap)
if (model->fromgame != fg_quake3 && model->fromgame != fg_doom3 && lightmap && !r_temporalscenecache.ival)
{
int k;
@ -2926,9 +2929,22 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent)
if (!b)
continue;
*b = *ob;
if (b->vbo && b->maxmeshes)
{
b->meshbuf = *b->mesh[0];
b->meshbuf.numindexes = b->mesh[b->maxmeshes-1]->indexes+b->mesh[b->maxmeshes-1]->numindexes-b->mesh[0]->indexes;
b->meshbuf.numvertexes = b->mesh[b->maxmeshes-1]->xyz_array+b->mesh[b->maxmeshes-1]->numvertexes-b->mesh[0]->xyz_array;
b->mesh = &b->meshptr;
b->meshptr = &b->meshbuf;
b->meshes = b->maxmeshes = 1;
}
else
{
// if (b->texture)
// b->shader = R_TextureAnimation(ent->framestate.g[FS_REG].frame[0], b->texture)->shader;
b->meshes = b->maxmeshes;
b->meshes = b->maxmeshes;
}
b->ent = ent;
b->flags = bef;
@ -2961,6 +2977,7 @@ struct webostate_s
{
char dbgid[12];
struct webostate_s *next;
int lastvalid; //keyed to cls.framecount, for cleaning up.
model_t *wmodel;
int cluster[2];
qboolean generating;
@ -2971,6 +2988,8 @@ struct webostate_s
int numbatches;
int lightstylevalues[MAX_NET_LIGHTSTYLES]; //when using workers that only reprocessing lighting at 10fps, things get too ugly when things go out of sync
//TODO qbyte *bakedsubmodels; //flags saying whether each submodel was baked or not. baked submodels need to be untinted uncaled unrotated at origin etc
vec3_t lastpos;
batch_t *rbatches[SHADER_SORT_COUNT];
@ -3019,6 +3038,8 @@ void R_GeneratedWorldEBO(void *ctx, void *data, size_t a_, size_t b_)
webogeneratingstate = 0;
mod = webostate->wmodel;
webostate->lastvalid = cls.framecount;
for (i = 0, idxcount = 0; i < webostate->numbatches; i++)
idxcount += webostate->batches[i].numidx;
#ifdef GLQUAKE
@ -3116,6 +3137,9 @@ static void Surf_SimpleWorld_Q1BSP(struct webostate_s *es, qbyte *pvs)
model_t *wmodel = es->wmodel;
int l = wmodel->numclusters;
int fc = -r_framecount;
int i;
// int s, f, lastface;
struct wesbatch_s *eb;
for (leaf = wmodel->leafs+l; l-- > 0; leaf--)
{
if ((pvs[l>>3] & (1u<<(l&7))) && leaf->nummarksurfaces)
@ -3127,8 +3151,6 @@ static void Surf_SimpleWorld_Q1BSP(struct webostate_s *es, qbyte *pvs)
surf = *mark++;
if (surf->visframe != fc)
{
int i;
struct wesbatch_s *eb;
surf->visframe = fc;
Surf_RenderDynamicLightmaps_Worker (wmodel, surf, es->lightstylevalues);
@ -3147,6 +3169,32 @@ static void Surf_SimpleWorld_Q1BSP(struct webostate_s *es, qbyte *pvs)
}
}
}
/*TODO for (s = 0; s < wmodel->numsubmodels; s++)
{
if (!es->bakedsubmodels[s])
continue; //not baking this one (not currently visible or something)
//FIXME: pvscull it here?
lastface = wmodel->submodels[s].firstface + wmodel->submodels[s].numfaces;
for (f = wmodel->submodels[s].firstface; f < lastface; f++)
{
surf = wmodel->surfaces;
Surf_RenderDynamicLightmaps_Worker (wmodel, surf, es->lightstylevalues);
mesh = surf->mesh;
eb = &es->batches[surf->sbatch->webobatch];
if (eb->maxidx < eb->numidx + mesh->numindexes)
{
//FIXME: pre-allocate
eb->maxidx = eb->numidx + surf->mesh->numindexes + 512;
eb->idxbuffer = BZ_Realloc(eb->idxbuffer, eb->maxidx * sizeof(index_t));
}
for (i = 0; i < mesh->numindexes; i++)
eb->idxbuffer[eb->numidx+i] = mesh->indexes[i] + mesh->vbofirstvert;
eb->numidx += mesh->numindexes;
}
}*/
}
#endif
#if defined(Q2BSPS) || defined(Q3BSPS)
@ -3272,14 +3320,22 @@ void Surf_DrawWorld (void)
Surf_LightmapShift(currentmodel);
#ifdef THREADEDWORLD
if ((r_dynamic.ival < 0 || currentmodel->numbatches) && !r_refdef.recurse && currentmodel->type == mod_brush)
if ((r_temporalscenecache.ival || currentmodel->numbatches) && !r_refdef.recurse && currentmodel->type == mod_brush)
{
struct webostate_s *webostate, *best = NULL;
struct webostate_s *webostate, *best = NULL, *kill;
vec_t bestdist = FLT_MAX;
for (webostate = webostates; webostate; webostate = webostate->next)
{
if (webostate->wmodel != currentmodel)
continue;
kill = webostate->next;
if (kill && kill->lastvalid < cls.framecount-5)
{
webostate->next = kill->next;
R_DestroyWorldEBO(kill);
}
if (webostate->cluster[0] == r_viewcluster && webostate->cluster[1] == r_viewcluster2)
{
best = webostate;
@ -3331,6 +3387,7 @@ void Surf_DrawWorld (void)
batch->ebobatch = currentmodel->numbatches;
currentmodel->numbatches++;
}
/*TODO submodels too*/
}
webogeneratingstate = true;
webogenerating = BZ_Malloc(sizeof(*webogenerating) + sizeof(webogenerating->batches[0]) * (currentmodel->numbatches-1) + currentmodel->pvsbytes);
@ -3387,6 +3444,10 @@ void Surf_DrawWorld (void)
{
entvis = surfvis = webostate->pvs.buffer;
webostate->lastvalid = cls.framecount;
r_dynamic.ival = -1; //don't waste time on dlighting models.
RSpeedEnd(RSPEED_WORLDNODE);
areas[0] = 1;
@ -3565,11 +3626,7 @@ void Surf_DeInit(void)
for (i = 0; i < numlightmaps; i++)
{
if (!lightmap[i])
continue;
if (!lightmap[i]->external)
Image_DestroyTexture(lightmap[i]->lightmap_texture);
BZ_Free(lightmap[i]);
Surf_FreeLightmap(lightmap[i]);
lightmap[i] = NULL;
}
@ -3738,6 +3795,25 @@ uploadfmt_t Surf_LightmapMode(model_t *model)
return fmt;
}
static void Surf_FreeLightmap(lightmapinfo_t *lm)
{
if (lm)
{
#ifdef GLQUAKE
if (lm->pbo_handle)
{
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lm->pbo_handle);
qglUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB);
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
qglDeleteBuffersARB(1, &lm->pbo_handle);
}
#endif
if (!lm->external)
Image_DestroyTexture(lm->lightmap_texture);
BZ_Free(lm);
}
}
//needs to be followed by a BE_UploadAllLightmaps at some point
int Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolean deluxe)
{
@ -3774,31 +3850,69 @@ int Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolea
i = numlightmaps + count;
lightmap = BZ_Realloc(lightmap, sizeof(*lightmap)*(i));
while(i > first)
while(i --> first)
{
i--;
#ifdef GLQUAKE
extern cvar_t gl_pbolightmaps;
//we might as well use a pbo for our staging memory.
if (qrenderer == QR_OPENGL && qglBufferStorage && qglMapBufferRange && gl_pbolightmaps.ival && Sys_IsMainThread())
{ //glBufferStorage and GL_MAP_PERSISTENT_BIT generally means gl4.4+
//pbos are 2.1
if (deluxe && ((i - numlightmaps)&1))
{
lightmap[i] = Z_Malloc(sizeof(*lightmap[i]));
lightmap[i]->width = width;
lightmap[i]->height = height;
lightmap[i]->lightmaps = NULL;
lightmap[i]->stainmaps = NULL;
lightmap[i]->hasdeluxe = false;
lightmap[i]->pixbytes = dpixbytes;
lightmap[i]->fmt = dfmt;
}
else
{
lightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(stmap)*3)*width*height);
lightmap[i]->width = width;
lightmap[i]->height = height;
lightmap[i]->lightmaps = NULL;
lightmap[i]->stainmaps = (qbyte*)(lightmap[i]+1);
lightmap[i]->hasdeluxe = deluxe;
lightmap[i]->pixbytes = pixbytes;
lightmap[i]->fmt = fmt;
}
if (deluxe && ((i - numlightmaps)&1))
{ //deluxemaps always use a specific format.
lightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(qbyte)*dpixbytes)*width*height);
lightmap[i]->width = width;
lightmap[i]->height = height;
lightmap[i]->lightmaps = (qbyte*)(lightmap[i]+1);
lightmap[i]->stainmaps = NULL;
lightmap[i]->hasdeluxe = false;
lightmap[i]->pixbytes = dpixbytes;
lightmap[i]->fmt = dfmt;
qglGenBuffersARB(1, &lightmap[i]->pbo_handle);
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lightmap[i]->pbo_handle);
//note: we only write the memory. the pbo would normally be in system memory anyway so there shouldn't be too much cost from coherent mappings.
qglBufferStorage(GL_PIXEL_UNPACK_BUFFER_ARB, lightmap[i]->pixbytes*width*height, NULL, GL_MAP_WRITE_BIT|GL_MAP_PERSISTENT_BIT|GL_MAP_COHERENT_BIT);
lightmap[i]->lightmaps = qglMapBufferRange(GL_PIXEL_UNPACK_BUFFER_ARB, 0, lightmap[i]->pixbytes*width*height, GL_MAP_WRITE_BIT|GL_MAP_PERSISTENT_BIT|GL_MAP_COHERENT_BIT);
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
}
else
#endif
{
lightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(qbyte)*pixbytes + sizeof(stmap)*3)*width*height);
lightmap[i]->width = width;
lightmap[i]->height = height;
lightmap[i]->lightmaps = (qbyte*)(lightmap[i]+1);
lightmap[i]->stainmaps = (stmap*)(lightmap[i]->lightmaps+pixbytes*width*height);
lightmap[i]->hasdeluxe = deluxe;
lightmap[i]->pixbytes = pixbytes;
lightmap[i]->fmt = fmt;
if (deluxe && ((i - numlightmaps)&1))
{ //deluxemaps always use a specific format.
lightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(qbyte)*dpixbytes)*width*height);
lightmap[i]->width = width;
lightmap[i]->height = height;
lightmap[i]->lightmaps = (qbyte*)(lightmap[i]+1);
lightmap[i]->stainmaps = NULL;
lightmap[i]->hasdeluxe = false;
lightmap[i]->pixbytes = dpixbytes;
lightmap[i]->fmt = dfmt;
}
else
{
lightmap[i] = Z_Malloc(sizeof(*lightmap[i]) + (sizeof(qbyte)*pixbytes + sizeof(stmap)*3)*width*height);
lightmap[i]->width = width;
lightmap[i]->height = height;
lightmap[i]->lightmaps = (qbyte*)(lightmap[i]+1);
lightmap[i]->stainmaps = (stmap*)(lightmap[i]->lightmaps+pixbytes*width*height);
lightmap[i]->hasdeluxe = deluxe;
lightmap[i]->pixbytes = pixbytes;
lightmap[i]->fmt = fmt;
}
}
lightmap[i]->rectchange.l = 0;
@ -4141,12 +4255,7 @@ void Surf_BuildLightmaps (void)
while(numlightmaps > 0)
{
numlightmaps--;
if (!lightmap[numlightmaps])
continue;
if (!lightmap[numlightmaps]->external)
Image_DestroyTexture(lightmap[numlightmaps]->lightmap_texture);
BZ_Free(lightmap[numlightmaps]);
Surf_FreeLightmap(lightmap[numlightmaps]);
lightmap[numlightmaps] = NULL;
}
@ -4199,7 +4308,7 @@ void Surf_NewMap (void)
if (cl.worldmodel)
COM_StripExtension(COM_SkipPath(cl.worldmodel->name), namebuf, sizeof(namebuf));
COM_FileBase(cl.worldmodel->name, namebuf, sizeof(namebuf));
else
*namebuf = '\0';
Cvar_Set(&host_mapname, namebuf);

View file

@ -378,6 +378,9 @@ typedef struct {
glRect_t rectchange;
qbyte *lightmaps; //[pixbytes*LMBLOCK_WIDTH*LMBLOCK_HEIGHT];
stmap *stainmaps; //[3*LMBLOCK_WIDTH*LMBLOCK_HEIGHT]; //rgb no a. added to lightmap for added (hopefully) speed.
#ifdef GLQUAKE
int pbo_handle; //when set, lightmaps is a persistently mapped write-only pbo for us to scribble data into, ready to be copied to the actual texture without waiting for glTexSubImage to complete.
#endif
} lightmapinfo_t;
extern lightmapinfo_t **lightmap;
extern int numlightmaps;
@ -387,6 +390,7 @@ void QDECL Surf_RebuildLightmap_Callback (struct cvar_s *var, char *oldvalue);
void R_SkyShutdown(void);
void R_SetSky(const char *skyname);
texid_t R_GetDefaultEnvmap(void);
#if defined(GLQUAKE)
void GLR_Init (void);
@ -653,6 +657,7 @@ extern cvar_t r_lavastyle;
extern cvar_t r_slimestyle;
extern cvar_t r_telestyle;
extern cvar_t r_dynamic;
extern cvar_t r_temporalscenecache;
extern cvar_t r_novis;
extern cvar_t r_netgraph;
extern cvar_t r_deluxemapping_cvar;
@ -672,6 +677,7 @@ extern cvar_t r_xflip;
extern cvar_t gl_mindist, gl_maxdist;
extern cvar_t r_clear;
extern cvar_t r_clearcolour;
extern cvar_t gl_poly;
extern cvar_t gl_affinemodels;
extern cvar_t r_renderscale;

View file

@ -27,6 +27,7 @@ int rquant[RQUANT_MAX];
void R_InitParticleTexture (void);
void R_RestartRenderer (rendererstate_t *newr);
static void R_UpdateRendererOpts(void);
qboolean vid_isfullscreen;
@ -112,7 +113,7 @@ cvar_t cl_cursorbiasx = CVAR ("cl_cursor_bias_x", "0.0");
cvar_t cl_cursorbiasy = CVAR ("cl_cursor_bias_y", "0.0");
#ifdef QWSKINS
cvar_t gl_nocolors = CVARF ("gl_nocolors", "0", CVAR_ARCHIVE);
cvar_t gl_nocolors = CVARFD ("gl_nocolors", "0", CVAR_ARCHIVE, "Ignores player colours and skins, reducing texture memory usage at the cost of not knowing whether you're killing your team mates.");
#endif
cvar_t gl_part_flame = CVARFD ("gl_part_flame", "1", CVAR_ARCHIVE, "Enable particle emitting from models. Mainly used for torch and flame effects.");
@ -141,7 +142,7 @@ cvar_t r_bloodstains = CVARF ("r_bloodstains", "1", CVAR_ARCHIVE);
cvar_t r_bouncysparks = CVARFD ("r_bouncysparks", "1",
CVAR_ARCHIVE,
"Enables particle interaction with world surfaces, allowing for bouncy particles, stains, and decals.");
cvar_t r_drawentities = CVAR ("r_drawentities", "1");
cvar_t r_drawentities = CVARFD ("r_drawentities", "1", CVAR_CHEAT, "Controls whether to draw entities or not.\n0: Draw no entities.\n1: Draw everything as normal.\n2: Draw everything but bmodels.\n3: Draw bmodels only.");
cvar_t r_max_gpu_bones = CVARD ("r_max_gpu_bones", "", "Specifies the maximum number of bones that can be handled on the GPU. If empty, will guess.");
cvar_t r_drawflat = CVARAF ("r_drawflat", "0", "gl_textureless",
CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM);
@ -157,7 +158,8 @@ cvar_t r_refractreflect_scale = CVARD ("r_refractreflect_scale", "0.5", "Use
cvar_t r_drawviewmodel = CVARF ("r_drawviewmodel", "1", CVAR_ARCHIVE);
cvar_t r_drawviewmodelinvis = CVAR ("r_drawviewmodelinvis", "0");
cvar_t r_dynamic = CVARFD ("r_dynamic", IFMINIMAL("0","1"),
CVAR_ARCHIVE, "-1: the engine will use only pvs to determine which surfaces are visible. This can significantly reduce CPU time, but only if there are many surfaces with few textures visible from the camera.\n0: no standard dlights at all.\n1: coloured dlights will be used, they may show through walls. These are not realtime things.\n2: The dlights will be forced to monochrome (this does not affect coronas/flashblends/rtlights attached to the same light).");
CVAR_ARCHIVE, "0: no standard dlights at all.\n1: coloured dlights will be used, they may show through walls. These are not realtime things.\n2: The dlights will be forced to monochrome (this does not affect coronas/flashblends/rtlights attached to the same light).");
cvar_t r_temporalscenecache = CVARFD ("r_temporalscenecache", "0", CVAR_ARCHIVE, "Controls whether to generate+reuse a scene cache over multiple frames. This is generated on a separate thread to avoid any associated costs. This can significantly boost framerates on complex maps, but can also stress the gpu more (performance tradeoff that varies per map). An outdated cache may be used if the cache takes too long to build (eg: lightmap animations), which could cause the odd glitch when moving fast (but retain more consistent framerates - another tradeoff).\n0: Tranditional quake rendering.\n1: Generate+Use the scene cache.");
cvar_t r_fastturb = CVARF ("r_fastturb", "0",
CVAR_SHADERSYSTEM);
cvar_t r_fastsky = CVARF ("r_fastsky", "0",
@ -296,7 +298,7 @@ cvar_t vid_conwidth = CVARF ("vid_conwidth", "0",
//see R_RestartRenderer_f for the effective default 'if (newr.renderer == -1)'.
cvar_t vid_renderer = CVARFD ("vid_renderer", "",
CVAR_ARCHIVE | CVAR_VIDEOLATCH, "Specifies which backend is used. Values that might work are: sv (dedicated server), headless (null renderer), vk (vulkan), gl (opengl), egl (opengl es), d3d9 (direct3d 9), d3d11 (direct3d 11, with default hardware rendering), d3d11 warp (direct3d 11, with software rendering).");
cvar_t vid_renderer_opts = CVARFD ("_vid_renderer_opts", "", CVAR_NOSET, "The possible video renderer apis, in \"value\" \"description\" pairs, for gamecode to read.");
cvar_t vid_renderer_opts = CVARFD ("_vid_renderer_opts", NULL, CVAR_NOSET, "The possible video renderer apis, in \"value\" \"description\" pairs, for gamecode to read.");
cvar_t vid_bpp = CVARFD ("vid_bpp", "0",
CVAR_ARCHIVE | CVAR_VIDEOLATCH, "The number of colour bits to request from the renedering context");
@ -369,8 +371,9 @@ cvar_t vid_gl_context_robustness = CVARD ("vid_gl_context_robustness", "1", "A
cvar_t vid_gl_context_selfreset = CVARD ("vid_gl_context_selfreset", "1", "Upon hardware failure, have the engine create a new context instead of depending on the drivers to restore everything. This can help to avoid graphics drivers randomly killing your game, and can help reduce memory requirements.");
cvar_t vid_gl_context_noerror = CVARD ("vid_gl_context_noerror", "", "Disables OpenGL's error checks for a small performance speedup. May cause segfaults if stuff wasn't properly implemented/tested.");
cvar_t gl_immutable_textures = CVARD ("gl_immutable_textures", "1", "Controls whether to use immutable GPU memory allocations for OpenGL textures. This potentially means less work for the drivers and thus higher framerates.");
cvar_t gl_immutable_buffers = CVARD ("gl_immutable_buffers", "1", "Controls whether to use immutable GPU memory allocations for static OpenGL vertex buffers. This potentially means less work for the drivers and thus higher framerates.");
cvar_t gl_immutable_textures = CVARFD ("gl_immutable_textures", "1", CVAR_VIDEOLATCH, "Controls whether to use immutable GPU memory allocations for OpenGL textures. This potentially means less work for the drivers and thus higher framerates.");
cvar_t gl_immutable_buffers = CVARFD ("gl_immutable_buffers", "1", CVAR_VIDEOLATCH, "Controls whether to use immutable GPU memory allocations for static OpenGL vertex buffers. This potentially means less work for the drivers and thus higher framerates.");
cvar_t gl_pbolightmaps = CVARFD ("gl_pbolightmaps", "1", CVAR_RENDERERLATCH, "Controls whether to use PBOs for streaming lightmap updates. This prevents CPU stalls while the driver reads out the lightmap data (lightmap updates are still not free though).");
#endif
#if 1
@ -409,6 +412,7 @@ cvar_t gl_load24bit = CVARF ("gl_load24bit", "1",
cvar_t r_clear = CVARAF("r_clear","0",
"gl_clear", 0);
cvar_t r_clearcolour = CVARAF("r_clearcolour", "0.12 0.12 0.12", "r_clearcolor"/*american spelling*/, 0);
cvar_t gl_max_size = CVARFD ("gl_max_size", "8192", CVAR_RENDERERLATCH, "Specifies the maximum texture size that the engine may use. Textures larger than this will be downsized. Clamped by the value the driver supports.");
cvar_t gl_menutint_shader = CVARD ("gl_menutint_shader", "1", "Controls the use of GLSL to desaturate the background when drawing the menu, like quake's dos software renderer used to do before the ugly dithering of winquake.");
@ -506,7 +510,7 @@ cvar_t vid_hardwaregamma = CVARFD ("vid_hardwaregamma", "1",
cvar_t vid_desktopgamma = CVARFD ("vid_desktopgamma", "0",
CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Apply gamma ramps upon the desktop rather than the window.");
cvar_t r_fog_cullentities = CVARD ("r_fog_cullentities", "1", "Automatically cull entities according to fog.");
cvar_t r_fog_cullentities = CVARD ("r_fog_cullentities", "1", "0: Never cull entities by fog...\n1: Automatically cull entities according to fog.\n2: Force fog culling regardless ");
cvar_t r_fog_exp2 = CVARD ("r_fog_exp2", "1", "Expresses how fog fades with distance. 0 (matching DarkPlaces's default) is typically more realistic, while 1 (matching FitzQuake and others) is more common.");
cvar_t r_fog_permutation = CVARFD ("r_fog_permutation", "1", CVAR_SHADERSYSTEM, "Renders fog using a material permutation. 0 plays nicer with q3 shaders, but 1 is otherwise a better choice.");
@ -545,7 +549,7 @@ void GLRenderer_Init(void)
Cvar_Register (&gl_immutable_textures, GLRENDEREROPTIONS);
Cvar_Register (&gl_immutable_buffers, GLRENDEREROPTIONS);
Cvar_Register (&gl_pbolightmaps, GLRENDEREROPTIONS);
//renderer
Cvar_Register (&gl_affinemodels, GLRENDEREROPTIONS);
@ -806,26 +810,9 @@ void Renderer_Init(void)
#endif
Cvar_Register (&in_windowed_mouse, VIDCOMMANDGROUP);
Cvar_Register (&vid_renderer, VIDCOMMANDGROUP);
vid_renderer_opts.enginevalue =
#ifdef GLQUAKE
"gl \"OpenGL\" "
#endif
#ifdef VKQUAKE
"vk \"Vulkan\" "
#endif
#ifdef D3D8QUAKE
// "d3d8 \"Direct3D 8\" "
#endif
#ifdef D3D9QUAKE
"d3d9 \"Direct3D 9\" "
#endif
#ifdef D3D11QUAKE
"d3d11 \"Direct3D 11\" "
#endif
#ifdef SWQUAKE
"sw \"Software Rendering\" "
#endif
"";
R_UpdateRendererOpts();
Cvar_Register (&vid_renderer_opts, VIDCOMMANDGROUP);
Cvar_Register (&vid_fullscreen, VIDCOMMANDGROUP);
@ -986,6 +973,7 @@ void Renderer_Init(void)
Cvar_Register (&r_speeds, SCREENOPTIONS);
Cvar_Register (&r_netgraph, SCREENOPTIONS);
Cvar_Register (&r_temporalscenecache, GRAPHICALNICETIES);
Cvar_Register (&r_dynamic, GRAPHICALNICETIES);
Cvar_Register (&r_lightmap_saturation, GRAPHICALNICETIES);
@ -1006,6 +994,7 @@ void Renderer_Init(void)
Cvar_Register (&gl_blendsprites, GLRENDEREROPTIONS);
Cvar_Register (&r_clear, GLRENDEREROPTIONS);
Cvar_Register (&r_clearcolour, GLRENDEREROPTIONS);
Cvar_Register (&gl_max_size, GLRENDEREROPTIONS);
Cvar_Register (&gl_maxdist, GLRENDEREROPTIONS);
Cvar_Register (&gl_texturemode, GLRENDEREROPTIONS);
@ -1871,6 +1860,9 @@ TRACE(("dbg: R_ApplyRenderer: efrags\n"));
Shader_DoReload();
CSQC_RendererRestarted();
#endif
#ifdef MENU_DAT
MP_RendererRestarted();
#endif
if (newr && qrenderer != QR_NONE)
{
@ -2335,12 +2327,35 @@ void R_SetRenderer_f (void)
R_RestartRenderer(&newr);
}
static void R_UpdateRendererOpts(void)
{
char *v = NULL;
size_t i;
struct sortedrenderers_s sorted[countof(rendererinfo)];
for (i = 0; i < countof(sorted); i++)
{
sorted[i].index = i;
sorted[i].r = rendererinfo[i];
sorted[i].pri = R_PriorityForRenderer(sorted[i].r);
}
qsort(sorted, countof(sorted), sizeof(sorted[0]), R_SortRenderers);
v = NULL;
for (i = 0; i < countof(rendererinfo); i++)
{
rendererinfo_t *r = sorted[i].r;
if (r && r->description)
{
if (r->rtype == QR_HEADLESS || r->rtype == QR_NONE)
continue; //skip these, they're kinda dangerous.
Z_StrCat(&v, va("%s \"%s\" ", r->name[0], r->description));
}
}
Z_Free(vid_renderer_opts.enginevalue);
vid_renderer_opts.enginevalue = v;
}
@ -2552,6 +2567,7 @@ texture_t *R_TextureAnimation_Q2 (texture_t *base)
unsigned int r_viewcontents;
//mleaf_t *r_viewleaf, *r_oldviewleaf;
//mleaf_t *r_viewleaf2, *r_oldviewleaf2;
int r_viewarea;
int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2;
int r_visframecount;
mleaf_t *r_vischain; // linked list of visible leafs
@ -3057,7 +3073,7 @@ void R_SetFrustum (float projmat[16], float viewmat[16])
//do far plane
//fog will logically not actually reach 0, though precision issues will force it. we cut off at an exponant of -500
if (r_refdef.globalfog.density && r_refdef.globalfog.alpha>=1 && (r_fog_cullentities.ival&&r_skyfog.value>=1) && !r_refdef.globalfog.depthbias)
if (r_refdef.globalfog.density && r_refdef.globalfog.alpha>=1 && (r_fog_cullentities.ival==2||(r_fog_cullentities.ival&&r_skyfog.value>=1)) && !r_refdef.globalfog.depthbias)
{
float culldist;
float fog;

View file

@ -208,7 +208,7 @@ void Draw_AltFunString(float x, float y, const void *str)
}
//Draws a marked up string no wider than $width virtual pixels.
void Draw_FunStringWidth(float x, float y, const void *str, int width, int rightalign, qboolean highlight)
void Draw_FunStringWidthFont(struct font_s *font, float x, float y, const void *str, int width, int rightalign, qboolean highlight)
{
conchar_t buffer[2048];
conchar_t *w;
@ -224,7 +224,7 @@ void Draw_FunStringWidth(float x, float y, const void *str, int width, int right
codeflags |= CON_BLINKTEXT;
COM_ParseFunString(codeflags, str, buffer, sizeof(buffer), false);
Font_BeginString(font_default, x, y, &px, &py);
Font_BeginString(font, x, y, &px, &py);
if (rightalign)
{
for (w = buffer; *w; )
@ -258,7 +258,7 @@ void Draw_FunStringWidth(float x, float y, const void *str, int width, int right
return;
px = Font_DrawChar(px, py, codeflags, codepoint);
}
Font_EndString(font_default);
Font_EndString(font);
}
#ifdef QUAKEHUD

View file

@ -80,6 +80,7 @@ void SCR_ShowPic_Remove_f(void);
//a header is better than none...
void Draw_TextBox (int x, int y, int width, int lines);
void Draw_ApproxTextBox (float x, float y, float width, float height);
enum fs_relative;

View file

@ -33,11 +33,16 @@ We also have no doppler with WebAudio.
//emscripten provides an openal -> webaudio wrapper. its not the best, but does get the job done.
#define OPENAL_STATIC //our javascript port doesn't support dynamic linking (bss+data segments get too messy).
#define SDRVNAME "WebAudio" //IE doesn't support webaudio, resulting in noticable error messages about no openal, which is technically incorrect. So lets be clear about this.
qboolean firefoxstaticsounds; //FireFox bugs out with static sounds. they all end up full volume AND THIS IS REALLY LOUD AND REALLY ANNOYING.
#define SDRVNAMEDESC "WebAudio:"
#else
#define SDRVNAME "OpenAL"
#define SDRVNAMEDESC "OAL:"
#define USEEFX
#endif
#ifndef HAVE_MIXER
#undef SDRVNAMEDESC
#define SDRVNAMEDESC "" //remove the prefixes in user-visible desciptions when there's (probably) no other devices anyway
#endif
#ifdef OPENAL_STATIC
#include <AL/al.h> //output
@ -1532,11 +1537,7 @@ static qboolean QDECL OpenAL_Enumerate(void (QDECL *callback)(const char *driver
devnames = palcGetString(NULL, ALC_DEVICE_SPECIFIER);
while(*devnames)
{
#ifdef FTE_TARGET_WEB
callback(SDRVNAME, devnames, va("WebAudio:%s", devnames));
#else
callback(SDRVNAME, devnames, va("OAL:%s", devnames));
#endif
callback(SDRVNAME, devnames, va(SDRVNAMEDESC"%s", devnames));
devnames += strlen(devnames)+1;
}
return true;
@ -1586,7 +1587,7 @@ static qboolean QDECL OPENAL_Capture_Enumerate (void (QDECL *callback) (const ch
devnames = palcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
while(*devnames)
{
callback(SDRVNAME, devnames, va("OAL:%s", devnames));
callback(SDRVNAME, devnames, va(SDRVNAMEDESC"%s", devnames));
devnames += strlen(devnames)+1;
}
return true;

View file

@ -540,7 +540,7 @@ static qboolean QDECL ALSA_Enumerate(void (QDECL *cb) (const char *drivername, c
{
char *d = psnd_device_name_get_hint(hints[i], "DESC");
if (d)
cb(SDRVNAME, n, va("ALSA (%s)", d));
cb(SDRVNAME, n, va("ALSA:%s", d));
else
cb(SDRVNAME, n, n);
free(d);

View file

@ -746,11 +746,11 @@ static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int
return false;
}
if (info.format == 1 && info.width == 1)
if (info.format == 1 && info.width == 1) //unsigned bytes
COM_CharBias(data + info.dataofs, info.samples*info.numchannels);
else if (info.format == 1 && info.width == 2)
else if (info.format == 1 && info.width == 2) //signed shorts
COM_SwapLittleShortBlock((short *)(data + info.dataofs), info.samples*info.numchannels);
else if (info.format == 3 && info.width == 4)
else if (info.format == 3 && info.width == 4) //signed floats
{
S_ShortedLittleFloats(data + info.dataofs, info.samples*info.numchannels);
info.width = 2;
@ -760,11 +760,12 @@ static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int
s->loadstate = SLS_FAILED;
switch(info.format)
{
case 1:
case 3: Con_Printf ("%s has an unsupported width (%i bits).\n", s->name, info.width*8); break;
case 6: Con_Printf ("%s uses unsupported a-law format.\n", s->name); break;
case 7: Con_Printf ("%s uses unsupported mu-law format.\n", s->name); break;
default: Con_Printf ("%s has an unsupported format.\n", s->name); break;
case 1/*WAVE_FORMAT_PCM*/:
case 3/*WAVE_FORMAT_IEEE_FLOAT*/: Con_Printf ("%s has an unsupported width (%i bits).\n", s->name, info.width*8); break;
case 6/*WAVE_FORMAT_ALAW*/: Con_Printf ("%s uses unsupported a-law format.\n", s->name); break;
case 7/*WAVE_FORMAT_MULAW*/: Con_Printf ("%s uses unsupported mu-law format.\n", s->name); break;
case 0xfffe/*WAVE_FORMAT_EXTENSIBLE*/:
default: Con_Printf ("%s has an unsupported format (%#x).\n", s->name, info.format); break;
}
return false;
}

View file

@ -170,6 +170,17 @@ void Sys_Printf (char *fmt, ...)
unsigned int codeflags, codepoint;
FILE *out = stdout;
#ifdef SUBSERVERS
if (SSV_IsSubServer())
{
va_start (argptr,fmt);
vsnprintf (text,sizeof(text)-1, fmt,argptr);
va_end (argptr);
SSV_PrintToMaster(text);
return;
}
#endif
if (nostdout)
{
#ifdef _DEBUG
@ -1269,7 +1280,7 @@ void Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, cha
callback(ctx, clipboard_buffer);
}
void Sys_SaveClipboard(clipboardtype_t cbt, char *text) {
void Sys_SaveClipboard(clipboardtype_t cbt, const char *text) {
Q_strncpyz(clipboard_buffer, text, SYS_CLIPBOARD_SIZE);
}
#endif

View file

@ -832,16 +832,16 @@ qboolean Con_Editor_Key(console_t *con, unsigned int unicode, int key)
Q_snprintfz(con->title, sizeof(con->title), "MODIFIED: %s", con->name);
return true;
}
void Con_Editor_CloseCallback(void *ctx, int op)
void Con_Editor_CloseCallback(void *ctx, promptbutton_t op)
{
console_t *con = ctx;
if (con != con_curwindow) //ensure that it still exists (lame only-active-window check)
return;
if (op == 0)
if (op == PROMPT_YES)
Con_Editor_Save(con);
if (op != -1) //-1 == cancel
if (op != PROMPT_CANCEL)
Con_Destroy(con);
}
qboolean Con_Editor_Close(console_t *con, qboolean force)

View file

@ -45,8 +45,8 @@ typedef qboolean qbool;
#define strlcpy Q_strncpyz
#define strlcat Q_strncatz
#define Q_stricmp stricmp
#define Q_strnicmp strnicmp
#define Q_stricmp strcasecmp
#define Q_strnicmp strncasecmp
extern int cl_spikeindex, cl_playerindex, cl_h_playerindex, cl_flagindex, cl_rocketindex, cl_grenadeindex, cl_gib1index, cl_gib2index, cl_gib3index;
@ -82,20 +82,8 @@ static void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue);
#define TP_SKIN_CVARS
#endif
//a list of all the cvars
//this is down to the fact that I keep defining them but forgetting to register. :/
#define TP_CVARS \
TP_SKIN_CVARS \
TP_CVAR(cl_fakename, ""); \
TP_CVAR(cl_parseSay, "1"); \
TP_CVAR(cl_parseFunChars, "1"); \
TP_CVAR(cl_triggers, "1"); \
TP_CVAR(tp_autostatus, ""); /* things which will not always change, but are useful */ \
TP_CVAR(tp_forceTriggers, "0"); \
TP_CVAR(tp_loadlocs, "1"); \
TP_CVAR(tp_soundtrigger, "~"); \
\
#ifdef QUAKESTATS
#define TP_NAME_CVARS \
TP_CVAR(tp_name_none, ""); \
TP_CVAR(tp_name_axe, "axe"); \
TP_CVAR(tp_name_sg, "sg"); \
@ -121,7 +109,6 @@ static void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue);
TP_CVAR(tp_name_backpack, "pack"); \
TP_CVAR(tp_name_flag, "flag"); \
TP_CVAR(tp_name_nothing, "nothing"); \
TP_CVAR(tp_name_someplace, "someplace"); \
TP_CVAR(tp_name_at, "at"); \
TP_CVAR(tp_need_ra, "50"); \
TP_CVAR(tp_need_ya, "50"); \
@ -173,7 +160,26 @@ static void QDECL TP_EnemyColor_CB (struct cvar_s *var, char *oldvalue);
TP_CVAR(loc_name_quad, "quad"); \
TP_CVAR(loc_name_pent, "pent"); \
TP_CVAR(loc_name_ring, "ring"); \
TP_CVAR(loc_name_suit, "suit")
TP_CVAR(loc_name_suit, "suit");
#else
#define TP_NAME_CVARS
#endif
//a list of all the cvars
//this is down to the fact that I keep defining them but forgetting to register. :/
#define TP_CVARS \
TP_SKIN_CVARS \
TP_NAME_CVARS \
TP_CVAR(cl_fakename, ""); \
TP_CVAR(cl_parseSay, "1"); \
TP_CVAR(cl_parseFunChars, "1"); \
TP_CVAR(cl_triggers, "1"); \
TP_CVAR(tp_autostatus, ""); /* things which will not always change, but are useful */ \
TP_CVAR(tp_forceTriggers, "0"); \
TP_CVAR(tp_loadlocs, "1"); \
TP_CVAR(tp_soundtrigger, "~"); \
\
TP_CVAR(tp_name_someplace, "someplace")
//create the globals for all the TP cvars.
#define TP_CVAR(name,def) cvar_t name = CVAR(#name, def)
@ -186,13 +192,12 @@ TP_CVARS;
extern cvar_t host_mapname;
void TP_UpdateAutoStatus(void);
#define MAX_LOC_NAME 48
#ifdef QUAKESTATS
static void TP_FindModelNumbers (void);
static void TP_FindPoint (void);
#define MAX_LOC_NAME 48
// this structure is cleared after entering a new map
typedef struct tvars_s {
char autoteamstatus[256];
@ -229,6 +234,8 @@ typedef struct tvars_s {
float lastdrop_time;
char lasttrigger_match[256];
enum {
POINT_TYPE_ENEMY,
POINT_TYPE_TEAMMATE,
@ -238,7 +245,8 @@ typedef struct tvars_s {
float pointtime;
} tvars_t;
tvars_t vars;
static tvars_t vars;
#endif
typedef struct item_vis_s {
@ -298,6 +306,120 @@ void TP_ExecTrigger (char *s, qboolean indemos)
}
}
/*
==========================================================================
HELPER FUNCTIONS
==========================================================================
*/
static int TP_CountPlayers (void)
{
int i, count;
count = 0;
for (i = 0; i < cl.allocated_client_slots ; i++) {
if (cl.players[i].name[0] && !cl.players[i].spectator)
count++;
}
return count;
}
static char *TP_PlayerTeam (void)
{
return cl.players[cl.playerview[SP].playernum].team;
}
static char *TP_EnemyTeam (void)
{
int i;
static char enemyteam[MAX_INFO_KEY];
char *myteam = TP_PlayerTeam();
for (i = 0; i < cl.allocated_client_slots ; i++) {
if (cl.players[i].name[0] && !cl.players[i].spectator)
{
strcpy (enemyteam, cl.players[i].team);
if (strcmp(myteam, cl.players[i].team) != 0)
return enemyteam;
}
}
return "";
}
static char *TP_PlayerName (void)
{
return cl.players[cl.playerview[SP].playernum].name;
}
static char *TP_EnemyName (void)
{
int i;
char *myname;
static char enemyname[MAX_SCOREBOARDNAME];
myname = TP_PlayerName ();
for (i = 0; i < cl.allocated_client_slots ; i++) {
if (cl.players[i].name[0] && !cl.players[i].spectator)
{
strcpy (enemyname, cl.players[i].name);
if (!strcmp(enemyname, myname))
return enemyname;
}
}
return "";
}
static char *TP_MapName (void)
{
return host_mapname.string;
}
char *TP_GenerateDemoName(void)
{
if (cl.playerview[SP].spectator)
{ // FIXME: if tracking a player, use his name
return va ("spec_%s_%s",
TP_PlayerName(),
TP_MapName());
}
else
{ // guess game type and write demo name
int i = TP_CountPlayers();
if (cl.teamplay && i >= 3)
{ // Teamplay
return va ("%s_%s_vs_%s_%s",
TP_PlayerName(),
TP_PlayerTeam(),
TP_EnemyTeam(),
TP_MapName());
}
else
{
if (i == 2)
{ // Duel
return va ("%s_vs_%s_%s",
TP_PlayerName(),
TP_EnemyName(),
TP_MapName());
}
else if (i > 2)
{ // FFA
return va ("%s_ffa_%s",
TP_PlayerName(),
TP_MapName());
}
else
{ // one player
return va ("%s_%s",
TP_PlayerName(),
TP_MapName());
}
}
}
}
/*
==========================================================================
@ -308,7 +430,7 @@ void TP_ExecTrigger (char *s, qboolean indemos)
#define MAX_MACRO_VALUE 256
static char macro_buf[MAX_MACRO_VALUE] = "";
#ifndef QUAKETC
#ifdef QUAKESTATS
// buffer-size-safe helper functions
//static void MacroBuf_strcat (char *str) {
// strlcat (macro_buf, str, sizeof(macro_buf));
@ -557,6 +679,7 @@ static char *Macro_Location (void)
return TP_LocationName (cl.playerview[SP].simorg);
}
#ifdef QUAKESTATS
static char *Macro_LastDeath (void)
{
if (vars.deathtrigger_time)
@ -637,7 +760,7 @@ static char *Macro_PointNameAtLocation (void)
return vars.pointname;
}
#ifdef QUAKESTATS
static char *Macro_Need (void)
{
int i, weapon;
@ -713,7 +836,6 @@ done:
return macro_buf;
}
#endif
static char *Skin_To_TFSkin (char *myskin)
{
@ -853,7 +975,6 @@ static char *Macro_Point_LED(void)
return macro_buf;
}
#ifdef QUAKESTATS
static char *Macro_MyStatus_LED(void)
{
int count;
@ -886,7 +1007,6 @@ static char *Macro_MyStatus_LED(void)
return macro_buf;
}
#endif
static void CountNearbyPlayers(qboolean dead)
{
@ -1042,7 +1162,7 @@ static char *Macro_LastSeenPowerup(void)
return macro_buf;
}
char *Macro_LastDrop (void)
static char *Macro_LastDrop (void)
{
if (vars.lastdrop_time)
return vars.lastdroploc;
@ -1050,7 +1170,7 @@ char *Macro_LastDrop (void)
return tp_name_someplace.string;
}
char *Macro_LastDropTime (void)
static char *Macro_LastDropTime (void)
{
if (vars.lastdrop_time)
Q_snprintfz (macro_buf, 32, "%d", (int) (realtime - vars.lastdrop_time));
@ -1059,8 +1179,7 @@ char *Macro_LastDropTime (void)
return macro_buf;
}
#ifdef QUAKESTATS
char *Macro_CombinedHealth(void)
static char *Macro_CombinedHealth(void)
{
float h;
float t, a, m;
@ -1091,7 +1210,7 @@ char *Macro_CombinedHealth(void)
return macro_buf;
}
char *Macro_Coloured_Armour(void)
static char *Macro_Coloured_Armour(void)
{
if (cl.playerview[SP].stats[STAT_ITEMS] & IT_ARMOR3)
return "{^s^xe00%%a^r}";
@ -1103,7 +1222,7 @@ char *Macro_Coloured_Armour(void)
return "{0}";
}
char *Macro_Coloured_Powerups(void)
static char *Macro_Coloured_Powerups(void)
{
char *quad, *pent, *ring;
quad = (cl.playerview[SP].stats[STAT_ITEMS] & IT_QUAD) ?va("^x03f%s", tp_name_quad.string):"";
@ -1118,7 +1237,7 @@ char *Macro_Coloured_Powerups(void)
else
return "";
}
char *Macro_Coloured_Short_Powerups(void)
static char *Macro_Coloured_Short_Powerups(void)
{
char *quad, *pent, *ring;
quad = (cl.playerview[SP].stats[STAT_ITEMS] & IT_QUAD) ?"^x03fq":"";
@ -1133,21 +1252,36 @@ char *Macro_Coloured_Short_Powerups(void)
else
return "";
}
char *Macro_LastIP(void)
static char *Macro_Match_Status(void)
{
if (cls.state == ca_disconnected)
return "disconnected";
if (cls.state < ca_active)
return "connecting";
switch(cl.matchstate)
{
case MATCH_DONTKNOW:
case MATCH_INPROGRESS:
default:
return "normal";
case MATCH_COUNTDOWN:
return "countdown";
case MATCH_STANDBY:
return "standby";
}
}
/*static char *Macro_LastIP(void)
{ //report the last ip that someone said in chat.
return "---";
}
char *Macro_MP3Info(void)
{
static char *Macro_MP3Info(void)
{ //for people trying to be cool but really just annoying everyone
return "---";
}
char *Macro_Match_Status(void)
{
return "---";
}
char *Macro_LastTrigger_Match(void)
{
return "---";
*/
static char *Macro_LastTrigger_Match(void)
{ //returns the last line that triggered a msg_trigger
return vars.lasttrigger_match;
}
#endif
@ -1179,6 +1313,7 @@ $triggermatch is the last chat message that exec'd a msg_trigger.
static void TP_InitMacros(void)
{
Cmd_AddMacro("latency", Macro_Latency, false);
Cmd_AddMacro("location", Macro_Location, false);
#ifdef QUAKESTATS
Cmd_AddMacro("health", Macro_Health, true);
Cmd_AddMacro("armortype", Macro_ArmorType, true);
@ -1195,13 +1330,12 @@ static void TP_InitMacros(void)
Cmd_AddMacro("bestammo", Macro_BestAmmo, true);
Cmd_AddMacro("powerups", Macro_Powerups, true);
Cmd_AddMacro("droppedweapon", Macro_DroppedWeapon, true);
#endif
Cmd_AddMacro("location", Macro_Location, false);
Cmd_AddMacro("tf_skin", Macro_TF_Skin, true);
Cmd_AddMacro("deathloc", Macro_LastDeath, true);
Cmd_AddMacro("tookatloc", Macro_TookAtLoc, true);
Cmd_AddMacro("tookloc", Macro_TookLoc, true);
Cmd_AddMacro("took", Macro_Took, true);
Cmd_AddMacro("tf_skin", Macro_TF_Skin, true);
//ones added by Spike, for fuhquake compatability
Cmd_AddMacro("connectiontype", Macro_ConnectionType, false);
@ -1211,16 +1345,16 @@ static void TP_InitMacros(void)
Cmd_AddMacro("pointloc", Macro_PointLocation, true);
Cmd_AddMacro("matchname", Macro_Match_Name, false);
Cmd_AddMacro("matchtype", Macro_Match_Type, false);
#ifdef QUAKESTATS
Cmd_AddMacro("need", Macro_Need, true);
Cmd_AddMacro("ledstatus", Macro_MyStatus_LED, true);
#endif
Cmd_AddMacro("ledpoint", Macro_Point_LED, true);
Cmd_AddMacro("droploc", Macro_LastDrop, true);
Cmd_AddMacro("droptime", Macro_LastDropTime, true);
// Cmd_AddMacro("matchstatus", Macro_Match_Status, false);
Cmd_AddMacro("matchstatus", Macro_Match_Status, false);
Cmd_AddMacro("triggermatch", Macro_LastTrigger_Match, false);
#endif
// Cmd_AddMacro("mp3info", Macro_MP3Info, false);
// Cmd_AddMacro("triggermatch", Macro_LastTrigger_Match, false);
//new, fte only (at least when first implemented)
#ifdef QUAKESTATS
@ -1234,10 +1368,10 @@ static void TP_InitMacros(void)
Cmd_AddMacro("colored_armor", Macro_Coloured_Armour, true); //*shudder*
Cmd_AddMacro("colored_powerups", Macro_Coloured_Powerups, true);
Cmd_AddMacro("colored_short_powerups", Macro_Coloured_Short_Powerups, true);
#endif
Cmd_AddMacro("gamedir", Macro_Gamedir, false);
Cmd_AddMacro("lastloc", Macro_Last_Location, true);
Cmd_AddMacro("lastpowerup", Macro_LastSeenPowerup, true);
#endif
Cmd_AddMacro("gamedir", Macro_Gamedir, false);
}
#define MAX_MACRO_STRING 1024
@ -1321,17 +1455,16 @@ static char *TP_ParseMacroString (char *s)
case 'A': macro_string = Macro_ArmorType(); break;
case 'b': macro_string = Macro_BestWeaponAndAmmo(); break;
case 'c': macro_string = Macro_Cells(); break;
#endif
case 'd': macro_string = Macro_LastDeath(); break;
// case 'D':
#ifdef QUAKESTATS
case 'h': macro_string = Macro_Health(); break;
#endif
case 'i': macro_string = Macro_TookAtLoc(); break;
case 'j': macro_string = Macro_LastPointAtLoc(); break;
case 'k': macro_string = Macro_LastTookOrPointed(); break;
case 'l': macro_string = Macro_Location(); break;
case 'L': macro_string = Macro_Last_Location(); break;
#endif
case 'l': macro_string = Macro_Location(); break;
#ifdef QUAKESTATS
case 'm': macro_string = Macro_LastTookOrPointed(); break;
case 'o': macro_string = Macro_CountNearbyFriendlyPlayers(); break;
@ -1340,22 +1473,19 @@ static char *TP_ParseMacroString (char *s)
case 'E': macro_string = Macro_Count_Last_NearbyEnemyPlayers(); break;
case 'P':
#ifdef QUAKESTATS
case 'p': macro_string = Macro_Powerups(); break;
#endif
case 'q': macro_string = Macro_LastSeenPowerup(); break;
// case 'r': macro_string = Macro_LastReportedLoc(); break;
case 's': macro_string = Macro_EnemyStatus_LED(); break;
case 'S': macro_string = Macro_TF_Skin(); break;
case 't': macro_string = Macro_PointNameAtLocation(); break;
#ifdef QUAKESTATS
case 'u': macro_string = Macro_Need(); break;
case 'w': macro_string = Macro_WeaponAndAmmo(); break;
#endif
case 'x': macro_string = Macro_PointName(); break;
case 'X': macro_string = Macro_Took(); break;
case 'y': macro_string = Macro_PointLocation(); break;
case 'Y': macro_string = Macro_TookLoc(); break;
#endif
case 'n': //vicinity
case 'N': //hides from you
@ -1477,10 +1607,9 @@ typedef struct locdata_s {
char name[MAX_LOC_NAME];
} locdata_t;
#define MAX_LOC_ENTRIES 4096
locdata_t locdata[MAX_LOC_ENTRIES]; // FIXME: allocate dynamically?
int loc_numentries;
static locdata_t *locdata; // FIXME: allocate dynamically?
static size_t loc_numentries;
static size_t loc_maxentries;
static void TP_LoadLocFile (char *filename, qbool quiet)
@ -1563,8 +1692,8 @@ static void TP_LoadLocFile (char *filename, qbool quiet)
max[2] = strtod(comma, &comma);
if (*comma++ == ',')
{
if (loc_numentries >= MAX_LOC_ENTRIES)
continue;
if (loc_numentries == loc_maxentries)
Z_ReallocElements((void**)&locdata, &loc_maxentries, loc_numentries+64, sizeof(*locdata));
loc = &locdata[loc_numentries];
loc_numentries++;
@ -1618,9 +1747,8 @@ static void TP_LoadLocFile (char *filename, qbool quiet)
continue;
}
if (loc_numentries >= MAX_LOC_ENTRIES)
continue;
if (loc_numentries == loc_maxentries)
Z_ReallocElements((void**)&locdata, &loc_maxentries, loc_numentries+64, sizeof(*locdata));
loc = &locdata[loc_numentries];
loc_numentries++;
@ -1628,18 +1756,17 @@ static void TP_LoadLocFile (char *filename, qbool quiet)
loc->min[i] = loc->max[i] = atoi(Cmd_Argv(i)) / 8.0;
loc->name[0] = 0;
loc->name[sizeof(loc->name)-1] = 0; // can't rely on strncat
for (i = 3; i < argc; i++)
{
if (i != 3)
strncat (loc->name, " ", sizeof(loc->name)-1);
strncat (loc->name, Cmd_Argv(i), sizeof(loc->name)-1);
Q_strncatz (loc->name, " ", sizeof(loc->name));
Q_strncatz (loc->name, Cmd_Argv(i), sizeof(loc->name));
}
}
}
if (!quiet)
Com_Printf ("Loaded %s (%i points)\n", fullpath, loc_numentries);
Com_Printf ("Loaded %s (%lu points)\n", fullpath, (unsigned long)loc_numentries);
}
static void TP_LoadLocFile_f (void)
@ -1820,6 +1947,9 @@ void TP_SearchForMsgTriggers (char *s, int level)
string = Cmd_AliasExist (t->name, RESTRICT_LOCAL);
if (string)
{
#ifdef QUAKESTATS
Q_strncpyz(vars.lasttrigger_match, s, sizeof (vars.lasttrigger_match));
#endif
Cbuf_AddText (string, RESTRICT_LOCAL);
Cbuf_AddText ("\n", RESTRICT_LOCAL);
// Cbuf_ExecuteLevel (RESTRICT_LOCAL);
@ -1868,72 +1998,6 @@ ok:
}
}*/
int TP_CountPlayers (void)
{
int i, count;
count = 0;
for (i = 0; i < cl.allocated_client_slots ; i++) {
if (cl.players[i].name[0] && !cl.players[i].spectator)
count++;
}
return count;
}
char *TP_PlayerTeam (void)
{
return cl.players[cl.playerview[SP].playernum].team;
}
char *TP_EnemyTeam (void)
{
int i;
static char enemyteam[MAX_INFO_KEY];
char *myteam = TP_PlayerTeam();
for (i = 0; i < cl.allocated_client_slots ; i++) {
if (cl.players[i].name[0] && !cl.players[i].spectator)
{
strcpy (enemyteam, cl.players[i].team);
if (strcmp(myteam, cl.players[i].team) != 0)
return enemyteam;
}
}
return "";
}
char *TP_PlayerName (void)
{
return cl.players[cl.playerview[SP].playernum].name;
}
char *TP_EnemyName (void)
{
int i;
char *myname;
static char enemyname[MAX_SCOREBOARDNAME];
myname = TP_PlayerName ();
for (i = 0; i < cl.allocated_client_slots ; i++) {
if (cl.players[i].name[0] && !cl.players[i].spectator)
{
strcpy (enemyname, cl.players[i].name);
if (!strcmp(enemyname, myname))
return enemyname;
}
}
return "";
}
char *TP_MapName (void)
{
return host_mapname.string;
}
#ifdef QWSKINS
/*
=============================================================================
@ -2124,8 +2188,10 @@ void TP_NewMap (void)
static char last_map[MAX_QPATH];
char locname[MAX_QPATH];
#ifdef QUAKESTATS
memset (&vars, 0, sizeof(vars));
TP_FindModelNumbers ();
#endif
// FIXME, just try to load the loc file no matter what?
if (strcmp(host_mapname.string, last_map))
@ -2142,7 +2208,9 @@ void TP_NewMap (void)
strlcpy (last_map, "", sizeof(last_map));
}
#ifdef QUAKESTATS
TP_UpdateAutoStatus();
#endif
TP_ExecTrigger ("f_newmap", false);
}
@ -2351,6 +2419,7 @@ int TP_CategorizeMessage (char *s, int *offset, player_info_t **plr)
return flags;
}
#ifdef QUAKESTATS
//===================================================================
// Pickup triggers
//
@ -2420,8 +2489,8 @@ static void FlagCommand (int *flags, int defaultflags) {
for (i = 0 ; i < NUM_ITEMFLAGS ; i++)
if (*flags & (1 << i)) {
if (*str)
strncat (str, " ", sizeof(str) - strlen(str) - 1);
strncat (str, pknames[i], sizeof(str) - strlen(str) - 1);
Q_strncatz (str, " ", sizeof(str));
Q_strncatz (str, pknames[i], sizeof(str));
}
Com_Printf ("%s\n", str);
return;
@ -2624,7 +2693,7 @@ static item_t tp_items[] = {
{ it_flag, &tp_name_flag, "progs/tf_stan.mdl",
{0, 0, 45}, 40, 0
},
{ it_ra|it_ya|it_ga, NULL, "progs/armor.mdl",
{ it_ra|it_ya|it_ga, &tp_name_armor, "progs/armor.mdl", //generic armour, used only when the skin number if invalid for the other types.
{0, 0, 24}, 22, 0
},
{ it_ga, &tp_name_ga, "progs/armor.mdl",
@ -2701,7 +2770,6 @@ static void TP_FindModelNumbers (void)
}
}
#ifndef QUAKETC
// on success, result is non-zero
// on failure, result is zero
// for armors, returns skinnum+1 on success
@ -2875,11 +2943,9 @@ static void TP_ItemTaken (char *s, int flag, vec3_t org, int entnum, item_t *ite
}
*/
}
#endif
void TP_ParsePlayerInfo(player_state_t *oldstate, player_state_t *state, player_info_t *info)
{
#ifndef QUAKETC
playerview_t *pv = &cl.playerview[SP];
// if (TP_NeedRefreshSkins())
// {
@ -2918,12 +2984,10 @@ void TP_ParsePlayerInfo(player_state_t *oldstate, player_state_t *state, player_
strcpy (vars.lastdroploc, Macro_Location());
}
}
#endif
}
void TP_CheckPickupSound (char *s, vec3_t org, int seat)
{
#ifndef QUAKETC
int entnum;
item_t *item;
playerview_t *pv = &cl.playerview[seat];
@ -3031,7 +3095,6 @@ more:
return;
TP_ItemTaken (item->cvar->string, item->itemflag, org, entnum, item, seat);
}
#endif
}
qboolean R_CullSphere (vec3_t org, float radius);
@ -3165,7 +3228,7 @@ static char *Utils_TF_ColorToTeam_Failsafe(int color)
return (best == -1) ? "" : teams[best];
}
char *Utils_TF_ColorToTeam(int color)
static char *Utils_TF_ColorToTeam(int color)
{
char *s;
@ -3193,7 +3256,6 @@ char *Utils_TF_ColorToTeam(int color)
return Utils_TF_ColorToTeam_Failsafe(color);
}
static void TP_FindPoint (void)
{
packet_entities_t *pak;
@ -3377,7 +3439,6 @@ nothing:
pmove.skipent = oldskip;
}
void TP_UpdateAutoStatus(void)
{
char newstatusbuf[sizeof(vars.autoteamstatus)];
@ -3420,7 +3481,6 @@ void TP_UpdateAutoStatus(void)
void TP_StatChanged (int stat, int value)
{
#ifdef QUAKESTATS
playerview_t *pv = &cl.playerview[SP];
int i;
if (stat == STAT_HEALTH)
@ -3486,11 +3546,11 @@ void TP_StatChanged (int stat, int value)
TP_ExecTrigger ("f_weaponchange", false);
vars.activeweapon = pv->stats[STAT_ACTIVEWEAPON];
}
#endif
vars.stat_framecounts[stat] = cls.framecount;
TP_UpdateAutoStatus();
}
#endif
/*
@ -3569,8 +3629,9 @@ qbool TP_CheckSoundTrigger (char *str)
}
#define MAX_FILTERS 8
#define MAX_FILTER_LENGTH 4
static char filter_strings[8][MAX_FILTER_LENGTH+1];
static char filter_strings[MAX_FILTERS][MAX_FILTER_LENGTH+1];
static int num_filters = 0;
/*
@ -3659,7 +3720,7 @@ static void TP_MsgFilter_f (void)
}
strlcpy (filter_strings[num_filters], s+1, sizeof(filter_strings[0]));
num_filters++;
if (num_filters >= 8)
if (num_filters >= countof(filter_strings))
break;
}
}
@ -3685,9 +3746,11 @@ void TP_Init (void)
Cmd_AddCommand ("colorize", TP_Colourise_f); //us
//Cmd_AddCommand ("colorise", TP_Colourise_f); //piss off both.
#endif
#ifdef QUAKESTATS
Cmd_AddCommand ("tp_took", TP_Took_f);
Cmd_AddCommand ("tp_pickup", TP_Pickup_f);
Cmd_AddCommand ("tp_point", TP_Point_f);
#endif
TP_InitMacros();
}
@ -3698,7 +3761,10 @@ void TP_Init (void)
qboolean TP_SuppressMessage(char *buf) {
char *s;
playerview_t *pv = &cl.playerview[SP];
unsigned int seat;
if (cls.demoplayback)
return false;
for (s = buf; *s && *s != 0x7f; s++)
;
@ -3707,7 +3773,10 @@ qboolean TP_SuppressMessage(char *buf) {
*s++ = '\n';
*s++ = 0;
return (!cls.demoplayback && !pv->spectator && *s - 'A' == pv->playernum);
for (seat = 0; seat < cl.splitclients; seat++)
if (!cl.playerview[seat].spectator && *s - 'A' == cl.playerview[seat].playernum)
return true;
}
return false;
}
@ -3718,7 +3787,7 @@ void CL_Say (qboolean team, char *extra)
{
extern cvar_t cl_fakename;
char text[2048], sendtext[2048], *s;
playerview_t *pv = &cl.playerview[SP];
playerview_t *pv = &cl.playerview[CL_TargettedSplit(false)];
if (Cmd_Argc() < 2)
{
@ -3863,7 +3932,9 @@ void CL_SayMe_f (void)
void CL_SayTeam_f (void)
{
#ifdef QUAKESTATS
vars.autoteamstatus_time = realtime + 3;
#endif
CL_Say (true, NULL);
}

View file

@ -81,7 +81,7 @@ cvar_t tp_disputablemacros = CVARF("tp_disputablemacros", "1", CVAR_SEMICHEAT);
#define MAX_MACROS 64
#define MAX_MACROS 70
typedef struct {
char name[32];
@ -104,9 +104,9 @@ void Cmd_AddMacro(char *s, char *(*f)(void), int disputableintentions)
if (i == MAX_MACROS)
Sys_Error("Cmd_AddMacro: macro_count == MAX_MACROS");
Q_strncpyz(macro_commands[macro_count].name, s, sizeof(macro_commands[macro_count].name));
macro_commands[macro_count].func = f;
macro_commands[macro_count].disputableintentions = disputableintentions;
Q_strncpyz(macro_commands[i].name, s, sizeof(macro_commands[macro_count].name));
macro_commands[i].func = f;
macro_commands[i].disputableintentions = disputableintentions;
if (i == macro_count)
macro_count++;
@ -4251,9 +4251,6 @@ void Cmd_Init (void)
Cmd_AddCommandAD ("showalias", Cmd_ShowAlias_f, Key_Alias_c, NULL);
// Cmd_AddCommand ("msg_trigger", Cmd_Msg_Trigger_f);
// Cmd_AddCommand ("filter", Cmd_Msg_Filter_f);
Cmd_AddCommandAD ("toggle", Cmd_toggle_f, Cmd_Set_c, "Toggles a cvar between two values\ntoggle CVARNAME [newval [altval]]");
Cmd_AddCommandAD ("set", Cmd_set_f, Cmd_Set_c, "Changes the current value of the named cvar, creating it if it doesn't yet exist.");
Cmd_AddCommandAD ("setfl", Cmd_set_f, Cmd_Set_c, "Changes the current value of the named cvar, creating it if it doesn't yet exist. The third arg allows setting cvar flags and should be u, s, or a. This command should normally be used only inside default.cfg.");

View file

@ -2835,12 +2835,13 @@ qboolean Plug_Init(void)
CHECKBUILTIN(Sys_CloseLibrary);
#endif
rbefuncs = plugfuncs->GetEngineInterface("RBE", sizeof(rbeplugfuncs_t));
if (rbefuncs && rbefuncs->version < RBEPLUGFUNCS_VERSION)
rbefuncs = plugfuncs->GetEngineInterface("RBE", sizeof(*rbefuncs));
if (rbefuncs && ( rbefuncs->version < RBEPLUGFUNCS_VERSION ||
rbefuncs->wedictsize != sizeof(wedict_t)))
rbefuncs = NULL;
if (!rbefuncs)
{
Con_Printf("ODE plugin failed: Engine does not support external rigid body engines.\n");
Con_Printf("ODE plugin failed: Engine is incompatible.\n");
return false;
}
#ifndef ODE_STATIC

View file

@ -103,8 +103,6 @@ cvar_t ezcompat_markup = CVARD("ezcompat_markup", "1", "Attempt compatibility wi
cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active.");
cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger delayed eremote exploits in any engine (including "DISTRIBUTION") which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients.");
cvar_t sys_platform = CVAR("sys_platform", PLATFORM);
cvar_t pkg_downloads_url = CVARFD("pkg_downloads_url", NULL, CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "The URL of a package updates list."); //read from the default.fmf
cvar_t pkg_autoupdate = CVARFD("pkg_autoupdate", "-1", CVAR_NOTFROMSERVER|CVAR_NOSAVE|CVAR_NOSET, "Controls autoupdates, can only be changed via the downloads menu.\n0: off.\n1: enabled (stable only).\n2: enabled (unstable).\nNote that autoupdate will still prompt the user to actually apply the changes."); //read from the package list only.
#ifdef HAVE_LEGACY
cvar_t pm_noround = CVARD("pm_noround", "0", "Disables player prediction snapping, in a way that cannot be reliably predicted but may be needed to avoid map bugs.");
#endif
@ -4288,6 +4286,12 @@ skipwhite:
len++;
}
}
if (c == '\\' && data[1] == '\"')
{
if (tokentype)
*tokentype = TTP_STRING;
return COM_ParseCString(data+1, token, tokenlen, NULL);
}
// parse single characters
if (strchr(punctuation, c))

View file

@ -583,9 +583,10 @@ char *VFS_GETS(vfsfile_t *vf, char *buffer, size_t buflen);
void VARGS VFS_PRINTF(vfsfile_t *vf, const char *fmt, ...) LIKEPRINTF(2);
enum fs_relative{
FS_BINARYPATH, //for dlls and stuff
//note that many of theses paths can map to multiple system locations. FS_NativePath can vary somewhat in terms of what it returns, generally favouring writable locations rather then the path that actually contains a file.
FS_BINARYPATH, //where the 'exe' is located. we'll check here for dlls too.
FS_LIBRARYPATH, //for system dlls and stuff
FS_ROOT, //./ (effective -homedir if enabled, otherwise effective -basedir arg)
FS_ROOT, //either homedir or basedir,
FS_SYSTEM, //a system path. absolute paths are explicitly allowed and expected, but not required.
//after this point, all types must be relative to a gamedir
@ -605,6 +606,7 @@ void FS_CreatePath(const char *pname, enum fs_relative relativeto);
qboolean FS_Rename(const char *oldf, const char *newf, enum fs_relative relativeto); //0 on success, non-0 on error
qboolean FS_Rename2(const char *oldf, const char *newf, enum fs_relative oldrelativeto, enum fs_relative newrelativeto);
qboolean FS_Remove(const char *fname, enum fs_relative relativeto); //0 on success, non-0 on error
qboolean FS_RemoveTree(searchpathfuncs_t *pathhandle, const char *fname);
qboolean FS_Copy(const char *source, const char *dest, enum fs_relative relativesource, enum fs_relative relativedest);
qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out, int outlen); //if you really need to fopen yourself
qboolean FS_WriteFile (const char *filename, const void *data, int len, enum fs_relative relativeto);
@ -656,7 +658,7 @@ enum manifestdeptype_e
};
typedef struct
{
qboolean blockupdate; //set to block the updateurl from being used this session. this avoids recursive updates when manifests contain the same update url.
char *filename; //filename the manifest was read from. not necessarily writable... NULL when the manifest is synthesised or from http.
enum
{
MANIFEST_SECURITY_NOT, //don't trust it, don't even allow downloadsurl.
@ -679,17 +681,20 @@ typedef struct
} homedirtype;
char *mainconfig; //eg "fte.cfg", reducing conflicts with other engines, but can be other values...
char *updateurl; //url to download an updated manifest file from.
char *updatefile; //this is the file that needs to be written to update the manifest.
qboolean blockupdate; //set to block the updateurl from being used this session. this avoids recursive updates when manifests contain the same update url.
char *installation; //optional hardcoded commercial name, used for scanning the registry to find existing installs.
char *formalname; //the commercial name of the game. you'll get FULLENGINENAME otherwise.
#ifdef PACKAGEMANAGER
char *downloadsurl; //optional installable files (menu)
char *installupd; //which download/updated package to install.
#endif
char *protocolname; //the name used for purposes of dpmaster
char *defaultexec; //execed after cvars are reset, to give game-specific engine-defaults.
char *defaultoverrides; //execed after default.cfg, to give usable defaults even when the mod the user is running is shit.
char *eula; //when running as an installer, the user will be presented with this as a prompt
char *rtcbroker; //the broker to use for webrtc connections.
char *basedir; //this is where we expect to find the data.
char *iconname; //path we can find the icon (relative to the fmf's location)
struct
{
enum
@ -705,7 +710,7 @@ typedef struct
} flags;
char *path;
} gamepath[8];
struct manpack_s
struct manpack_s //FIXME: this struct should be replaced with packagemanager info instead.
{
int type;
char *path; //the 'pure' name
@ -719,8 +724,9 @@ typedef struct
} ftemanifest_t;
extern ftemanifest_t *fs_manifest; //currently active manifest.
void FS_Manifest_Free(ftemanifest_t *man);
ftemanifest_t *FS_Manifest_Parse(const char *fname, const char *data);
void PM_Shutdown(void);
ftemanifest_t *FS_Manifest_ReadMem(const char *fname, const char *basedir, const char *data);
ftemanifest_t *FS_Manifest_ReadSystem(const char *fname, const char *basedir);
void PM_Shutdown(qboolean soft);
void PM_Command_f(void);
qboolean PM_CanInstall(const char *packagename);
@ -737,6 +743,7 @@ struct gamepacks
char *subpath; //within the package (for zips)
};
void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths);
qboolean FS_GamedirIsOkay(const char *path);
char *FS_GetGamedir(qboolean publicpathonly);
char *FS_GetManifestArgs(void);
int FS_GetManifestArgv(char **argv, int maxargs);
@ -853,6 +860,7 @@ size_t Base64_DecodeBlock(const char *in, const char *in_end, qbyte *out, size_t
size_t Base16_EncodeBlock(const char *in, size_t length, qbyte *out, size_t outsize);
size_t Base16_DecodeBlock(const char *in, qbyte *out, size_t outsize);
#define DIGEST_MAXSIZE (512/8) //largest valid digest size, in bytes
typedef struct
{
unsigned int digestsize;

View file

@ -181,7 +181,7 @@
// Features required by vanilla quake/quakeworld...
//#define QUAKETC
#define QUAKESTATS //defines STAT_HEALTH etc. if omitted, you'll need to provide that functionality yourself.
#define QUAKEHUD //support for drawing the vanilla hud.
#define QUAKEHUD //support for drawing the vanilla hud. disable this if you're always going to be using csqc (or equivelent)
#define QWSKINS //disabling this means no qw .pcx skins nor enemy/team skin/colour forcing
//#define NOBUILTINMENUS
//#define NOLEGACY //just spike trying to kill off crappy crap...

File diff suppressed because it is too large Load diff

View file

@ -78,10 +78,19 @@ unsigned int PM_MarkUpdates (void); //mark new/updated packages as needing insta
void PM_ApplyChanges(void); //for -install/-doinstall args
void PM_ManifestPackage(const char *name, int security);
qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize); //names the engine we should be running
void PM_AddManifestPackages(ftemanifest_t *man);
void Menu_Download_Update(void);
int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr);
struct modlist_s
{
ftemanifest_t *manifest;
char *gamedir;
char *description;
};
struct modlist_s *Mods_GetMod(size_t diridx);
#define SPF_REFERENCED 1 //something has been loaded from this path. should filter out client references...
#define SPF_COPYPROTECTED 2 //downloads are not allowed fom here.
#define SPF_TEMPORARY 4 //a map-specific path, purged at map change.

View file

@ -378,6 +378,31 @@ static qboolean QDECL FSSTDIO_FileStat (searchpathfuncs_t *handle, flocation_t *
return false;
}
static qboolean QDECL FSSTDIO_RenameFile(searchpathfuncs_t *handle, const char *oldname, const char *newname)
{
stdiopath_t *sp = (void*)handle;
char oldsyspath[MAX_OSPATH];
char newsyspath[MAX_OSPATH];
if (fs_readonly)
return false;
if ((unsigned int)snprintf (oldsyspath, sizeof(oldsyspath), "%s/%s", sp->rootpath, oldname) > sizeof(oldsyspath)-1)
return false; //too long
if ((unsigned int)snprintf (newsyspath, sizeof(newsyspath), "%s/%s", sp->rootpath, newname) > sizeof(newsyspath)-1)
return false; //too long
return Sys_Rename(oldsyspath, newsyspath);
}
static qboolean QDECL FSSTDIO_RemoveFile(searchpathfuncs_t *handle, const char *filename)
{
stdiopath_t *sp = (void*)handle;
char syspath[MAX_OSPATH];
if (fs_readonly)
return false;
if ((unsigned int)snprintf (syspath, sizeof(syspath), "%s/%s", sp->rootpath, filename) > sizeof(syspath)-1)
return false; //too long
if (*filename && filename[strlen(filename)-1] == '/')
return Sys_rmdir(syspath);
return Sys_remove(syspath);
}
searchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, searchpathfuncs_t *parent, const char *filename, const char *desc, const char *prefix)
{
@ -404,6 +429,8 @@ searchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, searchpathfuncs
np->pub.PollChanges = FSSTDIO_PollChanges;
np->pub.FileStat = FSSTDIO_FileStat;
np->pub.CreateFile = FSSTDIO_CreateLoc;
np->pub.RenameFile = FSSTDIO_RenameFile;
np->pub.RemoveFile = FSSTDIO_RemoveFile;
return &np->pub;
}

View file

@ -657,6 +657,8 @@ static qboolean QDECL VFSW32_RemoveFile(searchpathfuncs_t *handle, const char *f
if (fs_readonly)
return false;
snprintf (syspath, sizeof(syspath)-1, "%s/%s", wp->rootpath, filename);
if (*filename && filename[strlen(filename)-1] == '/')
return Sys_rmdir(syspath);
return Sys_remove(syspath);
}

View file

@ -755,10 +755,10 @@ struct certprompt_s
qbyte cert[1];
};
static struct certprompt_s *certlog_curprompt;
static void CertLog_Add_Prompted(void *vctx, int button)
static void CertLog_Add_Prompted(void *vctx, promptbutton_t button)
{
struct certprompt_s *ctx = vctx;
if (button == 0) //button_yes / button_left
if (button == PROMPT_YES) //button_yes / button_left
{
CertLog_Update(ctx->hostname, ctx->cert, ctx->certsize);
CertLog_Write();

View file

@ -1962,6 +1962,7 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
static rbeplugfuncs_t funcs =
{
RBEPLUGFUNCS_VERSION,
sizeof(wedict_t),
World_RegisterPhysicsEngine,
World_UnregisterPhysicsEngine,

View file

@ -1606,29 +1606,26 @@ void QCBUILTIN PF_memcpy (pubprogfuncs_t *prinst, struct globalvars_s *pr_global
int dst = G_INT(OFS_PARM0);
int src = G_INT(OFS_PARM1);
int size = G_INT(OFS_PARM2);
if (dst < 0 || dst+size >= prinst->stringtablesize)
{
if (size < 0 || size > prinst->stringtablesize)
PR_BIError(prinst, "PF_memcpy: invalid size\n");
else if (dst < 0 || dst+size > prinst->stringtablesize)
PR_BIError(prinst, "PF_memcpy: invalid dest\n");
return;
}
if (src < 0 || src+size >= prinst->stringtablesize)
{
else if (src < 0 || src+size > prinst->stringtablesize)
PR_BIError(prinst, "PF_memcpy: invalid source\n");
return;
}
memmove(prinst->stringtable + dst, prinst->stringtable + src, size);
else
memmove(prinst->stringtable + dst, prinst->stringtable + src, size);
}
void QCBUILTIN PF_memfill8 (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
int dst = G_INT(OFS_PARM0);
int val = G_INT(OFS_PARM1);
int size = G_INT(OFS_PARM2);
if (dst < 0 || dst+size >= prinst->stringtablesize)
{
PR_BIError(prinst, "PF_memcpy: invalid dest\n");
return;
}
memset(prinst->stringtable + dst, val, size);
if (size < 0 || size > prinst->stringtablesize)
PR_BIError(prinst, "PF_memcpy: invalid size\n");
else if (dst < 0 || dst+size > prinst->stringtablesize)
PR_BIError(prinst, "PF_memfill8: invalid dest\n");
else
memset(prinst->stringtable + dst, val, size);
}
void QCBUILTIN PF_memptradd (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
@ -1670,6 +1667,8 @@ typedef struct
char *stringdata;
};
} pf_hashentry_t;
#define HASH_REPLACE 256
#define HASH_ADD 512
#define FIRSTTABLE 1
static pf_hashtab_t *pf_hashtab;
static size_t pf_hash_maxtables;
@ -1787,6 +1786,7 @@ void QCBUILTIN PF_hash_getcb (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
memcpy(G_VECTOR(OFS_RETURN), G_VECTOR(OFS_PARM2), sizeof(vec3_t));
*/
}
void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
pf_hashtab_t *tab = PF_hash_findtab(prinst, G_FLOAT(OFS_PARM0));
@ -1799,7 +1799,7 @@ void QCBUILTIN PF_hash_add (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
{
if (!type)
type = tab->defaulttype;
if (!(flags & 512) || (flags & 256))
if (!(flags & HASH_ADD) || (flags & HASH_REPLACE))
{
ent = Hash_Get(&tab->tab, name);
if (ent)
@ -1907,11 +1907,138 @@ void QCBUILTIN PF_hash_createtab (pubprogfuncs_t *prinst, struct globalvars_s *p
G_FLOAT(OFS_RETURN) = i + FIRSTTABLE;
}
void pf_hash_savegame(void) //write the persistant table to a saved game.
static void PF_hash_savetab(void *ctx, void *data)
{
char tmp[8192];
pf_hashentry_t *ent = data;
if (ent->type == ev_string)
VFS_PRINTF (ctx, "\t%i \"%s\" %s\n", ent->type, ent->name, COM_QuotedString(ent->stringdata, tmp, sizeof(tmp), false));
else if (ent->type == ev_vector)
VFS_PRINTF (ctx, "\t%i \"%s\" %f %f %f\n", ent->type, ent->name, ent->data[0], ent->data[1], ent->data[2]);
else if (ent->type == ev_float)
VFS_PRINTF (ctx, "\t%i \"%s\" %f\n", ent->type, ent->name, *(float*)ent->data);
else
VFS_PRINTF (ctx, "\t%i \"%s\" %#x\n", ent->type, ent->name, *(int*)ent->data);
}
void pf_hash_loadgame(void) //(re)load the persistant table.
static void PR_hash_savegame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary) //write the persistant table to a saved game.
{
unsigned int tab;
char *tmp = NULL;
for (tab = 0; tab < pf_hash_maxtables; tab++)
{
if (pf_hashtab[tab].prinst == prinst)// && (pf_hashtab[tab].flags & BUFFLAG_SAVED))
{
VFS_PRINTF (f, "hashtable %u %i %u\n", tab+FIRSTTABLE, pf_hashtab[tab].defaulttype, (unsigned int)pf_hashtab[tab].tab.numbuckets);
VFS_PRINTF (f, "{\n");
Hash_Enumerate(&pf_hashtab[tab].tab, PF_hash_savetab, f);
VFS_PRINTF (f, "}\n");
}
}
free(tmp);
}
static const char *PR_hash_loadgame(pubprogfuncs_t *prinst, const char *l)
{
char name[8192];
char token[65536];
int tabno;
int nlen, vlen;
etype_t hashtype;
size_t buffersize;
com_tokentype_t tt;
pf_hashtab_t *tab;
pf_hashentry_t *ent;
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
tabno = atoi(token)-FIRSTTABLE;
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
hashtype = atoi(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
buffersize = atoi(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_LINEENDING)return NULL;
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_PUNCTUATION)return NULL;
if (strcmp(token, "{")) return NULL;
if (tabno < 0 || tabno >= 1<<16)
return NULL;
if (tabno >= pf_hash_maxtables)
Z_ReallocElements((void**)&pf_hashtab, &pf_hash_maxtables, tabno+1, sizeof(*pf_hashtab));
tab = &pf_hashtab[tabno];
if (tab->prinst)
{
tab->prinst = NULL;
Hash_Enumerate(&tab->tab, PF_hash_destroytab_enum, NULL);
Z_Free(tab->bucketmem);
tab->bucketmem = NULL;
}
tab->prinst = prinst;
tab->defaulttype = hashtype;
tab->bucketmem = Z_Malloc(Hash_BytesForBuckets(buffersize));
Hash_InitTable(&tab->tab, buffersize, tab->bucketmem);
for(;;)
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);
if (tt == TTP_LINEENDING)
continue;
if (tt == TTP_PUNCTUATION && !strcmp(token, "}"))
break;
if (tt != TTP_RAWTOKEN)
break;
hashtype = atoi(token);
l = COM_ParseTokenOut(l, NULL, name, sizeof(name), &tt);if (tt != TTP_STRING)return NULL;
nlen = strlen(name);
if (hashtype == ev_string)
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_STRING)return NULL;
vlen = strlen(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_LINEENDING)return NULL;
ent = BZ_Malloc(sizeof(*ent) + nlen+1 + vlen+1);
ent->name = (char*)(ent+1);
ent->type = hashtype;
ent->stringdata = ent->name+(nlen+1);
memcpy(ent->name, name, nlen);
ent->name[nlen] = 0;
memcpy(ent->stringdata, token, vlen+1);
Hash_Add(&tab->tab, ent->name, ent, &ent->buck);
}
else
{
vec3_t data = {0,0,0};
if (hashtype == ev_vector)
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
data[0] = atof(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
data[1] = atof(token);
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
data[2] = atof(token);
}
else if (hashtype == ev_float)
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
*data = atof(token);
}
else //treat it as an ev_int
{
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_RAWTOKEN)return NULL;
*(int*)data = atoi(token);
}
l = COM_ParseTokenOut(l, NULL, token, sizeof(token), &tt);if (tt != TTP_LINEENDING)return NULL;
ent = BZ_Malloc(sizeof(*ent) + nlen + 1);
ent->name = (char*)(ent+1);
ent->type = hashtype;
memcpy(ent->name, name, nlen);
ent->name[nlen] = 0;
memcpy(ent->data, data, sizeof(vec3_t));
Hash_Add(&tab->tab, ent->name, ent, &ent->buck);
}
}
return l;
}
void pf_hash_preserve(void) //map changed, make sure it can be reset properly.
{
@ -4042,7 +4169,7 @@ struct strbuf {
};
#define BUFFLAG_SAVED 1
#define BUFSTRBASE 1
#define BUFSTRBASE 1 //officially these are 0-based (ie: use negatives for not-a-buffer), but fte biases it to catch qc bugs.
struct strbuf *strbuflist;
size_t strbufmax;
@ -4532,9 +4659,9 @@ void QCBUILTIN PF_buf_cvarlist (pubprogfuncs_t *prinst, struct globalvars_s *pr
for (grp=cvar_groups ; grp ; grp=grp->next)
for (var=grp->cvars ; var ; var=var->next)
{
if (plen && (pwc?wildcmp(pattern, var->name):strncmp(var->name, pattern, plen)))
if (plen && (pwc?!wildcmp(pattern, var->name):strncmp(var->name, pattern, plen)))
continue;
if (alen && (awc?!wildcmp(antipattern, var->name):!strncmp(var->name, antipattern, alen)))
if (alen && (awc?wildcmp(antipattern, var->name):!strncmp(var->name, antipattern, alen)))
continue;
PF_bufstr_add_internal(bufno, var->name, true);
@ -4819,7 +4946,8 @@ static void PR_uri_get_callback2(int iarg, void *data)
G_INT(OFS_PARM2) = prinst->AllocTempString(prinst, &buffer, len+1);
len = VFS_READ(ctx->file, buffer, len);
if (len < 0)
buffer[len] = 0;
len = 0;
buffer[len] = 0;
G_INT(OFS_PARM3) = len;
}
@ -6571,6 +6699,12 @@ qboolean PR_Common_LoadGame(pubprogfuncs_t *prinst, char *command, const char **
if (!l)
return false;
}
else if (!strcmp(command, "hashtable"))
{
l = PR_hash_loadgame(prinst, l);
if (!l)
return false;
}
else
return false;
*file = l;
@ -6579,6 +6713,7 @@ qboolean PR_Common_LoadGame(pubprogfuncs_t *prinst, char *command, const char **
void PR_Common_SaveGame(vfsfile_t *f, pubprogfuncs_t *prinst, qboolean binary)
{
PR_buf_savegame(f, prinst, binary);
PR_hash_savegame(f, prinst, binary);
}
@ -6804,6 +6939,7 @@ lh_extension_t QSG_Extensions[] = {
#ifndef SERVERONLY
{"DP_CON_SETA", 0, NULL, {NULL}, "The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file."},
#endif
{"DP_CSQC_ROTATEMOVES"},
{"DP_EF_ADDITIVE"},
//--{"DP_ENT_ALPHA"}, //listed above
{"DP_EF_BLUE"}, //hah!! This is QuakeWorld!!!
@ -6841,6 +6977,7 @@ lh_extension_t QSG_Extensions[] = {
{"DP_QC_CVAR_DEFSTRING", 1, NULL, {"cvar_defstring"}},
{"DP_QC_CVAR_STRING", 1, NULL, {"cvar_string"}}, //448 builtin.
{"DP_QC_CVAR_TYPE", 1, NULL, {"cvar_type"}},
{"DP_QC_DIGEST_SHA256"},
{"DP_QC_EDICT_NUM", 1, NULL, {"edict_num"}},
{"DP_QC_ENTITYDATA", 5, NULL, {"numentityfields", "entityfieldname", "entityfieldtype", "getentityfieldstring", "putentityfieldstring"}},
{"DP_QC_ETOS", 1, NULL, {"etos"}},
@ -6987,6 +7124,10 @@ lh_extension_t QSG_Extensions[] = {
{"FTE_QC_CHECKPVS", 1, NULL, {"checkpvs"}},
{"FTE_QC_CROSSPRODUCT", 1, NULL, {"crossproduct"}},
{"FTE_QC_CUSTOMSKINS", 1, NULL, {"setcustomskin", "loadcustomskin", "applycustomskin", "releasecustomskin"}, "The engine supports the use of q3 skins, as well as the use of such skin 'files' to specify rich top+bottom colours, qw skins, geomsets, or texture composition even on non-players.."},
{"FTE_QC_DIGEST_SHA1"},
{"FTE_QC_DIGEST_SHA224"},
{"FTE_QC_DIGEST_SHA384"},
{"FTE_QC_DIGEST_SHA512"},
{"FTE_QC_FS_SEARCH_SIZEMTIME", 2, NULL, {"search_getfilesize", "search_getfilemtime"}},
{"FTE_QC_HARDWARECURSORS", 0, NULL, {NULL}, "setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed."},
{"FTE_QC_HASHTABLES", 6, NULL, {"hash_createtab", "hash_destroytab", "hash_add", "hash_get", "hash_delete", "hash_getkey"}, "Provides efficient string-based lookups."},

View file

@ -455,6 +455,7 @@ void QCBUILTIN PF_cl_setwindowcaption (pubprogfuncs_t *prinst, struct globalvars
void QCBUILTIN PF_cl_playingdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_runningserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getgamedirinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cl_getpackagemanagerinfo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_create (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_destroy (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
void QCBUILTIN PF_cs_media_command (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals);
@ -606,8 +607,9 @@ unsigned int FTEToDPContents(unsigned int contents);
#define SOLID_BSP 4 // bsp clip, touch on edge, block
#define SOLID_PHASEH2 5 // hexen2 flag - these ents can be freely walked through or something
#define SOLID_CORPSE 5 // non-solid to solid_slidebox entities and itself.
#define SOLID_LADDER 20 //dmw. touch on edge, not blocking. Touching players have different physics. Otherwise a SOLID_TRIGGER
#define SOLID_LADDER 20 //spike: legacy. forces FTECONTENTS_LADDER.
#define SOLID_PORTAL 21 //1: traces always use point-size. 2: various movetypes automatically transform entities. 3: traces that impact portal bbox use a union. 4. traces ignore part of the world within the portal's box
#define SOLID_BSPTRIGGER 22 //spike: like solid trigger, except uses bsp checks instead of just aabb.
#define SOLID_PHYSICS_BOX 32 // deprecated. physics object (mins, maxs, mass, origin, axis_forward, axis_left, axis_up, velocity, spinvelocity)
#define SOLID_PHYSICS_SPHERE 33 // deprecated. physics object (mins, maxs, mass, origin, axis_forward, axis_left, axis_up, velocity, spinvelocity)
#define SOLID_PHYSICS_CAPSULE 34 // deprecated. physics object (mins, maxs, mass, origin, axis_forward, axis_left, axis_up, velocity, spinvelocity)
@ -639,6 +641,7 @@ unsigned int FTEToDPContents(unsigned int contents);
typedef struct
{
int version;
int wedictsize; //sizeof(wedict_t)
qboolean (QDECL *RegisterPhysicsEngine)(const char *enginename, void(QDECL*start_physics)(world_t*world)); //returns false if there's already one active.
void (QDECL *UnregisterPhysicsEngine)(const char *enginename); //returns false if there's already one active.
@ -745,6 +748,22 @@ typedef enum
VF_SKYROOM_CAMERA = 222,
VF_PIXELPSCALE = 223, //[dpi_x, dpi_y, dpi_y/dpi_x]
VF_PROJECTIONOFFSET = 224, //allows for off-axis projections.
VF_DP_CLEARSCREEN = 201, // weird behaviour that disables a whole load of things.
//fuck DP and their complete lack of respect for existing implemenetations
VF_DP_FOG_DENSITY = 202, //misassigned
VF_DP_FOG_COLOR = 203, //misassigned
VF_DP_FOG_COLOR_R = 204, //misassigned
VF_DP_FOG_COLOR_G = 205, //misassigned
VF_DP_FOG_COLOR_B = 206, //misassigned
VF_DP_FOG_ALPHA = 207, //misassigned
VF_DP_FOG_START = 208, //misassigned
VF_DP_FOG_END = 209, //misassigned
VF_DP_FOG_HEIGHT = 210, //misassigned
VF_DP_FOG_FADEDEPTH = 211, //misassigned
VF_DP_MAINVIEW = 400, // defective. should be a viewid instead, allowing for per-view motionblur instead of disabling it outright
VF_DP_MINFPS_QUALITY = 401, //multiplier for lod and culling to try to reduce costs.
} viewflags;
/*FIXME: this should be changed*/
@ -808,6 +827,32 @@ enum csqc_input_event
CSIE_GYROSCOPE = 7, /*x, y, z rotational acceleration*/
};
enum getgamedirinfo_e
{
GGDI_GAMEDIR=0, //the publically visible gamedir reported by servers.
GGDI_DESCRIPTION=1, //some text from the .fmf or a gamedirin
GGDI_OVERRIDES=2, //some text you can parse for custom info.
GGDI_LOADCOMMAND=3, //returns a string which can be localcmded to load the mod, with whatever quirks are needed to activate it properly.
GGDI_ICON=4, //returns a string which can be drawpiced.
GGDI_ALLGAMEDIRS=5, //; delimited list basegames;gamedirs ordering
};
enum packagemanagerinfo_e
{
GPMI_NAME, //name of the package, for use with the pkg command.
GPMI_CATEGORY, //category text
GPMI_TITLE, //name of the package, for showing the user.
GPMI_VERSION, //version info (may have multiple with the same name but different versions)
GPMI_DESCRIPTION, //some blurb
GPMI_LICENSE, //what license its distributed under
GPMI_AUTHOR, //name of the person(s) who created it
GPMI_WEBSITE, //where to contribute/find out more info/etc
GPMI_INSTALLED, //current state
GPMI_ACTION, //desired state
GPMI_AVAILABLE, //whether it may be downloaded or not.
GPMI_FILESIZE, //whether it may be downloaded or not.
GPMI_GAMEDIR, //so you know which mod(s) its relevant for
};
#ifdef TERRAIN
enum terrainedit_e
{

View file

@ -363,14 +363,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
enum clustercmdops_e
{
ccmd_bad = 0, //abort!
ccmd_stuffcmd = 1, //regular ol stuffcmd
ccmd_stuffcmd, //regular ol stuffcmd
//string concommand
ccmd_print = 2,
ccmd_setcvar, //master->server to order a cvar change.
ccmd_print,
//string message
ccmd_acceptserver,
//serverid
ccmd_lostplayer, //player dropped/timed out
//long plid
ccmd_foundplayer, //server->master, saying that a player tried to connect directly (and we need their info)
//string name
//string clientaddress
//string guid
ccmd_takeplayer, //master->server, saying to allocate a slot for a player.
//long plid
//long fromsvid (0=no reply needed)

View file

@ -193,6 +193,8 @@ void VM_fcloseall (int owner)
//filesystem searches result in a tightly-packed blob of null-terminated filenames (along with a count for how many entries)
//$modlist searches give both gamedir AND description strings (in that order) instead of just one string per entry (loaded via fs_game cvar along with a vid_restart).
typedef struct {
char *initialbuffer;
char *buffer;

View file

@ -425,6 +425,7 @@ pubsubserver_t *Sys_ForkServer(void)
linsubserver_t *ctx;
char *argv[64];
int argc = 0;
int l;
argv[argc++] = exename;
argv[argc++] = "-clusterslave";
@ -437,9 +438,10 @@ pubsubserver_t *Sys_ForkServer(void)
#elif 0
strcpy(exename, "/tmp/ftedbg/fteqw.sv");
#else
memset(exename, 0, sizeof(exename)); //having problems with valgrind being stupid.
if (readlink("/proc/self/exe", exename, sizeof(exename)-1) <= 0)
l = readlink("/proc/self/exe", exename, sizeof(exename)-1);
if (l <= 0)
return NULL;
exename[l] = 0;
#endif
Con_DPrintf("Execing %s\n", exename);

View file

@ -2898,7 +2898,7 @@ void BE_GenModelBatches(batch_t **batches, const dlight_t *dl, unsigned int bemo
switch(emodel->type)
{
case mod_brush:
if (r_drawentities.ival == 2)
if (r_drawentities.ival == 2 && cls.allow_cheats) //2 is considered a cheat, because it can be used as a wallhack (whereas mdls are not normally considered as occluding).
continue;
Surf_GenBrushBatches(batches, ent);
break;

View file

@ -1021,48 +1021,6 @@ void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)
}*/
}
static void RevertToKnownState(void)
{
if (shaderstate.currentvao)
qglBindVertexArray(0);
shaderstate.currentvao = 0;
shaderstate.curvertexvbo = ~0;
GL_SelectVBO(0);
// GL_SelectEBO(0);
while(shaderstate.lastpasstmus>0)
{
GL_LazyBind(--shaderstate.lastpasstmus, 0, r_nulltex);
}
GL_SelectTexture(0);
#ifndef GLSLONLY
if (!gl_config_nofixedfunc)
{
BE_SetPassBlendMode(0, PBM_REPLACE);
qglColor4f(1,1,1,1);
GL_DeSelectProgram();
}
#endif
shaderstate.shaderbits &= ~(SBITS_DEPTHFUNC_BITS|SBITS_MASK_BITS|SBITS_AFFINE);
shaderstate.shaderbits |= SBITS_MISC_DEPTHWRITE;
shaderstate.shaderbits &= ~(SBITS_BLEND_BITS);
qglDisable(GL_BLEND);
qglDepthFunc(GL_LEQUAL);
qglDepthMask(GL_TRUE);
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
}
void PPL_RevertToKnownState(void)
{
RevertToKnownState();
}
#ifdef RTLIGHTS
void GLBE_SetupForShadowMap(dlight_t *dl, int texwidth, int texheight, float shadowscale)
{
@ -5552,7 +5510,7 @@ void GLBE_SubmitMeshes (batch_t **worldbatches, int start, int stop)
}
}
static void BE_UpdateLightmaps(void)
void GLBE_UpdateLightmaps(void)
{
lightmapinfo_t *lm;
int lmidx;
@ -5580,7 +5538,20 @@ static void BE_UpdateLightmaps(void)
GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
qglTexImage2D(GL_TEXTURE_2D, 0, gl_config.formatinfo[lm->fmt].internalformat, lm->width, lm->height, 0, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lm->lightmaps);
if (qglTexStorage2D && gl_config.formatinfo[lm->fmt].sizedformat)
{
qglTexStorage2D(GL_TEXTURE_2D, 1, gl_config.formatinfo[lm->fmt].sizedformat, lm->width, lm->height);
if (lm->pbo_handle)
{
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lm->pbo_handle);
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, (char*)NULL + t*lm->width*lm->pixbytes);
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
}
else
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lm->lightmaps+t*lm->width*lm->pixbytes);
}
else
qglTexImage2D(GL_TEXTURE_2D, 0, gl_config.formatinfo[lm->fmt].internalformat, lm->width, lm->height, 0, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lm->lightmaps);
if (gl_config.glversion >= (gl_config.gles?3.0:3.3))
{
@ -5593,7 +5564,14 @@ static void BE_UpdateLightmaps(void)
else
{
GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lm->lightmaps+t*lm->width*lm->pixbytes);
if (lm->pbo_handle)
{
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, lm->pbo_handle);
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, (char*)NULL + t*lm->width*lm->pixbytes);
qglBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
}
else
qglTexSubImage2D(GL_TEXTURE_2D, 0, 0, t, lm->width, b-t, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lm->lightmaps+t*lm->width*lm->pixbytes);
}
lm->modified = false;
lm->rectchange.l = lm->width;
@ -5651,10 +5629,7 @@ void GLBE_RenderToTextureUpdate2d(qboolean destchanged)
shaderstate.tex_sourcecol = R2D_RT_GetTexture(r_refdef.rt_sourcecolour.texname, &width, &height);
shaderstate.tex_sourcedepth = R2D_RT_GetTexture(r_refdef.rt_depth.texname, &width, &height);
if (*r_refdef.nearenvmap.texname)
shaderstate.tex_reflectcube = Image_GetTexture(r_refdef.nearenvmap.texname, NULL, IF_TEXTYPE_CUBE, NULL, NULL, 0, 0, TF_INVALID);
else
shaderstate.tex_reflectcube = r_nulltex;
shaderstate.tex_reflectcube = R_GetDefaultEnvmap();
}
}
void GLBE_FBO_Sources(texid_t sourcecolour, texid_t sourcedepth)
@ -6301,7 +6276,7 @@ void GLBE_DrawWorld (batch_t **worldbatches)
// else
// shaderstate.updatetime = cl.servertime;
BE_UpdateLightmaps();
GLBE_UpdateLightmaps();
if (worldbatches)
{
if (worldbatches[SHADER_SORT_SKY] && r_refdef.skyroom_enabled)

View file

@ -2665,7 +2665,10 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max
if (codepoint > ' ')
l = n;
else
{
l = n;
break;
}
}
if (l == start && bt>start)
l = Font_DecodeReverse(bt, start, &codeflags, &codepoint);
@ -2677,6 +2680,14 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max
if (foundlines == maxlines)
break;
for (;;)
{
n = Font_Decode(l, &codeflags, &codepoint);
if (!(codeflags & CON_HIDDEN) && (codepoint != ' '))
break;
l = n;
}
start=l;
if (start == end)
break;

View file

@ -3413,7 +3413,9 @@ static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt, size
else if (!strncmp(extfmt, "RGBA", 4)) newfmt = PTI_RGBA8; //32bpp, we don't normally need this alpha precision (padding can be handy though, for the lazy).
else if (!strncmp(extfmt, "RGB", 4)) newfmt = PTI_RGB8; //24bpp
else if (!strncmp(extfmt, "565", 4)) newfmt = PTI_RGB565; //16bpp
else if (!strncmp(extfmt, "4444", 4)) newfmt = PTI_RGBA4444; //16bpp
else if (!strncmp(extfmt, "5551", 4)) newfmt = PTI_RGBA5551; //16bpp
else if (!strncmp(extfmt, "LUM8", 4)) newfmt = PTI_L8; //8bpp
else if (!strncmp(extfmt, "EXP5", 4)) newfmt = PTI_E5BGR9; //32bpp, we don't normally need this alpha precision...
else if (!strncmp(extfmt, "BC1", 4)) newfmt = PTI_BC1_RGBA; //4bpp
else if (!strncmp(extfmt, "BC2", 4)) newfmt = PTI_BC2_RGBA; //8bpp, we don't normally need this alpha precision...
@ -5482,6 +5484,10 @@ static qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsi
//
// set up the submodels (FIXME: this is confusing)
//
for (j=0 ; j<2 ; j++)
Q1BSP_CheckHullNodes(&mod->hulls[j]);
for (i=0, submod = mod; i<mod->numsubmodels ; i++)
{
bm = &mod->submodels[i];
@ -5489,7 +5495,7 @@ static qboolean QDECL Mod_LoadBrushModel (model_t *mod, void *buffer, size_t fsi
submod->rootnode = submod->nodes + bm->headnode[0];
submod->hulls[0].firstclipnode = bm->headnode[0];
submod->hulls[0].available = true;
Q1BSP_CheckHullNodes(&submod->hulls[0]);
// Q1BSP_CheckHullNodes(&submod->hulls[0]);
TRACE(("LoadBrushModel %i\n", __LINE__));
for (j=1 ; j<MAX_MAP_HULLSM ; j++)
@ -5501,8 +5507,8 @@ TRACE(("LoadBrushModel %i\n", __LINE__));
if (submod->hulls[j].firstclipnode > submod->hulls[j].lastclipnode)
submod->hulls[j].available = false;
if (submod->hulls[j].available)
Q1BSP_CheckHullNodes(&submod->hulls[j]);
// if (submod->hulls[j].available)
// Q1BSP_CheckHullNodes(&submod->hulls[j]);
}
if (mod->fromgame == fg_halflife && i)

View file

@ -157,8 +157,9 @@ typedef struct batch_s
{
struct
{
unsigned int shadowbatch; //a unique index to accelerate shadowmesh generation (dlights, yay!)
unsigned int ebobatch; //
unsigned int shadowbatch; //a unique index to accelerate shadowmesh generation (dlights, yay!)
unsigned int ebobatch; //
unsigned int webobatch; //su
};
struct
{
@ -166,6 +167,11 @@ typedef struct batch_s
unsigned int surf_count;
};
vec4_t plane; /*used only at load (for portal surfaces, so multiple planes are not part of the same batch)*/
struct
{
mesh_t meshbuf;
mesh_t *meshptr;
};
};
} batch_t;
/*

View file

@ -1365,7 +1365,7 @@ void R_Clear (qboolean fbo)
{
/*tbh, this entire function should be in the backend*/
{
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); //FIXME: breaks backend!
if (!depthcleared || fbo)
{
GL_ForceDepthWritable();

View file

@ -532,58 +532,4 @@ void GLBE_GenBrushModelVBO(model_t *mod)
#endif
}
void GLBE_UploadAllLightmaps(void)
{
lightmapinfo_t *lm;
int i;
//
// upload all lightmaps that were filled
//
for (i=0 ; i<numlightmaps ; i++)
{
if (!lightmap[i])
break; // no more used
lm = lightmap[i];
lm->rectchange.l = lm->width;
lm->rectchange.t = lm->height;
lm->rectchange.r = 0;
lm->rectchange.b = 0;
if (!lm->modified)
continue;
lm->modified = false;
if (!TEXVALID(lm->lightmap_texture))
TEXASSIGN(lm->lightmap_texture, Image_CreateTexture(va("***lightmap %i***", i), NULL, (r_lightmap_nearest.ival?IF_NEAREST:IF_LINEAR)|IF_NOMIPMAP));
if (!lm->lightmap_texture->num)
qglGenTextures(1, &lm->lightmap_texture->num);
GL_MTBind(0, GL_TEXTURE_2D, lm->lightmap_texture);
if (r_lightmap_nearest.ival)
{
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
else
{
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
qglTexImage2D(GL_TEXTURE_2D, 0, gl_config.formatinfo[lm->fmt].internalformat, lm->width, lm->height, 0, gl_config.formatinfo[lm->fmt].format, gl_config.formatinfo[lm->fmt].type, lightmap[i]->lightmaps);
if (gl_config.glversion >= (gl_config.gles?3.0:3.3))
{
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, gl_config.formatinfo[lm->fmt].swizzle_r);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, gl_config.formatinfo[lm->fmt].swizzle_g);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, gl_config.formatinfo[lm->fmt].swizzle_b);
qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, gl_config.formatinfo[lm->fmt].swizzle_a);
}
//for completeness.
lm->lightmap_texture->format = lm->fmt;
lm->lightmap_texture->width = lm->width;
lm->lightmap_texture->height = lm->height;
lm->lightmap_texture->depth = 1;
lm->lightmap_texture->status = TEX_LOADED;
}
}
#endif

View file

@ -161,7 +161,13 @@ qboolean GLSCR_UpdateScreen (void)
if (r_clear.ival)
{
GL_ForceDepthWritable();
qglClearColor((r_clear.ival&1)?1:0, (r_clear.ival&2)?1:0, (r_clear.ival&4)?1:0, 1);
if (r_clearcolour.ival)
{
r_clearcolour.vec4[0] = host_basepal[(r_clearcolour.ival & 0xFF)*3+0]/255.0;
r_clearcolour.vec4[1] = host_basepal[(r_clearcolour.ival & 0xFF)*3+1]/255.0;
r_clearcolour.vec4[2] = host_basepal[(r_clearcolour.ival & 0xFF)*3+2]/255.0;
}
qglClearColor(r_clearcolour.vec4[0], r_clearcolour.vec4[1], r_clearcolour.vec4[2], 1);
qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
depthcleared = true;
}

View file

@ -3854,7 +3854,7 @@ rendererinfo_t openglrendererinfo = {
GLBE_Init,
GLBE_GenBrushModelVBO,
GLBE_ClearVBO,
GLBE_UploadAllLightmaps,
GLBE_UpdateLightmaps,
GLBE_SelectEntity,
GLBE_SelectDLight,
GLBE_Scissor,

View file

@ -4447,7 +4447,7 @@ rendererinfo_t eglrendererinfo =
GLBE_Init,
GLBE_GenBrushModelVBO,
GLBE_ClearVBO,
GLBE_UploadAllLightmaps,
GLBE_UpdateLightmaps,
GLBE_SelectEntity,
GLBE_SelectDLight,
GLBE_Scissor,

View file

@ -1697,7 +1697,7 @@ rendererinfo_t rendererinfo_wayland_gl =
GLBE_Init,
GLBE_GenBrushModelVBO,
GLBE_ClearVBO,
GLBE_UploadAllLightmaps,
GLBE_UpdateLightmaps,
GLBE_SelectEntity,
GLBE_SelectDLight,
GLBE_Scissor,

View file

@ -52,6 +52,18 @@ void R_SkyShutdown(void)
forcedsky = NULL;
}
//lets the backend know which fallback envmap it can use.
texid_t R_GetDefaultEnvmap(void)
{
if (*r_refdef.nearenvmap.texname)
return Image_GetTexture(r_refdef.nearenvmap.texname, NULL, IF_TEXTYPE_CUBE, NULL, NULL, 0, 0, TF_INVALID);
if (forcedsky && TEXLOADED(forcedsky->defaulttextures->reflectcube))
return forcedsky->defaulttextures->reflectcube;
return r_nulltex;
}
void R_SetSky(const char *sky)
{
int i;

View file

@ -319,7 +319,7 @@ extern vec3_t r_origin;
//
extern refdef_t r_refdef;
extern unsigned int r_viewcontents;
int r_viewarea;
extern int r_viewarea;
extern int r_viewcluster, r_viewcluster2, r_oldviewcluster, r_oldviewcluster2; //q2
extern texture_t *r_notexture_mip;
@ -330,8 +330,6 @@ extern const char *gl_vendor;
extern const char *gl_renderer;
extern const char *gl_version;
FTE_DEPRECATED void PPL_RevertToKnownState(void);
qboolean R_CullBox (vec3_t mins, vec3_t maxs);
qboolean R_CullEntityBox(entity_t *e, vec3_t modmins, vec3_t modmaxs);
qboolean R_CullSphere (vec3_t origin, float radius);
@ -735,7 +733,16 @@ extern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);
#define GLintptr qintptr_t
#define GLsizeiptr quintptr_t
#ifndef GL_MAP_READ_BIT
#define GL_MAP_READ_BIT 1
#define GL_MAP_READ_BIT 0x0001
#endif
#ifndef GL_MAP_WRITE_BIT
#define GL_MAP_WRITE_BIT 0x0002
#endif
#ifndef GL_MAP_PERSISTENT_BIT
#define GL_MAP_PERSISTENT_BIT 0x0040
#endif
#ifndef GL_MAP_COHERENT_BIT
#define GL_MAP_COHERENT_BIT 0x0080
#endif
extern void *(APIENTRY *qglMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);

View file

@ -857,7 +857,7 @@ void GLBE_SubmitBatch(batch_t *batch);
batch_t *GLBE_GetTempBatch(void);
void GLBE_GenBrushModelVBO(model_t *mod);
void GLBE_ClearVBO(vbo_t *vbo, qboolean dataonly);
void GLBE_UploadAllLightmaps(void);
void GLBE_UpdateLightmaps(void);
void GLBE_DrawWorld (batch_t **worldbatches);
qboolean GLBE_LightCullModel(vec3_t org, model_t *model);
void GLBE_SelectEntity(entity_t *ent);

View file

@ -196,7 +196,7 @@ static void PF_fmem_unlink(progfuncs_t *progfuncs, qcmemfreeblock_t *p)
}
}
static void PR_memvalidate (progfuncs_t *progfuncs)
void PR_memvalidate (progfuncs_t *progfuncs)
{
qcmemfreeblock_t *p;
unsigned int b,l;

View file

@ -363,7 +363,7 @@ pbool PDECL ED_CanFree (edict_t *ed)
ed->v->solid = 0;
ed->xv->pvsflags = 0;
#ifdef QUAKETC
#ifndef HAVE_LEGACY
//ideal world...
ed->v->nextthink = 0;
ed->v->think = 0;
@ -1492,7 +1492,9 @@ static void PR_SSProfile_f(void)
static void PR_SSPoke_f(void)
{
if (!SV_MayCheat())
if (MSV_ForwardToAutoServer())
;
else if (!SV_MayCheat())
Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n");
else if (svprogfuncs && svprogfuncs->EvaluateDebugString)
Con_TPrintf("Result: %s\n", svprogfuncs->EvaluateDebugString(svprogfuncs, Cmd_Args()));
@ -4211,7 +4213,7 @@ static void QCBUILTIN PF_sv_getlight (pubprogfuncs_t *prinst, struct globalvars_
}
}
#ifndef QUAKETC
#ifdef HAVE_LEGACY
/*
=========
PF_conprint
@ -4670,6 +4672,15 @@ static void QCBUILTIN PF_walkmove (pubprogfuncs_t *prinst, struct globalvars_s *
// pr_xfunction = oldf;
pr_global_struct->self = oldself;
}
static void QCBUILTIN PF_walkmovedist (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ //for wrath, doesn't actually provide anything useful other than to stop crashes.
wedict_t *ent = PROG_TO_WEDICT(prinst, pr_global_struct->self);
vec3_t start;
VectorCopy(ent->v->origin, start);
PF_walkmove(prinst, pr_globals);
VectorSubtract(ent->v->origin, start, start);
G_FLOAT(OFS_RETURN) = VectorLength(start);
}
void QCBUILTIN PF_applylightstyle(int style, const char *val, vec3_t rgb)
{
@ -5707,7 +5718,7 @@ static void QCBUILTIN PF_WriteString2 (pubprogfuncs_t *prinst, struct globalvars
G_FLOAT(OFS_PARM1) = old;
}
#if !defined(QUAKETC) && defined(NETPREPARSE)
#if defined(HAVE_LEGACY) && defined(NETPREPARSE)
//qtest-only builtins.
static void QCBUILTIN PF_qtSingle_WriteByte (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
@ -6468,7 +6479,7 @@ static void QCBUILTIN PF_Ignore(pubprogfuncs_t *prinst, struct globalvars_s *pr_
G_INT(OFS_RETURN) = 0;
}
#ifndef QUAKETC
#ifdef HAVE_LEGACY
static void QCBUILTIN PF_mvdsv_newstring(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) //mvdsv
{
char *s;
@ -7115,7 +7126,7 @@ void QCBUILTIN PF_ExecuteCommand (pubprogfuncs_t *prinst, struct globalvars_s *
pr_global_struct->other = old_other;
}
#ifndef QUAKETC
#ifdef HAVE_LEGACY
/*
=================
PF_teamfield
@ -10502,7 +10513,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"altstr_ins", PF_Fixme, 0, 0, 0, 86, D("string(string str, float num, string set)", NULL), true},
{"findflags", PF_Fixme, 0, 0, 0, 87, "entity(entity start, .float field, float match)"},
{"findchainflags", PF_Fixme, 0, 0, 0, 88, "entity(.float field, float match)"},
{"mcvar_defstring", PF_Fixme, 0, 0, 0, 89, "string(string name)" STUB},
{"cvar_defstring", PF_Fixme, 0, 0, 0, 89, "string(string name)"},
{"setmodel", PF_Fixme, 0, 0, 0, 90, D("void(entity ent, string mname)", "Menuqc-specific version.")},
{"precache_model", PF_Fixme, 0, 0, 0, 91, D("void(string mname)", "Menuqc-specific version.")},
@ -10585,7 +10596,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"WriteString", PF_WriteString, 58, 58, 58, 0, D("void(float to, string val)", "Writes a variable-length null terminated string. There are length limits. The codepage is not translated, so be sure that client+server agree on whether utf-8 is being used or not (or just stick to ascii+markup).")}, //58
{"WriteEntity", PF_WriteEntity, 59, 59, 59, 0, D("void(float to, entity val)", "Writes the index of the specified entity (the network data size is not specified). This can be read clientside using the readentitynum builtin, with caveats.")}, //59
#if !defined(QUAKETC) && defined(NETPREPARSE)
#if defined(HAVE_LEGACY) && defined(NETPREPARSE)
{"swritebyte", PF_qtSingle_WriteByte, 0, 0, 0, 0, D("void(float val)", "A legacy of qtest - like WriteByte, except writes explicitly to the MSG_ONE target."), true}, //52
{"swritechar", PF_qtSingle_WriteChar, 0, 0, 0, 0, D("void(float val)", NULL), true}, //53
{"swriteshort", PF_qtSingle_WriteShort, 0, 0, 0, 0, D("void(float val)", NULL), true}, //54
@ -10656,7 +10667,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
"void(vector where, float set)", "Once the MSG_MULTICAST network message buffer has been filled with data, this builtin is used to dispatch it to the given target, filtering by pvs for reduced network bandwidth.")}, //82
#ifndef QUAKETC
#ifdef HAVE_LEGACY
//mvdsv (don't require ebfs usage in qw)
{"executecommand", PF_ExecuteCommand, 0, 0, 0, 83, D("void()","Attempt to flush the localcmd buffer NOW. This is unsafe, as many events might cause the map to be purged while still executing qc code."), true},
{"mvdtokenize", PF_tokenize_console,0, 0, 0, 84, D("void(string str)",NULL), true},
@ -10982,10 +10993,10 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"frametoname", PF_frametoname, 0, 0, 0, 284, "string(float modidx, float framenum)"},
{"skintoname", PF_skintoname, 0, 0, 0, 285, "string(float modidx, float skin)"},
{"resourcestatus", PF_resourcestatus, 0, 0, 0, 286, D("float(float resourcetype, float tryload, string resourcename)", "resourcetype must be one of the RESTYPE_ constants. Returns one of the RESSTATE_ constants. Tryload 0 is a query only. Tryload 1 will attempt to reload the content if it was flushed.")},
{"hash_createtab", PF_hash_createtab, 0, 0, 0, 287, D("hashtable(float tabsize, optional float defaulttype)", "Creates a hash table object with at least 'tabsize' slots. hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return).")},
{"hash_createtab", PF_hash_createtab, 0, 0, 0, 287, D("hashtable(float tabsize, optional float defaulttype)", "Creates a hash table object.\nThe tabsize argument is a performance hint and should generally be set to something similar to the number of entries expected, typically a power of two assumption. Too high simply wastes memory, too low results in extra string compares but no actual bugs.\ndefaulttype must be one of the EV_* values, if specified.\nThe hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return).")},
{"hash_destroytab", PF_hash_destroytab, 0, 0, 0, 288, D("void(hashtable table)", "Destroys a hash table object.")},
{"hash_add", PF_hash_add, 0, 0, 0, 289, D("void(hashtable table, string name, __variant value, optional float typeandflags)", "Adds the given key with the given value to the table.\nIf flags&HASH_REPLACE, the old value will be removed, if not set then multiple values may be added for a single key, they won't overwrite.\nThe type argument describes how the value should be stored and saved to files. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games.")},
{"hash_get", PF_hash_get, 0, 0, 0, 290, D("__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index)", "looks up the specified key name in the hash table. returns deflt if key was not found. If stringsonly=1, the return value will be in the form of a tempstring, otherwise it'll be the original value argument exactly as it was. If requiretype is specified, then values not of the specified type will be ignored. Hurrah for multiple types with the same name.")},
{"hash_add", PF_hash_add, 0, 0, 0, 289, D("void(hashtable table, string name, __variant value, optional float typeandflags)", "Adds the given key with the given value to the table.\nIf flags&HASH_REPLACE, the old value will be removed, otherwise if flags&HASH_ADD then a duplicate entry will be added with a second value (can be obtained via hash_get's index argument).\nThe type argument describes how the value should be stored in saved games, as well as providing constraints with the hash_get function. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games - be sure to be explicit with EV_STRING where appropriate because tempstrings may be reclaimed before the get (especially with saved games or table 0).")},
{"hash_get", PF_hash_get, 0, 0, 0, 290, D("__variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index)","Looks up the specified key name in the hash table. Returns deflt if the key was not found.\nIf requiretype is specified then the function will only consider entries of the matching type (allowing you to store both flags+strings under a single name without getting confused).\nIf index is specified then the function will ignore the first N entries with the same key (applicable only with entries added using HASH_ADD, not HASH_REPLACE), allowing you to store multiple entries. Keep querying higher indexes starting from 0 until it returns the deflt value.\nYou will usually need to cast the result of this function to a real datatype.")},
{"hash_delete", PF_hash_delete, 0, 0, 0, 291, D("__variant(hashtable table, string name)", "removes the named key. returns the value of the object that was destroyed, or 0 on error.")},
{"hash_getkey", PF_hash_getkey, 0, 0, 0, 292, D("string(hashtable table, float idx)", "gets some random key name. add+delete can change return values of this, so don't blindly increment the key index if you're removing all.")},
{"hash_getcb", PF_hash_getcb, 0, 0, 0, 293, D("void(hashtable table, void(string keyname, __variant val) callback, optional string name)", "For each item in the table that matches the name, call the callback. if name is omitted, will enumerate ALL keys."), true},
@ -11150,6 +11161,8 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"cvars_haveunsaved",PF_Fixme, 0, 0, 0, 0, D("float()", "Returns true if any archived cvar has an unsaved value.")},
{"entityprotection",PF_entityprotection,0, 0, 0, 0, D("float(entity e, float nowreadonly)", "Changes the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea).")},
{"getlocationname", PF_Fixme, 0, 0, 0, 0, D("string(vector pos)", "Looks up the specified position in the current map's .loc file and reports the nearest marked name.")},
//end fte extras
//DP extras
@ -11409,7 +11422,8 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"addwantedhostcachekey",PF_Fixme, 0, 0, 0, 623, "void(string key)"},
{"getextresponse", PF_Fixme, 0, 0, 0, 624, "string()"},
{"netaddress_resolve",PF_netaddress_resolve,0, 0, 0, 625, "string(string dnsname, optional float defport)"},
{"getgamedirinfo", PF_Fixme, 0, 0, 0, 626, "string(float n, float prop)" STUB},
{"getgamedirinfo", PF_Fixme, 0, 0, 0, 626, "string(float n, float prop)"},
{"getpackagemanagerinfo",PF_Fixme, 0, 0, 0, 0, D("string(int n, int prop)", "Queries information about a package from the engine's package manager subsystem. Actions can be taken via the pkg console command.")},
{"sprintf", PF_sprintf, 0, 0, 0, 627, D("string(string fmt, ...)", "'prints' to a formatted temp-string. Mostly acts as in C, however %d assumes floats (fteqcc has arg checking. Use it.).\ntype conversions: l=arg is an int, h=arg is a float, and will work as a prefix for any float or int representation.\nfloat representations: d=decimal, e,E=exponent-notation, f,F=floating-point notation, g,G=terse float, c=char code, x,X=hex\nother representations: i=int, s=string, S=quoted and marked-up string, v=vector, p=pointer\nso %ld will accept an int arg, while %hi will expect a float arg.\nentities, fields, and functions will generally need to be printed as ints with %i.")},
{"getsurfacenumtriangles",PF_getsurfacenumtriangles,0,0,0, 628, "float(entity e, float s)"},
{"getsurfacetriangle",PF_getsurfacetriangle,0, 0, 0, 629, "vector(entity e, float s, float n)"},
@ -11421,12 +11435,13 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"crypto_getencryptlevel",PF_Fixme, 0, 0, 0, 635, "string(string addr)" STUB},
{"crypto_getmykeyfp",PF_Fixme, 0, 0, 0, 636, "string(string addr)" STUB},
{"crypto_getmyidfp",PF_Fixme, 0, 0, 0, 637, "string(float addr)" STUB},
// {"VM_CL_RotateMoves",PF_Fixme, 0, 0, 0, 638, ""},
// {"CL_RotateMoves", PF_Fixme, 0, 0, 0, 638, D("void(vector anglechange)", "Rewrites the input log history to rotate all unacknowledged frames according to the angle delta specified.")},
{"digest_hex", PF_digest_hex, 0, 0, 0, 639, "string(string digest, string data, ...)"},
{"digest_ptr", PF_digest_ptr, 0, 0, 0, 0, D("string(string digest, void *data, int length)", "Calculates the digest of a single contiguous block of memory (including nulls) using the specified hash function.")},
// {"V_CalcRefdef", PF_Fixme, 0, 0, 0, 640, "void(entity e)"},
{"V_CalcRefdef", PF_Fixme, 0, 0, 0, 640, "void(entity e, float flags)" STUB},
{"crypto_getmyidstatus",PF_Fixme, 0, 0, 0, 641, "float(float i)" STUB},
{"coverage", PF_Fixme, 0, 0, 0, 642, "void()" STUB},
{"crypto_getidstatus",PF_Fixme, 0, 0, 0, 643, "float(string addr)" STUB},
//end dp extras
//wrath extras...
@ -11435,6 +11450,7 @@ static BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"fremove", PF_fremove, 0, 0, 0, 652, D("float(string fname)", "Deletes the named file - path is relative to data/ subdir, like fopen's FILE_WRITE. Returns 0 on success.")},
{"fexists", PF_fexists, 0, 0, 0, 653, D("float(string fname)", "Use whichpack instead. Returns true if it exists inside the default writable path.")},
{"rmtree", PF_rmtree, 0, 0, 0, 654, D("float(string path)", "Dangerous, but sandboxed to data/")},
{"walkmovedist", PF_walkmovedist, 0, 0, 0, 655, D("float(float yaw, float dist, optional float settraceglobals)", "Attempt to walk the entity at a given angle for a given distance.\nif settraceglobals is set, the trace_* globals will be set, showing the results of the movement.\nThis function will trigger touch events."), true},
//end wrath extras
{"getrmqeffectsversion",PF_Ignore, 0, 0, 0, 666, "float()" STUB},
@ -11590,7 +11606,7 @@ void PR_ResetBuiltins(progstype_t type) //fix all nulls to PF_FIXME and add any
builtincount[i]=100;
}
#if !defined(QUAKETC) && defined(NETPREPARSE)
#if defined(HAVE_LEGACY) && defined(NETPREPARSE)
if (type == PROG_PREREL)
{
pr_builtin[52] = PF_qtSingle_WriteByte;
@ -12120,6 +12136,10 @@ void PR_DumpPlatform_f(void)
{"trace_triangle_id", "int", QW|NQ|CS, D("1-based. 0 if not known.")},
{"trace_networkentity", "int", CS, D("Repots which ssqc entnum was hit when a csqc traceline impacts an ssqc-based brush entity.")},
{"pmove_org", "vector", CS, D("Reports the origin of the engineside player (after prediction). Does not work when the player is a csqc-owned entity.")},
{"pmove_vel", "vector", CS, D("Reports the velocity of the engineside player (after prediction). Does not work when the player is a csqc-owned entity.")},
{"pmove_onground", "float", CS, D("Reports the onground state of the engineside player (after prediction). Does not work when the player is a csqc-owned entity.")},
{"global_gravitydir", "vector", QW|NQ|CS, D("The direction gravity should act in if not otherwise specified per entity."), 0,"'0 0 -1'"},
{"serverid", "int", QW|NQ|CS, D("The unique id of this server within the server cluster.")},
@ -12259,6 +12279,7 @@ void PR_DumpPlatform_f(void)
{"SOLID_CORPSE", "const float", QW|NQ|CS, D("Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .solid value to SOLID_BBOX or so, perform the traceline, then revert the player's .solid value."), SOLID_CORPSE},
{"SOLID_LADDER", "const float", QW|NQ|CS, D("Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead."), SOLID_LADDER},
{"SOLID_PORTAL", "const float", QW|NQ|CS, D("CSG subtraction volume combined with entity transformations on impact."), SOLID_PORTAL},
{"SOLID_BSPTRIGGER", "const float", QW|NQ|CS, D("For complex-shaped trigger volumes, instead of being a pure aabb."), SOLID_BSPTRIGGER},
{"SOLID_PHYSICS_BOX", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_BOX},
{"SOLID_PHYSICS_SPHERE", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_SPHERE},
{"SOLID_PHYSICS_CAPSULE", "const float", QW|NQ|CS, NULL, SOLID_PHYSICS_CAPSULE},
@ -12385,7 +12406,7 @@ void PR_DumpPlatform_f(void)
//not putting other svcs here, qc shouldn't otherwise need to generate svcs directly.
{"SVC_CGAMEPACKET", "const float", QW|NQ, D("Direct ssqc->csqc message. Must only be multicast. The data triggers a CSQC_Parse_Event call in the csqc for the csqc to read the contents. The server *may* insert length information for clients connected via proxies which are not able to cope with custom csqc payloads. This should only ever be used in conjunction with the MSG_MULTICAST destination."), svcfte_cgamepacket},
#ifndef QUAKETC
#ifdef HAVE_LEGACY
{"MSG_BROADCAST", "const float", QW|NQ, D("The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family."), MSG_BROADCAST},
{"MSG_ONE", "const float", QW|NQ, D("The byte(s) will be reliably sent to the player specified in the msg_entity global. WARNING: in quakeworld servers without network preparsing enabled, this can result in illegible server messages (due to individual reliable messages being split between multiple backbuffers/packets). NQ has larger reliable buffers which avoids this issue, but still kicks the client."), MSG_ONE},
{"MSG_ALL", "const float", QW|NQ, D("The byte(s) will be reliably sent to all players."), MSG_ALL},
@ -12676,6 +12697,13 @@ void PR_DumpPlatform_f(void)
{"IE_JOYAXIS", "const float", CS|MENU, D("Specifies that what value a joystick/controller axis currently specifies. x=axis, y=value. Will be called multiple times, once for each axis of each active controller."), CSIE_JOYAXIS},
{"IE_GYROSCOPE", "const float", CS|MENU, NULL, CSIE_GYROSCOPE},
{"GGDI_GAMEDIR", "const float", CS|MENU, D("Used with getgamedirinfo to query the mod's public gamedir. There is often other info that cannot be expressed with just a gamedir name, resulting in dupes or other weirdness."), GGDI_GAMEDIR},
{"GGDI_DESCRIPTION", "const float", CS|MENU, D("The human-readable title of the mod. Empty when no data is known (ie: the gamedir just contains some maps)."), GGDI_DESCRIPTION},
{"GGDI_OVERRIDES", "const float", CS|MENU, D("A list of settings overrides."), GGDI_OVERRIDES},
{"GGDI_LOADCOMMAND", "const float", CS|MENU, D("The console command needed to actually load the mod."), GGDI_LOADCOMMAND},
{"GGDI_ICON", "const float", CS|MENU, D("The mod's Icon path, ready for drawpic."), GGDI_ICON},
{"GGDI_GAMEDIRLIST", "const float", CS|MENU, D("A semi-colon delimited list of gamedirs that the mod's content can be loaded through."), GGDI_ICON},
{"CLIENTTYPE_DISCONNECTED","const float", QW|NQ, D("Return value from clienttype() builtin. This entity is a player slot that is currently empty."), CLIENTTYPE_DISCONNECTED},
{"CLIENTTYPE_REAL", "const float", QW|NQ, D("This is a real player, and not a bot."), CLIENTTYPE_REAL},
{"CLIENTTYPE_BOT", "const float", QW|NQ, D("This player slot does not correlate to a real player, any messages sent to this client will be ignored."), CLIENTTYPE_BOT},

File diff suppressed because it is too large Load diff

View file

@ -618,8 +618,8 @@ typedef struct client_s
//true/false/persist
unsigned int penalties;
qbyte istobeloaded; //loadgame creates place holders for clients to connect to. Effectivly loading a game reconnects all clients, but has precreated ents.
qboolean spawned; //the player's entity was spawned.
qbyte istobeloaded; //spawnparms are known.
qboolean spawned; //gamecode knows about it.
double floodprotmessage;
double lastspoke;
@ -1227,6 +1227,10 @@ typedef struct pubsubserver_s
netadr_t addrv4;
netadr_t addrv6;
char printtext[4096]; //to split it into lines.
qboolean started;
#ifdef HAVE_CLIENT
console_t *console;
#endif
} pubsubserver_t;
extern qboolean isClusterSlave;
void SSV_UpdateAddresses(void);
@ -1244,12 +1248,12 @@ void Sys_InstructMaster(sizebuf_t *cmd); //first two bytes will always be the le
#define SSV_IsSubServer() isClusterSlave
void MSV_SubServerCommand_f(void);
void MSV_SubServerCommand_f(void);
void MSV_MapCluster_f(void);
void SSV_Send(const char *dest, const char *src, const char *cmd, const char *msg);
qboolean MSV_ClusterLogin(svconnectinfo_t *info);
void MSV_PollSlaves(void);
qboolean MSV_ForwardToAutoServer(void); //forwards console command to a default subserver. ie: whichever one our client is on.
void MSV_Status(void);
void MSV_OpenUserDatabase(void);
#else
@ -1257,12 +1261,14 @@ void MSV_OpenUserDatabase(void);
#define MSV_ClusterLogin(info) false
#define SSV_IsSubServer() false
#define MSV_OpenUserDatabase()
#define MSV_PollSlaves() false
#define MSV_ForwardToAutoServer() false
#endif
//
// sv_init.c
//
void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic);
void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic, int playerslots);
void SV_UnspawnServer (void);
void SV_FlushSignon (qboolean force);
void SV_UpdateMaxPlayers(int newmax);
@ -1356,7 +1362,8 @@ void SV_VoiceSendPacket(client_t *client, sizebuf_t *buf);
#endif
void SV_ClientThink (void);
void SV_Begin_Core(client_t *split);
void SV_Begin_Core(client_t *split); //sets up the player's gamecode state
void SV_DespawnClient(client_t *cl); //shuts down the gamecode state.
void VoteFlushAll(void);
void SV_SetUpClientEdict (client_t *cl, edict_t *ent);
@ -1651,6 +1658,7 @@ int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *heade
// savegame.c
void SV_Savegame_f (void);
void SV_DeleteSavegame_f (void);
void SV_Savegame_c(int argn, const char *partial, struct xcommandargcompletioncb_s *ctx);
void SV_Loadgame_f (void);
qboolean SV_Loadgame (const char *unsafe_savename);

View file

@ -36,6 +36,9 @@ qboolean SV_MayCheat(void)
return sv_allow_cheats!=0;
}
#ifdef SUBSERVERS
cvar_t sv_autooffload = CVARD("sv_autooffload", "0", "Automatically start the server in a separate process, so that sporadic or persistent gamecode slowdowns do not affect visual framerates. Note: Offloaded servers have separate cvar states which may complicate usage.");
#endif
extern cvar_t cl_warncmd;
cvar_t sv_cheats = CVARF("sv_cheats", "0", CVAR_LATCH);
extern redirect_t sv_redirected;
@ -557,6 +560,12 @@ void SV_Map_f (void)
}
#endif
#ifdef SUBSERVERS
//disconnect first if you want to stop your current server getting the command instead.
if (sv.state == ss_clustermode && MSV_ForwardToAutoServer())
return;
#endif
if (!Q_strcasecmp(Cmd_Argv(0), "map_restart"))
{
const char *arg = Cmd_Argv(1);
@ -754,13 +763,21 @@ void SV_Map_f (void)
SCR_SetLoadingStage(LS_NONE);
#endif
if (SSV_IsSubServer())
if (SSV_IsSubServer() && !sv.state) //subservers don't leave defunct servers with no maps lying around.
Cbuf_AddText("\nquit\n", RESTRICT_LOCAL);
return;
}
}
}
#ifdef SUBSERVERS
if (!isDedicated && sv_autooffload.ival && !sv.state && !SSV_IsSubServer() && !strcmp(Cmd_Argv(0), "map") && Cmd_Argc()==2)
{
Cmd_ExecuteString(va("mapcluster \"%s\"", Cmd_Argv(1)), Cmd_ExecLevel);
return;
}
#endif
#ifdef MVD_RECORDING
if (sv.mvdrecording)
SV_MVDStop_f();
@ -914,7 +931,7 @@ void SV_Map_f (void)
{
if (waschangelevel && !startspot)
startspot = "";
SV_SpawnServer (level, startspot, false, cinematic);
SV_SpawnServer (level, startspot, false, cinematic, 0);
}
SCR_SetLoadingFile("server spawned");
@ -3181,6 +3198,9 @@ void SV_InitOperatorCommands (void)
Cmd_AddCommand ("download", SV_Download_f);
}
#ifdef SUBSERVERS
Cvar_Register(&sv_autooffload, "server control variables");
#endif
Cvar_Register(&sv_cheats, "Server Permissions");
if (COM_CheckParm ("-cheats"))
{

View file

@ -94,6 +94,15 @@ static void MSV_ServerCrashed(pubsubserver_t *server)
link_t *l, *next;
clusterplayer_t *pl;
#ifdef HAVE_CLIENT
if (server->console)
{
Con_PrintCon(server->console, "<Server Ended>\n", server->console->flags);
Q_snprintfz(server->console->title, sizeof(server->console->title), "SERVER DEAD");
server->console->userdata = NULL; //forget about us! for we are no more!
}
#endif
//forget any players that are meant to be on this server.
for (l = clusterplayers.next ; l != &clusterplayers ; l = next)
{
@ -101,7 +110,7 @@ static void MSV_ServerCrashed(pubsubserver_t *server)
pl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers);
if (pl->server == server)
{
Con_Printf("%s(%s) crashed out\n", pl->name, server->name);
Con_Printf("%s's node crashed out (%s)\n", pl->name, server->name);
RemoveLink(&pl->allplayers);
Z_Free(pl);
}
@ -145,6 +154,35 @@ static int MSV_Loop_Read(pubsubserver_t *ps)
return 1;
}
static void MSV_SendCvars(pubsubserver_t *s)
{
extern cvar_t skill, sv_nqplayerphysics, sv_pure, sv_minpitch, sv_maxpitch;
cvar_t *cvars[] = {
&developer,
&deathmatch, &coop, &skill, &teamplay,
&nomonsters, &gamecfg, &noexit, &temp1,
&scratch1, &scratch2, &scratch3, &scratch4,
&saved1, &saved2, &saved3, &saved4, &savedgamecfg,
&sv_nqplayerphysics, &sv_pure, &sv_mintic, &sv_maxtic,
&sv_minpitch, &sv_maxpitch};
sizebuf_t send;
char send_buf[8192];
size_t v;
memset(&send, 0, sizeof(send));
send.data = send_buf;
send.maxsize = sizeof(send_buf);
for (v = 0; v < countof(cvars); v++)
{
send.cursize = 2;
MSG_WriteByte(&send, ccmd_setcvar);
MSG_WriteString(&send, cvars[v]->name);
MSG_WriteString(&send, cvars[v]->string);
s->funcs.InstructSlave(s, &send);
}
}
static void MSV_Link_Server(pubsubserver_t *s, int id, const char *mapname)
{
sizebuf_t send;
@ -159,6 +197,8 @@ static void MSV_Link_Server(pubsubserver_t *s, int id, const char *mapname)
if (mapname)
{
MSV_SendCvars(s);
Q_strncpyz(s->name, mapname, sizeof(s->name));
memset(&send, 0, sizeof(send));
@ -258,20 +298,25 @@ qboolean MSV_AddressForServer(netadr_t *ret, int natype, pubsubserver_t *s)
return false;
}
void MSV_InstructSlave(unsigned int id, sizebuf_t *cmd)
qboolean MSV_InstructSlave(unsigned int id, sizebuf_t *cmd)
{
pubsubserver_t *s;
if (!id)
{
for (s = subservers; s; s = s->next)
s->funcs.InstructSlave(s, cmd);
return subservers?true:false;
}
else
{
s = MSV_FindSubServer(id);
if (s)
{
s->funcs.InstructSlave(s, cmd);
return true;
}
}
return false;
}
void SV_SetupNetworkBuffers(qboolean bigcoords);
@ -351,7 +396,7 @@ void MSV_Status(void)
clusterplayer_t *pl;
for (s = subservers; s; s = s->next)
{
Con_Printf("%i: %s", s->id, s->name);
Con_Printf("^[%i: %s\\ssv\\%u^]", s->id, s->name, s->id);
if (s->addrv4.type != NA_INVALID)
Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4));
if (s->addrv6.type != NA_INVALID)
@ -362,13 +407,111 @@ void MSV_Status(void)
FOR_EACH_LINK(l, clusterplayers)
{
pl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers);
Con_Printf("%i(%s): (%s) %s (%s)\n", pl->playerid, pl->server->name, pl->guid, pl->name, pl->address);
Con_Printf("^[%i(%s)\\ssv\\%u^]: (%s) %s (%s)\n", pl->playerid, pl->server->name, pl->server->id, pl->guid, pl->name, pl->address);
}
}
#ifdef HAVE_CLIENT
static int MSV_SubConsole_LineBuffered(console_t *con, const char *utf8line)
{
pubsubserver_t *s = con->userdata;
if (s)
{
sizebuf_t buf;
char bufmem[65536];
Con_PrintCon(con, va("]%s\n", utf8line), PFS_FORCEUTF8|PFS_NONOTIFY);
if (!strcmp(utf8line, "clear"))
{
Con_ClearCon(con);
return true;
}
buf.data = bufmem;
buf.maxsize = sizeof(bufmem);
buf.cursize = 2;
buf.packing = SZ_RAWBYTES;
MSG_WriteByte(&buf, ccmd_stuffcmd);
MSG_WriteString(&buf, utf8line); //FIXME: is utf-8 a problem?
buf.data[0] = buf.cursize & 0xff;
buf.data[1] = (buf.cursize>>8) & 0xff;
s->funcs.InstructSlave(s, &buf);
}
else
Con_Footerf(con, false, "< Unable to send >");
return true;
}
static qboolean MSV_SubConsole_Close (console_t *con, qboolean force)
{ //force=true is the final close, the rest are merely queries to see if its save.
pubsubserver_t *s = con->userdata;
if (force && s)
{ //stop prints from this server from going here.
s->console = NULL;
}
return true;
}
static void MSV_SubConsole_Update(pubsubserver_t *s)
{
if (s->console)
{
if (s->console->flags & CONF_ISWINDOW)
Q_snprintfz(s->console->title, sizeof(s->console->title), "%u:%s", s->id, s->name);
else
Q_snprintfz(s->console->title, sizeof(s->console->title), "Server %u: %s", s->id, s->name);
}
}
static void MSV_SubConsole_Show(pubsubserver_t *s, qboolean show)
{
console_t *con = s->console;
if (!con)
{
for (con = con_head; con; con = con->next)
{
if (con->close == MSV_SubConsole_Close && !con->userdata)
break;
}
if (!con)
{
con = Con_Create(NULL, CONF_NOTIFY);
if (0)//con)
{
/*make it a console window thing*/
con->flags |= CONF_ISWINDOW;
con->wnd_x = 0;
con->wnd_y = 0;
con->wnd_w = vid.width/2;
con->wnd_h = vid.height/2;
}
}
if (con)
{
s->console = con;
MSV_SubConsole_Update(s);
con->parseflags = PFS_FORCEUTF8;
con->userdata = s;
con->linebuffered = MSV_SubConsole_LineBuffered;
// con->redirect = Con_Editor_Key;
con->close = MSV_SubConsole_Close;
con->maxlines = 0x7fffffff; //line limit is effectively unbounded, for a 31-bit process.
//use the server's status command as a header.
if (show)
MSV_SubConsole_LineBuffered(con, "status");
}
}
if (con && show)
Con_SetActive(con);
}
#else
#define MSV_SubConsole_Update(s)
#endif
void MSV_SubServerCommand_f(void)
{
sizebuf_t buf;
char bufmem[1024];
char bufmem[65536];
pubsubserver_t *s;
int id;
char *c;
@ -377,7 +520,7 @@ void MSV_SubServerCommand_f(void)
Con_Printf("Active servers on this cluster:\n");
for (s = subservers; s; s = s->next)
{
Con_Printf("%i: %s %i+%i", s->id, s->name, s->activeplayers, s->transferingplayers);
Con_Printf("^[%i: %s %i+%i\\ssv\\%u^]", s->id, s->name, s->activeplayers, s->transferingplayers, s->id);
if (s->addrv4.type != NA_INVALID)
Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4));
if (s->addrv6.type != NA_INVALID)
@ -388,6 +531,15 @@ void MSV_SubServerCommand_f(void)
}
if (!strcmp(Cmd_Argv(0), "ssv_all"))
id = 0;
#ifdef HAVE_CLIENT
else if (Cmd_Argc() == 2)
{ //subservers, meet subconsoles
s = MSV_FindSubServer(atoi(Cmd_Argv(1)));
if (s)
MSV_SubConsole_Show(s, true);
return;
}
#endif
else
{
id = atoi(Cmd_Argv(1));
@ -403,22 +555,90 @@ void MSV_SubServerCommand_f(void)
MSG_WriteString(&buf, c);
buf.data[0] = buf.cursize & 0xff;
buf.data[1] = (buf.cursize>>8) & 0xff;
MSV_InstructSlave(id, &buf);
if (!MSV_InstructSlave(id, &buf))
Con_Printf("No node for index.\n");
}
qboolean MSV_ForwardToAutoServer(void)
{
pubsubserver_t *s;
if (sv.state > ss_clustermode)
return false; //don't forward if we have our own server.
if (subservers && !subservers->next && sv.state == ss_clustermode)
s = subservers; //there is only one.
else
{
s = NULL;
#ifdef HAVE_CLIENT
if (!s && cls.state >= ca_connected)
{ //find the one the local player is currently on.
for (; s; s = s->next)
{
if (s->addrv6.type!=NA_INVALID && NET_CompareAdr(&s->addrv4, &cls.netchan.remote_address))
break;
if (s->addrv6.type!=NA_INVALID && NET_CompareAdr(&s->addrv6, &cls.netchan.remote_address))
break;
}
}
#endif
if (!s)
return false;
}
{
sizebuf_t buf;
char bufmem[65536];
const char *cmd = Cmd_Argv(0);
const char *args = Cmd_Args();;
buf.data = bufmem;
buf.maxsize = sizeof(bufmem);
buf.cursize = 2;
buf.packing = SZ_RAWBYTES;
MSG_WriteByte(&buf, ccmd_stuffcmd);
SZ_Write(&buf, cmd, strlen(cmd));
if(*args)
MSG_WriteChar(&buf, ' ');
MSG_WriteString(&buf, args);
buf.data[0] = buf.cursize & 0xff;
buf.data[1] = (buf.cursize>>8) & 0xff;
s->funcs.InstructSlave(s, &buf);
return true;
}
}
static void MSV_PrintFromSubServer(pubsubserver_t *s, const char *newtext)
{
char *nl;
#ifdef HAVE_CLIENT
if (!s->console)
{
// extern cvar_t con_window;
// if (con_window.ival) //might as well pop one up.
MSV_SubConsole_Show(s, false);
}
if (s->console)
{
if (*s->printtext)
{ //flush it if there was something buffered there...
Con_PrintCon(s->console, s->printtext, s->console->flags);
*s->printtext = 0;
}
Con_PrintCon(s->console, newtext, s->console->flags);
return;
}
#endif
Q_strncatz(s->printtext, newtext, sizeof(s->printtext));
while((nl = strchr(s->printtext, '\n')))
{ //FIXME: handle overflows.
*nl++ = 0;
Con_Printf("^6%i(%s)^7: %s\n", s->id, s->name, s->printtext);
Con_Printf("^[^6%i(%s)\\ssv\\%u^]: %s\n", s->id, s->name, s->id, s->printtext);
memmove(s->printtext, nl, strlen(nl)+1);
}
if (strlen(s->printtext) > sizeof(s->printtext)/2)
{
Con_Printf("^6%i(%s)^7: %s\n", s->id, s->name, s->printtext);
Con_Printf("^[^6%i(%s)\\ssv\\%u^]: %s\n", s->id, s->name, s->id, s->printtext);
*s->printtext = 0;
}
}
@ -510,6 +730,51 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
}
}
break;
case ccmd_foundplayer:
{
char guid[64];
char plnamebuf[64];
char *plname = MSG_ReadStringBuffer(plnamebuf, sizeof(plnamebuf));
char *claddr = MSG_ReadString();
char *clguid = MSG_ReadStringBuffer(guid, sizeof(guid));
extern int nextuserid;
const unsigned int statsblobsize = 0;
const void *statsblob = NULL;
sizebuf_t send;
qbyte send_buf[MAX_QWMSGLEN];
clusterplayer_t *pl;
if (sv.logindatabase)
break; //if we're using a login database then this could be used as an exploit.
memset(&send, 0, sizeof(send));
send.data = send_buf;
send.maxsize = sizeof(send_buf);
send.cursize = 2;
pl = Z_Malloc(sizeof(*pl));
Q_strncpyz(pl->name, plname, sizeof(pl->name));
Q_strncpyz(pl->guid, clguid, sizeof(pl->guid));
Q_strncpyz(pl->address, claddr, sizeof(pl->address));
pl->playerid = ++nextuserid;
InsertLinkBefore(&pl->allplayers, &clusterplayers);
pl->server = s;
s->activeplayers++;
MSG_WriteByte(&send, ccmd_takeplayer);
MSG_WriteLong(&send, pl->playerid);
MSG_WriteString(&send, pl->name);
MSG_WriteLong(&send, 0); //from server
MSG_WriteString(&send, pl->address);
MSG_WriteString(&send, pl->guid);
MSG_WriteByte(&send, statsblobsize/4);
SZ_Write(&send, statsblob, statsblobsize&~3);
s->funcs.InstructSlave(s, &send);
}
break;
case ccmd_transferplayer:
{ //server is offering a player to another server
char guid[64];
@ -639,8 +904,13 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
}
}
}
MSV_SubConsole_Update(s);
if (s->started)
Con_DPrintf("^[^6[%i:%s: map changed]\\ssv\\%u\\tip\\Click for server's console^]\n", s->id, s->name, s->id);
else
Con_Printf("^[^6[%i:%s: new node initialised]\\ssv\\%u\\tip\\Click for server's console^]\n", s->id, s->name, s->id);
s->started = true;
}
Con_Printf("%i:%s: restarted\n", s->id, s->name);
break;
case ccmd_stringcmd:
{
@ -770,6 +1040,15 @@ void SSV_ReadFromControlServer(void)
SV_EndRedirect();
break;
case ccmd_setcvar:
{
cvar_t *var = Cvar_FindVar(MSG_ReadString());
const char *val = MSG_ReadString();
Con_Printf("Setting cvar \"%s\" to \"%s\"\n", var?var->name:"UNKNOWN", val);
Cvar_Set(var, val);
}
break;
//cluster has 'accepted' us as an allowed server. this is where it tells us who we're meant to be, which needs to be set up ready for the players that are (probably) about to join us
case ccmd_acceptserver:
svs.clusterserverid = MSG_ReadLong();

View file

@ -3443,14 +3443,13 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli
state->effects |= EF_GREEN;
}
}
else
if (state->number <= sv.allocated_client_slots) // clear only client ents
{
if (state->effects & NQEF_NODRAW)
state->modelindex = 0;
}
if (state->number <= sv.allocated_client_slots) // clear only client ents
state->effects &= ~ (QWEF_FLAG1|QWEF_FLAG2);
}
if ((state->effects & EF_DIMLIGHT) && !(state->effects & (EF_RED|EF_BLUE)))
{
@ -3771,9 +3770,10 @@ void SV_Snapshot_BuildQ1(client_t *client, packet_entities_t *pack, pvscamera_t
}
//QSG_DIMENSION_PLANES
if (client->edict)
if (!((int)client->edict->xv->dimension_see & ((int)ent->xv->dimension_seen | (int)ent->xv->dimension_ghost)))
continue; //not in this dimension - sorry...
if (clent) //don't crash
if (!((int)clent->xv->dimension_see & ((int)ent->xv->dimension_seen | (int)ent->xv->dimension_ghost))) //not able to see it.
if (c >= maxc) //always network the player entity though
continue;
if (cameras && tracecullent && !((unsigned int)ent->v->effects & (EF_DIMLIGHT|EF_BLUE|EF_RED|EF_BRIGHTLIGHT|EF_BRIGHTFIELD|EF_NODEPTHTEST)))

View file

@ -694,6 +694,10 @@ void SV_UpdateMaxPlayers(int newmax)
}
for (i = 0; i < min(newmax, svs.allocated_client_slots); i++)
{
if (svs.clients[i].name == old[i].namebuf)
svs.clients[i].name = svs.clients[i].namebuf;
if (svs.clients[i].team == old[i].teambuf)
svs.clients[i].team = svs.clients[i].teambuf;
if (svs.clients[i].netchan.message.data)
svs.clients[i].netchan.message.data = (qbyte*)&svs.clients[i] + (svs.clients[i].netchan.message.data - (qbyte*)&old[i]);
if (svs.clients[i].datagram.data)
@ -823,7 +827,7 @@ clients along with it.
This is only called from the SV_Map_f() function.
================
*/
void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic)
void SV_SpawnServer (const char *server, const char *startspot, qboolean noents, qboolean usecinematic, int playerslots)
{
extern cvar_t allow_download_refpackages;
func_t f;
@ -1331,6 +1335,8 @@ MSV_OpenUserDatabase();
i = QWMAX_CLIENTS;
}
}
if (playerslots)
i = playerslots; //saved game? force it.
if (i > MAX_CLIENTS)
i = MAX_CLIENTS;
SV_UpdateMaxPlayers(i);
@ -1376,7 +1382,7 @@ MSV_OpenUserDatabase();
#endif
#ifdef Q3SERVER
case GT_QUAKE3:
SV_UpdateMaxPlayers(max(8,maxclients.ival));
SV_UpdateMaxPlayers(playerslots?playerslots:max(8,maxclients.ival));
break;
#endif
#ifdef HLSERVER

View file

@ -86,7 +86,8 @@ extern cvar_t password;
#endif
cvar_t spectator_password = CVARF("spectator_password", "", CVAR_NOUNSAFEEXPAND); // password for entering as a sepctator
cvar_t allow_download = CVARAD("allow_download", "1", /*q3*/"sv_allowDownload", "If 1, permits downloading. Set to 0 to unconditionally block *ALL* downloads.");
static cvar_t sv_dlURL = CVARFD(/*ioq3*/"sv_dlURL", "", CVAR_SERVERINFO|CVAR_ARCHIVE, "Provides clients with an external url from which they can obtain pk3s/packages from an external http server instead of having to download over udp.");
cvar_t allow_download = CVARAD("allow_download", "1", /*q3*/"sv_allowDownload", "If 1, permits downloading. Set to 0 to unconditionally block *ALL* downloads from this server. You may wish to set sv_dlURL if you wish clients to still be able to download content.");
cvar_t allow_download_skins = CVARD("allow_download_skins", "1", "0 blocks downloading of any file in the skins/ directory");
cvar_t allow_download_models = CVARD("allow_download_models", "1", "0 blocks downloading of any file in the progs/ or models/ directory");
cvar_t allow_download_sounds = CVARD("allow_download_sounds", "1", "0 blocks downloading of any file in the sound/ directory");
@ -295,7 +296,7 @@ void SV_Shutdown (void)
#endif
Mod_Shutdown(true);
#ifdef PACKAGEMANAGER
PM_Shutdown();
PM_Shutdown(false);
#endif
COM_DestroyWorkerThread();
FS_Shutdown();
@ -570,43 +571,7 @@ void SV_DropClient (client_t *drop)
case GT_PROGS:
if (svprogfuncs)
{
if (drop->spawned && host_initialized)
{
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
{
Q1QVM_DropClient(drop);
}
else
#endif
{
if (!drop->spectator)
{
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, drop->edict);
if (pr_global_ptrs->ClientDisconnect)
PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect);
sv.spawned_client_slots--;
}
else
{
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, drop->edict);
if (SpectatorDisconnect)
PR_ExecuteProgram (svprogfuncs, SpectatorDisconnect);
sv.spawned_observer_slots--;
}
}
if (progstype == PROG_NQ)
ED_Clear(svprogfuncs, drop->edict);
}
drop->spawned = false;
if (svprogfuncs && drop->edict && drop->edict->v)
drop->edict->v->frags = 0;
SV_DespawnClient(drop);
drop->edict = NULL;
if (drop->spawninfo)
@ -2657,9 +2622,29 @@ void SV_DoDirectConnect(svconnectinfo_t *fte_restrict info)
{
if (SSV_IsSubServer())
{
SV_RejectMessage (info->protocol, "Direct connections are not permitted.\n");
Con_TPrintf ("* rejected direct connection\n");
return;
if (1)
{
sizebuf_t s;
qbyte send[8192];
memset(&s, 0, sizeof(s));
s.data = send;
s.maxsize = sizeof(send);
s.cursize = 2;
MSG_WriteByte(&s, ccmd_foundplayer);
MSG_WriteString(&s, name);
MSG_WriteString(&s, NET_AdrToString (adrbuf, sizeof(adrbuf), &info->adr));
MSG_WriteString(&s, info->guid);
SSV_InstructMaster(&s);
return;
}
else
{
SV_RejectMessage (info->protocol, "Direct connections are not permitted.\n");
Con_TPrintf ("* rejected direct connection\n");
return;
}
}
/*single player logic*/
@ -5436,6 +5421,7 @@ void SV_InitLocal (void)
Cvar_Register (&filterban, cvargroup_servercontrol);
Cvar_Register (&sv_dlURL, cvargroup_serverpermissions);
Cvar_Register (&allow_download, cvargroup_serverpermissions);
Cvar_Register (&allow_download_skins, cvargroup_serverpermissions);
Cvar_Register (&allow_download_models, cvargroup_serverpermissions);
@ -5497,6 +5483,7 @@ void SV_InitLocal (void)
Cmd_AddCommandAD ("loadgame", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
Cmd_AddCommandAD ("save", SV_Savegame_f, SV_Savegame_c, "Saves the game to the named location.");
Cmd_AddCommandAD ("load", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
Cmd_AddCommandAD ("unsavegame", SV_DeleteSavegame_f, SV_Savegame_c, "Wipes an existing saved game from disk.");
#endif
#ifdef MVD_RECORDING
@ -6028,12 +6015,7 @@ void SV_Init (quakeparms_t *parms)
manarg = COM_CheckParm("-manifest");
if (manarg && manarg < com_argc-1 && com_argv[manarg+1])
{
char *man = FS_MallocFile(com_argv[manarg+1], FS_SYSTEM, NULL);
FS_ChangeGame(FS_Manifest_Parse(NULL, man), true, true);
BZ_Free(man);
}
FS_ChangeGame(FS_Manifest_ReadSystem(com_argv[manarg+1], NULL), true, true);
else
FS_ChangeGame(NULL, true, true);

View file

@ -1288,12 +1288,7 @@ void SV_Init (struct quakeparms_s *parms)
manarg = COM_CheckParm("-manifest");
if (manarg && manarg < com_argc-1 && com_argv[manarg+1])
{
char *man = FS_MallocFile(com_argv[manarg+1], FS_SYSTEM, NULL);
FS_ChangeGame(FS_Manifest_Parse(NULL, man), true, true);
BZ_Free(man);
}
FS_ChangeGame(FS_Manifest_ReadSystem(com_argv[manarg+1], NULL), true, true);
else
FS_ChangeGame(NULL, true, true);

View file

@ -59,6 +59,7 @@ cvar_t sv_gameplayfix_multiplethinks = CVARD( "sv_gameplayfix_multiplethinks",
cvar_t sv_gameplayfix_stepdown = CVARD( "sv_gameplayfix_stepdown", "0", "Attempt to step down steps, instead of only up them. Affects non-predicted movetype_walk.");
cvar_t sv_gameplayfix_bouncedownslopes = CVARD( "sv_gameplayfix_grenadebouncedownslopes", "0", "MOVETYPE_BOUNCE speeds are calculated relative to the impacted surface, instead of the vertical, reducing the chance of grenades just sitting there on slopes.");
cvar_t sv_gameplayfix_trappedwithin = CVARD( "sv_gameplayfix_trappedwithin", "0", "Blocks further entity movement when an entity is already inside another entity. This ensures that bsp precision issues cannot allow the entity to completely pass through eg the world.");
//cvar_t sv_gameplayfix_radialmaxvelocity = CVARD( "sv_gameplayfix_radialmaxvelocity", "0", "Applies maxvelocity radially instead of axially.");
#if !defined(CLIENTONLY) && defined(NQPROT) && defined(HAVE_LEGACY)
cvar_t sv_gameplayfix_spawnbeforethinks = CVARD( "sv_gameplayfix_spawnbeforethinks", "0", "Fixes an issue where player thinks (including Pre+Post) can be called before PutClientInServer. Unfortunately at least one mod depends upon PreThink being called first in order to correctly determine spawn positions.");
#endif
@ -150,28 +151,50 @@ SV_CheckVelocity
void WPhys_CheckVelocity (world_t *w, wedict_t *ent)
{
int i;
extern cvar_t sv_nqplayerphysics;
//
// bound velocity
//
for (i=0 ; i<3 ; i++)
{
if (IS_NAN(ent->v->velocity[i]))
if (sv_nqplayerphysics.ival)
{ //bound axially (like vanilla)
for (i=0 ; i<3 ; i++)
{
Con_DPrintf ("Got a NaN velocity on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname));
ent->v->velocity[i] = 0;
}
if (IS_NAN(ent->v->origin[i]))
{
Con_Printf ("Got a NaN origin on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname));
ent->v->origin[i] = 0;
if (IS_NAN(ent->v->velocity[i]))
{
Con_DPrintf ("Got a NaN velocity on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname));
ent->v->velocity[i] = 0;
}
if (IS_NAN(ent->v->origin[i]))
{
Con_Printf ("Got a NaN origin on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname));
ent->v->origin[i] = 0;
}
if (ent->v->velocity[i] > sv_maxvelocity.value)
ent->v->velocity[i] = sv_maxvelocity.value;
else if (ent->v->velocity[i] < -sv_maxvelocity.value)
ent->v->velocity[i] = -sv_maxvelocity.value;
}
}
else
{ //bound radially (for sanity)
for (i=0 ; i<3 ; i++)
{
if (IS_NAN(ent->v->velocity[i]))
{
Con_DPrintf ("Got a NaN velocity on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname));
ent->v->velocity[i] = 0;
}
if (IS_NAN(ent->v->origin[i]))
{
Con_Printf ("Got a NaN origin on entity %i (%s)\n", ent->entnum, PR_GetString(w->progs, ent->v->classname));
ent->v->origin[i] = 0;
}
}
if (Length(ent->v->velocity) > sv_maxvelocity.value)
{
// Con_DPrintf("Slowing %s\n", PR_GetString(w->progs, ent->v->classname));
VectorScale (ent->v->velocity, sv_maxvelocity.value/Length(ent->v->velocity), ent->v->velocity);
if (Length(ent->v->velocity) > sv_maxvelocity.value)
{
// Con_DPrintf("Slowing %s\n", PR_GetString(w->progs, ent->v->classname));
VectorScale (ent->v->velocity, sv_maxvelocity.value/Length(ent->v->velocity), ent->v->velocity);
}
}
}

View file

@ -1457,13 +1457,14 @@ void SV_StartSound (int ent, vec3_t origin, float *velocity, int seenmask, int c
void QDECL SVQ1_StartSound (float *origin, wedict_t *wentity, int channel, const char *sample, int volume, float attenuation, float pitchadj, float timeofs, unsigned int chflags)
{
edict_t *entity = (edict_t*)wentity;
int i;
int i, solid;
vec3_t originbuf;
float *velocity = NULL;
if (!origin)
{
origin = originbuf;
if (entity->v->solid == SOLID_BSP)
solid = entity->v->solid;
if (solid == SOLID_BSP || solid == SOLID_BSPTRIGGER)
{
for (i=0 ; i<3 ; i++)
origin[i] = entity->v->origin[i]+0.5*(entity->v->mins[i]+entity->v->maxs[i]);

View file

@ -1890,7 +1890,7 @@ void SVQW_Spawn_f (void)
{
ent = split->edict;
if (split->istobeloaded) //minimal setup
if (split->spawned) //minimal setup
{
split->entgravity = ent->xv->gravity;
split->maxspeed = ent->xv->maxspeed;
@ -1984,6 +1984,62 @@ void SV_SpawnSpectator (void)
}
}
void SV_DespawnClient(client_t *cl)
{ //this disconnects the client from its entity state
if (!cl->spawned)
return; //nothing to do.
cl->spawned = false;
#ifdef Q2SERVER
if (ge)
{
ge->ClientDisconnect(cl->q2edict);
return;
}
#endif
if (svprogfuncs)
{
if (host_initialized)
{
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
{
Q1QVM_DropClient(cl);
}
else
#endif
{
if (!cl->spectator)
{
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);
if (pr_global_ptrs->ClientDisconnect)
PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect);
sv.spawned_client_slots--;
}
else
{
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, cl->edict);
if (SpectatorDisconnect)
PR_ExecuteProgram (svprogfuncs, SpectatorDisconnect);
sv.spawned_observer_slots--;
}
}
if (progstype == PROG_NQ)
ED_Clear(svprogfuncs, cl->edict);
}
if (svprogfuncs && cl->edict && cl->edict->v)
cl->edict->v->frags = 0;
}
}
void SV_Begin_Core(client_t *split)
{ //this is the client-protocol-independant core, for q1/q2 gamecode
client_t *oh;
@ -1993,7 +2049,28 @@ void SV_Begin_Core(client_t *split)
#endif
if (split->spawned)
{
//NEH_RESTOREGAME
//officially RestoreGame tells the mod when the game has been loaded.
//this allows mods to send any stuffcmds the client will have forgotten.
//the original intention would not have been client-specific (and indeed nehahra only saves in singleplayer)
//doing it elsewhere unfortunately results in race conditions.
func_t f = PR_FindFunction(svprogfuncs, "RestoreGame", PR_ANY);
if (f)
{
pr_global_struct->time = sv.world.physicstime;
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict);
PR_ExecuteProgram (svprogfuncs, f);
}
ClientReliableWrite_Begin(split, svc_setangle, 8);
if (host_client->ezprotocolextensions1 & EZPEXT1_SETANGLEREASON)
ClientReliableWrite_Byte (host_client, 0);
ClientReliableWrite_Angle (host_client, split->edict->v->v_angle[0]);
ClientReliableWrite_Angle (host_client, split->edict->v->v_angle[1]);
ClientReliableWrite_Angle (host_client, 0); //roll angle is messy with cl_roll. we don't want to be stuck rolling.
return;
}
split->spawned = true;
#ifdef Q2SERVER
@ -2010,20 +2087,6 @@ void SV_Begin_Core(client_t *split)
}
else
#endif
if (split->istobeloaded)
{
func_t f;
split->istobeloaded = false;
f = PR_FindFunction(svprogfuncs, "RestoreGame", PR_ANY);
if (f)
{
pr_global_struct->time = sv.world.physicstime;
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, split->edict);
PR_ExecuteProgram (svprogfuncs, f);
}
}
else
{
#ifdef HAVE_LEGACY
split->edict->xv->clientcolors = split->playercolor;
@ -5349,17 +5412,7 @@ void Cmd_Join_f (void)
if (!host_client->spectator)
continue;
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
Q1QVM_DropClient(host_client);
else
#endif
if (SpectatorDisconnect)
PR_ExecuteProgram (svprogfuncs, SpectatorDisconnect);
sv.spawned_observer_slots--;
SV_DespawnClient(host_client);
SV_SetUpClientEdict (host_client, host_client->edict);
@ -5483,18 +5536,8 @@ void Cmd_Observe_f (void)
if (host_client->spectator)
continue;
// call the prog function for removing a client
// this will set the body to a dead frame, among other things
#ifdef VM_Q1
if (svs.gametype == GT_Q1QVM)
Q1QVM_DropClient(host_client);
else
#endif
{
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, sv_player);
PR_ExecuteProgram (svprogfuncs, pr_global_struct->ClientDisconnect);
}
sv.spawned_client_slots--;
SV_DespawnClient(host_client);
SV_SetUpClientEdict (host_client, host_client->edict);
@ -5630,9 +5673,10 @@ void SV_EnableClientsCSQC(void)
host_client->csqcactive = true;
//if the csqc has just restarted, its probably going to want us to resend all csqc ents from scratch because of all the setup it might do.
for (e = 1; e < host_client->max_net_ents; e++)
if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)
host_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;
if (host_client->pendingcsqcbits)
for (e = 1; e < host_client->max_net_ents; e++)
if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)
host_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;
}
void SV_DisableClientsCSQC(void)
{
@ -6603,7 +6647,7 @@ static qboolean AddEntityToPmove(world_t *w, wedict_t *player, wedict_t *check)
pe->forcecontentsmask = 0;
break;
}
if (solid == SOLID_PORTAL || solid == SOLID_BSP)
if (solid == SOLID_PORTAL || solid == SOLID_BSP || solid == SOLID_BSPTRIGGER)
{
if(progstype != PROG_H2)
pe->angles[0]*=r_meshpitch.value; //quake is wierd. I guess someone fixed it hexen2... or my code is buggy or something...
@ -8099,8 +8143,16 @@ void SV_ExecuteClientMessage (client_t *cl)
#endif
case clcdp_ackframe:
cl->delta_sequence = MSG_ReadLong();
if (cl->delta_sequence == -1 && cl->pendingdeltabits)
cl->pendingdeltabits[0] = UF_REMOVE;
if (cl->delta_sequence == -1)
{
unsigned int e;
if (cl->pendingdeltabits)
cl->pendingdeltabits[0] = UF_REMOVE;
if (host_client->pendingcsqcbits)
for (e = 1; e < host_client->max_net_ents; e++)
if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)
host_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;
}
SV_AckEntityFrame(cl, cl->delta_sequence);
break;
}
@ -8617,8 +8669,16 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
case clcdp_ackframe:
cl->delta_sequence = MSG_ReadLong();
if (cl->delta_sequence == -1 && cl->pendingdeltabits)
cl->pendingdeltabits[0] = UF_REMOVE;
if (cl->delta_sequence == -1)
{
unsigned int e;
if (cl->pendingdeltabits)
cl->pendingdeltabits[0] = UF_REMOVE;
if (host_client->pendingcsqcbits)
for (e = 1; e < host_client->max_net_ents; e++)
if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)
host_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;
}
SV_AckEntityFrame(cl, cl->delta_sequence);
// if (cl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].sequence == cl->delta_sequence)
// if (cl->frameunion.frames[cl->delta_sequence&UPDATE_MASK].ping_time < 0)

View file

@ -31,6 +31,8 @@ line of sight checks trace->crosscontent, but bullets don't
*/
#define SOLID_ISTRIGGER(solid) ((solid)==SOLID_TRIGGER||(solid)==SOLID_BSPTRIGGER||(solid)==SOLID_LADDER)
size_t areagridsequence; //used to avoid poking the same ent twice.
extern cvar_t sv_compatiblehulls;
@ -445,7 +447,7 @@ void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node)
if (touch == ent)
continue;
if (!touch->v->touch || touch->v->solid != SOLID_TRIGGER)
if (!touch->v->touch || !SOLID_ISTRIGGER(touch->v->solid))
continue;
if (ent->v->absmin[0] > touch->v->absmax[0]
@ -469,7 +471,7 @@ void World_TouchLinks (world_t *w, wedict_t *ent, areanode_t *node)
//make sure nothing moved it away
if (ED_ISFREE(touch))
continue;
if (!touch->v->touch || touch->v->solid != SOLID_TRIGGER)
if (!touch->v->touch || !SOLID_ISTRIGGER(touch->v->solid))
continue;
if (ent->v->absmin[0] > touch->v->absmax[0]
@ -511,6 +513,7 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers)
{
vec_t *mins;
vec_t *maxs;
int solid;
#ifdef USEAREAGRID
World_UnlinkEdict (ent); // unlink from old position
@ -549,7 +552,8 @@ void QDECL World_LinkEdict (world_t *w, wedict_t *ent, qboolean touch_triggers)
}
// set the abs box
if (ent->v->solid == SOLID_BSP &&
solid = ent->v->solid;
if ((solid == SOLID_BSP||solid == SOLID_BSPTRIGGER) &&
(ent->v->angles[0] || ent->v->angles[1] || ent->v->angles[2]) )
{ // expand for rotation
#if 1
@ -1186,7 +1190,7 @@ wedict_t *World_TestEntityPosition (world_t *w, wedict_t *ent)
{
trace_t trace;
trace = World_Move (w, ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, ((ent->v->solid == SOLID_NOT || ent->v->solid == SOLID_TRIGGER)?MOVE_NOMONSTERS:0), ent);
trace = World_Move (w, ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, ((ent->v->solid == SOLID_NOT || ent->v->solid == SOLID_TRIGGER || ent->v->solid == SOLID_BSPTRIGGER)?MOVE_NOMONSTERS:0), ent);
if (trace.startsolid || trace.allsolid)
return trace.ent?trace.ent:w->edicts;
@ -1268,7 +1272,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
framestate_t framestate;
// get the clipping hull
if ((ent->v->solid == SOLID_BSP || ent->v->solid == SOLID_PORTAL) && mdlidx)
if ((ent->v->solid == SOLID_BSP || ent->v->solid == SOLID_BSPTRIGGER || ent->v->solid == SOLID_PORTAL) && mdlidx)
{
model = w->Get_CModel(w, mdlidx);
if (!model || (model->type != mod_brush && model->type != mod_heightmap))
@ -1309,7 +1313,7 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
trace.startsolid = false;
hitmodel = false;
}
else if (ent->v->solid != SOLID_BSP)
else if (ent->v->solid != SOLID_BSP && ent->v->solid != SOLID_BSPTRIGGER)
{
eang[0]*=r_meshpitch.value; //carmack made bsp models rotate wrongly.
World_TransformedTrace(model, hullnum, &framestate, start, end, mins, maxs, capsule, &trace, eorg, eang, hitcontentsmask);
@ -1408,7 +1412,7 @@ int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int
if (check->v->solid == SOLID_NOT)
continue; // deactivated
if ((check->v->solid == SOLID_TRIGGER) != (areatype == AREA_TRIGGER))
if ((check->v->solid == SOLID_TRIGGER||check->v->solid == SOLID_BSPTRIGGER) != (areatype == AREA_TRIGGER))
continue;
}
@ -1449,7 +1453,7 @@ int World_AreaEdicts (world_t *w, vec3_t mins, vec3_t maxs, wedict_t **list, int
if (check->v->solid == SOLID_NOT)
continue; // deactivated
if ((check->v->solid == SOLID_TRIGGER) != (areatype == AREA_TRIGGER))
if ((check->v->solid == SOLID_TRIGGER||check->v->solid == SOLID_BSPTRIGGER) != (areatype == AREA_TRIGGER))
continue;
}
@ -1500,7 +1504,7 @@ static void World_AreaEdicts_r (areanode_t *node)
continue; // deactivated
/*q2 still has solid/trigger lists, emulate that here*/
if ((check->v->solid == SOLID_TRIGGER) != (area_type == AREA_TRIGGER))
if ((check->v->solid == SOLID_TRIGGER||check->v->solid == SOLID_BSPTRIGGER) != (area_type == AREA_TRIGGER))
continue;
if (check->v->absmin[0] > area_maxs[0]
@ -1841,7 +1845,7 @@ static void World_ClipToEverything (world_t *w, moveclip_t *clip)
continue;
if (touch->v->solid == SOLID_NOT && !((int)touch->v->flags & FL_FINDABLE_NONSOLID))
continue;
if (touch->v->solid == SOLID_TRIGGER && !((int)touch->v->flags & FL_FINDABLE_NONSOLID))
if ((touch->v->solid == SOLID_TRIGGER||touch->v->solid == SOLID_BSPTRIGGER) && !((int)touch->v->flags & FL_FINDABLE_NONSOLID))
continue;
if (touch == clip->passedict)
@ -1919,7 +1923,7 @@ static void World_ClipToEverything (world_t *w, moveclip_t *clip)
void World_TouchAllLinks (world_t *w, wedict_t *ent)
{
wedict_t *touchedicts[2048], *touch;
int num;
int num, solid;
num = World_AreaEdicts(w, ent->v->absmin, ent->v->absmax, touchedicts, countof(touchedicts), AREA_TRIGGER);
while (num-- > 0)
{
@ -1928,7 +1932,8 @@ void World_TouchAllLinks (world_t *w, wedict_t *ent)
//make sure nothing moved it away
if (ED_ISFREE(touch))
continue;
if (!touch->v->touch || touch->v->solid != SOLID_TRIGGER)
solid =touch->v->solid;
if (!touch->v->touch || (solid!= SOLID_TRIGGER && solid!= SOLID_BSPTRIGGER))
continue;
if (touch == ent)
continue;
@ -1944,6 +1949,12 @@ void World_TouchAllLinks (world_t *w, wedict_t *ent)
if (!((int)ent->xv->dimension_solid & (int)touch->xv->dimension_hit)) //didn't change did it?...
continue;
if (solid == SOLID_BSPTRIGGER)
{
if (!World_ClipMoveToEntity(w, touch, touch->v->origin, touch->v->angles, ent->v->origin, ent->v->mins, ent->v->maxs, ent->v->origin, 0, false, (ent->xv->geomtype == GEOMTYPE_CAPSULE), MASK_WORLDSOLID).startsolid)
continue;
}
w->Event_Touch(w, touch, ent, NULL);
if (ED_ISFREE(ent))
@ -1985,7 +1996,7 @@ static void World_ClipToLinks (world_t *w, areagridlink_t *node, moveclip_t *cli
continue;
/*if its a trigger, we only clip against it if the flags are aligned*/
if (touch->v->solid == SOLID_TRIGGER || touch->v->solid == SOLID_LADDER)
if (SOLID_ISTRIGGER(touch->v->solid))
{
if (!(clip->type & MOVE_TRIGGERS))
continue;
@ -2218,7 +2229,7 @@ static void World_ClipToLinks (world_t *w, areanode_t *node, moveclip_t *clip)
continue;
/*if its a trigger, we only clip against it if the flags are aligned*/
if (touch->v->solid == SOLID_TRIGGER || touch->v->solid == SOLID_LADDER)
if (SOLID_ISTRIGGER(touch->v->solid))
{
if (!(clip->type & MOVE_TRIGGERS))
continue;
@ -2351,7 +2362,7 @@ static unsigned int World_ContentsOfLinks (world_t *w, areanode_t *node, vec3_t
continue;
/*if its a trigger, we only clip against it if the flags are aligned*/
if (touch->v->solid == SOLID_TRIGGER)
if (touch->v->solid == SOLID_TRIGGER||touch->v->solid == SOLID_BSPTRIGGER)
continue;
if (pos[0] > touch->v->absmax[0]
@ -2723,7 +2734,7 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
}
else if (passedict->v->solid == SOLID_CORPSE)
clip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY; //corpses ignore corpses
else if (passedict->v->solid == SOLID_TRIGGER)
else if (passedict->v->solid == SOLID_TRIGGER||passedict->v->solid == SOLID_BSPTRIGGER)
clip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY; //triggers ignore corpses too, apparently
else
clip.hitcontentsmask = FTECONTENTS_SOLID|Q2CONTENTS_WINDOW | FTECONTENTS_BODY | FTECONTENTS_CORPSE; //regular projectiles.
@ -2834,7 +2845,7 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
continue;
if (touch == clip.passedict)
continue;
if (touch->v->solid == SOLID_TRIGGER || touch->v->solid == SOLID_LADDER)
if (SOLID_ISTRIGGER(touch->v->solid))
{
if (!(clip.type & MOVE_TRIGGERS))
continue;