X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=clvm_cmds.c;h=434152284490572f0ba73ac5407cdd1ce972296f;hb=2b85979b17195cd31b35737b4bc0e5947d7c2711;hp=f0faa31fe659f916f7917d3ea8eeced913e95302;hpb=46d1a71fd6c4ec6c32bde524f68d496bb00a97e5;p=xonotic%2Fdarkplaces.git diff --git a/clvm_cmds.c b/clvm_cmds.c index f0faa31f..43415228 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -241,7 +241,7 @@ void CL_VM_SetTraceGlobals(const trace_t *trace, int svent) #define CL_HitNetworkBrushModels(move) !((move) == MOVE_WORLDONLY) #define CL_HitNetworkPlayers(move) !((move) == MOVE_WORLDONLY || (move) == MOVE_NOMONSTERS) -// #16 float(vector v1, vector v2, float movetype, entity ignore) traceline +// #16 void(vector v1, vector v2, float movetype, entity ignore) traceline static void VM_CL_traceline (void) { float *v1, *v2; @@ -258,7 +258,7 @@ static void VM_CL_traceline (void) 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])) + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || 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 = CL_Move(v1, vec3_origin, vec3_origin, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true); @@ -296,7 +296,7 @@ static void VM_CL_tracebox (void) 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])) + if (IS_NAN(v1[0]) || IS_NAN(v1[1]) || IS_NAN(v1[2]) || IS_NAN(v2[0]) || IS_NAN(v2[1]) || 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 = CL_Move(v1, m1, m2, v2, move, ent, CL_GenericHitSuperContentsMask(ent), CL_HitNetworkBrushModels(move), CL_HitNetworkPlayers(move), &svent, true); @@ -393,7 +393,7 @@ void VM_CL_precache_model (void) } } PRVM_G_FLOAT(OFS_RETURN) = 0; - m = Mod_ForName(name, false, false, false); + m = Mod_ForName(name, false, false, name[0] == '*' ? cl.model_name[1] : NULL); if(m && m->loaded) { for (i = 0;i < MAX_MODELS;i++) @@ -435,8 +435,16 @@ static void VM_CL_findradius (void) vec3_t org, eorg, mins, maxs; int i, numtouchedicts; prvm_edict_t *touchedicts[MAX_EDICTS]; + int chainfield; - VM_SAFEPARMCOUNT(2, VM_CL_findradius); + VM_SAFEPARMCOUNTRANGE(2, 3, VM_CL_findradius); + + if(prog->argc == 3) + chainfield = PRVM_G_INT(OFS_PARM2); + else + chainfield = prog->fieldoffsets.chain; + if(chainfield < 0) + PRVM_ERROR("VM_findchain: %s doesnt have the specified chain field !", PRVM_NAME); chain = (prvm_edict_t *)prog->edicts; @@ -478,7 +486,7 @@ static void VM_CL_findradius (void) VectorMAMAM(1, eorg, -0.5f, ent->fields.client->mins, -0.5f, ent->fields.client->maxs, eorg); if (DotProduct(eorg, eorg) < radius2) { - ent->fields.client->chain = PRVM_EDICT_TO_PROG(chain); + PRVM_EDICTFIELDVALUE(ent, chainfield)->edict = PRVM_EDICT_TO_PROG(chain); chain = ent; } } @@ -932,8 +940,11 @@ static void VM_CL_unproject (void) VM_SAFEPARMCOUNT(1, VM_CL_unproject); f = PRVM_G_VECTOR(OFS_PARM0); if(v_flipped.integer) - f[0] = r_refdef.view.x + r_refdef.view.width - f[0]; - VectorSet(temp, f[2], (-1.0 + 2.0 * (f[0] - r_refdef.view.x)) / r_refdef.view.width * f[2] * -r_refdef.view.frustum_x, (-1.0 + 2.0 * (f[1] - r_refdef.view.y)) / r_refdef.view.height * f[2] * -r_refdef.view.frustum_y); + f[0] = (2 * r_refdef.view.x + r_refdef.view.width) * (vid_conwidth.integer / (float) vid.width) - f[0]; + VectorSet(temp, + f[2], + (-1.0 + 2.0 * (f[0] / (vid_conwidth.integer / (float) vid.width) - r_refdef.view.x) / r_refdef.view.width) * f[2] * -r_refdef.view.frustum_x, + (-1.0 + 2.0 * (f[1] / (vid_conheight.integer / (float) vid.height) - r_refdef.view.y) / r_refdef.view.height) * f[2] * -r_refdef.view.frustum_y); Matrix4x4_Transform(&r_refdef.view.matrix, temp, PRVM_G_VECTOR(OFS_RETURN)); } @@ -950,7 +961,10 @@ static void VM_CL_project (void) Matrix4x4_Transform(&m, f, v); if(v_flipped.integer) v[1] = -v[1]; - VectorSet(PRVM_G_VECTOR(OFS_RETURN), r_refdef.view.x + r_refdef.view.width*0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x), r_refdef.view.y + r_refdef.view.height*0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y), v[0]); + VectorSet(PRVM_G_VECTOR(OFS_RETURN), + (vid_conwidth.integer / (float) vid.width) * (r_refdef.view.x + r_refdef.view.width*0.5*(1.0+v[1]/v[0]/-r_refdef.view.frustum_x)), + (vid_conheight.integer / (float) vid.height) * (r_refdef.view.y + r_refdef.view.height*0.5*(1.0+v[2]/v[0]/-r_refdef.view.frustum_y)), + v[0]); } //#330 float(float stnum) getstatf (EXT_CSQC) @@ -1151,6 +1165,7 @@ static void VM_CL_getinputstate (void) int i, frame; VM_SAFEPARMCOUNT(1, VM_CL_getinputstate); frame = (int)PRVM_G_FLOAT(OFS_PARM0); + PRVM_G_FLOAT(OFS_RETURN) = false; for (i = 0;i < CL_MAX_USERCMDS;i++) { if (cl.movecmd[i].sequence == frame) @@ -1171,6 +1186,7 @@ static void VM_CL_getinputstate (void) VectorCopy(cl.playerstandmins, prog->globals.client->pmove_mins); VectorCopy(cl.playerstandmaxs, prog->globals.client->pmove_maxs); } + PRVM_G_FLOAT(OFS_RETURN) = true; } } } @@ -1407,10 +1423,10 @@ static void VM_CL_makestatic (void) // copy it to the current state memset(staticent, 0, sizeof(*staticent)); staticent->render.model = CL_GetModelByIndex((int)ent->fields.client->modelindex); - staticent->render.frame1 = staticent->render.frame2 = (int)ent->fields.client->frame; - staticent->render.framelerp = 0; + staticent->render.framegroupblend[0].frame = (int)ent->fields.client->frame; + staticent->render.framegroupblend[0].lerp = 1; // make torchs play out of sync - staticent->render.frame1time = staticent->render.frame2time = lhrandom(-10, -1); + staticent->render.framegroupblend[0].start = lhrandom(-10, -1); staticent->render.skinnum = (int)ent->fields.client->skin; staticent->render.effects = (int)ent->fields.client->effects; staticent->render.alpha = 1; @@ -2165,6 +2181,15 @@ void VM_CL_setattachment (void) ///////////////////////////////////////// // DP_MD3_TAGINFO extension coded by VorteX +int CL_GetTagIndex (prvm_edict_t *e, const char *tagname) +{ + dp_model_t *model = CL_GetModelFromEdict(e); + if (model) + return Mod_Alias_GetTagIndexForName(model, (int)e->fields.client->skin, tagname); + else + return -1; +} + int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, const char **tagname, matrix4x4_t *tag_localmatrix) { int r; @@ -2194,14 +2219,47 @@ int CL_GetExtendedTagInfo (prvm_edict_t *e, int tagindex, int *parentindex, cons return 1; } -int CL_GetTagIndex (prvm_edict_t *e, const char *tagname) +void CL_GetEntityMatrix (prvm_edict_t *ent, matrix4x4_t *out, qboolean viewmatrix) { - dp_model_t *model = CL_GetModelFromEdict(e); - if (model) - return Mod_Alias_GetTagIndexForName(model, (int)e->fields.client->skin, tagname); + prvm_eval_t *val; + float scale; + float pitchsign = 1; + dp_model_t *model; + + scale = 1; + val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); + if (val && val->_float != 0) + scale = val->_float; + + // TODO do we need the same weird angle inverting logic here as in the server side case? + if(viewmatrix) + Matrix4x4_CreateFromQuakeEntity(out, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], scale * cl_viewmodel_scale.value); else - return -1; -}; + { + if ((model = CL_GetModelFromEdict(ent)) && model->type == mod_alias) + pitchsign = -1; + Matrix4x4_CreateFromQuakeEntity(out, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], pitchsign * ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale); + } +} + + +int CL_GetEntityLocalTagMatrix(prvm_edict_t *ent, int tagindex, matrix4x4_t *out) +{ + int frame; + dp_model_t *model; + if (tagindex >= 0 + && (model = CL_GetModelFromEdict(ent)) + && model->animscenes) + { + // if model has wrong frame, engine automatically switches to model first frame + frame = (int)ent->fields.client->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) @@ -2215,12 +2273,11 @@ extern cvar_t cl_bobcycle; extern cvar_t cl_bobup; int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) { + int ret; prvm_eval_t *val; - int reqframe, attachloop; + int attachloop; matrix4x4_t entitymatrix, tagmatrix, attachmatrix; - prvm_edict_t *attachent; dp_model_t *model; - float scale; *out = identitymatrix; // warnings and errors return identical matrix @@ -2230,81 +2287,40 @@ int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) return 2; model = CL_GetModelFromEdict(ent); - if(!model) return 3; - if (ent->fields.client->frame >= 0 && ent->fields.client->frame < model->numframes && model->animscenes) - reqframe = model->animscenes[(int)ent->fields.client->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; + attachloop = 0; + for(;;) { - int ret = Mod_Alias_GetTagMatrix(model, reqframe, tagindex - 1, &tagmatrix); - if (ret) + if(attachloop >= 256) + return 5; + // apply transformation by child's tagindex on parent entity and then + // by parent entity itself + ret = CL_GetEntityLocalTagMatrix(ent, tagindex - 1, &attachmatrix); + if(ret && attachloop == 0) return ret; - } - else - tagmatrix = identitymatrix; - - if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict) - { // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity - attachloop = 0; - do + CL_GetEntityMatrix(ent, &entitymatrix, false); + Matrix4x4_Concat(&tagmatrix, &attachmatrix, out); + Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); + // next iteration we process the parent entity + if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict) { - attachent = PRVM_EDICT_NUM(val->edict); // to this it entity our entity is attached - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index); - - model = CL_GetModelFromEdict(attachent); - - if (model && val->_float >= 1 && model->animscenes && attachent->fields.client->frame >= 0 && attachent->fields.client->frame < model->numframes) - Mod_Alias_GetTagMatrix(model, model->animscenes[(int)attachent->fields.client->frame].firstframe, (int)val->_float - 1, &attachmatrix); - else - attachmatrix = identitymatrix; - - // apply transformation by child entity matrix - scale = 1; - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); - if (val && val->_float != 0) - scale = val->_float; - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale); - Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); - Matrix4x4_Copy(&tagmatrix, out); - - // finally transformate by matrix of tag on parent entity - Matrix4x4_Concat(out, &attachmatrix, &tagmatrix); - Matrix4x4_Copy(&tagmatrix, out); - - ent = attachent; - attachloop += 1; - if (attachloop > 255) // prevent runaway looping - return 5; + tagindex = (int)PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_index)->_float; + ent = PRVM_EDICT_NUM(val->edict); } - while ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.tag_entity)) && val->edict); + else + break; + attachloop++; } - // normal or RENDER_VIEWMODEL entity (or main parent entity on attach chain) - scale = 1; - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); - if (val && val->_float != 0) - scale = val->_float; - // Alias models have inverse pitch, bmodels can't have tags, so don't check for modeltype... - // FIXME: support RF_USEAXIS - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2], -ent->fields.client->angles[0], ent->fields.client->angles[1], ent->fields.client->angles[2], scale); - Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); - + // RENDER_VIEWMODEL magic if ((val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.renderflags)) && (RF_VIEWMODEL & (int)val->_float)) - {// RENDER_VIEWMODEL magic + { Matrix4x4_Copy(&tagmatrix, out); - scale = 1; - val = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.scale); - if (val && val->_float != 0) - scale = val->_float; - - Matrix4x4_CreateFromQuakeEntity(&entitymatrix, cl.csqc_origin[0], cl.csqc_origin[1], cl.csqc_origin[2], cl.csqc_angles[0], cl.csqc_angles[1], cl.csqc_angles[2], scale); + CL_GetEntityMatrix(prog->edicts, &entitymatrix, true); Matrix4x4_Concat(out, &entitymatrix, &tagmatrix); /* @@ -2379,16 +2395,17 @@ void VM_CL_gettaginfo (void) const char *tagname; int returncode; prvm_eval_t *val; - vec3_t fo, ri, up, trans; + vec3_t fo, le, up, trans; VM_SAFEPARMCOUNT(2, VM_CL_gettaginfo); e = PRVM_G_EDICT(OFS_PARM0); tagindex = (int)PRVM_G_FLOAT(OFS_PARM1); returncode = CL_GetTagMatrix(&tag_matrix, e, tagindex); - Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN)); + Matrix4x4_ToVectors(&tag_matrix, prog->globals.client->v_forward, le, prog->globals.client->v_up, PRVM_G_VECTOR(OFS_RETURN)); + VectorScale(le, -1, prog->globals.client->v_right); CL_GetExtendedTagInfo(e, tagindex, &parentindex, &tagname, &tag_localmatrix); - Matrix4x4_ToVectors(&tag_localmatrix, fo, ri, up, trans); + Matrix4x4_ToVectors(&tag_localmatrix, fo, le, up, trans); if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_parent))) val->_float = parentindex; @@ -2399,7 +2416,7 @@ void VM_CL_gettaginfo (void) if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_forward))) VectorCopy(fo, val->vector); if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_right))) - VectorCopy(ri, val->vector); + VectorScale(le, -1, val->vector); if((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.gettaginfo_up))) VectorCopy(up, val->vector); @@ -3070,6 +3087,75 @@ void VM_CL_serverkey(void) PRVM_G_INT(OFS_RETURN) = PRVM_SetTempString(string); } +/* +================= +VM_CL_checkpvs + +Checks if an entity is in a point's PVS. +Should be fast but can be inexact. + +float checkpvs(vector viewpos, entity viewee) = #240; +================= +*/ +static void VM_CL_checkpvs (void) +{ + vec3_t viewpos; + prvm_edict_t *viewee; + vec3_t mi, ma; +#if 1 + unsigned char *pvs; +#else + static int fatpvsbytes; + static unsigned char fatpvs[MAX_MAP_LEAFS/8]; +#endif + + VM_SAFEPARMCOUNT(2, VM_SV_checkpvs); + VectorCopy(PRVM_G_VECTOR(OFS_PARM0), viewpos); + viewee = PRVM_G_EDICT(OFS_PARM1); + + if(viewee->priv.required->free) + { + VM_Warning("checkpvs: can not check free entity\n"); + PRVM_G_FLOAT(OFS_RETURN) = 4; + return; + } + + VectorAdd(viewee->fields.server->origin, viewee->fields.server->mins, mi); + VectorAdd(viewee->fields.server->origin, viewee->fields.server->maxs, ma); + +#if 1 + if(!sv.worldmodel->brush.GetPVS || !sv.worldmodel->brush.BoxTouchingPVS) + { + // no PVS support on this worldmodel... darn + PRVM_G_FLOAT(OFS_RETURN) = 3; + return; + } + pvs = sv.worldmodel->brush.GetPVS(sv.worldmodel, viewpos); + if(!pvs) + { + // viewpos isn't in any PVS... darn + PRVM_G_FLOAT(OFS_RETURN) = 2; + return; + } + PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, pvs, mi, ma); +#else + // using fat PVS like FTEQW does (slow) + if(!sv.worldmodel->brush.FatPVS || !sv.worldmodel->brush.BoxTouchingPVS) + { + // no PVS support on this worldmodel... darn + PRVM_G_FLOAT(OFS_RETURN) = 3; + return; + } + fatpvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, viewpos, 8, fatpvs, sizeof(fatpvs), false); + if(!fatpvsbytes) + { + // viewpos isn't in any PVS... darn + PRVM_G_FLOAT(OFS_RETURN) = 2; + return; + } + PRVM_G_FLOAT(OFS_RETURN) = sv.worldmodel->brush.BoxTouchingPVS(sv.worldmodel, fatpvs, mi, ma); +#endif +} //============================================================================ // To create a almost working builtin file from this replace: @@ -3096,7 +3182,7 @@ VM_vlen, // #12 float(vector v) vlen (QUAKE) VM_vectoyaw, // #13 float(vector v) vectoyaw (QUAKE) VM_CL_spawn, // #14 entity() spawn (QUAKE) VM_remove, // #15 void(entity e) remove (QUAKE) -VM_CL_traceline, // #16 float(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE) +VM_CL_traceline, // #16 void(vector v1, vector v2, float tryents, entity ignoreentity) traceline (QUAKE) NULL, // #17 entity() checkclient (QUAKE) VM_find, // #18 entity(entity start, .string fld, string match) find (QUAKE) VM_precache_sound, // #19 void(string s) precache_sound (QUAKE) @@ -3322,7 +3408,7 @@ NULL, // #236 NULL, // #237 NULL, // #238 NULL, // #239 -NULL, // #240 +VM_CL_checkpvs, // #240 NULL, // #241 NULL, // #242 NULL, // #243 @@ -3412,7 +3498,7 @@ VM_drawresetcliparea, // #325 void(void) drawresetcliparea VM_drawcolorcodedstring, // #326 float drawcolorcodedstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) (EXT_CSQC) VM_stringwidth, // #327 // FIXME is this okay? VM_drawsubpic, // #328 // FIXME is this okay? -NULL, // #329 +VM_drawrotpic, // #329 // FIXME is this okay? VM_CL_getstatf, // #330 float(float stnum) getstatf (EXT_CSQC) VM_CL_getstati, // #331 float(float stnum) getstati (EXT_CSQC) VM_CL_getstats, // #332 string(float firststnum) getstats (EXT_CSQC) @@ -3603,9 +3689,113 @@ VM_argv_start_index, // #515 float(float idx) argv_start_index = #515; (DP_Q VM_argv_end_index, // #516 float(float idx) argv_end_index = #516; (DP_QC_TOKENIZE_CONSOLE) VM_buf_cvarlist, // #517 void(float buf, string prefix, string antiprefix) buf_cvarlist = #517; (DP_QC_STRINGBUFFERS_CVARLIST) VM_cvar_description, // #518 float(string name) cvar_description = #518; (DP_QC_CVAR_DESCRIPTION) -NULL, // #519 +VM_gettime, // #519 float(float timer) gettime = #519; (DP_QC_GETTIME) VM_keynumtostring, // #520 string keynumtostring(float keynum) VM_findkeysforcommand, // #521 string findkeysforcommand(string command) +NULL, // #522 +NULL, // #523 +NULL, // #524 +NULL, // #525 +NULL, // #526 +NULL, // #527 +NULL, // #528 +NULL, // #529 +NULL, // #530 +NULL, // #531 +NULL, // #532 +NULL, // #533 +NULL, // #534 +NULL, // #535 +NULL, // #536 +NULL, // #537 +NULL, // #538 +NULL, // #539 +NULL, // #540 +NULL, // #541 +NULL, // #542 +NULL, // #543 +NULL, // #544 +NULL, // #545 +NULL, // #546 +NULL, // #547 +NULL, // #548 +NULL, // #549 +NULL, // #550 +NULL, // #551 +NULL, // #552 +NULL, // #553 +NULL, // #554 +NULL, // #555 +NULL, // #556 +NULL, // #557 +NULL, // #558 +NULL, // #559 +NULL, // #560 +NULL, // #561 +NULL, // #562 +NULL, // #563 +NULL, // #564 +NULL, // #565 +NULL, // #566 +NULL, // #567 +NULL, // #568 +NULL, // #569 +NULL, // #570 +NULL, // #571 +NULL, // #572 +NULL, // #573 +NULL, // #574 +NULL, // #575 +NULL, // #576 +NULL, // #577 +NULL, // #578 +NULL, // #579 +NULL, // #580 +NULL, // #581 +NULL, // #582 +NULL, // #583 +NULL, // #584 +NULL, // #585 +NULL, // #586 +NULL, // #587 +NULL, // #588 +NULL, // #589 +NULL, // #590 +NULL, // #591 +NULL, // #592 +NULL, // #593 +NULL, // #594 +NULL, // #595 +NULL, // #596 +NULL, // #597 +NULL, // #598 +NULL, // #599 +NULL, // #600 +NULL, // #601 +NULL, // #602 +NULL, // #603 +NULL, // #604 +NULL, // #605 +NULL, // #606 +NULL, // #607 +NULL, // #608 +NULL, // #609 +NULL, // #610 +NULL, // #611 +NULL, // #612 +NULL, // #613 +NULL, // #614 +NULL, // #615 +NULL, // #616 +NULL, // #617 +NULL, // #618 +NULL, // #619 +NULL, // #620 +NULL, // #621 +NULL, // #622 +NULL, // #623 +VM_getextresponse, // #624 string getextresponse(void) +NULL, // #625 }; const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t);