Add map selection list to menusys. Some other minor tweaks.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5743 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-08-13 10:09:39 +00:00
parent 744b9a25db
commit 51ef92fa52
21 changed files with 1013 additions and 614 deletions

View File

@ -178,7 +178,9 @@ IF(CMAKE_C_COMPILER_ID MATCHES "GNU")
#SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wc++-compat") #lul, thousands of errors!
ENDIF()
IF(CMAKE_BUILD_TYPE MATCHES "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong")
IF(NOT ${WIN32})
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong")
ENDIF()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu89")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEBUG")
ENDIF()

View File

@ -517,7 +517,7 @@ static void Prompt_Draw(struct menu_s *g)
int x = 64;
int y = 76;
float scale = Font_CharVHeight(font_console);
int w = 224*scale/8;
int w = 320*scale/8;
int h = (m->lines+3)*scale;
int i;
const char *msg = m->messages;

View File

@ -817,11 +817,11 @@ qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize,
strcpy(ctx->hostname, hostname);
if (l) //FIXME: show expiry info for the old cert, warn if more than a month?
accepttext = localtext("Replace Trust");
accepttext = localtext("Replace");
else if (!certlogproblems)
accepttext = localtext("Pin Trust");
accepttext = localtext("Pin");
else
accepttext = localtext("Trust Anyway");
accepttext = localtext("Trust");
for (i = 0, len = 0; i < countof(lines); i++)
len += strlen(lines[i]);

View File

@ -176,7 +176,7 @@ static int SSPI_CopyIntoBuffer(struct sslbuf *buf, const void *data, unsigned in
return bytes;
}
static void SSPI_Error(sslfile_t *f, char *error, ...)
static void SSPI_Error(sslfile_t *f, const char *error, ...)
{
va_list argptr;
char string[1024];
@ -186,7 +186,12 @@ static void SSPI_Error(sslfile_t *f, char *error, ...)
f->handshaking = HS_ERROR;
if (*string)
Sys_Printf("%s", string);
{
if (f->datagram)
Con_Printf(CON_ERROR "%s", string);
else
Sys_Printf(CON_ERROR "%s", string);
}
if (f->stream)
VFS_CLOSE(f->stream);
@ -391,12 +396,19 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data,
size_t knownsize;
void *knowncert;
char realdomain[256];
unsigned int probs = 0;
if (datagram)
{
if (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT)
probs |= CERTLOG_MISSINGCA;
if (status == CERT_E_EXPIRED)
probs |= CERTLOG_EXPIRED;
if (status == SEC_E_WRONG_PRINCIPAL)
probs |= CERTLOG_WRONGHOST;
if (status == CERT_E_UNTRUSTEDROOT || SUCCEEDED(status))
{
#ifndef SERVERONLY
if (CertLog_ConnectOkay(narrowen(realdomain, sizeof(realdomain), domain), data, datasize))
if (CertLog_ConnectOkay(narrowen(realdomain, sizeof(realdomain), domain), data, datasize, probs))
status = SEC_E_OK;
else
#endif
@ -427,8 +439,16 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data,
#ifndef SERVERONLY
//self-signed and expired certs are understandable in many situations.
//prompt and cache (although this connection attempt will fail).
if (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT)
probs |= CERTLOG_MISSINGCA;
else if (status == CERT_E_EXPIRED)
probs |= CERTLOG_EXPIRED;
else if (status == SEC_E_WRONG_PRINCIPAL)
probs |= CERTLOG_WRONGHOST;
else if (status != SEC_E_OK)
probs |= CERTLOG_UNKNOWN;
if (status == CERT_E_UNTRUSTEDROOT || status == CERT_E_UNTRUSTEDTESTROOT || status == CERT_E_EXPIRED)
if (CertLog_ConnectOkay(realdomain, data, datasize))
if (CertLog_ConnectOkay(realdomain, data, datasize, probs))
return SEC_E_OK;
#endif
@ -607,14 +627,14 @@ static void SSPI_GenServerCredentials(sslfile_t *f)
if (!cred)
{
SSPI_Error(f, "Unable to load/generate certificate\n");
SSPI_Error(f, localtext("Unable to load/generate certificate\n"));
return;
}
ss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_INBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime);
if (ss < 0)
{
SSPI_Error(f, "AcquireCredentialsHandle failed\n");
SSPI_Error(f, localtext("WinSSPI: AcquireCredentialsHandle failed\n"));
return;
}
}
@ -677,7 +697,7 @@ retry:
ss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime);
if (ss < 0)
{
SSPI_Error(f, "AcquireCredentialsHandle failed\n");
SSPI_Error(f, localtext("WINSSPI: AcquireCredentialsHandle failed\n"));
return;
}
@ -803,11 +823,11 @@ retry:
}
else
return;
if (ss == SEC_I_INCOMPLETE_CREDENTIALS)
{
SSPI_Error(f, "server requires credentials\n");
SSPI_Error(f, localtext("server requires credentials\n"));
return;
}
@ -851,14 +871,12 @@ retry:
ss = secur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &remotecert);
if (ss != SEC_E_OK)
{
f->handshaking = HS_ERROR;
SSPI_Error(f, "unable to read server's certificate\n");
SSPI_Error(f, localtext("unable to read server's certificate\n"));
return;
}
if (VerifyServerCertificate(remotecert, f->wpeername, 0, f->datagram))
{
f->handshaking = HS_ERROR;
SSPI_Error(f, "Error validating certificante\n");
SSPI_Error(f, localtext("Error validating certificante\n"));
return;
}
}
@ -1206,7 +1224,8 @@ static neterr_t SSPI_DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize
if (f->handshaking == HS_ERROR)
ret = NETERR_DISCONNECTED;
ret = NETERR_CLOGGED; //not ready yet
else
ret = NETERR_CLOGGED; //not ready yet
}
else
{

View File

@ -4041,7 +4041,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
if (msg_readcount+17 <= net_message.cursize && !strncmp("challengeconnect ", &net_message.data[msg_readcount], 17))
{
if (sv_showconnectionlessmessages.ival)
Con_Printf("CCREQ_CONNECT_COOKIE\n");
Con_Printf("%s: CCREQ_CONNECT_COOKIE\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
Cmd_TokenizeString(MSG_ReadStringLine(), false, false);
/*okay, so this is a reliable packet from a client, containing a 'cmd challengeconnect $challenge' response*/
str = va("connect %i %i %s \"\\name\\unconnected\\mod\\%s\\modver\\%s\\flags\\%s\\password\\%s\"", NQ_NETCHAN_VERSION, 0, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), Cmd_Argv(4), Cmd_Argv(5));
@ -4108,7 +4108,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
{
case CCREQ_CONNECT:
if (sv_showconnectionlessmessages.ival)
Con_Printf("CCREQ_CONNECT\n");
Con_Printf("%s: CCREQ_CONNECT\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
sb.maxsize = sizeof(buffer);
sb.data = buffer;
@ -4204,7 +4204,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true;
case CCREQ_SERVER_INFO:
if (sv_showconnectionlessmessages.ival)
Con_Printf("CCREQ_SERVER_INFO\n");
Con_Printf("%s: CCREQ_SERVER_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
if (sv_public.ival < 0)
return false;
if (SV_BannedReason (&net_from))
@ -4236,7 +4236,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true;
case CCREQ_PLAYER_INFO:
if (sv_showconnectionlessmessages.ival)
Con_Printf("CCREQ_PLAYER_INFO\n");
Con_Printf("%s: CCREQ_PLAYER_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
if (sv_public.ival < 0)
return false;
if (SV_BannedReason (&net_from))
@ -4270,7 +4270,7 @@ qboolean SVNQ_ConnectionlessPacket(void)
return true;
case CCREQ_RULE_INFO:
if (sv_showconnectionlessmessages.ival)
Con_Printf("CCREQ_RULE_INFO\n");
Con_Printf("%s: CCREQ_RULE_INFO\n", NET_AdrToString (com_token, sizeof(com_token), &net_from));
if (sv_public.ival < 0)
return false;
if (SV_BannedReason (&net_from))

View File

@ -58,6 +58,7 @@ static plugfsfuncs_t *fsfuncs;
#ifdef XR_NO_PROTOTYPES
#define XRFUNCS \
XRFUNC(xrGetInstanceProcAddr) \
XRFUNC(xrResultToString) \
XRFUNC(xrEnumerateInstanceExtensionProperties) \
XRFUNC(xrCreateInstance) \
XRFUNC(xrGetInstanceProperties) \
@ -302,6 +303,76 @@ static void XR_Shutdown(void)
memset(&xr, 0, sizeof(xr));
}
static const char *XR_StringForResult(XrResult res)
{
#if 0
//this is a bit of a joke really. xrResultToString requires a valid instance so is unusable for printing out the various reasons why we might fail to create an instance.
static char buffer[XR_MAX_RESULT_STRING_SIZE];
if (XR_SUCCEEDED(res=xrResultToString(xr.instance, res, buffer)))
return buffer;
return va("XrResult %i", res);
#else
switch(res)
{
case XR_SUCCESS: return "XR_SUCCESS";
case XR_TIMEOUT_EXPIRED: return "XR_TIMEOUT_EXPIRED";
case XR_SESSION_LOSS_PENDING: return "XR_SESSION_LOSS_PENDING";
case XR_EVENT_UNAVAILABLE: return "XR_EVENT_UNAVAILABLE";
case XR_SPACE_BOUNDS_UNAVAILABLE: return "XR_SPACE_BOUNDS_UNAVAILABLE";
case XR_SESSION_NOT_FOCUSED: return "XR_SESSION_NOT_FOCUSED";
case XR_FRAME_DISCARDED: return "XR_FRAME_DISCARDED";
case XR_ERROR_VALIDATION_FAILURE: return "XR_ERROR_VALIDATION_FAILURE";
case XR_ERROR_RUNTIME_FAILURE: return "XR_ERROR_RUNTIME_FAILURE";
case XR_ERROR_OUT_OF_MEMORY: return "XR_ERROR_OUT_OF_MEMORY";
case XR_ERROR_API_VERSION_UNSUPPORTED: return "XR_ERROR_API_VERSION_UNSUPPORTED";
case XR_ERROR_INITIALIZATION_FAILED: return "XR_ERROR_INITIALIZATION_FAILED";
case XR_ERROR_FUNCTION_UNSUPPORTED: return "XR_ERROR_FUNCTION_UNSUPPORTED";
case XR_ERROR_FEATURE_UNSUPPORTED: return "XR_ERROR_FEATURE_UNSUPPORTED";
case XR_ERROR_EXTENSION_NOT_PRESENT: return "XR_ERROR_EXTENSION_NOT_PRESENT";
case XR_ERROR_LIMIT_REACHED: return "XR_ERROR_LIMIT_REACHED";
case XR_ERROR_SIZE_INSUFFICIENT: return "XR_ERROR_SIZE_INSUFFICIENT";
case XR_ERROR_HANDLE_INVALID: return "XR_ERROR_HANDLE_INVALID";
case XR_ERROR_INSTANCE_LOST: return "XR_ERROR_INSTANCE_LOST";
case XR_ERROR_SESSION_RUNNING: return "XR_ERROR_SESSION_RUNNING";
case XR_ERROR_SESSION_NOT_RUNNING: return "XR_ERROR_SESSION_NOT_RUNNING";
case XR_ERROR_SESSION_LOST: return "XR_ERROR_SESSION_LOST";
case XR_ERROR_SYSTEM_INVALID: return "XR_ERROR_SYSTEM_INVALID";
case XR_ERROR_PATH_INVALID: return "XR_ERROR_PATH_INVALID";
case XR_ERROR_PATH_COUNT_EXCEEDED: return "XR_ERROR_PATH_COUNT_EXCEEDED";
case XR_ERROR_PATH_FORMAT_INVALID: return "XR_ERROR_PATH_FORMAT_INVALID";
case XR_ERROR_PATH_UNSUPPORTED: return "XR_ERROR_PATH_UNSUPPORTED";
case XR_ERROR_LAYER_INVALID: return "XR_ERROR_LAYER_INVALID";
case XR_ERROR_LAYER_LIMIT_EXCEEDED: return "XR_ERROR_LAYER_LIMIT_EXCEEDED";
case XR_ERROR_SWAPCHAIN_RECT_INVALID: return "XR_ERROR_SWAPCHAIN_RECT_INVALID";
case XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED: return "XR_ERROR_SWAPCHAIN_FORMAT_UNSUPPORTED";
case XR_ERROR_ACTION_TYPE_MISMATCH: return "XR_ERROR_ACTION_TYPE_MISMATCH";
case XR_ERROR_SESSION_NOT_READY: return "XR_ERROR_SESSION_NOT_READY";
case XR_ERROR_SESSION_NOT_STOPPING: return "XR_ERROR_SESSION_NOT_STOPPING";
case XR_ERROR_TIME_INVALID: return "XR_ERROR_TIME_INVALID";
case XR_ERROR_REFERENCE_SPACE_UNSUPPORTED: return "XR_ERROR_REFERENCE_SPACE_UNSUPPORTED";
case XR_ERROR_FILE_ACCESS_ERROR: return "XR_ERROR_FILE_ACCESS_ERROR";
case XR_ERROR_FILE_CONTENTS_INVALID: return "XR_ERROR_FILE_CONTENTS_INVALID";
case XR_ERROR_FORM_FACTOR_UNSUPPORTED: return "XR_ERROR_FORM_FACTOR_UNSUPPORTED";
case XR_ERROR_FORM_FACTOR_UNAVAILABLE: return "XR_ERROR_FORM_FACTOR_UNAVAILABLE";
case XR_ERROR_API_LAYER_NOT_PRESENT: return "XR_ERROR_API_LAYER_NOT_PRESENT";
case XR_ERROR_CALL_ORDER_INVALID: return "XR_ERROR_CALL_ORDER_INVALID";
case XR_ERROR_GRAPHICS_DEVICE_INVALID: return "XR_ERROR_GRAPHICS_DEVICE_INVALID";
case XR_ERROR_POSE_INVALID: return "XR_ERROR_POSE_INVALID";
case XR_ERROR_INDEX_OUT_OF_RANGE: return "XR_ERROR_INDEX_OUT_OF_RANGE";
case XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED: return "XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED";
case XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED: return "XR_ERROR_ENVIRONMENT_BLEND_MODE_UNSUPPORTED";
case XR_ERROR_NAME_DUPLICATED: return "XR_ERROR_NAME_DUPLICATED";
case XR_ERROR_NAME_INVALID: return "XR_ERROR_NAME_INVALID";
case XR_ERROR_ACTIONSET_NOT_ATTACHED: return "XR_ERROR_ACTIONSET_NOT_ATTACHED";
case XR_ERROR_ACTIONSETS_ALREADY_ATTACHED: return "XR_ERROR_ACTIONSETS_ALREADY_ATTACHED";
case XR_ERROR_LOCALIZED_NAME_DUPLICATED: return "XR_ERROR_LOCALIZED_NAME_DUPLICATED";
case XR_ERROR_LOCALIZED_NAME_INVALID: return "XR_ERROR_LOCALIZED_NAME_INVALID";
default:
return va("XrResult %i", res);
}
#endif
}
static qboolean XR_PreInit(vrsetup_t *qreqs)
{
XrResult res;
@ -366,6 +437,7 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
}
#endif
xr.instance = XR_NULL_HANDLE;
{
unsigned int exts = 0, u=0;
XrExtensionProperties *extlist;
@ -376,20 +448,26 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
for (u = 0; u < exts; u++)
extlist[u].type = XR_TYPE_EXTENSION_PROPERTIES;
xrEnumerateInstanceExtensionProperties(NULL, exts, &exts, extlist);
Con_Printf("OpenXR:");
for (u = 0; u < exts; u++)
Con_Printf(" %s", extlist[u].extensionName);
Con_Printf("\n");
for (u = 0; u < exts; u++)
if (!strcmp(extlist[u].extensionName, ext))
break;
free(extlist);
}
else
Con_DPrintf("OpenXR: xrEnumerateInstanceExtensionProperties failed (%s)\n", XR_StringForResult(res));
if (u == exts)
{
Con_Printf("OpenXR: instance driver does not support required %s\n", ext);
return false;
return false; //would just give an error on xrCreateInstance anyway.
}
}
xr.instance = XR_NULL_HANDLE;
//create our instance
{
XrInstanceCreateInfo createinfo = {XR_TYPE_INSTANCE_CREATE_INFO};
@ -406,14 +484,17 @@ static qboolean XR_PreInit(vrsetup_t *qreqs)
res = xrCreateInstance(&createinfo, &xr.instance);
}
if (XR_FAILED(res) || !xr.instance)
{
Con_Printf("OpenXR Runtime: xrCreateInstance failed (%s)\n", XR_StringForResult(res));
return false;
}
{
XrInstanceProperties props = {XR_TYPE_INSTANCE_PROPERTIES};
if (!XR_FAILED(xrGetInstanceProperties(xr.instance, &props)))
Con_Printf("OpenXR Runtime: %s %u.%u.%u\n", props.runtimeName, XR_VERSION_MAJOR(props.runtimeVersion), XR_VERSION_MINOR(props.runtimeVersion), XR_VERSION_PATCH(props.runtimeVersion));
else
Con_Printf("OpenXR Runtime: Unable to determine runtime version\n");
Con_Printf("OpenXR Runtime: Unable to determine runtime version (%s)\n", XR_StringForResult(res));
}
{
@ -717,7 +798,7 @@ static XrAction XR_DefineAction(XrActionType type, const char *name, const char
Q_strlcpy(info.localizedActionName, xr.actions[u].actdescription, sizeof(info.localizedActionName));
res = xrCreateAction(xr.actionset.actionSet, &info, &xr.actions[u].action);
if (XR_FAILED(res))
Con_Printf("openxr: Unable to create action %s [%s] - %i\n", info.actionName, info.localizedActionName, res);
Con_Printf("openxr: Unable to create action %s [%s] - %s\n", info.actionName, info.localizedActionName, XR_StringForResult(res));
return xr.actions[u].action;
}
@ -840,7 +921,7 @@ static int XR_BindProfileStr(const char *fname, const char *file)
suggestedbindings.suggestedBindings = bindings;
res = xrSuggestInteractionProfileBindings(xr.instance, &suggestedbindings);
if (XR_FAILED(res))
Con_Printf("%s: xrSuggestInteractionProfileBindings failed - %i\n", fname, res);
Con_Printf("%s: xrSuggestInteractionProfileBindings failed - %s\n", fname, XR_StringForResult(res));
return acts;
}
}
@ -878,7 +959,7 @@ static void XR_SetupInputs(void)
xr.actionset.subactionPath = XR_NULL_PATH;
res = xrCreateActionSet(xr.instance, &info, &xr.actionset.actionSet);
if (XR_FAILED(res))
Con_Printf("openxr: Unable to create actionset - %i\n", res);
Con_Printf("openxr: Unable to create actionset - %s\n", XR_StringForResult(res));
}
h = 0;
@ -967,7 +1048,7 @@ static void XR_SetupInputs(void)
res = xrCreateActionSpace(xr.session, &info, &xr.actions[h].space);
if (XR_FAILED(res))
Con_Printf("openxr: xrCreateActionSpace failed - %i\n", res);
Con_Printf("openxr: xrCreateActionSpace failed - %s\n", XR_StringForResult(res));
}
break;
default:
@ -983,7 +1064,7 @@ static void XR_SetupInputs(void)
info.actionSets = &xr.actionset.actionSet;
res = xrAttachSessionActionSets(xr.session, &info);
if (XR_FAILED(res))
Con_Printf("openxr: xrAttachSessionActionSets failed - %i\n", res);
Con_Printf("openxr: xrAttachSessionActionSets failed - %s\n", XR_StringForResult(res));
}
#if 1
@ -1040,7 +1121,7 @@ static void XR_SetupInputs(void)
else if (res == XR_ERROR_HANDLE_INVALID) //monado reports this for unimplemented things.
Con_Printf("\t%s: error XR_ERROR_HANDLE_INVALID (not implemented?)\n", xr.actions[u].actname);
else
Con_Printf("\t%s: error %i\n", xr.actions[u].actname, res);
Con_Printf("\t%s: error %s\n", xr.actions[u].actname, XR_StringForResult(res));
}
}
#endif
@ -1172,7 +1253,10 @@ static qboolean XR_Begin(void)
res = xrCreateSession(xr.instance, &sessioninfo, &xr.session);
}
if (XR_FAILED(res))
{
Con_Printf("OpenXR: xrCreateSession failed (%s)\n", XR_StringForResult(res));
return false;
}
{
XrReferenceSpaceCreateInfo info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
@ -1185,9 +1269,9 @@ static qboolean XR_Begin(void)
xrEnumerateSwapchainFormats(xr.session, 0, &swapfmts, NULL);
fmts = alloca(sizeof(*fmts)*swapfmts);
xrEnumerateSwapchainFormats(xr.session, swapfmts, &swapfmts, fmts);
res = xrEnumerateSwapchainFormats(xr.session, swapfmts, &swapfmts, fmts);
if (!swapfmts)
Con_Printf("OpenXR: No swapchain formats to use\n");
Con_Printf("OpenXR: No swapchain formats to use (%s)\n", XR_StringForResult(res));
#ifdef XR_USE_GRAPHICS_API_OPENGL
else if (xr.renderer == QR_OPENGL)
{
@ -1260,10 +1344,16 @@ static qboolean XR_Begin(void)
swapinfo.mipCount = 1;
res = xrCreateSwapchain(xr.session, &swapinfo, &xr.eye[u].swapchain);
if (XR_FAILED(res))
{
Con_Printf("OpenXR: xrCreateSwapchain failed (%s)\n", XR_StringForResult(res));
return false;
}
res = xrEnumerateSwapchainImages(xr.eye[u].swapchain, 0, &xr.eye[u].numswapimages, NULL);
if (XR_FAILED(res))
{
Con_Printf("OpenXR: xrEnumerateSwapchainImages failed (%s)\n", XR_StringForResult(res));
return false;
}
//using a separate swapchain for each eye, so just depend upon npot here and use the whole image.
xr.eye[u].subimage.imageRect.offset.x = 0;
@ -1398,13 +1488,13 @@ static void XR_ProcessEvents(void)
info.primaryViewConfigurationType = xr.viewtype;
res = xrBeginSession(xr.session, &info);
if (XR_FAILED(res))
Con_Printf("Unable to begin session: %i\n", res);
Con_Printf("Unable to begin session: %s\n", XR_StringForResult(res));
}
break;
case XR_SESSION_STATE_STOPPING:
res = xrEndSession(xr.session);
if (XR_FAILED(res))
Con_Printf("Unable to end session: %i\n", res);
Con_Printf("Unable to end session: %s\n", XR_StringForResult(res));
break;
}
xr.state = s->state;
@ -1439,7 +1529,7 @@ static qboolean XR_SyncFrame(double *frametime)
res = xrWaitFrame(xr.session, NULL, &xr.framestate);
if (XR_FAILED(res))
{
Con_Printf("xrWaitFrame: %i\n", res);
Con_Printf("xrWaitFrame: %s\n", XR_StringForResult(res));
return false;
}
time = xr.framestate.predictedDisplayTime;
@ -1487,7 +1577,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
{
res = xrRequestExitSession(xr.session);
if (XR_FAILED(res))
Con_Printf("openxr: Unable to request session end: %i\n", res);
Con_Printf("openxr: Unable to request session end: %s\n", XR_StringForResult(res));
XR_ProcessEvents();
}
@ -1507,7 +1597,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
res = xrBeginFrame(xr.session, NULL);
if (XR_FAILED(res))
Con_Printf("xrBeginFrame: %i\n", res);
Con_Printf("xrBeginFrame: %s\n", XR_StringForResult(res));
if (xr.framestate.shouldRender)
{
uint32_t eyecount;
@ -1522,7 +1612,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
locateinfo.space = xr.space;
res = xrLocateViews(xr.session, &locateinfo, &viewstate, xr.viewcount, &eyecount, eyeview);
if (XR_FAILED(res))
Con_Printf("xrLocateViews: %i\n", res);
Con_Printf("xrLocateViews: %s\n", XR_StringForResult(res));
proj.layerFlags = 0;
proj.space = xr.space;
@ -1536,7 +1626,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
unsigned int imgidx = 0;
res = xrAcquireSwapchainImage(xr.eye[u].swapchain, NULL, &imgidx);
if (XR_FAILED(res))
Con_Printf("xrAcquireSwapchainImage: %i\n", res);
Con_Printf("xrAcquireSwapchainImage: %s\n", XR_StringForResult(res));
memset(&projviews[u], 0, sizeof(projviews[u]));
projviews[u].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW;
@ -1553,7 +1643,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
waitinfo.timeout = 100000;
res = xrWaitSwapchainImage(xr.eye[u].swapchain, &waitinfo);
if (XR_FAILED(res))
Con_Printf("xrWaitSwapchainImage: %i\n", res);
Con_Printf("xrWaitSwapchainImage: %s\n", XR_StringForResult(res));
rendereye(&xr.eye[u].swapimages[imgidx], fovoverride, transform);
//GL note: the OpenXR specification says NOTHING about the application having to glFlush or glFinish.
// I take this to mean that the openxr runtime is responsible for setting up barriers or w/e inside ReleaseSwapchainImage.
@ -1561,7 +1651,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
// I take this to mean that the openxr runtime is responsible for barriers (as it'll need to transition it to general or shader-read anyway).
res = xrReleaseSwapchainImage(xr.eye[u].swapchain, NULL);
if (XR_FAILED(res))
Con_Printf("xrReleaseSwapchainImage: %i\n", res);
Con_Printf("xrReleaseSwapchainImage: %s\n", XR_StringForResult(res));
}
proj.viewCount = u;
}
@ -1572,7 +1662,7 @@ static qboolean XR_Render(void(*rendereye)(texid_t tex, vec4_t fovoverride, vec3
res = xrEndFrame(xr.session, &endframeinfo);
if (XR_FAILED(res))
{
Con_Printf("xrEndFrame: %i\n", res);
Con_Printf("xrEndFrame: %s\n", XR_StringForResult(res));
if (res == XR_ERROR_SESSION_LOST || res == XR_ERROR_SESSION_NOT_RUNNING || res == XR_ERROR_SWAPCHAIN_RECT_INVALID)
XR_SessionEnded(); //something sessiony
else //if (res == XR_ERROR_INSTANCE_LOST)

View File

@ -45,37 +45,33 @@ float(float isnew) updateplayer =
// deltalisten("progs/player.mdl", updateplayer, 0);
//};
var float autocvar_dp_workarounds_allow = TRUE;
var float autocvar_dp_workarounds_force = FALSE;
void(float apilevel, string enginename, float engineversion) CSQC_Init =
{
dprint(sprintf("CSQC: Running on \"%s\" ver=%g, api=%g\n", enginename, engineversion, apilevel));
if (!apilevel)
dp_workarounds = autocvar_dp_workarounds_allow;
if (autocvar_dp_workarounds_force)
dp_workarounds = TRUE;
if (dp_workarounds)
print("DP-specific workarounds are enabled\n");
//make sure the engine knows what commands we want to handle
#define cmd(n,fnc,inc) registercommand(n);
concommandslist
#undef cmd
// Hud_Init(); //make sure the hud images are precached properly, so there's no stalls.
};
float (float event, float parama, float paramb, float devid) CSQC_InputEvent =
{
if (!thedesktop)
return event!=IE_KEYUP;
if (items_keypress(thedesktop, event, parama, paramb, devid))
return TRUE;
#ifdef CSQC_SIMPLE
if (event == IE_KEYDOWN && parama == K_ESCAPE)
{
M_Main(thedesktop);
return TRUE;
}
#endif
return FALSE;
};
#ifdef CSQC_SIMPLE
//simplecsqc doesn't give us access to the 3d scene.
//instead we have hud+scoreboard entrypoints.
//we're a menu, so we use the scoreboard/overlay entrypoint (one that's skipped when a menu is shown).
//we do need to implement a real scoreboard though. :(
void(vector virtsize, float showscores) CSQC_DrawScores =
{
items_draw(thedesktop, virtsize);
};
#define mydesktop mitem_desktop
#else
/*The desktop object will not normally draw anything, but you can get the desktop object to do the drawing by overriding its 'drawgame' method.
The primary advantage of doing the drawing this way is that the menu system can properly handle mouse positions in 3d space with multiple views. The menu system also handles splitscreen efficiently. Note that the menu system will handle clearing the scene and adding entities before this function is called.
You could instead draw the game then draw the menusystem over the top, if you're more comfortable with that.
@ -105,9 +101,29 @@ class mydesktop : mitem_desktop
void(float width, float height, float do2d) CSQC_UpdateView =
{
if (!thedesktop)
thedesktop = spawn(mydesktop);
items_draw(thedesktop, [width, height]);
};
//void(float width, float height, float do2d) CSQC_UpdateView_Loading = CSQC_UpdateView;
#endif
//var float autocvar_dp_workarounds_allow = TRUE;
//var float autocvar_dp_workarounds_force = FALSE;
void(float apilevel, string enginename, float engineversion) CSQC_Init =
{
dprint(sprintf("CSQC: Running on \"%s\" ver=%g, api=%g\n", enginename, engineversion, apilevel));
FingerprintEngine();
//make sure the engine knows what commands we want to handle
#define cmd(n,fnc,inc) registercommand(n);
concommandslist
#undef cmd
thedesktop = spawn(mydesktop);
#ifdef CSQC_SIMPLE
Hud_Init(); //make sure the hud images are precached properly, so there's no stalls.
#endif
};

View File

@ -1,10 +1,18 @@
#pragma progs_dat "../csprogs.dat"
#define CSQC //select the module
//#pragma TARGET FTE
#define CSQC_SIMPLE
#define CSQC //select the module
#ifdef CSQC_SIMPLE
#include "qsextensions.qc" //also sets up system defs
#undef CSQC_SIMPLE
#include "fteextensions.qc" //extra stuff...
#else
#include "fteextensions.qc" //also sets up system defs
#endif
#includelist
fteextensions.qc //also sets up system defs
menusys/mitems.qc //root type
menusys/mitems_common.qc //basic types
menusys/mitem_desktop.qc //other sort of root item
@ -22,26 +30,26 @@ menusys/mitem_spinnymodel.qc //rotating 3d models, used for art/theme.
//define the commands.
//cmd argments are: Name, Function, Sourcefile(may be empty)
//note that this list can be expanded in multiple places.
#define concommandslist \
cmd("togglemenu", M_ToggleMenu, ) \
cmd("m_main", M_Main, menu/main.qc) \
cmd("m_pop", M_Pop, ) \
cmd("m_options", M_Options, menu/options.qc) \
#define concommandslist \
cmd("togglemenu", M_ToggleMenu, ) \
cmd("m_main", M_Main, menu/main.qc) \
cmd("m_pop", M_Pop, ) \
cmd("m_options", M_Options, menu/options.qc) \
cmd("m_keys", M_Options_Keys, menu/options_keys.qc) \
cmd("m_basicopts", M_Options_Basic, menu/options_basic.qc) \
cmd("m_video", M_Options_Video, menu/options_video.qc) \
cmd("m_effects", M_Options_Effects, menu/options_effects.qc) \
cmd("m_audio", M_Options_Audio, menu/options_audio.qc) \
cmd("m_particles", M_Options_Particles, menu/options_particles.qc) \
cmd("m_particles", M_Options_Particles,menu/options_particles.qc) \
cmd("m_hud", M_Options_Hud, menu/options_hud.qc) \
cmd("m_load", M_Load, menu/loadsave.qc) \
cmd("m_save", M_Save, ) \
cmd("m_quit", M_Quit, menu/quit.qc) \
cmd("m_newgame", M_NewGame, menu/newgame.qc) \
cmd("m_servers", M_Servers, menu/servers.qc) \
cmd("m_configs", M_Configs, menu/options_configs.qc) \
cmd("m_reset", M_Reset, ) \
cmd("m_preset", M_Preset, menu/presets.qc)
cmd("m_load", M_Load, menu/loadsave.qc) \
cmd("m_save", M_Save, ) \
cmd("m_quit", M_Quit, menu/quit.qc) \
cmd("m_newgame", M_NewGame, menu/newgame.qc) \
cmd("m_servers", M_Servers, menu/servers.qc) \
cmd("m_configs", M_Configs, menu/options_configs.qc) \
cmd("m_reset", M_Reset, ) \
cmd("m_preset", M_Preset, menu/presets.qc)
//make sure all the right files are included
#define cmd(n,fnc,inc) inc
@ -50,4 +58,5 @@ menusys/mitem_spinnymodel.qc //rotating 3d models, used for art/theme.
#endlist
#undef cmd
#include "cs/hud.qc"
#include "cs/entrypoints.qc"

View File

@ -1,5 +1,5 @@
/*
This file was generated by FTE Quake 5668, dated 2020-04-19T01:23:32.981932Z.
This file was generated by FTE Quake 5740, dated 2020-08-03T10:34:44.534153Z.
This file can be regenerated by issuing the following command:
pr_dumpplatform -o fteextensions
Available options:
@ -31,6 +31,17 @@ Available options:
#if !defined(SSQC) && (defined(QWSSQC) || defined(NQSSQC))
#define SSQC
#endif
#if defined(CSQC) || defined(MENU)
#define DEP_CSQC DEP
#else
#define DEP_CSQC __deprecated("Use CSQC for this")
#endif
#ifndef DEP
#define DEP __deprecated //predefine this if you want to avoid our deprecation warnings.
#endif
#ifndef FTEDEP
#define FTEDEP(reason) //for symbols deprecated in FTE that may still be useful/required for other engines
#endif
#define FTE_PEXT_SETVIEW /* NQ's svc_setview works correctly even in quakeworld */
#define DP_ENT_SCALE
#define FTE_PEXT_LIGHTSTYLECOL
@ -105,6 +116,7 @@ Available options:
#define DP_QC_FINDCHAINFLAGS
#define DP_QC_FINDFLOAT
#define DP_QC_FS_SEARCH
#define DP_QC_FS_SEARCH_PACKFILE
#define DP_QC_GETSURFACE
#define DP_QC_GETSURFACEPOINTATTRIBUTE
#define DP_QC_GETTAGINFO
@ -341,7 +353,7 @@ void() PlayerPreThink; /* With Prediction(QW compat/FTE default): Called before
No Prediction(NQ compat): Called AFTER the player's movement intents have already been processed (ie: velocity will have already changed according to input_*, but before the actual position change. */
void() PlayerPostThink; /* Called after the player's input commands are processed. */
void() ClientKill; /* Called in response to 'cmd kill' (or just 'kill'). */
void(optional float csqcactive) ClientConnect; /* Called after the connecting client has finished loading and is ready to receive active entities. Note that this is NOT the first place that a client might be referred to. */
void() ClientConnect; /* Called after the connecting client has finished loading and is ready to receive active entities. Note that this is NOT the first place that a client might be referred to. To determine if the client has csqc active (and kick anyone that doesn't), you can use if(infokeyf(self,INFOKEY_P_CSQCACTIVE)) {sprint(self, "CSQC is required for this server\n");dropclient(self);} */
void() PutClientInServer; /* Enginewise, this is only ever called immediately after ClientConnect and is thus a little redundant. Modwise, this is also called for respawning a player etc. */
void() ClientDisconnect; /* Called once a client disconnects or times out. Not guarenteed to be called on map changes. */
void() SetNewParms; /* Called without context when a new client initially connects (before ClientConnect is even called). This function is expected to only set the parm* globals so that they can be decoded properly later. You should not rely on 'self' being set. */
@ -517,6 +529,14 @@ float pmove_onground; /* Reports the onground state of the engineside player (af
vector global_gravitydir = '0 0 -1'; /* The direction gravity should act in if not otherwise specified per entity. */
int serverid; /* The unique id of this server within the server cluster. */
#endif
#ifdef SSQC
.float button3;
.float button4;
.float button5;
.float button6;
.float button7;
.float button8;
#endif
#if defined(NQSSQC)
.float lastruntime; /* This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons. */
#endif
@ -529,9 +549,9 @@ int serverid; /* The unique id of this server within the server cluster. */
.entity movechain; /* This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity. */
.void() chainmoved; /* Called when the entity is moved as a result of being part of another entity's .movechain */
.void(float old, float new) contentstransition; /* This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own. */
.float dimension_solid; /* This is the bitmask of dimensions which the entity is solid within. */
.float dimension_solid; /* This is the bitmask of dimensions which the entity is solid within. This is not networked, instead csqc traces impacting ssqc entities assumes the ssqc entity to have a dimension_solid of 1. */
.float dimension_hit; /* This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through. */
.int hitcontentsmaski; /* Traces performed for this entity will impact against surfaces that match this contents mask. */
.int hitcontentsmaski; /* Traces performed for this entity will impact against surfaces that match this contents mask (CONTENTBITS_* constants). */
.float dphitcontentsmask; /* Some crappy field that inefficiently requires translating to the native contents flags. Ditch the 'dp', do it properly. */
.float scale; /* Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16. */
.float fatness; /* How many QuakeUnits to push the entity's verticies along their normals by. */
@ -545,13 +565,13 @@ int serverid; /* The unique id of this server within the server cluster. */
.float basebone; /* The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects. */
.float baseframe; /* See basebone */
.void() customphysics; /* Called once each physics frame, overriding the entity's .movetype field and associated logic. You'll probably want to use tracebox to move it through the world. Be sure to call .think as appropriate. */
.entity tag_entity;
.float tag_index;
.entity tag_entity; /* Specifies which entity this entity's origin+angles is 'attached' to. */
.float tag_index; /* Specifies the tag or bone on the parent entity that we're attached to. If this is -1 then the entity is instead a q3-like camera portal, with the tag_entity saying the entity to display for. If tag_entity is world then this is a q3-like portal surface marker with a separate camera (with a tag_entity referring to the portal surface). */
.float skeletonindex; /* This object serves as a container for the skeletal bone states used to override the animation data. */
.vector colormod; /* Provides a colour tint for the entity (does not affect fullbrights). */
.vector glowmod; /* Scaler for an entity's fullbright textures. */
.vector gravitydir; /* Specifies the direction in which gravity acts. Must be normalised. '0 0 0' also means down. Use '0 0 1' if you want the player to be able to run on ceilings. */
.vector(vector org, vector ang) camera_transform; /* Provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc. */
.vector(vector org, vector ang) camera_transform; /* A callback that provides portal transform information for portal surfaces attached to this entity. Also used to open up pvs in ssqc. */
#endif
#ifdef SSQC
.float pmove_flags;
@ -582,24 +602,24 @@ int serverid; /* The unique id of this server within the server cluster. */
.entity view2; /* defines a second viewpoint, typically displayed in a corner of the screen (also punches open pvs). */
.vector movement; /* These are the directions that the player is currently trying to move in (ie: which +forward/+moveright/+moveup etc buttons they have held), expressed relative to that player's angles. Order is forward, right, up. */
.float vw_index; /* This acts as a second modelindex, using the same frames etc. */
.entity nodrawtoclient; /* This entity will not be sent to the player named by this field. They will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */
.entity drawonlytoclient; /* This entity will be sent *only* to the player named by this field. To other players they will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */
.entity viewmodelforclient; /* This entity will be sent only to the player named by this field, and this entity will be attached to the player's view as an additional weapon model. */
.entity exteriormodeltoclient; /* This entity will be invisible to the player named by this field, except in mirrors or mirror-like surfaces, where it will be visible as normal. It may still cast shadows as normal, and generate lights+particles, depending on client settings. Does not affect how other players see the entity. */
__deprecated("Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.") .entity nodrawtoclient; /* This entity will not be sent to the player named by this field. They will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */
__deprecated("Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.") .entity drawonlytoclient; /* This entity will be sent *only* to the player named by this field. To other players they will be invisible and not emit dlights/particles. Does not work in MVD-recorded game. */
__deprecated("Redundant. Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.") .entity viewmodelforclient; /* This entity will be sent only to the player named by this field, and this entity will be attached to the player's view as an additional weapon model. */
__deprecated("Cannot be recorded in MVDs, nor work properly with splitscreen. Use CSQC instead.") .entity exteriormodeltoclient; /* This entity will be invisible to the player named by this field, except in mirrors or mirror-like surfaces, where it will be visible as normal. It may still cast shadows as normal, and generate lights+particles, depending on client settings. Does not affect how other players see the entity. */
.entity clientcamera; /* Controls which entity to use for this client's camera. */
.float glow_size; /* Some outdated particle trail thing. */
.float glow_color; /* Some outdated particle trail thing. */
.float glow_trail; /* Some outdated particle trail thing. */
.float traileffectnum; /* This should be set to the result of particleeffectnum, in order to attach a custom trail effect to an entity as it moves. */
.float emiteffectnum; /* This should be set to the result of particleeffectnum, in order to continually spawn particles in the direction that this entity faces. */
.float dimension_see; /* This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible. */
.float dimension_seen; /* This is the dimension mask (bitfield) that the client is visible within. Clients that cannot see this dimension mask will not see this entity. */
.float dimension_ghost; /* If this entity is visible only within these dimensions, it will become transparent, as if a ghost. */
.float dimension_ghost_alpha; /* If this entity is subject to dimension_ghost, this is the scaler for its alpha value. If 0, 0.5 will be used instead. */
__deprecated("Does not work with MVDs nor splitscreen.") .float dimension_see; /* This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible. */
__deprecated("Does not work with MVDs nor splitscreen.") .float dimension_seen; /* This is the dimension mask (bitfield) that the client is visible within. Clients that cannot see this dimension mask will not see this entity. */
__deprecated("Does not work with MVDs nor splitscreen.") .float dimension_ghost; /* If this entity is visible only within these dimensions, it will become transparent, as if a ghost. */
__deprecated("Does not work with MVDs nor splitscreen.") .float dimension_ghost_alpha; /* If this entity is subject to dimension_ghost, this is the scaler for its alpha value. If 0, 0.5 will be used instead. */
.float(entity playerent, float changedflags) SendEntity; /* Called by the engine whenever an entity needs to be (re)sent to a client's csprogs, either because SendFlags was set or because data was lost. Must write its data to the MSG_ENTITY buffer. Will be called at the engine's leasure. */
.float SendFlags; /* Indicates that something in the entity has been changed, and that it needs to be updated to all players that can see it. The engine will clear it at some point, with the cleared bits appearing in the 'changedflags' argument of the SendEntity method. */
.float Version; /* Obsolete, set a SendFlags bit instead. */
.float clientcolors;
__deprecated("Use SendFlags instead.") .float Version; /* Obsolete */
__deprecated("Doesn't support RGB player colours.") .float clientcolors;
.float viewzoom;
.float items2;
.float playerclass;
@ -607,7 +627,7 @@ int serverid; /* The unique id of this server within the server cluster. */
.float light_level; /* Used by hexen2 to indicate the light level where the player is standing. */
.float pvsflags; /* Reconfigures when the entity is visible to clients */
.float uniquespawnid; /* Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient. */
.float() customizeentityforclient; /* Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see. */
DEP_CSQC .float() customizeentityforclient; /* Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see. */
#endif
#ifdef CSQC
.float frame3; /* Some people just don't understand how to use framegroups... */
@ -643,7 +663,7 @@ void(float pauseduration) SV_PausedTic; /* For each frame that the server is pau
float(float newstatus) SV_ShouldPause; /* Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event. */
void() SV_RunClientCommand; /* Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs. */
void() SV_AddDebugPolygons; /* Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server. */
void() SV_PlayerPhysics; /* Compatibility method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful. */
DEP_CSQC void() SV_PlayerPhysics; /* Compatibility method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful. */
void() EndFrame; /* Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame. */
string(string addr, string uinfo, string features) SV_CheckRejectConnection; /* Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments. */
#endif
@ -694,10 +714,11 @@ void() m_init;
void() m_shutdown;
void(vector screensize) m_draw; /* Provides the menuqc with a chance to draw. Will be called even if the menu does not have focus, so be sure to avoid that. COMPAT: screensize is not provided in DP. */
void(vector screensize, float opaque) m_drawloading; /* Additional drawing function to draw loading screens. If opaque is set, then this function must ensure that the entire screen is overdrawn (even if just by a black drawfill). */
void(string rendererdescription) Menu_RendererRestarted; /* Called by the engine after the video was restarted. This serves to notify the MenuQC that any render targets that it may have cached were purged, and will need to be regenerated. */
float(float evtype, float scanx, float chary, float devid) Menu_InputEvent; /* If present, this is called instead of m_keydown and m_keyup
Called whenever a key is pressed, the mouse is moved, etc. evtype will be one of the IE_* constants. The other arguments vary depending on the evtype. Key presses are not guarenteed to have both scan and unichar values set at the same time. */
void(float scan, float chr) m_keydown;
void(float scan, float chr) m_keyup;
__deprecated("Use Menu_InputEvent") void(float scan, float chr) m_keydown;
__deprecated("Use Menu_InputEvent") void(float scan, float chr) m_keyup;
void(float wantmode) m_toggle;
float(string cmd) m_consolecommand;
#endif
@ -727,7 +748,7 @@ const float FONT_DEFAULT = 0;
#endif
const float TRUE = 1;
const float FALSE = 0; /* File not found... */
const float M_PI = 3.14159;
const float M_PI = 3.14159; /* Mathematica Pi constant. */
#if defined(CSQC) || defined(SSQC)
const float MOVETYPE_NONE = 0;
const float MOVETYPE_WALK = 3;
@ -748,8 +769,8 @@ const float SOLID_TRIGGER = 1;
const float SOLID_BBOX = 2;
const float SOLID_SLIDEBOX = 3;
const float SOLID_BSP = 4; /* Does not collide against other SOLID_BSP entities. Normally paired with MOVETYPE_PUSH. */
const float SOLID_CORPSE = 5; /* 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. */
const float SOLID_LADDER = 20; /* Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead. */
const float SOLID_CORPSE = 5; /* Non-solid to SOLID_SLIDEBOX or other SOLID_CORPSE entities. For hitscan weapons to hit corpses, change the player's .hitcontentsmaski value to include CONTENTBIT_CORPSE, perform the traceline, then revert the player's .solid value. */
__deprecated("Obsoleted by .skin=CONTENTS_LADDER") const float SOLID_LADDER = 20; /* Obsolete and may be removed at some point. Use skin=CONTENT_LADDER and solid_bsp or solid_trigger instead. */
const float SOLID_PORTAL = 21; /* CSG subtraction volume combined with entity transformations on impact. */
const float SOLID_BSPTRIGGER = 22; /* For complex-shaped trigger volumes, instead of being a pure aabb. */
const float SOLID_PHYSICS_BOX = 32;
@ -833,12 +854,12 @@ const int CONTENTBIT_SOLID = 0x00000001i;
const int CONTENTBIT_LAVA = 0x00000008i;
const int CONTENTBIT_SLIME = 0x00000010i;
const int CONTENTBIT_WATER = 0x00000020i;
const int CONTENTBIT_FTELADDER = 0x00004000i;
const int CONTENTBIT_FTELADDER = 0x00004000i; /* Content bit used for .skin=CONTENT_LADDER entities. */
const int CONTENTBIT_PLAYERCLIP = 0x00010000i;
const int CONTENTBIT_MONSTERCLIP = 0x00020000i;
const int CONTENTBIT_BODY = 0x02000000i;
const int CONTENTBIT_CORPSE = 0x04000000i;
const int CONTENTBIT_Q2LADDER = 0x20000000i; /* Content bit specific to q2bsp */
const int CONTENTBIT_BODY = 0x02000000i; /* Content bit that indicates collisions against SOLID_BBOX/SOLID_SLIDEBOX entities. */
const int CONTENTBIT_CORPSE = 0x04000000i; /* Content bit that indicates collisions against SOLID_CORPSE entities. */
const int CONTENTBIT_Q2LADDER = 0x20000000i; /* Content bit specific to q2bsp (conflicts with q3bsp contents so use with caution). */
const int CONTENTBIT_SKY = 0x80000000i; /* Content bit somewhat specific to q1bsp (aliases to NODROP in q3bsp), but you should probably check surfaceflags&SURF_SKY as well for q2+q3bsp too. */
const int CONTENTBITS_POINTSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY; /* Bits that traceline would normally consider solid */
const int CONTENTBITS_BOXSOLID = CONTENTBIT_SOLID|0x00000002i|CONTENTBIT_BODY|CONTENTBIT_PLAYERCLIP; /* Bits that tracebox would normally consider solid */
@ -887,9 +908,18 @@ const float ATTN_STATIC = 3; /* Even more attenuation to avoid torches drowing o
#endif
#ifdef SSQC
const float SVC_CGAMEPACKET = 83; /* 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. */
#endif
#if defined(NQSSQC)
const float MSG_BROADCAST = 0; /* The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family. */
const float MSG_ONE = 1; /* 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. */
const float MSG_ALL = 2; /* The byte(s) will be reliably sent to all players. */
#endif
#if defined(QWSSQC)
__deprecated("Use MSG_MULTICAST+multicast(MULTICAST_*)") const float MSG_BROADCAST; /* The byte(s) will be unreliably sent to all players. MSG_ constants are valid arguments to the Write* builtin family. */
__deprecated("Use MSG_MULTICAST+multicast(MULTICAST_ONE_R)") const float MSG_ONE = 1; /* 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. */
__deprecated("Use MSG_MULTICAST+multicast(MULTICAST_ALL)") const float MSG_ALL = 2; /* The byte(s) will be reliably sent to all players. */
#endif
#ifdef SSQC
const float MSG_INIT = 3; /* The byte(s) will be written into the signon buffer. Clients will see these messages when they connect later. This buffer is only flushed on map changes, so spamming it _WILL_ result in overflows. */
const float MSG_MULTICAST = 4; /* The byte(s) will be written into the multicast buffer for more selective sending. Messages sent this way will never be split across packets, and using this for csqc-only messages will not break protocol translation. */
const float MSG_ENTITY = 5; /* The byte(s) will be written into the entity buffer. This is a special value used only inside 'SendEntity' functions. */
@ -1039,7 +1069,7 @@ const float EF_FLAG1 = 16;
const float EF_FLAG2 = 32;
#endif
#if defined(CSQC) || defined(NQSSQC)
const float EF_NODRAW = 16;
const float EF_NODRAW = 16; /* Disables drawing of the model. Does NOT work on QW players. */
#endif
#if defined(CSQC) || defined(SSQC)
const float EF_ADDITIVE = 32; /* The entity will be drawn with an additive blend. This is NOT supported on players in any quakeworld engine. */
@ -1064,15 +1094,15 @@ const float MF_TRACER2 = 64; /* AKA: hellknight projectile trail */
const float MF_TRACER3 = 128; /* AKA: purple vore trail */
#endif
#ifdef SSQC
const float SL_ORG_TL = 20; /* Used with showpic etc, specifies that the x+y values are relative to the top-left of the screen */
const float SL_ORG_TR = 21;
const float SL_ORG_BL = 22;
const float SL_ORG_BR = 23;
const float SL_ORG_MM = 24;
const float SL_ORG_TM = 25;
const float SL_ORG_BM = 26;
const float SL_ORG_ML = 27;
const float SL_ORG_MR = 28;
DEP_CSQC const float SL_ORG_TL = 20; /* Used with showpic etc, specifies that the x+y values are relative to the top-left of the screen */
DEP_CSQC const float SL_ORG_TR = 21;
DEP_CSQC const float SL_ORG_BL = 22;
DEP_CSQC const float SL_ORG_BR = 23;
DEP_CSQC const float SL_ORG_MM = 24;
DEP_CSQC const float SL_ORG_TM = 25;
DEP_CSQC const float SL_ORG_BM = 26;
DEP_CSQC const float SL_ORG_ML = 27;
DEP_CSQC const float SL_ORG_MR = 28;
#endif
#if defined(CSQC) || defined(SSQC)
const float PFLAGS_NOSHADOW = 1; /* Associated RT lights attached will not cast shadows, making them significantly faster to draw. */
@ -1081,7 +1111,6 @@ const float PFLAGS_CORONA = 2; /* Enables support of coronas on the associated r
#ifdef SSQC
const float PFLAGS_FULLDYNAMIC = 128; /* When set in self.pflags, enables fully-customised dynamic lights. Custom rtlight information is not otherwise used. */
#endif
#if defined(CSQC) || defined(SSQC)
const float EV_STRING = 1;
const float EV_FLOAT = 2;
const float EV_VECTOR = 3;
@ -1091,7 +1120,6 @@ const float EV_FUNCTION = 6;
const float EV_POINTER = 7;
const float EV_INTEGER = 8;
const float EV_VARIANT = 9;
#endif
hashtable gamestate; /* Special hash table index for hash_add and hash_get. Entries in this table will persist over map changes (and doesn't need to be created/deleted). */
const float HASH_REPLACE = 256; /* Used with hash_add. Attempts to remove the old value instead of adding two values for a single key. */
const float HASH_ADD = 512; /* Used with hash_add. The new entry will be inserted in addition to the existing entry. */
@ -1119,6 +1147,9 @@ const float STAT_VIEWZOOM = 21; /* Scales fov and sensitiity. Part of DP_VIEWZOO
const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */
#endif
#if defined(CSQC) || defined(MENU)
const float PRECACHE_PIC_FROMWAD = 1; /* Attempt to load it from the legacy gfx.wad file (usually its better to just use a gfx/ prefix instead). */
const float PRECACHE_PIC_DOWNLOAD = 256; /* If no image could be loaded then attempt to download one from the server. This flag can cause the function to block until completion. (Slow!) */
const float PRECACHE_PIC_TEST = 512; /* The precache will block until the image is fully loaded, returning a null string on failure. (Slow!) */
const float VF_MIN = 1; /* The top-left of the 3d viewport in screenspace. The VF_ values are used via the setviewprop/getviewprop builtins. */
const float VF_MIN_X = 2;
const float VF_MIN_Y = 3;
@ -1338,8 +1369,8 @@ void(string err,...) objerror = #3; /*
void(string text,...) print = #4; /* Part of DP_SV_PRINT
Hello, world. Shoves junk on the console. Hopefully people will bother to read it, maybe. */
void(string text,...) bprint = #5;
void(float clientnum, string text,...) msprint = #6;
DEP void(string text,...) bprint = #5;
DEP void(float clientnum, string text,...) msprint = #6;
void(string text,...) cprint = #7; /*
Tries to show the given message in the centre of the screen, assuming that its not obscured by menus. Oh hey look, you're calling it in menuqc! */
@ -1403,10 +1434,10 @@ float(string) strlen = #52; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/
string(string, optional string, optional string, optional string, optional string, optional string, optional string, optional string) strcat = #53; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/
string(string s, float start, float length) substring = #54; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/
vector(string) stov = #55; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/
string(string) strzone = #56; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
FTEDEP("Redundant") string(string) strzone = #56; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
Exists in FTE for compat only, no different from strcat. */
void(string) strunzone = #57; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
FTEDEP("Redundant") void(string) strunzone = #57; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
Exists in FTE for compat only, does nothing. */
float(string) tokenize = #58; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND
@ -1450,25 +1481,25 @@ void() crash = #72; /*
void() stackdump = #73; /*
Prints out the QC's stack, for console-based error reports. */
searchhandle(string pattern, float caseinsensitive, float quiet) search_begin = #74; /* Part of DP_QC_FS_SEARCH*/
void(searchhandle handle) search_end = #75; /* Part of DP_QC_FS_SEARCH*/
float(searchhandle handle) search_getsize = #76; /* Part of DP_QC_FS_SEARCH*/
string(searchhandle handle, float num) search_getfilename = #77; /* Part of DP_QC_FS_SEARCH*/
searchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3} flags, float quiet, optional string package) search_begin = #74; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/
void(searchhandle handle) search_end = #75; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/
float(searchhandle handle) search_getsize = #76; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/
string(searchhandle handle, float num) search_getfilename = #77; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/
float(entity) etof = #79;
entity(float) ftoe = #80;
float(string) validstring = #81; /*
Returns true if str isn't null. In case 'if [not](str)' was configured to test for empty instead of null. */
float(string str) altstr_count = #82; /*
DEP float(string str) altstr_count = #82; /*
Reports how many single-quotes there were in the string, divided by 2. */
string(string str) altstr_prepare = #83; /*
DEP string(string str) altstr_prepare = #83; /*
Adds markup to escape only single-quotes. Does not add any. */
string(string str, float num) altstr_get = #84; /*
DEP string(string str, float num) altstr_get = #84; /*
Gets the Nth single-quoted token in the input. */
string(string str, float num, string setval) altstr_set = #85; /*
DEP string(string str, float num, string setval) altstr_set = #85; /*
Changes the Nth single-quoted token. The setval argument must not contain any single-quotes (use altstr_prepare to ensure this). */
entity(entity start, .float field, float match) findflags = #87; /* Part of DP_QC_FINDFLAGS*/
@ -1564,6 +1595,12 @@ entity(entity start, .string fld, string match) find = #18; /*
Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.
If you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */
#endif
entity*(.__variant fld, __variant match, int type=EV_STRING, __out int count) find_list = #0:find_list; /*
Scan for the next entity with a given field set to the given 'match' value. start should be either world, or the previous entity that was found. Returns world on failure/if there are no more.
If you have many many entities then you may find that hashtables will give more performance (but requires extra upkeep). */
#if defined(CSQC) || defined(SSQC)
string(string s) precache_sound = #19; /*
Precaches a sound, making it known to clients and loading it from disk. This builtin (strongly) should be called during spawn functions. This builtin must be called for the sound before the sound builtin is called, or it might not even be heard. */
@ -1584,7 +1621,10 @@ void(entity client, float flags, string s) stuffcmdflags = #0:stuffcmdflags; /*
#endif
#if defined(CSQC) || defined(SSQC)
entity(vector org, float rad, optional .entity chainfield) findradius = #22; /*
Finds all entities within a distance of the 'org' specified. One entity is returned directly, while other entities are returned via that entity's .chain field. */
Finds all entities within a distance of the 'org' specified. One entity is returned directly, while other entities are returned via that entity's .chain field. Use findradius_list for an updated alternative without reenterancy issues. */
entity*(vector org, float rad, __out int foundcount, int sort=0) findradius_list = #0:findradius_list; /*
Finds all entities linked with a bbox within a distance of the 'org' specified, returning the list as a temp-array (world signifies the end). Unlike findradius, sv_gameplayfix_blowupfallenzombies is ignored (use FL_FINDABLE_NONSOLID instead), while sv_gameplayfix_findradiusdistancetobox and dpcompat_findradiusarealinks are force-enabled. The resulting buffer will automatically be cleaned up by the engine and does not need to be freed. */
#endif
#if defined(NQSSQC)
@ -1785,7 +1825,7 @@ float(string) stof = #81; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_
void(vector where, float set) multicast = #82; /* Part of FTE_QC_MULTICAST
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. */
void(entity to, string str) redirectcmd = #101; /* Part of ??MVDSV_BUILTINS
DEP void(entity to, string str) redirectcmd = #101; /* Part of ??MVDSV_BUILTINS
Executes a single console command, and sends the text generated by it to the specified player. The command will be executed at the end of the frame once QC is no longer running - you may wish to pre/postfix it with 'echo'. */
#endif
@ -1852,10 +1892,10 @@ float(string builtinname) builtin_find = #100; /*
float(float value) anglemod = #102;
#endif
#ifdef SSQC
void(string slot, string picname, float x, float y, float zone, optional entity player) showpic = #104; /* Part of TEI_SHOWLMP2*/
void(string slot, optional entity player) hidepic = #105; /* Part of TEI_SHOWLMP2*/
void(string slot, float x, float y, float zone, optional entity player) movepic = #106; /* Part of TEI_SHOWLMP2*/
void(string slot, string picname, optional entity player) changepic = #107; /* Part of TEI_SHOWLMP2*/
DEP_CSQC void(string slot, string picname, float x, float y, float zone, optional entity player) showpic = #104; /* Part of TEI_SHOWLMP2*/
DEP_CSQC void(string slot, optional entity player) hidepic = #105; /* Part of TEI_SHOWLMP2*/
DEP_CSQC void(string slot, float x, float y, float zone, optional entity player) movepic = #106; /* Part of TEI_SHOWLMP2*/
DEP_CSQC void(string slot, string picname, optional entity player) changepic = #107; /* Part of TEI_SHOWLMP2*/
#endif
#if defined(CSQC) || defined(SSQC)
filestream(string filename, float mode, optional float mmapminsize) fopen = #110; /* Part of FRIK_FILE
@ -1887,13 +1927,16 @@ float(string s) strlen = #114; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*
string(string s1, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) strcat = #115; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/
string(string s, float start, float length) substring = #116; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/
vector(string s) stov = #117; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/
string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
FTEDEP("Redundant") string(string s, ...) strzone = #118; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
Create a semi-permanent copy of a string that only becomes invalid once strunzone is called on the string (instead of when the engine assumes your string has left scope). This builtin has become redundant in FTEQW due to the FTE_QC_PERSISTENTTEMPSTRINGS extension and is now functionally identical to strcat for compatibility with old engines+mods. */
void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
FTEDEP("Redundant") void(string s) strunzone = #119; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS
Destroys a string that was allocated by strunzone. Further references to the string MAY crash the game. In FTE, this function became redundant and now does nothing. */
#endif
void*(int bytes) createbuffer = #0:createbuffer; /*
Returns a temporary buffer that can be written to / read from. The buffer will be garbage collected and thus cannot be explicitly freed. Tempstrings and buffer references must not be stored into the buffer as the garbage collector will not scan these. */
#ifdef SSQC
void(string cvar, float val) cvar_setf = #176;
#endif
@ -2021,7 +2064,7 @@ string(string s) strtrim = #0:strtrim; /*
Trims the whitespace from the start+end of the string. */
#if defined(CSQC) || defined(SSQC)
void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY
__deprecated("Use strftime.") void() calltimeofday = #231; /* Part of FTE_CALLTIMEOFDAY
Asks the engine to instantly call the qc's 'timeofday' function, before returning. For compatibility with mvdsv.
timeofday should have the prototype: void(float secs, float mins, float hour, float day, float mon, float year, string strvalue)
The strftime builtin is more versatile and less weird. */
@ -2415,8 +2458,8 @@ void(float width, vector pos1, vector pos2, vector rgb, float alpha, optional fl
float(string name) iscachedpic = #316; /*
Checks to see if the image is currently loaded. Engines might lie, or cache between maps. */
string(string name, optional float trywad) precache_pic = #317; /*
Forces the engine to load the named image. If trywad is specified, the specified name must lack any path and extension. */
string(string name, optional float flags) precache_pic = #317; /*
Forces the engine to load the named image. Flags are a bitmask of the PRECACHE_PIC_* flags. */
#endif
#if defined(CSQC) || defined(MENU)
@ -2430,11 +2473,14 @@ int*(string filename, __out int width, __out int height) r_readimage = #0:r_read
#ifdef CSQC
#define draw_getimagesize drawgetimagesize
vector(string picname) drawgetimagesize = #318; /*
Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. */
Returns the dimensions of the named image. Images specified with .lmp should give the original .lmp's dimensions even if texture replacements use a different resolution. WARNING: this function may be slow if used without or directly after its initial precache_pic. */
void(string name) freepic = #319; /*
Tells the engine that the image is no longer needed. The image will appear to be new the next time its needed. */
string(string modelname, int frame, float frametime) spriteframe = #0:spriteframe; /*
Obtains a suitable shader name to draw a sprite's shader via drawpic/R_BeginPolygon/etc, instead of needing to create a scene. */
float(vector position, float character, vector size, vector rgb, float alpha, optional float drawflag) drawcharacter = #320; /*
Draw the given quake character at the given position.
If flag&4, the function will consider the char to be a unicode char instead (or display as a ? if outside the 32-127 range).
@ -2534,7 +2580,7 @@ string(float keynum) keynumtostring = #340; /*
#endif
#ifdef MENU
string(float keynum) keynumtostring_csqc = #340; /*
DEP string(float keynum) keynumtostring_csqc = #340; /*
Returns a hunam-readable name for the given keycode, as a tempstring. */
#endif
@ -2544,7 +2590,7 @@ float(string keyname) stringtokeynum = #341; /*
#endif
#ifdef MENU
float(string keyname) stringtokeynum_csqc = #341; /*
DEP float(string keyname) stringtokeynum_csqc = #341; /*
Looks up the key name in the same way that the bind command would, returning the keycode for that key. */
#endif
@ -2598,10 +2644,10 @@ int(float playernum, string keyname, optional void *outptr, int size) getplayerk
#endif
#if defined(CSQC) || defined(MENU)
void(float seat, string keyname, string newvalue) setlocaluserinfo = #0:setlocaluserinfo; /*
Change a userinfo key for the local player, equivelent to the setinfo console command. The server will normally forward the setting to other clients. */
Change a userinfo key for the specified local player seat, equivelent to the setinfo console command. The server will normally forward the setting to other clients. */
string(float seat, string keyname) getlocaluserinfo = #0:getlocaluserinfo; /*
Reads a local userinfo key for the active seat. This is not quite the same as getplayerkeyvalue, due to latency and possible serverside filtering. */
Reads a local userinfo key for the specified local player seat. This is not quite the same as getplayerkeyvalue, due to latency and possible serverside filtering. */
void(float seat, string keyname, void *outptr, int size) setlocaluserinfoblob = #0:setlocaluserinfoblob; /*
Sets the userinfo key to a blob that may contain nulls etc. Keys with a leading underscore will be visible to only the server (for user-specific binary settings). */
@ -2659,12 +2705,10 @@ void(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_re
Reconfigures a reverb slot for weird effects. Slot 0 is reserved for no effects. Slot 1 is reserved for underwater effects. Reserved slots will be reinitialised on snd_restart, but can otherwise be changed. These reverb slots can be activated with SetListener. Note that reverb will currently only work when using OpenAL. */
#endif
#if defined(CSQC) || defined(MENU)
void(string cmdname) registercommand = #352; /*
Register the given console command, for easy console use.
Console commands that are later used will invoke CSQC_ConsoleCommand. */
Console commands that are later used will invoke CSQC_ConsoleCommand/m_consolecommand/ConsoleCmd according to module. */
#endif
float(entity ent) wasfreed = #353; /*
Quickly check to see if the entity is currently free. This function is only valid during the two-second non-reuse window, after that it may give bad results. Try one second to make it more robust. */
@ -2830,6 +2874,16 @@ float(entity e, float nowreadonly) entityprotection = #0:entityprotection; /*
string(vector pos) getlocationname = #0:getlocationname; /*
Looks up the specified position in the current map's .loc file and reports the nearest marked name. */
#endif
#ifdef MENU
void(int cliptype) clipboard_get = #0:clipboard_get; /*
Attempts to query the system clipboard. Any pasted text will be returned via Menu_InputEvent */
#endif
#if defined(CSQC) || defined(MENU)
void(int cliptype, string text) clipboard_set = #0:clipboard_set; /*
Changes the system clipboard to the specified text. */
#endif
#if defined(CSQC) || defined(SSQC)
entity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY
@ -2837,7 +2891,7 @@ entity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPY
#endif
#ifdef SSQC
void(entity ent, float colours) setcolors = #401; /*
__deprecated("No RGB support.") void(entity ent, float colours) setcolors = #401; /*
Changes a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours. */
#endif
@ -2915,14 +2969,14 @@ void(entity e, string s) clientcommand = #440; /* Part of KRIMZON_SV_PARSECLIENT
float(string s) tokenize = #441; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/
string(float n) argv = #442; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/
void(entity e, entity tagentity, string tagname) setattachment = #443; /* Part of DP_GFX_QUAKE3MODELTAGS*/
searchhandle(string pattern, float caseinsensitive, float quiet) search_begin = #444; /* Part of DP_QC_FS_SEARCH
initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. */
searchhandle(string pattern, enumflags:float{SB_CASEINSENSITIVE=1<<0,SB_FULLPACKAGEPATH=1<<1,SB_ALLOWDUPES=1<<2,SB_FORCESEARCH=1<<3} flags, float quiet, optional string filterpackage) search_begin = #444; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE
initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. SB_FULLPACKAGEPATH interprets the filterpackage arg as a full package path to avoid gamedir ambiguity, equivelent to whichpack's WP_FULLPACKAGEPATH flag. SB_ALLOWDUPES allows returning multiple entries with the same name (but different package, useful with search_fopen). SB_FORCESEARCH requires use of the filterpackage and SB_FULLPACKAGEPATH flag, initiating searches from gamedirs/packages which are not currently active. */
void(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH*/
float(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH
void(searchhandle handle) search_end = #445; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE*/
float(searchhandle handle) search_getsize = #446; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE
Retrieves the number of files that were found. */
string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH
string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_QC_FS_SEARCH, DP_QC_FS_SEARCH_PACKFILE
Retrieves name of one of the files that was found by the initial search. */
#endif
@ -2932,6 +2986,12 @@ float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize
string(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME
Retrieves modification time of one of the files. */
string(searchhandle handle, float num) search_getpackagename = #0:search_getpackagename; /*
Retrieves the name of the package containing the file. Search with SB_FULLPACKAGEPATH to see gamedir/package info */
filestream(searchhandle handle, float num) search_fopen = #0:search_fopen; /*
Opens the file directly, without getting confused about entries from other packages. Read access only. */
#if defined(CSQC) || defined(SSQC)
string(string cvarname) cvar_string = #448; /* Part of DP_QC_CVAR_STRING*/
entity(entity start, .float fld, float match) findflags = #449; /* Part of DP_QC_FINDFLAGS*/
@ -3020,7 +3080,7 @@ void(string id, float newstate) cin_setstate = #0:cin_setstate;
float(string id) cin_getstate = #0:cin_getstate;
void(string file) cin_restart = #0:cin_restart;
#endif
float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/
__deprecated("Use digest_hex") float(float caseinsensitive, string s, ...) crc16 = #494; /* Part of DP_QC_CRC16*/
float(string name) cvar_type = #495; /* Part of DP_QC_CVAR_TYPE*/
float() numentityfields = #496; /* Part of DP_QC_ENTITYDATA
Gives the number of named entity fields. Note that this is not the size of an entity, but rather just the number of unique names (ie: vectors use 4 names rather than 3). */
@ -3051,16 +3111,20 @@ string() ReadPicture = #501; /*
void(float effectindex, entity own, vector org_from, vector org_to, vector dir_from, vector dir_to, float countmultiplier, optional float flags) boxparticles = #502;
#endif
string(string filename, optional float makereferenced) whichpack = #503; /* Part of DP_QC_WHICHPACK
Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If makereferenced is true, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */
string(string filename, optional enumflags:float{WP_REFERENCEPACKAGE,WP_FULLPACKAGEPATH} flags) whichpack = #503; /* Part of DP_QC_WHICHPACK
Returns the pak file name that contains the file specified. progs/player.mdl will generally return something like 'pak0.pak'. If WP_REFERENCE, clients will automatically be told that the returned package should be pre-downloaded and used, even if allow_download_refpackages is not set. */
#ifdef CSQC
__variant(float entnum, float fieldnum) getentity = #504; /*
Looks up fields from non-csqc-visible entities. The entity will need to be within the player's pvs. fieldnum should be one of the GE_ constants. */
#endif
string(string in) uri_escape = #510; /* Part of DP_QC_URI_ESCAPE*/
string(string in) uri_unescape = #511; /* Part of DP_QC_URI_ESCAPE*/
string(string in) uri_escape = #510; /* Part of DP_QC_URI_ESCAPE
Uses percent-encoding to encode any bytes in the input string which are not ascii alphanumeric, period, hyphen, or underscore. All other bytes will expand to eg '%20' for a single space char. This encoding scheme is compatible with http and other uris. */
string(string in) uri_unescape = #511; /* Part of DP_QC_URI_ESCAPE
Undo any percent-encoding in the input string, hopefully resulting in the same original sequence of bytes (and thus chars too). */
float(entity ent) num_for_edict = #512;
#define uri_post uri_get
float(string uril, float id, optional string postmimetype, optional string postdata) uri_get = #513; /* Part of DP_QC_URI_GET, DP_QC_URI_POST
@ -3087,8 +3151,8 @@ string(string cvarname) cvar_description = #518; /*
float(optional float timetype) gettime = #519;
#endif
#ifdef CSQC
string(float keynum) keynumtostring_omgwtf = #520;
string(string command, optional float bindmap) findkeysforcommand = #521; /*
DEP string(float keynum) keynumtostring_omgwtf = #520;
__deprecated("Does not support modifiers") string(string command, optional float bindmap) findkeysforcommand = #521; /*
Returns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE. */
string(string command, optional float bindmap) findkeysforcommandex = #0:findkeysforcommandex; /*
@ -3133,6 +3197,9 @@ float(string filename, strbuf bufhandle) buf_loadfile = #535; /*
float(filestream filehandle, strbuf bufhandle, optional float startpos, optional float numstrings) buf_writefile = #536; /*
Writes the contents of a string buffer onto the end of the supplied filehandle (you must have already used fopen). Additional optional arguments permit you to constrain the writes to a subsection of the stringbuffer. */
float(float bufhandle, string match, float matchrule, float startpos, float step) bufstr_find = #537; /*
Looks for the first occurence of the specified string in the buffer, returning its index or -1 on failure. */
#ifdef SSQC
float(optional float forcestate) physics_supported = #0:physics_supported; /*
Queries whether rigid body physics is enabled or not. CSQC and SSQC may report different values. If the force argument is specified then the engine will try to activate or release physics (returning the new state, which may fail if plugins or dlls are missing). Note that restarting the physics engine is likely to result in hitches when collision trees get generated. The state may change if a plugin is disabled mid-map. */
@ -3172,7 +3239,7 @@ vector(float vidmode, optional float forfullscreen) getresolution = #608; /*
#endif
#ifdef CSQC
string(float keynum) keynumtostring_menu = #609;
DEP string(float keynum) keynumtostring_menu = #609;
#endif
#ifdef MENU
string(float keynum) keynumtostring = #609; /*
@ -3433,4 +3500,7 @@ accessor filestream : float
inline set string = {fputs(this,value);};
};
#endif
#undef DEP_CSQC
#undef FTEDEP
#undef DEP
#pragma noref 0

View File

@ -1,36 +1,28 @@
#pragma progs_dat "../menu.dat"
//#pragma target fte
#pragma target fte
#define MENU //select the module
#define MENU //select the module
#includelist
fteextensions.qc //also sets up system defs
fteextensions.qc //also sets up system defs
menusys/mitems.qc //root item type
menusys/mitems_common.qc //basic types
menusys/mitem_desktop.qc //other sort of root item
menusys/mitem_exmenu.qc //fullscreen/exclusive menus
menusys/mitem_edittext.qc //simple text editor
menusys/mitem_tabs.qc //tabs
menusys/mitem_colours.qc //colour picker
menusys/mitem_checkbox.qc //checkbox (boolean thingies)
menusys/mitem_slider.qc //scrollbars
menusys/mitem_combo.qc //multiple-choice thingies
menusys/mitem_bind.qc //key binding thingie
menusys/mitems.qc //root item type
menusys/mitems_common.qc //basic types
menusys/mitem_desktop.qc //other sort of root item
menusys/mitem_exmenu.qc //fullscreen/exclusive menus
menusys/mitem_edittext.qc //simple text editor
menusys/mitem_tabs.qc //tabs
menusys/mitem_colours.qc //colour picker
menusys/mitem_checkbox.qc //checkbox (boolean thingies)
menusys/mitem_slider.qc //scrollbars
menusys/mitem_combo.qc //multiple-choice thingies
menusys/mitem_bind.qc //key binding thingie
menusys/mitem_spinnymodel.qc //menu art
#endlist
enum
{
E_FTE,
E_QSS,
} engine; //For use ONLY with known possible cvar values.
//might as well put this here.
void(mitem_desktop desktop) M_Pop =
{
mitem it = desktop.item_kactivechild;
@ -61,6 +53,7 @@ void(mitem_desktop desktop) M_Pop =
cmd("m_servers", M_Servers, menu/servers.qc) \
cmd("m_configs", M_Configs, menu/options_configs.qc) \
cmd("m_reset", M_Reset, ) \
cmd("m_dir", M_Dir, ) \
cmd("m_preset", M_Preset, menu/presets.qc)
//make sure all the right files are included
@ -78,8 +71,9 @@ void(vector screensize) m_draw =
cltime = gettime(0);
items_draw(desktop, screensize);
};
void(float scan, float chr) m_keydown = {items_keypress(desktop, scan, chr, TRUE);};
void(float scan, float chr) m_keyup = {items_keypress(desktop, scan, chr, FALSE);};
float(float evtype, float scanx, float chary, float devid) Menu_InputEvent = {return items_keypress(desktop, evtype, scanx, chary, devid);};
void(float scan, float chr) m_keydown = {ui.mousepos = getmousepos();queryscreensize();items_keypress(desktop, IE_KEYDOWN, scan, chr, 0);}; //for DP compat.
void(float scan, float chr) m_keyup = {ui.mousepos = getmousepos();queryscreensize();items_keypress(desktop, IE_KEYUP, scan, chr, 0);}; //for DP compat.
void(float mode) m_toggle
{ //mode is stupid. 1=enable,0=disable,-1=actually toggle.
if (mode < 0)
@ -114,15 +108,10 @@ float(string cstr) m_consolecommand =
return TRUE;
}
var float autocvar_dp_workarounds_allow = TRUE;
var float autocvar_dp_workarounds_force = FALSE;
var float autocvar_dp_workarounds = FALSE;
void() m_init =
{
string e = cvar_string("pr_engine");
if (!strncmp(e, "QSS ", 4))
engine = E_QSS;
else
engine = E_FTE;
FingerprintEngine();
desktop = spawn(mitem_desktop);
@ -141,11 +130,7 @@ void() m_init =
}
//work around some dp differences/bugs.
//this check identifies one significant bug in DP.
//if anyone actually cares to fix DP, then there is no reason they cannot do so by just removing DP_QC_RENDERSCENE and then fixing anything else that arises.
if (checkextension("DP_QC_RENDER_SCENE") && !checkextension("DP_CON_SET"))
dp_workarounds = autocvar(dp_workarounds_allow, TRUE);
if (autocvar(dp_workarounds_force, FALSE))
if (autocvar(dp_workarounds, FALSE))
dp_workarounds = TRUE;
if (dp_workarounds)

View File

@ -1,270 +1,270 @@
#include "../menusys/mitem_grid.qc"
class mitem_cvargrid : mitem_grid
{
strbuf grid_buf_names; //left column (
static void(strbuf newbuf) grid_setbuf =
{
if (grid_buf_names >= 0)
buf_del(grid_buf_names);
grid_buf_names = newbuf;
grid_numchildren = buf_getsize(grid_buf_names);
item_resized();
grid_kactive = grid_numchildren?0:-1;
grid_selectionchanged(-1,grid_kactive);
};
float cursorpos;
string newval;
static void() mitem_cvargrid =
{
grid_buf_names = -1;
cursorpos = -1;
};
static void(float idx) startedit =
{
if (cursorpos < 0)
{
string v = bufstr_get(grid_buf_names, idx);
float flags = cvar_type(v);
if (!(flags&1) || (flags&32)) //(!exists || readonly). we don't bother to check for private - we can still set them (they'll just read as empty).
return;
newval = strzone(cvar_string(v));
cursorpos = strlen(newval);
}
};
static string() getdesc =
{
float idx = grid_kactive;
if (idx < 0 || idx >= grid_numchildren)
return __NULL__;
string v = bufstr_get(grid_buf_names, idx);
return cvar_description(v);
};
virtual void(vector pos, float idx) grid_draw;
virtual float(vector pos, float scan, float char, float down, float idx) grid_keypress;
virtual void(float olditem, float newitem) grid_selectionchanged;
};
void(vector pos, float idx) mitem_cvargrid::grid_draw =
{
string text = bufstr_get(grid_buf_names, idx);
string value = cvar_string(text);
string defvalue = cvar_defstring(text);
float flags;
vector col = item_rgb;
if (item_flags & IF_MFOCUSED && idx == grid_mactive)
col_z = 0;
if (item_flags & IF_KFOCUSED && idx == grid_kactive)
col_x = 0;
flags = cvar_type(text);
if (!(flags&1))
value = "<INVALID>";
// if (flags & 2) //archive
// text = strcat("^hseta^h ", text);
if (flags & 4) //private
{
if (!value) //should always be true...
#include "../menusys/mitem_grid.qc"
class mitem_cvargrid : mitem_grid
{
strbuf grid_buf_names; //left column (
static void(strbuf newbuf) grid_setbuf =
{
if (grid_buf_names >= 0)
buf_del(grid_buf_names);
grid_buf_names = newbuf;
grid_numchildren = buf_getsize(grid_buf_names);
item_resized();
grid_kactive = grid_numchildren?0:-1;
grid_selectionchanged(-1,grid_kactive);
};
float cursorpos;
string newval;
static void() mitem_cvargrid =
{
grid_buf_names = -1;
cursorpos = -1;
};
static void(float idx) startedit =
{
if (cursorpos < 0)
{
string v = bufstr_get(grid_buf_names, idx);
float flags = cvar_type(v);
if (!(flags&1) || (flags&32)) //(!exists || readonly). we don't bother to check for private - we can still set them (they'll just read as empty).
return;
newval = strzone(cvar_string(v));
cursorpos = strlen(newval);
}
};
static string() getdesc =
{
float idx = grid_kactive;
if (idx < 0 || idx >= grid_numchildren)
return __NULL__;
string v = bufstr_get(grid_buf_names, idx);
return cvar_description(v);
};
virtual void(vector pos, float idx) grid_draw;
virtual float(vector pos, float scan, float char, float down, float idx) grid_keypress;
virtual void(float olditem, float newitem) grid_selectionchanged;
};
void(vector pos, float idx) mitem_cvargrid::grid_draw =
{
string text = bufstr_get(grid_buf_names, idx);
string value = cvar_string(text);
string defvalue = cvar_defstring(text);
float flags;
vector col = item_rgb;
if (item_flags & IF_MFOCUSED && idx == grid_mactive)
col_z = 0;
if (item_flags & IF_KFOCUSED && idx == grid_kactive)
col_x = 0;
flags = cvar_type(text);
if (!(flags&1))
value = "<INVALID>";
// if (flags & 2) //archive
// text = strcat("^hseta^h ", text);
if (flags & 4) //private
{
if (!value) //should always be true...
value = "???";
if (checkextension("FTE_EXTENDEDTEXTCODES"))
value = strcat("^&FE", value); //yellow, so you know its valid is meaningless.
}
//8 means engine, or something
//16 just means it has a description. who even cares?
if (checkextension("FTE_EXTENDEDTEXTCODES"))
value = strcat("^&FE", value); //yellow, so you know its valid is meaningless.
}
//8 means engine, or something
//16 just means it has a description. who even cares?
if (flags & 32)
{
if (checkextension("FTE_EXTENDEDTEXTCODES"))
if (checkextension("FTE_EXTENDEDTEXTCODES"))
value = strcat("^&F4", value); //make it red so you know its pointless trying to change it.
}
if (idx == grid_kactive && cursorpos >= 0)
{ //we're editing...
value = newval;
if (value == defvalue)
col *= 0.5;
if (((cltime*4)&1) && (item_flags & IF_KFOCUSED))
value = strcat(substring(value, 0, cursorpos), chr2str(0xe00b), substring(value, cursorpos+1, -1)); //replace the char with a box... ugly, whatever
}
else if (value == defvalue)
col *= 0.5;
vector valuepos = [pos_x+item_size_x/2, pos_y];
pos_x = valuepos_x - stringwidth(text, TRUE, '1 1 0'*this.item_scale) - 8;
valuepos_x += 1;
ui.drawstring(pos, text, '1 1 0' * item_scale, col, item_alpha, 0);
ui.drawstring(valuepos, value, '1 1 0' * item_scale, col, item_alpha, 0);
};
void(float olditem, float newitem) mitem_cvargrid::grid_selectionchanged =
{
if (olditem == newitem)
return;
if (olditem && cursorpos >= 0)
{ //we were editing... change it now.
string v = bufstr_get(grid_buf_names, olditem);
cvar_set(v, newval);
}
cursorpos = -1; //stop editing now.
newval = "";
};
float(vector pos, float scan, float char, float down, float idx) mitem_cvargrid::grid_keypress =
{
if (!down)
return FALSE;
else if (scan == K_ESCAPE)
{
if (cursorpos >= 0)
cursorpos = -1; //cancel editing, forgetting the change.
else
return FALSE;
}
else if (scan == K_ENTER)
{
if (cursorpos >= 0)
grid_selectionchanged(idx, -1);
else
startedit(idx);
}
else if (scan == K_LEFTARROW && cursorpos>=0)
cursorpos = max(cursorpos-1, 0);
else if (scan == K_RIGHTARROW && cursorpos>=0)
cursorpos+=1;
else if (scan == K_MOUSE1)
{
startedit(idx);
if (cursorpos>=0)
{
float valuepos = pos_x+(item_size_x/2)+1;
cursorpos = strlen(newval);
if (ui.mousepos[0] > valuepos-8)
while (cursorpos>0 && ui.mousepos[0] < valuepos+stringwidth(substring(newval, 0, cursorpos), TRUE, '1 1 0'*item_scale))
cursorpos--;
}
}
else if (scan == K_DEL && cursorpos<0)
{ //reset to default
string v = bufstr_get(grid_buf_names, idx);
cvar_set(v, cvar_defstring(v));
}
else if (scan == K_HOME && cursorpos>= 0)
cursorpos = 0;
else if (scan == K_END && cursorpos>= 0)
cursorpos = strlen(newval);
else if (scan == K_DEL && cursorpos>=0 && cursorpos<strlen(newval))
newval = strzone(strcat(substring(newval, 0, cursorpos), substring(newval, cursorpos+1, -1)));
else if ((scan == K_BACKSPACE || scan == K_DEL) && cursorpos>=0)
{
if (cursorpos>0)
{
newval = strzone(strcat(substring(newval, 0, cursorpos-1), substring(newval, cursorpos, -1)));
cursorpos -= 1;
}
}
else if (char >= ' ' && cursorpos>=0)
{
string ins = chr2str(char);
newval = strzone(strcat(substring(newval, 0, cursorpos), ins, substring(newval, cursorpos, -1)));
cursorpos += strlen(ins);
}
else
return FALSE;
return TRUE;
};
class cvarsmenu : mitem_exmenu
{
string filter;
float modifiedonly;
mitem_cvargrid grid;
static void(void) updatefilters =
{
strbuf b = buf_create();
buf_cvarlist(b, filter, "_*");
if (modifiedonly)
{
for (float i = buf_getsize(b); i --> 0; )
{
string v = bufstr_get(b, i);
if (cvar_string(v) == cvar_defstring(v))
bufstr_free(b, i);
else if (cvar_type(v)&32)
bufstr_free(b, i); //also remove read-only cvars. we can't reset them or anything so there's no point listing them here.
}
buf_sort(b, 0, 0); //sort the names, to clean up any gaps
}
grid.grid_setbuf(b);
};
virtual string(string key) get =
{
if (key == "*filter")
return filter;
if (key == "*modified")
return ftos(modifiedonly);
if (key == "*desc")
return grid.getdesc();
return super::get(key);
};
virtual void(string key, string newval) set =
{
if (key == "*filter")
}
if (idx == grid_kactive && cursorpos >= 0)
{ //we're editing...
value = newval;
if (value == defvalue)
col *= 0.5;
if (((cltime*4)&1) && (item_flags & IF_KFOCUSED))
value = strcat(substring(value, 0, cursorpos), chr2str(0xe00b), substring(value, cursorpos+1, -1)); //replace the char with a box... ugly, whatever
}
else if (value == defvalue)
col *= 0.5;
vector valuepos = [pos_x+item_size_x/2, pos_y];
pos_x = valuepos_x - stringwidth(text, TRUE, '1 1 0'*this.item_scale) - 8;
valuepos_x += 1;
ui.drawstring(pos, text, '1 1 0' * item_scale, col, item_alpha, 0);
ui.drawstring(valuepos, value, '1 1 0' * item_scale, col, item_alpha, 0);
};
void(float olditem, float newitem) mitem_cvargrid::grid_selectionchanged =
{
if (olditem == newitem)
return;
if (olditem && cursorpos >= 0)
{ //we were editing... change it now.
string v = bufstr_get(grid_buf_names, olditem);
cvar_set(v, newval);
}
cursorpos = -1; //stop editing now.
newval = "";
};
float(vector pos, float scan, float char, float down, float idx) mitem_cvargrid::grid_keypress =
{
if (!down)
return FALSE;
else if (scan == K_ESCAPE)
{
if (cursorpos >= 0)
cursorpos = -1; //cancel editing, forgetting the change.
else
return FALSE;
}
else if (scan == K_ENTER)
{
if (cursorpos >= 0)
grid_selectionchanged(idx, -1);
else
startedit(idx);
}
else if (scan == K_LEFTARROW && cursorpos>=0)
cursorpos = max(cursorpos-1, 0);
else if (scan == K_RIGHTARROW && cursorpos>=0)
cursorpos+=1;
else if (scan == K_MOUSE1)
{
startedit(idx);
if (cursorpos>=0)
{
float valuepos = pos_x+(item_size_x/2)+1;
cursorpos = strlen(newval);
if (ui.mousepos[0] > valuepos-8)
while (cursorpos>0 && ui.mousepos[0] < valuepos+stringwidth(substring(newval, 0, cursorpos), TRUE, '1 1 0'*item_scale))
cursorpos--;
}
}
else if (scan == K_DEL && cursorpos<0)
{ //reset to default
string v = bufstr_get(grid_buf_names, idx);
cvar_set(v, cvar_defstring(v));
}
else if (scan == K_HOME && cursorpos>= 0)
cursorpos = 0;
else if (scan == K_END && cursorpos>= 0)
cursorpos = strlen(newval);
else if (scan == K_DEL && cursorpos>=0 && cursorpos<strlen(newval))
newval = strzone(strcat(substring(newval, 0, cursorpos), substring(newval, cursorpos+1, -1)));
else if ((scan == K_BACKSPACE || scan == K_DEL) && cursorpos>=0)
{
if (cursorpos>0)
{
newval = strzone(strcat(substring(newval, 0, cursorpos-1), substring(newval, cursorpos, -1)));
cursorpos -= 1;
}
}
else if (char >= ' ' && cursorpos>=0)
{
string ins = chr2str(char);
newval = strzone(strcat(substring(newval, 0, cursorpos), ins, substring(newval, cursorpos, -1)));
cursorpos += strlen(ins);
}
else
return FALSE;
return TRUE;
};
class cvarsmenu : mitem_exmenu
{
string filter;
float modifiedonly;
mitem_cvargrid grid;
static void(void) updatefilters =
{
strbuf b = buf_create();
buf_cvarlist(b, filter, "_*");
if (modifiedonly)
{
for (float i = buf_getsize(b); i --> 0; )
{
string v = bufstr_get(b, i);
if (cvar_string(v) == cvar_defstring(v))
bufstr_free(b, i);
else if (cvar_type(v)&32)
bufstr_free(b, i); //also remove read-only cvars. we can't reset them or anything so there's no point listing them here.
}
buf_sort(b, 0, 0); //sort the names, to clean up any gaps
}
grid.grid_setbuf(b);
};
virtual string(string key) get =
{
if (key == "*filter")
return filter;
if (key == "*modified")
return ftos(modifiedonly);
if (key == "*desc")
return grid.getdesc();
return super::get(key);
};
virtual void(string key, string newval) set =
{
if (key == "*filter")
{
string old = filter;
filter = strzone(newval);
filter = strzone(newval);
if (old)
strunzone(old);
}
else if (key == "*modified")
modifiedonly = stof(newval);
else
return super::set(key, newval);
updatefilters();
};
virtual float(string key) isvalid =
{
if (key == "*filter")
return TRUE;
if (key == "*modified")
return TRUE;
return super::isvalid(key);
};
};
void(mitem_desktop desktop) M_Menu_Cvars =
{
mitem it;
float h = (480+240)/2;
//create the menu, give it focus, and make sure its displayed over everything else.
cvarsmenu m = spawn(cvarsmenu, item_text:_("Mods List"), item_flags:IF_SELECTABLE, item_command:"m_main", frame_stdheight:h);
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
//draw title art above the options
mitem_pic banner = spawn(mitem_pic, item_text:"gfx/ttl_cstm.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
m.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);
it = menuitemeditt_spawn("Cvar Filter", "*filter", '280 8');
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, h*-0.5+32], [40, 8]);
it = menuitemcheck_spawn("Modified Only", "*modified", '280 8');
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [40, h*-0.5+32], [200, 8]);
//spawn our grid for the actual options. this provides a scrollbar if we have too many items.
m.grid = spawn(mitem_cvargrid, item_flags: IF_SELECTABLE, item_scale: 8);
m.add(m.grid, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*-0.5+48], [320, h*0.5-64]);
it = spawn(mitem_label, item_text: "*desc", item_flags: 0, item_scale: 8);
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*0.5-60], [320, h*0.5]);
m.updatefilters();
//and give us a suitable menu tint too, just because.
addmenuback(m);
};
else if (key == "*modified")
modifiedonly = stof(newval);
else
return super::set(key, newval);
updatefilters();
};
virtual float(string key) isvalid =
{
if (key == "*filter")
return TRUE;
if (key == "*modified")
return TRUE;
return super::isvalid(key);
};
};
void(mitem_desktop desktop) M_Menu_Cvars =
{
mitem it;
float h = (480+240)/2;
//create the menu, give it focus, and make sure its displayed over everything else.
cvarsmenu m = spawn(cvarsmenu, item_text:_("Mods List"), item_flags:IF_SELECTABLE, item_command:"m_main", frame_stdheight:h);
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
//draw title art above the options
mitem_pic banner = spawn(mitem_pic, item_text:"gfx/ttl_cstm.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
m.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [banner.item_size_x*-0.5, h*-0.5], [banner.item_size_x*0.5, 24]);
it = menuitemeditt_spawn("Cvar Filter", "*filter", '280 8');
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, h*-0.5+32], [40, 8]);
it = menuitemcheck_spawn("Modified Only", "*modified", '280 8');
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [40, h*-0.5+32], [200, 8]);
//spawn our grid for the actual options. this provides a scrollbar if we have too many items.
m.grid = spawn(mitem_cvargrid, item_flags: IF_SELECTABLE, item_scale: 8);
m.add(m.grid, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*-0.5+48], [320, h*0.5-64]);
it = spawn(mitem_label, item_text: "*desc", item_flags: 0, item_scale: 8);
m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_OWN_MIN|RS_Y_MAX_PARENT_MID, [-160, h*0.5-60], [320, h*0.5]);
m.updatefilters();
//and give us a suitable menu tint too, just because.
addmenuback(m);
};

View File

@ -6,6 +6,25 @@ Choice of buttons available is somewhat dynamic.
There's also some generic kludge crap in here, like menu background tints
*/
enum
{
E_FTE,
E_QSS,
} engine; //For use ONLY with known possible cvar values.
void() FingerprintEngine =
{ //this is evil.
//different engines have different values/meanings for different cvars.
//basically we end up having no choice but to guess which engine we're running in, otherwise we can't tailor all the options properly.
string e = cvar_string("pr_engine");
if (strstrofs(e, "QSS")>=0)
engine = E_QSS;
else if (strstrofs(e, "FTE")>=0)
engine = E_FTE;
else
engine = E_FTE; //don't really know.
};
/*
Adds a background tint to a (typically) exmenu parent.
In FTE, we use built-in stuff to give a sepia effect.
@ -107,5 +126,11 @@ nonstatic void(mitem_desktop desktop) M_Main =
local string it = (random()<0.9)?"progs/quaddama.mdl":"progs/invulner.mdl";
m.add(spawn (mitem_spinnymodel, item_text: it), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);
#ifdef CSQC
m.add(spawn(mitem_text, item_text:strcat("CSQC: ", cvar_string("pr_engine")), item_scale:8), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX|RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX,[0,-8],[0,0]);
#else
m.add(spawn(mitem_text, item_text:strcat("MQC: ", cvar_string("pr_engine")), item_scale:8), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MAX|RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX,[0,-8],[0,0]);
#endif
addmenuback(m);
};

View File

@ -1,7 +1,211 @@
//FIXME: maxclients is a QW thing. NQ engines use maxplayers (which requires a disconnect to apply)
//maxclients is a QW thing. NQ engines use maxplayers (which requires a disconnect to apply)
#include "../menusys/mitem_grid.qc"
static string newgameinfo;
nonstatic void(mitem_desktop desktop) M_Dir =
{ //implementing this primarily so its available in QSS. :P
string path = argv(1);
searchhandle h = search_begin(path, SB_FULLPACKAGEPATH, 0);
print(sprintf("Directory listing of %S (%g files)\n", path, search_getsize(h)));
for (float i = 0; i < search_getsize(h); i++)
{
float sz = search_getfilesize(h,i);
string szfmt = "b";
if (sz > 512*1024*1024)
sz /= 1024*1024*1024, szfmt = "gb";
else if (sz > 512*1024)
sz /= 1024*1024, szfmt = "mb";
else if (sz > 512)
sz /= 1024, szfmt = "kb";
print(sprintf(" %S (%.4g %s, %s, %S)\n",
search_getfilename(h,i),
sz,szfmt,
search_getfilemtime(h,i),
search_getpackagename(h,i)
));
}
search_end(h);
};
#define FOURCC(a,b,c,d) ((int)(a)<<0i)|((int)(b)<<8i)|((int)(c)<<16i)|((int)(d)<<24i)
static string(string name) getmapdesc =
{
filestream f;
if (!checkbuiltin(fread) || !checkbuiltin(fseek) || !checkbuiltin(memalloc) || !checkbuiltin(memgetval))
return ""; //we can't do that in this engine... don't show anything, because its pointless showing the same thing twice.
else
{ //we can do it, so do it.
f = fopen(name, FILE_READ);
if (f < 0)
print(sprintf("Unable to read %s\n", name));
}
name = substring(name, 5, -5);
if (f < 0)
return name;
int *header = memalloc(sizeof(int)*4);
int bspver = 0, entofs, entlen;
if (fread(f, (void*)header, sizeof(int)*4) == sizeof(int)*4 && ((bspver=header[0]),(
bspver == FOURCC('I','B','S','P') || //IBSP (q2/q3)
bspver == FOURCC('R','B','S','P') || //RBSP (jk2o etc)
bspver == FOURCC('F','B','S','P') || //IBSP (qfusion/warsow)
bspver == 29i || //q1
bspver == 30i || //hl
bspver == FOURCC('B','S','P','2')))) //bsp2
{
if (bspver == FOURCC('I','B','S','P'))
{ //has an actual version number! ooo...
entofs = header[2];
entlen = header[3];
}
else
{
entofs = header[1];
entlen = header[2];
}
fseek(f, entofs);
string s = (string)memalloc(entlen+1);
fread(f, (void*)s, entlen);
float argc = tokenize(s);
if (argv(0) == "{")
{
for (float p = 1; p < argc; p+=2)
{
string t = argv(p);
if (t == "message")
{ //finally found the human-readable name of the map. woo.
name = argv(p+1);
break;
}
if (t == "}") //don't read the message from some kind of trigger
break;
if (t == "{") //some sort of corruption
break;
}
}
else
name = "ERROR";
memfree((void*)s);
}
else
name = sprintf("UNSUPPORTED %i", bspver);
memfree(header);
fclose(f);
return name;
};
static string(string name) packagetogamedir =
{
float so = strstrofs(name, "/");
if (so>=0)
name = substring(name, 0, so);
//don't hide id1/dm4 etc just because the user previously auto-downloaded eg qw/aerowalk
name = strtolower(name);
if (name == "qw" || name == "fte")
name = "id1";
return name;
};
class mitem_maplist : mitem_grid
{
strbuf names;
strbuf descs;
virtual void() item_remove =
{
buf_del(names);
buf_del(descs);
super::item_remove();
};
static void() mitem_maplist =
{
searchhandle searchtable;
searchtable = search_begin("maps/*.bsp", SB_FULLPACKAGEPATH, 0);
float files = search_getsize(searchtable);
if (files && checkbuiltin(search_getpackagename))
{ //find which gamedir the first entry is in (highest-priority is first)
string gd = packagetogamedir(search_getpackagename(searchtable,0));
for (float count = 1; count < files; count++)
{
string mgd = packagetogamedir(search_getpackagename(searchtable,count));
if (mgd != gd)
{ //the gamedir changed... truncate the list here.
files = count;
break;
}
}
}
grid_numchildren = 0;
names = buf_create();
for (float count = 0; count < files; count++)
{
string n = search_getfilename(searchtable, count);
string shortname = substring(n, 5, -5);
if (!strncmp(shortname, "_", 1) || !strncmp(shortname, "b_", 2))
continue; //don't add b_* names
bufstr_set(names, grid_numchildren, n);
grid_numchildren++;
}
search_end(searchtable);
buf_sort(names, 0, FALSE); //make sure they're sorted now.
descs = buf_create();
item_resized(); //FIXME: is this needed?
grid_kactive = grid_numchildren?0:-1;
grid_selectionchanged(-1,grid_kactive);
};
virtual void(vector pos, float idx) grid_draw =
{
string map = bufstr_get(names, idx);
string desc = bufstr_get(descs, idx);
if not(desc)
{
desc = map;
static float lasttime;
if (time != lasttime)
{ //fix up the name and cache it.
lasttime = time;
desc = getmapdesc(map);
bufstr_set(descs, idx, desc);
}
}
map = substring(map, 5, -5);
vector col = (map == get("map"))?[1.16, 0.54, 0.41]:'1 1 1';
if (item_flags & IF_MFOCUSED && idx == grid_mactive)
col_z = 0;
if (item_flags & IF_KFOCUSED && idx == grid_kactive)
col_x = 0;
vector valuepos = [pos_x+item_size_x/2, pos_y];
pos_x = valuepos_x - stringwidth(map, TRUE, '1 1 0'*this.item_scale) - 8;
valuepos_x += 1;
ui.drawstring(pos, map, '1 1 0' * item_scale, col, item_alpha, 0);
ui.drawstring(valuepos, desc, '1 1 0' * item_scale, col, item_alpha, 0);
};
virtual float(vector pos, float scan, float chr, float down, float idx) grid_keypress =
{
if ((scan == K_ENTER || scan == K_MOUSE1) && down)
{
string map = bufstr_get(names, idx);
map = substring(map, 5, -5);
set("map", map);
return TRUE;
}
return FALSE;
};
// virtual void(float olditem, float newitem) grid_selectionchanged;
};
class mitem_newgame : mitem_exmenu
{
virtual float(string key) isvalid =
{
return TRUE;
};
virtual string(string key) get =
{
return infoget(newgameinfo, key);
@ -24,6 +228,7 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
if (gametype == "sp")
{
//single player has no options. the start map itself gives skill+episode options.
//use 0 for maxplayers. this allows splitscreen to allocate more.
localcmd("\ndisconnect; deathmatch 0; coop 0; maxplayers 0; timelimit 0; fraglimit 0; teamplay 0; samelevel 0; startmap_sp\n");
return;
}
@ -34,13 +239,33 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
cvar_set("coop", infoget(newgameinfo, "coop"));
cvar_set("teamplay", infoget(newgameinfo, "teamplay"));
cvar_set("sv_public", infoget(newgameinfo, "sv_public"));
cvar_set("maxclients", infoget(newgameinfo, "maxclients"));
cvar_set("timelimit", infoget(newgameinfo, "timelimit"));
cvar_set("fraglimit", infoget(newgameinfo, "fraglimit"));
string map = infoget(newgameinfo, "map");
if (map == "")
map = sprintf("dm%g", floor(random(1, 6)));
localcmd(sprintf("\nmap \"%s\"\n", map));
{
if (infoget(newgameinfo, "deathmatch"))
map = sprintf("dm%g", floor(random(1, 6)));
else
map = "start";
}
if (engine == E_QSS)
{
if (stof(infoget(newgameinfo, "maxclients")) != cvar("maxplayers"))
localcmd(sprintf("\ndisconnect; maxplayers %S\n", infoget(newgameinfo, "maxclients")?:"0"));
}
else
cvar_set("maxclients", infoget(newgameinfo, "maxclients"));
//'map' acts slightly differently in different engines.
//qss: kicks everyone, resets all parms
//fte: just resets parms.
//whereas 'changelevel' works more predictably, but does mean you might get more weapons than were really intended.
if (engine == E_QSS && gametype == "warp")
localcmd(sprintf("\nchangelevel %S\n", map));
else
localcmd(sprintf("\nmap %S\n", map));
return;
}
@ -51,22 +276,33 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
newgameinfo = infoadd(newgameinfo, "deathmatch", cvar_string("deathmatch"));
newgameinfo = infoadd(newgameinfo, "teamplay", cvar_string("teamplay"));
newgameinfo = infoadd(newgameinfo, "sv_public", cvar_string("sv_public"));
newgameinfo = infoadd(newgameinfo, "maxclients", cvar_string("maxclients"));
newgameinfo = infoadd(newgameinfo, "maxclients", cvar_string((engine == E_QSS)?"maxplayers":"maxclients"));
newgameinfo = infoadd(newgameinfo, "timelimit", cvar_string("timelimit"));
newgameinfo = infoadd(newgameinfo, "fraglimit", cvar_string("fraglimit"));
newgameinfo = strzone(newgameinfo);
//create the menu
m = spawn(mitem_newgame, item_text:_("New Game"), item_flags:IF_SELECTABLE, item_command:"m_main");
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
m.totop();
#if 1
mitem_frame fr = m;
#else
//create a frame for its scrollbar.
float h = 100;
mitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE);
m.add(fr, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [-160, -h], [160, h*2]);
#endif
switch(gametype)
{
case "tdm":
case "dm":
case "coop":
case "sp":
case "warp":
break;
default:
//show game type selection
@ -78,23 +314,28 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
m.addm(spawn(mitem_text, item_text:"Deathmatch", item_command:"m_pop;m_newgame dm", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
m.addm(spawn(mitem_text, item_text:"Team Deathmatch", item_command:"m_pop;m_newgame tdm", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
m.addm(spawn(mitem_text, item_text:"Map Selection", item_command:"m_pop;m_newgame warp", item_scale:16, item_flags:IF_CENTERALIGN), [0, pos], [160, pos+16]); pos += 16;
m.add(spawn (mitem_spinnymodel, item_text: "progs/soldier.mdl",firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);
return;
}
pos = (16/-2)*(4);
pos = -100;
banner = spawn(mitem_pic, item_text:"gfx/p_multi.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
m.addm(banner, [(160-banner.item_size_x)*0.5, pos-32], [(160+banner.item_size_x)*0.5, pos-8]);
m.addm(banner, [(0-banner.item_size_x)*0.5, pos-32], [(0+banner.item_size_x)*0.5, pos-8]);
m.addm(menuitemeditt_spawn(_("Hostname"), "hostname", '280 8'), [-160, pos], [160, pos+8]); pos += 8;
m.addm(menuitemcombo_spawn(_("Public"), "sv_public", '280 8', _(
"-1 \"Reject All (Splitscreen)\" "
if (gametype != "warp")
{
fr.addm(menuitemeditt_spawn(_("Hostname"), "hostname", '280 8'), [-160, pos], [160, pos+8]); pos += 8;
fr.addm(menuitemcombo_spawn(_("Public"), "sv_public", '280 8', (engine == E_QSS)?_( "0 \"LAN\" "
"1 \"Internet\" "
):_( "-1 \"Reject All (Splitscreen)\" "
"0 \"Private (Manual IP Sharing)\" "
"1 \"Public (Manual Config)\" "
"2 \"Public (Holepunch)\" "
)), [-160, pos], [160, pos+8]); pos += 8;
m.addm(menuitemcombo_spawn(_("Max Clients"), "maxclients", '280 8', _(
fr.addm(menuitemcombo_spawn(_("Max Clients"), "maxclients", '280 8', _(
"2 \"Two\" "
"3 \"Three\" "
"4 \"Four\" "
@ -102,6 +343,7 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
"16 \"Sixteen\" "
"32 \"Thirty Two\" "
)), [-160, pos], [160, pos+8]); pos += 8;
}
if (gametype == "dm" || gametype == "tdm")
{
@ -110,7 +352,7 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
m.set("deathmatch", "1");
m.set("coop", "0");
}
m.addm(menuitemcombo_spawn(_("Deathmatch Mode"), "deathmatch", '280 8', _(
fr.addm(menuitemcombo_spawn(_("Deathmatch Mode"), "deathmatch", '280 8', _(
"1 \"Weapons Respawn\" "
"2 \"Weapons Stay\" "
"3 \"Powerups Respawn\" "
@ -136,14 +378,18 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
m.set("teamplay", "0");
}
if (gametype == "coop")
m.addm(menuitemcheck_spawn(_("No Friendly Fire"), "teamplay", '280 8'), [-160, pos], [160, pos+8]); pos += 8;
fr.addm(menuitemcheck_spawn(_("No Friendly Fire"), "teamplay", '280 8'), [-160, pos], [160, pos+8]), pos += 8;
// if (gametype == "dm" || gametype == "tdm")
if (gametype == "coop")
m.set("map", "start");
else
{
m.addm(menuitemcombo_spawn(_("Time Limit"), "timelimit", '280 8', _(
m.set("map", "");
if (gametype != "warp")
{
fr.addm(menuitemcombo_spawn(_("Time Limit"), "timelimit", '280 8', _(
"0 \"No Limit\" "
"5 \"5 minutes\" "
"10 \"10 minutes\" "
@ -158,7 +404,7 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
"55 \"55 minutes\" "
"60 \"1 hour\" "
)), [-160, pos], [160, pos+8]); pos += 8;
m.addm(menuitemcombo_spawn(_("Frag Limit"), "fraglimit", '280 8', _(
fr.addm(menuitemcombo_spawn(_("Frag Limit"), "fraglimit", '280 8', _(
"0 \"No Limit\" "
"10 \"10 frags\" "
"20 \"20 frags\" "
@ -171,57 +417,24 @@ nonstatic void(mitem_desktop desktop) M_NewGame =
"90 \"90 frags\" "
"100 \"100 frags\" "
)), [-160, pos], [160, pos+8]); pos += 8;
}
m.set("map", "");
m.addm(menuitemcombo_spawn(_("Initial Map"), "map", '280 8', _(
"dm1 \"DM1 (dm1)\" "
"dm2 \"DM2 (dm2)\" "
"dm3 \"DM3 (dm3)\" "
"dm4 \"DM4 (dm4)\" "
"dm5 \"DM5 (dm5)\" "
"dm6 \"DM6 (dm6)\" "
"start \"Start (Introduction)\" "
"e1m1 \"E1M1 (The Slipgate Complex)\" "
"e1m2 \"E1M2 (Castle Of The Damned)\" "
"e1m3 \"E1M3 (The Necropolis)\" "
"e1m4 \"E1M4 (The Grisly Grotto)\" "
"e1m5 \"E1M5 (Gloom Keep)\" "
"e1m6 \"E1M6 (The Door To Chthon)\" "
"e1m7 \"E1M7 (The House Of Chthon)\" "
"e1m8 \"E1M8 (Ziggarat Vertigo)\" "
"e2m1 \"E2M1 (The Installation)\" "
"e2m2 \"E2M2 (The Ogre Citadel)\" "
"e2m3 \"E2M3 (The Crypt Of Decay)\" "
"e2m4 \"E2M4 (The Ebon Fortress)\" "
"e2m5 \"E2M5 (The Wizard's Manse)\" "
"e2m6 \"E2M6 (The Dismal Oubliette\" "
"e2m7 \"E2M7 (The Underearth)\" "
"e3m1 \"E3M1 (Termination Central)\" "
"e3m2 \"E3M2 (The Vaults Of Zin)\" "
"e3m3 \"E3M3 (The Tomb Of Terror)\" "
"e3m4 \"E3M4 (Satan's Dark Delight)\" "
"e3m5 \"E3M5 (The Wind Tunnels)\" "
"e3m6 \"E3M6 (Chambers Of Torment)\" "
"e3m7 \"E3M7 (Tha Haunted Halls)\" "
"e4m1 \"E4M1 (The Sewage System)\" "
"e4m2 \"E4M2 (The Tower Of Despair)\" "
"e4m3 \"E4M3 (The Elder God Shrine)\" "
"e4m4 \"E4M4 (The Palace Of Hate)\" "
"e4m5 \"E4M5 (Hell's Atrium)\" "
"e4m6 \"E4M6 (The Pain Maze)\" "
"e4m7 \"E4M7 (Azure Agony)\" "
"e4m8 \"E4M8 (The Nameless City)\" "
"end \"End (Shub-Niggurath's Pit)\" "
)), [-160, pos], [160, pos+8]); pos += 8;
pos += 8;
fr.addm(spawn(mitem_maplist, item_scale:8, item_flags:IF_CENTERALIGN|IF_SELECTABLE), [-160, pos], [150, 100-16-8]), pos = 100-16;
}
m.addm(spawn(mitem_text, item_text:"BEGIN!", item_command:"m_pop;m_newgame begin", item_scale:16, item_flags:IF_CENTERALIGN), [-160, pos], [160, pos+16]);
fr.addm(spawn(mitem_text, item_text:"BEGIN!", item_command:"m_pop;m_newgame begin", item_scale:16, item_flags:IF_CENTERALIGN), [-160, pos], [160, pos+16]);pos += 16;
//random art for style
if (gametype == "coop")
{
m.add(spawn (mitem_spinnymodel, item_text: "progs/soldier.mdl", firstframe:73, framecount:8, shootframe:81, shootframes:9), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, -240/2], [0, 240/2]);
}
else
{
m.add(spawn (mitem_spinnymodel, item_text: "progs/player.mdl", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, 12*-16/2], [0, 12*16/2]);
}
addmenuback(m);
};

View File

@ -172,10 +172,7 @@ nonstatic void(mitem_desktop desktop) M_Options_Basic =
else
fr.add(menuitemcheck_spawn(_("Crosshair"), "crosshair", '280 8'), fl, [0, pos], [0, 8]), pos += 8;
if (engine==E_QSS) //lerps are kinda broken so don't do the running+shooting+spinning thing.
m.add(spawn (mitem_playerpreview, item_text: "progs/player.mdl", firstframe:12, framecount:5, angles_y:random(90,270)), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-200, 12*-16/2], [-40, 12*16/2]);
else
m.add(spawn (mitem_playerpreview, item_text: "progs/player.mdl", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-200, 12*-16/2], [-40, 12*16/2]);
m.add(spawn (mitem_playerpreview, item_text: "progs/player.mdl", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-200, 12*-16/2], [-40, 12*16/2]);
addmenuback(m);
};

View File

@ -19,8 +19,6 @@ nonstatic void(mitem_desktop desktop) M_Options_Effects =
fr.add(menuitemcheck_spawn(_("Show Framerate"), cv3("showfps", "scr_showfps", "show_fps"), '280 8'), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemcheck_spawn(_("High Res Textures"), "gl_load24bit", '280 8'), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemcheck_spawn(_("High Res Textures"), "gl_load24bit", '280 8'), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemcombo_spawn(_("Texture Mode"), "gl_texturemode", '280 8', _(
"GL_NEAREST \"Nearest\" "
"GL_NEAREST_MIPMAP_NEAREST \"Nearest (Nearest mips)\" "
@ -38,7 +36,7 @@ nonstatic void(mitem_desktop desktop) M_Options_Effects =
//"lln \"Linear (Linear mips, Unsmooth)\" "
)), fl, [0, pos], [0, 8]); pos += 8;
fr.add(menuitemcombo_spawn(_("Anistrophy"), "gl_texture_anisotropic_filtering", '280 8', _(
fr.add(menuitemcombo_spawn(_("Anistrophy"), cv2("gl_texture_anisotropy"/*qs*/, "gl_texture_anisotropic_filtering"/*fte*/), '280 8', _(
"0 \"Off\" "
"2 \"2\" "
"4 \"4\" "

View File

@ -32,12 +32,18 @@ class mitem_bind : mitem
void(vector pos) mitem_bind::item_draw =
{
#ifdef CSQC
tokenize(findkeysforcommandex(self.item_command));
string key1 = argv(0);
string key2 = argv(1);
#else
/*this is not my API...*/
tokenize(findkeysforcommand(self.item_command));
string key1 = argv(0);
string key2 = argv(1);
if (key1 != "") key1 = (key1=="-1")?"":keynumtostring(stof(key1));
if (key2 != "") key2 = (key2=="-1")?"":keynumtostring(stof(key2));
#endif
super::item_draw(pos);
pos_x += self.item_size_x / 2;
@ -87,6 +93,11 @@ float(vector pos, float scan, float char, float down) mitem_bind::item_keypress
}
if (scan == K_DEL || scan == K_BACKSPACE)
{
#ifdef CSQC
float c = tokenize(findkeysforcommandex(self.item_command));
for (float i = 0; i < c; i++)
localcmd(sprintf("bind \"%s\" \"\"\n", argv(i)));
#else
/*again, this is not my API...*/
tokenize(findkeysforcommand(self.item_command));
string key1 = argv(0);
@ -95,6 +106,7 @@ float(vector pos, float scan, float char, float down) mitem_bind::item_keypress
if (key2 != "") key2 = (key2=="-1")?"":keynumtostring(stof(key2));
if (key1 != "") localcmd(sprintf("bind \"%s\" \"\"\n", key1));
if (key2 != "") localcmd(sprintf("bind \"%s\" \"\"\n", key2));
#endif
return TRUE;
}
return FALSE;

View File

@ -64,8 +64,6 @@ void() mitem_combo::item_remove =
if (p)
p.item_remove();
strunzone(mstrlist);
item_text = __NULL__;
item_command = __NULL__;
super::item_remove();
};
@ -361,13 +359,11 @@ float(vector pos, float scan, float char, float down) mitem_combo::item_keypress
};
mitem_combo(string text, string command, vector sz, string valuelist) menuitemcombo_spawn =
{
mitem_combo n = spawn(mitem_combo);
mitem_combo n = spawn(mitem_combo, item_text:text, item_command:command);
n.item_scale = sz_y;
n.item_text = text;
n.item_size = sz;
n.mstrlist = strzone(valuelist);
n.item_command = command;
if (n.isvalid(command))
n.item_flags |= IF_SELECTABLE;
return n;

View File

@ -202,8 +202,8 @@ void(float seat, vector minpos, vector size) mitem_desktop::drawgame_helper =
if (mouseinbox(minpos, size))
{
ui.havemouseworld = TRUE;
ui.mouseworldnear = unproject([ui.mousepos[0], ui.mousepos[1], 0]);
ui.mouseworldfar = unproject([ui.mousepos[0], ui.mousepos[1], 100000]);
// ui.mouseworldnear = unproject([ui.mousepos[0], ui.mousepos[1], 0]);
// ui.mouseworldfar = unproject([ui.mousepos[0], ui.mousepos[1], 100000]);
}
};
#endif
@ -299,6 +299,7 @@ void(float force) items_updategrabs =
void(mitem_desktop desktop, vector screensize) items_draw =
{
ui.screensize = screensize;
queryscreensize();
#ifdef MENU
@ -335,17 +336,22 @@ void(mitem_desktop desktop, vector screensize) items_draw =
ui.oldmousepos = ui.mousepos;
};
#ifdef CSQC
//items_keypress has quite strong dimorphism. These are meant to tailored to the target's available event notifications, rather than being really rather annoying.
csqconly float(mitem_desktop desktop, float evtype, float scanx, float chary, float devid) items_keypress =
float(mitem_desktop desktop, float evtype, float scanx, float chary, float devid) items_keypress =
{
local float result = FALSE;
vector pos;
mitem p;
switch(evtype)
{
#define IE_PASTE 8
case IE_PASTE: //down, with JUST char info.
case IE_KEYDOWN:
case IE_KEYUP:
if (scanx == K_LCTRL)
ui.ctrlheld = ((evtype==IE_KEYDOWN)?ui.ctrlheld|1:ui.ctrlheld&~1);
if (scanx == K_RCTRL)
ui.ctrlheld = ((evtype==IE_KEYDOWN)?ui.ctrlheld|2:ui.ctrlheld&~2);
if (scanx == K_LSHIFT)
ui.shiftheld = ((evtype==IE_KEYDOWN)?ui.shiftheld|1:ui.shiftheld&~1);
if (scanx == K_RSHIFT)
@ -379,13 +385,13 @@ csqconly float(mitem_desktop desktop, float evtype, float scanx, float chary, fl
pos = '0 0 0';
for (p = ui.kgrabs; p; p = p.item_parent)
pos += p.item_position;
result = ui.kgrabs.item_keypress(pos, scanx, chary, (evtype == IE_KEYDOWN)|2);
result = ui.kgrabs.item_keypress(pos, scanx, chary, (evtype == IE_KEYDOWN||evtype == IE_PASTE)|2);
if (result & 2)
return result & 1;
}
}
if (desktop && desktop.item_keypress)
result = desktop.item_keypress(desktop.item_position, scanx, chary, evtype == IE_KEYDOWN);
result = desktop.item_keypress(desktop.item_position, scanx, chary, evtype == IE_KEYDOWN||evtype == IE_PASTE);
if (scanx >= K_MOUSE1 && scanx <= K_MOUSE5)
{
if (evtype == IE_KEYDOWN)
@ -412,57 +418,5 @@ csqconly float(mitem_desktop desktop, float evtype, float scanx, float chary, fl
}
return result;
};
#endif
#ifdef MENU
menuonly float(mitem_desktop desktop, float scan, float char, float down) items_keypress =
{
local float result = FALSE;
local vector pos;
local mitem p;
ui.mousepos = getmousepos();
queryscreensize();
#ifdef HEIRACHYDEBUG
if (scan == K_F1 && down)
{
mitem_printtree(desktop, "items_keypress", __LINE__);
return TRUE;
}
#endif
if (scan >= K_MOUSE1 && scan <= K_MOUSE5)
{
if (ui.mgrabs)
{
pos = '0 0 0';
for (p = ui.mgrabs; p; p = p.item_parent)
pos += p.item_position;
result = ui.mgrabs.item_keypress(pos, scan, char, (down)|2);
if (result & 2)
{
ui.mousedown = 0;
return result & 1;
}
}
}
else
{
if (ui.kgrabs)
{
pos = '0 0 0';
for (p = ui.kgrabs; p; p = p.item_parent)
pos += p.item_position;
result = ui.kgrabs.item_keypress(pos, scan, char, (down)|2);
if (result & 2)
return result & 1;
}
}
if (desktop && desktop.item_keypress)
result = desktop.item_keypress(desktop.item_position, scan, char, down);
return result;
};
#endif

View File

@ -9,6 +9,11 @@ class mitem_edit : mitem
virtual void() item_remove;
float spos;
void() mitem_edit =
{
item_text = strzone(item_text);
item_command = strzone(item_command);
};
virtual void() item_resized =
{
if (isvalid(item_command))
@ -47,7 +52,7 @@ void(vector pos) mitem_edit::item_draw =
curval = strcat(substring(curval, 0, spos), chr2str(0xe00b), substring(curval, spos+1, -1)); //replace the char with a box... ugly, whatever
ui.drawstring(pos, curval, '1 1 0' * item_scale, item_rgb, item_alpha, 0);
};
float(vector pos, float scan, float char, float down) mitem_edit::item_keypress =
float(vector pos, float scan, float chr, float down) mitem_edit::item_keypress =
{
if (!down)
return FALSE;
@ -83,10 +88,24 @@ float(vector pos, float scan, float char, float down) mitem_edit::item_keypress
spos -= 1;
}
}
else if (char >= ' ')
else if ((ui.shiftheld && scan == K_INS) || (ui.ctrlheld && chr == 'v'))
{ //paste
#ifndef CSQC
if (checkbuiltin(clipboard_get))
clipboard_get(0);
#endif
return TRUE;
}
else if ((ui.ctrlheld && scan == K_INS) || (ui.ctrlheld && chr == 'c'))
{ //copy
if (checkbuiltin(clipboard_set))
clipboard_set(0, curval);
return TRUE;
}
else if (chr >= ' ')
{
curval = strcat(substring(curval, 0, spos), chr2str(char), substring(curval, spos, -1));
spos += strlen(chr2str(char));
curval = strcat(substring(curval, 0, spos), chr2str(chr), substring(curval, spos, -1));
spos += strlen(chr2str(chr));
}
else
return FALSE;
@ -96,13 +115,11 @@ float(vector pos, float scan, float char, float down) mitem_edit::item_keypress
};
mitem_edit(string text, string command, vector sz) menuitemeditt_spawn =
{
mitem_edit n = spawn(mitem_edit);
mitem_edit n = spawn(mitem_edit, item_text:text, item_command:command);
n.item_scale = sz_y;
n.item_text = strzone(text);
n.item_size = sz;
n.spos = 10000000; //will be clipped so meh
n.item_command = strzone(command);
n.item_flags |= IF_SELECTABLE;
return n;
};

View File

@ -131,13 +131,12 @@ class mitem_spinnymodel : mitem
void() mitem_spinnymodel =
{
#if defined(MENU) || defined(CSQC_SIMPLE)
if (!checkextension("DP_QC_RENDER_SCENE") || dp_workarounds)
if (!checkbuiltin(renderscene))
{
item_draw = dp_draw;
return;
// item_draw = dp_draw;
// return;
}
#endif
precache_model(item_text);
setmodel(this, item_text); //use the size information from the engine, woo for unreliability.
zbias += (mins_z - maxs_z)/2 - mins_z; //center the model on its z axis, so the whole thing is visible.

View File

@ -1,5 +1,3 @@
/*all these .funcs etc need self assigned properly first, as is customary with qc*/
#ifdef MENU
#define cltime time
#endif
@ -121,6 +119,7 @@ typedef struct uiinfo_s
mitem kgrabs; //kfocused or mfocused or both or none to say what sort of grabs is in effect.
mitem mgrabs; //says who has stolen all input events.
float ctrlheld; //ctrl is held.
float shiftheld; //shift is held.
float mousedown; //which mouse buttons are currently held.
vector oldmousepos; //old mouse position, to track whether its moved.
@ -158,9 +157,7 @@ void() queryscreensize =
ui.screensize[2] = 0;
#endif
#ifdef CSQC
#ifdef CSQC_SIMPLE
ui.screensize = screensize;
#else
#ifndef CSQC_SIMPLE
//make sure the screensize is set.
normalize('0 0 1'); //when getproperty fails to return a meaningful value, make sure that we don't read some random stale value.
ui.screensize = (vector)getproperty(VF_SCREENVSIZE);