X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=prvm_cmds.c;h=359fc3866a1cd873c2748d1f562eaa0e142906a3;hp=ad060cbde18eb81c6cab7e7663e6fd6079ab8d5d;hb=691ac0330045dfcf51fda91fc7704e5fb56b3173;hpb=a8fa60f5e8370fded3d26994136a924903d9508e diff --git a/prvm_cmds.c b/prvm_cmds.c index ad060cbd..359fc386 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -10,6 +10,11 @@ #include "libcurl.h" #include +#include "cl_collision.h" +#include "clvm_cmds.h" +#include "ft2.h" +#include "mdfour.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 @@ -23,7 +28,7 @@ void VM_Warning(const char *fmt, ...) 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 @@ -49,6 +54,177 @@ void VM_CheckEmptyString (const char *s) PRVM_ERROR ("%s: Bad string", PRVM_NAME); } +void VM_GenerateFrameGroupBlend(framegroupblend_t *framegroupblend, const prvm_edict_t *ed) +{ + // 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) + framegroupblend[0].frame = (int) PRVM_gameedictfloat(ed, frame ); + framegroupblend[1].frame = (int) PRVM_gameedictfloat(ed, frame2 ); + framegroupblend[2].frame = (int) PRVM_gameedictfloat(ed, frame3 ); + framegroupblend[3].frame = (int) PRVM_gameedictfloat(ed, frame4 ); + framegroupblend[0].start = PRVM_gameedictfloat(ed, frame1time); + framegroupblend[1].start = PRVM_gameedictfloat(ed, frame2time); + framegroupblend[2].start = PRVM_gameedictfloat(ed, frame3time); + framegroupblend[3].start = PRVM_gameedictfloat(ed, frame4time); + framegroupblend[1].lerp = PRVM_gameedictfloat(ed, lerpfrac ); + framegroupblend[2].lerp = PRVM_gameedictfloat(ed, lerpfrac3 ); + framegroupblend[3].lerp = PRVM_gameedictfloat(ed, lerpfrac4 ); + // 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.model || !ed->priv.server->skeleton.model->num_bones) + { + if(ed->priv.server->skeleton.relativetransforms) + Mem_Free(ed->priv.server->skeleton.relativetransforms); + ed->priv.server->skeleton.relativetransforms = NULL; + return; + } + + { + int skeletonindex = -1; + skeleton_t *skeleton; + skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 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) + if (!ed->priv.server->skeleton.relativetransforms) + ed->priv.server->skeleton.relativetransforms = (matrix4x4_t *)Mem_Alloc(prog->progs_mempool, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t)); + memcpy(ed->priv.server->skeleton.relativetransforms, skeleton->relativetransforms, ed->priv.server->skeleton.model->num_bones * sizeof(matrix4x4_t)); + } + else + { + if(ed->priv.server->skeleton.relativetransforms) + Mem_Free(ed->priv.server->skeleton.relativetransforms); + ed->priv.server->skeleton.relativetransforms = NULL; + } + } +} + +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 @@ -82,7 +258,7 @@ checkextension(extensionname) static qboolean checkextension(const char *name) { int len; - char *e, *start; + const char *e, *start; len = (int)strlen(name); for (e = prog->extensionstring;*e;e++) @@ -95,7 +271,25 @@ static qboolean checkextension(const char *name) while (*e && *e != ' ') e++; if ((e - start) == len && !strncasecmp(start, name, len)) + { + // special sheck for ODE + if (!strncasecmp("DP_PHYSICS_ODE", name, 14)) + { +#ifdef USEODE + return ode_dll ? true : false; +#else + return false; +#endif + } + + // special sheck for d0_blind_id + if (!strcasecmp("DP_CRYPTO", name)) + return Crypto_Available(); + if (!strcasecmp("DP_QC_DIGEST_SHA256", name)) + return Crypto_Available(); + return true; + } } return false; } @@ -124,11 +318,8 @@ void VM_error (void) VM_VarString(0, string, sizeof(string)); Con_Printf("======%s ERROR in %s:\n%s\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); - if (prog->globaloffsets.self >= 0) - { - ed = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict); - PRVM_ED_Print(ed, NULL); - } + ed = PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)); + PRVM_ED_Print(ed, NULL); PRVM_ERROR ("%s: Program error in function %s:\n%s\nTip: read above for entity information\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); } @@ -150,16 +341,9 @@ void VM_objerror (void) VM_VarString(0, string, sizeof(string)); Con_Printf("======OBJECT ERROR======\n"); // , PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); // or include them? FIXME - if (prog->globaloffsets.self >= 0) - { - ed = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict); - PRVM_ED_Print(ed, NULL); - - PRVM_ED_Free (ed); - } - else - // objerror has to display the object fields -> else call - PRVM_ERROR ("VM_objecterror: self not defined !"); + ed = PRVM_PROG_TO_EDICT(PRVM_allglobaledict(self)); + PRVM_ED_Print(ed, NULL); + PRVM_ED_Free (ed); Con_Printf("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information\n", PRVM_NAME, PRVM_GetString(prog->xfunction->s_name), string); } @@ -559,15 +743,12 @@ void VM_dprint (void) { 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 - } } /* @@ -689,7 +870,7 @@ void VM_ftoe(void) VM_SAFEPARMCOUNT(1, VM_ftoe); ent = (int)PRVM_G_FLOAT(OFS_PARM0); - if (ent < 0 || ent >= MAX_EDICTS || PRVM_PROG_TO_EDICT(ent)->priv.required->free) + if (ent < 0 || ent >= prog->max_edicts || PRVM_PROG_TO_EDICT(ent)->priv.required->free) ent = 0; // return world instead of a free or invalid entity PRVM_G_INT(OFS_RETURN) = ent; @@ -789,12 +970,12 @@ void VM_remove (void) ed = PRVM_G_EDICT(OFS_PARM0); if( PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts ) { - if (developer.integer >= 1) + if (developer.integer > 0) 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 > 0) VM_Warning( "VM_remove: tried to remove an already freed entity!\n" ); } else @@ -930,7 +1111,7 @@ void VM_findchain (void) if (strcmp(t,s)) continue; - PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_NUM_FOR_EDICT(chain); + PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_NUM_FOR_EDICT(chain); chain = ent; } @@ -978,7 +1159,7 @@ void VM_findchainfloat (void) if (PRVM_E_FLOAT(ent,f) != s) continue; - PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_EDICT_TO_PROG(chain); + PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_EDICT_TO_PROG(chain); chain = ent; } @@ -1066,7 +1247,7 @@ void VM_findchainflags (void) if (!((int)PRVM_E_FLOAT(ent,f) & s)) continue; - PRVM_EDICTFIELDVALUE(ent,chainfield)->edict = PRVM_EDICT_TO_PROG(chain); + PRVM_EDICTFIELDEDICT(ent,chainfield) = PRVM_EDICT_TO_PROG(chain); chain = ent; } @@ -1660,14 +1841,14 @@ void VM_fopen(void) 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(); } } @@ -1701,8 +1882,8 @@ void VM_fclose(void) 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); } /* @@ -1752,8 +1933,8 @@ void VM_fgets(void) 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); } @@ -1788,8 +1969,8 @@ void VM_fputs(void) 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); } /* @@ -1834,7 +2015,7 @@ Return the number of entity fields - NOT offsets */ void VM_numentityfields(void) { - PRVM_G_FLOAT(OFS_RETURN) = prog->progs->numfielddefs; + PRVM_G_FLOAT(OFS_RETURN) = prog->numfielddefs; } // KrimZon - DP_QC_ENTITYDATA @@ -1851,7 +2032,7 @@ void VM_entityfieldname(void) ddef_t *d; int i = (int)PRVM_G_FLOAT(OFS_PARM0); - if (i < 0 || i >= prog->progs->numfielddefs) + if (i < 0 || i >= prog->numfielddefs) { VM_Warning("VM_entityfieldname: %s: field index out of bounds\n", PRVM_NAME); PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(""); @@ -1875,7 +2056,7 @@ void VM_entityfieldtype(void) ddef_t *d; int i = (int)PRVM_G_FLOAT(OFS_PARM0); - if (i < 0 || i >= prog->progs->numfielddefs) + if (i < 0 || i >= prog->numfielddefs) { VM_Warning("VM_entityfieldtype: %s: field index out of bounds\n", PRVM_NAME); PRVM_G_FLOAT(OFS_RETURN) = -1.0; @@ -1903,7 +2084,7 @@ void VM_getentityfieldstring(void) prvm_edict_t * ent; int i = (int)PRVM_G_FLOAT(OFS_PARM0); - if (i < 0 || i >= prog->progs->numfielddefs) + if (i < 0 || i >= prog->numfielddefs) { VM_Warning("VM_entityfielddata: %s: field index out of bounds\n", PRVM_NAME); PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(""); @@ -1950,7 +2131,7 @@ void VM_putentityfieldstring(void) prvm_edict_t * ent; int i = (int)PRVM_G_FLOAT(OFS_PARM0); - if (i < 0 || i >= prog->progs->numfielddefs) + if (i < 0 || i >= prog->numfielddefs) { VM_Warning("VM_entityfielddata: %s: field index out of bounds\n", PRVM_NAME); PRVM_G_FLOAT(OFS_RETURN) = 0.0f; @@ -1984,7 +2165,8 @@ void VM_strlen(void) { 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 @@ -2027,7 +2209,8 @@ void VM_strlennocol(void) 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; } @@ -2107,12 +2290,15 @@ string substring(string s, float start, float length) // returns a section of a string as a tempstring void VM_substring(void) { - int start, length, slength, maxlen; + 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); @@ -2130,6 +2316,40 @@ void VM_substring(void) 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); } /* @@ -2158,25 +2378,42 @@ void VM_strreplace(void) subject_len = (int)strlen(subject); si = 0; - for (i = 0; i < subject_len; i++) + for (i = 0; i <= subject_len - search_len; i++) { - for (j = 0; j < search_len && i+j < subject_len; j++) + for (j = 0; j < search_len; j++) // thus, i+j < subject_len if (subject[i+j] != search[j]) break; - if (j == search_len || i+j == subject_len) + if (j == search_len) { - // found it at offset 'i' + // NOTE: if search_len == 0, we always hit THIS case, and never the other + // found it at offset 'i' for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++) string[si++] = replace[j]; - i += search_len - 1; + if(search_len > 0) + { + i += search_len - 1; + } + else + { + // the above would subtract 1 from i... so we + // don't do that, but instead output the next + // char + if (si < (int)sizeof(string) - 1) + string[si++] = subject[i]; + } } else { - // not found + // in THIS case, we know search_len > 0, thus i < subject_len + // not found if (si < (int)sizeof(string) - 1) string[si++] = subject[i]; } } + // remaining chars (these cannot match) + for (; i < subject_len; i++) + if (si < (int)sizeof(string) - 1) + string[si++] = subject[i]; string[si] = '\0'; PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string); @@ -2208,25 +2445,42 @@ void VM_strireplace(void) subject_len = (int)strlen(subject); si = 0; - for (i = 0; i < subject_len; i++) + for (i = 0; i <= subject_len - search_len; i++) { - for (j = 0; j < search_len && i+j < subject_len; j++) + for (j = 0; j < search_len; j++) // thus, i+j < subject_len if (tolower(subject[i+j]) != tolower(search[j])) break; - if (j == search_len || i+j == subject_len) + if (j == search_len) { - // found it at offset 'i' + // NOTE: if search_len == 0, we always hit THIS case, and never the other + // found it at offset 'i' for (j = 0; j < replace_len && si < (int)sizeof(string) - 1; j++) string[si++] = replace[j]; - i += search_len - 1; + if(search_len > 0) + { + i += search_len - 1; + } + else + { + // the above would subtract 1 from i... so we + // don't do that, but instead output the next + // char + if (si < (int)sizeof(string) - 1) + string[si++] = subject[i]; + } } else { - // not found + // in THIS case, we know search_len > 0, thus i < subject_len + // not found if (si < (int)sizeof(string) - 1) string[si++] = subject[i]; } } + // remaining chars (these cannot match) + for (; i < subject_len; i++) + if (si < (int)sizeof(string) - 1) + string[si++] = subject[i]; string[si] = '\0'; PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string); @@ -2655,6 +2909,51 @@ void VM_gettime(void) } } +/* +========= +VM_getsoundtime + +float getsoundtime(void) +========= +*/ + +void VM_getsoundtime (void) +{ + int entnum, entchannel, pnum; + VM_SAFEPARMCOUNT(2,VM_getsoundtime); + + pnum = PRVM_GetProgNr(); + if (pnum == PRVM_MENUPROG) + { + VM_Warning("VM_getsoundtime: %s: not supported on this progs\n", PRVM_NAME); + PRVM_G_FLOAT(OFS_RETURN) = -1; + return; + } + entnum = ((pnum == PRVM_CLIENTPROG) ? MAX_EDICTS : 0) + PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0)); + entchannel = (int)PRVM_G_FLOAT(OFS_PARM1); + entchannel = CHAN_USER2ENGINE(entchannel); + if (!IS_CHAN(entchannel)) + VM_Warning("VM_getsoundtime: %s: bad channel %i\n", PRVM_NAME, entchannel); + PRVM_G_FLOAT(OFS_RETURN) = (float)S_GetEntChannelPosition(entnum, entchannel); +} + +/* +========= +VM_GetSoundLen + +string soundlength (string sample) +========= +*/ +void VM_soundlength (void) +{ + const char *s; + + VM_SAFEPARMCOUNT(1, VM_soundlength); + + s = PRVM_G_STRING(OFS_PARM0); + PRVM_G_FLOAT(OFS_RETURN) = S_SoundLength(s); +} + /* ========= VM_loadfromdata @@ -2912,6 +3211,7 @@ string chr(float ascii) */ void VM_chr(void) { + /* char tmp[2]; VM_SAFEPARMCOUNT(1, VM_chr); @@ -2919,6 +3219,15 @@ void VM_chr(void) 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); } //============================================================================= @@ -2957,7 +3266,7 @@ void VM_precache_pic(void) VM_CheckEmptyString (s); // AK Draw_CachePic is supposed to always return a valid pointer - if( Draw_CachePic_Flags(s, CACHEPICFLAG_NOTPERSISTENT)->tex == r_texture_notexture ) + if( Draw_CachePic_Flags(s, 0)->tex == r_texture_notexture ) PRVM_G_INT(OFS_RETURN) = OFS_NULL; } @@ -2980,17 +3289,24 @@ void VM_freepic(void) Draw_FreePic(s); } -dp_font_t *getdrawfont(void) +void getdrawfontscale(float *sx, float *sy) { - if(prog->globaloffsets.drawfont >= 0) + vec3_t v; + *sx = *sy = 1; + VectorCopy(PRVM_drawglobalvector(drawfontscale), v); + if(VectorLength2(v) > 0) { - int f = (int) PRVM_G_FLOAT(prog->globaloffsets.drawfont); - if(f < 0 || f >= MAX_FONTS) - return FONT_DEFAULT; - return &dp_fonts[f]; + *sx = v[0]; + *sy = v[1]; } - else +} + +dp_font_t *getdrawfont(void) +{ + int f = (int) PRVM_drawglobalfloat(drawfont); + if(f < 0 || f >= dp_fonts.maxsize) return FONT_DEFAULT; + return &dp_fonts.f[f]; } /* @@ -3005,6 +3321,7 @@ void VM_drawcharacter(void) float *pos,*scale,*rgb; char character; int flag; + float sx, sy; VM_SAFEPARMCOUNT(6,VM_drawcharacter); character = (char) PRVM_G_FLOAT(OFS_PARM1); @@ -3037,7 +3354,8 @@ void VM_drawcharacter(void) 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; } @@ -3045,21 +3363,23 @@ void VM_drawcharacter(void) ========= VM_drawstring -float drawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) +float drawstring(vector position, string text, vector scale, vector rgb, float alpha[, float flag]) ========= */ void VM_drawstring(void) { float *pos,*scale,*rgb; const char *string; - int flag; - VM_SAFEPARMCOUNT(6,VM_drawstring); + int flag = 0; + float sx, sy; + VM_SAFEPARMCOUNTRANGE(5,6,VM_drawstring); string = PRVM_G_STRING(OFS_PARM1); pos = PRVM_G_VECTOR(OFS_PARM0); scale = PRVM_G_VECTOR(OFS_PARM2); rgb = PRVM_G_VECTOR(OFS_PARM3); - flag = (int)PRVM_G_FLOAT(OFS_PARM5); + if (prog->argc >= 6) + flag = (int)PRVM_G_FLOAT(OFS_PARM5); if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS) { @@ -3078,7 +3398,9 @@ void VM_drawstring(void) 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; } @@ -3087,21 +3409,42 @@ void VM_drawstring(void) VM_drawcolorcodedstring float drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag) +/ +float drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) ========= */ void VM_drawcolorcodedstring(void) { - float *pos,*scale; + float *pos, *scale; const char *string; - int flag,color; - VM_SAFEPARMCOUNT(5,VM_drawstring); + int flag; + vec3_t rgb; + float sx, sy, alpha; - string = PRVM_G_STRING(OFS_PARM1); - pos = PRVM_G_VECTOR(OFS_PARM0); - scale = PRVM_G_VECTOR(OFS_PARM2); - flag = (int)PRVM_G_FLOAT(OFS_PARM4); + VM_SAFEPARMCOUNTRANGE(5,6,VM_drawcolorcodedstring); - if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS) + if (prog->argc == 6) // full 6 parms, like normal drawstring + { + pos = PRVM_G_VECTOR(OFS_PARM0); + string = PRVM_G_STRING(OFS_PARM1); + scale = PRVM_G_VECTOR(OFS_PARM2); + VectorCopy(PRVM_G_VECTOR(OFS_PARM3), rgb); + alpha = PRVM_G_FLOAT(OFS_PARM4); + flag = (int)PRVM_G_FLOAT(OFS_PARM5); + } + else + { + pos = PRVM_G_VECTOR(OFS_PARM0); + string = PRVM_G_STRING(OFS_PARM1); + scale = PRVM_G_VECTOR(OFS_PARM2); + rgb[0] = 1.0; + rgb[1] = 1.0; + rgb[2] = 1.0; + alpha = PRVM_G_FLOAT(OFS_PARM3); + flag = (int)PRVM_G_FLOAT(OFS_PARM4); + } + + if(flag < DRAWFLAG_NORMAL || flag >= DRAWFLAG_NUMFLAGS) { PRVM_G_FLOAT(OFS_RETURN) = -2; VM_Warning("VM_drawcolorcodedstring: %s: wrong DRAWFLAG %i !\n",PRVM_NAME,flag); @@ -3118,9 +3461,12 @@ void VM_drawcolorcodedstring(void) 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()); - PRVM_G_FLOAT(OFS_RETURN) = 1; + getdrawfontscale(&sx, &sy); + DrawQ_String_Scale(pos[0], pos[1], string, 0, scale[0], scale[1], sx, sy, rgb[0], rgb[1], rgb[2], alpha, flag, NULL, false, getdrawfont()); + if (prog->argc == 6) // also return vector of last color + VectorCopy(DrawQ_Color, PRVM_G_VECTOR(OFS_RETURN)); + else + PRVM_G_FLOAT(OFS_RETURN) = 1; } /* ========= @@ -3132,10 +3478,39 @@ float stringwidth(string text, float allowColorCodes, float size) void VM_stringwidth(void) { const char *string; - float sz, mult; // sz is intended font size so we can later add freetype support, mult is font size multiplier in pixels per character cell + 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; + 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_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); @@ -3149,8 +3524,178 @@ void VM_stringwidth(void) 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()) * mult; // 1x1 characters, don't actually draw + PRVM_G_FLOAT(OFS_RETURN) = DrawQ_TextWidth(string, 0, !colors, getdrawfont()) * mult; // 1x1 characters, don't actually draw +*/ +} + +/* +========= +VM_findfont + +float findfont(string s) +========= +*/ + +float getdrawfontnum(const char *fontname) +{ + int i; + + for(i = 0; i < dp_fonts.maxsize; ++i) + if(!strcmp(dp_fonts.f[i].title, fontname)) + return i; + return -1; +} + +void VM_findfont(void) +{ + VM_SAFEPARMCOUNT(1,VM_findfont); + PRVM_G_FLOAT(OFS_RETURN) = getdrawfontnum(PRVM_G_STRING(OFS_PARM0)); +} + +/* +========= +VM_loadfont + +float loadfont(string fontname, string fontmaps, string sizes, float slot) +========= +*/ + +dp_font_t *FindFont(const char *title, qboolean allocate_new); +void LoadFont(qboolean override, const char *name, dp_font_t *fnt, float scale, float voffset); +void VM_loadfont(void) +{ + const char *fontname, *filelist, *sizes, *c, *cm; + char mainfont[MAX_QPATH]; + int i, numsizes; + float sz, scale, voffset; + dp_font_t *f; + + VM_SAFEPARMCOUNTRANGE(3,6,VM_loadfont); + + fontname = PRVM_G_STRING(OFS_PARM0); + if (!fontname[0]) + fontname = "default"; + + filelist = PRVM_G_STRING(OFS_PARM1); + if (!filelist[0]) + filelist = "gfx/conchars"; + + sizes = PRVM_G_STRING(OFS_PARM2); + if (!sizes[0]) + sizes = "10"; + + // find a font + f = NULL; + if (prog->argc >= 4) + { + i = PRVM_G_FLOAT(OFS_PARM3); + if (i >= 0 && i < dp_fonts.maxsize) + { + f = &dp_fonts.f[i]; + strlcpy(f->title, fontname, sizeof(f->title)); // replace name + } + } + if (!f) + f = FindFont(fontname, true); + if (!f) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + return; // something go wrong + } + + memset(f->fallbacks, 0, sizeof(f->fallbacks)); + memset(f->fallback_faces, 0, sizeof(f->fallback_faces)); + + // first font is handled "normally" + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->req_face = atoi(c+1); + else + { + f->req_face = 0; + c = cm; + } + if(!c || (c - filelist) > MAX_QPATH) + strlcpy(mainfont, filelist, sizeof(mainfont)); + else + { + memcpy(mainfont, filelist, c - filelist); + mainfont[c - filelist] = 0; + } + + // handle fallbacks + for(i = 0; i < MAX_FONT_FALLBACKS; ++i) + { + c = strchr(filelist, ','); + if(!c) + break; + filelist = c + 1; + if(!*filelist) + break; + c = strchr(filelist, ':'); + cm = strchr(filelist, ','); + if(c && (!cm || c < cm)) + f->fallback_faces[i] = atoi(c+1); + else + { + f->fallback_faces[i] = 0; // f->req_face; could make it stick to the default-font's face index + c = cm; + } + if(!c || (c-filelist) > MAX_QPATH) + { + strlcpy(f->fallbacks[i], filelist, sizeof(mainfont)); + } + else + { + memcpy(f->fallbacks[i], filelist, c - filelist); + f->fallbacks[i][c - filelist] = 0; + } + } + + // handle sizes + for(i = 0; i < MAX_FONT_SIZES; ++i) + f->req_sizes[i] = -1; + for (numsizes = 0,c = sizes;;) + { + if (!COM_ParseToken_VM_Tokenize(&c, 0)) + break; + sz = atof(com_token); + // detect crap size + if (sz < 0.001f || sz > 1000.0f) + { + VM_Warning("VM_loadfont: crap size %s", com_token); + continue; + } + // check overflow + if (numsizes == MAX_FONT_SIZES) + { + VM_Warning("VM_loadfont: MAX_FONT_SIZES = %i exceeded", MAX_FONT_SIZES); + break; + } + f->req_sizes[numsizes] = sz; + numsizes++; + } + + // additional scale/hoffset parms + scale = 1; + voffset = 0; + if (prog->argc >= 5) + { + scale = PRVM_G_FLOAT(OFS_PARM4); + if (scale <= 0) + scale = 1; + } + if (prog->argc >= 6) + voffset = PRVM_G_FLOAT(OFS_PARM5); + + // load + LoadFont(true, mainfont, f, scale, voffset); + + // return index of loaded font + PRVM_G_FLOAT(OFS_RETURN) = (f - dp_fonts.f); } + /* ========= VM_drawpic @@ -3162,9 +3707,9 @@ void VM_drawpic(void) { const char *picname; float *size, *pos, *rgb; - int flag; + int flag = 0; - VM_SAFEPARMCOUNT(6,VM_drawpic); + VM_SAFEPARMCOUNTRANGE(5,6,VM_drawpic); picname = PRVM_G_STRING(OFS_PARM1); VM_CheckEmptyString (picname); @@ -3180,7 +3725,8 @@ void VM_drawpic(void) pos = PRVM_G_VECTOR(OFS_PARM0); size = PRVM_G_VECTOR(OFS_PARM2); rgb = PRVM_G_VECTOR(OFS_PARM3); - flag = (int) PRVM_G_FLOAT(OFS_PARM5); + if (prog->argc >= 6) + flag = (int) PRVM_G_FLOAT(OFS_PARM5); if(flag < DRAWFLAG_NORMAL || flag >=DRAWFLAG_NUMFLAGS) { @@ -3192,7 +3738,7 @@ void VM_drawpic(void) if(pos[2] || size[2]) Con_Printf("VM_drawpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size"))); - DrawQ_Pic(pos[0], pos[1], Draw_CachePic (picname), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag); + DrawQ_Pic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], rgb[0], rgb[1], rgb[2], PRVM_G_FLOAT(OFS_PARM4), flag); PRVM_G_FLOAT(OFS_RETURN) = 1; } /* @@ -3237,7 +3783,7 @@ void VM_drawrotpic(void) 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); + DrawQ_RotPic(pos[0], pos[1], Draw_CachePic_Flags(picname, CACHEPICFLAG_NOTPERSISTENT), 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; } /* @@ -3285,7 +3831,7 @@ void VM_drawsubpic(void) if(pos[2] || size[2]) Con_Printf("VM_drawsubpic: z value%s from %s discarded\n",(pos[2] && size[2]) ? "s" : " ",((pos[2] && size[2]) ? "pos and size" : (pos[2] ? "pos" : "size"))); - DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic (picname), + DrawQ_SuperPic(pos[0], pos[1], Draw_CachePic_Flags (picname, CACHEPICFLAG_NOTPERSISTENT), size[0], size[1], srcPos[0], srcPos[1], rgb[0], rgb[1], rgb[2], alpha, srcPos[0] + srcSize[0], srcPos[1], rgb[0], rgb[1], rgb[2], alpha, @@ -3405,31 +3951,35 @@ void VM_keynumtostring (void) ========= VM_findkeysforcommand -string findkeysforcommand(string command) +string findkeysforcommand(string command, float bindmap) the returned string is an altstring ========= */ -#define NUMKEYS 5 // TODO: merge the constant in keys.c with this one somewhen - +#define FKFC_NUMKEYS 5 void M_FindKeysForCommand(const char *command, int *keys); void VM_findkeysforcommand(void) { const char *cmd; char ret[VM_STRINGTEMP_LENGTH]; - int keys[NUMKEYS]; + int keys[FKFC_NUMKEYS]; int i; + int bindmap; - VM_SAFEPARMCOUNT(1, VM_findkeysforcommand); + VM_SAFEPARMCOUNTRANGE(1, 2, VM_findkeysforcommand); cmd = PRVM_G_STRING(OFS_PARM0); + if(prog->argc == 2) + bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1); + else + bindmap = 0; // consistent to "bind" VM_CheckEmptyString(cmd); - M_FindKeysForCommand(cmd, keys); + Key_FindKeysForCommand(cmd, keys, FKFC_NUMKEYS, bindmap); ret[0] = 0; - for(i = 0; i < NUMKEYS; i++) + for(i = 0; i < FKFC_NUMKEYS; i++) strlcat(ret, va(" \'%i\'", keys[i]), sizeof(ret)); PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(ret); @@ -3446,21 +3996,94 @@ void VM_stringtokeynum (void) { VM_SAFEPARMCOUNT( 1, VM_keynumtostring ); - PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0)); + PRVM_G_FLOAT(OFS_RETURN) = Key_StringToKeynum(PRVM_G_STRING(OFS_PARM0)); } -// CL_Video interface functions - /* -======================== -VM_cin_open +========= +VM_getkeybind -float cin_open(string file, string name) -======================== +string getkeybind(float key, float bindmap) +========= */ -void VM_cin_open( void ) +void VM_getkeybind (void) { - const char *file; + int bindmap; + VM_SAFEPARMCOUNTRANGE(1, 2, VM_CL_getkeybind); + if(prog->argc == 2) + bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM1), MAX_BINDMAPS-1); + else + bindmap = 0; // consistent to "bind" + + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(Key_GetBind((int)PRVM_G_FLOAT(OFS_PARM0), bindmap)); +} + +/* +========= +VM_setkeybind + +float setkeybind(float key, string cmd, float bindmap) +========= +*/ +void VM_setkeybind (void) +{ + int bindmap; + VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_setkeybind); + if(prog->argc == 3) + bindmap = bound(-1, PRVM_G_FLOAT(OFS_PARM2), MAX_BINDMAPS-1); + else + bindmap = 0; // consistent to "bind" + + PRVM_G_FLOAT(OFS_RETURN) = 0; + if(Key_SetBinding((int)PRVM_G_FLOAT(OFS_PARM0), bindmap, PRVM_G_STRING(OFS_PARM1))) + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +/* +========= +VM_getbindmap + +vector getbindmaps() +========= +*/ +void VM_getbindmaps (void) +{ + int fg, bg; + VM_SAFEPARMCOUNT(0, VM_CL_getbindmap); + Key_GetBindMap(&fg, &bg); + PRVM_G_VECTOR(OFS_RETURN)[0] = fg; + PRVM_G_VECTOR(OFS_RETURN)[1] = bg; + PRVM_G_VECTOR(OFS_RETURN)[2] = 0; +} + +/* +========= +VM_setbindmap + +float setbindmaps(vector bindmap) +========= +*/ +void VM_setbindmaps (void) +{ + VM_SAFEPARMCOUNT(1, VM_CL_setbindmap); + PRVM_G_FLOAT(OFS_RETURN) = 0; + if(PRVM_G_VECTOR(OFS_PARM0)[2] == 0) + if(Key_SetBindMap((int)PRVM_G_VECTOR(OFS_PARM0)[0], (int)PRVM_G_VECTOR(OFS_PARM0)[1])) + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +// CL_Video interface functions + +/* +======================== +VM_cin_open + +float cin_open(string file, string name) +======================== +*/ +void VM_cin_open( void ) +{ + const char *file; const char *name; VM_SAFEPARMCOUNT( 2, VM_cin_open ); @@ -3471,7 +4094,7 @@ void VM_cin_open( void ) VM_CheckEmptyString( file ); VM_CheckEmptyString( name ); - if( CL_OpenVideo( file, name, MENUOWNER ) ) + if( CL_OpenVideo( file, name, MENUOWNER, "" ) ) PRVM_G_FLOAT( OFS_RETURN ) = 1; else PRVM_G_FLOAT( OFS_RETURN ) = 0; @@ -3817,17 +4440,8 @@ void makevectors(vector angle) */ void VM_makevectors (void) { - prvm_eval_t *valforward, *valright, *valup; - valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward); - valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right); - valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up); - if (!valforward || !valright || !valup) - { - VM_Warning("makevectors: could not find v_forward, v_right, or v_up global variables\n"); - return; - } VM_SAFEPARMCOUNT(1, VM_makevectors); - AngleVectors (PRVM_G_VECTOR(OFS_PARM0), valforward->vector, valright->vector, valup->vector); + AngleVectors(PRVM_G_VECTOR(OFS_PARM0), PRVM_gameglobalvector(v_forward), PRVM_gameglobalvector(v_right), PRVM_gameglobalvector(v_up)); } /* @@ -3840,18 +4454,9 @@ vectorvectors(vector) */ void VM_vectorvectors (void) { - prvm_eval_t *valforward, *valright, *valup; - valforward = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_forward); - valright = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_right); - valup = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.v_up); - if (!valforward || !valright || !valup) - { - VM_Warning("vectorvectors: could not find v_forward, v_right, or v_up global variables\n"); - return; - } VM_SAFEPARMCOUNT(1, VM_vectorvectors); - VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), valforward->vector); - VectorVectors(valforward->vector, valright->vector, valup->vector); + VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), PRVM_gameglobalvector(v_forward)); + VectorVectors(PRVM_gameglobalvector(v_forward), PRVM_gameglobalvector(v_right), PRVM_gameglobalvector(v_up)); } /* @@ -4060,18 +4665,16 @@ string altstr_ins(string altstr, float num, string set) 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++ ) @@ -4136,8 +4739,8 @@ static int BufStr_SortStringsUP (const void *in1, const void *in2) const char *a, *b; a = *((const char **) in1); b = *((const char **) in2); - if(!a[0]) return 1; - if(!b[0]) return -1; + if(!a || !a[0]) return 1; + if(!b || !b[0]) return -1; return strncmp(a, b, stringbuffers_sortlength); } @@ -4146,8 +4749,8 @@ static int BufStr_SortStringsDOWN (const void *in1, const void *in2) const char *a, *b; a = *((const char **) in1); b = *((const char **) in2); - if(!a[0]) return 1; - if(!b[0]) return -1; + if(!a || !a[0]) return 1; + if(!b || !b[0]) return -1; return strncmp(b, a, stringbuffers_sortlength); } @@ -4156,19 +4759,34 @@ static int BufStr_SortStringsDOWN (const void *in1, const void *in2) 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); + + 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 @@ -4376,7 +4994,7 @@ void VM_bufstr_get (void) strindex = (int)PRVM_G_FLOAT(OFS_PARM1); if (strindex < 0) { - VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME); + // VM_Warning("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME); return; } if (strindex < stringbuffer->num_strings && stringbuffer->strings[strindex]) @@ -4392,6 +5010,7 @@ void bufstr_set(float bufhandle, float string_index, string str) = #466; */ void VM_bufstr_set (void) { + size_t alloclen; int strindex; prvm_stringbuffer_t *stringbuffer; const char *news; @@ -4418,10 +5037,11 @@ void VM_bufstr_set (void) Mem_Free(stringbuffer->strings[strindex]); stringbuffer->strings[strindex] = NULL; - news = PRVM_G_STRING(OFS_PARM2); - if (news && news[0]) + if(PRVM_G_INT(OFS_PARM2)) { - size_t alloclen = strlen(news) + 1; + // not the NULL string! + news = PRVM_G_STRING(OFS_PARM2); + alloclen = strlen(news) + 1; stringbuffer->strings[strindex] = (char *)Mem_Alloc(prog->progs_mempool, alloclen); memcpy(stringbuffer->strings[strindex], news, alloclen); } @@ -4453,12 +5073,12 @@ void VM_bufstr_add (void) VM_Warning("VM_bufstr_add: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); return; } - string = PRVM_G_STRING(OFS_PARM1); - if(!string || !string[0]) + if(!PRVM_G_INT(OFS_PARM1)) // NULL string { VM_Warning("VM_bufstr_add: can not add an empty string to buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); return; } + string = PRVM_G_STRING(OFS_PARM1); order = (int)PRVM_G_FLOAT(OFS_PARM2); if(order) strindex = stringbuffer->num_strings; @@ -4616,7 +5236,7 @@ void VM_changeyaw (void) // parameters because they are the parameters to SV_MoveToGoal, not this //VM_SAFEPARMCOUNT(0, VM_changeyaw); - ent = PRVM_PROG_TO_EDICT(PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict); + ent = PRVM_PROG_TO_EDICT(PRVM_gameglobaledict(self)); if (ent == prog->edicts) { VM_Warning("changeyaw: can not modify world entity\n"); @@ -4627,14 +5247,10 @@ void VM_changeyaw (void) VM_Warning("changeyaw: can not modify free entity\n"); return; } - if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.ideal_yaw < 0 || prog->fieldoffsets.yaw_speed < 0) - { - VM_Warning("changeyaw: angles, ideal_yaw, or yaw_speed field(s) not found\n"); - return; - } - current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1]); - ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.ideal_yaw)->_float; - speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.yaw_speed)->_float; + current = PRVM_gameedictvector(ent, angles)[1]; + current = ANGLEMOD(current); + ideal = PRVM_gameedictfloat(ent, ideal_yaw); + speed = PRVM_gameedictfloat(ent, yaw_speed); if (current == ideal) return; @@ -4660,7 +5276,8 @@ void VM_changeyaw (void) move = -speed; } - PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[1] = ANGLEMOD (current + move); + current += move; + PRVM_gameedictvector(ent, angles)[1] = ANGLEMOD(current); } /* @@ -4686,14 +5303,10 @@ void VM_changepitch (void) VM_Warning("changepitch: can not modify free entity\n"); return; } - if (prog->fieldoffsets.angles < 0 || prog->fieldoffsets.idealpitch < 0 || prog->fieldoffsets.pitch_speed < 0) - { - VM_Warning("changepitch: angles, idealpitch, or pitch_speed field(s) not found\n"); - return; - } - current = ANGLEMOD(PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0]); - ideal = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.idealpitch)->_float; - speed = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.pitch_speed)->_float; + current = PRVM_gameedictvector(ent, angles)[0]; + current = ANGLEMOD(current); + ideal = PRVM_gameedictfloat(ent, idealpitch); + speed = PRVM_gameedictfloat(ent, pitch_speed); if (current == ideal) return; @@ -4719,7 +5332,8 @@ void VM_changepitch (void) move = -speed; } - PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.angles)->vector[0] = ANGLEMOD (current + move); + current += move; + PRVM_gameedictvector(ent, angles)[0] = ANGLEMOD(current); } @@ -4746,6 +5360,7 @@ void VM_strstrofs (void) instr = PRVM_G_STRING(OFS_PARM0); match = PRVM_G_STRING(OFS_PARM1); firstofs = (prog->argc > 2)?(int)PRVM_G_FLOAT(OFS_PARM2):0; + firstofs = u8_bytelen(instr, firstofs); if (firstofs && (firstofs < 0 || firstofs > (int)strlen(instr))) { @@ -4757,17 +5372,27 @@ void VM_strstrofs (void) if (!match) PRVM_G_FLOAT(OFS_RETURN) = -1; else - PRVM_G_FLOAT(OFS_RETURN) = match - instr; + PRVM_G_FLOAT(OFS_RETURN) = u8_strnlen(instr, match-instr); } //#222 string(string s, float index) str2chr (FTE_STRINGS) 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; } @@ -4775,6 +5400,7 @@ void VM_str2chr (void) //#223 string(float c, ...) chr2str (FTE_STRINGS) void VM_chr2str (void) { + /* char t[9]; int i; VM_SAFEPARMCOUNTRANGE(0, 8, VM_chr2str); @@ -4782,6 +5408,15 @@ void VM_chr2str (void) 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) @@ -5007,12 +5642,61 @@ void VM_crc16(void) { float insensitive; static char s[VM_STRINGTEMP_LENGTH]; - VM_SAFEPARMCOUNTRANGE(2, 8, VM_hash); + VM_SAFEPARMCOUNTRANGE(2, 8, VM_crc16); insensitive = PRVM_G_FLOAT(OFS_PARM0); VM_VarString(1, s, sizeof(s)); PRVM_G_FLOAT(OFS_RETURN) = (unsigned short) ((insensitive ? CRC_Block_CaseInsensitive : CRC_Block) ((unsigned char *) s, strlen(s))); } +// #639 float(string digest, string data, ...) digest_hex +void VM_digest_hex(void) +{ + const char *digest; + + static char out[32]; + static char outhex[65]; + int outlen; + + static char s[VM_STRINGTEMP_LENGTH]; + int len; + + VM_SAFEPARMCOUNTRANGE(2, 8, VM_digest_hex); + digest = PRVM_G_STRING(OFS_PARM0); + if(!digest) + digest = ""; + VM_VarString(1, s, sizeof(s)); + len = strlen(s); + + outlen = 0; + + if(!strcmp(digest, "MD4")) + { + outlen = 16; + mdfour((unsigned char *) out, (unsigned char *) s, len); + } + else if(!strcmp(digest, "SHA256") && Crypto_Available()) + { + outlen = 32; + sha256((unsigned char *) out, (unsigned char *) s, len); + } + // no warning needed on mismatch - we return string_null to QC + + if(outlen) + { + int i; + static const char *hexmap = "0123456789abcdef"; + for(i = 0; i < outlen; ++i) + { + outhex[2*i] = hexmap[(out[i] >> 4) & 15]; + outhex[2*i+1] = hexmap[(out[i] >> 0) & 15]; + } + outhex[2*i] = 0; + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(outhex); + } + else + PRVM_G_INT(OFS_RETURN) = 0; +} + void VM_wasfreed (void) { VM_SAFEPARMCOUNT(1, VM_wasfreed); @@ -5021,65 +5705,37 @@ void VM_wasfreed (void) void VM_SetTraceGlobals(const trace_t *trace) { - prvm_eval_t *val; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_allsolid))) - val->_float = trace->allsolid; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_startsolid))) - val->_float = trace->startsolid; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_fraction))) - val->_float = trace->fraction; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inwater))) - val->_float = trace->inwater; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_inopen))) - val->_float = trace->inopen; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_endpos))) - VectorCopy(trace->endpos, val->vector); - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_normal))) - VectorCopy(trace->plane.normal, val->vector); - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_plane_dist))) - val->_float = trace->plane.dist; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_ent))) - val->edict = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts); - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dpstartcontents))) - val->_float = trace->startsupercontents; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitcontents))) - val->_float = trace->hitsupercontents; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphitq3surfaceflags))) - val->_float = trace->hitq3surfaceflags; - if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.trace_dphittexturename))) - val->string = trace->hittexture ? PRVM_SetTempString(trace->hittexture->name) : 0; + PRVM_gameglobalfloat(trace_allsolid) = trace->allsolid; + PRVM_gameglobalfloat(trace_startsolid) = trace->startsolid; + PRVM_gameglobalfloat(trace_fraction) = trace->fraction; + PRVM_gameglobalfloat(trace_inwater) = trace->inwater; + PRVM_gameglobalfloat(trace_inopen) = trace->inopen; + VectorCopy(trace->endpos, PRVM_gameglobalvector(trace_endpos)); + VectorCopy(trace->plane.normal, PRVM_gameglobalvector(trace_plane_normal)); + PRVM_gameglobalfloat(trace_plane_dist) = trace->plane.dist; + PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(trace->ent ? trace->ent : prog->edicts); + PRVM_gameglobalfloat(trace_dpstartcontents) = trace->startsupercontents; + PRVM_gameglobalfloat(trace_dphitcontents) = trace->hitsupercontents; + PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = trace->hitq3surfaceflags; + PRVM_gameglobalstring(trace_dphittexturename) = 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; + PRVM_gameglobalfloat(trace_allsolid) = 0; + PRVM_gameglobalfloat(trace_startsolid) = 0; + PRVM_gameglobalfloat(trace_fraction) = 0; + PRVM_gameglobalfloat(trace_inwater) = 0; + PRVM_gameglobalfloat(trace_inopen) = 0; + VectorClear(PRVM_gameglobalvector(trace_endpos)); + VectorClear(PRVM_gameglobalvector(trace_plane_normal)); + PRVM_gameglobalfloat(trace_plane_dist) = 0; + PRVM_gameglobaledict(trace_ent) = PRVM_EDICT_TO_PROG(prog->edicts); + PRVM_gameglobalfloat(trace_dpstartcontents) = 0; + PRVM_gameglobalfloat(trace_dphitcontents) = 0; + PRVM_gameglobalfloat(trace_dphitq3surfaceflags) = 0; + PRVM_gameglobalstring(trace_dphittexturename) = 0; } //============= @@ -5120,7 +5776,7 @@ void VM_uri_escape (void) || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || (*p == '-') || (*p == '_') || (*p == '.') - || (*p == '!') || (*p == '~') || (*p == '*') + || (*p == '!') || (*p == '~') || (*p == '\'') || (*p == '(') || (*p == ')')) *q++ = *p; else @@ -5201,6 +5857,10 @@ typedef struct double starttime; float id; char buffer[MAX_INPUTLINE]; + unsigned char *postdata; // free when uri_to_prog_t is freed + size_t postlen; + char *sigdata; // free when uri_to_prog_t is freed + size_t siglen; } uri_to_prog_t; @@ -5211,13 +5871,17 @@ static void uri_to_string_callback(int status, size_t length_received, unsigned if(!PRVM_ProgLoaded(handle->prognr)) { // curl reply came too late... so just drop it + if(handle->postdata) + Z_Free(handle->postdata); + if(handle->sigdata) + Z_Free(handle->sigdata); Z_Free(handle); return; } PRVM_SetProg(handle->prognr); PRVM_Begin; - if((prog->starttime == handle->starttime) && (prog->funcoffsets.URI_Get_Callback)) + if((prog->starttime == handle->starttime) && (PRVM_allfunction(URI_Get_Callback))) { if(length_received >= sizeof(handle->buffer)) length_received = sizeof(handle->buffer) - 1; @@ -5226,10 +5890,14 @@ static void uri_to_string_callback(int status, size_t length_received, unsigned PRVM_G_FLOAT(OFS_PARM0) = handle->id; PRVM_G_FLOAT(OFS_PARM1) = status; PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(handle->buffer); - PRVM_ExecuteProgram(prog->funcoffsets.URI_Get_Callback, "QC function URI_Get_Callback is missing"); + PRVM_ExecuteProgram(PRVM_allfunction(URI_Get_Callback), "QC function URI_Get_Callback is missing"); } PRVM_End; + if(handle->postdata) + Z_Free(handle->postdata); + if(handle->sigdata) + Z_Free(handle->sigdata); Z_Free(handle); } @@ -5241,26 +5909,157 @@ void VM_uri_get (void) float id; qboolean ret; uri_to_prog_t *handle; - - if(!prog->funcoffsets.URI_Get_Callback) + const char *posttype = NULL; + const char *postseparator = NULL; + int poststringbuffer = -1; + int postkeyid = -1; + const char *query_string = NULL; + size_t lq; + + if(!PRVM_allfunction(URI_Get_Callback)) PRVM_ERROR("uri_get called by %s without URI_Get_Callback defined", PRVM_NAME); - VM_SAFEPARMCOUNT(2, VM_uri_get); + VM_SAFEPARMCOUNTRANGE(2, 6, VM_uri_get); url = PRVM_G_STRING(OFS_PARM0); id = PRVM_G_FLOAT(OFS_PARM1); + if(prog->argc >= 3) + posttype = PRVM_G_STRING(OFS_PARM2); + if(prog->argc >= 4) + postseparator = PRVM_G_STRING(OFS_PARM3); + if(prog->argc >= 5) + poststringbuffer = PRVM_G_FLOAT(OFS_PARM4); + if(prog->argc >= 6) + postkeyid = PRVM_G_FLOAT(OFS_PARM5); 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! + query_string = strchr(url, '?'); + if(query_string) + ++query_string; + lq = query_string ? strlen(query_string) : 0; + handle->prognr = PRVM_GetProgNr(); handle->starttime = prog->starttime; handle->id = id; - ret = Curl_Begin_ToMemory(url, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle); + if(postseparator && posttype && *posttype) + { + size_t l = strlen(postseparator); + if(poststringbuffer >= 0) + { + size_t ltotal; + int i; + // "implode" + prvm_stringbuffer_t *stringbuffer; + stringbuffer = (prvm_stringbuffer_t *)Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, poststringbuffer); + if(!stringbuffer) + { + VM_Warning("uri_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + ltotal = 0; + for(i = 0;i < stringbuffer->num_strings;i++) + { + if(i > 0) + ltotal += l; + if(stringbuffer->strings[i]) + ltotal += strlen(stringbuffer->strings[i]); + } + handle->postdata = (unsigned char *)Z_Malloc(ltotal + 1 + lq); + handle->postlen = ltotal; + ltotal = 0; + for(i = 0;i < stringbuffer->num_strings;i++) + { + if(i > 0) + { + memcpy(handle->postdata + ltotal, postseparator, l); + ltotal += l; + } + if(stringbuffer->strings[i]) + { + memcpy(handle->postdata + ltotal, stringbuffer->strings[i], strlen(stringbuffer->strings[i])); + ltotal += strlen(stringbuffer->strings[i]); + } + } + if(ltotal != handle->postlen) + PRVM_ERROR ("%s: string buffer content size mismatch, possible overrun", PRVM_NAME); + } + else + { + handle->postdata = (unsigned char *)Z_Malloc(l + 1 + lq); + handle->postlen = l; + memcpy(handle->postdata, postseparator, l); + } + handle->postdata[handle->postlen] = 0; + if(query_string) + memcpy(handle->postdata + handle->postlen + 1, query_string, lq); + if(postkeyid >= 0) + { + // POST: we sign postdata \0 query string + size_t ll; + handle->sigdata = (char *)Z_Malloc(8192); + strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192); + l = strlen(handle->sigdata); + handle->siglen = Crypto_SignDataDetached(handle->postdata, handle->postlen + 1 + lq, postkeyid, handle->sigdata + l, 8192 - l); + if(!handle->siglen) + { + Z_Free(handle->sigdata); + handle->sigdata = NULL; + goto out1; + } + ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1); + if(!ll) + { + Z_Free(handle->sigdata); + handle->sigdata = NULL; + goto out1; + } + handle->siglen = l + ll; + handle->sigdata[handle->siglen] = 0; + } +out1: + ret = Curl_Begin_ToMemory_POST(url, handle->sigdata, 0, posttype, handle->postdata, handle->postlen, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle); + } + else + { + if(postkeyid >= 0 && query_string) + { + // GET: we sign JUST the query string + size_t l, ll; + handle->sigdata = (char *)Z_Malloc(8192); + strlcpy(handle->sigdata, "X-D0-Blind-ID-Detached-Signature: ", 8192); + l = strlen(handle->sigdata); + handle->siglen = Crypto_SignDataDetached(query_string, lq, postkeyid, handle->sigdata + l, 8192 - l); + if(!handle->siglen) + { + Z_Free(handle->sigdata); + handle->sigdata = NULL; + goto out2; + } + ll = base64_encode((unsigned char *) (handle->sigdata + l), handle->siglen, 8192 - l - 1); + if(!ll) + { + Z_Free(handle->sigdata); + handle->sigdata = NULL; + goto out2; + } + handle->siglen = l + ll; + handle->sigdata[handle->siglen] = 0; + } +out2: + handle->postdata = NULL; + handle->postlen = 0; + ret = Curl_Begin_ToMemory(url, 0, (unsigned char *) handle->buffer, sizeof(handle->buffer), uri_to_string_callback, handle); + } if(ret) { PRVM_G_INT(OFS_RETURN) = 1; } else { + if(handle->postdata) + Z_Free(handle->postdata); + if(handle->sigdata) + Z_Free(handle->sigdata); Z_Free(handle); PRVM_G_INT(OFS_RETURN) = 0; } @@ -5291,14 +6090,14 @@ void VM_CL_getextresponse (void) { VM_SAFEPARMCOUNT(0,VM_argv); - if (net_extresponse_count <= 0) + if (cl_net_extresponse_count <= 0) PRVM_G_INT(OFS_RETURN) = OFS_NULL; else { int first; - --net_extresponse_count; - first = (net_extresponse_last + NET_EXTRESPONSE_MAX - net_extresponse_count) % NET_EXTRESPONSE_MAX; - PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(net_extresponse[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]); } } @@ -5313,10 +6112,30 @@ void VM_SV_getextresponse (void) 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_SetEngineString(sv_net_extresponse[first]); + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(sv_net_extresponse[first]); } } +/* +========= +Common functions between menu.dat and clsprogs +========= +*/ + +//#349 float() isdemo +void VM_CL_isdemo (void) +{ + VM_SAFEPARMCOUNT(0, VM_CL_isdemo); + PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback; +} + +//#355 float() videoplaying +void VM_CL_videoplaying (void) +{ + VM_SAFEPARMCOUNT(0, VM_CL_videoplaying); + PRVM_G_FLOAT(OFS_RETURN) = cl_videoplaying; +} + /* ========= VM_M_callfunction @@ -5385,3 +6204,842 @@ void VM_isfunction(void) 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; + int isfloat; + static int dummyivec[3] = {0, 0, 0}; + static float dummyvec[3] = {0, 0, 0}; + +#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)argc) ? (PRVM_G_FLOAT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_VECTOR(a) (((a)>=1 && (a)argc) ? (PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyvec) +#define GETARG_INT(a) (((a)>=1 && (a)argc) ? (PRVM_G_INT(OFS_PARM0 + 3 * (a))) : 0) +#define GETARG_INTVECTOR(a) (((a)>=1 && (a)argc) ? ((int*) PRVM_G_VECTOR(OFS_PARM0 + 3 * (a))) : dummyivec) +#define GETARG_STRING(a) (((a)>=1 && (a)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; + isfloat = -1; + + // 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; + } + } + + for(;;) + { + switch(*s) + { + case 'h': isfloat = 1; break; + case 'l': isfloat = 0; break; + case 'L': isfloat = 0; break; + case 'j': break; + case 'z': break; + case 't': break; + default: + goto nolength; + } + ++s; + } +nolength: + + // now s points to the final directive char and is no longer changed + if(isfloat < 0) + { + if(*s == 'i') + isfloat = 0; + else + isfloat = 1; + } + + 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++ = '*'; + if(precision >= 0) + { + *f++ = '.'; + *f++ = '*'; + } + *f++ = *s; + *f++ = 0; + + if(width < 0) // not set + width = 0; + + switch(*s) + { + case 'd': case 'i': + if(precision < 0) // not set + o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (int) GETARG_FLOAT(thisarg) : (int) GETARG_INT(thisarg))); + else + 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 + o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + else + 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 + o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg))); + else + o += dpsnprintf(o, end - o, formatbuf, width, precision, (isfloat ? (double) GETARG_FLOAT(thisarg) : (double) GETARG_INT(thisarg))); + break; + case 'v': case 'V': + f[-2] += 'g' - 'v'; + if(precision < 0) // not set + o += dpsnprintf(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf), + width, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]), + width, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]), + width, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2]) + ); + else + o += dpsnprintf(o, end - o, va("%s %s %s", /* NESTED SPRINTF IS NESTED */ formatbuf, formatbuf, formatbuf), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[0] : (double) GETARG_INTVECTOR(thisarg)[0]), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[1] : (double) GETARG_INTVECTOR(thisarg)[1]), + width, precision, (isfloat ? (double) GETARG_VECTOR(thisarg)[2] : (double) GETARG_INTVECTOR(thisarg)[2]) + ); + break; + case 'c': + if(flags & PRINTF_ALTERNATE) + { + if(precision < 0) // not set + o += dpsnprintf(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); + else + 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 = ""; + if(precision < 0) // not set + precision = end - o - 1; + o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision); + } + break; + case 's': + if(flags & PRINTF_ALTERNATE) + { + if(precision < 0) // not set + o += dpsnprintf(o, end - o, formatbuf, width, GETARG_STRING(thisarg)); + else + o += dpsnprintf(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg)); + } + else + { + if(precision < 0) // not set + precision = end - o - 1; + 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) +{ + skeleton_t *skeleton; + int skeletonindex = -1; + qboolean need = false; + if(!(model->surfmesh.isanimated && 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))) != 0; + skeletonindex = (int)PRVM_gameedictfloat(ed, skeletonindex) - 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))) != 0; + 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 = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_svector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_tvector3f = (float *)Mem_Alloc(prog->progs_mempool, sizeof(float[3]) * animatemodel_cache.max_vertices); + animatemodel_cache.buf_normal3f = (float *)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(&n, &m); + 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, PRVM_serveredictvector(ed, 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) +{ + const vec3_t d = {-1, -1, -1}; + 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 + VectorMA(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], surface->num_firstvertex, d, PRVM_G_VECTOR(OFS_RETURN)); +} + +// +// physics builtins +// + +void World_Physics_ApplyCmd(prvm_edict_t *ed, edict_odefunc_t *f); + +#define VM_physics_ApplyCmd(ed,f) if (!ed->priv.server->ode_body) VM_physics_newstackfunction(ed, f); else World_Physics_ApplyCmd(ed, f) + +edict_odefunc_t *VM_physics_newstackfunction(prvm_edict_t *ed, edict_odefunc_t *f) +{ + edict_odefunc_t *newfunc, *func; + + newfunc = (edict_odefunc_t *)Mem_Alloc(prog->progs_mempool, sizeof(edict_odefunc_t)); + memcpy(newfunc, f, sizeof(edict_odefunc_t)); + newfunc->next = NULL; + if (!ed->priv.server->ode_func) + ed->priv.server->ode_func = newfunc; + else + { + for (func = ed->priv.server->ode_func; func->next; func = func->next); + func->next = newfunc; + } + return newfunc; +} + +// void(entity e, float physics_enabled) physics_enable = #; +void VM_physics_enable(void) +{ + prvm_edict_t *ed; + edict_odefunc_t f; + + VM_SAFEPARMCOUNT(2, VM_physics_enable); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!ed) + { + if (developer.integer > 0) + VM_Warning("VM_physics_enable: null entity!\n"); + return; + } + // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer + if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS) + { + VM_Warning("VM_physics_enable: entity is not MOVETYPE_PHYSICS!\n"); + return; + } + f.type = PRVM_G_FLOAT(OFS_PARM1) == 0 ? ODEFUNC_DISABLE : ODEFUNC_ENABLE; + VM_physics_ApplyCmd(ed, &f); +} + +// void(entity e, vector force, vector relative_ofs) physics_addforce = #; +void VM_physics_addforce(void) +{ + prvm_edict_t *ed; + edict_odefunc_t f; + + VM_SAFEPARMCOUNT(3, VM_physics_addforce); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!ed) + { + if (developer.integer > 0) + VM_Warning("VM_physics_addforce: null entity!\n"); + return; + } + // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer + if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS) + { + VM_Warning("VM_physics_addforce: entity is not MOVETYPE_PHYSICS!\n"); + return; + } + f.type = ODEFUNC_RELFORCEATPOS; + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1); + VectorSubtract(PRVM_serveredictvector(ed, origin), PRVM_G_VECTOR(OFS_PARM2), f.v2); + VM_physics_ApplyCmd(ed, &f); +} + +// void(entity e, vector torque) physics_addtorque = #; +void VM_physics_addtorque(void) +{ + prvm_edict_t *ed; + edict_odefunc_t f; + + VM_SAFEPARMCOUNT(2, VM_physics_addtorque); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!ed) + { + if (developer.integer > 0) + VM_Warning("VM_physics_addtorque: null entity!\n"); + return; + } + // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer + if (PRVM_serveredictfloat(ed, movetype) != MOVETYPE_PHYSICS) + { + VM_Warning("VM_physics_addtorque: entity is not MOVETYPE_PHYSICS!\n"); + return; + } + f.type = ODEFUNC_RELTORQUE; + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1); + VM_physics_ApplyCmd(ed, &f); +}