#include "libcurl.h"
#include <time.h>
+#include "cl_collision.h"
+#include "clvm_cmds.h"
+#include "ft2.h"
+
extern cvar_t prvm_backtraceforwarnings;
// LordHavoc: changed this to NOT use a return statement, so that it can be used in functions that must return a value
dpvsnprintf(msg,sizeof(msg),fmt,argptr);
va_end(argptr);
- Con_Print(msg);
+ Con_DPrint(msg);
// TODO: either add a cvar/cmd to control the state dumping or replace some of the calls with Con_Printf [9/13/2006 Black]
if(prvm_backtraceforwarnings.integer && recursive != realtime) // NOTE: this compares to the time, just in case if PRVM_PrintState causes a Host_Error and keeps recursive set
void VM_CheckEmptyString (const char *s)
{
- if (s[0] <= ' ')
+ if (ISWHITESPACE(s[0]))
PRVM_ERROR ("%s: Bad string", PRVM_NAME);
}
+void VM_GenerateFrameGroupBlend(framegroupblend_t *framegroupblend, const prvm_edict_t *ed)
+{
+ prvm_eval_t *val;
+ // self.frame is the interpolation target (new frame)
+ // self.frame1time is the animation base time for the interpolation target
+ // self.frame2 is the interpolation start (previous frame)
+ // self.frame2time is the animation base time for the interpolation start
+ // self.lerpfrac is the interpolation strength for self.frame2
+ // self.lerpfrac3 is the interpolation strength for self.frame3
+ // self.lerpfrac4 is the interpolation strength for self.frame4
+ // pitch angle on a player model where the animator set up 5 sets of
+ // animations and the csqc simply lerps between sets)
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame))) framegroupblend[0].frame = (int) val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2))) framegroupblend[1].frame = (int) val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame3))) framegroupblend[2].frame = (int) val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame4))) framegroupblend[3].frame = (int) val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame1time))) framegroupblend[0].start = val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2time))) framegroupblend[1].start = val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame3time))) framegroupblend[2].start = val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame4time))) framegroupblend[3].start = val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac))) framegroupblend[1].lerp = val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac3))) framegroupblend[2].lerp = val->_float;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac4))) framegroupblend[3].lerp = val->_float;
+ // assume that the (missing) lerpfrac1 is whatever remains after lerpfrac2+lerpfrac3+lerpfrac4 are summed
+ framegroupblend[0].lerp = 1 - framegroupblend[1].lerp - framegroupblend[2].lerp - framegroupblend[3].lerp;
+}
+
+// LordHavoc: quite tempting to break apart this function to reuse the
+// duplicated code, but I suspect it is better for performance
+// this way
+void VM_FrameBlendFromFrameGroupBlend(frameblend_t *frameblend, const framegroupblend_t *framegroupblend, const dp_model_t *model)
+{
+ int sub2, numframes, f, i, k;
+ int isfirstframegroup = true;
+ int nolerp;
+ double sublerp, lerp, d;
+ const animscene_t *scene;
+ const framegroupblend_t *g;
+ frameblend_t *blend = frameblend;
+
+ memset(blend, 0, MAX_FRAMEBLENDS * sizeof(*blend));
+
+ if (!model || !model->surfmesh.isanimated)
+ {
+ blend[0].lerp = 1;
+ return;
+ }
+
+ nolerp = (model->type == mod_sprite) ? !r_lerpsprites.integer : !r_lerpmodels.integer;
+ numframes = model->numframes;
+ for (k = 0, g = framegroupblend;k < MAX_FRAMEGROUPBLENDS;k++, g++)
+ {
+ f = g->frame;
+ if ((unsigned int)f >= (unsigned int)numframes)
+ {
+ Con_DPrintf("VM_FrameBlendFromFrameGroupBlend: no such frame %d in model %s\n", f, model->name);
+ f = 0;
+ }
+ d = lerp = g->lerp;
+ if (lerp <= 0)
+ continue;
+ if (nolerp)
+ {
+ if (isfirstframegroup)
+ {
+ d = lerp = 1;
+ isfirstframegroup = false;
+ }
+ else
+ continue;
+ }
+ if (model->animscenes)
+ {
+ scene = model->animscenes + f;
+ f = scene->firstframe;
+ if (scene->framecount > 1)
+ {
+ // this code path is only used on .zym models and torches
+ sublerp = scene->framerate * (cl.time - g->start);
+ f = (int) floor(sublerp);
+ sublerp -= f;
+ sub2 = f + 1;
+ if (sublerp < (1.0 / 65536.0f))
+ sublerp = 0;
+ if (sublerp > (65535.0f / 65536.0f))
+ sublerp = 1;
+ if (nolerp)
+ sublerp = 0;
+ if (scene->loop)
+ {
+ f = (f % scene->framecount);
+ sub2 = (sub2 % scene->framecount);
+ }
+ f = bound(0, f, (scene->framecount - 1)) + scene->firstframe;
+ sub2 = bound(0, sub2, (scene->framecount - 1)) + scene->firstframe;
+ d = sublerp * lerp;
+ // two framelerps produced from one animation
+ if (d > 0)
+ {
+ for (i = 0;i < MAX_FRAMEBLENDS;i++)
+ {
+ if (blend[i].lerp <= 0 || blend[i].subframe == sub2)
+ {
+ blend[i].subframe = sub2;
+ blend[i].lerp += d;
+ break;
+ }
+ }
+ }
+ d = (1 - sublerp) * lerp;
+ }
+ }
+ if (d > 0)
+ {
+ for (i = 0;i < MAX_FRAMEBLENDS;i++)
+ {
+ if (blend[i].lerp <= 0 || blend[i].subframe == f)
+ {
+ blend[i].subframe = f;
+ blend[i].lerp += d;
+ break;
+ }
+ }
+ }
+ }
+}
+
+void VM_UpdateEdictSkeleton(prvm_edict_t *ed, const dp_model_t *edmodel, const frameblend_t *frameblend)
+{
+ if (ed->priv.server->skeleton.model != edmodel)
+ {
+ VM_RemoveEdictSkeleton(ed);
+ ed->priv.server->skeleton.model = edmodel;
+ }
+ if (!ed->priv.server->skeleton.relativetransforms && ed->priv.server->skeleton.model && ed->priv.server->skeleton.model->num_bones)
+ ed->priv.server->skeleton.relativetransforms = Mem_Alloc(prog->progs_mempool, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
+ if (ed->priv.server->skeleton.relativetransforms)
+ {
+ int skeletonindex = -1;
+ skeleton_t *skeleton;
+ prvm_eval_t *val;
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.skeletonindex))) skeletonindex = (int)val->_float - 1;
+ if (skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones)
+ {
+ // custom skeleton controlled by the game (FTE_CSQC_SKELETONOBJECTS)
+ memcpy(ed->priv.server->skeleton.relativetransforms, skeleton->relativetransforms, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t));
+ }
+ else
+ {
+ // generated skeleton from frame animation
+ int blendindex;
+ int bonenum;
+ int numbones = ed->priv.server->skeleton.model->num_bones;
+ const short *framebones6s;
+ float lerp;
+ float scale = ed->priv.server->skeleton.model->num_posescale;
+ matrix4x4_t *relativetransforms = ed->priv.server->skeleton.relativetransforms;
+ matrix4x4_t matrix;
+ memset(relativetransforms, 0, numbones * sizeof(matrix4x4_t));
+ for (blendindex = 0;blendindex < MAX_FRAMEBLENDS && frameblend[blendindex].lerp > 0;blendindex++)
+ {
+ lerp = frameblend[blendindex].lerp;
+ framebones6s = ed->priv.server->skeleton.model->data_poses6s + 6 * frameblend[blendindex].subframe * numbones;
+ for (bonenum = 0;bonenum < numbones;bonenum++)
+ {
+ Matrix4x4_FromBonePose6s(&matrix, scale, framebones6s + 6 * bonenum);
+ Matrix4x4_Accumulate(&ed->priv.server->skeleton.relativetransforms[bonenum], &matrix, lerp);
+ }
+ }
+ }
+ }
+}
+
+void VM_RemoveEdictSkeleton(prvm_edict_t *ed)
+{
+ if (ed->priv.server->skeleton.relativetransforms)
+ Mem_Free(ed->priv.server->skeleton.relativetransforms);
+ memset(&ed->priv.server->skeleton, 0, sizeof(ed->priv.server->skeleton));
+}
+
+
+
+
//============================================================================
//BUILT-IN FUNCTIONS
Cbuf_AddText(string);
}
+static qboolean PRVM_Cvar_ReadOk(const char *string)
+{
+ cvar_t *cvar;
+ cvar = Cvar_FindVar(string);
+ return ((cvar) && ((cvar->flags & CVAR_PRIVATE) == 0));
+}
+
/*
=================
VM_cvar
VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar);
VM_VarString(0, string, sizeof(string));
VM_CheckEmptyString(string);
- PRVM_G_FLOAT(OFS_RETURN) = Cvar_VariableValue(string);
+ PRVM_G_FLOAT(OFS_RETURN) = PRVM_Cvar_ReadOk(string) ? Cvar_VariableValue(string) : 0;
}
/*
float CVAR_TYPEFLAG_PRIVATE = 4;
float CVAR_TYPEFLAG_ENGINE = 8;
float CVAR_TYPEFLAG_HASDESCRIPTION = 16;
+float CVAR_TYPEFLAG_READONLY = 32;
=================
*/
void VM_cvar_type (void)
ret |= 4; // CVAR_TYPE_PRIVATE
if(!(cvar->flags & CVAR_ALLOCATED))
ret |= 8; // CVAR_TYPE_ENGINE
- if(strcmp(cvar->description, "custom cvar")) // has to match Cvar_Get's placeholder string
+ if(cvar->description != cvar_dummy_description)
ret |= 16; // CVAR_TYPE_HASDESCRIPTION
+ if(cvar->flags & CVAR_READONLY)
+ ret |= 32; // CVAR_TYPE_READONLY
PRVM_G_FLOAT(OFS_RETURN) = ret;
}
VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_string);
VM_VarString(0, string, sizeof(string));
VM_CheckEmptyString(string);
- PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableString(string));
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(PRVM_Cvar_ReadOk(string) ? Cvar_VariableString(string) : "");
}
VM_CheckEmptyString(string);
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableDefString(string));
}
+
+/*
+========================
+VM_cvar_defstring
+
+const string VM_cvar_description (string, ...)
+========================
+*/
+void VM_cvar_description (void)
+{
+ char string[VM_STRINGTEMP_LENGTH];
+ VM_SAFEPARMCOUNTRANGE(1,8,VM_cvar_description);
+ VM_VarString(0, string, sizeof(string));
+ VM_CheckEmptyString(string);
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Cvar_VariableDescription(string));
+}
/*
=================
VM_cvar_set
{
char string[VM_STRINGTEMP_LENGTH];
VM_SAFEPARMCOUNTRANGE(1, 8, VM_dprint);
- if (developer.integer)
- {
- VM_VarString(0, string, sizeof(string));
+ VM_VarString(0, string, sizeof(string));
#if 1
- Con_Printf("%s", string);
+ Con_DPrintf("%s", string);
#else
- Con_Printf("%s: %s", PRVM_NAME, string);
+ Con_DPrintf("%s: %s", PRVM_NAME, string);
#endif
- }
}
/*
ed = PRVM_G_EDICT(OFS_PARM0);
if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
{
- if (developer.integer >= 1)
+ if (developer.integer)
VM_Warning( "VM_remove: tried to remove the null entity or a reserved entity!\n" );
}
else if( ed->priv.required->free )
{
- if (developer.integer >= 1)
+ if (developer.integer)
VM_Warning( "VM_remove: tried to remove an already freed entity!\n" );
}
else
int f;
const char *s, *t;
prvm_edict_t *ent, *chain;
+ int chainfield;
- VM_SAFEPARMCOUNT(2,VM_findchain);
+ VM_SAFEPARMCOUNTRANGE(2,3,VM_findchain);
- if (prog->fieldoffsets.chain < 0)
- PRVM_ERROR("VM_findchain: %s doesnt have a chain field !", PRVM_NAME);
+ if(prog->argc == 3)
+ chainfield = PRVM_G_INT(OFS_PARM2);
+ else
+ chainfield = prog->fieldoffsets.chain;
+ if (chainfield < 0)
+ PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
chain = prog->edicts;
if (strcmp(t,s))
continue;
- PRVM_EDICTFIELDVALUE(ent,prog->fieldoffsets.chain)->edict = PRVM_NUM_FOR_EDICT(chain);
+ PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_NUM_FOR_EDICT(chain);
chain = ent;
}
int f;
float s;
prvm_edict_t *ent, *chain;
+ int chainfield;
- VM_SAFEPARMCOUNT(2, VM_findchainfloat);
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainfloat);
- if (prog->fieldoffsets.chain < 0)
- PRVM_ERROR("VM_findchainfloat: %s doesnt have a chain field !", PRVM_NAME);
+ if(prog->argc == 3)
+ chainfield = PRVM_G_INT(OFS_PARM2);
+ else
+ chainfield = prog->fieldoffsets.chain;
+ if (chainfield < 0)
+ PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
chain = (prvm_edict_t *)prog->edicts;
if (PRVM_E_FLOAT(ent,f) != s)
continue;
- PRVM_EDICTFIELDVALUE(ent,prog->fieldoffsets.chain)->edict = PRVM_EDICT_TO_PROG(chain);
+ PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_EDICT_TO_PROG(chain);
chain = ent;
}
int f;
int s;
prvm_edict_t *ent, *chain;
+ int chainfield;
- VM_SAFEPARMCOUNT(2, VM_findchainflags);
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_findchainflags);
- if (prog->fieldoffsets.chain < 0)
- PRVM_ERROR("VM_findchainflags: %s doesnt have a chain field !", PRVM_NAME);
+ if(prog->argc == 3)
+ chainfield = PRVM_G_INT(OFS_PARM2);
+ else
+ chainfield = prog->fieldoffsets.chain;
+ if (chainfield < 0)
+ PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME);
chain = (prvm_edict_t *)prog->edicts;
if (!((int)PRVM_E_FLOAT(ent,f) & s))
continue;
- PRVM_EDICTFIELDVALUE(ent,prog->fieldoffsets.chain)->edict = PRVM_EDICT_TO_PROG(chain);
+ PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_EDICT_TO_PROG(chain);
chain = ent;
}
s = PRVM_G_STRING(OFS_PARM0);
PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0);
- VM_CheckEmptyString(s);
+ //VM_CheckEmptyString(s);
- if(snd_initialized.integer && !S_PrecacheSound(s, true, false))
+ if(snd_initialized.integer && !S_PrecacheSound(s, true, true))
{
VM_Warning("VM_precache_sound: Failed to load %s for %s\n", s, PRVM_NAME);
return;
return;
}
- Cvar_Get(name, value, flags);
+ Cvar_Get(name, value, flags, NULL);
PRVM_G_FLOAT(OFS_RETURN) = 1; // success
}
PRVM_G_FLOAT(OFS_RETURN) = pow(PRVM_G_FLOAT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1));
}
+void VM_log (void)
+{
+ VM_SAFEPARMCOUNT(1,VM_log);
+ PRVM_G_FLOAT(OFS_RETURN) = log(PRVM_G_FLOAT(OFS_PARM0));
+}
+
void VM_Files_Init(void)
{
int i;
if (prog->openfiles[filenum] == NULL)
{
PRVM_G_FLOAT(OFS_RETURN) = -1;
- if (developer.integer >= 100)
+ if (developer_extra.integer)
VM_Warning("VM_fopen: %s: %s mode %s failed\n", PRVM_NAME, filename, modestring);
}
else
{
PRVM_G_FLOAT(OFS_RETURN) = filenum;
- if (developer.integer >= 100)
- Con_Printf("VM_fopen: %s: %s mode %s opened as #%i\n", PRVM_NAME, filename, modestring, filenum);
+ if (developer_extra.integer)
+ Con_DPrintf("VM_fopen: %s: %s mode %s opened as #%i\n", PRVM_NAME, filename, modestring, filenum);
prog->openfiles_origin[filenum] = PRVM_AllocationOrigin();
}
}
prog->openfiles[filenum] = NULL;
if(prog->openfiles_origin[filenum])
PRVM_Free((char *)prog->openfiles_origin[filenum]);
- if (developer.integer >= 100)
- Con_Printf("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum);
+ if (developer_extra.integer)
+ Con_DPrintf("VM_fclose: %s: #%i closed\n", PRVM_NAME, filenum);
}
/*
if (c != '\n')
FS_UnGetc(prog->openfiles[filenum], (unsigned char)c);
}
- if (developer.integer >= 100)
- Con_Printf("fgets: %s: %s\n", PRVM_NAME, string);
+ if (developer_extra.integer)
+ Con_DPrintf("fgets: %s: %s\n", PRVM_NAME, string);
if (c >= 0 || end)
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
}
VM_VarString(1, string, sizeof(string));
if ((stringlength = (int)strlen(string)))
FS_Write(prog->openfiles[filenum], string, stringlength);
- if (developer.integer >= 100)
- Con_Printf("fputs: %s: %s\n", PRVM_NAME, string);
+ if (developer_extra.integer)
+ Con_DPrintf("fputs: %s: %s\n", PRVM_NAME, string);
}
/*
}
// parse the string into the value
- PRVM_G_FLOAT(OFS_RETURN) = ( PRVM_ED_ParseEpair(ent, d, PRVM_G_STRING(OFS_PARM2)) ) ? 1.0f : 0.0f;
+ PRVM_G_FLOAT(OFS_RETURN) = ( PRVM_ED_ParseEpair(ent, d, PRVM_G_STRING(OFS_PARM2), false) ) ? 1.0f : 0.0f;
}
/*
{
VM_SAFEPARMCOUNT(1,VM_strlen);
- PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
+ //PRVM_G_FLOAT(OFS_RETURN) = strlen(PRVM_G_STRING(OFS_PARM0));
+ PRVM_G_FLOAT(OFS_RETURN) = u8_strlen(PRVM_G_STRING(OFS_PARM0));
}
// DRESK - Decolorized String
szString = PRVM_G_STRING(OFS_PARM0);
- nCnt = COM_StringLengthNoColors(szString, 0, NULL);
+ //nCnt = COM_StringLengthNoColors(szString, 0, NULL);
+ nCnt = u8_COM_StringLengthNoColors(szString, 0, NULL);
PRVM_G_FLOAT(OFS_RETURN) = nCnt;
}
// returns a section of a string as a tempstring
void VM_substring(void)
{
- int i, start, length;
+ int start, length;
+ int u_slength = 0, u_start;
+ size_t u_length;
const char *s;
char string[VM_STRINGTEMP_LENGTH];
VM_SAFEPARMCOUNT(3,VM_substring);
+ /*
s = PRVM_G_STRING(OFS_PARM0);
start = (int)PRVM_G_FLOAT(OFS_PARM1);
length = (int)PRVM_G_FLOAT(OFS_PARM2);
- for (i = 0;i < start && *s;i++, s++);
- for (i = 0;i < (int)sizeof(string) - 1 && *s && i < length;i++, s++)
- string[i] = *s;
- string[i] = 0;
+ slength = strlen(s);
+
+ if (start < 0) // FTE_STRINGS feature
+ start += slength;
+ start = bound(0, start, slength);
+
+ if (length < 0) // FTE_STRINGS feature
+ length += slength - start + 1;
+ maxlen = min((int)sizeof(string) - 1, slength - start);
+ length = bound(0, length, maxlen);
+
+ memcpy(string, s + start, length);
+ string[length] = 0;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
+ */
+
+ s = PRVM_G_STRING(OFS_PARM0);
+ start = (int)PRVM_G_FLOAT(OFS_PARM1);
+ length = (int)PRVM_G_FLOAT(OFS_PARM2);
+
+ if (start < 0) // FTE_STRINGS feature
+ {
+ u_slength = u8_strlen(s);
+ start += u_slength;
+ start = bound(0, start, u_slength);
+ }
+
+ if (length < 0) // FTE_STRINGS feature
+ {
+ if (!u_slength) // it's not calculated when it's not needed above
+ u_slength = u8_strlen(s);
+ length += u_slength - start + 1;
+ }
+
+ // positive start, positive length
+ u_start = u8_byteofs(s, start, NULL);
+ if (u_start < 0)
+ {
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
+ return;
+ }
+ u_length = u8_bytelen(s + u_start, length);
+ if (u_length >= sizeof(string)-1)
+ u_length = sizeof(string)-1;
+
+ memcpy(string, s + u_start, u_length);
+ string[u_length] = 0;
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string);
}
//float(string s) tokenize = #441; // takes apart a string into individal words (access them with argv), returns how many
//this function originally written by KrimZon, made shorter by LordHavoc
//20040203: rewritten by LordHavoc (no longer uses allocations)
-int num_tokens = 0;
-int tokens[256];
+static int num_tokens = 0;
+static int tokens[VM_STRINGTEMP_LENGTH / 2];
+static int tokens_startpos[VM_STRINGTEMP_LENGTH / 2];
+static int tokens_endpos[VM_STRINGTEMP_LENGTH / 2];
+static char tokenize_string[VM_STRINGTEMP_LENGTH];
void VM_tokenize (void)
{
const char *p;
- static char string[VM_STRINGTEMP_LENGTH]; // static, because it's big
VM_SAFEPARMCOUNT(1,VM_tokenize);
- strlcpy(string, PRVM_G_STRING(OFS_PARM0), sizeof(string));
- p = string;
+ strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
+ p = tokenize_string;
num_tokens = 0;
- while(COM_ParseToken_VM_Tokenize(&p, false))
+ for(;;)
{
if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
break;
- tokens[num_tokens++] = PRVM_SetTempString(com_token);
+
+ // skip whitespace here to find token start pos
+ while(*p && ISWHITESPACE(*p))
+ ++p;
+
+ tokens_startpos[num_tokens] = p - tokenize_string;
+ if(!COM_ParseToken_VM_Tokenize(&p, false))
+ break;
+ tokens_endpos[num_tokens] = p - tokenize_string;
+ tokens[num_tokens] = PRVM_SetTempString(com_token);
+ ++num_tokens;
+ }
+
+ PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
+}
+
+//float(string s) tokenize = #514; // takes apart a string into individal words (access them with argv), returns how many
+void VM_tokenize_console (void)
+{
+ const char *p;
+
+ VM_SAFEPARMCOUNT(1,VM_tokenize);
+
+ strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
+ p = tokenize_string;
+
+ num_tokens = 0;
+ for(;;)
+ {
+ if (num_tokens >= (int)(sizeof(tokens)/sizeof(tokens[0])))
+ break;
+
+ // skip whitespace here to find token start pos
+ while(*p && ISWHITESPACE(*p))
+ ++p;
+
+ tokens_startpos[num_tokens] = p - tokenize_string;
+ if(!COM_ParseToken_Console(&p))
+ break;
+ tokens_endpos[num_tokens] = p - tokenize_string;
+ tokens[num_tokens] = PRVM_SetTempString(com_token);
+ ++num_tokens;
}
PRVM_G_FLOAT(OFS_RETURN) = num_tokens;
int numseparators;
int separatorlen[7];
const char *separators[7];
- const char *p;
+ const char *p, *p0;
const char *token;
char tokentext[MAX_INPUTLINE];
- static char string[VM_STRINGTEMP_LENGTH]; // static, because it's big
VM_SAFEPARMCOUNTRANGE(2, 8,VM_tokenizebyseparator);
- strlcpy(string, PRVM_G_STRING(OFS_PARM0), sizeof(string));
- p = string;
+ strlcpy(tokenize_string, PRVM_G_STRING(OFS_PARM0), sizeof(tokenize_string));
+ p = tokenize_string;
numseparators = 0;
for (j = 1;j < prog->argc;j++)
while (num_tokens < (int)(sizeof(tokens)/sizeof(tokens[0])))
{
token = tokentext + j;
+ tokens_startpos[num_tokens] = p - tokenize_string;
+ p0 = p;
while (*p)
{
for (k = 0;k < numseparators;k++)
if (j < (int)sizeof(tokentext)-1)
tokentext[j++] = *p;
p++;
+ p0 = p;
}
+ tokens_endpos[num_tokens] = p0 - tokenize_string;
if (j >= (int)sizeof(tokentext))
break;
tokentext[j++] = 0;
token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
+ if(token_num < 0)
+ token_num += num_tokens;
+
if (token_num >= 0 && token_num < num_tokens)
PRVM_G_INT(OFS_RETURN) = tokens[token_num];
else
PRVM_G_INT(OFS_RETURN) = OFS_NULL;
}
+//float(float n) argv_start_index = #515; // returns the start index of a token
+void VM_argv_start_index (void)
+{
+ int token_num;
+
+ VM_SAFEPARMCOUNT(1,VM_argv);
+
+ token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
+
+ if(token_num < 0)
+ token_num += num_tokens;
+
+ if (token_num >= 0 && token_num < num_tokens)
+ PRVM_G_FLOAT(OFS_RETURN) = tokens_startpos[token_num];
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+}
+
+//float(float n) argv_end_index = #516; // returns the end index of a token
+void VM_argv_end_index (void)
+{
+ int token_num;
+
+ VM_SAFEPARMCOUNT(1,VM_argv);
+
+ token_num = (int)PRVM_G_FLOAT(OFS_PARM0);
+
+ if(token_num < 0)
+ token_num += num_tokens;
+
+ if (token_num >= 0 && token_num < num_tokens)
+ PRVM_G_FLOAT(OFS_RETURN) = tokens_endpos[token_num];
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+}
+
/*
=========
VM_isserver
float gettime(void)
=========
*/
+extern double host_starttime;
+float CDAudio_GetPosition(void);
void VM_gettime(void)
{
- VM_SAFEPARMCOUNT(0,VM_gettime);
+ int timer_index;
- PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
+ VM_SAFEPARMCOUNTRANGE(0,1,VM_gettime);
+
+ if(prog->argc == 0)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
+ }
+ else
+ {
+ timer_index = (int) PRVM_G_FLOAT(OFS_PARM0);
+ switch(timer_index)
+ {
+ case 0: // GETTIME_FRAMESTART
+ PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
+ break;
+ case 1: // GETTIME_REALTIME
+ PRVM_G_FLOAT(OFS_RETURN) = (float) Sys_DoubleTime();
+ break;
+ case 2: // GETTIME_HIRES
+ PRVM_G_FLOAT(OFS_RETURN) = (float) (Sys_DoubleTime() - realtime);
+ break;
+ case 3: // GETTIME_UPTIME
+ PRVM_G_FLOAT(OFS_RETURN) = (float) (Sys_DoubleTime() - host_starttime);
+ break;
+ case 4: // GETTIME_CDTRACK
+ PRVM_G_FLOAT(OFS_RETURN) = (float) CDAudio_GetPosition();
+ break;
+ default:
+ VM_Warning("VM_gettime: %s: unsupported timer specified, returning realtime\n", PRVM_NAME);
+ PRVM_G_FLOAT(OFS_RETURN) = (float) realtime;
+ break;
+ }
+ }
}
/*
*/
void VM_chr(void)
{
+ /*
char tmp[2];
VM_SAFEPARMCOUNT(1, VM_chr);
tmp[1] = 0;
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
+ */
+
+ char tmp[8];
+ int len;
+ VM_SAFEPARMCOUNT(1, VM_chr);
+
+ len = u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0), tmp, sizeof(tmp));
+ tmp[len] = 0;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(tmp);
}
//=============================================================================
Draw_FreePic(s);
}
-dp_font_t *getdrawfont()
+void getdrawfontscale(float *sx, float *sy)
+{
+ vec3_t v;
+ *sx = *sy = 1;
+ if(prog->globaloffsets.drawfontscale >= 0)
+ {
+ VectorCopy(PRVM_G_VECTOR(prog->globaloffsets.drawfontscale), v);
+ if(VectorLength2(v) > 0)
+ {
+ *sx = v[0];
+ *sy = v[1];
+ }
+ }
+}
+
+dp_font_t *getdrawfont(void)
{
if(prog->globaloffsets.drawfont >= 0)
{
- int f = PRVM_G_FLOAT(prog->globaloffsets.drawfont);
+ int f = (int) PRVM_G_FLOAT(prog->globaloffsets.drawfont);
if(f < 0 || f >= MAX_FONTS)
return FONT_DEFAULT;
return &dp_fonts[f];
float *pos,*scale,*rgb;
char character;
int flag;
+ float sx, sy;
VM_SAFEPARMCOUNT(6,VM_drawcharacter);
character = (char) PRVM_G_FLOAT(OFS_PARM1);
return;
}
- DrawQ_String_Font(pos[0], pos[1], &character, 1, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont());
+ getdrawfontscale(&sx, &sy);
+ DrawQ_String_Scale(pos[0], pos[1], &character, 1, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont());
PRVM_G_FLOAT(OFS_RETURN) = 1;
}
float *pos,*scale,*rgb;
const char *string;
int flag;
+ float sx, sy;
VM_SAFEPARMCOUNT(6,VM_drawstring);
string = PRVM_G_STRING(OFS_PARM1);
if(pos[2] || scale[2])
Con_Printf("VM_drawstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
- DrawQ_String_Font(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont());
+ getdrawfontscale(&sx, &sy);
+ DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true, getdrawfont());
+ //Font_DrawString(pos[0], pos[1], string, 0, scale[0], scale[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag, NULL, true);
PRVM_G_FLOAT(OFS_RETURN) = 1;
}
{
float *pos,*scale;
const char *string;
- int flag,color;
+ int flag;
+ float sx, sy;
VM_SAFEPARMCOUNT(5,VM_drawstring);
string = PRVM_G_STRING(OFS_PARM1);
if(pos[2] || scale[2])
Con_Printf("VM_drawcolorcodedstring: z value%s from %s discarded\n",(pos[2] && scale[2]) ? "s" : " ",((pos[2] && scale[2]) ? "pos and scale" : (pos[2] ? "pos" : "scale")));
- color = -1;
- DrawQ_String_Font(pos[0], pos[1], string, 0, scale[0], scale[1], 1, 1, 1, PRVM_G_FLOAT(OFS_PARM3), flag, NULL, false, getdrawfont());
+ getdrawfontscale(&sx, &sy);
+ DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, 1, 1, 1, PRVM_G_FLOAT(OFS_PARM3), flag, NULL, false, getdrawfont());
PRVM_G_FLOAT(OFS_RETURN) = 1;
}
/*
=========
VM_stringwidth
-float stringwidth(string text, float allowColorCodes)
+float stringwidth(string text, float allowColorCodes, float size)
=========
*/
void VM_stringwidth(void)
{
const char *string;
+ float *szv;
+ float mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell
int colors;
- VM_SAFEPARMCOUNT(2,VM_drawstring);
+ float sx, sy;
+ size_t maxlen = 0;
+ VM_SAFEPARMCOUNTRANGE(2,3,VM_drawstring);
+
+ getdrawfontscale(&sx, &sy);
+ if(prog->argc == 3)
+ {
+ szv = PRVM_G_VECTOR(OFS_PARM2);
+ mult = 1;
+ }
+ else
+ {
+ // we want the width for 8x8 font size, divided by 8
+ static float defsize[] = {8, 8};
+ szv = defsize;
+ mult = 0.125;
+ // to make sure snapping is turned off, ALWAYS use a nontrivial scale in this case
+ if(sx >= 0.9 && sx <= 1.1)
+ {
+ mult *= 2;
+ sx /= 2;
+ sy /= 2;
+ }
+ }
string = PRVM_G_STRING(OFS_PARM0);
colors = (int)PRVM_G_FLOAT(OFS_PARM1);
- PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_Font(string, 0, !colors, getdrawfont()); // 1x1 characters, don't actually draw
+ PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth_UntilWidth_TrackColors_Scale(string, &maxlen, szv[0], szv[1], sx, sy, NULL, !colors, getdrawfont(), 1000000000) * mult;
+/*
+ if(prog->argc == 3)
+ {
+ mult = sz = PRVM_G_FLOAT(OFS_PARM2);
+ }
+ else
+ {
+ sz = 8;
+ mult = 1;
+ }
+
+ string = PRVM_G_STRING(OFS_PARM0);
+ colors = (int)PRVM_G_FLOAT(OFS_PARM1);
+
+ PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw
+*/
+
}
/*
=========
}
/*
=========
+VM_drawrotpic
+
+float drawrotpic(vector position, string pic, vector size, vector org, float angle, vector rgb, float alpha, float flag)
+=========
+*/
+void VM_drawrotpic(void)
+{
+ const char *picname;
+ float *size, *pos, *org, *rgb;
+ int flag;
+
+ VM_SAFEPARMCOUNT(8,VM_drawrotpic);
+
+ picname = PRVM_G_STRING(OFS_PARM1);
+ VM_CheckEmptyString (picname);
+
+ // is pic cached ? no function yet for that
+ if(!1)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = -4;
+ VM_Warning("VM_drawrotpic: %s: %s not cached !\n", PRVM_NAME, picname);
+ return;
+ }
+
+ pos = PRVM_G_VECTOR(OFS_PARM0);
+ size = PRVM_G_VECTOR(OFS_PARM2);
+ org = PRVM_G_VECTOR(OFS_PARM3);
+ rgb = PRVM_G_VECTOR(OFS_PARM5);
+ flag = (int) PRVM_G_FLOAT(OFS_PARM7);
+
+ if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS)
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = -2;
+ VM_Warning("VM_drawrotpic: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag);
+ return;
+ }
+
+ if(pos[2] || size[2] || org[2])
+ Con_Printf("VM_drawrotpic: z value from pos/size/org discarded\n");
+
+ DrawQ_RotPic(pos[0], pos[1], Draw_CachePic(picname), size[0], size[1], org[0], org[1], PRVM_G_FLOAT(OFS_PARM4), rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM6), flag);
+ PRVM_G_FLOAT(OFS_RETURN) = 1;
+}
+/*
+=========
VM_drawsubpic
float drawsubpic(vector position, vector size, string pic, vector srcPos, vector srcSize, vector rgb, float alpha, float flag)
return;
}
- PRVM_G_FLOAT( OFS_RETURN ) = (CL_Gecko_Event_Key( instance, key, eventtype ) == true);
+ PRVM_G_FLOAT( OFS_RETURN ) = (CL_Gecko_Event_Key( instance, (keynum_t) key, eventtype ) == true);
}
/*
if( !instance ) {
return;
}
- CL_Gecko_Resize( instance, w, h );
+ CL_Gecko_Resize( instance, (int) w, (int) h );
}
int n1, n2;
VM_SAFEPARMCOUNT(2, VM_bitshift);
- n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0));
+ n1 = (int)fabs((float)((int)PRVM_G_FLOAT(OFS_PARM0)));
n2 = (int)PRVM_G_FLOAT(OFS_PARM1);
if(!n1)
PRVM_G_FLOAT(OFS_RETURN) = n1;
void VM_altstr_ins(void)
{
int num;
- const char *setstr;
const char *set;
- const char *instr;
const char *in;
char *out;
char outstr[VM_STRINGTEMP_LENGTH];
VM_SAFEPARMCOUNT(3, VM_altstr_ins);
- in = instr = PRVM_G_STRING( OFS_PARM0 );
+ in = PRVM_G_STRING( OFS_PARM0 );
num = (int)PRVM_G_FLOAT( OFS_PARM1 );
- set = setstr = PRVM_G_STRING( OFS_PARM2 );
+ set = PRVM_G_STRING( OFS_PARM2 );
out = outstr;
for( num = num * 2 + 2 ; *in && num > 0 ; *out++ = *in++ )
stringbuffer->max_strings = max(stringbuffer->max_strings * 2, 128);
while (stringbuffer->max_strings <= strindex)
stringbuffer->max_strings *= 2;
- stringbuffer->strings = Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
+ stringbuffer->strings = (char **) Mem_Alloc(prog->progs_mempool, stringbuffer->max_strings * sizeof(stringbuffer->strings[0]));
if (stringbuffer->num_strings > 0)
memcpy(stringbuffer->strings, oldstrings, stringbuffer->num_strings * sizeof(stringbuffer->strings[0]));
if (oldstrings)
VM_buf_create
creates new buffer, and returns it's index, returns -1 if failed
float buf_create(void) = #460;
+float newbuf(string format, float flags) = #460;
========================
*/
+
void VM_buf_create (void)
{
prvm_stringbuffer_t *stringbuffer;
int i;
- VM_SAFEPARMCOUNT(0, VM_buf_create);
- stringbuffer = Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
+
+ VM_SAFEPARMCOUNTRANGE(0, 2, VM_buf_create);
+
+ // VorteX: optional parm1 (buffer format) is unfinished, to keep intact with future databuffers extension must be set to "string"
+ if(prog->argc >= 1 && strcmp(PRVM_G_STRING(OFS_PARM0), "string"))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ return;
+ }
+ stringbuffer = (prvm_stringbuffer_t *) Mem_ExpandableArray_AllocRecord(&prog->stringbuffersarray);
for (i = 0;stringbuffer != Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);i++);
stringbuffer->origin = PRVM_AllocationOrigin();
+ // optional flags parm
+ if(prog->argc == 2)
+ stringbuffer->flags = (int)PRVM_G_FLOAT(OFS_PARM1) & 0xFF;
PRVM_G_FLOAT(OFS_RETURN) = i;
}
+
+
/*
========================
VM_buf_del
BufStr_Shrink(stringbuffer);
}
+
+
+
+
+
+
+void VM_buf_cvarlist(void)
+{
+ cvar_t *cvar;
+ const char *partial, *antipartial;
+ size_t len, antilen;
+ size_t alloclen;
+ qboolean ispattern, antiispattern;
+ int n;
+ prvm_stringbuffer_t *stringbuffer;
+ VM_SAFEPARMCOUNTRANGE(2, 3, VM_buf_cvarlist);
+
+ stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, (int)PRVM_G_FLOAT(OFS_PARM0));
+ if(!stringbuffer)
+ {
+ VM_Warning("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME);
+ return;
+ }
+
+ partial = PRVM_G_STRING(OFS_PARM1);
+ if(!partial)
+ len = 0;
+ else
+ len = strlen(partial);
+
+ if(prog->argc == 3)
+ antipartial = PRVM_G_STRING(OFS_PARM2);
+ else
+ antipartial = NULL;
+ if(!antipartial)
+ antilen = 0;
+ else
+ antilen = strlen(antipartial);
+
+ for (n = 0;n < stringbuffer->num_strings;n++)
+ if (stringbuffer->strings[n])
+ Mem_Free(stringbuffer->strings[n]);
+ if (stringbuffer->strings)
+ Mem_Free(stringbuffer->strings);
+ stringbuffer->strings = NULL;
+
+ ispattern = partial && (strchr(partial, '*') || strchr(partial, '?'));
+ antiispattern = antipartial && (strchr(antipartial, '*') || strchr(antipartial, '?'));
+
+ n = 0;
+ for(cvar = cvar_vars; cvar; cvar = cvar->next)
+ {
+ if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
+ continue;
+
+ if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
+ continue;
+
+ ++n;
+ }
+
+ stringbuffer->max_strings = stringbuffer->num_strings = n;
+ if (stringbuffer->max_strings)
+ stringbuffer->strings = (char **)Mem_Alloc(prog->progs_mempool, sizeof(stringbuffer->strings[0]) * stringbuffer->max_strings);
+
+ n = 0;
+ for(cvar = cvar_vars; cvar; cvar = cvar->next)
+ {
+ if(len && (ispattern ? !matchpattern_with_separator(cvar->name, partial, false, "", false) : strncmp(partial, cvar->name, len)))
+ continue;
+
+ if(antilen && (antiispattern ? matchpattern_with_separator(cvar->name, antipartial, false, "", false) : !strncmp(antipartial, cvar->name, antilen)))
+ continue;
+
+ alloclen = strlen(cvar->name) + 1;
+ stringbuffer->strings[n] = (char *)Mem_Alloc(prog->progs_mempool, alloclen);
+ memcpy(stringbuffer->strings[n], cvar->name, alloclen);
+
+ ++n;
+ }
+}
+
+
+
+
//=============
/*
VM_SAFEPARMCOUNTRANGE(2, 3, VM_strstrofs);
instr = PRVM_G_STRING(OFS_PARM0);
match = PRVM_G_STRING(OFS_PARM1);
- firstofs = (prog->argc > 2)?PRVM_G_FLOAT(OFS_PARM2):0;
+ firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0;
+ firstofs = u8_bytelen(instr, firstofs);
if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr)))
{
void VM_str2chr (void)
{
const char *s;
+ Uchar ch;
+ int index;
VM_SAFEPARMCOUNT(2, VM_str2chr);
s = PRVM_G_STRING(OFS_PARM0);
- if((unsigned)PRVM_G_FLOAT(OFS_PARM1) < strlen(s))
- PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(unsigned)PRVM_G_FLOAT(OFS_PARM1)];
+ index = u8_bytelen(s, (int)PRVM_G_FLOAT(OFS_PARM1));
+
+ if((unsigned)index < strlen(s))
+ {
+ if (utf8_enable.integer)
+ ch = u8_getchar_noendptr(s + index);
+ else
+ ch = (unsigned char)s[index];
+ PRVM_G_FLOAT(OFS_RETURN) = ch;
+ }
else
PRVM_G_FLOAT(OFS_RETURN) = 0;
}
//#223 string(float c, ...) chr2str (FTE_STRINGS)
void VM_chr2str (void)
{
+ /*
char t[9];
int i;
VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3);
t[i] = 0;
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
+ */
+ char t[9 * 4 + 1];
+ int i;
+ size_t len = 0;
+ VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str);
+ for(i = 0; i < prog->argc && len < sizeof(t)-1; ++i)
+ len += u8_fromchar((Uchar)PRVM_G_FLOAT(OFS_PARM0+i*3), t + len, sizeof(t)-1);
+ t[len] = 0;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(t);
}
static int chrconv_number(int i, int base, int conv)
VM_SAFEPARMCOUNTRANGE(3, 8, VM_strconv);
- ccase = PRVM_G_FLOAT(OFS_PARM0); //0 same, 1 lower, 2 upper
- redalpha = PRVM_G_FLOAT(OFS_PARM1); //0 same, 1 white, 2 red, 5 alternate, 6 alternate-alternate
- rednum = PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
+ ccase = (int) PRVM_G_FLOAT(OFS_PARM0); //0 same, 1 lower, 2 upper
+ redalpha = (int) PRVM_G_FLOAT(OFS_PARM1); //0 same, 1 white, 2 red, 5 alternate, 6 alternate-alternate
+ rednum = (int) PRVM_G_FLOAT(OFS_PARM2); //0 same, 1 white, 2 red, 3 redspecial, 4 whitespecial, 5 alternate, 6 alternate-alternate
VM_VarString(3, (char *) resbuf, sizeof(resbuf));
len = strlen((char *) resbuf);
char destbuf[VM_STRINGTEMP_LENGTH];
int pad;
VM_SAFEPARMCOUNTRANGE(1, 8, VM_strpad);
- pad = PRVM_G_FLOAT(OFS_PARM0);
+ pad = (int) PRVM_G_FLOAT(OFS_PARM0);
VM_VarString(1, src, sizeof(src));
// note: < 0 = left padding, > 0 = right padding,
val->string = trace->hittexture ? PRVM_SetTempString(trace->hittexture->name) : 0;
}
+void VM_ClearTraceGlobals(void)
+{
+ // clean up all trace globals when leaving the VM (anti-triggerbot safeguard)
+ prvm_eval_t *val;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_allsolid)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_startsolid)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_fraction)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inwater)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inopen)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos)))
+ VectorClear(val->vector);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_normal)))
+ VectorClear(val->vector);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_dist)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_ent)))
+ val->edict = PRVM_EDICT_TO_PROG(prog->edicts);
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags)))
+ val->_float = 0;
+ if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename)))
+ val->string = 0;
+}
+
//=============
void VM_Cmd_Init(void)
static void uri_to_string_callback(int status, size_t length_received, unsigned char *buffer, void *cbdata)
{
- uri_to_prog_t *handle = cbdata;
+ uri_to_prog_t *handle = (uri_to_prog_t *) cbdata;
if(!PRVM_ProgLoaded(handle->prognr))
{
url = PRVM_G_STRING(OFS_PARM0);
id = PRVM_G_FLOAT(OFS_PARM1);
- handle = Z_Malloc(sizeof(*handle)); // this can't be the prog's mem pool, as curl may call the callback later!
+ handle = (uri_to_prog_t *) Z_Malloc(sizeof(*handle)); // this can't be the prog's mem pool, as curl may call the callback later!
handle->prognr = PRVM_GetProgNr();
handle->starttime = prog->starttime;
ip = PRVM_G_STRING(OFS_PARM0);
port = 0;
if(prog->argc > 1)
- port = PRVM_G_FLOAT(OFS_PARM1);
+ port = (int) PRVM_G_FLOAT(OFS_PARM1);
if(LHNETADDRESS_FromString(&addr, ip, port) && LHNETADDRESS_ToString(&addr, normalized, sizeof(normalized), prog->argc > 1))
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(normalized);
else
PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString("");
}
+
+//string(void) getextresponse = #624; // returns the next extResponse packet that was sent to this client
+void VM_CL_getextresponse (void)
+{
+ VM_SAFEPARMCOUNT(0,VM_argv);
+
+ if (cl_net_extresponse_count <= 0)
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ else
+ {
+ int first;
+ --cl_net_extresponse_count;
+ first = (cl_net_extresponse_last + NET_EXTRESPONSE_MAX - cl_net_extresponse_count) % NET_EXTRESPONSE_MAX;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(cl_net_extresponse[first]);
+ }
+}
+
+void VM_SV_getextresponse (void)
+{
+ VM_SAFEPARMCOUNT(0,VM_argv);
+
+ if (sv_net_extresponse_count <= 0)
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ else
+ {
+ int first;
+ --sv_net_extresponse_count;
+ first = (sv_net_extresponse_last + NET_EXTRESPONSE_MAX - sv_net_extresponse_count) % NET_EXTRESPONSE_MAX;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(sv_net_extresponse[first]);
+ }
+}
+
+/*
+=========
+VM_M_callfunction
+
+ callfunction(...,string function_name)
+Extension: pass
+=========
+*/
+mfunction_t *PRVM_ED_FindFunction (const char *name);
+void VM_callfunction(void)
+{
+ mfunction_t *func;
+ const char *s;
+
+ VM_SAFEPARMCOUNTRANGE(1, 8, VM_callfunction);
+
+ s = PRVM_G_STRING(OFS_PARM0+(prog->argc - 1)*3);
+
+ VM_CheckEmptyString(s);
+
+ func = PRVM_ED_FindFunction(s);
+
+ if(!func)
+ PRVM_ERROR("VM_callfunciton: function %s not found !", s);
+ else if (func->first_statement < 0)
+ {
+ // negative statements are built in functions
+ int builtinnumber = -func->first_statement;
+ prog->xfunction->builtinsprofile++;
+ if (builtinnumber < prog->numbuiltins && prog->builtins[builtinnumber])
+ prog->builtins[builtinnumber]();
+ else
+ PRVM_ERROR("No such builtin #%i in %s; most likely cause: outdated engine build. Try updating!", builtinnumber, PRVM_NAME);
+ }
+ else if(func - prog->functions > 0)
+ {
+ prog->argc--;
+ PRVM_ExecuteProgram(func - prog->functions,"");
+ prog->argc++;
+ }
+}
+
+/*
+=========
+VM_isfunction
+
+float isfunction(string function_name)
+=========
+*/
+mfunction_t *PRVM_ED_FindFunction (const char *name);
+void VM_isfunction(void)
+{
+ mfunction_t *func;
+ const char *s;
+
+ VM_SAFEPARMCOUNT(1, VM_isfunction);
+
+ s = PRVM_G_STRING(OFS_PARM0);
+
+ VM_CheckEmptyString(s);
+
+ func = PRVM_ED_FindFunction(s);
+
+ if(!func)
+ PRVM_G_FLOAT(OFS_RETURN) = false;
+ else
+ PRVM_G_FLOAT(OFS_RETURN) = true;
+}
+
+/*
+=========
+VM_sprintf
+
+string sprintf(string format, ...)
+=========
+*/
+
+void VM_sprintf(void)
+{
+ const char *s, *s0;
+ char outbuf[MAX_INPUTLINE];
+ char *o = outbuf, *end = outbuf + sizeof(outbuf), *err;
+ int argpos = 1;
+ int width, precision, thisarg, flags;
+ char formatbuf[16];
+ char *f;
+ qboolean isfloat;
+#define PRINTF_ALTERNATE 1
+#define PRINTF_ZEROPAD 2
+#define PRINTF_LEFT 4
+#define PRINTF_SPACEPOSITIVE 8
+#define PRINTF_SIGNPOSITIVE 16
+
+ formatbuf[0] = '%';
+
+ s = PRVM_G_STRING(OFS_PARM0);
+
+#define GETARG_FLOAT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0)
+#define GETARG_INT(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0)
+#define GETARG_STRING(a) (((a)>=1 && (a)<prog->argc) ? (PRVM_G_STRING(OFS_PARM0 + 3 * (a))) : "")
+
+ for(;;)
+ {
+ s0 = s;
+ switch(*s)
+ {
+ case 0:
+ goto finished;
+ case '%':
+ ++s;
+
+ if(*s == '%')
+ goto verbatim;
+
+ // complete directive format:
+ // %3$*1$.*2$ld
+
+ width = -1;
+ precision = -1;
+ thisarg = -1;
+ flags = 0;
+
+ // is number following?
+ if(*s >= '0' && *s <= '9')
+ {
+ width = strtol(s, &err, 10);
+ if(!err)
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ if(*err == '$')
+ {
+ thisarg = width;
+ width = -1;
+ s = err + 1;
+ }
+ else
+ {
+ if(*s == '0')
+ {
+ flags |= PRINTF_ZEROPAD;
+ if(width == 0)
+ width = -1; // it was just a flag
+ }
+ s = err;
+ }
+ }
+
+ if(width < 0)
+ {
+ for(;;)
+ {
+ switch(*s)
+ {
+ case '#': flags |= PRINTF_ALTERNATE; break;
+ case '0': flags |= PRINTF_ZEROPAD; break;
+ case '-': flags |= PRINTF_LEFT; break;
+ case ' ': flags |= PRINTF_SPACEPOSITIVE; break;
+ case '+': flags |= PRINTF_SIGNPOSITIVE; break;
+ default:
+ goto noflags;
+ }
+ ++s;
+ }
+noflags:
+ if(*s == '*')
+ {
+ ++s;
+ if(*s >= '0' && *s <= '9')
+ {
+ width = strtol(s, &err, 10);
+ if(!err || *err != '$')
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err + 1;
+ }
+ else
+ width = argpos++;
+ width = GETARG_FLOAT(width);
+ if(width < 0)
+ {
+ flags |= PRINTF_LEFT;
+ width = -width;
+ }
+ }
+ else if(*s >= '0' && *s <= '9')
+ {
+ width = strtol(s, &err, 10);
+ if(!err)
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err;
+ if(width < 0)
+ {
+ flags |= PRINTF_LEFT;
+ width = -width;
+ }
+ }
+ // otherwise width stays -1
+ }
+
+ if(*s == '.')
+ {
+ ++s;
+ if(*s == '*')
+ {
+ ++s;
+ if(*s >= '0' && *s <= '9')
+ {
+ precision = strtol(s, &err, 10);
+ if(!err || *err != '$')
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err + 1;
+ }
+ else
+ precision = argpos++;
+ precision = GETARG_FLOAT(precision);
+ }
+ else if(*s >= '0' && *s <= '9')
+ {
+ precision = strtol(s, &err, 10);
+ if(!err)
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ s = err;
+ }
+ else
+ {
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ }
+
+ isfloat = true;
+ for(;;)
+ {
+ switch(*s)
+ {
+ case 'h': isfloat = true; break;
+ case 'l': isfloat = false; break;
+ case 'L': isfloat = false; break;
+ case 'j': break;
+ case 'z': break;
+ case 't': break;
+ default:
+ goto nolength;
+ }
+ ++s;
+ }
+nolength:
+
+ if(thisarg < 0)
+ thisarg = argpos++;
+
+ if(o < end - 1)
+ {
+ f = &formatbuf[1];
+ if(*s != 's' && *s != 'c')
+ if(flags & PRINTF_ALTERNATE) *f++ = '#';
+ if(flags & PRINTF_ZEROPAD) *f++ = '0';
+ if(flags & PRINTF_LEFT) *f++ = '-';
+ if(flags & PRINTF_SPACEPOSITIVE) *f++ = ' ';
+ if(flags & PRINTF_SIGNPOSITIVE) *f++ = '+';
+ *f++ = '*';
+ *f++ = '.';
+ *f++ = '*';
+ *f++ = *s;
+ *f++ = 0;
+
+ if(width < 0) // not set
+ width = 0;
+
+ switch(*s)
+ {
+ case 'd': case 'i':
+ if(precision < 0) // not set
+ precision = 1;
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg)));
+ break;
+ case 'o': case 'u': case 'x': case 'X':
+ if(precision < 0) // not set
+ precision = 1;
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+ break;
+ case 'e': case 'E': case 'f': case 'F': case 'g': case 'G':
+ if(precision < 0) // not set
+ precision = 6;
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg)));
+ break;
+ case 'c':
+ if(precision < 0) // not set
+ precision = end - o - 1;
+ if(flags & PRINTF_ALTERNATE)
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)));
+ else
+ {
+ unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg));
+ const char *buf = u8_encodech(c, NULL);
+ if(!buf)
+ buf = "";
+ o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision);
+ }
+ break;
+ case 's':
+ if(precision < 0) // not set
+ precision = end - o - 1;
+ if(flags & PRINTF_ALTERNATE)
+ o += dpsnprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg));
+ else
+ o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision);
+ break;
+ default:
+ VM_Warning("VM_sprintf: invalid directive in %s: %s\n", PRVM_NAME, s0);
+ goto finished;
+ }
+ }
+ ++s;
+ break;
+ default:
+verbatim:
+ if(o < end - 1)
+ *o++ = *s++;
+ break;
+ }
+ }
+finished:
+ *o = 0;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(outbuf);
+}
+
+
+// surface querying
+
+static dp_model_t *getmodel(prvm_edict_t *ed)
+{
+ switch(PRVM_GetProgNr())
+ {
+ case PRVM_SERVERPROG:
+ return SV_GetModelFromEdict(ed);
+ case PRVM_CLIENTPROG:
+ return CL_GetModelFromEdict(ed);
+ default:
+ return NULL;
+ }
+}
+
+typedef struct
+{
+ unsigned int progid;
+ dp_model_t *model;
+ frameblend_t frameblend[MAX_FRAMEBLENDS];
+ skeleton_t *skeleton_p;
+ skeleton_t skeleton;
+ float *data_vertex3f;
+ float *data_svector3f;
+ float *data_tvector3f;
+ float *data_normal3f;
+ int max_vertices;
+ float *buf_vertex3f;
+ float *buf_svector3f;
+ float *buf_tvector3f;
+ float *buf_normal3f;
+}
+animatemodel_cache_t;
+static animatemodel_cache_t animatemodel_cache;
+
+void animatemodel(dp_model_t *model, prvm_edict_t *ed)
+{
+ prvm_eval_t *val;
+ skeleton_t *skeleton;
+ int skeletonindex = -1;
+ qboolean need = false;
+ if(!model->AnimateVertices)
+ {
+ animatemodel_cache.data_vertex3f = model->surfmesh.data_vertex3f;
+ animatemodel_cache.data_svector3f = model->surfmesh.data_svector3f;
+ animatemodel_cache.data_tvector3f = model->surfmesh.data_tvector3f;
+ animatemodel_cache.data_normal3f = model->surfmesh.data_normal3f;
+ return;
+ }
+ if(animatemodel_cache.progid != prog->id)
+ memset(&animatemodel_cache, 0, sizeof(animatemodel_cache));
+ need |= (animatemodel_cache.model != model);
+ VM_GenerateFrameGroupBlend(ed->priv.server->framegroupblend, ed);
+ VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model);
+ need |= (memcmp(&animatemodel_cache.frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend)));
+ if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.skeletonindex))) skeletonindex = (int)val->_float - 1;
+ if (!(skeletonindex >= 0 && skeletonindex < MAX_EDICTS && (skeleton = prog->skeletons[skeletonindex]) && skeleton->model->num_bones == ed->priv.server->skeleton.model->num_bones))
+ skeleton = NULL;
+ need |= (animatemodel_cache.skeleton_p != skeleton);
+ if(skeleton)
+ need |= (memcmp(&animatemodel_cache.skeleton, skeleton, sizeof(ed->priv.server->skeleton)));
+ if(!need)
+ return;
+ if(model->surfmesh.num_vertices > animatemodel_cache.max_vertices)
+ {
+ animatemodel_cache.max_vertices = model->surfmesh.num_vertices * 2;
+ if(animatemodel_cache.buf_vertex3f) Mem_Free(animatemodel_cache.buf_vertex3f);
+ if(animatemodel_cache.buf_svector3f) Mem_Free(animatemodel_cache.buf_svector3f);
+ if(animatemodel_cache.buf_tvector3f) Mem_Free(animatemodel_cache.buf_tvector3f);
+ if(animatemodel_cache.buf_normal3f) Mem_Free(animatemodel_cache.buf_normal3f);
+ animatemodel_cache.buf_vertex3f = Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
+ animatemodel_cache.buf_svector3f = Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
+ animatemodel_cache.buf_tvector3f = Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
+ animatemodel_cache.buf_normal3f = Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices);
+ }
+ animatemodel_cache.data_vertex3f = animatemodel_cache.buf_vertex3f;
+ animatemodel_cache.data_svector3f = animatemodel_cache.buf_svector3f;
+ animatemodel_cache.data_tvector3f = animatemodel_cache.buf_tvector3f;
+ animatemodel_cache.data_normal3f = animatemodel_cache.buf_normal3f;
+ VM_UpdateEdictSkeleton(ed, model, ed->priv.server->frameblend);
+ model->AnimateVertices(model, ed->priv.server->frameblend, &ed->priv.server->skeleton, animatemodel_cache.data_vertex3f, animatemodel_cache.data_normal3f, animatemodel_cache.data_svector3f, animatemodel_cache.data_tvector3f);
+ animatemodel_cache.progid = prog->id;
+ animatemodel_cache.model = model;
+ memcpy(&animatemodel_cache.frameblend, &ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
+ animatemodel_cache.skeleton_p = skeleton;
+ if(skeleton)
+ memcpy(&animatemodel_cache.skeleton, skeleton, sizeof(ed->priv.server->skeleton));
+}
+
+static void getmatrix(prvm_edict_t *ed, matrix4x4_t *out)
+{
+ switch(PRVM_GetProgNr())
+ {
+ case PRVM_SERVERPROG:
+ SV_GetEntityMatrix(ed, out, false);
+ break;
+ case PRVM_CLIENTPROG:
+ CL_GetEntityMatrix(ed, out, false);
+ break;
+ default:
+ *out = identitymatrix;
+ break;
+ }
+}
+
+static void applytransform_forward(const vec3_t in, prvm_edict_t *ed, vec3_t out)
+{
+ matrix4x4_t m;
+ getmatrix(ed, &m);
+ Matrix4x4_Transform(&m, in, out);
+}
+
+static void applytransform_forward_direction(const vec3_t in, prvm_edict_t *ed, vec3_t out)
+{
+ matrix4x4_t m;
+ getmatrix(ed, &m);
+ Matrix4x4_Transform3x3(&m, in, out);
+}
+
+static void applytransform_inverted(const vec3_t in, prvm_edict_t *ed, vec3_t out)
+{
+ matrix4x4_t m, n;
+ getmatrix(ed, &m);
+ Matrix4x4_Invert_Full(&m, &n);
+ Matrix4x4_Transform3x3(&n, in, out);
+}
+
+static void applytransform_forward_normal(const vec3_t in, prvm_edict_t *ed, vec3_t out)
+{
+ matrix4x4_t m;
+ float p[4];
+ getmatrix(ed, &m);
+ Matrix4x4_TransformPositivePlane(&m, in[0], in[1], in[2], 0, p);
+ VectorCopy(p, out);
+}
+
+static void clippointtosurface(prvm_edict_t *ed, dp_model_t *model, msurface_t *surface, vec3_t p, vec3_t out)
+{
+ int i, j, k;
+ float *v[3], facenormal[3], edgenormal[3], sidenormal[3], temp[3], offsetdist, dist, bestdist;
+ const int *e;
+ animatemodel(model, ed);
+ bestdist = 1000000000;
+ VectorCopy(p, out);
+ for (i = 0, e = (model->surfmesh.data_element3i + 3 * surface->num_firsttriangle);i < surface->num_triangles;i++, e += 3)
+ {
+ // clip original point to each triangle of the surface and find the
+ // triangle that is closest
+ v[0] = animatemodel_cache.data_vertex3f + e[0] * 3;
+ v[1] = animatemodel_cache.data_vertex3f + e[1] * 3;
+ v[2] = animatemodel_cache.data_vertex3f + e[2] * 3;
+ TriangleNormal(v[0], v[1], v[2], facenormal);
+ VectorNormalize(facenormal);
+ offsetdist = DotProduct(v[0], facenormal) - DotProduct(p, facenormal);
+ VectorMA(p, offsetdist, facenormal, temp);
+ for (j = 0, k = 2;j < 3;k = j, j++)
+ {
+ VectorSubtract(v[k], v[j], edgenormal);
+ CrossProduct(edgenormal, facenormal, sidenormal);
+ VectorNormalize(sidenormal);
+ offsetdist = DotProduct(v[k], sidenormal) - DotProduct(temp, sidenormal);
+ if (offsetdist < 0)
+ VectorMA(temp, offsetdist, sidenormal, temp);
+ }
+ dist = VectorDistance2(temp, p);
+ if (bestdist > dist)
+ {
+ bestdist = dist;
+ VectorCopy(temp, out);
+ }
+ }
+}
+
+static msurface_t *getsurface(dp_model_t *model, int surfacenum)
+{
+ if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces)
+ return NULL;
+ return model->data_surfaces + surfacenum + model->firstmodelsurface;
+}
+
+
+//PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434;
+void VM_getsurfacenumpoints(void)
+{
+ dp_model_t *model;
+ msurface_t *surface;
+ VM_SAFEPARMCOUNT(2, VM_getsurfacenumpoints);
+ // return 0 if no such surface
+ if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+
+ // note: this (incorrectly) assumes it is a simple polygon
+ PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices;
+}
+//PF_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint = #435;
+void VM_getsurfacepoint(void)
+{
+ prvm_edict_t *ed;
+ dp_model_t *model;
+ msurface_t *surface;
+ int pointnum;
+ VM_SAFEPARMCOUNT(3, VM_getsurfacepoint);
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ // note: this (incorrectly) assumes it is a simple polygon
+ pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
+ if (pointnum < 0 || pointnum >= surface->num_vertices)
+ return;
+ animatemodel(model, ed);
+ applytransform_forward(&(animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+}
+//PF_getsurfacepointattribute, // #486 vector(entity e, float s, float n, float a) getsurfacepointattribute = #486;
+// float SPA_POSITION = 0;
+// float SPA_S_AXIS = 1;
+// float SPA_T_AXIS = 2;
+// float SPA_R_AXIS = 3; // same as SPA_NORMAL
+// float SPA_TEXCOORDS0 = 4;
+// float SPA_LIGHTMAP0_TEXCOORDS = 5;
+// float SPA_LIGHTMAP0_COLOR = 6;
+void VM_getsurfacepointattribute(void)
+{
+ prvm_edict_t *ed;
+ dp_model_t *model;
+ msurface_t *surface;
+ int pointnum;
+ int attributetype;
+
+ VM_SAFEPARMCOUNT(4, VM_getsurfacepoint);
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ pointnum = (int)PRVM_G_FLOAT(OFS_PARM2);
+ if (pointnum < 0 || pointnum >= surface->num_vertices)
+ return;
+ attributetype = (int) PRVM_G_FLOAT(OFS_PARM3);
+
+ animatemodel(model, ed);
+
+ switch( attributetype ) {
+ // float SPA_POSITION = 0;
+ case 0:
+ applytransform_forward(&(animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+ break;
+ // float SPA_S_AXIS = 1;
+ case 1:
+ applytransform_forward_direction(&(animatemodel_cache.data_svector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+ break;
+ // float SPA_T_AXIS = 2;
+ case 2:
+ applytransform_forward_direction(&(animatemodel_cache.data_tvector3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+ break;
+ // float SPA_R_AXIS = 3; // same as SPA_NORMAL
+ case 3:
+ applytransform_forward_direction(&(animatemodel_cache.data_normal3f + 3 * surface->num_firstvertex)[pointnum * 3], ed, PRVM_G_VECTOR(OFS_RETURN));
+ break;
+ // float SPA_TEXCOORDS0 = 4;
+ case 4: {
+ float *ret = PRVM_G_VECTOR(OFS_RETURN);
+ float *texcoord = &(model->surfmesh.data_texcoordtexture2f + 2 * surface->num_firstvertex)[pointnum * 2];
+ ret[0] = texcoord[0];
+ ret[1] = texcoord[1];
+ ret[2] = 0.0f;
+ break;
+ }
+ // float SPA_LIGHTMAP0_TEXCOORDS = 5;
+ case 5: {
+ float *ret = PRVM_G_VECTOR(OFS_RETURN);
+ float *texcoord = &(model->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[pointnum * 2];
+ ret[0] = texcoord[0];
+ ret[1] = texcoord[1];
+ ret[2] = 0.0f;
+ break;
+ }
+ // float SPA_LIGHTMAP0_COLOR = 6;
+ case 6:
+ // ignore alpha for now..
+ VectorCopy( &(model->surfmesh.data_lightmapcolor4f + 4 * surface->num_firstvertex)[pointnum * 4], PRVM_G_VECTOR(OFS_RETURN));
+ break;
+ default:
+ VectorSet( PRVM_G_VECTOR(OFS_RETURN), 0.0f, 0.0f, 0.0f );
+ break;
+ }
+}
+//PF_getsurfacenormal, // #436 vector(entity e, float s) getsurfacenormal = #436;
+void VM_getsurfacenormal(void)
+{
+ dp_model_t *model;
+ msurface_t *surface;
+ vec3_t normal;
+ VM_SAFEPARMCOUNT(2, VM_getsurfacenormal);
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ // note: this only returns the first triangle, so it doesn't work very
+ // well for curved surfaces or arbitrary meshes
+ animatemodel(model, PRVM_G_EDICT(OFS_PARM0));
+ TriangleNormal((animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex), (animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex) + 3, (animatemodel_cache.data_vertex3f + 3 * surface->num_firstvertex) + 6, normal);
+ applytransform_forward_normal(normal, PRVM_G_EDICT(OFS_PARM0), PRVM_G_VECTOR(OFS_RETURN));
+ VectorNormalize(PRVM_G_VECTOR(OFS_RETURN));
+}
+//PF_getsurfacetexture, // #437 string(entity e, float s) getsurfacetexture = #437;
+void VM_getsurfacetexture(void)
+{
+ dp_model_t *model;
+ msurface_t *surface;
+ VM_SAFEPARMCOUNT(2, VM_getsurfacetexture);
+ PRVM_G_INT(OFS_RETURN) = OFS_NULL;
+ if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(surface->texture->name);
+}
+//PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438;
+void VM_getsurfacenearpoint(void)
+{
+ int surfacenum, best;
+ vec3_t clipped, p;
+ vec_t dist, bestdist;
+ prvm_edict_t *ed;
+ dp_model_t *model;
+ msurface_t *surface;
+ vec_t *point;
+ VM_SAFEPARMCOUNT(2, VM_getsurfacenearpoint);
+ PRVM_G_FLOAT(OFS_RETURN) = -1;
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ point = PRVM_G_VECTOR(OFS_PARM1);
+
+ if (!ed || ed->priv.server->free)
+ return;
+ model = getmodel(ed);
+ if (!model || !model->num_surfaces)
+ return;
+
+ animatemodel(model, ed);
+
+ applytransform_inverted(point, ed, p);
+ best = -1;
+ bestdist = 1000000000;
+ for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++)
+ {
+ surface = model->data_surfaces + surfacenum + model->firstmodelsurface;
+ // first see if the nearest point on the surface's box is closer than the previous match
+ clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0];
+ clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1];
+ clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2];
+ dist = VectorLength2(clipped);
+ if (dist < bestdist)
+ {
+ // it is, check the nearest point on the actual geometry
+ clippointtosurface(ed, model, surface, p, clipped);
+ VectorSubtract(clipped, p, clipped);
+ dist += VectorLength2(clipped);
+ if (dist < bestdist)
+ {
+ // that's closer too, store it as the best match
+ best = surfacenum;
+ bestdist = dist;
+ }
+ }
+ }
+ PRVM_G_FLOAT(OFS_RETURN) = best;
+}
+//PF_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint = #439;
+void VM_getsurfaceclippedpoint(void)
+{
+ prvm_edict_t *ed;
+ dp_model_t *model;
+ msurface_t *surface;
+ vec3_t p, out;
+ VM_SAFEPARMCOUNT(3, VM_te_getsurfaceclippedpoint);
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ animatemodel(model, ed);
+ applytransform_inverted(PRVM_G_VECTOR(OFS_PARM2), ed, p);
+ clippointtosurface(ed, model, surface, p, out);
+ VectorAdd(out, ed->fields.server->origin, PRVM_G_VECTOR(OFS_RETURN));
+}
+
+//PF_getsurfacenumtriangles, // #??? float(entity e, float s) getsurfacenumtriangles = #???;
+void VM_getsurfacenumtriangles(void)
+{
+ dp_model_t *model;
+ msurface_t *surface;
+ VM_SAFEPARMCOUNT(2, VM_SV_getsurfacenumtriangles);
+ // return 0 if no such surface
+ if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ {
+ PRVM_G_FLOAT(OFS_RETURN) = 0;
+ return;
+ }
+
+ // note: this (incorrectly) assumes it is a simple polygon
+ PRVM_G_FLOAT(OFS_RETURN) = surface->num_triangles;
+}
+//PF_getsurfacetriangle, // #??? vector(entity e, float s, float n) getsurfacetriangle = #???;
+void VM_getsurfacetriangle(void)
+{
+ prvm_edict_t *ed;
+ dp_model_t *model;
+ msurface_t *surface;
+ int trinum;
+ VM_SAFEPARMCOUNT(3, VM_SV_getsurfacetriangle);
+ VectorClear(PRVM_G_VECTOR(OFS_RETURN));
+ ed = PRVM_G_EDICT(OFS_PARM0);
+ if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1))))
+ return;
+ trinum = (int)PRVM_G_FLOAT(OFS_PARM2);
+ if (trinum < 0 || trinum >= surface->num_triangles)
+ return;
+ // FIXME: implement rotation/scaling
+ VectorCopy(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], PRVM_G_VECTOR(OFS_RETURN));
+}