//======================================================================= // Copyright XashXT Group 2007 © // sv_utils.c - server vm utils //======================================================================= #include "engine.h" #include "server.h" /* ============ SV_CalcBBox Returns the actual bounding box of a bmodel. This is a big improvement over what q2 normally does with rotating bmodels - q2 sets absmin, absmax to a cube that will completely contain the bmodel at *any* rotation on *any* axis, whether the bmodel can actually rotate to that angle or not. This leads to a lot of false block tests in SV_Push if another bmodel is in the vicinity. ============ */ void SV_CalcBBox(edict_t *ent, vec3_t mins, vec3_t maxs) { vec3_t forward, left, up, f1, l1, u1; vec3_t p[8]; int i, j, k, j2, k4; for(k = 0; k < 2; k++) { k4 = k * 4; if(k) p[k4][2] = maxs[2]; else p[k4][2] = mins[2]; p[k4 + 1][2] = p[k4][2]; p[k4 + 2][2] = p[k4][2]; p[k4 + 3][2] = p[k4][2]; for(j = 0; j < 2; j++) { j2 = j * 2; if(j) p[j2+k4][1] = maxs[1]; else p[j2+k4][1] = mins[1]; p[j2 + k4 + 1][1] = p[j2 + k4][1]; for(i = 0; i < 2; i++) { if(i) p[i + j2 + k4][0] = maxs[0]; else p[i + j2 + k4][0] = mins[0]; } } } AngleVectors(ent->progs.sv->angles, forward, left, up); for(i = 0; i < 8; i++) { VectorScale(forward, p[i][0], f1); VectorScale(left, -p[i][1], l1); VectorScale(up, p[i][2], u1); VectorAdd(ent->progs.sv->origin, f1, p[i]); VectorAdd(p[i], l1, p[i]); VectorAdd(p[i], u1, p[i]); } VectorCopy(p[0], ent->progs.sv->mins); VectorCopy(p[0], ent->progs.sv->maxs); for(i = 1; i < 8; i++) { ent->progs.sv->mins[0] = min(ent->progs.sv->mins[0], p[i][0]); ent->progs.sv->mins[1] = min(ent->progs.sv->mins[1], p[i][1]); ent->progs.sv->mins[2] = min(ent->progs.sv->mins[2], p[i][2]); ent->progs.sv->maxs[0] = max(ent->progs.sv->maxs[0], p[i][0]); ent->progs.sv->maxs[1] = max(ent->progs.sv->maxs[1], p[i][1]); ent->progs.sv->maxs[2] = max(ent->progs.sv->maxs[2], p[i][2]); } } void SV_SetMinMaxSize (edict_t *e, float *min, float *max, bool rotate) { int i; for (i = 0; i < 3; i++) if (min[i] > max[i]) PRVM_ERROR("SV_SetMinMaxSize: backwards mins/maxs"); // set derived values if( rotate && e->progs.sv->solid == SOLID_BBOX) { SV_CalcBBox( e, min, max ); } else { VectorCopy (min, e->progs.sv->mins); VectorCopy (max, e->progs.sv->maxs); } VectorSubtract (max, min, e->progs.sv->size ); // TODO: fill also mass and density SV_LinkEdict (e); } void SV_SetModel (edict_t *ent, const char *name) { int i; cmodel_t *mod; i = SV_ModelIndex( name ); ent->progs.sv->model = PRVM_SetEngineString(sv.configstrings[CS_MODELS+i]); ent->progs.sv->modelindex = ent->progs.sv->modelindex = i; mod = CM_LoadModel( i ); if( mod ) SV_SetMinMaxSize( ent, mod->mins, mod->maxs, false ); } float SV_AngleMod( float ideal, float current, float speed ) { float move; if (current == ideal) // already there? return anglemod( current ); move = ideal - current; if (ideal > current) { if (move >= 180) move = move - 360; } else { if (move <= -180) move = move + 360; } if (move > 0) { if (move > speed) move = speed; } else { if (move < -speed) move = -speed; } return anglemod(current + move); } void SV_ConfigString (int index, const char *val) { if (index < 0 || index >= MAX_CONFIGSTRINGS) Host_Error ("configstring: bad index %i value %s\n", index, val); if (!*val) val = ""; // change the string in sv strcpy (sv.configstrings[index], val); if (sv.state != ss_loading) { // send the update to everyone SZ_Clear (&sv.multicast); MSG_Begin(svc_configstring); MSG_WriteShort (&sv.multicast, index); MSG_WriteString (&sv.multicast, (char *)val); MSG_Send(MSG_ALL_R, vec3_origin, NULL ); } } /* =============================================================================== NETWORK MESSAGE WRITING =============================================================================== */ void PF_BeginMessage(void) { int svc_dest = (int)PRVM_G_FLOAT(OFS_PARM0); // some users can send message with engine index // reduce number to avoid overflow problems or cheating svc_dest = bound(svc_bad, svc_dest, svc_nop); MSG_Begin( svc_dest ); } void PF_WriteByte (void){ MSG_WriteByte(&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); } void PF_WriteChar (void){ MSG_WriteChar(&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); } void PF_WriteShort (void){ MSG_WriteShort(&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); } void PF_WriteLong (void){ MSG_WriteLong(&sv.multicast, (int)PRVM_G_FLOAT(OFS_PARM0)); } void PF_WriteFloat (void){ MSG_WriteFloat(&sv.multicast, PRVM_G_FLOAT(OFS_PARM0)); } void PF_WriteAngle (void){ MSG_WriteAngle32(&sv.multicast, PRVM_G_FLOAT(OFS_PARM0)); } void PF_WriteCoord (void){ MSG_WriteCoord32(&sv.multicast, PRVM_G_FLOAT(OFS_PARM0)); } void PF_WriteString (void){ MSG_WriteString(&sv.multicast, PRVM_G_STRING(OFS_PARM0)); } void PF_WriteEntity (void){ MSG_WriteShort(&sv.multicast, PRVM_G_EDICTNUM(OFS_PARM1)); } // entindex void PF_EndMessage (void) { int send_to = (int)PRVM_G_FLOAT(OFS_PARM0); edict_t *ed = PRVM_G_EDICT(OFS_PARM2); if(PRVM_NUM_FOR_EDICT(ed) > prog->num_edicts) { VM_Warning("MsgEnd: sending message from killed entity\n"); return; } // align range send_to = bound(MSG_ONE, send_to, MSG_PVS_R); MSG_Send( send_to, PRVM_G_VECTOR(OFS_PARM1), ed ); } /* ================= PF_sprint single print to a specific client sprint(clientent, value) ================= */ void PF_sprint (void) { client_state_t *client; int num; char string[VM_STRINGTEMP_LENGTH]; num = PRVM_G_EDICTNUM(OFS_PARM0); if (num < 1 || num > maxclients->value || svs.clients[num - 1].state != cs_spawned) { VM_Warning("tried to centerprint to a non-client\n"); return; } client = svs.clients + num-1; VM_VarString(1, string, sizeof(string)); SV_ClientPrintf (client, PRINT_CHAT, "%s", string ); } /* ================= PF_centerprint single print to a specific client centerprint(clientent, value) ================= */ void PF_centerprint (void) { client_state_t *client; int num; char string[VM_STRINGTEMP_LENGTH]; num = PRVM_G_EDICTNUM(OFS_PARM0); if(num < 1 || num > maxclients->value || svs.clients[num-1].state != cs_spawned) { VM_Warning("tried to centerprint to a non-client\n"); return; } client = svs.clients + num - 1; VM_VarString(1, string, sizeof(string)); MSG_Begin( svc_centerprint ); MSG_WriteString (&sv.multicast, string ); MSG_Send(MSG_ONE_R, NULL, client->edict ); } /* ================= PF_inpvs Also checks portalareas so that doors block sight ================= */ void PF_inpvs ( void ) { float *p1, *p2; int leafnum, cluster; int area1, area2; byte *mask; p1 = PRVM_G_VECTOR(OFS_PARM0); p2 = PRVM_G_VECTOR(OFS_PARM1); leafnum = CM_PointLeafnum (p1); cluster = CM_LeafCluster (leafnum); area1 = CM_LeafArea (leafnum); mask = CM_ClusterPVS (cluster); leafnum = CM_PointLeafnum (p2); cluster = CM_LeafCluster (leafnum); area2 = CM_LeafArea (leafnum); if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7))))) { PRVM_G_FLOAT(OFS_RETURN) = 0; } else if (!CM_AreasConnected (area1, area2)) { PRVM_G_FLOAT(OFS_RETURN) = 0; // a door blocks sight } else PRVM_G_FLOAT(OFS_RETURN) = 1; } /* ================= PF_inphs Also checks portalareas so that doors block sound ================= */ void PF_inphs (void) { float *p1, *p2; int leafnum, cluster; int area1, area2; byte *mask; p1 = PRVM_G_VECTOR(OFS_PARM0); p2 = PRVM_G_VECTOR(OFS_PARM1); leafnum = CM_PointLeafnum (p1); cluster = CM_LeafCluster (leafnum); area1 = CM_LeafArea (leafnum); mask = CM_ClusterPHS (cluster); leafnum = CM_PointLeafnum (p2); cluster = CM_LeafCluster (leafnum); area2 = CM_LeafArea (leafnum); if ( mask && (!(mask[cluster>>3] & (1<<(cluster&7))))) { PRVM_G_FLOAT(OFS_RETURN) = 0; // more than one bounce away } else if (!CM_AreasConnected (area1, area2)) { PRVM_G_FLOAT(OFS_RETURN) = 0; // a door blocks hearing } else PRVM_G_FLOAT(OFS_RETURN) = 1; } /* =============== PF_droptofloor void droptofloor( void ) =============== */ void PF_droptofloor (void) { edict_t *ent; vec3_t end; trace_t trace; // assume failure if it returns early PRVM_G_FLOAT(OFS_RETURN) = 0; ent = PRVM_PROG_TO_EDICT(prog->globals.sv->pev); if (ent == prog->edicts) { VM_Warning("droptofloor: can not modify world entity\n"); return; } if (ent->priv.sv->free) { VM_Warning("droptofloor: can not modify free entity\n"); return; } VectorCopy (ent->progs.sv->origin, end); end[2] -= 256; trace = SV_Trace(ent->progs.sv->origin, ent->progs.sv->mins, ent->progs.sv->maxs, end, ent, MASK_SOLID ); if (trace.startsolid) { VM_Warning("droptofloor: %s startsolid at %g %g %g\n", PRVM_GetString(ent->progs.sv->classname), ent->progs.sv->origin[0], ent->progs.sv->origin[1], ent->progs.sv->origin[2]); SV_FreeEdict (ent); return; } if (trace.fraction != 1) { VectorCopy (trace.endpos, ent->progs.sv->origin); SV_LinkEdict (ent); ent->progs.sv->aiflags = (int)ent->progs.sv->aiflags | AI_ONGROUND; ent->progs.sv->groundentity = PRVM_EDICT_TO_PROG(trace.ent); PRVM_G_FLOAT(OFS_RETURN) = 1; } } /* ================= PF_sound Each entity can have eight independant sound sources, like voice, weapon, feet, etc. Channel 0 is an auto-allocate channel, the others override anything already running on that entity/channel pair. An attenuation of 0 will play full volume everywhere in the level. Larger attenuations will drop off. ================= */ void PF_sound (void) { const char *sample; int channel, sound_idx; edict_t *entity; int volume; float attenuation; entity = PRVM_G_EDICT(OFS_PARM0); channel = (int)PRVM_G_FLOAT(OFS_PARM1); sample = PRVM_G_STRING(OFS_PARM2); volume = (int)(PRVM_G_FLOAT(OFS_PARM3) * 255); attenuation = PRVM_G_FLOAT(OFS_PARM4); if (volume < 0 || volume > 255) { VM_Warning("SV_StartSound: volume must be in range 0 - 255\n"); return; } if (attenuation < 0 || attenuation > 4) { VM_Warning("SV_StartSound: attenuation must be in range 0-4\n"); return; } if (channel < 0 || channel > 7) { VM_Warning("SV_StartSound: channel must be in range 0-7\n"); return; } sound_idx = SV_SoundIndex( sample ); SV_StartSound (NULL, entity, channel, sound_idx, volume / 255.0f, attenuation, 0 ); } void PF_event( void ) { edict_t *ent = PRVM_G_EDICT(OFS_PARM0); // event effects ent->priv.sv->event = (int)PRVM_G_FLOAT(OFS_PARM1); } /* ================= PF_ambientsound void ambientsound( entity e, string sample) ================= */ void PF_ambientsound (void) { const char *samp; edict_t *soundent; soundent = PRVM_G_EDICT(OFS_PARM0); samp = PRVM_G_STRING(OFS_PARM1); // check to see if samp was properly precached soundent->progs.sv->noise3 = SV_SoundIndex( samp ); } /* ================= PF_particle particle(origin, color, count) ================= */ void PF_particle (void) { float *org, *dir; float color; float count; org = PRVM_G_VECTOR(OFS_PARM0); dir = PRVM_G_VECTOR(OFS_PARM1); color = PRVM_G_FLOAT(OFS_PARM2); count = PRVM_G_FLOAT(OFS_PARM3); SV_StartParticle (org, dir, (int)color, (int)count); } /* ================= PF_traceline Used for use tracing and shot targeting Traces are blocked by bbox and exact bsp entityes, and also slide box entities if the tryents flag is set. traceline (vector v1, v2, float mask, entity ignore) ================= */ void PF_traceline (void) { float *v1, *v2; trace_t trace; int mask; edict_t *ent; prog->xfunction->builtinsprofile += 30; v1 = PRVM_G_VECTOR(OFS_PARM0); v2 = PRVM_G_VECTOR(OFS_PARM1); mask = (int)PRVM_G_FLOAT(OFS_PARM2); ent = PRVM_G_EDICT(OFS_PARM3); if(mask == 1) mask = MASK_SOLID; else if(mask == 2) mask = MASK_SHOT; else if(mask == 3) mask = MASK_MONSTERSOLID; else if(mask == 4) mask = MASK_WATER; else mask = MASK_ALL; 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], mask, PRVM_EDICT_TO_PROG(ent)); trace = SV_Trace (v1, vec3_origin, vec3_origin, v2, ent, mask ); prog->globals.sv->trace_allsolid = trace.allsolid; prog->globals.sv->trace_startsolid = trace.startsolid; prog->globals.sv->trace_fraction = trace.fraction; prog->globals.sv->trace_contents = trace.contents; VectorCopy (trace.endpos, prog->globals.sv->trace_endpos); VectorCopy (trace.plane.normal, prog->globals.sv->trace_plane_normal); prog->globals.sv->trace_plane_dist = trace.plane.dist; if (trace.ent) prog->globals.sv->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); else prog->globals.sv->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); } /* ================= PF_tracebox Used for use tracing and shot targeting Traces are blocked by bbox and exact bsp entityes, and also slide box entities if the tryents flag is set. tracebox (vector v1, vector mins, vector maxs, vector v2, float mask, entity ignore) ================= */ void PF_tracebox (void) { float *v1, *v2, *m1, *m2; trace_t trace; int mask; edict_t *ent; prog->xfunction->builtinsprofile += 30; v1 = PRVM_G_VECTOR(OFS_PARM0); m1 = PRVM_G_VECTOR(OFS_PARM1); m2 = PRVM_G_VECTOR(OFS_PARM2); v2 = PRVM_G_VECTOR(OFS_PARM3); mask = (int)PRVM_G_FLOAT(OFS_PARM4); ent = PRVM_G_EDICT(OFS_PARM5); if(mask == 1) mask = MASK_SOLID; else if(mask == 2) mask = MASK_SHOT; else if(mask == 3) mask = MASK_MONSTERSOLID; else if(mask == 4) mask = MASK_WATER; else mask = MASK_ALL; 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], mask, PRVM_EDICT_TO_PROG(ent)); trace = SV_Trace (v1, m1, m2, v2, ent, mask ); prog->globals.sv->trace_allsolid = trace.allsolid; prog->globals.sv->trace_startsolid = trace.startsolid; prog->globals.sv->trace_fraction = trace.fraction; prog->globals.sv->trace_contents = trace.contents; VectorCopy (trace.endpos, prog->globals.sv->trace_endpos); VectorCopy (trace.plane.normal, prog->globals.sv->trace_plane_normal); prog->globals.sv->trace_plane_dist = trace.plane.dist; if (trace.ent) prog->globals.sv->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); else prog->globals.sv->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); } /* ================= PF_tracetoss tracetoss (entity e, entity ignore) ================= */ void PF_tracetoss (void) { trace_t trace; edict_t *ent; edict_t *ignore; prog->xfunction->builtinsprofile += 600; ent = PRVM_G_EDICT(OFS_PARM0); if (ent == prog->edicts) { VM_Warning("tracetoss: can not use world entity\n"); return; } ignore = PRVM_G_EDICT(OFS_PARM1); trace = SV_TraceToss (ent, ignore); prog->globals.sv->trace_allsolid = trace.allsolid; prog->globals.sv->trace_startsolid = trace.startsolid; prog->globals.sv->trace_fraction = trace.fraction; prog->globals.sv->trace_contents = trace.contents; VectorCopy (trace.endpos, prog->globals.sv->trace_endpos); VectorCopy (trace.plane.normal, prog->globals.sv->trace_plane_normal); prog->globals.sv->trace_plane_dist = trace.plane.dist; if (trace.ent) prog->globals.sv->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); else prog->globals.sv->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); } void PF_create( void ) { //FIXME: apply classname, origin and angles for new entity VM_create(); } void PF_modelframes (void) { } void PF_changelevel (void) { Msg("changelevel\n"); } /* ============= PF_checkbottom ============= */ void PF_checkbottom (void) { PRVM_G_FLOAT(OFS_RETURN) = SV_CheckBottom (PRVM_G_EDICT(OFS_PARM0)); } /* ============= PF_pointcontents ============= */ void PF_pointcontents (void) { PRVM_G_FLOAT(OFS_RETURN) = SV_PointContents(PRVM_G_VECTOR(OFS_PARM0)); } /* ============= PF_makestatic ============= */ void PF_makestatic (void) { // quake1 legacy PRVM_ED_Free(PRVM_G_EDICT(OFS_PARM0)); } /* =============== PF_walkmove float walkmove(float yaw, float dist) =============== */ void PF_walkmove (void) { edict_t *ent; float yaw, dist; vec3_t move; mfunction_t *oldf; int oldpev; // assume failure if it returns early PRVM_G_FLOAT(OFS_RETURN) = 0; ent = PRVM_PROG_TO_EDICT(prog->globals.sv->pev); if (ent == prog->edicts) { VM_Warning("walkmove: can not modify world entity\n"); return; } if (ent->priv.sv->free) { VM_Warning("walkmove: can not modify free entity\n"); return; } yaw = PRVM_G_FLOAT(OFS_PARM0); dist = PRVM_G_FLOAT(OFS_PARM1); if (!((int)ent->progs.sv->aiflags & (AI_ONGROUND|AI_FLY|AI_SWIM))) return; yaw = yaw*M_PI*2 / 360; move[0] = cos(yaw)*dist; move[1] = sin(yaw)*dist; move[2] = 0; // save program state, because SV_movestep may call other progs oldf = prog->xfunction; oldpev = prog->globals.sv->pev; PRVM_G_FLOAT(OFS_RETURN) = SV_MoveStep(ent, move, true); // restore program state prog->xfunction = oldf; prog->globals.sv->pev = oldpev; } /* =============== PF_lightstyle void lightstyle(float style, string value) =============== */ void PF_lightstyle (void) { int style; const char *val; style = (int)PRVM_G_FLOAT(OFS_PARM0); val = PRVM_G_STRING(OFS_PARM1); if( (uint) style >= MAX_LIGHTSTYLES ) { PRVM_ERROR( "PF_lightstyle: style: %i >= 64", style ); } SV_ConfigString (CS_LIGHTS + style, val ); } /* ============= PF_aim Pick a vector for the player to shoot along vector aim(entity, missilespeed) ============= */ void PF_aim (void) { edict_t *ent, *check, *bestent; vec3_t start, dir, end, bestdir; int i, j; trace_t tr; float dist, bestdist; float speed; int flags = Cvar_VariableValue( "dmflags" ); // assume failure if it returns early VectorCopy(prog->globals.sv->v_forward, PRVM_G_VECTOR(OFS_RETURN)); ent = PRVM_G_EDICT(OFS_PARM0); if (ent == prog->edicts) { VM_Warning("aim: can not use world entity\n"); return; } if (ent->priv.sv->free) { VM_Warning("aim: can not use free entity\n"); return; } speed = PRVM_G_FLOAT(OFS_PARM1); VectorCopy (ent->progs.sv->origin, start); start[2] += 20; // try sending a trace straight VectorCopy (prog->globals.sv->v_forward, dir); VectorMA (start, 2048, dir, end); tr = SV_Trace (start, vec3_origin, vec3_origin, end, ent, MASK_ALL ); if (tr.ent && ((edict_t *)tr.ent)->progs.sv->takedamage == 2 && (flags & DF_NO_FRIENDLY_FIRE || ent->progs.sv->team <=0 || ent->progs.sv->team != ((edict_t *)tr.ent)->progs.sv->team)) { VectorCopy (prog->globals.sv->v_forward, PRVM_G_VECTOR(OFS_RETURN)); return; } // try all possible entities VectorCopy (dir, bestdir); bestdist = 0.5f; bestent = NULL; check = PRVM_NEXT_EDICT(prog->edicts); for (i = 1; i < prog->num_edicts; i++, check = PRVM_NEXT_EDICT(check)) { prog->xfunction->builtinsprofile++; if (check->progs.sv->takedamage != 2) // DAMAGE_AIM continue; if (check == ent) continue; if (flags & DF_NO_FRIENDLY_FIRE && ent->progs.sv->team > 0 && ent->progs.sv->team == check->progs.sv->team) continue; // don't aim at teammate for (j = 0; j < 3; j++) end[j] = check->progs.sv->origin[j] + 0.5 * (check->progs.sv->mins[j] + check->progs.sv->maxs[j]); VectorSubtract (end, start, dir); VectorNormalize (dir); dist = DotProduct (dir, prog->globals.sv->v_forward); if (dist < bestdist) continue; // to far to turn tr = SV_Trace (start, vec3_origin, vec3_origin, end, ent, MASK_ALL ); if (tr.ent == check) { // can shoot at this one bestdist = dist; bestent = check; } } if (bestent) { VectorSubtract (bestent->progs.sv->origin, ent->progs.sv->origin, dir); dist = DotProduct (dir, prog->globals.sv->v_forward); VectorScale (prog->globals.sv->v_forward, dist, end); end[2] = dir[2]; VectorNormalize (end); VectorCopy (end, PRVM_G_VECTOR(OFS_RETURN)); } else { VectorCopy (bestdir, PRVM_G_VECTOR(OFS_RETURN)); } } /* =============== PF_Configstring =============== */ void PF_configstring( void ) { SV_ConfigString((int)PRVM_G_FLOAT(OFS_PARM0), PRVM_G_STRING(OFS_PARM1)); } void PF_areaportalstate( void ) { CM_SetAreaPortalState((int)PRVM_G_FLOAT(OFS_PARM0), (bool)PRVM_G_FLOAT(OFS_PARM1)); } /* ============== PF_changeyaw This was a major timewaster in progs, so it was converted to C ============== */ void PF_changeyaw (void) { edict_t *ent; ent = PRVM_PROG_TO_EDICT(prog->globals.sv->pev); if (ent == prog->edicts) { VM_Warning("changeyaw: can not modify world entity\n"); return; } if (ent->priv.sv->free) { VM_Warning("changeyaw: can not modify free entity\n"); return; } ent->progs.sv->angles[1] = SV_AngleMod( ent->progs.sv->ideal_yaw, anglemod(ent->progs.sv->angles[1]), ent->progs.sv->yaw_speed ); } /* ============== PF_changepitch ============== */ void PF_changepitch (void) { edict_t *ent; ent = PRVM_PROG_TO_EDICT(prog->globals.sv->pev); if (ent == prog->edicts) { VM_Warning("changepitch: can not modify world entity\n"); return; } if (ent->priv.sv->free) { VM_Warning("changepitch: can not modify free entity\n"); return; } ent->progs.sv->angles[0] = SV_AngleMod( 30, anglemod(ent->progs.sv->angles[0]), 30 ); } /* ============== PF_createPhysBox ============== */ void PF_createPhysBox( void ) { edict_t *ent = PRVM_G_EDICT(OFS_PARM0); Phys->CreateBOX( ent->priv.sv, ent->progs.sv->mins, ent->progs.sv->maxs, ent->progs.sv->origin, ent->progs.sv->angles, &ent->priv.sv->collision, &ent->priv.sv->physbody ); } /* ================= PF_findradius Returns a chain of entities that have origins within a spherical area findradius (origin, radius) ================= */ void PF_findradius (void) { edict_t *ent, *chain; vec_t radius, radius2; vec3_t org, eorg; int i; chain = (edict_t *)prog->edicts; VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org); radius = PRVM_G_FLOAT(OFS_PARM1); radius2 = radius * radius; ent = prog->edicts; for (i = 1; i < prog->num_edicts ; i++, ent = PRVM_NEXT_EDICT(ent)) { if (ent->priv.sv->free) continue; if (ent->progs.sv->solid == SOLID_NOT) continue; VectorSubtract(org, ent->progs.sv->origin, eorg); VectorMAMAM(1, eorg, 0.5f, ent->progs.sv->mins, 0.5f, ent->progs.sv->maxs, eorg); if (DotProduct(eorg, eorg) < radius2) { ent->progs.sv->chain = PRVM_EDICT_TO_PROG(chain); chain = ent; } } VM_RETURN_EDICT(chain); } void PF_precache_model (void) { PRVM_G_FLOAT(OFS_RETURN) = SV_ModelIndex(PRVM_G_STRING(OFS_PARM0)); } void PF_precache_sound (void) { PRVM_G_FLOAT(OFS_RETURN) = SV_SoundIndex(PRVM_G_STRING(OFS_PARM0)); } void PF_modelindex (void) { int index = SV_FindIndex (PRVM_G_STRING(OFS_PARM0), CS_MODELS, MAX_MODELS, false); if(!index) VM_Warning("modelindex: %s not precached\n", PRVM_G_STRING(OFS_PARM0)); PRVM_G_FLOAT(OFS_RETURN) = index; } void PF_decalindex (void) { // it will precache new decals too PRVM_G_FLOAT(OFS_RETURN) = SV_DecalIndex(PRVM_G_STRING(OFS_PARM0)); } void PF_imageindex (void) { // it will precache new images too PRVM_G_FLOAT(OFS_RETURN) = SV_ImageIndex(PRVM_G_STRING(OFS_PARM0)); } void PF_getlightlevel (void) { edict_t *ent; ent = PRVM_G_EDICT(OFS_PARM0); if (ent == prog->edicts) { VM_Warning("getlightlevel: can't get light level at world entity\n"); return; } if (ent->priv.sv->free) { VM_Warning("getlightlevel: can't get light level at free entity\n"); return; } PRVM_G_FLOAT(OFS_RETURN) = 1.0; //FIXME: implement } /* ================= PF_setstats void setstats(entity client, float stat_num, float value) ================= */ void PF_setstats( void ) { edict_t *e; int stat_num; const char *string; short value; e = PRVM_G_EDICT(OFS_PARM0); if(!e->priv.sv->client) { VM_Warning("setstats: stats applied only for players\n"); return; } stat_num = (int)PRVM_G_FLOAT(OFS_PARM1); if(stat_num < 0 || stat_num > MAX_STATS) { VM_Warning("setstats: invalid stats number\n"); return; } string = PRVM_G_STRING(OFS_PARM2); switch(stat_num) { case STAT_ZOOM: case STAT_SPEED: case STAT_CHASE: case STAT_HELPICON: case STAT_AMMO_ICON: case STAT_ARMOR_ICON: case STAT_TIMER_ICON: case STAT_HEALTH_ICON: case STAT_PICKUP_ICON: case STAT_SELECTED_ICON: case STAT_SELECTED_ITEM: value = SV_ImageIndex( string ); break; case STAT_AMMO: case STAT_FRAGS: case STAT_TIMER: case STAT_ARMOR: case STAT_HEALTH: case STAT_FLASHES: case STAT_LAYOUTS: case STAT_SPECTATOR: value = atoi( string ); break; default: MsgWarn("unknown stat type %d\n", stat_num ); return; } e->priv.sv->client->ps.stats[stat_num] = value; } /* ================= PF_setmodel setmodel(entity, model) ================= */ void PF_setmodel( void ) { edict_t *e; e = PRVM_G_EDICT(OFS_PARM0); if (e == prog->edicts) { VM_Warning("setmodel: can not modify world entity\n"); return; } if (e->priv.sv->free) { VM_Warning("setmodel: can not modify free entity\n"); return; } SV_SetModel( e, PRVM_G_STRING(OFS_PARM1)); } /* ================= PF_setsize the size box is rotated by the current angle setsize (entity, minvector, maxvector) ================= */ void PF_setsize( void ) { edict_t *e; float *min, *max; e = PRVM_G_EDICT(OFS_PARM0); if (e == prog->edicts) { VM_Warning("setsize: can not modify world entity\n"); return; } if (e->priv.sv->free) { VM_Warning("setsize: can not modify free entity\n"); return; } min = PRVM_G_VECTOR(OFS_PARM1); max = PRVM_G_VECTOR(OFS_PARM2); SV_SetMinMaxSize (e, min, max, !VectorIsNull(e->progs.sv->angles)); } /* ================= PF_setorigin This is the only valid way to move an object without using the physics of the world (setting velocity and waiting). Directly changing origin will not set internal links correctly, so clipping would be messed up. This should be called when an object is spawned, and then only if it is teleported. setorigin (entity, origin) ================= */ void PF_setorigin (void) { edict_t *e; float *org; e = PRVM_G_EDICT(OFS_PARM0); if (e == prog->edicts) { VM_Warning("setorigin: can not modify world entity\n"); return; } if (e->priv.sv->free) { VM_Warning("setorigin: can not modify free entity\n"); return; } org = PRVM_G_VECTOR(OFS_PARM1); VectorCopy (org, e->progs.sv->origin); SV_LinkEdict (e); } /* ============== PF_dropclient ============== */ void PF_dropclient (void) { int clientnum = PRVM_G_EDICTNUM(OFS_PARM0) - 1; if (clientnum < 0 || clientnum >= host.maxclients) { VM_Warning("dropclient: not a client\n"); return; } if (svs.clients[clientnum].state != cs_spawned) { VM_Warning("dropclient: that client slot is not connected\n"); return; } SV_DropClient(svs.clients + clientnum); } /* ============== PF_spawnclient ============== */ void PF_spawnclient (void) { int i; edict_t *ed; prog->xfunction->builtinsprofile += 2; ed = prog->edicts; for (i = 0; i < maxclients->value; i++) { if (svs.clients[i].state != cs_spawned) { prog->xfunction->builtinsprofile += 100; svs.clients[i].state = cs_connected; ed = PRVM_EDICT_NUM(i + 1); SV_ClientConnect(ed, "" ); break; } } VM_RETURN_EDICT(ed); } //NOTE: intervals between various "interfaces" was leave for future expansions prvm_builtin_t vm_sv_builtins[] = { NULL, // #0 // network messaging PF_BeginMessage, // #1 void MsgBegin (float dest) PF_WriteByte, // #2 void WriteByte (float f) PF_WriteChar, // #3 void WriteChar (float f) PF_WriteShort, // #4 void WriteShort (float f) PF_WriteLong, // #5 void WriteLong (float f) PF_WriteFloat, // #6 void WriteFloat (float f) PF_WriteAngle, // #7 void WriteAngle (float f) PF_WriteCoord, // #8 void WriteCoord (float f) PF_WriteString, // #9 void WriteString (string s) PF_WriteEntity, // #10 void WriteEntity (entity s) PF_EndMessage, // #11 void MsgEnd(float to, vector pos, entity e) // mathlib VM_min, // #12 float min(float a, float b ) VM_max, // #13 float max(float a, float b ) VM_bound, // #14 float bound(float min, float val, float max) VM_pow, // #15 float pow(float x, float y) VM_sin, // #16 float sin(float f) VM_cos, // #17 float cos(float f) VM_sqrt, // #18 float sqrt(float f) VM_rint, // #19 float rint (float v) VM_floor, // #20 float floor(float v) VM_ceil, // #21 float ceil (float v) VM_fabs, // #22 float fabs (float f) VM_random_long, // #23 float random_long( void ) VM_random_float, // #24 float random_float( void ) NULL, // #25 NULL, // #26 NULL, // #27 NULL, // #28 NULL, // #29 NULL, // #30 // vector mathlib VM_normalize, // #31 vector normalize(vector v) VM_veclength, // #32 float veclength(vector v) VM_vectoyaw, // #33 float vectoyaw(vector v) VM_vectoangles, // #34 vector vectoangles(vector v) VM_randomvec, // #35 vector randomvec( void ) VM_vectorvectors, // #36 void vectorvectors(vector dir) VM_makevectors, // #37 void makevectors(vector dir) VM_makevectors2, // #38 void makevectors2(vector dir) NULL, // #39 NULL, // #40 // stdlib functions VM_atof, // #41 float atof(string s) VM_ftoa, // #42 string ftoa(float s) VM_vtoa, // #43 string vtoa(vector v) VM_atov, // #44 vector atov(string s) VM_print, // #45 void Msg( ... ) VM_wprint, // #46 void MsgWarn( ... ) VM_objerror, // #47 void Error( ... ) VM_bprint, // #48 void bprint(string s) PF_sprint, // #49 void sprint(entity client, string s) PF_centerprint, // #50 void centerprint(entity client, strings) VM_cvar, // #51 float cvar(string s) VM_cvar_set, // #52 void cvar_set(string var, string val) VM_allocstring, // #53 string AllocString(string s) VM_freestring, // #54 void FreeString(string s) VM_strlen, // #55 float strlen(string s) VM_strcat, // #56 string strcat(string s1, string s2) VM_argv, // #57 string argv( float parm ) NULL, // #58 NULL, // #59 NULL, // #60 // internal debugger VM_break, // #61 void break( void ) VM_crash, // #62 void crash( void ) VM_coredump, // #63 void coredump( void ) VM_stackdump, // #64 void stackdump( void ) VM_traceon, // #65 void trace_on( void ) VM_traceoff, // #66 void trace_off( void ) VM_eprint, // #67 void dump_edict(entity e) VM_nextent, // #68 entity nextent(entity e) NULL, // #69 NULL, // #70 // engine functions (like half-life enginefuncs_s) PF_precache_model, // #71 float precache_model(string s) PF_precache_sound, // #72 float precache_sound(string s) PF_setmodel, // #73 float setmodel(entity e, string m) PF_modelindex, // #74 float model_index(string s) PF_decalindex, // #75 float decal_index(string s) PF_imageindex, // #76 float image_index(string s) PF_setsize, // #77 void setsize(entity e, vector min, vector max) PF_changelevel, // #78 void changelevel(string mapname, string spotname) PF_changeyaw, // #79 void ChangeYaw( void ) PF_changepitch, // #80 void ChangePitch( void ) VM_find, // #81 entity find(entity start, .string fld, string match) PF_getlightlevel, // #82 float getEntityIllum( entity e ) PF_findradius, // #83 entity FindInSphere(vector org, float rad) PF_inpvs, // #84 float InPVS( vector v1, vector v2 ) PF_inphs, // #85 float InPHS( vector v1, vector v2 ) PF_create, // #86 entity create( string name, string model, vector org ) VM_remove, // #87 void remove( entity e ) PF_droptofloor, // #88 float droptofloor( void ) PF_walkmove, // #89 float walkmove(float yaw, float dist) PF_setorigin, // #90 void setorigin(entity e, vector o) PF_sound, // #91 void sound(entity e, float chan, string samp, float vol, float attn) PF_ambientsound, // #92 void ambientsound(entity e, string samp) PF_traceline, // #93 void traceline(vector v1, vector v2, float mask, entity ignore) PF_tracetoss, // #94 void tracetoss (entity e, entity ignore) PF_tracebox, // #95 void tracebox (vector v1, vector mins, vector maxs, vector v2, float mask, entity ignore) PF_checkbottom, // #96 float checkbottom(entity e) PF_lightstyle, // #97 void lightstyle(float style, string value) PF_pointcontents, // #98 float pointcontents(vector v) PF_aim, // #99 vector aim(entity e, float speed) VM_servercmd, // #100 void server_command( string command ) VM_clientcmd, // #101 void client_command( entity e, string s) PF_particle, // #102 void particle(vector o, vector d, float color, float count) PF_areaportalstate, // #103 void areaportal_state( float num, float state ) PF_setstats, // #104 void setstats(entity e, float f, string stats) PF_configstring, // #105 void configstring(float num, string s) PF_makestatic, // #106 void makestatic(entity e) PF_modelframes, // #107 float model_frames(float modelindex) PF_event, // #108 void set_effect( entity e, float effect ) PF_createPhysBox, // #109 NULL, // #110 NULL, // #111 NULL, // #112 NULL, // #113 NULL, // #114 NULL, // #115 NULL, // #116 NULL, // #117 NULL, // #118 NULL, // #119 e10, e10, e10, e10, e10, e10, e10, e10, // #120-199 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 NULL, // #218 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, e10, e10, e10, e10, e10, e10, e10, e10, e10, // #300-399 NULL, // #400 NULL, // #401 VM_findchain, // #402 entity(.string fld, string match) findchain (DP_QC_FINDCHAIN) VM_findchainfloat, // #403 entity(.float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT) NULL, // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT) NULL, // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD) NULL, // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER) NULL, // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB) NULL, // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE) NULL, // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN) NULL, // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW) NULL, // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK) NULL, // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1) NULL, // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1) NULL, // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1) NULL, // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1) NULL, // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH) NULL, // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH) NULL, // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS) NULL, // #432 NULL, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN) NULL, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE) NULL, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE) NULL, // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE) NULL, // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE) NULL, // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE) NULL, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE) NULL, // #440 VM_tokenize, // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND) VM_argv, // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND) NULL, // #443 void(entity e, entity tagentity, string tagname) setattachment (DP_GFX_QUAKE3MODELTAGS) VM_search_begin, // #444 float(string pattern, float caseinsensitive, float quiet) search_begin (DP_FS_SEARCH) 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) 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) NULL, // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO) NULL, // #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) NULL, // #455 NULL, // #456 NULL, // #457 void(vector org, vector vel, float howmany) te_flamejet = #457 (DP_TE_FLAMEJET) NULL, // #458 NULL, // #459 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) NULL, // #470 NULL, // #471 NULL, // #472 NULL, // #473 NULL, // #474 NULL, // #475 NULL, // #476 NULL, // #477 NULL, // #478 NULL, // #479 e10, e10 // #480-499 (LordHavoc) }; const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t); //num of builtins /* =============== SV_ShutdownGameProgs Called when either the entire server is being killed, or it is changing to a different game directory. =============== */ void SV_ShutdownGameProgs (void) { edict_t *ent; int i; SV_VM_Begin(); for (i = 1; prog && i < prog->num_edicts; i++) { ent = PRVM_EDICT_NUM(i); SV_FreeEdict( ent );// release physic } SV_VM_End(); if(!svs.gclients) return; Mem_Free( svs.gclients ); svs.gclients = NULL; } /* =============== SV_InitGameProgs Init the game subsystem for a new map =============== */ void SV_InitGameProgs (void) { Msg("\n"); SV_VM_Setup(); }