mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 08:00:51 +00:00
12c84046f3
This is an extremely extensive patch as it hits every cvar, and every usage of the cvars. Cvars no longer store the value they control, instead, they use a cexpr value object to reference the value and specify the value's type (currently, a null type is used for strings). Non-string cvars are passed through cexpr, allowing expressions in the cvars' settings. Also, cvars have returned to an enhanced version of the original (id quake) registration scheme. As a minor benefit, relevant code having direct access to the cvar-controlled variables is probably a slight optimization as it removed a pointer dereference, and the variables can be located for data locality. The static cvar descriptors are made private as an additional safety layer, though there's nothing stopping external modification via Cvar_FindVar (which is needed for adding listeners). While not used yet (partly due to working out the design), cvars can have a validation function. Registering a cvar allows a primary listener (and its data) to be specified: it will always be called first when the cvar is modified. The combination of proper listeners and direct access to the controlled variable greatly simplifies the more complex cvar interactions as much less null checking is required, and there's no need for one cvar's callback to call another's. nq-x11 is known to work at least well enough for the demos. More testing will come.
860 lines
18 KiB
C
860 lines
18 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
#if defined(_WIN32) && defined(HAVE_MALLOC_H)
|
|
# include <malloc.h>
|
|
#endif
|
|
#include <ctype.h>
|
|
|
|
#include "qfalloca.h"
|
|
|
|
#include "QF/cmd.h"
|
|
#include "QF/dstring.h"
|
|
#include "QF/quakefs.h"
|
|
#include "QF/sys.h"
|
|
|
|
#include "qw/include/server.h"
|
|
#include "qw/include/sv_pr_cpqw.h"
|
|
#include "qw/include/sv_progs.h"
|
|
#include "world.h"
|
|
|
|
static struct {
|
|
pr_func_t ClientCommand;
|
|
} cpqw_funcs;
|
|
|
|
/*
|
|
PF_getuid
|
|
|
|
float(entity client) getuid
|
|
*/
|
|
static void
|
|
PF_getuid (progs_t *pr, void *data)
|
|
{
|
|
edict_t *client_ent;
|
|
int e_num;
|
|
|
|
float retval = 0;
|
|
|
|
client_ent = P_EDICT (pr, 0);
|
|
e_num = NUM_FOR_EDICT (pr, client_ent);
|
|
|
|
if (e_num <= MAX_CLIENTS && e_num >= 1)
|
|
retval = (float) svs.clients[e_num - 1].userid;
|
|
|
|
R_FLOAT (pr) = retval;
|
|
}
|
|
|
|
/*
|
|
PF_strcat
|
|
|
|
string(string st1, string st2) strcat
|
|
*/
|
|
static void
|
|
PF_strcat (progs_t *pr, void *data)
|
|
{
|
|
const char *st1 = P_GSTRING (pr, 0);
|
|
const char *st2 = P_GSTRING (pr, 1);
|
|
|
|
R_STRING (pr) = PR_CatStrings (pr, st1, st2);
|
|
}
|
|
|
|
/*
|
|
PF_padstr
|
|
|
|
string(string st, float len) padstr
|
|
*/
|
|
static void
|
|
PF_padstr (progs_t *pr, void *data)
|
|
{
|
|
const char *st;
|
|
size_t i, padlen, givenlen;
|
|
char *padding;
|
|
|
|
st = P_GSTRING (pr, 0);
|
|
padlen = (unsigned int) P_FLOAT (pr, 1);
|
|
givenlen = strlen (st);
|
|
|
|
// Check if nothing should be done due to error or no need to..
|
|
if ( (padlen <= givenlen)) { // nothing should be done
|
|
R_INT (pr) = P_STRING (pr, 0); // return given string
|
|
return;
|
|
}
|
|
// ok, lets pad it!
|
|
padlen -= givenlen;
|
|
padding = alloca (padlen + 1);
|
|
|
|
for (i = 0; i < padlen; i++)
|
|
padding[i] = ' ';
|
|
padding[i] = '\0';
|
|
|
|
R_STRING (pr) = PR_CatStrings (pr, st, padding);
|
|
}
|
|
|
|
/*
|
|
PF_colstr
|
|
|
|
string(string srcstr, float action) colstr
|
|
*/
|
|
// Possible action values
|
|
#define COLSTR_WHITE 0 // converts any red chars to white
|
|
#define COLSTR_MIX1 1 // mix red and white chars
|
|
#define COLSTR_RED 2 // converts any white chars to red
|
|
#define COLSTR_MIX2 3 // mix red and white chars
|
|
#define COLSTR_NUMBER 4 // converts any numbers to special
|
|
// number chars
|
|
#define COLSTR_NUMBERV 5 // second variant of special number
|
|
// chars (different on only some
|
|
// non-original charsets)
|
|
|
|
static void
|
|
PF_colstr (progs_t *pr, void *data)
|
|
{
|
|
const char *srcstr;
|
|
unsigned char *result;
|
|
unsigned action;
|
|
size_t len, i;
|
|
|
|
srcstr = P_GSTRING (pr, 0);
|
|
action = (unsigned) P_FLOAT (pr, 1);
|
|
len = strlen (srcstr);
|
|
|
|
// Check for errors, if any, return given string
|
|
if (len == 0 || action > COLSTR_NUMBERV) {
|
|
R_INT (pr) = P_STRING (pr, 0); // return given string
|
|
return;
|
|
}
|
|
// Process string..
|
|
result = alloca (len + 1);
|
|
strcpy ((char *) result, srcstr);
|
|
|
|
switch (action) {
|
|
case COLSTR_WHITE:
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (result[i] > 160) // is red char
|
|
result[i] -= 128;
|
|
}
|
|
|
|
break;
|
|
|
|
case COLSTR_MIX1:
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (i & 0x01) {
|
|
if (result[i] < 128 && result[i] > 32) // is white char
|
|
result[i] += 128;
|
|
} else {
|
|
if (result[i] > 160) // is red char
|
|
result[i] -= 128;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case COLSTR_RED:
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (result[i] < 128 && result[i] > 32) // is white char
|
|
result[i] += 128;
|
|
}
|
|
|
|
break;
|
|
|
|
case COLSTR_MIX2:
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (i & 0x01) {
|
|
if (result[i] > 160) // is red char
|
|
result[i] -= 128;
|
|
} else {
|
|
if (result[i] < 128 && result[i] > 32) // is white char
|
|
result[i] += 128;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case COLSTR_NUMBER:
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (result[i] > 47 && result[i] < 58) // is a white number
|
|
result[i] -= 30;
|
|
}
|
|
|
|
break;
|
|
|
|
case COLSTR_NUMBERV:
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (result[i] > 47 && result[i] < 58) // is a white number
|
|
result[i] += 98;
|
|
}
|
|
}
|
|
|
|
RETURN_STRING (pr, (char *) result);
|
|
}
|
|
|
|
/*
|
|
PF_strcasecmp
|
|
|
|
float(string st1, string st2) strcasecmp
|
|
*/
|
|
static void
|
|
PF_strcasecmp (progs_t *pr, void *data)
|
|
{
|
|
const char *st1;
|
|
const char *st2;
|
|
|
|
st1 = P_GSTRING (pr, 0);
|
|
st2 = P_GSTRING (pr, 1);
|
|
|
|
R_FLOAT (pr) = (float) strncasecmp (st1, st2, 99999);
|
|
}
|
|
|
|
/*
|
|
PF_strlen
|
|
|
|
float(string st) strlen
|
|
*/
|
|
|
|
static void
|
|
PF_strlen (progs_t *pr, void *data)
|
|
{
|
|
const char *st;
|
|
|
|
st = P_GSTRING (pr, 0);
|
|
|
|
R_FLOAT (pr) = (float) strlen (st);
|
|
}
|
|
|
|
static char
|
|
KK_qwchar (char c)
|
|
{
|
|
c &= 0x7f;
|
|
if (c >= ' ')
|
|
return tolower ((byte) c);
|
|
if (c >= 0x12 && c <= 0x1b)
|
|
return c - 0x12 + '0';
|
|
switch (c) {
|
|
case 0x05:
|
|
case 0x0e:
|
|
case 0x0f:
|
|
case 0x1c:
|
|
return '.';
|
|
case 0x10:
|
|
return '[';
|
|
case 0x11:
|
|
return ']';
|
|
default:
|
|
return 'X';
|
|
}
|
|
}
|
|
|
|
static edict_t *
|
|
KK_Match_Str2 (const char *substr)
|
|
{
|
|
int i, j, count;
|
|
dstring_t *lstr = dstring_new ();
|
|
dstring_t *lname = dstring_new ();
|
|
client_t *cl;
|
|
edict_t *retedict = 0;
|
|
|
|
dstring_copystr (lstr, substr);
|
|
for (j = 0; lstr->str[j]; j++)
|
|
lstr->str[j] = KK_qwchar (lstr->str[j]);
|
|
|
|
for (i = count = 0, cl = svs.clients; i < MAX_CLIENTS; i++, cl++) {
|
|
if (!cl->state)
|
|
continue;
|
|
dstring_copystr (lname, cl->name);
|
|
for (j = 0; lname->str[j]; j++)
|
|
lname->str[j] = KK_qwchar (lname->str[j]);
|
|
if (strstr (lname->str, lstr->str)) {
|
|
retedict = cl->edict;
|
|
count++;
|
|
}
|
|
}
|
|
if (count > 1)
|
|
retedict = 0;
|
|
dstring_delete (lstr);
|
|
dstring_delete (lname);
|
|
return retedict;
|
|
}
|
|
|
|
/*
|
|
PF_getclient
|
|
|
|
entity(string st) getclient
|
|
*/
|
|
static void
|
|
PF_getclient (progs_t *pr, void *data)
|
|
{
|
|
edict_t *ent;
|
|
const char *st;
|
|
int i, uid;
|
|
client_t *cl;
|
|
|
|
st = P_GSTRING (pr, 0);
|
|
|
|
ent = (edict_t *) KK_Match_Str2 (st);
|
|
|
|
// no substring match?
|
|
if (!ent) {
|
|
uid = atoi (st); // lets assume its an user id
|
|
|
|
if (uid != 0) // was it even a number?
|
|
{
|
|
// then, lets see if a client with that userid is here
|
|
for (i = 0, cl = svs.clients; i < MAX_CLIENTS && !ent; i++, cl++) {
|
|
if (cl->userid == uid) // yeah, found it
|
|
ent = cl->edict;
|
|
}
|
|
}
|
|
}
|
|
// Failed retrieving an user by name and by userid, so return world
|
|
if (!ent)
|
|
ent = sv.edicts;
|
|
|
|
RETURN_EDICT (pr, ent);
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PF_mutedtime
|
|
|
|
float(entity client) mutedtime
|
|
==============
|
|
*/
|
|
|
|
static void
|
|
PF_mutedtime (progs_t *pr, void *data)
|
|
{
|
|
edict_t *client_ent;
|
|
int e_num;
|
|
|
|
float retval = 0;
|
|
|
|
client_ent = P_EDICT (pr, 0);
|
|
e_num = NUM_FOR_EDICT (pr, client_ent);
|
|
|
|
if (e_num <= MAX_CLIENTS && e_num > 0) {
|
|
if (realtime >= svs.clients[e_num - 1].lockedtill)
|
|
retval = (float) 0;
|
|
else
|
|
retval = (float) svs.clients[e_num - 1].lockedtill - realtime;
|
|
}
|
|
|
|
R_FLOAT (pr) = retval;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
PF_validatefile
|
|
|
|
float(string st) validatefile
|
|
==============
|
|
*/
|
|
|
|
static void
|
|
PF_validatefile (progs_t *pr, void *data)
|
|
{
|
|
float retval;
|
|
QFile *f;
|
|
const char *st;
|
|
|
|
st = P_GSTRING (pr, 0);
|
|
|
|
f = QFS_FOpenFile (st);
|
|
if (!f) {
|
|
retval = 0.0;
|
|
} else {
|
|
retval = 1.0;
|
|
Qclose (f);
|
|
}
|
|
|
|
R_FLOAT (pr) = retval;
|
|
}
|
|
|
|
/*
|
|
PF_putsaytime
|
|
|
|
void(entity client) putsaytime
|
|
*/
|
|
|
|
extern int fp_messages, fp_persecond, fp_secondsdead;
|
|
|
|
static void
|
|
PF_putsaytime (progs_t *pr, void *data)
|
|
{
|
|
edict_t *client_ent;
|
|
int e_num, tmp;
|
|
|
|
client_ent = P_EDICT (pr, 0);
|
|
e_num = NUM_FOR_EDICT (pr, client_ent);
|
|
|
|
if (e_num <= MAX_CLIENTS && e_num >= 1) {
|
|
tmp = svs.clients[e_num - 1].whensaidhead - fp_messages + 1;
|
|
if (tmp < 0)
|
|
tmp = 10 + tmp;
|
|
if (svs.clients[e_num - 1].whensaid[tmp]
|
|
&& (realtime - svs.clients[e_num - 1].whensaid[tmp] <
|
|
fp_persecond)) {
|
|
svs.clients[e_num - 1].lockedtill = realtime + fp_secondsdead;
|
|
return;
|
|
}
|
|
|
|
svs.clients[e_num - 1].whensaidhead++;
|
|
if (svs.clients[e_num - 1].whensaidhead > 9)
|
|
svs.clients[e_num - 1].whensaidhead = 0;
|
|
svs.clients[e_num - 1].whensaid[svs.clients[e_num - 1].whensaidhead] =
|
|
realtime;
|
|
}
|
|
}
|
|
|
|
/*
|
|
PF_makestr
|
|
|
|
string(string st) makestr
|
|
*/
|
|
|
|
static void
|
|
PF_makestr (progs_t *pr, void *data)
|
|
{
|
|
pr_string_t res = PR_NewMutableString (pr);
|
|
dstring_t *dst = PR_GetMutableString (pr, res);
|
|
const char *src = P_GSTRING (pr, 0);
|
|
|
|
dstring_copystr (dst, src);
|
|
R_STRING (pr) = res;
|
|
}
|
|
|
|
/*
|
|
PF_delstr
|
|
|
|
void(string st) delstr
|
|
*/
|
|
|
|
static void
|
|
PF_delstr (progs_t *pr, void *data)
|
|
{
|
|
PR_FreeString (pr, P_STRING (pr, 0));
|
|
}
|
|
|
|
/*
|
|
PF_getwave
|
|
|
|
float(float inputnum, float modes, float minnum, float maxnum,
|
|
float balance, float offset, float shape) getwave
|
|
*/
|
|
|
|
static float
|
|
GetCircleWave (float inputnum)
|
|
{
|
|
inputnum = inputnum - (int) inputnum;
|
|
|
|
if (inputnum >= 0.75) // -1 to 0
|
|
{
|
|
if (inputnum == 0.75)
|
|
return -1;
|
|
|
|
inputnum = inputnum - 0.75;
|
|
|
|
inputnum = inputnum * 4;
|
|
|
|
return (1 - sqrt (1 - (inputnum * inputnum))) - 1;
|
|
} else if (inputnum >= 0.5) // 0 to -1
|
|
{
|
|
if (inputnum == 0.5)
|
|
return 0;
|
|
|
|
inputnum = inputnum - 0.5;
|
|
|
|
inputnum = 0.25 - inputnum;
|
|
inputnum = inputnum * 4;
|
|
|
|
return (1 - sqrt (1 - (inputnum * inputnum))) - 1;
|
|
} else if (inputnum >= 0.25) // 1 to 0
|
|
{
|
|
if (inputnum == 0.25)
|
|
return 1;
|
|
|
|
inputnum = inputnum - 0.25;
|
|
|
|
inputnum = inputnum * 4;
|
|
|
|
return sqrt (1 - (inputnum * inputnum));
|
|
} else // 0 to 1
|
|
{
|
|
if (inputnum == 0)
|
|
return 0;
|
|
|
|
inputnum = 0.25 - inputnum;
|
|
inputnum = inputnum * 4;
|
|
|
|
return sqrt (1 - (inputnum * inputnum));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static float
|
|
GetLinearWave (float inputnum)
|
|
{
|
|
inputnum = inputnum - (int) inputnum;
|
|
|
|
if (inputnum <= 0.25)
|
|
return inputnum / 0.25;
|
|
else if (inputnum <= 0.5)
|
|
return 1 - ((inputnum - 0.25) / 0.25);
|
|
else if (inputnum <= 0.75)
|
|
return -(inputnum - 0.5) / 0.25;
|
|
else
|
|
return -1 + (inputnum - 0.75) / 0.25;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// GetWave possible mode flags
|
|
#define GWAVE_STANDARD 0
|
|
#define GWAVE_USEMINMAX 1
|
|
#define GWAVE_USEBALANCE 2 // TODO: Unimplemented yet
|
|
#define GWAVE_USEOFFSET 4
|
|
#define GWAVE_USESHAPE 8
|
|
|
|
static void
|
|
PF_getwave (progs_t *pr, void *data)
|
|
{
|
|
float retval, inputnum, minnum, maxnum, balance, offset, shape;
|
|
float temp;
|
|
|
|
unsigned int modes;
|
|
|
|
inputnum = P_FLOAT (pr, 0);
|
|
|
|
modes = (unsigned int) P_FLOAT (pr, 1);
|
|
|
|
balance = P_FLOAT (pr, 4);
|
|
(void) balance; //FIXME
|
|
|
|
if (modes & GWAVE_USEOFFSET)
|
|
offset = P_FLOAT (pr, 5);
|
|
else
|
|
offset = 0;
|
|
|
|
// Use special shape?
|
|
if (modes & GWAVE_USESHAPE) {
|
|
shape = P_FLOAT (pr, 6);
|
|
|
|
if (shape >= -1 && shape <= 1 && shape != 0) {
|
|
if (shape < 0) // sine/linear mix
|
|
{
|
|
if (shape == -1) // full linear
|
|
retval = GetLinearWave (inputnum + offset);
|
|
else {
|
|
// Get standard sinus
|
|
retval = sin (2 * M_PI * (inputnum + offset));
|
|
|
|
temp = GetLinearWave (inputnum + offset);
|
|
retval =
|
|
(retval * (1 - fabs (shape))) + (temp * fabs (shape));
|
|
}
|
|
} else // sine/circular mix
|
|
{
|
|
if (shape == 1) // full circular
|
|
retval = GetCircleWave (inputnum + offset);
|
|
else {
|
|
// Get standard sinus
|
|
retval = sin (2 * M_PI * (inputnum + offset));
|
|
|
|
temp = GetCircleWave (inputnum + offset);
|
|
retval = retval * (1 - shape) + temp * shape;
|
|
}
|
|
}
|
|
} else // 0 or invalid shape
|
|
{
|
|
// Get standard sinus
|
|
retval = sin (2 * M_PI * (inputnum + offset));
|
|
}
|
|
} else // dont use shape then..
|
|
{
|
|
// Get standard sinus
|
|
retval = sin (2 * M_PI * (inputnum + offset));
|
|
}
|
|
|
|
// Use maximum/minimum values?
|
|
if (modes & GWAVE_USEMINMAX) {
|
|
minnum = P_FLOAT (pr, 2);
|
|
maxnum = P_FLOAT (pr, 3);
|
|
|
|
retval = minnum + ((retval + 1) / 2) * (maxnum - minnum);
|
|
}
|
|
// Return it!
|
|
R_FLOAT (pr) = retval;
|
|
}
|
|
|
|
/*
|
|
PF_clientsound
|
|
|
|
void(entity client) clientsound
|
|
*/
|
|
|
|
static void
|
|
PF_clientsound (progs_t *pr, void *data)
|
|
{
|
|
const char *sample;
|
|
int channel;
|
|
edict_t *entity;
|
|
int volume;
|
|
float attenuation;
|
|
|
|
int sound_num;
|
|
int i;
|
|
pr_int_t ent;
|
|
vec3_t origin;
|
|
|
|
entity = P_EDICT (pr, 0);
|
|
channel = P_FLOAT (pr, 1);
|
|
sample = P_GSTRING (pr, 2);
|
|
volume = P_FLOAT (pr, 3) * 255;
|
|
attenuation = P_FLOAT (pr, 4);
|
|
|
|
ent = NUM_FOR_EDICT (pr, entity);
|
|
|
|
// If not a client go away
|
|
if (ent > MAX_CLIENTS || ent < 1)
|
|
return;
|
|
|
|
if (volume < 0 || volume > 255)
|
|
Sys_Error ("SV_StartSound: volume = %i", volume);
|
|
|
|
if (attenuation < 0 || attenuation > 4)
|
|
Sys_Error ("SV_StartSound: attenuation = %f", attenuation);
|
|
|
|
if (channel < 0 || channel > 15)
|
|
Sys_Error ("SV_StartSound: channel = %i", channel);
|
|
|
|
// find precache number for sound
|
|
for (sound_num = 1; sound_num < MAX_SOUNDS
|
|
&& sv.sound_precache[sound_num]; sound_num++)
|
|
if (!strcmp (sample, sv.sound_precache[sound_num]))
|
|
break;
|
|
|
|
if (sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num]) {
|
|
Sys_Printf ("SV_StartSound: %s not precacheed\n", sample);
|
|
return;
|
|
}
|
|
|
|
channel &= 7;
|
|
|
|
channel = (ent << 3) | channel;
|
|
|
|
if (volume != DEFAULT_SOUND_PACKET_VOLUME)
|
|
channel |= SND_VOLUME;
|
|
if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
|
|
channel |= SND_ATTENUATION;
|
|
|
|
// use the entity origin
|
|
VectorCopy (SVvector (entity, origin), origin);
|
|
|
|
MSG_WriteByte (&svs.clients[ent - 1].netchan.message, svc_sound);
|
|
MSG_WriteShort (&svs.clients[ent - 1].netchan.message, channel);
|
|
if (channel & SND_VOLUME)
|
|
MSG_WriteByte (&svs.clients[ent - 1].netchan.message, volume);
|
|
if (channel & SND_ATTENUATION)
|
|
MSG_WriteByte (&svs.clients[ent - 1].netchan.message, attenuation * 64);
|
|
MSG_WriteByte (&svs.clients[ent - 1].netchan.message, sound_num);
|
|
for (i = 0; i < 3; i++)
|
|
MSG_WriteCoord (&svs.clients[ent - 1].netchan.message, origin[i]);
|
|
}
|
|
|
|
/*
|
|
PF_touchworld
|
|
|
|
void() touchworld
|
|
*/
|
|
|
|
static void
|
|
PF_touchworld (progs_t *pr, void *data)
|
|
{
|
|
edict_t *self;
|
|
pr_int_t oself;
|
|
|
|
oself = *sv_globals.self;
|
|
|
|
self = PROG_TO_EDICT (pr, *sv_globals.self);
|
|
SV_LinkEdict (self, true);
|
|
|
|
*sv_globals.self = oself;
|
|
}
|
|
|
|
/*
|
|
CPQW_traceline
|
|
|
|
Used for use tracing and shot targeting.
|
|
Traces are blocked by bbox and exact bsp entityes, and also slide box
|
|
entities if the tryents flag is set.
|
|
|
|
traceline (vector1, vector2, tryents)
|
|
// float (vector v1, vector v2, float tryents) traceline
|
|
*/
|
|
#define TL_ANY_SOLID 0
|
|
#define TL_BSP_ONLY 1
|
|
#define TL_TRIGGERS 3 // scan for triggers
|
|
#define TL_EVERYTHING 4 // scan for anything
|
|
|
|
static void
|
|
PF_traceline (progs_t *pr, void *data)
|
|
{
|
|
float *v1, *v2;
|
|
edict_t *ent;
|
|
int nomonsters;
|
|
trace_t trace;
|
|
static int tl_to_move[] = {
|
|
MOVE_NORMAL,
|
|
MOVE_NORMAL,
|
|
MOVE_NORMAL,
|
|
MOVE_TRIGGERS,
|
|
MOVE_EVERYTHING,
|
|
};
|
|
|
|
v1 = P_VECTOR (pr, 0);
|
|
v2 = P_VECTOR (pr, 1);
|
|
nomonsters = P_FLOAT (pr, 2);
|
|
ent = P_EDICT (pr, 3);
|
|
|
|
if (nomonsters < TL_ANY_SOLID || nomonsters > TL_EVERYTHING)
|
|
nomonsters = TL_ANY_SOLID;
|
|
nomonsters = tl_to_move[nomonsters];
|
|
|
|
if (sv_antilag == 2)
|
|
nomonsters |= MOVE_LAGGED;
|
|
|
|
trace = SV_Move (v1, vec3_origin, vec3_origin, v2, nomonsters, ent);
|
|
|
|
*sv_globals.trace_allsolid = trace.allsolid;
|
|
*sv_globals.trace_startsolid = trace.startsolid;
|
|
*sv_globals.trace_fraction = trace.fraction;
|
|
*sv_globals.trace_inwater = trace.inwater;
|
|
*sv_globals.trace_inopen = trace.inopen;
|
|
VectorCopy (trace.endpos, *sv_globals.trace_endpos);
|
|
VectorCopy (trace.plane.normal, *sv_globals.trace_plane_normal);
|
|
*sv_globals.trace_plane_dist = trace.plane.dist;
|
|
|
|
if (trace.ent)
|
|
*sv_globals.trace_ent = EDICT_TO_PROG (pr, trace.ent);
|
|
else
|
|
*sv_globals.trace_ent = EDICT_TO_PROG (pr, sv.edicts);
|
|
}
|
|
|
|
#define CPQW (PR_RANGE_CPQW << PR_RANGE_SHIFT) |
|
|
|
|
#define bi(x,n,np,params...) {"CPCW:"#x, PF_##x, n, np, {params}}
|
|
#define p(type) PR_PARAM(type)
|
|
#define P(a, s) { .size = (s), .alignment = BITOP_LOG2 (a), }
|
|
static builtin_t builtins[] = {
|
|
bi(traceline, CPQW 16, 3, p(vector), p(vector), p(float)),
|
|
bi(getuid, CPQW 83, 1, p(entity)),
|
|
bi(strcat, CPQW 84, 2, p(string), p(string)),
|
|
bi(padstr, CPQW 85, 2, p(string), p(float)),
|
|
bi(colstr, CPQW 86, 2, p(string), p(float)),
|
|
bi(strcasecmp, CPQW 87, 2, p(string), p(string)),
|
|
bi(strlen, CPQW 88, 1, p(string)),
|
|
bi(getclient, CPQW 89, 1, p(string)),
|
|
bi(mutedtime, CPQW 90, 1, p(entity)),
|
|
bi(validatefile, CPQW 91, 1, p(string)),
|
|
bi(putsaytime, CPQW 92, 1, p(entity)),
|
|
bi(makestr, CPQW 93, 1, p(string)),
|
|
bi(delstr, CPQW 94, 1, p(string)),
|
|
bi(getwave, CPQW 95, 7, p(float), p(float), p(float), p(float),
|
|
p(float), p(float), p(float)),
|
|
bi(clientsound, CPQW 96, 1, p(entity)),
|
|
bi(touchworld, CPQW 97, 0),
|
|
{0}
|
|
};
|
|
|
|
static struct {
|
|
const char *name;
|
|
pr_func_t *field;
|
|
} cpqw_func_list[] = {
|
|
{"ClientCommand", &cpqw_funcs.ClientCommand},
|
|
{"UserInfoChanged", &sv_funcs.UserInfoChanged},
|
|
};
|
|
|
|
static int
|
|
cpqw_user_cmd (void)
|
|
{
|
|
int argc, i;
|
|
progs_t *pr = &sv_pr_state;
|
|
|
|
if (cpqw_funcs.ClientCommand) {
|
|
argc = Cmd_Argc ();
|
|
if (argc > 7)
|
|
argc = 7;
|
|
|
|
*sv_globals.time = sv.time;
|
|
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
|
|
|
|
PR_PushFrame (pr);
|
|
PR_RESET_PARAMS (pr);
|
|
P_FLOAT (pr, 0) = argc;
|
|
for (i = 1; i < argc + 1; i++)
|
|
P_STRING (pr, i) = PR_SetTempString (pr, Cmd_Argv (i - 1));
|
|
for (; i < 8; i++)
|
|
P_STRING (pr, i) = 0;
|
|
pr->pr_argc = 8;
|
|
PR_ExecuteProgram (pr, cpqw_funcs.ClientCommand);
|
|
PR_PopFrame (pr);
|
|
return (int) R_FLOAT (pr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
cpqw_load (progs_t *pr)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0;
|
|
i < sizeof (cpqw_func_list) / sizeof (cpqw_func_list[0]); i++) {
|
|
dfunction_t *f = PR_FindFunction (pr, cpqw_func_list[i].name);
|
|
|
|
*cpqw_func_list[i].field = 0;
|
|
if (f)
|
|
*cpqw_func_list[i].field = (pr_func_t) (f - pr->pr_functions);
|
|
}
|
|
ucmd_unknown = cpqw_user_cmd;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
SV_PR_CPQW_Init (progs_t *pr)
|
|
{
|
|
PR_RegisterBuiltins (pr, builtins, 0);
|
|
PR_AddLoadFunc (pr, cpqw_load);
|
|
}
|