1
0
Fork 0
forked from fte/fteqw

add support for ipv6 scope ids (so you can use the correct link-local addresses).

fix portal rendering with non-identity model matricies. add portal csg behaviour (with nq physics at least).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4686 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2014-06-16 16:21:28 +00:00
parent a4db77b22f
commit b026bbacfe
16 changed files with 423 additions and 338 deletions

View file

@ -1471,7 +1471,7 @@ static void Alias_GLDrawSkeletalBones(galiasbone_t *bones, float *bonepose, int
{
int i;
int p;
vec3_t org, dest;
// vec3_t org, dest;
qglBegin(GL_LINES);
qglColor3f(0, 0, 1);
@ -3796,33 +3796,22 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res
#ifdef SKELETALMODELS
if (inf->numbones)
{
galiasbone_t *bone;
galiasgroup_t *g1, *g2;
galiasbone_t *bone = inf->ofsbones;
float tempmatrix[12]; //flipped between this and bonematrix
float *matrix; //the matrix for a single bone in a single pose.
float m[12]; //combined interpolated version of 'matrix'.
int b, k; //counters
float *pose[4]; //the per-bone matricies (one for each pose)
float plerp[4]; //the ammount of that pose to use (must combine to 1)
int numposes = 0;
int frame1, frame2;
float f1time, f2time;
float f2ness;
#ifdef warningmsg
#pragma warningmsg("fixme: no baseframe info")
#endif
int numbonegroups = 0;
skellerps_t lerps[FS_COUNT], *lerp;
if (tagnum <= 0 || tagnum > inf->numbones)
return false;
tagnum--; //tagnum 0 is 'use my angles/org'
bone = inf->ofsbones;
if (fstate->bonestate)
//data comes from skeletal object, if possible
if (!numbonegroups && fstate->bonestate)
{
if (tagnum >= fstate->bonecount)
return false;
@ -3833,91 +3822,32 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res
return true;
}
pose[0] = fstate->bonestate;
plerp[0] = 1;
numposes = 1;
lerps[0].pose[0] = fstate->bonestate;
lerps[0].frac[0] = 1;
lerps[0].lerpcount = 1;
lerps[0].firstbone = 0;
lerps[0].endbone = fstate->bonecount;
numbonegroups = 1;
}
else
{
frame1 = fstate->g[FS_REG].frame[0];
frame2 = fstate->g[FS_REG].frame[1];
f1time = fstate->g[FS_REG].frametime[0];
f2time = fstate->g[FS_REG].frametime[1];
f2ness = fstate->g[FS_REG].lerpfrac;
if (frame1 < 0 || frame1 >= inf->groups)
//try getting the data from the frame state
if (!numbonegroups)
numbonegroups = Alias_FindRawSkelData(inf, fstate, lerps, 0, inf->numbones);
//try base pose?
if (!numbonegroups && inf->baseframeofs)
{
lerps[0].pose[0] = inf->baseframeofs;
lerps[0].frac[0] = 1;
lerps[0].lerpcount = 1;
lerps[0].firstbone = 0;
lerps[0].endbone = inf->numbones;
numbonegroups = 1;
}
//make sure it was all okay.
if (!numbonegroups || tagnum >= lerps[numbonegroups-1].endbone)
return false;
if (frame2 < 0 || frame2 >= inf->groups)
{
f2ness = 0;
frame2 = frame1;
}
//the higher level merges old/new anims, but we still need to blend between automated frame-groups.
g1 = &inf->groupofs[frame1];
g2 = &inf->groupofs[frame2];
if (f2ness != 1)
{
f1time *= g1->rate;
if (g1->loop)
{
frame1 = (int)f1time%g1->numposes;
frame2 = ((int)f1time+1)%g1->numposes;
f1time = f1time - (int)f1time;
}
else
{
frame1 = (int)f1time;
frame2 = ((int)f1time+1);
f1time = f1time - (int)f1time;
if (frame2 >= g1->numposes)
{
frame1 = frame2 = g1->numposes-1;
f1time = 0;
}
}
pose[numposes] = g1->boneofs + inf->numbones*12*frame1;
plerp[numposes] = (1-f1time) * (1-f2ness);
numposes++;
if (frame1 != frame2)
{
pose[numposes] = g1->boneofs + inf->numbones*12*frame2;
plerp[numposes] = f1time * (1-f2ness);
numposes++;
}
}
if (f2ness)
{
f2time *= g2->rate;
if (g2->loop)
{
frame1 = (int)f2time%g2->numposes;
frame2 = ((int)f2time+1)%g2->numposes;
f2time = f2time - (int)f2time;
}
else
{
frame1 = (int)f2time;
frame2 = ((int)f2time+1);
f2time = f2time - (int)f2time;
if (frame2 >= g2->numposes)
{
frame1 = frame2 = g2->numposes-1;
f2time = 0;
}
}
pose[numposes] = g2->boneofs + inf->numbones*12*frame1;
plerp[numposes] = (1-f2time) * f2ness;
numposes++;
if (frame1 != frame2)
{
pose[numposes] = g2->boneofs + inf->numbones*12*frame2;
plerp[numposes] = f2time * f2ness;
numposes++;
}
}
}
//set up the identity matrix
for (k = 0;k < 12;k++)
@ -3927,15 +3857,23 @@ qboolean Mod_GetTag(model_t *model, int tagnum, framestate_t *fstate, float *res
result[10] = 1;
while(tagnum >= 0)
{
for (lerp = lerps; tagnum < lerp->endbone; lerp++)
;
//set up the per-bone transform matrix
matrix = lerp->pose[0] + tagnum*12;
for (k = 0;k < 12;k++)
m[k] = 0;
for (b = 0;b < numposes;b++)
m[k] = matrix[k] * lerp->frac[0];
for (b = 1;b < lerp->lerpcount;b++)
{
matrix = pose[b] + tagnum*12;
matrix = lerp->pose[b] + tagnum*12;
for (k = 0;k < 12;k++)
m[k] += matrix[k] * plerp[b];
m[k] += matrix[k] * lerp->frac[b];
}
if (lerp->skeltype == SKEL_ABSOLUTE)
{
memcpy(result, m, sizeof(tempmatrix));
return true;
}
memcpy(tempmatrix, result, sizeof(tempmatrix));

View file

@ -2761,7 +2761,6 @@ void FS_ReloadPackFilesFlags(unsigned int reloadflags)
if (fs_refnames && fs_refcrcs)
{ //q3 is annoying as hell
int r;
int crc2;
char *rc = fs_refcrcs;
char *rn = fs_refnames;

View file

@ -54,7 +54,8 @@ typedef struct
} address;
unsigned short port;
unsigned short connum;
unsigned short connum; //which quake connection/socket the address is talking about. 1-based. 0 is unspecified.
unsigned int scopeid; //ipv6 interface id thing.
} netadr_t;
struct sockaddr_qstorage

View file

@ -83,7 +83,7 @@ fragmentation works like IP, offset and morefrags. offset is *8 (decode: (offset
int net_drop;
cvar_t showpackets = SCVAR("showpackets", "0");
cvar_t showdrop = SCVAR("showdrop", "0");
cvar_t qport = SCVAR("qport", "0");
cvar_t qport = CVARF("qport", "0", CVAR_NOSAVE);
cvar_t net_mtu = CVARD("net_mtu", "1440", "Specifies a maximum udp payload size, above which packets will be fragmented. If routers all worked properly this could be some massive value, and some massive value may work really nicely for lans. Use smaller values than the default if you're connecting through nested tunnels through routers that fail with IP fragmentation.");
cvar_t net_compress = CVARD("net_compress", "0", "Enables huffman compression of network packets.");

View file

@ -154,6 +154,7 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s)
memcpy(&((struct sockaddr_in6*)s)->sin6_addr, a->address.ip6, sizeof(struct in6_addr));
((struct sockaddr_in6*)s)->sin6_port = a->port;
((struct sockaddr_in6 *)s)->sin6_scope_id = a->scopeid;
return sizeof(struct sockaddr_in6);
#endif
#ifdef USEIPX
@ -202,6 +203,7 @@ void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a)
a->type = NA_IPV6;
memcpy(&a->address.ip6, &((struct sockaddr_in6 *)s)->sin6_addr, sizeof(a->address.ip6));
a->port = ((struct sockaddr_in6 *)s)->sin6_port;
a->scopeid = ((struct sockaddr_in6 *)s)->sin6_scope_id;
break;
#endif
#ifdef USEIPX
@ -544,6 +546,13 @@ char *NET_AdrToString (char *s, int len, netadr_t *a)
}
}
if (a->scopeid)
{
snprintf (p, len-strlen(s), "%%%u",
a->scopeid);
p += strlen(p);
}
if (a->port)
snprintf (p, len-strlen(s), "]:%i",
ntohs(a->port));

View file

@ -452,49 +452,17 @@ void QCBUILTIN PF_getsurfacetexture(pubprogfuncs_t *prinst, struct globalvars_s
surf = &model->surfaces[surfnum];
G_INT(OFS_RETURN) = PR_TempString(prinst, surf->texinfo->texture->name);
}
// #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)
void QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
#define TriangleNormal(a,b,c,n) ( \
(n)[0] = ((a)[1] - (b)[1]) * ((c)[2] - (b)[2]) - ((a)[2] - (b)[2]) * ((c)[1] - (b)[1]), \
(n)[1] = ((a)[2] - (b)[2]) * ((c)[0] - (b)[0]) - ((a)[0] - (b)[0]) * ((c)[2] - (b)[2]), \
(n)[2] = ((a)[0] - (b)[0]) * ((c)[1] - (b)[1]) - ((a)[1] - (b)[1]) * ((c)[0] - (b)[0]) \
)
model_t *model;
wedict_t *ent;
msurface_t *surf;
int i;
float *point;
vec3_t edgedir;
vec3_t edgenormal;
vec3_t cpoint, temp;
static float getsurface_clippointpoly(model_t *model, msurface_t *surf, vec3_t point, vec3_t bestcpoint, float bestdist)
{
int e, edge;
vec3_t edgedir, edgenormal, cpoint, temp;
mvertex_t *v1, *v2;
int edge;
int e;
float bestdist = 0x7fffffff, dist;
int bestsurf = -1;
world_t *w = prinst->parms->user;
ent = G_WEDICT(prinst, OFS_PARM0);
point = G_VECTOR(OFS_PARM1);
G_FLOAT(OFS_RETURN) = -1;
model = w->Get_CModel(w, ent->v->modelindex);
if (!model || model->type != mod_brush)
return;
if (model->fromgame == fg_quake)
{
//all polies, we can skip parts. special case.
surf = model->surfaces + model->firstmodelsurface;
for (i = 0; i < model->nummodelsurfaces; i++, surf++)
{
dist = DotProduct(point, surf->plane->normal) - surf->plane->dist;
float dist = DotProduct(point, surf->plane->normal) - surf->plane->dist;
//don't care about SURF_PLANEBACK, the maths works out the same.
if (dist*dist < bestdist)
@ -532,47 +500,20 @@ void QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_
dist = DotProduct(temp, temp);
if (dist < bestdist)
{
bestsurf = i;
bestdist = dist;
VectorCopy(cpoint, bestcpoint);
}
}
return bestdist;
}
}
else
static float getsurface_clippointtri(model_t *model, msurface_t *surf, vec3_t point, vec3_t bestcpoint, float bestdist)
{
int j;
float *v1, *v2;
vec3_t trinorm;
//if performance is needed, I suppose we could try walking bsp nodes a bit
surf = model->surfaces + model->firstmodelsurface;
for (i = 0; i < model->nummodelsurfaces; i++, surf++)
{
mesh_t *mesh = surf->mesh;
/* vec3_t mins, maxs;
//calc the surface bounds
ClearBounds(mins, maxs);
for (j = 0; j < mesh->numvertexes; j++)
AddPointToBounds(mesh->xyz_array[j], mins, maxs);
//clip the point to within those bounds
for (j = 0; j < 3; j++)
{
if (cpoint[j] < mins[j])
cpoint[j] = mins[j];
else
cpoint[j] = point[j];
if (cpoint[j] > maxs[j])
cpoint[j] = maxs[j];
}
//if the point got clipped to too far away, we can't do much
VectorSubtract(point, cpoint, temp);
dist = DotProduct(temp, temp);
if (dist*dist > bestdist)
continue;
*/
vec3_t trinorm, edgedir, edgenormal, temp, cpoint;
float dist;
int e;
float *v1, *v2;
for (j = 0; j < mesh->numindexes; j+=3)
{
//calculate the distance from the plane
@ -606,12 +547,63 @@ void QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_
dist = DotProduct(temp, temp);
if (dist < bestdist)
{
bestsurf = i;
bestdist = dist;
//can't break, one of the other tris might be closer.
VectorCopy(cpoint, bestcpoint);
}
}
}
return bestdist;
}
// #438 float(entity e, vector p) getsurfacenearpoint (DP_QC_GETSURFACE)
void QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
model_t *model;
wedict_t *ent;
msurface_t *surf;
int i;
float *point;
vec3_t cpoint = {0,0,0};
float bestdist = 0x7fffffff, dist;
int bestsurf = -1;
world_t *w = prinst->parms->user;
ent = G_WEDICT(prinst, OFS_PARM0);
point = G_VECTOR(OFS_PARM1);
G_FLOAT(OFS_RETURN) = -1;
model = w->Get_CModel(w, ent->v->modelindex);
if (!model || model->type != mod_brush)
return;
if (model->fromgame == fg_quake)
{
//all polies, we can skip parts. special case.
surf = model->surfaces + model->firstmodelsurface;
for (i = 0; i < model->nummodelsurfaces; i++, surf++)
{
dist = getsurface_clippointpoly(model, surf, point, cpoint, bestdist);
if (dist < bestdist)
{
bestdist = dist;
bestsurf = i;
}
}
}
else
{
//if performance is needed, I suppose we could try walking bsp nodes a bit
surf = model->surfaces + model->firstmodelsurface;
for (i = 0; i < model->nummodelsurfaces; i++, surf++)
{
dist = getsurface_clippointtri(model, surf, point, cpoint, bestdist);
if (dist < bestdist)
{
bestdist = dist;
bestsurf = i;
}
}
}
G_FLOAT(OFS_RETURN) = bestsurf;
@ -620,7 +612,40 @@ void QCBUILTIN PF_getsurfacenearpoint(pubprogfuncs_t *prinst, struct globalvars_
// #439 vector(entity e, float s, vector p) getsurfaceclippedpoint (DP_QC_GETSURFACE)
void QCBUILTIN PF_getsurfaceclippedpoint(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
Con_Printf("PF_getsurfaceclippedpoint not implemented\n");
model_t *model;
wedict_t *ent;
msurface_t *surf;
float *point;
unsigned int surfnum;
world_t *w = prinst->parms->user;
float *result = G_VECTOR(OFS_RETURN);
ent = G_WEDICT(prinst, OFS_PARM0);
surfnum = G_FLOAT(OFS_PARM1);
point = G_VECTOR(OFS_PARM2);
VectorCopy(point, result);
model = w->Get_CModel(w, ent->v->modelindex);
if (!model || model->type != mod_brush)
return;
if (surfnum >= model->nummodelsurfaces)
return;
if (model->fromgame == fg_quake)
{
//all polies, we can skip parts. special case.
surf = model->surfaces + model->firstmodelsurface + surfnum;
getsurface_clippointpoly(model, surf, point, result, 0x7fffffff);
}
else
{
//if performance is needed, I suppose we could try walking bsp nodes a bit
surf = model->surfaces + model->firstmodelsurface + surfnum;
getsurface_clippointtri(model, surf, point, result, 0x7fffffff);
}
}
// #628 float(entity e, float s) getsurfacenumtriangles

View file

@ -354,7 +354,6 @@ Global
{9767E236-8454-44E9-8999-CD5BDAFBE9BA}.Debug|Win32.ActiveCfg = Debug|Win32
{9767E236-8454-44E9-8999-CD5BDAFBE9BA}.Debug|x64.ActiveCfg = Debug|Win32
{9767E236-8454-44E9-8999-CD5BDAFBE9BA}.GLDebug|Win32.ActiveCfg = Debug|Win32
{9767E236-8454-44E9-8999-CD5BDAFBE9BA}.GLDebug|Win32.Build.0 = Debug|Win32
{9767E236-8454-44E9-8999-CD5BDAFBE9BA}.GLDebug|x64.ActiveCfg = Debug|Win32
{9767E236-8454-44E9-8999-CD5BDAFBE9BA}.GLRelease|Win32.ActiveCfg = Release|Win32
{9767E236-8454-44E9-8999-CD5BDAFBE9BA}.GLRelease|x64.ActiveCfg = Release|Win32
@ -389,7 +388,6 @@ Global
{72269FEE-293D-40BC-A7AE-E429F4496869}.Debug|Win32.Build.0 = Debug|Win32
{72269FEE-293D-40BC-A7AE-E429F4496869}.Debug|x64.ActiveCfg = Debug|Win32
{72269FEE-293D-40BC-A7AE-E429F4496869}.GLDebug|Win32.ActiveCfg = Debug|Win32
{72269FEE-293D-40BC-A7AE-E429F4496869}.GLDebug|Win32.Build.0 = Debug|Win32
{72269FEE-293D-40BC-A7AE-E429F4496869}.GLDebug|x64.ActiveCfg = Debug|x64
{72269FEE-293D-40BC-A7AE-E429F4496869}.GLRelease|Win32.ActiveCfg = Release|Win32
{72269FEE-293D-40BC-A7AE-E429F4496869}.GLRelease|Win32.Build.0 = Release|Win32
@ -545,7 +543,6 @@ Global
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug|x64.ActiveCfg = GLDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.Debug|x64.Build.0 = GLDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.GLDebug|Win32.ActiveCfg = GLDebug|Win32
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.GLDebug|Win32.Build.0 = GLDebug|Win32
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.GLDebug|x64.ActiveCfg = GLDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.GLDebug|x64.Build.0 = GLDebug|x64
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1365}.GLRelease|Win32.ActiveCfg = GLRelease|Win32

View file

@ -816,14 +816,13 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2],
{
entity_t *view;
// GLdouble glplane[4];
plane_t plane;
plane_t plane, oplane;
float vmat[16];
refdef_t oldrefdef;
vec3_t r;
int i;
mesh_t *mesh = batch->mesh[batch->firstmesh];
qbyte newvis[(MAX_MAP_LEAFS+7)/8];
plane_t oplane;
float ivmat[16], trmat[16];
if (r_refdef.recurse >= R_MAX_RECURSE-1)
@ -837,7 +836,28 @@ void GLR_DrawPortal(batch_t *batch, batch_t **blist, batch_t *depthmasklist[2],
{
VectorCopy(mesh->normals_array[0], plane.normal);
}
if (batch->ent == &r_worldentity)
{
plane.dist = DotProduct(mesh->xyz_array[0], plane.normal);
}
else
{
vec3_t point, vel;
VectorCopy(plane.normal, oplane.normal);
//rotate the surface normal around its entity's matrix
plane.normal[0] = oplane.normal[0]*batch->ent->axis[0][0] + oplane.normal[1]*batch->ent->axis[1][0] + oplane.normal[2]*batch->ent->axis[2][0];
plane.normal[1] = oplane.normal[0]*batch->ent->axis[0][1] + oplane.normal[1]*batch->ent->axis[1][1] + oplane.normal[2]*batch->ent->axis[2][1];
plane.normal[2] = oplane.normal[0]*batch->ent->axis[0][2] + oplane.normal[1]*batch->ent->axis[1][2] + oplane.normal[2]*batch->ent->axis[2][2];
//rotate some point on the mesh around its entity's matrix
point[0] = mesh->xyz_array[0][0]*batch->ent->axis[0][0] + mesh->xyz_array[0][1]*batch->ent->axis[1][0] + mesh->xyz_array[0][2]*batch->ent->axis[2][0] + batch->ent->origin[0];
point[1] = mesh->xyz_array[0][0]*batch->ent->axis[0][1] + mesh->xyz_array[0][1]*batch->ent->axis[1][1] + mesh->xyz_array[0][2]*batch->ent->axis[2][1] + batch->ent->origin[1];
point[2] = mesh->xyz_array[0][0]*batch->ent->axis[0][2] + mesh->xyz_array[0][1]*batch->ent->axis[1][2] + mesh->xyz_array[0][2]*batch->ent->axis[2][2] + batch->ent->origin[2];
//now we can figure out the plane dist
plane.dist = DotProduct(point, plane.normal);
}
//if we're too far away from the surface, don't draw anything
if (batch->shader->flags & SHADER_AGEN_PORTAL)

View file

@ -902,19 +902,25 @@ char *ASMCALL PR_StringToNative (pubprogfuncs_t *ppf, string_t str)
{
int i = str & ~STRING_SPECMASK;
if (i >= prinst.numallocedstrings)
{
if (!progfuncs->funcs.pr_trace)
{
printf("invalid string %x\n", str);
PR_StackTrace(&progfuncs->funcs, false);
progfuncs->funcs.pr_trace = 1;
PR_StackTrace(&progfuncs->funcs, false);
}
return "";
}
if (prinst.allocedstrings[i])
return prinst.allocedstrings[i];
else
{
if (!progfuncs->funcs.pr_trace)
{
printf("invalid string %x\n", str);
PR_StackTrace(&progfuncs->funcs, false);
progfuncs->funcs.pr_trace = 1;
PR_StackTrace(&progfuncs->funcs, false);
}
return ""; //urm, was freed...
}
}
@ -922,20 +928,26 @@ char *ASMCALL PR_StringToNative (pubprogfuncs_t *ppf, string_t str)
{
int i = str & ~STRING_SPECMASK;
if (i >= prinst.numtempstrings)
{
if (!progfuncs->funcs.pr_trace)
{
printf("invalid temp string %x\n", str);
PR_StackTrace(&progfuncs->funcs, false);
progfuncs->funcs.pr_trace = 1;
PR_StackTrace(&progfuncs->funcs, false);
}
return "";
}
return prinst.tempstrings[i];
}
if ((unsigned int)str >= (unsigned int)prinst.addressableused)
{
if (!progfuncs->funcs.pr_trace)
{
printf("invalid string offset %x\n", str);
PR_StackTrace(&progfuncs->funcs, false);
progfuncs->funcs.pr_trace = 1;
PR_StackTrace(&progfuncs->funcs, false);
}
return "";
}
return progfuncs->funcs.stringtable + str;

View file

@ -8821,7 +8821,6 @@ qboolean SV_RunFullQCMovement(client_t *client, usercmd_t *ucmd)
(pr_global_struct->input_movevalues)[1] = ucmd->sidemove;
(pr_global_struct->input_movevalues)[2] = ucmd->upmove;
pr_global_struct->input_buttons = ucmd->buttons;
// pr_global_struct->input_impulse = ucmd->impulse;
pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict);
PR_ExecuteProgram(svprogfuncs, gfuncs.RunClientCommand);
@ -9547,7 +9546,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"getsurfacenormal",PF_getsurfacenormal,0, 0, 0, 436, "vector(entity e, float s)"},// (DP_QC_GETSURFACE)
{"getsurfacetexture",PF_getsurfacetexture,0, 0, 0, 437, "string(entity e, float s)"},// (DP_QC_GETSURFACE)
{"getsurfacenearpoint",PF_getsurfacenearpoint,0,0, 0, 438, "float(entity e, vector p)"},// (DP_QC_GETSURFACE)
{"getsurfaceclippedpoint",PF_getsurfaceclippedpoint,0,0,0, 439, "vector(entity e, float s, vector p)" STUB},// (DP_QC_GETSURFACE)
{"getsurfaceclippedpoint",PF_getsurfaceclippedpoint,0,0,0, 439, "vector(entity e, float s, vector p)"},// (DP_QC_GETSURFACE)
#ifndef SERVERONLY
//begin menu-only
@ -10243,6 +10242,11 @@ void PR_DumpPlatform_f(void)
{"end_sys_fields", "void", QW|NQ|CS|MENU},
{"time", "float", MENU, "The current local time. Increases while paused."},
{"input_timelength", "float", QW|NQ},
{"input_angles", "vector", QW|NQ},
{"input_movevalues", "vector", QW|NQ},
{"input_buttons", "float", QW|NQ},
{"input_impulse", "float", QW|NQ},
#define comfieldfloat(name,desc) {#name, ".float", FL, desc},
#define comfieldvector(name,desc) {#name, ".vector", FL, desc},

View file

@ -109,31 +109,31 @@ and the extension fields are added on the end and can have extra vm-specific stu
*/
/*DO NOT ADD TO THIS STRUCTURE (base-qw-compat for q1qvm)*/
#define comqcfields \
comfieldfloat(modelindex,NULL)\
comfieldvector(absmin,NULL)\
comfieldvector(absmax,NULL)\
comfieldfloat(ltime,NULL)\
comfieldfloat(lastruntime,NULL) /*type doesn't match the qc, we use a hidden double instead. this is dead.*/ \
comfieldfloat(movetype,NULL)\
comfieldfloat(solid,NULL)\
comfieldvector(origin,NULL)\
comfieldvector(oldorigin,NULL)\
comfieldvector(velocity,NULL)\
comfieldvector(angles,NULL)\
comfieldvector(avelocity,NULL)\
comfieldstring(classname,NULL)\
comfieldstring(model,NULL)\
comfieldfloat(frame,NULL)\
comfieldfloat(skin,NULL)\
comfieldfloat(effects,NULL)\
comfieldvector(mins,NULL)\
comfieldvector(maxs,NULL)\
comfieldvector(size,NULL)\
comfieldfloat(modelindex,"This is the model precache index for the model that was set on the entity, instead of having to look up the model according to the .model field. Use setmodel to change it.")\
comfieldvector(absmin,"Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates.")\
comfieldvector(absmax,"Set by the engine when the entity is relinked (by setorigin, setsize, or setmodel). This is in world coordinates.")\
comfieldfloat(ltime,"On MOVETYPE_PUSH entities, this is used as an alternative to the 'time' global, and .nextthink is synced to this instead of time. This allows time to effectively freeze if the entity is blocked, ensuring the think happens when the entity reaches the target point instead of randomly.")\
comfieldfloat(lastruntime,"This field used to be used to avoid running an entity multiple times in a single frame due to quakeworld's out-of-order thinks. It is no longer used by FTE due to precision issues, but may still be updated for compatibility reasons.") /*type doesn't match the qc, we use a hidden double instead. this is dead.*/ \
comfieldfloat(movetype,"Describes how the entity moves. One of the MOVETYPE_ constants.")\
comfieldfloat(solid,"Describes whether the entity is solid or not, and any special properties infered by that. Must be one of the SOLID_ constants")\
comfieldvector(origin,"The current location of the entity in world space. Inline bsp entities (ie: ones placed by a mapper) will typically have a value of '0 0 0' in their neutral pose, as the geometry is offset from that. It is the reference point of the entity rather than the center of its geometry, for non-bsp models, this is often not a significant distinction.")\
comfieldvector(oldorigin,"This is often used on players to reset the player back to where they were last frame if they somehow got stuck inside something due to fpu precision. Never change a player's oldorigin field to inside a solid, because that might cause them to become pemanently stuck.")\
comfieldvector(velocity,"The direction and speed that the entity is moving in world space.")\
comfieldvector(angles,"The eular angles the entity is facing in, in pitch, yaw, roll order. Note that non-bsp models use a negated pitch due to a widely-proliferated-and-thus-unfixable legacy bug.")\
comfieldvector(avelocity,"The amount the entity's angles change by each frame. Note that this is direct eular angles, and thus the angular change is non-linear and often just looks buggy.")\
comfieldstring(classname,"Identifies the class/type of the entity. Useful for debugging, also used for loading, but its value is not otherwise significant to the engine, this leaves the mod free to set it to whatever it wants and randomly test strings for values in whatever inefficient way it chooses fit.")\
comfieldstring(model,"The model name that was set via setmodel, in theory. Often, this is cleared to null to prevent the engine from being seen by clients while not changing modelindex. This behaviour allows inline models to remain solid yet be invisible.")\
comfieldfloat(frame,"The current frame the entity is meant to be displayed in. In CSQC, note the lerpfrac and frame2 fields as well. if it specifies a framegroup, the framegroup will autoanimate in ssqc, but not in csqc.")\
comfieldfloat(skin,"The skin index to use. on a bsp entity, setting this to 1 will switch to the 'activated' texture instead. A negative value will be understood as a replacement contents value, so setting it to CONTENTS_WATER will make a movable pool of water.")\
comfieldfloat(effects,"Lots of random flags that change random effects.")\
comfieldvector(mins,"The minimum extent of the model (ie: the bottom-left coordinate relative to the entity's origin). Change via setsize. May also be changed by setmodel.")\
comfieldvector(maxs,"like mins, but in the other direction.")\
comfieldvector(size,"maxs-mins. Updated when the entity is relinked (by setorigin, setsize, setmodel)")\
comfieldfunction(touch, ".void()",NULL)\
comfieldfunction(use, ".void()",NULL)\
comfieldfunction(think, ".void()",NULL)\
comfieldfunction(blocked, ".void()",NULL)\
comfieldfloat(nextthink,NULL)\
comfieldfloat(nextthink,"The time at which the entity is next scheduled to fire its think event. For MOVETYPE_PUSH entities, this is relative to that entity's ltime field, for all other entities it is relative to the time gloal.")\
comfieldentity(groundentity,NULL)\
comfieldfloat(health,NULL)\
comfieldfloat(frags,NULL)\
@ -191,15 +191,15 @@ and the extension fields are added on the end and can have extra vm-specific stu
comfieldvector(punchangle,NULL) /*std in nq*/\
comfieldfloat(gravity,NULL) /*added in quake 1.09 (for hipnotic)*/\
comfieldfloat(hull,"Overrides the hull used by the entity for walkmove/movetogoal and not traceline/tracebox.")/*PEXT_HEXEN2*/\
comfieldentity(movechain,NULL)/*hexen2*/\
comfieldfunction(chainmoved, ".void()",NULL)/*hexen2*/\
comfieldentity(movechain,"This is a linked list of entities which will be moved whenever this entity moves, logically they are attached to this entity.")/*hexen2*/\
comfieldfunction(chainmoved, ".void()","Called when the entity is moved as a result of being part of another entity's .movechain")/*hexen2*/\
comfieldfunction(contentstransition, ".void(float old, float new)","This function is called when the entity moves between water and air. If specified, default splash sounds will be disabled allowing you to provide your own.")/*ENTITYCONTENTSTRANSITION*/\
comfieldfloat(dimension_solid,"This is the bitmask of dimensions which the entity is solid within.")/*EXT_DIMENSION_PHYSICS*/\
comfieldfloat(dimension_hit,"This is the bitmask of dimensions which the entity will be blocked by. If other.dimension_solid & self.dimension_hit, our traces will impact and not proceed. If its false, the traces will NOT impact, allowing self to pass straight through.")/*EXT_DIMENSION_PHYSICS*/\
comfieldfloat(hitcontentsmask,NULL)\
comfieldfloat(scale,NULL)/*DP_ENT_SCALE*/\
comfieldfloat(fatness,NULL)/*FTE_PEXT_FATNESS*/\
comfieldfloat(alpha,NULL)/*DP_ENT_ALPHA*/\
comfieldfloat(scale,"Multiplier that resizes the entity. 1 is normal sized, 2 is double sized. scale 0 is remapped to 1. In SSQC, this is limited to 1/16th precision, with a maximum just shy of 16.")/*DP_ENT_SCALE*/\
comfieldfloat(fatness,"How many QuakeUnits to push the entity's verticies along their normals by.")/*FTE_PEXT_FATNESS*/\
comfieldfloat(alpha,"The transparency of the entity. 1 means opaque, 0.0001 means virtually invisible. 0 is remapped to 1, for compatibility.")/*DP_ENT_ALPHA*/\
comfieldentity(tag_entity,NULL)\
comfieldfloat(tag_index,NULL)\
comfieldfloat(skeletonindex,"This object serves as a container for the skeletal bone states used to override the animation data.") /*FTE_CSQC_SKELETONOBJECTS*/\
@ -222,12 +222,12 @@ and the extension fields are added on the end and can have extra vm-specific stu
comfieldfloat(maxspeed,NULL)/*added in quake 1.09*/\
comfieldfloat(items2,NULL) /*added in quake 1.09 (for hipnotic)*/\
comfieldentity(view2,NULL)/*FTE_PEXT_VIEW2*/\
comfieldvector(movement,NULL)\
comfieldvector(movement,"These are the directions that the player is currently trying to move in (ie: which +forward/+moveright/+moveup etc buttons they have held), expressed relative to that player's angles. Order is forward, right, up.")\
comfieldfloat(vw_index,NULL)\
comfieldentity(nodrawtoclient,NULL)\
comfieldentity(drawonlytoclient,NULL)\
comfieldentity(viewmodelforclient,NULL)/*DP_ENT_VIEWMODEL*/\
comfieldentity(exteriormodeltoclient,NULL)\
comfieldentity(nodrawtoclient,"This entity will not be sent to the player named by this field. They will be invisible and not emit dlights/particles. Does not work in MVD-recorded game.")\
comfieldentity(drawonlytoclient,"This entity will be sent *only* to the player named by this field. To other players they will be invisible and not emit dlights/particles. Does not work in MVD-recorded game.")\
comfieldentity(viewmodelforclient,"This entity will be sent only to the player named by this field, and this entity will be attached to the player's view as an additional weapon model.")/*DP_ENT_VIEWMODEL*/\
comfieldentity(exteriormodeltoclient,"This entity will be invisible to the player named by this field, except in mirrors or mirror-like surfaces, where it will be visible as normal. It may still cast shadows as normal, and generate lights+particles, depending on client settings. Does not affect how other players see the entity.")\
comfieldfloat(button3,"DP_INPUTBUTTONS (note in qw, we set 1 to equal 3, to match zquake/fuhquake/mvdsv)")\
comfieldfloat(button4,NULL)\
comfieldfloat(button5,NULL)\
@ -238,10 +238,10 @@ and the extension fields are added on the end and can have extra vm-specific stu
comfieldfloat(glow_size,NULL)\
comfieldfloat(glow_color,NULL)\
comfieldfloat(glow_trail,NULL)\
comfieldfloat(traileffectnum,NULL)/*DP_ENT_TRAILEFFECTNUM*/\
comfieldvector(color,NULL)/*Hexen2 has a .float color, the warnings should be benign*/ \
comfieldfloat(light_lev,NULL)\
comfieldfloat(style,NULL)\
comfieldfloat(traileffectnum,"This should be set to the result of particleeffectnum, in order to attach a custom trail effect to an entity as it moves.")/*DP_ENT_TRAILEFFECTNUM*/\
comfieldvector(color,"This affects the colour of realtime lights that were enabled via the pflags field.")/*Hexen2 has a .float color, the warnings should be benign*/ \
comfieldfloat(light_lev,"This is the radius of an entity's light. This is not normally used by the engine, but is used for realtime lights (ones that are enabled with the pflags field).")\
comfieldfloat(style,"Used by the light util to decide how an entity's light should animate. On an entity with pflags set, this also affects realtime lights.")\
comfieldfloat(pflags,"Realtime lighting flags")\
comfieldfloat(clientcolors,NULL)\
comfieldfloat(dimension_see,"This is the dimension mask (bitfield) that the client is allowed to see. Entities and events not in this dimension mask will be invisible.")/*EXT_DIMENSION_VISIBLE*/\
@ -251,44 +251,44 @@ and the extension fields are added on the end and can have extra vm-specific stu
comfieldfloat(playerclass,NULL)/*hexen2 requirements*/\
comfieldfloat(drawflags,"Various flags that affect lighting values and scaling. Typically set to 96 in quake for proper compatibility with DP_QC_SCALE.")/*hexen2*/\
comfieldfloat(hasted,NULL)/*hexen2 uses this AS WELL as maxspeed*/\
comfieldfloat(light_level,NULL)/*hexen2's grabbing light level from client*/\
comfieldfloat(light_level,"Used by hexen2 to indicate the light level where the player is standing.")\
comfieldfloat(abslight,"Allows overriding light levels. Use drawflags to state that this field should actually be used.")/*hexen2's force a lightlevel*/\
comfieldfunction(SendEntity, ".float(entity playerent, float changedflags)",NULL)/*EXT_CSQC*/\
comfieldfloat(SendFlags,NULL)/*EXT_CSQC_1 (one of the DP guys came up with it)*/\
comfieldfunction(SendEntity, ".float(entity playerent, float changedflags)","Called by the engine whenever an entity needs to be (re)sent to a client's csprogs, either because SendFlags was set or because data was lost. Must write its data to the MSG_ENTITY buffer. Will be called at the engine's leasure.")/*EXT_CSQC*/\
comfieldfloat(SendFlags,"Indicates that something in the entity has been changed, and that it needs to be updated to all players that can see it. The engine will clear it at some point, with the cleared bits appearing in the 'changedflags' argument of the SendEntity method.")/*EXT_CSQC_1 (one of the DP guys came up with it)*/\
comfieldfloat(Version,"Obsolete, set a SendFlags bit instead.")/*EXT_CSQC (obsolete)*/\
comfieldfloat(pvsflags,"Reconfigures when the entity is visible to clients")/*EXT_CSQC_1*/\
comfieldfloat(modelflags,NULL)\
comfieldfloat(uniquespawnid,NULL)/*FTE_ENT_UNIQUESPAWNID*/\
comfieldfunction(customizeentityforclient, ".float()",NULL)
comfieldfloat(modelflags,"Used to override the flags set in the entity's model. Should be set according to the MF_ constants. Use effects|=EF_NOMODELFLAGS to ignore the model's flags completely. The traileffectnum field is more versatile.")\
comfieldfloat(uniquespawnid,"Incremented by 1 whenever the entity is respawned. Persists across remove calls, for when the two-second grace period is insufficient.")/*FTE_ENT_UNIQUESPAWNID*/\
comfieldfunction(customizeentityforclient, ".float()","Called just before an entity is sent to a client (non-csqc protocol). This gives you a chance to tailor 'self' according to what 'other' should see.")
//this is the list for all the csqc fields.
//(the #define is so the list always matches the ones pulled out)
#define csqcextfields \
comfieldfloat(entnum,NULL) \
comfieldfloat(frame2,NULL) /*EXT_CSQC_1*/\
comfieldfloat(frame1time,NULL) /*EXT_CSQC_1*/\
comfieldfloat(frame2time,NULL) /*EXT_CSQC_1*/\
comfieldfloat(lerpfrac,NULL) /*EXT_CSQC_1*/\
comfieldfloat(entnum,"This is the number of the entity that the ssqc is using.") \
comfieldfloat(frame2,"This is typically the old frame of the entity. if lerpfrac is 1, .frame will be ignored and .frame2 will be used solely. lerpfrac 0.5 will give an even 50/50 blend.") /*EXT_CSQC_1*/\
comfieldfloat(frame1time,"This controls the time into the framegroup/animation named by .frame, you should increment this value according to frametime or to distance moved, depending on the sort of animation you're attempting. You may wish to avoid incrementing this while lerpfrac is still changing, to avoid wasting parts of the animation.") /*EXT_CSQC_1*/\
comfieldfloat(frame2time,".frame2 equivelent of frame1time.") /*EXT_CSQC_1*/\
comfieldfloat(lerpfrac,"The value 0 means the entity will animate using only .frame, which will be jerky. As this value is incremented, more of frame2 will be used. If you wish to use .frame2 as the 'old' frame, it is generally recommended to start this field with the value 1, to decrement it by frametime, and when it drops below 0 add 1 to it and update .frame2 and .frame to lerp into the new frame.") /*EXT_CSQC_1*/\
comfieldfloat(renderflags,NULL)\
comfieldfloat(forceshader,"Contains a shader handle used to replace all surfaces upon the entity.")/*FTE_CSQC_SHADERS*/\
\
comfieldfloat(baseframe,NULL) /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baseframe2,NULL) /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baseframe1time,NULL) /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baseframe2time,NULL) /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baselerpfrac,NULL) /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(basebone,NULL) /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baseframe,"See basebone") /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baseframe2,"See basebone") /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baseframe1time,"See basebone") /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baseframe2time,"See basebone") /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(baselerpfrac,"See basebone") /*FTE_CSQC_BASEFRAME*/\
comfieldfloat(basebone,"The base* frame animations are equivelent to their non-base versions, except that they only affect bone numbers below the 'basebone' value. This means that the base* animation can affect the legs of a skeletal model independantly of the normal animation fields affecting the torso area. For more complex animation than this, use skeletal objects.") /*FTE_CSQC_BASEFRAME*/\
\
comfieldfloat(bonecontrol1,NULL) /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol2,NULL) /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol3,NULL) /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol4,NULL) /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol5,NULL) /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(subblendfrac,NULL) /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(basesubblendfrac,NULL) /*FTE_CSQC_HALFLIFE_MODELS+FTE_CSQC_BASEFRAME*/\
comfieldfloat(bonecontrol1,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol2,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol3,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol4,"Halflife model format bone controller. On player models, this typically affects the spine's yaw.") /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(bonecontrol5,"Halflife model format bone controller. This typically affects the mouth.") /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(subblendfrac,"Weird animation value specific to halflife models. On player models, this typically affects the spine's pitch.") /*FTE_CSQC_HALFLIFE_MODELS*/\
comfieldfloat(basesubblendfrac,"See basebone") /*FTE_CSQC_HALFLIFE_MODELS+FTE_CSQC_BASEFRAME*/\
\
comfieldfloat(drawmask,NULL) /*So that the qc can specify all rockets at once or all bannanas at once*/ \
comfieldfunction(predraw, ".float()",NULL) /*If present, is called just before it's drawn.*/
comfieldfloat(drawmask, "Matces the bitmask passed to the addentities builtin, to easily submit entities to the renderer. Not otherwise meaningful.") /*So that the qc can specify all rockets at once or all bannanas at once*/ \
comfieldfunction(predraw, ".float()","Called as part of the addentities builtin. Returns one of the PREDRAW_ constants. This gives you a chance to interpolate or animate entities as desired.") /*If present, is called just before it's drawn.*/
typedef struct stdentvars_s //standard = standard for qw
{

View file

@ -910,14 +910,18 @@ void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean us
#endif
{
char *exts[] = {"maps/%s.bsp", "maps/%s.cm", "maps/%s.hmp", NULL};
strcpy (sv.name, server);
int depth, bestdepth;
Q_strncpyz (sv.name, server, sizeof(sv.name));
Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[0], server);
if (!COM_FCheckExists(sv.modelname))
bestdepth = COM_FDepthFile(sv.modelname, false);
for (i = 1; exts[i]; i++)
{
if (COM_FCheckExists(va(exts[1], server)))
Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[1], server);
else if (COM_FCheckExists(va(exts[2], server)))
Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[2], server);
depth = COM_FDepthFile(va(exts[i], server), false);
if (depth < 0)
{
bestdepth = depth;
Q_snprintfz (sv.modelname, sizeof(sv.modelname), exts[i], server);
}
}
sv.world.worldmodel = Mod_ForName (sv.modelname, MLV_ERROR);
}

View file

@ -3074,28 +3074,17 @@ client_t *SVC_DirectConnect(void)
Con_TPrintf("%s: diff prot connect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
else
Con_TPrintf("%s:dup connect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
cl->protocol = SCP_BAD; //make sure the netchan doesn't try sending anything.
SV_DropClient(cl);
cl->protocol = protocol;
/*
nextuserid--;
return NULL;
*/
}
/*else if (cl->state == cs_zombie)
{
Con_Printf ("%s:reconnect\n", NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
//need to make sure they're really gone (free memory from this client now that we know they're not a zombie.
}*/
else
Con_TPrintf ("%s:%s:reconnect\n", sv.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
//silently drop the old connection, without causing the old client to get a disconnect or anything stupid like that.
cl->protocol = SCP_BAD;
SV_DropClient (cl);
cl->protocol = protocol;
}*/
else
{
// Con_TPrintf ("%s:%s:reconnect\n", sv.name, NET_AdrToString (adrbuf, sizeof(adrbuf), &adr));
SV_DropClient (cl);
}
break;
}
}
@ -3190,7 +3179,10 @@ client_t *SVC_DirectConnect(void)
/*single player logic*/
if (sv.allocated_client_slots == 1 && net_from.type == NA_LOOPBACK)
if (svs.clients[0].state >= cs_connected)
{
Con_Printf("Kicking %s to make space for local client\n", svs.clients[0].name);
SV_DropClient(svs.clients);
}
// if at server limits, refuse connection
if ( maxclients.ival > MAX_CLIENTS )

View file

@ -279,7 +279,7 @@ static void WPhys_PortalTransform(world_t *w, wedict_t *ent, wedict_t *portal, v
VectorCopy(G_VECTOR(OFS_RETURN), org);
VectorCopy(w->g.v_forward, ent->v->velocity);
VectorCopy(w->g.v_right, move);
VectorCopy(w->g.v_up, ent->xv->gravitydir);
// VectorCopy(w->g.v_up, ent->xv->gravitydir);
//transform the angles too

View file

@ -6970,8 +6970,12 @@ void SVNQ_ReadClientMove (usercmd_t *move)
SV_PostRunCmd();
}
else
{
if (i)
host_client->edict->v->impulse = i;
host_client->isindependant = false;
}
}
void SVNQ_ExecuteClientMessage (client_t *cl)
{

View file

@ -299,11 +299,12 @@ void Q2BSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, float *mins,
int topnode;
int i, j;
int area;
int nullarea = (model->fromgame == fg_quake2)?0:-1;
//ent->num_leafs == q2's ent->num_clusters
ent->num_leafs = 0;
ent->areanum = 0;
ent->areanum2 = 0;
ent->areanum = nullarea;
ent->areanum2 = nullarea;
if (!mins || !maxs)
return;
@ -317,7 +318,7 @@ void Q2BSP_FindTouchedLeafs(model_t *model, struct pvscache_s *ent, float *mins,
{
clusters[i] = CM_LeafCluster (model, leafs[i]);
area = CM_LeafArea (model, leafs[i]);
if (area)
if (area != nullarea)
{ // doors may legally straggle two areas,
// but nothing should ever need more than that
if (ent->areanum && ent->areanum != area)
@ -1041,6 +1042,8 @@ static trace_t World_ClipMoveToEntity (world_t *w, wedict_t *ent, vec3_t eorg, v
{
//solid_portal cares only about origins and as such has no mins/max
TransformedTrace(model, 0, ent->v->frame, start, end, vec3_origin, vec3_origin, &trace, eorg, ent->v->angles, hitcontentsmask);
if (trace.startsolid) //portals should not block traces. this prevents infinite looping
trace.startsolid = trace.allsolid = false;
hitmodel = false;
}
else if (ent->v->solid != SOLID_BSP)
@ -1362,6 +1365,74 @@ void WorldQ2_ClipMoveToEntities (world_t *w, moveclip_t *clip )
#endif
//===========================================================================
//a portal is flush with a world surface behind it.
//this causes problems. namely that we can't pass through the portal plane if the bsp behind it prevents out origin from getting through.
//so if the trace was clipped and ended infront of the portal, continue the trace to the edges of the portal cutout instead.
void World_PortalCSG(wedict_t *portal, float trminz, float trmaxz, vec3_t start, vec3_t end, trace_t *trace)
{
vec4_t planes[6]; //far, near, right, left, up, down
int plane;
vec3_t worldpos;
float portalradius = portal->v->impulse;
//only run this code if we impacted on the portal's parent.
if (trace->fraction == 1 && !trace->startsolid)
return;
if (!portalradius)
return;
if (trace->startsolid)
VectorCopy(start, worldpos); //make sure we use a sane valid position.
else
VectorCopy(trace->endpos, worldpos);
//determine the csg area. normals should be facing in
AngleVectors(portal->v->angles, planes[1], planes[3], planes[5]);
VectorNegate(planes[1], planes[0]);
VectorNegate(planes[3], planes[2]);
VectorNegate(planes[5], planes[4]);
trminz = fabs(trminz);
portalradius/=2;
planes[0][3] = DotProduct(portal->v->origin, planes[0]) - trminz-16;
planes[1][3] = DotProduct(portal->v->origin, planes[1]) - (1.0/32); //an epsilon beyond the portal
planes[2][3] = DotProduct(portal->v->origin, planes[2]) - portalradius+trminz;
planes[3][3] = DotProduct(portal->v->origin, planes[3]) - portalradius+trminz;
planes[4][3] = DotProduct(portal->v->origin, planes[4]) - portalradius+trminz;
planes[5][3] = DotProduct(portal->v->origin, planes[5]) - portalradius+trminz;
//if we're actually inside the csg region
for (plane = 0; plane < 6; plane++)
{
float d = DotProduct(worldpos, planes[plane]);
if (d - planes[plane][3] >= 0)
continue; //endpos is inside
else
return; //end is already outside
}
//yup, we're inside, the trace shouldn't end where it actually did
trace->fraction = 1;
trace->startsolid = trace->allsolid = false;
VectorInterpolate(start, 1, end, trace->endpos);
for (plane = 0; plane < 6; plane++)
{
float ds = DotProduct(start, planes[plane]) - planes[plane][3];
float de = DotProduct(end, planes[plane]) - planes[plane][3];
float frac;
if (ds > 0 && de < 0)
{
frac = (ds-(1.0/32)) / (ds - de);
if (frac < trace->fraction)
{
if (frac < 0)
frac = 0;
trace->fraction = frac;
VectorInterpolate(start, frac, end, trace->endpos);
VectorCopy(planes[plane], trace->plane.normal);
trace->plane.dist = planes[plane][3];
}
}
}
}
/*
====================
@ -1420,8 +1491,8 @@ static void World_ClipToEverything (world_t *w, moveclip_t *clip)
continue; // points never interact
// might intersect, so do an exact clip
if (clip->trace.allsolid)
return;
// if (clip->trace.allsolid)
// return;
if (clip->passedict)
{
if ((wedict_t*)PROG_TO_EDICT(w->progs, touch->v->owner) == clip->passedict)
@ -1430,6 +1501,12 @@ static void World_ClipToEverything (world_t *w, moveclip_t *clip)
continue; // don't clip against owner
}
if (touch->v->solid == SOLID_PORTAL)
{
//make sure we don't hit the world if we're inside the portal
World_PortalCSG(touch, clip->mins[2], clip->maxs[2], clip->start, clip->end, &clip->trace);
}
if ((int)touch->v->flags & FL_MONSTER)
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->hitcontentsmask);
else
@ -1531,14 +1608,17 @@ static void World_ClipToLinks (world_t *w, areanode_t *node, moveclip_t *clip)
continue; // don't clip against owner
}
if (touch->v->solid == SOLID_PORTAL)
{
//make sure we don't hit the world if we're inside the portal
World_PortalCSG(touch, clip->mins[2], clip->maxs[2], clip->start, clip->end, &clip->trace);
}
if ((int)touch->v->flags & FL_MONSTER)
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins2, clip->maxs2, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->hitcontentsmask);
else
trace = World_ClipMoveToEntity (w, touch, touch->v->origin, clip->start, clip->mins, clip->maxs, clip->end, clip->hullnum, clip->type & MOVE_HITMODEL, clip->hitcontentsmask);
if (trace.startsolid && touch->v->solid == SOLID_PORTAL)
continue;
if (trace.allsolid || trace.startsolid ||
trace.fraction < clip->trace.fraction)
{