openxr plugin: tweaked - inputs should be working properly now, and are visible to csqc. subject to further breaking changes, however.

_pext_vrinputs: added cvar to enable vr inputs protocol extension allowing vr inputs to be networked to ssqc too. defaults to 0 for now, will be renamed when deemed final.
updates menu: the prompt to enable sources is now more explicit instead of expecting the user to have a clue.
updates menu: added a v3 sources format, which should be more maintainable. not final.
updates menu: try to give reasons why sources might be failing (to help blame ISPs if they try fucking over TTH dns again).
presets menu: no longer closes the instant a preset is chosen. some presets have a couple of modifiers listed. force the demo loop in the background to serve as a preview.
prompts menus: now does word wrapping.
ftemaster: support importing server lists from other master servers (requested by Eukara).
server: try to detect when non-reply inbound packets are blocked by firewalls/nats/etc (using ftemaster to do so).
qcvm: added pointcontentsmask builtin, allowing it to probe more than just world, with fte's full contentbit range instead of just q1 legacy.
qcvm: memfill8 builtin now works on createbuffer() pointers.
qcvm: add missing unsigned ops. Fixed double comparison ops. fixed bug with op_store_i64. added missing OP_LOADP_I64
qcc: added '#pragma framerate RATE' for overriding implicit nextthink durations.
qcc: fixed '#pragma DONT_COMPILE_THIS_FILE' to not screw up comments.
qcc: added __GITURL__ __GITHASH__ __GITDATE__ __GITDATETIME__ __GITDESC__ for any mods that might want to make use of that.
qcc: fix up -Fhashonly a little
setrenderer: support for vulkan gpu enumeration.
rulesets: reworked to support custom rulesets (using hashes to catch haxxors, though still nothing prevents just changing the client to ignore rulesets)
bspx: use our BIH code for the bspx BRUSHLIST lump instead of the older less efficient code.
(static)iqm+obj: these model formats can now be used for the worldmodel (with a suitable .ent file). Also using BIH for much better collision performance.
pmove: tried to optimise PM_NudgePosition, should boost fps in stress tests.
wayland: fix a crash on startup. mousegrabs now works better.
imagetool: uses sdl for previews.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5813 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-04-14 05:21:04 +00:00
parent cd50a54a5a
commit b9cd6ec91b
160 changed files with 13078 additions and 5542 deletions

View file

@ -0,0 +1,131 @@
void(int mod, int id) DrawEngineBrushWireframe =
{
const vector col = '1 0 0';
for(int facenum = 0;;)
{
int points = brush_getfacepoints(mod, id, ++facenum, &facepoints[0], MAX_FACEPOINTS);
if (!points)
break; //end of face list, I guess
R_BeginPolygon("chop");
R_PolygonVertex(facepoints[0], '0 0', col, 1);
for (int point = 1; point < points; point++)
{
R_PolygonVertex(facepoints[point], '0 0', col, 1);
R_EndPolygon();
R_PolygonVertex(facepoints[point], '0 0', col, 1);
}
R_PolygonVertex(facepoints[0], '0 0', col, 1);
R_EndPolygon();
}
};
void(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushWireframe =
{
int f;
int point, points;
for(f = 0; f < numfaces;)
{
points = brush_calcfacepoints(++f, faces, numfaces, facepoints, MAX_FACEPOINTS);
if (!points)
continue; //should probably warn somehow about this
R_BeginPolygon(shader);
R_PolygonVertex(facepoints[0], '0 0', col, alpha);
for (point = 0; point < points-1; )
{
point++;
R_PolygonVertex(facepoints[point], '0 0', col, alpha);
R_EndPolygon();
R_PolygonVertex(facepoints[point], '0 0', col, alpha);
}
R_PolygonVertex(facepoints[0], '0 0', col, alpha);
R_EndPolygon();
}
};
void(brushface_t *faces, int numfaces, string shader, vector col, float alpha) DrawQCBrushSolid =
{
int f;
int point, points;
for(f = 0; f < numfaces;)
{
points = brush_calcfacepoints(++f, faces, numfaces, facepoints, MAX_FACEPOINTS);
if (!points)
continue; //should probably warn somehow about this
R_BeginPolygon(shader);
for (point = 0; point < points; point++)
R_PolygonVertex(facepoints[point], '0 0', col, alpha);
R_EndPolygon();
}
};
void(brushface_t *faces, int numfaces, vector col, float alpha) DrawQCBrushTextured =
{
int f;
int point, points;
for(f = 0; f < numfaces; f++)
{
points = brush_calcfacepoints(1+f, faces, numfaces, facepoints, MAX_FACEPOINTS);
if (points)
{
//this is unfortunate. the built in shaders expect to use lightmaps. we don't have those.
//because lightmaps are special things, we end up in a real mess. so lets just make sure there's a shader now, because we can.
//FIXME: we don't manage to pick up the size of the original wad image
shaderforname(faces[f].shadername,
sprintf("{"
"{\n"
"map \"%s\"\n"
"rgbgen vertex\n"
"alphagen vertex\n"
"}\n"
"}", faces[f].shadername));
vector sz = drawgetimagesize(faces[f].shadername);
R_BeginPolygon(faces[f].shadername);
for (point = 0; point < points; point++)
R_PolygonVertex(facepoints[point], [(facepoints[point] * faces[f].sdir + faces[f].sbias)/sz_x, (facepoints[point] * faces[f].tdir + faces[f].tbias)/sz_y], col, alpha);
R_EndPolygon();
}
}
};
void(vector *p, int points, string shader, vector col, float alpha) DrawAxisExtensions =
{
R_BeginPolygon(shader);
for (int point = 0; point < points; point++)
{
R_PolygonVertex(p[point] + [ 64, 0, 0], '0 0', col, alpha);
R_PolygonVertex(p[point] + [-64, 0, 0], '0 0', col, alpha);
R_EndPolygon();
R_PolygonVertex(p[point] + [0, 64, 0], '0 0', col, alpha);
R_PolygonVertex(p[point] + [0, -64, 0], '0 0', col, alpha);
R_EndPolygon();
R_PolygonVertex(p[point] + [0, 0, 64], '0 0', col, alpha);
R_PolygonVertex(p[point] + [0, 0, -64], '0 0', col, alpha);
R_EndPolygon();
}
};
void(int brushid) editor_drawbbox =
{
static vector bbox[2];
int p = brush_getfacepoints(selectedbrushmodel, brushid, 0, bbox, bbox.length);
if (p == 2)
{
R_BeginPolygon("chop");
#define line(x,y) R_PolygonVertex(x, '0 0', '1 0 0', 1); R_PolygonVertex(y, '0 0', '1 0 0', 1); R_EndPolygon()
line(bbox[0], ([bbox[1][0], bbox[0][1], bbox[0][2]]));
line(bbox[0], ([bbox[0][0], bbox[1][1], bbox[0][2]]));
line(bbox[0], ([bbox[0][0], bbox[0][1], bbox[1][2]]));
line(bbox[1], ([bbox[0][0], bbox[1][1], bbox[1][2]]));
line(bbox[1], ([bbox[1][0], bbox[0][1], bbox[1][2]]));
line(bbox[1], ([bbox[1][0], bbox[1][1], bbox[0][2]]));
line(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]]));
line(([bbox[1][0], bbox[0][1], bbox[0][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]]));
line(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]]));
line(([bbox[0][0], bbox[1][1], bbox[0][2]]), ([bbox[1][0], bbox[1][1], bbox[0][2]]));
line(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[0][0], bbox[1][1], bbox[1][2]]));
line(([bbox[0][0], bbox[0][1], bbox[1][2]]), ([bbox[1][0], bbox[0][1], bbox[1][2]]));
#undef line
}
};

View file

@ -0,0 +1,199 @@
//history is implemented using a ringbuffer
typedef struct
{
float timestamp;
int brushmodel;
int wasdelete;
int id;
brushface_t *brushdata; //list of faces
patchvert_t *patchdata; //list of control points
union
{
struct
{
int numfaces;
int contents;
} brushinfo;
patchinfo_t patchinfo;
};
} history_t;
history_t *historyring;
int historyring_length;
int history_min; //oldest we can go
int history; //updated on each change
int history_max; //max value for redo
//when replaying history, ids get changed, which makes things fun. this updates selection state for edited brushes.
void(int modelidx, int old, int new) history_remap =
{
for (int i = history_min; i < history_max; i++)
{
history_t *h = &historyring[i & (historyring_length-1)];
if (h->id == old)
h->id = new;
}
int i = brush_isselected(modelidx, old);
if (i)
{
i--;
brush_selected(modelidx, old, -1, FALSE);
brush_selected(modelidx, new, -1, TRUE);
selectedbrushes[i].id = new;
}
};
void() brush_undo =
{
if (history == history_min)
print("Nothing to undo.\n");
do
{
if (history <= history_min)
return;
history--;
history_t *h = &historyring[history & (historyring_length-1)];
//we're undoing, so deletes create and creates delete. weird, yes.
if (h->wasdelete)
{
int newid;
if (h->patchdata)
newid = patch_create(h->brushmodel, 0, h->patchdata, h->patchinfo);
else
newid = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);
history_remap(h->brushmodel, h->id, newid);
h->id = newid;
}
else
{
brush_delete(h->brushmodel, h->id);
h->id = 0;
}
if (history == history_min)
break; //as far back as we can go, more timestamps are invalid
} while (historyring[(history-1) & (historyring_length-1)].timestamp == h->timestamp);
};
void() brush_redo =
{
if (history == history_max)
print("Nothing to redo.");
do
{
if (history >= history_max)
return;
history_t *h = &historyring[history & (historyring_length-1)];
//we're redoing stuff that has previously been done. yay.
if (h->wasdelete)
{
brush_delete(h->brushmodel, h->id);
h->id = 0;
}
else
{
int newid;
if (h->patchdata)
newid = patch_create(h->brushmodel, 0, h->patchdata, h->patchinfo);
else
newid = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);
history_remap(h->brushmodel, h->id, newid);
h->id = newid;
}
history++;
if (history == history_max)
return; //don't poke beyond the end...
} while (historyring[history & (historyring_length-1)].timestamp == h->timestamp);
};
history_t *() history_allocate = //returns a new history entry.
{
if (!historyring)
{
history_min = history_max = history;
historyring_length = 512;
historyring = memalloc(sizeof(*historyring)*historyring_length);
}
history_t *h = &historyring[history & (historyring_length-1)];
history++;
history_max = history; //always break any pending redos
if (history_min < history_max - historyring_length)
history_min = history_max - historyring_length;
h->timestamp = time;
memfree(h->brushdata);
h->brushdata = __NULL__;
return h;
};
void(history_t *h) history_deallocate = //cancels a newly created history entry, in case something went wrong.
{
history--;
if (h == &historyring[history & (historyring_length-1)])
history_max--;
};
//create and journal.
int(int mod, brushface_t *faces, int numfaces, int contents, float selectnow) brush_history_create =
{
history_t *h = history_allocate();
h->brushdata = memalloc(sizeof(brushface_t) * numfaces);
memcpy(h->brushdata, faces, sizeof(brushface_t) * numfaces);
h->brushinfo.numfaces = numfaces;
h->brushinfo.contents = contents;
h->brushmodel = mod;
h->wasdelete = FALSE;
h->id = brush_create(h->brushmodel, h->brushdata, h->brushinfo.numfaces, h->brushinfo.contents);
if (!h->id)
history_deallocate(h);
else if (selectnow)
brush_select(mod, h->id);
return h->id;
};
//delete and journal.
void(int mod, int id) brush_history_delete =
{
int numfaces = brush_get(mod, id, __NULL__, 0, __NULL__);
int numcp = patch_getcp(mod, id, __NULL__, 0, __NULL__);
brush_deselect(mod, id);
if (!numfaces && !numcp)
return; //doesn't exist or something, some kind of error. we can't log a delete if it didn't delete anything, if only because we can't recreate it if its undone.
history_t *h = history_allocate();
h->brushmodel = mod;
h->id = id;
h->wasdelete = TRUE;
if (numcp)
{
h->patchdata = memalloc(sizeof(patchvert_t) * numcp);
patch_getcp(mod, id, h->patchdata, numcp, &h->patchinfo);
}
else
{
h->brushdata = memalloc(sizeof(brushface_t) * numfaces);
h->brushinfo.numfaces = brush_get(mod, id, h->brushdata, numfaces, &h->brushinfo.contents);
}
brush_delete(mod, id);
};
int(int mod, int id, brushface_t *faces, int numfaces, int contents) brush_history_edit =
{
int wasselected = brush_isselected(mod, id);
if (wasselected)
selectedbrushes[wasselected-1].id = -id; //hack so that brush_history_delete won't remove this entry.
brush_history_delete(mod, id);
id = brush_history_create(mod, faces, numfaces, contents, FALSE);
if (wasselected)
{
selectedbrushes[wasselected-1].id = id;
brush_selected(mod, id, -1, TRUE);
}
return id;
};

View file

@ -0,0 +1,430 @@
//basic cube plane normals, for selection.
static nosave const vector axis[6] = {
'-1 0 0',
'0 -1 0',
'0 0 -1',
'1 0 0',
'0 1 0',
'0 0 1'
};
float dists[6];
//generates default quakeed-style texture mapping for the given surface.
//this sucks for cylinders, but keeps slopes and things easy.
void(brushface_t *fa) reset_texturecoords =
{
//figure out some default texture coords
fa->sdir = '0 0 0';
fa->sbias = 0;
fa->tdir = '0 0 0';
fa->tbias = 0;
float a=fabs(fa->planenormal[0]),b=fabs(fa->planenormal[1]),c=fabs(fa->planenormal[2]);
if (a>=b&&a>=c)
fa->sdir[1] = 1;
else
fa->sdir[0] = 1;
if (c>a&&c>b)
fa->tdir[1] = -1;
else
fa->tdir[2] = -1;
};
int(brushface_t *fa, int famax, vector *points, int numpoints, float height) BrushFromPoints =
{
int count = 0;
fa->planenormal = normalize(crossproduct(points[2] - points[0], points[1] - points[0]));
fa->planedist = bt_point[0] * fa->planenormal + height;
fa->shadername = autocvar_ca_newbrushtexture;
reset_texturecoords(fa);
fa++;
fa->planenormal = -normalize(crossproduct(points[2] - points[0], points[1] - points[0]));
fa->planedist = (bt_point[0] * fa->planenormal);
fa->shadername = autocvar_ca_newbrushtexture;
reset_texturecoords(fa);
fa++;
count = 2;
for (int p = 0; p < numpoints; p++)
{
int n = p + 1;
if (n == numpoints)
n = 0;
fa->planenormal = normalize(crossproduct(points[p] - bt_point[n], tmp.faces[0].planenormal));
fa->planedist = points[p] * fa->planenormal;
fa->shadername = autocvar_ca_newbrushtexture;
reset_texturecoords(fa);
fa++;
count++;
}
return count;
}
vector(vector guess) brush_snappoint =
{
if (nogrid)
return guess;
int facenum, points;
float bestdist = autocvar_ca_grid*autocvar_ca_grid; //worst case value so we don't snap to grid when there's a vertex 0.001qu away from the grid.
vector best = guess * (1.0/autocvar_ca_grid);
best_x = rint(best_x); //snap to grid
best_y = rint(best_y);
best_z = rint(best_z);
best *= autocvar_ca_grid;
//find surfaces within 32qu of the point (via plane volume). use a tetrahedron instead if you want something more circular
for (facenum = 0; facenum < axis.length; facenum++)
dists[facenum] = (guess * axis[facenum]) + autocvar_ca_grid;
int numbrushes = brush_findinvolume(selectedbrushmodel, axis, dists, 6, brushlist, __NULL__, brushlist.length);
for (int brushnum = 0; brushnum < numbrushes; brushnum++)
{
for (facenum = 0; ; )
{
points = brush_getfacepoints(selectedbrushmodel, brushlist[brushnum], ++facenum, facepoints, MAX_FACEPOINTS);
if (!points)
break; //end of face list, I guess
//walk the faces we found and use the point if its nearer than our previous guess.
for (int point = 0; point < points; point++)
{
vector disp = facepoints[point] - guess;
float dist = disp*disp;
if (dist < bestdist)
best = facepoints[point];
}
}
}
return best;
};
//move a brush so that its planes all move without any translations in positions or texcoords
void brushface_translate(brushface_t *fa, int numfaces, vector displacement)
{
int i;
for (i = 0; i < numfaces; i++, fa++)
{
fa->planedist += fa->planenormal * displacement;
fa->sbias -= fa->sdir * displacement;
fa->tbias -= fa->tdir * displacement;
}
};
//rotates a list of faces by the current v_* vectors, around the origin.
//translate before+after first in order to deal with pivots.
void brushface_rotate(brushface_t *fa, int numfaces)
{
for (int i = 0; i < numfaces; i++, fa++)
{
vector orig = fa->planenormal;
fa->planenormal[0] = orig * v_forward;
fa->planenormal[1] = orig * -v_right; //quake just isn't right...
fa->planenormal[2] = orig * v_up;
orig = fa->sdir;
fa->sdir[0] = orig * v_forward;
fa->sdir[1] = orig * -v_right; //quake just isn't right...
fa->sdir[2] = orig * v_up;
orig = fa->tdir;
fa->tdir[0] = orig * v_forward;
fa->tdir[1] = orig * -v_right; //quake just isn't right...
fa->tdir[2] = orig * v_up;
}
};
vector(vector dir) axialize =
{
vector a;
a_x = fabs(dir_x);
a_y = fabs(dir_y);
a_z = fabs(dir_z);
if (a_x > a_y && a_x > a_z)
return (dir_x > 0)?[1,0,0]:[-1,0,0];
if (a_y > a_x && a_y > a_z)
return (dir_y > 0)?[0,1,0]:[0,-1,0];
return (dir_z > 0)?[0,0,1]:[0,0,-1];
};
vector(vector in) channelizeangle =
{
in_x = anglemod(in_x);
in_y = anglemod(in_y);
in_z = anglemod(in_z);
if (in_x > 180)
in_x -= 360;
if (in_y > 180)
in_y -= 360;
if (in_z > 180)
in_z -= 360;
float fx = fabs(in_x);
float fy = fabs(in_y);
float fz = fabs(in_z);
cprint(sprintf("%v", in));
if (fx > fy && fx > fz)
return [in_x,0,0];
if (fy > fz)
return [0,in_y,0];
return [0,0,in_z];
}
vector(vector p1, vector p2, vector norm, float dist) planelinepoint =
{
float d1 = p1*norm - dist;
float d2 = p2*norm - dist;
float frac = d1 / (d2-d1);
// frac = bound(0, frac, 1);
return p2 + (p1-p2)*frac; //convert that frac into an actual position
};
int(brushface_t *faces, int numfaces, vector *points, int numpoints) isconcave =
{
int result = 0;
//if any of the points are outside the brush, then we know that one of the planes cut straight through in a concavey kind of way
for(int f = 0; f < numfaces; f++)
{
vector n = faces[f].planenormal;
float d = faces[f].planedist + EPSILON; //epsilon to cover precision issues
for (int p = 0; p < numpoints; p++)
{
if (points[p] * n > d)
{
result++;
break;
}
}
}
return result;
};
//returns the resulting brush id
int(int model, int brush1, int brush2, int face1, int face2) mergebrushes =
{
int extrafaces;
float found = FALSE;
brushface_t *fa;
brushface_t *infa;
int i;
if (brush1 == brush2)
{
print("cannot merge brush with itself\n");
return 0;
}
if (!brush1 || !brush2)
{
print("no brush targetted\n");
return 0;
}
if (patch_getcp(model, brush1, __NULL__, 0, __NULL__) || patch_getcp(model, brush2, __NULL__, 0, __NULL__))
{ //fixme: should be possible if they're reasonably adjacent with the same numbers of CPs
print("unable to merge patches\n");
return 0;
}
tmp.numfaces = brush_get(model, brush1, tmp.faces, tmp.faces.length, &tmp.contents);
tmp.numcp = 0;
infa = &tmp.faces[tmp.numfaces];
extrafaces = brush_get(model, brush2, &tmp.faces[tmp.numfaces], tmp.faces.length-tmp.numfaces, &tmp.contents);
//merge the two sets of planes together.
for(infa = &tmp.faces[tmp.numfaces]; extrafaces > 0; infa++, extrafaces--)
{
for (fa = tmp.faces, i = 0; i < tmp.numfaces; i++, fa++)
{
//fixme: needs some tolerance / epsilon
if (fa->planenormal == -infa->planenormal && fa->planedist == -infa->planedist)
{
//inverted. this is the plane we're merging over, so strip it out
memcpy(fa, &fa[1], sizeof(brushface_t) * ((tmp.numfaces-i)-1));
tmp.numfaces--;
i--;
fa--;
found = TRUE;
break;
}
else if (fa->planenormal * infa->planenormal > 0.999 && fa->planedist == infa->planedist)
{
//print("coplanar\n");
//coplanar surfaces are considered safe to ignore. we use the selected brush's texturing info
break;
}
}
if (i == tmp.numfaces)
{ //okay, this plane is important, merge it into the selected brush.
//print("merge plane\n");
memcpy(fa, infa, sizeof(brushface_t));
tmp.numfaces++;
}
}
if (!found)
{
print("Brushes do not share a plane\n");
#if 0
//try to find a surface to move to match to the given plane
float val;
float bestval = -0.707; //must be within 45 degrees
int bestface = -1;
tmp_numfaces = brush_get(model, brush1, tmp_faces, tmp_faces.length, &tmp_contents);
brush_get(model, brush2, &tmp_faces[tmp_numfaces], tmp_faces.length-tmp_numfaces, &tmp_contents);
infa = &tmp_faces[tmp_numfaces + face2-1i];
for (i = 0; i < tmp_numfaces; i++)
{
val = tmp_faces[i].planenormal * infa->planenormal;
if (val < bestval)
{
bestval = val;
bestface = i;
}
}
if (bestface != -1)
{
//FIXME: determine volume and reject it if we shrink?
tmp_faces[bestface].planenormal = -infa->planenormal;
tmp_faces[bestface].planedist = -infa->planedist;
// if (isconcave(tmp_faces, tmp_numfaces))
// {
// print("Resulting brush would be concave\n");
// return 0;
// }
brush_history_delete(model, brush1);
return brush_history_create(model, tmp_faces, tmp_numfaces, tmp_contents);
}
else
#endif
return 0;
}
else
{
vector *points = memalloc(sizeof(vector)*64*64);
int numpoints = 0, f;
for(f = 0; (i = brush_getfacepoints(model, brush1, ++f, points+numpoints, 64*64-numpoints)); )
numpoints += i;
for(f = 0; (i = brush_getfacepoints(model, brush2, ++f, points+numpoints, 64*64-numpoints)); )
numpoints += i;
if (isconcave(tmp.faces, tmp.numfaces, points, numpoints))
{
print("Resulting brush would be concave\n");
memfree(points);
return 0;
}
memfree(points);
//FIXME: verify that no planes got dropped, as this indicates that the region wasn't convex, and we probably just destroyed the entire thing.
brush_history_delete(model, brush1);
brush_history_delete(model, brush2);
return brush_history_create(model, tmp.faces, tmp.numfaces, tmp.contents, TRUE);
}
};
/*
void(vector org, vector ang) editor_brushes_simpleclone =
{
vector midpoint;
if (!selectedbrush)
return;
tmp.numfaces = brush_get(selectedbrushmodel, selectedbrush, tmp.faces, tmp.faces.length, &tmp.contents);
if (ang != '0 0 0')
{
brush_getfacepoints(selectedbrushmodel, selectedbrush, 0, &midpoint, 1);
brushface_translate(tmp.faces, tmp_numfaces, -midpoint);
makevectors(ang);
brushface_rotate(tmp_faces, tmp_numfaces);
brushface_translate(tmp.faces, tmp_numfaces, midpoint + org);
}
else
brushface_translate(tmp.faces, tmp_numfaces, org);
brush_history_create(selectedbrushmodel, tmp.faces, tmp.numfaces, tmp.contents, TRUE);
};
*/
void() brushedit_subtract =
{
int discard;
vector selnormals[tmp.faces.length];
float seldists[tmp.faces.length];
for (int sb = 0; sb < selectedbrushcount; sb++)
{
int mod = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id;
int planecount = brush_get(mod, brush, tmp.faces, tmp.faces.length, &tmp.contents);
if (!planecount)
continue; //csg can't subtract patches
for (int i = 0; i < planecount; i++)
{
selnormals[i] = tmp.faces[i].planenormal;
seldists[i] = tmp.faces[i].planedist;
}
int numbrushes = brush_findinvolume(mod, selnormals, seldists, planecount, brushlist, __NULL__, brushlist.length);
while (numbrushes --> 0)
{
int br = brushlist[numbrushes];
if (brush_isselected(mod, br))
continue; //ignore other selected brushes. race conditions suck
int counto = brush_get(mod, br, tmp.faces, tmp.faces.length, &tmp.contents);
if (!counto)
continue; //don't cut into patches.
int counts = counto + brush_get(mod, brush, tmp.faces+counto, tmp.faces.length-counto, &discard);
brush_history_delete(mod, br);
while(counts --> counto)
{
//only consider the resulting brush if the new face actually contributed anything.
//this reduces dupes.
if (brush_calcfacepoints(1+counts, tmp.faces, counts+1, facepoints, MAX_FACEPOINTS))
{
//determine the brush defined by this plane
tmp.faces[counts].planenormal *= -1;
tmp.faces[counts].planedist *= -1;
brush_history_create(mod, tmp.faces, counts+1, tmp.contents, FALSE);
}
}
}
}
};
void() brushedit_resettextures =
{
for (int sb = 0; sb < selectedbrushcount; sb++)
{
int model = selectedbrushes[sb].model;
int brush = selectedbrushes[sb].id;
int face = selectedbrushes[sb].face;
int planecount = brush_get(model, brush, tmp.faces, tmp.faces.length, &tmp.contents);
if (planecount)
{
for (int i = 0; i < planecount; i++)
if (!face || face == planecount+1)
reset_texturecoords(&tmp.faces[i]);
brush_history_edit(model, brush, tmp.faces, planecount, tmp.contents);
}
}
};

View file

@ -0,0 +1,108 @@
var float autocvar_ca_brush_view = 0; //0=normal, 1=x, 2=y, 3=z
var float autocvar_ca_brush_viewsize = 1024; //for different views.
var string autocvar_ca_newbrushtexture = "metal4_2";
var float autocvar_ca_newbrushheight = 64;
var float autocvar_ca_grid = 16;
#define EPSILON (1.0 / 32) //inprecision sucks.
#define MAX_FACEPOINTS 64
vector facepoints[MAX_FACEPOINTS]; //for temp usage
#define MAX_BRUSHFACES 64
struct
{
//brush state
brushface_t faces[MAX_BRUSHFACES];
int numfaces;
int contents;
//patch state
patchvert_t *cp;
int numcp, maxcp;
patchinfo_t patchinfo;
} tmp;
//int selected;
int brushlist[2048];
enum : int
{
BT_NONE, //selection
BT_MOVE = BT_NONE,
BT_ROTATE,
BT_MERGE,
BT_PUSHFACE,
BT_CREATE,
BT_CREATEDRAG,
BT_CLONEDISPLACE,
BT_SLICE,
BT_MOVETEXTURE,
BT_VERTEXEDIT
};
int mousetool;
int brushtool;
int bt_points;
vector bt_displace;
vector bt_point[64];
int nogrid; //+nogrid, temporarily disables grid locks
typedef struct
{
int model;
int id;
int face;
//fixme: do we need an array of faces here?
} selbrush_t;
selbrush_t *selectedbrushes;
int selectedbrushcount;
var int selectedbrushmodel = 1; //by default, the worldmodel. this is tracked to know which submodel to insert new brushes into.
int(int modelidx, int brushid) brush_isselected =
{
for (int i = 0; i < selectedbrushcount; i++)
if (selectedbrushes[i].id == brushid)
if (selectedbrushes[i].model == modelidx)
return i+1;
return 0;
};
int(int modelidx, int brushid) brush_deselect =
{
int i = brush_isselected(modelidx, brushid);
if (!i)
return FALSE;
brush_selected(modelidx, brushid, -1, FALSE);
memcpy(&selectedbrushes[i-1], &selectedbrushes[i], sizeof(selbrush_t)*(selectedbrushcount-i));
selectedbrushcount--;
return TRUE;
};
void() brush_deselectall =
{
for (int i = 0; i < selectedbrushcount; i++)
brush_selected(selectedbrushes[i].model, selectedbrushes[i].id, -1, FALSE);
selectedbrushcount = 0;
};
void(int modelidx, int brushid) brush_select =
{
if (!brush_isselected(modelidx, brushid))
{
selbrush_t *n = memalloc(sizeof(selbrush_t) * (selectedbrushcount+1));
memcpy(n, selectedbrushes, sizeof(selbrush_t)*selectedbrushcount);
memfree(selectedbrushes);
selectedbrushes = n;
n[selectedbrushcount].model = modelidx;
n[selectedbrushcount].id = brushid;
selectedbrushcount++;
brush_selected(modelidx, brushid, -1, TRUE);
}
selectedbrushmodel = modelidx;
};

View file

@ -0,0 +1,324 @@
typedef struct
{
int numverts;
int numidx;
vector *p;
int *i;
int selectedvert1;
int selectedvert2;
int model;
int brush;
} vertsoup_t;
vertsoup_t vertedit;
//take a brush apart and generate trisoup from it.
//note that the trisoup does not have textures. they will be reconciled when reforming the brush.
void(vertsoup_t *vertedit, int model, int brush) Debrushify =
{
int *ni;
int i, j, k;
int points;
vector p;
vector *np;
static int fi[64];
vertedit->model = model;
vertedit->brush = brush;
vertedit->numidx = 0;
vertedit->numverts = 0;
for (i = 0; ; )
{
points = brush_getfacepoints(vertedit->model, vertedit->brush, ++i, facepoints, facepoints.length);
if (!points)
break;
//allocate a few new indexes
ni = memalloc(sizeof(*ni) * (vertedit->numidx+3*(points-2)));
memcpy(ni, vertedit->i, sizeof(*ni) * vertedit->numidx);
memfree(vertedit->i);
vertedit->i = ni;
ni += vertedit->numidx;
vertedit->numidx += 3*(points-2);
for (j = 0; j < points; j++)
{
p = facepoints[j];
p_x = floor(p_x + 0.5); //gah, bloomin inprecision.
p_y = floor(p_y + 0.5);
p_z = floor(p_z + 0.5);
for (k = 0; k < vertedit->numverts; k++)
{ //try to be nice and re-use verts.
if (vertedit->p[k] == p)
break;
}
if (k == vertedit->numverts)
{
//if it wasn't found, we need to allocate a new one
np = memalloc(sizeof(*np) * (vertedit->numverts+1));
memcpy(np, vertedit->p, sizeof(*np) * vertedit->numverts);
memfree(vertedit->p);
vertedit->p = np;
np += vertedit->numverts;
vertedit->numverts += 1;
*np = p;
}
fi[j] = k;
}
for (j = 2; j < points; j++)
{
*ni++ = fi[0];
*ni++ = fi[j-1];
*ni++ = fi[j];
}
}
};
//determines only the various points of the brush, without duplicates. doesn't care about indexes.
void(brushface_t *faces, int numfaces) DebrushifyLite =
{
int points, k;
vector p;
vector *np;
int fi[64];
vertedit.numidx = 0;
vertedit.numverts = 0;
for (int i = 0; ; )
{
points = brush_calcfacepoints(++i, faces, numfaces, facepoints, facepoints.length);
if (!points)
break;
for (int j = 0; j < points; j++)
{
p = facepoints[j];
p_x = floor(p_x + 0.5); //gah, bloomin inprecision.
p_y = floor(p_y + 0.5);
p_z = floor(p_z + 0.5);
for (k = 0; k < vertedit.numverts; k++)
{ //try to be nice and re-use verts.
if (vertedit.p[k] == p)
break;
}
if (k == vertedit.numverts)
{
//if it wasn't found, we need to allocate a new one
np = memalloc(sizeof(*np) * (vertedit.numverts+1));
memcpy(np, vertedit.p, sizeof(*np) * vertedit.numverts);
memfree(vertedit.p);
vertedit.p = np;
np += vertedit.numverts;
vertedit.numverts += 1;
*np = p;
}
fi[j] = k;
}
}
};
static float(vertsoup_t *soup, int *idx, __out vector norm, __out float dist) planenormal =
{
vector v1 = soup->p[idx[0]];
vector v2 = soup->p[idx[1]];
vector v3 = soup->p[idx[2]];
vector d1 = v3 - v1;
vector d2 = v2 - v1;
vector d3 = v3 - v2;
norm = normalize(crossproduct(d1, d2));
dist = norm * v1;
if (!d1 || !d2 || !d3 || !norm)
return FALSE;
return TRUE;
};
void(vertsoup_t *soup, int drawit) Rebrushify =
{
brushface_t faces[64];
int numfaces, f, point;
string shader = 0;
vector n;
float d;
int o=0;
tmp.numfaces = brush_get(soup->model, soup->brush, tmp.faces, tmp.faces.length, &tmp.contents);
//if any triangle's neighbour opposes it, reform the edge across the quad to try to keep it convex.
for(point = 0; point+2 < soup->numidx; point+=3)
{
int p1 = soup->i[point+0];
int p2 = soup->i[point+1];
int p3 = soup->i[point+2];
if (!planenormal(soup, &soup->i[point], n, d))
continue; //degenerate
d += EPSILON;
for(f = point+3; f+2 < soup->numidx; f+=3)
{
int o1 = soup->i[f+0];
int o2 = soup->i[f+1];
int o3 = soup->i[f+2];
p1p2edge:
if (o2 == p1 && o1 == p2)
o = o3;
else if (o3 == p1 && o2 == p2)
o = o1;
else if (o1 == p1 && o3 == p2)
o = o2;
else
goto p2p3edge;
if (soup->p[o] * n > d)
{
soup->i[f+0] = p3;
soup->i[f+1] = o;
soup->i[f+2] = p2;
p2 = o;
}
continue;
p2p3edge:
if (o2 == p2 && o1 == p3)
o = o3;
else if (o3 == p2 && o2 == p3)
o = o1;
else if (o1 == p2 && o3 == p3)
o = o2;
else
goto p3p1edge;
if (soup->p[o] * n > d)
{
soup->i[f+0] = p1;
soup->i[f+1] = o;
soup->i[f+2] = p3;
p3 = o;
}
continue;
p3p1edge:
if (o2 == p3 && o1 == p1)
o = o3;
else if (o3 == p3 && o2 == p1)
o = o1;
else if (o1 == p3 && o3 == p1)
o = o2;
else
continue;
if (soup->p[o] * n > d)
{
soup->i[f+0] = p2;
soup->i[f+1] = o;
soup->i[f+2] = p1;
p1 = o;
}
continue;
}
soup->i[point+0] = p1;
soup->i[point+1] = p2;
soup->i[point+2] = p3;
}
//generate the plane info
numfaces = 0;
for(point = 0; point+2 < soup->numidx; point+=3)
{
if (!planenormal(soup, &soup->i[point], n, d))
continue; //a degenerate triangle is one that probably got merged or something
for (f = 0; f < numfaces; f++)
{
if (faces[f].planenormal == n && faces[f].planedist == d)
break;
}
if (f < numfaces)
continue; //duplicate plane
for (f = 0; f < tmp.numfaces; f++)
{
if (tmp.faces[f].planenormal * n > 0.999) //stupid inprecisions
{
if (numfaces == 64)
return;
//note that we only care about the normal, not the dist. this means you can move the entire face forward+back without loosing textures.
faces[numfaces].shadername = shader = tmp.faces[f].shadername;
faces[numfaces].planenormal = n;
faces[numfaces].planedist = d;
faces[numfaces].sdir = tmp.faces[f].sdir;
faces[numfaces].sbias = tmp.faces[f].sbias;
faces[numfaces].tdir = tmp.faces[f].tdir;
faces[numfaces].tbias = tmp.faces[f].tbias;
numfaces++;
break;
}
}
if (f < tmp.numfaces)
continue; //matched a plane in the original brush
if (numfaces == 64)
return;
//FIXME: find aproximate faces to give corners or something
//okay, it appears to be new. that's a pain.
faces[numfaces].shadername = 0;
faces[numfaces].planenormal = n;
faces[numfaces].planedist = d;
reset_texturecoords(&faces[numfaces]);
numfaces++;
}
//any surface without a texture/shader yet should inherit one from some other surface
if (!shader)
shader = autocvar_ca_newbrushtexture;
for(f = 0; f < numfaces; f++)
{
if (!faces[f].shadername)
faces[f].shadername = shader;
}
int internals = 0;
//If we have a point outside a plane, then we know we have a concave volume.
//chop the points
for(f = 0; f < numfaces; f++)
{
n = faces[f].planenormal;
d = faces[f].planedist;
for (point = 0; point < soup->numidx; point++) //would ideally use points, but I want to cover dead ones
{
if (soup->p[soup->i[point]] * n > d + EPSILON)
{
internals++;
break;
}
}
}
// cprint(sprintf("%i internal splits, %i faces\n", internals, numfaces));
if (numfaces <= 3)
return; //can't possibly be valid.
if (drawit)
{
//draw it wireframe WITH depth testing
//DrawQCBrushWireframe(faces, numfaces, "terrainedit", '1 0 0', 1);
//draw textured preview (yay for block lighting)
DrawQCBrushTextured(faces, numfaces, '0.7 0.7 0.7', 1);
//draw it wireframe without depth testing
DrawQCBrushWireframe(faces, numfaces, "chop", internals?[1,0,0]:[0,0,1], 1);
}
else
{
brush_history_edit(soup->model, soup->brush, faces, numfaces, 1i);
soup->numidx = 0;
soup->numverts = 0;
}
};

View file

@ -20,9 +20,6 @@ float releasedmouse;
#ifdef WALLBROWSERS
string pointedshadername;
#endif
vector pointedsurfacenormal;
entity pointedent;
float pointedsurface;
float editorrsd[editornames.length];

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -180,7 +180,7 @@ void(float num) editor_ents_delete =
if (num > 0 && num < numents)
{
hash_destroytab(editents[num].fields);
editents[num].fields = 0;
editents[num].fields = 0i;
terrain_edit(TEREDIT_ENT_SET, num, __NULL__);
}
};
@ -271,8 +271,8 @@ void(entedit_t *nent) editor_ents_updated =
for (i = 0; i < 8; i++)
{
nent->bboxverts[i].st = (vec2){0,0};
nent->bboxverts[i].rgba = (vec4){nent->colourmod[0], nent->colourmod[1], nent->colourmod[2], nent->alpha?nent->alpha:1};
nent->bboxverts[i].st = (vec2){{0,0}};
nent->bboxverts[i].rgba = (vec4){{nent->colourmod[0], nent->colourmod[1], nent->colourmod[2], nent->alpha?nent->alpha:1}};
nent->bboxverts[i].xyz[0] = nent->org[0] + ((i&1i)?nent->maxs[0]:nent->mins[0]);
nent->bboxverts[i].xyz[1] = nent->org[1] + (((i&2i)?nent->maxs[1]:nent->mins[1]));
nent->bboxverts[i].xyz[2] = nent->org[2] + ((i&4)?nent->maxs[2]:nent->mins[2]);
@ -335,7 +335,7 @@ void() editor_ents_reload =
nent = &editents[id];
if (nent->fields)
hash_destroytab(nent->fields);
nent->fields = 0;
nent->fields = __NULL__;
if (field == __NULL__) //empty entity slot.
continue;
nent->fields = hash_createtab(12, EV_STRING);

View file

@ -219,7 +219,7 @@ static void(string descname) loadparticles =
if (descname != "")
{
f = fopen(strcat("particles/", descname, ".cfg"), FILE_READNL);
if (f >= 0)
if ((float)f >= 0)
{
particlesfile = strzone(fgets(f));
fclose(f);
@ -253,7 +253,7 @@ static void(string descname) saveparticle =
if (descname != "")
{
f = fopen(strcat("particles/", descname, ".cfg"), FILE_WRITE);
if (f >= 0)
if ((float)f >= 0)
{
fputs(f, particlesfile);
fclose(f);

View file

@ -588,7 +588,7 @@ void(vector mousepos) editor_terrain_overlay =
}
if (curtool == ter_tex)
{
if (texturesearch < 0)
if ((float)texturesearch < 0)
texturesearch = search_begin("textures/*", FALSE, TRUE);
if (texturesearchfirst > search_getsize(texturesearch)-4)
texturesearchfirst = search_getsize(texturesearch)-4;

View file

@ -383,7 +383,7 @@ void(string shader, vector org, vector s, vector t, string text, vector col, flo
vector st = draw_getimagesize(shader);
vector pos = org;
float chr;
float idx;
float idx = 0;
vector tc0,tc1,tc2,tc3;
//with GL_LINEAR sampling, the sample value is interpolated from the nearby two pixels

View file

@ -78,8 +78,8 @@ void(vector pos, textfield_t *fld, textfield_linedesc_t *fields) textfield_draw
{
string t;
int l;
int nummatches;
int lastmatch;
int nummatches = 0;
int lastmatch = 0;
for (l = 0i; l < fld->linecount; l+=1i, pos_y += 8)
{
if (l == fld->cursorline && ((cltime * 4) & 1))
@ -154,7 +154,7 @@ void(textfield_t *fld) textfield_localcmd =
string(textfield_t *fld) textfield_strcat =
{
int l;
string res;
string res = "";
for (l = 0i; l < fld->linecount; l+=1i)
{
res = strcat(res, fld->lines[l], "\n");

View file

@ -9,6 +9,7 @@
float show_scoreboard;
string dbgstr;
vector mousepos;
void() CSQC_Shutdown = {};
@ -223,6 +224,8 @@ void() drawloadingscreen =
void(float apilevel, string enginename, float engineversion) CSQC_Init =
{
__using renderflags;
checkengineversion();
precache_model("progs/eyes.mdl");
@ -248,6 +251,8 @@ void(float apilevel, string enginename, float engineversion) CSQC_Init =
void(entity ent) CSQC_DrawViewModel =
{
__using shaderforname, checkbuiltin, forceshader;
if (player_local.sveffects & SVE_INVIS)
{
if (!checkbuiltin(shaderforname))
@ -321,6 +326,18 @@ float (float event, float parama, float paramb, float paramc) CSQC_InputEvent =
break;
}
}
else if (event == IE_MOUSEABS)
mousepos = [parama, paramb];
else if (event == IE_MOUSEDELTA)
{ //move it...
mousepos += [parama, paramb];
//bound it.
vector ssize = getviewprop(VF_SCREENVSIZE);
mousepos_x = bound(0, mousepos_x, ssize_x);
mousepos_y = bound(0, mousepos_y, ssize_y);
}
return Menu_InputEvent(event, parama, paramb);
};
@ -334,8 +351,6 @@ void(float width, float height, float do2d) CSQC_UpdateView =
clearscene();
setviewprop(VF_DRAWCROSSHAIR, 1);
//force fullscreen views (ignore viewsize).
setviewprop(VF_MIN, '0 0 0');
setviewprop(VF_SIZE_X, width);
@ -355,6 +370,7 @@ void(float width, float height, float do2d) CSQC_UpdateView =
{
setviewprop(VF_DRAWENGINESBAR, 1);
}
setviewprop(VF_DRAWCROSSHAIR, !player_local || !checkbuiltin(project));
addentities(MASK_NORMAL|MASK_ENGINE);
@ -404,6 +420,33 @@ void(float width, float height, float do2d) CSQC_UpdateView =
default:
if (hudtype)
Hud_Draw(hudtype, show_scoreboard, width, height);
//quicky `project` test
if (player_local)
{
if (checkbuiltin(project))
{
setviewprop(VF_DRAWCROSSHAIR, 0);
vector shotorg = player_local.origin+'0 0 16'; //quake is weird.
traceline(shotorg, shotorg + v_forward*8192, FALSE, player_local);
drawcharacter(project(trace_endpos)-'4 4', '+', '8 8', '1 1 1',1);
}
if (autocvar(cg_unprojecttest, FALSE))
{
static float nexttime;
nexttime += clframetime;
drawcharacter(mousepos-'4 4', '+', '8 8', '1 1 1',1);
if (nexttime > 1)
{
vector near = unproject([mousepos_x,mousepos_y,0]);
vector far = unproject([mousepos_x,mousepos_y,1]);
traceline(near, far, FALSE, player_local);
te_explosion(trace_endpos);
nexttime-=1;
}
}
}
break;
}

View file

@ -7,6 +7,7 @@ ParsePlayer: Called from CSQC_Ent_Parse for csqc protocol ents
static void() RemovePlayer;
enum
{
MF_BAD,
@ -69,7 +70,7 @@ static float() Player_Interpolate =
self.renderflags = RF_EXTERNALMODEL;
#ifdef POWERUP_SHELLS
if (!checkbuiltin(shaderforname))
if (checkbuiltin(shaderforname))
{
if (self.sveffects & SVE_INVIS)
self.forceshader = shaderforname("powerups/invisibility");

View file

@ -1158,7 +1158,7 @@ nonstatic vector(string skinname) Anim_GetHeadOffset =
float(float channel, string soundname, vector pos, float vol, float attenuation) CSQC_ServerSound =
float(float channel, string soundname, vector pos, float vol, float attenuation, float flags) CSQC_ServerSound =
{ //the server started a sound on an entity that the csqc has control over.
if (!self.headent)
{
@ -1248,7 +1248,7 @@ float(float sventnum, float channel, string soundname, float vol, float att, vec
{
self = findfloat(world, entnum, sventnum);
if (self)
return CSQC_ServerSound(channel, soundname, org, vol, att);
return CSQC_ServerSound(channel, soundname, org, vol, att, flags);
return false;
};
#endif

View file

@ -3,9 +3,11 @@
#define CSQC
optsall.qc
#ifdef QSS
qsextensions.qc
#endif
csqc_api.qc
//qsextensions.qc
#else
fteextensions.qc
#endif
cs/defs.qc
common/classes.qc

View file

@ -9,7 +9,7 @@ You will probably need to use FTEQCC to compile this.
#if defined(QUAKEWORLD) || defined(MENU)
#error Mixed up module defs
#endif
#ifndef CSQC
#if !defined(CSQC) && !defined(SSQC) && !defined(MENU)
#define CSQC
#endif
#ifndef CSQC_SIMPLE
@ -97,6 +97,7 @@ You will probably need to use FTEQCC to compile this.
//Explicitly flag this stuff as probably-not-referenced, meaning fteqcc will shut up about it and silently strip what it can.
#pragma noref 1
#if defined(CSQC_SIMPLE) || defined(SSQC)
entity self,other,world;
float time,frametime,force_retouch;
string mapname;
@ -110,6 +111,7 @@ float trace_inopen,trace_inwater;
entity msg_entity;
void() main,StartFrame,PlayerPreThink,PlayerPostThink,ClientKill,ClientConnect,PutClientInServer,ClientDisconnect,SetNewParms,SetChangeParms;
void end_sys_globals;
.float modelindex;
.vector absmin, absmax;
.float ltime,movetype,solid;
@ -142,6 +144,7 @@ void end_sys_globals;
.float sounds;
.string noise, noise1, noise2, noise3;
void end_sys_fields;
#endif
//Some custom types (that might be redefined as accessors by fteextensions.qc, although we don't define any methods here)
@ -158,15 +161,35 @@ accessor filestream:float;
#define infostring string
#define filestream float
#endif
void(string cmd) SV_ParseClientCommand;
void() EndFrame;
void(float apilevel, string enginename, float engineversion) CSQC_Init;
float(string cmdstr) CSQC_ConsoleCommand;
void(vector virtsize, float showscores) CSQC_DrawHud;
void(vector virtsize, float showscores) CSQC_DrawScores;
float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent;
void() CSQC_Parse_Event;
float(float save, float take, vector dir) CSQC_Parse_Damage;
//Common entry points
void() EndFrame
void(string cmdtext) GameCommand
//Serverside entry points
void(string cmd) SV_ParseClientCommand
void() SV_RunClientCommand
//CSQC entry points
void(float apilevel, string enginename, float engineversion) CSQC_Init
void() CSQC_Shutdown
void(vector virtsize, float showscores) CSQC_DrawHud
void(vector virtsize, float showscores) CSQC_DrawScores
float(float evtype, float scanx, float chary, float devid) CSQC_InputEvent
float(string cmdstr) CSQC_ConsoleCommand
void() CSQC_Parse_Event
float(float save, float take, vector dir) CSQC_Parse_Damage
void(float vwidth, float vheight, float notmenu) CSQC_UpdateView
void() CSQC_Input_Frame
float(string msg) CSQC_Parse_CenterPrint
void(string printmsg, float printlvl) CSQC_Parse_Print
void(float isnew) CSQC_Ent_Update
void() CSQC_Ent_Remove
float() CSQC_Parse_TempEntity
void(string msg) CSQC_Parse_StuffCmd
float cltime; /* increases regardless of pause state or game speed */
float maxclients; /* maximum number of players possible on this server */
float intermission; /* in intermission */
@ -175,11 +198,8 @@ float player_localnum; /* the player slot that is believed to be assigned to us
float player_localentnum; /* the entity number that the view is attached to */
const float FALSE = 0;
const float TRUE = 1;
const float IE_KEYDOWN = 0;
const float IE_KEYUP = 1;
const float IE_MOUSEDELTA = 2;
const float IE_MOUSEABS = 3;
const float IE_JOYAXIS = 6;
const float MASK_ENGINE = 1;
const float MASK_VIEWMODEL = 2;
const float STAT_HEALTH = 0; /* Player's health. */
const float STAT_WEAPONMODELI = 2; /* This is the modelindex of the current viewmodel (renamed from the original name 'STAT_WEAPON' due to confusions). */
const float STAT_AMMO = 3; /* player.currentammo */
@ -202,7 +222,39 @@ const float STAT_IDEALPITCH = 25;
const float STAT_PUNCHANGLE_X = 26;
const float STAT_PUNCHANGLE_Y = 27;
const float STAT_PUNCHANGLE_Z = 28;
const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */
const float SOLID_BBOX = 2;
const float SOLID_BSP = 4;
const float SOLID_NOT = 0;
const float SOLID_SLIDEBOX = 3;
const float SOLID_TRIGGER = 1;
const float CONTENTS_EMPTY = -1;
const float CONTENTS_SOLID = -2;
const float CONTENTS_WATER = -3;
const float CONTENTS_SLIME = -4;
const float CONTENTS_LAVA = -5;
const float CONTENTS_SKY = -6;
const float CONTENTS_LADDER = -16;
__used var float physics_mode = 2;
const float TE_SPIKE = 0;
const float TE_SUPERSPIKE = 1;
const float TE_GUNSHOT = 2;
const float TE_EXPLOSION = 3;
const float TE_TAREXPLOSION = 4;
const float TE_LIGHTNING1 = 5;
const float TE_LIGHTNING2 = 6;
const float TE_WIZSPIKE = 7;
const float TE_KNIGHTSPIKE = 8;
const float TE_LIGHTNING3 = 9;
const float TE_LAVASPLASH = 10;
const float TE_TELEPORT = 11;
const float TE_EXPLOSION2 = 12;
const float TE_BEAM = 13;
const float IE_KEYDOWN = 0;
const float IE_KEYUP = 1;
const float IE_MOUSEDELTA = 2;
const float IE_MOUSEABS = 3;
const float IE_JOYAXIS = 6;
const float VF_MIN = 1;const float VF_MIN_X = 2;const float VF_MIN_Y = 3;const float VF_SIZE = 4;const float VF_SIZE_X = 5;const float VF_SIZE_Y = 6;const float VF_VIEWPORT = 7;const float VF_FOV = 8;const float VF_FOV_X = 9;const float VF_FOV_Y = 10;const float VF_ORIGIN = 11;const float VF_ORIGIN_X = 12;const float VF_ORIGIN_Y = 13;const float VF_ORIGIN_Z = 14;const float VF_ANGLES = 15;const float VF_ANGLES_X = 16;const float VF_ANGLES_Y = 17;const float VF_ANGLES_Z = 18;const float VF_DRAWWORLD = 19;const float VF_DRAWENGINESBAR = 20;const float VF_DRAWCROSSHAIR = 21;const float VF_CL_VIEWANGLES = 33;const float VF_CL_VIEWANGLES_X = 34;const float VF_CL_VIEWANGLES_Y = 35;const float VF_CL_VIEWANGLES_Z = 36;const float VF_AFOV = 203;const float VF_SCREENVSIZE = 204;const float VF_SCREENPSIZE = 205;const float RF_VIEWMODEL = 1;const float RF_EXTERNALMODEL = 2;const float RF_DEPTHHACK = 4;const float RF_ADDITIVE = 8;const float RF_USEAXIS = 16;const float RF_NOSHADOW = 32;const float RF_WEIRDFRAMETIMES = 64;const float SLIST_HOSTCACHEVIEWCOUNT = 0;const float SLIST_HOSTCACHETOTALCOUNT = 1;const float SLIST_SORTFIELD = 6;const float SLIST_SORTDESCENDING = 7;const float STAT_USER = 32; /* Custom user stats start here (lower values are reserved for engine use). */
const float EV_VOID = 0;
const float EV_STRING = 1;
const float EV_FLOAT = 2;
@ -215,26 +267,41 @@ const float EV_INTEGER = 8;
//Supported Extension fields
.float gravity;
//.float items2; /*if defined, overrides serverflags for displaying runes on the hud*/
.float traileffectnum; /*can also be set with 'traileffect' from a map editor*/
.float emiteffectnum; /*can also be set with 'traileffect' from a map editor*/
.vector movement; /*describes which forward/right/up keys the player is holidng*/
.entity viewmodelforclient; /*attaches this entity to the specified player's view. invisible to other players*/
.entity exteriormodeltoclient;/*hides the entity in the specified player's main view. it will remain visible in mirrors etc.*/
.float scale; /*rescales the etntiy*/
.float alpha; /*entity opacity*/
.vector colormod; /*tints the entity's colours*/
.float alpha;
.float scale;
.vector colormod;
.entity tag_entity;
.float tag_index;
.float modelflags;
.vector origin;
.vector angles;
.float frame;
.float skin;
.void() customphysics;
.float gravity;
.float frame2;
.float lerpfrac;
.float frame1time;
.float frame2time;
.float renderflags;
.float entnum;
.float drawmask;
.float() predraw;
//.float items2;
.vector movement;
.entity viewmodelforclient;
.entity exteriormodeltoclient;
.float traileffectnum;
.float emiteffectnum;
.float button3;
.float button4;
.float button5;
.float button6;
.float button7;
.float button8;
.float viewzoom; /*rescales the user's fov*/
.float modelflags; /*provides additional modelflags to use (effects&EF_NOMODELFLAGS to replace the original model's)*/
.float viewzoom;
.float(entity to, float changedflags) SendEntity;
.float SendFlags;
//Supported Extension Constants
@ -273,23 +340,60 @@ void(entity,vector) setorigin = #2;
void(entity,string) setmodel = #3;
void(entity,vector,vector) setsize = #4;
float() random = #7;
//void(entity e, float chan, string samp, float vol, float atten, optional float speedpct, optional float flags, optional float timeofs) sound = #8;
vector(vector) normalize = #9;
void(string e) error = #10;
void(string n) objerror = #11;
float(vector) vlen = #12;
//float(vector fwd) vectoyaw = #13;
entity() spawn = #14;
void(entity e) remove = #15;
void(vector v1, vector v2, float flags, entity ent) traceline = #16;
entity() checkclient = #17;
entity(entity start, .string fld, string match) find = #18;
string(string s) precache_sound = #19;
string(string s) precache_model = #20;
void(entity client, string s) stuffcmd = #21;
//entity(vector org, float rad, optional .entity chainfield) findradius = #22;
//void(string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7, optional string s8) bprint = #23;
//void(entity pl, string s, optional string s2, optional string s3, optional string s4, optional string s5, optional string s6, optional string s7) sprint = #24;
void(string,...) dprint = #25;
string(float) ftos = #26;
string(vector) vtos = #27;
//float(float yaw, float dist, optional float settraceglobals) walkmove = #32;
float() droptofloor = #34;
//void(float lightstyle, string stylestring, optional vector rgb) lightstyle = #35;
float(float n) rint = #36;
float(float n) floor = #37;
float(float n) ceil = #38;
float(entity e) checkbottom = #40;
float(vector point) pointcontents = #41;
float(float n) fabs = #43;
vector(entity e, float speed) aim = #44;
float(string) cvar = #45;
void(string,...) localcmd = #46;
entity(entity) nextent = #47;
//void(vector o, vector d, float color, float count) particle = #48;
void() changeyaw = #49;
void(float to, float val) WriteByte = #52;
void(float to, float val) WriteChar = #53;
void(float to, float val) WriteShort = #54;
void(float to, float val) WriteLong = #55;
void(float to, float val) WriteCoord = #56;
void(float to, float val) WriteAngle = #57;
void(float to, string val) WriteString = #58;
void(float to, entity val) WriteEntity = #59;
void(float step) movetogoal = #67;
string(string s) precache_file = #68;
void(entity e) makestatic = #69;
void(string mapname, optional string newmapstartspot) changelevel = #70;
void(string var, string val) cvar_set = #72;
void(entity ent, string text, optional string text2, optional string text3, optional string text4, optional string text5, optional string text6, optional string text7) centerprint = #73;
void (vector pos, string samp, float vol, float atten) ambientsound = #74;
string(string str) precache_model2 = #75;
string(string str) precache_sound2 = #76;
string(string str) precache_file2 = #77;
void(entity player) setspawnparms = #78;
//Builtin list
@ -691,8 +795,8 @@ entity(entity from, optional entity to) copyentity = #400; /*
void(entity ent, float colours) setcolors = #401; /*
Changes a player's colours. The bits 0-3 are the lower/trouser colour, bits 4-7 are the upper/shirt colours. */
entity(.string field, string match) findchain = #402;
entity(.float fld, float match) findchainfloat = #403;
entity(.string field, string match, optional .entity chainfield) findchain = #402;
entity(.float fld, float match, optional .entity chainfield) findchainfloat = #403;
void(vector org, vector dir, float count) te_blood = #405;
void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlerain = #409;
void(vector mincorner, vector maxcorner, vector vel, float howmany, float color) te_particlesnow = #410;
@ -723,7 +827,7 @@ string(float n) argv = #442;
float() argc = #0;
void(entity e, entity tagentity, string tagname) setattachment = #443; /* */
searchhandle(string pattern, optional float caseinsensitive, optional float quiet) search_begin = #444; /*
searchhandle(string pattern, float flags, float quiet, optional string filterpackage) search_begin = #444; /*
initiate a filesystem scan based upon filenames. Be sure to call search_end on the returned handle. */
void(searchhandle handle) search_end = #445; /* */
@ -745,7 +849,7 @@ string(searchhandle handle, float num) search_getpackagename = #0; /*
string(string cvarname) cvar_string = #448;
entity(entity start, .float fld, float match) findflags = #449;
entity(.float fld, float match) findchainflags = #450;
entity(.float fld, float match, optional .entity chainfield) findchainflags = #450;
void(entity player) dropclient = #453;
entity() spawnclient = #454; /*
Spawns a dummy player entity.
@ -890,9 +994,10 @@ string(float n, float prop) getgamedirinfo = #626; /*
string(string fmt, ...) sprintf = #627;
float(entity e, float s) getsurfacenumtriangles = #628;
vector(entity e, float s, float n) getsurfacetriangle = #629;
float(float key, string bind, optional float bindmap) setkeybind = #630; /*
float(float key, string bind, optional float bindmap, optional float modifier) setkeybind = #630; /*
Changes a key binding. */
string(string digest, string data, ...) digest_hex = #639;
//Builtin Stubs List (these are present for simpler compatibility, but not properly supported in QuakeSpasm at this time).
@ -915,7 +1020,6 @@ void(float mask, float fld, float num, float op) sethostcachemasknumber = #617;
void(string key) addwantedhostcachekey = #623;
vector() getbindmaps = #631;
float(vector bm) setbindmaps = #632;
string(string digest, string data, ...) digest_hex = #639;
*/
const float K_TAB = 9;
const float K_ENTER = 13;

View file

@ -541,11 +541,8 @@ void() PutClientInServer =
self.effects = 0;
self.invincible_time = 0;
if (serverusingcsqc)
{
self.SendEntity = SendPlayer;
self.SendFlags = FULLSEND;
}
self.SendEntity = SendPlayer;
self.SendFlags = FULLSEND;
DecodeLevelParms ();
@ -1258,7 +1255,7 @@ enum {
float(entity ent) GetGender =
{
local string s;
if (!isdp)
if (checkbuiltin(infokey))
{
s = infokey(ent, "s");
if (s == "" || s == "m" || s == "male" || s == "yesplease")
@ -1267,7 +1264,8 @@ float(entity ent) GetGender =
return GENDER_FEMALE;
return GENDER_NEUTER;
}
return GENDER_MALE;
else
return GENDER_NEUTER;
};
/*

View file

@ -505,3 +505,7 @@ float isdp;
#define FULLSEND 0xffffff
#ifdef QWSSQC
#define sprint(pl,...) sprint(pl, PRINT_HIGH, __VA_ARGS__)
#define bprint(...) bprint(PRINT_HIGH, __VA_ARGS__)
#endif

View file

@ -63,6 +63,9 @@ void() Laser_Touch =
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
#ifdef QWSSQC
WriteByte (MSG_BROADCAST, 1); //count
#endif
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);

View file

@ -573,6 +573,7 @@ void() trigger_hurt =
float PUSH_ONCE = 1;
void() target_position = {};
//function provided by mercury (of oztf)
void () trigger_push_findtarget =
{
@ -634,6 +635,7 @@ Pushes the player
*/
void() trigger_push =
{
print("Found trigger_push\n");
InitTrigger ();
precache_sound ("ambience/windfly.wav");
self.touch = trigger_push_touch;

View file

@ -64,6 +64,9 @@ void() W_FireAxe =
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);
WriteByte (MSG_BROADCAST, 8);
WriteString (MSG_BROADCAST, "overlong\n");
}
else
{
@ -72,6 +75,9 @@ void() W_FireAxe =
WriteCoord (MSG_MULTICAST, org_x);
WriteCoord (MSG_MULTICAST, org_y);
WriteCoord (MSG_MULTICAST, org_z);
WriteByte (MSG_MULTICAST, 8);
WriteString (MSG_MULTICAST, "overlong\n");
multicast(org, MULTICAST_PVS);
}
}
@ -231,6 +237,9 @@ void(float damage, vector dir) TraceAttack =
{
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte (MSG_BROADCAST, TE_GUNSHOT);
#ifdef QWSSQC
WriteByte (MSG_BROADCAST, 1); //count
#endif
WriteCoord (MSG_BROADCAST, org_x);
WriteCoord (MSG_BROADCAST, org_y);
WriteCoord (MSG_BROADCAST, org_z);

View file

@ -332,10 +332,17 @@ void() worldspawn =
// 63 testing
lightstyle(63, "a");
isdp = autocvar(test_dpcompat, 0);
serverusingcsqc = checkextension("EXT_CSQC") || checkextension("EXT_CSQC_1");
if (isdp)
serverusingcsqc = TRUE;
if (!serverusingcsqc)
{
if (checkbuiltin(print))
print("Server does not support csqc\n");
else
{
cvar_set("developer", "1");
dprint("Server is not using csqc\n");
}
}
};
void() StartFrame =

View file

@ -1,10 +1,10 @@
../progs.dat
//#define QWSSQC
#define SSQC
optsall.qc
#ifdef QSS
#if defined(QSS) && !defined(QWSSQC)
qsextensions.qc
#undef CSQC
#endif
fteextensions.qc