X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=svvm_cmds.c;h=7f33f9c1b361367ad6b46885fa258e35f2e3bf7e;hp=fe00a4458f4cf159fb6730ff791ba031a5bcf188;hb=7fe21433f8c26bd2442ab72707f7f6063c1932e7;hpb=9cfa155ad179e8cb0bd04a52fadff291d6cec411 diff --git a/svvm_cmds.c b/svvm_cmds.c index fe00a445..7f33f9c1 100644 --- a/svvm_cmds.c +++ b/svvm_cmds.c @@ -3,11 +3,13 @@ //============================================================================ // Server -#define PF_WARNING(s) do{Con_Printf(s);PRVM_PrintState();return;}while(0) -cvar_t sv_aim = {CVAR_SAVE, "sv_aim", "2"}; //"0.93"}; // LordHavoc: disabled autoaim by default +cvar_t sv_aim = {CVAR_SAVE, "sv_aim", "2", "maximum cosine angle for quake's vertical autoaim, a value above 1 completely disables the autoaim, quake used 0.93"}; //"0.93"}; // LordHavoc: disabled autoaim by default char *vm_sv_extensions = +"BX_WAL_SUPPORT " +"DP_CON_EXPANDCVAR " +"DP_CON_ALIASPARAMETERS " "DP_BUTTONCHAT " "DP_BUTTONUSE " "DP_CL_LOADSKY " @@ -18,12 +20,14 @@ char *vm_sv_extensions = "DP_EF_BLUE " "DP_EF_FLAME " "DP_EF_FULLBRIGHT " +"DP_EF_DOUBLESIDED " "DP_EF_NODEPTHTEST " "DP_EF_NODRAW " "DP_EF_NOSHADOW " "DP_EF_RED " "DP_EF_STARDUST " "DP_ENT_ALPHA " +"DP_ENT_COLORMOD " "DP_ENT_CUSTOMCOLORMAP " "DP_ENT_EXTERIORMODELTOCLIENT " "DP_ENT_GLOW " @@ -31,6 +35,7 @@ char *vm_sv_extensions = "DP_ENT_SCALE " "DP_ENT_VIEWMODEL " "DP_GFX_EXTERNALTEXTURES " +"DP_GFX_EXTERNALTEXTURES_PERMAP " "DP_GFX_FOG " "DP_GFX_QUAKE3MODELTAGS " "DP_GFX_SKINFILES " @@ -44,6 +49,7 @@ char *vm_sv_extensions = "DP_MONSTERWALK " "DP_MOVETYPEBOUNCEMISSILE " "DP_MOVETYPEFOLLOW " +"DP_QC_ASINACOSATANATAN2TAN " "DP_QC_CHANGEPITCH " "DP_QC_COPYENTITY " "DP_QC_CVAR_STRING " @@ -61,6 +67,10 @@ char *vm_sv_extensions = "DP_QC_MULTIPLETEMPSTRINGS " "DP_QC_RANDOMVEC " "DP_QC_SINCOSSQRTPOW " +"DP_QC_STRFTIME " +"DP_QC_STRINGBUFFERS " +"DP_QC_STRINGCOLORFUNCTIONS " +"DP_QC_UNLIMITEDTEMPSTRINGS " "DP_QC_TRACEBOX " "DP_QC_TRACETOSS " "DP_QC_TRACE_MOVETYPE_HITMODEL " @@ -80,16 +90,21 @@ char *vm_sv_extensions = "DP_SV_BOTCLIENT " "DP_SV_CLIENTCOLORS " "DP_SV_CLIENTNAME " +"DP_SV_CUSTOMIZEENTITYFORCLIENT " "DP_SV_DRAWONLYTOCLIENT " "DP_SV_DROPCLIENT " "DP_SV_EFFECT " +"DP_SV_ENTITYCONTENTSTRANSITION " "DP_SV_NODRAWTOCLIENT " "DP_SV_PING " "DP_SV_PLAYERPHYSICS " +"DP_SV_PRECACHEANYTIME " +"DP_SV_PRINT " "DP_SV_PUNCHVECTOR " "DP_SV_ROTATINGBMODEL " "DP_SV_SETCOLOR " "DP_SV_SLOWMO " +"DP_SV_WRITEUNTERMINATEDSTRING " "DP_TE_BLOOD " "DP_TE_BLOODSHOWER " "DP_TE_CUSTOMFLASH " @@ -103,7 +118,10 @@ char *vm_sv_extensions = "DP_TE_SMALLFLASH " "DP_TE_SPARK " "DP_TE_STANDARDEFFECTBUILTINS " +"DP_TRACE_HITCONTENTSMASK_SURFACEINFO " "DP_VIEWZOOM " +"EXT_BITSHIFT " +//"EXT_CSQC " // not ready yet "FRIK_FILE " "KRIMZON_SV_PARSECLIENTCOMMAND " "NEH_CMD_PLAY2 " @@ -113,7 +131,6 @@ char *vm_sv_extensions = "TENEBRAE_GFX_DLIGHTS " "TW_SV_STEPCONTROL " "NEXUIZ_PLAYERMODEL " -"NEXUIZ_PLAYERSKIN " ; /* @@ -129,20 +146,6 @@ void PF_makevectors (void) AngleVectors (PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up); } -/* -============== -PF_vectorvectors - -Writes new values for v_forward, v_up, and v_right based on the given forward vector -vectorvectors(vector, vector) -============== -*/ -void PF_vectorvectors (void) -{ - VectorNormalize2(PRVM_G_VECTOR(OFS_PARM0), prog->globals.server->v_forward); - VectorVectors(prog->globals.server->v_forward, prog->globals.server->v_right, prog->globals.server->v_up); -} - /* ================= PF_setorigin @@ -159,9 +162,15 @@ void PF_setorigin (void) e = PRVM_G_EDICT(OFS_PARM0); if (e == prog->edicts) - PF_WARNING("setorigin: can not modify world entity\n"); + { + VM_Warning("setorigin: can not modify world entity\n"); + return; + } if (e->priv.server->free) - PF_WARNING("setorigin: can not modify free entity\n"); + { + VM_Warning("setorigin: can not modify free entity\n"); + return; + } org = PRVM_G_VECTOR(OFS_PARM1); VectorCopy (org, e->fields.server->origin); SV_LinkEdict (e, false); @@ -174,7 +183,7 @@ void SetMinMaxSize (prvm_edict_t *e, float *min, float *max, qboolean rotate) for (i=0 ; i<3 ; i++) if (min[i] > max[i]) - PRVM_ERROR("SetMinMaxSize: backwards mins/maxs\n"); + PRVM_ERROR("SetMinMaxSize: backwards mins/maxs"); // set derived values VectorCopy (min, e->fields.server->mins); @@ -201,9 +210,15 @@ void PF_setsize (void) e = PRVM_G_EDICT(OFS_PARM0); if (e == prog->edicts) - PF_WARNING("setsize: can not modify world entity\n"); + { + VM_Warning("setsize: can not modify world entity\n"); + return; + } if (e->priv.server->free) - PF_WARNING("setsize: can not modify free entity\n"); + { + VM_Warning("setsize: can not modify free entity\n"); + return; + } min = PRVM_G_VECTOR(OFS_PARM1); max = PRVM_G_VECTOR(OFS_PARM2); SetMinMaxSize (e, min, max, false); @@ -226,9 +241,15 @@ void PF_setmodel (void) e = PRVM_G_EDICT(OFS_PARM0); if (e == prog->edicts) - PF_WARNING("setmodel: can not modify world entity\n"); + { + VM_Warning("setmodel: can not modify world entity\n"); + return; + } if (e->priv.server->free) - PF_WARNING("setmodel: can not modify free entity\n"); + { + VM_Warning("setmodel: can not modify free entity\n"); + return; + } i = SV_ModelIndex(PRVM_G_STRING(OFS_PARM1), 1); e->fields.server->model = PRVM_SetEngineString(sv.model_precache[i]); e->fields.server->modelindex = i; @@ -265,14 +286,17 @@ void PF_sprint (void) if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active) { - Con_Print("tried to sprint to a non-client\n"); + VM_Warning("tried to centerprint to a non-client\n"); return; } client = svs.clients + entnum-1; + if (!client->netconnection) + return; + VM_VarString(1, string, sizeof(string)); - MSG_WriteChar(&client->message,svc_print); - MSG_WriteString(&client->message, string); + MSG_WriteChar(&client->netconnection->message,svc_print); + MSG_WriteString(&client->netconnection->message, string); } @@ -295,14 +319,17 @@ void PF_centerprint (void) if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active) { - Con_Print("tried to sprint to a non-client\n"); + VM_Warning("tried to centerprint to a non-client\n"); return; } client = svs.clients + entnum-1; + if (!client->netconnection) + return; + VM_VarString(1, string, sizeof(string)); - MSG_WriteChar(&client->message,svc_centerprint); - MSG_WriteString(&client->message, string); + MSG_WriteChar(&client->netconnection->message,svc_centerprint); + MSG_WriteString(&client->netconnection->message, string); } /* @@ -322,7 +349,7 @@ void PF_particle (void) dir = PRVM_G_VECTOR(OFS_PARM1); color = PRVM_G_FLOAT(OFS_PARM2); count = PRVM_G_FLOAT(OFS_PARM3); - SV_StartParticle (org, dir, color, count); + SV_StartParticle (org, dir, (int)color, (int)count); } @@ -367,8 +394,8 @@ void PF_ambientsound (void) else MSG_WriteByte (&sv.signon, soundnum); - MSG_WriteByte (&sv.signon, vol*255); - MSG_WriteByte (&sv.signon, attenuation*64); + MSG_WriteByte (&sv.signon, (int)(vol*255)); + MSG_WriteByte (&sv.signon, (int)(attenuation*64)); } @@ -396,19 +423,28 @@ void PF_sound (void) float attenuation; entity = PRVM_G_EDICT(OFS_PARM0); - channel = PRVM_G_FLOAT(OFS_PARM1); + channel = (int)PRVM_G_FLOAT(OFS_PARM1); sample = PRVM_G_STRING(OFS_PARM2); - volume = PRVM_G_FLOAT(OFS_PARM3) * 255; + volume = (int)(PRVM_G_FLOAT(OFS_PARM3) * 255); attenuation = PRVM_G_FLOAT(OFS_PARM4); if (volume < 0 || volume > 255) - PF_WARNING("SV_StartSound: volume must be in range 0-1\n"); + { + VM_Warning("SV_StartSound: volume must be in range 0-1\n"); + return; + } if (attenuation < 0 || attenuation > 4) - PF_WARNING("SV_StartSound: attenuation must be in range 0-4\n"); + { + VM_Warning("SV_StartSound: attenuation must be in range 0-4\n"); + return; + } if (channel < 0 || channel > 7) - PF_WARNING("SV_StartSound: channel must be in range 0-7\n"); + { + VM_Warning("SV_StartSound: channel must be in range 0-7\n"); + return; + } SV_StartSound (entity, channel, sample, volume, attenuation); } @@ -430,14 +466,18 @@ void PF_traceline (void) trace_t trace; int move; prvm_edict_t *ent; + prvm_eval_t *val; prog->xfunction->builtinsprofile += 30; v1 = PRVM_G_VECTOR(OFS_PARM0); v2 = PRVM_G_VECTOR(OFS_PARM1); - move = PRVM_G_FLOAT(OFS_PARM2); + move = (int)PRVM_G_FLOAT(OFS_PARM2); ent = PRVM_G_EDICT(OFS_PARM3); + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2])) + PRVM_ERROR("%s: NAN errors detected in traceline('%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent)); + trace = SV_Move (v1, vec3_origin, vec3_origin, v2, move, ent); prog->globals.server->trace_allsolid = trace.allsolid; @@ -452,7 +492,19 @@ void PF_traceline (void) prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); else prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); - // FIXME: add trace_endcontents + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents))) + val->_float = trace.startsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents))) + val->_float = trace.hitsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags))) + val->_float = trace.hitq3surfaceflags; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename))) + { + if (trace.hittexture) + val->string = PRVM_SetTempString(trace.hittexture->name); + else + val->string = 0; + } } @@ -474,6 +526,7 @@ void PF_tracebox (void) trace_t trace; int move; prvm_edict_t *ent; + prvm_eval_t *val; prog->xfunction->builtinsprofile += 30; @@ -481,9 +534,12 @@ void PF_tracebox (void) m1 = PRVM_G_VECTOR(OFS_PARM1); m2 = PRVM_G_VECTOR(OFS_PARM2); v2 = PRVM_G_VECTOR(OFS_PARM3); - move = PRVM_G_FLOAT(OFS_PARM4); + move = (int)PRVM_G_FLOAT(OFS_PARM4); ent = PRVM_G_EDICT(OFS_PARM5); + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v1[2]) || IS_NAN(v2[2])) + PRVM_ERROR("%s: NAN errors detected in tracebox('%f %f %f', '%f %f %f', '%f %f %f', '%f %f %f', %i, entity %i)\n", PRVM_NAME, v1[0], v1[1], v1[2], m1[0], m1[1], m1[2], m2[0], m2[1], m2[2], v2[0], v2[1], v2[2], move, PRVM_EDICT_TO_PROG(ent)); + trace = SV_Move (v1, m1, m2, v2, move, ent); prog->globals.server->trace_allsolid = trace.allsolid; @@ -498,6 +554,19 @@ void PF_tracebox (void) prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); else prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents))) + val->_float = trace.startsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents))) + val->_float = trace.hitsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags))) + val->_float = trace.hitq3surfaceflags; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename))) + { + if (trace.hittexture) + val->string = PRVM_SetTempString(trace.hittexture->name); + else + val->string = 0; + } } extern trace_t SV_Trace_Toss (prvm_edict_t *ent, prvm_edict_t *ignore); @@ -506,12 +575,16 @@ void PF_tracetoss (void) trace_t trace; prvm_edict_t *ent; prvm_edict_t *ignore; + prvm_eval_t *val; prog->xfunction->builtinsprofile += 600; ent = PRVM_G_EDICT(OFS_PARM0); if (ent == prog->edicts) - PF_WARNING("tracetoss: can not use world entity\n"); + { + VM_Warning("tracetoss: can not use world entity\n"); + return; + } ignore = PRVM_G_EDICT(OFS_PARM1); trace = SV_Trace_Toss (ent, ignore); @@ -528,6 +601,19 @@ void PF_tracetoss (void) prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); else prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dpstartcontents))) + val->_float = trace.startsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitcontents))) + val->_float = trace.hitsupercontents; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphitq3surfaceflags))) + val->_float = trace.hitq3surfaceflags; + if ((val = PRVM_GETGLOBALFIELDVALUE(gval_trace_dphittexturename))) + { + if (trace.hittexture) + val->string = PRVM_SetTempString(trace.hittexture->name); + else + val->string = 0; + } } @@ -548,7 +634,7 @@ void PF_checkpos (void) //============================================================================ int checkpvsbytes; -qbyte checkpvs[MAX_MAP_LEAFS/8]; +unsigned char checkpvs[MAX_MAP_LEAFS/8]; int PF_newcheckclient (int check) { @@ -649,26 +735,27 @@ PF_stuffcmd Sends text over to the client's execution buffer -stuffcmd (clientent, value) +stuffcmd (clientent, value, ...) ================= */ void PF_stuffcmd (void) { int entnum; - const char *str; client_t *old; + char string[VM_STRINGTEMP_LENGTH]; entnum = PRVM_G_EDICTNUM(OFS_PARM0); if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active) { - Con_Print("Can't stuffcmd to a non-client\n"); + VM_Warning("Can't stuffcmd to a non-client\n"); return; } - str = PRVM_G_STRING(OFS_PARM1); + + VM_VarString(1, string, sizeof(string)); old = host_client; host_client = svs.clients + entnum-1; - Host_ClientCommands ("%s", str); + Host_ClientCommands ("%s", string); host_client = old; } @@ -739,63 +826,6 @@ void PF_findradius (void) VM_RETURN_EDICT(chain); } -// LordHavoc: search for flags in float fields -void PF_findflags (void) -{ - int e; - int f; - int s; - prvm_edict_t *ed; - - e = PRVM_G_EDICTNUM(OFS_PARM0); - f = PRVM_G_INT(OFS_PARM1); - s = (int)PRVM_G_FLOAT(OFS_PARM2); - - for (e++ ; e < prog->num_edicts ; e++) - { - prog->xfunction->builtinsprofile++; - ed = PRVM_EDICT_NUM(e); - if (ed->priv.server->free) - continue; - if ((int)PRVM_E_FLOAT(ed,f) & s) - { - VM_RETURN_EDICT(ed); - return; - } - } - - VM_RETURN_EDICT(prog->edicts); -} - -// LordHavoc: chained search for flags in float fields -void PF_findchainflags (void) -{ - int i; - int f; - int s; - prvm_edict_t *ent, *chain; - - chain = (prvm_edict_t *)prog->edicts; - - f = PRVM_G_INT(OFS_PARM0); - s = (int)PRVM_G_FLOAT(OFS_PARM1); - - ent = PRVM_NEXT_EDICT(prog->edicts); - for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) - { - prog->xfunction->builtinsprofile++; - if (ent->priv.server->free) - continue; - if (!((int)PRVM_E_FLOAT(ent,f) & s)) - continue; - - ent->fields.server->chain = PRVM_EDICT_TO_PROG(chain); - chain = ent; - } - - VM_RETURN_EDICT(chain); -} - void PF_precache_file (void) { // precache_file is only used to copy files with qcc, it does nothing PRVM_G_INT(OFS_RETURN) = PRVM_G_INT(OFS_PARM0); @@ -834,9 +864,15 @@ void PF_walkmove (void) ent = PRVM_PROG_TO_EDICT(prog->globals.server->self); if (ent == prog->edicts) - PF_WARNING("walkmove: can not modify world entity\n"); + { + VM_Warning("walkmove: can not modify world entity\n"); + return; + } if (ent->priv.server->free) - PF_WARNING("walkmove: can not modify free entity\n"); + { + VM_Warning("walkmove: can not modify free entity\n"); + return; + } yaw = PRVM_G_FLOAT(OFS_PARM0); dist = PRVM_G_FLOAT(OFS_PARM1); @@ -879,18 +915,25 @@ void PF_droptofloor (void) ent = PRVM_PROG_TO_EDICT(prog->globals.server->self); if (ent == prog->edicts) - PF_WARNING("droptofloor: can not modify world entity\n"); + { + VM_Warning("droptofloor: can not modify world entity\n"); + return; + } if (ent->priv.server->free) - PF_WARNING("droptofloor: can not modify free entity\n"); + { + VM_Warning("droptofloor: can not modify free entity\n"); + return; + } VectorCopy (ent->fields.server->origin, end); end[2] -= 256; trace = SV_Move (ent->fields.server->origin, ent->fields.server->mins, ent->fields.server->maxs, end, MOVE_NORMAL, ent); - if (trace.fraction != 1) + if (trace.fraction != 1 || (trace.startsolid && sv_gameplayfix_droptofloorstartsolid.integer)) { - VectorCopy (trace.endpos, ent->fields.server->origin); + if (trace.fraction < 1) + VectorCopy (trace.endpos, ent->fields.server->origin); SV_LinkEdict (ent, false); ent->fields.server->flags = (int)ent->fields.server->flags | FL_ONGROUND; ent->fields.server->groundentity = PRVM_EDICT_TO_PROG(trace.ent); @@ -914,9 +957,13 @@ void PF_lightstyle (void) client_t *client; int j; - style = PRVM_G_FLOAT(OFS_PARM0); + style = (int)PRVM_G_FLOAT(OFS_PARM0); val = PRVM_G_STRING(OFS_PARM1); + if( (unsigned) style >= MAX_LIGHTSTYLES ) { + PRVM_ERROR( "PF_lightstyle: style: %i >= 64", style ); + } + // change the string in sv strlcpy(sv.lightstyles[style], val, sizeof(sv.lightstyles[style])); @@ -926,11 +973,11 @@ void PF_lightstyle (void) for (j = 0, client = svs.clients;j < svs.maxclients;j++, client++) { - if (client->active) + if (client->active && client->netconnection) { - MSG_WriteChar (&client->message, svc_lightstyle); - MSG_WriteChar (&client->message,style); - MSG_WriteString (&client->message, val); + MSG_WriteChar (&client->netconnection->message, svc_lightstyle); + MSG_WriteChar (&client->netconnection->message,style); + MSG_WriteString (&client->netconnection->message, val); } } } @@ -952,7 +999,7 @@ PF_pointcontents */ void PF_pointcontents (void) { - PRVM_G_FLOAT(OFS_RETURN) = SV_PointQ1Contents(PRVM_G_VECTOR(OFS_PARM0)); + PRVM_G_FLOAT(OFS_RETURN) = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_G_VECTOR(OFS_PARM0))); } /* @@ -980,9 +1027,15 @@ void PF_aim (void) ent = PRVM_G_EDICT(OFS_PARM0); if (ent == prog->edicts) - PF_WARNING("aim: can not use world entity\n"); + { + VM_Warning("aim: can not use world entity\n"); + return; + } if (ent->priv.server->free) - PF_WARNING("aim: can not use free entity\n"); + { + VM_Warning("aim: can not use free entity\n"); + return; + } speed = PRVM_G_FLOAT(OFS_PARM1); VectorCopy (ent->fields.server->origin, start); @@ -1060,9 +1113,15 @@ void PF_changeyaw (void) ent = PRVM_PROG_TO_EDICT(prog->globals.server->self); if (ent == prog->edicts) - PF_WARNING("changeyaw: can not modify world entity\n"); + { + VM_Warning("changeyaw: can not modify world entity\n"); + return; + } if (ent->priv.server->free) - PF_WARNING("changeyaw: can not modify free entity\n"); + { + VM_Warning("changeyaw: can not modify free entity\n"); + return; + } current = ANGLEMOD(ent->fields.server->angles[1]); ideal = ent->fields.server->ideal_yaw; speed = ent->fields.server->yaw_speed; @@ -1107,22 +1166,28 @@ void PF_changepitch (void) ent = PRVM_G_EDICT(OFS_PARM0); if (ent == prog->edicts) - PF_WARNING("changepitch: can not modify world entity\n"); + { + VM_Warning("changepitch: can not modify world entity\n"); + return; + } if (ent->priv.server->free) - PF_WARNING("changepitch: can not modify free entity\n"); + { + VM_Warning("changepitch: can not modify free entity\n"); + return; + } current = ANGLEMOD( ent->fields.server->angles[0] ); if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_idealpitch))) ideal = val->_float; else { - PF_WARNING("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch\n"); + VM_Warning("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch\n"); return; } if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_pitch_speed))) speed = val->_float; else { - PF_WARNING("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch\n"); + VM_Warning("PF_changepitch: .float idealpitch and .float pitch_speed must be defined to use changepitch\n"); return; } @@ -1165,14 +1230,16 @@ MESSAGE WRITING #define MSG_ONE 1 // reliable to one (msg_entity) #define MSG_ALL 2 // reliable to all #define MSG_INIT 3 // write to the init string +#define MSG_ENTITY 5 sizebuf_t *WriteDest (void) { int entnum; int dest; prvm_edict_t *ent; + extern sizebuf_t *sv2csqcbuf; - dest = PRVM_G_FLOAT(OFS_PARM0); + dest = (int)PRVM_G_FLOAT(OFS_PARM0); switch (dest) { case MSG_BROADCAST: @@ -1181,19 +1248,24 @@ sizebuf_t *WriteDest (void) case MSG_ONE: ent = PRVM_PROG_TO_EDICT(prog->globals.server->msg_entity); entnum = PRVM_NUM_FOR_EDICT(ent); - if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active) - Host_Error("WriteDest: tried to write to non-client\n"); - return &svs.clients[entnum-1].message; + if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active || !svs.clients[entnum-1].netconnection) + { + VM_Warning ("WriteDest: tried to write to non-client\n"); + return &sv.reliable_datagram; + } + else + return &svs.clients[entnum-1].netconnection->message; + default: + VM_Warning ("WriteDest: bad destination\n"); case MSG_ALL: return &sv.reliable_datagram; case MSG_INIT: return &sv.signon; - default: - Host_Error("WriteDest: bad destination"); - break; + case MSG_ENTITY: + return sv2csqcbuf; } return NULL; @@ -1201,22 +1273,22 @@ sizebuf_t *WriteDest (void) void PF_WriteByte (void) { - MSG_WriteByte (WriteDest(), PRVM_G_FLOAT(OFS_PARM1)); + MSG_WriteByte (WriteDest(), (int)PRVM_G_FLOAT(OFS_PARM1)); } void PF_WriteChar (void) { - MSG_WriteChar (WriteDest(), PRVM_G_FLOAT(OFS_PARM1)); + MSG_WriteChar (WriteDest(), (int)PRVM_G_FLOAT(OFS_PARM1)); } void PF_WriteShort (void) { - MSG_WriteShort (WriteDest(), PRVM_G_FLOAT(OFS_PARM1)); + MSG_WriteShort (WriteDest(), (int)PRVM_G_FLOAT(OFS_PARM1)); } void PF_WriteLong (void) { - MSG_WriteLong (WriteDest(), PRVM_G_FLOAT(OFS_PARM1)); + MSG_WriteLong (WriteDest(), (int)PRVM_G_FLOAT(OFS_PARM1)); } void PF_WriteAngle (void) @@ -1234,6 +1306,11 @@ void PF_WriteString (void) MSG_WriteString (WriteDest(), PRVM_G_STRING(OFS_PARM1)); } +void PF_WriteUnterminatedString (void) +{ + MSG_WriteUnterminatedString (WriteDest(), PRVM_G_STRING(OFS_PARM1)); +} + void PF_WriteEntity (void) { @@ -1249,9 +1326,15 @@ void PF_makestatic (void) ent = PRVM_G_EDICT(OFS_PARM0); if (ent == prog->edicts) - PF_WARNING("makestatic: can not modify world entity\n"); + { + VM_Warning("makestatic: can not modify world entity\n"); + return; + } if (ent->priv.server->free) - PF_WARNING("makestatic: can not modify free entity\n"); + { + VM_Warning("makestatic: can not modify free entity\n"); + return; + } large = false; if (ent->fields.server->modelindex >= 256 || ent->fields.server->frame >= 256) @@ -1260,18 +1343,18 @@ void PF_makestatic (void) if (large) { MSG_WriteByte (&sv.signon,svc_spawnstatic2); - MSG_WriteShort (&sv.signon, ent->fields.server->modelindex); - MSG_WriteShort (&sv.signon, ent->fields.server->frame); + MSG_WriteShort (&sv.signon, (int)ent->fields.server->modelindex); + MSG_WriteShort (&sv.signon, (int)ent->fields.server->frame); } else { MSG_WriteByte (&sv.signon,svc_spawnstatic); - MSG_WriteByte (&sv.signon, ent->fields.server->modelindex); - MSG_WriteByte (&sv.signon, ent->fields.server->frame); + MSG_WriteByte (&sv.signon, (int)ent->fields.server->modelindex); + MSG_WriteByte (&sv.signon, (int)ent->fields.server->frame); } - MSG_WriteByte (&sv.signon, ent->fields.server->colormap); - MSG_WriteByte (&sv.signon, ent->fields.server->skin); + MSG_WriteByte (&sv.signon, (int)ent->fields.server->colormap); + MSG_WriteByte (&sv.signon, (int)ent->fields.server->skin); for (i=0 ; i<3 ; i++) { MSG_WriteCoord(&sv.signon, ent->fields.server->origin[i], sv.protocol); @@ -1348,7 +1431,7 @@ void PF_registercvar (void) // check for overlap with a command if (Cmd_Exists (name)) { - Con_Printf("PF_registercvar: %s is a command\n", name); + VM_Warning("PF_registercvar: %s is a command\n", name); return; } @@ -1357,6 +1440,160 @@ void PF_registercvar (void) PRVM_G_FLOAT(OFS_RETURN) = 1; // success } +typedef struct +{ + unsigned char type; // 1/2/8 or other value if isn't used + int fieldoffset; +}autosentstat_t; + +static autosentstat_t *vm_autosentstats = NULL; //[515]: it starts from 0, not 32 +static int vm_autosentstats_last; + +void VM_AutoSentStats_Clear (void) +{ + if(vm_autosentstats) + { + Z_Free(vm_autosentstats); + vm_autosentstats = NULL; + vm_autosentstats_last = -1; + } +} + +//[515]: add check if even bigger ? "try to use two stats, cause it's too big" ? +#define VM_SENDSTAT(a,b,c)\ +{\ +/* if((c))*/\ + if((c)==(unsigned char)(c))\ + {\ + MSG_WriteByte((a), svc_updatestatubyte);\ + MSG_WriteByte((a), (b));\ + MSG_WriteByte((a), (c));\ + }\ + else\ + {\ + MSG_WriteByte((a), svc_updatestat);\ + MSG_WriteByte((a), (b));\ + MSG_WriteLong((a), (c));\ + }\ +}\ + +void VM_SV_WriteAutoSentStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats) +{ + int i, v, *si; + char s[17]; + const char *t; + qboolean send; + union + { + float f; + int i; + }k; + + if(!vm_autosentstats) + return; + + send = (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5); + + for(i=0; i= (MAX_CL_STATS-32)) + { + VM_Warning("PF_SV_AddStat: index >= MAX_CL_STATS\n"); + return; + } + if(i > (MAX_CL_STATS-32-4) && type == 1) + { + VM_Warning("PF_SV_AddStat: index > (MAX_CL_STATS-4) with string\n"); + return; + } + vm_autosentstats[i].type = type; + vm_autosentstats[i].fieldoffset = off; + if(vm_autosentstats_last < i) + vm_autosentstats_last = i; +} + /* ================= PF_copyentity @@ -1371,14 +1608,26 @@ void PF_copyentity (void) prvm_edict_t *in, *out; in = PRVM_G_EDICT(OFS_PARM0); if (in == prog->edicts) - PF_WARNING("copyentity: can not read world entity\n"); + { + VM_Warning("copyentity: can not read world entity\n"); + return; + } if (in->priv.server->free) - PF_WARNING("copyentity: can not read free entity\n"); + { + VM_Warning("copyentity: can not read free entity\n"); + return; + } out = PRVM_G_EDICT(OFS_PARM1); if (out == prog->edicts) - PF_WARNING("copyentity: can not modify world entity\n"); + { + VM_Warning("copyentity: can not modify world entity\n"); + return; + } if (out->priv.server->free) - PF_WARNING("copyentity: can not modify free entity\n"); + { + VM_Warning("copyentity: can not modify free entity\n"); + return; + } memcpy(out->fields.server, in->fields.server, prog->progs->entityfields * 4); } @@ -1399,7 +1648,7 @@ void PF_setcolor (void) prvm_eval_t *val; entnum = PRVM_G_EDICTNUM(OFS_PARM0); - i = PRVM_G_FLOAT(OFS_PARM1); + i = (int)PRVM_G_FLOAT(OFS_PARM1); if (entnum < 1 || entnum > svs.maxclients || !svs.clients[entnum-1].active) { @@ -1437,13 +1686,32 @@ void PF_effect (void) int i; const char *s; s = PRVM_G_STRING(OFS_PARM1); - if (!s || !s[0]) - PF_WARNING("effect: no model specified\n"); + if (!s[0]) + { + VM_Warning("effect: no model specified\n"); + return; + } i = SV_ModelIndex(s, 1); if (!i) - PF_WARNING("effect: model not precached\n"); - SV_StartEffect(PRVM_G_VECTOR(OFS_PARM0), i, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3), PRVM_G_FLOAT(OFS_PARM4)); + { + VM_Warning("effect: model not precached\n"); + return; + } + + if (PRVM_G_FLOAT(OFS_PARM3) < 1) + { + VM_Warning("effect: framecount < 1\n"); + return; + } + + if (PRVM_G_FLOAT(OFS_PARM4) < 1) + { + VM_Warning("effect: framerate < 1\n"); + return; + } + + SV_StartEffect(PRVM_G_VECTOR(OFS_PARM0), i, (int)PRVM_G_FLOAT(OFS_PARM2), (int)PRVM_G_FLOAT(OFS_PARM3), (int)PRVM_G_FLOAT(OFS_PARM4)); } void PF_te_blood (void) @@ -1481,7 +1749,7 @@ void PF_te_bloodshower (void) // speed MSG_WriteCoord(&sv.datagram, PRVM_G_FLOAT(OFS_PARM2), sv.protocol); // count - MSG_WriteShort(&sv.datagram, bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); + MSG_WriteShort(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); } void PF_te_explosionrgb (void) @@ -1517,9 +1785,9 @@ void PF_te_particlecube (void) MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[1], sv.protocol); MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[2], sv.protocol); // count - MSG_WriteShort(&sv.datagram, bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); + MSG_WriteShort(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); // color - MSG_WriteByte(&sv.datagram, PRVM_G_FLOAT(OFS_PARM4)); + MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM4)); // gravity true/false MSG_WriteByte(&sv.datagram, ((int) PRVM_G_FLOAT(OFS_PARM5)) != 0); // randomvel @@ -1545,9 +1813,9 @@ void PF_te_particlerain (void) MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[1], sv.protocol); MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[2], sv.protocol); // count - MSG_WriteShort(&sv.datagram, bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); + MSG_WriteShort(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); // color - MSG_WriteByte(&sv.datagram, PRVM_G_FLOAT(OFS_PARM4)); + MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM4)); } void PF_te_particlesnow (void) @@ -1569,9 +1837,9 @@ void PF_te_particlesnow (void) MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[1], sv.protocol); MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM2)[2], sv.protocol); // count - MSG_WriteShort(&sv.datagram, bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); + MSG_WriteShort(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM3), 65535)); // color - MSG_WriteByte(&sv.datagram, PRVM_G_FLOAT(OFS_PARM4)); + MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM4)); } void PF_te_spark (void) @@ -1653,13 +1921,13 @@ void PF_te_customflash (void) MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol); MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol); // radius - MSG_WriteByte(&sv.datagram, bound(0, PRVM_G_FLOAT(OFS_PARM1) / 8 - 1, 255)); + MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM1) / 8 - 1, 255)); // lifetime - MSG_WriteByte(&sv.datagram, bound(0, PRVM_G_FLOAT(OFS_PARM2) * 256 - 1, 255)); + MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_FLOAT(OFS_PARM2) * 256 - 1, 255)); // color - MSG_WriteByte(&sv.datagram, bound(0, PRVM_G_VECTOR(OFS_PARM3)[0] * 255, 255)); - MSG_WriteByte(&sv.datagram, bound(0, PRVM_G_VECTOR(OFS_PARM3)[1] * 255, 255)); - MSG_WriteByte(&sv.datagram, bound(0, PRVM_G_VECTOR(OFS_PARM3)[2] * 255, 255)); + MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_VECTOR(OFS_PARM3)[0] * 255, 255)); + MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_VECTOR(OFS_PARM3)[1] * 255, 255)); + MSG_WriteByte(&sv.datagram, (int)bound(0, PRVM_G_VECTOR(OFS_PARM3)[2] * 255, 255)); } void PF_te_gunshot (void) @@ -1761,8 +2029,8 @@ void PF_te_explosion2 (void) MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol); MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol); // color - MSG_WriteByte(&sv.datagram, PRVM_G_FLOAT(OFS_PARM1)); - MSG_WriteByte(&sv.datagram, PRVM_G_FLOAT(OFS_PARM2)); + MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM1)); + MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM2)); } void PF_te_lightning1 (void) @@ -1838,20 +2106,36 @@ void PF_te_plasmaburn (void) MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol); } -static void clippointtosurface(msurface_t *surface, vec3_t p, vec3_t out) +void PF_te_flamejet (void) +{ + MSG_WriteByte(&sv.datagram, svc_temp_entity); + MSG_WriteByte(&sv.datagram, TE_FLAMEJET); + // org + MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[0], sv.protocol); + MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[1], sv.protocol); + MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM0)[2], sv.protocol); + // vel + MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM1)[0], sv.protocol); + MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM1)[1], sv.protocol); + MSG_WriteCoord(&sv.datagram, PRVM_G_VECTOR(OFS_PARM1)[2], sv.protocol); + // count + MSG_WriteByte(&sv.datagram, (int)PRVM_G_FLOAT(OFS_PARM2)); +} + +void clippointtosurface(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; bestdist = 1000000000; VectorCopy(p, out); - for (i = 0, e = (surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle);i < surface->num_triangles;i++, e += 3) + 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] = surface->groupmesh->data_vertex3f + e[0] * 3; - v[1] = surface->groupmesh->data_vertex3f + e[1] * 3; - v[2] = surface->groupmesh->data_vertex3f + e[2] * 3; + v[0] = model->surfmesh.data_vertex3f + e[0] * 3; + v[1] = model->surfmesh.data_vertex3f + e[1] * 3; + v[2] = model->surfmesh.data_vertex3f + e[2] * 3; TriangleNormal(v[0], v[1], v[2], facenormal); VectorNormalize(facenormal); offsetdist = DotProduct(v[0], facenormal) - DotProduct(p, facenormal); @@ -1874,16 +2158,19 @@ static void clippointtosurface(msurface_t *surface, vec3_t p, vec3_t out) } } -static msurface_t *getsurface(prvm_edict_t *ed, int surfacenum) +static model_t *getmodel(prvm_edict_t *ed) { int modelindex; - model_t *model; if (!ed || ed->priv.server->free) return NULL; - modelindex = ed->fields.server->modelindex; + modelindex = (int)ed->fields.server->modelindex; if (modelindex < 1 || modelindex >= MAX_MODELS) return NULL; - model = sv.models[modelindex]; + return sv.models[modelindex]; +} + +static msurface_t *getsurface(model_t *model, int surfacenum) +{ if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces) return NULL; return model->data_surfaces + surfacenum + model->firstmodelsurface; @@ -1893,9 +2180,10 @@ static msurface_t *getsurface(prvm_edict_t *ed, int surfacenum) //PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints = #434; void PF_getsurfacenumpoints(void) { + model_t *model; msurface_t *surface; // return 0 if no such surface - if (!(surface = getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)))) + if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) { PRVM_G_FLOAT(OFS_RETURN) = 0; return; @@ -1908,50 +2196,51 @@ void PF_getsurfacenumpoints(void) void PF_getsurfacepoint(void) { prvm_edict_t *ed; + model_t *model; msurface_t *surface; int pointnum; VectorClear(PRVM_G_VECTOR(OFS_RETURN)); ed = PRVM_G_EDICT(OFS_PARM0); - if (!ed || ed->priv.server->free) - return; - if (!(surface = getsurface(ed, PRVM_G_FLOAT(OFS_PARM1)))) + if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) return; // note: this (incorrectly) assumes it is a simple polygon - pointnum = PRVM_G_FLOAT(OFS_PARM2); + pointnum = (int)PRVM_G_FLOAT(OFS_PARM2); if (pointnum < 0 || pointnum >= surface->num_vertices) return; // FIXME: implement rotation/scaling - VectorAdd(&(surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.server->origin, PRVM_G_VECTOR(OFS_RETURN)); + VectorAdd(&(model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex)[pointnum * 3], ed->fields.server->origin, PRVM_G_VECTOR(OFS_RETURN)); } //PF_getsurfacenormal, // #436 vector(entity e, float s) getsurfacenormal = #436; void PF_getsurfacenormal(void) { + model_t *model; msurface_t *surface; vec3_t normal; VectorClear(PRVM_G_VECTOR(OFS_RETURN)); - if (!(surface = getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)))) + if (!(model = getmodel(PRVM_G_EDICT(OFS_PARM0))) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) return; // FIXME: implement rotation/scaling // note: this (incorrectly) assumes it is a simple polygon // note: this only returns the first triangle, so it doesn't work very // well for curved surfaces or arbitrary meshes - TriangleNormal((surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex), (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + 3, (surface->groupmesh->data_vertex3f + 3 * surface->num_firstvertex) + 6, normal); + TriangleNormal((model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex), (model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + 3, (model->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + 6, normal); VectorNormalize(normal); VectorCopy(normal, PRVM_G_VECTOR(OFS_RETURN)); } //PF_getsurfacetexture, // #437 string(entity e, float s) getsurfacetexture = #437; void PF_getsurfacetexture(void) { + model_t *model; msurface_t *surface; - PRVM_G_INT(OFS_RETURN) = 0; - if (!(surface = getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)))) + 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_SetEngineString(surface->texture->name); + PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(surface->texture->name); } //PF_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint = #438; void PF_getsurfacenearpoint(void) { - int surfacenum, best, modelindex; + int surfacenum, best; vec3_t clipped, p; vec_t dist, bestdist; prvm_edict_t *ed; @@ -1964,11 +2253,8 @@ void PF_getsurfacenearpoint(void) if (!ed || ed->priv.server->free) return; - modelindex = ed->fields.server->modelindex; - if (modelindex < 1 || modelindex >= MAX_MODELS) - return; - model = sv.models[modelindex]; - if (!model->num_surfaces) + model = getmodel(ed); + if (!model || !model->num_surfaces) return; // FIXME: implement rotation/scaling @@ -1986,7 +2272,7 @@ void PF_getsurfacenearpoint(void) if (dist < bestdist) { // it is, check the nearest point on the actual geometry - clippointtosurface(surface, p, clipped); + clippointtosurface(model, surface, p, clipped); VectorSubtract(clipped, p, clipped); dist += VectorLength2(clipped); if (dist < bestdist) @@ -2003,17 +2289,16 @@ void PF_getsurfacenearpoint(void) void PF_getsurfaceclippedpoint(void) { prvm_edict_t *ed; + model_t *model; msurface_t *surface; vec3_t p, out; VectorClear(PRVM_G_VECTOR(OFS_RETURN)); ed = PRVM_G_EDICT(OFS_PARM0); - if (!ed || ed->priv.server->free) - return; - if (!(surface = getsurface(ed, PRVM_G_FLOAT(OFS_PARM1)))) + if (!(model = getmodel(ed)) || !(surface = getsurface(model, (int)PRVM_G_FLOAT(OFS_PARM1)))) return; // FIXME: implement rotation/scaling VectorSubtract(PRVM_G_VECTOR(OFS_PARM2), ed->fields.server->origin, p); - clippointtosurface(surface, p, out); + clippointtosurface(model, surface, p, out); // FIXME: implement rotation/scaling VectorAdd(out, ed->fields.server->origin, PRVM_G_VECTOR(OFS_RETURN)); } @@ -2050,9 +2335,15 @@ void PF_setattachment (void) model_t *model; if (e == prog->edicts) - PF_WARNING("setattachment: can not modify world entity\n"); + { + VM_Warning("setattachment: can not modify world entity\n"); + return; + } if (e->priv.server->free) - PF_WARNING("setattachment: can not modify free entity\n"); + { + VM_Warning("setattachment: can not modify free entity\n"); + return; + } if (tagentity == NULL) tagentity = prog->edicts; @@ -2069,7 +2360,7 @@ void PF_setattachment (void) modelindex = (int)tagentity->fields.server->modelindex; if (modelindex >= 0 && modelindex < MAX_MODELS && (model = sv.models[modelindex])) { - v->_float = Mod_Alias_GetTagIndexForName(model, tagentity->fields.server->skin, tagname); + v->_float = Mod_Alias_GetTagIndexForName(model, (int)tagentity->fields.server->skin, tagname); if (v->_float == 0) Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i (model \"%s\") but could not find it\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity), model->name); } @@ -2086,14 +2377,45 @@ int SV_GetTagIndex (prvm_edict_t *e, const char *tagname) int i; model_t *model; - i = e->fields.server->modelindex; + i = (int)e->fields.server->modelindex; if (i < 1 || i >= MAX_MODELS) return -1; model = sv.models[i]; - return Mod_Alias_GetTagIndexForName(model, e->fields.server->skin, tagname); + return Mod_Alias_GetTagIndexForName(model, (int)e->fields.server->skin, tagname); }; +void SV_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix) +{ + float scale = PRVM_GETEDICTFIELDVALUE(ent, eval_scale)->_float; + if (scale == 0) + scale = 1; + if (viewmatrix) + Matrix4x4_CreateFromQuakeEntity(out, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + ent->fields.server->view_ofs[2], ent->fields.server->v_angle[0], ent->fields.server->v_angle[1], ent->fields.server->v_angle[2], scale); + else + Matrix4x4_CreateFromQuakeEntity(out, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2], -ent->fields.server->angles[0], ent->fields.server->angles[1], ent->fields.server->angles[2], scale * cl_viewmodel_scale.value); +} + +int SV_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out) +{ + int modelindex; + int frame; + model_t *model; + if (tagindex >= 0 + && (modelindex = (int)ent->fields.server->modelindex) >= 1 && modelindex < MAX_MODELS + && (model = sv.models[(int)ent->fields.server->modelindex]) + && model->animscenes) + { + // if model has wrong frame, engine automatically switches to model first frame + frame = (int)ent->fields.server->frame; + if (frame < 0 || frame >= model->numframes) + frame = 0; + return Mod_Alias_GetTagMatrix(model, model->animscenes[frame].firstframe, tagindex, out); + } + *out = identitymatrix; + return 0; +} + // Warnings/errors code: // 0 - normal (everything all-right) // 1 - world entity @@ -2106,13 +2428,13 @@ extern cvar_t cl_bobcycle; extern cvar_t cl_bobup; int SV_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) { + int ret; prvm_eval_t *val; - int modelindex, reqframe, attachloop; + int modelindex, attachloop; matrix4x4_t entitymatrix, tagmatrix, attachmatrix; - prvm_edict_t *attachent; model_t *model; - Matrix4x4_CreateIdentity(out); // warnings and errors return identical matrix + *out = identitymatrix; // warnings and errors return identical matrix if (ent == prog->edicts) return 1; @@ -2125,84 +2447,40 @@ int SV_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) model = sv.models[modelindex]; - if (ent->fields.server->frame >= 0 && ent->fields.server->frame < model->numframes && model->animscenes) - reqframe = model->animscenes[(int)ent->fields.server->frame].firstframe; - else - reqframe = 0; // if model has wrong frame, engine automatically switches to model first frame - - // get initial tag matrix - if (tagindex) + tagmatrix = identitymatrix; + // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity + attachloop = 0; + for (;;) { - int ret = Mod_Alias_GetTagMatrix(model, reqframe, tagindex - 1, &tagmatrix); - if (ret) + if (attachloop >= 256) // prevent runaway looping + return 5; + // apply transformation by child's tagindex on parent entity and then + // by parent entity itself + ret = SV_GetEntityLocalTagMatrix(ent, tagindex - 1, &attachmatrix); + if (ret && attachloop == 0) return ret; - } - else - Matrix4x4_CreateIdentity(&tagmatrix); - - if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_entity)) && val->edict) - { // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity - attachloop = 0; - do + Matrix4x4_Concat(out, &attachmatrix, &tagmatrix); + SV_GetEntityMatrix(ent, &entitymatrix, false); + Matrix4x4_Concat(&tagmatrix, &entitymatrix, out); + // next iteration we process the parent entity + if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_entity)) && val->edict) { - attachent = PRVM_EDICT_NUM(val->edict); // to this it entity our entity is attached - val = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_index); - if (val->_float >= 1 && attachent->fields.server->modelindex >= 1 && attachent->fields.server->modelindex < MAX_MODELS && (model = sv.models[(int)attachent->fields.server->modelindex]) && model->animscenes && attachent->fields.server->frame >= 0 && attachent->fields.server->frame < model->numframes) - Mod_Alias_GetTagMatrix(model, model->animscenes[(int)attachent->fields.server->frame].firstframe, val->_float - 1, &attachmatrix); - else - Matrix4x4_CreateIdentity(&attachmatrix); - - // apply transformation by child entity matrix - val = PRVM_GETEDICTFIELDVALUE(ent, eval_scale); - if (val->_float == 0) - val->_float = 1; - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2], -ent->fields.server->angles[0], ent->fields.server->angles[1], ent->fields.server->angles[2], val->_float); - Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); - out->m[0][3] = entitymatrix.m[0][3] + val->_float*(entitymatrix.m[0][0]*tagmatrix.m[0][3] + entitymatrix.m[0][1]*tagmatrix.m[1][3] + entitymatrix.m[0][2]*tagmatrix.m[2][3]); - out->m[1][3] = entitymatrix.m[1][3] + val->_float*(entitymatrix.m[1][0]*tagmatrix.m[0][3] + entitymatrix.m[1][1]*tagmatrix.m[1][3] + entitymatrix.m[1][2]*tagmatrix.m[2][3]); - out->m[2][3] = entitymatrix.m[2][3] + val->_float*(entitymatrix.m[2][0]*tagmatrix.m[0][3] + entitymatrix.m[2][1]*tagmatrix.m[1][3] + entitymatrix.m[2][2]*tagmatrix.m[2][3]); - Matrix4x4_Copy(&tagmatrix, out); - - // finally transformate by matrix of tag on parent entity - Matrix4x4_Concat(out, &attachmatrix, &tagmatrix); - out->m[0][3] = attachmatrix.m[0][3] + attachmatrix.m[0][0]*tagmatrix.m[0][3] + attachmatrix.m[0][1]*tagmatrix.m[1][3] + attachmatrix.m[0][2]*tagmatrix.m[2][3]; - out->m[1][3] = attachmatrix.m[1][3] + attachmatrix.m[1][0]*tagmatrix.m[0][3] + attachmatrix.m[1][1]*tagmatrix.m[1][3] + attachmatrix.m[1][2]*tagmatrix.m[2][3]; - out->m[2][3] = attachmatrix.m[2][3] + attachmatrix.m[2][0]*tagmatrix.m[0][3] + attachmatrix.m[2][1]*tagmatrix.m[1][3] + attachmatrix.m[2][2]*tagmatrix.m[2][3]; - Matrix4x4_Copy(&tagmatrix, out); - - ent = attachent; - attachloop += 1; - if (attachloop > 255) // prevent runaway looping - return 5; + tagindex = (int)PRVM_GETEDICTFIELDVALUE(ent, eval_tag_index)->_float; + ent = PRVM_EDICT_NUM(val->edict); } - while ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_tag_entity)) && val->edict); + else + break; + attachloop++; } - // normal or RENDER_VIEWMODEL entity (or main parent entity on attach chain) - val = PRVM_GETEDICTFIELDVALUE(ent, eval_scale); - if (val->_float == 0) - val->_float = 1; - // Alias models have inverse pitch, bmodels can't have tags, so don't check for modeltype... - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2], -ent->fields.server->angles[0], ent->fields.server->angles[1], ent->fields.server->angles[2], val->_float); - Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); - out->m[0][3] = entitymatrix.m[0][3] + val->_float*(entitymatrix.m[0][0]*tagmatrix.m[0][3] + entitymatrix.m[0][1]*tagmatrix.m[1][3] + entitymatrix.m[0][2]*tagmatrix.m[2][3]); - out->m[1][3] = entitymatrix.m[1][3] + val->_float*(entitymatrix.m[1][0]*tagmatrix.m[0][3] + entitymatrix.m[1][1]*tagmatrix.m[1][3] + entitymatrix.m[1][2]*tagmatrix.m[2][3]); - out->m[2][3] = entitymatrix.m[2][3] + val->_float*(entitymatrix.m[2][0]*tagmatrix.m[0][3] + entitymatrix.m[2][1]*tagmatrix.m[1][3] + entitymatrix.m[2][2]*tagmatrix.m[2][3]); - + // RENDER_VIEWMODEL magic if ((val = PRVM_GETEDICTFIELDVALUE(ent, eval_viewmodelforclient)) && val->edict) - {// RENDER_VIEWMODEL magic + { Matrix4x4_Copy(&tagmatrix, out); ent = PRVM_EDICT_NUM(val->edict); - val = PRVM_GETEDICTFIELDVALUE(ent, eval_scale); - if (val->_float == 0) - val->_float = 1; - - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.server->origin[0], ent->fields.server->origin[1], ent->fields.server->origin[2] + ent->fields.server->view_ofs[2], ent->fields.server->v_angle[0], ent->fields.server->v_angle[1], ent->fields.server->v_angle[2], val->_float); + SV_GetEntityMatrix(ent, &entitymatrix, true); Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); - out->m[0][3] = entitymatrix.m[0][3] + val->_float*(entitymatrix.m[0][0]*tagmatrix.m[0][3] + entitymatrix.m[0][1]*tagmatrix.m[1][3] + entitymatrix.m[0][2]*tagmatrix.m[2][3]); - out->m[1][3] = entitymatrix.m[1][3] + val->_float*(entitymatrix.m[1][0]*tagmatrix.m[0][3] + entitymatrix.m[1][1]*tagmatrix.m[1][3] + entitymatrix.m[1][2]*tagmatrix.m[2][3]); - out->m[2][3] = entitymatrix.m[2][3] + val->_float*(entitymatrix.m[2][0]*tagmatrix.m[0][3] + entitymatrix.m[2][1]*tagmatrix.m[1][3] + entitymatrix.m[2][2]*tagmatrix.m[2][3]); /* // Cl_bob, ported from rendering code @@ -2223,7 +2501,7 @@ int SV_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) // (don't count Z, or jumping messes it up) bob = sqrt(ent->fields.server->velocity[0]*ent->fields.server->velocity[0] + ent->fields.server->velocity[1]*ent->fields.server->velocity[1])*cl_bob.value; bob = bob*0.3 + bob*0.7*cycle; - out->m[2][3] += bound(-7, bob, 4); + Matrix4x4_AdjustOrigin(out, 0, 0, bound(-7, bob, 4)); } */ } @@ -2239,9 +2517,15 @@ void PF_gettagindex (void) int modelindex, tag_index; if (ent == prog->edicts) - PF_WARNING("gettagindex: can't affect world entity\n"); + { + VM_Warning("gettagindex: can't affect world entity\n"); + return; + } if (ent->priv.server->free) - PF_WARNING("gettagindex: can't affect free entity\n"); + { + VM_Warning("gettagindex: can't affect free entity\n"); + return; + } modelindex = (int)ent->fields.server->modelindex; tag_index = 0; @@ -2270,10 +2554,10 @@ void PF_gettaginfo (void) switch(returncode) { case 1: - PF_WARNING("gettagindex: can't affect world entity\n"); + VM_Warning("gettagindex: can't affect world entity\n"); break; case 2: - PF_WARNING("gettagindex: can't affect free entity\n"); + VM_Warning("gettagindex: can't affect free entity\n"); break; case 3: Con_DPrintf("SV_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e)); @@ -2294,9 +2578,15 @@ void PF_dropclient (void) client_t *oldhostclient; clientnum = PRVM_G_EDICTNUM(OFS_PARM0) - 1; if (clientnum < 0 || clientnum >= svs.maxclients) - PF_WARNING("dropclient: not a client\n"); + { + VM_Warning("dropclient: not a client\n"); + return; + } if (!svs.clients[clientnum].active) - PF_WARNING("dropclient: that client slot is not connected\n"); + { + VM_Warning("dropclient: that client slot is not connected\n"); + return; + } oldhostclient = host_client; host_client = svs.clients + clientnum; SV_DropClient(false); @@ -2316,6 +2606,9 @@ void PF_spawnclient (void) { prog->xfunction->builtinsprofile += 100; SV_ConnectClient (i, NULL); + // this has to be set or else ClientDisconnect won't be called + // we assume the qc will call ClientConnect... + svs.clients[i].clientconnectcalled = true; ed = PRVM_EDICT_NUM(i + 1); break; } @@ -2338,9 +2631,14 @@ void PF_clienttype (void) PRVM_G_FLOAT(OFS_RETURN) = 2; } +void PF_edict_num (void) +{ + VM_RETURN_EDICT(PRVM_EDICT_NUM((int)PRVM_G_FLOAT(OFS_PARM0))); +} + prvm_builtin_t vm_sv_builtins[] = { NULL, // #0 -PF_makevectors, // #1 void(entity e) makevectors +PF_makevectors, // #1 void(vector ang) makevectors PF_setorigin, // #2 void(entity e, vector o) setorigin PF_setmodel, // #3 void(entity e, string m) setmodel PF_setsize, // #4 void(entity e, vector min, vector max) setsize @@ -2460,8 +2758,54 @@ VM_stov, // #117 vector(string) stov (FRIK_FILE) VM_strzone, // #118 string(string s) strzone (FRIK_FILE) VM_strunzone, // #119 void(string s) strunzone (FRIK_FILE) e10, e10, e10, e10, e10, e10, e10, e10, // #120-199 -e10, e10, e10, e10, e10, e10, e10, e10, e10, e10, // #200-299 -e10, e10, e10, e10, e10, e10, e10, e10, e10, e10, // #300-399 +// FTEQW range #200-#299 +NULL, // #200 +NULL, // #201 +NULL, // #202 +NULL, // #203 +NULL, // #204 +NULL, // #205 +NULL, // #206 +NULL, // #207 +NULL, // #208 +NULL, // #209 +NULL, // #210 +NULL, // #211 +NULL, // #212 +NULL, // #213 +NULL, // #214 +NULL, // #215 +NULL, // #216 +NULL, // #217 +VM_bitshift, // #218 float(float number, float quantity) bitshift (EXT_BITSHIFT) +NULL, // #219 +e10, // #220-#229 +e10, // #230-#239 +e10, // #240-#249 +e10, // #250-#259 +e10, // #260-#269 +e10, // #270-#279 +e10, // #280-#289 +e10, // #290-#299 +e10, // #300-309 +e10, // #310-319 +e10, // #320-329 +NULL, // #330 +NULL, // #331 +NULL, // #332 +NULL, // #333 +NULL, // #334 +NULL, // #335 +NULL, // #336 +NULL, // #337 +NULL, // #338 +VM_print, // #339 void(string, ...) print (DP_SV_PRINT) +e10, // #340-349 +e10, // #350-359 +e10, // #360-369 +e10, // #370-379 +e10, // #380-389 +e10, // #390-399 VM_copyentity, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY) PF_setcolor, // #401 void(entity ent, float colors) setcolor (DP_QC_SETCOLOR) VM_findchain, // #402 entity(.string fld, string match) findchain (DP_QC_FINDCHAIN) @@ -2494,7 +2838,7 @@ PF_te_lightning1, // #428 void(entity own, vector start, vector end) te_lightn PF_te_lightning2, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS) PF_te_lightning3, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS) PF_te_beam, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS) -PF_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS) +VM_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS) PF_te_plasmaburn, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN) PF_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE) PF_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE) @@ -2511,18 +2855,39 @@ VM_search_end, // #445 void(float handle) search_end (DP_FS_SEARCH) VM_search_getsize, // #446 float(float handle) search_getsize (DP_FS_SEARCH) VM_search_getfilename, // #447 string(float handle, float num) search_getfilename (DP_FS_SEARCH) VM_cvar_string, // #448 string(string s) cvar_string (DP_QC_CVAR_STRING) -PF_findflags, // #449 entity(entity start, .float fld, float match) findflags (DP_QC_FINDFLAGS) -PF_findchainflags, // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS) +VM_findflags, // #449 entity(entity start, .float fld, float match) findflags (DP_QC_FINDFLAGS) +VM_findchainflags, // #450 entity(.float fld, float match) findchainflags (DP_QC_FINDCHAINFLAGS) PF_gettagindex, // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO) PF_gettaginfo, // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO) PF_dropclient, // #453 void(entity clent) dropclient (DP_SV_DROPCLIENT) PF_spawnclient, // #454 entity() spawnclient (DP_SV_BOTCLIENT) PF_clienttype, // #455 float(entity clent) clienttype (DP_SV_BOTCLIENT) -NULL, // #456 -NULL, // #457 +PF_WriteUnterminatedString, // #456 void(float to, string s) WriteUnterminatedString (DP_SV_WRITEUNTERMINATEDSTRING) +PF_te_flamejet, // #457 void(vector org, vector vel, float howmany) te_flamejet = #457 (DP_TE_FLAMEJET) NULL, // #458 -NULL, // #459 -e10, e10, e10, e10 // #460-499 (LordHavoc) +PF_edict_num, // #459 entity(float num) (??) +VM_buf_create, // #460 float() buf_create (DP_QC_STRINGBUFFERS) +VM_buf_del, // #461 void(float bufhandle) buf_del (DP_QC_STRINGBUFFERS) +VM_buf_getsize, // #462 float(float bufhandle) buf_getsize (DP_QC_STRINGBUFFERS) +VM_buf_copy, // #463 void(float bufhandle_from, float bufhandle_to) buf_copy (DP_QC_STRINGBUFFERS) +VM_buf_sort, // #464 void(float bufhandle, float sortpower, float backward) buf_sort (DP_QC_STRINGBUFFERS) +VM_buf_implode, // #465 string(float bufhandle, string glue) buf_implode (DP_QC_STRINGBUFFERS) +VM_bufstr_get, // #466 string(float bufhandle, float string_index) bufstr_get (DP_QC_STRINGBUFFERS) +VM_bufstr_set, // #467 void(float bufhandle, float string_index, string str) bufstr_set (DP_QC_STRINGBUFFERS) +VM_bufstr_add, // #468 float(float bufhandle, string str, float order) bufstr_add (DP_QC_STRINGBUFFERS) +VM_bufstr_free, // #469 void(float bufhandle, float string_index) bufstr_free (DP_QC_STRINGBUFFERS) +PF_SV_AddStat, // #470 void(float index, float type, .void field) SV_AddStat (EXT_CSQC) +VM_asin, // #471 float(float s) VM_asin (DP_QC_ASINACOSATANATAN2TAN) +VM_acos, // #472 float(float c) VM_acos (DP_QC_ASINACOSATANATAN2TAN) +VM_atan, // #473 float(float t) VM_atan (DP_QC_ASINACOSATANATAN2TAN) +VM_atan2, // #474 float(float c, float s) VM_atan2 (DP_QC_ASINACOSATANATAN2TAN) +VM_tan, // #475 float(float a) VM_tan (DP_QC_ASINACOSATANATAN2TAN) +VM_strlennocol, // #476 float(string s) : DRESK - String Length (not counting color codes) (DP_QC_STRINGCOLORFUNCTIONS) +VM_strdecolorize, // #477 string(string s) : DRESK - Decolorized String (DP_SV_STRINGCOLORFUNCTIONS) +VM_strftime, // #478 string(float uselocaltime, string format, ...) (DP_QC_STRFTIME) +NULL, // #478 +NULL, // #479 +e10, e10 // #480-499 (LordHavoc) }; const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t);