From 81b270db70a660690a29e4aa59573621a7baeb1c Mon Sep 17 00:00:00 2001 From: Spoike Date: Sun, 19 Apr 2020 03:56:01 +0000 Subject: [PATCH] Add mods menu and all-cvars menu to menusys. combo widgets now have sliders on dropdown lists. text edit widgets accept mouse-clicks to move the cursor. Fixed a bug with audio device selection. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5671 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- CMakeLists.txt | 3 + ftechrootbuild.sh | 52 ++ quakec/menusys/fteextensions.qc | 975 +++++++++++++++++------ quakec/menusys/menu.src | 38 +- quakec/menusys/menu/cvars.qc | 265 ++++++ quakec/menusys/menu/main.qc | 10 +- quakec/menusys/menu/mods.qc | 77 ++ quakec/menusys/menu/options.qc | 8 +- quakec/menusys/menu/options_audio.qc | 38 +- quakec/menusys/menu/options_hud.qc | 2 +- quakec/menusys/menu/updates.qc | 184 +++++ quakec/menusys/menusys/mitem_combo.qc | 177 ++-- quakec/menusys/menusys/mitem_desktop.qc | 7 +- quakec/menusys/menusys/mitem_edittext.qc | 25 +- quakec/menusys/menusys/mitem_frame.qc | 73 +- quakec/menusys/menusys/mitem_grid.qc | 210 +++++ quakec/menusys/menusys/mitems.qc | 45 +- quakec/menusys/menusys/mitems_common.qc | 112 +++ 18 files changed, 1954 insertions(+), 347 deletions(-) create mode 100644 ftechrootbuild.sh create mode 100644 quakec/menusys/menu/cvars.qc create mode 100644 quakec/menusys/menu/mods.qc create mode 100644 quakec/menusys/menu/updates.qc create mode 100644 quakec/menusys/menusys/mitem_grid.qc diff --git a/CMakeLists.txt b/CMakeLists.txt index 26a7a4ec7..fdf01cfdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1126,6 +1126,9 @@ ADD_CUSTOM_TARGET(menusys ALL quakec/menusys/menu/presets.qc quakec/menusys/menu/servers.qc quakec/menusys/menu/main.qc + quakec/menusys/menu/mods.qc + quakec/menusys/menu/cvars.qc + quakec/menusys/menu/updates.qc quakec/menusys/menu/options_audio.qc quakec/menusys/menu/options_configs.qc quakec/menusys/menu/options_hud.qc diff --git a/ftechrootbuild.sh b/ftechrootbuild.sh new file mode 100644 index 000000000..7270733fe --- /dev/null +++ b/ftechrootbuild.sh @@ -0,0 +1,52 @@ +#!/bin/sh +#Script to set up a debian chroot suitable for compiling fte's public builds. +#deterministic builds are attempted but also requires: +# gcc/etc versions must match exactly (we use debian oldstable, which should reduce issues...) +# sourcecode must be unmodified, particuarly if 'svnversion' reports modified even in an irrelevant file then embedded revision numbers will be wrong. +# third party dependancies need to work and not get messed up (either me failing to re-run makelibs, random wget failures, or outdated revisions being removed from public access) +# obtained sourcecode revision must match the binary you're trying to duplicate (pre-5601 will insist on updating to latest svn (which may not even have a public build), so expect problems trying to duplicate older builds when the scripts instead try to grab the most recent build). +#for regular use you should probably set up schroot so you don't need to remember so many args +#requires about 2.3gb for the chroot+win64 build. +#android and emscripten targets require proper mounting of /proc and /dev and are NOT supported by this script. don't try enabling them + +FTEBUILD=/tmp/ftebuild #change freely +CHUID=1000 #should generally be your non-root user id, giving you the same access in or out of the chroot... +CHUSER=spike #sadly this matters. youll just need to pretend to be me inside your chroot for now. +DEBIANMIRROR=http://ftp.uk.debian.org/debian/ +DEBIANVERSION=stretch #oldstable now... should update to stable, but paranoid to update due to portability of glibc symbols. +LANG= #no language packs installed, so would be spammy if the following rules inherit the host lang +#FTEREVISON="-r 5601" #earlier than 5601 will fail (scripts will try to update to latest) +#THREADS="-j 8" #override number of threads to compile with, if you have a decent cpu. + +#package lists +#general packages required to get the build system working (python+unzip+etc is for third-party dependancies) +GENERALPACKAGES= subversion build-essential automake ca-certificates unzip p7zip-full zip libtool python pkg-config +#package list needed to crosscompile for windows +WINTARGETPACKAGES= mingw-w64 +#dev packages required to compile the linux build properly. Comment out for less downloads/diskusage +LINUXTARGETPACKAGES= gcc-multilib g++-multilib mesa-common-dev libasound2-dev libxcursor-dev libgnutls28-dev + +#NOTE: chroot does NOT wipe all environment settings. some get carried over. This is a problem if your distro has a default PATH that excludes the system programs on debian, so this is included to be sure. +export PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games + +#Set up our chroot (you can skip this part entirely if you're preconfigured a VM) +#make sure debootstrap is installed, without erroring out if you're not on debian-derivative (NOTE: such users will needs to manually install it first from somewhere!) +(which apt-get>/dev/null) && apt-get install --no-install-recommends debootstrap +#create the new debian chroot. it should receive the most recent versions of packages. +debootstrap $DEBIANVERSION $FTEBUILD $DEBIANMIRROR +echo "FTEBuild">$FTEBUILD/etc/debian_chroot #optional, so it shows if you decide to run a bash prompt inside the chroot. +chroot $FTEBUILD adduser --uid $CHUID $CHUSER #create a user (with a homedir), so we dont depend upon root inside the guest, where possible + +#Install the extra packages needed to build +chroot $FTEBUILD apt-get install --no-install-recommends $GENERALPACKAGES $WINTARGETPACKAGES $LINUXTARGETPACKAGES +#NOW we finally start with non-debian downloads by grabbing the FTE sourcecode +chroot $FTEBUILD su $CHUSER -c "svn checkout https://svn.code.sf.net/p/fteqw/code/trunk ~/quake/fteqw-code $FTEREVISON" #grab all the source code. +#FTE has some setup bollocks, which does some toolchain checks and such. You can choose which targets to build here. +#NOTE: the following line will download third-party packages. +chroot $FTEBUILD su $CHUSER -c "cd ~/quake/fteqw-code && ./build_setup.sh --noupdate" +#And finally the main rebuild thing. drop the --noupdate part if you want to build the latest-available revision. +chroot $FTEBUILD su $CHUSER -c "cd ~/quake/fteqw-code && ./build_wip.sh --noupdate $THREADS" + + +#to remove your chroot afterwards: +#rm --one-file-system -rf $FTEBUILD diff --git a/quakec/menusys/fteextensions.qc b/quakec/menusys/fteextensions.qc index 5d951378c..1e8b0ea62 100644 --- a/quakec/menusys/fteextensions.qc +++ b/quakec/menusys/fteextensions.qc @@ -1,7 +1,7 @@ /* -This file was automatically generated by FTE QuakeWorld v1.05 +This file was generated by FTE Quake 5668, dated 2020-04-19T01:23:32.981932Z. This file can be regenerated by issuing the following command: -pr_dumpplatform +pr_dumpplatform -o fteextensions Available options: -Ffte - target only FTE (optimations and additional extensions) -Tnq - dump specifically NQ fields @@ -14,12 +14,13 @@ Available options: */ #pragma noref 1 //#pragma flag enable logicops -#pragma warning error Q101 /*too many parms*/ -#pragma warning error Q105 /*too few parms*/ -#pragma warning error Q106 /*assignment to constant/lvalue*/ -#pragma warning error Q208 /*system crc unknown*/ -#pragma warning enable F301 /*non-utf-8 strings*/ -#pragma warning enable F302 /*uninitialised locals*/ +#pragma warning error Q101 /*too many parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/ +#pragma warning error Q105 /*too few parms. The vanilla qcc didn't validate properly, hence why fteqcc normally treats it as a warning.*/ +#pragma warning error Q106 /*assignment to constant/lvalue. Define them as var if you want to initialise something.*/ +#pragma warning error Q208 /*system crc unknown. Compatibility goes out of the window if you disable this.*/ +#pragma warning disable F211 /*system crc outdated (eg: dp's csqc). Note that this may trigger emulation.*/ +#pragma warning enable F301 /*non-utf-8 strings. Think of the foreigners! Also think of text editors that insist on screwing up your char encodings.*/ +#pragma warning enable F302 /*uninitialised locals. They usually default to 0 in qc (except in recursive functions), but its still probably a bug*/ #if !defined(CSQC) && !defined(NQSSQC) && !defined(QWSSQC)&& !defined(MENU) #ifdef QUAKEWORLD #define QWSSQC @@ -63,6 +64,7 @@ Available options: #define BX_COLOREDTEXT #define DP_CON_SET /* The 'set' console command exists, and can be used to create/set cvars. */ #define DP_CON_SETA /* The 'seta' console command exists, like the 'set' command, but also marks the cvar for archiving, allowing it to be written into the user's config. Use this command in your default.cfg file. */ +#define DP_CSQC_ROTATEMOVES #define DP_EF_ADDITIVE #define DP_EF_BLUE #define DP_EF_FULLBRIGHT @@ -76,13 +78,13 @@ Available options: #define DP_ENT_TRAILEFFECTNUM /* self.traileffectnum=particleeffectnum("myeffectname"); can be used to attach a particle trail to the given server entity. This is equivelent to calling trailparticles each frame. */ #define DP_ENT_VIEWMODEL #define DP_GECKO_SUPPORT +#define DP_GFX_FONTS #define DP_GFX_SKINFILES #define DP_GFX_SKYBOX #define DP_HALFLIFE_MAP_CVAR #define DP_INPUTBUTTONS #define DP_LIGHTSTYLE_STATICVALUE #define DP_LITSUPPORT -#define DP_MD3_TAGSINFO #define DP_MONSTERWALK /* MOVETYPE_WALK is valid on non-player entities. Note that only players receive acceleration etc in line with none/bounce/fly/noclip movetypes on the player, thus you will have to provide your own accelerations (incluing gravity) yourself. */ #define DP_MOVETYPEBOUNCEMISSILE #define DP_MOVETYPEFOLLOW @@ -93,6 +95,7 @@ Available options: #define DP_QC_CVAR_DEFSTRING #define DP_QC_CVAR_STRING #define DP_QC_CVAR_TYPE +#define DP_QC_DIGEST_SHA256 #define DP_QC_EDICT_NUM #define DP_QC_ENTITYDATA #define DP_QC_ETOS @@ -104,11 +107,13 @@ Available options: #define DP_QC_FS_SEARCH #define DP_QC_GETSURFACE #define DP_QC_GETSURFACEPOINTATTRIBUTE +#define DP_QC_GETTAGINFO #define DP_QC_MINMAXBOUND #define DP_QC_MULTIPLETEMPSTRINGS /* Superseded by DP_QC_UNLIMITEDTEMPSTRINGS. Functions that return a temporary string will not overwrite/destroy previous temporary strings until at least 16 strings are returned (or control returns to the engine). */ #define DP_QC_RANDOMVEC #define DP_QC_RENDER_SCENE /* clearscene+addentity+setviewprop+renderscene+setmodel are available to menuqc. WARNING: DP advertises this extension without actually supporting it, FTE does actually support it. */ #define DP_QC_SINCOSSQRTPOW +#define DP_QC_SPRINTF /* Provides the sprintf builtin, which allows for rich formatting along the lines of C's function with the same name. Not to be confused with QC's sprint builtin. */ #define DP_QC_STRFTIME #define DP_QC_STRING_CASE_FUNCTIONS #define DP_QC_STRINGBUFFERS @@ -131,11 +136,13 @@ Available options: #define DP_QUAKE2_SPRITE #define DP_QUAKE3_MODEL #define DP_REGISTERCVAR +#define DP_SND_SOUND7_WIP2 #define DP_SND_STEREOWAV #define DP_SND_OGGVORBIS #define DP_SOLIDCORPSE #define DP_SPRITE32 #define DP_SV_BOTCLIENT +#define DP_SV_CLIENTCAMERA /* Works like svc_setview except also handles pvs. */ #define DP_SV_CLIENTCOLORS /* Provided only for compatibility with DP. */ #define DP_SV_CLIENTNAME /* Provided only for compatibility with DP. */ #define DP_SV_DRAWONLYTOCLIENT @@ -146,6 +153,7 @@ Available options: #define DP_SV_PLAYERPHYSICS /* Allows reworking parts of NQ player physics. USE AT OWN RISK - this necessitates NQ physics and is thus guarenteed to break prediction. */ #define DP_SV_POINTSOUND #define DP_SV_PRECACHEANYTIME /* Specifies that the various precache builtins can be called at any time. WARNING: precaches are sent reliably while sound events, modelindexes, and particle events are not. This can mean sounds and particles might not work the first time around, or models may take a while to appear (after the reliables are received and the model is loaded from disk). Always attempt to precache a little in advance in order to reduce these issues (preferably at the start of the map...) */ +#define DP_SV_PRINT /* Says that the print builtin can be used from nqssqc (as well as just csqc), bypassing the developer cvar issues. */ #define DP_SV_SETCOLOR #define DP_SV_SPAWNFUNC_PREFIX #define DP_SV_WRITEPICTURE @@ -154,6 +162,8 @@ Available options: #define DP_TE_CUSTOMFLASH #define DP_TE_EXPLOSIONRGB #define DP_TE_PARTICLECUBE +#define DP_TE_PARTICLERAIN +#define DP_TE_PARTICLESNOW #define DP_TE_SMALLFLASH #define DP_TE_SPARK #define DP_TE_STANDARDEFFECTBUILTINS @@ -166,10 +176,13 @@ Available options: #define FTE_CALLTIMEOFDAY /* Replication of mvdsv functionality (call calltimeofday to cause 'timeofday' to be called, with arguments that can be saved off to a global). Generally strftime is simpler to use. */ #define FTE_CSQC_ALTCONSOLES /* The engine tracks multiple consoles. These may or may not be directly visible to the user. */ #define FTE_CSQC_BASEFRAME /* Specifies that .basebone, .baseframe2, .baselerpfrac, baseframe1time, etc exist in csqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations. */ -#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */ -#define FTE_CSQC_SERVERBROWSER +#define FTE_CSQC_HALFLIFE_MODELS +#define FTE_CSQC_SERVERBROWSER /* Provides builtins to query the engine's serverbrowser servers list from ssqc. Note that these builtins are always available in menuqc. */ #define FTE_CSQC_SKELETONOBJECTS /* Provides container objects for skeletal bone data, which can be modified on a per bone basis if needed. This allows you to dynamically generate animations (or just blend them with greater customisation) instead of being limited to a single animation or two. */ +#define FTE_CSQC_RAWIMAGES /* Provides raw rgba image access to csqc. With this, the csprogs can read textures into qc-accessible memory, modify it, and then upload it to the renderer. */ #define FTE_CSQC_RENDERTARGETS /* VF_RT_DESTCOLOUR exists and can be used to redirect any rendering to a texture instead of the screen. */ +#define FTE_CSQC_REVERB /* Specifies that the mod can create custom reverb effects. Whether they will actually be used or not depends upon the sound driver. */ +#define FTE_CSQC_WINDOWCAPTION /* Provides csqc with the ability to change the window caption as displayed when running windowed or in the task bar when switched out. */ #define FTE_ENT_SKIN_CONTENTS /* self.skin = CONTENTS_WATER; makes a brush entity into water. use -16 for a ladder. */ #define FTE_ENT_UNIQUESPAWNID #define FTE_EXTENDEDTEXTCODES @@ -177,6 +190,7 @@ Available options: #define FTE_FORCEINFOKEY /* Provides an easy way to change a user's userinfo from the server. */ #define FTE_GFX_QUAKE3SHADERS /* specifies that the engine has full support for vanilla quake3 shaders */ #define FTE_GFX_REMAPSHADER /* With the raw power of stuffcmds, the r_remapshader console command is exposed! This mystical command can be used to remap any shader to another. Remapped shaders that specify $diffuse etc in some form will inherit the textures implied by the surface. */ +#define FTE_GFX_MODELEVENTS /* Provides a query for per-animation events in model files, including from progs/foo.mdl.events files. */ #define FTE_ISBACKBUFFERED /* Allows you to check if a client has too many reliable messages pending. */ #define FTE_MEMALLOC /* Allows dynamically allocating memory. Use pointers to access this memory. Memory will not be saved into saved games. */ #define FTE_MEDIA_CIN /* playfilm command supports q2 cin files. */ @@ -184,26 +198,39 @@ Available options: #define FTE_MULTIPROGS /* Multiple progs.dat files can be loaded inside the same qcvm. Insert new ones with addprogs inside the 'init' function, and use externvalue+externset to rewrite globals (and hook functions) to link them together. Note that the result is generally not very clean unless you carefully design for it beforehand. */ #define FTE_MULTITHREADED /* Faux multithreading, allowing multiple contexts to run in sequence. */ #define FTE_MVD_PLAYERSTATS /* In csqc, getplayerstat can be used to query any player's stats when playing back MVDs. isdemo will return 2 in this case. */ -#define FTE_NPCCHAT #define FTE_PART_SCRIPT /* Specifies that the r_particledesc cvar can be used to select a list of particle effects to load from particles/*.cfg, the format of which is documented elsewhere. */ #define FTE_PART_NAMESPACES /* Specifies that the engine can use foo.bar to load effect foo from particle description bar. When used via ssqc, this should cause the client to download whatever effects as needed. */ #define FTE_PART_NAMESPACE_EFFECTINFO /* Specifies that effectinfo.bar can load effects from effectinfo.txt for DP compatibility. */ +#define FTE_QC_BASEFRAME /* Specifies that .basebone and .baseframe exist in ssqc. These fields affect all bones in the entity's model with a lower index than the .basebone field, allowing you to give separate control to the legs of a skeletal model, without affecting the torso animations, from ssqc. */ +#define FTE_QC_FILE_BINARY /* Extends FRIK_FILE with binary read+write, as well as allowing seeking. Requires pointers. */ +#define FTE_QC_CHANGELEVEL_HUB /* Adds an extra argument to changelevel which is carried over to the next map in the 'spawnspot' global. Maps will be saved+reloaded until the extra argument is omitted again, purging all saved maps. Saved games will contain a copy of each preserved map. parm1-parm64 globals can be used, giving more space to transfer more player data. */ #define FTE_QC_CHECKCOMMAND /* Provides a way to test if a console command exists, and whether its a command/alias/cvar. Does not say anything about the expected meanings of any arguments or values. */ #define FTE_QC_CHECKPVS -#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits, it will be emulated using regular draws - this at least still avoids conflicting cursors. */ +#define FTE_QC_CROSSPRODUCT +#define FTE_QC_CUSTOMSKINS /* The engine supports the use of q3 skins, as well as the use of such skin 'files' to specify rich top+bottom colours, qw skins, geomsets, or texture composition even on non-players.. */ +#define FTE_QC_DIGEST_SHA1 +#define FTE_QC_DIGEST_SHA224 +#define FTE_QC_DIGEST_SHA384 +#define FTE_QC_DIGEST_SHA512 +#define FTE_QC_FS_SEARCH_SIZEMTIME +#define FTE_QC_HARDWARECURSORS /* setcursormode exists in both csqc+menuqc, and accepts additional arguments to specify a cursor image to use when this module has focus. If the image exceeds hardware limits (or hardware cursors are unsupported), it will be emulated using regular draws - this at least still avoids conflicting cursors as only one will ever be used, even if console+menu+csqc are all overlayed. */ #define FTE_QC_HASHTABLES /* Provides efficient string-based lookups. */ +#define FTE_QC_INFOKEY /* QuakeWorld's infokey builtin works, and reports at least name+topcolor+bottomcolor+ping(in ms)+ip(unmasked, but not always ipv4)+team(aka bottomcolor in nq). Does not require actual localinfo/serverinfo/userinfo, but they're _highly_ recommended to any engines with csqc */ #define FTE_QC_INTCONV /* Provides string<>int conversions, including hex representations. */ #define FTE_QC_MATCHCLIENTNAME +#define FTE_QC_MULTICAST /* QuakeWorld's multicast builtin works along with MSG_MULTICAST, but also with unicast support. */ #define FTE_QC_PAUSED #define FTE_QC_PERSISTENTTEMPSTRINGS /* Supersedes DP_QC_MULTIPLETEMPSTRINGS. Temp strings are garbage collected automatically, and do not expire while they're still in use. This makes strzone redundant. */ #define FTE_QC_RAGDOLL_WIP #define FTE_QC_SENDPACKET /* Allows the use of out-of-band udp packets to/from other hosts. Includes the SV_ParseConnectionlessPacket event. */ +#define FTE_QC_STUFFCMDFLAGS /* Variation on regular stuffcmd that gives control over how spectators/mvds should be treated. */ #define FTE_QC_TRACETRIGGER #define FTE_QUAKE2_CLIENT /* This engine is able to act as a quake2 client */ #define FTE_QUAKE2_SERVER /* This engine is able to act as a quake2 server */ #define FTE_QUAKE3_CLIENT /* This engine is able to act as a quake3 client */ #define FTE_QUAKE3_SERVER /* This engine is able to act as a quake3 server */ #define FTE_SOLID_LADDER /* Allows a simple trigger to remove effects of gravity (solid 20). obsolete. will prolly be removed at some point as it is not networked properly. Use FTE_ENT_SKIN_CONTENTS */ +#define FTE_SPLITSCREEN /* Client supports splitscreen, controlled via cl_splitscreen. Servers require allow_splitscreen 1 if splitscreen is to be used over the internet. Mods that use csqc will need to be aware for this to work properly. per-client networking may be problematic. */ #define FTE_SQL /* Provides sql* builtins which can be used for sql database access */ #define FTE_SQL_SQLITE /* SQL functionality is able to utilise sqlite databases */ #define FTE_STRINGS /* Extra builtins (and additional behaviour) to make string manipulation easier */ @@ -225,11 +252,22 @@ Available options: #define ZQ_MOVETYPE_NONE /* MOVETYPE_NONE works on players. */ #define ZQ_VWEP #define ZQ_QC_STRINGS /* The strings-only subset of FRIK_FILE is supported. */ + +#ifdef _ACCESSORS +accessor strbuf : float; +accessor searchhandle : float; +accessor hashtable : float; +accessor infostring : string; +accessor filestream : float; +accessor filestream : float; +#else #define strbuf float #define searchhandle float #define hashtable float #define infostring string #define filestream float +#endif + entity self; /* The magic me */ #if defined(CSQC) || defined(SSQC) entity other; /* Valid in touch functions, this is the entity that we touched. */ @@ -269,7 +307,7 @@ float total_secrets; float total_monsters; float found_secrets; float killed_monsters; -float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; +float parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10, parm11, parm12, parm13, parm14, parm15, parm16; /* Player specific mod-defined values that are transferred from one map to the next. These are set by the qc inside calls to SetNewParms and SetChangeParms, and can then be read on a per-player basis after a call to the setspawnparms builtin. They are not otherwise valid. */ #endif #ifdef CSQC float intermission; @@ -297,16 +335,17 @@ float input_impulse; #endif #ifdef SSQC entity msg_entity; -void() main; -void() StartFrame; -void() PlayerPreThink; -void() PlayerPostThink; -void() ClientKill; -void() ClientConnect; -void() PutClientInServer; -void() ClientDisconnect; -void() SetNewParms; -void() SetChangeParms; +void() main; /* This function is never called, and is effectively dead code. */ +void() StartFrame; /* Called at the start of each new physics frame. Player entities may think out of sequence so try not to depend upon explicit ordering too much. */ +void() PlayerPreThink; /* With Prediction(QW compat/FTE default): Called before the player's input commands are processed. +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() 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. */ +void() SetChangeParms; /* Called for each client on map changes. Should copy various entity fields to the parm* globals. */ #endif void end_sys_globals; #if defined(CSQC) || defined(SSQC) @@ -332,7 +371,7 @@ void end_sys_globals; .vector oldorigin; /* This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck. */ .vector velocity; /* The direction and speed that the entity is moving in world space. */ .vector angles; /* The eular angles the entity is facing in, in pitch, yaw, roll order. Due to a legacy bug, mdl/iqm/etc formats use +x=UP, bsp/spr/etc formats use +x=DOWN. */ -.vector avelocity; /* The amount the entity's angles change by each frame. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy. */ +.vector avelocity; /* The amount the entity's angles change by per second. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy if you're changing more than one angle at a time. */ #endif #ifdef CSQC .float pmove_flags; @@ -397,7 +436,7 @@ void end_sys_globals; .float button1; .float button2; .float impulse; -.float fixangle; +.float fixangle; /* Forces the clientside view angles to change to the value of .angles (has some lag). If set to 1/TRUE, the server will guess whether to send a delta or an explicit angle. If 2, will always send a delta (due to lag between transmission and acknowledgement, this cannot be spammed reliably). If 3, will always send an explicit angle. */ .vector v_angle; /* The angles a player is viewing. +x is DOWN (pitch, yaw, roll) */ #endif #if defined(NQSSQC) @@ -467,6 +506,14 @@ int trace_brush_faceid; int trace_surface_id; /* 1-based. 0 if not known. */ int trace_bone_id; /* 1-based. 0 if not known. typically needs MOVE_HITMODEL. */ int trace_triangle_id; /* 1-based. 0 if not known. */ +#endif +#ifdef CSQC +int trace_networkentity; /* Repots which ssqc entnum was hit when a csqc traceline impacts an ssqc-based brush entity. */ +vector pmove_org; /* Reports the origin of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */ +vector pmove_vel; /* Reports the velocity of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */ +float pmove_onground; /* Reports the onground state of the engineside player (after prediction). Does not work when the player is a csqc-owned entity. */ +#endif +#if defined(CSQC) || defined(SSQC) 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 @@ -477,7 +524,7 @@ int serverid; /* The unique id of this server within the server cluster. */ .vector punchangle; #endif #if defined(CSQC) || defined(SSQC) -.float gravity; +.float gravity; /* Multiplier applied in addition to sv_gravity (not absolute units), to control the gravity affecting this entity specifically. */ .float hull; /* Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox. */ .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 */ @@ -485,6 +532,7 @@ int serverid; /* The unique id of this server within the server cluster. */ .float dimension_solid; /* This is the bitmask of dimensions which the entity is solid within. */ .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. */ +.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. */ .float alpha; /* The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility. */ @@ -500,8 +548,8 @@ int serverid; /* The unique id of this server within the server cluster. */ .entity tag_entity; .float tag_index; .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. */ -.vector glowmod; +.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. */ #endif @@ -522,6 +570,8 @@ int serverid; /* The unique id of this server within the server cluster. */ #endif #if defined(CSQC) || defined(SSQC) .float pitch_speed; +.float drawflags; /* Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE. */ +.float abslight; /* Allows overriding light levels. Use drawflags to state that this field should actually be used. */ .vector color; /* This affects the colour of realtime lights that were enabled via the pflags field. */ .float light_lev; /* This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field). */ .float style; /* Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights. */ @@ -529,29 +579,23 @@ int serverid; /* The unique id of this server within the server cluster. */ #endif #ifdef SSQC .float maxspeed; -.entity view2; +.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; +.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. */ -.float button3; /* DP_INPUTBUTTONS (note in qw, we set 1 to equal 3, to match zquake/fuhquake/mvdsv) */ -.float button4; -.float button5; -.float button6; -.float button7; -.float button8; -.float glow_size; -.float glow_color; -.float glow_trail; +.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. */ -.float drawflags; /* Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE. */ -.float abslight; /* Allows overriding light levels. Use drawflags to state that this field should actually be used. */ .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. */ @@ -567,16 +611,27 @@ int serverid; /* The unique id of this server within the server cluster. */ #endif #ifdef CSQC .float frame3; /* Some people just don't understand how to use framegroups... */ +.float frame3time; /* .frame3 equivelent of frame1time. */ +.float lerpfrac3; /* Weight of .frame3 - .frame's weight is automatically calculated as 1-(lerpfrac+lerpfrac3+lerpfrac4), as a result these fields should NEVER add to above 1. */ .float frame4; -.float lerpfrac3; +.float frame4time; /* .frame4 equivelent of frame1time. */ .float lerpfrac4; .float forceshader; /* Contains a shader handle used to replace all surfaces upon the entity. */ .float baseframe2; /* See basebone */ .float baseframe1time; /* See basebone */ .float baseframe2time; /* See basebone */ .float baselerpfrac; /* See basebone */ +.float bonecontrol1; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol2; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol3; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol4; /* Halflife model format bone controller. On player models, this typically affects the spine's yaw. */ +.float bonecontrol5; /* Halflife model format bone controller. This typically affects the mouth. */ +.float subblendfrac; /* Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch, or yaw, or... */ +.float subblend2frac; /* Weird animation value specific to halflife models. I've no idea what this does, probably nothing for most models. */ +.float basesubblendfrac; /* See basebone */ +.float basesubblend2frac; /* See basebone */ #endif -void(float reqid, float responsecode, string resourcebody) URI_Get_Callback; /* Called as an eventual result of the uri_get builtin. */ +void(float reqid, float responsecode, string resourcebody, int resourcebytes) URI_Get_Callback; /* Called as an eventual result of the uri_get builtin. */ #ifdef SSQC void() SpectatorConnect; /* Called when a spectator joins the game. */ void() SpectatorDisconnect; /* Called when a spectator disconnects from the game. */ @@ -588,16 +643,18 @@ 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; /* Legacy 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() 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 #ifdef CSQC void(float apilevel, string enginename, float engineversion) CSQC_Init; /* Called at startup. enginename and engineversion are arbitary hints and can take any form. enginename should be consistant between revisions, but this cannot truely be relied upon. */ -void() CSQC_WorldLoaded; /* Called after model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp. */ +void() CSQC_WorldLoaded; /* Called after the server's model+sound precaches have been executed. Gives a chance for the qc to read the entity lump from the bsp (via getentitytoken). */ void() CSQC_Shutdown; /* Specifies that the csqc is going down. Save your persistant settings here. */ void(float vwidth, float vheight, float notmenu) CSQC_UpdateView; /* Called every single video frame. The CSQC is responsible for rendering the entire screen. */ void(float vwidth, float vheight, float notmenu) CSQC_UpdateViewLoading; /* Alternative to CSQC_UpdateView, called when the engine thinks there should be a loading screen. If present, will inhibit the engine's normal loading screen, deferring to qc to draw it. */ +void(vector viewsize, float scoresshown) CSQC_DrawHud; /* Part of simple csqc, called after drawing the 3d view whenever CSQC_UpdateView is not defined. */ +void(vector viewsize, float scoresshown) CSQC_DrawScores; /* Part of simple csqc, called after CSQC_DrawHud whenever CSQC_UpdateView is not defined, and when there are no menus/console active. */ void(string msg) CSQC_Parse_StuffCmd; /* Gives the CSQC a chance to intercept stuffcmds. Use the tokenize builtin to parse the message. Unrecognised commands would normally be localcmded, but its probably better to drop unrecognised stuffcmds completely. */ float(string msg) CSQC_Parse_CenterPrint; /* Gives the CSQC a chance to intercept centerprints. Return true if you wish the engine to otherwise ignore the centerprint. */ float(float save, float take, vector inflictororg) CSQC_Parse_Damage; /* Called as a result of player.dmg_save or player.dmg_take being set on the server. @@ -607,18 +664,27 @@ void(string printmsg, float printlvl) CSQC_Parse_Print; /* Gives the CSQC a chan void() CSQC_Parse_Event; /* Called when the client receives an SVC_CGAMEPACKET. The csqc should read the data or call the error builtin if it does not recognise the message. */ float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent; /* 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. */ __used void() CSQC_Input_Frame; /* Called just before each time clientcommandframe is updated. You can edit the input_* globals in order to apply your own player inputs within csqc, which may allow you a convienient way to pass certain info to ssqc. */ -void(void) CSQC_RendererRestarted; /* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */ +void(string rendererdescription) CSQC_RendererRestarted; /* Called by the engine after the video was restarted. This serves to notify the CSQC that any render targets that it may have cached were purged, and will need to be regenerated. */ float(string cmd) CSQC_ConsoleCommand; /* Called if the user uses any console command registed via registercommand. */ -float(string text, string info) CSQC_ConsoleLink; /* Called if the user clicks a ^[text\infokey\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself. */ -void(float isnew) CSQC_Ent_Update; +float(string text, string info) CSQC_ConsoleLink; /* Called if the user clicks a ^[text\infokey\infovalue^] link. Use infoget to read/check each supported key. Return true if you wish the engine to not attempt to handle the link itself. +WARNING: link text can potentially come from other players, so be careful about what you allow to be changed. */ +void(float entnum) CSQC_Ent_Spawn; /* Clumsily defined function for compat with DP. Should call spawn, set that ent's entnum field, and return the entity inside the 'self' global which will then be used for fllowing Ent_Updates. MUST NOT PARSE ANY NETWORK DATA (which makes it kinda useless). */ +void(float isnew) CSQC_Ent_Update; /* Parses the data sent by ssqc's various SendEntity functions (must use the exact same reads as the ssqc used writes - to debug this rule more easily, you may wish to use sv_csqcdebug). 'self' provides context between frames, and self.entnum should normally report which ssqc entity . Be aware that interpolation will need to happen separately. */ void() CSQC_Ent_Remove; float(float entnum, float channel, string soundname, float vol, float attenuation, vector pos, float pitchmod, float flags) CSQC_Event_Sound; -float(string resname, string restype) CSQC_LoadResource; /* Called each time some resource is being loaded. CSQC can invoke various draw calls to provide a loading screen, until WorldLoaded is called. */ -float() CSQC_Parse_TempEntity; /* Please don't use this. Use CSQC_Parse_Event and multicasts instead. */ +float() CSQC_Parse_TempEntity; /* Please don't use this. Use CSQC_Parse_Event and multicasts instead. +The use of serverside protocol translation to handle QW vs NQ protocols mean that you're likely to end up reading slightly different data. Which is bad. +Return true to say that you fully handled the tempentity. Return false to have the client attempt to rewind the network stream and parse the message itself. */ #endif #if defined(CSQC) || defined(MENU) void(string cmdtext) GameCommand; #endif +string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) Cef_GeneratePage; /* Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are +-separated key/value pairs (use tokenizebyseparator). */ +#ifdef SSQC +string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders) HTTP_GeneratePage; /* Provides an entrypoint to generate pages for pages requested over http (sv_port_tcp+net_enable_http). Headers are +-separated key/value pairs (use tokenizebyseparator). Return __NULL__ to let the engine handle it, an empty string for a 404, and any other text for a regular 200 response. */ +#endif #if defined(CSQC) || defined(SSQC) void(float prevprogs) init; /* Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget). */ void() initents; /* Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules. */ @@ -627,15 +693,20 @@ void() initents; /* Part of FTE_MULTIPROGS. Called after fields have been finali 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). */ +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; void(float wantmode) m_toggle; float(string cmd) m_consolecommand; #endif #ifdef SSQC -float parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32; +float parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32; /* Additional spawn parms, following the same parmN theme. */ float parm33, parm34, parm35, parm36, parm37, parm38, parm39, parm40, parm41, parm42, parm43, parm44, parm45, parm46, parm47, parm48; float parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64; +string parm_string; /* Like the regular parmN globals, but preserves string contents. */ +string startspot; /* Receives the value of the second argument to changelevel from the previous map. */ var float dimension_send; /* Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero. */ //var float dimension_default = 255; /* Default dimension bitmask */ @@ -680,6 +751,7 @@ const float SOLID_BSP = 4; /* Does not collide against other SOLID_BSP entities. 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_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; const float SOLID_PHYSICS_SPHERE = 33; const float SOLID_PHYSICS_CAPSULE = 34; @@ -756,21 +828,28 @@ const float CONTENT_SLIME = -4; const float CONTENT_LAVA = -5; const float CONTENT_SKY = -6; const float CONTENT_LADDER = -16; /* If this value is assigned to a solid_bsp's .skin field, the entity will become a ladder volume. */ -const int CONTENTBIT_NONE = 0x00000000; -const int CONTENTBIT_SOLID = 0x00000001; -const int CONTENTBIT_LAVA = 0x00000008; -const int CONTENTBIT_SLIME = 0x00000010; -const int CONTENTBIT_WATER = 0x00000020; -const int CONTENTBIT_FTELADDER = 0x00004000; -const int CONTENTBIT_PLAYERCLIP = 0x00010000; -const int CONTENTBIT_MONSTERCLIP = 0x00020000; +const int CONTENTBIT_NONE = 0x00000000i; +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_PLAYERCLIP = 0x00010000i; +const int CONTENTBIT_MONSTERCLIP = 0x00020000i; const int CONTENTBIT_BODY = 0x02000000i; const int CONTENTBIT_CORPSE = 0x04000000i; -const int CONTENTBIT_Q2LADDER = 0x20000000i; -const int CONTENTBIT_SKY = 0x80000000i; -const int CONTENTBITS_POINTSOLID = (0x00000001i|0x00000002i|0x02000000i); -const int CONTENTBITS_BOXSOLID = (0x00000001i|0x00010000i|0x00000002i|0x02000000i); -const int CONTENTBITS_FLUID = (0x00000020i|0x00000010i|0x00000008i|0x80000000i); +const int CONTENTBIT_Q2LADDER = 0x20000000i; /* Content bit specific to q2bsp */ +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 */ +const int CONTENTBITS_FLUID = CONTENTBIT_WATER|CONTENTBIT_SLIME|CONTENTBIT_LAVA|CONTENTBIT_SKY; +const float SPA_POSITION = 0; /* These SPA_* constants are to specify which attribute is returned by the getsurfacepointattribute builtin */ +const float SPA_S_AXIS = 1; +const float SPA_T_AXIS = 2; +const float SPA_R_AXIS = 3; /* aka: SPA_NORMAL */ +const float SPA_TEXCOORDS0 = 4; +const float SPA_LIGHTMAP0_TEXCOORDS = 5; +const float SPA_LIGHTMAP0_COLOR = 6; const float CHAN_AUTO = 0; /* The automatic channel, play as many sounds on this channel as you want, and they'll all play, however the other channels will replace each other. */ const float CHAN_WEAPON = 1; const float CHAN_VOICE = 2; @@ -794,14 +873,10 @@ const float SOUNDFLAG_NOSPACIALISE = 4; /* The different audio channels are play #endif #if defined(CSQC) || defined(SSQC) const float SOUNDFLAG_NOREVERB = 32; /* Disables the use of underwater/reverb effects on this sound effect. */ -#endif -#ifdef SSQC -const float SOUNDFLAG_UNICAST = 256; /* The sound will be heard only by the player specified by msg_entity. */ -#endif -#if defined(CSQC) || defined(SSQC) const float SOUNDFLAG_FOLLOW = 64; /* The sound's origin will updated to follow the emitting entity. */ #endif #ifdef SSQC +const float SOUNDFLAG_UNICAST = 256; /* The sound will be sent only by the player specified by msg_entity. Spectators and related splitscreen players will also hear the sound. */ const float SOUNDFLAG_SENDVELOCITY = 512; /* The entity's current velocity will be sent to the client, only useful if doppler is enabled. */ #endif #if defined(CSQC) || defined(SSQC) @@ -821,11 +896,13 @@ const float MSG_ENTITY = 5; /* The byte(s) will be written into the entity buffe const float MULTICAST_ALL = 0; /* The multicast message is unreliably sent to all players. MULTICAST_ constants are valid arguments for the multicast builtin, which ignores the specified origin when given this constant. */ const float MULTICAST_PHS = 1; /* The multicast message is unreliably sent to only players that can potentially hear the specified origin. Its quite loose. */ const float MULTICAST_PVS = 2; /* The multicast message is unreliably sent to only players that can potentially see the specified origin. */ -const float MULTICAST_ONE = 6; /* The multicast message is unreliably sent to the player specified in the msg_entity global. The specified origin is ignored. */ +const float MULTICAST_ONE = 6; /* The multicast message is unreliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored. */ +const float MULTICAST_ONE_NOSPECS = 9; /* The multicast message is unreliably sent to the player specified in the msg_entity global. The specified origin is ignored. */ const float MULTICAST_ALL_R = 3; /* The multicast message is reliably sent to all players. The specified origin is ignored. */ const float MULTICAST_PHS_R = 4; /* The multicast message is reliably sent to only players that can potentially hear the specified origin. Players might still not receive it if they are out of range. */ const float MULTICAST_PVS_R = 5; /* The multicast message is reliably sent to only players that can potentially see the specified origin. Players might still not receive it if they cannot see the event. */ -const float MULTICAST_ONE_R = 7; /* The multicast message is reliably sent to the player specified in the msg_entity global. The specified origin is ignored */ +const float MULTICAST_ONE_R = 7; /* The multicast message is reliably sent to the player (AND ALL TRACKING SPECTATORS) specified in the msg_entity global. The specified origin is ignored */ +const float MULTICAST_ONE_R_NOSPECS = 10; /* The multicast message is reliably sent to the player specified in the msg_entity global. The specified origin is ignored */ #endif #if defined(QWSSQC) const float PRINT_LOW = 0; @@ -837,8 +914,8 @@ const float PRINT_CHAT = 3; const float PVSF_NORMALPVS = 0; /* Filter first by PVS, then filter this entity using tracelines if sv_cullentities is enabled. */ const float PVSF_NOTRACECHECK = 1; /* Filter strictly by PVS. */ const float PVSF_USEPHS = 2; /* Send if we're close enough to be able to hear this entity. */ -const float PVSF_IGNOREPVS = 3; /* Ignores pvs. This entity is visible whereever you are on the map. */ -const float PVSF_NOREMOVE = 128; /* Once visible to a client, this entity will remain visible. This can be useful for csqc and corpses. */ +const float PVSF_IGNOREPVS = 3; /* Ignores pvs. This entity is visible whereever you are on the map. Updates will be sent regardless of pvs or phs */ +const float PVSF_NOREMOVE = 128; /* Once visible to a client, this entity will remain visible. This can be useful for csqc and corpses. While this flag is set, no CSQC_Remove events will be sent for the entity, but this does NOT mean that it will still receive further updates while outside of the pvs. */ const string INFOKEY_P_IP = "ip"; /* The apparent ip address of the client. This may be a proxy's ip address. */ const string INFOKEY_P_REALIP = "realip"; /* If sv_getrealip is set, this gives the ip as determine using that algorithm. */ const string INFOKEY_P_CSQCACTIVE = "csqcactive"; /* Client has csqc enabled. CSQC ents etc will be sent to this player. */ @@ -859,6 +936,7 @@ const string INFOKEY_P_ISLAGGED = "*ismuted"; /* 1 if the player has the fakelag #if defined(CSQC) || defined(SSQC) const string INFOKEY_P_PING = "ping"; /* The player's ping time, in milliseconds. */ const string INFOKEY_P_NAME = "name"; /* The player's name. */ +const string INFOKEY_P_SPECTATOR = "*spectator"; /* Whether the player is a spectator or not. */ const string INFOKEY_P_TOPCOLOR = "topcolor"; /* The player's upper/shirt colour (palette index). */ const string INFOKEY_P_BOTTOMCOLOR = "bottomcolor"; /* The player's lower/pants/trouser colour (palette index). */ #endif @@ -884,11 +962,13 @@ const string SERVERKEY_DLSTATE = "dlstate"; /* The progress of any current downl files-remaining, total-size, unknown-sizes-flag, file-localname, file-remotename, file-percent, file-rate, file-received-bytes, file-total-bytes If the current file info is omitted, then we are waiting for a download to start. */ const string SERVERKEY_PROTOCOL = "protocol"; /* The protocol we are connected to the server with. */ -const string SERVERKEY_MAXPLAYERS = "maxplayers"; /* The protocol we are connected to the server with. */ +const string SERVERKEY_MAXPLAYERS = "maxplayers"; /* The number of player/spectator slots allocated on the server. */ #endif #ifdef SSQC -const float STUFFCMD_IGNOREINDEMO = 1; /* The protocol we are connected to the server with. */ -const float STUFFCMD_DEMOONLY = 2; /* The protocol we are connected to the server with. */ +const float STUFFCMD_IGNOREINDEMO = 1; /* This stuffcmd will NOT be written to mvds/qtv. */ +const float STUFFCMD_DEMOONLY = 2; /* This stuffcmd will ONLY be written into mvds/qtv streams. */ +const float STUFFCMD_BROADCAST = 4; /* The stuffcmd will be broadcast server-wide (according to the mvd filters). */ +const float STUFFCMD_UNRELIABLE = 8; /* The stuffcmd might not arrive. It might also get there faster than ones sent over the reliable channel. */ #endif #if defined(CSQC) || defined(SSQC) const float FL_FLY = 1; @@ -960,9 +1040,9 @@ const float EF_FLAG2 = 32; #endif #if defined(CSQC) || defined(NQSSQC) const float EF_NODRAW = 16; -const float EF_ADDITIVE = 32; /* The entity will be drawn with an additive blend. */ #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. */ const float EF_BLUE = 64; /* A blue glow */ const float EF_RED = 128; /* A red glow */ const float EF_GREEN = 262144; /* A green glow */ @@ -1062,13 +1142,19 @@ const float VF_ANGLES_Z = 18; const float VF_DRAWWORLD = 19; /* boolean. If set to 1, the engine will draw the world and static/persistant rtlights. If 0, the world will be skipped and everything will be fullbright. */ const float VF_DRAWENGINESBAR = 20; /* boolean. If set to 1, the sbar will be drawn, and viewsize will be honoured automatically. */ const float VF_DRAWCROSSHAIR = 21; /* boolean. If set to 1, the engine will draw its default crosshair. */ +#endif +#if defined(CSQC) || defined(MENU) +const float VF_MINDIST = 23; /* The distance of the near clip plane from the view position. Should generally not be <=0, as this would introduce NANs. */ +const float VF_MAXDIST = 24; /* The distance of the far clip plane from the view position. If 0, will be considered infinite. */ +#endif +#ifdef CSQC const float VF_CL_VIEWANGLES = 33; const float VF_CL_VIEWANGLES_X = 34; const float VF_CL_VIEWANGLES_Y = 35; const float VF_CL_VIEWANGLES_Z = 36; #endif #if defined(CSQC) || defined(MENU) -const float VF_PERSPECTIVE = 200; /* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport. */ +const float VF_PERSPECTIVE = 200; /* 1: regular rendering. Fov specifies the angle. 0: isometric-style. Fov specifies the number of Quake Units each side of the viewport, and mindist restrictions are removed, pvs culling should be disabled. */ #endif #ifdef CSQC #define VF_LPLAYER VF_ACTIVESEAT @@ -1084,13 +1170,37 @@ const float VF_VIEWENTITY = 206; /* Changes the RF_EXTERNALMODEL flag on entitie #endif #if defined(CSQC) || defined(MENU) const float VF_RT_DESTCOLOUR = 212; /* The texture name to write colour info into, this includes both 3d and 2d drawing. -Additional arguments are: format (rgba8=1,rgba16f=2,rgba32f=3), sizexy. +Additional arguments are: format (IMGFMT_*), sizexy. Written to by both 3d and 2d rendering. Note that any rendertarget textures may be destroyed on video mode changes or so. Shaders can name render targets by prefixing texture names with '$rt:', or $sourcecolour. */ +const float VF_RT_DESTCOLOUR1 = 213; /* Like VF_RT_DESTCOLOUR, for multiple render targets. */ +const float VF_RT_DESTCOLOUR2 = 214; /* Like VF_RT_DESTCOLOUR, for multiple render targets. */ +const float VF_RT_DESTCOLOUR3 = 215; /* Like VF_RT_DESTCOLOUR, for multiple render targets. */ const float VF_RT_SOURCECOLOUR = 209; /* The texture name to use with shaders that specify a $sourcecolour map. */ -const float VF_RT_DEPTH = 210; /* The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (16bit=4,24bit=5,32bit=6), sizexy. */ +const float VF_RT_DEPTH = 210; /* The texture name to use as a depth buffer. Also used for shaders that specify $sourcedepth. 1-based. Additional arguments are: format (IMGFMT_D*), sizexy. */ const float VF_RT_RIPPLE = 211; /* The texture name to use as a ripplemap (target for shaders with 'sort ripple'). Also used for shaders that specify $ripplemap. 1-based. Additional arguments are: format, sizexy. */ const float VF_ENVMAP = 220; /* The cubemap name to use as a fallback for $reflectcube, if a shader was unable to load one. Note that this doesn't automatically change shader permutations or anything. */ +const float VF_USERDATA = 221; /* Pointer (and byte size) to an array of vec4s. This data is then globally visible to all glsl via the w_user uniform. */ +#endif +#ifdef CSQC +const float VF_SKYROOM_CAMERA = 222; /* Controls the camera position of the skyroom (which will be drawn underneath transparent sky surfaces). This should move slightly with the real camera, but not so much that the skycamera enters walls. Requires a skyshader with a blend mode on the first pass (or no passes). */ +#endif +#if defined(CSQC) || defined(MENU) +const float VF_PROJECTIONOFFSET = 224; /* vec2 horizontal+vertical offset for the projection matrix, for weird off-centre rendering. */ +const float IMGFMT_R8G8B8A8 = 1; /* Typical 32bit rgba pixel format. */ +const float IMGFMT_R16G16B16A16F = 2; /* Half-Float pixel format. Requires gl3 support. */ +const float IMGFMT_R32G32B32A32F = 3; /* Regular Float pixel format. Requires gl3 support. */ +const float IMGFMT_D16 = 4; /* 16-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */ +const float IMGFMT_D24 = 5; /* 24-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */ +const float IMGFMT_D32 = 6; /* 32-bit depth pixel format. Must not be used with VF_RT_DESTCOLOUR*. */ +const float IMGFMT_R8 = 7; /* Single channel red-only 8bit pixel format. */ +const float IMGFMT_R16F = 8; /* Single channel red-only Half-Float pixel format. Requires gl3 support. */ +const float IMGFMT_R32F = 9; /* Single channel red-only Float pixel format. Requires gl3 support. */ +const float IMGFMT_A2B10G10R10 = 10; /* Packed 32-bit packed 10-bit colour pixel format. Requires gl3 support. */ +const float IMGFMT_R5G6B5 = 11; /* Packed 16-bit colour pixel format. */ +const float IMGFMT_R4G4B4A4 = 12; /* Packed 16-bit colour pixel format, with alpha */ +const float IMGFMT_R8G8 = 13; /* 16-bit two-channel pixel format. */ +const float IMGFMT_R32G32B32F = 14; /* A pixel format that matches QC's vector type. */ #endif #ifdef CSQC const float RF_VIEWMODEL = 1; /* Specifies that the entity is a view model, and that its origin is relative to the current view position. These entities are also subject to viewweapon bob. */ @@ -1104,14 +1214,22 @@ const float RF_ADDITIVE = 8; /* Shaders from this entity will temporarily be hac const float RF_USEAXIS = 16; /* The entity will be oriented according to the current v_forward+v_right+v_up vector values instead of the entity's .angles field. */ const float RF_NOSHADOW = 32; /* This entity will not cast shadows. Often useful on view models. */ const float RF_FRAMETIMESARESTARTTIMES = 64; /* Specifies that the frame1time, frame2time field are timestamps (denoting the start of the animation) rather than time into the animation. */ +#endif +#if defined(CSQC) || defined(MENU) const float IE_KEYDOWN = 0; /* Specifies that a key was pressed. Second argument is the scan code. Third argument is the unicode (printable) char value. Fourth argument denotes which keyboard(or mouse, if its a mouse 'scan' key) the event came from. Note that some systems may completely separate scan codes and unicode values, with a 0 value for the unspecified argument. */ const float IE_KEYUP = 1; /* Specifies that a key was released. Arguments are the same as IE_KEYDOWN. On some systems, this may be fired instantly after IE_KEYDOWN was fired. */ -const float IE_MOUSEDELTA = 2; /* Specifies that a mouse was moved (touch screens and tablets typically give IE_MOUSEABS events instead, use _windowed_mouse 0 to test code to cope with either). Second argument is the X displacement, third argument is the Y displacement. Fourth argument is which mouse or touch event triggered the event. */ +const float IE_MOUSEDELTA = 2; /* Specifies that a mouse was moved (touch screens and tablets typically give IE_MOUSEABS events instead, use in_windowed_mouse 0 to test code to cope with either). Second argument is the X displacement, third argument is the Y displacement. Fourth argument is which mouse or touch event triggered the event. */ const float IE_MOUSEABS = 3; /* Specifies that a mouse cursor or touch event was moved to a specific location relative to the virtual screen space. Second argument is the new X position, third argument is the new Y position. Fourth argument is which mouse or touch event triggered the event. */ const float IE_ACCELEROMETER = 4; const float IE_FOCUS = 5; /* Specifies that input focus was given. parama says mouse focus, paramb says keyboard focus. If either are -1, then it is unchanged. */ const float IE_JOYAXIS = 6; /* Specifies that what value a joystick/controller axis currently specifies. x=axis, y=value. Will be called multiple times, once for each axis of each active controller. */ const float IE_GYROSCOPE = 7; +const float GGDI_GAMEDIR = 0; /* Used with getgamedirinfo to query the mod's public gamedir. There is often other info that cannot be expressed with just a gamedir name, resulting in dupes or other weirdness. */ +const float GGDI_DESCRIPTION = 1; /* The human-readable title of the mod. Empty when no data is known (ie: the gamedir just contains some maps). */ +const float GGDI_OVERRIDES = 2; /* A list of settings overrides. */ +const float GGDI_LOADCOMMAND = 3; /* The console command needed to actually load the mod. */ +const float GGDI_ICON = 4; /* The mod's Icon path, ready for drawpic. */ +const float GGDI_GAMEDIRLIST = 4; /* A semi-colon delimited list of gamedirs that the mod's content can be loaded through. */ #endif #ifdef SSQC const float CLIENTTYPE_DISCONNECTED = 0; /* Return value from clienttype() builtin. This entity is a player slot that is currently empty. */ @@ -1149,6 +1267,8 @@ const float LFIELD_ROTATION = 13; const float LFIELD_DIETIME = 14; const float LFIELD_RGBDECAY = 15; const float LFIELD_RADIUSDECAY = 16; +const float LFIELD_STYLESTRING = 17; +const float LFIELD_NEARCLIP = 18; const float LFLAG_NORMALMODE = 1; const float LFLAG_REALTIMEMODE = 2; const float LFLAG_LIGHTMAP = 4; @@ -1156,6 +1276,7 @@ const float LFLAG_FLASHBLEND = 8; const float LFLAG_NOSHADOWS = 256; const float LFLAG_SHADOWMAP = 512; const float LFLAG_CREPUSCULAR = 1024; +const float LFLAG_ORTHOSUN = 2048; const float TEREDIT_RELOAD = 0; const float TEREDIT_SAVE = 1; const float TEREDIT_SETHOLE = 2; @@ -1179,9 +1300,10 @@ const float TEREDIT_MESH_KILL = 16; const float TEREDIT_TINT = 17; const float TEREDIT_RESET_SECT = 20; const float TEREDIT_RELOAD_SECT = 21; -const float TEREDIT_ENTS_WIPE = 22; -const float TEREDIT_ENTS_CONCAT = 23; -const float TEREDIT_ENTS_GET = 24; +const float TEREDIT_ENT_GET = 26; +const float TEREDIT_ENT_SET = 27; +const float TEREDIT_ENT_ADD = 28; +const float TEREDIT_ENT_COUNT = 29; #endif #if defined(CSQC) || defined(MENU) const float SLIST_HOSTCACHEVIEWCOUNT = 0; @@ -1207,18 +1329,26 @@ const float SLIST_TEST_NOTSTARTSWITH = 9; float(string ext) checkextension = #1; /* Checks if the named extension is supported by the running engine. */ -void(string err,...) error = #2; -void(string err,...) objerror = #3; -void(string text,...) print = #4; +void(string err,...) error = #2; /* + Fatal error that will trigger a crash-to-console that users will actually notice. */ + +void(string err,...) objerror = #3; /* + For some reason this has been redefined as non-fatal, and as it won't force the user to look at the console it'll generally be ignored completely so really what's the point? Other than as a convoluted way to remove(self) that is. */ + +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; -void(string text,...) cprint = #7; +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! */ + vector(vector) normalize = #8; float(vector) vlen = #9; float(vector) vectoyaw = #10; vector(vector) vectoangles = #11; float() random = #12; -void(string) localcmd = #13; +void(string,...) localcmd = #13; float(string name) cvar = #14; void(string name, string value) cvar_set = #15; void(string text) dprint = #16; @@ -1226,18 +1356,26 @@ string(float) ftos = #17; float(float) fabs = #18; string(vector) vtos = #19; string(entity) etos = #20; /* Part of DP_QC_ETOS*/ -float(string) stof = #21; /* Part of FRIK_FILE, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/ +float(string) stof = #21; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/ entity() spawn = #22; void(entity) remove = #23; entity(entity start, .string field, string match) find = #24; entity(entity start, .__variant field, __variant match) findfloat = #25; /* Part of DP_QC_FINDFLOAT*/ entity(.string field, string match) findchain = #26; /* Part of DP_QC_FINDCHAIN*/ entity(.__variant field, __variant match) findchainfloat = #27; /* Part of DP_QC_FINDCHAINFLOAT*/ -string(string file) precache_file = #28; +string(string file) precache_file = #28; /* + Attempts to download the named file from the current server, if it isn't found locally. Not very useful as menuqc is normally meant to work before joining servers too. */ + string(string sample) precache_sound = #29; -void() coredump = #30; -void() traceon = #31; -void() traceoff = #32; +void() coredump = #30; /* + Takes a dump, writing the qcvm's state to disk. There are normally easier ways to debug, but I suppose this one still beats print spam. */ + +void() traceon = #31; /* + Enables single-stepping. Its generally easier to just set a breakpoint. */ + +void() traceoff = #32; /* + Disables single-stepping. Which sucks if you started said singlestepping outside of qc. */ + void(entity) eprint = #33; float(float) rint = #34; float(float) floor = #35; @@ -1247,12 +1385,16 @@ float(float) sin = #38; /* Part of DP_QC_SINCOSSQRTPOW*/ float(float) cos = #39; /* Part of DP_QC_SINCOSSQRTPOW*/ float(float) sqrt = #40; /* Part of DP_QC_SINCOSSQRTPOW*/ vector() randomvector = #41; -float(string name, string value, float flags) registercvar = #42; /* Part of DP_REGISTERCVAR*/ +float(string name, string value, float flags) registercvar = #42; /* Part of DP_REGISTERCVAR + Creates the cvar if it didn't already exist. This presents issues for setting those cvars via startup configs of course, and autocvars are easier but I suppose they don't get any flags (which are ignored anyway, of course). */ + float(float,...) min = #43; /* Part of DP_QC_MINMAXBOUND*/ float(float,...) max = #44; /* Part of DP_QC_MINMAXBOUND*/ float(float min,float value,float max) bound = #45; /* Part of DP_QC_MINMAXBOUND*/ float(float,float) pow = #46; /* Part of DP_QC_SINCOSSQRTPOW*/ -void(entity src, entity dst) copyentity = #47; /* Part of DP_QC_COPYENTITY*/ +void(entity src, entity dst) copyentity = #47; /* Part of DP_QC_COPYENTITY + Copies all entity fields from one entity into another (forgetting any that were previously set on the destination). */ + filestream(string filename, float mode) fopen = #48; /* Part of FRIK_FILE*/ void(filestream fhandle) fclose = #49; /* Part of FRIK_FILE*/ string(filestream fhandle) fgets = #50; /* Part of FRIK_FILE*/ @@ -1261,36 +1403,77 @@ 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*/ -void(string) strunzone = #57; /* Part of FRIK_FILE, FTE_STRINGS, ZQ_QC_STRINGS*/ -float(string) tokenize = #58; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ -string(float) argv = #59; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND*/ -float() isserver = #60; -float() clientcount = #61; -float() clientstate = #62; -void(string map) changelevel = #64; -void(string sample, optional float channel, optional float volume) localsound = #65; -vector() getmousepos = #66; +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 + Exists in FTE for compat only, does nothing. */ + +float(string) tokenize = #58; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND + Splits up the given string into its different components (what constitutes a token separator is not well defined and has been hacked about with over the years so have fun with that), returning the number of tokens that were found. Call argv(0 through ret-1) to retrieve each individual token. Take care to not use this recursively. */ + +string(float) argv = #59; /* Part of KRIMZON_SV_PARSECLIENTCOMMAND + Returns one of the tokens found via tokenize (and equivelent builtins). */ + +float() isserver = #60; /* + Returns true if the local engine is running a server, and thus cvars and localcmds are shared with said server. */ + +float() clientcount = #61; /* + Returns the maximum number of players on the server. Useless if its a remote server, so its a kinda useless builtin really. */ + +float() clientstate = #62; /* + Tells you whether the client is actually connected to anything. 0 for a dedicated server (but dedicated servers don't normally run menuqc anyway), 2 if connecting or connected to a server (but not necessarily spawned+active), 1 for sitting around idle without trying to connect to anything yet. */ + +void(string map) changelevel = #64; /* + Not really any different from a localcmd, but with proper string escapes. */ + +void(string sample, optional float channel, optional float volume) localsound = #65; /* + Plays a sound, locally. precaching is optional, but recommended. */ + +vector() getmousepos = #66; /* + Obsolete. Return values depend upon the current cursor mode. Implement Menu_InputEvent instead, so you can handle deltas as-is or absolutes if that's all the OS can provide. */ + float(optional float timetype) gettime = #67; -void(string data) loadfromdata = #68; -void(string data) loadfromfile = #69; +void(string s) loadfromdata = #68; /* + Reads a set of entities from the given string. This string should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ + +void(string s) loadfromfile = #69; /* + Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ + float(float val, float m) mod = #70; -string(string name) cvar_string = #71; /* Part of DP_QC_CVAR_STRING*/ -void() crash = #72; -void() stackdump = #73; +string(string name) cvar_string = #71; /* Part of DP_QC_CVAR_STRING + Returns the value of a cvar, as a string. */ + +void() crash = #72; /* + Demonstrates that no program is bug free. */ + +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*/ float(entity) etof = #79; entity(float) ftoe = #80; -float(string) validstring = #81; -float(string str) altstr_count = #82; -string(string str) altstr_prepare = #83; -string(string str, float num) altstr_get = #84; -string(string str, float num, string set) altstr_set = #85; +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; /* + Reports how many single-quotes there were in the string, divided by 2. */ + +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; /* + Gets the Nth single-quoted token in the input. */ + +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*/ entity(.float field, float match) findchainflags = #88; /* Part of DP_QC_FINDCHAINFLAGS*/ +string(string name) cvar_defstring = #89; /* Part of DP_QC_CVAR_DEFSTRING*/ void(entity ent, string mname) setmodel = #90; /* Menuqc-specific version. */ @@ -1351,10 +1534,15 @@ entity() spawn = #14; /* Adds a brand new entity into the world! Hurrah, you're now a parent! */ void(entity e) remove = #15; /* - Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After two seconds, the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid. */ + Destroys the given entity and clears some limited fields (including model, modelindex, solid, classname). Any references to the entity following the call are an error. After half a second the entity will be reused, in the interim you can unfortunatly still read its fields to see if the reference is no longer valid. */ +#endif +void(entity e) removeinstant = #0:removeinstant; /* + Same thing as the regular remove builtin, but bypasses the half-second rule. The entity slot may be reused instantly. Be CERTAIN that you have no lingering references, because if they're followed they will end up poking an entirely different type of entity! So only use this where you're sure its safe. */ + +#if defined(CSQC) || defined(SSQC) void(vector v1, vector v2, float flags, entity ent) traceline = #16; /* - Traces an infinitely thin line through the world from v1 towards v2. + Traces a thin line through the world from v1 towards v2. Will not collide with ent, ent.owner, or any entity who's owner field refers to ent. The passed entity will also be used to determine whether to use a capsule trace, the contents that the trace should impact, and a couple of other extra fields that define the trace. There are no side effects beyond the trace_* globals being written. @@ -1373,7 +1561,8 @@ entity() checkclient = #17; /* #endif #if defined(CSQC) || defined(SSQC) 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. */ + 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). */ 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. */ @@ -1388,13 +1577,13 @@ void(entity client, string s) stuffcmd = #21; /* Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \n. This builtin is generally considered evil. */ -void(entity client, float flags, string s) stuffcmdflags = #0:stuffcmdflags; /* +void(entity client, float flags, string s) stuffcmdflags = #0:stuffcmdflags; /* Part of FTE_QC_STUFFCMDFLAGS Sends a console command (or cvar) to the client, where it will be executed. Different clients support different commands. Do NOT forget the final \n. This (just as evil) variant allows specifying some flags too. See the STUFFCMD_* constants. */ #endif #if defined(CSQC) || defined(SSQC) -entity(vector org, float rad) findradius = #22; /* +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. */ #endif @@ -1481,7 +1670,7 @@ float(float) fabs = #43; /* #endif #ifdef SSQC vector(entity player, float missilespeed) aim = #44; /* - Returns a direction vector (specifically v_forward on error). This builtin attempts to guess what pitch angle to fire projectiles at for people that don't know about mouselook. Does not affect yaw angles. */ + Returns a tweaked copy of the v_forward vector (must be set! ie: makevectors(player.v_angle) ). This is important for keyboard users (that don't want to have to look up/down the whole time), as well as joystick users (who's aim is otherwise annoyingly imprecise). Only the upwards component of the result will differ from the value of v_forward. The builtin will select the enemy closest to the crosshair within the angle of acos(sv_aim). */ #endif #if defined(CSQC) || defined(SSQC) @@ -1509,13 +1698,27 @@ vector(vector fwd, optional vector up) vectoangles = #51; /* void(float to, float val) WriteByte = #52; /* Writes a single byte into a network message buffer. Typically you will find a more correct alternative to writing arbitary data. 'to' should be one of the MSG_* constants. MSG_ONE must have msg_entity set first. */ -void(float to, float val) WriteChar = #53; -void(float to, float val) WriteShort = #54; -void(float to, float val) WriteLong = #55; -void(float to, float val) WriteCoord = #56; -void(float to, float val) WriteAngle = #57; -void(float to, string val) WriteString = #58; -void(float to, entity val) WriteEntity = #59; +void(float to, float val) WriteChar = #53; /* + Writes a signed value between -128 and 127. */ + +void(float to, float val) WriteShort = #54; /* + Writes a signed value between -32768 and 32767. As an exception, values up to 65535 will not trigger warnings (but readshort will read the result as negative!) */ + +void(float to, float val) WriteLong = #55; /* + Writes a signed 32bit integer. Note that the input argument being of float type limits the resulting integer to a mere 24 consecutive bits of validity. Use WriteInt if you want to write an entire 32bit int without data loss. */ + +void(float to, float val) WriteCoord = #56; /* + Writes a single value suitable for a map coordinate axis. The precision is not strictly specified but is assumed to be of at least 13.3 fixed-point precision (ie: +/-4k with 1/8th precision). */ + +void(float to, float val) WriteAngle = #57; /* + Writes a single value suitable for an angle axis. The precision is not strictly specified but is assumed to be 8bit, giving 256 notches instead of the assumed 360 range passed in. */ + +void(float to, string val) WriteString = #58; /* + Writes a variable-length null terminated string. There are length limits. The codepage is not translated, so be sure that client+server agree on whether utf-8 is being used or not (or just stick to ascii+markup). */ + +void(float to, entity val) WriteEntity = #59; /* + Writes the index of the specified entity (the network data size is not specified). This can be read clientside using the readentitynum builtin, with caveats. */ + #endif #if defined(CSQC) || defined(SSQC) float(float angle) sin = #60; /* Part of DP_QC_SINCOSSQRTPOW @@ -1548,7 +1751,7 @@ void(string mapname, optional string newmapstartspot) changelevel = #70; /* #endif #if defined(CSQC) || defined(SSQC) void(string cvarname, string valuetoset) cvar_set = #72; /* - Instantly sets a cvar to the given string value. */ + Instantly sets a cvar to the given string value. Warning: the resulting string includes apostrophies surrounding the result. You may wish to use sprintf instead. */ #endif #ifdef SSQC @@ -1563,18 +1766,38 @@ string(string str) precache_file2 = #77; #ifdef SSQC void(entity player) setspawnparms = #78; void(entity killer, entity killee) logfrag = #79; /* Part of QW_ENGINE*/ -string(entity e, string key) infokey = #80; /* Part of QW_ENGINE +#endif +#if defined(CSQC) || defined(SSQC) +string(entity e, string key) infokey = #80; /* Part of FTE_QC_INFOKEY, QW_ENGINE If e is world, returns the field 'key' from either the serverinfo or the localinfo. If e is a player, returns the value of 'key' from the player's userinfo string. There are a few special exceptions, like 'ip' which is not technically part of the userinfo. */ +#endif +#ifdef SSQC +float(entity e, string key) infokeyf = #0:infokeyf; /* + Identical to regular infokey, except returns a float. */ + #endif #if defined(CSQC) || defined(SSQC) -float(string) stof = #81; /* Part of FRIK_FILE, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/ +float(string) stof = #81; /* Part of FRIK_FILE, FTE_QC_INFOKEY, FTE_STRINGS, QW_ENGINE, ZQ_QC_STRINGS*/ #endif #ifdef SSQC #define unicast(pl,reli) do{msg_entity = pl; multicast('0 0 0', reli?MULITCAST_ONE_R:MULTICAST_ONE);}while(0) -void(vector where, float set) multicast = #82; /* +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 + 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 +#if defined(CSQC) || defined(SSQC) +string(float style, optional __out vector rgb) getlightstyle = #0:getlightstyle; /* + Obtains the light style string for the given style. */ + +vector(float style) getlightstylergb = #0:getlightstylergb; /* + Obtains the current rgb value of the specified light style. In csqc, this is correct with regard to the current frame, while ssqc gives no guarentees about time and ignores client cvars. Note: use getlight if you want the actual light value at a point. */ + +#endif +#ifdef SSQC void(float style, float val, optional vector rgb) lightstylestatic = #5; /* Sets the lightstyle to an explicit numerical level. From Hexen2. */ @@ -1618,7 +1841,7 @@ float(string extname) checkextension = #99; /* #endif float(__variant funcref) checkbuiltin = #0:checkbuiltin; /* - Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. */ + Checks to see if the specified builtin is supported/mapped. This is intended as a way to check for #0 functions, allowing for simple single-builtin functions. Warning, if two different engines map different builtins to the same number, then this function will not tell you which will be called, only that it won't crash (the exception being #0, which are remapped as available). */ #ifdef SSQC float(string builtinname) builtin_find = #100; /* @@ -1646,12 +1869,19 @@ void(filestream fhandle, string s, optional string s2, optional string s3, optio Writes the given string(s) into the file. For compatibility with fgets, you should ensure that the string is terminated with a \n - this will not otherwise be done for you. It is up to the engine whether dos or unix line endings are actually written. */ #endif -int(filestream fhandle, void *ptr, int size) fread = #0:fread; /* +int(filestream fhandle, void *ptr, int size) fread = #0:fread; /* Part of FTE_QC_FILE_BINARY Reads binary data out of the file. Returns truncated lengths if the read exceeds the length of the file. */ -int(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* +int(filestream fhandle, void *ptr, int size) fwrite = #0:fwrite; /* Part of FTE_QC_FILE_BINARY Writes binary data out of the file. */ +#define ftell fseek //c compat +int(filestream fhandle, optional int newoffset) fseek = #0:fseek; /* Part of FTE_QC_FILE_BINARY + Changes the current position of the file, if specified. Returns prior position, in bytes. */ + +int(filestream fhandle, optional int newsize) fsize = #0:fsize; /* Part of FTE_QC_FILE_BINARY + Reports the total size of the file, in bytes. Can also be used to truncate/extend the file */ + #if defined(CSQC) || defined(SSQC) 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*/ @@ -1671,6 +1901,13 @@ void(string cvar, float val) cvar_setf = #176; void(string soundname, optional float channel, optional float volume) localsound = #177; /* Plays a sound... locally... probably best not to call this from ssqc. Also disables reverb. */ +#endif +#ifdef SSQC +float(string soundname, float queryonly) getsoundindex = #0:getsoundindex; /* + Provides a way to query if a sound is already precached or not. The return value can also be checked for <=255 to see if it'll work over any network protocol. The sound index can also be used for writebyte hacks, but this is discouraged - use SOUNDFLAG_UNICAST instead. */ + +#endif +#if defined(CSQC) || defined(SSQC) float(string modelname, optional float queryonly) getmodelindex = #200; /* Acts as an alternative to precache_model(foo);setmodel(bar, foo); return bar.modelindex; If queryonly is set and the model was not previously precached, the builtin will return 0 without needlessly precaching the model. */ @@ -1722,7 +1959,9 @@ void(float sleeptime) sleep = #212; /* Part of FTE_MULTITHREADED void(entity player, string key, string value) forceinfokey = #213; /* Part of FTE_FORCEINFOKEY Directly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers. */ -void(string filename, float starttag, entity edict) chat = #214; /* Part of FTE_NPCCHAT*/ +void(entity player, string key, void *data, int size) forceinfokeyblob = #0:forceinfokeyblob; /* + Directly changes a user's info without pinging off the client. Also allows explicitly setting * keys, including *spectator. Does not affect the user's config or other servers. */ + #endif #if defined(CSQC) || defined(SSQC) void(vector org, vector dmin, vector dmax, float colour, float effect, float count) particle2 = #215; /* Part of FTE_HEXEN2*/ @@ -1806,7 +2045,9 @@ float(entity player) isbackbuffered = #234; /* Part of FTE_ISBACKBUFFERED #endif #if defined(CSQC) || defined(SSQC) -void(vector angle) rotatevectorsbyangle = #235; +void(vector angle) rotatevectorsbyangle = #235; /* + rotates the v_forward,v_right,v_up matrix by the specified angles. */ + void(vector fwd, vector right, vector up) rotatevectorsbyvectors = #236; float(float mdlindex, string skinname) skinforname = #237; #endif @@ -1816,6 +2057,11 @@ float(string shadername, optional string defaultshader, ...) shaderforname = #23 If the shader could not be loaded from disk (missing file or ruleset_allow_shaders 0), it will be created from the 'defaultshader' string if specified, or a 'skin shader' default will be used. defaultshader if not empty should include the outer {} that you would ordinarily find in a shader. */ +#endif +#ifdef CSQC +void(string shadername, string replacement, float timeoffset) remapshader = #0:remapshader; /* + All surfaces drawn with the specified shader will instead be drawn using the specified replacement shader. Shaders can be remapped to something else later by using the same source shadername. This is mostly useful for worldmodel surfaces (eg showing which team is currently winning). Entities should generally use setcustomskin or forceshader instead. Remaps will be forgotten on vid_reload, but can be reapplied via CSQC_RendererRestarted. */ + #endif #if defined(CSQC) || defined(SSQC) void(vector org, optional float count) te_bloodqw = #239; /* Part of FTE_TE_STANDARDEFFECTBUILTINS*/ @@ -1828,13 +2074,17 @@ float(vector viewpos, entity entity) checkpvs = #240; /* Part of FTE_QC_CHECKPVS #endif #ifdef SSQC entity(string match, optional float matchnum) matchclientname = #241; /* Part of FTE_QC_MATCHCLIENTNAME*/ -void(string dest, string content) sendpacket = #242; /* Part of FTE_QC_SENDPACKET*/ #endif +float(string destaddress, string content) sendpacket = #242; /* Part of FTE_QC_SENDPACKET + Sends a UDP packet to the specified destination. Note that the payload will be prefixed with four 255 bytes as a sort of security feature. */ + #ifdef CSQC vector(entity ent, float tagnum) rotatevectorsbytag = #244; #endif -#ifdef SSQC +#if defined(CSQC) || defined(SSQC) float(float dividend, float divisor) mod = #245; +#endif +#ifdef SSQC float(optional string host, optional string user, optional string pass, optional string defaultdb, optional string driver) sqlconnect = #250; /* Part of FTE_SQL*/ void(float serveridx) sqldisconnect = #251; /* Part of FTE_SQL*/ float(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof, float firstrow) callback, float querytype, string query) sqlopenquery = #252; /* Part of FTE_SQL*/ @@ -1861,10 +2111,10 @@ string(int) htos = #262; /* Part of FTE_QC_INTCONV Formats an integer as a base16 string, with leading 0s and no prefix. Always returns 8 characters. */ #endif -int(float) ftoi = #0:ftoi; /* +int(float) ftoi = #0:ftoi; /* Part of FTE_QC_INTCONV Converts the given float into a true integer without depending on extended qcvm instructions. */ -float(int) itof = #0:itof; /* +float(int) itof = #0:itof; /* Part of FTE_QC_INTCONV Converts the given true integer into a float without depending on extended qcvm instructions. */ #if defined(CSQC) || defined(SSQC) @@ -1876,6 +2126,23 @@ float(float skel, entity ent, float modelindex, float retainfrac, float firstbon Animation data (according to the entity's frame info) is pulled from the specified model and blended into the specified skeletal object. If retainfrac is set to 0 on the first call and 1 on the others, you can blend multiple animations together according to the addfrac value. The final weight should be 1. Other values will result in scaling and/or other weirdness. You can use firstbone and lastbone to update only part of the skeletal object, to allow legs to animate separately from torso, use 0 for both arguments to specify all, as bones are 1-based. */ +typedef struct +{ + int sourcemodelindex; /*frame data will be imported from this model, bones must be compatible*/ + int reserved; + int firstbone; + int lastbone; + float prescale; /*0 destroys existing data, 1 retains it*/ + float scale[4]; /*you'll need to do lerpfrac manually*/ + int animation[4]; + float animationtime[4]; + /*halflife models*/ + float subblend[2]; + float controllers[5]; +} skelblend_t; +float(float skel, int numblends, skelblend_t *weights, int structsize) skel_build_ptr = #0:skel_build_ptr; /* + Like skel_build, but slightly simpler. */ + float(float skel) skel_get_numbones = #265; /* Part of FTE_CSQC_SKELETONOBJECTS Retrives the number of bones in the model. The valid range is 1<=bone<=numbones. */ @@ -1898,12 +2165,15 @@ vector(float skel, float bonenum) skel_get_boneabs = #270; /* Part of FTE_CSQC_S void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_set_bone = #271; /* Part of FTE_CSQC_SKELETONOBJECTS Sets a bone position relative to its parent. If the orientation arguments are not specified, v_forward+v_right+v_up are used instead. */ -void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bone = #272; /* Part of FTE_CSQC_SKELETONOBJECTS +void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_premul_bone = #272; /* Part of FTE_CSQC_SKELETONOBJECTS Transforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */ -void(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up) skel_mul_bones = #273; /* Part of FTE_CSQC_SKELETONOBJECTS +void(float skel, float startbone, float endbone, vector org, optional vector fwd, optional vector right, optional vector up) skel_premul_bones = #273; /* Part of FTE_CSQC_SKELETONOBJECTS Transforms an entire consecutive range of bones by a matrix. You can use makevectors to generate a rotation matrix from an angle, but you'll probably want to divide the angle by the number of bones. */ +void(float skel, float bonenum, vector org, optional vector fwd, optional vector right, optional vector up) skel_postmul_bone = #0:skel_postmul_bone; /* + Transforms a single bone by a matrix. You can use makevectors to generate a rotation matrix from an angle. */ + void(float skeldst, float skelsrc, float startbone, float entbone) skel_copybones = #274; /* Part of FTE_CSQC_SKELETONOBJECTS Copy bone data from one skeleton directly into another. */ @@ -1916,7 +2186,24 @@ float(float modidx, string framename) frameforname = #276; /* Part of FTE_CSQC_S float(float modidx, float framenum) frameduration = #277; /* Part of FTE_CSQC_SKELETONOBJECTS Retrieves the duration (in seconds) of the specified framegroup. */ -void(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* +void(float modidx, float framenum, __inout float basetime, float targettime, void(float timestamp, int code, string data) callback) processmodelevents = #0:processmodelevents; /* Part of FTE_GFX_MODELEVENTS + Calls a callback for each event that has been reached. Basetime is set to targettime. */ + +float(float modidx, float framenum, __inout float basetime, float targettime, __out int code, __out string data) getnextmodelevent = #0:getnextmodelevent; /* + Reports the next event within a model's animation. Returns a boolean if an event was found between basetime and targettime. Writes to basetime,code,data arguments (if an event was found, basetime is set to the event's time, otherwise to targettime). + WARNING: this builtin cannot deal with multiple events with the same timestamp (only the first will be reported). */ + +float(float modidx, float framenum, int eventidx, __out float timestamp, __out int code, __out string data) getmodeleventidx = #0:getmodeleventidx; /* + Reports an indexed event within a model's animation. Writes to timestamp,code,data arguments on success. Returns false if the animation/event/model was out of range/invalid. Does not consider looping animations (retry from index 0 if it fails and you know that its a looping animation). This builtin is more annoying to use than getnextmodelevent, but can be made to deal with multiple events with the exact same timestamp. */ + +#endif +#define dotproduct(v1,v2) ((vector)(v1)*(vector)(v2)) +vector(vector v1, vector v2) crossproduct = #0:crossproduct; /* Part of FTE_QC_CROSSPRODUCT + Small helper function to calculate the crossproduct of two vectors. */ + +#if defined(CSQC) || defined(SSQC) +float(entity pusher, vector move, vector amove) pushmove = #0:pushmove; +__variant(float action, optional vector pos, optional float radius, optional float quant, ...) terrain_edit = #278; /* Part of FTE_TERRAIN_MAP Realtime terrain editing. Actions are the TEREDIT_ constants. */ typedef struct @@ -1929,33 +2216,46 @@ typedef struct vector tdir; float tbias; } brushface_t; -int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* +int(float modelidx, int brushid, brushface_t *out_faces, int maxfaces, int *out_contents) brush_get = #0:brush_get; /* Part of FTE_RAW_MAP Queries a brush's information. You must pre-allocate the face array for the builtin to write to. Return value is the number of faces retrieved, 0 on error. */ -int(float modelidx, brushface_t *in_faces, int numfaces, int contents) brush_create = #0:brush_create; /* +int(float modelidx, brushface_t *in_faces, int numfaces, int contents, optional int brushid) brush_create = #0:brush_create; /* Part of FTE_RAW_MAP Inserts a new brush into the model. Return value is the new brush's id. */ -void(float modelidx, int brushid) brush_delete = #0:brush_delete; /* +void(float modelidx, int brushid) brush_delete = #0:brush_delete; /* Part of FTE_RAW_MAP Destroys the specified brush. */ -float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* +float(float modelid, int brushid, int faceid, float selectedstate) brush_selected = #0:brush_selected; /* Part of FTE_RAW_MAP Allows you to easily set transient visual properties of a brush. returns old value. selectedstate=-1 changes nothing (called for its return value). */ -int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* +int(float modelid, int brushid, int faceid, vector *points, int maxpoints) brush_getfacepoints = #0:brush_getfacepoints; /* Part of FTE_RAW_MAP Returns the list of verticies surrounding the given face. If face is 0, returns the center of the brush (if space for 1 point) or the mins+maxs (if space for 2 points). */ -int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* +int(int faceid, brushface_t *in_faces, int numfaces, vector *points, int maxpoints) brush_calcfacepoints = #0:brush_calcfacepoints; /* Part of FTE_RAW_MAP Determines the points of the specified face, if the specified brush were to actually be created. */ -int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* +int(float modelid, vector *planes, float *dists, int numplanes, int *out_brushes, int *out_faces, int maxresults) brush_findinvolume = #0:brush_findinvolume; /* Part of FTE_RAW_MAP Allows you to easily obtain a list of brushes+faces within the given bounding region. If out_faces is not null, the same brush might be listed twice. */ +typedef struct +{ + vector dest; + int linkflags; +} nodeslist_t; +void(entity ent, vector dest, int denylinkflags, void(entity ent, vector dest, int numnodes, nodeslist_t *nodelist) callback) route_calculate = #0:route_calculate; /* + Begin calculating a route. The callback function will be called once the route has finished being calculated. The route must be memfreed once it is no longer needed. The route must be followed in reverse order (ie: the first node that must be reached is at index numnodes-1). If no route is available then the callback will be called with no nodes. */ + void(optional entity ent, optional vector neworigin) touchtriggers = #279; /* Triggers a touch events between self and every SOLID_TRIGGER entity that it is in contact with. This should typically just be the triggers touch functions. Also optionally updates the origin of the moved entity. */ #endif #ifdef SSQC -void(float buf, float fl) WriteFloat = #280; +void(float buf, float fl) WriteFloat = #280; /* + Writes a full 32bit float without any data conversions at all, for full precision. */ + +void(float buf, int fl) WriteInt = #0:WriteInt; /* + Equivelent to WriteLong, but doesn't truncate to a float first before converting back to an int. */ + #endif #if defined(CSQC) || defined(SSQC) float(entity skelent, string dollcmd, float animskel) skel_ragupdate = #281; /* @@ -1984,18 +2284,24 @@ float(float resourcetype, float tryload, string resourcename) resourcestatus = # #endif hashtable(float tabsize, optional float defaulttype) hash_createtab = #287; /* Part of FTE_QC_HASHTABLES - Creates a hash table object with at least 'tabsize' slots. hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return). */ + Creates a hash table object. + The tabsize argument is a performance hint and should generally be set to something similar to the number of entries expected, typically a power of two assumption. Too high simply wastes memory, too low results in extra string compares but no actual bugs. + defaulttype must be one of the EV_* values, if specified. + The hash table with index 0 is a game-persistant table and will NEVER be returned by this builtin (except as an error return). */ void(hashtable table) hash_destroytab = #288; /* Part of FTE_QC_HASHTABLES Destroys a hash table object. */ void(hashtable table, string name, __variant value, optional float typeandflags) hash_add = #289; /* Part of FTE_QC_HASHTABLES Adds the given key with the given value to the table. - If flags&HASH_REPLACE, the old value will be removed, if not set then multiple values may be added for a single key, they won't overwrite. - The type argument describes how the value should be stored and saved to files. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games. */ + If flags&HASH_REPLACE, the old value will be removed, otherwise if flags&HASH_ADD then a duplicate entry will be added with a second value (can be obtained via hash_get's index argument). + The type argument describes how the value should be stored in saved games, as well as providing constraints with the hash_get function. While you can claim that all variables are just vectors, being more precise can result in less issues with tempstrings or saved games - be sure to be explicit with EV_STRING where appropriate because tempstrings may be reclaimed before the get (especially with saved games or table 0). */ __variant(hashtable table, string name, optional __variant deflt, optional float requiretype, optional float index) hash_get = #290; /* Part of FTE_QC_HASHTABLES - looks up the specified key name in the hash table. returns deflt if key was not found. If stringsonly=1, the return value will be in the form of a tempstring, otherwise it'll be the original value argument exactly as it was. If requiretype is specified, then values not of the specified type will be ignored. Hurrah for multiple types with the same name. */ + Looks up the specified key name in the hash table. Returns deflt if the key was not found. + If requiretype is specified then the function will only consider entries of the matching type (allowing you to store both flags+strings under a single name without getting confused). + If index is specified then the function will ignore the first N entries with the same key (applicable only with entries added using HASH_ADD, not HASH_REPLACE), allowing you to store multiple entries. Keep querying higher indexes starting from 0 until it returns the deflt value. + You will usually need to cast the result of this function to a real datatype. */ __variant(hashtable table, string name) hash_delete = #291; /* Part of FTE_QC_HASHTABLES removes the named key. returns the value of the object that was destroyed, or 0 on error. */ @@ -2016,6 +2322,11 @@ void(string dest, string from, string cmd, string info) clusterevent = #0:cluste string(entity player, optional string newnode) clustertransfer = #0:clustertransfer; /* Only functions in mapcluster mode. Initiate transfer of the player to a different node. Can take some time. If dest is specified, returns null on error. Otherwise returns the current/new target node (or null if not transferring). */ +#endif +#if defined(CSQC) || defined(SSQC) +float(float mdlidx) modelframecount = #0:modelframecount; /* + Retrieves the number of frames in the specified model. */ + #endif #if defined(CSQC) || defined(MENU) void() clearscene = #300; /* @@ -2036,7 +2347,14 @@ void(entity ent) addentity = #302; /* #endif #ifdef CSQC -void(string texturename, float flags, void *verts, int *indexes, int numindexes) addtrisoup_1 = #0:addtrisoup_1; /* +void(entity ent) removeentity = #0:removeentity; /* + Undoes all addentities added to the scene from the given entity, without removing ALL entities (useful for splitscreen/etc, readd modified versions as desired). */ + +typedef float vec2[2]; +typedef float vec3[3]; +typedef float vec4[4]; +typedef struct trisoup_simple_vert_s {vec3 xyz;vec2 st;vec4 rgba;} trisoup_simple_vert_t; +void(string texturename, int flags, struct trisoup_simple_vert_s *verts, int *indexes, int numindexes) addtrisoup_simple = #0:addtrisoup_simple; /* Adds the specified trisoup into the scene as additional geometry. This permits caching geometry to reduce builtin spam. Indexes are a triangle list (so eg quads will need 6 indicies to form two triangles). NOTE: this is not going to be a speedup over polygons if you're still generating lots of new data every frame. */ #endif @@ -2064,8 +2382,13 @@ void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex = #3 Specifies a polygon vertex with its various properties. */ void() R_EndPolygon = #308; /* - Ends the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon if you wish to draw another polygon with the same shader. */ + Ends the current polygon. At least 3 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader. */ +#ifdef CSQC +void(float radius, vector texcoordbias) R_EndPolygonRibbon = #0:R_EndPolygonRibbon; /* + Ends the current primitive and duplicates each vertex sideways into a ribbon. The texcoordbias will be added to each duplicated vertex allowing for regular 2d textures. At least 2 verticies must have been specified. You do not need to call beginpolygon again if you wish to draw another polygon with the same shader. */ + +#endif #if defined(CSQC) || defined(MENU) #define getviewprop getproperty __variant(float property) getproperty = #309; /* @@ -2081,8 +2404,8 @@ vector (vector v) project = #311; /* #endif #if defined(CSQC) || defined(MENU) -void(vector pos, vector size, float alignflags, string text) drawtextfield = #0:drawtextfield; /* - Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. */ +float(vector pos, vector size, float alignflags, string text) drawtextfield = #0:drawtextfield; /* + Draws a multi-line block of text, including word wrapping and alignment. alignflags bits are RTLB, typically 3. Returns the total number of lines. */ #endif #ifdef CSQC @@ -2093,14 +2416,18 @@ 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 any lack path and extension. */ + Forces the engine to load the named image. If trywad is specified, the specified name must lack any path and extension. */ -void(string imagename, int width, int height, int *pixeldata) r_uploadimage = #0:r_uploadimage; /* - Updates a texture with the specified rgba data. Will be created if needed. */ +#endif +#if defined(CSQC) || defined(MENU) +void(string imagename, int width, int height, void *pixeldata, optional int datasize, optional int format) r_uploadimage = #0:r_uploadimage; /* Part of FTE_CSQC_RAWIMAGES + Updates a texture with the specified rgba data (uploading it to the gpu). Will be created if needed. If datasize is specified then the image is decoded (eg .ktx or .dds data) instead of being raw R8G8B8A data. You'll typically want shaderforname to also generate a shader to use the texture. */ -int*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* - Reads and decodes an image from disk, providing raw pixel data. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it. */ +int*(string filename, __out int width, __out int height) r_readimage = #0:r_readimage; /* Part of FTE_CSQC_RAWIMAGES + Reads and decodes an image from disk, providing raw R8G8B8A8 pixel data. Should not be used for dds or ktx etc formats. Returns __NULL__ if the image could not be read for any reason. Use memfree to free the data once you're done with it. */ +#endif +#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. */ @@ -2156,16 +2483,17 @@ void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector tx #endif #ifdef CSQC -float(float stnum) getstati = #330; /* - Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat (converted to a float). */ +#define getstati_punf(stnum) (float)(__variant)getstati(stnum) +int(float stnum) getstati = #330; /* + Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat. Use getstati_punf if you wish to type-pun a float stat as an int to avoid truncation issues in DP. */ #define getstatbits getstatf float(float stnum, optional float firstbit, optional float bitcount) getstatf = #331; /* - Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat. */ + Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat (converted into a float, so there are no VM dependancies). */ string(float stnum) getstats = #332; /* Retrieves the value of the given EV_STRING stat, as a tempstring. - Older engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but FTE QuakeWorld uses a separate namespace for string stats and has a much higher length limit. */ + Older engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but FTE Quake uses a separate namespace for string stats and has a much higher length limit. */ __variant(float playernum, float statnum, float stattype) getplayerstat = #0:getplayerstat; /* Retrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits. */ @@ -2186,7 +2514,8 @@ void(float effectnum, entity ent, vector start, vector end) trailparticles = #33 Draws the given effect between the two named points. If ent is not world, distances will be cached in the entity in order to avoid framerate dependancies. The entity is not otherwise used. */ void(float effectnum, vector origin, optional vector dir, optional float count) pointparticles = #337; /* Part of FTE_SV_POINTPARTICLES - Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument. */ + Spawn a load of particles from the given effect at the given point traveling or aiming along the direction specified. The number of particles are scaled by the count argument. + For regular particles, the dir vector is multiplied by the 'veladd' property (while orgadd will push the particles along it). Decals will use it as a hint to align to the correct surface. In both cases, it should normally be a unit vector, but other lengths will still work. If it has length 0 then FTE will assume downwards. */ #endif #ifdef CSQC @@ -2195,7 +2524,7 @@ void(string s, ...) cprint = #338; /* #endif #if defined(CSQC) || defined(SSQC) -void(string s, ...) print = #339; /* +void(string s, ...) print = #339; /* Part of DP_SV_PRINT Unconditionally print on the local system's console, even in ssqc (doesn't care about the value of the developer cvar). */ #endif @@ -2232,8 +2561,15 @@ float(float effective) getcursormode = #0:getcursormode; /* #endif #ifdef CSQC vector() getmousepos = #344; /* - Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent for such things in csqc mods. */ + Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent instead for such things in csqc mods. */ +#endif +#if defined(CSQC) || defined(MENU) +void(vector newpos) setmousepos = #0:setmousepos; /* + Warps the mouse cursor to the given location. Should normally only be done following setcursormode(TRUE,...). The warp MAY be visible through *_InputEvent, but normally be seen as an IE_ABSMOUSE event anyway. Not all systems support cursor warping (or even cursors), so this is a hint only and you should not depend upon it. */ + +#endif +#ifdef CSQC float(float inputsequencenum) getinputstate = #345; /* Looks up an input frame from the log, setting the input_* globals accordingly. The sequence number range used for prediction should normally be servercommandframe < sequence <= clientcommandframe. @@ -2253,15 +2589,43 @@ string(float playernum, string keyname) getplayerkeyvalue = #348; /* Look up a player's userinfo, to discover things like their name, topcolor, bottomcolor, skin, team, *ver. Also includes scoreboard info like frags, ping, pl, userid, entertime, as well as voipspeaking and voiploudness. */ +float(float playernum, string keyname, optional float assumevalue) getplayerkeyfloat = #0:getplayerkeyfloat; /* + Cheaper version of getplayerkeyvalue that avoids the need for so many tempstrings. */ + +int(float playernum, string keyname, optional void *outptr, int size) getplayerkeyblob = #0:getplayerkeyblob; /* + Obtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */ + +#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. */ + +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. */ + +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). */ + +int(float seat, string keyname, void *outptr, int maxsize) getlocaluserinfoblob = #0:getlocaluserinfoblob; /* + Obtains a copy of the full data blob. Will write up to size bytes but return the full size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */ + +#endif +#ifdef SSQC +int(string keyname, optional void *outptr, int size) getlocalinfo = #0:getlocalinfo; /* + Obtains a copy of a data blob (with spaces) from the server's private localinfo. Will write up to size bytes and return the actual size. Does not null terminate (but memalloc(ret+1) will, if you want to cast the buffer to a string), and the blob may contain embedded nulls. Ignores all special keys, returning only what is actually there. */ + +void(string keyname, optional void *outptr, int size) setlocalinfo = #0:setlocalinfo; /* + Changes the server's private localinfo. This data will be available for the following map, and will *usually* reload with saved games. */ + #endif #if defined(CSQC) || defined(MENU) float() isdemo = #349; /* - Returns if the client is currently playing a demo or not */ + Returns if the client is currently playing a demo or not. Returns 2 when playing an mvd (where other player's stats can be queried, or the pov can be changed freely). */ #endif #ifdef CSQC float() isserver = #350; /* - Returns if the client is acting as the server (aka: listen server) */ + Returns non-zero whenever the local console can directly affect the server (ie: listen servers or single-player). Compat note: DP returns 0 for single-player. */ void(vector origin, vector forward, vector right, vector up, optional float reverbtype) SetListener = #351; /* Sets the position of the view, as far as the audio subsystem is concerned. This should be called once per CSQC_UpdateView as it will otherwise revert to default. For reverbtype, see setup_reverb or treat as 'underwater'. */ @@ -2291,7 +2655,7 @@ typedef struct { float flRoomRolloffFactor; int iDecayHFLimit; } reverbinfo_t; -void(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* +void(float reverbslot, reverbinfo_t *reverbinfo, int sizeofreverinfo_t) setup_reverb = #0:setup_reverb; /* Part of FTE_CSQC_REVERB 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 @@ -2301,12 +2665,23 @@ void(string cmdname) registercommand = #352; /* Console commands that are later used will invoke CSQC_ConsoleCommand. */ #endif -#if defined(CSQC) || defined(SSQC) 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. */ +#if defined(CSQC) || defined(SSQC) string(string key) serverkey = #354; /* - Look up a key in the server's public serverinfo string */ + Look up a key in the server's public serverinfo string. If the key contains binary data then it will be truncated at the first null. */ + +float(string key, optional float assumevalue) serverkeyfloat = #0:serverkeyfloat; /* + Version of serverkey that returns the value as a float (which avoids tempstrings). */ + +int(string key, optional void *ptr, int maxsize) serverkeyblob = #0:serverkeyblob; /* + Version of serverkey that returns data as a blob (ie: binary data that may contain nulls). Returns the full blob size, even if truncated (pass maxsize=0 to query required storage). */ + +#endif +#ifdef SSQC +void(string key, void *ptr, optional int size) setserverkey = #0:setserverkey; /* + Changes the server's serverinfo. */ #endif #ifdef CSQC @@ -2319,30 +2694,54 @@ string(optional string resetstring) getentitytoken = #355; /* #endif #if defined(CSQC) || defined(MENU) -float(string s) findfont = #356; /* +float(string s) findfont = #356; /* Part of DP_GFX_FONTS Looks up a named font slot. Matches the actual font name as a last resort. */ -float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset) loadfont = #357; /* - too convoluted for me to even try to explain correct usage. Try drawfont = loadfont("", "cour", "16", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high), if you have the freetype2 library in windows.. */ +float(string fontname, string fontmaps, string sizes, float slot, optional float fix_scale, optional float fix_voffset) loadfont = #357; /* Part of DP_GFX_FONTS + too convoluted for me to even try to explain correct usage. Try drawfont = loadfont("", "cour", "16", -1, 0, 0); to switch to the courier font (optimised for 16 virtual pixels high) ('cour' requires mscorefonts installed in linux). Additionally you can add "outline=1" as an extra token in the sizes string, to have more readable outlined fonts. */ #endif #ifdef CSQC void(string evname, string evargs, ...) sendevent = #359; /* - Invoke Cmd_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors. */ + Invoke CSEv_evname_evargs in ssqc. evargs must be a string of initials refering to the types of the arguments to pass. v=vector, e=entity(.entnum field is sent), f=float, i=int. 6 arguments max - you can get more if you pack your floats into vectors. */ + +float() readbyte = #360; /* + Reads an unsigned 8-bit value, pair with WriteByte. */ + +float() readchar = #361; /* + Reads a signed 8-bit value. Paired with WriteChar. */ + +float() readshort = #362; /* + Reads a signed 16-bit value. Paired with WriteShort. */ + +float() readlong = #363; /* + Reads a signed 32-bit value. Paired with WriteLong or WriteInt. */ + +float() readcoord = #364; /* + Reads a value matching the unspecified precision written ONLY by WriteCoord. */ + +float() readangle = #365; /* + Reads a value matching the unspecified precision written ONLY by WriteAngle. */ + +string() readstring = #366; /* + Reads a null-terminated string. */ + +float() readfloat = #367; /* + Reads a float without any truncation nor conversions. Data MUST have originally been written with WriteFloat. */ + +int() readint = #0:readint; /* + Reads a 32bit int without any conversions to float, otherwise interchangable with readlong. */ + +float() readentitynum = #368; /* + Reads the serverside index of an entity, paired with WriteEntity. There may be nothing else known about the entity yet, so the result typically needs to be saved as-is and re-looked up each frame. This can be done via getentity(NUM, GE_*) for non-csqc ents, or findentity(world,entnum,NUM) - both of which can fail due to latency. */ -float() readbyte = #360; -float() readchar = #361; -float() readshort = #362; -float() readlong = #363; -float() readcoord = #364; -float() readangle = #365; -string() readstring = #366; -float() readfloat = #367; -float() readentitynum = #368; float(string modelname, float(float isnew) updatecallback, float flags) deltalisten = #371; /* Specifies a per-modelindex callback to listen for engine-networking entity updates. Such entities are automatically interpolated by the engine (unless flags specifies not to). The various standard entity fields will be overwritten each frame before the updatecallback function is called. */ +float(vector org, float radius, vector rgb) dynamiclight_spawnstatic = #0:dynamiclight_spawnstatic; /* + Creates a static persistent light at the given position with the specified colour. Additional properties must be set via dynamiclight_set. */ + __variant(float lno, float fld) dynamiclight_get = #372; /* Retrieves a property from the given dynamic/rt light. Return type depends upon the light field requested. */ @@ -2357,16 +2756,27 @@ void(string shadername, vector origin, vector up, vector side, vector rgb, float #endif #if defined(CSQC) || defined(MENU) -void(entity e, string skinfilename, optional string skindata) setcustomskin = #376; /* - Sets an entity's skin overrides. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format: - surfacename,shadername - makes the named surface use the named shader - replace "surfacename" "shadername" - same. +void(entity e, string skinfilename, optional string skindata) setcustomskin = #376; /* Part of FTE_QC_CUSTOMSKINS + Sets an entity's skin overrides to a new skin object. Releases the entities old skin (refcounted). */ + +#endif +#ifdef CSQC +float(string skinfilename, optional string skindata) loadcustomskin = #377; /* + Creates a new skin object and returns it. These are custom per-entity surface->shader lookups. The skinfilename/data should be in .skin format: + surfacename,shadername - makes the named surface use the named shader (legacy format for compat with q3) + replace "surfacename" "shadername" - non-legacy equivalent. qwskin "foo" - use an unmodified quakeworld player skin (including crop+repalette rules) q1lower 0xff0000 - specify an override for the entity's lower colour, in this case to red q1upper 0x0000ff - specify an override for the entity's lower colour, in this case to blue compose "surfacename" "shader" "imagename@x,y:w,h$s,t,s2,t2?r,g,b,a" - compose a skin texture from multiple images. The texture is determined to be sufficient to hold the first named image, additional images can be named as extra tokens on the same line. - Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). */ + Use a + at the end of the line to continue reading image tokens from the next line also, the named shader must use 'map $diffuse' to read the composed texture (compatible with the defaultskin shader). Must be matched with a releasecustomskin call later, and is pointless without applycustomskin. */ + +void(entity e, float skinobj) applycustomskin = #378; /* + Updates the entity's custom skin (refcounted). */ + +void(float skinobj) releasecustomskin = #379; /* + Lets the engine know that the skin will no longer be needed. Thanks to refcounting any ents with the skin already applied will retain their skin until later changed. It is valid to destroy a skin just after applying it to an ent in the same function that it was created in, as the skin will only be destroyed once its refcount rops to 0. */ #endif __variant*(int size) memalloc = #384; /* Part of FTE_MEMALLOC @@ -2406,13 +2816,9 @@ void(string conname, vector pos, vector size, float fontsize) con_draw = #393; / float(string conname, float inevtype, float parama, float paramb, float paramc) con_input = #394; /* Part of FTE_CSQC_ALTCONSOLES Forwards input events to the named console. Mouse updates should be absolute only. */ -#endif -#ifdef CSQC -void(string newcaption) setwindowcaption = #0:setwindowcaption; /* +void(string newcaption) setwindowcaption = #0:setwindowcaption; /* Part of FTE_CSQC_WINDOWCAPTION Replaces the title of the game window, as seen when task switching or just running in windowed mode. */ -#endif -#if defined(CSQC) || defined(MENU) float() cvars_haveunsaved = #0:cvars_haveunsaved; /* Returns true if any archived cvar has an unsaved value. */ @@ -2420,6 +2826,11 @@ float() cvars_haveunsaved = #0:cvars_haveunsaved; /* float(entity e, float nowreadonly) entityprotection = #0:entityprotection; /* Changes the protection on the specified entity to protect it from further edits from QC. The return value is the previous setting. Note that this can be used to unprotect the world, but doing so long term is not advised as you will no longer be able to detect invalid entity references. Also, world is not networked, so results might not be seen by clients (or in other words, world.avelocity_y=64 is a bad idea). */ +#ifdef CSQC +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 #if defined(CSQC) || defined(SSQC) entity(entity from, optional entity to) copyentity = #400; /* Part of DP_QC_COPYENTITY Copies all fields from one entity to another. */ @@ -2431,8 +2842,8 @@ void(entity ent, float colours) setcolors = #401; /* #endif #if defined(CSQC) || defined(SSQC) -entity(.string field, string match) findchain = #402; /* Part of DP_QC_FINDCHAIN*/ -entity(.float fld, float match) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/ +entity(.string field, string match, optional .entity chainfield) findchain = #402; /* Part of DP_QC_FINDCHAIN*/ +entity(.float fld, float match, optional .entity chainfield) findchainfloat = #403; /* Part of DP_QC_FINDCHAINFLOAT*/ void(vector org, string modelname, float startframe, float endframe, float framerate) effect = #404; /* Part of DP_SV_EFFECT Spawns a self-animating sprite */ @@ -2440,8 +2851,8 @@ void(vector org, vector dir, float count) te_blood = #405; /* Part of DP_TE_BLOO void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower = #406; /* Part of _DP_TE_BLOODSHOWER*/ void(vector org, vector color) te_explosionrgb = #407; /* Part of DP_TE_EXPLOSIONRGB*/ void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube = #408; /* Part of DP_TE_PARTICLECUBE*/ -void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of _DP_TE_PARTICLERAIN*/ -void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of _DP_TE_PARTICLESNOW*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409; /* Part of DP_TE_PARTICLERAIN*/ +void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410; /* Part of DP_TE_PARTICLESNOW*/ void(vector org, vector vel, float howmany) te_spark = #411; /* Part of DP_TE_SPARK*/ void(vector org) te_gunshotquad = #412; /* Part of _DP_TE_QUADEFFECTS1*/ void(vector org) te_spikequad = #413; /* Part of _DP_TE_QUADEFFECTS1*/ @@ -2481,7 +2892,7 @@ void(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #444; /* string(strbuf bufhandle, string glue) buf_implode = #445; /* Part of DP_QC_STRINGBUFFERS*/ string(strbuf bufhandle, float string_index) bufstr_get = #446; /* Part of DP_QC_STRINGBUFFERS*/ void(strbuf bufhandle, float string_index, string str) bufstr_set = #447; /* Part of DP_QC_STRINGBUFFERS*/ -float(strbuf bufhandle, string str, float order) bufstr_add = #448; /* Part of DP_QC_STRINGBUFFERS*/ +float(strbuf bufhandle, string str, float ordered) bufstr_add = #448; /* Part of DP_QC_STRINGBUFFERS*/ void(strbuf bufhandle, float string_index) bufstr_free = #449; /* Part of DP_QC_STRINGBUFFERS*/ float(string name) iscachedpic = #451; string(string name, optional float trywad) precache_pic = #452; @@ -2492,6 +2903,7 @@ float(vector position, vector size, vector rgb, float alpha, optional float flag void(float x, float y, float width, float height) drawsetcliparea = #458; void(void) drawresetcliparea = #459; vector(string picname) drawgetimagesize = #460; +void(float width, vector pos1, vector pos2) drawline = #466; float(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring = #467; float(string text, float usecolours, optional vector fontsize) stringwidth = #468; void(vector pos, vector sz, string pic, vector srcpos, vector srcsz, vector rgb, float alpha, float flag) drawsubpic = #469; @@ -2514,18 +2926,18 @@ string(searchhandle handle, float num) search_getfilename = #447; /* Part of DP_ Retrieves name of one of the files that was found by the initial search. */ #endif -float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* +float(searchhandle handle, float num) search_getfilesize = #0:search_getfilesize; /* Part of FTE_QC_FS_SEARCH_SIZEMTIME Retrieves the size of one of the files that was found by the initial search. */ -string(searchhandle handle, float num) search_getfilemtime = #0:search_getfilemtime; /* +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. */ #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*/ -entity(.float fld, float match) findchainflags = #450; /* Part of DP_QC_FINDCHAINFLAGS*/ -float(entity ent, string tagname) gettagindex = #451; /* Part of DP_MD3_TAGSINFO*/ -vector(entity ent, float tagindex) gettaginfo = #452; /* Part of DP_MD3_TAGSINFO +entity(.float fld, float match, optional .entity chainfield) findchainflags = #450; /* Part of DP_QC_FINDCHAINFLAGS*/ +float(entity ent, string tagname) gettagindex = #451; /* Part of DP_QC_GETTAGINFO*/ +vector(entity ent, float tagindex) gettaginfo = #452; /* Part of DP_QC_GETTAGINFO Obtains the current worldspace position+orientation of the bone or tag from the given entity. The return value is the world coord, v_forward, v_right, v_up are also set according to the bone/tag's orientation. */ #endif @@ -2536,6 +2948,7 @@ float(entity client) clienttype = #455; /* Part of DP_SV_BOTCLIENT*/ void(float target, string str) WriteUnterminatedString = #456; /* Part of DP_SV_WRITEUNTERMINATEDSTRING*/ #endif #if defined(CSQC) || defined(SSQC) +void(vector org, vector vel, float howmany) te_flamejet = #457; /* Part of _DP_TE_FLAMEJET*/ entity(float entnum) edict_num = #459; /* Part of DP_QC_EDICT_NUM*/ strbuf() buf_create = #460; /* Part of DP_QC_STRINGBUFFERS*/ void(strbuf bufhandle) buf_del = #461; /* Part of DP_QC_STRINGBUFFERS*/ @@ -2545,7 +2958,7 @@ void(strbuf bufhandle, float sortprefixlen, float backward) buf_sort = #464; /* string(strbuf bufhandle, string glue) buf_implode = #465; /* Part of DP_QC_STRINGBUFFERS*/ string(strbuf bufhandle, float string_index) bufstr_get = #466; /* Part of DP_QC_STRINGBUFFERS*/ void(strbuf bufhandle, float string_index, string str) bufstr_set = #467; /* Part of DP_QC_STRINGBUFFERS*/ -float(strbuf bufhandle, string str, float order) bufstr_add = #468; /* Part of DP_QC_STRINGBUFFERS*/ +float(strbuf bufhandle, string str, float ordered) bufstr_add = #468; /* Part of DP_QC_STRINGBUFFERS*/ void(strbuf bufhandle, float string_index) bufstr_free = #469; /* Part of DP_QC_STRINGBUFFERS*/ #endif float(float s) asin = #471; /* Part of DP_QC_ASINACOSATANATAN2TAN*/ @@ -2565,8 +2978,8 @@ string(float uselocaltime, string format, ...) strftime = #478; /* Part of DP_QC float(string s, string separator1, ...) tokenizebyseparator = #479; /* Part of DP_QC_TOKENIZEBYSEPARATOR*/ string(string s) strtolower = #480; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/ string(string s) strtoupper = #481; /* Part of DP_QC_STRING_CASE_FUNCTIONS*/ -string(string s) cvar_defstring = #482; /* Part of DP_QC_CVAR_DEFSTRING*/ #if defined(CSQC) || defined(SSQC) +string(string s) cvar_defstring = #482; /* Part of DP_QC_CVAR_DEFSTRING*/ void(vector origin, string sample, float volume, float attenuation) pointsound = #483; /* Part of DP_SV_POINTSOUND*/ #endif string(string search, string replace, string subject) strreplace = #484; /* Part of DP_QC_STRREPLACE*/ @@ -2575,7 +2988,7 @@ string(string search, string replace, string subject) strireplace = #485; /* Par vector(entity e, float s, float n, float a) getsurfacepointattribute = #486; /* Part of DP_QC_GETSURFACEPOINTATTRIBUTE*/ #endif #if defined(CSQC) || defined(MENU) -float(string name) gecko_create = #487; /* Part of DP_GECKO_SUPPORT +float(string name, optional string initialURI) gecko_create = #487; /* Part of DP_GECKO_SUPPORT Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing. */ void(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT @@ -2584,7 +2997,7 @@ void(string name) gecko_destroy = #488; /* Part of DP_GECKO_SUPPORT void(string name, string URI) gecko_navigate = #489; /* Part of DP_GECKO_SUPPORT Sends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus. */ -float(string name, float key, float eventtype) gecko_keyevent = #490; /* Part of DP_GECKO_SUPPORT +float(string name, float key, float eventtype, optional float charcode) gecko_keyevent = #490; /* Part of DP_GECKO_SUPPORT Send a key event to a media decoder. This applies only to interactive decoders like browsers. */ void(string name, float x, float y) gecko_mousemove = #491; /* Part of DP_GECKO_SUPPORT @@ -2627,7 +3040,15 @@ float(float fieldnum) entityfieldtype = #498; /* Part of DP_QC_ENTITYDATA string(float fieldnum, entity ent) getentityfieldstring = #499; /* Part of DP_QC_ENTITYDATA*/ float(float fieldnum, entity ent, string s) putentityfieldstring = #500; /* Part of DP_QC_ENTITYDATA*/ +#ifdef SSQC +void(float to, string s, float sz) WritePicture = #501; /* Part of DP_SV_WRITEPICTURE + Encodes the named image across the network as-is adhering to some size limit. In FTE, this simply writes the string and is equivelent to writestring and sz is ignored. WritePicture should be paired with ReadPicture in csqc. */ + +#endif #ifdef CSQC +string() ReadPicture = #501; /* + Reads a picture that was written by ReadPicture, and returns a name that can be used in drawpic and other 2d drawing functions. In FTE, this acts as a readstring-with-downloadcheck - the image will appear normally once it has been downloaded, but its size may be incorrect until then. */ + 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 @@ -2681,10 +3102,16 @@ void(string s) loadfromdata = #529; /* void(string s) loadfromfile = #530; /* Reads a set of entities from the named file. This file should have the same format as a .ent file or a saved game. Entities will be spawned as required. If you need to see the entities that were created, you should use parseentitydata instead. */ +void(float pause) setpause = #531; /* + Sets whether the server should or should not be paused. This does not affect auto-paused things like when the console is down. */ + #endif #ifdef SSQC float(string mname) precache_vwep_model = #532; /* Part of ZQ_VWEP*/ #endif +float(float v, optional float base) log = #532; /* Part of ??MVDSV_BUILTINS + Determines the logarithm of the input value according to the specified base. This can be used to calculate how much something was shifted by. */ + #ifdef CSQC float(entity e, float channel, string newsample, float volume, float attenuation, float pitchpct, float flags, float timeoffset) soundupdate = #0:soundupdate; /* Changes the properties of the current sound being played on the given entity channel. newsample may be empty, and will be ignored in this case. timeoffset is relative to the current position (subtract the result of getsoundtime for absolute positions). Negative volume can be used to stop the sound. Return value is a fractional value based upon the number of audio devices that could be updated - test against TRUE rather than non-zero. */ @@ -2692,6 +3119,8 @@ float(entity e, float channel, string newsample, float volume, float attenuation float(entity e, float channel) getsoundtime = #533; /* Returns the current playback time of the sample on the given entity's channel. Beware CHAN_AUTO (in csqc, channels are not limited by network protocol). */ +float(entity e, float channel) getchannellevel = #0:getchannellevel; /* */ + #endif #if defined(CSQC) || defined(MENU) float(string sample) soundlength = #534; /* @@ -2704,6 +3133,11 @@ 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. */ +#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. */ + +#endif #if defined(CSQC) || defined(SSQC) void(entity e, float physics_enabled) physics_enable = #540; /* Enable or disable the physics attached to a MOVETYPE_PHYSICS entity. Entities which have been disabled in this way will stop taking so much cpu time. */ @@ -2770,23 +3204,55 @@ void(float mask, float fld, string str, float op) sethostcachemaskstring = #616; void(float mask, float fld, float num, float op) sethostcachemasknumber = #617; /* Part of FTE_CSQC_SERVERBROWSER*/ void() resorthostcache = #618; /* Part of FTE_CSQC_SERVERBROWSER*/ void(float fld, float descending) sethostcachesort = #619; /* Part of FTE_CSQC_SERVERBROWSER*/ -void() refreshhostcache = #620; /* Part of FTE_CSQC_SERVERBROWSER*/ +void(optional float dopurge) refreshhostcache = #620; /* Part of FTE_CSQC_SERVERBROWSER*/ float(float fld, float hostnr) gethostcachenumber = #621; /* Part of FTE_CSQC_SERVERBROWSER*/ float(string key) gethostcacheindexforkey = #622; /* Part of FTE_CSQC_SERVERBROWSER*/ void(string key) addwantedhostcachekey = #623; /* Part of FTE_CSQC_SERVERBROWSER*/ string() getextresponse = #624; /* Part of FTE_CSQC_SERVERBROWSER*/ #endif string(string dnsname, optional float defport) netaddress_resolve = #625; -string(string fmt, ...) sprintf = #627; +#if defined(CSQC) || defined(MENU) +string(float n, float prop) getgamedirinfo = #626; +string(int n, int prop) getpackagemanagerinfo = #0:getpackagemanagerinfo; /* + Queries information about a package from the engine's package manager subsystem. Actions can be taken via the pkg console command. */ + +#endif +string(string fmt, ...) sprintf = #627; /* Part of DP_QC_SPRINTF + 'prints' to a formatted temp-string. Mostly acts as in C, however %d assumes floats (fteqcc has arg checking. Use it.). + type conversions: l=arg is an int, h=arg is a float, and will work as a prefix for any float or int representation. + float representations: d=decimal, e,E=exponent-notation, f,F=floating-point notation, g,G=terse float, c=char code, x,X=hex + other representations: i=int, s=string, S=quoted and marked-up string, v=vector, p=pointer + so %ld will accept an int arg, while %hi will expect a float arg. + entities, fields, and functions will generally need to be printed as ints with %i. */ + #if defined(CSQC) || defined(SSQC) float(entity e, float s) getsurfacenumtriangles = #628; vector(entity e, float s, float n) getsurfacetriangle = #629; #endif #if defined(CSQC) || defined(MENU) +float(float key, string bind, optional float bindmap, optional float modifier) setkeybind = #630; vector() getbindmaps = #631; float(vector bm) setbindmaps = #632; #endif string(string digest, string data, ...) digest_hex = #639; +string(string digest, void *data, int length) digest_ptr = #0:digest_ptr; /* + Calculates the digest of a single contiguous block of memory (including nulls) using the specified hash function. */ + +float(string src, string dst) fcopy = #650; /* + Equivelent to fopen+fread+fwrite+fclose from QC (ie: reads from $gamedir/data/ or $gamedir, but always writes to $gamedir/data/ ) */ + +float(string src, string dst) frename = #651; /* + Renames the file, returning 0 on success. Both paths are relative to the data/ subdir. */ + +float(string fname) fremove = #652; /* + Deletes the named file - path is relative to data/ subdir, like fopen's FILE_WRITE. Returns 0 on success. */ + +float(string fname) fexists = #653; /* + Use whichpack instead. Returns true if it exists inside the default writable path. */ + +float(string path) rmtree = #654; /* + Dangerous, but sandboxed to data/ */ + #if defined(CSQC) || defined(MENU) #define K_TAB 9 #define K_ENTER 13 @@ -2849,11 +3315,13 @@ string(string digest, string data, ...) digest_hex = #639; #define K_MOUSE8 521 #define K_MOUSE9 522 #define K_MOUSE10 523 -#define K_LWIN 239 -#define K_RWIN 240 +#define K_MWHEELUP 515 +#define K_MWHEELDOWN 516 +#define K_LWIN -239 +#define K_RWIN -240 #define K_APP -241 #define K_SEARCH -242 -#define K_POWER 130 +#define K_POWER -130 #define K_VOLUP -243 #define K_VOLDOWN -244 #define K_JOY1 768 @@ -2893,15 +3361,76 @@ string(string digest, string data, ...) digest_hex = #639; #define K_AUX31 814 #define K_AUX32 815 #define K_PAUSE 153 -#define K_MWHEELUP 515 -#define K_MWHEELDOWN 516 #define K_PRINTSCREEN 174 #define K_CAPSLOCK 155 #define K_SCROLLLOCK 156 #define K_SEMICOLON 59 #define K_PLUS 43 +#define K_MINUS 45 #define K_TILDE 126 #define K_BACKQUOTE 96 #define K_BACKSLASH 92 +#define K_GP_A 826 +#define K_GP_B 827 +#define K_GP_X 828 +#define K_GP_Y 829 +#define K_GP_LSHOULDER 824 +#define K_GP_RSHOULDER 825 +#define K_GP_LTRIGGER 830 +#define K_GP_RTRIGGER 831 +#define K_GP_BACK 821 +#define K_GP_START 820 +#define K_GP_LTHUMB 822 +#define K_GP_RTHUMB 823 +#define K_GP_DPAD_UP 816 +#define K_GP_DPAD_DOWN 817 +#define K_GP_DPAD_LEFT 818 +#define K_GP_DPAD_RIGHT 819 +#define K_GP_GUIDE -202 +#define K_GP_UNKNOWN -255 +#define K_GP_LTHUMB_UP 832 +#define K_GP_LTHUMB_DOWN 833 +#define K_GP_LTHUMB_LEFT 834 +#define K_GP_LTHUMB_RIGHT 835 +#define K_GP_RTHUMB_UP 836 +#define K_GP_RTHUMB_DOWN 837 +#define K_GP_RTHUMB_LEFT 838 +#define K_GP_RTHUMB_RIGHT 839 +#endif +#ifdef _ACCESSORS +accessor strbuf : float +{ + inline get float asfloat[float idx] = {return stof(bufstr_get(this, idx));}; + inline set float asfloat[float idx] = {bufstr_set(this, idx, ftos(value));}; + get string[float] = bufstr_get; + set string[float] = bufstr_set; + get float length = buf_getsize; +}; +accessor searchhandle : float +{ + get string[float] = search_getfilename; + get float length = search_getsize; +}; +accessor hashtable : float +{ + inline get vector v[string key] = {return hash_get(this, key, '0 0 0', EV_VECTOR);}; + inline set vector v[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_VECTOR);}; + inline get string s[string key] = {return hash_get(this, key, "", EV_STRING);}; + inline set string s[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_STRING);}; + inline get float f[string key] = {return hash_get(this, key, 0.0, EV_FLOAT);}; + inline set float f[string key] = {hash_add(this, key, value, HASH_REPLACE|EV_FLOAT);}; + inline get __variant[string key] = {return hash_get(this, key, __NULL__);}; + inline set __variant[string key] = {hash_add(this, key, value, HASH_REPLACE);}; +}; +accessor infostring : string +{ + get string[string] = infoget; + inline set& string[string fld] = {this = infoadd(this, fld, value);}; +}; +accessor filestream : float +{ + get string = fgets; + inline set string = {fputs(this,value);}; +}; #endif #pragma noref 0 diff --git a/quakec/menusys/menu.src b/quakec/menusys/menu.src index 06b89b0d5..cedc10c9b 100644 --- a/quakec/menusys/menu.src +++ b/quakec/menusys/menu.src @@ -48,6 +48,9 @@ void(mitem_desktop desktop) M_Pop = cmd("m_load", M_Load, menu/loadsave.qc) \ cmd("m_save", M_Save, ) \ cmd("m_quit", M_Quit, menu/quit.qc) \ + cmd("m_mods", M_Menu_Mods, menu/mods.qc) \ + cmd("m_updates", M_Menu_Updates, menu/updates.qc) \ + cmd("m_cvars", M_Menu_Cvars, menu/cvars.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) \ @@ -71,7 +74,12 @@ void(mitem_desktop desktop) M_Pop = mitem_desktop desktop; void() m_shutdown = {}; -void(vector screensize) m_draw = {items_draw(desktop);}; +void(vector screensize) m_draw = +{ + if (dp_workarounds) + cltime = gettime(0); + items_draw(desktop); +}; 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);}; void(float mode) m_toggle @@ -90,6 +98,23 @@ void(float mode) m_toggle items_updategrabs(TRUE); }; +float(string cstr) m_consolecommand = +{ + tokenize(cstr); + string cmd = argv(0); + + switch(cmd) + { +//switch on the known commands. +#define cmd(n,f) case n: f(desktop); break; + concommandslist +#undef cmd + default: + return FALSE; + } + items_updategrabs(TRUE); + return TRUE; +} var float autocvar_dp_workarounds_allow = TRUE; var float autocvar_dp_workarounds_force = FALSE; @@ -97,10 +122,19 @@ void() m_init = { desktop = spawn(mitem_desktop); + if (checkbuiltin(registercommand)) + { +#define cmd(n,f) registercommand(n); + concommandslist +#undef cmd + } + else + { //register the console commands via the alias command. #define cmd(n,f) localcmd("alias " n " \"menu_cmd " n " $*\"\n"); - concommandslist + concommandslist #undef cmd + } //work around some dp differences/bugs. //this check identifies one significant bug in DP. diff --git a/quakec/menusys/menu/cvars.qc b/quakec/menusys/menu/cvars.qc new file mode 100644 index 000000000..ce6e904d9 --- /dev/null +++ b/quakec/menusys/menu/cvars.qc @@ -0,0 +1,265 @@ +#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 (flags & 32) + { + 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") + filter = newval; + 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 565e7412a..8a9ccb8b2 100644 --- a/quakec/menusys/menu/main.qc +++ b/quakec/menusys/menu/main.qc @@ -39,6 +39,12 @@ float(string cmd) assumefalsecheckcommand = return checkcommand(cmd); }; +float(__variant cmd, float assumption) checkbuiltin2 = +{ + if (!checkbuiltin(checkbuiltin)) //teehee + return assumption; + return checkbuiltin(cmd); +}; nonstatic void(mitem_desktop desktop) M_Main = @@ -88,8 +94,10 @@ nonstatic void(mitem_desktop desktop) M_Main = if (assumefalsecheckcommand("cef")) {menuitemtext_cladd16(m, _("Browser"), "m_pop;cef google.com", y); y += 16;} if (assumefalsecheckcommand("xmpp")) {menuitemtext_cladd16(m, _("Social"), "m_pop;xmpp", y); y += 16;} if (assumefalsecheckcommand("irc")) {menuitemtext_cladd16(m, _("IRC"), "m_pop;irc /info", y); y += 16;} - if (assumefalsecheckcommand("menu_download")) {menuitemtext_cladd16(m, _("Updates+Packages"), "m_pop;menu_download", y); y += 16;} + /*if (checkbuiltin2(getpackagemanagerinfo,FALSE)) {menuitemtext_cladd16(m, _("Updates+Packages"), "m_pop;m_updates", y); y += 16;} + else*/ if (assumefalsecheckcommand("menu_download")) {menuitemtext_cladd16(m, _("Updates+Packages"), "m_pop;menu_download", y); y += 16;} if (assumefalsecheckcommand("qi")) {menuitemtext_cladd16(m, _("Quake Injector"), "m_pop;qi", y); y += 16;} + if (checkbuiltin2(getgamedirinfo,TRUE)) {menuitemtext_cladd16(m, _("Mods"), "m_pop;m_mods", y); y += 16;} {menuitemtext_cladd16(m, _("Options"), "m_pop;m_options", y); y += 16;} {menuitemtext_cladd16(m, _("Quit"), "m_pop;m_quit", y); y += 16;} diff --git a/quakec/menusys/menu/mods.qc b/quakec/menusys/menu/mods.qc new file mode 100644 index 000000000..ba476d1c1 --- /dev/null +++ b/quakec/menusys/menu/mods.qc @@ -0,0 +1,77 @@ +class mitem_pictext : mitem_text +{ + string pic; + virtual void(vector pos) item_draw = + { + if (pic) + drawpic(pos, pic, [item_size_y,item_size_y], '1 1 1', 1, 0); + pos_x += item_size_y; + super::item_draw(pos); + }; +}; + +void(mitem_desktop desktop) M_Menu_Mods = +{ + float h = (480+240)/2; + + //create the menu, give it focus, and make sure its displayed over everything else. + mitem_exmenu m = spawn(mitem_exmenu, 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]); + + //spawn a container frame for the actual options. this provides a scrollbar if we have too many items. + 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_OWN_MIN|RS_Y_MAX_PARENT_MID, [-140, h*-0.5+32], [280, h*0.5]); + + float mod; + for (mod = 0; ; mod++) + { + string gamedir = getgamedirinfo(mod, GGDI_GAMEDIR); + if not(gamedir) + break; + string desc = getgamedirinfo(mod, GGDI_DESCRIPTION); + if (desc=="") + { //clean up some stuff a little, if we know it. + string lwr = strtolower(gamedir); //windows sucks. + if (lwr == "hipnotic") + desc = "Scourge of Armagon"; + else if (lwr == "rogue") + desc = "Dissolution of Eternity"; + else if (lwr == "dopa") + desc = "Dimension of the Past"; + else if (lwr == "ad") + desc = "Arcane Dimensions"; + else if (lwr == "quoth") + desc = "Quoth"; + else if (lwr == "rally") + desc = "Quake Rally"; + else if (lwr == "fortress") + desc = "Team Fortress"; + } + if (desc=="") + desc = sprintf("%s/", gamedir); //no description given, so make it clear that its an actual subdir name + else + desc = sprintf("%s ^h(%s)", desc, gamedir); //include the gamedir, faded somewhat. + string cmd = getgamedirinfo(mod, GGDI_LOADCOMMAND); + if not(cmd) //for dp users, if they somehow run this + cmd = sprintf("gamedir %s", gamedir); + + string icon = getgamedirinfo(mod, GGDI_ICON); + + //add some extra stuff to reset the menu + cmd = strcat("m_pop; ", cmd, "; togglemenu"); + + fr.add(spawn(mitem_pictext, pic:icon, item_text:desc, item_command:cmd, item_scale:16, item_flags:0), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, mod*16], [0, 16]); + } + + if (!mod) + fr.add(spawn(mitem_text, item_text:"No mods detected", item_scale:16, item_flags:0), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, mod*16], [0, 16]); + + //and give us a suitable menu tint too, just because. + addmenuback(m); +}; diff --git a/quakec/menusys/menu/options.qc b/quakec/menusys/menu/options.qc index b5a46b7d5..d80c4e9bf 100644 --- a/quakec/menusys/menu/options.qc +++ b/quakec/menusys/menu/options.qc @@ -38,6 +38,7 @@ nonstatic void(mitem_desktop desktop) M_Options = {fr.add(spawn(mitem_text, item_text:"Graphical Presets", item_command:"m_pop;m_preset", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;} fr.add(spawn(mitem_text, item_text:"Game Configs", item_command:"m_pop;m_configs", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16; fr.add(spawn(mitem_text, item_text:"Basic Setup", item_command:"m_pop;m_basicopts", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16; + fr.add(spawn(mitem_text, item_text:"Keys", item_command:"m_pop;m_keys", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16; fr.add(spawn(mitem_text, item_text:"Audio", item_command:"m_pop;m_audio", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16; fr.add(spawn(mitem_text, item_text:"Video", item_command:"m_pop;m_video", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16; fr.add(spawn(mitem_text, item_text:"Effects", item_command:"m_pop;m_effects", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16; @@ -45,11 +46,12 @@ nonstatic void(mitem_desktop desktop) M_Options = {fr.add(spawn(mitem_text, item_text:"Particles", item_command:"m_pop;m_particles", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;} if (assumefalsecheckcommand("ezhud_nquake")) {fr.add(spawn(mitem_text, item_text:"Hud", item_command:"m_pop;m_hud", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;} - fr.add(spawn(mitem_text, item_text:"Keys", item_command:"m_pop;m_keys", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16; - if (assumefalsecheckcommand("cfg_save")) - {fr.add(spawn(mitem_text, item_text:"Save Settings", item_command:"cfg_save", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;} + if (checkbuiltin2(buf_cvarlist, TRUE)) + {fr.add(spawn(mitem_text, item_text:"Advanced Guru Settings", item_command:"m_pop;m_cvars", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;} if (assumefalsecheckcommand("cvarreset")) {fr.add(spawn(mitem_text, item_text:"Reset to Defaults", item_command:"m_reset", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;} + if (assumefalsecheckcommand("cfg_save")) + {fr.add(spawn(mitem_text, item_text:"Save Settings", item_command:"cfg_save", item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]); pos += 16;} //random art for style #if 1//def CSQC diff --git a/quakec/menusys/menu/options_audio.qc b/quakec/menusys/menu/options_audio.qc index cfd7dcee3..ae509e472 100644 --- a/quakec/menusys/menu/options_audio.qc +++ b/quakec/menusys/menu/options_audio.qc @@ -1,8 +1,30 @@ +static class audiomenu : mitem_exmenu +{ + virtual string(string key) get = + { + if (key == "s_device" || key == "cl_voip_capturedevice") + { //the cvar supports multiple options, but we only support one. :( + //so just return the first to avoid getting too confused. + tokenize(super::get(key)); + return argv(0); + } + return super::get(key); + }; + virtual void(string key, string val) set = + { + if (key == "s_device" || key == "cl_voip_capturedevice") + { //add some quotes. + val = strcat("\"", val, "\""); + } + super::set(key, val); + }; +}; + nonstatic void(mitem_desktop desktop) M_Options_Audio = { local float pos; mitem_exmenu m; - m = spawn(mitem_exmenu, item_text:_("Audio Options"), item_flags:IF_SELECTABLE, item_command:"m_options"); + m = spawn(audiomenu, item_text:_("Audio Options"), item_flags:IF_SELECTABLE, item_command:"m_options"); 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(); @@ -29,12 +51,14 @@ nonstatic void(mitem_desktop desktop) M_Options_Audio = fr.add(menuitemslider_spawn(_("Music Volume"), "bgmvolume", '0 0.5 0.05', '280 8'), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemslider_spawn(_("Channels"), "s_numspeakers", '1 6 1', '280 8'), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcombo_spawn(_("Audio Quality"), "s_khz", '280 8', _( - "11025 \"11025hz (vanilla quake)\" " - "22050 \"22050hz\" " - "44100 \"44100hz (cd quality)\" " - "48000 \"48000hz (dvd quality)\" " - "96000 \"96000hz\" " - "192000 \"192000hz\" " + "8 \"8000hz (telephone quality)\" " + "11.025 \"11025hz (vanilla quake)\" " + "22.05 \"22050hz\" " + "44.1 \"44100hz (cd quality)\" " + "48 \"48000hz (dvd quality)\" " + //higher values are probably pointless when source data doesn't go that high, so not going to list them. + //"96 \"96000hz (blu-ray quality)\" " + //"192 \"192000hz (professional quality)\" " )), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcheck_spawn(_("Doppler"), "s_doppler", '280 8'), fl, [0, pos], [0, 8]); pos += 8; fr.add(menuitemcheck_spawn(_("8bit audio"), "s_loadas8bit", '280 8'), fl, [0, pos], [0, 8]); pos += 8; diff --git a/quakec/menusys/menu/options_hud.qc b/quakec/menusys/menu/options_hud.qc index 6e3381ca9..2ec6c5d3a 100644 --- a/quakec/menusys/menu/options_hud.qc +++ b/quakec/menusys/menu/options_hud.qc @@ -17,7 +17,7 @@ nonstatic void(mitem_desktop desktop) M_Options_Hud = //spawn a container frame for the actual options. this provides a scrollbar if we have too many items. mitem_frame fr = spawn(mitem_frame, item_flags: IF_SELECTABLE, frame_hasscroll:TRUE); - m.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h], [0, h*2]); + m.add(fr, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, -h+16], [0, h*2+32]); float fs = search_begin("huds/*.cfg", TRUE, TRUE); float fc = search_getsize(fs); diff --git a/quakec/menusys/menu/updates.qc b/quakec/menusys/menu/updates.qc new file mode 100644 index 000000000..72837e6c7 --- /dev/null +++ b/quakec/menusys/menu/updates.qc @@ -0,0 +1,184 @@ +#include "../menusys/mitem_grid.qc" + +enum +{ + GPMI_NAME, //name of the package, for use with the pkg command. + GPMI_CATEGORY, //category text + GPMI_TITLE, //name of the package, for showing the user. + GPMI_VERSION, //version info (may have multiple with the same name but different versions) + GPMI_DESCRIPTION, //some blurb + GPMI_LICENSE, //what license its distributed under + GPMI_AUTHOR, //name of the person(s) who created it + GPMI_WEBSITE, //where to contribute/find out more info/etc + GPMI_INSTALLED, //current state + GPMI_ACTION, //desired state + GPMI_AVAILABLE, //whether it may be downloaded or not. + GPMI_FILESIZE, //whether it may be downloaded or not. +}; + +class mitem_updategrid : mitem_grid +{ + virtual void(vector pos) item_draw = + { //make sure we see any updates as they're detected... + if (getpackagemanagerinfo(grid_numchildren, GPMI_NAME)) + { + grid_numchildren++; + while(getpackagemanagerinfo(grid_numchildren, GPMI_NAME)) + grid_numchildren++; + item_resized(); + } + super::item_draw(pos); + }; + 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_updategrid::grid_draw = +{ + string text = getpackagemanagerinfo(idx, GPMI_TITLE); + + 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; + + string status = getpackagemanagerinfo(idx, GPMI_INSTALLED); + string action = getpackagemanagerinfo(idx, GPMI_ACTION); + + if (action == "purge") //delete the thing + action = "^&F4 "; + else if (action=="reinstall") //purge then reinstall(the purge status was explicit at least.) + action = "^&F2P"; + else if (action=="user") //install(explcitly by user) + action = "^&F2 "; + else if (action=="auto") //install(to satisfy dependancies) + action = "^&FA "; + else if (action=="disable") + action = "^&FE "; + else if (action=="retain") + action = "^&FE "; + else + action = "^&F4 "; + + if (status == "0%" || status == "pending") + { + ui.drawfill(pos, [item_size_x, item_scale], '0 0 0.5', 0.1, 0); + action = status = " "; + } + else if (strstrofs(status, "%")>=0) + { + float frac = stof(status)/100; + ui.drawfill(pos, [item_size_x*frac, item_scale], '0 0 0.5', 0.9, 0); + action = status = " "; + } + else if (status == "corrupt") + status = "^&FE?"; //yellow + else if (status == "enabled") + status = "^&F2 "; //green + else if (status == "present") + status = "^&FE "; //yellow + else + status = "^&F4 "; //red + + ui.drawstring(pos, strcat(status, action, "^&F-", text), '1 1 0' * item_scale, col, item_alpha, 0); +}; +/*void(float olditem, float newitem) mitem_updategrid::grid_selectionchanged = +{ +}; +*/float(vector pos, float scan, float char, float down, float idx) mitem_updategrid::grid_keypress = +{ + string text; + if (!down) + return FALSE; + else if (scan == K_DEL||scan == K_BACKSPACE||scan == K_LEFTARROW) + { + text = getpackagemanagerinfo(idx, GPMI_NAME); + if (!text) + return FALSE; + + string action = getpackagemanagerinfo(idx, GPMI_ACTION); + if (action == "purge") //delete the thing + action = "rem"; + else if (action=="reinstall") //purge then reinstall(the purge status was explicit at least.) + action = "rem"; + else if (action=="user") //install(explcitly by user) + action = "rem"; + else if (action=="auto") //install(to satisfy dependancies) + action = "rem"; + else + action = "del"; + + text = strcat("pkg quiet_", action, " \"", text, "\"\n"); + localcmd(text); + } + else if (scan == K_RIGHTARROW || scan==K_ENTER || scan == K_MOUSE1) + { + text = getpackagemanagerinfo(idx, GPMI_NAME); + if (!text) + return FALSE; + text = strcat("pkg quiet_add \"", text, "\"\n"); + localcmd(text); + } + else + return FALSE; + return TRUE; +}; + + + + +class menu_updates : mitem_exmenu +{ + string filter; + + mitem_updategrid grid; + + virtual string(string key) get = + { + if (key == "info") + { + string text, tmp; + tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_VERSION); text = strcat(text, "^aVersion:^a ", tmp?:"Unspecified"); + tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_AUTHOR); text = strcat(text, "\n^aAuthor:^a ", tmp?:"Unknown"); + tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_LICENSE); text = strcat(text, "\n^aLicense:^a ", tmp?:"Unknown"); + tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_WEBSITE); text = strcat(text, "\n^aWebsite:^a ", tmp?:"Unknown"); + tmp = getpackagemanagerinfo(grid.grid_kactive, GPMI_DESCRIPTION); text = strcat(text, "\n^aDescription:^a ", tmp?:"No description specified"); + return text; + } + return super::get(key); + }; +}; + + + +void(mitem_desktop desktop) M_Menu_Updates = +{ + mitem it; + float h = (480+240)/2; + + localcmd("pkg update\n"); + + //create the menu, give it focus, and make sure its displayed over everything else. + menu_updates m = spawn(menu_updates, item_text:_("Updates 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]); + + //spawn our grid for the actual options. this provides a scrollbar if we have too many items. + m.grid = spawn(mitem_updategrid, item_flags: IF_SELECTABLE, item_scale: 8); + m.add(m.grid, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, h*-0.5+32], [40, h*0.5]); + + it = spawn(mitem_label, item_text: "info", item_flags: 0, item_scale: 8); + m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MID, [40, h*-0.5+32], [0, h*0.5-16]); + + it = spawn(mitem_button, item_text: "Apply", item_command: "pkg apply\n", item_flags: IF_SELECTABLE, item_scale: 8); + m.add(it, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MID, [40+1, h*0.5-16+1], [0-1, h*0.5-1]); + + //and give us a suitable menu tint too, just because. + addmenuback(m); +}; diff --git a/quakec/menusys/menusys/mitem_combo.qc b/quakec/menusys/menusys/mitem_combo.qc index 23f419bdd..daadf9b1e 100644 --- a/quakec/menusys/menusys/mitem_combo.qc +++ b/quakec/menusys/menusys/mitem_combo.qc @@ -11,13 +11,13 @@ The possible values is a separate popup. class mitem_combo; class mitem_combo_popup; -class mitem_combo : mitem +class mitem_combo : mitem_edit { virtual void(vector pos) item_draw; virtual float(vector pos, float scan, float char, float down) item_keypress; virtual void(mitem newfocus, float changedflag) item_focuschange; - mitem_combo_popup cfriend; + mitem_combo_popup cpopup; string mstrlist; float firstrow; float visrows; @@ -32,6 +32,10 @@ class mitem_combo : mitem item_flags &= ~IF_SELECTABLE; super::item_resized(); }; + void() mitem_combo = + { + spos = -1; + }; }; class mitem_combo_popup : mitem @@ -40,22 +44,28 @@ class mitem_combo_popup : mitem virtual float(vector pos, float scan, float char, float down) item_keypress; virtual void(mitem newfocus, float changedflag) item_focuschange; - mitem_combo pfriend; + mitem_combo pcombo; + mitem_vslider pslider; + float slidercache; virtual void() item_remove = { - if (pfriend) - pfriend.cfriend = 0; + if (pslider) + pslider.item_remove(); + if (pcombo) + pcombo.cpopup = 0; super::item_remove(); }; }; void() mitem_combo::item_remove = { - mitem_combo_popup p = cfriend; + mitem_combo_popup p = cpopup; if (p) p.item_remove(); strunzone(mstrlist); + item_text = __NULL__; + item_command = __NULL__; super::item_remove(); }; @@ -67,10 +77,16 @@ void(vector pos) mitem_combo::item_draw = if (!(item_flags & IF_SELECTABLE)) rgb *= 0.2; - if (cfriend) - cfriend.item_position = pos + [item_size_x / 2, item_size_y]; + if (cpopup) + cpopup.item_position = pos + [item_size_x / 2, item_size_y]; - super::item_draw(pos); + if (spos >= 0) + { + super::item_draw(pos); + return; + } + else + mitem::item_draw(pos); v = tokenize(mstrlist); @@ -105,17 +121,18 @@ void(vector pos) mitem_combo::item_draw = }; void(mitem newfocus, float flag) mitem_combo::item_focuschange = { - if (!cfriend || !(flag & IF_KFOCUSED)) + if (!cpopup || !(flag & IF_KFOCUSED)) return; //don't care - if (newfocus != (mitem)this && newfocus != (mitem)cfriend) + if (newfocus != (mitem)this && newfocus != (mitem)cpopup) { - cfriend.item_size = cfriend.maxs = '0 0'; - cfriend.item_flags &~= IF_SELECTABLE; + cpopup.item_size = cpopup.maxs = '0 0'; + cpopup.item_flags &~= IF_SELECTABLE; } + spos = -1; }; void(mitem newfocus, float flag) mitem_combo_popup::item_focuschange = { - pfriend.item_focuschange(newfocus, flag); + pcombo.item_focuschange(newfocus, flag); }; void(vector pos) mitem_combo_popup::item_draw = { @@ -123,11 +140,15 @@ void(vector pos) mitem_combo_popup::item_draw = if (item_size_y < 1) return; - local mitem_combo f = pfriend; + local mitem_combo f = pcombo; item_command = f.item_command; local string curval = f.get(f.item_command); local float i, m, c, v; + vector popupsize = item_size; + + + if (!((f.item_flags | item_flags) & IF_KFOCUSED)) { item_size = maxs = '0 0'; @@ -135,13 +156,13 @@ void(vector pos) mitem_combo_popup::item_draw = return; } - ui.drawfill(pos, item_size, item_rgb, item_alpha, 0); + ui.drawfill(pos, popupsize, item_rgb, item_alpha, 0); /* //border - ui.drawfill(pos, [item_size_x, 1], TD_BOT, item_alpha, 0); - ui.drawfill(pos, [1, item_size_y - 1], TD_RGT, item_alpha, 0); - ui.drawfill(pos + [item_size_x-1, 1], [1, item_size_y - 1], TD_LFT, item_alpha, 0); - ui.drawfill(pos + [0, item_size_y-1], [item_size_x, 1], TD_TOP, item_alpha, 0); + ui.drawfill(pos, [popupsize_x, 1], TD_BOT, item_alpha, 0); + ui.drawfill(pos, [1, popupsize_y - 1], TD_RGT, item_alpha, 0); + ui.drawfill(pos + [popupsize_x-1, 1], [1, popupsize_y - 1], TD_LFT, item_alpha, 0); + ui.drawfill(pos + [0, popupsize_y-1], [popupsize_x, 1], TD_TOP, item_alpha, 0); */ pos_x += 1; v = tokenize(f.mstrlist); @@ -149,13 +170,13 @@ void(vector pos) mitem_combo_popup::item_draw = if (argv(c) == curval) break; if (c >= v) - c = 0; + c = -1; i = f.firstrow; i = i*2; if (!f.visrows) i = 0; - else + else if (c >= 0) { //bound the displayed position if (c < i) @@ -163,17 +184,52 @@ void(vector pos) mitem_combo_popup::item_draw = if (i < c - (f.visrows-1)*2) i = c - (f.visrows-1)*2; } - m = i + f.visrows*2; f.firstrow = floor(i*0.5); - //constrain the drawing so it doesn't overflow the combo - ui.setcliparea(pos[0], pos[1], item_size[0], item_size[1]); + if (v > f.visrows*2) + { + if (!pslider) + { + slidercache = -2; + pslider = spawn(mitem_vslider); + } + } + else if (pslider) + { + pslider.item_remove(); + pslider = __NULL__; + } + + if (pslider) + { + popupsize_x -= pslider.item_size_x; + if (slidercache != c) + { //current index changed, force the slider to the active item. + slidercache = c; + pslider.val = f.firstrow; + } + pslider.maxv = v/2 - f.visrows; + pslider.item_size_y = popupsize_y; + pslider.item_draw(pos + [popupsize_x, 0]); + } + + //constrain the drawing so it doesn't overflow the popup + ui.setcliparea(pos[0], pos[1], popupsize[0], popupsize[1]); + + if (pslider) + { + f.firstrow = i = floor(pslider.val); + m = ((i!=pslider.val) + i + f.visrows)*2; + i *= 2; + pos[1] -= (pslider.val-i/2) * item_scale; + } + else m = i + f.visrows*2; for (; i < m && i < v ; i+=2) { col = f.item_rgb; if (item_flags & IF_MFOCUSED) - if (mouseinbox(pos, [item_size_x, item_scale])) + if (mouseinbox(pos, [popupsize_x, item_scale])) col_z = 0; if (c == i) col_x = 0; @@ -187,7 +243,14 @@ void(vector pos) mitem_combo_popup::item_draw = }; float(vector pos, float scan, float char, float down) mitem_combo_popup::item_keypress = { - return pfriend.item_keypress(pos - [0, pfriend.item_size_y], scan, char, down); + if (pslider && scan == K_MOUSE1) + { + vector sliderpos = pos + [item_size_x-pslider.item_size_x,0]; + if (mouseinbox(pos + [item_size_x-pslider.item_size_x,0], pslider.item_size)) + return pslider.item_keypress(sliderpos, scan, char, down); + } + + return pcombo.item_keypress(pos - [0, pcombo.item_size_y], scan, char, down); }; float(vector pos, float scan, float char, float down) mitem_combo::item_keypress = { @@ -210,21 +273,21 @@ float(vector pos, float scan, float char, float down) mitem_combo::item_keypress if (scan == K_ESCAPE || scan == K_MOUSE2) { - if (cfriend) + if (cpopup) { - cfriend.item_remove(); + cpopup.item_remove(); return TRUE; } return FALSE; } - else if (scan == K_MWHEELUP || (scan == K_UPARROW && cfriend)) + else if (scan == K_MWHEELUP || (scan == K_UPARROW && cpopup)) { i -= 2; if (i < 0) i = c - 2; curval = argv(i); } - else if (scan == K_MWHEELDOWN || (scan == K_DOWNARROW && cfriend)) + else if (scan == K_MWHEELDOWN || (scan == K_DOWNARROW && cpopup)) { i += 2; if (i >= c) @@ -233,20 +296,20 @@ float(vector pos, float scan, float char, float down) mitem_combo::item_keypress } else if (scan == K_MOUSE1 || scan == K_ENTER) { - if (scan == K_ENTER && cfriend) + if (scan == K_ENTER && cpopup) { - cfriend.item_remove(); + cpopup.item_remove(); return TRUE; } visrows = ((c>18)?18/2:c/2); - if (!cfriend) + if (!cpopup) { - cfriend = spawn(mitem_combo_popup); - cfriend.pfriend = this; - cfriend.item_scale = 8; - cfriend.item_rgb = MENUBACK_RGB; - cfriend.item_alpha = MENUBACK_ALPHA; + cpopup = spawn(mitem_combo_popup); + cpopup.pcombo = this; + cpopup.item_scale = 8; + cpopup.item_rgb = MENUBACK_RGB; + cpopup.item_alpha = MENUBACK_ALPHA; pos = item_position; mitem_frame fr = item_parent; while (fr.item_parent) @@ -254,36 +317,44 @@ float(vector pos, float scan, float char, float down) mitem_combo::item_keypress pos += fr.item_position; fr = fr.item_parent; } - fr.addr(cfriend, RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN, pos + [self.item_size_x / 2, self.item_size_y], [item_size_x*0.5, item_size_y*visrows]); + fr.addr(cpopup, RS_X_MIN_PARENT_MIN | RS_X_MAX_OWN_MIN | RS_Y_MIN_PARENT_MIN | RS_Y_MAX_OWN_MIN, pos + [self.item_size_x / 2, self.item_size_y], [item_size_x*0.5, item_size_y*visrows]); } - cfriend.item_size = cfriend.maxs = [item_size_x*0.5, item_size_y*visrows]; - cfriend.item_flags |= IF_SELECTABLE; - cfriend.totop(); + cpopup.item_size = cpopup.maxs = [item_size_x*0.5, item_size_y*visrows]; + cpopup.item_flags |= IF_SELECTABLE; + cpopup.totop(); - if (scan == K_MOUSE1 && (cfriend.item_flags & IF_MFOCUSED)) + if (scan == K_MOUSE1 && (cpopup.item_flags & IF_MFOCUSED)) { //if they clicked inside the popup, change the selected item. f = ui.mousepos[1] - (pos_y + item_size_y); - f /= cfriend.item_scale; + f /= cpopup.item_scale; f += firstrow; i = floor(f) * 2; if (i < c) { curval = argv(i); - cfriend.item_flags &~= IF_SELECTABLE; - cfriend.item_size = cfriend.maxs = '0 0'; + cpopup.item_flags &~= IF_SELECTABLE; + cpopup.item_size = cpopup.maxs = '0 0'; item_parent.item_focuschange(this, IF_MFOCUSED|IF_KFOCUSED); - cfriend.item_remove(); + cpopup.item_remove(); } } } - else if (scan == K_BACKSPACE || scan == K_DEL) - curval = substring(curval, 0, -2); - else if (char >= ' ') - curval = strcat(curval, chr2str(char)); else - return FALSE; + { + if (spos < 0) + { + spos = strlen(curval); + if (!super::item_keypress(pos, scan, char, down)) + { + spos = -1; + return FALSE; + } + return TRUE; + } + else return super::item_keypress(pos, scan, char, down); + } set(item_command, curval); return TRUE; diff --git a/quakec/menusys/menusys/mitem_desktop.qc b/quakec/menusys/menusys/mitem_desktop.qc index d11c38655..e1a9bde9d 100644 --- a/quakec/menusys/menusys/mitem_desktop.qc +++ b/quakec/menusys/menusys/mitem_desktop.qc @@ -321,10 +321,11 @@ void(mitem_desktop desktop) items_draw = items_updategrabs(FALSE); if (dp_workarounds && oldgrabstate) { - if (drawgetimagesize(autocvar_cl_cursor) == '0 0') +// //hopefully dp isn't broken and reports non-zero sizes for files that failed. +// if (drawgetimagesize(autocvar_cl_cursor) == '0 0') +// ui.drawpic(ui.mousepos - autocvar_cl_cursorbias*'1 1', autocvar_cl_cursor, autocvar_cl_cursorsize*'1 1', '1 1 1', 1, 0); +// else ui.drawcharacter(ui.mousepos - [stringwidth("+", TRUE, '4 4')*0.5, 4], '+', '8 8', '1 1 1', 1, 0); - else - ui.drawpic(ui.mousepos - autocvar_cl_cursorbias*'1 1', autocvar_cl_cursor, autocvar_cl_cursorsize*'1 1', '1 1 1', 1, 0); } #ifndef MENU diff --git a/quakec/menusys/menusys/mitem_edittext.qc b/quakec/menusys/menusys/mitem_edittext.qc index 825065a5c..4b9195bfd 100644 --- a/quakec/menusys/menusys/mitem_edittext.qc +++ b/quakec/menusys/menusys/mitem_edittext.qc @@ -21,8 +21,10 @@ class mitem_edit : mitem void() mitem_edit::item_remove = { - strunzone(item_text); - strunzone(item_command); + if (item_text) + strunzone(item_text); + if (item_command) + strunzone(item_command); super::item_remove(); }; @@ -59,11 +61,20 @@ float(vector pos, float scan, float char, float down) mitem_edit::item_keypress spos = max(spos-1, 0); else if (scan == K_RIGHTARROW) spos+=1; -/* else if (scan == K_MOUSE1) + else if (scan == K_HOME) + spos = 0; + else if (scan == K_END) + spos = strlen(curval); + else if (scan == K_MOUSE1) { - //FIXME: figure out the spos for the cursor - return TRUE; - }*/ + float valuepos = pos_x+(item_size_x/2)+1; + if (ui.mousepos[0] > valuepos-8) + { + spos = strlen(curval); + while (spos>0 && ui.mousepos[0] < valuepos+stringwidth(substring(curval, 0, spos), TRUE, '1 1 0'*item_scale)) + spos--; + } + } else if (scan == K_BACKSPACE || scan == K_DEL) { if (spos) @@ -89,7 +100,7 @@ mitem_edit(string text, string command, vector sz) menuitemeditt_spawn = n.item_scale = sz_y; n.item_text = strzone(text); n.item_size = sz; - n.spos = 100000; //will be clipped so meh + n.spos = 10000000; //will be clipped so meh n.item_command = strzone(command); n.item_flags |= IF_SELECTABLE; diff --git a/quakec/menusys/menusys/mitem_frame.qc b/quakec/menusys/menusys/mitem_frame.qc index 9a119766a..b8de18010 100644 --- a/quakec/menusys/menusys/mitem_frame.qc +++ b/quakec/menusys/menusys/mitem_frame.qc @@ -33,6 +33,7 @@ class mitem_frame : mitem mitem_vslider vslider; //displayed if any client item's max[y] > our item_size[y] vector item_framesize; //x=sides, y=top, z=bottom + float frame_stdheight; //adjust RS_Y_MIN_PARENT_MID to pinch inwards when the frame is smaller than this. Child items crossing the mid-point must size gracefully (like subframes). float frame_hasscroll; static mitem(string title) findchildtext; @@ -49,6 +50,71 @@ class mitem_frame : mitem }; }; +void(mitem ch, vector parentsize) mitem_parentresized = +{ + float f = ch.resizeflags; + + if (f & RS_X_FRACTION) + ch.item_position[0] = parentsize[0] * ch.mins[0]; + else if (f & RS_X_MIN_PARENT_MAX) + ch.item_position[0] = parentsize[0] + ch.mins[0]; + else if (f & RS_X_MIN_PARENT_MID) + ch.item_position[0] = parentsize[0]/2 + ch.mins[0]; + else //if (f & RS_X_MIN_PARENT_MIN) + ch.item_position[0] = ch.mins[0]; + + if (f & RS_X_FRACTION) + ch.item_position[0] = parentsize[0] * ch.maxs[0]; + else if (f & RS_X_MAX_PARENT_MIN) + ch.item_size[0] = ch.maxs[0] - ch.item_position[0]; + else if (f & RS_X_MAX_PARENT_MID) + ch.item_size[0] = ch.maxs[0]+parentsize[0]/2 - ch.item_position[0]; + else if (f & RS_X_MAX_PARENT_MAX) + ch.item_size[0] = ch.maxs[0]+parentsize[0] - ch.item_position[0]; + else //if (f & RS_X_MAX_OWN_MIN) + ch.item_size[0] = ch.maxs[0]; + + if (f & RS_Y_FRACTION) + ch.item_position[1] = parentsize[1] * ch.mins[1]; + else if (f & RS_Y_MIN_PARENT_MAX) + ch.item_position[1] = parentsize[1] + ch.mins[1]; + else if (f & RS_Y_MIN_PARENT_MID) + { + ch.item_position[1] = parentsize[1]/2 + ch.mins[1]; + if (ch.item_parent.frame_stdheight) + if (parentsize[1] < ch.item_parent.frame_stdheight) + { + if (ch.item_position[1] < parentsize[1]*0.5) + ch.item_position[1] += (ch.item_parent.frame_stdheight-parentsize[1])*0.5; + else + ch.item_position[1] -= (ch.item_parent.frame_stdheight-parentsize[1])*0.5; + } + } + else //if (f & RS_Y_MIN_PARENT_MIN) + ch.item_position[1] = ch.mins[1]; + + if (f & RS_Y_FRACTION) + ch.item_position[1] = parentsize[1] * ch.maxs[1]; + else if (f & RS_Y_MAX_PARENT_MIN) + ch.item_size[1] = ch.maxs[1] - ch.item_position[1]; + else if (f & RS_Y_MAX_PARENT_MID) + { + ch.item_size[1] = ch.maxs[1]+parentsize[1]/2 - ch.item_position[1]; + if (ch.item_parent.frame_stdheight) + if (parentsize[1] < ch.item_parent.frame_stdheight) + { + if (ch.item_position[1]+ch.item_size[1] < parentsize[1]*0.5) + ch.item_size[1] += (ch.item_parent.frame_stdheight-parentsize[1])*0.5; + else + ch.item_size[1] -= (ch.item_parent.frame_stdheight-parentsize[1])*0.5; + } + } + else if (f & RS_Y_MAX_PARENT_MAX) + ch.item_size[1] = ch.maxs[1]+parentsize[1] - ch.item_position[1]; + else //if (f & RS_Y_MAX_OWN_MIN) + ch.item_size[1] = ch.maxs[1]; +}; + void(mitem fromitem, string cmd) mitem_frame::item_execcommand = { if (item_parent) @@ -90,7 +156,8 @@ void(mitem newitem, float originflags, vector originmin, vector originmax) mitem newitem.resizeflags = originflags; newitem.mins = originmin; newitem.maxs = originmax; - mitem_parentresized(newitem, [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])]); + local vector parentsize = [item_size[0] - item_framesize[0]*2, item_size[1] - (item_framesize[1] + item_framesize[2])]; + mitem_parentresized(newitem, parentsize); newitem.item_resized(); item_flags |= IF_CLIENTMOVED; //make sure it happens. @@ -312,7 +379,7 @@ void(vector pos) mitem_vslider::item_draw = float h = item_size[1]; float isize = w; - vector v = drawgetimagesize("scrollbars/slider.tga"); + vector v = dp_workarounds?'0 0 0':drawgetimagesize("scrollbars/slider.tga"); if (v != '0 0 0') { @@ -352,7 +419,7 @@ void(vector pos) mitem_vslider::item_draw = }; void() mitem_vslider::mitem_vslider = { - item_size[0] = 16; + item_size[0] = 8; item_size[1] = 128; }; diff --git a/quakec/menusys/menusys/mitem_grid.qc b/quakec/menusys/menusys/mitem_grid.qc new file mode 100644 index 000000000..4e9a21000 --- /dev/null +++ b/quakec/menusys/menusys/mitem_grid.qc @@ -0,0 +1,210 @@ +#ifndef MITEM_GRID_QC__ +#define MITEM_GRID_QC__ + +//simple key/value grid (with editing). +class mitem_grid : mitem +{ + //virtual void(mitem newfocus, float changedflag) item_focuschange; + virtual float(vector pos, float scan, float char, float down) item_keypress; + virtual void(vector pos) item_draw; + virtual void() item_resized; + + //first child is determined from the vslider. + float grid_numchildren; + float grid_mactive; + float grid_kactive; + + mitem_vslider vslider; //displayed if any client item's max[y] > our item_size[y] + + virtual void(vector pos, float idx) grid_draw = 0; + virtual float(vector pos, float scan, float char, float down, float idx) grid_keypress = {return FALSE;}; + virtual void(float olditem, float newitem) grid_selectionchanged = {}; //just key focus +}; + +//does NOT wrap. +//does NOT pass go. +static mitem(mitem item, float upwards) menu_simplenextitem = +{ + mitem_frame menu = item.item_parent; + mitem prev; + if (upwards) + { + if (item) + { + item = item.item_next; + if (!item) + return __NULL__; + return item; + } + else + return __NULL__; + } + else + { + for(prev = menu.item_children; prev.item_next; prev = prev.item_next) + { + if (prev.item_next == item) + return prev; + } + return __NULL__; + } +}; + +float(vector pos, float scan, float char, float down) mitem_grid::item_keypress = +{ + float ch; + float handled = FALSE; + + if (scan >= K_MOUSE1 && scan <= K_MOUSE5 && scan != K_MWHEELUP && scan != K_MWHEELDOWN) + { + ch = grid_mactive; + if (ch != grid_kactive) + if (down && scan == K_MOUSE1) //keyboard focus follows on mouse click. + { + item_focuschange((ch>=0)?__NULL__:vslider, IF_KFOCUSED); + grid_selectionchanged(grid_kactive, ch); + grid_kactive = ch; + handled = TRUE; + } + } + else + { + ch = grid_kactive; + if (ch<0 && down) + { + //if there's no key child active, then go and pick one so you can just start using keyboard access etc. + if (grid_mactive) + ch = grid_mactive; + else + ch = 0; + item_focuschange((ch>=0)?__NULL__:vslider, IF_KFOCUSED); + grid_selectionchanged(grid_kactive, ch); + grid_kactive = ch; + } + } + + if (vslider) + pos[1] -= vslider.val; + + if (ch<0&&vslider) + { + if (vslider.item_keypress) + handled = vslider.item_keypress(pos + vslider.item_position, scan, char, down); + } + else if (!handled && ch >= 0 && ch < grid_numchildren) + handled = grid_keypress(pos + vslider.item_position + [0,ch]*item_scale, scan, char, down, ch); + if (!handled && down) + { + ch = grid_kactive; + switch(scan) + { + case K_MWHEELUP: + case K_MWHEELDOWN: + if (vslider) //give mwheel to the slider if its visible. + handled = vslider.item_keypress(pos + vslider.item_position, scan, char, down); + break; + case K_HOME: + ch = 0; + break; + case K_END: + ch = grid_numchildren-1; + break; + case K_PGUP: + ch = max(0, grid_kactive-5); + break; + case K_PGDN: + ch = min(grid_numchildren-1, grid_kactive+5); + break; + case K_UPARROW: + ch = max(grid_kactive-1, 0); + break; + case K_DOWNARROW: + ch = min(grid_kactive+1, grid_numchildren-1); + break; + } + if (ch != grid_kactive) + { + grid_selectionchanged(grid_kactive, ch); + grid_kactive = ch; + handled = TRUE; + } + } + + return handled; +}; +void() mitem_grid::item_resized = +{ + float clientheight = grid_numchildren * item_scale; + if (clientheight > item_size[1]) + { + if (!vslider) + vslider = spawn(mitem_vslider, val:0, stride:8); + vslider.maxv = clientheight - item_size[1]; + } + else if (vslider) + { + vslider.item_remove(); + vslider = (mitem_vslider)__NULL__; + } +} + +void(vector pos) mitem_grid::item_draw = +{ + local vector omin = ui.drawrectmin, omax = ui.drawrectmax; + local vector clientpos; + local vector clientsize; + + clientpos = pos; + clientsize = this.item_size; + if (vslider) + { + //scroll+shrink the client area to fit the slider on it. + clientpos[1] -= vslider.val; + clientsize[0] -= vslider.item_size[0]; + } + + if (ui.mousepos != ui.oldmousepos) + { + local mitem newfocus = __NULL__; + grid_mactive = floor((ui.mousepos[1]-clientpos_y)/item_scale); + + if (vslider) + if (mouseinbox(pos + [clientsize[0], 0], vslider.item_size)) + { + newfocus = vslider; + grid_mactive = -1; + } + this.item_focuschange(newfocus, IF_MFOCUSED); + } + + //clip the draw rect to our area, so children don't draw outside it. don't draw if its inverted. + if (pos_x > ui.drawrectmin[0]) + ui.drawrectmin[0] = pos_x; + if (pos_y > ui.drawrectmin[1]) + ui.drawrectmin[1] = pos_y; + if (pos_x+clientsize_x < ui.drawrectmax[0]) + ui.drawrectmax[0] = pos_x+clientsize_x; + if (pos_y+clientsize_y < ui.drawrectmax[1]) + ui.drawrectmax[1] = pos_y+clientsize[1]; + if (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1]) + { + ui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]); + float c = max(0,floor((ui.drawrectmin[1]-clientpos_y)/item_scale)); //calculate how many we can skip + for (clientpos_y += item_scale * c; c < grid_numchildren; c++, clientpos_y += item_scale) + { + if (clientpos_y >= ui.drawrectmax[1]) + break; + grid_draw(clientpos, c); + } + ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y); + + if (vslider) + { + vslider.item_size[1] = clientsize[1]; + vslider.item_draw(pos + [clientsize[0], 0]); + } + } + ui.drawrectmin = omin; + ui.drawrectmax = omax; +}; +#endif diff --git a/quakec/menusys/menusys/mitems.qc b/quakec/menusys/menusys/mitems.qc index 782b580f6..2661fec18 100644 --- a/quakec/menusys/menusys/mitems.qc +++ b/quakec/menusys/menusys/mitems.qc @@ -190,50 +190,7 @@ float(vector minp, vector sz) mouseinbox = }; -void(mitem ch, vector parentsize) mitem_parentresized = -{ - float f = ch.resizeflags; - - if (f & RS_X_FRACTION) - ch.item_position[0] = parentsize[0] * ch.mins[0]; - else if (f & RS_X_MIN_PARENT_MAX) - ch.item_position[0] = parentsize[0] + ch.mins[0]; - else if (f & RS_X_MIN_PARENT_MID) - ch.item_position[0] = parentsize[0]/2 + ch.mins[0]; - else //if (f & RS_X_MIN_PARENT_MIN) - ch.item_position[0] = ch.mins[0]; - - if (f & RS_X_FRACTION) - ch.item_position[0] = parentsize[0] * ch.maxs[0]; - else if (f & RS_X_MAX_PARENT_MIN) - ch.item_size[0] = ch.maxs[0] - ch.item_position[0]; - else if (f & RS_X_MAX_PARENT_MID) - ch.item_size[0] = ch.maxs[0]+parentsize[0]/2 - ch.item_position[0]; - else if (f & RS_X_MAX_PARENT_MAX) - ch.item_size[0] = ch.maxs[0]+parentsize[0] - ch.item_position[0]; - else //if (f & RS_X_MAX_OWN_MIN) - ch.item_size[0] = ch.maxs[0]; - - if (f & RS_Y_FRACTION) - ch.item_position[1] = parentsize[1] * ch.mins[1]; - else if (f & RS_Y_MIN_PARENT_MAX) - ch.item_position[1] = parentsize[1] + ch.mins[1]; - else if (f & RS_Y_MIN_PARENT_MID) - ch.item_position[1] = parentsize[1]/2 + ch.mins[1]; - else //if (f & RS_Y_MIN_PARENT_MIN) - ch.item_position[1] = ch.mins[1]; - - if (f & RS_Y_FRACTION) - ch.item_position[1] = parentsize[1] * ch.maxs[1]; - else if (f & RS_Y_MAX_PARENT_MIN) - ch.item_size[1] = ch.maxs[1] - ch.item_position[1]; - else if (f & RS_Y_MAX_PARENT_MID) - ch.item_size[1] = ch.maxs[1]+parentsize[1]/2 - ch.item_position[1]; - else if (f & RS_Y_MAX_PARENT_MAX) - ch.item_size[1] = ch.maxs[1]+parentsize[1] - ch.item_position[1]; - else //if (f & RS_Y_MAX_OWN_MIN) - ch.item_size[1] = ch.maxs[1]; -}; +void(mitem ch, vector parentsize) mitem_parentresized; #include "mitem_frame.qc" void() mitem::item_resized = diff --git a/quakec/menusys/menusys/mitems_common.qc b/quakec/menusys/menusys/mitems_common.qc index b8b73a3be..ce119473f 100644 --- a/quakec/menusys/menusys/mitems_common.qc +++ b/quakec/menusys/menusys/mitems_common.qc @@ -133,6 +133,118 @@ mitem(string text, string command, float height) menuitemtext_spawn = }; +class mitem_label : mitem +{ //class that queries its frame to find the text to show. + mitem_vslider vslider; + virtual void(vector pos) item_draw = + { + vector rgb = menuitem_textcolour(this); + string text = get(item_text); + float fl = 2; //top-align + vector textpos = pos; + vector clientsize = item_size; + if (item_flags & IF_CENTERALIGN) + fl |= 0; //default is to center align + else if (item_flags & IF_RIGHTALIGN) + fl |= 4; //right align. + else + fl |= 1; //left align. + + if (vslider) + { + textpos[1] -= vslider.val; + clientsize[1] += vslider.val; + clientsize[0] -= vslider.item_size[0]; + } + + local vector omin = ui.drawrectmin, omax = ui.drawrectmax; + + //clip the draw rect to our area, so text doesn't appear outside it. don't draw if its inverted. + if (pos_x > ui.drawrectmin[0]) + ui.drawrectmin[0] = pos_x; + if (pos_y > ui.drawrectmin[1]) + ui.drawrectmin[1] = pos_y; + if (pos_x+clientsize_x < ui.drawrectmax[0]) + ui.drawrectmax[0] = pos_x+clientsize_x; + if (pos_y+clientsize_y < ui.drawrectmax[1]) + ui.drawrectmax[1] = pos_y+clientsize[1]; + if (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1]) + { + ui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]); + clientsize[1] = drawtextfield(textpos, clientsize, fl, text) * item_scale; + ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y); + + if (vslider) + { + vslider.item_size[1] = item_size[1]; + if (ui.mousepos != ui.oldmousepos) + { + if (mouseinbox(pos + [clientsize[0], 0], vslider.item_size)) + this.item_focuschange(vslider, IF_MFOCUSED); + } + vslider.item_draw(pos + [clientsize[0], 0]); + } + } + ui.drawrectmin = omin; + ui.drawrectmax = omax; + + if (clientsize[1] > item_size[1]) + { //enough text that we need a scrollbar. + if (!vslider) + vslider = spawn(mitem_vslider, val:0, stride:8); + vslider.maxv = clientsize[1] - item_size[1]; + item_flags |= IF_SELECTABLE; + } + else if (vslider) + { //the text got shorter... + vslider.item_remove(); + vslider = (mitem_vslider)__NULL__; + if (item_command == "") + item_flags &~= IF_SELECTABLE; + } + }; + virtual float(vector pos, float scan, float char, float down) item_keypress = + { + if (vslider) + if (vslider.item_keypress(pos, scan, char, down)) + return TRUE; + if (this.item_command) + { + if (!down) + return FALSE; + if (scan == K_ENTER || (scan == K_MOUSE1 && mouseinbox(pos, this.item_size))) + { + item_parent.item_execcommand(this, this.item_command); +// localcmd(strcat(this.item_command, "\n")); + return TRUE; + } + } + return FALSE; + }; + + //zone+unzone input strings as needed + virtual void() item_remove = + { + if (item_command) + strunzone(item_command); + if (vslider) + { + vslider.item_remove(); + vslider = (mitem_vslider)__NULL__; + } + super::item_remove(); + }; + void() mitem_label = + { + if (item_command != "") + { + item_command = strzone(item_command); + item_flags |= IF_SELECTABLE; + } + }; +}; + + /*************************************************************************** basic text item. identical to text, but includes a 3dish border.