From 715bf430b1612f5a4527968b683db80de14f10f9 Mon Sep 17 00:00:00 2001 From: havoc Date: Mon, 16 Jan 2006 03:52:42 +0000 Subject: [PATCH] csqc patch from [515], seems to work with [515]'s dpcsqc test mod, needs a lot of code cleanup/merging, and VF_FOV stuff is currently ignored git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@5910 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_collision.c | 27 +- cl_collision.h | 2 +- cl_input.c | 7 +- cl_main.c | 266 ++++- cl_parse.c | 30 +- cl_screen.c | 6 +- cl_screen.h | 2 + client.h | 21 + clprogdefs.h | 92 +- clvm_cmds.c | 2994 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd.c | 32 +- console.c | 1 + csprogs.c | 614 ++++++++++ csprogs.h | 64 ++ gl_backend.c | 11 +- gl_rmain.c | 108 +- host.c | 9 +- keys.c | 24 + makefile.inc | 5 +- mvm_cmds.c | 46 +- progs.h | 2 + progsvm.h | 5 +- protocol.c | 151 +++ protocol.h | 2 +- prvm_cmds.c | 837 +++++++++++++- prvm_cmds.h | 23 + sbar.c | 562 ++++----- sv_main.c | 50 +- svvm_cmds.c | 241 +++- vid_wgl.c | 1 + view.c | 5 + world_cs.c | 732 ++++++++++++ 32 files changed, 6527 insertions(+), 445 deletions(-) create mode 100644 clvm_cmds.c create mode 100644 csprogs.c create mode 100644 csprogs.h create mode 100644 world_cs.c diff --git a/cl_collision.c b/cl_collision.c index 6a138a8e..fdaed38b 100644 --- a/cl_collision.c +++ b/cl_collision.c @@ -181,10 +181,12 @@ trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, co return cliptrace; } -float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent) +float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent, qboolean csqcents) { float maxfrac, maxrealfrac; - int n; + int n, entsnum; + entity_t *entlist; + unsigned char *entactivelist; entity_render_t *ent; float tracemins[3], tracemaxs[3]; trace_t trace; @@ -213,12 +215,25 @@ float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, ve tracemins[2] = min(start[2], end[2]); tracemaxs[2] = max(start[2], end[2]); + if(csqcents) + { + entlist = cl_csqcentities; + entactivelist = cl_csqcentities_active; + entsnum = cl_num_csqcentities; + } + else + { + entlist = cl_entities; + entactivelist = cl_entities_active; + entsnum = cl_num_entities; + } + // look for embedded bmodels - for (n = 0;n < cl_num_entities;n++) + for (n = 0;n < entsnum;n++) { - if (!cl_entities_active[n]) + if (!entactivelist[n]) continue; - ent = &cl_entities[n].render; + ent = &entlist[n].render; if (!BoxesOverlap(ent->mins, ent->maxs, tracemins, tracemaxs)) continue; if (!ent->model || !ent->model->TraceBox) @@ -226,7 +241,7 @@ float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, ve if ((ent->flags & RENDER_EXTERIORMODEL) && !chase_active.integer) continue; // if transparent and not selectable, skip entity - if (!(cl_entities[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST)))) + if (!(entlist[n].state_current.effects & EF_SELECTABLE) && (ent->alpha < 1 || (ent->effects & (EF_ADDITIVE | EF_NODEPTHTEST)))) continue; if (ent == ignoreent) continue; diff --git a/cl_collision.h b/cl_collision.h index 532ad250..a0194089 100644 --- a/cl_collision.h +++ b/cl_collision.h @@ -3,7 +3,7 @@ #define CL_COLLISION_H trace_t CL_TraceBox(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitbmodels, int *hitent, int hitsupercontentsmask, qboolean hitplayers); -float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent); +float CL_SelectTraceLine(const vec3_t start, const vec3_t end, vec3_t impact, vec3_t normal, int *hitent, entity_render_t *ignoreent, qboolean csqcents); void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius); int CL_PointQ1Contents(const vec3_t p); int CL_PointSuperContents(const vec3_t p); diff --git a/cl_input.c b/cl_input.c index 1ea050da..729e306d 100644 --- a/cl_input.c +++ b/cl_input.c @@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // rights reserved. #include "quakedef.h" +#include "csprogs.h" /* =============================================================================== @@ -415,7 +416,7 @@ void CL_Move (void) old_mouse_y = my; // if not in menu, apply mouse move to viewangles/movement - if (in_client_mouse) + if (!cl.csqc_wantsmousemove && in_client_mouse) { if (cl_prydoncursor.integer) { @@ -506,7 +507,7 @@ void CL_UpdatePrydonCursor(void) VectorSet(temp, cl.cmd.cursor_screen[2] * scale[2], cl.cmd.cursor_screen[0] * scale[0], cl.cmd.cursor_screen[1] * scale[1]); Matrix4x4_Transform(&r_refdef.viewentitymatrix, temp, cl.cmd.cursor_end); // trace from view origin to the cursor - cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl_entities[cl.playerentity].render : NULL); + cl.cmd.cursor_fraction = CL_SelectTraceLine(cl.cmd.cursor_start, cl.cmd.cursor_end, cl.cmd.cursor_impact, cl.cmd.cursor_normal, &cl.cmd.cursor_entitynumber, (chase_active.integer || cl.intermission) ? &cl_entities[cl.playerentity].render : NULL, false); // makes sparks where cursor is //CL_SparkShower(cl.cmd.cursor_impact, cl.cmd.cursor_normal, 5, 0); } @@ -869,6 +870,8 @@ void CL_SendMove(void) if (cl.cmd.cursor_screen[1] <= -1) bits |= 32; if (cl.cmd.cursor_screen[1] >= 1) bits |= 64; + csqc_buttons = bits; + // always dump the first two messages, because they may contain leftover inputs from the last level if (++cl.movemessages >= 2) { diff --git a/cl_main.c b/cl_main.c index 0606a8d6..c35aec1d 100644 --- a/cl_main.c +++ b/cl_main.c @@ -23,11 +23,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "cl_collision.h" #include "cl_video.h" #include "image.h" +#include "csprogs.h" #include "r_shadow.h" // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. +cvar_t csqc_progname = {0, "csqc_progname","csprogs.dat"}; //[515]: csqc crc check and right csprogs name according to progs.dat +cvar_t csqc_progcrc = {CVAR_READONLY, "csqc_progcrc","0"}; + cvar_t cl_shownet = {0, "cl_shownet","0"}; cvar_t cl_nolerp = {0, "cl_nolerp", "0"}; @@ -77,6 +81,7 @@ client_static_t cls; client_state_t cl; int cl_max_entities; +int cl_max_csqcentities; int cl_max_static_entities; int cl_max_temp_entities; int cl_max_effects; @@ -87,7 +92,9 @@ int cl_max_brushmodel_entities; int cl_activedlights; entity_t *cl_entities; +entity_t *cl_csqcentities; //[515]: csqc unsigned char *cl_entities_active; +unsigned char *cl_csqcentities_active; //[515]: csqc entity_t *cl_static_entities; entity_t *cl_temp_entities; cl_effect_t *cl_effects; @@ -97,6 +104,7 @@ lightstyle_t *cl_lightstyle; int *cl_brushmodel_entities; int cl_num_entities; +int cl_num_csqcentities; //[515]: csqc int cl_num_static_entities; int cl_num_temp_entities; int cl_num_brushmodel_entities; @@ -116,7 +124,9 @@ void CL_ClearState(void) int i; if (cl_entities) Mem_Free(cl_entities);cl_entities = NULL; + if (cl_csqcentities) Mem_Free(cl_csqcentities);cl_csqcentities = NULL; //[515]: csqc if (cl_entities_active) Mem_Free(cl_entities_active);cl_entities_active = NULL; + if (cl_csqcentities_active) Mem_Free(cl_csqcentities_active);cl_csqcentities_active = NULL; //[515]: csqc if (cl_static_entities) Mem_Free(cl_static_entities);cl_static_entities = NULL; if (cl_temp_entities) Mem_Free(cl_temp_entities);cl_temp_entities = NULL; if (cl_effects) Mem_Free(cl_effects);cl_effects = NULL; @@ -139,12 +149,14 @@ void CL_ClearState(void) SZ_Clear (&cls.message); cl_num_entities = 0; + cl_num_csqcentities = 0; //[515]: csqc cl_num_static_entities = 0; cl_num_temp_entities = 0; cl_num_brushmodel_entities = 0; // tweak these if the game runs out cl_max_entities = 256; + cl_max_csqcentities = 256; //[515]: csqc cl_max_static_entities = 256; cl_max_temp_entities = 512; cl_max_effects = 256; @@ -155,7 +167,9 @@ void CL_ClearState(void) cl_activedlights = 0; cl_entities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_entities * sizeof(entity_t)); + cl_csqcentities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_csqcentities * sizeof(entity_t)); //[515]: csqc cl_entities_active = (unsigned char *)Mem_Alloc(cl_mempool, cl_max_brushmodel_entities * sizeof(unsigned char)); + cl_csqcentities_active = (unsigned char *)Mem_Alloc(cl_mempool, cl_max_brushmodel_entities * sizeof(unsigned char)); //[515]: csqc cl_static_entities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_static_entities * sizeof(entity_t)); cl_temp_entities = (entity_t *)Mem_Alloc(cl_mempool, cl_max_temp_entities * sizeof(entity_t)); cl_effects = (cl_effect_t *)Mem_Alloc(cl_mempool, cl_max_effects * sizeof(cl_effect_t)); @@ -175,6 +189,15 @@ void CL_ClearState(void) cl_entities[i].state_current = defaultstate; } + for (i = 0;i < cl_max_csqcentities;i++) + { + cl_csqcentities[i].state_baseline = defaultstate; //[515]: csqc + cl_csqcentities[i].state_previous = defaultstate; //[515]: csqc + cl_csqcentities[i].state_current = defaultstate; //[515]: csqc + cl_csqcentities[i].csqc = true; + cl_csqcentities[i].state_current.number = -i; + } + if (gamemode == GAME_NEXUIZ) { VectorSet(cl_playerstandmins, -16, -16, -24); @@ -220,6 +243,34 @@ void CL_ExpandEntities(int num) } } +void CL_ExpandCSQCEntities(int num) +{ + int i, oldmaxentities; + entity_t *oldentities; + if (num >= cl_max_csqcentities) + { + if (!cl_csqcentities) + Sys_Error("CL_ExpandCSQCEntities: cl_csqcentities not initialized\n"); + if (num >= MAX_EDICTS) + Host_Error("CL_ExpandCSQCEntities: num %i >= %i\n", num, MAX_EDICTS); + oldmaxentities = cl_max_csqcentities; + oldentities = cl_csqcentities; + cl_max_csqcentities = (num & ~255) + 256; + cl_csqcentities = Mem_Alloc(cl_mempool, cl_max_csqcentities * sizeof(entity_t)); + memcpy(cl_csqcentities, oldentities, oldmaxentities * sizeof(entity_t)); + Mem_Free(oldentities); + for (i = oldmaxentities;i < cl_max_csqcentities;i++) + { + cl_csqcentities[i].state_baseline = defaultstate; + cl_csqcentities[i].state_previous = defaultstate; + cl_csqcentities[i].state_current = defaultstate; + cl_csqcentities[i].csqc = true; + cl_csqcentities[i].state_current.number = -i; + } + } +} + +void CL_VM_ShutDown (void); /* ===================== CL_Disconnect @@ -235,6 +286,7 @@ void CL_Disconnect(void) Con_DPrintf("CL_Disconnect\n"); + CL_VM_ShutDown(); // stop sounds (especially looping!) S_StopAllSounds (); @@ -643,7 +695,7 @@ void CL_LinkNetworkEntity(entity_t *e) { e->persistent.linkframe = entitylinkframenumber; // skip inactive entities and world - if (!e->state_current.active || e == cl_entities) + if (!e->state_current.active || e == cl_entities || e == cl_csqcentities) return; e->render.alpha = e->state_current.alpha * (1.0f / 255.0f); // FIXME: interpolate? e->render.scale = e->state_current.scale * (1.0f / 16.0f); // FIXME: interpolate? @@ -691,23 +743,35 @@ void CL_LinkNetworkEntity(entity_t *e) e->render.skinnum = e->state_current.skin; if (e->render.flags & RENDER_VIEWMODEL && !e->state_current.tagentity) { - if (!r_drawviewmodel.integer || chase_active.integer || envmap) + if (!r_drawviewmodel.integer || chase_active.integer || envmap)// || csqc_loaded) return; - if (cl.viewentity) - CL_LinkNetworkEntity(cl_entities + cl.viewentity); - matrix = &viewmodelmatrix; - if (e == &cl.viewent && cl_entities[cl.viewentity].state_current.active) + if (!e->csqc) { - e->state_current.alpha = cl_entities[cl.viewentity].state_current.alpha; - e->state_current.effects = EF_NOSHADOW | (cl_entities[cl.viewentity].state_current.effects & (EF_ADDITIVE | EF_REFLECTIVE | EF_FULLBRIGHT | EF_NODEPTHTEST)); + if (cl.viewentity) + CL_LinkNetworkEntity(cl_entities + cl.viewentity); + if (e == &cl.viewent && cl_entities[cl.viewentity].state_current.active) + { + e->state_current.alpha = cl_entities[cl.viewentity].state_current.alpha; + e->state_current.effects = EF_NOSHADOW | (cl_entities[cl.viewentity].state_current.effects & (EF_ADDITIVE | EF_REFLECTIVE | EF_FULLBRIGHT | EF_NODEPTHTEST)); + } } + matrix = &viewmodelmatrix; } else { // if the tag entity is currently impossible, skip it - if (e->state_current.tagentity >= cl_num_entities) - return; - t = cl_entities + e->state_current.tagentity; + if (!e->csqc) + { + if (e->state_current.tagentity >= cl_num_entities) + return; + t = cl_entities + e->state_current.tagentity; + } + else + { + if (e->state_current.tagentity >= cl_num_csqcentities) + return; + t = cl_csqcentities + e->state_current.tagentity; + } // if the tag entity is inactive, skip it if (!t->state_current.active) return; @@ -741,7 +805,7 @@ void CL_LinkNetworkEntity(entity_t *e) // movement lerp // if it's the player entity, update according to client movement - if (e == cl_entities + cl.playerentity && cl.movement) + if (e == cl_entities + cl.playerentity && cl.movement)// && !e->csqc) { lerp = (cl.time - cl.mtime[1]) / (cl.mtime[0] - cl.mtime[1]); lerp = bound(0, lerp, 1); @@ -766,7 +830,10 @@ void CL_LinkNetworkEntity(entity_t *e) } // model setup and some modelflags - e->render.model = cl.model_precache[e->state_current.modelindex]; + if(e->state_current.modelindex < MAX_MODELS) + e->render.model = cl.model_precache[e->state_current.modelindex]; + else + e->render.model = cl.csqc_model_precache[65536-e->state_current.modelindex]; if (e->render.model) { // if model is alias or this is a tenebrae-like dlight, reverse pitch direction @@ -1025,6 +1092,7 @@ void CL_LinkNetworkEntity(entity_t *e) if (gamemode == GAME_TENEBRAE && e->render.model && e->render.model->type == mod_sprite) e->render.effects |= EF_ADDITIVE; // player model is only shown with chase_active on + if (!e->csqc) if (e->state_current.number == cl.viewentity) e->render.flags |= RENDER_EXTERIORMODEL; // transparent stuff can't be lit during the opaque stage @@ -1043,6 +1111,7 @@ void CL_LinkNetworkEntity(entity_t *e) && (!(e->render.flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cl.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer))) e->render.flags |= RENDER_SHADOW; // as soon as player is known we can call V_CalcRefDef + if (!csqc_loaded) if (e->state_current.number == cl.viewentity) V_CalcRefdef(); if (e->render.model && e->render.model->name[0] == '*' && e->render.model->TraceBox) @@ -1073,6 +1142,25 @@ void CL_RelinkWorld(void) r_refdef.worldmodel = cl.worldmodel; } +void CL_RelinkCSQCWorld(void) //[515]: csqc +{ + entity_t *ent = &cl_csqcentities[0]; + if(!csqc_loaded) + return; +// cl_brushmodel_entities[cl_num_brushmodel_entities++] = 0; + // FIXME: this should be done at load + Matrix4x4_CreateIdentity(&ent->render.matrix); + Matrix4x4_CreateIdentity(&ent->render.inversematrix); + R_LerpAnimation(&ent->render); + CL_BoundingBoxForEntity(&ent->render); + ent->render.flags = RENDER_SHADOW; + if (!r_fullbright.integer) + ent->render.flags |= RENDER_LIGHT; + VectorSet(ent->render.colormod, 1, 1, 1); +// r_refdef.worldentity = &ent->render; +// r_refdef.worldmodel = cl.worldmodel; +} + static void CL_RelinkStaticEntities(void) { int i; @@ -1100,52 +1188,82 @@ static void CL_RelinkStaticEntities(void) CL_RelinkEntities =============== */ -static void CL_RelinkNetworkEntities(void) +static void CL_RelinkNetworkEntities(int drawmask) { entity_t *ent; - int i; + int i, k; - ent = &cl.viewent; - ent->state_previous = ent->state_current; - ent->state_current = defaultstate; - ent->state_current.time = cl.time; - ent->state_current.number = -1; - ent->state_current.active = true; - ent->state_current.modelindex = cl.stats[STAT_WEAPON]; - ent->state_current.frame = cl.stats[STAT_WEAPONFRAME]; - ent->state_current.flags = RENDER_VIEWMODEL; - if ((cl.stats[STAT_HEALTH] <= 0 && cl_deathnoviewmodel.integer) || cl.intermission) - ent->state_current.modelindex = 0; - else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + if(!csqc_loaded) { - if (gamemode == GAME_TRANSFUSION) - ent->state_current.alpha = 128; - else + ent = &cl.viewent; + ent->state_previous = ent->state_current; + ent->state_current = defaultstate; + ent->state_current.time = cl.time; + ent->state_current.number = -1; + ent->state_current.active = true; + ent->state_current.modelindex = cl.stats[STAT_WEAPON]; + ent->state_current.frame = cl.stats[STAT_WEAPONFRAME]; + ent->state_current.flags = RENDER_VIEWMODEL; + if ((cl.stats[STAT_HEALTH] <= 0 && cl_deathnoviewmodel.integer) || cl.intermission) ent->state_current.modelindex = 0; - } + else if (cl.stats[STAT_ITEMS] & IT_INVISIBILITY) + { + if (gamemode == GAME_TRANSFUSION) + ent->state_current.alpha = 128; + else + ent->state_current.modelindex = 0; + } - // reset animation interpolation on weaponmodel if model changed - if (ent->state_previous.modelindex != ent->state_current.modelindex) - { - ent->render.frame = ent->render.frame1 = ent->render.frame2 = ent->state_current.frame; - ent->render.frame1time = ent->render.frame2time = cl.time; - ent->render.framelerp = 1; + // reset animation interpolation on weaponmodel if model changed + if (ent->state_previous.modelindex != ent->state_current.modelindex) + { + ent->render.frame = ent->render.frame1 = ent->render.frame2 = ent->state_current.frame; + ent->render.frame1time = ent->render.frame2time = cl.time; + ent->render.framelerp = 1; + } } // start on the entity after the world entitylinkframenumber++; - for (i = 1;i < cl_num_entities;i++) + if(drawmask & ENTMASK_ENGINE || !csqc_loaded) { - if (cl_entities_active[i]) + for (i = 1;i < cl_num_entities;i++) { - ent = cl_entities + i; - if (ent->state_current.active) - CL_LinkNetworkEntity(ent); - else - cl_entities_active[i] = false; + if (cl_entities_active[i]) + { + ent = cl_entities + i; + if (!(drawmask & ENTMASK_ENGINEVIEWMODELS)) + if (ent->state_current.flags & RENDER_VIEWMODEL) //[515]: csqc drawmask + { + cl_entities_active[i] = false; + continue; + } + if (ent->state_current.active) + CL_LinkNetworkEntity(ent); + else + cl_entities_active[i] = false; + } } } - CL_LinkNetworkEntity(&cl.viewent); + + //[515]: csqc + if(csqc_loaded) + { + for (i=1,k=cl_num_csqcentities;k;i++) + { + if (cl_csqcentities_active[i]) + { + --k; + ent = cl_csqcentities + i; + if (ent->state_current.active) + CL_LinkNetworkEntity(ent); + else + cl_csqcentities_active[i] = false; + } + } + } + else + CL_LinkNetworkEntity(&cl.viewent); } static void CL_RelinkEffects(void) @@ -1188,7 +1306,10 @@ static void CL_RelinkEffects(void) ent->render.frame2time = e->frame2time; // normal stuff - ent->render.model = cl.model_precache[e->modelindex]; + if(e->modelindex < MAX_MODELS) + ent->render.model = cl.model_precache[e->modelindex]; + else + ent->render.model = cl.csqc_model_precache[65536-e->modelindex]; ent->render.frame = ent->render.frame2; ent->render.colormap = -1; // no special coloring ent->render.alpha = 1; @@ -1318,6 +1439,27 @@ void CL_LerpPlayer(float frac) } } +void CSQC_RelinkAllEntities (int drawmask) +{ + CL_RelinkNetworkEntities(drawmask); + if(drawmask & ENTMASK_ENGINE) + { + // move particles + CL_MoveParticles(); + R_MoveExplosions(); + } + + // link stuff + CL_RelinkWorld(); + CL_RelinkCSQCWorld(); //[515]: csqc + if(drawmask & ENTMASK_ENGINE) + { + CL_RelinkStaticEntities(); + CL_RelinkBeams(); + CL_RelinkEffects(); + } +} + /* =============== CL_ReadFromServer @@ -1326,6 +1468,7 @@ Read all incoming data from the server =============== */ extern void CL_ClientMovement_Replay(); + int CL_ReadFromServer(void) { CL_ReadDemoMessage(); @@ -1347,17 +1490,23 @@ int CL_ReadFromServer(void) // relink network entities (note: this sets up the view!) CL_ClientMovement_Replay(); - CL_RelinkNetworkEntities(); - - // move particles - CL_MoveParticles(); - R_MoveExplosions(); - - // link stuff - CL_RelinkWorld(); - CL_RelinkStaticEntities(); - CL_RelinkBeams(); - CL_RelinkEffects(); + if(!csqc_loaded) //[515]: csqc + { + CL_RelinkNetworkEntities(65536); + + // move particles + CL_MoveParticles(); + R_MoveExplosions(); + + // link stuff + CL_RelinkWorld(); + CL_RelinkCSQCWorld(); //[515]: csqc + CL_RelinkStaticEntities(); + CL_RelinkBeams(); + CL_RelinkEffects(); + } + else + csqc_frame = true; // run cgame code (which can add more entities) CL_CGVM_Frame(); @@ -1496,6 +1645,9 @@ void CL_Init (void) // // register our commands // + Cvar_RegisterVariable (&csqc_progname); + Cvar_RegisterVariable (&csqc_progcrc); + Cvar_RegisterVariable (&cl_upspeed); Cvar_RegisterVariable (&cl_forwardspeed); Cvar_RegisterVariable (&cl_backspeed); diff --git a/cl_parse.c b/cl_parse.c index 8ca0b336..90214057 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "quakedef.h" #include "cdaudio.h" #include "cl_collision.h" +#include "csprogs.h" char *svc_strings[128] = { @@ -90,7 +91,7 @@ char *svc_strings[128] = "svc_spawnbaseline2", // 55 // short modelindex instead of byte "svc_spawnstatic2", // 56 // short modelindex instead of byte "svc_entities", // 57 // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata - "svc_unusedlh3", // 58 + "svc_csqcentities", // 58 // [short] entnum [variable length] entitydata ... [short] 0x0000 "svc_spawnstaticsound2", // 59 // [coord3] [short] samp [byte] vol [byte] aten }; @@ -390,6 +391,7 @@ void CL_ParseServerInfo (void) // disable until we get textures for it R_ResetSkyBox(); + memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache)); //[515]: csqc memset(cl.model_precache, 0, sizeof(cl.model_precache)); memset(cl.sound_precache, 0, sizeof(cl.sound_precache)); @@ -500,8 +502,8 @@ void CL_ValidateState(entity_state_t *s) if (!s->active) return; - if (s->modelindex >= MAX_MODELS) - Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)", s->modelindex, MAX_MODELS); + if (s->modelindex >= MAX_MODELS && (65536-s->modelindex) >= MAX_MODELS) + Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)\n", s->modelindex, MAX_MODELS); // colormap is client index + 1 if ((!s->flags & RENDER_COLORMAPPED) && s->colormap > cl.maxclients) @@ -695,6 +697,7 @@ void CL_ParseClientdata (void) cl.stats[STAT_ITEMS] = MSG_ReadLong (); cl.onground = (bits & SU_ONGROUND) != 0; + csqc_onground = cl.onground; //[515]: cause without this csqc will receive not right value on svc_print =/ cl.inwater = (bits & SU_INWATER) != 0; if (cl.protocol == PROTOCOL_DARKPLACES5) @@ -1331,6 +1334,14 @@ void CL_ParseTempEntity(void) #define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf("%3i:%s\n", msg_readcount-1, x); +//[515]: csqc +void CL_VM_Init (void); +qboolean CL_VM_Parse_TempEntity (void); +void CL_VM_Parse_StuffCmd (const char *msg); +void CL_VM_Parse_CenterPrint (const char *msg); +void CSQC_AddPrintText (const char *msg); +void CSQC_ReadEntities (void); +// static unsigned char cgamenetbuffer[65536]; /* @@ -1475,15 +1486,15 @@ void CL_ParseServerMessage(void) break; case svc_print: - Con_Print(MSG_ReadString()); + CSQC_AddPrintText(MSG_ReadString()); //[515]: csqc break; case svc_centerprint: - SCR_CenterPrint(MSG_ReadString ()); + CL_VM_Parse_CenterPrint(MSG_ReadString ()); //[515]: csqc break; case svc_stufftext: - Cbuf_AddText (MSG_ReadString ()); + CL_VM_Parse_StuffCmd(MSG_ReadString ()); //[515]: csqc break; case svc_damage: @@ -1492,6 +1503,7 @@ void CL_ParseServerMessage(void) case svc_serverinfo: CL_ParseServerInfo (); + CL_VM_Init(); //[515]: init csqc break; case svc_setangle: @@ -1622,7 +1634,8 @@ void CL_ParseServerMessage(void) CL_ParseStatic (true); break; case svc_temp_entity: - CL_ParseTempEntity (); + if(!CL_VM_Parse_TempEntity()) + CL_ParseTempEntity (); break; case svc_setpause: @@ -1761,6 +1774,9 @@ void CL_ParseServerMessage(void) else EntityFrame5_CL_ReadFrame(); break; + case svc_csqcentities: + CSQC_ReadEntities(); + break; } } diff --git a/cl_screen.c b/cl_screen.c index 3e181fbd..20f4ac79 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -3,6 +3,7 @@ #include "cl_video.h" #include "jpeg.h" #include "cl_collision.h" +#include "csprogs.h" cvar_t scr_viewsize = {CVAR_SAVE, "viewsize","100"}; cvar_t scr_fov = {CVAR_SAVE, "fov","90"}; // 1 - 170 @@ -758,7 +759,7 @@ void DrawQ_Mesh (drawqueuemesh_t *mesh, int flags) r_refdef.drawqueuesize += dq->size; } -void DrawQ_Lines (drawqueuemesh_t *mesh, int flags) +void DrawQ_LineLoop (drawqueuemesh_t *mesh, int flags) { int size; void *p; @@ -1520,7 +1521,8 @@ void CL_UpdateScreen(void) } R_Shadow_EditLights_DrawSelectedLightProperties(); - SCR_DrawConsole(); + if(!csqc_loaded) + SCR_DrawConsole(); SCR_DrawBrand(); diff --git a/cl_screen.h b/cl_screen.h index d58d7642..b1663a15 100644 --- a/cl_screen.h +++ b/cl_screen.h @@ -68,6 +68,8 @@ void DrawQ_SetClipArea(float x, float y, float width, float height); void DrawQ_ResetClipArea(void); // draw a line void DrawQ_Line(float width, float x1, float y1, float x2, float y2, float r, float g, float b, float alpha, int flags); +// draw a line loop +void DrawQ_LineLoop(drawqueuemesh_t *mesh, int flags); void SHOWLMP_decodehide(void); void SHOWLMP_decodeshow(void); diff --git a/client.h b/client.h index 1f815d72..aa323fcd 100644 --- a/client.h +++ b/client.h @@ -323,6 +323,7 @@ entity_persistent_t; typedef struct entity_s { + qboolean csqc; // baseline state (default values) entity_state_t state_baseline; // previous state (interpolating from this) @@ -476,6 +477,14 @@ typedef struct client_movementqueue_s } client_movementqueue_t; +//[515]: csqc +typedef struct +{ + qboolean drawworld; + qboolean drawenginesbar; + qboolean drawcrosshair; +}csqc_vidvars_t; + // // the client_state_t structure is wiped completely at every // server signon @@ -553,6 +562,12 @@ typedef struct client_state_s float driftmove; double laststop; +//[515]: added for csqc purposes + float sensitivityscale; + csqc_vidvars_t csqc_vidvars; //[515]: these parms must be set to true by default + qboolean csqc_wantsmousemove; + struct model_s *csqc_model_precache[MAX_MODELS]; + // local amount for smoothing stepups //float crouch; @@ -663,6 +678,9 @@ extern cvar_t cl_anglespeedkey; extern cvar_t cl_autofire; +extern cvar_t csqc_progname; //[515]: csqc crc check and right csprogs name according to progs.dat +extern cvar_t csqc_progcrc; + extern cvar_t cl_shownet; extern cvar_t cl_nolerp; @@ -697,13 +715,16 @@ extern vec3_t cl_playercrouchmaxs; // these are updated by CL_ClearState extern int cl_num_entities; +extern int cl_num_csqcentities; extern int cl_num_static_entities; extern int cl_num_temp_entities; extern int cl_num_brushmodel_entities; extern mempool_t *cl_mempool; extern entity_t *cl_entities; +extern entity_t *cl_csqcentities; extern unsigned char *cl_entities_active; +extern unsigned char *cl_csqcentities_active; extern entity_t *cl_static_entities; extern entity_t *cl_temp_entities; extern int *cl_brushmodel_entities; diff --git a/clprogdefs.h b/clprogdefs.h index 900831d3..44c9ba22 100644 --- a/clprogdefs.h +++ b/clprogdefs.h @@ -1,12 +1,96 @@ /* file generated by qcc, do not modify */ + +#ifndef CLPROGDEFS_H +#define CLPROGDEFS_H + typedef struct cl_globalvars_s { - int pad[28]; + int pad[28]; + int self; + int other; + int world; + float time; + float frametime; + float player_localentnum; + float player_localnum; + float maxclients; + float clientcommandframe; + float servercommandframe; + string_t mapname; + vec3_t v_forward; + vec3_t v_up; + vec3_t v_right; + float trace_allsolid; + float trace_startsolid; + float trace_fraction; + vec3_t trace_endpos; + vec3_t trace_plane_normal; + float trace_plane_dist; + int trace_ent; + float trace_inopen; + float trace_inwater; + func_t CSQC_Init; + func_t CSQC_Shutdown; + func_t CSQC_InputEvent; + func_t CSQC_UpdateView; + func_t CSQC_ConsoleCommand; + vec3_t pmove_org; + vec3_t pmove_vel; + vec3_t pmove_mins; + vec3_t pmove_maxs; + float input_timelength; + vec3_t input_angles; + vec3_t input_movevalues; + float input_buttons; + float movevar_gravity; + float movevar_stopspeed; + float movevar_maxspeed; + float movevar_spectatormaxspeed; + float movevar_accelerate; + float movevar_airaccelerate; + float movevar_wateraccelerate; + float movevar_friction; + float movevar_waterfriction; + float movevar_entgravity; } cl_globalvars_t; -/*typedef struct cl_entvars_s +typedef struct cl_entvars_s { -} cl_entvars_t;*/ + float modelindex; + vec3_t absmin; + vec3_t absmax; + float entnum; + float drawmask; + func_t predraw; + float movetype; + float solid; + vec3_t origin; + vec3_t oldorigin; + vec3_t velocity; + vec3_t angles; + vec3_t avelocity; + string_t classname; + string_t model; + float frame; + float skin; + float effects; + vec3_t mins; + vec3_t maxs; + vec3_t size; + func_t touch; + func_t use; + func_t think; + func_t blocked; + float nextthink; + int chain; + string_t netname; + int enemy; + float flags; + float colormap; + int owner; +} cl_entvars_t; + +#define CL_PROGHEADER_CRC 52195 -#define CL_PROGHEADER_CRC 12923 +#endif diff --git a/clvm_cmds.c b/clvm_cmds.c new file mode 100644 index 00000000..f6b8ea3b --- /dev/null +++ b/clvm_cmds.c @@ -0,0 +1,2994 @@ +#include "prvm_cmds.h" +#include "csprogs.h" +#include "cl_collision.h" + +//============================================================================ +// Client +//[515]: unsolved PROBLEMS +//- finish player physics code (cs_runplayerphysics) +//- fix R_AddDynamicLight +//- EntWasFreed ? +//- RF_DEPTHHACK is not like it should be +//- add builtin that sets cl.viewangles instead of reading "input_angles" global +//- finish lines support for R_Polygon*** +//- insert selecttraceline into traceline somehow + +//4 feature darkplaces csqc: add builtin to clientside qc for reading triangles of model meshes (useful to orient a ui along a triangle of a model mesh) +//4 feature darkplaces csqc: add builtins to clientside qc for gl calls + +#ifndef PF_WARNING +#define PF_WARNING(s) do{Con_Printf(s);PRVM_PrintState();return;}while(0) +#endif + +//[515]: really need new list ? +char *vm_cl_extensions = +"DP_CON_SET " +"DP_CON_SETA " +"DP_CON_STARTMAP " +"DP_EF_ADDITIVE " +"DP_EF_BLUE " +"DP_EF_FLAME " +"DP_EF_FULLBRIGHT " +"DP_EF_NODEPTHTEST " +"DP_EF_NODRAW " +"DP_EF_NOSHADOW " +"DP_EF_RED " +"DP_EF_STARDUST " +"DP_ENT_ALPHA " +"DP_ENT_CUSTOMCOLORMAP " +"DP_ENT_GLOW " +"DP_ENT_SCALE " +"DP_GFX_EXTERNALTEXTURES " +"DP_GFX_FOG " +"DP_GFX_QUAKE3MODELTAGS " +"DP_GFX_SKINFILES " +"DP_GFX_SKYBOX " +"DP_HALFLIFE_MAP " +"DP_HALFLIFE_MAP_CVAR " +"DP_HALFLIFE_SPRITE " +"DP_INPUTBUTTONS " +"DP_LITSPRITES " +"DP_LITSUPPORT " +"DP_MONSTERWALK " +"DP_MOVETYPEBOUNCEMISSILE " +"DP_MOVETYPEFOLLOW " +"DP_QC_CHANGEPITCH " +"DP_QC_COPYENTITY " +"DP_QC_CVAR_STRING " +"DP_QC_ETOS " +"DP_QC_FINDCHAIN " +"DP_QC_FINDCHAINFLAGS " +"DP_QC_FINDCHAINFLOAT " +"DP_QC_FINDFLAGS " +"DP_QC_FINDFLOAT " +"DP_QC_FS_SEARCH " // Black: same as in the menu qc +"DP_QC_GETLIGHT " +"DP_QC_GETSURFACE " +"DP_QC_GETTAGINFO " +"DP_QC_MINMAXBOUND " +"DP_QC_MULTIPLETEMPSTRINGS " +"DP_QC_RANDOMVEC " +"DP_QC_SINCOSSQRTPOW " +//"DP_QC_STRINGBUFFERS " //[515]: not needed ? +"DP_QC_TRACEBOX " +//"DP_QC_TRACETOSS " +"DP_QC_TRACE_MOVETYPE_HITMODEL " +"DP_QC_TRACE_MOVETYPE_WORLDONLY " +"DP_QC_VECTORVECTORS " +"DP_QUAKE2_MODEL " +"DP_QUAKE2_SPRITE " +"DP_QUAKE3_MAP " +"DP_QUAKE3_MODEL " +"DP_REGISTERCVAR " +"DP_SND_DIRECTIONLESSATTNNONE " +"DP_SND_FAKETRACKS " +"DP_SND_OGGVORBIS " +"DP_SND_STEREOWAV " +"DP_SOLIDCORPSE " +"DP_SPRITE32 " +"DP_SV_EFFECT " +"DP_SV_ROTATINGBMODEL " +"DP_SV_SLOWMO " +"DP_TE_BLOOD " +"DP_TE_BLOODSHOWER " +"DP_TE_CUSTOMFLASH " +"DP_TE_EXPLOSIONRGB " +"DP_TE_FLAMEJET " +"DP_TE_PARTICLECUBE " +"DP_TE_PARTICLERAIN " +"DP_TE_PARTICLESNOW " +"DP_TE_PLASMABURN " +"DP_TE_QUADEFFECTS1 " +"DP_TE_SMALLFLASH " +"DP_TE_SPARK " +"DP_TE_STANDARDEFFECTBUILTINS " +"EXT_BITSHIFT " +"EXT_CSQC " +"FRIK_FILE " +"KRIMZON_SV_PARSECLIENTCOMMAND " +"NEH_CMD_PLAY2 " +"NXQ_GFX_LETTERBOX " +"PRYDON_CLIENTCURSOR " +"TENEBRAE_GFX_DLIGHTS " +"TW_SV_STEPCONTROL " +"NEXUIZ_PLAYERMODEL " +"NEXUIZ_PLAYERSKIN " +; + +sfx_t *S_FindName(const char *name); +int CL_PointQ1Contents(const vec3_t p); +void PF_registercvar (void); +int Sbar_GetPlayer (int index); +void Sbar_SortFrags (void); +void CL_FindNonSolidLocation(const vec3_t in, vec3_t out, vec_t radius); +void CL_ExpandCSQCEntities(int num); +void CSQC_RelinkAllEntities (int drawmask); +void CSQC_RelinkCSQCEntities (void); +char *Key_GetBind (int key); + + + + + +// #1 void(vector ang) makevectors +void VM_CL_makevectors (void) +{ + VM_SAFEPARMCOUNT(1, VM_CL_makevectors); + AngleVectors (PRVM_G_VECTOR(OFS_PARM0), prog->globals.client->v_forward, prog->globals.client->v_right, prog->globals.client->v_up); +} + +// #2 void(entity e, vector o) setorigin +void VM_CL_setorigin (void) +{ + prvm_edict_t *e; + float *org; + + e = PRVM_G_EDICT(OFS_PARM0); + if (e == prog->edicts) + PF_WARNING("setorigin: can not modify world entity\n"); + if (e->priv.required->free) + PF_WARNING("setorigin: can not modify free entity\n"); + org = PRVM_G_VECTOR(OFS_PARM1); + VectorCopy (org, e->fields.client->origin); +} + +// #3 void(entity e, string m) setmodel +void VM_CL_setmodel (void) +{ + prvm_edict_t *e; + const char *m; + struct model_s *mod; + int i; + + VM_SAFEPARMCOUNT(2, VM_CL_setmodel); + + e = PRVM_G_EDICT(OFS_PARM0); + m = PRVM_G_STRING(OFS_PARM1); + for(i=0;iname, m)) + { + e->fields.client->model = PRVM_SetEngineString(cl.csqc_model_precache[i]->name); + e->fields.client->modelindex = -(i+1); + return; + } + + for (i=0, mod = cl.model_precache[0] ; i < MAX_MODELS ; i++, mod = cl.model_precache[i]) + if(mod) + if(!strcmp(mod->name, m)) + { + e->fields.client->model = PRVM_SetEngineString(mod->name); + e->fields.client->modelindex = i; + return; + } + e->fields.client->modelindex = 0; + e->fields.client->model = 0; +} + +// #4 void(entity e, vector min, vector max) setsize +void VM_CL_setsize (void) +{ + prvm_edict_t *e; + float *min, *max; + VM_SAFEPARMCOUNT(3, VM_CL_setsize); + + e = PRVM_G_EDICT(OFS_PARM0); + if (e == prog->edicts) + PF_WARNING("setsize: can not modify world entity\n"); + if (e->priv.server->free) + PF_WARNING("setsize: can not modify free entity\n"); + min = PRVM_G_VECTOR(OFS_PARM1); + max = PRVM_G_VECTOR(OFS_PARM2); + + VectorCopy (min, e->fields.client->mins); + VectorCopy (max, e->fields.client->maxs); + VectorSubtract (max, min, e->fields.client->size); +} + +// #8 void(entity e, float chan, string samp) sound +void VM_CL_sound (void) +{ + const char *sample; + int channel; + prvm_edict_t *entity; + int volume; + float attenuation; + + VM_SAFEPARMCOUNT(5, VM_CL_sound); + + entity = PRVM_G_EDICT(OFS_PARM0); + channel = PRVM_G_FLOAT(OFS_PARM1); + sample = PRVM_G_STRING(OFS_PARM2); + volume = PRVM_G_FLOAT(OFS_PARM3)*255; + attenuation = PRVM_G_FLOAT(OFS_PARM4); + + if (volume < 0 || volume > 255) + PF_WARNING("VM_CL_sound: volume must be in range 0-1\n"); + + if (attenuation < 0 || attenuation > 4) + PF_WARNING("VM_CL_sound: attenuation must be in range 0-4\n"); + + if (channel < 0 || channel > 7) + PF_WARNING("VM_CL_sound: channel must be in range 0-7\n"); + + S_StartSound(32768 + PRVM_NUM_FOR_EDICT(entity), channel, S_FindName(sample), entity->fields.client->origin, volume, attenuation); +} + +// #14 entity() spawn +void VM_CL_spawn (void) +{ + prvm_edict_t *ed; + ed = PRVM_ED_Alloc(); + ed->fields.client->entnum = PRVM_NUM_FOR_EDICT(ed); //[515]: not needed any more ? + VM_RETURN_EDICT(ed); +} + +// #16 float(vector v1, vector v2, float tryents) traceline +void VM_CL_traceline (void) +{ + float *v1, *v2; + trace_t trace; + int ent; + + v1 = PRVM_G_VECTOR(OFS_PARM0); + v2 = PRVM_G_VECTOR(OFS_PARM1); + + trace = CL_TraceBox(v1, vec3_origin, vec3_origin, v2, 1, &ent, 1, false); + + prog->globals.client->trace_allsolid = trace.allsolid; + prog->globals.client->trace_startsolid = trace.startsolid; + prog->globals.client->trace_fraction = trace.fraction; + prog->globals.client->trace_inwater = trace.inwater; + prog->globals.client->trace_inopen = trace.inopen; + VectorCopy (trace.endpos, prog->globals.client->trace_endpos); + VectorCopy (trace.plane.normal, prog->globals.client->trace_plane_normal); + prog->globals.client->trace_plane_dist = trace.plane.dist; + if (ent) + prog->globals.client->trace_ent = ent; + else + prog->globals.client->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); +} + +// #19 void(string s) precache_sound +void VM_CL_precache_sound (void) +{ + const char *n; + VM_SAFEPARMCOUNT(1, VM_CL_precache_sound); + n = PRVM_G_STRING(OFS_PARM0); + S_PrecacheSound(n, true, false); +} + +// #20 void(string s) precache_model +void VM_CL_precache_model (void) +{ + const char *name; + int i; + model_t *m; + + VM_SAFEPARMCOUNT(1, VM_CL_precache_model); + + name = PRVM_G_STRING(OFS_PARM0); + for(i=1;iname, name)) + { + i = -(i+1); + break; + } + if(i) + { + PRVM_G_FLOAT(OFS_RETURN) = i; + return; + } + PRVM_G_FLOAT(OFS_RETURN) = 0; + m = Mod_ForName(name, false, false, false); + if(m && m->loaded) + { + for(i=1;iedicts); + for(k=0,i=1; inum_edicts ;i++, ent = PRVM_NEXT_EDICT(ent)) + { + if (ent->priv.required->free) + continue; +// VectorAdd(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->absmin); +// VectorAdd(ent->fields.client->origin, ent->fields.client->maxs, ent->fields.client->absmax); + if(BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax)) + list[k++] = ent; + } + return k; +} + +// #22 entity(vector org, float rad) findradius +void VM_CL_findradius (void) +{ + prvm_edict_t *ent, *chain; + vec_t radius, radius2; + vec3_t org, eorg, mins, maxs; + int i, numtouchedicts; + prvm_edict_t *touchedicts[MAX_EDICTS]; + + chain = (prvm_edict_t *)prog->edicts; + + VectorCopy(PRVM_G_VECTOR(OFS_PARM0), org); + radius = PRVM_G_FLOAT(OFS_PARM1); + radius2 = radius * radius; + + mins[0] = org[0] - (radius + 1); + mins[1] = org[1] - (radius + 1); + mins[2] = org[2] - (radius + 1); + maxs[0] = org[0] + (radius + 1); + maxs[1] = org[1] + (radius + 1); + maxs[2] = org[2] + (radius + 1); + numtouchedicts = CSQC_EntitiesInBox(mins, maxs, MAX_EDICTS, touchedicts); + if (numtouchedicts > MAX_EDICTS) + { + // this never happens //[515]: for what then ? + Con_Printf("CSQC_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS); + numtouchedicts = MAX_EDICTS; + } + for (i = 0;i < numtouchedicts;i++) + { + ent = touchedicts[i]; + // Quake did not return non-solid entities but darkplaces does + // (note: this is the reason you can't blow up fallen zombies) + if (ent->fields.client->solid == SOLID_NOT && !sv_gameplayfix_blowupfallenzombies.integer) + continue; + // LordHavoc: compare against bounding box rather than center so it + // doesn't miss large objects, and use DotProduct instead of Length + // for a major speedup + VectorSubtract(org, ent->fields.client->origin, eorg); + if (sv_gameplayfix_findradiusdistancetobox.integer) + { + eorg[0] -= bound(ent->fields.client->mins[0], eorg[0], ent->fields.client->maxs[0]); + eorg[1] -= bound(ent->fields.client->mins[1], eorg[1], ent->fields.client->maxs[1]); + eorg[2] -= bound(ent->fields.client->mins[2], eorg[2], ent->fields.client->maxs[2]); + } + else + 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); + chain = ent; + } + } + + VM_RETURN_EDICT(chain); +} + +// #34 float() droptofloor +void VM_CL_droptofloor (void) +{ + prvm_edict_t *ent; + vec3_t end; + trace_t trace; + int i; + + // assume failure if it returns early + PRVM_G_FLOAT(OFS_RETURN) = 0; + + ent = PRVM_PROG_TO_EDICT(prog->globals.client->self); + if (ent == prog->edicts) + PF_WARNING("droptofloor: can not modify world entity\n"); + if (ent->priv.server->free) + PF_WARNING("droptofloor: can not modify free entity\n"); + + VectorCopy (ent->fields.client->origin, end); + end[2] -= 256; + + trace = CL_TraceBox(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, end, 1, &i, 1, false); + + if (trace.fraction != 1) + { + VectorCopy (trace.endpos, ent->fields.client->origin); + ent->fields.client->flags = (int)ent->fields.client->flags | FL_ONGROUND; +// ent->fields.client->groundentity = PRVM_EDICT_TO_PROG(trace.ent); + PRVM_G_FLOAT(OFS_RETURN) = 1; + // if support is destroyed, keep suspended (gross hack for floating items in various maps) +// ent->priv.server->suspendedinairflag = true; + } +} + +// #35 void(float style, string value) lightstyle +void VM_CL_lightstyle (void) +{ + int i; + const char *c; + + VM_SAFEPARMCOUNT(2, VM_CL_lightstyle); + + i = PRVM_G_FLOAT(OFS_PARM0); + c = PRVM_G_STRING(OFS_PARM1); + if (i >= MAX_LIGHTSTYLES) + PF_WARNING("VM_CL_lightstyle >= MAX_LIGHTSTYLES\n"); + strlcpy (cl_lightstyle[i].map, MSG_ReadString(), sizeof (cl_lightstyle[i].map)); + cl_lightstyle[i].map[MAX_STYLESTRING - 1] = 0; + cl_lightstyle[i].length = (int)strlen(cl_lightstyle[i].map); +} + +// #40 float(entity e) checkbottom +void VM_CL_checkbottom (void) +{ + static int cs_yes, cs_no; + prvm_edict_t *ent; + vec3_t mins, maxs, start, stop; + trace_t trace; + int x, y, hit; + float mid, bottom; + + VM_SAFEPARMCOUNT(1, VM_CL_checkbottom); + ent = PRVM_G_EDICT(OFS_PARM0); + PRVM_G_FLOAT(OFS_RETURN) = 0; + + VectorAdd (ent->fields.client->origin, ent->fields.client->mins, mins); + VectorAdd (ent->fields.client->origin, ent->fields.client->maxs, maxs); + +// if all of the points under the corners are solid world, don't bother +// with the tougher checks +// the corners must be within 16 of the midpoint + start[2] = mins[2] - 1; + for (x=0 ; x<=1 ; x++) + for (y=0 ; y<=1 ; y++) + { + start[0] = x ? maxs[0] : mins[0]; + start[1] = y ? maxs[1] : mins[1]; + if (!(CL_PointSuperContents(start) & SUPERCONTENTS_SOLID)) + goto realcheck; + } + + cs_yes++; + PRVM_G_FLOAT(OFS_RETURN) = true; + return; // we got out easy + +realcheck: + cs_no++; +// +// check it for real... +// + start[2] = mins[2]; + +// the midpoint must be within 16 of the bottom + start[0] = stop[0] = (mins[0] + maxs[0])*0.5; + start[1] = stop[1] = (mins[1] + maxs[1])*0.5; + stop[2] = start[2] - 2*sv_stepheight.value; + trace = CL_TraceBox (start, vec3_origin, vec3_origin, stop, 1, &hit, 1, true); + + if (trace.fraction == 1.0) + return; + + mid = bottom = trace.endpos[2]; + +// the corners must be within 16 of the midpoint + for (x=0 ; x<=1 ; x++) + for (y=0 ; y<=1 ; y++) + { + start[0] = stop[0] = x ? maxs[0] : mins[0]; + start[1] = stop[1] = y ? maxs[1] : mins[1]; + + trace = CL_TraceBox (start, vec3_origin, vec3_origin, stop, 1, &hit, 1, true); + + if (trace.fraction != 1.0 && trace.endpos[2] > bottom) + bottom = trace.endpos[2]; + if (trace.fraction == 1.0 || mid - trace.endpos[2] > sv_stepheight.value) + return; + } + + cs_yes++; + PRVM_G_FLOAT(OFS_RETURN) = true; +} + +// #41 float(vector v) pointcontents +void VM_CL_pointcontents (void) +{ + VM_SAFEPARMCOUNT(1, VM_CL_pointcontents); + PRVM_G_FLOAT(OFS_RETURN) = CL_PointQ1Contents(PRVM_G_VECTOR(OFS_PARM0)); +} + +// #48 void(vector o, vector d, float color, float count) particle +void VM_CL_particle (void) +{ + float *org, *dir; + int count; + unsigned char color; + VM_SAFEPARMCOUNT(4, VM_CL_particle); + + org = PRVM_G_VECTOR(OFS_PARM0); + dir = PRVM_G_VECTOR(OFS_PARM1); + color = PRVM_G_FLOAT(OFS_PARM2); + count = PRVM_G_FLOAT(OFS_PARM3); + if (cl_particles_blood_bloodhack.integer) + { + if (color == 73) + { + // regular blood + CL_BloodPuff(org, dir, count / 2); + return; + } + if (color == 225) + { + // lightning blood + CL_BloodPuff(org, dir, count / 2); + return; + } + } + CL_RunParticleEffect (org, dir, color, count); +} + +// #49 void(entity ent, float ideal_yaw, float speed_yaw) ChangeYaw +void VM_CL_changeyaw (void) +{ + prvm_edict_t *ent; + float ideal, current, move, speed; + VM_SAFEPARMCOUNT(3, VM_CL_changeyaw); + + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent == prog->edicts) + PF_WARNING("changeyaw: can not modify world entity\n"); + if (ent->priv.server->free) + PF_WARNING("changeyaw: can not modify free entity\n"); + current = ANGLEMOD(ent->fields.client->angles[1]); + ideal = PRVM_G_FLOAT(OFS_PARM1); + speed = PRVM_G_FLOAT(OFS_PARM2); + + if (current == ideal) + return; + 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; + } + + ent->fields.client->angles[1] = ANGLEMOD (current + move); +} + +// #63 void(entity ent, float ideal_pitch, float speed_pitch) changepitch (DP_QC_CHANGEPITCH) +void VM_CL_changepitch (void) +{ + prvm_edict_t *ent; + float ideal, current, move, speed; + VM_SAFEPARMCOUNT(3, VM_CL_changepitch); + + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent == prog->edicts) + PF_WARNING("changepitch: can not modify world entity\n"); + if (ent->priv.server->free) + PF_WARNING("changepitch: can not modify free entity\n"); + current = ANGLEMOD( ent->fields.client->angles[0] ); + ideal = PRVM_G_FLOAT(OFS_PARM1); + speed = PRVM_G_FLOAT(OFS_PARM2); + + if (current == ideal) + return; + 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; + } + + ent->fields.client->angles[0] = ANGLEMOD (current + move); +} + +// #64 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS) +void VM_CL_tracetoss (void) +{ +/* trace_t trace; + prvm_edict_t *ent; + prvm_edict_t *ignore; + + ent = PRVM_G_EDICT(OFS_PARM0); + if (ent == prog->edicts) + PF_WARNING("tracetoss: can not use world entity\n"); + ignore = PRVM_G_EDICT(OFS_PARM1); + +//FIXME + trace = SV_Trace_Toss (ent, ignore); + + prog->globals.server->trace_allsolid = trace.allsolid; + prog->globals.server->trace_startsolid = trace.startsolid; + prog->globals.server->trace_fraction = trace.fraction; + prog->globals.server->trace_inwater = trace.inwater; + prog->globals.server->trace_inopen = trace.inopen; + VectorCopy (trace.endpos, prog->globals.server->trace_endpos); + VectorCopy (trace.plane.normal, prog->globals.server->trace_plane_normal); + prog->globals.server->trace_plane_dist = trace.plane.dist; + if (trace.ent) + prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(trace.ent); + else + prog->globals.server->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); +*/ +} + +// #74 void(vector pos, string samp, float vol, float atten) ambientsound +void VM_CL_ambientsound (void) +{ + float *f; + sfx_t *s; + VM_SAFEPARMCOUNT(4, VM_CL_ambientsound); + s = S_FindName(PRVM_G_STRING(OFS_PARM0)); + f = PRVM_G_VECTOR(OFS_PARM1); + S_StaticSound (s, f, PRVM_G_FLOAT(OFS_PARM2), PRVM_G_FLOAT(OFS_PARM3)*64); +} + +// #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX) +void VM_CL_tracebox (void) +{ + float *v1, *v2, *m1, *m2; + trace_t trace; + int ent; + + v1 = PRVM_G_VECTOR(OFS_PARM0); + m1 = PRVM_G_VECTOR(OFS_PARM1); + m2 = PRVM_G_VECTOR(OFS_PARM2); + v2 = PRVM_G_VECTOR(OFS_PARM3); + + trace = CL_TraceBox(v1, m1, m2, v2, 1, &ent, 1, false); + + prog->globals.client->trace_allsolid = trace.allsolid; + prog->globals.client->trace_startsolid = trace.startsolid; + prog->globals.client->trace_fraction = trace.fraction; + prog->globals.client->trace_inwater = trace.inwater; + prog->globals.client->trace_inopen = trace.inopen; + VectorCopy (trace.endpos, prog->globals.client->trace_endpos); + VectorCopy (trace.plane.normal, prog->globals.client->trace_plane_normal); + prog->globals.client->trace_plane_dist = trace.plane.dist; + if (ent) + prog->globals.client->trace_ent = ent; + else + prog->globals.client->trace_ent = PRVM_EDICT_TO_PROG(prog->edicts); +} + +// #92 vector(vector org) getlight (DP_QC_GETLIGHT) +void VM_CL_getlight (void) +{ + vec3_t ambientcolor, diffusecolor, diffusenormal; + vec_t *p; + + VM_SAFEPARMCOUNT(1, VM_CL_getlight); + + p = PRVM_G_VECTOR(OFS_PARM0); + VectorClear(ambientcolor); + VectorClear(diffusecolor); + VectorClear(diffusenormal); + if (cl.worldmodel && cl.worldmodel->brush.LightPoint) + cl.worldmodel->brush.LightPoint(cl.worldmodel, p, ambientcolor, diffusecolor, diffusenormal); + VectorMA(ambientcolor, 0.5, diffusecolor, PRVM_G_VECTOR(OFS_RETURN)); +} + + +//============================================================================ +//[515]: SCENE MANAGER builtins +void V_CalcRefdef (void);//view.c +void CSQC_R_ClearScreen (void);//gl_rmain.c +void CSQC_R_RenderScene (void);//gl_rmain.c +void CSQC_AddEntity (int n);//csprogs.c +void CSQC_ClearCSQCEntities (void); + +matrix4x4_t csqc_listenermatrix; +qboolean csqc_usecsqclistener = false, csqc_frame = false;//[515]: per-frame +qboolean csqc_onground; + +static char *particleeffect_names[] = +{ + "TE_SPIKE", + "TE_SUPERSPIKE", + "TE_GUNSHOT", + "TE_EXPLOSION", + "TE_TAREXPLOSION", + "TE_LIGHTNING1",//trail + "TE_LIGHTNING2",//trail + "TE_WIZSPIKE", + "TE_KNIGHTSPIKE", + "TE_LIGHTNING3",//trail + "TE_LAVASPLASH", + "TE_TELEPORT", + "TE_EXPLOSION2", + "TE_BEAM",//trail + "TE_EXPLOSION3", + "",//TE_LIGHTNING4NEH + "TE_BLOOD", + "TE_SPARK", + "",//TE_BLOODSHOWER + "TE_EXPLOSIONRGB", + "",//unused + "", + "", + "TE_GUNSHOTQUAD", + "TE_SPIKEQUAD", + "TE_SUPERSPIKEQUAD", + "TE_EXPLOSIONQUAD", + "", + "", + "", + "TE_FLAMEJET", + "TE_PLASMABURN", + "TE_TEI_G3", + "TE_TEI_SMOKE", + "TE_TEI_BIGEXPLOSION", + "TE_TEI_PLASMAHIT", + + + //trail effects (as modelflags) + "EF_ROCKET", + "EF_GRENADE", + "EF_GIB", + "EF_TRACER", + "EF_ZOMGIB", + "EF_TRACER2", + "EF_TRACER3", + "EF_NEH_CIGAR", + "EF_NEXUIZ_PLASMA", + "EF_GLOWTRAIL", +}; + +#define CSQC_TRAILSTART 36 +static const int particleeffects_num = sizeof(particleeffect_names)/sizeof(char*); + +static void CSQC_R_RecalcView (void) +{ + extern matrix4x4_t viewmodelmatrix; + Matrix4x4_CreateIdentity(&viewmodelmatrix); + Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix); + Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, csqc_origin[0], csqc_origin[1], csqc_origin[2], csqc_angles[0], csqc_angles[1], csqc_angles[2], 1); + Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, csqc_origin[0], csqc_origin[1], csqc_origin[2], csqc_angles[0], csqc_angles[1], csqc_angles[2], 0.3); +} + +//#300 void() clearscene (EXT_CSQC) +void VM_R_ClearScene (void) +{ + VM_SAFEPARMCOUNT(0, VM_R_ClearScene); +// CSQC_R_RecalcView(); + if(csqc_frame) + CSQC_ClearCSQCEntities(); + CSQC_R_ClearScreen(); +} + +//#301 void(float mask) addentities (EXT_CSQC) +void VM_R_AddEntities (void) +{ + VM_SAFEPARMCOUNT(1, VM_R_AddEntities); + csqc_drawmask = PRVM_G_FLOAT(OFS_PARM0); +} + +//#302 void(entity ent) addentity (EXT_CSQC) +void VM_R_AddEntity (void) +{ + VM_SAFEPARMCOUNT(1, VM_R_AddEntity); + CSQC_AddEntity(PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0))); +} + +//#303 float(float property, ...) setproperty (EXT_CSQC) +void VM_R_SetView (void) +{ + int c; + float *f; + float k; + + if(prog->argc < 2) + VM_SAFEPARMCOUNT(2, VM_R_SetView); + + c = PRVM_G_FLOAT(OFS_PARM0); + f = PRVM_G_VECTOR(OFS_PARM1); + k = PRVM_G_FLOAT(OFS_PARM1); + + switch(c) + { + case VF_MIN: r_refdef.x = f[0]; + r_refdef.y = f[1]; + break; + case VF_MIN_X: r_refdef.x = k; + break; + case VF_MIN_Y: r_refdef.y = k; + break; + case VF_SIZE: r_refdef.width = f[0]; + r_refdef.height = f[1]; + break; + case VF_SIZE_Y: r_refdef.width = k; + break; + case VF_SIZE_X: r_refdef.height = k; + break; + case VF_VIEWPORT: r_refdef.x = f[0]; + r_refdef.y = f[1]; + f = PRVM_G_VECTOR(OFS_PARM2); + r_refdef.width = f[0]; + r_refdef.height = f[1]; + break; + case VF_FOV: //r_refdef.fov_x = f[0]; // FIXME! + //r_refdef.fov_y = f[1]; // FIXME! + break; + case VF_FOVX: //r_refdef.fov_x = k; // FIXME! + break; + case VF_FOVY: //r_refdef.fov_y = k; // FIXME! + break; + case VF_ORIGIN: VectorCopy(f, csqc_origin); + CSQC_R_RecalcView(); + break; + case VF_ORIGIN_X: csqc_origin[0] = k; + CSQC_R_RecalcView(); + break; + case VF_ORIGIN_Y: csqc_origin[1] = k; + CSQC_R_RecalcView(); + break; + case VF_ORIGIN_Z: csqc_origin[2] = k; + CSQC_R_RecalcView(); + break; + case VF_ANGLES: VectorCopy(f, csqc_angles); + CSQC_R_RecalcView(); + break; + case VF_ANGLES_X: csqc_angles[0] = k; + CSQC_R_RecalcView(); + break; + case VF_ANGLES_Y: csqc_angles[1] = k; + CSQC_R_RecalcView(); + break; + case VF_ANGLES_Z: csqc_angles[2] = k; + CSQC_R_RecalcView(); + break; + case VF_DRAWWORLD: cl.csqc_vidvars.drawworld = k; + break; + case VF_DRAWENGINESBAR: cl.csqc_vidvars.drawenginesbar = k; + break; + case VF_DRAWCROSSHAIR: cl.csqc_vidvars.drawcrosshair = k; + break; + + case VF_CL_VIEWANGLES: VectorCopy(f, cl.viewangles); + break; + case VF_CL_VIEWANGLES_X:cl.viewangles[0] = k; + break; + case VF_CL_VIEWANGLES_Y:cl.viewangles[1] = k; + break; + case VF_CL_VIEWANGLES_Z:cl.viewangles[2] = k; + break; + + default: Con_Printf("VM_R_SetView : unknown parm %i\n", c); + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + PRVM_G_FLOAT(OFS_RETURN) = 1; +} + +//#304 void() renderscene (EXT_CSQC) +void VM_R_RenderScene (void) //#134 +{ + VM_SAFEPARMCOUNT(0, VM_R_RenderScene); + + if(csqc_frame) + { + CSQC_RelinkCSQCEntities(); + CSQC_RelinkAllEntities(csqc_drawmask); + } + + CSQC_R_RenderScene(); +} + +//#305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC) +void VM_R_AddDynamicLight (void) +{ + float *pos, *col; + matrix4x4_t tempmatrix; + VM_SAFEPARMCOUNT(3, VM_R_AddDynamicLight); + + pos = PRVM_G_VECTOR(OFS_PARM0); + col = PRVM_G_VECTOR(OFS_PARM2); + Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]); + CL_AllocDlight(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), col[0], col[1], col[2], 500, 0, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + //CL_AllocDlight(NULL, &tempmatrix, PRVM_G_FLOAT(OFS_PARM1), col[0], col[1], col[2], 500, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); +} + +//============================================================================ + +//#310 vector (vector v) cs_unproject (EXT_CSQC) +void VM_CL_unproject (void) +{ + float *f; + vec3_t temp; + + VM_SAFEPARMCOUNT(1, VM_CL_unproject); + f = PRVM_G_VECTOR(OFS_PARM0); + VectorSet(temp, f[2], f[0] * f[2] * -r_refdef.frustum_x * 2.0 / r_refdef.width, f[1] * f[2] * -r_refdef.frustum_y * 2.0 / r_refdef.height); + Matrix4x4_Transform(&r_refdef.viewentitymatrix, temp, PRVM_G_VECTOR(OFS_RETURN)); +} + +//#311 vector (vector v) cs_project (EXT_CSQC) +void VM_CL_project (void) +{ + float *f; + vec3_t v; + matrix4x4_t m; + + VM_SAFEPARMCOUNT(1, VM_CL_project); + f = PRVM_G_VECTOR(OFS_PARM0); + Matrix4x4_Invert_Simple(&m, &r_refdef.viewentitymatrix); + Matrix4x4_Transform(&m, f, v); + VectorSet(PRVM_G_VECTOR(OFS_RETURN), v[1]/v[0]/-r_refdef.frustum_x*0.5*r_refdef.width, v[2]/v[0]/-r_refdef.frustum_y*r_refdef.height*0.5, v[0]); +} + +//#330 float(float stnum) getstatf (EXT_CSQC) +void VM_CL_getstatf (void) +{ + int i; + union + { + float f; + int l; + }dat; + VM_SAFEPARMCOUNT(1, VM_CL_getstatf); + i = PRVM_G_FLOAT(OFS_PARM0); + if(i < 0 || i >= MAX_CL_STATS) + { + Con_Printf("VM_CL_getstatf: index>=MAX_CL_STATS or index<0\n"); + return; + } + dat.l = cl.stats[i]; + PRVM_G_FLOAT(OFS_RETURN) = dat.f; +} + +//#331 float(float stnum) getstati (EXT_CSQC) +void VM_CL_getstati (void) +{ + int i, index; + VM_SAFEPARMCOUNT(1, VM_CL_getstati); + index = PRVM_G_FLOAT(OFS_PARM0); + + if(index < 0 || index >= MAX_CL_STATS) + { + Con_Printf("VM_CL_getstati: index>=MAX_CL_STATS or index<0\n"); + return; + } + i = cl.stats[index]; + PRVM_G_FLOAT(OFS_RETURN) = i; +} + +//#332 string(float firststnum) getstats (EXT_CSQC) +void VM_CL_getstats (void) +{ + int i; + char *t; + VM_SAFEPARMCOUNT(1, VM_CL_getstats); + i = PRVM_G_FLOAT(OFS_PARM0); + if(i < 0 || i > MAX_CL_STATS-4) + { + Con_Printf("VM_CL_getstats: index>MAX_CL_STATS-4 or index<0\n"); + return; + } + t = VM_GetTempString(); + strlcpy(t, (char*)&cl.stats[i], 16); + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t); +} + +//#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC) +void VM_CL_setmodelindex (void) +{ + int i; + prvm_edict_t *t; + struct model_s *m; + + VM_SAFEPARMCOUNT(2, VM_CL_setmodelindex); + + t = PRVM_G_EDICT(OFS_PARM0); + i = (int)PRVM_G_FLOAT(OFS_PARM1); + + t->fields.client->model = 0; + t->fields.client->modelindex = 0; + + if(!i) + return; + if(i<0) + { + i = -(i+1); + if(i >= MAX_MODELS) + PF_WARNING("VM_CL_setmodelindex >= MAX_MODELS\n"); + m = cl.csqc_model_precache[i]; + } + else + if(i >= MAX_MODELS) + PF_WARNING("VM_CL_setmodelindex >= MAX_MODELS\n"); + else + m = cl.model_precache[i]; + if(!m) + PF_WARNING("VM_CL_setmodelindex: null model\n"); + t->fields.client->model = PRVM_SetEngineString(m->name); + t->fields.client->modelindex = i; +} + +//#334 string(float mdlindex) modelnameforindex (EXT_CSQC) +void VM_CL_modelnameforindex (void) +{ + int i; + + VM_SAFEPARMCOUNT(1, VM_CL_modelnameforindex); + + PRVM_G_INT(OFS_RETURN) = 0; + i = PRVM_G_FLOAT(OFS_PARM0); + if(i<0) + { + i = -(i+1); + if(i >= MAX_MODELS) + PF_WARNING("VM_CL_modelnameforindex >= MAX_MODELS\n"); + if(cl.csqc_model_precache[i]) + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(cl.csqc_model_precache[i]->name); + return; + } + if(i >= MAX_MODELS) + PF_WARNING("VM_CL_modelnameforindex >= MAX_MODELS\n"); + if(cl.model_precache[i]) + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(cl.model_precache[i]->name); +} + +//#335 float(string effectname) particleeffectnum (EXT_CSQC) +void VM_CL_particleeffectnum (void) +{ + const char *n; + int i; + VM_SAFEPARMCOUNT(1, VM_CL_particleeffectnum); + n = PRVM_G_STRING(OFS_PARM0); + for(i=0;ientity == ent && ent) + { + //b->entity = ent; + b->lightning = lightning; + b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0; + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + } + + // find a free beam + for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++) + { + if (!b->model || b->endtime < cl.time) + { + b->entity = ent; + b->lightning = lightning; + b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0; + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + } + Con_Print("beam list overflow!\n"); +} + +// #336 void(entity ent, float effectnum, vector start, vector end[, float color]) trailparticles (EXT_CSQC) +void VM_CL_trailparticles (void) +{ + int i, entnum, col; + float *start, *end; + entity_t *ent; + VM_SAFEPARMCOUNT(4, VM_CL_trailparticles); + + entnum = PRVM_NUM_FOR_EDICT(PRVM_G_EDICT(OFS_PARM0)); + i = PRVM_G_FLOAT(OFS_PARM1); + start = PRVM_G_VECTOR(OFS_PARM2); + end = PRVM_G_VECTOR(OFS_PARM3); + + if(i >= particleeffects_num) + return; + if (entnum >= MAX_EDICTS) + { + Con_Printf("CSQC_ParseBeam: invalid entity number %i\n", entnum); + return; + } + if (entnum >= cl_max_csqcentities) + CL_ExpandCSQCEntities(entnum); + + ent = &cl_csqcentities[entnum]; + + if(prog->argc > 4) + col = PRVM_G_FLOAT(OFS_PARM4); + else + col = ent->state_current.glowcolor; + + switch(i) + { + case TE_LIGHTNING1: + CSQC_ParseBeam(entnum, start, end, cl.model_bolt, true); + break; + case TE_LIGHTNING2: + CSQC_ParseBeam(entnum, start, end, cl.model_bolt2, true); + break; + case TE_LIGHTNING3: + CSQC_ParseBeam(entnum, start, end, cl.model_bolt3, false); + break; + case TE_BEAM: + CSQC_ParseBeam(entnum, start, end, cl.model_beam, false); + break; + default: + CL_RocketTrail(start, end, i-CSQC_TRAILSTART, col, ent); + break; + } +} + +//#337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC) +void VM_CL_pointparticles (void) +{ + int i, n; + float *f, *v; + if(prog->argc < 2) + VM_SAFEPARMCOUNT(2, VM_CL_pointparticles); + i = PRVM_G_FLOAT(OFS_PARM0); + f = PRVM_G_VECTOR(OFS_PARM1); + if(prog->argc >= 4) + { + v = PRVM_G_VECTOR(OFS_PARM2); + n = PRVM_G_FLOAT(OFS_PARM3); + } + else + { + v = vec3_origin; + n = 15; + } + + if(i >= particleeffects_num) + return; + + switch(i) + { + case TE_SPIKE: + case TE_SPIKEQUAD: + case TE_GUNSHOT: + case TE_GUNSHOTQUAD: + CL_SparkShower(f, v, 15, 1); + CL_Smoke(f, v, 15); + if (cl_particles_bulletimpacts.integer) + CL_BulletMark(f); + break; + case TE_SUPERSPIKE: + case TE_SUPERSPIKEQUAD: + CL_SparkShower(f, v, 30, 1); + CL_Smoke(f, v, 30); + if (cl_particles_bulletimpacts.integer) + CL_BulletMark(f); + break; + case TE_EXPLOSION: + case TE_EXPLOSIONQUAD: + case TE_TEI_BIGEXPLOSION: + CL_ParticleExplosion(f); + break; + case TE_TAREXPLOSION: + CL_BlobExplosion(f); + break; + case TE_WIZSPIKE: + CL_RunParticleEffect(f, v, 20, 30); + break; + case TE_KNIGHTSPIKE: + CL_RunParticleEffect(f, v, 226, 20); + break; + case TE_LAVASPLASH: + CL_LavaSplash(f); + break; + case TE_TELEPORT: + CL_TeleportSplash(f); + break; + case TE_EXPLOSION2: + case TE_EXPLOSION3: + case TE_EXPLOSIONRGB: + CL_ParticleExplosion2(f, v[0], v[1]); + break; + case TE_BLOOD: + CL_BloodPuff(f, v, n); + break; + case TE_SPARK: + CL_SparkShower(f, v, n, 1); + break; + case TE_FLAMEJET: + CL_Flames(f, v, n); + break; + case TE_PLASMABURN: + CL_PlasmaBurn(f); + break; + case TE_TEI_G3: + CL_BeamParticle(f, v, 8, 1, 1, 1, 1, 1); + break; + case TE_TEI_SMOKE: + CL_Tei_Smoke(f, v, n); + break; + case TE_TEI_PLASMAHIT: + CL_Tei_PlasmaHit(f, v, n); + break; + default:break; + } +} + +//#338 void(string s) cprint (EXT_CSQC) +void VM_CL_centerprint (void) +{ + char s[VM_STRINGTEMP_LENGTH]; + if(prog->argc < 1) + VM_SAFEPARMCOUNT(1, VM_CL_centerprint); + VM_VarString(0, s, sizeof(s)); + SCR_CenterPrint(s); +} + +//#342 string(float keynum) getkeybind (EXT_CSQC) +void VM_CL_getkeybind (void) +{ + int i; + + VM_SAFEPARMCOUNT(1, VM_CL_getkeybind); + i = PRVM_G_FLOAT(OFS_PARM0); + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(Key_GetBind(i)); +} + +//#343 void(float usecursor) setcursormode (EXT_CSQC) +void VM_CL_setcursormode (void) +{ + VM_SAFEPARMCOUNT(1, VM_CL_setcursormode); + cl.csqc_wantsmousemove = PRVM_G_FLOAT(OFS_PARM0); + cl_ignoremousemove = true; +} + +//#345 float(float framenum) getinputstate (EXT_CSQC) +void VM_CL_getinputstate (void) +{ + int i, frame; + VM_SAFEPARMCOUNT(1, VM_CL_getinputstate); + frame = PRVM_G_FLOAT(OFS_PARM0); + for (i = 0;i < cl.movement_numqueue;i++) + if (cl.movement_queue[i].sequence == frame) + { + VectorCopy(cl.movement_queue[i].viewangles, prog->globals.client->input_angles); + //prog->globals.client->input_buttons = cl.movement_queue[i].//FIXME + VectorCopy(cl.movement_queue[i].move, prog->globals.client->input_movevalues); + prog->globals.client->input_timelength = cl.movement_queue[i].frametime; + if(cl.movement_queue[i].crouch) + { + VectorCopy(cl_playercrouchmins, prog->globals.client->pmove_mins); + VectorCopy(cl_playercrouchmaxs, prog->globals.client->pmove_maxs); + } + else + { + VectorCopy(cl_playerstandmins, prog->globals.client->pmove_mins); + VectorCopy(cl_playerstandmaxs, prog->globals.client->pmove_maxs); + } + } +} + +//#346 void(float sens) setsensitivityscaler (EXT_CSQC) +void VM_CL_setsensitivityscale (void) +{ + VM_SAFEPARMCOUNT(1, VM_CL_setsensitivityscale); + cl.sensitivityscale = PRVM_G_FLOAT(OFS_PARM0); +} + +//#347 void() runstandardplayerphysics (EXT_CSQC) +void VM_CL_runplayerphysics (void) +{ +} + +//#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC) +void VM_CL_getplayerkey (void) +{ + int i; + char t[128]; + const char *c; + char *temp; + + VM_SAFEPARMCOUNT(2, VM_CL_getplayerkey); + + i = PRVM_G_FLOAT(OFS_PARM0); + c = PRVM_G_STRING(OFS_PARM1); + PRVM_G_INT(OFS_RETURN) = OFS_NULL; + Sbar_SortFrags(); + + i = Sbar_GetPlayer(i); + if(i < 0) + return; + + t[0] = 0; + + if(!strcasecmp(c, "name")) + strcpy(t, cl.scores[i].name); + else + if(!strcasecmp(c, "frags")) + sprintf(t, "%i", cl.scores[i].frags); +// else +// if(!strcasecmp(c, "ping")) +// sprintf(t, "%i", cl.scores[i].ping); +// else +// if(!strcasecmp(c, "entertime")) +// sprintf(t, "%f", cl.scores[i].entertime); + else + if(!strcasecmp(c, "colors")) + sprintf(t, "%i", cl.scores[i].colors); + else + if(!strcasecmp(c, "topcolor")) + sprintf(t, "%i", cl.scores[i].colors & 0xf0); + else + if(!strcasecmp(c, "bottomcolor")) + sprintf(t, "%i", (cl.scores[i].colors &15)<<4); + else + if(!strcasecmp(c, "viewentity")) + sprintf(t, "%i", i+1); + if(!t[0]) + return; + temp = VM_GetTempString(); + strcpy(temp, t); + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(temp); +} + +//#349 float() isdemo (EXT_CSQC) +void VM_CL_isdemo (void) +{ + PRVM_G_FLOAT(OFS_RETURN) = cls.demoplayback; +} + +//#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC) +void VM_CL_setlistener (void) +{ + VM_SAFEPARMCOUNT(4, VM_CL_setlistener); + Matrix4x4_FromVectors(&csqc_listenermatrix, PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), PRVM_G_VECTOR(OFS_PARM3), PRVM_G_VECTOR(OFS_PARM0)); + csqc_usecsqclistener = true; //use csqc listener at this frame +} + +//#352 void(string cmdname) registercommand (EXT_CSQC) +void VM_CL_registercmd (void) +{ + char *t; + VM_SAFEPARMCOUNT(1, VM_CL_registercmd); + if(!Cmd_Exists(PRVM_G_STRING(OFS_PARM0))) + { + t = Z_Malloc(strlen(PRVM_G_STRING(OFS_PARM0))+1); + strcpy(t, PRVM_G_STRING(OFS_PARM0)); + Cmd_AddCommand(t, NULL); + } + else + Cmd_AddCommand(PRVM_G_STRING(OFS_PARM0), NULL); + +} + +//#354 float() playernum (EXT_CSQC) +void VM_CL_playernum (void) +{ + int i, k; + + VM_SAFEPARMCOUNT(0, VM_CL_playernum); + + for(i=k=0 ; i= cl_max_csqcentities) + CL_ExpandCSQCEntities(ent); + + // override any beam with the same entity + for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++) + { + if (b->entity == ent && ent) + { + //b->entity = ent; + b->lightning = lightning; + b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0; + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + } + + // find a free beam + for (i = 0, b = cl_beams;i < cl_max_beams;i++, b++) + { + if (!b->model || b->endtime < cl.time) + { + b->entity = ent; + b->lightning = lightning; + b->relativestartvalid = (ent && cl_csqcentities[ent].state_current.active) ? 2 : 0; + b->model = m; + b->endtime = cl.time + 0.2; + VectorCopy (start, b->start); + VectorCopy (end, b->end); + return; + } + } + Con_Print("beam list overflow!\n"); +} + +// #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS) +void VM_CL_te_lightning1 (void) +{ + VM_SAFEPARMCOUNT(3, VM_CL_te_lightning1); + VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt, true); +} + +// #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS) +void VM_CL_te_lightning2 (void) +{ + VM_SAFEPARMCOUNT(3, VM_CL_te_lightning2); + VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt2, true); +} + +// #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS) +void VM_CL_te_lightning3 (void) +{ + VM_SAFEPARMCOUNT(3, VM_CL_te_lightning3); + VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_bolt3, false); +} + +// #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS) +void VM_CL_te_beam (void) +{ + VM_SAFEPARMCOUNT(3, VM_CL_te_beam); + VM_CL_NewBeam(PRVM_G_EDICTNUM(OFS_PARM0), PRVM_G_VECTOR(OFS_PARM1), PRVM_G_VECTOR(OFS_PARM2), cl.model_beam, false); +} + +// #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN) +void VM_CL_te_plasmaburn (void) +{ + float *pos; + vec3_t pos2; + matrix4x4_t tempmatrix; + VM_SAFEPARMCOUNT(1, VM_CL_te_plasmaburn); + + pos = PRVM_G_VECTOR(OFS_PARM0); + CL_FindNonSolidLocation(pos, pos2, 4); + Matrix4x4_CreateTranslate(&tempmatrix, pos2[0], pos2[1], pos2[2]); + CL_AllocDlight(NULL, &tempmatrix, 200, 1, 1, 1, 1000, 0.2, 0, -1, true, 1, 0.25, 1, 0, 0, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE); + CL_PlasmaBurn(pos2); +} + + +//==================================================================== +//DP_QC_GETSURFACE + +void clippointtosurface(msurface_t *surface, vec3_t p, vec3_t out); +static msurface_t *cl_getsurface(prvm_edict_t *ed, int surfacenum) +{ + int modelindex; + model_t *model = NULL; + if (!ed || ed->priv.server->free) + return NULL; + modelindex = ed->fields.client->modelindex; + if(!modelindex) + return NULL; + if(modelindex<0) + { + modelindex = -(modelindex+1); + if(modelindex < MAX_MODELS) + model = cl.csqc_model_precache[modelindex]; + } + else + { + if(modelindex < MAX_MODELS) + model = cl.model_precache[modelindex]; + } + if(!model) + return NULL; + if (surfacenum < 0 || surfacenum >= model->nummodelsurfaces) + return NULL; + return model->data_surfaces + surfacenum + model->firstmodelsurface; +} + +// #434 float(entity e, float s) getsurfacenumpoints +void VM_CL_getsurfacenumpoints(void) +{ + msurface_t *surface; + // return 0 if no such surface + if (!(surface = cl_getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)))) + { + PRVM_G_FLOAT(OFS_RETURN) = 0; + return; + } + + // note: this (incorrectly) assumes it is a simple polygon + PRVM_G_FLOAT(OFS_RETURN) = surface->num_vertices; +} + +// #435 vector(entity e, float s, float n) getsurfacepoint +void VM_CL_getsurfacepoint(void) +{ + prvm_edict_t *ed; + 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 = cl_getsurface(ed, PRVM_G_FLOAT(OFS_PARM1)))) + return; + // note: this (incorrectly) assumes it is a simple polygon + pointnum = 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.client->origin, PRVM_G_VECTOR(OFS_RETURN)); +} + +// #436 vector(entity e, float s) getsurfacenormal +void VM_CL_getsurfacenormal(void) +{ + msurface_t *surface; + vec3_t normal; + VectorClear(PRVM_G_VECTOR(OFS_RETURN)); + if (!(surface = cl_getsurface(PRVM_G_EDICT(OFS_PARM0), 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); + VectorNormalize(normal); + VectorCopy(normal, PRVM_G_VECTOR(OFS_RETURN)); +} + +// #437 string(entity e, float s) getsurfacetexture +void VM_CL_getsurfacetexture(void) +{ + msurface_t *surface; + PRVM_G_INT(OFS_RETURN) = 0; + if (!(surface = cl_getsurface(PRVM_G_EDICT(OFS_PARM0), PRVM_G_FLOAT(OFS_PARM1)))) + return; + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(surface->texture->name); +} + +// #438 float(entity e, vector p) getsurfacenearpoint +void VM_CL_getsurfacenearpoint(void) +{ + int surfacenum, best, modelindex; + vec3_t clipped, p; + vec_t dist, bestdist; + prvm_edict_t *ed; + model_t *model = NULL; + msurface_t *surface; + vec_t *point; + PRVM_G_FLOAT(OFS_RETURN) = -1; + ed = PRVM_G_EDICT(OFS_PARM0); + point = PRVM_G_VECTOR(OFS_PARM1); + + if (!ed || ed->priv.server->free) + return; + modelindex = ed->fields.client->modelindex; + if(!modelindex) + return; + if(modelindex<0) + { + modelindex = -(modelindex+1); + if(modelindex < MAX_MODELS) + model = cl.csqc_model_precache[modelindex]; + } + else + if(modelindex < MAX_MODELS) + model = cl.model_precache[modelindex]; + if(!model) + return; + if (!model->num_surfaces) + return; + + // FIXME: implement rotation/scaling + VectorSubtract(point, ed->fields.client->origin, p); + best = -1; + bestdist = 1000000000; + for (surfacenum = 0;surfacenum < model->nummodelsurfaces;surfacenum++) + { + surface = model->data_surfaces + surfacenum + model->firstmodelsurface; + // first see if the nearest point on the surface's box is closer than the previous match + clipped[0] = bound(surface->mins[0], p[0], surface->maxs[0]) - p[0]; + clipped[1] = bound(surface->mins[1], p[1], surface->maxs[1]) - p[1]; + clipped[2] = bound(surface->mins[2], p[2], surface->maxs[2]) - p[2]; + dist = VectorLength2(clipped); + if (dist < bestdist) + { + // it is, check the nearest point on the actual geometry + clippointtosurface(surface, p, clipped); + VectorSubtract(clipped, p, clipped); + dist += VectorLength2(clipped); + if (dist < bestdist) + { + // that's closer too, store it as the best match + best = surfacenum; + bestdist = dist; + } + } + } + PRVM_G_FLOAT(OFS_RETURN) = best; +} + +// #439 vector(entity e, float s, vector p) getsurfaceclippedpoint +void VM_CL_getsurfaceclippedpoint(void) +{ + prvm_edict_t *ed; + 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 = cl_getsurface(ed, PRVM_G_FLOAT(OFS_PARM1)))) + return; + // FIXME: implement rotation/scaling + VectorSubtract(PRVM_G_VECTOR(OFS_PARM2), ed->fields.client->origin, p); + clippointtosurface(surface, p, out); + // FIXME: implement rotation/scaling + VectorAdd(out, ed->fields.client->origin, PRVM_G_VECTOR(OFS_RETURN)); +} + +// #443 void(entity e, entity tagentity, string tagname) setattachment +void VM_CL_setattachment (void) +{ + prvm_edict_t *e = PRVM_G_EDICT(OFS_PARM0); + prvm_edict_t *tagentity = PRVM_G_EDICT(OFS_PARM1); + const char *tagname = PRVM_G_STRING(OFS_PARM2); + prvm_eval_t *v; + int modelindex; + model_t *model; + + if (e == prog->edicts) + PF_WARNING("setattachment: can not modify world entity\n"); + if (e->priv.server->free) + PF_WARNING("setattachment: can not modify free entity\n"); + + if (tagentity == NULL) + tagentity = prog->edicts; + + v = PRVM_GETEDICTFIELDVALUE(e, csqc_fieldoff_tag_entity); + if (v) + v->edict = PRVM_EDICT_TO_PROG(tagentity); + + v = PRVM_GETEDICTFIELDVALUE(e, csqc_fieldoff_tag_index); + if (v) + v->_float = 0; + if (tagentity != NULL && tagentity != prog->edicts && tagname && tagname[0]) + { + modelindex = (int)tagentity->fields.client->modelindex; + model = NULL; + + if(modelindex) + { + if(modelindex<0) + { + modelindex = -(modelindex+1); + if(modelindex < MAX_MODELS) + model = cl.csqc_model_precache[modelindex]; + } + else + if(modelindex < MAX_MODELS) + model = cl.model_precache[modelindex]; + } + + if (model) + { + v->_float = Mod_Alias_GetTagIndexForName(model, tagentity->fields.client->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); + } + else + Con_DPrintf("setattachment(edict %i, edict %i, string \"%s\"): tried to find tag named \"%s\" on entity %i but it has no model\n", PRVM_NUM_FOR_EDICT(e), PRVM_NUM_FOR_EDICT(tagentity), tagname, tagname, PRVM_NUM_FOR_EDICT(tagentity)); + } +} + +///////////////////////////////////////// +// DP_MD3_TAGINFO extension coded by VorteX + +int CL_GetTagIndex (prvm_edict_t *e, const char *tagname) +{ + int i; + model_t *m; + + i = e->fields.client->modelindex; + + if(!i) + return -1; + if(i<0) + { + i = -(i+1); + if(i >= MAX_MODELS) + return -1; + m = cl.csqc_model_precache[i]; + } + else + if(i >= MAX_MODELS) + return -1; + else + m = cl.model_precache[i]; + + return Mod_Alias_GetTagIndexForName(m, e->fields.client->skin, tagname); +}; + +// Warnings/errors code: +// 0 - normal (everything all-right) +// 1 - world entity +// 2 - free entity +// 3 - null or non-precached model +// 4 - no tags with requested index +// 5 - runaway loop at attachment chain +extern cvar_t cl_bob; +extern cvar_t cl_bobcycle; +extern cvar_t cl_bobup; +int CL_GetTagMatrix (matrix4x4_t *out, prvm_edict_t *ent, int tagindex) +{ + prvm_eval_t *val; + int modelindex, reqframe, attachloop, i; + matrix4x4_t entitymatrix, tagmatrix, attachmatrix; + prvm_edict_t *attachent; + model_t *model; + + Matrix4x4_CreateIdentity(out); // warnings and errors return identical matrix + + if (ent == prog->edicts) + return 1; + if (ent->priv.server->free) + return 2; + + modelindex = (int)ent->fields.client->modelindex; + + if(!modelindex) + return 3; + if(modelindex<0) + { + modelindex = -(modelindex+1); + if(modelindex >= MAX_MODELS) + return 3; + model = cl.csqc_model_precache[modelindex]; + } + else + if(modelindex >= MAX_MODELS) + return 3; + else + model = cl.model_precache[modelindex]; + + 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) + { + int ret = Mod_Alias_GetTagMatrix(model, reqframe, tagindex - 1, &tagmatrix); + if (ret) + return ret; + } + else + Matrix4x4_CreateIdentity(&tagmatrix); + + if ((val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_tag_entity)) && val->edict) + { // DP_GFX_QUAKE3MODELTAGS, scan all chain and stop on unattached entity + attachloop = 0; + do + { + attachent = PRVM_EDICT_NUM(val->edict); // to this it entity our entity is attached + val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_tag_index); + + model = NULL; + i = attachent->fields.client->modelindex; + if(i<0) + { + i = -(i+1); + if(i < MAX_MODELS) + model = cl.csqc_model_precache[i]; + } + else + if(i < MAX_MODELS) + model = cl.model_precache[i]; + + 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, val->_float - 1, &attachmatrix); + else + Matrix4x4_CreateIdentity(&attachmatrix); + + // apply transformation by child entity matrix + val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_scale); + if (val->_float == 0) + val->_float = 1; + 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], 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; + } + while ((val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_tag_entity)) && val->edict); + } + + // normal or RENDER_VIEWMODEL entity (or main parent entity on attach chain) + val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_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.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], 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]); + + if ((val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_renderflags)) && (RF_VIEWMODEL & (int)val->_float)) + {// RENDER_VIEWMODEL magic + Matrix4x4_Copy(&tagmatrix, out); + + val = PRVM_GETEDICTFIELDVALUE(ent, csqc_fieldoff_scale); + if (val->_float == 0) + val->_float = 1; + + Matrix4x4_CreateFromQuakeEntity(&entitymatrix, csqc_origin[0], csqc_origin[1], csqc_origin[2], csqc_angles[0], csqc_angles[1], csqc_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]); + + /* + // Cl_bob, ported from rendering code + if (ent->fields.client->health > 0 && cl_bob.value && cl_bobcycle.value) + { + double bob, cycle; + // LordHavoc: this code is *weird*, but not replacable (I think it + // should be done in QC on the server, but oh well, quake is quake) + // LordHavoc: figured out bobup: the time at which the sin is at 180 + // degrees (which allows lengthening or squishing the peak or valley) + cycle = sv.time/cl_bobcycle.value; + cycle -= (int)cycle; + if (cycle < cl_bobup.value) + cycle = sin(M_PI * cycle / cl_bobup.value); + else + cycle = sin(M_PI + M_PI * (cycle-cl_bobup.value)/(1.0 - cl_bobup.value)); + // bob is proportional to velocity in the xy plane + // (don't count Z, or jumping messes it up) + bob = sqrt(ent->fields.client->velocity[0]*ent->fields.client->velocity[0] + ent->fields.client->velocity[1]*ent->fields.client->velocity[1])*cl_bob.value; + bob = bob*0.3 + bob*0.7*cycle; + out->m[2][3] += bound(-7, bob, 4); + } + */ + } + return 0; +} + +// #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO) +void VM_CL_gettagindex (void) +{ + prvm_edict_t *ent = PRVM_G_EDICT(OFS_PARM0); + const char *tag_name = PRVM_G_STRING(OFS_PARM1); + int modelindex, tag_index; + + if (ent == prog->edicts) + PF_WARNING("gettagindex: can't affect world entity\n"); + if (ent->priv.server->free) + PF_WARNING("gettagindex: can't affect free entity\n"); + + modelindex = (int)ent->fields.client->modelindex; + if(modelindex < 0) + modelindex = -(modelindex+1); + tag_index = 0; + if (modelindex <= 0 || modelindex >= MAX_MODELS) + Con_DPrintf("gettagindex(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(ent)); + else + { + tag_index = CL_GetTagIndex(ent, tag_name); + if (tag_index == 0) + Con_DPrintf("gettagindex(entity #%i): tag \"%s\" not found\n", PRVM_NUM_FOR_EDICT(ent), tag_name); + } + PRVM_G_FLOAT(OFS_RETURN) = tag_index; +} + +// #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO) +void VM_CL_gettaginfo (void) +{ + prvm_edict_t *e = PRVM_G_EDICT(OFS_PARM0); + int tagindex = (int)PRVM_G_FLOAT(OFS_PARM1); + matrix4x4_t tag_matrix; + int returncode; + + 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)); + + switch(returncode) + { + case 1: + PF_WARNING("gettagindex: can't affect world entity\n"); + break; + case 2: + PF_WARNING("gettagindex: can't affect free entity\n"); + break; + case 3: + Con_DPrintf("CL_GetTagMatrix(entity #%i): null or non-precached model\n", PRVM_NUM_FOR_EDICT(e)); + break; + case 4: + Con_DPrintf("CL_GetTagMatrix(entity #%i): model has no tag with requested index %i\n", PRVM_NUM_FOR_EDICT(e), tagindex); + break; + case 5: + Con_DPrintf("CL_GetTagMatrix(entity #%i): runaway loop at attachment chain\n", PRVM_NUM_FOR_EDICT(e)); + break; + } +} + +//================================================= +//[515]: here goes test/unfinished/etc. + +//[515]: check if it is what it should be +void VM_WasFreed (void) +{ + prvm_edict_t *e; + VM_SAFEPARMCOUNT(1, VM_WasFreed); + + e = PRVM_G_EDICT(OFS_PARM0); + if (!e->priv.required->free || (e->priv.required->free && (e->priv.required->freetime < 2 || (*prog->time - e->priv.required->freetime) > 0.5 ))) + PRVM_G_FLOAT(OFS_RETURN) = false; + else + PRVM_G_FLOAT(OFS_RETURN) = true; +} + +void VM_CL_select_cube (void) +{ + int i; + int chain_of; + float *mins2, *maxs2; + prvm_edict_t *ent, *chain; + vec3_t mins1, maxs1; + + VM_SAFEPARMCOUNT(2, VM_CL_select_cube); + + // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another + if(!prog->flag & PRVM_FE_CHAIN) + PRVM_ERROR("VM_findchain: %s doesnt have a chain field !\n", PRVM_NAME); + + chain_of = PRVM_ED_FindField("chain")->ofs; + chain = prog->edicts; + + mins2 = PRVM_G_VECTOR(OFS_PARM0); + maxs2 = PRVM_G_VECTOR(OFS_PARM1); + + ent = PRVM_NEXT_EDICT(prog->edicts); + for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + { + if (ent->priv.required->free) + continue; + VectorCopy(ent->fields.client->origin, mins1); + VectorAdd(mins1, ent->fields.client->maxs, maxs1); + VectorAdd(mins1, ent->fields.client->mins, mins1); + if (mins1[0] > maxs2[0] || mins1[1] > maxs2[1] || mins1[2] > maxs2[2]) + continue; + if (maxs1[0] < mins2[0] || maxs1[1] < mins2[1] || maxs1[2] < mins2[2]) + continue; + PRVM_E_INT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain); + chain = ent; + } + + VM_RETURN_EDICT(chain); +} + +void VM_CL_select_super (void) +{ +/* int i; + int chain_of; + float *v[8]; + prvm_edict_t *ent, *chain; + vec3_t mins1, maxs1; + + VM_SAFEPARMCOUNT(8, VM_findchain); + for(i=0;i<8;i++) + v[i] = PRVM_G_VECTOR(OFS_PARM0+i*3); + + // is the same like !(prog->flag & PRVM_FE_CHAIN) - even if the operator precedence is another + if(!prog->flag & PRVM_FE_CHAIN) + PRVM_ERROR("VM_findchain: %s doesnt have a chain field !\n", PRVM_NAME); + + chain_of = PRVM_ED_FindField("chain")->ofs; + chain = prog->edicts; + + mins2 = PRVM_G_VECTOR(OFS_PARM0); + maxs2 = PRVM_G_VECTOR(OFS_PARM1); + + ent = PRVM_NEXT_EDICT(prog->edicts); + for (i = 1;i < prog->num_edicts;i++, ent = PRVM_NEXT_EDICT(ent)) + { + if (ent->priv.required->free) + continue; + VectorCopy(ent->fields.client->origin, mins1); + VectorAdd(mins1, ent->fields.client->maxs, maxs1); + VectorAdd(mins1, ent->fields.client->mins, mins1); + if (mins1[0] > maxs2[0] || mins1[1] > maxs2[1] || mins1[2] > maxs2[2]) + continue; + if (maxs1[0] < mins2[0] || maxs1[1] < mins2[1] || maxs1[2] < mins2[2]) + continue; + PRVM_E_INT(ent,chain_of) = PRVM_NUM_FOR_EDICT(chain); + chain = ent; + } + + VM_RETURN_EDICT(chain);*/ +} + +static int Is_Text_Color (char c, char t) +{ + int a = 0; + char c2 = c - (c & 128); + char t2 = t - (t & 128); + + if(c != '^' && c2 != '^') return 0; + if(t >= '0' && t <= '9') a = 1; + if(t2 >= '0' && t2 <= '9') a = 1; +/* if(t >= 'A' && t <= 'Z') a = 2; + if(t2 >= 'A' && t2 <= 'Z') a = 2; + + if(a == 1 && scr_colortext.integer > 0) + return 1; + if(a == 2 && scr_multifonts.integer > 0) + return 2; +*/ + return a; +} + +void VM_uncolorstring (void) //#170 +{ + const char *in; + char *out; + int k = 0, i = 0; + + VM_SAFEPARMCOUNT(1, VM_uncolorstring); + in = PRVM_G_STRING(OFS_PARM0); + if(!in) + PRVM_ERROR ("VM_uncolorstring: %s: NULL\n", PRVM_NAME); + VM_CheckEmptyString (in); + out = VM_GetTempString(); + + while (in[k]) + { + if(in[k+1]) + if(Is_Text_Color(in[k], in[k+1]) == 1/* || (in[k] == '&' && in[k+1] == 'r')*/) + { + k += 2; + continue; + } + out[i] = in[k]; + ++k; + ++i; + } +} + +void VM_CL_selecttraceline (void) +{ + float *v1, *v2; + int ent, ignore, csqcents; + + v1 = PRVM_G_VECTOR(OFS_PARM0); + v2 = PRVM_G_VECTOR(OFS_PARM1); + ignore = PRVM_G_FLOAT(OFS_PARM2); + csqcents = PRVM_G_FLOAT(OFS_PARM3); + ent = 0; + + if((csqcents && ignore > cl_num_csqcentities) || (!csqcents && ignore > cl_num_entities)) + { + Con_Printf("VM_CL_selecttraceline: out of entities\n"); + return; + } + else + if(csqcents) + prog->globals.client->trace_fraction = CL_SelectTraceLine(v1, v2, prog->globals.client->trace_endpos, prog->globals.client->trace_plane_normal, &prog->globals.client->trace_ent, &cl_csqcentities[ignore].render, csqcents); + else + prog->globals.client->trace_fraction = CL_SelectTraceLine(v1, v2, prog->globals.client->trace_endpos, prog->globals.client->trace_plane_normal, &ent, &cl_entities[ignore].render, csqcents); + PRVM_G_FLOAT(OFS_RETURN) = ent; +} + +void VM_charindex (void) +{ + const char *s; + s = PRVM_G_STRING(OFS_PARM0); + if(!s) + return; + if((unsigned)PRVM_G_FLOAT(OFS_PARM1) > strlen(s)) + return; + PRVM_G_FLOAT(OFS_RETURN) = (unsigned char)s[(int)PRVM_G_FLOAT(OFS_PARM1)]; +} + +//#223 string(float c, ...) chr2str (FTE_STRINGS) +void VM_chr2str (void) +{ + char *t; + int i; + t = VM_GetTempString(); + for(i=0;iargc;i++) + t[i] = (unsigned char)PRVM_G_FLOAT(OFS_PARM0+i*3); + t[i] = 0; + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(t); +} + +//#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS) +void VM_strncmp (void) +{ + const char *s1, *s2; + VM_SAFEPARMCOUNT(1, VM_strncmp); + s1 = PRVM_G_STRING(OFS_PARM0); + s2 = PRVM_G_STRING(OFS_PARM1); + PRVM_G_FLOAT(OFS_RETURN) = strncmp(s1, s2, (size_t)PRVM_G_FLOAT(OFS_PARM2)); +} + +//============================================================================ +//============================================================================ + +prvm_builtin_t vm_cl_builtins[] = { +0, // to be consistent with the old vm +VM_CL_makevectors, // #1 void(vector ang) makevectors +VM_CL_setorigin, // #2 void(entity e, vector o) setorigin +VM_CL_setmodel, // #3 void(entity e, string m) setmodel +VM_CL_setsize, // #4 void(entity e, vector min, vector max) setsize +0, +VM_break, // #6 void() break +VM_random, // #7 float() random +VM_CL_sound, // #8 void(entity e, float chan, string samp) sound +VM_normalize, // #9 vector(vector v) normalize +VM_error, // #10 void(string e) error +VM_objerror, // #11 void(string e) objerror +VM_vlen, // #12 float(vector v) vlen +VM_vectoyaw, // #13 float(vector v) vectoyaw +VM_CL_spawn, // #14 entity() spawn +VM_remove, // #15 void(entity e) remove +VM_CL_traceline, // #16 float(vector v1, vector v2, float tryents) traceline +0, +VM_find, // #18 entity(entity start, .string fld, string match) find +VM_CL_precache_sound, // #19 void(string s) precache_sound +VM_CL_precache_model, // #20 void(string s) precache_model +0, +VM_CL_findradius, // #22 entity(vector org, float rad) findradius +0, +0, +VM_dprint, // #25 void(string s) dprint +VM_ftos, // #26 void(string s) ftos +VM_vtos, // #27 void(string s) vtos +VM_coredump, // #28 void() coredump +VM_traceon, // #29 void() traceon +VM_traceoff, // #30 void() traceoff +VM_eprint, // #31 void(entity e) eprint +0, +NULL, // #33 +VM_CL_droptofloor, // #34 float() droptofloor +VM_CL_lightstyle, // #35 void(float style, string value) lightstyle +VM_rint, // #36 float(float v) rint +VM_floor, // #37 float(float v) floor +VM_ceil, // #38 float(float v) ceil +NULL, // #39 +VM_CL_checkbottom, // #40 float(entity e) checkbottom +VM_CL_pointcontents, // #41 float(vector v) pointcontents +NULL, // #42 +VM_fabs, // #43 float(float f) fabs +0, +VM_cvar, // #45 float(string s) cvar +VM_localcmd, // #46 void(string s) localcmd +VM_nextent, // #47 entity(entity e) nextent +VM_CL_particle, // #48 void(vector o, vector d, float color, float count) particle +VM_CL_changeyaw, // #49 void(entity ent, float ideal_yaw, float speed_yaw) ChangeYaw +NULL, // #50 +VM_vectoangles, // #51 vector(vector v) vectoangles +0, // #52 void(float to, float f) WriteByte +0, // #53 void(float to, float f) WriteChar +0, // #54 void(float to, float f) WriteShort +0, // #55 void(float to, float f) WriteLong +0, // #56 void(float to, float f) WriteCoord +0, // #57 void(float to, float f) WriteAngle +0, // #58 void(float to, string s) WriteString +0, +VM_sin, // #60 float(float f) sin (DP_QC_SINCOSSQRTPOW) +VM_cos, // #61 float(float f) cos (DP_QC_SINCOSSQRTPOW) +VM_sqrt, // #62 float(float f) sqrt (DP_QC_SINCOSSQRTPOW) +VM_CL_changepitch, // #63 void(entity ent, float ideal_pitch, float speed_pitch) changepitch (DP_QC_CHANGEPITCH) +VM_CL_tracetoss, // #64 void(entity e, entity ignore) tracetoss (DP_QC_TRACETOSS) +VM_etos, // #65 string(entity ent) etos (DP_QC_ETOS) +NULL, // #66 +0, // #67 +0, // #68 +0, // #69 +0, // #70 +NULL, // #71 +VM_cvar_set, // #72 void(string var, string val) cvar_set +0, // #73 +VM_CL_ambientsound, // #74 void(vector pos, string samp, float vol, float atten) ambientsound +VM_CL_precache_model, // #75 string(string s) precache_model2 +VM_CL_precache_sound, // #76 string(string s) precache_sound2 +0, // #77 +VM_chr, // #78 +NULL, // #79 +NULL, // #80 +VM_stof, // #81 float(string s) stof (FRIK_FILE) +NULL, // #82 +NULL, // #83 +NULL, // #84 +NULL, // #85 +NULL, // #86 +NULL, // #87 +NULL, // #88 +NULL, // #89 +VM_CL_tracebox, // #90 void(vector v1, vector min, vector max, vector v2, float nomonsters, entity forent) tracebox (DP_QC_TRACEBOX) +VM_randomvec, // #91 vector() randomvec (DP_QC_RANDOMVEC) +VM_CL_getlight, // #92 vector(vector org) getlight (DP_QC_GETLIGHT) +PF_registercvar, // #93 float(string name, string value) registercvar (DP_REGISTERCVAR) +VM_min, // #94 float(float a, floats) min (DP_QC_MINMAXBOUND) +VM_max, // #95 float(float a, floats) max (DP_QC_MINMAXBOUND) +VM_bound, // #96 float(float minimum, float val, float maximum) bound (DP_QC_MINMAXBOUND) +VM_pow, // #97 float(float f, float f) pow (DP_QC_SINCOSSQRTPOW) +VM_findfloat, // #98 entity(entity start, .float fld, float match) findfloat (DP_QC_FINDFLOAT) +VM_checkextension, // #99 float(string s) checkextension (the basis of the extension system) +NULL, // #100 +NULL, // #101 +NULL, // #102 +NULL, // #103 +NULL, // #104 +NULL, // #105 +NULL, // #106 +NULL, // #107 +NULL, // #108 +NULL, // #109 +VM_fopen, // #110 float(string filename, float mode) fopen (FRIK_FILE) +VM_fclose, // #111 void(float fhandle) fclose (FRIK_FILE) +VM_fgets, // #112 string(float fhandle) fgets (FRIK_FILE) +VM_fputs, // #113 void(float fhandle, string s) fputs (FRIK_FILE) +VM_strlen, // #114 float(string s) strlen (FRIK_FILE) +VM_strcat, // #115 string(string s1, string s2) strcat (FRIK_FILE) +VM_substring, // #116 string(string s, float start, float length) substring (FRIK_FILE) +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, //#200-209 +0, //#210 +0, //#211 +0, //#212 +0, //#213 +0, //#214 +0, //#215 +0, //#216 +0, //#217 +VM_bitshift, //#218 float(float number, float quantity) bitshift (EXT_BITSHIFT) +0, //#219 +0, //#220 +0, //#221 +VM_charindex, //#222 float(string str, float ofs) str2chr (FTE_STRINGS) +VM_chr2str, //#223 string(float c, ...) chr2str (FTE_STRINGS) +0, //#224 +0, //#225 +0, //#226 +0, //#227 +VM_strncmp, //#228 float(string s1, string s2, float len) strncmp (FTE_STRINGS) +0, +e10, e10, e10, e10, e10, e10, e10, // #230-299 + +//======CSQC start=======// +//3d world (buffer/buffering) operations +VM_R_ClearScene, //#300 void() clearscene (EXT_CSQC) +VM_R_AddEntities, //#301 void(float mask) addentities (EXT_CSQC) +VM_R_AddEntity, //#302 void(entity ent) addentity (EXT_CSQC) +VM_R_SetView, //#303 float(float property, ...) setproperty (EXT_CSQC) +VM_R_RenderScene, //#304 void() renderscene (EXT_CSQC) +VM_R_AddDynamicLight, //#305 void(vector org, float radius, vector lightcolours) adddynamiclight (EXT_CSQC) +VM_R_PolygonBegin, //#306 void(string texturename, float flag[, float is2d, float lines]) R_BeginPolygon +VM_R_PolygonVertex, //#307 void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex +VM_R_PolygonEnd, //#308 void() R_EndPolygon +0, //#309 + +//maths stuff that uses the current view settings +VM_CL_unproject, //#310 vector (vector v) cs_unproject (EXT_CSQC) +VM_CL_project, //#311 vector (vector v) cs_project (EXT_CSQC) +0, //#312 +0, //#313 +0, //#314 + +//2d (immediate) operations +VM_drawline, //#315 void(float width, vector pos1, vector pos2, float flag) drawline (EXT_CSQC) +VM_iscachedpic, //#316 float(string name) iscachedpic (EXT_CSQC) +VM_precache_pic, //#317 string(string name, float trywad) precache_pic (EXT_CSQC) +VM_getimagesize, //#318 vector(string picname) draw_getimagesize (EXT_CSQC) +VM_freepic, //#319 void(string name) freepic (EXT_CSQC) +VM_drawcharacter, //#320 float(vector position, float character, vector scale, vector rgb, float alpha, float flag) drawcharacter (EXT_CSQC) +VM_drawstring, //#321 float(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring (EXT_CSQC) +VM_drawpic, //#322 float(vector position, string pic, vector size, vector rgb, float alpha, float flag) drawpic (EXT_CSQC) +VM_drawfill, //#323 float(vector position, vector size, vector rgb, float alpha, float flag) drawfill (EXT_CSQC) +VM_drawsetcliparea, //#324 void(float x, float y, float width, float height) drawsetcliparea +VM_drawresetcliparea, //#325 void(void) drawresetcliparea +0, //#326 +0, //#327 +0, //#328 +0, //#329 + +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) +VM_CL_setmodelindex, //#333 void(entity e, float mdlindex) setmodelindex (EXT_CSQC) +VM_CL_modelnameforindex, //#334 string(float mdlindex) modelnameforindex (EXT_CSQC) +VM_CL_particleeffectnum, //#335 float(string effectname) particleeffectnum (EXT_CSQC) +VM_CL_trailparticles, //#336 void(entity ent, float effectnum, vector start, vector end) trailparticles (EXT_CSQC) +VM_CL_pointparticles, //#337 void(float effectnum, vector origin [, vector dir, float count]) pointparticles (EXT_CSQC) +VM_CL_centerprint, //#338 void(string s) cprint (EXT_CSQC) +VM_print, //#339 void(string s) print (EXT_CSQC) +VM_keynumtostring, //#340 string(float keynum) keynumtostring (EXT_CSQC) +VM_stringtokeynum, //#341 float(string keyname) stringtokeynum (EXT_CSQC) +VM_CL_getkeybind, //#342 string(float keynum) getkeybind (EXT_CSQC) +VM_CL_setcursormode, //#343 void(float usecursor) setcursormode (EXT_CSQC) +VM_getmousepos, //#344 vector() getmousepos (EXT_CSQC) +VM_CL_getinputstate, //#345 float(float framenum) getinputstate (EXT_CSQC) +VM_CL_setsensitivityscale, //#346 void(float sens) setsensitivityscaler (EXT_CSQC) +VM_CL_runplayerphysics, //#347 void() runstandardplayerphysics (EXT_CSQC) +VM_CL_getplayerkey, //#348 string(float playernum, string keyname) getplayerkeyvalue (EXT_CSQC) +VM_CL_isdemo, //#349 float() isdemo (EXT_CSQC) +VM_isserver, //#350 float() isserver (EXT_CSQC) +VM_CL_setlistener, //#351 void(vector origin, vector forward, vector right, vector up) SetListener (EXT_CSQC) +VM_CL_registercmd, //#352 void(string cmdname) registercommand (EXT_CSQC) +VM_WasFreed, //#353 float(entity ent) wasfreed (EXT_CSQC) (should be availabe on server too) +VM_CL_playernum, //#354 float() playernum +VM_CL_onground, //#355 float() cl_onground (EXT_CSQC) +VM_charindex, //#356 float(string s, float num) charindex +VM_CL_selecttraceline, //#357 float(vector start, vector end, float ignore, float csqcents) selecttraceline +0, //#358 +0, //#359 +VM_CL_ReadByte, //#360 float() readbyte (EXT_CSQC) +VM_CL_ReadChar, //#361 float() readchar (EXT_CSQC) +VM_CL_ReadShort, //#362 float() readshort (EXT_CSQC) +VM_CL_ReadLong, //#363 float() readlong (EXT_CSQC) +VM_CL_ReadCoord, //#364 float() readcoord (EXT_CSQC) +VM_CL_ReadAngle, //#365 float() readangle (EXT_CSQC) +VM_CL_ReadString, //#366 string() readstring (EXT_CSQC) +VM_CL_ReadFloat, //#367 float() readfloat (EXT_CSQC) +0, //#368 +0, //#369 +0, //#370 +0, //#371 +0, //#372 +0, //#373 +0, //#374 +0, //#375 +0, //#376 +0, //#377 +0, //#378 +0, //#379 +0, //#380 +0, //#381 +0, //#382 +0, //#383 +0, //#384 +0, //#385 +0, //#386 +0, //#387 +0, //#388 +0, //#389 +0, //#390 +0, //#391 +0, //#392 +0, //#393 +0, //#394 +0, //#395 +0, //#396 +0, //#397 +0, //#398 +0, //#399 +//=========CSQC end========// + +VM_copyentity, // #400 void(entity from, entity to) copyentity (DP_QC_COPYENTITY) +0, +VM_findchain, // #402 entity(.string fld, string match) findchain (DP_QC_FINDCHAIN) +VM_findchainfloat, // #403 entity(.float fld, float match) findchainfloat (DP_QC_FINDCHAINFLOAT) +VM_CL_effect, // #404 void(vector org, string modelname, float startframe, float endframe, float framerate) effect (DP_SV_EFFECT) +VM_CL_te_blood, // #405 void(vector org, vector velocity, float howmany) te_blood (DP_TE_BLOOD) +VM_CL_te_bloodshower, // #406 void(vector mincorner, vector maxcorner, float explosionspeed, float howmany) te_bloodshower (DP_TE_BLOODSHOWER) +VM_CL_te_explosionrgb, // #407 void(vector org, vector color) te_explosionrgb (DP_TE_EXPLOSIONRGB) +VM_CL_te_particlecube, // #408 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color, float gravityflag, float randomveljitter) te_particlecube (DP_TE_PARTICLECUBE) +VM_CL_te_particlerain, // #409 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain (DP_TE_PARTICLERAIN) +VM_CL_te_particlesnow, // #410 void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow (DP_TE_PARTICLESNOW) +VM_CL_te_spark, // #411 void(vector org, vector vel, float howmany) te_spark (DP_TE_SPARK) +VM_CL_te_gunshotquad, // #412 void(vector org) te_gunshotquad (DP_QUADEFFECTS1) +VM_CL_te_spikequad, // #413 void(vector org) te_spikequad (DP_QUADEFFECTS1) +VM_CL_te_superspikequad, // #414 void(vector org) te_superspikequad (DP_QUADEFFECTS1) +VM_CL_te_explosionquad, // #415 void(vector org) te_explosionquad (DP_QUADEFFECTS1) +VM_CL_te_smallflash, // #416 void(vector org) te_smallflash (DP_TE_SMALLFLASH) +VM_CL_te_customflash, // #417 void(vector org, float radius, float lifetime, vector color) te_customflash (DP_TE_CUSTOMFLASH) +VM_CL_te_gunshot, // #418 void(vector org) te_gunshot (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_spike, // #419 void(vector org) te_spike (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_superspike, // #420 void(vector org) te_superspike (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_explosion, // #421 void(vector org) te_explosion (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_tarexplosion, // #422 void(vector org) te_tarexplosion (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_wizspike, // #423 void(vector org) te_wizspike (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_knightspike, // #424 void(vector org) te_knightspike (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_lavasplash, // #425 void(vector org) te_lavasplash (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_teleport, // #426 void(vector org) te_teleport (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_explosion2, // #427 void(vector org, float colorstart, float colorlength) te_explosion2 (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_lightning1, // #428 void(entity own, vector start, vector end) te_lightning1 (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_lightning2, // #429 void(entity own, vector start, vector end) te_lightning2 (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_lightning3, // #430 void(entity own, vector start, vector end) te_lightning3 (DP_TE_STANDARDEFFECTBUILTINS) +VM_CL_te_beam, // #431 void(entity own, vector start, vector end) te_beam (DP_TE_STANDARDEFFECTBUILTINS) +VM_vectorvectors, // #432 void(vector dir) vectorvectors (DP_QC_VECTORVECTORS) +VM_CL_te_plasmaburn, // #433 void(vector org) te_plasmaburn (DP_TE_PLASMABURN) +VM_CL_getsurfacenumpoints, // #434 float(entity e, float s) getsurfacenumpoints (DP_QC_GETSURFACE) +VM_CL_getsurfacepoint, // #435 vector(entity e, float s, float n) getsurfacepoint (DP_QC_GETSURFACE) +VM_CL_getsurfacenormal, // #436 vector(entity e, float s) getsurfacenormal (DP_QC_GETSURFACE) +VM_CL_getsurfacetexture, // #437 string(entity e, float s) getsurfacetexture (DP_QC_GETSURFACE) +VM_CL_getsurfacenearpoint, // #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE) +VM_CL_getsurfaceclippedpoint, // #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE) +0, // #440 +VM_tokenize, // #441 float(string s) tokenize (KRIMZON_SV_PARSECLIENTCOMMAND) +VM_argv, // #442 string(float n) argv (KRIMZON_SV_PARSECLIENTCOMMAND) +VM_CL_setattachment, // #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) +VM_CL_gettagindex, // #451 float(entity ent, string tagname) gettagindex (DP_QC_GETTAGINFO) +VM_CL_gettaginfo, // #452 vector(entity ent, float tagindex) gettaginfo (DP_QC_GETTAGINFO) +0, // #453 +0, // #454 +0, // #455 +NULL, // #456 +NULL, // #457 +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) +e10, e10, e10 // #470-499 (LordHavoc) +}; + +const int vm_cl_numbuiltins = sizeof(vm_cl_builtins) / sizeof(prvm_builtin_t); + +void VM_CL_Cmd_Init(void) +{ +} + +void VM_CL_Cmd_Reset(void) +{ +} + diff --git a/cmd.c b/cmd.c index 9a8a56da..de0a7720 100644 --- a/cmd.c +++ b/cmd.c @@ -387,6 +387,7 @@ typedef struct cmd_function_s struct cmd_function_s *next; const char *name; xcommand_t function; + qboolean csqcfunc; } cmd_function_t; static int cmd_argc; @@ -696,14 +697,24 @@ void Cmd_AddCommand (const char *cmd_name, xcommand_t function) { if (!strcmp (cmd_name, cmd->name)) { - Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name); - return; + if (function) + { + Con_Printf("Cmd_AddCommand: %s already defined\n", cmd_name); + return; + } + else //[515]: csqc + { + cmd->csqcfunc = true; + return; + } } } cmd = (cmd_function_t *)Mem_Alloc(cmd_mempool, sizeof(cmd_function_t)); cmd->name = cmd_name; cmd->function = function; + if(!function) //[515]: csqc + cmd->csqcfunc = true; cmd->next = cmd_functions; // insert it at the right alphanumeric position @@ -899,6 +910,14 @@ const char **Cmd_CompleteAliasBuildList (const char *partial) return buf; } +void Cmd_ClearCsqcFuncs (void) +{ + cmd_function_t *cmd; + for (cmd=cmd_functions ; cmd ; cmd=cmd->next) + cmd->csqcfunc = false; +} + +qboolean CL_VM_ConsoleCommand (const char *cmd); /* ============ Cmd_ExecuteString @@ -930,7 +949,14 @@ void Cmd_ExecuteString (const char *text, cmd_source_t src) { if (!strcasecmp (cmd_argv[0],cmd->name)) { - cmd->function (); + if(cmd->function && !cmd->csqcfunc) + cmd->function (); + else + if(CL_VM_ConsoleCommand (text)) //[515]: csqc + return; + else + if(cmd->function) + cmd->function (); cmd_tokenizebufferpos = oldpos; return; } diff --git a/console.c b/console.c index 0ba430c3..641bc965 100644 --- a/console.c +++ b/console.c @@ -904,6 +904,7 @@ qboolean GetMapList (const char *s, char *completedname, int completednamebuffer char keyname[64]; char entfilename[MAX_QPATH]; strcpy(message, "^1**ERROR**^7"); + p = 0; f = FS_Open(t->filenames[i], "rb", true, false); if(f) { diff --git a/csprogs.c b/csprogs.c new file mode 100644 index 00000000..1de0cf97 --- /dev/null +++ b/csprogs.c @@ -0,0 +1,614 @@ +#include "quakedef.h" +#include "progsvm.h" +#include "clprogdefs.h" +#include "csprogs.h" + +//============================================================================ +// Client prog handling +//[515]: omg !!! optimize it ! a lot of hacks here and there also :P + +#define CSQC_RETURNVAL prog->globals.generic[OFS_RETURN] +#define CSQC_BEGIN csqc_tmpprog=prog;prog=0;PRVM_SetProg(PRVM_CLIENTPROG); +#define CSQC_END prog=csqc_tmpprog; +static prvm_prog_t *csqc_tmpprog; + +//[515]: these are required funcs +#define CL_F_INIT "CSQC_Init" +#define CL_F_INPUTEVENT "CSQC_InputEvent" +#define CL_F_UPDATEVIEW "CSQC_UpdateView" +#define CL_F_CONSOLECOMMAND "CSQC_ConsoleCommand" +#define CL_F_SHUTDOWN "CSQC_Shutdown" + +//[515]: these are optional +#define CL_F_PARSE_TEMPENTITY "CSQC_Parse_TempEntity" //[515]: very helpfull when you want to create your own particles/decals/etc for effects that allready exist +#define CL_F_PARSE_STUFFCMD "CSQC_Parse_StuffCmd" +#define CL_F_PARSE_PRINT "CSQC_Parse_Print" +#define CL_F_PARSE_CENTERPRINT "CSQC_Parse_CenterPrint" +#define CL_F_ENT_UPDATE "CSQC_Ent_Update" +#define CL_F_ENT_REMOVE "CSQC_Ent_Remove" +#define CL_F_EVENT "CSQC_Event" //[515]: engine call this for its own needs + //so csqc can do some things according to what engine it's running on + //example: to say about edicts increase, whatever... + +#define CSQC_PRINTBUFFERLEN 8192 //[515]: enough ? + +static char *cl_required_func[] = +{ + CL_F_INIT, + CL_F_INPUTEVENT, + CL_F_UPDATEVIEW, + CL_F_CONSOLECOMMAND, + CL_F_SHUTDOWN +}; + +static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*); + +unsigned int csqc_drawmask = 0; +static char *csqc_printtextbuf = NULL; +static unsigned short *csqc_sv2csqcents; //[515]: server entities numbers on client side. FIXME : make pointers instead of numbers ? + +static mfunction_t *CSQC_Parse_TempEntity; +static mfunction_t *CSQC_Parse_StuffCmd; +static mfunction_t *CSQC_Parse_Print; +static mfunction_t *CSQC_Parse_CenterPrint; +static mfunction_t *CSQC_Ent_Update; +static mfunction_t *CSQC_Ent_Remove; +static mfunction_t *CSQC_Event; + +static int csqc_fieldoff_alpha; +static int csqc_fieldoff_colormod; +static int csqc_fieldoff_effects; +int csqc_fieldoff_scale; +int csqc_fieldoff_renderflags; +int csqc_fieldoff_tag_entity; +int csqc_fieldoff_tag_index; + +qboolean csqc_loaded = false; + +extern entity_t *cl_csqcentities; +extern unsigned char *cl_csqcentities_active; +extern int cl_num_csqcentities; + +vec3_t csqc_origin, csqc_angles; +static double csqc_frametime = 0; +int csqc_buttons; + +static mempool_t *csqc_mempool; + +static void CL_VM_FindEdictFieldOffsets (void) +{ + csqc_fieldoff_alpha = PRVM_ED_FindFieldOffset("alpha"); + csqc_fieldoff_scale = PRVM_ED_FindFieldOffset("scale"); + csqc_fieldoff_colormod = PRVM_ED_FindFieldOffset("colormod"); + csqc_fieldoff_renderflags = PRVM_ED_FindFieldOffset("renderflags"); + csqc_fieldoff_effects = PRVM_ED_FindFieldOffset("effects"); + csqc_fieldoff_tag_entity = PRVM_ED_FindFieldOffset("tag_entity"); + csqc_fieldoff_tag_index = PRVM_ED_FindFieldOffset("tag_index"); + + CSQC_Parse_TempEntity = PRVM_ED_FindFunction (CL_F_PARSE_TEMPENTITY); + CSQC_Parse_StuffCmd = PRVM_ED_FindFunction (CL_F_PARSE_STUFFCMD); + CSQC_Parse_Print = PRVM_ED_FindFunction (CL_F_PARSE_PRINT); + CSQC_Parse_CenterPrint = PRVM_ED_FindFunction (CL_F_PARSE_CENTERPRINT); + CSQC_Ent_Update = PRVM_ED_FindFunction (CL_F_ENT_UPDATE); + CSQC_Ent_Remove = PRVM_ED_FindFunction (CL_F_ENT_REMOVE); + CSQC_Event = PRVM_ED_FindFunction (CL_F_EVENT); + + if(CSQC_Parse_Print) + { + csqc_printtextbuf = Mem_Alloc(csqc_mempool, CSQC_PRINTBUFFERLEN); + csqc_printtextbuf[0] = 0; + } +} + +void CL_VM_Error (const char *format, ...) //[515]: hope it will be never executed =) +{ + char errorstring[4096]; + va_list argptr; + + va_start (argptr, format); + dpvsnprintf (errorstring, sizeof(errorstring), format, argptr); + va_end (argptr); +// Con_Printf( "CL_VM_Error: %s\n", errorstring ); + + PRVM_Crash(); + csqc_loaded = false; + Mem_FreePool(&csqc_mempool); + + Cvar_SetValueQuick(&csqc_progcrc, 0); + +// Host_AbortCurrentFrame(); //[515]: hmmm... if server says it needs csqc then client MUST disconnect + Host_Error(va("CL_VM_Error: %s", errorstring)); +} + +//[515]: set globals before calling R_UpdateView, WEIRD CRAP +static void CSQC_SetGlobals (void) +{ + //extern cvar_t sv_accelerate, sv_friction, sv_gravity, sv_stopspeed, sv_maxspeed; + + CSQC_BEGIN + *prog->time = cl.time; + prog->globals.client->frametime = cl.time - csqc_frametime; + csqc_frametime = cl.time; + prog->globals.client->servercommandframe = cl.servermovesequence; + prog->globals.client->clientcommandframe = cl.movemessages; + VectorCopy(cl.viewangles, prog->globals.client->input_angles); + VectorCopy(cl.viewangles, csqc_angles); + prog->globals.client->input_buttons = csqc_buttons; + VectorSet(prog->globals.client->input_movevalues, cl.cmd.forwardmove, cl.cmd.sidemove, cl.cmd.upmove); + //VectorCopy(cl.movement_origin, csqc_origin); + VectorCopy(cl_entities[cl.viewentity].render.origin, csqc_origin); + VectorCopy(csqc_origin, prog->globals.client->pmove_org); + prog->globals.client->maxclients = cl.maxclients; + //VectorCopy(cl.movement_velocity, prog->globals.client->pmove_vel); + VectorCopy(cl.velocity, prog->globals.client->pmove_vel); + CSQC_END +} + +static void CSQC_Predraw (prvm_edict_t *ed) +{ + int b; + if(!ed->fields.client->predraw) + return; + b = prog->globals.client->self; + prog->globals.client->self = PRVM_EDICT_TO_PROG(ed); + PRVM_ExecuteProgram(ed->fields.client->predraw, "CSQC_Predraw: NULL function\n"); + prog->globals.client->self = b; +} + +static void CSQC_Think (prvm_edict_t *ed) +{ + int b; + if(ed->fields.client->think) + if(ed->fields.client->nextthink && ed->fields.client->nextthink <= *prog->time) + { + ed->fields.client->nextthink = 0; + b = prog->globals.client->self; + prog->globals.client->self = PRVM_EDICT_TO_PROG(ed); + PRVM_ExecuteProgram(ed->fields.client->think, "CSQC_Think: NULL function\n"); + prog->globals.client->self = b; + } +} + +//[515]: weird too +static qboolean CSQC_EdictToEntity (prvm_edict_t *ed, entity_t *e) +{ + int i; + prvm_eval_t *val; + + i = ed->fields.client->modelindex; + e->state_current.modelindex = 0; + if(i >= MAX_MODELS || i <= -MAX_MODELS) //[515]: make work as error ? + { + Con_Print("CSQC_EdictToEntity: modelindex >= MAX_MODELS\n"); + ed->fields.client->modelindex = 0; + } + else + e->state_current.modelindex = i; + if(!e->state_current.modelindex) + return false; + + e->state_current.time = cl.time; + + i = 0; + if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_renderflags)) && val->_float) + { + i = val->_float; + if(i & RF_VIEWMODEL) e->state_current.flags |= RENDER_VIEWMODEL; + if(i & RF_EXTERNALMODEL)e->state_current.flags |= RENDER_EXTERIORMODEL; + if(i & RF_DEPTHHACK) e->state_current.effects |= EF_NODEPTHTEST; + if(i & RF_ADDITIVE) e->state_current.effects |= EF_ADDITIVE; + } + + if(i & RF_USEAXIS) //FIXME!!! + VectorCopy(ed->fields.client->angles, e->persistent.newangles); + else + VectorCopy(ed->fields.client->angles, e->persistent.newangles); + + VectorCopy(ed->fields.client->origin, e->persistent.neworigin); + VectorCopy(ed->fields.client->origin, e->state_current.origin); + e->state_current.colormap = ed->fields.client->colormap; + e->state_current.effects = ed->fields.client->effects; + e->state_current.frame = ed->fields.client->frame; + e->state_current.skin = ed->fields.client->skin; + + if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_alpha)) && val->_float) e->state_current.alpha = val->_float*255; + if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_scale)) && val->_float) e->state_current.scale = val->_float*16; + if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_colormod)) && VectorLength2(val->vector)) VectorScale(val->vector, 32, e->state_current.colormod); + if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_effects)) && val->_float) e->state_current.effects = val->_float; + if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_tag_entity)) && val->edict) + { + e->state_current.tagentity = val->edict; + if((val = PRVM_GETEDICTFIELDVALUE(ed, csqc_fieldoff_tag_index)) && val->_float) + e->state_current.tagindex = val->_float; + } + + return true; +} + +void CSQC_ClearCSQCEntities (void) +{ + memset(cl_csqcentities_active, 0, sizeof(cl_csqcentities_active)); + cl_num_csqcentities = 0; + csqc_drawmask = 0; +} + +void CL_ExpandCSQCEntities (int num); + +void CSQC_RelinkCSQCEntities (void) +{ + int i; + entity_t *e; + prvm_edict_t *ed; + + *prog->time = cl.time; + for(i=1;inum_edicts;i++) + { + if(i >= cl_max_csqcentities) + CL_ExpandCSQCEntities(i); + + e = &cl_csqcentities[i]; + ed = &prog->edicts[i]; + if(ed->priv.required->free) + { + e->state_current.active = false; + cl_csqcentities_active[i] = false; + continue; + } + VectorAdd(ed->fields.client->origin, ed->fields.client->mins, ed->fields.client->absmin); + VectorAdd(ed->fields.client->origin, ed->fields.client->maxs, ed->fields.client->absmax); + CSQC_Think(ed); + if(ed->priv.required->free) + { + e->state_current.active = false; + cl_csqcentities_active[i] = false; + continue; + } + CSQC_Predraw(ed); + if(ed->priv.required->free) + { + e->state_current.active = false; + cl_csqcentities_active[i] = false; + continue; + } + if(!cl_csqcentities_active[i]) + if(!((int)ed->fields.client->drawmask & csqc_drawmask)) + continue; + + e->state_previous = e->state_current; + e->state_current = defaultstate; + if((cl_csqcentities_active[i] = CSQC_EdictToEntity(ed, e))) + { + if(!e->state_current.active) + { + if(!e->state_previous.active) + VectorCopy(ed->fields.client->origin, e->persistent.trail_origin);//[515]: hack to make good gibs =/ + e->state_previous = e->state_current; + e->state_current.active = true; + } + e->persistent.lerpdeltatime = 0;//prog->globals.client->frametime; + cl_num_csqcentities++; + } + } +} + +//[515]: omfg... it's all weird =/ +void CSQC_AddEntity (int n) +{ + cl_csqcentities_active[n] = true; +} + +qboolean CL_VM_InputEvent (qboolean pressed, int key) +{ + qboolean r; + if(!csqc_loaded) + return false; + CSQC_BEGIN + *prog->time = cl.time; + PRVM_G_FLOAT(OFS_PARM0) = pressed; + PRVM_G_FLOAT(OFS_PARM1) = key; + PRVM_ExecuteProgram (prog->globals.client->CSQC_InputEvent, CL_F_INPUTEVENT); + r = CSQC_RETURNVAL; + CSQC_END + return r; +} + +qboolean CL_VM_UpdateView (void) +{ +// vec3_t oldangles; + if(!csqc_loaded) + return false; + CSQC_BEGIN + //VectorCopy(cl.viewangles, oldangles); + *prog->time = cl.time; + CSQC_SetGlobals(); + csqc_drawmask = 0; + cl_num_csqcentities = 0; + PRVM_ExecuteProgram (prog->globals.client->CSQC_UpdateView, CL_F_UPDATEVIEW); + //VectorCopy(oldangles, cl.viewangles); + CSQC_END + csqc_frame = false; + return true; +} + +qboolean CL_VM_ConsoleCommand (const char *cmd) +{ + qboolean r; + if(!csqc_loaded) + return false; + CSQC_BEGIN + *prog->time = cl.time; + PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(cmd); + PRVM_ExecuteProgram (prog->globals.client->CSQC_ConsoleCommand, CL_F_CONSOLECOMMAND); + r = CSQC_RETURNVAL; + CSQC_END + return r; +} + +qboolean CL_VM_Parse_TempEntity (void) +{ + int t; + qboolean r; + if(!csqc_loaded || !CSQC_Parse_TempEntity) + return false; + t = msg_readcount; + CSQC_BEGIN + *prog->time = cl.time; + PRVM_ExecuteProgram ((func_t)(CSQC_Parse_TempEntity - prog->functions), CL_F_PARSE_TEMPENTITY); + r = CSQC_RETURNVAL; + CSQC_END + if(!r) + { + msg_readcount = t; + msg_badread = false; + } + return r; +} + +void CL_VM_Parse_StuffCmd (const char *msg) +{ + if(!csqc_loaded) //[515]: add more here + if(msg[0] == 'c') + if(msg[1] == 's') + if(msg[2] == 'q') + if(msg[3] == 'c') + { + Cvar_SetQuick(&csqc_progcrc, "0"); + csqc_progcrc.flags = 0; + Cmd_ExecuteString (msg, src_command); + csqc_progcrc.flags = CVAR_READONLY; + return; + } + if(!csqc_loaded || !CSQC_Parse_StuffCmd) + { + Cbuf_AddText(msg); + return; + } + CSQC_BEGIN + *prog->time = cl.time; + PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg); + PRVM_ExecuteProgram ((func_t)(CSQC_Parse_StuffCmd - prog->functions), CL_F_PARSE_STUFFCMD); + CSQC_END +} + +static void CL_VM_Parse_Print (const char *msg) +{ + CSQC_BEGIN + *prog->time = cl.time; + PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg); + PRVM_ExecuteProgram ((func_t)(CSQC_Parse_Print - prog->functions), CL_F_PARSE_PRINT); + CSQC_END +} + +void CSQC_AddPrintText (const char *msg) +{ + int i; + if(!csqc_loaded || !CSQC_Parse_Print) + { + Con_Print(msg); + return; + } + i = strlen(msg)-1; + if(msg[i] != '\n' && msg[i] != '\r') + { + if(strlen(csqc_printtextbuf)+i >= CSQC_PRINTBUFFERLEN) + { + CL_VM_Parse_Print(csqc_printtextbuf); + csqc_printtextbuf[0] = 0; + } + else + strcat(csqc_printtextbuf, msg); + return; + } + strcat(csqc_printtextbuf, msg); + CL_VM_Parse_Print(csqc_printtextbuf); + csqc_printtextbuf[0] = 0; +} + +void CL_VM_Parse_CenterPrint (const char *msg) +{ + if(!csqc_loaded || !CSQC_Parse_CenterPrint) + { + SCR_CenterPrint((char*)msg); + return; + } + CSQC_BEGIN + *prog->time = cl.time; + PRVM_G_INT(OFS_PARM0) = PRVM_SetEngineString(msg); + PRVM_ExecuteProgram ((func_t)(CSQC_Parse_CenterPrint - prog->functions), CL_F_PARSE_CENTERPRINT); + CSQC_END +} + +float CL_VM_Event (float event) //[515]: needed ? I'd say "YES", but don't know for what :D +{ + float r; + if(!csqc_loaded || !CSQC_Event) + return 0; + CSQC_BEGIN + *prog->time = cl.time; + PRVM_G_FLOAT(OFS_PARM0) = event; + PRVM_ExecuteProgram ((func_t)(CSQC_Event - prog->functions), CL_F_EVENT); + r = CSQC_RETURNVAL; + CSQC_END + return r; +} + +void CSQC_ReadEntities (void) +{ + unsigned short entnum, oldself, realentnum; + CSQC_BEGIN + *prog->time = cl.time; + oldself = prog->globals.client->self; + while(1) + { + entnum = MSG_ReadShort(); + if(!entnum) + return; + realentnum = entnum & 0x7FFF; + prog->globals.client->self = csqc_sv2csqcents[realentnum]; + if(entnum & 0x8000) + { + if(prog->globals.client->self) + { + PRVM_ExecuteProgram((func_t)(CSQC_Ent_Remove - prog->functions), CL_F_ENT_REMOVE); + csqc_sv2csqcents[realentnum] = 0; + } + else + Con_Printf("Smth bad happens in csqc...\n"); //[515]: never happens ? + } + else + { + if(!prog->globals.client->self) + { + prvm_edict_t *ed; + ed = PRVM_ED_Alloc(); + ed->fields.client->entnum = realentnum; + prog->globals.client->self = csqc_sv2csqcents[realentnum] = PRVM_EDICT_TO_PROG(ed); + PRVM_G_FLOAT(OFS_PARM0) = 1; + } + else + PRVM_G_FLOAT(OFS_PARM0) = 0; + PRVM_ExecuteProgram((func_t)(CSQC_Ent_Update - prog->functions), CL_F_ENT_UPDATE); + } + } + prog->globals.client->self = oldself; + CSQC_END +} + +void Cmd_ClearCsqcFuncs (void); + +void CL_VM_Init (void) +{ + entity_t *ent; + + csqc_loaded = false; + memset(cl.csqc_model_precache, 0, sizeof(cl.csqc_model_precache)); + memset(&cl.csqc_vidvars, true, sizeof(csqc_vidvars_t)); + + if(!FS_FileExists(csqc_progname.string)) + { + if(!sv.active && csqc_progcrc.integer) + { + Con_Printf("CL_VM_Init: server requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string); + CL_Disconnect(); + } + return; + } + else + if(!sv.active && !csqc_progcrc.integer) //[515]: because cheaters may use csqc while server didn't allowed it ! + { + Con_Printf("CL_VM_Init: server didn't sent CSQC crc, so CSQC is disabled\n"); + return; + } + + PRVM_Begin; + PRVM_InitProg(PRVM_CLIENTPROG); + + // allocate the mempools + prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL); + prog->headercrc = CL_PROGHEADER_CRC; + prog->edictprivate_size = 0; // no private struct used + prog->name = csqc_progname.string; + prog->num_edicts = 1; + prog->limit_edicts = CL_MAX_EDICTS; + prog->extensionstring = vm_cl_extensions; + prog->builtins = vm_cl_builtins; + prog->numbuiltins = vm_cl_numbuiltins; + prog->init_cmd = VM_CL_Cmd_Init; + prog->reset_cmd = VM_CL_Cmd_Reset; + prog->error_cmd = CL_VM_Error; + + PRVM_LoadProgs(csqc_progname.string, cl_numrequiredfunc, cl_required_func, 0, NULL); + + if(!sv.active && !cls.demoplayback && prog->filecrc != (unsigned short)csqc_progcrc.integer) + { + Con_Printf("^1Your CSQC version differs from server's one (%i!=%i)\n", prog->filecrc, csqc_progcrc.integer); + PRVM_ResetProg(); + CL_Disconnect(); + return; + } + + if(prog->loaded) + { + Cvar_SetValueQuick(&csqc_progcrc, prog->filecrc); + Con_Printf("CSQC ^5loaded (crc=%i)\n", csqc_progcrc.integer); + } + else + { + CL_VM_Error("CSQC ^2failed to load\n"); + if(!sv.active) + CL_Disconnect(); + return; + } + + csqc_mempool = Mem_AllocPool("CSQC", 0, NULL); + + //[515]: optional fields & funcs + CL_VM_FindEdictFieldOffsets(); + + // set time + *prog->time = cl.time; + csqc_frametime = 0; + + prog->globals.client->mapname = PRVM_SetEngineString(cl.worldmodel->name); + prog->globals.client->player_localentnum = cl.playerentity; + + // call the prog init + PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(CL_F_INIT) - prog->functions), CL_F_INIT); + + PRVM_End; + csqc_loaded = true; + + csqc_sv2csqcents = Mem_Alloc(csqc_mempool, MAX_EDICTS*sizeof(unsigned short)); + memset(csqc_sv2csqcents, 0, MAX_EDICTS*sizeof(unsigned short)); + + cl.csqc_vidvars.drawcrosshair = false; + cl.csqc_vidvars.drawenginesbar = false; + + // local state + ent = &cl_csqcentities[0]; + // entire entity array was cleared, so just fill in a few fields + ent->state_current.active = true; + ent->render.model = cl.worldmodel = cl.model_precache[1]; + ent->render.scale = 1; // some of the renderer still relies on scale + ent->render.alpha = 1; + ent->render.flags = RENDER_SHADOW | RENDER_LIGHT; + Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, 0, 0, 0, 0, 0, 0, 1); + Matrix4x4_Invert_Simple(&ent->render.inversematrix, &ent->render.matrix); + CL_BoundingBoxForEntity(&ent->render); +} + +void CL_VM_ShutDown (void) +{ + Cmd_ClearCsqcFuncs(); + Cvar_SetValueQuick(&csqc_progcrc, 0); + if(!csqc_loaded) + return; + CSQC_BEGIN + *prog->time = cl.time; + PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(CL_F_SHUTDOWN) - prog->functions), CL_F_SHUTDOWN); + PRVM_ResetProg(); + CSQC_END + Con_Print("CSQC ^1unloaded\n"); + csqc_loaded = false; + Mem_FreePool(&csqc_mempool); +} diff --git a/csprogs.h b/csprogs.h new file mode 100644 index 00000000..8d6f2ccb --- /dev/null +++ b/csprogs.h @@ -0,0 +1,64 @@ +#ifndef CSPROGS_H +#define CSPROGS_H + +#define CL_MAX_EDICTS (1<<12) + +#define ENTMASK_ENGINE 1 +#define ENTMASK_ENGINEVIEWMODELS 2 +#define ENTMASK_NORMAL 4 + +#define VF_MIN 1 //(vector) +#define VF_MIN_X 2 //(float) +#define VF_MIN_Y 3 //(float) +#define VF_SIZE 4 //(vector) (viewport size) +#define VF_SIZE_Y 5 //(float) +#define VF_SIZE_X 6 //(float) +#define VF_VIEWPORT 7 //(vector, vector) +#define VF_FOV 8 //(vector) +#define VF_FOVX 9 //(float) +#define VF_FOVY 10 //(float) +#define VF_ORIGIN 11 //(vector) +#define VF_ORIGIN_X 12 //(float) +#define VF_ORIGIN_Y 13 //(float) +#define VF_ORIGIN_Z 14 //(float) +#define VF_ANGLES 15 //(vector) +#define VF_ANGLES_X 16 //(float) +#define VF_ANGLES_Y 17 //(float) +#define VF_ANGLES_Z 18 //(float) + +#define VF_DRAWWORLD 19 //(float) //actually world model and sky +#define VF_DRAWENGINESBAR 20 //(float) +#define VF_DRAWCROSSHAIR 21 //(float) + +#define VF_CL_VIEWANGLES 33 //(vector) //sweet thing for RPGs/... +#define VF_CL_VIEWANGLES_X 34 //(float) +#define VF_CL_VIEWANGLES_Y 35 //(float) +#define VF_CL_VIEWANGLES_Z 36 //(float) + +//#define VF_PERSPECTIVE 200 + +#define RF_VIEWMODEL 1 // The entity is never drawn in mirrors. In engines with realtime lighting, it casts no shadows. +#define RF_EXTERNALMODEL 2 // The entity is appears in mirrors but not in the normal view. It does still cast shadows in engines with realtime lighting. +#define RF_DEPTHHACK 4 // The entity appears closer to the view than normal, either by scaling it wierdly or by just using a depthrange. This will usually be found in conjunction with RF_VIEWMODEL +#define RF_ADDITIVE 8 // Add the entity acording to it's alpha values instead of the normal blend +#define RF_USEAXIS 16 // When set, the entity will use the v_forward, v_right and v_up globals instead of it's angles field for orientation. Angles will be ignored compleatly. + // Note that to use this properly, you'll NEED to use the predraw function to set the globals. +//#define RF_DOUBLESIDED 32 + +extern unsigned int csqc_drawmask; +extern int cl_max_csqcentities; +extern qboolean csqc_frame; +extern int csqc_buttons; +extern qboolean csqc_loaded; +extern qboolean csqc_onground; +extern vec3_t csqc_origin, csqc_angles; +extern unsigned int csqc_drawmask; +extern int csqc_fieldoff_scale; +extern int csqc_fieldoff_renderflags; +extern int csqc_fieldoff_tag_entity; +extern int csqc_fieldoff_tag_index; +extern cvar_t csqc_progcrc; +extern qboolean csqc_usecsqclistener; +extern matrix4x4_t csqc_listenermatrix; + +#endif diff --git a/gl_backend.c b/gl_backend.c index fa007258..9e12d566 100644 --- a/gl_backend.c +++ b/gl_backend.c @@ -1733,6 +1733,9 @@ void R_ClearScreen(void) } } +qboolean CL_VM_UpdateView (void); +void SCR_DrawConsole (void); + int r_stereo_side; void SCR_DrawScreen (void) @@ -1793,7 +1796,10 @@ void SCR_DrawScreen (void) r_refdef.frustum_x *= r_refdef.frustumscale_x; r_refdef.frustum_y *= r_refdef.frustumscale_y; - R_RenderView(); + if(!CL_VM_UpdateView()) + R_RenderView(); + else + SCR_DrawConsole(); if (scr_zoomwindow.integer) { @@ -1810,7 +1816,8 @@ void SCR_DrawScreen (void) r_refdef.frustum_x *= r_refdef.frustumscale_x; r_refdef.frustum_y *= r_refdef.frustumscale_y; - R_RenderView(); + if(!CL_VM_UpdateView()) + R_RenderView(); } } diff --git a/gl_rmain.c b/gl_rmain.c index d8970209..c8216106 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -1097,7 +1097,58 @@ void R_RenderView(void) GL_ScissorTest(false); } +//[515]: csqc +void CSQC_R_ClearScreen (void) +{ + if (!r_refdef.entities/* || !r_refdef.worldmodel*/) + return; //Host_Error ("R_RenderView: NULL worldmodel"); + + r_view_width = bound(0, r_refdef.width, vid.width); + r_view_height = bound(0, r_refdef.height, vid.height); + r_view_depth = 1; + r_view_x = bound(0, r_refdef.x, vid.width - r_refdef.width); + r_view_y = bound(0, r_refdef.y, vid.height - r_refdef.height); + r_view_z = 0; + r_view_matrix = r_refdef.viewentitymatrix; + GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1); + r_rtworld = r_shadow_realtime_world.integer; + r_rtworldshadows = r_shadow_realtime_world_shadows.integer && gl_stencil; + r_rtdlight = (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer) && !gl_flashblend.integer; + r_rtdlightshadows = r_rtdlight && (r_rtworld ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer) && gl_stencil; + r_lightmapintensity = r_rtworld ? r_shadow_realtime_world_lightmaps.value : 1; + + // GL is weird because it's bottom to top, r_view_y is top to bottom + qglViewport(r_view_x, vid.height - (r_view_y + r_view_height), r_view_width, r_view_height); + GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height); + GL_ScissorTest(true); + GL_DepthMask(true); + R_ClearScreen(); + R_Textures_Frame(); + R_UpdateFog(); + R_TimeReport("setup"); +} + +//[515]: csqc +void CSQC_R_RenderScene (void) +{ + qglDepthFunc(GL_LEQUAL); + qglPolygonOffset(0, 0); + qglEnable(GL_POLYGON_OFFSET_FILL); + + R_RenderScene(); + + qglPolygonOffset(0, 0); + qglDisable(GL_POLYGON_OFFSET_FILL); + + R_BlendView(); + R_TimeReport("blendview"); + + GL_Scissor(0, 0, vid.width, vid.height); + GL_ScissorTest(false); +} + extern void R_DrawLightningBeams (void); +extern void VM_AddPolygonsToMeshQueue (void); void R_RenderScene(void) { // don't let sound skip if going slow @@ -1136,21 +1187,24 @@ void R_RenderScene(void) if (r_refdef.extraupdate) S_ExtraUpdate (); - GL_ShowTrisColor(0.025, 0.025, 0, 1); - if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky) + if (cl.csqc_vidvars.drawworld) { - r_refdef.worldmodel->DrawSky(r_refdef.worldentity); - R_TimeReport("worldsky"); - } + GL_ShowTrisColor(0.025, 0.025, 0, 1); + if (r_refdef.worldmodel && r_refdef.worldmodel->DrawSky) + { + r_refdef.worldmodel->DrawSky(r_refdef.worldentity); + R_TimeReport("worldsky"); + } - if (R_DrawBrushModelsSky()) - R_TimeReport("bmodelsky"); + if (R_DrawBrushModelsSky()) + R_TimeReport("bmodelsky"); - GL_ShowTrisColor(0.05, 0.05, 0.05, 1); - if (r_refdef.worldmodel && r_refdef.worldmodel->Draw) - { - r_refdef.worldmodel->Draw(r_refdef.worldentity); - R_TimeReport("world"); + GL_ShowTrisColor(0.05, 0.05, 0.05, 1); + if (r_refdef.worldmodel && r_refdef.worldmodel->Draw) + { + r_refdef.worldmodel->Draw(r_refdef.worldentity); + R_TimeReport("world"); + } } // don't let sound skip if going slow @@ -1176,23 +1230,33 @@ void R_RenderScene(void) GL_ShowTrisColor(0.1, 0, 0, 1); - R_DrawLightningBeams(); - R_TimeReport("lightning"); + if (cl.csqc_vidvars.drawworld) + { + R_DrawLightningBeams(); + R_TimeReport("lightning"); - R_DrawParticles(); - R_TimeReport("particles"); + R_DrawParticles(); + R_TimeReport("particles"); - R_DrawExplosions(); - R_TimeReport("explosions"); + R_DrawExplosions(); + R_TimeReport("explosions"); + } R_MeshQueue_RenderTransparent(); R_TimeReport("drawtrans"); - R_DrawCoronas(); - R_TimeReport("coronas"); + if (cl.csqc_vidvars.drawworld) + { + R_DrawCoronas(); + R_TimeReport("coronas"); + } + if(cl.csqc_vidvars.drawcrosshair) + { + R_DrawWorldCrosshair(); + R_TimeReport("crosshair"); + } - R_DrawWorldCrosshair(); - R_TimeReport("crosshair"); + VM_AddPolygonsToMeshQueue(); R_MeshQueue_Render(); R_MeshQueue_EndScene(); diff --git a/host.c b/host.c index 10b964c1..1064e9c0 100644 --- a/host.c +++ b/host.c @@ -24,6 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "cdaudio.h" #include "cl_video.h" #include "progsvm.h" +#include "csprogs.h" /* @@ -801,7 +802,13 @@ void _Host_Frame (float time) time2 = Sys_DoubleTime(); // update audio - S_Update(&r_refdef.viewentitymatrix); + if(csqc_usecsqclistener) + { + S_Update(&csqc_listenermatrix); + csqc_usecsqclistener = false; + } + else + S_Update(&r_refdef.viewentitymatrix); CDAudio_Update(); diff --git a/keys.c b/keys.c index 7277a99f..97070dd6 100644 --- a/keys.c +++ b/keys.c @@ -820,6 +820,16 @@ Key_Init (void) Cmd_AddCommand ("unbindall", Key_Unbindall_f); } +const char *Key_GetBind (int key) +{ + const char *bind; + bind = keybindings[key_bmap][key]; + if (!bind) + bind = keybindings[key_bmap2][key]; + return bind; +} + +qboolean CL_VM_InputEvent (qboolean pressed, int key); /* =================== @@ -831,12 +841,26 @@ void Key_Event (int key, char ascii, qboolean down) { const char *bind; + qboolean q; // get key binding bind = keybindings[key_bmap][key]; if (!bind) bind = keybindings[key_bmap2][key]; + if(key_dest == key_game) + { + q = CL_VM_InputEvent(!down, key); + if(q) + { + if (down) + keydown[key] = min(keydown[key] + 1, 2); + else + keydown[key] = 0; + return; + } + } + if (!down) { // clear repeat count now that the key is released diff --git a/makefile.inc b/makefile.inc index 3996f070..7354784e 100644 --- a/makefile.inc +++ b/makefile.inc @@ -65,10 +65,12 @@ OBJ_COMMON= \ cl_particles.o \ cl_screen.o \ cl_video.o \ + clvm_cmds.o \ cmd.o \ collision.o \ common.o \ console.o \ + csprogs.o \ curves.o \ cvar.o \ dpvsimpledecode.o \ @@ -83,8 +85,8 @@ OBJ_COMMON= \ host.o \ host_cmd.o \ image.o \ - jpeg.o \ image_png.o \ + jpeg.o \ keys.o \ lhnet.o \ mathlib.o \ @@ -125,6 +127,7 @@ OBJ_COMMON= \ view.o \ wad.o \ world.o \ + world_cs.o \ zone.o # note that builddate.c is very intentionally not compiled to a .o before diff --git a/mvm_cmds.c b/mvm_cmds.c index 47976cfb..92a227c1 100644 --- a/mvm_cmds.c +++ b/mvm_cmds.c @@ -282,45 +282,6 @@ void VM_M_getresolution(void) PRVM_G_VECTOR(OFS_RETURN)[2] = 0; } -/* -========= -VM_M_keynumtostring - -string keynumtostring(float keynum) -========= -*/ -void VM_M_keynumtostring(void) -{ - int keynum; - char *tmp; - VM_SAFEPARMCOUNT(1, VM_M_keynumtostring); - - keynum = PRVM_G_FLOAT(OFS_PARM0); - - tmp = VM_GetTempString(); - - strcpy(tmp, Key_KeynumToString(keynum)); - - PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp); -} - -/* -========= -VM_M_stringtokeynum - -float stringtokeynum(string key) -========= -*/ -void VM_M_stringtokeynum( void ) -{ - const char *str; - VM_SAFEPARMCOUNT( 1, VM_M_keynumtostring ); - - str = PRVM_G_STRING( OFS_PARM0 ); - - PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum( str ); -} - /* ========= VM_M_findkeysforcommand @@ -940,7 +901,8 @@ prvm_builtin_t vm_m_builtins[] = { VM_cin_setstate, VM_cin_getstate, VM_cin_restart, // 465 - 0,0,0,0,0, // 470 + VM_drawline, // 466 + 0,0,0,0, // 470 e10, // 480 e10, // 490 e10, // 500 @@ -954,12 +916,12 @@ prvm_builtin_t vm_m_builtins[] = { VM_M_writetofile, VM_M_isfunction, VM_M_getresolution, - VM_M_keynumtostring, + VM_keynumtostring, VM_M_findkeysforcommand,// 610 VM_M_getserverliststat, VM_M_getserverliststring, VM_parseentitydata, - VM_M_stringtokeynum, + VM_stringtokeynum, VM_M_resetserverlistmasks, VM_M_setserverlistmaskstring, VM_M_setserverlistmasknumber, diff --git a/progs.h b/progs.h index 9afca776..6aeb4f15 100644 --- a/progs.h +++ b/progs.h @@ -123,6 +123,8 @@ extern int eval_cursor_trace_ent; extern int eval_colormod; extern int eval_playermodel; extern int eval_playerskin; +extern int eval_SendEntity; +extern int eval_Version; extern int eval_customizeentityforclient; extern mfunction_t *SV_PlayerPhysicsQC; diff --git a/progsvm.h b/progsvm.h index daa1b5b3..0ef7dff3 100644 --- a/progsvm.h +++ b/progsvm.h @@ -31,6 +31,7 @@ The code uses void pointers instead. #include "pr_comp.h" // defs shared with qcc #include "progdefs.h" // generated by program cdefs +#include "clprogdefs.h" // generated by program cdefs /* typedef union vm_eval_s @@ -203,7 +204,8 @@ typedef struct prvm_edict_s union { void *vp; - entvars_t *server; + entvars_t *server; + cl_entvars_t *client; } fields; } prvm_edict_t; @@ -266,6 +268,7 @@ typedef struct prvm_prog_s union { float *generic; globalvars_t *server; + cl_globalvars_t *client; } globals; int maxknownstrings; diff --git a/protocol.c b/protocol.c index 034b11fe..ba7cb812 100644 --- a/protocol.c +++ b/protocol.c @@ -239,6 +239,137 @@ void EntityFrameQuake_ISeeDeadEntities(void) } } +static mempool_t *sv2csqc = NULL; +int csqc_clent = 0; +sizebuf_t *sv2csqcbuf = NULL; +static unsigned char *sv2csqcents_version[64]; + +void EntityFrameCSQC_ClearVersions (void) +{ + if(sv2csqc) + { + Mem_FreePool(&sv2csqc); + sv2csqc = NULL; + } + memset(sv2csqcents_version, 0, 64*sizeof(unsigned char *)); +} + +void EntityFrameCSQC_InitClientVersions (int client, qboolean clear) +{ + if(!sv2csqc) + sv2csqc = Mem_AllocPool("SV2CSQC", 0, NULL); + if(sv2csqcents_version[client]) + { + Mem_Free(sv2csqcents_version[client]); + sv2csqcents_version[client] = NULL; + } + sv2csqcents_version[client] = Mem_Alloc(sv2csqc, MAX_EDICTS); + memset(sv2csqcents_version[client], 0, MAX_EDICTS); +} + +//[515]: we use only one array per-client for SendEntity feature +void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_state_t *states) +{ + sizebuf_t buf; + unsigned char data[2048]; + const entity_state_t *s; + unsigned short i, t, t2, t0; + prvm_eval_t *val, *val2; + int csqcents = 0; + + if(!eval_SendEntity || !eval_Version) + return; + --csqc_clent; + if(!sv2csqcents_version[csqc_clent]) + EntityFrameCSQC_InitClientVersions(csqc_clent, false); + + for (csqcents = i = 0, s = states;i < numstates;i++, s++) + { + //[515]: entities remove + if(i+1 >= numstates) + t2 = prog->num_edicts; + else + t2 = states[i+1].number; + if(!i) + { + t0 = 1; + t2 = s->number; + } + else + t0 = s->number+1; + for(t=t0; tactive) +// continue; + val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_SendEntity); + if(val->function) + { + val2 = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_Version); + if(sv2csqcents_version[csqc_clent][s->number] == (unsigned char)val2->_float) + continue; + if(!csqcents) + { + csqcents = 1; + memset(&buf, 0, sizeof(buf)); + buf.data = data; + buf.maxsize = sizeof(data); + sv2csqcbuf = &buf; + SZ_Clear(&buf); + MSG_WriteByte(&buf, svc_csqcentities); + } + if((unsigned char)val2->_float == 0) + val2->_float = 1; + MSG_WriteShort(&buf, s->number); + ((int *)prog->globals.generic)[OFS_PARM0] = csqc_clent+1; + prog->globals.server->self = s->number; + PRVM_ExecuteProgram(val->function, "Null SendEntity\n"); + if(!prog->globals.generic[OFS_RETURN]) + { + buf.cursize -= 2; + if(sv2csqcents_version[csqc_clent][s->number]) + { + sv2csqcents_version[csqc_clent][s->number] = 0; + MSG_WriteShort(&buf, (unsigned short)s->number | 0x8000); + csqcents++; + } + } + else + { + sv2csqcents_version[csqc_clent][s->number] = (unsigned char)val2->_float; + csqcents++; + } + if (msg->cursize + buf.cursize > msg->maxsize) + break; + } + } + if(csqcents) + { + if(csqcents > 1) + { + MSG_WriteShort(&buf, 0); + SZ_Write(msg, buf.data, buf.cursize); + } + sv2csqcbuf = NULL; + } +} + void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_state_t *states) { const entity_state_t *s; @@ -246,6 +377,7 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_sta int i, bits; sizebuf_t buf; unsigned char data[128]; + prvm_eval_t *val; // prepare the buffer memset(&buf, 0, sizeof(buf)); @@ -254,6 +386,10 @@ void EntityFrameQuake_WriteFrame(sizebuf_t *msg, int numstates, const entity_sta for (i = 0, s = states;i < numstates;i++, s++) { + val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_SendEntity); + if(val && val->function) + continue; + // prepare the buffer SZ_Clear(&buf); @@ -877,6 +1013,7 @@ void EntityFrame_WriteFrame(sizebuf_t *msg, entityframe_database_t *d, int numst entity_frame_t *o = &deltaframe; const entity_state_t *ent, *delta; vec3_t eye; + prvm_eval_t *val; d->latestframenum++; @@ -906,6 +1043,10 @@ void EntityFrame_WriteFrame(sizebuf_t *msg, entityframe_database_t *d, int numst { ent = states + i; number = ent->number; + + val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[number]), eval_SendEntity); + if(val && val->function) + continue; for (;onum < o->numentities && o->entitydata[onum].number < number;onum++) { // write remove message @@ -1348,6 +1489,7 @@ void EntityFrame4_WriteFrame(sizebuf_t *msg, entityframe4_database_t *d, int num int i, n, startnumber; sizebuf_t buf; unsigned char data[128]; + prvm_eval_t *val; // if there isn't enough space to accomplish anything, skip it if (msg->cursize + 24 > msg->maxsize) @@ -1392,6 +1534,9 @@ void EntityFrame4_WriteFrame(sizebuf_t *msg, entityframe4_database_t *d, int num d->currententitynumber = 1; for (i = 0, n = startnumber;n < prog->max_edicts;n++) { + val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[n]), eval_SendEntity); + if(val && val->function) + continue; // find the old state to delta from e = EntityFrame4_GetReferenceEntity(d, n); // prepare the buffer @@ -1558,6 +1703,12 @@ int EntityState5_Priority(entityframe5_database_t *d, int stateindex) void EntityState5_WriteUpdate(int number, const entity_state_t *s, int changedbits, sizebuf_t *msg) { unsigned int bits = 0; + + prvm_eval_t *val; + val = PRVM_GETEDICTFIELDVALUE((&prog->edicts[s->number]), eval_SendEntity); + if(val && val->function) + return; + if (!s->active) MSG_WriteShort(msg, number | 0x8000); else diff --git a/protocol.h b/protocol.h index 2855e0f8..1054a27b 100644 --- a/protocol.h +++ b/protocol.h @@ -232,7 +232,7 @@ void Protocol_Names(char *buffer, size_t buffersize); #define svc_spawnbaseline2 55 // short modelindex instead of byte #define svc_spawnstatic2 56 // short modelindex instead of byte #define svc_entities 57 // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata -#define svc_unusedlh3 58 +#define svc_csqcentities 58 // [short] entnum [variable length] entitydata ... [short] 0x0000 #define svc_spawnstaticsound2 59 // [coord3] [short] samp [byte] vol [byte] aten // diff --git a/prvm_cmds.c b/prvm_cmds.c index 53b90484..aadcabc8 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -2618,6 +2618,45 @@ void VM_getimagesize(void) PRVM_G_VECTOR(OFS_RETURN)[2] = 0; } +/* +========= +VM_keynumtostring + +string keynumtostring(float keynum) +========= +*/ +void VM_keynumtostring (void) +{ + int keynum; + char *tmp; + VM_SAFEPARMCOUNT(1, VM_keynumtostring); + + keynum = PRVM_G_FLOAT(OFS_PARM0); + + tmp = VM_GetTempString(); + + strcpy(tmp, Key_KeynumToString(keynum)); + + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(tmp); +} + +/* +========= +VM_stringtokeynum + +float stringtokeynum(string key) +========= +*/ +void VM_stringtokeynum (void) +{ + const char *str; + VM_SAFEPARMCOUNT( 1, VM_keynumtostring ); + + str = PRVM_G_STRING( OFS_PARM0 ); + + PRVM_G_INT(OFS_RETURN) = Key_StringToKeynum( str ); +} + // CL_Video interface functions /* @@ -2735,6 +2774,314 @@ void VM_cin_restart( void ) CL_RestartVideo( video ); } +/* +============== +VM_vectorvectors + +Writes new values for v_forward, v_up, and v_right based on the given forward vector +vectorvectors(vector, vector) +============== +*/ +void VM_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); +} + +/* +======================== +VM_drawline + +void drawline(float width, vector pos1, vector pos2, vector rgb, float alpha, float flags) +======================== +*/ +void VM_drawline (void) +{ + float *c1, *c2, *rgb; + float alpha, width; + unsigned char flags; + + VM_SAFEPARMCOUNT(6, VM_drawline); + width = PRVM_G_FLOAT(OFS_PARM0); + c1 = PRVM_G_VECTOR(OFS_PARM1); + c2 = PRVM_G_VECTOR(OFS_PARM2); + rgb = PRVM_G_VECTOR(OFS_PARM3); + alpha = PRVM_G_FLOAT(OFS_PARM4); + flags = PRVM_G_FLOAT(OFS_PARM5); + DrawQ_Line(width, c1[0], c1[1], c2[0], c2[1], rgb[0], rgb[1], rgb[2], alpha, flags); +} + +//==================== +//QC POLYGON functions +//==================== + +typedef struct +{ + rtexture_t *tex; + float data[36]; //[515]: enough for polygons + unsigned char flags; //[515]: + VM_POLYGON_2D and VM_POLYGON_FL4V flags +}vm_polygon_t; + +//static float vm_polygon_linewidth = 1; +static mempool_t *vm_polygons_pool = NULL; +static unsigned char vm_current_vertices = 0; +static qboolean vm_polygons_initialized = false; +static vm_polygon_t *vm_polygons = NULL; +static unsigned long vm_polygons_num = 0, vm_drawpolygons_num = 0; //[515]: ok long on 64bit ? +static qboolean vm_polygonbegin = false; //[515]: for "no-crap-on-the-screen" check +#define VM_DEFPOLYNUM 64 //[515]: enough for default ? + +#define VM_POLYGON_FL3V 16 //more than 2 vertices (used only for lines) +#define VM_POLYGON_FLLINES 32 +#define VM_POLYGON_FL2D 64 +#define VM_POLYGON_FL4V 128 //4 vertices + +void VM_InitPolygons (void) +{ + vm_polygons_pool = Mem_AllocPool("VMPOLY", 0, NULL); + vm_polygons = Mem_Alloc(vm_polygons_pool, VM_DEFPOLYNUM*sizeof(vm_polygon_t)); + memset(vm_polygons, 0, VM_DEFPOLYNUM*sizeof(vm_polygon_t)); + vm_polygons_num = VM_DEFPOLYNUM; + vm_polygonbegin = vm_drawpolygons_num = 0; + vm_polygons_initialized = true; +} + +void VM_DrawPolygonCallback (const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight) +{ + const vm_polygon_t *p = &vm_polygons[surfacenumber]; + int flags = p->flags & 0x0f; + + if(flags == DRAWFLAG_ADDITIVE) + GL_BlendFunc(GL_SRC_ALPHA, GL_ONE); + else if(flags == DRAWFLAG_MODULATE) + GL_BlendFunc(GL_DST_COLOR, GL_ZERO); + else if(flags == DRAWFLAG_2XMODULATE) + GL_BlendFunc(GL_DST_COLOR,GL_SRC_COLOR); + else + GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + R_Mesh_TexBind(0, R_GetTexture(p->tex)); + + //[515]: is speed is max ? + if(p->flags & VM_POLYGON_FLLINES) //[515]: lines + { + qglLineWidth(p->data[13]); + qglBegin(GL_LINE_LOOP); + qglTexCoord1f (p->data[12]); + qglColor4f (p->data[20], p->data[21], p->data[22], p->data[23]); + qglVertex3f (p->data[0] , p->data[1], p->data[2]); + + qglTexCoord1f (p->data[14]); + qglColor4f (p->data[24], p->data[25], p->data[26], p->data[27]); + qglVertex3f (p->data[3] , p->data[4], p->data[5]); + + if(p->flags & VM_POLYGON_FL3V) + { + qglTexCoord1f (p->data[16]); + qglColor4f (p->data[28], p->data[29], p->data[30], p->data[31]); + qglVertex3f (p->data[6] , p->data[7], p->data[8]); + + if(p->flags & VM_POLYGON_FL4V) + { + qglTexCoord1f (p->data[18]); + qglColor4f (p->data[32], p->data[33], p->data[34], p->data[35]); + qglVertex3f (p->data[9] , p->data[10], p->data[11]); + } + } + qglEnd(); + } + else + { + qglBegin(GL_POLYGON); + qglTexCoord2f (p->data[12], p->data[13]); + qglColor4f (p->data[20], p->data[21], p->data[22], p->data[23]); + qglVertex3f (p->data[0] , p->data[1], p->data[2]); + + qglTexCoord2f (p->data[14], p->data[15]); + qglColor4f (p->data[24], p->data[25], p->data[26], p->data[27]); + qglVertex3f (p->data[3] , p->data[4], p->data[5]); + + qglTexCoord2f (p->data[16], p->data[17]); + qglColor4f (p->data[28], p->data[29], p->data[30], p->data[31]); + qglVertex3f (p->data[6] , p->data[7], p->data[8]); + + if(p->flags & VM_POLYGON_FL4V) + { + qglTexCoord2f (p->data[18], p->data[19]); + qglColor4f (p->data[32], p->data[33], p->data[34], p->data[35]); + qglVertex3f (p->data[9] , p->data[10], p->data[11]); + } + qglEnd(); + } +} + +void VM_AddPolygonTo2DScene (vm_polygon_t *p) +{ + drawqueuemesh_t mesh; + static int picelements[6] = {0, 1, 2, 0, 2, 3}; + + mesh.texture = p->tex; + mesh.data_element3i = picelements; + mesh.data_vertex3f = p->data; + mesh.data_texcoord2f = p->data + 12; + mesh.data_color4f = p->data + 20; + if(p->flags & VM_POLYGON_FL4V) + { + mesh.num_vertices = 4; + mesh.num_triangles = 2; + } + else + { + mesh.num_vertices = 3; + mesh.num_triangles = 1; + } + if(p->flags & VM_POLYGON_FLLINES) //[515]: lines + DrawQ_LineLoop (&mesh, (p->flags&0x0f)); + else + DrawQ_Mesh (&mesh, (p->flags&0x0f)); +} + +//void(string texturename, float flag, float 2d, float lines) R_BeginPolygon +void VM_R_PolygonBegin (void) +{ + vm_polygon_t *p; + const char *picname; + if(prog->argc < 2) + VM_SAFEPARMCOUNT(2, VM_R_PolygonBegin); + + if(!vm_polygons_initialized) + VM_InitPolygons(); + if(vm_polygonbegin) + { + Con_Printf("VM_R_PolygonBegin: called twice without VM_R_PolygonEnd after first\n"); + return; + } + if(vm_drawpolygons_num >= vm_polygons_num) + { + p = Mem_Alloc(vm_polygons_pool, 2 * vm_polygons_num * sizeof(vm_polygon_t)); + memset(p, 0, 2 * vm_polygons_num * sizeof(vm_polygon_t)); + memcpy(p, vm_polygons, vm_polygons_num * sizeof(vm_polygon_t)); + Mem_Free(vm_polygons); + vm_polygons = p; + vm_polygons_num *= 2; + } + p = &vm_polygons[vm_drawpolygons_num]; + picname = PRVM_G_STRING(OFS_PARM0); + if(picname[0]) + p->tex = Draw_CachePic(picname, false)->tex; + else + p->tex = r_texture_notexture; + p->flags = (unsigned char)PRVM_G_FLOAT(OFS_PARM1); + vm_current_vertices = 0; + vm_polygonbegin = true; + if(prog->argc >= 3) + { + if(PRVM_G_FLOAT(OFS_PARM2)) + p->flags |= VM_POLYGON_FL2D; + if(prog->argc >= 4 && PRVM_G_FLOAT(OFS_PARM3)) + { + p->data[13] = PRVM_G_FLOAT(OFS_PARM3); //[515]: linewidth + p->flags |= VM_POLYGON_FLLINES; + } + } +} + +//void(vector org, vector texcoords, vector rgb, float alpha) R_PolygonVertex +void VM_R_PolygonVertex (void) +{ + float *coords, *tx, *rgb, alpha; + vm_polygon_t *p; + VM_SAFEPARMCOUNT(4, VM_R_PolygonVertex); + + if(!vm_polygonbegin) + { + Con_Printf("VM_R_PolygonVertex: VM_R_PolygonBegin wasn't called\n"); + return; + } + coords = PRVM_G_VECTOR(OFS_PARM0); + tx = PRVM_G_VECTOR(OFS_PARM1); + rgb = PRVM_G_VECTOR(OFS_PARM2); + alpha = PRVM_G_FLOAT(OFS_PARM3); + + p = &vm_polygons[vm_drawpolygons_num]; + if(vm_current_vertices > 4) + { + Con_Printf("VM_R_PolygonVertex: may have 4 vertices max\n"); + return; + } + + p->data[vm_current_vertices*3] = coords[0]; + p->data[1+vm_current_vertices*3] = coords[1]; + if(!(p->flags & VM_POLYGON_FL2D)) + p->data[2+vm_current_vertices*3] = coords[2]; + + p->data[12+vm_current_vertices*2] = tx[0]; + if(!(p->flags & VM_POLYGON_FLLINES)) + p->data[13+vm_current_vertices*2] = tx[1]; + + p->data[20+vm_current_vertices*4] = rgb[0]; + p->data[21+vm_current_vertices*4] = rgb[1]; + p->data[22+vm_current_vertices*4] = rgb[2]; + p->data[23+vm_current_vertices*4] = alpha; + + vm_current_vertices++; + if(vm_current_vertices == 4) + p->flags |= VM_POLYGON_FL4V; + else + if(vm_current_vertices == 3) + p->flags |= VM_POLYGON_FL3V; +} + +//void() R_EndPolygon +void VM_R_PolygonEnd (void) +{ + if(!vm_polygonbegin) + { + Con_Printf("VM_R_PolygonEnd: VM_R_PolygonBegin wasn't called\n"); + return; + } + if(vm_current_vertices > 2 || (vm_current_vertices >= 2 && vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FLLINES)) + { + if(vm_polygons[vm_drawpolygons_num].flags & VM_POLYGON_FL2D) //[515]: don't use qcpolygons memory if 2D + VM_AddPolygonTo2DScene(&vm_polygons[vm_drawpolygons_num]); + else + vm_drawpolygons_num++; + } + else + Con_Printf("VM_R_PolygonEnd: %i vertices isn't a good choice\n", vm_current_vertices); + vm_polygonbegin = false; +} + +void VM_AddPolygonsToMeshQueue (void) +{ + unsigned int i; + if(!vm_drawpolygons_num) + return; + for(i = 0;i < vm_drawpolygons_num;i++) + R_MeshQueue_Add(VM_DrawPolygonCallback, NULL, i, NULL); + vm_drawpolygons_num = 0; +} + + + + +// float(float number, float quantity) bitshift (EXT_BITSHIFT) +void VM_bitshift (void) +{ + int n1, n2; + VM_SAFEPARMCOUNT(2, VM_bitshift); + + n1 = (int)fabs((int)PRVM_G_FLOAT(OFS_PARM0)); + n2 = (int)PRVM_G_FLOAT(OFS_PARM1); + if(!n1) + PRVM_G_FLOAT(OFS_RETURN) = n1; + else + if(n2 < 0) + PRVM_G_FLOAT(OFS_RETURN) = (n1 >> -n2); + else + PRVM_G_FLOAT(OFS_RETURN) = (n1 << n2); +} + //////////////////////////////////////// // AltString functions //////////////////////////////////////// @@ -2942,11 +3289,494 @@ void VM_altstr_ins(void) PRVM_G_INT( OFS_RETURN ) = PRVM_SetEngineString( outstr ); } + +//////////////////////////////////////// +// BufString functions +//////////////////////////////////////// +//[515]: string buffers support +#define MAX_QCSTR_BUFFERS 128 +#define MAX_QCSTR_STRINGS 1024 + +typedef struct +{ + int num_strings; + char *strings[MAX_QCSTR_STRINGS]; +}qcstrbuffer_t; + +static qcstrbuffer_t *qcstringbuffers[MAX_QCSTR_BUFFERS]; +static int num_qcstringbuffers; +static int buf_sortpower; + +#define BUFSTR_BUFFER(a) (a>=MAX_QCSTR_BUFFERS) ? NULL : (qcstringbuffers[a]) +#define BUFSTR_ISFREE(a) (anum_strings<=0) ? 1 : 0 + +static int BufStr_FindFreeBuffer (void) +{ + int i; + if(num_qcstringbuffers == MAX_QCSTR_BUFFERS) + return -1; + for(i=0;inum_strings > 0) + { + for(i=0;inum_strings;i++) + if(b->strings[i]) + free(b->strings[i]); + num_qcstringbuffers--; + } + free(qcstringbuffers[index]); + qcstringbuffers[index] = NULL; + } +} + +static int BufStr_FindFreeString (qcstrbuffer_t *b) +{ + int i; + for(i=0;inum_strings;i++) + if(!b->strings[i] || !b->strings[i][0]) + return i; + if(i == MAX_QCSTR_STRINGS) return -1; + else return i; +} + +static int BufStr_SortStringsUP (const void *in1, const void *in2) +{ + const char *a, *b; + a = *((const char **) in1); + b = *((const char **) in2); + if(!a[0]) return 1; + if(!b[0]) return -1; + return strncmp(a, b, buf_sortpower); +} + +static int BufStr_SortStringsDOWN (const void *in1, const void *in2) +{ + const char *a, *b; + a = *((const char **) in1); + b = *((const char **) in2); + if(!a[0]) return 1; + if(!b[0]) return -1; + return strncmp(b, a, buf_sortpower); +} + +#ifdef REMOVETHIS +static void VM_BufStr_Init (void) +{ + memset(qcstringbuffers, 0, sizeof(qcstringbuffers)); + num_qcstringbuffers = 0; +} + +static void VM_BufStr_ShutDown (void) +{ + int i; + for(i=0;i= 0) + num_qcstringbuffers++; + //else + //Con_Printf("VM_buf_create: buffers overflow in %s\n", PRVM_NAME); + PRVM_G_FLOAT(OFS_RETURN) = i; +} + +/* +======================== +VM_buf_del +deletes buffer and all strings in it +void buf_del(float bufhandle) = #461; +======================== +*/ +void VM_buf_del (void) +{ + VM_SAFEPARMCOUNT(1, VM_buf_del); + if(BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0))) + BufStr_ClearBuffer((int)PRVM_G_FLOAT(OFS_PARM0)); + else + { + Con_Printf("VM_buf_del: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } +} + +/* +======================== +VM_buf_getsize +how many strings are stored in buffer +float buf_getsize(float bufhandle) = #462; +======================== +*/ +void VM_buf_getsize (void) +{ + qcstrbuffer_t *b; + VM_SAFEPARMCOUNT(1, VM_buf_getsize); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + PRVM_G_FLOAT(OFS_RETURN) = -1; + Con_Printf("VM_buf_getsize: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + else + PRVM_G_FLOAT(OFS_RETURN) = b->num_strings; +} + +/* +======================== +VM_buf_copy +copy all content from one buffer to another, make sure it exists +void buf_copy(float bufhandle_from, float bufhandle_to) = #463; +======================== +*/ +void VM_buf_copy (void) +{ + qcstrbuffer_t *b1, *b2; + int i; + VM_SAFEPARMCOUNT(2, VM_buf_copy); + + b1 = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b1) + { + Con_Printf("VM_buf_copy: invalid source buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + i = PRVM_G_FLOAT(OFS_PARM1); + if(i == (int)PRVM_G_FLOAT(OFS_PARM0)) + { + Con_Printf("VM_buf_copy: source == destination (%i) in %s\n", i, PRVM_NAME); + return; + } + b2 = BUFSTR_BUFFER(i); + if(!b2) + { + Con_Printf("VM_buf_copy: invalid destination buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME); + return; + } + + BufStr_ClearBuffer(i); + qcstringbuffers[i] = malloc(sizeof(qcstrbuffer_t)); + memset(qcstringbuffers[i], 0, sizeof(qcstrbuffer_t)); + b2->num_strings = b1->num_strings; + + for(i=0;inum_strings;i++) + if(b1->strings[i] && b1->strings[i][0]) + { + b2->strings[i] = malloc(strlen(b1->strings[i])+1); + if(!b2->strings[i]) + { + Con_Printf("VM_buf_copy: not enough memory for buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM1), PRVM_NAME); + break; + } + strcpy(b2->strings[i], b1->strings[i]); + } +} + +/* +======================== +VM_buf_sort +sort buffer by beginnings of strings (sortpower defaults it's lenght) +"backward == TRUE" means that sorting goes upside-down +void buf_sort(float bufhandle, float sortpower, float backward) = #464; +======================== +*/ +void VM_buf_sort (void) +{ + qcstrbuffer_t *b; + int i; + VM_SAFEPARMCOUNT(3, VM_buf_sort); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + Con_Printf("VM_buf_sort: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + if(b->num_strings <= 0) + { + Con_Printf("VM_buf_sort: tried to sort empty buffer %i in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + buf_sortpower = PRVM_G_FLOAT(OFS_PARM1); + if(buf_sortpower <= 0) + buf_sortpower = 99999999; + + if(!PRVM_G_FLOAT(OFS_PARM2)) + qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsUP); + else + qsort(b->strings, b->num_strings, sizeof(char*), BufStr_SortStringsDOWN); + + for(i=b->num_strings-1;i>=0;i--) //[515]: delete empty lines + if(b->strings) + { + if(b->strings[i][0]) + break; + else + { + free(b->strings[i]); + --b->num_strings; + b->strings[i] = NULL; + } + } + else + --b->num_strings; +} + +/* +======================== +VM_buf_implode +concantenates all buffer string into one with "glue" separator and returns it as tempstring +string buf_implode(float bufhandle, string glue) = #465; +======================== +*/ +void VM_buf_implode (void) +{ + qcstrbuffer_t *b; + char *k; + const char *sep; + int i, l; + VM_SAFEPARMCOUNT(2, VM_buf_implode); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + PRVM_G_INT(OFS_RETURN) = 0; + if(!b) + { + Con_Printf("VM_buf_implode: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + if(!b->num_strings) + return; + sep = PRVM_G_STRING(OFS_PARM1); + k = VM_GetTempString(); + k[0] = 0; + for(l=i=0;inum_strings;i++) + if(b->strings[i]) + { + l += strlen(b->strings[i]); + if(l>=4095) + break; + k = strcat(k, b->strings[i]); + if(!k) + break; + if(sep && (i != b->num_strings-1)) + { + l += strlen(sep); + if(l>=4095) + break; + k = strcat(k, sep); + if(!k) + break; + } + } + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(k); +} + +/* +======================== +VM_bufstr_get +get a string from buffer, returns direct pointer, dont str_unzone it! +string bufstr_get(float bufhandle, float string_index) = #465; +======================== +*/ +void VM_bufstr_get (void) +{ + qcstrbuffer_t *b; + int strindex; + VM_SAFEPARMCOUNT(2, VM_bufstr_get); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + Con_Printf("VM_bufstr_get: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + strindex = (int)PRVM_G_FLOAT(OFS_PARM1); + if(strindex < 0 || strindex > MAX_QCSTR_STRINGS) + { + Con_Printf("VM_bufstr_get: invalid string index %i used in %s\n", strindex, PRVM_NAME); + return; + } + PRVM_G_INT(OFS_RETURN) = 0; + if(b->num_strings <= strindex) + return; + if(b->strings[strindex]) + PRVM_G_INT(OFS_RETURN) = PRVM_SetEngineString(b->strings[strindex]); +} + +/* +======================== +VM_bufstr_set +copies a string into selected slot of buffer +void bufstr_set(float bufhandle, float string_index, string str) = #466; +======================== +*/ +void VM_bufstr_set (void) +{ + int bufindex, strindex; + qcstrbuffer_t *b; + const char *news; + + VM_SAFEPARMCOUNT(3, VM_bufstr_set); + + bufindex = PRVM_G_FLOAT(OFS_PARM0); + b = BUFSTR_BUFFER(bufindex); + if(!b) + { + Con_Printf("VM_bufstr_set: invalid buffer %i used in %s\n", bufindex, PRVM_NAME); + return; + } + strindex = PRVM_G_FLOAT(OFS_PARM1); + if(strindex < 0 || strindex > MAX_QCSTR_STRINGS) + { + Con_Printf("VM_bufstr_set: invalid string index %i used in %s\n", strindex, PRVM_NAME); + return; + } + news = PRVM_G_STRING(OFS_PARM2); + if(!news) + { + Con_Printf("VM_bufstr_set: null string used in %s\n", PRVM_NAME); + return; + } + if(b->strings[strindex]) + free(b->strings[strindex]); + b->strings[strindex] = malloc(strlen(news)+1); + strcpy(b->strings[strindex], news); +} + +/* +======================== +VM_bufstr_add +adds string to buffer in nearest free slot and returns it +"order == TRUE" means that string will be added after last "full" slot +float bufstr_add(float bufhandle, string str, float order) = #467; +======================== +*/ +void VM_bufstr_add (void) +{ + int bufindex, order, strindex; + qcstrbuffer_t *b; + const char *string; + + VM_SAFEPARMCOUNT(3, VM_bufstr_add); + + bufindex = PRVM_G_FLOAT(OFS_PARM0); + b = BUFSTR_BUFFER(bufindex); + PRVM_G_FLOAT(OFS_RETURN) = -1; + if(!b) + { + Con_Printf("VM_bufstr_add: invalid buffer %i used in %s\n", bufindex, PRVM_NAME); + return; + } + string = PRVM_G_STRING(OFS_PARM1); + if(!string) + { + Con_Printf("VM_bufstr_add: null string used in %s\n", PRVM_NAME); + return; + } + + order = PRVM_G_FLOAT(OFS_PARM2); + if(order) + strindex = b->num_strings; + else + { + strindex = BufStr_FindFreeString(b); + if(strindex < 0) + { + Con_Printf("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME); + return; + } + } + + while(b->num_strings <= strindex) + { + if(b->num_strings == MAX_QCSTR_STRINGS) + { + Con_Printf("VM_bufstr_add: buffer %i has no free string slots in %s\n", bufindex, PRVM_NAME); + return; + } + b->strings[b->num_strings] = NULL; + b->num_strings++; + } + if(b->strings[strindex]) + free(b->strings[strindex]); + b->strings[strindex] = malloc(strlen(string)+1); + strcpy(b->strings[strindex], string); + PRVM_G_FLOAT(OFS_RETURN) = strindex; +} + +/* +======================== +VM_bufstr_free +delete string from buffer +void bufstr_free(float bufhandle, float string_index) = #468; +======================== +*/ +void VM_bufstr_free (void) +{ + int i; + qcstrbuffer_t *b; + VM_SAFEPARMCOUNT(2, VM_bufstr_free); + + b = BUFSTR_BUFFER((int)PRVM_G_FLOAT(OFS_PARM0)); + if(!b) + { + Con_Printf("VM_bufstr_free: invalid buffer %i used in %s\n", (int)PRVM_G_FLOAT(OFS_PARM0), PRVM_NAME); + return; + } + i = PRVM_G_FLOAT(OFS_PARM1); + if(i < 0 || i > MAX_QCSTR_STRINGS) + { + Con_Printf("VM_bufstr_free: invalid string index %i used in %s\n", i, PRVM_NAME); + return; + } + if(b->strings[i]) + free(b->strings[i]); + b->strings[i] = NULL; + if(i+1 == b->num_strings) + --b->num_strings; +} + +//============= + void VM_Cmd_Init(void) { // only init the stuff for the current prog VM_Files_Init(); VM_Search_Init(); +// VM_BufStr_Init(); + if(vm_polygons_initialized) + { + Mem_FreePool(&vm_polygons_pool); + vm_polygons_initialized = false; + } } void VM_Cmd_Reset(void) @@ -2954,6 +3784,11 @@ void VM_Cmd_Reset(void) CL_PurgeOwner( MENUOWNER ); VM_Search_Reset(); VM_Files_CloseAll(); +// VM_BufStr_ShutDown(); + if(vm_polygons_initialized) + { + Mem_FreePool(&vm_polygons_pool); + vm_polygons_initialized = false; + } } - diff --git a/prvm_cmds.h b/prvm_cmds.h index 51ccd3e3..5c9673f0 100644 --- a/prvm_cmds.h +++ b/prvm_cmds.h @@ -322,17 +322,40 @@ void VM_drawsetcliparea(void); void VM_drawresetcliparea(void); void VM_getimagesize(void); +void VM_vectorvectors (void); + +void VM_keynumtostring (void); +void VM_stringtokeynum (void); + void VM_cin_open( void ); void VM_cin_close( void ); void VM_cin_setstate( void ); void VM_cin_getstate( void ); void VM_cin_restart( void ); +void VM_drawline (void); +void VM_R_PolygonBegin (void); +void VM_R_PolygonVertex (void); +void VM_R_PolygonEnd (void); + +void VM_bitshift (void); + void VM_altstr_count( void ); void VM_altstr_prepare( void ); void VM_altstr_get( void ); void VM_altstr_set( void ); void VM_altstr_ins(void); +void VM_buf_create(void); +void VM_buf_del (void); +void VM_buf_getsize (void); +void VM_buf_copy (void); +void VM_buf_sort (void); +void VM_buf_implode (void); +void VM_bufstr_get (void); +void VM_bufstr_set (void); +void VM_bufstr_add (void); +void VM_bufstr_free (void); + void VM_Cmd_Init(void); void VM_Cmd_Reset(void); diff --git a/sbar.c b/sbar.c index 16513a97..9501a26a 100644 --- a/sbar.c +++ b/sbar.c @@ -485,6 +485,22 @@ Sbar_SortFrags */ static int fragsort[MAX_SCOREBOARD]; static int scoreboardlines; + +//[515]: Sbar_GetPlayer for csqc "getplayerkey" func +int Sbar_GetPlayer (int index) +{ + if(index < 0) + { + index = -1-index; + if(index >= scoreboardlines) + return -1; + index = fragsort[index]; + } + if(index >= scoreboardlines) + return -1; + return index; +} + static scoreboard_t teams[MAX_SCOREBOARD]; static int teamsort[MAX_SCOREBOARD]; static int teamlines; @@ -1024,327 +1040,331 @@ extern float v_dmg_time, v_dmg_roll, v_dmg_pitch; extern cvar_t v_kicktime; void Sbar_Draw (void) { - if (cl.intermission == 1) - { - Sbar_IntermissionOverlay(); - return; - } - else if (cl.intermission == 2) - { - Sbar_FinaleOverlay(); - return; - } - - if (gamemode == GAME_NETHERWORLD) + if(cl.csqc_vidvars.drawenginesbar) //[515]: csqc drawsbar { - } - else if (gamemode == GAME_SOM) - { - if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer)) - Sbar_DrawScoreboard (); - else if (sb_lines) + if (cl.intermission == 1) { - // this is the top left of the sbar area - sbar_x = 0; - sbar_y = vid_conheight.integer - 24*3; + Sbar_IntermissionOverlay(); + return; + } + else if (cl.intermission == 2) + { + Sbar_FinaleOverlay(); + return; + } - // armor - if (cl.stats[STAT_ARMOR]) + if (gamemode == GAME_NETHERWORLD) + { + } + else if (gamemode == GAME_SOM) + { + if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer)) + Sbar_DrawScoreboard (); + else if (sb_lines) { - if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - Sbar_DrawPic(0, 0, somsb_armor[2]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - Sbar_DrawPic(0, 0, somsb_armor[1]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - Sbar_DrawPic(0, 0, somsb_armor[0]); - Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); - } + // this is the top left of the sbar area + sbar_x = 0; + sbar_y = vid_conheight.integer - 24*3; - // health - Sbar_DrawPic(0, 24, somsb_health); - Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25); - - // ammo icon - if (cl.stats[STAT_ITEMS] & IT_SHELLS) - Sbar_DrawPic(0, 48, somsb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & IT_NAILS) - Sbar_DrawPic(0, 48, somsb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) - Sbar_DrawPic(0, 48, somsb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & IT_CELLS) - Sbar_DrawPic(0, 48, somsb_ammo[3]); - Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false); - if (cl.stats[STAT_SHELLS]) - Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true); - } - } - else if (gamemode == GAME_NEXUIZ) - { - sbar_y = vid_conheight.integer - 47; - sbar_x = (vid_conwidth.integer - 640)/2; + // armor + if (cl.stats[STAT_ARMOR]) + { + if (cl.stats[STAT_ITEMS] & IT_ARMOR3) + Sbar_DrawPic(0, 0, somsb_armor[2]); + else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) + Sbar_DrawPic(0, 0, somsb_armor[1]); + else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) + Sbar_DrawPic(0, 0, somsb_armor[0]); + Sbar_DrawNum(24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); + } - if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer)) - { - Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value); - Sbar_DrawScoreboard (); + // health + Sbar_DrawPic(0, 24, somsb_health); + Sbar_DrawNum(24, 24, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25); + + // ammo icon + if (cl.stats[STAT_ITEMS] & IT_SHELLS) + Sbar_DrawPic(0, 48, somsb_ammo[0]); + else if (cl.stats[STAT_ITEMS] & IT_NAILS) + Sbar_DrawPic(0, 48, somsb_ammo[1]); + else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) + Sbar_DrawPic(0, 48, somsb_ammo[2]); + else if (cl.stats[STAT_ITEMS] & IT_CELLS) + Sbar_DrawPic(0, 48, somsb_ammo[3]); + Sbar_DrawNum(24, 48, cl.stats[STAT_AMMO], 3, false); + if (cl.stats[STAT_SHELLS]) + Sbar_DrawNum(24 + 3*24, 48, cl.stats[STAT_SHELLS], 1, true); + } } - else if (sb_lines) + else if (gamemode == GAME_NEXUIZ) { - int i; - double time; - float fade; + sbar_y = vid_conheight.integer - 47; + sbar_x = (vid_conwidth.integer - 640)/2; - // we have a max time 2s (min time = 0) - if ((time = cl.time - cl.weapontime) < 2) + if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer)) { - fade = (1.0 - 0.5 * time); - fade *= fade; - for (i = 0; i < 8;i++) - if (cl.stats[STAT_ITEMS] & (1 << i)) - Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON])); - - if((cl.stats[STAT_ITEMS] & (1<<12))) - Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1)); + Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value); + Sbar_DrawScoreboard (); } + else if (sb_lines) + { + int i; + double time; + float fade; - //if (!cl.islocalgame) - // Sbar_DrawFrags (); + // we have a max time 2s (min time = 0) + if ((time = cl.time - cl.weapontime) < 2) + { + fade = (1.0 - 0.5 * time); + fade *= fade; + for (i = 0; i < 8;i++) + if (cl.stats[STAT_ITEMS] & (1 << i)) + Sbar_DrawWeapon(i + 1, fade, (i + 2 == cl.stats[STAT_ACTIVEWEAPON])); + + if((cl.stats[STAT_ITEMS] & (1<<12))) + Sbar_DrawWeapon(0, fade, (cl.stats[STAT_ACTIVEWEAPON] == 1)); + } - if (sb_lines > 24) - Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value); - else - Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value); + //if (!cl.islocalgame) + // Sbar_DrawFrags (); - // special items - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) - { - // Nexuiz has no anum pics - //Sbar_DrawNum (36, 0, 666, 3, 1); - // Nexuiz has no disc pic - //Sbar_DrawPic (0, 0, sb_disc); - } + if (sb_lines > 24) + Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_fg.value); + else + Sbar_DrawAlphaPic (0, 0, sb_sbar_minimal, sbar_alpha_fg.value); - // armor - Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0); + // special items + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) + { + // Nexuiz has no anum pics + //Sbar_DrawNum (36, 0, 666, 3, 1); + // Nexuiz has no disc pic + //Sbar_DrawPic (0, 0, sb_disc); + } - // health - if(cl.stats[STAT_HEALTH] > 100) - Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0); - else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5) - Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0); - else - Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0); + // armor + Sbar_DrawXNum ((340-3*24), 12, cl.stats[STAT_ARMOR], 3, 24, 0.6,0.7,0.8,1,0); - // AK dont draw ammo for the laser - if(cl.stats[STAT_ACTIVEWEAPON] != 12) - { - if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS) - Sbar_DrawPic (519, 0, sb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS) - Sbar_DrawPic (519, 0, sb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS) - Sbar_DrawPic (519, 0, sb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS) - Sbar_DrawPic (519, 0, sb_ammo[3]); - - if(cl.stats[STAT_AMMO] <= 10) - Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0); + // health + if(cl.stats[STAT_HEALTH] > 100) + Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,1,1,1,1,0); + else if(cl.stats[STAT_HEALTH] <= 25 && cl.time - (int)cl.time > 0.5) + Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.7,0,0,1,0); else - Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0); + Sbar_DrawXNum((154-3*24),12,cl.stats[STAT_HEALTH],3,24,0.6,0.7,0.8,1,0); - } + // AK dont draw ammo for the laser + if(cl.stats[STAT_ACTIVEWEAPON] != 12) + { + if (cl.stats[STAT_ITEMS] & NEX_IT_SHELLS) + Sbar_DrawPic (519, 0, sb_ammo[0]); + else if (cl.stats[STAT_ITEMS] & NEX_IT_BULLETS) + Sbar_DrawPic (519, 0, sb_ammo[1]); + else if (cl.stats[STAT_ITEMS] & NEX_IT_ROCKETS) + Sbar_DrawPic (519, 0, sb_ammo[2]); + else if (cl.stats[STAT_ITEMS] & NEX_IT_CELLS) + Sbar_DrawPic (519, 0, sb_ammo[3]); + + if(cl.stats[STAT_AMMO] <= 10) + Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.7, 0,0,1,0); + else + Sbar_DrawXNum ((519-3*24), 12, cl.stats[STAT_AMMO], 3, 24, 0.6, 0.7,0.8,1,0); - if (sb_lines > 24) - DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay->name,0,0,1,1,1,1,DRAWFLAG_MODULATE); - } + } - //if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH) - // Sbar_MiniDeathmatchOverlay (0, 17); - } - else if (gamemode == GAME_ZYMOTIC) - { -#if 1 - float scale = 64.0f / 256.0f; - float kickoffset[3]; - VectorClear(kickoffset); - if (v_dmg_time > 0) - { - kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale; - kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale; - } - sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]; - sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1]; - // left1 16, 48 : 126 -66 - // left2 16, 128 : 196 -66 - // right 176, 48 : 196 -136 - Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 48 * scale, zymsb_crosshair_left1->name, 64*scale, 80*scale, 78*scale, -66*scale, cl.stats[STAT_AMMO] * (1.0 / 200.0), cl.stats[STAT_SHELLS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL); - Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 128 * scale, zymsb_crosshair_left2->name, 64*scale, 80*scale, 68*scale, -66*scale, cl.stats[STAT_NAILS] * (1.0 / 200.0), cl.stats[STAT_ROCKETS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL); - Sbar_DrawGauge(sbar_x + 176 * scale, sbar_y + 48 * scale, zymsb_crosshair_right->name, 64*scale, 160*scale, 148*scale, -136*scale, cl.stats[STAT_ARMOR] * (1.0 / 300.0), cl.stats[STAT_HEALTH] * (1.0 / 300.0), 0.0f,0.5f,1.0f,1.0f, 1.0f,0.0f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL); - DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL); -#else - float scale = 128.0f / 256.0f; - float healthstart, healthheight, healthstarttc, healthendtc; - float shieldstart, shieldheight, shieldstarttc, shieldendtc; - float ammostart, ammoheight, ammostarttc, ammoendtc; - float clipstart, clipheight, clipstarttc, clipendtc; - float kickoffset[3], offset; - VectorClear(kickoffset); - if (v_dmg_time > 0) - { - kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale; - kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale; - } - sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]; - sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1]; - offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?) - DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + ( 88 - offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 0,0, 1,1,1,1, 1,0, 1,1,1,1, 0,1, 1,1,1,1, 1,1, 1,1,1,1, 0); - DrawQ_SuperPic(sbar_x + (132 + offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 0,1, 1,1,1,1, 0,0, 1,1,1,1, 1,1, 1,1,1,1, 1,0, 1,1,1,1, 0); - DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + (132 + offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 1,1, 1,1,1,1, 0,1, 1,1,1,1, 1,0, 1,1,1,1, 0,0, 1,1,1,1, 0); - DrawQ_SuperPic(sbar_x + ( 88 - offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 1,0, 1,1,1,1, 1,1, 1,1,1,1, 0,0, 1,1,1,1, 0,1, 1,1,1,1, 0); - healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f); - shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f); - healthstart = 204 - healthheight; - shieldstart = healthstart - shieldheight; - healthstarttc = healthstart * (1.0f / 256.0f); - healthendtc = (healthstart + healthheight) * (1.0f / 256.0f); - shieldstarttc = shieldstart * (1.0f / 256.0f); - shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f); - ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f); - ammostart = 114 - ammoheight; - ammostarttc = ammostart * (1.0f / 256.0f); - ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f); - clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f); - clipstart = 190 - clipheight; - clipstarttc = clipstart * (1.0f / 256.0f); - clipendtc = (clipstart + clipheight) * (1.0f / 256.0f); - if (healthheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + healthstart * scale, zymsb_crosshair_health->name, 256 * scale, healthheight * scale, 0,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 1,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 0,healthendtc, 1.0f,0.0f,0.0f,1.0f, 1,healthendtc, 1.0f,0.0f,0.0f,1.0f, DRAWFLAG_NORMAL); - if (shieldheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + shieldstart * scale, zymsb_crosshair_health->name, 256 * scale, shieldheight * scale, 0,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 1,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 0,shieldendtc, 0.0f,0.5f,1.0f,1.0f, 1,shieldendtc, 0.0f,0.5f,1.0f,1.0f, DRAWFLAG_NORMAL); - if (ammoheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + ammostart * scale, zymsb_crosshair_ammo->name, 256 * scale, ammoheight * scale, 0,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 1,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 0,ammoendtc, 0.8f,0.8f,0.0f,1.0f, 1,ammoendtc, 0.8f,0.8f,0.0f,1.0f, DRAWFLAG_NORMAL); - if (clipheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + clipstart * scale, zymsb_crosshair_clip->name, 256 * scale, clipheight * scale, 0,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 1,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 0,clipendtc, 1.0f,1.0f,0.0f,1.0f, 1,clipendtc, 1.0f,1.0f,0.0f,1.0f, DRAWFLAG_NORMAL); - DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background->name, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL); - DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL); -#endif - } - else // Quake and others - { - sbar_y = vid_conheight.integer - SBAR_HEIGHT; - if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION) - sbar_x = 0; - else - sbar_x = (vid_conwidth.integer - 320)/2; + if (sb_lines > 24) + DrawQ_Pic(sbar_x,sbar_y,sb_sbar_overlay->name,0,0,1,1,1,1,DRAWFLAG_MODULATE); + } - if (sb_lines > 24) - { - if (gamemode != GAME_GOODVSBAD2) - Sbar_DrawInventory (); - if (!cl.islocalgame && gamemode != GAME_TRANSFUSION) - Sbar_DrawFrags (); + //if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH) + // Sbar_MiniDeathmatchOverlay (0, 17); } - - if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer)) + else if (gamemode == GAME_ZYMOTIC) { - if (gamemode != GAME_GOODVSBAD2) - Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value); - Sbar_DrawScoreboard (); + #if 1 + float scale = 64.0f / 256.0f; + float kickoffset[3]; + VectorClear(kickoffset); + if (v_dmg_time > 0) + { + kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale; + kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale; + } + sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]; + sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1]; + // left1 16, 48 : 126 -66 + // left2 16, 128 : 196 -66 + // right 176, 48 : 196 -136 + Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 48 * scale, zymsb_crosshair_left1->name, 64*scale, 80*scale, 78*scale, -66*scale, cl.stats[STAT_AMMO] * (1.0 / 200.0), cl.stats[STAT_SHELLS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL); + Sbar_DrawGauge(sbar_x + 16 * scale, sbar_y + 128 * scale, zymsb_crosshair_left2->name, 64*scale, 80*scale, 68*scale, -66*scale, cl.stats[STAT_NAILS] * (1.0 / 200.0), cl.stats[STAT_ROCKETS] * (1.0 / 200.0), 0.8f,0.8f,0.0f,1.0f, 0.8f,0.5f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL); + Sbar_DrawGauge(sbar_x + 176 * scale, sbar_y + 48 * scale, zymsb_crosshair_right->name, 64*scale, 160*scale, 148*scale, -136*scale, cl.stats[STAT_ARMOR] * (1.0 / 300.0), cl.stats[STAT_HEALTH] * (1.0 / 300.0), 0.0f,0.5f,1.0f,1.0f, 1.0f,0.0f,0.0f,1.0f, 0.3f,0.3f,0.3f,1.0f, DRAWFLAG_NORMAL); + DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL); + #else + float scale = 128.0f / 256.0f; + float healthstart, healthheight, healthstarttc, healthendtc; + float shieldstart, shieldheight, shieldstarttc, shieldendtc; + float ammostart, ammoheight, ammostarttc, ammoendtc; + float clipstart, clipheight, clipstarttc, clipendtc; + float kickoffset[3], offset; + VectorClear(kickoffset); + if (v_dmg_time > 0) + { + kickoffset[0] = (v_dmg_time/v_kicktime.value*v_dmg_roll) * 10 * scale; + kickoffset[1] = (v_dmg_time/v_kicktime.value*v_dmg_pitch) * 10 * scale; + } + sbar_x = (vid_conwidth.integer - 256 * scale)/2 + kickoffset[0]; + sbar_y = (vid_conheight.integer - 256 * scale)/2 + kickoffset[1]; + offset = 0; // TODO: offset should be controlled by recoil (question: how to detect firing?) + DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + ( 88 - offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 0,0, 1,1,1,1, 1,0, 1,1,1,1, 0,1, 1,1,1,1, 1,1, 1,1,1,1, 0); + DrawQ_SuperPic(sbar_x + (132 + offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 0,1, 1,1,1,1, 0,0, 1,1,1,1, 1,1, 1,1,1,1, 1,0, 1,1,1,1, 0); + DrawQ_SuperPic(sbar_x + 120 * scale, sbar_y + (132 + offset) * scale, zymsb_crosshair_line->name, 16 * scale, 36 * scale, 1,1, 1,1,1,1, 0,1, 1,1,1,1, 1,0, 1,1,1,1, 0,0, 1,1,1,1, 0); + DrawQ_SuperPic(sbar_x + ( 88 - offset) * scale, sbar_y + 120 * scale, zymsb_crosshair_line->name, 36 * scale, 16 * scale, 1,0, 1,1,1,1, 1,1, 1,1,1,1, 0,0, 1,1,1,1, 0,1, 1,1,1,1, 0); + healthheight = cl.stats[STAT_HEALTH] * (152.0f / 300.0f); + shieldheight = cl.stats[STAT_ARMOR] * (152.0f / 300.0f); + healthstart = 204 - healthheight; + shieldstart = healthstart - shieldheight; + healthstarttc = healthstart * (1.0f / 256.0f); + healthendtc = (healthstart + healthheight) * (1.0f / 256.0f); + shieldstarttc = shieldstart * (1.0f / 256.0f); + shieldendtc = (shieldstart + shieldheight) * (1.0f / 256.0f); + ammoheight = cl.stats[STAT_SHELLS] * (62.0f / 200.0f); + ammostart = 114 - ammoheight; + ammostarttc = ammostart * (1.0f / 256.0f); + ammoendtc = (ammostart + ammoheight) * (1.0f / 256.0f); + clipheight = cl.stats[STAT_AMMO] * (122.0f / 200.0f); + clipstart = 190 - clipheight; + clipstarttc = clipstart * (1.0f / 256.0f); + clipendtc = (clipstart + clipheight) * (1.0f / 256.0f); + if (healthheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + healthstart * scale, zymsb_crosshair_health->name, 256 * scale, healthheight * scale, 0,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 1,healthstarttc, 1.0f,0.0f,0.0f,1.0f, 0,healthendtc, 1.0f,0.0f,0.0f,1.0f, 1,healthendtc, 1.0f,0.0f,0.0f,1.0f, DRAWFLAG_NORMAL); + if (shieldheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + shieldstart * scale, zymsb_crosshair_health->name, 256 * scale, shieldheight * scale, 0,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 1,shieldstarttc, 0.0f,0.5f,1.0f,1.0f, 0,shieldendtc, 0.0f,0.5f,1.0f,1.0f, 1,shieldendtc, 0.0f,0.5f,1.0f,1.0f, DRAWFLAG_NORMAL); + if (ammoheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + ammostart * scale, zymsb_crosshair_ammo->name, 256 * scale, ammoheight * scale, 0,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 1,ammostarttc, 0.8f,0.8f,0.0f,1.0f, 0,ammoendtc, 0.8f,0.8f,0.0f,1.0f, 1,ammoendtc, 0.8f,0.8f,0.0f,1.0f, DRAWFLAG_NORMAL); + if (clipheight > 0) DrawQ_SuperPic(sbar_x + 0 * scale, sbar_y + clipstart * scale, zymsb_crosshair_clip->name, 256 * scale, clipheight * scale, 0,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 1,clipstarttc, 1.0f,1.0f,0.0f,1.0f, 0,clipendtc, 1.0f,1.0f,0.0f,1.0f, 1,clipendtc, 1.0f,1.0f,0.0f,1.0f, DRAWFLAG_NORMAL); + DrawQ_Pic(sbar_x + 0 * scale, sbar_y + 0 * scale, zymsb_crosshair_background->name, 256 * scale, 256 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL); + DrawQ_Pic(sbar_x + 120 * scale, sbar_y + 120 * scale, zymsb_crosshair_center->name, 16 * scale, 16 * scale, 1, 1, 1, 1, DRAWFLAG_NORMAL); + #endif } - else if (sb_lines) + else // Quake and others { - Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value); + sbar_y = vid_conheight.integer - SBAR_HEIGHT; + if (cl.gametype == GAME_DEATHMATCH && gamemode != GAME_TRANSFUSION) + sbar_x = 0; + else + sbar_x = (vid_conwidth.integer - 320)/2; - // keys (hipnotic only) - //MED 01/04/97 moved keys here so they would not be overwritten - if (gamemode == GAME_HIPNOTIC) + if (sb_lines > 24) { - if (cl.stats[STAT_ITEMS] & IT_KEY1) - Sbar_DrawPic (209, 3, sb_items[0]); - if (cl.stats[STAT_ITEMS] & IT_KEY2) - Sbar_DrawPic (209, 12, sb_items[1]); + if (gamemode != GAME_GOODVSBAD2) + Sbar_DrawInventory (); + if (!cl.islocalgame && gamemode != GAME_TRANSFUSION) + Sbar_DrawFrags (); } - // armor - if (gamemode != GAME_GOODVSBAD2) + + if (sb_showscores || (cl.stats[STAT_HEALTH] <= 0 && cl_deathscoreboard.integer)) { - if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) + if (gamemode != GAME_GOODVSBAD2) + Sbar_DrawAlphaPic (0, 0, sb_scorebar, sbar_alpha_bg.value); + Sbar_DrawScoreboard (); + } + else if (sb_lines) + { + Sbar_DrawAlphaPic (0, 0, sb_sbar, sbar_alpha_bg.value); + + // keys (hipnotic only) + //MED 01/04/97 moved keys here so they would not be overwritten + if (gamemode == GAME_HIPNOTIC) { - Sbar_DrawNum (24, 0, 666, 3, 1); - Sbar_DrawPic (0, 0, sb_disc); + if (cl.stats[STAT_ITEMS] & IT_KEY1) + Sbar_DrawPic (209, 3, sb_items[0]); + if (cl.stats[STAT_ITEMS] & IT_KEY2) + Sbar_DrawPic (209, 12, sb_items[1]); } - else + // armor + if (gamemode != GAME_GOODVSBAD2) { - if (gamemode == GAME_ROGUE) + if (cl.stats[STAT_ITEMS] & IT_INVULNERABILITY) { - Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); - if (cl.stats[STAT_ITEMS] & RIT_ARMOR3) - Sbar_DrawPic (0, 0, sb_armor[2]); - else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2) - Sbar_DrawPic (0, 0, sb_armor[1]); - else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1) - Sbar_DrawPic (0, 0, sb_armor[0]); + Sbar_DrawNum (24, 0, 666, 3, 1); + Sbar_DrawPic (0, 0, sb_disc); } else { - Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); - if (cl.stats[STAT_ITEMS] & IT_ARMOR3) - Sbar_DrawPic (0, 0, sb_armor[2]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) - Sbar_DrawPic (0, 0, sb_armor[1]); - else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) - Sbar_DrawPic (0, 0, sb_armor[0]); + if (gamemode == GAME_ROGUE) + { + Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); + if (cl.stats[STAT_ITEMS] & RIT_ARMOR3) + Sbar_DrawPic (0, 0, sb_armor[2]); + else if (cl.stats[STAT_ITEMS] & RIT_ARMOR2) + Sbar_DrawPic (0, 0, sb_armor[1]); + else if (cl.stats[STAT_ITEMS] & RIT_ARMOR1) + Sbar_DrawPic (0, 0, sb_armor[0]); + } + else + { + Sbar_DrawNum (24, 0, cl.stats[STAT_ARMOR], 3, cl.stats[STAT_ARMOR] <= 25); + if (cl.stats[STAT_ITEMS] & IT_ARMOR3) + Sbar_DrawPic (0, 0, sb_armor[2]); + else if (cl.stats[STAT_ITEMS] & IT_ARMOR2) + Sbar_DrawPic (0, 0, sb_armor[1]); + else if (cl.stats[STAT_ITEMS] & IT_ARMOR1) + Sbar_DrawPic (0, 0, sb_armor[0]); + } } } - } - // face - Sbar_DrawFace (); + // face + Sbar_DrawFace (); - // health - Sbar_DrawNum (154, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25); + // health + Sbar_DrawNum (154, 0, cl.stats[STAT_HEALTH], 3, cl.stats[STAT_HEALTH] <= 25); - // ammo icon - if (gamemode == GAME_ROGUE) - { - if (cl.stats[STAT_ITEMS] & RIT_SHELLS) - Sbar_DrawPic (224, 0, sb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & RIT_NAILS) - Sbar_DrawPic (224, 0, sb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS) - Sbar_DrawPic (224, 0, sb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & RIT_CELLS) - Sbar_DrawPic (224, 0, sb_ammo[3]); - else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS) - Sbar_DrawPic (224, 0, rsb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO) - Sbar_DrawPic (224, 0, rsb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS) - Sbar_DrawPic (224, 0, rsb_ammo[2]); - } - else - { - if (cl.stats[STAT_ITEMS] & IT_SHELLS) - Sbar_DrawPic (224, 0, sb_ammo[0]); - else if (cl.stats[STAT_ITEMS] & IT_NAILS) - Sbar_DrawPic (224, 0, sb_ammo[1]); - else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) - Sbar_DrawPic (224, 0, sb_ammo[2]); - else if (cl.stats[STAT_ITEMS] & IT_CELLS) - Sbar_DrawPic (224, 0, sb_ammo[3]); - } + // ammo icon + if (gamemode == GAME_ROGUE) + { + if (cl.stats[STAT_ITEMS] & RIT_SHELLS) + Sbar_DrawPic (224, 0, sb_ammo[0]); + else if (cl.stats[STAT_ITEMS] & RIT_NAILS) + Sbar_DrawPic (224, 0, sb_ammo[1]); + else if (cl.stats[STAT_ITEMS] & RIT_ROCKETS) + Sbar_DrawPic (224, 0, sb_ammo[2]); + else if (cl.stats[STAT_ITEMS] & RIT_CELLS) + Sbar_DrawPic (224, 0, sb_ammo[3]); + else if (cl.stats[STAT_ITEMS] & RIT_LAVA_NAILS) + Sbar_DrawPic (224, 0, rsb_ammo[0]); + else if (cl.stats[STAT_ITEMS] & RIT_PLASMA_AMMO) + Sbar_DrawPic (224, 0, rsb_ammo[1]); + else if (cl.stats[STAT_ITEMS] & RIT_MULTI_ROCKETS) + Sbar_DrawPic (224, 0, rsb_ammo[2]); + } + else + { + if (cl.stats[STAT_ITEMS] & IT_SHELLS) + Sbar_DrawPic (224, 0, sb_ammo[0]); + else if (cl.stats[STAT_ITEMS] & IT_NAILS) + Sbar_DrawPic (224, 0, sb_ammo[1]); + else if (cl.stats[STAT_ITEMS] & IT_ROCKETS) + Sbar_DrawPic (224, 0, sb_ammo[2]); + else if (cl.stats[STAT_ITEMS] & IT_CELLS) + Sbar_DrawPic (224, 0, sb_ammo[3]); + } - Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); + Sbar_DrawNum (248, 0, cl.stats[STAT_AMMO], 3, cl.stats[STAT_AMMO] <= 10); - } + } - if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH) - { - if (gamemode == GAME_TRANSFUSION) - Sbar_MiniDeathmatchOverlay (0, 0); - else - Sbar_MiniDeathmatchOverlay (324, vid_conheight.integer - sb_lines); + if (vid_conwidth.integer > 320 && cl.gametype == GAME_DEATHMATCH) + { + if (gamemode == GAME_TRANSFUSION) + Sbar_MiniDeathmatchOverlay (0, 0); + else + Sbar_MiniDeathmatchOverlay (324, vid_conheight.integer - sb_lines); + } } } Sbar_ShowFPS(); - R_Draw2DCrosshair(); + if(cl.csqc_vidvars.drawcrosshair) + R_Draw2DCrosshair(); if (cl_prydoncursor.integer) DrawQ_Pic((cl.cmd.cursor_screen[0] + 1) * 0.5 * vid_conwidth.integer, (cl.cmd.cursor_screen[1] + 1) * 0.5 * vid_conheight.integer, va("gfx/prydoncursor%03i", cl_prydoncursor.integer), 0, 0, 1, 1, 1, 1, 0); diff --git a/sv_main.c b/sv_main.c index 7296d9d9..c9d8e301 100644 --- a/sv_main.c +++ b/sv_main.c @@ -24,6 +24,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. void SV_VM_Init(); void SV_VM_Setup(); +void VM_AutoSentStats_Clear (void); +void EntityFrameCSQC_ClearVersions (void); +void EntityFrameCSQC_InitClientVersions (int client, qboolean clear); +void VM_SV_WriteAutoSentStats (client_t *client, prvm_edict_t *ent, sizebuf_t *msg, int *stats); +void EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int numstates, const entity_state_t *states); + + // select which protocol to host, this is fed to Protocol_EnumForName cvar_t sv_protocolname = {0, "sv_protocolname", "DP7"}; cvar_t sv_ratelimitlocalplayer = {0, "sv_ratelimitlocalplayer", "0"}; @@ -275,6 +282,7 @@ CLIENT SPAWNING ============================================================================== */ +static const char *SV_InitCmd; //[515]: svprogs able to send cmd to client on connect /* ================ SV_SendServerinfo @@ -320,6 +328,18 @@ void SV_SendServerinfo (client_t *client) dpsnprintf (message, sizeof (message), "\002\nServer: %s build %s (progs %i crc)", gamename, buildstring, prog->filecrc); MSG_WriteString (&client->message,message); + // LordHavoc: this does not work on dedicated servers, needs fixing. +extern qboolean csqc_loaded; +//[515]: init csprogs according to version of svprogs, check the crc, etc. + if(csqc_loaded && (cls.state == ca_dedicated || PRVM_NUM_FOR_EDICT(client->edict) != 1)) + { + MSG_WriteByte (&client->message, svc_stufftext); + if(SV_InitCmd) + MSG_WriteString (&client->message, va("csqc_progcrc %i;%s\n", csqc_progcrc.integer, SV_InitCmd)); + else + MSG_WriteString (&client->message, va("csqc_progcrc %i\n", csqc_progcrc.integer)); + } + MSG_WriteByte (&client->message, svc_serverinfo); MSG_WriteLong (&client->message, Protocol_NumberForEnum(sv.protocol)); MSG_WriteByte (&client->message, svs.maxclients); @@ -371,6 +391,9 @@ void SV_ConnectClient (int clientnum, netconn_t *netconnection) client = svs.clients + clientnum; + if(netconnection)//[515]: bots don't play with csqc =) + EntityFrameCSQC_InitClientVersions(clientnum, false); + // set up the client_t if (sv.loadgame) memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms)); @@ -855,6 +878,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * { int i, numsendstates; entity_state_t *s; + extern int csqc_clent; // if there isn't enough space to accomplish anything, skip it if (msg->cursize + 25 > msg->maxsize) @@ -874,7 +898,7 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * if (sv.worldmodel && sv.worldmodel->brush.FatPVS) sv_writeentitiestoclient_pvsbytes = sv.worldmodel->brush.FatPVS(sv.worldmodel, sv_writeentitiestoclient_testeye, 8, sv_writeentitiestoclient_pvs, sizeof(sv_writeentitiestoclient_pvs)); - sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes + csqc_clent = sv_writeentitiestoclient_clentnum = PRVM_EDICT_TO_PROG(clent); // LordHavoc: for comparison purposes sententitiesmark++; @@ -896,6 +920,8 @@ void SV_WriteEntitiesToClient(client_t *client, prvm_edict_t *clent, sizebuf_t * if (sv_cullentities_stats.integer) Con_Printf("client \"%s\" entities: %d total, %d visible, %d culled by: %d pvs %d trace\n", client->name, sv_writeentitiestoclient_totalentities, sv_writeentitiestoclient_visibleentities, sv_writeentitiestoclient_culled_pvs + sv_writeentitiestoclient_culled_trace, sv_writeentitiestoclient_culled_pvs, sv_writeentitiestoclient_culled_trace); + EntityFrameCSQC_WriteFrame(msg, numsendstates, sendstates); + if (client->entitydatabase5) EntityFrame5_WriteFrame(msg, client->entitydatabase5, numsendstates, sendstates, client - svs.clients + 1, stats, client->movesequence); else if (client->entitydatabase4) @@ -1196,6 +1222,7 @@ qboolean SV_SendClientDatagram (client_t *client) // add the client specific data to the datagram SV_WriteClientdataToMessage (client, client->edict, &msg, stats); + VM_SV_WriteAutoSentStats (client, client->edict, &msg, stats); SV_WriteEntitiesToClient (client, client->edict, &msg, stats); // expand packet size to allow effects to go over the rate limit @@ -1797,6 +1824,10 @@ void SV_SpawnServer (const char *server) ent->fields.server = (void *)((unsigned char *)prog->edictsfields + i * prog->edict_size); }*/ + // reset client csqc entity versions right away. + for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++) + EntityFrameCSQC_InitClientVersions(i, true); + sv.datagram.maxsize = sizeof(sv.datagram_buf); sv.datagram.cursize = 0; sv.datagram.data = sv.datagram_buf; @@ -2184,6 +2215,8 @@ int eval_cursor_trace_ent; int eval_colormod; int eval_playermodel; int eval_playerskin; +int eval_SendEntity; +int eval_Version; int eval_customizeentityforclient; mfunction_t *SV_PlayerPhysicsQC; @@ -2191,6 +2224,8 @@ mfunction_t *EndFrameQC; //KrimZon - SERVER COMMANDS IN QUAKEC mfunction_t *SV_ParseClientCommandQC; +ddef_t *PRVM_ED_FindGlobal(const char *name); + void SV_VM_FindEdictFieldOffsets(void) { eval_gravity = PRVM_ED_FindFieldOffset("gravity"); @@ -2252,6 +2287,8 @@ void SV_VM_FindEdictFieldOffsets(void) eval_colormod = PRVM_ED_FindFieldOffset("colormod"); eval_playermodel = PRVM_ED_FindFieldOffset("playermodel"); eval_playerskin = PRVM_ED_FindFieldOffset("playerskin"); + eval_SendEntity = PRVM_ED_FindFieldOffset("SendEntity"); + eval_Version = PRVM_ED_FindFieldOffset("Version"); eval_customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient"); // LordHavoc: allowing QuakeC to override the player movement code @@ -2260,6 +2297,12 @@ void SV_VM_FindEdictFieldOffsets(void) EndFrameQC = PRVM_ED_FindFunction ("EndFrame"); //KrimZon - SERVER COMMANDS IN QUAKEC SV_ParseClientCommandQC = PRVM_ED_FindFunction ("SV_ParseClientCommand"); + + //[515]: init stufftext string (it is sent before svc_serverinfo) + if(PRVM_ED_FindGlobal("SV_InitCmd") && PRVM_ED_FindGlobal("SV_InitCmd")->type & ev_string) + SV_InitCmd = PRVM_G_STRING(PRVM_ED_FindGlobal("SV_InitCmd")->ofs); + else + SV_InitCmd = NULL; } #define REQFIELDS (sizeof(reqfields) / sizeof(prvm_required_field_t)) @@ -2315,6 +2358,7 @@ prvm_required_field_t reqfields[] = {ev_float, "scale"}, {ev_float, "style"}, {ev_float, "tag_index"}, + {ev_float, "Version"}, {ev_float, "viewzoom"}, {ev_vector, "color"}, {ev_vector, "colormod"}, @@ -2325,6 +2369,7 @@ prvm_required_field_t reqfields[] = {ev_vector, "punchvector"}, {ev_string, "playermodel"}, {ev_string, "playerskin"}, + {ev_function, "SendEntity"}, {ev_function, "customizeentityforclient"}, }; @@ -2360,6 +2405,9 @@ void SV_VM_Setup(void) PRVM_LoadProgs( sv_progs.string, 0, NULL, REQFIELDS, reqfields ); SV_VM_FindEdictFieldOffsets(); + VM_AutoSentStats_Clear();//[515]: csqc + EntityFrameCSQC_ClearVersions();//[515]: csqc + PRVM_End; } diff --git a/svvm_cmds.c b/svvm_cmds.c index 9d1f803d..94db32b8 100644 --- a/svvm_cmds.c +++ b/svvm_cmds.c @@ -65,6 +65,7 @@ char *vm_sv_extensions = "DP_QC_MULTIPLETEMPSTRINGS " "DP_QC_RANDOMVEC " "DP_QC_SINCOSSQRTPOW " +"DP_QC_STRINGBUFFERS " "DP_QC_TRACEBOX " "DP_QC_TRACETOSS " "DP_QC_TRACE_MOVETYPE_HITMODEL " @@ -111,6 +112,8 @@ char *vm_sv_extensions = "DP_TE_SPARK " "DP_TE_STANDARDEFFECTBUILTINS " "DP_VIEWZOOM " +"EXT_BITSHIFT " +"EXT_CSQC " "FRIK_FILE " "KRIMZON_SV_PARSECLIENTCOMMAND " "NEH_CMD_PLAY2 " @@ -135,20 +138,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 @@ -1119,12 +1108,14 @@ 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); switch (dest) @@ -1150,6 +1141,9 @@ sizebuf_t *WriteDest (void) case MSG_INIT: return &sv.signon; + + case MSG_ENTITY: + return sv2csqcbuf; } return NULL; @@ -1318,6 +1312,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) + { + 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)) + { + Con_Printf("PF_SV_AddStat: index >= MAX_CL_STATS\n"); + return; + } + if(i > (MAX_CL_STATS-32-4) && type == 1) + { + Con_Printf("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 @@ -1815,7 +1963,7 @@ void PF_te_flamejet (void) MSG_WriteByte(&sv.datagram, PRVM_G_FLOAT(OFS_PARM2)); } -static void clippointtosurface(msurface_t *surface, vec3_t p, vec3_t out) +void clippointtosurface(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; @@ -2302,9 +2450,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 @@ -2424,7 +2577,35 @@ 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 +// 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, e10, e10, e10, e10, e10, e10, e10, e10, e10, // #300-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) @@ -2458,7 +2639,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) @@ -2485,8 +2666,28 @@ PF_clienttype, // #455 float(entity clent) clienttype (DP_SV_BOTCLIENT) 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) +NULL, // #471 +NULL, // #472 +NULL, // #473 +NULL, // #474 +NULL, // #475 +NULL, // #476 +NULL, // #477 +NULL, // #478 +NULL, // #479 +e10, e10 // #471-499 (LordHavoc) }; const int vm_sv_numbuiltins = sizeof(vm_sv_builtins) / sizeof(prvm_builtin_t); diff --git a/vid_wgl.c b/vid_wgl.c index 8cce143a..6f189c46 100644 --- a/vid_wgl.c +++ b/vid_wgl.c @@ -1306,6 +1306,7 @@ static void IN_MouseMove (void) in_mouse_y = my; // if the mouse has moved, force it to the center, so there's room to move + if (!cl.csqc_wantsmousemove) if (mx || my) SetCursorPos ((window_x + vid.width / 2), (window_y + vid.height / 2)); } diff --git a/view.c b/view.c index 3c4d5314..6f202b10 100644 --- a/view.c +++ b/view.c @@ -308,6 +308,7 @@ static void V_BonusFlash_f (void) extern matrix4x4_t viewmodelmatrix; #include "cl_collision.h" +#include "csprogs.h" /* ================== @@ -321,6 +322,8 @@ void V_CalcRefdef (void) entity_t *ent; float vieworg[3], gunorg[3], viewangles[3]; trace_t trace; + if(csqc_loaded) + return; VectorClear(gunorg); Matrix4x4_CreateIdentity(&viewmodelmatrix); Matrix4x4_CreateIdentity(&r_refdef.viewentitymatrix); @@ -490,6 +493,8 @@ void V_CalcRefdef (void) Matrix4x4_CreateFromQuakeEntity(&r_refdef.viewentitymatrix, vieworg[0], vieworg[1], vieworg[2], viewangles[0], viewangles[1], viewangles[2] + v_idlescale.value * sin(cl.time*v_iroll_cycle.value) * v_iroll_level.value, 1); // calculate a viewmodel matrix for use in view-attached entities Matrix4x4_CreateFromQuakeEntity(&viewmodelmatrix, gunorg[0], gunorg[1], gunorg[2], viewangles[0], viewangles[1], viewangles[2], 0.3); + VectorCopy(vieworg, csqc_origin); + VectorCopy(viewangles, csqc_angles); } } } diff --git a/world_cs.c b/world_cs.c new file mode 100644 index 00000000..11e212f8 --- /dev/null +++ b/world_cs.c @@ -0,0 +1,732 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// world.c -- world query functions + +#include "quakedef.h" + +/* + +entities never clip against themselves, or their owner + +line of sight checks trace->inopen and trace->inwater, but bullets don't + +*/ + +extern cvar_t sv_debugmove; +extern cvar_t sv_areagrid_mingridsize; + +trace_t CSSV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict); + +void CSSV_AreaStats_f(void); + +void CSSV_World_Init(void) +{ + Cvar_RegisterVariable(&sv_debugmove); + Cvar_RegisterVariable(&sv_areagrid_mingridsize); + Cmd_AddCommand("cssv_areastats", CSSV_AreaStats_f); + Collision_Init(); +} + +//============================================================================ + +// CSClearLink is used for new headnodes +static void CSClearLink (link_t *l) +{ + l->entitynumber = 0; + l->prev = l->next = l; +} + +static void CSRemoveLink (link_t *l) +{ + l->next->prev = l->prev; + l->prev->next = l->next; +} + +static void CSInsertLinkBefore (link_t *l, link_t *before, int entitynumber) +{ + l->entitynumber = entitynumber; + l->next = before; + l->prev = before->prev; + l->prev->next = l; + l->next->prev = l; +} + + +/* +=============================================================================== + +ENTITY AREA CHECKING + +=============================================================================== +*/ + +int cssv_areagrid_stats_calls = 0; +int cssv_areagrid_stats_nodechecks = 0; +int cssv_areagrid_stats_entitychecks = 0; + +void CSSV_AreaStats_f(void) +{ + Con_Printf("csareagrid check stats: %d calls %d nodes (%f per call) %d entities (%f per call)\n", cssv_areagrid_stats_calls, cssv_areagrid_stats_nodechecks, (double) cssv_areagrid_stats_nodechecks / (double) cssv_areagrid_stats_calls, cssv_areagrid_stats_entitychecks, (double) cssv_areagrid_stats_entitychecks / (double) cssv_areagrid_stats_calls); + cssv_areagrid_stats_calls = 0; + cssv_areagrid_stats_nodechecks = 0; + cssv_areagrid_stats_entitychecks = 0; +} + +typedef struct areagrid_s +{ + link_t edicts; +} +csareagrid_t; + +#define CSAREA_GRID 512 +#define CSAREA_GRIDNODES (CSAREA_GRID * CSAREA_GRID) + +static csareagrid_t cssv_areagrid[CSAREA_GRIDNODES], cssv_areagrid_outside; +static vec3_t cssv_areagrid_bias, cssv_areagrid_scale, cssv_areagrid_mins, cssv_areagrid_maxs, cssv_areagrid_size; +static int cssv_areagrid_marknumber = 1; + +void CSSV_CreateAreaGrid (vec3_t mins, vec3_t maxs) +{ + int i; + CSClearLink (&cssv_areagrid_outside.edicts); + // choose either the world box size, or a larger box to ensure the grid isn't too fine + cssv_areagrid_size[0] = max(maxs[0] - mins[0], CSAREA_GRID * sv_areagrid_mingridsize.value); + cssv_areagrid_size[1] = max(maxs[1] - mins[1], CSAREA_GRID * sv_areagrid_mingridsize.value); + cssv_areagrid_size[2] = max(maxs[2] - mins[2], CSAREA_GRID * sv_areagrid_mingridsize.value); + // figure out the corners of such a box, centered at the center of the world box + cssv_areagrid_mins[0] = (mins[0] + maxs[0] - cssv_areagrid_size[0]) * 0.5f; + cssv_areagrid_mins[1] = (mins[1] + maxs[1] - cssv_areagrid_size[1]) * 0.5f; + cssv_areagrid_mins[2] = (mins[2] + maxs[2] - cssv_areagrid_size[2]) * 0.5f; + cssv_areagrid_maxs[0] = (mins[0] + maxs[0] + cssv_areagrid_size[0]) * 0.5f; + cssv_areagrid_maxs[1] = (mins[1] + maxs[1] + cssv_areagrid_size[1]) * 0.5f; + cssv_areagrid_maxs[2] = (mins[2] + maxs[2] + cssv_areagrid_size[2]) * 0.5f; + // now calculate the actual useful info from that + VectorNegate(cssv_areagrid_mins, cssv_areagrid_bias); + cssv_areagrid_scale[0] = CSAREA_GRID / cssv_areagrid_size[0]; + cssv_areagrid_scale[1] = CSAREA_GRID / cssv_areagrid_size[1]; + cssv_areagrid_scale[2] = CSAREA_GRID / cssv_areagrid_size[2]; + for (i = 0;i < CSAREA_GRIDNODES;i++) + { + CSClearLink (&cssv_areagrid[i].edicts); + } + Con_DPrintf("cssv_areagrid settings: divisions %ix%ix1 : box %f %f %f : %f %f %f size %f %f %f grid %f %f %f (mingrid %f)\n", CSAREA_GRID, CSAREA_GRID, cssv_areagrid_mins[0], cssv_areagrid_mins[1], cssv_areagrid_mins[2], cssv_areagrid_maxs[0], cssv_areagrid_maxs[1], cssv_areagrid_maxs[2], cssv_areagrid_size[0], cssv_areagrid_size[1], cssv_areagrid_size[2], 1.0f / cssv_areagrid_scale[0], 1.0f / cssv_areagrid_scale[1], 1.0f / cssv_areagrid_scale[2], sv_areagrid_mingridsize.value); +} + +/* +=============== +CSSV_ClearWorld + +=============== +*/ +void CSSV_ClearWorld (void) +{ + CSSV_CreateAreaGrid(cl.worldmodel->normalmins, cl.worldmodel->normalmaxs); +} + + +/* +=============== +CSSV_UnlinkEdict + +=============== +*/ +void CSSV_UnlinkEdict (prvm_edict_t *ent) +{ + int i; + for (i = 0;i < ENTITYGRIDAREAS;i++) + { + if (ent->priv.server->areagrid[i].prev) + { + CSRemoveLink (&ent->priv.server->areagrid[i]); + ent->priv.server->areagrid[i].prev = ent->priv.server->areagrid[i].next = NULL; + } + } +} + +int CSSV_EntitiesInBox(vec3_t mins, vec3_t maxs, int maxlist, prvm_edict_t **list) +{ + int numlist; + csareagrid_t *grid; + link_t *l; + prvm_edict_t *ent; + int igrid[3], igridmins[3], igridmaxs[3]; + + cssv_areagrid_stats_calls++; + cssv_areagrid_marknumber++; + igridmins[0] = (int) ((mins[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]); + igridmins[1] = (int) ((mins[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]); + //igridmins[2] = (int) ((mins[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]); + igridmaxs[0] = (int) ((maxs[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]) + 1; + igridmaxs[1] = (int) ((maxs[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]) + 1; + //igridmaxs[2] = (int) ((maxs[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]) + 1; + igridmins[0] = max(0, igridmins[0]); + igridmins[1] = max(0, igridmins[1]); + //igridmins[2] = max(0, igridmins[2]); + igridmaxs[0] = min(CSAREA_GRID, igridmaxs[0]); + igridmaxs[1] = min(CSAREA_GRID, igridmaxs[1]); + //igridmaxs[2] = min(CSAREA_GRID, igridmaxs[2]); + + numlist = 0; + // add entities not linked into areagrid because they are too big or + // outside the grid bounds + if (cssv_areagrid_outside.edicts.next != &cssv_areagrid_outside.edicts) + { + for (l = cssv_areagrid_outside.edicts.next;l != &cssv_areagrid_outside.edicts;l = l->next) + { + ent = PRVM_EDICT_NUM_UNSIGNED(l->entitynumber); + if (ent->priv.server->areagridmarknumber != cssv_areagrid_marknumber) + { + ent->priv.server->areagridmarknumber = cssv_areagrid_marknumber; + if (!ent->priv.server->free && BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax)) + { + if (numlist < maxlist) + list[numlist] = ent; + numlist++; + } + cssv_areagrid_stats_entitychecks++; + } + } + } + // add grid linked entities + for (igrid[1] = igridmins[1];igrid[1] < igridmaxs[1];igrid[1]++) + { + grid = cssv_areagrid + igrid[1] * CSAREA_GRID + igridmins[0]; + for (igrid[0] = igridmins[0];igrid[0] < igridmaxs[0];igrid[0]++, grid++) + { + if (grid->edicts.next != &grid->edicts) + { + for (l = grid->edicts.next;l != &grid->edicts;l = l->next) + { + ent = PRVM_EDICT_NUM_UNSIGNED(l->entitynumber); + if (ent->priv.server->areagridmarknumber != cssv_areagrid_marknumber) + { + ent->priv.server->areagridmarknumber = cssv_areagrid_marknumber; + if (!ent->priv.server->free && BoxesOverlap(mins, maxs, ent->fields.client->absmin, ent->fields.client->absmax)) + { + if (numlist < maxlist) + list[numlist] = ent; + numlist++; + } + } + cssv_areagrid_stats_entitychecks++; + } + } + } + } + return numlist; +} + +void CSSV_TouchAreaGrid(prvm_edict_t *ent) +{ + int i, numtouchedicts, old_self, old_other; + prvm_edict_t *touch, *touchedicts[MAX_EDICTS]; + + // build a list of edicts to touch, because the link loop can be corrupted + // by CSSV_IncreaseEdicts called during touch functions + numtouchedicts = CSSV_EntitiesInBox(ent->fields.client->absmin, ent->fields.client->absmax, MAX_EDICTS, touchedicts); + if (numtouchedicts > MAX_EDICTS) + { + // this never happens + Con_Printf("CSSV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS); + numtouchedicts = MAX_EDICTS; + } + + old_self = prog->globals.client->self; + old_other = prog->globals.client->other; + for (i = 0;i < numtouchedicts;i++) + { + touch = touchedicts[i]; + if (touch != ent && (int)touch->fields.client->solid == SOLID_TRIGGER && touch->fields.client->touch) + { + prog->globals.client->self = PRVM_EDICT_TO_PROG(touch); + prog->globals.client->other = PRVM_EDICT_TO_PROG(ent); + prog->globals.client->time = cl.time; + PRVM_ExecuteProgram (touch->fields.client->touch, "QC function self.touch is missing"); + } + } + prog->globals.client->self = old_self; + prog->globals.client->other = old_other; +} + +void CSSV_LinkEdict_AreaGrid(prvm_edict_t *ent) +{ + csareagrid_t *grid; + int igrid[3], igridmins[3], igridmaxs[3], gridnum, entitynumber = PRVM_NUM_FOR_EDICT(ent); + + if (entitynumber <= 0 || entitynumber >= prog->max_edicts || PRVM_EDICT_NUM(entitynumber) != ent) + { + Con_Printf ("CSSV_LinkEdict_AreaGrid: invalid edict %p (edicts is %p, edict compared to prog->edicts is %i)\n", ent, prog->edicts, entitynumber); + return; + } + + igridmins[0] = (int) ((ent->fields.client->absmin[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]); + igridmins[1] = (int) ((ent->fields.client->absmin[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]); + //igridmins[2] = (int) ((ent->fields.client->absmin[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]); + igridmaxs[0] = (int) ((ent->fields.client->absmax[0] + cssv_areagrid_bias[0]) * cssv_areagrid_scale[0]) + 1; + igridmaxs[1] = (int) ((ent->fields.client->absmax[1] + cssv_areagrid_bias[1]) * cssv_areagrid_scale[1]) + 1; + //igridmaxs[2] = (int) ((ent->fields.client->absmax[2] + cssv_areagrid_bias[2]) * cssv_areagrid_scale[2]) + 1; + if (igridmins[0] < 0 || igridmaxs[0] > CSAREA_GRID || igridmins[1] < 0 || igridmaxs[1] > CSAREA_GRID || ((igridmaxs[0] - igridmins[0]) * (igridmaxs[1] - igridmins[1])) > ENTITYGRIDAREAS) + { + // wow, something outside the grid, store it as such + CSInsertLinkBefore (&ent->priv.server->areagrid[0], &cssv_areagrid_outside.edicts, entitynumber); + return; + } + + gridnum = 0; + for (igrid[1] = igridmins[1];igrid[1] < igridmaxs[1];igrid[1]++) + { + grid = cssv_areagrid + igrid[1] * CSAREA_GRID + igridmins[0]; + for (igrid[0] = igridmins[0];igrid[0] < igridmaxs[0];igrid[0]++, grid++, gridnum++) + CSInsertLinkBefore (&ent->priv.server->areagrid[gridnum], &grid->edicts, entitynumber); + } +} + +/* +=============== +SV_LinkEdict + +=============== +*/ +void CSSV_LinkEdict (prvm_edict_t *ent, qboolean touch_triggers) +{ + model_t *model; + + if (ent->priv.server->areagrid[0].prev) + CSSV_UnlinkEdict (ent); // unlink from old position + + if (ent == prog->edicts) + return; // don't add the world + + if (ent->priv.server->free) + return; + +// set the abs box + + if (ent->fields.client->solid == SOLID_BSP) + { + int modelindex = ent->fields.client->modelindex; + if (modelindex < 0 || modelindex > MAX_MODELS) + { + Con_Printf("edict %i: SOLID_BSP with invalid modelindex!\n", PRVM_NUM_FOR_EDICT(ent)); + modelindex = 0; + } + model = cl.model_precache[modelindex]; + if (model != NULL) + { + if (!model->TraceBox) + Con_Printf("edict %i: SOLID_BSP with non-collidable model\n", PRVM_NUM_FOR_EDICT(ent)); + + if (ent->fields.client->angles[0] || ent->fields.client->angles[2] || ent->fields.client->avelocity[0] || ent->fields.client->avelocity[2]) + { + VectorAdd(ent->fields.client->origin, model->rotatedmins, ent->fields.client->absmin); + VectorAdd(ent->fields.client->origin, model->rotatedmaxs, ent->fields.client->absmax); + } + else if (ent->fields.client->angles[1] || ent->fields.client->avelocity[1]) + { + VectorAdd(ent->fields.client->origin, model->yawmins, ent->fields.client->absmin); + VectorAdd(ent->fields.client->origin, model->yawmaxs, ent->fields.client->absmax); + } + else + { + VectorAdd(ent->fields.client->origin, model->normalmins, ent->fields.client->absmin); + VectorAdd(ent->fields.client->origin, model->normalmaxs, ent->fields.client->absmax); + } + } + else + { + // SOLID_BSP with no model is valid, mainly because some QC setup code does so temporarily + VectorAdd(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->absmin); + VectorAdd(ent->fields.client->origin, ent->fields.client->maxs, ent->fields.client->absmax); + } + } + else + { + VectorAdd(ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->absmin); + VectorAdd(ent->fields.client->origin, ent->fields.client->maxs, ent->fields.client->absmax); + } + +// +// to make items easier to pick up and allow them to be grabbed off +// of shelves, the abs sizes are expanded +// + if ((int)ent->fields.client->flags & FL_ITEM) + { + ent->fields.client->absmin[0] -= 15; + ent->fields.client->absmin[1] -= 15; + ent->fields.client->absmin[2] -= 1; + ent->fields.client->absmax[0] += 15; + ent->fields.client->absmax[1] += 15; + ent->fields.client->absmax[2] += 1; + } + else + { + // because movement is clipped an epsilon away from an actual edge, + // we must fully check even when bounding boxes don't quite touch + ent->fields.client->absmin[0] -= 1; + ent->fields.client->absmin[1] -= 1; + ent->fields.client->absmin[2] -= 1; + ent->fields.client->absmax[0] += 1; + ent->fields.client->absmax[1] += 1; + ent->fields.client->absmax[2] += 1; + } + + if (ent->fields.client->solid == SOLID_NOT) + return; + + CSSV_LinkEdict_AreaGrid(ent); + +// if touch_triggers, touch all entities at this node and descend for more + if (touch_triggers) + CSSV_TouchAreaGrid(ent); +} + + + +/* +=============================================================================== + +POINT TESTING IN HULLS + +=============================================================================== +*/ + +/* +============ +SV_TestEntityPosition + +This could be a lot more efficient... +============ +*/ +int CSSV_TestEntityPosition (prvm_edict_t *ent) +{ + return CSSV_Move (ent->fields.client->origin, ent->fields.client->mins, ent->fields.client->maxs, ent->fields.client->origin, MOVE_NORMAL, ent).startsolid; +} + + +/* +=============================================================================== + +LINE TESTING IN HULLS + +=============================================================================== +*/ + +/* +================== +SV_ClipMoveToEntity + +Handles selection or creation of a clipping hull, and offseting (and +eventually rotation) of the end points +================== +*/ +trace_t CSSV_ClipMoveToEntity(prvm_edict_t *ent, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int movetype, int hitsupercontents) +{ + trace_t trace; + model_t *model = NULL; + matrix4x4_t matrix, imatrix; + float tempnormal[3], starttransformed[3], endtransformed[3]; + float starttransformedmins[3], starttransformedmaxs[3], endtransformedmins[3], endtransformedmaxs[3]; + + memset(&trace, 0, sizeof(trace)); + trace.fraction = trace.realfraction = 1; + VectorCopy(end, trace.endpos); + + if ((int) ent->fields.client->solid == SOLID_BSP || movetype == MOVE_HITMODEL) + { + unsigned int modelindex = ent->fields.client->modelindex; + // if the modelindex is 0, it shouldn't be SOLID_BSP! + if (modelindex == 0) + { + Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with no model\n", PRVM_NUM_FOR_EDICT(ent)); + return trace; + } + if (modelindex >= MAX_MODELS) + { + Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent)); + return trace; + } + model = cl.model_precache[modelindex]; + if (modelindex != 0 && model == NULL) + { + Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with invalid modelindex\n", PRVM_NUM_FOR_EDICT(ent)); + return trace; + } + + if ((int) ent->fields.client->solid == SOLID_BSP) + { + if (!model->TraceBox) + { + Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP with a non-collidable model\n", PRVM_NUM_FOR_EDICT(ent)); + return trace; + } + //if (ent->fields.client->movetype != MOVETYPE_PUSH) + //{ + // Con_Printf("SV_ClipMoveToEntity: edict %i: SOLID_BSP without MOVETYPE_PUSH\n", PRVM_NUM_FOR_EDICT(ent)); + // return trace; + //} + } + Matrix4x4_CreateFromQuakeEntity(&matrix, 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], 1); + } + else + Matrix4x4_CreateTranslate(&matrix, ent->fields.client->origin[0], ent->fields.client->origin[1], ent->fields.client->origin[2]); + + Matrix4x4_Invert_Simple(&imatrix, &matrix); + Matrix4x4_Transform(&imatrix, start, starttransformed); + Matrix4x4_Transform(&imatrix, end, endtransformed); +#if COLLISIONPARANOID >= 3 + Con_Printf("trans(%f %f %f -> %f %f %f, %f %f %f -> %f %f %f)", start[0], start[1], start[2], starttransformed[0], starttransformed[1], starttransformed[2], end[0], end[1], end[2], endtransformed[0], endtransformed[1], endtransformed[2]); +#endif + + if (model && model->TraceBox) + { + int frame; + frame = (int)ent->fields.client->frame; + frame = bound(0, frame, (model->numframes - 1)); + VectorAdd(starttransformed, maxs, starttransformedmaxs); + VectorAdd(endtransformed, maxs, endtransformedmaxs); + VectorAdd(starttransformed, mins, starttransformedmins); + VectorAdd(endtransformed, mins, endtransformedmins); + model->TraceBox(model, frame, &trace, starttransformedmins, starttransformedmaxs, endtransformedmins, endtransformedmaxs, hitsupercontents); + } + else + Collision_ClipTrace_Box(&trace, ent->fields.client->mins, ent->fields.client->maxs, starttransformed, mins, maxs, endtransformed, hitsupercontents, SUPERCONTENTS_SOLID); + trace.fraction = bound(0, trace.fraction, 1); + trace.realfraction = bound(0, trace.realfraction, 1); + + if (trace.fraction < 1) + { + VectorLerp(start, trace.fraction, end, trace.endpos); + VectorCopy(trace.plane.normal, tempnormal); + Matrix4x4_Transform3x3(&matrix, tempnormal, trace.plane.normal); + // FIXME: should recalc trace.plane.dist + } + else + VectorCopy(end, trace.endpos); + + return trace; +} + +//=========================================================================== + +/* +================== +SV_Move +================== +*/ +#if COLLISIONPARANOID >= 1 +trace_t CSSV_Move_(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict) +#else +trace_t CSSV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict) +#endif +{ + vec3_t hullmins, hullmaxs; + int i; + int hitsupercontentsmask; + int passedictprog; + qboolean pointtrace; + prvm_edict_t *traceowner, *touch; + trace_t trace; + // bounding box of entire move area + vec3_t clipboxmins, clipboxmaxs; + // size of the moving object + vec3_t clipmins, clipmaxs; + // size when clipping against monsters + vec3_t clipmins2, clipmaxs2; + // start and end origin of move + vec3_t clipstart, clipend; + // trace results + trace_t cliptrace; + int numtouchedicts; + prvm_edict_t *touchedicts[MAX_EDICTS]; + + VectorCopy(start, clipstart); + VectorCopy(end, clipend); + VectorCopy(mins, clipmins); + VectorCopy(maxs, clipmaxs); + VectorCopy(mins, clipmins2); + VectorCopy(maxs, clipmaxs2); +#if COLLISIONPARANOID >= 3 + Con_Printf("move(%f %f %f,%f %f %f)", clipstart[0], clipstart[1], clipstart[2], clipend[0], clipend[1], clipend[2]); +#endif + + hitsupercontentsmask = SUPERCONTENTS_SOLID; + if (passedict) + { + if (passedict->fields.client->solid == SOLID_SLIDEBOX) + hitsupercontentsmask |= SUPERCONTENTS_PLAYERCLIP; + if ((int)passedict->fields.client->flags & FL_MONSTER) + hitsupercontentsmask |= SUPERCONTENTS_MONSTERCLIP; + } + + // clip to world + cliptrace = CSSV_ClipMoveToEntity(prog->edicts, clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask); + if (cliptrace.startsolid || cliptrace.fraction < 1) + cliptrace.ent = prog->edicts; + if (type == MOVE_WORLDONLY) + return cliptrace; + + if (type == MOVE_MISSILE) + { + // LordHavoc: modified this, was = -15, now -= 15 + for (i = 0;i < 3;i++) + { + clipmins2[i] -= 15; + clipmaxs2[i] += 15; + } + } + + // get adjusted box for bmodel collisions if the world is q1bsp or hlbsp + if (cl.worldmodel && cl.worldmodel->brush.RoundUpToHullSize) + cl.worldmodel->brush.RoundUpToHullSize(cl.worldmodel, clipmins, clipmaxs, hullmins, hullmaxs); + else + { + VectorCopy(clipmins, hullmins); + VectorCopy(clipmaxs, hullmaxs); + } + + // create the bounding box of the entire move + for (i = 0;i < 3;i++) + { + clipboxmins[i] = min(clipstart[i], cliptrace.endpos[i]) + min(hullmins[i], clipmins2[i]) - 1; + clipboxmaxs[i] = max(clipstart[i], cliptrace.endpos[i]) + max(hullmaxs[i], clipmaxs2[i]) + 1; + } + + // debug override to test against everything + if (sv_debugmove.integer) + { + clipboxmins[0] = clipboxmins[1] = clipboxmins[2] = -999999999; + clipboxmaxs[0] = clipboxmaxs[1] = clipboxmaxs[2] = 999999999; + } + + // if the passedict is world, make it NULL (to avoid two checks each time) + if (passedict == prog->edicts) + passedict = NULL; + // precalculate prog value for passedict for comparisons + passedictprog = PRVM_EDICT_TO_PROG(passedict); + // figure out whether this is a point trace for comparisons + pointtrace = VectorCompare(clipmins, clipmaxs); + // precalculate passedict's owner edict pointer for comparisons + traceowner = passedict ? PRVM_PROG_TO_EDICT(passedict->fields.client->owner) : 0; + + // clip to enttiies + numtouchedicts = CSSV_EntitiesInBox(clipboxmins, clipboxmaxs, MAX_EDICTS, touchedicts); + if (numtouchedicts > MAX_EDICTS) + { + // this never happens + Con_Printf("CSSV_EntitiesInBox returned %i edicts, max was %i\n", numtouchedicts, MAX_EDICTS); + numtouchedicts = MAX_EDICTS; + } + for (i = 0;i < numtouchedicts;i++) + { + touch = touchedicts[i]; + + if (touch->fields.client->solid < SOLID_BBOX) + continue; + if (type == MOVE_NOMONSTERS && touch->fields.client->solid != SOLID_BSP) + continue; + + if (passedict) + { + // don't clip against self + if (passedict == touch) + continue; + // don't clip owned entities against owner + if (traceowner == touch) + continue; + // don't clip owner against owned entities + if (passedictprog == touch->fields.client->owner) + continue; + // don't clip points against points (they can't collide) + if (pointtrace && VectorCompare(touch->fields.client->mins, touch->fields.client->maxs) && (type != MOVE_MISSILE || !((int)touch->fields.client->flags & FL_MONSTER))) + continue; + // don't clip corpse against character + if (passedict->fields.client->solid == SOLID_CORPSE && (touch->fields.client->solid == SOLID_SLIDEBOX || touch->fields.client->solid == SOLID_CORPSE)) + continue; + // don't clip character against corpse + if (passedict->fields.client->solid == SOLID_SLIDEBOX && touch->fields.client->solid == SOLID_CORPSE) + continue; + } + + // might interact, so do an exact clip + if ((int)touch->fields.client->flags & FL_MONSTER) + trace = CSSV_ClipMoveToEntity(touch, clipstart, clipmins2, clipmaxs2, clipend, type, hitsupercontentsmask); + else + trace = CSSV_ClipMoveToEntity(touch, clipstart, clipmins, clipmaxs, clipend, type, hitsupercontentsmask); + // LordHavoc: take the 'best' answers from the new trace and combine with existing data + if (trace.allsolid) + cliptrace.allsolid = true; + if (trace.startsolid) + { + cliptrace.startsolid = true; + if (cliptrace.realfraction == 1) + cliptrace.ent = touch; + } + // don't set this except on the world, because it can easily confuse + // monsters underwater if there's a bmodel involved in the trace + // (inopen && inwater is how they check water visibility) + //if (trace.inopen) + // cliptrace.inopen = true; + if (trace.inwater) + cliptrace.inwater = true; + if (trace.realfraction < cliptrace.realfraction) + { + cliptrace.fraction = trace.fraction; + cliptrace.realfraction = trace.realfraction; + VectorCopy(trace.endpos, cliptrace.endpos); + cliptrace.plane = trace.plane; + cliptrace.ent = touch; + } + cliptrace.startsupercontents |= trace.startsupercontents; + } + + return cliptrace; +} + +#if COLLISIONPARANOID >= 1 +trace_t CSSV_Move(const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int type, prvm_edict_t *passedict) +{ + int endstuck; + trace_t trace; + vec3_t temp; + trace = CSSV_Move_(start, mins, maxs, end, type, passedict); + if (passedict) + { + VectorCopy(trace.endpos, temp); + endstuck = CSSV_Move_(temp, mins, maxs, temp, type, passedict).startsolid; +#if COLLISIONPARANOID < 3 + if (trace.startsolid || endstuck) +#endif + Con_Printf("%s{e%i:%f %f %f:%f %f %f:%f:%f %f %f%s%s}\n", (trace.startsolid || endstuck) ? "\002" : "", passedict ? passedict - prog->edicts : -1, passedict->fields.client->origin[0], passedict->fields.client->origin[1], passedict->fields.client->origin[2], end[0] - passedict->fields.client->origin[0], end[1] - passedict->fields.client->origin[1], end[2] - passedict->fields.client->origin[2], trace.fraction, trace.endpos[0] - passedict->fields.client->origin[0], trace.endpos[1] - passedict->fields.client->origin[1], trace.endpos[2] - passedict->fields.client->origin[2], trace.startsolid ? " startstuck" : "", endstuck ? " endstuck" : ""); + } + return trace; +} +#endif + +int CSSV_PointSuperContents(const vec3_t point) +{ + return CSSV_Move(point, vec3_origin, vec3_origin, point, sv_gameplayfix_swiminbmodels.integer ? MOVE_NOMONSTERS : MOVE_WORLDONLY, NULL).startsupercontents; +} + +int CSSV_PointQ1Contents(const vec3_t point) +{ + return Mod_Q1BSP_NativeContentsFromSuperContents(NULL, CSSV_PointSuperContents(point)); +} + + -- 2.39.2