mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-26 22:01:50 +00:00
4fcd615308
some tweaks to try to make webgl more robust. added r_max_gpu_bones cvar. defaults to 0 in webgl for now (64 otherwise). fixed gles2+skeletal issue that was breaking webgl (note r_max_gpu_bones needs to be set to 32 or something for it to actually use glsl bones with webgl). sound rates now internally use decimals instead of percents. because meh. allow PEXT2_MAXPLAYERS and angle deltas in nq. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5052 fc73d0e0-1445-4013-8a0c-d673dee63da5
2389 lines
58 KiB
C
2389 lines
58 KiB
C
#include "quakedef.h"
|
|
|
|
/*
|
|
I think globals.maxentities is the hard cap, rather than current max like in q1.
|
|
*/
|
|
|
|
#ifdef HLSERVER
|
|
|
|
#include "winquake.h"
|
|
#include "svhl_gcapi.h"
|
|
#include "pr_common.h"
|
|
|
|
#include "crc.h"
|
|
#include "model_hl.h"
|
|
|
|
#if defined(_MSC_VER)
|
|
#if _MSC_VER >= 1300
|
|
#define __func__ __FUNCTION__
|
|
#else
|
|
#define __func__ "unknown"
|
|
#endif
|
|
#else
|
|
//I hope you're c99 and have a __func__
|
|
#endif
|
|
|
|
//extern cvar_t temp1;
|
|
#define ignore(s) Con_DPrintf("Fixme: " s "\n")
|
|
#define notimpl(l) Con_Printf("halflife sv builtin not implemented on line %i\n", l)
|
|
#define notimpf(f) Con_Printf("halflife sv builtin %s not implemented\n", f)
|
|
#define bi_begin() //if (temp1.ival)Con_Printf("enter %s\n", __func__)
|
|
#define bi_end() //if (temp1.ival)Con_Printf("leave %s\n", __func__)
|
|
#define bi_trace() bi_begin(); bi_end()
|
|
|
|
|
|
dllhandle_t *hlgamecode;
|
|
SVHL_Globals_t SVHL_Globals;
|
|
SVHL_GameFuncs_t SVHL_GameFuncs;
|
|
|
|
static zonegroup_t hlmapmemgroup; //flushed at end-of-map.
|
|
|
|
#define MAX_HL_EDICTS 2048
|
|
hledict_t *SVHL_Edict;
|
|
int SVHL_NumActiveEnts;
|
|
|
|
int lastusermessage;
|
|
|
|
|
|
|
|
|
|
string_t QDECL GHL_AllocString(const char *string)
|
|
{
|
|
char *news;
|
|
bi_begin();
|
|
if (!string)
|
|
return 0;
|
|
news = ZG_Malloc(&hlmapmemgroup, strlen(string)+1);
|
|
memcpy(news, string, strlen(string)+1);
|
|
bi_end();
|
|
return news - SVHL_Globals.stringbase;
|
|
}
|
|
int QDECL GHL_PrecacheModel(const char *name)
|
|
{
|
|
int i;
|
|
bi_trace();
|
|
|
|
if (name[0] <= ' ')
|
|
{
|
|
Con_Printf ("precache_model: empty string\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i=1 ; i<MAX_PRECACHE_MODELS ; i++)
|
|
{
|
|
if (!sv.strings.model_precache[i])
|
|
{
|
|
if (strlen(name)>=MAX_QPATH-1) //probably safest to keep this.
|
|
{
|
|
SV_Error ("Precache name too long");
|
|
return 0;
|
|
}
|
|
name = sv.strings.model_precache[i] = SVHL_Globals.stringbase+GHL_AllocString(name);
|
|
|
|
if (!strcmp(name + strlen(name) - 4, ".bsp"))
|
|
sv.models[i] = Mod_FindName(name);
|
|
|
|
if (sv.state != ss_loading)
|
|
{
|
|
Con_DPrintf("Delayed model precache: %s\n", name);
|
|
MSG_WriteByte(&sv.reliable_datagram, svcfte_precache);
|
|
MSG_WriteShort(&sv.reliable_datagram, i);
|
|
MSG_WriteString(&sv.reliable_datagram, name);
|
|
#ifdef NQPROT
|
|
MSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);
|
|
MSG_WriteShort(&sv.nqreliable_datagram, i);
|
|
MSG_WriteString(&sv.nqreliable_datagram, name);
|
|
#endif
|
|
}
|
|
|
|
return i;
|
|
}
|
|
if (!strcmp(sv.strings.model_precache[i], name))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
SV_Error ("GHL_precache_model: overflow");
|
|
return 0;
|
|
}
|
|
int QDECL GHL_PrecacheSound(char *name)
|
|
{
|
|
int i;
|
|
bi_trace();
|
|
|
|
if (name[0] <= ' ')
|
|
{
|
|
Con_Printf ("precache_sound: empty string\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i=1 ; i<MAX_PRECACHE_SOUNDS ; i++)
|
|
{
|
|
if (!*sv.strings.sound_precache[i])
|
|
{
|
|
if (strlen(name)>=MAX_QPATH-1) //probably safest to keep this.
|
|
{
|
|
SV_Error ("Precache name too long");
|
|
return 0;
|
|
}
|
|
strcpy(sv.strings.sound_precache[i], name);
|
|
name = sv.strings.sound_precache[i];
|
|
|
|
if (sv.state != ss_loading)
|
|
{
|
|
Con_DPrintf("Delayed sound precache: %s\n", name);
|
|
MSG_WriteByte(&sv.reliable_datagram, svcfte_precache);
|
|
MSG_WriteShort(&sv.reliable_datagram, -i);
|
|
MSG_WriteString(&sv.reliable_datagram, name);
|
|
#ifdef NQPROT
|
|
MSG_WriteByte(&sv.nqreliable_datagram, svcdp_precache);
|
|
MSG_WriteShort(&sv.nqreliable_datagram, -i);
|
|
MSG_WriteString(&sv.nqreliable_datagram, name);
|
|
#endif
|
|
}
|
|
|
|
return i;
|
|
}
|
|
if (!strcmp(sv.strings.sound_precache[i], name))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
SV_Error ("GHL_precache_sound: overflow");
|
|
return 0;
|
|
}
|
|
void QDECL GHL_SetModel(hledict_t *ed, char *modelname)
|
|
{
|
|
model_t *mod;
|
|
int mdlidx = GHL_PrecacheModel(modelname);
|
|
bi_trace();
|
|
ed->v.modelindex = mdlidx;
|
|
ed->v.model = sv.strings.model_precache[mdlidx] - SVHL_Globals.stringbase;
|
|
|
|
mod = sv.models[mdlidx];
|
|
if (mod)
|
|
{
|
|
VectorCopy(mod->mins, ed->v.mins);
|
|
VectorCopy(mod->maxs, ed->v.maxs);
|
|
VectorSubtract(mod->maxs, mod->mins, ed->v.size);
|
|
}
|
|
SVHL_LinkEdict(ed, false);
|
|
}
|
|
unk QDECL GHL_ModelIndex(unk){notimpf(__func__);}
|
|
int QDECL GHL_ModelFrames(int midx)
|
|
{
|
|
bi_trace();
|
|
//returns the number of frames(sequences I assume) this model has
|
|
return Mod_GetFrameCount(sv.models[mdlidx], surfaceidx);
|
|
}
|
|
void QDECL GHL_SetSize(hledict_t *ed, float *mins, float *maxs)
|
|
{
|
|
bi_trace();
|
|
VectorCopy(mins, ed->v.mins);
|
|
VectorCopy(maxs, ed->v.maxs);
|
|
SVHL_LinkEdict(ed, false);
|
|
}
|
|
void QDECL GHL_ChangeLevel(char *nextmap, char *startspot)
|
|
{
|
|
bi_trace();
|
|
Cbuf_AddText(va("changelevel %s %s@%f@%f@%f\n", nextmap, startspot, SVHL_Globals.landmark[0], SVHL_Globals.landmark[1], SVHL_Globals.landmark[2]), RESTRICT_PROGS);
|
|
}
|
|
unk QDECL GHL_GetSpawnParms(unk){notimpf(__func__);}
|
|
unk QDECL GHL_SaveSpawnParms(unk){notimpf(__func__);}
|
|
float QDECL GHL_VecToYaw(float *inv)
|
|
{
|
|
vec3_t outa;
|
|
bi_trace();
|
|
|
|
VectorAngles(inv, NULL, outa);
|
|
return outa[1];
|
|
}
|
|
void QDECL GHL_VecToAngles(float *inv, float *outa)
|
|
{
|
|
bi_trace();
|
|
VectorAngles(inv, NULL, outa);
|
|
}
|
|
#define DEG2RAD( a ) ( a * M_PI ) / 180.0F
|
|
void QDECL GHL_MoveToOrigin(hledict_t *ent, vec3_t dest, float dist, int moveflags)
|
|
{
|
|
//mode 0: move_normal
|
|
//mode 1: no idea
|
|
//mode 2: test only
|
|
// float dz;
|
|
vec3_t oldorg, neworg, end;
|
|
trace_t trace;
|
|
// int i;
|
|
int eflags = ent->v.flags;
|
|
const vec3_t up = {0, 0, 1};
|
|
vec3_t move;
|
|
qboolean relink = true;
|
|
qboolean domove = true;
|
|
|
|
bi_trace();
|
|
|
|
if (moveflags)
|
|
{ //strafe. just move directly.
|
|
VectorSubtract(dest, ent->v.origin, move);
|
|
move[2] = 0;
|
|
VectorNormalize(move);
|
|
VectorMA(ent->v.origin, dist, move, move);
|
|
}
|
|
else
|
|
{
|
|
float yaw = DEG2RAD(ent->v.angles[1]);
|
|
move[0] = cos(yaw) * dist;
|
|
move[1] = sin(yaw) * dist;
|
|
move[2] = 0;
|
|
}
|
|
|
|
// try the move
|
|
VectorCopy (ent->v.origin, oldorg);
|
|
VectorAdd (ent->v.origin, move, neworg);
|
|
|
|
#if 0
|
|
// flying monsters don't step up
|
|
if (eflags & (FL_SWIM | FL_FLY))
|
|
{
|
|
// try one move with vertical motion, then one without
|
|
for (i=0 ; i<2 ; i++)
|
|
{
|
|
VectorAdd (ent->v.origin, move, neworg);
|
|
if (!noenemy)
|
|
{
|
|
enemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy);
|
|
if (i == 0 && enemy->entnum)
|
|
{
|
|
VectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end);
|
|
dz = DotProduct(end, axis[2]);
|
|
if (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/
|
|
dz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2];
|
|
if (dz > 40)
|
|
VectorMA(neworg, -8, up, neworg);
|
|
if (dz < 30)
|
|
VectorMA(neworg, 8, up, neworg);
|
|
}
|
|
}
|
|
trace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);
|
|
if (set_move_trace)
|
|
set_move_trace(world->progs, set_trace_globs, &trace);
|
|
|
|
if (trace.fraction == 1)
|
|
{
|
|
if ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID))
|
|
continue; // swim monster left water
|
|
|
|
if (domove)
|
|
VectorCopy (trace.endpos, ent->v->origin);
|
|
if (relink)
|
|
World_LinkEdict (world, ent, true);
|
|
return true;
|
|
}
|
|
|
|
if (noenemy || !enemy->entnum)
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
// push down from a step height above the wished position
|
|
VectorMA(neworg, movevars.stepheight, up, neworg);
|
|
VectorMA(neworg, movevars.stepheight*-2, up, end);
|
|
|
|
trace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
|
|
|
|
if (trace.allsolid)
|
|
return false;
|
|
|
|
if (trace.startsolid)
|
|
{
|
|
//move up by an extra step, if needed
|
|
VectorMA(neworg, -movevars.stepheight, up, neworg);
|
|
trace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
|
|
if (trace.allsolid || trace.startsolid)
|
|
return false;
|
|
}
|
|
if (trace.fraction == 1)
|
|
{
|
|
// if monster had the ground pulled out, go ahead and fall
|
|
if ( (int)eflags & FL_PARTIALGROUND )
|
|
{
|
|
if (domove)
|
|
{
|
|
VectorAdd (ent->v.origin, move, ent->v.origin);
|
|
if (relink)
|
|
SVHL_LinkEdict (ent, true);
|
|
ent->v.flags = (int)eflags & ~FL_ONGROUND;
|
|
}
|
|
// Con_Printf ("fall down\n");
|
|
return true;
|
|
}
|
|
|
|
return false; // walked off an edge
|
|
}
|
|
|
|
// check point traces down for dangling corners
|
|
if (domove)
|
|
VectorCopy (trace.endpos, ent->v.origin);
|
|
|
|
/* if (!World_CheckBottom (world, ent, up))
|
|
{
|
|
if ( (int)ent->v->flags & FL_PARTIALGROUND )
|
|
{ // entity had floor mostly pulled out from underneath it
|
|
// and is trying to correct
|
|
if (relink)
|
|
SVHL_LinkEdict (ent, true);
|
|
return true;
|
|
}
|
|
|
|
if (domove)
|
|
VectorCopy (oldorg, ent->v->origin);
|
|
return false;
|
|
}
|
|
*/
|
|
if ( (int)ent->v.flags & FL_PARTIALGROUND )
|
|
{
|
|
// Con_Printf ("back on ground\n");
|
|
ent->v.flags &= ~FL_PARTIALGROUND;
|
|
}
|
|
ent->v.groundentity = trace.ent;
|
|
|
|
// the move is ok
|
|
if (relink)
|
|
SVHL_LinkEdict (ent, true);
|
|
return true;
|
|
}
|
|
unk QDECL GHL_ChangeYaw(unk){notimpf(__func__);}
|
|
unk QDECL GHL_ChangePitch(unk){notimpf(__func__);}
|
|
hledict_t *QDECL GHL_FindEntityByString(hledict_t *last, char *field, char *value)
|
|
{
|
|
hledict_t *ent;
|
|
int i;
|
|
int ofs;
|
|
string_t str;
|
|
bi_trace();
|
|
if (!strcmp(field, "targetname"))
|
|
ofs = (char*)&((hledict_t *)NULL)->v.targetname - (char*)NULL;
|
|
else if (!strcmp(field, "classname"))
|
|
ofs = (char*)&((hledict_t *)NULL)->v.classname - (char*)NULL;
|
|
else
|
|
{
|
|
Con_Printf("Fixme: Look for %s=%s\n", field, value);
|
|
return NULL;
|
|
}
|
|
|
|
if (last)
|
|
i=last-SVHL_Edict+1;
|
|
else
|
|
i = 0;
|
|
for (; i<SVHL_Globals.maxentities; i++)
|
|
{
|
|
ent = &SVHL_Edict[i];
|
|
if (ent->isfree)
|
|
continue;
|
|
str = *(string_t*)((char*)ent+ofs);
|
|
if (str && !strcmp(SVHL_Globals.stringbase+str, value))
|
|
return ent;
|
|
}
|
|
return SVHL_Edict;
|
|
}
|
|
void Sh_CalcPointLight(vec3_t point, vec3_t light);
|
|
int QDECL GHL_GetEntityIllum(hledict_t *ent)
|
|
{
|
|
vec3_t diffuse, ambient, dir;
|
|
float lev = 0;
|
|
#if defined(RTLIGHTS) && !defined(SERVERONLY)
|
|
Sh_CalcPointLight(ent->v.origin, ambient);
|
|
lev += VectorLength(ambient);
|
|
|
|
if (!r_shadow_realtime_world.ival || r_shadow_realtime_world_lightmaps.value)
|
|
#endif
|
|
{
|
|
sv.world.worldmodel->funcs.LightPointValues(sv.world.worldmodel, ent->v.origin, ambient, diffuse, dir);
|
|
lev += (VectorLength(ambient) + VectorLength(diffuse)/2.0)/256;
|
|
}
|
|
return lev * 255; //I assume its 0-255, no idea
|
|
}
|
|
hledict_t *QDECL GHL_FindEntityInSphere(hledict_t *last, float *org, float radius)
|
|
{
|
|
int i, j;
|
|
vec3_t eorg;
|
|
hledict_t *ent;
|
|
|
|
bi_trace();
|
|
|
|
radius = radius*radius;
|
|
|
|
if (last)
|
|
i=last-SVHL_Edict+1;
|
|
else
|
|
i = 0;
|
|
for (; i<SVHL_Globals.maxentities; i++)
|
|
{
|
|
ent = &SVHL_Edict[i];
|
|
if (ent->isfree)
|
|
continue;
|
|
if (!ent->v.solid)
|
|
continue;
|
|
for (j=0; j<3; j++)
|
|
eorg[j] = org[j] - (ent->v.origin[j] + (ent->v.mins[j] + ent->v.maxs[j])*0.5);
|
|
if (DotProduct(eorg,eorg) > radius)
|
|
continue;
|
|
|
|
//its close enough
|
|
return ent;
|
|
}
|
|
return NULL;
|
|
}
|
|
hledict_t *QDECL GHL_FindClientInPVS(hledict_t *ed)
|
|
{
|
|
qbyte *viewerpvs;
|
|
int best = 0, i;
|
|
float bestdist = 99999999; //HL maps are limited in size anyway
|
|
float d;
|
|
int clusternum;
|
|
vec3_t ofs;
|
|
hledict_t *other;
|
|
|
|
bi_trace();
|
|
|
|
//fixme: we need to track some state
|
|
//a different client should be returned each call _per ent_ (so it can be used once per frame)
|
|
|
|
viewerpvs = sv.world.worldmodel->funcs.ClusterPVS(sv.world.worldmodel, sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, ed->v.origin), NULL, 0);
|
|
|
|
for (i = 0; i < svs.allocated_client_slots; i++)
|
|
{
|
|
if (svs.clients[i].state == cs_spawned)
|
|
{
|
|
other = &SVHL_Edict[i+1];
|
|
if (ed == other)
|
|
continue; //ignore yourself.
|
|
if (svs.clients[i].spectator)
|
|
continue; //ignore spectators
|
|
|
|
clusternum = sv.world.worldmodel->funcs.ClusterForPoint(sv.world.worldmodel, other->v.origin)-1;/*pvs is 1 based, leafs are 0 based*/
|
|
if (viewerpvs[clusternum>>3] & (1<<(clusternum&7)))
|
|
{
|
|
VectorSubtract(ed->v.origin, other->v.origin, ofs);
|
|
d = DotProduct(ofs, ofs);
|
|
|
|
if (d < bestdist)
|
|
{
|
|
bestdist = d;
|
|
best = i+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (best)
|
|
return &SVHL_Edict[best];
|
|
return NULL;
|
|
}
|
|
unk QDECL GHL_EntitiesInPVS(unk){notimpf(__func__);}
|
|
void QDECL GHL_MakeVectors(float *angles)
|
|
{
|
|
bi_trace();
|
|
AngleVectors(angles, SVHL_Globals.v_forward, SVHL_Globals.v_right, SVHL_Globals.v_up);
|
|
}
|
|
void QDECL GHL_AngleVectors(float *angles, float *forward, float *right, float *up)
|
|
{
|
|
bi_trace();
|
|
AngleVectors(angles, forward, right, up);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////
|
|
|
|
hledict_t *QDECL GHL_CreateEntity(void)
|
|
{
|
|
int i;
|
|
static int spawnnumber;
|
|
bi_trace();
|
|
spawnnumber++;
|
|
|
|
for (i = sv.allocated_client_slots; i < SVHL_NumActiveEnts; i++)
|
|
{
|
|
if (SVHL_Edict[i].isfree)
|
|
{
|
|
if (SVHL_Edict[i].freetime > sv.time)
|
|
continue;
|
|
|
|
memset(&SVHL_Edict[i], 0, sizeof(SVHL_Edict[i]));
|
|
SVHL_Edict[i].spawnnumber = spawnnumber;
|
|
SVHL_Edict[i].v.edict = &SVHL_Edict[i];
|
|
return &SVHL_Edict[i];
|
|
}
|
|
}
|
|
if (i < MAX_HL_EDICTS)
|
|
{
|
|
SVHL_NumActiveEnts++;
|
|
memset(&SVHL_Edict[i], 0, sizeof(SVHL_Edict[i]));
|
|
SVHL_Edict[i].spawnnumber = spawnnumber;
|
|
SVHL_Edict[i].v.edict = &SVHL_Edict[i];
|
|
return &SVHL_Edict[i];
|
|
}
|
|
SV_Error("Ran out of free edicts");
|
|
return NULL;
|
|
}
|
|
void QDECL GHL_RemoveEntity(hledict_t *ed)
|
|
{
|
|
bi_trace();
|
|
SVHL_UnlinkEdict(ed);
|
|
ed->isfree = true;
|
|
ed->freetime = sv.time+2;
|
|
}
|
|
hledict_t *QDECL GHL_CreateNamedEntity(string_t name)
|
|
{
|
|
void (QDECL *spawnfunc)(hlentvars_t *evars);
|
|
hledict_t *ed;
|
|
bi_trace();
|
|
ed = GHL_CreateEntity();
|
|
if (!ed)
|
|
return NULL;
|
|
ed->v.classname = name;
|
|
|
|
spawnfunc = (void(QDECL *)(hlentvars_t*))GetProcAddress((HINSTANCE)hlgamecode, name+SVHL_Globals.stringbase);
|
|
if (spawnfunc)
|
|
spawnfunc(&ed->v);
|
|
return ed;
|
|
}
|
|
void *QDECL GHL_PvAllocEntPrivateData(hledict_t *ed, long quant)
|
|
{
|
|
bi_trace();
|
|
if (!ed)
|
|
return NULL;
|
|
ed->moddata = ZG_Malloc(&hlmapmemgroup, quant);
|
|
return ed->moddata;
|
|
}
|
|
unk QDECL GHL_PvEntPrivateData(unk)
|
|
{
|
|
bi_trace();
|
|
notimpf(__func__);
|
|
}
|
|
unk QDECL GHL_FreeEntPrivateData(unk)
|
|
{
|
|
bi_trace();
|
|
notimpf(__func__);
|
|
}
|
|
unk QDECL GHL_GetVarsOfEnt(unk)
|
|
{
|
|
bi_trace();
|
|
notimpf(__func__);
|
|
}
|
|
hledict_t *QDECL GHL_PEntityOfEntOffset(int ednum)
|
|
{
|
|
bi_trace();
|
|
return (hledict_t *)(ednum + (char*)SVHL_Edict);
|
|
}
|
|
int QDECL GHL_EntOffsetOfPEntity(hledict_t *ed)
|
|
{
|
|
bi_trace();
|
|
return (char*)ed - (char*)SVHL_Edict;
|
|
}
|
|
int QDECL GHL_IndexOfEdict(hledict_t *ed)
|
|
{
|
|
bi_trace();
|
|
return ed - SVHL_Edict;
|
|
}
|
|
hledict_t *QDECL GHL_PEntityOfEntIndex(int idx)
|
|
{
|
|
bi_trace();
|
|
return &SVHL_Edict[idx];
|
|
}
|
|
unk QDECL GHL_FindEntityByVars(unk)
|
|
{
|
|
bi_trace();
|
|
notimpf(__func__);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////
|
|
|
|
unk QDECL GHL_MakeStatic(unk){notimpf(__func__);}
|
|
unk QDECL GHL_EntIsOnFloor(unk){notimpf(__func__);}
|
|
int QDECL GHL_DropToFloor(hledict_t *ed)
|
|
{
|
|
vec3_t top;
|
|
vec3_t bottom;
|
|
trace_t tr;
|
|
bi_trace();
|
|
VectorCopy(ed->v.origin, top);
|
|
VectorCopy(ed->v.origin, bottom);
|
|
top[2] += 1;
|
|
bottom[2] -= 256;
|
|
tr = SVHL_Move(top, ed->v.mins, ed->v.maxs, bottom, 0, 0, ed);
|
|
VectorCopy(tr.endpos, ed->v.origin);
|
|
return tr.fraction != 0 && tr.fraction != 1;
|
|
}
|
|
int QDECL GHL_WalkMove(hledict_t *ent, float yaw, float dist, int mode)
|
|
{
|
|
//mode 0: no idea
|
|
//mode 1: no idea
|
|
//mode 2: test only
|
|
// float dz;
|
|
vec3_t oldorg, neworg, end;
|
|
trace_t trace;
|
|
// int i;
|
|
int eflags = ent->v.flags;
|
|
const vec3_t up = {0, 0, 1};
|
|
vec3_t move;
|
|
qboolean relink = mode != 2;
|
|
qboolean domove = mode != 2;
|
|
|
|
bi_trace();
|
|
|
|
yaw = DEG2RAD(yaw);
|
|
move[0] = cos(yaw) * dist;
|
|
move[1] = sin(yaw) * dist;
|
|
move[2] = 0;
|
|
|
|
// try the move
|
|
VectorCopy (ent->v.origin, oldorg);
|
|
VectorAdd (ent->v.origin, move, neworg);
|
|
|
|
#if 0
|
|
// flying monsters don't step up
|
|
if (eflags & (FL_SWIM | FL_FLY))
|
|
{
|
|
// try one move with vertical motion, then one without
|
|
for (i=0 ; i<2 ; i++)
|
|
{
|
|
VectorAdd (ent->v.origin, move, neworg);
|
|
if (!noenemy)
|
|
{
|
|
enemy = (wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy);
|
|
if (i == 0 && enemy->entnum)
|
|
{
|
|
VectorSubtract(ent->v->origin, ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->origin, end);
|
|
dz = DotProduct(end, axis[2]);
|
|
if (eflags & FLH2_HUNTFACE) /*get the ent's origin_z to match its victims face*/
|
|
dz += ((wedict_t*)PROG_TO_EDICT(world->progs, ent->v->enemy))->v->view_ofs[2];
|
|
if (dz > 40)
|
|
VectorMA(neworg, -8, up, neworg);
|
|
if (dz < 30)
|
|
VectorMA(neworg, 8, up, neworg);
|
|
}
|
|
}
|
|
trace = World_Move (world, ent->v->origin, ent->v->mins, ent->v->maxs, neworg, false, ent);
|
|
if (set_move_trace)
|
|
set_move_trace(world->progs, set_trace_globs, &trace);
|
|
|
|
if (trace.fraction == 1)
|
|
{
|
|
if ( (eflags & FL_SWIM) && !(World_PointContents(world, trace.endpos) & FTECONTENTS_FLUID))
|
|
continue; // swim monster left water
|
|
|
|
if (domove)
|
|
VectorCopy (trace.endpos, ent->v->origin);
|
|
if (relink)
|
|
World_LinkEdict (world, ent, true);
|
|
return true;
|
|
}
|
|
|
|
if (noenemy || !enemy->entnum)
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
// push down from a step height above the wished position
|
|
VectorMA(neworg, movevars.stepheight, up, neworg);
|
|
VectorMA(neworg, movevars.stepheight*-2, up, end);
|
|
|
|
trace = SVHL_Move(neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
|
|
|
|
if (trace.allsolid)
|
|
return false;
|
|
|
|
if (trace.startsolid)
|
|
{
|
|
//move up by an extra step, if needed
|
|
VectorMA(neworg, -movevars.stepheight, up, neworg);
|
|
trace = SVHL_Move (neworg, ent->v.mins, ent->v.maxs, end, 0, 0, ent);
|
|
if (trace.allsolid || trace.startsolid)
|
|
return false;
|
|
}
|
|
if (trace.fraction == 1)
|
|
{
|
|
// if monster had the ground pulled out, go ahead and fall
|
|
if ( (int)eflags & FL_PARTIALGROUND )
|
|
{
|
|
if (domove)
|
|
{
|
|
VectorAdd (ent->v.origin, move, ent->v.origin);
|
|
if (relink)
|
|
SVHL_LinkEdict (ent, true);
|
|
ent->v.flags = (int)eflags & ~FL_ONGROUND;
|
|
}
|
|
// Con_Printf ("fall down\n");
|
|
return true;
|
|
}
|
|
|
|
return false; // walked off an edge
|
|
}
|
|
|
|
// check point traces down for dangling corners
|
|
if (domove)
|
|
VectorCopy (trace.endpos, ent->v.origin);
|
|
|
|
/* if (!World_CheckBottom (world, ent, up))
|
|
{
|
|
if ( (int)ent->v->flags & FL_PARTIALGROUND )
|
|
{ // entity had floor mostly pulled out from underneath it
|
|
// and is trying to correct
|
|
if (relink)
|
|
SVHL_LinkEdict (ent, true);
|
|
return true;
|
|
}
|
|
|
|
if (domove)
|
|
VectorCopy (oldorg, ent->v->origin);
|
|
return false;
|
|
}
|
|
*/
|
|
if ( (int)ent->v.flags & FL_PARTIALGROUND )
|
|
{
|
|
// Con_Printf ("back on ground\n");
|
|
ent->v.flags &= ~FL_PARTIALGROUND;
|
|
}
|
|
ent->v.groundentity = trace.ent;
|
|
|
|
// the move is ok
|
|
if (relink)
|
|
SVHL_LinkEdict (ent, true);
|
|
return true;
|
|
}
|
|
void QDECL GHL_SetOrigin(hledict_t *ed, float *neworg)
|
|
{
|
|
bi_trace();
|
|
VectorCopy(neworg, ed->v.origin);
|
|
SVHL_LinkEdict(ed, false);
|
|
}
|
|
void QDECL GHL_EmitSound(hledict_t *ed, int chan, char *soundname, float vol, float atten, int flags, int pitch)
|
|
{
|
|
bi_trace();
|
|
if (*soundname == '!')
|
|
return; //would need us to parse sound/sentances.txt I guess
|
|
SV_StartSound(ed-SVHL_Edict, ed->v.origin, ~0, chan, soundname, vol*255, atten, pitch, 0, 0);
|
|
}
|
|
void QDECL GHL_EmitAmbientSound(hledict_t *ed, float *org, char *soundname, float vol, float atten, unsigned int flags, int pitch)
|
|
{
|
|
bi_trace();
|
|
SV_StartSound(0, org, ~0, 0, soundname, vol*255, atten, pitch, 0, 0);
|
|
}
|
|
void QDECL GHL_TraceLine(float *start, float *end, int flags, hledict_t *ignore, hltraceresult_t *result)
|
|
{
|
|
trace_t t;
|
|
bi_trace();
|
|
|
|
t = SVHL_Move(start, vec3_origin, vec3_origin, end, flags, 0, ignore);
|
|
|
|
result->allsolid = t.allsolid;
|
|
result->startsolid = t.startsolid;
|
|
result->inopen = t.inopen;
|
|
result->inwater = t.inwater;
|
|
result->fraction = t.fraction;
|
|
VectorCopy(t.endpos, result->endpos);
|
|
result->planedist = t.plane.dist;
|
|
VectorCopy(t.plane.normal, result->planenormal);
|
|
result->touched = t.ent;
|
|
if (!result->touched)
|
|
result->touched = &SVHL_Edict[0];
|
|
result->hitgroup = 0;
|
|
}
|
|
unk QDECL GHL_TraceToss(unk){notimpf(__func__);}
|
|
unk QDECL GHL_TraceMonsterHull(unk){notimpf(__func__);}
|
|
void QDECL GHL_TraceHull(float *start, float *end, int flags, int hullnum, hledict_t *ignore, hltraceresult_t *result)
|
|
{
|
|
trace_t t;
|
|
bi_trace();
|
|
|
|
t = SVHL_Move(start, sv.world.worldmodel->hulls[hullnum].clip_mins, sv.world.worldmodel->hulls[hullnum].clip_maxs, end, flags, 0, ignore);
|
|
|
|
result->allsolid = t.allsolid;
|
|
result->startsolid = t.startsolid;
|
|
result->inopen = t.inopen;
|
|
result->inwater = t.inwater;
|
|
result->fraction = t.fraction;
|
|
VectorCopy(t.endpos, result->endpos);
|
|
result->planedist = t.plane.dist;
|
|
VectorCopy(t.plane.normal, result->planenormal);
|
|
result->touched = t.ent;
|
|
result->hitgroup = 0;
|
|
}
|
|
unk QDECL GHL_TraceModel(unk){notimpf(__func__);}
|
|
char *QDECL GHL_TraceTexture(hledict_t *againstent, vec3_t start, vec3_t end)
|
|
{
|
|
trace_t tr;
|
|
bi_trace();
|
|
sv.world.worldmodel->funcs.NativeTrace(sv.world.worldmodel, 0, NULLFRAMESTATE, NULL, start, end, vec3_origin, vec3_origin, false, MASK_WORLDSOLID, &tr);
|
|
return tr.surface->name;
|
|
}
|
|
unk QDECL GHL_TraceSphere(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetAimVector(unk){notimpf(__func__);}
|
|
void QDECL GHL_ServerCommand(char *cmd)
|
|
{
|
|
bi_trace();
|
|
if (!strcmp(cmd, "reload\n"))
|
|
cmd = "restart\n";
|
|
Cbuf_AddText(cmd, RESTRICT_PROGS);
|
|
}
|
|
void QDECL GHL_ServerExecute(void)
|
|
{
|
|
bi_trace();
|
|
Cbuf_ExecuteLevel(RESTRICT_PROGS);
|
|
}
|
|
unk QDECL GHL_ClientCommand(unk){notimpf(__func__);}
|
|
unk QDECL GHL_ParticleEffect(unk){notimpf(__func__);}
|
|
void QDECL GHL_LightStyle(int stylenum, char *stylestr)
|
|
{
|
|
vec3_t rgb = {1,1,1};
|
|
bi_trace();
|
|
PF_applylightstyle(stylenum, stylestr, rgb);
|
|
}
|
|
int QDECL GHL_DecalIndex(char *decalname)
|
|
{
|
|
bi_trace();
|
|
Con_Printf("Fixme: precache decal %s\n", decalname);
|
|
return 0;
|
|
}
|
|
int QDECL GHL_PointContents(float *org)
|
|
{
|
|
bi_trace();
|
|
return Q1CONTENTS_EMPTY;
|
|
}
|
|
|
|
int svhl_messagedest;
|
|
vec3_t svhl_messageorigin;
|
|
hledict_t *svhl_messageent;
|
|
void QDECL GHL_MessageBegin(int dest, int type, float *org, hledict_t *ent)
|
|
{
|
|
bi_trace();
|
|
|
|
svhl_messagedest = dest;
|
|
if (org)
|
|
VectorCopy(org, svhl_messageorigin);
|
|
else
|
|
VectorClear(svhl_messageorigin);
|
|
svhl_messageent = ent;
|
|
|
|
if (sv.multicast.cursize)
|
|
{
|
|
Con_Printf("MessageBegin called without MessageEnd\n");
|
|
SZ_Clear (&sv.multicast);
|
|
}
|
|
MSG_WriteByte(&sv.multicast, svcfte_cgamepacket);
|
|
MSG_WriteShort(&sv.multicast, 0);
|
|
MSG_WriteByte(&sv.multicast, type);
|
|
}
|
|
void QDECL GHL_MessageEnd(unk)
|
|
{
|
|
unsigned short len;
|
|
client_t *cl;
|
|
|
|
bi_trace();
|
|
|
|
if (!sv.multicast.cursize)
|
|
{
|
|
Con_Printf("MessageEnd called without MessageBegin\n");
|
|
return;
|
|
}
|
|
|
|
//update the length
|
|
len = sv.multicast.cursize - 3;
|
|
sv.multicast.data[1] = len&0xff;
|
|
sv.multicast.data[2] = len>>8;
|
|
|
|
switch(svhl_messagedest)
|
|
{
|
|
case MSG_BROADCAST:
|
|
SZ_Write(&sv.datagram, sv.multicast.data, sv.multicast.cursize);
|
|
break;
|
|
case MSG_ONE:
|
|
cl = &svs.clients[svhl_messageent - SVHL_Edict - 1];
|
|
if (cl->state >= cs_spawned)
|
|
{
|
|
ClientReliableCheckBlock(cl, sv.multicast.cursize);
|
|
ClientReliableWrite_SZ(cl, sv.multicast.data, sv.multicast.cursize);
|
|
}
|
|
break;
|
|
case MSG_ALL:
|
|
SZ_Write(&sv.reliable_datagram, sv.multicast.data, sv.multicast.cursize);
|
|
break;
|
|
case MSG_MULTICAST:
|
|
SV_Multicast(svhl_messageorigin, MULTICAST_PVS);
|
|
break;
|
|
case MSG_MULTICAST+1:
|
|
SV_Multicast(svhl_messageorigin, MULTICAST_PHS);
|
|
break;
|
|
case 9:
|
|
//spectators only
|
|
break;
|
|
default:
|
|
Con_Printf("GHL_MessageEnd: dest type %i not supported\n", svhl_messagedest);
|
|
break;
|
|
}
|
|
|
|
SZ_Clear (&sv.multicast);
|
|
}
|
|
void QDECL GHL_WriteByte(int value)
|
|
{
|
|
bi_trace();
|
|
MSG_WriteByte(&sv.multicast, value & 0xff);
|
|
}
|
|
void QDECL GHL_WriteChar(int value)
|
|
{
|
|
bi_trace();
|
|
MSG_WriteChar(&sv.multicast, value);
|
|
}
|
|
void QDECL GHL_WriteShort(int value)
|
|
{
|
|
bi_trace();
|
|
MSG_WriteShort(&sv.multicast, value);
|
|
}
|
|
void QDECL GHL_WriteLong(int value)
|
|
{
|
|
bi_trace();
|
|
MSG_WriteLong(&sv.multicast, value);
|
|
}
|
|
void QDECL GHL_WriteAngle(float value)
|
|
{
|
|
bi_trace();
|
|
MSG_WriteAngle8(&sv.multicast, value);
|
|
}
|
|
void QDECL GHL_WriteCoord(float value)
|
|
{
|
|
coorddata i = MSG_ToCoord(value, 2);
|
|
bi_trace();
|
|
SZ_Write (&sv.multicast, (void*)&i, 2);
|
|
}
|
|
void QDECL GHL_WriteString(char *string)
|
|
{
|
|
bi_trace();
|
|
MSG_WriteString(&sv.multicast, string);
|
|
}
|
|
void QDECL GHL_WriteEntity(int entnum)
|
|
{
|
|
bi_trace();
|
|
MSG_WriteEntity(&sv.multicast, entnum);
|
|
}
|
|
|
|
|
|
void QDECL GHL_AlertMessage(int level, char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
bi_trace();
|
|
|
|
if (level == 2 && !developer.ival)
|
|
return;
|
|
|
|
va_start (argptr, fmt);
|
|
vsnprintf (string,sizeof(string)-1, fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
Con_Printf("%s\n", string);
|
|
}
|
|
void QDECL GHL_EngineFprintf(FILE *f, char *fmt, ...)
|
|
{
|
|
bi_trace();
|
|
SV_Error("Halflife gamecode tried to use EngineFprintf\n");
|
|
}
|
|
unk QDECL GHL_SzFromIndex(unk){notimpf(__func__);}
|
|
void *QDECL GHL_GetModelPtr(hledict_t *ed)
|
|
{
|
|
bi_trace();
|
|
#ifdef SERVERONLY
|
|
return NULL;
|
|
#else
|
|
if (!ed->v.modelindex)
|
|
return NULL;
|
|
if (!sv.models[ed->v.modelindex])
|
|
sv.models[ed->v.modelindex] = Mod_ForName(sv.strings.model_precache[ed->v.modelindex], false);
|
|
return Mod_GetHalfLifeModelData(sv.models[ed->v.modelindex]);
|
|
#endif
|
|
}
|
|
int QDECL GHL_RegUserMsg(char *msgname, int msgsize)
|
|
{
|
|
bi_trace();
|
|
//we use 1 as the code to choose others.
|
|
if (lastusermessage <= 1)
|
|
return -1;
|
|
|
|
SV_FlushSignon ();
|
|
|
|
//for new clients
|
|
MSG_WriteByte(&sv.signon, svcfte_cgamepacket);
|
|
MSG_WriteShort(&sv.signon, strlen(msgname)+3);
|
|
MSG_WriteByte(&sv.signon, 1);
|
|
MSG_WriteByte(&sv.signon, lastusermessage);
|
|
MSG_WriteString(&sv.signon, msgname);
|
|
|
|
//and if the client is already spawned...
|
|
MSG_WriteByte(&sv.reliable_datagram, svcfte_cgamepacket);
|
|
MSG_WriteShort(&sv.reliable_datagram, strlen(msgname)+3);
|
|
MSG_WriteByte(&sv.reliable_datagram, 1);
|
|
MSG_WriteByte(&sv.reliable_datagram, lastusermessage);
|
|
MSG_WriteString(&sv.reliable_datagram, msgname);
|
|
|
|
return lastusermessage--;
|
|
}
|
|
unk QDECL GHL_AnimationAutomove(unk){notimpf(__func__);}
|
|
|
|
static void GHL_GetFrameState(hledict_t *ent, framestate_t *fstate)
|
|
{
|
|
memset(fstate, 0, sizeof(*fstate));
|
|
|
|
fstate->g[FS_REG].frametime[0] = (SVHL_Globals.time - ent->v.framestarttime) * ent->v.framerate;
|
|
fstate->g[FS_REG].frame[0] = ent->v.frame;
|
|
fstate->g[FS_REG].lerpweight[0] = 1;
|
|
fstate->g[FS_REG].subblendfrac = ent->v.blending[0]; //fixme: which is upper?
|
|
|
|
//FIXME: no lower parts.
|
|
|
|
fstate->bonecontrols[0] = ent->v.controller[0] / 255.0;
|
|
fstate->bonecontrols[1] = ent->v.controller[1] / 255.0;
|
|
fstate->bonecontrols[2] = ent->v.controller[2] / 255.0;
|
|
fstate->bonecontrols[3] = ent->v.controller[3] / 255.0;
|
|
}
|
|
static void bonemat_fromqcvectors(float *out, const float vx[3], const float vy[3], const float vz[3], const float t[3])
|
|
{
|
|
out[0] = vx[0];
|
|
out[1] = -vy[0];
|
|
out[2] = vz[0];
|
|
out[3] = t[0];
|
|
out[4] = vx[1];
|
|
out[5] = -vy[1];
|
|
out[6] = vz[1];
|
|
out[7] = t[1];
|
|
out[8] = vx[2];
|
|
out[9] = -vy[2];
|
|
out[10] = vz[2];
|
|
out[11] = t[2];
|
|
}
|
|
static void bonemat_fromidentity(float *out)
|
|
{
|
|
out[0] = 1;
|
|
out[1] = 0;
|
|
out[2] = 0;
|
|
out[3] = 0;
|
|
|
|
out[4] = 0;
|
|
out[5] = 1;
|
|
out[6] = 0;
|
|
out[7] = 0;
|
|
|
|
out[8] = 0;
|
|
out[9] = 0;
|
|
out[10] = 1;
|
|
out[11] = 0;
|
|
}
|
|
static void bonemat_toqcvectors(const float *in, float vx[3], float vy[3], float vz[3], float t[3])
|
|
{
|
|
vx[0] = in[0];
|
|
vx[1] = in[4];
|
|
vx[2] = in[8];
|
|
vy[0] = -in[1];
|
|
vy[1] = -in[5];
|
|
vy[2] = -in[9];
|
|
vz[0] = in[2];
|
|
vz[1] = in[6];
|
|
vz[2] = in[10];
|
|
t [0] = in[3];
|
|
t [1] = in[7];
|
|
t [2] = in[11];
|
|
}
|
|
static void bonemat_fromhlentity(hledict_t *ed, float *trans)
|
|
{
|
|
vec3_t d[3], a;
|
|
a[0] = -ed->v.angles[0];
|
|
a[1] = ed->v.angles[1];
|
|
a[2] = ed->v.angles[2];
|
|
AngleVectors(a, d[0], d[1], d[2]);
|
|
bonemat_fromqcvectors(trans, d[0], d[1], d[2], ed->v.origin);
|
|
}
|
|
void QDECL GHL_GetBonePosition(hledict_t *ed, int bone, vec3_t org, vec3_t ang)
|
|
{
|
|
float transent[12];
|
|
float transforms[12];
|
|
float result[12];
|
|
model_t *mod = sv.models[ed->v.modelindex];
|
|
vec3_t axis[3];
|
|
framestate_t fstate;
|
|
GHL_GetFrameState(ed, &fstate);
|
|
|
|
bone += 1; //I *think* the bones are 0-based unlike our tag-based bone numbers
|
|
|
|
if (!Mod_GetTag(mod, bone, &fstate, transforms))
|
|
{
|
|
bonemat_fromidentity(transforms);
|
|
}
|
|
|
|
bonemat_fromhlentity(ed, transent);
|
|
R_ConcatTransforms((void*)transent, (void*)transforms, (void*)result);
|
|
|
|
bonemat_toqcvectors(result, axis[0], axis[1], axis[2], org);
|
|
VectorAngles(axis[0], axis[2], ang);
|
|
}
|
|
|
|
hlintptr_t QDECL GHL_FunctionFromName(char *name)
|
|
{
|
|
bi_trace();
|
|
return (hlintptr_t)Sys_GetAddressForName(hlgamecode, name);
|
|
}
|
|
char *QDECL GHL_NameForFunction(hlintptr_t function)
|
|
{
|
|
bi_trace();
|
|
return Sys_GetNameForAddress(hlgamecode, (void*)function);
|
|
}
|
|
|
|
unk QDECL GHL_ClientPrintf(unk)
|
|
{
|
|
bi_trace();
|
|
// SV_ClientPrintf(
|
|
notimpf(__func__);
|
|
}
|
|
void QDECL GHL_ServerPrint(char *msg)
|
|
{
|
|
bi_trace();
|
|
Con_Printf("%s", msg);
|
|
}
|
|
char *QDECL GHL_Cmd_Args(void)
|
|
{
|
|
bi_trace();
|
|
return Cmd_Args();
|
|
}
|
|
char *QDECL GHL_Cmd_Argv(int arg)
|
|
{
|
|
bi_trace();
|
|
return Cmd_Argv(arg);
|
|
}
|
|
int QDECL GHL_Cmd_Argc(unk)
|
|
{
|
|
bi_trace();
|
|
return Cmd_Argc();
|
|
}
|
|
unk QDECL GHL_GetAttachment(unk){notimpf(__func__);}
|
|
void QDECL GHL_CRC32_Init(hlcrc_t *crc)
|
|
{
|
|
unsigned short crc16;
|
|
bi_trace();
|
|
QCRC_Init(&crc16);
|
|
*crc = crc16;
|
|
}
|
|
void QDECL GHL_CRC32_ProcessBuffer(hlcrc_t *crc, qbyte *p, int len)
|
|
{
|
|
unsigned short crc16 = *crc;
|
|
bi_trace();
|
|
while(len-->0)
|
|
{
|
|
QCRC_ProcessByte(&crc16, *p++);
|
|
}
|
|
*crc = crc16;
|
|
}
|
|
void QDECL GHL_CRC32_ProcessByte(hlcrc_t *crc, qbyte b)
|
|
{
|
|
unsigned short crc16 = *crc;
|
|
bi_trace();
|
|
QCRC_ProcessByte(&crc16, b);
|
|
*crc = crc16;
|
|
}
|
|
hlcrc_t QDECL GHL_CRC32_Final(hlcrc_t crc)
|
|
{
|
|
unsigned short crc16 = crc;
|
|
bi_trace();
|
|
return QCRC_Value(crc16);
|
|
}
|
|
long QDECL GHL_RandomLong(long minv, long maxv)
|
|
{
|
|
bi_trace();
|
|
return minv + frandom()*(maxv-minv);
|
|
}
|
|
float QDECL GHL_RandomFloat(float minv, float maxv)
|
|
{
|
|
bi_trace();
|
|
return minv + frandom()*(maxv-minv);
|
|
}
|
|
unk QDECL GHL_SetView(unk){notimpf(__func__);}
|
|
unk QDECL GHL_Time(unk){notimpf(__func__);}
|
|
unk QDECL GHL_CrosshairAngle(unk){notimpf(__func__);}
|
|
void *QDECL GHL_LoadFileForMe(char *name, int *size_out)
|
|
{
|
|
int fsize;
|
|
void *fptr;
|
|
bi_trace();
|
|
fsize = FS_LoadFile(name, &fptr);
|
|
if (size_out)
|
|
*size_out = fsize;
|
|
if (fsize == -1)
|
|
return NULL;
|
|
return fptr;
|
|
}
|
|
void QDECL GHL_FreeFile(void *fptr)
|
|
{
|
|
bi_trace();
|
|
FS_FreeFile(fptr);
|
|
}
|
|
unk QDECL GHL_EndSection(unk){notimpf(__func__);}
|
|
#include <sys/stat.h>
|
|
int QDECL GHL_CompareFileTime(char *fname1, char *fname2, int *result)
|
|
{
|
|
flocation_t loc1, loc2;
|
|
struct stat stat1, stat2;
|
|
bi_trace();
|
|
|
|
//results:
|
|
//1 = f1 is newer
|
|
//0 = equal age
|
|
//-1 = f2 is newer
|
|
//at least I think that's what it means.
|
|
*result = 0;
|
|
if (!FS_FLocateFile(fname1, FSLFRT_IFFOUND, &loc1) || !FS_FLocateFile(fname2, FSLFRT_IFFOUND, &loc2))
|
|
return 0;
|
|
|
|
if (stat(loc1.rawname, &stat1) || stat(loc2.rawname, &stat2))
|
|
return 0;
|
|
|
|
if (stat1.st_mtime > stat2.st_mtime)
|
|
*result = 1;
|
|
else if (stat1.st_mtime < stat2.st_mtime)
|
|
*result = -1;
|
|
else
|
|
*result = 0;
|
|
|
|
return 1;
|
|
}
|
|
void QDECL GHL_GetGameDir(char *gamedir)
|
|
{
|
|
extern char gamedirfile[];
|
|
bi_trace();
|
|
//warning: the output buffer size is not specified!
|
|
Q_strncpyz(gamedir, gamedirfile, MAX_QPATH);
|
|
}
|
|
unk QDECL GHL_Cvar_RegisterVariable(unk){notimpf(__func__);}
|
|
unk QDECL GHL_FadeClientVolume(unk){notimpf(__func__);}
|
|
unk QDECL GHL_SetClientMaxspeed(unk)
|
|
{
|
|
bi_trace();
|
|
notimpf(__func__);
|
|
}
|
|
unk QDECL GHL_CreateFakeClient(unk){notimpf(__func__);}
|
|
unk QDECL GHL_RunPlayerMove(unk){notimpf(__func__);}
|
|
int QDECL GHL_NumberOfEntities(void)
|
|
{
|
|
bi_trace();
|
|
return 0;
|
|
}
|
|
char *QDECL GHL_GetInfoKeyBuffer(hledict_t *ed)
|
|
{
|
|
bi_trace();
|
|
if (!ed)
|
|
return svs.info;
|
|
|
|
return svs.clients[ed - SVHL_Edict - 1].userinfo;
|
|
}
|
|
char *QDECL GHL_InfoKeyValue(char *infostr, char *key)
|
|
{
|
|
bi_trace();
|
|
return Info_ValueForKey(infostr, key);
|
|
}
|
|
unk QDECL GHL_SetKeyValue(unk){notimpf(__func__);}
|
|
unk QDECL GHL_SetClientKeyValue(unk){notimpf(__func__);}
|
|
unk QDECL GHL_IsMapValid(unk){notimpf(__func__);}
|
|
unk QDECL GHL_StaticDecal(unk){notimpf(__func__);}
|
|
unk QDECL GHL_PrecacheGeneric(unk){notimpf(__func__);}
|
|
int QDECL GHL_GetPlayerUserId(hledict_t *ed)
|
|
{
|
|
unsigned int clnum = (ed - SVHL_Edict) - 1;
|
|
bi_trace();
|
|
if (clnum >= sv.allocated_client_slots)
|
|
return -1;
|
|
return svs.clients[clnum].userid;
|
|
}
|
|
unk QDECL GHL_BuildSoundMsg(unk){notimpf(__func__);}
|
|
|
|
int QDECL GHL_IsDedicatedServer(void)
|
|
{
|
|
bi_trace();
|
|
#ifdef SERVERONLY
|
|
return 1;
|
|
#else
|
|
return qrenderer == QR_NONE;
|
|
#endif
|
|
}
|
|
|
|
hlcvar_t *hlcvar_malloced;
|
|
hlcvar_t *hlcvar_stored;
|
|
void SVHL_UpdateCvar(cvar_t *var)
|
|
{
|
|
if (!var->hlcvar)
|
|
return; //nothing to do
|
|
var->hlcvar->string = var->string;
|
|
var->hlcvar->value = var->value;
|
|
}
|
|
|
|
void SVHL_FreeCvars(void)
|
|
{
|
|
cvar_t *nc;
|
|
hlcvar_t *n;
|
|
|
|
//forget all
|
|
while (hlcvar_malloced)
|
|
{
|
|
n = hlcvar_malloced;
|
|
hlcvar_malloced = n->next;
|
|
|
|
nc = Cvar_FindVar(n->name);
|
|
if (nc)
|
|
nc->hlcvar = NULL;
|
|
Z_Free(n);
|
|
}
|
|
|
|
while (hlcvar_stored)
|
|
{
|
|
n = hlcvar_stored;
|
|
hlcvar_stored = n->next;
|
|
|
|
nc = Cvar_FindVar(n->name);
|
|
if (nc)
|
|
nc->hlcvar = NULL;
|
|
}
|
|
}
|
|
void SVHL_FreeCvar(hlcvar_t *var)
|
|
{
|
|
cvar_t *nc;
|
|
hlcvar_t **ref;
|
|
|
|
//unlink (free if it was malloced)
|
|
ref = &hlcvar_malloced;
|
|
while (*ref)
|
|
{
|
|
if (*ref == var)
|
|
{
|
|
(*ref) = (*ref)->next;
|
|
|
|
nc = Cvar_FindVar(var->name);
|
|
if (nc)
|
|
nc->hlcvar = NULL;
|
|
Z_Free(var);
|
|
return;
|
|
}
|
|
ref = &(*ref)->next;
|
|
}
|
|
|
|
ref = &hlcvar_stored;
|
|
while (*ref)
|
|
{
|
|
if (*ref == var)
|
|
{
|
|
(*ref) = (*ref)->next;
|
|
|
|
nc = Cvar_FindVar(var->name);
|
|
if (nc)
|
|
nc->hlcvar = NULL;
|
|
return;
|
|
}
|
|
ref = &(*ref)->next;
|
|
}
|
|
}
|
|
|
|
hlcvar_t *QDECL GHL_CVarGetPointer(char *varname)
|
|
{
|
|
cvar_t *var;
|
|
hlcvar_t *hlvar;
|
|
bi_trace();
|
|
var = Cvar_Get(varname, "", 0, "HalfLife");
|
|
if (!var)
|
|
{
|
|
Con_Printf("Not giving cvar \"%s\" to game\n", varname);
|
|
return NULL;
|
|
}
|
|
hlvar = var->hlcvar;
|
|
if (!hlvar)
|
|
{
|
|
hlvar = var->hlcvar = Z_Malloc(sizeof(hlcvar_t));
|
|
hlvar->name = var->name;
|
|
hlvar->string = var->string;
|
|
hlvar->value = var->value;
|
|
|
|
hlvar->next = hlcvar_malloced;
|
|
hlcvar_malloced = hlvar;
|
|
}
|
|
return hlvar;
|
|
}
|
|
void QDECL GHL_CVarRegister(hlcvar_t *hlvar)
|
|
{
|
|
cvar_t *var;
|
|
bi_trace();
|
|
var = Cvar_Get(hlvar->name, hlvar->string, 0, "HalfLife");
|
|
if (!var)
|
|
{
|
|
Con_Printf("Not giving cvar \"%s\" to game\n", hlvar->name);
|
|
return;
|
|
}
|
|
if (var->hlcvar)
|
|
{
|
|
SVHL_FreeCvar(var->hlcvar);
|
|
|
|
hlvar->next = hlcvar_stored;
|
|
hlcvar_stored = hlvar;
|
|
}
|
|
var->hlcvar = hlvar;
|
|
}
|
|
float QDECL GHL_CVarGetFloat(char *vname)
|
|
{
|
|
cvar_t *var = Cvar_FindVar(vname);
|
|
bi_trace();
|
|
if (var)
|
|
return var->value;
|
|
Con_Printf("cvar %s does not exist\n", vname);
|
|
return 0;
|
|
}
|
|
char *QDECL GHL_CVarGetString(char *vname)
|
|
{
|
|
cvar_t *var = Cvar_FindVar(vname);
|
|
bi_trace();
|
|
if (var)
|
|
return var->string;
|
|
Con_Printf("cvar %s does not exist\n", vname);
|
|
return "";
|
|
}
|
|
void QDECL GHL_CVarSetFloat(char *vname, float value)
|
|
{
|
|
cvar_t *var = Cvar_FindVar(vname);
|
|
bi_trace();
|
|
if (var)
|
|
Cvar_SetValue(var, value);
|
|
else
|
|
Con_Printf("cvar %s does not exist\n", vname);
|
|
}
|
|
void QDECL GHL_CVarSetString(char *vname, char *value)
|
|
{
|
|
cvar_t *var = Cvar_FindVar(vname);
|
|
bi_trace();
|
|
if (var)
|
|
Cvar_Set(var, value);
|
|
else
|
|
Con_Printf("cvar %s does not exist\n", vname);
|
|
}
|
|
|
|
unk QDECL GHL_GetPlayerWONId(unk){notimpf(__func__);}
|
|
unk QDECL GHL_Info_RemoveKey(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetPhysicsKeyValue(unk){notimpf(__func__);}
|
|
void QDECL GHL_SetPhysicsKeyValue(hledict_t *ent, char *key, char *value)
|
|
{
|
|
bi_begin();
|
|
notimpf(__func__);
|
|
bi_end();
|
|
}
|
|
unk QDECL GHL_GetPhysicsInfoString(unk){notimpf(__func__);}
|
|
unsigned short QDECL GHL_PrecacheEvent(int eventtype, char *eventname)
|
|
{
|
|
bi_trace();
|
|
Con_Printf("Fixme: GHL_PrecacheEvent: %s\n", eventname);
|
|
return 0;
|
|
}
|
|
void QDECL GHL_PlaybackEvent(int flags, hledict_t *ent, unsigned short eventidx, float delay, float *origin, float *angles, float f1, float f2, int i1, int i2, int b1, int b2)
|
|
{
|
|
bi_trace();
|
|
ignore("GHL_PlaybackEvent not implemented");
|
|
}
|
|
unk QDECL GHL_SetFatPVS(unk){notimpf(__func__);}
|
|
unk QDECL GHL_SetFatPAS(unk){notimpf(__func__);}
|
|
unk QDECL GHL_CheckVisibility(unk){notimpf(__func__);}
|
|
unk QDECL GHL_DeltaSetField(unk){notimpf(__func__);}
|
|
unk QDECL GHL_DeltaUnsetField(unk){notimpf(__func__);}
|
|
unk QDECL GHL_DeltaAddEncoder(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetCurrentPlayer(unk){notimpf(__func__);}
|
|
int QDECL GHL_CanSkipPlayer(hledict_t *playerent)
|
|
{
|
|
bi_trace();
|
|
return false;
|
|
// notimpf(__func__);
|
|
}
|
|
unk QDECL GHL_DeltaFindField(unk){notimpf(__func__);}
|
|
unk QDECL GHL_DeltaSetFieldByIndex(unk){notimpf(__func__);}
|
|
unk QDECL GHL_DeltaUnsetFieldByIndex(unk){notimpf(__func__);}
|
|
unk QDECL GHL_SetGroupMask(unk){notimpf(__func__);}
|
|
unk QDECL GHL_CreateInstancedBaseline(unk){notimpf(__func__);}
|
|
unk QDECL GHL_Cvar_DirectSet(unk){notimpf(__func__);}
|
|
unk QDECL GHL_ForceUnmodified(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetPlayerStats(unk){notimpf(__func__);}
|
|
unk QDECL GHL_AddServerCommand(unk){notimpf(__func__);}
|
|
unk QDECL GHL_Voice_GetClientListening(unk){notimpf(__func__);}
|
|
qboolean QDECL GHL_Voice_SetClientListening(int listener, int sender, int shouldlisten)
|
|
{
|
|
bi_trace();
|
|
return false;
|
|
}
|
|
char *QDECL GHL_GetPlayerAuthId(hledict_t *playered)
|
|
{
|
|
unsigned int clnum = (playered - SVHL_Edict) - 1;
|
|
bi_trace();
|
|
if (clnum >= sv.allocated_client_slots)
|
|
return NULL;
|
|
return svs.clients[clnum].guid;
|
|
}
|
|
unk QDECL GHL_SequenceGet(unk){notimpf(__func__);}
|
|
unk QDECL GHL_SequencePickSentence(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetFileSize(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetApproxWavePlayLen(unk){notimpf(__func__);}
|
|
unk QDECL GHL_IsCareerMatch(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetLocalizedStringLength(unk){notimpf(__func__);}
|
|
unk QDECL GHL_RegisterTutorMessageShown(unk){notimpf(__func__);}
|
|
unk QDECL GHL_GetTimesTutorMessageShown(unk){notimpf(__func__);}
|
|
unk QDECL GHL_ProcessTutorMessageDecayBuffer(unk){notimpf(__func__);}
|
|
unk QDECL GHL_ConstructTutorMessageDecayBuffer(unk){notimpf(__func__);}
|
|
unk QDECL GHL_ResetTutorMessageDecayData(unk){notimpf(__func__);}
|
|
unk QDECL GHL_QueryClientCvarValue(unk){notimpf(__func__);}
|
|
unk QDECL GHL_QueryClientCvarValue2(unk){notimpf(__func__);}
|
|
|
|
|
|
|
|
|
|
//====================================================================================================
|
|
|
|
|
|
|
|
|
|
|
|
SVHL_Builtins_t SVHL_Builtins =
|
|
{
|
|
GHL_PrecacheModel,
|
|
GHL_PrecacheSound,
|
|
GHL_SetModel,
|
|
GHL_ModelIndex,
|
|
GHL_ModelFrames,
|
|
GHL_SetSize,
|
|
GHL_ChangeLevel,
|
|
GHL_GetSpawnParms,
|
|
GHL_SaveSpawnParms,
|
|
GHL_VecToYaw,
|
|
GHL_VecToAngles,
|
|
GHL_MoveToOrigin,
|
|
GHL_ChangeYaw,
|
|
GHL_ChangePitch,
|
|
GHL_FindEntityByString,
|
|
GHL_GetEntityIllum,
|
|
GHL_FindEntityInSphere,
|
|
GHL_FindClientInPVS,
|
|
GHL_EntitiesInPVS,
|
|
GHL_MakeVectors,
|
|
GHL_AngleVectors,
|
|
GHL_CreateEntity,
|
|
GHL_RemoveEntity,
|
|
GHL_CreateNamedEntity,
|
|
GHL_MakeStatic,
|
|
GHL_EntIsOnFloor,
|
|
GHL_DropToFloor,
|
|
GHL_WalkMove,
|
|
GHL_SetOrigin,
|
|
GHL_EmitSound,
|
|
GHL_EmitAmbientSound,
|
|
GHL_TraceLine,
|
|
GHL_TraceToss,
|
|
GHL_TraceMonsterHull,
|
|
GHL_TraceHull,
|
|
GHL_TraceModel,
|
|
GHL_TraceTexture,
|
|
GHL_TraceSphere,
|
|
GHL_GetAimVector,
|
|
GHL_ServerCommand,
|
|
GHL_ServerExecute,
|
|
GHL_ClientCommand,
|
|
GHL_ParticleEffect,
|
|
GHL_LightStyle,
|
|
GHL_DecalIndex,
|
|
GHL_PointContents,
|
|
GHL_MessageBegin,
|
|
GHL_MessageEnd,
|
|
GHL_WriteByte,
|
|
GHL_WriteChar,
|
|
GHL_WriteShort,
|
|
GHL_WriteLong,
|
|
GHL_WriteAngle,
|
|
GHL_WriteCoord,
|
|
GHL_WriteString,
|
|
GHL_WriteEntity,
|
|
GHL_CVarRegister,
|
|
GHL_CVarGetFloat,
|
|
GHL_CVarGetString,
|
|
GHL_CVarSetFloat,
|
|
GHL_CVarSetString,
|
|
GHL_AlertMessage,
|
|
GHL_EngineFprintf,
|
|
GHL_PvAllocEntPrivateData,
|
|
GHL_PvEntPrivateData,
|
|
GHL_FreeEntPrivateData,
|
|
GHL_SzFromIndex,
|
|
GHL_AllocString,
|
|
GHL_GetVarsOfEnt,
|
|
GHL_PEntityOfEntOffset,
|
|
GHL_EntOffsetOfPEntity,
|
|
GHL_IndexOfEdict,
|
|
GHL_PEntityOfEntIndex,
|
|
GHL_FindEntityByVars,
|
|
GHL_GetModelPtr,
|
|
GHL_RegUserMsg,
|
|
GHL_AnimationAutomove,
|
|
GHL_GetBonePosition,
|
|
GHL_FunctionFromName,
|
|
GHL_NameForFunction,
|
|
GHL_ClientPrintf,
|
|
GHL_ServerPrint,
|
|
GHL_Cmd_Args,
|
|
GHL_Cmd_Argv,
|
|
GHL_Cmd_Argc,
|
|
GHL_GetAttachment,
|
|
GHL_CRC32_Init,
|
|
GHL_CRC32_ProcessBuffer,
|
|
GHL_CRC32_ProcessByte,
|
|
GHL_CRC32_Final,
|
|
GHL_RandomLong,
|
|
GHL_RandomFloat,
|
|
GHL_SetView,
|
|
GHL_Time,
|
|
GHL_CrosshairAngle,
|
|
GHL_LoadFileForMe,
|
|
GHL_FreeFile,
|
|
GHL_EndSection,
|
|
GHL_CompareFileTime,
|
|
GHL_GetGameDir,
|
|
GHL_Cvar_RegisterVariable,
|
|
GHL_FadeClientVolume,
|
|
GHL_SetClientMaxspeed,
|
|
GHL_CreateFakeClient,
|
|
GHL_RunPlayerMove,
|
|
GHL_NumberOfEntities,
|
|
GHL_GetInfoKeyBuffer,
|
|
GHL_InfoKeyValue,
|
|
GHL_SetKeyValue,
|
|
GHL_SetClientKeyValue,
|
|
GHL_IsMapValid,
|
|
GHL_StaticDecal,
|
|
GHL_PrecacheGeneric,
|
|
GHL_GetPlayerUserId,
|
|
GHL_BuildSoundMsg,
|
|
GHL_IsDedicatedServer,
|
|
#if HALFLIFE_API_VERSION > 138
|
|
GHL_CVarGetPointer,
|
|
GHL_GetPlayerWONId,
|
|
GHL_Info_RemoveKey,
|
|
GHL_GetPhysicsKeyValue,
|
|
GHL_SetPhysicsKeyValue,
|
|
GHL_GetPhysicsInfoString,
|
|
GHL_PrecacheEvent,
|
|
GHL_PlaybackEvent,
|
|
GHL_SetFatPVS,
|
|
GHL_SetFatPAS,
|
|
GHL_CheckVisibility,
|
|
GHL_DeltaSetField,
|
|
GHL_DeltaUnsetField,
|
|
GHL_DeltaAddEncoder,
|
|
GHL_GetCurrentPlayer,
|
|
GHL_CanSkipPlayer,
|
|
GHL_DeltaFindField,
|
|
GHL_DeltaSetFieldByIndex,
|
|
GHL_DeltaUnsetFieldByIndex,
|
|
GHL_SetGroupMask,
|
|
GHL_CreateInstancedBaseline,
|
|
GHL_Cvar_DirectSet,
|
|
GHL_ForceUnmodified,
|
|
GHL_GetPlayerStats,
|
|
GHL_AddServerCommand,
|
|
GHL_Voice_GetClientListening,
|
|
GHL_Voice_SetClientListening,
|
|
GHL_GetPlayerAuthId,
|
|
GHL_SequenceGet,
|
|
GHL_SequencePickSentence,
|
|
GHL_GetFileSize,
|
|
GHL_GetApproxWavePlayLen,
|
|
GHL_IsCareerMatch,
|
|
GHL_GetLocalizedStringLength,
|
|
GHL_RegisterTutorMessageShown,
|
|
GHL_GetTimesTutorMessageShown,
|
|
GHL_ProcessTutorMessageDecayBuffer,
|
|
GHL_ConstructTutorMessageDecayBuffer,
|
|
GHL_ResetTutorMessageDecayData,
|
|
GHL_QueryClientCvarValue,
|
|
GHL_QueryClientCvarValue2,
|
|
#endif
|
|
|
|
0xdeadbeef
|
|
};
|
|
|
|
void SV_ReadLibListDotGam(void)
|
|
{
|
|
char key[1024];
|
|
char value[1024];
|
|
char *file;
|
|
char *s;
|
|
size_t fsize;
|
|
|
|
Info_SetValueForStarKey(svs.info, "*gamedll", "", sizeof(svs.info));
|
|
Info_SetValueForStarKey(svs.info, "*cldll", "", sizeof(svs.info));
|
|
|
|
file = COM_LoadTempFile("liblist.gam", &fsize);
|
|
if (!file)
|
|
return;
|
|
|
|
Info_SetValueForStarKey(svs.info, "*cldll", "1", sizeof(svs.info));
|
|
|
|
while ((file = COM_ParseOut(file, key, sizeof(key))))
|
|
{
|
|
file = COM_ParseOut(file, value, sizeof(value));
|
|
|
|
while((s = strchr(value, '\\')))
|
|
*s = '/';
|
|
|
|
if (!strcmp(key, "gamedll"
|
|
#ifdef __linux__
|
|
"_linux"
|
|
#endif
|
|
))
|
|
Info_SetValueForStarKey(svs.info, "*gamedll", value, sizeof(svs.info));
|
|
if (!strcmp(key, "cldll"))
|
|
Info_SetValueForStarKey(svs.info, "*cldll", atoi(value)?"1":"", sizeof(svs.info));
|
|
}
|
|
}
|
|
|
|
int SVHL_InitGame(void)
|
|
{
|
|
char *gamedll;
|
|
void *iterator;
|
|
char path[MAX_OSPATH];
|
|
char fullname[MAX_OSPATH];
|
|
void (WINAPI *GiveFnptrsToDll) (funcs, globals);
|
|
int (QDECL *GetEntityAPI)(SVHL_GameFuncs_t *pFunctionTable, int apivers);
|
|
|
|
dllfunction_t hlgamefuncs[] =
|
|
{
|
|
{(void**)&GiveFnptrsToDll, "GiveFnptrsToDll"},
|
|
{(void**)&GetEntityAPI, "GetEntityAPI"},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
memset(&SVHL_Globals, 0, sizeof(SVHL_Globals));
|
|
|
|
if (sizeof(long) != sizeof(void*))
|
|
{
|
|
Con_Printf("sizeof(long)!=sizeof(ptr): Cannot run halflife gamecode on this platform\n");
|
|
return 0;
|
|
}
|
|
|
|
SV_ReadLibListDotGam();
|
|
|
|
if (hlgamecode)
|
|
{
|
|
ZG_FreeGroup(&hlmapmemgroup);
|
|
|
|
SVHL_Edict = ZG_Malloc(&hlmapmemgroup, sizeof(*SVHL_Edict) * MAX_HL_EDICTS);
|
|
SVHL_Globals.maxentities = MAX_HL_EDICTS; //I think this is correct
|
|
return 1;
|
|
}
|
|
|
|
gamedll = Info_ValueForKey(svs.info, "*gamedll");
|
|
iterator = NULL;
|
|
//FIXME: game dlls from game paths are evil/exploitable.
|
|
while(COM_IteratePaths(&iterator, path, sizeof(path), NULL, 0))
|
|
{
|
|
snprintf (fullname, sizeof(fullname), "%s%s", path, gamedll);
|
|
hlgamecode = Sys_LoadLibrary(fullname, hlgamefuncs);
|
|
if (hlgamecode)
|
|
break;
|
|
}
|
|
|
|
if (!hlgamecode)
|
|
return 0;
|
|
|
|
SVHL_Edict = ZG_Malloc(&hlmapmemgroup, sizeof(*SVHL_Edict) * MAX_HL_EDICTS);
|
|
SVHL_Globals.maxentities = MAX_HL_EDICTS; //I think this is correct
|
|
GiveFnptrsToDll(&SVHL_Builtins, &SVHL_Globals);
|
|
|
|
if (!GetEntityAPI(&SVHL_GameFuncs, HALFLIFE_API_VERSION))
|
|
{
|
|
Con_Printf(CON_ERROR "Error: %s is incompatible (Engine is compiled for %i)\n", fullname, HALFLIFE_API_VERSION);
|
|
if (GetEntityAPI(&SVHL_GameFuncs, 138))
|
|
Con_Printf(CON_ERROR "mod is 138\n");
|
|
Sys_CloseLibrary(hlgamecode);
|
|
hlgamecode = NULL;
|
|
return 0;
|
|
}
|
|
|
|
bi_begin();
|
|
SVHL_GameFuncs.GameDLLInit();
|
|
bi_end();
|
|
return 1;
|
|
}
|
|
|
|
void SVHL_SaveLevelCache(char *filename)
|
|
{
|
|
|
|
}
|
|
|
|
void SVHL_SetupGame(void)
|
|
{
|
|
lastusermessage = 255;
|
|
//called on new map
|
|
}
|
|
|
|
void SVHL_SpawnEntities(char *entstring)
|
|
{
|
|
char key[256];
|
|
char value[1024];
|
|
char classname[1024];
|
|
hlfielddef_t fdef;
|
|
hledict_t *ed, *world;
|
|
extern cvar_t coop; //who'd have thought it, eh?
|
|
char *ts;
|
|
int i;
|
|
|
|
//initialise globals
|
|
SVHL_Globals.stringbase = ""; //uninitialised strings are considered empty and not crashy. this ensures that is true.
|
|
SVHL_Globals.maxclients = svs.allocated_client_slots;
|
|
SVHL_Globals.deathmatch = deathmatch.value;
|
|
SVHL_Globals.coop = coop.value;
|
|
SVHL_Globals.serverflags = 0;
|
|
if (!strncmp(sv.modelname, "maps/", 5))
|
|
COM_StripExtension(sv.modelname+5, value, sizeof(value));
|
|
else
|
|
COM_StripExtension(sv.modelname, value, sizeof(value));
|
|
SVHL_Globals.mapname = GHL_AllocString(value);
|
|
SVHL_Globals.time = 0;
|
|
|
|
SVHL_NumActiveEnts = 0;
|
|
|
|
sv.allocated_client_slots = 0;
|
|
|
|
//touch world.
|
|
world = GHL_CreateNamedEntity(GHL_AllocString("worldspawn"));
|
|
world->v.solid = SOLID_BSP;
|
|
GHL_SetModel(world, sv.modelname);
|
|
|
|
//spawn player ents
|
|
for (i = 0; i < SVHL_Globals.maxclients; i++)
|
|
{
|
|
sv.allocated_client_slots++;
|
|
ed = GHL_CreateNamedEntity(GHL_AllocString("player"));
|
|
}
|
|
for (i = 0; i < sv.allocated_client_slots; i++)
|
|
{
|
|
SVHL_Edict[i].isfree = true;
|
|
}
|
|
sv.allocated_client_slots = i;
|
|
|
|
//precache the inline models (and touch them).
|
|
sv.strings.model_precache[0] = "";
|
|
sv.strings.model_precache[1] = sv.modelname; //the qvm doesn't have access to this array
|
|
for (i=1 ; i<sv.world.worldmodel->numsubmodels ; i++)
|
|
{
|
|
const char *s = va("*%i", i);
|
|
char *n;
|
|
n = ZG_Malloc(&hlmapmemgroup, strlen(s)+1);
|
|
strcpy(n, s);
|
|
sv.strings.model_precache[1+i] = n;
|
|
sv.models[i+1] = Mod_ForName (Mod_FixName(n, sv.modelname), false);
|
|
}
|
|
|
|
while (entstring)
|
|
{
|
|
entstring = COM_ParseOut(entstring, key, sizeof(key));
|
|
if (strcmp(key, "{"))
|
|
break;
|
|
|
|
*classname = 0;
|
|
|
|
ts = entstring;
|
|
while (ts)
|
|
{
|
|
ts = COM_ParseOut(ts, key, sizeof(key));
|
|
if (!strcmp(key, "}"))
|
|
break;
|
|
ts = COM_ParseOut(ts, value, sizeof(value));
|
|
|
|
if (!strcmp(key, "classname"))
|
|
{
|
|
memcpy(classname, value, strlen(value)+1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (world)
|
|
{
|
|
if (strcmp(classname, "worldspawn"))
|
|
SV_Error("first entity is not worldspawn");
|
|
ed = world;
|
|
world = NULL;
|
|
}
|
|
else
|
|
ed = GHL_CreateNamedEntity(GHL_AllocString(classname));
|
|
|
|
while (entstring)
|
|
{
|
|
entstring = COM_ParseOut(entstring, key, sizeof(key));
|
|
if (!strcmp(key, "}"))
|
|
break;
|
|
entstring = COM_ParseOut(entstring, value, sizeof(value));
|
|
|
|
if (*key == '_')
|
|
continue;
|
|
|
|
if (!strcmp(key, "classname"))
|
|
memcpy(classname, value, strlen(value)+1);
|
|
|
|
fdef.handled = false;
|
|
if (!*classname)
|
|
fdef.classname = NULL;
|
|
else
|
|
fdef.classname = classname;
|
|
fdef.key = key;
|
|
fdef.value = value;
|
|
SVHL_GameFuncs.DispatchKeyValue(ed, &fdef);
|
|
if (!fdef.handled)
|
|
{
|
|
if (!strcmp(key, "angle"))
|
|
{
|
|
float a = atof(value);
|
|
sprintf(value, "%f %f %f", 0.0f, a, 0.0f);
|
|
strcpy(key, "angles");
|
|
SVHL_GameFuncs.DispatchKeyValue(ed, &fdef);
|
|
}
|
|
if (!fdef.handled)
|
|
Con_Printf("Bad field on %s, %s\n", classname, key);
|
|
}
|
|
}
|
|
SVHL_GameFuncs.DispatchSpawn(ed);
|
|
}
|
|
|
|
bi_begin();
|
|
SVHL_GameFuncs.ServerActivate(SVHL_Edict, SVHL_NumActiveEnts, sv.allocated_client_slots);
|
|
bi_end();
|
|
}
|
|
|
|
void SVHL_ShutdownGame(void)
|
|
{
|
|
SVHL_FreeCvars();
|
|
|
|
//gametype changed, or server shutdown
|
|
Sys_CloseLibrary(hlgamecode);
|
|
hlgamecode = NULL;
|
|
|
|
SVHL_Edict = NULL;
|
|
memset(&SVHL_Globals, 0, sizeof(SVHL_Globals));
|
|
memset(&SVHL_GameFuncs, 0, sizeof(SVHL_GameFuncs));
|
|
memset(&SVHL_GameFuncsEx, 0, sizeof(SVHL_GameFuncsEx));
|
|
|
|
ZG_FreeGroup(&hlmapmemgroup);
|
|
}
|
|
|
|
qboolean HLSV_ClientCommand(client_t *client)
|
|
{
|
|
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
|
|
if (!hlgamecode)
|
|
return false;
|
|
if (!strcmp("noclip", Cmd_Argv(0)))
|
|
{
|
|
if (ed->v.movetype != MOVETYPE_NOCLIP)
|
|
ed->v.movetype = MOVETYPE_NOCLIP;
|
|
else
|
|
ed->v.movetype = MOVETYPE_WALK;
|
|
return true;
|
|
}
|
|
if (!strcmp("kill", Cmd_Argv(0)))
|
|
{
|
|
SVHL_GameFuncs.ClientKill(ed);
|
|
return true;
|
|
}
|
|
bi_begin();
|
|
SVHL_GameFuncs.ClientCommand(ed);
|
|
bi_end();
|
|
return true;
|
|
}
|
|
|
|
qboolean SVHL_ClientConnect(client_t *client, netadr_t adr, char rejectmessage[128])
|
|
{
|
|
qboolean result;
|
|
char ipadr[256];
|
|
NET_AdrToString(ipadr, sizeof(ipadr), &adr);
|
|
strcpy(rejectmessage, "Rejected by gamecode");
|
|
|
|
sv.skipbprintclient = client;
|
|
bi_begin();
|
|
client->hledict = &SVHL_Edict[client-svs.clients+1];
|
|
result = SVHL_GameFuncs.ClientConnect(client->hledict, client->name, ipadr, rejectmessage);
|
|
bi_end();
|
|
sv.skipbprintclient = NULL;
|
|
|
|
return result;
|
|
}
|
|
|
|
void SVHL_BuildStats(client_t *client, int *si, float *sf, char **ss)
|
|
{
|
|
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
|
|
|
|
si[STAT_HEALTH] = ed->v.health;
|
|
si[STAT_VIEWHEIGHT] = ed->v.view_ofs[2];
|
|
si[STAT_WEAPONMODELI] = SV_ModelIndex(SVHL_Globals.stringbase+ed->v.vmodelindex);
|
|
si[STAT_ITEMS] = ed->v.weapons;
|
|
}
|
|
|
|
void SVHL_PutClientInServer(client_t *client)
|
|
{
|
|
hledict_t *ed = &SVHL_Edict[client - svs.clients + 1];
|
|
ed->isfree = false;
|
|
bi_begin();
|
|
SVHL_GameFuncs.ClientPutInServer(&SVHL_Edict[client-svs.clients+1]);
|
|
bi_end();
|
|
}
|
|
|
|
void SVHL_DropClient(client_t *drop)
|
|
{
|
|
hledict_t *ed = &SVHL_Edict[drop - svs.clients + 1];
|
|
bi_begin();
|
|
SVHL_GameFuncs.ClientDisconnect(&SVHL_Edict[drop-svs.clients+1]);
|
|
bi_end();
|
|
drop->hledict = NULL;
|
|
ed->isfree = true;
|
|
}
|
|
|
|
static void SVHL_RunCmdR(hledict_t *ed, usercmd_t *ucmd)
|
|
{
|
|
int i;
|
|
hledict_t *other;
|
|
|
|
// chop up very long commands
|
|
if (ucmd->msec > 50)
|
|
{
|
|
usercmd_t cmd = *ucmd;
|
|
|
|
cmd.msec = ucmd->msec/2;
|
|
cmd.msec_compat = floor(cmd.msec);
|
|
SVHL_RunCmdR (ed, &cmd);
|
|
cmd.msec_compat = ucmd->msec - cmd.msec_compat;
|
|
cmd.impulse = 0;
|
|
SVHL_RunCmdR (ed, &cmd);
|
|
return;
|
|
}
|
|
|
|
host_frametime = ucmd->msec * 0.001;
|
|
host_frametime *= sv.gamespeed;
|
|
if (host_frametime > 0.1)
|
|
host_frametime = 0.1;
|
|
|
|
pmove.cmd = *ucmd;
|
|
switch(ed->v.movetype)
|
|
{
|
|
default:
|
|
case MOVETYPE_WALK:
|
|
pmove.pm_type = PM_NORMAL;
|
|
break;
|
|
case MOVETYPE_FLY:
|
|
pmove.pm_type = PM_FLY;
|
|
break;
|
|
case MOVETYPE_NOCLIP:
|
|
pmove.pm_type = PM_SPECTATOR;
|
|
break;
|
|
case MOVETYPE_NONE:
|
|
pmove.pm_type = PM_NONE;
|
|
break;
|
|
}
|
|
pmove.numphysent = 1;
|
|
pmove.physents[0].model = sv.world.worldmodel;
|
|
pmove.physents[0].info = 0;
|
|
pmove.skipent = -1;
|
|
pmove.onladder = false;
|
|
pmove.capsule = false;
|
|
|
|
if (ed->v.flags & (1<<24))
|
|
{
|
|
pmove.cmd.forwardmove = 0;
|
|
pmove.cmd.sidemove = 0;
|
|
pmove.cmd.upmove = 0;
|
|
}
|
|
|
|
{
|
|
hledict_t *list[256];
|
|
int count;
|
|
physent_t *pe;
|
|
vec3_t pmove_mins, pmove_maxs;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
pmove_mins[i] = pmove.origin[i] - 256;
|
|
pmove_maxs[i] = pmove.origin[i] + 256;
|
|
}
|
|
|
|
count = SVHL_AreaEdicts(pmove_mins, pmove_maxs, list, sizeof(list)/sizeof(list[0]));
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
other = list[i];
|
|
if (other == ed)
|
|
continue;
|
|
if (other->v.owner == ed)
|
|
continue;
|
|
if (other->v.flags & (1<<23)) //has monsterclip flag set, so ignore it
|
|
continue;
|
|
|
|
pe = &pmove.physents[pmove.numphysent];
|
|
switch(other->v.skin)
|
|
{
|
|
case Q1CONTENTS_EMPTY:
|
|
pe->forcecontentsmask = FTECONTENTS_EMPTY;
|
|
break;
|
|
case Q1CONTENTS_SOLID:
|
|
pe->forcecontentsmask = FTECONTENTS_SOLID;
|
|
break;
|
|
case Q1CONTENTS_WATER:
|
|
pe->forcecontentsmask = FTECONTENTS_WATER;
|
|
break;
|
|
case Q1CONTENTS_SLIME:
|
|
pe->forcecontentsmask = FTECONTENTS_SLIME;
|
|
break;
|
|
case Q1CONTENTS_LAVA:
|
|
pe->forcecontentsmask = FTECONTENTS_LAVA;
|
|
break;
|
|
case Q1CONTENTS_SKY:
|
|
pe->forcecontentsmask = FTECONTENTS_SKY;
|
|
break;
|
|
case Q1CONTENTS_LADDER:
|
|
pe->forcecontentsmask = FTECONTENTS_LADDER;
|
|
break;
|
|
default:
|
|
pe->forcecontentsmask = 0;
|
|
break;
|
|
}
|
|
|
|
if (other->v.solid == SOLID_NOT || other->v.solid == SOLID_TRIGGER)
|
|
{
|
|
if (!pe->forcecontentsmask)
|
|
continue;
|
|
pe->nonsolid = true;
|
|
}
|
|
else
|
|
pe->nonsolid = false;
|
|
|
|
if (other->v.modelindex)
|
|
{
|
|
pe->model = sv.models[other->v.modelindex];
|
|
if (pe->model && pe->model->type != mod_brush)
|
|
pe->model = NULL;
|
|
}
|
|
else
|
|
pe->model = NULL;
|
|
pmove.numphysent++;
|
|
pe->info = other - SVHL_Edict;
|
|
VectorCopy(other->v.origin, pe->origin);
|
|
VectorCopy(other->v.mins, pe->mins);
|
|
VectorCopy(other->v.maxs, pe->maxs);
|
|
VectorCopy(other->v.angles, pe->angles);
|
|
}
|
|
}
|
|
|
|
VectorCopy(ed->v.mins, pmove.player_mins);
|
|
VectorCopy(ed->v.maxs, pmove.player_maxs);
|
|
|
|
VectorCopy(ed->v.origin, pmove.origin);
|
|
VectorCopy(ed->v.velocity, pmove.velocity);
|
|
if (ed->v.flags & (1<<22))
|
|
{
|
|
VectorCopy(ed->v.basevelocity, pmove.basevelocity);
|
|
}
|
|
else
|
|
VectorClear(pmove.basevelocity);
|
|
|
|
PM_PlayerMove(sv.gamespeed);
|
|
|
|
VectorCopy(pmove.origin, ed->v.origin);
|
|
VectorCopy(pmove.velocity, ed->v.velocity);
|
|
|
|
if (pmove.onground)
|
|
{
|
|
ed->v.flags |= FL_ONGROUND;
|
|
ed->v.groundentity = &SVHL_Edict[pmove.physents[pmove.groundent].info];
|
|
}
|
|
else
|
|
ed->v.flags &= ~FL_ONGROUND;
|
|
|
|
for (i = 0; i < pmove.numtouch; i++)
|
|
{
|
|
other = &SVHL_Edict[pmove.physents[pmove.touchindex[i]].info];
|
|
SVHL_GameFuncs.DispatchTouch(other, ed);
|
|
}
|
|
|
|
SVHL_LinkEdict(ed, true);
|
|
}
|
|
|
|
void SVHL_RunCmd(client_t *cl, usercmd_t *ucmd)
|
|
{
|
|
hledict_t *ed = &SVHL_Edict[cl - svs.clients + 1];
|
|
|
|
#if HALFLIFE_API_VERSION >= 140
|
|
ed->v.buttons = ucmd->buttons;
|
|
#else
|
|
//assume they're not running halflife cgame
|
|
ed->v.buttons = 0;
|
|
|
|
if (ucmd->buttons & 1)
|
|
ed->v.buttons |= (1<<0); //shoot
|
|
if (ucmd->buttons & 2)
|
|
ed->v.buttons |= (1<<1); //jump
|
|
if (ucmd->buttons & 8)
|
|
ed->v.buttons |= (1<<2); //duck
|
|
if (ucmd->forwardmove > 0)
|
|
ed->v.buttons |= (1<<3); //forward
|
|
if (ucmd->forwardmove < 0)
|
|
ed->v.buttons |= (1<<4); //back
|
|
if (ucmd->buttons & 4)
|
|
ed->v.buttons |= (1<<5); //use
|
|
//ed->v.buttons |= (1<<6); //cancel
|
|
//ed->v.buttons |= (1<<7); //turn left
|
|
//ed->v.buttons |= (1<<8); //turn right
|
|
if (ucmd->sidemove > 0)
|
|
ed->v.buttons |= (1<<9); //move left
|
|
if (ucmd->sidemove < 0)
|
|
ed->v.buttons |= (1<<10); //move right
|
|
//ed->v.buttons |= (1<<11); //shoot2
|
|
//ed->v.buttons |= (1<<12); //run
|
|
if (ucmd->buttons & 16)
|
|
ed->v.buttons |= (1<<13); //reload
|
|
//ed->v.buttons |= (1<<14); //alt1
|
|
//ed->v.buttons |= (1<<15); //alt2
|
|
#endif
|
|
|
|
if (ucmd->impulse)
|
|
ed->v.impulse = ucmd->impulse;
|
|
ed->v.v_angle[0] = SHORT2ANGLE(ucmd->angles[0]);
|
|
ed->v.v_angle[1] = SHORT2ANGLE(ucmd->angles[1]);
|
|
ed->v.v_angle[2] = SHORT2ANGLE(ucmd->angles[2]);
|
|
|
|
ed->v.angles[0] = 0;
|
|
ed->v.angles[1] = SHORT2ANGLE(ucmd->angles[1]);
|
|
ed->v.angles[2] = SHORT2ANGLE(ucmd->angles[2]);
|
|
|
|
if (IS_NAN(ed->v.velocity[0]) || IS_NAN(ed->v.velocity[1]) || IS_NAN(ed->v.velocity[2]))
|
|
VectorClear(ed->v.velocity);
|
|
|
|
bi_begin();
|
|
SVHL_GameFuncs.PlayerPreThink(ed);
|
|
SVHL_RunCmdR(ed, ucmd);
|
|
|
|
if (ed->v.nextthink && ed->v.nextthink < sv.time)
|
|
{
|
|
ed->v.nextthink = 0;
|
|
SVHL_GameFuncs.DispatchThink(ed);
|
|
}
|
|
|
|
SVHL_GameFuncs.PlayerPostThink(ed);
|
|
|
|
bi_end();
|
|
}
|
|
|
|
|
|
void SVHL_RunPlayerCommand(client_t *cl, usercmd_t *oldest, usercmd_t *oldcmd, usercmd_t *newcmd)
|
|
{
|
|
hledict_t *e = &SVHL_Edict[cl - svs.clients + 1];
|
|
|
|
SVHL_Globals.time = sv.time;
|
|
if (net_drop < 20)
|
|
{
|
|
while (net_drop > 2)
|
|
{
|
|
SVHL_RunCmd (cl, &cl->lastcmd);
|
|
net_drop--;
|
|
}
|
|
if (net_drop > 1)
|
|
SVHL_RunCmd (cl, oldest);
|
|
if (net_drop > 0)
|
|
SVHL_RunCmd (cl, oldcmd);
|
|
}
|
|
SVHL_RunCmd (cl, newcmd);
|
|
}
|
|
|
|
void SVHL_Snapshot_Build(client_t *client, packet_entities_t *pack, qbyte *pvs, edict_t *clent, qboolean ignorepvs)
|
|
{
|
|
hledict_t *e;
|
|
entity_state_t *s;
|
|
int i;
|
|
|
|
pack->servertime = sv.time;
|
|
pack->num_entities = 0;
|
|
|
|
for (i = 1; i < MAX_HL_EDICTS; i++)
|
|
{
|
|
e = &SVHL_Edict[i];
|
|
if (!e)
|
|
break;
|
|
if (e->isfree)
|
|
continue;
|
|
|
|
if (!e->v.modelindex || !e->v.model)
|
|
continue;
|
|
if (e->v.effects & 128)
|
|
continue;
|
|
|
|
if (pack->num_entities == pack->max_entities)
|
|
break;
|
|
|
|
s = &pack->entities[pack->num_entities++];
|
|
|
|
s->number = i;
|
|
s->modelindex = e->v.modelindex;
|
|
s->frame = e->v.sequence1;
|
|
s->effects = e->v.effects & 0x0f;
|
|
s->dpflags = 0;
|
|
s->skinnum = e->v.skin;
|
|
s->scale = 16;
|
|
s->trans = 255;
|
|
s->colormod[0] = nullentitystate.colormod[0];
|
|
s->colormod[1] = nullentitystate.colormod[1];
|
|
s->colormod[2] = nullentitystate.colormod[2];
|
|
VectorCopy(e->v.angles, s->angles);
|
|
VectorCopy(e->v.origin, s->origin);
|
|
|
|
if (!e->v.velocity[0] && !e->v.velocity[1] && !e->v.velocity[2])
|
|
s->dpflags |= RENDER_STEP;
|
|
|
|
s->trans = e->v.renderamt*255;
|
|
switch (e->v.rendermode)
|
|
{
|
|
case 0:
|
|
s->trans = 255;
|
|
break;
|
|
case 1: //used on laser beams, apparently. disables textures or something.
|
|
break;
|
|
case 2: //transparent windows.
|
|
break;
|
|
case 3: //used on coronarey sprites. both additive and resizing to give constant distance
|
|
s->effects |= NQEF_ADDITIVE;
|
|
break;
|
|
case 4: //used on fence textures, apparently. we already deal with these clientside.
|
|
s->trans = 255;
|
|
break;
|
|
case 5: //used on the torch at the start.
|
|
s->effects |= NQEF_ADDITIVE;
|
|
break;
|
|
default:
|
|
Con_Printf("Rendermode %s %i\n", SVHL_Globals.stringbase+e->v.model, e->v.rendermode);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
qbyte *SVHL_Snapshot_SetupPVS(client_t *client, qbyte *pvs, unsigned int pvsbufsize)
|
|
{
|
|
vec3_t org;
|
|
|
|
if (client->hledict)
|
|
{
|
|
VectorAdd (client->hledict->v.origin, client->hledict->v.view_ofs, org);
|
|
sv.world.worldmodel->funcs.FatPVS(sv.world.worldmodel, org, pvs, pvsbufsize, false);
|
|
}
|
|
|
|
return pvs;
|
|
}
|
|
|
|
#endif
|