diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e2f7e45..e951b0e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/engine/client/menu.c b/engine/client/menu.c index 6836fbc3..f8de4728 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -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; diff --git a/engine/common/log.c b/engine/common/log.c index c1dd8d01..8e92dc17 100644 --- a/engine/common/log.c +++ b/engine/common/log.c @@ -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]); diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index e6c4d663..99e8eaa6 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -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 { diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index ac4635fa..4c6660c5 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -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)) diff --git a/plugins/openxr.c b/plugins/openxr.c index bd8998d4..da5a179d 100644 --- a/plugins/openxr.c +++ b/plugins/openxr.c @@ -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) diff --git a/quakec/menusys/cs/entrypoints.qc b/quakec/menusys/cs/entrypoints.qc index 49d3ee56..29cd98c3 100644 --- a/quakec/menusys/cs/entrypoints.qc +++ b/quakec/menusys/cs/entrypoints.qc @@ -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 +}; \ No newline at end of file diff --git a/quakec/menusys/csprogs.src b/quakec/menusys/csprogs.src index e73fa1db..3157cbe0 100644 --- a/quakec/menusys/csprogs.src +++ b/quakec/menusys/csprogs.src @@ -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" diff --git a/quakec/menusys/fteextensions.qc b/quakec/menusys/fteextensions.qc index 1e8b0ea6..bef42bfa 100644 --- a/quakec/menusys/fteextensions.qc +++ b/quakec/menusys/fteextensions.qc @@ -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 diff --git a/quakec/menusys/menu.src b/quakec/menusys/menu.src index d772b9e3..dba86e0d 100644 --- a/quakec/menusys/menu.src +++ b/quakec/menusys/menu.src @@ -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) diff --git a/quakec/menusys/menu/cvars.qc b/quakec/menusys/menu/cvars.qc index 2645aab5..ddf675c9 100644 --- a/quakec/menusys/menu/cvars.qc +++ b/quakec/menusys/menu/cvars.qc @@ -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 = ""; -// 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 = ""; +// 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=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=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); +}; diff --git a/quakec/menusys/menu/main.qc b/quakec/menusys/menu/main.qc index 2596d21a..2b9f6d35 100644 --- a/quakec/menusys/menu/main.qc +++ b/quakec/menusys/menu/main.qc @@ -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); }; diff --git a/quakec/menusys/menu/newgame.qc b/quakec/menusys/menu/newgame.qc index faf58f1a..81194ba6 100644 --- a/quakec/menusys/menu/newgame.qc +++ b/quakec/menusys/menu/newgame.qc @@ -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); }; diff --git a/quakec/menusys/menu/options_basic.qc b/quakec/menusys/menu/options_basic.qc index 02445c4a..d2e3984f 100644 --- a/quakec/menusys/menu/options_basic.qc +++ b/quakec/menusys/menu/options_basic.qc @@ -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); }; diff --git a/quakec/menusys/menu/options_effects.qc b/quakec/menusys/menu/options_effects.qc index b85fecb6..42a1f76c 100644 --- a/quakec/menusys/menu/options_effects.qc +++ b/quakec/menusys/menu/options_effects.qc @@ -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\" " diff --git a/quakec/menusys/menusys/mitem_bind.qc b/quakec/menusys/menusys/mitem_bind.qc index 08ef94b7..74a047e8 100644 --- a/quakec/menusys/menusys/mitem_bind.qc +++ b/quakec/menusys/menusys/mitem_bind.qc @@ -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; diff --git a/quakec/menusys/menusys/mitem_combo.qc b/quakec/menusys/menusys/mitem_combo.qc index daadf9b1..c40fc18d 100644 --- a/quakec/menusys/menusys/mitem_combo.qc +++ b/quakec/menusys/menusys/mitem_combo.qc @@ -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; diff --git a/quakec/menusys/menusys/mitem_desktop.qc b/quakec/menusys/menusys/mitem_desktop.qc index ac3d5078..b7b16c1a 100644 --- a/quakec/menusys/menusys/mitem_desktop.qc +++ b/quakec/menusys/menusys/mitem_desktop.qc @@ -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 diff --git a/quakec/menusys/menusys/mitem_edittext.qc b/quakec/menusys/menusys/mitem_edittext.qc index 4b9195bf..964aa328 100644 --- a/quakec/menusys/menusys/mitem_edittext.qc +++ b/quakec/menusys/menusys/mitem_edittext.qc @@ -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; }; diff --git a/quakec/menusys/menusys/mitem_spinnymodel.qc b/quakec/menusys/menusys/mitem_spinnymodel.qc index 6d1851de..aa5bc90d 100644 --- a/quakec/menusys/menusys/mitem_spinnymodel.qc +++ b/quakec/menusys/menusys/mitem_spinnymodel.qc @@ -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. diff --git a/quakec/menusys/menusys/mitems.qc b/quakec/menusys/menusys/mitems.qc index 2661fec1..93f68af4 100644 --- a/quakec/menusys/menusys/mitems.qc +++ b/quakec/menusys/menusys/mitems.qc @@ -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);